#!/usr/bin/env bash
# shellcheck source=/dev/null
# shellcheck extended-analysis=false

### project ###

PREFIX="/usr"
PROGNAME="SteamTinkerLaunch"
NICEPROGNAME="Steam Tinker Launch"
PROGVERS="v14.0.20250602"
PROGCMD="${0##*/}"
PROGINTERNALPROTNAME="Proton-stl"
SHOSTL="stl"
GHURL="https://github.com"
AGHURL="https://api.github.com"
PROJECTPAGE="$GHURL/sonic2kk/${PROGNAME,,}"
PPW="$PROJECTPAGE/wiki"
CURWIKI="$PPW"
STARTDEBUG=1
ONSTEAMDECK=0

### internal dependencies ###
#STARTINTDEPS
GIT="git"
PGREP="pgrep"
PIDOF="pidof"
PKILL="pkill"
TAR="tar"
UNZIP="unzip"
WGET="wget"
XDO="xdotool"
XPROP="xprop"
XRANDR="xrandr"
XWININFO="xwininfo"
XXD="xxd"
#ENDINTDEPS

### (optionally) used programs ###
GAMEMODERUN="gamemoderun"
GAMESCOPE="gamescope"
NYRNA="nyrna"
STEAM="steam"
RECO="resetcollections"
STERECO="$STEAM ${STEAM}://${RECO}"
STRACE="strace"
WINECFG="winecfg"
WICO="wineconsole"
SYSWINETRICKS="winetricks"
REPLAY="replay-sorcery"
INNOEXTRACT="innoextract"
CABEXTRACT="cabextract"
LSUSB="lsusb"
JQ="jq"
CONVERT="convert"
IDENTIFY="identify"
RSYNC="rsync"
CONTY="conty.sh"
SEVZA="7za"
PERES="peres"
GDB="gdb"
XDGMIME="xdg-mime"
XDGO="xdg-open"
OBSCAP="obs-gamecapture"

CHECKHMD=1
X32D="x32dbg"
X64D="x64dbg"
FACO="favorites.conf"
MENSO="menusort.conf"
MENUBLOCK="menublock.conf"
DSHM="/dev/shm"
STLSHM="$DSHM/${PROGNAME,,}"
MTEMP="$STLSHM/menutemp"
STLICON="$STLSHM/${PROGNAME,,}-steam-checked.png"
NOICON="$STLSHM/empty.png"
FAVPIC="$STLSHM/fav.jpg"
DFDIR="$STLSHM/desktopfiles"
CLOSETMP="$STLSHM/${PROGNAME,,}-closing.tmp"
PROTBUMPTEMP="$STLSHM/protonbump.tmp"
KILLSWITCH="$STLSHM/KillSwitch"
TEMPLOG="$STLSHM/${PROGNAME,,}.log"
PRELOG="$STLSHM/prelog.log"
APPMALOG="$STLSHM/listAppManifests.log"
GGDLOG="$STLSHM/getGameData.log"
WINRESLOG="$STLSHM/winres.log"
SHADLOG="$STLSHM/shaderupdate.log"
GWIDFILE="$STLSHM/${PROGNAME,,}-sbsgwid"
PIDLOCK="$STLSHM/pid.lock"
LAMOINST="$STLSHM/LastMOInst.txt"
GDBGAMERUN="$STLSHM/gdbgamerun.sh"
GDBRUN="$STLSHM/gdbrun.sh"
VRINITLOCK="$STLSHM/vrinit.lock"
IGCSINITLOCK="$STLSHM/igcsinit.lock"
VRINITRESULT="$STLSHM/vrinit.conf"
VWRUN="$STLSHM/vwrun.txt"
STELILIST="$STLSHM/SteamLibraries.txt"
UUUPATCH="${PROGNAME,,}-uuu-patch"
UUUPATCHCOMMAND="$STLSHM/${UUUPATCH}.sh"
GCD="GameCfgDiffers"
OVFS="openvr_fsr"
OVRA="openvr_api.dll"
OVRMOD="openvr_mod.cfg"
GWXTEMP="$STLSHM/gamewinXID.txt"
TRAYCUSC="$STLSHM/customscript"
TEMPGPIDFILE="$STLSHM/gamepid.txt"
STLSETENTRIES="$STLSHM/setentries.txt"
CLOSEVARS="$STLSHM/closevars.txt"
STLRAWENTRIES="$STLSHM/rawentries.txt"
STLNOBLOCKENTRIES="$STLSHM/noblockentries.txt"
STLCATSORTENTRIES="$STLSHM/catsortentries.txt"
UPWINTMPL="$STLSHM/upwintmpl.txt"
STLMINWIN="$STLSHM/minwin.txt"
STLMENUVALBLOCKCFG="$STLSHM/$MENUBLOCK"
VARSIN="$STLSHM/vars-in.txt"
FUPDATE="$STLSHM/fupdate.txt"
STPAVARS="$STLSHM/steampaths.txt"
PROTONCSV="$STLSHM/ProtonCSV.txt"
LOGINUSERSCSV="$STLSHM/LoginUsersCSV.txt"
SWRF="$STLSHM/SWR.txt"
UWRF="$STLSHM/UWR.txt"
EWRF="$STLSHM/EWR.txt"
NOSTSGDBIDSHMFILE="$STLSHM/NOSTSGDBID.txt"
NON="none"
NOPE="nope"
TMPL="template"
DLWINEVERSION="$NON"
GAMENAME="$NON"
EARLYUSEWINE=0
FAVMENU="$MTEMP/favmenu"
GAMEMENU="$MTEMP/gamemenu"
GAMETEMPMENU="$MTEMP/gametemplmenu"
GLOBALMENU="$MTEMP/globalmenu"
GAMMENU="Game Menu"
SETMENU="Settings Menu"
FAVOMENU="Favorite Menu"
LOSE="Local Settings"
APDA="Application Data"
APD="AppData"
ADRO="$APD/Roaming"
ADLO="$APD/Local"
ADLOLO="$APD/LocalLow"
EAGA="EA Games"
LAGA="Larian Studios"
BIOW="BioWare"
SAGE="Saved Games"
CDPR="CD Projekt Red"
LSAD="$LOSE/$APDA"
RUNCONTY="$NON"
WINX="800"
WINY="600"
F1ACTION="bash -c OpenWiki"
F1ACTIONCG="bash -c setColGui"
BTS="backup-timestamp"
DUMMYBIN="echo"
STLAIDSIZE="12"
INFLATPAK=0
WDIB="wine-discord-ipc-bridge"
FIXGAMESCOPE=0
SMALLDESK=0
VTX_DOTNET_ROOT="c:\\Program Files\\dotnet\\\\"
STLQUIET=0

# Hardcoded SLR AppID for native games -- See `setSLRReap`
# TODO refactor to be array for native AIDs? Some games (CS2) can use SLR 3.0
# We should allow the user to choose whether to use SLE 1.0 or 3.0 for native games
SLRAID="1070560"
DEFWINEDPI="96"
WINEDPIVALUES="${DEFWINEDPI}!120!150!240"
SGDBTIMEOUT="15"
SGDBRETRIES="5"

### default vars ###

if [ -z "$XDG_CONFIG_HOME" ]; then
	STLCFGDIR="$HOME/.config/${PROGNAME,,}"													# either hardcoded config dir
else
	STLCFGDIR="$XDG_CONFIG_HOME/${PROGNAME,,}"												# or in XDG_CONFIG_HOME if the user set the variable
fi

#486
function wip {
if [ -z "$XDG_CACHE_HOME" ]; then
	STLCACHEDIR="$HOME/.cache/${PROGNAME,,}"
else
	STLCACHEDIR="$XDG_CACHE_HOME/${PROGNAME,,}"
fi

if [ -z "$XDG_DATA_HOME" ]; then
	STLDATADIR="$HOME/.local/share/${PROGNAME,,}"
else
	STLDATADIR="$XDG_DATA_HOME/${PROGNAME,,}"
fi
}

SYSTEMSTLCFGDIR="$PREFIX/share/${PROGNAME,,}"												# systemwide config dir
BASELOGDIR="$STLCFGDIR/logs"																# base logfile dir
DEFLOGDIR="$BASELOGDIR/${PROGNAME,,}"																# default logfile dir

if [ -z "$LOGDIR" ]; then
	LOGDIR="$DEFLOGDIR"
fi

LOGDIRID="$LOGDIR/id"

LOGDIRTI="$LOGDIR/title"
STLPROTONLOGDIR="$BASELOGDIR/proton"
STLPROTONIDLOGDIR="$STLPROTONLOGDIR/id"
STLPROTONTILOGDIR="$STLPROTONLOGDIR/title"
STLDXVKLOGDIR="$BASELOGDIR/dxvk"
STLWINELOGDIR="$BASELOGDIR/wine"
STLVKD3DLOGDIR="$BASELOGDIR/vkd3d"
STLGLLOGDIR="$BASELOGDIR/gamelaunch"
STLGLLOGDIRID="$STLGLLOGDIR/id"
STLGLLOGDIRTI="$STLGLLOGDIR/title"
PLAYTIMELOGDIR="$BASELOGDIR/playtime"
STLGAMEDIR="$STLCFGDIR/gamecfgs"
STLGUIDIR="$STLCFGDIR/guicfgs"
STLGAMEDIRID="$STLGAMEDIR/id"
STLCUSTVARSDIR="$STLGAMEDIR/customvars"
STLGAMEDIRTI="$STLGAMEDIR/title"
STLCOLLECTIONDIR="$STLCFGDIR/collections"
STLREGDIR="$STLCFGDIR/regs"
STLDLDIR="$STLCFGDIR/downloads"
STLBACKDIR="$STLCFGDIR/backup"
BACKEX="$STLBACKDIR/exclude"
HIDEDIR="$STLCFGDIR/hide"
STLTEMPDIR="$STLCFGDIR/temp"
CODA="compatdata"
STLCOMPDAT="$STLCFGDIR/$CODA"
METADIR="$STLCFGDIR/meta"
GEMETA="$METADIR/id/general"
CUMETA="$METADIR/id/custom"
EVMETAID="$METADIR/eval/id"
EVMETASKIPID="$EVMETAID/skip"
EVMETACUSTOMID="$EVMETAID/custom"
EVMETAADDONID="$EVMETAID/addon"
EVMETATITLE="$METADIR/eval/title"
EVMETAOLD="$METADIR/eval/old"
EVALSC="evaluatorscript"
GECUST="get-current-step"
ISCRI="iscriptevaluator"
LIINPA="linux_install_path"
STEWOS="Steamworks Shared"
LECO="legacycompat"
FRSTATE="$STLSHM/firuState.txt"
TIGEMETA="$METADIR/title/general"
TICUMETA="$METADIR/title/custom"
DRC="drive_c"
DRCU="$DRC/users"
STUS="steamuser"
PUBUS="Public"
SUBADIR="$STLBACKDIR/$STUS"
SUBADIRID="$SUBADIR/id"
SUBADIRTI="$SUBADIR/title"
RSSUB="reshade-shaders"
WTDLDIR="$STLDLDIR/winetricks"
DLWT="$WTDLDIR/src/winetricks"
STLSHADDIR="$STLDLDIR/shaders"
SHADREPOLIST="$STLSHADDIR/repolist.txt"
SHADERREPOBLOCKLIST="$STLSHADDIR/repoblocklist.txt"
TWEAKDIR="$STLCFGDIR/tweaks"																# the parent directory for all user tweakfiles
USERTWEAKDIR="$TWEAKDIR/user"																# the place for the users own main tweakfiles
TWEAKCMDDIR="$TWEAKDIR/cmd"																	# dir for scriptfiles used by tweakfiles
SBSTWEAKDIR="$TWEAKDIR/sbs"																	# directory for optional config overrides for easier side-by-side VR gaming
MO2DLDIR="$STLDLDIR/mo2"
HMMDLDIR="$STLDLDIR/hedgemodmanager"
HMMVERFILE="$HMMDLDIR/hmmver.txt"
CONTYDLDIR="$STLDLDIR/conty"
X64DBGDLDIR="$STLDLDIR/$X64D"
GEOELFDLDIR="$STLDLDIR/geo11"
WDIBDLDIR="$STLDLDIR/${WDIB}"
EWDIB="${WDIB//-}"
RUNWDIB="$WDIBDLDIR/${EWDIB}.exe"
LGEOELFSYM="$GEOELFDLDIR/latest"
CUSTOMFALLBACKPIC="$STLDLDIR/custom-fallback.png"
VTX="vortex"
VOGAT="${VTX}games.txt"
STLVORTEXDIR="$STLCFGDIR/$VTX"
VORTEXDLDIR="$STLDLDIR/$VTX"
VORTSETCMD="$STLSHM/vortset.cmd"
VTST="$STLSHM/vsetup.txt"
SPEK="SpecialK"
SPEKDLDIR="$STLDLDIR/${SPEK,,}"
FWS="FlawlessWidescreen"
FWSDLDIR="$STLDLDIR/${FWS,,}"
YADAIDLDIR="$STLDLDIR/yadappimage"
DOTN="dotnet"
MO2="mo2"
STLMO2DIR="$STLCFGDIR/$MO2"
STLMO2DLDATDIR="$STLMO2DIR/dldata"
MO="ModOrganizer"
HMM="HedgeModManager"
HMMNICE="Hedge Mod Manager"
HMMSTABLE="stable"
HMMDEV="nightly"
HMMAUTO="auto"
HMMDF="$HMM-${PROGNAME,,}.desktop"
HMMDFDL="$HMM-${PROGNAME,,}-dl.desktop"
HMMDFPA="$HOME/.local/share/applications/$HMMDF"
HMMDFDLPA="$HOME/.local/share/applications/$HMMDFDL"
MORDIR="$DRC/Modding"
MOERDIR="$MORDIR/MO2"
MOERPATH="$MOERDIR/${MO}.exe"
STLHMMDIR="$STLCFGDIR/${HMM,,}"
DOCS="Documents"
MYDOCS="My $DOCS"
MYGAMES="My Games"
PLACEHOLDERAID="31337"
PLACEHOLDERGN="Placeholder"
DPRS="Depressurizer"
DPRSDLDIR="$STLDLDIR/${DPRS,}"
DEPS="Dependencies"
DEPSGE="${DEPS}Gui.exe"
DEPSDLDIR="$STLDLDIR/${DEPS,}"
DEPSLATDIR="$DEPSDLDIR/latest"
DEPSL64="$DEPSLATDIR/64/$DEPSGE"
DEPSL32="$DEPSLATDIR/32/$DEPSGE"
MAHU="mangohud"
MANGOAPP="mangoapp"
MAHUCFGDIR="$STLCFGDIR/$MAHU"
MAHUCID="$MAHUCFGDIR/id"
MAHUCTI="$MAHUCFGDIR/title"
MAHUTMPL="$MAHUCFGDIR/$MAHU-${PROGNAME,,}-template.conf"
MAHULOGDIR="$BASELOGDIR/$MAHU"
MAHULID="$MAHULOGDIR/id"
MAHULTI="$MAHULOGDIR/title"
PFX86="Program Files (x86)"
PFX86S="$PFX86/Steam"
APIN="appinfo"
SLO="Steam Launch Option"
SOMEPOPULARWINEPAKS="dotnet4 quartz xact"
SOMEWINEDEBUGOPTIONS="-all,+steam,+vrclient,+vulkan"
DEFSGDBHERODIMS="3840x1240,1920x620"
DEFSGDBBOXARTDIMS="600x900"
DEFSGDBTENFOOTDIMS="920x430,460x215"
SGDBHASFILEOPTS="skip!backup!replace"
SGDBTYPEOPTS="static!animated!static,animated!animated,static"
SGDBTAGOPTS="any!true!false"
SGDBHEROSTYLEOPTS="alternate,blurred,material"
SGDBLOGOSTYLEOPTS="official,white,black,custom"
SGDBGRIDSTYLEOPTS="alternate,blurred,white_logo,material,no_logo"
SGDBTNFTSTYLEOPTS="alternate,blurred,white_logo,material"
GETSTAID="99[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]99"

STLGAMES="$STLCFGDIR/games"
STLSTEAMDECKLASTVERS="$STLCFGDIR/lastvers"
STLGDESKD="$STLGAMES/desktop"
STLIDFD="$STLGAMES/desktopfiles"
STLISLDFD="$STLGAMES/sldesktopfiles"
STLAPPINFOIDDIR="$STLGAMES/$APIN"
STLGHEADD="$STLGAMES/header"
STLGSAD="$STLGAMES/standalone"
STLGICONS="$STLGAMES/icons"
STLGDECKCOMPAT="$STLGAMES/deckinfo"
STLGICO="$STLGICONS/ico"
STLGZIP="$STLGICONS/zip"
STLGPNG="$STLGICONS/png"
STLGSAPD="$STLGSAD/titles"
STLGSACD="$STLGSAD/$CODA"
STLGPEVKD="$STLGAMES/pev"
MO2INSTFAIL="$STLSHM/${MO}-failed.txt"
ABSGAMEEXEPATH="$NON"
GEOELF="geo-11"
ISORIGIN=0

#STLGSCPTD="$STLGAMES/scripts"
NOGAMES=",70_2260196511,228980,858280,961940,1054830,1070560,1113280,1245040,1391110,1420170,"
# Taken from GE-Proton7-24 changelog, removing some duplicates
WINE_FSR_CUSTOM_RESOLUTIONS=(
	# 1080p
	"960x540"
	"1129x635"
	"1280x720"
	"1477x831"

	# 2K
	"1506x847"
	"1706x960"
	"1970x1108"

	# Ultra-Wide
	"1720x720"
	"2024x847"
	"2293x960"
	"2646x1108"

	# 4K
	"1920x1080"
	"2259x1270"
	"2560x1440"
	"2954x1662"

	# 32:9 (5120x1440) -- Samsung Neo G9
	"2560x720"
	"3012x847"
	"3413x960"
	"3839x1108"
)

# Hex patterns for various blocks in shortcuts.vdf that we can grep for -- Casing matters!
SHORTCUTVDFFILESTARTHEXPAT="0073686f7274637574730000300002"  # Bytes for beginning of the shortcuts.vdf file
SHORTCUTVDFENTRYBEGINHEXPAT="00080800.*?0002"  # Pattern for beginning of shortcut entry in shortcuts.vdf -- Beginning of file has a different pattern, but every other pattern begins like this
SHORTCUTSVDFENTRYENDHEXPAT="000808"  # Pattern for how shortcuts.vdf blocks end
SHORTCUTVDFAPPIDHEXPAT="617070696400"  # 'appid'
SHORTCUTVDFNAMEHEXPAT="(014170704e616d6500|6170706e616d6500)"  # 'AppName' and 'appname'
SHORTCUTVDFEXEHEXPAT="000145786500"  # 'Exe' ('exe' is 6578650a if we ever need it)
SHORTCUTVDFSTARTDIRHEXPAT="0001537461727444697200"  # 'StartDir'
SHORTCUTVDFICONHEXPAT="000169636f6e00"  # 'icon'
SHORTCUTVDFENDPAT="0001"  # Generic end pattern for each shortcut.vdf column

function setGNID {
	# only keep alphabet chars
	INGN="$(tr -cd '[:alnum:]' <<< "${1^^}")"
	INGN="${INGN//[[:digit:]]/}"
	MIDNUMSIZE=$((STLAIDSIZE -4))

	if [ "${#INGN}" -gt "$MIDNUMSIZE" ]; then
		INGN="${INGN:0:MIDNUMSIZE}"
	fi

	while [ "${#INGN}" -lt "$MIDNUMSIZE" ]; do
		INGN="${INGN}A"
	done

	ALPHARR=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)

	function numberOut {
		for n in "${!ALPHARR[@]}"; do
			if [ "${ALPHARR[$n]}" == "$1" ]; then
				NUM="$((n +1))"
				if [ "$NUM" -gt 9 ]; then
					printf "%s" "${NUM:1:1}"
				else
					printf "%s" "${NUM:0:1}"
				fi
			fi
		done
	}

	function outNum {
		i=0
		while [ "$i" -lt "${#1}" ]; do
			numberOut "${1:$i:1}"
			i=$((i+1))
		done
		printf "\n"
	}

	MIDNUM="$(outNum "$INGN")"
	echo "99${MIDNUM}99"
}

function initAID {
	STLPLAY=0
	if [ -n "$STEAM_COMPAT_APP_ID" ]; then
		AID="$STEAM_COMPAT_APP_ID"
		writelog "INFO" "${FUNCNAME[0]} - Set AID from STEAM_COMPAT_APP_ID to '$AID'" "P"
	fi

	if [ -z "$AID" ] || [ "$AID" -eq "0" ]; then
		# shellcheck disable=SC2154		# SteamAppId comes from Steam
		if [ -z "$SteamAppId" ]; then
			if grep -q "^play" <<< "$@" && [ -n "$2" ] && [ -z "$3" ] && [ "$2" != "gui" ] && [ "$2" != "ed" ]; then
				STLPLAY=1
				if [ "$2" -eq "$2" ] 2>/dev/null ; then
					AID="$2"
				elif [ -f "$2" ]; then
					GN="${2##*/}"
					AID="$(setGNID "$GN")"
				fi
				writelog "INFO" "${FUNCNAME[0]} - Set AID to '$PROGCMD' ID '$AID'" "P"
			elif grep -q "^play" <<< "$@" && [ -f "$3" ]; then
				STLPLAY=1
				GN="${3##*/}"
				AID="$2"

				writelog "INFO" "${FUNCNAME[0]} - Set AID to '$PROGCMD' ID '$AID'" "P"
			else
				AID="$PLACEHOLDERAID"
				writelog "INFO" "${FUNCNAME[0]} - Set AID to PLACEHOLDERAID '$AID'" "P"
			fi
		else
			if [ "$SteamAppId" -eq "0" ]; then
				AID="${STEAM_COMPAT_DATA_PATH##*/}"
				writelog "INFO" "${FUNCNAME[0]} - Set AID from STEAM_COMPAT_DATA_PATH '$STEAM_COMPAT_DATA_PATH' to '${STEAM_COMPAT_DATA_PATH##*/}', because SteamAppId is '$SteamAppId'" "P"
			else
				AID="$SteamAppId"
				writelog "INFO" "${FUNCNAME[0]} - Set AID to SteamAppId '$SteamAppId' coming from Steam" "P"
			fi
		fi
	fi

	if [ -n "$STEAM_COMPAT_DATA_PATH" ]; then
		OSCDP="$STEAM_COMPAT_DATA_PATH"
		writelog "INFO" "${FUNCNAME[0]} - Set OSCDP to STEAM_COMPAT_DATA_PATH '$STEAM_COMPAT_DATA_PATH'" "P"
	fi
}

STLFAVMENUCFG="$STLCFGDIR/$FACO"															# optional config (in fact just a list with variables) which holds all entries for the individual favorites menu
STLMENUSORTCFG="$STLCFGDIR/$MENSO"															# holds the category sort order for all menus
STLMENUBLOCKCFG="$STLCFGDIR/$MENUBLOCK"
#STARTEDITORCFGLIST
STLDEFGLOBALCFG="$STLCFGDIR/global.conf"													# global config
STLDEFGAMECFG="$STLCFGDIR/default_template.conf"											# the default config template used to create new per game configs - will be auto created if not found
GLOBCUSTVARS="$STLCUSTVARSDIR/global-custom-vars.conf"										# global config file for user defined custom variables

function setSDCfg {
STLSDLCFG="$STLCFGDIR/steamdeck.conf"														# Steam Deck config
}

function setAIDCfgs {
STLGAMECFG="$STLGAMEDIRID/$AID.conf"														# the game specific config file which is used by the launched game - created from $STLDEFGAMECFG if not found
TWEAKCFG="$USERTWEAKDIR/$AID.conf"															# the game specific shareable config tweak overrides
SBSTWEAKCFG="$SBSTWEAKDIR/$AID.conf"														# the game specific shareable config sbs tweak overrides
LOGFILE="$LOGDIRID/$AID.log"
GAMECUSTVARS="$STLCUSTVARSDIR/$AID.conf"													# game specific config file for user defined custom variables
}

STLURLCFG="$STLCFGDIR/url.conf"																# url config
CUSTOMPROTONLIST="$STLCFGDIR/protonlist.txt"												# plain textfile with additional user proton versions
VORTEXSTAGELIST="$STLVORTEXDIR/stages.txt"													# plain textfile with Vortex Stage directories - one per Steam Library partition
SEENVORTEXGAMES="$STLVORTEXDIR/$CODA/seen$VOGAT"
EXGLOB="$BACKEX/exclude-global.txt"
#ENDEDITORCFGLIST

STLLANGDIR="$STLCFGDIR/lang"
STLDEFLANG="english"

STLDXVKDIR="$STLCFGDIR/dxvk"																# base dxvk config dir from where default per game configs are automatically parsed
ISGAME=0																					# 1=game was launched, 2=windows game, 3=linux game
WFEAR="waitforexitandrun"
SA="steamapps"
SAC="$SA/common"
L2EA="link2ea"
CTD="compatibilitytools.d"
SLR="SteamLinuxRuntime"
STERU="steam-runtime"
BIWI="bin/wine"
DBW="dist/$BIWI"
FBW="files/$BIWI"
STLPROTDIR="$STLCFGDIR/proton"
STLPROTCOMPDATDIR="$STLPROTDIR/$CODA"
STLPROTSTUSDIR="$STLPROTDIR/$STUS"
STLPROTPUBUSDIR="$STLPROTDIR/$PUBUS"
STLEARLYPROTCONF="$STLPROTDIR/earlyproton.conf"
PROCU="proton/custom"
CTVDF="compatibilitytool.vdf"
TOMA="toolmanifest.vdf"
LIFOVDF="libraryfolders.vdf"
SCV="sharedconfig.vdf"
AIVDF="$APIN.vdf"
AITXT="$APIN.txt"
AAVDF="appcache/$AIVDF"
APVDF="appcache/packageinfo.vdf"
USDA="userdata"
SCVDF="shortcuts.vdf"
SRSCV="7/remote/$SCV"
LCV="localconfig.vdf"
COCOV="config/config.vdf"
LUCOV="config/loginusers.vdf"
SCSHVDF="screenshots.vdf"
SCRSH="760/$SCSHVDF"
LASTRUN="$LOGDIR/lastrun.txt"
SAIT="steam_appid.txt"
DXGI="dxgi.dll"
D3D9="d3d9.dll"
D3D11="d3d11.dll"
D3D47="d3dcompiler_47.dll"
OGL32="opengl32.dll"
AUTO="auto"
D3D47DLDIR="$STLDLDIR/${D3D47%.*}"
SPEKDLLNAMELIST="$AUTO!$DXGI!$D3D9!$D3D11!$OGL32"
RESHADEDLLNAMELIST="$DXGI!$D3D9!$D3D11!$OGL32!$DXGI,$D3D9"
SOMEWINEDLLOVERRIDES="dinput8=n,b!dxgi=n,b!d3d9=n,b!${D3D47//.dll}=n,b!xaudio2_7=n,b"
RS_DX_DEST="$DXGI"
RS_D9_DEST="$D3D9"
RESH="ReShade"
RSSU="${RESH}_Setup"
RSINI="${RESH}.ini"
RSTXT="${RESH}.txt"
IGCS="IGCSInjector"
UUU="UniversalUE4Unlocker"
LFM="launchFavMenu"
LGAM="launchGameMenu"
LGATM="launchGameTemplateMenu"
LGLM="launchGlobalMenu"
LCM="launchCategoryMenu"
HEADLINEFONT="larger"
FONTSIZES="!xx-small!x-small!small!smaller!medium!large!larger!!x-large!xx-large!"
SREG="system.reg"
NSGA="non-steam game"
FSGDBA="FetchSteamGridDBArtwork"
SGA="set game artwork"
BTVP="$DRC/Program Files/Black Tree Gaming Ltd/${VTX^}"
VTXRAA="resources/app.asar"
RABP="${VTXRAA}.unpacked/bundledPlugins"

# make SC happy:
GUI_CUSTOMCMD=""
################

function setSteamPath {
	HSR="$HOME/.steam/root"
	HSS="$HOME/.steam/steam"

	if [ -z "${!1}" ]; then
		if [ -e "${HSR}/${2}" ]; then
			# readlink might be better in both STPAs here to be distribution independant, possible side effects not tested!
			STPA="$(readlink -f "${HSR}/${2}")"
			export "$1"="$STPA"
			echo "$1=\"$STPA\"" >> "$STPAVARS"
			writelog "INFO" "${FUNCNAME[0]} - Set '$1' to '$STPA'"
		elif [ -e "${HSS}/${2}" ]; then
			STPA="$(readlink -f "${HSS}/${2}")"
			export "$1"="$STPA"
			echo "$1=\"$STPA\"" >> "$STPAVARS"
			writelog "INFO" "${FUNCNAME[0]} - Set '$1' to '$STPA'"
		else
			writelog "WARN" "${FUNCNAME[0]} - '$2' not found for variable '$1' in '$HSR' or '$HSS'!"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - '$1' already defined as '${!1}'"
		echo "$1=\"${!1}\"" >> "$STPAVARS"
	fi
}

function setSteamPaths {
	if [ -f "$STPAVARS" ] && grep -q "^SUSDA" "$STPAVARS" ; then
		writelog "INFO" "${FUNCNAME[0]} - Reading Steam Path variables from '$STPAVARS'"
		loadCfg "$STPAVARS" X
	else
		setSteamPath "SROOT"
		mkProjDir "$SROOT/$CTD"
		setSteamPath "SUSDA" "$USDA"
		setSteamPath "DEFSTEAMAPPS" "$SA"
		setSteamPath "DEFSTEAMAPPSCOMMON" "$SAC"
		setSteamPath "CFGVDF" "$COCOV"
		setSteamPath "LOGUVDF" "$LUCOV"
		setSteamPath "LFVDF" "$SA/$LIFOVDF"
		setSteamPath "FAIVDF" "$AAVDF"
		setSteamPath "PIVDF" "$APVDF"
		setSteamPath "STEAMCOMPATOOLS" "$CTD"
		setSteamPath "ICODIR" "steam/games"

		if [ -z "$STEAM_COMPAT_CLIENT_INSTALL_PATH" ]; then
			export STEAM_COMPAT_CLIENT_INSTALL_PATH="$SROOT"
			echo "STEAM_COMPAT_CLIENT_INSTALL_PATH=\"$SROOT\"" >> "$STPAVARS"
		fi

		if [ -f "$STLDEFGLOBALCFG" ] && grep -q "^STEAMUSERID=" "$STLDEFGLOBALCFG" ; then
			STEAMUSERID="$(grep "^STEAMUSERID=" "$STLDEFGLOBALCFG" | grep -o "[[:digit:]]*")"
			STUIDPATH="$SUSDA/$STEAMUSERID"

			writelog "INFO" "${FUNCNAME[0]} - Parsing Steam UserID from global config as '$STEAMUSERID' -- STUIDPATH is now '$STUIDPATH'"
		else
			if [ -d "$SUSDA" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Trying to determine Steam UserID and userdata path"

				STEAMUSERID=""
				STUIDPATH=""

				# Try to set the path to the userdata folder (this contains grids, shortcuts.vdf, etc)
				# fillLoginUsersCSV will fall back to taking the first userdata folder in the Steam userdata dir and will set it to MostRecent=1
				# if it doesn't get any matches in loginusers.vdf, so we don't have to do the fallback here
				if [ ! -f "$LOGINUSERSCSV" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Filling Users CSV"
					fillLoginUsersCSV
				fi

				# Try to find the Steam Userdata folder and current Steam UserID based on generated LoginUsersCSV
				# This allows us to select the currently logged in user as the Steam User we want to use, meaning
				# we will use their userdata directory.
				#
				# If the currently logged in user changes we will then be able to use their userdata directory instead
				# This allows us to use the correct userdata folder for the currently logged in user by default when
				# there are multiple Steam user accounts logged into the same machine
				#
				# See also: https://github.com/sonic2kk/steamtinkerlaunch/issues/1140
				while read -r loginuser; do
					LOGINUSERCSVSHORTAID="$( echo "${loginuser}" | cut -d ';' -f2  )"
					LOGINUSERCSVMOSTRECENT="$( echo "${loginuser}" | cut -d ';' -f3  )"

					# if the loginuser loop variable is the MostRecent in loginusers.vdf, then:
					# - set the current Steam User ID to the Short UserID
					# - set the Steam userdata path to the base path + the Short UserID
					if [ "${LOGINUSERCSVMOSTRECENT}" -eq 1 ]; then
						STEAMUSERID="${LOGINUSERCSVSHORTAID}"
						STUIDPATH="${SUSDA}/${STEAMUSERID}"

						writelog "INFO" "${FUNCNAME[0]} - Found MostRecent Steam User '${STEAMUSERID}' from '${LOGINUSERSCSV}' - STUIDPATH is now '${STUIDPATH}'"
						break
					fi
				done < "$LOGINUSERSCSV"

				# Since fillLoginUsersCSV should handle the fallback for us, if we still have no matches,
				# assume no users found at all, meaning no users are logged in!
				# This will cause problems, so log a warning
				#
				# Hopefully this never happens under normal usage... We should always be able to find the Steam User
				if [ -z "${STEAMUSERID}" ] || [ -z "${STUIDPATH}" ]; then
					writelog "WARN" "${FUNCNAME[0]} - Could not find any logged in Steam users in '$LOGINUSERSCSV' (are any users logged in?) - other variables depend on it, expect problems!" "E"
				elif [ ! -d "${STUIDPATH}" ]; then
					# If we were able to get the Most Recent Steam user but the userdata path for this user with this UserID does not actually exist, something has gone horribly wrong!
					# One possible but unlikely scenario is that the MostRecent user in LognUsersCSV file was removed from the Steam Client, so the userdata path would no longer exist
					# Users should remove /dev/shm/steamtinkerlaunch if the accounts or Steam Client config changes in any way so this would only be a temporary issue
					writelog "WARN" "${FUNCNAME[0]} - Built Steam userdata path for User ID '${STEAMUSERID}' at path '${STUIDPATH}', but this path does not exist! This will probably cause problems!" "E"
				fi
			else
				writelog "WARN" "${FUNCNAME[0]} - Steam '$USDA' directory not found, other variables depend on it - Expect problems" "E"
			fi
		fi

		SUIC="$STUIDPATH/config"
		FLCV="$SUIC/$LCV"

		{
		echo "STUIDPATH=\"$STUIDPATH\""
		echo "STEAMUSERID=\"$STEAMUSERID\""
		echo "SUIC=\"$SUIC\""
		echo "FLCV=\"$FLCV\""
		} >> "$STPAVARS"

		writelog "INFO" "${FUNCNAME[0]} - Found SteamUserId '$STEAMUSERID'"
	fi
}

function setAwkBin {
	if [ -z "$AWKBIN" ];then
		if [ -x "$(command -v "gawk")" ]; then
			AWKBIN="gawk"
			if [ -z "$1" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Found '$AWKBIN' as an 'awk' variant. It should work without any issues, because 'gawk' was tested completely"
			fi
		elif [ -x "$(command -v "mawk")" ]; then
			AWKBIN="mawk"
			if [ -z "$1" ]; then
				writelog "WARN" "${FUNCNAME[0]} - Only found '$AWKBIN' as an 'awk' variant. It might be incompatible in several functions, as only 'gawk' was tested completely!"
			fi
		elif [ -x "$(command -v "awk")" ]; then
			AWKBIN="mawk"
			if [ -z "$1" ]; then
				writelog "WARN" "${FUNCNAME[0]} - Only found '$AWKBIN' as an 'awk' variant. It might be incompatible in several functions, as only 'gawk' was tested completely!"
			fi
		fi

		if [ -z "$AWKBIN" ];then
			writelog "ERROR" "${FUNCNAME[0]} - No 'awk' variant found, but at least one is required (best would be 'gawk') - Can't continue" "E"
			exit
		else
			export AWKBIN="$AWKBIN"
		fi
	fi
}

function awk {
	if [ -z "$AWKBIN" ];then
		setAwkBin "X"
	fi
	"$AWKBIN" "$@"
}

function OpenWikiPage {
	if [ -n "$1" ]; then
		if grep -q "$PPW" <<< "$1"; then
			WIKURL="$1"
		else
			WIKURL="$PPW/$1"
		fi
	else
		if [ -n "$CURWIKI" ]; then
			WIKURL="$CURWIKI"
		fi
	fi

	if [ -n "$WIKURL" ]; then
		if [ "$ONSTEAMDECK" -eq 1 ]; then
			# Only open wiki on Steam Deck Game Mode
			if [ "$FIXGAMESCOPE" -eq 0 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Opening wiki URL '$WIKURL' using xdg-open on Steam Deck since Yad AppImage does not have WebKit support"
				"$XDGO" "$WIKURL"
			else
				writelog "SKIP" "${FUNCNAME[0]} - Running in Steam Deck Game Mode - Opening wiki page using xdg-open here may not work or may have undesired results - Skipping"
			fi
		else
			TITLE="${PROGNAME}-Wiki"
			pollWinRes "$TITLE"
			"$YAD" --window-icon="$STLICON" --title="$TITLE" --on-top --center "$WINDECO" --html --uri="$WIKURL" "$GEOM" >/dev/null 2>/dev/null
		fi
	fi
}

function OpenWiki {
	"${PROGCMD}" wiki "$CURWIKI"
}

export -f OpenWiki

function StatusWindow {
	YAD=yad
    TITLE="${PROGNAME}-$3"
    pollWinRes "$TITLE"
	writelog "INFO" "${FUNCNAME[0]} - for '$1'"

    RUNFUNC="$2"
    $RUNFUNC |
    while read -r line; do
		echo "# ${line}";
	done | "$YAD" --window-icon="$STLICON" --title="$TITLE" --on-top --progress --progress-text="$1..." --pulsate --center --no-buttons --auto-close "$WINDECO" "$GEOM"
}

function setColGui {
	HAVCOL=0
	if grep -q "^COLCOUNT" "$CURGUICFG"; then
		CCR="$(grep "^COLCOUNT" "$CURGUICFG" | cut -d '=' -f2)"
		CURCOL="${CCR//\"}"
		HAVCOL=1
	else
		CURCOL=1
	fi

	export CURWIKI="$PPW/Gui-Columns"

	TITLE="${FUNCNAME[0]}"
	WTR="${CURGUICFG##*/}"
	WINTITLE="${WTR//.conf}"
	SELCOL="$("$YAD" --f1-action="$F1ACTION" --center --form --separator="\n" --field="Columns in $WINTITLE":NUM "$CURCOL" --title="$TITLE" "$GEOM")"

	if [ -z "$SELCOL" ] || [ "$SELCOL" -eq 0 ]; then
		SELCOL=1
	fi

	if [ "$HAVCOL" -eq 0 ]; then
		echo "COLCOUNT=\"$SELCOL\"" >> "$CURGUICFG"
	else
		if [ "$CURCOL" -ne "$SELCOL" ]; then
			sed "s:COLCOUNT=\"$CURCOL\":COLCOUNT=\"$SELCOL\":g" -i "$CURGUICFG"
		fi
	fi
}
export -f setColGui

function dlCheck {
	function chkFile {
		if [ "$FLCHK" == "stat" ]; then
			ISCHK="$("$FLCHK" -c%s "$DLDST" | cut -d ' ' -f1)"
		else
			ISCHK="$("$FLCHK" "$DLDST" | cut -d ' ' -f1)"
		fi
		CHKTXT="$(strFix "$NOTY_CHK" "$FLCHK" "$DLDST")"
		writelog "INFO" "${FUNCNAME[0]} - $CHKTXT"
		notiShow "$CHKTXT" "S"

		if [ "$ISCHK" == "$INCHK" ];then
			CHKTXT="$(strFix "$NOTY_CHKOK" "$FLCHK" "${DLDST##*/}" "$ISCHK")"
			writelog "INFO" "${FUNCNAME[0]} - $CHKTXT"
			notiShow "$CHKTXT" "S"
		else
			CHKTXT="$(strFix "$NOTY_CHKNOK" "${DLDST##*/}" "$ISCHK" "$INCHK")"
			writelog "WARN" "${FUNCNAME[0]} - $CHKTXT"
			notiShow "$CHKTXT" "S"
		fi
		sleep 2
	}

	DLSRC="$1"
	DLDST="$2"
	FLCHK="$3"
	DLTITLE="$4"
	INCHK="$5"

	if [ "$FLCHK" != "C" ]; then
		if [ -f "$DLDST" ] && [ "$FLCHK" != "X" ]; then
			chkFile
		else
			if [ "$DLTITLE" != "$NON" ]; then
				writelog "INFO" "${FUNCNAME[0]} - $DLTITLE"
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$DLDST")" "S"
				if grep -q "show-progress" <<< "$("$WGET" --help)" && [ "$ONSTEAMDECK" -eq 0 ]; then
					writelog "INFO" "${FUNCNAME[0]} - '$WGET -q --show-progress $DLSRC -O $DLDST'"
					"$WGET" -q --show-progress "$DLSRC" -O "$DLDST" 2>&1 | sed -u -e "s:\.::g;s:.*K::g;s:^[[:space:]]*::g" | grep -v "SSL_INIT"
				else
					writelog "INFO" "${FUNCNAME[0]} - '$WGET -q $DLSRC -O $DLDST'"
					"$WGET" -q "$DLSRC" -O "$DLDST" 2>&1 | sed -u -e "s:\.::g;s:.*K::g;s:^[[:space:]]*::g" | grep -v "SSL_INIT"
				fi
			else
				"$WGET" -q "$DLSRC" -O "$DLDST" 1>/dev/null 2>&1
			fi
		fi
	else
		FLCHK="$1"
		DLDST="$2"
		INCHK="$4"
		writelog "INFO" "${FUNCNAME[0]} - Only checking already downloaded file '$DLDST'"
		chkFile
	fi

	if [ -n "$INCHK" ] && [ "$INCHK" != "$NON" ]; then
		if [ "$FLCHK" == "X" ]; then
			notiShow "$(strFix "$NOTY_DLCUSTOMPROTON2" "$DLDST")" "S"
			writelog "INFO" "${FUNCNAME[0]} - $(strFix "$NOTY_DLCUSTOMPROTON2" "$DLDST")"
		elif [ "$FLCHK" != "C" ]; then
			chkFile
		fi
	fi
}

function getGamePic {
	if [ "$DLGAMEDATA" -eq 1 ] && [ "$STLPLAY" -eq 0 ]; then
		DLPIC="$1"
		if [[ ( ! -f "$DLPIC" || ! -s "$DLPIC" ) && "$STLPLAY" -eq 0 ]]; then
			DLTITLE="Downloading picture for game '$(basename "${1//.jpg/}")'"
			dlCheck "$2" "$DLPIC" "X" "$DLTITLE"
		fi
	fi
}

function getGameName {
	if [ -n "$GN" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using 'GN' as Game Name: '$GN'"
		GNRAW="$GN"
	elif [ -f "$STLGAMEDIRID/$1.conf" ]; then
		GNRAW="$(grep "#GAMENAME" "$STLGAMEDIRID/$1.conf" | cut -d '=' -f2)"
		writelog "INFO" "${FUNCNAME[0]} - Found Game Name '$GNRAW' in '$STLGAMEDIRID/$1.conf'"
	elif grep -q "_${1}\." <<< "$(listAppManifests)" ; then
		APPMA="$(grep "_${1}\." <<< "$(listAppManifests)")"
		if [ -f "$APPMA" ]; then
			GNRAW1="$(grep "\"name\"" "$APPMA" | awk -F '"name"' '{print $NF}')"
			GNRAW="$(awk '{$1=$1};1' <<< "$GNRAW1")"
			writelog "INFO" "${FUNCNAME[0]} - Found Game Name '$GNRAW' in '$APPMA'"
		else
			writelog "SKIP" "${FUNCNAME[0]} - file '$APPMA' not found"
		fi
	elif [ -f "$STLAPPINFOIDDIR/${1}.bin" ]; then
		GNRAW="$(getAppInfoData "$AID" "name")"
		writelog "INFO" "${FUNCNAME[0]} - Found Game Name '$GNRAW' in '$STLAPPINFOIDDIR/${1}.bin'"
	else
		if [ "$DLGAMEDATA" -eq 1 ]; then
			APIURL="https://api.steampowered.com/ISteamApps/GetAppList/v2"
			APIDL="$STLDLDIR/SteamApps.json"
			MAXAGE=1440
			writelog "INFO" "${FUNCNAME[0]} - Downloading gamedata for '$1'"

			if [ ! -f "$APIDL" ] || test "$(find "$APIDL" -mmin +"$MAXAGE")"; then
				dlCheck	"$APIURL" "$APIDL" "X" "Downloading $APIDL"
			fi
		fi

		if [ -f "$APIDL" ]; then
			if [ ! -x "$(command -v "$JQ")" ]; then
				writelog "WARN" "${FUNCNAME[0]} - Can't get data from '$APIDL' because '$JQ' is not installed"
			else
				writelog "INFO" "${FUNCNAME[0]} - Searching Game Name for '$1' in $APIDL"
				GNRAW="$("$JQ" ".applist.apps[] | select (.appid==$1) | .name" "$APIDL")"

				if [ -n "$GNRAW" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Found Game Name '$GNRAW' in $APIDL"
				fi
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - file '$APIDL' not found"
		fi
	fi
	writelog "INFO" "${FUNCNAME[0]} - Outgoing game name is '${GNRAW//\"/}'"

	GAMENAME="${GNRAW//\"/}"
}

function writeDesktopFile {
	DESTDTF="$2"
	DESTPIC="$3"

	if [ -f "$DESTDTF" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - $DESTDTF already exists"
	else
		getGameName "$1"
		if [ -z "$GAMENAME" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Could not find gamename for game id '$1'"
		elif [ -n "$GAMENAME" ] && [ "$GAMENAME" != "$NON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Creating '$DESTDTF' for '$GAMENAME' ($1)"
			{
				echo "[Desktop Entry]"
				echo "Name=${GAMENAME//\"/}"
				echo "Comment=$DF_COMMENT"
				echo "Exec=steam steam://rungameid/$1"
				echo "Icon=$DESTPIC"
				echo "Terminal=false"
				echo "Type=Application"
				echo "Categories=Game;"
			} >> "$DESTDTF"
		fi
	fi
}

function createDesktopIconFile {
	if [ "$1" -eq "$1" ] 2>/dev/null; then
		AID="$1"
	else
		AID="$(getIDFromTitle "$1")"
	fi

	WANTGPNG="$STLGPNG/${AID}.png"

	if [ "$STLPLAY" -eq 1 ]; then
		mkProjDir "$STLISLDFD"
		INTDTFILE="$STLISLDFD/$AID.desktop"
	else
		mkProjDir "$STLIDFD"
		INTDTFILE="$STLIDFD/$AID.desktop"
	fi

	if [ -f "$INTDTFILE" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Already have an internal desktop file '$INTDTFILE'"
	else
		if [ -n "$AID" ] && [ ! -f "$WANTGPNG" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Don't have a desktop icon yet - trying to create it"
			if [ "$STLPLAY" -eq 1 ]; then
				if [ -n "$4" ]; then
					GAMENAME="$3"
					HAVEPA="$4"
					standaloneGameIcon "$WANTGPNG" "$AID" "$GAMENAME" "$HAVEPA"
				else
					writelog "WARN" "${FUNCNAME[0]} - Not enough arguments passes to create an icon - got '$*'"
				fi
			else
				getGameIcon
			fi
		fi

		# create desktop file internally for optionally using it on the desktop
		if [ -f "$WANTGPNG" ] && [ ! -f "$INTDTFILE" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Creating $INTDTFILE"
			if [ "$STLPLAY" -eq 1 ]; then
				if [ -n "$4" ]; then
					GAMENAME="$3"
					HAVPA="$4"
					standaloneDesktopFile "$INTDTFILE" "$WANTGPNG" "$AID" "$GAMENAME" "$HAVEPA"
				else
					writelog "WARN" "${FUNCNAME[0]} - Not enough arguments passes to create a desktopfile - got '$*'"
				fi
			else
				writeDesktopFile "$AID" "$INTDTFILE" "$WANTGPNG"
			fi
		fi
	fi

	# set desktop file mode from command line:
	if [ -f "$INTDTFILE" ] && [ "$CREATEDESKTOPICON" -eq 0 ] && [ -n "$2" ]; then
		CREATEDESKTOPICON="$2"
	fi

	# copy the desktop file to the system
	if [ -f "$INTDTFILE" ] && [ "$CREATEDESKTOPICON" -ne 0 ]; then
		if [ "$CREATEDESKTOPICON" -eq "1" ] || [ "$CREATEDESKTOPICON" -eq "3" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Creating desktop icon for '$AID' on the desktop"
			cp "$INTDTFILE" "$HOME/Desktop/" 2>/dev/null
		fi

		if [ "$CREATEDESKTOPICON" -eq "2" ] || [ "$CREATEDESKTOPICON" -eq "3" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Creating desktop icon for '$AID' for the desktop application menu"
			cp "$INTDTFILE" "$HOME/.local/share/applications/" 2>/dev/null
		fi
	fi
}

function getOwnedHexAids {
	if [ -z "$PIVDF" ]; then
		setSteamPaths
	fi

	if [ -f "$PIVDF" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Searching in '$PIVDF' for HexAids"
		HAIDS="61707069647300023000"
		"$XXD" -c "$(wc -c "$PIVDF")" -p "$PIVDF" | sed -E "s:$HAIDS:\n$HAIDS:g" | grep -o "$HAIDS.\{0,6\}" | sed "s:$HAIDS::"
	else
		writelog "SKIP" "${FUNCNAME[0]} - '$PIVDF' not found"
	fi
}

function getOwnedAids {
	mkProjDir "$STLSHM"
	OAIDLIST="$STLSHM/owned-aids.txt"
	if [ ! -f "$OAIDLIST" ]; then
		while read -r line; do
			getAidFromHexAid "$line" >> "$OAIDLIST"
		done <<< "$(getOwnedHexAids)"

		sort -n -u "$OAIDLIST" -o "$OAIDLIST"
	fi

	if [ -f "$OAIDLIST" ]; then
		cat "$OAIDLIST"
	fi
}

function getAidFromHexAid {
	unset REVAID
	while read -r line; do
		REVAID="$REVAID${line}"
	done <<< "$(fold -2 <<< "$1" | tac)"
	printf "%d\n" "0x$REVAID"
}

function getHexAidForAid {
	unset HEXAID
	if [ -f "$GEMETA/$1.conf" ]; then
		loadCfg "$GEMETA/$1.conf" X
	fi

	if [ -n "$HEXAID" ]; then
		if [ -z "$2" ]; then
			echo "$HEXAID"
		fi
	else
		HXTST="$(printf '%x\n' "$1" | fold -w2 | tail -n1)"

		if [ "${#HXTST}" -eq 1 ]; then
			SHEX1="$(printf '0%x\n' "$1" | fold -w2 | tac)";
		else
			SHEX1="$(printf '%x\n' "$1" | fold -w2 | tac)";
		fi

		while read -r line; do
			HEXAID="$HEXAID${line}"
		done <<< "$SHEX1"

		if [ -z "$2" ]; then
			echo "$HEXAID"
		fi
		touch "$FUPDATE"
		touch "$GEMETA/$1.conf"
		updateConfigEntry "HEXAID" "$HEXAID" "$GEMETA/$1.conf"
	fi
}

function getRawAppIDInfo {
	AIIDRAW="$STLAPPINFOIDDIR/${1}.bin"

	if [ -z "$FAIVDF" ]; then
		setSteamPaths
	fi

	if [ -z "$LOGRAWINFO" ]; then
		LOGRAWINFO=1
	fi

	if [ -s "$AIIDRAW" ] && [ -z "$2" ]; then
		if [ -z "$LOGRAWINFO" ]; then
			LOGRAWINFO=1
		fi

		if [ "$LOGRAWINFO" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found raw $APIN file '$AIIDRAW'"
		fi
	else
		if [ -n "$2" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Updating raw $APIN file '$AIIDRAW'"
		fi

		if [ ! -s "$AIIDRAW" ]; then
			rm "$AIIDRAW" 2>/dev/null
		fi

		HEXAID="$(getHexAidForAid "$1")"
		HAID="02617070696400"

		SHMAIVDF="$STLSHM/${AIVDF//\.vdf/\.hex}"
		if [ ! -f "$SHMAIVDF" ]; then
			"$XXD" -c "$(wc -c "$FAIVDF")" -p "$FAIVDF" "$SHMAIVDF"
		fi
		sed -E "s:$HAID:\n$HAID:g" "$SHMAIVDF" | grep "^${HAID}${HEXAID}00" | "$XXD" -r -p - "$AIIDRAW"
	fi
}

function getAppInfoData {
	# $1=AID; $2=category; optional $3=skip stdout; optional $4=force writing metadata again
	if [ -n "$2" ] && [ "$STLPLAY" -eq 0 ]; then
		AILIST="$GLOBALMISCDIR/$AITXT"
		if ! [ -f "$AILIST" ]; then
			SCRIPTDIR="$( realpath "$0" )"
			SCRIPTDIR="${SCRIPTDIR%/*}"

			writelog "INFO" "${FUNCNAME[0]} - No '$AITXT' found in Global Misc Dir '$GLOBALMISCDIR', checking for one in the script dir '$SCRIPTDIR'"
			if [ -f "$SCRIPTDIR/misc/$AITXT" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Found '$AITXT' in '$SCRIPTDIR/misc' - Using this"
				AILIST="$SCRIPTDIR/misc/$AITXT"
			else
				writelog "INFO" "${FUNCNAME[0]} - Could not find '$AITXT' in script directory - giving up"
			fi
		fi

		if grep -q "$2" "$AILIST"; then

			UPDAT="${2^^}"
			unset "$UPDAT"

			if [ -f "$GEMETA/$1.conf" ]; then
				loadCfg "$GEMETA/$1.conf" X
			fi

			if [ -n "${!UPDAT}" ] && [ -z "$4" ]; then
				if [ -z "$3" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Got value '${!UPDAT}' for '$UPDAT' from '$GEMETA/$1.conf'"
					echo "${!UPDAT}"
				fi
			else
				LOGRAWINFO=0
				getRawAppIDInfo "$1"
				SRCHEX="$STLAPPINFOIDDIR/$1.bin"

				if [ -f "$SRCHEX" ]; then
					if [ -z "$3" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Retrieving data for '$2' from '$SRCHEX'"
					fi

					HVAR="$(echo -n "$2" | "$XXD" -ps)"
					unset HXOUT

					if [ "$2" == "metacritic_score" ]; then
						UPVALHX="$("$XXD" -c "$(wc -c "$SRCHEX")" -p "$SRCHEX" | sed -E "s:$HVAR:\n$HVAR:g" | grep "^$HVAR" | head -c "$(( $(wc -c <<< "$HVAR") + 3))" | tail -c2)"
						if [ -n "$UPVALHX" ]; then
							UPVAL="$((16#$UPVALHX))"
						fi
					else
						OCOUNT=0
						ZCOUNT=0
						while read -r line; do
							if [ "$line" == "01" ]; then
								OCOUNT=$((OCOUNT+1))
								if [[ "$OCOUNT" -eq 2 ]]; then
									break
								fi
							elif [ "$line" == "00" ] ; then
								ZCOUNT=$((ZCOUNT+1))
								if [[ "$ZCOUNT" -eq 2 ]]; then
									break
								fi
							else
								HXOUT="$HXOUT$line"
							fi
						done <<< "$("$XXD" -c "$(wc -c "$SRCHEX")" -p "$SRCHEX" | sed -E "s:01$HVAR:\n01$HVAR:g" | grep "^01$HVAR" | fold -2)"
						UPVALHX="${HXOUT//${HVAR}00/}"
						UPVALHX1="${UPVALHX//${HVAR}/}"
						UPVALHXO="${UPVALHX1%*00}"

						UPVAL="$("$XXD" -r -p - <<< "$UPVALHXO")"
					fi

					if [ -n "$UPVAL" ]; then
						touch "$FUPDATE"
						touch "$GEMETA/$1.conf"
						updateConfigEntry "$UPDAT" "$UPVAL" "$GEMETA/$1.conf"
						if [ -z "$3" ]; then
							echo "$UPVAL"
						fi
					fi
				fi
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - '$2' is not in '$AILIST'"
		fi
	fi
}

function writeAllAIMeta {
	if [ -n "$1" ] && [ "$1" -eq "$1" ]; then
		if [ -n "$2" ]; then
			rm "$STLAPPINFOIDDIR/${1}.bin" 2>/dev/null
		fi
		AILIST="$GLOBALMISCDIR/$AITXT"
		while read -r line; do
		 	getAppInfoData "$1" "$line" X "$2"
		done < "$GLOBALMISCDIR/$AITXT"
	else
		writelog "SKIP" "${FUNCNAME[0]} - need SteamAppId as arg 1"
	fi
}

function getGameIcon {
	function IcotoPng {
		if [ -x "$(command -v "$CONVERT" 2>/dev/null)" ]; then
			if [ -x "$(command -v "$IDENTIFY" 2>/dev/null)" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Determining largest ico in '$WANTGICO' using '$IDENTIFY'"
				LICO="$("$IDENTIFY" "$WANTGICO" | sort -n -k3 | tail -n1 | grep -oP '\[\K[^\]]+')"
			else
				writelog "INFO" "${FUNCNAME[0]} - Command '$IDENTIFY' not found, using first ico in '$WANTGICO'"
				LICO="0"
			fi

			if [ -z "$LICO" ] || [ "$LICO" -ne "$LICO" ] 2>/dev/null; then
				writelog "INFO" "${FUNCNAME[0]} - No specific ico found using the first one in '$WANTGICO'"
				LICO="0"
			fi

			if [ "$LICO" -eq "$LICO" ] 2>/dev/null; then
				writelog "INFO" "${FUNCNAME[0]} - Converting ico '$LICO' in '$WANTGICO' to '$WANTGPNG' using command: $CONVERT ${WANTGICO}[${LICO}] $WANTGPNG"
				"$CONVERT" "${WANTGICO}[${LICO}]" "$WANTGPNG"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Command '$CONVERT' not found, so converting '$WANTGICO' to '$WANTGPNG' is not possible - skipping"
		fi
	}

	function getIco {
		WANTGICO="$STLGICO/${AID}.ico"
		if [ -f "$WANTGICO" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Already have game ico '$WANTGICO'"
			IcotoPng
		else
			ICONAME="$(getAppInfoData "$AID" "clienticon")"
			if [ -n "$ICONAME" ]; then
				ICOPATH="$ICODIR/${ICONAME}.ico"
				if [ -f "$ICOPATH" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Found ico for '$AID' under '$ICOPATH'"
					cp "$ICOPATH" "$WANTGICO"
					IcotoPng
				else
					writelog "SKIP" "${FUNCNAME[0]} - Absolute path for Steam ico '$ICONAME' not valid - skipping"
				fi
			else
				writelog "SKIP" "${FUNCNAME[0]} - Could not find the icon name for '$AID' in $APIN - skipping"
			fi
		fi
	}

	function extractGZip {
		BIGGESTPNG="$("$UNZIP" -l "$WANTGZIP" | grep "\.png" | sort -n | tail -n1 | awk -F ':[0-9][0-9]   ' '{print $NF}')" # weak 'awk' might break here(?)
		if [ -n "$BIGGESTPNG" ];then
			writelog "INFO" "${FUNCNAME[0]} - Extracting biggest png file '$BIGGESTPNG' in archive '$WANTGZIP' to '$WANTGPNG'"
			writelog "INFO" "${FUNCNAME[0]} - $UNZIP -dqq ${WANTGPNG//.png} $WANTGZIP $BIGGESTPNG"
			"$UNZIP" -q -d "${WANTGPNG//.png}" "$WANTGZIP" "$BIGGESTPNG"
			mv "${WANTGPNG//.png}/$BIGGESTPNG" "$WANTGPNG"
			rm -rf "${WANTGPNG//.png}"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Could not determine the biggest png file in archive '$WANTGZIP'- skipping"
		fi
	}

	if [ -z "$AID" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Don't have a Steam gameid - skipping"
	else
		WANTGPNG="$STLGPNG/${AID}.png"
		if [ -f "$WANTGPNG" ]; then
			writelog "Info" "${FUNCNAME[0]} - Already have game icon png '$WANTGPNG' - nothing to to"
		else
			if [ -z "$ICODIR" ]; then
				setSteamPaths
			fi

			if [ ! -d "$ICODIR" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - Steam ico directory not found - skipping"
			else
				WANTGZIP="$STLGZIP/${AID}.zip"
				if [ -f "$WANTGZIP" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Already have game icon zip '$WANTGZIP' - extracting"
					extractGZip "$WANTGZIP" "$WANTGPNG"
				else
					writelog "INFO" "${FUNCNAME[0]} - Looking for icon hash in $APIN"

					LICONZIP="$(getAppInfoData "$AID" "linuxclienticon")"
					if [ -n "$LICONZIP" ]; then
						LICONPATH="$ICODIR/${LICONZIP}.zip"
						if [ -f "$LICONPATH" ]; then
							writelog "INFO" "${FUNCNAME[0]} - Found icon zip for '$AID' under '$LICONPATH'"
							cp "$LICONPATH" "$WANTGZIP"
							extractGZip "$WANTGZIP" "$WANTGPNG"
						else
							writelog "INFO" "${FUNCNAME[0]} - Absolute path for Steam icon zip '$LICONZIP' not valid - trying to find ico instead"
							getIco
						fi
					else
						writelog "SKIP" "${FUNCNAME[0]} - Could not find the icon zip for '$AID' in $APIN - trying to find ico instead"
						getIco
					fi
				fi
			fi
		fi
	fi
}

function getGameData {
	if ! grep -q ",${1}," <<< "$NOGAMES" && [ "$STLPLAY" -eq 0 ]; then
		DLPIC="$STLGHEADD/$1.jpg"
		if [ ! -f "$DLPIC" ] || [ ! -s "$DLPIC" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Downloading picture '$DLPIC' from '$STASSURL/$1/header.jpg'" "X" "$GGDLOG"
			getGamePic "$DLPIC" "$STASSURL/$1/header.jpg"
		fi

		DESTDTF="$STLGDESKD/$1.desktop"
		if [ ! -f "$DESTDTF" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Creating desktopfile '$DESTDTF'" "X" "$GGDLOG"
			writeDesktopFile "$1" "$DESTDTF" "$DLPIC"
		fi
	fi
}

function getParsableGameList {
	if [ -z "$SUSDA" ] || [ -z "$STUIDPATH" ]; then
		setSteamPaths
	fi
	if [ -d "$SUSDA" ]; then
		SC="$STUIDPATH/$SRSCV"
		APPI="Apps"
		APPO="StartMenuShortcutCheck"
		LIST="$(awk "/$APPI/,/$APPO/" "$SC" | grep -v "$APPI\|$APPO" | awk '{printf "%s+",$0} END {print ""}' | sed 's/"[0-9][0-9]/\n&/g')"
		LISTCNT="$(wc -l <<< "$LIST")"
		writelog "INFO" "${FUNCNAME[0]} - Found '$LISTCNT' parsable Game Entries in '$SC'"
		if [ "$LISTCNT" -eq 0 ]; then
			writelog "SKIP" "${FUNCNAME[0]} - No game found in any Steam collection"
		fi
		echo "$LIST"
	else
		writelog "SKIP" "${FUNCNAME[0]} - '$SUSDA' not found - this should not happen! - skipping"
	fi
}

function getInstalledGamesFromCollection {
	CAT="$1"

	if [ -n "$CAT" ]; then
		while read -r CATAID; do
		echo "$CATAID"
		done <<< "$(getParsableGameList | grep "\"$CAT\"" | sed "s:\"::g" | sort -n | cut -d '+' -f1)"
	fi
}

function listInstalledGameIDs {
	while read -r APPMA; do
		grep -Eo "[[:digit:]]*" <<< "${APPMA##*/}"
	done <<< "$(listAppManifests)"
}

function getGameDataForInstalledGames {
	if [ "$(listInstalledGameIDs | wc -l)" -eq 0 ]; then
		writelog "SKIP" "${FUNCNAME[0]} - No installed games found!"
	else
		while read -r CATAID; do
			if [ -n "$CATAID" ]; then
				getGameData "$CATAID"
			fi
		done <<< "$(listInstalledGameIDs)"
	fi
}

function listSteamShortcutGameIDs {
	if haveAnySteamShortcuts ; then
		while read -r SCVDFE; do
			parseSteamShortcutEntryAppID "$SCVDFE"
		done <<< "$( getSteamShortcutHex )"
	else
		writelog "SKIP" "${FUNCNAME[0]} - No Steam shortcuts found!"
	fi
}

function checkSGDbApi {
	if [ -z "$SGDBAPIKEY" ] || [ "$SGDBAPIKEY" == "$NON" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - No SteamGrid Api Key found - Get one at 'https://www.steamgriddb.com/profile/preferences/api/' (requires a SteamGridDB account) and see the SteamGridDB wiki page for guidance on how to supply the API key."
		writelog "SKIP" "${FUNCNAME[0]} - and save it in the Global Config ('SGDBAPIKEY')"
		return 1
	else
		return 0
	fi
}

## Generic function to fetch some artwork from SteamGridDB based on an endpoint
## TODO: Steam only officially supports PNGs, test to see if WebP works when manually copied, and if it doesn't, we should try to only download PNG files
## TODO: Add max filesize option? Some artworks are really big, we should skip ones that are too large (though this may mean many animated APNG artworks will get skipped, because APNG can be huge)
function downloadArtFromSteamGridDB {
	if checkSGDbApi && [ "$STLPLAY" -eq 0 ]; then
		# Required
		SEARCHID="$1"  # ID to search on (should be either Steam AppID or Game ID, but we just pass it to the endpoint given)
		SEARCHENDPOINT="$2"  # Endpoint which should either be an endpoint for Steam games (Steam AppID endpoint) or Non-Steam Games (SGDB Game ID Endpoint)
		SGDBFILENAME="${3:-SEARCHID}"  # Name to give to file i.e. "124123p.png" (can't use ${SEARCHID}${SUFFIX} because SearchID may not be the AppID) -- Defaults to just using passed AppID

		# Optional
		SEARCHSTYLES="$4"
		SEARCHDIMS="$5"
		SEARCHTYPES="$6"
		SEARCHNSFW="$7"
		SEARCHHUMOR="$8"
		SEARCHEPILEPSY="$9"

		SGDBHASFILE="${10:-SGDBHASFILE}"  # Option to override action to take when file already exists
		FORCESGDBDLTOSTEAM="${11}"  # Option to force downloading artwork to Steam Grid folder

		SGDB_ENDPOINT_STR="${SEARCHENDPOINT}/$(echo "$SEARCHID" | awk '{print $1}' | paste -s -d, -)?"
		# Only include query params if provided
		# e.g.: "?styles=${SEARCHSTYLES}&dimensions=${SEARCHDIMS}&types=${SGDBTYPES}&nsfw=${SEARCHNSFW}&humor=${SEARCHHUMOR}"
		if [ -n "$SEARCHSTYLES" ]; then
			SGDB_ENDPOINT_STR+="&styles=${SEARCHSTYLES}"
		fi
		if [ -n "$SEARCHDIMS" ]; then
			SGDB_ENDPOINT_STR+="&dimensions=${SEARCHDIMS}"
		fi
		if [ -n "$SEARCHTYPES" ]; then
			SGDB_ENDPOINT_STR+="&types=${SEARCHTYPES}"
		fi
		if [ -n "$SEARCHNSFW" ]; then
			SGDB_ENDPOINT_STR+="&nsfw=${SEARCHNSFW}"
		fi
		if [ -n "$SEARCHHUMOR" ]; then
			SGDB_ENDPOINT_STR+="&humor=${SEARCHHUMOR}"
		fi
		if [ -n "$SEARCHEPILEPSY" ]; then
			SGDB_ENDPOINT_STR+="&epilepsy=${SEARCHEPILEPSY}"
		fi

		writelog "INFO" "${FUNCNAME[0]} - Outgoing SteamGridDB endpoint is: $SGDB_ENDPOINT_STR"

		# TODO break into reusable function for both this and `getSGDBGameIDFromTitle`?
		# If the whole batch has no grids we get a 404 and wget gives an error. --content-on-error ensures we still get the response json and the following logic still works
		RESPONSE="$("$WGET" --timeout="${SGDBTIMEOUT}" --tries="${SGDBRETRIES}" --content-on-error --header="Authorization: Bearer $SGDBAPIKEY" -q "$SGDB_ENDPOINT_STR" -O - 2> >(grep -v "SSL_INIT"))"
		if ! "$JQ" -e '.success' 1> /dev/null <<< "$RESPONSE"; then
			writelog "INFO" "${FUNCNAME[0]} - The server response wasn't 'success' for this batch of requested games."
		fi

		# catch single grid without downloads
		RESPONSE_LENGTH=$("$JQ" '.data | length' <<< "$RESPONSE")
		if [ "$RESPONSE_LENGTH" = 0 ]; then
			writelog "INFO" "${FUNCNAME[0]} - No grid found to download - maybe loosen filters?"
			echo "Could not find artwork on SteamGridDB to save with filename '$SGDBFILENAME' -- Check the log for details"
		fi

		# TODO: This could be handled by the http return value - 200 is single-part - 207 is multi-part
		# Rewrite response object to fit the following loop if the response isn't multi-part
		if "$JQ" -e ".data[0].url" 1> /dev/null <<< "$RESPONSE"; then
			RESPONSE="{\"success\":true,\"data\":[$RESPONSE]}"
			RESPONSE_LENGTH=1
		fi

		for i in $(seq 0 $(("$RESPONSE_LENGTH" - 1))); do
			# match the current json array member against the appid list, this assumes we get the same order back we put in before
			if ! "$JQ" -e ".data[$i].success" 1> /dev/null <<< "$RESPONSE"; then
				writelog "INFO" "${FUNCNAME[0]} - The server response for '$SEARCHID' wasn't 'success'"
			fi
			if ! URLSTR=$("$JQ" -e -r ".data[$i].data[0].url" <<< "$RESPONSE"); then
				writelog "INFO" "${FUNCNAME[0]} - No grid found to download for '$SEARCHID' - maybe loosen filters?"
			fi

			GRIDDLURL="${URLSTR//\"}"
			if grep -q "^https" <<< "$GRIDDLURL"; then
				DLSRC="${GRIDDLURL//\"}"

				if [ "$SGDBDLTOSTEAM" -eq 1 ] || [ "$FORCESGDBDLTOSTEAM" -eq 1 ]; then
					if [ -z "$SUSDA" ]; then
						setSteamPaths
					fi
					if [ -d "$SUIC" ]; then
						GRIDDLDIR="${SUIC}/grid"
					fi
				else
					GRIDDLDIR="$STLDLDIR/steamgriddb"
				fi

				mkProjDir "$GRIDDLDIR"
				DLDST="${GRIDDLDIR}/${SGDBFILENAME}.${GRIDDLURL##*.}"  # Makes filename like <appid>.<org_extension>, which could be something like "70_logo.png" (with full path preceding this, so something like "~/Games/Grids/Half-Life/70_logo.png")
				STARTDL=1

				if [ -f "$DLDST" ]; then
					if [ "$SGDBHASFILE" == "skip" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Download of existing file is set to '$SGDBHASFILE' - doing nothing"
						STARTDL=0
					elif [ "$SGDBHASFILE" == "backup" ]; then
						BACKDIR="${GRIDDLDIR}/backup"
						mkProjDir "$BACKDIR"
						writelog "INFO" "${FUNCNAME[0]} - Backup existing file into '$BACKDIR', because SGDBHASFILE is set to '$SGDBHASFILE'"
						mv "$DLDST" "$BACKDIR"
					elif [ "$SGDBHASFILE" == "replace" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Replacing existing file '$DLDST', because SGDBHASFILE is set to '$SGDBHASFILE'"
						rm "$DLDST" 2>/dev/null
					fi
				fi

				if [ "$STARTDL" -eq 1 ]; then
					dlCheck "$DLSRC" "$DLDST" "X" "Downloading '$DLSRC' to '$DLDST'"
				fi
			else
				writelog "INFO" "${FUNCNAME[0]} - No grid found to download for '$SEARCHID' - maybe loosen filters?"
			fi
		done
	fi
}

# Takes in an Steam AppID or list of Steam AppIDs and downloads (hero, logo boxart) for each one - only supports Steam AppIDs as we can't easily map SteamGridDB Game IDs and Non-Steam Game AppIDs
# For Steam games, the AppID is the ID we search on; for Non-Steam Games, we search on a specific Game ID
# In future, we could make a separate function for this
function getSteamGridDBArtwork {
	# Download artwork with given parameters for each ID passed in
	# Split into batches of 100 games - too many and cloudflare blocks requests because of a too big header file
	while mapfile -t -n 100 ary && ((${#ary[@]})); do
		SGDBSEARCHAID=$(printf '%s\n' "${ary[@]}")

		commandlineGetSteamGridDBArtwork --search-id="$SGDBSEARCHAID" --steam
	done <<< "${1}"
}

# GUI frontend for below 'commandlineGetSteamGridDBArtwork'
function getSteamGridDBArtworkGUI {
	FSGDBAWFILENAMEAPPID="$1"
	AID="$FSGDBAWFILENAMEAPPID"  # AID needed for setShowPic

	FSGDBAW_HEADERTITLE="$( getTitleFromID "$AID" "1" ) ($AID)"  # Display title and AppID for clarity in case showPic is not present/unclear

	writelog "INFO" "${FUNCNAME[0]} - Starting the Gui for SteamGridDB Artwork selection for '$AID'"

	export CURWIKI="$PPW/SteamGridDB"
	TITLE="${PROGNAME}-$FSGDBA"
	pollWinRes "$TITLE"
	setShowPic

	# AppID passed from commandline is used as --filename-appid
	# User can provide Steam AppID, SteamGridDB Game ID, or Game Name (used to attempt to fetch SteamGridDB Game ID)
	# - Steam AppID is prioritised if provided
	# - commandlineGetSteamGridDBArtwork is set to fall back to SteamGridDB Game ID if Game Name doesn't return anything, so passing both is fine
	# SGDBHASFILE will use the global option by default and populate the dropdown with the relevant option, just like the command does
	# It will use the Global Menu default, but also allows the user to specify a different action this time
	#
	# FSGDBAW = Fetcch SteamGridDB ArtWork :-)
	FSGDBAWGUISET="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --form --scroll --center --on-top "$WINDECO" \
	--title="$TITLE" --separator="|" --image="$SHOWPIC" \
	--text="$(spanFont "$(strFix "$GUI_FSGDBAW" "$FSGDBAW_HEADERTITLE")" "H")\n${DESC_FSGDBAW}" \
	--field=" ":LBL " " \
	--field="$GUI_FSGDBAWAPPID!$DESC_FSGDBAWAPPID ('FSGDBAWAPPID')" "${FSGDBAWAPPID/#-/ -}" \
	--field="$GUI_FSGDBAWGAMEID!$DESC_FSGDBAWGAMEID ('FSGDBAWGAMEID')" "${FSGDBAWGAMEID/#-/ -}" \
	--field="$GUI_FSGDBAWSEARCHNAME!$DESC_FSGDBAWSEARCHNAME ('FSGDBAWSEARCHNAME')" "${FSGDBAWSEARCHNAME/#-/ -}" \
	--field="$GUI_SGDBHASFILE!$DESC_SGDBHASFILE ('FSGDBAWHASFILE')":CB "$(cleanDropDown "${SGDBHASFILE/#-/ -}" "${SGDBHASFILEOPTS}")" \
	--field="$GUI_FSGDBAWAPPLYARTWORK!$DESC_FSGDBAWAPPLYARTWORK ('FSGDBAWAPPLYARTWORK')":CHK "1" \
	--button="$BUT_CAN":0 --button="$BUT_DONE":2 "$GEOM")"

	case $? in
		0) writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_CAN'" ;;
		2)
			{
				writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_DONE'"
				mapfile -d "|" -t -O "${#FSGDBAWARR[@]}" FSGDBAWARR < <(printf '%s' "$FSGDBAWGUISET")

				FSGDBAWAPPID="${FSGDBAWARR[1]}"
				FSGDBAWGAMEID="${FSGDBAWARR[2]}"
				FSGDBAWSEARCHNAME="${FSGDBAWARR[3]}"
				FSGDBAWHASFILE="--${FSGDBAWARR[4]}-existing"  # i.e. turns 'replace' into '--replace-existing'
				FSGDBAWAPPLYARTWORK="$( retBool "${FSGDBAWARR[5]}" )"

				if [ -z "${FSGDBAWAPPID}" ] && [ -z "${FSGDBAWGAMEID}" ] && [ -z "${FSGDBAWSEARCHNAME}" ]; then
					writelog "ERROR" "${FUNCNAME[0]} - You must pass at least a Steam AppID, SteamGridDB Game ID, or SteamGridDB Game Name"
					echo "You must pass at least a Steam AppID, SteamGridDB Game ID, or SteamGridDB Game Name"
					notiShow "$NOTY_FSGDBAWINVALID"
					return
				fi

				notiShow "$( strFix "$NOTY_FSGDBAW" "$FSGDBAW_HEADERTITLE" )"

				FSGDBAWGAMETYPEFLAG="--nonsteam"  # Default to non-steam, since Game ID and Game Name will use SGDB /game/ endpoint
				if [ -n "$FSGDBAWAPPID" ]; then
					FSGDBAWGAMETYPEFLAG="--steam"  # Only use SGDB Steam game endpoint if we pass a Steam AppID to search for artwork on
					FSGDBAWSEARCHID="${FSGDBAWAPPID}"
				else
					FSGDBAWSEARCHID="${FSGDBAWGAMEID}"
				fi

				FSGDBAWAPPLYARTWORKFLAG="--apply"  # Checkbox is defaulted to 1 (enabled), so default flag to '--apply'
				if [ "$FSGDBAWAPPLYARTWORK" -eq 0 ]; then
					FSGDBAWAPPLYARTWORKFLAG="--no-apply"
				fi

				# Execute actual fetching of artwork, could probably put notifier here
				writelog "INFO" "${FUNCNAME[0]} - Executing 'commandlineGetSteamGridDBArtwork --search-id=\"${FSGDBAWGAMEID}\" --search-name=\"${FSGDBAWSEARCHNAME}\" --filename-appid=\"${FSGDBAWFILENAMEAPPID}\" \"${FSGDBAWHASFILE}\" \"${FSGDBAWGAMETYPEFLAG}\"'"
				commandlineGetSteamGridDBArtwork --search-id="${FSGDBAWSEARCHID}" --search-name="${FSGDBAWSEARCHNAME}" --filename-appid="${FSGDBAWFILENAMEAPPID}" "${FSGDBAWHASFILE}" "${FSGDBAWAPPLYARTWORKFLAG}" "${FSGDBAWGAMETYPEFLAG}"
			}
	esac
}

# Used to get either Steam or Non-Steam artwork depending on a flag -- Used internally and for commandline usage
function commandlineGetSteamGridDBArtwork {
	SGDBENDPOINTTYPE="steam" # assume Steam game by default (search Steam AppID endpoint)
	GSGDBA_HASFILE="$SGDBHASFILE"  # Optional override for how to handle existinf file (downloadArtFromSteamGridDB defaults to '$SGDBHASFILE')
	GSGDBA_APPLYARTWORK="$SGDBDLTOSTEAM"
	GSGDBA_SEARCHNAME=""
	GSGDBA_FOUNDGAMEID=""  # ID found from SteamGridDB endpoint using GSGDBA_SEARCHNAME
	for i in "${@}"; do
		case $i in
			--search-id=*)  # ID to hit SteamGridDB API endpoint with (for Steam games this is the AppID which we will also use as filename)
				GSGDBA_APPID="${i#*=}"
				GSGDBA_FILENAME="${GSGDBA_APPID}"  # By default, file will be named <appid> with a suffix for each grid type (only non-steam games need this overridden since they search on Game ID and not Steam AppID)
				shift ;;
			--search-name=*)
				GSGDBA_SEARCHNAME="${i#*=}"  # Optional SteamGridDB Game Name -- Will use this to try and find matching SteamGridDB Game Art
				shift ;;
			--steam)
				SGDBENDPOINTTYPE="steam"  # used to generate the correct endpoint to hit, defaults to /heroes/game but this will make it heroes/steam
				shift ;;
			--nonsteam)
				SGDBENDPOINTTYPE="game"
				shift ;;
			--filename-appid=*)
				GSGDBA_FILENAME="${i#*=}"  # AppID to use in filename (Non-Steam Games need a different AppID)
				shift ;;
			## Override Global Menu setting for how to handle existing artwork
			## in case user wants to replace all existing artwork, default STL setting is 'skip' and will only copy files over to grid dir if they don't exist, so user can easily fill in missing artwork only)
			--replace-existing)
				GSGDBA_HASFILE="replace"
				shift ;;
			--backup-existing)
				GSGDBA_HASFILE="backup"
				shift ;;
			--skip-existing)
				GSGDBA_HASFILE="skip"
				shift ;;
			## Flag to force downloading to SteamGridDB folder (used for addNonSteamGame internally)
			--apply)
				GSGDBA_APPLYARTWORK="1"
				shift ;;
			--no-apply)
				GSGDBA_APPLYARTWORK="0"
				shift ;;
		esac
	done

	# If we pass a name to search on and we get a Game ID back from SteamGridDB, set this as the ID to search for artwork on
	if [ -n "$GSGDBA_SEARCHNAME" ]; then
		if [ -n "$GSGDBA_FILENAME" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Searching SteamGridDB for game name matching '$GSGDBA_SEARCHNAME'"
			GSGDBA_FOUNDGAMEID="$( getSGDBGameIDFromTitle "$GSGDBA_SEARCHNAME" )"
			if [ -n "$GSGDBA_FOUNDGAMEID" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Found game name matching '$GSGDBA_SEARCHNAME' with Game ID '$GSGDBA_FOUNDGAMEID' -- Using this Game ID to search for SteamGridDB Game Art"
				GSGDBA_APPID="$GSGDBA_FOUNDGAMEID"
				writelog "INFO" "${FUNCNAME[0]} - Forcing endpoint type as --nonsteam since we're searching with a found SteamGridDB Game ID"
				SGDBENDPOINTTYPE="game"
			fi
		else
			writelog "ERROR" "${FUNCNAME[0]} - You must provide a filename AppID when searching with SteamGridDB Game Name"
			echo "You must provide a filename AppID when searching with SteamGridDB Game Name"
		fi
	fi

	SGDBSEARCHENDPOINT_HERO="${BASESTEAMGRIDDBAPI}/heroes/${SGDBENDPOINTTYPE}"
	SGDBSEARCHENDPOINT_LOGO="${BASESTEAMGRIDDBAPI}/logos/${SGDBENDPOINTTYPE}"
	SGDBSEARCHENDPOINT_BOXART="${BASESTEAMGRIDDBAPI}/grids/${SGDBENDPOINTTYPE}"	 # Grid endpoint is used for Boxart and Tenfoot, which SteamGridDB counts as vertical/horizontal grids respectively

	# Download Hero, Logo, Boxart, Tenfoot from SteamGridDB from given endpoint using given AppID
	# On SteamGridDB tenfoot called horizontal Steam grid, so fetch it by passing specific dimensions matching this -- Users can override this, but default is what SteamGridDB expects for the tenfoot sizes
	if [ "$SGDBDLHERO" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Downloading Hero artwork, because SGDBDLHERO is '$SGDBDLHERO'"
		downloadArtFromSteamGridDB "$GSGDBA_APPID" "$SGDBSEARCHENDPOINT_HERO" "${GSGDBA_FILENAME}_hero" "$SGDBHEROSTYLES" "$SGDBHERODIMS" "$SGDBHEROTYPES" "$SGDBHERONSFW" "$SGDBHEROHUMOR" "$SGDBHEROEPILEPSY" "$GSGDBA_HASFILE" "$GSGDBA_APPLYARTWORK"
	fi
	if [ "$SGDBDLLOGO" -eq 1 ]; then
		# Logo doesn't have dimensions, so it's left intentionally blank
		writelog "INFO" "${FUNCNAME[0]} - Downloading Logo artwork, because SGDBDLLOGO is '$SGDBDLLOGO'"
		downloadArtFromSteamGridDB "$GSGDBA_APPID" "$SGDBSEARCHENDPOINT_LOGO" "${GSGDBA_FILENAME}_logo" "$SGDBLOGOSTYLES" "" "$SGDBLOGOTYPES" "$SGDBLOGONSFW" "$SGDBLOGOHUMOR" "$SGDBLOGOEPILEPSY" "$GSGDBA_HASFILE" "$GSGDBA_APPLYARTWORK"
	fi
	if [ "$SGDBDLBOXART" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Downloading Boxart (Steam Vertical Grid) artwork, because SGDBDLBOXART is '$SGDBDLBOXART'"
		downloadArtFromSteamGridDB "$GSGDBA_APPID" "$SGDBSEARCHENDPOINT_BOXART" "${GSGDBA_FILENAME}p" "$SGDBBOXARTSTYLES" "$SGDBBOXARTDIMS" "$SGDBBOXARTTYPES" "$SGDBBOXARTNSFW" "$SGDBBOXARTHUMOR" "$SGDBBOXARTEPILEPSY" "$GSGDBA_HASFILE" "$GSGDBA_APPLYARTWORK"
	fi
	if [ "$SGDBDLTENFOOT" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Downloading Tenfoot (Steam Horizontal Grid) artwork, because SGDBDLTENFOOT is '$SGDBDLTENFOOT'"
		downloadArtFromSteamGridDB "$GSGDBA_APPID" "$SGDBSEARCHENDPOINT_BOXART" "${GSGDBA_FILENAME}" "$SGDBTENFOOTSTYLES" "$SGDBTENFOOTDIMS" "$SGDBTENFOOTTYPES" "$SGDBTENFOOTNSFW" "$SGDBTENFOOTHUMOR" "$SGDBTENFOOTEPILEPSY" "$GSGDBA_HASFILE" "$GSGDBA_APPLYARTWORK"
	fi

	echo "$GSGDBA_APPID" > "$NOSTSGDBIDSHMFILE"  # Store ID in case other functions need it (i.e. addNonSteamGame) -- Little hacky, would rather return this somehow...
}

function getGridsForOwnedGames {
	if checkSGDbApi; then
		while read -r OWNSTGAAID; do
			getSteamGridDBArtwork "$OWNSTGAAID"
		done <<< "$( getOwnedAids )"
	fi
}
function getGridsForInstalledGames {
	if checkSGDbApi; then
		if [ "$(listInstalledGameIDs | wc -l)" -gt 0 ]; then
			while read -r INSTGAAID; do
				getSteamGridDBArtwork "$INSTGAAID"
			done <<< "$( listInstalledGameIDs )"
		else
			writelog "SKIP" "${FUNCNAME[0]} - No installed games found!"
		fi
	fi
}
function getGridsForNonSteamGames {
	if ! haveAnySteamShortcuts ; then
		writelog "SKIP" "${FUNCNAME[0]} - No Non-Steam Games found, skipping"
		echo "No Non-Steam Games found, not downloading grids"

		return
	fi

	if checkSGDbApi; then
		# Get Non-Steam Game Name + ID
		writelog "INFO" "${FUNCNAME[0]} - Fetching artwork for all Non-Steam Games"

		# Back up shortcuts.vdf in case something goes wrong...
		SCPATH="$STUIDPATH/config/$SCVDF"
		cp "$SCPATH" "${SCPATH//.vdf}_${PROGNAME}_backup.vdf" 2>/dev/null

		while read -r SCVDFE; do
			SVDFEAID="$( parseSteamShortcutEntryAppID "$SCVDFE" )"
			SVDFENAME="$( parseSteamShortcutEntryAppName "$SCVDFE" )"

			writelog "INFO" "${FUNCNAME[0]} - Updating artwork for game '$SVDFENAME ('$SVDFEAID')'"
			echo "Updating artwork for game '$SVDFENAME ('$SVDFEAID')'"

			commandlineGetSteamGridDBArtwork --search-name="$SVDFENAME" --filename-appid="$SVDFEAID" --nonsteam

			CMDLINEGETSGDBARTAID="$( cat "$NOSTSGDBIDSHMFILE" )"
			getSteamGridDBNonSteamIcon "$SVDFEAID" "$CMDLINEGETSGDBARTAID"
			SVDFEICON="$( findNonSteamGameIcon )"  # Return icon path )"
			if [ -n "$SVDFEICON" ]; then  # Need this check because sometimes we don't get anything back from SGDB i.e. unknown name
				writelog "INFO" "${FUNCNAME[0]} - Found icon for game '${SVDFENAME} (${SVDFEAID})' at '$SVDFEICON'"
				editSteamShortcutEntry "$SVDFEAID" "icon" "$SVDFEICON"
			fi
		done <<< "$( getSteamShortcutHex )"
	fi
}

# Search SteamGridDB endpoint using game title and return the first (best match) Game ID
function getSGDBGameIDFromTitle {
	SGDBSEARCHNAME="$1"

	if [ -n "$SGDBSEARCHNAME" ]; then
		SGDBSEARCHENDPOINT="${BASESTEAMGRIDDBAPI}/search/autocomplete/${SGDBSEARCHNAME}"
		if checkSGDbApi; then
			SGDBSEARCHNAMERESP="$( "$WGET" --timeout="${SGDBTIMEOUT}" --tries="${SGDBRETRIES}" --content-on-error --header="Authorization: Bearer $SGDBAPIKEY" -q "$SGDBSEARCHENDPOINT" -O - 2>  >(grep -v "SSL_INIT") )"
			if "$JQ" -e '.success' 1> /dev/null <<< "$SGDBSEARCHNAMERESP"; then
				if [ "$( "$JQ" '.data | length' <<< "$SGDBSEARCHNAMERESP" )" -gt 0 ]; then
					SGDBSEARCH_FOUNDNAME="$( "$JQ" '.data[0].name' <<< "$SGDBSEARCHNAMERESP" )"
					SGDBSEARCH_FOUNDGAID="$( "$JQ" '.data[0].id' <<< "$SGDBSEARCHNAMERESP" )"

					writelog "INFO" "${FUNCNAME[0]} - Searched SteamGridDB for name '$SGDBSEARCHNAME'"
					writelog "INFO" "${FUNCNAME[0]} - SteamGridDB return Game ID '$SGDBSEARCH_FOUNDGAID' and name '$SGDBSEARCH_FOUNDNAME'."
					echo "$SGDBSEARCH_FOUNDGAID"
				else
					writelog "WARN" "${FUNCNAME[0]} - No game name was returned for this request -- Check if this game name works on SteamGridDB's website search"
				fi
			else
				writelog "WARN" "${FUNCNAME[0]} - The server response wasn't 'success' for this request."
			fi
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - No game name given."
		echo "No game name given."
	fi
}

# Remove artwork for single game based on AppID, or all grids
function removeSteamGrids {
	RMGAMEGRID="${1,,}"  # Should be Steam AppID or "all"
	SGGRIDDIR="${STUIDPATH}/config/grid"

	if [ -z "$1" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - No parameter given, cannot remove artwork, skipping"
		echo "You must provide either a Steam AppID to remove artwork for, or specify 'all' to remove all game artwork"
	fi

	if [ "$1" == "all" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Removing grid artwork for all Steam games"
		echo "Removing grid artwork for all Steam games..."
		rmDirIfExists "${SGGRIDDIR}"
		mkdir "${SGGRIDDIR}"
		writelog "INFO" "${FUNCNAME[0]} - Finished removing grid artwork for all Steam games"
	else
		# Find any grid artwork for AppID -- Have to use find and use it on each artwork name because we don't want to match AppIDs which contain other AppIDs
		# i.e. searching for '140*' would return matches with '1402750' as well
		writelog "INFO" "${FUNCNAME[0]} - Removing any grid artwork for game with AppID '$1'"
		find "${SGGRIDDIR}" -name "${RMGAMEGRID}_hero.*" -exec rm {} \;  # Hero
		find "${SGGRIDDIR}" -name "${RMGAMEGRID}_logo.*" -exec rm {} \;  # Logo
		find "${SGGRIDDIR}" -name "${RMGAMEGRID}p.*" -exec rm {} \;  # Boxart
		find "${SGGRIDDIR}" -name "${RMGAMEGRID}.*" -exec rm {} \;  # Tenfoot
		find "${SGGRIDDIR}" -name "${RMGAMEGRID}_icon.*" -exec rm {} \;  # Icon (custom STL name for Non-Steam Games)
		writelog "INFO" "${FUNCNAME[0]} - Finishedd removing grid artwork for game with AppID '$1' -- Assuming 'find' found any artwork in the first place"
	fi

	echo "Finished removing grid artwork, restart Steam for the changes to fully take effect."
}

function getDataForAllGamesinSharedConfig {
	while read -r CATAID; do
		getGameData "$CATAID"
	done <<< "$(getParsableGameList | cut -d '+' -f1 | sed "s:\"::g" | grep "[0-9]" | sort -n)"
}

function getActiveSteamCollections {
	getParsableGameList | grep "\"tags\"" | awk -F '{+' '{print $NF}' | sed 's/\"/'$'\\\n/g' | sort -u | grep -i "^[a-z]"
}

function createCollectionMenus {
	# create launcher menu for all installed games
	DFIDIR="$DFDIR/installed"
	mkProjDir "$DFIDIR"

	if [ "$(listInstalledGameIDs | wc -l)" -eq 0 ]; then
		writelog "SKIP" "${FUNCNAME[0]} - No installed games found!"
	else
		while read -r CATGAME; do
			if [ -n "$CATGAME" ] && [ ! -h "$DFIDIR/$CATGAME.desktop" ] && [ -f "$STLGDESKD/$CATGAME.desktop" ]; then
				ln -s "$STLGDESKD/$CATGAME.desktop" "$DFIDIR"
			fi
		done <<< "$(listInstalledGameIDs)"
	fi

	# create launcher menu for collection '$1'
	if [ -n "$1" ] && [ "$1" == "update" ] && [ "$(find "$DFDIR" -name "*.desktop" | wc -l)" -gt 0 ]; then
		find "$DFDIR" -name "*.desktop" -exec rm {} \;
	fi

	if [ "$(find "$DFDIR" -name "*.desktop" | wc -l)" -eq 0 ]; then
		while read -r CAT; do
			DFCDIR="$DFDIR/$CAT"
			mkProjDir "$DFCDIR"
			while read -r CATGAME; do
				if [ -f "$STLGDESKD/$CATGAME.desktop" ]; then
					if [ ! -h "$DFCDIR/$CATGAME.desktop" ]; then
						ln -s "$STLGDESKD/$CATGAME.desktop" "$DFCDIR"
					fi
				fi
			done <<< "$(getInstalledGamesFromCollection "$CAT")"
		done < <(getActiveSteamCollections)
	fi
}

function listSteamLibraries {
	function getBIF {
		local REGEX='"(BaseInstallFolder[^"]*|path)"\s+"(.*)"\s*$'
		in="$1"
		if [[ $in =~ $REGEX ]] && [ -n "${BASH_REMATCH[2]}" ]; then
			echo "${BASH_REMATCH[2]}/$SA"
		else
			writelog "WARN" "${FUNCNAME[0]} - Failed to parse Base Install Folder from '$in'"
		fi
	}

	function listSLs {
		if [ -f "$CFGVDF" ] || [ -f "$LFVDF" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Searching appmanifest files in '$CFGVDF' and '$LFVDF'" "X" "$APPMALOG"
			while read -r BIF; do
				getBIF "$BIF"
			done <<< "$(grep "\"BaseInstallFolder\|\"path\"" "$CFGVDF" "$LFVDF" 2>/dev/null)" | sort -u
		else
			writelog "SKIP" "${FUNCNAME[0]} - Neither file CFGVDF '$CFGVDF' nor file LFVDF '$LFVDF' found - this should not happen! - skipping"
		fi
	}

	MAXAGE=360
	if [ ! -f "$STELILIST" ] || test "$(find "$STELILIST" -mmin +"$MAXAGE")"; then
		writelog "INFO" "${FUNCNAME[0]} - (Re-)creating '$STELILIST'"
		rm "$STELILIST" 2>/dev/null
		listSLs | sort -u > "$STELILIST"
	else
		writelog "SKIP" "${FUNCNAME[0]} - not recreating already available '$STELILIST'"
	fi
}

function setSteamLibraryPaths {
	while read -r lpath; do
		# Ignores library folders if they are not valid directories -- This var comes from Steam but apparently can still have invalid library folders, maybe Steam bug?
		if [ ! -d "$lpath" ]; then
			writelog "WARN" "${FUNCNAME[0]} - Library folder '$lpath' does not seem to be a valid directory, even though this comes from Steam itself - Ignoring, but please report if this is invalid or causes issues"
			continue
		fi

		if [ -z "$STEAM_COMPAT_LIBRARY_PATHS" ]; then
			STEAM_COMPAT_LIBRARY_PATHS="$lpath"  # This var should come from Steam, even the Proton script uses it
		else
			if ! grep -q "$lpath" <<< "$STEAM_COMPAT_LIBRARY_PATHS" ; then
				STEAM_COMPAT_LIBRARY_PATHS="$STEAM_COMPAT_LIBRARY_PATHS:$lpath"
			fi
		fi

		while read -r cmpath; do
			if [ -n "$cmpath" ]; then
				if [ -z "$STEAM_COMPAT_MOUNTS" ]; then
					STEAM_COMPAT_MOUNTS="$cmpath"
				else
					if ! grep -q "$cmpath" <<< "$STEAM_COMPAT_MOUNTS" ; then
						STEAM_COMPAT_MOUNTS="$STEAM_COMPAT_MOUNTS:$cmpath"
					fi
				fi

				if [ -z "$STEAM_COMPAT_TOOL_PATHS" ]; then
					STEAM_COMPAT_TOOL_PATHS="$cmpath"
				else
					if ! grep -q "$cmpath" <<< "$STEAM_COMPAT_TOOL_PATHS" ; then
						STEAM_COMPAT_TOOL_PATHS="$STEAM_COMPAT_TOOL_PATHS:$cmpath"
					fi
				fi
			fi
		done <<< "$(find "$lpath" -mindepth 2 -maxdepth 2 -type d \( -name "Proton" -o -name "$STEWOS" -o -name "${SLR}*" \))"
	done < "$STELILIST"

	export STEAM_COMPAT_LIBRARY_PATHS
	export STEAM_COMPAT_MOUNTS
	export STEAM_COMPAT_TOOL_PATHS

	writelog "INFO" "${FUNCNAME[0]} - STEAM_COMPAT_LIBRARY_PATHS set to '$STEAM_COMPAT_LIBRARY_PATHS'"
	writelog "INFO" "${FUNCNAME[0]} - STEAM_COMPAT_MOUNTS set to '$STEAM_COMPAT_MOUNTS'"
	writelog "INFO" "${FUNCNAME[0]} - STEAM_COMPAT_TOOL_PATHS set to '$STEAM_COMPAT_TOOL_PATHS'"
}

function listAppManifests {
	function findAppMa {
		if [ -d "$1" ]; then
			find "$1" -mindepth 1 -maxdepth 1 -type f -name "appmanifest_*.acf"
		fi
	}

	# getBIF takes a string containing a BaseInstallFolder or path and returns the
	function getBIF {
		# REGEX needs to be a variable, quoted string literals are not treated as regexps by test
		# BASH_REMATCH[1] will be the key (BaseInstallFolder* or path)
		# BASH_REMATCH[2] will be the value (the actual steam library folder)
		local REGEX='"(BaseInstallFolder[^"]*|path)"\s+"(.*)"\s*$'
		in="$1"

		# Test the regex against input, then print the extracted path to stdout
		if [[ $in =~ $REGEX ]] && [ -n "${BASH_REMATCH[2]}" ]; then
			echo "${BASH_REMATCH[2]}/$SA"
		else
			writelog "WARN" "${FUNCNAME[0]} - Failed to parse Base Install Folder from '$in'"
		fi
	}

	function listAllAppMas {
		if [ -d "$DEFSTEAMAPPS" ]; then
			findAppMa "$DEFSTEAMAPPS"
		else
			writelog "SKIP" "${FUNCNAME[0]} - '$DEFSTEAMAPPS' not found - this should not happen! - skipping"
		fi

		if [ -f "$CFGVDF" ] || [ -f "$LFVDF" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Searching appmanifest files in '$CFGVDF' and '$LFVDF'" "X" "$APPMALOG"
			while read -r BIF; do
				findAppMa "$(getBIF "$BIF")"
			done <<< "$(grep "\"BaseInstallFolder\|\"path\"" "$CFGVDF" "$LFVDF" 2>/dev/null)" | sort -u
		else
			writelog "SKIP" "${FUNCNAME[0]} - Neither file CFGVDF '$CFGVDF' nor file LFVDF '$LFVDF' found - this should not happen! - skipping"
		fi
	}

	listAllAppMas | sort -u
}

function createCollectionList {
	function listCAT {
		while read -r CATMENU; do
			echo "${CATMENU##*/}"
		done <<< "$(find "$DFDIR" -mindepth 1 -maxdepth 1 -type d)"
	}
	CATLIST="$(listCAT | sort -u | tr '\n' '!' | sed "s:^!::" | sed "s:!$::")"
}

function setGeom {
	GEOM="--geometry=${WINX}x${WINY}+${POSX}+${POSY}"
}

function pollWinRes {
	TITLE="$1"
	POSX=0
	POSY=0
	unset COLCOUNT

	if [ "$ONSTEAMDECK" -eq 1 ]; then
		SCREENRES="1280x800"
		WINX=1280
		WINY=800
		setGeom
	else
		SCREENRES="$(getScreenRes r)"
	fi

	if [ -z "$SCREENRES" ]; then  SCREENRES="any"; fi

	if [ "$FIXGAMESCOPE" -eq 0 ]; then  # skip this if FIXGAMESCOPE is 1 - so for now only if running in GameMode on the Steam Deck
		TEMPL="template"
		GAMEGUICFG="$STLGUIDIR/$SCREENRES/${AID}/${TITLE}.conf"
		TEMPLGUICFG="$STLGUIDIR/$SCREENRES/${TEMPL}/${TITLE}.conf"
		GLOBTEMPLGUICFG="$GLOBALSTLGUIDIR/$SCREENRES/${TEMPL}/${TITLE}.conf"

		mkProjDir "${GAMEGUICFG%/*}"
		mkProjDir "${TEMPLGUICFG%/*}"

		if [ -f "$TEMPLGUICFG" ] && [ ! -f "$GAMEGUICFG" ]; then
			loadCfg "$TEMPLGUICFG" X
			setGeom
			writelog "INFO" "${FUNCNAME[0]} - Using GEOM '$GEOM' from '$TEMPLGUICFG'" "$WINRESLOG"
		elif [ -f "$GLOBTEMPLGUICFG" ] && [ ! -f "$GAMEGUICFG" ]; then
			loadCfg "$GLOBTEMPLGUICFG" X
			setGeom
			writelog "INFO" "${FUNCNAME[0]} - Using GEOM '$GEOM' from '$GLOBTEMPLGUICFG'" "$WINRESLOG"
		elif [ -f "$GAMEGUICFG" ]; then
			loadCfg "$GAMEGUICFG" X
			setGeom
			writelog "INFO" "${FUNCNAME[0]} - Using GEOM '$GEOM' from '$GAMEGUICFG'" "$WINRESLOG"
		else
			touch "$GAMEGUICFG"
			echo "WINX=\"$WINX\"" > "$GAMEGUICFG"
			echo "WINY=\"$WINY\"" >> "$GAMEGUICFG"

			if [ -z "$GEOM" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Using harmless '--center' as variable 'GEOM', because there are multiple side-effects in yad if the string is empty"
				GEOM="--center"
			fi

			writelog "INFO" "${FUNCNAME[0]} - Creating initial '$GAMEGUICFG' with unused default values" "$WINRESLOG"
		fi
	fi

	if [ -n "$2" ]; then
		DEFCOL="$2"
	else
		DEFCOL=1
	fi

	if [ -z "$COLCOUNT" ]; then
		if [ "$ONSTEAMDECK" -eq 0 ]; then
			updateConfigEntry "COLCOUNT" "$DEFCOL" "$GAMEGUICFG"
		fi
		COLCOUNT="$DEFCOL"
	fi

	CURGUICFG="$GAMEGUICFG"
	export CURGUICFG="$CURGUICFG"

	if [ "$FIXGAMESCOPE" -eq 0 ]; then
		updateWinRes "$TITLE" "$GAMEGUICFG" "$TEMPLGUICFG" &
	fi
}

function openGameLauncher {
	function GLgui {
		LISTDIR="$DFDIR/$1"
		if [ -d "$LISTDIR" ] && [ "$(find "$LISTDIR" -name "*.desktop" | wc -l)" -ge 1 ]; then
			"$YAD" --f1-action="$F1ACTION" --icons --window-icon="$STLICON" --read-dir="$LISTDIR" "$WINDECO" --title="$TITLE" --single-click --keep-icon-size --center --compact --sort-by-name "$GEOM"
		else
			writelog "SKIP" "${FUNCNAME[0]} - No games found in '$LISTDIR' or directory itself not found"
		fi
	}

	if grep -q "update" <<< "$@"; then
		createCollectionMenus "update"
	else
		createCollectionMenus "dummy"
	fi

	if [ "$(find "$DFDIR" -name "*.desktop" | wc -l)" -eq 0 ]; then
		writelog "INFO" "${FUNCNAME[0]} - No desktop file found in '$DFDIR'"
		if [ "$1" == "auto" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found argument 'auto'. Automatically creating/downloading required data for all installed games found"
			getGameDataForInstalledGames
			if [ -n "$2" ] && [ "$2" == "update" ]; then
				createCollectionMenus "$2"
			else
				createCollectionMenus "dummy"
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - Either add argument 'auto' to automatically create/download required data or check '$PROGCMD --help'"
			exit
		fi
	fi

	export CURWIKI="$PPW/Game-Launcher"
	TITLE="${PROGNAME}-Launcher"
	pollWinRes "$TITLE"

	if [ -z "$1" ] || [ "$1" == "auto" ] || [ "$1" == "update" ]; then
		GLgui "installed"
	else
		if [ "$1" == "menu" ]; then
			createCollectionList
			CATMENU="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --center "$WINDECO" --form --scroll --separator="\n" --quoted-output \
			--text="$(spanFont "$GUI_CHOOSECAT" "H")" \
			--field="$GUI_CHOOSECATS":CB "$(cleanDropDown "installed" "$CATLIST")" \
			--title="$TITLE" "$GEOM"
			)"

			if [ -n "${CATMENU//\'}" ]; then
				GLgui "${CATMENU//\'}"
			fi
		elif [ "$1" == "last" ]; then
			if [ -f "$LASTRUN" ]; then
				PREVAID="$(grep "^PREVAID" "$LASTRUN" | cut -d '=' -f2)"
				if [ -n "$PREVAID" ]; then
					AID="${PREVAID//\"}"
					if [ -f "$STLGDESKD/$AID.desktop" ]; then
						writelog "INFO" "${FUNCNAME[0]} - '$1' selected, so opening splash for '$STLGDESKD/$AID.desktop'"
						DFCDIR="$DFDIR/last"
						mkProjDir "$DFCDIR"
						cp "$STLGDESKD/$AID.desktop" "$DFCDIR"
						GLgui "$1"
					else
						writelog "SKIP" "${FUNCNAME[0]} - '$1' selected, but file '$STLGDESKD/$AID.desktop' not found"
					fi
				fi
			fi
		elif [ -d "$DFDIR/$1" ]; then
			GLgui "$1"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Steam Collection '$1' not found in '$DFDIR'"
			writelog "SKIP" "${FUNCNAME[0]} - Only found '$(ls "$DFDIR")'"
		fi
	fi
}

function DBGMS {
	echo "$(date) - $1" >> "$STLSHM/DBGMS.txt"
}

function resetAID {
	if [ -n "$1" ]; then
		if [ -n "${1##*[!0-9]*}" ]; then
			AID="$1"
		else
			if [ "$1" == "last" ]; then
				if [ -f "$LASTRUN" ]; then
					PREVAID="$(grep "^PREVAID" "$LASTRUN" | cut -d '=' -f2)"
					if [ -n "$PREVAID" ]; then
						AID="${PREVAID//\"}"
						PREVGAME="$(grep "^PREVGAME" "$LASTRUN" | cut -d '=' -f2)"
						if [ -n "$PREVGAME" ]; then
							GN="${PREVGAME//\"}"
						fi
					fi
				else
					AID="$PLACEHOLDERAID"
				fi
			fi
		fi
	fi
	# should not happen, but just in case it is still empty, set the placeholders
	if [ -z "$AID" ]; then
		AID="$PLACEHOLDERAID"
	fi

	if [ "$AID" == "$PLACEHOLDERAID" ]; then
		GN="$PLACEHOLDERGN"
	fi
	setAIDCfgs
}

function setGN {
	if [ -z "$GN" ]; then
		getGameName "$1"
		GN="$GAMENAME"
	fi
}

function createSymLink {
	if [ ! -L "$3" ] && [ -e "$2" ]; then
		ln -s "$2" "$3"
		writelog "INFO" "$1 - Set symlink from '$2' to '$3'"
	else
		if [ -L "$3" ] && [ -z "$4" ]; then
			writelog "INFO" "$1 - Symlink '$3' already exists"
		fi

		if [ ! -e "$2" ]; then
			writelog "INFO" "$1 - '$2' does not exists"
		fi
	fi
}

function linkLog {
	if [ -z "$GN" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Skipping symlinking logfile - no valid game name found"
	else
		createSymLink "${FUNCNAME[0]}" "$LOGFILE" "$LOGDIRTI/$GN.log"
	fi
}

function setGlobalAIDCfgs {
	GLOBALSBSTWEAKCFG="$GLOBALSBSTWEAKS/$AID.conf"
	GLOBALTWEAKCFG="$GLOBALTWEAKS/$AID.conf"
}

function setCompatDataTitle {
	if [ "$STORECOMPDATTITLE" -eq 1 ] && [ -n "$OSCDP" ]; then
		mkProjDir "$STLCOMPDAT"
		COMPDATTITLE="$STLCOMPDAT/$GN"
		if readlink "$COMPDATTITLE" >/dev/null ; then
			writelog "INFO" "${FUNCNAME[0]} - Symlink $COMPDATTITLE already exists"
			if [ "$(readlink "$COMPDATTITLE")" == "$OSCDP" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Symlink '$COMPDATTITLE' already points to the correct directory '$OSCDP'"
			else
				writelog "INFO" "${FUNCNAME[0]} - Symlink '$COMPDATTITLE' points to '$(readlink "$COMPDATTITLE")' which is not the correct directory '$OSCDP' - renewing!"
				rm "$COMPDATTITLE"
				createSymLink "${FUNCNAME[0]}" "$OSCDP" "$COMPDATTITLE"
			fi
		else
			createSymLink "${FUNCNAME[0]}" "$OSCDP" "$COMPDATTITLE"
		fi
	fi
}

function setGameVars {
	getGameOS "$@"
	# common
	if [ -n "$STEAM_COMPAT_INSTALL_PATH" ]; then
		EFD="$STEAM_COMPAT_INSTALL_PATH"
	else
		EFD="$(dirname "$GP")"
	fi

	GFD="$(awk -F 'common' '{print $1}' <<< "$EFD")common/$(awk -F 'common' '{print $NF}' <<< "$EFD" | cut -d'/' -f2)" # f.e. used for vortex symlinks
	GN="$(grep -oE 'common/[^\/]+' <<< "$EFD" | awk -F 'common/' '{print $NF}')"				# THIS is hopefully the proper game name

	if [ -z "$STEAM_COMPAT_TOOL_PATHS" ] || [ "$STEAM_COMPAT_TOOL_PATHS" == "" ]; then
		HAVESCTP=0
	else
		HAVESCTP=1
		ORG_STEAM_COMPAT_TOOL_PATHS="$STEAM_COMPAT_TOOL_PATHS"
	fi

	if [ -n "$STEAM_COMPAT_INSTALL_PATH" ]; then
		STECOSHAPA="$STEAM_COMPAT_SHADER_PATH"
	fi

	# maybe loop through all paths possibly in STEAM_COMPAT_LIBRARY_PATHS?
	if [ -d "$STEAM_COMPAT_LIBRARY_PATHS" ]; then
		APPMAFE="${STEAM_COMPAT_LIBRARY_PATHS}/appmanifest_${AID}.acf"
	else
		APPMAFE="$(listAppManifests | grep -m1 "${1}.acf")"  # this should cover it though as well already
	fi

	REAPSESTR="reaper SteamLaunch"
	if grep -q "$REAPSESTR" <<< "$@"; then
		HAVEREAP=1
	else
		HAVEREAP=0
	fi

	if grep -q "$SLR" <<< "$@"; then
		writelog "INFO" "${FUNCNAME[0]} - Found SLR is launch option"
		HAVESLR=1
	else
		writelog "INFO" "${FUNCNAME[0]} - No SLR is in launch option"
		HAVESLR=0
	fi

	INSTLSTR="${PROGNAME}/${PROGCMD}"
	if grep -q "$INSTLSTR" <<< "$@"; then
		HAVEINSTL=1
	else
		HAVEINSTL=0
	fi

	# first put the initial command line into an array - who knows if we need it again
	while read -r INGARG; do
		mapfile -t -O "${#INGCMD[@]}" INGCMD <<< "$INGARG"
	done <<< "$(printf "%s\n" "$@")"

	# then put the reaper command into an array, as it is the first command in the line
	if [ "$HAVEREAP" -eq 1 ]; then
		FOUNDREAP=0
		while read -r INGARG; do
			if [ "$FOUNDREAP" -eq 0 ]; then
				mapfile -t -O "${#REAPCMD[@]}" REAPCMD <<< "$INGARG"
				if [ "$INGARG" == "--" ]; then
					FOUNDREAP=1
				fi
			else
				mapfile -t -O "${#WIPAGCMD[@]}" WIPAGCMD <<< "$INGARG"
			fi
		done <<< "$(printf "%s\n" "${INGCMD[@]}")"
	else
		while read -r INGARG; do
			mapfile -t -O "${#WIPAGCMD[@]}" WIPAGCMD <<< "$INGARG"
		done <<< "$(printf "%s\n" "${INGCMD[@]}")"
	fi
	THISREAP="${REAPCMD[*]}"
	THISREAP="${THISREAP% SteamLaunch*}"
	if [ -z "$LASTREAP" ] || { [ -n "$LASTREAP" ] && [ "$LASTREAP" != "$THISREAP" ];}; then
		updateConfigEntry "LASTREAP" "$THISREAP" "$STLDEFGLOBALCFG"
	fi

	# if the SLR is called from command line put it into an array as well
	if [ "$HAVESLR" -eq 1 ]; then
		FOUNDSLR=0
		while read -r ORGARG; do
			if [ "$FOUNDSLR" -eq 0 ]; then
				mapfile -t -O "${#RUNSLR[@]}" RUNSLR <<< "$ORGARG"
				if [ "$ORGARG" == "--" ]; then
					FOUNDSLR=1
				fi
			else
				mapfile -t -O "${#WIPBGCMD[@]}" WIPBGCMD <<< "$ORGARG"
			fi
		done <<< "$(printf "%s\n" "${WIPAGCMD[@]}")"
	else
		while read -r ORGARG; do
			mapfile -t -O "${#WIPBGCMD[@]}" WIPBGCMD <<< "$ORGARG"
		done <<< "$(printf "%s\n" "${WIPAGCMD[@]}")"
	fi

	if [ -z "$LASTSLR" ] || { [ -n "$LASTSLR" ] && [ "$LASTSLR" != "${RUNSLR[*]}" ];}; then
		updateConfigEntry "LASTSLR" "${RUNSLR[*]}" "$STLDEFGLOBALCFG"
	fi

	# put the proton path into an array as well if provided from command line
	if [ "$HAVEINPROTON" -eq 1 ]; then
		FOUNDIPRO=0
		while read -r ORGARG; do
			if [ "$FOUNDIPRO" -eq 0 ]; then
				mapfile -t -O "${#INPROTCMD[@]}" INPROTCMD <<< "$ORGARG"
				if [ "$ORGARG" == "$WFEAR" ]; then
					FOUNDIPRO=1
				fi
			else
				mapfile -t -O "${#WIPCGCMD[@]}" WIPCGCMD <<< "$ORGARG"
			fi
		done <<< "$(printf "%s\n" "${WIPBGCMD[@]}")"

		if [ "${INPROTCMD[-1]}" == "$WFEAR" ]; then
			unset "INPROTCMD[-1]"  # removing last INPROTCMD element as it is '$WFEAR'
		fi

		INPROTV="$(setProtonPathVersion "${INPROTCMD[*]}")"
	else
		while read -r ORGARG; do
			mapfile -t -O "${#WIPCGCMD[@]}" WIPCGCMD <<< "$ORGARG"
		done <<< "$(printf "%s\n" "${WIPBGCMD[@]}")"
	fi

	# put the own $PROGCMD command into an array if provided from command line
	if [ "$HAVEINSTL" -eq 1 ]; then
		FOUNDINSTL=0
		while read -r ORGARG; do
			if [ "$FOUNDINSTL" -eq 0 ]; then
				mapfile -t -O "${#INSTLCMD[@]}" INSTLCMD <<< "$ORGARG"
				if [[ "$ORGARG" =~ $INSTLSTR ]]; then
					FOUNDINSTL=1
				fi
			else
				mapfile -t -O "${#WIPDGCMD[@]}" WIPDGCMD <<< "$ORGARG"
			fi
		done <<< "$(printf "%s\n" "${WIPCGCMD[@]}")"
	else
		while read -r ORGARG; do
			mapfile -t -O "${#WIPDGCMD[@]}" WIPDGCMD <<< "$ORGARG"
		done <<< "$(printf "%s\n" "${WIPCGCMD[@]}")"
	fi

	# SLRCT = Steam Linux Runtime from Compatibility Tool
	# Refers to instances where the SLR comes in the launch command for a game
	# This can happen if you launch a game with SLR 1.0 or 3.0 selected as a compatibility tool, as Steam
	# will give the launch command wrapped with the SLR.
	#
	# See setSLRReap for implementation on how we use this
	if grep -q "$SLR" <<< "${WIPDGCMD[@]}"; then
		HAVESLRCT=1
	else
		HAVESLRCT=0
	fi

	if [ "$HAVESLRCT" -eq 1 ]; then
		FOUNDSLR=0
		while read -r ORGARG; do
			if [ "$FOUNDSLR" -eq 0 ]; then
				mapfile -t -O "${#RUNSLRCT[@]}" RUNSLRCT <<< "$ORGARG"
				if [ "$ORGARG" == "--" ]; then
					FOUNDSLR=1
				fi
			else
				mapfile -t -O "${#ORGFGCMD[@]}" ORGFGCMD <<< "$ORGARG"
			fi
		done <<< "$(printf "%s\n" "${WIPDGCMD[@]}")"
	else
		while read -r ORGARG; do
			mapfile -t -O "${#ORGFGCMD[@]}" ORGFGCMD <<< "$ORGARG"
		done <<< "$(printf "%s\n" "${WIPDGCMD[@]}")"
	fi

	# what is left now is the game executable and its command line arguments - splitting

	FOUNDORGGCMD=0
	while read -r ORGARG; do
		if [ "$FOUNDORGGCMD" -eq 0 ]; then
			mapfile -t -O "${#ORGGCMD[@]}" ORGGCMD <<< "$ORGARG"
			if [[ "$ORGARG" =~ $GP ]]; then
				FOUNDORGGCMD=1
			fi
		else
			mapfile -t -O "${#ORGCMDARGS[@]}" ORGCMDARGS <<< "$ORGARG"
		fi
	done <<< "$(printf "%s\n" "${ORGFGCMD[@]}")"

	if [ "${ORGGCMD[0]}" == "$WFEAR" ]; then
		# removing first ORGGCMD element as it is '$WFEAR'
		unset "ORGGCMD[0]"
	fi

	if [ -z "$GAMEEXE" ]; then
		GAMEEXE="${ORGGCMD[*]}"
		GAMEEXE="${GAMEEXE##*/}"
	fi
}

function getGameOS {
	function setLin {
		if [ -f "$OSCHECKGAMEEXE" ]; then
			GP="$OSCHECKGAMEEXE"
		else
			GPRAW="$(printf "%s\n" "$@" | grep -m1 "$SAC")"
			if grep -q "/./" <<< "$GPRAW"; then
				GP="${GPRAW//\/.\//\/}"
			else
				GP="$GPRAW"
			fi
		fi
		ABSGAMEEXEPATH="$GP"
		GE="${GP##*/}"																			# the game executable
		ISGAME=3																				# no STEAM_COMPAT_DATA_PATH, so it is no windows game
	}

	function setWin {
		if [ -f "$OSCHECKGAMEEXE" ]; then
			GP="$OSCHECKGAMEEXE"
		else
			GP="$(printf "%s\n" "$@" | grep -v "proton\|$SLR" | grep -m1 "$SAC")"				# the absolute game path of the windows game exe
		fi
		ABSGAMEEXEPATH="$GP"
		GPFX="$STEAM_COMPAT_DATA_PATH/pfx"														# currently used WINEPREFIX
		GE="$(awk -F '.exe' '{print $1}' <<< "${GP##*/}")"										# just the windows game exe name
		writelog "INFO" "${FUNCNAME[0]} - '$GE' determined to be a Windows Game"
		ISGAME=2
	}
	if [ "$STLPLAY" -eq 0 ]; then
		if [ "$ABSGAMEEXEPATH" == "$NON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Starting game OS detection"

			while read -r INGARG; do
				if grep -q "$STEAM_COMPAT_INSTALL_PATH" <<< "$INGARG"; then
					OSCHECKGAMEEXE="$INGARG"
				fi
				mapfile -t -O "${#OSCHECKINGCMD[@]}" OSCHECKINGCMD <<< "$INGARG"
			done <<< "$(printf "%s\n" "$@")"

			if [ -n "$STEAM_COMPAT_DATA_PATH" ]; then
				if grep -q "$L2EA" <<< "$@"; then
					ISORIGIN=1
				fi

				if [ "$ISORIGIN" -eq 1 ]; then
					writelog "INFO" "${FUNCNAME[0]} - Found '$L2EA' in the command line, so this is a Windows game!"
					setWin "$@"
				elif [ -f "$OSCHECKGAMEEXE" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Making some checks on '$OSCHECKGAMEEXE' to determine the OS version of the game"
					if grep -q "shell script" <<< "$(file "$(realpath "$OSCHECKGAMEEXE")")" || grep -q "ELF.*.LSB" <<< "$(file "$(realpath "$OSCHECKGAMEEXE")")" ; then
						writelog "INFO" "${FUNCNAME[0]} - Looks like this is a Linux game!"
						setLin "$@"
					elif grep -q "PE32" <<< "$(file "$OSCHECKGAMEEXE")"; then
						writelog "INFO" "${FUNCNAME[0]} - Looks like this is a Windows game!"
						setWin "$@"
					else
						writelog "WARN" "${FUNCNAME[0]} - Could not determine OS version of '$OSCHECKGAMEEXE' (add check?), so assuming this is a Windows game"
						setWin "$@"
					fi
				else
					writelog "WARN" "${FUNCNAME[0]} - Could not extract the full game binary path from the incoming game launch command, so assuming this is a Windows game!"
					setWin "$@"
				fi
			else
				writelog "INFO" "${FUNCNAME[0]} - STEAM_COMPAT_DATA_PATH is not defined, so this is either a Linux Game or no game was started at all"
				setLin "$@"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - ISGAME is already set to '$ISGAME' - nothing to determine"
		fi
	fi
}

function setCustomGameVars {
	# there doesn't seem to be a possibility to distinguish game platform based on given variables, so using at least a basic arch check on the exe:
	GP="$(printf "%s\n" "$@" | grep -v "$WFEAR" | head -n1)"

	getGameOS "$@"

	GE="$(awk -F '.exe' '{print $1}' <<< "${GP##*/}")"
	EFD="$(dirname "$GP")"
	# need to just guess here, because of missing fix strings
	GFD="$EFD"
	GN="$GE"

	# Allows custom programs to use SLR and force it from the toolmanifest
	USESLR=1
	HAVESLR=0

	while read -r ORGARG; do
		mapfile -t -O "${#ORGGCMD[@]}" ORGGCMD <<< "$ORGARG"
	done <<< "$(printf "%s\n" "$@")"
	if [ "${ORGGCMD[0]}" == "$WFEAR" ]; then
		unset "ORGGCMD[0]"  # removing first ORGGCMD element as it is '$WFEAR'
	fi
}

function prepareGUI {
	WINDECO="--undecorated"
	if [ -n "$USEWINDECO" ]; then
		if [ "$USEWINDECO" -eq 1 ]; then
			WINDECO="--decorated"
		elif [ "$USEWINDECO" -eq 0 ] && [ "$XDG_SESSION_TYPE" == "wayland" ]; then
			writelog "WARN" "${FUNCNAME[0]} - Disabling Yad window decorations does nothing on Wayland!"
		fi
	fi
}

function createLanguageList {
	function listLANG {
		while read -r RSPF; do
			TLANG="${RSPF//.txt/}"
			TLANG="${TLANG##*/}"
			printf '%s!' "$TLANG"
		done <<< "$(find "$STLLANGDIR" -name "*.txt")"

		while read -r RSPF; do
			TLANG="${RSPF//.txt/}"
			TLANG="${TLANG##*/}"
			printf '%s!' "$TLANG"
		done <<< "$(find "$GLOBALSTLLANGDIR" -name "*.txt")"
	}
	LANGYADLIST="$(listLANG)"
}

function loadLangFile {
	local SCRIPTDIR
	local LOCALLANGFILE

	if [ -n "$1" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Language from command line is '$1'" "P"

		LANGFILENAME="$1"
		if [ -z "$GLOBALSTLLANGDIR" ]; then
			GLOBLANG="$SYSTEMSTLCFGDIR/lang/"
			writelog "INFO" "${FUNCNAME[0]} - SYSTEMSTLCFGDIR is '$SYSTEMSTLCFGDIR'" "P"
		else
			GLOBLANG="$GLOBALSTLLANGDIR"
			writelog "INFO" "${FUNCNAME[0]} - GLOBALSTLLANGDIR is '$GLOBALSTLLANGDIR'" "P"
		fi

		if [ -f "$LANGFILENAME" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Loading command line langfile '$LANGFILENAME'" "P"
			source "$LANGFILENAME"
			STLLANG="$(cut -d '.' -f1 <<< "${LANGFILENAME##*/}")"
			LAFI="$STLLANGDIR/${STLLANG}.txt"
			if [ ! -f "$LAFI" ]; then
				mkProjDir "$STLLANGDIR"
				cp "$LANGFILENAME" "$LAFI"
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - Command line language '$LANGFILENAME' is no file - trying to find its absolute path" "P"

			LAFI="$STLLANGDIR/${LANGFILENAME}.txt"

			SCRIPTDIR="$( realpath "$0" )"
			SCRIPTDIR="${SCRIPTDIR%/*}"
			LOCALLANGFILE="$SCRIPTDIR/lang/${LANGFILENAME}.txt"

			if [ -f "$LAFI" ]; then
				# If langfile in ~/.config/steamtinkerlaunch/lang exists, and we have a langfile installed globally or in the scriptdir, update the user-installed langfile
				writelog "INFO" "${FUNCNAME[0]} - Found user-installed $LAFI, attempting to update it"
				UPDATELANGFILEPATH=""
				if [ -f "$SYSTEMSTLCFGDIR/lang/${LANGFILENAME}.txt" ]; then
					UPDATELANGFILEPATH="$SYSTEMSTLCFGDIR/lang/${LANGFILENAME}.txt"  # Globally installed langfile (or stlprefix langfile on steam deck) - This one takes priority as it is assumed to be the most up to date
				elif [ -f "$LOCALLANGFILE" ]; then
					UPDATELANGFILEPATH="$LOCALLANGFILE" # langfile from scriptdir (on steam deck first-time install this would be the install directory)
				fi

				if [ -n "$UPDATELANGFILEPATH" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Found lang file to replace the existing user-installed file with under '$UPDATELANGFILEPATH'"
					chmod +w "$LAFI" #Ensure write permissions before removing
					rm "$LAFI"
					cp "$UPDATELANGFILEPATH" "$LAFI"
					chmod -R +w "$STLLANGDIR" #Ensure write permissions for next update!
				else
					writelog "INFO" "${FUNCNAME[0]} - No lang file to replace existing user-installed file with, not updating langfile"
				fi

				writelog "INFO" "${FUNCNAME[0]} - Loading found user-installed $LAFI" "P"
				source "$LAFI"
				STLLANG="$(cut -d '.' -f1 <<< "${LANGFILENAME##*/}")"
			elif [ -f "$LOCALLANGFILE" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Loading language file from script directory '$LOCALLANGFILE'"
				source "$LOCALLANGFILE"
				STLLANG="$(cut -d '.' -f1 <<< "${LOCALLANGFILE##*/}")"
			else
				LAFI="$GLOBLANG/${LANGFILENAME}.txt"

				if [ -f "$LAFI" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Loading found system wide $LAFI" "P"
					source "$LAFI"
				else
					writelog "ERROR" "${FUNCNAME[0]} - Language file '$LAFI' could not be found" "P"
				fi
			fi
		fi
	fi
}

function loadLanguage {
	writelog "INFO" "${FUNCNAME[0]} - First load the default language '$STLDEFLANG' to make sure all variables are filled"
	loadLangFile "$STLDEFLANG"

	# Prevents loadLanguage from creating the global.conf too early when it may be missing values
	# i.e. loadLanguage may be called before setSteamPaths, which can result in creating paths without the required Steam path variables set yet
	#      This can happen with Luxtorpead, where the LUXTORPEDACMD may be written out before `setSteamPaths` has been called to set `STEAMCOMPATTOOL`
	if [ -f "$STLDEFGLOBALCFG" ]; then
		saveCfg "$STLDEFGLOBALCFG" X
		loadCfg "$STLDEFGLOBALCFG" X
	fi

	writelog "INFO" "${FUNCNAME[0]} - Loading STLLANG from '$STLDEFGLOBALCFG'"

	ARGSLANG="$(awk -F 'lang=' '{print $2}' <<< "$@" | cut -d ' ' -f1)"
	if [ -n "$ARGSLANG" ]; then
		STLLANG="$ARGSLANG"
		writelog "INFO" "${FUNCNAME[0]} - STLLANG from command line' is '$STLLANG'"

	elif [ -f "$STLDEFGLOBALCFG" ]; then
		STLLRAW="$(grep "^STLLANG" "$STLDEFGLOBALCFG" | cut -d '=' -f2)"
		STLLANG="${STLLRAW//\"/}"
		writelog "INFO" "${FUNCNAME[0]} - STLLANG from '$STLDEFGLOBALCFG' is '$STLLANG'"
	else
		writelog "WARN" "${FUNCNAME[0]} - Could not determine STLLANG"
	fi

	if [ -n "$STLLANG" ] && [ "$STLLANG" != "$STLDEFLANG" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Now load the language file '$STLLANG'"
		loadLangFile "$STLLANG"
		touch "$FUPDATE"
		updateConfigEntry "STLLANG" "$STLLANG" "$STLDEFGLOBALCFG"
	fi

	if [ -z "$DESC_STLLANG" ]; then # example variable, if it is empty it means no language file was loaded above
		writelog "ERROR" "${FUNCNAME[0]} - ###############################" "E"
		writelog "ERROR" "${FUNCNAME[0]} - No language file could be loaded! For the initial setup at least one file (default english) is required" "E"
		writelog "ERROR" "${FUNCNAME[0]} - You can ether copy a valid file to '$STLLANGDIR' or '$SYSTEMSTLCFGDIR/lang' or provide an absolute path via command line using the lang= option" "E"
		writelog "ERROR" "${FUNCNAME[0]} - ###############################" "E"
		exit
	fi
}

function setProtonPathVersion {
	if [ -n "$INPROTV" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using directly known '$INPROTV' as Proton Version for '$1'"
		echo "$INPROTV"
	else
		if [ -n "$1" ]; then
			PRTPATH="$1"
			CTVDF="$(dirname "$PRTPATH")/$CTVDF"
			PPV="$(dirname "$PRTPATH")/version"
			if [ -f "$CTVDF" ]; then
				PROTVOUT="$(grep "display_name" "$CTVDF" | grep -v "e.g." | sed "s:\" \":\";\":g" | cut -d ';' -f2)"
			elif [ -f "$PPV" ]; then
				PROTVOUT="$(awk '{print $2}' < "$PPV")"
			fi

			if [ -z "$PROTVOUT" ]; then
				# if no useful version was provided - hardcode it here:
				if grep -q "Proton 3.7" <<<"$PRTPATH"; then
					PROTVOUT="proton-3.7-8"
				else
					# fallback if everything fails - in the rare cases where this unknown proton version is used this might cause problems
					# if you need it open an issue and it will get a hardcoded entry as well
					PROTVOUT="proton-unknown-$((900 + RANDOM % 100))"
				fi
			fi
			#writelog "INFO" "${FUNCNAME[0]} - Setting the Proton Version for '$1' to '${PROTVOUT//\"/}'"	# checking $PROTONCSV should be enough
			echo "${PROTVOUT//\"/}"
		fi
	fi
}

function fillProtonCSV {
	if [ -n "$1" ]; then
		protonfileV="$1"
	else
		protonfileV="$(setProtonPathVersion "$PROTBIN")"
	fi

	if [ -n "$protonfileV" ]; then
		PCSV="\"${protonfileV//\"/}\";\"$(readlink -f "$PROTBIN")\""
		if [[ ! " ${ProtonCSV[*]} " =~ $PCSV ]]; then  # $PCSV can always be read if interested
			mapfile -t -O "${#ProtonCSV[@]}" ProtonCSV <<< "$PCSV"
		else
			writelog "SKIP" "${FUNCNAME[0]} - '$PCSV' is already in the Proton array"
		fi
	fi
}

## Get internal name for a Proton version, first by checking for a 'compatibilitytool.vdf' file in its root directory, then for a Proton version file
## Right now this is only used by addNonSteamGame
## TODO at some point maybe we should store this in the ProtonCSV as well? Then the format would be 'versionfilename;protonpath;internalname'
function getProtonInternalName {
	## Tools are not necessarily guaranteed to have this comment, but I checked several and they all had it:
	## - SteamTinkerLaunch (naturally)
	## - All GE-Proton8 releases
	## - Standard Proton-tkg releases
	## - Luxtorpeda
	##
	## Steam Linux Runtime 1.0 (scout) / Native Linux Steam Linux Runtime is identified as 'steamlinuxruntime'
	## No idea where the Steam Client gets this from, maybe it's just hardcoded, I couldn't find a string anywhere in the SteamLinuxRuntime installation folder or the 'appmanifest_1070560.acf'
	function getProtonInternalNameVdf {
		CTVPATH="$1"
		if [ -f "$CTVPATH" ]; then
			grep -i "// internal" "$CTVPATH" | sed 's-// Internal name of this tool--' | xargs
		else
			writelog "WARN" "${FUNCNAME[0]} - Could not find compatibilitytool.vdf file for Proton version at '$CTVPATH'"
			echo ""
		fi
	}

	## Get the Proton version version text file
	function getProtonInternalNameVersionFile {
		PPVPATH="$1"
		if [ -f "$PPV" ]; then
			awk '{print $2}' < "$PPV"
		else
			writelog "WARN" "${FUNCNAME[0]} - Could not find Proton version file at '$PPVPATH'"
		fi
	}

	## Check if the path provided is for a Valve Proton version, by making some assumptions around the directory structure
	function checkIsValveProton {
		VPP="$1"  # Valve Proton Path

		writelog "INFO" "${FUNCNAME[0]} - Checking if Proton version at '$VPP' is a Valve Proton version"
		# We used to check only for 'dist', but Proton 9.0 onwards (including Experimental & Hotfix) use 'files' instead of 'dist'
		# The only assumption we can make for a Valve Proton version now is if it's missing the compatibilitytools.vdf file, since
		# They Valve Proton can have either 'files' or 'dist'
		#
		# The main thing we *need* to check for is "$VPP/$CTVDF" (compatibilitytools.vdf in the compat tool folder), as all
		# third-party compat tools should have this and Valve Proton versions do not (their info is stored in appinfo.vdf internal to Steam)
		# The checks for 'files' and 'dist' are just for sanity to make sure this is a valid Proton version altogether
		if [[ ! -f "$VPP/$CTVDF" && ( -d "$VPP/dist" || -d "$VPP/files" ) ]]; then
			writelog "INFO" "${FUNCNAME[0]} - Looks like we have a Valve Proton release here"
			return 0
		else
			writelog "INFO" "${FUNCNAME[0]} - Doesn't look like a Valve Proton release, directory structure doesn't match"
			return 1
		fi
	}

	## Build the Valve Proton internal name based on its version + some hardcoding for Experimental and Hotfix
	function getValveProtonInternalName {
		BASEPRTNAM="$1"
		INTPROTNAM="proton_"
		FINALINTPROTNAM=""

		writelog "INFO" "${FUNCNAME[0]} - Building Proton version internal name using version information"
		PRTVERS="$( echo "${BASEPRTNAM%-*}" | cut -d '-' -f2 )"  # Turn proton-8.0-3c into proton-8.0, then into 8.0

		PRTMAJORVERS="$( echo "$PRTVERS" | cut -d '.' -f1 )"  # Get minor version e.g. '8' from '8.0', '4' from '4.11'
		PRTMINORVERS="$( echo "$PRTVERS" | cut -d '.' -f2 )"  # Get minor version e.g. '0' from '8.0', '11' from '4.11'

		## If minor vers > 0, we need to include it in the internal name -- Defaults to just major version
		INTPROTVERSUFFIX="${PRTMAJORVERS}"
		if [ "$PRTMINORVERS" -gt 0 ]; then
			INTPROTVERSUFFIX+="${PRTMINORVERS}"
		fi

		FINALINTPROTNAM="${INTPROTNAM}${INTPROTVERSUFFIX}"

		writelog "INFO" "${FUNCNAME[0]} - Final Internal Proton name for given Proton name '$BASEPRTNAM' string is '$FINALINTPROTNAM'"
		echo "$FINALINTPROTNAM"
	}

	PROTCSVSTR="$1"

	PRTVERS="$( echo "$PROTCSVSTR" | cut -d ';' -f1 )"
	PRTPATH="$( echo "$PROTCSVSTR" | cut -d ';' -f2 )"
	PRTPATHDIR="$( dirname "$PRTPATH" )"

	CTVDFPA="$PRTPATHDIR/$CTVDF"
	PPV="$PRTPATHDIR/version"

	INTPROTNAME="$( getProtonInternalNameVdf "$CTVDFPA" )"  # First attempt to get the internal name from compatibilitytool.vdf
	if [ -n "$INTPROTNAME" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Got Proton Internal name '$INTPROTNAME' from '$CTVDFPA'"
		echo "$INTPROTNAME"
	elif [[ $PRTVERS == experimental* ]]; then  # Experimental hardcode
		writelog "INFO" "${FUNCNAME[0]} - Looks like we have Proton Experimental -- Hardcoding internal name 'proton_experimental'"
		echo "proton_experimental"
	elif [[ $PRTVERS == hotfix* ]]; then  # Hotfix hardcode
		writelog "INFO" "${FUNCNAME[0]} - Looks like we have Proton Hotfix here -- Hardcoding internal name to 'proton_hotfix'"
		echo "proton_hotfix"
	else
		writelog "INFO" "${FUNCNAME[0]} - Could not get internal Proton name for '$PRTVERS' from '$CTVDF' - Maybe it didn't have this file"
		writelog "INFO" "${FUNCNAME[0]} - Checking if we have a Valve Proton version here to build the internal name from"

		if checkIsValveProton "$PRTPATHDIR"; then
			writelog "INFO" "${FUNCNAME[0]} - Seems we have a Valve Proton version, building internal name manually"
			getValveProtonInternalName "$PRTVERS"
		else
			writelog "INFO" "${FUNCNAME[0]} - Doesn't seem like we have a Valve Proton version"
			writelog "INFO" "${FUNCNAME[0]} - Still could not find Proton internal name from '$CTVDF' - Giving up and falling back to the Proton version, some tools use this as their internal name"
			echo "$PRTVERS"
		fi
	fi
}

function printProtonArr {
	printf "%s\n" "${ProtonCSV[@]//\"/}"
}

function prettyPrintProtonArr {
	for PV in "${ProtonCSV[@]//\"/}"
	do
		PVNAME=$( echo "$PV" | cut -d ';' -f1 )
		PVPATH=$( echo "$PV" | cut -d ';' -f2 )

		if [ -n "$1" ]; then
			if [ "$1" == "name" ] || [ "$1" == "n" ]; then
				printf "%s\n" "$PVNAME"
			elif [ "$1" == "path" ] || [ "$1" == "p" ]; then
				printf "%s\n" "$PVPATH"
			fi
		else
			printf "%s -> %s\n" "$PVNAME" "$PVPATH"
		fi

	done
}

function delEmptyFile {
	if [ -f "$1" ]; then
		if [ "$(wc -l < "$1")" -le 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Removing empty file '$1'"
			rm "$1" 2>/dev/null
		fi
	fi
}

function rmDupLines {
	if grep -q "gawk" <<< "$AWKBIN"; then
		gawk -i inplace '!visited[$0]++' "$1"
	else
		awk '!seen[$0]++' "$1" > "$STLSHM/${FUNCNAME[0]}"
		cp "$STLSHM/${FUNCNAME[0]}" "$1"
		rm "$STLSHM/${FUNCNAME[0]}" 2>/dev/null
	fi
}

function getProtNameFromPath {
	grep "$1" "$PROTONCSV" | cut -d ';' -f1
}

function getAvailableProtonVersions {
	# skip this function if a linux game was started
	if [ "$ISGAME" -eq 2 ] || [ -n "$2" ]; then
		# ...and if USEWINE is enabled
		if [ -n "$USEWINE" ] && [ "$USEWINE" -eq 1 ]; then
			writelog "SKIP" "${FUNCNAME[0]} - USEWINE is enabled - skipping this function"
		elif [ -f "$STLGAMECFG" ] && grep -q "USEWINE=\"1\"" "$STLGAMECFG" ; then
			writelog "SKIP" "${FUNCNAME[0]} - USEWINE is enabled in the to-be-loaded gameconfig '$STLGAMECFG' - skipping this function"
			# could still be enabled via steamcollection, but this would be an overkill here, as ${FUNCNAME[0]} is non-fatal
		else
			delEmptyFile "$PROTONCSV"

			# find new proton versions in CUSTPROTEXTDIR
			if [ ! -f "$CUSTOMPROTONLIST" ] || [ "$(find "$CUSTPROTEXTDIR" -mindepth 1 -maxdepth 1 -type d | wc -l)" -gt "$(wc -l < "$CUSTOMPROTONLIST")" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Updating protonlist '$CUSTOMPROTONLIST' with possible new proton versions from '$CUSTPROTEXTDIR'"
				find "$CUSTPROTEXTDIR" -type f -name "proton" >> "$CUSTOMPROTONLIST"
			fi

			if [ ! -f "$PROTONCSV" ] || { [ -n "$1" ] && [ "$1" = "up" ]; } || [ "$1" == "F" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Initially creating an array with available Proton versions"

				# following symlinks (find -L) and using maxdepth 2 to avoid duplicates caused by _(user created)_ symlinks

				# user installed compatibilitytool:
				if [ -d "$STEAMCOMPATOOLS" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Adding Proton versions found in STEAMCOMPATOOLS '$STEAMCOMPATOOLS'"
					while read -r PROTBIN; do
						if [ -f "$PROTBIN" ]; then
							writelog "INFO" "${FUNCNAME[0]} - Found proton directory: '$PROTBIN'"
							fillProtonCSV
						fi
					done <<< "$(find -L "$STEAMCOMPATOOLS" -mindepth 2 -maxdepth 2 -type f -name "proton")"
				else
					writelog "SKIP" "${FUNCNAME[0]} - Directory STEAMCOMPATOOLS '$STEAMCOMPATOOLS' not found - skipping"
				fi

				if [ -n "$STEAM_EXTRA_COMPAT_TOOLS_PATHS" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Adding Proton versions found in STEAM_EXTRA_COMPAT_TOOLS_PATHS '$STEAM_EXTRA_COMPAT_TOOLS_PATHS'"
					while read -r extrapath; do
						writelog "INFO" "${FUNCNAME[0]} - Searching for Proton version in '$extrapath'"
						while read -r PROTBIN; do
							if [ -f "$PROTBIN" ]; then
								writelog "INFO" "${FUNCNAME[0]} - Found proton directory: '$PROTBIN'"
								fillProtonCSV
							fi
						done <<< "$(find -L "$extrapath" -mindepth 2 -maxdepth 2 -type f -name "proton")"
					done <<< "$(tr ':' '\n' <<< "$STEAM_EXTRA_COMPAT_TOOLS_PATHS")"
				else
					if [ -d "$SYSSTEAMCOMPATOOLS" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Adding Proton versions found in SYSSTEAMCOMPATOOLS '$SYSSTEAMCOMPATOOLS'"
						while read -r PROTBIN; do
							if [ -f "$PROTBIN" ]; then
								writelog "INFO" "${FUNCNAME[0]} - Found proton directory: '$PROTBIN'"
								fillProtonCSV
							fi
						done <<< "$(find -L "$SYSSTEAMCOMPATOOLS" -mindepth 2 -maxdepth 2 -type f -name "proton")"
					else
						writelog "SKIP" "${FUNCNAME[0]} - Directory SYSSTEAMCOMPATOOLS '$SYSSTEAMCOMPATOOLS' not found - skipping"
					fi
				fi

				# official proton versions installed via Steam in default SteamLibrary
				if ! grep -q "\"path\".*.\"$SROOT\"" "$LFVDF"; then
					if [ -d "$DEFSTEAMAPPSCOMMON" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Adding Proton versions found in DEFSTEAMAPPSCOMMON '$DEFSTEAMAPPSCOMMON'"
						while read -r protondir; do
							PROTBIN="$protondir/proton"
							if [ -f "$PROTBIN" ]; then
								writelog "INFO" "${FUNCNAME[0]} - Found proton directory: '$protondir'"
								fillProtonCSV
							fi
						done <<< "$(find -L "$DEFSTEAMAPPSCOMMON" -mindepth 2 -maxdepth 2 -type f -name "proton")"
					else
						writelog "SKIP" "${FUNCNAME[0]} - Directory DEFSTEAMAPPSCOMMON '$DEFSTEAMAPPSCOMMON' not found - this should not happen! - skipping"
					fi
				fi

				# official proton versions installed via Steam in additional SteamLibrary Paths
				if [ -f "$CFGVDF" ] || [ -f "$LFVDF" ]; then
					if ! grep -q "BaseInstallFolder\|\"path\"" "$CFGVDF" "$LFVDF" 2>/dev/null; then
						writelog "INFO" "${FUNCNAME[0]} - No additional Steam Libraries configured in '$CFGVDF' or '$LFVDF' - so no need to search in there"
					else
						writelog "INFO" "${FUNCNAME[0]} - Adding Proton versions found in additional SteamLibrary Paths"
						while read -r protondir; do
							PROTBIN="$protondir/proton"
							if [ -f "$PROTBIN" ]; then
								writelog "INFO" "${FUNCNAME[0]} - Found proton directory: '$protondir'"
								fillProtonCSV
							fi
						done <<< "$(while read -r SLP; do if [ -d "${SLP//\"/}/$SAC" ]; then find "${SLP//\"/}/$SAC" -mindepth 1 -maxdepth 1 -type d -name "Proton*"; fi; done <<< "$(grep "BaseInstallFolder\|\"path\"" "$CFGVDF" "$LFVDF" 2>/dev/null | rev | cut -f1 | rev | sort -u)")"
					fi
				else
					writelog "SKIP" "${FUNCNAME[0]} - Neither file CFGVDF '$CFGVDF' nor file LFVDF '$LFVDF' found - this should not happen! - skipping"
				fi

				# custom Proton List:
				if [ -f "$CUSTOMPROTONLIST" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Adding Proton versions found in CUSTOMPROTONLIST '$CUSTOMPROTONLIST'"

					rmDupLines "$CUSTOMPROTONLIST"
					sed '/^$/d' -i "$CUSTOMPROTONLIST"

					while read -r PROTLINE; do
						writelog "INFO" "${FUNCNAME[0]} - Checking line '$PROTLINE' in '$CUSTOMPROTONLIST'"

						if grep -q ";" <<< "$PROTLINE"; then
							PROTBIN="$(cut -d ';' -f2 <<< "$PROTLINE")"
							PROTVERS="$(cut -d ';' -f1 <<< "$PROTLINE")"
							writelog "INFO" "${FUNCNAME[0]} - Adding '$PROTVERS' to the list"
							fillProtonCSV "$PROTVERS"
						elif [ -f "$PROTLINE" ]; then
							writelog "INFO" "${FUNCNAME[0]} - File '$PROTLINE' exists - adding it to the list"
							PROTBIN="$PROTLINE"
							fillProtonCSV
						else
							writelog "INFO" "${FUNCNAME[0]} - Removing invalid line '$PROTLINE' from '$CUSTOMPROTONLIST'"
							mapfile -t -O "${#ProtonMissing[@]}" ProtonMissing <<< "$PROTLINE"
						fi
					done <<< "$(grep -v "^#" "$CUSTOMPROTONLIST")"

					# remove files from custom list which do not exist (anymore)
					if [ -n "${ProtonMissing[0]}" ]; then
						while read -r NOPROT; do
							sed "/${NOPROT//\//\\/}/d" -i "$CUSTOMPROTONLIST"
						done <<< "$(printf "%s\n" "${ProtonMissing[@]}")"
						unset ProtonMissing
					fi
				fi
			else
				writelog "INFO" "${FUNCNAME[0]} - Creating an array with available Proton versions using the file '$PROTONCSV' which was created during a previous run"
				mapfile -t -O "${#ProtonCSV[@]}" ProtonCSV < "$PROTONCSV"
			fi
			printProtonArr > "$PROTONCSV"
			rmDupLines "$PROTONCSV"
		fi
	fi
}

function setDefaultCfgValues {

	function setDefaultCfgValuesurl {
		if [ -z "$PROJECTPAGE" ]						; then	PROJECTPAGE="$GHURL/sonic2kk/steamtinkerlaunch"; fi
		if [ -z "$CP_PROTONTKG" ]						; then	CP_PROTONTKG="$GHURL/Frogging-Family/wine-tkg-git"; fi
		if [ -z "$CP_PROTONGE" ]						; then	CP_PROTONGE="$GHURL/GloriousEggroll/proton-ge-custom"; fi
		if [ -z "$CP_PROTONSTL" ]						; then	CP_PROTONSTL="$GHURL/sonic2kk/steamtinkerlaunch-tweaks"; fi
		if [ -z "$DL_D3D47_64" ]						; then	DL_D3D47_64="https://lutris.net/files/tools/dll/$D3D47"; fi
		if [ -z "$DL_D3D47_32" ]						; then	DL_D3D47_32="http://dege.freeweb.hu/dgVoodoo2/bin/D3DCompiler_47.zip"; fi
		if [ -z "$RESHADEDLURL" ]						; then	RESHADEDLURL="https://reshade.me/downloads"; fi
		if [ -z "$RESHADEPROJURL" ]						; then	RESHADEPROJURL="https://github.com/crosire/reshade"; fi
		if [ -z "$VORTEXPROJURL" ]						; then  VORTEXPROJURL="$GHURL/Nexus-Mods/${VTX^}"; fi
		if [ -z "$DXVKURL" ]							; then	DXVKURL="$GHURL/doitsujin/dxvk"; fi
		if [ -z "$XLIVEURL" ]							; then	XLIVEURL="$GHURL/ThirteenAG/Ultimate-ASI-Loader/releases/download/v4.61/Ultimate-ASI-Loader.zip"; fi
		if [ -z "$STASSURL" ]							; then	STASSURL="https://steamcdn-a.akamaihd.net/steam/apps"; fi
		if [ -z "$WINETRICKSURL" ]						; then	WINETRICKSURL="$GHURL/Winetricks/winetricks"; fi
		if [ -z "$X64DBGURL" ]							; then	X64DBGURL="$GHURL/x64dbg/x64dbg/releases/tag/snapshot";fi
		if [ -z "$BASESTEAMGRIDDBAPI" ]					; then	BASESTEAMGRIDDBAPI="https://www.steamgriddb.com/api/v2";fi
		if [ -z "$CONTYRELURL" ]						; then	CONTYRELURL="$GHURL/Kron4ek/Conty/releases"; fi
		if [ -z "$MO2PROJURL" ]							; then  MO2PROJURL="$GHURL/ModOrganizer2/modorganizer"; fi
		if [ -z "$HMMPROJURL" ]                         ; then  HMMPROJURL="$GHURL/thesupersonic16/HedgeModManager"; fi
		if [ -z "$SPEKPROJURL" ]						; then  SPEKPROJURL="$GHURL/SpecialKO/SpecialK"; fi
		if [ -z "$CW_KRON4EK" ]							; then	CW_KRON4EK="$GHURL/Kron4ek/Wine-Builds/releases"; fi
		if [ -z "$CW_LUTRIS" ]							; then  CW_LUTRIS="$GHURL/lutris/wine/releases"; fi
		if [ -z "$CW_WINEGE" ]							; then  CW_WINEGE="$GHURL/GloriousEggroll/wine-ge-custom/releases"; fi
		if [ -z "$IGCSZIP" ]							; then	IGCSZIP="$GHURL/FransBouma/InjectableGenericCameraSystem/releases/download/IGCSInjector_102/IGCSInjector_v102.zip"; fi
		if [ -z "$UUUURL" ]								; then	UUUURL="https://framedsc.github.io/GeneralGuides/universal_ue4_consoleunlocker.htm#downloading-the-uuu"; fi
		if [ -z "$OVRFSRURL" ]							; then	OVRFSRURL="$GHURL/fholger/$OVFS/releases"; fi
		if [ -z "$DPRSRELURL" ]							; then	DPRSRELURL="$GHURL/${DPRS}/${DPRS}/releases"; fi
		if [ -z "$DEPURL" ]								; then	DEPURL="$GHURL/lucasg/${DEPS}/releases";fi
		if [ -z "$SPEKURL" ]							; then	SPEKURL="https://sk-data.special-k.info/";fi
		if [ -z "$SPEKGHURL" ]							; then	SPEKGHURL="$GHURL/${SPEK}O/${SPEK}/releases";fi
		if [ -z "$SPEKCOMPURL" ]						; then	SPEKCOMPURL="https://www.pcgamingwiki.com/wiki/List_of_games_compatible_with_Special_K#Compatibility_list";fi
		if [ -z "$FWSURL" ]								; then	FWSURL="https://www.${FWS,,}.org/fws";fi
		if [ -z "$YAIURL" ]								; then	YAIURL="$GHURL/sonic2kk/steamtinkerlaunch-tweaks/releases/download"; fi
		if [ -z "$WINERELOADURL" ]						; then	WINERELOADURL="https://gist.githubusercontent.com/rbernon/cdbdc1b0e892f91e7449fcf3dda80bb7/raw/d8cf549bf751d99ed0fe515e36f99ff5c01b7287"; fi
		if [ -z "$GEOELFURL" ]							; then	GEOELFURL="http://helixmod.blogspot.com/2022/06/announcing-new-geo-11-3d-driver.html"; fi
		if [ -z "$WDIBURL" ]							; then	WDIBURL="$GHURL/0e4ef622/$WDIB/releases"; fi
	}

	function setDefaultCfgValuesglobal {
		if [ -z "$STLLANG" ]							; then	STLLANG="$STLDEFLANG"; fi
		if [ -z "$SKIPINTDEPCHECK" ]					; then	SKIPINTDEPCHECK="0"; fi
		if [ -z "$STRACEDIR" ]							; then	STRACEDIR="$LOGDIR"; fi
		if [ -z "$LOGDIR" ]								; then	LOGDIR="$DEFLOGDIR"; fi
		if [ -z "$LOGLEVEL" ]							; then	LOGLEVEL="2"; fi
		if [ -z "$RESETLOG" ]							; then	RESETLOG="1"; fi
		if [ -z "$STLEDITOR" ]							; then	STLEDITOR="$(command -v "geany")"; fi
		if [ -z "$MAXASK" ]								; then	MAXASK="3"; fi
		if [ -z "$BROWSER" ]							; then	BROWSER="$(command -v "firefox")"; fi
		if [ -z "$NOTY" ]								; then	NOTY="$(command -v "notify-send")"; fi
		if [ -z "$NOTYARGS" ]							; then	NOTYARGS="-i $STLICON -a $PROGNAME"; fi
		if [ -z "$USENOTIFIER" ]						; then	USENOTIFIER="1"; fi
		if [ -z "$NETMON" ]								; then	NETMON="$(command -v "netstat")"; fi
		if [ -z "$NETOPTS" ]							; then	NETOPTS="-taucp -W"; fi
		if [ -z "$NETMONDIR" ]							; then	NETMONDIR="$LOGDIR"; fi
		if [ -z "$VRVIDEOPLAYER" ]						; then	VRVIDEOPLAYER="$(command -v "vr-video-player")"; fi
		if [ -z "$GLOBALCOLLECTIONDIR" ]				; then	GLOBALCOLLECTIONDIR="$SYSTEMSTLCFGDIR/collections"; fi
		if [ -z "$GLOBALMISCDIR" ]						; then	GLOBALMISCDIR="$SYSTEMSTLCFGDIR/misc"; fi
		if [ -z "$GLOBALSBSTWEAKS" ]					; then	GLOBALSBSTWEAKS="$SYSTEMSTLCFGDIR/misc/sbstweaks"; fi
		if [ -z "$GLOBALTWEAKS" ]						; then	GLOBALTWEAKS="$SYSTEMSTLCFGDIR/tweaks"; fi
		if [ -z "$GLOBALEVALDIR" ]						; then	GLOBALEVALDIR="$SYSTEMSTLCFGDIR/eval"; fi
		if [ -z "$GLOBALSTLLANGDIR" ]					; then	GLOBALSTLLANGDIR="$SYSTEMSTLCFGDIR/lang"; fi
		if [ -z "$GLOBALSTLGUIDIR" ]					; then	GLOBALSTLGUIDIR="$SYSTEMSTLCFGDIR/guicfgs"; fi
		if [ -z "$BOXTRONCMD" ]							; then	BOXTRONCMD="/usr/share/boxtron/run-dosbox"; fi
		if [ -z "$BOXTRONARGS" ]						; then	BOXTRONARGS="--wait-before-run"; fi
		if [ -z "$ROBERTACMD" ]							; then	ROBERTACMD="$STEAMCOMPATOOLS/roberta/run-vm"; fi
		if [ -z "$ROBERTAARGS" ]						; then	ROBERTAARGS="--wait-before-run"; fi
		if [ -z "$LUXTORPEDACMD" ]						; then	LUXTORPEDACMD="$STEAMCOMPATOOLS/luxtorpeda/luxtorpeda.sh"; fi
		if [ -z "$LUXTORPEDAARGS" ]						; then	LUXTORPEDAARGS="wait-before-run"; fi
		if [ -z "$RSVERS" ]								; then	RSVERS="5.9.1"; fi
		if [ -z "$USERSSPEKVERS" ]						; then  USERSSPEKVERS="1"; fi
		if [ -z "$RSSPEKVERS" ]							; then  RSSPEKVERS="5.4.2"; fi
		if [ -z "$AUTOBUMPRESHADE" ]					; then	AUTOBUMPRESHADE="0"; fi
		if [ -z "$DOWNLOAD_RESHADE" ]					; then	DOWNLOAD_RESHADE="1"; fi
		if [ -z "$RESHADESRCDIR" ]						; then	RESHADESRCDIR="$STLDLDIR/reshade"; fi
		if [ -z "$D3D47_64" ]							; then	D3D47_64="${D3D47//./_64.}"; fi
		if [ -z "$D3D47_32" ]							; then	D3D47_32="${D3D47//./_32.}"; fi
		if [ -z "$RS_64" ]								; then	RS_64="ReShade64.dll"; fi
		if [ -z "$RS_32" ]								; then	RS_32="ReShade32.dll"; fi
		if [ -z "$RS_64_VK" ]							; then	RS_64_VK="ReShade64.json"; fi
		if [ -z "$RS_32_VK" ]							; then	RS_32_VK="ReShade32.json"; fi
		if [ -z "$DLSHADER" ]							; then	DLSHADER="1"; fi
		if [ -z "$SAVESETSIZE" ]						; then	SAVESETSIZE="1"; fi
		if [ -z "$STARTMENU" ]							; then	STARTMENU="Menu"; fi
		if [ -z "$YADFORCEXWAYLAND" ]                   ; then  YADFORCEXWAYLAND="0"; fi
		if [ -z "$USEWINDECO" ]							; then	USEWINDECO="1"; fi
		if [ -z "$HEADLINEFONT" ]						; then	HEADLINEFONT="larger"; fi
		if [ -z "$USETRAYICON" ]						; then	USETRAYICON="1"; fi
		if [ -z "$USEGAMEPICS" ]						; then	USEGAMEPICS="1"; fi
		if [ -z "$USECUSTOMFALLBACKPIC" ]				; then	USECUSTOMFALLBACKPIC="0"; fi
		if [ -z "$GITHUBUSER" ]							; then	GITHUBUSER="$NON"; fi
		if [ -z "$DLGAMEDATA" ]							; then	DLGAMEDATA="1"; fi
		if [ -z "$DLSTEAMDECKCOMPATINFO" ]				; then  DLSTEAMDECKCOMPATINFO="1"; fi
		if [ -z "$USEPDBRATING" ]						; then	USEPDBRATING="1"; fi
		if [ -z "$PDBRATINGCACHE" ]						; then	PDBRATINGCACHE="1"; fi
		if [ -z "$DLWINETRICKS" ]						; then	DLWINETRICKS="0"; fi
		if [ -z "$AUTOLASTPROTON" ]						; then	AUTOLASTPROTON="1"; fi
		if [ -z "$AUTOPULLPROTON" ]						; then	AUTOPULLPROTON="1"; fi
		if [ -z "$CUSTPROTDLDIR" ]						; then	CUSTPROTDLDIR="$STLDLDIR/$PROCU"; fi
		if [ -z "$CUSTPROTEXTDIR" ]						; then	CUSTPROTEXTDIR="$STLCFGDIR/$PROCU"; fi
		if [ -z "$WINEDLDIR" ]							; then	WINEDLDIR="$STLDLDIR/wine"; fi
		if [ -z "$WINEEXTDIR" ]							; then	WINEEXTDIR="$STLCFGDIR/wine"; fi
		if [ -z "$USEVORTEXPROTON" ]					; then	USEVORTEXPROTON="$NON"; fi
		if [ -z "$VORTEXCOMPDATA" ]						; then	VORTEXCOMPDATA="$STLVORTEXDIR/$CODA"; fi
		if [ -z "$VORTEXDOWNLOADPATH" ]					; then	VORTEXDOWNLOADPATH="$STLVORTEXDIR/downloads"; fi
		if [ -z "$USEVORTEXPRERELEASE" ]				; then	USEVORTEXPRERELEASE="0"; fi
		if [ -z "$VORTEXUSESLR" ]						; then  VORTEXUSESLR="1"; fi
		if [ -z "$VORTEXUSESLRPOSTINSTALL" ]			; then  VORTEXUSESLRPOSTINSTALL="0"; fi
		if [ -z "$DISABLEVORTEXAUTOUPDATE" ]			; then	DISABLEVORTEXAUTOUPDATE="0"; fi
		if [ -z "$USEVORTEXCUSTOMVER" ]					; then	USEVORTEXCUSTOMVER="0"; fi
		if [ -z "$VORTEXCUSTOMVER" ]					; then	VORTEXCUSTOMVER="$NON"; fi
		if [ -z "$VORTEXDEVICESCALEFACTOR" ]			; then  VORTEXDEVICESCALEFACTOR="1"; fi
		if [ -z "$DISABLE_AUTOSTAGES" ]					; then	DISABLE_AUTOSTAGES="0"; fi
		if [ -z "$NOSTEAMSTLDEF" ]						; then	NOSTEAMSTLDEF="0"; fi
		if [ -z "$SGDBAPIKEY" ]							; then	SGDBAPIKEY="$NON"; fi
		if [ -z "$SGDBDLTOSTEAM" ]						; then	SGDBDLTOSTEAM="0"; fi
		if [ -z "$SGDBHASFILE" ]						; then	SGDBHASFILE="skip"; fi
		if [ -z "$SGDBAUTODL" ]							; then	SGDBAUTODL="$NON"; fi
		if [ -z "$SGDBDLHERO" ]							; then  SGDBDLHERO="1"; fi
		if [ -z "$SGDBDLLOGO" ]							; then  SGDBDLLOGO="1"; fi
		if [ -z "$SGDBDLBOXART" ]						; then  SGDBDLBOXART="1"; fi
		if [ -z "$SGDBDLTENFOOT" ]						; then  SGDBDLTENFOOT="1"; fi
		if [ -z "$SGDBHERODIMS" ]						; then  SGDBHERODIMS="$DEFSGDBHERODIMS"; fi
		if [ -z "$SGDBHEROTYPES" ]						; then  SGDBHEROTYPES="static"; fi
		if [ -z "$SGDBHEROSTYLES" ]						; then  SGDBHEROSTYLES="$SGDBHEROSTYLEOPTS"; fi
		if [ -z "$SGDBHERONSFW" ]						; then  SGDBHERONSFW="any"; fi
		if [ -z "$SGDBHEROHUMOR" ]						; then  SGDBHEROHUMOR="any"; fi
		if [ -z "$SGDBHEROEPILEPSY" ]					; then  SGDBHEROEPILEPSY="false"; fi
		if [ -z "$SGDBLOGOTYPES" ]						; then  SGDBLOGOTYPES="static"; fi
		if [ -z "$SGDBLOGOSTYLES" ]						; then  SGDBLOGOSTYLES="$SGDBLOGOSTYLEOPTS"; fi
		if [ -z "$SGDBLOGONSFW" ]						; then  SGDBLOGONSFW="any"; fi
		if [ -z "$SGDBLOGOHUMOR" ]						; then  SGDBLOGOHUMOR="any"; fi
		if [ -z "$SGDBLOGOEPILEPSY" ]					; then  SGDBLOGOEPILEPSY="false"; fi
		if [ -z "$SGDBBOXARTDIMS" ]						; then  SGDBBOXARTDIMS="$DEFSGDBBOXARTDIMS"; fi
		if [ -z "$SGDBBOXARTTYPES" ]					; then  SGDBBOXARTTYPES="static"; fi
		if [ -z "$SGDBBOXARTSTYLES" ]					; then  SGDBBOXARTSTYLES="$SGDBGRIDSTYLEOPTS"; fi
		if [ -z "$SGDBBOXARTNSFW" ]						; then  SGDBBOXARTNSFW="any"; fi
		if [ -z "$SGDBBOXARTHUMOR" ]					; then  SGDBBOXARTHUMOR="any"; fi
		if [ -z "$SGDBBOXARTEPILEPSY" ]					; then  SGDBBOXARTEPILEPSY="false"; fi
		if [ -z "$SGDBTENFOOTDIMS" ]					; then  SGDBTENFOOTDIMS="$DEFSGDBTENFOOTDIMS"; fi
		if [ -z "$SGDBTENFOOTTYPES" ]					; then  SGDBTENFOOTTYPES="static"; fi
		if [ -z "$SGDBTENFOOTSTYLES" ]					; then  SGDBTENFOOTSTYLES="$SGDBTNFTSTYLEOPTS"; fi
		if [ -z "$SGDBTENFOOTNSFW" ]					; then  SGDBTENFOOTNSFW="any"; fi
		if [ -z "$SGDBTENFOOTHUMOR" ]					; then  SGDBTENFOOTHUMOR="any"; fi
		if [ -z "$SGDBTENFOOTEPILEPSY" ]				; then  SGDBTENFOOTEPILEPSY="false"; fi
		if [ -z "$STORECOMPDATTITLE" ]					; then	STORECOMPDATTITLE="1"; fi
		if [ -z "$CUSTCONTY" ]							; then	CUSTCONTY="$NON"; fi
		if [ -z "$UPDATECONTY" ]						; then	UPDATECONTY="1"; fi
		if [ -z "$LOGPLAYTIME" ]						; then	LOGPLAYTIME="1"; fi
		if [ -z "$YAD" ]								; then	YAD="$(command -v "yad")"; fi
		if [ -z "$DPRSCOMPDATA" ]						; then	DPRSCOMPDATA="$STLCFGDIR/${DPRS,}/$CODA"; fi
		if [ -z "$USEDPRSPROTON" ]						; then	USEDPRSPROTON="$NON"; fi
		if [ -z "$DPRSUSEVDFSYMLINKS" ]					; then	DPRSUSEVDFSYMLINKS="0"; fi
		if [ -z "$DPRSPAUTOUP" ]						; then	DPRSPAUTOUP="0"; fi
		if [ -z "$DEPSAUTOUP" ]							; then	DEPSAUTOUP="0"; fi
		if [ -z "$MO2COMPDATA" ]						; then	MO2COMPDATA="$STLMO2DIR/$CODA"; fi
		if [ -z "$HMMCOMPDATA" ]                        ; then  HMMCOMPDATA="$STLHMMDIR/$CODA"; fi
		if [ -z "$HMMDLVER" ]                           ; then  HMMDLVER="$HMMSTABLE"; fi
		if [ -z "$USEMO2PROTON" ]						; then	USEMO2PROTON="$NON"; fi
		if [ -z "$USEMO2CUSTOMINSTALLER" ]				; then  USEMO2CUSTOMINSTALLER="0"; fi
		if [ -z "$MO2CUSTOMINSTALLER" ]					; then  MO2CUSTOMINSTALLER="$NON"; fi
		if [ -z "$USEHMMPROTON" ]                       ; then  USEHMMPROTON="$NON"; fi
		if [ -z "$USETERM" ]							; then	USETERM="$(command -v "xterm")"; fi
		if [ -z "$TERMARGS" ]							; then	TERMARGS="-e"; fi
		if [ -z "$USEGLOBALWINEDPI" ]					; then  USEGLOBALWINEDPI="0"; fi
		if [ -z "$GLOBALWINEDPI" ]						; then  GLOBALWINEDPI="$DEFWINEDPI"; fi
		if [ -z "$STEAMDECK_AUTOUP" ]					; then  STEAMDECK_AUTOUP="1"; fi
	}

	function setDefaultCfgValuesdefault_template {
		if [ -z "$KEEPSTLOPEN" ]						; then	KEEPSTLOPEN="0"; fi
		if [ -z "$USESLR" ]								; then	USESLR="1"; fi
		if [ -z "$FORCESLR" ]							; then	FORCESLR="0"; fi
		if [ -z "$IGNORECOMPATSLR" ]					; then	IGNORECOMPATSLR="0"; fi
		if [ -z "$USEREAP" ]							; then	USEREAP="1"; fi
		if [ -z "$FORCEREAP" ]							; then	FORCEREAP="0"; fi
		if [ -z "$USEPROTON" ]							; then	USEPROTON="$(getDefaultProton)"; fi
		if [ -z "$REDIRCOMPDATA" ]						; then	REDIRCOMPDATA="disabled"; fi
		if [ -z "$REDIRSTEAMUSER" ]						; then	REDIRSTEAMUSER="disabled"; fi
		if [ -z "$ONLYPROTMAJORREDIRECT" ]              ; then  ONLYPROTMAJORREDIRECT="0"; fi
		if [ -z "$AUTOBUMPGE" ]							; then	AUTOBUMPGE="0"; fi
		if [ -z "$AUTOBUMPPROTON" ]						; then	AUTOBUMPPROTON="0"; fi
		if [ -z "$USECUSTOMCMD" ]						; then	USECUSTOMCMD="0"; fi
		if [ -z "$CUSTOMCMD" ]							; then	CUSTOMCMD="$DUMMYBIN"; fi
		if [ -z "$CUSTOMCMD_ARGS" ]						; then	CUSTOMCMD_ARGS="$NON"; fi
		if [ -z "$ONLY_CUSTOMCMD" ]						; then	ONLY_CUSTOMCMD="0";	fi
		if [ -z "$FORK_CUSTOMCMD" ]						; then	FORK_CUSTOMCMD="0";	fi
		if [ -z "$EXTPROGS_CUSTOMCMD" ]					; then  EXTPROGS_CUSTOMCMD="0"; fi
		if [ -z "$CUSTOMCMD_USESLR" ]					; then  CUSTOMCMD_USESLR="1"; fi
		if [ -z "$CUSTOMCMDFORCEWIN" ]					; then  CUSTOMCMDFORCEWIN="0"; fi
		if [ -z "$WAITFORCUSTOMCMD" ]					; then	WAITFORCUSTOMCMD="0"; fi
		if [ -z "$INJECT_CUSTOMCMD" ]					; then	INJECT_CUSTOMCMD="0"; fi
		if [ -z "$INJECTWAIT" ]							; then	INJECTWAIT="0";	fi
		if [ -z "$USEIGCS" ]							; then	USEIGCS="0"; fi
		if [ -z "$UUUSEIGCS" ]							; then	UUUSEIGCS="0"; fi
		if [ -z "$IGCSWAIT" ]							; then	IGCSWAIT="0"; fi
		if [ -z "$UUUSEPATCH" ]							; then	UUUSEPATCH="0"; fi
		if [ -z "$UUUPATCHWAIT" ]						; then	UUUPATCHWAIT="0"; fi
		if [ -z "$UUUSEVR" ]							; then	UUUSEVR="0"; fi
		if [ -z "$GAMEARGS" ]							; then	GAMEARGS="$NON"; fi
		if [ -z "$HARDARGS" ]							; then	HARDARGS="$NOPE"; fi
		if [ -z "$USEGAMEMODERUN" ]						; then	USEGAMEMODERUN="0";	fi
		if [ -z "$USEGAMESCOPE" ]						; then	USEGAMESCOPE="0"; fi
		if [ -z "$USEGAMESCOPEWSI" ]					; then  USEGAMESCOPEWSI="0"; fi
		if [ -z "$GAMESCOPE_ARGS" ]						; then	GAMESCOPE_ARGS="--"; fi
		if [ -z "$USEOBSCAP" ]							; then	USEOBSCAP="0"; fi
		if [ -z "$USEZINK" ]							; then	USEZINK="0"; fi
		if [ -z "$USEPRIMERUN" ]						; then	USEPRIMERUN="0"; fi
		if [ -z "$TOGSTEAMWEBHELPER" ]					; then	TOGSTEAMWEBHELPER="0"; fi
		if [ -z "$USEMANGOHUD" ]						; then	USEMANGOHUD="0"; fi
		if [ -z "$USEMANGOAPP" ]						; then	USEMANGOAPP="0"; fi
		if [ -z "$MAHUBIN" ]							; then	MAHUBIN="$(command -v "$MAHU")"; fi
		if [ -z "$MAHUARGS" ]							; then	MAHUARGS="$NON"; fi
		if [ -z "$MAHUDLSYM" ]							; then  MAHUDLSYM="0"; fi
		if [ -z "$LDPMAHU" ]							; then	LDPMAHU="0"; fi
		if [ -z "$MAHUVAR" ]							; then	MAHUVAR="0"; fi
		if [ -z "$USEMANGOHUDSTLCFG" ]					; then	USEMANGOHUDSTLCFG="0";	fi
		if [ -z "$VULKANPOSTPROCESSOR" ]				; then  VULKANPOSTPROCESSOR="$NON"; fi
		if [ -z "$RUN_NYRNA" ]							; then	RUN_NYRNA="0"; fi
		if [ -z "$RUN_REPLAY" ]							; then	RUN_REPLAY="0";	fi
		if [ -z "$RUN_X64DBG" ]							; then	RUN_X64DBG="0";	fi
		if [ -z "$X64DBG_ATTACHONSTARTUP" ]				; then  X64DBG_ATTACHONSTARTUP="1"; fi
		if [ -z "$RUN_GDB" ]							; then	RUN_GDB="0";	fi
		if [ -z "$USE_WDIB" ]							; then	USE_WDIB="0";	fi
		if [ -z "$USEVORTEX" ]							; then	USEVORTEX="0"; fi
		if [ -z "$WAITVORTEX" ]							; then	WAITVORTEX="2"; fi
		if [ -z "$RUN_VORTEX_WINETRICKS" ]				; then	RUN_VORTEX_WINETRICKS="0"; fi
		if [ -z "$RUN_VORTEX_WINECFG" ]					; then	RUN_VORTEX_WINECFG="0";fi
		if [ -z "$CHANGE_PULSE_LATENCY" ] 				; then	CHANGE_PULSE_LATENCY="0"; fi
		if [ -z "$STL_PULSE_LATENCY_MSEC" ]				; then	STL_PULSE_LATENCY_MSEC="60"; fi
		if [ -z "$TOGGLEWINDOWS" ]						; then	TOGGLEWINDOWS="0"; fi
		if [ -z "$RUN_WINETRICKS" ]						; then	RUN_WINETRICKS="0"; fi
		if [ -z "$WINETRICKSPAKS" ]						; then	WINETRICKSPAKS="$NON"; fi
		if [ -z "$RUN_WINECFG" ]						; then	RUN_WINECFG="0"; fi
		if [ -z "$USEWINE" ]							; then	USEWINE="0"; fi
		if [ -z "$WINEVERSION" ]						; then	WINEVERSION="$DUMMYBIN"; fi
		if [ -z "$WINEDEFAULT" ]						; then	WINEDEFAULT="$DUMMYBIN"; fi
		if [ -z "$USEWICO" ]							; then	USEWICO="0"; fi
		if [ -z "$VIRTUALDESKTOP" ]						; then	VIRTUALDESKTOP="0"; fi
		if [ -z "$VDRES" ]								; then	VDRES="$NON"; fi
		if [ -z "$USEBOXTRON" ]							; then	USEBOXTRON="0"; fi
		if [ -z "$USEROBERTA" ]							; then	USEROBERTA="0"; fi
		if [ -z "$USELUXTORPEDA" ]						; then	USELUXTORPEDA="0"; fi
		if [ -z "$REGEDIT" ]							; then	REGEDIT="0"; fi
		if [ -z "$USEGEOELF" ]							; then	USEGEOELF="0"; fi
		if [ -z "$AUTOGEOELF" ]							; then	AUTOGEOELF="0"; fi
		if [ -z "$RESHADE_DEPTH3D" ]					; then	RESHADE_DEPTH3D="0"; fi
		if [ -z "$USERESHADE" ]							; then	USERESHADE="0"; fi
		if [ -z "$CUSTOMCMDRESHADE" ]					; then	CUSTOMCMDRESHADE="0"; fi
		if [ -z "$RESHADEUPDATE" ]						; then	RESHADEUPDATE="0"; fi
		if [ -z "$CREATERESHINI" ]						; then  CREATERESHINI="1"; fi
		if [ -z "$RSOVRD" ] 							; then  RSOVRD="0"; fi
		if [ -z "$RSOVRVERS" ] 							; then  RSOVRVERS="$RSVERS"; fi
		if [ -z "$RESHADEDLLNAME" ]						; then  RESHADEDLLNAME="$DXGI"; fi
		if [ -z "$CHOOSESHADERS" ]						; then	CHOOSESHADERS="0"; fi
		if [ -z "$ALTEXEPATH" ]							; then	ALTEXEPATH="/tmp"; fi
		if [ -z "$ARCHALTEXE" ]							; then	ARCHALTEXE="$DUMMYBIN"; fi
		if [ -z "$USEOPENVRFSR" ]						; then	USEOPENVRFSR="0"; fi
		if [ -z "$RUNSBSVR" ]							; then	RUNSBSVR="0"; fi
		if [ -z "$RUNSBS" ]								; then	RUNSBS="0"; fi
		if [ -z "$VRVIDEOPLAYERARGS" ]					; then	VRVIDEOPLAYERARGS="--flat"; fi
		if [ -z "$SBSZOOM" ]							; then	SBSZOOM="1.0"; fi
		if [ -z "$SBSVRGEOELF" ]						; then	SBSVRGEOELF="0"; fi
		if [ -z "$SBSVRRS" ]							; then	SBSVRRS="0"; fi
		if [ -z "$SBSRS" ]								; then	SBSRS="0"; fi
		if [ -z "$MINVRWINH" ] 							; then	MINVRWINH="640"; fi
		if [ -z "$WAITFORTHISPID" ]						; then	WAITFORTHISPID="$NON"; fi
		if [ -z "$WAITEDITOR" ]							; then	WAITEDITOR="2"; fi
		if [ -z "$HELPURL" ]							; then	HELPURL="$NON"; fi
		if [ -z "$CREATEDESKTOPICON" ]					; then	CREATEDESKTOPICON="0"; fi
		if [ -z "$STEAMAPPIDFILE" ]						; then	STEAMAPPIDFILE="0"; fi
		if [ -z "$CHECKCOLLECTIONS" ]					; then	CHECKCOLLECTIONS="1"; fi
		if [ -z "$BACKUPSTEAMUSER" ]					; then	BACKUPSTEAMUSER="0"; fi
		if [ -z "$RESTORESTEAMUSER" ]					; then	RESTORESTEAMUSER="$NON"; fi
		if [ -z "$USESUSYM" ]							; then	USESUSYM="0"; fi
		if [ -z "$USEGLOBSUSYM" ]						; then	USEGLOBSUSYM="0"; fi
		if [ -z "$STRACERUN" ]							; then	STRACERUN="0"; fi
		if [ -z "$STRACEOPTS" ]							; then	STRACEOPTS="-f -t -e trace=file"; fi
		if [ -z "$USENETMON" ]							; then	USENETMON="0"; fi
		if [ -z "$BLOCKINTERNET" ]						; then	BLOCKINTERNET="0"; fi
		if [ -z "$USE_STLDXVKCFG" ]						; then	USE_STLDXVKCFG="0"; fi
		if [ -z "$DXVK_HUD" ]							; then	DXVK_HUD="0"; fi
		if [ -z "$DXVK_LOG_LEVEL" ]						; then	DXVK_LOG_LEVEL="$NON"; fi
		if [ -z "$DXVK_LOG_PATH" ]						; then	DXVK_LOG_PATH="$STLDXVKLOGDIR"; fi
		if [ -z "$DXVK_SCALE" ]							; then	DXVK_SCALE="1.0"; fi
		if [ -z "$DXVK_FPSLIMIT" ]                      ; then  DXVK_FPSLIMIT="none"; fi
		if [ -z "$DXVK_ASYNC" ]							; then	DXVK_ASYNC="0"; fi
		if [ -z "$DXVK_HDR" ]							; then  DXVK_HDR="0"; fi
		if [ -z "$PROTON_LOG" ]							; then	PROTON_LOG="0"; fi
		if [ -z "$PROTON_LOG_DIR" ]						; then	PROTON_LOG_DIR="$STLPROTONIDLOGDIR"; fi
		if [ -z "$USEWINEDEBUGPROTON" ]					; then	USEWINEDEBUGPROTON="0"; fi
		if [ -z "$WINE_LOG_DIR" ]						; then	WINE_LOG_DIR="$STLWINELOGDIR"; fi
		if [ -z "$PROTON_DEBUG_DIR" ]					; then	PROTON_DEBUG_DIR="/tmp"; fi
		if [ -z "$PROTON_USE_WINED3D" ]					; then	PROTON_USE_WINED3D="0"; fi
		if [ -z "$PROTON_NO_D3D11" ]					; then	PROTON_NO_D3D11="0"; fi
		if [ -z "$PROTON_NO_D3D10" ]					; then	PROTON_NO_D3D10="0"; fi
		if [ -z "$PROTON_NO_ESYNC" ]					; then	PROTON_NO_ESYNC="0"; fi
		if [ -z "$PROTON_NO_FSYNC" ]					; then	PROTON_NO_FSYNC="0"; fi
		if [ -z "$ENABLE_WINESYNC" ]					; then	ENABLE_WINESYNC="0"; fi
		if [ -z "$PROTON_ENABLE_NVAPI" ]				; then	PROTON_ENABLE_NVAPI="0"; fi
		if [ -z "$PROTON_HIDE_NVIDIA_GPU" ]				; then	PROTON_HIDE_NVIDIA_GPU="1"; fi
		if [ -z "$STL_VKD3D_CONFIG" ]					; then	STL_VKD3D_CONFIG="$NON"; fi
		if [ -z "$STL_VKD3D_DEBUG" ]					; then	STL_VKD3D_DEBUG="$NON"; fi
		if [ -z "$STL_VKD3D_SHADER_DEBUG" ]				; then	STL_VKD3D_SHADER_DEBUG="$NON"; fi
		if [ -z "$STL_VKD3D_LOG_FILE" ]					; then	STL_VKD3D_LOG_FILE="$NON"; fi
		if [ -z "$STL_VKD3D_VULKAN_DEVICE" ]			; then	STL_VKD3D_VULKAN_DEVICE="$NON"; fi
		if [ -z "$STL_VKD3D_FILTER_DEVICE_NAME" ]		; then	STL_VKD3D_FILTER_DEVICE_NAME="$NON"; fi
		if [ -z "$STL_VKD3D_DISABLE_EXTENSIONS" ]		; then	STL_VKD3D_DISABLE_EXTENSIONS="$NON"; fi
		if [ -z "$STL_VKD3D_TEST_DEBUG" ]				; then	STL_VKD3D_TEST_DEBUG="0"; fi
		if [ -z "$STL_VKD3D_TEST_FILTER" ]				; then	STL_VKD3D_TEST_FILTER="$NON"; fi
		if [ -z "$STL_VKD3D_TEST_EXCLUDE" ]				; then	STL_VKD3D_TEST_EXCLUDE="$NON"; fi
		if [ -z "$STL_VKD3D_TEST_PLATFORM" ]			; then	STL_VKD3D_TEST_PLATFORM="$NON"; fi
		if [ -z "$STL_VKD3D_TEST_BUG" ]					; then	STL_VKD3D_TEST_BUG="$NON"; fi
		if [ -z "$STL_VKD3D_PROFILE_PATH" ]				; then	STL_VKD3D_PROFILE_PATH="$NON"; fi
		if [ -z "$USEDLSS" ]							; then	USEDLSS="0"; fi
		if [ -z "$USERAYTRACING" ]						; then	USERAYTRACING="0"; fi
		if [ -z "$PROTON_FORCE_LARGE_ADDRESS_AWARE" ]	; then	PROTON_FORCE_LARGE_ADDRESS_AWARE="1"; fi
		if [ -z "$PROTON_DUMP_DEBUG_COMMANDS" ]			; then	PROTON_DUMP_DEBUG_COMMANDS="0"; fi
		if [ -z "$CLEANPROTONTEMP" ]					; then	CLEANPROTONTEMP="0"; fi
		if [ -z "$WINE_FULLSCREEN_INTEGER_SCALING" ]	; then	WINE_FULLSCREEN_INTEGER_SCALING="0"; fi
		if [ -z "$WINE_FULLSCREEN_FSR" ]				; then	WINE_FULLSCREEN_FSR="0"; fi
		if [ -z "$WINE_FULLSCREEN_FSR_STRENGTH" ]		; then	WINE_FULLSCREEN_FSR_STRENGTH="2"; fi
		if [ -z "$WINE_FULLSCREEN_FSR_MODE" ]			; then  WINE_FULLSCREEN_FSR_MODE="none"; fi
		if [ -z "$WINE_FULLSCREEN_FSR_CUSTOM_MODE" ]	; then  WINE_FULLSCREEN_FSR_CUSTOM_MODE="none"; fi
		if [ -z "$USEPERGAMEWINEDPI" ]					; then  USEPERGAMEWINEDPI="0"; fi
		if [ -z "$PERGAMEWINEDPI" ]						; then  PERGAMEWINEDPI="$DEFWINEDPI"; fi
		if [ -z "$STLWINEDEBUG" ]						; then	STLWINEDEBUG="-all"; fi
		if [ -z "$STLWINEDLLOVERRIDES" ]				; then	STLWINEDLLOVERRIDES="$NON"; fi
		if [ -z "$USERSTART" ]							; then	USERSTART="$DUMMYBIN"; fi
		if [ -z "$USERSTOP" ]							; then	USERSTOP="$DUMMYBIN"; fi
		if [ -z "$AUTOCONTY" ]							; then	AUTOCONTY="0"; fi
		if [ -z "$USECONTY" ]							; then	USECONTY="0"; fi
		if [ -z "$CRASHGUESS" ]							; then	CRASHGUESS="60"; fi
		if [ -z "$ONLYWICO" ]							; then	ONLYWICO="0"; fi
		if [ -z "$GAMESCREENRES" ]						; then	GAMESCREENRES="$NON"; fi
		if [ -z "$FIXSYMLINKS" ]						; then	FIXSYMLINKS="0"; fi
		if [ -z "$UNSYMLINK" ]							; then	UNSYMLINK="0"; fi
		if [ -z "$DELPFX" ]								; then	DELPFX="0"; fi
		if [ -z "$RUN_DEPS" ]							; then	RUN_DEPS="0"; fi
		if [ -z "$SORTGARGS" ]							; then	SORTGARGS="0"; fi
		if [ -z "$MO2MODE" ]							; then	MO2MODE="disabled"; fi
		if [ -z "$WAITMO2" ]							; then	WAITMO2="2"; fi
		if [ -z "$MO2SILENTMODEEXEOVERRIDE" ]			; then  MO2SILENTMODEEXEOVERRIDE="$NON"; fi
		if [ -z "$USESPECIALK" ]						; then	USESPECIALK="0"; fi
		if [ -z "$SPEKDLLNAME" ]						; then  SPEKDLLNAME="$AUTO"; fi
		if [ -z "$USERESHSPEKPLUGIN" ]					; then  USERESHSPEKPLUGIN="1"; fi
		if [ -z "$USESPEKD3D47" ]						; then  USESPEKD3D47="1"; fi
		if [ -z "$SDLUSEWAYLAND" ]						; then  SDLUSEWAYLAND="0"; fi
		if [ -z "$STLRAD_PFTST" ]                       ; then  STLRAD_PFTST="none"; fi
		if [ -z "$SPEKVERS" ]							; then	SPEKVERS="stable"; fi
		if [ -z "$AUTOSPEK" ]							; then	AUTOSPEK="0"; fi
		if [ -z "$USEFWS" ]								; then	USEFWS="0"; fi
		if [ -z "$USEPEV_PELDD" ]						; then	USEPEV_PELDD="0"; fi
		if [ -z "$USEPEV_PEPACK" ]						; then	USEPEV_PEPACK="0"; fi
		if [ -z "$USEPEV_PERES" ]						; then	USEPEV_PERES="0"; fi
		if [ -z "$USEPEV_PESCAN" ]						; then	USEPEV_PESCAN="0"; fi
		if [ -z "$USEPEV_PESEC" ]						; then	USEPEV_PESEC="0"; fi
		if [ -z "$USEPEV_PESTR" ]						; then	USEPEV_PESTR="0"; fi
		if [ -z "$USEPEV_READPE" ]						; then	USEPEV_READPE="0"; fi
	}

	"${FUNCNAME[0]}$1"
}

function saveCfg {

	function saveCfgurl {

		setDefaultCfgValues "$2"

		if [ -f "$1" ]; then
			updateConfigFile "$1" "${FUNCNAME[0]}" "$3"
		else
			#STARTsaveCfgurl
			{
			echo "## config Version: $PROGVERS"
			echo "##########################"
			echo "## Url Config:"
			echo "##########################"
			echo "PROJECTPAGE=\"$PROJECTPAGE\""
			echo "##########################"
			echo "## Proton GE DL URL"
			echo "CP_PROTONGE=\"$CP_PROTONGE\""
			echo "## Proton TKG DL URL"
			echo "CP_PROTONTKG=\"$CP_PROTONTKG\""
			echo "## Proton STL DL URL"
			echo "CP_PROTONSTL=\"$CP_PROTONSTL\""
			echo "## Wine Kron4ek URL"
			echo "CW_KRON4EK=\"$CW_KRON4EK\""
			echo "## Lutris Wine URL"
			echo "CW_LUTRIS=\"$CW_LUTRIS\""
			echo "## Wine-GE URL"
			echo "CW_WINEGE=\"$CW_WINEGE\""
			echo "## d3d47 64bit DL URL "
			echo "DL_D3D47_64=\"$DL_D3D47_64\""
			echo "## d3d47 32bit DL URL"
			echo "DL_D3D47_32=\"$DL_D3D47_32\""
			echo "## Dxvk Project URL"
			echo "DXVKURL=\"$DXVKURL\""
			echo "## ${RESH} DL URL"
			echo "RESHADEDLURL=\"$RESHADEDLURL\""
			echo "## ${RESH} Project URL"
			echo "RESHADEPROJURl=\"$RESHADEPROJURL\""
			echo "## ${VTX^} Project URL"
			echo "VORTEXPROJURL=\"$VORTEXPROJURL\""
			echo "## Xlive DL URL"
			echo "XLIVEURL=\"$XLIVEURL\""
			echo "## Steam Asset URL"
			echo "STASSURL=\"$STASSURL\""
			echo "## winetricks URL"
			echo "WINETRICKSURL=\"$WINETRICKSURL\""
			echo "## x64dbg URL"
			echo "X64DBGURL=\"$X64DBGURL\""
			echo "## SteamGridDB Api URL"
			echo "BASESTEAMGRIDDBAPI=\"$BASESTEAMGRIDDBAPI\""
			echo "## Conty DL URL"
			echo "CONTYRELURL=\"$CONTYRELURL\""
			echo "## Mod Organizer 2 Project URL"
			echo "MO2PROJURL=\"$MO2PROJURL\""
			echo "## HedgeModManager Project URL"
			echo "HMMPROJURL=\"$HMMPROJURL\""
			echo "## SpecialK Project URL"
			echo "SPEKPROJURL=\"$SPEKPROJURL\""
			echo "## $DPRS DL URL"
			echo "DPRSRELURL=\"$DPRSRELURL\""
			echo "## $DEPS URL"
			echo "DEPURL=\"$DEPURL\""
			echo "## $SPEK URL"
			echo "SPEKURL=\"$SPEKURL\""
			echo "## $SPEK GH URL"
			echo "SPEKGHURL=\"$SPEKGHURL\""
			echo "## $SPEK compatibility URL"
			echo "SPEKCOMPURL=\"$SPEKCOMPURL\""
			echo "## $FWS DL URL"
			echo "FWSURL=\"$FWSURL\""
			echo "## $YAD appimage url"
			echo "YAIURL=\"$YAIURL\""
			echo "## wine reload URL"
			echo "WINERELOADURL=\"$WINERELOADURL\""
			echo "## $GEOELF url"
			echo "GEOELFURL=\"$GEOELFURL\""
			echo "## $WDIBURL url"
			echo "WDIBURL=\"$WDIBURL\""
			} >> "$1"
			#ENDsaveCfgurl
		fi
	}

	function saveCfggui {

		setDefaultCfgValues "$2"

		if [ -f "$1" ]; then
			updateConfigFile "$1" "${FUNCNAME[0]}" "$3"
		else
			#STARTsaveCfggui
			{
			echo "## config Version: $PROGVERS"
			echo "##########################"
			echo "## Settings Selection GUI:"
			echo "WINX=\"$WINX\""
			echo "WINY=\"$WINY\""
			echo "POSX=\"$POSX\""
			echo "POSY=\"$POSY\""
			} >> "$1"
			#ENDsaveCfggui
		fi
	}

	function saveCfgglobal {

		setDefaultCfgValues "$2"

		if [ -f "$1" ]; then
			updateConfigFile "$1" "${FUNCNAME[0]}" "$3"
		else
			#STARTsaveCfgglobal
			{
			echo "## config Version: $PROGVERS"
			echo "##########################"
			echo "## $DESC_STLLANG"
			echo "STLLANG=\"$STLLANG\""
			echo "## $DESC_SKIPINTDEPCHECK"
			echo "SKIPINTDEPCHECK=\"$SKIPINTDEPCHECK\""
			echo "## $DESC_YAD"
			echo "YAD=\"$YAD\""
			echo "## $DESC_CUSTPROTDLDIR"
			echo "CUSTPROTDLDIR=\"$CUSTPROTDLDIR\""
			echo "## $DESC_CUSTPROTEXTDIR"
			echo "CUSTPROTEXTDIR=\"$CUSTPROTEXTDIR\""
			echo "## $DESC_CUPROTOCOMPAT"
			echo "CUPROTOCOMPAT=\"$CUPROTOCOMPAT\""
			echo "## $DESC_WINEDLDIR"
			echo "WINEDLDIR=\"$WINEDLDIR\""
			echo "## $DESC_WINEEXTDIR"
			echo "WINEEXTDIR=\"$WINEEXTDIR\""
			echo "## $DESC_USEGLOBALWINEDPI"
			echo "USEGLOBALWINEDPI=\"$USEGLOBALWINEDPI\""
			echo "## $DESC_GLOBALWINEDPI"
			echo "GLOBALWINEDPI=\"$GLOBALWINEDPI\""
			echo "## $DESC_AUTOLASTPROTON"
			echo "AUTOLASTPROTON=\"$AUTOLASTPROTON\""
			echo "## $DESC_STRACEDIR"
			echo "STRACEDIR=\"$STRACEDIR\""
			echo "## $(strFix "$DESC_LOGDIR" "$PROGNAME")"
			echo "LOGDIR=\"$LOGDIR\""
			echo "## $DESC_LOGLEVEL"
			echo "LOGLEVEL=\"$LOGLEVEL\""
			echo "## $DESC_RESETLOG"
			echo "RESETLOG=\"$RESETLOG\""
			echo "## $DESC_STLEDITOR"
			echo "STLEDITOR=\"$STLEDITOR\""
			echo "## $DESC_MAXASK"
			echo "MAXASK=\"$MAXASK\""
			echo "## $DESC_BROWSER"
			echo "BROWSER=\"$BROWSER\""
			echo "## $DESC_USENOTIFIER"
			echo "USENOTIFIER=\"$USENOTIFIER\""
			echo "## $DESC_NOTY"
			echo "NOTY=\"$NOTY\""
			echo "## $DESC_NOTYARGS"
			echo "NOTYARGS=\"$NOTYARGS\""
			echo "## $DESC_NETMON"
			echo "NETMON=\"$NETMON\""
			echo "## $DESC_NETOPTS"
			echo "NETOPTS=\"$NETOPTS\""
			echo "## $DESC_NETMONDIR"
			echo "NETMONDIR=\"$NETMONDIR\""
			echo "## $DESC_VRVIDEOPLAYER"
			echo "VRVIDEOPLAYER=\"$VRVIDEOPLAYER\""
			echo "## $DESC_GLOBALSBSTWEAKS"
			echo "GLOBALSBSTWEAKS=\"$GLOBALSBSTWEAKS\""
			echo "## $DESC_GLOBALTWEAKS"
			echo "GLOBALTWEAKS=\"$GLOBALTWEAKS\""
			echo "## $DESC_GLOBALCOLLECTIONDIR"
			echo "GLOBALCOLLECTIONDIR=\"$GLOBALCOLLECTIONDIR\""
			echo "## $DESC_GLOBALMISCDIR"
			echo "GLOBALMISCDIR=\"$GLOBALMISCDIR\""
			echo "## $DESC_GLOBALEVALDIR"
			echo "GLOBALEVALDIR=\"$GLOBALEVALDIR\""
			echo "## $DESC_GLOBALSTLLANGDIR"
			echo "GLOBALSTLLANGDIR=\"$GLOBALSTLLANGDIR\""
			echo "## $DESC_GLOBALSTLGUIDIR"
			echo "GLOBALSTLGUIDIR=\"$GLOBALSTLGUIDIR\""
			echo "## $DESC_BOXTRONCMD"
			echo "BOXTRONCMD=\"$BOXTRONCMD\""
			echo "## $DESC_BOXTRONARGS"
			echo "BOXTRONARGS=\"$BOXTRONARGS\""
			echo "## $DESC_ROBERTACMD"
			echo "ROBERTACMD=\"$ROBERTACMD\""
			echo "## $DESC_ROBERTAARGS"
			echo "ROBERTAARGS=\"$ROBERTAARGS\""
			echo "## $DESC_LUXTORPEDACMD"
			echo "LUXTORPEDACMD=\"$LUXTORPEDACMD\""
			echo "## $DESC_LUXTORPEDAARGS"
			echo "LUXTORPEDAARGS=\"$LUXTORPEDAARGS\""
			echo "## $DESC_DOWNLOAD_RESHADE"
			echo "DOWNLOAD_RESHADE=\"$DOWNLOAD_RESHADE\""
			echo "## $DESC_RSVERS"
			echo "RSVERS=\"$RSVERS\""
			echo "## $DESC_AUTOBUMPRESHADE"
			echo "AUTOBUMPRESHADE=\"$AUTOBUMPRESHADE\""
			echo "## $DESC_RESHADESRCDIR"
			echo "RESHADESRCDIR=\"$RESHADESRCDIR\""
			echo "## $DESC_D3D47_64"
			echo "D3D47_64=\"$D3D47_64\""
			echo "## $DESC_D3D47_32"
			echo "D3D47_32=\"$D3D47_32\""
			echo "## $DESC_RS_64"
			echo "RS_64=\"$RS_64\""
			echo "## $DESC_RS_32"
			echo "RS_32=\"$RS_32\""
			echo "## $DESC_RS_64_VK"
			echo "RS_64_VK=\"$RS_64_VK\""
			echo "## $DESC_RS_32_VK"
			echo "RS_32_VK=\"$RS_32_VK\""
			echo "## $DESC_DLSHADER"
			echo "DLSHADER=\"$DLSHADER\""
			echo "## $DESC_SAVESETSIZE"
			echo "SAVESETSIZE=\"$SAVESETSIZE\""
			echo "## $DESC_STARTMENU"
			echo "STARTMENU=\"$STARTMENU\""
			echo "## $DESC_HEADLINEFONT"
			echo "HEADLINEFONT=\"$HEADLINEFONT\""
			echo "## $DESC_YADFORCEXWAYLAND"
			echo "YADFORCEXWAYLAND=\"$YADFORCEXWAYLAND\""
			echo "## DESC_USERSSPEKVERS"
			echo "USERSSPEKVERS=\"$USERSSPEKVERS\""
			echo "## $DESC_RSSPEKVERS"
			echo "RSSPEKVERS=\"$RSSPEKVERS\""
			echo "## $DESC_USEWINDECO"
			echo "USEWINDECO=\"$USEWINDECO\""
			echo "## $DESC_USETRAYICON"
			echo "USETRAYICON=\"$USETRAYICON\""
			echo "## $DESC_USEGAMEPICS"
			echo "USEGAMEPICS=\"$USEGAMEPICS\""
			echo "## $DESC_USECUSTOMFALLBACKPIC"
			echo "USECUSTOMFALLBACKPIC=\"$USECUSTOMFALLBACKPIC\""
			echo "## $DESC_GITHUBUSER"
			echo "GITHUBUSER=\"$GITHUBUSER\""
			echo "## $DESC_DLGAMEDATA"
			echo "DLGAMEDATA=\"$DLGAMEDATA\""
			echo "## $DESC_DLSTEAMDECKCOMPATINFO"
			echo "DLSTEAMDECKCOMPATINFO=\"$DLSTEAMDECKCOMPATINFO\""
			echo "## $DESC_USEPDBRATING"
			echo "USEPDBRATING=\"$USEPDBRATING\""
			echo "## $DESC_PDBRATINGCACHE"
			echo "PDBRATINGCACHE=\"$PDBRATINGCACHE\""
			echo "## $DESC_DLWINETRICKS"
			echo "DLWINETRICKS=\"$DLWINETRICKS\""
			echo "## $DESC_USEVORTEXPROTON"
			echo "USEVORTEXPROTON=\"$USEVORTEXPROTON\""
			echo "## $DESC_VORTEXCOMPDATA"
			echo "VORTEXCOMPDATA=\"$VORTEXCOMPDATA\""
			echo "## $DESC_VORTEXDOWNLOADPATH"
			echo "VORTEXDOWNLOADPATH=\"$VORTEXDOWNLOADPATH\""
			echo "## $DESC_USEVORTEXPRERELEASE"
			echo "USEVORTEXPRERELEASE=\"$USEVORTEXPRERELEASE\""
			echo "## $DESC_VORTEXUSESLR"
			echo "VORTEXUSESLR=\"$VORTEXUSESLR\""
			echo "## $DESC_VORTEXUSESLRPOSTINSTALL"
			echo "VORTEXUSESLR=\"$VORTEXUSESLRPOSTINSTALL\""
			echo "## $DESC_DISABLEVORTEXAUTOUPDATE"
			echo "DISABLEVORTEXAUTOUPDATE=\"$DISABLEVORTEXAUTOUPDATE\""
			echo "## $DESC_USEVORTEXCUSTOMVER"
			echo "USEVORTEXCUSTOMVER=\"$USEVORTEXCUSTOMVER\""
			echo "## $DESC_VORTEXCUSTOMVER"
			echo "VORTEXCUSTOMVER=\"$VORTEXCUSTOMVER\""
			echo "## $DESC_VORTEXDEVICESCALEFACTOR"
			echo "VORTEXDEVICESCALEFACTOR=\"$VORTEXDEVICESCALEFACTOR\""
			echo "## $(strFix "$DESC_DISABLE_AUTOSTAGES" "$PROGNAME")"
			echo "DISABLE_AUTOSTAGES=\"$DISABLE_AUTOSTAGES\""
			echo "## $DESC_NOSTEAMSTLDEF"
			echo "NOSTEAMSTLDEF=\"$NOSTEAMSTLDEF\""
			echo "## $DESC_SGDBAPIKEY"
			echo "SGDBAPIKEY=\"$SGDBAPIKEY\""
			echo "## Hero $DESC_SGDBDLHERO"
			echo "SGDBDLHERO=\"$SGDBDLHERO\""
			echo "## Hero $DESC_SGDBDIMS"
			echo "SGDBHERODIMS=\"$SGDBHERODIMS\""
			echo "## Hero $DESC_SGDBTYPES"
			echo "SGDBHEROTYPES=\"$SGDBHEROTYPES\""
			echo "## Hero $DESC_SGDBSTYLES"
			echo "SGDBHEROSTYLES=\"$SGDBHEROSTYLES\""
			echo "## Hero $DESC_SGDBNSFW"
			echo "SGDBHERONSFW=\"$SGDBHERONSFW\""
			echo "## Hero $DESC_SGDBHUMOR"
			echo "SGDBHEROHUMOR=\"$SGDBHEROHUMOR\""
			echo "## Hero $DESC_SGDBEPILEPSY"
			echo "SGDBHEROEPILEPSY=\"$SGDBHEROEPILEPSY\""
			echo "## Logo $DESC_SGDBDLLOGO"
			echo "SGDBDLLOGO=\"$SGDBDLLOGO\""
			echo "## Logo $DESC_SGDBTYPES"
			echo "SGDBLOGOTYPES=\"$SGDBLOGOTYPES\""
			echo "## Logo $DESC_SGDBSTYLES"
			echo "SGDBLOGOSTYLES=\"$SGDBLOGOSTYLES\""
			echo "## Logo $DESC_SGDBNSFW"
			echo "SGDBLOGONSFW=\"$SGDBLOGONSFW\""
			echo "## Logo $DESC_SGDBHUMOR"
			echo "SGDBLOGOHUMOR=\"$SGDBLOGOHUMOR\""
			echo "## Logo $DESC_SGDBEPILEPSY"
			echo "SGDBLOGOEPILEPSY=\"$SGDBLOGOEPILEPSY\""
			echo "## Boxart $DESC_SGDBDLBOXART"
			echo "SGDBDLBOXART=\"$SGDBDLBOXART\""
			echo "## Boxart $DESC_SGDBDIMS"
			echo "SGDBBOXARTDIMS=\"$SGDBBOXARTDIMS\""
			echo "## Boxart $DESC_SGDBTYPES"
			echo "SGDBBOXARTTYPES=\"$SGDBBOXARTTYPES\""
			echo "## Boxart $DESC_SGDBSTYLES"
			echo "SGDBBOXARTSTYLES=\"$SGDBBOXARTSTYLES\""
			echo "## Boxart $DESC_SGDBNSFW"
			echo "SGDBBOXARTNSFW=\"$SGDBBOXARTNSFW\""
			echo "## Boxart $DESC_SGDBHUMOR"
			echo "SGDBBOXARTHUMOR=\"$SGDBBOXARTHUMOR\""
			echo "## Boxart $DESC_SGDBEPILEPSY"
			echo "SGDBBOXARTEPILEPSY=\"$SGDBBOXARTEPILEPSY\""
			echo "## Tenfoot $DESC_SGDBDLTENFOOT"
			echo "SGDBDLTENFOOT=\"$SGDBDLTENFOOT\""
			echo "## Tenfoot $DESC_SGDBDIMS"
			echo "SGDBTENFOOTDIMS=\"$SGDBTENFOOTDIMS\""
			echo "## Tenfoot $DESC_SGDBTYPES"
			echo "SGDBTENFOOTTYPES=\"$SGDBTENFOOTTYPES\""
			echo "## Tenfoot $DESC_SGDBSTYLES"
			echo "SGDBTENFOOTSTYLES=\"$SGDBTENFOOTSTYLES\""
			echo "## Tenfoot $DESC_SGDBNSFW"
			echo "SGDBTENFOOTNSFW=\"$SGDBTENFOOTNSFW\""
			echo "## Tenfoot $DESC_SGDBHUMOR"
			echo "SGDBTENFOOTHUMOR=\"$SGDBTENFOOTHUMOR\""
			echo "## Tenfoot $DESC_SGDBEPILEPSY"
			echo "SGDBTENFOOTEPILEPSY=\"$SGDBTENFOOTEPILEPSY\""
			echo "## $DESC_SGDBDLTOSTEAM"
			echo "SGDBDLTOSTEAM=\"$SGDBDLTOSTEAM\""
			echo "## $DESC_SGDBHASFILE"
			echo "SGDBHASFILE=\"$SGDBHASFILE\""
			echo "## $DESC_SGDBAUTODL"
			echo "SGDBAUTODL=\"$SGDBAUTODL\""
			echo "## $DESC_STORECOMPDATTITLE"
			echo "STORECOMPDATTITLE=\"$STORECOMPDATTITLE\""
			echo "## $DESC_CUSTCONTY"
			echo "CUSTCONTY=\"$CUSTCONTY\""
			echo "## $DESC_UPDATECONTY"
			echo "UPDATECONTY=\"$UPDATECONTY\""
			echo "## $DESC_LOGPLAYTIME"
			echo "LOGPLAYTIME=\"$LOGPLAYTIME\""
			echo "## $DESC_DPRSCOMPDATA"
			echo "DPRSCOMPDATA=\"$DPRSCOMPDATA\""
			echo "## $DESC_USEDPRSPROTON"
			echo "USEDPRSPROTON=\"$USEDPRSPROTON\""
			echo "## $DESC_DPRSUSEVDFSYMLINKS"
			echo "DPRSUSEVDFSYMLINKS=\"$DPRSUSEVDFSYMLINKS\""
			echo "## $DESC_DPRSPAUTOUP"
			echo "DPRSPAUTOUP=\"$DPRSPAUTOUP\""
			echo "## $DESC_DEPSAUTOUP"
			echo "DEPSAUTOUP=\"$DEPSAUTOUP\""
			echo "## $DESC_MO2COMPDATA"
			echo "MO2COMPDATA=\"$MO2COMPDATA\""
			echo "## $DESC_USEMO2PROTON"
			echo "USEMO2PROTON=\"$USEMO2PROTON\""
			echo "## $DESC_USEMO2CUSTOMINSTALLER"
			echo "USEMO2CUSTOMINSTALLER=\"$USEMO2CUSTOMINSTALLER\""
			echo "## $DESC_MO2CUSTOMINSTALLER"
			echo "MO2CUSTOMINSTALLER=\"$MO2CUSTOMINSTALLER\""
			echo "## $DESC_HMMCOMPDATA"
			echo "HMMCOMPDATA=\"$HMMCOMPDATA\""
			echo "## $DESC_USEHMMPROTON"
			echo "USEHMMPROTON=\"$USEHMMPROTON\""
			echo "## $DESC_HMMDLVER"
			echo "HMMDLVER=\"$HMMDLVER\""
			echo "## $DESC_USETERM"
			echo "USETERM=\"$USETERM\""
			echo "## $DESC_TERMARGS"
			echo "TERMARGS=\"$TERMARGS\""
			echo "## $DESC_STEAMDECK_AUTOUP"
			echo "STEAMDECK_AUTOUP=\"$STEAMDECK_AUTOUP\""

			} >> "$1"
			#ENDsaveCfgglobal
		fi
		updateEditor "$1"
	}

	function saveCfgdefault_template {

		setDefaultCfgValues "$2"

		if [ -f "$1" ]; then
			updateConfigFile "$1" "${FUNCNAME[0]}" "$3"
		else
			#STARTsaveCfgdefault_template
			{
			echo "## config Version: $PROGVERS"
			echo "##########################"
			echo "## $DESC_KEEPSTLOPEN"
			echo "KEEPSTLOPEN=\"$KEEPSTLOPEN\""
			echo "## $DESC_USESLR"
			echo "USESLR=\"$USESLR\""
			echo "## $DESC_FORCESLR"
			echo "FORCESLR=\"$FORCESLR\""
			echo "## $DESC_IGNORECOMPATSLR"
			echo "IGNORECOMPATSLR=\"$IGNORECOMPATSLR\""
			echo "## $DESC_USEREAP"
			echo "USEREAP=\"$USEREAP\""
			echo "## $DESC_FORCEREAP"
			echo "FORCEREAP=\"$FORCEREAP\""
			echo "## $DESC_USEPROTON"
			echo "USEPROTON=\"$USEPROTON\""
			echo "## $DESC_REDIRCOMPDATA"
			echo "REDIRCOMPDATA=\"$REDIRCOMPDATA\""
			echo "## $DESC_REDIRSTEAMUSER"
			echo "REDIRSTEAMUSER=\"$REDIRSTEAMUSER\""
			echo "## $DESC_ONLYPROTMAJORREDIRECT"
			echo "ONLYPROTMAJORREDIRECT=\"$ONLYPROTMAJORREDIRECT\""
			echo "## $DESC_AUTOBUMPGE"
			echo "AUTOBUMPGE=\"$AUTOBUMPGE\""
			echo "## $DESC_AUTOBUMPPROTON"
			echo "AUTOBUMPPROTON=\"$AUTOBUMPPROTON\""
			echo "## $DESC_USECUSTOMCMD"
			echo "USECUSTOMCMD=\"$USECUSTOMCMD\""
			echo "## $DESC_CUSTOMCMD"
			echo "CUSTOMCMD=\"$CUSTOMCMD\""
			echo "## $DESC_CUSTOMCMD_ARGS"
			echo "CUSTOMCMD_ARGS=\"$CUSTOMCMD_ARGS\""
			echo "## $DESC_ONLY_CUSTOMCMD"
			echo "ONLY_CUSTOMCMD=\"$ONLY_CUSTOMCMD\""
			echo "## $DESC_FORK_CUSTOMCMD"
			echo "FORK_CUSTOMCMD=\"$FORK_CUSTOMCMD\""
			echo "## $DESC_EXTPROGS_CUSTOMCMD"
			echo "EXTPROGS_CUSTOMCMD=\"$EXTPROGS_CUSTOMCMD\""
			echo "## $DESC_CUSTOMCMD_USESLR"
			echo "CUSTOMCMD_USESLR=\"$CUSTOMCMD_USESLR\""
			echo "## $DESC_CUSTOMCMD_FORCEWIN"
			echo "CUSTOMCMDFORCEWIN=\"$CUSTOMCMDFORCEWIN\""
			echo "## $DESC_WAITFORCUSTOMCMD"
			echo "WAITFORCUSTOMCMD=\"$WAITFORCUSTOMCMD\""
			echo "## $DESC_INJECT_CUSTOMCMD"
			echo "INJECT_CUSTOMCMD=\"$INJECT_CUSTOMCMD\""
			echo "## $DESC_INJECTWAIT"
			echo "INJECTWAIT=\"$INJECTWAIT\""
			echo "## $DESC_USEIGCS"
			echo "USEIGCS=\"$USEIGCS\""
			echo "## $DESC_UUUSEIGCS"
			echo "UUUSEIGCS=\"$UUUSEIGCS\""
			echo "## $DESC_IGCSWAIT"
			echo "IGCSWAIT=\"$IGCSWAIT\""
			echo "## $DESC_UUUSEPATCH"
			echo "UUUSEPATCH=\"$UUUSEPATCH\""
			echo "## $DESC_UUUPATCHWAIT"
			echo "UUUPATCHWAIT=\"$UUUPATCHWAIT\""
			echo "## $DESC_UUUSEVR"
			echo "UUUSEVR=\"$UUUSEVR\""
			echo "## $DESC_GAMEARGS"
			echo "GAMEARGS=\"$GAMEARGS\""
			echo "## $DESC_HARDARGS"
			echo "HARDARGS=\"$HARDARGS\""
			echo "## $DESC_USEGAMEMODERUN"
			echo "USEGAMEMODERUN=\"$USEGAMEMODERUN\""
			echo "## $DESC_USEGAMESCOPE"
			echo "USEGAMESCOPE=\"$USEGAMESCOPE\""
			echo "## $DESC_GAMESCOPE_ARGS"
			echo "GAMESCOPE_ARGS=\"$GAMESCOPE_ARGS\""
			echo "## $DESC_USEOBSCAP"
			echo "USEOBSCAP=\"$USEOBSCAP\""
			echo "## $DESC_USEZINK"
			echo "USEZINK=\"$USEZINK\""
			echo "## $DESC_USEPRIMERUN"
			echo "USEPRIMERUN=\"$USEPRIMERUN\""
			echo "## $DESC_TOGSTEAMWEBHELPER"
			echo "TOGSTEAMWEBHELPER=\"$TOGSTEAMWEBHELPER\""
			echo "## $DESC_USEMANGOHUD"
			echo "USEMANGOHUD=\"$USEMANGOHUD\""
			echo "## $DESC_USEMANGOAPP"
			echo "USEMANGOAPP=\"$USEMANGOAPP\""
			echo "## $DESC_MAHUBIN"
			echo "MAHUBIN=\"$MAHUBIN\""
			echo "## $DESC_MAHUARGS"
			echo "MAHUARGS=\"$MAHUARGS\""
			echo "## $DESC_MAHUDLSYM"
			echo "MAHUDLSYM=\"$MAHUDLSYM\""
			echo "## $DESC_LDPMAHU"
			echo "LDPMAHU=\"$LDPMAHU\""
			echo "## $DESC_MAHUVAR"
			echo "MAHUVAR=\"$MAHUVAR\""
			echo "## $DESC_USEMANGOHUDSTLCFG"
			echo "USEMANGOHUDSTLCFG=\"$USEMANGOHUDSTLCFG\""
			echo "## $DESC_VULKANPOSTPROCESSOR"
			echo "VULKANPOSTPROCESSOR=\"$VULKANPOSTPROCESSOR\""
			echo "## $DESC_RUN_NYRNA"
			echo "RUN_NYRNA=\"$RUN_NYRNA\""
			echo "## $DESC_RUN_REPLAY"
			echo "RUN_REPLAY=\"$RUN_REPLAY\""
			echo "## $DESC_RUN_X64DBG"
			echo "RUN_X64DBG=\"$RUN_X64DBG\""
			echo "## $DESC_X64DBG_ATTACHONSTARTUP"
			echo "X64DBG_ATTACHONSTARTUP=\"$X64DBG_ATTACHONSTARTUP\""
			echo "## $DESC_RUN_GDB"
			echo "RUN_GDB=\"$RUN_GDB\""
			echo "## $DESC_USE_WDIB"
			echo "USE_WDIB=\"$USE_WDIB\""
			echo "## $DESC_USEVORTEX"
			echo "USEVORTEX=\"$USEVORTEX\""
			echo "## $DESC_WAITVORTEX"
			echo "WAITVORTEX=\"$WAITVORTEX\""
			echo "## $DESC_RUN_VORTEX_WINETRICKS"
			echo "RUN_VORTEX_WINETRICKS=\"$RUN_VORTEX_WINETRICKS\""
			echo "## $DESC_RUN_VORTEX_WINECFG"
			echo "RUN_VORTEX_WINECFG=\"$RUN_VORTEX_WINECFG\""
			echo "## $DESC_CHANGE_PULSE_LATENCY"
			echo "CHANGE_PULSE_LATENCY=\"$CHANGE_PULSE_LATENCY\""
			echo "## $DESC_STL_PULSE_LATENCY_MSEC"
			echo "STL_PULSE_LATENCY_MSEC=\"$STL_PULSE_LATENCY_MSEC\""
			echo "## $DESC_TOGGLEWINDOWS"
			echo "TOGGLEWINDOWS=\"$TOGGLEWINDOWS\""
			echo "## $DESC_RUN_WINETRICKS"
			echo "RUN_WINETRICKS=\"$RUN_WINETRICKS\""
			echo "## $DESC_WINETRICKSPAKS"
			echo "WINETRICKSPAKS=\"$WINETRICKSPAKS\""
			echo "## $DESC_RUN_WINECFG"
			echo "RUN_WINECFG=\"$RUN_WINECFG\""
			echo "## $DESC_USEWINE"
			echo "USEWINE=\"$USEWINE\""
			echo "## $DESC_USEWICO"
			echo "USEWICO=\"$USEWICO\""
			echo "## $DESC_WINEVERSION"
			echo "WINEVERSION=\"$WINEVERSION\""
			echo "## $DESC_WINEDEFAULT"
			echo "WINEDEFAULT=\"$WINEDEFAULT\""
			echo "## $DESC_VIRTUALDESKTOP"
			echo "VIRTUALDESKTOP=\"$VIRTUALDESKTOP\""
			echo "## $DESC_VDRES"
			echo "VDRES=\"$VDRES\""
			echo "## $DESC_USEBOXTRON"
			echo "USEBOXTRON=\"$USEBOXTRON\""
			echo "## $DESC_USEROBERTA"
			echo "USEROBERTA=\"$USEROBERTA\""
			echo "## $DESC_USELUXTORPEDA"
			echo "USELUXTORPEDA=\"0\""
			echo "## $DESC_REGEDIT"
			echo "REGEDIT=\"$REGEDIT\""
			echo "## $DESC_USEGEOELF"
			echo "USEGEOELF=\"$USEGEOELF\""
			echo "## $DESC_AUTOGEOELF"
			echo "AUTOGEOELF=\"$AUTOGEOELF\""
			echo "## $DESC_RESHADE_DEPTH3D"
			echo "RESHADE_DEPTH3D=\"$RESHADE_DEPTH3D\""
			echo "## $DESC_USERESHADE"
			echo "USERESHADE=\"$USERESHADE\""
			echo "## $DESC_CUSTOMCMDRESHADE"
			echo "CUSTOMCMDRESHADE=\"$CUSTOMCMDRESHADE\""
			echo "## $DESC_RESHADEUPDATE"
			echo "RESHADEUPDATE=\"$RESHADEUPDATE\""
			echo "## $DESC_CREATERESHINI"
			echo "CREATERESHINI=\"$CREATERESHINI\""
			echo "## $DESC_RESHADEDLLNAME"
			echo "RESHADEDLLNAME=\"$RESHADEDLLNAME\""
			echo "## $DESC_RESHADEOVERRIDETOGGLE"
			echo "RSOVRD=\"$RSOVRD\""
			echo "## $DESC_RESHADEOVERRIDEVERSION"
			echo "RSOVRVERS=\"$RSOVRVERS\""
			echo "## $DESC_CHOOSESHADERS"
			echo "CHOOSESHADERS=\"$CHOOSESHADERS\""
			echo "## $DESC_ALTEXEPATH"
			echo "ALTEXEPATH=\"$ALTEXEPATH\""
			echo "## $DESC_ARCHALTEXE"
			echo "ARCHALTEXE=\"$ARCHALTEXE\""
			echo "## $DESC_USEOPENVRFSR"
			echo "USEOPENVRFSR=\"$USEOPENVRFSR\""
			echo "## $DESC_RUNSBSVR"
			echo "RUNSBSVR=\"$RUNSBSVR\""
			echo "## $DESC_VRVIDEOPLAYERARGS"
			echo "VRVIDEOPLAYERARGS=\"$VRVIDEOPLAYERARGS\""
			echo "## $DESC_SBSZOOM"
			echo "SBSZOOM=\"$SBSZOOM\""
			echo "## $DESC_SBSVRGEOELF"
			echo "SBSVRGEOELF=\"$SBSVRGEOELF\""
			echo "## $DESC_SBSVRRS"
			echo "SBSVRRS=\"$SBSVRRS\""
			echo "## $DESC_SBSRS"
			echo "SBSRS=\"$SBSRS\""
			echo "## $DESC_MINVRWINH"
			echo "MINVRWINH=\"$MINVRWINH\""
			echo "## $DESC_WAITFORTHISPID"
			echo "WAITFORTHISPID=\"$WAITFORTHISPID\""
			echo "## $DESC_WAITEDITOR"
			echo "WAITEDITOR=\"$WAITEDITOR\""
			echo "## $DESC_STEAMAPPIDFILE"
			echo "STEAMAPPIDFILE=\"$STEAMAPPIDFILE\""
			echo "## $DESC_CHECKCOLLECTIONS"
			echo "CHECKCOLLECTIONS=\"$CHECKCOLLECTIONS\""
			echo "## $DESC_BACKUPSTEAMUSER"
			echo "BACKUPSTEAMUSER=\"$BACKUPSTEAMUSER\""
			echo "## $DESC_RESTORESTEAMUSER"
			echo "RESTORESTEAMUSER=\"$RESTORESTEAMUSER\""
			echo "## $DESC_USESUSYM"
			echo "USESUSYM=\"$USESUSYM\""
			echo "## $DESC_USEGLOBSUSYM"
			echo "USEGLOBSUSYM=\"$USEGLOBSUSYM\""
			echo "## $DESC_USE_STLDXVKCFG"
			echo "USE_STLDXVKCFG=\"$USE_STLDXVKCFG\""
			echo "## $(strFix "$DESC_DXVKVARS" "$DXVKURL")"
			echo "DXVK_HUD=\"$DXVK_HUD\""
			echo "## $DESC_DXVK_LOG_LEVEL"
			echo "DXVK_LOG_LEVEL=\"$DXVK_LOG_LEVEL\""
			echo "## $DESC_DXVK_LOG_PATH"
			echo "DXVK_LOG_PATH=\"$DXVK_LOG_PATH\""
			echo "## $DESC_DXVK_SCALE"
			echo "DXVK_SCALE=\"$DXVK_SCALE\""
			echo "## $DESC_DXVK_FPSLIMIT"
			echo "DXVK_FPSLIMIT=\"$DXVK_FPSLIMIT\""
			echo "## $DESC_DXVK_ASYNC"
			echo "DXVK_ASYNC=\"$DXVK_ASYNC\""
			echo "## $DESC_DXVK_HDR"
			echo "DXVK_HDR=\"$DXVK_HDR\""
			echo "## $DESC_STRACERUN"
			echo "STRACERUN=\"$STRACERUN\""
			echo "## $DESC_STRACEOPTS"
			echo "STRACEOPTS=\"$STRACEOPTS\""
			echo "## $DESC_USENETMON"
			echo "USENETMON=\"$USENETMON\""
			echo "## $DESC_BLOCKINTERNET"
			echo "BLOCKINTERNET=\"$BLOCKINTERNET\""
			echo "## $DESC_PROTON_LOG"
			echo "PROTON_LOG=\"$PROTON_LOG\""
			echo "## $DESC_PROTON_LOG_DIR"
			echo "PROTON_LOG_DIR=\"$PROTON_LOG_DIR\""
			echo "## $DESC_USEWINEDEBUGPROTON"
			echo "USEWINEDEBUGPROTON=\"$USEWINEDEBUGPROTON\""
			echo "## $DESC_PROTON_DUMP_DEBUG_COMMANDS"
			echo "PROTON_DUMP_DEBUG_COMMANDS=\"$PROTON_DUMP_DEBUG_COMMANDS\""
			echo "## $DESC_PROTON_DEBUG_DIR"
			echo "PROTON_DEBUG_DIR=\"$PROTON_DEBUG_DIR\""
			echo "## $DESC_PROTON_USE_WINED3D"
			echo "PROTON_USE_WINED3D=\"$PROTON_USE_WINED3D\""
			echo "## $DESC_PROTON_NO_D3D11"
			echo "PROTON_NO_D3D11=\"$PROTON_NO_D3D11\""
			echo "## $DESC_PROTON_NO_D3D10"
			echo "PROTON_NO_D3D10=\"$PROTON_NO_D3D10\""
			echo "## $DESC_PROTON_NO_ESYNC"
			echo "PROTON_NO_ESYNC=\"$PROTON_NO_ESYNC\""
			echo "## $DESC_PROTON_NO_FSYNC"
			echo "PROTON_NO_FSYNC=\"$PROTON_NO_FSYNC\""
			echo "## $DESC_ENABLE_WINESYNC"
			echo "ENABLE_WINESYNC=\"$ENABLE_WINESYNC\""
			echo "## $DESC_PROTON_ENABLE_NVAPI"
			echo "PROTON_ENABLE_NVAPI=\"$PROTON_ENABLE_NVAPI\""
			echo "## $DESC_PROTON_HIDE_NVIDIA_GPU"
			echo "PROTON_HIDE_NVIDIA_GPU=\"$PROTON_HIDE_NVIDIA_GPU\""
			echo "## $DESC_USEDLSS"
			echo "USEDLSS=\"$USEDLSS\""
			echo "## $DESC_USERAYTRACING"
			echo "USERAYTRACING=\"$USERAYTRACING\""
			echo "## $DESC_PROTON_FORCE_LARGE_ADDRESS_AWARE"
			echo "PROTON_FORCE_LARGE_ADDRESS_AWARE=\"$PROTON_FORCE_LARGE_ADDRESS_AWARE\""
			echo "## $DESC_WINE_FULLSCREEN_INTEGER_SCALING"
			echo "WINE_FULLSCREEN_INTEGER_SCALING=\"$WINE_FULLSCREEN_INTEGER_SCALING\""
			echo "## $DESC_WINE_FULLSCREEN_FSR"
			echo "WINE_FULLSCREEN_FSR=\"$WINE_FULLSCREEN_FSR\""
			echo "## $DESC_WINE_FULLSCREEN_FSR_STRENGTH"
			echo "WINE_FULLSCREEN_FSR_STRENGTH=\"$WINE_FULLSCREEN_FSR_STRENGTH\""
			echo "## $DESC_USEPERGAMEWINEDPI"
			echo "USEPERGAMEWINEDPI=\"$USEPERGAMEWINEDPI\""
			echo "## $DESC_PERGAMEWINEDPI"
			echo "PERGAMEWINEDPI=\"$PERGAMEWINEDPI\""
			echo "## $DESC_CLEANPROTONTEMP"
			echo "CLEANPROTONTEMP=\"$CLEANPROTONTEMP\""
			echo "## $DESC_STLWINEDEBUG"
			echo "STLWINEDEBUG=\"$STLWINEDEBUG\""
			echo "## $DESC_STLWINEDLLOVERRIDES"
			echo "STLWINEDLLOVERRIDES=\"$STLWINEDLLOVERRIDES\""
			echo "## $DESC_WINE_LOG_DIR"
			echo "WINE_LOG_DIR=\"$WINE_LOG_DIR\""
			echo "## $DESC_USERSTART"
			echo "USERSTART=\"$USERSTART\""
			echo "## $DESC_USERSTOP"
			echo "USERSTOP=\"$USERSTOP\""
			echo "## $DESC_AUTOCONTY"
			echo "AUTOCONTY=\"$AUTOCONTY\""
			echo "## $DESC_USECONTY"
			echo "USECONTY=\"$USECONTY\""
			echo "## $DESC_CRASHGUESS"
			echo "CRASHGUESS=\"$CRASHGUESS\""
			echo "## $DESC_ONLYWICO"
			echo "ONLYWICO=\"$ONLYWICO\""
			echo "## $DESC_GAMESCREENRES"
			echo "GAMESCREENRES=\"$GAMESCREENRES\""
			echo "## Default Help URL"
			echo "HELPURL=\"$HELPURL\""
			echo "## Desktop Icon Mode"
			echo "CREATEDESKTOPICON=\"$CREATEDESKTOPICON\""
			echo "## $DESC_FIXSYMLINKS"
			echo "FIXSYMLINKS=\"$FIXSYMLINKS\""
			echo "## $DESC_UNSYMLINK"
			echo "UNSYMLINK=\"$UNSYMLINK\""
			echo "## $DESC_DELPFX"
			echo "DELPFX=\"$DELPFX\""
			echo "## $DESC_RUN_DEPS"
			echo "RUN_DEPS=\"$RUN_DEPS\""
			echo "## $DESC_SORTGARGS"
			echo "SORTGARGS=\"$SORTGARGS\""
			echo "## $DESC_WAITMO2"
			echo "WAITMO2=\"$WAITMO2\""
			echo "## $DESC_MO2SILENTMODEEXEOVERRIDE"
			echo "MO2SILENTMODEDESC_MO2SILENTMODEEXEOVERRIDE=\"$MO2SILENTMODEDESC_MO2SILENTMODEEXEOVERRIDE\""
			echo "## $DESC_MO2MODE"
			echo "MO2MODE=\"$MO2MODE\""
			echo "## $DESC_USESPECIALK"
			echo "USESPECIALK=\"$USESPECIALK\""
			echo "## $DESC_SPEKDLLNAME"
			echo "SPEKDLLNAME=\"$SPEKDLLNAME\""
			echo "## $DESC_USERESHSPEKPLUGIN"
			echo "USERESHSPEKPLUGIN=\"$USERESHSPEKPLUGIN\""
			echo "## $DESC_USESPEKD3D47"
			echo "USESPEKD3D47=\"$USESPEKD3D47\""
			echo "## $DESC_SDLUSEWAYLAND"
			echo "SDLUSEWAYLAND=\"$SDLUSEWAYLAND\""
			echo "## $DESC_STLRAD_PFTST"
			echo "STLRAD_PFTST=\"$STLRAD_PFTST\""
			echo "## $DESC_SPEKVERS"
			echo "SPEKVERS=\"$SPEKVERS\""
			echo "## $DESC_AUTOSPEK"
			echo "AUTOSPEK=\"$AUTOSPEK\""
			echo "## $DESC_USEFWS"
			echo "USEFWS=\"$USEFWS\""
			echo "## $DESC_USEPEV_PELDD"
			echo "USEPEV_PELDD=\"$USEPEV_PELDD\""
			echo "## $DESC_USEPEV_PEPACK"
			echo "USEPEV_PEPACK=\"$USEPEV_PEPACK\""
			echo "## $DESC_USEPEV_PERES"
			echo "USEPEV_PERES=\"$USEPEV_PERES\""
			echo "## $DESC_USEPEV_PESCAN"
			echo "USEPEV_PESCAN=\"$USEPEV_PESCAN\""
			echo "## DESC_USEPEV_PESEC"
			echo "USEPEV_PESEC=\"$USEPEV_PESEC\""
			echo "## $DESC_USEPEV_PESTR"
			echo "USEPEV_PESTR=\"$USEPEV_PESTR\""
			echo "## $DESC_USEPEV_READPE"
			echo "USEPEV_READPE=\"$USEPEV_READPE\""
			echo "## $DESC_STL_VKD3D_CONFIG"
			echo "STL_VKD3D_CONFIG=\"$STL_VKD3D_CONFIG\""
			echo "## $DESC_STL_VKD3D_DEBUG"
			echo "STL_VKD3D_DEBUG=\"$STL_VKD3D_DEBUG\""
			echo "## $DESC_STL_VKD3D_SHADER_DEBUG"
			echo "STL_VKD3D_SHADER_DEBUG=\"$STL_VKD3D_SHADER_DEBUG\""
			echo "## $DESC_STL_VKD3D_LOG_FILE"
			echo "STL_VKD3D_LOG_FILE=\"$STL_VKD3D_LOG_FILE\""
			echo "## $DESC_STL_VKD3D_VULKAN_DEVICE"
			echo "STL_VKD3D_VULKAN_DEVICE=\"$STL_VKD3D_VULKAN_DEVICE\""
			echo "## $DESC_STL_VKD3D_FILTER_DEVICE_NAME"
			echo "STL_VKD3D_FILTER_DEVICE_NAME=\"$STL_VKD3D_FILTER_DEVICE_NAME\""
			echo "## $DESC_STL_VKD3D_DISABLE_EXTENSIONS"
			echo "STL_VKD3D_DISABLE_EXTENSIONS=\"$STL_VKD3D_DISABLE_EXTENSIONS\""
			echo "## $DESC_STL_VKD3D_TEST_DEBUG"
			echo "STL_VKD3D_TEST_DEBUG=\"$STL_VKD3D_TEST_DEBUG\""
			echo "## $DESC_STL_VKD3D_TEST_FILTER"
			echo "STL_VKD3D_TEST_FILTER=\"$STL_VKD3D_TEST_FILTER\""
			echo "## $DESC_STL_VKD3D_TEST_EXCLUDE"
			echo "STL_VKD3D_TEST_EXCLUDE=\"$STL_VKD3D_TEST_EXCLUDE\""
			echo "## $DESC_STL_VKD3D_TEST_PLATFORM"
			echo "STL_VKD3D_TEST_PLATFORM=\"$STL_VKD3D_TEST_PLATFORM\""
			echo "## $DESC_STL_VKD3D_TEST_BUG"
			echo "STL_VKD3D_TEST_BUG=\"$STL_VKD3D_TEST_BUG\""
			echo "## $DESC_STL_VKD3D_PROFILE_PATH"
			echo "STL_VKD3D_PROFILE_PATH=\"$STL_VKD3D_PROFILE_PATH\""
			} >> "$1"

			#ENDsaveCfgdefault_template
		fi
	}

	SCFG="$(basename "${1//.conf/}")"
	"${FUNCNAME[0]}$SCFG" "$1" "$SCFG" "$2"

	if grep "$STLCFGDIR" "$1" >/dev/null ; then
		writelog "UPDATE" "${FUNCNAME[0]} - Replacing '$STLCFGDIR' with 'STLCFGDIR' in '$1'"
		sed "s:$STLCFGDIR:STLCFGDIR:g" -i "$1"
	fi
}

function notiShow {
	if [ "$ONSTEAMDECK" -eq 1 ] && [ "$FIXGAMESCOPE" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Skipping notifier on SteamDeck Game Mode"
		USENOTIFIER=0 # might avoid a 2nd try during this session
	elif [ "$STLQUIET" -eq 1 ]; then
		USENOTIFIER=0
	else
		if [ -n "$2" ] && [ "$2" == "X" ]; then
			if [ -z "$NOTY" ]; then
				NOTY="$(command -v "notify-send")"
			fi
		fi

		if [ -n "$USENOTIFIER" ] && [ "$USENOTIFIER" -eq 1 ] && { [ -z "$2" ] || { [ -n "$2" ] && [ "$2" != "S" ]; };} || { [ -n "$2" ] && [ "$2" == "X" ]; }; then
			if [ -x "$(command -v "$NOTY")" ]; then
				if [ -z "${NOTYARGSARR[0]}" ]; then
					mapfile -d " " -t -O "${#NOTYARGSARR[@]}" NOTYARGSARR < <(printf '%s' "$NOTYARGS")
				fi
				"$NOTY" "${NOTYARGSARR[@]}" "$1"
			else
				writelog "INFO" "${FUNCNAME[0]} - Warning - '$NOTY' not found - disabling notifier"
				USENOTIFIER=0
			fi
		fi

		if [ -n "$2" ] && [ "$2" == "S" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Message '$1' should go to StatusWindow"
			echo "$1"
		fi
	fi
}

function strFix {
	STRIN="$1"
	if [ -z "$2" ]; then
		echo "$STRIN"
	else
		STRIN2="${STRIN//XXX/$2}"
		STRIN3="${STRIN2//YYY/$3}"
		STRIN4="${STRIN3//ZZZ/$4}"
		echo "${STRIN4//QQQ/$5}"
	fi
}

function getProtPathFromCSV {
	delEmptyFile "$PROTONCSV"
	if [ ! -f "$PROTONCSV" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Creating '$PROTONCSV'"
		getAvailableProtonVersions "up" X
	fi

	if [ -f "$PROTONCSV" ]; then
		grep "^$1" "$PROTONCSV" | sort -nr | head -n1 | cut -d ';' -f2
	fi
}

function setRunWineServer {
	if [ "$ISGAME" -eq 2 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Initiated from '$1'"

		if [ -n "$USEPROTON" ] && [ ! -f "$(getProtPathFromCSV "$USEPROTON")" ] && [ "$HAVEINPROTON" -eq 0 ]; then
			fixProtonVersionMismatch "USEPROTON" "$STLGAMECFG"
		fi

		if [ -z "$RUNPROTON" ] && [ "$HAVEINPROTON" -eq 1 ]; then
			RUNPROTON="${INPROTCMD[*]}"
		fi

		if [ -z "$RUNPROTON" ]; then
			setRunProtonFromUseProton
		fi

		CHECKWINED="$(dirname "$RUNPROTON")/$DBW"
		CHECKWINEF="$(dirname "$RUNPROTON")/$FBW"
		if [ -f "$CHECKWINED" ]; then
			RUNWINE="$CHECKWINED"
			RUNWINESERVER="${RUNWINE}server"
			writelog "INFO" "${FUNCNAME[0]} - Set the wine binary for proton in path '$RUNPROTON'"
			writelog "INFO" "${FUNCNAME[0]} - to '$RUNWINE'"
			writelog "INFO" "${FUNCNAME[0]} - and wineserver to '$RUNWINESERVER'"
		elif [ -f "$CHECKWINEF" ]; then
			RUNWINE="$CHECKWINEF"
			RUNWINESERVER="${RUNWINE}server"
			writelog "INFO" "${FUNCNAME[0]} - Set the wine binary for proton in path '$RUNPROTON'"
			writelog "INFO" "${FUNCNAME[0]} - to '$RUNWINE'"
			writelog "INFO" "${FUNCNAME[0]} - and wineserver to '$RUNWINESERVER'"
		else
			writelog "WARN" "${FUNCNAME[0]} - Couldn't find the wine binary for the proton in path '$RUNPROTON' - falling back to 'wine' for plain RUNWINE and 'wineserver' for RUNWINESERVER"
		fi
	fi
}

function setNewProtVars {
	if [ "$ISGAME" -eq 2 ] && [ "$USEWINE" -eq 0 ]; then
		if [ "$HAVEINPROTON" -eq 1 ]; then
			if [ -z "$RUNWINESERVER" ]; then
				setRunWineServer "${FUNCNAME[0]}"
			fi
		else
			# arg1 is absolute proton path
			if [ "$HAVEINPROTON" -eq 0 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Setting new Proton Variables based on '$1'"
				RUNPROTON="$1"
				CHECKWINED="$(dirname "$RUNPROTON")/$DBW"
				CHECKWINEF="$(dirname "$RUNPROTON")/$FBW"

				writelog "INFO" "${FUNCNAME[0]} - Continuing with RUNPROTON='$RUNPROTON'"

				PDTGZ="proton_dist.tar.gz"
				if [ ! -f "$CHECKWINED" ] && [ ! -f "$CHECKWINEF" ] && [ -f "$(dirname "$RUNPROTON")/$PDTGZ" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Wine binary not found, but a proton archive '$PDTGZ' - extracting now"
					mkProjDir "$(dirname "$RUNPROTON")/dist"
					"$TAR" xf "$(dirname "$RUNPROTON")/$PDTGZ" -C "$(dirname "$RUNPROTON")/dist" 2>/dev/null
				fi

				PROTONVERSION="$(setProtonPathVersion "$RUNPROTON")"
				USEPROTON="$PROTONVERSION"

				setRunWineServer "${FUNCNAME[0]}"
			else
				writelog "SKIP" "${FUNCNAME[0]} - Using Steam Proton '$FIRSTUSEPROTON' instead of ${PROGNAME,,} Proton '$USEPROTON', because '$PROGCMD' was used as '$SLO'"
				notiShow "$(strFix "$NOTY_SETNEWPROTVARS" "$FIRSTUSEPROTON")"
			fi
		fi

		if [ "$USEWINEDEBUGPROTON" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using '$STLWINEDEBUG' as Proton 'WINEDEBUG' parameters, because 'USEWINEDEBUGPROTON' is enabled"
			export WINEDEBUG="$STLWINEDEBUG"
		fi

		if [ -n "$STLWINEDLLOVERRIDES" ] && [ "$STLWINEDLLOVERRIDES" != "$NON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using '$STLWINEDLLOVERRIDES' as 'WINEDLLOVERRIDES' parameter"
			export WINEDLLOVERRIDES="$WINEDLLOVERRIDES;$STLWINEDLLOVERRIDES"
		fi
	fi
}

# in case you wonder NOP='Newest Official Proton'
function getNOP {
	createProtonList
	NEWESTPROTRAW="$(printProtonArr | grep "^proton-[0-9]." | sort -nr | head -n1)"

	if [ "$1" == "p" ]; then
		cut -d ';' -f2 <<< "$NEWESTPROTRAW"
	elif [ "$1" == "v" ]; then
		cut -d ';' -f1 <<< "$NEWESTPROTRAW"
	fi
}

function setNOP {
	writelog "INFO" "${FUNCNAME[0]} - Selecting newest official Proton available"
	NOPPATH="$(getNOP "p")"
	if [ -n "$NOPPATH" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Selected '$NOPPATH'"
		NEWPROPA="$( dirname "$NOPPATH" )"
		NEWPROPA="${NEWPROPA%*/}"
		NEWPROPA="${NEWPROPA##*/}"
		notiShow "$(strFix "$NOTY_WANTPROTON2" "$NEWPROPA" "$USEPROTON")"

		setNewProtVars "$NOPPATH"
	else
		writelog "SKIP" "${FUNCNAME[0]} - Haven't found anything"
	fi
}

function needNewProton {
	if [ "$HAVEINPROTON" -eq 0 ]; then
		getAvailableProtonVersions "up"
		export CURWIKI="$PPW/Proton-Versions"
		TITLE="${PROGNAME}-NeedNewProtonVersion"
		pollWinRes "$TITLE"
		writelog "INFO" "${FUNCNAME[0]} - No Proton Version was found - opening a requester to choose from one"
		createProtonList

		PICKPROTON="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --center "$WINDECO" --form --scroll --separator="\n" --quoted-output \
		--text="$(spanFont "$GUI_NEEDNEWPROTON" "H")" \
		--field="$GUI_NEEDNEWPROTON2":CB "$(cleanDropDown "${USEPROTON/#-/ -}" "$PROTYADLIST")" \
		--title="$TITLE" "$GEOM")"

		if [ -n "$PICKPROTON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Selected Proton Version $PICKPROTON"
			setNewProtVars "$(getProtPathFromCSV "$PICKPROTON")"
		else
			writelog "INFO" "${FUNCNAME[0]} - No Proton Version was selected - try again"
			"${FUNCNAME[0]}"
		fi
	fi
}

function dlCustomProton {
	if [[ -n "$CUPROTOCOMPAT" && "$CUPROTOCOMPAT" -eq 1 ]]; then
		CUPROEXTDIR="$STEAMCOMPATOOLS"
	else
		CUPROEXTDIR="$CUSTPROTEXTDIR"
	fi

	CPURL="${1//\"/}"
	CPURLFILE="${CPURL##*/}"
	DSTDL="$CUSTPROTDLDIR/$CPURLFILE"
	if [ ! -f "$DSTDL" ]; then
		notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$CPURL")" "S"
		DLCHK="X"
		INCHK="$NON"
		# only "-STL" and "GE" proton have a sha512sum
		if grep -q "\-GE" <<< "$CPURLFILE" || grep -q "GE-" <<< "$CPURLFILE" || grep -q "\-STL" <<< "$CPURLFILE"; then
			DLCHK="sha512sum"
			INCHK="$("$WGET" -q "${CPURL%%.tar*}.${DLCHK}" -O - 2> >(grep -v "SSL_INIT") | cut -d ' ' -f1)"
		fi
		dlCheck "$CPURL" "$DSTDL" "$DLCHK" "Downloading '$CPURL' to '$CUSTPROTDLDIR'" "$INCHK"
	else
		writelog "INFO" "${FUNCNAME[0]} - File '$DSTDL' already exists - nothing to download"
		if [ -z "$2" ]; then
			notiShow "$(strFix "$NOTY_DLCUSTOMPROTON4" "$CPURLFILE")"
		fi
	fi

	PROTNAMERAW="${CPURLFILE%%.tar*}"
	PROTNAME="${PROTNAMERAW//.zip/}"
	if [ -d "$CUPROEXTDIR/$PROTNAME" ]; then
		writelog "INFO" "${FUNCNAME[0]} - directory '$CUPROEXTDIR/$PROTNAME' already exists - nothing to extract"
	else
		# only "-STL" and "GE" proton have a sha512sum
		if grep -q "\-GE" <<< "$CPURLFILE" || grep -q "GE-" <<< "$CPURLFILE" || grep -q "\-STL" <<< "$CPURLFILE"; then
			DLCHK="sha512sum"
			writelog "INFO" "${FUNCNAME[0]} - checking if the checksum of the already downloaded file is correct '${CPURL%%.tar*}.${DLCHK}'"
			INCHK="$("$WGET" -q "${CPURL%%.tar*}.${DLCHK}" -O - 2> >(grep -v "SSL_INIT") | cut -d ' ' -f1)"
			dlCheck "$DLCHK" "$DSTDL" "C" "$INCHK"
		fi

		if grep -q "\.tar\." <<< "$DSTDL"; then
			notiShow "$(strFix "$NOTY_DLCUSTOMPROTON5" "$CPURLFILE")" "S"

			PROTRELPATH="$("$TAR" -tf "$DSTDL" 2>/dev/null | grep "proton$" 2>/dev/null )"
			if [ -n "$PROTRELPATH" ]; then
				if grep -q "^proton$" <<< "$PROTRELPATH"; then
					EXTDEST="$CUPROEXTDIR/$PROTNAME"
					PROTFULLPATH="$EXTDEST/$PROTRELPATH"
					writelog "INFO" "${FUNCNAME[0]} - Archive is a tarbomb - creating parent directory '$EXTDEST' as extract dest dir"
					mkProjDir "$EXTDEST"
				else
					writelog "INFO" "${FUNCNAME[0]} - Archive contains proton in subdirectory '$PROTRELPATH'"
					PROTFULLPATH="$CUPROEXTDIR/$PROTRELPATH"
					EXTDEST="$CUPROEXTDIR"
				fi

				if [ -f "$PROTFULLPATH" ]; then
					writelog "SKIP" "${FUNCNAME[0]} - The destination path '$PROTFULLPATH' already exists - looks like '$DSTDL' was already extracted before"
				else
					notiShow "$(strFix "$NOTY_DLCUSTOMPROTON3" "$CPURL")" "S"
					"$TAR" xf "$DSTDL" -C "$EXTDEST" 2>/dev/null
					getAvailableProtonVersions "up"
					touch "$PROTBUMPTEMP"
				fi
			else
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON6" "$DSTDL")" "S"
				writelog "SKIP" "${FUNCNAME[0]} - Archive doesn't seem to contain a 'proton' file"
			fi
		elif grep -q "\.zip$" <<< "$DSTDL"; then
			if grep -q "proton$" <<< "$("$UNZIP" -l "$DSTDL" 2>/dev/null)"; then
				writelog "INFO" "${FUNCNAME[0]} - Archive contains proton"
				SUBDIR="$(basename "${DSTDL//.zip/}")"
				PROTRELPATH="$SUBDIR/proton"
				PROTFULLPATH="$CUPROEXTDIR/$PROTRELPATH"
				if [ -f "$PROTFULLPATH" ]; then
					writelog "SKIP" "${FUNCNAME[0]} - The destination path '$PROTFULLPATH' already exists - looks like '$DSTDL' was already extracted before"
				else
					notiShow "$(strFix "$NOTY_DLCUSTOMPROTON3" "$CPURL")" "S"
					"$UNZIP" "$DSTDL" -d "$CUPROEXTDIR/$SUBDIR" 2>/dev/null
					getAvailableProtonVersions "up"
					touch "$PROTBUMPTEMP"
					notiShow "$GUI_DONE" "S"
				fi
			else
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON6" "$CPURL")" "S"
				writelog "SKIP" "${FUNCNAME[0]} - Archive doesn't seem to contain a 'proton' file"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Don't know how to extract "
		fi
	fi

	rm "$PROTONCSV" 2>/dev/null

	if [ -f "$PROTFULLPATH" ]; then
		addCustomProtonToList "$PROTFULLPATH"
	fi
}

function createDLProtList {
	if [ ! -x "$(command -v "$JQ")" ]; then
		writelog "WARN" "${FUNCNAME[0]} - 'jq' is not installed - Can't generate list of online Proton versions"
	else
		writelog "INFO" "${FUNCNAME[0]} - Generating list of online available custom Proton builds"

		PROTDLLIST="$STLSHM/ProtonDL.txt"
		MAXAGE=360

		if [ ! -f "$PROTDLLIST" ] || test "$(find "$PROTDLLIST" -mmin +"$MAXAGE")"; then
			rm "$PROTDLLIST" 2>/dev/null
			while read -r CPURL; do
				if grep -q "$GHURL" <<< "${!CPURL}"; then
					SRCURL="${!CPURL}"
					SRCURL="${SRCURL//\/releases}"
					SRCURL="${SRCURL//$GHURL/$AGHURL\/repos}"
					SRCURL="${SRCURL}/releases"

					"$WGET" -q "$SRCURL" -O - | "$JQ" -r '.[].assets[].browser_download_url' | grep "tar.gz\|tar.xz" | grep -v "Yad\|7.x" >> "$PROTDLLIST"
				fi
			done <<< "$(grep "^CP_" "$STLURLCFG" | cut -d '=' -f1)"
		fi
	fi

	delEmptyFile "$PROTDLLIST"

	if [ ! -f "$PROTDLLIST" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - Could not generating list of online available custom Proton builds - Probably $GHURL changed something"
	else
		unset ProtonDLList
		unset ProtonDLDispList

		while read -r CPVERS; do
			mapfile -t -O "${#ProtonDLList[@]}" ProtonDLList <<< "$CPVERS"
			mapfile -t -O "${#ProtonDLDispList[@]}" ProtonDLDispList <<< "${CPVERS##*/}"
		done < "$PROTDLLIST"
	fi
}

function dlCustomProtonGUI {
	createDLProtList

	writelog "INFO" "${FUNCNAME[0]} - Opening dialog to choose a download"

	DLPROTLIST="$(printf "!%s\n" "${ProtonDLDispList[@]//\"/}" | tr -d '\n' | sed "s:^!::" | sed "s:!$::")"
	export CURWIKI="$PPW/Download-Custom-Proton"
	TITLE="${PROGNAME}-DownloadCustomProton"
	pollWinRes "$TITLE"

	if [ -z "$DLPROTON" ]; then
		DLPROTON="${ProtonDLDispList[0]}"
	fi

	DLDISPCUSTPROT="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
	--title="$TITLE" \
	--text="$(spanFont "$GUI_DLCUSTPROTTEXT" "H")" \
	--field=" ":LBL " " --separator="" \
	--field="$GUI_DLCUSTPROTTEXT2!$GUI_DLCUSTPROTTEXT":CBE "$(cleanDropDown "${DLPROTON/#-/ -}" "$DLPROTLIST")" \
	"$GEOM"
	)"

	if [ -n "${DLDISPCUSTPROT}" ]; then
		if grep -q "^http" <<< "${DLDISPCUSTPROT}"; then
			writelog "INFO" "${FUNCNAME[0]} - The URL '$DLDISPCUSTPROT' was entered manually - downloading directly"
			StatusWindow "$GUI_DLCUSTPROT" "dlCustomProton ${DLDISPCUSTPROT}" "DownloadCustomProtonStatus"
		else
			DLURL="$(printf "%s\n" "${ProtonDLList[@]}" | grep -m1 "${DLDISPCUSTPROT}")"
			writelog "INFO" "${FUNCNAME[0]} - '${DLDISPCUSTPROT}' was selected - downloading '$DLURL'"
			StatusWindow "$GUI_DLCUSTPROT" "dlCustomProton ${DLURL}" "DownloadCustomProtonStatus"
		fi

		createProtonList
	fi
}

function openSteamGridDir {
	writelog "INFO" "${FUNCNAME[0]} - Opening Steam Grid directory"
	"$XDGO" "$STUIDPATH/config/grid"
}

# Set artwork for Steam game by copying/linking/moving passed artwork to steam grid folder
function setGameArt {
	function applyGameArt {
		GAMEARTAPPID="$1"
		GAMEARTSOURCE="$2"  # e.g. /home/gaben/GamesArt/cs2_hero.png
		GAMEARTSUFFIX="$3"  # e.g. "_hero" etc
		GAMEARTCMD="$4"

		SGGRIDDIR="$STUIDPATH/config/grid"
		GAMEARTBASE="$( basename "$GAMEARTSOURCE" )"
		GAMEARTDEST="${SGGRIDDIR}/${GAMEARTAPPID}${GAMEARTSUFFIX}.${GAMEARTBASE#*.}"  # path to filename in grid e.g. turns "/home/gaben/GamesArt/cs2_hero.png" into "~/.local/share/Steam/userdata/1234567/config/grid/4440654_hero.png"

		if [ -n "$GAMEARTSOURCE" ]; then
			if [ -f "$GAMEARTDEST" ]; then
				writelog "WARN" "${FUNCNAME[0]} - Existing art already exists at '$GAMEARTDEST' - Removing file..."
				rm "$GAMEARTDEST"
			fi

			if [ -f "$GAMEARTSOURCE" ]; then
				$GAMEARTCMD "$GAMEARTSOURCE" "$GAMEARTDEST"
				writelog "INFO" "${FUNCNAME[0]} - Successfully set game art for '$GAMEARTSOURCE' at '$GAMEARTDEST'"
			else
				writelog "WARN" "${FUNCNAME[0]} - Given game art '$GAMEARTSOURCE' does not exist, skipping..."
			fi
		fi
	}

	GAME_APPID="$1"  # We don't validate AppID as it would drastically slow down the process for large libraries

	SETARTCMD="cp"  # Default command will copy art
	for i in "$@"; do
		case $i in
			-hr=*|--hero=*)
				SGHERO="${i#*=}"  # <appid>_hero.png -- Banner used on game screen, logo goes on top of this
				shift ;;
			-lg=*|--logo=*)
				SGLOGO="${i#*=}"  # <appid>_logo.png -- Logo used e.g. on game screen
				shift ;;
			-ba=*|--boxart=*)
				SGBOXART="${i#*=}"  # <appid>p.png -- Used in library
				shift ;;
			-tf=*|--tenfoot=*)
				SGTENFOOT="${i#*=}"  # <appid>.png -- Used as small boxart for e.g. most recently played banner
				shift ;;
			--copy)
				SETARTCMD="cp"  # Copy file to grid folder -- Default
				shift ;;
			--link)
				SETARTCMD="ln -s"  # Symlink file to grid folder
				shift ;;
			--move)
				SETARTCMD="mv"  # Move file to grid folder
				shift ;;
		esac
	done

	applyGameArt "$GAME_APPID" "$SGHERO" "_hero" "$SETARTCMD"
	applyGameArt "$GAME_APPID" "$SGLOGO" "_logo" "$SETARTCMD"
	applyGameArt "$GAME_APPID" "$SGBOXART" "p" "$SETARTCMD"
	applyGameArt "$GAME_APPID" "$SGTENFOOT" "" "$SETARTCMD"

	writelog "INFO" "${FUNCNAME[0]} - Finished setting game art for '$GAME_APPID'. Restart Steam for the changes to take effect."
	echo "Finished setting game art for '$GAME_APPID'. Restart Steam for the changes to take effect."
}

# Shows the Yad GUI for selecting artwork to pass to setGameArt
function setGameArtGui {
	writelog "INFO" "${FUNCNAME[0]} - Starting the GUI for setting game artwork"

	if [ -z "$SUSDA" ] || [ -z "$STUIDPATH" ]; then
		setSteamPaths
	fi

	export CURWIKI="$PPW/Custom-Game-Artwork"
	TITLE="${PROGNAME}-$SGA"
	pollWinRes "$TITLE"

	SGAGAMENAME="Unknown Game"
	if [ -n "$1" ]; then
		SGAGAMENAME="$( getTitleFromID "$1" "1" )"
		if [ -z "$AID" ] || [ "$AID" -eq "$PLACEHOLDERAID" ]; then
			AID="$1"
		fi
	fi

	setShowPic

	SGASETACTIONS="copy!link!move"
	SGASET="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
	--title="$TITLE" --separator="|" --image="$SHOWPIC" \
	--text="$(spanFont "$( strFix "$GUI_SGATITLE" "$SGAGAMENAME" "$1" )" "H")\n$GUI_SGATEXT" \
	--field=" ":LBL " " \
	--field="$GUI_SGAHERO!$DESC_SGAHERO ('SGAHERO')":FL "${SGAHERO/#-/ -}" \
	--field="$GUI_SGALOGO!$DESC_SGALOGO ('SGALOGO')":FL "${SGALOGO/#-/ -}" \
	--field="$GUI_SGABOXART!$DESC_SGABOXART ('SGABOXART')":FL "${SGABOXART/#-/ -}" \
	--field="$GUI_SGATENFOOT!$DESC_SGATENFOOT ('SGATENFOOT')":FL "${SGATENFOOT/#-/ -}" \
	--field="$GUI_SGASETACTION!$DESC_SGASETACTION ('SGASETACTION')":CB "$( cleanDropDown "copy" "$SGASETACTIONS" )" \
	--button="$BUT_CAN":0 --button="$BUT_DONE":2 "$GEOM" )"

	case $? in
		0)  writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_CAN'" ;;
		2)  writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_DONE'"
			mapfile -d "|" -t -O "${#SGASETARR[@]}" SGASETARR < <(printf '%s' "$SGASET")
			SGAHERO="${SGASETARR[1]}"
			SGALOGO="${SGASETARR[2]}"
			SGABOXART="${SGASETARR[3]}"
			SGATENFOOT="${SGASETARR[4]}"
			SGACOPYMETHOD="${SGASETARR[5]}"

			setGameArt "$1" --hero="$SGAHERO" --logo="$SGALOGO" --boxart="$SGABOXART" --tenfoot="$SGATENFOOT" "--${SGACOPYMETHOD}"
	esac
}

function CustomFallbackPicture {
	if [ "$USECUSTOMFALLBACKPIC" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using custom fallback picture if possible and required"

		if [ ! -f "$CUSTOMFALLBACKPIC" ]; then
			if [ -n "$GITHUBUSER" ] && [ "$GITHUBUSER" != "$NON" ]; then
				dlCheck "$GHURL/${GITHUBUSER}.png" "$CUSTOMFALLBACKPIC" "X" "Downloading github avatar for user '$GITHUBUSER' to use as custom fallback picture"
			fi
		fi

		if [ -f "$CUSTOMFALLBACKPIC" ]; then
			SHOWPIC="$CUSTOMFALLBACKPIC"
			writelog "INFO" "${FUNCNAME[0]} - Using '$SHOWPIC' as custom fallback picture"
		else
			writelog "INFO" "${FUNCNAME[0]} - No custom fallback picture found - using internal picture instead"
		fi
	fi
}

# This could use a bit of a rewrite
function setShowPic {
	GRIDBANNER="$( find "$STUIDPATH/config/grid" -iname "${AID}_hero.*" -type f -exec realpath {} \; | head -n1  )"

	if [ "$STLPLAY" -eq 1 ] && [ -f "$STLGPNG/${AID}.png" ]; then
		SHOWPIC="$STLGPNG/${AID}.png"
	elif [ "$USEGAMEPICS" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Determining game picture"
		if [ -s "$STLGHEADD/$AID.jpg" ]; then
			SHOWPIC="$STLGHEADD/$AID.jpg"
			writelog "INFO" "${FUNCNAME[0]} - Using '$SHOWPIC' as game picture"
		else
			SHOWPIC="$STLICON"
			CustomFallbackPicture

			if [ ! -f "$STLGHEADD/$AID.jpg" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Using '$SHOWPIC' as fallback picture, because '$STLGHEADD/$AID.jpg' doesn't exist"
			elif [ ! -s "$STLGHEADD/$AID.jpg" ]; then
				# If we have a 0bytes file and we get here, try replace it with GRIDBANNER
				if [ -z "$SUSDA" ] || [ -z "$STUIDPATH" ]; then
					setSteamPaths
				fi
				# CUSTPIC replaced with GRIDBANNER, because CUSTPIC expects PNG
				CUSTPIC="$STUIDPATH/config/grid/${AID}_hero.png"
				if [ -f "$GRIDBANNER" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Found custom game picture under '$GRIDBANNER'"
					if [ -x "$(command -v "$CONVERT" 2>/dev/null)" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Replacing the empty '$STLGHEADD/$AID.jpg' file with a scaled down version and using that"
						"$CONVERT" "$GRIDBANNER" -resize "460x215!" "$STLGHEADD/$AID.jpg"
						SHOWPIC="$STLGHEADD/$AID.jpg"
					else
						writelog "SKIP" "${FUNCNAME[0]} - Command '$CONVERT' not found, so scaling the custom game picture is not possible - skipping"
					fi
				else
					writelog "INFO" "${FUNCNAME[0]} - Using '$SHOWPIC' as fallback picture, because '$STLGHEADD/$AID.jpg' has zero bytes"
					writelog "INFO" "${FUNCNAME[0]} - Leaving '$STLGHEADD/$AID.jpg' as is to avoid another download attempt"
					writelog "INFO" "${FUNCNAME[0]} - Feel free to replace it with a custom picture"
				fi
			elif [[ ( ! -f "$STLGHEADD/$AID.jpg" || ! -s "$STLGHEADD/$AID.jpg" ) && -n "$GRIDBANNER" ]]; then
				# If banner image still doesn't exist, use grid folder one (i.e. non-steam games)
				# Find custom image in grid folder and store resized image at '$STLGHEADD/<org_image_name_with_extension>'
				writelog "INFO" "${FUNCNAME[0]} - Using hero image found in Steam Grid folder - '$GRIDBANNER'"
				CUSTPIC="$GRIDBANNER"
				"$CONVERT" "$CUSTPIC" -resize "460x215!" "$STLGHEADD/${AID}.jpg"
				SHOWPIC="$STLGHEADD/${AID}.jpg"
			fi
		fi
	else
		SHOWPIC="$NOICON"
		writelog "INFO" "${FUNCNAME[0]} - Using '$SHOWPIC' as invisible picture"
	fi
}

function addCustomProtonToList {
	if [ -f "$1" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Received directly the file as argument"
		if [ -n "$2" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Received '$2' as Proton Version"
			OUTNEWCUSTPROT="$2;$1"
		else
			OUTNEWCUSTPROT="$1"
		fi
	else
		NEWCUSTPROT="$1"

		if grep -q "||" <<< "$NEWCUSTPROT"; then
			if grep -q "|http" <<< "$NEWCUSTPROT"; then
				CPURLWIP="$(tr '|' '"' <<< "$NEWCUSTPROT" | sed "s:\"http:\";\"http:g")"
				CPURL="$(cut -d ';' -f2 <<< "$CPURLWIP")"
				StatusWindow "$GUI_DLCUSTPROT" "dlCustomProton ${CPURL}" "DownloadCustomProtonStatus"
			else
				CPWIP="$(tr -s '|' <<< "$NEWCUSTPROT" | tr '|' '"')"
				if [ -f "${CPWIP//\"/}" ]; then
					OUTNEWCUSTPROT="$CPWIP"
				fi
			fi
		else
			if grep -q "|http" <<< "$NEWCUSTPROT"; then
				CPURLWIP="$(tr '|' '"' <<< "$NEWCUSTPROT" | sed "s:\"http:\";\"http:g")"
				CPURL="$(cut -d ';' -f2 <<< "$CPURLWIP")"
				StatusWindow "$GUI_DLCUSTPROT" "dlCustomProton ${CPURL}" "DownloadCustomProtonStatus"
			else
				CPWIP="$(tr '|' '"' <<< "$NEWCUSTPROT" | sed "s:\"/:\";\"/:g")"
				CPWIPF="$(cut -d ';' -f2 <<< "$CPWIP")"
				if [ -f "${CPWIPF//\"/}" ]; then
					OUTNEWCUSTPROT="$CPWIP"
				fi
			fi
		fi
	fi

	if [ -n "$OUTNEWCUSTPROT" ] ; then
		writelog "INFO" "${FUNCNAME[0]} - Adding '$OUTNEWCUSTPROT' to '$CUSTOMPROTONLIST'"
		echo "$OUTNEWCUSTPROT" >> "$CUSTOMPROTONLIST"
		writelog "INFO" "${FUNCNAME[0]} - (Re-)creating the internal List of available Proton-Versions"
		getAvailableProtonVersions "up" X
	fi
}

function dlLatestGE {
	createDLProtList

	if [ -n "${ProtonDLList[0]}" ]; then
		if [ "$1" == "latestge" ] || [ "$1" == "lge" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Downloading latest Proton GE"
		else
			writelog "INFO" "${FUNCNAME[0]} - Downloading latest custom Proton ${ProtonDLDispList[0]//\"/}"
		fi
		StatusWindow "$GUI_DLCUSTPROT" "dlCustomProton ${ProtonDLList[0]//\"/} $2" "DownloadCustomProtonStatus"
	else
		writelog "ERROR" "${FUNCNAME[0]} - Could not create list of downloadable Proton-Versions"
	fi
}

function dlCustomProtonGate {
	if [ -z "$1" ]; then
		dlCustomProtonGUI
	else
		if grep -q "^http" <<< "$1"; then
			writelog "INFO" "${FUNCNAME[0]} - '$1' is an URL - sending directly to dlCustomProton"
			StatusWindow "$GUI_DLCUSTPROT" "dlCustomProton $1" "DownloadCustomProtonStatus"
		else
			if [ "$1" == "latest" ] || [ "$1" == "l" ] || [ "$1" == "latestge" ] || [ "$1" == "lge" ]; then
				dlLatestGE "$1"
			elif [ "$1" == "latesttkg" ] || [ "$1" == "ltkg" ]; then
				createDLProtList
				writelog "INFO" "${FUNCNAME[0]} - Downloading latest Proton TKG"
				StatusWindow "$GUI_DLCUSTPROT" "dlCustomProton $(printf "%s\n" "${ProtonDLList[@]}" | grep -im1 "tkg")" "DownloadCustomProtonStatus"
			else
				writelog "SKIP" "${FUNCNAME[0]} - Don't know what to do with argument '$1'"
			fi
		fi
	fi
}

function addCustomProton {
	if [ -z "$1" ]; then
		if [ ! -f "$CUSTOMPROTONLIST" ]; then
			{
			echo "# List of custom proton paths, which are not stored in the usual default locations (see README)"
			echo "# (optionally with identifying name (f.e. proton version) as field one)"
			echo "# Files can be added by using the '$PROGCMD' command line or the GUI (or manually of course)"
			echo "# Two valid examples:"
			echo "# \"Proton-47.11-FWX-3\";\"/random/path/to/a/custom/binary/proton\""
			echo "# \"/random/path/to/a/custom/binary/proton\""
			echo "# (In the first example \"Proton-47.11-FWX-3\" will be used as identifying proton version"
			echo "# in the second example the identifying proton version will be searched in '$CTVDF'"
			echo "# and 'version' files in the besides the given proton binary."
			echo "# When no proton version could be found, the selected file will be marked as invalid and removed at once)"
			echo "################################################"
			} > "$CUSTOMPROTONLIST"
		fi
		export CURWIKI="$PPW/Custom-Proton-Autoupdate"
		TITLE="${PROGNAME}-AddCustomProton"
		pollWinRes "$TITLE"

		NEWCUSTPROT="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
		--title="$TITLE" \
		--text="$(spanFont "$GUI_ADDCUSTOMBINARY" "H")" \
		--field=" ":LBL " " \
		--field="$GUI_PROTONVERSIONNAME!$DESC_PROTONVERSIONNAME" "Proton-" \
		--field="$GUI_CUSTOMPROTONBINARY":FL "proton" --file-filter="$GUI_PROTONFILES (proton)| proton" \
		"$GEOM"
		)"

		addCustomProtonToList "$NEWCUSTPROT"
	else
		if grep -q "proton$" <<< "$1"; then
			if [ -f "$1" ]; then
				writelog "INFO" "${FUNCNAME[0]} - '$1' is a path to an existing 'proton' file - adding to the Custom Proton List"
				addCustomProtonToList "$@"
			else
			writelog "SKIP" "${FUNCNAME[0]} - File '$1' does not exist - skipping"
			fi
		fi
	fi
}

function saveMenuEntries {
	SAVMENUCAT="$1"
	writelog "INFO" "${FUNCNAME[0]} - Saving changed Settings"
	writelog "INFO" "${FUNCNAME[0]} - Clearing Results array with '${#Results[@]}' elements"
	writelog "INFO" "${FUNCNAME[0]} - Pulling values from tempfile '$MKCFG'"
	unset Results
	mapfile -t -O "${#Results[@]}" Results < "$MKCFG"

	for i in "${!Results[@]}"; do
		ONUM="$((i +1))"
		VAL="${Results[$i]}"

		RAWENT="$(sed "${ONUM}q;d" "${SAVMENUCAT}-${TMPL}")"
		VAR="$(grep -oP "\('\K[^\')]+" <<< "$RAWENT")"

		if [ -n "$VAL" ]; then
			if grep -q "MENU_GAME" <<< "$RAWENT"; then
				if [ "$SAVMENUCAT" == "$GAMETEMPMENU" ]; then
					SAVECFG="$STLDEFGAMECFG"
				else
					SAVECFG="$STLGAMECFG"
				fi
			elif grep -q "MENU_URL" <<< "$RAWENT"; then
				SAVECFG="$STLURLCFG"
			elif grep -q "MENU_GLOBAL" <<< "$RAWENT"; then
				SAVECFG="$STLDEFGLOBALCFG"
			else
				writelog "SKIP" "${FUNCNAME[0]} - No valid configfile determined for VAR='$VAR' WRITEVAL='$WRITEVAL' - this is an error!"
			fi
			VALUNQ="${VAL//\'/}"
			WRITEVAL="${VALUNQ/# -/-}"

			if [ -n "$VAR" ] && [ -z "$WRITEVAL" ] && [ -n "$SAVECFG" ]; then
				WRITEVAL="$NON"
				writelog "INFO" "${FUNCNAME[0]} - Value for '$VAR' is empty - automatically setting '$NON'"
			fi

			if [ -n "$VAR" ] && [ -n "$WRITEVAL" ] && [ -n "$SAVECFG" ]; then
				updateConfigEntry "$VAR" "$WRITEVAL" "$SAVECFG"
			fi
		fi
	done
	writelog "INFO" "${FUNCNAME[0]} - Done with Saving changed Settings"
}

function saveNewRes {
	if [ "$SAVESETSIZE" -eq 1 ] && [ -n "$3" ]; then
		SNEWW="$1"
		SNEWH="$2"
		CFG="$3"
		touch "$CFG"
		updateConfigEntry "WINX" "$SNEWW" "$CFG"
		updateConfigEntry "WINY" "$SNEWH" "$CFG"
		writelog "INFO" "${FUNCNAME[0]} - Saved new resolution '${SNEWW}x${SNEWH}' in '$CFG'" "$WINRESLOG"
	fi
}

function updateThisWinTemplate {
	if [ -f "$UPWINTMPL" ] && [ -f "$2" ]; then
		cp "$2" "$STLGUIDIR/$SCREENRES/${TEMPL}/${1}.conf"
		rm "$UPWINTMPL"
		writelog "INFO" "${FUNCNAME[0]} - Updated Window Template '$STLGUIDIR/$SCREENRES/${TEMPL}/${1}.conf' with '$2'" "$WINRESLOG"
		notiShow "$(strFix "$NOTY_TEMPLUP" "$1")"
	fi
}

function updateWinRes {
	if [ -z "$SAVESETSIZE" ] || [ "$ONSTEAMDECK" -eq 1 ]; then
		SAVESETSIZE=0
	fi

	if [ "$SAVESETSIZE" -eq 1 ]; then
		WNAM="$1"
		CFG="$2"
		TEMPLGUICFG="$3"

		writelog "INFO" "${FUNCNAME[0]} - Starting resolution-poll for '$GAMEGUICFG' with incoming '${WINX}x${WINY}'" "$WINRESLOG"

		ORGW="${WINX}"
		ORGH="${WINY}"
		NEWW="${WINX}"
		NEWH="${WINY}"

		MAXWAIT=3
		COUNTER=0

		while ! "$XWININFO" -name "$WNAM" -stats >/dev/null 2>/dev/null; do
			if [ -f "$CLOSETMP" ]; then
				writelog "WAIT" "${FUNCNAME[0]} - ${PROGNAME,,} is just closing - leaving loop" "$WINRESLOG"
				break
			fi
			if [[ "$COUNTER" -ge "$MAXWAIT" ]]; then
				writelog "SKIP" "${FUNCNAME[0]} - Timeout waiting for Window '$WNAM'" "$WINRESLOG"
				return
			fi

			writelog "INFO" "${FUNCNAME[0]} - Waiting for Window '$WNAM'" "$WINRESLOG"
			COUNTER=$((COUNTER+1))
			sleep 1
		done

		writelog "INFO" "${FUNCNAME[0]} - Window '$WNAM' is running - polling the resolution" "$WINRESLOG"

		while true; do
			SRES="$("$XWININFO" -name "$WNAM" -stats 2>/dev/null | awk '$1=="-geometry" {print $2}' | cut -d '+' -f1)"
			if grep -q "x" <<< "$SRES"; then
				PREVW="$NEWW"
				PREVH="$NEWH"

				TNEWW1="${SRES%x*}"
				TNEWW="${TNEWW1%%-*}"

				TNEWH1="${SRES#*x}"
				TNEWH="${TNEWH1%%-*}"

				if [ "$TNEWW" -ne "$ORGW" ] || [ "$TNEWH" -ne "$ORGH" ]; then
					if [ "$TNEWW" != "$PREVW" ] || [ "$TNEWH" != "$PREVH" ]; then
						if [ -n "${TNEWW##*[!0-9]*}" ] && [ -n "${TNEWH##*[!0-9]*}" ]; then
							NEWW="$TNEWW"
							NEWH="$TNEWH"
							writelog "INFO" "${FUNCNAME[0]} - Found new Window Resolution '${NEWW}x${NEWH}' for Window '$WNAM'" "$WINRESLOG"
						else
							writelog "SKIP" "${FUNCNAME[0]} - Skipping found false-positive size '${NEWW}x${NEWH}'"	"$WINRESLOG"
						fi
					fi
				fi
				sleep 1
			else
				if [ "$NEWW" -ne "$ORGW" ] || [ "$NEWH" -ne "$ORGH" ]; then
					writelog "INFO" "${FUNCNAME[0]} - The Window '$WNAM' was closed - saving the last seen resolution '${NEWW}x${NEWH}' into config '$CFG'" "$WINRESLOG"
					saveNewRes "$NEWW" "$NEWH" "$CFG"
					if [ ! -f "$TEMPLGUICFG" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Creating template '$TEMPLGUICFG' with the same resolution '${NEWW}x${NEWH}'" "$WINRESLOG"
						cp "$CFG" "$TEMPLGUICFG"
					fi
				else
					writelog "INFO" "${FUNCNAME[0]} - The Window '$WNAM' was closed - the resolution didn't change - nothing to do" "$WINRESLOG"
				fi

				updateThisWinTemplate "$TITLE" "$CFG"

				return
			fi
		done
	fi
}

function updateEditor {
	CFG="$1"

	loadCfg "$CFG" X
	if grep -q "$XDGO" <<< "$STLEDITOR" || [ ! -f "$STLEDITOR" ] ; then
		writelog "WARN" "${FUNCNAME[0]} - '$XDGO' selected as editor or configured editor not found - trying to find an installed editor installed"
		if [ -x "$(command -v "$XDGMIME" 2>/dev/null)" ]; then
			XDGED="$(command -v "$("$XDGMIME" query default text/plain | cut -d '.' -f1)" 2>/dev/null)"
			if [ -x "$XDGED" ]; then
				writelog "INFO" "${FUNCNAME[0]} - $XDGMIME points to '$XDGED', which also exists"
				FOUNDEDITOR="$XDGED"
			fi
		fi

		if [ -z "$FOUNDEDITOR" ]; then
			if [ -x "$(command -v "geany")" ]; then
				FOUNDEDITOR="$(command -v "geany")"
			elif [ -x "$(command -v "gedit")" ]; then
				FOUNDEDITOR="$(command -v "gedit")"
			elif [ -x "$(command -v "leafpad")" ]; then
				FOUNDEDITOR="$(command -v "leafpad")"
			elif [ -x "$(command -v "kwrite")" ]; then
				FOUNDEDITOR="$(command -v "kwrite")"
			fi
		fi

		if [ -n "$FOUNDEDITOR" ]; then
			writelog "INFO" "${FUNCNAME[0]} - changing STLEDITOR to '$FOUNDEDITOR' in '$CFG'"
			updateConfigEntry "STLEDITOR" "$FOUNDEDITOR" "$CFG"
			loadCfg "$CFG"
		else
			writelog "INFO" "${FUNCNAME[0]} - No valid editor found - will fall back to '$XDGO'."
		fi
	fi
}

function setGPfxFromAppMa {
	if [ -n "$2" ] && [ -f "$2" ]; then
		echo "${2%/*}/$CODA/$1/pfx"
	else
		if [ -z "$GPFX" ]; then
			APPMAFE="$(listAppManifests | grep -m1 "${1}.acf")"
			if [ -n "$APPMAFE" ]; then
				if [ ! -d "$APPMAFE" ]; then
					writelog "ERROR" "${FUNCNAME[0]} - '$APPMAFE' is no valid directory - probably function 'listAppManifests' requires a fix here!"
				else
					AMF="${APPMAFE##*/}"
					GPFX="${APPMAFE%/*}/$CODA/$1/pfx"
					writelog "INFO" "${FUNCNAME[0]} - Found WINEPREFIX '$GPFX' in '$AMF'"
				fi
			fi
		fi
	fi
	if [ -z "$GPFX" ] && [ -n "$AMF" ] && [ -z "$2" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Could not retrieve WINEPREFIX from '$AMF'"
	fi
}

function getGameTextFiles {
	setGPfxFromAppMa "$AID"
	unset GameTxtFiles

	if [ -d "$GPFX/$DRCU/$STUS" ]; then
		# an option to parse user-contributed per game config lists with the actual config files instead of searching for any textfile would be easy
		EXID="$HIDEDIR/hide-${AID}.txt"
		touch "$EXID"
		SKIPGTF=0

		if [ -n "$1" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Searching for at least one editable textfile in '$GPFX/$DRCU/$STUS'"
			find "$GPFX/$DRCU/$STUS" -type f -exec grep -Iq . {} \; -print -quit >/dev/null
		else
			while read -r gtxtfile; do
				while read -r skip; do
					if grep -q "$skip" <<< "$gtxtfile"; then
						SKIPGTF=1
					fi
				done < "$EXID"

				if [ "$SKIPGTF" -eq 0 ]; then
					GameTxtFiles+=("$gtxtfile")
				else
					SKIPGTF=0
				fi
			done <<< "$(find "$GPFX/$DRCU/$STUS" -type f -exec grep -Iq . {} \; -print)"
		fi
	fi
}

function getAvailableCfgs {
	unset CfgFiles
	while read -r cfgfile; do
		if [ -f "${!cfgfile}" ]; then
			CfgFiles+=("${!cfgfile}")
		fi
	done <<< "$(sed -n "/^#STARTEDITORCFGLIST/,/^#ENDEDITORCFGLIST/p;/^#ENDEDITORCFGLIST/q" "$0" | grep -v "^#" | grep -v "LOGFILE" | grep "=" | cut -d '=' -f1)"

	getGameTextFiles "X"
}

function confirmReq {
	QUESTION="$2"
	TITLE="${PROGNAME}-$1"
	pollWinRes "$TITLE"
	setShowPic

	"$YAD" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center --on-top "$WINDECO" --title="$TITLE" --text="$(spanFont "$QUESTION" "H")" "$GEOM"
	echo "$?"
}

function EditorDialog {
	writelog "INFO" "${FUNCNAME[0]} - Opening Editor Dialog"

	resetAID "$1"
	loadCfg "$STLGAMECFG" X
	getAvailableCfgs

	if [ "$BACKUPSTEAMUSER" -eq 1 ]; then
		EXID="$BACKEX/exclude-${AID}.txt"
		if [ -f "$EXID" ]; then
			CfgFiles+=("$EXID")
		fi
	fi

	HIDEID="$HIDEDIR/hide-${AID}.txt"
	if [ -f "$HIDEID" ]; then
		CfgFiles+=("$HIDEID")
	fi

	if [ -f "$MAHUTMPL" ]; then
		CfgFiles+=("$MAHUTMPL")
	fi

	MHIC="$MAHUCID/${AID}.conf"
	if [ -f "$MHIC" ]; then
		CfgFiles+=("$MHIC")
	fi

	getGameTextFiles
	CfgFiles+=("${GameTxtFiles[@]}")

	if [ -f "$LOGFILE" ]; then
		CfgFiles+=("$LOGFILE")
	fi

	REVALSC="$EVMETAID/${EVALSC}_${AID}.vdf"
	CEVALSC="$EVMETACUSTOMID/${EVALSC}_${AID}.vdf"
	AEVALSC="$EVMETAADDONID/${EVALSC}_${AID}.vdf"

	if [ -f "$REVALSC" ]; then
		CfgFiles+=("$REVALSC")
	fi

	if [ -f "$CEVALSC" ]; then
		CfgFiles+=("$CEVALSC")
	fi

	if [ "$UUUSEIGCS" -eq 1 ] || [ "$USEIGCS" -eq 1 ]; then
		IGCSINI="$EFD/${IGCS}.ini"
		if	 [ -f "$IGCSINI" ]; then
			CfgFiles+=("$IGCSINI")
		fi
	fi

	if [ "$UUUSEPATCH" -eq 1 ] || [ "$UUUSEVR" -eq 1 ]; then
		UUUPATCHFILE="$(find "$MEGAMEDIR" -name "$UUUPATCH")"
		if	[ -f "$UUUPATCHFILE" ]; then
			CfgFiles+=("$UUUPATCHFILE")
		fi
	fi

	if [ "$USERESHADE" -eq 1 ] || [ "$USESPECIALK" -eq 1 ]; then
		setFullGameExePath "FGEP"
		while read -r inifile; do
			if [ -f "$inifile" ]; then
				CfgFiles+=("$inifile")
			fi
		done <<< "$(find "$FGEP" -mindepth 1 -maxdepth 1 -type f -name "*.ini")"
	fi

	if [ "$USEOPENVRFSR" -eq 1 ]; then
		OVRFP="$EFD/${OVFS}-${SHOSTL}-enabled.txt"
		if [ -f "$OVRFP" ]; then
			OVRMODF="$EFD/$(grep "$OVRMOD" "$OVRFP")"
			if [ -f "$OVRMODF" ]; then
				CfgFiles+=("$OVRMODF")
			fi
		fi
	fi

	if [ -f "$AEVALSC" ]; then
		CfgFiles+=("$AEVALSC")
	fi

	if [ -f "$STLPROTONIDLOGDIR/steam-${AID}.log" ]; then
		CfgFiles+=("$STLPROTONIDLOGDIR/steam-${AID}.log")
	fi

	if [ -f "$STLDXVKLOGDIR/${GE}_d3d11.log" ]; then
		CfgFiles+=("$STLDXVKLOGDIR/${GE}_d3d11.log")
	fi

	if [ -f "$STLDXVKLOGDIR/${GE}_dxgi.log" ]; then
		CfgFiles+=("$STLDXVKLOGDIR/${GE}_dxgi.log")
	fi

	writelog "INFO" "${FUNCNAME[0]} - Found ${#CfgFiles[@]} available Config Files - opening Checklist"
	export CURWIKI="$PPW/Editor-Menu"
	TITLE="${PROGNAME}-Editor"
	pollWinRes "$TITLE"

	setShowPic

	EDFILES="$(while read -r f; do	echo "FALSE"; echo "$f"; done <<< "$(printf "%s\n" "${CfgFiles[@]}" | sort -u)" | \
	"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center "$WINDECO" --list --checklist --column=Edit --column=ConfigFile --separator="\n" --print-column="2" \
	--text="$(spanFont "$(strFix "$GUI_EDITORDIALOG" "$SGNAID")" "H")" --title="$TITLE" --button="$BUT_EDIT":0 --button="$BUT_HIDE":2 --button="$BUT_OD":4 --button="$BUT_DEL":6 --button="$BUT_CAN":8 "$GEOM")"
	case $? in
		0)  {
				if [ -n "$EDFILES" ]; then
					if [ -n "$HELPURL" ] && [ "$HELPURL" != "$NON" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Opening the url '$HELPURL' in the browser"
						checkHelpUrl "$HELPURL"
					fi

					if [ -z "$STLEDITOR" ] || [ "$STLEDITOR" -eq "$NON" ]; then
						writelog "INFO" "${FUNCNAME[0]} - No editor found or selected. Falling back to '$XDGO'"
						writelog "WARN" "${FUNCNAME[0]} - If you find games fail to close try setting an editor manually"
						STLEDITOR=$XDGO
					fi

					writelog "INFO" "${FUNCNAME[0]} - Opening Editor '$STLEDITOR' with selected Config Files"
					mapfile -t -O "${#EdArr[@]}" EdArr <<< "$EDFILES"
					"$STLEDITOR" "${EdArr[@]}"
					unset EdArr
					# kill browser if it was opened with the editor:
					if [ -n "$KILLBROWSER" ]; then
						if [ "$KILLBROWSER" -eq 1 ]; then
							"$PKILL" -f "$BROWSER"
						fi
					fi
				fi
			}
		;;
		2)  {
		writelog "INFO" "${FUNCNAME[0]} - Selected HIDE 1"
				if [ -n "$EDFILES" ]; then
					HIDELIST="$HIDEDIR/hide-${AID}.txt"
					writelog "INFO" "${FUNCNAME[0]} - Selected HIDE - Hiding all selected files from the EditorList by adding them to the Exclude list '$HIDELIST'"
					echo "$EDFILES"	 >> "$HIDELIST"
					sort -u"$HIDELIST" -o "$HIDELIST"
					sed "/^ *$/d" -i "$HIDELIST"
				fi
			}
		;;
		4)
			if [ -x "$(command -v "$XDGO" 2>/dev/null)" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Selected Open Directory - Opening the directory containing the first selected file using '$XDGO'"
				mapfile -t -O "${#EdArr[@]}" EdArr <<< "$EDFILES"
				"$XDGO" "$(dirname "${EdArr[0]}")"
				unset EdArr
			else
				writelog "SKIP" "${FUNCNAME[0]} - '$XDGO' not found - can't open the directory"
			fi
		;;
		6)  writelog "INFO" "${FUNCNAME[0]} - Selected Delete"
			RSEL="$(confirmReq "Ask_Delete_Selected_Files" "$GUI_DELSEL")"
			if [ "$RSEL" -eq 0 ]; then
			{
				writelog "INFO" "${FUNCNAME[0]} - Confirmed deletion of all selected files"
				mapfile -t -O "${#EdArr[@]}" EdArr <<< "$EDFILES"
				rm "${EdArr[@]}" 2>/dev/null
				unset EdArr
			}
			else
				writelog "INFO" "${FUNCNAME[0]} - Selected CANCEL - Not deleting the files"
			fi
		;;
		8)  writelog "INFO" "${FUNCNAME[0]} - Selected CANCEL"
		;;
	esac

	goBackToPrevFunction "${FUNCNAME[0]}" "$2"
}

function goBackToPrevFunction {
	IAM="$1"
	PREV="$2"

	if [ "$IAM" != "$PREV" ]; then
		if [ -z "$3" ]; then
			MYGOBACK=1
		else
			MYGOBACK="$3"
		fi

		if [ -z "$PREV" ]; then
			writelog "INFO" "${FUNCNAME[0]} - '$IAM' closed"
		else
			if [ "$PREV" != "$NON" ] && [ "$MYGOBACK" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - '$IAM' closed - Going back to the '$PREV'"
				"$PREV" "$AID" "$IAM"
			else
				writelog "INFO" "${FUNCNAME[0]} - '$IAM' closed"
			fi
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - Took a wrong road somewhere - '$IAM' and '$PREV' - leaving"
	fi
}

function closeTrayIcon {
	notiShow "$(strFix "$NOTY_CLOSETRAY" "$YADTRAYPID")"
	writelog "INFO" "${FUNCNAME[0]} - Closing TrayIcon '$YADTRAYPID'"
	kill "$YADTRAYPID" 2>/dev/null
}

function cleanYadLeftOvers {
	closeTrayIcon
}

function GETALTEXEPATH {
	if [ -n "$ALTEXEPATH" ] && [ "$ALTEXEPATH" != "/tmp" ]; then
		if [ -d "$ALTEXEPATH" ]; then
			echo "$ALTEXEPATH"
		elif [ -d "$EFD/$ALTEXEPATH" ]; then
			echo "$EFD/$ALTEXEPATH"
		fi
	fi
}

#STARTIEX
function TrayIconExports {
	export XWI="$XWININFO"
	export XDO="$XDO"
	export PROGCMD="$PROGCMD"

	export -f writelog
	export AID="$AID"

	export GPFX="$GPFX"
	export KILLSWITCH="$KILLSWITCH"
	export -f killProtonGame
	export -f PauseGame
	export EFD="$EFD"
	export GAMEWINDOW="$GAMEWINDOW"
	export UPWINTMPL="$UPWINTMPL"
	export TRAYCUSC="$TRAYCUSC"

	if [ -n "$(GETALTEXEPATH)" ]; then
		SHADDESTDIR="$(GETALTEXEPATH)"
	fi

	export SHADDESTDIR="$SHADDESTDIR"

	KPFX="$GPFX"
	if [ "$USEWINE" -eq 1 ]; then
		setWineVars
		KPFX="$GWFX"
	fi

	if [ ! -f "$RUNWINE" ]; then
		setRunWineServer "${FUNCNAME[0]}"
	fi

	export RUNWINESERVER="$RUNWINESERVER"

	if [ -n "$KPFX" ]; then
		echo "WINEPREFIX=\"$KPFX\" \"$RUNWINESERVER\" -k" > "$KILLSWITCH"
		if [ "$USEMANGOAPP" -eq 1 ]; then
			echo "$PKILL -f \"$MANGOAPP\"" >> "$KILLSWITCH"
		fi
		echo "touch $CLOSETMP" >> "$KILLSWITCH"
		chmod +x "$KILLSWITCH"
	fi

	export SHADDESTDIR="$SHADDESTDIR"
	export -f TrayShaderMenu
	export -f TrayVR
	export -f TrayPickWin
	export -f TraySRC
	export -f TrayUWT
	export -f TrayLCS
	export -f TrayGameFiles
	export -f TrayMO2
	export -f TrayProtonList
	export -f TrayOpenIssue
}
#ENDIEX

function openTrayIcon {
	setShadDestDir

	if [ -z "$1" ]; then
		# loading gameconfig here, as some vars might have to be exported
		writelog "INFO" "${FUNCNAME[0]} - LoadCfg: $STLGAMECFG"
		loadCfg "$STLGAMECFG"
	fi

	if [ "$ONSTEAMDECK" -eq 1 ] && [ "$FIXGAMESCOPE" -eq 1 ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Skipping TrayIcon on SteamDeck Game Mode" "X"
	else
		if [ -z "$YADTRAYPID" ] && [ "$USETRAYICON" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Opening trayIcon:" "X"

			# variables and functions used by exported functions below

			# functions for the trayIcon Menu
			function killProtonGame {
				"$KILLSWITCH"
			}

			function PauseGame {
				PAUSEPID="$(sleep 5 && "$XDO" getactivewindow getwindowpid)"
				# idea taken with friendly permission from $GHURL/Ilazki:
				GAMESTATE="$(ps -q "$PAUSEPID" -o state --no-headers)"
				GAMESIG="-STOP"

				if [ "$GAMESTATE" = "T" ] ; then
					GAMESIG="-CONT"
				fi

				kill "$GAMESIG" "$PAUSEPID"
			}

			function TrayShaderMenu {
				"$PROGCMD" update gameshaders "$SHADDESTDIR"
			}

			function TrayVR {
				if [ -z "$GAMEWINDOW" ]; then
					GAMEWINDOW="$(sleep 2 && "$XWI" -stats | grep ^"$XWI" | tail -n1 | cut -d '"' -f2)";
				fi

				if [ -n "$GAMEWINDOW" ]; then
					writelog "INFO" "${FUNCNAME[0]} - TrayIcon: picked '$PICKWINDOWNAME'"
					"$PROGCMD" "vr" "$PICKWINDOWNAME" "$AID" "s"
				else
					writelog "SKIP" "${FUNCNAME[0]} - TrayIcon: Didn't find a game window name to open in vr"
				fi
			}

			function TrayPickWin {
				writelog "INFO" "${FUNCNAME[0]} - TrayIcon: Executing Window Pick command for game '$GN '$AID'"
				"$PROGCMD" "pw" "$AID" "$GN"
			}

			function TraySRC {
				writelog "INFO" "${FUNCNAME[0]} - TrayIcon: Executing command '$STEAM ${STEAM}://${RECO}'"
				"$PROGCMD" "src"
			}

			function TrayUWT {
				writelog "INFO" "${FUNCNAME[0]} - TrayIcon: Triggering Window Template Update"
				touch "$UPWINTMPL"
			}

			function TrayLCS {
				writelog "INFO" "${FUNCNAME[0]} - TrayIcon: Triggering Launch custom script"
				"$TRAYCUSC"
			}

			function TrayGameFiles {
				writelog "INFO" "${FUNCNAME[0]} - TrayIcon: Executing Game Files command for game '$GN '$AID'"
				"$PROGCMD" "gf" "$AID"
			}

			function TrayMO2 {
				writelog "INFO" "${FUNCNAME[0]} - TrayIcon: Starting standalone $MO instance"
				"$PROGCMD" "mo2" "start"
			}

			function TrayProtonList {
				writelog "INFO" "${FUNCNAME[0]} - TrayIcon: Updating list of available Proton versions"
				"$PROGCMD" "proton" "list"
			}

			function TrayOpenIssue {
				writelog "INFO" "${FUNCNAME[0]} - TrayIcon: Open Issue Tracker with User's Default Browser"
				"$PROGCMD" "openissue"
			}

			TrayIconExports

			# actually open the actual trayIcon
			GDK_BACKEND=x11 "$YAD" --image="$STLICON" --notification --item-separator=","\
			--menu="$TRAY_KILLSWITCH,bash -c killProtonGame \
			|$TRAY_PAUSE,bash -c PauseGame \
			|$TRAY_SHADER,bash -c TrayShaderMenu \
			|$TRAY_VR,bash -c TrayVR \
			|$TRAY_PICKWINDOW,bash -c TrayPickWin \
			|$TRAY_SRC,bash -c TraySRC \
			|$TRAY_UWT,bash -c TrayUWT \
			|$TRAY_LCS,bash -c TrayLCS \
			|$GUI_GAFI,bash -c TrayGameFiles \
			|$TRAY_UPL,bash -c TrayProtonList \
			|$FBUT_GUISET_MO,bash -c TrayMO2 \
			|$TRAY_OPENISSUE,bash -c TrayOpenIssue" \
			--text="$TRAY_TOOLTIP" >/dev/null 2>/dev/null &
			YADTRAYPID="$!"
		fi
	fi
}

function createProtonList {
	delEmptyFile "$PROTONCSV"
	if [ ! -f "$PROTONCSV" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Looking for available Proton versions"
		getAvailableProtonVersions "up" X
	fi

	if [ "$ISGAME" -eq 2 ] || [ -n "$1" ] ; then
		PROTYADLIST="$(printf "!%s\n" "${ProtonCSV[@]//\"/}" | sort -u | cut -d ';' -f1 | tr -d '\n' | sed "s:^!::" | sed "s:!$::")"
	fi
}

function createDropdownLists {
	createProtonList
	createWineList
	createLanguageList
}

function favoritesMenuEntries {
	if [ -f "$STLFAVMENUCFG" ]; then
		unset FavSel
		mapfile -t -O "${#FavSel[@]}" FavSel < "$STLFAVMENUCFG"
	else
		FAVMENUCFG="$GLOBALMISCDIR/$FACO"

		if [ -f "$FAVMENUCFG" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using global favorites preset for preselecting some entries"
			mapfile -t -O "${#FavSel[@]}" FavSel < "$FAVMENUCFG"
		else
			writelog "INFO" "${FUNCNAME[0]} - Global favorites preset '$FAVMENUCFG' not found - starting with zero preselection"
			declare -a FavSel
		fi
	fi

	while read -r ENTRY; do
		GE="GUI_${ENTRY}"
		DE="DESC_${ENTRY}"
		if [ -z "${!GE}" ]; then
			GEOUT="no description"
		else
			GEOUT="${!GE}"
		fi

		if [ -z "${!DE}" ]; then
			DEOUT="no description"
		else
			DEOUT="${!DE}"
		fi

		if grep -q "^$ENTRY$" <<< "$(printf "%s\n" "${FavSel[@]}")"; then
			echo TRUE
			echo "$GEOUT"
			echo "$ENTRY"
			echo "$DEOUT"
		else
			echo FALSE
			echo "$GEOUT"
			echo "$ENTRY"
			echo "$DEOUT"
		fi
	done <<< "$(sort "$STLSETENTRIES")"
}

function favoritesMenu {
	if [ -z "$2" ]; then
		BACKFUNC="$NON"
	else
		if [ "$2" == "setGuiFavoritesSelection" ]; then
			BACKFUNC="MainMenu"
		else
			BACKFUNC="$2"
		fi
	fi

	if [ -f "${FAVMENU}-${TMPL}" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Found an autoconfigured favorites menu template '${FAVMENU}-${TMPL}' - using it directly"
		openCustMenu "$1" "$BACKFUNC" "Favorites" "$FAVMENU" "$LFM"
	else
		if [ ! -f "$STLFAVMENUCFG" ]; then
			writelog "INFO" "${FUNCNAME[0]} - No Favorites defined yet - opening Favorites Selection"
			rm "${FAVMENU}-${TMPL}" 2>/dev/null
			setGuiFavoritesSelection "$AID" "${FUNCNAME[0]}"
		fi

		if [ -f "$STLFAVMENUCFG" ]; then
			TEMPF="${FAVMENU}-${TMPL}_w1"
			touch "$TEMPF"
			while read -r savfav; do
				SAVCAT="CAT_$(grep "'$savfav'" "$STLRAWENTRIES" | grep -oP '#CAT_\K[^`]+')"
				FAVHEADL="$(grep "#HEAD" "$STLRAWENTRIES" | grep "$SAVCAT")"
				FAVHEAD="HEAD_$(grep -oP '#HEAD_\K[^`]+' <<< "$FAVHEADL")"
				if ! grep -q "$FAVHEAD" "$TEMPF" && [ "$SAVCAT" != "CAT_" ]; then
					echo "$FAVHEADL" >> "$TEMPF"
				fi

				grep "'$savfav'" "$STLRAWENTRIES" >> "$TEMPF"
			done < "$STLFAVMENUCFG"

			getFilteredEntries "$TEMPF" >> "${FAVMENU}-${TMPL}"

			rm "$TEMPF" 2>/dev/null

			writelog "INFO" "${FUNCNAME[0]} - Found '$(wc -l < "${FAVMENU}-${TMPL}")' selected Favorites - opening the Menu now"
			openCustMenu "$1" "$BACKFUNC" "Favorites" "$FAVMENU" "$LFM"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Still no Favorites defined - skipping"
		fi
	fi
}

function listAllSettingsEntries {
	if [ -f "$STLSETENTRIES" ]; then
		writelog "INFO" "${FUNCNAME[0]} - '$STLSETENTRIES' already exists - nothing to do"
	else
		writelog "INFO" "${FUNCNAME[0]} - Creating '$STLSETENTRIES'"
		while read -r ENTRY; do
			echo "$ENTRY" | tee -a "$STLRAWENTRIES" >/dev/null
			grep -oP "\('\K[^\')]+" <<< "$ENTRY" | tee -a "$STLSETENTRIES" >/dev/null
		done <<< "$(sed -n "/^#STARTSETENTRIES/,/^#ENDSETENTRIES/p;/^#ENDSETENTRIES/q" "$0" | grep "\--field")"
	fi
}

function spanFont {
	STRIN="$1"

	if [ "$2" == "H" ]; then
		FOSI="$HEADLINEFONT"
		FOWE="bold"
	fi

	SPIN="<span font_size='$FOSI' font_weight='$FOWE'>"
	SPOUT="</span>"
	echo "$SPIN$STRIN$SPOUT"
}

function AllSettingsEntriesDummyFunction {
#STARTSETENTRIES
	echo \
--field="$(spanFont "$GUI_OPTSGUI" "H")":LBL "SKIP" `#CAT_Gui` `#HEAD_Gui` `#MENU_GAME` `#MENU_GLOBAL` \
--field="     $GUI_STLLANG!$DESC_STLLANG ('STLLANG')":CB "$(cleanDropDown "${STLLANG/#-/ -}" "$LANGYADLIST")" `#CAT_Gui` `#MENU_GLOBAL` \
--field="     $GUI_STARTMENU!$DESC_STARTMENU ('STARTMENU')":CB "$(cleanDropDown "${STARTMENU/#-/ -}" "Editor!Favorites!Game!Menu")" `#CAT_Gui` `#MENU_GLOBAL` \
--field="     $GUI_WAITEDITOR!$DESC_WAITEDITOR ('WAITEDITOR')":NUM "${WAITEDITOR/#-/ -}" `#CAT_Gui` `#MENU_GAME` \
--field="     $GUI_MAXASK!$DESC_MAXASK ('MAXASK')":NUM "${MAXASK/#-/ -}" `#CAT_Gui` `#MENU_GLOBAL` \
--field="     $GUI_SAVESETSIZE!$DESC_SAVESETSIZE ('SAVESETSIZE')":CHK "${SAVESETSIZE/#-/ -}" `#CAT_Gui` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_TOGGLEWINDOWS!$DESC_TOGGLEWINDOWS ('TOGGLEWINDOWS')":CHK "${TOGGLEWINDOWS/#-/ -}" `#CAT_Gui` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USETRAYICON!$DESC_USETRAYICON ('USETRAYICON')":CHK "${USETRAYICON/#-/ -}" `#CAT_Gui` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_USENOTIFIER!$DESC_USENOTIFIER ('USENOTIFIER')":CHK "${USENOTIFIER/#-/ -}" `#CAT_Gui` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_HEADLINEFONT!$DESC_HEADLINEFONT ('HEADLINEFONT')":CB "$(cleanDropDown "${HEADLINEFONT/#-/ -}" "$FONTSIZES")" `#CAT_Gui` `#MENU_GLOBAL` \
--field="     $GUI_YADFORCEXWAYLAND!$DESC_YADFORCEXWAYLAND ('YADFORCEXWAYLAND')":CHK "${YADFORCEXWAYLAND/#-/ -}" `#CAT_Gui` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_USEWINDECO!$DESC_USEWINDECO ('USEWINDECO')":CHK "${USEWINDECO/#-/ -}" `#CAT_Gui` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_DLGAMEDATA!$DESC_DLGAMEDATA ('DLGAMEDATA')":CHK "${DLGAMEDATA/#-/ -}" `#CAT_Gui` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_DLSTEAMDECKCOMPATINFO!$DESC_DLSTEAMDECKCOMPATINFO ('DLSTEAMDECKCOMPATINFO')":CHK "${DLSTEAMDECKCOMPATINFO}" `#CAT_Gui` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_USEGAMEPICS!$DESC_USEGAMEPICS ('USEGAMEPICS')":CHK "${USEGAMEPICS/#-/ -}" `#CAT_Gui` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_USECUSTOMFALLBACKPIC!$DESC_USECUSTOMFALLBACKPIC ('USECUSTOMFALLBACKPIC')":CHK "${USECUSTOMFALLBACKPIC/#-/ -}" `#CAT_Gui` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_GITHUBUSER!$DESC_GITHUBUSER ('GITHUBUSER')":CBE "${GITHUBUSER/#-/ -}" `#CAT_Gui` `#MENU_GLOBAL` \
--field="     $GUI_CHECKCOLLECTIONS!$DESC_CHECKCOLLECTIONS ('CHECKCOLLECTIONS')":CHK "${CHECKCOLLECTIONS/#-/ -}" `#CAT_Gui` `#SUB_Checkbox` `#MENU_GAME` \
--field="$(spanFont "$GUI_OPTSGRID" "H")":LBL "SKIP" `#CAT_SteamGridDB` `#HEAD_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBDLTOSTEAM!$DESC_SGDBDLTOSTEAM ('SGDBDLTOSTEAM')":CHK "${SGDBDLTOSTEAM/#-/ -}" `#CAT_SteamGridDB` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_SGDBAPIKEY!$DESC_SGDBAPIKEY ('SGDBAPIKEY')":CBE "${SGDBAPIKEY/#-/ -}" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBHASFILE!$DESC_SGDBHASFILE ('SGDBHASFILE')":CB "$(cleanDropDown "${SGDBHASFILE/#-/ -}" "${SGDBHASFILEOPTS}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBAUTODL!$DESC_SGDBAUTODL ('SGDBAUTODL')":CB "$(cleanDropDown  "${SGDBAUTODL/#-/ -}" "$NON!after_game!before_game!no_meta")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBDLHERO!$DESC_SGDBDLHERO ('SGDBDLHERO')":CHK "${SGDBDLHERO/#-/ -}" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBDIMS!$DESC_SGDBDIMS ('SGDBHERODIMS')":CBE "$(cleanDropDown "${SGDBHERODIMS/#-/ -}" "${DEFSGDBHERODIMS//,/\!}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBTYPES!$DESC_SGDBTYPES ('SGDBHEROTYPES')":CBE "$(cleanDropDown "${SGDBHEROTYPES/#-/ -}" "${SGDBTYPEOPTS}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBSTYLES!$DESC_SGDBSTYLES ('SGDBHEROSTYLES')":CBE "$(cleanDropDown "${SGDBHEROSTYLES/#-/ -}" "${SGDBHEROSTYLEOPTS//,/\!}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBNSFW!$DESC_SGDBNSFW ('SGDBHERONSFW')":CBE "$(cleanDropDown "${SGDBHERONSFW/#-/ -}" "${SGDBTAGOPTS}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBHUMOR!$DESC_SGDBHUMOR ('SGDBHEROHUMOR')":CBE "$(cleanDropDown "${SGDBHEROHUMOR/#-/ -}" "${SGDBTAGOPTS}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBEPILEPSY!$DESC_SGDBEPILEPSY ('SGDBHEROEPILEPSY')":CBE "$(cleanDropDown "${SGDBHEROEPILEPSY/#-/ -}" "$SGDBTAGOPTS")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBDLLOGO!$DESC_SGDBDLLOGO ('SGDBDLLOGO')":CHK "${SGDBDLLOGO/#-/ -}" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBTYPES!$DESC_SGDBTYPES ('SGDBLOGOTYPES')":CBE "$(cleanDropDown "${SGDBLOGOTYPES/#-/ -}" "${SGDBTYPEOPTS}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBSTYLES!$DESC_SGDBSTYLES ('SGDBLOGOSTYLES')":CBE "$(cleanDropDown "${SGDBLOGOSTYLES/#-/ -}" "${SGDBLOGOSTYLEOPTS//,/\!}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBNSFW!$DESC_SGDBNSFW ('SGDBLOGONSFW')":CBE "$(cleanDropDown "${SGDBLOGONSFW/#-/ -}" "${SGDBTAGOPTS}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBHUMOR!$DESC_SGDBHUMOR ('SGDBLOGOHUMOR')":CBE "$(cleanDropDown "${SGDBLOGOHUMOR/#-/ -}" "${SGDBTAGOPTS}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBEPILEPSY!$DESC_SGDBEPILEPSY ('SGDBLOGOEPILEPSY')":CBE "$(cleanDropDown "${SGDBLOGOEPILEPSY/#-/ -}" "$SGDBTAGOPTS")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBDLBOXART!$DESC_SGDBDLBOXART ('SGDBDLBOXART')":CHK "${SGDBDLBOXART/#-/ -}" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBDIMS!$DESC_SGDBDIMS ('SGDBBOXARTDIMS')":CBE "$(cleanDropDown "${SGDBBOXARTDIMS/#-/ -}" "${DEFSGDBBOXARTDIMS//,/\!}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBTYPES!$DESC_SGDBTYPES ('SGDBBOXARTTYPES')":CBE "$(cleanDropDown "${SGDBBOXARTTYPES/#-/ -}" "${SGDBTYPEOPTS}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBSTYLES!$DESC_SGDBSTYLES ('SGDBBOXARTSTYLES')":CBE "$(cleanDropDown "${SGDBBOXARTSTYLES/#-/ -}" "${SGDBGRIDSTYLEOPTS//,/\!}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBNSFW!$DESC_SGDBNSFW ('SGDBBOXARTNSFW')":CBE "$(cleanDropDown "${SGDBBOXARTNSFW/#-/ -}" "${SGDBTAGOPTS}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBHUMOR!$DESC_SGDBHUMOR ('SGDBBOXARTHUMOR')":CBE "$(cleanDropDown "${SGDBBOXARTHUMOR/#-/ -}" "${SGDBTAGOPTS}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBEPILEPSY!$DESC_SGDBEPILEPSY ('SGDBBOXARTEPILEPSY')":CBE "$(cleanDropDown "${SGDBBOXARTEPILEPSY/#-/ -}" "$SGDBTAGOPTS")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBDLTENFOOT!$DESC_SGDBDLTENFOOT ('SGDBDLTENFOOT')":CHK "${SGDBDLTENFOOT/#-/ -}" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBDIMS!$DESC_SGDBDIMS ('SGDBTENFOOTDIMS')":CBE "$(cleanDropDown "${SGDBTENFOOTDIMS/#-/ -}" "${DEFSGDBTENFOOTDIMS//,/\!}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBTYPES!$DESC_SGDBTYPES ('SGDBTENFOOTTYPES')":CBE "$(cleanDropDown "${SGDBTENFOOTTYPES/#-/ -}" "${SGDBTYPEOPTS}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBSTYLES!$DESC_SGDBSTYLES ('SGDBTENFOOTSTYLES')":CBE "$(cleanDropDown "${SGDBTENFOOTSTYLES/#-/ -}" "${SGDBTNFTSTYLEOPTS//,/\!}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBNSFW!$DESC_SGDBNSFW ('SGDBTENFOOTNSFW')":CBE "$(cleanDropDown "${SGDBTENFOOTNSFW/#-/ -}" "${SGDBTAGOPTS}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBHUMOR!$DESC_SGDBHUMOR ('SGDBTENFOOTHUMOR')":CBE "$(cleanDropDown "${SGDBTENFOOTHUMOR/#-/ -}" "${SGDBTAGOPTS}")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="     $GUI_SGDBEPILEPSY!$DESC_SGDBEPILEPSY ('SGDBTENFOOTEPILEPSY')":CBE "$(cleanDropDown "${SGDBTENFOOTEPILEPSY/#-/ -}" "$SGDBTAGOPTS")" `#CAT_SteamGridDB` `#MENU_GLOBAL` \
--field="$(spanFont "$GUI_OPTSHMM" "H")":LBL "SKIP" `#CAT_HMM` `#HEAD_HMM` `#MENU_GLOBAL` \
--field="     $GUI_HMMDLVER!$DESC_HMMDLVER ('HMMDLVER')":CB "$(cleanDropDown "${HMMDLVER/#-/ -}" "$HMMSTABLE!$HMMDEV")" `#CAT_HMM` `#MENU_GLOBAL` \
--field="     $GUI_HMMCOMPDATA!$DESC_HMMCOMPDATA ('HMMCOMPDATA')":DIR "${HMMCOMPDATA/#-/ -}" `#CAT_HMM` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_USEHMMPROTON!$DESC_USEHMMPROTON ('USEHMMPROTON')":CB "$(cleanDropDown "${USEHMMPROTON/#-/ -}" "$PROTYADLIST")" `#CAT_HMM` `#MENU_GLOBAL` \
--field="$(spanFont "$GUI_OPTSMISC" "H")":LBL "SKIP" `#CAT_Misc` `#HEAD_Misc` `#MENU_GAME` `#MENU_GLOBAL`  \
--field="     $GUI_KEEPSTLOPEN!$DESC_KEEPSTLOPEN ('KEEPSTLOPEN')":CHK "${KEEPSTLOPEN/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USECUSTOMCMD!$DESC_USECUSTOMCMD ('USECUSTOMCMD')":CHK "${USECUSTOMCMD/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_CUSTOMCMD!$DESC_CUSTOMCMD $GUI_ECHOPLAC ('CUSTOMCMD')":FL "${OPCUSTPATH/#-/ -}" `#CAT_Misc` `#MENU_GAME` \
--field="     $GUI_CUSTOMCMD_ARGS!$DESC_CUSTOMCMD_ARGS ('CUSTOMCMD_ARGS')" "$( printf "%s" "${CUSTOMCMD_ARGS/#-/ -}" )" `#CAT_Misc` `#MENU_GAME` \
--field="     $GUI_FORK_CUSTOMCMD!$DESC_FORK_CUSTOMCMD ('FORK_CUSTOMCMD')":CHK "${FORK_CUSTOMCMD/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_EXTPROGS_CUSTOMCMD!$DESC_EXTPROGS_CUSTOMCMD ('EXTPROGS_CUSTOMCMD')":CHK "${EXTPROGS_CUSTOMCMD/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_CUSTOMCMD_USESLR!$DESC_CUSTOMCMD_USESLR ('CUSTOMCMD_USESLR')":CHK "${CUSTOMCMD_USESLR/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_ONLY_CUSTOMCMD!$DESC_ONLY_CUSTOMCMD ('ONLY_CUSTOMCMD')":CHK "${ONLY_CUSTOMCMD/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_CUSTOMCMD_FORCEWIN!$DESC_CUSTOMCMD_FORCEWIN ('CUSTOMCMDFORCEWIN')":CHK "${CUSTOMCMDFORCEWIN/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_WAITFORCUSTOMCMD!$DESC_WAITFORCUSTOMCMD ('WAITFORCUSTOMCMD')":NUM "${WAITFORCUSTOMCMD/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_INJECT_CUSTOMCMD!$DESC_INJECT_CUSTOMCMD ('INJECT_CUSTOMCMD')":CHK "${INJECT_CUSTOMCMD/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_INJECTWAIT!$DESC_INJECTWAIT ('INJECTWAIT')":NUM "${INJECTWAIT/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEIGCS!$DESC_USEIGCS ('USEIGCS')":CHK "${USEIGCS/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_UUUSEIGCS!$DESC_UUUSEIGCS ('UUUSEIGCS')":CHK "${UUUSEIGCS/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_IGCSWAIT!$DESC_IGCSWAIT ('IGCSWAIT')":NUM "${IGCSWAIT/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_UUUSEPATCH!$DESC_UUUSEPATCH ('UUUSEPATCH')":CHK "${UUUSEPATCH/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_UUUPATCHWAIT!$DESC_UUUPATCHWAIT ('UUUPATCHWAIT')":NUM "${UUUPATCHWAIT/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USERSTART!$DESC_USERSTART $GUI_ECHOPLAC ('USERSTART')":FL "${USERSTART/#-/ -}" `#CAT_Misc` `#MENU_GAME` \
--field="     $GUI_USERSTOP!$DESC_USERSTOP $GUI_ECHOPLAC ('USERSTOP')":FL "${USERSTOP/#-/ -}" `#CAT_Misc` `#MENU_GAME` \
--field="     $GUI_GAMEARGS!$DESC_GAMEARGS ('GAMEARGS')" "${GAMEARGS/#-/ -}" `#CAT_Misc` `#MENU_GAME` \
--field="     $GUI_SORTGARGS!$DESC_SORTGARGS ('SORTGARGS')":CHK "${SORTGARGS/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_HARDARGS!$DESC_HARDARGS ('HARDARGS')":CBE "$(cleanDropDown "${HARDARGS/#-/ -}" "$ARGUMENTS")" `#CAT_Misc` `#MENU_GAME` \
--field="     $GUI_SLOARGS!$DESC_SLOARGS ('SLOARGS')":RO "${SLOARGS/#-/ -}" `#CAT_Misc` `#MENU_GAME` \
--field="     $GUI_CHANGE_PULSE_LATENCY!$DESC_CHANGE_PULSE_LATENCY ('CHANGE_PULSE_LATENCY')":CHK "${CHANGE_PULSE_LATENCY/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_STL_PULSE_LATENCY_MSEC!$DESC_STL_PULSE_LATENCY_MSEC ('STL_PULSE_LATENCY_MSEC')":NUM "${STL_PULSE_LATENCY_MSEC/#-/ -}" `#CAT_Misc` `#MENU_GAME` \
--field="     $GUI_LOGLEVEL!$DESC_LOGLEVEL ('LOGLEVEL')":CB "$(cleanDropDown "${LOGLEVEL/#-/ -}" "0!1!2")" `#CAT_Misc` `#MENU_GLOBAL` \
--field="     $GUI_RESETLOG!$DESC_RESETLOG ('RESETLOG')":CHK "${RESETLOG/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_STEAMAPPIDFILE!$DESC_STEAMAPPIDFILE ('STEAMAPPIDFILE')":CHK "${STEAMAPPIDFILE/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_SKIPINTDEPCHECK!$DESC_SKIPINTDEPCHECK ('SKIPINTDEPCHECK')":CHK "${SKIPINTDEPCHECK/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_NOSTEAMSTLDEF!$DESC_NOSTEAMSTLDEF ('NOSTEAMSTLDEF')":CHK "${NOSTEAMSTLDEF/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_STORECOMPDATTITLE!$DESC_STORECOMPDATTITLE ('STORECOMPDATTITLE')":CHK "${STORECOMPDATTITLE/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_LOGPLAYTIME!$DESC_LOGPLAYTIME ('LOGPLAYTIME')":CHK "${LOGPLAYTIME/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_CRASHGUESS!$DESC_CRASHGUESS ('CRASHGUESS')":NUM "${CRASHGUESS/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_HELPURL!$DESC_HELPURL ('HELPURL')":CB "$(cleanDropDown "${HELPURL}" "${HUYLIST}${NON}")" `#CAT_Misc` `#MENU_GAME` \
--field="     $GUI_CREATEDESKTOPICON!$DESC_CREATEDESKTOPICON ('CREATEDESKTOPICON')":NUM "${CREATEDESKTOPICON}!0..3" `#CAT_Misc` `#MENU_GAME` \
--field="     $GUI_GAMESCREENRES!$DESC_GAMESCREENRES ('GAMESCREENRES')":CBE "$(cleanDropDown "${GAMESCREENRES/#-/ -}" "$(printf "%s\n" "$(listScreenRes | tr '\n' '!')")$NON")" `#CAT_Misc` `#MENU_GAME` \
--field="     $GUI_TOGSTEAMWEBHELPER!$DESC_TOGSTEAMWEBHELPER ('TOGSTEAMWEBHELPER')":CHK "${TOGSTEAMWEBHELPER/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEGEOELF!$DESC_USEGEOELF ('USEGEOELF')":CHK "${USEGEOELF/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_AUTOGEOELF!$DESC_AUTOGEOELF ('AUTOGEOELF')":CHK "${AUTOGEOELF/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEFWS!$DESC_USEFWS ('USEFWS')":CHK "${USEFWS/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USETERM!$DESC_USETERM ('USETERM')":FL "${USETERM/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_TERMARGS!$DESC_TERMARGS ('TERMARGS')" "${TERMARGS/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_RUNSBS!$DESC_RUNSBS ('RUNSBS')":CHK "${RUNSBS/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_SDLUSEWAYLAND!$DESC_SDLUSEWAYLAND ('SDLUSEWAYLAND')":CHK "${SDLUSEWAYLAND/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_STLRAD_PFTST!$DESC_STLRAD_PFTST ('STLRAD_PFTST')":CBE "$(cleanDropDown "${STLRAD_PFTST/#-/ -}" "none!gpl!sam!rt!emulate_rt!rtwave64!video_decode")"  `#CAT_Misc` `#MENU_GAME` \
--field="     $GUI_STEAMDECK_AUTOUP!$DESC_STEAMDECK_AUTOUP ('STEAMDECK_AUTOUP')":CHK "${STEAMDECK_AUTOUP/#-/ -}" `#CAT_Misc` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="$(spanFont "$GUI_OPTSPROTON" "H")":LBL "SKIP" `#CAT_Proton` `#HEAD_Proton` `#MENU_GAME` `#MENU_GLOBAL` \
--field="     $GUI_USEPROTON!$DESC_USEPROTON ('USEPROTON')":CB "$(cleanDropDown  "${USEPROTON/#-/ -}" "$PROTYADLIST")" `#CAT_Proton` `#MENU_GAME` \
--field="     $GUI_USESLR!$DESC_USESLR ('USESLR')":CHK "${USESLR/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_FORCESLR!$DESC_FORCESLR ('FORCESLR')":CHK "${FORCESLR/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_IGNORECOMPATSLR!$DESC_IGNORECOMPATSLR ('IGNORECOMPATSLR')":CHK "${IGNORECOMPATSLR/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEREAP!$DESC_USEREAP ('USEREAP')":CHK "${USEREAP/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_FORCEREAP!$DESC_FORCEREAP ('FORCEREAP')":CHK "${FORCEREAP/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_AUTOLASTPROTON!$DESC_AUTOLASTPROTON ('AUTOLASTPROTON')":CHK "${AUTOLASTPROTON/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_REDIRCOMPDATA!$DESC_REDIRCOMPDATA ('REDIRCOMPDATA')":CB "$(cleanDropDown "${REDIRCOMPDATA/#-/ -}" "disabled!single-proton!global-proton")" `#CAT_Proton` `#MENU_GAME` \
--field="     $GUI_REDIRSTEAMUSER!$DESC_REDIRSTEAMUSER ('REDIRSTEAMUSER')":CB "$(cleanDropDown "${REDIRSTEAMUSER/#-/ -}" "disabled!symlink!restore-backup")" `#CAT_Proton` `#MENU_GAME` \
--field="     $GUI_ONLYPROTMAJORREDIRECT!$DESC_ONLYPROTMAJORREDIRECT ('ONLYPROTMAJORREDIRECT')":CHK "${ONLYPROTMAJORREDIRECT/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_AUTOBUMPGE!$DESC_AUTOBUMPGE ('AUTOBUMPGE')":CHK "${AUTOBUMPGE/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_AUTOBUMPPROTON!$DESC_AUTOBUMPPROTON ('AUTOBUMPPROTON')":CHK "${AUTOBUMPPROTON/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USESPECIALK!$DESC_USESPECIALK ('USESPECIALK')":CHK "${USESPECIALK/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_SPEKVERS!$DESC_SPEKVERS ('SPEKVERS')":CB "$(cleanDropDown "${SPEKVERS/#-/ -}" "stable!nightly!custom")" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_SPEKDLLNAME!$DESC_SPEKDLLNAME ('SPEKDLLNAME')":CBE "$( cleanDropDown "${SPEKDLLNAME/#-/ -}" "$SPEKDLLNAMELIST" )" `#CAT_Proton` `#MENU_GAME` \
--field="     $GUI_USERESHSPEKPLUGIN!$DESC_USERESHSPEKPLUGIN ('USERESHSPEKPLUGIN')":CHK "${USERESHSPEKPLUGIN/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USESPEKD3D47!$DESC_USESPEKD3D47 ('USESPEKD3D47')":CHK "${USESPEKD3D47/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_AUTOSPEK!$DESC_AUTOSPEK ('AUTOSPEK')":CHK "${AUTOSPEK/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_PROTON_LOG!$DESC_PROTON_LOG $HOME/steam-$AID.log ('PROTON_LOG')":CHK "${PROTON_LOG/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_PROTON_LOG_DIR!$DESC_PROTON_LOG_DIR ('PROTON_LOG_DIR')":DIR "${PROTON_LOG_DIR/#-/ -}" `#CAT_Proton` `#SUB_Directories` `#MENU_GAME` \
--field="     $GUI_USEWINEDEBUGPROTON!$DESC_USEWINEDEBUGPROTON ('USEWINEDEBUGPROTON')":CHK "${USEWINEDEBUGPROTON/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_PROTON_NO_D3D10!$DESC_PROTON_NO_D3D10 ('PROTON_NO_D3D10')":CHK "${PROTON_NO_D3D10/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_PROTON_NO_D3D11!$DESC_PROTON_NO_D3D11 ('PROTON_NO_D3D11')":CHK "${PROTON_NO_D3D11/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_PROTON_NO_ESYNC!$DESC_PROTON_NO_ESYNC ('PROTON_NO_ESYNC')":CHK "${PROTON_NO_ESYNC/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_PROTON_NO_FSYNC!$DESC_PROTON_NO_FSYNC ('PROTON_NO_FSYNC')":CHK "${PROTON_NO_FSYNC/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_ENABLE_WINESYNC!$DESC_ENABLE_WINESYNC ('ENABLE_WINESYNC')":CHK "${ENABLE_WINESYNC/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_PROTON_ENABLE_NVAPI!$DESC_PROTON_ENABLE_NVAPI ('PROTON_ENABLE_NVAPI')":CHK "${PROTON_ENABLE_NVAPI/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_PROTON_HIDE_NVIDIA_GPU!$DESC_PROTON_HIDE_NVIDIA_GPU ('PROTON_HIDE_NVIDIA_GPU')":CHK "${PROTON_HIDE_NVIDIA_GPU/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEDLSS!$DESC_USEDLSS ('USEDLSS')":CHK "${USEDLSS/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_PROTON_USE_WINED3D!$DESC_PROTON_USE_WINED3D ('PROTON_USE_WINED3D')":CHK "${PROTON_USE_WINED3D/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_PROTON_DEBUG_DIR!$DESC_PROTON_DEBUG_DIR ('PROTON_DEBUG_DIR')":DIR "${PROTON_DEBUG_DIR/#-/ -}" `#CAT_Proton` `#SUB_Directories` `#MENU_GAME` \
--field="     $GUI_PROTON_DUMP_DEBUG_COMMANDS!$DESC_PROTON_DUMP_DEBUG_COMMANDS ('PROTON_DUMP_DEBUG_COMMANDS')":CHK "${PROTON_DUMP_DEBUG_COMMANDS/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_PROTON_FORCE_LARGE_ADDRESS_AWARE!$DESC_PROTON_FORCE_LARGE_ADDRESS_AWARE ('PROTON_FORCE_LARGE_ADDRESS_AWARE')":CHK "${PROTON_FORCE_LARGE_ADDRESS_AWARE/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USE_STLDXVKCFG!$DESC_USE_STLDXVKCFG ('USE_STLDXVKCFG')":CHK "${USE_STLDXVKCFG/#-/ -}" `#CAT_Proton` `#SUB_Dxvk` `#MENU_GAME` \
--field="     $GUI_DXVK_HUD!$DESC_DXVK_HUD ('DXVK_HUD')":CBE "$(cleanDropDown "${DXVK_HUD/#-/ -}" "0!1")" `#CAT_Proton` `#SUB_Dxvk` `#MENU_GAME` \
--field="     $GUI_DXVK_LOG_LEVEL!$DESC_DXVK_LOG_LEVEL ('DXVK_LOG_LEVEL')":CB "$(cleanDropDown "${DXVK_LOG_LEVEL/#-/ -}" "none!error!warn!info!debug")" `#CAT_Proton` `#SUB_Dxvk` `#MENU_GAME` \
--field="     $GUI_DXVK_LOG_PATH!$DESC_DXVK_LOG_PATH ('DXVK_LOG_PATH')":DIR "${DXVK_LOG_PATH/#-/ -}" `#CAT_Proton` `#SUB_Directories` `#MENU_GAME` \
--field="     $GUI_DXVK_SCALE!$DESC_DXVK_SCALE ('DXVK_SCALE')" "${DXVK_SCALE/#-/ -}" `#CAT_Proton` `#MENU_GAME` \
--field="     $GUI_DXVK_FPSLIMIT!$DESC_DXVK_FPSLIMIT ('DXVK_FPSLIMIT')":CBE "$(cleanDropDown "${DXVK_FPSLIMIT/#-/ -}" "none!30!60!75!90!120!144!165!240")"  `#CAT_Proton` `#MENU_GAME` \
--field="     $GUI_DXVK_ASYNC!$DESC_DXVK_ASYNC ('DXVK_ASYNC')":CHK "${DXVK_ASYNC/#-/ -}" `#CAT_Proton` `#SUB_Dxvk` `#MENU_GAME` \
--field="     $GUI_DXVK_HDR!$DESC_DXVK_HDR ('DXVK_HDR')":CHK "${DXVK_HDR/#-/ -}" `#CAT_Proton` `#SUB_Dxvk` `#MENU_GAME` \
--field="     $GUI_CUSTPROTDLDIR!$DESC_CUSTPROTDLDIR ('CUSTPROTDLDIR')":DIR "${CUSTPROTDLDIR/#-/ -}" `#CAT_Proton` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_CUSTPROTEXTDIR!$DESC_CUSTPROTEXTDIR ('CUSTPROTEXTDIR')":DIR "${CUSTPROTEXTDIR/#-/ -}" `#CAT_Proton` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_CUPROTOCOMPAT!$DESC_CUPROTOCOMPAT ('CUPROTOCOMPAT')":CHK "${CUPROTOCOMPAT/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_USESUSYM!$DESC_USESUSYM ('USESUSYM')":CHK "${USESUSYM/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEGLOBSUSYM!$DESC_USEGLOBSUSYM ('USEGLOBSUSYM')":CHK "${USEGLOBSUSYM/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_FIXSYMLINKS!$DESC_FIXSYMLINKS ('FIXSYMLINKS')":CHK "${FIXSYMLINKS/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_UNSYMLINK!$DESC_UNSYMLINK ('UNSYMLINK')":CHK "${UNSYMLINK/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_DELPFX!$DESC_DELPFX ('DELPFX')":CHK "${DELPFX/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_BACKUPSTEAMUSER!$DESC_BACKUPSTEAMUSER ('BACKUPSTEAMUSER')":CHK "${BACKUPSTEAMUSER/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_RESTORESTEAMUSER!$DESC_RESTORESTEAMUSER ('RESTORESTEAMUSER')":CB "$(cleanDropDown "${RESTORESTEAMUSER/#-/ -}" "$NON!ask-always!ask-if-dst-has-no-backup-timestamp!ask-if-unsure!restore-always!restore-if-backup-timestamp-is-newer!restore-if-dst-has-no-backup-timestamp!restore-if-dst-is-empty")" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_CLEANPROTONTEMP!$DESC_CLEANPROTONTEMP ('CLEANPROTONTEMP')":CHK "${CLEANPROTONTEMP/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEPDBRATING!$DESC_USEPDBRATING ('USEPDBRATING')":CHK "${USEPDBRATING/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_PDBRATINGCACHE!$DESC_PDBRATINGCACHE ('PDBRATINGCACHE')":NUM "${PDBRATINGCACHE/#-/ -}" `#CAT_Proton` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="$(spanFont "$GUI_OPTSVKD3D" "H")":LBL "SKIP" `#CAT_VKD3D` `#HEAD_VKD3D` `#MENU_GAME` \
--field="     $GUI_USERAYTRACING!$DESC_USERAYTRACING ('USERAYTRACING')":CHK "${USERAYTRACING/#-/ -}" `#CAT_VKD3D` `#MENU_GAME` \
--field="     $GUI_STL_VKD3D_CONFIG!$DESC_STL_VKD3D_CONFIG ('STL_VKD3D_CONFIG')":CB "$(cleanDropDown "${STL_VKD3D_CONFIG/#-/ -}" "$NON!vk_debug!skip_application_workarounds!dxr!dxr11!force_static_cbv!single_queue!no_upload_hvv!force_host_cached!no_invariant_position")" `#CAT_VKD3D` `#MENU_GAME` \
--field="     $GUI_STL_VKD3D_DEBUG!$DESC_STL_VKD3D_DEBUG ('STL_VKD3D_DEBUG')":CB "$(cleanDropDown "${STL_VKD3D_DEBUG/#-/ -}" "$NON!err!info!fixme!warn!trace")" `#CAT_VKD3D` `#MENU_GAME` \
--field="     $GUI_STL_VKD3D_SHADER_DEBUG!$DESC_STL_VKD3D_SHADER_DEBUG ('STL_VKD3D_SHADER_DEBUG')":CB "$(cleanDropDown "${STL_VKD3D_SHADER_DEBUG/#-/ -}" "$NON!err!info!fixme!warn!trace")" `#CAT_VKD3D` `#MENU_GAME` \
--field="     $GUI_STL_VKD3D_LOG_FILE!$DESC_STL_VKD3D_LOG_FILE ('STL_VKD3D_LOG_FILE')":CBE "$(cleanDropDown "${STL_VKD3D_LOG_FILE/#-/ -}" "$NON!$STLVKD3DLOGDIR")" `#CAT_VKD3D` `#MENU_GAME` \
--field="     $GUI_STL_VKD3D_VULKAN_DEVICE!$DESC_STL_VKD3D_VULKAN_DEVICE ('STL_VKD3D_VULKAN_DEVICE')":CBE "$(cleanDropDown "${STL_VKD3D_VULKAN_DEVICE/#-/ -}")" `#CAT_VKD3D` `#MENU_GAME` \
--field="     $GUI_STL_VKD3D_FILTER_DEVICE_NAME!$DESC_STL_VKD3D_FILTER_DEVICE_NAME ('STL_VKD3D_FILTER_DEVICE_NAME')":CBE "$(cleanDropDown "${STL_VKD3D_FILTER_DEVICE_NAME/#-/ -}")" `#CAT_VKD3D` `#MENU_GLOBAL` \
--field="     $GUI_STL_VKD3D_DISABLE_EXTENSIONS!$DESC_STL_VKD3D_DISABLE_EXTENSIONS ('STL_VKD3D_DISABLE_EXTENSIONS')":CBE "$(cleanDropDown "${STL_VKD3D_DISABLE_EXTENSIONS/#-/ -}")" `#CAT_VKD3D` `#MENU_GAME` \
--field="     $GUI_STL_VKD3D_TEST_DEBUG!$DESC_STL_VKD3D_TEST_DEBUG ('STL_VKD3D_TEST_DEBUG')":NUM "${STL_VKD3D_TEST_DEBUG}!0..2" `#CAT_VKD3D` `#MENU_GAME` \
--field="     $GUI_STL_VKD3D_TEST_FILTER!$DESC_STL_VKD3D_TEST_FILTER ('STL_VKD3D_TEST_FILTER')":CB "$(cleanDropDown "${STL_VKD3D_TEST_FILTER/#-/ -}" "$NON!clear_render_target")" `#CAT_VKD3D` `#MENU_GAME` \
--field="     $GUI_STL_VKD3D_TEST_EXCLUDE!$DESC_STL_VKD3D_TEST_EXCLUDE ('STL_VKD3D_TEST_EXCLUDE')":CB "$(cleanDropDown "${STL_VKD3D_TEST_EXCLUDE/#-/ -}" "$NON!test_root_signature_priority,test_conservative_rasterization_dxil")" `#CAT_VKD3D` `#MENU_GAME` \
--field="     $GUI_STL_VKD3D_TEST_PLATFORM!$DESC_STL_VKD3D_TEST_PLATFORM ('STL_VKD3D_TEST_PLATFORM')":CB "$(cleanDropDown "${STL_VKD3D_TEST_PLATFORM/#-/ -}" "$NON!wine!windows!other")" `#CAT_VKD3D` `#MENU_GAME` \
--field="     $GUI_STL_VKD3D_TEST_BUG!$DESC_STL_VKD3D_TEST_BUG ('STL_VKD3D_TEST_BUG')":CBE "$(cleanDropDown "${STL_VKD3D_TEST_BUG/#-/ -}" "$NON!0")" `#CAT_VKD3D` `#MENU_GAME` \
--field="     $GUI_STL_VKD3D_PROFILE_PATH!$DESC_STL_VKD3D_PROFILE_PATH ('STL_VKD3D_PROFILE_PATH')":DIR "${STL_VKD3D_PROFILE_PATH/#-/ -}" `#CAT_VKD3D` `#MENU_GAME` \
--field="$(spanFont "$GUI_OPTSSHADER" "H")":LBL "SKIP" `#CAT_Shader` `#HEAD_Shader` `#MENU_GAME` `#MENU_GLOBAL` \
--field="     $GUI_USERESHADE!$DESC_USERESHADE ('USERESHADE')":CHK "${USERESHADE/#-/ -}" `#CAT_Shader` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_CUSTOMCMDRESHADE!$DESC_CUSTOMCMDRESHADE ('CUSTOMCMDRESHADE')":CHK "${CUSTOMCMDRESHADE/#-/ -}" `#CAT_Shader` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_DOWNLOAD_RESHADE!$DESC_DOWNLOAD_RESHADE ('DOWNLOAD_RESHADE')":CHK "${DOWNLOAD_RESHADE/#-/ -}" `#CAT_Shader` `#SUB_ReShade` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_RESHADEUPDATE!$DESC_RESHADEUPDATE ('RESHADEUPDATE')":CHK "${RESHADEUPDATE/#-/ -}" `#CAT_Shader` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_CREATERESHINI!$DESC_CREATERESHINI ('CREATERESHINI')":CHK "${CREATERESHINI/#-/ -}" `#CAT_Shader` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_RESHADEOVERRIDETOGGLE!$DESC_RESHADEOVERRIDETOGGLE ('RSOVRD')":CHK "${RSOVRD/#-/ -}" `#CAT_Shader` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_RSVERS!$DESC_RSVERS ('RSVERS')" "${RSVERS/#-/ -}" `#CAT_Shader` `#SUB_ReShade` `#MENU_GLOBAL` \
--field="     $GUI_USERSSPEKVERS!$DESC_USERSSPEKVERS ('USERSSPEKVERS')":CHK "${USERSSPEKVERS/#-/ -}" `#CAT_Shader` `#SUB_ReShade` `#MENU_GLOBAL` \
--field="     $GUI_RSSPEKVERS!$DESC_RSSPEKVERS ('RSSPEKVERS')" "${RSSPEKVERS/#-/ -}" `#CAT_Shader` `#SUB_ReShade` `#MENU_GLOBAL` \
--field="     $GUI_AUTOBUMPRESHADE!$DESC_AUTOBUMPRESHADE ('AUTOBUMPRESHADE')":CHK "${AUTOBUMPRESHADE/#-/ -}" `#CAT_Shader` `#SUB_ReShade` `#MENU_GLOBAL` \
--field="     $GUI_RS_32!$DESC_RS_32 ('RS_32')" "${RS_32/#-/ -}" `#CAT_Shader` `#SUB_ReShade` `#MENU_GLOBAL` \
--field="     $GUI_RS_64!$DESC_RS_64 ('RS_64')" "${RS_64/#-/ -}" `#CAT_Shader` `#SUB_ReShade` `#MENU_GLOBAL` \
--field="     $GUI_RS_32_VK!$DESC_RS_32_VK ('RS_32_VK')" "${RS_32_VK/#-/ -}" `#CAT_Shader` `#SUB_ReShade` `#MENU_GLOBAL` \
--field="     $GUI_RS_64_VK!$DESC_RS_64_VK ('RS_64_VK')" "${RS_64_VK/#-/ -}" `#CAT_Shader` `#SUB_ReShade` `#MENU_GLOBAL` \
--field="     $GUI_D3D47_32!$DESC_D3D47_32 ('D3D47_32')" "${D3D47_32/#-/ -}" `#CAT_Shader` `#SUB_ReShade` `#MENU_GLOBAL` \
--field="     $GUI_D3D47_64!$DESC_D3D47_64 ('D3D47_64')" "${D3D47_64/#-/ -}" `#CAT_Shader` `#SUB_ReShade` `#MENU_GLOBAL` \
--field="     $GUI_RESHADEOVERRIDEVERSION!$DESC_RESHADEOVERRIDEVERSION ('RSOVRVERS')":CBE "$(cleanDropDown "${RSOVRVERS/#-/ -}" "$RESHADEVERSIONS")" `#CAT_Shader` `#SUB_ReShade` `#MENU_GAME` \
--field="     $GUI_RESHADEDLLNAME!$DESC_RESHADEDLLNAME ('RESHADEDLLNAME')":CBE "$(cleanDropDown "${RESHADEDLLNAME/#-/ -}" "$RESHADEDLLNAMELIST")" `#CAT_Shader` `#SUB_ReShade` `#MENU_GAME` \
--field="     $GUI_ARCHALTEXE!$DESC_ARCHALTEXE ('ARCHALTEXE')":FL "${ARCHALTEXE/#-/ -}" `#CAT_Shader` `#SUB_ReShade` `#MENU_GAME` \
--field="     $GUI_ALTEXEPATH!$DESC_ALTEXEPATH ('ALTEXEPATH')":DIR "${ALTEXEPATH/#-/ -}" `#CAT_Shader` `#SUB_Directories` `#MENU_GAME` \
--field="     $GUI_RESHADESRCDIR!$DESC_RESHADESRCDIR ('RESHADESRCDIR')":DIR "${RESHADESRCDIR/#-/ -}" `#CAT_Shader` `#SUB_ReShade` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_RESHADE_DEPTH3D!$DESC_RESHADE_DEPTH3D ('RESHADE_DEPTH3D')":CHK "${RESHADE_DEPTH3D/#-/ -}" `#CAT_Shader` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_DLSHADER!$DESC_DLSHADER ('DLSHADER')":CHK "${DLSHADER/#-/ -}" `#CAT_Shader` `#SUB_Checkbox` `#MENU_GLOBAL`  \
--field="     $GUI_CHOOSESHADERS!$DESC_CHOOSESHADERS ('CHOOSESHADERS')":CHK "${CHOOSESHADERS/#-/ -}" `#CAT_Shader` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_SBSRS!$DESC_SBSRS ('SBSRS')":CHK "${SBSRS/#-/ -}" `#CAT_Shader` `#SUB_Checkbox` `#SUB_ReShade` `#MENU_GAME` \
--field="     $GUI_VULKANPOSTPROCESSOR!$DESC_VULKANPOSTPROCESSOR ('VULKANPOSTPROCESSOR')":CB "$( cleanDropDown "${VULKANPOSTPROCESSOR/#-/ -}" "$NON!vkBasalt" )" `#CAT_Shader` `#MENU_GAME` \
--field="$(spanFont "$PROGNAME $GUI_PATHS" "H")":LBL "SKIP" `#CAT_Paths` `#HEAD_Stl` `#MENU_GLOBAL`  \
--field="     $GUI_GLOBALCOLLECTIONDIR!$DESC_GLOBALCOLLECTIONDIR ('GLOBALCOLLECTIONDIR')":DIR "${GLOBALCOLLECTIONDIR/#-/ -}" `#CAT_Paths` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_GLOBALMISCDIR!$DESC_GLOBALMISCDIR ('GLOBALMISCDIR')":DIR "${GLOBALMISCDIR/#-/ -}" `#CAT_Paths` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_GLOBALSBSTWEAKS!$DESC_GLOBALSBSTWEAKS ('GLOBALSBSTWEAKS')":DIR "${GLOBALSBSTWEAKS/#-/ -}" `#CAT_Paths` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_GLOBALTWEAKS!$DESC_GLOBALTWEAKS ('GLOBALTWEAKS')":DIR "${GLOBALTWEAKS/#-/ -}" `#CAT_Paths` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_GLOBALSTLLANGDIR!$DESC_GLOBALSTLLANGDIR ('GLOBALSTLLANGDIR')":DIR "${GLOBALSTLLANGDIR/#-/ -}" `#CAT_Paths` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_GLOBALEVALDIR!$DESC_GLOBALEVALDIR ('GLOBALEVALDIR')":DIR "${GLOBALEVALDIR/#-/ -}" `#CAT_Paths` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_GLOBALSTLGUIDIR!$DESC_GLOBALSTLGUIDIR ('GLOBALSTLGUIDIR')":DIR "${GLOBALSTLGUIDIR/#-/ -}" `#CAT_Paths` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $PROGNAME $GUI_LOGDIR!$(strFix "$DESC_LOGDIR" "$PROGNAME") ('LOGDIR')":DIR "${LOGDIR/#-/ -}" `#CAT_Paths` `#SUB_Directories` `#MENU_GLOBAL` \
--field="$(spanFont "$GUI_OPTSTOOLS" "H")":LBL "SKIP" `#CAT_Tools` `#HEAD_Tools` `#MENU_GAME` `#MENU_GLOBAL`  \
--field="     $GUI_USEGAMEMODERUN!$DESC_USEGAMEMODERUN ('USEGAMEMODERUN')":CHK "${USEGAMEMODERUN/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEMANGOHUD!$DESC_USEMANGOHUD ('USEMANGOHUD')":CHK "${USEMANGOHUD/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEMANGOAPP!$DESC_USEMANGOAPP ('USEMANGOAPP')":CHK "${USEMANGOAPP/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_MAHUVAR!$DESC_MAHUVAR ('MAHUVAR')":CHK "${MAHUVAR/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_MAHUBIN!$DESC_MAHUBIN ('MAHUBIN')":FL "${MAHUBIN/#-/ -}" `#CAT_Tools` `#MENU_GAME` \
--field="     $GUI_MAHUARGS!$DESC_MAHUARGS ('MAHUARGS')":CBE "${MAHUARGS/#-/ -}!no_display!fps_limit=60,show_fps_limit!ram,vram!battery,battery_icon,gamepad_battery,gamepad_battery_icon!arch!gamemode!vsync!gl_vsync!vulkan_driver!wine" `#CAT_Tools` `#MENU_GAME` \
--field="     $GUI_MAHUDLSYM!$DESC_MAHUDLSYM ('MAHUDLSYM')":CHK "${MAHUDLSYM}" `#CAT_Tools` `#MENU_GAME` \
--field="     $GUI_LDPMAHU!$DESC_LDPMAHU ('LDPMAHU')":CHK "${LDPMAHU/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEMANGOHUDSTLCFG!$DESC_USEMANGOHUDSTLCFG ('USEMANGOHUDSTLCFG')":CHK "${USEMANGOHUDSTLCFG/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEGAMESCOPE!$DESC_USEGAMESCOPE ('USEGAMESCOPE')":CHK "${USEGAMESCOPE/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_GAMESCOPE_ARGS!$DESC_GAMESCOPE_ARGS ('GAMESCOPE_ARGS')" "${GAMESCOPE_ARGS/#-/ -}" `#CAT_Tools` `#MENU_GAME` \
--field="     $GUI_USEOBSCAP!$DESC_USEOBSCAP ('USEOBSCAP')":CHK "${USEOBSCAP/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEZINK!$DESC_USEZINK ('USEZINK')":CHK "${USEZINK/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEPRIMERUN!$DESC_USEPRIMERUN ('USEPRIMERUN')":CHK "${USEPRIMERUN/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_RUN_NYRNA!$DESC_RUN_NYRNA ('RUN_NYRNA')":CHK "${RUN_NYRNA/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_RUN_REPLAY!$DESC_RUN_REPLAY ('RUN_REPLAY')":CHK "${RUN_REPLAY/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEBOXTRON!$DESC_USEBOXTRON ('USEBOXTRON')":CHK "${USEBOXTRON/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_BOXTRONCMD!$DESC_BOXTRONCMD ('BOXTRONCMD')":FL "${BOXTRONCMD/#-/ -}" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_BOXTRONARGS!$DESC_BOXTRONARGS ('BOXTRONARGS')" "${BOXTRONARGS/#-/ -}" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_USEROBERTA!$DESC_USEROBERTA ('USEROBERTA')":CHK "${USEROBERTA/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_ROBERTACMD!$DESC_ROBERTACMD ('ROBERTACMD')":FL "${ROBERTACMD/#-/ -}" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_ROBERTAARGS!$DESC_ROBERTAARGS ('ROBERTAARGS')" "${ROBERTAARGS/#-/ -}" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_USELUXTORPEDA!$DESC_USELUXTORPEDA ('USELUXTORPEDA')":CHK "${USELUXTORPEDA/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_LUXTORPEDACMD!$DESC_LUXTORPEDACMD ('LUXTORPEDACMD')":FL "${LUXTORPEDACMD/#-/ -}" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_LUXTORPEDAARGS!$DESC_LUXTORPEDAARGS ('LUXTORPEDAARGS')" "${LUXTORPEDAARGS/#-/ -}" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_USE_WDIB!$DESC_USE_WDIB ('USE_WDIB')":CHK "${USE_WDIB/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_STLEDITOR!$DESC_STLEDITOR ('STLEDITOR')":FL "${STLEDITOR/#-/ -}" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_BROWSER!$DESC_BROWSER ('BROWSER')":FL "${BROWSER/#-/ -}" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_NOTY!$DESC_NOTY ('NOTY')":FL "${NOTY/#-/ -}" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_NOTYARGS!$DESC_NOTYARGS ('NOTYARGS')" "${NOTYARGS/#-/ -}" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_USENETMON!$DESC_USENETMON ('USENETMON')":CHK "${USENETMON/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_NETMON!$DESC_NETMON ('NETMON')":FL "${NETMON/#-/ -}" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_NETOPTS!$DESC_NETOPTS ('NETOPTS')" "${NETOPTS/#-/ -}" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_NETMONDIR!$DESC_NETMONDIR ('NETMONDIR')":DIR "${NETMONDIR/#-/ -}" `#CAT_Tools` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_BLOCKINTERNET!$DESC_BLOCKINTERNET ('BLOCKINTERNET')":CHK "${BLOCKINTERNET/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_STRACERUN!$DESC_STRACERUN ('STRACERUN')":CHK "${STRACERUN/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_STRACEDIR!$DESC_STRACEDIR ('STRACEDIR')":DIR "${STRACEDIR/#-/ -}" `#CAT_Tools` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_STRACEOPTS!$DESC_STRACEOPTS ('STRACEOPTS')" "${STRACEOPTS/#-/ -}" `#CAT_Tools` `#MENU_GAME` \
--field="     $GUI_AUTOCONTY!$DESC_AUTOCONTY ('AUTOCONTY')":CHK "${AUTOCONTY/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USECONTY!$DESC_USECONTY ('USECONTY')":CHK "${USECONTY/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_CUSTCONTY!$DESC_CUSTCONTY ('CUSTCONTY')":FL "${CUSTCONTY/#-/ -}" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_UPDATECONTY!$DESC_UPDATECONTY ('UPDATECONTY')":CHK "${UPDATECONTY/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_RUN_X64DBG!$DESC_RUN_X64DBG ('RUN_X64DBG')":CHK "${RUN_X64DBG/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_X64DBG_ATTACHONSTARTUP!$DESC_X64DBG_ATTACHONSTARTUP ('X64DBG_ATTACHONSTARTUP')":CHK "${X64DBG_ATTACHONSTARTUP/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_RUN_GDB!$DESC_RUN_GDB ('RUN_GDB')":CHK "${RUN_GDB/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_DPRSCOMPDATA!$DESC_DPRSCOMPDATA ('DPRSCOMPDATA')":DIR "${DPRSCOMPDATA/#-/ -}" `#CAT_Tools` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_USEDPRSPROTON!$DESC_USEDPRSPROTON ('USEDPRSPROTON')":CB "$(cleanDropDown "${USEDPRSPROTON/#-/ -}" "$PROTYADLIST")" `#CAT_Tools` `#MENU_GLOBAL` \
--field="     $GUI_DPRSUSEVDFSYMLINKS!$DESC_DPRSUSEVDFSYMLINKS ('DPRSUSEVDFSYMLINKS')":CHK "${DPRSUSEVDFSYMLINKS/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_DPRSPAUTOUP!$DESC_DPRSPAUTOUP ('DPRSPAUTOUP')":CHK "${DPRSPAUTOUP/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_RUN_DEPS!$DESC_RUN_DEPS ('RUN_DEPS')":CHK "${RUN_DEPS/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_DEPSAUTOUP!$DESC_DEPSAUTOUP ('DEPSAUTOUP')":CHK "${DEPSAUTOUP/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_USEPEV_PELDD!$DESC_USEPEV_PELDD ('USEPEV_PELDD')":CHK "${USEPEV_PELDD/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEPEV_PEPACK!$DESC_USEPEV_PEPACK ('USEPEV_PEPACK')":CHK "${USEPEV_PEPACK/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEPEV_PERES!$DESC_USEPEV_PERES ('USEPEV_PERES')":CHK "${USEPEV_PERES/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEPEV_PESCAN!$DESC_USEPEV_PESCAN ('USEPEV_PESCAN')":CHK "${USEPEV_PESCAN/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEPEV_PESEC!$DESC_USEPEV_PESEC ('USEPEV_PESEC')":CHK "${USEPEV_PESEC/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEPEV_PESTR!$DESC_USEPEV_PESTR ('USEPEV_PESTR')":CHK "${USEPEV_PESTR/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEPEV_READPE!$DESC_USEPEV_READPE ('USEPEV_READPE')":CHK "${USEPEV_READPE/#-/ -}" `#CAT_Tools` `#SUB_Checkbox` `#MENU_GAME` \
--field="$(spanFont "$GUI_OPTSURLS" "H")":LBL "SKIP" `#CAT_Urls` `#HEAD_Urls` `#MENU_URL` \
--field="     $GUI_PROJECTPAGE!$(strFix "$DESC_PROJECTPAGE" "$PROGNAME") ('PROJECTPAGE')" "${PROJECTPAGE/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_CP_PROTONTKG!$DESC_CP_PROTONTKG ('CP_PROTONTKG')" "${CP_PROTONTKG/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_CP_PROTONGE!$DESC_CP_PROTONGE ('CP_PROTONGE')" "${CP_PROTONGE/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_CP_PROTONSTL!$DESC_CP_PROTONSTL ('CP_PROTONSTL')" "${CP_PROTONSTL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_CW_KRON4EK!$DESC_CW_KRON4EK ('CW_KRON4EK')" "${CW_KRON4EK/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_CW_LUTRIS!$DESC_CW_LUTRIS ('CW_LUTRIS')" "${CW_LUTRIS/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_CW_WINEGE!$DESC_CW_WINEGE ('CW_WINEGE')" "${CW_WINEGE/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_DL_D3D47_64!$DESC_DL_D3D47_64 ('DL_D3D47_64')" "${DL_D3D47_64/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_DL_D3D47_32!$DESC_DL_D3D47_32 ('DL_D3D47_32')" "${DL_D3D47_32/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_RESHADEDLURL!$DESC_RESHADEDLURL ('RESHADEDLURL')" "${RESHADEDLURL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_VORTEXRELURL!$DESC_VORTEXRELURL ('VORTEXPROJURL')" "${VORTEXPROJURL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_DXVKURL!$DESC_DXVKURL ('DXVKURL')" "${DXVKURL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_XLIVEURL!$DESC_XLIVEURL ('XLIVEURL')" "${XLIVEURL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_STASSURL!$DESC_STASSURL ('STASSURL')" "${STASSURL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_WINETRICKSURL!$DESC_WINETRICKSURL ('WINETRICKSURL')" "${WINETRICKSURL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_X64DBGURL!$DESC_X64DBGURL ('X64DBGURL')" "${X64DBGURL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_STEAMGRIDDBAPI!$DESC_STEAMGRIDDBAPI ('BASESTEAMGRIDDBAPI')" "${BASESTEAMGRIDDBAPI/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_CONTYRELURL!$DESC_CONTYRELURL ('CONTYRELURL')" "${CONTYRELURL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_MO2DLURL!$DESC_MO2DLURL ('MO2PROJURL')" "${MO2PROJURL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_HMMDLURL!$DESC_HMMDLURL ('HMMPROJURL')" "${HMMPROJURL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_SPEKPROJURL!$DESC_SPEKPROJURL ('SPEKPROJURL')" "${SPEKPROJURL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_DPRSRELURL!$DESC_DPRSRELURL ('DPRSRELURL')" "${DPRSRELURL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="     $GUI_DEPURL!$DESC_DEPURL ('DEPURL')" "${DEPURL/#-/ -}" `#CAT_Urls` `#MENU_URL` \
--field="$(spanFont "$GUI_OPTSVR" "H")":LBL "SKIP" `#CAT_VR` `#HEAD_VR` `#MENU_GAME` `#MENU_GLOBAL`  \
--field="     $GUI_SBSVRGEOELF!$DESC_SBSVRGEOELF ('SBSVRGEOELF')":CHK "${SBSVRGEOELF/#-/ -}" `#CAT_VR` `#SUB_Checkbox` `#SUB_ReShade` `#MENU_GAME` \
--field="     $GUI_SBSVRRS!$DESC_SBSVRRS ('SBSVRRS')":CHK "${SBSVRRS/#-/ -}" `#CAT_VR` `#SUB_Checkbox` `#SUB_ReShade` `#MENU_GAME` \
--field="     $GUI_RUNSBSVR!$DESC_RUNSBSVR ('RUNSBSVR')":CHK "${RUNSBSVR/#-/ -}" `#CAT_VR` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_SBSZOOM!$DESC_SBSZOOM ('SBSZOOM')" "${SBSZOOM/#-/ -}" `#CAT_VR` `#MENU_GAME` \
--field="     $GUI_MINVRWINH!$DESC_MINVRWINH ('MINVRWINH')":NUM "${MINVRWINH/#-/ -}" `#CAT_VR` `#MENU_GAME` \
--field="     $GUI_VRVIDEOPLAYER!$DESC_VRVIDEOPLAYER ('VRVIDEOPLAYER')":FL "${VRVIDEOPLAYER/#-/ -}" `#CAT_VR` `#MENU_GLOBAL` \
--field="     $GUI_VRVIDEOPLAYERARGS!$DESC_VRVIDEOPLAYERARGS ('VRVIDEOPLAYERARGS')" "${VRVIDEOPLAYERARGS/#-/ -}" `#CAT_VR` `#MENU_GAME` \
--field="     $GUI_WAITFORTHISPID!$DESC_WAITFORTHISPID ('WAITFORTHISPID')" "${WAITFORTHISPID/#-/ -}" `#CAT_VR` `#MENU_GAME` \
--field="     $GUI_UUUSEVR!$DESC_UUUSEVR ('UUUSEVR')":CHK "${UUUSEVR/#-/ -}" `#CAT_VR` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEOPENVRFSR!$DESC_USEOPENVRFSR ('USEOPENVRFSR')":CHK "${USEOPENVRFSR/#-/ -}" `#CAT_VR` `#SUB_Checkbox` `#MENU_GAME` \
--field="$(spanFont "$GUI_OPTSVORTEX" "H")":LBL "SKIP" `#CAT_Vortex` `#HEAD_Vortex` `#MENU_GAME` `#MENU_GLOBAL` \
--field="     $GUI_USEVORTEX!$DESC_USEVORTEX ('USEVORTEX')":CHK "${USEVORTEX/#-/ -}" `#CAT_Vortex` `#MENU_GAME` \
--field="     $GUI_VORTEXDEVICESCALEFACTOR!$DESC_VORTEXDEVICESCALEFACTOR ('VORTEXDEVICESCALEFACTOR')" "${VORTEXDEVICESCALEFACTOR/#-/ -}" `#CAT_Vortex` `#MENU_GLOBAL` \
--field="     $GUI_WAITVORTEX!$DESC_WAITVORTEX ('WAITVORTEX')":NUM "${WAITVORTEX/#-/ -}" `#CAT_Vortex` `#MENU_GAME` \
--field="     $GUI_RUN_VORTEX_WINETRICKS!$DESC_RUN_VORTEX_WINETRICKS ('RUN_VORTEX_WINETRICKS')":CHK "${RUN_VORTEX_WINETRICKS/#-/ -}" `#CAT_Vortex` `#MENU_GAME` \
--field="     $GUI_RUN_VORTEX_WINECFG!$DESC_RUN_VORTEX_WINECFG ('RUN_VORTEX_WINECFG')":CHK "${RUN_VORTEX_WINECFG/#-/ -}" `#CAT_Vortex` `#MENU_GAME` \
--field="     $GUI_USEVORTEXPRERELEASE!$DESC_USEVORTEXPRERELEASE ('USEVORTEXPRERELEASE')":CHK "${USEVORTEXPRERELEASE/#-/ -}" `#CAT_Vortex` `#SUB_Checkbox` `#MENU_GLOBAL`  \
--field="     $GUI_DISABLEVORTEXAUTOUPDATE!$DESC_DISABLEVORTEXAUTOUPDATE ('DISABLEVORTEXAUTOUPDATE')":CHK "${DISABLEVORTEXAUTOUPDATE/#-/ -}" `#CAT_Vortex` `#SUB_Checkbox` `#MENU_GLOBAL`  \
--field="     $GUI_VORTEXUSESLR!$DESC_VORTEXUSESLR ('VORTEXUSESLR')":CHK "${VORTEXUSESLR#-/ -}" `#CAT_Vortex` `#SUB_Checkbox` `#MENU_GLOBAL`  \
--field="     $GUI_VORTEXDOWNLOADPATH!$DESC_VORTEXDOWNLOADPATH ('VORTEXDOWNLOADPATH')":DIR "${VORTEXDOWNLOADPATH/#-/ -}" `#CAT_Vortex` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_VORTEXCOMPDATA!$DESC_VORTEXCOMPDATA ('VORTEXCOMPDATA')":DIR "${VORTEXCOMPDATA/#-/ -}" `#CAT_Vortex` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_USEVORTEXPROTON!$DESC_USEVORTEXPROTON ('USEVORTEXPROTON')":CB "$(cleanDropDown "${USEVORTEXPROTON/#-/ -}" "$PROTYADLIST")" `#CAT_Vortex` `#MENU_GLOBAL` \
--field="     $GUI_USEVORTEXCUSTOMVER!$DESC_USEVORTEXCUSTOMVER ('USEVORTEXCUSTOMVER')":CHK "${USEVORTEXCUSTOMVER#-/ -}" `#CAT_Vortex` `#SUB_checkbox` `#MENU_GLOBAL` \
--field="     $GUI_VORTEXCUSTOMVER!$DESC_VORTEXCUSTOMVER ('VORTEXCUSTOMVER')":CBE "$( cleanDropDown "${VORTEXCUSTOMVER/#-/ -}" "v1.8.0!v1.7.0!v1.6.0!$NON" )" `#CAT_Vortex` `#SUB_checkbox` `#MENU_GLOBAL` \
--field="     $GUI_DISABLE_AUTOSTAGES!$(strFix "$DESC_DISABLE_AUTOSTAGES" "$PROGNAME") ('DISABLE_AUTOSTAGES')":CHK "${DISABLE_AUTOSTAGES/#-/ -}" `#CAT_Vortex` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_VORTEXUSESLRPOSTINSTALL!$DESC_VORTEXUSESLRPOSTINSTALL ('VORTEXUSESLRPOSTINSTALL')":CHK "${VORTEXUSESLRPOSTINSTALL#-/ -}" `#CAT_Vortex` `#SUB_Checkbox` `#MENU_GLOBAL`  \
--field="$(spanFont "$GUI_OPTSWINE" "H")":LBL "SKIP" `#CAT_Wine` `#HEAD_Wine` `#MENU_GAME` `#MENU_GLOBAL` \
--field="     $GUI_USEWINE!$DESC_USEWINE ('USEWINE')":CHK "${USEWINE/#-/ -}" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_WINEVERSION!$DESC_WINEVERSION ('WINEVERSION')":CBE "$(cleanDropDown "${WINEVERSION/#-/ -}" "$WINEYADLIST")" `#CAT_Wine` `#MENU_GAME` \
--field="     $GUI_WINEDEFAULT!$DESC_WINEDEFAULT ('WINEDEFAULT')":CBE "$(cleanDropDown "${WINEDEFAULT/#-/ -}" "$WINEYADLIST")" `#CAT_Wine` `#MENU_GAME` \
--field="     $GUI_RUN_WINETRICKS!$DESC_RUN_WINETRICKS ('RUN_WINETRICKS')":CHK "${RUN_WINETRICKS/#-/ -}" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_DLWINETRICKS!$DESC_DLWINETRICKS ('DLWINETRICKS')":CHK "${DLWINETRICKS/#-/ -}" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_WINETRICKSPAKS!$DESC_WINETRICKSPAKS ('WINETRICKSPAKS')":CBE "$(cleanDropDown "${WINETRICKSPAKS/#-/ -}" "$SOMEPOPULARWINEPAKS")" `#CAT_Wine` `#MENU_GAME` \
--field="     $GUI_RUN_WINECFG!$DESC_RUN_WINECFG ('RUN_WINECFG')":CHK "${RUN_WINECFG/#-/ -}" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_REGEDIT!$DESC_REGEDIT ('REGEDIT')":CHK "${REGEDIT/#-/ -}" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_VIRTUALDESKTOP!$DESC_VIRTUALDESKTOP ('VIRTUALDESKTOP')":CHK "${VIRTUALDESKTOP/#-/ -}" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GAME`  \
--field="     $GUI_VDRES!$DESC_VDRES ('VDRES')":CBE "$(cleanDropDown "${VDRES/#-/ -}" "$(printf "%s\n" "$(listScreenRes | tr '\n' '!')")$NON")" `#CAT_Wine` `#MENU_GAME` \
--field="     $GUI_USEWICO!$DESC_USEWICO ('USEWICO')":CHK "${USEWICO/#-/ -}" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_STLWINEDEBUG!$DESC_STLWINEDEBUG ('STLWINEDEBUG')":CBE "$(cleanDropDown "${STLWINEDEBUG/#-/ -}" "$SOMEWINEDEBUGOPTIONS")" `#CAT_Wine` `#MENU_GAME` \
--field="     $GUI_STLWINEDLLOVERRIDES!$DESC_STLWINEDLLOVERRIDES ('STLWINEDLLOVERRIDES')":CBE "$(cleanDropDown "${STLWINEDLLOVERRIDES/#-/ -}" "$SOMEWINEDLLOVERRIDES")" `#CAT_Wine` `#MENU_GAME` \
--field="     $GUI_WINE_FULLSCREEN_INTEGER_SCALING!$DESC_WINE_FULLSCREEN_INTEGER_SCALING ('WINE_FULLSCREEN_INTEGER_SCALING')":CHK "${WINE_FULLSCREEN_INTEGER_SCALING/#-/ -}" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_WINE_FULLSCREEN_FSR!$DESC_WINE_FULLSCREEN_FSR ('WINE_FULLSCREEN_FSR')":CHK "${WINE_FULLSCREEN_FSR/#-/ -}" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_WINE_FULLSCREEN_FSR_STRENGTH!$DESC_WINE_FULLSCREEN_FSR_STRENGTH ('WINE_FULLSCREEN_FSR_STRENGTH')":NUM "${WINE_FULLSCREEN_FSR_STRENGTH/#-/ -}!0..5" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_WINE_FULLSCREEN_FSR_MODE!$DESC_WINE_FULLSCREEN_FSR_MODE ('WINE_FULLSCREEN_FSR_MODE')":CBE "$(cleanDropDown "${WINE_FULLSCREEN_FSR_MODE/#-/ -}" "none!performance!balanced!quality!ultra")" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_WINE_FULLSCREEN_FSR_CUSTOM_MODE!$DESC_WINE_FULLSCREEN_FSR_CUSTOM_MODE ('WINE_FULLSCREEN_FSR_CUSTOM_MODE')":CBE "$(cleanDropDown "${WINE_FULLSCREEN_FSR_CUSTOM_MODE/#-/ -}" "none!$(printf "%s!" "${WINE_FSR_CUSTOM_RESOLUTIONS[@]}" | sed 's/!$//')")" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_WINEDLDIR!$DESC_WINEDLDIR ('WINEDLDIR')":DIR "${WINEDLDIR/#-/ -}" `#CAT_Wine` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_WINEEXTDIR!$DESC_WINEEXTDIR ('WINEEXTDIR')":DIR "${WINEEXTDIR/#-/ -}" `#CAT_Wine` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_WINE_LOG_DIR!$DESC_WINE_LOG_DIR ('WINE_LOG_DIR')":DIR "${WINE_LOG_DIR/#-/ -}" `#CAT_Wine` `#SUB_Directories` `#MENU_GAME` \
--field="     $GUI_ONLYWICO!$DESC_ONLYWICO ('ONLYWICO')":CHK "${ONLYWICO/#-/ -}" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_USEGLOBALWINEDPI!$DESC_USEGLOBALWINEDPI ('USEGLOBALWINEDPI')":CHK "${USEGLOBALWINEDPI/#-/ -}" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_GLOBALWINEDPI!$DESC_GLOBALWINEDPI ('GLOBALWINEDPI')":CBE "$( cleanDropDown "${GLOBALWINEDPI/#-/ -}" "$WINEDPIVALUES" )" `#CAT_Wine` `#MENU_GLOBAL` \
--field="     $GUI_USEPERGAMEWINEDPI!$DESC_USEPERGAMEWINEDPI ('USEPERGAMEWINEDPI')":CHK "${USEPERGAMEWINEDPI/#-/ -}" `#CAT_Wine` `#SUB_Checkbox` `#MENU_GAME` \
--field="     $GUI_PERGAMEWINEDPI!$DESC_PERGAMEWINEDPI ('PERGAMEWINEDPI')":CBE "$( cleanDropDown "${PERGAMEWINEDPI/#-/ -}" "$WINEDPIVALUES" )" `#CAT_Wine` `#MENU_GAME` \
--field="$(spanFont "$GUI_OPTSMO2" "H")":LBL "SKIP" `#CAT_MO2` `#HEAD_Vortex` `#MENU_GAME` `#MENU_GLOBAL` \
--field="     $GUI_MO2COMPDATA!$DESC_MO2COMPDATA ('MO2COMPDATA')":DIR "${MO2COMPDATA/#-/ -}" `#CAT_MO2` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_USEMO2PROTON!$DESC_USEMO2PROTON ('USEMO2PROTON')":CB "$(cleanDropDown "${USEMO2PROTON/#-/ -}" "$PROTYADLIST")" `#CAT_MO2` `#MENU_GLOBAL` \
--field="     $GUI_USEMO2CUSTOMINSTALLER!$DESC_USEMO2CUSTOMINSTALLER ('USEMO2CUSTOMINSTALLER')":CHK "${USEMO2CUSTOMINSTALLER/#- -}" `#CAT_MO2` `#SUB_Checkbox` `#MENU_GLOBAL` \
--field="     $GUI_MO2CUSTOMINSTALLER!$DESC_MO2CUSTOMINSTALLER ('MO2CUSTOMINSTALLER')":FL "${MO2CUSTOMINSTALLER/#-/ -}" `#CAT_MO2` `#SUB_Directories` `#MENU_GLOBAL` \
--field="     $GUI_MO2MODE!$DESC_MO2MODE ('MO2MODE')":CB "$(cleanDropDown "${MO2MODE/#-/ -}" "disabled!gui!silent")" `#CAT_MO2` `#MENU_GAME` \
--field="     $GUI_WAITMO2!$DESC_WAITMO2 ('WAITMO2')":NUM "${WAITMO2/#-/ -}" `#CAT_MO2` `#MENU_GAME` \
--field="     $GUI_MO2SILENTMODEEXEOVERRIDE!$DESC_MO2SILENTMODEEXEOVERRIDE ('MO2SILENTMODEEXEOVERRIDE')":CBE "$(cleanDropDown "${MO2SILENTMODEEXEOVERRIDE/#-/ -}" "$MO2SILENTMODEEXEPROFILES" )" `#CAT_MO2` `#MENU_GAME` \
#ENDSETENTRIES
}

function rmCfgTemp {
	if [ -n "$MKCFG" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Deleting and resetting MKCFG '$MKCFG' - triggered from '$1'"
		rm "$MKCFG" 2>/dev/null
		unset "MKCFG"
	fi
}

function mkCfgTemp {
	rmCfgTemp "${FUNCNAME[0]}"
	MKCFG="$(mktemp "$STLSHM/menu.XXXXXXXX")"
}

function prepareMenu {
	writelog "INFO" "${FUNCNAME[0]} - Opening Menu"
	GOBACK=1

	loadCfg "$STLDEFGLOBALCFG"
	loadCfg "$STLGAMECFG"
	loadCfg "$STLURLCFG"

	resetAID "$1"
	createGameCfg
	setGN "$1"

	if [ -z "$GN" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - No game name found for '$AID' - this should not happen"
		exit
	fi
	createDropdownLists
	getAvailableCfgs
}

function openGameMenu {
	prepareMenu "$@"
	if [ -z "$2" ]; then
		FUNC="$NON"
	else
		FUNC="$2"
	fi
	PARGUI="${FUNCNAME[0]}"
	openMenu "$AID" "$FUNC" "$STLGAMECFG" "0" "$GAMEMENU" "$LGAM" "$SHOWPIC" "EMPTY" "EMPTY" "$(strFix "$GUI_SETHEAD1" "$SGNAID")" "MENU_GAME"
}

function openGameDefaultMenu {
	prepareMenu "$@"
	if [ -z "$2" ]; then
		FUNC="$NON"
	else
		FUNC="$2"
	fi
	PARGUI="${FUNCNAME[0]}"
	openMenu "$AID" "$FUNC" "$STLDEFGAMECFG" "0" "$GAMETEMPMENU" "$LGATM" "$NOICON" "EMPTY" "EMPTY" "$GUI_SETHEAD2" "MENU_GAME"
}

function openGlobalMenu {
	prepareMenu "$@"
	autoBumpReShade
	if [ -z "$2" ]; then
		FUNC="$NON"
	else
		FUNC="$2"
	fi
	PARGUI="${FUNCNAME[0]}"
	openMenu "$AID" "$FUNC" "$STLDEFGLOBALCFG" "0" "$GLOBALMENU" "$LGLM" "$NOICON" "EMPTY" "EMPTY" "$GUI_SETHEAD3" "MENU_GLOBAL"
}

function startSteamGame {
	if [ "$ISGAME" -ge 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Starting game"
	else
		if [ "$ISGAME" -ne 1 ] && [ "$AID" != "$PLACEHOLDERAID" ]; then
			STEAMARGS=(-applaunch "$AID")
			writelog "INFO" "${FUNCNAME[0]} - -------- starting game $AID in steam --------" "E"
			FINALSTARTCMD=("$STEAM" "${STEAMARGS[@]}")
			startGame "${FINALSTARTCMD[@]}" 2>/dev/null >/dev/null &
		fi
	fi
}

function openCustMenu {
	MYCAT="$3"
	MYMENU="$4"
	MYFUNC="$5"

	prepareMenu "$@"

	createGameCfg
	createDropdownLists

	TITLE="${PROGNAME}-${MYCAT}"
	pollWinRes "$TITLE" 1

	setShowPic
	cp "$SHOWPIC" "$FAVPIC"

	mkCfgTemp "${FUNCNAME[0]}"

	if [ "$2" == "$NON" ]; then
		BUT0="$BUT_EXIT"
		GOTO="Exit"
	else
		BUT0="$BUT_BACK"
		GOTO="$2"
	fi

	BUT2="$BUT_MAINMENU"
	BUT4="$BUT_RELOAD"
	BUT6="$BUT_SAVERELOAD"
	BUT8="$BUT_SAVEPLAY"
	BUT10="$BUT_PLAY"

	MYTMPL="${MYMENU}-${TMPL}"

	if [ "$MYCAT" == "Favorites" ]; then
		MYTEXT="$(strFix "$GUI_FAVORITESMENU" "$SGNAID")"
	else
		MYTEXT="$(strFix "$GUI_CATMENU" "$SGNAID" "$MYCAT")"
		if [ ! -f "$MYTMPL" ]; then
			grep "CAT_${MYCAT}" "$STLRAWENTRIES" > "$MYTMPL"
		fi
	fi

	# shellcheck disable=SC2028 # doesn't like the newline seperator, but it is valid
	{
		echo "function $MYFUNC {"
		echo "\"$YAD\" --columns=\"$COLCOUNT\" --f1-action=\"$F1ACTIONCG\" --text=\"$(spanFont "$MYTEXT" "H")\" \\"
		echo "--title=\"$TITLE\" --image \"$FAVPIC\" --image-on-top --window-icon=\"$STLICON\" --center \"$WINDECO\" --form --separator=\"\\n\" --quoted-output \\"
		echo "--button=\"$BUT0\":0 --button=\"$BUT2\":2 --button=\"$BUT4\":4 --button=\"$BUT6\":6 --button=\"$BUT8\":8 --button=\"$BUT10\":10 $GEOM \\"
		cat "$MYTMPL"
		echo "--scroll"
		echo "}"
	} >"$MYMENU"

	source "$MYMENU"

	if [ -z "$MYFUNC" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Seems like some arguments are missing - got '$*'"
	else
		"$MYFUNC" > "$MKCFG"
		case $? in
		0)  {
				clickInfo "${FUNCNAME[0]}" "$?" "$BUT0" "Category '$MYCAT' Menu" "$GOTO"
				if [ "$GOTO" == "Exit" ]; then
					GOBACK=0
					closeSTL " ######### STOP EARLY $PROGNAME $PROGVERS #########"
					exit
				else
					rmCfgTemp "${FUNCNAME[0]}"
				fi
			}
		;;
		2) 	{
				clickInfo "${FUNCNAME[0]}" "$?" "$BUT2" "Category '$MYCAT' Menu" "$SETMENU"
				MainMenu "$AID" "${FUNCNAME[0]}"
			}
		;;
		4) 	{
				clickInfo "${FUNCNAME[0]}" "$?" "$BUT4" "Category '$MYCAT' Menu" "restart Menu"
				GOBACK=0
				writelog "INFO" "${FUNCNAME[0]} - Reload Configs and restart Category '$MYCAT' Menu"
				if [ "$SAVESETSIZE" -eq 1 ] ; then	sleep 1;	fi
				"${FUNCNAME[0]}" "$@"
			}
		;;
		6) {
				clickInfo "${FUNCNAME[0]}" "$?" "$BUT6" "Category '$MYCAT' Menu" "restart Menu"
				GOBACK=0
				saveMenuEntries "$MYMENU"
				if [ "$SAVESETSIZE" -eq 1 ] ; then	sleep 1;	fi

				if [ "$MYCAT" == "$GCD" ];then
					setGameCfgDiffs
				fi
				"${FUNCNAME[0]}" "$@"
			}
		;;
		8) 	{
				clickInfo "${FUNCNAME[0]}" "$?" "$BUT8" "Category '$MYCAT' Menu" "Game Start"
				GOBACK=0
				saveMenuEntries "$MYMENU"
				startSteamGame
			}
		;;
		10)	{
				clickInfo "${FUNCNAME[0]}" "$?" "$BUT10" "Category '$MYCAT' Menu" "Game Start"
				GOBACK=0
				startSteamGame
			}
		;;
		esac
		rmCfgTemp "${FUNCNAME[0]}"
		goBackToPrevFunction "${FUNCNAME[0]}" "$2" "$GOBACK"
	fi
}

function setGuiSortOrder {
	if [ ! -f "$STLMENUSORTCFG" ]; then
		writelog "INFO" "${FUNCNAME[0]} - '$STLMENUSORTCFG' missing"
		getCatsFromCode > "$STLMENUSORTCFG" 2>/dev/null
	fi
	export CURWIKI="$PPW/Gui-Sort-Order"
	TITLE="${PROGNAME}-SortCategories"
	pollWinRes "$TITLE"

	NEWSORT="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --center --on-top "$WINDECO" \
		--list --editable --column "Category" --separator="" --print-all \
		--title="$TITLE" --text="$(spanFont "$GUI_SORTCAT" "H")" "$GEOM" < "$STLMENUSORTCFG")"
	case $? in
		0) 	{
				writelog "INFO" "${FUNCNAME[0]} - Clicked OK - Saving new sortorder into '$STLMENUSORTCFG'"
				echo "$NEWSORT" > "$STLMENUSORTCFG"
			}
		;;
		1)	{
				writelog "INFO" "${FUNCNAME[0]} - Clicked Cancel"
			}
		;;
	esac
}

function setGuiFavoritesSelection {
	export CURWIKI="$PPW/Favorites-Menu"
	TITLE="${PROGNAME}-FavoritesSelection"
	pollWinRes "$TITLE"

	setShowPic

	FAVENTRIES="$(favoritesMenuEntries | \
	"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center "$WINDECO" --list --checklist \
	--column="$GUI_ADD" --column="$GUI_DESC" --column="$GUI_VAR" --column="LongDesc" --separator="\n" --print-column="3" --tooltip-column 4 --hide-column 4 \
	--text="$(spanFont "$GUI_FAVORITESSEL" "H")" --title="$TITLE" "$GEOM")"
	case $? in
		0)  {
				if [ -n "$FAVENTRIES" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Updating '$STLFAVMENUCFG' with selected Favorites"
					echo "${FAVENTRIES[@]}" > "$STLFAVMENUCFG"
					sort -u "$STLFAVMENUCFG" -o "$STLFAVMENUCFG"
					rm "${FAVMENU}-${TMPL}" 2>/dev/null
				else
					writelog "INFO" "${FUNCNAME[0]} - Nothing selected"
				fi
			}
		;;
		1)  {
				writelog "INFO" "${FUNCNAME[0]} - Selected CANCEL - creating empty '$STLFAVMENUCFG' to skip asking again"
				touch "$STLFAVMENUCFG"
			}
		;;
	esac

	goBackToPrevFunction "${FUNCNAME[0]}" "$2"
}

function setGuiBlockSelection {
	export CURWIKI="$PPW/Gui-Hide-Categories"
	TITLE="${PROGNAME}-EntryBlockSelection"
	pollWinRes "$TITLE"

	BLENTRIES="$(createBlockYadList | \
	"$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --center --list --checklist \
	--column="$GUI_HIDE" --column="$GUI_DESC" --separator=" " --print-column="2" \
	--text="$(spanFont "$GUI_BLOCKSEL" "H")" --title="$TITLE" "$GEOM")"
	case $? in
		0)  {
				writelog "INFO" "${FUNCNAME[0]} - Updating '$STLMENUBLOCKCFG' with selected blocks"
				while read -r blentry; do
					if grep -q "$blentry" <<< "$(printf '%s\n' "$BLENTRIES")"; then
						updateConfigEntry "$blentry" "TRUE" "$STLMENUBLOCKCFG"
					else
						updateConfigEntry "$blentry" "FALSE" "$STLMENUBLOCKCFG"
					fi
				done <<< "$(grep -v "^#" "$STLMENUBLOCKCFG" | cut -d '=' -f1)"

				checkEntryBlocklist
			}
		;;
		1)  writelog "INFO" "${FUNCNAME[0]} - Selected CANCEL"
		;;
	esac

	goBackToPrevFunction "${FUNCNAME[0]}" "$2"
}

function setGameCfgDiffs {
	writelog "INFO" "${FUNCNAME[0]} - Getting changes between game config and default"
	CHTEMP="$STLSHM/${AID}_cfgchanges.txt"
	comm -13 <(grep -v "^#" "$STLDEFGAMECFG" | grep "=" | sort -u) <(grep -v "^#" "$STLGAMECFG" | grep "=" | sort -u) > "$CHTEMP"
	rm "$MTEMP/$GCD" "$MTEMP/$GCD-${TMPL}" 2>/dev/null

	while read -r change; do
		GVAR="${change%=*}"
		TVAL="$(grep "^${GVAR}=" "$STLDEFGAMECFG" | cut -d '=' -f2)"
		TVALO="${TVAL//\"/}"
		if [ -n "$TVAL" ];then
			FRLI="$(sed -n "/^#STARTSETENTRIES/,/^#ENDSETENTRIES/p;/^#ENDSETENTRIES/q" "$0" | grep "'$GVAR'")"
			FRL1="${FRLI//\')/\') - $GUI_TEMPVAL $TVALO}"
			echo "$FRL1" >> "$MTEMP/$GCD-${TMPL}"
		fi
	done < "$CHTEMP"
	sed '/^$/d' -i "$MTEMP/$GCD-${TMPL}"
}

function setGuiCategoryMenuSel {
	export CURWIKI="$PPW/Category-Menus"
	TITLE="${PROGNAME}-CategorySelection"
	pollWinRes "$TITLE"

	setShowPic

	CATDD="$(cleanDropDown "${GCD}" "$(getCatsFromCode | tr '\n' '!' | sed "s:^!::" | sed "s:!$::")")"

	SELECTCAT="$("$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center "$WINDECO" --form --separator="\n" \
	--text="$(spanFont "$(strFix "$GUI_CATSEL" "$PROGNAME")" "H")" \
	--title="$TITLE" --field=" $GUI_CAT!$(strFix "$GUI_CATSEL" "$PROGNAME")":CB "$CATDD" "$GEOM")"
	case $? in
		0)  {
				if [ "$SELECTCAT" == "$GCD" ]; then
					setGameCfgDiffs
				fi

				writelog "INFO" "${FUNCNAME[0]} - Selected '$SELECTCAT' - Opening Category Menu"
				openCustMenu "$AID" "${FUNCNAME[0]}" "$SELECTCAT" "$MTEMP/$SELECTCAT" "$LCM"
			}
		;;
		1)  {
				writelog "INFO" "${FUNCNAME[0]} - Nothing selected - going to the $SETMENU"
				MainMenu "$AID" "${FUNCNAME[0]}"
			}
		;;
	esac
}

function clickInfo {
	writelog "INFO" "$1 - Clicked '$2' - '$3'"
	writelog "INFO" "$1 - exiting '$4' and opening '$5'"
}

function getLaPl {
	if [ -f "$PLAYTIMELOGDIR/$GN.log" ]; then
		echo "<b>$GUI_LASTPLAYED</b> $(tail -n1 "$PLAYTIMELOGDIR/${GN}.log")"
	fi
}

function fixShowGnAid {
	local PRETTYGAMENAME

    # Temp workaround to fix some games not launching because of locale bugs, particularly on Steam Deck
    # Not limited to Steam Deck since this could prevent future bug reports
    # May cause other bugs so keep an eye on this, but probably this is broken because of KDE not correctly setting all locales? Seems to affect KDE on Arch primarily
    if [ -n "$LANG" ]; then
        export LC_ALL="$LANG"  # $LANG should always be defined, unless locales are VERY broken
    fi

	# Will use "real" game name if it exists, e.g. "NieR:Automata" instead of NieRAutomata
	PRETTYGAMENAME="$( getTitleFromID "$AID" "1" )"
	if [ -n "$PRETTYGAMENAME" ]; then
		SGNAID="$PRETTYGAMENAME ($AID)"
		SGNAID="${SGNAID//&/+}"
	elif [ -n "$GN" ]; then
		SGNAID="$GN ($AID)"
		SGNAID="${SGNAID//&/+}"
	else
		SGNAID="$PLACEHOLDERGN ($PLACEHOLDERAID)"
	fi
}

function fixShow {
	echo "${1//&/+}"
}

## This function exists because previously, ReShade and ReShade+SpecialK were thought to need separate directories
## This is not the case; ReShade DLLs still go into the game folder with SpecialK, but are unnnamed, so both have the same SHADDESTDIR
function setShadDestDir {
	autoCollectionSettings

	setFullGameExePath "SHADDESTDIR"
}

function refreshProtList {
	if [ -f "$PROTBUMPTEMP" ]; then
		rm "$PROTONCSV" 2>/dev/null
		unset ProtonCSV
		rm "$PROTBUMPTEMP" 2>/dev/null
		createProtonList X
	fi
}

function MainMenu {
	writelog "INFO" "${FUNCNAME[0]} - Preparing to load Main Menu"

	createDLReShadeList
	createMO2SilentModeExeProfilesList
	prepareMenu "$@"

	setShowPic

	setHuyList
	setOPCustPath

	fixCustomMeta "$CUMETA/$AID.conf" # will be removed again later
	loadCfg "$CUMETA/$AID.conf" X
	loadCfg "$GEMETA/$AID.conf" X

	export CURWIKI="$PPW/Main-Menu"
	TITLE="${PROGNAME}-MainMenu"
	if [ "$ONSTEAMDECK" -eq 1 ]; then
		pollWinRes "$TITLE" 2
	else
		pollWinRes "$TITLE" 4
	fi
	fixShowGnAid

	if [ -z "$STEAMDECKCOMPATRATING" ]; then
		prepareSteamDeckCompatInfo  # Only fetches data once, we might already have it from askSettings so don't fetch again - Minor optimisation
	fi

	LAPL="$(getLaPl)"
	SETHEAD="$(spanFont "${PROGNAME} (${PROGNAME,,}) - ${PROGVERS} \n \n$SGNAID" "H")"

	TT_OPENURL="Help Url Gui"

	# Last played date/time with STL
	if [ -n "$LAPL" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Game Info Gotten!"
		SETHEAD="${SETHEAD}\n<i>$LAPL</i>"
	fi

	# Game config file info
	# TODO hide if no gamecfgs - Maybe this check needs an overhaul?
	SETHEAD="${SETHEAD}\n<i>(${#CfgFiles[@]} $GUI_EDITABLECFGS)\n($GUI_EDITABLEGAMECFGS)</i>"

	# Show Steam Deck compatibility on Main Menu when in Steam Deck Desktop Mode
	if [ "$SMALLDESK" -eq 1 ]; then
		SETHEAD="$SETHEAD \n<b>${GUI_SDCR}:</b> $STEAMDECKCOMPATRATING"
	fi

	if [ "$SMALLDESK" -eq 0 ]; then
		if [ -n "$DEVELOPER" ]; then
			SETHEAD="$SETHEAD \n\n<b>${GUI_DEV}:</b> $DEVELOPER"
		fi

		if [ -n "$PUBLISHER" ]; then
			SETHEAD="$SETHEAD \n<b>${GUI_PUB}:</b> $PUBLISHER"
		fi

		prepareProtonDBRating
		getGameFiles "$AID"
		setGameFilesArray
		TT_GAFI="$( printf "<u>%s</u>" "${GUI_GAFI}" )"
		TT_GAFI="${TT_GAFI}$(for i in "${!GamFiles[@]}"; do printf "\n<b>%s</b>: %s" "${GamDesc[$i]}" "${GamFiles[$i]}"; done)"

		# Only shown on the Game Files tooltip
		if [ -n "$METACRITIC_SCORE" ] && ! grep -q "${GUI_MCS}" "$PDBRAINFO"; then
			echo "<b>${GUI_MCS}:</b> $METACRITIC_SCORE" >> "$PDBRAINFO"
		fi

		if [ -n "$OSLIST" ] && ! grep -q "${GUI_OSL}" "$PDBRAINFO"; then
			echo "<b>${GUI_OSL}</b>: $OSLIST" >> "$PDBRAINFO"
		fi

		if [ -f "$PDBRAINFO" ] && grep -q "[0-9]" "$PDBRAINFO" ;then
			SETHEAD="$SETHEAD \n$(cat "$PDBRASINF")"
			TT_OPENURL="$(cat "$PDBRAINFO")"
		fi

		if [ -n "$STEAMDECKCOMPATRATING" ]; then
			SETHEAD="$SETHEAD \n<b>${GUI_SDCR}:</b> $STEAMDECKCOMPATRATING"
		fi

		if [ -L "$GPFX/$DRCU/$STUS" ]; then
			SETHEAD="$SETHEAD \n<b>${GUI_SSU}:</b> symlink to $(readlink "$GPFX/$DRCU/$STUS")"
		fi

		if [ "$ISORIGIN" -eq 1 ]; then
			SETHEAD="$SETHEAD \n<b>${GUI_SGW}:</b> Origin interfering"
		else
			if [ -n "$GAMEWINDOW" ] && [ "$GAMEWINDOW" != "$NON" ]; then
				SETHEAD="$SETHEAD \n<b>${GUI_SGW}:</b> $(fixShow "$GAMEWINDOW")"
			fi
		fi

		# Fetch game exe from metadata files if not defined yet
		if [ -z "$GAMEEXE" ]; then
			GAMEEXE="$( basename "$( getGameExe "$AID" "1" )" )"
		fi
		if [ -n "$GAMEEXE" ]; then
			SETHEAD="$SETHEAD \n<b>${GUI_SGE}:</b> $GAMEEXE"
		fi

		if [ "$ISORIGIN" -eq 1 ]; then
			SETHEAD="$SETHEAD \n<b>${GUI_SGA}:</b> Origin $L2EA url"
		else
			if [ "${#ORGCMDARGS[@]}" -ge 1 ]; then
				SETHEAD="$( printf "%s \n<b>%s</b> %q" "${SETHEAD}" "${GUI_SGA}" "${ORGCMDARGS[*]}" )"
			fi
		fi
	fi
	# filter html incompatible chars here
	#SETHEAD="${SETHEAD//&/+}"

	createProtonList X

	"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --scroll --center --window-icon="$STLICON" --form --center "$WINDECO" --title="$TITLE" \
	--text="$SETHEAD" \
	--columns="$COLCOUNT" --f1-action="$F1ACTIONCG" --separator="" \
	--field="$FBUT_GUISET_DCP":FBTN "$(realpath "$0") dcp" \
	--field="$FBUT_GUISET_DW":FBTN "$(realpath "$0") dw" \
	--field="$FBUT_GUISET_RECREATEPFX":FBTN "$(realpath "$0") ccd \"$AID\" \"s\"" \
	--field="$FBUT_GUISET_WDC":FBTN "$(realpath "$0") wdc \"$AID\"" \
	--field="$FBUT_GUISET_WTSEL":FBTN "$(realpath "$0") wt \"$AID\"" \
	--field="$FBUT_GUISET_ADDNSGA!$TT_ADDNSGA":FBTN "$(realpath "$0") ansg" \
	--field="$FBUT_GUISET_CREATEEVALSC":FBTN "$(realpath "$0") cfi \"$AID\"" \
	--field="$FBUT_GUISET_OTR!$TT_OTR":FBTN "$(realpath "$0") otr \"$AID\"" \
	--field="$FBUT_GUISET_DXHSEL":FBTN "$(realpath "$0") dxh \"$AID\"" \
	--field="$FBUT_GUISET_SHADERREPOS!$TT_SHADERREPOS":FBTN "$(realpath "$0") update shaders repos" \
	--field="$FBUT_GUISET_UPSHADER!$TT_UPSHADER":FBTN "$(realpath "$0") update gameshaders \"$SHADDESTDIR\"" \
	--field="$FBUT_GUISET_FAVSEL!$TT_FAVSEL":FBTN "$(realpath "$0") fav \"$AID\" set"\
	--field="$FBUT_GUISET_BLOCKCAT":FBTN "$(realpath "$0") block" \
	--field="$FBUT_GUISET_SORTCAT!$TT_SORTCAT":FBTN "$(realpath "$0") sort" \
	--field="$FBUT_GUISET_OPURL!$TT_OPENURL":FBTN "$(realpath "$0") hu \"$AID\" X" \
	--field="$FBUT_GUISET_GASCO!$TT_GASCO":FBTN "$(realpath "$0") gs \"$AID\" \"$GN\"" \
	--field="$FBUT_GUISET_VORTEX!$TT_VORTEX":FBTN "$(realpath "$0") vortex gui" \
	--field="$FBUT_GUISET_MO!$TT_MO":FBTN "$(realpath "$0") mo2 start" \
	--field="$FBUT_GUISET_GETSLR!$TT_GETSLR":FBTN "$(realpath "$0") getslrbtn \"$AID\"" \
	--field="$GUI_GAFI!$TT_GAFI":FBTN "$(realpath "$0") gf \"$AID\"" \
	--button="$BUT_EXIT":0 \
	--button="$BUT_GUISET_CATMENUSHORT":4 \
	--button="$BUT_GM":6 \
	--button="$BUT_DGM":8 \
	--button="$BUT_GLM":10 \
	--button="$BUT_FAV":12 \
	--button="$BUT_EDITORMENU":14 \
	--button="$BUT_PLAY":16 \
	"$GEOM"
	case $? in
	0)  {
			clickInfo "${FUNCNAME[0]}" "$?" "$BUT_EXIT" "$SETMENU" "Exit"
			GOBACK=0
			closeSTL " ######### STOP EARLY $PROGNAME $PROGVERS #########"
			exit
		}
	;;
	4) 	{
			clickInfo "${FUNCNAME[0]}" "$?" "$BUT_GUISET_CATMENUSHORT" "$SETMENU" "Category Menu Selection"
			refreshProtList
			setGuiCategoryMenuSel "$AID" "${FUNCNAME[0]}"
		}
	;;
	6) 	{
			clickInfo "${FUNCNAME[0]}" "$?" "$BUT_GM" "$SETMENU" "$GAMMENU"
			refreshProtList
			openGameMenu "$AID" "${FUNCNAME[0]}"
		}
	;;
	8) 	{
			clickInfo "${FUNCNAME[0]}" "$?" "$BUT_DGM" "$SETMENU" "Game Default Menu"
			refreshProtList
			openGameDefaultMenu "$AID" "${FUNCNAME[0]}"
		}
	;;
	10) 	{
			clickInfo "${FUNCNAME[0]}" "$?" "$BUT_GLM" "$SETMENU" "Global Menu"
			refreshProtList
			openGlobalMenu "$AID" "${FUNCNAME[0]}" "1"
		}
	;;
	12) 	{
			clickInfo "${FUNCNAME[0]}" "$?" "$BUT_FAV" "$SETMENU" "Favorites"
			refreshProtList
			favoritesMenu "$AID" "${FUNCNAME[0]}"
		}
	;;
	14) 	{
			clickInfo "${FUNCNAME[0]}" "$?" "$BUT_EDITORMENU" "$SETMENU" "EditorDialog"
			EditorDialog "$AID" "${FUNCNAME[0]}"
		}
	;;
	16) 	{
			clickInfo "${FUNCNAME[0]}" "$?" "$BUT_PLAY" "$SETMENU" "Game"
			GOBACK=0
			startSteamGame
		}
	;;
	esac

	goBackToPrevFunction "${FUNCNAME[0]}" "$2" "$GOBACK"
}

function createBlockYadList {
	checkEntryBlocklist
	while read -r block; do
		BLCAT="${block%=*}"
		BLVALR="${block#*=}"
		BLVAL="${BLVALR//\"/}"
		if [ "$BLVAL" -eq 1 ] ; then
			echo TRUE
			echo "$BLCAT"
		else
			echo FALSE
			echo "$BLCAT"
		fi
	done <<< "$(grep -v "^#" "$STLMENUBLOCKCFG" | grep "=")"
}

function getCatsFromCode {
	sed "s:CAT_:\nCAT_:g" "$STLRAWENTRIES" | grep "^CAT_" | cut -d '`' -f1 | cut -d '_' -f2 | sort -u
}

function updateMenuSortFile {
	if [ -f "$STLMENUSORTCFG" ] && [ -s "$STLMENUSORTCFG" ]; then
		while read -r catent; do
			if ! grep -q "^$catent" "$STLMENUSORTCFG"; then
				writelog "INFO" "${FUNCNAME[0]} - Adding missing Category '$catent' to '$STLMENUSORTCFG'"
				echo "$catent" >> "$STLMENUSORTCFG"
			fi
		done <<< "$(getCatsFromCode)"
	fi
}

function checkEntryBlocklist {
	# create blocklist with disabled categories
	if [ ! -f "$STLMENUBLOCKCFG" ]; then
		echo "#All enabled categories will be hidden in the menus" >"$STLMENUBLOCKCFG"
		while read -r block; do
			echo "${block}=\"0\"" >> "$STLMENUBLOCKCFG"
		done <<< "$(getCatsFromCode)"
	fi

	if [ -f "$STLMENUBLOCKCFG" ]; then
		# autoadding new (commented out) categories
		while read -r block; do
			if ! grep -q "^${block}" "$STLMENUBLOCKCFG"; then
				echo "${block}=\"0\"" >> "$STLMENUBLOCKCFG"
			fi
		done <<< "$(getCatsFromCode)"

		# create validity check file
		if [ ! -f "$STLMENUVALBLOCKCFG" ]; then
			cp "$STLMENUBLOCKCFG" "$STLMENUVALBLOCKCFG"
		fi

		# if validity check file differs - remove temporary menu files
		if ! cmp -s "$STLMENUBLOCKCFG" "$STLMENUVALBLOCKCFG"; then
			writelog "INFO" "${FUNCNAME[0]} - Removing temp menufiles in '$STLSHM'"
			find "$MTEMP" -maxdepth 1 -type f -name "*menu*" -exec rm {} \;
			cp "$STLMENUBLOCKCFG" "$STLMENUVALBLOCKCFG"
		fi
	fi
}

function getFilteredEntries {
	if [ -z "$1" ]; then
		INLIST="$STLRAWENTRIES"
	else
		INLIST="$1"
	fi

	# first remove blocked entries
	if grep -q "=\"1\"" "$STLMENUBLOCKCFG"; then
		writelog "INFO" "${FUNCNAME[0]} - Excluding blocked elements from '$STLMENUBLOCKCFG'"
		unset BlockCats
		while read -r line; do
			mapfile -t -O "${#BlockCats[@]}" BlockCats <<< "CAT_${line}"
		done <<< "$(grep "=\"1\"" "$STLMENUBLOCKCFG" | cut -d '=' -f1)"

		while read -r outline; do
			INCAT="CAT_$(grep -oP '#CAT_\K[^`]+' <<< "${outline}")"
			if [[ ! "${BlockCats[*]}" =~ $INCAT ]]; then
				echo "$outline" >> "$STLNOBLOCKENTRIES"
			fi
		done < "$INLIST"
	else
		writelog "INFO" "${FUNCNAME[0]} - No blocked elements found in '$STLMENUBLOCKCFG' - working with '$INLIST'"
		cp "$INLIST" "$STLNOBLOCKENTRIES"
	fi

	# now the sort order
	if [ ! -f "$STLMENUSORTCFG" ]; then
		writelog "INFO" "${FUNCNAME[0]} - '$STLMENUSORTCFG' missing - creating a default one"
		getCatsFromCode > "$STLMENUSORTCFG" 2>/dev/null
	fi

	writelog "INFO" "${FUNCNAME[0]} - Sort config '$STLMENUSORTCFG' found - sorting"

	while read -r line; do
		if [ -n "$line" ]; then
			grep "\`#CAT_${line}\`" "$STLNOBLOCKENTRIES" >> "$STLCATSORTENTRIES"
		fi
	done < "$STLMENUSORTCFG"
	cat "$STLCATSORTENTRIES"
	rm "$STLNOBLOCKENTRIES" 2>/dev/null
	rm "$STLCATSORTENTRIES" 2>/dev/null
}

function splitMenu {
	ARGSPLIT="$1"
	MYTMPL="$2"
	if [ "$ARGSPLIT" -gt 0 ]; then
		FUCO="$(wc -l < "$MYTMPL")"
		CUTCO=$((FUCO / 2 ))
		RESTCO=$((FUCO - CUTCO))
		head -n "$CUTCO" "$MYTMPL" > "${MYTMPL}_${ARGSPLIT}"

		if [ "$ARGSPLIT" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - ARGSPLIT is set to '$ARGSPLIT' - so using first half of '$MYTMPL' for the menu"
			head -n "$CUTCO" "$MYTMPL" > "${MYTMPL}_${ARGSPLIT}"
			mv "${MYTMPL}_${ARGSPLIT}" "$MYTMPL"
		elif [ "$ARGSPLIT" -eq 2 ]; then
			writelog "INFO" "${FUNCNAME[0]} - ARGSPLIT is set to '$ARGSPLIT' - so using second half of '$MYTMPL' for the menu"
			head -n "$CUTCO" "$MYTMPL" | grep "#HEAD" | tail -n 1 > "${MYTMPL}_${ARGSPLIT}"
			tail -n "$RESTCO" "$MYTMPL" >> "${MYTMPL}_${ARGSPLIT}"
			mv "${MYTMPL}_${ARGSPLIT}" "$MYTMPL"
		else
			writelog "INFO" "${FUNCNAME[0]} - ARGSPLIT is set to '$ARGSPLIT' - so using the whole '$MYTMPL' for the menu"
		fi
	fi
}

function createMenu {
	ARGSPLIT="$1"
	ARGMENU="$2"
	ARGFUNC="$3"
	ARGPIC="$4"
	UNUSED="$5"
	ARGCOLS="$6"
	ARGTITLE="$7"
	ARGPCMD="$8"
	ARGSPAT="$9"

	NEEDARG="9"

	MYTMPL="${ARGMENU}-${TMPL}"

	if [ "$#" -ne "$NEEDARG" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - Need $NEEDARG arguments and got '$#':"
		writelog "ERROR" "${FUNCNAME[0]} - '$*'"
	else
		writelog "INFO" "${FUNCNAME[0]} - got '$#' elements: '$*'"
	fi

	if [ ! -f "$MYTMPL" ] || [ ! -s "$MYTMPL" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Creating menu template '$MYTMPL'"
		getFilteredEntries "$STLRAWENTRIES" | grep "$ARGSPAT" > "$MYTMPL"
		rmDupLines "$MYTMPL"
		splitMenu "$ARGSPLIT" "$MYTMPL"
	fi

	if [ "$ARGPCMD" == "$NON" ]; then
		QBUT0="$BUT_EXIT"
	else
		QBUT0="$BUT_BACK"
	fi

	TITLE="${PROGNAME}-$ARGFUNC"

	echo "" >"$ARGMENU"
	# shellcheck disable=SC2028 # doesn't like the newline seperator, but it is valid
	{
		echo "function $ARGFUNC {"
		echo "\"$YAD\" --columns=\"$COLCOUNT\" --f1-action=\"$F1ACTIONCG\" --text=\"$(spanFont "$ARGTITLE" "H")\" \\"
		echo "--title=\"$TITLE\" --image \"$ARGPIC\" --image-on-top --window-icon=\"$STLICON\" --center \"$WINDECO\" --form --separator=\"\\n\" --quoted-output \\"
		echo "--button=\"$QBUT0\":0 --button=\"$BUT_MAINMENU\":2 --button=\"$BUT_RELOAD\":4 --button=\"$BUT_SAVERELOAD\":6 --button=\"$BUT_SAVEPLAY\":8 --button=\"$BUT_PLAY\":10 $GEOM \\"
		cat "$MYTMPL"
		echo "--scroll"
		echo "}"
	} >>"$ARGMENU"
}

function openMenu {
	ARGID="$1"
	ARGPCMD="$2"		# "$9"
	ARGLOADCFG="$3"
	# most args for createMenu:
	ARGSPLIT="$4"
	ARGMENU="$5"
	ARGFUNC="$6"
	ARGPIC="$7"
	UNUSED="$8"
	ARGCOLS="$9"
	ARGTITLE="${10}"
	ARGSPAT="${11}"

	resetAID "$ARGID"
	createGameCfg
	setGN "$ARGID"
	createDropdownLists

	mkCfgTemp "${FUNCNAME[0]}"

	if [ "$LOADCFG" == "$STLGAMECFG" ] ; then
		setShowPic
	fi

	loadCfg "$ARGLOADCFG" X

	TITLE="${PROGNAME}-$ARGFUNC"
	pollWinRes "$TITLE"

	writelog "INFO" "${FUNCNAME[0]} - Starting createMenu \"$ARGMENU\" \"$ARGFUNC\" \"$ARGPIC\" \"$UNUSED\" \"$ARGCOLS\" \"$ARGTITLE\" \"$ARGPCMD\" \"$ARGSPAT\""
	createMenu "$ARGSPLIT" "$ARGMENU" "$ARGFUNC" "$ARGPIC" "$UNUSED" "$ARGCOLS" "$ARGTITLE" "$ARGPCMD" "$ARGSPAT"

	source "$ARGMENU"
	writelog "INFO" "${FUNCNAME[0]} - Currently used tempfile is '$MKCFG'"
	"$ARGFUNC" > "$MKCFG"

	case $? in
	0) 	{
			if [ "$ARGPCMD" == "$NON" ]; then
				clickInfo "${FUNCNAME[0]}" "$?" "$QBUT0" "$GAMMENU" "Exit"
				GOBACK=0
				closeSTL " ######### STOP EARLY $PROGNAME $PROGVERS #########"
				exit
			else
				clickInfo "${FUNCNAME[0]}" "$?" "$QBUT0" "$GAMMENU" "$ARGPCMD"
				"$ARGPCMD" "$AID" "${FUNCNAME[0]}"
			fi
		}
	;;
	2)  {
			clickInfo "${FUNCNAME[0]}" "$?" "$BUT_MAINMENU" "$FAVOMENU" "$SETMENU"
			MainMenu "$AID" "${FUNCNAME[0]}"
		}
	;;
	4) 	{
			GOBACK=0
			clickInfo "${FUNCNAME[0]}" "$?" "$BUT_RELOAD" "$FAVOMENU" "$PARGUI"
			if [ "$SAVESETSIZE" -eq 1 ] ; then	sleep 1;	fi
			"$PARGUI" "$AID" "$ARGPCMD" "$ARGSPLIT"
		}
	;;
	6) 	{
			clickInfo "${FUNCNAME[0]}" "$?" "$BUT_SAVERELOAD" "$FAVOMENU" "$PARGUI"
			GOBACK=0
			writelog "INFO" "${FUNCNAME[0]} - Saving config '$ARGLOADCFG' and restart '$PARGUI' GUI"
			saveMenuEntries "$ARGMENU"
			if [ "$SAVESETSIZE" -eq 1 ] ; then	sleep 1;	fi
			"$PARGUI" "$AID" "$ARGPCMD" "$ARGSPLIT"
		}
	;;
	8)  {
			clickInfo "${FUNCNAME[0]}" "$?" "$BUT_SAVEPLAY" "$FAVOMENU" "Game Start"
			GOBACK=0
			writelog "INFO" "${FUNCNAME[0]} - Saving config '$ARGLOADCFG' and starting the game"
			saveMenuEntries "$ARGMENU"
			startSteamGame
		}
	;;
	10)	{
			clickInfo "${FUNCNAME[0]}" "$?" "$BUT_PLAY" "$FAVOMENU" "Game Start"
			GOBACK=0
			startSteamGame
		}
	;;
	esac
}

function writelog {
#DBGMS "IN - $2"
# LOGLEVEL=0: disable log

# LOGLEVEL=1: log only:
LOGONE="404,ERROR,SKIP,WARN,CREATE"

# LOGLEVEL=2: log also - including:
#LOGTWO="HACK,INFO,NEW,UPDATE,WAIT"

	if [ -n "$4" ] && [ "$3" == "X" ] && grep -q ".log" <<< "$4"; then
		echo "$(date) $1 - $2" | tee -a "$4" >/dev/null
	else
		if [ -z "$LOGLEVEL" ]; then
			LOGLEVEL=2
		fi

		if [ "$LOGLEVEL" -eq 1 ]; then
			if grep -q "$1" <<< "$LOGONE"; then
				if [ -z "$LOGFILE" ]; then
					echo "$(date) $1 - $2" | tee -a "$TEMPLOG" >/dev/null
				else
					echo "$(date) $1 - $2" | tee -a "$TEMPLOG" "$LOGFILE" >/dev/null
				fi
			fi
		fi

		if [ "$LOGLEVEL" -eq 2 ]; then
			if [ -z "$LOGFILE" ]; then
				echo "$(date) $1 - $2" | tee -a "$TEMPLOG" >/dev/null
			else
				echo "$(date) $1 - $2" | tee -a "$TEMPLOG" "$LOGFILE" >/dev/null
			fi
		fi

		if [ -n "$3" ]; then
			if [ "$3" == "E" ]; then
				echo "$(date) $1 - $2"
			elif [ "$3" == "P" ] && [ -f "$PRELOG" ]; then
				echo "$(date) $1 - $2" | tee -a "$PRELOG" >/dev/null
			elif grep -q ".log" <<< "$3"; then
				echo "$(date) $1 - $2" | tee -a "$3" >/dev/null
			fi
		fi
	fi
#DBGMS "OUT - $2"
}

# generic git clone/pull function
function gitUpdate {
	GITDIR="$1"
	GITURL="$2"
	if [ -d "$GITDIR/.git" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Pulling '$GITURL' update in '$GITDIR'"

		if [ "$ONSTEAMDECK" -eq 1 ]; then
			LD_PRELOAD="/usr/lib/libcurl.so.4" "$GIT" --work-tree="$GITDIR" --git-dir="$GITDIR/.git" pull --rebase=false &> "$STLSHM/${FUNCNAME[0]}-SteamDeck-${GITDIR##*/}"
		else
			"$GIT" --work-tree="$GITDIR" --git-dir="$GITDIR/.git" pull --rebase=false &> "$STLSHM/${FUNCNAME[0]}-${GITDIR##*/}"
		fi
	else
		mkProjDir "$GITDIR"
		writelog "INFO" "${FUNCNAME[0]} - Cloning '$GITURL' in '$GITDIR'"

		if [ "$ONSTEAMDECK" -eq 1 ]; then
			LD_PRELOAD="/usr/lib/libcurl.so.4" "$GIT" clone "$GITURL" "$GITDIR" &> "$STLSHM/${FUNCNAME[0]}-SteamDeck-${GITDIR##*/}"
		else
			"$GIT" clone "$GITURL" "$GITDIR" &> "$STLSHM/${FUNCNAME[0]}-${GITDIR##*/}"
		fi
	fi
}

function fetchGitHubTags {
    PROJURL="$1"
    N="$2"

    RELEASESURL="${PROJURL}/releases"
    TAGSURL="${PROJURL}/tags"

    TAGSGREP="${RELEASESURL#"$GHURL"}/tag"

    mapfile -t BASETAGS < <("$WGET" -q "${TAGSURL}" -O - 2> >(grep -v "SSL_INIT") | grep -oE "${TAGSGREP}[^\"]+" | sort -urV | grep -m "$N" "$TAGSGREP")
    for TAG in "${BASETAGS[@]}"; do
        basename "$TAG"
    done
}

# Just for fun ;)
function getSeasonalGreeting {
	CURRDATE="$( date +"%d-%m" )"
	if [ "$CURRDATE" = "25-12" ];then
		echo "Happy Holidays!"
	elif [ "$CURRDATE" = "31-12" ] || [ "$CURRDATE" = "01-01" ]; then
		echo "Happy New Year!"
	elif [ "$CURRDATE" = "31-10" ]; then
		TM="$( date +"%H:%M" )"
		if [ "$TM" = "00:00" ]; then
			echo "Happy Halloween, it's the witching hour!"
		else
			echo "Happy Halloween!"
		fi
	fi
}

function migrateCfgOption {
	# temporary function to update specific configuration options - will vary depending on the current steamtinkerlaunch version and mostly won't be used at all
	if [ "$1" == "$STLGAMECFG" ]; then
		# update dxvk config handling:
		if grep -q "^#STLDXVKCFG" "$STLGAMECFG" && ! grep -q "^STLDXVKCFG" "$STLGAMECFG"; then
			writelog "INFO" "${FUNCNAME[0]} - Commenting in STLDXVKCFG in '$STLGAMECFG' automatically"
			sed "s:^#STLDXVKCFG:STLDXVKCFG:g" -i "$STLGAMECFG"
		fi

		if [ -z "$STLDXVKCFG" ]; then
			STLDXVKLINE="$(grep "^STLDXVKCFG" "$STLGAMECFG")"
			STLDXVKLINE="${STLDXVKLINE//\"/}"
			STLDXVKLINE="${STLDXVKLINE//STLCFGDIR/$STLCFGDIR}"
			if [ -n "$STLDXVKLINE" ]; then
				export "${STLDXVKLINE?}"
			fi
		fi

		if [ -z "$STLDXVKCFG" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Variable 'STLDXVKCFG' could not be found - giving up"
		else
			if [ -z "$STLGAMECFG" ]; then
				setAIDCfgs
			fi

			if grep -q "^STLDXVKCFG" "$STLGAMECFG" && ! grep -q "^USE_STLDXVKCFG=\"1\"" "$STLGAMECFG"; then
				if [ -f "$STLDXVKCFG" ]; then
					if [ "$(wc -l < "$STLDXVKCFG")" -eq 1 ] && grep "^NOTE" "$STLDXVKCFG"; then
						writelog "INFO" "${FUNCNAME[0]} - Empty placeholder config '$STLDXVKCFG' found - not enabling USE_STLDXVKCFG automatically"
					elif [ "$(wc -l < "$STLDXVKCFG")" -gt 1 ] || { [ "$(wc -l < "$STLDXVKCFG")" -eq 1 ] && ! grep "^NOTE" "$STLDXVKCFG"; }; then
						mkProjDir "$STLTEMPDIR"
						DXVKMIGLIST="$STLTEMPDIR/dxvkcfg.txt"
						touch "$DXVKMIGLIST"
						if grep -q "^$AID$" "$DXVKMIGLIST"; then
							writelog "SKIP" "${FUNCNAME[0]} - STLDXVKCFG was already updated for '$AID' before"
						else
							writelog "INFO" "${FUNCNAME[0]} - Found STLDXVKCFG in '$STLGAMECFG' being used, so automatically enabling USE_STLDXVKCFG"
							touch "$FUPDATE"
							USE_STLDXVKCFG=1
							updateConfigEntry "USE_STLDXVKCFG" "$USE_STLDXVKCFG" "$STLGAMECFG"
							echo "$AID" > "$DXVKMIGLIST"
						fi
					else
						writelog "INFO" "${FUNCNAME[0]} - Unknown constellation - shouldn't happen"
					fi
				else
					writelog "INFO" "${FUNCNAME[0]} - File '$STLDXVKCFG' does not exist - nothing to do"
				fi
			fi
		fi

		# specialk replace "old" version
		if [ "$SPEKVERS" == "discord" ] || [ "$SPEKVERS" == "old" ] || [ "$SPEKVERS" == "latest" ] || [ "$SPEKVERS" == "default" ] || [ "$SPEKVERS" == "test" ]; then
			SPEKVERS="stable"
			touch "$FUPDATE"
			writelog "INFO" "${FUNCNAME[0]} - Automatically updating variable SPEKVERS from 'old' to '$SPEKVERS' in '$STLGAMECFG'"
			updateConfigEntry "SPEKVERS" "$SPEKVERS" "$STLGAMECFG"
		fi

		# stop MO2MODE from being 'none' in weird scenarios -- Issue was confirmed to be exclusive to SteamOS and may be a (temporary?) SteamOS regression
		if [ "$MO2MODE" == "$NON" ]; then
			MO2MODE="disabled"
			touch "$FUPDATE"
			writelog "INFO" "${FUNCNAME[0]} - ModOrganizer 2 variable MO2MODE is somehow '$NON' -- Defaulting this to 'disabled'"
			updateConfigEntry "MO2MODE" "$MO2MODE" "$STLGAMECFG"
		fi

		# collections update
		if [ -n "$CHECKCATEGORIES" ] && [ "$CHECKCATEGORIES" -eq 1 ]; then
			CHECKCOLLECTIONS="$CHECKCATEGORIES"
			touch "$FUPDATE"
			writelog "INFO" "${FUNCNAME[0]} - Automatically updating variable CHECKCOLLECTIONS from old variable CHECKCATEGORIES"
			updateConfigEntry "CHECKCOLLECTIONS" "$CHECKCOLLECTIONS" "$STLGAMECFG"
		fi

		# ReShade changes -- Combine USERESHADE and RESHADE_INSTALL into one option (ensure USERESHADE matches whatever value INSTALL_RESHADE had now that they are equivalent)
		if [ -n "$INSTALL_RESHADE" ] && [ "$INSTALL_RESHADE" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found legacy 'INSTALL_RESHADE' with value '$INSTALL_RESHADE' -- disabling as this is no longer used"
			INSTALL_RESHADE=0
			USERESHADE=1

			# If INSTALL_RESHADE was ever on, disable it and force USERESHADE to 1 as these options are equivalent now
			touch "$FUPDATE"
			updateConfigEntry "INSTALL_RESHADE" "$INSTALL_RESHADE" "$STLGAMECFG"
			updateConfigEntry "USERESHADE" "$USERESHADE" "$STLGAMECFG"
		fi

#486
function wip {
		writelog "INFO" "${FUNCNAME[0]} - Upcoming file migration. Some files in '$STLCFGDIR' will soon be migrated to '$STLDATADIR' or '$STLCACHEDIR' - see #486"

		if [ "$(df -P "$STLCFGDIR" | awk 'END{print $NF}')" == "$(df -P "$STLCACHEDIR" | awk 'END{print $NF}')" ];then
			writelog "INFO" "${FUNCNAME[0]} - '$STLCACHEDIR' is on the same partition like '$STLCFGDIR'"
		else
			writelog "INFO" "${FUNCNAME[0]} - '$STLCACHEDIR' is on a different partition like '$STLCFGDIR' - need to check if there's enough space for migrating"
			OLDSTLDLDIR="$STLCFGDIR/downloads"
			NEWSTLDLDIR="$STLCACHEDIR/downloads"
			OLDDLDIRSIZE="$(du -sb "$OLDSTLDLDIR" | awk 'END{print $1}')"
			DLDIRSIZE="$(df -k --output=avail "$NEWSTLDLDIR" | tail -n1)"
			writelog "INFO" "${FUNCNAME[0]} - OLDDLDIRSIZE is '$OLDDLDIRSIZE'; DLDIRSIZE is '$DLDIRSIZE'"
		fi
}
	fi
}

function loadCfg {
	CFGFILE="$1"

	if [ -f "$CFGFILE" ]; then

		# disable logging here when the program just started (cosmetics)
		if [ -z "$2" ]; then
			writelog "INFO" "${FUNCNAME[0]} - '$CFGFILE' START"
		fi

		if [ "$CFGFILE" == "$STLGAMECFG" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Loading game config '$CFGFILE'"
		fi

		while read -r line; do
			if [ -n "$line" ]; then
				EXPORTLINE="${line//\"/}"
				EXPORTLINE="${EXPORTLINE//STLCFGDIR/$STLCFGDIR}"
				export "${EXPORTLINE?}"
			fi
		done <<< "$(grep -v "^#\|^$" "$CFGFILE")"

		# config migration #XXXXXXXXXXXXXX
		migrateCfgOption "$CFGFILE"

		# disable logging here when the program just started (cosmetics)
		if [ -z "$2" ]; then
			writelog "INFO" "${FUNCNAME[0]} - '$CFGFILE' STOP"
		fi
	fi
}

function extWine64Run {
	if [ "$USEWINE" -eq 0 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Command in Proton WINEPREFIX is: WINE=\"$RUNWINE\" WINEARCH=win64 WINEDEBUG=\"$STLWINEDEBUG\" WINEPREFIX=\"$GPFX\" $*"
		WINE="$RUNWINE" WINEARCH=win64 WINEDEBUG="$STLWINEDEBUG" WINEPREFIX="$GPFX" "$@" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
	else
		writelog "INFO" "${FUNCNAME[0]} - Command in Wine WINEPREFIX is: WINE=\"$RUNWINE\" WINEARCH=\"$RUNWINEARCH\" WINEDEBUG=\"$STLWINEDEBUG\" WINEPREFIX=\"$GWFX\" $*"
		WINE="$RUNWINE" WINEARCH="$RUNWINEARCH" WINEDEBUG="$STLWINEDEBUG" WINEPREFIX="$GWFX" "$@" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
	fi
}

function extProtonRun {
	MODE="$1"
	PROGRAM="$2"
	PROGARGS="$3"
	EXTPROGRAMARGS="$4"  # e.g. args like GameScope/GameMode taken from `buildCustomCmdLaunch` for ONLY_CUSTOMCMD
	EXTWINERUN=0
	EXTPROTUSESLR="${5:-0}"  # Should extProtonRun fetch and use SLR (default to 0 -- turned off)

	if [ "$USEWINE" -eq 1 ] && [[ ! "$WINEVERSION" =~ ${DUMMYBIN}$ ]] && [ "$WINEVERSION" != "$NON" ]; then
		EXTWINERUN=1
	fi

	setRunProtonFromUseProton

	# could help here:
	if [ ! -f "${RUNPROTON//\"/}" ]; then
		writelog "WARN" "${FUNCNAME[0]} - '$USEPROTON' seems outdated as the executable ${RUNPROTON//\"/} wasn't found"
		fixProtonVersionMismatch "USEPROTON" "$STLGAMECFG"
	fi

	writelog "INFO" "${FUNCNAME[0]} - Continuing with RUNPROTON='$RUNPROTON'"

	if [ ! -f "$RUNPROTON" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - When this error occurs, probably the configured Proton (or a similar) version is no longer available."
		writelog "ERROR" "${FUNCNAME[0]} - Because the RUNPROTON '$RUNPROTON' isn't ready here yet - please open an issue when you made sure it is set to an existing one and the error persists"
	else
		if [ -z "$PROGARGS" ] || [ "$PROGARGS" == "$NON" ]; then
			RUNPROGARGS=""
		else
			mapfile -d " " -t -O "${#TMP_RUNPROGARGS[@]}" TMP_RUNPROGARGS < <( printf "%q" "$PROGARGS" )

			# Basically check for and join paths that contain spaces because above mapfile will strip them
			# TODO: This does NOT work with paths that use forward slashes
			for i in "${!TMP_RUNPROGARGS[@]}"; do
				# Remove trailing backslash, i.e. turn `--launch\` into `--launch`
				TMP_RUNPROGARGS[i]="${TMP_RUNPROGARGS[i]%\\}"

				# If the last seen element in the array ended with a backslash, assume
				# this is an incomplete path and join them
				#
				# This is not perfect as valid paths that just end with backslashes will not work,
				# but we can document this on the wiki
				#
				# i.e. "Z:\this\is\a\path\ MY_VAR=2" will not work, but "Z:\this\is\a\path MY_VAR=2" will work
				if [[ $LASTRUNPROGARG = *"\\" ]]; then
					# Remove 'i-1' (previous element), because 'i' (current element) will contain 'i-1'
					unset "TMP_RUNPROGARGS[i-1]"
					TMP_RUNPROGARGS[i]="${LASTRUNPROGARG} ${TMP_RUNPROGARGS[i]}"
				fi
				LASTRUNPROGARG="${TMP_RUNPROGARGS[i]}"
			done

			# Generate new array with null strings removed.
			mapfile -t -O "${#RUNPROGARGS[@]}" RUNPROGARGS < <( printf "%s\n" "${TMP_RUNPROGARGS[@]}" | grep -v "^$" )
		fi

		FWAIT=2

		# mirrors above RUNPROGARGS
		# TODO what if we try to pass paths with spaces? This could be problematic here...
		if [ -z "$EXTPROGRAMARGS" ]; then
			writelog "INFO" "${FUNCNAME[0]} - No external program args here it seems"
			RUNEXTPROGRAMARGS=( "" )
		else
			writelog "INFO" "${FUNCNAME[0]} - Looks like we got some external program args, '${EXTPROGRAMARGS}'"
			mapfile -d " " -t -O "${#RUNEXTPROGRAMARGS[@]}" RUNEXTPROGRAMARGS < <(printf '%s' "$EXTPROGRAMARGS")
		fi

		# Have to set SLR in extProtonRun because we can't pass the array to the function
		# Hopefully unsetting is safe and doesn't mean places that need the SLR will lose it from this 'unset'
		if [ "$EXTPROTUSESLR" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - EXTPROTUSESLR is '$EXTPROTUSESLR' -- Attempting to find and use SLR with extProtonRun"

			unset "${SLRCMD[@]}"
			setSLRReap
		fi

		# append SLR to beginning of RUNEXTPROGRAMARGS, if SLR is defined
		# TODO this is the exact same logic as in launchCustomProg (except the log messages are slightly different), is there any way to share it?
		if [ -n "${SLRCMD[*]}" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Gotten Steam Linux Runtime for Proton launch, using RUNEXTPROGRAMARGS array to contain it and add it to launch command"

			OLDRUNEXTPROGRAMARGS=( "${RUNEXTPROGRAMARGS[@]}" )

			unset "${RUNEXTPROGRAMARGS[@]}"
			RUNEXTPROGRAMARGS=( "${SLRCMD[@]}" )

			# OLDRUNEXTPROGRAMARGS should only contain one item, the passed args for the custom command
			# if the first item here is not empty, assume we have to include the old pass args in the new array
			#
			# if blank, it means OLDRUNEXTPROGRAMARGS was most likely empty (or started with a blank element, which would cause a crash anyway)
			# so we can just create RUNEXTPROGRAMARGS with the SLR as the only element
			if [ -n "${OLDRUNEXTPROGRAMARGS[0]}" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Seems like some arguments were given to the custom command, including them alongside the Steam Linux Runtime arguments"
				RUNEXTPROGRAMARGS+=( "${OLDRUNEXTPROGRAMARGS[@]}" )
			fi
		elif [ -z "${SLRCMD[*]}" ] && [ "$CUSTOMCMD_USESLR" -eq 1 ]; then
			writelog "WARN" "${FUNCNAME[0]} - Attempted to fetch Steam Linux Runtime but failed to find one!"
		fi
		unset "${SLRCMD[@]}"

		# TODO pass "$EXTPROGRAMARGS" to programs running with Wine as well(?)
		# TODO refactor a bit to be a little cleaner if possible
		# TODO it should be possible to pass PROTON_LOG to these Proton commands, however this also requries SteamGameId to be set -- This is not defined outside of Steam AND inside of Steam it would conflict with an actual Game ID if used
		#      if we want to use PROTON_LOG we need the ability to pass in PROTON_LOG=? and a custom SteamGameId that is only set for these program runs
		#      i.e. a default SteamGameId could be `extProtonRun`, but launchCustomProg could pass `customcommand`.
		#
		#      This would be tricky to add, but would be nice to have!
		CUSTPROGNAME="$( basename "$PROGRAM" )"
		if [ "$MODE" == "F" ]; then  # Forked Proton/Wine 'normal' custom program
			if [ -n "${RUNPROGARGS[0]}" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Starting '$PROGRAM' with arguments '${RUNPROGARGS[*]}' forked into the background"
				restoreOrgVars
				if [ "$EXTWINERUN" -eq 1 ]; then
					(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED_ARGS_WINE" "$CUSTPROGNAME" )"; extWine64Run "$PROGRAM" "${RUNPROGARGS[@]}") &
				else
					if [ -n "${RUNEXTPROGRAMARGS[0]}" ]; then
						(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED_ARGS" "$CUSTPROGNAME" )"; "${RUNEXTPROGRAMARGS[@]}" "$RUNPROTON" run "$PROGRAM" "${RUNPROGARGS[@]}" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log") &
					else
						(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED_ARGS" "$CUSTPROGNAME" )"; "$RUNPROTON" run "$PROGRAM" "${RUNPROGARGS[@]}" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log") &
					fi
				fi
				emptyVars "O" "X"
			else
				writelog "INFO" "${FUNCNAME[0]} - Starting '$PROGRAM' forked into the background"
				restoreOrgVars
				if [ "$EXTWINERUN" -eq 1 ]; then
					(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED_WINE" "$CUSTPROGNAME" )"; extWine64Run "$PROGRAM") &
				else
					if [ -n "${RUNEXTPROGRAMARGS[0]}" ]; then
						(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED" "$CUSTPROGNAME" )"; "${RUNEXTPROGRAMARGS[@]}" "$RUNPROTON" run "$PROGRAM" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log") &
					else
						(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED" "$CUSTPROGNAME" )"; "$RUNPROTON" run "$PROGRAM" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log") &
					fi
				fi
				emptyVars "O" "X"
			fi
		elif [ "$MODE" == "FC" ]; then  # Forked Proton/Wine 'command line' custom program
			if [ -n "${RUNPROGARGS[0]}" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Starting '$WICO $PROGRAM' with arguments '${RUNPROGARGS[*]}' forked into the background"
				restoreOrgVars
				if [ "$EXTWINERUN" -eq 1 ]; then
					(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED_ARGS_WINE" "$CUSTPROGNAME" )"; extWine64Run "$RUNWICO" "$PROGRAM" "${RUNPROGARGS[@]}") &
				else
					if [ -n "${RUNEXTPROGRAMARGS[0]}" ]; then
						(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED_ARGS" "$CUSTPROGNAME" )"; "${RUNEXTPROGRAMARGS[@]}" "$RUNPROTON" run "$WICO" "$PROGRAM" "${RUNPROGARGS[@]}" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log") &
					else
						(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED_ARGS" "$CUSTPROGNAME" )"; "$RUNPROTON" run "$WICO" "$PROGRAM" "${RUNPROGARGS[@]}" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log") &
					fi
				fi
				emptyVars "O" "X"
			else
				writelog "INFO" "${FUNCNAME[0]} - Starting '$WICO $PROGRAM' forked into the background"
				restoreOrgVars
				if [ "$EXTWINERUN" -eq 1 ]; then
					(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED_WINE" "$CUSTPROGNAME" )"; extWine64Run "$RUNWICO" "$PROGRAM") &
				else
					if [ -n "${RUNEXTPROGRAMARGS[0]}" ]; then
						(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED" "$CUSTPROGNAME" )"; "${RUNEXTPROGRAMARGS[@]}" "$RUNPROTON" run "$WICO" "$PROGRAM" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log") &
					else
						(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED" "$CUSTPROGNAME" )"; "$RUNPROTON" run "$WICO" "$PROGRAM" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log") &
					fi
				fi
				emptyVars "O" "X"
			fi
		elif [ "$MODE" == "R" ]; then  # Regular (no fork/wait/etc) Proton/Wine custom program
			if [ -n "${RUNPROGARGS[0]}" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Starting '$PROGRAM' with arguments '${RUNPROGARGS[*]}' regularly"
				restoreOrgVars
				if [ "$EXTWINERUN" -eq 1 ]; then
					notiShow "$( strFix "$NOTY_CUSTPROG_REG_ARGS_WINE" "$CUSTPROGNAME" )"
					extWine64Run "$PROGRAM" "${RUNPROGARGS[@]}"
				else
					notiShow "$( strFix "$NOTY_CUSTPROG_REG_ARGS" "$CUSTPROGNAME" )"
					if [ -n "${RUNEXTPROGRAMARGS[0]}" ]; then
						"${RUNEXTPROGRAMARGS[@]}" "$RUNPROTON" run "$PROGRAM" "${RUNPROGARGS[@]}" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
					else
						"$RUNPROTON" run "$PROGRAM" "${RUNPROGARGS[@]}" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
					fi
				fi
				emptyVars "O" "X"
			else
				writelog "INFO" "${FUNCNAME[0]} - Starting '$PROGRAM' regularly"
				restoreOrgVars
				if [ "$EXTWINERUN" -eq 1 ]; then
					notiShow "$( strFix "$NOTY_CUSTPROG_REG_WINE" "$CUSTPROGNAME" )"
					extWine64Run "$PROGRAM"
				else
					notiShow "$( strFix "$NOTY_CUSTPROG_REG" "$CUSTPROGNAME" )"
					if [ -n "${RUNEXTPROGRAMARGS[0]}" ]; then
						writelog "INFO" "${FUNCNAME[0]} - \"${RUNEXTPROGRAMARGS[*]}\" \"$RUNPROTON\" run \"$PROGRAM\""
						"${RUNEXTPROGRAMARGS[@]}" "$RUNPROTON" waitforexitandrun "$PROGRAM" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
					else
						"$RUNPROTON" run "$PROGRAM" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
					fi
				fi
				emptyVars "O" "X"
			fi
		elif [ "$MODE" == "RC" ]; then  # Regular (no fork/wait/etc) Proton/Wine 'command line' custom program
			if [ -n "${RUNPROGARGS[0]}" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Starting '$WICO $PROGRAM' with arguments '${RUNPROGARGS[*]}' regularly"
				restoreOrgVars
				if [ "$EXTWINERUN" -eq 1 ]; then
					notiShow "$( strFix "$NOTY_CUSTPROG_REG_ARGS_WINE" "$CUSTPROGNAME" )"
					extWine64Run "$RUNWICO" "$PROGRAM" "${RUNPROGARGS[@]}"
				else
					notiShow "$( strFix "$NOTY_CUSTPROG_REG_ARGS" "$CUSTPROGNAME" )"
					if [ -n "${RUNEXTPROGRAMARGS[0]}" ]; then
						"${RUNEXTPROGRAMARGS[@]}" "$RUNPROTON" run "$WICO" "$PROGRAM" "${RUNPROGARGS[@]}" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
					else
						"$RUNPROTON" run "$WICO" "$PROGRAM" "${RUNPROGARGS[@]}" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
					fi
				fi
				emptyVars "O" "X"
			else
				writelog "INFO" "${FUNCNAME[0]} - Starting '$WICO $PROGRAM' regularly"
				restoreOrgVars
				if [ "$EXTWINERUN" -eq 1 ]; then
					notiShow "$( strFix "$NOTY_CUSTPROG_REG_WINE" "$CUSTPROGNAME" )"
					extWine64Run "$RUNWICO" "$PROGRAM"
				else
					notiShow "$( strFix "$NOTY_CUSTPROG_REG" "$CUSTPROGNAME" )"
					if [ -n "${RUNEXTPROGRAMARGS[0]}" ]; then
						"${RUNEXTPROGRAMARGS[@]}" "$RUNPROTON" run "$WICO" "$PROGRAM" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
					else
						"$RUNPROTON" run "$WICO" "$PROGRAM" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
					fi
				fi
				emptyVars "O" "X"
			fi
		fi
	fi
}

function getWindowHeight {
	WINID="$1"
	"$XWININFO" -id "$WINID" -stats | awk '$1=="-geometry" {print $2}' | cut -d '+' -f1 | cut -d 'x' -f2
}

function getLatestGeoElf {
	GEOELFDLURL="$("$WGET" -q "$GEOELFURL" -O - 2> >(grep -v "SSL_INIT") | sed $'s/<a href=/\\\n/g' | grep "^\"https" | grep "\.zip" | grep -oP '"\K[^"]+' | grep -m1 https)" # maybe make less fragile later :)
	GEOELFFILE="${GEOELFDLURL##*/}"
	CURGEOELF="${GEOELFDLURL##*+v}"
	CURGEOELF="${CURGEOELF%.*}";

	CURGEOELFDIR="$GEOELFDLDIR/$CURGEOELF"
	mkProjDir "$CURGEOELFDIR"

	GEODSTD="$CURGEOELFDIR"
	GEODSTF="$GEODSTD/$GEOELFFILE"

	if [ -f "$GEODSTF" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Already have '$GEODSTF' - skipping download"
	else
		writelog "INFO" "${FUNCNAME[0]} - Downloading '$GEOELFDLURL' to '$GEODSTF'"
		dlCheck "$GEOELFDLURL" "$GEODSTF" "X" "Downloading '$GEOELFFILE'"
		"$UNZIP" -q "$GEODSTF" -d "$GEODSTD" 2>/dev/null
		writelog "INFO" "${FUNCNAME[0]} - Extracted '$GEODSTF' to '$GEODSTD'"
		if [ -L "$LGEOELFSYM" ]; then
			rm "$LGEOELFSYM"
			writelog "INFO" "${FUNCNAME[0]} - Updating symlink '$LGEOELFSYM' pointing to '$CURGEOELFDIR'"
		else
			writelog "INFO" "${FUNCNAME[0]} - Creating symlink '$LGEOELFSYM' pointing to '$CURGEOELFDIR'"
		fi
		ln -s "$CURGEOELFDIR" "$LGEOELFSYM"
		cp "$(find "$GEODSTD" -name "*Version*")" "$GEODSTD/${GEOELF}-version.txt"
	fi
}

function configureGeoElf {
	function installGeo {
		function copyGeo {
			rm "$GEOELFENA" 2>/dev/null
			while read -r file; do
				cp "$file" "$GEOELFDDIR"
				echo "${file##*/}" >> "$GEOELFENA"
			done <<< "$(find "$LGEOELFSYM/$1" -mindepth 1 -maxdepth 1 -type f)"

			# not sure yet if (ShaderFixes) subdirectory is even required - maybe implement later TODO?
			#			while read -r dir; do
			#				mkdir "$GEOELFDDIR/${dir##*/}"
			#			done <<< "$(find "$LGEOELFSYM/$1" -mindepth 1 -type d)"
			# ...

			cp "$LGEOELFSYM/${GEOELF}-version.txt" "$GEOELFDDIR"
			echo "${GEOELF}-version.txt" >> "$GEOELFENA"
		}

		if [ ! -d "$LGEOELFSYM/$1" ] || [ "$AUTOGEOELF" -eq 1 ]; then
			getLatestGeoElf
		fi

		if [ -d "$LGEOELFSYM/$1" ]; then
			if [ -f "$GEOELFENA" ]; then
				if [ "$AUTOGEOELF" -eq 1 ]; then
					GEOELFVI="$(cat "$GEOELFDDIR/${GEOELF}-version.txt")"
					GEOELFVA="$(cat "$LGEOELFSYM/${GEOELF}-version.txt")"
					writelog "INFO" "${FUNCNAME[0]} - Installed $GEOELF version is '$GEOELFVI', latest downloaded version is '$GEOELFVA'"
					# no need to check the newer version directly, because latest is either equal or newer
					if [ "$GEOELFVI" != "$GEOELFVA" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Installing new $GEOELF version '$GEOELFVA'"
						copyGeo "$1"
					else
						writelog "INFO" "${FUNCNAME[0]} - Installed $GEOELF version '$GEOELFVI' is identical to the latest downloaded version '$GEOELFVA' - nothing to do"
					fi
				else
					writelog "SKIP" "${FUNCNAME[0]} - $GEOELF is already installed in the game dir and autoupdate is disabled - nothing to do"
				fi
			else
				writelog "INFO" "${FUNCNAME[0]} - Installing geo-11 drivers from '$LGEOELFSYM/$1' to '$GEOELFDDIR'"
				copyGeo "$1"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - '$LGEOELFSYM' could not be found - can't enable $GEOELF"
			USEGEOELF=0
		fi
	}

	GEOELFDDIR="$EFD"
	setFullGameExePath "GEOELFDDIR"
	GEOELFENA="$GEOELFDDIR/${GEOELF}_enabled.txt"

	if [ "$USEGEOELF" -eq 1 ]; then
		if [ "$USECUSTOMCMD" -eq 1 ] && [ -f "$CUSTOMCMD" ]; then
			ARCHEXE="$CUSTOMCMD"
		else
			ARCHEXE="$GP"
		fi

		if [ "$(getArch "$ARCHEXE")" == "32" ]; then
			installGeo "x32"
		elif [ "$(getArch "$ARCHEXE")" == "64" ]; then
			installGeo "x64"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Could not determine the architecture of '$GP' - not installing '$GEOELF'" "E"
		fi
	else
		if [ -f "$GEOELFENA" ]; then
			writelog "INFO" "${FUNCNAME[0]} - $GEOELF was previously enabled, so removing all its files from '$GEOELFDDIR'"
			while read -r file; do
				RMFILE="$GEOELFDDIR/$file"
				if [ -f "$RMFILE" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Removing '$RMFILE'"
					rm "$GEOELFDDIR/$file"
				else
					writelog "SKIP" "${FUNCNAME[0]} - '$RMFILE' missing - nothing to do" "E"
				fi
			done < "$GEOELFENA"
			rm "$GEOELFENA"
		fi
	fi
}

function initSteamVR {
	if [ "$RUNSBSVR" -eq 1 ]; then
		SVRJUSTSTARTED=0
		STEAMVRARGS=(-applaunch 250820)

		if "$PGREP" -a "vrcompositor" >/dev/null ; then
			writelog "INFO" "${FUNCNAME[0]} - Looks like SteamVR is already running - skipping this function"
		else
			writelog "WARN" "${FUNCNAME[0]} - This function might be removed as it blocks exiting the launched game"

			if ! "$PGREP" -a "vrcompositor" >/dev/null ; then
				writelog "INFO" "${FUNCNAME[0]} - Vrcompositor not running, so starting SteamVR now:"
				if ! "$STEAM" "${STEAMVRARGS[@]}" 2>/dev/null >/dev/null ; then
					writelog "SKIP" "${FUNCNAME[0]} - Starting SteamVR FAILED - skipping SBS-VR"
					echo "RUNSBSVR=\"0\"" > "$VRINITLOCK"
				else
					writelog "INFO" "${FUNCNAME[0]} - Started SteamVR"
					SVRJUSTSTARTED=1
				fi
			fi

			if ! "$PGREP" -a "vrstartup" >/dev/null ; then
				writelog "INFO" "${FUNCNAME[0]} - No vrstartup process running"
			else
				if [ "$SVRJUSTSTARTED" -eq 1 ]; then
					writelog "INFO" "${FUNCNAME[0]} - SteamVR initializing"
					while true; do
						writelog "INFO" "${FUNCNAME[0]} - Waiting for end of vrstartup"
						if ! "$PGREP" -a "vrstartup" >/dev/null ; then
							break
						fi
						if [ -f "$CLOSETMP" ]; then
							writelog "WAIT" "${FUNCNAME[0]} - ${PROGNAME,,} is just closing - leaving loop"
							break
						fi
					done
				else
					writelog "SKIP" "${FUNCNAME[0]} - Vrstartup found, but we didn't start SteamVR before! - skipping SBS-VR - just in case"
					echo "RUNSBSVR=\"0\"" > "$VRINITLOCK"
				fi
			fi

			if [ "$SVRJUSTSTARTED" -eq 1 ]; then
				while true; do
					if ! "$PGREP" -a "vrstartup" >/dev/null ; then
						writelog "WAIT" "${FUNCNAME[0]} - No vrstartup instance running"
						break
					fi
					if [ -f "$CLOSETMP" ]; then
						writelog "WAIT" "${FUNCNAME[0]} - ${PROGNAME,,} is just closing - leaving loop"
						break
					fi
					writelog "WAIT" "${FUNCNAME[0]} - Waiting for end of vrstartup"
				done
			fi

			if [ "$SVRJUSTSTARTED" -eq 1 ]; then
			MAXWAIT=10
			COUNTER=0
				while ! "$PGREP" -a "vrcompositor" >/dev/null; do
					if [ -f "$CLOSETMP" ]; then
						writelog "WAIT" "${FUNCNAME[0]} - ${PROGNAME,,} is just closing - leaving loop"
						break
					fi
					if [[ "$COUNTER" -ge "$MAXWAIT" ]]; then
						writelog "SKIP" "${FUNCNAME[0]} - ERROR - timeout waiting for SteamVR - exit"
						"$PKILL" -f "$VRVIDEOPLAYER"
						echo "RUNSBSVR=\"0\"" > "$VRINITLOCK"
						exit 1
					fi
					writelog "WAIT" "${FUNCNAME[0]} - Sec $COUNTER/$MAXWAIT waiting for vrcompositor"
					COUNTER=$((COUNTER+1))
					sleep 1
				done
			else
				writelog "INFO" "${FUNCNAME[0]} - we didn't start SteamVR before so no need to wait for vrcompositor"
			fi

			if "$PGREP" -a "vrcompositor" >/dev/null ; then
				while true; do
					if ! "$PGREP" -a "vrstartup" >/dev/null ; then
						writelog "WAIT" "${FUNCNAME[0]} - No vrstartup instance running - looks good"
						break
					fi
					sleep 1
					writelog "WAIT" "${FUNCNAME[0]} - Waiting for end of vrstartup"
				done

				writelog "INFO" "${FUNCNAME[0]} - Success - SteamVR running"
				sleep 1 # better safe than sorry

			else
				writelog "SKIP" "${FUNCNAME[0]} - SteamVR start failed - vrcompositor still not running - skipping SBS-VR!"
				echo "RUNSBSVR=\"0\"" > "$VRINITLOCK"
			fi
		fi
	fi
}

function dlOvrFSR {
	function getLatestOVRFSR {
		basename "$("$WGET" -q "$OVRFSRURL" -O - 2> >(grep -v "SSL_INIT") | grep -E 'releases.*download.*zip' | cut -d '"' -f2 | head -n1)"
	}

	DLDST="$STLDLDIR/$OVFS"
	mkProjDir "$DLDST"
	OVRFSRZIP="$(getLatestOVRFSR)"
	FSR1="${OVRFSRZIP//openvr_}"
	FSRV="${FSR1%.*}"

	if [ ! -f "$DLDST/$OVRFSRZIP" ]; then
		notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$OVRFSRZIP")" "S"
		dlCheck "${OVRFSRURL}/download/$FSRV/$OVRFSRZIP" "$DLDST/$OVRFSRZIP" "X" "Downloading '$OVRFSRZIP'"
		notiShow "$(strFix "$NOTY_DLCUSTOMPROTON2" "$OVRFSRZIP")" "S"
	fi

	if [ ! -s "$DLDST/$OVRFSRZIP" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Downloaded file '$DLDST/$OVRFSRZIP' is empty - removing"
		rm "$DLDST/$OVRFSRZIP" 2>/dev/null
	else
		notiShow "$(strFix "$NOTY_DLCUSTOMPROTON3" "$OVRFSRZIP")" "S"
		writelog "INFO" "${FUNCNAME[0]} - Download of '$OVRFSRZIP' to '$DLDST' was successful"
	 	OMSRC="$DLDST/$OVRMOD"
	 	if [ -f "$OMSRC" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Removing old '$OMSRC'"
			rm "$OMSRC" 2>/dev/null
	 	fi
	 	OASRC="$DLDST/$OVRA"
	 	if [ -f "$OASRC" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Removing old '$OASRC'"
			rm "$OASRC" 2>/dev/null
	 	fi

	 	"$UNZIP" -q "$DLDST/$OVRFSRZIP" -d "$DLDST" 2>/dev/null
		writelog "INFO" "${FUNCNAME[0]} - Extracted '$OVRFSRZIP' to '$DLDST'"
	fi
}

function checkOpenVRFSR {
	OVRFSENA="${OVFS}-${PROGNAME,,}-enabled.txt"
	OVRFP="$EFD/$OVRFSENA"
	OVRAO="openvr_api.orig.dll"

	if [ "$USEOPENVRFSR" -eq 1 ]; then
		OVRPATH="$(find "$EFD" -name "*$OVRA" | head -n1)"
		if [ -f "$OVRPATH" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found '$OVRA' under '$OVRPATH'"
			OVRASRC="$STLDLDIR/$OVFS/$OVRA"
			if [ ! -f "$OVRASRC" ]; then
				writelog "INFO" "${FUNCNAME[0]} - No $OVFS source dll found under '$OVRASRC' - Trying automatic download"
				StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "$OVRFSRZIP")" "dlOvrFSR" "DownloadOvrFSRStatus"
			fi

			if [ ! -f "$OVRASRC" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - Still no $OVFS source dll found under '$OVRASRC' - automatic download failed!"
			else
				OVRDIR="${OVRPATH%/*}"
				writelog "INFO" "${FUNCNAME[0]} - Moving original '$OVRPATH' to $OVRDIR/$OVRAO"
				mv "$OVRPATH" "$OVRDIR/$OVRAO"
				writelog "INFO" "${FUNCNAME[0]} - Copying '$OVRASRC' to '$OVRPATH'"
				cp "$OVRASRC" "$OVRPATH"
				echo "${OVRPATH//$EFD/}" > "$OVRFP"

				OVRMODSRC="$STLDLDIR/$OVFS/$OVRMOD"
				if [ -f "$OVRMODSRC" ]; then
					if [ ! -f "$OVRFP" ]; then
						writelog "WARN" "${FUNCNAME[0]} - No install 'log' found - should not happen here!"
					else
						OVRMODDST="${OVRPATH%/*}/$OVRMOD"
						if [ -f "$OVRMODDST" ]; then
							writelog "INFO" "${FUNCNAME[0]} - Moving '$OVRMODDST' to '${OVRMODDST}_old'"
							mv "$OVRMODDST" "${OVRMODDST}_old"
						fi
						writelog "INFO" "${FUNCNAME[0]} - Copying '$OVRMODSRC' to '$OVRMODDST'"
						cp "$OVRMODSRC" "$OVRMODDST"
						echo "${OVRMODDST//$EFD/}" >> "$OVRFP"
					fi
				fi

				if [ -f "$OVRFP" ]; then
					writelog "INFO" "${FUNCNAME[0]} - '$OVFS' installation (hopefully) succeeded!"
				fi
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - USEOPENVRFSR is enabled, but no OVRA found in '$EFD'"
		fi
	else
		if [ -f "$OVRFP" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found old '$OVFS' installation in '$EFD' - Removing"
			while read -r line; do
				if grep -q "$OVRA" <<< "$line"; then
					OVRAOP="$EFD/${line//$OVRA/$OVRAO}"
				if [ -f "$OVRAOP" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Restoring original $OVRA from '$OVRAOP'"
						mv "$OVRAOP" "$EFD/$line" 2>/dev/null
					else
						writelog "SKIP" "${FUNCNAME[0]} - No '$OVRAOP' found to restore"
					fi
				else
					writelog "INFO" "${FUNCNAME[0]} - Removing '$EFD/$line'"
					rm "$EDF/$line" 2>/dev/null
				fi
			done < "$OVRFP"
			rm "$OVRFP" 2>/dev/null
		fi
	fi
}

function getGamePidFromFile {
	if [ -z "$GAMEWINPID" ] && [ -f "$TEMPGPIDFILE" ]; then
		loadCfg "$TEMPGPIDFILE"
		rm "$TEMPGPIDFILE"
		writelog "INFO" "${FUNCNAME[0]} - Got GAMEWINPID '$GAMEWINPID' from temp file"
	fi
}

function getGamePidFromWindowName {
	if [ -n "$GAMEWINDOW" ] && [ "$GAMEWINDOW" != "$NON" ]; then
		# xdotool at least doesn't like '(' and ')', so cutting them out as a partial match should be enough
		writelog "INFO" "${FUNCNAME[0]} - Trying to get the PID of the window '$GAMEWINDOW'"
		TESTPID="$("$XWININFO" -name "${GAMEWINDOW//\"/}" -wm | grep "Process id:" | awk -F 'Process id: ' '{print $2}' | cut -d ' ' -f1)"
		if [ -n "$TESTPID" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found the PID '$TESTPID' for the window '$GAMEWINDOW'"
			echo "$TESTPID"
		fi
	fi
}

function GAMEPID {
	if	[ "$USECUSTOMCMD" -eq 1 ] && [ "$ONLY_CUSTOMCMD" -eq 1 ]; then
		"$PGREP" -a "" | grep "${CUSTOMCMD##*/}" | grep "Z:" | grep "\.exe" | grep -v "CrashHandler" | cut -d ' ' -f1 | tail -n1
	else
		if [ -n "$WAITFORTHISPID" ] && [ "$WAITFORTHISPID" != "$NON" ]; then
			GAMPI="$("$PIDOF" "$WAITFORTHISPID" | cut -d ' ' -f1)"
		else
			if [ -n "$GAMEWINDOW" ] && [ "$GAMEWINDOW" != "$NON" ]; then
				GAMPI="$("$XWININFO" -name "${GAMEWINDOW//\"/}" -wm | grep "Process id:" | awk -F 'Process id: ' '{print $2}' | cut -d ' ' -f1)"
				writelog "INFO" "${FUNCNAME[0]} - Found gamewindow '$GAMEWINDOW' PID $GAMPI"
			else
				# very likely this needs to be improved/changed
				GAMPI="$("$PGREP" -a "" | grep "$GE" | grep "Z:" | grep "\.exe" | grep -v "CrashHandler" | cut -d ' ' -f1 | tail -n1)"
			fi
		fi
		echo "$GAMPI"
	fi
}

function waitForGamePid {
	if [ -n "$WAITFORTHISPID" ] && [ "$WAITFORTHISPID" != "$NON" ]; then
		writelog "WAIT" "${FUNCNAME[0]} - Waiting for alternative process WAITFORTHISPID '$WAITFORTHISPID'"
	elif [ "$USECUSTOMCMD" -eq 1 ] && [ "$ONLY_CUSTOMCMD" -eq 1 ]; then
		writelog "WAIT" "${FUNCNAME[0]} - Waiting for custom process CUSTOMCMD '$CUSTOMCMD'"
	fi

	while [ -z "$(GAMEPID)" ]; do
		writelog "WAIT" "${FUNCNAME[0]} - Waiting for game process $(GAMEPID)"
		sleep 1
	done
	writelog "INFO" "${FUNCNAME[0]} - Game process found at $(GAMEPID)"
}

function getGameWinXIDFromPid {
	GPID="$1"
	while read -r WINS; do
		if [ "$("$XPROP" -id "$(printf 0x%x'\n' "$WINS")" | grep "_NET_WM_STATE(ATOM)" -c)" -ge 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found a controllable windowid"
			WSIZ="$(getWindowHeight "$WINS")"
			if [ "$WSIZ" -lt "$MINVRWINH" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - '$(printf 0x%x'\n' "$WINS")' height is less than $MINVRWINH - this is very likely not the game window - skipping"
			else
				writelog "INFO" "${FUNCNAME[0]} Found window id $(printf 0x%x'\n' "$WINS") for '$GE' running with PID '$GPID'"
				echo "$WINS"
				break
			fi
		fi
	done <<< "$("$XDO" search --pid "$GPID")"
}

function getGameWinNameFromXid {
	"$XDO" getwindowname "$1"
}

function getGameWindowPID {
	if [ "$MO2MODE" == "gui" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Skipping check for window pid, because MO2MODE is '$MO2MODE'"
	fi

	if [ -n "$GAMEWINPID" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Already have the GAMEWINPID '$GAMEWINPID'"
		echo "$GAMEWINPID"

		return
	fi

	MAXWAIT=20
	COUNTER=0
	TESTPID="$NON"
	WASCLOSED=0

	touch "$PIDLOCK"

	while [ "$COUNTER" -lt "$MAXWAIT" ]; do
		if [ -f "$CLOSETMP" ]; then
			writelog "WAIT" "${FUNCNAME[0]} - ${PROGNAME,,} is just closing - leaving loop"
			WASCLOSED=1
			break
		fi
		TESTPID="$("$XDO" getactivewindow getwindowpid)"
		SYMCWD="$(readlink "/proc/$TESTPID/cwd")"
		SYMEXE="$(readlink "/proc/$TESTPID/exe")"

		if [ -z "$TESTPID" ]; then
			continue
		fi

		if [ -n "$HAVPA" ]; then
			writelog "WAIT" "${FUNCNAME[0]} - Found PID '$TESTPID' for game 'HAVPA' '$HAVPA' - leaving loop"
			FOUNDWIN="$YES"
			break;
		elif [ -n "$EXECUTABLE" ] && [ "$EXECUTABLE" == "$(cat "/proc/$TESTPID/comm")" ]; then
			writelog "WAIT" "${FUNCNAME[0]} - Found PID '$TESTPID' for game 'EXECUTABLE' '$EXECUTABLE' - leaving loop"
			FOUNDWIN="$YES"
			break;
		elif [ -n "$GE" ] && [ "$GE" == "$(cut -d '.' -f1 < "/proc/$TESTPID/comm")" ]; then
			writelog "WAIT" "${FUNCNAME[0]} - Found PID '$TESTPID' for game executable 'GE' '$GE' - leaving loop"
			FOUNDWIN="$YES"
			break;
		else
			# might not even be required anymore - maybe remove later:
			if [[ "$SYMCWD" == "$EFD" ]] && [[ "$SYMEXE" != *"$YAD"* ]]; then
				writelog "WAIT" "${FUNCNAME[0]} - Found PID '$TESTPID' for CWD '$SYMCWD' with EXE '$SYMEXE' - leaving loop"
				FOUNDWIN="$YES"
				break;
			fi
			if [ -n "$STEAM_COMPAT_CLIENT_INSTALL_PATH" ] && [[ "$SYMCWD" == "$STEAM_COMPAT_CLIENT_INSTALL_PATH" ]] && [[ "$SYMEXE" != *"$YAD"* ]]; then
				writelog "WAIT" "${FUNCNAME[0]} - Found PID '$TESTPID' for STEAM_COMPAT_CLIENT_INSTALL_PATH '$SYMCWD' with EXE '$SYMEXE' - leaving loop"
				FOUNDWIN="$YES"
				break;
			fi
		fi

		writelog "WAIT" "${FUNCNAME[0]} - Sec $COUNTER/$MAXWAIT Game Window with pwd '$EFD' not yet in front"
		COUNTER=$((COUNTER+1))
		sleep 1
	done

	rm "$PIDLOCK" 2>/dev/null

	if [ "$FOUNDWIN" == "$YES" ]; then
		if [ "$TESTPID" == "$NON" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - FAIL - Found PID returned but it is empty: '$TESTPID'"
		fi

		writelog "INFO" "${FUNCNAME[0]} - Found PID '$TESTPID' for running exe '$(readlink "/proc/$TESTPID/exe")'"
		echo "$TESTPID"
	elif [ "$WASCLOSED" -eq 0 ]; then
		writelog "SKIP" "${FUNCNAME[0]} - ERROR - timeout waiting for '$EFD' window"
		echo "$NON"
	fi
}

function storeGameWindowNameMeta {
	GAMEWINDOW="$1"
	if [ -n "$GAMEWINDOW" ] && [ "$GAMEWINDOW" != "$NON" ] && [ "$STLPLAY" -eq 0 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Found game window name '$GAMEWINDOW' - saving into metadata file '$GEMETA/$AID.conf' for game '$MGNA ($AID)'"
		touch "$FUPDATE"
		touch "$GEMETA/$AID.conf"
		updateConfigEntry "GAMEWINDOW" "$GAMEWINDOW" "$GEMETA/$AID.conf"
	fi
}

function pickGameWindowNameMeta {
	if [ -n "$1" ]; then
		AID="$1"
	fi
	if [ -n "$2" ]; then
		GN="$2"
	fi
	fixShowGnAid
	writelog "INFO" "${FUNCNAME[0]} - Picking 'GAMEWINDOW' for '$SGNAID'"

	GAMEWINXID="$(printf 0x%x'\n' "$("$XDO" selectwindow)")"
	GAMEWINDOW="$("$XDO" getwindowname "$GAMEWINXID")"
	storeGameWindowNameMeta "$GAMEWINDOW"
	if [ -n "$AID" ]; then
		notiShow "$(strFix "$NOTY_PICKWINS" "$GAMEWINDOW" "$GN" "$AID")"
		writelog "INFO" "${FUNCNAME[0]} - Picked 'GAMEWINDOW $GAMEWINDOW' for '$SGNAID'"
	else
		notiShow "$(strFix "$NOTY_PICKWINN" "$GAMEWINDOW")"
	fi
}

# General function for the "steamtinkerlaunch list <arg> function"
# Can take two types of commands:
# - `steamtinkerlaunch list owned/installed/non-steam` - Returns "Game Name (AppID)"
# - `steamtinkerlaunch list owned/installed/non-steam id/name/path/full` - Returns either AppID, Game Name, Game Paths, or all in the format "Game Name (AppID) -> /path/to/game"
#
# This function is not very efficient, Non-Steam Games in particular are inefficient because we read shortcuts.vdf each time we want to parse info
# We parse it once to get the IDs, then again in each `getTitleFromID` and `getGameDir` call. This makes it pretty slow
# It works for now, but in future we should enhance it
#
# One potential way to enhance this function is to split it out into a separate function for each filter type, but we would need
# to consider how this function is used by other parts of the codebase and if that could be disruptive.
function listSteamGames {
	function getGameCount {
		TOTALGAMESOWNEDPRINTFSTR=""

		if [ "$LSFILTER" == "owned" ] || [ "$LSFILTER" == "o" ]; then
			TOTALGAMESOWNEDPRINTFSTR="Total games owned"
		elif [ "$LSFILTER" == "non-steam" ] || [ "$LSFILTER" == "nsg" ]; then
			TOTALGAMESOWNEDPRINTFSTR="Total Non-Steam Games in library"
		else
			TOTALGAMESOWNEDPRINTFSTR="Total games installed"
		fi

		printf "${TOTALGAMESOWNEDPRINTFSTR}: %s\n" "${#LISTAIDSARR[@]}"
	}

	LSFILTER="$1"  # e.g. "owned", "installed", "non-steam"
	LSTYPE="$2"  # e.g. "id", "name", "path", "count", "full"
	LISTAIDS=""

	SEARCHSTEAMSHORTCUTS="0"

	if [ "$LSFILTER" == "owned" ] || [ "$LSFILTER" == "o" ]; then
		LISTAIDS="$( getOwnedAids )"
	elif [ "$LSFILTER" == "installed" ] || [ "$LSFILTER" == "i" ]; then
		LISTAIDS="$( listInstalledGameIDs )"
	elif [ "$LSFILTER" == "non-steam" ] || [ "$LSFILTER" == "nsg" ]; then
		LISTAIDS="$( listNonSteamGameIDs )"
		SEARCHSTEAMSHORTCUTS="1"  # Only search Steam Shortcuts if we passed that filter type
	else
		writelog "INFO" "${FUNCNAME[0]} - Unknown argument passed to 'list' command - '$LSFILTER'"
		echo "unknown argument passed to 'list' command - '$LSFILTER'"

		exit
	fi

	if [ -z "$LISTAIDS" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - No games found for given filter '$LSFILTER'"
		echo "No games found for filter '$LSFILTER'."

		exit
	fi

	readarray -t LISTAIDSARR <<<"$LISTAIDS"

	if [ "$LSTYPE" == "id" ]; then
		for AID in "${LISTAIDSARR[@]}"; do
			echo "$AID"
		done
	elif [ "$LSTYPE" == "name" ]; then
		for AID in "${LISTAIDSARR[@]}"; do
			getTitleFromID "${AID}" "${SEARCHSTEAMSHORTCUTS}"
		done
	elif [ "$LSTYPE" == "path" ]; then
		if [ "$LSFILTER" == "owned" ] || [ "$LSFILTER" == "o" ]; then
			echo "Cannot use 'path' option when returning 'owned' games, as not all owned games will have an installation path!"
			echo "Use another option instead, or leave blank to only return path for games which have an installation path."

			exit
		fi
		for AID in "${LISTAIDSARR[@]}"; do
			getGameDir "$AID" "1" "${SEARCHSTEAMSHORTCUTS}"
		done
	elif [ "$LSTYPE" == "count" ]; then
		printf "\n%s\n" "$( getGameCount )"
	elif [ "$LSTYPE" == "full" ] || [ -z "$LSTYPE" ]; then  # This is the default if id/name/path/full is not passed
		for AID in "${LISTAIDSARR[@]}"; do
			GAMDIR="$( getGameDir "$AID" "" "${SEARCHSTEAMSHORTCUTS}" )"
			GAMDIREXISTS=$?

			# Only display game dir if the game is installed, i.e. if getGameDir does not return 1
			# This means we won't return an error if we're returning OWNED games, as some owned games may not have paths
			if [ "$GAMDIREXISTS" -eq 1 ]; then
				GAMNAM="$( getTitleFromID "$AID" "${SEARCHSTEAMSHORTCUTS}" )"
				GAMNAMEXISTS=$?
				if [ "$GAMNAMEXISTS" -eq 1 ]; then
					echo "$AID"  # Game name unknown, probably never installed before? Just return AppID in this case
				else
					echo "$GAMNAM ($AID)"
				fi
			else
				echo "$GAMDIR"
			fi
		done

		printf "\n%s\n" "$( getGameCount )"  # Show total for "full"
	fi
}

# TODO do we want a way to specify that this function should only return Steam or Non-Steam AppIDs?
function getIDFromTitle {
	SEARCHSTEAMSHORTCUTS="${2:-0}"  # Default to not searching Steam shortcuts
	if [ -z "$1" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - No game title was provided to search on -- Nothing to do!"
		echo "A Game Title (part of it might be enough) is required as argument"

		return 1
	fi
	# Check installed game appmanifests for name matches
	FOUNDMATCHES=()

	# Steam games
	while read -r APPMA; do
		APPMATITLE="$( getValueFromAppManifest "name" "$APPMA" )"
		if [[ ${APPMATITLE,,} == *"${1,,}"* ]]; then
			APPMAAID="$( basename "${APPMA%.*}" | cut -d '_' -f2 )"
			FOUNDGAMNAM="$( printf "%s\t\t(%s)" "$APPMAAID" "$APPMATITLE" )"  # Doing it this way makes tabs even for some reason
			FOUNDMATCHES+=( "$FOUNDGAMNAM" )
		fi
	done <<< "$( listAppManifests )"

	# Steam shortcuts
	if [ "$SEARCHSTEAMSHORTCUTS" -eq "1" ] && haveAnySteamShortcuts ; then
		while read -r SCVDFE; do
			SVDFENAME="$( parseSteamShortcutEntryAppName "$SCVDFE" )"
			SVDFEAID="$( parseSteamShortcutEntryAppID "$SCVDFE" )"

			if [[ ${SVDFENAME,,} == *"${1,,}"* ]]; then
				FOUNDGAMNAM="$( printf "%s\t\t(%s)" "$SVDFEAID" "$SVDFENAME" )"
				FOUNDMATCHES+=( "$FOUNDGAMNAM" )
			fi
		done <<< "$( getSteamShortcutHex )"
	fi

	if [ "${#FOUNDMATCHES[@]}" -gt 0 ]; then
		printf "%s\n" "${FOUNDMATCHES[@]}"
	else
		echo "Could not find AppID for name '$1'."
	fi
}

function getTitleFromID {
	SEARCHSTEAMSHORTCUTS="${2:-0}"  # Default to not searching Steam shortcuts
	FOUNDGAMETITLE=""

	if [ -z "$1" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - Did not get an AppID to search on -- Nothing to do!"
		echo "A Game ID is required as argument"

		return 1;
	fi

	# Search meta file if we have one for Game Title
	if [ -f "$GEMETA/${1}.conf" ]; then
		if grep -q "^NAME" "$GEMETA/${1}.conf"; then
			FOUNDGAMETITLE="$( grep "^NAME" "$GEMETA/${1}.conf" | cut -d '"' -f2 )"
		else
			FOUNDGAMETITLE="$( getAppInfoData "$1" "name" )"
		fi
	fi

	# Try to get title from Steam appmanifest if we didn't find Game Title in meta file
	if [ -z "$FOUNDGAMETITLE" ]; then
		GAMEMANIFEST="$( listAppManifests | grep -m1 "appmanifest_${1}.acf" )"

		if [ -n "$GAMEMANIFEST" ]; then
			FOUNDGAMETITLE="$( getValueFromAppManifest "name" "$GAMEMANIFEST" )"
		fi
	fi

	# If we still haven't found the Game Title in the game meta file or appmanifest, check Steam Shortcuts if the option is enabled and if we have any
	if [ -z "$FOUNDGAMETITLE" ] && [ "$SEARCHSTEAMSHORTCUTS" -eq "1" ] && haveAnySteamShortcuts ; then
		while read -r SCVDFE; do
			SVDFENAME="$( parseSteamShortcutEntryAppName "$SCVDFE" )"
			SVDFEAID="$( parseSteamShortcutEntryAppID "$SCVDFE" )"

			if [ "$SVDFEAID" -eq "$1" ]; then
				FOUNDGAMETITLE="$SVDFENAME"
				break
			fi
		done <<< "$( getSteamShortcutHex )"
	fi

	# If we didn't find the name in the STL meta file, Steam appmanifest, or shortcuts.vdf, give up
	if [ -z "$FOUNDGAMETITLE" ]; then
		echo "No Title found for '$1'"
		return 1
	fi

	echo "$FOUNDGAMETITLE"
}

# Relies on game executable existing in STL meta file (i.e. any game launched before with STL)
function getGameExe {
	SEARCHSTEAMSHORTCUTS="${2:-0}"  # Default to not searching Steam shortcuts

	if [ -z "$1" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - Called getGameExe without AppID -- Nothing to do!"
		echo "A Game ID is required as argument"

		return 1
	fi

	INAID="$1"
	EXE=""
	NOTFOUNDSTR="No Executable or SteamTinkerLaunch file found for '$1'"

	# Try to get appid from game name -- If multiple values, just pick first one
	if ! [ "$INAID" -eq "$INAID" ] 2>/dev/null; then
		INAID="$( getIDFromTitle "$INAID" | head -n1 )"
		INAID="$( trimWhitespaces "${INAID%(*}")"
	fi

	EXEGAMNAM="$( getTitleFromID "$INAID" )"

	# Try to get game EXE from STL meta file
	if [ -n "$INAID" ] && [ -f "$GEMETA/${INAID}.conf" ]; then
		# Some games might only have "EXECUTABLE" in their meta conf file
		EXE="$(grep "^EXECUTABLE" "$GEMETA/${INAID}.conf" | cut -d '"' -f2)"
		if [ -z "${EXE}" ]; then
			EXE="$(grep "^GAMEEXE" "$GEMETA/${INAID}.conf" | cut -d '"' -f2)"
		fi

		if [ -n "$EXE" ]; then
			EXE="$EXEGAMNAM ($INAID) -> $( getGameDir "$INAID" "only" )/$EXE"
		fi
	fi

	# Search on game shortcuts if EXE still not found
	if [ -z "$EXE" ] && [ "$SEARCHSTEAMSHORTCUTS" -eq 1 ] && haveAnySteamShortcuts ; then
		# Have to search on all shortcuts because we need to check game name as fallback
		while read -r SCVDFE; do
			SCVDFEAID="$( parseSteamShortcutEntryAppID "$SCVDFE" )"
			SCVDFENAME="$( parseSteamShortcutEntryAppName "$SCVDFE" )"
			SCVDFEEXE="$( parseSteamShortcutEntryExe "$SCVDFE" )"

			if [ "$SCVDFEAID" -eq "$1" ] 2>/dev/null || [[ ${SCVDFENAME,,} == *"${1,,}"* ]]; then
				SCVDFEEXE="${SCVDFEEXE#\"}"
				EXE="$SCVDFENAME ($SCVDFEAID) -> ${SCVDFEEXE%\"}"
				break
			fi
		done <<< "$( getSteamShortcutHex )"
	fi

	echo "${EXE:-$NOTFOUNDSTR}"
}

function getCompatData {
	SEARCHSTEAMSHORTCUTS="${2:-0}"  # Default to not searching Steam shortcuts

	if [ -z "$1" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - No AppID or Game Title provided -- Nothing to do!"
		echo "A Game ID or Game Title is required as argument"

		return 1
	fi

	# SteamTinkerLaunch stores a symlinkk to the prefix of each game launched with it,
	# so if we have this folder we can try to find if we have a symlink to the desired game's prefix as it may be faster
	if [ -d "$STLCOMPDAT" ]; then
		# Skips games which have ';' in the name (i.e. "ROBOTICS;NOTES"), as this throws off the 'cut' command, and instead we fall back to the getGameDir check
		if [ "$1" -eq "$1" ] 2>/dev/null; then  # This is for AppID check (-eq will fail if not integer expression)
			while read -r "complink"; do
				TEST="$(readlink "$complink" | grep "/$1$")"
				if [ -d "$TEST" ] && ! [[ "${complink##*/}" == *";"* ]]; then
					FCOMPDAT="${complink##*/};$TEST"
					break
				fi
			done <<< "$(find "$STLCOMPDAT")"
		else  # This is for Game Name checks
			TEST="$(find "$STLCOMPDAT" -iname "*${1}*" | head -n1)"
			if [ -n "$TEST" ] && ! [[ "${TEST##*/}" == *";"* ]]; then
				FCOMPDAT="${TEST##*/};$(readlink "$TEST")"
			fi
		fi

		# If we found a matching compatdata, return it here
		if [ -n "$FCOMPDAT" ]; then
			COMPATGAMENAME="$( echo "$FCOMPDAT" | cut -d ";" -f 1 )"
			COMPATGAMEPATH="$( echo "$FCOMPDAT" | cut -d ";" -f 2 )"
			COMPATGAMEAID="$( basename "$COMPATGAMEPATH" )"

			echo "${COMPATGAMENAME} (${COMPATGAMEAID}) -> ${COMPATGAMEPATH}"

			return 0
		fi
	fi

	unset FCOMPDAT

	# If no symlink found in STL dir, check the game's library folder, with fallback to the Steam root library folder (i.e. Non-Steam Games)
	writelog "INFO" "${FUNCNAME[0]} - Could not find compatdata named in STL symlink dir, searching with getGameLibraryFolder..."
	SEARCHGAMEDIR="$( getGameDir "$1" )"

	COMPATGAMESTR=""  # Final output string
	NOTFOUNDSTR="No $CODA dir found for '$1'"
	COMPATGAMEPATH="${SEARCHGAMEDIR##*-> }"
	if [ -d "$COMPATGAMEPATH" ]; then
		COMPATGAMENAME="$( echo "${SEARCHGAMEDIR%(*}" | xargs )" # e.g. TEKKEN 7
		COMPATGAMEAID="$( echo "$SEARCHGAMEDIR" | grep -oE '\([^)]+\)' | tail -n1 | sed 's:(::g;s:)::g' )"  # e.g. 389730

		LIFOCOMPATDIR="$( realpath "$COMPATGAMEPATH/../../$CODA/$COMPATGAMEAID" )"
		SROOTCOMPATDIR="$SROOT/$SA/$CODA/$COMPATGAMEAID"

		COMPATDATADIR="$( [ -d "$LIFOCOMPATDIR" ] && echo "$LIFOCOMPATDIR" || echo "$SROOTCOMPATDIR" )"
		if [ -d "$COMPATDATADIR" ]; then
			COMPATGAMESTR="$COMPATGAMENAME ($COMPATGAMEAID) -> $COMPATDATADIR"
		fi
	fi

	# Check Steam Shortcuts for games never launched with STL
	if [ ! -d "$SEARCHGAMEDIR" ] && [ "$SEARCHSTEAMSHORTCUTS" -eq 1 ] && haveAnySteamShortcuts ; then
		while read -r SCVDFE; do
			SCVDFEAID="$( parseSteamShortcutEntryAppID "$SCVDFE" )"
			SCVDFENAME="$( parseSteamShortcutEntryAppName "$SCVDFE" )"

			## If we have a match, build a hardcoded compatdata pointing at the Steam Root compatdata dir and if it exists, return that
			## Seems like this is always where Steam generates compatdata for Non-Steam Games
			## may instead be primary drive which defaults to Steam Root, but for now looks like Steam Root is the main place, so should work most of the time
			if [ "$SCVDFEAID" -eq "$1" ] 2>/dev/null || [[ ${SCVDFENAME,,} == *"${1,,}"* ]]; then
				SCVDFECODA="$SROOT/$SA/$CODA/${SCVDFEAID}"
				if [ -d "$SCVDFECODA" ]; then
					COMPATGAMESTR="$SCVDFENAME ($SCVDFEAID) -> $SCVDFECODA"
				fi
				break
			fi
		done <<< "$( getSteamShortcutHex )"
	fi

	echo "${COMPATGAMESTR:-$NOTFOUNDSTR}"
}

# Credit to StackOverflow community wiki
function trimWhitespaces {
	INSTR="$*"
	INSTR="${INSTR#"${INSTR%%[![:space:]]*}"}"  # remove leading whitespace characters
	INSTR="${INSTR%"${INSTR##*[![:space:]]}"}"  # remove trailing whitespace characters
	echo "$INSTR"
}

# Extracts a value from a given App Manifest file path
function getValueFromAppManifest {
	KEY="$1"
	APPMA="$2"
	EXTVAL="$( grep -m1 "$KEY" "$APPMA" | sed "s-\t- -g;s-\"${KEY}\"--g;s-\"--g" )"  # xargs gets angry when names have single quotes, e.g. "Shantae and the Pirate's Curse"

	trimWhitespaces "$EXTVAL"
}

# Returns game install directory in the format "Game (AppID) -> /path/to/gamefolder"
function getGameDir {
	ONLYPATH="$2"
	SEARCHSTEAMSHORTCUTS="${3:-0}"  # Default to not searching Steam shortcuts
	FOUNDINSTEAMSHORTCUTS=0

	# Exit early if no search name/appid is given
	if [ -z "$1" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - Missing GameID or Game Title to search on -- Nothing to do!"
		echo "A Game ID or Game Title is required as argument"

		return 1
	fi

	# First assume user entered AppID, then if no AppManifest found, try get AppID from name and search on that
	SEARCHMANIFEST="$( listAppManifests | grep -m1 "appmanifest_${1}.acf" )"
	if [ -z "$SEARCHMANIFEST" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Could not find App Manifest with entered argument '$1' - Assuming it is a game title and trying to find its AppID from title"
		SEARCHGETIDFROMTITLE="$( getIDFromTitle "$1" | head -n1 )"

		writelog "INFO" "${FUNCNAME[0]} - called 'getIDFromTitle' for argument '$1', it returned '$SEARCHGETIDFROMTITLE'"
		SEARCHAID="$( echo "$SEARCHGETIDFROMTITLE" | cut -d "(" -f 1 | xargs)"

		writelog "INFO" "${FUNCNAME[0]} - Extracted AppID from 'getIDFromTitle' result is '$SEARCHAID' - Searching for App Manifest with this AppID"

		SEARCHMANIFEST="$( listAppManifests | grep -m1 "appmanifest_${SEARCHAID}.acf" )"

		# This nesting is ugly, but only exists for logging purposes
		if [ -z "$SEARCHMANIFEST" ]; then
			if [ "$SEARCHSTEAMSHORTCUTS" -eq 0 ]; then
				writelog "ERROR" "${FUNCNAME[0]} - Could not find game directory for '$1' - Maybe it is not installed"
			else
				writelog "WARN" "${FUNCNAME[0]} - Could not find game directory for '$1' - Will search on Steam Shortcuts next"
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - Found matching App Manifest '$SEARCHMANIFEST'"
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - Found matching App Manifest file for presumed entered AppID '$1' - Manifest file is '$SEARCHMANIFEST'"
	fi

	APPMAINSTDIR="$( getValueFromAppManifest "installdir" "$SEARCHMANIFEST" 2>/dev/null )"
	APPMALIBFLDR="$( dirname "$SEARCHMANIFEST" )"
	GAMINSTDIR="$APPMALIBFLDR/common/$APPMAINSTDIR"
	MUSINSTDIR="$APPMALIBFLDR/music/$APPMAINSTDIR"  # Fixes a not found error for installed soundtracks

	# If still not found, optionally search Steam shortcuts
	if [ ! -d "$GAMINSTDIR" ] && [ "$SEARCHSTEAMSHORTCUTS" -eq 1 ] && haveAnySteamShortcuts ; then
		while read -r SCVDFE; do
			SCVDFEAID="$( parseSteamShortcutEntryAppID "$SCVDFE" )"
			SCVDFENAME="$( parseSteamShortcutEntryAppName "$SCVDFE" )"
			SCVDFEEXE="$( parseSteamShortcutEntryExe "$SCVDFE" )"

			## If we have a match, build a hardcoded compatdata pointing at the Steam Root compatdata dir and if it exists, return that
			## Seems like this is always where Steam generates compatdata for Non-Steam Games
			## may instead be primary drive which defaults to Steam Root, but for now looks like Steam Root is the main place, so should work most of the time
			if [ "$SCVDFEAID" -eq "$1" ] 2>/dev/null || [[ ${SCVDFENAME,,} == *"${1,,}"* ]]; then
				APPMAGN="${SCVDFENAME}"
				APPMAAID="${SCVDFEAID}"
				GAMINSTDIR="$( dirname "${SCVDFEEXE}" )"  # Could still fail if EXE dir no longer exists, but edge case

				# TODO make this a function later, we use this a lot
				GAMINSTDIR="${GAMINSTDIR#\"}"
				GAMINSTDIR="${GAMINSTDIR%\"}"

				FOUNDINSTEAMSHORTCUTS=1
				break
			fi
		done <<< "$( getSteamShortcutHex )"
	fi

	# Exit now if we didn't find the game directory
	if [ ! -d "${GAMINSTDIR}" ] && [ ! -d "${MUSINSTDIR}" ]; then
		echo "Could not find install directory for '$1'"
		return 1
	fi

	# Don't fetch these if we found and set the information already from a Steam shortcuts, since we already set these variables if we found a Steam shortcut
	# We don't get here if we didn't find a game dir for either a Steam game or shortcut
	if [ "$FOUNDINSTEAMSHORTCUTS" -eq 0 ]; then
		APPMAGN="$( getValueFromAppManifest "name" "$SEARCHMANIFEST" )"
		APPMAAID="$( getValueFromAppManifest "appid" "$SEARCHMANIFEST" )"
	fi

	if [ -z "$ONLYPATH" ]; then
		printf "%s (%s) -> %s\n" "$APPMAGN" "$APPMAAID" "$GAMINSTDIR"
	else
		printf "%s\n" "$GAMINSTDIR"  # Only output path, used by "listSteamGames"
	fi
}

### BEGIN BINARY VDF FUNCTIONS ###

# Convert Steam Shortcut AppID from hex to 32bit unsigned integer
function convertSteamShortcutAppID {
    SHORTCUTAPPIDHEX="$1"
    SHORTCUTAPPIDLITTLEENDIAN="$( echo "$SHORTCUTAPPIDHEX" | tac -rs .. | tr -d '\n' )"
    echo "$((16#${SHORTCUTAPPIDLITTLEENDIAN}))"
}

# Convert shortcuts.vdf hex to text with nullbyte stripped
function convertSteamShortcutHex {
	printf "%s" "$1" | xxd -r -p | tr -d '\0'
}

# Get the raw, unparsed hex for an entry from shortcuts.vdf
function getSteamShortcutEntryHex {
	SHORTCUTSVDFINPUTHEX="$1"  # The hex block representing the shortcut
	SHORTCUTSVDFMATCHPATTERN="$2"  # The pattern to match against in the block

	printf "%s" "$SHORTCUTSVDFINPUTHEX" | grep -oP "${SHORTCUTSVDFMATCHPATTERN}\K.*?(?=${SHORTCUTVDFENDPAT})"
}

# Parse a hex shortcuts.vdf entry based on a start pattern and convert to text - Unfortunately does not work for appid
function parseSteamShortcutEntryHex {
	SHORTCUTSVDFINPUTHEX="$1"  # The hex block representing the shortcut
	SHORTCUTSVDFMATCHPATTERN="$2"  # The pattern to match against in the block

	convertSteamShortcutHex "$( getSteamShortcutEntryHex "$SHORTCUTSVDFINPUTHEX" "$SHORTCUTSVDFMATCHPATTERN" )"
}

# Find shortcut entry by AppID and return the hex
function findSteamShortcutByAppID {
	SHORTCUTENTRYAID="$1"

	writelog "INFO" "${FUNCNAME[0]} - Searching for shortcut entry with AppID '$SHORTCUTENTRYAID'"
	while read -r SCVDFE; do
		SVDFEAID="$( parseSteamShortcutEntryAppID "$SCVDFE" )"
		if [ "$SVDFEAID" -eq "$SHORTCUTENTRYAID" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found shortcut entry with AppID '$SHORTCUTENTRYAID'"  # Updating it how?
			echo "$SCVDFE"
			break
		fi
	done <<< "$( getSteamShortcutHex )"
}

function replaceSteamShortcutEntryValue {
	SHORTCUTSVDFENTRY="$1"
	SHORTCUTSVDFMATCHPATTERN="$2"
	SHORTCUTSVDFNEWVAL="$3"

	SHORTCUTSVDFOLDVAL="$( getSteamShortcutEntryHex "$SHORTCUTSVDFENTRY" "$SHORTCUTSVDFMATCHPATTERN" )"  # Get the value without start and end bytes
	SHORTCUTSVDFOLDCOL="$( printf "%s" "$SHORTCUTSVDFENTRY" | grep -oP "${SHORTCUTSVDFMATCHPATTERN}.*?${SHORTCUTVDFENDPAT}" )"  # Get value with start and end bytes
	SHORTCUTSVDFNEWCOL="${SHORTCUTSVDFOLDCOL//"$SHORTCUTSVDFOLDVAL"/"$SHORTCUTSVDFNEWVAL"}"

	# Handle blank entries by simply hardcoding old entry and building new entry
	if [ -z "$SHORTCUTSVDFOLDVAL" ]; then
		SHORTCUTSVDFOLDCOL="${SHORTCUTSVDFMATCHPATTERN}${SHORTCUTVDFENDPAT}"
		SHORTCUTSVDFNEWCOL="${SHORTCUTSVDFMATCHPATTERN}${SHORTCUTSVDFNEWVAL}${SHORTCUTVDFENDPAT}"
	fi

	SHORTCUTNEWENTRY="${SHORTCUTSVDFENTRY//"$SHORTCUTSVDFOLDCOL"/"$SHORTCUTSVDFNEWCOL"}"

	printf "%s" "$SHORTCUTNEWENTRY" | tr -d '\0'
}

## Takes a shortcut appid, finds the shortcut entry, updates the given column value, replaces the hex for that section in the hex for the shortcuts.vdf file, writes out updated hex to new file
function editSteamShortcutEntry {
	SCPATH="$STUIDPATH/config/$SCVDF"  # TODO make this a globally accessible path instead of hardcoding it everywhere

	SHORTCUTENTRYAID="$1"  # i.e. 23435463
	SHORTCUTCOLUMN="$2"  # i.e. "appname"
	SHORTCUTNEWVAL="$( xxd -p -c 0 <<< "$3" )"  # i.e. "New Name" but in hex

	SHORTCUTSCONTENT="$( getSteamShortcutsVdfFileHex )"
	SHORTCUTSENTRY="$( findSteamShortcutByAppID "$SHORTCUTENTRYAID" )"

	## Find bytes that represent the column in shortcuts.vdf
	SHORTCUTEDITSTARTBYTES=""
	case $SHORTCUTCOLUMN in
		"appid")
			writelog "WARN" "${FUNCNAME[0]} - AppID not supported, skipping"
			shift ;;
		"appname")
			SHORTCUTEDITSTARTBYTES="${SHORTCUTVDFNAMEHEXPAT}"
			shift ;;
		"Exe")
			SHORTCUTEDITSTARTBYTES="${SHORTCUTVDFEXEHEXPAT}"
			shift;;
		"StartDir")
			SHORTCUTEDITSTARTBYTES="${SHORTCUTVDFSTARTDIRHEXPAT}"
			shift ;;
		"icon")
			SHORTCUTEDITSTARTBYTES="${SHORTCUTVDFICONHEXPAT}"
			shift ;;
	esac

	if [ -z "$SHORTCUTEDITSTARTBYTES" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Unknown or unsupported column name '$SHORTCUTCOLUMN', skipping"
		return
	fi

	writelog "INFO" "${FUNCNAME[0]} - Proceeding to edit '$SHORTCUTCOLUMN' field of shortcut '$SHORTCUTENTRYAID'"

	# Replace original entry's value bytes with new bytes, then replace the old bytes in the entire shortcuts file with the new bytes and write it out
	SHORTCUTNEWENTRY="$( replaceSteamShortcutEntryValue "$SHORTCUTSENTRY" "$SHORTCUTEDITSTARTBYTES" "$SHORTCUTNEWVAL" )"
	SHORTCUTSCONTENT="${SHORTCUTSCONTENT//"$SHORTCUTSENTRY"/"$SHORTCUTNEWENTRY"}"

	# Write out new bytes with bad 0a byte removed (causes issues when reading paths etc, so strip it out)
	echo "$SHORTCUTSCONTENT" | sed 's/0a//g' | xxd -r -p > "$SCPATH"
}

# Get shortcuts.vdf hex and grep each entry using start and end patterns (including a special case for the beginning of shortcuts.vdf)
function getSteamShortcutHex {
	SCPATH="$STUIDPATH/config/$SCVDF"
	getSteamShortcutsVdfFileHex | grep -oP "(${SHORTCUTVDFFILESTARTHEXPAT}|${SHORTCUTVDFENTRYBEGINHEXPAT})\K.*?(?=${SHORTCUTSVDFENTRYENDHEXPAT})"  # Get entire shortcuts.vdf as hex, then grep each entry using the begin and end patterns for each block
}

# Get full shortcuts.vdf hex including all start and end bytes -- Used for editing shortcuts.vdf
function getSteamShortcutsVdfFileHex {
	SCPATH="$STUIDPATH/config/$SCVDF"
	xxd -p -c 0 "$SCPATH"
}

function listNonSteamGameIDs {
	writelog "INFO" "${FUNCNAME[0]} - Reading all Non-Steam AppIDs from shortcuts.vdf"
	while read -r SCVDFE; do
		parseSteamShortcutEntryAppID "$SCVDFE"
	done <<< "$( getSteamShortcutHex )"
}

function haveAnySteamShortcuts {
	if [ "$( getSteamShortcutHex | wc -c )" -gt 0 ]; then
		return 0
	else
		return 1
	fi
}

# Grep and convert AppID from a given block of hex representing a shortcut entry in shortcuts.vdf by taking the first 8 bytes
function parseSteamShortcutEntryAppID {
	convertSteamShortcutAppID "$( printf "%s" "$1" | grep -oP "${SHORTCUTVDFAPPIDHEXPAT}\K.{8}" )"
}

### Functions to get information from specific parts of the shortcuts VDF ###
function parseSteamShortcutEntryAppName {
	parseSteamShortcutEntryHex "$1" "${SHORTCUTVDFNAMEHEXPAT}"
}

function parseSteamShortcutEntryExe {
	parseSteamShortcutEntryHex "$1" "${SHORTCUTVDFEXEHEXPAT}"
}

function parseSteamShortcutEntryStartDir {
	parseSteamShortcutEntryHex "$1" "${SHORTCUTVDFSTARTDIRHEXPAT}"
}

function parseSteamShortcutEntryIcon {
	parseSteamShortcutEntryHex "$1" "${SHORTCUTVDFICONHEXPAT}"
}

### END BINARY VDF FUNCTIONS ###

function getGameWindowName {
	if [ -n "$GAMEWINDOW" ] && [ "$GAMEWINDOW" != "$NON" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Already have the gamewindow name: '$GAMEWINDOW' - skipping"
		rm "$PIDLOCK" 2>/dev/null
	else
		rm "$TEMPGPIDFILE" 2>/dev/null

		if [ -n "$GPFX" ]; then
			SYSREG="$GPFX/$SREG"
			if [ ! -f "$SYSREG" ] ; then
				writelog "WAIT" "${FUNCNAME[0]} - Waiting for the pfx '$GPFX' to be full created"
			fi

			while [ ! -f "$SYSREG" ]; do
				if [ -f "$CLOSETMP" ]; then
					break
				fi
				sleep 1
			done
		fi

		writelog "INFO" "${FUNCNAME[0]} - No gamewindow name stored in metadata '$GEMETA/$AID.conf' yet. Trying to find it now"
		FOUNDWIN="$NON"
		GAMEWINPID="$(getGameWindowPID)"
		if [ -n "$GAMEWINPID" ] ; then
			if [[ "$GAMEWINPID" == "$NON" ]] ; then
				writelog "SKIP" "${FUNCNAME[0]} - No valid game window PID found"
			else
				writelog "INFO" "${FUNCNAME[0]} - Found valid game window PID '$GAMEWINPID'"
				echo "GAMEWINPID=\"$GAMEWINPID\"" > "$TEMPGPIDFILE"

				GAMEWINXID="$(getGameWinXIDFromPid "$GAMEWINPID")"
				if [ -n "$GAMEWINXID" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Found game window XID '$GAMEWINXID'"
					storeGameWindowNameMeta "$(getGameWinNameFromXid "$GAMEWINXID")"
				fi
			fi
		fi
	fi
}

function getCfgHeader {
	echo "#########"
	echo "#GAMENAME=\"$GAMENAME\""
	echo "#GAMEEXE=\"$GAMEEXE\""
	echo "#GAMEID=\"$AID\""
	echo "#PROTONVERSION=\"$PROTONVERSION\""
	echo "#########"
}

function SBSrunVRVideoPlayer {
	SBSVRWINNAME="vr-video-player"

	if [ "$RUNSBSVR" -eq 1 ]; then
		if [ -z "$GAMEWINXID" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - ERROR - GAMEWINXID is empty"
			writelog "SKIP" "${FUNCNAME[0]} - ERROR - forcefully killing game with $PKILL -9 '$GAMEWINPID' - should exit this script as well"
			getGamePidFromFile
			"$PKILL" -9 "$GAMEWINPID"
		else
			if [ -z "$VRVIDEOPLAYERARGS" ];	then
				writelog "SKIP" "${FUNCNAME[0]} - ERROR - no VRVIDEOPLAYERARGS '$VRVIDEOPLAYERARGS'"
			fi

			mapfile -d " " -t -O "${#RUNVRVIDEOPLAYERARGS[@]}" RUNVRVIDEOPLAYERARGS < <(printf '%s' "$VRVIDEOPLAYERARGS")

			writelog "INFO" "${FUNCNAME[0]} - Starting '$VRVIDEOPLAYER' with args '${RUNVRVIDEOPLAYERARGS[*]}' for windowid '$GAMEWINXID'"

			GWIDDEC="$(("$GAMEWINXID"))"
			echo "GWIDDEC=$GWIDDEC" > "$GWIDFILE"

			sleep 1	# ugly, but it might need a bit...

			if [ -z "$SBSZOOM" ]; then
				"$VRVIDEOPLAYER" "${RUNVRVIDEOPLAYERARGS[@]}" "$GAMEWINXID" 2>/dev/null &
			else
				"$VRVIDEOPLAYER" "${RUNVRVIDEOPLAYERARGS[@]}" --zoom "$SBSZOOM" "$GAMEWINXID" 2>/dev/null &
			fi

			writelog "INFO" "${FUNCNAME[0]} - Waiting for '$VRVIDEOPLAYER' window '$SBSVRWINNAME' for GAMEWINXID '$GAMEWINXID'"

			MAXWAIT=20
			COUNTER=0

			while ! "$XWININFO" -name "$SBSVRWINNAME" -stats >/dev/null 2>/dev/null; do
				if [ -f "$CLOSETMP" ]; then
					writelog "WAIT" "${FUNCNAME[0]} - ${PROGNAME,,} is just closing - leaving loop"
					break
				fi
				if [[ "$COUNTER" -ge "$MAXWAIT" ]]; then
					writelog "SKIP" "${FUNCNAME[0]} - ERROR - timeout waiting for '$VRVIDEOPLAYER' - exit"
					"$PKILL" -f "$VRVIDEOPLAYER"
					RUNSBSVR=0
					exit 1
				fi
				if ! "$PGREP" -f "$VRVIDEOPLAYER" ; then
					if [ "$COUNTER" -ge 3 ]; then
						writelog "SKIP" "${FUNCNAME[0]} - ERROR - '$VRVIDEOPLAYER' not running (crashed?) no need to wait for its window to appear - exit"
						RUNSBSVR=0
						exit 1
					else
						writelog "WARN" "${FUNCNAME[0]} - '$VRVIDEOPLAYER' not running yet - waiting a bit longer"
					fi
				fi

				writelog "WAIT" "${FUNCNAME[0]} - WAIT - '$COUNTER/$MAXWAIT' sec waiting for '$VRVIDEOPLAYER' window '$SBSVRWINNAME'"
				COUNTER=$((COUNTER+1))
				sleep 1
			done

			# player windowid:
			SBSVRWID=$("$XWININFO" -name "$SBSVRWINNAME" -stats | grep "^$XWININFO" | awk -F 'id: ' '{print $2}' | cut -d ' ' -f1)

			if [ -n "$SBSVRWID" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Pressing w in '$VRVIDEOPLAYER' window '$SBSVRWINNAME' to adjust view: '$XDO windowactivate --sync $SBSVRWID key w'"
				"$XDO" windowactivate --sync "$SBSVRWID" key w

				writelog "INFO" "${FUNCNAME[0]} - Activating game window with id '$GAMEWINXID' for input"
				"$XDO" windowactivate --sync "$GAMEWINXID" click 1
			else
				writelog "SKIP" "${FUNCNAME[0]} - WARN - SBSVRWID '$SBSVRWID' is empty!"
			fi
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - Skipping because RUNSBSVR was set to 0"
	fi
}

function SBSinitVRVideoPlayer {
	if [ "$RUNSBSVR" -eq 0 ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Skipping because RUNSBSVR was set to 0"
		return
	fi

	if [ -z "$GAMEWINXID" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Could not find GAMEWINXID -- Skipping"
		return
	fi

	if [ "$GAMEWINXID" == "0x0" ]; then
		writelog "SKIP" "${FUNCNAME[0]} GAMEWINXID '$GAMEWINXID' is invalid - skipping VR"
		RUNSBSVR=0
		return
	fi

	writelog "INFO" "${FUNCNAME[0]} Using the gamewindow id '$GAMEWINXID' for stereoscopic 3D VR"
	SBSrunVRVideoPlayer	"$GAMEWINXID" 2>/dev/null &
}

function SBSstopVRVideoPlayer {
	if [ "$RUNSBSVR" -eq 1 ]; then

		MAXWAIT=20
		COUNTER=0
		while [ -z "$GAMEWINPID" ]; do
			if [ -f "$CLOSETMP" ]; then
				writelog "WAIT" "${FUNCNAME[0]} - ${PROGNAME,,} is just closing - leaving loop"
				break
			fi
			if [[ "$COUNTER" -ge "$MAXWAIT" ]]; then
				writelog "SKIP" "${FUNCNAME[0]} - ERROR - timeout waiting for Game process - exit"
				break
			fi

			writelog "INFO" "${FUNCNAME[0]} - Don't have a Game process GAMEWINPID yet - waiting"
			getGamePidFromFile
			GAMEWINPID="$(getGamePidFromWindowName)"
			COUNTER=$((COUNTER+1))
			sleep 1
		done

		writelog "INFO" "${FUNCNAME[0]} - Waiting for game process '$GAMEWINPID' to finish..."

		if ! "$PGREP" -a "vrcompositor" >/dev/null ; then
			writelog "SKIP" "${FUNCNAME[0]} - ERROR - vrcompositor not running but it should - bailing out DRYRUN"
		fi

		tail --pid="$GAMEWINPID" -f /dev/null
		writelog "INFO" "${FUNCNAME[0]} - Game process '$GAMEWINPID' finished - closing '$VRVIDEOPLAYER'"

		if [ -f "$GWIDFILE" ]; then
			source "$GWIDFILE"
			GWIDTXT="/tmp/${VRVIDEOPLAYER##*/}_${GWIDDEC}"

			if [ -f "$GWIDTXT" ]; then
				writelog "INFO" "${FUNCNAME[0]} - '$GWIDTXT' found"
				updateConfigEntry "SBSZOOM" "$(cat "$GWIDTXT")" "$SBSTWEAKCFG"
				rm "$GWIDTXT" >/dev/null 2>/dev/null
			else
				writelog "SKIP" "${FUNCNAME[0]} - GWIDTXT '$GWIDTXT' not found - skipping"
			fi
			rm "$GWIDFILE" >/dev/null 2>/dev/null
		else
			writelog "SKIP" "${FUNCNAME[0]} - GWIDFILE '$GWIDFILE' not found - skipping"
		fi

		"$PKILL" -f "$VRVIDEOPLAYER"

		writelog "INFO" "${FUNCNAME[0]} - -------- finished SBS-VR --------"
	else
		writelog "SKIP" "${FUNCNAME[0]} - Skipping because RUNSBSVR was set to 0"
	fi
}

function waitForGameWindowName {
	if [ -n "$GAMEWINDOW" ] && [ "$GAMEWINDOW" != "$NON" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Already have a Game Window '$GAMEWINDOW'"
	else
		MAXWAIT=20
		COUNTER=0
		writelog "INFO" "${FUNCNAME[0]} - Waiting for parallel process to find the Game Window"

		while [ -f "$PIDLOCK" ]; do
			if [ -f "$CLOSETMP" ] || [[ "$COUNTER" -ge "$MAXWAIT" ]]; then
				break
			fi
			COUNTER=$((COUNTER+1))
			sleep 1
		done

		COUNTER=0

		while ! grep -q "^GAMEWINDOW" "$GEMETA/$AID.conf"; do
			if [ -f "$CLOSETMP" ]; then
				writelog "WAIT" "${FUNCNAME[0]} - ${PROGNAME,,} is just closing - leaving loop"
				break
			fi
			if [[ "$COUNTER" -ge "$MAXWAIT" ]]; then
				writelog "SKIP" "${FUNCNAME[0]} - Giving up waiting for GAMEWINDOW to appear in the game metadata '$GEMETA/$AID.conf'"
				break
			fi

			writelog "WAIT" "${FUNCNAME[0]} - WAIT - '$COUNTER/$MAXWAIT' sec waiting for GAMEWINDOW to appear in game metadata '$GEMETA/$AID.conf'"
			COUNTER=$((COUNTER+1))
			sleep 1
		done
		loadCfg "$GEMETA/$AID.conf" X

		if [ -n "$GAMEWINDOW" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Parallel process found Game Window '$GAMEWINDOW'"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Parallel process didn't find a Game Window GAMEWINDOW"
		fi
	fi
}

function getGameWindowXID {
	if [ -n "$GAMEWINXID" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Already have the windowid '$GAMEWINXID'"
	else
		if [ -n "$GAMEWINDOW" ] && [ "$GAMEWINDOW" != "$NON" ]; then
			GAMEWINXID="$("$XWININFO" -name "${GAMEWINDOW//\"/}" -stats | grep "^$XWININFO" | awk -F 'id: ' '{print $2}' | cut -d ' ' -f1)"
			if [ -n "$GAMEWINXID" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Found windowid '$GAMEWINXID' for the windowname '$GAMEWINDOW'"
			fi
		fi
		if [ -z "$GAMEWINXID" ]; then
			if [ -n "$GAMEWINPID" ]; then
				GAMEWINXID="$(getGameWinXIDFromPid "$GAMEWINPID")"
			else
				writelog "SKIP" "${FUNCNAME[0]} - Don't have a game pid '$GAMEWINPID' to detect the windowid GAMEWINXID"
			fi
			if [ -n "$GAMEWINXID" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Found windowid '$GAMEWINXID' for the game pid '$GAMEWINPID'"
			fi
		fi
	fi
	if [ -n "$GAMEWINPID" ]; then
		echo "$GAMEWINPID"
	else
		writelog "SKIP" "${FUNCNAME[0]} - Failed to detect the windowid GAMEWINXID"
	fi
}

function waitForGameWindowXid {
	if [ -n "$GAMEWINXID" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Already have the game window XID '$GAMEWINXID'"
	else
		if [ -n "$GAMEWINDOW" ] && [ "$GAMEWINDOW" != "$NON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Waiting for a window GAMEWINXID of the game window '$GAMEWINDOW'"
			MAXWAIT=20
			COUNTER=0
			while ! "$XWININFO" -name "${GAMEWINDOW//\"/}"; do
				if [ -f "$CLOSETMP" ]; then
					writelog "WAIT" "${FUNCNAME[0]} - ${PROGNAME,,} is just closing - leaving loop"
					break
				fi
				if [[ "$COUNTER" -ge "$MAXWAIT" ]]; then
					writelog "SKIP" "${FUNCNAME[0]} - Giving up waiting for GAMEWINXID"
					break
				fi

				writelog "WAIT" "${FUNCNAME[0]} - WAIT '$COUNTER/$MAXWAIT'"
				sleep 1
				COUNTER=$((COUNTER+1))
			done
		elif [ -n "$GAMEWINPID" ]; then
			GAMEWINXID="$(getGameWinXIDFromPid "$GAMEWINPID")"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Can't wait for the windowid GAMEWINXID without either a valid GAMEWINDOW or GAMEWINPID"
		fi

		GAMEWINXID="$("$XWININFO" -name "${GAMEWINDOW//\"/}" -stats | grep "^$XWININFO" | awk -F 'id: ' '{print $2}' | cut -d ' ' -f1)"

		if [ -n "$GAMEWINXID" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found the game window id '$GAMEWINXID'"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Didn't find a game window id GAMEWINXID"
		fi
	fi
}

function initSBSVR {
	if [ "$RUNSBSVR" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Checking if VR is available"
		touch "$VRINITLOCK"
		checkHMDPresent
		initSteamVR
		mv "$VRINITLOCK" "$VRINITRESULT"
	fi
}

function startSBSVR {
	if [ "$RUNSBSVR" -eq 0 ]; then
		return
	fi

	MAXWAIT=20
	COUNTER=0

	writelog "INFO" "${FUNCNAME[0]} - Waiting for parallel process to detect VR HMD presence"

	while [ -f "$VRINITLOCK" ]; do
		if [ -f "$CLOSETMP" ] || [[ "$COUNTER" -ge "$MAXWAIT" ]]; then
			break
		fi
		COUNTER=$((COUNTER+1))
		sleep 1
	done

	loadCfg "$VRINITRESULT"
	rm "$VRINITRESULT"

	if [ "$RUNSBSVR" -eq 0 ]; then
		writelog "SKIP" "${FUNCNAME[0]} - VR mode was cancelled, because the parallel process could not initialize VR"
		return
	fi

	if [ -f "$SBSTWEAKCFG" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Loading SBS configfile '$SBSTWEAKCFG' to get current values"
		loadCfg "$SBSTWEAKCFG"
	fi
	writelog "INFO" "${FUNCNAME[0]} - Preparing VR launch for '$AID'"
	waitForGameWindowName
	waitForGameWindowXid
	SBSinitVRVideoPlayer
	SBSstopVRVideoPlayer


}

function checkHMDPresent {
	if "$PGREP" -a "vrcompositor" >/dev/null ; then
		writelog "INFO" "${FUNCNAME[0]} - Looks like SteamVR is already running - skipping this function"
		return
	fi

	if [ "$CHECKHMD" -eq 0 ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Skipping, as '$LSUSB' was not found"
		return
	fi

	UUDEV="/lib/udev/rules.d"
	EUDEV="/etc/udev/rules.d"
	SVR="steam-vr"
	NOVRP="1142"
	FOUNDHMD=0

	SVRRULE="$(find "$UUDEV" -name "*$SVR*")"
	if [ -z "$SVRRULE" ]; then
		SVRRULE="$(find "$EUDEV" -name "*$SVR*")"
	fi

	if [ -n "$SVRRULE" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Found $SVR udev rule - trying to find one of the VR devices before starting SteamVR"

		while read -r line; do
			IDV="$(cut -d ',' -f3 <<< "$line" | grep -oP '"\K[^"]+')"
			IDP="$(cut -d ',' -f4 <<< "$line" | grep -v "$NOVRP" | grep -oP '"\K[^"]+')"
			if [ -n "$IDV" ] && [ -n "$IDP" ]; then
				IDVP="$IDV:$IDP"
				if "$LSUSB" | grep -q "$IDVP"; then
					FOUNDHMD=1
				fi
			fi
		done < "$SVRRULE"
	else
		echo "no $SVR udev rule found"
		writelog "WARN" "${FUNCNAME[0]} - No $SVR udev rule found. As it might be stored under a different name, this is just a warning"
	fi

	if [ "$FOUNDHMD" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Found $SVR hardware using '$LSUSB' - continuing"
	else
		writelog "SKIP" "${FUNCNAME[0]} - No $SVR hardware found using '$LSUSB' - cancelling the SteamVR start"
		RUNSBSVR=0
		echo "RUNSBSVR=\"0\"" > "$VRINITLOCK"
	fi

}

# start game in side-by-side VR:
function checkSBSVRLaunch {
	if [ "$1" != "$NON" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Incoming gamewindow name is '$1'"
		RUNSBSVR=1
	fi

	if [ -z "$RUNSBSVR" ] || [ "$RUNSBSVR" -eq 0 ]; then
		return
	fi
	# override game configs with a sbs-tweak config if available:
	# first look for a global tweak:
	if [ -f "$GLOBALSBSTWEAKCFG" ]; then
		writelog "INFO" "${FUNCNAME[0]} - VR using overrides found in '$GLOBALSBSTWEAKCFG'"
		loadCfg "$GLOBALSBSTWEAKCFG"
	fi

	# then for a user tweak - (overriding the global one):
	if [ -f "$SBSTWEAKCFG" ]; then
		writelog "INFO" "${FUNCNAME[0]} - VR using overrides found in '$SBSTWEAKCFG'"
		loadCfg "$SBSTWEAKCFG"
	fi

	# start the whole side-by-side process:
	if [ "$1" != "$NON" ]; then
		writelog "INFO" "${FUNCNAME[0]} - ${FUNCNAME[0]} - Using argument 1 as GAMEWINDOW '$GAMEWINDOW'"
		export GAMEWINDOW="$1"
	fi

	writelog "INFO" "${FUNCNAME[0]} - ${FUNCNAME[0]} - Starting VRlaunch for '$AID'"
	if [ "$RUNSBSVR" -eq 1 ]; then
		initSBSVR &
	else
		writelog "SKIP" "${FUNCNAME[0]} - ERROR - RUNSBSVR is '$RUNSBSVR' which is invalid - setting to 0"
		RUNSBSVR=0
	fi

}

function checkSBSLaunch {
	if [ "$RUNSBS" -eq 0 ]; then
		return
	fi

	# first look for a global tweak:
	if [ -f "$GLOBALSBSTWEAKCFG" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using SBS overrides found in '$GLOBALSBSTWEAKCFG'"
		loadCfg "$GLOBALSBSTWEAKCFG"
	fi

	# then for a user tweak - (overriding the global one):
	if [ -f "$SBSTWEAKCFG" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using SBS overrides found in '$SBSTWEAKCFG'"
		loadCfg "$SBSTWEAKCFG"
	fi

}

function dld3d47 {
	function dld3d4732 {
		dlCheck "$DL_D3D47_32" "$D3D47DLDIR/${D3D47//.dll/.zip}" "X" "Downloading '$D3D47_32' into '$D3D47DLDIR'"
		find "$DLDST" -size 0 -delete
		"$UNZIP" "$DLDST" -d "$D3D47DLDIR" 2>/dev/null
		mv "$D3D47DLDIR/$D3D47" "$D3D47DLDIR/$D3D47_32" 2>/dev/null
	}

	function dld3d4764 {
		if [ ! -f "$D3D47DLDIR/$D3D47_64" ]; then
			dlCheck "$DL_D3D47_64" "$D3D47DLDIR/$D3D47_64" "X" "Downloading '$D3D47_64' into '$D3D47DLDIR'"
			find "$D3D47DLDIR/$D3D47_64" -size 0 -delete
		fi
	}
	mkProjDir "$D3D47DLDIR"
	dld3d47"$1"
}

function installd3d47dll {
	D3D47DESTPATH="$2/$D3D47"

	if [ "$USESPEKD3D47" -eq 0 ] && [ "$USESPECIALK" -eq 1 ]; then  # We need to check if SpecialK is enabled so we don't end up removing this if it's installed for ReShade
		# User has disabled d3dcompiler_47 for use with SpecialK -- Check if it's installed and tracked by us, and if so, remove it!
		writelog "INFO" "${FUNCNAME[0]} - USESPEKD3D47 is '$USESPEKD3D47'"
		writelog "INFO" "${FUNCNAME[0]} - D3D47DESTPATH is '$D3D47DESTPATH'"
		if [ -f "$SPEKENA" ]; then
			# DLL exists in game files and SpecialK tracked file is present
			if grep -qw "$D3D47DESTPATH" "$SPEKENA"; then
				# DLL exists and is present in SpecialK tracking file, assume this is ours and remove it!
				writelog "INFO" "${FUNCNAME[0]} - Found tracked '$D3D47' DLL at '$D3D47DESTPATH' -- Assuming this is ours and removing it!"
				rm "$D3D47DESTPATH"  # Remove DLL file
				sed -i "s#${D3D47DESTPATH}##g" "$SPEKENA"  # Remove tracked D3D47 DLL (apparently sed doesn't like using delete with paths, so we use substituion)
			fi
		fi
	elif [ ! -f "$D3D47DESTPATH" ]; then
		if [ ! -f "$D3D47DLDIR/$1" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Sourcefile '$D3D47DLDIR/$1' missing - trying to download"
			dld3d47 "32"
			dld3d47 "64"
		fi

		if [ ! -f "$D3D47DLDIR/$1" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Sourcefile '$D3D47DLDIR/$1' still missing - skipping this file"
		else
			# We should only copy the DLL and write to the DLL tracking file if the DLL is not already in the destination folder
			cp "$D3D47DLDIR/$1" "$D3D47DESTPATH" >/dev/null 2>/dev/null
			writelog "INFO" "${FUNCNAME[0]} - Copied '$D3D47DLDIR/$1' to '$2/$D3D47'"
			if [ "$USESPECIALK" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Writing '$D3D47DESTPATH' to '$SPEKENA'"
				echo "$D3D47DESTPATH" >> "$SPEKENA"
			elif [ "$USERESHADE" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Writing '$D3D47' to '$2/$RSTXT'"
				echo "$D3D47" >> "$2/$RSTXT"
				sort "$2/$RSTXT" -u -o "$2/$RSTXT"
			else
				writelog "WARN" "${FUNCNAME[0]} - Function was called but neither ReShade nor SpecialK was specified -- No need to write out to a file that we're tracking this DLL"
			fi
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - Destfile '$D3D47DESTPATH' already exists - skipping"
	fi
}

function autoBumpReShade {
	RSVERSLATEST="$( fetchGitHubTags "$RESHADEPROJURL" "1" )"
	RSVERSLATEST="${RSVERSLATEST//v/}"
	if [ "$AUTOBUMPRESHADE" -eq 1 ] && [[ "$RSVERS" < "$RSVERSLATEST" ]]; then
		writelog "INFO" "${FUNCNAME[0]} - Found newer version of '$RESH' - Updating '$RSVERS' to '$RSVERSLATEST"
		touch "$FUPDATE"
		updateConfigEntry "RSVERS" "$RSVERSLATEST" "$STLDEFGLOBALCFG"
	else
		writelog "SKIP" "${FUNCNAME[0]} - '$RSVERS' is the latest version of '$RESH' - not updating"
	fi
}

function createDLReShadeList {
	if ! ping -q -c1 github.com &>/dev/null; then
		writelog "INFO" "${FUNCNAME[0]} - Can't reach GitHub, so not attempting to fetch ReShade versions list"
 		RESHADEVERSIONS="none"
	else
		RSVERSONLINE="$( fetchGitHubTags "$RESHADEPROJURL" "3" )"
		RSVERSONLINE="${RSVERSONLINE//$'\n'/!}"
		RSVERSONLINE="${RSVERSONLINE//$!/}"
		RSVERSONLINE="${RSVERSONLINE//v/}"
		writelog "INFO" "${FUNCNAME[0]} - Found the following '$RESH' versions online '$RSVERSONLINE'"
		RESHADEVERSIONS="$RSOVRVERS!$RSVERSONLINE!4.91!3.4.1"
	fi
}

function dlReShade {
	if [ -z "$1" ]; then
		DLVERS="$RSVERS"
	else
		DLVERS="$1"
	fi

	DLDST="${RESHADESRCDIR}/${RSSU}_${DLVERS}.exe"
	RSSETUP="${RESHADEDLURL}/${RSSU}_${DLVERS}.exe"

	dlCheck "$RSSETUP" "$DLDST" "X" "Downloading $RSSU"
	echo "$DLVERS" > "${DLDST//.exe/.log}"

	if [ ! -s "$DLDST" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Downloaded file '$DLDST' is empty - removing"
		rm "$DLDST" 2>/dev/null
	else
		"$UNZIP" -qo "$DLDST" -d "$RESHADESRCDIR/${DLVERS}" 2>/dev/null
		writelog "INFO" "${FUNCNAME[0]} - Downloaded and extracted ${RESH}-v${DLVERS} file '$DLDST'"
	fi
}

function overrideReShadeVersion {
	## ReShade version priority is as follows:
	## 1. Game Menu Override version ('RSOVRVERS') -- Only applies if 'RSOVRD' checkbox is toggled on
	## 2. SpecialK ReShade Override version ('RSSPEKVERS') -- Only applies if ReShade+SpecialK are used together, and if '$USERSSPEKVERS' checkbox is toggled on
	## 3. Global Menu ReShade version ('RSVERS')

	if [ "$RSOVRD" -eq 1 ]; then  # Game Menu ReShade Override version -- Takes priority over Global ReShade version AND SpecialK ReShade version
		if [[ ! "$RSOVRVERS" = "$RSVERS" ]]; then
			writelog "INFO" "${FUNCNAME[0]} - Overriding global '$RESH' version '$RSVERS' with '$RSOVRVERS'"
       		RSVERS="$RSOVRVERS"
   		else
       		writelog "SKIP" "${FUNCNAME[0]} - '$RESH' Override version and '$RESH' global version match - Not overriding"
   		fi
	elif [ "$USESPECIALK" -eq 1 ] && [ "$USERESHADE" -eq 1 ] && [ "$USERSSPEKVERS" -eq 1 ] && [ "$USERESHSPEKPLUGIN" -eq 1 ]; then  # Global Menu ReShade version to load when ReShade is loaded via SpecialK
		writelog "INFO" "${FUNCNAME[0]} - Overriding global '$RESH' version '$RSVERS' with SpecialK ReShade version override '$RSSPEKVERS' as it is enabled and ReShade+SpecialK are enabled together"
		RSVERS="$RSSPEKVERS"
	else
    	writelog "SKIP" "${FUNCNAME[0]} - '$RESH' override is disabled - Skipping"
	fi
}

# prepare reshade files if not found:
function prepareReshadeFiles {
	overrideReShadeVersion
	if [ "$DOWNLOAD_RESHADE" -eq 1 ]; then
	writelog "INFO" "${FUNCNAME[0]} - DOWNLOAD_RESHADE enabled"
		if [ ! -f "$D3D47DLDIR/$D3D47_32" ]; then dd
			writelog "404" "${FUNCNAME[0]} - '$D3D47DLDIR/$D3D47_32' missing - downloading"

			if [ ! -d "$RESHADESRCDIR" ]; then
				writelog "404" "${FUNCNAME[0]} - '$RESHADESRCDIR' does not exist - trying to create it"
				mkProjDir "$RESHADESRCDIR"
			fi
		fi
		dld3d47 "32"
		dld3d47 "64"

		#Check if ReShade file are missing
		if [ ! -f "$RESHADESRCDIR/$RSVERS/$RS_64" ] || [ ! -f "$RESHADESRCDIR/$RSVERS/$RS_32" ]; then
			writelog "404" "${FUNCNAME[0]} - '$RESHADESRCDIR/$RSVERS/$RS_64' and/or '$RS_32' missing - downloading"
			dlReShade
		fi

		if [ ! -f "$RESHADESRCDIR/$RSVERS/$RS_64_VK" ] || [ ! -f "$RESHADESRCDIR/$RSVERS/$RS_32_VK" ]; then
			writelog "404" "${FUNCNAME[0]} - '$RESHADESRCDIR/$RSVERS/$RS_64_VK' and/or '$RS_32_VK' missing - downloading"
			dlReShade
		fi

		#Check if ReShade file is zero bytes
		if [ ! -s "$RESHADESRCDIR/$RSVERS/$RS_64" ] || [ ! -s "$RESHADESRCDIR/$RSVERS/$RS_32" ]; then
			writelog "404" "${FUNCNAME[0]} - '$RESHADESRCDIR/$RSVERS/$RS_64' and/or '$RS_32' corrupted - downloading"
			dlReShade
		fi
		if [ ! -s "$RESHADESRCDIR/$RSVERS/$RS_64_VK" ] || [ ! -s "$RESHADESRCDIR/$RSVERS/$RS_32_VK" ]; then
			writelog "404" "${FUNCNAME[0]} - '$RESHADESRCDIR/$RSVERS/$RS_64_VK' and/or '$RS_32_VK' corrupted - downloading"
			dlReShade
		fi

		if [ "$RESHADEUPDATE" -eq 1 ]; then
			if [ -f "${RESHADESRCDIR}/${RSSU}.log" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Found ${RESH} download log '${RESHADESRCDIR}/${RSSU}.log'"
				if [ "$RSVERS" != "$(cat "${RESHADESRCDIR}/${RSSU}.log")" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Last downloaded is ${RESH} version '$(cat "${RESHADESRCDIR}/${RSSU}.log")' is not equal to the latest available ${RESH} version '$RSVERS' - updating"
					dlReShade
				fi
			fi
		fi
	fi

	# make sure Depth3D is even wanted
	if [ "$RESHADE_DEPTH3D" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - RESHADE_DEPTH3D enabled"
		StatusWindow "$GUI_DLSHADER" "dlShaders depth3d" "DownloadShadersStatus"
	fi
}

function SHADSRC {
	echo "$STLSHADDIR/${1,,}"
}

function createShaderRepoList {
	SHADREPOURL="https://www.pcgamingwiki.com/wiki/${RESH}"
	MAXAGE=1440

	if [ ! -f "$SHADREPOLIST" ] || test "$(find "$SHADREPOLIST" -mmin +"$MAXAGE")"; then
		# if this breaks, we'll use bundled static url list instead
		"$WGET" -q "$SHADREPOURL" -O - 2> >(grep -v "SSL_INIT") | sed -n '/Repository</,/table>/p' | grep "^<td>" | awk 'ORS=NR%3?FS:RS' | grep -v "https://blues" | sed "s:<td><a rel=\"nofollow\" class=\"external text\" href=::; s:</a></td> <td>:;:; s:</td> <td>:;:; s:\">:\";:; s:<br />: :; s: ;:;:; s:/tree/master::; s:/reshade/Shaders::" > "$SHADREPOLIST"
	fi

	RCL="repocustomlist.txt"
	SHADREPOCUSTOMLIST="$STLSHADDIR/$RCL"

	if [ ! -f "$SHADREPOCUSTOMLIST" ]; then
		cp "$GLOBALMISCDIR/$RCL" "$SHADREPOCUSTOMLIST"
	fi

	if [ -f "$SHADREPOCUSTOMLIST" ]; then
		cat "$SHADREPOCUSTOMLIST" >> "$SHADREPOLIST"
		sort -u "$SHADREPOLIST" -o "$SHADREPOLIST"
	fi

	sed '/^$/d' -i "$SHADREPOLIST"
}

function unblockrssub {
	if grep -q "^${RSSUB}$" "$SHADERREPOBLOCKLIST"; then
		writelog "INFO" "${FUNCNAME[0]} - Removing essential '${RSSUB}' from the shader blocklist '$SHADERREPOBLOCKLIST'"
		grep -v "^${RSSUB}$" "$SHADERREPOBLOCKLIST" > "$STLSHM/SHADERREPOBLOCKLIST_tmp.txt"
		mv "$STLSHM/SHADERREPOBLOCKLIST_tmp.txt" "$SHADERREPOBLOCKLIST"
	fi
}

function dlShaders {
	createShaderRepoList
	touch "$SHADERREPOBLOCKLIST"
	unblockrssub

	if [ -z "$1" ]; then
		if [ "$DLSHADER" -eq 1 ]; then
			while read -r SHADLINE; do
				SHADURL="$(cut -d ';' -f1 <<< "$SHADLINE")"
				SHADNAM="$(cut -d ';' -f2 <<< "$SHADLINE")"
				if ! grep -qi "^${SHADNAM}$" "$SHADERREPOBLOCKLIST"; then
					writelog "INFO" "${FUNCNAME[0]} - Updating $SHADNAM"
					notiShow "$(strFix "$NOTY_DLSHADERS" "$SHADNAM")" "S"
					gitUpdate "$(SHADSRC "$SHADNAM")" "${SHADURL//\"/}"
				else
					writelog "SKIP" "${FUNCNAME[0]} - Skipping $SHADNAM"
				fi
			done < "$SHADREPOLIST"
			notiShow "$GUI_DONE" "S"
		fi
	else
		if [ "$1" == "list" ]; then
			while read -r SHADLINE; do
				SHADNAM="$(cut -d ';' -f2 <<< "$SHADLINE")"
				echo "\"${SHADNAM,,}\""
			done < "$SHADREPOLIST" | sort
		elif [ "$1" == "repos" ]; then
			ShaderRepoDialog
		else
			SHADURL="$(grep -i ";$1;" "$SHADREPOLIST" | cut -d ';' -f1)"
			if [ -n "$SHADURL" ]; then
				gitUpdate "$(SHADSRC "$1")" "${SHADURL//\"/}"
			else
				writelog "SKIP" "${FUNCNAME[0]} - Invalid shader $1"
			fi
		fi
	fi
}

function ShaderRepoDialog {
	createShaderRepoList
	unblockrssub
	fixShowGnAid
	export CURWIKI="$PPW/Shader-Repositories"
	TITLE="${PROGNAME}-${FUNCNAME[0]}"
	pollWinRes "$TITLE"

	setShowPic

	REPOPICKS="$(
	while read -r SHADLINE; do
		SHADURL="$(cut -d ';' -f1 <<< "$SHADLINE")"
		SHADNAM="$(cut -d ';' -f2 <<< "$SHADLINE")"
		SHADAUT="$(cut -d ';' -f3 <<< "$SHADLINE")"
		SHADDES="$(cut -d ';' -f4 <<< "$SHADLINE")"

		if grep -qi "^${SHADNAM}$" "$SHADERREPOBLOCKLIST"; then
			echo FALSE
		else
			echo TRUE
		fi

		echo "$SHADURL"
		echo "$SHADNAM"
		echo "$SHADAUT"
		echo "$SHADDES"
	done < "$SHADREPOLIST" | \
	"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center "$WINDECO" --list --checklist --column="$GUI_USE" --column="$GUI_URL" --column="$GUI_NAME" --column="$GUI_AUTH" --column="$GUI_DESC" --separator="" --print-column="3" \
	--text="$(spanFont "$(strFix "$GUI_SHADREPDIALOG" "$SGNAID")" "H")" --title="$TITLE" --button="$BUT_CAN:0" --button="$BUT_SELECT:2" "$GEOM")"
	case $? in
		0)	{
				writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_CAN' - Cancelling selection"
			}
		;;
		2)	{
				writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_SELECT' - Saving Selection"

				if [ -z "$REPOPICKS" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Nothing selected"
					REPOPICKS=""
				else
					rm "$SHADERREPOBLOCKLIST" 2>/dev/null
					while read -r SHADLINE; do
						SHADNAM="$(cut -d ';' -f2 <<< "$SHADLINE")"
						if ! grep -q "$SHADNAM" <<< "$REPOPICKS"; then
							echo "${SHADNAM,,}" >> "$SHADERREPOBLOCKLIST"
						fi
					done < "$SHADREPOLIST"
					touch "$SHADERREPOBLOCKLIST"
					sort -u "$SHADERREPOBLOCKLIST" -o "$SHADERREPOBLOCKLIST"
					unblockrssub
				fi
			}
		;;
	esac
}

function setFullGameExePath {
	if [[ ( "$USECUSTOMCMD" -eq 1 && -f "$CUSTOMCMD" && "$CUSTOMCMDRESHADE" -eq 1 ) || "$ONLY_CUSTOMCMD" -eq 1 ]]; then
		# Use Alternative EXE Path if defined instead of custom command path
		# We should only use the custom command directory if no alternatiive EXE path is defined, and
		# we should prioritise the alt path if it is defined
		DEFINEDALTEXEPATH="$(GETALTEXEPATH)"
		FGEP="${DEFINEDALTEXEPATH:-${CUSTOMCMD%/*}}"
		writelog "INFO" "${FUNCNAME[0]} - Using the directory '$FGEP' of the used custom command as absolute game exe path"

		if [ "$CUSTOMCMDRESHADE" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - User enabled 'CUSTOMCMDRESHADE' - Using custom command directory with exe as ReShade installation directory"
		fi

		export "$1"="$FGEP"
	else
		if [ "$USECUSTOMCMD" -eq 1 ] && [ ! -f "$CUSTOMCMD" ]; then
			writelog "WARN" "${FUNCNAME[0]} - User enabled Custom Command, but custom command at '$CUSTOMCMD' is not a file!"
		fi

		if [ "$CUSTOMCMDRESHADE" -eq 0 ]; then
			writelog "INFO" "${FUNCNAME[0]} - User did not enable 'CUSTOMCMDRESHADE' - Using the game's exe directory as the ReShade installation directory"
		fi
		setShaderDest
		if [ -n "$SHADDESTDIR" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using SHADDESTDIR '$SHADDESTDIR' for '$1'"
			export "$1"="$SHADDESTDIR"
		elif [ -n "$EFD" ]; then
			loadCfg "$GEMETA/$AID.conf" X
			if [ -z "$EXECUTABLE" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Using the base game directory '$EFD' as absolute game exe path - probably never reached?"
				export "$1"="$EFD"
			else
				if [ "$ISORIGIN" -eq 1 ] && grep -q "$L2EA" <<< "$EXECUTABLE" ; then
					MYMETA="$EVMETAID/${EVALSC}_${AID}.vdf"
					if [ -f "$MYMETA" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Origin game detected - looking for the real executable name instead of the used command '$EXECUTABLE'"
						touch "$FUPDATE"
						updateConfigEntry "ORIGINEXE" "$EXECUTABLE" "$GEMETA/${AID}.conf" # unused, but who knows what it is good for later
						# shellcheck disable=SC1003
						EXECUTABLE="$(grep "Uninstall" "$MYMETA" -A10 | grep "DisplayIcon" | awk -F '\\' '{print $NF}')"
						EXECUTABLE="${EXECUTABLE//\"}"
						touch "$FUPDATE"
						updateConfigEntry "EXECUTABLE" "$EXECUTABLE" "$GEMETA/${AID}.conf"
						GAMEEXE="${EXECUTABLE//.exe}"
						touch "$FUPDATE"
						updateConfigEntry "GAMEEXE" "$GAMEEXE" "$GEMETA/${AID}.conf"
					else
						writelog "WARN" "${FUNCNAME[0]} - Origin game detected - but $EVALSC file $MYMETA not found - can't look for original game name"
					fi
				else
					writelog "WARN" "${FUNCNAME[0]} - Using some weird old function to determine the absolute exe path - please report, this should never be reached"
				fi

				if grep -q "\\\\" <<< "$EXECUTABLE"; then
					RELEX="${EXECUTABLE//\\//}"
					FGEP="${EFD}/${RELEX%/*}"
					if [ ! -d "$FGEP" ] && [ -d "$EFD" ]; then
						if grep -q "/" <<< "${RELEX%/*}"; then
							FGEP="${EFD}"
							while read -r subdir; do
								FGEP="$(find "$FGEP" -iname "$subdir")"
							done <<< "$(tr '/' '\n' <<< "${RELEX%/*}")"
						else
							FGEP="$(find "${EFD}" -iname "${RELEX%/*}")"
						fi
					fi

					if [ -d "$FGEP" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Using '$FGEP' as absolute game exe path"
						export "$1"="$FGEP"
					fi
				fi
			fi
		fi
	fi
}

function setShaderDest {
	if [ -z "$SHADLOGGED" ]; then
		SHADLOGGED=0
	fi

	if [ -n "$(GETALTEXEPATH)" ]; then
		SHADDESTDIR="$(GETALTEXEPATH)"
		writelog "INFO" "${FUNCNAME[0]} - Overriding SHADDESTDIR to '$SHADDESTDIR' because ALTEXEPATH is set to '$ALTEXEPATH'"
	fi

	if [ -z "$SHADDESTDIR" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Determining Shader destination directory SHADDESTDIR"
		if [ -z "$1" ] || [ "$1" == "last" ]; then
			if [ "$ABSGAMEEXEPATH" != "$NON" ]; then
				SHADDESTDIR="${ABSGAMEEXEPATH%/*}"
				writelog "INFO" "${FUNCNAME[0]} - Using variable ABSGAMEEXEPATH for Shader destination directory '$SHADDESTDIR'"
			else
				resetAID "last"
				if [ -f "$LASTRUN" ] && grep -q "$AID" "$LASTRUN"; then
					ABSGAMEEXEPATH="$(grep "^PREVABSGAMEEXEPATH" "$LASTRUN" | cut -d '=' -f2)"
					ABSGAMEEXEPATHDIR="${ABSGAMEEXEPATH%/*}"
					if [ -d "${ABSGAMEEXEPATHDIR//\"/}" ]; then
						SHADDESTDIR="${ABSGAMEEXEPATHDIR//\"/}"
						writelog "INFO" "${FUNCNAME[0]} - Using last PREVABSGAMEEXEPATH variable from '$LASTRUN' for Shader destination directory '$SHADDESTDIR'"
					else
						writelog "WARN" "${FUNCNAME[0]} - Found PREVABSGAMEEXEPATH variable in '$LASTRUN' but its directory does not exist"
					fi
				else
					notiShow "$NOTY_NOAIDNOPREV"
				fi
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - Using argument '$1' for Shader destination directory '$SHADDESTDIR'"
			if [ -f "$1" ]; then
				SHADDESTDIR="$(dirname "$1")"
			else
				SHADDESTDIR="$1"
			fi
		fi

		if [ -n "$SHADDESTDIR" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using Shader destination directory '$SHADDESTDIR'"
		fi
	fi

	if [ -n "$SHADDESTDIR" ] && [ "$SHADLOGGED" -eq 0 ] ; then
		writelog "INFO" "${FUNCNAME[0]} - Shader destination directory is '$SHADDESTDIR'"
		SHADLOGGED=1
	fi
}

function disableThisGameShaderRepo {
	RMREP="$1"
	if [ -n "$2" ]; then
		SHADDESTDIR="$2"
	else
		setShaderDest
	fi

	if [ -z "$RSDSTS" ]; then
		RSDST="$SHADDESTDIR/$RSSUB"
		RSDSTS="$RSDST/Shaders"
		RSDSTT="$RSDST/Textures"
		RSDSTE="$RSDST/enabled"
	fi

	if [ -n "$RMREP" ] && [ -f "$RSDSTE/$RMREP" ]; then
		notiShow "$(strFix "$NOTY_SHADDIS" "$RMREP")"

		# removed disabled shaders
		if [ -d "$RSDSTS" ]; then
			while read -r syml; do
				if [[ "$(readlink "$syml")" =~ $RMREP ]]; then
					writelog "INFO" "${FUNCNAME[0]} - Removing shader symlink '$syml' from deactivated repo '$RMREP'" "X" "$SHADLOG"
					rm "$syml"
				fi
			done <<< "$(find -L "$RSDSTS")"
		fi
		# removed disabled textures
		if [ -d "$RSDSTT" ]; then
			while read -r syml; do
				if [[ "$(readlink "$syml")" =~ $RMREP ]]; then
					writelog "INFO" "${FUNCNAME[0]} - Removing texture symlink '$syml' from deactivated repo '$RMREP'" "X" "$SHADLOG"
					rm "$syml"
				fi
			done <<< "$(find -L "$RSDSTT")"
		fi
		rm "$RSDSTE/$RMREP"
	fi
}

function enableThisGameShaderRepo {
	SELREPO="$1"
	REPDIR="$STLSHADDIR/$SELREPO"

	if grep -q "^${SELREPO}$" "$SHADERREPOBLOCKLIST"; then
		writelog "SKIP" "${FUNCNAME[0]} - The selected repo '$SELREPO' is in the block list '$SHADERREPOBLOCKLIST'" "X" "$SHADLOG"
	else
		if [ -n "$2" ]; then
			SHADDESTDIR="$2"
		else
			setShaderDest
		fi

		if [ ! -d "$REPDIR" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - The directory '$REPDIR' for the selected repo '$SELREPO' does not exist" "X" "$SHADLOG"
		else
			if [ -z "$RSDSTS" ]; then
				RSDST="$SHADDESTDIR/$RSSUB"
				RSDSTS="$RSDST/Shaders"
				RSDSTT="$RSDST/Textures"
				RSDSTE="$RSDST/enabled"

				mkProjDir "$RSDSTS"
				mkProjDir "$RSDSTT"
				mkProjDir "$RSDSTE"
			fi

			if [ -f "$RSDSTE/$SELREPO" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - Repo '$SELREPO' is already activated for the game" "X" "$SHADLOG"
			else
				notiShow "$(strFix "$NOTY_SHADENA" "$SELREPO")"
				# updating shaders
				writelog "INFO" "${FUNCNAME[0]} - Updating shaders for activated repo '$SELREPO'" "X" "$SHADLOG"
				SHADERSRC="$(find "$REPDIR" -type d -iname "Shaders")"
				if [ -n "$SHADERSRC" ]; then
					while read -r shaderfile; do
						writelog "INFO" "${FUNCNAME[0]} - Creating symlink '$RSDSTS/${shaderfile##*/}' for shader '$shaderfile'" "X" "$SHADLOG"
						ln -s "$shaderfile" "$RSDSTS/${shaderfile##*/}" 2>/dev/null
					done <<< "$(find "$SHADERSRC" -mindepth 1 -maxdepth 1)"
					touch "$RSDSTE/$SELREPO"
				fi

				# updating textures
				writelog "INFO" "${FUNCNAME[0]} - Updating textures for activated repo '$SELREPO'" "X" "$SHADLOG"
				TEXTURESRC="$(find "$REPDIR" -type d -iname "Textures")"
				if [ -n "$TEXTURESRC" ]; then
					while read -r texfile; do
						writelog "INFO" "${FUNCNAME[0]} - Creating symlink '$RSDSTT/${texfile##*/}' for texture '$texfile'" "X" "$SHADLOG"
						ln -s "$texfile" "$RSDSTT/${texfile##*/}" 2>/dev/null
					done <<< "$(find "$TEXTURESRC" -mindepth 1 -maxdepth 1)"
				fi
			fi
		fi
	fi
}

function GameShaderDialog {
	touch "$SHADERREPOBLOCKLIST"

	setShaderDest "$1"

	RSDST="$SHADDESTDIR/$RSSUB"
	RSDSTS="$RSDST/Shaders"
	RSDSTT="$RSDST/Textures"
	RSDSTE="$RSDST/enabled"

	if [ -f "$SHADLOG" ]; then
		rm "$SHADLOG" 2>/dev/null
	fi

	if [ "$SHADDESTDIR" != "$NON" ]; then
		mkProjDir "$RSDSTS"
		mkProjDir "$RSDSTT"
		mkProjDir "$RSDSTE"
	fi

	if [ -d "$RSDST" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Opening Shader Selection Dialog for dir '$RSDST'"

		SHADDLLAST="$STLSHADDIR/lastdl.txt"
		MAXAGE=1440

		if [ ! -f "$SHADDLLAST" ] || test "$(find "$SHADDLLAST" -mmin +"$MAXAGE")"; then
			StatusWindow "$GUI_DLSHADER" "dlShaders" "DownloadShadersStatus"
			echo "$(date) - ${FUNCNAME[0]}" > "$SHADDLLAST"
		fi

		export CURWIKI="$PPW/Shader-Management"
		TITLE="${PROGNAME}-Shader"
		pollWinRes "$TITLE"

		setShowPic

		unset AVAILREPOS
		unset SELREPOS
		unset UNSELREPOS

		# appending a ';' to the reponames to prevent cutting the wrong, similar filename
		mapfile -d "|" -t -O "${#AVAILREPOS[@]}" AVAILREPOS <<< "$(find "$STLSHADDIR" -mindepth 1 -maxdepth 1 -not -empty -type d -printf "%p;\n")"

		SELREPOS="$(while read -r repo; do REPONAME="${repo##*/}"; if [ -f "$RSDSTE/${REPONAME//;}" ]; then	echo TRUE ; echo "${REPONAME//;}"; else echo FALSE ; echo "${REPONAME//;}" ;fi ; done <<< "$(printf "%s\n" "${AVAILREPOS[@]}")" | \
		"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center "$WINDECO" --list --checklist --column="$GUI_ADD" --column=Shader-Repo --separator=" " --print-column="2" \
		--text="$(spanFont "$(strFix "$GUI_SHADERDIALOG" "${RSDST##*/}")" "H")" --title="$TITLE" "$GEOM")"
		case $? in
			0)  {
					UNSELREPO=( "${AVAILREPOS[@]}" )

					if [ -n "${SELREPOS[0]}" ]; then
						writelog "INFO" "${FUNCNAME[0]} - At least one repo was enabled, so automatically enabling required repo '$RSSUB'" "X" "$SHADLOG"
						SELREPOS=( "${SELREPOS[@]}" "$RSSUB" )

						writelog "INFO" "${FUNCNAME[0]} - Activating shaders for enabled repos" "X" "$SHADLOG"

						while read -r SELREPO; do
							writelog "INFO" "${FUNCNAME[0]} - Enabled: $SELREPO" "X" "$SHADLOG"
							unset REPDIR
							enableThisGameShaderRepo "$SELREPO"
							REPDIR="$STLSHADDIR/${SELREPO};"
							UNSELREPO=( "${UNSELREPO[@]/$REPDIR}" )

						done <<< "$(printf "%s\n" "${SELREPOS[@]}")"
					fi

					writelog "INFO" "${FUNCNAME[0]} - Deactivating shaders for disabled repos" "X" "$SHADLOG"

					while read -r UNSEL; do
						if [ -n "$UNSEL" ] && [ "$UNSEL" != ";" ]; then
							unset RMREP
							RMREP="${UNSEL//;}"
							RMREP="${RMREP##*/}"
							if [ "$RMREP" != "$RSSUB" ]; then
								writelog "INFO" "${FUNCNAME[0]} - Disabled: $RMREP" "X" "$SHADLOG"
								disableThisGameShaderRepo "$RMREP"
							fi
						fi
					done <<< "$(printf "%s\n" "${UNSELREPO[@]}")"

					if [ -z "${SELREPOS[0]}" ] && [ -f "$RSDSTE/$RSSUB" ]; then
						writelog "INFO" "${FUNCNAME[0]} - No repo was enabled, so also disabling the repo '$RSSUB'" "X" "$SHADLOG"
						disableThisGameShaderRepo "$RSSUB"
					fi

					writelog "INFO" "${FUNCNAME[0]} - Deactivating shaders for blocked repos" "X" "$SHADLOG"
					while read -r BLOCKREP; do
						disableThisGameShaderRepo "$BLOCKREP"
					done < "$SHADERREPOBLOCKLIST"
				}
			;;
			1)  writelog "INFO" "${FUNCNAME[0]} - Selected CANCEL"
			;;
		esac
	else
		writelog "SKIP" "${FUNCNAME[0]} - Dest Dir '$SHADDESTDIR' does not exist and could not be created - skipping"
		if [ -z "$SHADDESTDIR" ]; then
			SHADDESTDIR="$NON"
		fi
		notiShow "$(strFix "$NOTY_MISSDIR" "$SHADDESTDIR")"
	fi

	if [ -n "$2" ];	then "$2";	fi
}

function getArch {
	# maybe remove reduntant lines later
	if [ "$(file "$1" | grep -c "PE32 ")" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Architecture for '$1' is 32bit"
		echo "32"
	elif [ "$(file "$1" | grep -c "PE32+ ")" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Architecture for '$1' is 64bit"
		echo "64"
	else
		if [ "$(find "$(dirname "$1")" -name "*.exe" | wc -l)" -ge 0 ]; then
			TESTEXE="$(find "$(dirname "$1")" -name "*.exe" | head -n1)"
			if [ "$(file "$TESTEXE" | grep -c "PE32 ")" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Architecture for bundled '$TESTEXE' for '$1' is 32bit"
				echo "32"
			elif [ "$(file "$TESTEXE" | grep -c "PE32+ ")" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Architecture for bundled '$TESTEXE' for '$1' is 64bit"
				echo "64"
			fi
		elif [ "$(find "$(dirname "$1")" -name "*.dll" | wc -l)" -ge 0 ]; then
			TESTDLL="$(find "$(dirname "$1")" -name "*.dll" | head -n1)"
			if [ "$(file "$TESTDLL" | grep -c "PE32 ")" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Architecture for bundled '$TESTDLL' for '$1' is 32bit"
				echo "32"
			elif [ "$(file "$TESTDLL" | grep -c "PE32+ ")" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Architecture for bundled '$TESTDLL' for '$1' is 64bit"
				echo "64"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Could not detect architecture for '$1' directly or indirectly"
		fi
	fi
}

function chooseShaders {
	if [ "$CHOOSESHADERS" -eq 1 ]; then
		setShadDestDir
		writelog "INFO" "${FUNCNAME[0]} - Opening Shader Menu - shader destination path is '$SHADDESTDIR'"
		GameShaderDialog "$SHADDESTDIR"
	fi
}

# Sort & add given ReShade DLL name to our tracked list of ReShade DLLs, if it is not already present
function appendToRSTXT {
	if ! [ -f "$INSTDESTDIR/$RSTXT" ]; then
		writelog "INFO" "${FUNCNAME[0]} - '$INSTDESTDIR/$RSTXT' does not already exist -- Will create a new file"
		touch "$INSTDESTDIR/$RSTXT"
	fi

	if ! grep -qw "$1" "$INSTDESTDIR/$RSTXT"; then
		echo "$1" >> "$INSTDESTDIR/$RSTXT"
		writelog "INFO" "${FUNCNAME[0]} - Added '$1' to list of tracked ReShade DLLs at to '$INSTDESTDIR/$RSTXT'"
	else
		writelog "INFO" "${FUNCNAME[0]} - ReShade DLL '$1' already on list of tracked ReShade DLLs at '$INSTDESTDIR/$RSTXT' - Nothing to do."
	fi

	sort "$INSTDESTDIR/$RSTXT" -u -o "$INSTDESTDIR/$RSTXT"
}

# $2 used to specify NOD3D9 wehn we copied both DXGI and D3D9 DLLs, but we no longer do that, so $2 is unused
# Last refactored for: https://github.com/sonic2kk/steamtinkerlaunch/pull/881
function installRSdll {
	RSDLLNAMECONFLICTFOUND=0

	# Manage creating backup if untracked DLL with our selected ReShade DLL name already exists at location
	# This function could be changed in future to take the path as a parameter as well, but that was not important at time of writing
	function manageDuplicateRSDLL {
		writelog "WARN" "${FUNCNAME[0]} - DLL with name '$1' found in game dir '$INSTDESTDIR' but is not tracked by us - This is possibly a game/mod DLL"
		writelog "WARN" "${FUNCNAME[0]} - Backing up DLL at '$INSTDESTDIR/$1' to '$INSTDESTDIR/${1}.bak' and moving our ReShade DLL anyway"

		if [ -f "$INSTDESTDIR/${1}.bak" ]; then
			writelog "ERROR" "${FUNCNAME[0]} - ERROR: Back-up DLL name '${1}.bak' already exists -- This is probably a very bad thing!"
		fi

		mv "$INSTDESTDIR/$1" "$INSTDESTDIR/${1}.bak" 2>/dev/null
		cp "$RESHADESRCDIR/$RSVERS/$2" "$INSTDESTDIR/$1" >/dev/null 2>/dev/null
	}

	if [ ! -f "$INSTDESTDIR/$1" ] || [ "$1" == "F" ]; then
		if [ ! -f "$RESHADESRCDIR/$RSVERS/$3" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Sourcefile '$RESHADESRCDIR/$RSVERS/$3' missing - skipping this file"
		else
			# Installing DLL for the first time
			cp "$RESHADESRCDIR/$RSVERS/$3" "$INSTDESTDIR/$1" >/dev/null 2>/dev/null
			writelog "INFO" "${FUNCNAME[0]} - Copied '$RESHADESRCDIR/$RSVERS/$3' to '$INSTDESTDIR/$1'"
		fi
	else
		# Check for ReShade DLL name conflicts
		if [ -f "$INSTDESTDIR/$1" ] && [ -f "$INSTDESTDIR/$RSTXT" ] && ! grep -qw "$1" "$INSTDESTDIR/$RSTXT"; then
			RSDLLNAMECONFLICTFOUND=1
		fi

		if [ "$RESHADEUPDATE" -eq 0 ]; then
			if [ "$RSDLLNAMECONFLICTFOUND" -eq 1 ]; then
				# DLL with name matching our ReShade DLL name exists, but was not installed by us (missing from ReShade.txt) - Backing up existing DLL and installing ReShade DLL anyway
				writelog "INFO" "${FUNCNAME[0]} - ReShade update is DISABLED"
				writelog "WARN" "${FUNCNAME[0]} - Specified ReShade DLL name conflict detected!"

				manageDuplicateRSDLL "$1" "$3"  # 1 = target ReShade DLL name, 3 = ReShade DLL name from STL downloads folder to copy with the name specified in $1
			else
				writelog "SKIP" "${FUNCNAME[0]} - Destfile '$INSTDESTDIR/$1' already exists and is tracked by us, but not checking the installed version, because RESHADEUPDATE is '$RESHADEUPDATE'"
			fi
		else
			if grep -q "${RSVERS%%_*}" <<< "$(strings "$INSTDESTDIR/$1" | grep "^Initializing")"; then
				# Existing ReShade DLL found and matches our selected version -- Don't update
				writelog "SKIP" "${FUNCNAME[0]} - Destfile '$INSTDESTDIR/$1' already exists and looks up-to-date - skipping this file"
			else
				if [ "$RSDLLNAMECONFLICTFOUND" -eq 1 ]; then
					writelog "INFO" "${FUNCNAME[0]} - ReShade update is ENABLED"
					writelog "INFO" "${FUNCNAME[0]} - Specified ReShade DLL name conflict detected!"

					manageDuplicateRSDLL "$1" "$3"  # 1 = target ReShade DLL name, 3 = ReShade DLL name from STL downloads folder to copy with the name specified in $1
				else
					# DLL either does not already exist or if it does, it's in the ReShade.txt file
					writelog "INFO" "${FUNCNAME[0]} - Destfile '$INSTDESTDIR/$1' already exists, but has a different version or is not a ReShade DLL - updating"
					cp "$RESHADESRCDIR/$RSVERS/$3" "$INSTDESTDIR/$1" >/dev/null 2>/dev/null
				fi
			fi
		fi
	fi
}

# install reshade:
function installReshade {
	if [ "$USERESHADE" -eq 1 ]; then
		prepareReshadeFiles
		setShadDestDir  # Have to use setShadDestDir because setShadDest will use ABSGAMEEXEPATH which is not Custom Command

		INSTDESTDIR="$SHADDESTDIR"

		# Default ReShade DLL name to use to dxgi.dll if no DLL name is provided
		if [ -z "$RESHADEDLLNAME" ]; then
			writelog "INFO" "${FUNCNAME[0]} - RESHADEDLLNAME is blank - Defaulting to '$DXGI'"
			RESHADEDLLNAME="$DXGI"
		fi

		# checking for previous dll conficts between $RS_DX_DEST and $RS_D9_DEST
		# note: modern ReShade uses "ReShade_exenamehere.log", and old versions use "dxgi.log" (if ReShade dll is named dxgi.dll). we support both names.
		RESHADE_CONFLICTS=$(find "$INSTDESTDIR" -maxdepth 1 \( -name "${RS_DX_DEST//.dll/.log}" -or -name "ReShade_*.log" \) -print0 | xargs -0 -r grep -l "Another ReShade instance was already loaded from" | wc -l)
		if [ "$RESHADE_CONFLICTS" -ge 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found $RS_DX_DEST conflict with $RS_D9_DEST"
			if [ -f "$INSTDESTDIR/$RS_D9_DEST" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Removing $RS_D9_DEST"
				rm "$INSTDESTDIR/$RS_D9_DEST"
			else
				writelog "SKIP" "${FUNCNAME[0]} - $RS_D9_DEST not found"
			fi

			if [ -z "$NOD3D9" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Blocking re-installation of '$RS_D9_DEST' by setting NOD3D9=1 in '$STLGAMECFG'"
				updateConfigEntry "NOD3D9" "1" "$STLGAMECFG"
				export NOD3D9=1
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - No conflict found in old logfiles"
		fi

		getReShadeExeArch

		if [ -d "$INSTDESTDIR" ]; then
			# Get ReShade DLL names from comma separated list -- User will probably mostly only pass one, but this will handle cases where they might want multiple (ex: d3d9, opengl32)
			RSDLLNAMEARR=()  # Make sure the array of DLL names is always reset when installReshade is called, to avoid duplicate entries
			mapfile -d "," -t -O "${#RSDLLNAMEARR[@]}" RSDLLNAMEARR < <(printf '%s' "$RESHADEDLLNAME")

			for CUSTRSDLL in "${RSDLLNAMEARR[@]}"; do
				# Append extension if no extension in DLL
				if ! [[ $CUSTRSDLL == *.* ]]; then
					CUSTRSDLL="${CUSTRSDLL}.dll"
				fi
			done

			# Check architecture to know which ReShade DLL architectures to copy over
			if [ "$(getArch "$CHARCH")" == "32" ]; then
				#32bit:
				writelog "INFO" "${FUNCNAME[0]} - Installing 32bit ${RESH} as '$CHARCH' is 32bit"
				RSD3D47DLL="$D3D47_32"
				RSARCHDLL="$RS_32"
			elif [ "$(getArch "$CHARCH")" == "64" ]; then
				#64bit:
				writelog "INFO" "${FUNCNAME[0]} - Installing 64bit ${RESH} as '$CHARCH' is 64bit"
				RSD3D47DLL="$D3D47_64"
				RSARCHDLL="$RS_64"
			else
				#feelsbad.jpg:
				writelog "SKIP" "${FUNCNAME[0]} - ERROR in ${RESH} installation - no file information detected for '$CHARCH' or any 'neighbor file' - setting USERESHADE=0 for this session"
				export USERESHADE=0
			fi

			# Common conditional to install either 32bit/64bit ReShade DLLs, since process is same, just different DLL names
			# USESPECIALK check needed because we can't use custom DLL names when using SpecialK -- It expects the raw ReShade32/ReShade64 DLL names
			#
			# Only install ReShade "normally" if (ReShade is on and SpecialK is off) or (if ReShade+SpecialK are on and ReShade SpecialK Plugin is disabled)
			if [[ ( "$USERESHADE" -eq 1 && "$USESPECIALK" -eq 0 ) || ( "$USERESHADE" -eq 1 && "$USESPECIALK" -eq 1 && "$USERESHSPEKPLUGIN" -eq 0 ) ]]; then
				removeReShadeSpecialKInstallation "1"  # Remove any existing ReShade SpecialK Plugin installation so we can replace it with a 'regular' ReShade install

				# Check to make sure none of our ReShade names conflict with SpecialK -- This will abort install, and is the only place we need to abort install
				if [ "$USESPECIALK" -eq 1 ] && [ "$USERESHSPEKPLUGIN" -eq 0 ]; then
					getSpecialKGameRenderApi
					SPEKREALDLLNAME="$( basename "$SPEKDST" )"
					for CUSTRSDLL in "${RSDLLNAMEARR[@]}"; do
						if [[ "$CUSTRSDLL" = "$SPEKREALDLLNAME" ]] && [ -f "$SPEKREALDLLNAME" ] && [ ! -f "$INSTDESTDIR/$RSTXT" ]; then
							# If our ReShade DLL matches the chosen SpecialK DLL name, if the SpecialK DLL is already in the game files, and if it's not a ReShade DLL (i.e. RSTXT doesn't exist), assume we have a SpecialK conflict
							# If ReShade+SpecialK are installed fresh at the same time, ReShade is installed early, and this logic prevents logging that ReShade won't be installed because it conflicts with SpecialK
							# Which is incorrect because it's detecting our installed ReShade DLLs as SpecialK DLLs
							writelog "ERROR" "${FUNCNAME[0]} - SpecialK is enabled and the chosen ReShade DLL name conflicts with the SpecialK DLL name -- Not installing ReShade"
							notiShow "$( strFix "$NOTY_SPEKRESHDLLCONFLICT" "$SPEK" "$RESH" )"

							return
						fi
					done
				fi

				# actual ReShade DLL name (either d3d9/d3d11/dxgi, or a custom user selected name)
				# notiShow "$NOTY_RESHADEINSTALLING"
				for CUSTRSDLL in "${RSDLLNAMEARR[@]}"; do
					installRSdll "$CUSTRSDLL" "0" "$RSARCHDLL"
				done

				#d3d47 - Required for ReShade
				# NOTE 25/08/23: *Is* it still required?
				installd3d47dll "$RSD3D47DLL" "$INSTDESTDIR"

				# Rewrite the ReShade TXT file to ensure it only has our installed ReShade DLLs
				if [ -f "$RSTXT" ]; then
					rm "$RSTXT"
				fi

				# Add d3dcompiler_47 and custom ReShade DLL names, removing any non-ReShade DLLs (ensures previously entered DLL names get removed and not incorrectly tracked as ReShade DLLs)
				# appendToRSTXT "$D3D47"
				for CUSTRSDLL in "${RSDLLNAMEARR[@]}"; do
					writelog "INFO" "${FUNCNAME[0]} - Writing '$CUSTRSDLL' to '$RSTXT'"
					appendToRSTXT "$CUSTRSDLL"
				done
			else
				if [ "$USERESHADE" -eq 1 ] && [ "$USESPECIALK" -eq 1 ]; then
					# End here, as ReShade Installation code will be handled by SpecialK
					writelog "SKIP" "${FUNCNAME[0]} - USERESHADE and USESPECIALK are enabled together, and ReShade is being used as a Plugin, skipping custom ReShade DLL name as SpecialK needs specific ReShade DLL names"
				fi
			fi

			# This makes sure if we updated any DLL names in RSDLLNAMEARR to end with '.dll' that these are written out to the game config file
			# Doing this ensures we don't end up with the array containing 'dxgi.dll' but the config file value being 'dxgi' (if the user left out the extension)
			# This is not strictly necessary but I wanted this consistency -- It's also why we loop through RSDLLNAMEARR twice :-)
			touch "$FUPDATE"
			CONFIGRSDLLNAMESTR="$( printf '%s,' "${RSDLLNAMEARR[@]}" )"
			writelog "INFO" "${FUNCNAME[0]} - Updating RESHADEDLLNAME config entry to include any potentially-updated ReShade DLL names so they all end with '.dll' if no extension was provided"
			updateConfigEntry "RESHADEDLLNAME" "${CONFIGRSDLLNAMESTR%,}" "$STLGAMECFG"
		else
			writelog "SKIP" "${FUNCNAME[0]} - INSTDESTDIR '$INSTDESTDIR' not found"
		fi
	fi
}

# Remove ReShade files (i.e. to replace with SpecialK)
function removeReShadeInstallation {
	KEEPRESHADEINI="${1:-0}"

	writelog "INFO" "${FUNCNAME[0]} - INSTDESTDIR is '$INSTDESTDIR'"

	while read -r RSDLLREMOVEFILE; do
		RSDLLREMOVEPATH="${INSTDESTDIR}/$RSDLLREMOVEFILE"
		if [ -f "$RSDLLREMOVEPATH" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Removing non-SpecialK ReShade DLL from '$RSDLLREMOVEPATH'"
			rm "$RSDLLREMOVEPATH"
		fi
	done < "$INSTDESTDIR/$RSTXT"

	if [ -f "$INSTDESTDIR/$RSINI" ] && [ "$KEEPRESHADEINI" -eq 0 ]; then
		rm "$INSTDESTDIR/$RSINI"
	fi

	if [ -f "$INSTDESTDIR/$RSTXT" ]; then
		rm "$INSTDESTDIR/$RSTXT"
	fi
}

# Remove ReShade SpecialK Plugin installation (i.e. to replace it with non-plugin installation)
function removeReShadeSpecialKInstallation {
	KEEPRESHADEINI="${1:-0}"

	# DLLs and JSON files
	RESHSPEKREMOVEDLLS=( "$INSTDESTDIR/$RS_32" "$INSTDESTDIR/${RS_32//.dll/.json}" "$INSTDESTDIR/$RS_64" "$INSTDESTDIR/${RS_64//.dll/.json}" )
	for RESHSPEKREMOVEDLL in "${RESHSPEKREMOVEDLLS[@]}"; do
		rmFileIfExists "$RESHSPEKREMOVEDLL"
	done

	# INI file
	if [ "$KEEPRESHADEINI" -eq 0 ]; then
		rmFileIfExists "$INSTDESTDIR/$RSINI"
	fi
}

function installDepth3DReshade {
	SHADERPOOL="depth3d"

	if [ "$RESHADE_DEPTH3D" -eq 1 ]; then
		StatusWindow "$GUI_DLSHADER" "dlShaders $SHADERPOOL" "DownloadCustomProtonStatus"
		setShadDestDir
		enableThisGameShaderRepo "$SHADERPOOL"
	fi
}

# Get archiecture of executable that ReShade is being used for, so we know which DLL to copy (32bit/64bit)
function getReShadeExeArch {
	if [ -n "$ARCHALTEXE" ] && [[ ! "$ARCHALTEXE" =~ ${DUMMYBIN}$ ]]; then
		CHARCH="$ARCHALTEXE"
	else
		CHARCH="$GP"
	fi
}

# Install steps for ReShade and SpecialK are a bit different
function installReshadeForSpecialK {
	writelog "INFO" "${FUNCNAME[0]} - Installing ReShade DLLs for use with SpecialK (DLLs will not be renamed so SpecialK can read them)"

	# Raw copy ReShade DLLs using installRSdll -- Should make integrating things like ReShade update easier
	# These DLLSs are not tracked, we should be tracking them in ReShade.txt so toggling ReShade off correctly removes them
	# When turning ReShade off we should also check the DLL names and if SpecialK is no longer in use, to clean up a SpecialK+ReShade install (i.e. if using ReShade64.dll but SpecialK is off, just remove instead of renaming to .dll_off)	installRSdll "$RS_32" "0" "$RS_32"
	getReShadeExeArch

	# Very similar logic used for installReshade
	if [ "$(getArch "$CHARCH")" == "32" ]; then
		# Remove any existing ReShade DLLs so they can be replaced with SpecialK ones
		# TODO check if INI needs renamed someway
		removeReShadeInstallation "1"  # Remove any existing ReShade installation

		#32bit
		writelog "INFO" "${FUNCNAME[0]} - Installing 32bit ${RESH} for ${SPEK} as '$CHARCH' is 32bit"
		installd3d47dll "$D3D47_32" "$INSTDESTDIR"
		installRSdll "$RS_32" "0" "$RS_32"
		installRSdll "${RS_32//.dll/.json}" "0" "${RS_32//.dll/.json}"
	elif [ "$(getArch "$CHARCH")" == "64" ]; then
		removeReShadeInstallation "1"  # Remove any existing ReShade installation

		#64bit
		writelog "INFO" "${FUNCNAME[0]} - Installing 64bit ${RESH} for ${SPEK} as '$CHARCH' is 64bit"
		installd3d47dll "$D3D47_64" "$INSTDESTDIR"
		installRSdll "$RS_64" "0" "$RS_64"
		installRSdll "${RS_64//.dll/.json}" "0" "${RS_64//.dll/.json}"
	else
		writelog "SKIP" "${FUNCNAME[0]} - ERROR in ${RESH}+${SPEK} installation - no file information detected for '$CHARCH' or any 'neighbor file' - setting USERESHADE=0 for this session"
		export USERESHADE=0
	fi
}

# Helper to create ReShade INI
function createReShadeINI {
	if [ "$CREATERESHINI" -eq 0 ]; then
		writelog "SKIP" "${FUNCNAME[0]} - ReShade INI creation is disabled (CREATERESHINI is '$CREATERESHINI') -- Skipping"
		return
	fi

	writelog "INFO" "${FUNCNAME[0]} - Creating ReShade INI file"

	if [ -f "$FRSINI" ]; then
		if grep -q "EffectSearchPaths=.\$RSSUB\Shaders" "$FRSINI"; then
			writelog "SKIP" "${FUNCNAME[0]} - Already have '$FRSINI' with default paths pointing to '$RSSUB'"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Found a '$FRSINI' without default paths pointing to '$RSSUB' - not touching it"
		fi
	else
		if [ -f "$FRSOINI" ] && grep -q "EffectSearchPaths=.*$RSSUB.*Shaders" "$FRSOINI"; then
			writelog "INFO" "${FUNCNAME[0]} - Re-enabling previously disabled '$FRSOINI'"
			mv "$FRSOINI" "$FRSINI"
		else
			# This used to use echo but was changed to use printf to address ShellCheck SC2028
			# In testing using both echo and printf produced the same string result, but if this causes issues we can re-evaluate
			writelog "INFO" "${FUNCNAME[0]} - Creating initial '$FRSINI' with default paths pointing to '$RSSUB'"
			{
				echo "[GENERAL]"
				printf "EffectSearchPaths=.\\%s\Shaders\n" "$RSSUB"
				printf "TextureSearchPaths=.\\%s\Textures\n" "$RSSUB"
				echo "PreprocessorDefinitions=RESHADE_DEPTH_LINEARIZATION_FAR_PLANE=1000.0,RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN=0,RESHADE_DEPTH_INPUT_IS_REVERSED=1,RESHADE_DEPTH_INPUT_IS_LOGARITHMIC=0"
			} > "$FRSINI"
		fi
	fi
}

function checkReshade {
	setShadDestDir

	RSLIST="$SHADDESTDIR/$RSTXT"
	RSOLIST="${RSLIST}_off"
	FRSINI="$SHADDESTDIR/$RSINI"
	FRSOINI="$SHADDESTDIR/${RSINI}_off"

	# TODO remove later:
	RSENABLED="${RESH}-${PROGNAME,,}-enabled.txt"
	RSDISABLED="${RESH}-${PROGNAME,,}-disabled.txt"
	# this doesn't cover all migration constellations, but better than nothing
	if [ "$USERESHADE" -eq 1 ] && [ -f "$SHADDESTDIR/$RSENABLED" ]; then
		mv "$SHADDESTDIR/$RSENABLED" "$RSLIST"
		if [ -f "$SHADDESTDIR/$RS_DX_DEST" ] && grep -q "$RESH" "$SHADDESTDIR/$RS_DX_DEST"; then
			echo "$RS_DX_DEST" >> "$RSLIST"
		fi

		if [ -f "$SHADDESTDIR/$RS_D9_DEST" ] && grep -q "$RESH" "$SHADDESTDIR/$RS_D9_DEST"; then
			echo "$RS_D9_DEST" >> "$RSLIST"
		fi
		sort "$RSLIST" -u -o "$RSLIST"
	elif [ "$USERESHADE" -eq 1 ] && [ -f "$SHADDESTDIR/$RSDISABLED" ]; then
		mv "$SHADDESTDIR/$RSDISABLED" "$RSOLIST"
		if [ -f "$SHADDESTDIR/$RS_DX_DEST" ] && grep -q "$RESH" "$SHADDESTDIR/$RS_DX_DEST"; then
			echo "$RS_DX_DEST" >> "$RSOLIST"
		fi

		if [ -f "$SHADDESTDIR/$RS_D9_DEST" ] && grep -q "$RESH" "$SHADDESTDIR/$RS_D9_DEST"; then
			echo "$RS_D9_DEST" >> "$RSOLIST"
		fi
		sort "$RSOLIST" -u -o "$RSOLIST"
	fi

	if [ "$USERESHADE" -eq 1 ]; then
		createReShadeINI

		# EXPERIMENTALLY RE-ENABLED
		# NOTE that this has no ReShade updating or version override checks, so it is missing many features that regular ReShade has!
		if [ "$USESPECIALK" -eq 1 ] && [ "$USERESHSPEKPLUGIN" -eq 1 ]; then
			writelog "WARN" "${FUNCNAME[0]} - Both '$SPEK' and '$RESH' are enabled." "E"
			writelog "WARN" "${FUNCNAME[0]} - This has historically caused crashes, but has been experimentally re-enabled!"
			writelog "WARN" "${FUNCNAME[0]} - Manual intervention may be required to fix crashes, such as renaming the SpecialK DLL to fix the SpecialK UI, or running dos2unix on INI files to fix crashes"
			writelog "WARN" "${FUNCNAME[0]} - For more information, see: https://github.com/sonic2kk/steamtinkerlaunch/issues/894"

			writelog "INFO" "${FUNCNAME[0]} - Using ${RESH} and $SPEK together"
			mkProjDir "$SHADDESTDIR"
			installReshadeForSpecialK
		else
			if [ -f "$RSOLIST" ]; then
				writelog "INFO" "${FUNCNAME[0]} - ${RESH} has been disabled previously using '${PROGNAME,,}' - enabling it now"
				while read -r rsdll; do
					if [ -f "$SHADDESTDIR/${rsdll}_off" ]; then
						mv "$SHADDESTDIR/${rsdll}_off" "$SHADDESTDIR/$rsdll"
					else
						writelog "WARN" "${FUNCNAME[0]} - '$SHADDESTDIR/${rsdll}_off' was supposed to be reenabled, but the file is missing"
					fi
				done < "$SHADDESTDIR/${RSTXT}_off"
				mv "$RSOLIST" "$RSLIST"
			fi

			if [ ! -f "$SHADDESTDIR/$D3D47" ]; then
				writelog "INFO" "${FUNCNAME[0]} - USERESHADE is '$USERESHADE' - looks like ${RESH} is not yet installed in '$SHADDESTDIR' - installing because USERESHADE is enabled"
				installReshade
			fi

			if [ -f "$FRSINI" ] && [ ! -f "$RSLIST" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Looks like ${RESH} was installed previously using '${PROGNAME,,}' without creating '$RSLIST' - recreating it now"
				installReshade F
			fi

			writelog "INFO" "${FUNCNAME[0]} - Setting WINEDLLOVERRIDES for ${RESH}: dxgi=n,b;d3d9=n,b;${D3D47//.dll}=n,b;d3d11=n,b;opengl32=n,b;${RESHADEDLLNAME//.dll}=n,b"
			WINEDLLOVERRIDES="$WINEDLLOVERRIDES;dxgi=n,b;d3d9=n,b;${D3D47//.dll}=n,b;d3d11=n,b;opengl32=n,b;${RESHADEDLLNAME//.dll}=n,b"
			if [ "$USESPECIALK" -eq 1 ] && [ "$USERESHSPEKPLUGIN" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Adding SpecialK DLL name to WINEDLLOVERRIDES because it is enabled and 'USERESHSPEKPLUGIN' is also enabled"
				WINEDLLOVERRIDES+=";$( basename "$SPEKDST" )=n,b"
			fi
			export WINEDLLOVERRIDES="$WINEDLLOVERRIDES"
		fi
	else
		if [ -f "$FRSINI" ]; then
			writelog "INFO" "${FUNCNAME[0]} - ${RESH} has been disabled by the user, so renaming '$FRSINI' to '$FRSOINI'"
			mv "$FRSINI" "$FRSOINI"
		fi

		if [ -f "$RSLIST" ]; then
			writelog "INFO" "${FUNCNAME[0]} - ${RESH} has been installed previously with '${PROGNAME,,}' - disabling it now"
			while read -r rsdll; do
				if [ -f "$SHADDESTDIR/${rsdll}" ]; then
					mv "$SHADDESTDIR/$rsdll" "$SHADDESTDIR/${rsdll}_off"
				else
					writelog "WARN" "${FUNCNAME[0]} - '$SHADDESTDIR/${rsdll}' was supposed to be disabled, but the file is already missing"
				fi
			done < "$RSLIST"
			mv "$RSLIST" "$RSOLIST"
		fi

		if [ "$USESPECIALK" -eq 0 ]; then
			if [ -f "$SHADDESTDIR/$RS_DX_DEST" ] && grep -q "$RESH" "$SHADDESTDIR/$RS_DX_DEST"; then
				writelog "WARN" "${FUNCNAME[0]} - Found unknown '$RESH' dll '$RS_DX_DEST' in '$SHADDESTDIR'"
			fi

			if [ -f "$SHADDESTDIR/$RS_D9_DEST" ] && grep -q "$RESH" "$SHADDESTDIR/$RS_D9_DEST"; then
				writelog "WARN" "${FUNCNAME[0]} - Found unknown '$RESH' dll '$RS_D9_DEST' in '$SHADDESTDIR'"
			fi
		fi
	fi
}

function extSpek {
	SRCARCH="$1"
	SPEXT64="$SPEKDLDIR/$SPEKVERS/${SPEK}64.dll"

	if [ -f "$SPEXT64" ] && [ "$AUTOSPEK" -eq 0 ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Already have '$SPEXT64' - skipping extraction" "E"
	else
		if [ ! -f "$SRCARCH" ]; then
			SRCARCH="${SRCARCH//lK/l_K}"
		fi
		if [ -f "$SRCARCH" ]; then
			SRCARCHEXT="${SRCARCH##*.}"
			if [ "$SRCARCHEXT" = "zip" ]; then  # zip archive from GitHub Actions (downloaded from nightly.link URL)
				writelog "INFO" "${FUNCNAME[0]} - Extracting '$SRCARCH' into '$SPEKDLDIR/$SPEKVERS' using '$UNZIP'"
				"$UNZIP" -o "$SRCARCH" -d "$SPEKDLDIR/$SPEKVERS"
			else  # else assume 7zip
				if [ -x "$(command -v "$SEVZA")" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Extracting '$SRCARCH' to '$SPEKDLDIR/$SPEKVERS'" "E"
					"$SEVZA" x "$SRCARCH" -o"$SPEKDLDIR/$SPEKVERS" 2>/dev/null
				else
					writelog "SKIP" "${FUNCNAME[0]} - Can't extract '$SRCARCH', because '$SEVZA' wasn't found!" "E"
				fi
			fi
		fi
	fi
}

# Get latest artifact download link from nightly.link
function getLatestNightlyLinkArtifactURL {
	NIGHTLYLINKURL="$1"
	NIGHTLYLINKURLPAT="$2"

	"$WGET" -q "${NIGHTLYLINKURL}" -O - 2> >(grep -v "SSL_INIT") | grep -oP "${NIGHTLYLINKURLPAT}" | head -n1  # Grep all links matching this pattern and pick the first one (should only be one anyway)
}

# Use innoextract to extract SpecialK32/64.dll from executable
function extractSpecialKEXE {
	POSSIBLESPEKEXE="$1"
	if  [ -x "$(command -v "$INNOEXTRACT")" ]; then
		SPEKEXESPEK32PATH="app/${SPEK}32.dll"
		SPEKEXESPEK64PATH="app/${SPEK}64.dll"

		SPEKEXEFILESLIST="$( "$INNOEXTRACT" "--list" "$POSSIBLESPEKEXE" )"

		if grep -q "$SPEKEXESPEK32PATH" <<< "$SPEKEXEFILESLIST" && grep -q "$SPEKEXESPEK64PATH" <<< "$SPEKEXEFILESLIST"; then
			notiShow "$( strFix "$NOTY_USESPEKCUSTOMEXE" "$( basename "$POSSIBLESPEKEXE" )" )"
			writelog "INFO" "${FUNCNAME[0]} - Found valid SpecialK executable to extract"
			# Extract EXE, select and move DLLs to SPEKVERS folder, remove all innoextract files
			"$INNOEXTRACT" -m -s -d "$SPEKDLDIR/$SPEKVERS" "$POSSIBLESPEKEXE"

			mv "$SPEKDLDIR/$SPEKVERS/$SPEKEXESPEK32PATH" "$SPEKDLDIR/$SPEKVERS"
			mv "$SPEKDLDIR/$SPEKVERS/$SPEKEXESPEK64PATH" "$SPEKDLDIR/$SPEKVERS"

			writelog "INFO" "${FUNCNAME[0]} - Successfully extracted '${SPEK}32.dll' and '${SPEK}64.dll' from '$( basename "$POSSIBLESPEKEXE" )'"
		else
			writelog "SKIP" "${FUNCNAME[0]} - SpecialK executable did not contain both '$SPEKEXESPEK32PATH' and '$SPEKEXESPEK64PATH' -- Not extracting"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - Cannot extract custom SpecialK EXE because dependency '$INNOEXTRACT' is missing!"
	fi
}

function dlSpecialK {
	if [ -n "$1" ]; then
		SPEKVERS="$1"
	fi

	SPEKARC="${SPEK}.7z"

	mkProjDir "$SPEKDLDIR/$SPEKVERS"
	mkProjDir "$SPEKDLDIR/custom"  # Ensure custom is here, in case user downloads SpecialK and then wants to use custom -- Very minor QoL

	if [ "$SPEKVERS" == "stable" ]; then
		SPEKDLURL="$SPEKURL$SPEKARC"
		writelog "INFO" "${FUNCNAME[0]} - Using Stable SpecialK download URL '$SPEKDLURL'"
	elif [ "$SPEKVERS" == "nightly" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using SpecialK Nightly release, fetching from nightly.link"

		SPEKAPIURLPATH="${SPEKPROJURL//$GHURL}"
		SPEKNIGHTLYHASH="$( fetchLatestGitHubActionsBuild "${AGHURL}/repos${SPEKAPIURLPATH}" 1 "Builds" 0 | cut -d ';' -f2 )"  # Get commit hash for latest SpecialK artifact from only success workflows named "Builds"
		SPEKNIGHTLYURL="https://nightly.link${SPEKAPIURLPATH}/workflows/build-windows/main"

		writelog "INFO" "${FUNCNAME[0]} - SpecialK GitHub Actions hash is '$SPEKNIGHTLYHASH'"
		writelog "INFO" "${FUNCNAME[0]} - SpecialK nightly.link URL is '$SPEKNIGHTLYURL'"

		SPEKNIGHTLYURLPATTERN="${SPEKNIGHTLYURL}.*?${SPEKNIGHTLYHASH}[a-zA-Z0-9].zip(?=\")"  # Hash in archive name is 8 chars, but fetchLatestGitHubActionsBuild only returns 7, so accept one extra alphanumeric character when parsing name
		SPEKDLURL="$( getLatestNightlyLinkArtifactURL "$SPEKNIGHTLYURL" "$SPEKNIGHTLYURLPATTERN" )"
		SPEKARC="$( basename "$SPEKDLURL" )"  # Need to make sure the archive name uses the nightly archive name, which isn't fixed

		writelog "INFO" "${FUNCNAME[0]} - SpecialK DL URL is '$SPEKDLURL'"
		writelog "INFO" "${FUNCNAME[0]} - SpecialK Archive name from DL URL is '$SPEKARC'"
	elif [ "$SPEKVERS" == "custom" ]; then
		writelog "INFO" "${FUNCNAME[0]} - SpecialK version '$SPEKVERS' selected, not downloading anything"
	else
		SPEKDLURL="$SPEKGHURL/download/SK_${SPEKVERS//./_}/$SPEKARC"
	fi

	SPEKDL="$SPEKDLDIR/$SPEKVERS/$SPEKARC"

	SPEK32SRC="$SPEKDLDIR/$SPEKVERS/${SPEK}32.dll"
	SPEK64SRC="$SPEKDLDIR/$SPEKVERS/${SPEK}64.dll"

	SPEK32BASE="${SPEK}32.dll"
	SPEK64BASE="${SPEK}64.dll"

	## For custom SpecialK, we either use existing placed DLLs or attempt to extract them from a SpecialK EXE
	## If we have no custom exe and no SpecialK32/64 DLL pair, SpecialK will not be installed
	if [ "$SPEKVERS" == "custom" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Custom SpecialK version selected -- Looking for manually placed SpecialK DLLs or EXE to extract them from"
		POSSIBLECUSTOMSPEKEXE="$( find "$SPEKDLDIR/$SPEKVERS" -type f -name "*.exe" -print -quit )"
		POSSIBLECUSTOMSPEKEXE="$( realpath "$POSSIBLECUSTOMSPEKEXE" )"

		if [ -f "$SPEK32SRC" ] && [ -f "$SPEK64SRC" ]; then
			notiShow "$NOTY_USESPEKCUSTOMDLL"
			writelog "INFO" "${FUNCNAME[0]} - Found '${SPEK}32.dll' and '${SPEK}64.dll' -- Using this as SpecialK version"
		elif [ -f "$POSSIBLECUSTOMSPEKEXE" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found possible SpecialK EXE '$POSSIBLECUSTOMSPEKEXE' -- Attempting to extract SpecialK DLLs from this executable"
			extractSpecialKEXE "$POSSIBLECUSTOMSPEKEXE"
		fi
	else  # download SpecialK
		if [ ! -f "$SPEK32SRC" ] && [ ! -f "$SPEK64SRC" ]; then
			notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$SPEK")"
			dlCheck "$SPEKDLURL" "$SPEKDL" "X" "Downloading '$SPEKDLURL' to '$SPEKDLDIR'"
			extSpek "$SPEKDL"
		elif [ "$AUTOSPEK" -eq 1 ] && { [ "$SPEKVERS" == "stable" ] || [ "$SPEKVERS" == "nightly" ] ;}; then
			writelog "INFO" "${FUNCNAME[0]} - AUTOSPEK is enabled and SPEKVERS is '$SPEKVERS' - so looking for $SPEK updates" "E"
			notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$SPEK")"
			dlCheck "$SPEKDLURL" "$SPEKDL" "X" "Downloading '$SPEKDLURL' to '$SPEKDLDIR'"
			extSpek "$SPEKDL"
		else
			writelog "INFO" "${FUNCNAME[0]} - Already have the SpecialK DLLs, nothing to update" "E"
		fi
	fi

	writelog "INFO" "${FUNCNAME[0]} - Cleaning up SpecialK version folder '$SPEKDLDIR/$SPEKVERS'"
	# Clean up everything that isn't SPEK32SRC and SPEK64SRC
	for SPEKVERDIRFILE in "$SPEKDLDIR/$SPEKVERS"/*; do
		SPEKVERDIRFILEBASENAME="$( basename "$SPEKVERDIRFILE" )"
		SPEKVERDIRFILEREALPATH="$( realpath "$SPEKVERDIRFILE" )"  # Just in case
		if [ "$SPEKVERDIRFILEBASENAME" != "$SPEK32BASE" ] && [ "$SPEKVERDIRFILEBASENAME" != "$SPEK64BASE" ]; then
			rmFileIfExists "$SPEKVERDIRFILEREALPATH"
			rmDirIfExists "$SPEKVERDIRFILEREALPATH"
		fi
	done

	# Check to make sure DLLs are still satisfied
	if [ -f "$SPEK32SRC" ]; then
		writelog "INFO" "${FUNCNAME[0]} - '$SPEK32SRC' is ready" "E"
	else
		writelog "SKIP" "${FUNCNAME[0]} - '$SPEK32SRC' is missing!" "E"
	fi

	if [ -f "$SPEK64SRC" ]; then
		writelog "INFO" "${FUNCNAME[0]} - '$SPEK64SRC' is ready" "E"
	else
		writelog "SKIP" "${FUNCNAME[0]} - '$SPEK64SRC' is missing!" "E"
	fi
}

## Get rendering API from PCGamingWiki compatibility list
## This is missing some supported games (such as NieR:Replicant and Monster Hunter World) but is generally a good reference point
##
## For compatibility reasons we use d3d11.dll as the SpecialK DLL name for Direct3D 11 games, as this seems to be more compatible than dxgi.dll on Linux -- Mainly when it comes to using ReShade and SpecialK
## ReShade and SpecialK work better together for Direct3D 11 games if we use d3d11.dll as the name
##
## Behaviour is this:
## - If we find Direct3D 11 as the rendering API for our game, use d3d11.dll as the SpecialK DLL name
## - If we find an unknown rendering API for our game, use dxgi.dll as a fallback
## - If we cannot find our game in the list, assume D3D11 and fall back to d3d11.dll
##    - TODO this point is not ideal, we should add an API override at some point
function getSpecialKGameRenderApi {
	SPEKCOMP="$SPEKDLDIR/${SPEK}_compat.html"
	MAXAGE=1440
	if [ ! -f "$SPEKCOMP" ] || test "$(find "$SPEKCOMP" -mmin +"$MAXAGE")"; then
		dlCheck "$SPEKCOMPURL" "$SPEKCOMP" "X" "Downloading '$SPEKCOMP'"
	fi

	if [[ -n "$SPEKDLLNAME" ]] && [[ "$SPEKDLLNAME" != "$AUTO" ]]; then
		# Use custom SpecialK DLL name if we're not using 'auto' OR if the DLL name field is blank
		writelog "INFO" "${FUNCNAME[0]} - User selected SpecialK DLL override name '$SPEKDLLNAME' - Will attempt to use this as the SpecialK DLL name"
		FOUNDSPEKDLLNAME="$SPEKDLLNAME"
	else
		writelog "INFO" "${FUNCNAME[0]} - Searching Render Api for '$GN' in '$SPEKCOMP'"
		RAPI="$(sed -n "/id=\"Compatibility_list\"/,$ p" "$SPEKCOMP" | grep -A1 "${GN// /\*.\*}" | tail -n1 | cut -d '>' -f2 | cut -d '<' -f1)"
		if [ -n "$RAPI" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found Render Api '$RAPI'"
			if [ "$RAPI" == "Direct3D 12" ]; then
				FOUNDSPEKDLLNAME="$DXGI"
			elif [ "$RAPI" == "Direct3D 11" ]; then
				FOUNDSPEKDLLNAME="$D3D11"
			elif [ "$RAPI" == "Direct3D 9" ]; then
				FOUNDSPEKDLLNAME="$D3D9"
			elif [ "$RAPI" == "OpenGL" ]; then
				FOUNDSPEKDLLNAME="$OGL32"
			else
				writelog "INFO" "${FUNCNAME[0]} - Unknown Render Api '$RAPI' - assuming 'Direct3D 11'"
				FOUNDSPEKDLLNAME="$DXGI"
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - Could not find Render Api - assuming 'Direct3D 11'"
			FOUNDSPEKDLLNAME="$DXGI"
		fi
	fi

	SPEKDST="$SPEKDDIR/$FOUNDSPEKDLLNAME"
	writelog "INFO" "${FUNCNAME[0]} - SpecialK DLL install path is '$SPEKDST'"
}

## Example to get key UsingWine under section [Compatibility.General] in dxgi.ini and change it from False toTrue
## writeValueToIni "Compatibility.General" "UsingWINE" "false" "true" "dxgi.ini"
function writeValueToIni {
	INISECTION="$1"  # i.e Compatibility.General
	INIKEY="$2"  # i.e. UsingWINE
	INIFROMVAL="$3"  # value to change from, i.e. false
	INITOVAL="$4"  # value to change to i.e. true
	INIFILE="$5"  # i.e. dxgi.ini

	ININEWSTR="${INIKEY}=${INITOVAL}"

	if [ -f "$INIFILE" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Found the ini file '$INIFILE' - setting '$ININEWSTR' under '$INISECTION'" "E"
		if grep -q "$INIKEY=$INIFROMVAL" "$INIFILE"; then
			writelog "INFO" "${FUNCNAME[0]} - Setting '$INIKEY' in the config to '$INITOVAL'" "E"
			sed "s:$INIKEY=$INIFROMVAL:$ININEWSTR:" -i "$INIFILE"
		else
			writelog "INFO" "${FUNCNAME[0]} - Adding a new entry '$INIKEY' in the ini, because it is missing" "E"
			if grep -q "$INISECTION" "$INIFILE"; then
				# Just key=val is missing
				sed "/\[$INISECTION\]/a $ININEWSTR" -i "$INIFILE"
			else
				# Heading and key=val missing
				writelog "INFO" "${FUNCNAME[0]} - Creating new section '$INISECTION' with '$ININEWSTR' in '$INIFILE'"

				{
					echo "[$INISECTION]"
					echo "$ININEWSTR"
					echo ""
				} >> "$SPEKINI"
			fi
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - Could not find INI file at '$INIFILE'"
	fi
}

function prepareSpecialKIni {
	SPEKDLLNAMEFORINI="$( basename "$SPEKDST" )"
	SPEKINI="${SPEKDLLNAMEFORINI//.dll/.ini}"  # Name INI after chosen SpecialK DLL name

	COGE="Compatibility.General"
	UWI="UsingWINE"

	ROSD="Render.OSD"
	SIVC="ShowInVideoCapture"

	if [ -n "$SPEKINI" ]; then
		if [ ! -f "$SPEKINI" ]; then
			writelog "INFO" "${FUNCNAME[0]} - SpecialK INI '$SPEKINI' does not exist, creating initial blank INI"
			touch "$SPEKINI"
		fi

		writeValueToIni "$COGE" "$UWI" "false" "true" "$SPEKINI"  # SpecialK may already detect and set this appropriately now
		writeValueToIni "$ROSD" "$SIVC" "true" "false" "$SPEKINI"  # Seems to be needed to prevent crashing sometimes
	fi
}

function useSpecialK {
	function installSpekDll {
		SPEKSRC="$1"
		SPEKDLLCONFLICTFOUND=0
		SHOULDINSTALLSPEK=1
		SPEKD3D47DLL="$4"
		SPEKD3D47DLLPATH="${SPEKDDIR}/${D3D47}"  # this will always be named /path/to/d3dcompiler_47, because we name the DLL differently on move, we don't need to keep the architecture in the DLL name
		SPEKDLLEXPORTNAME="$( basename "$SPEKDST" )"  # Make sure we use an actual name and not 'auto'

		if [ "$USERESHADE" -eq 1 ] && [ -f "$SPEKDDIR/$RSTXT" ] && [ "$USERESHSPEKPLUGIN" -eq 0 ]; then
			## ReShade is already installed and in use, and ReShade+SpecialK have selected DLL names conflict

			## Check each entered ReShade DLL name and see if any conflict with the entered SpecialK DLL name
			writelog "INFO" "${FUNCNAME[0]} - ReShade is installed and not loaded as SpecialK plugin -- Checking for DLL naming conflicts"
			mapfile -d "," -t -O "${#SPEKRSDLLNAMECHECKARR[@]}" SPEKRSDLLNAMECHECKARR < <(printf '%s' "$RESHADEDLLNAME")

			SPEKREALDLLNAME="$( basename "$SPEKDST" )"  # Makes sure we don't compare against 'auto'
			for SPEKRSCHECKDLL in "${SPEKRSDLLNAMECHECKARR[@]}"; do
				if [[ "$SPEKRSCHECKDLL" == "$SPEKREALDLLNAME" ]] && [ -f "$SPEKRSCHECKDLL" ]; then
					writelog "ERROR" "${FUNCNAME[0]} - ReShade is enabled and the chosen SpecialK DLL name conflicts with the ReShade DLL name -- Not installing SpecialK"
					notiShow "$( strFix "$NOTY_SPEKRESHDLLCONFLICT" "$RESH" "$SPEK" )"
					SPEKDLLCONFLICTFOUND=1
					break
				fi
			done

			if [ -f "$SPEKDST" ] && [ -f "$SPEKENA" ] && grep -qw "$SPEKDST" "$SPEKENA"; then
				if [ "$AUTOSPEK" -eq 1 ]; then
					writelog "INFO" "${FUNCNAME[0]} - Updating existing tracked SpecialK DLLs"
					removeSpekDlls  # Remove existing SpecialK DLLs so we can update them, if auto-update SpecialK is enabled
				else
					writelog "INFO" "${FUNCNAME[0]} - SpecialK is installed, tracked, and up-to-date -- Nothing to do"
					SHOULDINSTALLSPEK=0
				fi
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - SpecialK loading normally"
			# Check to see if SpecialK DLL already exists and is not tracked by us
			# There's probably scope to make installRSdll generic so it applies for both ReShade+SpecialK
			if [ ! -f "$SPEKDST" ]; then
				writelog "INFO" "${FUNCNAME[0]} - No SpecialK DLL installation found, installing as normal"
			elif [ -f "$SPEKDST" ] && ! grep -qw "$SPEKDST" "$SPEKENA"; then
				writelog "WARN" "${FUNCNAME[0]} - The chosen SpecialK DLL name already exists at '$SPEKDST' but is not tracked by us -- This may cause issues!"
				writelog "WARN" "${FUNCNAME[0]} - Attempting to back up existing found DLL with name '$SPEKDST' so that we can install SpecialK"

				SPEKDLLBAKNAM="$SPEKDST.bak"
				if [ -f "$SPEKDLLBAKNAM" ]; then
					writelog "ERROR" "${FUNCNAME[0]} - Backup DLL name already exists at '$SPEKDST/$SPEKDLLBAKNAM', cannot move this DLL to allow SpecialK to install -- Not installing SpecialK"
					notiShow "$NOTY_SPEKDLLCONFLICT" "X"
					SPEKDLLCONFLICTFOUND=1
				else
					writelog "INFO" "${FUNCNAME[0]} - Moving existing DLL '$SPEKDST' to '$SPEKDLLBAKNAM' so we can install SpecialK without conflicts"
					mv "$SPEKDST" "$SPEKDLLBAKNAM"
				fi
			elif [ -f "$SPEKDST" ] && [ -f "$SPEKENA" ] && grep -qw "$SPEKDST" "$SPEKENA"; then
				if [ "$AUTOSPEK" -eq 1 ]; then
					writelog "INFO" "${FUNCNAME[0]} - Updating existing tracked SpecialK DLLs"
					removeSpekDlls  # Remove existing SpecialK DLLs so we can update them, if auto-update SpecialK is enabled
				else
					writelog "INFO" "${FUNCNAME[0]} - SpecialK is installed, tracked, and up-to-date -- Nothing to do"
					SHOULDINSTALLSPEK=0
				fi
			fi
		fi

		# Make sure we don't track DLL renames
		rmFileIfExists "$SPEKENA"
		touch "$SPEKENA"

		if [ "$SPEKDLLCONFLICTFOUND" -eq 0 ] && [ "$SHOULDINSTALLSPEK" -eq 1 ]; then
			installd3d47dll "$SPEKD3D47DLL" "$SPEKDDIR"

			writelog "INFO" "${FUNCNAME[0]} - Installing '${SPEKSRC##*/}' as '$GP' is $2-bit" "E"
			notiShow "$NOTY_SPECIALKINSTALLING"
			cp "$SPEKSRC" "$SPEKDST"
			echo "$SPEKDST" >> "$SPEKENA"
			if [ -f "${SPEKSRC//dll/pdb}" ]; then
				SPEKPDB="${SPEKSRC##*/}"
				SPEKPDB="${SPEKPDB//dll/pdb}"
				writelog "INFO" "${FUNCNAME[0]} - Also installing debugging '$SPEKPDB'" "E"
				cp "${SPEKSRC//dll/pdb}" "$SPEKDDIR"
				echo "$SPEKDDIR/$SPEKPDB" >> "$SPEKENA"
			fi
			prepareSpecialKIni  # Moved here so this is created only once we confirm SpecialK can be installed
		elif [ "$SPEKDLLCONFLICTFOUND" -eq 0 ] && [ "$SHOULDINSTALLSPEK" -eq 0 ]; then
			# In this scenario, SpecialK is already installed so we don't need to install it, so write out the DLL name to the SPEKENA file to ensure we don't end up with a blank file
			# We can't always write out to the file unconditionally as SPEKDLLCONFLICTFOUND means SpecialK wasn't installed
			echo "$SPEKDST" >> "$SPEKENA"  # SpecialK DLL
			echo "$SPEKD3D47DLLPATH" >> "$SPEKENA"  # d3d47 DLL

			installd3d47dll "$SPEKD3D47DLL" "$SPEKDDIR"

			prepareSpecialKIni  # Needed here to update some values that may only exist after first launch
		elif [ "$SPEKDLLCONFLICTFOUND" -eq 1 ]; then
			writelog "ERROR" "${FUNCNAME[0]} - Could not install SpecialK -- DLL naming conflict was found"
		fi

		if [ "$SPEKDLLCONFLICTFOUND" -eq 0 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Setting WINEDLLOVERRIDES for ${SPEK}: dxgi=n,b;d3d9=n,b;${D3D47//.dll}=n,b;d3d11=n,b;opengl32=n,b;${SPEKDLLEXPORTNAME//.dll}=n,b"
			export WINEDLLOVERRIDES="$WINEDLLOVERRIDES;dxgi=n,b;d3d9=n,b;${D3D47//.dll}=n,b;d3d11=n,b;opengl32=n,b;${SPEKDLLEXPORTNAME//.dll}=n,b"
		fi
	}

	# Manage installing 32bit/64bit SpecialK DLL
	function installSpekArchDll {
		if [ "$USECUSTOMCMD" -eq 1 ] && [ -f "$CUSTOMCMD" ]; then
			ARCHEXE="$CUSTOMCMD"
		else
			ARCHEXE="$GP"
		fi

		if [ "$(getArch "$ARCHEXE")" == "32" ]; then
			installSpekDll "$SPEK32SRC" "32" "x86" "$D3D47_32"
		elif [ "$(getArch "$ARCHEXE")" == "64" ]; then
			installSpekDll "$SPEK64SRC" "64" "x64" "$D3D47_64"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Could not determine the architecture of '$GP' - not installing '$SPEK'" "E"
		fi
	}

	SPEKDDIR="$EFD"
	setFullGameExePath "SPEKDDIR"
	SPEKENA="$SPEKDDIR/${SPEK}_enabled.txt"

	# Ensure SpecialK DLL name ends with '.dll', even if we're not using SpecialK
	if [ -n "$SPEKDLLNAME" ] && ! [[ $SPEKDLLNAME == *.* ]]; then
		# Don't rename DLL if we're using "auto"
		if [[ ! "$SPEKDLLNAME" == "${AUTO}" ]]; then
			writelog "INFO" "${FUNCNAME[0]} - Renaming SPEKDLLNAME to '${SPEKDLLNAME}.dll'"
			SPEKDLLNAME="${SPEKDLLNAME}.dll"
		elif [[ "$SPEKDLLNAME" == "${AUTO}.dll" ]]; then
			SPEKDLLNAME="$AUTO"
		fi

		touch "$FUPDATE"
		updateConfigEntry "SPEKDLLNAME" "$SPEKDLLNAME" "$STLGAMECFG"
	fi

	if [ "$USESPECIALK" -eq 1 ]; then
		if [ "$AUTOSPEK" -eq 1 ] && { [ "$SPEKVERS" == "stable" ] || [ "$SPEKVERS" == "nightly" ];}; then
			writelog "INFO" "${FUNCNAME[0]} - Updating $SPEK in the gamedir because AUTOSPEK is enabled"
		fi

		writelog "INFO" "${FUNCNAME[0]} - ${SPEK} is enabled - Installing dlls if required"
		dlSpecialK

		getSpecialKGameRenderApi

		writelog "INFO" "${FUNCNAME[0]} - Using '$SPEKDST' as $SPEK destination dll"

		installSpekArchDll
	else
		if [ -f "$SPEKENA" ]; then
			writelog "INFO" "${FUNCNAME[0]} - ${SPEK} was enabled before, removing existing $SPEK dlls"
			removeSpekDlls
			if [ "$USERESHADE" -eq 1 ]; then
				removeReShadeSpecialKInstallation "1"
			fi
		fi
	fi
}

function removeSpekDlls {
	while read -r spekdll; do
		rm "$spekdll" 2>/dev/null
	done < "$SPEKENA"
	rm "$SPEKENA" 2>/dev/null
}

function getUsedVars {
	while read -r line; do
		if grep -q -v "^#" <<< "$line"; then
			awk -F '=' '{print $1}' <<< "$line"
		fi
	done <"$1"
}

function getScreenRes {
	function widthList {
		"$XRANDR" --verbose | grep "\*" -A2 | grep -oP 'width\K[^start]+'
	}

	function heightList {
		"$XRANDR" --verbose | grep "\*" -A2 | grep -oP 'height\K[^start]+'
	}

	function getRes {
		if grep -q "^[0-9]*$" <<< "$FOUNDW" && grep -q "^[0-9]*$" <<< "$FOUNDH"; then
			FOUNDRES="${FOUNDW}x${FOUNDH}"
			writelog "INFO" "${FUNCNAME[0]} - Detected screen resolution '$FOUNDRES'" "X"
			echo "$FOUNDRES"
		else
			writelog "INFO" "${FUNCNAME[0]} - Screen resolution for width '$FOUNDW' and height '$FOUNDH' is invalid" "X"
		fi
	}

	HCNT="$(wc -l <<< "$(widthList)")"
	if [ "$HCNT" -eq 1 ] || [ "$HCNT" -gt 2 ]; then
		FOUNDW="$(widthList | head -n1 | tr -dc '0-9')"
		FOUNDH="$(heightList | head -n1 | tr -dc '0-9')"
	else
		XCUT="$("$XRANDR" --listactivemonitors | grep -Eo "\+[1-9][[:digit:]]*\+" | grep -Eo "[[:digit:]]*")"
		MPOS="$("$XDO" getmouselocation --shell | head -n1 | cut -d '=' -f2)"
		if [ "$MPOS" -gt "$XCUT" ]; then
			FOUNDW="$(widthList | tail -n1 | tr -dc '0-9')"
			FOUNDH="$(heightList | tail -n1 | tr -dc '0-9')"
		else
			FOUNDW="$(widthList | head -n1 | tr -dc '0-9')"
			FOUNDH="$(heightList | head -n1 | tr -dc '0-9')"
		fi
	fi

	if [ "$1" == "w" ]; then
		if grep -q "^[0-9]*$" <<< "$FOUNDW"; then
			writelog "INFO" "${FUNCNAME[0]} - Found screen width '$FOUNDW'" "X"
			echo "$FOUNDW"
		else
			writelog "INFO" "${FUNCNAME[0]} - Screen width '$FOUNDW' is invalid" "X"
		fi
	elif [ "$1" == "h" ]; then
		if grep -q "^[0-9]*$" <<< "$FOUNDH"; then
			writelog "INFO" "${FUNCNAME[0]} - Found screen height '$FOUNDH'" "X"
			echo "$FOUNDH"
		else
			writelog "INFO" "${FUNCNAME[0]} - Screen height '$FOUNDH' is invalid" "X"
		fi
	else
		getRes
	fi
}

function listScreenRes {
	while read -r lres; do
		echo "${lres%*[[:blank:]]}" | cut -d ' ' -f1
	done <<< "$("$XRANDR" --verbose | grep "+VSync$")" | sort -nur
}

function setInitWinXY {
	DEFRESSHM="$STLSHM/defres.txt"
	if [ -f "$DEFRESSHM" ] ; then
		loadCfg "$DEFRESSHM" X
		writelog "INFO" "${FUNCNAME[0]} - Using '${WINX}x${WINY}' from config '$DEFRESSHM'"
	else
		if [ "$ONSTEAMDECK" -eq 1 ]; then
			WINX="1280"
			WINY="800"
		else
			SCRW="$(getScreenRes w)"
			SCRH="$(getScreenRes h)"
			WINX=$(( SCRW * 3 / 4))
			WINY=$(( SCRH * 3 / 4))
		fi

		{
		echo "WINX=\"$WINX\""
		echo "WINY=\"$WINY\""
		} >> "$DEFRESSHM"
		writelog "INFO" "${FUNCNAME[0]} - Using '${WINX}x${WINY}' as default resolution for all windows without a configured resolution"
	fi
}

function setNewRes {
	SCREENRES="$(getScreenRes r)"
	if [ "$GAMESCREENRES" != "$NON" ] && [ "$GAMESCREENRES" != "$SCREENRES" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Setting screen resolution to '$GAMESCREENRES' using '$XRANDR'" "X"
		"$XRANDR" -s "$GAMESCREENRES"
	fi
}

function setPrevRes {
	if [ "$GAMESCREENRES" != "$NON" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Returning to previous screen resolution via '$XRANDR -s 0'" "X"
		"$XRANDR" -s 0
	fi
}

function customUserScriptStart {
	if [ -n "$USERSTART" ] && [[ ! "$USERSTART" =~ ${DUMMYBIN}$ ]]; then
		if [ -x "$USERSTART" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Starting custom user startscript '$USERSTART'"
			if [ "$USEWINE" -eq 0 ]; then
				"$USERSTART" "1" "$AID" "$GP" "$GPFX" &
			else
				"$USERSTART" "1" "$AID" "$GP" "$GWFX" &
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Custom user startscript '$USERSTART' not found or not executable"
		fi
	fi
}

function customUserScriptStop {
	if [ -n "$USERSTOP" ] && [[ ! "$USERSTOP" =~ ${DUMMYBIN}$ ]]; then
		if [ -x "$USERSTOP" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Starting custom user stopscript '$USERSTOP'"
			if [ "$USEWINE" -eq 0 ]; then
				"$USERSTOP" "0" "$AID" "$GP" "$GPFX" &
			else
				"$USERSTOP" "0" "$AID" "$GP" "$GWFX" &
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Custom user stopscript '$USERSTOP' not found or not executable"
		fi
	fi
}

function editorSkipped {
	if [ -z "$MAXASK" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Maximal editor requester count MAXASK not defined - skipping"
	else
		if ! grep -q "^ASKCNT" "$STLGAMECFG"; then
			SETASKCNT=1
			updateConfigEntry "ASKCNT" "$SETASKCNT" "$STLGAMECFG"
		else
			SETASKCNT=$(($(grep "ASKCNT" "$STLGAMECFG" | cut -d '=' -f2 | sed 's/\"//g') +1))
			updateConfigEntry "ASKCNT" "$SETASKCNT" "$STLGAMECFG"
		fi

		ASKCNT="$SETASKCNT"

		if [ "$ASKCNT" -ge "$MAXASK" ]; then
			notiShow "$(strFix "$NOTY_CANCELREQ1" "$MAXASK" "$GN" "$AID")"
			writelog "INFO" "${FUNCNAME[0]} - 'ASKCNT $ASKCNT' reached 'MAXASK $MAXASK' - disabling requester and resetting counter"
			updateConfigEntry "WAITEDITOR" "0" "$STLGAMECFG"
			updateConfigEntry "ASKCNT" "0" "$STLGAMECFG"
		elif [ "$ASKCNT" -lt "$MAXASK" ]; then
			notiShow "$(strFix "$NOTY_CANCELREQ2" "$ASKCNT" $((MAXASK - ASKCNT)) "$GN" "$AID")"
		fi
	fi
}

function checkWaitRequester {
	if [ -f "$EWRF" ] ; then
		if grep -q "^WAITEDITOR=\"0\"" "$STLGAMECFG"; then
			writelog "INFO" "${FUNCNAME[0]} - Re-enabling Wait Requester in '$STLGAMECFG', because '$EWRF' was found"
			updateConfigEntry "WAITEDITOR" "2" "$STLGAMECFG"
		fi
		rm "$EWRF"
	fi

	if [ -f "$UWRF" ] ; then
		if [ -f "$SWRF" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Stop skipping Wait Requester, because '$UWRF' was found"
			rm "$SWRF"
		fi
		rm "$UWRF"
	fi
}

function askSettings {
	if ! grep -q "^WAITEDITOR=\"0\"" "$STLGAMECFG"; then
		if [ -f "$SWRF" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Skipping Wait-Requester because skip file was found under '$SWRF'"
		else
			# open editor requester
			if grep -q "^WAITEDITOR" "$STLGAMECFG"; then
				WEDGAME="$(grep "^WAITEDITOR" "$STLGAMECFG"| cut -d '=' -f2)"
				WAITEDITOR="${WEDGAME//\"/}"
				writelog "INFO" "${FUNCNAME[0]} - Using game specific requester timeout '$WAITEDITOR'"
			fi

			writeAllAIMeta "$AID" &

			if [ "$WAITEDITOR" -gt 0 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Opening Requester with timeout '$WAITEDITOR'"

				getAvailableCfgs
				fixShowGnAid
				export CURWIKI="$PPW/Wait-Requester"
				TITLE="${PROGNAME}-OpenSettings"
				pollWinRes "$TITLE"

				setShowPic

				if [ "$STARTMENU" == "Editor" ]; then
					REQQEST="$GUI_ASKOPENED"
					REQBUT="$BUT_EDITORMENU"
					LAUNCHMENU="EditorDialog"
				elif [ "$STARTMENU" == "Favorites" ]; then
					REQQEST="$GUI_ASKOPENFAV"
					REQBUT="$BUT_FAV"
					LAUNCHMENU="favoritesMenu"
				elif [ "$STARTMENU" == "Game" ]; then
					REQQEST="$GUI_ASKOPENGAM"
					REQBUT="$BUT_GM"
					LAUNCHMENU="openGameMenu"
				else
					REQQEST="$GUI_ASKOPENSET"
					REQBUT="$BUT_MAINMENU"
					LAUNCHMENU="MainMenu"
				fi

				ASKSETSTLVERS="<b>${PROGNAME} ${PROGVERS}</b>"

				LAPL="$(getLaPl)"

				PDBROUT=""
				prepareProtonDBRating
				if [ -f "$PDBRASINF" ];then
					PDBROUT="$(cat "$PDBRASINF")"
				fi

				# Tested and this should not break Non-Steam Games - Open an issue if it does!
				prepareSteamDeckCompatInfo
				if [ ! -f "$STLGDECKCOMPAT/${AID}-deckcompatrating.json" ] && [ "$DLSTEAMDECKCOMPATINFO" -eq 1 ]; then
					# If the Steam Deck compat rating json doesn't exist, assume something messed up e.g. offline or JQ is not installed
					writelog "INFO" "${FUNCNAME[0]} - Could not retrieve Steam Deck compatibility rating, defaulting to $STEAMDECKCOMPAT_UNKNOWN - Maybe '$JQ' is missing or we are offline?"
				fi

				if [ -n "$STEAMDECKCOMPATRATING" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Fetched Steam Deck compatibility info, will show on wait requester"
					STEAMDECKCOMPATOUT="<b>$GUI_SDCR:</b> ${STEAMDECKCOMPATRATING:$STEAMDECKCOMPAT_UNKNOWN}"
				fi

				writelog "INFO" "${FUNCNAME[0]} - Steam Deck compatibility rating string is '$STEAMDECKCOMPATOUT'"
				writelog "INFO" "${FUNCNAME[0]} - Preparing to show Wait Requester"

				"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
				--title="$TITLE" \
				--text="$(spanFont "$ASKSETSTLVERS" "H")\n\n$(spanFont "$SGNAID - $REQQEST" "H")" \
				--field="<i>$PDBROUT</i>":LBL \
				--field="<i>$STEAMDECKCOMPATOUT</i>":LBL \
				--field="<i>$LAPL</i>":LBL \
				--field="<i>(${#CfgFiles[@]} $GUI_EDITABLECFGS)</i>":LBL \
				--field="<i>($GUI_EDITABLEGAMECFGS)</i>":LBL \
				--button="$REQBUT":0 \
				--button="$BUT_SKIP":1 \
				--timeout="$WAITEDITOR" \
				--timeout-indicator=top \
				"$GEOM"

				WAITREQRESULT=$?

				writelog "INFO" "${FUNCNAME[0]} - Wait Requester result was '$WAITREQRESULT'"

				case $WAITREQRESULT in
					0)  {
						writelog "INFO" "${FUNCNAME[0]} - Selected $REQBUT - Starting $SETMENU"
						"$LAUNCHMENU" "$AID"
						}
					;;
					1)  writelog "INFO" "${FUNCNAME[0]} - Selected $BUT_SKIP - Starting game without opening the $SETMENU"
						editorSkipped ;;
					70) writelog "INFO" "${FUNCNAME[0]} - TIMEOUT - Starting game without opening the $SETMENU" ;;
					*) {
					   writelog "WARN" "${FUNCNAME[0]} - Wait Requester returned unknown exit code '${WAITREQRESULT}' - Defaulting to opening the $SETMENU"
					   "$LAUNCHMENU" "$AID"
					   }
					;;
				esac
			fi
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - Skipping Wait-Requester because WAITEDITOR is 0 in '$STLGAMECFG'"
	fi
}

# create project dir $1 - no idea what the former arg2 was good for :)
function mkProjDir {
	mkdir -p "$1" 2>/dev/null >/dev/null
}

# create project dirs
function createProjectDirs {
	mkProjDir "$STLCFGDIR"
	mkProjDir "$STLLANGDIR"
	mkProjDir "$LOGDIRID"
	mkProjDir "$LOGDIRTI"
	mkProjDir "$STLPROTONIDLOGDIR"
	mkProjDir "$STLPROTONTILOGDIR"
	mkProjDir "$STLDXVKLOGDIR"
	mkProjDir "$STLWINELOGDIR"
	mkProjDir "$STLGLLOGDIRID"
	mkProjDir "$STLGLLOGDIRTI"
	mkProjDir "$STLGAMEDIRID"
	mkProjDir "$STLGAMEDIRTI"
	mkProjDir "$STLCOLLECTIONDIR"
	mkProjDir "$TWEAKDIR"
	mkProjDir "$USERTWEAKDIR"
	mkProjDir "$TWEAKCMDDIR"
	mkProjDir "$SBSTWEAKDIR"
	mkProjDir "$STLDLDIR"
	mkProjDir "$STLSHADDIR"
	mkProjDir "$STLVORTEXDIR"
	mkProjDir "${STLVORTEXDIR}/downloads"
	mkProjDir "$RESHADESRCDIR"
	mkProjDir "$CUSTPROTDLDIR"
	mkProjDir "$CUSTPROTEXTDIR"
	mkProjDir "$WINEDLDIR"
	mkProjDir "$WINEEXTDIR"
	mkProjDir "$STLGAMES"
	mkProjDir "$STLGDESKD"
	mkProjDir "$STLIDFD"
	mkProjDir "$STLGHEADD"
	mkProjDir "$STLGICO"
	mkProjDir "$STLGZIP"
	mkProjDir "$STLGPNG"
	mkProjDir "$STLAPPINFOIDDIR"
	mkProjDir "$HIDEDIR"
	mkProjDir "$VORTEXCOMPDATA"
	mkProjDir "$GEMETA"
	mkProjDir "$MO2COMPDATA"
	mkProjDir "$STLVKD3DLOGDIR"
	mkProjDir "$GEMETA"
	mkProjDir "$CUMETA"
	mkProjDir "$TIGEMETA"
	mkProjDir "$TICUMETA"
	mkProjDir "$STLCUSTVARSDIR"
	mkProjDir "$STLGDECKCOMPAT"
}

# add missing config entries to configfile $1 using seperator $2:
function updateConfigFile {

	if [ -z "$1" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Expected configfile as argument 1"
	else
		CFGFILE="$1"
		SEP="$2"

		# disable logging temporarily when the program just started (cosmetics)
		if [ -n "$3" ]; then
			ORGLOGLEVEL="$LOGLEVEL"
			LOGLEVEL=0
		fi

		if grep "$STLCFGDIR" "$CFGFILE" >/dev/null ; then
			writelog "UPDATE" "${FUNCNAME[0]} - Replacing '$STLCFGDIR' with 'STLCFGDIR' in '$CFGFILE'"
			sed "s:$STLCFGDIR:STLCFGDIR:g" -i "$CFGFILE"
		fi

		if grep -q "config Version: $PROGVERS" "$CFGFILE"; then
			writelog "SKIP" "${FUNCNAME[0]} - Config file '$CFGFILE' already at version '$PROGVERS'"
		else
			OLDVERS="$(grep "config Version" "$CFGFILE" | awk -F ': ' '{print $2}')"

			if [ -n "$OLDVERS" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Updating '$CFGFILE' from '$OLDVERS' to '$PROGVERS'"
				sed "s/config Version: $OLDVERS/config Version: $PROGVERS/" -i "$CFGFILE"
			else
				writelog "INFO" "${FUNCNAME[0]} - Updating '$CFGFILE' to '$PROGVERS'"
				sed "1s/^/##########################\n/" -i "$CFGFILE"
				sed "1s/^/## config Version: $PROGVERS\n/" -i "$CFGFILE"
			fi

			UPFROMTMPL=0
			if [ "$CFGFILE" == "$STLGAMECFG" ] && [ -f "$STLDEFGAMECFG" ]; then
				UPFROMTMPL=1
			fi

			while read -r RAWLINE; do
				LCAT="$(cut -d '=' -f1 <<< "$RAWLINE")"
				LVAL="$(cut -d '=' -f2 <<< "$RAWLINE")"
				if ! grep "^${LCAT}=" "$CFGFILE" >/dev/null ; then
					writelog "UPDATE" "${FUNCNAME[0]} - Entry '$LCAT' is missing in '$CFGFILE' - adding it now!"
					if [ "$UPFROMTMPL" -eq 1 ]; then
						OUTVAL="$(grep "^${LCAT}=" "$STLDEFGAMECFG" | cut -d '=' -f2)"
						OUTVAL="${OUTVAL//\"}"
					else
						if grep -q "\\$" <<< "$LVAL"; then
							if grep -q "WINX$\|WINY\|POSX\|POSY$" <<< "$LVAL"; then
								VARNAM=${LVAL//\$/DEF}
							else
								VARNAM=${LVAL//\$/}
							fi
							OUTVAL=${!VARNAM}
						else
							OUTVAL="$LVAL"
						fi
					fi
					OUTVAL="${OUTVAL//$STLCFGDIR/STLCFGDIR}"
					ADDLINE="$LCAT=\"$OUTVAL\""
					writelog "UPDATE" "${FUNCNAME[0]} - Adding line '$ADDLINE'"
					echo "$ADDLINE" >> "$CFGFILE"
				fi
			done <<< "$(sed -n "/#START$SEP/,/#END$SEP/p;/#END$SEP/q" "$0" | awk -F 'echo ' '{print $2}' | grep -v "\"##" | awk '{$1=$1};1' | sed '/^[[:space:]]*$/d'| sed 's/\"//g' | sed 's/=\\/=/g' | sed 's/\\$//g')"
		fi

		# re-enable logging
		if [ -n "$3" ]; then
			LOGLEVEL="$ORGLOGLEVEL"
		fi
	fi
}

function linkGameCfg {
	if [ -z "$GN" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Skipping symlinking config - no valid game name found"
	else
		createSymLink "${FUNCNAME[0]}" "$STLGAMECFG" "${STLGAMEDIRTI}/${GN}.conf"
	fi
}

# create game configs:
function createGameCfg {

	if [ -f "$STLGAMECFG" ]; then
		# add missing config entries in the default global config:
		updateConfigFile "$STLGAMECFG" "saveCfgdefault_template"
	else
		updateConfigEntry "CUSTOMCMD" "$DUMMYBIN" "$STLDEFGAMECFG"
		getGameName "$AID"
		if [ -n "$GAMENAME" ] && [ "$GAMENAME" != "$NON" ]; then
		{
		echo "## config Version: $PROGVERS"
		echo "##########################"
		echo "#########"
		echo "#$PROGNAME $PROGVERS"
		echo "#########"
		getCfgHeader
		echo "## set the default config file for DXVK_CONFIG_FILE which is used when found - defaults to config found in $STLDXVKDIR"
		echo "STLDXVKCFG=\"$STLDXVKDIR/$AID.conf\""
		grep -v "config Version" "$STLDEFGAMECFG"
		} >> "$STLGAMECFG"
		else
			writelog "SKIP" "${FUNCNAME[0]} - No game name found for '$AID' - does the game exist?"
		fi
	fi
	linkGameCfg
}

# override game configs with a tweak config if available:
function checkTweakLaunch {
	if [ -z "$TWEAKCMD" ]; then
		TWEAKCMD=""
	fi

	if [ -f "$GLOBALTWEAKCFG" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using overrides found in '$GLOBALTWEAKCFG'"
		notiShow "$(strFix "$NOTY_GLOBALTWEAK" "$GLOBALTWEAKCFG")"
		loadCfg "$GLOBALTWEAKCFG"
	fi

	# then user config - (overriding the global one)
	if [ -f "$TWEAKCFG" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using overrides found in '$TWEAKCFG'"
		loadCfg "$TWEAKCFG"
	fi

	if [ -n "$TWEAKCMD" ]; then
		# tweak command defined
		if [ -f "$TWEAKCMD" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found TWEAKCMD '$TWEAKCMD'"
			RUNTWEAK="$TWEAKCMD"
		elif [ -f "$TWEAKCMDDIR/$TWEAKCMD" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found TWEAKCMD '$TWEAKCMD' in '$TWEAKCMDDIR'"
			RUNTWEAK="$TWEAKCMDDIR/$TWEAKCMD"
		elif [ -f "$GFD/$TWEAKCMD" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found TWEAKCMD '$TWEAKCMD' in '$GFD'"
			RUNTWEAK="$GFD/$TWEAKCMD"
		fi
		# tweak command found

		if [ -n "$RUNTWEAK" ]; then

			if grep -q "^TWEAKFILE" "$RUNTWEAK"; then
				# dependency for tweak command defined
				writelog "INFO" "${FUNCNAME[0]} - TWEAKFILE configured in $RUNTWEAK as dependency - checking if the file exists in gamedir - relative to the gameexe"
				TWEAKFILE="$(grep "^TWEAKFILE" "$RUNTWEAK" | awk -F 'TWEAKFILE=' '{print $2}')"
				if [ -f "$EFD/$TWEAKFILE" ]; then
					# dependency for tweak command found
					writelog "INFO" "${FUNCNAME[0]} - Found tweakcmd dependency in $EFD/$TWEAKFILE - starting the tweakcmd now"
					# start tweak command
					"$RUNTWEAK"
					writelog "INFO" "${FUNCNAME[0]} - $RUNTWEAK finished"
				else
					# dependency for tweak command not found
					writelog "SKIP" "${FUNCNAME[0]} - Configured TWEAKFILE $TWEAKFILE not found - skipping launch of the tweakcmd $TWEAKCMD"
				fi
			else
				# start tweak command
				writelog "INFO" "${FUNCNAME[0]} - No TWEAKFILE configured in $RUNTWEAK as dependency - starting the tweakcmd regularly now"
				"$RUNTWEAK"
				writelog "INFO" "${FUNCNAME[0]} - $RUNTWEAK finished"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Configured TWEAKCMD $TWEAKCMD not found - can't start it"
		fi
	fi
}

function genDefIcon {
	if [ ! -f "$STLICON" ]; then
		base64 -d <<< "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAZlBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcHByAgIAAAAD///8AAACfn5+AgIC/v78QEBDv7+9gYGBQUFCvr6/Pz88wMDAgICDf399wcHBAQECPj4/mYUh/AAAAEXRSTlMAYICf778gj3DfUEAwz48gEPfJjAsAAAKXSURBVFjDnZfpetsgFEQtW17jtGVAgNBi9/1fsiDz5dbBTKLMH286xwgBFzaSa7Pdbi+Hzc9y+NXikffmJ/wp4pM3xvQWOF9X8zugH1yIgqD0BDSreRucsUixxvloWMsP8wiMsQXpZdDrDMfIqwljp1K6CdaVhsv2VZaL3oBBqVvv1CPOT059NjR4nbf42xlGlckG4dO/qKF7yoh9asCkVM0gvLFWq0/xAK6xB26KGDKv1WCR7lLieowex80enWKGzC+A/SsKbeFdF+8BUIoaMh8TJlivuxjtLWyIXwFJQA3Cx+gROaN2SgTMsPCSOZiYMMe3WdDCEYNPfD0ObezEQFrAeRViJ57gOc/icdocgJnw80z4GTikudjXed6GHru0mLUwhCcGg/awzFRAV3li0MBFxnqVFwOfbZrwgGa8GEpeDJQXQ8mPoxgIL4ay/+RdjRdDwUPFQAwVXgzClwJowovhmYd3zkMMVV4MwuNmeizpzS0bCC+rvw5IWcb3HTH3ZdQuCZlnRVZHakm3cNnUAYsr8bsqnnuBtUAjpqF8TKUPpIcayseUT0H4L55iTjkOhOfjSAyFIPN8JEuKuSA8nUsSmY0Fz2ZzaSh4sp5UDMLzFa1mEL5muJB1V/j6qix1oYwWvlYXpDLR6kQq0wGg5W9OPKmNUp1JG2h13iOs4cv9QQvHec93KCv2SEXW7tK4gO8TieC7O9XX6bD/9l75ZW44frVb54YJ8Xz2DkN4ajDY5xPLc7t64alhAN4+zkwq3B+dOd9hhWeGweIopzZlgN4E0yN+4Kuu8Lv/z43KfNQSX1v3vZKED15OrtoYo52e6pVn0hkfemC3+ux8PQO2jxf5CWhPzz/+3p6/cXpv3vHIefsnf/UP3QHUzgWHsSYAAAAASUVORK5CYII=" > "$STLICON"
	fi

	if [ ! -f "$NOICON" ]; then
		base64 -d <<< "R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" > "$NOICON"
	fi
}

function createDefaultCfgs {
	writelog "INFO" "${FUNCNAME[0]} - START"

	createProjectDirs

	loadLanguage "$@"

	writelog "INFO" "${FUNCNAME[0]} - setSteamPaths:"
	setSteamPaths

	saveCfg "$STLDEFGLOBALCFG" X
	loadCfg "$STLDEFGLOBALCFG" X
	saveCfg "$STLURLCFG" X
	loadCfg "$STLURLCFG" X
	prepareGUI
	genDefIcon

	createProjectDirs
	getGameOS "$@"
	delEmptyFile "$PROTONCSV"

	if [ "$HAVEINPROTON" -eq 0 ]; then
		if [ "$ISGAME" -eq 2 ] || [ ! -f "$PROTONCSV" ]; then
			writelog "INFO" "${FUNCNAME[0]} - createProtonList:"
			createProtonList X
			writelog "INFO" "${FUNCNAME[0]} - createProtonList end"
		elif [ "$ISGAME" -ne 2 ] && [ -f "$PROTONCSV" ]; then
			mapfile -t -O "${#ProtonCSV[@]}" ProtonCSV < "$PROTONCSV"
		fi
	fi

	checkStartMode
	saveCfg "$STLDEFGAMECFG" X
	createProjectDirs
	setGlobalAIDCfgs
	listAllSettingsEntries
	checkEntryBlocklist
	updateMenuSortFile
	writelog "INFO" "${FUNCNAME[0]} - STOP"
}

# updates or creates option $1 with value $2 in configfile $3:
function updateConfigEntry {
	CFGCAT="$1"
	CFGVALUE="$2"
	CFGFILE="$3"

	if [ "$CFGCAT" == "CUSTOMCMD" ] && [ "$CFGFILE" == "$STLDEFGAMECFG" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Emptying '$CFGCAT' for '$STLDEFGAMECFG'"
		CFGVALUE="$DUMMYBIN"
	fi

	if [ -z "$3" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Expected 3 arguments - only got $*"
	else
		if [ ! -f "$CFGFILE" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Configfile '$CFGFILE' does not exist - skipping config update"
		else
			if [ -n "$CFGVALUE" ]; then
				if [ "$CFGVALUE" == "TRUE" ]; then
					CFGVALUE="1"
				elif [ "$CFGVALUE" == "FALSE" ]; then
					CFGVALUE="0"
				fi

				if [ "$CFGVALUE" == "DUMMY" ]; then
					CFGVALUE=""
				fi

				# Help prevent expanding incoming config values by escaping them (i.e. when using with sed)
				ESCAPED_CFGVALUE="$( printf "%s\n" "$CFGVALUE" | sed 's/\\/\\\\/g' )"

				# only save value if it changed
				# sed needs escaped string because otherwise it'll expand escape sequences in strings with backslashes
				# i.e. config values with Windows paths, '\home\test' will have '\t' expanded as a tab character
				# We have to use the regular one for echo though.
				if { [ "${!CFGCAT}" != "$CFGVALUE" ] && [ "${!CFGCAT}" != "${CFGVALUE//$STLCFGDIR/STLCFGDIR}" ];} || [ -f "$FUPDATE" ]; then
					CFGVALUE="${CFGVALUE//$STLCFGDIR/STLCFGDIR}"
					if [ "$(grep -c "#${CFGCAT}=" "$CFGFILE")" -eq 1 ]; then
						writelog "INFO" "${FUNCNAME[0]} - Option '$CFGCAT' commented out in config '${CFGFILE##*/}' - activating it with the new value '$CFGVALUE'"
						sed -i "/^#${CFGCAT}=/c$CFGCAT=\"$ESCAPED_CFGVALUE\"" "$CFGFILE"
					elif [ "$(grep -c "^${CFGCAT}=" "$CFGFILE")" -eq 0 ]; then
						writelog "INFO" "${FUNCNAME[0]} - '$CFGCAT' option missing in config '${CFGFILE##*/}' - adding a new line"
						echo "$CFGCAT=\"$CFGVALUE\"" >> "$CFGFILE"
					else
						writelog "INFO" "${FUNCNAME[0]} - Option '$CFGCAT' is updated with the new value '$CFGVALUE' in config '${CFGFILE##*/}'"
						sed -i "/^${CFGCAT}=/c$CFGCAT=\"$ESCAPED_CFGVALUE\"" "$CFGFILE"
					fi
					rm "$FUPDATE" 2>/dev/null
				fi
			fi
		fi
	fi

	CFGCAT=""
	CFGVALUE=""
	CFGFILE=""
}

# autoapply configuration settings based on the steam collections the game is in:
function autoCollectionSettings {
	if [ "$CHECKCOLLECTIONS" -eq 1 ] && [ "$STLPLAY" -eq 0 ]; then
		if [ -z "$SUSDA" ] || [ -z "$STUIDPATH" ]; then
			setSteamPaths
		fi
		if [ -d "$SUSDA" ]; then
			SC="$STUIDPATH/$SRSCV"

			if [ ! -f "$SC" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - File '${SC##*/}' not found in steam userid dir - skipping"
			else
				writelog "INFO" "${FUNCNAME[0]} - Searching collections for game '$AID' in '$SC'"
				while read -r SCAT; do
					GLOBALSCATCONF="$(find "$GLOBALCOLLECTIONDIR" -type f -iname "$SCAT.conf")"

					# Should we break after a file to load is found? Is there benefit to loading another file?
					if [ -f "$GLOBALSCATCONF" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Global Config '$GLOBALSCATCONF' found - loading its settings"
						loadCfg "$GLOBALSCATCONF"
					else
						writelog "SKIP" "${FUNCNAME[0]} - Global Config '$GLOBALSCATCONF' not found - skipping"
					fi

					SCATCONF="$(find "$STLCOLLECTIONDIR" -type f -iname "$SCAT.conf")"

					if [ -f "$SCATCONF" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Collections Directory Config '$SCATCONF' found - loading its settings"
						loadCfg "$SCATCONF"
					else
						writelog "SKIP" "${FUNCNAME[0]} - Collections Directory Config '$SCATCONF' not found - skipping"
					fi

					SCATCMD="$TWEAKCMDDIR/$SCAT.sh"
					if [ -f "$SCATCMD" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Steam-Collection user command '$SCATCMD' found - executing"
						if [ "$USEWINE" -eq 0 ]; then
							"$SCATCMD" "$AID" "$EFD" "$GPFX"
						else
							"$SCATCMD" "$AID" "$EFD" "$GWFX"
						fi
					fi
				done <<< "$(sed -n "/\"$AID\"/,/}/p;" "$SC" | sed -n "/\"tags\"/,/}/p" | sed -n "/{/,/}/p" | grep -v '{\|}' | cut -d '"' -f4)"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - '$SUSDA' not found - this should not happen! - skipping"
		fi
	fi
}

function stracerun {
	writelog "INFO" "${FUNCNAME[0]} - Starting stracerun"
	waitForGamePid
	writelog "INFO" "${FUNCNAME[0]} - $STRACE -p $(GAMEPID) $STRACEOPTS -o $STRACEDIR/$AID.log"
	mapfile -d " " -t -O "${#RUNSTRACEOPTS[@]}" RUNSTRACEOPTS < <(printf '%s' "$STRACEOPTS")
	"$STRACE" -p "$(GAMEPID)" "${RUNSTRACEOPTS[@]}" -o "$STRACEDIR/$AID.log"
}

function checkStraceLaunch {
	if [ -n "$STRACERUN" ]; then
		if [ "$STRACERUN" -eq 1 ]; then
			stracerun &
		fi
	fi
}

function netrun {
	writelog "INFO" "${FUNCNAME[0]} - Starting network traffic monitor"

	waitForGamePid

	if [ -n "$NETMONDIR" ]; then
		if [ ! -d "$NETMONDIR" ]; then
			writelog "INFO" "${FUNCNAME[0]} - $NETMON dest directory $NETMONDIR does not exist - trying to create it"
			mkProjDir "$NETMONDIR"
		fi

		if [ -d "$NETMONDIR" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Writing network traffic for $AID int dest directory $NETMONDIR"
			if [ -f "$NETMONDIR/$AID-$NETMON.log" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Removing old $NETMONDIR/$AID-$NETMON.log"
				rm "$NETMONDIR/$AID-$NETMON.log"
			fi
			mapfile -d " " -t -O "${#RUNNETOPTS[@]}" RUNNETOPTS < <(printf '%s' "$NETOPTS")
			"$NETMON" "${RUNNETOPTS[@]}" | grep "wineserver" | grep -v "localhost\|0.0.0.0" >> "$NETMONDIR/$AID-$NETMON.log"
		else
			writelog "SKIP" "${FUNCNAME[0]} - $NETMON dest directory $NETMONDIR still does not exist - skipping"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - $NETMON dest directory variable NETMONDIR is empty"
	fi
}

function checkNetMonLaunch {
	if [ "$USENETMON" -eq 1 ]; then
		if [ -n "$NETMON" ]; then
			netrun &
		fi
	fi
}

function checkXliveless {
	if [ -n "$NOGFWL" ]; then
		if [ "$NOGFWL" -eq 1 ]; then
			rm -rf "$GPFX/$DRC/$PFX86/Microsoft Games for Windows - LIVE"
			rm -rf "$GPFX/$DRC/Program Files/Common Files/Microsoft Shared/Windows Live"
			# option for USEWINE probably not really required
			WLID="WLIDSvcM.exe"
			if "$PGREP" "$WLID" >/dev/null; then
				writelog "INFO" "${FUNCNAME[0]} - GFWL starts '$WLID' directly after installation and it never exists - killing it now"
				"$PKILL" -9 "$WLID"
			fi

			XLIVEDLL="xlive.dll"
			XLDL="$STLDLDIR/xlive/"
			XLDST="$XLDL/$XLIVEDLL"
			mkProjDir "$XLDL"

			writelog "INFO" "${FUNCNAME[0]} - Game '$SGNAID' needs '$XLIVEDLL' - checking"
			if [ -f "$EFD/$XLIVEDLL" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - Found '$XLIVEDLL' in dir $EFD - nothing to do"
			else
				writelog "INFO" "${FUNCNAME[0]} - '$XLIVEDLL' not found in gamedir '$EFD'"
				if [ ! -f "$XLDST" ]; then
					dlCheck "$XLIVEURL" "$XLDST" "X" "'$XLDST' not found - downloading automatically from '$XLIVEURL'"
					"$UNZIP" "$XLDL/${DLURL##*/}" -d "$XLDL"
					if [ -f "$XLDL/dinput8.dll" ]; then
						mv "$XLDL/dinput8.dll" "$XLDST"
					fi
				fi
				if [ -f "$XLDST" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Found '$XLIVEDLL' in '$XLDL' - copying into gamedir '$EFD'"
					cp "$XLDST" "$EFD"
				fi
			fi
		fi
	fi
}

function setGameScopeVars {
	function getGameScopeArg {
		ARGS="$1"  # e.g. "$GAMESCOPE_ARGS"
		UNESCAPED_FLAG="$2"
		FLAG="${2//-/\\-}"  # e.g. "--hdr-enabled" becomes "\-\-hdr\-enabled"
		VAR="$3"  # e.g. "$GSHDR"
		TRUEVAL="$4"  # e.g. "1" (on UI)
		DEFVAL="$5"  # e.g. "0" (on UI)
		ARGTYPE="${6,,}"  # e.g. "chk", "cb", etc (matches Yad widget types mostly)

		# Set values for undefined arguments
		if [ -n "$VAR" ]; then
			return
		fi

		# If the flag is not in the args string, return the default value for display purposes
		if ! grep -qw "$FLAG" <<< "$ARGS"; then
			echo "$DEFVAL"
			return
		fi

		if [[ $ARGTYPE =~ "cb" ]] || [[ $ARGTYPE =~ "num" ]]; then
			# Get the value given to the argument as the enabled/selected value, e.g. get '2' from '-U 2' if we passed '-U'
			# If the value does not contain only numbers (with or without decimals) then this will be blank and we return the default value 'DEFVAL'
			GSPARSEDARGVAL="$( tr ' ' '\n' <<< "$ARGS" | grep -wA1 "$FLAG" | tail -n1 )"

			# Don't validate parsed value for combobox, this is free-text and could be anything
			if ! [[ $ARGTYPE =~ "num" ]]; then
				echo "$GSPARSEDARGVAL"
				return
			fi

			# If we get passed an invalid GameScope commandline argument where a flag that is supposed to be followed by a NUMBER is not,
			# we could end up returning the next argument, e.g. `-s -f` would return `-s` if we didn't include the `grep -P`
			# Using the `grep -P` we filter out potential garbage returned by the rest of the parsing.
			#
			# We are most likely to encounter problems without this `grep -P` when it comes to the `GSINTRES` and `GSSHWRES`,
			# as if these are blank we end up displaying 'x'.
			#
			# For comboboxes that can display text, we will just have to assume we are passed a valid string, as freetext comboboxes could contain
			# anything and we cannot/should not try to assume what is valid for them in this way.
			# This logic only exists to filter out non-numerical values for flags which expect to be given a numerical argument
			#
			# For more background, see: https://github.com/sonic2kk/steamtinkerlaunch/pull/1152#issuecomment-2316286429
			GSPARSEDARGNUMVAL="$( echo "${GSPARSEDARGVAL}" | grep -P "^([\d]+)(?:\.([\d]{1,2}?))?$" )"
			if [ -n "${GSPARSEDARGNUMVAL}" ]; then
				echo "$GSPARSEDARGNUMVAL"
			else
				echo "$DEFVAL"
			fi
		elif [[ $ARGTYPE =~ "path" ]] || [[ $ARGTYPE =~ "txt" ]]; then
			# Get value given to arguments with two dashes, like `--`
			echo "$ARGS" | sed 's:--:\n--:g' | grep -wA1 "$FLAG" | sed "s:${UNESCAPED_FLAG}::g;s:-:\n-:g" | head -n1 | xargs
		else
			echo "$TRUEVAL"
		fi
	}

	function getGameScopeGeneralOpts {
		# GameScope Show Resolution (corresponds to -W, -H options, uppercase) -- Actual GameScope window size (defaults to 1280x720) -- Dropdown
		# Although this is a combobox, we still use "num" as the `getGameScopeArg` type because we want numeric validation
		GSSHOWRESARGWIDTH="$( getGameScopeArg "$GAMESCOPE_ARGS" "-W" "$GSSHOWRESARGWIDTH" "" "1280" "num")"
		GSSHOWRESARGHEIGHT="$( getGameScopeArg "$GAMESCOPE_ARGS" "-H" "$GSSHOWRESARGHEIGHT" "" "720" "num")"

		GSSHWRES="${GSSHOWRESARGWIDTH}x${GSSHOWRESARGHEIGHT}"

		# GameScope Internal Resolution (corresponds to -w, -h options, lowercase) -- Resolution that games see (defaults to 1280x720) -- Dropdown
		# Although this is a combobox, we still use "num" as the `getGameScopeArg` type because we want numeric validation
		GSINTRESARGWIDTH="$( getGameScopeArg "$GAMESCOPE_ARGS" "-w" "$GSINTRESARGWIDTH" "" "1280" "num")"
		GSINTRESARGHEIGHT="$( getGameScopeArg "$GAMESCOPE_ARGS" "-h" "$GSINTRESARGHEIGHT" "" "720" "num")"

		GSINTRES="${GSINTRESARGWIDTH}x${GSINTRESARGHEIGHT}"

		# Default internal resolution to $NON ('none') if blank -- Ensures we don't pass invalid resolution to GameScope
		if [ -z "$GSINTRES" ]; then  GSINTRES="$NON"; fi

		# Default show resolution to $NON ('none') if blank -- Ensures we don't pass invalid resolution to GameScope
		if [ -z "$GSSHWRES" ]; then  GSSHWRES="$NON"; fi

		# Focused Frame Rate Limit -- Dropdown
		GSFLR="$( getGameScopeArg "$GAMESCOPE_ARGS" "-r" "$GSFLR" "" "$UL" "cb")"

		# Unfocused Frame Rate Limit -- Dropdown
		GSFLU="$( getGameScopeArg "$GAMESCOPE_ARGS" "-o" "$GSFLU" "" "$UL" "cb")"

		# Fullscreen (-f) -- Checkbox
		GSFS="$( getGameScopeArg "$GAMESCOPE_ARGS" "-f" "$GSFS" "1" "0" )"

		# Borderless Window (-b) -- Checkbox
		GSBW="$( getGameScopeArg "$GAMESCOPE_ARGS" "-b" "$GSBW" "1" "0" )"

		# Steam Integration (-e) -- Can fix some strange bugs with some peripherals -- Checkbox
		GSSE="$( getGameScopeArg "$GAMESCOPE_ARGS" "-e" "$GSSE" "1" "0" )"

		# Force windows to be fullscreen inside the nested dislay (--force-windows-fullscreen) -- Checkbox
		GSFWF="$( getGameScopeArg "$GAMESCOPE_ARGS" "--force-windows-fullscreen" "$GSFWF" "1" "0" )"

		# Force grab cursor -- Keeps cursor inside GameScope nested display -- Checkbox
		GSFGC="$( getGameScopeArg "$GAMESCOPE_ARGS" "--force-grab-cursor" "$GSFGC" "1" "0" )"

		# Force grab keyboard -- Keeps keyboard input locked to GameScope nested display -- Checkbox
		GSFGK="$( getGameScopeArg "$GAMESCOPE_ARGS" "-g" "$GSFGK" "1" "0" )"

		# Orientation (--force-orientation) -- Can be either 'left', 'right', 'normal', 'upsidedown' (Defaults to 'normal') -- Dropdown
		# NOTE: Passing this normally to GameScope (e.g. `gamescope --force-orientation upsidedown -- supertuxkart`) doesn't seem to do anything?
		GSFOOR="$( getGameScopeArg "$GAMESCOPE_ARGS" "--force-orientation" "$GSFOOR" "" "$GSNORM" "cb")"

		# Custom cursor image (--cursor) -- File picker
		GSENABLECUSTCUR="0"
		GSCURSOR="$( getGameScopeArg "$GAMESCOPE_ARGS" "--cursor" "$GSCURSOR" "" "" "path" )"
		if [ -n "$GSCURSOR" ]; then  GSENABLECUSTCUR="1"; fi

		# Mouse Sensitivity (-s) -- Spinner
		GSMOUSESENSITIVITY="$( getGameScopeArg "$GAMESCOPE_ARGS" "-s" "$GSMOUSESENSITIVITY" "" "1.0" "num" )"

		# There is a `--cursor-scale-height` option, but at time of writing (15/02/23) this doesn't seem to do anything - We can add it in future, but not now
		# There is a `--framerate-limit` option, but I am not totally sure how it differs from -r/-o -- I tested it out and the behaviour seemed identical? Maybe one works in both nested/embedded and the other doesn't, not sure...
	}

	function getGameScopeFilteringOpts {
		# Equivalent to check for --fsr-sharpness or --sharpness, so should work fine
		if grep -q "\-\-filter" <<< "$("$(command -v "$GAMESCOPE")" --help 2>&1)"; then
			GSNEWFILTERMODE=1  # Even though it's called NEWFILTERMODE, it also applies to scaling - Naming is hard
		fi

		# Default scale/filter to none to be safe
		if [ "$GSNEWFILTERMODE" -eq 1 ]; then
			## !!These are the newer flags for checking filter/scale!!
			#
			# Filtering option (-F) -- Combobox (replaces old individual Nearest/FSR/NIS options)
			GSFLTR="$( getGameScopeArg "$GAMESCOPE_ARGS" "-F" "$GSFLTR" "" "$NON" "cb" )"

			# Scaling option (-S) -- Combobox (replaces old individual Integer Scaling option)
			GSSCALE="$( getGameScopeArg "$GAMESCOPE_ARGS" "-S" "$GSSCALE" "" "$NON" "cb" )"
		else
			## !!These are legacy flags with individual switches!!
			## These values are checked for and used to apply the correct filter selection
			## on the UI (i.e. passing -U will select FSR for the filter dropdown)
			##
			## Scale
			# Integer scaling (-i)
			GSIS="$( getGameScopeArg "$GAMESCOPE_ARGS" "-i" "$GSIS" "1" "" )"
			if [ -n "$GSIS" ]; then
				GSSCALE="integer"  # This is hardcoded but /shrug, this is probably not going to matter when parsing a legacy GameScope switch
			fi

			## Filtering (no default values so that if these aren't in the gamescope args, they are -z)
			# Nearest Neighbor (-n)
			GSNN="$( getGameScopeArg "$GAMESCOPE_ARGS" "-n" "$GSNN" "nearest" "" )"

			# FidelityFX 1.0 enabled (-U)
			GSFSR="$( getGameScopeArg "$GAMESCOPE_ARGS" "-U" "$GSFSR" "fsr" "" )"

			# NVIDIA Image Scaling v1.0.3 (-Y) -- Checkbox
			GSNIS="$( getGameScopeArg "$GAMESCOPE_ARGS" "-Y" "$GSNIS" "nis" "" )"


			# This copies the way GameScope prioritises the check (see parse_upscaler_filter in GameScope main.cpp)
			# This maps the old-style filter switches (-U, -Y, -n) to the GameScope dropdowns
			# Eventually this will no longer be needed, once a suitable length of time has passed, but for now it exists to serve as backwards compatibility for users between GameScope versions.
			# Once `-F`/`-S` are standard, we can remove this code
			if [ -n "$GSNN" ]; then
				GSFLTR="nearest"
			elif [ -n "$GSFSR" ]; then
				GSFLTR="fsr"
			elif [ -n "$GSNIS" ]; then
				GSFLTR="nis"
			fi
		fi

		# AMD FidelityFX 1.0 / NVIDIA Image Sharpening upscaler value -- Spinner
		GSFSRS="$( getGameScopeArg "$GAMESCOPE_ARGS" "--fsr-sharpness" "$GSFSRS" "" "2" "num" )"

		# Max Scale Factor -- No idea how this option actually works, the documentation is a bit sparce but Steam Deck seems to default it to 2 (https://github.com/Plagman/gamescope/issues/588#issue-1338038588)
		GSMSF="$( getGameScopeArg "$GAMESCOPE_ARGS" "-m" "$GSMSF" "" "0" "num" )"

		# ReShade Effect File Path (--reshade-effect) -- File picker
		GSRSEP="$( getGameScopeArg "$GAMESCOPE_ARGS" "--reshade-effect" "$GSRSEP" "" "" "path" )"

		# ReShade Technique IDX (index?) (--reshade-technique-idx) -- Spinner
		GSRSTI="$( getGameScopeArg "$GAMESCOPE_ARGS" "--reshade-technique-idx" "$GSRSTI" "" "0" "num" )"
	}

	function getGameScopeHDROpts {
		# HDR (--hdr-enabled) -- Checkbox
		GSHDR="$( getGameScopeArg "$GAMESCOPE_ARGS" "--hdr-enabled" "$GSHDR" "1" "0" )"

		# HDR Wide Gammut for SDR (--hdr-wide-gammut-for-sdr) - Checkbox
		GSHDRWGFS="$( getGameScopeArg "$GAMESCOPE_ARGS" "--hdr-wide-gammut-for-sdr" "$GSHDRWGFS" "1" "0" )"

		# HDR SDR Content Nits (--hdr-sdr-content-nits) -- Defaults to 400 -- Numberbox
		GSHDRSCNITS="$( getGameScopeArg "$GAMESCOPE_ARGS" "--hdr-sdr-content-nits" "$GSHDRSCNITS" "1" "0" )"

		# HDR Inverse Tone Mapping Enabled (--hdr-itm-enable) -- Checkbox
		GSHDRITM="$( getGameScopeArg "$GAMESCOPE_ARGS" "--hdr-itm-enable" "$GSHDRITM" "1" "0" )"

		# HDR Inverse Tone Mapping SDR NITs (--hdr-itm-sdr-nits) -- Spinner
		# Default: 100 nits
		# Max: 1000 nits
		#
		# We default to 0 though in case a user doesn't want to use it, so we won't pass when this is 0
		GSHDRITMSDRNITS="$( getGameScopeArg "$GAMESCOPE_ARGS" "--hdr-itm-sdr-nits" "$GSHDRITMSDRNITS" "" "0" "num" )"

		# HDR Inverse Tone Mapping Target NITs (--hdr-itm-target-nits) -- Spinner
		# Default: 1000 nits
		# Max: 10000 nits
		#
		# Like `GSHDRITMSDRNITS`, we default to 0 because we don't want to always pass a value
		GSHDRITMTGTNITS="$( getGameScopeArg "$GAMESCOPE_ARGS" "--hdr-itm-target-nits" "$GSHDRITMTGTNITS" "" "0" "num" )"

		# There is a --sdr-gamut-wideness option which takes a (float?) value between 0 and 1. Not sure how this is used or what the default is,
		# so it is not added for now, but it could be added in future if requested/once more is known about it
	}

	function getGameScopeVROpts {
		# There are some other GameScope VR options:
		# * --vr-overlay-physical-width
		# * --vr-overlay-physical-curvature
		# * --vr-overlay-physical-pre-curve-pitch
		#
		# These options are probably very bespoke and not important to the average user, especially the physcial width option which takes a value in metres!
		# Usage on these options is also a bit unclear, probably documented in the GameScope source but not sure -We could add these in future if it is requested

		# Enable OpenVR (--openvr) -- Checkbox
		GSVR="$( getGameScopeArg "$GAMESCOPE_ARGS" "--openvr" "$GSVR" "1" "0" )"

		# SteamVR Explicit Name (--vr-overlay-explicit-name) -- Textbox
		GSVREXNA="$( getGameScopeArg "$GAMESCOPE_ARGS" "--vr-overlay-explicit-name" "$GSVREXNA" "" "" "txt" )"

		# SteamVR Default Name when no window title available (--vr-overlay-default-name) -- Textbox
		GSVRDEFNAM="$( getGameScopeArg "$GAMESCOPE_ARGS" "--vr-overlay-default-name" "$GSVRDEFNAM" "" "" "txt" )"

		# SteamVR Overlay Key String (--vr-overlay-key) -- Textbox
		GSVROVERLAYKEY="$( getGameScopeArg "$GAMESCOPE_ARGS" "--vr-overlay-key" "$GSVROVERLAYKEY" "" "" "txt" )"

		# SteamVR Overlay Icon (--vr-overlay-icon) -- Similar to cursor picker -- File picker
		GSVRICONENABLE="0"
		GSVRICON="$( getGameScopeArg "$GAMESCOPE_ARGS" "--vr-overlay-icon" "$GSVRICON" "" "" "path" )"
		if [ -n "$GSVRICON" ]; then  GSVRICONENABLE="1"; fi

		# Focus VR overlay immediately (--vr-overlay-show-immediately) -- Checkbox
		GSVRSHOIMM="$( getGameScopeArg "$GAMESCOPE_ARGS" "--vr-overlay-show-immediately" "$GSVRSHOIMM" "1" "0" )"

		# Enable SteamVR Control Bar (--vr-overlay-enable-control-bar) -- Checkbox
		GSVRCONTROLBAR="$( getGameScopeArg "$GAMESCOPE_ARGS" "--vr-overlay-enable-control-bar" "$GSVRCONTROLBAR" "1" "0" )"

		# Enable SteamVR Keyboard Button on Control Bar (--vr-overlay-enable-control-bar-keyboard) -- Checkbox
		GSVRCONTROLBARKEYBOARD="$( getGameScopeArg "$GAMESCOPE_ARGS" "--vr-overlay-enable-control-bar-keyboard" "$GSVRCONTROLBARKEYBOARD" "1" "0" )"

		# Enable SteamVR Close Button on Control Bar (--vr-overlay-enable-control-bar-close) -- Checkbox
		GSVRCONTROLBARCLOSE="$( getGameScopeArg "$GAMESCOPE_ARGS" "--vr-overlay-enable-control-bar-close" "$GSVRCONTROLBARCLOSE" "1" "0" )"

		# VR Trackpad Scroll Speed (--vr-scrolls-speed) -- Spinner
		GSVRSCROLLSSPEED="$( getGameScopeArg "$GAMESCOPE_ARGS" "--vr-scrolls-speed" "$GSVRSCROLLSSPEED" "" "8.0" "num" )"

		# Show SteamVR Overlay as Modal (--vr-overlay-modal) -- Checkbox
		GSVRMODAL="$( getGameScopeArg "$GAMESCOPE_ARGS" "--vr-overlay-modal" "$GSVRMODAL" "1" "0" )"
	}

	function getGameScopeEmbeddedOpts {
		# Default action on touch (--default-touch-mode) -- Dropdown
		# --------------------
		# 0: hover
		# 1: leftclick
		# 2: rightclick
		# 3: middleclick
		# 4: passthrough
		#
		# More information in the GameScope commit that added it: https://github.com/Plagman/gamescope/commit/39c9e93e0c0539d4c767e1be1c96e0d38778af12
		GSDEFTOUCHMODE="$( getGameScopeArg "$GAMESCOPE_ARGS" "--default-touch-mode" "$GSDEFTOUCHMODE" "" "${GSDEF}" "cb" )"
		case $GSDEFTOUCHMODE in
			0) GSDEFTOUCHMODE="${GSHOVER}" ;;
			1) GSDEFTOUCHMODE="${GSLEFTCLICK}" ;;
			2) GSDEFTOUCHMODE="${GSRIGHTCLICK}" ;;
			3) GSDEFTOUCHMODE="${GSMIDDLECLICK}" ;;
			4) GSDEFTOUCHMODE="${GSPASSTHRU}" ;;
		esac

		# Enable Immediate Flips (--immediate-flips) -- Probably equivalent to the Steam Deck's "allow tearing" option -- Checkbox
		GSIMMEDIATEFLIPS="$( getGameScopeArg "$GAMESCOPE_ARGS" "--immediate-flips" "$GSIMMEDIATEFLIPS" "1" "0" )"

		# Enable Adaptive Sync / VRR (--adaptive-sync) -- Checkbox
		GSADAPTIVESYNC="$( getGameScopeArg "$GAMESCOPE_ARGS" "--adaptive-sync" "$GSADAPTIVESYNC" "1" "0" )"

		# Preferred GameScope Output in order of preference -- Not sure how exactly this should be passed and it can take N number of outputs - For this reason we just give the user a textbox and let them enter their displays manually
		GSPREFOUT="$( getGameScopeArg "$GAMESCOPE_ARGS" "-O" "$GSPREFOUT" "" "" "txt" )"

		# GameScope DRM Mode Generation algorithm to use -- GameScope takes either "cvt" or "fixed", but we will have a "default" option in the dropdown which means we will ignore the flag altogether
		GSDRMMODE="$( getGameScopeArg "$GAMESCOPE_ARGS" "--generate-drm-mode" "$GSDRMMODE" "" "$GSDEF" "cb")"
	}

	function getGameScopeAdvancedOpts {
		# Path to write GameScope statistics to (--stats-path) -- File picker
		# '-T' option is also availsble, but we have to use '--stats-path' because of how 'getGameScopeArg' works
		GSSTATSPATHENABLE="0"
		GSSTATSPATH="$( getGameScopeArg "$GAMESCOPE_ARGS" "--stats-path" "$GSSTATSPATH" "" "" "path" )"
		if [ -n "$GSSTATSPATH" ]; then  GSSTATSPATHENABLE="1"; fi

		# Backend for GameScope to use (--backend) -- Dropdown
		GSBACKEND="$( getGameScopeArg "$GAMESCOPE_ARGS" "--backend" "$GSBACKEND" "" "auto" "cb" )"

		# Amount of time in milliseconds to wait before hiding the cursor (-C) -- Spinner
		GSHIDECURSORDELAY="$( getGameScopeArg "$GAMESCOPE_ARGS" "-C" "$GSHIDECURSORDELAY" "" "0" "num" )"

		# Disables direct scan-out (--force-composition) -- Checkbox
		GSFORCECOMP="$( getGameScopeArg "$GAMESCOPE_ARGS" "--force-composition" "$GSFORCECOMP" "1" "0" )"

		# Draw debug hud (--debug-hud) -- Checkbox
		GSDEBUGHUD="$( getGameScopeArg "$GAMESCOPE_ARGS" "--debug-hud" "$GSDEBUGHUD" "1" "0" )"

		# Force HDR support flag (--hdr-debug-force-support) -- Checkbox
		GSFORCEHDRSUPPORT="$( getGameScopeArg "$GAMESCOPE_ARGS" "--hdr-debug-force-support" "$GSFORCEHDRSUPPORT" "1" "0" )"

		# Force HDR output on display (--hdr-debug-force-output) -- Will look terrible if unsupported -- Checkbox
		GSFORCEHDROUTPUT="$( getGameScopeArg "$GAMESCOPE_ARGS" "--hdr-debug-force-output" "$GSFORCEHDROUTPUT" "1" "0" )"

		# Prefer Vulkan device for compositing (--prefer-vk-device) -- Checkbox
		GSPREFERVKDEVICE="$( getGameScopeArg "$GAMESCOPE_ARGS" "--prefer-vk-device" "$GSPREFERVKDEVICE" "1" "0" )"

		# Expose Wayland (--expose-wayland) -- Checkbox
		GSWAYLAND="$( getGameScopeArg "$GAMESCOPE_ARGS" "--expose-wayland" "$GSWAYLAND" "1" "0" )"

		# Realtime Scheduling (--rt) -- Checkbox
		GSRT="$( getGameScopeArg "$GAMESCOPE_ARGS" "--rt" "$GSRT" "1" "0" )"

		# Headless (--headless) -- Checkbox
		GSHDLS="$( getGameScopeArg "$GAMESCOPE_ARGS" "--headless" "$GSHDLS" "1" "0" )"
	}

	# Set storage vars
	UL="unlimited"
	FSRS_STR="\-\-fsr\-sharpness"
	GSNORM="normal"
	GSFOOROPTS="left!right!${GSNORM}!upsidedown"
	GSFLTROPTS="$NON!linear!nearest!fsr!nis!pixel"
	GSSCALEOPTS="$NON!auto!integer!fit!fill!stretch"
	GSDEF="default"
	GSHOVER="hover:0"
	GSLEFTCLICK="leftclick:1"
	GSRIGHTCLICK="rightclick:2"
	GSMIDDLECLICK="middleclick:3"
	GSPASSTHRU="passthrough:4"
	GSTOUCHMODES="${GSDEF}!${GSHOVER}!${GSLEFTCLICK}!${GSRIGHTCLICK}!${GSMIDDLECLICK}!${GSPASSTHRU}" # Corresponds to 0,1,2,3,4 respectively internally by GameScope -- Default is ingored and the flag is not passed to GameScope
	GSDRMMODES="${GSDEF}!cvt!fixed"
	GSNEWFILTERMODE=0  # Whether gamescope uses -U/-Y/-n/-i (legacy) or -F/-S (new)
	GSBACKENDOPTS="auto!sdl!wayland!drm!headless!openvr"

	# Get values for UI elements based on existing GameScope args
	getGameScopeGeneralOpts
	getGameScopeFilteringOpts
	getGameScopeHDROpts
	getGameScopeVROpts
	getGameScopeEmbeddedOpts
	getGameScopeAdvancedOpts
}

function GameScopeGui {
	if [ -n "$1" ]; then
		AID="$1"
		setAIDCfgs
	fi

	if [ -n "$2" ]; then
		GN="$2"
		fixShowGnAid
	fi

	# Setup Yad UI stuff
	loadCfg "$STLGAMECFG"
	export CURWIKI="$PPW/GameScope"
	TITLE="${PROGNAME}-${FUNCNAME[0]}"
	pollWinRes "$TITLE"
	setShowPic
	setGameScopeVars  # Get values for UI elements below

	# GameScope Yad options form
	GASCOS="$("$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --scroll --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
			--title="$TITLE" --separator="|" \
			--text="$(spanFont "$(strFix "$GUI_GASCOSET" "$SGNAID")" "H")" \
			--field="$(spanFont "$GUI_GSGENERALSET" "H")":LBL "SKIP" \
			--field="$GUI_GSINTRES!$DESC_GSINTRES ('GSINTRES')":CBE "$(cleanDropDown "${GSINTRES//\"}" "$(printf "%s\n" "$("$XRANDR" --current | grep "[0-9]x" | awk '{print $1}' | grep "^[0-9]" | tr '\n' '!')")")" \
			--field="$GUI_GSSHWRES!$DESC_GSSHWRES ('GSSHWRES')":CBE "$(cleanDropDown "${GSSHWRES//\"}" "$(printf "%s\n" "$("$XRANDR" --current | grep "[0-9]x" | awk '{print $1}' | grep "^[0-9]" | tr '\n' '!')")")" \
			--field="$GUI_GSFLR!$DESC_GSFLR ('GSFLR')":CBE "$(cleanDropDown "${GSFLR//\"}" "30!60!90!120!$UL")" \
			--field="$GUI_GSFLU!$DESC_GSFLU ('GSFLU')":CBE "$(cleanDropDown "${GSFLU//\"}" "30!60!90!120!$UL")" \
			--field="$GUI_USEGAMESCOPE!$DESC_USEGAMESCOPE ('USEGAMESCOPE')":CHK "${USEGAMESCOPE/#-/ -}" \
			--field="$GUI_GSFS!$DESC_GSFS ('GSFS')":CHK "$GSFS" \
			--field="$GUI_GSBW!$DESC_GSBW ('GSBW')":CHK "$GSBW" \
			--field="$GUI_GSSE!$DESC_GSSE ('GSSE')":CHK "$GSSE" \
			--field="$GUI_GSFWF!$DESC_GSFWF ('GSFWF')":CHK "$GSFWF" \
			--field="$GUI_GSFGC!$DESC_GSFGC ('GSFGC')":CHK "$GSFGC" \
			--field="$GUI_GSFGK!$DESC_GSFGK ('GSFGK')":CHK "$GSFGK" \
			--field="$GUI_GSFOOR!$DESC_GSFOOR ('GSFOOR')":CB "$(cleanDropDown "${GSFOOR}" "${GSFOOROPTS}")" \
			--field="$GUI_GSENABLECUSTCUR!$GUI_GSENABLECUSTCUR ('GSENABLECUSTCUR')":CHK "$GSENABLECUSTCUR" \
			--field="$GUI_GSCURSOR!$DESC_GSCURSOR ('GSCURSOR')":FL "${GSCURSOR/#-/ -}" \
			--field="$GUI_GSMOUSESENSITIVITY!$DESC_GSMOUSESENSITIVITY ('GSMOUSESENSITIVITY')":NUM "${GSMOUSESENSITIVITY/#-/ -}" \
			--field="$(spanFont "$GUI_GSFILTERINGSET" "H")":LBL "SKIP" \
			--field="$GUI_GSFLTR!$DESC_GSFLTR ('GSFLTR')":CBE "$(cleanDropDown "${GSFLTR}" "${GSFLTROPTS}")" \
			--field="$GUI_GSSCALE!$DESC_GSSCALE ('GSSCALE')":CBE "$(cleanDropDown "${GSSCALE}" "${GSSCALEOPTS}")" \
			--field="$GUI_GSFSRS!$DESC_GSFSRS ('GSFSRS')":NUM "${GSFSRS/#-/ -}:!0..20" \
			--field="$GUI_GSMSF!$DESC_GSMSF ('GSMSF')":NUM "$GSMSF" \
			--field="$GUI_GSRSEP!$DESC_GSRSEP ('GSRSEP')":FL "${GSRSEP//\"}" \
			--field="$GUI_GSRSTI!$DESC_GSRSTI ('GSRSTI')":NUM "${GSRSTI/#-/ -}" \
			--field="$(spanFont "$GUI_GSHDRSET" "H")":LBL "SKIP" \
			--field="$GUI_GSHDR!$DESC_GSHDR ('GSHDR')":CHK "$GSHDR" \
			--field="$GUI_GSHDRWGFS!$DESC_GSHDRWGFS ('GSHDRWGFS')":CHK "$GSHDRWGFS" \
			--field="$GUI_GSHDRSCNITS!$DESC_GSHDRSCNITS ('GSHDRSCNITS')":NUM "${GSHDRSCNITS/#-/ -}" \
			--field="$GUI_GSHDRITM!$DESC_GSHDRITM ('GSHDRITM')":CHK "$GSHDRITM" \
			--field="$GUI_GSHDRITMSDRNITS!$DESC_GSHDRITMSDRNITS ('GSHDRITMSDRNITS')":NUM "${GSHDRITMSDRNITS/#-/ -}" \
			--field="$GUI_GSHDRITMTGTNITS!$DESC_GSHDRITMTGTNITS ('GSHDRITMTGTNITS')":NUM "${GSHDRITMTGTNITS/#-/ -}" \
			--field="$(spanFont "$GUI_GSVRSET" "H")":LBL "SKIP" \
			--field="$GUI_GSVR!$DESC_GSVR ('GSVR')":CHK "$GSVR" \
			--field="$GUI_GSVREXNA!$DESC_GSVREXNA ('GSVREXNA')" "$GSVREXNA" \
			--field="$GUI_GSVRDEFNAM!$DESC_GSVRDEFNAM ('GSVRDEFNAM')" "$GSVRDEFNAM" \
			--field="$GUI_GSVROVERLAYKEY!$DESC_GSVROVERLAYKEY ('GSVROVERLAYKEY')" "$GSVROVERLAYKEY" \
			--field="$GUI_GSVRICONENABLE!$DESC_GSVRICONENABLE ('GSVRICONENABLE')":CHK "$GSVRICONENABLE" \
			--field="$GUI_GSVRICON!$DESC_GSVRICON ('GSVRICON')":FL "${GSVRICON//\"}" \
			--field="$GUI_GSVRSHOIMM!$DESC_GSVRSHOIMM ('GSVRSHOIMM')":CHK "$GSVRSHOIMM" \
			--field="$GUI_GSVRCONTROLBAR!$DESC_GSVRCONTROLBAR ('GSVRCONTROLBAR')":CHK "$GSVRCONTROLBAR" \
			--field="$GUI_GSVRCONTROLBARKEYBOARD!$DESC_GSVRCONTROLBARKEYBOARD ('GSVRCONTROLBARKEYBOARD')":CHK "$GSVRCONTROLBARKEYBOARD" \
			--field="$GUI_GSVRCONTROLBARCLOSE!$DESC_GSVRCONTROLBARCLOSE ('GSVRCONTROLBARCLOSE')":CHK "$GSVRCONTROLBARCLOSE" \
			--field="$GUI_GSVRSCROLLSSPEED!$DESC_GSVRSCROLLSSPEED ('GSVRSCROLLSSPEED')":NUM "$GSVRSCROLLSSPEED" \
			--field="$GUI_GSVRMODAL!$DESC_GSVRMODAL ('GSVRMODAL')":CHK "$GSVRMODAL" \
			--field="$(spanFont "$GUI_GSEMBEDDEDSET" "H")":LBL "SKIP" \
			--field="$GUI_GSDEFTOUCHMODE!$DESC_GSDEFTOUCHMODE ('GSDEFTOUCHMODE')":CB "$(cleanDropDown "${GSDEFTOUCHMODE}" "${GSTOUCHMODES}")" \
			--field="$GUI_GSIMMEDIATEFLIPS!$DESC_GSIMMEDIATEFLIPS ('GSIMMEDIATEFLIPS')":CHK "$GSIMMEDIATEFLIPS" \
			--field="$GUI_GSADAPTIVESYNC!$DESC_GSADAPTIVESYNC ('GSADAPTIVESYNC')":CHK "$GSADAPTIVESYNC" \
			--field="$GUI_GSPREFOUT!$DESC_GSPREFOUT ('GSPREFOUT')" "" \
			--field="$GUI_GSDRMMODE!$DESC_GSDRMMODE ('GSDRMMODE')":CB "$(cleanDropDown "${GSDRMMODE}" "${GSDRMMODES}")" \
			--field="$(spanFont "$GUI_GSADVOPTIONS" "H")":LBL "SKIP" \
			--field="$GUI_GSBACKEND!$DESC_GSBACKEND ('GSBACKEND')":CBE "$(cleanDropDown "${GSBACKEND}" "${GSBACKENDOPTS}")" \
			--field="$GUI_GSSTATSPATHENABLE!$DESC_GSSTATSPATHENABLE ('GSSTATSPATHENABLE')":CHK "$GSSTATSPATHENABLE" \
			--field="$GUI_GSSTATSPATH!$DESC_GSSTATSPATH ('GSSTATSPATH')":DIR "${GSSTATSPATH//\"}" \
			--field="$GUI_GSHIDECURSORDELAY!$DESC_GSHIDECURSORDELAY ('GSHIDECURSORDELAY')":NUM "${GSHIDECURSORDELAY/#-/ -}" \
			--field="$GUI_GSFORCECOMP!$DESC_GSFORCECOMP ('GSFORCECOMP')":CHK "$GSFORCECOMP" \
			--field="$GUI_GSDEBUGHUD!$DESC_GSDEBUGHUD ('GSDEBUGHUD')":CHK "$GSDEBUGHUD" \
			--field="$GUI_GSFORCEHDRSUPPORT!$DESC_GSFORCEHDRSUPPORT ('GSFORCEHDRSUPPORT')":CHK "$GSFORCEHDRSUPPORT" \
			--field="$GUI_GSFORCEHDROUTPUT!$DESC_GSFORCEHDROUTPUT ('GSFORCEHDROUTPUT')":CHK "$GSFORCEHDROUTPUT" \
			--field="$GUI_GSPREFERVKDEVICE!$DESC_GSPREFERVKDEVICE ('GSPREFERVKDEVICE')":CHK "$GSPREFERVKDEVICE" \
			--field="$GUI_GSWAYLAND!$DESC_GSWAYLAND ('GSWAYLAND')":CHK "$GSWAYLAND" \
			--field="$GUI_GSRT!$DESC_GSRT ('GSRT')":CHK "$GSRT" \
			--field="$GUI_GSHDLS!$DESC_GSHDLS ('GSHDLS')":CHK "$GSHDLS" \
			--field="$GUI_USEGAMESCOPEWSI!$DESC_USEGAMESCOPEWSI ('USEGAMESCOPEWSI')":CHK "$USEGAMESCOPEWSI" \
			--button="$BUT_CAN:0" --button="$BUT_DGM:2" --button="$BUT_DONE:4" "$GEOM"
			)"
			case $? in
				0)	{
						writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_CAN' - Exiting"
					}
				;;
				2)  {
						writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_DGM' - Resetting GameScope options to default"
						GameScopeReset
						GameScopeGui
				    }
				;;
				4)	{
						## TODO This section could still be simplified a bit further probably

						# Get selected GameScope options
						mapfile -d "|" -t -O "${#GSARR[@]}" GSARR < <(printf '%s' "$GASCOS")

						# Use "relative positioning" type system by calculating the position/index of each heading, and using this to get the values at a given index in the GSARR array
						# for example GSFILTERHEADING=16, then the first element will be GSFILTERHEADING + 1
						# This is ${GSARR[17]}, but if we were to add another option to the General settings, we would only need to bump GSGENERALOPTSLEN to 16
						GSGENERALHEADING=0  # Index in GSARR of General heading label
						GSGENERALOPTSLEN=15

						GSFILTERINGHEADING=$(( GSGENERALHEADING +  GSGENERALOPTSLEN + 1 )) # Index in GSARR of Filtering heading label
						GSFILTERINGOPTSLEN=6

						GSHDRHEADING=$(( GSFILTERINGHEADING + GSFILTERINGOPTSLEN + 1 ))
						GSHDROPTSLEN=6

						GSVRHEADING=$(( GSHDRHEADING + GSHDROPTSLEN + 1 ))
						GSVROPTSLEN=12

						GSEMBEDDEDHEADING=$(( GSVRHEADING + GSVROPTSLEN + 1 ))
						GSEMBEDDEDOPTSLEN=5

						GSADVANCEDHEADING=$(( GSEMBEDDEDHEADING + GSEMBEDDEDOPTSLEN + 1 ))
						# GSADVANCEDOPTSLEN=12  # Commented because Shellcheck gets angry about this being unused, but it is a good reference

						# GSARR[0] is the General heading
						GSINTRES="${GSARR[$GSGENERALHEADING + 1]}"
						GSSHWRES="${GSARR[$GSGENERALHEADING + 2]}"
						GSFLR="${GSARR[$GSGENERALHEADING + 3]}"
						GSFLU="${GSARR[$GSGENERALHEADING + 4]}"
						USEGAMESCOPE="${GSARR[$GSGENERALHEADING + 5]}"
						GSFS="${GSARR[$GSGENERALHEADING + 6]}"
						GSBW="${GSARR[$GSGENERALHEADING + 7]}"
						GSSE="${GSARR[$GSGENERALHEADING + 8]}"
						GSFWF="${GSARR[$GSGENERALHEADING + 9]}"
						GSFGC="${GSARR[$GSGENERALHEADING + 10]}"
						GSFGK="${GSARR[$GSGENERALHEADING + 11]}"
						GSFOOR="${GSARR[$GSGENERALHEADING + 12]}"
						GSENABLECUSTCUR="${GSARR[$GSGENERALHEADING + 13]}"
						GSCURSOR="${GSARR[$GSGENERALHEADING + 14]}"
						GSMOUSESENSITIVITY="${GSARR[$GSGENERALHEADING + 15]}"
						# GSARR[16] is the Filtering heading
						GSFLTR="${GSARR[$GSFILTERINGHEADING + 1]}"
						GSSCALE="${GSARR[$GSFILTERINGHEADING + 2]}"
						GSFSRS="${GSARR[$GSFILTERINGHEADING + 3]}"
						GSMSF="${GSARR[$GSFILTERINGHEADING + 4]}"
						GSRSEP="${GSARR[$GSFILTERINGHEADING + 5]}"
						GSRSTI="${GSARR[$GSFILTERINGHEADING + 6]}"
						# GSARR[23] is the HDR heading
						GSHDR="${GSARR[$GSHDRHEADING + 1]}"
						GSHDRWGFS="${GSARR[$GSHDRHEADING + 2]}"
						GSHDRSCNITS="${GSARR[$GSHDRHEADING + 3]}"
						GSHDRITM="${GSARR[$GSHDRHEADING + 4]}"
						GSHDRITMSDRNITS="${GSARR[$GSHDRHEADING + 5]}"
						GSHDRITMTGTNITS="${GSARR[$GSHDRHEADING + 6]}"
						# GSARR[30] is the VR heading
						GSVR="${GSARR[$GSVRHEADING + 1]}"
						GSVREXNA="${GSARR[$GSVRHEADING + 2]}"
						GSVRDEFNAM="${GSARR[$GSVRHEADING + 3]}"
						GSVROVERLAYKEY="${GSARR[$GSVRHEADING + 4]}"
						GSVRICONENABLE="${GSARR[$GSVRHEADING + 5]}"
						GSVRICON="${GSARR[$GSVRHEADING + 6]}"
						GSVRSHOIMM="${GSARR[$GSVRHEADING + 7]}"
						GSVRCONTROLBAR="${GSARR[$GSVRHEADING + 8]}"
						GSVRCONTROLBARKEYBOARD="${GSARR[$GSVRHEADING + 9]}"
						GSVRCONTROLBARCLOSE="${GSARR[$GSVRHEADING + 10]}"
						GSVRSCROLLSSPEED="${GSARR[$GSVRHEADING + 11]}"
						GSVRMODAL="${GSARR[$GSVRHEADING + 12]}"
						# GSARR[43] is the Embedded heading
						GSDEFTOUCHMODE="${GSARR[$GSEMBEDDEDHEADING + 1]}"
						GSIMMEDIATEFLIPS="${GSARR[$GSEMBEDDEDHEADING + 2]}"
						GSADAPTIVESYNC="${GSARR[$GSEMBEDDEDHEADING + 3]}"
						GSPREFOUT="${GSARR[$GSEMBEDDEDHEADING + 4]}"
						GSDRMMODE="${GSARR[$GSEMBEDDEDHEADING + 5]}"
						# GSARR[49] is the Advanced heading
						GSBACKEND="${GSARR[$GSADVANCEDHEADING + 1]}"
						GSSTATSPATHENABLE="${GSARR[$GSADVANCEDHEADING + 2]}"
						GSSTATSPATH="${GSARR[$GSADVANCEDHEADING + 3]}"
						GSHIDECURSORDELAY="${GSARR[$GSADVANCEDHEADING + 4]}"
						GSFORCECOMP="${GSARR[$GSADVANCEDHEADING + 5]}"
						GSDEBUGHUD="${GSARR[$GSADVANCEDHEADING + 6]}"
						GSFORCEHDRSUPPORT="${GSARR[$GSADVANCEDHEADING + 7]}"
						GSFORCEHDROUTPUT="${GSARR[$GSADVANCEDHEADING + 8]}"
						GSPREFERVKDEVICE="${GSARR[$GSADVANCEDHEADING + 9]}"
						GSWAYLAND="${GSARR[$GSADVANCEDHEADING + 10]}"
						GSRT="${GSARR[$GSADVANCEDHEADING + 11]}"
						GSHDLS="${GSARR[$GSADVANCEDHEADING + 12]}"
						USEGAMESCOPEWSI="${GSARR[$GSADVANCEDHEADING + 13]}"

						# Build the GameScope arguments string
						unset GAMESCOPE_ARGS
						GAMESCOPE_ARGS=""

						# Internal width/height (-w, -h) broken out from string like '1280x720'
						# NOTE: In future if `GSINTW` is blank but `GSINTH` is set, we could calculate a corresponding 16:9 width like GameScope does
						GSINTW1="${GSINTRES%x*}"
						GSINTW="${GSINTW1%%-*}"
						GSINTH1="${GSINTRES#*x}"
						GSINTH="${GSINTH1%%-*}"

						# Show width/height (-W, -H) broken out from string like '1280x720'
						# NOTE: In future if `GSINTW` is blank but `GSINTH` is set, we could calculate a corresponding 16:9 width like GameScope does
						GSSHWW1="${GSSHWRES%x*}"
						GSSHWW="${GSSHWW1%%-*}"
						GSSHWH1="${GSSHWRES#*x}"
						GSSHWH="${GSSHWH1%%-*}"

						### GENERAL OPTIONS ###
						if [ -n "$GSINTW" ] && [ -n "$GSINTH" ]                         ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -w ${GSINTW} -h ${GSINTH}"; fi
						if [ -n "$GSSHWW" ] && [ -n "$GSSHWH" ]                         ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -W ${GSSHWW} -H ${GSSHWH}"; fi
						if [ "$GSFLR" -eq "$GSFLR" ] 2>/dev/null                        ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -r ${GSFLR}"; fi
						if [ "$GSFLU" -eq "$GSFLU" ] 2>/dev/null                        ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -o ${GSFLU}"; fi
						if [ "$GSFS" == "TRUE" ]                                        ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -f"; fi
						if [ "$GSBW" == "TRUE" ]                                        ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -b"; fi
						if [ "$GSSE" == "TRUE" ]                                        ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -e"; fi
						if [ "$GSFWF" == "TRUE" ]                                       ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --force-windows-fullscreen"; fi
						if [ "$GSFGC" == "TRUE" ]                                       ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --force-grab-cursor"; fi
						if [ "$GSFGK" == "TRUE" ]                                       ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -g"; fi
						if [ -f "$GSCURSOR" ] && [ "$GSENABLECUSTCUR" == "TRUE" ]       ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --cursor '${GSCURSOR}'"; fi
						if [ "$GSMOUSESENSITIVITY" -gt 1 ]							; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -s ${GSMOUSESENSITIVITY}"; fi
						if [ ! "$GSFOOR" == "$GSNORM" ] && [ -n "$GSFOOR" ]             ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --force-orientation ${GSFOOR}"; fi  # Only force orientation if option other than default 'normal' is selected
						### GENERAL OPTIONS END ###

						### FILTERING OPTIONS ###
						# Used to control whether to apply --sharpness since there are various conditions where
						# this could be true (new -F option but ONLY if we pass fsr/nis, or legacy -U/-S option)
						GSAPPLYSHARPNESS=0
						if [ ! "$GSFLTR" == "$NON" ] && [ -n "$GSFLTR" ]; then
							# Pass -F if available for current GameScope version, otherwise pass legacy switches
							if [ "$GSNEWFILTERMODE" -eq 1 ]; then
								GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -F ${GSFLTR}"
							else
								# Only manages -U/-Y/-n, because those were the only filtering switches available
								# we don't have any other to account for with older GameScope versions -- If a different
								# option is selected for GSFLTR, then we just don't pass any flags
								#
								# Even though with legacy options, all 3 of these could be passed, the -F flag won't support this and neither
								# will the UI, so we only support selecting 1
								if [ "$GSFLTR" == "nearest" ]; then
									GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -n"
								elif [ "$GSFLTR" == "fsr" ]; then
									GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -U"
								elif [ "$GSFLTR" == "nis" ]; then
									GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -Y"
								fi
							fi

							if [ "$GSFLTR" == "fsr" ] || [ "$GSFLTR" == "nis" ]; then  # CASE SENSITIVE
								GSAPPLYSHARPNESS=1
							fi
						fi

						if [ ! "$GSSCALE" == "$NON" ] && [ -n "$GSSCALE" ]; then
							if [ "$GSNEWFILTERMODE" -eq 1 ]; then
								GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -S ${GSSCALE}"
							else
								# This is the only legacy scale switch, -i
								if [ "$GSSCALE" == "integer" ]; then
									GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -i"
								fi
							fi
						fi

						if [ ! "$GSMSF" == "0" ]                                        ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -m ${GSMSF}"; fi # Ignore Max Scale Factor if 0
						if [ "$GSFSRS" -eq "$GSFSRS" ] 2>/dev/null && [ "$GSAPPLYSHARPNESS" -eq 1 ]; then
							# Sharpness Value should only be passed if FSR or NIS is enabled
							writelog "INFO" "${FUNCNAME[0]} - Adding sharpness parameter to the gamescope arguments:"
							if grep -q "$FSRS_STR" <<< "$("$(command -v "$GAMESCOPE")" --help 2>&1)"; then
								writelog "INFO" "${FUNCNAME[0]} - using '--fsr-sharpness ${GSFSRS}'"
								GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --fsr-sharpness ${GSFSRS}"
							else
								writelog "INFO" "${FUNCNAME[0]} - using '--sharpness ${GSFSRS}'"
								GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --sharpness ${GSFSRS}"
							fi
						fi

						if [ -f "$GSRSEP" ]                                        		; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --reshade-effect '${GSRSEP}'"; fi
						if [ ! "$GSRSTI" == "0" ]                              			; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --reshade-technique-idx ${GSRSTI}"; fi
						## FILTERING OPTIONS END ###

						### HDR OPTIONS ###
						# Possible to check if any HDR displays available and warn if not?
						if [ "$GSHDR" == "TRUE" ]; then
							GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --hdr-enabled";

							writelog "INFO" "${FUNCNAME[0]} - GameScope HDR enabled, forcing DXVK_HDR=1"
							export DXVK_HDR=1
						fi
						if [ "$GSHDRWGFS" == "TRUE" ]; then
							# Don't enable GSHDRWGFS if GSHDR is not enabled first
							if [ "$GSHDR" == "TRUE" ]; then
								GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --hdr-wide-gammut-for-sdr"
							else
								writelog "WARN" "${FUNCNAME[0]} - GSHDRWGFS (--hdr-wide-gammut-for-sdr) option for GameScope enabled but HDR was not enabled - Ignoring as this option would have no effect"
							fi
						fi
						if [ ! "$GSHDRSCNITS" == "400" ]; then
							# Only pass value if nits != 400 && HDR enabled (400 is the default)
							if [ "$GSHDR" == "TRUE" ] && [ "$GSHDRWGFS" == "TRUE" ]; then
								GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --hdr-sdr-content-nits ${GSHDRSCNITS}"
							else
								writelog "WARN" "${FUNCNAME[0]} - GSHDRSCNITS (--hdr-sdr-content-nits) option for GameScope was set but HDR and SDR were not enabled - Ignoring as this option would have no effect"
							fi
						else
							writelog "INFO" "${FUNCNAME[0]} - GSHDRSCNITS (--hdr-sdr-content-nits) option for GameScope was left at default 203 - GameScope should use this anyway - Ignoring as this option would have no effect"
						fi
						if [ "$GSHDRITM" == "TRUE" ]                                    ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --hdr-itm-enable"; fi
						if [ ! "$GSHDRITMSDRNITS" == "0" ]                              ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --hdr-itm-sdr-nits ${GSHDRITMSDRNITS}"; fi
						if [ ! "$GSHDRITMTGTNITS" == "0" ]                              ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --hdr-itm-target-nits ${GSHDRITMTGTNITS}"; fi
						### HDR OPTIONS END ###

						### VR OPTIONS ###
						if [ "$GSVR" == "TRUE" ]                                        ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --openvr"; fi
						if [ "${#GSVREXNA}" -gt 0 ]                                     ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --vr-overlay-explicit-name '${GSVREXNA}'"; fi  # Don't set explicit name if it's blank
						if [ "${#GSVRDEFNAM}" -gt 0 ]                                   ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --vr-overlay-default-name '${GSVRDEFNAM}'"; fi  # Don't set default name if it's blank
						if [ "${#GSVROVERLAYKEY}" -gt 0 ] 								; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --vr-overlay-key '${GSVROVERLAYKEY}'"; fi  # Don't set overlay key if it's blank
						if [ -f "$GSVRICON" ] && [ "$GSVRICONENABLE" == "TRUE" ]        ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --vr-overlay-icon '${GSVRICON}'"; fi
						if [ "$GSVRSHOIMM" == "TRUE" ]                                  ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --vr-overlay-show-immediately"; fi
						if [ "$GSVRCONTROLBAR" == "TRUE" ]                              ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --vr-overlay-enable-control-bar"; fi
						if [ "$GSVRCONTROLBARKEYBOARD" == "TRUE" ]                      ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --vr-overlay-enable-control-bar-keyboard"; fi
						if [ "$GSVRCONTROLBARCLOSE" == "TRUE" ]                         ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --vr-overlay-enable-control-bar-close"; fi
						if [ "$GSVRSCROLLSSPEED" -gt 8 ]                                ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --vr-scrolls-speed ${GSVRSCROLLSSPEED}"; fi  # 8.0 is the default value
						if [ "$GSVRMODAL" == "TRUE" ]                                   ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --vr-overlay-modal"; fi
						### VR OPTIONS END

						### EMBEDDED OPTIONS ###
						# Don't pass default touch mode option if we left at default
						if [ ! "${GSDEFTOUCHMODE}" == "${GSDEF}" ] && [ -n "${GSDEFTOUCHMODE}" ]; then
							# Get the corresponding number that should be passed to GameScope from the number in the dropdown string, e.g. gets "0" from "hover:0"
							SELECTEDTOUCHMODE="$( echo "$GSDEFTOUCHMODE" | cut -d ":" -f 2 )"
							GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --default-touch-mode ${SELECTEDTOUCHMODE}"
						fi
						if [ "$GSIMMEDIATEFLIPS" == "TRUE" ]                            ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --immediate-flips"; fi
						if [ "$GSADAPTIVESYNC" == "TRUE" ]                              ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --adaptive-sync"; fi
						if [ "${#GSPREFOUT}" -gt 0 ]                                    ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -O ${GSPREFOUT}"; fi  # Don't pass preferred output(s) if the textbox is blank
						# EMBEDDED OPTIONS END

						## ADVANCED OPTIONS ###
						if [ ! "$GSBACKEND" == "auto" ]                                 ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --backend ${GSBACKEND}"; fi  # Don't pass GameScope Backend if left at "auto"; GameScope defaults to this anyway
						if [ ! "$GSDRMMODE" == "${GSDEF}" ] && [ -n "$GSDRMMODE" ]      ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --generate-drm-mode ${GSDRMMODE}"; fi  # Don't pass DRM mode if "default"
						if [ -d "$GSSTATSPATH" ] && [ "$GSSTATSPATHENABLE" == "TRUE" ]  ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --stats-path '${GSSTATSPATH}'"; fi
						if [ ! "$GSHIDECURSORDELAY" == "0" ]                            ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} -C ${GSHIDECURSORDELAY}"; fi  # Ignore cursor delay if it's 0
						if [ "$GSFORCECOMP" == "TRUE" ]                                 ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --force-composition"; fi
						if [ "$GSDEBUGHUD" == "TRUE" ]                                  ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --debug-hud"; fi
						if [ "$GSFORCEHDRSUPPORT" == "TRUE" ]                           ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --hdr-debug-force-support"; fi
						if [ "$GSFORCEHDROUTPUT" == "TRUE" ]                            ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --hdr-debug-force-output"; fi
						if [ "$GSPREFERVKDEVICE" == "TRUE" ]                            ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --prefer-vk-device"; fi
						if [ "$GSWAYLAND" == "TRUE" ]                                   ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --expose-wayland"; fi
						if [ "$GSRT" == "TRUE" ]                                        ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --rt"; fi
						if [ "$GSHDLS" == "TRUE" ]                                      ; then  GAMESCOPE_ARGS="${GAMESCOPE_ARGS} --headless"; fi
						### ADVANCED OPTIONS END ###

						# Remove trailing whitespace and append '--'
						GAMESCOPE_ARGS="${GAMESCOPE_ARGS# } --"

						writelog "INFO" "${FUNCNAME[0]} - Saving configured GAMESCOPE_ARGS '$GAMESCOPE_ARGS' into '$STLGAMECFG'"
						touch "$FUPDATE"
						updateConfigEntry "GAMESCOPE_ARGS" "$GAMESCOPE_ARGS" "$STLGAMECFG"
						touch "$FUPDATE"
						updateConfigEntry "USEGAMESCOPE" "$USEGAMESCOPE" "$STLGAMECFG"
						touch "$FUPDATE"
						updateConfigEntry "USEGAMESCOPEWSI" "$USEGAMESCOPEWSI" "$STLGAMECFG"  # GAMESCOPE_WSI_ENABLE=1 option, since its an env var and not a gamescope flag we save it to game config
					}
				;;
			esac
}

function GameScopeReset {
	# This resets the options on the GUI to blank, but when the UI is re-loaded the options are set again to their menu defaults
	# once the "Done" button is pressed e.g. the resolutions default to 1280x720
	GAMESCOPE_ARGS="$NON"
	touch "$FUPDATE"
	updateConfigEntry "GAMESCOPE_ARGS" "$GAMESCOPE_ARGS" "$STLGAMECFG"
	setGameScopeVars  # This needs to be called to fix empty dropdown values for some reason (even though it should get called from GameScopeGui /shrug)
}

function StandaloneProtonGame {
	function SapRun {
		if [ "$SAPRUN" == "TRUE" ]; then
			RUNSAPPROTON="$(getProtPathFromCSV "$SAPPROTON")"
			if [ ! -f "$RUNSAPPROTON" ]; then
				RUNSAPPROTON="$(fixProtonVersionMismatch "SAPPROTON" "$STLGAMECFG" X)"
			fi

			if [ ! -f "$RUNSAPPROTON" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - No executable for selected Proton '$SAPPROTON' found"
			elif [ ! -f "$SAPEXE" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - No executable found"
			elif [ ! -d "$SAP_COMPAT_DATA_PATH" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - No $CODA dir found"
			else
				if [ -z "$SAPARGS" ]; then
					RUNSAPARGS=""
				else
					mapfile -d " " -t -O "${#RUNSAPARGS[@]}" RUNSAPARGS < <(printf '%s' "$SAPARGS")
				fi

				writelog "INFO" "${FUNCNAME[0]} - Starting '$SAPEXE' with '$SAPPROTON' with STEAM_COMPAT_DATA_PATH '$SAP_COMPAT_DATA_PATH'"
				STEAM_COMPAT_DATA_PATH="$SAP_COMPAT_DATA_PATH" "$RUNSAPPROTON" run "$SAPEXE" "${RUNSAPARGS[@]}"
			fi
		fi
	}

	function SapGui {
		export CURWIKI="$PPW/Standalone-Proton"
		TITLE="${PROGNAME}-StandaloneProtonGame"
		pollWinRes "$TITLE"

		SAPGAMELIST="$(find "$STLGSAPD" -type f -exec basename {} .conf \; | tr '\n' '!')"

		if [ -z "$SAP_COMPAT_DATA_PATH" ]; then
			SAP_COMPAT_DATA_PATH="$STLGSACD/${PROGNAME,,}-$((10000 + RANDOM % 10000))"
			mkProjDir "$SAP_COMPAT_DATA_PATH"
			IN_SAP_COMPAT_DATA_PATH="$SAP_COMPAT_DATA_PATH"
		fi

		PROTPARTS="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
		--title="$TITLE" \
		--text="$(spanFont "$GUI_SAPTEXT" "H")" \
		--field=" ":LBL " " --separator="|" \
		--field="$GUI_SAPGAME!$DESC_SAPGAME":CBE "$(cleanDropDown "${SAPGAME}" "$SAPGAMELIST")" \
		--field="$GUI_SAPPROTON!$DESC_SAPPROTON":CB "$(cleanDropDown "${SAPPROTON//\"}" "$PROTYADLIST")" \
		--field="$GUI_SAP_COMPAT_DATA_PATH!$DESC_SAP_COMPAT_DATA_PATH":DIR "${SAP_COMPAT_DATA_PATH//\"}" \
		--field="$GUI_SAPEXE!$DESC_SAPEXE":FL "${SAPEXE//\"}" \
		--field="$GUI_SAPARGS!$DESC_SAPARGS" "${SAPARGS//\"}"\
		--field="$GUI_SAPRUN!$DESC_SAPRUN":CHK "$SAPRUN" \
		--button="$BUT_CAN:0" --button="$BUT_LOAD:2" --button="$BUT_RUN:4" "$GEOM"
		)"
		case $? in
			0)	{
					writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_CAN' - Exiting"
					if [ -d "$IN_SAP_COMPAT_DATA_PATH" ]; then
						rmdir "$IN_SAP_COMPAT_DATA_PATH"
					fi
				}
			;;
			2)	{
					writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_LOAD'"
					mapfile -d "|" -t -O "${#SAPARR[@]}" SAPARR < <(printf '%s' "$PROTPARTS")
					SAPGAME="${SAPARR[1]}"
					writelog "INFO" "Loading '${STLGSAPD}/${SAPGAME}.conf' and starting the gui ${FUNCNAME[0]}"
					loadCfg "${STLGSAPD}/${SAPGAME}.conf"

					if [ "$IN_SAP_COMPAT_DATA_PATH" != "$SAP_COMPAT_DATA_PATH" ]; then
						if [ -d "$IN_SAP_COMPAT_DATA_PATH" ]; then
							writelog "INFO" "${FUNCNAME[0]} - User chose an own COMPAT_DATA_PATH, removing autocreated '$IN_SAP_COMPAT_DATA_PATH'"
							rmdir "$IN_SAP_COMPAT_DATA_PATH"
						fi
					fi

					"${FUNCNAME[0]}"
				}
			;;
			4)	{
					writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_RUN' - Exiting"
					unset SAPARR
					mapfile -d "|" -t -O "${#SAPARR[@]}" SAPARR < <(printf '%s' "$PROTPARTS")
					SAPGAME="${SAPARR[1]}"
					SAPPROTON="${SAPARR[2]}"
					SAP_COMPAT_DATA_PATH="${SAPARR[3]}"
					SAPEXE="${SAPARR[4]}"
					SAPARGS="${SAPARR[5]}"
					SAPRUN="${SAPARR[6]}"

					if [ "$IN_SAP_COMPAT_DATA_PATH" != "$SAP_COMPAT_DATA_PATH" ]; then
						if [ -d "$IN_SAP_COMPAT_DATA_PATH" ]; then
							writelog "INFO" "${FUNCNAME[0]} - User chose an own COMPAT_DATA_PATH, removing autocreated '$IN_SAP_COMPAT_DATA_PATH'"
							rmdir "$IN_SAP_COMPAT_DATA_PATH"
						fi
					fi

					if [ -n "$SAPGAME" ];then
						SAPCFG="${STLGSAPD}/${SAPGAME}.conf"

						touch "$FUPDATE" "$SAPCFG"
						updateConfigEntry "SAPGAME" "$SAPGAME" "$SAPCFG"
						if [ -n "$SAPPROTON" ];then
							touch "$FUPDATE"
							updateConfigEntry "SAPPROTON" "$SAPPROTON" "$SAPCFG"
						fi
						if [ -n "$SAP_COMPAT_DATA_PATH" ];then
							touch "$FUPDATE"
							updateConfigEntry "SAP_COMPAT_DATA_PATH" "$SAP_COMPAT_DATA_PATH" "$SAPCFG"
						fi
						if [ -n "$SAPEXE" ];then
							touch "$FUPDATE"
							updateConfigEntry "SAPEXE" "$SAPEXE" "$SAPCFG"
						fi
						if [ -n "$SAPARGS" ];then
							touch "$FUPDATE"
							updateConfigEntry "SAPARGS" "$SAPARGS" "$SAPCFG"
						fi
						if [ -n "$SAPRUN" ];then
							touch "$FUPDATE"
							updateConfigEntry "SAPARGS" "$SAPARGS" "$SAPCFG"
						fi
					fi
					SapRun
				}
			;;
		esac
	}

	createProtonList X

	mkProjDir "$STLGSAPD"
	SAPGUI=1
	SAPRUN="TRUE"

	if [ -n "$1" ] && [ -z "$2" ] && [ -f "${STLGSAPD}/${1}.conf" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Loading '${STLGSAPD}/${1}.conf' silently"
		loadCfg "${STLGSAPD}/${1}.conf"
		SAPGUI=0
		SapRun
	elif [ -n "$1" ] && [ ! -f "${STLGSAPD}/${1}.conf" ]; then
		if [ "$1" == "list" ]; then
			find "$STLGSAPD" -type f -exec basename {} .conf \;
			SAPGUI=0
			SAPRUN="FALSE"
		else
			writelog "INFO" "${FUNCNAME[0]} - Using '$1' as game title"
			SAPGAME="$1"
		fi
	elif [ -n "$1" ] && [ -n "$2" ] && [ -f "${STLGSAPD}/${1}.conf" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Loading '${STLGSAPD}/${1}.conf' and starting the gui"
		loadCfg "${STLGSAPD}/${1}.conf"
		SAPGUI=1
	elif [ -z "$1" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Only starting plain gui"
	fi

	if [ "$SAPGUI" -eq 1 ]; then
		SapGui
	fi
}

function getWinecfgExecutable {
	# Check if we have a systemwide Winecfg
	if [ -x "$(command -v "$WINECFG")" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using Winecfg found at '$WINECFG'"
		OTWINECFGEXE="$WINECFG"
	else
		# Try to use winecfg with Proton executable
		writelog "INFO" "${FUNCNAME[0]} - Trying to use Winetrickks with game Proton version"
		if [ -z "$RUNPROTON" ]; then
			writelog "WARN" "${FUNCNAME[0]} - RUNPROTON is empty - '$RUNPROTON' - Maybe this is not a Proton game?"
		else
			WINECFGBASEPATH="$(dirname "$RUNPROTON")"

			writelog "INFO" "${FUNCNAME[0]} - RUNPROTON is '$RUNPROTON'"
			writelog "INFO" "${FUNCNAME[0]} - WINECFGBASEPATH is '$WINECFGBASEPATH'"

			if [ -d "$WINECFGBASEPATH" ]; then
				OTWINECFGEXE="$( find "$WINECFGBASEPATH" -name "winecfg.exe" | head -n1 )"
				writelog "INFO" "${FUNCNAME[0]} - Using Winecfg found at '$OTWINECFGEXE'"
			else
				writelog "WARN" "${FUNCNAME[0]} - Could not find directory name for Proton version '$RUNPROTON' - This probably shouldn't happen! - Could not get Winecfg executable to run"
			fi
		fi
	fi
}

# Extracted from part of setModWine
# Does not handle Proton version mismatches but this should hopefully be handled before game launch -- a PR would be welcome for this until I get around to it :-)
function getWineBinFromProtPath {
	INPROTON="$1"

	CHECKDNWINED="$(dirname "$INPROTON")/$DBW"  # Valve Proton structure
	CHECKDNWINEF="$(dirname "$INPROTON")/$FBW"  # GE-Proton structure

	FWINEVAR=""
	if [ -f "$CHECKDNWINED" ]; then
		writelog "INFO" "${FUNCNAME[0]} - CHECKDNWINED is a file -- '${CHECKDNWINED}' -- Looks like we have a Valve Proton here"
		FWINEVAR="$CHECKDNWINED"
	elif [ -f "$CHECKDNWINEF" ]; then
		FWINEVAR="$CHECKDNWINEF"
		writelog "INFO" "${FUNCNAME[0]} - CHECKDNWINEF is a file -- '${CHECKDNWINEF}' -- Looks like we have a GE-Proton here"
	else
		writelog "ERROR" "${FUNCNAME[0]} - Could not find Wine binary for Proton '$INPROTON' - can't continue"
	fi

	echo "$FWINEVAR"
}

# These functions will always use the game Proton version instead of the Proton version in the dropdown
# I don't think there's a way for us to get the Proton version in this dropdown to use with the game
#
# Having these as primary menu buttons would mean the dialog would close and the user would have to re-open the one-time run menu to run their game
# The tradeoff for now is having them set the Proton version by changing the game version before going to the one-time menu, and then they can run Winetricks
#
# I think this is a better tradeoff than having the menu close, as the amount of users who would change the Proton version are probably minimal
# compared to the users who would be annoyed by the dialog closing



# These two winecfg and winetricks functions need to be updated to accomodate a passed in AppID from the command line
# - needs to be able to get game config / proton versions
# - handle cases where a Proton version is not set for a game that has never been launched with STL
# - abort for native games (may need to check executable for this?)
function oneTimeWinecfg {
	OTPROT="$( getProtPathFromCSV "$USEPROTON" )"
	OTWINE="$( getWineBinFromProtPath "$OTPROT" )"

	if [ -z "$GPFX" ]; then
		getGameFiles "$AID"  # Potentially fix GPFX not being set in some situations
	fi

	writelog "INFO" "${FUNCNAME[0]} - Running OneTime Winecfg with Wine '$RUNWINE'"

	if [ -z "$RUNPROTON" ]; then
		RUNPROTON="$OTPROT"  # Makes getWinecfgExecutable happy
	fi

	getWinecfgExecutable
	WINEDEBUG="-all" WINEPREFIX="$GPFX" "$OTWINE" "$OTWINECFGEXE" # GPFX is not defined on Steam Deck for some reason? Need to fix, then this should work
}

# Needs updated to accomodate a passed in AppID from the command line
function oneTimeWinetricks {
	writelog "INFO" "${FUNCNAME[0]} - Getting Winetricks binary"
	chooseWinetricks
	if [ ! -x "$(command -v "$WINETRICKS")" ]; then
		writelog "WARN" "${FUNCNAME[0]} - Could not run one-time Winetricks because Winetricks is not installed - Skipping"
	else
		OTPROT="$( getProtPathFromCSV "$USEPROTON" )"
		OTWINE="$( getWineBinFromProtPath "$OTPROT" )"

		if [ -z "$GPFX" ]; then
			getGameFiles "$AID"  # Potentially fix GPFX not being set in some situations
		fi

		writelog "INFO" "${FUNCNAME[0]} - Running OneTime Winetricks for prefix '$GPFX'"

		WINE="$OTWINE" WINEPREFIX="$GPFX" "$WINETRICKS"  # GPFX is not defined on Steam Deck for some reason? Need to fix, then this should work
	fi
}

# Does the shared setup for one-time run commandline and GUI functions
function setOneTimeRunVars {
	if [ -n "$1" ]; then
		AID="$1"
		setAIDCfgs
	fi
	loadCfg "$STLGAMECFG"

	if [ -z "$STEAM_COMPAT_DATA_PATH" ]; then
		METCFG="$CUMETA/${AID}.conf"
		if [ -f "$METCFG" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Loading Metadata '$METCFG'"
			fixCustomMeta "$METCFG" # will be removed again later
			loadCfg "$METCFG"
		fi

		if [ -n "$WINEPREFIX" ] && [ -d "$WINEPREFIX" ]; then
			STEAM_COMPAT_DATA_PATH="${WINEPREFIX%/*}"
			writelog "INFO" "${FUNCNAME[0]} - Found STEAM_COMPAT_DATA_PATH '$STEAM_COMPAT_DATA_PATH'"
		fi
	fi

	# Since we can use OTR with native Linux games, we just warn when there is no STEAM_COMPAT_DATA_PATH
	if [ -z "$STEAM_COMPAT_DATA_PATH" ]; then
		writelog "WARN" "${FUNCNAME[0]} - STEAM_COMPAT_DATA_PATH could not be determined - This may mean we're running a native Linux game here, in which case this can be safely ignored."
		writelog "WARN" "${FUNCNAME[0]} - If you need to use a Windows executable you'll have to use One-Time Run with a Windows game, or the Windows release of this game."
	fi
}

function OneTimeRunReset {
	# Reset all One-Time Run variables with the dummy value (easier than removing them, which should be unnecessary)
	writelog "INFO" "${FUNCNAME[0]} - Restoring defaults for One Time Run variables"

	# Remove config file entries -- Blank entries will act as "(none)" for files/paths, "false" for checkboxes, and the
	# Proton version is handled already with a mismatch check
	touch "$FUPDATE"
	updateConfigEntry "OTPROTON" "DUMMY" "$STLGAMECFG"
	touch "$FUPDATE"
	updateConfigEntry "OTEXE" "DUMMY" "$STLGAMECFG"
	touch "$FUPDATE"
	updateConfigEntry "OTARGS" "DUMMY" "$STLGAMECFG"
	touch "$FUPDATE"
	updateConfigEntry "OTUSEEXEDIR" "DUMMY" "$STLGAMECFG"
	touch "$FUPDATE"
	updateConfigEntry "OTFORCEPROTON" "DUMMY" "$STLGAMECFG"
	touch "$FUPDATE"
	updateConfigEntry "OTSLR" "DUMMY" "$STLGAMECFG"

	# Unset variables to ensure values are cleared on the UI
	unset "$OTPROTON"
	unset "$OTEXE"
	unset "$OTARGS"
	unset "$OTUSEEXEDIR"
	unset "$OTFORCEPROTON"
	unset "$OTSLR"
}

# Called when a user passes arguments for onetimerun
# TODO a way to use default game Proton version, either if '--proton' is not supplied or if '--proton="default"'?
# TODO add a way to use PROTON_LOG with custom command?
function commandlineOneTimeRun {
	setOneTimeRunVars "$1"
	# Get incoming arguments
	OTRUNDIR="$( pwd )"  # Default to current script directory
	USEEXEDIR=0
	OTFORCEPROTON=0
	OTSLR=0
	OTRESET=0
	for i in "$@"; do
		case $i in
			--exe=*)
				OTEXE="$( realpath "${i#*=}" )"
				shift ;;
			--proton=*)
				# Needed for saving to work, since RUNOTPROTON gets overridden with Proton path from CSV
				RUNOTPROTON="${i#*=}"
				OTPROTON="$RUNOTPROTON"
				shift ;;
			--workingdir=*)
				PASSEDRUNDIR="${i#*=}"
				if [ -n "$PASSEDRUNDIR" ] && [ -d "$PASSEDRUNDIR" ]; then  # Ensure working directory exists
					OTRUNDIR="$PASSEDRUNDIR"
				fi
				shift ;;
			--useexedir)
				USEEXEDIR=1
				shift ;;
			--args=*)
				OTARGS="${i#*=}"
				shift ;;
			--forceproton)
				OTFORCEPROTON=1
				shift ;;
			--useslr)
				OTSLR=1
				shift ;;
			--save)
				OTSAVE="TRUE"
				shift ;;
			--default)
				OneTimeRunReset
				OTRESET=1
				shift ;;
		esac
	done

	# When default is passed, all other options are ignored, as it should be used standalone
	# (and priamrily internally for the defaults button)
	if [ "$OTRESET" -eq 1 ]; then
		return
	fi

	# Ensure EXE is given and that directory to run the exe in is valid, and also ensure we have a valid Proton version to run the exe with
	# TODO refactor to flatten
	if [ -n "$OTEXE" ] && [ -f "$OTEXE" ]; then  # Valid executable required (Windows executable or Linux executable/file/etc, not really an EXE for Linux but oh well - Naming is hard!)
		if [ "$USEEXEDIR" -eq 1 ]; then  # Use EXE dir as working dir
			OTRUNDIR="$( dirname "$OTEXE" )"
			writelog "INFO" "${FUNCNAME[0]} - Using executable directory '$OTRUNDIR' as working directory"
		fi

		if [ -d "$OTRUNDIR" ]; then  # Working directory needs to be valid
			# Don't check executable type if OTFORCEPROTON
			if [ "$OTFORCEPROTON" -eq 1 ]; then
				ISWINDOWSEXE=1
			else
				ISWINDOWSEXE="$(file "$OTEXE" | grep -c "PE32")"  # TODO how do things like .bat scripts work here?
			fi

			if [ "$ISWINDOWSEXE" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Looks like we've got a Windows executable here or the user forced Proton"
				# Ensure we have STEAM_COMPAT_DATA_PATH before we try to run a Windows executable
				if [ -z "$STEAM_COMPAT_DATA_PATH" ]; then
					writelog "ERROR" "${FUNCNAME[0]} - Cannot run Windows executable without a STEAM_COMPAT_DATA_PATH -- Maybe you're using One-Time Run with a native Linux game?"
					echo "Cannot run Windows executable without a STEAM_COMPAT_DATA_PATH -- Maybe you're using One-Time Run with a native Linux game?"
					return
				fi
				writelog "INFO" "${FUNCNAME[0]} - Trying to find Proton version to launch executable with from given Proton name '$RUNOTPROTON'"

				# If user did not pass '--proton', then use the game's Proton version
				if [ -z "$RUNOTPROTON" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Did not get RUNOTPROTON from '--proton' option (perhaps this option was omitted?), so using per-game Proton version (USEPROTON) '$USEPROTON'"
					RUNOTPROTON="$USEPROTON"
					OTPROTON="$USEPROTON"
				fi

				RUNOTPROTON="$(getProtPathFromCSV "$RUNOTPROTON")"
				if [ ! -f "$RUNOTPROTON" ]; then
					writelog "INFO" "${FUNCNAME[0]} - RUNOTPROTON '$RUNOTPROTON' could not be found in ProtonCSV -- Attempting to resolve mismatch"
					# "1" forces fixProtonVersionMismatch to run even though ISGAME != 2 (ISGAME -eq 2 means Proton game, but ISGAME may not be 2 for One-Time Run, so we force Proton version resolution)
					if [ -n "$RUNOTPROTON" ]; then
						RUNOTPROTON="$(fixProtonVersionMismatch "RUNOTPROTON" "$STLGAMECFG" "1" X)"
					else
						writelog "INFO" "${FUNCNAME[0]} - Attemptiing to resolve Proton version mismatch for blank RUNOTPROTON '$RUNOTPROTON' by instead trying to resolve based on USEPROTON '$USEPROTON' which should hopefully not be blank"
						RUNOTPROTON="$(fixProtonVersionMismatch "USEPROTON" "$STLGAMECFG" "1" X)"
					fi

					# Update OTPROTON to make sure it matches any Proton version mismatch fixes / to have a value if One-Time Run was called without '--proton' (i.e use the Game Proton version)
					OTPROTON="$( getProtNameFromPath "$RUNOTPROTON" )"
				fi
			else
				writelog "INFO" "${FUNCNAME[0]} - Given executable detected as native Linux binary, this is usually correct but if this was detected incorrectly you should force Proton!"
			fi

			if [ -f "$RUNOTPROTON" ] || [ "$ISWINDOWSEXE" -eq 0 ]; then
				if [ -f "$RUNOTPROTON" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Found Proton version '$RUNOTPROTON'"
				else
					writelog "INFO" "${FUNCNAME[0]} - Executable is native Linux, no Proton version required."
				fi

				if [ -z "$OTARGS" ]; then  # Set the custom arguments for executable
					RUNOTARGS=""
				else
					mapfile -d " " -t -O "${#RUNOTARGS[@]}" RUNOTARGS < <(printf '%s' "$OTARGS")
					writelog "INFO" "${FUNCNAME[0]} - Passing arguments to One-Time Run executable '${OTRUNARGS[*]}'"
				fi

				# NOTE: Having save here means values won't save unless a launch is successful -- This is probably ok, but just a note for future reference
				if [ "$OTSAVE" == "TRUE" ];then  # Save one-time settings to game config file -- Really only useful for OneTimeRunGUI
					writelog "INFO" "${FUNCNAME[0]} - Saving One time run settings into '$STLGAMECFG'"

					# Only save Proton version if we're using a Windows executable, otherwise we don't set a Proton version!
					if [ "$ISWINDOWSEXE" -eq 1 ]; then
						touch "$FUPDATE"
						updateConfigEntry "OTPROTON" "$OTPROTON" "$STLGAMECFG"
					fi
					touch "$FUPDATE"
					updateConfigEntry "OTEXE" "$OTEXE" "$STLGAMECFG"
					touch "$FUPDATE"
					updateConfigEntry "OTARGS" "$OTARGS" "$STLGAMECFG"
					touch "$FUPDATE"
					updateConfigEntry "OTUSEEXEDIR" "$USEEXEDIR" "$STLGAMECFG"
					touch "$FUPDATE"
					updateConfigEntry "OTFORCEPROTON" "$OTFORCEPROTON" "$STLGAMECFG"
					touch "$FUPDATE"
					updateConfigEntry "OTSLR" "$OTSLR" "$STLGAMECFG"

					# Only write out OTRUNDIR if the path, and is not the current script working directory (default path already) AND if USEEXEXIR is false (USEEXEDIR overrides OTRUNDIR)
					CFGOTRUNDIR="DUMMY"
					if [ "$USEEXEDIR" -eq 0 ] && [ -d "$OTRUNDIR" ] && [ "$OTRUNDIR" != "$( pwd )" ]; then
						CFGOTRUNDIR="$OTRUNDIR"
					fi
					touch "$FUPDATE"
					updateConfigEntry "OTRUNDIR" "$CFGOTRUNDIR" "$STLGAMECFG"
				fi

				# Run in subshell to avoid messing with current script paths
				if [ "$ISWINDOWSEXE" -eq 1 ]; then
					if [ "$OTSLR" -eq 1 ]; then
						# Get SLR for given OTR Proton
						OTSLRPROT="$( getProtNameFromPath "$RUNOTPROTON" )"
						setNonGameSLRReap "1" "$OTSLRPROT"
					fi

					NOTYPROTNAME="$( basename "$( dirname "$RUNOTPROTON" )" )"  # NOTE: This will need updated if we allow Wine as the path will be different!

					if [ -n "${SLRCMD[*]}" ] && [ "$OTSLR" -eq 1 ]; then  # Use SLR
						writelog "INFO" "${FUNCNAME[0]} - Starting '$OTEXE' with '$RUNOTPROTON' with STEAM_COMPAT_DATA_PATH '$STEAM_COMPAT_DATA_PATH' and using working directory '$OTRUNDIR' and using the Steam Linux Runtime"
						writelog "INFO" "${FUNCNAME[0]} - cd \"$OTRUNDIR\" && STEAM_COMPAT_CLIENT_INSTALL_PATH=\"${SROOT}\" STEAM_COMPAT_DATA_PATH=\"$STEAM_COMPAT_DATA_PATH\" \"${SLRCMD[*]}\" \"$RUNOTPROTON\" run \"$OTEXE\" \"${RUNOTARGS[*]}\""

						notiShow "$( strFix "$NOTY_OTRSTARTSLR" "$( basename "$OTEXE" )" "$NOTYPROTNAME" )"

						(cd "$OTRUNDIR" && STEAM_COMPAT_CLIENT_INSTALL_PATH="$SROOT" STEAM_COMPAT_DATA_PATH="$STEAM_COMPAT_DATA_PATH" "${SLRCMD[@]}" "$RUNOTPROTON" run "$OTEXE" "${RUNOTARGS[@]}")
					else  # No SLR
						writelog "INFO" "${FUNCNAME[0]} - Starting '$OTEXE' with '$RUNOTPROTON' with STEAM_COMPAT_DATA_PATH '$STEAM_COMPAT_DATA_PATH' and using working directory '$OTRUNDIR'"
						writelog "INFO" "${FUNCNAME[0]} - cd \"$OTRUNDIR\" && STEAM_COMPAT_CLIENT_INSTALL_PATH=\"${SROOT}\" STEAM_COMPAT_DATA_PATH=\"$STEAM_COMPAT_DATA_PATH\" \"$RUNOTPROTON\" run \"$OTEXE\" \"${RUNOTARGS[*]}\""

						notiShow "$( strFix "$NOTY_OTRSTART" "$( basename "$OTEXE" )" "$NOTYPROTNAME" )"

						(cd "$OTRUNDIR" && STEAM_COMPAT_CLIENT_INSTALL_PATH="$SROOT" STEAM_COMPAT_DATA_PATH="$STEAM_COMPAT_DATA_PATH" "$RUNOTPROTON" run "$OTEXE" "${RUNOTARGS[@]}")
					fi
				else  # Native Linux
					if [ "$OTSLR" -eq 1 ]; then
						setNonGameSLRReap
					fi

					if [ -n "${SLRCMD[*]}" ] && [ "$OTSLR" -eq 1 ]; then  # Use SLR
						writelog "INFO" "${FUNCNAME[0]} - Starting Native Linux program '$OTEXE' using working directory '$OTRUNDIR' and the Steam Linux Runtime"
						writelog "INFO" "${FUNCNAME[0]} - cd \"$OTRUNDIR\" && \"${SLRCMD[*]}\" \"$OTEXE\" \"${RUNOTARGS[*]}\""

						notiShow "$( strFix "$NOTY_OTRSTARTNATIVESLR" "$( basename "$OTEXE" )" )"

						(cd "$OTRUNDIR" && "${SLRCMD[@]}" "$OTEXE" "${RUNOTARGS[@]}")
					else  # No SLR
						writelog "INFO" "${FUNCNAME[0]} - Starting Native Linux program '$OTEXE' using working directory '$OTRUNDIR'"
						writelog "INFO" "${FUNCNAME[0]} - cd \"$OTRUNDIR\" && \"$OTEXE\" \"${RUNOTARGS[*]}\""

						notiShow "$( strFix "$NOTY_OTRSTARTNATIVE" "$( basename "$OTEXE" )" )"

						(cd "$OTRUNDIR" && "$OTEXE" "${RUNOTARGS[@]}")
					fi
				fi
			else
				writelog "ERROR" "${FUNCNAME[0]} - Could not find valid Proton to launch custom executable with"
				notiShow "$( strFix "$NOTY_OTRPROTINVALID" "$OTPROTON" )"
				echo "Could not find valid Proton to launch custom executable with ('$OTPROTON') -- Is it definitely installed?"
			fi
		else
			writelog "WARN" "${FUNCNAME[0]} - Working directory '$OTRUNDIR' is no valid directory -- Cannot continue"
			notiShow "$( strFix "$NOTY_OTRRUNDIRINVALID" "$OTRUNDIR" )"
			echo "Working directory '$OTRUNDIR' doesn't appear to be valid -- Does it definitely exist and have correct permissions?"

		fi
	else
		writelog "ERROR" "${FUNCNAME[0]} - One-Time Run command '$OTEXE' is not valid -- Cannot continue"
		if [ -z "$OTEXE" ]; then
			notiShow "$NOTY_OTREXEBLANK" "X"
			echo "Selected One-Time Run executable appears to be blank: '$OTEXE'"
		else
			notiShow "$( strFix "$NOTY_OTREXEINVALID" "$OTEXE" )"
			echo "Selected One-Time Run executable '$OTEXE' doesn't appear to be valid."
		fi
	fi
}


function OneTimeRunGui {
	setOneTimeRunVars "$1"
	createProtonList X
	export CURWIKI="$PPW/One-Time-Run"
	TITLE="${PROGNAME}-${FUNCNAME[0]}"
	pollWinRes "$TITLE"
	setShowPic
	if [ -z "$OTPROTON" ]; then
		OTPROTON="$USEPROTON"
	fi

	# TODO GUI looks weird because of uneven number of checkboxes, but this will be fixed once we add a checkbox for Steam Linux Runtime as well
	OTCMDS="$("$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
		--title="$TITLE" --separator="|" \
		--columns="2" \
		--text="$(spanFont "$GUI_ONETIMERUN" "H")" \
		--field="$GUI_OTPROTON!$DESC_OTPROTON":CB "$(cleanDropDown "${OTPROTON//\"}" "$PROTYADLIST")" \
		--field="$GUI_OTEXE!$DESC_OTEXE":FL "${OTEXE//\"}" \
		--field="$GUI_OTARGS!$DESC_OTARGS" "${OTARGS//\"}" \
		--field="$GUI_OTRFORCEPROTON!$DESC_OTRFORCEPROTON":CHK "$OTFORCEPROTON" \
		--field="$BUT_RUNWINECFG!$DESC_RUNWINECFG":FBTN "$( realpath "$0" ) runwinecfg" \
		--field="$GUI_OTRCUSTWORKINGDIR!$DESC_OTRCUSTWORKINGDIR":DIR "$OTRUNDIR" \
		--field="$GUI_OTRUSEEXEDIR!$DESC_OTRUSEEXEDIR":CHK "$OTUSEEXEDIR" \
		--field="$GUI_OTSAVE!$DESC_OTSAVE":CHK "FALSE" \
		--field="$GUI_OTRSLR!$DESC_OTRSLR":CHK "$OTSLR" \
		--field="$BUT_RUNWINETRICKS!$DESC_RUNWINETRICKS":FBTN "$( realpath "$0" ) runwinetricks" \
		--button="$BUT_CAN:0" \
		--button="$BUT_DGM:2" \
		--button="$BUT_RUNONETIMECMD:4" \
		"$GEOM"
	)"
	case $? in
		# Selected Cancel
		0)	{
				writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_CAN' - Exiting"
			}
		;;
		2) {
				writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_DGM' - Resetting One-Time Run values"
				OneTimeRunReset
				OneTimeRunGui
		   }
		;;
		# Selected Run
		4)	{
				mapfile -d "|" -t -O "${#OTARR[@]}" OTARR < <(printf '%s' "$OTCMDS")

				OTPROTON="${OTARR[0]}"
				OTEXE="${OTARR[1]}"
				OTARGS="${OTARR[2]}"
				OTFORCEPROTON="${OTARR[3]}"
				# OTARR[4] and OTARR[8] are the WINECFG and WINETRICKS buttons
				OTCUSTWORKDIR="${OTARR[5]}"
				OTUSEEXEDIR="${OTARR[6]}"
				OTSAVE="${OTARR[7]}"
				OTSLR="${OTARR[8]}"

				writelog "INFO" "${FUNCNAME[0]} - OTPROTON is '$OTPROTON'"
				writelog "INFO" "${FUNCNAME[0]} - OTEXE is '$OTEXE'"
				writelog "INFO" "${FUNCNAME[0]} - OTARGS is '$OTARGS'"
				writelog "INFO" "${FUNCNAME[0]} - OTFORCEPROTON is '$OTFORCEPROTON'"
				writelog "INFO" "${FUNCNAME[0]} - OTCUSTWORKDIR is '$OTCUSTWORKDIR'"
				writelog "INFO" "${FUNCNAME[0]} - OTUSEEXEDIR is '$OTUSEEXEDIR'"
				writelog "INFO" "${FUNCNAME[0]} - OTSAVE is '$OTSAVE'"
				writelog "INFO" "${FUNCNAME[0]} - OTSLR is '$OTSLR'"

				OTR_FLAGARGS=( --exe="$OTEXE" --proton="$OTPROTON" --args="$OTARGS" )  # Default arguments that we'll always pass (Proton will be ignored if not Windows executable, commandlineOneTimeRun figures that bit out)

				# USEEXEDIR will take priority over custom working dir, cannot use both at the same time either
				# Default is script working directory i.e. pwd
				if [ "$OTUSEEXEDIR" == "TRUE" ]; then
					OTR_FLAGARGS+=( --useexedir )
				elif [ -n "$OTCUSTWORKDIR" ]; then
					OTR_FLAGARGS+=( --workingdir="$OTCUSTWORKDIR" )
				fi

				if [ "$OTFORCEPROTON" == "TRUE" ]; then
					OTR_FLAGARGS+=( --forceproton )
				fi

				if [ "$OTSLR" == "TRUE" ]; then
					OTR_FLAGARGS+=( --useslr )
				fi

				if [ "$OTSAVE" == "TRUE" ]; then
					OTR_FLAGARGS+=( --save )
				fi

				commandlineOneTimeRun "$1" "${OTR_FLAGARGS[@]}"
			}
		;;
	esac
}

function setOPCustPath {
	if [ -z "$CUSTOMCMD" ] || [[ "$CUSTOMCMD" =~ ${DUMMYBIN}$ ]]; then
		OPCUSTPATH="$GP"
	fi

	if [ -z "$OPCUSTPATH" ]; then
		OPCUSTPATH="$CUSTOMCMD"
	fi

	if [ -n "$OPCUSTPATH" ]; then
		export OPCUSTPATH="$OPCUSTPATH"
		writelog "INFO" "${FUNCNAME[0]} - Default path for custom exe file requester is '$OPCUSTPATH'"
	fi
}

# Build a string like 'export ENABLE_VKBASALT=1' and evaluate that string as code
# Allows us to more flexibly enable vkBasalt forks in future like vkShade
function setVulkanPostProcessor {
	if [ ! "$VULKANPOSTPROCESSOR" = "$NON" ]; then
		VULKANPOSTPROCESSOREXPORTVAR="ENABLE_${VULKANPOSTPROCESSOR^^}"

		writelog "INFO" "${FUNCNAME[0]} - Enabling Vulkan Post-Processor '$VULKANPOSTPROCESSOR' with environment with '$VULKANPOSTPROCESSOREXPORTVAR'"
		eval "export ${VULKANPOSTPROCESSOREXPORTVAR}=1"
	fi
}

function checkWinesync {
	if [ "$ENABLE_WINESYNC" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Enabling Winesync variables because 'ENABLE_WINESYNC' is '$ENABLE_WINESYNC'"
		export WINEESYNC=0
		export WINEFSYNC=0
		export WINEFSYNC_FUTEX2=0
	fi
}

function checkPrimerun {
	if [ "$USEPRIMERUN" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Enabling Primerun variables because 'USEPRIMERUN' is '$USEPRIMERUN'"
		export __NV_PRIME_RENDER_OFFLOAD=1
		export __VK_LAYER_NV_optimus=NVIDIA_only
		export __GLX_VENDOR_LIBRARY_NAME=nvidia
		export VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/nvidia_icd.json
	fi
}

function checkZink {
	if [ "$USEZINK" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Enabling Zink variables because 'USEZINK' is '$USEZINK'"
		export __GLX_VENDOR_LIBRARY_NAME=mesa
		export MESA_LOADER_DRIVER_OVERRIDE=zink
		export GALLIUM_DRIVER=zink
	fi
}

function setCommandLaunchVars {
	if [ "$USEGAMEMODERUN" -eq 1 ]; then
		GMR="$(command -v "$GAMEMODERUN")"
	fi

	if [ "$USEOBSCAP" -eq 1 ]; then
		OBSC="$(command -v "$OBSCAP")"
	fi

	if [ "$USEGAMESCOPE" -eq 1 ]; then
		if [ "$USEMANGOAPP" -eq 1 ]; then
			if [ "$ONSTEAMDECK" -eq 1 ]; then
				if [ "$FIXGAMESCOPE" -eq 1 ]; then
					writelog "SKIP" "${FUNCNAME[0]} - Disabling USEMANGOAPP variable in Steam Deck Game Mode, because Steam Deck uses $MANGOAPP already by default"
					USEMANGOAPP=0
				else
					writelog "INFO" "${FUNCNAME[0]} - Allowing USEMANGOAPP variable in Steam Deck Desktop Mode"
					USEMANGOAPP=1
				fi
			else
				writelog "SKIP" "${FUNCNAME[0]} - Not adding $GAMESCOPE to the game launch command, because $MANGOAPP is enabled, which triggers it automatically"
				USEGAMESCOPE=0
			fi
		else
			# Enable the ENABLE_GAMESCOPE_WSI environment variable if checkbox is enabled on GameScopeGui
			if [ "$USEGAMESCOPEWSI" -eq 1 ]; then
				export ENABLE_GAMESCOPE_WSI=1
			fi

			GSC="$(command -v "$GAMESCOPE")"

			gameScopeArgs "$GAMESCOPE_ARGS"  # Create GameScope args array - Is called twice because we call `setCommandLaunchVars` above and in `buildCustomCmdLaunch` it seems
		fi
	fi

	# NOTE: Primerun and Zink both set ' __GLX_VENDOR_LIBRARY_NAME', so Zink has to go after Primerun as shown to activate correctly
	checkWinesync
	checkPrimerun
	checkZink

	# This could be expanded in future as a general option to force Wayland for games/engines that support it, e.g. '-wayland' flag for unity
	if [ "$SDLUSEWAYLAND" -eq 1 ]; then
		export SDL_VIDEODRIVER=wayland
	fi

	if [ -n "$STLRAD_PFTST" ] && [ "$STLRAD_PFTST" != "none" ]; then
		writelog "INFO" "${FUNCNAME[0]} - STLRADV_PFTST is not empty or none - Exporting RADV_PERFTEST=$STLRAD_PFTST"
		export RADV_PERFTEST=$STLRAD_PFTST
	fi

	setVulkanPostProcessor
	setWineDpiScaling
}

# Used to create the launch command for games and custom commands so they can use various program functions i.e. GameScope
function buildCustomCmdLaunch {
	setCommandLaunchVars  # Checks for things like GameMode, GameScope, etc
	FINALOUTCMD=()

	# Lifted originally from `launchSteamGame`
	if [ -n "$GMR" ]; then
		if [ -n "${FINALOUTCMD[0]}" ]; then
			FINALOUTCMD=("${FINALOUTCMD[@]}" "$GMR")
		else
			FINALOUTCMD=("$GMR")
		fi
	fi

	# GameScope has to be appended before other commands
	if [ -n "$GSC" ]; then
		if [ -n "${FINALOUTCMD[0]}" ]; then
			FINALOUTCMD=("${FINALOUTCMD[@]}" "$GSC")
		else
			FINALOUTCMD=("$GSC")
		fi

		if [ -n "${FINALOUTCMD[0]}" ]; then
			FINALOUTCMD=("${FINALOUTCMD[@]}" "${GAMESCOPEARGSARR[@]}")
		fi
	fi

	# OBS capture has to go after GameScope
	if [ "$USEOBSCAP" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - USEOBSCAP is enabled - preparing $OBSCAP command"
		if [ -n "${FINALOUTCMD[0]}" ]; then
			FINALOUTCMD=("${FINALOUTCMD[@]}" "$OBSC")
		else
			FINALOUTCMD=("$OBSC")
		fi
	fi

	# MangoHud has to go inside GameScope
	if [ "$USEMANGOHUD" -eq 1 ]; then
		if [ "$MAHUARGS" != "$NON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Exporting MANGOHUD_CONFIG with $MAHU arguments '$MAHUARGS'"
			export MANGOHUD_CONFIG="$MAHUARGS"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Not exporting  MANGOHUD_CONFIG '$MAHUARGS'"
		fi

		if [ "$MAHUVAR" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Exporting MANHOGUD variable and skipping all $MAHU binary settings, because MAHUVAR is '$MAHUVAR'"
			export MANGOHUD=1
		else
			if [ -f "$MAHUBIN" ]; then
				writelog "INFO" "${FUNCNAME[0]} - $MAHU is enabled"
				if [ "$LDPMAHU" -eq 1 ]; then
					writelog "INFO" "${FUNCNAME[0]} - Preloading $MAHU"
					export LD_PRELOAD="$LD_PRELOAD $MAHUBIN"
				else
					if [ -n "${FINALOUTCMD[0]}" ]; then
						FINALOUTCMD=("${FINALOUTCMD[@]}" "$MAHUBIN")
					else
						FINALOUTCMD=("$MAHUBIN")
					fi

					# Append '--dlsym' to mangohud command -- There is also the MANGOHUD_DLSYM=1 env var but this can be set manually, and does not always work
					# i.e. Torchlight II works with '--dlsym' but not 'MANGOHUD_DLSYM=1'
					if [ "$MAHUDLSYM" -eq 1 ]; then
						FINALOUTCMD=("${FINALOUTCMD[@]}" "--dlsym")
					fi
				fi
			else
				writelog "WARN" "${FUNCNAME[0]} - $MAHU binary not found - disabling"
				USEMANGOHUD=0
			fi
		fi
	fi
}

function launchCustomProg {
	if [ -n "$1" ]; then
		CUSTOMCMD="$WICO"
	fi

	if [ -z "$CUSTOMCMD" ] || [[ "$CUSTOMCMD" =~ ${DUMMYBIN}$ ]]; then
		writelog "INFO" "${FUNCNAME[0]} - CUSTOMCMD variable is empty - opening file requester"
		fixShowGnAid

		export CURWIKI="$PPW/Custom-Program"
		TITLE="${PROGNAME}-OpenCustomProgram"
		pollWinRes "$TITLE"

		ZCUST="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
		--title="$TITLE" \
		--text="$(spanFont "$SGNAID - $GUI_SELECTCUSTOMEXE" "H")" \
		--field=" ":LBL " " \
		--field="$GUI_SELECTEXE":FL "${OPCUSTPATH/#-/ -}" "$GEOM")"

		if [ -n "$ZCUST" ]; then
			writelog "INFO" "${FUNCNAME[0]} - '${ZCUST//|/}' selected for CUSTOMCMD - updating configfile '$STLGAMECFG'"
			updateConfigEntry "CUSTOMCMD" "${ZCUST//|/}" "$STLGAMECFG"

			CUSTOMCMD="${ZCUST//|/}"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Nothing selected for CUSTOMCMD - skipping"
			if [ "$ONLY_CUSTOMCMD" -eq 1 ]; then
				writelog "SKIP" "${FUNCNAME[0]} - ONLY_CUSTOMCMD is enabled - bailing out here"
				closeSTL " ######### STOP EARLY '$PROGNAME $PROGVERS' #########"
				exit
			else
				writelog "SKIP" "${FUNCNAME[0]} - Continuing with the main game"
				return
			fi
		fi
	fi

	if [ -z "$CUSTOMCMD" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - CUSTOMCMD variable is empty - but it shouldn't be empty here!"
	fi

	CHCUSTDIR=0
	if [ -n "$1" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using '$WICO' as custom command"
		LACO="$WICO"
		CUSTCOM="$WICO"
	elif [ -x "$(command -v "$CUSTOMCMD")" ]; then
		writelog "INFO" "${FUNCNAME[0]} - '$CUSTOMCMD' found"
		LACO="$CUSTOMCMD"
		CUSTCOM="$(command -v "$CUSTOMCMD")"
		CHCUSTDIR=1
	else
		writelog "INFO" "${FUNCNAME[0]} - '$CUSTOMCMD' not found - searching in gamedir"

		if [ -f "$EFD/$CUSTOMCMD" ]; then
			writelog "INFO" "${FUNCNAME[0]} - '$CUSTOMCMD' was found in gamedir '$EFD'"
			LACO="$EFD/$CUSTOMCMD"
			CUSTCOM="$EFD/$CUSTOMCMD"
		else
			writelog "INFO" "${FUNCNAME[0]} - '$CUSTOMCMD' also not in '$EFD/$CUSTOMCMD' - checking if absolute path was provided"

			if [ -f "$CUSTOMCMD" ]; then
				writelog "INFO" "${FUNCNAME[0]} - '$CUSTOMCMD' is absolute path"
				LACO="$CUSTOMCMD"
				CUSTCOM="$CUSTOMCMD"
				CHCUSTDIR=1
			else
				writelog "INFO" "${FUNCNAME[0]} - CUSTOMCMD file '$CUSTOMCMD' not found - opening file requester"
				fixShowGnAid
				export CURWIKI="$PPW/Custom-Program"
				TITLE="${PROGNAME}-OpenCustomProgram"
				pollWinRes "$TITLE"

				ZCUST="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
				--title="$TITLE" \
				--text="$(spanFont "$SGNAID - $GUI_SELECTCUSTOMEXE" "H")" \
				--field=" ":LBL " " \
				--field="$GUI_SELECTEXE":FL "${OPCUSTPATH/#-/ -}" "$GEOM")"

				if [ -n "$ZCUST" ]; then
					writelog "INFO" "${FUNCNAME[0]} - '${ZCUST//|/}' selected for CUSTOMCMD - updating configfile '$STLGAMECFG'"
					updateConfigEntry "CUSTOMCMD" "${ZCUST//|/}" "$STLGAMECFG"
					LACO="${ZCUST//|/}"
					CUSTCOM="${ZCUST//|/}"
					CUSTOMCMD="${ZCUST//|/}"
					CHCUSTDIR=1
				else
					writelog "SKIP" "${FUNCNAME[0]} - Nothing selected for CUSTOMCMD - skipping"
					if [ "$ONLY_CUSTOMCMD" -eq 1 ]; then
						writelog "SKIP" "${FUNCNAME[0]} - ONLY_CUSTOMCMD is enabled - bailing out here"
						closeSTL " ######### STOP EARLY $PROGNAME $PROGVERS #########"
						exit
					else
						writelog "SKIP" "${FUNCNAME[0]} - Continuing with the main game"
						return
					fi
				fi
			fi
		fi
	fi

	if [ -z "$LACO" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - ERROR - launch command empty- skipping launch"
	else
		startSBSVR &

		if [ "$CHCUSTDIR" -eq 1 ]; then
			CUSTDIR="$(dirname "$CUSTOMCMD")"
			cd "$CUSTDIR" >/dev/null || return
			writelog "INFO" "${FUNCNAME[0]} - Changed pwd into the custom directory '$PWD'"
		fi

		# Putting logic here means we have FINALOUTCMD for Wine/Proton *and* native games
		if [ "$EXTPROGS_CUSTOMCMD" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - EXTPROGS_CUSTOMCMD was set to 1, so checking for custom arguments like MangoHUD and GameScope"
			buildCustomCmdLaunch  # Create FINALOUTCMD
			writelog "INFO" "${FUNCNAME[0]} - Generated arguments for custom command are now '${FINALOUTCMD[*]}'"
		fi

		# Use Wine/Proton with a custom command if it is detected as a Windows executable, a batch script (.bat), a Windows shortcut (.lnk), or if the user forces Proton
		# Cannot always rely on `file` to return `PE32` even for Windows executables -- See #710 (seems to be when exe files are built on Linux they are not PE32)
		if [ "$(file "$CUSTCOM" | grep -c "PE32")" -eq 1 ] || grep -q ".bat" <<< "$CUSTCOM" || grep -q ".lnk" <<< "$CUSTCOM" || [ "$CUSTOMCMDFORCEWIN" -eq 1 ]; then
			if [ "$CUSTOMCMDFORCEWIN" -eq 1 ]; then
				# Force custom command to use Wine/Proton, even if we do not detect it as a valid Windows binary
				writelog "INFO" "${FUNCNAME[0]} - CUSTOMCMDFORCEWIN is '$CUSTOMCMDFORCEWIN' - User wants to force this custom command as a Windows program"
				if [ "$(file "$CUSTCOM" | grep -c "PE32")" -eq 0 ]; then
					writelog "WARN" "${FUNCNAME[0]} - Custom command does not appear to be a Windows program by normal SteamTinkerLaunch checks, but CUSTOMCMDFORCEWIN was enabled, so using Proton anyway"
				else
					writelog "INFO" "${FUNCNAME[0]} - Custom command seems to be a Windows program anyway even though CUSTOMCMDFORCEWIN was enabled"
				fi
			else
				writelog "INFO" "${FUNCNAME[0]} - '$CUSTCOM' seems to be a MS Windows program - starting through proton"
			fi

			if [ "$USEWICO" -eq 1 ] && [ "$(file "$CUSTCOM" | grep -c "(console)")" -eq 1 ]; then  # Command line Wine/Proton custom program
				writelog "INFO" "${FUNCNAME[0]} - '$CUSTCOM' seems to be a MS console program - starting using '$WICO'"
				if [ "$FORK_CUSTOMCMD" -eq 1 ]; then
					writelog "INFO" "${FUNCNAME[0]} - FORK_CUSTOMCMD is set to 1 - forking the custom program in background and continue"
					extProtonRun "FC" "$LACO" "$CUSTOMCMD_ARGS" "" "${CUSTOMCMD_USESLR}"
				elif [ "$ONLY_CUSTOMCMD" -eq 1 ] && [ -n "${FINALOUTCMD[*]}" ]; then
					writelog "INFO" "${FUNCNAME[0]} - ONLY_CUSTOMCMD is set to 1 and we have some arguments in FINALOUTCMD - passing to extProtonRun to build a valid start command"
					extProtonRun "R" "$LACO" "$CUSTOMCMD_ARGS" "$FINALOUTCMD" "${CUSTOMCMD_USESLR}"  # extProtonRun will handle adding the FINALOUTCMD args to
				else
					extProtonRun "RC" "$LACO" "$CUSTOMCMD_ARGS" "" "${CUSTOMCMD_USESLR}"
				fi
			else  # GUI Wine/Proton program
				writelog "INFO" "${FUNCNAME[0]} - '$CUSTCOM' seems to be a MS gui program - starting regularly"

				if [ "$FORK_CUSTOMCMD" -eq 1 ]; then
					writelog "INFO" "${FUNCNAME[0]} - FORK_CUSTOMCMD is set to 1 - forking the custom program in background and continue"
					extProtonRun "F" "$LACO" "$CUSTOMCMD_ARGS" "" "${CUSTOMCMD_USESLR}"
				elif [ "$ONLY_CUSTOMCMD" -eq 1 ] && [ -n "$FINALOUTCMD" ]; then
					writelog "INFO" "${FUNCNAME[0]} - ONLY_CUSTOMCMD is set to 1 and we have some arguments in FINALOUTCMD - passing to extProtonRun to build a valid start command"
					extProtonRun "R" "$LACO" "$CUSTOMCMD_ARGS" "${FINALOUTCMD[*]}" "${CUSTOMCMD_USESLR}" "${CUSTOMCMD_USESLR}"  # extProtonRun will handle adding the FINALOUTCMD args
				else
					extProtonRun "R" "$LACO" "$CUSTOMCMD_ARGS" "" "${CUSTOMCMD_USESLR}"
				fi
			fi
		else  # Native custom command
			writelog "INFO" "${FUNCNAME[0]} - Seems like we may have a Linux executable here"

			# Arguments to append to executable
			if [ -z "$CUSTOMCMD_ARGS" ] || [ "$CUSTOMCMD_ARGS" == "$NON" ]; then
				RUNCUSTOMCMD_ARGS=""
			else
				writelog "INFO" "${FUNCNAME[0]} - Starting the custom program '$CUSTOMCMD' with args: '$CUSTOMCMD_ARGS'"
				mapfile -d " " -t -O "${#RUNCUSTOMCMD_ARGS[@]}" RUNCUSTOMCMD_ARGS < <(printf '%s' "$CUSTOMCMD_ARGS")
			fi

			# Custom program args to preceed executable (e.g. /usr/bin/gamemode /usr/bin/gamescope -- ./game.sh)
			if [ -z "$FINALOUTCMD" ]; then
				writelog "INFO" "${FUNCNAME[0]} - No external program args here it seems"
				RUNEXTPROGRAMARGS=( "" )  # Initialise to array with empty string so we don't have to do checks in each if block
			else
				writelog "INFO" "${FUNCNAME[0]} - Looks like we got some external program args, '${FINALOUTCMD[*]}'"
				mapfile -d " " -t -O "${#RUNEXTPROGRAMARGS[@]}" RUNEXTPROGRAMARGS < <(printf '%s' "${FINALOUTCMD[*]}")
			fi

			FWAIT=2

			# TODO should respect selected SLR once #1087 is implemented
			if [ "$CUSTOMCMD_USESLR" -eq 1 ]; then
				unset "${SLRCMD[@]}"

				writelog "INFO" "${FUNCNAME[0]} - Steam Linux Runtime enabled, attempting to fetch Steam Linux Runtime for native Custom Command"
				# "2" is the FORCESLRTYPE, meaning we want to force to get the native SLR -- We do this in case we are trying to launch a native custom command with a Proton title
				# In this case, setSLRReap is going to have the Proton SLR vars set from the game launch, so we need to force it here to use the native SLR
				setNonGameSLRReap "2"
			fi

			# TODO this is the exact same logic as in extProtonRun (except the log messages are slightly different), is there any way to share it?
			if [ -n "${SLRCMD[*]}" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Gotten Steam Linux Runtime for native launch, using RUNEXTPROGRAMARGS array to contain it and add it to launch command"

				OLDRUNEXTPROGRAMARGS=( "${RUNEXTPROGRAMARGS[@]}" )

				unset "${RUNEXTPROGRAMARGS[@]}"
				RUNEXTPROGRAMARGS=( "${SLRCMD[@]}" )

				# OLDRUNEXTPROGRAMARGS should only contain one item, the passed args for the custom command
				# if the first item here is not empty, assume we have to include the old pass args in the new array
				#
				# if blank, it means OLDRUNEXTPROGRAMARGS was most likely empty (or started with a blank element, which would cause a crash anyway)
				# so we can just create RUNEXTPROGRAMARGS with the SLR as the only element
				if [ -n "${OLDRUNEXTPROGRAMARGS[0]}" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Seems like some arguments were given to the custom command, including them alongside the Steam Linux Runtime arguments"
					RUNEXTPROGRAMARGS+=( "${OLDRUNEXTPROGRAMARGS[@]}" )
				fi
			elif [ -z "${SLRCMD[*]}" ] && [ "$CUSTOMCMD_USESLR" -eq 1 ]; then
				writelog "WARN" "${FUNCNAME[0]} - Attempted to fetch Steam Linux Runtime but failed to find one!"
			fi
			unset "${SLRCMD[@]}"

			# Launch native custom command
			NATIVEPROGNAME="$( basename "$LACO" )"
			if [ -n "$1" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Starting only the '$WICO' with command: '$LACO'"
				"${RUNEXTPROGRAMARGS[@]}" "$LACO"
				writelog "STOP" "######### CLEANUP #########"
				closeSTL "######### DONE - $PROGNAME $PROGVERS #########"
				exit
			else
				writelog "INFO" "${FUNCNAME[0]} - '$CUSTCOM' doesn't seem to be a MS Windows exe - regular start (without further analysing)"
				if [ "$FORK_CUSTOMCMD" -eq 1 ]; then  # Forked native custom program
					writelog "INFO" "${FUNCNAME[0]} - FORK_CUSTOMCMD is set to 1 - forking the custom program in background and continue"
					if [ -n "${RUNEXTPROGRAMARGS[0]}" ]; then
						writelog "INFO" "${FUNCNAME[0]} - \"${RUNEXTPROGRAMARGS[*]}\" \"${LACO}\" \"${RUNCUSTOMCMD_ARGS[*]}\""
						(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED_NATIVE" "$NATIVEPROGNAME" )"; "${RUNEXTPROGRAMARGS[@]}" "$LACO" "${RUNCUSTOMCMD_ARGS[@]}" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log") &
					else
						writelog "INFO" "${FUNCNAME[0]} - \"${LACO}\" \"${RUNCUSTOMCMD_ARGS[*]}\""
						(sleep "$FWAIT"; notiShow "$( strFix "$NOTY_CUSTPROG_FORKED_NATIVE" "$NATIVEPROGNAME" )"; "$LACO" "${RUNCUSTOMCMD_ARGS[@]}" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log") &
					fi
				else  # Regular native executable
					writelog "INFO" "${FUNCNAME[0]} - Starting native custom command regularly"
					notiShow "$( strFix "$NOTY_CUSTPROG_REG_NATIVE" "$NATIVEPROGNAME" )"
					if [ -n "${RUNEXTPROGRAMARGS[0]}" ]; then
						writelog "INFO" "${FUNCNAME[0]} - \"${RUNEXTPROGRAMARGS[*]}\" \"${LACO}\" \"${RUNCUSTOMCMD_ARGS[*]}\""
						"${RUNEXTPROGRAMARGS[@]}" "$LACO" "${RUNCUSTOMCMD_ARGS[@]}" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
					else
						writelog "INFO" "${FUNCNAME[0]} - \"${LACO}\" \"${RUNCUSTOMCMD_ARGS[*]}\""
						"$LACO" "${RUNCUSTOMCMD_ARGS[@]}" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
					fi
				fi
			fi
		fi

		if [ "$CHCUSTDIR" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Changing pwd into previous directory"
			cd - >/dev/null || return
		fi
	fi
}

function getGameFiles {
	AID="$1"

	if [ -z "$APPMAFE" ]; then
		APPMAFE="$(listAppManifests | grep -m1 "${AID}.acf")"
	fi

	if [ -f "$APPMAFE" ]; then
		if [ -z "$GPFX" ]; then
			GPFX="$(dirname "$APPMAFE")/$CODA/$1/pfx"
		fi

		if [ -z "$EFD" ]; then
			EFD="$(getGameDirFromAM "$APPMAFE")"
		fi

		if [ -z "$STECOSHAPA" ]; then
			STECOSHAPA="${APPMAFE%/*}/shadercache/$AID"
		fi
	fi
}

function createCustomCfgs {
	if [ ! -f "$GAMECUSTVARS" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Creating emtpy game-specific config '$GAMECUSTVARS' for user defined custom variables"
		touch "$GAMECUSTVARS"
	fi

	if [ ! -f "$GLOBCUSTVARS" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Creating emtpy global config '$GLOBCUSTVARS' for user defined custom variables"
		touch "$GLOBCUSTVARS"
	fi

}

function loadCustomVars {
	if [ -s "$GLOBCUSTVARS" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Loading user defined custom variables from global config '$GLOBCUSTVARS'"
		loadCfg "$GLOBCUSTVARS"
	else
		writelog "INFO" "${FUNCNAME[0]} - Empty global config '$GLOBCUSTVARS' for user defined custom variables not loaded"
	fi

	if [ -s "$GAMECUSTVARS" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Loading user defined custom variables from game-specific config '$GAMECUSTVARS'"
		loadCfg "$GAMECUSTVARS"
	else
		writelog "INFO" "${FUNCNAME[0]} - Empty game-specific config '$GAMECUSTVARS' for user defined custom variables not loaded"
	fi
}

function setGameFilesArray {
	unset GamDesc
	unset GamFiles
	if [ "$EFD" != "." ] && [ -d "$EFD" ]; then
		GamDesc+=("$GUI_GD")
		GamFiles+=("$EFD")
	fi

	if [ -d "$GPFX" ]; then
		GamDesc+=("$GUI_WP")
		GamFiles+=("$GPFX")
	fi

	if [ -f "$APPMAFE" ]; then
		GamDesc+=("$GUI_AM")
		GamFiles+=("$APPMAFE")
	fi

	if [ -d "$STECOSHAPA" ]; then
		GamDesc+=("$GUI_SP")
		GamFiles+=("$STECOSHAPA")
	fi

	if [ -n "$STLDXVKCFG" ]; then
		GamDesc+=("$GUI_DXVKCFG")
		GamFiles+=("$STLDXVKCFG")
	fi

	if [ -f "$MAHUCID/${AID}.conf" ]; then
		GamDesc+=("$GUI_MANGOHUDGAMECFG")
		GamFiles+=("$MAHUCID/${AID}.conf")
	fi

	# Open /dev/shm/steamtinkerlaunch directory
	if [ -d "$STLSHM" ]; then
		GamDesc+=("$GUI_STLSHMDIR")
		GamFiles+=("$STLSHM")
	fi

	# Open logfile at /dev/shm/steamtinkerlaunch/steamtinkerlaunch.log
	if [ -f "$TEMPLOG" ]; then
		GamDesc+=("$GUI_STLSHMLOG")
		GamFiles+=("$TEMPLOG")
	fi

	# Open game logging directory at, by default, STLCFGDIR/logs/steamtinkerlaunch
	if [ -d "$LOGDIR" ]; then
		GamDesc+=("$GUI_STLPERGAMELOGSDIR")
		GamFiles+=("$LOGDIR")
	fi

	createCustomCfgs

	GamDesc+=("$GUI_STLCVFILE")
	GamFiles+=("$GAMECUSTVARS")

	GamDesc+=("$GUI_STLGLBCVFILE")
	GamFiles+=("$GLOBCUSTVARS")
}

function GameFilesMenu {
	if [ -n "$1" ]; then
		getGameFiles "$1"
	else
		setGameVars
	fi

	fixShowGnAid

	setGameFilesArray

	if [ "${#GamFiles[@]}" -ge 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Found ${#GamFiles[@]} available game files and directories - opening menu"
		export CURWIKI="$PPW/Game-Files"
		TITLE="${PROGNAME}-Game-Files"
		pollWinRes "$TITLE"

		setShowPic
		OPFILES="$(for i in "${!GamFiles[@]}"; do printf "FALSE\n%s\n%s\n" "${GamDesc[$i]}" "${GamFiles[$i]}"; done | \
		"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center "$WINDECO" --list --checklist --column=Open --column=Description --column=Path --separator="\n" --print-column="3" \
		--text="$(spanFont "$(strFix "$GUI_GAFIDIALOG" "$SGNAID")" "H")" --title="$TITLE" --button="$BUT_CAN:0" --button="$BUT_SELECT:2" "$GEOM")"
		case $? in
			0) writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_CAN' - Cancelling selection"
			;;
			2)
				if [ -x "$(command -v "$XDGO" 2>/dev/null)" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Selected Open - Opening selected files and directories using '$XDGO'"
					mapfile -t -O "${#OpArr[@]}" OpArr <<< "$OPFILES"
					if [ "${#OpArr[@]}" -ge 1 ]; then
						while read -r gafi; do
							if [ -n "$gafi" ]; then
								if [ "$gafi" == "$STLDXVKCFG" ] && [ ! -f "$STLDXVKCFG" ]; then
									writelog "INFO" "${FUNCNAME[0]} - Creating blank game-specific DXVK config in '$STLDXVKCFG' for user-defined DXVK configuration options"
									echo "## $(strFix "$STLDXVKCFG_WARNING" "$DXVKURL")" > "$STLDXVKCFG"
									if [ "$USE_STLDXVKCFG" -eq 0 ]; then
										writelog "INFO" "${FUNCNAME[0]} - Enabling USE_STLDXVKCFG automatically, because the user selected to open the config file '$STLDXVKCFG'"
										USE_STLDXVKCFG=1
										touch "$FUPDATE"
										updateConfigEntry "USE_STLDXVKCFG" "$USE_STLDXVKCFG" "$STLGAMECFG"
									fi
								fi
								"$XDGO" "$gafi"
							fi
						done <<< "$(printf "%s\n" "${OpArr[@]}")"
						unset OpArr
					else
						writelog "SKIP" "${FUNCNAME[0]} - Nothing selected"
					fi
				else
					writelog "SKIP" "${FUNCNAME[0]} - '$XDGO' not found - can't open the directory"
				fi
			;;
		esac
	else
		writelog "SKIP" "${FUNCNAME[0]} - Could not find any game files"
	fi
}

function DxvkHudPick {
	if [ -n "$1" ]; then
		AID="$1"
		setAIDCfgs
	fi

	if [ ! -f "$STLGAMECFG" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - Game config '$STLGAMECFG' not found - exiting"
	else
		DXVKHUDLIST="1,devinfo,fps,frametimes,submissions,drawcalls,pipelines,memory,gpuload,version,api,compiler,samplers,full"

		writelog "INFO" "${FUNCNAME[0]} - LoadCfg: $STLGAMECFG"
		loadCfg "$STLGAMECFG"

		unset CURDXH

		if [ "$DXVK_HUD" == "0" ]; then
			declare -a CURDXH
		else
			mapfile -d " " -t -O "${#CURDXH[@]}" CURDXH <<< "$(printf '%s\n' "$DXVK_HUD")"
		fi
		fixShowGnAid
		export CURWIKI="$PPW/Dxvk-Hud-Options"
		TITLE="${PROGNAME}-DXVK-Hud-Options"
		pollWinRes "$TITLE"

		setShowPic

		DXHUDOPTS="$(
		while read -r dxline; do
			if [[ "${CURDXH[*]}" =~ $dxline ]]; then
				echo TRUE
			else
				echo FALSE
			fi
			echo "$dxline"
		done <<< "$(tr ',' '\n' <<< "$DXVKHUDLIST")"	 | \
		"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center "$WINDECO" --list --checklist --column="$GUI_ADD" --column="$GUI_DXH" --separator="" --print-column="2" \
		--text="$(spanFont "$(strFix "$GUI_DXHDIALOG" "$SGNAID")" "H")" --title="$TITLE" "$GEOM")"

		if [ -n "$DXHUDOPTS" ]; then
			unset DXVK_HUD
			while read -r line; do
				DXVK_HUD="${DXVK_HUD},$line"
			done <<< "$DXHUDOPTS"
			DXVK_HUD="${DXVK_HUD#*[[:blank:]]}"
			DXVK_HUD="${DXVK_HUD#*,}"
			DXVK_HUD="${DXVK_HUD%*[[:blank:]]}"
			touch "$FUPDATE"
			updateConfigEntry "DXVK_HUD" "$DXVK_HUD" "$STLGAMECFG"
		else
			writelog "INFO" "${FUNCNAME[0]} - Nothing selected"
			touch "$FUPDATE"
			DXVK_HUD="0"
			updateConfigEntry "DXVK_HUD" "0" "$STLGAMECFG"
		fi

		if [ -n "$2" ]; then
			"$2" "$AID" "${FUNCNAME[0]}"
		fi
	fi
}

function symlinkSteamUser {

	SteamUserDir="$GPFX/$DRCU/$STUS"
	PublicUserDir="$GPFX/$DRCU/$PUBUS"

	if [ "$1" -eq 1 ]; then
		mkProjDir "$STLPROTSTUSDIR"
		mkProjDir "$STLPROTPUBUSDIR"

		if [ "$USEGLOBSUSYM" -eq 1 ]; then
			SUSYD="$STLPROTSTUSDIR/global"
            PUBUSYD="$STLPROTPUBUSDIR/global"

		else
			SUSYD="$STLPROTSTUSDIR/$AID"
            PUBUSYD="$STLPROTPUBUSDIR/$AID"
		fi

		writelog "INFO" "${FUNCNAME[0]} - Testing if creating a symlink '$SteamUserDir' pointing to '$SUSYD' is required"

		if [ ! -d "$SUSYD" ]; then
			writelog "INFO" "${FUNCNAME[0]} - '$SUSYD' does not yet exists"
			if [ -d "$SteamUserDir" ]; then
				mv "$SteamUserDir" "$SUSYD"
			else
				mkProjDir "$SUSYD"
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - '$SUSYD' does already exists"
		fi

		writelog "INFO" "${FUNCNAME[0]} - Testing if creating a symlink '$PublicUserDir' pointing to '$PUBUSYD' is required"

		if [ ! -d "$PUBUSYD" ]; then
			writelog "INFO" "${FUNCNAME[0]} - '$PUBUSYD' does not yet exists"
			if [ -d "$PublicUserDir" ]; then
				mv "$PublicUserDir" "$PUBUSYD"
			else
				mkProjDir "$PUBUSYD"
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - '$PUBUSYD' does already exists"
		fi


		if [ -L "$SteamUserDir" ]; then
			if [ "$(readlink "$SteamUserDir")" == "$SUSYD" ]; then
				writelog "INFO" "${FUNCNAME[0]} - '$SteamUserDir' is already a symlink pointing to '$SUSYD' - nothing to do"
			else
				writelog "INFO" "${FUNCNAME[0]} - '$SteamUserDir' is already a symlink pointing to the unexpected directory '$(readlink "$SteamUserDir")' instead of '$SUSYD' - nothing to do"
			fi
		else
			if [ -d "$SteamUserDir" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Syncing files from '$SteamUserDir' to '$SUSYD'"
				"$RSYNC" -amu "$SteamUserDir" "$SUSYD"
				BACKSUD="${SteamUserDir}_${PROGNAME,,}_$((900 + RANDOM % 100))"
				mv "$SteamUserDir" "$BACKSUD"
				writelog "INFO" "${FUNCNAME[0]} - Backing up '$SteamUserDir' to '$BACKSUD'"
			fi

			if [ ! -d "$SteamUserDir" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Creating '$SteamUserDir' symlink pointing to '$SUSYD' using command:"
				writelog "INFO" "${FUNCNAME[0]} - 'ln -s \"$SUSYD\" \"$SteamUserDir\"'"

				ln -s "$SUSYD" "$SteamUserDir"
			else
				writelog "SKIP" "${FUNCNAME[0]} - '$SteamUserDir' still exists - can't create a symlink pointing to '$SUSYD'"
			fi
		fi

        # Symlinking Public user directory

		if [ -L "$PublicUserDir" ]; then
			if [ "$(readlink "$PublicUserDir")" == "$PUBUSYD" ]; then
				writelog "INFO" "${FUNCNAME[0]} - '$PublicUserDir' is already a symlink pointing to '$PUBUSYD' - nothing to do"
			else
				writelog "INFO" "${FUNCNAME[0]} - '$PublicUserDir' is already a symlink pointing to the unexpected directory '$(readlink "$PublicUserDir")' instead of '$PUBUSYD' - nothing to do"
			fi
		else
			if [ -d "$PublicUserDir" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Syncing files from '$PublicUserDir' to '$PUBUSYD'"
				"$RSYNC" -amu "$PublicUserDir" "$PUBUSYD"
				BACKSUD="${PublicUserDir}_${PROGNAME,,}_$((900 + RANDOM % 100))"
				mv "$PublicUserDir" "$BACKSUD"
				writelog "INFO" "${FUNCNAME[0]} - Backing up '$PublicUserDir' to '$BACKSUD'"
			fi

			if [ ! -d "$PublicUserDir" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Creating '$PublicUserDir' symlink pointing to '$PUBUSYD' using command:"
				writelog "INFO" "${FUNCNAME[0]} - 'ln -s \"$PUBUSYD\" \"$PublicUserDir\"'"

				ln -s "$PUBUSYD" "$PublicUserDir"
			else
				writelog "SKIP" "${FUNCNAME[0]} - '$PublicUserDir' still exists - can't create a symlink pointing to '$PUBUSYD'"
			fi
		fi

		if [ "$(readlink "$SteamUserDir")" == "$SUSYD" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Pulling files from backup if existing and '$SteamUserDir' is empty and a symlink to '$SUSYD'"
			restoreSteamUser "restore-if-dst-is-empty"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - Symlinking $STUS is disabled - Checking if a symlink needs to be reverted"

		if [ -L "$SteamUserDir" ]; then
			SUSYD="$(readlink "$SteamUserDir")"
			writelog "INFO" "${FUNCNAME[0]} - '$SteamUserDir' points to '$SUSYD'"
			if [ -d "$SUSYD" ]; then
				rm "$SteamUserDir"
				mkProjDir "$SteamUserDir"
				if grep -q "$AID" <<< "$SUSYD"; then
					writelog "INFO" "${FUNCNAME[0]} - The directory '$SUSYD' where the symlink '$SteamUserDir' points to is game specific - "
					writelog "INFO" "${FUNCNAME[0]} - Migrating all files into the freshly created '$SteamUserDir' from it"
					"$RSYNC" -amu "$SUSYD" "$SteamUserDir"
				else
					writelog "INFO" "${FUNCNAME[0]} - The directory '$SUSYD' where the symlink '$SteamUserDir' points to is not game specific - "
					writelog "INFO" "${FUNCNAME[0]} - Not migrating any files into the freshly created '$SteamUserDir' from it - "
					writelog "INFO" "${FUNCNAME[0]} - Pulling files from backup if existing instead"
					restoreSteamUser "restore-if-dst-is-empty"
				fi
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - '$SteamUserDir' is no symlink - nothing to do"
		fi
	fi
}

function redirectSCDP {
	function checkCompatdataSteamUser {
		if [ "$REDIRSTEAMUSER" == "symlink" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Checking creation of a '$STUS' symlink, as REDIRSTEAMUSER is '$REDIRSTEAMUSER'"
			symlinkSteamUser 1
		elif [ "$REDIRSTEAMUSER" == "restore-backup" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Calling restoreSteamUser to migrate '$STUS', as REDIRSTEAMUSER is '$REDIRSTEAMUSER'"
			restoreSteamUser
		else
			writelog "INFO" "${FUNCNAME[0]} - Leaving '$STUS' directory untouched, as REDIRSTEAMUSER is '$REDIRSTEAMUSER'"
		fi
	}

	function SetCompatdataSymlink {
		DIR="$1"
		SYM="$2"
		writelog "INFO" "${FUNCNAME[0]} - Using '$DIR' as new $CODA dir and '$SYM' as symlink pointing to it"

		if [ ! -f "${DIR}/version" ]; then
			writelog "INFO" "${FUNCNAME[0]} - New $CODA '$DIR' does not exist yet"
			if [ -d "${STEAM_COMPAT_DATA_PATH}_SAC" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Moving found backed autocloud $CODA '${STEAM_COMPAT_DATA_PATH}_SAC' to '$DIR'"
				mkProjDir "${DIR%/*}"
				mv "${STEAM_COMPAT_DATA_PATH}_SAC" "$DIR"
			else
				writelog "INFO" "${FUNCNAME[0]} - Creating new $CODA dir '$DIR'"
				mkProjDir "$DIR"
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - Using existing $CODA '$DIR'"
			if [ -d "${STEAM_COMPAT_DATA_PATH}_SAC" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Found backed autocloud $CODA '${STEAM_COMPAT_DATA_PATH}_SAC'"
			fi
		fi

		if [ -L "$SYM" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Removing old symlink '$SYM'"
			rm "$SYM" 2>/dev/null #remove old symlink
		elif [ -d "$SYM" ]; then
			BACKSCDP="${SYM}-BAK$((100 + RANDOM % 100))"
			writelog "WARN" "${FUNCNAME[0]} - '$SYM' does still exist as directory - renaming to '$BACKSCDP'"
			mv "$SYM" "$BACKSCDP"
		fi

		writelog "INFO" "${FUNCNAME[0]} - Creating symlink via 'ln -s \"$DIR\" \"$SYM\"'"
		ln -s "$DIR" "$SYM"
		notiShow "$(strFix "$NOTY_GLOBALTWEAK" "$DIR")"

		SPVF="$STEAM_COMPAT_DATA_PATH/${SHOSTL}-version"
		if [ ! -f "$SPVF" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Creating '$SPVF' with version '$DESTPROT'"
			echo "$DESTPROT" > "$SPVF"
		else
			SCDPSPV="$(cat "$SPVF")"
			if [ "$SCDPSPV" == "$DESTPROT" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Found '$SPVF' with version '$DESTPROT'"
			else
				writelog "INFO" "${FUNCNAME[0]} - Proton version '$SCDPSPV' in '$SPVF' doesn't match expected version '$DESTPROT' DEBUG"
			fi
		fi

		if [ "$REDIRCOMPDATA" == "global-proton" ]; then
			UBAID="${STEAM_COMPAT_DATA_PATH}/used_by-$AID"
			if [ ! -f "$UBAID" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Creating '$UBAID'" # and trying to trigger Steam First Time Setup"
				touch "$UBAID"
			fi
		fi

		checkCompatdataSteamUser
	}

	function checkCompatdataSymlink {
		if [ -L "$STEAM_COMPAT_DATA_PATH" ] || [ -d "$STEAM_COMPAT_DATA_PATH" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using $CODA '$REDIRCOMPDATA' mode"

			PVF="$STEAM_COMPAT_DATA_PATH/version"
			SCDPPV="$(cat "$PVF")"

			SPVF="$STEAM_COMPAT_DATA_PATH/${PROGNAME,,}-version"
			SCDPSPV="$(cat "$SPVF")"

			if [ -f "$SPVF" ]; then
				if [ "$SCDPSPV" == "$DESTPROT" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Proton version '$SCDPSPV' in '$SPVF' matches expected version '$DESTPROT' (allowing minor version mismatch)"
					CDPVOK=1
				else
					writelog "INFO" "${FUNCNAME[0]} - Proton version '$SCDPSPV' in '$SPVF' is not the expected version '$DESTPROT'"
					CDPVOK=0
				fi
			else
				if [ ! -f "$PVF" ] && [ -d "$STEAM_COMPAT_DATA_PATH" ]; then
					writelog "INFO" "${FUNCNAME[0]} - STEAM_COMPAT_DATA_PATH doesn't have a proton version file '$PVF'"
					writelog "INFO" "${FUNCNAME[0]} - Steam probably just re-created the $CODA by creating the steam_autocloud.vdf"
					writelog "INFO" "${FUNCNAME[0]} - Moving the $CODA out of the way"
					mv "$STEAM_COMPAT_DATA_PATH" "${STEAM_COMPAT_DATA_PATH}_SAC"
					SCDPPV=0
				elif [ ! -f "$PVF" ] && [ -L "$STEAM_COMPAT_DATA_PATH" ]; then
					writelog "INFO" "${FUNCNAME[0]} - STEAM_COMPAT_DATA_PATH is a symlink - it should have a '$PVF' though - $(ls -la "$PVF") DEBUG"
					SCDPPV=0
				fi

				if [ "$SCDPPV" == "$DESTPROT" ] || [ "$SCDPPV" == "${ORGUSEPROTON//proton-}" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Proton in $CODA '$STEAM_COMPAT_DATA_PATH' matches the expected version '$DESTPROT' (allowing minor version mismatch)"
					CDPVOK=1
				else
					writelog "INFO" "${FUNCNAME[0]} - Proton in $CODA '$STEAM_COMPAT_DATA_PATH' has not the expected version '$DESTPROT' but '$SCDPPV'"
					CDPVOK=0
				fi
			fi
		fi

		if [ -L "$STEAM_COMPAT_DATA_PATH" ]; then
			writelog "INFO" "${FUNCNAME[0]} - '$STEAM_COMPAT_DATA_PATH' is already a symbolic link - comparing versions"

			if [ "$(readlink "$STEAM_COMPAT_DATA_PATH")" == "$DESTCOMPDATA" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Symlink '$STEAM_COMPAT_DATA_PATH' points correctly to the expected directory '$DESTCOMPDATA'"
				CDSYMOK=1
			else
				writelog "WARN" "${FUNCNAME[0]} - Symlink '$STEAM_COMPAT_DATA_PATH' does not point correctly to the expected directory '$DESTCOMPDATA', but instead to '$(readlink "$STEAM_COMPAT_DATA_PATH")'"
				CDSYMOK=0
			fi

			if [ "$CDSYMOK" -eq 1 ] && [ "$CDPVOK" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Both $CODA symlink '$STEAM_COMPAT_DATA_PATH' and Proton version are correct - nothing to do here"
			elif [ "$CDSYMOK" -eq 1 ] && [ "$CDPVOK" -eq 0 ]; then
				writelog "INFO" "${FUNCNAME[0]} - $CODA symlink '$STEAM_COMPAT_DATA_PATH' is correct, but Proton version is not - this is unusual but continuing anyway"
			elif [ "$CDSYMOK" -eq 0 ] && [ "$CDPVOK" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - $CODA symlink '$STEAM_COMPAT_DATA_PATH' is not the expected one, but Proton version matches - assuming manual configuration from the user and using it"
			elif [ "$CDSYMOK" -eq 0 ] && [ "$CDPVOK" -eq 0 ]; then
				writelog "INFO" "${FUNCNAME[0]} - $CODA symlink '$STEAM_COMPAT_DATA_PATH' is not the expected one and Proton version doesn't match as well, assuming the Proton version was changed - redirecting $CODA to '$DESTCOMPDATA'"
				SetCompatdataSymlink "$DESTCOMPDATA" "$STEAM_COMPAT_DATA_PATH"
			fi
		elif [ -d "$STEAM_COMPAT_DATA_PATH" ]; then
			writelog "INFO" "${FUNCNAME[0]} - '$STEAM_COMPAT_DATA_PATH' is the original directory"
			if [ -d "$DESTCOMPDATA" ]; then # probably not worth to check the proton version in $DESTCOMPDATA here as well(?)
				BACKSCDP="${STEAM_COMPAT_DATA_PATH}-BAK$((100 + RANDOM % 100))"
				writelog "INFO" "${FUNCNAME[0]} - '$DESTCOMPDATA' does already exist - moving '$STEAM_COMPAT_DATA_PATH' to '$BACKSCDP' and setting symlink"
				mv "$STEAM_COMPAT_DATA_PATH" "$BACKSCDP"
				SetCompatdataSymlink "$DESTCOMPDATA" "$STEAM_COMPAT_DATA_PATH"
			else
				if [ "$CDPVOK" -eq 1 ]; then
					writelog "INFO" "${FUNCNAME[0]} - '$DESTCOMPDATA' does not yet exist, moving '$STEAM_COMPAT_DATA_PATH' as the Proton version is correct and creating symlink"
					mv "$STEAM_COMPAT_DATA_PATH" "$DESTCOMPDATA"
					SetCompatdataSymlink "$DESTCOMPDATA" "$STEAM_COMPAT_DATA_PATH"
				else
					writelog "INFO" "${FUNCNAME[0]} - '$DESTCOMPDATA' does not yet exist, creating a new one, because '$STEAM_COMPAT_DATA_PATH' uses an incorrect Proton version"
					BACKSCDP="${STEAM_COMPAT_DATA_PATH}-BAK$((100 + RANDOM % 100))"
					writelog "INFO" "${FUNCNAME[0]} - Renaming '$STEAM_COMPAT_DATA_PATH' to '$BACKSCDP' and setting symlink"
					mv "$STEAM_COMPAT_DATA_PATH" "$BACKSCDP"
					SetCompatdataSymlink "$DESTCOMPDATA" "$STEAM_COMPAT_DATA_PATH"
				fi
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - '$STEAM_COMPAT_DATA_PATH' is neither a directory nor a symbolic link - creating it"
			SetCompatdataSymlink "$DESTCOMPDATA" "$STEAM_COMPAT_DATA_PATH"
		fi
	}

	function fixDestCompat {
		DESTCOMPDATA2="${DESTCOMPDATA//--/-}"
		if [ -d "$DESTCOMPDATA" ] && [ ! -d "$DESTCOMPDATA2" ] && [ "$DESTCOMPDATA" != "$DESTCOMPDATA2" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Renaming old '${DESTCOMPDATA##*/}' to '${DESTCOMPDATA2##*/}'"
			mv "$DESTCOMPDATA" "$DESTCOMPDATA2"
			DESTCOMPDATA="$DESTCOMPDATA2"
		fi
	}

	## There is room for improvement here, community contributions welcome!
	if [ -n "$STEAM_COMPAT_DATA_PATH" ]; then # should be enough
		VERSPROTMAJOR="$( printf '%s' "${USEPROTON}" | sed -e 's/-\(rc\|beta\)[0-9]\+$//' \
															-e 's/-[0-9A-Za-z_]\+$//' )"
		# Above first removes -beta# and -rc# from Proton names. Then turns the Proton name from "proton-8.0-3c" to "proton-8.0"
		VERSFIX="$(grep "${PROTVERSNOMINOR}" "$PROTONCSV" | cut -d ';' -f1 | sort -nr | head -n1)"

		if [ "${ONLYPROTMAJORREDIRECT}" -eq 1 ]; then  # Only create redirect folders for major Proton version bumps, such as GE-Proton7 -> GE-Proton8, or Proton 7.0 -> Proton 8.0 (IGNORING minor fix versions)
			DESTPROT1="${VERSPROTMAJOR//proton-}"
			writelog "INFO" "${FUNCNAME[0]} - Using major Proton version without minor version fix '$DESTPROT1' for $CODA directory name, because ONLYPROTMAJORREDIRECT is '$ONLYPROTMAJORREDIRECT'"
		elif [ -n "$VERSFIX" ]; then  # Create a compat directory lile 'proton-8.0-3c', WITH the minor fix version
			DESTPROT1="${VERSFIX//proton-}"
			writelog "INFO" "${FUNCNAME[0]} - Using Proton version with minor version fix '$DESTPROT1' for $CODA directory name"
		else  # ???
			DESTPROT1="${USEPROTON//proton-}"
			writelog "INFO" "${FUNCNAME[0]} - Using Proton version '$DESTPROT1' for '$CODA' directory name"
		fi

		DESTPROT="${DESTPROT1//Proton}"
		# above might have to be expanded later, depending on proton names

		if [ "$REDIRCOMPDATA" == "single-proton" ]; then
			DESTCOMPDATA="${STEAM_COMPAT_DATA_PATH//${STEAM_COMPAT_DATA_PATH##*/}/${PROGNAME,,}\/${STEAM_COMPAT_DATA_PATH##*/}-proton-${DESTPROT}}"
			fixDestCompat
			checkCompatdataSymlink
		elif [ "$REDIRCOMPDATA" == "global-proton" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using global 'STEAM_COMPAT_DATA_PATH'"
			DESTCOMPDATA="$STLPROTCOMPDATDIR/${CODA}-proton-${DESTPROT}"
			fixDestCompat
			checkCompatdataSymlink
		else
			writelog "INFO" "${FUNCNAME[0]} - Using regular $CODA '$STEAM_COMPAT_DATA_PATH'"
			if [ -L "$STEAM_COMPAT_DATA_PATH" ]; then
				if [ -f "${STEAM_COMPAT_DATA_PATH}/used_by-$AID" ]; then
					rm "${STEAM_COMPAT_DATA_PATH}/used_by-$AID" 2>/dev/null
					writelog "INFO" "${FUNCNAME[0]} - '$STEAM_COMPAT_DATA_PATH' is a symlink. Removing it, as REDIRCOMPDATA is $REDIRCOMPDATA"
					rm "$STEAM_COMPAT_DATA_PATH"
					mkProjDir "$STEAM_COMPAT_DATA_PATH"
				else
					# See STL issue #692 for background on the above removing logic removing valid user prefixes
					writelog "INFO" "${FUNCNAME[0]} - '$STEAM_COMPAT_DATA_PATH' is a symlink, but we don't have a 'used_by-$AID' file in this game's prefix -- Assuming this is a user-created symlink and not removing"
					writelog "INFO" "${FUNCNAME[0]} - User-created symlinks are valid on Steam Deck to work around some Steam Client bugs"
				fi
			fi
		fi
	fi
}

function launchIGCS {
	IGCSPROCESS="$(grep "^Process=" "$IGCSINI" | cut -d '=' -f2)"
	writelog "INFO" "${FUNCNAME[0]} - Injecting '$IGCSDLL' into exe '${IGCSPROCESS%.*}' using '$IGCS'"
	writelog "INFO" "${FUNCNAME[0]} - IGCSDST '$IGCSDST'"
	rm "$IGCSINITLOCK" 2>/dev/null
	extProtonRun "R" "$IGCSDST"
}

function injectIGCS {
	writelog "INFO" "${FUNCNAME[0]} - Starting '$IGCS'"
	waitForGamePid
	writelog "INFO" "${FUNCNAME[0]} - Game is running, starting '$IGCS' in '$IGCSWAIT' seconds"
	touch "$IGCSINITLOCK"
	sleep "$IGCSWAIT"
	launchIGCS
}

function postIGCS {
	COUNTER=0

	while [ ! -f "$IGCSINITLOCK" ]; do
		writelog "INFO" "${FUNCNAME[0]} - Waiting for $IGCSINITLOCK to appear"
		if [ -f "$CLOSETMP" ] || [[ "$COUNTER" -ge "$IGCSWAIT" ]]; then
			break
		fi
		COUNTER=$((COUNTER+1))
		sleep 1
	done

	writelog "INFO" "${FUNCNAME[0]} - Waited '$COUNTER/$IGCSWAIT' seconds"

	IGCSWAITLEFT=$((IGCSWAIT - COUNTER + 2))
	writelog "INFO" "${FUNCNAME[0]} - Waiting (max $IGCSWAITLEFT more seconds) for $IGCS to initialize"
	COUNTER=0

	while [ -f "$IGCSINITLOCK" ]; do
		if [ -f "$CLOSETMP" ] || [[ "$COUNTER" -ge "$IGCSWAITLEFT" ]]; then
			break
		fi
		COUNTER=$((COUNTER+1))
		sleep 1
	done

	COUNTER=0
	if [ -f "$IGCSINITLOCK" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Lock file $IGCSINITLOCK still exists - giving up"
	else
		COUNTER=0
		WAITWIN=3
		writelog "INFO" "${FUNCNAME[0]} - $IGCS just started, now waiting a bit for its window"

		IGCSWIN="$("$XDO" search --name "$IGCS")"
		# use if $IGCS alone also matches games window:
		# IGCSWIN="$("$XDO" search --name "$SA.*.$IGCS")" # =+ steamapps
		while ! [ "$IGCSWIN" -eq "$IGCSWIN" ] 2>/dev/null; do
			if [[ "$COUNTER" -ge "$WAITWIN" ]]; then
				break
			fi
			COUNTER=$((COUNTER+1))
			sleep 1
		done

		writelog "INFO" "${FUNCNAME[0]} - Closing '$IGCS' window '$IGCSWIN'"
		"$XDO" windowactivate --sync "$IGCSWIN"	key "KP_Enter"

		writelog "INFO" "${FUNCNAME[0]} - And minimize false-positive UUU warn window"
		IGCSPROCESS="$(grep "^Process=" "$IGCSINI" | cut -d '=' -f2)"
		"$XDO" windowminimize "$("$XDO" search --name "${IGCSPROCESS%.*}")"

		if [ -f "$GWXTEMP" ]; then
			WIFO="$(cat "$GWXTEMP")"
			writelog "INFO" "${FUNCNAME[0]} - Setting focus on window '$WIFO'"
			"$XDO" windowactivate "$WIFO"
		else
			writelog "INFO" "${FUNCNAME[0]} - No '$GWXTEMP' found"
		fi
	fi
}

function selectIGCSdll {
	if [ -d "$EFD" ]; then
		fixShowGnAid
		export CURWIKI="$PPW/$IGCS"
		TITLE="${PROGNAME}-Select-IGCS-dll"
		pollWinRes "$TITLE"
		setShowPic

		PICKIGCSDLL="$("$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
		--separator="" --title="$TITLE" \
		--text="$(spanFont "$SGNAID - $GUI_SELECTIGCSDLL" "H")" \
		--field=" ":LBL " " \
		--field="$GUI_SELECTDLL":FL "${EFD}" --file-filter="$GUI_DLLFILES (*.dll)| *.dll" "$GEOM")"
		if [ -f "$PICKIGCSDLL" ]; then
			IGCSDLL="${PICKIGCSDLL##*/}"
			if [ ! -f "$EFD/$IGCSDLL" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Copying selected dll '$PICKIGCSDLL' to '$EFD'"
				cp "$PICKIGCSDLL" "$EFD"
			fi
			IGCSINI="$EFD/${IGCS}.ini"
			writelog "INFO" "${FUNCNAME[0]} - Inserting '$IGCSDLL' into '$IGCSINI'"
			sed "/^Dll=/d" -i "$IGCSINI"
			echo "Dll=$IGCSDLL" >> "$IGCSINI"
		else
			writelog "SKIP" "${FUNCNAME[0]} - No dll selected - skipping"
		fi
	fi
}

function createUUUPatchCommand {
	echo "$XDO windowactivate \"GAMEWINXID\" && sleep 1 && $XDO key \"grave\" && $XDO type \"exec $UUUPATCH\" && $XDO key \"KP_Enter\"" > "$UUUPATCHCOMMAND"
	chmod +x "$UUUPATCHCOMMAND"
}

function getGameWXID {
	if [ -n "$VRPGWINXIS" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using found variable VRPGWINXIS '$VRPGWINXIS' as GAMEWINXID"
		GAMEWINXID="$VRPGWINXIS"
	fi

	if [ -n "$GAMEWINXID" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Already have the windowid '$GAMEWINXID'"
	else
		GAMEWINPID="$(getGameWindowPID)"
		writelog "INFO" "${FUNCNAME[0]} - Determining GAMEWINXID via GAMEWINPID '$GAMEWINPID'"
		GAMEWINXID="$(getGameWinXIDFromPid "$GAMEWINPID")"
 	fi

	if [ -n "$GAMEWINXID" ]; then
		export VRPGWINXIS="$GAMEWINXID"
	fi
}

function injectUUUPatch {
	if [ "$(find "$EFD" -name "$UUUPATCH" | wc -l)" -ge 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - starting UUU patch command auto executing '$UUUPATCH'"
		waitForGamePid
		getGameWXID

		if [ -n "$GAMEWINXID" ]; then
			echo "$GAMEWINXID" > "$GWXTEMP"

			sed "s:GAMEWINXID:$GAMEWINXID:g" -i "$UUUPATCHCOMMAND"
			writelog "INFO" "${FUNCNAME[0]} - starting patch command auto executing '$UUUPATCH'"

			if [ "$UUUPATCHWAIT" -ge 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Game is running, starting UUU patch command '$UUUPATCHCOMMAND' in '$UUUPATCHWAIT' seconds"
				if [ ! -f "$TRAYCUSC" ] || grep -q "$UUUPATCH" "$TRAYCUSC"; then
					cp "$UUUPATCHCOMMAND" "$TRAYCUSC"
				fi
				sleep "$UUUPATCHWAIT"
				"$UUUPATCHCOMMAND"
			else
				writelog "INFO" "${FUNCNAME[0]} - UUUPATCHWAIT is '$UUUPATCHWAIT', so placing the UUU patch command behind the TrayIcon command '$TRAY_LCS' for manual execution"
				cp "$UUUPATCHCOMMAND" "$TRAYCUSC"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - game window id could not be found - can't start the UUU patch command"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - UE4 console script '$UUUPATCH' not found in game directory '$EFD'"
	fi
}

function checkUUUPatchLaunch {
	if [ "$UUUSEPATCH" -eq 1 ] || [ "$UUUSEVR" -eq 1 ]; then
		createUUUPatchCommand
		injectUUUPatch &
	fi
}

function prepareUEVRpatch {
	if [ "$UUUSEVR" -eq 1 ]; then
		UUUPATCHFILE="$1"

		if [ ! -f "$UUUPATCHFILE" ]; then
			if [ -f "$GLOBALMISCDIR/${UUUPATCH}-${AID}" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Copying game specific '$GLOBALMISCDIR/${UUUPATCH}-${AID}' to '$UUUPATCHFILE'"
				cp "$GLOBALMISCDIR/$UUUPATCH-${AID}" "$UUUPATCHFILE"
			elif [ -f "$GLOBALMISCDIR/${UUUPATCH}" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Copying generic '$GLOBALMISCDIR/${UUUPATCH}' to '$UUUPATCHFILE'"
				cp "$GLOBALMISCDIR/$UUUPATCH" "$UUUPATCHFILE"
			else
				writelog "WARN" "${FUNCNAME[0]} - No source '$UUUPATCH' file found in '$GLOBALMISCDIR'"
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - '$UUUPATCHFILE' already available"
		fi
	fi
}

function checkIGCSInjector {

	if [ "$UUUSEPATCH" -eq 1 ] || [ "$UUUSEVR" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Enabling '$UUU' with '$IGCS' as UUU patch mode is enabled"
		UUUSEIGCS=1
	fi

	if [ "$UUUSEIGCS" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using '$UUU' with '$IGCS' is enabled"
		USEIGCS=1
	fi

	if [ "$USEIGCS" -eq 1 ]; then
		IGCSEXE="${IGCS}.exe"
		IGCSDST="$EFD/$IGCSEXE"
		if [ ! -f "$IGCSDST" ]; then
			writelog "INFO" "${FUNCNAME[0]} - '$IGCSDST' not found"
			IGCSDL="$STLDLDIR/igcs/"
			IGCSSRC="$IGCSDL/$IGCSEXE"
			mkProjDir "$IGCSDL"

			if [ ! -f "$IGCSSRC" ]; then
				IGCSDST="$IGCSDL/${IGCSZIP##*/}"
				if [ ! -f "$IGCSDST" ]; then
					dlCheck "$IGCSZIP" "$IGCSDST" "X" "'$IGCSDST' not found - downloading automatically from '$IGCSZIP'"
				fi
				"$UNZIP" "$IGCSDST" -d "$IGCSDL"
			fi

			if [ -f "$IGCSSRC" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Copying '$IGCSSRC' to '$IGCSDST'"
				cp "$IGCSSRC" "$IGCSDST"
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - '$IGCSDST' found"
		fi

		if [ -f "$IGCSDST" ]; then
			IGCSINI="$EFD/${IGCS}.ini"
			WSE="$(find "$EFD" -name "*-Win64-Shipping.exe" | head -n1)"

			if [ -n "$WSE" ]; then
				prepareUEVRpatch "${WSE%/*}/../$UUUPATCH"
			fi

			if [ ! -f "$IGCSINI" ]; then
				writelog "INFO" "${FUNCNAME[0]} - '$IGCSINI' not found - creating it"
				echo "[InjectionData]" > "$IGCSINI"
				echo "Process=$GAMEEXE" >> "$IGCSINI"
				if [ "$UUUSEIGCS" -eq 1 ]; then
					writelog "INFO" "${FUNCNAME[0]} - WSE is '$WSE'"

					if [ -n "$WSE" ]; then
						echo "Process=${WSE##*/}" >> "$IGCSINI"
					else
						echo "Process=$GAMEEXE" >> "$IGCSINI"
					fi
					writelog "INFO" "${FUNCNAME[0]} - Using '${UUU}.dll' as dll in '$IGCSINI'"
					echo "Dll=${UUU}.dll" >> "$IGCSINI"
				else
					echo "Process=$GAMEEXE" >> "$IGCSINI"
				fi
			fi

			IGCSDLL="$(grep "^Dll=" "$IGCSINI" | cut -d '=' -f2)"
			if [ "$UUUSEIGCS" -eq 1 ]; then
				if [ "$IGCSDLL" != "${UUU}.dll" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Updating dll in '$IGCSINI' to '${UUU}.dll' because 'UUUSEIGCS' is enabled"
					sed "/^Dll=/d" -i "$IGCSINI"
					echo "Dll=${UUU}.dll" >> "$IGCSINI"
				fi
			fi

			IGCSDLL="$(grep "^Dll=" "$IGCSINI" | cut -d '=' -f2)"
			if [ ! -f "$EFD/$IGCSDLL" ]; then
				if [ "$UUUSEIGCS" -eq 1 ]; then
					UUUDL="$STLDLDIR/uuu"
					UUUSRC="$UUUDL/$IGCSDLL"
					mkProjDir "$UUUDL"
					writelog "INFO" "${FUNCNAME[0]} - 'UUUSEIGCS' is enabled, but '$EFD/$IGCSDLL' is not available - checking if '$UUUSRC' is available"
					if [ ! -f "$UUUSRC" ]; then
						if [ -x "$(command -v "$XDGO" 2>/dev/null)" ]; then
							writelog "WARN" "${FUNCNAME[0]} - '$UUUSRC' was not found - opening Info requester, because it needs to be downloaded manually from '$UUUURL' and extracted to '$UUUDL'"
							export CURWIKI="$PPW/$UUU"
							TITLE="${PROGNAME}-$UUU-Info"
							pollWinRes "$TITLE"
							setShowPic

							"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
							--title="$TITLE" --text="$(spanFont "$(strFix "$GUI_UUUINFO1" "$UUU")" "H")" \
							--field="Url: :RW" "$UUUURL" \
							--field="$GUI_UUUINFO2:LBL" " " \
							--field="$UUUDL":FBTN "$XDGO $UUUDL" "$GEOM"
						else
							writelog "SKIP" "${FUNCNAME[0]} - '$UUUSRC' was not found, but can't open the Info requester, because '$XDGO' was not found"
						fi
					fi

					if [ -f "$UUUSRC" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Copying '$UUUSRC' to '$EFD'"
						cp "$UUUSRC" "$EFD"
					else
						writelog "SKIP" "${FUNCNAME[0]} -'$UUUSRC' could not be found - skipping"
					fi
				else
					writelog "INFO" "${FUNCNAME[0]} - No valid dll found in '$IGCSINI' - Choose one"
					selectIGCSdll
				fi
			fi

			IGCSDLL="$(grep "^Dll=" "$IGCSINI" | cut -d '=' -f2)"
			if [ -f "$EFD/$IGCSDLL" ]; then
				injectIGCS &
				postIGCS &
			else
				writelog "SKIP" "${FUNCNAME[0]} - Still no valid dll found in '$IGCSINI' - giving up"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - '$IGCSDST' not found and could not be created - skipping '$IGCS'"
		fi
	fi
}

function injectCustomProg {
	writelog "INFO" "${FUNCNAME[0]} - 'Injecting' custom command - i.e. starting delayed"
	waitForGamePid
	writelog "INFO" "${FUNCNAME[0]} - Game is running, starting custom command in '$INJECTWAIT' seconds"
	sleep "$INJECTWAIT"
	launchCustomProg
}

function delayGameForCustomLaunch {
	if [ "$USECUSTOMCMD" -eq 1 ] && [ "$FORK_CUSTOMCMD" -eq 1 ] && [ "$ONLY_CUSTOMCMD" -eq 0 ] && [ "$WAITFORCUSTOMCMD" -ge 1 ]; then
		COUNTER=0
		MAXTRY="$WAITFORCUSTOMCMD"

		function CUCOPID {
			"$PGREP" -a "" | grep "${CUSTOMCMD##*/}" | grep "Z:" | grep "\.exe" | grep -v "CrashHandler" | cut -d ' ' -f1 | tail -n1
		}

		function waitforCustomPid {
				while [ -z "$(CUCOPID)" ]; do
					if [[ "$COUNTER" -ge "$MAXTRY" ]]; then
						writelog "SKIP" "${FUNCNAME[0]} - Giving up waiting for custom program pid"
						break
					else
						writelog "WAIT" "${FUNCNAME[0]} - Waiting for custom program process $(CUCOPID)"
						COUNTER=$((COUNTER+1))
						sleep 1
					fi
				done

				if [ -n "$(CUCOPID)" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Custom program process found at $(CUCOPID) after $COUNTER seconds"
				fi
		}

		waitforCustomPid

		if [ "$WAITFORCUSTOMCMD" -gt 1 ]; then
			RESTWAIT=$((WAITFORCUSTOMCMD - COUNTER))
			if [ "$RESTWAIT" -gt 0 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Waiting $RESTWAIT more seconds before proceeding with loading the game"
				sleep "$RESTWAIT"
			fi
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - Nothing to do"
	fi
}

function checkCustomLaunch {
	if [ "$ONLYWICO" -eq 1 ]; then
		# only start $WICO
		writelog "INFO" "${FUNCNAME[0]} - 'ONLYWICO' is enabled - starting only $WICO"
		launchCustomProg "ONLYWICO"
	else
		# start a custom program:
		if [ -n "$USECUSTOMCMD" ] ; then
			if [ "$USECUSTOMCMD" -eq 1 ] ; then
				writelog "INFO" "${FUNCNAME[0]} - USECUSTOMCMD is set to '$USECUSTOMCMD' - trying to start custom program '$CUSTOMCMD'"

				if [ "$INJECT_CUSTOMCMD" -eq 1 ]; then
					injectCustomProg &
				else
					# fork in background and continue
					if [ "$ONLY_CUSTOMCMD" -eq 1 ]; then
						SECONDS=0
					fi

					if [ "$FORK_CUSTOMCMD" -eq 1 ]; then
						writelog "INFO" "${FUNCNAME[0]} - FORK_CUSTOMCMD is set to 1 - forking the custom program in background and continue"
						launchCustomProg
					# or wait
					else
						writelog "INFO" "${FUNCNAME[0]} - FORK_CUSTOMCMD is set to 0 - starting the custom program regularly"
						launchCustomProg
					fi

					if [ "$ONLY_CUSTOMCMD" -eq 1 ]; then
						writelog "INFO" "${FUNCNAME[0]} - ONLY_CUSTOMCMD is set to 1 means only custom program '$CUSTOMCMD' is supposed to start - exiting here"
						duration=$SECONDS
						logPlayTime "$duration"
						writelog "INFO" "${FUNCNAME[0]} - ## CUSTOMCMD STOPPED after '$duration' seconds playtime"
						closeSTL " ######### STOP EARLY $PROGNAME $PROGVERS #########"
						exit
					fi
				fi
			else
				if [ -n "$CUSTOMCMD" ] && [[ ! "$CUSTOMCMD" =~ ${DUMMYBIN}$ ]]; then
					writelog "SKIP" "${FUNCNAME[0]} - USECUSTOMCMD is '$USECUSTOMCMD' therefore skipping the custom program '$CUSTOMCMD'"
				fi
			fi
		fi
	fi
}

function setFWSArch {
	if [ -z "$FWSARCH" ]; then
		FWSARCH="64"
		if [ -n "$GP" ] && [ -f "$GP" ]; then
			if [ "$(getArch "$GP")" == "32" ]; then
				FWSARCH="32"
			fi
		fi
	fi
}

function dlFWS {
	setFWSArch
	mkProjDir "$FWSDLDIR/$FWSARCH"
	DSTFILE="${FWS,,}_x64.zip"

	if [ "$FWSARCH" == "32" ]; then
		DSTFILE="${DSTFILE//_x64}"
		SPAT="86"
	else
		SPAT="64"
	fi
	DLCHK="md5sum"

	INCHK="$("$WGET" -q "${FWSURL%%/fws*}" -O - 2> >(grep -v "SSL_INIT") | grep -A1 "x${SPAT} ZIP Package" | grep MD5 | grep -oP '> \K[^<]+')"
	DLDST="$FWSDLDIR/$FWSARCH/$DSTFILE"

	if [ ! -f "$DLDST" ]; then
		notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$DSTFILE")"
		dlCheck "$FWSURL/$DSTFILE" "$DLDST" "$DLCHK" "Downloading '$DSTFILE'" "$INCHK"
	fi

	FWSEXE="$FWSDLDIR/$FWSARCH/${FWS}.exe"
	if [ ! -f "$FWSEXE" ]; then
		if [ -f "$DLDST" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Extracting $DSTFILE in '$FWSDLDIR/$FWSARCH'"
			"$UNZIP" -q "$DLDST" -d "$FWSDLDIR/$FWSARCH"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Downloading '$FWS' failed! Disabling '$FWS'"
			USEFWS=0
		fi
	fi

	if [ ! -f "$FWSEXE" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Extracting '$FWS' failed! Disabling '$FWS'"
		USEFWS=0
	fi
}

function checkFWS {
	if [ "$USEFWS" -eq 1 ]; then
		dlFWS
		if [ "$USEFWS" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - 'USEFWS' is enabled - starting '$FWS' exe '$FWSEXE'"
			setFWSArch
			writelog "INFO" "${FUNCNAME[0]} - Starting '$FWSEXE' forked into the background"
			restoreOrgVars
			(sleep 5; "$RUNPROTON" run "$FWSEXE") &
			emptyVars "O" "X"
		fi
	fi
}

function togWindows {
	function windowminimize {
		rm "$STLMINWIN" 2>/dev/null
		while read -r WN; do
			WINNAME="${WN##*[[:blank:]]}"
			if "$XPROP" -id "$WINNAME" | grep "_NET_WM_ACTION_MINIMIZE" -q ; then
				if "$XPROP" -id "$WINNAME" | grep "_NET_WM_STATE_HIDDEN" -q ; then
					writelog "SKIP" "${FUNCNAME[1]} ${FUNCNAME[0]} - Skipping minimized '$WINNAME'"
				else
					writelog "INFO" "${FUNCNAME[1]} ${FUNCNAME[0]} - Minimizing '$WINNAME'"
					echo "$WINNAME" >> "$STLMINWIN"
					"$XDO" "${FUNCNAME[0]}" "$WINNAME"
				fi
			fi
		done <<< "$("$XPROP" -root | grep "_NET_CLIENT_LIST(WINDOW)" | cut -d '#' -f2 | tr ',' '\n')"
	}

	function windowraise {
		if [ ! -f "$STLMINWIN" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Skipping, because no minimized window file STLMINWIN found"
		else
			while read -r WN; do
				WINNAME="${WN##*[[:blank:]]}"
				writelog "INFO" "${FUNCNAME[0]} - Raising '$WINNAME'"
				"$XDO" "${FUNCNAME[0]}" "$WINNAME"
				COUNTER=0
				MAXTRY=3
				while grep -q "_NET_WM_STATE_HIDDEN" -q <<< "$("$XPROP" -id "$WINNAME")"; do
					if [[ "$COUNTER" -ge "$MAXTRY" ]]; then
						echo "$WINNAME" >> "${STLMINWIN}_lazy"
						break
					else
						writelog "INFO" "${FUNCNAME[0]} - '$WINNAME' minimized after $COUNTER tries - raising again"
						"$XDO" "${FUNCNAME[0]}" "$WINNAME"
						COUNTER=$((COUNTER+1))
					fi
				done

			done < "$STLMINWIN"

			if [ -f "${STLMINWIN}_lazy" ]; then
				while read -r WN; do
					WINNAME="${WN##*[[:blank:]]}"
					writelog "INFO" "${FUNCNAME[0]} - Raising lazy '$WINNAME'"
					"$XDO" "${FUNCNAME[0]}" "$WINNAME"
				done < "${STLMINWIN}_lazy"
			fi

			rm "$STLMINWIN" "${STLMINWIN}_lazy" 2>/dev/null
		fi
	}

	if [ "$TOGGLEWINDOWS" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Setting windows to $1"
		"$1"
	fi
}

function installWinetricksPaks {
	WTPAKS="$1"
	WTWINE="$2"
	WTRUN="$3"

	WTSHMLOG="$STLSHM/WINETRICKS.log"

	if [ "$WTRUN" == "wineVortexRun" ]; then
		WTPFX="$VORTEXPFX"
	else
		WTPFX="$GPFX"
	fi

	if [ "$USEWINE" -eq 1 ]; then
		 WTPFX="$GWFX"
	fi

	if [ -n "$WTPAKS" ] && [ "$WTPAKS" != "$NON" ] && [ "$WTPAKS" != "0" ]; then
		chooseWinetricks
		mapfile -d " " -t -O "${#INSTPAKS[@]}" INSTPAKS < <(printf '%s' "$WTPAKS")

		WTLOG="$WTPFX/winetricks.log"

		if [ ! -f "$WTLOG" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Installing '$WTPAKS' silently with $WINETRICKS"
			notiShow "$(strFix "$NOTY_WTINST" "$WTPAKS" "$("$WINETRICKS" -V | cut -d ' ' -f1)" "$("$WTWINE" --version)")"
			restoreOrgVars
			if [ -n "$4" ] && [ "$4" == "F" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Using 'force' mode as argument '$4' was given"
				"$WTRUN" "$WINETRICKS" --force --unattended "${INSTPAKS[@]}" >> "$WTSHMLOG" 2>/dev/null
			else
				"$WTRUN" "$WINETRICKS" --unattended "${INSTPAKS[@]}" >> "$WTSHMLOG" 2>/dev/null
			fi
			notiShow "$NOTY_WTFIN"
			emptyVars "O" "X"
			writelog "INFO" "${FUNCNAME[0]} - '$WINETRICKS' Installation of '$WTPAKS' exited"
		fi

		if [ -f "$WTLOG" ]; then
			rmDupLines "$WTLOG"
			if [ ! -f "${WTLOG//.log/.checked}" ]; then
				mapfile -t -O "${#NOTINSTALLED[@]}" NOTINSTALLED <<< "$(comm -23 <(echo "${INSTPAKS[*]}" | tr ' ' '\n' | sort) <(sort < "$WTLOG"))"
				if [ -n "${NOTINSTALLED[0]}" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Trying to installing following new or previously missed packages now: '${NOTINSTALLED[*]}'"
					notiShow "$(strFix "$NOTY_WTINST" "${NOTINSTALLED[*]}" "$("$WINETRICKS" -V | cut -d ' ' -f1)" "$("$WTWINE" --version)")"
					"$WTRUN" "$WINETRICKS" --force --unattended "${NOTINSTALLED[@]}" >> "$WTSHMLOG" 2>/dev/null
					notiShow "$NOTY_WTFIN"
				else
					writelog "INFO" "${FUNCNAME[0]} - All packages of '$WTPAKS' are already installed - nothing to do"
					touch "${WTLOG//.log/.checked}"
				fi
				unset NOTINSTALLED
			else
				writelog "INFO" "${FUNCNAME[0]} - Found ${WTLOG//.log/.checked} - Nothing to do"
			fi
		fi
		unset INSTPAKS
	fi
}

# start winetricks before game launch:
function checkWinetricksLaunch {
	# gui:
	if [ -n "$RUN_WINETRICKS" ]; then
		if [ "$RUN_WINETRICKS" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Launching '$WINETRICKS' before game start"
			extWine64Run "$WINETRICKS" --gui  >> "$STLSHM/WINETRICKS_GUI.log" 2>/dev/null
		fi
	fi
	# silent:
	installWinetricksPaks "$WINETRICKSPAKS" "$RUNWINE" "extWine64Run"
}

function chooseWinetricks {
	if [ -n "$WINETRICKS" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - 'WINETRICKS variable' already exists and points to '$WINETRICKS'"
	else
		if [ "$DLWINETRICKS" -eq 1 ] || [ "$ONSTEAMDECK" -eq 1 ] || [ "$INFLATPAK" -eq 1 ] ; then
			WTDLLAST="${WTDLDIR}.txt"
			MAXAGE=1440

			if [ ! -f "$DLWT" ] || [ ! -f "$WTDLLAST" ] || test "$(find "$WTDLLAST" -mmin +"$MAXAGE")"; then
				gitUpdate "$WTDLDIR" "$WINETRICKSURL"
				echo "$(date) - ${FUNCNAME[0]}" > "$WTDLLAST"
			fi

			if [ -f "$DLWT" ]; then
				WINETRICKS="$DLWT"
				writelog "INFO" "${FUNCNAME[0]} - Using '$DLWT' with version '$($DLWT -V | cut -d ' ' -f1)'"
			else
				writelog "SKIP" "${FUNCNAME[0]} - DLWINETRICKS is set to '$DLWINETRICKS', but '$DLWT' was not found!"
			fi
		else
			if [ -x "$(command -v "$SYSWINETRICKS")" ]; then
				WINETRICKS="$SYSWINETRICKS"
				writelog "INFO" "${FUNCNAME[0]} - Using systemwide winetricks with version '$($SYSWINETRICKS -V | cut -d ' ' -f1)'"
			fi
		fi
	fi
}

## Manage setting the Wine DPI scale factor in the registry
function setWineDpiScaling {
	WINEDPIREGPATH="HKEY_CURRENT_USER\\Control Panel\\Desktop"
	WINEDPIREGKEY="LogPixels"
	WINEDPIUSEVAL=""

	## Didn't reuse updateWineRegistryKey because it can't handle the DWORD stuff we need to do here
	## Room for a potential refactor in future

	# If no Wine DPI option is enabled, skip
	if [ "$USEPERGAMEWINEDPI" -eq 0 ] && [ "$USEGLOBALWINEDPI" -eq 0 ]; then
		return
	fi

	WINEDPISCALEREGWINECMD="$( getWineBinFromProtPath "$( getProtPathFromCSV "$USEPROTON" )" )"  # Get the Wine binary to run regedit with
	# Per-Game Wine DPI takes priority over Global, so Global goes in elseif
	if [ "$USEPERGAMEWINEDPI" -eq 1 ]; then  # Per-Game
		if [ -z "$PERGAMEWINEDPI" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Per-Game Wine DPI was enabled, but its value was blank -- Overwriting it as '$DEFWINEDPI'"
			PERGAMEWINEDPI="$DEFWINEDPI"
		fi
		writelog "INFO" "${FUNCNAME[0]} - Setting Per-Game Wine DPI to '$PERGAMEWINEDPI' into prefix '$GPFX'"
		WINEDPIUSEVAL="$PERGAMEWINEDPI"
	elif [ "$USEGLOBALWINEDPI" -eq 1 ]; then  # Global
		if [ -z "$GLOBALWINEDPI" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Global Wine DPI was enabled, but its value was blank -- Overwriting it as '$DEFWINEDPI'"
			GLOBALWINEDPI="$DEFWINEDPI"
		fi
		writelog "INFO" "${FUNCNAME[0]} - Setting Global Wine DPI to '$GLOBALWINEDPI' into prefix '$GPFX'"
		WINEDPIUSEVAL="$GLOBALWINEDPI"
	fi

	# Make extra sure we only set the DPI scaling when we should
	if [ -n "$WINEDPIUSEVAL" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Updating '$WINEDPIREGKEY' with DPI value '$WINEDPIUSEVAL'"
		WINEPREFIX="$GPFX" "$WINEDPISCALEREGWINECMD" reg "add" "$WINEDPIREGPATH" "/v" "$WINEDPIREGKEY" "/t" "REG_DWORD" "/d" "${WINEDPIUSEVAL}" "/f"
	fi
}

function SetWineDebugChannels {
	if [ -n "$1" ]; then
		AID="$1"
		setAIDCfgs
	fi
	loadCfg "$STLGAMECFG"
	fixShowGnAid
	export CURWIKI="$PPW/Wine-Debug"
	TITLE="${PROGNAME}-${FUNCNAME[0]}"
	pollWinRes "$TITLE"
	setShowPic

	WDCTXT="winedebugchannels.txt"
	WDC="$GLOBALMISCDIR/$WDCTXT"

	WDCSEL="$(
	while read -r wtch; do
		if grep -q "\-${wtch}" <<< "$STLWINEDEBUG"; then
			echo TRUE
		else
			echo FALSE
		fi
		if grep -q "+${wtch}" <<< "$STLWINEDEBUG"; then
			echo TRUE
		else
			echo FALSE
		fi
		echo "$wtch"
	done < "$WDC" | \
	"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center "$WINDECO" --list --checklist --column="$GUI_MINUS":CHK --column="$GUI_PLUS":CHK --column="$GUI_WDCH" --separator=";" --print-all \
	--text="$(spanFont "$(strFix "$GUI_WDCDIALOG" "$SGNAID")" "H")" --title="$TITLE" --button="$BUT_CAN:0" --button="$BUT_SELECT:2" "$GEOM")"
	case $? in
		0)	{
				writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_CAN' - Cancelling selection"
			}
		;;
		2)	{
				writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_SELECT' - Saving Debug Options"

				if [ -z "$WDCSEL" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Nothing selected"
				else
					unset STLWINEDEBUG
					while read -r chanline;do
						chan="$(cut -d ';' -f3 <<< "$chanline")"
						if grep -q "TRUE;FALSE" <<< "$chanline"; then
							STLWINEDEBUG="${STLWINEDEBUG},-${chan}"
						elif grep -q "FALSE;TRUE" <<< "$chanline"; then
							STLWINEDEBUG="${STLWINEDEBUG},+${chan}"
						elif grep -q "TRUE;TRUE" <<< "$chanline"; then
							STLWINEDEBUG="${STLWINEDEBUG},-${chan}"
						fi
					done <<< "$WDCSEL"
					touch "$FUPDATE"
					writelog "INFO" "${FUNCNAME[0]} - Saving following Wine Debug options: '$STLWINEDEBUG'"
					updateConfigEntry "STLWINEDEBUG" "${STLWINEDEBUG#,*}" "$STLGAMECFG"
				fi
			}
		;;
	esac
}

function WinetricksPick {
	WTPREF="$1"

	unset CURWTPAKS OTHERPAKS PAKSEL

	if [ "$WINETRICKSPAKS" == "$NON" ]; then
		declare -a CURWTPAKS
	else
		mapfile -d " " -t -O "${#CURWTPAKS[@]}" CURWTPAKS <<< "$(printf '%s\n' "$WINETRICKSPAKS" | sed '/^[[:space:]]*$/d')"
	fi

	while read -r line; do
		mapfile -d " " -t -O "${#PAKSEL[@]}" PAKSEL <<< "${line%% *}"
	done <<< "$(winetricks "$WTPREF" list)"

	while read -r curpak; do
	if [[ ! "${PAKSEL[*]}" =~ $curpak ]]; then
		mapfile -d " " -t -O "${#OTHERPAKS[@]}" OTHERPAKS <<< "${curpak%% *}"
	fi
	done <<< "$(printf "%s\n" "${CURWTPAKS[@]}")"

	fixShowGnAid
	export CURWIKI="$PPW/Winetricks"
	TITLE="${PROGNAME}-WinetricksPackageSelection-$WTPREF"
	pollWinRes "$TITLE"

	setShowPic
	# TODO? some yad/gtk warnings in WTPREF=apps DESCR:
	WTPICKS="$(
	while read -r packline; do
		PACKNAME="${packline%% *}"

		if [[ "${CURWTPAKS[*]}" =~ $PACKNAME ]]; then
			echo TRUE
		else
			echo FALSE
		fi

		echo "$PACKNAME"

		DESCRRAW="${packline#* }"
		DESCR1="${DESCRRAW//$(grep -oP '\(\K[^\)]+' <<< "$DESCRRAW")}";
		DESCR2="${DESCR1//\!}"
		DESCR="${DESCR2//&/&amp}"
		echo "${DESCR#"${DESCR%%[![:space:]]*}"}"
	done <<< "$("$WINETRICKS" "$WTPREF" list)"	 | \
	"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center "$WINDECO" --list --checklist --column="$GUI_ADD" --column="$GUI_WTPACK" --column="$GUI_WTDESC" --separator="" --print-column="2" \
	--text="$(spanFont "$(strFix "$GUI_WTPACKDIALOG" "$SGNAID")" "H")" --title="$TITLE" --button="$BUT_CAN:0" --button="$BUT_SELECT:2" "$GEOM")"
	case $? in
		0)	{
				writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_CAN' - Cancelling selection"
			}
		;;
		2)	{
				writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_SELECT' - Saving Selection"

				if [ -z "$WTPICKS" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Nothing selected"
					WTPICKS=""
				fi

				unset WINETRICKSPAKS
				while read -r line; do
					WINETRICKSPAKS="$WINETRICKSPAKS $line"
				done <<< "$WTPICKS"

				while read -r line; do
					WINETRICKSPAKS="$WINETRICKSPAKS $line"
				done <<< "${OTHERPAKS[*]}"

				if [ -z "$WINETRICKSPAKS" ] || [ "$WINETRICKSPAKS" == "  " ]; then
					WINETRICKSPAKS="$NON"
				fi

				WINETRICKSPAKS="${WINETRICKSPAKS#*[[:blank:]]}"
				WINETRICKSPAKS="${WINETRICKSPAKS%*[[:blank:]]}"

				declare -A WTNODUPPAKS
				for i in $WINETRICKSPAKS; do
					WTNODUPPAKS[$i]=1
				done

				WINETRICKSPAKS="${!WTNODUPPAKS[*]}"

				touch "$FUPDATE"
				updateConfigEntry "WINETRICKSPAKS" "$WINETRICKSPAKS" "$STLGAMECFG"
			}
		;;
	esac
	if [ -n "$2" ]; then
		"$2" "$AID" "${FUNCNAME[0]}"
	fi
}

function cleanDropDown {
	CURSEL="$1"
	OPTIONS="$2"

	FILTOPTS="${OPTIONS//\!$CURSEL\!/\!}"
	FILTOPTS="!${FILTOPTS//$CURSEL\!/\!}"
	FILTOPTS="${FILTOPTS//\!\!/\!}"
	echo "$CURSEL$FILTOPTS"
}

function chooseWinetricksPrefix {
	if [ -n "$1" ]; then
		AID="$1"
		setAIDCfgs
	fi

	if [ ! -f "$STLGAMECFG" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - Game config '$STLGAMECFG' not found - exiting"
	else
		chooseWinetricks
		loadCfg "$STLGAMECFG"
		ORGWINETRICKSPAKS="$WINETRICKSPAKS"

		writelog "INFO" "${FUNCNAME[0]} - Opening dialog to choose a winetricks prefix"
		export CURWIKI="$PPW/Winetricks"
		TITLE="${PROGNAME}-${FUNCNAME[0]}"
		pollWinRes "$TITLE"
		setShowPic

		WTLOG="$GPFX/winetricks.log"

		function WTPAKINSTLIST {
			unset WTLIST

			if [ -f "$WTLOG" ]; then
				rmDupLines "$WTLOG"
			fi

			while read -r line; do
				if [ -f "$WTLOG" ] && grep -q "^$line$" "$WTLOG"; then
					WTLIST="$WTLIST $line $GUI_WTAI,"
				else
					WTLIST="$WTLIST $line,"
				fi
			done <<< "$(tr ' ' '\n' <<< "$WINETRICKSPAKS")"
			WTLIST="${WTLIST/ /}"
			WTLIST="${WTLIST%,*}"
			echo "$WTLIST"
		}

		createProtonList X
		if [ -z "$WTPROTON" ]; then
			WTPROTON="$USEPROTON"
		fi

		WTCATPFX="$("$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
		--title="$TITLE" --separator=";" \
		--text="$(spanFont "$GUI_CHOOSEWTCATPFX" "H")" \
		--field=" ":LBL " " \
		--field="$GUI_WTCATPFX!$GUI_CHOOSEWTCATPFX":CB "dlls!apps!dlls!fonts" \
		--field="$GUI_WTPROTON!$(strFix "$DESC_WTPROTON" "$BUT_INSTALL")":CB "$(cleanDropDown "${WTPROTON//\"}" "$PROTYADLIST")" \
		--field="$GUI_WTPROTONSAVE!$(strFix "$DESC_WTPROTONSAVE" "$BUT_INSTALL")":CHK "FALSE" \
		--field=" ":LBL " " \
		--field="$(spanFont "$GUI_CURSELWTPACKS" "H")":LBL " " \
		--field="$(WTPAKINSTLIST)":LBL " " \
		--button="$BUT_SELECT:2" --button="$BUT_DONE:4" --button="$BUT_INSTALL:6" "$GEOM"
		)"
		case $? in
			2)	{
					WTPREF="$(cut -d ';' -f2 <<< "$WTCATPFX")" # WEAK - will be broken when adding fields
					writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_SELECT' - Opening '$WTPREF' list"
					WinetricksPick "$WTPREF" "${FUNCNAME[0]}"
				}
			;;
			4)	{
					writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_DONE'"
					if [ "$ORGWINETRICKSPAKS" != "$WINETRICKSPAKS" ]; then
						rm "${WTLOG//.log/.checked}" 2>/dev/null
						writelog "INFO" "${FUNCNAME[0]} - Changed will be installed before next game start"
					else
						writelog "INFO" "${FUNCNAME[0]} - Package list hasn't changed"
					fi
				}
			;;
			6)	{
					WTPROTON="$(cut -d ';' -f3 <<< "$WTCATPFX")"
					writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_INSTALL' - Installing '$WINETRICKSPAKS' using '$WTPROTON'"
					RUNWTPROTON="$(getProtPathFromCSV "$WTPROTON")"

					if [ ! -f "$RUNWTPROTON" ]; then
						RUNWTPROTON="$(fixProtonVersionMismatch "WTPROTON" "$STLGAMECFG" X)"
					fi

					writelog "INFO" "${FUNCNAME[0]} - using proton binary '$RUNWTPROTON' for '$WTPROTON'"

					if [ -f "$(dirname "$RUNWTPROTON")/$DBW" ]; then
						RUNWTWINE="$(dirname "$RUNWTPROTON")/$DBW"
					elif [ -f "$(dirname "$RUNWTPROTON")/$FBW" ]; then
						RUNWTWINE="$(dirname "$RUNWTPROTON")/$FBW"
					fi

					writelog "INFO" "${FUNCNAME[0]} - Using wine '$RUNWTWINE' for winetricks"

					WTPROTONSAVE="$(cut -d ';' -f4 <<< "$WTCATPFX")"
					if [ "$WTPROTONSAVE" == "TRUE" ];then
						writelog "INFO" "${FUNCNAME[0]} - Saving Winetricks Proton WTPROTON '$WTPROTON' into '$STLGAMECFG'"
						touch "$FUPDATE"
						updateConfigEntry "WTPROTON" "$WTPROTON" "$STLGAMECFG"
					fi

					if [ -f "$RUNWTWINE" ]; then
						rm "${WTLOG//.log/.checked}" 2>/dev/null
						installWinetricksPaks "$WINETRICKSPAKS" "$RUNWTWINE" "extWine64Run"
					else
						writelog "ERROR" "${FUNCNAME[0]} - Could not find wine binary for current proton '$WTPROTON'"
					fi
				}
			;;
		esac
	fi
}

# start $WINECFG before game launch:
function checkWineCfgLaunch {
	if [ -n "$RUN_WINECFG" ]; then
		if [ "$RUN_WINECFG" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Starting '$WINECFG' at prefix '$GPFX' with Wine '$RUNWINE':"
			if [ "$USEWINE" -eq 0 ]; then
				#513
				WINE="$RUNWINE" WINEARCH=win64 WINEDEBUG="$STLWINEDEBUG" WINEPREFIX="$GPFX" "$RUNWINE" "${WINECFG}.exe" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
			else
				extWine64Run "$RUNWINECFG"
			fi
		else
			writelog "WARN" "${FUNCNAME[0]} - Function was called but RUN_WINECFG ('$RUN_WINECFG') was not enabled - skipping"
		fi
	else
		writelog "WARN" "${FUNCNAME[0]} - Function was called but RUN_WINECFG ('$RUN_WINECFG') was not defined - skipping"
	fi
}

function regEdit {
	REGEDIT="regedit"
	if [ -z "$1" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Starting $REGEDIT without arguments"
		extWine64Run "$RUNWINE" "$REGEDIT"
	else
		writelog "INFO" "${FUNCNAME[0]} - Starting $REGEDIT with argument $1"
		if [ "$USEWINE" -eq 0 ]; then
			extWine64Run "$RUNWINE" "$REGEDIT" "$1"
		else
			extWine64Run "$RUNWINE" "$RUNREGEDIT" "$1"
		fi
		writelog "INFO" "${FUNCNAME[0]} - Applied '$1' using '$REGEDIT' - Setting REGEDIT to 0 now in '$STLGAMECFG'"
		updateConfigEntry "REGEDIT" "0" "$STLGAMECFG"
	fi
}

function customRegs {
	TEMPREG="$GPFX/VirtualDesktop.reg"

	if [ "$VIRTUALDESKTOP" -eq 1 ]; then
		SETVD=1
		if [ -z "$VDRES" ] || [ "$VDRES" == "$NON" ]; then
			VDRES="$(getScreenRes r)"
		fi

		if [ -f "$TEMPREG" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Looks like virtual desktop was already applied before"
			if grep -q "$VDRES" "$TEMPREG"; then
				writelog "SKIP" "${FUNCNAME[0]} - The configured resolution in '$TEMPREG' did not change - nothing to do"
				SETVD=0
			else
				writelog "INFO" "${FUNCNAME[0]} - The configured resolution in '$TEMPREG' changed - updating to '$VDRES'"
			fi
		fi

		if [ "$SETVD" -eq 1 ]; then
			if touch "$TEMPREG"; then
				writelog "INFO" "${FUNCNAME[0]} - VIRTUALDESKTOP is set to 1 - enabling virtual desktop"
				{
				echo "Windows Registry Editor Version 5.00"
				echo "[HKEY_CURRENT_USER\Software\Wine\Explorer]"
				echo "\"Desktop\"=\"Default\""
				echo "[HKEY_CURRENT_USER\Software\Wine\Explorer\Desktops]"
				echo "\"Default\"=\"$VDRES\""
				} >> "$TEMPREG"
				if [ -f "$TEMPREG" ]; then
					regEdit "$TEMPREG"
				else
					writelog "SKIP" "${FUNCNAME[0]} - '$TEMPREG' should be here, but it isn't!"
				fi
			else
				writelog "SKIP" "${FUNCNAME[0]} - Could not create '$TEMPREG' - skipping!"
			fi
		fi
	elif [ "$VIRTUALDESKTOP" -eq 0 ]; then
		if [ -f "$TEMPREG" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Virtual desktop is disabled, but was enabled before - removing the registry value"
			sed "s:\[:\[-:g" -i "$TEMPREG"
			regEdit "$TEMPREG"
			rm "$TEMPREG"
		fi
	fi

	if [ "$REGEDIT" -eq 1 ]; then
		mkProjDir "$STLREGDIR"
		if [ -f "$STLREGDIR/$AID.reg" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Applying registry file '$STLREGDIR/$AID.reg'"
			regEdit "$STLREGDIR/$AID.reg"
		else
			writelog "INFO" "${FUNCNAME[0]} - No game specific regfile found. Opening the regedit editor"
			regEdit
		fi
	fi
}

function useNyrna {
	if [ -n "$RUN_NYRNA" ]; then
		if [ "$RUN_NYRNA" -eq 1 ]; then
			if "$PGREP" -f "$NYRNA" >/dev/null; then
				writelog "SKIP" "${FUNCNAME[0]} - '$NYRNA' already running - skipping"
				RUN_NYRNA=0
			else
			writelog "INFO" "${FUNCNAME[0]} - Starting '$NYRNA'"
			"$NYRNA" &
			fi
		fi
	fi
}

function useReplay {
	if [ -n "$RUN_REPLAY" ]; then
		if [ "$RUN_REPLAY" -eq 1 ]; then
			if "$PGREP" -fx "$(command -v "$REPLAY")" >/dev/null; then
				writelog "SKIP" "${FUNCNAME[0]} - '$REPLAY' already running - skipping"
				RUN_REPLAY=0
			else
			writelog "INFO" "${FUNCNAME[0]} - Starting '$REPLAY'"
			"$REPLAY" &
			fi
		fi
	fi
}

function killPrefixOnGameExit {
	waitForGamePid
	GPID="$(GAMEPID)"
	if [ -n "$GPID" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Game pid '$GPID' found"
	fi
	writelog "INFO" "${FUNCNAME[0]} - Waiting for '$GPID' closing to kill Proton"

	tail --pid="$GPID" -f /dev/null
	writelog "INFO" "${FUNCNAME[0]} - Game process '$GPID' finished - Force closing Proton"
	if [ "$USEWINE" -eq 0 ]; then
		WINEPREFIX="$GPFX" "$RUNWINESERVER" -k
	else
		WINEPREFIX="$GWFX" "$RUNWINESERVER" -k
	fi

	touch "$CLOSETMP"
}

function StateSteamWebHelper {
	if [ "$TOGSTEAMWEBHELPER" -eq 1 ]; then
		SWH="steamwebhelper"

		if [ "$1" == "pause" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Stopping all '$SWH' processes"
			SIGVAL="-SIGSTOP"
		elif [ "$1" == "cont" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Continuing all '$SWH' processes"
			SIGVAL="-SIGCONT"
		else
			writelog "INFO" "${FUNCNAME[0]} - No argument - Toggling '$SWH' processes"
			TESTPID="$("$PGREP" "$SWH" | grep -v "$("$PGREP" "${SWH}\.")" | head -n1)"
			SWHSTATE="$(ps -q "$TESTPID" -o state --no-headers)"
			SIGVAL="-SIGSTOP"
			if [ "$SWHSTATE" = "T" ] ; then
				SIGVAL="-SIGCONT"
			fi
		fi

		while read -r swhpid; do
			if [ -n "$swhpid" ] && [ "$swhpid" -eq "$swhpid" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Executing 'kill \"$SIGVAL\" \"$swhpid\"' to '$1' the $SWH"
				kill "$SIGVAL" "$swhpid"
			fi
		done <<< "$("$PGREP" "$SWH")"
	fi
}

function dlWDIB {
	mkProjDir "$WDIBDLDIR"
	WDIBDSRC="$GHURL/$("$WGET" -q "$WDIBURL" -O - | grep -m1 -E 'releases.*download.*exe*' | cut -d '"' -f2 | head -n1)"

	if [ ! -f "$RUNWDIB" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Downloading '$WDIB' to '$WDIBDLDIR'"
		dlCheck "$WDIBDSRC" "$RUNWDIB" "X" "Downloading '$WDIB' to '$WDIBDLDIR'"
	fi

	if [ -f "$RUNWDIB" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Found $RUNWDIB - skipping download"
	fi
}

# start wine-discord-ipc-bridge
function checkWDIB {
	if [ "$ISGAME" -eq 2 ] && [ "$USE_WDIB" -eq 1 ] && [ "$ISGAME" -eq 2 ] && [ "$USEWINE" -eq 0 ]; then
		dlWDIB

		if [ ! -f "$RUNWDIB" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Download failed - can't start '$WDIB' - skipping"
			USE_WDIB=0
		else
			writelog "INFO" "${FUNCNAME[0]} - Starting '$RUNWDIB' in the backgound for '$AID'"
			extProtonRun "F" "$RUNWDIB"
			killPrefixOnGameExit &
		fi
	fi
}

function checkSteamAppIDFile {
	if [ -d "$EFD" ] && [ "$STLPLAY" -eq 0 ]; then
		GSAIT="$EFD/$SAIT"
		CSAIT="$EFD/check-$SAIT"

		if [ ! -f "$CSAIT" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Creating new control file '$CSAIT'"
			if [ ! -f "$GSAIT" ]; then
				echo "OWNSAIT=\"0\"" > "$CSAIT"
			else
				echo "OWNSAIT=\"1\"" > "$CSAIT"
			fi
		fi

		if [ "$STEAMAPPIDFILE" -eq 1 ]; then
			if [ ! -f "$GSAIT" ]; then
				if ! haveNonSteamGame; then
					writelog "INFO" "${FUNCNAME[0]} - Creating '$GSAIT' because STEAMAPPIDFILE is enabled"
					echo "$AID" > "$GSAIT"
				else
					# Can't create steam_appid.txt for Non-Steam Games because this causes crashes, see #941
					writelog "INFO" "${FUNCNAME[0]} - Looks like we have a Non-Steam Game, not creating '$GSAIT' because this would cause crashes, and forcing 'STEAMAPPIDFILE' to '0'"
					touch "$FUPDATE"
					STEAMAPPIDFILE=0
					updateConfigEntry "STEAMAPPIDFILE" "$STEAMAPPIDFILE" "$STLGAMECFG"
				fi
			else
				writelog "INFO" "${FUNCNAME[0]} - STEAMAPPIDFILE is enabled and '$GSAIT' already exists - nothing to do"
			fi
		else
			if [ ! -f "$GSAIT" ]; then
				writelog "INFO" "${FUNCNAME[0]} - STEAMAPPIDFILE is disabled and there's no '$GSAIT' - nothing to do"
			else
				if [ ! -f "$CSAIT" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Removing '$GSAIT' because STEAMAPPIDFILE is disabled"
					rm "$GSAIT" 2>/dev/null
				else
					loadCfg "$CSAIT" X
					if [ "$OWNSAIT" -eq 1 ]; then
						writelog "INFO" "${FUNCNAME[0]} - OWNSAIT is '$OWNSAIT', means the game ships an own '$SAIT'. Leaving untouched and enabling STEAMAPPIDFILE"
						touch "$FUPDATE"
						STEAMAPPIDFILE=1
						updateConfigEntry "STEAMAPPIDFILE" "$STEAMAPPIDFILE" "$STLGAMECFG"
					else
						writelog "INFO" "${FUNCNAME[0]} - Removing '$GSAIT' because the game doesn't own it and STEAMAPPIDFILE is disabled"
						rm "$GSAIT" 2>/dev/null
					fi
				fi
			fi
		fi
	fi
}

function checkPulse {
	if [ "$CHANGE_PULSE_LATENCY" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Setting PULSE_LATENCY_MSEC to '$STL_PULSE_LATENCY_MSEC'"
		export PULSE_LATENCY_MSEC="$STL_PULSE_LATENCY_MSEC"
	fi
}

function getProtWinePath {
	CHECKWINED="${1%/*}/$DBW"
	CHECKWINEF="${1%/*}/$FBW"
	if [ -f "$CHECKWINED" ]; then
		echo "$CHECKWINED"
	elif [ -f "$CHECKWINEF" ]; then
		echo "$CHECKWINEF"
	fi
}

function listSteWoShPaks {
	STSHT1="${STEWOS,,}"
	STSHTXT="${STSHT1//\ /-}.txt"
	STESHALIST="$GLOBALMISCDIR/$STSHTXT"
	cut -d ';' -f1 "$STESHALIST"
}

function getGameDirFromAM {
	APPMAFE="$1"
	GIRAW="$(grep "\"installdir\"" "$APPMAFE" | awk -F '"installdir"' '{print $NF}')"
	GINS="$(awk '{$1=$1};1' <<< "${GIRAW//\"/}")"
	OUTGD="${APPMAFE%/*}/common/$GINS"
	if [ -d "$OUTGD" ]; then
		echo "$OUTGD"
	fi
}

function getGameDirFromAID {
	APPMAFE="$(listAppManifests | grep -m1 "appmanifest_${1}.acf")"
	getGameDirFromAM "$APPMAFE"
}

function runExe {
	IFILE="$1"
	AIDORPFX="$2"
	INSTWINE="$3"
	INSTARGS="$4"

	mapfile -d " " -t -O "${#INSTARGSARR[@]}" INSTARGSARR < <(printf '%s' "${INSTARGS//\"}")

	if [ "$AIDORPFX" -eq "$AIDORPFX" ] 2>/dev/null; then
		writelog "INFO" "${FUNCNAME[0]} - '$AIDORPFX' seems to be a SteamAppId - determining its pfx" E
		setGPfxFromAppMa "$AIDORPFX"
		if [ -n "$GPFX" ]; then
			IPFX="$GPFX"
			IAID="$AIDORPFX"
		fi
	else
		IPFX="$AIDORPFX"
		IAID="$(grep -oP "$CODA/\K[^/pfx]+" <<< "$IPFX")"
	fi

	if [ "$INSTWINE" != "$NON" ]; then
		IWINE="$INSTWINE"
	else
		if [ "$IAID" -eq "$IAID" ] 2>/dev/null; then
			IAIDCFG="$STLGAMEDIRID/$IAID.conf"
			if [ -f "$IAIDCFG" ]; then
				IUSEPROT="$(grep "^USEPROTON=" "$IAIDCFG" | cut -d '=' -f2)"
				writelog "INFO" "${FUNCNAME[0]} - Searching for wine for '$IUSEPROT' defined in $IAIDCFG"
				IRUNPROT="$(getProtPathFromCSV "${IUSEPROT//\"}")"
				if [ -f "$IRUNPROT" ]; then
					IWINE="$(getProtWinePath "$IRUNPROT")"
					writelog "INFO" "${FUNCNAME[0]} - Using '$IWINE' for installing '$1'"
				else
					writelog "SKIP" "${FUNCNAME[0]} - Could not find path for proton '${IUSEPROT//\"}' in '$IAIDCFG'"
				fi
			fi
		fi

		if [ ! -f "$IAIDCFG" ] || [ ! -f "$IWINE" ]; then
			writelog "INFO" "${FUNCNAME[0]} - No wine binary found - falling back to wine from FIRSTUSEPROTON '$FIRSTUSEPROTON'"
			IRUNPROT="$(getProtPathFromCSV "$FIRSTUSEPROTON")"
			if [ -f "$IRUNPROT" ]; then
				IWINE="$(getProtWinePath "$IRUNPROT")"
				writelog "INFO" "${FUNCNAME[0]} - Using '$IWINE' for installing '$IFILE'"
			else
				writelog "SKIP" "${FUNCNAME[0]} - Could not find path for proton '$FIRSTUSEPROTON' in '$IAIDCFG'"
			fi
		fi
	fi

	if [ -n "$IPFX" ] && [ -f "$IWINE" ] && [ -f "$IFILE" ]; then
		cd "${IFILE%/*}" >/dev/null || return
		if [ -n "$USEMSI" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Installing package via command 'WINEPREFIX=$IPFX $IWINE msiexec /i $IFILE ${INSTARGS//\"/}'"
			WINEDEBUG="-all" WINEPREFIX="$IPFX" "$IWINE" msiexec /i "$IFILE" "${INSTARGSARR[@]}" >"$STLSHM/${FUNCNAME[0]}_${IFILE##*/}.log" 2>"$STLSHM/${FUNCNAME[0]}_${IFILE##*/}.log"
		else
			if [[ "$1" =~ "dotnet" ]]; then
				MOGUID="$(WINEPREFIX="$IPFX" "$IWINE" uninstaller --list | grep "Wine Mono Windows Support" | cut -d '|' -f1)"
				if [ -n "$MOGUID" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Uninstalling Mono before installing '${IFILE##*/}'"
					WINEPREFIX="$IPFX" "$IWINE" uninstaller --remove "$MOGUID" >"$STLSHM/${FUNCNAME[0]}_mono-uninst.log" 2>"$STLSHM/${FUNCNAME[0]}_mono-uninst.log"
				fi
			fi
			WINEDEBUG="-all" WINEPREFIX="$IPFX" "$IWINE" "$IFILE" "${INSTARGSARR[@]}" >"$STLSHM/${FUNCNAME[0]}_${IFILE##*/}.log" 2>"$STLSHM/${FUNCNAME[0]}_${IFILE##*/}.log"
		fi
		cd - >/dev/null || return
	else
		writelog "SKIP" "${FUNCNAME[0]} - At least one component of the package installation is missing, check above logs"
	fi
}

# Install Steamworks Shared Package (package name mapping can be found at eval/packages/<name>)
function installSteWoShPak {
	AIDORPFX="$2"
	if [ -n "$3" ]; then
		INSTWINE="$3"
	else
		INSTWINE="$NON"
	fi

	STSHT1="${STEWOS,,}"
	STSHTXT="${STSHT1//\ /-}.txt"
	STESHALIST="$GLOBALMISCDIR/$STSHTXT"

	CORED="$STEAM_COMPAT_CLIENT_INSTALL_PATH/$SAC/$STEWOS/_CommonRedist/"

	if grep -q "^\"$1\";" "$STESHALIST"; then
		RELPATH="$(grep "^\"$1\";" "$STESHALIST" | cut -d ';' -f2)"
		INSTARGS="$(grep "^\"$1\";" "$STESHALIST" | cut -d ';' -f3)"
		USEMSI="$(grep "^\"$1\";" "$STESHALIST" | cut -d ';' -f4)"
		IFILE="$CORED/${RELPATH//\"/}"
		notiShow "$(strFix "$NOTY_INSTSTART" "$1")"
		writelog "INFO" "${FUNCNAME[0]} - 'runExe \"$IFILE\" \"$AIDORPFX\" \"$INSTWINE\" \"$INSTARGS\"'"
		runExe "$IFILE" "$AIDORPFX" "$INSTWINE" "$INSTARGS"
		notiShow "$(strFix "$NOTY_INSTSTOP" "$1")"
	else
		writelog "SKIP" "${FUNCNAME[0]} - Could not find '$1' in '$STESHALIST' - doesn't seem to be a valid '$STEWOS' name"
	fi
}

# generic Mod functions (Vortex + MO2):

function prepModGameSym {
	DSTL="$1"
	gdir="$2"
	if [ -n "$2" ]; then
		if [ ! -L "$DSTL" ] || [ "$(readlink "$DSTL")" != "$gdir" ]; then
			if [ -L "$DSTL" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Removing symlink '$DSTL' pointing to '$(readlink "$DSTL")'"
				rm "$DSTL" 2>/dev/null
			fi
			if [ -d "$DSTL" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - Not creating symlink from '$gdir' to '$DSTL', because '$DSTL' is a real directory"
			else
				writelog "INFO" "${FUNCNAME[0]} - Creating symlink from '$gdir' to '$DSTL'"
				ln -s "$gdir" "$DSTL"
			fi
		fi
	fi
}

function setModGameReg {
	DSTPFX="$1"
	MODWINE="$2"
	if [ -f "$MODGREG" ]; then
		WINEPREFIX="$DSTPFX" "$MODWINE" regedit "$MODGREG" 2>/dev/null
		WINEPREFIX="$DSTPFX" "${MODWINE}64" regedit "$MODGREG" 2>/dev/null
		rm "$MODGREG" 2>/dev/null
	fi
}

function setModGameSyms {
	SYMODE="$1" # MODE - either 'li' or 'set'
	SRCPFX="$2" # Game prefix
	MODGNA="$3" # Game name
	MODGID="$4" # Game Steam ID
	DSTPFX="$5" # Mod manager prefix
	CHKAPMA="$6" # Game app-manifest (.acf)

	if [ -f "$MO2INSTFAIL" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - '$MO2INSTFAIL' found - seems like installation failed previously - skipping ${FUNCNAME[0]}"
	elif [ ! -d "$SRCPFX" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - '$SRCPFX' missing - looks like the game was removed or never started yet"
	else
		if [ "$SYMODE" == "li" ]; then
			echo "$MODGID;$MODGNA;$SRCPFX;"
		else
			mkProjDir "$DSTPFX/$DRCU/$STUS/$DOCS/$MYGAMES"
			mkProjDir "$DSTPFX/$DRCU/$STUS/$ADLO/cache"

			MODLOOT="$DSTPFX/$DRCU/$STUS/$ADLO/LOOT"
			if [ -L "$MODLOOT" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Removing symlink '$MODLOOT' for creating a real directory instead - is it worth/useful to migrate the content to '$MODLOOT'?"
			fi
			mkProjDir "$MODLOOT"

			writelog "INFO" "${FUNCNAME[0]} - Found game '$MODGNA ($MODGID)' with pfx '$SRCPFX'"
			# maybe merge redundant code later:

			if [ -d "$SRCPFX/$DRCU/$STUS/$DOCS/$MYGAMES" ]; then
				while read -r gdir; do
					DSTL="$DSTPFX/$DRCU/$STUS/$DOCS/$MYGAMES/${gdir##*/}"
					prepModGameSym "$DSTL" "$gdir"
				done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$DOCS/$MYGAMES" -mindepth 1 -maxdepth 1 '(' -type d -o -type l -xtype d ')')"
			fi

			if [ -d "$SRCPFX/$DRCU/$STUS/$DOCS" ]; then
				while read -r gdir; do
					if [ "${gdir##*/}" == "$MYGAMES" ]; then
						while read -r gsdir; do
							DSTL="$DSTPFX/$DRCU/$STUS/$DOCS/$MYGAMES/${gsdir##*/}"
							prepModGameSym "$DSTL" "$gsdir"
						done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$DOCS/$MYGAMES" -mindepth 1 -maxdepth 1 '(' -type d -o -type l -xtype d ')')"
					elif [ "${gdir##*/}" == "$EAGA" ]; then
						mkProjDir "$DSTPFX/$DRCU/$STUS/$DOCS/$EAGA"
						while read -r geadir; do
							DSTL="$DSTPFX/$DRCU/$STUS/$DOCS/$EAGA/${geadir##*/}"
							prepModGameSym "$DSTL" "$geadir"
						done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$DOCS/$EAGA" -mindepth 1 -maxdepth 1 '(' -type d -o -type l -xtype d ')')"
					elif [ "${gdir##*/}" == "$LAGA" ]; then
						mkProjDir "$DSTPFX/$DRCU/$STUS/$DOCS/$LAGA"
						while read -r gladir; do
							DSTL="$DSTPFX/$DRCU/$STUS/$DOCS/$LAGA/${gladir##*/}"
							prepModGameSym "$DSTL" "$gladir"
						done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$DOCS/$LAGA" -mindepth 1 -maxdepth 1 '(' -type d -o -type l -xtype d ')')"
					elif [ "${gdir##*/}" == "$BIOW" ]; then
						mkProjDir "$DSTPFX/$DRCU/$STUS/$DOCS/$BIOW"
						while read -r gbiodir; do
							DSTL="$DSTPFX/$DRCU/$STUS/$DOCS/$BIOW/${gbiodir##*/}"
							prepModGameSym "$DSTL" "$gbiodir"
						done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$DOCS/$BIOW" -mindepth 1 -maxdepth 1 '(' -type d -o -type l -xtype d ')')"
					else
						DSTL="$DSTPFX/$DRCU/$STUS/$DOCS/${gdir##*/}"
						prepModGameSym "$DSTL" "$gdir"
					fi
				done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$DOCS" -mindepth 1 -maxdepth 1 -not -empty '(' -type d -o -type l -xtype d ')')"
			fi

			if [ -d "$SRCPFX/$DRCU/$STUS/$MYDOCS" ] && [ ! -L "$SRCPFX/$DRCU/$STUS/$MYDOCS" ]; then
				while read -r gdir; do
					if [ "${gdir##*/}" == "$MYGAMES" ]; then
						while read -r gsdir; do
							DSTL="$DSTPFX/$DRCU/$STUS/$DOCS/$MYGAMES/${gsdir##*/}"
							prepModGameSym "$DSTL" "$gsdir"
						done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$MYDOCS/$MYGAMES" -mindepth 1 -maxdepth 1 '(' -type d -o -type l -xtype d ')')"
					elif [ "${gdir##*/}" == "$EAGA" ]; then
						mkProjDir "$DSTPFX/$DRCU/$STUS/$DOCS/$EAGA"
						while read -r geadir; do
							DSTL="$DSTPFX/$DRCU/$STUS/$DOCS/$EAGA/${geadir##*/}"
							prepModGameSym "$DSTL" "$geadir"
						done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$MYDOCS/$EAGA" -mindepth 1 -maxdepth 1 '(' -type d -o -type l -xtype d ')')"
					elif [ "${gdir##*/}" == "$LAGA" ]; then
						mkProjDir "$DSTPFX/$DRCU/$STUS/$DOCS/$LAGA"
						while read -r gladir; do
							DSTL="$DSTPFX/$DRCU/$STUS/$DOCS/$LAGA/${gladir##*/}"
							prepModGameSym "$DSTL" "$gladir"
						done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$MYDOCS/$LAGA" -mindepth 1 -maxdepth 1 '(' -type d -o -type l -xtype d ')')"
					elif [ "${gdir##*/}" == "$BIOW" ]; then
						mkProjDir "$DSTPFX/$DRCU/$STUS/$DOCS/$BIOW"
						while read -r gbiodir; do
							DSTL="$DSTPFX/$DRCU/$STUS/$DOCS/$BIOW/${gbiodir##*/}"
							prepModGameSym "$DSTL" "$gbiodir"
						done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$DOCS/$BIOW" -mindepth 1 -maxdepth 1 '(' -type d -o -type l -xtype d ')')"
					else
						DSTL="$DSTPFX/$DRCU/$STUS/$DOCS/${gdir##*/}"
						prepModGameSym "$DSTL" "$gdir"
					fi
				done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$MYDOCS" -mindepth 1 -maxdepth 1 -not -empty '(' -type d -o -type l -xtype d ')')"
			fi

			if [ -d "$SRCPFX/$DRCU/$STUS/$APDA" ]; then
				while read -r gdir; do
					if [ "${gdir##*/}" != "Microsoft" ] ; then
						DSTL="$DSTPFX/$DRCU/$STUS/$ADRO/${gdir##*/}"
						prepModGameSym "$DSTL" "$gdir"
					fi
				done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$APDA" -mindepth 1 -maxdepth 1 -not -empty '(' -type d -o -type l -xtype d ')')"
			fi

			if [ -d "$SRCPFX/$DRCU/$STUS/$ADLO" ]; then
				while read -r gdir; do
					if [ "${gdir##*/}" != "openvr" ] && [ "${gdir##*/}" != "Microsoft" ] && [ "${gdir##*/}" != "cache" ] ; then
						if [ "${gdir##*/}" == "$MO" ] && [ ! -L "$gdir" ]; then
							writelog "WARN" "${FUNCNAME[0]} - Renaming old real directory '$gdir' to '${gdir}_OLD', to make place for a symlink into '$DSTPFX'"
							mv "$gdir" "${gdir}_OLD"
						else
							DSTL="$DSTPFX/$DRCU/$STUS/$ADLO/${gdir##*/}"
							prepModGameSym "$DSTL" "$gdir"
						fi
					fi
				done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$ADLO" -mindepth 1 -maxdepth 1 -not -empty '(' -type d -o -type l -xtype d ')')"
			fi

			if [ -d "$SRCPFX/$DRCU/$STUS/$ADLOLO" ]; then
				while read -r gdir; do
					if [ "${gdir##*/}" != "openvr" ] && [ "${gdir##*/}" != "Microsoft" ] ; then
						if [ "${gdir##*/}" == "$MO" ] && [ ! -L "$gdir" ]; then
							writelog "WARN" "${FUNCNAME[0]} - Renaming old real directory '$gdir' to '${gdir}_OLD', to make place for a symlink into '$DSTPFX'"
							mv "$gdir" "${gdir}_OLD"
						else
							DSTL="$DSTPFX/$DRCU/$STUS/$ADLOLO/${gdir##*/}"
							prepModGameSym "$DSTL" "$gdir"
						fi
					fi
				done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$ADLOLO" -mindepth 1 -maxdepth 1 -not -empty '(' -type d -o -type l -xtype d ')')"
			fi

			if [ -d "$SRCPFX/$DRCU/$STUS/$ADLO/LOOT" ]; then
				while read -r gdir; do
					DSTL="$MODLOOT/${gdir##*/}"
					prepModGameSym "$DSTL" "$gdir"
				done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$ADLO/LOOT" -mindepth 1 -maxdepth 1 -not -empty '(' -type d -o -type l -xtype d ')')"
			fi

			if [ -d "$SRCPFX/$DRCU/$STUS/$LSAD" ]; then
				while read -r gdir; do
					if [ "${gdir##*/}" != "openvr" ] && [ "${gdir##*/}" != "Microsoft" ] ; then
						DSTL="$DSTPFX/$DRCU/$STUS/$ADLO/${gdir##*/}"
						prepModGameSym "$DSTL" "$gdir"
					fi
				done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$LSAD" -mindepth 1 -maxdepth 1 -not -empty '(' -type d -o -type l -xtype d ')')"
			fi

			if [ -d "$SRCPFX/$DRCU/$STUS/$SAGE/$CDPR" ]; then
				mkProjDir "$DSTPFX/$DRCU/$STUS/$SAGE/$CDPR"
				while read -r gdir; do
						DSTL="$DSTPFX/$DRCU/$STUS/$SAGE/$CDPR/${gdir##*/}"
						prepModGameSym "$DSTL" "$gdir"
				done <<< "$(find -L "$SRCPFX/$DRCU/$STUS/$SAGE/$CDPR" -mindepth 1 -maxdepth 1 -not -empty '(' -type d -o -type l -xtype d ')')"
			fi

			MSREG="$SRCPFX/$SREG"
			if [ -f "$MSREG" ]; then
				if grep -qi "^\"installed path\"=" "$MSREG"; then
					MODGREG="$STLSHM/modgames.reg"
					if [ ! -f "$MODGREG" ]; then
						echo "Windows Registry Editor Version 5.00" > "$MODGREG"
					fi
					MEFD="$(getGameDirFromAM "$CHKAPMA")"
					if [ -d "$MEFD" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Game Dir is '$MEFD' - adding the windows variant into the registry, because it is required for this game"
						grep -i -B2 "^\"installed path\"=" "$MSREG" | grep -v "^#\|LastKey\|CurrentVersion\|^--" | head -n1 | sed "s:^\[Software:\[HKEY_LOCAL_MACHINE\\\Software:" | sed "s:\\\\Wow6432Node::" >> "$MODGREG"
						# the path could be grepped as well, but at least two games had the Steamworks Shared path in the reg value instead here
						printf "\"installed path\"=\"Z:%s\\\\\"\n" "${MEFD//\//\\\\}" >> "$MODGREG"
					else
						writelog "SKIP" "${FUNCNAME[0]} - Game Dir '$MEFD' not found"
					fi
				fi
			else
				writelog "SKIP" "${FUNCNAME[0]} - '$MSREG' not found"
			fi
		fi
	fi
}

function listWinSteamLibraries {
	COUNTER=1
	SL1="${SROOT%*/}"
	echo "\"$COUNTER\" \"Z:${SL1//\//\\\\}\""

	listSteamLibraries

	unset SLARR
	mapfile -t -O "${#SLARR[@]}" SLARR <<< "$SL1"

	while read -r line; do
		COUNTER=$((COUNTER+1))
		W1="${line//\"}"
		W1="${line//\/steamapps/}"
		mapfile -t -O "${#SLARR[@]}" SLARR <<< "$W1"
		W2="${W1//\//\\\\}"
		echo "\"$COUNTER\" \"Z:$W2\""
	done < "$STELILIST"
}

function installDotNet {
	INSTPFX="$1"
	INSTWINE="$2"
	if [ -n "$3" ]; then
		DNVER="$3"
	else
		DNVER="48"
	fi
	LOGSUFFIX=" ${4}"
	DNFORCE="$5"
	chooseWinetricks
	ILOG="$STLSHM/installDotNet.log"
	DNLOGPRETTYNAME="${ILOG}${LOGSUFFIX}"  # Mainly used for HMM game install logs
	rm "${ILOG}${LOGNAME}" 2>/dev/null

	# Experiment -- Add option to 'force' dotnet install (idea here is to fix dotnet48 failing)
	# Adding --force will force it to always be installed for a given prefix, even if it is already installed  -- May not even fix issues, but could
	writelog "INFO" "${FUNCNAME[0]} - Starting $DOTN$DNVER install - check ${DNLOGPRETTYNAME}"
	if [ -z "$DNFORCE" ]; then
		writelog "INFO" "${FUNCNAME[0]} - WINEDEBUG=\"-all\" WINEPREFIX=\"$INSTPFX\" WINE=\"$INSTWINE\" \"$WINETRICKS\" --unattended \"$DOTN$DNVER\""

		WINEDEBUG="-all" WINEPREFIX="$INSTPFX" WINE="$INSTWINE" "$WINETRICKS" --unattended "$DOTN$DNVER" >> "${DNLOGPRETTYNAME}"
	else
		writelog "INFO" "${FUNCNAME[0]} - WINEDEBUG=\"-all\" WINEPREFIX=\"$INSTPFX\" WINE=\"$INSTWINE\" \"$WINETRICKS\" --force --unattended \"$DOTN$DNVER\""

		WINEDEBUG="-all" WINEPREFIX="$INSTPFX" WINE="$INSTWINE" "$WINETRICKS" --force --unattended "$DOTN$DNVER" >> "${DNLOGPRETTYNAME}"
	fi

	# WINEDEBUG="-all" WINEPREFIX="$INSTPFX" WINE="$INSTWINE" "$WINETRICKS" --unattended "$DOTN$DNVER" >> "${ILOG}${LOGSUFFIX}"
	writelog "INFO" "${FUNCNAME[0]} - Stopped $DOTN$DNVER install - check ${DNLOGPRETTYNAME}"
}

function updateWineRegistryKey {
	REGOP="$1"  # i.e. "add", "delete"
	REGPATH="$2"
	REGVAL="$3"
	WINEPFX="$4"
	WINERUNCMD="$5"

	writelog "INFO" "${FUNCNAME[0]} - Removing key '$REGVAL' from '$REGPATH' using '$WINERUNCMD'"
	WINEPREFIX="$WINEPFX" "$WINERUNCMD" reg "${REGOP}" "${REGPATH}" "/v" "${REGVAL}" "/f"
}

### HEDGEMODMANAGER (HMM) BEGIN

# NOTE: This was written with HMM in mind, but it may work generally too
# Returns the download URL to the latest GitHub actions build of a repository and the commit SHA it was built from separated by a comma
#
# EX: DefinitelyNotValve/HalfLife2EpisodeThree/suites/12345678/artifacts/87654321;0df23c5
function fetchLatestGitHubActionsBuild {
    PROJAPIURL="$1"
    ARTIFACTNUM=$2
	WORKFLOWNAME="$3"
	ALLOWFAILEDWORKFLOWS="${4:-1}"  # default to allow failed builds

    # /actions/runs
	ARTIFACTRUNSRESPJQ=".workflow_runs"
	## optionally filter by workflow name if we give one
	if [ -n "$WORKFLOWNAME" ]; then
		ARTIFACTRUNSRESPJQ="[ ${ARTIFACTRUNSRESPJQ}[] | select(.name==\"${WORKFLOWNAME}\")]"
    fi
	## optionally filter to only success builds
	if [ "$ALLOWFAILEDWORKFLOWS" -eq 0 ]; then
		ARTIFACTRUNSRESPJQ="[ ${ARTIFACTRUNSRESPJQ}[] | select (.conclusion==\"success\") ]"  # conclusion can be success/failure
	fi
	ARTIFACTRUNSRESPJQ="${ARTIFACTRUNSRESPJQ} | first"
	ARTIFACTRUNSRESP="$( curl -s "${PROJAPIURL}/actions/runs" | "$JQ" "${ARTIFACTRUNSRESPJQ}" )"
	ARTIFACTRUNSUITEID="$( echo "$ARTIFACTRUNSRESP" | "$JQ" '.check_suite_id' )"
    LATESTARTIFACTURL="$( echo "$ARTIFACTRUNSRESP" | "$JQ" '.artifacts_url' | cut -d '"' -f 2 )"

    # /actions/runs/<artifact_id>/artifacts
	LATESTARTIFACTRESP="$( curl -s "${LATESTARTIFACTURL}" | "$JQ" ".artifacts[${ARTIFACTNUM}]" )"
	LATESTARTIFACTID="$( echo "$LATESTARTIFACTRESP" | "$JQ" ".id" )"
    LATESTARTIFACTSHA="$( echo "$LATESTARTIFACTRESP" | "$JQ" ".workflow_run.head_sha" | cut -d '"' -f 2 )"

    ARTIFACTDLURL="suites/${ARTIFACTRUNSUITEID}/artifacts/${LATESTARTIFACTID}"
    echo "${ARTIFACTDLURL};${LATESTARTIFACTSHA::7}"
}

# HMM doesn't really have a "setup" exe, but MO2 and Vortex use this naming convention so... keeping it :-)
function getLatestHMMVer {
	HMMSET="HedgeModManager"

	writelog "INFO" "${FUNCNAME[0]} - Searching for latest '$HMMSET' Release under '$HMMPROJURL'"
	HMMSETUP="$( getLatestGitHubExeVer "HedgeModManager" "https://github.com/thesupersonic16/HedgeModManager" )"
	if [ -n "$HMMSETUP" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Found '$HMMSETUP'"
	else
		writelog "ERROR" "${FUNCNAME[0]} - Could not find any '$HMMSET' Release"
	fi
}

function checkLatestHMM {
	AVAILABLEHMMVER="$1"
	HMMSPATH="$2"

	HMMUPDATEAVAILABLE=0

	# Auto-update executable - May not be needed if HMM auto updater works
	if [ -f "$HMMVERFILE" ]; then
		CURRHMMVER="$( head -n 1 "$HMMVERFILE" )"
		if [[ "$CURRHMMVER" = "$AVAILABLEHMMVER" ]]; then
			writelog "INFO" "${FUNCNAME[0]} - Latest HedgeModManager already downloaded - Nothing to do"  # Probably have to do a version check here later
			echo "Latest HedgeModManager is already downloaded or was downloaded previously"
		else
			writelog "INFO" "${FUNCNAME[0]} - HedgeModManager update is available ($CURRHMMVER -> $AVAILABLEHMMVER) - Updating"
			echo "HedgeModManager update is available ($CURRHMMVER -> $AVAILABLEHMMVER) - Updating"
			if [ -f "$HMMSPATH" ]; then
				rm "$HMMSPATH"
			else
				writelog "INFO" "${FUNCNAME[0]} - HedgeModManager executable doesn't exist - nothing to remove before updating"  # Could happen if EXE is removed but version tracking file is not
				echo "No existing HedgeModManager executable"
			fi
			HMMUPDATEAVAILABLE=1
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - No HedgeModManager version file found at '$HMMVERFILE' - Nothing to check, assuming we need to update"
		echo "Could not get existing HedgeModManager version - Downloading latest '$AVAILABLEHMMVER'"
		HMMUPDATEAVAILABLE=1
	fi
}

function dlLatestHMM {

	function dlLatestHMMDev {
		HMMARCHIVENAME="${HMM}-Release.zip"
		HMMARCHIVEPATH="$HMMDLDIR/${HMMARCHIVENAME}"

		HMMSETUP="${HMM}.exe"
		HMMSETUPBASE="$( basename "${HMMSETUP}" )"  # Doing this in case "HMMSETUP" ever changes
		HMMSPATH="$HMMDLDIR/$HMMSETUP"

		HMMUPDATEAVAILABLE=0

		HMMAPIURLPATH="${HMMPROJURL//$GHURL}"
		HMMLATESTDEV="${HMMPROJURL}/$( fetchLatestGitHubActionsBuild "${AGHURL}/repos${HMMAPIURLPATH}" 1 )"  # Used to get the latest dev version SHA
		HMMLATESTDEVURL="https://nightly.link${HMMAPIURLPATH}/workflows/build/rewrite/${HMMARCHIVENAME}"  # Use nightly.link to get latest artifact download link for HMM
		HMMVER="$( echo "$HMMLATESTDEV" | cut -d ";" -f 2 )"

		writelog "INFO" "${FUNCNAME[0]} - HedgeModManager artifact URL: '$HMMLATESTDEVURL' ($HMMVER)"

		checkLatestHMM "$HMMVER" "$HMMSPATH"

		mkProjDir "$HMMDLDIR"
		if [ ! -f "$HMMSPATH" ] || [ "$HMMUPDATEAVAILABLE" -eq 1 ]; then
			# No exe OR we need to update, check if we have the archive to extract it from
			writelog "INFO" "${FUNCNAME[0]} - Either no HedgeModManager executable downloaded and extracted, or there is an update available - HMMUPDATEAVAILABLE is '${HMMUPDATEAVAILABLE}'"
			if [ ! -f "$HMMARCHIVEPATH" ] || [ "$HMMUPDATEAVAILABLE" -eq 1 ]; then
				# (No archive and no exe) or update available, download from GitHub
				writelog "INFO" "${FUNCNAME[0]} - Either no HedgeModManager artifact present, or an update is available - HMMUPDATEAVAILABLE is '${HMMUPDATEAVAILABLE}'"

				# Download
				writelog "INFO" "${FUNCNAME[0]} - Downloading latest HedgeModManager Development Artifact from URL '$HMMLATESTDEVURL'"
				echo "Downloading latest HedgeModManager Development Artifact"
				notiShow "$(strFix "$NOTY_HMMDL" "$HMMDLVER")" "X"
				dlCheck "$HMMLATESTDEVURL" "$HMMARCHIVEPATH" "X" "Downloading latest HedgeModManager Development '$HMMVER'" &>/dev/null
			else
				# We have existing archive but no executable
				writelog "INFO" "${FUNCNAME[0]} - Found existing and up-to-date HedgeModManager archive at '$HMMSPATH' - Extracting"
				echo "Found up-to-date HedgeModManager Development release archive - Extracting "
			fi

			# Extract
			# Check if download success
			if [ -f "$HMMARCHIVEPATH" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Downloaded latest HedgeModManager Artifact - Extracting archive at '$HMMARCHIVEPATH'"
				echo "Extracting latest HedgeModManager Development Artifact"
				"$UNZIP" -qo "$HMMARCHIVEPATH" -d "$HMMDLDIR"  # Extract quiet and overwrite existing file if present without confirmation

				# If download sucess, try to extract
				if [ -f "$HMMSPATH" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Successfully extracted HedgeModManager archive at '$HMMSPATH'"
					echo "Successfully extracted HedgeModManager archive"
					writelog "Info" "${FUNCNAME[0]} - Removing archive '$HMMARCHIVENAME' after successful extraction"
					rm "$HMMARCHIVEPATH"
					echo "$HMMVER" > "$HMMVERFILE"  # Update version on successfull download and extract
				else
					# Download failed
					writelog "WARN" "${FUNCNAME[0]} - Failed to extract HedgeModManager archive at '$HMMSPATH'"
					echo "Failed to extract HedgeModManager archive"
				fi
			else
				# Download failed
				writelog "WARN" "${FUNCNAME[0]} - Failed to download latest HedgeModManager Development archive ('$HMMVER') to '$( basename "$HMMSETUP" )'"
				echo "Failed to download latest HedgeModManager development release archive"
			fi
		else
			# We have executable - Nothing to do
			writelog "INFO" "${FUNCNAME[0]} - Found existing and up-to-date HedgeModManager executable at '$HMMSPATH'"
			echo "Found up-to-date HedgeModManager executable - Nothing to download"
		fi
	}

	# Download latest HedgeModManager stable release
	function dlLatestHMMStable {
		getLatestHMMVer

		# These values return the version URL from GitHub, so when we called "basename" we're getting the URL basename
		# The format that comes back is something like /releases/7.8-2/HedgeModManager.exe
		if [ -n "$HMMSETUP" ]; then
			mkProjDir "$HMMDLDIR"
			HMMSETUPBASE="$( basename "$HMMSETUP" )"
			HMMVER="$( dirname "$HMMSETUP" )"
			HMMVER="${HMMVER##*/}"
			HMMSPATH="$HMMDLDIR/$HMMSETUPBASE"

			echo "Latest available version is '$HMMVER' - Checking to see if we are up-to-date"
			checkLatestHMM "$HMMVER" "$HMMSPATH"

			if [ ! -f "$HMMSPATH" ] || [ "$HMMUPDATEAVAILABLE" -eq 1 ]; then
				DLURL="${HMMPROJURL//"HedgeModManager"}$HMMSETUP"
				writelog "INFO" "${FUNCNAME[0]} - Downloading '$HMMSETUPBASE' to '$( basename "$HMMDLDIR" )' from '$DLURL'"
				echo "Downloading HedgeModManager ${HMMVER}"
				notiShow "$(strFix "$NOTY_HMMDL" "$HMMVER")" "X"
				dlCheck "$DLURL" "$HMMSPATH" "X" "Downloading HedgeModManager $HMMSETUP $HMMVER" &>/dev/null
				if [ -f "$HMMSPATH" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Successfully downloaded HedgeModManager $HMMVER - continuing installation"
					echo "Successfully downloaded HedgeModManager $HMMVER to '$( basename "$HMMSETUP" )'"
					echo "$HMMVER" > "$HMMVERFILE"
				else
					writelog "ERROR" "${FUNCNAME[0]} - Failed to download HedgeModManager from '$DLURL'"
					echo "Failed to download HedgeModManager"
				fi
			else
				writelog "INFO" "${FUNCNAME[0]} - HedgeModManager executable already downloaded - Nothing to do"
				echo "HedgeModManager is up-to-date"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - No HMMSETUP defined - nothing to download - skipping"
			echo "Could not find HedgeModManager release"
		fi
	}

	# Internet connection check
	if ! ping -q -c1 github.com &>/dev/null; then
		writelog "WARN" "${FUNCNAME[0]} - Looks like we're offline or GitHub is down, not attempting to download HedgeModManager when offline - May cause issues if no HMM exe is downloaded!"
		writelog "WARN" "${FUNCNAME[0]} - Will still attempt to install Winetricks as they may be cached"
		echo "WARNING: Can't reach GitHub - Not attempting to download HedgeModManager"

		# Set HMMVER offline if we have it in the HMMVERFILE + some warning logging if there is no version file
		if [ -f "$HMMVERFILE" ]; then
			HMMVER="$( head -n 1 "$HMMVERFILE" )"
			if [ -z "$HMMVER" ]; then
				writelog "WARN" "${FUNCNAME[0]} - HedgeModManager version stored in HMMVERFILE at '$HMMVERFILE' appears to be empty, installation may have failed last time!"
			else
				writelog "INFO" "${FUNCNAME[0]} - HedgeModManager version stored in HMMVERFILE is '$HMMVER' - It seems like HedgeModManager successfully installed before, so running offline should work fine"
			fi

			if [ -f "$HMMDLDIR/${HMM}.exe" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Offline HedgeModManager executable found in '$HMMDLDIR/${HMM}.exe' - Assuming that this is a valid, pre-downloaded HMM executable that the user placed for offline use"
				HMMSPATH="$HMMDLDIR/$HMMSETUP"
			else
				writelog "WARN" "${FUNCNAME[0]} - No Offline HedgeModManager executable found in '$HMMDLDIR' with name '${HMM}.exe' - Installation will probably not work when we get to 'installHMM' stage!"
			fi
		else
			writelog "WARN" "${FUNCNAME[0]} - No HMMVERFILE found at '$HMMVERFILE' - HedgeModManager may not have been installed and so may fail to start offline!"
		fi
	fi

	DLVER="$1"
	if [ -z "$DLVER" ]; then
		writelog "INFO" "${FUNCNAME[0]} - No value passed for whether user wants stable or development HedgeModManager - Assuming they want stable!"
	fi
	writelog "INFO" "${FUNCNAME[0]} - User wants HedgeModManager '${DLVER:-stable}'"

	# This will come from desktop file usually
	if [[ "$DLVER" = "$HMMAUTO" ]]; then
		DLVER="$HMMDLVER"
	fi

	if [[ "$DLVER" = "$HMMDEV" ]]; then
		# Get latest GitHub artifacts ver
		writelog "INFO" "${FUNCNAME[0]} - Checking latest available HedgeModManager Development version"
		echo "Checking latest available HedgeModManager Development version"

		dlLatestHMMDev
	else
		# If we don't pass dev, assume we want stable
		writelog "INFO" "${FUNCNAME[0]} - Checking latest available HedgeModManager Release/Stable version"
		echo "Checking latest available HedgeModManager Release version"

		dlLatestHMMStable
	fi
}

# NOTE: Winetricks *needs* GE-Proton to install dotnet48 correctly!
function setHMMVars {
	HMMPFX="${HMMCOMPDATA}/pfx"
	if [ -z "$HMMEXE" ]; then
		HMMEXE="$HMMDLDIR/${HMM}.exe"
		writelog "INFO" "${FUNCNAME[0]} - HMM EXE was not set - It is now '$HMMEXE'"
	fi
	HMMGAMES="$GLOBALMISCDIR/hmmgames.txt"
	if [ -z "$HMMWINE" ] || [ ! -f "$HMMWINE" ]; then
		if [ "$USEHMMPROTON" == "$NON" ]; then
			if [ ! -f "$PROTONCSV" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Looking for available Proton versions"
				getAvailableProtonVersions "up" X
			fi

			if ! grep -q "^GE" "$PROTONCSV"; then
				writelog "INFO" "${FUNCNAME[0]} - calling autoBumpGE"
				autoBumpGE "X"
			else
				writelog "INFO" "${FUNCNAME[0]} - Seems like we have a GE-Proton version available already"
			fi

			SETHMMPROT="$(grep "^GE" "$PROTONCSV" | sort -V | tail -n1)"

			SETHMMPROT="${SETHMMPROT%%;*}"

			USEHMMPROTON="$SETHMMPROT"

			touch "$FUPDATE"
			updateConfigEntry "USEHMMPROTON" "$USEHMMPROTON" "$STLDEFGLOBALCFG"
			writelog "INFO" "${FUNCNAME[0]} - USEHMMPROT is '$NON', so using latest GE-Proton"
		else
			writelog "INFO" "${FUNCNAME[0]} - USEHMMPROTON was set -- it is '$USEHMMPROTON'"
			SETHMMPROT="$USEHMMPROTON"
		fi

		# Sometimes ProtonCSV can have a version of GE-Proton that doesn't actually exist (or that used to exist, but doesn't anymore)
		#
		# This check forces it to look in the file in a different order (reverse version sort order `sort -Vr`) which should get it to find GE-Proton
		# and updates the relevant HMM Proton/Wine variables + writes out to config file
		#
		# The real fix here is to correctly re-populate the Proton versions in ProtonCSV.txt so that STL never tries to use an invalid Proton version to begin with!
		#
		# This check could be much cleaner, but the check is here outside of the above check to force the Proton value to get updated for any existing users
		if [ ! -f "$( getProtPathFromCSV "$SETHMMPROT" )" ]; then
			writelog "WARN" "${FUNCNAME[0]} - SETHMMPROT was not a directory -- Attempting to find it again"
			SETHMMPROT="$(grep "^GE" "$PROTONCSV" | sort -Vr | tail -n1)"
			SETHMMPROT="${SETHMMPROT%%;*}"

			if [ ! -f "$( getProtPathFromCSV "$SETHMMPROT" )" ]; then
				writelog "ERROR" "${FUNCNAME[0]} - Still could not find GE-Proton in PROTONCSV - This should be reported!"
			else
				writelog "INFO" "${FUNCNAME[0]} - SETHMMPROT was updated to value '$SETHMMPROT'"
				USEHMMPROTON="$SETHMMPROT"

				touch "$FUPDATE"
				updateConfigEntry "USEHMMPROTON" "$USEHMMPROTON" "$STLDEFGLOBALCFG"
			fi
		fi

		setModWine "SETHMMPROT" "HMMRUNPROT" "HMMWINE"
	fi
}

# Doesn't really "install" HMM, just creates the prefix for it if it doesn't already exist
# HMM's UI needs `dotnet48` to run and `d3dx9 vcrun2019 d3dcompiler_47` to render - we don't want to install this for every game, give HMM its own prefix to run in
# We can remove these if HMM ever works with Wine out of the box
function installHMM {
	if [ -f "$HMMSPATH" ]; then
		setHMMVars
		chooseWinetricks  # Force install of Winetricks earlier to prevent potential missing Winetricks var
		if [ -f "$HMMEXE" ]; then
			writelog "INFO" "${FUNCNAME[0]} - HedgeModManager executable found at '$HMMEXE' - Checking if we need to set up its Wine prefix"
			if [ ! -d "$HMMCOMPDATA" ]; then
				writelog "INFO" "${FUNCNAME[0]} - No existing HedgeModManager prefix found - Creating one"
				echo "Installing HedgeModManager $HMMVER"
				notiShow "$( strFix "$NOTY_HMMINST" "$HMMVER" )" "X"
				mkProjDir "$HMMCOMPDATA/pfx"

				# Install dotnet48
				writelog "INFO" "${FUNCNAME[0]} - Installing dotnet48 for $HMM"
				installDotNet "$HMMPFX" "$HMMWINE" "48" "HMMPREFIX" "X"  # 'X' here is to enable the experimental '--force' parameter
				writelog "INFO" "${FUNCNAME[0]} - Done"

				# Setup prefix with Proton
				touch "${HMMCOMPDATA}/tracked_files"
				STEAM_COMPAT_CLIENT_INSTALL_PATH="$SROOT" STEAM_COMPAT_DATA_PATH="$HMMCOMPDATA" "$HMMRUNPROT" "run" 2> "$STLSHM/${FUNCNAME[0]}_protonrun.log"

				# Install other needed Winetricks
				writelog "INFO" "${FUNCNAME[0]} - Installing 'd3dx9' 'vcrun2019' 'd3dcompiler_47' with '$HMMWINE' - These extra Winetricks are needed for HedgeModManager to run as well"
				OGGPFX="$GPFX"
				GPFX="$HMMPFX"
				RUNWINE="$HMMWINE"
				installWinetricksPaks "d3dx9 vcrun2019 d3dcompiler_47" "$HMMWINE" "extWine64Run"

				writelog "INFO" "${FUNCNAME[0]} - Installing extra 7zip dependency -- Experimental, may not work"
				installWinetricksPaks "7zip" "$HMMWINE" "extWine64Run"

				GPFX="$OGGPFX"
				writelog "INFO" "${FUNCNAME[0]} - Finished installing extra HedgeModManager winetricks"

				configureHMMPfxReg &>/dev/null

				writelog "INFO" "${FUNCNAME[0]} - Finished setting up HedgeModManager prefix"
				echo "Successfully installed HedgeModManager $HMMVER"
				notiShow "$NOTY_HMMINSTFIN" "X"
			else
				writelog "SKIP" "${FUNCNAME[0]} - HedgeModManager prefix already exists - Not recreating - Skipping installation"

				configureHMMPfxReg &>/dev/null  # The registry configuration needed may change overtime and may not have been fully configured at installation

				echo "Finished installing HedgeModManager"
			fi
		else
			writelog "ERROR" "${FUNCNAME[0]} - HedgeModManager '$HMMEXE' went missing - Maybe user is connected to the Internet"
			echo "HedgeModManager executable '$HMMEXE' went missing or was never downloaded to begin with - Are you connected to the Internet?"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - '$HMMSPATH' not found - Nothing to install - skipping"
	fi

	# Create .desktop file for 1-click install support and adding HMM to application menu
	writelog "INFO" "${FUNCNAME[0]} - Adding HedgeModManager to application menu and setting up link handlers"
	echo "Adding HedgeModManager to application menu and setting up link handlers"
	createHMMDesktopFile
}

# Remove Steam reg keys - needed to get HMM to find games with our prefix
function configureHMMPfxReg {
	writelog "INFO" "${FUNCNAME[0]} - Removing extra Steam registry keys from HedgeModManager prefix '$HMMPFX'"

	# Reg paths
	HKLMSTEAM="HKEY_LOCAL_MACHINE\\Software\\Wow6432Node\\Valve\\Steam"
	HKCUSTEAM="HKEY_CURRENT_USER\\Software\\Valve\\Steam"
	KHCUSTEAMACTPRO="${HKCUSTEAM}\\ActiveProcess"

	# Reg values
	STEXE="SteamExe"
	STPA="SteamPath"
	INSTPA="InstallPath"
	STCLIDLL="SteamClientDll"
	STCLIDLL64="SteamClientDll64"

	# HKEY_LOCAL_MACHINE
	updateWineRegistryKey "delete" "$HKLMSTEAM" "$INSTPA" "$HMMPFX" "$HMMWINE"

	# KEY_CURRENT_USER
	updateWineRegistryKey "delete" "$HKCUSTEAM" "$STEXE" "$HMMPFX" "$HMMWINE"
	updateWineRegistryKey "delete" "$HKCUSTEAM" "$STPA" "$HMMPFX" "$HMMWINE"

	# ActiveProcess
	updateWineRegistryKey "delete" "$KHCUSTEAMACTPRO" "$STPA" "$HMMPFX" "$HMMWINE"
	updateWineRegistryKey "delete" "$KHCUSTEAMACTPRO" "$STCLIDLL" "$HMMPFX" "$HMMWINE"
	updateWineRegistryKey "delete" "$KHCUSTEAMACTPRO" "$STCLIDLL64" "$HMMPFX" "$HMMWINE"

	writelog "INFO" "${FUNCNAME[0]} - Finished removing registry keys from HedgeModManager prefix"
}

# Game-specific Winetricks
function prepareHMMGameWinetricks {
	# HMMGTWEAKAID="$1"  # Game AppID
	HMMGINSTPFX="$2"  # Prefix to install winetricks to (i.e. game prefix)

	# Set GPFX to game's prefix - Can't guarantee current GPFX will be the game's prefix so force it to ensure winetrick(s) are installed to the game's prefix and not HMM's prefix
	OGGPFX="$GPFX"
	GPFX="$HMMGINSTPFX"
	writelog "SKIP" "${FUNCNAME[0]} - No known tweaks needed for HMM mods currently - skipping"
	GPFX="$OGGPFX"
}

# Install dotnet48 for every 64bit game
function prepareHMMGames {
	setHMMVars

	# Get all hardcoded HMM supported games
	writelog "INFO" "${FUNCNAME[0]} - Reading all hardcoded HedgeModManager supported games from '$HMMGAMES'"
	echo "Setting up installed HedgeModManager compatible games"
	notiShow "$NOTY_HMMCONFIG" "X"
	while read -r HMMG; do
		# Get HMM game information from hmmgames.txt lines - games are stored as: "game name";appid;"architecture"
		HMMGN="$( echo "$HMMG" | cut -d ";" -f 1 | cut -d '"' -f2 )"
		HMMGAID="$( echo "$HMMG" | cut -d ";" -f 2 | cut -d '"' -f2 )"
		HMMGARCH="$( echo "$HMMG" | cut -d ";" -f 3 | cut -d '"' -f2 )"

		# Sometimes on Steam Deck it seems like the Wineprefix is created in a different library folder than the one the game is installed in
		# Might be a Steam Client bug, but if it persists / if this is the new behaviour, this check may need updated!
		HMMGAPPMA="$( listAppManifests | grep -m1 "${HMMGAID}.acf" )"
		HMMGPFX="$( setGPfxFromAppMa "$HMMGAID" "$HMMGAPPMA" )"
		if [ ! -d "$HMMGPFX" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Wineprefix for game '$HMMGN ($HMMGAID)' did not already exist at '$HMMGPFX' - The game may not have been started before!"
		fi

		# If HMM game is 64bit, installed and has a compatdata dir, install dotnet48 for that game
		if [[ "$HMMGARCH" = "64" ]]; then
			GPFX="$HMMPFX"

			# Check if we have a valid compatdata for a game (pfx/drive_c/users/steamuser)
			GPFXSTUS="$HMMGPFX/$DRCU/$STUS"
			if [ -d "$GPFXSTUS" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Found compatdata dir for '$HMMGN' at '$GPFXSTUS' - Assuming it is installed"

				writelog "INFO" "${FUNCNAME[0]} - Install dotnet48 for install 64bit HedgeModManager game '$HMMGN'"
				notiShow "$( strFix "$NOTY_HMMGAMCONFIG" "$HMMGN" )" "X"
				if [ -z "$1" ]; then
					echo "Running configuration for '$HMMGN'"
					installDotNet "$HMMGPFX" "$HMMWINE" "48" "$HMMGN" &>/dev/null
					writelog "INFO" "${FUNCNAME[0]} - Finished installing dotnet48 for '$HMMGN'"
				else
					echo "Running configuration for '$HMMGN' using --force"
					installDotNet "$HMMGPFX" "$HMMWINE" "48" "$HMMGN" "X" &>/dev/null
					writelog "INFO" "${FUNCNAME[0]} - Finished installing dotnet48 for '$HMMGN' using --force"
				fi
			else
				writelog "SKIP" "${FUNCNAME[0]} - Could not find compatdata dir for '$HMMGN' - Assuming that it is not installed"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - '$HMMGN' is 32bit, skipping"
		fi

		writelog "INFO" "${FUNCNAME[0]} - Checking if we need to apply any game-specific tweaks to improve mod compatibility"
		prepareHMMGameWinetricks "$HMMGAID" "$HMMGPFX"
	done <"$HMMGAMES"
	echo "Finished configuring installed HedgeModManager games"
}

function startHMM {
	# TODO HMM button on UI somewhere in future? Not sure
	HMMDLVERARG="$1"
	HMMDOTNETFORCE="$2"

	dlLatestHMM "$HMMDLVERARG"
	installHMM

	# If this variable has *any* value, `--force` will be added to the installDotNet for each HMM game - This essentially forces a reinstall of dotnet for each game on each HMM bootup
	prepareHMMGames "$HMMDOTNETFORCE"

	writelog "INFO" "${FUNCNAME[0]} - Starting HedgeModManager in prefix '$HMMPFX' with Wine '$HMMWINE' and using executable at '$HMMEXE'"
	echo "Starting HedgeModManager"
	notiShow "$( strFix "$NOTY_HMMSTART" "$HMMVER" )" "X"

	WINEDEBUG="-all" WINEPREFIX="$HMMPFX" "$HMMWINE" "$HMMEXE" "$HMMARGS" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}.log"
}

# This will only remove the HMM Wineprefix and download executable files -- It will not remove any installed Winetricks or mods
function uninstallHMM {
	writelog "INFO" "${FUNCNAME[0]} - Preparing to uninstall HedgeModManager"
	echo "Preparing to uninstall HedgeModManager"

	rmDirIfExists "$HMMCOMPDATA"
	rmDirIfExists "$HMMDLDIR"
	rmFileIfExists "$HMMDFPA"
	rmFileIfExists "$HMMDFDLPA"

	writelog "INFO" "${FUNCNAME[0]} - Successfully uninstalled HedgeModManager"
	echo "Uninstalled HedgeModManager"
}

function listSupportedHMMGames {
	# List HMM compatible games from in format "Game Name (appid) (architecture)"
	while read -r HMMGAME; do
		HMMGAMENAME="$( echo "$HMMGAME" | cut -d ";" -f 1 | cut -d '"' -f2 )"
		HMMGAMEAID="$( echo "$HMMGAME" | cut -d ";" -f 2 | cut -d '"' -f2 )"
		HMMGAMEARCH="$( echo "$HMMGAME" | cut -d ";" -f 3 | cut -d '"' -f2 )"

		echo "$HMMGAMENAME ($HMMGAMEAID) ($HMMGAMEARCH-bit)"
	done < "$GLOBALMISCDIR/hmmgames.txt"
}

# Output installed HMM supported games in the format "Game (AppID) -> /path/to/prefix"
function listInstalledHMMGames {
	writelog "INFO" "${FUNCNAME[0]} - Looking for installed HedgeModManager games"
	OGGPFX="$GPFX"
	while read -r HMMGAME; do
		HMMGN="$( echo "$HMMGAME" | cut -d ";" -f 1 | cut -d '"' -f2 )"
		HMMGAID="$( echo "$HMMGAME" | cut -d ";" -f 2 | cut -d '"' -f2 )"

		# Check if game has compatdata dir, if it does then assume it is installed, also attempt to preserve existing GPFX, though this will probably only be called from the command line
		HMMGAPPMA="$( listAppManifests | grep -m1 "${HMMGAID}.acf" )"
		HMMGPFX="$( setGPfxFromAppMa "$HMMGAID" "$HMMGAPPMA" )"

		# Check if it has a "proper" directory structure and is not just a blank compatdata
		if [ -d "$HMMGPFX/$DRCU/$STUS" ]; then
			printf "%s (%s) -> %s\n" "$HMMGN" "$HMMGAID" "$HMMGPFX"
		fi
	done < "$GLOBALMISCDIR/hmmgames.txt"
	GPFX="$OGGPFX"
}

function listOwnedHMMGames {
	writelog "INFO" "${FUNCNAME[0]} - Looking for owned HedgeModManager games"

	HMMGAMES="$GLOBALMISCDIR/hmmgames.txt"
	while read -r line; do
		OWNEDHMMGAMELINE="$( grep "\"$line\"" "$HMMGAMES" )"
		OWNEDHMMGAMENAME="$( echo "$OWNEDHMMGAMELINE" | cut -d ";" -f 1 | cut -d '"' -f 2 )"
		OWNEDHMMGAMEAID="$( echo "$OWNEDHMMGAMELINE" | cut -d ";" -f 2 | cut -d '"' -f 2 )"
		OWNEDHMMGAMEARCH="$( echo "$OWNEDHMMGAMELINE" | cut -d ";" -f 3 | cut -d '"' -f 2 )"

		# If we have all of these, we can safely assume the game is installed -- Virtually identical logic to Vortex's list-owned
		if [ -n "$OWNEDHMMGAMENAME" ] && [ -n "$OWNEDHMMGAMEAID" ] && [ -n "$OWNEDHMMGAMEARCH" ]; then
			printf "%s (%s) (%s-bit)\n" "$OWNEDHMMGAMENAME" "$OWNEDHMMGAMEAID" "$OWNEDHMMGAMEARCH"
		fi
	done <<< "$(getOwnedAids)"
}

# Handle incoming mod download requests for HMM via mimetype URL "gamebananaidentifier:modurl" (ex: hedgemmgens:https://gamebanana.com/mmdl/455806,Mod,50766)
# HMM starts as a separate process for this, so it's fine to just call the exe (+selected proton +correct prefix) with the `-gb` parameter
function dlHedgeMod {
	setHMMVars

	if [ ! -d "$HMMPFX" ]; then
		writelog "INFO" "${FUNCNAME[0]} - HedgeModManager is not installed!"
		echo "It looks like HedgeModManager is not installed. Can't download URL, please try again!"
		notiShow "$NOTY_HMMDLMODFAIL" "X"  # Temp, use strings and better message later
	else
		writelog "INFO" "${FUNCNAME[0]} - Downloading HedgeModManager mod '$1' using '$HMMEXE' in prefix '$HMMPFX' with Wine '$HMMWINE'"


		BANANAHANDLER="$( echo "$1" | cut -d ":" -f 1 )"  # GameBanana Mime handler and Mod ID

		MODGAMENAME="$( grep "$BANANAHANDLER" "$HMMGAMES" | cut -d ";" -f 1 | cut -d '"' -f 2 )"
		MODID="$( basename "$( echo "$1" | cut -d ":" -f 3 )" | cut -d "," -f 3 )"

		echo "Downloading HedgeModManager $MODGAMENAME mod '$1'..."
		notiShow "$( strFix "$NOTY_HMMDLMOD" "$MODGAMENAME" "$MODID" )" "X"

		WINEDEBUG="-all" WINEPREFIX="$HMMPFX" "${HMMWINE}" "${HMMEXE}" -gb "$1" &>/dev/null
	fi
}

# Create a .desktop file entry/entries for HedgeModManager
#
# This seems to not work on Steam Deck - Maybe the .desktop file should just called xdg-open?
# Though the issue seemed more to be that HMM's desktop file wasn't being called at all from Firefox...
function createHMMDesktopFile {
	HMMICONNM="icon256.png"
	HMMICOPATH="$HMMDLDIR/$HMMICONNM"
	HMMICOURL="${HMMPROJURL}/raw/rewrite/HedgeModManager/Resources/Graphics/${HMMICONNM}"

	# Download HMM icon file from HMM repo to the HMM DL folder if it wasn't already downloaded
	if [ ! -f "$HMMICOPATH" ]; then
		"$WGET" "$HMMICOURL" -O "$HMMICOPATH"
	fi

	rmFileIfExists "$HMMDFPA"

	# Generate application launch .desktop file - Re-create each time to set correct path
	writelog "INFO" "${FUNCNAME[0]} - Creating new HedgeModManager desktop file at '$HMMDFPA'"
	{
		echo "[Desktop Entry]"
		echo "Type=Application"
		echo "Categories=Game;"
		echo "Name=${HMMNICE}"
		echo "Comment=A mod manager for Hedgehog Engine games on PC (Installed by SteamTinkerLaunch)"
		echo "MimeType=x-scheme-handler/hedgemm"
		if [ "$INFLATPAK" -eq 1 ]; then
			echo "Exec=/usr/bin/flatpak run --command=steamtinkerlaunch $FLATPAK_ID ${HMM,,} start ${HMMAUTO} \"%u\""  # "hmm start" takes dl channel as argument, next arg is the URL
		else
			echo "Exec=$(realpath "$0") ${HMM,,} start ${HMMAUTO} \"%u\""  # "hmm start" takes dl channel as argument, next arg is the URL
		fi
		echo "Icon=${HMMICOPATH}"
		echo "Terminal=false"
		echo "X-KeepTerminal=false"
	} > "$HMMDFPA"

	# Get all HMM mime handlers in use on GameBanana from hardcoded list in hmmgames.txt
	HMMGAMEMIMES="x-scheme-handler/hedgemm"
	while read -r HMMGAME; do
		# 4th col in hmmgames.txt is the MimeType name
		HMMGAMEMIMES+=";x-scheme-handler/$( echo "$HMMGAME" | cut -d ";" -f 4 | cut -d '"' -f2 )"
	done < "$GLOBALMISCDIR/hmmgames.txt"

	if [ ! -f "$HMMDFDLPA" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Creating HedgeModManager Desktop file for handing supported game MimeTypes"
	else
		writelog "INFO" "${FUNCNAME[0]} - Updating HedgeModManager Desktop file for handing supported game MimeTypes"
		rm "$HMMDFDLPA"
	fi

	# Create Desktop file for handling mod dowwnloads using HMM handlers
	{
		echo "[Desktop Entry]"
		echo "Type=Application"
		echo "Categories=Utilities;"
		echo "Name=${HMM} ($PROGNAME) - GameBanana Handler"
		echo "Comment=Link Handler - For internal use only"
		echo "Icon=$STLICON"
		echo "MimeType=$HMMGAMEMIMES"
		echo "Terminal=false"
		echo "X-KeepTerminal=false"
		echo "Path=$HMMDLDIR"
		if [ "$INFLATPAK" -eq 1 ]; then
			echo "Exec=/usr/bin/flatpak run --command=steamtinkerlaunch $FLATPAK_ID ${HMM,,} u %u"  # "hmm start" takes dl channel as argument, next arg is the URL
		else
			echo "Exec=$(realpath "$0") ${HMM,,} u %u"  # "hmm start" takes dl channel as argument, next arg is the URL
		fi
		echo "NoDisplay=false"
		echo "Hidden=false"
	} >> "$HMMDFDLPA"

	# Associate GameBanana Mimes with DL desktop file
	if [ -x "$(command -v "$XDGMIME" 2>/dev/null)" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Setting download defaults for HedgeModManager GameBanana protocols via '$XDGMIME' pointing at '$HMMDFDLPA'"
		for HMMMIME in ${HMMGAMEMIMES//;/ }; do
			"$XDGMIME" default "$HMMDFDL" "$HMMMIME"
		done
	else
		writelog "SKIP" "${FUNCNAME[0]} - Required Mime handler '$XDGMIME' not found - couldn't set download defaults for HedgeModManager GameBanana protocols - skipping"
	fi
}

### HEDGEMODMANAGER (HMM) END

#### MO2 + Vortex ####
# NOTE: This was written with MO2 and Vortex in mind (seems to also currently work for HMM)
# It relies on projects having proper releases and tagging
# It may need tweaking if this is used for other projects in future or might require an entirely new function
function getLatestGitHubExeVer {
    SETUPNAME="$1"
    PROJURL="$2"
    EXCLUDEPRERELEASES="${3:-0}"  # i.e. to only get latest stable Vortex

    RELEASESURL="${PROJURL}/releases"
    if [ "$EXCLUDEPRERELEASES" -eq 1 ]; then
        TAGSURL="${RELEASESURL}/latest"  # Will redirect to release tagged with "latest" instead of pre-release
    else
        TAGSURL="${PROJURL}/tags"
    fi

    TAGSGREP="${RELEASESURL#"$GHURL"}/tag"

    LATESTTAG="$("$WGET" -q "${TAGSURL}" -O - 2> >(grep -v "SSL_INIT") | grep -m1 "$TAGSGREP" | grep -oE "${TAGSGREP}[^\"]+")"
    LATESTVER="${LATESTTAG##*/}"

	getGitHubExeVer "$SETUPNAME" "$PROJURL" "$LATESTVER"
}

# Get GitHub release information for a given version tag -- Used by getLatestGitHubExeVer and can also be used standalone i.e. for downloading a specific Vortex version.
function getGitHubExeVer {
	SETUPNAME="$1"
	PROJURL="$2"
	TAG="$3"

	"$WGET" -q "${PROJURL}/releases/expanded_assets/${TAG}" -O - 2> >(grep -v "SSL_INIT") | grep "exe" | grep -m1 "$SETUPNAME" | grep -oE "${SETUPNAME}[^\"]+"
}

#### VORTEX START: ####

function addVortexStage {
	if [ ! -f "$VORTEXSTAGELIST" ]; then
		{
		echo "# List of directories, which ${VTX^} uses as 'Stage directories'"
		echo "# (see Wiki for a comprehensive description)"
		} > "$VORTEXSTAGELIST"
	fi

	if [ -z "$1" ]; then
		export CURWIKI="$PPW/${VTX^}"
		TITLE="${PROGNAME}-AddVortexStage"
		pollWinRes "$TITLE"

		NEWVS="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
		--file --directory \
		--title="$TITLE" \
		--text="$(spanFont "$GUI_SELECTVORTEXDIR" "H")" "$GEOM")"
	else
		if [ -d "$1" ]; then
			NEWVS="$1"
		elif [ -d "$(dirname "$1")" ]; then
			if mkProjDir "$1"; then
				NEWVS="$1"
			else
				writelog "SKIP" "${FUNCNAME[0]} - Skipping invalid argument '$1'"
			fi
		fi
	fi

	if [ -n "$NEWVS" ]; then
		echo "$NEWVS" >> "$VORTEXSTAGELIST"
		rmDupLines "$VORTEXSTAGELIST"
	fi
}

function wineVortexRun {
	sleep 1  # required!

	## Only use SLR is available and (if user explicitly wants to run Vortex with dotnet OR if dotnet6 is not already installed), because the SLR can cause hardlink deployment to fail
	## See also: https://github.com/sonic2kk/steamtinkerlaunch/issues/828

	writelog "INFO" "${FUNCNAME[0]} - Vortex logs will be stored at '${VWRUN}'"
	writelog "INFO" "${FUNCNAME[0]} - Vortex installation location is '${VORTEXEXE}'"
	if [[ -n "${SLRCMD[*]}" && ( "$VORTEXUSESLRPOSTINSTALL" -eq 1 || ! -d "$VORTEXPFX/$DRC/Program Files/dotnet" ) ]]; then
		writelog "INFO" "${FUNCNAME[0]} - PATH=\"${SLTPATH}\" LD_LIBRARY_PATH=\"\" WINE=\"${VORTEXWINE}\" WINEARCH=\"win64\" WINEDEBUG=\"-all\" WINEPREFIX=\"${VORTEXPFX}\" \"${SLRCMD[*]}\" \"$*\""
		PATH="$STLPATH" LD_LIBRARY_PATH="" LD_PRELOAD="" WINE="$VORTEXWINE" WINEARCH="win64" WINEDEBUG="-all" WINEPREFIX="$VORTEXPFX" "${SLRCMD[@]}" "$@" > "$VWRUN" 2>/dev/null
	else
		writelog "INFO" "${FUNCNAME[0]} - PATH=\"${SLTPATH}\" LD_LIBRARY_PATH=\"\" WINE=\"${VORTEXWINE}\" WINEARCH=\"win64\" WINEDEBUG=\"-all\" WINEPREFIX=\"${VORTEXPFX}\" \"$*\""
		PATH="$STLPATH" LD_LIBRARY_PATH="" LD_PRELOAD="" WINE="$VORTEXWINE" WINEARCH="win64" WINEDEBUG="-all" WINEPREFIX="$VORTEXPFX" "$@" > "$VWRUN" 2>/dev/null
	fi

	unset "${SLRCMD[@]}"  # Ensure SLR is removed so that it won't be fetched and set for games launched after Vortex
}

function cleanVortex {
	MSCOR="mscorsvw.exe"
	if "$PGREP" "$MSCOR" >/dev/null; then
		writelog "INFO" "${FUNCNAME[0]} - Killing leftovers of $MSCOR"
		"$PKILL" -9 "$MSCOR"
	fi
}

function setVortexDLMime {
	writelog "INFO" "${FUNCNAME[0]} - INFO: Linking Nexus Mods downloads to ${VTX^}"

	VD="$VTX-${PROGNAME,,}-dl.desktop"
	FVD="$HOME/.local/share/applications/$VD"

	if [ ! -f "$FVD" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Creating new desktop file $FVD"
		{
		echo "[Desktop Entry]"
		echo "Type=Application"
		echo "Categories=Utilities;"
		echo "Name=${VTX^} ($PROGNAME - ${PROGNAME,,})"
		echo "Comment=Link Handler - For internal use only"
		echo "Icon=$STLICON"
		echo "MimeType=x-scheme-handler/nxm;x-scheme-handler/nxm-protocol"
		echo "Terminal=false"
		echo "X-KeepTerminal=false"
		echo "Path=$(dirname "$VORTEXEXE")"
		if [ "$INFLATPAK" -eq 1 ]; then
			echo "Exec=/usr/bin/flatpak run --command=steamtinkerlaunch $FLATPAK_ID $VTX u %u"
		else
			echo "Exec=$(realpath "$0") $VTX u %u"
		fi
		echo "NoDisplay=false"
		echo "Hidden=false"
		} >> "$FVD"

		MO2D="$MO-${PROGNAME,,}-dl.desktop"
		FMO2D="$HOME/.local/share/applications/$MO2D"
		if [ -f "$FMO2D" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Renaming desktopfile ${FMO2D} to ${FMO2D}-off, because '$VD' was created"
			mv "$FMO2D" "${FMO2D}-off"
		fi
	else
		if grep -q "$VORTEXPFX" "$FVD"; then
			writelog "INFO" "${FUNCNAME[0]} - Desktopfile $FVD seems to be up2date"
			return
		else
			writelog "INFO" "${FUNCNAME[0]} - Renaming desktopfile $FVD and creating a new one for ${PROGNAME,,}"
			mv "$FVD" "$FVD-old"
			setVortexDLMime
		fi
	fi

	# setting mime types for nxm
	if [ -x "$(command -v "$XDGMIME" 2>/dev/null)" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Setting download defaults for nexusmod protocol via $XDGMIME pointing at $VD"
		"$XDGMIME" default "$VD" x-scheme-handler/nxm
		"$XDGMIME" default "$VD" x-scheme-handler/nxm-protocol
	else
		writelog "SKIP" "${FUNCNAME[0]} - $XDGMIME not found - couldn't set download defaults for nexusmod protocol - skipping"
	fi
}

function getLatestVortVer {
	VSET="$VTX-setup"
	if [ "$USEVORTEXPRERELEASE" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Search for latest ${VTX^} Beta Release, if one is available (will fall back to Stable by default)"
		VORTEXSETUP="$(getLatestGitHubExeVer "$VSET" "$VORTEXPROJURL" )"
	else
		writelog "INFO" "${FUNCNAME[0]} - Search for latest ${VTX^} Stable Release"
		VORTEXSETUP="$(getLatestGitHubExeVer "$VSET" "$VORTEXPROJURL" "1" )"
	fi
	writelog "INFO" "${FUNCNAME[0]} - Found '$VORTEXSETUP'"
	echo "VORTEXSETUP=$VORTEXSETUP" > "$VTST"
}

# Get version based on specified release tag (passed either from commandline or preference)
function getVortVer {
	VTXTAGVER="$1"
	VSET="$VTX-setup"
	VORTEXSETUP="$( getGitHubExeVer "$VSET" "$VORTEXPROJURL" "$VTXTAGVER" )"
	if [ -z "$VORTEXSETUP" ]; then
		writelog "WARN" "${FUNCNAME[0]} - Could not find Vortex setup executable for version '$VTXTAGVER' - VORTEXSETUP came back '$VORTEXSETUP'"
		writelog "INFO" "${FUNCNAME[0]} - Falling back to latest Vortex version"

		getLatestVortVer
	else
		writelog "INFO" "${FUNCNAME[0]} - Successfully fetched Vortex - Downloaded executable will be '$VORTEXSETUP'"
	fi
}

function dlLatestVortex {
	# VORTEXCUSTOMVER is set in Global Menu -- Use it if it's set to a sane value and if the setting is enabled
	if [ "$USEVORTEXCUSTOMVER" -eq 1 ] && [ "$VORTEXCUSTOMVER" != "$NON" ] && [ -n "$VORTEXCUSTOMVER" ]; then
		writelog "INFO" "${FUNCNAME[0]} - VORTEXCUSTOMVER specified and is '$VORTEXCUSTOMVER' - Will attempt to find and install this version"
		getVortVer "$VORTEXCUSTOMVER"
	else
		writelog "INFO" "${FUNCNAME[0]} - Downloading latest Vortex version (not using any custom Vortex version)"
		getLatestVortVer
	fi

	if [ -n "$VORTEXSETUP" ]; then
		export VSPATH="$VORTEXDLDIR/$VORTEXSETUP"

		# download:
		if [ ! -d "$VORTEXDLDIR" ]; then
			mkProjDir "$VORTEXDLDIR"
		fi

		if [ ! -f "$VSPATH" ]; then
			VVRAW="$(grep -oP "${VSET}-\K[^X]+" <<< "$VORTEXSETUP")"
			VORTEXVERSION="${VVRAW%.exe}"

			DLURL="$VORTEXPROJURL/releases/download/v$VORTEXVERSION/$VORTEXSETUP"
			# no idea how the sha512 is formatted in the yaml, so simply checking the size
			DLCHK="stat"
			INCHK="$("$WGET" -q "${DLURL//$VORTEXSETUP/latest.yml}" -O - 2> >(grep -v "SSL_INIT") | grep "size:" | gawk -F': ' '{print $2}')"

			writelog "INFO" "${FUNCNAME[0]} - Downloading $VORTEXSETUP to $VORTEXDLDIR from '$DLURL'"
			if [ -n "$1" ]; then
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$VORTEXSETUP")" "S"
				dlCheck "$DLURL" "$VSPATH" "$DLCHK" "Downloading '$VORTEXSETUP'" "$INCHK"
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON2" "$VORTEXSETUP")" "S"
			else
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$VORTEXSETUP")"
				dlCheck "$DLURL" "$VSPATH" "$DLCHK" "Downloading '$VORTEXSETUP'" "$INCHK"
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON2" "$VORTEXSETUP")"
			fi
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - No VORTEXSETUP defined - nothing to download - skipping"
	fi
}

function getVortexStage {
	if [ -z "$VORTEXSTAGING" ]; then
		WANTSTAGE="$1"
		mkProjDir "$WANTSTAGE"
		if [ -d "$WANTSTAGE" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Created dir '$WANTSTAGE' $PARTLOG"
			VORTEXSTAGING="$WANTSTAGE"
		fi
	fi
}

function getInstalledGamesWithVortexSupport {
	getVortexSupported
	if [ "$(listInstalledGameIDs | wc -l)" -eq 0 ]; then
		writelog "SKIP" "${FUNCNAME[0]} - No installed games found!"
	else
		mapfile -t -O "${#INSTGAMES[@]}" INSTGAMES <<< "$(listInstalledGameIDs)"
		mapfile -t -O "${#INSTVGAMES[@]}" INSTVGAMES <<< "$(comm -12 <(printf "%s\n" "${VOSTIDS[@]}" | sort -u) <(printf "%s\n" "${INSTGAMES[@]}" | sort -u))"
		if [ -n "$1" ]; then
			printf "%s\n" "${INSTVGAMES[@]}"
		fi
	fi
}

function dlVortexSupportedList {
	VORTEXGAMES="$GLOBALMISCDIR/$VOGAT"
	VORTSUPURL="https://www.nexusmods.com/about/vortex/"
	VORTHTMLLIST="$STLSHM/${VOGAT//txt/html}"
	VORTTMPLIST="$STLSHM/${VOGAT//.txt/-temp.txt}"

	if [ ! -f "$VORTHTMLLIST" ]; then
		dlCheck "$VORTSUPURL" "$VORTHTMLLIST" "X" "Downloading list of ${VTX^} supported games"
	fi

	awk '/supported-games/,/Vortex FAQ/' "$VORTHTMLLIST" | grep -oP "(?<=<li><a href=).*" | grep -v "Nexus Mods" | sed "s:^\"/:\":; s:\">:\";\":; s:</a></li>:\":" | sort -o "$VORTTMPLIST"
	readarray -t VTXOLGAMES <<<"$( grep -wF -f "$VORTTMPLIST" "$VORTEXGAMES" > "${VORTTMPLIST//-temp/-temp2}"
	{
		cat "${VORTTMPLIST//-temp/-temp2}"
		comm -23 "$VORTEXGAMES" "${VORTTMPLIST//-temp/-temp2}"
	} | sort -u )"

	for VTXOLG in "${VTXOLGAMES[@]}"; do
		VTXGN="$( echo "$VTXOLG" | cut -d ";" -f 2 | cut -d '"' -f 2 )"
		VTXGAID="$( echo "$VTXOLG" | cut -d ";" -f 3 | cut -d '"' -f 2 )"
		printf "%s (%s)\n" "$VTXGN" "$VTXGAID"
	done

	rm "${VORTTMPLIST//-temp/-temp2}" "$VORTTMPLIST" 2>/dev/null
}

function getGameSteamCollections {
	SCGAME="$1"

	if [ -z "$SUSDA" ] || [ -z "$STUIDPATH" ]; then
		setSteamPaths
	fi
	if [ -d "$SUSDA" ]; then
		SC="$STUIDPATH/$SRSCV"

		if [ ! -f "$SC" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - File '${SC##*/}' not found in steam userid dir - skipping"
		else
			writelog "INFO" "${FUNCNAME[0]} - File '${SC##*/}' found in steam userid dir - searching collections for game '$SCGAME'"

			while read -r SCAT; do
				mapfile -t -O "${#GSCATS[@]}" GSCATS <<< "$SCAT"
			done <<< "$(sed -n "/\"$SCGAME\"/,/}/p;" "$SC" | sed -n "/\"tags\"/,/}/p" | sed -n "/{/,/}/p" | grep -v '{\|}' | awk '{print $2}' | sed "s:\"::g")"
			if [ -n "$2" ]; then
				OUT1="$(printf "%s," "${GSCATS[@]}")"
				printf "%s\n" "${OUT1%*,}"
			fi
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - '$SUSDA' not found - this should not happen! - skipping"
	fi
}

function VortexGamesDialog {
	writelog "INFO" "${FUNCNAME[0]} - Opening ${VTX^} Dialog for en/disabling ${VTX^} for installed games"
	setVortexVars
	getInstalledGamesWithVortexSupport
	export CURWIKI="$PPW/${VTX^}"
	TITLE="${PROGNAME}-${VTX^} Toggle"
	pollWinRes "$TITLE"

	setShowPic
	VGNLIST="$STLSHM/VGNLIST.txt"
	VGNSCLIST="$STLSHM/VGNSCLIST.txt"

	VINGAMES="$(while read -r f; do
		if [ -z "$f" ]; then
			continue
		fi

		loadCfg "$GEMETA/$f.conf" X
		VTXGAMFILENAME="$( grep "$f" "$VORTEXGAMES" | cut -d ";" -f 2 | cut -d '"' -f 2 )"
		if [ -n "$VTXGAMFILENAME" ]; then
			GNAM="$VTXGAMFILENAME"
			GNAM="$( echo "${VTXGAMFILENAME//$'\n'/;}" | cut -d ";" -f 2 )"
		fi
		writelog "INFO" "${FUNCNAME[0]} - Game is '$VTXGAMFILENAME'"

		if [ ! -f "$STLGAMEDIRID/${f}.conf" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Game config '$STLGAMEDIRID/${f}.conf' not found, so creating a minimal one from '$STLDEFGAMECFG'"
			grep -v "config Version" "$STLDEFGAMECFG" >> "$STLGAMEDIRID/${f}.conf"
		fi

		if grep -q "Vortex" <<< "$(getGameSteamCollections "$f" "X")"; then
			echo TRUE
			echo "$f"
			echo "$GNAM"
			echo "$GUI_Y"
			echo "$f" >> "$VGNSCLIST"
		else
			if grep -q "^USEVORTEX=\"0\"" "$STLGAMEDIRID/${f}.conf" || ! grep -q "^USEVORTEX=" "$STLGAMEDIRID/${f}.conf"; then
				echo FALSE
				echo "$f"
				echo "$GNAM"
				echo "$GUI_N"
			else
				echo TRUE
				echo "$f"
				echo "$GNAM"
				echo "$GUI_N"
			fi
		fi
	done <<< "$(printf "%s\n" "${INSTVGAMES[@]}")" | \
	"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center "$WINDECO" --list --checklist --column="Use Vortex" --column="Game ID" --column="Game Title" --column "Vortex Steam Collection" --separator=";" --print-column="2" \
	--text="$(spanFont "$GUI_VINFO" "H")\n<span font=\"italic\">($GUI_VINFO1)</span>" --title="$TITLE" "$GEOM")"
	case $? in
		0)
			while read -r checkvgame; do
				VGNAM="$(grep "^$checkvgame" "$VGNLIST" | awk -F ';' '{print $2}')"
				GVCFG="$STLGAMEDIRID/${checkvgame}.conf"

				if ! grep -q "$checkvgame" <<< "${VINGAMES[@]}"; then
					writelog "INFO" "${FUNCNAME[0]} - Disabling ${VTX^} for '$VGNAM' in '$GVCFG', if not already disabled"
					touch "$FUPDATE"
					updateConfigEntry "USEVORTEX" "0" "$GVCFG"
					if grep -q "$checkvgame" "$VGNSCLIST" 2>/dev/null; then
						writelog "WARN" "${FUNCNAME[0]} - To really disable ${VTX^} for '$VGNAM', the game needs to be removed from the ${VTX^} Steam Collection manually"
					fi
				else
					writelog "INFO" "${FUNCNAME[0]} - Enabling ${VTX^} for '$VGNAM' if not already enabled"
					touch "$FUPDATE"
					updateConfigEntry "USEVORTEX" "1" "$GVCFG"
				fi

			done <<< "$(printf "%s\n" "${INSTVGAMES[@]//\"/}")"

		;;
		1) writelog "INFO" "${FUNCNAME[0]} - Selected CANCEL"
		;;
	esac

	rm "$VGNLIST" "$VGNSCLIST" 2>/dev/null
}

function VortexSymDialog {
	setVortexVars
	VPDRC="$VORTEXPFX/$DRC"
	if [ -d "$VPDRC" ]; then
		export CURWIKI="$PPW/${VTX^}"
		TITLE="${PROGNAME}-${VTX^} Symlinks"
		pollWinRes "$TITLE"

		setShowPic

		cd "$VPDRC" >/dev/null || return
		find . -type l -printf '%p\n%l\n' | "$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center "$WINDECO" --list --column="Symlink in '${VPDRC}/'" --column="Points to Game WinePrefix" --print-column="3" \
		--text="$(spanFont "$GUI_VOSY" "H")\n" --title="$TITLE" "$GEOM"
		cd - >/dev/null || return
	else
		writelog "SKIP" "${FUNCNAME[0]} - Directory '$VPDRC' not found "
	fi
}

function getVortexSupported {
	function gVSIDs {
		SDIR="$1"
		if [ -d "$SDIR" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Searching for SteamIDs in '$SDIR'"
			mapfile -t -O "${#VOSTIDS[@]}" VOSTIDS <<< "$(grep -iR "STEAMAPP_ID =\|STEAM_ID =\|steamAppId:" "$SDIR" | grep -v "module.exports" | grep -oP "'\K[^']+" | grep "[0-9]" | sort -u)"
		fi
	}

	setVortexVars
	VGPDIR="$VORTEXINSTDIR/$RABP"
	VUPDIR="$VORTEXPFX/$DRCU/$STUS/$APDA/Vortex/plugins"

	gVSIDs "$VGPDIR"
	gVSIDs "$VUPDIR"
}

# If extra formatting is added this file may need updated
function getVortexSupportedNames {
	VOSTINDEXJS="index.js"
	VOSTINFOJSON="info.json"

	VORTEXPFX="${VORTEXCOMPDATA//\"/}/pfx"
	VORTEXINSTDIR="$VORTEXPFX/$BTVP"
	VORTEXEXE="$VORTEXINSTDIR/${VTX^}.exe"

	if [ ! -f "$VORTEXEXE" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - Cannot get Vortex game list - No Vortex executable at '$VORTEXEXE' - Vortex may not be installed"
		echo "Cannot get Vortex game list - Are you sure Vortex is installed?"
		return
	fi

	VGPDIR="$VORTEXINSTDIR/$RABP"
	for VOSTGAMDIR in "$VGPDIR/game-"*/; do
		if [ -f "$VOSTGAMDIR/$VOSTINDEXJS" ] && [ -f "$VOSTGAMDIR/$VOSTINFOJSON" ]; then
			# Get Vortex game name from the Vortex supported gamedir's `info.json` file
			VOSTGAMDIRNAM="$( "$JQ" '.name' "$VOSTGAMDIR/$VOSTINFOJSON" | sed 's-^"--g;s-^Game:--g;s-"$--g;s-^Stub:--g;s-^Game--g;s-^\ --g' )"

			# Match Steam AppIDs from `index.js` file - grep order is order if preference to search on
			VOSTGAMDIRAID="$( sed "s-'--g;s-\"--g" "$VOSTGAMDIR/$VOSTINDEXJS" | grep -ioE "steamAppId: [0-9]+|STEAMAPP_ID = [0-9]+|STEAM_ID = [0-9]+|APPID = [0-9]+|steamId: [0-9]+" | grep -oE "[0-9]+" | head -n1 )"

			# If we still can't find it, search based on domain name (usually this is directory name without game- pfx) from Vortex games list, may be incomplete so we don't search on it by default
			if [ -z "$VOSTGAMDIRAID" ]; then
				VORTEXGAMES="$GLOBALMISCDIR/$VOGAT"
				VSGDN="$( basename "$VOSTGAMDIR" | sed 's:game-::g' )"
				VOSTGAMDIRAID="$( grep -im1 "$VSGDN" "$VORTEXGAMES" | cut -d ";" -f 3 | cut -d '"' -f 2 )"
				# If we STILL can't find it, search Vortex games list based on the game's "full" name in `info.json`
				if [ -z "$VOSTGAMDIRAID" ]; then
					VOSTGAMDIRAID="$( grep -im1 "$VOSTGAMDIRNAM" "$VORTEXGAMES" | cut -d ";" -f 3 | cut -d '"' -f 2 )"
					# Special hack for kotor as Vortex groups these two games together annoyingly and doesn't list them separately - an improved way to handle this would be welcome
					if [[ $VOSTGAMDIRNAM = *"Knights of the Old Republic"* ]]; then
						printf "Star Wars: Knights of the Old Republic (32370)\nStar Wars: Knights of the Old Republic II (208580)\n"
						continue
					fi
				fi
			fi

			printf '%s (%s)\n' "$VOSTGAMDIRNAM" "$VOSTGAMDIRAID"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Could not find '$VOSTINDEXJS' or '$VOSTINFOJSON' for Vortex game in '$VOSTGAMDIR' - Skipping"
		fi
	done
}

function checkVortexRegs {
	function addReg {
		MODGREG="$STLSHM/modgames.reg"
		if [ ! -f "$MODGREG" ]; then
			echo "Windows Registry Editor Version 5.00" > "$MODGREG"
		fi
		{
			echo "[$1]"
			echo "\"$2\"=\"$3\""
		} >> "${MODGREG}"
	}

	if grep -q "Wow6432Node" <<< "$1"; then
		REGKEY="$1"
		REG32KEY="${REGKEY//\\Wow6432Node\\/}"
	elif grep -q "WOW6432Node" <<< "$1"; then
		REGKEY="${1//WOW6432Node/Wow6432Node}"
		REG32KEY="${REGKEY//\\Wow6432Node\\/}"
	else
		REG32KEY="$1"
		REGKEY="${REG32KEY//Software\\\\/Software\\\\Wow6432Node\\\\}"
	fi
	PATHKEY="$2"
	INSTP="$3"

	writelog "INFO" "${FUNCNAME[0]} - Checking RegKey '$REGKEY' and updating RegKey '$REG32KEY' in registry for game '$NEXUSGAMEID' now"

	# check if registry path exists:
	if wineVortexRun "$VORTEXWINE" reg QUERY "$REGKEY" >/dev/null ; then
		writelog "INFO" "${FUNCNAME[0]} - Registry path $REGKEY already set"
		# value of the currently set registry path:
		REGPATH="$(wineVortexRun "$VORTEXWINE" reg QUERY "$REGKEY" | grep -i "$PATHKEY" | awk -F 'REG_SZ' '{print $NF}' | awk '{$1=$1};1' | tr -d "\n\r")"
		if [ "$REGPATH" == "${INSTP//\\\\/\\}" ]; then
			writelog "INFO" "${FUNCNAME[0]} - The registry entry '$REGPATH' for '$PATHKEY' is identical to the gamepath '${INSTP//\\\\/\\}'"
		else
			if [ -n "$REGPATH" ]; then
				writelog "WARN" "${FUNCNAME[0]} - The registry entry '$REGPATH' for '$PATHKEY' is not equal to gamepath '${INSTP//\\\\/\\}' - resetting registry to '${INSTP//\\\\/\\}'"
			else
				writelog "WARN" "${FUNCNAME[0]} - The registry entry for '$PATHKEY' is empty - resetting registry to '${INSTP//\\\\/\\}'"
			fi
			wineVortexRun "$VORTEXWINE" reg DELETE "$REGKEY" /f >/dev/null
		fi
	else
		writelog "NEW" "${FUNCNAME[0]} - Registry path '$REGKEY' does not exist - creating '$PATHKEY' entry for '$INSTP'"
	fi
	if [ -n "$INSTP" ]; then
		addReg "$REG32KEY" "$PATHKEY" "$INSTP"
	else
		writelog "SKIP" "${FUNCNAME[0]} - INSTP is empty - REG32KEY is '$REG32KEY' and PATHKEY is '$PATHKEY'"
	fi
}

function setVortSet {
	echo "${VTX^}.exe --set $1" >> "$VORTSETCMD"
}

function runVortex {
	cd "$VORTEXINSTDIR" >/dev/null || return
	wineVortexRun "$VORTEXWINE" "${VTX^}.exe" "--force-device-scale-factor=${VORTEXDEVICESCALEFACTOR}" "$@"
	cd - >/dev/null || return
}

function runVortSetCmd {
	if [ -f "$VORTSETCMD" ]; then
		rmDupLines "$VORTSETCMD"
		cd "$VORTEXINSTDIR" >/dev/null || return
		wineVortexRun "$VORTEXWINE" "$VORTSETCMD"
		cd - >/dev/null || return
	fi
}

function setVortexDLPath {
	# configure Vortex Download Dir:
	if [ ! -d "$VORTEXDOWNLOADPATH" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Creating ${VTX^} Download Dir '$VORTEXDOWNLOADPATH'"
		mkProjDir "$VORTEXDOWNLOADPATH"
	fi

	VDPF="$VORTEXDOWNLOADPATH/__vortex_downloads_folder"
	if [ ! -f "$VDPF" ]; then
		echo "{\"instance\":\"empty\"}" > "$VDPF"
	fi

	VORTEXDOWNLOADWINPATH="Z:${VORTEXDOWNLOADPATH//\//\\\\}"
	writelog "INFO" "${FUNCNAME[0]} - Setting ${VTX^} Download WinDir '$VORTEXDOWNLOADWINPATH' in ${VTX^}"
	echo "@echo off" > "$VORTSETCMD"
	setVortSet "settings.downloads.path=true"
	setVortSet "settings.downloads.path=\\\"$VORTEXDOWNLOADWINPATH\\\""
}

function setGameVortexStaging {
	VGAMEDIR="$1"

	if [ ! -d  "$VGAMEDIR" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - argument 1 '$1' is no valid directory - can't continue"
	else
		writelog "INFO" "${FUNCNAME[0]} - Looking for the mount point of the partition where the game dir '$VGAMEDIR' is"
		# find matching Staging Directory:
		GAMEMP="$( df --output=target "$VGAMEDIR" | tail -n1 )"  # df returns "Mounted on" heaqding, use tail to get actual path (using this method ensures file paths with spaces work too)
		writelog "INFO" "${FUNCNAME[0]} - Mount point of partition where the game is installed: '$GAMEMP'"
		unset CONFSTAGE VORTEXSTAGING

		if [ -f "$VORTEXSTAGELIST" ]; then
			CONFSTAGE="$(grep "${GAMEMP}/" "$VORTEXSTAGELIST")"
		fi

		if [ -n "$CONFSTAGE" ]; then
			if [ -d "$CONFSTAGE" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Configured VORTEXSTAGING dir found: '$CONFSTAGE'"
				VORTEXSTAGING="$CONFSTAGE"
			else
				writelog "ERROR" "${FUNCNAME[0]} - Configured entry '$CONFSTAGE' found in '$VORTEXSTAGELIST', but this isn't a useable directory"
			fi
		fi

		if [ -z "$VORTEXSTAGING" ]; then
			if [ "$DISABLE_AUTOSTAGES" -eq 1 ]; then
				writelog "SKIP" "${FUNCNAME[0]} - VORTEXSTAGING is empty and autostages was disabled by the user - skipping vortex"
				USEVORTEX="0"
			else
				PARTLOG=" - using that as VORTEXSTAGING dir for all games on partition' $GAMEMP'"
				HOMEMP="$(df -P "${STLVORTEXDIR%/*}" | awk 'END{print $NF}')"
				writelog "INFO" "${FUNCNAME[0]} - HOMEMP is $HOMEMP and GAMEMP is $GAMEMP"

				# don't pollute base steam installation with a ~/.steam/steam/Vortex dir, so default to $STLVORTEXDIR/stageing
				if [ "$GAMEMP" == "$HOMEMP" ]; then
					getVortexStage "$STLVORTEXDIR/staging"
				fi

				# try in base directory of the partition:
				getVortexStage "$GAMEMP/${VTX^}"

				# then try in the current SteamLibrary dir besides steamapps, as it should be writeable by the user and is unused from steam(?):
				getVortexStage "$(awk -F 'steamapps' '{print $1}' <<< "$VGAMEDIR")${VTX^}"

				# updating Vortex config with the new found VORTEXSTAGING dir:
				touch "$VORTEXSTAGELIST"
				if [ -n "$VORTEXSTAGING" ]; then
					if ! grep -q "$VORTEXSTAGING" < "$VORTEXSTAGELIST"; then
						writelog "INFO" "${FUNCNAME[0]} - Adding '$VORTEXSTAGING' to the ${VTX^} Stage List '$VORTEXSTAGELIST'"
						addVortexStage "$VORTEXSTAGING"
					fi
				fi
			fi
		fi

		if [ -z "$VORTEXSTAGING" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - No useable staging directory autodetected - giving up"
			USEVORTEX="0"
		fi

		if [ -n "$VORTEXSTAGING" ]; then
			writelog "INFO" "${FUNCNAME[0]} - VORTEXSTAGING set to '$VORTEXSTAGING' - configuring '$NEXUSGAMEID' Staging folder installPath"
			VGSGM="$VORTEXSTAGING/$NEXUSGAMEID/mods"

			writelog "INFO" "${FUNCNAME[0]} - Creating ${VTX^} Staging folder '$VGSGM'"
			mkProjDir "$VGSGM"

			VGSGMSF="$VGSGM/__vortex_staging_folder"
			if [ ! -f "$VGSGMSF" ]; then
				echo "{\"instance\":\"empty\",\"game\":\"NEXUSGAMEID\"}" > "$VGSGMSF"
			fi

			GAMESTAGINGWINFOLDER="Z:${VGSGM//\//\\\\}"
			GAMESTAGINGWINFOLDER="${GAMESTAGINGWINFOLDER//$NEXUSGAMEID/\{GAME\}}"
			writelog "INFO" "${FUNCNAME[0]} - Setting Staging folder '$GAMESTAGINGWINFOLDER' in Vortex"

			setVortSet "settings.mods.installPath.$NEXUSGAMEID=true"
			setVortSet "settings.mods.installPath.$NEXUSGAMEID=\"\\\"$GAMESTAGINGWINFOLDER\\\"\""
			setVortSet "settings.mods.activator.$NEXUSGAMEID=\"\\\"hardlink_activator\\\"\""
		fi
	fi
}

function activateVortexGame {
	NEXUSGAMEID="$(grep "\"$1\"" "$VORTEXGAMES" | cut -d ';' -f1)"
	NEXUSGAMEID="${NEXUSGAMEID//\"}"
	if [ -n "$NEXUSGAMEID" ]; then
			NEXRAND="$(tr -dc 'a-zA-Z0-9' < /dev/urandom | fold -w 9 | head -n1)" # valid?
			writelog "INFO" "${FUNCNAME[0]} - Activating game '$NEXUSGAMEID' ($1) in ${VTX^}" "E"
			setVortSet "settings.mods.activator.$NEXUSGAMEID=\"\\\"hardlink_activator\\\"\""
			setVortSet "settings.profiles.activeProfileId=\"\\\"$NEXRAND\\\"\""
			setVortSet "settings.profiles.lastActiveProfile.$NEXUSGAMEID=\"\\\"$NEXRAND\\\"\""
			setVortSet "settings.profiles.nextProfileId=\"\\\"$NEXRAND\\\"\""
			runVortSetCmd
	else
		writelog "ERROR" "${FUNCNAME[0]} - No valid  NEXUSGAMEID found for '$1'" "E"
	fi
}

function setupGameVortex {
	VZGAMEDIR="Z:${1//\//\\\\}"
	VORTGETSET="$STLSHM/vortgetset.txt"

	if [ ! -f "$VORTGETSET" ]; then
		WINEDEBUG="-all" WINEPREFIX="$VORTEXPFX" "$VORTEXWINE" "$VORTEXEXE" "--get" "settings" > "$VORTGETSET" 2>/dev/null
	fi

	if [ -f "$VORTGETSET" ] && grep -q "$NEXUSGAMEID=\"hardlink_activator\"" "$VORTGETSET"; then
		writelog "SKIP" "${FUNCNAME[0]} - '$NEXUSGAMEID' is already added to Vortex"
	else
		writelog "INFO" "${FUNCNAME[0]} - Activating game dir '$VZGAMEDIR' for '$NEXUSGAMEID ($VAID)' in Vortex"
		setVortSet "settings.gameMode.discovered.$NEXUSGAMEID.environment.SteamAPPId=\"\\\"$VAID\\\"\""
		setVortSet "settings.gameMode.discovered.$NEXUSGAMEID.hidden=false"
		setVortSet "settings.gameMode.discovered.$NEXUSGAMEID.path=true"
		setVortSet "settings.gameMode.discovered.$NEXUSGAMEID.path=\"\\\"$VZGAMEDIR\\\"\""
		setVortSet "settings.gameMode.discovered.$NEXUSGAMEID.pathSetManually=true"
	fi
}

function setInstPathReg {
	NEXUSGAMEFILE="$VORTEXINSTDIR/$RABP/game-$NEXUSGAMEID/index.js"

	if [ ! -f "$NEXUSGAMEFILE" ]; then
		writelog "WARN" "${FUNCNAME[0]} - Could not find '$NEXUSGAMEFILE' - maybe ${VTX} uses a different name for the game than '$NEXUSGAMEID'?"
	else
		writelog "INFO" "${FUNCNAME[0]} - Found '$NEXUSGAMEFILE' - looking for usable data"

		# search registry install path in NEXUSGAMEFILE
		if grep -E 'instPath.*winapi.RegGetValue' "$NEXUSGAMEFILE" -A1 | grep "HKEY_LOCAL_MACHINE" -q ; then
			writelog "INFO" "${FUNCNAME[0]} - Found some instPath registry value in '$NEXUSGAMEFILE' - trying to extract it"
			REGKEY=""
			PATHKEY=""
			RAWREG="$(grep -E 'instPath.*winapi.RegGetValue' "$NEXUSGAMEFILE" -A3 | tr -d "\n\r" | awk -F 'RegGetValue' '{print $2}' | cut -d';' -f1 | tr -s " " | sed "s:^(::g" | sed "s:)$::g" | sed 's/, /,/g' | awk '{$1=$1;print}')"

			if [ -n "$RAWREG" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Analyzing found registry snippet $RAWREG"

				if grep -q "HKEY" <<< "$RAWREG"; then
					writelog "INFO" "${FUNCNAME[0]} - Found a HKEY entry: $RAWREG - working on it"
					SNIP="','S" # :)
					REGWIP1="${RAWREG//HINE$SNIP/HINE\\S}"
					REGWIP="${REGWIP1//T_USER','S/T_USER\\\\S}"

					writelog "INFO" "${FUNCNAME[0]} - REGWIP is $REGWIP"

					REGWIPKEY="$(awk -F ',' '{print $1}' <<< "$REGWIP" | sed "s:'::g")"
					PATHKEY="$(awk -F ',' '{print $2}' <<< "$REGWIP" | sed "s:'::g")"

					if grep -q -i "WOW6432Node" <<< "$REGWIPKEY"; then
						writelog "INFO" "${FUNCNAME[0]} - Squeezing in a 'WOW6432Node' into the '$REGWIPKEY' string"
						REGKEY="${REGWIPKEY/[Ss][Oo][Ff][Tt][Ww][Aa][Rr][Ee]/Software\\\\\\WOW6432Node}"
					else
						REGKEY="$REGWIPKEY"
					fi

					writelog "INFO" "${FUNCNAME[0]} - Final REGKEY is '$REGKEY'"
				else
					if grep -q "hive" <<< "$RAWREG"; then
						writelog "INFO" "${FUNCNAME[0]} - Found a hive, key, name placeholder - required?"
					else
						writelog "SKIP" "${FUNCNAME[0]} - No valid registry found in cut entry '$RAWREG' - skipping"
					fi
				fi
			else
				writelog "SKIP" "${FUNCNAME[0]} - Haven't found any useable registry entries in '$NEXUSGAMEFILE' - skipping registry insert"
			fi

			# insert registry key when found:
			if [ -n "$REGKEY" ] && [ -n "$PATHKEY" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Inserting registry key '$REGKEY' '$PATHKEY' 'Z:${VGAMEDIR//\//\\\\}'"
				checkVortexRegs "$REGKEY" "$PATHKEY" "Z:${VGAMEDIR//\//\\\\}"
			else
				writelog "SKIP" "${FUNCNAME[0]} - REGKEY '$REGKEY' or PATHKEY '$PATHKEY' is empty - skipping registry insert"
			fi
		fi
	fi
}

function prepareVortexGame {
	VAID="$1"

	if [ -z "$VAID" ]; then
		return
	fi

	if grep -q "\"$VAID\"" "$SEENVORTEXGAMES" 2>/dev/null; then
		NEXUSGAMEID="$(grep "\"$VAID\"" "$VORTEXGAMES" | cut -d ';' -f1)"
		writelog "INFO" "${FUNCNAME[0]} - '$NEXUSGAMEID ($VAID)' is already setup for '${VTX^}' - remove from '$SEENVORTEXGAMES' for retry"
	else
		if grep -q "\"$VAID\"" "$VORTEXGAMES"; then
			if [ -z "$NEXUSGAMEID" ]; then
				NEXUSGAMEID="$(grep "\"$VAID\"" "$VORTEXGAMES" | cut -d ';' -f1)"
				NEXUSGAMEID="${NEXUSGAMEID//\"}"
				updateConfigEntry "NEXUSGAMEID" "$NEXUSGAMEID" "$STLGAMECFG"
				updateConfigEntry "NEXUSGAMEID" "$NEXUSGAMEID" "$GEMETA/$AID.conf"
			fi

			VGNAME="$(grep "\"$VAID\"" "$VORTEXGAMES" | cut -d ';' -f2)"
			VGNAME="${VGNAME//\"}"
			writelog "INFO" "${FUNCNAME[0]} - $(strFix "$NOTY_PREPVTX" "$VGNAME" "$VAID" "$NEXUSGAMEID")"
			notiShow "$(strFix "$NOTY_PREPVTX" "$VGNAME" "$VAID" "$NEXUSGAMEID")" "S"
			# prepare symlinks in VORTEXPFX
			VAPPMAFE="$(listAppManifests | grep -m1 "${VAID}.acf")"
			VGPFX="$(setGPfxFromAppMa "$VAID" "$VAPPMAFE")"

			GPFXSTUS="$VGPFX/$DRCU/$STUS"
			if [ -d "$GPFXSTUS" ]; then
				setModGameSyms "set" "$VGPFX" "$VGNAME" "$VAID" "$VORTEXPFX" "$VAPPMAFE"
				VGAMEDIR="$(getGameDirFromAID "$VAID")"
				writelog "INFO" "${FUNCNAME[0]} - Game dir for '$VAID' found is: '$VGAMEDIR')"
				setInstPathReg
				checkVortexRegs "HKEY_LOCAL_MACHINE\\Software\\\Wow6432Node\\\Valve\\\Steam\\\Apps\\$VAID" "Installed Path" "Z:${VGAMEDIR//\//\\\\}"
				setModGameReg "$VORTEXPFX" "$VORTEXWINE"
			fi

			grep "\"$VAID\"" "$VORTEXGAMES" >> "$SEENVORTEXGAMES"
			rmDupLines "$SEENVORTEXGAMES"
			if [ ! -d  "$VGAMEDIR" ]; then
				writelog "ERROR" "${FUNCNAME[0]} - variable VGAMEDIR '$VGAMEDIR' is no valid directory - can't continue"
			elif [ -z  "$VGAMEDIR" ]; then
				writelog "ERROR" "${FUNCNAME[0]} - variable VGAMEDIR does not exist - can't continue"
			else
				setupGameVortex "$VGAMEDIR"
				setGameVortexStaging "$VGAMEDIR"
			fi

			if [ -z "$2" ]; then
				writelog "INFO" "${FUNCNAME[0]} - $(strFix "$NOTY_APPLVTX" "$NEXUSGAMEID" "$VORTSETCMD")"
				notiShow "$(strFix "$NOTY_APPLVTX" "$NEXUSGAMEID" "$VORTSETCMD")" "S"
				runVortSetCmd
				writelog "INFO" "${FUNCNAME[0]} - Symlinks, registry entries and $VTX settings for '$NEXUSGAMEID' should be ready at this point for ${VTX^}"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Skip game '$VAID' is not supported by ${VTX^} or the ID is not listed in '$VORTEXGAMES'" "E"
		fi
	fi
}

function prepareAllInstalledVortexGames {
	writelog "INFO" "${FUNCNAME[0]} - Preparing all installed games supported by ${VTX^}" "E"
	setVortexVars
	while read -r line; do
		unset NEXUSGAMEID
		prepareVortexGame "$line" "X"
	done <<< "$(getInstalledGamesWithVortexSupport X)"
	writelog "INFO" "${FUNCNAME[0]} - Applying ${VTX^} settings for all games via autogenerated cmd '$VORTSETCMD'" "E"
	runVortSetCmd
	writelog "INFO" "${FUNCNAME[0]} - Symlinks, registry entries and $VTX settings for all found supported games should be ready at this point for ${VTX^}" "E"
}

function setVortexConfigVdf {
	mkdir -p "$VORTEXPFX/$DRC/$PFX86S/config"
	mkdir -p "$VORTEXPFX/$DRC/$PFX86S/$SAC"
	VTXSTCFG="$VORTEXPFX/$DRC/$PFX86S/$COCOV"
	writelog "INFO" "${FUNCNAME[0]} - Updating '$COCOV' in the ${VTX^} pfx, to make newly games available when auto-detectable"
	cp "$CFGVDF" "$VTXSTCFG"
	while read -r line; do
		BIF="$(awk '{print $2}' <<< "$line")"
		BIF="${BIF//\"}"
		sed "s:$BIF:Z\:$BIF:" -i "$VTXSTCFG"
	done <<< "$(grep "BaseInstallFolder" "$VTXSTCFG")"
}

function resetVortexSettings {
	setVortexVars
	runVortex "--get" "settings"
	grep -v "^info\: Epic" "$VWRUN" > "$STLSHM/vortsetbefore.txt"
	rm "$VWRUN" 2>/dev/null
	setVortexDLPath
	setVortexConfigVdf
	rm "$SEENVORTEXGAMES" 2>/dev/null
	prepareAllInstalledVortexGames
	runVortex "--get" "settings"
	grep -v "^info\: Epic" "$VWRUN" > "$STLSHM/vortsetafter.txt"

	writelog "INFO" "${FUNCNAME[0]} - Diff between ${VTX^} settings before and after reset:" "E"
	diff -u "$STLSHM/vortsetbefore.txt" "$STLSHM/vortsetafter.txt"
}

function setVortexReleaseChannel {
	# Vortex settings.update.channel can be either 'stable', 'beta', or 'none' (where 'none' is 'No automatic updates')
	writelog "INFO" "${FUNCNAME[0]} - DISABLEVORTEXAUTOUPDATE is '1'"
	VTXUPDATECHANNEL="stable"
	if [ "$DISABLEVORTEXAUTOUPDATE" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Disabling Vortex automatic updates"
		VTXUPDATECHANNEL="none"
	else
		if [ "$USEVORTEXPRERELEASE" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Setting Vortex to use Pre-Release channel"
			VTXUPDATECHANNEL="beta"
		else
			writelog "INFO" "${FUNCNAME[0]} - Setting Vortex to use Stable channel"
			VTXUPDATECHANNEL="stable"
		fi
	fi

	setVortSet "settings.update.channel=\"\\\"$VTXUPDATECHANNEL\\\"\""
}

function startVortex {
	setVortexVars
	setVortexSLR
	askVortex "$1"
	if [ "$USEVORTEX" -eq 1 ]; then
		if [ ! -f "$VORTEXEXE" ]; then
			writelog "WARN" "${FUNCNAME[0]} - VORTEXEXE '$VORTEXEXE' does not exist - installing now"
			StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "${VTX^}")" "dlLatestVortex S" "DownloadVortexStatus"
			StatusWindow "$(strFix "$NOTY_INSTSTART" "${VTX^}")" "installVortex" "InstallVortexStatus"
		fi

		if [ "$RUN_VORTEX_WINETRICKS" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Starting $WINETRICKS before Vortex"
			chooseWinetricks
			WINE="$VORTEXWINE" WINEDEBUG="-all" WINEPREFIX="$VORTEXPFX" "$WINETRICKS"
		fi

		if [ "$RUN_VORTEX_WINECFG" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Starting $WINECFG before Vortex"
			WINE="$VORTEXWINE" WINEDEBUG="-all" WINEPREFIX="$VORTEXPFX" "$WINECFG"
		fi

		if [ -f "$VORTEXEXE" ]; then
			setVortexDLMime
			setVortexDLPath
			setVortexConfigVdf
			setVortexReleaseChannel

			if [ -n "$2" ] && [ "$2" -eq "$2" ] 2>/dev/null; then
				StatusWindow "${VTX^}" "prepareVortexGame $2" "PrepareVortexGameStatus"
			elif [ -n "$AID" ] && [ "$AID" != "$PLACEHOLDERAID" ]; then
				StatusWindow "${VTX^}" "prepareVortexGame $AID" "PrepareVortexGameStatus"
			fi

			if [ -n "$NEXUSGAMEID" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Starting ${VTX^} now with command 'runVortex \"--game\" \"$NEXUSGAMEID\"' in WINEPREFIX '$VORTEXPFX'"
				runVortex "--game" "$NEXUSGAMEID"
			else
				if [ "$2" == "url" ]; then
					if [ -z "$3" ]; then
						writelog "INFO" "${FUNCNAME[0]} - need arg3"
						howto
					else
						writelog "INFO" "${FUNCNAME[0]} - Starting ${VTX^} now with command 'runVortex \"-d\" \"$3\""
						runVortex "-d" "$3"
					fi
				elif [ "$2" == "getset" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Showing ${VTX^} settings as requested"
					runVortex "--get" "settings"
					grep -v "^info: Epic" "$VWRUN"
					rm "$VWRUN" 2>/dev/null
				elif [ "$1" == "activate" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Activating game '$2'"
					activateVortexGame "$2"
				else
					StatusWindow "${VTX^}" "prepareAllInstalledVortexGames" "PrepareVortexGameStatus"
					writelog "INFO" "${FUNCNAME[0]} - Starting ${VTX^} without options" "E"
					runVortex
				fi
			fi

			cleanVortex
			writelog "INFO" "${FUNCNAME[0]} - ${VTX^} exited - starting game now"
		else
			writelog "ERROR" "${FUNCNAME[0]} - VORTEXEXE '$VORTEXEXE' not found! - exit"
			exit
		fi
	fi
}

function setVortexSELaunch {
	if [ "$1" == "$AID" ] && [ -d "$EFD" ]; then
		SEEXE="$EFD/$2"
		if [ ! -f "$SEEXE" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Special exe '$2' for '$SGNAID' not found in gamedir '$EFD' - starting normal exe"
		else
			writelog "INFO" "${FUNCNAME[0]} - Found special exe '$2' for '$SGNAID' in gamedir '$EFD'"
			export CURWIKI="$PPW/Vortex"
			TITLE="ScriptExtenderRequester"
			pollWinRes "$TITLE"

			"$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --center "$WINDECO" \
			--title="$TITLE" \
			--text="$GUI_SEBINFOUND '$EFD/$2'" \
			--button="${GE^^}":0 \
			--button="$BUT_SAVERUN ${GE^^}":1 \
			--button="$BUT_SAVERUN ${2^^}":2 \
			"$GEOM"

			case $? in
				0)  {
					writelog "INFO" "${FUNCNAME[0]} - Starting with the regular Game Exe '$GE'"
					USECUSTOMCMD="0"
					}
				;;
				1) 	{
					writelog "INFO" "${FUNCNAME[0]} - Starting with the regular Game Exe '$GE' and don't ask again"
					USECUSTOMCMD="0"
					updateConfigEntry "SELAUNCH" "0" "$STLGAMECFG"
					}
				;;
				2)  {
					writelog "INFO" "${FUNCNAME[0]} - Starting with the Script Extender Exe '$2' and saving as default"
					writelog "INFO" "${FUNCNAME[0]} - Configuring default start of special exe '$2' by enabling SELAUNCH in '$STLGAMECFG'"
					updateConfigEntry "CUSTOMCMD" "$SEEXE" "$STLGAMECFG"
					updateConfigEntry "USECUSTOMCMD" "1" "$STLGAMECFG"
					updateConfigEntry "ONLY_CUSTOMCMD" "1" "$STLGAMECFG"
					updateConfigEntry "SELAUNCH" "0" "$STLGAMECFG"
					CUSTOMCMD="$SEEXE"
					USECUSTOMCMD="1"
					ONLY_CUSTOMCMD=1
					writelog "INFO" "${FUNCNAME[0]} - Starting $SEEXE instead of the game exe directly after this ${VTX^} instance"
					}
				;;
			esac
		fi
	fi
}

function checkVortexSELaunch {
	# (mostly for Vortex)
	# if $1 is 1 check if a preconfigured exe instead of the game is defined/found - f.e. script extender for skyrim, fallout etc
	# if $1 is 2 it is assumed the check already happended before'

	if [ -z "$1" ]; then
		writelog "INFO" "${FUNCNAME[0]} - using fix '2' as SECHECK"
		SECHECK="2"
	else
		writelog "INFO" "${FUNCNAME[0]} - using argument 1 '$1' as SECHECK"
		SECHECK="$1"
	fi

	if [ "$SECHECK" -eq 0 ]; then
		writelog "INFO" "${FUNCNAME[0]} - SELAUNCH set to '$SECHECK' - skipping any SE checks and directly starting what is configured in '$STLGAMECFG'"
	else
		if [ "$SECHECK" -eq 2 ] && [ -n "$SELAUNCH" ] && [ "$SELAUNCH" -eq 1 ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Skipping option $SECHECK because SELAUNCH is already enabled"
		else
			setVortexSELaunch "377160" "f4se_loader.exe" "$SECHECK" # Fallout4
			setVortexSELaunch "611660" "f4sevr_loader.exe" "$SECHECK"	# Fallout4 VR
			setVortexSELaunch "611670" "sksevr_loader.exe" "$SECHECK" # Skyrim VR
			setVortexSELaunch "489830" "skse64_loader.exe" "$SECHECK" # Skyrim Special Edition
			setVortexSELaunch "72850" "skse_loader.exe" "$SECHECK"	# Skyrim
			setVortexSELaunch "933480" "skse_loader.exe" "$SECHECK"	# Enderal
			setVortexSELaunch "22300" "fose_loader.exe" "$SECHECK"	# Fallout 3
			setVortexSELaunch "22370" "fose_loader.exe" "$SECHECK"	# Fallout 3 GOTY
			setVortexSELaunch "22380" "nvse_loader.exe" "$SECHECK"	# Fallout New Vegas
			setVortexSELaunch "22330" "obse_loader.exe" "$SECHECK"	# Oblivion
		fi
	fi
}

function getInstVtxVers {
	grep -ahm1 "\"version\": " "${VORTEXINSTDIR}/${VTXRAA}" | cut -d ':' -f2 | cut -d '"' -f2 # fragile
}

function VortexOptions {
	export CURWIKI="$PPW/Vortex"
	TITLE="${PROGNAME}-${FUNCNAME[0]}"

	if [ "$ONSTEAMDECK" -eq 1 ]; then
		pollWinRes "$TITLE" 1
	else
		pollWinRes "$TITLE" 4
	fi

	setShowPic

	VTXHEAD="${VTX^} Options"

	if [ -n "${VORTEXCOMPDATA}" ]; then
		TT_CODA="${VTX^} $CODA: $VORTEXCOMPDATA"
	fi

	if [ -f "${VORTEXSTAGELIST}" ]; then
		TT_STAGES="$(cat "${VORTEXSTAGELIST}")"
	fi

	if [ -f "${VORTEXINSTDIR}/${VTXRAA}" ]; then
		TT_CODA="$(printf '%s\n%s\n' "${TT_CODA}" "Version installed: $(getInstVtxVers)")"
	fi
	mkProjDir "$VORTEXDLDIR"
	VTXDLV="$(find "${VORTEXDLDIR}" -name "${VTX}-setup*" | sort -V | tail -n1 | awk -F'${VTX}-setup' '{print $NF}')"
	if [ -n "$VTXDLV" ]; then
		VSD1="${VTXDLV//${VORTEXDLDIR}\/${VTX}-setup-}"
		VSD="${VSD1//.exe}"
		TT_DL="Newest setup downloaded: ${VSD}"
	fi

	getLatestVortVer
	if [ -n "$VORTEXSETUP" ]; then
		VSO1="${VORTEXSETUP//${VTX}-setup-}"
		VSO="${VSO1//.exe}"
		TT_DL="$(printf '%s\n%s\n' "$TT_DL" "Newest setup online: ${VSO}")"
	fi

	if [ "$ONSTEAMDECK" -eq 1 ]; then
		INVTX="$(realpath "$0") $VTX install"
	else
		INVTX="$(realpath "$0") $VTX install gui"
	fi

	"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --center --window-icon="$STLICON" --form --center "$WINDECO" --title="$TITLE" \
	--text="$VTXHEAD" --columns="$COLCOUNT" --f1-action="$F1ACTIONCG" --separator="" \
	--field="$FBUT_GUISET_VTXINST!$TT_CODA":FBTN "$INVTX" \
	--field="$FBUT_GUISET_VTXSTART":FBTN "$(realpath "$0") $VTX start" \
	--field="$FBUT_GUISET_VTXSTAGE!$TT_STAGES":FBTN "$(realpath "$0") $VTX stage" \
	--field="$FBUT_GUISET_VTXGAMES":FBTN "$(realpath "$0") $VTX games" \
	--field="$FBUT_GUISET_VTXSYMS":FBTN "$(realpath "$0") $VTX symlinks" \
	--field="$FBUT_GUISET_VTXDL!$TT_DL":FBTN "$(realpath "$0") $VTX download" \
	--button="$BUT_DONE:0" "$GEOM"

	writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_DONE' - Closing Menu"
}

function setModWine {
	USEDNPROTONVAR="$1"
	USEDNPROTON="${!1}"
	DNPROTON="${!2}"
	DNWINEVAR="$3"
	INUVP="$USEDNPROTON"

	if [ -z "$DNPROTON" ] || [ ! -f "$DNPROTON" ]; then
		if [ -z "${ProtonCSV[0]}" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Getting avaialble Proton versions"
			getAvailableProtonVersions "up" X
		fi
		DNPROTON="$(getProtPathFromCSV "$USEDNPROTON")"
		if [ ! -f "$DNPROTON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Proton version mismatch"
			DNPROTON="$(fixProtonVersionMismatch "$USEDNPROTONVAR" "$STLDEFGLOBALCFG" X)"
			writelog "INFO" "${FUNCNAME[0]} - Resolve, USEDNPROTONVAR is now '$USEDNPROTONVAR'"
		fi
		writelog "INFO" "${FUNCNAME[0]} - DNPROTON is '${DNPROTON}'"

		if [ ! -f "$DNPROTON" ]; then
			createDLProtList
			DLURL="$(printf "%s\n" "${ProtonDLList[@]}" | grep -m1 "$USEDNPROTON")"
			if [ -n "$DLURL" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Downloading: '$DLURL'" "E"
				StatusWindow "$GUI_DLCUSTPROT" "dlCustomProton ${DLURL//|/\"}" "DownloadCustomProtonStatus"
				DNPROTON="$(getProtPathFromCSV "$USEDNPROTON")"
			else
				writelog "SKIP" "${FUNCNAME[0]} - No download URL found for requested '$USEDNPROTON' - skipping" "E"
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - DNPROTON is a file -- it is '$DNPROTON'"
		fi
	fi

	if [ -n "$DNPROTON" ] && [ -f "$DNPROTON" ]; then
		export "$2"="$DNPROTON"
		CHECKDNWINED="$(dirname "$DNPROTON")/$DBW"
		CHECKDNWINEF="$(dirname "$DNPROTON")/$FBW"

		if [ -f "$CHECKDNWINED" ]; then
			FWINEVAR="$CHECKDNWINED"
		elif [ -f "$CHECKDNWINEF" ]; then
			FWINEVAR="$CHECKDNWINEF"
		else
			writelog "ERROR" "${FUNCNAME[0]} - $DNWINEVAR was not found - can't continue"
		fi

		if [ -f "$FWINEVAR" ]; then
			export "$DNWINEVAR"="$FWINEVAR"
		fi

		if [ "$INUVP" != "$USEDNPROTON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Updating 'USEDNPROTON' in '${STLDEFGLOBALCFG##*/}' to '$USEDNPROTON'" "E"
			touch "$FUPDATE"
			updateConfigEntry "$USEDNPROTONVAR" "$USEDNPROTON" "$STLDEFGLOBALCFG"
		fi
	else
		writelog "ERROR" "${FUNCNAME[0]} - DNPROTON was not found - can't continue" "E"
	fi
}

# Custom function to force SLR for a program outside of Steam running with Proton e.g. Vortex (may also work for One-Time Run)
function setNonGameSLRReap {
	USESLR=1
	HAVESLR=0
	HAVEREAP="${HAVEREAP:-0}"
	HAVESLRCT="${HAVESLRCT:-0}"

	# 0 - No SLR force, let setSLRReap determine what Proton version to use (Default)
	# 1 - Proton SLR
	# 2 - Native SLR
	FORCESLRTYPE="$1"

	# Only get SLRPROTONNAME if we're forcing Proton, otherwise ignore
	if [ -n "$FORCESLRTYPE" ] && [ "$FORCESLRTYPE" -eq 1 ]; then
		SLRPROTONNAME="$( getProtPathFromCSV "$2" )"  # This could be the name of the Proton version to run i.e. Vortex
	fi

	unset "${SLRCMD[@]}"
	setSLRReap "1" "$FORCESLRTYPE" "$SLRPROTONNAME"  # Get SLRCMD, optionally enforcing Proton (so we don't fall back to native Linux) and setting the Proton version to fetch the SLR info from (e.g. whether to use soldier, sniper, etc)
}

function setVortexVars {
	VORTEXPFX="${VORTEXCOMPDATA//\"/}/pfx"
	if [ -z "$VORTEXEXE" ]; then
		VORTEXINSTDIR="$VORTEXPFX/$BTVP"
		VORTEXEXE="$VORTEXINSTDIR/${VTX^}.exe"
	fi

	if [ "$USEVORTEXPROTON" == "$NON" ]; then
		if [ ! -f "$PROTONCSV" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Looking for available Proton versions"
			getAvailableProtonVersions "up" X
		fi

		if ! grep -q "^GE" "$PROTONCSV"; then
			writelog "INFO" "${FUNCNAME[0]} - Seems like there is no GE Proton available - getting one:"
			autoBumpGE "X"
		fi

		delEmptyFile "$PROTONCSV"

		if [ ! -f "$PROTONCSV" ]; then
			writelog "ERROR" "${FUNCNAME[0]} - Could find '$PROTONCSV'"
		else
			SETVTXPROT="$(grep "^GE" "$PROTONCSV" | sort -V | tail -n1)"
			SETVTXPROT="${SETVTXPROT%%;*}"
			USEVORTEXPROTON="$SETVTXPROT"
			touch "$FUPDATE"
			updateConfigEntry "USEVORTEXPROTON" "$USEVORTEXPROTON" "$STLDEFGLOBALCFG"
			writelog "INFO" "${FUNCNAME[0]} - USEVORTEXPROTON is '$NON', so using latest Proton-GE '$SETVTXPROT' automatically" "E"
		fi
	else
		SETVTXPROT="$USEVORTEXPROTON"
	fi

	export DOTNET_ROOT="$VTX_DOTNET_ROOT"

	writelog "INFO" "${FUNCNAME[0]} - Using $USEVORTEXPROTON for $VTX"

	VORTEXGAMES="$GLOBALMISCDIR/$VOGAT"
	if [ -z "$VORTEXWINE" ] || [ ! -f "$VORTEXWINE" ]; then
		setModWine "SETVTXPROT" "VORTEXPROTON" "VORTEXWINE"
	fi
}

## NOTE: We can't use this in setVortexVars because the SLR should only be used to install Vortex pretty much, but there
## are cases where the user can force it. So we only call this and then unset SLRCMD when necessary. Using this function
## gives us creater control over when the SLR is used because we have to *explicitly* use it (setVortexVars is called
## from a bunch of places).
##
## This can fix prepareAllInstalledVortexGames failing when trying to create links, which it can't seem to do on SteamOS when running in the SLR!!
## See #823 for background.
function setVortexSLR {
	## Both of the SLR options for Vortex assume a valid Vortex Proton version.
	## otherwise the SLR won't be used.

	# Use SLR to install Vortex -- Recommended, see https://github.com/sonic2kk/steamtinkerlaunch/issues/806
	if [ "$VORTEXUSESLR" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - VORTEXUSESLR is '$VORTEXUSESLR', using Steam Linux Runtime to install Vortex"
		setNonGameSLRReap "1" "$USEVORTEXPROTON"  # Force the requested SLR for Vortex Proton version if we enable SLR option
	else
		writelog "INFO" "${FUNCNAME[0]} - Vortex will run WITHOUT the Steam Linux Runtime"
	fi

	## Use SLR for Vortex in general - Not recommended, see https://github.com/sonic2kk/steamtinkerlaunch/issues/828
	if [ "$VORTEXUSESLRPOSTINSTALL" -eq 1 ]; then
		writelog "WARN" "${FUNCNAME[0]} - WARNING: VORTEXUSESLRPOSTINSTALL is '$VORTEXUSESLRPOSTINSTALL', Vortex will be launched with the Steam Linux Runtime -- This may cause problems with mod deployment!"
		setNonGameSLRReap "1" "$USEVORTEXPROTON"
	fi
}

# TODO handle getting passed a custom executable
function installVortex {
	if [ -z "$VORTEXSETUP" ]; then
		if [ -f "$VTST" ]; then
			source "$VTST"
		fi
	fi

	if [ -z "$VSPATH" ]; then
		VSPATH="$VORTEXDLDIR/$VORTEXSETUP"
	fi

	if [ -f "$VSPATH" ]; then
		setVortexVars
		setVortexSLR
		if [ -f "$VORTEXEXE" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - '$VORTEXEXE' does already exists - nothing to install - skipping" "E"
		else
			if [ ! -f "$VORTEXPROTON" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - VORTEXPROTON '$VORTEXPROTON' not found - can't continue" "E"
			else
				writelog "INFO" "${FUNCNAME[0]} - Using '$VORTEXPROTON' for installation" "E"
				mkProjDir "$VORTEXCOMPDATA"

				## TODO dotnet installation currently fails on Steam Deck because it needs to be installed with the SLR
				## There is no clean way to run Winetricks from the SLR if it's in `/usr/bin/winetricks` or similar, so
				## for now we leave it up to Vortex to install dotnet6.
				##
				## This could be fixed with some hacky symlinking of dotnet perhaps, even temporarily while we install
				## some winetricks components, and then removed afterwards, but this would be a separate feature.
				## Removing this line should helph get Vortex working on SteamOS for now, until they break it again.
				##
				## For context, see https://github.com/sonic2kk/steamtinkerlaunch/issues/806#issuecomment-1565759961

				# Vortex 1.8.0+ only requires DotNet6 (only need to pass desktop6, as installDotNet will append dotnet)
				# writelog "INFO" "${FUNCNAME[0]} - Installing .NET 6 for Vortex Mod Manager"
				# notiShow "$(strFix "$NOTY_INSTSTART" "${DOTN^}")" "S"
				# installDotNet "$VORTEXPFX" "$VORTEXWINE" "desktop6"  # Should be easy to bump if a newer version is ever required

				touch "${VORTEXCOMPDATA}/tracked_files"
				STEAM_COMPAT_CLIENT_INSTALL_PATH="$SROOT" STEAM_COMPAT_DATA_PATH="$VORTEXCOMPDATA" "$VORTEXPROTON" "run" 2> "$STLSHM/${FUNCNAME[0]}_protonrun.log"
				notiShow "$GUI_DONE" "S"
				sleep 3
				writelog "INFO" "${FUNCNAME[0]} - Installing '$VSPATH' into '$VORTEXPFX'" E
				notiShow "$(strFix "$NOTY_INSTSTART" "${VSPATH##*/}")" "S"
				# writelog "INFO" "${FUNCNAME[0]} - 'WINEDEBUG=\"-all\" WINEPREFIX=\"$VORTEXPFX\" \"$VORTEXWINE\" \"$VSPATH\" \"/S\"'" E

				# --------------------------
				# Get SLR for Vortex Proton
				unset "${SLRCMD[@]}"
				if [ "$VORTEXUSESLR" -eq 1 ]; then
					setNonGameSLRReap "1" "$USEVORTEXPROTON"
				fi

				# If we got the runtime above and we want to use the SLR with Vortex, use it to install Vortex
				if [ "$VORTEXUSESLR" -eq 1 ] && [ -n "${SLRCMD[*]}" ]; then
					writelog "INFO" "${FUNCNAME[0]} - 'WINEDEBUG=\"-all\" WINEPREFIX=\"$VORTEXPFX\" \"${SLRCMD[*]}\" \"$VORTEXWINE\" \"$VSPATH\" \"/S\"'" E
					WINEDEBUG="-all" WINEPREFIX="$VORTEXPFX" "${SLRCMD[@]}" "$VORTEXWINE" "$VSPATH" "/S"
				else
					writelog "INFO" "${FUNCNAME[0]} - 'WINEDEBUG=\"-all\" WINEPREFIX=\"$VORTEXPFX\" \"$VORTEXWINE\" \"$VSPATH\" \"/S\"'" E
					WINEDEBUG="-all" WINEPREFIX="$VORTEXPFX" "$VORTEXWINE" "$VSPATH" "/S"
				fi
				unset "${SLRCMD[@]}"
				# --------------------------

				notiShow "$(strFix "$NOTY_INSTSTOP" "${VSPATH##*/}")" "S"
				writelog "INFO" "${FUNCNAME[0]} - Base ${VTX^} installation finished" E
				setVortexDLMime
				notiShow "$GUI_DONE" "S"
			fi
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - '$VSPATH' not found - nothing to install - skipping"
	fi

	unset "${SLRCMD[@]}"
}

function installVortexGui {
	export CURWIKI="$PPW/Vortex"
	TITLE="${PROGNAME}-${FUNCNAME[0]}"
	pollWinRes "$TITLE"
	setShowPic

	createProtonList X

	if [ -f "${VORTEXINSTDIR}/${VTXRAA}" ]; then
		GUI_VTXINST="$(printf '%s\n%s\n' "${GUI_VTXINST}" "Version installed: $(getInstVtxVers)")"
	fi
	mkProjDir "$VORTEXDLDIR"
	VTXDLV="$(find "${VORTEXDLDIR}" -name "${VTX}-setup*" | sort -V | tail -n1 | awk -F'${VTX}-setup' '{print $NF}')"
	if [ -n "$VTXDLV" ]; then
		VSD1="${VTXDLV//${VORTEXDLDIR}\/${VTX}-setup-}"
		VSD="${VSD1//.exe}"
		GUI_VTXINST="$(printf '%s\n%s\n' "${GUI_VTXINST}" "Newest setup downloaded: ${VSD}")"
	fi

	getLatestVortVer
	if [ -n "$VORTEXSETUP" ]; then
		VSO1="${VORTEXSETUP//${VTX}-setup-}"
		VSO="${VSO1//.exe}"
		GUI_VTXINST="$(printf '%s\n%s\n' "${GUI_VTXINST}" "Newest setup online: ${VSO}")"
	fi

	VTXINSTARGS="$("$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
		--title="$TITLE" --separator="|" \
		--text="$(spanFont "$GUI_VTXINST" "H")" \
		--field="$GUI_USEVORTEXPROTON!$DESC_USEVORTEXPROTON ('USEVORTEXPROTON')":CB "$(cleanDropDown "$USEVORTEXPROTON" "$PROTYADLIST")" \
		--button="$BUT_CAN:0" --button="$BUT_INSTALL:2" "$GEOM"
		)"
		case $? in
			0)	{
					writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_CAN' - Exiting"
				}
			;;
			2)	{
					mapfile -d "|" -t -O "${#VTARR[@]}" VTARR < <(printf '%s' "$VTXINSTARGS")
					USEVORTEXPROTON="${VTARR[0]}"
					StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "${VTX^}")" "dlLatestVortex S" "DownloadVortexStatus"
					StatusWindow "$(strFix "$NOTY_INSTSTART" "${VTX^}")" "installVortex" "InstallVortexStatus"
				}
			;;
		esac
}

function askVortex {
	if [ "$USEVORTEX" -eq "1" ] && [ "$1" == "ask" ]; then
		if [ "$WAITVORTEX" -gt 0 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Opening ${VTX^} Requester with timeout '$WAITVORTEX'"
			fixShowGnAid
			export CURWIKI="$PPW/Vortex"
			TITLE="${PROGNAME}-OpenVortex"
			pollWinRes "$TITLE"

			setShowPic

			"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
			--title="$TITLE" \
			--text="$(spanFont "$SGNAID - $GUI_ASKVORTEX" "H")" \
			--button="$BUT_VORTEX":0 \
			--button="$BUT_CAN":4 \
			--timeout="$WAITVORTEX" \
			--timeout-indicator=top \
			"$GEOM"

			case $? in
				0)  {
						writelog "INFO" "${FUNCNAME[0]} - Selected to Start ${VTX^}, so not disabling it"
					}
				;;
				4)  {
						writelog "INFO" "${FUNCNAME[0]} - Selected CANCEL - Not starting ${VTX^}"
						USEVORTEX="0"
					}
				;;
				70) {
						writelog "INFO" "${FUNCNAME[0]} - TIMEOUT - Not starting ${VTX^}"
						USEVORTEX="0"
					}
				;;
			esac
		else
			writelog "INFO" "${FUNCNAME[0]} - ${VTX^} Requester was skipped because WAITVORTEX is '$WAITVORTEX'"
		fi
	fi
}

# vtxWinecfg and mo2Winecfg are separate functions and separate from oneTimeWinetricks in case they may need some custom logics
function vtxWinecfg {
	setVortexVars
	fallbackIfNoRunProton "$USEVORTEXPROTON"
	getWinecfgExecutable

	if [ -d "$VORTEXPFX" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Running Winecfg for Vortex"
		writelog "INFO" "${FUNCNAME[0]} - WINEDEBUG=\"-all\" WINEPREFIX=\"$VORTEXPFX\" \"$VORTEXWINE\" \"$OTWINECFGEXE\""
		WINEDEBUG="-all" WINEPREFIX="$VORTEXPFX" "$VORTEXWINE" "$OTWINECFGEXE"
	else
		writelog "ERROR" "${FUNCNAME[0]} - Vortex is not installed or prefix is missing, cannot run Winecfg for Vortex -- VORTEXPFX is '$VORTEXPFX'"
		echo "Vortex is not installed or prefix is missing, cannot run Winecfg for Vortex"
	fi
}

function vtxWinetricks {
	setVortexVars
	chooseWinetricks
	if [ -d "$VORTEXPFX" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Running Winetricks for Vortex"
		writelog "INFO" "${FUNCNAME[0]} - WINEDEBUG=\"-all\" WINEPREFIX=\"$VORTEXPFX\" WINE=\"$VORTEXWINE\" \"$WINETRICKS\""
		WINEDEBUG="-all" WINEPREFIX="$VORTEXPFX" WINE="$VORTEXWINE" "$WINETRICKS"
	else
		writelog "ERROR" "${FUNCNAME[0]} - Vortex is not installed or prefix is missing, cannot run Winetricks for Vortex -- VORTEXPFX is '$VORTEXPFX'"
		echo "Vortex is not installed or prefix is missing, cannot run Winetricks for Vortex"
	fi
}

#### VORTEX STOP ####

# Takes a custom Proton version name and sets it to RUNPROTON -- Mostly used for mo2winecfg and vtxWinecfg
function fallbackIfNoRunProton {
	NEWRUNPROTONNAME="$1"
	if [ -n "$RUNPROTON" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - RUNPROTON is already defined as '$RUNPROTON' -- Skipping"
		return
	fi

	NEWRUNPROTONPATH="$( getProtPathFromCSV "${NEWRUNPROTONNAME}" )"
	if [ -z "$NEWRUNPROTONPATH" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - Could not find path to NEWRUNPROTONNAME '${NEWRUNPROTONPATH}' (is it defined in ProtonCSV.txt?) -- Aborting"
		return
	fi

	# NEWRUNPROTONPATH points to Proton script file, so use -f
	if [ ! -f "$NEWRUNPROTONPATH" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - Found Proton path path '${NEWRUNPROTONPATH}' in ProtonCSV for '${NEWRUNPROTONNAME}', but this path does not exist! Aborting"
		return
	fi

	writelog "INFO" "${FUNCNAME[0]} - RUNPROTON is empty and found valid replacement based on '${NEWRUNPROTONPATH}' - Updated RUNPROTON path is '${NEWRUNPROTONPATH}'"
	RUNPROTON="$NEWRUNPROTONPATH"
}

function warnInvalidModToolLaunch {
	MODTOOLNAME="$1"
	"$YAD" --title="SteamTinkerLaunch - $MODTOOLNAME Invalid Usage" --text="$( strFix "$GUI_MODTOOLINVALIDUSAGE" "$MODTOOLNAME")" --button="OK"
}

#### MO2 MOD ORGANIZER START: ####

function getLatestMO2Ver {
	# Temporarily hardcode to MO2 v2.4.4 until a Proton version with this patch is available: https://gitlab.winehq.org/wine/wine/-/merge_requests/3931
	writelog "INFO" "${FUNCNAME[0]} - Temporarily hardcoding ModOrganizer 2 version to v2.4.4 until v2.5.0 works under Proton"
	writelog "INFO" "${FUNCNAME[0]} - Please open an issue if this Wine patch is available in a Proton version and MO2 works under Proton again: https://gitlab.winehq.org/wine/wine/-/merge_requests/3931"
	MO2SETUP="Mod.Organizer-2.4.4.exe"

	MO2SET="Mod.Organizer"

	# writelog "INFO" "${FUNCNAME[0]} - Search for latest '$MO2SET' Release under '$MO2PROJURL'"
	# MO2SETUP="$(getLatestGitHubExeVer "$MO2SET" "$MO2PROJURL" "1")"
	# if [ -n "$MO2SETUP" ]; then
	# 	writelog "INFO" "${FUNCNAME[0]} - Found '$MO2SETUP'"
	# else
	# 	writelog "ERROR" "${FUNCNAME[0]} - Could not find any '$MO2SET' Release"
	# fi
}

function dlLatestMO2 {
	# Custom executable
	# Only download MO2 if MO2CUSTOMINSTALLER is disabled, AND if a custom installer is not valid (if undefined, or $NON, or doesn't exist)
	if [ "$USEMO2CUSTOMINSTALLER" -eq 1 ] && checkCustomModToolInstaller "ModOrganizer 2" "$MO2CUSTOMINSTALLER"; then
		writelog "INFO" "${FUNCNAME[0]} - Valid ModOrganizer 2 custom installer executable found ('$MO2CUSTOMINSTALLER') -- Using this to install MO2 instead of downloading from GitHub"
		MO2SPATH="$( realpath "$MO2CUSTOMINSTALLER" )"  # Use custom exe
		return
	fi

	# Regular download from GitHub
	getLatestMO2Ver
	if [ -n "$MO2SETUP" ]; then
		mkProjDir "$MO2DLDIR"
		MO2SPATH="$MO2DLDIR/$MO2SETUP"
		if [ ! -f "$MO2SPATH" ]; then
			MO2VRAW="$(grep -oP "${MO2SET}-\K[^X]+" <<< "$MO2SETUP")"
			MO2VERSION="${MO2VRAW%.exe}"
			DLURL="$MO2PROJURL/releases/download/v$MO2VERSION/$MO2SETUP"
			writelog "INFO" "${FUNCNAME[0]} - Downloading $MO2SETUP to $MO2DLDIR from '$DLURL'"

			if [ -n "$1" ]; then
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$MO2SETUP")" "S"
				dlCheck "$DLURL" "$MO2SPATH" "X" "Downloading '$MO2SETUP'"
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON2" "$MO2SETUP")" "S"
			else
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$MO2SETUP")"
				dlCheck "$DLURL" "$MO2SPATH" "X" "Downloading '$MO2SETUP'"
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON2" "$MO2SETUP")"
			fi
			if [ -f "$MO2SPATH" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Download succeeded - continuing installation"
			else
				writelog "ERROR" "${FUNCNAME[0]} - Download failed!"
			fi
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - No MO2SETUP defined - nothing to download - skipping"
	fi
}

function setMO2Vars {
	if [ -z "$MOINST" ]; then
		MOINST="$NON"
	fi

	if [ "$MOINST" == "$NON" ]; then
		if [ -n "$GPFX" ]; then
			MOINST="portable"
			writelog "INFO" "${FUNCNAME[0]} - Found the variable for the game wineprefix '$GPFX', so using a $MOINST instance of '$MO2'"
			MO2PFX="$GPFX"
			MO2CODA="${GPFX//\/pfx}"
		else
			MOINST="global"
			writelog "INFO" "${FUNCNAME[0]} - No game wineprefix found in env, so using a $MOINST instance of '$MO2'"
			MO2CODA="$MO2COMPDATA"
			MO2PFX="${MO2COMPDATA//\"/}/pfx"
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - The '$MO2' instance was already set to '$MOINST' during this run"
	fi

	if [ -z "$MO2EXE" ]; then
		MO2EXE="$MO2PFX/$MOERPATH"
		NXMG="nxmhandler"
		NMXHLOG="$MO2PFX/$DRCU/$STUS/$ADLO/$MO/${NXMG}.log"
	fi
	MO2GAMES="$GLOBALMISCDIR/mo2games.txt"
	writelog "INFO" "${FUNCNAME[0]} - The $MO2 helper-file is set to '$MO2GAMES'"

	if [ -z "$MO2WINE" ] || [ ! -f "$MO2WINE" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Preparing Proton variables for a $MOINST $MO2 instance"

		if [ "$MOINST" == "portable" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using proton version '$USEPROTON', which is currently configured for the game $GAMENAME"
			SETMO2PROT="$USEPROTON"
		else
			if [ "$USEMO2PROTON" == "$NON" ]; then
				if [ ! -f "$PROTONCSV" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Looking for available Proton versions"
					getAvailableProtonVersions "up" X
				fi

				if ! grep -q "^GE" "$PROTONCSV"; then
					autoBumpGE "X"
				fi

				SETMO2PROT="$(grep "^GE" "$PROTONCSV" | sort -V | tail -n1)"
				SETMO2PROT="${SETMO2PROT%%;*}"
				USEMO2PROTON="$SETMO2PROT"
				touch "$FUPDATE"
				updateConfigEntry "USEMO2PROTON" "$USEMO2PROTON" "$STLDEFGLOBALCFG"
				writelog "INFO" "${FUNCNAME[0]} - USEMO2PROTON is '$NON', so using latest Proton-GE '$SETMO2PROT' automatically" "E"
			else
				SETMO2PROT="$USEMO2PROTON"
			fi
		fi

		writelog "INFO" "${FUNCNAME[0]} - Using $SETMO2PROT for $MO"
		setModWine "SETMO2PROT" "MO2RUNPROT" "MO2WINE"
	fi
}

# Helper to check if custom exe for mod tool is valid
# $1 = name of tool (for display purpses), $2 = path to custom executable
# Intended for commandline primarily, not installMO2/etc directly - Could probably be re-used for Vortex
function checkCustomModToolInstaller {
	if [ -z "$1" ]; then
		writelog "WARN" "${FUNCNAME[0]} - Did not pass tool name"
	fi

	if [ -n "$2" ] && [ "$2" != "$NON" ]; then  # Ensure file is given
		if [ -f "$2" ] && [ -s "$2" ]; then  # Ensure file is file and is > 0bytes
			writelog "INFO" "${FUNCNAME[0]} - Got valid $1 executable '$2' is a valid file -- Will use this to install $1"
			echo "Got valid $1 executable '$2' is a valid file -- Will use this to install $1"
			return 0
		else
			writelog "INFO" "${FUNCNAME[0]} - Custom $1 executable '$2' is not a valid file -- Skipping"
			echo "Custom $1 executable '$2' is not a valid file -- Skipping"
			return 1
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - Custom executable ('$2') is not defined -- Skipping"
		echo "Custom executable ('$2') is not defined -- Skipping"
		return 1
	fi
}

function installMO2 {
	dlLatestMO2 "S"

	if [ -f "$MO2SPATH" ]; then
		setMO2Vars
		if [ -f "$MO2EXE" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - '$MO2EXE' does already exists - nothing to install - skipping"
		elif [ -f "$MO2INSTFAIL" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - '$MO2INSTFAIL' found - seems like installation failed previously - skipping further attempts to avoid loops"
		else
			if [ -f "$MO2RUNPROT" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Using '$MO2RUNPROT' for installation" "E"
				mkProjDir "$MO2CODA"
				touch "${MO2COMPDATA}/tracked_files"
				STEAM_COMPAT_CLIENT_INSTALL_PATH="$SROOT" STEAM_COMPAT_DATA_PATH="$MO2CODA" "$MO2RUNPROT" "run" 2> "$STLSHM/${FUNCNAME[0]}_protonrun.log"
				writelog "INFO" "${FUNCNAME[0]} - Installing '$MO2SPATH' into '$MO2PFX'"
				notiShow "$(strFix "$NOTY_INSTSTART" "${MO2SPATH##*/}")"
				# the '$MO2SPATH' installer at least fails on the Steam Deck, so using $INNOEXTRACT for the installation if available
				MININNO=1.9
				WIMOINST=1

				if [ -f "$(command -v "$INNOEXTRACT")" ]; then
					INNOVER="$("$INNOEXTRACT" --version | head -n1 | cut -d ' ' -f2)"
					if [ "$(printf '%s\n' "$MININNO" "$INNOVER" | sort -V | head -n1)" != "$MININNO" ] || grep -qi "[A-Z]" <<< "$INNOVER" ; then
						writelog "ERROR" "${FUNCNAME[0]} - Version for '$INNOEXTRACT' is invalid. You need to at least version '$MININNO'"
						writelog "ERROR" "${FUNCNAME[0]} - Starting $MO2EXE using wine/proton instead"
						WIMOINST=1
					else
						WIMOINST=0
					fi

					if [ "$WIMOINST" -eq 0 ]; then
						writelog "INFO" "${FUNCNAME[0]} - Using $INNOEXTRACT binary found in path: '$(command -v "$INNOEXTRACT")'"
						MO2DST="$MO2PFX/$MORDIR"
						mkProjDir "$MO2DST"
						"$INNOEXTRACT" -m -s -d "$MO2DST" "$MO2SPATH"
						if [ -d "$MO2DST/app" ]; then
							mv "$MO2DST/app" "$MO2DST/${MO2^^}"
							writelog "INFO" "${FUNCNAME[0]} - Installed '$MO' into '$MO2DST/${MO2^^}' using '$INNOEXTRACT'"
						else
							writelog "WARN" "${FUNCNAME[0]} - Extraction of '$MO' into '$MO2DST/${MO2^^}' using $INNOEXTRACT failed or the output directory is called differently"
						fi
					fi
				fi

				if [ "$WIMOINST" -eq 1 ]; then
					if [ "$ONSTEAMDECK" -eq 1 ]; then
						writelog "WARN" "${FUNCNAME[0]} - Unfortunately '$INNOEXTRACT' is required to install $MO on the Steam Deck, but it wasn't found"
					else
						writelog "INFO" "${FUNCNAME[0]} - '$INNOEXTRACT' not found, trying to use the installer '$MO2SPATH' regularly"
						sleep 3
						WINEDEBUG="-all" WINEPREFIX="$MO2PFX" "$MO2WINE" "$MO2SPATH" "/VERYSILENT"
					fi
				fi

				if [ ! -f "$MO2EXE" ]; then
					writelog "WARN" "${FUNCNAME[0]} - '$MO2EXE' not found after installation, creating '$MO2INSTFAIL' to avoid further attempts"
					date > "$MO2INSTFAIL"
				fi
				notiShow "$(strFix "$NOTY_INSTSTOP" "${MO2SPATH##*/}")" "S"
				writelog "INFO" "${FUNCNAME[0]} - Base ${MO} installation finished"
				notiShow "$GUI_DONE" "S"
			else
				writelog "SKIP" "${FUNCNAME[0]} - MO2RUNPROT '$MO2RUNPROT' not found, can't continue with $MO installion"
			fi
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - '$MO2SETUP' not found - nothing to install - skipping"
	fi
}

function checkInstalledMO2Games {
	if [ -f "$MO2INSTFAIL" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - '$MO2INSTFAIL' found - seems like installation failed previously - skipping ${FUNCNAME[0]}"
	elif [ ! -d "$SRCPFX" ]; then
		if [ -n "$1" ]; then
			SYMODE="$1"
		else
			SYMODE="set"
		fi

		while read -r line; do
			MGID="$(cut -d ';' -f2 <<< "$line")"
			MGID="${MGID//\"}"

			while read -r sl; do
				CHKAPMA="$sl/$SA/appmanifest_${MGID}.acf"
				if [ -f "$CHKAPMA" ]; then
					MGNA1="$(cut -d ';' -f1 <<< "$line")"
					MGNA="${MGNA1//\"}"
					MGPFX="$(dirname "$CHKAPMA")/$CODA/${MGID}/pfx"
					setModGameSyms "$SYMODE" "$MGPFX" "$MGNA" "$MGID" "$MO2PFX" "$CHKAPMA"
				fi
			done <<< "$(printf "%s\n" "${SLARR[@]}")"
		done < "$MO2GAMES"
		setModGameReg "$MO2PFX" "$MO2WINE"
	fi
}

function prepAllMO2Games {
	MO2STDIR="$MO2PFX/$DRC/$PFX86S"
	MO2SADIR="$MO2STDIR/$SA"
	mkProjDir "$MO2SADIR"

	rm "$MO2SADIR/$LIFOVDF" 2>/dev/null
	{
		echo "\"LibraryFolders\""
		echo "{"
		listWinSteamLibraries
		echo "}"
	} >> "$MO2SADIR/$LIFOVDF"

	checkInstalledMO2Games "$1"
}

# Output supported MO2 games in format "Name (AppID)"
function listMO2Games {
	MO2GAMES="$GLOBALMISCDIR/mo2games.txt"
	while read -r MO2GAM; do
		MO2GAMNAM="$( echo "$MO2GAM" | cut -d ";" -f 1 | cut -d '"' -f 2 )"
		MO2GAMAID="$( echo "$MO2GAM" | cut -d ";" -f 2 | cut -d '"' -f 2 )"

		printf "%s (%s)\n" "$MO2GAMNAM" "$MO2GAMAID"
	done <"$MO2GAMES"
}

function manageMO2GInstance {
	setMO2Vars
	if [ ! -f "$MO2EXE" ]; then
		StatusWindow "$(strFix "$NOTY_INSTSTART" "$MO")" "installMO2" "InstallMO2Status"
	fi

	if [ -f "$MO2INSTFAIL" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - '$MO2INSTFAIL' found - seems like installation failed previously - skipping ${FUNCNAME[0]}"
	else
		if [ -z "$1" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - argument 1 '$1' is invalid"
		else
			writelog "INFO" "${FUNCNAME[0]} - Looking for '$1' in '$MO2GAMES'"
			if grep -q "$1" "$MO2GAMES"; then
				MO2AID="$1"
				MO2GA1="$(grep -m1 "\"$MO2AID\"" "$MO2GAMES" | cut -d ';' -f1)"
				MO2GAM="${MO2GA1//\"}"

				MO2GAMIN1="$(grep -m1 "\"$MO2AID\"" "$MO2GAMES" | cut -d ';' -f3)"
				MO2GAMINI="${MO2GAMIN1//\"}"

				if [ -n "$MO2GAM" ]; then
					if [ -n "$2" ] && [ "$2" == "portable" ]; then
						MOIN="$MO2PFX/$MOERDIR"
						writelog "INFO" "${FUNCNAME[0]} - preparing '$2' instance in '$MOIN' for '$1'"
						GLOBMOIN="${MO2COMPDATA//\"/}/pfx/$DRCU/$STUS/$ADLO/$MO/$MO2GAM"
					else
						MOIN="$MO2PFX/$DRCU/$STUS/$ADLO/$MO/$MO2GAM"
						GLOBMOIN="$MOIN"
					fi

					MODPRDE="$MOIN/profiles/Default"
					MODLIST="$MODPRDE/modlist.txt"
					MOININI="$MOIN/${MO}.ini"
					MOINEW=0

					GLOBZMOIN="Z:${GLOBMOIN//\//\\\\}"

					if [ ! -f "$MOININI" ]; then
						MO2GADI="$(getGameDirFromAID "$MO2AID")"

						writelog "INFO" "${FUNCNAME[0]} - Creating an initial '$MOININI'"

						if [ -d "$MO2GADI" ]; then
							MO2GAZDI="Z:${MO2GADI//\//\\\\}"
							mkProjDir "$MOIN"
							writelog "INFO" "${FUNCNAME[0]} - GLOBZMOIN is '$GLOBZMOIN'"
							touch "$MOININI"
							# This used to use `echo` but was changed because of ShellCheck SC2028
							# The behaviour was different too, echo was returning '\\\\' but printf was returning '\\'
							# Based on the rest of the paths, I think printf's '\\' is actually correct, but if this breaks anything,
							# we can re-evaluate.
							{
							echo "[General]"
							echo "gameName=$MO2GAMINI"
							echo "selected_profile=@ByteArray(Default)"
							echo "gamePath=@ByteArray($MO2GAZDI)"
							echo "[Settings]"
							printf "download_directory=%s\\\\\\\\downloads\r\n" "${GLOBZMOIN}"
							printf "cache_directory=%s\\\\\\\\webcache\r\n" "${GLOBZMOIN}"
							printf "mod_directory=%s\\\\\\\\mods\r\n" "${GLOBZMOIN}"
							printf "overwrite_directory=%s\\\\\\\\overwrite\r\n" "${GLOBZMOIN}"
							printf "profiles_directory=%s\\\\\\\\profiles\r\n" "${GLOBZMOIN}"
							} >> "$MOININI"
							MOINEW=1
						else
							writelog "SKIP" "${FUNCNAME[0]} - '$MO2AID' is a supported Id, but the game installation could not be found"
						fi
					else
						writelog "INFO" "${FUNCNAME[0]} - '$MOININI' does already exist"
						if grep -q "${GLOBZMOIN}" "$MOININI"; then
							writelog "INFO" "${FUNCNAME[0]} - and it uses $PROGNAME paths"
						else
							# XXXXXXXXXXXX maybe TODO optionally add above paths if missing
							writelog "INFO" "${FUNCNAME[0]} - the file was created by the user, leaving it unmodified"
						fi
					fi
					if [ -d "$MOIN" ]; then
						if [ -n "$2" ]; then
							if [ "$2" == "portable" ]; then
								writelog "INFO" "${FUNCNAME[0]} - Using $2 instance"
								MO2INST="$2"
							else
								writelog "INFO" "${FUNCNAME[0]} - Using $MO instance '$MO2GAM'"
								MO2INST="$MO2GAM"
							fi
						else
							if [ "$MOINEW" -eq 1 ]; then
								writelog "INFO" "${FUNCNAME[0]} - Created initial $MO instance '$MO2GAM'"
							else
								writelog "SKIP" "${FUNCNAME[0]} - $MO instance '$MO2GAM' already exists"
							fi
						fi
					fi
				else
					writelog "INFO" "${FUNCNAME[0]} - Could not find game name for '$MO2AID' - starting regularly with default instance"
				fi
			else
				STAMO2=0
				writelog "SKIP" "${FUNCNAME[0]} - '$1' is no supported $MO game"
				listMO2Games
			fi
		fi
	fi
}

function createAllMO2Instances {
	setMO2Vars
	while read -r line; do
		manageMO2GInstance "$line"
	done <<< "$(prepAllMO2Games "li" | cut -d ';' -f1)"
}

function prepareMO2 {
	setMO2Vars
	STAMO2=1
	if [ -n "$1" ] && [ "$1" != "$NON" ]; then
		if [ "$1" -eq "$1" ] 2>/dev/null; then
			if [ -n "$MOINST" ] && [ "$MOINST" == "portable" ]; then
				manageMO2GInstance "$1" "$MOINST"
			else
				manageMO2GInstance "$1"
			fi

			if [ "$2" == "disabled" ]; then
				STAMO2=0
			fi
		else
			if grep -q "^\"$1\"" "$MO2GAMES"; then
				writelog "INFO" "${FUNCNAME[0]} - Using $MO instance '$1'"
				MO2INST="$1"
			fi
		fi
	else
		if [ ! -f "$MO2EXE" ]; then
			StatusWindow "$(strFix "$NOTY_INSTSTART" "$MO")" "installMO2" "InstallMO2Status"
		fi

		if [ -f "$MO2INSTFAIL" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - '$MO2INSTFAIL' found - seems like installation failed previously - skipping ${FUNCNAME[0]}"
		else
			if [ "$MOINST" == "global" ]; then
				updateMO2GlobConf
				writelog "INFO" "${FUNCNAME[0]} - Updating/creating $MO instances for $MOINST instance"
				createAllMO2Instances
				STAMO2=1
			else
				writelog "SKIP" "${FUNCNAME[0]} - $MOINST instance running - nothing to prepare"
			fi
		fi
	fi

	if [ "$STAMO2" -eq 1 ] && [ "$2" != "disabled" ]; then
		if [ ! -f "$MO2EXE" ]; then
			StatusWindow "$(strFix "$NOTY_INSTSTART" "$MO")" "installMO2" "InstallMO2Status"
		fi

		if [ "$MOINST" == "global" ]; then
			writelog "INFO" "${FUNCNAME[0]} - preparing all games for $MOINST instance"
			prepAllMO2Games "set"
		fi

		if [ -f "$MO2EXE" ]; then
			if [ "$MOINST" == "global" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Checking for instance to be launched as $MOINST instance"
				if [ -z "$MO2INST" ]; then
					if [ -f "$NMXHLOG" ]; then
						writelog "INFO" "${FUNCNAME[0]} - No $MO instance provided - searching last one found in '$NMXHLOG'"
						RINST1="$(tac "$NMXHLOG" | grep -m1 \"reg\")"
						RINST2="${RINST1##*\"reg\" }"
						RINST="$(cut -d '"' -f2 <<< "$RINST2")"
						if [ -n "$RINST" ]; then
							MO2INST1="$(grep -m1 "\"$RINST\"" "$MO2GAMES" | cut -d ';' -f1)"
							MO2INST="${MO2INST1//\"}"
							writelog "INFO" "${FUNCNAME[0]} - Found '$RINST' as last used game in '$NMXHLOG', so using instance '$MO2INST'"
							MO2AID="$(grep -m1 "\"$RINST\"" "$MO2GAMES" | cut -d ';' -f2)"
							manageMO2GInstance "${MO2AID//\"}" "X"
							if [ -z "$EFD" ]; then
								if [ -z "$APPMAFE" ]; then
									APPMAFE="$(listAppManifests | grep -m1 "${MO2AID//\"}.acf")"
								fi
								EFD="$(getGameDirFromAM "$APPMAFE")"
							fi
						fi
					else
						writelog "INFO" "${FUNCNAME[0]} - No log '$NMXHLOG' found, so the last used instance can't be determined - using last supported instance installed instead"
						LAINST="$(checkInstalledMO2Games "li" | tail -n1)"
						MO2INST="$(cut -d ';' -f2 <<< "$LAINST")"
						MO2AID="$(cut -d ';' -f1 <<< "$LAINST")"
					fi
				fi
			fi

			if [ -n "$MO2INST" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Using $MO instance '$MO2INST'"
			else
				writelog "INFO" "${FUNCNAME[0]} - No $MO instance provided"
			fi

			setMO2DLMime
		else
			writelog "ERROR" "${FUNCNAME[0]} - No '$MO2EXE' found - can't continue"
		fi
	fi
}

function createMO2SilentModeExeProfilesList {
	# Get all of the ModOrganizer 2 executables launch configurations in the instance's INI
	# The user can use this to override which 'moshortcut://' is launched in Silent Mode
	MO2SILENTMODEEXEPROFILES="$NON"
	MO2GAMES="$GLOBALMISCDIR/mo2games.txt"

	# Taken from manageMO2GInstance
	MO2GA1="$(grep -m1 "\"$AID\"" "$MO2GAMES" | cut -d ';' -f1)"
	MO2GAM="${MO2GA1//\"}"
	if [ -z "$MO2GAM" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Does not appear that '$AID' is a ModOrganizer 2 game, nothing to do"
		return
	fi

	# MO2COMPDATA taken from setMO2Vars
	MOIN="${MO2COMPDATA//\"/}/pfx/$DRCU/$STUS/$ADLO/$MO/${MO2GAM}/${MO}.ini"
	writelog "INFO" "${FUNCNAME[0]} - MOIN is '${MOIN}'"
	if [ ! -f "$MOIN" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Nothing to do, ModOrganizer 2 instance INI for '$MO2GAM ($AID)' doesn't exist at '$MOIN' -- Perhaps this game has never been managed with MO2 before?"
		return
	fi

	# Executables are stored in INI in format '1\title=Executable Name', where '1' is its position in the MO2 executable profile list
	while read -r MO2EXEPROF; do
		# Remove \r with Windows line encoding in INI (\n should already be removed)
		MO2SILENTMODEEXEPROFILES="${MO2SILENTMODEEXEPROFILES}!${MO2EXEPROF//$'\r'/}"
	done <<< "$( sed -n 's/^[0-9]\\title=//p' "$MOIN" )"

	writelog "INFO" "${FUNCNAME[0]} - MO2SILENTMODEEXEPROFILES list is '${MO2SILENTMODEEXEPROFILES}'"
}

function startMO2 {
	prepareMO2 "$NON" "gui"
	if [ -d "${MO2EXE%/*}" ] ; then
		writelog "INFO" "${FUNCNAME[0]} - Starting '$MO2EXE'" E
		PFXSUTEMP="$GPFX/$DRCU/$STUS/Temp"
		mkProjDir "$PFXSUTEMP"
		cd "${MO2EXE%/*}" >/dev/null || return

		if [ -n "$MOINST" ] && [ "$MOINST" == "portable" ]; then
			updateMO2PortConf
		else
			updateMO2GlobConf
		fi

		if [ -n "$MO2INST" ]; then
			writelog "INFO" "${FUNCNAME[0]} - WINEDEBUG=\"-all\" WINEPREFIX=\"$MO2PFX\" \"$MO2WINE\" \"$MO2EXE\" -i \"$MO2INST\"" E
			WINEDEBUG="-all" WINEPREFIX="$MO2PFX" "$MO2WINE" "$MO2EXE" -i "$MO2INST" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}_${IFILE##*/}.log"
		else
			writelog "INFO" "${FUNCNAME[0]} - WINEDEBUG=\"-all\" WINEPREFIX=\"$MO2PFX\" \"$MO2WINE\" \"$MO2EXE\"" E
			WINEDEBUG="-all" WINEPREFIX="$MO2PFX" "$MO2WINE" "$MO2EXE" 2>&1 | tee "$STLSHM/${FUNCNAME[0]}_${IFILE##*/}.log"
		fi
		cd - >/dev/null || return
		mkProjDir "$PFXSUTEMP"
	elif [ -f "$MO2INSTFAIL" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - '$MO2INSTFAIL' found - seems like installation failed previously - can't start '$MO'"
	else
		writelog "SKIP" "${FUNCNAME[0]} - Could not find '$MO2EXE' - can't start $MO - this should not happen" E
	fi
}

function dlMod2nexurl {
	setMO2Vars

	MONGUR1="${1//nxm:\/\/}"
	MONGURL="${MONGUR1%%/*}"
	MYPORTDLDAT="${STLMO2DLDATDIR}/${MONGURL}.conf"

	function dlMod2globnexurl {
		MYGLOBDLDAT="${STLMO2DLDATDIR}/global.conf"

		if [ -f "$MYGLOBDLDAT" ]; then
			source "$MYGLOBDLDAT"
			if [ -d "${GMO2EXE%/*}" ] && [ -f "$RUNPROTON" ] && [ -n "$STEAM_COMPAT_CLIENT_INSTALL_PATH" ] && [ -n "$STEAM_COMPAT_DATA_PATH" ]; then
				MYINST="$(grep -m1 "\"${MONGURL}\"" "$MO2GAMES" | cut -d ';' -f1)"
				MYINST="${MYINST//\"/}"
				if [ -n "$MYINST" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Starting global '$RUNPROTON run ${MO}.exe -i $MYINST $1'"
					STEAM_COMPAT_CLIENT_INSTALL_PATH="$STEAM_COMPAT_CLIENT_INSTALL_PATH" STEAM_COMPAT_DATA_PATH="$STEAM_COMPAT_DATA_PATH" "$RUNPROTON" "run" "${MO}.exe" -i "$MYINST" "$1" 2>&1 | tee /tmp/RUNMO2DL.log
				else
					writelog "ERROR" "${FUNCNAME[0]} - Could not find a valid $MO instance for '${MONGURL}' - giving up"
				fi
			elif [ -d "${GMO2EXE%/*}" ] && [ -n "$MO2PFX" ] && [ -n "$MO2WINE" ]; then
				MYINST="$(grep -m1 "\"${MONGURL}\"" "$MO2GAMES" | cut -d ';' -f1)"
				MYINST="${MYINST//\"/}"
				if [ -n "$MYINST" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Starting global MO2 in prefix '$MO2PFX' using '$MO2WINE' on $MYINST"
					WINEDEBUG="-all" WINEPREFIX="$MO2PFX" "$MO2WINE" "$GMO2EXE" -i "$MYINST" "$1" 2>&1 | tee /tmp/RUNMO2DL.log
				else
					writelog "ERROR" "${FUNCNAME[0]} - Could not find a valid global MO2 instance"
				fi
			else
				writelog "ERROR" "${FUNCNAME[0]} - Attempted to download Url '$1' for game '$MONGURL', but seems like global '$MYGLOBDLDAT' has incomplete data - giving up" "E"
			fi
		else
			writelog "ERROR" "${FUNCNAME[0]} - Attempted to download Url '$1' for game '$MONGURL', but the source script '$MYGLOBDLDAT' for global $MO is missing - giving up" "E"
		fi
	}

	if [ -f "$LAMOINST" ] && grep -q "global" "$LAMOINST"; then
		writelog "INFO" "${FUNCNAME[0]} - The last used $MO2 instance was global', so using the global $MO installation for the download" "E"
		dlMod2globnexurl "$1"
	elif [ -f "$MYPORTDLDAT" ]; then
		source "$MYPORTDLDAT"
		if [ -d "${GMO2EXE%/*}" ] && [ -f "$RUNPROTON" ] && [ -n "$STEAM_COMPAT_CLIENT_INSTALL_PATH" ] && [ -n "$STEAM_COMPAT_DATA_PATH" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Download Url '$1' for game '$MONGURL' using data from portable '$MYPORTDLDAT'"
			cd "${GMO2EXE%/*}" >/dev/null || return
			writelog "INFO" "${FUNCNAME[0]} - Starting portable '$RUNPROTON run ${MO}.exe $1'"
			STEAM_COMPAT_CLIENT_INSTALL_PATH="$STEAM_COMPAT_CLIENT_INSTALL_PATH" STEAM_COMPAT_DATA_PATH="$STEAM_COMPAT_DATA_PATH" "$RUNPROTON" run "${MO}.exe" "$1" 2>&1 | tee /tmp/RUNMO2DL.log
			cd - >/dev/null || return
		elif [ -d "${GMO2EXE%/*}" ] && [ -n "$MO2PFX" ] && [ -n "$MO2WINE" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Download Url '$1' for game '$MONGURL' using data from portable '$MYPORTDLDAT'"
			cd "${GMO2EXE%/*}" >/dev/null || return
			writelog "INFO" "${FUNCNAME[0]} - Starting portable MO2 in prefix '$MO2PFX' using '$MO2WINE'"
			if [ -n "$MO2INST" ]; then
				WINEDEBUG="-all" WINEPREFIX="$MO2PFX" "$MO2WINE" "$GMO2EXE" -i "$MO2INST" "$1" 2>&1 | tee /tmp/RUNMO2DL.log
			else
				WINEDEBUG="-all" WINEPREFIX="$MO2PFX" "$MO2WINE" "$GMO2EXE" "$1" 2>&1 | tee /tmp/RUNMO2DL.log
			fi
			cd - >/dev/null || return
		else
			writelog "ERROR" "${FUNCNAME[0]} - Attempted to download Url '$1' for game '$MONGURL', but seems like portable '$MYPORTDLDAT' has incomplete data - trying global $MO" "E"
			dlMod2globnexurl "$1"
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - Attempted to download Url '$1' for game '$MONGURL', but the source script '$MYPORTDLDAT' for portable $MO2 is missing - trying to start a global $MO" "E"
		dlMod2globnexurl "$1"
	fi
}

function setMO2DLMime {
	setMO2Vars

	MO2D="$MO-${PROGNAME,,}-dl.desktop"
	FMO2D="$HOME/.local/share/applications/$MO2D"

	if [ ! -f "$FMO2D" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Creating new desktop file $MO2D"
		{
		echo "[Desktop Entry]"
		echo "Type=Application"
		echo "Categories=Utilities;"
		echo "Name=$MO ($PROGNAME - ${PROGNAME,,})"
		echo "Comment=Link Handler - For internal use only"
		echo "Icon=$STLICON"
		echo "MimeType=x-scheme-handler/nxm;x-scheme-handler/nxm-protocol"
		echo "Terminal=false"
		echo "X-KeepTerminal=false"
		echo "Path=$(dirname "$MO2EXE")"
		if [ "$INFLATPAK" -eq 1 ]; then
			echo "Exec=/usr/bin/flatpak run --command=steamtinkerlaunch $FLATPAK_ID mo2 u %u"
		else
			echo "Exec=$(realpath "$0") mo2 u %u"
		fi
		echo "NoDisplay=false"
		echo "Hidden=false"
		} >> "$FMO2D"

		VD="$VTX-${PROGNAME,,}-dl.desktop"
		FVD="$HOME/.local/share/applications/$VD"
		if [ -f "$FVD" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Renaming desktopfile ${FVD} to ${FVD}-off, because '$MO2D' was created"
			mv "$FVD" "$FVD-off"
		fi
	else
		if grep -q "$MO2PFX" "$FMO2D"; then
			writelog "INFO" "${FUNCNAME[0]} - Desktopfile '$FMO2D' looks to be up2date"
			return
		else
			writelog "INFO" "${FUNCNAME[0]} - Renaming desktopfile '$FMO2D' and creating a new one for ${PROGNAME,,}"
			mv "$FMO2D" "$FMO2D-old"
			setMO2DLMime
		fi
	fi

	# setting mime types for nxm

	if [ -x "$(command -v "$XDGMIME" 2>/dev/null)" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Setting download defaults for nexusmod protocol via $XDGMIME pointing at $MO2D"
		"$XDGMIME" default "$MO2D" x-scheme-handler/nxm
		"$XDGMIME" default "$MO2D" x-scheme-handler/nxm-protocol
	else
		writelog "SKIP" "${FUNCNAME[0]} - $XDGMIME not found - couldn't set download defaults for nexusmod protocol - skipping"
	fi
}

function checkMO2 {
	# migrateCfgOption should mean this never happens, but can never be too careful -- Has begun happening since 12/01/2024 for a small number of Steam Deck users
	if [ "$MO2MODE" == "$NON" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - MO2MODE is '$NON' -- This should not happen but has been observed in the wild, so explicitly returning here"
		return
	fi

	# MO2 disabled means don't use MO2 at all
	if [ "$MO2MODE" == "disabled" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - MO2MODE is 'disabled' -- Skipping checkMO2!"
		return
	fi

	# MO2 Wait Requester logic to choose between GUI, Silent, Cancel, or default MO2 mode if the Wait Requester times out
	writelog "INFO" "${FUNCNAME[0]} - MO2MODE is '$MO2MODE' - starting MO2"
	if [ "$WAITMO2" -gt 0 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Opening $MO Requester with timeout '$WAITMO2'"
		fixShowGnAid
		export CURWIKI="$PPW/Mod-Organizer-2"
		TITLE="${PROGNAME}-Open-Mod-Organizer2"
		pollWinRes "$TITLE"

		setShowPic
		"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
		--title="$TITLE" \
		--text="$(spanFont "$SGNAID - $GUI_ASKMO2" "H")" \
		--button="$BUT_MO2_GUI":0 \
		--button="$BUT_MO2_SIL":4 \
		--button="$BUT_MO2_SKIP":6 \
		--timeout="$WAITMO2" \
		--timeout-indicator=top \
		"$GEOM"

		case $? in
			0)  {
					writelog "INFO" "${FUNCNAME[0]} - Selected to start $MO with gui"
					MO2MODE="gui"
				}
			;;
			4)  {
					writelog "INFO" "${FUNCNAME[0]} - Selected to start $MO with mods silently"
					MO2MODE="silent"
				}
			;;
			6)  {
					writelog "INFO" "${FUNCNAME[0]} - Selected CANCEL - Not starting $MO at all"
					MO2MODE="disabled"
				}
			;;
			70) {
					writelog "INFO" "${FUNCNAME[0]} - TIMEOUT - Starting $MO2 with default ModOrganizer 2 mode" # with mods silently"
				}
			;;
		esac
	else
		writelog "INFO" "${FUNCNAME[0]} - $MO Requester was skipped because WAITMO2 is '$WAITMO2' - not changing MO2MODE '$MO2MODE'"
	fi

	prepareMO2 "$AID" "$MO2MODE"
	if [ "$MO2MODE" != "disabled" ] && [ "$USECUSTOMCMD" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Disabling custom command, because $MO2 is enabled"
		USECUSTOMCMD=0
	fi
}

function mo2Winecfg {
	setMO2Vars
	fallbackIfNoRunProton "$USEMO2PROTON"
	getWinecfgExecutable
	if [ -d "$MO2PFX" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Running Winecfg for MO2"
		writelog "INFO" "${FUNCNAME[0]} - WINEDEBUG=\"-all\" WINEPREFIX=\"$MO2PFX\" \"$MO2WINE\" \"$OTWINECFGEXE\""
		WINEDEBUG="-all" WINEPREFIX="$MO2PFX" "$MO2WINE" "$OTWINECFGEXE"
	else
		writelog "ERROR" "${FUNCNAME[0]} - ModOrganizer 2 is not installed or prefix is missing, cannot run Winecfg for MO2"
		echo "ModOrganizer 2 is not installed or prefix is missing, cannot run Winecfg for ModOrganizer 2"
	fi
}

function mo2Winetricks {
	setMO2Vars
	chooseWinetricks
	if [ -d "$MO2PFX" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Running Winetricks for MO2"
		writelog "INFO" "${FUNCNAME[0]} - WINEDEBUG=\"-all\" WINEPREFIX=\"$MO2PFX\" WINE=\"$MO2WINE\" \"$WINETRICKS\""
		WINEDEBUG="-all" WINEPREFIX="$MO2PFX" WINE="$MO2WINE" "$WINETRICKS"
	else
		writelog "ERROR" "${FUNCNAME[0]} - ModOrganizer 2 is not installed or prefix is missing, cannot run Winetricks for MO2"
		echo "ModOrganizer 2 is not installed or prefix is missing, cannot run Winetricks for ModOrganizer 2"
	fi
}

#### MO2 MOD ORGANIZER STOP ####

# dprs:
function dlLatestDprs {
	writelog "INFO" "${FUNCNAME[0]} - Search for latest $DPRS version online"
	LATDPRS="$("$WGET" -q "${DPRSRELURL}/latest" -O - 2> >(grep -v "SSL_INIT") | grep -m1 "${DPRS}-v*.*.exe" | grep -oE "${DPRS}-v[^\"]+")"

	if [ -n "$LATDPRS" ]; then
		DSPATH="$DPRSDLDIR/$LATDPRS"

		if [ ! -f "$DSPATH" ]; then
			DPRSVR1="${LATDPRS//$DPRS-}"
			DPRSVRS="${DPRSVR1//.exe}"

			DLURL="$DPRSRELURL/download/$DPRSVRS/$LATDPRS"
			writelog "INFO" "${FUNCNAME[0]} - Downloading $LATDPRS to $VORTEXDLDIR from '$DLURL'"
			notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$LATDPRS")"
			dlCheck "$DLURL" "$DSPATH" "X" "Downloading '$LATDPRS'"
			notiShow "$(strFix "$NOTY_DLCUSTOMPROTON2" "$LATDPRS")"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Already have the latest version in '$DSPATH'"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - No valid '$DPRS' version found ('$LATDPRS') - nothing to download - skipping"
	fi
}

function dlLatestDepsProg {
	writelog "INFO" "${FUNCNAME[0]} - Search for latest $DEPS version online"
	DEPS64ZIP="${DEPS}_x64_Release.zip"
	DEPS32ZIP="${DEPS64ZIP//64/86}"

	LATDEPSR1="$("$WGET" -q "${DEPURL}/latest" -O - 2> >(grep -v "SSL_INIT") | grep -m1 "$DEPS64ZIP")"
	LATDEPSR2="${LATDEPSR1%/*}"
	LATDEPSV="${LATDEPSR2##*/}"

	if [ -n "$LATDEPSV" ]; then
		DLDIR="$DEPSDLDIR/$LATDEPSV"

		if [ -s "$DEPSLATDIR" ] && [ "$(readlink -f "$DEPSLATDIR")" == "$(readlink -f "$DLDIR")" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Symlink '$DEPSLATDIR' already points to latest version '$DLDIR'" E
		else
			mkProjDir "$DLDIR/64"
			mkProjDir "$DLDIR/32"
			DEPS64ZPATH="$DLDIR/$DEPS64ZIP"
			DEPS32ZPATH="$DLDIR/$DEPS32ZIP"

			if [ ! -f "$DEPS64ZPATH" ]; then
				DLURL="$DEPURL/download/$LATDEPSV/$DEPS64ZIP"
				writelog "INFO" "${FUNCNAME[0]} - Downloading $DEPS64ZIP to '$DLDIR' from '$DLURL'"
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$DEPS64ZIP")" "S"
				dlCheck "$DLURL" "$DEPS64ZPATH" "X" "Downloading '$DEPS64ZIP'"
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON2" "$DEPS64ZIP")" "S"
			else
				writelog "SKIP" "${FUNCNAME[0]} - Already have '$DEPS64ZIP' in '$DLDIR'"
			fi

			if [ ! -f "$DEPS32ZPATH" ]; then
				DLURL="$DEPURL/download/$LATDEPSV/$DEPS32ZIP"
				writelog "INFO" "${FUNCNAME[0]} - Downloading $DEPS32ZIP to '$DLDIR' from '$DLURL'"
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$DEPS64ZIP")" "S"
				dlCheck "$DLURL" "$DEPS32ZPATH" "X" "Downloading '$DEPS32ZIP'"
				notiShow "$(strFix "$NOTY_DLCUSTOMPROTON2" "$DEPS64ZIP")" "S"
			else
				writelog "SKIP" "${FUNCNAME[0]} - Already have '$DEPS32ZIP' in '$DLDIR'"
			fi

			rm "$DEPSLATDIR" 2>/dev/null
			writelog "INFO" "${FUNCNAME[0]} - 'ln -s \"$DLDIR\" \"$DEPSLATDIR\"'" E
			ln -s "$DLDIR" "$DEPSLATDIR"

			if [ ! -f "$DEPSL64" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Extracting $DEPS64ZIP to '$DLDIR/64'"
				"$UNZIP" -q "$DEPS64ZPATH" -d "$DLDIR/64"
				notiShow "$GUI_DONE" "S"
			fi

			if [ ! -f "$DEPSL32" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Extracting $DEPS32ZIP to '$DLDIR/32'"
				"$UNZIP" -q "$DEPS32ZPATH" -d "$DLDIR/32"
				notiShow "$GUI_DONE" "S"
			fi
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - No valid '$DEPS' version found ('$LATDPRS') - nothing to download - skipping"
	fi
}

function checkDepsLaunch {
	if [ "$RUN_DEPS" -eq 1 ] && [ "$ISGAME" -eq 2 ] && [ "$USEWINE" -eq 0 ]; then

		if [ "$DEPSAUTOUP" -eq 1 ] || [ ! -f "$DEPSL64" ]; then
			StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "$DEPS")" "dlLatestDepsProg" "DownloadDepsStatus"
		fi

		writelog "INFO" "${FUNCNAME[0]} - Starting '$DEPS' for '$GE ($AID)'"

		if [ "$(getArch "$GP")" == "32" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using '$DEPSL32' as '$GE' is 32bit"
			DEPSEXE="$DEPSL32"
		elif [ "$(getArch "$GP")" == "64" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using '$DEPSL64' as '$GE' is 64bit"
			DEPSEXE="$DEPSL64"
		else
			writelog "INFO" "${FUNCNAME[0]} - Could not get architecture of '$GP' - using '$DEPSL64'"
			DEPSEXE="$DEPSL64"
		fi

		if [ ! -f "$DEPSEXE" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Installing failed - can't start '$DEPSEXE' - skipping"
			RUN_DEPS=0
		else
			WGP="$(extWine64Run "$RUNWINE" winepath -w "$GP")"
			writelog "INFO" "${FUNCNAME[0]} - Starting '$DEPSEXE' using extWine64Run for the game '$WGP'"
			extWine64Run "$RUNWINE" "$DEPSEXE" "$WGP"
		fi
	fi
}

function startDepressurizer {
	if [ ! -d "$DPRSCOMPDATA" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Creating '$DPRS' $CODA and installing deps"
		mkProjDir "$DPRSCOMPDATA"
		reCreateCompatdata "ccd" "$DPRS" "s"
	fi

	mkProjDir "$DPRSDLDIR"
	if [ "$DPRSPAUTOUP" -eq 1 ]; then
		dlLatestDprs
	fi

	DPRSEXE="$(find "${DPRSDLDIR}" -name "${DPRS}-v*.exe" | sort -V | tail -n1)"

	if [ ! -f "$DPRSEXE" ] || { [ "$(getArch "$DPRSEXE")" -ne "32" ] && [ "$(getArch "$DPRSEXE")" -ne "64" ];}; then
		dlLatestDprs
		DPRSEXE="$(find "${DPRSDLDIR}" -name "${DPRS}-v*.exe" | sort -V | tail -n1)"
	fi

	if [ ! -f "$DPRSEXE" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - Could not find or download a '$DPRS' exe"
	else

	# copy or symlink required vdf files from steam to the DPRSCOMPDATA

	DPRSPFX="${DPRSCOMPDATA//\"/}/pfx"
	VDFDSTDIR="$DPRSPFX/$DRC/$PFX86S"

	if [ -z "$SUSDA" ] || [ -z "$STUIDPATH" ]; then
		setSteamPaths
	fi
	SC="$STUIDPATH/$SRSCV"
	FSCV="$STUIDPATH/config/$SCVDF"

	AIDST="$VDFDSTDIR/$AAVDF"
	PIDST="$VDFDSTDIR/$APVDF"
	SRSDST="$VDFDSTDIR/$USDA/$STEAMUSERID/$SRSCV"
	SRLDST="$VDFDSTDIR/$USDA/${FLCV//$SUSDA\/}"
	SCDST="$VDFDSTDIR/$USDA/${FLCV//$SUSDA\/}"
	SCSHDST="$VDFDSTDIR/$USDA/$STEAMUSERID/$SCRSH"

	AISRC="$FAIVDF"
	PISRC="$PIVDF"
	SRSSRC="$STUIDPATH/$SRSCV"
	SRLSRC="$FLCV"
	SCSRC="$STUIDPATH/config/$SCVDF"
	SCSHSRC="$STUIDPATH/$SCRSH"

	{
		echo "SC $SC"
		echo "FLCV $FLCV"
		echo "FSCV $FSCV"
		echo "AIDST $AIDST"
		echo "PIDST $PIDST"
		echo "SRSDST $SRSDST"
		echo "SRLDST $SRLDST"
		echo "SCDST $SCDST"
		echo "SCSHDST $SCSHDST"
		echo "AISRC $AISRC"
		echo "PISRC $PISRC"
		echo "SRSSRC $SRSSRC"
		echo "SRLSRC $SRLSRC"
		echo "SCSRC $SCSRC"
		echo "SCSHSRC $SCSHSRC"
	} > "$STLSHM/${DPRS}-paths.txt"

	function cleanCpSrcTo {
		if [ -f "$1" ]; then
			mkProjDir "${2%/*}"
			if ! cmp -s "$1" "$2" || [ -L "$2" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Copying '$1' to '$2'"
				rm "$2" 2>/dev/null
				cp "$1" "$2"
			else
				writelog "SKIP" "${FUNCNAME[0]} - '$2' is identical to '$1'"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Source file '$1' not found"
		fi
	}

	function cleanLnSrcTo {
		if [ -f "$1" ]; then
			mkProjDir "${2%/*}"
			if [ "$(readlink -f "$2")" == "$(readlink -f "$1")" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - '$2' is already pointing to '$1'"
			else
				writelog "INFO" "${FUNCNAME[0]} - Symlinking '$1' to '$2'"
				rm "$2" 2>/dev/null
				ln -s "$1" "$2"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Source file '$1' not found"
		fi
	}

	if [ "$DPRSUSEVDFSYMLINKS" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Symlinking required vdf files from the linux steam install into '$DPRSCOMPDATA'"
		cleanLnSrcTo "$AISRC" "$AIDST"
		cleanLnSrcTo "$PISRC" "$PIDST"
		cleanLnSrcTo "$SRSSRC" "$SRSDST"
		cleanLnSrcTo "$SRLSRC" "$SRLDST"
		cleanLnSrcTo "$SCSRC" "$SCDST"
		cleanLnSrcTo "$SCSHSRC" "$SCSHDST"
	elif [ "$DPRSUSEVDFSYMLINKS" -eq 0 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Copying required vdf files from the linux steam install to '$DPRSCOMPDATA'"
		cleanCpSrcTo "$AISRC" "$AIDST"
		cleanCpSrcTo "$PISRC" "$PIDST"
		cleanCpSrcTo "$SRSSRC" "$SRSDST"
		cleanCpSrcTo "$SRLSRC" "$SRLDST"
		cleanCpSrcTo "$SCSRC" "$SCDST"
		cleanCpSrcTo "$SCSHSRC" "$SCSHDST"
	else
		writelog "WARN" "${FUNCNAME[0]} - DPRSUSEVDFSYMLINKS is neither '1' nor '0' - this shouldn't happen!"
	fi

	# start $DPRS via proton starting here

		writelog "INFO" "${FUNCNAME[0]} - Using Proton Version '$USEDPRSPROTON'"

		if test -z "$DPRSPROTON" || [ ! -f "$DPRSPROTON" ]; then
			if [ -z "${ProtonCSV[0]}" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Don't have the Array of available Proton versions yet - creating"
				getAvailableProtonVersions "up" X
			fi
			DPRSPROTON="$(getProtPathFromCSV "$USEDPRSPROTON")"
		fi

		if [ ! -f "$DPRSPROTON" ]; then
			DPRSPROTON="$(fixProtonVersionMismatch "USEDPRSPROTON" "$STLGAMECFG" X)"
		fi

		if [ ! -f "$DPRSPROTON" ]; then
			writelog "WARN" "${FUNCNAME[0]} - proton file '$DPRSPROTON' for proton version '$USEDPRSPROTON' not found - trying 'USEPROTON' instead"
			DPRSPROTON="$(getProtPathFromCSV "$USEPROTON")"
		fi

		CHECKWINED="$(dirname "$DPRSPROTON")/$DBW"
		CHECKWINEF="$(dirname "$DPRSPROTON")/$FBW"
		if [ -f "$CHECKWINED" ]; then
			DPRSWINE="$CHECKWINED"
		elif [ -f "$CHECKWINEF" ]; then
			DPRSWINE="$CHECKWINEF"
		fi

		if [ -f "$DPRSWINE" ];then
			writelog "INFO" "${FUNCNAME[0]} - Starting '$DPRSEXE' using '$DPRSWINE'"
			DPRSWINEDEBUG="-all"
			DPRSSUDIR="$DPRSPFX/$DRCU/$STUS/$APDA/$DPRS"
			mkProjDir "$DPRSSUDIR"
			cd "$DPRSSUDIR" >/dev/null || return
			sleep 1
			# LC_ALL="C" was previously used here, but may not be required anymore
			PATH="$STLPATH" LD_LIBRARY_PATH="" LD_PRELOAD="" WINE="$DPRSWINE" WINEARCH="win64" WINEDEBUG="$DPRSWINEDEBUG" WINEPREFIX="$DPRSPFX" "$DPRSWINE" "$DPRSEXE" >/dev/null 2>/dev/null
			cd - >/dev/null || return
		else
			writelog "ERROR" "${FUNCNAME[0]} - Proton Wine for not found - can't start '$DPRSEXE'"
		fi
	fi
}

function checkDep {
	CATNAM="$1"
	CHECKPROG="$2"

	if [ -n "${!CATNAM##*[!0-9]*}" ]; then
		if [ "${!CATNAM}" -eq 1 ]; then
			if [ ! -x "$(command -v "$CHECKPROG")" ]; then
				writelog "WARN" "${FUNCNAME[0]} - Disabling '$CATNAM' because '$CHECKPROG' is missing"
				notiShow "$(strFix "$NOTY_PROGRAMMISSING" "$CATNAM" "$CHECKPROG")"
				unset "$CATNAM"
			fi
		fi
	fi
}

function checkExtDeps {
	if [ -z "$NOTY" ]; then
		NOTY="notify-send"
	fi

	checkDep "USEGAMEMODERUN" "$GAMEMODERUN"
	checkDep "USEGAMESCOPE" "$GAMESCOPE"
	checkDep "RUN_NYRNA" "$NYRNA"
	checkDep "STRACERUN" "$STRACE"
	checkDep "RUN_WINETRICKS" "$WINETRICKS"
	checkDep "WINETRICKSPAKS" "$WINETRICKS"
	checkDep "RUN_REPLAY" "$REPLAY"
	checkDep "USELUXTORPEDA" "$LUXTORPEDACMD"
	checkDep "USEROBERTA" "$ROBERTACMD"
	checkDep "USEBOXTRON" "$BOXTRONCMD"
	checkDep "RUNSBSVR" "$VRVIDEOPLAYER"
	checkDep "USENETMON" "$NETMON"
	checkDep "USENOTIFIER" "$NOTY"
	checkDep "CHECKHMD" "$LSUSB"
	checkDep "BACKUPSTEAMUSER" "$RSYNC"
	checkDep "USESPECIALK" "$SEVZA"
	checkDep "USEMANGOAPP" "$MANGOAPP"
	checkDep "USEOBSCAP" "$OBSCAP"
}

function setYadBin {
	local YADINSTLDLDIR
	local YADINSTLDEPS

	function findYad {
		SEARCHDIR="$1"

		for f in "$SEARCHDIR"/*
		do
			MATCHFILE="$( basename "$f" | grep -ioE "(.*yad).*\.appimage$" )"
			if [ -f "$SEARCHDIR/$MATCHFILE" ]; then
				echo "$MATCHFILE"
				return
			fi
		done
	}

	if [ -f "$1" ]; then
		YADFILE="$1"
	elif [ "$1" == "conty" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using conty as yad binary"
		if [ ! -f "$CONTYDLDIR/$CONTY" ]; then
			StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "${CONTY%%.*}")" "dlConty" "DownloadContyStatus"
		else
			writelog "INFO" "${FUNCNAME[0]} - Found Conty binary in '$CONTYDLDIR/$CONTY'"
		fi

		if [ -f "$CONTYDLDIR/$CONTY" ]; then
			if [ "$(readlink "$CONTYDLDIR/$YAD")" == "$CONTYDLDIR/$CONTY" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Symlink '$CONTYDLDIR/$YAD' already points to '$CONTYDLDIR/$CONTY'"
			else
				writelog "INFO" "${FUNCNAME[0]} - Creating symlink '$CONTYDLDIR/${YAD##*/}' pointing to '$CONTYDLDIR/$CONTY'"
				ln -s "$CONTYDLDIR/$CONTY" "$CONTYDLDIR/${YAD##*/}"
			fi

			if [ "$(readlink "$CONTYDLDIR/${YAD##*/}")" == "$CONTYDLDIR/$CONTY" ]; then
				YADFILE="$CONTYDLDIR//${YAD##*/}"
			fi
		fi

	elif [ "$1" == "ai" ] || [ "$1" == "appimage" ]; then
		# YADSTLIMAGE="Yad-8418e37-x86_64.AppImage"
		YADSTLIMAGE="Yad-13.0-x86_64.AppImage"
		YAIDL="$YAIURL/$YADSTLIMAGE/$YADSTLIMAGE"
		YADAPPIMAGE="$YADSTLIMAGE"
		DLCHK="sha512sum"
		mkProjDir "$YADAIDLDIR"

		if [ -n "$2" ] && grep -q "^http" <<< "$2"; then
			YAIDL="$2"
			DLCHK="X"
			if grep -q "AppImage$" <<< "$YAIDL"; then
				YADAPPIMAGE="${YAIDL##*/}"
			else
				YADAPPIMAGE="Yad-$(date +%Y%m%d)-x86_64.AppImage"
			fi

			writelog "INFO" "${FUNCNAME[0]} - Trying to download $YAD from provided url '$YAIDL'"
		elif [ -n "$2" ] && [ -f "$2" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Trying to use provided file '$2' for yad"
			YAIDST="$2"
			DLCHK="X"
		elif [ -n "$2" ] && [ ! -f "$2" ]; then
			if [ "$2" == "sd" ]; then
				export ONSTEAMDECK=1
				YADAIDLDIR="$STLSDPATH"
				YAIDST="$YADAIDLDIR/$YADAPPIMAGE"
				YAIURL="$GHURL/sonic2kk/steamtinkerlaunch-tweaks/releases/download"
				YAIDL="$YAIURL/$YADSTLIMAGE/$YADSTLIMAGE"
				YADAPPIMAGE="$YADSTLIMAGE"

				# If offline and Yad isn't installed, try to find Yad somewhere sensible
				if [ "$INTERNETCONNECTION" -eq 0 ] && ! [ -f "$YAIDST" ]; then
					writelog "WARN" "${FUNCNAME[0]} - No internet connection, searching for locally saved Yad AppImage"
					echo "No internet connection, searching for locally saved Yad AppImage..."
					YADINSTLDLDIR="$( findYad "$STLDLDIR/yadappimage" )"
					YADINSTLDEPS="$( findYad "$STLDEPS" )"

					if [ -f "$STLDLDIR/yadappimage/$YADAPPIMAGE" ]; then
						# Yad in $HOME/.config/steamtinkerlaunch/downloads with full matching name
						writelog "INFO" "${FUNCNAME[0]} - Found local Yad AppImage in '$STLDLDIR/yadappimage/$YADAPPIMAGE'"
						echo "Found local Yad AppImage in '$STLDLDIR/yadappimage/$YADAPPIMAGE'!"
						mv "$STLDLDIR/yadappimage/$YADAPPIMAGE" "$YAIDST" || steamDeckInstallFail
					elif [ -f "$STLDEPS/$YADAPPIMAGE" ]; then
						# Yad in $HOME/stl/deps with full matching name
						writelog "INFO" "${FUNCNAME[0]} - Found local Yad AppImage in '$STLDEPS/$YADAPPIMAGE'"
						echo "Found local Yad AppImage in '$STLDEPS/$YADAPPIMAGE'!"
						mv "$STLDEPS/$YADAPPIMAGE" "$YAIDST" || steamDeckInstallFail
					elif [ -n "$YADINSTLDLDIR" ]; then
						# Partial match for Yad in STLDLDIR but only with filename starting with `yad` and ending with `.appimage` (case insensitive)
						writelog "INFO" "${FUNCNAME[0]} - Found local Yad AppImage in '$STLDLDIR/yadappimage/$YADINSTLDLDIR'"
						echo "Found local Yad AppImage in '$STLDLDIR/yadappimage/$YADINSTLDLDIR'!"
						mv "$STLDLDIR/yadappimage/$YADINSTLDLDIR" "$YAIDST" || steamDeckInstallFail
					elif [ -n "$YADINSTLDEPS" ]; then
						# Partial match for Yad in STLDEPS but only with filename starting with `yad` and ending with `.appimage` (case insensitive)
						writelog "INFO" "${FUNCNAME[0]} - Found local Yad AppImage in '$STLDEPS/$YADINSTLDEPS'"
						echo "Found local Yad AppImage in '$STLDEPS/$YADINSTLDEPS'!"
						mv "$STLDEPS/$YADINSTLDEPS" "$YAIDST" || steamDeckInstallFail
					else
						# No match for Yad anywhere, offline installation will most likely fail
						# Some ISPs will fail the offline check even if a user is online, so we will attempt to download Yad anyway if we can't find it - This may be problematic though!
						# Here be dragons!
						writelog "ERROR" "${FUNCNAME[0]} - Cannot find locally saved Yad AppImage, will attempt to download from GitHub anyway - See #704"
						echo "Cannot find locally saved Yad AppImage, will attempt to download from GitHub anyway - See #704"
						# return 1;
					fi
				else
					writelog "INFO" "${FUNCNAME[0]} - Downloading default AppImage for SteamDeck to '$YADAIDLDIR'"
				fi
			else
				writelog "WARN" "${FUNCNAME[0]} - Provided string '$2' is neither a http download url nor a valid absolute path to a file - downloading and using the default instead"
			fi
		fi

		if [ -f "$YAIDST" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using Yad AppImage under '$YAIDST'"
		else
			YAIDST="$YADAIDLDIR/$YADAPPIMAGE"
			if [ "$DLCHK" == "sha512sum" ]; then
				INCHK="$("$WGET" -q "${YAIDL}.${DLCHK}" -O - 2> >(grep -v "SSL_INIT") | cut -d ' ' -f1)"
			else
				INCHK="$NON"
			fi

			# Only show download notification if we didn't find local AppImage on Steam Deck
			if [ "$ONSTEAMDECK" -eq 1 ]; then
				notiShow "$( strFix "$NOTY_STEAMDECK_DEPSDOWNLOAD" "Yad" )" "X"
				strFix "$NOTY_STEAMDECK_DEPSDOWNLOAD" "Yad"
			fi

			# Yad Download
			dlCheck "$YAIDL" "$YAIDST" "$DLCHK" "Downloading '$YAIDL' to '$YAIDST'" "$INCHK"
		fi
		if [ -f "$YAIDST" ]; then
			YADFILE="$YAIDST"
		fi
	else
		writelog "ERROR" "${FUNCNAME[0]} - '$1' is no valid option"
	fi

	if [ -n "$YADFILE" ]; then
		MINYAD="7.2"
		chmod +x "$YADFILE" 2>/dev/null
		if [ "$ONSTEAMDECK" -eq 1 ]; then
			# skipping version check on SteamDeck, because the program might have been started via ssh, and yad requires a display
			writelog "INFO" "${FUNCNAME[0]} - Using '$YADFILE' on SteamDeck"
			if [ ! -f "$YADAIDLDIR/yad" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Creating symlink from '$YADFILE' to '$YADAIDLDIR/$YAD'"
				ln -s "$YADFILE" "$YADAIDLDIR/yad"
			fi
			touch "$FUPDATE"
			updateConfigEntry "YAD" "$YADAIDLDIR/yad" "$STLDEFGLOBALCFG"  # Update Yad entry on config file on Steam Deck to point to symlink in deps dir
		else
			YADVER="$("$YADFILE" --version | tail -n1 | cut -d ' ' -f1)"
			if [ "$(printf '%s\n' "$MINYAD" "$YADVER" | sort -V | head -n1)" != "$MINYAD" ] || grep -qi "[A-Z]" <<< "$YADVER" ; then
				writelog "ERROR" "${FUNCNAME[0]} - Version for '$YADFILE' is invalid. You need to at least version '$MINYAD'"
			else
				writelog "INFO" "${FUNCNAME[0]} - configuring yad binary to '$YADFILE'"
				touch "$FUPDATE"
				updateConfigEntry "YAD" "$YADFILE" "$STLDEFGLOBALCFG"
			fi
		fi
	fi
}

function checkIntDeps {

	if [ "$SKIPINTDEPCHECK" -eq 1 ] || [ "$1" == "yad" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Skipping dependency check for internally used programs"
	else
		DEPSMISSING=0

		while read -r INTDEP; do
			if [ ! -x "$(command -v "${!INTDEP}")" ]; then
				writelog "ERROR" "${FUNCNAME[0]} - ${!INTDEP} not found!" "E"
				notiShow "$(strFix "$NOTY_NOTFOUND" "${!INTDEP}")"
				DEPSMISSING=1
			fi
		done <<< "$(sed -n "/^#STARTINTDEPS/,/^#ENDINTDEPS/p;/^#ENDINTDEPS/q" "$0" | grep -v "^#" | cut -d '=' -f1)"

		if [ -z "$YAD" ]; then
			YAD="$(command -v "yad")"
		fi

		if [ -n "$YAD" ] && [ ! -f "$YAD" ]; then
			OYAD="$YAD"
			writelog "WARN" "${FUNCNAME[0]} - Configured YAD '$YAD' was not found!  Trying to find in in a new location" "E"
			NYAD="$(command -v "yad")"
			if [ -n "$NYAD" ] && [ "$NYAD" != "$OYAD" ];then
				writelog "INFO" "${FUNCNAME[0]} - Updating YAD from '$OYAD' to '$NYAD'" "E"
				YAD="$NYAD"
				touch "$FUPDATE"
				updateConfigEntry "YAD" "$NYAD" "$STLDEFGLOBALCFG"
			else
				writelog "WARN" "${FUNCNAME[0]} - Could not find a new $YAD version" "E"
			fi
		fi

		if [ ! -x "$(command -v "$YAD")" ]; then
			DEPSMISSING=1
			writelog "ERROR" "${FUNCNAME[0]} - Yad dependency ('$YAD') was not found! Check '${PROGCMD} --help' for alternatives and/or read '$PROJECTPAGE/wiki/Yad'" "E"
			notiShow "$(strFix "$NOTY_NOTFOUND" "$YAD")"
		fi


		if [ ! -x "$(command -v "steam")" ] && [ "$INFLATPAK" -eq 0 ]; then
			DEPSMISSING=1
			echo "ERROR" "Steam not found"
			writelog "ERROR" "${FUNCNAME[0]} - Steam not found"
		fi

		setAwkBin

		if [ "$ONSTEAMDECK" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Skipping yad version check on SteamDeck"
		else
			MINYAD="7.2"
			YADVER="0"

			if [ -f "$YAD" ]; then
				YADVER="$("$YAD" --version | tail -n1 | cut -d ' ' -f1)"
				writelog "INFO" "${FUNCNAME[0]} - Result of version check for yad binary '$YAD' is '$YADVER'"
			fi

			if [ "$(printf '%s\n' "$MINYAD" "$YADVER" | sort -V | head -n1)" != "$MINYAD" ]; then
				writelog "ERROR" "${FUNCNAME[0]} - Yad version '$YADVER' is too old. You need to update to at least '$MINYAD'" "E"
				notiShow "$(strFix "$NOTY_NOTFOUND2" "$YADVER" "$MINYAD")"
				if [ "$1" != "--help" ] && [ "$1" != "-h" ]; then
					exit
				fi
			else
				# If the Yad version is valid, and if we don't have Yad in the config file currently, write it out
				if [ -f "$YAD" ] && grep -q "YAD=\"\"" "$STLDEFGLOBALCFG"; then
					writelog "INFO" "${FUNCNAME[0]} - Internal Yad variable is '$YAD' but Yad is not defined in the Global Config, updating with Yad variable value..."

					touch "$FUPDATE"
					updateConfigEntry "YAD" "$YAD" "$STLDEFGLOBALCFG"
				else
					writelog "INFO" "${FUNCNAME[0]} - Yad is set correctly in the Global Config, nothing to do."
				fi
			fi
		fi

		if [ "$DEPSMISSING" -eq 1 ]; then
			writelog "ERROR" "${FUNCNAME[0]} - Above programs need to be installed to use '${PROGNAME,,}'" "E"
			writelog "ERROR" "${FUNCNAME[0]} The dependency check can be disabled by enabling 'SKIPINTDEPCHECK' - exiting now" "E"
			if [ "$1" != "--help" ] && [ "$1" != "-h" ]; then
				exit
			fi
		fi
	fi
}

#####################################################
### CORE LAUNCH START ###

function setLinGameVals {
	if [ "$ISGAME" -eq 3 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Looks like this is a native linux game - disabling some values which are only useful for win games"
		USEVORTEX="0"
		WINETRICKSPAKS="$NON"
		RUN_WINETRICKS="0"
		RUN_WINECFG="0"
		REGEDIT="0"
		VIRTUALDESKTOP="0"
		USERESHADE="0"
		UUUSEIGCS="0"
		IGCSDST="0"
		USESPECIALK="0"
		USEFWS="0"
		USEGEOELF="0"
	fi
}

function prepareProton {
	if [ "$ISGAME" -eq 2 ]; then
		if [ "$USEWINE" -eq 0 ]; then
			if [ "$EARLYUSEWINE" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Wine has just been disabled, so preparing Proton now"
				getAvailableProtonVersions "up"
				checkStartMode
			fi

			if [ "$HAVEINPROTON" -eq 0 ]; then
				if [ -n "$USEPROTON" ] && [ ! -f "$(getProtPathFromCSV "$USEPROTON")" ]; then
					fixProtonVersionMismatch "USEPROTON" "$STLGAMECFG"
				fi

				# (re)initialize Proton dependant variables, when USEPROTON changed in a menu above
				if [ -n "$USEPROTON" ] && [ "$USEPROTON" != "$FIRSTGAMEUSEPROTON" ] && [ "$ISGAME" -eq 2 ]; then
					writelog "INFO" "${FUNCNAME[0]} - setNewProtVars, because '$USEPROTON' != '$FIRSTGAMEUSEPROTON'"
					setNewProtVars "$(getProtPathFromCSV "$USEPROTON")"
				fi
			else
				USEPROTON="$INPROTV"
				setNewProtVars "$(getProtPathFromCSV "$USEPROTON")"
				writelog "INFO" "${FUNCNAME[0]} - Checking the Proton version is not required, because $SLO is used. Proton version set in Steam is '$USEPROTON'"
			fi

			if [ "$USEWINEDEBUGPROTON" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Using '$STLWINEDEBUG' as Proton 'WINEDEBUG' parameters, because 'USEWINEDEBUGPROTON' is enabled"
				export WINEDEBUG="$STLWINEDEBUG"
			fi

			if [ -n "$STLWINEDLLOVERRIDES" ] && [ "$STLWINEDLLOVERRIDES" != "$NON" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Using '$STLWINEDLLOVERRIDES' as 'WINEDLLOVERRIDES' parameter"
				export WINEDLLOVERRIDES="$WINEDLLOVERRIDES;$STLWINEDLLOVERRIDES"
			fi
		fi

		# export DXVK_CONFIG_FILE if STLDXVKCFG was found or if USEDLSS is enabled:
		DXDLSSCFG="$GLOBALMISCDIR/dxvk-no-nvapiHack.conf"

		if [ "$USE_STLDXVKCFG" -eq 1 ]; then
			if [ ! -f "$STLDXVKCFG" ]; then
				writelog "INFO" "${FUNCNAME[0]} - USE_STLDXVKCFG is enabled, but the config '$STLDXVKCFG' does not exist yet - creating a blank one"
				echo "## $(strFix "$STLDXVKCFG_WARNING" "$DXVKURL")" > "$STLDXVKCFG"
			fi
			export DXVK_CONFIG_FILE="$STLDXVKCFG"
			if [ -n "$DXVK_HUD" ] && [ "$DXVK_HUD" -eq 0 ]; then
				writelog "INFO" "${FUNCNAME[0]} - disabling variable DXVK_HUD which is set to 0 to make the variable DXVK_CONFIG_FILE functional"
				unset DXVK_HUD
			fi
			writelog "INFO" "${FUNCNAME[0]} - exporting variable 'DXVK_CONFIG_FILE' pointing to '$DXVK_CONFIG_FILE'"
		elif [ "$USEDLSS" -eq 1 ] && [ -f "$DXDLSSCFG" ]; then
			writelog "INFO" "${FUNCNAME[0]} - DLSS is enabled and '$DXDLSSCFG' was found - enabling all options required for DLSS"
			export DXVK_CONFIG_FILE="$DXDLSSCFG"
			if [ -n "$DXVK_HUD" ] && [ "$DXVK_HUD" -eq 0 ]; then
				writelog "INFO" "${FUNCNAME[0]} - disabling variable DXVK_HUD which is set to 0 to make the variable DXVK_CONFIG_FILE functional"
				unset DXVK_HUD
			fi
			export PROTON_ENABLE_NVAPI=1
			export PROTON_HIDE_NVIDIA_GPU=0
		fi

		# set VKD3D variables, if it's STL_ variant is != "$NON"

		if [ "$STL_VKD3D_CONFIG" != "$NON" ]; then
			export VKD3D_CONFIG="$STL_VKD3D_CONFIG"
		fi

		if [ "$STL_VKD3D_DEBUG" != "$NON" ]; then
			export VKD3D_DEBUG="$STL_VKD3D_DEBUG"
		fi

		if [ "$STL_VKD3D_SHADER_DEBUG" != "$NON" ]; then
			export VKD3D_SHADER_DEBUG="$STL_VKD3D_SHADER_DEBUG"
		fi

		if [ "$STL_VKD3D_LOG_FILE" != "$NON" ]; then
			export VKD3D_LOG_FILE="$STL_VKD3D_LOG_FILE"
		fi

		if [ "$STL_VKD3D_VULKAN_DEVICE" != "$NON" ]; then
			export VKD3D_VULKAN_DEVICE="$STL_VKD3D_VULKAN_DEVICE"
		fi

		if [ "$STL_VKD3D_FILTER_DEVICE_NAME" != "$NON" ]; then
			export VKD3D_FILTER_DEVICE_NAME="$STL_VKD3D_FILTER_DEVICE_NAME"
		fi

		if [ "$STL_VKD3D_DISABLE_EXTENSIONS" != "$NON" ]; then
			export VKD3D_DISABLE_EXTENSIONS="$STL_VKD3D_DISABLE_EXTENSIONS"
		fi

		if [ "$STL_VKD3D_TEST_DEBUG" != "$NON" ]; then
			export VKD3D_TEST_DEBUG="$STL_VKD3D_TEST_DEBUG"
		fi

		if [ "$STL_VKD3D_TEST_FILTER" != "$NON" ]; then
			export VKD3D_TEST_FILTER="$STL_VKD3D_TEST_FILTER"
		fi

		if [ "$STL_VKD3D_TEST_EXCLUDE" != "$NON" ]; then
			export VKD3D_TEST_EXCLUDE="$STL_VKD3D_TEST_EXCLUDE"
		fi

		if [ "$STL_VKD3D_TEST_PLATFORM" != "$NON" ]; then
			export VKD3D_TEST_PLATFORM="$STL_VKD3D_TEST_PLATFORM"
		fi

		if [ "$STL_VKD3D_TEST_BUG" != "$NON" ]; then
			export VKD3D_TEST_BUG="$STL_VKD3D_TEST_BUG"
		fi

		if [ "$STL_VKD3D_PROFILE_PATH" != "$NON" ]; then
			export VKD3D_PROFILE_PATH="$STL_VKD3D_PROFILE_PATH"
		fi

		# shortcut to enable all required flags for SBSVR with $GEOELF
		if [ "$SBSVRGEOELF" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - $PROGNAME - $SBSVRGEOELF enabled - starting game in SBS-VR using $GEOELF"
			export RUNSBSVR=1
			export USEGEOELF=1
		# shortcut to enable all required flags for SBSVR with ${RESH}
		elif [ "$SBSVRRS" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - $PROGNAME - SBSVRRS enabled - starting game in SBS-VR using ${RESH}"
			export RUNSBSVR=1
			export RESHADE_DEPTH3D=1
			export USERESHADE=1

			setVulkanPostProcessor
		fi

		if [ "$SBSRS" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - $PROGNAME - SBSRS enabled - starting game in SBS using ${RESH}"
			export RESHADE_DEPTH3D=1
			export USERESHADE=1

			setVulkanPostProcessor
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - No Proton game"
	fi
}

function sortGameArgs {
	if [ -z "$SLOARGS" ] || [ "$SLOARGS" == "$NON" ]; then
		SLOARGS="$NON"

		if [ "$SORTGARGS" -eq 1 ]; then
			if [ "${#ORGCMDARGS[@]}" -ge 1 ]; then
				CFSTG="coming from Steam/the game"
				if [ -n "$ARGUMENTS" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Game arguments '$ARGUMENTS' $CFSTG directly"
					writelog "INFO" "${FUNCNAME[0]} - Separating them from the found arguments '${ORGCMDARGS[*]}'"

					while read -r arg; do
						if [ -n "$arg" ] && [ "$arg" != "" ] && grep -q -- "$arg" <<< "$ARGUMENTS" ; then
							writelog "INFO" "${FUNCNAME[0]} - '$arg' is an argument $CFSTG, because it is within ARGUMENTS: '$ARGUMENTS'"
							mapfile -d " " -t -O "${#HARGSARR[@]}" HARGSARR <<< "$arg"
						elif [[ ! "${HARGSARR[*]}" =~ $arg ]] && [ -n "$arg" ] && [ "$arg" != "" ]; then
							writelog "INFO" "${FUNCNAME[0]} - '$arg' is probably an argument coming from $SLO"
							mapfile -d " " -t -O "${#SLOARRGS[@]}" SLOARRGS <<< "$arg"
						else
							writelog "SKIP" "${FUNCNAME[0]} - skipping '$arg'"
						fi
					done <<< "$(printf "%s\n" "${ORGCMDARGS[@]}")"

					if [ "${#HARGSARR[@]}" -ge 1 ] && [ "$HARDARGS" == "$NOPE" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Filling empty game config variable 'HARDARGS' with arguments $CFSTG"
						HARDARGSR="$(printf "%s" "${HARGSARR[@]}" | tr '\n' ' ')"
						HARDARGS="${HARDARGSR%*[[:blank:]]}"
						touch "$FUPDATE"
						updateConfigEntry "HARDARGS" "$HARDARGS" "$STLGAMECFG"
					fi

				else
					writelog "INFO" "${FUNCNAME[0]} - No game arguments $CFSTG found"
					writelog "INFO" "${FUNCNAME[0]} - So assuming '${ORGCMDARGS[*]}' comes completely from $SLO."
					while read -r arg; do
						mapfile -d " " -t -O "${#SLOARRGS[@]}" SLOARRGS <<< "$arg"
					done <<< "$(printf "%s\n" "${ORGCMDARGS[@]}")"
				fi

				if [ "${#SLOARRGS[@]}" -ge 1 ]; then
					SLOARGSR="$(printf "%s" "${SLOARRGS[@]}" | tr '\n' ' ')"
					SLOARGS="${SLOARGSR%*[[:blank:]]}"
					writelog "INFO" "${FUNCNAME[0]} - Set SLOARGS to '$SLOARGS'"
				fi
			else
				writelog "SKIP" "${FUNCNAME[0]} - No game arguments $CFSTG or via $SLO"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Sorting the game arguments is disabled"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - Nothing to do on the 2nd run"
	fi
}

function initOldProtonArr {
	delEmptyFile "$PROTONCSV"
	if [ -f "$PROTONCSV" ]; then
		unset ProtonCSV
		writelog "INFO" "${FUNCNAME[0]} - Creating an initial array with available Proton versions using the file '$PROTONCSV' which was created during a previous run"
		mapfile -t -O "${#ProtonCSV[@]}" ProtonCSV < "$PROTONCSV"
	fi
}

function initFirstProton {
	initOldProtonArr
	if [ "$HAVEINPROTON" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - skipping function, because Proton version is provided by commandline ($PROGCMD used via '$SLO')"
	else
		writelog "INFO" "${FUNCNAME[0]} - Initializing Proton"

		FUP1="$(grep "^USEPROTON=" "$STLDEFGAMECFG" | cut -d '=' -f2)"
		FIRSTUSEPROTON="${FUP1//\"}"

		if [ -z "$FIRSTUSEPROTON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - No Proton version available in template yet - searching for one"
			FIRSTUSEPROTON="$(getDefaultProton)"
			touch "$FUPDATE"
			updateConfigEntry "USEPROTON" "$USEPROTON" "$STLDEFGAMECFG"
			writelog "INFO" "${FUNCNAME[0]} - Updated 'USEPROTON' in '$STLDEFGAMECFG' to '$USEPROTON'"
		fi
		if [ ! -f "$(getProtPathFromCSV "$FIRSTUSEPROTON")" ]; then
			USEPROTON="$FIRSTUSEPROTON"
			fixProtonVersionMismatch "USEPROTON" "$STLDEFGAMECFG"
			FUP1="$(grep "^USEPROTON=" "$STLDEFGAMECFG" | cut -d '=' -f2)"
			FIRSTUSEPROTON="${FUP1//\"}"
		fi

		writelog "INFO" "${FUNCNAME[0]} - Initial Proton version 'FIRSTUSEPROTON' from '$STLDEFGAMECFG' is '$FIRSTUSEPROTON'"
	fi
}

function setMangoHudCfg {
	if [ "$USEMANGOHUDSTLCFG" -eq 1 ]; then
		mkProjDir "$MAHUCID"
		mkProjDir "$MAHUCTI"
		MHIC="$MAHUCID/${AID}.conf"
		writelog "INFO" "${FUNCNAME[0]} - Using internal $MAHU config '$MHIC' as requested"
		getGameName "$AID"
		MHTC="$MAHUCTI/${GAMENAME}.conf"

		if [ ! -f "$MHIC" ]; then
			writelog "WARN" "${FUNCNAME[0]} - '$MHIC' does not exist (yet) - trying to create it from the template '$MAHUTMPL'"
			if [ ! -f "$MAHUTMPL" ]; then
				writelog "WARN" "${FUNCNAME[0]} - The template '$MAHUTMPL' does not exist (yet) - trying to get the upstream example config"
				MAHUEX="/usr/share/doc/mangohud/MangoHud.conf.example"
				if [ -f "$MAHUEX" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Copying '$MAHUEX' to '$MAHUTMPL'"
					cp "$MAHUEX" "$MAHUTMPL"
				else
					writelog "SKIP" "${FUNCNAME[0]} - Could not find '$MAHUEX'"
				fi
			fi

			if ! grep -q "$MAHULID/XXX" "$MAHUTMPL" && ! grep -q "^output_folder" "$MAHUTMPL" ; then
				writelog "INFO" "${FUNCNAME[0]} - Appending default $MAHU output_folder to '$MAHUTMPL', because none is configured"
				echo "output_folder = $MAHULID/XXX" >> "$MAHUTMPL"
			fi

			if [ -f "$MAHUTMPL" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Copying '$MAHUTMPL' to '$MHIC'"
				cp "$MAHUTMPL" "$MHIC"
				if grep -q "$MAHULID/XXX" "$MHIC"; then
					writelog "INFO" "${FUNCNAME[0]} - Setting $MAHU output_folder to '$MAHULID/$AID' in $MHIC"
					sed "s:$MAHULID/XXX:$MAHULID/$AID:" -i "$MHIC"
				fi
			else
				writelog "SKIP" "${FUNCNAME[0]} - Could not find or create '$MAHUTMPL'"
			fi
		fi
		if [ -f "$MHIC" ]; then
			if grep -q "$MAHULID/$AID" "$MHIC"; then
				mkProjDir "$MAHULID/$AID"
				mkProjDir "$MAHULTI"
				createSymLink "${FUNCNAME[0]}" "$MAHULID/$AID" "$MAHULTI/${GAMENAME}"
			fi

			writelog "INFO" "${FUNCNAME[0]} - Pointing variable MANGOHUD_CONFIGFILE to '$MHIC'"
			export MANGOHUD_CONFIGFILE="$MHIC"
			createSymLink "${FUNCNAME[0]}" "$MHIC" "$MHTC"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Could not find or create '$MHIC'"
		fi
	fi
}

function autoBumpGE {
	if { [ "$AUTOBUMPGE" -eq 1 ] && grep -q "^GE" <<< "$USEPROTON" && [ "$ISGAME" -eq 2 ];} || [ -n "$1" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Current Proton version is $USEPROTON"
		dlLatestGE "lge" "X"
		getAvailableProtonVersions "up" X

		if [ ! -f "$PROTONCSV" ]; then
			writelog "ERROR" "${FUNCNAME[0]} - Could not find '$PROTONCSV'"
		else
			NEWESTGE="$(grep "^GE" "$PROTONCSV" | sort -V | tail -n1)"
			NEWESTGE="${NEWESTGE%%;*}"
			if [ "$NEWESTGE" == "$USEPROTON" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - $USEPROTON is already the newest GE Proton version"
			else
				writelog "INFO" "${FUNCNAME[0]} - $(strFix "$NOTY_BUMP" "$USEPROTON" "$USEPROTON" "$NEWESTGE")"
				notiShow "$(strFix "$NOTY_BUMP" "$USEPROTON" "$USEPROTON" "$NEWESTGE")"
				USEPROTON="$NEWESTGE"
				touch "$FUPDATE"
				updateConfigEntry "USEPROTON" "$USEPROTON" "$STLGAMECFG"
			fi
		fi
	fi
}

function autoBumpProton {
	if [ "$AUTOBUMPPROTON" -eq 1 ] && grep -v -q "^GE" <<< "$USEPROTON" && [ "$ISGAME" -eq 2 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Current Proton version is $USEPROTON"
		NEWESTPROTON="$(grep -v "^GE" "$PROTONCSV" | sort -V | tail -n1)"
		NEWESTPROTON="${NEWESTPROTON%%;*}"
		if [ "$NEWESTPROTON" == "$USEPROTON" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - $USEPROTON is already the newest official Proton version"
		else
			writelog "INFO" "${FUNCNAME[0]} - $(strFix "$NOTY_BUMP" "$USEPROTON" "$USEPROTON" "$NEWESTPROTON")"
			notiShow "$(strFix "$NOTY_BUMP" "$USEPROTON" "$USEPROTON" "$NEWESTPROTON")"
			USEPROTON="$NEWESTPROTON"
			touch "$FUPDATE"
			updateConfigEntry "USEPROTON" "$USEPROTON" "$STLGAMECFG"
		fi
	fi
}

function checkPev {
	if [ "$ISGAME" -eq 2 ] && [ -f "$GP" ]; then
		unset USEPEVA
		mapfile -d " " -t -O "${#USEPEVA[@]}" USEPEVA < <(printf '%s' "USEPEV_PELDD USEPEV_PEPACK USEPEV_PERES USEPEV_PESCAN USEPEV_PESEC USEPEV_PESTR USEPEV_READPE")

		if [ -n "$USEALLPEV" ] && [ "$USEALLPEV" -eq 1 ]; then
			while read -r SUSEPEV; do
				if [ -n "$SUSEPEV" ]; then
					export "$SUSEPEV=1"
				fi
			done <<< "$(printf "%s\n" "${USEPEVA[@]}")"
		fi

		while read -r USEPEV; do
			if [ "${!USEPEV}" -eq 1 ]; then
				PEVCMD="${USEPEV##*_}"
				PEVCMD="${PEVCMD,,}"
				if [ -x "$(command -v "$PEVCMD")" ]; then
					PEVDSTI="$STLGPEVKD/$PEVCMD/id/$AID"
					PEVDSTT="$STLGPEVKD/$PEVCMD/title/$GN"

					if [ "$PEVCMD" == "$PERES" ]; then
						if [ ! -d "$PEVDSTI" ] || [ "$(find "$PEVDSTI" -type f | wc -l)" -eq 0 ]; then
							mkProjDir "$PEVDSTI"
							writelog "INFO" "${FUNCNAME[0]} - $USEPEV is enabled, extracting data from '$GP' using '$PEVCMD' to '$PEVDSTI'"
							cd "$PEVDSTI" >/dev/null || return
							notiShow "$(strFix "$NOTY_ANALYZE" "$GE" "$PEVCMD")"
							"$PEVCMD" -x "$GP" &
							cd - >/dev/null || return
						else
							writelog "SKIP" "${FUNCNAME[0]} - $USEPEV is enabled, but already have files in '$PEVDSTI'"
						fi
					else
						PEVDSTF="$PEVDSTI/$PEVCMD-$AID.txt"
						if [ ! -f "$PEVDSTF" ]; then
							mkProjDir "$PEVDSTI"
							writelog "INFO" "${FUNCNAME[0]} - $USEPEV is enabled, using command '$PEVCMD' to write data from '$GP' into '$PEVDSTF'"
							notiShow "$(strFix "$NOTY_ANALYZE" "$GE" "$PEVCMD")"
							"$PEVCMD" "$GP" > "$PEVDSTF" &
						else
							writelog "SKIP" "${FUNCNAME[0]} - $USEPEV is enabled, but already have the datafile '$PEVDSTF'"
						fi
					fi
					mkProjDir "${PEVDSTT%/*}"
					if [ ! -L "$PEVDSTT" ]; then
						ln -rs "$PEVDSTI" "$PEVDSTT"
					fi
				else
					writelog "SKIP" "${FUNCNAME[0]} - $USEPEV is enabled, but command '$PEVCMD' could not be found"
				fi
			fi
		done <<< "$(printf "%s\n" "${USEPEVA[@]}")"
	fi
}

function checkAllPev {
	if  [ -n "$USEALLPEV" ] && [ "$USEALLPEV" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - 'USEALLPEV' is enabled, extracting data from '$GP' with all supported pev tools"
		checkPev
	fi
}

function setDxvkVars {
	if [ "$PROTON_USE_WINED3D" -eq 0 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Exporting variable 'DXVK_FRAME_RATE=$DXVK_FPSLIMIT' to limit game framerate"
		export DXVK_FRAME_RATE=$DXVK_FPSLIMIT
	fi
}

function prepareLaunch {
	rm "$CLOSETMP" 2>/dev/null
	rm "$KILLSWITCH" 2>/dev/null

	linkLog

	createProjectDirs
	fixShowGnAid
	saveCfg "$STLDEFGLOBALCFG" X
	loadCfg "$STLDEFGLOBALCFG" X
	writelog "START" "######### Game Launch: $SGNAID #########"

	getGameData "$AID"

	writelog "INFO" "${FUNCNAME[0]} - Game launch args '${ORGGCMD[*]}'"
	writelog "INFO" "${FUNCNAME[0]} - Gamedir '$GFD'"
	if [ -n "$GPFX" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Proton wineprefix '$GPFX'"
	fi

	if [ -z "${INGCMD[*]}" ] && [ -n "${ORGGCMD[*]}" ]; then
		writelog "INFO" "${FUNCNAME[0]} - No INGCMD but valid ORGGCMD - May be Non-Steam game"
	fi
	writelog "INFO" "${FUNCNAME[0]} -------------------"
	writelog "INFO" "${FUNCNAME[0]} - CreateGameCfg:"
	createGameCfg

	writelog "INFO" "${FUNCNAME[0]} - First LoadCfg: $STLGAMECFG"
	loadCfg "$STLGAMECFG"

	checkPev

	autoBumpGE
	autoBumpProton
	autoBumpReShade

	if [ "$ISGAME" -eq 2 ] && [ "$HAVEINPROTON" -eq 0 ]; then
		FIRSTGAMEUSEPROTON="$USEPROTON"
		writelog "INFO" "${FUNCNAME[0]} - Initial Proton version from the game config: '$FIRSTGAMEUSEPROTON'"
		if [ -n "$USEPROTON" ] && [ ! -f "$(getProtPathFromCSV "$USEPROTON")" ]; then
			fixProtonVersionMismatch "USEPROTON" "$STLGAMECFG"
		fi

		writelog "INFO" "${FUNCNAME[0]} - Initializing internal Proton Vars for '$FIRSTGAMEUSEPROTON' using setNewProtVars"
		setNewProtVars "$(getProtPathFromCSV "$USEPROTON")"
	fi

	writelog "INFO" "${FUNCNAME[0]} - OpenTrayIcon:"
	openTrayIcon X

	fixCustomMeta "$CUMETA/$AID.conf" # will be removed again later
	loadCfg "$CUMETA/$AID.conf" X
	loadCfg "$GEMETA/$AID.conf" X

	writelog "INFO" "${FUNCNAME[0]} - sortGameArgs:"
	sortGameArgs

	if [ "$ONSTEAMDECK" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - steamdeckControl:"
		steamdeckControl
	fi

	checkWaitRequester

	if [ -z "$1" ]; then
		writelog "INFO" "${FUNCNAME[0]} - AskSettings:"
		askSettings
	else
		MainMenu "$AID"
	fi

	writelog "INFO" "${FUNCNAME[0]} - Second LoadCfg after menus: $STLGAMECFG"
	loadCfg "$STLGAMECFG"

	# Setup DXVK
	if [ -n "$DXVK_SCALE" ] && [ "$DXVK_HUD" != "0" ]; then
		writelog "INFO" "${FUNCNAME[0]} - appending 'scale=$DXVK_SCALE' to the DXVK_HUD variable '$DXVK_HUD'"
		DXVK_HUD="${DXVK_HUD},scale=$DXVK_SCALE"
	fi
	setDxvkVars

	# in case a path changed in between, call createProjectDirs again:
	createProjectDirs



	# autoapply configuration settings based on the steam collections the game is in:
	autoCollectionSettings

	checkAllPev

	writelog "INFO" "${FUNCNAME[0]} - sortGameArgs again, in case something changed above:"
	sortGameArgs

	if [ "$ISGAME" -eq 2 ]; then
		writelog "INFO" "${FUNCNAME[0]} - symlinkSteamUser:"
		symlinkSteamUser "$USESUSYM"

		writelog "INFO" "${FUNCNAME[0]} - restoreSteamUser:"
		restoreSteamUser

		writelog "INFO" "${FUNCNAME[0]} - prepareProton:"
		prepareProton
	fi
####

	writelog "INFO" "${FUNCNAME[0]} - setLinGameVals :"
	setLinGameVals

#################

	# override tweak settings
	writelog "INFO" "${FUNCNAME[0]} - CheckTweakLaunch:"
	checkTweakLaunch

	if [ "$ISGAME" -eq 2 ]; then
		writelog "INFO" "${FUNCNAME[0]} - checkXliveless:"
		checkXliveless

		# choose either system winetricks or downloaded one
		writelog "INFO" "${FUNCNAME[0]} - chooseWinetricks:"
		chooseWinetricks
	fi

	# check dependencies - disable functions if dependency programs are missing and/or warn
	writelog "INFO" "${FUNCNAME[0]} - checkExtDeps:"
	checkExtDeps
	writelog "INFO" "${FUNCNAME[0]} - checkExtDeps done"

	if [ "$ISGAME" -eq 2 ]; then
		writelog "INFO" "${FUNCNAME[0]} - setWineVars:"
		setWineVars

		# start winetricks gui if RUN_WINETRICKS is 1 or silently if WINETRICKSPAKS is not empty
		writelog "INFO" "${FUNCNAME[0]} - CheckWinetricksLaunch:"
		checkWinetricksLaunch

		# start $WINECFG if RUN_WINECFG is 1
		writelog "INFO" "${FUNCNAME[0]} - CheckWineCfgLaunch:"
		checkWineCfgLaunch

		# apply some regs if requested
		writelog "INFO" "${FUNCNAME[0]} - CustomRegs:"
		customRegs
	fi

	# minimize all open windows if TOGGLEWINDOWS is 1
	writelog "INFO" "${FUNCNAME[0]} - TogWindows:"
	togWindows windowminimize

	# Pause steamwebhelper if requested
	writelog "INFO" "${FUNCNAME[0]} - StateSteamWebHelper:"
	StateSteamWebHelper pause

	if [ "$ISGAME" -eq 2 ]; then
		# install ${RESH} if USERESHADE is 1
		writelog "INFO" "${FUNCNAME[0]} - InstallReshade:"
		installReshade

		# install Depth3D Shader if RESHADE_DEPTH3D is 1
		writelog "INFO" "${FUNCNAME[0]} - installDepth3DReshade:"
		installDepth3DReshade

		# start game with ${RESH} if USERESHADE is 1
		writelog "INFO" "${FUNCNAME[0]} - checkReshade:"
		checkReshade

		# start game with ${SPEK} if USESPECIALK is 1
		writelog "INFO" "${FUNCNAME[0]} - useSpecialK:"
		useSpecialK

		# start game with $GEOELF if USEGEOELF is 1
		writelog "INFO" "${FUNCNAME[0]} - configureGeoElf:"
		configureGeoElf

		# open Shader Menu if CHOOSESHADERS is 1
		writelog "INFO" "${FUNCNAME[0]} - ChooseShaders:"
		chooseShaders
	fi

	# configure $MAHU
	if [ "$USEMANGOHUD" -eq 1 ]; then
		if [ -f "$MAHUBIN" ] || [ "$MAHUVAR" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - setMangoHudCfg:"
			setMangoHudCfg
		else
			writelog "SKIP" "${FUNCNAME[0]} - USEMANGOHUD is enabled, but $MAHU binary '$MAHUBIN' not found - disabling USEMANGOHUD"
			USEMANGOHUD=0
		fi
	fi

	# start $NYRNA if RUN_NYRNA is 1
	writelog "INFO" "${FUNCNAME[0]} - UseNyrnaz:"
	useNyrna

	# start $REPLAY if RUN_REPLAY is 1
	writelog "INFO" "${FUNCNAME[0]} - UseReplay:"
	useReplay

	# start game with side-by-side VR if RUNSBSVR is not 0
	writelog "INFO" "${FUNCNAME[0]} - CheckSBSVRLaunch:"
	checkSBSVRLaunch "$NON"

	if [ "$RUNSBS" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - CheckSBSLaunch:"
		checkSBSLaunch
	fi

	# start Vortex if USEVORTEX is 1
	if [ "$USEVORTEX" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - ${VTX^} is enabled - startVortex:"
		startVortex "ask" "$AID"
		# (for Vortex) if SELAUNCH is 0 start a preconfigured exe directly
		writelog "INFO" "${FUNCNAME[0]} - CheckVortexSELaunch with argument '$SELAUNCH':"
		checkVortexSELaunch "$SELAUNCH"
	fi

	writelog "INFO" "${FUNCNAME[0]} - checkMO2:"
	checkMO2

	# set other screen resolution
	writelog "INFO" "${FUNCNAME[0]} - setNewRes:"
	setNewRes

	# start custom user script
	writelog "INFO" "${FUNCNAME[0]} - customUserScriptStart:"
	customUserScriptStart

	# redirect STEAM_COMPAT_DATA_PATH on request
	writelog "INFO" "${FUNCNAME[0]} - redirectSCDP:"
	redirectSCDP

	if [ "$ISGAME" -eq 2 ]; then
		# start igcsinjector if USEIGCS is enabled
		writelog "INFO" "${FUNCNAME[0]} - checkIGCSInjector:"
		checkIGCSInjector

		# check if openvr-fsr is enabled
		writelog "INFO" "${FUNCNAME[0]} - checkOpenVRFSR:"
		checkOpenVRFSR
	fi

	# start '$FWS' if USEFWS is enabled
	writelog "INFO" "${FUNCNAME[0]} - CheckFWS:"
	checkFWS

	# load custom variables if available
	loadCustomVars

	# set pulse latency if CHANGE_PULSE_LATENCY is 1
	writelog "INFO" "${FUNCNAME[0]} - checkPulse:"
	checkPulse

	# start strace process in the background if STRACERUN is 1
	writelog "INFO" "${FUNCNAME[0]} - CheckStraceLaunch:"
	checkStraceLaunch

	# start network monitor process in the background if USENETMON is enabled and NETMON found
	writelog "INFO" "${FUNCNAME[0]} - CheckNetMonLaunch:"
	checkNetMonLaunch

	# start wine-discord-ipc-bridge when game starts if USE_WDIB is -eq 1
	writelog "INFO" "${FUNCNAME[0]} - checkWDIB:"
	checkWDIB

	# create/remove steam_appid.txt
	writelog "INFO" "${FUNCNAME[0]} - checkSteamAppIDFile:"
	checkSteamAppIDFile

	# start a custom program if USECUSTOMCMD is enabled
	writelog "INFO" "${FUNCNAME[0]} - CheckCustomLaunch:"
	checkCustomLaunch

	if [ "$ISGAME" -eq 2 ]; then
		# start a custom program if UUUSEPATCH or UUUSEVR is enabled
		writelog "INFO" "${FUNCNAME[0]} - checkUUUPatchLaunch:"
		checkUUUPatchLaunch
	fi

	# Automatic Grid Update Check before_game
	if [ "$SGDBAUTODL" == "before_game" ] && [ "$STLPLAY" -eq 0 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Automatic Grid Update Check '$SGDBAUTODL'"
		# getGrids "$AID" &
		commandlineGetSteamGridDBArtwork --search-id="$AID" --steam
	fi

	if [ "$ISGAME" -eq 2 ]; then
		# Create GameTitle Symlink in STLCOMPDAT if enabled
		writelog "INFO" "${FUNCNAME[0]} - setCompatDataTitle:"
		setCompatDataTitle
	fi

	# delay game launch if requested via WAITFORCUSTOMCOMMAND
	delayGameForCustomLaunch

	# GAME START
	writelog "INFO" "${FUNCNAME[0]} - launchSteamGame:"
	launchSteamGame

	if [ "$KEEPSTLOPEN" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - KEEPSTLOPEN is enabled - re-opening the $PROGNAME menu"
		prepareLaunch X
	else
		# GAME ENDED - Closing:
		writelog "STOP" "######### CLEANUP #########"
		closeSTL "######### DONE - $PROGNAME $PROGVERS #########"
	fi
}

function switchProton {
	writelog "INFO" "${FUNCNAME[0]} - Switching used Proton and its Variables to version '$1'"

	if [ ! -f "$(getProtPathFromCSV "$1")" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Tried to switch to Proton '$1', but it is not available"
		if [ "$AUTOPULLPROTON" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - AUTOPULLPROTON is enabled, so trying to download and enable '$1'"
			createDLProtList
			DLURL="$(printf "%s\n" "${ProtonDLList[@]}" | grep -m1 "$1")"
			if [ -n "$DLURL" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Download for requested '$1' found: '$DLURL'"
				StatusWindow "$GUI_DLCUSTPROT" "dlCustomProton ${DLURL//|/\"}" "DownloadCustomProtonStatus"
			else
				writelog "SKIP" "${FUNCNAME[0]} - No download URL found for requested '$1' - skipping"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - AUTOPULLPROTON is disabled, so giving up switching to proton version '$1'"
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - Requested Proton version '$1' found under '$(getProtPathFromCSV "$1")'"
	fi

	if [ -f "$(getProtPathFromCSV "$1")" ]; then
		USEPROTON="$1"
		setNewProtVars "$(getProtPathFromCSV "$USEPROTON")"
	fi
}

function prepareProtonDBRating {
	if [ "$USEPDBRATING" ] && [ "$STLPLAY" -eq 0 ]; then
		PDBRAINFO="$STLSHM/PTB-${AID}.txt"
		PDBRASINF="${PDBRAINFO//-/short-}"

		if [ ! -f "$PDBRAINFO" ];then
			mapfile -d ";" -t -O "${#PDBARR[@]}" PDBARR <<< "$(getProtonDBRating "$AID")"
			PDBCONFI="${PDBARR[0]}"
			PDBSCORE="${PDBARR[1]}"
			PDBTOTAL="${PDBARR[2]}"
			PDBTREND="${PDBARR[3]}"
			PDBBESTT="${PDBARR[4]}"
			printf '<b>ProtonDB Rating Trend:</b> %s' "${PDBTREND^}" > "$PDBRASINF"
			printf '<u>%s</u>\n<b>Confidence:</b> %s\n<b>Score:</b> %s\n<b>Total Votes:</b> %s\n<b>Rating Trend:</b> %s\n<b>Best Rating:</b> %s\n' "$GUI_USEPDBRATING" "${PDBCONFI}" "${PDBSCORE}" "${PDBTOTAL}" "${PDBTREND}" "${PDBBESTT}" > "$PDBRAINFO"
		fi
	fi
}

function getProtonDBRating {
	if [ -n "$1" ]; then
		AID="$1"
	fi

	PDBAPI="https://www.protondb.com/api/v1/reports/summaries/XXX.json"

	if [ ! -x "$(command -v "$JQ")" ]; then
		writelog "WARN" "${FUNCNAME[0]} - Can't get data from '${PDBAPI//XXX/$AID}' because '$JQ' is not installed"
	else
		DMIN=1440

		if [ -n "$PDBRATINGCACHE" ] && [ "$PDBRATINGCACHE" -ge 1 ]; then
			PDBDLDIR="$STLDLDIR/proton/rating"
			mkProjDir "$PDBDLDIR"
			PDBAJ="$PDBDLDIR/${AID}.json"
			if [ ! -f "$PDBAJ" ] || test "$(find "$PDBAJ" -mmin +"$(( DMIN * PDBRATINGCACHE ))")"; then
				rm "$PDBAJ" 2>/dev/null
				dlCheck "${PDBAPI//XXX/$AID}" "$PDBAJ" "X" "$NON"
			fi

			if [ -f "$PDBAJ" ]; then
				"$JQ" -r '. | "\(.confidence);\(.score);\(.total);\(.trendingTier);\(.bestReportedTier)"' "$PDBAJ"
			fi
		else
			"$WGET" -q "${PDBAPI//XXX/$AID}" -O - 2> >(grep -v "SSL_INIT") | "$JQ" -r '. | "\(.confidence);\(.score);\(.total);\(.trendingTier);\(.bestReportedTier)"'
		fi
	fi
}

function fixProtonVersionMismatch {
	FORCEPROTONMISMATCHRESOLVE="${3:-0}"  # I.e. passed by One-Time Run where "ISGAME -eq 3"
	if [ "$ISGAME" -eq 2 ] || [ "$FORCEPROTONMISMATCHRESOLVE" -eq 1 ]; then
		ORGPROTCAT="$1"
		MIMAPROT="${!1}"

		if [[ "$MIMAPROT" =~ "experimental" ]]; then
			# Look for replacement Proton Experimental - There should only ever be one Proton Experimental version installed at a time
			writelog "INFO" "${FUNCNAME[0]} - Mismatch for 'experimental' Proton version - Looking for updated Proton Experimental"

			# Even though there should only be one Experimental version, only take the first one just in case (should not cause issues, hopefully)
			REPLACEMENTEXPERIMENTAL="$( grep 'experimental' "$PROTONCSV" | head -1 | cut -d ';' -f1 )"
			if [ -n "$REPLACEMENTEXPERIMENTAL" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Found potential replacement for Proton Experimental with '$REPLACEMENTEXPERIMENTAL'"
				switchProton "$REPLACEMENTEXPERIMENTAL"
			else
				# If no Proton replacement, fall back to first Proton version in ProtonCSV
				# Not ideal but a user might've uninstalled Proton Experimental, and we can't assume what versions of Proton they will have except that they'll have *some* version
				FALLBACKPROTON="$( head -1 "$PROTONCSV" )"
				writelog "INFO" "${FUNCNAME[0]} - No potential replacement for Proton Experimental found, falling back to first Proton version in ProtonCSV ('$FALLBACKPROTON')"
				switchProton "$FALLBACKPROTON"
			fi

			touch "$FUPDATE"
			updateConfigEntry "USEPROTON" "$USEPROTON" "$2"
		elif [[ "$MIMAPROT" =~ "proton-unknown" ]]; then
			# Should we fall back to first item in ProtonCSV here?
			writelog "INFO" "${FUNCNAME[0]} - Skipping function for 'proton-unknown' Proton version"
		else
			writelog "INFO" "${FUNCNAME[0]} - Incoming category is '$ORGPROTCAT' with value '$MIMAPROT'"
			writelog "INFO" "${FUNCNAME[0]} - Looking for an alternative similar version for '$MIMAPROT'"

			notiShow "$(strFix "$NOTY_WANTPROTON3" "$MIMAPROT")"

			if [ ! -f "$PROTONCSV" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Looking for available Proton versions"
				getAvailableProtonVersions "up"
			fi

			MIMAPROTSHORT="^${MIMAPROT%-*}"
			NEWMINORPROT="$(grep "$MIMAPROTSHORT" "$PROTONCSV" | cut -d ';' -f1 | sort -nr | head -n1)"

			if [ -f "$(getProtPathFromCSV "${NEWMINORPROT//\"/}")" ] && [ -n "${NEWMINORPROT//\"/}" ]; then
				NEWMINORRUN="$(getProtPathFromCSV "${NEWMINORPROT//\"/}")"
				writelog "INFO" "${FUNCNAME[0]} - Found Proton '$NEWMINORPROT' in path '$NEWMINORRUN' as an alternative for the requested '$MIMAPROT'"
			elif grep -q "GE" <<< "$MIMAPROT"; then
				writelog "INFO" "${FUNCNAME[0]} - No alternative for '$MIMAPROT' found directly"
				NEWMINORPROT="$(grep "GE" "$PROTONCSV" | cut -d ';' -f1 | sort -nr | head -n1)"
				if [ -f "$(getProtPathFromCSV "${NEWMINORPROT//\"/}")" ]; then
					NEWMINORRUN="$(getProtPathFromCSV "${NEWMINORPROT//\"/}")"
					writelog "INFO" "${FUNCNAME[0]} - Using found '$NEWMINORPROT' instead, as it is at least also a GE Proton like the requested '$MIMAPROT'"
				fi
			fi

			if [ -n "$3" ]; then
				if [ -f "$NEWMINORRUN" ]; then
					writelog "INFO" "${FUNCNAME[0]} - found '$NEWMINORRUN'"
					echo "$NEWMINORRUN"
				fi
			else
				if [ -f "$NEWMINORRUN" ]; then
					switchProton "$NEWMINORPROT"
					notiShow "$(strFix "$NOTY_WANTPROTON2" "$NEWMINORPROT" "$MIMAPROT")"
				elif [ "$AUTOLASTPROTON" -eq 1 ]; then
					writelog "INFO" "${FUNCNAME[0]} - Automatically selecting newest official one"
					setNOP
				else
					writelog "INFO" "${FUNCNAME[0]} - Asking for one"
					needNewProton
				fi
			fi

			if [ ! -f "$(getProtPathFromCSV "$MIMAPROT")" ] && [ "$ORGPROTCAT" == "USEPROTON" ] && [ "$MIMAPROT" != "$NEWMINORPROT" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Automatically updating USEPROTON to '$USEPROTON' in '$2'"
				touch "$FUPDATE"
				updateConfigEntry "USEPROTON" "$USEPROTON" "$2"
				setInternalProtonVars
			fi
		fi
	fi
}

function dlConty {
	function upChkConty {
		DLCHK="sha256sum"

		notiShow "$(strFix "$NOTY_DLCONTY" "$CONTYRELURL/$CONTYDLVERS")" "S"
		dlCheck "$CONTYRELURL/$CONTYDLVERS" "$CONTYDLDIR/$CONTY" "$DLCHK" "Downloading '$CONTYRELURL/$CONTYDLVERS' to '$CONTYDLDIR'" "$INCHK"
		notiShow "$GUI_DONE" "S"
	}

	mkProjDir "$CONTYDLDIR"
	CONTYDLVERS="$("$WGET" -q "$CONTYRELURL" -O - 2> >(grep -v "SSL_INIT") | grep -m1 "download.*.$CONTY" | grep -oP 'releases\K[^"]+')"
	CONTYVERS="${CONTYDLVERS%/*}"
	CONTYVERS="${CONTYVERS##*/}"

	INCHK="$("$WGET" -q "${CONTYRELURL}/tag/${CONTYVERS}" -O - 2> >(grep -v "SSL_INIT")| grep "SHA256" -A10 | grep -m1 "${CONTY}" | cut -d ' ' -f1)"

	if [ -f "$CONTYDLDIR/$CONTY" ]; then
		if [ "$UPDATECONTY" -eq 1 ]; then
			if [ -f "$CONTYDLDIR/version.txt" ]; then
				DLVERS="$(cat "$CONTYDLDIR/version.txt")"
			else
				DLVERS="0.0"
			fi

			if [ "$DLVERS" == "$CONTYVERS" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - Skipping downloading Conty - existing version is identical to the latest upstream version '$CONTYVERS'"
			else
				writelog "INFO" "${FUNCNAME[0]} - Updating the available Conty version '$DLVERS' with the newer version '$CONTYVERS'"
				upChkConty
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Skipping downloading Conty - already have one, and update is disabled"
		fi
	else
		upChkConty
	fi

	if [ -f "$CONTYDLDIR/$CONTY" ]; then
		DLSHA="$(sha256sum "$CONTYDLDIR/$CONTY" | cut -d ' ' -f1)"
		writelog "INFO" "${FUNCNAME[0]} - : Downloaded $CONTY has sha256sum '$DLSHA' and should have '$INCHK'"

		if [ "$INCHK" == "$DLSHA" ]; then
			writelog "INFO" "${FUNCNAME[0]} - : $(strFix "$NOTY_CHECKSUM_OK" "$DLSHA")"
			notiShow "$(strFix "$NOTY_CHECKSUM_OK" "$DLSHA")" "S"
			chmod +x "$CONTYDLDIR/$CONTY"
			export RUNCONTY="$CONTYDLDIR/$CONTY"
			if grep -q "[0-9]" <<< "$CONTYVERS"; then
				writelog "INFO" "${FUNCNAME[0]} - Updating Conty version to '$CONTYVERS' in '$CONTYDLDIR/version.txt'"
				echo "$CONTYVERS" > "$CONTYDLDIR/version.txt"
			else
				writelog "SKIP" "${FUNCNAME[0]} - No version found in Url - not writing '$CONTYDLDIR/version.txt'"
			fi
		else
			writelog "ERROR" "${FUNCNAME[0]} - $(strFix "$NOTY_CHECKSUM_NOK" "$DLSHA") - removing the download"
			notiShow "$(strFix "$NOTY_CHECKSUM_NOK" "$DLSHA")" "S"
			rm "$CONTYDLDIR/$CONTY"
		fi
	fi
}

function updateConty {
	if [ "$UPDATECONTY" -eq 1 ]; then
		if [ ! -f "$CONTYDLDIR/version.txt" ]; then
			writelog "INFO" "${FUNCNAME[0]} - No Conty version file found in - so simply downloading latest release"
			StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "${CONTY%%.*}")" "dlConty" "DownloadContyStatus"
		else
			CONTYDLVERS="$("$WGET" -q "$CONTYRELURL" -O - 2> >(grep -v "SSL_INIT") | grep -m1 "download.*.$CONTY" | grep -oP 'releases\K[^"]+')"
			CONTYVERS="${CONTYDLVERS%/*}"
			CONTYVERS="${CONTYVERS##*/}"
			if [ -f "$CONTYDLDIR/version.txt" ]; then
				DLVERS="$(cat "$CONTYDLDIR/version.txt")"
			else
				DLVERS="0.0"
			fi
			writelog "INFO" "${FUNCNAME[0]} - $(strFix "$NOTY_UPDATECONTY" "$CONTYVERS" "$DLVERS")"
			notiShow "$(strFix "$NOTY_UPDATECONTY" "$CONTYVERS" "$DLVERS")"
			StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "${CONTY%%.*}")" "dlConty" "DownloadContyStatus"
		fi
	fi
}

function askConty {
		writelog "INFO" "${FUNCNAME[0]} - Opening Ask Conty Requester"
		fixShowGnAid
		export CURWIKI="$PPW/Conty"
		TITLE="${PROGNAME}-SelectConty"
		pollWinRes "$TITLE"

		setShowPic

		"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
		--title="$TITLE" \
		--text="$(spanFont "$SGNAID - $GUI_ASKCONTY" "H")" \
		--button="$BUT_DLCONTY":0 \
		--button="$BUT_SELCONTY":2 \
		--button="$SKIPCONTY":4 \
		"$GEOM"

		case $? in
			0)  {
					writelog "INFO" "${FUNCNAME[0]} - Selected to download Conty"
					StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "${CONTY%%.*}")" "dlConty" "DownloadContyStatus"
				}
			;;
			2)  {
					writelog "INFO" "${FUNCNAME[0]} - Selected to pick local Conty executable"
					PICKCONT="$("$YAD" --file)"

					if [ -f "$PICKCONT" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Picked '$PICKCONT' as Conty executable"
						export RUNCONTY="$PICKCONT"
						touch "$FUPDATE"
						updateConfigEntry "CUSTCONTY" "$PICKCONT" "$STLDEFGLOBALCFG"
						CUSTCONTY="$PICKCONT"
					else
						writelog "SKIP" "${FUNCNAME[0]} - No Conty executable selected - skipping"
						export RUNCONTY="$NON"
					fi
				}
			;;
			4)  {
					writelog "SKIP" "${FUNCNAME[0]} - Selected CANCEL - Not using Conty"
					export RUNCONTY="$NON"
				}
			;;
		esac
}

function findConty {
	if [ -n "$CUSTCONTY" ] && [ "$CUSTCONTY" != "$NON" ] && [ -x "$CUSTCONTY" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using custom Conty '$CUSTCONTY'"
		export RUNCONTY="$CUSTCONTY"
	elif [ -x "$CONTYDLDIR/$CONTY" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using downloaded Conty '$CONTYDLDIR/$CONTY'"
		updateConty
		export RUNCONTY="$CONTYDLDIR/$CONTY"
	elif [ -x "$(command -v "$CONTY")" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using Conty in 'PATH' '$("$WHICH" "$CONTY")'"
		RUNCONTY="$(command -v "$CONTY")"
		export RUNCONTY
	else
		writelog "INFO" "${FUNCNAME[0]} - No Conty found - Opening requester"
		askConty
		if [ "$RUNCONTY" != "$NON" ]; then
			"${FUNCNAME[0]}"
		fi
	fi
}

function checkConty {
	if [ "$AUTOCONTY" -eq 1 ]; then
		if [ "$(getArch "$GP")" == "32" ]; then
			CONTYMODE=1
		else
			CONTYMODE=0
		fi
	elif [ "$USECONTY" -eq 1 ]; then
		CONTYMODE=2
	else
		CONTYMODE=0
	fi

	if [ "$CONTYMODE" -ge 1 ]; then
		findConty

		if [ -x "$RUNCONTY" ] && [ "$RUNCONTY" != "$NON" ]; then
			if [ "$CONTYMODE" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - '$GP' is 32bit - Starting it using Conty executable '$RUNCONTY'"
			else
				writelog "INFO" "${FUNCNAME[0]} - Starting '$GP' using Conty executable '$RUNCONTY', because 'USECONTY' is enabled"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - No Conty executable found 'RUNCONTY' is '$RUNCONTY' - continuing regularly"
			CONTYMODE=0
		fi
	fi
}

function logPlayTime {
	# Takes seconds and converts it into HH:MM:SS
	function getLengthPlayed {
		((h=${1}/3600))
		((m=(${1}%3600)/60))
		((s=${1}%60))

		if [ "$h" -gt 0 ]; then
			printf "%02d hrs, %02d mins, %02d secs" "$h" "$m" "$s"
		elif [ "$m" -gt 0 ]; then
			printf "%02d mins, %02d secs" "$m" "$s"
		else
			printf "%02d secs" "$s"
		fi
	}

	if [ "$LOGPLAYTIME" -eq 1 ] && [ -n "$GN" ] ; then
		mkProjDir "$PLAYTIMELOGDIR"
		TIMEPLAYEDSTR="$(date +%d.%m.%Y) for $(getLengthPlayed "$1")" # different date formats?
		echo "$TIMEPLAYEDSTR" >> "$PLAYTIMELOGDIR/${GN}.log"
	fi
}

function setHuyList {
	HUTXT="helpurls.txt"

	HELPURLLIST="$GLOBALMISCDIR/$HUTXT"

	if [ -f "$STLCFGDIR/$HUTXT" ]; then
		HELPURLLIST="$STLCFGDIR/$HUTXT"
	fi

	DHU="defhelpurl"

	if [ -z "$HELPURL" ] || [ "$HELPURL" == "$NON" ]; then
		HELPURL="$(grep "$DHU" "$HELPURLLIST" | cut -d ';' -f2)"
	fi
	HUYLIST="$(while read -r line; do cut -d ';' -f1 <<<"$line"; done <<< "$(grep -v "$DHU" "$HELPURLLIST")" | tr '\n' '!')"

	loadCfg "$GEMETA/$AID.conf" X

	if [ -n "$METACRITIC_FULLURL" ]; then
		HUYLIST="${HUYLIST}metacritic!"
	fi

	if [ -n "$GAMEMANUALURL" ]; then
		HUYLIST="${HUYLIST}gamemanual!"
	fi

	export HUYLIST
}

function checkHelpUrl {
	WANTHELPURL="$1"
	if [ -n "$WANTHELPURL" ] && [ "$WANTHELPURL" != "$NON" ] && [ ! -f "$STLSHM/KillBrowser-$AID.txt" ]; then

		if [ "$WANTHELPURL" == "metacritic" ]; then
			HELPURL="$METACRITIC_FULLURL"
		elif [ "$WANTHELPURL" == "gamemanual" ]; then
			HELPURL="$GAMEMANUALURL"
		else
			HELPURL="$(grep "$WANTHELPURL" "$HELPURLLIST" | grep -v "$DHU" | cut -d ';' -f2)"
			writelog "INFO" "${FUNCNAME[0]} - Searching Help Url for '$WANTHELPURL' with command: 'grep \"$WANTHELPURL\" \"$HELPURLLIST\" | grep -v \"$DHU\" | cut -d ';' -f2'"
		fi

		if [ -n "$HELPURL" ]; then
			HELPURLNQ="${HELPURL//\"}"

			if [ -z "$BROWSER" ] || [ "$BROWSER" -eq "$NON" ]; then
				writelog "INFO" "${FUNCNAME[0]} - No web browser selected falling back to '$XDGO'"
				writelog "WARN" "${FUNCNAME[0]} - If issues occur opening web pages try setting a browser manually"
				BROWSER="$XDGO"
			fi

			writelog "INFO" "${FUNCNAME[0]} - Selected to open '${HELPURLNQ//AID/$AID}' with '$BROWSER'"
			# If $BROWSER is $XDGO we don't want to kill it as it will likely kill something like Steam instead
			if [ "$BROWSER" != "$XDGO" ]; then
				if ! "$PGREP" -f "$BROWSER" >/dev/null; then
					touch "$STLSHM/KillBrowser-$AID.txt"
				fi
			fi

			"$BROWSER" "${HELPURLNQ//AID/$AID}" 2>/dev/null &
		fi
	fi
}

function HelpUrlMenu {
	if [ -n "$1" ]; then
		AID="$1"
		setAIDCfgs
	fi
	fixShowGnAid
	export CURWIKI="$PPW/Help-Url"
	TITLE="${PROGNAME}-${FUNCNAME[0]}"
	pollWinRes "$TITLE"
	setShowPic
	setHuyList

	WANTHELPURL="$("$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
	--title="$TITLE" --separator="\n" \
	--text="$(spanFont "$SGNAID $(strFix "$GUI_ASKOPURL" "$1")" "H")" \
	--field="$GUI_HELPURL!$DESC_HELPURL ":CB "$(cleanDropDown "${HELPURL}" "${HUYLIST}${NON}")" \
	--button="$BUT_CAN":0 \
	--button="$BUT_OPURL":2 \
	"$GEOM"	)"

	case $? in
		0)  {
				checkHelpUrl "$WANTHELPURL"
				writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_CAN' - Leaving"
			}
		;;
		2)  {
				writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_OPURL' - Opening '$WANTHELPURL' in browser"
				checkHelpUrl "$WANTHELPURL"
			}
		;;
	esac
}

function checkPlayTime {
	WASSTESTA="$STLSHM/SteamStart_${AID}.txt"
	if [ ! -f "$WASSTESTA" ]; then
		writelog "INFO" "${FUNCNAME[0]} - The game was not started via '$PROGCMD' but directly from Steam - skipping Crash Requester"
	elif [ -z "$1" ]; then
		writelog "INFO" "${FUNCNAME[0]} - The game was not even started"
	else
		if [  -n "$CRASHGUESS" ] && [ "$1" -le "$CRASHGUESS" ]; then
			writelog "INFO" "${FUNCNAME[0]} - The game stopped after '$1' seconds - opening CrashGuess Requester"
			steamdeckControl
			fixShowGnAid
			export CURWIKI="$PPW/Crash-Guess"
			TITLE="${PROGNAME}-CrashGuess"
			pollWinRes "$TITLE"

			setShowPic

			setHuyList

			WANTHELPURL="$("$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
			--title="$TITLE" --separator="\n" \
			--text="$(spanFont "$SGNAID $(strFix "$GUI_ASKCRASHGUESS" "$1")" "H")" \
			--field="$GUI_HELPURL!$DESC_HELPURL ":CB "$(cleanDropDown "${HELPURL}" "${HUYLIST}${NON}")" \
			--button="$BUT_RETRYCG":0 \
			--button="$BUT_SKIPCG":2 \
			--button="$BUT_NO":4 \
			"$GEOM"	)"

			case $? in
				0)  {
						checkHelpUrl "$WANTHELPURL"
						writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_RETRYCG' so opening settings"
						duration=0
						SECONDS=0
						prepareLaunch X
					}
				;;
				2)  {
						writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_SKIPCG' - Setting CRASHGUESS to 0 for $SGNAID"
						updateConfigEntry "CRASHGUESS" "0" "$STLGAMECFG"
					}
				;;
				4)  {
						writelog "SKIP" "${FUNCNAME[0]} - Selected '$BUT_NO' - Exiting regularly"
					}
				;;
			esac
		else
			writelog "INFO" "${FUNCNAME[0]} - Playtime '$1' was longer than CRASHGUESS '$CRASHGUESS', not opening the requester"
		fi
	fi
	rm "$WASSTESTA" 2>/dev/null
}

function fixSymlinks {
	if [ "$FIXSYMLINKS" -eq 1 ]; then
		if [ ! -f "$RUNPROTON" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Fixing Symlinks is enabled, but RUNPROTON '$RUNPROTON' does not exist"
		else
			LRUNPROTDIR="${RUNPROTON%/*}"
			ORUNPROTDIR="$(readlink -f "$LRUNPROTDIR")"
			writelog "INFO" "${FUNCNAME[0]} - Starting Symlink Fix as requested"

			# the loop needs >1 second even without updating any symlinks and might need much longer with many syminks to be recreated
			# maybe worth to create a parsable checkfile to skip it on the next run?
			while read -r fixme; do
				SYML="$(awk -F ';' '{print $1}' <<< "$fixme")"
				OFIL="$(awk -F ';' '{print $2}' <<< "$fixme")"

				# make sure the found result is correct
				if [ "$(readlink -f "${SYML//\"}")" == "$(readlink -f "${OFIL//\"}")" ]; then
					OFIA="${OFIL//\"}"
					OFIB="${OFIA##*/files/}"
					OFIC="${OFIB//\/x86_64-windows}"
					OFID="${OFIC//\/i386-windows}"
					OOUT="${OFID##*/dist/}"

					if [ -n "$OOUT" ] && [ "$OOUT" != "" ]; then
						if [ -d "$LRUNPROTDIR/files" ]; then
							NFIL="$LRUNPROTDIR/files/$OOUT"
						elif [ -d "$LRUNPROTDIR/dist" ]; then
							NFIL="$LRUNPROTDIR/dist/$OOUT"
						else
							writelog "SKIP" "${FUNCNAME[0]} - No 'files' or 'dist' dir found in '$LRUNPROTDIR'"
						fi

						unset NEWFILE
						if [ -f "$NFIL" ]; then
							NEWFILE="$NFIL"
						elif [ -f "${NFIL//lib64\/wine/lib64\/wine\/x86_64-windows}" ]; then
							NEWFILE="${NFIL//lib64\/wine/lib64\/wine\/x86_64-windows}"
						elif [ -f "${NFIL//lib\/wine/lib\/wine\/i386-windows}" ]; then
							NEWFILE="${NFIL//lib\/wine/lib\/wine\/i386-windows}"
						else
							writelog "SKIP" "${FUNCNAME[0]} - No equivalent file found for '$OOUT' in '$LRUNPROTDIR'" # collect in generic nofile/${PROTONVERSION}.txt file to skip early?
						fi

						if [ -f "$NEWFILE" ]; then
							writelog "INFO" "${FUNCNAME[0]} - Symlink '${SYML//\"}' points to '${OFIL//\"}', but '$NEWFILE' would be correct - fixing:"
							rm "${SYML//\"}"
							writelog "INFO" "${FUNCNAME[0]} - 'ln -s \"$NEWFILE\" \"${SYML//\"}\"'"
							ln -s "$NEWFILE" "${SYML//\"}"
						fi
					fi
				else
					writelog "SKIP" "${FUNCNAME[0]} - '${SYML//\"}' does not point to '${OFIL//\"}' but to '$(readlink -f "${SYML//\"}")' - Skipping"
				fi
			done <<< "$(find "${GPFX}/${DRC}" -type l -exec echo \"{}\" \; -exec readlink -v {} \; | grep -v "$LRUNPROTDIR\|$ORUNPROTDIR\|$STUS" | grep -iB1 "Proton" | grep -v "^\-\-" | sed 'N;s/\n/;\"/' | sed 's/$/\"/')"
			writelog "INFO" "${FUNCNAME[0]} - Stop Symlink Fix"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - Fixing Symlinks is not enabled"
	fi
}

function unSymlink {
	if [ "$UNSYMLINK" -eq 1 ]; then
			LRUNPROTDIR="${RUNPROTON%/*}"
			ORUNPROTDIR="$(readlink -f "$LRUNPROTDIR")"
			writelog "INFO" "${FUNCNAME[0]} - Starting unsymlinking as requested - might needs some time, depending on the symlink count"

			while read -r unsym; do
				SYML="$(awk -F ';' '{print $1}' <<< "$unsym")"
				OFIL="$(awk -F ';' '{print $2}' <<< "$unsym")"
				mv "${SYML//\"}" "${SYML//\"}_OLD"
				cp "${OFIL//\"}" "${SYML//\"}"
				if [ -f "${SYML//\"}" ]; then
					rm "${SYML//\"}_OLD"
				else
					writelog "SKIP" "${FUNCNAME[0]} - Copying to '${SYML//\"}' failed - reverting symlink"
					mv "${SYML//\"}_OLD" "${SYML//\"}"
				fi
			done <<< "$(find "${GPFX}/${DRC}" -type l -exec echo \"{}\" \; -exec readlink -v {} \; | grep -iB1 "Proton" | grep -v "^\-\-" | sed 'N;s/\n/;\"/' | sed 's/$/\"/')"
			writelog "INFO" "${FUNCNAME[0]} - Stop Unsymlinking"
	else
		writelog "SKIP" "${FUNCNAME[0]} - Unsymlinking is not enabled"
	fi
}

function delPrefix {
	if [ "$DELPFX" -eq 1 ]; then
		if [ -d "$STEAM_COMPAT_DATA_PATH" ] ; then
			writelog "INFO" "${FUNCNAME[0]} - Removing the prefix was enabled - Creating a backup of steamuser before"
			BACKUPSTEAMUSER=1
			backupSteamUser "$AID"

			writelog "INFO" "${FUNCNAME[0]} - Removing the prefix '$GPFX' as requested"
			notiShow "$(strFix "$NOTY_DELPFX" "$GPFX")"
			rm -rf "$STEAM_COMPAT_DATA_PATH" 2>/dev/null
			mkProjDir "$STEAM_COMPAT_DATA_PATH"
		fi
	fi
}

function createOrUpdateSymDir {
	SYML="$1"
	ODIR="$2"
	if [ -d "$SYML" ] && [ ! -L "$SYML" ] ; then
		writelog "WARN" "${FUNCNAME[0]} - Renaming existing '$SYML' to '${SYML}_old'"
		mv "$SYML" "${SYML}_old"
	fi

	if [ -L "$SYML" ] && [ "$(readlink "$SYML")" != "$ODIR" ]; then
		writelog "WARN" "${FUNCNAME[0]} - Renaming existing symlink '$SYML' to '${SYML}_old'"
		writelog "WARN" "${FUNCNAME[0]} - because it points to '$(readlink "$SYML")'"
		mv "$SYML" "${SYML}_old"
	fi

	if [ ! -L "$SYML" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Created symlink from '$ODIR' to '$SYML'"
		ln -s "$ODIR" "$SYML"
	fi
}

function gameFix {
	if [ "$AID" -eq 223220 ] || [ "$AID" -eq 246960 ]; then
		writelog "INFO" "${FUNCNAME[0]} - got '$AID' as STEAM_COMPAT_APP_ID - need to change pwd from '$PWD' to '${STEAM_COMPAT_INSTALL_PATH}/launcher'"
		cd "${STEAM_COMPAT_INSTALL_PATH}/launcher" || return
	fi
}

function updateMO2GlobConf {
	mkProjDir "$STLMO2DLDATDIR"
	MYGLOBDLDAT="${STLMO2DLDATDIR}/global.conf"
	writelog "INFO" "${FUNCNAME[0]} - Updating '$MYGLOBDLDAT' with up to date data"
	{
		echo "GMO2EXE=\"$MO2EXE\""
		echo "RUNPROTON=\"$MO2RUNPROT\""
		echo "MO2PFX=\"$MO2PFX\""
		echo "MO2WINE=\"$MO2WINE\""
		echo "MO2INST=\"$MO2INST\""
		echo "STEAM_COMPAT_CLIENT_INSTALL_PATH=\"$STEAM_COMPAT_CLIENT_INSTALL_PATH\""
		echo "STEAM_COMPAT_DATA_PATH=\"$MO2CODA\""
	} > "$MYGLOBDLDAT"
	echo "global" > "$LAMOINST"
}

function updateMO2PortConf {
	MONGURL="$(grep -i "\"$MO2GAM\"" "$MO2GAMES" | head -n1 | cut -d ';' -f4)"
	MONGURL="${MONGURL//\"}"

	if [ -z "${MONGURL}" ]; then
		# Fall back to AID if MO2GAM couldn't be found
		if [ -n "$AID" ]; then
			MONGURL="$(grep -i "\"$AID\"" "$MO2GAMES" | head -n1 | cut -d ';' -f4)"
			MONGURL="${MONGURL//\"}"
			if [ -z "${MONGURL}" ]; then
				writelog "WARN" "${FUNCNAME[0]} - extracting the gamename MONGURL from '$MO2GAMES' by looking for '$MO2GAM' or '$AID' failed - the file '$MYPORTDLDAT' won't be found"
				return
			fi
		else
			writelog "WARN" "${FUNCNAME[0]} - extracting the gamename MONGURL from '$MO2GAMES' by looking for '$MO2GAM' failed - the file '$MYPORTDLDAT' won't be found (and no AID to fall back to)"
			return
		fi
	fi

	mkProjDir "$STLMO2DLDATDIR"
	MYPORTDLDAT="${STLMO2DLDATDIR}/${MONGURL}.conf"

	writelog "INFO" "${FUNCNAME[0]} - Updating '$MYPORTDLDAT' with up to date data"

	{
		echo "GMO2EXE=\"$MO2EXE\""
		echo "RUNPROTON=\"$RUNPROTON\""
		echo "MO2PFX=\"$MO2PFX\""
		echo "MO2WINE=\"$MO2WINE\""
		echo "MO2INST=\"$MO2INST\""
		echo "STEAM_COMPAT_CLIENT_INSTALL_PATH=\"$STEAM_COMPAT_CLIENT_INSTALL_PATH\""
		echo "STEAM_COMPAT_DATA_PATH=\"$STEAM_COMPAT_DATA_PATH\""
	} > "$MYPORTDLDAT"
	echo "portable" > "$LAMOINST"
}

function prepMO2 {
	function setBaseMO2RunCmd {
		unset RUNCMD
		while read -r arg; do
			mapfile -t -O "${#RUNCMD[@]}" RUNCMD <<< "$arg"
			if [ "$arg" == "$WFEAR" ]; then
				break
			fi
		done <<< "$(printf "%s\n" "$@")"
	}

	if [ "$ISGAME" -eq 2 ] && [ "$USEWINE" -ne 1 ]; then
		if [ "$MO2MODE" != "disabled" ]; then
			if [ "$MOINST" == "global" ]; then
				writelog "INFO" "${FUNCNAME[0]} - MO2MODE is '$MO2MODE' - preparing MO2 symlinks in '$GPFX' for '$MOINST' instance"

				GAMMO2DAT="$GPFX/$DRCU/$STUS/$ADLO/$MO"
				ORGMO2DAT="$MO2PFX/$DRCU/$STUS/$ADLO/$MO"
				createOrUpdateSymDir "$GAMMO2DAT" "$ORGMO2DAT"

				OMODDIR="$MO2PFX/$MORDIR"
				GMODDIR="$GPFX/$MORDIR"
				createOrUpdateSymDir "$GMODDIR" "$OMODDIR"
			else
				writelog "INFO" "${FUNCNAME[0]} - MO2MODE is '$MO2MODE' - preparing MO2 symlinks in '$GPFX' for '$MOINST' instance"
			fi

			setBaseMO2RunCmd "$@"
			GMO2EXE="$GPFX/$MOERPATH"
		fi

		if [ "$MO2MODE" == "silent" ]; then
			setMO2Vars
			MO2GAMIN1="$(grep -m1 "\"$AID\"" "$MO2GAMES" | cut -d ';' -f3)"
			MO2GAMINI="${MO2GAMIN1//\"}"

			if [ -n "$MO2GAMINI" ] && [ -f "$MO2EXE" ]; then
				if [ "$(grep -c "^+" "$MODLIST")" -eq 0 ]; then
					writelog "SKIP" "${FUNCNAME[0]} - Not starting $MO silent as requested, because no mod is enabled in '$MODLIST'"
					notiShow "$(strFix "$NOTY_NOMO" "$GN" "$AID")"
					MO2MODE="gui"
				else
					MO2SILENTMODERUN="$MO2GAMINI"  # Executable profile to use in silent mode, defaults to INI but can be overridden

					# Check if MO2 Silent Mode executable has been overwritten
					if [ -n "$MO2SILENTMODEEXEOVERRIDE" ] && [ "$MO2SILENTMODEEXEOVERRIDE" != "$NON" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Overridding ModOrganizer 2 Silent Mode shortcut with '$MO2SILENTMODEEXEOVERRIDE' because it is defined and is not '$NON'"
						MO2SILENTMODERUN="$MO2SILENTMODEEXEOVERRIDE"
					fi

					# Run MO2 EXE with 'moshortcut://:' in for Silent Mode, as this will run a given Executable Profile
					# This defaults to the Executable profile that matches the INI filename. We infer this from mo2games.txt
					#
					# For example, 'Oblivion' will always have 'Oblivion.ini' because we make sure we name the entries in mo2games.txt correctly,
					# and MO2 will always create an Executable profile for *just* the game without its launcher (in this case, 'Oblivion'), so we can assume
					# the INI name will always have a corresponding Executable profile (unless manually removed by the user, and in such a case, the override option allows them to fix that)
					#
					# Some games (like Skyrim Special Edition) may need to use another executable profile, like SKSE64, so a user can override this value and the command
					# would become 'moshortcut://:SKSE64'
					#
					# If a given executable name is invalid, ModOrganizer 2 will show an error dialog stating so and will exit
					RUNCMD=("${RUNCMD[@]}" "$MO2EXE" "moshortcut://:$MO2SILENTMODERUN")

					writelog "INFO" "${FUNCNAME[0]} - Starting '$SGNAID' with $MO enabled mods with command '${RUNCMD[*]}'"
					notiShow "$(strFix "$NOTY_STARTSIMO" "$GN" "$AID")"
				fi
			else
				writelog "SKIP" "${FUNCNAME[0]} - Not starting $MO silent as requested, because MO2GAMINI ('$MO2GAMINI') or MO2EXE ('$MO2EXE') is empty"
				MO2MODE="gui"
			fi
		fi

		if [ "$MO2MODE" == "gui" ]; then
			if [ -n "$MO2GAM" ]; then
				if [ -n "$MOINST" ] && [ "$MOINST" == "portable" ]; then
				#XXXXXXXXXXXXXXXX
				# testing and not working because wabbajack wants to open a nexus url for authentication, which doesn't seem to work
					USEWABBAJACK=0

					WAB="wabbajack"
					if [ "$USEWABBAJACK" -eq 1 ]; then
						writelog "INFO" "${FUNCNAME[0]} - ${WAB^} is enabled - starting"
						WABINST="$MO2DLDIR/${WAB^}.exe"
						export DOTNET_ROOT="$VTX_DOTNET_ROOT"
						export DOTNET_BUNDLE_EXTRACT_BASE_DIR="$VTX_DOTNET_ROOT"
						installDotNet "$MO2PFX" "$MO2WINE" "48"
						RUNCMD=("$MO2WINE" "$WABINST")
					else
						writelog "INFO" "${FUNCNAME[0]} - ${WAB^} MOINST $MOINST"

						writelog "INFO" "${FUNCNAME[0]} - Running $MOINST instance"
						RUNCMD=("${RUNCMD[@]}" "$MO2EXE")
					fi
				else
					RUNCMD=("${RUNCMD[@]}" "$MO2EXE" "-i" "${MO2GAM//\"}")
				fi

				writelog "INFO" "${FUNCNAME[0]} - Starting '$SGNAID' with $MO gui via '${RUNCMD[*]}'"
				notiShow "$(strFix "$NOTY_STARTVEMO" "$GN" "$AID")"
			else
				RUNCMD=("${RUNCMD[@]}" "$MO2EXE")
				writelog "INFO" "${FUNCNAME[0]} - Starting '$SGNAID' with $MO gui via '${RUNCMD[*]}'"
				notiShow "$(strFix "$NOTY_STARTVEMO" "$GN" "$AID")"
			fi

			if [ -n "$MOINST" ] && [ "$MOINST" == "portable" ]; then
				updateMO2PortConf
			else
				updateMO2GlobConf
			fi
		fi
	fi
}

function startGame {
	loadCfg "$GEMETA/$AID.conf"

	if [ "$STLPLAY" -eq 0 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Getting the Game Window name:"
		getGameWindowName &
	fi

	touch "$PIDLOCK"
	startSBSVR &
	gameFix
	checkConty

	if [ "$BLOCKINTERNET" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Starting game with blocked internet access"
		BLOCKCMD="unshare"
		BLOCKARGS="-n -r"
		unset BLOCKARGSARR
		mapfile -d " " -t -O "${#BLOCKARGSARR[@]}" BLOCKARGSARR < <(printf '%s' "$BLOCKARGS")
		RUNCMD=("$BLOCKCMD" "${BLOCKARGSARR[@]}" "${@}")
	else
		RUNCMD=("${@}")
	fi

	writelog "INFO" "${FUNCNAME[0]} - Full start command is '${*}'"

	prepMO2 "$@"

	if [ "$HAVESCTP" -eq 1 ] && [ "$ISGAME" -eq 2 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Rebuilding STEAM_COMPAT_TOOL_PATHS variable:"
		writelog "INFO" "${FUNCNAME[0]} - Adding RUNPROTON '${RUNPROTON%/*}'"
		STEAM_COMPAT_TOOL_PATHS="${RUNPROTON%/*}"
		if [ "$HAVESCTP" -eq 1 ] && [ "$USESLR" -eq 1 ]; then
			RUNSLA="${RUNSLR[0]}"
			writelog "INFO" "${FUNCNAME[0]} - Adding '${RUNSLA%/*}' because USESLR is enabled"
			STEAM_COMPAT_TOOL_PATHS="$STEAM_COMPAT_TOOL_PATHS:${RUNSLA%/*}"
		fi
		writelog "INFO" "${FUNCNAME[0]} - Result: Set STEAM_COMPAT_TOOL_PATHS from '$ORG_STEAM_COMPAT_TOOL_PATHS' to '$STEAM_COMPAT_TOOL_PATHS'"
	elif [ "$RUNFORCESLR" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Rebuilding STEAM_COMPAT_TOOL_PATHS variable, because SLR was forced"
		writelog "INFO" "${FUNCNAME[0]} - Adding RUNPROTON '${RUNPROTON%/*}'"
		STEAM_COMPAT_TOOL_PATHS="${RUNPROTON%/*}"
		LASTSLRPATH="${LASTSLR% --verb*}"
		STEAM_COMPAT_TOOL_PATHS="$STEAM_COMPAT_TOOL_PATHS:${LASTSLRPATH%/*}"
		writelog "INFO" "${FUNCNAME[0]} - Result: Updated STEAM_COMPAT_TOOL_PATHS to '$STEAM_COMPAT_TOOL_PATHS'"
	fi

	SECONDS=0

	if [ "$MO2MODE" != "disabled" ] && [ -n "$GMO2EXE" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Changing pwd to '${GMO2EXE%/*}' for $MO launch"
		cd "${GMO2EXE%/*}" >/dev/null || return
	fi

	if ! command -v "$GAMESCOPE" >/dev/null && [ "$USEMANGOAPP" -eq 1 ]; then
		writelog "WARN" "${FUNCNAME[0]} - Disabling USEMANGOAPP because '$GAMESCOPE' wasn't found"
		USEMANGOAPP=0
	fi

	if [ -x "$RUNCONTY" ] && [ "$RUNCONTY" != "$NON" ]; then
		writelog "INFO" "${FUNCNAME[0]} - ## Starting game using Conty executable '$RUNCONTY'"
		"$RUNCONTY" "${RUNCMD[@]}"
	else
		writelog "INFO" "${FUNCNAME[0]} - ## ORIGINAL INCOMING LAUNCH COMMAND: '${INGCMD[*]}'"
		writelog "INFO" "${FUNCNAME[0]} - ## STL LAUNCH COMMAND: '${RUNCMD[*]}'"
		writelog "INFO" "${FUNCNAME[0]} - ## GAMESTART HERE ###"

		restoreOrgVars

		GRUNLOG="$STLGLLOGDIRID/${AID}.log"
		if [ "$ISORIGIN" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - ## ${L2EA}  LAUNCH COMMAND: '${RUNCMD[*]}'"

			function runEA {
				"${RUNCMD[@]}" 2>&1 | tee "$GRUNLOG"
			}

			unsetSTLvars
			runEA &
		elif [ "$USEMANGOAPP" -eq 1 ]; then
			# mangoapp gamestart - COMMENTDEBUG:
			writelog "INFO" "${FUNCNAME[0]} - Using $MANGOAPP"
			MRSH="$STLSHM/maprun.sh"
			printf "\"%s\" " "${RUNCMD[@]}" > "$MRSH"
			sed -i "s/\\\/\\\\\\\/" "$STLSHM/maprun.sh"
			chmod +x "$MRSH"
			gameScopeArgs "$GAMESCOPE_ARGS"

			function runMA {
				function mappRun {
					"$MRSH" & "$MANGOAPP"
				}
				export MANGOAPP
				export MRSH
				export -f mappRun
				if [ -n "${GAMESCOPEARGSARR[0]}" ]; then
					writelog "INFO" "${FUNCNAME[0]} - ## ${MANGOAPP^^} GAMESCOPE LAUNCH COMMAND: '$(command -v "$GAMESCOPE") ${GAMESCOPEARGSARR[*]}'"
					"$(command -v "$GAMESCOPE")" "${GAMESCOPEARGSARR[@]}" bash -c mappRun 2>&1 | tee "$GRUNLOG"
				else
					"$(command -v "$GAMESCOPE")" -- bash -c mappRun 2>&1 | tee "$GRUNLOG"
				fi
			}
			runMA &
		else
			# regular gamestart - COMMENTDEBUG:
			"${RUNCMD[@]}" 2>&1 | tee "$GRUNLOG"
		fi
	fi

	if [ "$MO2MODE" != "disabled" ]; then
		cd - >/dev/null || return
	fi

	emptyVars "O" "X" # clear original variables again (mostly for a continous log (date))
	createSymLink "${FUNCNAME[0]}" "$GRUNLOG" "${STLGLLOGDIRTI}/${GN}.log"

# this is broken - maybe later:
#	if [ "$ISGAME" -eq 2 ] && [ "$USEWINE" -eq 0 ] && { grep -qi "GE-" <<< "$USEPROTON" || grep -qi "\-TKG" <<< "$USEPROTON" || [ "$ISORIGIN" -eq 1 ];}; then
	if [ "$ISGAME" -eq 2 ] && [ "$USEWINE" -eq 0 ]; then
		if [ "$ISORIGIN" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Waiting for $L2EA process to finish"
			# TODO maybe add waitForOriginPid - the sleep is not too nice (but at least non blocking)
			sleep 20
			OE="EADesktop.exe"
			WSPID="$("$PGREP" -a "" | grep "$OE" | grep -v grep | cut -d ' ' -f1 | tail -n1)"
			writelog "INFO" "${FUNCNAME[0]} - $L2EA game launch pid is $WSPID"
		elif [ "$USEMANGOAPP" -eq 1 ]; then
			# could be used generally, but undecided yet if waitForGamePid should be used always by default
			waitForGamePid
			RLRUNWINESERVER="$(readlink -f "$RUNWINESERVER")"
			WSPID="$("$PGREP" -a "" | grep "$RLRUNWINESERVER" | grep -v grep | cut -d ' ' -f1 | tail -n1)"
			writelog "INFO" "${FUNCNAME[0]} - $MANGOAPP game launch pid is $WSPID"
		elif [ "$USECUSTOMCMD" -eq 1 ] && [ "$FORK_CUSTOMCMD" -eq 1 ] && [ "$ONLY_CUSTOMCMD" -eq 0 ] && [ "$WAITFORCUSTOMCMD" -ge 1 ] && [ -n "$(CUCOPID)" ];then
			WSPID="$(CUCOPID)"
			writelog "INFO" "${FUNCNAME[0]} - Custom program pid is $WSPID"
		else
			RLRUNWINESERVER="$(readlink -f "$RUNWINESERVER")"
			WSPID="$("$PGREP" -a "" | grep "$RLRUNWINESERVER" | grep -v grep | cut -d ' ' -f1 | tail -n1)"
		fi
		if [ -n "$WSPID" ] && [ "$WSPID" -eq "$WSPID" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Waiting for the process '$WSPID' to finish"
			tail --pid="$WSPID" -f /dev/null

			if [ "$USEMANGOAPP" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - $MANGOAPP game launch finished - closing"
				"$PKILL" -f "$MANGOAPP"
			else
				writelog "INFO" "${FUNCNAME[0]} - Process '$WSPID' finished - closing"
			fi
		else
			writelog "ERROR" "${FUNCNAME[0]} - Could not determine pid of '$RLRUNWINESERVER'"
		fi
	else
		writelog "WARN" "${FUNCNAME[0]} - Skipping Wait for any PID - ISGAME:'$ISGAME';USEWINE:'$USEWINE' - possible missing function!"
	fi

	duration=$SECONDS
	logPlayTime "$duration"
	writelog "INFO" "${FUNCNAME[0]} - ## GAMESTOP after '$duration' seconds playtime"

	# Continue steamwebhelper if requested
	StateSteamWebHelper cont
}

# Shellcheck doesn't like the sed commands here and wants parameter expansion, but I'm not sure that these sed actions are possible with it.
# The Shellcheck wiki says more complex sed examples can be ignored, so we ignore it
function gameScopeArgs {  # This implementation could be VASTLY improved!
	ARGSTRING="$1"
	unset GAMESCOPEARGSARR

	# Even if no args are given we always have to end GameScope commands with '--'
	# $1 == NON when we save with no arguments, doing this visually preserves the GameScope "none" text while still gracefully handling blank args by forcing '--'
	if [ "$1" == "$NON" ] || [ -z "$( trimWhitespaces "$1" )" ]; then  # trimWhitespaces accounts for strings that are just whitespaces, i.e. '     '
		writelog "INFO" "${FUNCNAME[0]} - No gamescope arguments given, but we need to end with '--', so forcing GAMESCOPEARGSARR to '--' and returning"
		GAMESCOPEARGSARR=("--")
		return
	fi

	# This removes paths from the GameScope args array as spaces in paths can cause issues, then builds the array, and then re-inserts the paths where it finds empty single-quotes which we wrap paths with in GameScopeGui.
	# When saving from the main menu, single quotes seem to get cleared, so we need to ensure paths are wrapped with them

	# Store paths from GameScope array string
	IFS_backup=$IFS
	IFS=$'\n'
	mapfile -t GAMESCOPE_ARGPATHS < <( echo "$ARGSTRING" | grep -oP "'/(.+?)'" )
	writelog "INFO" "${FUNCNAME[0]} - GameScope incoming args are '${ARGSTRING[*]}'"
	# If the above is empty, try and surround any existing paths with quotes and then grep for the file paths from the quotes - This means paths cannot contain quotes, but oh well. Compromise!
	if [ -z "${GAMESCOPE_ARGPATHS[*]}" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Could not find any paths from incoming GameScope arguments, checking if we need to surround any paths in quotes..."
		unset GAMESCOPE_ARGPATHS

		# shellcheck disable=SC2001
		ARGSTRING="$(echo "${ARGSTRING}" | sed "s:\(/\S* \S*\):'\1':g" )"  # Finds paths and surrounds them in single quotes so the below grep works!
		mapfile -t GAMESCOPE_ARGPATHS < <( echo "$ARGSTRING" | grep -oP "'/(.+?)'" )
		if [ -n "${GAMESCOPE_ARGPATHS[*]}" ]; then
			writelog "INFO" "${FUNCNAME[0]} - We found some paths we need to update - Updated GameScope args string is '$ARGSTRING'"
		else
			writelog "INFO" "${FUNCNAME[0]} - Still could not find any paths from incoming GameScope arguments, assuming we don't have any paths in our arguments"
		fi
	fi
	IFS=$IFS_backup

	# Remove all text between single quotes -- We assume all text between single quotes in this context will be a GameScope path arg
	writelog "INFO" "${FUNCNAME[0]} - GameScope arg paths are '${GAMESCOPE_ARGPATHS[*]}'"
	# shellcheck disable=SC2001
	ARGSTRING="$( echo "$ARGSTRING" | sed "s:'[^']*':'':g" )"
	mapfile -d " " -t -O "${#GAMESCOPEARGSARR[@]}" GAMESCOPEARGSARR < <(printf '%s' "$ARGSTRING")

	INSERTARG=$((0))  # Which path arg to insert from the `GAMESCOPE_ARGPATHS` array
	GAMESCOPEARGSARR_COPY=("${GAMESCOPEARGSARR[@]}")
	for i in "${!GAMESCOPEARGSARR_COPY[@]}"; do
		if [[ "${GAMESCOPEARGSARR_COPY[i]}" == *"'"* ]]; then
			GAMESCOPEARGSARR_COPY[i]="${GAMESCOPE_ARGPATHS[${INSERTARG}]}"
			INSERTARG=$((INSERTARG + 1))
		fi
	done

	unset GAMESCOPEARGSARR  # Reset array and re-assign it to copied array with updated argument values
	GAMESCOPEARGSARR=("${GAMESCOPEARGSARR_COPY[@]}")

	# Ensure GameScope args always end with "--", otherwise GameScope will try to use everyting following it as a GameScope command and fail
	# With the GameScope GUI we add "--" to the end, but if the user manually updates their launch options, this will help prevent them from getting into a failed state
	if [ "${GAMESCOPEARGSARR[-1]}" != "--" ]; then
		writelog "WARN" "${FUNCNAME[0]} - Last Gamescope argument is not '--' so manually appending this to the end of GAMESCOPESARGSARR"
		writelog "WARN" "${FUNCNAME[0]} - This is invalid syntax but manually fixing it"
		GAMESCOPEARGSARR+=("--")
	fi

	if [ "${#GAMESCOPEARGSARR[@]}" -ge 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using following gamescope arguments: '${GAMESCOPEARGSARR[*]}'"
	else
		writelog "INFO" "${FUNCNAME[0]} - gamescope doesn't have any command line arguments"
	fi
}

function gameArgs {
	ARGSTRING="$1"
	unset GAMEARGSARR

	if [ "$1" == "$GAMEARGS" ]; then
		if [ "$SORTGARGS" -eq 1 ]; then
			# add (originally "hardcoded", but now possibly modified) command line arguments coming directly from Steam/the game
			if [ "$HARDARGS" != "$NOPE" ] && [ "$HARDARGS" != "$NON" ]; then
				mapfile -d " " -t -O "${#GAMEARGSARR[@]}" GAMEARGSARR < <(printf '%s' "$HARDARGS")
			fi

			# now append those command line arguments coming from $SLO
			if [ "$SLOARGS" != "$NON" ]; then
				mapfile -d " " -t -O "${#GAMEARGSARR[@]}" GAMEARGSARR < <(printf '%s' "$SLOARGS")
			fi
		else
			if [ "${#ORGCMDARGS[@]}" -ge 1 ]; then
				GAMEARGSARR=("${ORGCMDARGS[@]}")
			fi
		fi
	fi

	# finally add the own custom command line arguments
	if [ "$1" != "$NON" ]; then
		mapfile -d " " -t -O "${#GAMEARGSARR[@]}" GAMEARGSARR < <(printf '%s' "$ARGSTRING")
	fi

	if [ "${#GAMEARGSARR[@]}" -ge 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Combined final game command line arguments: '${GAMEARGSARR[*]}'"
	else
		writelog "INFO" "${FUNCNAME[0]} - Game doesn't use any command line arguments"
	fi
}

function fixCustomMeta {
	if [ -f "$1" ] && grep -q "^GAMEDIR=" "$1"; then
		writelog "INFO" "${FUNCNAME[0]} - Renaming variable 'GAMEDIR' to 'MEGAMEDIR' in '$1', because 'GAMEDIR' also exists in the generic metadata, coming from steam" "E"
		sed "s:^GAMEDIR=:MEGAMEDIR=:" -i "$1"
	fi
}

function initPlay {
	# HAVESLR=0  # Previously enabled to force disable SLR from launch command, but now we can handle SLR from launch command and safely ignore it if not present
	## TODO: Are any of these other values still needed?
	HAVESLRCT=0
	HAVEREAP=0
	HAVESCTP=0
	INCOPATH=0
	STLPLAY=1

	function setHaveConfs {
		if [ -n "$HAVID" ]; then
			if [ -z "$HAVCUME" ]; then
				HAVCUME="$CUMETA/$HAVID.conf"
			fi

			if [ -z "$HAVGEME" ]; then
				HAVGEME="$GEMETA/$HAVID.conf"
			fi

			if [ -z "$HAVGACO" ]; then
				HAVGACO="$STLGAMEDIRID/$HAVID.conf"
			fi
		fi
	}

	function loadHaveConfs {
		if [ -f "$HAVGEME" ] && [ -z "$LOADEDHAVGEME" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Loading generic metadata found under '$HAVGEME'" "E"
			loadCfg "$HAVGEME"
			LOADEDHAVGEME=1
		fi

		if [ -f "$HAVCUME" ] && [ -z "$LOADEDHAVCUME" ]; then
			fixCustomMeta "$HAVCUME"
			writelog "INFO" "${FUNCNAME[0]} - Loading custom metadata found under '$HAVCUME'" "E"
			loadCfg "$HAVCUME"
			LOADEDHAVCUME=1
		fi

		if [ -f "$HAVGACO" ] && [ -z "$LOADEDHAVGACO" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Loading game config '$HAVGACO'" "E"
			loadCfg "$HAVGACO"
			LOADEDHAVGACO=1
		fi
	}

	function saveHaveCuMe {
		if [ -n "$HAVCUME" ] && [ ! -f "$HAVCUME" ]; then
			touch "$HAVCUME"
		fi

		if [ -n "$MEGAMEDIR" ]; then
			touch "$FUPDATE"
			updateConfigEntry "MEGAMEDIR" "$MEGAMEDIR" "$HAVCUME"
		fi
	}

	function saveHaveGeMe {
		if [ -n "$HAVGEME" ] && [ ! -f "$HAVGEME" ]; then
			touch "$HAVGEME"
		fi

		if [ -n "$HAVID" ]; then
			touch "$FUPDATE"
			updateConfigEntry "GAMEID" "$HAVID" "$HAVGEME"
		elif [ -n "$AID" ] && [ "$AID" != "$PLACEHOLDERAID" ]; then
			touch "$FUPDATE"
			updateConfigEntry "GAMEID" "$AID" "$HAVGEME"
		fi

		if [ -n "$EXECUTABLE" ]; then
			touch "$FUPDATE"
			updateConfigEntry "EXECUTABLE" "$EXECUTABLE" "$HAVGEME"
			if [ -z "$GAMEEXE" ]; then
				GAMEEXE="${EXECUTABLE//.exe}"
			fi
		fi

		if [ -n "$GE" ] && [ -z "$GAMEEXE" ]; then
			GAMEEXE="$GE"
		fi

		if [ -n "$GAMEEXE" ]; then
			touch "$FUPDATE"
			updateConfigEntry "GAMEEXE" "$GAMEEXE" "$HAVGEME"
		fi

		if [ -z "$GAMENAME" ]; then
			GAMENAME="${EXECUTABLE//.exe}"
		fi

		if [ -n "$GAMENAME" ]; then
			touch "$FUPDATE"
			updateConfigEntry "GAMENAME" "$GAMENAME" "$HAVGEME"
		fi

		if [ -z "$KEEPGAMENAME" ]; then
			KEEPGAMENAME=1
			touch "$FUPDATE"
			updateConfigEntry "KEEPGAMENAME" "$KEEPGAMENAME" "$HAVGEME"
		fi
	}

	if [ "$1" -eq "$1" ] 2>/dev/null; then
		writelog "INFO" "${FUNCNAME[0]} - Assuming incoming argument '$1' is a SteamAppId" "E"
		HAVID="$1"
	elif [ -f "$1" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Assuming incoming argument '$1' is an absolute path to a game exe" "E"
		HAVPA="$1"
		INCOPATH=1
	else
		writelog "INFO" "${FUNCNAME[0]} - Assuming incoming argument '$1' is a game title" "E"
		HAVTI="$1"
	fi

	if [ -n "$HAVTI" ] || [ -n "$HAVID" ] && { [ -n "$2" ] && [ -f "$2" ];}; then
		HAVPA="$2"
	fi

	if [ -n "$HAVTI" ]; then
		CKHAVGEME="$(find "$TIGEMETA" -iname "$HAVTI.conf")"
		if [ -f "$CKHAVGEME" ]; then
			HAVGEME="$CKHAVGEME"
		fi

		CKHAVCUME="$(find "$TICUMETA" -iname "$HAVTI.conf")"
		if [ -f "$CKHAVCUME" ]; then
			HAVCUME="$CKHAVCUME"
		fi

		if [ -z "$HAVGEME" ]; then
			HAVGEMECFG="$(grep -Ri "NAME=\"$HAVTI" "$GEMETA/" | head -n1 | cut -d ':' -f1)"
			CKHAVGEME="$GEMETA/$HAVGEMECFG"
			if [ -f "$CKHAVGEME" ]; then
				HAVGEME="$CKHAVGEME"
			fi

			CKHAVCUME="$(find "$CUMETA" -iname "$HAVGEMECFG")"
			if [ -f "$CKHAVCUME" ]; then
				HAVCUME="$CKHAVCUME"
			fi
		fi
	fi

	setHaveConfs
	loadHaveConfs

	if [ -n "$GAMEID" ] && [ -z "$HAVID" ]; then
		HAVID="$GAMEID"
		writelog "INFO" "${FUNCNAME[0]} - Found SteamAppId '$HAVID'" "E"
	fi

	setHaveConfs
	loadHaveConfs

	if [ -z "$EXECUTABLE" ] && [ -f "$HAVPA" ]; then
		EXECUTABLE="${HAVPA##*/}"
	fi

	if [ -z "$EXECUTABLE" ] && [ -n "$GAMENAME" ]; then
		EXECUTABLE="$GAMENAME"
	fi

	if [ -z "$HAVPA" ] && [ -n "$MEGAMEDIR" ] && [ -n "$EXECUTABLE" ]; then
		CHKHAVPA="$MEGAMEDIR/$EXECUTABLE"
		if [ -f "$CHKHAVPA" ]; then
			HAVPA="$CHKHAVPA"
		fi
	fi

	if [ -n "$EXECUTABLE" ] && [ -n "$HAVPA" ] && [ "$EXECUTABLE" != "${HAVPA##*/}" ]; then
		EXECUTABLE="${HAVPA##*/}"
	fi

	if [ -n "$MEGAMEDIR" ]; then
		GFD="$MEGAMEDIR"
	fi

	if [ -z "$HAVID" ] &&  [ -z "$GAMEID" ] && [ -n "$HAVPA" ]; then
		HAVID="$(setGNID "${HAVPA##*/}")"
		if [ -z "$GFD" ]; then
			GFD="${HAVPA%/*}"
		fi
		if [ -z "$EFD" ]; then
			EFD="${HAVPA%/*}"
		fi
	fi

	if [ -n "$MEGAMEDIR" ] && [ -n "$HAVPA" ] && [ "$MEGAMEDIR" != "${HAVPA%/*}" ]; then
		MEGAMEDIR="${HAVPA%/*}"
	fi

	if [ -n "${HAVPA%/*}" ] && [ -z "$EFD" ]; then EFD="${HAVPA%/*}"; fi
	if [ -n "${HAVPA%/*}" ] && [ -z "$GFD" ]; then GFD="${HAVPA%/*}"; fi

	setHaveConfs
	loadHaveConfs

	if [ -n "$HAVID" ] &&  [ -z "$GAMEID" ] && { [ -z "$AID" ] || [ "$AID" == "$PLACEHOLDERAID" ];}; then
		writelog "INFO" "${FUNCNAME[0]} - Setting AID to '$HAVID'" "E"
		export AID="$HAVID"
	fi

	if [ -n "$GAMEID" ] && { [ -z "$AID" ] || [ "$AID" == "$PLACEHOLDERAID" ];}; then
		writelog "INFO" "${FUNCNAME[0]} - Setting AID to '$GAMEID'" "E"
		export AID="$GAMEID"
	fi
	saveHaveCuMe
	saveHaveGeMe
    # from here all required vars should be ready to launch
	if [ -n "$AID" ] && [ "$AID" != "$PLACEHOLDERAID" ]; then
		if [ -z "$HAVGACO" ]; then
			HAVGACO="$STLGAMEDIRID/$AID.conf"
		fi

		loadHaveConfs

		if [ -n "$GAMENAME" ] && [ "$GAMENAME" != "$NON" ]; then
			ICGN="$GAMENAME"
		elif [ -n "$GAMEEXE" ] && [ "$GAMEEXE" != "$NON" ]; then
			ICGN="$GAMEEXE"
		elif [ -n "$EXECUTABLE" ] && [ "$EXECUTABLE" != "$NON" ]; then
			ICGN="$GAMEEXE"
		else
			ICGN="$NON"
		fi

		createDesktopIconFile "$AID" "0" "$ICGN" "$HAVPA"

		if [ -f "$HAVPA" ]; then
			if [ -z "$GP" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Set 'GP' to '$HAVPA'" "E"
				GP="$HAVPA"
			else
				writelog "INFO" "${FUNCNAME[0]} - Already have 'GP' '$GP'" "E"
			fi

			if [ "$INCOPATH" -eq 1 ]; then
				while read -r INGARG; do
					mapfile -t -O "${#INGCMD[@]}" INGCMD <<< "$INGARG"
				done <<< "$(printf "%s\n" "$@")"

				FOUNDORGGCMD=0
				while read -r ORGARG; do
					if [ "$FOUNDORGGCMD" -eq 0 ]; then
						mapfile -t -O "${#ORGGCMD[@]}" ORGGCMD <<< "$ORGARG"
						if [[ "$ORGARG" =~ $GP ]]; then
							FOUNDORGGCMD=1
						fi
					else
						mapfile -t -O "${#ORGCMDARGS[@]}" ORGCMDARGS <<< "$ORGARG"
					fi
				done <<< "$(printf "%s\n" "${INGCMD[@]}")"
			else
				ORGGCMD=( "$HAVPA" )
			fi

			if [ -z "$GE" ] && [ -n "$HAVPA" ]; then
				GE="${HAVPA##*/}"
			fi

			if [ -z "$GP" ] && [ -n "$HAVPA" ]; then
				GP="$HAVPA"
			fi

			if [ -z "$GN" ] && [ -n "$HAVPA" ]; then
				GN="${HAVPA##*/}"
			fi

			writelog "INFO" "${FUNCNAME[0]} - Game executable used is '$HAVPA'" "E"

			if grep -q "shell script" <<< "$(file "$(realpath "$HAVPA")")" || grep -q "ELF.*.LSB" <<< "$(file "$(realpath "$HAVPA")")" ; then
				writelog "INFO" "${FUNCNAME[0]} - Assuming this is a linux binary" "E"
				ISGAME=3
				GPFX="$NON"
				cd "$MEGAMEDIR" || return
				prepareLaunch
				cd - || return
			else
				writelog "INFO" "${FUNCNAME[0]} - Assuming this is a windows binary" "E"

				if [ -z "$STEAM_COMPAT_INSTALL_PATH" ]; then
					if [ -n "$MEGAMEDIR" ]; then
						export STEAM_COMPAT_INSTALL_PATH="$MEGAMEDIR"
					elif [ -n "$EFD" ]; then
						export STEAM_COMPAT_INSTALL_PATH="$EFD"
					else
						writelog "INFO" "${FUNCNAME[0]} - STEAM_COMPAT_INSTALL_PATH (game dir) is unknown - setting it at least to '${STLDLDIR}'"
						export STEAM_COMPAT_INSTALL_PATH="$STLDLDIR"
					fi
				fi

				if [ -z "$STL_COMPAT_DATA_PATH" ]; then
					STL_COMPAT_DATA_PATH="$STLCOMPDAT/$AID"
				fi

				if [ -n "$STL_COMPAT_DATA_PATH" ]; then
					mkProjDir "$STL_COMPAT_DATA_PATH"
					writelog "INFO" "${FUNCNAME[0]} - Using '$STL_COMPAT_DATA_PATH' as STEAM_COMPAT_DATA_PATH" "E"
					export STEAM_COMPAT_DATA_PATH="$STL_COMPAT_DATA_PATH"
					export GPFX="${STL_COMPAT_DATA_PATH}/pfx"
					ISGAME=2
					cd "$STEAM_COMPAT_INSTALL_PATH" || return
					prepareLaunch
					cd - || return
				else
					writelog "ERROR" "${FUNCNAME[0]} - STEAM_COMPAT_DATA_PATH was not defined"
				fi
			fi
		fi
	else
		writelog "ERROR" "${FUNCNAME[0]} - Could not determine an AppID - can't continue" "E"
	fi
}

function standaloneGames {
	if [ -d "$GEMETA" ]; then
		while read -r line; do
			if [ -n "$1" ] && [ "$1" == "l" ]; then
				SAG="$(grep "^GAMENAME=" "$line" | cut -d '=' -f2)"
				if [ -n "$SAG" ]; then
					echo "${SAG//\"/}"
				fi
			elif [ -n "$1" ] && [ "$1" == "g" ]; then
				unset GAMENAME EXECUTABLE GAMEID
				loadCfg "$line"
				if [ -n "$GAMEID" ] && [ -n "$EXECUTABLE" ]; then
					echo "FALSE"

					if [ -n "$GAMENAME" ]; then
						echo "$GAMENAME"
					else
						echo "$EXECUTABLE"
					fi

					echo "$EXECUTABLE"
					echo "$GAMEID"
				else
					writelog "SKIP" "${FUNCNAME[0]} - Skipping config '$line', because at least one of GAMEID:'$GAMEID' EXECUTABLE:'$EXECUTABLE', GAMENAME:'$GAMENAME' is empty"
				fi
			else
				echo "$line"
			fi
		done <<< "$(find "$GEMETA" -name "${GETSTAID}.conf")"
	else
		writelog "ERROR" "${FUNCNAME[0]} - Directory GEMETA '$GEMETA' could not be found" "E"
 	fi
}

function standaloneDefGameIcon {
	if [ -x "$(command -v "$CONVERT" 2>/dev/null)" ]; then
		"$CONVERT" "$STLICON" -resize "128x128!" "$WANTGPNG"
	else
		cp "$STLICON" "$WANTGPNG"
	fi
}

function standaloneGameIcon {
	WANTGPNG="$1"
	AID="$2"
	GAMENAME="$3"
	HAVEPA="$4"
	if [ -f "$WANTGPNG" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Already have an icon: '$WANTGPNG'"
	else
		writelog "SKIP" "${FUNCNAME[0]} - Trying to find an icon for '$GAMENAME'"
		if grep -q "shell script" <<< "$(file "$(realpath "$HAVPA")")" || grep -q "ELF.*.LSB" <<< "$(file "$(realpath "$HAVPA")")" ; then
			# yes, quick&dirty!
			SYSICN="$(cut -d '=' -f2 <<< "$(grep "^Icon=" "$(find "/usr/share/applications/" -name "${HAVPA##*/}*.desktop" | head -n1)")")"
			if [ -n "$SYSICN" ]; then
				SYSICF="$(find "/usr/share/icons/" -name "${SYSICN}.png" | sort -nr | head -n1)"
				if [ -f "$SYSICF" ]; then
					if [ -x "$(command -v "$CONVERT" 2>/dev/null)" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Creating a '$SYSICF' copy with fix size 128x128 at '$WANTGPNG'"
						"$CONVERT" "$SYSICF" -resize "128x128!" "$WANTGPNG"
					else
						writelog "INFO" "${FUNCNAME[0]} - Copying '$SYSICF' to '$WANTGPNG'"
						cp "$SYSICF" "$WANTGPNG"
					fi
				else
					writelog "INFO" "${FUNCNAME[0]} - Did not find a default icon for '$GAMENAME' using '$STLICON' as default for '$WANTGPNG'"
					standaloneDefGameIcon
				fi
			fi
		else
			PEVDSTI="$STLGPEVKD/$PERES/id/$AID"

			if [ -x "$(command -v "$PERES")" ] && { [ ! -d "$PEVDSTI" ] || [ "$(find "$PEVDSTI" -type f | wc -l)" -eq 0 ];}; then
				mkProjDir "$PEVDSTI"
				writelog "INFO" "${FUNCNAME[0]} - extracting data from '${HAVEPA##*/}' using '$PERES' to '$PEVDSTI'"
				cd "$PEVDSTI" >/dev/null || return
				notiShow "$(strFix "$NOTY_ANALYZE" "$HAVEPA" "$PERES")"
				"$PERES" -x "$HAVEPA" &
				cd - >/dev/null || return
			fi

			PRI="$PEVDSTI/resources/icons"
			if [ ! -d "$PRI" ]; then
				writelog "INFO" "${FUNCNAME[0]} - directory for alternative icon location not found - using '$STLICON' as default for '$WANTGPNG'"
				standaloneDefGameIcon
			else
				FIRSTICO="$(find "$PRI" -type f -name "*.ico" -printf "%s %p\n" | sort -nr | head -n1 | cut -d ' ' -f2)"
				if [ -f "$FIRSTICO" ]; then
					if [ -x "$(command -v "$CONVERT" 2>/dev/null)" ]; then
						writelog "INFO" "${FUNCNAME[0]} - Converting '$FIRSTICO', extracted from '${HAVEPA##*/}' to '$WANTGPNG'"
						"$CONVERT" "$FIRSTICO" -resize "128x128!" "$WANTGPNG"
					else
						writelog "INFO" "${FUNCNAME[0]} - Could not convert '$FIRSTICO', because '$CONVERT' was not found - using '$STLICON' as default for '$WANTGPNG'"
						cp "$STLICON" "$WANTGPNG"
					fi
				else
					writelog "INFO" "${FUNCNAME[0]} - Could not extract icon from '${HAVEPA##*/}' - using '$STLICON' as default for '$WANTGPNG'"
					standaloneDefGameIcon
				fi
			fi
		fi
	fi
}

function standaloneDesktopFile {
	INTDTFILE="$1"
	WANTGPNG="$2"
	AID="$3"
	GAMENAME="$4"

	if [ -f "$INTDTFILE" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - $INTDTFILE already exists"
	else
		if [ -n "$GAMENAME" ] && [ "$GAMENAME" != "$NON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Creating '$INTDTFILE' for '$GAMENAME'"
			{
				echo "[Desktop Entry]"
				echo "Name=${GAMENAME//\"/}"
				echo "Comment=$(strFix "$DF_SLCOMMENT" "${GAMENAME//\"/}" "$PROGNAME")"
				if [ "$INFLATPAK" -eq 1 ]; then
					echo "Exec=/usr/bin/flatpak run --command=steamtinkerlaunch $FLATPAK_ID play $AID"
				else
					echo "Exec=$(realpath "$0") play $AID"
				fi
				echo "Icon=$WANTGPNG"
				echo "Terminal=false"
				echo "Type=Application"
				echo "Categories=Game;"
			} >> "$INTDTFILE"
		fi
	fi
}

function standaloneLaunch {
	setShowPic
	STLA="Standalone-Launcher"
	export CURWIKI="$PPW/$STLA"
	TITLE="${PROGNAME}-$STLA"
	pollWinRes "$TITLE"
	echo "STLISLDFD $STLISLDFD"
	if [ -d "$STLISLDFD" ] && [ "$(find "$STLISLDFD" -name "*.desktop" | wc -l)" -ge 1 ]; then
		"$YAD" --f1-action="$F1ACTION" --icons --window-icon="$STLICON" --read-dir="$STLISLDFD" "$WINDECO" --title="$TITLE" --single-click --keep-icon-size --center --compact --sort-by-name "$GEOM"
	else
		writelog "SKIP" "${FUNCNAME[0]} - No games found in '$STLISLDFD' or directory itself not found"
	fi
}

function standaloneEd {
	if [ -z "$1" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - Need a valid AppID or title for an installed standalone program" "E"
	else
		if [ "$1" -eq "$1" ] 2>/dev/null; then
			AID="$1"
			HAVID="$AID"
		else
			if [ -d "$STLISLDFD" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Looking for AppID for title '$1'" "E"
				TSTAID="$(grep "^Name=$1$" -h -A5 -R "$STLISLDFD" | grep -m1 "Exec=" | grep -o "$GETSTAID")"
				if [ -n "$TSTAID" ] && [ "$TSTAID" -eq "$TSTAID" ] 2>/dev/null; then
					AID="$TSTAID"
					HAVID="$AID"
				fi
			else
				writelog "ERROR" "${FUNCNAME[0]} - directory '$STLISLDFD' missing - can't search for '$1'" "E"
			fi
		fi

		if [ -n "$AID" ] && [ "$AID" != "$PLACEHOLDERAID" ]; then

			unset MEGAMEDIR GAMENAME KEEPGAMENAME STL_COMPAT_DATA_PATH
			writelog "INFO" "${FUNCNAME[0]} - Looking for configs for AppID '$AID'" "E"
			HAVGEME="$GEMETA/${AID}.conf"
			if [ -f "$HAVGEME" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Loading found config '$HAVGEME'" "E"
				loadCfg "$HAVGEME"
			fi

			HAVCUME="$CUMETA/${AID}.conf"
			if [ -f "$HAVCUME" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Loading found config '$HAVCUME'" "E"
				loadCfg "$HAVCUME"
			fi

			WANTGPNG="$STLGPNG/${AID}.png"
			CPICKPROG="$MEGAMEDIR/$EXECUTABLE"

			STED="Standalone-Editor"
			STLA="Standalone-Launcher"
			export CURWIKI="$PPW/$STLA"
			TITLE="${PROGNAME}-$STED"
			pollWinRes "$TITLE"

			NEWSTDAT="$("$YAD" --f1-action="$F1ACTION" --image "$WANTGPNG" --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
			--title="$TITLE" --separator="|" \
			--text="$STED" \
			--field=" ":LBL " " \
			--field="     $GUI_PICKPROG!$DESC_PICKPROG":FL "$CPICKPROG" \
			--field="     $GUI_GAMENAME!$DESC_GAMENAME" "${GAMENAME/#-/ -}" \
			--field="     $GUI_KEEPGAMENAME!$DESC_KEEPGAMENAME":CHK "${KEEPGAMENAME/#-/ -}" \
			--field="     $GUI_STL_COMPAT_DATA_PATH!$DESC_STL_COMPAT_DATA_PATH":DIR "${STL_COMPAT_DATA_PATH/#-/ -}" \
			--field="     $GUI_PICKICON!$DESC_PICKICON":FL "${WANTGPNG/#-/ -}" \
			--button="$BUT_DONE":0 --button="$BUT_CAN":2  "$GEOM")"
			case $? in
			0)  {
					writelog "INFO" "${FUNCNAME[0]} - Selected $BUT_DONE"
					unset NSTARR
					mapfile -d "|" -t -O "${#NSTARR[@]}" NSTARR < <(printf '%s' "$NEWSTDAT")
					NPICKPROG="${NSTARR[1]}"
					NGAMENAME="${NSTARR[2]}"
					NKEEPGAMENAME="${NSTARR[3]}"
					NSTL_COMPAT_DATA_PATH="${NSTARR[4]}"
					NPICKICON="${NSTARR[5]}"

					NEWIC=0

					if [ "$NPICKPROG" != "$CPICKPROG" ]; then
						NMEGAMEDIR="${NPICKPROG%/*}"
						NEXECUTABLE="${NPICKPROG##*/}"

						if [ "$NMEGAMEDIR" != "$MEGAMEDIR" ]; then
							touch "$FUPDATE"
							updateConfigEntry "MEGAMEDIR" "$NMEGAMEDIR" "$HAVCUME"
							NEWIC=1
						fi
						if [ "$NEXECUTABLE" != "$EXECUTABLE" ]; then
							touch "$FUPDATE"
							updateConfigEntry "EXECUTABLE" "$NEXECUTABLE" "$HAVGEME"
							NEWIC=1
						fi
					fi

					if [ "$NGAMENAME" != "$GAMENAME" ]; then
						touch "$FUPDATE"
						updateConfigEntry "GAMENAME" "$NGAMENAME" "$HAVGEME"
						NEWIC=1
					fi

					updateConfigEntry "KEEPGAMENAME" "$NKEEPGAMENAME" "$HAVGEME"

					if [ "$NSTL_COMPAT_DATA_PATH" != "$STL_COMPAT_DATA_PATH" ]; then
						touch "$FUPDATE"
						updateConfigEntry "STL_COMPAT_DATA_PATH" "$NSTL_COMPAT_DATA_PATH" "$HAVGEME"
						NEWIC=1
					fi

					if [ "$NPICKICON" != "$WANTGPNG" ]; then
						if [ -x "$(command -v "$CONVERT" 2>/dev/null)" ]; then
							"$CONVERT" "$NPICKICON" -resize "128x128!" "$WANTGPNG"
						else
							cp "$NPICKICON" "$WANTGPNG"
						fi
						NEWIC=1
					fi

					if [ "$NEWIC" -eq 1 ]; then
						loadCfg "$HAVGEME"
						loadCfg "$HAVCUME"
						WANTGPNG="$STLGPNG/${AID}.png"
						HAVEPA="$MEGAMEDIR/$EXECUTABLE"
						INTDTFILE="$STLISLDFD/$AID.desktop"
						rm "$INTDTFILE"
						standaloneDesktopFile "$INTDTFILE" "$WANTGPNG" "$AID" "$GAMENAME" "$HAVEPA"
					fi
				}
			;;
			2) 	{
					writelog "INFO" "${FUNCNAME[0]} - Selected $BUT_CAN"

				}
			;;
			esac
		else
			writelog "ERROR" "${FUNCNAME[0]} - No data found for '$1'" "E"
		fi
	fi
}

function createDLWineList {
	writelog "INFO" "${FUNCNAME[0]} - Generating list of online available Wine archives"
	WINEDLLIST="$STLSHM/WineDL.txt"
	MAXAGE=360

	if [ ! -f "$WINEDLLIST" ] || test "$(find "$WINEDLLIST" -mmin +"$MAXAGE")"; then
		rm "$WINEDLLIST" 2>/dev/null
		while read -r CWURL; do
		if grep -q "$GHURL" <<< "${!CWURL}"; then
			SRCURL="${!CWURL}"
			SRCURL="${SRCURL//\/releases}"
			SRCURL="${SRCURL//$GHURL/$AGHURL\/repos}"
			SRCURL="${SRCURL}/releases"
			"$WGET" -q "$SRCURL" -O - | "$JQ" -r '.[].assets[].browser_download_url' | grep "tar.gz\|tar.xz" >>  "$WINEDLLIST"
		fi
		done <<< "$(grep "^CW_" "$STLURLCFG" | cut -d '=' -f1)"
	fi

	unset WineDLList
	unset WineDLDispList
	while read -r CWVERS; do
		mapfile -t -O "${#WineDLList[@]}" WineDLList <<< "$CWVERS"
		mapfile -t -O "${#WineDLDispList[@]}" WineDLDispList <<< "${CWVERS##*/}"
	done < "$WINEDLLIST"
}

function dlWineGUI {
	createDLWineList

	writelog "INFO" "${FUNCNAME[0]} - Opening dialog to choose a download"

	DLWINELIST="$(printf "!%s\n" "${WineDLDispList[@]//\"/}" | tr -d '\n' | sed "s:^!::" | sed "s:!$::")"
	export CURWIKI="$PPW/Download-Custom-Wine"
	TITLE="${PROGNAME}-DownloadWine"
	pollWinRes "$TITLE"

	if [ -z "$DLWINE" ]; then
		DLWINE="${WineDLDispList[0]}"
	fi

	DLDISPWINE="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
	--title="$TITLE" \
	--text="$(spanFont "$GUI_DLWINETEXT" "H")" \
	--field=" ":LBL " " \
	--field="$GUI_DLWINETEXT2!$GUI_DLWINETEXT":CBE "$(cleanDropDown "${DLWINE/#-/ -}" "$DLWINELIST")" \
	"$GEOM")"

	if [ -n "${DLDISPWINE//|/\"}" ]; then
		if grep -q "^http" <<< "${DLDISPWINE//|/\"}"; then  #"
			DLURL="${DLDISPWINE//|/\"}"
			writelog "INFO" "${FUNCNAME[0]} - The URL '$DLURL' was entered manually - downloading directly"
		else
			DLWINEVERSION="${DLDISPWINE//|/}"
			DLURL="$(printf "%s\n" "${WineDLList[@]}" | grep -m1 "${DLDISPWINE//|}")"
			writelog "INFO" "${FUNCNAME[0]} - '${DLDISPWINE//|}' was selected - downloading '$DLURL'"
		fi
		StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "Wine")" "dlWine ${DLURL//|/\"}" "DownloadWineStatus"
	fi
}

# TODO currently unused:
function PickSpecificWine {
	export CURWIKI="$PPW/Download-Custom-Wine"
	TITLE="${PROGNAME}-${FUNCNAME[0]}"
	pollWinRes "$TITLE"

	SPECWINE="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
	--title="$TITLE" --separator="|" \
	--text="$(spanFont "GUI_DLSPECWINE" "H")" \
	--field=" ":LBL " " \
	--field="Vanilla":CHK "TRUE" \
	--field="Staging":CHK "TRUE" \
	--field="Proton":CHK "TRUE" \
	--field="TkG":CHK "TRUE" \
	--field=" ":LBL " " \
	--field="x86":CHK "TRUE" \
	--field="amd64":CHK "TRUE" \
	"$GEOM")"

	unset SWINSEL
	mapfile -d "|" -t -O "${#SWINSEL[@]}" SWINSEL < <(printf '%s' "$SPECWINE")
	WANTVAN="${SWINSEL[1]}"
	WANTSTA="${SWINSEL[2]}"
	WANTPRO="${SWINSEL[3]}"
	WANTTKG="${SWINSEL[4]}"
	WANTX86="${SWINSEL[6]}"
	WANTA64="${SWINSEL[7]}"

	if [ "$WANTVAN" == "TRUE" ]; then
		GWI="[0-9]-[x,a]"
		GWO="$NON"
	else
		GWO="[0-9]-[x,a]"
		GWI="releases"
	fi

	if [ "$WANTSTA" == "TRUE" ]; then
		GWI="${GWI}\|staging"
	else
		GWO="${GWO}\|staging"
	fi

	if [ "$WANTPRO" == "TRUE" ]; then
		GWI="${GWI}\|proton"
	else
		GWO="${GWO}\|proton"
	fi

	if [ "$WANTTKG" == "TRUE" ]; then
		GWI="${GWI}\|tkg"
	else
		GWO="${GWO}\|tkg"
	fi

	if [ "$WANTX86" == "TRUE" ]; then
		GWI="${GWI}\|x86"
	else
		GWO="${GWO}\|x86"
	fi

	if [ "$WANTA64" == "TRUE" ]; then
		GWI="${GWI}\|amd64"
	else
		GWO="${GWO}\|amd64"
	fi
}

function dlWine {
	WURL="${1//\"/}"
	WURLFILE="${WURL##*/}"
	DSTDL="$WINEDLDIR/$WURLFILE"
	if [ ! -f "$DSTDL" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Downloading '$WURL' to '$WINEDLDIR'"
		notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$WURL")" "S"
		dlCheck "$WURL" "$DSTDL" "X" "Downloading '$WURLFILE'"
		notiShow "$(strFix "$NOTY_DLCUSTOMPROTON2" "$WURL")" "S"
	else
		writelog "INFO" "${FUNCNAME[0]} - File '$DSTDL' already exists - nothing to download"
		notiShow "$(strFix "$NOTY_DLCUSTOMPROTON4" "${WURL##*/}")" "S"
	fi
	extractWine "$DSTDL"
}

function dlWineGate {
	if [ -z "$1" ]; then
		dlWineGUI
	else
		if grep -q "^http" <<< "$1"; then
			writelog "INFO" "${FUNCNAME[0]} - '$1' is an URL - sending directly to dlWine"
			StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "Wine")" "dlWine $*" "DownloadWineStatus"
		elif [ "$1" == "latest" ] || [ "$1" == "l" ]; then
			createDLWineList
			writelog "INFO" "${FUNCNAME[0]} - Downloading latest custom Wine ${WineDLList[0]//\"/}" "E"
			StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "Wine")" "dlWine ${WineDLList[0]//\"/}" "DownloadWineStatus"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Don't know what to do with argument '$1'"
		fi
	fi
}

function extractWine {
	if [ -f "$1" ]; then
		WURLFILE="${1##*/}"
		WDIRRAW="${WURLFILE//wine-/}"
		WDIR="${WDIRRAW//.tar.xz}"

		if [ -d "$WINEEXTDIR/$WDIR" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Directory '$WINEEXTDIR/$WDIR' already exists - nothing to extract"
		else
			writelog "INFO" "${FUNCNAME[0]} - Extracting archive '$WURLFILE' to '$WINEEXTDIR'"
			notiShow "$(strFix "$NOTY_DLCUSTOMPROTON3" "$1")" "S"
			"$TAR" xf "$1" -C "$WINEEXTDIR" 2>/dev/null
			notiShow "$GUI_DONE" "S"
		fi
	fi
}

function WineSelection {
	setShowPic
	export CURWIKI="$PPW/Wine-Support"
	TITLE="${PROGNAME}-ChooseWine"
	pollWinRes "$TITLE"

	writelog "INFO" "${FUNCNAME[0]} - Opening Wine Selection"

	 "$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --center --window-icon="$STLICON" --form --center "$WINDECO" \
	--title="$TITLE" \
	--text="$(spanFont "$GUI_SELWINE" "H")" \
	--button="$BUT_DLDWINE":0 \
	--button="$BUT_SELWINE":2 \
	"$GEOM"

	case $? in
	0)  {
			writelog "INFO" "${FUNCNAME[0]} - Selected Wine Download"
			dlWineGUI
			if [ "$DLWINEVERSION" != "$NON" ]; then
				WINEVERSION="${DLWINEVERSION//|/}"
				writelog "INFO" "${FUNCNAME[0]} - Chose downloaded '$WINEVERSION'"
			fi
		}
	;;
	2) 	{
			writelog "INFO" "${FUNCNAME[0]} - Selected Ready Wine"
			createWineList
			export CURWIKI="$PPW/Wine-Support"
			TITLE="${PROGNAME}-SelectedWine"
			pollWinRes "$TITLE"
			WINSEL="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --form --center --on-top "$WINDECO" \
			--title="$TITLE" \
			--text="$(spanFont "$GUI_SELIWINE" "H")" \
			--field=" ":LBL " " \
			--field="     $GUI_WINEVERSION!$DESC_WINEVERSION ('WINEVERSION')":CBE "$(cleanDropDown "${WINEVERSION/#-/ -}" "$WINEYADLIST")" \
			"$GEOM")"
			WINEVERSION="${WINSEL//|/}"
			writelog "INFO" "${FUNCNAME[0]} - Chose available '$WINEVERSION'"
		}
	;;
	esac

	writelog "INFO" "${FUNCNAME[0]} - Saving selected wine version '$WINEVERSION' into $STLGAMECFG"
	touch "$FUPDATE"
	updateConfigEntry "WINEVERSION" "$WINEVERSION" "$STLGAMECFG"

	if [ -n "$2" ]; then
		setWineVersion "$AID" "${FUNCNAME[0]}"
	fi
}

function setWineVersion {
	if [ "$USEWINE" -eq 1 ] && [ "$ISGAME" -eq 2 ]; then
		if [[ "$WINEVERSION" =~ ${DUMMYBIN}$ ]] || [ "$WINEVERSION" == "$NON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - No current wine version configured yet"
			if [[ ! "$WINEDEFAULT" =~ ${DUMMYBIN}$ ]] && [ "$WINEDEFAULT" != "$NON" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Default wine version set to '$WINEDEFAULT' - using that as current wine"
				WINEVERSION="$WINEDEFAULT"
			else
				writelog "INFO" "${FUNCNAME[0]} - No current wine version configured and no default one set, so opening a requester"
				WineSelection "$AID" "${FUNCNAME[0]}"
				writelog "INFO" "${FUNCNAME[0]} - Chose '$WINEVERSION' via requester"
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - Using current wine version '$WINEVERSION'"
		fi

		WINEVERSION="${WINEVERSION%%.tar*}"

		if [ ! -d "$WINEEXTDIR/${WINEVERSION}" ]; then

			if ! grep -q "$WINEVERSION" "$WINEDLLIST"; then
				writelog "ERROR" "${FUNCNAME[0]} - Failed to set wineversion to something usable: '$WINEVERSION' - giving up"
				closeSTL " ######### STOP EARLY '$PROGNAME $PROGVERS' #########"
				exit
			else
				writelog "INFO" "${FUNCNAME[0]} - Configured wine version '$WINEVERSION' is not installed yet, but was found detected as downloadable - trying to install it automatically"
				DLURL="$(grep "$WINEVERSION" "$WINEDLLIST" | head -n1)"
				writelog "INFO" "${FUNCNAME[0]} - Downloading '$WINEVERSION' from '$DLURL'"
				StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "Wine")" "dlWine ${DLURL//|/\"}" "DownloadWineStatus"
				if [ -d "$WINEEXTDIR/$WINEVERSION" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Downloading and extracting '$WINEVERSION' was successful"
				else
					writelog "ERROR" "${FUNCNAME[0]} - Downloading and extracting '$WINEVERSION' failed - giving up"
					closeSTL " ######### STOP EARLY '$PROGNAME $PROGVERS' #########"
					exit
				fi
			fi
		fi
	fi
	RUNWINEVERSION="$WINEVERSION"
	writelog "INFO" "${FUNCNAME[0]} - Selected wine version is '$RUNWINEVERSION'"
}

function createWineList {
	writelog "INFO" "${FUNCNAME[0]} - Updating the Wine Dropdown List"
	WINEYADLIST="$(printf "!%s\n" "$(find "$WINEEXTDIR" -mindepth 1 -maxdepth 1 -type d -printf '%P!')" | tr -d '\n' | sed "s:^!::" | sed "s:!$::")"
}

function setWineVars {
	WINEVERSION="${WINEVERSION%%.tar*}"

	if [ "$USEWINE" -eq 1 ] && [ "$ISGAME" -eq 2 ] && { [ -z "$RUNWINEVERSION" ] || [ "$RUNWINEVERSION" != "$WINEVERSION" ];}; then
		writelog "INFO" "${FUNCNAME[0]} - USEWINE is enabled. Creating some wine related variables"

		if [ -n "$RUNWINEVERSION" ] && [[ "$WINEVERSION" =~ ${DUMMYBIN}$ ]]; then
			WINEVERSION="$RUNWINEVERSION"
		else
			createDLWineList
		fi

		setWineVersion

		USEWINEBIN="$WINEEXTDIR/${WINEVERSION}/bin"
		writelog "INFO" "${FUNCNAME[0]} - Setting wine bin dir to '$USEWINEBIN'"

		RUNWINE="$USEWINEBIN/wine"
		writelog "INFO" "${FUNCNAME[0]} - Setting wine binary to '$RUNWINE'"

		RUNWINECFG="$USEWINEBIN/$WINECFG"
		writelog "INFO" "${FUNCNAME[0]} - Setting $WINECFG binary to '$RUNWINECFG'"

		RUNREGEDIT="$USEWINEBIN/regedit"
		writelog "INFO" "${FUNCNAME[0]} - Setting regedit binary to '$RUNREGEDIT'"

		RUNWICO="$USEWINEBIN/$WICO"
		writelog "INFO" "${FUNCNAME[0]} - Setting $WICO binary to '$RUNWICO'"

		if [ -n "$ARCHALTEXE" ] && [[ ! "$ARCHALTEXE" =~ ${DUMMYBIN}$ ]]; then
			CHARCH="$ARCHALTEXE"
		else
			CHARCH="$GP"
		fi

		if [ "$(getArch "$CHARCH")" == "32" ]; then
			RUNWINEARCH=win32
		else
			RUNWINEARCH=win64
		fi

		writelog "INFO" "${FUNCNAME[0]} - Game binary '${CHARCH##*/}' is $(getArch "$CHARCH")-bit so creating '$RUNWINEARCH' WINEPREFIX"
		GWFX="${GPFX//pfx/wfx}"
		writelog "INFO" "${FUNCNAME[0]} - Using WINEPREFIX '$GWFX'"
	fi
}

# Get path to 'require_tool_appid' specified in toolmanifest.vdf
function getRequireToolAppidPath {
	COMPATTOOLPATH="$1"

	if [ -d "$COMPATTOOLPATH" ]; then
		TOMAPATH="${COMPATTOOLPATH}/$TOMA"
		if [ -f "$TOMAPATH" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found tool manifest at '$TOMAPATH', attempting to get 'require_tool_appid' value..."

			REQUIRETOOLAID="$( getValueFromAppManifest "require_tool_appid" "$TOMAPATH" )"  # toolmanifest.vdf and some other files have identical structures to AppManifest files, so this works :-)
			if [ -n "$REQUIRETOOLAID" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Got 'require_tool_appid' from '$TOMAPATH' ('$REQUIRETOOLAID') - Returning path to tool"

				getGameDir "$REQUIRETOOLAID" "X"
			else
				writelog "INFO" "${FUNCNAME[0]} - Could not get 'require_tool_appid' from existing file '$TOMAPATH' - Assuming the key was not present"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Could not get Steam Linux Runtime, could not find tool manifest at '$TOMAPATH'"
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - Could not find directory for specified compat tool '$COMPATTOOLPATH'"
	fi
}

# Function to get SLR to append to game/program launch
# Primarily used to set SLRCMD so it can be appended, but also sets the reaper command
# TODO: refactor to use early returns and less indentation where possible
function setSLRReap {
	# This function has gotten a bit messy with all the override options, but these are used to allow setSLRReap to be re-used outside of regular game launches such as for Vortex.

	# These variables are only passed for Non-Game SLR launches i.e. Vortex, they are ignored for game launches and use fallback values
	OVERRIDESLR="$1"  # Always get SLR, ignoring other vars that specify otherwise
	SLRFORCETYPE="${2:-0}"  # Force fetch the Proton SLR (1), or native SLR (2), ignoring value of ISGAME
	SLRPROTONVER="$3"  # Proton version to fetch the SLR version from (where to find the toolmanifest.vdf from) -- Optional, will fall back to RUNPROTON set by game

	# Allow overriding USESLR/HAVESLR and forcing to fetch the SLR anyway (used for times when SLR is needed outside of regular game launch e.g. Vortex)
	if [ -n "$OVERRIDESLR" ]; then
		writelog "INFO" "${FUNCNAME[0]} - OVERRIDESLR is enabled, ignoring user settings and fetching SLR anyway"
	fi

	# Set the Proton path to look for the toolmanifest.vdf file in for game launches we want RUNPROTON, but for non-game SLR cases we want to set a custom Proton path without overriding RUNPROTON which could interfere with subsequent game launches)
	if [ -z "$SLRPROTONVER" ]; then
		writelog "INFO" "${FUNCNAME[0]} - SLRPROTONVER is not defined, this is fine as regular game launches don't pass this"

		writelog "INFO" "${FUNCNAME[0]} - Falling back to RUNPROTON which is '$RUNPROTON'"
		SLRPROTONVER="${RUNPROTON}"
	fi

	# USESLR tells whether the user has chosen to get the SLR, HAVESLR refers to the legacy check for the SLR passed from the compat tool/Steam launch command
	if [[ ( -n "$USESLR" && -n "$HAVESLR" ) || -n "$OVERRIDESLR" ]]; then
		# SLR fetching from Steam start command
		# --------------
		# Sometimes the SLR comes from the compatibility tool (hence SLRCT, SLR Compat Tool) -- This only happens with Proton <= 4.11, and more critically, with games that are using
		# a Steam Linux Runtime compatibility tool. Some games, like CS2, have an SLR forced by Valve Testing and this cannot be disabled by the user
		#
		# In this case, we want to take the SLR given to us by the compatibility tool and use that
		# HAVESLRCT=1 will only be true if the SLR is coming from the compatibility tool
		if [ "$HAVESLRCT" -eq 1 ] && [ "$USESLR" -eq 1 ] && [ "$IGNORECOMPATSLR" -eq 0 ]; then
			writelog "INFO" "${FUNCNAME[0]} - ## SLR is enabled via USESLR=$USESLR - prepending SLR from Compatibility Tool to the current launch command"
			writelog "INFO" "${FUNCNAME[0]} - ## This can happen if a game is running with a Steam Linux Runtime compatibility tool enabled"
			SLRCMD=("${RUNSLRCT[@]}")
			writelog "INFO" "${FUNCNAME[0]} - RUNSLRCT is '${RUNSLRCT[*]}'"
		# This is very, very legacy and will likely never happen again
		# While the above case covers games that get their SLR from the compatibility tool (only old Proton versions, and any native game using SLR 1.0 or 3.0),
		# this case covers *regular native games* that pass the SLR in their start command, which should not happen anymore
		#
		# The reason we use an 'elif' is because games should only meet one of these conditions:
		# - SLR comes from the selected compatibility tool (e.g. if a user selects SLR 1.0 for a native game, or if one is selected for them)
		# - SLR comes from the game start command even if no compat tool is used (should not happen anymore, legacy Steam behaviour)
		# - SLR is not given to us *at all*, so we use the SLR fetching below
		elif [ "$HAVESLR" -eq 1 ] && [ "$USESLR" -eq 1 ] && [ "$IGNORECOMPATSLR" -eq 0 ]; then
			writelog "INFO" "${FUNCNAME[0]} - ## SLR is enabled via USESLR=$USESLR - prepending SLR from command line to the current launch command"
			writelog "INFO" "${FUNCNAME[0]} - RUNSLR is '${RUNSLR[*]}'"
			SLRCMD=("${RUNSLR[@]}")
		# If the user enables "IGNORECOMPATSLR", disable HAVESLR so that the below logic for Pressure Vessel Funtime will kick in and fetch the SLR manually
		# HAVESLR controls whether we Have an SLR coming from the incoming command line options, which is nowadays only really the case for native titles with a Compat Tool forced/selected by Valve Testing
		elif [ "$HAVESLRCT" -eq 1 ] && [ "$USESLR" -eq 1 ] && [ "$IGNORECOMPATSLR" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - ## SLR was found in incoming Start Command, likely from a compatibility tool, but 'IGNORECOMPATSLR' is '${IGNORECOMPATSLR}'"
			writelog "INFO" "${FUNCNAME[0]} - ## Ignoring this incoming SLR from the selected Steam Linux Runtime Compatibility Tool and instead letting SteamTinkerLaunch find the Steam Linux Runtime instead"
			writelog "WARN" "${FUNCNAME[0]} - ## Note that some games require a specific Steam Linux Runtime version, and if a given Steam Linux Runtime version that SteamTinkerLaunch looks for is not found, or if the version found does not match what a game might require, launching may fail"

			HAVESLR=0
		fi

		# Legacy case to ignore SLR gotten from commandline
		if [ "$HAVESLR" -eq 1 ] && [ "$USESLR" -eq 0 ] ; then
			writelog "SKIP" "${FUNCNAME[0]} - USESLR is disabled, so skipping '$SLR' found in the commandline: '${RUNSLR[*]}'"
		fi
		# --------------

		# SLR fetching (from toolmanifest.vdf / native Linux SLR AppID)
		# ---------------
		# TODO This could probably be refactored to have less indentation and return early...
		RUNFORCESLR=0
		if [[ ( "$HAVESLR" -eq 0 && "$USESLR" -eq 1 ) || -n "$OVERRIDESLR" ]]; then
			if [ -n "$LASTSLR" ] && [ -f "${LASTSLR% --verb*}" ] && [ "$FORCESLR" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - ## No SLR provided from command line, but FORCESLR is $FORCESLR, so prepending LASTSLR '$LASTSLR' to the current launch command"
				mapfile -d " " -t -O "${#LASTSLRARR[@]}" LASTSLRARR < <(printf '%s' "$LASTSLR")
				SLRCMD=("${LASTSLRARR[@]}")
				RUNFORCESLR=1
			else
				# Steam usually does not pass the SLR in the start command anymore, and gets it from the `toolmanifest.vdf` with the `"require_tool_appid": "<app_id>"` for Proton games
				# For native games, on Steam Deck it seems Valve enforce the regular Steam Linux Runtime, so we have separate logic for fetching that
				writelog "INFO" "${FUNCNAME[0]} - No SLR provided from command line, attempting to fetch required SLR from current compatibility tool's '$TOMA'"

				SLR_PATH=""
				SLRENTRYPOINT=""
				SLRVERB=""
				PROTON_SLRCMD=("")
				NATIVE_SLRCMD=("")

				# Pressure Vessel Funtime 2nd Edition Ver. 2.31
				writelog "INFO" "${FUNCNAME[0]} - Now executing Pressure Vessel Funtime 2nd Edition Ver. 2.31"
				# Get SLR Paths
				# Use native SLR: if ( ( game is native AND NOT forcing Proton ) OR forcing native )
				if [[ ( "$ISGAME" -eq 3 && "$SLRFORCETYPE" -eq 0 ) || "$SLRFORCETYPE" -eq 2 ]]; then
					# Native games already have a hardcoded initial native SLR AppID, so we can get the path from this hardcoded AppID
					# However they need to get the required "nested" SLR from the toolmanifest from the hardcoded native SLR - This is the SLR that the regular native SLR runs inside of
					# This nested AppID is stored in the hardcoded SLR's toolmanifest
					writelog "INFO" "${FUNCNAME[0]} - Looks like we have a native Linux game here - Checking for plain SLR (AppID '$SLRAID')"
					REQUIRED_APPID="$SLRAID"  # AppID of native SLR

					NATIVE_SLR_PATH="$( getGameDir "$REQUIRED_APPID" "X" )"  # Native SLR
					if [ -d "$NATIVE_SLR_PATH" ]; then
						SLR_PATH="$( getRequireToolAppidPath "$NATIVE_SLR_PATH" )"  # Nested SLR path for native SLR to run inside of
						writelog "INFO" "${FUNCNAME[0]} - Nested Steam Linux Runtime for native game seems to be '$SLR_PATH'"
						NATIVE_SLR_ENTRYPOINT="${NATIVE_SLR_PATH}/scout-on-soldier-entry-point-v2"
						NATIVE_SLRCMD=("$NATIVE_SLR_ENTRYPOINT" "--")  # Extra part to pass for native CMD which needs to be appended to regular SLRCMD
					else
						writelog "WARN" "${FUNCNAME[0]} - Could not find Steam Linux Runtime with AppID '$SLRAID' for native Linux game - This will need to be installed manually!"
					fi
				else
					SLR_PATH="$( getRequireToolAppidPath "$( dirname "$SLRPROTONVER" )" )"  # Path to SLR based on AppID in Proton's `toolmanifest.vdf`
				fi

				# Build SLRCMD
				if [ -d "$SLR_PATH" ]; then
					writelog "INFO" "${FUNCNAME[0]} - '$SLR_PATH' exists - Path gotten from specified AppID looks valid"
					SLRENTRYPOINT="${SLR_PATH}/_v2-entry-point"
					SLRVERB="--verb=$WFEAR"
					PROTON_SLRCMD=("$SLRENTRYPOINT" "$SLRVERB" "--")
				else
					writelog "WARN" "${FUNCNAME[0]} - Could not get path to Steam Linux Runtime - This will need to be installed manually!"
					writelog "WARN" "${FUNCNAME[0]} - Ignoring USESLR option since valid Steam Linux Runtime could not be found"
				fi

				# Passing even a blank `NATIVE_SLRCMD[@]` prevents games from launching, so we need this check
				if [ -n "${NATIVE_SLRCMD[*]}" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Building Steam Linux Runtime command for native game"
					SLRCMD=("${PROTON_SLRCMD[@]}" "${NATIVE_SLRCMD[@]}")  # Not really "Proton" for native games, but naming is hard
				elif [ -n "${PROTON_SLRCMD[*]}" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Building Steam Linux Runtime command for Proton game"
					SLRCMD=("${PROTON_SLRCMD[@]}")
				else
					if [ "${REQUIRED_APPID}" = "${SLRAID}" ]; then  # Assume native when REQUIRED_APPID is set to the native Linux SLRAID
						writelog "WARN" "${FUNCNAME[0]} - No native linux Steam Linux Runtime found, game will not use Steam Linux Runtime"
					else  # If not native, can only be Proton
						writelog "WARN" "${FUNCNAME[0]} - No Proton Steam Linux Runtime found, game will not use Steam Linux Runtime"
					fi
				fi
			fi
		fi
		# ---------------

		# Set Reaper command (currently no way to toggle this if we call setSLRReap for non-game launches, doesn't seem to have any negative impact though?)
		# ---------------
		if [ "$HAVEREAP" -eq 1 ] && [ "$USEREAP" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - ## reaper command is enabled via USEREAP=$USEREAP - prepending to the current launch command"
			SLRCMD=("${REAPCMD[@]}" "${SLRCMD[@]}")
		elif [ "$HAVEREAP" -eq 0 ] && [ "$USEREAP" -eq 1 ] && [ -n "$LASTREAP" ] && [ -f "$LASTREAP" ] && [ "$FORCEREAP" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - ## No reaper command provided from command line, but FORCEREAP is $FORCEREAP, so prepending LASTREAP '$LASTREAP' to the current launch command"
			SLRCMD=("$LASTREAP" "SteamLaunch" "AppId=$AID" "--" "${SLRCMD[@]}")
		fi
		# ---------------

		# Reaper is started *by Steam now for Proton games* after a game launch (i.e. after %command% but *not* before) so if reaper is disabled we have to check for and kill it
		# Doesn't apply to native games because we use 'steamtinkerlaunch %command%', and reaper is started as part of '%command%'.
		if [ "$USEREAP" -eq 0 ]; then
			if "$PGREP" -x "reaper"; then
				writelog "INFO" "${FUNCNAME[0]} - USEREAP is '$USEREAP' and found reaper process, killing it!"
				"$PKILL" -9 "reaper"
			fi
		fi

		if [ -n "${SLRCMD[*]}" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Adding SLR '${SLRCMD[*]}' to the launch command"
		else
			if [ "$USESLR" -eq 1 ]; then
				notiShow "$NOTY_SLRMISSING" "X"
			else
				writelog "INFO" "${FUNCNAME[0]} - SLRCMD is not defined, but USESLR is disabled, so this should be safe to ignore"
			fi
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - USESLR and HAVESLR not defined -- Probably shouldn't happen?"
	fi
}

function fetchGameSLRGui {
	if [ "$ISGAME" -eq 3 ]; then
		commandlineFetchGameSLR "$1" "1" "1"  # Native Linux SLR
	else
		commandlineFetchGameSLR "$1" "0" "1"  # Proton SLR
	fi
}

## Fetch the AppID required by a game's selected Proton version, and prompt Steam to install it with steam://install/<appid>
function commandlineFetchGameSLR {
	FUSEID "$1"
	USENATIVE="$2"  # We could pass this from the UI if we know we have a native game (ISGAME -eq 3)
	SLRDISPLAYNOTIFIER="${3:-0}"

	if [ "$USENATIVE" -eq 1 ]; then  # Get native Linux SLR
		# Check if SLR is already installed
		EXISTINGSLRPATH="$( getGameDir "$SLRAID" "only" )"
		if [ -d "$EXISTINGSLRPATH" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Required Steam Linux Runtime ('$SLRAID') is already installed at '$EXISTINGSLRPATH' -- Nothing to do."
			echo "Required Steam Linux Runtime ('$SLRAID') is already installed at '$EXISTINGSLRPATH' -- Nothing to do."
			if [ "$SLRDISPLAYNOTIFIER" -eq 1 ]; then
				notiShow "$NOTY_INSTALLSLR_ALREADYEXISTS"
			fi

			return
		fi

		SLRINSTALLCMD="steam steam://install/$SLRAID"
		writelog "INFO" "${FUNCNAME[0]} - Installing Steam Linux Runtime for Native Linux games"
		echo "Installing Steam Linux Runtime for Native Linux games"

		eval "$SLRINSTALLCMD"
		echo "Continue installation of tool from Steam install dialog."

		if [ "$SLRDISPLAYNOTIFIER" -eq 1 ]; then
			notiShow "$NOTY_INSTALLSLR_DONE"
		fi
	elif [ -f "$STLGAMECFG" ] && [ -n "$USEPROTON" ]; then  # If this is a game launched before with STL, get the Steam Linux Runtime for it
		PROTPATH="$( dirname "$( getProtPathFromCSV "$USEPROTON" )" )"

		# Very similar to logic in getRequireToolAppidPath
		TOMAPATH="${PROTPATH}/$TOMA"
		if [ -f "$TOMAPATH" ]; then
			SLRID="$( getValueFromAppManifest "require_tool_appid" "$TOMAPATH" )"
			if [ -n "$SLRID" ]; then
				# Check if SLR is already installed
				EXISTINGSLRPATH="$( getGameDir "$SLRID" "only" )"
				if [ -d "$EXISTINGSLRPATH" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Required Steam Linux Runtime ('$SLRID') is already installed at '$EXISTINGSLRPATH' -- Nothing to do."
					echo "Required Steam Linux Runtime ('$SLRID') is already installed at '$EXISTINGSLRPATH' -- Nothing to do."

					if [ "$SLRDISPLAYNOTIFIER" -eq 1 ]; then
						notiShow "$NOTY_INSTALLSLR_ALREADYEXISTS"
					fi

					return
				fi

				SLRINSTALLCMD="steam steam://install/$SLRID"
				writelog "INFO" "${FUNCNAME[0]} - Game Proton version '$USEPROTON' expects Steam Linux Runtime with AppID '$SLRID' - Requesting it from Steam..."
				echo "Game Proton version '$USEPROTON' expects Steam Linux Runtime with AppID '$SLRID' - Requesting it from Steam..."

				eval "$SLRINSTALLCMD"
				echo "Continue installation of tool from Steam install dialog."

				if [ "$SLRDISPLAYNOTIFIER" -eq 1 ]; then
					notiShow "$NOTY_INSTALLSLR_DONE"
				fi
			else  # No require_tool_appid set in toolmanifest.vdf
				writelog "ERROR" "${FUNCNAME[0]} - require_tool_appid was not defined ('$SLRID') -- Maybe no Steam Linux Runtime is required for this Proton version?"
				echo "require_tool_appid was not defined ('$SLRID') -- Maybe no Steam Linux Runtime is required for this Proton version?"

				if [ "$SLRDISPLAYNOTIFIER" -eq 1 ]; then
					notiShow "$NOTY_INSTALLSLR_NOREQUIRETOOLAPPID"
				fi
			fi
		else  # No toolmanifest.vdf set at all
			writelog "ERROR" "${FUNCNAME[0]} - Could not find $TOMA for Proton version '$USEPROTON' at path '$PROTPATH'"
			echo "Could not find $TOMA for Proton version '$USEPROTON' at path '$PROTPATH'"

			if [ "$SLRDISPLAYNOTIFIER" -eq 1 ]; then
				notiShow "$NOTY_INSTALLSLR_NOTOOLMANIFEST"
			fi
		fi
	else  # Not a valid game used with STL before
		writelog "ERROR" "${FUNCNAME[0]} - Could not find STLGAMECFG ('$STLGAMECFG') or USEPROTON ('$USEPROTON') for AppID '$AID'"
		echo "Could not find STLGAMECFG ('$STLGAMECFG') or USEPROTON ('$USEPROTON') for AppID '$AID'"

		if [ "$SLRDISPLAYNOTIFIER" -eq 1 ]; then
			notiShow "$NOTY_INSTALLSLR_INVALIDGAME"
		fi
	fi
}

function setBoxtronCmd {
	DOSEXE="$GP"
	if [ -x "$(command -v "$BOXTRONCMD" 2>/dev/null)" ]; then
		notiShow "$(strFix "$NOTY_BOXTRON" "$GN" "$AID")"
		# disable CHANGE_PULSE_LATENCY else audio gets stuck
		CHANGE_PULSE_LATENCY="0"
		EXTSTARTCMD=("$BOXTRONCMD" "$BOXTRONARGS" "$DOSEXE")
		writelog "INFO" "${FUNCNAME[0]} - Starting game '$SGNAID' with boxtron"
	else
		writelog "ERROR" "${FUNCNAME[0]} - boxtron command '$BOXTRONCMD' not found - exit"
		exit
	fi
}

function setRobertaCmd {
	VMEXE="$GP"
	if [ -x "$(command -v "$ROBERTACMD" 2>/dev/null)" ]; then
		EXTSTARTCMD=("$ROBERTACMD" "$ROBERTAARGS" "$VMEXE")
		writelog "INFO" "${FUNCNAME[0]} - Starting game '$AID' with roberta"
		notiShow "$(strFix "$NOTY_ROBERTA" "$GN" "$AID")"
	else
		writelog "ERROR" "${FUNCNAME[0]} - roberta command '$ROBERTACMD' not found - exit"
		exit
	fi
}

function setLuxtorpedaCmd {
	LUXEXE="$GP"
	if [ -x "$(command -v "$LUXTORPEDACMD" 2>/dev/null)" ]; then
		if [ "$USESLR" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using ${LUXTORPEDACMD^} (Runtime)"
			LUMADO="manual-download"
			LUXTORPEDAARGS="runtime_$LUXTORPEDAARGS"
		else
			LUMADO="manual-download"
		fi
		# skip download if engine_choice.txt exists already:
		if [ ! -f "$HOME"/.config/luxtorpeda/"$AID"/engine_choice.txt ]; then
			writelog "INFO" "${FUNCNAME[0]} - Downloading native game data for '$AID' with luxtorpeda: '$LUXTORPEDACMD' '$LUMADO' $AID"
			notiShow "$(strFix "$NOTY_LUXTORPEDA1" "$GN" "$AID")"
			"$LUXTORPEDACMD" "$LUMADO" "$AID"
		fi
		notiShow "$(strFix "$NOTY_LUXTORPEDA2" "$GN" "$AID")"
		EXTSTARTCMD=("$LUXTORPEDACMD" "$LUXTORPEDAARGS" "$LUXEXE")
		writelog "INFO" "${FUNCNAME[0]} - Starting game '$AID' with luxtorpeda"
	else
		writelog "ERROR" "${FUNCNAME[0]} - luxtorpeda command '$LUXTORPEDACMD' not found - exit"
		exit
	fi
}

function setLinuxCmd {
	# maybe limit this to custom linux commands
	if [ "${GAMESTARTCMD[0]}" == "$WFEAR" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Removing '$WFEAR' from '${GAMESTARTCMD[*]}'"
		GAMESTARTCMD=( "${GAMESTARTCMD[@]:1}" )
		writelog "INFO" "${FUNCNAME[0]} - Result is '${GAMESTARTCMD[*]}'"
	fi

	# start with gamemoderun:
	if [ "$USEGAMEMODERUN" -eq 1 ]; then
		if [ "$USEGAMESCOPE" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Starting native game '$SGNAID' with '$GAMEMODERUN' and '$GAMESCOPE'"
			notiShow "$(strFix "$NOTY_STARTNATGAMOSC" "$GN" "$AID")"
			gameScopeArgs "$GAMESCOPE_ARGS"
		else
			writelog "INFO" "${FUNCNAME[0]} - Starting native game '$SGNAID' with '$GAMEMODERUN' - ${GAMESTARTCMD[*]}"
			notiShow "$(strFix "$NOTY_STARTNATGAMO" "$GN" "$AID")"
		fi
	# start with gamescope:
	elif [ "$USEGAMESCOPE" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Starting native game '$SGNAID' with $GAMESCOPE arguments '$GAMESCOPE_ARGS'"
		notiShow "$(strFix "$NOTY_STARTNATGAMSCO" "$GN" "$AID")"
		gameScopeArgs "$GAMESCOPE_ARGS"
	# regular start:
	else
		writelog "INFO" "${FUNCNAME[0]} - Starting native game '$SGNAID'"
		notiShow "$(strFix "$NOTY_STARTNAT" "$GN" "$AID")"
	fi
}

function setWineCmd {
	function startWineGame {
		writelog "INFO" "${FUNCNAME[0]} - Starting game '$GN' ($AID)' using Wine"
		extWine64Run "$@" "$RUNWINE" "${FINGAMECMD[*]}" &> "$WINE_LOG_DIR/${AID}.log"
		WINEPID="$!"
	}

	writelog "INFO" "${FUNCNAME[0]} - Using Wine instead of Proton"
	setWineVars

	RUNGAMECMD="${GAMESTARTCMD[*]}"
	RAWGAMECMD="${RUNGAMECMD#*waitforexitandrun }"
	mapfile -d " " -t -O "${#FINGAMECMD[@]}" FINGAMECMD < <(printf '%s' "$RAWGAMECMD")

	writelog "INFO" "${FUNCNAME[0]} - Starting game $GN with '$("$RUNWINE" --version)' and waiting for its PID to exit"

	# start with gamemoderun:
	if [ "$USEGAMEMODERUN" -eq 1 ]; then
		if [ "$USEGAMESCOPE" -eq 1 ]; then
			gameScopeArgs "$GAMESCOPE_ARGS"
			notiShow "$(strFix "$NOTY_STARTPROTGAMOSC" "$RUNWINEVERSION" "$GN" "$AID")"
			startWineGame "$GMR" "$GSC" "${GAMESCOPEARGSARR[@]}"
			writelog "INFO" "${FUNCNAME[0]} - Started game $GN via wine using '$GAMEMODERUN' and '$GAMESCOPE' with PID '$WINEPID'"
		else
			notiShow "$(strFix "$NOTY_STARTPROTGAMO" "$RUNWINEVERSION" "$GN" "$AID")"
			startWineGame "$GMR"
			writelog "INFO" "${FUNCNAME[0]} - Started game $GN via wine using '$GAMEMODERUN' with PID '$WINEPID'"
		fi
	# start with gamescope:
	elif [ "$USEGAMESCOPE" -eq 1 ]; then
		gameScopeArgs "$GAMESCOPE_ARGS"
		notiShow "$(strFix "$NOTY_STARTPROTGAMSCO" "$RUNWINEVERSION" "$GN" "$AID")"
		startWineGame "$GSC" "${GAMESCOPEARGSARR[@]}"
		writelog "INFO" "${FUNCNAME[0]} - Started game $GN via wine using '$GAMESCOPE' with PID '$WINEPID'"
	# regular start:
	else
		notiShow "$(strFix "$NOTY_STARTPROT" "$RUNWINEVERSION" "$GN" "$AID")"
		startWineGame
		writelog "INFO" "${FUNCNAME[0]} - Started game $GN via wine with PID '$WINEPID'"
	fi

	wait "$WINEPID"
	writelog "INFO" "${FUNCNAME[0]} - game PID '$WINEPID' exited..."
}

function setProtonCmd {
	# proton variants start here:
	if [ "$RUN_GDB" -eq 1 ]; then
		GDBGAMESTARTCMD=("${ORGGCMD[@]}" "${GAMEARGSARR[@]}")
	elif [ "$HAVEINPROTON" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Not overriding Proton and using Proton provided by steam commandline '${INPROTCMD[*]}'"
		PROTSTARTCMD=("${INPROTCMD[@]}" "$WFEAR")
	else
		writelog "INFO" "${FUNCNAME[0]} - Proton override enabled, so checking if it needs updated"

		if [ -z "$USEPROTON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - No current Proton found"
			if [ "$AUTOLASTPROTON" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Automatically selecting newest official one"
				setNOP
			fi
		fi


		setRunProtonFromUseProton # the last chance to set the Proton version before starting the game
		delPrefix # remove prefix if requested
		fixSymlinks # fixing outdated symlinks if requested
		unSymlink  # and/or unsymlink any proton symlink found

		if [ "$HAVEINPROTON" -eq 1 ] && [ "${INPROTCMD[*]}" == "$RUNPROTON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Command line proton '${INPROTCMD[*]}' is identical to RUNPROTON '$RUNPROTON' - nothing to change"
			PROTSTARTCMD=("${INPROTCMD[@]}" "$WFEAR")
		else
			if [ ! -f "${RUNPROTON//\"/}" ]; then
				writelog "WARN" "${FUNCNAME[0]} - '$USEPROTON' seems outdated as the executable ${RUNPROTON//\"/} wasn't found"
				fixProtonVersionMismatch "USEPROTON" "$STLGAMECFG"
			fi

			if [ "${GAMESTARTCMD[0]}" == "$WFEAR" ]; then
				unset "GAMESTARTCMD[0]"  # removing first ORGGCMD element as it is '$WFEAR'
			fi

			if [ -f "${RUNPROTON//\"/}" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Prepending Proton '$USEPROTON' (='${RUNPROTON//\"/}') to the command line '${GAMESTARTCMD[*]}'"
				PROTSTARTCMD=("${RUNPROTON//\"/}" "$WFEAR")
			else
				writelog "INFO" "${FUNCNAME[0]} - Still don't have a usable proton executable in RUNPROTON '{RUNPROTON//\"/}')"
				if [ "$HAVEINPROTON" -eq 1 ]; then
					writelog "INFO" "${FUNCNAME[0]} - Overriding Proton provided by steam commandline '${INPROTCMD[*]}' from command line with '$USEPROTON' (='${RUNPROTON//\"/}')"
					PROTSTARTCMD=("${INPROTCMD[@]}" "$WFEAR")
				else
					writelog "ERROR" "${FUNCNAME[0]} - Could not find any usable proton version - this will likely crash - please open an issue on '$PROJECTPAGE' with this log"
				fi
			fi
			writelog "INFO" "${FUNCNAME[0]} - UPDATED game start command is: ${GAMESTARTCMD[*]}"
		fi

		if [ "$USERAYTRACING" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Raytracing is enabled with the variable 'USERAYTRACING' - exporting 'VKD3D_CONFIG=dxr11' and appending '-dx12' to the game command line parameters"
			export VKD3D_CONFIG=dxr11
			GAMEARGSARR=("${GAMEARGSARR[@]}" "-dx12")
		fi
	fi

	# set the definitive used versions, which are also stored into writeLastRun
	PROTONVERSION="$(setProtonPathVersion "$RUNPROTON")"

	# start with x64dbg:
	if [ "$RUN_X64DBG" -eq 1 ]; then
		checkX64dbgLaunch "${GAMESTARTCMD[@]}"
	elif [ "$RUN_GDB" -eq 1 ]; then
		if [ -f "$(command -v "$USETERM")" ]; then
			prepareGdb
			injectGdb &
			writelog "INFO" "${FUNCNAME[0]} - Starting '$SGNAID' using '$GDBGAMERUN' and attaching gdb to the running process"
			writelog "WARN" "${FUNCNAME[0]} - This function might not always work as expected - not sure yet if it is worth to maintain"
			notiShow "$(strFix "$NOTY_STARTPROTGDB" "$PROTONVERSION" "$GN" "$AID")"
			"$GDBGAMERUN"
		else
			writelog "ERROR" "${FUNCNAME[0]} - '$GDB' was enabled, but configured terminal '$USETERM' was not found"
		fi
	elif [ "$RUN_DEPS" -eq 1 ]; then
		checkDepsLaunch "${GAMESTARTCMD[@]}"
	# start with gamemoderun:
	elif [ "$USEGAMEMODERUN" -eq 1 ]; then
		if [ "$USEGAMESCOPE" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Starting '$SGNAID' with Proton: '$PROTONVERSION' with '$GAMEMODERUN' and '$GAMESCOPE'"
			gameScopeArgs "$GAMESCOPE_ARGS"
			notiShow "$(strFix "$NOTY_STARTPROTGAMOSC" "$PROTONVERSION" "$GN" "$AID")"
		else
			writelog "INFO" "${FUNCNAME[0]} - Starting '$SGNAID' with Proton: '$PROTONVERSION' with '$GAMEMODERUN'"
			notiShow "$(strFix "$NOTY_STARTPROTGAMO" "$PROTONVERSION" "$GN" "$AID")"
		fi
	# start with gamescope:
	elif [ "$USEGAMESCOPE" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Starting '$SGNAID' with Proton: '$PROTONVERSION' with $GAMESCOPE arguments '$GAMESCOPE_ARGS'"
		notiShow "$(strFix "$NOTY_STARTPROTGAMSCO" "$PROTONVERSION" "$GN" "$AID")"
		gameScopeArgs "$GAMESCOPE_ARGS"
	# regular start:
	else
		writelog "INFO" "${FUNCNAME[0]} - Starting '$SGNAID' with Proton: '$PROTONVERSION'"
		notiShow "$(strFix "$NOTY_STARTPROT" "$PROTONVERSION" "$GN" "$AID")"
	fi
}

function launchSteamGame {
	steamdeckBeforeGame

	setCommandLaunchVars  # Re-usable function for Steam games and custom program launches

	writelog "INFO" "${FUNCNAME[0]} - Initial game command is '${INGCMD[*]}'"

	# Refetch SLR, in case we set it for custom command as well
	# i.e. custom command could use native SLR, but we might want to use Proton SLR for the game, or vice versa
	unset "${SLRCMD[@]}"
	setSLRReap

	if [ "$USEBOXTRON" -eq 1 ] || [ "$USEROBERTA" -eq 1 ] || [ "$USELUXTORPEDA" -eq 1 ]; then
		ISGAME=3
	fi

	# game start command for both proton and linux native games:
	if [ "$USEBOXTRON" -eq 0 ] && [ "$USEROBERTA" -eq 0 ] && [ "$USELUXTORPEDA" -eq 0 ]; then
		# the actual game launch:
		gameArgs "$GAMEARGS"
		GAMESTARTCMD=("${ORGGCMD[@]}")
	fi

	# first start with non-proton games here:
	if [ "$ISGAME" -eq 3 ]; then
		if [ "$USEBOXTRON" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Preparing boxtron command"
			setBoxtronCmd
		elif [ "$USEROBERTA" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Preparing roberta command"
			setRobertaCmd
		elif [ "$USELUXTORPEDA" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Preparing luxtorpeda command"
			setLuxtorpedaCmd
		else
			writelog "INFO" "${FUNCNAME[0]} - Preparing linux native game command"
			setLinuxCmd
		fi

	# now games using proton or wine:
	elif [ "$ISGAME" -eq 2 ]; then
		if [ "$USEWINE" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Preparing wine command"
			setWineCmd
		else
			writelog "INFO" "${FUNCNAME[0]} - Preparing proton command"
			setProtonCmd
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - With ISGAME '$ISGAME' the game failed to start"
	fi

	# X64DBG_ATTACHONSTARTUP controls launching the game if x64dbg and this are enabled together, similar to ONLY_CUSTOMCMD, so don't do Steam game launch and exit function
	if [ "$RUN_X64DBG" -eq 1 ] && [ "$X64DBG_ATTACHONSTARTUP" -eq 1 ]; then
		writelog "INFO" "${FUNCNAME[0]} - RUN_X64DBG and X64DBG_ATTACHONSTARTUP were both enabled, this means x64dbg managed running the game process, so we don't have to -- Aborting Steam game launch"
		return
	fi

	if [ "$USEWINE" -eq 0 ]; then
		# concatenate final game start command

		buildCustomCmdLaunch

		FINALSTARTCMD=( "${FINALOUTCMD[@]}" )  # Use FINALOUTCMD from buildCustomCmdLaunch

		if [ -n "${SLRCMD[0]}" ]; then
			if [ -n "${FINALSTARTCMD[0]}" ]; then
				FINALSTARTCMD=("${FINALSTARTCMD[@]}" "${SLRCMD[@]}")
			else
				FINALSTARTCMD=("${SLRCMD[@]}")
			fi
		fi

		if [ -n "${EXTSTARTCMD[0]}" ]; then
			if [ -n "${FINALSTARTCMD[0]}" ]; then
				FINALSTARTCMD=("${FINALSTARTCMD[@]}" "${EXTSTARTCMD[@]}")
			else
				FINALSTARTCMD=("${EXTSTARTCMD[@]}")
			fi
		else
			if [ -n "${PROTSTARTCMD[0]}" ]; then
				if [ -n "${FINALSTARTCMD[0]}" ]; then
					FINALSTARTCMD=("${FINALSTARTCMD[@]}" "${PROTSTARTCMD[@]}")
				else
					FINALSTARTCMD=("${PROTSTARTCMD[@]}")
				fi
			fi

			if [ -n "${GAMESTARTCMD[0]}" ]; then
				if [ -n "${FINALSTARTCMD[0]}" ]; then
					FINALSTARTCMD=("${FINALSTARTCMD[@]}" "${GAMESTARTCMD[@]}")
				else
					FINALSTARTCMD=("${GAMESTARTCMD[@]}")
				fi
			fi

			if [ -n "${GAMEARGSARR[0]}" ]; then
				if [ -n "${FINALSTARTCMD[0]}" ]; then
					FINALSTARTCMD=("${FINALSTARTCMD[@]}" "${GAMEARGSARR[@]}")
				else
					FINALSTARTCMD=("${GAMEARGSARR[@]}")
				fi
			fi
		fi

		if [ "$STARTDEBUG" -eq 1 ]; then
			{
				echo "$(date) - $GN ($AID) - ======================"
				echo "GMR $GMR"
				echo "GSC $GSC"
				echo "SLRCMD '${SLRCMD[*]}'"
				echo "PROTSTARTCMD '${PROTSTARTCMD[*]}'"
				echo "GAMESTARTCMD '${GAMESTARTCMD[*]}'"
				echo "GAMEARGSARR '${GAMEARGSARR[*]}'"
				echo "-----------"
				echo "I '${INGCMD[*]}'"
				echo "X '${FINALSTARTCMD[*]}'"
				echo "$(date) - $GN ($AID) - ======================"
				if [ "$HAVESCTP" -eq 0 ] && [ "$HAVEREAP" -eq 0 ]; then
					echo "$(date) - $GN ($AID) - HAVESCTP='$HAVESCTP', and HAVEREAP='$HAVEREAP' - assuming ${PROGNAME,,} is used as compat tool - using regular base game command to continue"
				elif [ "$HAVESCTP" -eq 0 ] && [ "$HAVEREAP" -eq 1 ]; then
					echo "$(date) - $GN ($AID) - HAVESCTP='$HAVESCTP', but also HAVEREAP='$HAVEREAP' - assuming ${PROGNAME,,} is set to both command and compat tool (or compat tool is empty) - steam doesn't provide STEAM_COMPAT_TOOL_PATHS, so have to cut out the reaper line to continue; HAVESLR='$HAVESLR'"
				elif [ "$HAVESCTP" -eq 1 ] && [ "$HAVEREAP" -eq 1 ]; then
					echo "$(date) - $GN ($AID) - HAVESCTP='$HAVESCTP', and HAVEREAP='$HAVEREAP' - assuming $PROGCMD is used as command line tool - switching proton version is disabled"
				elif [ "$HAVESCTP" -eq 1 ] && [ "$HAVEREAP" -eq 0 ]; then
					echo "$(date) - $GN ($AID) - HAVESCTP='$HAVESCTP', but also HAVEREAP='$HAVEREAP' - something went wrong - never seen this in the wild"
				fi
				echo "$(date) - $GN ($AID) - ======================"
				echo "$(date) - $GN ($AID) - INITIAL LAUNCH COMAND '${INGCMD[*]}'"
				echo "$(date) - $GN ($AID) - HAVESCTP='$HAVESCTP';HAVEREAP='$HAVEREAP';HAVESLR='$HAVESLR';HAVESLRCT='$HAVESLRCT';HAVEINPROTON='$HAVEINPROTON'"
				echo "$(date) - $GN ($AID) - REAPCMD '${REAPCMD[*]}'"
				echo "$(date) - $GN ($AID) - RUNSLR '${RUNSLR[*]}'"
				echo "$(date) - $GN ($AID) - RUNSLRCT '${RUNSLRCT[*]}'"
				echo "$(date) - $GN ($AID) - ISGAME '$ISGAME'"
				echo "$(date) - $GN ($AID) - INPROTCMD '${INPROTCMD[*]}'"
				echo "$(date) - $GN ($AID) - INSTLCMD '${INSTLCMD[*]}'"
				echo "$(date) - $GN ($AID) - ORGGCMD '${ORGGCMD[*]}'"
				echo "$(date) - $GN ($AID) - ORG_STEAM_COMPAT_TOOL_PATHS '${ORG_STEAM_COMPAT_TOOL_PATHS[*]}'"
				echo "$(date) - $GN ($AID) - USEREAP='$USEREAP'; USESLR=$USESLR'"
				echo "$(date) - $GN ($AID) - FINAL LAUNCH RUNCMD '${RUNCMD[*]}'"
				echo "$(date) - $GN ($AID) - FINALSTARTCMD '${FINALSTARTCMD[*]}'"
				echo "$(date) - $GN ($AID) - ======================"
			} >> "$STLSHM/${PROGNAME,,}-${FUNCNAME[0]}-STARTDEBUG.txt"
		fi

		# might be also useful for starting custom commands generally
		if [ "$USECUSTOMCMD" -eq 1 ] && [ "$FORK_CUSTOMCMD" -eq 1 ] && [ "$ONLY_CUSTOMCMD" -eq 0 ] && [ "$WAITFORCUSTOMCMD" -ge 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - WAITFORCUSTOMCMD is enabled, so replacing $WFEAR with 'run' in'${FINALSTARTCMD[*]}'"
			for i in "${!FINALSTARTCMD[@]}"; do
				if [[ ${FINALSTARTCMD[$i]} == "$WFEAR" ]]; then
					FINALSTARTCMD[i]="run"
				fi
			done
		fi

		writelog "INFO" "${FUNCNAME[0]} - Original incoming start command: '${INGCMD[*]}'"
		writelog "INFO" "${FUNCNAME[0]} - Final outgoing start command: '${FINALSTARTCMD[*]}'"

		startGame "${FINALSTARTCMD[@]}"

		writelog "STOP" "######### $PROGNAME $PROGVERS #########"
	fi
}

### CORE LAUNCH END ###

### COMMAND LINE START ###

function CompatTool {

	SCTS="$STEAMCOMPATOOLS/$PROGNAME"

	if [ "$1" == "add" ] ; then
		if [ -d "$SROOT" ]; then
			if [ ! -d "$STEAMCOMPATOOLS" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Initially creating dir '$STEAMCOMPATOOLS'"
				mkProjDir "$STEAMCOMPATOOLS"
				if [ ! -d "$SCTS" ]; then
					writelog "ERROR" "${FUNCNAME[0]} - Failed to create the directory '$STEAMCOMPATOOLS'"
				fi
			fi

			if [ "$ONSTEAMDECK" -eq 1 ]; then
				if [ -n "$2" ]; then
					STLBIN="$2"
				else
					STLBIN="$PREFIX/$PROGCMD"
				fi
			else
				STLBIN="$0"
			fi

			if [ ! -d "$SCTS" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Creating dir '$SCTS'"
				mkProjDir "$SCTS"

				if [ ! -d "$SCTS" ]; then
					writelog "ERROR" "${FUNCNAME[0]} - Failed to create the directory '$SCTS' - check your write priviledges on '$STEAMCOMPATOOLS'"
				fi

				CVDF="$SCTS/$CTVDF"
				writelog "INFO" "${FUNCNAME[0]} - Creating file '$CVDF'"
				{
				echo "\"compatibilitytools\""
				echo "{"
				echo "  \"compat_tools\""
				echo "  {"
				echo "	\"Proton-${SHOSTL}\" // Internal name of this tool"
				echo "	{"
				echo "	  \"install_path\" \".\""
				echo "	  \"display_name\" \"$NICEPROGNAME\""
				echo ""
				echo "	  \"from_oslist\"  \"windows\""
				echo "	  \"to_oslist\"    \"linux\""
				echo "	}"
				echo "  }"
				echo "}"
				} >> "$CVDF"

				if [ ! -f "$CVDF" ]; then
					writelog "ERROR" "${FUNCNAME[0]} - Failed to create the file '$CVDF' - check your write priviledges on '$SCTS'"
				fi

				TVDF="$SCTS/toolmanifest.vdf"
				writelog "INFO" "${FUNCNAME[0]} - Creating file '$TVDF'"
				{
				echo "\"manifest\""
				echo "{"
				echo "  \"commandline\" \"/$PROGCMD run\""
				echo "  \"commandline_$WFEAR\" \"/$PROGCMD $WFEAR\""
				echo "}"
				} >> "$TVDF"

				if [ ! -f "$CVDF" ]; then
					writelog "ERROR" "${FUNCNAME[0]} - Failed to create the file '$TVDF' - check your write priviledges on '$SCTS'"
				fi

				writelog "INFO" "${FUNCNAME[0]} - Creating symlink '$SCTS/$PROGCMD' pointing to '$STLBIN'" "E"
				ln -s "$(realpath "$STLBIN")" "$SCTS/$PROGCMD"

				if [ ! -L "$SCTS/$PROGCMD" ]; then
					writelog "ERROR" "${FUNCNAME[0]} - Failed to create the symlink '$SCTS/$PROGCMD' - check your write priviledges on '$SCTS'"
				fi
			else
				writelog "INFO" "${FUNCNAME[0]} - '$SCTS' already exists - checking if '$PROGCMD' symlink needs to be updated"
				if [ "$(readlink "$SCTS/$PROGCMD")" == "$(realpath "$STLBIN")" ]; then
					writelog "SKIP" "${FUNCNAME[0]} - Nothing to do the '$SCTS/$PROGCMD' symlink still points to '$STLBIN'" "E"
				else
					rm "$SCTS/$PROGCMD"
					ln -s "$(realpath "$STLBIN")" "$SCTS/$PROGCMD"
					writelog "SKIP" "${FUNCNAME[0]} - Updated the '$SCTS/$PROGCMD' symlink to '$STLBIN'" "E"
				fi
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Steam Home Dir '$SROOT' not found!"
		fi
	elif [ "$1" == "del" ]; then
		if [ ! -d "$SCTS" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Selected '$1' but '$SCTS' doesn't exist"
			return
		fi

		rm "$SCTS/$PROGCMD" 2>/dev/null
		# *.vdf is usually 'compatibilitytool.vdf', *.txt is usually 'VERSION.txt' created by ProtonUp-Qt
		find "$SCTS" -maxdepth 1 -type f \( -name "*.vdf" -o -name "*.txt" \) -exec rm {} \;
		rmdir "$SCTS"
		if [ ! -d "$SCTS" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Removed '$SCTS' successfully" "E"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Tried to carefully remove '$SCTS', but it still exists - any files inside '$SCTS'?" "E"
		fi
	else
		if [ ! -d "$SCTS" ]; then
			writelog "INFO" "${FUNCNAME[0]} - '$PROGNAME' is not installed as Steam Compatibility Tool in '$STEAMCOMPATOOLS'" "E"
		else
			writelog "INFO" "${FUNCNAME[0]} - '$PROGNAME' is installed as Steam Compatibility Tool in '$STEAMCOMPATOOLS' and points to '$(readlink "$SCTS/$PROGCMD")'" "E"
		fi
	fi
}

function setRunProtonFromUseProton {
	if [ -n "$USEPROTON" ]; then
		NRUNPROTON="$(getProtPathFromCSV "$USEPROTON")"

		if [ -n "$RUNPROTON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Setting ORUNPROTON to '$RUNPROTON'"
			ORUNPROTON="$RUNPROTON"
		fi

		if [ -n "$ORUNPROTON" ] && [ "$ORUNPROTON" == "$NRUNPROTON" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - RUNPROTON '$RUNPROTON' hasn't changed"
		elif [ -n "$NRUNPROTON" ]; then
			if [ -z "$ORUNPROTON" ] || [ "$ORUNPROTON" == "" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Initially setting RUNPROTON for USEPROTON '$USEPROTON' to '$NRUNPROTON'"
			else
				writelog "INFO" "${FUNCNAME[0]} - Updating RUNPROTON for USEPROTON '$USEPROTON' from '$ORUNPROTON' to '$NRUNPROTON'"
			fi
			RUNPROTON="$NRUNPROTON"
			writelog "INFO" "${FUNCNAME[0]} - Set RUNPROTON to '$RUNPROTON'"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - USEPROTON is empty"
	fi
}

function checkStartMode {
	if [ -n "${ORGGCMD[0]}" ]; then

		writelog "INFO" "${FUNCNAME[0]} - LoadCfg: $STLGAMECFG"
		loadCfg "$STLGAMECFG"

		if [ "$ISGAME" -eq 2 ]; then
			if [ -n "$USEWINE" ] && [ "$USEWINE" -eq 1 ]; then
				writelog "SKIP" "${FUNCNAME[0]} - USEWINE is enabled - skipping this function"
			elif grep -q "USEWINE=\"1\"" "$STLGAMECFG" ; then
				writelog "SKIP" "${FUNCNAME[0]} - USEWINE is enabled in the to-be-loaded gameconfig '$STLGAMECFG' - skipping this function"
				EARLYUSEWINE=1
				# could still be enabled via steamcollections, but this would be an overkill here, as ${FUNCNAME[0]} is non-fatal
			else
				if [ "$HAVEINPROTON" -eq 1 ]; then
					writelog "INFO" "${FUNCNAME[0]} - Game was started via '$SLO' ('$PROGCMD %command%'),"
					writelog "INFO" "${FUNCNAME[0]} - because a Proton path was found in the command line provided by steam"
					writelog "INFO" "${FUNCNAME[0]} - Override Proton is disabled, when using $PROGCMD as '$SLO', so using it as-is: '${INPROTCMD[*]}'"
					writelog "INFO" "${FUNCNAME[0]} - (ignoring USEPROTON '$USEPROTON' from game config)"
					RUNPROTON="${INPROTCMD[*]}"
					writelog "INFO" "${FUNCNAME[0]} - Set RUNPROTON to '$RUNPROTON'"

					USEPROTON="$INPROTV"
					writelog "INFO" "${FUNCNAME[0]} - Set USEPROTON to '$USEPROTON'"
				else
					writelog "INFO" "${FUNCNAME[0]} - Game was started as Steam Compatibility Tool - automatically enabling override Proton,"
					writelog "INFO" "${FUNCNAME[0]} - as proton doesn't appear in the command line here"
					setRunProtonFromUseProton
				fi

				writelog "INFO" "${FUNCNAME[0]} - Continuing with RUNPROTON='$RUNPROTON'"

				if [ -n "$RUNPROTON" ]; then
					CHECKWINE="$(dirname "$RUNPROTON")/$DBW"

					if [ -f "$CHECKWINE" ]; then
						RUNWINE="$CHECKWINE"
						writelog "INFO" "${FUNCNAME[0]} - Set the wine binary for proton in path '$RUNPROTON' to '$RUNWINE'"
					else
						writelog "WARN" "${FUNCNAME[0]} - Couldn't find the wine binary for the proton in path '$RUNPROTON'"
					fi

					PROTONVERSION="$(setProtonPathVersion "$RUNPROTON")"
				fi
			fi
		fi
	fi
}

function getDefaultProton {
	if [ -n "$INPROTV" ]; then
		echo "$INPROTV"
	else
		getNOP "v"
	fi
}

function rmFileIfExists {
	if [ -f "$1" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Removing '$1'"
		rm "$1"
	fi
}

function rmDirIfExists {
	if [ -d "$1" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Removing '$1'"
		rm -rf "$1"
	fi
}

function FUSEID {
	if [ -n "$1" ]; then
		USEID="$1"
	else
		USEID="$AID"
	fi

	if [ "$USEID" == "$PLACEHOLDERAID" ]; then
		if [ -f "$LASTRUN" ]; then
			PREVAID="$(grep "^PREVAID" "$LASTRUN" | cut -d '=' -f2)"
			if [ -n "$PREVAID" ]; then
				USEID="${PREVAID//\"}"
				PREVGAME="$(grep "^PREVGAME" "$LASTRUN" | cut -d '=' -f2)"
				if [ -n "$PREVGAME" ]; then
					GN="${PREVGAME//\"}"
				fi
			fi
		fi
	fi

	rmFileIfExists "$LOGDIR/$USEID.log"
	resetAID "$USEID"
	setGN "$USEID"
}

function howto {
	echo "========================="
	echo "$PROGNAME $PROGVERS"
	echo "========================="
	echo "Usage: $PROGCMD [options]..."
	echo ""
	echo "where options include:"
	echo "  (All <path> args must be absolute paths)"
	echo ""
	echo "    addcustomproton|acp <path>       Adds local custom Proton to"
	echo "                                       the internal list from dialog"
	echo "                                       or directly from <path>"
	echo "    addnonsteamgame|ansg <args>      Add a $NSGA to Steam"
	echo "                                       opens gui without args"
	echo ""
	echo "     Ensure the Steam Client is closed before adding shortcuts."
	echo "     <args>:                          for cli min arg is'-ep'"
	echo "     -an=|--appname=	                 App Name - optional"
	echo "     -ep=|--exepath=                  Full gamepath required"
	echo "     -sd=|--startdir=                 Start Dir - optional"
	echo "     -ip=|--iconpath=                 Icon Path - optional"
	echo "     -lo=|--launchoptions             Game Launch Options - optional"
	echo "     -hd=|--hide=                     Hide Game - optional*"
	echo "     -adc=|--allowdesktopconf=        Allow Desktop Conf - optional"
	echo "     -ao=|--allowoverlay=             Allow Overlay - optional"
	echo "     -vr=|--openvr=                   OpenVR - optional*"
	echo "     -t=|--tags=                      Tags - quoted, comma-separated - optional"
	echo "     -stllo|--stllaunchoption         Use '${PROGCMD} %command%' as Launch Option - optional"
	echo "     -ct=|--compatibilitytool=        Specify the name of the compatibility tool to use - optional"
	echo "                                       Can specify 'default' to use the global Steam Compatibility Tool"
	echo "     -hr=|--hero=                     Hero Art path    - Banner used on the Game Screen (3840x1240 recommended) - optional"
	echo "     -lg=|--logo=                     Logo Art path    - Logo that gets displayed on Game Screen (16:9 recommended) - optional"
	echo "     -ba=|--boxart=                   Box Art path     - Cover art used in the library (600x900 recommended) - optional"
	echo "     -tf=|--tenfoot=                  Tenfoot Art path - Small banner used for recently played game in library (600x350 recommended) - optional"
	echo "     --copy                            Copy art files to Steam Grid folder (default) - optional"
	echo "     --link                            Symlink art files to Steam Grid folder - optional"
	echo "     --move                            Move art files to Steam Grid folder - optional"
	echo "     --auto-artwork                  Use image files in the game EXE directory with names 'hero', 'logo', 'boxart', 'tenfoot' - optional"
	echo "                                       This will respect the copy method flags above."
	echo "                                       Note also that this will return for any files assumed to be images, even if Steam doesn't support the image format."
	echo "     --use-steamgriddb               Enable searching SteamGridDB for game artwork"
	echo "                                      By default, if no other options are provided, it will search SteamGridDB using --appname"
	echo "                                      If -sgid, -sgai, or -sgnm are passed, this option will automatically be enabled"
	echo "     --steamgriddb-game-name=       Custom name to search and download grid artwork on, if no ID is found it will fall back to Steam AppID OR SteamGridDB Game ID"
	echo "                                      If no IDs are provided and no match is found on the given name, no artwork will be downloaded"
	echo "     --steamgriddb-steam-appid=     Steam AppID to search and download grid artwork on, this will be used instead of SteamGridDB Game ID if both are passed"
	echo "     --steamgriddb-game-id=         Game ID from SteamGridDB to search and download grid artwork on"
	echo "                                        This requires a SteamGridDB API key. See the SteamGridDB wiki page for guidance on how to generate and supply it."
	echo "    backup <value>                   Backup found '$STUS' files"
	echo "                                       (for 'SteamAppID' or 'all')"
	echo "    block                            Opens the category Block selection menu"
	echo "    cleardeckdeps                    Remove downloaded Steam Deck dependencies, allowing them to"
	echo "    cleargamegrids <arg>            Remove downloaded game grids based on <arg>, which should be one of the following"
	echo "        <appid>                       Remove artwork for game with specific AppID, ex: 787480"
	echo "        all                           Remove ALL grid artwork by removing entire Steam Grids folder"
	echo "                                      update on next launch (This option is only applicable to SteamOS 3.X)"
	echo "    compat <cmd>                     Will (add|del|get) ${PROGNAME,,} as"
	echo "                                       Steam compatibility tool"
	echo "                                       under 'STEAMCOMPATOOLS'"
	echo "    configdir                        Open the SteamTinkerLaunch config directory with xdg-open"
	echo "    createappinfo|cai <id|i|o>       Create raw appinfo for <id>"
	echo "                                      or for 'installed|i' or 'owned|o' games"
	echo "                                      Append any arg to update the raw file"
	echo "                                      (takes a long time for many games!)"
	echo "    createappinfometa|caim <id|i|o>  Read data from raw appinfo for <id>"
	echo "                                      or for 'installed|i' or 'owned|o' games"
	echo "                                      and write to game metadata."
	echo "                                      Append any arg to update the metadata"
	echo "                                      (takes a long time for many games!)"
	echo "    createcompatdata|ccd <gameid><gui> (Re-)create compatdata for <gameid>"
	echo "                                       open <gui> to configure custom programs"
	echo "    createdesktopicon|cdi <game><id> Create desktop icon for <game>"
	echo "                                       <game> is the gameid or"
	echo "                                       with available meta game title"
	echo "                                       0=only internally"
	echo "                                       1=on the desktop"
	echo "                                       2=for the application menu"
	echo "                                       3=for desktop and application menu"
	echo "    createfirstinstall|cfi <gameid>  Open gui to create a Steam First Time Setup"
	echo "    <a|c|r|file> (see wiki)            file for game <gameid>"
	echo "    ${DPRS,}|dprs                    Prepare/launch ${DPRS}"
	echo "    dlcustomproton|dcp <url|l>       Download/install custom Proton"
	echo "                                       - from filerequester"
	echo "                                       - directly from <url>"
	echo "                                       - the 'latest|l' GE version"
	echo "                                       - 'latestge|lge' for Proton-GE"
	echo "                                       - 'latesttkg|ltkg' for Proton-TKG"
	echo "    dlwine|dw <url or latest|l>      Download/install Wine archive"
	echo "                                       from filerequester or"
	echo "                                       automatically the latest|l version or"
	echo "                                       directly from <url>"
	echo "    editor <value>                   Opens the editor menu"
	echo "                                       (for 'SteamAppID' or 'last')"
	echo "    fav <value>                      Opens the favorite menu"
	echo "                                       (for 'SteamAppID' or 'last')"
	echo "    gamefiles|gf <gameid>            Opens menu for opening <gameid> files"
	echo "    gamescope|gs <gameid>            Gamescope config-menu for <gameid>"
	echo "    (get commands work most reliably for games previously launched with ${PROGNAME,,}:)"
	echo "    getcompatdata|gc <gameid/title>  Print the Game compatdata path"
	echo "    getexe|ge <gameid/title>         Print the Game Exe for <gameid/title>"
	echo "    getgamedir|gg <gameid/title>     Print the Game install directory with game name and AppID"
	echo "                  only                 Only display install directory"
	echo "    getid|gi|gid <gametitle>         Print the SteamAppId for <gametitle>"
	echo "    getsteamgriddbid|sgdbid <title>  Get SteamGridDB Game ID for <title>"
	echo "    getslr <gameid>                  Fetch the required Steam Linux Runtime for <gameid>'s SteamTinkerLaunch Proton version"
	echo "               native                  (optional) get the native Steam Linux Runtime"
	echo "    gettitle|gt <gameid>             Print the Game Title for <gameid>"
	echo "    hedgemodmanager|hmm              HedgeModManager"
	echo "            install|i                  install latest stable HedgeModManager"
	echo "            download|d <channel>       download latest HedgeModManager release (stable or nightly)"
	echo "                                        defaults to stable"
	echo "            start|s <channel>          start HedgeModManager"
	echo "                                        automatically downloads and installs latest stable if <channel>"
	echo "                                        is not provided"
	echo "            url|u <url>              Download a GameBanana mod using MineType URL"
	echo "                                      Note that this will *not* work with regular browser URLs"
	echo "            desktopfile|df           (Re-)create .desktop files for HedgeModManager"
	echo "            list-supported           List games supported by HedgeModManager"
	echo "            list-installed           List installed games with HedgeModManager support"
	echo "            list-owned               List owned games on Steam with HedgeModManager support"
	echo "            resetmime                (Re)set MimeType and application menu entries"
	echo "            uninstall                Remove HedgeModManager downloads folder, desktop files and Wineprefix"
	echo "                                      Note that this will not remove your mods or installed Winetricks"
	echo "    lang=<option>                    Mostly to get translated configs on inital setup."
	echo "                                      <option> can be a language file name without suffix or an path to a valid language file"
    echo "    launcher <args>                  Start the Game Launcher"
	echo "             COLLECTION                Show only installed games from Steam collection COLLECTION"
	echo "             menu                      Open Steam Collection Menu"
	echo "             last                      Open last Game as 'Menu'"
	echo "             auto                      Create/Download data for all installed games first"
	echo "             update                    ReCreate all Collection Menus"
	echo "                                       Can be combined with auto"
	echo "    list <owned|installed|non-steam>   List info on <owned|o,installed|i,non-steam|nsg> games"
	echo "         <id|name|path|count|full>     Optionally specify whether you want to see"
	echo "                                        each game's <id|name|path|count|full>"
	echo "                                       NOTE: Non-Steam Games will NOT be included in <installed,owned>,"
	echo "                                        they will only show up for <non-steam|nsg>"
	echo "    listproton|lp                    List name and path of all Proton versions known to SteamTinkerLaunch"
	echo "         name|n                        Only list the Proton version names"
	echo "         path|p                        Only list paths to the Proton versions"
	echo "    meta                             Generates/Updates metadata"
	echo "                                       for all installed games"
	echo "    mo2                              Mod Organizer 2"
	echo "         create-instance|ci <id>     create MO2 instance for <id>"
	echo "         download|d                  download latest MO2 release"
	echo "         getprefix|gp                Output path to MO2 prefix"
	echo "         install|i                   install MO2 (includes download)"
	echo "          <path>                      optional path for custom MO2 installer executable"
	echo "         list-supported|ls           list games supported by MO2"
	echo "         list-installed|li           list installed games supported by MO2"
	echo "         resetmime                   (Re)set MimeType and application menu entries"
	echo "         start|s                     start MO2 (includes download+install)"
	echo "         url|u <url>                 open nxm url with MO2"
	echo "         winecfg                     Run Winecfg with MO2 Wine in MO2 prefix"
	echo "         winetricks|wt               Run Winetricks with MO2 Wine in MO2 prefix"
	echo "    opengridfolder|ogf               Open the Steam Grid folder where custom game artwork is stored using xdg-open"
	echo "    openissue|oi                     Opens issue tracker on GitHub in default browser"
	echo "    onetimerun|otr <gameid>          Opens One-Time Run menu for running a one-off executable in <gameid>'s Wineprefix"
	echo "                                       Optionally takes command-line parameters to run without the GUI. Note that only"
	echo "                                       the path to the one-time exe is required, STL will try to infer the other options"
	echo "                                       such as Proton version where possible."
	echo "         <args>:"
	echo "         --exe=                        Absolute path to the One-Time executable (can be Windows or Linux executable)"
	echo "         --proton=                     Name of the Proton version to use (see steamtinkerlaunch lp). Defaults to Per-Game Proton version ('USEPROTON'), ignored if not Windows executable"
	echo "         --workingdir=                 Working directory to launch the executable from"
	echo "         --useexedir                   Use the same directory as the executable as the working directory (overrides --workingdir)"
	echo "         --args=                       Arguments to append to the end of the executable launch"
	echo "         --forceproton                 Use Proton to run executable even if SteamTinkerLaunch detects doesn't detect it as a Windows executable"
	echo "         --useslr                      Use the Steam Linux Runtime to run the application, if available (uses SLR specified by Proton version or native Linux Steam Linux Runtime)"
	echo "         --save                        Save given configuration for use in the One-Time Run GUI for this game in future"
	echo "         --default                     Restore all saved One-Time Run values to default"
	echo "    play <gameid>                    Start game with id <gameid> directly"
 	echo "    proton|p <title> <X>             Start and/or create <title> with proton"
	echo "                                       without using steam."
	echo "                                       Optional <X> opens gui"
 	echo "    proton|p list                    Update the list of all available proton versions"
	echo "    set <var> <for> <value>          Change configuration settings, either per-game (<AppID>), for all games (<all>),"
	echo "                                      or in the Global config (<global>)"
	echo "                                       Example:"
	echo "      set RUN_REPLAY all 1               Set RUN_REPLAY for all games to 1"
	echo "      set STLEDITOR global \"/path\"     Set the path to the STLEDITOR binary in the global config"
	echo "    setgameart|sga <gameid> <args>   Set the artwork for various games by their AppID using absolute paths"
	echo "                                      Passing no args will open a GUI"
	echo "                                      By default, given artwork will be copied to the Steam Grid folder"
	echo "                                      Note that only ONE of the copy/link/move options should be used"
	echo "      <args>:                           "
	echo "      -hr=|--hero=                     Hero Art path    - Banner used on the Game Screen (3840x1240 recommended)"
	echo "      -lg=|--logo=                     Logo Art path    - Logo that gets displayed on Game Screen (16:9 recommended)"
	echo "      -ba=|--boxart=                   Box Art path     - Cover art used in the library (600x900 recommended)"
	echo "      -tf=|--tenfoot=                  Tenfoot Art path - Small banner used for recently played game in library (600x350 recommended)"
	echo "      --copy                            Copy art files to Steam Grid folder"
	echo "      --link                            Symlink art files to Steam Grid folder"
	echo "      --move                            Move art files to Steam Grid folder"
	echo "    settings <value>                 Opens the $SETMENU"
	echo "    src                              Shortcut '$STERECO'"
	echo "    steamdeckcompat <gameid>         Get information about Steam Deck compatibility for <gameid>"
	echo "                                       Will work offline if the information has been fetched before"
	echo "                                       (for 'SteamAppID' or 'last')"
	echo "    steamgriddb|sgdb <appid/args>    Download most relevant artwork from SteamGridDB based on Steam AppID or SteamGridDB Game ID"
	echo "                                      Attempts to search for and download hero, logo, and boxart from SteamGridDB, and can optionally"
	echo "                                      apply this artwork to the game in your Steam library, by giving it the AppID of the game to use"
	echo "                                      in the filename."
	echo "                                       This setting uses preferences specified on Global Menu under SteamGridDB options, such as"
	echo "                                       dimensions, and how to manage existing files (though an override can be specified for how"
	echo "                                       to manage existing artwork files)."
	echo "     Pass an AppID instead of the below args to open a GUI for selecting game artwork."
	echo "      <args>:"
	echo "      --search-id=                     Steam AppID or SteamGridDB Game ID to search for (ex: AppID 22330 or Game ID 5258102)"
	echo "      --search-name=                   Search SteamGridDB with a name and return the best-match Game ID, and use --nonsteam automatically."
	echo "                                        You must pass '--filename-appid' when using this option, otherwise artwork will use the wrong ID"
	echo "      --filename-appid=                Steam AppID to use when naming the file, only required when searching on Game ID or Game Name so the artwork is applied to the correct Shortcut"
	echo "      --steam                          The Search ID passed is for a Steam AppID (default)"
	echo "      --nonsteam                       The Search ID passed is for a SteamGridDB Game ID"
	echo "      --apply                          Apply the downloaded game artwork to the Steam game (if not set, artwork will be downloaded to SteamTinkerLaunch Grid directory only)"
	echo "      --no-apply                       Don't apply the downloaded game artwork (artwork will be downloaded to SteamTinkerLaunch Grid directory only)"
	echo "      --skip-existing                  Skip setting artwork for this game if it already has custom artwork"
	echo "      --replace-existing               Replace existing artwork files for this game -- they cannot be recovered later"
	echo "      --backup-existing                Backup existing artwork files for this game before replacing them, so they can be restored"
	echo ""
	echo "    steamworksshared|sws <opts>      Steamworks Shared:"
	echo "      <opts>:                          Options:"
	echo "      <l>                              List packages - or"
	echo "      <i>                              Install package with options:"
	echo "      <packagename>                    - packagename"
	echo "      <SteamAppID or pfx>              - SteamAppID or pfx path"
	echo "      <wine binary>                    - Optional wine binary path"
	echo "    update <value>                   Updates <value>:"
	echo "           gamedata <SteamAppID>       Updates missing desktopfiles"
	echo "                                       and pictures of installed games"
	echo "                                       or only from <SteamAppID>"
	echo "           grid <SteamAppID>         Update Steam Grid for installed game(s)"
	echo "                                       optional argument either a SteamAppID,"
	echo "                                       'owned', 'installed', or 'nonsteam|shortcut'"
	echo "                                       (default is 'installed')"
	echo "           allgamedata                 The same as above for"
	echo "                                       all games in $SCV"
	echo "           shaders <shadername>        all enabled shaders or <shadername>"
	echo "                                       'list' to list shaders"
	echo "                                       'repos' to update used shader repos"
	echo "           gameshaders <opt1> <opt2|3> open shader selection for dir <opt1>,"
	echo "                                       for last gamedir when <opt1> is empty"
	echo "                                       enable repo <opt1> for dir <opt2>"
	echo "                                        with empty <opt3> or <opt3>=enable"
	echo "                                       disable repo <opt1> for dir <opt2>"
	echo "                                        with  <opt3>=disable"
	echo "                                       block repo <opt1> with <opt2>=block"
	echo "                                       unblock repo <opt1> with <opt2>=unblock"
	echo "           reshade <version>           (re)-download ReShade (version)"
	echo "    version                          Output the program version"
	echo "    ${VTX} <value>                   ${VTX^} commandline options"
	echo "                                      ${VTX} download, install, and start can optionally take"
	echo "                                       a version tag to download."
	echo "                                      Ex: steamtinkerlaunch vortex start v1.8.0"
	echo "           getprefix|gp                Output path to Vortex prefix"
	echo "           install                     Installs ${VTX^}"
	echo "           list-supported|ls           List all ${VTX^}-supported Steam Game IDs"
	echo "                                       (offline - ${VTX^} needs to be installed)"
	echo "           list-online|lo              List ${VTX^}-supported games found online"
	echo "           list-owned|low              List owned games with ${VTX^} support"
	echo "           list-installed|li           List installed Games with ${VTX^} support"
	echo "           games                       Gui to en/disable ${VTX^} for supported game"
	echo "           symlinks                    Gui showing ${VTX^} symlinks"
	echo "           start                       Starts ${VTX^}"
	echo "           url|u <url>                 Open nxm url with ${VTX^}"
	echo "           getset                      Show config of installed ${VTX^}"
	echo "           reset                       Reset all autodetected settings in ${VTX^}"
	echo "           stage <path>                Add ${VTX^} stage via dialog"
    echo "                                        or directly the one given in <path>"
	echo "           resetmime                   (Re)set MimeType and application menu entries"
	echo "           winecfg                     Run Winecfg with ${VTX^} Wine in ${VTX^} prefix"
	echo "           winetricks|wt               Run Winetricks with ${VTX^} Wine in ${VTX^} prefix"
	echo "    vr <windowname> <SteamAppID> <s> Start SBS-VR mode for <windowname>"
	echo "                                       and game <SteamAppID>"
	echo "                                       's'ave windowname for the game"
	echo "    waitrequester|wr e|s|u           <e>nable the waitrequester for the launched game"
	echo "                                       (no effect if already enabled)"
	echo "                                     <s>kip the waitrequester temporarily"
	echo "                                     <u>nskip the temporary waitrequester skip"
	echo "    wiki <page>                      Opens the wiki <page> in yad"
	echo "    yad <yad binary> <opt>           Configure the used Yad binary"
	echo "                                       <yad binary> can be either"
	echo "                                       <absolute/path>"
	echo "                                       <conty>"
	echo "                                       <ai> or <appimage> plus optional <opt>:"
	echo "                                       <opt> can be either"
	echo "                                         <absolute/path/to/a/yad/appimage>"
	echo "                                         <http/s/yad/appimage/download/url>"
	echo "                                       (see Wiki for details)"
}

#STARTCMDLINE
function commandline {
	if [ "$1" == "addcustomproton" ] || [ "$1" == "acp" ]; then
		addCustomProton "$2" "$3"
	elif [ "$1" == "addnonsteamgame" ] || [ "$1" == "ansg" ] ; then
		if [ -z "$2" ]; then
			addNonSteamGameGui "$NON"
		else
			if grep -q "ep=\|--exepath=" <<< "$*"; then
				if grep -q "gui" <<< "$*"; then
					addNonSteamGameGui "$@"
				else
					addNonSteamGame "$@"
				fi
			else
				writelog "INFO" "${FUNCNAME[0]} - Command line parameters insufficent - starting the Gui"
				addNonSteamGameGui "$NON"
			fi
		fi
	elif [ "$1" == "backup" ]; then
		if [ "$2" == "all" ]; then
			backupSteamUserGate "$2"
		else
			FUSEID "$2"
			backupSteamUserGate "$USEID"
		fi
	elif [ "$1" == "block" ]; then
		FUSEID "$2"
		setGuiBlockSelection "$USEID"
	elif [ "$1" == "cleardeckdeps" ]; then
		# We check this in clearDeckDeps too, but thsis is just insurance
		if [ "$ONSTEAMDECK" -eq 1 ]; then
			clearDeckDeps
		else
			writelog "SKIP" "${FUNCNAME[0]} - Not on Steam Deck, nothing to do."
			echo "Not on Steam Deck, nothing to do."
		fi
	elif [ "$1" == "sort" ]; then
		setGuiSortOrder
	elif [ "$1" == "compat" ]; then
		if [ -n "$2" ]; then
			if [ "$2" == "add" ] || [ "$2" == "del" ] || [ "$2" == "get" ]; then
				CompatTool "$2"
			else
				howto
			fi
		else
			CompatTool "get"
		fi
	elif [ "$1" == "conty" ]; then
		if [ -n "$2" ]; then
			if [ "$2" == "up" ] || [ "$2" == "update" ]; then
				updateConty
			fi
		fi
	elif [ "$1" == "createcompatdata" ] || [ "$1" == "ccd" ]; then
		FUSEID "$2"
		writelog "INFO" "${FUNCNAME[0]} - Starting Install via command: reCreateCompatdata \"$1\" \"$USEID\" \"$3\""
		reCreateCompatdata "$1" "$USEID" "$3"
	elif [ "$1" == "createdesktopicon" ] || [ "$1" == "cdi" ]; then
		if [ "$2" == "all" ]; then
			if [ "$(listInstalledGameIDs | wc -l)" -eq 0 ]; then
				writelog "SKIP" "${FUNCNAME[0]} - No installed games found!" "E"
			else
				while read -r CATAID; do
					createDesktopIconFile "$CATAID" "$3"
				done <<< "$(listInstalledGameIDs)"
			fi
		else
			FUSEID "$2"
			createDesktopIconFile "$USEID" "$3"
		fi
	elif [ "$1" == "createappinfo" ] || [ "$1" == "cai" ]; then
		if [ "$2" == "installed" ] || [ "$2" == "i" ]; then
			if [ "$(listInstalledGameIDs | wc -l)" -eq 0 ]; then
				writelog "SKIP" "${FUNCNAME[0]} - No installed games found!" "E"
			else
				while read -r CATAID; do
					getRawAppIDInfo "$CATAID" "$3"
				done <<< "$(listInstalledGameIDs)"
			fi
		elif [ "$2" == "owned" ] || [ "$2" == "o" ]; then
			while read -r CATAID; do
				getRawAppIDInfo "$CATAID" "$3"
			done <<< "$(getOwnedAids)"
		else
			FUSEID "$2"
			getRawAppIDInfo "$USEID" "$3"
		fi
	elif [ "$1" == "createappinfometa" ] || [ "$1" == "caim" ]; then
		if [ -n "$2" ]; then
			if [ "$2" == "installed" ] || [ "$2" == "i" ]; then
				if [ "$(listInstalledGameIDs | wc -l)" -eq 0 ]; then
					writelog "SKIP" "${FUNCNAME[0]} - No installed games found!" "E"
				else
					while read -r CATAID; do
						writeAllAIMeta "$CATAID" "$3"
					done <<< "$(listInstalledGameIDs)"
				fi
			elif [ "$2" == "owned" ] || [ "$2" == "o" ]; then
				while read -r CATAID; do
					writeAllAIMeta "$CATAID" "$3"
				done <<< "$(getOwnedAids)"
			else
				if [ "$2" -eq "$2" ]; then
					FUSEID "$2"
					writeAllAIMeta "$USEID" "$3"
				fi
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - need at least 'installed|i' or 'owned|o' or a SteamAppId as arg2" "E"
		fi
	elif [ "$1" == "createfirstinstall" ] || [ "$1" == "cfi" ]; then
		FUSEID "$2"
		CreateCustomEvaluatorScript "$USEID"
	elif [ "$1" == "configdir" ]; then
		"$XDGO" "$STLCFGDIR"
	elif [ "$1" == "${DPRS,}" ] || [ "$1" == "dprs" ]; then
		startDepressurizer
	elif [ "$1" == "dlcustomproton" ] || [ "$1" == "dcp" ]; then
		dlCustomProtonGate "$2"
	elif [ "$1" == "dlwine" ] || [ "$1" == "dw" ]; then
		dlWineGate "$2"
	elif [ "$1" == "dxvkhud" ] || [ "$1" == "dxh" ]; then
		FUSEID "$2"
		DxvkHudPick "$USEID"
	elif [ "$1" == "listproton" ] || [ "$1" == "lp" ]; then
		prettyPrintProtonArr "$2"
	elif [ "$1" == "dotnet" ]; then
		if [ -n "$3" ]; then
			installDotNet "$2" "$3" "$4"
		else
			writelog "INFO" "${FUNCNAME[0]} - need at least a winepfx as arg2 '$2' and a wine binary as arg3" "E"
			#howto
		fi
	elif [ "$1" == "editor" ]; then
		FUSEID "$2"
		EditorDialog "$USEID"
	elif [ "$1" == "fav" ]; then
		FUSEID "$2"
		if [ -n "$3" ] && [ "$3" == "set" ]; then
			setGuiFavoritesSelection "$USEID"
		else
			openTrayIcon
			favoritesMenu "$USEID"
			cleanYadLeftOvers
		fi
	elif [ "$1" == "gamefiles" ] || [ "$1" == "gf" ]; then
		FUSEID "$2"
		GameFilesMenu "$USEID"
	elif [ "$1" == "gamescope" ] || [ "$1" == "gs" ]; then
		FUSEID "$2"
		GameScopeGui "$USEID" "$3"
	elif [ "$1" == "getexe" ] || [ "$1" == "ge" ]; then
		getGameExe "$2" "1"
	elif [ "$1" == "getid" ] || [ "$1" == "gi" ] || [ "$1" == "gid" ]; then
		getIDFromTitle "$2" "1"
	elif [ "$1" == "gettitle" ] || [ "$1" == "gt" ]; then
		getTitleFromID "$2" "1"
	elif [ "$1" == "getcompatdata" ] || [ "$1" == "gc" ]; then
		getCompatData "$2" "1"
	elif [ "$1" == "getgamedir" ] || [ "$1" == "gg" ]; then
		if [ "$3" == "only" ]; then
			getGameDir "$2" "X" "1"
		else
			getGameDir "$2" "" "1"
		fi
	elif [ "$1" == "help" ] || [ "$1" == "--help" ] || [ "$1" == "-h" ]; then
		howto
	elif [ "$1" == "helpurl" ] || [ "$1" == "hu" ]; then
		FUSEID "$2"
		HelpUrlMenu "$USEID"
		if [ -z "$3" ]; then
			rm "$STLSHM/KillBrowser-$AID.txt" 2>/dev/null
		fi
	elif [ "$1" == "launcher" ]; then
		openGameLauncher "$2" "$3"
	elif [ "$1" == "list" ]; then
		if [ -z "$2" ]; then
			echo "invalid usage - must pass one additional argument to 'list' command, either 'owned' or 'installed'"
		else
			listSteamGames "$2" "$3" # 3rd parameter is optional
		fi
	elif [ "$1" == "meta" ]; then
		createMetaData "yes"
	elif [ "$1" == "getslr" ]; then
		if [ "$3" == "native" ]; then
			FETCHNATIVESLR=1
		else
			FETCHNATIVESLR=0
		fi
		commandlineFetchGameSLR "$2" "$FETCHNATIVESLR"
	elif [ "$1" == "getslrbtn" ]; then  # Internal use only for the Main Menu button
		fetchGameSLRGui "$2"
	elif [ "$1" == "debug" ]; then
		## Why are you looking here? :-)
		DEBUGNOSTAID="-222353304"

		### This part of debug is for testing updating the JSON blob in the localconfig VDF
		### that holds information about which Collections a non-steam game is using
		###
		### It may also be some now-outdated testing for localconfig.vdf parsing

		# # Don't let the user run the internal debug command
		writelog "WARN" "${FUNCNAME[0]} - No debug for you!"
		echo "No debug for you!"
		return

		# DEBUGNOSTAID="-222353304"

		DEBUGTESTT="$( findSteamShortcutByAppID "3581081989" )"
		parseSteamShortcutEntryAppID "$DEBUGTESTT"

		# editSteamShortcutEntry "3666773025" "appname" "New Name 2"

		# return

		# DEBUG_LOCOVDF="$STUIDPATH/config/localconfig bsak.vdf"

		## Get nested VDF section
		# getNestedVdfSection "Valve/Steam/Apps/7/cloud" "2" "$DEBUG_LOCOVDF"

		## -----
		## Mark Non-Steam Game game with given AppID as 'hidden'
		## Could be extended to add to categories once we can get the category
		NOUSCOEXISTS=0
		DEBUG_LOCOVDF="$STUIDPATH/config/localconfig bsak.vdf"

		LOCOWESTO="$( getVdfSection "WebStorage" "" "" "$DEBUG_LOCOVDF" )"
		LOCOUSCO="$( getVdfSectionValue "$LOCOWESTO" "user-collections" "1" )"

		if [ -z "$LOCOUSCO" ]; then
			echo "No user-collections information defined, creating new one"
			# shellcheck disable=SC2034
			NOUSCOEXISTS=1  # debug var
			LOCOUSCO="\"{}\""
		fi

		LOCOUSCO="$( echo "$LOCOUSCO" | jq 'fromjson' )"

		## Insert Non-Steam Game into user-collection 'hidden' category, creating it if it doesn't exist
		if ! jq -e '. | try(.hidden)' <<< "$LOCOUSCO" >/dev/null ; then
			echo "No hidden games, adding blank hidden category"
			LOCOUSCO="$( jq '. += { hidden: { id: "hidden", added: [], removed: [] } }' <<< "$LOCOUSCO" )"
		fi

		LOCOUSCO="$( jq '.hidden.added += [ 1234567890 ]' <<< "$LOCOUSCO" )"

		if [ "$NOUSCOEXISTS" -eq 1 ]; then
			echo "Adding new section into VDF"
			addVdfSectionValue "$LOCOWESTO" "user-collections" "$LOCOUSCO" "$DEBUG_LOCOVDF"
		else
			echo "Editing existing VDF value with '$LOCOUSCO'"
			editVdfSectionValue "$LOCOWESTO" "user-collections" "$LOCOUSCO" "$DEBUG_LOCOVDF"
		fi

		addVdfSectionValue "$LOCOWESTO" "test-val" "testvall" "$DEBUG_LOCOVDF"

		## -----

		## Update OverlayAppEnable for given shortcut in localconfig.vdf
		SHORTCUTLOCALCONFIGVDFSECTION="$( getNestedVdfSection "Apps/${DEBUGNOSTAID}" "1" "$DEBUG_LOCOVDF" )"
		editVdfSectionValue "$SHORTCUTLOCALCONFIGVDFSECTION" "OverlayAppEnable" "0" "$DEBUG_LOCOVDF"
		# getVdfSectionValue "$SHORTCUTLOCALCONFIGVDFSECTION" "OverlayAppEnable"
	elif [ "$1" == "mo2" ]; then
		if [ -n "$2" ]; then
			if [ "$2" == "download" ] || [ "$2" == "d" ]; then
				dlLatestMO2
			elif [ "$2" == "install" ] || [ "$2" == "i" ]; then
				# Get path to custom MO2 exe from commandline -- Untested for now
				if [ -n "$3" ]; then
					USEMO2CUSTOMINSTALLER=1
					MO2CUSTOMINSTALLER="$3"
				fi

				StatusWindow "$(strFix "$NOTY_INSTSTART" "$MO")" "installMO2" "InstallMO2Status"
			elif [ "$2" == "start" ] || [ "$2" == "s" ]; then
				startMO2
			elif [ "$2" == "list-supported" ] || [ "$2" == "ls" ]; then
				listMO2Games
			elif [ "$2" == "list-installed" ] || [ "$2" == "li" ]; then
				# Output installed MO2 games in format "Name (AppID) -> /path/to/prefix"
				setMO2Vars
				mapfile -t -O "${#CMDINSTMO2GAMS}" CMDINSTMO2GAMS <<< "$( prepAllMO2Games "li" )"
				for IMO2G in "${CMDINSTMO2GAMS[@]}"; do
					IMO2GAID="$( echo "$IMO2G" | cut -d ";" -f 1 )"
					IMO2GN="$( echo "$IMO2G" | cut -d ";" -f 2 )"
					IMO2GPA="$( echo "$IMO2G" | cut -d ";" -f 3 )"

					printf "%s (%s) -> %s\n" "$IMO2GN" "$IMO2GAID" "$IMO2GPA"
				done
			elif [ "$2" == "create-instance" ] || [ "$2" == "ci" ]; then
				if [ -z "$3" ] || [ "$3" == "all" ]; then
					createAllMO2Instances
				else
					manageMO2GInstance "$3"
				fi
			elif [ "$2" == "url" ] || [ "$2" == "u" ]; then
				if [ -z "$3" ]; then
					writelog "ERROR" "${FUNCNAME[0]} - No URL was passed for ('$3') -- Maybe this is being incorrectly launched from the XDG menu?"
					warnInvalidModToolLaunch "ModOrganizer 2"
				else
					writelog "INFO" "${FUNCNAME[0]} - URL passed is '$3'"
					dlMod2nexurl "$3"
				fi
			elif [ "$2" == "getprefix" ] || [ "$2" == "gp" ]; then
				echo "$MO2COMPDATA/pfx"
			elif [ "$2" == "repairpfx" ]; then
				setMO2Vars

				# EXPERIMENTAL REPAIR OPTION
				writelog "INFO" "${FUNCNAME[0]} - Attempting to repair ModOrganizer 2 prefix using experimental repair option"
				writelog "INFO" "${FUNCNAME[0]} - This runs a basic Proton command in the game prefix in an attempt to repair/update corrupted/outdated files in the prefix"
				writelog "INFO" "${FUNCNAME[0]} - Attempting to repair ModOrganizer 2 prefix with 'STEAM_COMPAT_DATA_PATH=\"$MO2COMPDATA\" \"$MO2RUNPROT\" run wine'"

				echo "WARNING: This option is experimental and intended for use to fix kernel32.dll-related errors, here be dragons!"
				echo "Attempting to repair ModOrganizer 2 prefix with 'STEAM_COMPAT_DATA_PATH=\"$MO2COMPDATA\" \"$MO2RUNPROT\" run wine'"
				STEAM_COMPAT_DATA_PATH="$MO2COMPDATA" "$MO2RUNPROT" run wine
				writelog "INFO" "${FUNCNAME[0]} - Finished attempting to repair ModOrganizer 2 prefix"
				echo "Done. If there are any errors above, repair probably didn't succeed. If there are no errors and the prefix is still broken, try running something with Proton in the prefix using something similar to the command above."
			elif [ "$2" == "winecfg" ]; then
				mo2Winecfg
			elif [ "$2" == "winetricks" ] || [ "$2" == "wt" ]; then
				mo2Winetricks
			elif [ "$2" == "resetmime" ]; then
				writelog "INFO" "${FUNCMAME[0]} - (Re)setting MO2 .desktop file entries and MimeType associations"
				echo "(Re)setting MO2 .desktop file entries and MimeType associations"
				setMO2DLMime
			else
				writelog "INFO" "${FUNCNAME[0]} - arg2 '$2' is no valid command"
				howto
			fi
		else
			echo "need arg2"
			howto
		fi
	elif [ "$1" == "hedgemodmanager" ] || [ "$1" == "hmm" ]; then
		if [ -n "$2" ]; then
			if [ -n "$3" ]; then
				CMDHMMDLVER="$3"
			fi

			if [ "$2" == "download" ] || [ "$2" == "d" ]; then
				dlLatestHMM "$CMDHMMDLVER"
			elif [ "$2" == "install" ] || [ "$2" == "i" ]; then
				dlLatestHMM "$CMDHMMDLVER"
				installHMM
			elif [ "$2" == "start" ] || [ "$2" == "s" ]; then
				if [ "$4" == "--force" ] || [ "$4" == "-f" ]; then
					# This will force dotnet48 to be reinstalled for each installed 64bit HMM game if `--force` or `-f` is passed to `steamtinkerlaunch hmm start`
					startHMM "$CMDHMMDLVER" "X"
				else
					startHMM "$CMDHMMDLVER"
				fi
			elif [ "$2" == "list-supported" ] || [ "$2" == "ls" ]; then
				listSupportedHMMGames
			elif [ "$2" == "list-installed" ] || [ "$2" == "li" ]; then
				listInstalledHMMGames
			elif [ "$2" == "list-owned" ] || [ "$2" == "lo" ]; then
				listOwnedHMMGames
			elif [ "$2" == "desktopfile" ] || [ "$2" == "df" ]; then
				createHMMDesktopFile
			elif [ "$2" == "uninstall" ]; then
				uninstallHMM
			elif [ "$2" == "url" ] || [ "$2" == "u" ]; then
				if [ -z "$3" ]; then
					writelog "ERROR" "${FUNCNAME[0]} - No URL was passed for ('$3') -- Maybe this is being incorrectly launched from the XDG menu?"
					warnInvalidModToolLaunch "Hedge Mod Manager"
				else
					dlHedgeMod "$3"
				fi
			elif [ "$2" == "resetmime" ]; then
				writelog "INFO" "${FUNCMAME[0]} - (Re)setting HMM .desktop file entries and MimeType associations"
				echo "(Re)setting HMM .desktop file entries and MimeType associations"
				createHMMDesktopFile
			else
				echo "Need to input a valid arg2 '$2' to run a HedgeModManager command"
				howto
			fi
		else
			writelog "INFO" "arg2 '$2' is no valid command"
			howto
		fi
	elif [ "$1" == "opengridfolder" ] || [ "$1" == "ogf" ]; then
		openSteamGridDir
	elif [ "$1" == "setgameart" ] || [ "$1" == "sga" ]; then
		if [ -n "$2" ]; then
			if [ -z "$3" ]; then
				setGameArtGui "$2"  # Don't think this function needs to take any arguments
			else
				setGameArt "${@:2}"  # Pass all arguments except the first which is the command name e.g. `setgameart`
			fi
		else
			echo "At least one argument (AppID) must be provided"
		fi
	elif [ "$1" == "noty" ]; then
		if [ -n "$2" ]; then
			NTEXT="$2"
		else
			NTEXT="notifier test"
		fi
		notiShow "$NTEXT"
	elif [ "$1" == "onetimerun" ] || [ "$1" == "otr" ]; then
		FUSEID "$2"
		if [ -z "$2" ]; then
			writelog "WARN" "${FUNCNAME[0]} - No AppID provided for One-Time Run, attempting to start with last known AppID '$USEID'"
			if [ -n "$USEID" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Found AppID '$USEID' - Using this for One-Time Run"
				OneTimeRunGui "$USEID"
			else
				writelog "ERROR" "${FUNCNAME[0]} - Could not find last known AppID - Aborting One-Time Run"
			fi
		else
			if [ -z "$3" ]; then
				OneTimeRunGui "$USEID"
			else
				commandlineOneTimeRun "${@:2}"
			fi
		fi
	elif [ "$1" == "pdb" ]; then
		getProtonDBRating "$2"
	elif [ "$1" == "steamdeckcompat" ] || [ "$1" == "sdc" ]; then
		mapfile -d ";" -t -O "${#DECKCOMPATARR[@]}" DECKCOMPATARR <<< "$( getSteamDeckCompatInfo "$( echo "$2" | xargs )" )"
		unset "DECKCOMPATARR[-1]"

		if [ "${#DECKCOMPATARR[@]}" -eq "0" ]; then
			echo "Could not get Steam Deck compatibility information got AppID '$2' - Is this definitely correct?"
			echo "You can check get the AppID for a game by running 'steamtinkerlaunch getid <name>'"
		else
			echo "Valve's testing indicates that this game is ${DECKCOMPATARR[0]} on Steam Deck"
			if ! [ "${#DECKCOMPATARR[@]}" -eq "1" ]; then  # Only take a newline if there is compatibility information to show
				echo ""
				for COMPATSTR in "${DECKCOMPATARR[@]:1}"; do
					echo "$COMPATSTR"
				done
			fi
		fi
	elif [ "$1" == "pickwin" ] || [ "$1" == "pw" ]; then
		pickGameWindowNameMeta "$2" "$3"
	elif [ "$1" == "play" ]; then
		if [ -z "$2" ]; then
			writelog "INFO" "${FUNCNAME[0]} - need at a valid game name or SteamAppId of an installed game or an absolute path to a game exe as arg2 '$2' or 'gui' for a menu" "E"
		else
			if [ "$2" == "gui" ]; then
				standaloneLaunch
			elif [ "$2" == "ed" ]; then
				standaloneEd "${@:3}"
			elif [ "$2" == "list" ]; then
				standaloneGames l
			else
				initPlay "${@:2}"
			fi
		fi
	elif [ "$1" == "proton" ] || [ "$1" == "p" ]; then
		if [ -n "$2" ] && [ "$2" == "list" ]; then
			getAvailableProtonVersions "up" X
		else
			StandaloneProtonGame "$2" "$3"
		fi
	elif [ "$1" == "set" ]; then
		if [ -n "$2" ]; then
				if [ "$3" == "global" ]; then
					ENTLIST="$(sed -n "/#STARTsaveCfgglobal/,/ENDsaveCfgglobal/p;/#ENDsaveCfgglobal/q" "$0" | grep "echo" | grep "=" | cut -d '"' -f2 | cut -d '=' -f1 | sed 's/^#//')"
				else
					ENTLIST="$(sed -n "/#STARTsaveCfgdefault_template/,/#ENDsaveCfgdefault_template/p;/#ENDsaveCfgdefault_template/q" "$0" | grep "echo" | grep "=" | cut -d '"' -f2 | cut -d '=' -f1 | sed 's/^#//')"
				fi

				if ! grep "$2" <<< "$ENTLIST" >/dev/null; then
					writelog "INFO" "${FUNCNAME[0]} - '$2' is no valid entry - valid are:" "E"
					writelog "INFO" "${FUNCNAME[0]} ------------------------" "E"
					writelog "INFO" "${FUNCNAME[0]} - $ENTLIST" "E"
					writelog "INFO" "${FUNCNAME[0]} ------------------------" "E"
					exit
				fi
			if [ -n "$3" ]; then
				if [ -z "$4" ]; then
					writelog "INFO" "${FUNCNAME[0]} - argument 4 is missing - exit" "E"
					exit
				else
					if [ "$3" == "all" ]; then
						writelog "INFO" "${FUNCNAME[0]} - arg3 is all - updating all config files in '$STLGAMEDIRID':" "E"
						while read -r file; do
							writelog "INFO" "${FUNCNAME[0]} - updating entry '$2' to value '$4' in config $file" "E"
							touch "$FUPDATE"
							updateConfigEntry "$2" "$4" "$file"
						done <<< "$(find "$STLGAMEDIRID" -name "*.conf")"
					else
						if [ -f "$STLGAMEDIRID/$3.conf" ]; then
							writelog "INFO" "${FUNCNAME[0]} - updating entry '$2' to value '$4' in config '$STLGAMEDIRID/$3.conf'" "E"
							touch "$FUPDATE"
							updateConfigEntry "$2" "$4" "$STLGAMEDIRID/$3.conf"
						elif [ "$3" == "global" ] && [ -f "$STLDEFGLOBALCFG" ]; then
							writelog "INFO" "${FUNCNAME[0]} - update global config entry '$2' to value '$3' in global config file '$STLDEFGLOBALCFG'" "E"
							touch "$FUPDATE"
							updateConfigEntry "$2" "$4" "$STLDEFGLOBALCFG"
						else
							writelog "INFO" "${FUNCNAME[0]} - config file '$STLGAMEDIRID/$3.conf' does not exist - nothing to do - exit" "E"
							exit
						fi
					fi
				fi
			else
				writelog "INFO" "${FUNCNAME[0]} - arg3 is missing, you need to provide either the SteamAppId of the game or 'all' to batch update all game configs with the chosen entry!" "E"
				exit
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - arg2 is missing, you need to provide a valid config entry which should be updated!" "E"
			exit
		fi
	elif [ "$1" == "settings" ]; then
		startSettings "$2"
	elif [ "$1" == "src" ]; then
			"$STEAM" "${STEAM}://${RECO}"
	elif [ "$1" == "steamworksshared" ] || [ "$1" == "sws" ]; then
		if [ -n "$2" ]; then
			if [ "$2" == "install" ] || [ "$2" == "i" ]; then
				if [ -n "$4" ]; then
				installSteWoShPak "$3" "$4" "$5"
				else
					writelog "INFO" "${FUNCNAME[0]} - Need at least package name as arg 3 and a wineprefix OR a SteamAppID as arg 4" E
					writelog "INFO" "${FUNCNAME[0]} - and optionally an absolute path to to a wine binary as arg 5" E
				fi
			elif [ "$2" == "list" ] || [ "$2" == "l" ]; then
				listSteWoShPaks
			else
				writelog "INFO" "${FUNCNAME[0]} - arg2 '$2' is no valid command"
				howto
			fi
		else
			echo "need arg2"
			howto
		fi
	elif [ "$1" == "${SPEK,,}" ]; then
		if [ -n "$2" ]; then
			if [ "$2" == "download" ] || [ "$2" == "dl" ]; then
				dlSpecialK "$3"
			else
				howto
			fi
		else
			howto
		fi
	elif [ "$1" == "steamgriddb" ] || [ "$1" == "sgdb" ]; then
		# This is the new SteamGridDB commandline usage, we just expose direct function to commandline

		# No arguments passed, skip
		if [ -z "$2" ]; then
			echo "Need to pass arguments to SteamGridDB command, see 'steamtinkerlaunch help' for usage."
			return
		fi

		if [ -z "$3" ]; then
			# TODO ensure this new path does not break 'commandlineGetSteamGridDBArtwork' usage in any way!
			## Show GUI if only 3rd argument given (assume is AppID), i.e if user just entered "steamtinkerlaunch sgdb <appid>"
			getSteamGridDBArtworkGUI "$2"
		else
			# TODO if search ID is not provided but the first argument is an integer, assume it is the AppID
			## Will allow for usage like `steamtinkerlaunch sgdb 730 --apply` which is very clean
			commandlineGetSteamGridDBArtwork "$@"
		fi
	elif [ "$1" == "getsteamgriddbid" ] || [ "$1" == "sgdbid" ]; then
		getSGDBGameIDFromTitle "$2"
	elif [ "$1" == "update" ]; then
		if [ -n "$2" ]; then
			if [ "$2" == "gamedata" ]; then
				if [ -z "$3" ]; then
					getGameDataForInstalledGames
				else
					echo getGameData "$3"
					getGameData "$3"
				fi
			elif [ "$2" == "grid" ]; then
				if [ -z "$3" ]; then
					getGridsForOwnedGames
				elif [ "$3" == "owned" ]; then
					getGridsForOwnedGames
				elif [ "$3" == "installed" ]; then
					getGridsForInstalledGames
				elif [ "$3" == "nonsteam" ] || [ "$3" == "shortcuts" ]; then
					getGridsForNonSteamGames
				fi
			elif [ "$2" == "allgamedata" ]; then
				getDataForAllGamesinSharedConfig
			elif [ "$2" == "shader" ] || [ "$2" == "shaders" ]; then
				if [ -n "$3" ] && { [ "$3" == "repos" ] || [ "$3" == "list" ]; }; then
					dlShaders "$3"
				else
					StatusWindow "$GUI_DLSHADER" "dlShaders $3"  "DownloadShadersStatus"
				fi
			elif [ "$2" == "gameshader" ] || [ "$2" == "gameshaders" ]; then
				if [ -z "$3" ]; then
					writelog "INFO" "${FUNCNAME[0]} - No game directory in argument 3 provided - using last game!"
					GameShaderDialog
				else
					if [ -d "$3" ]; then
						writelog "INFO" "${FUNCNAME[0]} - command line: GameShaderDialog \"$3\""
						GameShaderDialog "$3"
					else
						if [ -n "$4" ]; then
							if [ -d "$4" ]; then
								if [ -n "$5" ] && [ "$5" == "disable" ]; then
									disableThisGameShaderRepo "$3" "$4"
								else
									enableThisGameShaderRepo "$3" "$4"
								fi
							elif [ "$4" == "block" ]; then
								echo "$3" >> "$SHADERREPOBLOCKLIST"
								sort -u "$SHADERREPOBLOCKLIST" -o "$SHADERREPOBLOCKLIST"
								unblockrssub
							elif [ "$4" == "unblock" ]; then
								grep -v "^${3}$" "$SHADERREPOBLOCKLIST" > "$STLSHM/SHADERREPOBLOCKLIST_tmp.txt"
								mv "$STLSHM/SHADERREPOBLOCKLIST_tmp.txt" "$SHADERREPOBLOCKLIST"
							else
								writelog "SKIP" "${FUNCNAME[0]} - Invalid argument '$4' - exit"
							fi
						else
							writelog "SKIP" "${FUNCNAME[0]} - Game directory '$3' does not exist - exit"
						fi
					fi
				fi
			elif [ "$2" == "reshade" ]; then
				dlReShade "$3"
			else
				howto
			fi
		else
			howto
		fi
	elif [ "$1" == "cleargamegrids" ]; then
		if [ -z "$2" ]; then
			writelog "ERROR" "${FUNCNAME[0]} - No parameter given, cannot remove artwork, skipping"
			echo "You must provide either a Steam AppID to remove artwork for, or specify 'all' to remove all game artwork"
		else
			removeSteamGrids "$2"
		fi
	elif [ "$1" == "version" ] || [ "$1" == "--version" ] || [ "$1" == "-v" ]; then
		echo "${PROGNAME,,}-${PROGVERS}"
	elif [ "$1" == "$VTX" ]; then
		# TODO Vortex uninstall option?
		# TODO Vortex commandline flag to set no auto update (i.e. --disable-auto-update)

		USEVORTEX=1
		if [ -n "$2" ]; then
			# If we get a third parameter and any passed command should download/install/start Vortex,
			# set the Vortex version to download to the given version (will only be used if Vortex is not already installed)
			#
			# TODO handle passing a custom installer file, probably only for "install" though where we'll force set a different var
			if [[ -n "$3" && ( "$2" == "download" || "$2" == "install" || "$2" == "start" || "$2" == "getset" ) ]]; then
				if [ "$USEVORTEXCUSTOMVER" -eq 0 ]; then
					writelog "WARN" "${FUNCNAME[0]} - Custom Vortex version passed ('$3') but USEVORTEXCUSTOMVER is '$USEVORTEXCUSTOMVER'"
					writelog "WARN" "${FUNCNAME[0]} - Assuming you know what you're doing and using this version anyway..."
				fi

				USEVORTEXCUSTOMVER=1
				VORTEXCUSTOMVER="$3"
			fi

			if [ "$2" == "install" ] || [ "$2" == "i" ]; then
				if [ -n "$3" ] && [ "$3" == "gui" ]; then
					installVortexGui
				else
					StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "${VTX^}")" "dlLatestVortex S" "DownloadVortexStatus"
					StatusWindow "$(strFix "$NOTY_INSTSTART" "${VTX^}")" "installVortex" "InstallVortexStatus"
				fi
			elif [ "$2" == "start" ]; then
				startVortex "noask" "$3"
			elif [ "$2" == "url" ] || [ "$2" == "u" ]; then
				if [ -z "$3" ]; then
					writelog "ERROR" "${FUNCNAME[0]} - No URL was passed for ('$3') -- Maybe this is being incorrectly launched from the XDG menu?"
					warnInvalidModToolLaunch "Vortex"
				else
					startVortex "noask" "url" "$3"
				fi
			elif [ "$2" == "getset" ]; then
				startVortex "noask" "$2"
			elif [ "$2" == "gui" ]; then
				VortexOptions
			elif [ "$2" == "reset" ]; then
				resetVortexSettings
			elif [ "$2" == "stage" ]; then
				addVortexStage "$3"
			elif [ "$2" == "list-supported" ] || [ "$2" == "ls" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Games With ${VTX^} Support found in the ${VTX^} installation:" "E"
				getVortexSupportedNames
			elif [ "$2" == "list-online" ] || [ "$2" == "lo" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Games With ${VTX^} Support listed online:" "E"
				dlVortexSupportedList
			elif [ "$2" == "list-owned" ] || [ "$2" == "low" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Games owned with ${VTX^} Support:" "E"
				VORTEXGAMES="$GLOBALMISCDIR/$VOGAT"
				while read -r line; do
					# TODO speed this up somehow?
					OWNEDVTXGAMELINE="$( grep "\"$line\"" "$VORTEXGAMES" )"
					OWNEDVTXGAMENAME="$( echo "$OWNEDVTXGAMELINE" | cut -d ";" -f2 | cut -d '"' -f 2 )"
					OWNEDVTXGAMEAID="$( echo "$OWNEDVTXGAMELINE" | cut -d ";" -f3 | cut -d '"' -f 2 )"

					if [ -n "$OWNEDVTXGAMENAME" ] && [ -n "$OWNEDVTXGAMEAID" ]; then
						printf "%s (%s)\n" "$OWNEDVTXGAMENAME" "$OWNEDVTXGAMEAID"
					fi
				done <<< "$(getOwnedAids)"
			elif [ "$2" == "list-installed" ] || [ "$2" == "li" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Installed Games With ${VTX^} Support"
				setVortexVars
				VORTEXGAMES="$GLOBALMISCDIR/$VOGAT"
				while read -r INSTALLEDVTXAID; do
					if [ -z "$INSTALLEDVTXAID" ]; then
						continue
					fi

					INSTALLEDVTXNAME="$( grep "$INSTALLEDVTXAID" "$VORTEXGAMES" | cut -d ";" -f 2 | cut -d '"' -f 2 )"

					# Bit hacky but fixes an instance where two games (on newlines) are returned but only one AppID is returned
					# Get rid of the newlines and replace with a semicolon, then cut and get the second game name which should be the matching AppID
					INSTALLEDVTXNAME="$( echo "${INSTALLEDVTXNAME//$'\n'/;}" | cut -d ";" -f 2 )"
					printf "%s (%s)\n" "$INSTALLEDVTXNAME" "$INSTALLEDVTXAID"
				done <<< "$(getInstalledGamesWithVortexSupport X)"
			elif [ "$2" == "games" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Opening Gui for en/disabling ${VTX^} for installed and supported games"
				VortexGamesDialog
			elif [ "$2" == "symlinks" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Opening Gui showing Symlinks in the ${VTX^} WINEPREFIX"
				VortexSymDialog
			elif [ "$2" == "download" ] || [ "$2" == "d" ]; then
				StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "${VTX^}")" "dlLatestVortex S" "DownloadVortexStatus"
			elif [ "$2" == "activate" ] && { [ -n "$3" ]  && [ "$3" -eq "$3" ] 2>/dev/null;}; then
				startVortex "activate" "$3"
			elif [ "$2" == "getprefix" ] || [ "$2" == "gp" ]; then
				echo "$VORTEXCOMPDATA/pfx"
			elif [ "$2" == "winecfg" ]; then
				vtxWinecfg
			elif [ "$2" == "winetricks" ] || [ "$2" == "wt" ]; then
				vtxWinetricks
			elif [ "$2" == "resetmime" ]; then
				writelog "INFO" "${FUNCMAME[0]} - (Re)setting ${VTX^} .desktop file entries and MimeType associations"
				echo "(Re)setting ${VTX^} .desktop file entries and MimeType associations"
				setVortexDLMime
			else
				writelog "INFO" "${FUNCNAME[0]} - arg2 '$2' is no valid command"
				howto
			fi
		else
			echo "need arg2"
			howto
		fi
	elif [ "$1" == "vr" ]; then
		if [ -n "$3" ]; then
			GAMEWINDOW="$2"
			AID="$3"
			setAIDCfgs
			if [ -n "$4" ] && [ "$4" == "s" ]; then
				storeGameWindowNameMeta "$(getGameWinNameFromXid "$2")"
			fi
			checkSBSVRLaunch "$2"
		else
			howto
		fi
	elif [ "$1" == "waitrequester" ] || [ "$1" == "wr" ]; then
		if [ -n "$2" ] && { [ "$2" == "e" ] || [ "$2" == "s" ] || [ "$2" == "u" ];}; then
			if [ "$2" == "e" ]; then
				writelog "INFO" "${FUNCNAME[0]} - enabling the Wait Requester for the next launched game"
				touch "$EWRF"
			elif [ "$2" == "s" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Skipping the Wait Requester temporarily for the next launched games"
				touch "$SWRF"
			elif [ "$2" == "u" ]; then
				if [ -f "$SWRF" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Disabling the temporary Wait Requester skipping"
					touch "$UWRF"
				else
					writelog "SKIP" "${FUNCNAME[0]} - The temporary Wait Requester skipping is not enabled, nothing to do"
				fi
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} ------------------------"
			writelog "INFO" "${FUNCNAME[0]} - need either e,s or u as arg2"
			howto
		fi
	elif [ "$1" == "wiki" ]; then
		OpenWikiPage "$2"
	elif [ "$1" == "winetricks" ] || [ "$1" == "wt" ]; then
		FUSEID "$2"
		chooseWinetricksPrefix "$USEID"
	elif [ "$1" == "runwinecfg" ] || [ "$1" == "onetimewinecfg" ] || [ "$1" == "otwcfg" ]; then
		# Assumes that if `runwinecfg` is called without any arguments that it's an internal call
		# Otherwise we assume if *any* arguments are passed that we're a user calling it from the command-line
		if [ -n "$2" ]; then
			# Always assume second argument is the AppID
			#
			# Could be improved in future by trying to find a matching game based on an entered game name
			# In the case of multiple matches we could just take the first match
			#
			# (Maybe AppID should be checked for first, on the off-chance that a game's names is the same as an AppID?
			writelog "INFO" "${FUNCNAME[0]} - Looks like we're a user calling this from the command line -- User passed '$2'"
			oneTimeWinecfg "$2"
		else
			writelog "INFO" "${FUNCNAME[0]} - Looks like we're getting an internal One-Time Winecfg call"
			oneTimeWinecfg
		fi
	elif [ "$1" == "runwinetricks" ] || [ "$1" == "onetimewinetricks" ] || [ "$1" == "otwt" ]; then
		# All of the above comments about Winecfg apply to this Winetricks logic as well
		if [ -n "$2" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Looks like we're a user calling this from the command line -- User passed '$2'"
			oneTimeWinetricks "$2"
		else
			writelog "INFO" "${FUNCNAME[0]} - Looks like we're getting an internal One-Time Winetricks call"
			oneTimeWinetricks
		fi
	elif [ "$1" == "winedebugchannel" ] || [ "$1" == "wdc" ]; then
		FUSEID "$2"
		SetWineDebugChannels "$USEID"
	elif [ "$1" == "yad" ]; then
		if [ -n "$2" ]; then
			setYadBin "$2" "$3"
		else
			writelog "INFO" "${FUNCNAME[0]} ------------------------"
			writelog "INFO" "${FUNCNAME[0]} - arg2 '$2' needs to be a valid yad parameter"
			howto
		fi
	elif [ "$1" == "openissue" ] || [ "$1" == "oi" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Opening issue tracker in user's default browser"
		"$XDGO" "$PROJECTPAGE/issues/new/choose"
	elif echo "$@" | grep -owq '\-q'; then
		writelog "INFO" "${FUNCNAME[0]} - Quiet mode enabled with '-q', suppressing notifier for this execution"
		export STLQUIET=1
		USENOTIFIER=0
	else
		if ! grep -q "lang=\|run" <<< "$@"; then
			writelog "INFO" "${FUNCNAME[0]} ------------------------"
			writelog "INFO" "${FUNCNAME[0]} - arg1 '$1' is no valid command"
			howto
		fi
	fi
}
#ENDCMDLINE


### COMMAND LINE END ###

function writeLastRun {
	writelog "INFO" "${FUNCNAME[0]} - Recreating $LASTRUN"
	{
	echo "RUNPROTON=\"$RUNPROTON\""
	echo "RUNWINE=\"$RUNWINE\""
	echo "PROTONVERSION=\"$PROTONVERSION\""
	echo "PREVAID=\"$AID\""
	echo "PREVGAME=\"$GN\""
	echo "PREVABSGAMEEXEPATH=\"$ABSGAMEEXEPATH\""
	} >	"$LASTRUN"
}

function storeMetaData {
	MAID="$1"
	MGNA1="${2//\//_}"
	MGNA="${MGNA1//\"/}"
	MPFX="$3"
	GDIR="$4"

	writelog "INFO" "${FUNCNAME[0]} - Saving metadata for game '$MGNA ($MAID)'"

	if [ ! -f "$GEMETA/$MAID.conf" ]; then
		if [ "$SGDBAUTODL" == "no_meta" ] ; then
			writelog "INFO" "${FUNCNAME[0]} - Automatic Grid Update Check '$SGDBAUTODL'"
			# getGrids "$MAID"
			commandlineGetSteamGridDBArtwork --search-id="$MAID" --steam
		fi
		touch "$GEMETA/$MAID.conf"
	fi

	loadCfg "$GEMETA/$MAID.conf" X
	updateConfigEntry "GAMEID" "$MAID" "$GEMETA/$MAID.conf"

	if [ -z "$KEEPGAMENAME" ] || [ "$KEEPGAMENAME" -eq 0 ] || [ "$GAMENAME" == "$NON" ]; then
		updateConfigEntry "GAMENAME" "$MGNA" "$GEMETA/$MAID.conf"
	fi

	if [ -n "$GE" ] && [ -z "$GAMEEXE" ]; then
		GAMEEXE="$GE"
	fi

	if [ -n "$GAMEEXE" ]; then
		updateConfigEntry "GAMEEXE" "$GAMEEXE" "$GEMETA/$MAID.conf"
	fi

	if [ -n "$GP" ]; then
		updateConfigEntry "GAMEARCH" "$(getArch "$GP")" "$GEMETA/$MAID.conf"
	fi

	if [ ! -f "$CUMETA/$MAID.conf" ]; then
		touch "$CUMETA/$MAID.conf"
	fi
	loadCfg "$CUMETA/$MAID.conf" X

	if [ "$MPFX" != "$NON" ]; then
		updateConfigEntry "WINEPREFIX" "$MPFX" "$CUMETA/$MAID.conf"
	fi

	updateConfigEntry "MEGAMEDIR" "$GDIR" "$CUMETA/$MAID.conf"

	if [ "$STLPLAY" -eq 1 ]; then
		touch "$FUPDATE"
		updateConfigEntry "STL_COMPAT_DATA_PATH" "$STEAM_COMPAT_DATA_PATH" "$CUMETA/$MAID.conf"
	fi
	createSymLink "${FUNCNAME[0]}" "$GEMETA/$MAID.conf" "$TIGEMETA/${MGNA}.conf" X
	createSymLink "${FUNCNAME[0]}" "$CUMETA/$MAID.conf" "$TICUMETA/${MGNA}.conf" X

	if [ "$STLPLAY" -eq 0 ]; then
		createSymLink "${FUNCNAME[0]}" "$EVMETAID/${EVALSC}_${MAID}.vdf" "$EVMETATITLE/${EVALSC}_${MGNA}.vdf"
	fi
}

function delMenuTemps {
	find "$STLSHM" -maxdepth 1 -type f -regextype posix-extended -regex '^.*menu.[A-Z,a-z,0-9]{8}' -exec rm {} \;
}

function delWinetricksTemps {
	# cosmetics - GE leaves empty winetricks directories back, removing them
	find "/tmp" -maxdepth 1 -type d -regextype posix-extended -regex '^.*winetricks.[A-Z,a-z,0-9]{8}' -exec rmdir {} \;	2>/dev/null
}

function cleanSUTemp {
	if [ "$ISGAME" -eq 2 ] && [ "$CLEANPROTONTEMP" -eq 1 ]; then
		PFXSUTEMP="$GPFX/$DRCU/$STUS/Temp"
		if [ -d "$PFXSUTEMP" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Cleaning up Temp directory '$PFXSUTEMP'"
			rm -rf "${PFXSUTEMP:?}"
		else
			writelog "SKIP" "${FUNCNAME[0]} - Temp directory '$PFXSUTEMP' exists, but is empty"
		fi
	fi
}

function restoreSteamUser {

	function startRestore {
		writelog "INFO" "${FUNCNAME[0]} - Restoring backup from '$BACKUPSRC' to '$SteamUserDir'"
		notiShow "$(strFix "$NOTY_STARTRESTORE" "$BACKUPSRC" "$AID")"
		mkProjDir "$SteamUserDir"
		"$RSYNC" -am "$BACKUPSRC" "$SteamUserDir"
		notiShow "$(strFix "$NOTY_STOPRESTORE" "$SteamUserDir")"
	}

	function startRestoreBecause {
		writelog "INFO" "${FUNCNAME[0]} - Restoring, because '$RSTUS' is set"
		startRestore
	}

	function askRestore {
		writelog "INFO" "${FUNCNAME[0]} - Asking if $STUS data from '$BACKUPSRC' shall be restored to '$SteamUserDir'"

		export CURWIKI="$PPW/Backup Support"
		TITLE="${PROGNAME}-Ask_Restore_SteamUser_data"
		pollWinRes "$TITLE"

		"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center --on-top "$WINDECO" \
		--title="$TITLE" \
		--text="$(spanFont "$(strFix "$GUI_AR" "$BACKUPSRC" "$SteamUserDir")" "H")\n<i>$1</i>" "$GEOM"
		case $? in
			0) 	{
					writelog "INFO" "${FUNCNAME[0]} - Restoring '$SteamUserDir' from '$BACKUPSRC' confirmed"
					startRestore
				}
			;;
			1)	{
					writelog "INFO" "${FUNCNAME[0]} - Selected CANCEL - Not restoring '$BACKUPSRC' to '$SteamUserDir'"
				}
			;;
		esac
	}

	if [ -n "$1" ] && [ "$1" != "$NON" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using value '$1' from argument for RESTORESTEAMUSER"
		RSTUS="$1"
	else
		writelog "INFO" "${FUNCNAME[0]} - Using configured value '$RESTORESTEAMUSER' for RESTORESTEAMUSER"
		RSTUS="$RESTORESTEAMUSER"
	fi

	SteamUserDir="$GPFX/$DRCU/$STUS"
	BACKUPSRC="$SUBADIRID/$AID/$STUS/"

	if [ "$RSTUS" == "$NON" ] ; then
		writelog "INFO" "${FUNCNAME[0]} - Restoration of $STUS data is disabled with RESTORESTEAMUSER being '$RSTUS"
	else
		if [ -n "$AID" ] && [ -d "$BACKUPSRC" ] && [ -d "$GPFX" ]; then
			if [ "$RSTUS" == "ask-always" ] ; then
				askRestore "$GUI_AR_ALWAYSASK"
			elif [ "$RSTUS" == "restore-always" ] ; then
				startRestoreBecause
			elif [ ! -f "$SteamUserDir/$BTS" ] ; then
				if [ "$RSTUS" == "ask-if-dst-has-no-backup-timestamp" ] ; then
					askRestore "$GUI_AR_ASKNODSTTS"
				elif [ "$RSTUS" == "restore-if-dst-has-no-backup-timestamp" ] ; then
					startRestoreBecause
				elif [ "$RSTUS" == "restore-if-dst-is-empty" ]; then
					if [ ! -d "$SteamUserDir" ] || [ "$(find "$SteamUserDir" -type f | wc -l)" -eq 0 ]; then
						startRestoreBecause
					elif [ "$RSTUS" == "ask-if-unsure" ] ; then
						askRestore "$GUI_AR_ALWAYSASK"
					fi
				elif [ "$RSTUS" == "ask-if-unsure" ] ; then
					askRestore "$GUI_AR_ASKUNSURE"
				fi
			elif [ -f "$BACKUPSRC/$BTS" ] && [ -f "$SteamUserDir/$BTS" ]; then
				if [ "$(cat "$BACKUPSRC/$BTS")" -gt "$(cat "$SteamUserDir/$BTS")" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Data in '$BACKUPSRC' is newer than in '$SteamUserDir'"
					if [ "$RSTUS" == "restore-if-backup-timestamp-is-newer" ] ; then
						startRestoreBecause
					elif [ "$RSTUS" == "ask-if-unsure" ] ; then
						askRestore "$GUI_AR_ALWAYSASK"
					fi
				else
					writelog "INFO" "${FUNCNAME[0]} - Data in '$BACKUPSRC' is older than in '$SteamUserDir'"
					if [ "$RSTUS" == "ask-if-unsure" ]; then
						askRestore "$GUI_AR_ASKUNSURE"
					elif [ "$RSTUS" == "restore-always" ]; then
						startRestoreBecause
					fi
				fi
			elif [ "$RSTUS" == "ask-if-unsure" ] ; then
				askRestore "$GUI_AR_ASKUNSURE"
			else
				writelog "SKIP" "${FUNCNAME[0]} - Have to skip - this should never happen. RESTORESTEAMUSER is '$RSTUS'"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - At least one of AID '$AID', BACKUPSRC '$BACKUPSRC', SteamUserDir '$SteamUserDir' is missing - can't start restoration of $STUS data"
		fi
	fi
}

function backupSteamUser {
	if [ "$BACKUPSTEAMUSER" -eq 1 ]; then
		BAID="$1"

		if [ -n "$GN" ]; then
			BACKTI="$GN"
		else
			getGameName "$BAID"
			if [ -n "$GAMENAME" ] && [ "$GAMENAME" != "$NON" ]; then
				BACKTI="$GAMENAME"
			fi
		fi

		if [ -n "$2" ]; then
			if [ -d "$2" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Using argument 2 '$2' as WINEPREFIX'"
				GPFX="$2"
			else
				writelog "SKIP" "${FUNCNAME[0]} - Directory in argument 2 '$2' does not exist"
				GPFX=""
			fi
		fi

		setGPfxFromAppMa "$BAID"

		if [ -z "$GPFX" ]; then
			if [ -f "$CUMETA/$BAID.conf" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Loading metadata config '$CUMETA/$BAID.conf'"
				loadCfg "$CUMETA/$BAID.conf" X
				if [ -d "$WINEPREFIX" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Searching in stored metadata WINEPREFIX '$WINEPREFIX' for files to backup"
					GPFX="$WINEPREFIX"
				else
					writelog "SKIP" "${FUNCNAME[0]} - Stored metadata WINEPREFIX '$WINEPREFIX' does not exist"
				fi
			else
				writelog "SKIP" "${FUNCNAME[0]} - No metadata config '$CUMETA/$BAID.conf' found to search for the WINEPREFIX"
				GPFX=""
			fi
		fi

		if [ -z "$GPFX" ]; then
			if [ -z "$BACKTI" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - Game pfx unknown for '$BAID'"
			else
				writelog "SKIP" "${FUNCNAME[0]} - Game pfx unknown for '$BACKTI ($BAID)'"
			fi
		else
			if [ -z "$2" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Backup enabled for Game '$BACKTI ($BAID)'"
			fi
			mkProjDir "$SUBADIRID/$BAID"
			mkProjDir "$SUBADIRTI"
			SteamUserDir="$GPFX/$DRCU/$STUS"

			if [ -d "$SteamUserDir" ]; then
				writelog "INFO" "${FUNCNAME[0]} - backing up directory '$SteamUserDir' to '$SUBADIRID/$BAID'"
				if [ -z "$2" ]; then
					notiShow "$(strFix "$NOTY_STARTBACKUP" "$BACKTI" "$BAID")"
				fi
				mkProjDir "$BACKEX"

				EXID="$BACKEX/exclude-${BAID}.txt"
				touch "$EXGLOB" "$EXID"
				"$RSYNC" -am --exclude-from="$EXGLOB" --exclude-from="$EXID" "$SteamUserDir" "$SUBADIRID/$BAID"
				date +%s > "$SUBADIRID/$BAID/$STUS/$BTS"

				if [ -z "$2" ]; then
					notiShow "$(strFix "$NOTY_STOPBACKUP" "$BACKTI" "$BAID")"
				fi
			else
				writelog "SKIP" "${FUNCNAME[0]} - directory '$SteamUserDir' does not exist - nothing to backup"
			fi

			if [ -z "$BACKTI" ]; then
				if [ -z "$2" ]; then
					writelog "SKIP" "${FUNCNAME[0]} - Skipping symlinking backup - no valid game name found"
				fi
			else
				createSymLink "${FUNCNAME[0]}" "$SUBADIRID/$BAID" "$SUBADIRTI/$BACKTI"
			fi
		fi
	fi
}

function backupSteamUserGate {
	BACKUPSTEAMUSER=1
	if [ "$1" != "all" ]; then
		backupSteamUser "$1"
	else
		LOGFILE="$TEMPLOG"
		writelog "INFO" "${FUNCNAME[0]} - Selected to backup all '$STUS' files from all found pfxes"

		while read -r APPMA; do
			AMF="${APPMA##*/}"
			BAID="$(cut -d'_' -f2 <<< "$AMF" | cut -d'.' -f1)"
			BPFX="$(dirname "$APPMA")/$CODA/$BAID/pfx"
			if [ -d "$BPFX" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Backup all '$STUS' files for pfx '$BPFX'"
				backupSteamUser "$BAID" "$BPFX"
			fi
		done <<< "$(listAppManifests)"
	fi
}

function createMetaData {
	LASTMETAUP="$METADIR/lastmeta.txt"
	MAXMETAAGE=1440

	if [ -n "$1" ] && [ "$1" == "yes" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Updating metadata requested via command line"
		rm "$LASTMETAUP"
	fi

	if [ ! -f "$LASTMETAUP" ] || test "$(find "$LASTMETAUP" -mmin +"$MAXMETAAGE")"; then
		writelog "INFO" "${FUNCNAME[0]} - Creating/Updating metadata for all installed games found"

		while read -r APPMA; do
			AMF="${APPMA##*/}"
			MAID="$(cut -d'_' -f2 <<< "$AMF" | cut -d'.' -f1)"

			GNRAW="$(grep "\"name\"" "$APPMA" | awk -F '"name"' '{print $NF}')"
			GNAM="$(awk '{$1=$1};1' <<< "$GNRAW")"
			GDIR="$(getGameDirFromAM "$APPMA")"
			MPFX="$(dirname "$APPMA")/$CODA/$MAID/pfx"

			storeMetaData "$MAID" "$GNAM" "$MPFX" "$GDIR"
		done <<< "$(listAppManifests)"
		writelog "INFO" "${FUNCNAME[0]} - Done with Creating/Updating metadata"
		date +%y-%m-%d > "$LASTMETAUP"
	fi
}

function getLatestX64dbgSnap {
	# TODO this could be improved, PR welcome!

	# wget the expanded assets page and get the first link contents
	# could be prettier and more robust but should work for now, despite being a little flimsy
	NEWX64DBGURL="https://github.com/x64dbg/x64dbg"  # global config one is a bit busted and can't be easily migrated
	X64DSNAPPATH="$( "$WGET" -q "${NEWX64DBGURL}/releases/expanded_assets/snapshot" -O - 2> >(grep -v "SSL_INIT") | grep -E "releases/download" | grep -oP '".*?"' | head -n1 | cut -d '"' -f2 )"  # doesn't return full path, only /x64dbg/x64dbg/releases/download/snapshot/snapshot_<date>.zip
	echo "${GHURL}${X64DSNAPPATH}"
}

function dlX64Dbg {
	DLDST="$X64DBGDLDIR"
	DLCH="$DLDST/commithash.txt"
	if [ -f "$DLCH" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - '$X64D' is already ready"
		return
	fi

	mkProjDir "$DLDST"
	X64ZIP="$(getLatestX64dbgSnap)"
	X64ZIPBASE="$( basename "$X64ZIP" )"

	# Download x64dbg
	if [ ! -f "$DLDST/$X64ZIPBASE" ]; then
		notiShow "$(strFix "$NOTY_DLCUSTOMPROTON" "$X64ZIP")" "S"
		dlCheck "$X64ZIP"  "$DLDST/$X64ZIPBASE" "X" "Downloading 'x64dbg'"
		notiShow "$(strFix "$NOTY_DLCUSTOMPROTON2" "$X64ZIP")" "S"
	fi

	DLDST="$X64DBGDLDIR"
	if [ ! -s "$DLDST" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Downloaded file '$DLDST/$X64ZIPBASE' is empty - removing"
		rm "$DLDST/$X64ZIPBASE" 2>/dev/null
		return
	fi

	# Extract x64dbg
	notiShow "$(strFix "$NOTY_DLCUSTOMPROTON3" "$X64ZIPBASE")" "S"
	writelog "INFO" "${FUNCNAME[0]} - Download of '$X64ZIPBASE' to '$DLDST' was successful"
	"$UNZIP" -q "$DLDST/$X64ZIPBASE" -d "$DLDST" 2>/dev/null
	notiShow "$GUI_DONE" "S"
	if [ -f "$DLCH" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Extracted '$X64ZIPBASE' to '$DLDST'"
	else
		writelog "SKIP" "${FUNCNAME[0]} - Extracting of file '$DLDST/$X64ZIPBASE' failed"
	fi
}

# start x64dbg
function checkX64dbgLaunch {
	if [ "$RUN_X64DBG" -eq 1 ] && [ "$ISGAME" -eq 2 ] && [ "$USEWINE" -eq 0 ]; then
		writelog "INFO" "${FUNCNAME[0]} - Starting '$X64D' for '$GE ($AID)'"

		if [ "$(getArch "$GP")" == "32" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using '$X32D' as '$GE' is 32bit"
			XDBGEXE="$X32D"
		elif [ "$(getArch "$GP")" == "64" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Using '$X64D' as '$GE' is 64bit"
			XDBGEXE="$X64D"
		else
			writelog "INFO" "${FUNCNAME[0]} - Could not get architecture of '$GP' - using '$X64D'"
			XDBGEXE="$X64D"
		fi

		XDBFPATH="$X64DBGDLDIR/release/${XDBGEXE//dbg/}/${XDBGEXE}.exe"

		if [ ! -f "$XDBFPATH" ]; then
			writelog "INFO" "${FUNCNAME[0]} - File '$X64EXE' does not exit - starting installer"
			StatusWindow "$(strFix "$NOTY_DLCUSTOMPROTON" "$X64D")" "dlX64Dbg" "DownloadX64DbgStatus"
		fi

		if [ ! -f "$XDBFPATH" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Installing failed - can't start '$XDBFPATH' - skipping"
			RUN_X64DBG=0
		else
			writelog "INFO" "${FUNCNAME[0]} - Applying registry '${X64D}.reg'"
			regEdit "$GLOBALMISCDIR/${X64D}.reg"

			DISPPROTVER="$( setProtonPathVersion "$RUNPROTON" )"
			if [ "$X64DBG_ATTACHONSTARTUP" -eq 1 ]; then
				writelog "INFO" "${FUNCNAME[0]} - Attaching game '$GE' running with '$RUNWINE' to x64dbg"
				writelog "INFO" "${FUNCNAME[0]} - Game execution will be handled by x64dbg from here"
				notiShow "$( strFix "$NOTY_X64DBG_ATTACHONSTARTUP" "$DISPPROTVER" "$GE" "$AID" )"

				WGP="$(extWine64Run "$RUNWINE" winepath -w "$GP" | tail -n1)"
				extWine64Run "$RUNWINE" "$XDBFPATH" "$WGP"  # Start x64dbg with game attached, but do NOT start the game
			else
				writelog "INFO" "${FUNCNAME[0]} - Launching x64dbg standalone with no process attached -- Game execution will continue normally"
				notiShow "$( strFix "$NOTY_RUN_X64DBG" "$DISPPROTVER" "$GE" "$AID" )"
				# does sleep 5 work on all systems with all games? May need changed/better solution in future!
				(sleep 5; "$RUNPROTON" run "$XDBFPATH" ) &  # Start x64dbg standalone with no process attached, and then launch game
			fi
		fi
	fi
}

function prepareGdb {
	GDBOPTS="$STLSHM/gdb.conf"
	WIREPY="WineReload.py"
	WINEREL="$STLSHM/$WIREPY"
	WIREURL="$WINERELOADURL/$WIREPY"
	GST10="gstreamer-1.0"

	if [ ! -f "$WINEREL" ]; then
		dlCheck "$WIREURL" "$WINEREL" "X" "Downloading '$DLSRC' to '$DLDST'"
	fi

	if [ ! -f "$GDBOPTS" ]; then
		{
		echo "set confirm off"
		echo "set pagination off"
		echo "handle SIGUSR1 noprint nostop"
		echo "handle SIGSYS noprint nostop"
		echo "source $WINEREL"
		} > "$GDBOPTS"
	fi

	if [ -z "$RUNPROTON" ]; then
		setRunProtonFromUseProton
	fi

	PROTONBASEPATH="$(dirname "$RUNPROTON")/files"
	if [ ! -d "$PROTONBASEPATH" ]; then
		PROTONBASEPATH="$(dirname "$RUNPROTON")/dist"
	fi

	setRunWineServer "${FUNCNAME[0]}"

	PBIPA="$PROTONBASEPATH/bin"
	PLIPA="$PROTONBASEPATH/lib"
	PLIPA64="$PROTONBASEPATH/lib64"


	if [ -n "$(GETALTEXEPATH)" ]; then
		WORKDIR="$(GETALTEXEPATH)"
	else
		WORKDIR="$EFD"
	fi

	{
	head -n1 "$0"
	echo "PATH=\"$PATH=:$PBIPA\" WINEDEBUG=\"-all\" WINEDLLPATH=\"${PLIPA64}/wine:${PLIPA}/wine:$WINEDLLPATH\" LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH:${PLIPA64}:${PLIPA}:$WORKDIR\" \
	WINEPREFIX=\"$GPFX\" WINEESYNC=1 WINEFSYNC=1 WINEDLLOVERRIDES=\"$WINEDLLOVERRIDES;steam.exe=b;dotnetfx35.exe=b;dxvk_config=n;d3d11=n;d3d10=n;d3d10core=n;d3d10_1=n;d3d9=n;dxgi=n\" \
	WINE_LARGE_ADDRESS_AWARE=1 GST_PLUGIN_SYSTEM_PATH_1_0=\"${PLIPA64}/${GST10}:${PLIPA}/${GST10}:$GST_PLUGIN_SYSTEM_PATH_1_0\" WINE_GST_REGISTRY_DIR=\"${WINEPREFIX}/${GST10}/\" \
	\"$RUNWINE\" \"steam.exe\" \"${GDBGAMESTARTCMD[*]}\""
	} > "$GDBGAMERUN"
	chmod +x "$GDBGAMERUN"
}

#start gdb
function injectGdb {
	function setgampi {
		GAMPI="$("$PGREP" -a "" | grep -i "${GP##*/}"  | grep "Z:" | grep -v "$PROGCMD" | cut -d ' ' -f1 | tail -n1)"
	}

	MAXWAIT=5
	COUNTER=0

	while ! [ "$GAMPI" -eq "$GAMPI" ] 2>/dev/null; do
		writelog "INFO" "${FUNCNAME[0]} - setgampi"
		setgampi
		if [[ "$COUNTER" -ge "$MAXWAIT" ]]; then
			writelog "ERROR" "${FUNCNAME[0]} - Timeout waiting for game pid $GAMPI - Skipping '$GDB'"
			return
		fi
		COUNTER=$((COUNTER+1))
		sleep 1
	done

	if [ -n "$GAMPI" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Found GamePid '$GAMPI' - Starting '$GDB'"
		{
		head -n1 "$0"
		echo "\"$GDB\" \"-x\" \"$GDBOPTS\" \"-p\" \"$GAMPI\""
		} > "$GDBRUN"
		chmod +x "$GDBRUN"
		"$USETERM" "$TERMARGS" "bash -c \"$GDBRUN\""
	fi
}

### BEGIN TEXT-BASED VDF INTERACTION FUNCTIONS
##
## This was written for Blush (https://github.com/sonic2kk/blush/) as part of the research on how to implement #905
## The code is pretty much the same but with variable names adapted to the SteamTinkerLaunch "convention"
## The code on Blush exists so this code can be used by others outside of SteamTinkerLaunch more easily

function backupVdfFile {
	ORGVDFNAME="$1"

	VDFBASENAME="$(basename "$ORGVDFNAME")"
	VDFDIRNAME="$(dirname "$ORGVDFNAME")"

	VDFNAME="${VDFBASENAME%%.*}"
	VDFEXT="${VDFBASENAME##*.}"
	BACKUPVDFNAME="${VDFDIRNAME}/${VDFNAME}_steamtinkerlaunch.${VDFEXT}"
	if [ -f "$ORGVDFNAME" ]; then
		SHOULDBACKUPVDF=1
		if [ -f "$BACKUPVDFNAME" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found existing VDF backup file '$BACKUPVDFNAME'"
			if [ "$(( $(date +"%s") - $(stat -c "%Y" "$BACKUPVDFNAME") ))" -gt "86400" ]; then  # file age > 1 day
				writelog "INFO" "${FUNCNAME[0]} - Existing VDF backup file is older than 1 day, overwriting"
				rm "$BACKUPVDFNAME"
			else
				writelog "SKIP" "${FUNCNAME[0]} - Existing VDF backup file is not older than 1 day, not overwriting"
				SHOULDBACKUPVDF=0
			fi
		fi

		if [ "$SHOULDBACKUPVDF" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Backing up VDF file '$VDFBASENAME' to '$( basename "$BACKUPVDFNAME" )'"
			cp "$ORGVDFNAME" "$BACKUPVDFNAME"
		else
			writelog "INFO" "${FUNCNAME[0]} - Not backing up VDF file '$VDFBASENAME'"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - VDF file to back up '$ORGVDFNAME' does not exist -- Nothing to back up"
	fi
}

## Generate string of [[:space:]] to represent indentation in VDF file, useful for searching
function generateVdfIndentString {
	SPACETYPE="${2:-\t}"  # Type of space, expected values could be '\t' (for writing) or '[[:space:]]' (for searching)

    printf "%.0s${SPACETYPE}" $(seq 1 "$1")
}

## Attempt to get the indentation level of the first occurance of a given VDF block
function guessVdfIndent {
    BLOCKNAME="$( safequoteVdfBlockName "$1" )"  # Block to check the indentation level on
    VDF="$2"

    grep -i "${BLOCKNAME}" "$VDF" | head -n1 | awk '{print gsub(/\t/,"")}'
}

## Surround a VDF block name with quotes if it doesn't have any
function safequoteVdfBlockName {
    QUOTEDBLOCKNAME="$1"
    if ! [[ $QUOTEDBLOCKNAME == \"* ]]; then
        QUOTEDBLOCKNAME="\"$QUOTEDBLOCKNAME\""
    fi

    echo "$QUOTEDBLOCKNAME"
}

## Use sed to grab a section of a given VDF file based on its indentation level
## TODO check if ENDPATTERN actually works?
function getVdfSection {
    STARTPATTERN="$( safequoteVdfBlockName "$1" )"
    ENDPATTERN="${2:-\}}"  # Default end pattern to end of block
    INDENT="$3"
    VDF="$4"
	STOPAFTERFIRSTMATCH="$5"

    if [ -z "$INDENT" ]; then
        INDENT="$(( $( guessVdfIndent "$STARTPATTERN" "$VDF" ) ))"
    fi

	# Only generate indent string if given tab length > 0
	# Allows for parsing top-level VDF section i.e. "UserLocalConfigStore" in localconfig.vdf
	INDENTSTR=""
	if [ "$INDENT" -gt 0 ]; then
		INDENTSTR="$( generateVdfIndentString "$INDENT" "[[:space:]]" )"
    else
		writelog "INFO" "${FUNCNAME[0]} - Indent is 0 ($INDENT)"
	fi
	INDENTEDSTARTPATTERN="${INDENTSTR}${STARTPATTERN}"
    INDENTEDENDPATTERN="${INDENTSTR}${ENDPATTERN}"

	writelog "INFO" "${FUNCNAME[0]} - Searching for VDF block with name '$STARTPATTERN' in VDF file '$VDF'"
	writelog "INFO" "${FUNCNAME[0]} - Start pattern is '$INDENTEDSTARTPATTERN'"

	# This is a very hacky solution to allow 'getNestedVdfSection' to use this function
	# It needs the start pattern exact match but other functions can't use this
	if [ -n "$STOPAFTERFIRSTMATCH" ]; then
		sed -n "/^${INDENTEDSTARTPATTERN}/I,/^${INDENTEDENDPATTERN}/I { p; /${INDENTEDENDPATTERN}/I q }" "$VDF"
	else
		sed -n "/^${INDENTEDSTARTPATTERN}/I,/^${INDENTEDENDPATTERN}/I p" "$VDF"
	fi
}

## Check if a VDF block (block_name) already exists inside a parent block (search_block)
## Ex: search_block "CompatToolMapping" for a specific block_name "22320"
function checkVdfSectionAlreadyExists {
    SEARCHBLOCK="$( safequoteVdfBlockName "${1:-\"}" )"  # Default to the first quotation, should be the start VDF file
    BLOCKNAME="$( safequoteVdfBlockName "$2" )"  # Block name to  search for
	VDF="$3"
	INDENT="$4"  # Optional to check from

	# Only set block indent if we gave an indent initally, otherwise use empty string so getVdfSection will ignore indent
	if [ -n "$INDENT" ]; then
		BLOCKINDENT="$(( INDENT + 1 ))"
	else
		BLOCKINDENT=""
	fi

    if [ -z "$BLOCKNAME" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - BLOCKNAME was not provided, skipping..."
        return
    fi

    SEARCHBLOCKVDFSECTION="$( getVdfSection "$SEARCHBLOCK" "" "$INDENT" "$VDF" )"
    if [ -z "$SEARCHBLOCKVDFSECTION" ]; then
		writelog "WARN" "${FUNCNAME[0]} - Could not find VDF section with name '$SEARCHBLOCK' in VDF file '$VDF' -- Skipping"
        return 0
    fi

	# Need to pass Indent + 1 because the block we're searching for is 1 deeper, i.e. searching "CompatToolMapping", the AppID key will be 1 indent deeper
    printf "%s" "$SEARCHBLOCKVDFSECTION" > "/tmp/tmp.vdf"
    getVdfSection "$BLOCKNAME" "" "$BLOCKINDENT" "/tmp/tmp.vdf" | grep -iq "$BLOCKNAME"
}

function getNestedVdfSection {
	VDFPATH="$1"  # i.e. "TopLevel/SecondLevel/ThirdLevel"
	INDENT="$2"  # indent to start searching from
	VDF="$3"

	mapfile -t -d '/' VDFPATHARRAY < <(echo -n "$VDFPATH")
	VDFPATHARRAYLEN="${#VDFPATHARRAY[*]}"
	if [ "$VDFPATHARRAYLEN" -eq 0 ]; then
		writelog "INFO" "${FUNCNAME[0]} - VDFPATHARRY is empty, nothing to do"
		return
	fi
	if [ -z "$INDENT" ]; then
		INDENT="$(( $( guessVdfIndent "${VDFPATHARRAY[0]}" "$VDF" ) ))"
	fi

	# Use getVdfSection on each section it finds until we run out of
	CURRENTSECTION=""
	for SECIND in "${!VDFPATHARRAY[@]}"; do
		SECTIONNAME="$( safequoteVdfBlockName "${VDFPATHARRAY[$SECIND]}" )"
		writelog "INFO" "${FUNCNAME[0]} - Searching for section with name '$SECTIONNAME'"
		NEXTSECTION="$( getVdfSection "$SECTIONNAME" "" "$INDENT" "$VDF" "X" )"
		writelog "INFO" "${FUNCNAME[0]} - NEXTSECTION is '$NEXTSECTION'"
		if [ -n "$NEXTSECTION" ]; then
			CURRENTSECTION="$NEXTSECTION"
			((INDENT+=1))
		else
			writelog "INFO" "${FUNCNAME[0]} - Found no matching section with name '$SECTIONNAME', bailing out"
			break
		fi
	done
	echo "$CURRENTSECTION"
}

## Create entry in given VDF block with matching indentation (Case-INsensitive)
## Appends to bottom of target block by default, but can optionally append to the top instead
##
## This doesn't support adding nested entries, at least not very easily
function createVdfEntry {
    VDF="$1"  # Absolute path to VDF to insert into
    PARENTBLOCKNAME="$( safequoteVdfBlockName "$2" )"  # Block to start from, e.g. "CompatToolMapping"
    NEWBLOCKNAME="$( safequoteVdfBlockName "$3" )"  # Name of new block, e.g. "<AppID>"
    POSITION="${4:-bottom}"  # POSITION to insert into, can be either top/bottom -- Bottom by default
	INDENT="$5"  # Indent that PARENTBLOCKNAME is at (0 = top of file)
	CHECKDUPLICATES="${6:-1}"  # Flag to check for duplicate section names

	# If no indent is given, guess the indent, otherwise use the specified one
	# -------
	# BASETABAMOUNT = Indent for the start of the new section, i.e. one indent in from the parent block
	# BLOCKTABAMOUNT = Indent for the block inside of the section
	#
	# Ex: With CompatToolMapping, BASETABAMOUNT represents the indent for the AppID, such as "22300"
	#                             BLOCKTABAMOUNT represents the indent for the contents of the block under this name
	# -------
	if [ -z "$INDENT" ]; then
		## Calculate indents for new block (one more than PARENTBLOCKNAME indent)
		BASETABAMOUNT="$(( $( guessVdfIndent "${PARENTBLOCKNAME}" "$VDF" ) + 1 ))"
		writelog "INFO" "${FUNCNAME[0]} - Guessed BASETABAMOUNT='${BASETABAMOUNT}'"
	else
		BASETABAMOUNT="$INDENT"
	fi

	# Indents for PARENTBLOCK
	BLOCKTABAMOUNT="$(( BASETABAMOUNT + 1 ))"
	PARENTBLOCKTABAMOUNT="$(( BASETABAMOUNT - 1 ))"

    ## Ensure no duplicates are written out (duplicate names can exist at different indent levels)
    if [ "$CHECKDUPLICATES" -eq 1 ] && checkVdfSectionAlreadyExists "$PARENTBLOCKNAME" "$NEWBLOCKNAME" "$VDF" "$PARENTBLOCKTABAMOUNT"; then
		writelog "SKIP" "${FUNCNAME[0]} - Block '$NEWBLOCKNAME' already exists in parent block '$PARENTBLOCKNAME' - Skipping"
        return
    fi

	writelog "INFO" "${FUNCNAME[0]} - Creating VDF data block to append to '$PARENTBLOCKNAME'"

    ## Create array from args, skip first four to get array of key/value pairs for VDF block
    NEWBLOCKVALUES=("${@:7}")
	writelog "INFO" "${FUNCNAME[0]} - NEWBLOCKVALUES are ${NEWBLOCKVALUES[*]}"

    NEWBLOCKVALUESDELIM="!"

    ## Tab amounts represented as string
    PARENTBLOCKTABSTR="$( generateVdfIndentString "$PARENTBLOCKTABAMOUNT" )"
    BASETABSTR="$( generateVdfIndentString "$BASETABAMOUNT" )"
    BLOCKTABSTR="$( generateVdfIndentString "$BLOCKTABAMOUNT" )"

	writelog "INFO" "${FUNCNAME[0]} - PARENTBLOCKTABAMOUNT is '$PARENTBLOCKTABAMOUNT'"
	writelog "INFO" "${FUNCNAME[0]} - BASETABSTR is '$BASETABSTR'"
	writelog "INFO" "${FUNCNAME[0]} - BLOCKTABSTR is '$BLOCKTABSTR'"

	writelog "INFO" "${FUNCNAME[0]} - Grep is '^${BASETABSTR}${PARENTBLOCKNAME}'"

    ## Calculations for line numbers
    ## PARENTBLOCKLENGTH is 1 line too short
	PARENTBLOCKLENGTH="$( getVdfSection "$PARENTBLOCKNAME" "" "$INDENT" "$VDF" | wc -l )"
	PARENTBLOCKLENGTH="$(( PARENTBLOCKLENGTH + 1 ))"

    BLOCKLINESTART="$( grep -Pin -- "^${PARENTBLOCKTABSTR}${PARENTBLOCKNAME}" "$VDF" | head -n1 | cut -d ':' -f1 | xargs )"
    TOPOFBLOCK="$(( BLOCKLINESTART + 2 ))"
	writelog "INFO" "${FUNCNAME[0]} - BLOCKLINESTART is '$BLOCKLINESTART'"

	# HACK: If parent block indent is -1, we can assume this means we want to add this VDF entry as the LAST block in the file
	#       If we want to add a block to the end of the file, we only need to move up 2 lines (last line is always blank)
	#       But if we're not at the end of the file we can assume we need to move up 3 lines (to account for the block/entry FOLLOWING the block we want to add)
	#
	#      For appending to the end of the VDF file, we want to start appending at the line that has the last closing brace (since the last line is blank, going up 2 lines gives us the line with the ending brace)
	#      For appending in any other case, we assume we have to move up 3 lines
	#
	#     To fix this we assume a default line offset of 3, but if PARENTBLOCKTABAMOUNT is 1, then we set the line offset to 2
	#     These are basically magic numbers discovered by trial and error, and a fix to make the logic more consistent is welcome
	BOTTOMOFBLOCKOFFSET=3
	if [ "$PARENTBLOCKTABAMOUNT" -eq -1 ]; then
		BOTTOMOFBLOCKOFFSET=2
	fi

    BOTTOMOFBLOCK="$(( BLOCKLINESTART + PARENTBLOCKLENGTH - BOTTOMOFBLOCKOFFSET ))"

	writelog "INFO" "${FUNCNAME[0]} - PARENTBLOCKLENGTH is '${PARENTBLOCKLENGTH}' lines"
	writelog "INFO" "${FUNCNAME[0]} - TOPOFBLOCK is line '${TOPOFBLOCK}'"
	writelog "INFO" "${FUNCNAME[0]} - BOTTOMOFBLOCK is line '${BOTTOMOFBLOCK}'"

    ## Decide which line to insert new block into (uses if/else for ease of logging)
    if [[ "${POSITION,,}" == "top" ]]; then
        INSERTLINE="${TOPOFBLOCK}"
		writelog "INFO" "${FUNCNAME[0]} - Will insert new block into top of '$PARENTBLOCKNAME' VDF section"
	else
		INSERTLINE="${BOTTOMOFBLOCK}"
		writelog "INFO" "${FUNCNAME[0]} - Will insert new block into bottom of '$PARENTBLOCKNAME' VDF section"
    fi

    ## Build new VDF entry string
    ## Maybe this could be a separate function at some point, that generates a VDF string from the input array?
    NEWBLOCKSTR="${BASETABSTR}${NEWBLOCKNAME}\n"  # Add tab + block name
    NEWBLOCKSTR+="${BASETABSTR}{\n"  # Add tab + opening brace
    for i in "${NEWBLOCKVALUES[@]}"; do
        ## Cut string in array at delimiter and store them as key/val
        NEWBLOCKDATA_KEY="$( echo "$i" | cut -d "${NEWBLOCKVALUESDELIM}" -f1 )"
        NEWBLOCKDATA_VAL="$( echo "$i" | cut -d "${NEWBLOCKVALUESDELIM}" -f2 )"

        NEWBLOCKDATA_KEY="$( safequoteVdfBlockName "$NEWBLOCKDATA_KEY" )"
        NEWBLOCKDATA_VAL="$( safequoteVdfBlockName "$NEWBLOCKDATA_VAL" )"

        NEWBLOCKSTR+="${BLOCKTABSTR}${NEWBLOCKDATA_KEY}"  # Add tab +  key
        NEWBLOCKSTR+="\t\t${NEWBLOCKDATA_VAL}\n"  # Add tab + val + newline
    done
    NEWBLOCKSTR+="${BASETABSTR}}"  # Add tab + closing brace

	writelog "INFO" "${FUNCNAME[0]} - Generated VDF block string '$NEWBLOCKSTR'"
	writelog "INFO" "${FUNCNAME[0]} - Writing out VDF block string to VDF file at '$VDF'"

	backupVdfFile "$VDF"

    ## Write out new string to calculated line in VDF file
    sed -i "${INSERTLINE}a\\${NEWBLOCKSTR}" "$VDF"
}

## Take in a VDF block and update a property in it, then update the original file with the updated block
## We can use this to update the compatibility tool for an existing VDF block, or update some Non-Steam Game properties
function editVdfSectionValue {
	VDFSECTION="$1"  # VDF section text i.e. from getNestedVdfSection
	VDFPROPERTYNAME="$2"  # i.e. 'OverlayAppEnable'
	VDFPROPERTYVAL="$3"  # i.e. '1'
	VDF="$4"

	VDFPROPERTYORGVAL="$( getVdfSectionValue "$VDFSECTION" "$VDFPROPERTYNAME" | sed 's/[]\/$*.^[]/\\&/g' )"
	VDFPROPERTYNEWVAL="$( createVdfPropertyString "${VDFPROPERTYNAME}" "${VDFPROPERTYVAL}" )"

	# maybe later, PR welcome if you can do this :-)
	#shellcheck disable=SC2001
	UPDATEDVDFSECTION="$( echo "${VDFSECTION}"| sed "s/${VDFPROPERTYORGVAL}/${VDFPROPERTYNEWVAL}/g" )"

	backupVdfFile "$VDF"
	substituteVdfSection "$VDFSECTION" "$UPDATEDVDFSECTION" "$VDF"
}

## Add a single value to bottom of a given VDF section
function addVdfSectionValue {
	VDFSECTION="$1"
	VDFPROPERTYNAME="$2"
	VDFPROPERTYVAL="$3"
	VDF="$4"

	VDFSECTIONEND="$( echo "$VDFSECTION" | tail -n1 )"
	VDFSECTIONENDLINE="$( echo "$VDFSECTION" | grep -in "$VDFSECTIONEND" | cut -d ':' -f1 )"
	VDFSECTIONINSERTLINE="$(( VDFSECTIONENDLINE - 1 ))"

	VDFSECTIONENDINDENTAMT="$( echo "$VDFSECTIONEND" | awk '{print gsub(/\t/,"")}' )"
	VDFPROPERTYINDENT="$( generateVdfIndentString "$(( VDFSECTIONENDINDENTAMT + 1 ))" "" )"

	VDFPROPERTY="${VDFPROPERTYINDENT}$( createVdfPropertyString "$VDFPROPERTYNAME" "$VDFPROPERTYVAL" )"
	UPDATEDVDFSECTION="$( echo "$VDFSECTION" | sed "${VDFSECTIONINSERTLINE}a\\${VDFPROPERTY}" )"

	substituteVdfSection "$VDFSECTION" "$UPDATEDVDFSECTION" "$VDF"
}

## Use parameter expansion to replace old block with new block in VDF file
## Thanks to StackOverflow for this answer, though was noted this may break down if the file exceeds 1mb -- Should work for us though
function substituteVdfSection {
	VDFOLDSECTION="$1"
	VDFNEWSECTION="$2"
	VDF="$3"

	VDFCONTENTS="$( cat "$VDF" )"
	UPDATEDVDFCONTENTS="${VDFCONTENTS//"$VDFOLDSECTION"/"$VDFNEWSECTION"}"
	printf "%s\n" "$UPDATEDVDFCONTENTS" > "$VDF"
}

## Extract value from text-based VDF block
## ex: "ExampleProperty"		"ex-val"
function getVdfSectionValue {
	VDFSECTION="$1"  # VDF section text i.e. from getNestedVdfSection
	VDFPROPERTYNAME="$2"  # i.e. 'OverlayAppEnable'
	ONLYVALUE="$3"

	VDFVAL="$( trimWhitespaces "$(echo "${VDFSECTION}" | grep "${VDFPROPERTYNAME}")" )"
	if [ -n "$ONLYVALUE" ]; then
		echo "$VDFVAL" | cut -f3
	else
		echo "$VDFVAL"
	fi
}

## Return a VDF property string
function createVdfPropertyString {
	if jq -e '.' 1>/dev/null 2>&1 <<<"$2"; then
		writelog "INFO" "${FUNCNAME[0]} - Looks like our input string '$2' is JSON -- Creating JSON VDF Property"
		printf "%s\t\t%s" "$( safequoteVdfBlockName "$1" )" "$( prepareJSONVdfProperty "$2" )"  # Don't use safequote on JSON string
	else
		writelog "INFO" "${FUNCNAME[0]} - Generating normal VDF property string for '$1: $2'"
		printf "%s\t\t%s" "$( safequoteVdfBlockName "$1" )" "$( safequoteVdfBlockName "$2" )"
	fi
}

## Format a JSON entry by double-escaping it and removing any surrounding quotes so that it can be written out into the VDF correctly
## i.e. turn "\"{\\\"foo\\\": \\\"bar\\\"}\"" -> \"{\\\"foo\\\": \\\"bar\\\"}\"
function prepareJSONVdfProperty {
	SANITISEDVDFJSON="$( jq '. | tojson | tojson' <<< "$1" )"
	SANITISEDVDFJSON="${SANITISEDVDFJSON#\"}"  # Remove any plain quote from start
	echo "${SANITISEDVDFJSON%\"}"  # Remove any plain quote from end
}

## Get the internal name of the compatibility tool selected for all titles from the Steam Client Compatibility settings
## ex: Proton 8.0-3 would return 'proton_8'
##
## Compatibility Tools, even ones that are Windows EXEs, are not given a compatibility tool by default, so this function can be used
## to select the one used by default for Windows games.
##
## This compatibility tool doesn't even have to be Proton.
function getGlobalSteamCompatToolInternalName {
	STEAMCOMPATTOOLSECTION="$( getVdfSection "CompatToolMapping" "" "" "$CFGVDF" )"  # Get CompatToolMapping section
	if [ -n "$STEAMCOMPATTOOLSECTION" ]; then
		printf "%s" "$STEAMCOMPATTOOLSECTION" > "/tmp/tmp.vdf"
    	GLOBALSTEAMCOMPATTOOLSECTION="$( getVdfSection "0" "" "" "/tmp/tmp.vdf" )"

		if [ -n "$GLOBALSTEAMCOMPATTOOLSECTION" ]; then
			echo "$GLOBALSTEAMCOMPATTOOLSECTION" | grep -i "name" | sed "s-\t- -g;s-\"name\"--g;s-\"--g" | xargs
		else
			writelog "SKIP" "${FUNCNAME[0]} - Could not find Global Compatibility Tool in CompatToolMapping in '$CFGVDF' - Giving up"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - Could not find CompatToolMapping section in '$CFGVDF' - Giving up"
	fi
}

# Takes an signed 32bit integer and converts it to an unsigned 32bit integer
# Steam uses this for Short UserIDs (userdata folder names) and Short Non-Steam Game AppIDs (Steam Shortcut Grid ID names)
function generateSteamShortID {
	echo $(( $1 & 0xFFFFFFFF ))
}

# Store loginusers data in CSV in in $STLSHM
# We parse this info out of loginusers.vdf which stores has blocks grouped by Long UserID
# We can convert down to the Short UserID from this.
#
# There should be a Short UserID folder in the SUSDA folder because each Steam User LongID in loginusers.vdf
# should also have a corresponding Short UserID userdata folder.
#
# Columns for now are as follows (we can extend in future if we need to):
# Long UserID,Short UserID,MostRecent
function fillLoginUsersCSV {
	# Don't overwrite LoginUsersCSV file if it exists and is not blank, only re-create it if SHM dir is cleared
	# The loginusers are not likely to change after the SHM dir is created so this is a bit more efficient
	if [ -f "$LOGINUSERSCSV" ] && [ -s "$LOGINUSERSCSV" ]; then
		writelog "INFO" "${FUNCNAME[0]} - '${LOGINUSERSCSV}' already exists -- Not re-creating"
		return
	fi

	# NOTE: For testing only
	# LOGUVDF="$HOME/.local/share/Steam/config/test_loginusers.vdf"

	# Toplevel block in loginusers.vdf is "users", get all block names ("[0-9]+" with one hardcoded indent, because we know we only have 1 indent)
	#
	# TODO it would be nice to have a generic function to get all toplevel VDF block names like this, but
	# We can't know the pattern and would need to know how to differentiate between a blockname and a property name
	# It just so happens for loginusers that it only contains blocks
	if [ -f "${LOGUVDF}" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Found loginusers file at '${LOGUVDF}' -- Will attempt to parse this file"
		mapfile -t LOGINUSERLONGIDS < <(getVdfSection '"users"' "" "" "${LOGUVDF}" | grep -oP '^\t"[0-9]+"' | tr -d '\t"\ ')
	else
		writelog "WARN" "${FUNCNAME[0]} - loginusers file at '${LOGUVDF}' does not exist! Will fall back to taking first Steam userdata folder as only login user"
	fi

	# If we couldn't parse loginusers.vdf, fall back to taking the first userdata folder we can find from the Steam userdata dir
	if [ "${#LOGINUSERLONGIDS}" -eq 0 ]; then
		writelog "WARN" "${FUNCNAME[0]} - Could not find any loginusers in '${LOGUVDF}', either loginusers file doesn't exist or did not return any parsable data -- Falling back to old method of grabbing first directory in '$SUSDA'"

		# If we don't have loginusers.vdf, we don't have the long UserID because you can't get the Long UserID from the Short UserID
		# The Short UserID uses bitwise AND which loses information
		#
		# In this case we just default to 1 (to avoid conflicting with the '0' userdata folder from Steam)
		# This should be fine as we never need the Long UserID
		LOGINUSERLONGID="1"

		# We used to do this in setSteamPaths before we tried to parse MostRecent login user
		LOGINUSERSHORTID="$( find "$SUSDA" -maxdepth 1 -type d -name "[1-9]*" | head -n1)"
		LOGINUSERSHORTID="${LOGINUSERSHORTID##*/}"

		# LOGINUSERLONGID="$( generateSteamShortID "${LOGINUSERLONGID}" )"
		LOGINUSERMOSTRECENT="1"  # Default to 1 as we would only have one loginuser in this case

		writelog "INFO" "${FUNCNAME[0]} - Writing loginuser '${LOGINUSERLONGID},${LOGINUSERSHORTID},${LOGINUSERMOSTRECENT}' to '${LOGINUSERSCSV}'"
		printf '%s;%s;%s\n' "${LOGINUSERLONGID}" "${LOGINUSERSHORTID}" "${LOGINUSERMOSTRECENT}" > "${LOGINUSERSCSV}"

		# Don't move onto below loop
		return
	fi

	# If we could parse loginusers.vdf, assume valid data and iterate over it, storing it in LoginUsersCSV
	for LOGINUSERLONGID in "${LOGINUSERLONGIDS[@]}"; do
		writelog "INFO" "the id is ${LOGINUSERLONGID}"

		LOGINUSERSHORTID="$( generateSteamShortID "${LOGINUSERLONGID}" )"
		LOGINUSERMOSTRECENT="0"  # Default MostRecent to 0

		# Check if user is most recent by trying to get the corresponding LongID block in loginusers.vdf
		# and then picking out the MostRecent field
		#
		# If we find any value for MostRecent in the Long UserID block, store it in LOGINUSERMOSTRECENT
		# This value should really only ever be 0 or 1
		LOGINUSERBLOCK="$( getVdfSection "${LOGINUSERLONGID}" "" "1" "${LOGUVDF}" )"
		if [ -n "${LOGINUSERBLOCK}" ]; then
			LOGINUSERMOSTRECENTVAL="$( getVdfSectionValue "${LOGINUSERBLOCK}" "MostRecent" "1" | tr -d '"' )"

			if [ -n "$LOGINUSERMOSTRECENTVAL" ]; then
				LOGINUSERMOSTRECENT="$LOGINUSERMOSTRECENTVAL"
			fi
		fi

		writelog "INFO" "${FUNCNAME[0]} - Writing loginuser '${LOGINUSERLONGID},${LOGINUSERSHORTID},${LOGINUSERMOSTRECENT}' to '${LOGINUSERSCSV}'"
		printf '%s;%s;%s\n' "${LOGINUSERLONGID}" "${LOGINUSERSHORTID}" "${LOGINUSERMOSTRECENT}"
	done >"$LOGINUSERSCSV"
}

function updateLocalConfigAppsValue {
	# Add key for specific AppID to localconfig.vdf's Apps section, creating the initial 'Apps' section if it doesn't exist
	# Used to set AllowOverlay and OpenVR when adding Non-Steam Games
	# Note that this may need reworked when we allow the user to select their Steam user

	LCVAID="$1"  # AppID for section name, i.e. '"-1123145"' (must be signed 32bit integer)
	LCVKEYNAME="$2"  # Key to write into section, i.e. '"OverlayAppEnable"'
	LCVKEYVAL="$3"  # Value to assign to key, i.e. '"1"'

	# This part in particular may need reworked if/when we add the option to select a Steam User
	if [ ! -f "$FLCV" ]; then
		writelog "WARN" "${FUNCNAME[0]} - No localconfig.vdf found at '${FLCV}' -- Nothing to do."
		return
	else
		writelog "INFO" "${FUNCNAME[0]} - Using localconfig.vdf (FLCV) file at '$FLCV'"
	fi

	# Get the "Apps" section in localconfig.vdf
	FLCVAPPSSECTION="$( getVdfSection "Apps" "" "1" "${FLCV}" )"
	if [ -z "$FLCVAPPSSECTION" ]; then
		writelog "INFO" "${FUNCNAME[0]} - ${FLCV} is missing 'Apps' section, creating it now"
		createVdfEntry "${FLCV}" "UserLocalConfigStore" "Apps" "" "0" ""
	else
		writelog "INFO" "${FUNCNAME[0]} - localconfig.vdf already has 'Apps' section, nothing to do"
	fi

	# Next we need to check if the given AppID
	FLCVAPPAID="$( getNestedVdfSection "Apps/${LCVAID}" "1" "$FLCV" )"
	if ! grep -q -- "$LCVAID" <<< "$FLCVAPPAID"; then
		# This case adds a new entry under the "Apps" section with the initial content: "LCVKEYNAME"		"LCVKEYVAL"
		writelog "INFO" "${FUNCNAME[0]} - No existing section in 'Apps' section for AppID '${LCVAID}' with key/value pair '${LCVKEYNAME}!${LCVKEYVAL}'"

		FLCVAPPAIDENTRY=( "${LCVKEYNAME}!${LCVKEYVAL}" )
		createVdfEntry "${FLCV}" "Apps" "${LCVAID}" "" "2" "" "${FLCVAPPAIDENTRY[@]}"
	else
		# This case is where the "AppID" section already exists under "Apps", so we want to add the value to the section if it doesn't exist,
		# or update the existing section
		writelog "INFO" "${FUNCNAME[0]} - 'Apps' section already has block for AppID '${LCVAID}', checking if the key '${LCVKEYNAME}' already exists"

		# Check if the "Apps/AppID" section has the given key already
		LCVFSECTIONVAL="$( getVdfSectionValue "${FLCVAPPAID}" "${LCVKEYNAME}" "1" )"
		writelog "INFO" "${FUNCNAME[0]} - LCVFSECTIONVAL is '$LCVFSECTIONVAL'"
		# editVdfSectionValue "${FLCVAPPAID}" "${LCVKEYNAME}" "${LCVKEYVAL}" "${FLCV}" "1"
		if [ -n "$LCVFSECTIONVAL" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Key '${LCVKEYNAME}' already exists in 'Apps/${LCVAID}' section, updating its value from '${LCVFSECTIONVAL}' to '${LCVKEYVAL}'"
			editVdfSectionValue "${FLCVAPPAID}" "${LCVKEYNAME}" "${LCVKEYVAL}" "${FLCV}"
		else
			writelog "INFO" "${FUNCNAME[0]} - Key '${LCVKEYNAME}' does not exist in 'Apps/${LCVAID}' section, adding it now with value '${LCVKEYVAL}'"
			addVdfSectionValue "${FLCVAPPAID}" "${LCVKEYNAME}" "${LCVKEYVAL}" "${FLCV}"
		fi
	fi
}

### END TEXT-BASED VDF INTERACTION FUNCTIONS

function startSettings {
	FUSEID "$1"

	writelog "INFO" "${FUNCNAME[0]} - createProtonList:"
	createProtonList X

	writelog "INFO" "${FUNCNAME[0]} - openTrayIcon:"
	openTrayIcon

	writelog "INFO" "${FUNCNAME[0]} - MainMenu:"
	MainMenu "$USEID"

	writelog "INFO" "${FUNCNAME[0]} - cleanYadLeftOvers:"
	cleanYadLeftOvers
}

function retBool {
	if [ "$1" == "TRUE" ]; then
		echo "1"
	else
		echo "0"
	fi
}

function SteamCatSelect {
	writelog "INFO" "${FUNCNAME[0]} - Steam Collection Selection"
	export CURWIKI="$PPW/Steam-Collections"
	TITLE="${PROGNAME}-SteamCollectionSelection"
	pollWinRes "$TITLE"

	setShowPic
	unset VALTAGS
	mapfile -d "\n" -t -O "${#VALTAGS[@]}" VALTAGS <<< "$(getActiveSteamCollections | grep -v "^rt[A-Z]")"
	SCATSELOUT="$(while read -r f; do if [[ ! "${SCATSEL[*]}" =~ $f ]]; then 	echo FALSE ; echo "$f"; else echo TRUE ; echo "$f" ;fi ; done <<< "$(printf "%s\n" "${VALTAGS[@]}")" | \
	"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center "$WINDECO" --list --checklist --column="" --column="Steam Collection" --separator="\n" --print-column="2" \
	--text="$(spanFont "$GUI_STEAMCATSEL" "H")" --title="$TITLE" --button="$BUT_SEL":0 --button="$BUT_CAN":2 "$GEOM")"

	case $? in
		0)  {
		writelog "INFO" "${FUNCNAME[0]} - Selected Select"
				if [ -n "$SCATSELOUT" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Selected following Collections: '$(sort -u <<< "$SCATSELOUT" | sed '/^$/d' | tr '\n' ',')'"
					unset SCATSEL
					mapfile -d "\n" -t -O "${#SCATSEL[@]}" SCATSEL <<< "$(sort -u <<< "$SCATSELOUT" | sed '/^$/d')"
				fi
			}
		;;
		2)  writelog "INFO" "${FUNCNAME[0]} - Selected CANCEL"
		;;
	esac

	goBackToPrevFunction "${FUNCNAME[0]}" "$2"
}

function AutoMarkSCat {
	writelog "INFO" "${FUNCNAME[0]} - Auto-marking specific Steam Collections"

	if ! grep -q "$DESC_NOST" <<< "$(printf "%s" "${SCATSEL[@]}" | tr '\n' ',')" && grep -q "$DESC_NOST" <<< "$(getActiveSteamCollections | tr '\n' ',')"; then
		writelog "INFO" "${FUNCNAME[0]} - Marking '$DESC_NOST' as Collection"
		mapfile -d "\n" -t -O "${#SCATSEL[@]}" SCATSEL <<< "$DESC_NOST"
	fi

	function maybeLater {
		if [ -f "$AUTOADDSCATLIST" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Marking everything found in '$AUTOADDSCATLIST' as Steam Collection"
			while read -r line; do
				mapfile -d "\n" -t -O "${#SCATSEL[@]}" SCATSEL <<< "$line"
			done < "$AUTOADDSCATLIST"
		else
			writelog "INFO" "${FUNCNAME[0]} - No file '$AUTOADDSCATLIST' containing Steam Collections found for adding"
		fi
	}
}

function filterUnwantedSteamCategories {
	FILTEREDTAGS="$(grep -v "^rt[A-Z]" <<< "$1")"
	printf "%s" "${FILTEREDTAGS[@]}" | tr '\n' '!' | sed 's/\!*$//g'
}

# Download icon for Non-Steam Game using SteamGridDB Game ID (can't set icon for Steam Native games)
function getSteamGridDBNonSteamIcon {
	NOSTICONAID="$1"  # Non-Steam AppID
	NOSTSGDBID="$2"  # SteamGridDB Game ID
	NOSTICONNAME="${NOSTICONAID}_icon"
	SGDBSEARCHENDPOINT_ICONS="${BASESTEAMGRIDDBAPI}/icons/game"

	# Download icon and put it in Steam grids folder, which should be a safe and intuitive location
	# We don't have any way to set search settings for icons and it would be confusing to have this in the Global Menu for now, so just leave blank
	# In future if we have Non-Steam Game global settings, we could include icon settings there too
	downloadArtFromSteamGridDB "$NOSTSGDBID" "$SGDBSEARCHENDPOINT_ICONS" "${NOSTICONNAME}" "" "" "" "" "" "" "replace" "1"
}

function addNonSteamGameGui {
	writelog "INFO" "${FUNCNAME[0]} - Starting the Gui for adding a $NSGA to Steam"

	# defaults
	if [ -n "$1" ]; then
		for i in "$@"; do
			case $i in
				-ep=*|--exepath=*)
				NOSTGEXEPATH="${i#*=}";
				shift
				;;
			esac
		done

		NOSTGAPPNAME="${NOSTGEXEPATH##*/}"
		NOSTGSTDIR="${NOSTGEXEPATH%/*}"
	fi

	if grep -q "^NOSTEAMSTLDEF=\"1\"" "$STLDEFGLOBALCFG"; then  # icon default
		NOSTGICONPATH="$STLICON"
	else
		NOSTGICONPATH=""
	fi
	NOSTGHIDE=0
	NOSTGADC=1
	NOSTGAO=1
	NOSTGVR=0
	unset VALTAGS
	mapfile -d "\n" -t -O "${#VALTAGS[@]}" VALTAGS <<< "$(getActiveSteamCollections | sed '/^$/d')"
	VALIDTAGS="$(filterUnwantedSteamCategories "${VALTAGS[@]}")"
	SGASETACTIONS="copy!link!move"

	AutoMarkSCat

	if [[ -v SCATSEL[@] ]]; then
		NOSTTAGS="$(filterUnwantedSteamCategories "${SCATSEL[@]}")"
	fi
	export CURWIKI="$PPW/Add-Non-Steam-Game"
	TITLE="${PROGNAME}-$NSGA"
	pollWinRes "$TITLE"

	# Generate list of Proton versions excluding Proton versions only available to SteamTinkerLaunch
	# May cause problems with symlinked Proton versions, unsure, unusual case anyway
	NSGPROTLIST=( "$NON" "default" )
	for STLKNOWNPROT in "${ProtonCSV[@]}"; do
		STLKNOWNPROTNAM="$( echo "$STLKNOWNPROT" | cut -d ';' -f1 )"
		STLKNOWNPROTPATH="$( echo "$STLKNOWNPROT" | cut -d ';' -f2 )"

		STLKNOWNPROTDIR="$( dirname "$STLKNOWNPROTPATH" )"
		if [[ $STLKNOWNPROTDIR = $STLCFGDIR* ]]; then
			writelog "SKIP" "${FUNCNAME[0]} - Proton version '$STLKNOWNPROTNAM' is in SteamTinkerLaunch config directory at '$STLKNOWNPROTPATH' -- This is not known by Steam, so skipping"
		elif [[ $STLKNOWNPROTDIR = $CUSTPROTEXTDIR* ]] && [[ $CUSTPROTEXTDIR != "$STEAMCOMPATOOLS" ]]; then
			writelog "INFO" "${FUNCNAME[0]} - Proton version '$STLKNOWNPROTNAM' is in SteamTinkerLaunch Custom Proton dir '$CUSTPROTEXTDIR' on path '$STLKNOWNPROTPATH' -- This path is not the Steam Compatibility Tool directory and so is not known by Steam, so skipping"
		else
			writelog "INFO" "${FUNCNAME[0]} - Proton version '$STLKNOWNPROTNAM' looks like it should be known by Steam as it is not in any SteamTinkerLaunch-specific folders on path '$STLKNOWNPROTPATH'"
			NSGPROTLIST+=("$STLKNOWNPROTNAM")
		fi
	done
	NSGPROTLIST+=( "$PROGINTERNALPROTNAME" "steamlinuxruntime" )

	NSGPROTYADLIST="$(printf "!%s\n" "${NSGPROTLIST[@]//\"/}" | sort -u | cut -d ';' -f1 | tr -d '\n' | sed "s:^!::" | sed "s:!$::")"

	## Language strings for artwork section were re-used from setGameArt
	NSGSET="$("$YAD" --f1-action="$F1ACTION" --window-icon="$STLICON" --form --scroll --center --on-top "$WINDECO" \
	--title="$TITLE" --separator="|" \
	--text="$(spanFont "$GUI_ADDNSG" "H")\n<i>$(strFix "$GUI_WARNNSG1" "$STERECO")</i>" \
	--field=" ":LBL " " \
	--field="$(spanFont "$GUI_NOSTGPATHS" "H")":LBL " " \
	--field="     $GUI_NOSTGAPPNAME!$DESC_NOSTGAPPNAME ('NOSTGAPPNAME')" "${NOSTGAPPNAME/#-/ -}" \
	--field="     $GUI_NOSTGEXEPATH!$DESC_NOSTGEXEPATH ('NOSTGEXEPATH')":FL "${NOSTGEXEPATH/#-/ -}" \
	--field="     $GUI_NOSTGSTDIR!$DESC_NOSTGSTDIR ('NOSTGSTDIR')":DIR "${NOSTGSTDIR/#-/ -}" \
	--field="$(spanFont "$GUI_NOSTGGAMEART" "H")":LBL " " \
	--field="     $GUI_NOSTGICONPATH!$DESC_NOSTGICONPATH ('NOSTGICONPATH')":FL "${NOSTGICONPATH/#-/ -}" \
	--field="     $GUI_SGAHERO!$DESC_SGAHERO ('NOSTGHERO')":FL "${NOSTGHERO/#-/ -}" \
	--field="     $GUI_SGALOGO!$DESC_SGALOGO ('NOSTGLOGO')":FL "${NOSTGLOGO/#-/ -}" \
	--field="     $GUI_SGABOXART!$DESC_SGABOXART ('NOSTGBOXART')":FL "${NOSTGBOXART/#-/ -}" \
	--field="     $GUI_SGATENFOOT!$DESC_SGATENFOOT ('NOSTGTENFOOT')":FL "${NOSTGTENFOOT/#-/ -}" \
	--field="     $GUI_SGASETACTION!$DESC_SGASETACTION ('NOSTGSETACTION')":CB "$( cleanDropDown "copy" "$SGASETACTIONS" )" \
	--field="     $GUI_NOSTGEXEARTWORKFALLBACK!$DESC_NOSTGEXEARTWORKFALLBACK ('NOSTGEXEARTWORKFALLBACK')":CHK "${NOSTGEXEARTWORKFALLBACK/#-/ -}" \
	--field="$(spanFont "$GUI_NOSTSGDB" "H")":LBL " " \
	--field="     $GUI_NOSTUSESGDB!$DESC_NOSTUSESGDB ('NOSTUSESGDB')":CHK "${NOSTUSESGDB/#-/ -}" \
	--field="     $GUI_NOSTSGDBSAID!$DESC_NOSTSGDBSAID ('NOSTSGDBSAID')" "${NOSTSGDBSAID/#-/ -}" \
	--field="     $GUI_NOSTSGDBAID!$DESC_NOSTSGDBAID ('NOSTSGDBAID')" "${NOSTSGDBAID/#-/ -}" \
	--field="     $GUI_NOSTSGDBSNAME!$DESC_NOSTSGDBSNAME ('NOSTSGDBSNAME')" "${NOSTSGDBSNAME/#-/ -}" \
	--field="$(spanFont "$GUI_NOSTGPROPS" "H")":LBL " " \
	--field="     $GUI_NOSTGCOMPATTOOL!$DESC_NOSTGCOMPATTOOL ('NOSTCOMPATTOOL')":CBE "$( cleanDropDown "$NON" "$NSGPROTYADLIST" )" \
	--field="     $GUI_NOSTGLAOP!$DESC_NOSTGLAOP ('NOSTGLAOP')" "${NOSTGLAOP/#-/ -}" \
	--field="     $GUI_NOSTTAGS!$DESC_NOSTTAGS ('NOSTTAGS')":CBE "$(cleanDropDown "${NOSTTAGS/#-/ -}" "$VALIDTAGS")" \
	--field="     $GUI_NOSTGHIDE!$DESC_NOSTGHIDE ('NOSTGHIDE')":CHK "${NOSTGHIDE/#-/ -}" \
	--field="     $GUI_NOSTGADC!$DESC_NOSTGADC ('NOSTGADC')":CHK "${NOSTGADC/#-/ -}" \
	--field="     $GUI_NOSTGAO!$DESC_NOSTGAO ('NOSTGAO')":CHK "${NOSTGAO/#-/ -}" \
	--field="     $GUI_NOSTGVR!$DESC_NOSTGVR ('NOSTGVR')":CHK "${NOSTGVR/#-/ -}" \
	--button="$BUT_CAN":0 --button="$BUT_TAGS":2 --button="$BUT_CREATE":4 "$GEOM")"

	case $? in
		0)  writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_CAN'"
		;;
		2)  writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_TAGS"
			SteamCatSelect "$NON" "${FUNCNAME[0]}"
		;;
		4)  writelog "INFO" "${FUNCNAME[0]} - Selected '$BUT_CREATE'"
			if [ -n "$NSGSET" ]; then
				mapfile -d "|" -t -O "${#NSGSETARR[@]}" NSGSETARR < <(printf '%s' "$NSGSET")

				writelog "INFO" "${FUNCNAME[0]} - The Non-Steam Game args are ${NSGSETARR[*]}"
				# NSGSETARR[0] is blank space
				# NSGSETARR[1] is Paths heading
				NOSTGAPPNAME="${NSGSETARR[2]}"
				NOSTGEXEPATH="${NSGSETARR[3]}"
				NOSTGSTDIR="${NSGSETARR[4]}"
				# NSGSETARR[5] is Artwork heading
				NOSTGICONPATH="${NSGSETARR[6]}"
				NOSTGHERO="${NSGSETARR[7]}"
				NOSTGLOGO="${NSGSETARR[8]}"
				NOSTGBOXART="${NSGSETARR[9]}"
				NOSTGTENFOOT="${NSGSETARR[10]}"
				NOSTGSETACTION="${NSGSETARR[11]}"
				NOSTGEXEARTWORKFALLBACK="$( retBool "${NSGSETARR[12]}" )"
				# NSGSETARR[13] is the SteamGridDB heading
				NOSTUSESGDB="$( retBool "${NSGSETARR[14]}" )"
				NOSTSGDBSAID="${NSGSETARR[15]}"
				NOSTSGDBAID="${NSGSETARR[16]}"
				NOSTSGDBSNAME="${NSGSETARR[17]}"
				# NSGSETARR[18] is Properties heading
				NOSTCOMPATTOOL="${NSGSETARR[19]}"
				NOSTGLAOP="${NSGSETARR[20]}"
				NOSTTAGS="${NSGSETARR[21]}"
				NOSTGHIDE="$( retBool "${NSGSETARR[22]}" )"
				NOSTGADC="$( retBool "${NSGSETARR[23]}" )"
				NOSTGAO="$( retBool "${NSGSETARR[24]}" )"
				NOSTGVR="$( retBool "${NSGSETARR[25]}" )"

				## ignore none compatibility tool
				if [[ "$NOSTCOMPATTOOL" = "$NON" ]]; then
					NOSTCOMPATTOOL=""
				fi

				NOSTARTEXECMD=""
				if [ "$NOSTGEXEARTWORKFALLBACK" -eq 1 ]; then
					NOSTARTEXECMD="--auto-artwork"
				fi

				if [ "$NOSTUSESGDB" -eq 0 ]; then
					# Ignore SteamGridDB values if SteamGridDB not enabled - Blank values means that function will ignore SteamGridDB entirely
					# We still need the checkbox so we can tell the addNonSteamGame function whether we want to fall back on Non-Steam Game Name
					NOSTSGDBAID=""
					NOSTSGDBSAID=""
					NOSTSGDBSNAME=""
				elif [[ ( -z "$NOSTSGDBAID" && -z "$NOSTSGDBSAID" && -z "$NOSTSGDBSNAME" ) && "$NOSTUSESGDB" -eq 1 ]]; then
					# If enabled SteamGridDB but did NOT pass any values, enable SteamGridDB which will allow us to search on Game Name
					# UI-specific logic that doesn't apply to commandline usage, since on commandline a user would manually pass this flag
					# only need to pass this flag if other search options are not set, as if one of these is set, we automatically enable SteamGridDB search
					NOSTGUIUSESGDB="--use-steamgriddb"
				fi

				## Arguments here like -hr, -lg, etc are made to match setGameArt
				writelog "INFO" "${FUNCNAME[0]} - addNonSteamGame -an=\"$NOSTGAPPNAME\" -ep=\"$NOSTGEXEPATH\" -sd=\"$NOSTGSTDIR\" -ip=\"$NOSTGICONPATH\" -lo=\"$NOSTGLAOP\" -hd=\"$NOSTGHIDE\" -adc=\"$NOSTGADC\" -ao=\"$NOSTGAO\" -vr=\"$NOSTGVR\" -t=\"$NOSTTAGS\" -ct=\"$NOSTCOMPATTOOL\" -hr=\"$NOSTGHERO\" -lg=\"$NOSTGLOGO\" -ba=\"$NOSTGBOXART\" -tf=\"$NOSTGTENFOOT\" \"--${NOSTGSETACTION}\" \"$NOSTARTEXECMD\" -sgai=\"$NOSTSGDBSAID\" -sgid=\"$NOSTSGDBAID\" -sgnm=\"$NOSTSGDBSNAME\" \"$NOSTGUIUSESGDB\""
				addNonSteamGame -an="$NOSTGAPPNAME" -ep="$NOSTGEXEPATH" -sd="$NOSTGSTDIR" -ip="$NOSTGICONPATH" -lo="$NOSTGLAOP" -hd="$NOSTGHIDE" -adc="$NOSTGADC" -ao="$NOSTGAO" -vr="$NOSTGVR" -t="$NOSTTAGS" -ct="$NOSTCOMPATTOOL" -hr="$NOSTGHERO" -lg="$NOSTGLOGO" -ba="$NOSTGBOXART" -tf="$NOSTGTENFOOT" "--${NOSTGSETACTION}" "$NOSTARTEXECMD" -sgai="$NOSTSGDBSAID" -sgid="$NOSTSGDBAID" -sgnm="$NOSTSGDBSNAME" "$NOSTGUIUSESGDB"
			fi
		;;
	esac
}

function findNonSteamGameIcon {
	find "${STUIDPATH}/config/grid/" -name "${NOSTAIDGRID}_icon.*" | head -n1 2>/dev/null
}

function addNonSteamGame {
	if [ -z "$SUSDA" ] || [ -z "$STUIDPATH" ]; then
		setSteamPaths
	fi
	SCPATH="$STUIDPATH/config/$SCVDF"

	function checkValidVDFBoolean {
		[ "$1" -eq 1 ] || [ "$1" -eq 0 ] && echo "$1"
	}

	function getCRC {
		echo -n "$1" | gzip -c | tail -c 8 | od -An -N 4 -tx4
	}

	function hex2dec {
		printf "%d\n" "0x${1#0x}"
	}

	## How Non-Steam AppIDs work, because it took me almost a year to figure this out
	## ----------------------
	## Steam stores shortcuts in a binary 'shortcuts.vdf', at SROOT/userdata/<id>/config
	##
	## Non-Steam AppIDs are 32bit little-endian (reverse byte order) signed integers, stored as hexidecimal
	## This is probably generated using a crc32 generated from AppName + Exe, but it can actually be anything
	## Steam likely does this to ensure "uniqueness" among entries, tools like Steam-ROM-Manager do the same thing likely for similar reasons
	##
	## For simplicity we generate a random 32bit signed integer using an md5, which we'll then convert to hex to store in the AppID file
	## Though we can write any AppID we want, Steam will reject invalid ones (i.e. big endian hex) it will overwrite our AppID
	## We can also convert this to an unsigned 32bit integer to get the AppID used for grids and other things, the unsigned int is just what Steam stores
	##
	## We can later re-use these functions to do several things:
	## - Check for and remove stray STL configs for no longer stored Non-Steam Game AppIDs (if we had Non-Steam Games we previously used with STL that we no longer use, we can remove these configs in case there is a conflict in future)

	### BEGIN MAGIC APPID FUNCTIONS
	## ----------
	# Generate random signed 32bit integer which can be converted into hex, using the first argument (AppName and Exe fields) as seed (in an attempt to reduce the chances of the same AppID being generated twice)
	function generateShortcutVDFAppId {
		seed="$( echo -n "$1" | md5sum | cut -c1-8 )"
		echo "-$(( 16#${seed} % 1000000000 ))"
	}

	function dec2hex {
		printf '%x\n' "$1" | cut -c 9-  # cut removes the 'ffffffff' from the string (represents the sign) and starts from the 9th character
	}

	# Takes big-endian ("normal") hexidecimal number and converts to little-endian
	function bigToLittleEndian {
		echo -n "$1" | tac -rs .. | tr -d '\n'
	}

	# Takes an signed 32bit integer and converts it to a 4byte little-endian hex number
	function generateShortcutVDFHexAppId {
		bigToLittleEndian "$( dec2hex "$1" )"
	}

	## ----------
	### END MAGIC APPID FUNCTIONS

	function splitTags {
		mapfile -d "," -t -O "${#TAGARR[@]}" TAGARR < <(printf '%s' "$1")
		for i in "${!TAGARR[@]}"; do
			if grep -q "${TAGARR[$i]}" <<< "$(getActiveSteamCollections)"; then
				printf '\x01%s\x00%s\x00' "$i" "${TAGARR[i]}"
			fi
		done
	}

	## Return first image file matching passed name (i.e "hero") in game EXE dir - Used to find named artwork files in the game EXE folder for Non-Steam Games as a fallback if no artwork is provided
	function findGameArtInExeDir {
		NOSTSEARCHDIR="$1"
		NOSTARTFILENAME="${2%%.*}"  # e.x. "hero", "logo"
		NOSTORGFILENAME="${3}"  # Used to return in case no artwork is found, so the original name is used as a fallback

		NOSTFOUNDARTWORK="$( realpath "$( find "$NOSTSEARCHDIR" -name "$NOSTARTFILENAME.*" | head -n1 )" 2>/dev/null )"
		if grep -q "image data" <<< "$( file "$NOSTFOUNDARTWORK" )"; then
			echo "$NOSTFOUNDARTWORK"
		else
			echo "$NOSTORGFILENAME"
		fi
	}

	NOSTHIDE=0  # Set in localconfig.vdf along with tags and overlay settings
	NOSTADC=1
	NOSTAO=1
	NOSTVR=0
	NOSTSTLLO=0
	NOSTAUTOARTWORK=0
	NOSTUSESGDB=0
	for i in "$@"; do
		case $i in
			## General Non-Steam Game properties
			-an=*|--appname=*)
				NOSTAPPNAME="${i#*=}"
				shift ;;
			-ep=*|--exepath=*)
				QEP="${i#*=}";
				if [ -n "$QEP" ]; then
					NOSTEXEPATH="\"$QEP\""
				fi
				shift ;;
			-sd=*|--startdir=*)
				QSD="${i#*=}"
				if [ -n "$QSD" ] && [ -d "$QSD" ]; then
					NOSTSTDIR="\"$QSD\""
				fi
				shift ;;
			-ip=*|--iconpath=*)
				NOSTICONPATH="${i#*=}"
				shift ;;
			-lo=*|--launchoptions=*)
				NOSTLAOP="${i#*=}"
				shift ;;
			-hd=*|--hide=*)
				NOSTHIDE="$( checkValidVDFBoolean "${i#*=}" )"
				shift ;;
			-adc=*|--allowdesktopconf=*)
				NOSTADC="$( checkValidVDFBoolean "${i#*=}" )"
				shift ;;
			-ao=*|--allowoverlay=*)
				NOSTAO="$( checkValidVDFBoolean "${i#*=}" )"
				shift ;;
			-vr=*|--openvr=*)
				NOSTVR="$( checkValidVDFBoolean "${i#*=}" )"
				shift ;;
			-t=*|--tags=*)
				NOSTTAGS="${i#*=}"
				shift ;;
			-stllo=*|--stllaunchoption=*)
				NOSTSTLLO="${i#*=}"
				shift ;;
			-ct=*|--compatibilitytool=*)
				## Get path based on passed name from ProtonCSV, then build argument needed for getProtonInternalName
				NOSTCOMPATTOOL=""
				NOSTCOMPATTOOLNAME="${i#*=}"
				if [ -n "$NOSTCOMPATTOOLNAME" ]; then
					if [[ "$NOSTCOMPATTOOLNAME" == "default" ]]; then  # Default fetches the global Steam compat tool
						GLOBALSTEAMCOMPATTOOL="$( getGlobalSteamCompatToolInternalName )"
						if [ -n "$GLOBALSTEAMCOMPATTOOL" ]; then
							NOSTCOMPATTOOL="$GLOBALSTEAMCOMPATTOOL"
						else
							writelog "INFO" "${FUNCNAME[0]} - Selected 'default' compatibility tool but could not find one in '$CFGVDF' -- Not writing compatibility tool for Non-Steam Game"
						fi
					else
						NOSTCOMPATTOOLPATH="$( getProtPathFromCSV "$NOSTCOMPATTOOLNAME" )"

						if [ -n "$NOSTCOMPATTOOLPATH" ]; then
							NOSTCOMPATTOOL="$( getProtonInternalName "${NOSTCOMPATTOOLNAME};${NOSTCOMPATTOOLPATH}" )"
						else  # i.e. if 'luxtorpeda' was passed, we don't have a path for this, so simply trust the user
							writelog "SKIP" "${FUNCNAME[0]} - Could not get Proton path for given Compatibility Tool '$NOSTCOMPATTOOLNAME' from ProtonCSV -- Simply assuming this internal name is valid and not known to SteamTinkerLaunch"
							NOSTCOMPATTOOL="$NOSTCOMPATTOOLNAME"
						fi
					fi
				else
					writelog "SKIP" "${FUNCNAME[0]} - Compatibility Tool name argument was passed, but was empty '$NOSTCOMPATTOOLNAME' -- Skipping"
				fi

				shift ;;
			-hr=*|--hero=*)
				NOSTGHERO="${i#*=}"  # <appid>_hero.png -- Banner used on game screen, logo goes on top of this
				shift ;;
			-lg=*|--logo=*)
				NOSTGLOGO="${i#*=}"  # <appid>_logo.png -- Logo used e.g. on game screen
				shift ;;
			-ba=*|--boxart=*)
				NOSTGBOXART="${i#*=}"  # <appid>p.png -- Used in library
				shift ;;
			-tf=*|--tenfoot=*)
				NOSTGTENFOOT="${i#*=}"  # <appid>.png -- Used as small boxart for e.g. most recently played banner
				shift ;;
			--auto-artwork)
				NOSTAUTOARTWORK=1  # Look for artwork with matching names from game EXE folder (hero/logo/boxart/tenfoot.png/jpg/jpeg/gif)
				shift ;;
			## SteamGridDB artwork options
			--use-steamgriddb)
				NOSTUSESGDB=1  # Commandline usage option so a user can tell us to search SteamGridDB using 'NOSTAPPNAME' -- If they pass any other values this will be enabled anyway
				shift ;;
			--steamgriddb-game-id=*|-sgid=*)
				NOSTSGDBGAMEID="${i#*=}"  # SteamGridDB Game ID to search for grids on (optional)
				shift ;;
			--steamgriddb-steam-appid=*|-sgai=*)
				NOSTSGDBSTAID="${i#*=}"  # Steam Game AppID to search for grids on (optional)
				shift ;;
			--steamgriddb-game-name=*|-sgnm=*)
				NOSTSGDBNAM="${i#*=}"  # Game Name to Search SteamGridDB on (will look for name on SteamGridDB, return the SteamGridDB Game ID, aand search on that) (optional)
				shift ;;
			## Used to pass to setGameArt to define how we want to set game artwork (essentially giving a Non-Steam Game UI the functionality of setGameArt since we call it here anyway)
			--copy)
				SGACOPYMETHOD="--copy"  # Copy file to grid folder -- Default
				shift ;;
			--link)
				SGACOPYMETHOD="--link"  # Symlink file to grid folder
				shift ;;
			--move)
				SGACOPYMETHOD="--move"  # Move file to grid folder
				shift ;;
			*) ;;
		esac
	done

	# Ensure we stop without valid EXE -- EXE is the only *required* field, all others can be inferred from it
	# We check against the string with quotes removed for the -z check even though the case above should match it - We have to do this to have a valid path for '-f' check
	NOSTEXEPATHNOQUOTE="$( sed -e 's/^"//' -e 's/"$//' <<< "${NOSTEXEPATH}" )"
	if [ -z "${NOSTEXEPATHNOQUOTE}" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - NOSTEXEPATH was blank, could not add Non-Steam Game -- Aborting!"
		notiShow "${NOTY_NOSTEXEBLANK}"
		echo "Error: Could not add Non-Steam Game -- Executable path was not provided"
		return 1
	elif [ ! -f "${NOSTEXEPATHNOQUOTE}" ]; then
		writelog "ERROR" "${FUNCNAME[0]} - NOSTEXEPATH is not a valid file, could not add Non-Steam Game -- Aborting!"
		notiShow "${NOTY_NOSTEXENOTFOUND}"
		echo "Error: Could not add Non-Steam Game -- Executable is not a valid file!"
		return 1
	fi

	NOSTAPPNAME="${NOSTAPPNAME:-${QEP##*/}}"

	if [ -z "${NOSTSTDIR}" ] || [ ! -d "${NOSTSTDIR}" ]; then
		QSD="$(dirname "$QEP")"; NOSTSTDIR="\"$QSD\""
	fi

	if [ "$NOSTSTLLO" -eq 1 ]; then NOSTGICONPATH="$STLICON"; fi

	# This is formatted as a flag because we can pass "$SGACOPYMETHOD" as an argument to setGameArt, and it will be interpreted as --copy
	SGACOPYMETHOD="${SGACOPYMETHOD:---copy}"

	# off by default but always passed from addNonSteamGame, so if we actually get a value for any of these fields, enable it
	# NOSTUSESGDB=1 flag is for user commandline usage, when they search SteamGridDB for artwork using game name, but don't want to pass other values
	if [ -n "$NOSTSGDBSTAID" ] || [ -n "$NOSTSGDBGAMEID" ] || [ -n "$NOSTSGDBNAM" ]; then NOSTUSESGDB=1; fi

	## These AppIDs are not necessarily guaranteed to be unique, i.e. if the user tries to add the same game twice or something
	## In future we could do a stricter check by attempting to parse the shortcuts.vdf file and re-generating the AppID if it already exists - could be expensive though
	NOSTAIDVDF="$( generateShortcutVDFAppId "${NOSTAPPNAME}${NOSTEXEPATH}" )"  # signed integer AppID, stored in the VDF as hexidecimal - ex: -598031679
	NOSTAIDVDFHEX="$( generateShortcutVDFHexAppId "$NOSTAIDVDF" )"  # 4byte little-endian hexidecimal of above 32bit signed integer, which we write out to the binary VDF - ex: c1c25adc
	NOSTAIDVDFHEXFMT="\x$(awk '{$1=$1}1' FPAT='.{2}' OFS="\\\x" <<< "$NOSTAIDVDFHEX")"  # binary-formatted string hex of the above which we actually write out - ex: \xc1\xc2\x5a\xdc
	NOSTAIDGRID="$( generateSteamShortID "$NOSTAIDVDF" )"  # unsigned 32bit ingeger version of "$NOSTAIDVDF", which is used as the AppID for Steam artwork ("grids"), as well as for our shortcuts

	writelog "INFO" "${FUNCNAME[0]} - === Adding new $NSGA ==="
	writelog "INFO" "${FUNCNAME[0]} - Signed Integer Shortcut AppID: '${NOSTAIDVDF}'"
	writelog "INFO" "${FUNCNAME[0]} - 4byte Little-Endian Hex AppID: '${NOSTAIDVDFHEX}'"
	writelog "INFO" "${FUNCNAME[0]} - Binary-formatted 4byte Little-Endian AppID: '${NOSTAIDVDFHEXFMT}'"
	writelog "INFO" "${FUNCNAME[0]} - Unsigned Integer Shortcut AppID (used for artwork): '${NOSTAIDGRID}'"
	writelog "INFO" "${FUNCNAME[0]} - App Name: '${NOSTAPPNAME}'"
	writelog "INFO" "${FUNCNAME[0]} - Exe Path: '${NOSTEXEPATH}'"
	writelog "INFO" "${FUNCNAME[0]} - Start Dir: '${NOSTSTDIR}'"
	writelog "INFO" "${FUNCNAME[0]} - Icon Path: '${NOSTICONPATH}'"
	writelog "INFO" "${FUNCNAME[0]} - Launch options: '${NOSTLAOP}'"
	writelog "INFO" "${FUNCNAME[0]} - Is Hidden: '${NOSTHIDE}'"
	writelog "INFO" "${FUNCNAME[0]} - Allow Desktop Config: '${NOSTADC}'"
	writelog "INFO" "${FUNCNAME[0]} - Allow Overlay: '${NOSTAO}'"
	writelog "INFO" "${FUNCNAME[0]} - OpenVR: '${NOSTVR}'"
	writelog "INFO" "${FUNCNAME[0]} - Tags: '${NOSTTAGS}'"
	writelog "INFO" "${FUNCNAME[0]} - Compatibility Tool: '${NOSTCOMPATTOOL}'"
	## Artwork logging -- These will be blank if no artwork is passed, that's OK
	writelog "INFO" "${FUNCNAME[0]} - Hero Artwork: '${NOSTGHERO}'"
	writelog "INFO" "${FUNCNAME[0]} - Logo Artwork: '${NOSTGLOGO}'"
	writelog "INFO" "${FUNCNAME[0]} - Boxart Artwork: '${NOSTGBOXART}'"
	writelog "INFO" "${FUNCNAME[0]} - Tenfoot Artwork: '${NOSTGTENFOOT}'"
	writelog "INFO" "${FUNCNAME[0]} - Copy Method for Artwork: '${SGACOPYMETHOD}'"
	writelog "INFO" "${FUNCNAME[0]} - EXE Dir Fallback Artwork: '${NOSTGEXEARTWORKFALLBACK}'"
	## SteamGridDB logging -- Also might be blank and that's fine
	writelog "INFO" "${FUNCNAME[0]} - Use SteamGridDB: '${NOSTUSESGDB}'"
	writelog "INFO" "${FUNCNAME[0]} - SteamGridDB Game ID: '${NOSTSGDBGAMEID}'"
	writelog "INFO" "${FUNCNAME[0]} - SteamGridDB Steam AppID: '${NOSTSGDBAID}'"
	writelog "INFO" "${FUNCNAME[0]} - SteamGridDB Search Name: '${NOSTSGDBNAM}'"

	if [ -f "$SCPATH" ]; then
		writelog "INFO" "${FUNCNAME[0]} - The file '$SCPATH' already exists, creating a backup, then removing the 2 closing backslashes at the end"
		cp "$SCPATH" "${SCPATH//.vdf}_${PROGNAME}_backup.vdf" 2>/dev/null
		truncate -s-2 "$SCPATH"
		OLDSET="$(grep -aPo '\x00[0-9]\x00\x02appid' "$SCPATH" | tail -n1 | tr -dc '0-9')"
		NEWSET=$((OLDSET + 1))
		writelog "INFO" "${FUNCNAME[0]} - Last set in file has ID '$OLDSET', so continuing with '$OLDSET'"
	else
		writelog "INFO" "${FUNCNAME[0]} - Creating new $SCPATH"
		printf '\x00%s\x00' "shortcuts" > "$SCPATH"
		NEWSET=0
	fi

	## Match any image file in same folder as EXE name hero, logo, boxart, tenfoot -- Matches will override selected options
	# If not found, fall back to actual file path provided meaning only artwork that exists will be used
	if [ "$NOSTAUTOARTWORK" -eq 1 ]; then
		NOSTEXEBASEDIR="$( dirname "$NOSTEXEPATH" | cut -d '"' -f2 )"

		NOSTGHERO="$( findGameArtInExeDir "$NOSTEXEBASEDIR" "hero" "$NOSTGHERO" )"
		NOSTGLOGO="$( findGameArtInExeDir "$NOSTEXEBASEDIR" "logo" "$NOSTGLOGO" )"
		NOSTGBOXART="$( findGameArtInExeDir "$NOSTEXEBASEDIR" "boxart" "$NOSTGBOXART" )"
		NOSTGTENFOOT="$( findGameArtInExeDir "$NOSTEXEBASEDIR" "tenfoot" "$NOSTGTENFOOT" )"
		NOSTICONPATH="$( findGameArtInExeDir "$NOSTEXEBASEDIR" "icon" "$NOSTICONPATH" )"
	fi

	## Fetch artwork from SteamGridDB
	if [ "$NOSTUSESGDB" -eq 1 ]; then
		# Regular artwork
		notiShow "$NOTY_SGDBDL"

		# The entered search name is prioritised over actual game EXE name, only one will be used and we will always prefer custom name
		# Ex: user names Non-Steam Game "The Elder Scrolls IV: Oblivion" but they enter a custom search name because they want artwork for "The Elder Scrolls IV: Oblivion Game of the Year Edition"
		# In case art is not found for the custom name, users should enter either the Steam AppID or the SteamGridDB Game ID to use as a fallback (Steam AppID will always be preferred because it will always be exact)
		#
		# Therefore, the order of priority for artwork searching is:
		# 1. Name search (only ONE of the below will be used)
		#     a. If the user enters a custom search name with --steamgriddb-game-name, search on that
		#     b. Otherwise, use the Non-Steam Game name
		# 2. Fallback to ID search if no SteamGridDB ID is found on the name search
		#    a. If the user enters a Steam AppID with --steamgriddb-steam-appid, search on that
		#    b. Otherwise, fall back to searching on an entered SteamGridDB Game ID
		# In short, search on ONE of the names, and if a Game ID is not found on either of these, fall back to searching on ONE of the passed IDs
		# If no IDs are found after all of this, we can't get artwork. We will not fall back to EXE name if no ID is found on custom name, and we will not fall back to SteamGridDB Game ID if no art is found for Steam AppID
		# If no values are provided we will simply search on Non-Steam Game name
		NOSTSEARCHNAME=""  # Name to search for SteamGridDB Game ID on (either custom name or app name)
		NOSTSEARCHID=""  # ID to search for the SteamGridDB artwork on (either Steam AppID or SteamGridDB Game ID)
		NOSTSEARCHFLAG="--nonsteam"  # Whether to search using a Steam AppID or SteamGridDB Game ID (will be set to --steam if we get an AppID)
		if [ -n "$NOSTSGDBSTAID" ]; then
			NOSTSEARCHID="$NOSTSGDBSTAID"
			NOSTSEARCHFLAG="--steam"  # If a match is found on game name above, commandlineGetSteamGridDBArtwork will manage falling back to searching with SteamGridDB Game ID, so this is safe
		elif [ -n "$NOSTSGDBGAMEID" ]; then
			NOSTSEARCHID="$NOSTSGDBGAMEID"
		fi

		# Only add NOSTAPPNAME as fallback if we don't have an ID to search on, because commandlineGetSteamGridDBArtwork will prefer name over ID, so if we have to fall back to Non-Steam Name (i.e. no entered custom name) then only do so if we don't have an ID given
		if [ -n "$NOSTSGDBNAM" ]; then
			NOSTSEARCHNAME="$NOSTSGDBNAM"
		elif [ -n "$NOSTAPPNAME" ] && [ -z "$NOSTSEARCHID" ]; then
			NOSTSEARCHNAME="$NOSTAPPNAME"
		fi

		# Store the ID we searched with, so getSteamGridDBNonSteamIcon doesn't have to hit the endpoint again and we save an API call
		commandlineGetSteamGridDBArtwork --search-name="$NOSTSEARCHNAME" --search-id="$NOSTSEARCHID" --filename-appid="$NOSTAIDGRID" "$NOSTSEARCHFLAG" --apply --replace-existing

		# Get ID that commandlineGetSteamGridDBArtwork searched on above and use that to search for the icon
		NOSTSGDBAPIGAMEID="$( cat "$NOSTSGDBIDSHMFILE" )"

		# Icon -- Only set if we successfully download an icon from SteamGridDB
		getSteamGridDBNonSteamIcon "$NOSTAIDGRID" "$NOSTSGDBAPIGAMEID"
		NOSTSGDBICON="$( findNonSteamGameIcon )"
		if [ -f "$NOSTSGDBICON" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found SteamGridDB icon path to '$NOSTSGDBICON' -- Using this as Non-Steam Game Icon"
			NOSTICONPATH="$NOSTSGDBICON"
		else
			writelog "INFO" "${FUNCNAME[0]} - Icon path does not exist at '$NOSTSGDBICON' - Maybe download failed?"
		fi

		notiShow "$NOTY_SGDBDLDONE"
	fi

	writelog "INFO" "${FUNCNAME[0]} - Adding new set '$NEWSET'"
	{
		printf '\x00%s\x00' "$NEWSET"
		printf '\x02%s\x00%b' "appid" "$NOSTAIDVDFHEXFMT"
		printf '\x01%s\x00%s\x00' "AppName" "$NOSTAPPNAME"
		printf '\x01%s\x00%s\x00' "Exe" "$NOSTEXEPATH"
		printf '\x01%s\x00%s\x00' "StartDir" "$NOSTSTDIR"
		printf '\x01%s\x00%s\x00' "icon" "$NOSTICONPATH"
		printf '\x01%s\x00%s\x00' "ShortcutPath" ""
		printf '\x01%s\x00%s\x00' "LaunchOptions" "$NOSTLAOP"

		printf '\x02%s\x00%b\x00\x00\x00' "IsHidden" "\x0${NOSTHIDE:-0}"
		printf '\x02%s\x00%b\x00\x00\x00' "AllowDesktopConfig" "\x0${NOSTADC:-0}"

		# These values are now stored in localconfig.vdf under the "Apps" section,
		# under a block using the Non-Steam Game Signed 32bit AppID. (i.e., -223056321)
		# This is handled by `updateLocalConfigAppsValue` below
		#
		# Unsure if required, but still write these to the shortcuts.vdf file for consistency
		printf '\x02%s\x00%b\x00\x00\x00' "AllowOverlay" "\x0${NOSTAO:-0}"
		printf '\x02%s\x00%b\x00\x00\x00' "OpenVR" "\x0${NOSTVR:-0}"

		printf '\x02%s\x00\x00\x00\x00\x00' "Devkit"
		printf '\x01%s\x00\x00' "DevkitGameID"
		printf '\x02%s\x00\x00\x00\x00\x00' "DevkitOverrideAppID"
		printf '\x02%s\x00\x00\x00\x00\x00' "LastPlayTime"
		printf '\x01%s\x00\x00' "FlatpakAppID"
		printf '\x00%s\x00' "tags"
		splitTags "$NOSTTAGS"  # TODO tags are now stored in localconfig.vdf (see #949) but we still write them here anyway
		printf '\x08\x08\x08\x08'
	} >> "$SCPATH"

	writelog "INFO" "${FUNCNAME[0]} - Finished writing out new Non-Steam Game Shortcut"
	writelog "INFO" "${FUNCNAME[0]} - Adding any chosen Non-Steam game artwork"

	setGameArt "$NOSTAIDGRID" --hero="$NOSTGHERO" --logo="$NOSTGLOGO" --boxart="$NOSTGBOXART" --tenfoot="$NOSTGTENFOOT" "$SGACOPYMETHOD"

	if [ -n "$NOSTCOMPATTOOL" ]; then
		if [ ! -f "$CFGVDF" ]; then
			writelog "SKIP" "${FUNCNAME[0]} - No Config VDF found at '$CFGVDF' -- Unable to set compatibility tool for Non-Steam Game, skipping"
		else
			writelog "INFO" "${FUNCNAME[0]} - Adding selected compatibility tool '$NOSTCOMPATTOOL' for Non-Steam Game"
			NSGVDFVALS=( "name!${NOSTCOMPATTOOL}" "config!" "priority!250" )
			createVdfEntry "$CFGVDF" "CompatToolMapping" "$NOSTAIDGRID" "" "" "" "${NSGVDFVALS[@]}"
			writelog "INFO" "${FUNCNAME[0]} - Finished adding Non-Steam Game compatibility tool to '$CFGVDF'"
		fi
	fi

	# Update "Apps" section in localconfig.vdf to create the section for the new Non-Steam Game and set AllowOverlay and OpenVR accordingly
	# In future if more options are stored here we can also set them in the same way
	writelog "INFO" "${FUNCNAME[0]} - Updating 'localconfig.vdf' to set OpenVR and AllowOverlay values, using Signed 32bit AppID '$NOSTAIDVDF'"
	updateLocalConfigAppsValue "$NOSTAIDVDF" "OverlayAppEnable" "$NOSTAO"
	updateLocalConfigAppsValue "$NOSTAIDVDF" "DisableLaunchInVR" "$(( 1-NOSTVR ))"  # localconfig.vdf tracks where OpenVR is DISabled rather than ENabled, so flip the boolean

	writelog "INFO" "${FUNCNAME[0]} - Finished adding new $NSGA"
	SGACOPYMETHOD=""  # Unset doesn't work for some reason with '--flag'
}

function setCloseVars {
# yes, ugly... ¯\_(ツ)_/¯
	if [ ! -f "$CLOSEVARS" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Storing all variables used in closeSTL into '$CLOSEVARS'"
		{
		echo BROWSER
		echo LOGLEVEL
		echo MO2MODE
		echo ONLY_CUSTOMCMD
		echo RUN_NYRNA
		echo RUN_REPLAY
		echo RUNSBSVR
		echo TOGSTEAMWEBHELPER
		echo USECUSTOMCMD
		echo USEMANGOAPP
		echo USENETMON
		echo USEPROTON
		echo USEWINE
		echo WAITFORTHISPID
		} >> "$CLOSEVARS"
		# should be sorted above already, but better safe
		sort "$CLOSEVARS" -o "$CLOSEVARS"
	fi
}

#STARTCLOSESTL ###
function closeSTL {
	writelog "INFO" "${FUNCNAME[0]} - closing STL"

	updateConfigEntry "CUSTOMCMD" "$DUMMYBIN" "$STLDEFGAMECFG"

	# dummy file which could be used to stop possible while loops
	writelog "INFO" "${FUNCNAME[0]} - Creating '$CLOSETMP'"
	touch "$CLOSETMP"

	setPrevRes
	customUserScriptStop	# USERSTOP

	rmFileIfExists "$MO2INSTFAIL" 2>/dev/null

	if [ "$USEMANGOAPP" -eq 1 ] && "$PGREP" -f "$MANGOAPP" >/dev/null; then
		writelog "INFO" "${FUNCNAME[0]} - Killing '$MANGOAPP'"
		"$PKILL" -f "$MANGOAPP"
	fi

	checkPlayTime "$duration"

	steamdeckClose

	if [ -f "$STLSHM/KillBrowser-$AID.txt" ]; then
		writelog "INFO" "${FUNCNAME[0]} - '$BROWSER' instance was created, so closing it now, to exit the game session"
		"$PKILL" -f "$BROWSER"
		rm "$STLSHM/KillBrowser-$AID.txt"
	fi

	cleanSUTemp
	backupSteamUser "$AID"

	if [ "$SGDBAUTODL" == "after_game" ] ; then
		writelog "INFO" "${FUNCNAME[0]} - Automatic Grid Update Check '$SGDBAUTODL'"
		# getGrids "$AID"
		commandlineGetSteamGridDBArtwork --search-id="$AID" --steam
	fi

	if [ "$ISGAME" -eq 2 ] && [ "$PROTON_LOG" -eq 1 ]; then
		createSymLink "${FUNCNAME[0]}" "$STLPROTONIDLOGDIR/steam-${AID}.log" "$STLPROTONTILOGDIR//steam-${GN}.log"
	fi

	writelog "INFO" "${FUNCNAME[0]} - Game '$SGNAID' exited - cleaning up custom processes if necessary"

# all variables in closeSTL are blocked from being unset before gamestart - dirty list with variables to keep which are not used here:
#DXVK_HUD DXVK_LOG_LEVEL ENABLE_VKBASALT LOGDIR LOGLEVEL MANGOHUD NETOPTS PROTON_DEBUG_DIR PROTON_DUMP_DEBUG_COMMANDS PROTON_FORCE_LARGE_ADDRESS_AWARE
#PROTON_LOG PROTON_LOG_DIR PROTON_NO_D3D10 PROTON_NO_D3D11 PROTON_NO_ESYNC PROTON_NO_FSYNC PROTON_ENABLE_NVAPI PROTON_HIDE_NVIDIA_GPU PROTON_USE_WINED3D
#USESLR WINEDLLOVERRIDES WINE_FULLSCREEN_INTEGER_SCALING WINE_FULLSCREEN_FSR WINE_FULLSCREEN_FSR_STRENGTH WINE_FULLSCREEN_FSR_MODE WINE_FULLSCREEN_FSR_CUSTOM_MODE

	# kill $VRVIDEOPLAYER in case it wasn't closed before
	if [ -n "$RUNSBSVR" ]; then
		if [ "$RUNSBSVR" -eq 1 ]; then
			if "$PGREP" -f "$VRVIDEOPLAYER" >/dev/null; then
				"$PKILL" -f "$VRVIDEOPLAYER"
				writelog "INFO" "${FUNCNAME[0]} - $VRVIDEOPLAYER killed"
			fi
		fi
	fi

	# kill $NYRNA if running
	if [ -n "$RUN_NYRNA" ]; then
		if [ "$RUN_NYRNA" -eq 1 ]; then
			if "$PGREP" -f "$NYRNA" >/dev/null; then
				"$PKILL" -f "$NYRNA"
				# also remove systray created in /tmp/ ("systray_" with 6 random chars should be save enough)
				find /tmp -maxdepth 1 -type f -regextype posix-extended -regex '^.*systray_[A-Z,a-z,0-9]{6}' -exec rm {} \;
				writelog "INFO" "${FUNCNAME[0]} - $NYRNA killed"
			fi
		fi
	fi

	# kill $REPLAY if running
	if [ -n "$RUN_REPLAY" ]; then
		if [ "$RUN_REPLAY" -eq 1 ]; then
			if "$PGREP" -f "$REPLAY" >/dev/null; then
				"$PKILL" -f "$REPLAY"
				writelog "INFO" "${FUNCNAME[0]} - $REPLAY killed"
			fi
		fi
	fi

	# stop network monitor if running
	if [ "$USENETMON" -eq 1 ]; then
		if "$PGREP" "$NETMON" >/dev/null; then
			"$PKILL" -f "$NETMON"
			writelog "INFO" "${FUNCNAME[0]} - $NETMON killed"
			rmDupLines "$NETMONDIR/$AID-$NETMON.log"  # remove duplicate lines to make reading easier
		fi
	fi

	togWindows windowraise	# TOGGLEWINDOWS
	getHexAidForAid "$AID" X
	cleanYadLeftOvers
	notiShow "$(strFix "$NOTY_STLSTOP" "$GN" "$AID" "$PROGNAME")"

	sleep 1 # so any while loop still running gets the chance to see it
	writelog "INFO" "${FUNCNAME[0]} - Removing '$CLOSETMP'"
	rm "$CLOSETMP" 2>/dev/null
	writelog "STOP" "######### ${FUNCNAME[0]} $PROGNAME $PROGVERS #########"
	rm "$UPWINTMPL" 2>/dev/null
	rm "$KILLSWITCH" 2>/dev/null
	rm "$GWXTEMP" 2>/dev/null
	rm "$STLSHM/lola-*.txt" 2>/dev/null

	# only useful for win games or games started outside steam:
	if [ "$STLPLAY" -eq 1 ] || { [ "$ISGAME" -eq 2 ] || [ "$ISGAME" -eq 3 ] && [ "$USEWINE" -eq 0 ];}; then
		writeLastRun
		storeMetaData "$AID" "$GN" "$GPFX" "$EFD"
	fi

	delMenuTemps
	delWinetricksTemps
	if [ "$AID" != "$PLACEHOLDERAID" ]; then
		echo "log can be found under: '$TEMPLOG' and '$LOGFILE'"
	else
		echo "log can be found under: '$TEMPLOG'"
	fi
}
#ENDCLOSESTL ###

# main:#################

function saveOrgVars {
	# TODO resolve the unused warnings by dynamically assigning the variables like we do in 'restoreOrgVars'

	writelog "INFO" "${FUNCNAME[0]} - Storing some original variables to restore them later" "P"

	env | grep "=" | sort -o "$VARSIN"

	# shellcheck disable=2034
	ORG_LD_PRELOAD="$LD_PRELOAD"
	# shellcheck disable=2034
	ORG_LD_LIBRARY_PATH="$LD_LIBRARY_PATH"
	# shellcheck disable=2034
	ORG_LC_ALL="$LC_ALL"
	# shellcheck disable=2034
	ORG_PATH="$PATH"
}

function emptyVars {
	# clear "original" variables
	if [ "$1" == "O" ] ; then
		LC_ALL=""
		STLPATH="$(tr ':' '\n' <<< "$PATH" | grep -v "$STERU" | tr '\n' ':')"
		PATH="$STLPATH"

		FPPATH="/app/utils/bin/"
		if [ -d "$FPPATH" ]; then
			PATH="${STLPATH%:}:$FPPATH"
		else
			PATH="$STLPATH"
		fi
		LD_LIBRARY_PATH=""
		LD_PRELOAD=""
		if [ -z "$2" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Emptied some original variables as they slowdown several system calls when started from steam" "P"
			writelog "INFO" "${FUNCNAME[0]} - Set \$PATH to '$PATH'" "P"
		fi
	# empty "some" internal variables created by ${PROGNAME,,} (before starting the game)
	elif [ "$1" == "S" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Clearing some '${PROGNAME,,}' internal variables before the game starts"

		TIEXV="$STLSHM/TrayIconVars.txt"
		TIEXF="$STLSHM/TrayIconFuncs.txt"

		if [ ! -f "$TIEXV" ]; then
			sed -n "/^#STARTIEX/,/^#ENDIEX/p;/^#ENDIEX/q" "$0" | grep export | grep -v "export \-f" | cut -d '=' -f1 | awk -F 'export ' '{print $2}' > "$TIEXV"
		fi

		if [ ! -f "$TIEXF" ]; then
			sed -n "/^#STARTIEX/,/^#ENDIEX/p;/^#ENDIEX/q" "$0" | grep "export \-f" | awk -F '-f ' '{print $2}' > "$TIEXF"
		fi

		while read -r intvar; do
			if [ -n "$intvar" ]; then
				if [[ "$intvar" =~ "BASH_FUNC" ]]; then
					BAFU1="${intvar#BASH_FUNC_*}"
					BAFU="${BAFU1%%\%}"
					if grep -q "${BAFU%%\%}" "$STLSHM/TIEXF"; then
						writelog "INFO" "${FUNCNAME[0]} - Skipping TrayIcon function '${BAFU%%\%}'"
					else
						if grep -q "function ${BAFU%%\%}" "$0"; then
							writelog "INFO" "${FUNCNAME[0]} - Clearing function '${BAFU%%\%}'"
							unset -f "${BAFU%%\%}"
						else
							writelog "INFO" "${FUNCNAME[0]} - Skipping unknown function '${BAFU%%\%}'"
						fi
					fi
				else
					if grep -q "$intvar" "$STLSHM/TIEXV"; then
						writelog "INFO" "${FUNCNAME[0]} - Skipping TrayIcon variable '$intvar'"
					else
						if ! grep -q "\$${intvar}" "$0" ; then
							writelog "INFO" "${FUNCNAME[0]} - Skipping unknown variable '$intvar'"
						else
							if grep -q "$intvar" <<< "$(sed -n "/^#STARTCLOSESTL/,/^#ENDCLOSESTL/p;/^#ENDCLOSESTL/q" "$0")"; then
								writelog "INFO" "${FUNCNAME[0]} - Skipping variable in closeSTL '$intvar'"
							else
								writelog "INFO" "${FUNCNAME[0]} - Clearing variable '$intvar'"
								unset "$intvar"
							fi
						fi
					fi
				fi
			fi
		done <<< "$(comm -32 <(cut -d '=' -f1 < "$STLSHM/${FUNCNAME[0]}-in") <(cut -d '=' -f1 < "$VARSIN"))"
	fi
}

function unsetSTLvars {
	setCloseVars
	writelog "INFO" "${FUNCNAME[0]} - Unsetting $PROGNAME internal variables"
	sort "$STLSETENTRIES" -o "${STLSETENTRIES}s"
	while read -r line; do
		unset "$line"
	done <<< "$(comm -3 "${STLSETENTRIES}s" "$CLOSEVARS")"
}

function restoreOrgVars {
	writelog "INFO" "${FUNCNAME[0]} - Restoring previously cleared environment variables"

	STLRESTOREVARS=( "LD_PRELOAD" "LD_LIBRARY_PATH" "LC_ALL" "PATH" )
	for RESTOREVAR in "${STLRESTOREVARS[@]}"; do

		# e.g. 'ORG_LD_PRELOAD' for 'LD_PRELOAD' on first iteration of the loop
		BUILTORGVARNAME="\$ORG_${RESTOREVAR}"

		# Check if the envvar has been defined in a custom-vars conf file, if so don't restore it and continue
		if grep -q "^${RESTOREVAR}" "$GLOBCUSTVARS" || grep -q "^${RESTOREVAR}" "$GAMECUSTVARS"; then
			writelog "WARN" "${FUNCNAME[0]} - Not restoring '${RESTOREVAR}' to original value as it is defined in a custom-vars conf file. This may potentially cause issues."

			continue
		fi

		eval "${RESTOREVAR}=\"${BUILTORGVARNAME}\""
	done
}

function rmOldLog {
	# restart $LOGFILE only if older than 3 minutes # TODO minutes configurable?
	setAIDCfgs
	if [ -f "$LOGFILE" ]; then
		if test "$(find "$LOGFILE" -mmin -3 2>/dev/null)"; then
			rm "$LOGFILE" 2>/dev/null
		fi
	fi
}

function CreateCustomEvaluatorScript {
	# create temporary '$TEVALSC'
	function createTempEvals {

		# first the correct install order:
		OCNT=0

		echo "\"$EVALSC\"" > "$TEVALSC"
		echo "{" >> "$TEVALSC"
		while read -r ordline; do
			if grep -q "$ordline" <<< "$EVALSCFILES"; then
				echo "	\"$OCNT\"" >> "$TEVALSC"
				if [ -f "${GLOBSTLEVPACKS}/$ordline" ]; then
					cat "${GLOBSTLEVPACKS}/$ordline" >> "$TEVALSC"
				else
					cat "${GLOBEVPACKS}/$ordline" >> "$TEVALSC"
				fi
				OCNT=$((OCNT+1))
			fi
		done < "$GLOBEVPACKORDER"

		# then append those packages which are not in the order list:
		while read -r wantevascs; do
			if ! grep -q "$wantevascs" "$GLOBEVPACKORDER"; then
				echo "	\"$OCNT\"" >> "$TEVALSC"
				if [ -f "${GLOBSTLEVPACKS}/$wantevascs" ]; then
					cat "${GLOBSTLEVPACKS}/$wantevascs" >> "$TEVALSC"
				else
					cat "${GLOBEVPACKS}/$wantevascs" >> "$TEVALSC"
				fi
				OCNT=$((OCNT+1))
			fi
		done <<< "$EVALSCFILES"

		while read -r intpak; do
			if [ -n "$intpak" ] ; then
				IPFDIR="$(awk -F'"' '{print $4}' <<< "$intpak")"
				IPDIR="${IPFDIR##*/}"
				INTPAKWA="$STLSHM/intpak"
				# dirty workaround - proper fix?
				echo "tac \"$OEVALSC\" | awk '/$IPDIR/,/appid/' | tac" > "$INTPAKWA"
				chmod +x "$INTPAKWA"
				{
					echo "	\"$OCNT\""
					echo "	{"
					"$INTPAKWA"
					echo "	}"
				} >> "$TEVALSC"
				OCNT=$((OCNT+1))
				rm "$INTPAKWA" 2>/dev/null
			fi
		done <<< "$(grep "$LIINPA" "$OEVALSC" | grep -v "${STEWOS}\|${STLDLDIR}\|STESHA\|STLDLDIR")"

		echo "}" >> "$TEVALSC"
		writelog "INFO" "${FUNCNAME[0]} - Created temporary '$TEVALSC'"
	}

	mkProjDir "$EVMETACUSTOMID"
	mkProjDir "$EVMETAADDONID"

	writelog "INFO" "${FUNCNAME[0]} - Starting Gui for Creating a custom '$EVALSC'"
	export CURWIKI="$PPW/Steam-First-Time-Setup"
	TITLE="${PROGNAME}-${FUNCNAME[0]}"
	pollWinRes "$TITLE"
	setShowPic

	EUNID="$1"

	REVALSC="$EVMETAID/${EVALSC}_${EUNID}.vdf"
	CEVALSC="$EVMETACUSTOMID/${EVALSC}_${EUNID}.vdf"
	AEVALSC="$EVMETAADDONID/${EVALSC}_${EUNID}.vdf"
	TEVALSC="${STLSHM}/${EVALSC}_${EUNID}_temp.vdf"

	REVP="packages"
	SEVP="stlpackages"
	GLOBEVPACKS="${GLOBALEVALDIR}/$REVP"
	GLOBSTLEVPACKS="${GLOBALEVALDIR}/$SEVP"

	GLOBEVPACKORDER="${GLOBALEVALDIR}/order.txt"
	TEVALSCSEL="$NON"

	if [ -f "$REVALSC" ]; then
		FOUNDEVALSC="Original"
		OEVALSC="$REVALSC"
		TEVALSCSEL="Original"
	fi

	if [ -f "$CEVALSC" ]; then
		if [ -n "$FOUNDEVALSC" ]; then
			FOUNDEVALSC="${FOUNDEVALSC}, Custom"
		else
			FOUNDEVALSC="Custom"
		fi
		OEVALSC="$CEVALSC"
		TEVALSCSEL="Custom"
	fi

	if [ -f "$AEVALSC" ]; then
		if [ -n "$FOUNDEVALSC" ]; then
			FOUNDEVALSC="${FOUNDEVALSC}, Addon"
		else
			FOUNDEVALSC="Addon"
		fi
		OEVALSC="$AEVALSC"
		TEVALSCSEL="Addon"
	fi

	if [ -f "$OEVALSC" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Pre-Selection is based on packages enabled in '$OEVALSC'"
	else
		TEVALSCSEL="$NON"
	fi

	GUITEXT1="$(strFix "$GUI_FOUNDEVALSC" "$FOUNDEVALSC")"
	GUITEXT2="$(strFix "$GUI_TEVALSCSEL" "$TEVALSCSEL")"
	GUITEXT3="$(strFix "$GUI_ADDEVALSC" "$SHADDESTDIR")"

	EVALSCFILES="$(
	while read -r EPACK; do
		if [ -f "$OEVALSC" ]; then
			PACKPROC="$(grep "process 1" "$EPACK")"
			if [ -n "$PACKPROC" ]; then
				writelog "INFO" "${FUNCNAME[0]} - Searching in '$PACKPROC' '$OEVALSC'"
				if grep -Fq "$PACKPROC" "$OEVALSC"; then
					echo TRUE ; echo "${EPACK##*/}"
				else
					echo FALSE ; echo "${EPACK##*/}"
				fi
			else
				echo FALSE ; echo "${EPACK##*/}"
			fi
		else
			echo FALSE ; echo "${EPACK##*/}"
		fi
	done <<< "$(find "${GLOBALEVALDIR}" \( -type f -and -path "*/$REVP/*" -or -path "*/$SEVP/*" \))" | \
		"$YAD" --f1-action="$F1ACTION" --image "$SHOWPIC" --image-on-top --window-icon="$STLICON" --center "$WINDECO" \
		--list --checklist --column="$GUI_ADD" --column=Script --separator="" --print-column="2" \
		--text="${GUITEXT1}\n$GUITEXT2\n$GUITEXT3" \
		--title="$TITLE" \
		--button="$BUT_SAEVALSC":2 --button="$BUT_SCEVALSC":4 --button="$BUT_ONLYINSTALL":6 --button="$BUT_CAN":8 \
		"$GEOM"
	)"
	case $? in
		2) 	{
				createTempEvals
				writelog "INFO" "${FUNCNAME[0]} - Selected Saving as Addon '$EVALSC': '$AEVALSC'"
				if [ -f "$AEVALSC" ]; then
					mv "$AEVALSC" "${AEVALSC//.vdf/_back.vdf}"
				fi
				mv "$TEVALSC" "$AEVALSC"
			}
		;;
		4)	{
				createTempEvals
				writelog "INFO" "${FUNCNAME[0]} - Selected Saving as Custom '$EVALSC': '$CEVALSC'"
				if [ -f "$CEVALSC" ]; then
					mv "$CEVALSC" "${CEVALSC//.vdf/_back.vdf}"
				fi
				mv "$TEVALSC" "$CEVALSC"
			}
		;;
		6)	{
				writelog "INFO" "${FUNCNAME[0]} - Selected to only install the packages without saving"
				createTempEvals
				writelog "INFO" "${FUNCNAME[0]} - Starting Install via command: reCreateCompatdata \"$EUNID\" \"${FUNCNAME[0]}\" \"$TEVALSC\""
				reCreateCompatdata "$EUNID" "${FUNCNAME[0]}" "$TEVALSC"
			}
		;;
		8)	{
				writelog "INFO" "${FUNCNAME[0]} - Selected CANCEL"
			}
		;;
	esac
}

function reCreateCompatdata {
	function runEvalsc {
		unset LEV
		mapfile -d " " -t -O "${#LEV[@]}" LEV <<< "run ${ISCEXE} ${LECO}\\${EVALSC}_${1}.vdf"
		rm "$EVALISRUN" 2>/dev/null
		touch "$EVALISRUN"
		checkFirstTimeRun "${LEV[@]}" >/dev/null 2>/dev/null
		rm "$EVALISRUN"	"$FRSTATE" "${FRSTATE}-prev" 2>/dev/null
		doneEvacInstall
	}

	function getCurStep {
		unset GCS
		mapfile -d " " -t -O "${#GCS[@]}" GCS <<< "run ${ISCEXE} --${GECUST} ${1}"
		checkFirstTimeRun "${GCS[@]}" >/dev/null 2>/dev/null
		PRFRSTATE="${FRSTATE}-prev"

		if [ -f "$FRSTATE" ]; then
			if ! cmp -s -- "$FRSTATE" "$PRFRSTATE"; then
				cp "$FRSTATE" "$PRFRSTATE"
				notiShow "$(strFix "$NOTY_FRSTATE" "$(cat "$FRSTATE")")" "X"
				writelog "INFO" "${FUNCNAME[0]} - $(strFix "$NOTY_FRSTATE" "$(cat "$FRSTATE")")"
			fi
		fi
	}

	WANTGUI=0
	if [ "$1" == "ccd" ] || [ "$1" == "createcompatdata" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Started from command-line"
		EUNID="$2"
		if [ -n "$3" ]; then
			WANTGUI=1
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - Started from gui"
		EUNID="$1"
		WANTGUI=1
		BACKFUNC="$2"
	fi

	SCCILECO="$STEAM_COMPAT_CLIENT_INSTALL_PATH/$LECO"
	ISCEXE="$SCCILECO/$ISCRI.exe"

	if [ -n "$EUNID" ]; then
		SRCORIGEVSC="$EVMETAID/${EVALSC}_${EUNID}.vdf"
		SRCCUSTEVSC="$EVMETACUSTOMID/${EVALSC}_${EUNID}.vdf"

		if [ "$EUNID" == "$DPRS" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Starting '$DPRS' Installation"
			SRCORIGEVSC="${GLOBALEVALDIR}/sets/${EVALSC}_${DPRS,}.vdf"
			SRCEVSC="$SRCORIGEVSC"
			DESTCODA="$DPRSCOMPDATA"
			EARLYPROT="$DPRSPROTON"
			export STEAM_COMPAT_APP_ID="$DPRS"
			STEAM_COMPAT_INSTALL_PATH="$DPRSCOMPDATA"
		fi

		if [ ! -f "$ISCEXE" ]; then
			writelog "ERROR" "${FUNCNAME[0]} - '$ISCEXE' not found"
		else
			if [ ! -f "$SRCORIGEVSC" ] && [ ! -f "$SRCCUSTEVSC" ]; then
				if [ "$WANTGUI" -eq 0 ]; then
					writelog "ERROR" "${FUNCNAME[0]} - Neither '$SRCORIGEVSC' nor '$SRCCUSTEVSC' found (see wiki if the reason for not being able to continue is unknown)"
					writelog "ERROR" "${FUNCNAME[0]} - Alternatively start the same command with an additional 'gui' parameter, to create a '$SRCCUSTEVSC' via gui"
					HAVEEVSC=0
				else
					writelog "INFO" "${FUNCNAME[0]} - Requester to create a custom Install file via Gui"
					CreateCustomEvaluatorScript "$EUNID"
					if [ -f "$SRCCUSTEVSC" ]; then
						HAVEEVSC=1
					else
						HAVEEVSC=0
					fi
				fi
			else
				HAVEEVSC=1
			fi

			if [ "$HAVEEVSC" -eq 1 ]; then
				if [ -f "$CUMETA/${EUNID}.conf" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Loading Metadata '$CUMETA/${EUNID}.conf'"
					loadCfg "$CUMETA/${EUNID}.conf"
				fi

				if [ -z "$DESTCODA" ]; then
					if [ -n "$WINEPREFIX" ]; then
						DESTCODA="${WINEPREFIX%/*}"
					elif [ -n "$STEAM_COMPAT_DATA_PATH" ]; then
						DESTCODA="$STEAM_COMPAT_DATA_PATH"
					elif [ -n "$GPFX" ]; then
						DESTCODA="${GPFX%/*}"
					else
						getCompatData "${EUNID}" >/dev/null
						DESTCODA="$(cut -d ';' -f2 <<< "$FCOMPDAT")"
					fi
				fi

				if [ -n "$DESTCODA" ]; then
					export STEAM_COMPAT_DATA_PATH="$DESTCODA"
					writelog "INFO" "${FUNCNAME[0]} - Destination $CODA is '$STEAM_COMPAT_DATA_PATH'"

					if [ -z "$STEAM_COMPAT_INSTALL_PATH" ]; then
						if [ -n "$MEGAMEDIR" ]; then
							export STEAM_COMPAT_INSTALL_PATH="$MEGAMEDIR"
						elif [ -n "$EFD" ]; then
							export STEAM_COMPAT_INSTALL_PATH="$EFD"
						else
							writelog "INFO" "${FUNCNAME[0]} - STEAM_COMPAT_INSTALL_PATH (game dir) is unknown - setting it at least to '${STLDLDIR}'"
							export STEAM_COMPAT_INSTALL_PATH="$STLDLDIR"
						fi
					fi

					RECRECODA=1

					find "$DESTCODA" -maxdepth 0 -empty -exec rmdir {} \; 2>/dev/null

					if [ -d "$DESTCODA" ]; then
						if [ "$3" == "s" ]; then
							RCSEL="$(confirmReq "Ask_Recreate_Compatdata" "$(strFix "$GUI_RECRECODA" "$EUNID" "${EUNID}_${PROGNAME,,}")")"
							if [ "$RCSEL" -eq 0 ]; then
							{
								writelog "INFO" "${FUNCNAME[0]} - Confirmed recreating the wineprefix"
								RECRECODA=1
							}
							else
								writelog "INFO" "${FUNCNAME[0]} - Selected CANCEL - Not recreating the wineprefix"
								RECRECODA=0
							fi
						fi

						if [ "$RECRECODA" -eq 1 ]; then
							if [ -d "${DESTCODA}_${PROGNAME,,}" ]; then
								writelog "INFO" "${FUNCNAME[0]} - Removing previous backup $CODA backup '${DESTCODA}_${PROGNAME,,}'"
								rm -rf "${DESTCODA}_${PROGNAME,,}" 2>/dev/null
							fi

							find "$DESTCODA" -maxdepth 0 -empty -exec rmdir {} \;

							if [ -d "$DESTCODA" ]; then
								writelog "INFO" "${FUNCNAME[0]} - Moving existing $CODA to '${DESTCODA}_${PROGNAME,,}'"
								mv "$DESTCODA" "${DESTCODA}_${PROGNAME,,}"
							fi
						fi
					fi

					if [ "$RECRECODA" -eq 1 ]; then
						if [ -z "$STEAM_COMPAT_APP_ID" ]; then
							export STEAM_COMPAT_APP_ID="${EUNID}"
						fi

						mkProjDir "$DESTCODA"
						EVALISRUN="$STLSHM/evalisrun.txt"

						cd "$STEAM_COMPAT_CLIENT_INSTALL_PATH" >/dev/null || return

						writelog "INFO" "${FUNCNAME[0]} - Launching First Run Installer"

						runEvalsc "${EUNID}" &

						while [ ! -f "$EVALISRUN" ];do
							sleep 0.1
						done

						while [ -f "$EVALISRUN" ];do
							getCurStep "${EUNID}"
							sleep 1
						done

						cd - >/dev/null || return
					fi
				else
					writelog "SKIP" "${FUNCNAME[0]} - No destination $CODA determined"
				fi
			fi

			if [ -n "$BACKFUNC" ]; then
				"$BACKFUNC" "$AID" "${FUNCNAME[0]}"
			fi
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - Need SteamAppId as argument"
	fi
}

function createModifiedEvalsc {
	if [ -z "$ISCRILOG" ]; then
		ISCRILOG="$STLSHM/${PROGNAME,,}-${ISCRI}-temp_$RANDOM.log"
	fi

	STESHA="$STEAM_COMPAT_CLIENT_INSTALL_PATH/$SAC/$STEWOS"
	if [ ! -d "$STESHA" ]; then
		writelog "WARN" "${FUNCNAME[0]} - '$STESHA' not found - packages expected there won't be found" "X" "$ISCRILOG"
		writelog "WARN" "${FUNCNAME[0]} - '$STESHA' is concatenated from '$STEAM_COMPAT_CLIENT_INSTALL_PATH', '$SAC', '$STEWOS'" "X" "$ISCRILOG"
	fi

	SOURCEEVALSC="$1"
	DSTEVALSC="$2"

	if [ ! -f "$SOURCEEVALSC" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - '$SOURCEEVALSC' does not exists" "X" "$ISCRILOG"
	elif [ ! -f "$DSTEVALSC" ]; then
		SCCILECO="$STEAM_COMPAT_CLIENT_INSTALL_PATH/$LECO"

		# creating a temporary copy of the evaluatorscript in $SCCILECO:
		writelog "INFO" "${FUNCNAME[0]} - Copying '$SOURCEEVALSC' to '$SCCILECO'" "X" "$ISCRILOG"
		cp "$SOURCEEVALSC" "$DSTEVALSC"

		# replace placeholders with real paths
		writelog "INFO" "${FUNCNAME[0]} - Replacing placeholders with real paths in '$DSTEVALSC'" "X" "$ISCRILOG"
		sed "s:STESHA:$STESHA:g" -i "$DSTEVALSC"
		sed "s:STLDLDIR:$STLDLDIR:g" -i "$DSTEVALSC"
	else
		writelog "SKIP" "${FUNCNAME[0]} - '$DSTEVALSC' already exists" "X" "$ISCRILOG"
	fi
}

function cleanupOutdatedEvals {
	writelog "INFO" "${FUNCNAME[0]} - Cleaning up outdated files in '$EVMETAID'"

	mkProjDir "$EVMETAOLD"
	while read -r oldfile; do
		if [ -f "$oldfile" ]; then
			mv "$oldfile" "$EVMETAOLD"
		fi
	done <<< "$(grep -Rl "foreign_install_path" "$EVMETAID")"

	while read -r oldfile; do
		if [ -f "$oldfile" ]; then
			while read -r lipent; do
				ABSLIINPA="$(cut -d \" -f4 <<< "$lipent")" # too weak?
				if [ ! -d "${ABSLIINPA//\"}" ] && [ "${ABSLIINPA//\"}" != "STLDLDIR" ] && [ "${ABSLIINPA//\"}" != "STESHA" ]; then
					mv "$oldfile" "$EVMETAOLD"
				fi
			done <<< "$(grep "$LIINPA" "$oldfile")"
		fi
	done <<< "$(grep -Rl "$LIINPA" "$EVMETAID")"
	writelog "INFO" "${FUNCNAME[0]} - Finished cleaning up '$EVMETAID'"
}

function doneEvacInstall {
	notiShow "$(strFix "$NOTY_FRDONE" "$EUNID")" "X"
	writelog "INFO" "${FUNCNAME[0]} - $(strFix "$NOTY_FRDONE" "$EUNID")" "E"
}

function runEvacInstall {
	INSTPROT="$1"
	# call $ISCRI via proton with the modified evaluatorscript as parameter:
	writelog "INFO" "${FUNCNAME[0]} - '$EVALSC' - START - STEAM_COMPAT_DATA_PATH=\"$STEAM_COMPAT_DATA_PATH\" '$INSTPROT run $ISCEXE $EVSC'" "X" "$ISCRILOG"
	STEAM_COMPAT_DATA_PATH="$STEAM_COMPAT_DATA_PATH" "$INSTPROT" run "$ISCEXE" "$EVSC"
	# move the temporary modified evaluatorscript into '$STMSHM'
	mv "$DSTEVSC" "$STLSHM/${DSTEVSC##*/}_$RANDOM"
}

function findEarlyProt {
	FEPLOG="$2"
	writelog "INFO" "${FUNCNAME[0]} - Trying to get a proton version from '$1'" "X" "$FEPLOG"
	if grep -q "^USEPROTON" "$1"; then
		CONFPROTRAW="$(grep "^USEPROTON" "$1" | cut -d '=' -f2)"
		CONFPROT="${CONFPROTRAW//\"}"

		if [ -z "$SUSDA" ]; then
			setSteamPaths
		fi

		createProtonList

		if [ -f "$PROTONCSV" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Searching '$CONFPROT' in '$PROTONCSV'" "X" "$FEPLOG"

			if grep -q "^${CONFPROT}" "$PROTONCSV"; then
				writelog "INFO" "${FUNCNAME[0]} - Found '$CONFPROT' in '$PROTONCSV'" "X" "$FEPLOG"
				EARLYPROT="$(grep "^${CONFPROT}" "$PROTONCSV" | cut -d ';' -f2)"
				export EARLYPROT
			else
				writelog "INFO" "${FUNCNAME[0]} - '$CONFPROT' not found in '$PROTONCSV'" "X" "$FEPLOG"
				# if configured USEPROTON proton binary was not found, try to find a minor version bump instead:
				TESTEARLYPROT="$(fixProtonVersionMismatch "CONFPROT" "$STLGAMECFG" "X")"
				if [ -f "$TESTEARLYPROT" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Found a minor version mismatch binary for '$CONFPROT':'$TESTEARLYPROT'" "X" "$FEPLOG"
					EARLYPROT="$TESTEARLYPROT"
					export EARLYPROT
				else
					if [ -n "$TESTEARLYPROT" ]; then
						writelog "SKIP" "${FUNCNAME[0]} - Found '$TESTEARLYPROT' does not exist" "X" "$FEPLOG"
					else
						writelog "SKIP" "${FUNCNAME[0]} - No minor version mismatch binary for '$CONFPROT' found'" "X" "$FEPLOG"
					fi
				fi
			fi
		else
			writelog "ERROR" "${FUNCNAME[0]} - Could not create '$PROTONCSV'" "X" "$FEPLOG"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - No USEPROTON entry found in '$1'" "X" "$FEPLOG"
	fi
}

function checkFirstTimeRun {
	ISCRILOG="$STLSHM/${PROGNAME,,}-${ISCRI}-${STEAM_COMPAT_APP_ID}.log"
	GPFX="$STEAM_COMPAT_DATA_PATH/pfx"
	SCCILECO="$STEAM_COMPAT_CLIENT_INSTALL_PATH/$LECO"
	ISCEXE="$SCCILECO/$ISCRI.exe"
	EVALPROTCONF="$STLSHM/evalprot-${STEAM_COMPAT_APP_ID}.conf"

	function waitForEvaluatorscript {
		mkProjDir "$EVMETAID"
		mkProjDir "$EVMETATITLE"

		ISCRILOG="$2"
		EVALVDF="$SCCILECO/$1"
		writelog "INFO" "${FUNCNAME[0]} - Waiting for '$EVALVDF' to appear" "X" "$ISCRILOG"
		while [ ! -f "$EVALVDF" ]; do
			sleep 0.1
		done
		cp "$EVALVDF" "$EVMETAID"
		writelog "INFO" "${FUNCNAME[0]} - Copied '$EVALVDF' to '$EVMETAID/$1'" "X" "$ISCRILOG"
	}

	rmOldLog

	if [ -n "$STEAM_RUNTIME_LIBRARY_PATH" ]; then
		touch "$STLSHM/SteamStart_${STEAM_COMPAT_APP_ID}.txt"
	fi

	if [ -f "$(find "$LOGFILE" -not -newermt '-5 seconds' 2>/dev/null)" ]; then
		if ! grep -q "$ISCRI" <<< "$(tail -n1 "$LOGFILE")"; then
			writelog "START" "######### '$ISCRI' #########" "X" "$ISCRILOG"
		fi
	fi
	# skip $ISCRI completely if a SKIP file exists for $STEAM_COMPAT_APP_ID
	if [ -f "$EVMETASKIPID/${EVALSC}_${STEAM_COMPAT_APP_ID}.vdf" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - Skipping '$ISCRI' line '$*' because '$EVMETASKIPID/${EVALSC}_${STEAM_COMPAT_APP_ID}.vdf' exists" "X" "$ISCRILOG"
	else
		# filter evaluatorscript command
		if grep -q "$EVALSC" <<< "$@"; then
			# shellcheck disable=SC1003
			EVSC="$(awk -F 'legacycompat\\\' '{print $NF}' <<< "$@")"
		fi

		if [ -d "$EVMETAID" ]; then
			cleanupOutdatedEvals
		fi

		# check if evaluatorscript should be started:
		if [ ! -d "$GPFX" ] || [ ! -f "$EVMETAID/$EVSC" ]; then
			# get a proton version for the install process:
			if grep -q "$EVALSC" <<< "$@"; then

				# first check if the game has a ${PROGNAME,,} config and use the corresponding proton version:
				if [ -z "$EARLYPROT" ] && [ ! -f "$EVALPROTCONF" ] && [ -f "$STLGAMEDIRID/$STEAM_COMPAT_APP_ID.conf" ]; then
					findEarlyProt "$STLGAMEDIRID/$STEAM_COMPAT_APP_ID.conf" "$ISCRILOG"
				fi
				# as fallback check if the game template ${PROGNAME,,} config has a usable proton version:
				if [ -z "$EARLYPROT" ] && [ ! -f "$EVALPROTCONF" ] && [ -f "$STLDEFGAMECFG" ]; then
					findEarlyProt "$STLDEFGAMECFG" "$ISCRILOG"
				fi

				# if everything else failed, check for a "static proton version" just for the base install - either from file or from system variable:
				if [ -z "$EARLYPROT" ] && [ ! -f "$EVALPROTCONF" ] && [ -f "$STLEARLYPROTCONF" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Trying to get a proton version from '$STLEARLYPROTCONF'" "X" "$ISCRILOG"
					source "$STLEARLYPROTCONF"
				fi

				# fallback if for whatever reason USEPROTON is empty everywhere - trying to get NOP
				if [ -z "$EARLYPROT" ]; then
					if [ -z "$SUSDA" ]; then
						setSteamPaths
					fi
					writelog "INFO" "${FUNCNAME[0]} - Last try - trying to get Newest Official Proton" "X" "$ISCRILOG"
					CONFPROT="$(getDefaultProton)"
					createProtonList
					EARLYPROT="$(grep "^${CONFPROT}" "$PROTONCSV" | cut -d ';' -f2)"
				fi

				# write found protonversion into temp config, so below GECUST runs can use it as well:
				if [ -n "$EARLYPROT" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Writing 'EARLYPROT=$EARLYPROT' into '$EVALPROTCONF'" "X" "$ISCRILOG"
					echo "EARLYPROT=\"$EARLYPROT\"" > "$EVALPROTCONF"
					echo "TEVSC=\"$EVSC\"" >> "$EVALPROTCONF"
				fi
			fi

			# reload proton version from above EVALSC run:
			TEVSC="$NON"
			if [ -z "$EARLYPROT" ] && [ -f "$EVALPROTCONF" ]; then
				source "$EVALPROTCONF"
			fi
			# only continue with proton being available:
			if [ -n "$EARLYPROT" ] && [ -f "$EARLYPROT" ]; then
					# filter evaluatorscript command
					if grep -q "$EVALSC" <<< "$@"; then
						mapfile -d " " -t -O "${#RUNISCCMD[@]}" RUNISCCMD < <(printf '%s' "$(awk -F 'run ' '{print $NF}' <<< "$*")")
						# no evaluatorscript for the started game yet in the metadata - so starting the collector:

						SRCCUSTEVSC="$EVMETACUSTOMID/$EVSC"
						SRCORIGEVSC="$EVMETAID/$EVSC"

						TEVALSC="${STLSHM}/${EVALSC}_${STEAM_COMPAT_APP_ID}_temp.vdf"
						if [ -f "$TEVALSC" ]; then
							SRCEVSC="$TEVALSC"
							writelog "INFO" "${FUNCNAME[0]} - Using '$SRCEVSC' as source" "X" "$ISCRILOG"
						fi

						if [ -z "$SRCEVSC" ]; then
							if [ ! -f "$SRCORIGEVSC" ] && [ ! -f "$SRCCUSTEVSC" ]; then
								SRCEVSC="$SRCORIGEVSC"
								writelog "INFO" "${FUNCNAME[0]} - Both the copy '$SRCORIGEVSC' of the original and a custom '$SRCCUSTEVSC' are missing - trying to get the original'" "X" "$ISCRILOG"
							elif [ -f "$SRCORIGEVSC" ] && [ ! -f "$SRCCUSTEVSC" ]; then
								SRCEVSC="$SRCORIGEVSC"
								writelog "INFO" "${FUNCNAME[0]} - Using '$SRCEVSC' as source" "X" "$ISCRILOG"
							elif [ -f "$SRCORIGEVSC" ] && [ -f "$SRCCUSTEVSC" ]; then
								SRCEVSC="$SRCCUSTEVSC"
								writelog "INFO" "${FUNCNAME[0]} - Using custom '$SRCCUSTEVSC' as source, as it overrides the original '$SRCORIGEVSC' which also does exist" "X" "$ISCRILOG"
							elif [ ! -f "$SRCORIGEVSC" ] && [ -f "$SRCCUSTEVSC" ]; then
								SRCEVSC="$SRCCUSTEVSC"
								writelog "INFO" "${FUNCNAME[0]} - Using custom '$SRCCUSTEVSC' as source, and the original '$SRCORIGEVSC' does not exist" "X" "$ISCRILOG"
							fi
						fi

						if [ -n "$EVSC" ] && [ ! -f "$SRCEVSC" ] && [ "$SRCEVSC" == "$SRCORIGEVSC" ]; then
							waitForEvaluatorscript "$EVSC" "$ISCRILOG" &
							writelog "INFO" "${FUNCNAME[0]} - '$EVALSC' START - '$EARLYPROT run ${RUNISCCMD[*]}'" "X" "$ISCRILOG"
							"$EARLYPROT" run "${RUNISCCMD[@]}"
							while [ ! -f "$SRCEVSC" ]; do
								sleep 0.1
							done
						fi

						# have a evaluatorscript to work with:
						if [ -f "$SRCEVSC" ]; then
							DSTEVSC="$STEAM_COMPAT_CLIENT_INSTALL_PATH/$EVSC"
							createModifiedEvalsc "$SRCEVSC" "$DSTEVSC"

							if grep -q "$EVMETACUSTOMID" <<< "$SRCEVSC" ; then
								notiShow "$(strFix "$NOTY_FRSTART" "Custom")" "X"
							elif grep -q "$STLSHM" <<< "$SRCEVSC" ; then
								notiShow "$(strFix "$NOTY_FRSTART" "Live")" "X"
							else
								notiShow "$(strFix "$NOTY_FRSTART" "Regular")" "X"
							fi

							runEvacInstall "$EARLYPROT"
							SRCADDONEVSC="$EVMETAADDONID/$EVSC"
							if [ -f "$SRCADDONEVSC" ]; then
								writelog "INFO" "${FUNCNAME[0]} - Found additional install script in '$SRCADDONEVSC'" "X" "$ISCRILOG"

								DSTEVSC="$STEAM_COMPAT_CLIENT_INSTALL_PATH/$EVSC"
								createModifiedEvalsc "$SRCADDONEVSC" "$DSTEVSC"
								notiShow "$(strFix "$NOTY_FRSTART" "Addon")" "X"
								runEvacInstall "$EARLYPROT"
							fi

							if [ "$SRCEVSC" == "$TEVALSC" ]; then
								rm "$TEVALSC" 2>/dev/null
								writelog "INFO" "${FUNCNAME[0]} - Removed temporary '$TEVALSC'" "X" "$ISCRILOG"
							fi

							writelog "INFO" "${FUNCNAME[0]} - '$EVALSC' - STOP - '$EARLYPROT run $ISCEXE $EVSC'" "X" "$ISCRILOG"
						else
							writelog "INFO" "${FUNCNAME[0]} - '$EVALSC' - SKIP - '$SRCEVSC' not found" "X" "$ISCRILOG"
						fi
					# filter get-current-step command - this only updates the steam install gui process.
					elif grep -q "$GECUST" <<< "$@"; then
						"$EARLYPROT" "$@" | tee "$FRSTATE" | tee -a "$ISCRILOG"
						echo "" >> "$ISCRILOG"
						STINFO="$(date +%H:%M:%S) $NICEPROGNAME running"
						if [ -f "$TEVSC" ]; then
							# shellcheck disable=SC1003
							while read -r COMRED; do
								if "$PGREP" -a "" | grep "$STEWOS" | grep -v "runasadmin" | grep "${COMRED//\"}" >/dev/null ; then
										writelog "INFO" "${FUNCNAME[0]} - Installation of '${COMRED//\"}' running" | tee -a "$ISCRILOG"
									STINFO="$(date +%H:%M:%S) $NICEPROGNAME sees ${COMRED//\"}"
									break
								fi
							done <<< "$(grep "process 1" "$TEVSC" | awk -F'\\' '{print $NF}')"
						fi
						printf '\n%s\n' "$STINFO"	# heh, hello Steam Gui
					# should not happen, but better log it anyway just in case:
					else
						writelog "SKIP" "${FUNCNAME[0]} - Skipping '$ISCRI' command '$*' - Neither '$EVALSC' nor '$GECUST' found in command exiting" "X" "$ISCRILOG"
					fi
			else
				if ! grep -q "$GECUST" <<< "$@"; then
					writelog "SKIP" "${FUNCNAME[0]} - No usable Proton version found - ignoring '$ISCRI' command '$*' - Exiting" "X" "$ISCRILOG"
				fi
			fi
		else
			if [ -d "$GPFX" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - Skipping '$ISCRI' command '$*', because '$GPFX' already exists - Exiting" "X" "$ISCRILOG"
			elif [ -f "$EVMETAID/$EVSC" ]; then
				writelog "SKIP" "${FUNCNAME[0]} - Skipping '$ISCRI' command '$*', because '$EVMETAID/$EVSC' already exists - Exiting" "X" "$ISCRILOG"
			fi
		fi
	fi
}

function verbRun {
	VERBLOG="$STLSHM/${PROGNAME,,}-VERB-${STEAM_COMPAT_APP_ID}.log"
	GPFX="$STEAM_COMPAT_DATA_PATH/pfx"
	SCCILECO="$STEAM_COMPAT_CLIENT_INSTALL_PATH/$LECO"
	EVALPROTCONF="$STLSHM/evalprot-${STEAM_COMPAT_APP_ID}.conf"

	writelog "INFO" "${FUNCNAME[0]} - 'verb' command detected '${*}'" "X" "$VERBLOG"
	writelog "INFO" "${FUNCNAME[0]} - Settings variable SteamAppId to '$STEAM_COMPAT_APP_ID' to make verb command happy"
	export SteamAppId="$STEAM_COMPAT_APP_ID"

	# first check if the game has a ${PROGNAME,,} config and use the corresponding proton version:
	if [ -z "$EARLYPROT" ] && [ -f "$STLGAMEDIRID/$STEAM_COMPAT_APP_ID.conf" ]; then
		findEarlyProt "$STLGAMEDIRID/$STEAM_COMPAT_APP_ID.conf" "$VERBLOG"
	fi
	# as fallback check if the game template ${PROGNAME,,} config has a usable proton version:
	if [ -z "$EARLYPROT" ] && [ -f "$STLDEFGAMECFG" ]; then
		findEarlyProt "$STLDEFGAMECFG" "$VERBLOG"
	fi

	# if everything else failed, check for a "static proton version" just for the base install - either from file or from system variable:
	if [ -z "$EARLYPROT" ] && [ -f "$STLEARLYPROTCONF" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Trying to get a proton version from '$STLEARLYPROTCONF'" "X" "$VERBLOG"
		source "$STLEARLYPROTCONF"
	fi

	# fallback if for whatever reason USEPROTON is empty everywhere - trying to get NOP
	if [ -z "$EARLYPROT" ]; then
		if [ -z "$SUSDA" ]; then
			setSteamPaths
		fi
		writelog "INFO" "${FUNCNAME[0]} - Last try - trying to get Newest Official Proton" "X" "$VERBLOG"
		CONFPROT="$(getDefaultProton)"
		createProtonList
		if [ -n "$CONFPROT" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Picking '^${CONFPROT}' from '$PROTONCSV'" "X" "$VERBLOG"
			EARLYPROT="$(grep "^${CONFPROT}" "$PROTONCSV" | cut -d ';' -f2)"
		else
			writelog "WARN" "${FUNCNAME[0]} - Could not determine default Proton" "X" "$VERBLOG"
		fi
	fi

	# write found protonversion into temp config, so below GECUST runs can use it as well:
	if [ -f "$EARLYPROT" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Running verb command '${*}' using proton '$EARLYPROT'" "X" "$VERBLOG"
		"$EARLYPROT" "$WFEAR" "$@" >> "$VERBLOG" 2>&1
	else
		writelog "INFO" "${FUNCNAME[0]} - Skipping verb command '${*}', because no usable proton '$EARLYPROT' was found" "X" "$VERBLOG"
	fi
}

function getCurrentCommandline {
	echo "$@" >> "${STLSHM}/cmdline.txt"
	# filter $ISCRI commands
	if grep -q "$ISCRI" <<< "$@"; then
		while read -r IARG; do
			mapfile -t -O "${#ISCRICMD[@]}" ISCRICMD <<< "$IARG"
		done <<< "$(printf "%s\n" "$@")"
		if [ "${ISCRICMD[0]}" == "%verb%" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Cutting '%verb%' from arguments '${*}'" "P"
			ISCRICMD=( "${ISCRICMD[@]:1}" )
		fi
		writelog "INFO" "${FUNCNAME[0]} - Running checkFirstTimeRun with arguments '${ISCRICMD[*]}'" "P"
		checkFirstTimeRun "${ISCRICMD[@]}"
	elif grep -q "%verb%" <<< "$@"; then
		echo "${FUNCNAME[0]} - Found '%verb%' in the command line '${*}'" >> "$STLSHM/DEBUG-verb.txt"
		while read -r IARG; do
			IARG="${IARG//steamservice.exe/SteamService.exe}"
			mapfile -t -O "${#VERBCMD[@]}" VERBCMD <<< "$IARG"
		done <<< "$(printf "%s\n" "$@")"
		VERBCMD=( "${VERBCMD[@]:1}" )
		echo "${FUNCNAME[0]} - Executing 'verb' command '${VERBCMD[*]}' through verbRun" >> "$STLSHM/DEBUG-verb.txt"
		verbRun "${VERBCMD[@]}" # >> "$STLSHM/DEBUG-verb.txt" 2>&1
		exit
	else
		INPROSTR="proton $WFEAR"
		if grep -q "$INPROSTR" <<< "$@"; then
			HAVEINPROTON=1
			writelog "INFO" "${FUNCNAME[0]} - Found Proton in command line arguments '${*}'" "P"
		else
			HAVEINPROTON=0
			writelog "INFO" "${FUNCNAME[0]} - No Proton in command line arguments '${*}'" "P"
		fi
	fi
}

function initShmStl {  # TODO can shorten?
	mkProjDir "$MTEMP"
	SHMVERS="$STLSHM/version"

	if [ -f "$SHMVERS" ]; then
		if [ "$PROGVERS" != "$(cat "$SHMVERS")" ]; then
			mv "${STLSHM}" "${STLSHM}-$((100 + RANDOM % 100))"
			mkProjDir "$MTEMP"
			echo "$PROGVERS" > "$SHMVERS"
		fi
	else
		echo "$PROGVERS" > "$SHMVERS"
	fi
}

### STEAM DECK BEGIN

function steamdeckClose {
	if [ "$ONSTEAMDECK" -eq 1 ]; then
		GTKCSSFILE="$HOME/.config/gtk-3.0/gtk.css"
		if [ -f "${GTKCSSFILE}_ORIGNAL" ] ; then
			writelog "INFO" "${FUNCNAME[0]} - recovering original gtk.css from '${GTKCSSFILE}_ORIGNAL'"
			mv "${GTKCSSFILE}_ORIGNAL" "$GTKCSSFILE"
		fi

		if [ -n "$STLCTLID" ] && [ "$STLCTLID" != "$PLACEHOLDERAID" ]; then
			VTAPP="769"
			writelog "INFO" "${FUNCNAME[0]} - Loading controller configuration of ValveTestApp769 '$VTAPP' to map the controller to the default Steam Deck settings via 'steam steam://forceinputappid/$VTAPP'"
			steam steam://forceinputappid/"$VTAPP"
		fi
	fi
}

function steamdeckBeforeGame {
	if [ "$ONSTEAMDECK" -eq 1 ]; then
		if [ "$FIXGAMESCOPE" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Final Deck Check: Looks like we're in Game Mode (FIXGAMESCOPE is '$FIXGAMESCOPE')"
			writelog "INFO" "${FUNCNAME[0]} - Force-enabling DXVK_HDR=1 for Steam Deck Game Mode, allows HDR support for Steam Deck OLED and HDR displays attached to Steam Deck"

			# Override config value without updating the stored value itself, to preserve compatibility with Desktop Mode
			export DXVK_HDR=1
		else
			writelog "INFO" "${FUNCNAME[0]} - Final Deck Check: Looks like we're in Desktop Mode (FIXGAMESCOPE is '$FIXGAMESCOPE')"
		fi

		if [ "$USEGAMESCOPE" -eq 1 ] && [ "$FIXGAMESCOPE" -eq 1 ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Disabling own GameScope on SteamDeck Game Mode" "X"
			USEGAMESCOPE=0
		else
			writelog "INFO" "${FUNCNAME[0]} - Allowing GameScope enabled on SteamDeck in Desktop Mode" "X"
		fi

		if [ "$USEGAMEMODERUN" -eq 1 ] && [ "$FIXGAMESCOPE" -eq 1 ]; then
			writelog "SKIP" "${FUNCNAME[0]} - Disabling own Feral GameMode tool (gamemoderun) on SteamDeck Game Mode" "X"
			USEGAMEMODERUN=0
		else
			writelog "INFO" "${FUNCNAME[0]} - Allowing Feral GameMode tool (gamemoderun) enabled on SteamDeck in Desktop Mode" "X"
		fi

		if [ -n "$STLCTLID" ] && [ "$STLCTLID" != "$PLACEHOLDERAID" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Loading controller configuration for the current game via 'steam steam://forceinputappid/$AID'"
			steam steam://forceinputappid/"$AID"
		fi
	fi
}

function steamdeckControl {
	if [ "$ONSTEAMDECK" -eq 1 ]; then
		setSDCfg
		export STLCTLID="$PLACEHOLDERAID"

		if [ ! -f "$STLSDLCFG" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Creating initial '$STLSDLCFG'"
			touch "$STLSDLCFG"
			echo "STLCTLID=\"$PLACEHOLDERAID\"" > "$STLSDLCFG"
		fi

		if [ -f "$STLSDLCFG" ]; then
			loadCfg "$STLSDLCFG" X
		fi

		if [ "$STLCTLID" != "$PLACEHOLDERAID" ]; then
			writelog "WARN" "${FUNCNAME[0]} - As documented you're on your own if mappings fail and you need to fix it manually via ssh"
			STLCNTRLD="$DEFSTEAMAPPSCOMMON/Steam Controller Configs/${STEAMUSERID}/config/${STLCTLID}"
			STLCNTRLF="$STLCNTRLD/controller_neptune.vdf"

			if [ -f "$STLCNTRLF" ] && ! grep -q "$NICEPROGNAME" "$STLCNTRLF"; then
				writelog "INFO" "${FUNCNAME[0]} - Good - found an original controller config under '$STLCNTRLF' - creating backup under ${STLCNTRLF}_ORG"
				mv "$STLCNTRLF" "${STLCNTRLF}_ORG"
			fi

			if  [ -f "$STLCNTRLF" ] && grep -q "$NICEPROGNAME" "$STLCNTRLF"; then
				if [ "$(grep -m1 "revision" "$SRCCTRLF" | grep -oE "[0-9]")" -gt "$(grep -m1 "revision" "$STLCNTRLF" | grep -oE "[0-9]")" ]; then
					writelog "INFO" "${FUNCNAME[0]} - Updating '$STLCNTRLF'"
					cp "$SRCCTRLF" "$STLCNTRLF"
				else
					writelog "INFO" "${FUNCNAME[0]} - '$STLCNTRLF' is already up-to-date"
				fi
			fi

			if [ -f "${STLCNTRLF}_ORG" ] && [ ! -f "$STLCNTRLF" ]; then
				SRCCTRLF="${PREFIX}/misc/stl-steamdeck-control.vdf"
				mkProjDir "$STLCNTRLD"
				writelog "INFO" "${FUNCNAME[0]} - Copying $NICEPROGNAME '$SRCCTRLF' to '$STLCNTRLF'"
				cp "$SRCCTRLF" "$STLCNTRLF"
				# might not be even required, but the data is ready anyway, so make it as complete as possible
				sed "s:XXXXXX:$STEAMUSERID:g" -i "$STLCNTRLF"
				sed "s:YYYYYY:$STLCTLID:g" -i "$STLCNTRLF"
			fi

			if grep -q "$NICEPROGNAME" "$STLCNTRLF"; then
				writelog "INFO" "${FUNCNAME[0]} - Loading controller configuration of SteamAppId '$STLCTLID' to allow joypad controls in $PROGNAME via 'steam steam://forceinputappid/$STLCTLID'"
				steam steam://forceinputappid/"$STLCTLID" > "$STLSHM/${FUNCNAME[0]}_stdout.txt" 2> "$STLSHM/${FUNCNAME[0]}_stderr.txt"
				writelog "INFO" "${FUNCNAME[0]} - controller settings for '$STLCTLID' loaded "
			else
				writelog "SKIP" "${FUNCNAME[0]} - Found controller config '', but it doesn't contain '$NICEPROGNAME' - not loading"
			fi
		else
			writelog "SKIP" "${FUNCNAME[0]} - Skipping loading controller mapping for Steam Deck, because STLCTLID is not configured: '$STLCTLID'"
		fi
	fi
}

# NOTE: More may be added here in future to handle install failure
function steamDeckInstallFail {
	printf "\n%s\n" "$NOTY_STEAMDECK_INSTALLFAIL"
	notiShow "$NOTY_STEAMDECK_INSTALLFAIL" "X"
	exit 1;
}

# Generic function to download dependency from URL (mainly intended for fetching from package repos)
function fetchAndExtractDependency {
	function removeFileExtension {
		local FNAME="$1"
		local LASTFNAME=""
		while ! [ "$FNAME" = "$LASTFNAME" ]; do
			LASTFNAME="$FNAME"
			FNAME="${FNAME%.*}"
		done
		echo "$FNAME"
	}

	# Return dependency name stripped of version, architecture, file extension
	function getPrettyDependencyName {
		removeFileExtension "$( echo "$1" | sed -E 's:(\-[0-9]\.[0-9])+(.+)::g;s:^[^A-Za-z0-9]+::g' )"
	}

	# Takes dependency filename and strips:
	# - any non-letters-or-numbers from start of string
	# - the architecture string and everything after it
	# - any remaining letters or numbers
	# - any remaining non-numbers from the end of the string
	function getDependencyVersion {
		echo "$1" | sed -E 's:^[^A-Za-z0-9]::g;s:x86+(.*)::g;s:[a-zA-Z]::g;s:[^0-9]+$::g' | grep -oE "[0-9]\.[0-9]+(.+)"
	}

	# Variables used to build URL
	local ARCHIVEURL
	local EXTRACTPATH
	local ARCHIVENAME

	ARCHIVEURL="$1"
	EXTRACTPATH="$2"
	ARCHIVENAME="${3:-${1##*/}}"  # If no extract name passed, take filename from end of archive URL

	local CURLCMD
	local EXTRACTCMD
	local EXTRACTCMDFLAGS

	CURLCMD="curl"
	EXTRACTCMD="tar"
	EXTRACTCMDFLAGS="xf"

	local LOCALVERS
	local STLVERS

	# Download dependency
	mkdir -p "$EXTRACTPATH"
	if ! [ -f "$EXTRACTPATH/$ARCHIVENAME" ]; then
		if ! [ "$INTERNETCONNECTION" -eq 0 ]; then
			writelog "INFO" "${FUNCNAME[0]} - Installing '$ARCHIVENAME' from '$ARCHIVEURL' to installation directory '$EXTRACTPATH'"
			echo "Downloading dependency '$ARCHIVENAME'..."
			if "$CURLCMD" -Lq "$ARCHIVEURL" -o "$EXTRACTPATH/$ARCHIVENAME"; then  # Not showing notifier to reduce notifier spam
				writelog "INFO" "${FUNCNAME[0]} - Successfully downloaded dependency '$ARCHIVENAME'"
				echo "Successfully downloaded $ARCHIVENAME!"
			else  # Download failure
				writelog "WARN" "${FUNCNAME[0]} - Failed to download dependency '$ARCHIVENAME', will attempt to continue with installation in case dependency archives already exist at '$EXTRACTPATH'"
				notiShow "$(strFix "$NOTY_STEAMDECK_DEPINSTALLFAIL" "$ARCHIVENAME")" "X"
				strFix "$NOTY_STEAMDECK_DEPINSTALLFAIL" "$ARCHIVENAME"
			fi
		else  # No internet connection
			writelog "WARN" "${FUNCNAME[0]} - No internet connection, can't download dependency '$ARCHIVENAME' - Will check for locally installed dependencies later on (should be located at '$EXTRACTPATH/$ARCHIVENAME')"
			echo "WARNING: No Internet Connection, cannot download dependency '$ARCHIVENAME'. For offline installation, manually place the file in '$EXTRACTPATH'."
			notiShow "$(strFix "$NOTY_STEAMDECK_NOINTERNETDEPSWARN" "$ARCHIVENAME" "$EXTRACTPATH")"
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - Dependency '$ARCHIVENAME' already exists at installation directory '$EXTRACTPATH', skipping redownload..."
		echo "Dependency '$ARCHIVENAME' already exists at installation directory '$EXTRACTPATH', nothing to download"
	fi

	# Extracting dependencies - If we can't find an exact match for the downloaded file, try to find a fuzzy match based on 'real' package name
	if ! [ -f "$EXTRACTPATH/$ARCHIVENAME" ]; then
		# Get list of files and remove version string from it, then check if this name is inside our target archive file
		# This basically tries to find any archives that have the 'actual' package name e.g. 'innoextract' from 'innoextract-1.1-1.pkg.tar.zst', then tries to search if any files in the directory match
		# the archive we passed in to try to download but couldn't and try to extract them. This lets us extract dependencies for a potential archive match if the name is not exact
		local DEPMATCHFOUND=0
		mapfile -t DEPFILES < <( find "$EXTRACTPATH" -maxdepth 1 -type f -iname \*"*.*"\* -exec basename {} \; )
		if ! [[ "${#DEPFILES[@]}" -eq 0 ]]; then
			writelog "INFO" "${FUNCNAME[0]} - Found archive in '$EXTRACTPATH' that is potential match for '$ARCHIVENAME'"
			for file in "${DEPFILES[@]}"; do
				# Removes version number, anything before the first space, and removes anything that isn't a sequence of letters or numbers from the beginning of the filename
				# Attempts to be as generous as possible in matching *any* match for the dependency name
				PRETTYFILENAME="$( getPrettyDependencyName "$file" )"
				PRETTYARCHIVENAME="$( getPrettyDependencyName "$ARCHIVENAME" )"

				if [[ ( "$ARCHIVENAME" =~ $PRETTYFILENAME || "$PRETTYFILENAME" =~ $PRETTYARCHIVENAME ) ]]; then
					writelog "INFO" "${FUNCNAME[0]} - Found potential existing dependency '$file', assuming dependency '$ARCHIVENAME' is already satisfied."
					echo "Found potential matching dependency archive for '$ARCHIVENAME' at '$file'"

					LOCALVERS="$( getDependencyVersion "$file" )"
					STLVERS="$( getDependencyVersion "$ARCHIVENAME" )"
					# Check local archive version vs. what STL asked for
					if [ -z "$LOCALVERS" ]; then
						# Could not get version string from archive
						writelog "WARN" "${FUNCNAME[0]} - Could not get version from archive - This dependency version may not work if is newer than '$STLVERS'"
						echo "Could not get version from archive - This dependency version may not work if is newer than '$STLVERS'!"
					else
						if [ "$LOCALVERS" = "$STLVERS" ]; then  # Version match
							writelog "INFO" "${FUNCNAME[0]} - Archive version appears to be the same version that SteamTinkerLaunch asked for - Should be ok to install this version"
							echo "Dependency version looks ok"
						else  # Version newer/older (might still work, but warn the user anyway)
							writelog "WARN" "${FUNCNAME[0]} - Archive version '${LOCALVERS}' does not match the version that SteamTinkerLaunch asked for ('$STLVERS') - This may cause problems with the installation!"
							echo "Dependency version does not match what SteamTinkerLaunch was looking for - This may cause problems with your installation!"
						fi
					fi

					ARCHIVENAME="$file"
					DEPMATCHFOUND=1
					break
				fi
			done
		fi

		# Abort if we couldn't match a dependency
		if [[ "$DEPMATCHFOUND" -eq 0 ]]; then
			writelog "INFO" "${FUNCNAME[0]} - Could not find any matching dependency archives in '$EXTRACTPATH' for '$ARCHIVENAME' - Assuming this dependency is missing and installation cannot continue"
			echo "Could not find any archive in '$EXTRACTPATH' for '$ARCHIVENAME', aborting install..."
			return 1;
		fi
	fi

	# Extract archive now that we know we should have *something* to extract
	writelog "INFO" "${FUNCNAME[0]} - Extracting dependency '$ARCHIVENAME' at '$EXTRACTPATH/$ARCHIVENAME'"
	echo "Extracting dependency '$ARCHIVENAME'..."
	if "$EXTRACTCMD" "$EXTRACTCMDFLAGS" "$EXTRACTPATH/$ARCHIVENAME" -C "$EXTRACTPATH"; then
		# Succes extraction
		writelog "INFO" "${FUNCNAME[0]} - Successfully extracted dependency to '$EXTRACTPATH/$ARCHIVENAME'"
		echo "Successfully extracted '$ARCHIVENAME' to '$EXTRACTPATH'"
	else
		# Failed extraction
		writelog "ERROR" "${FUNCNAME[0]} - Failed to extract dependency '$ARCHIVENAME' to '$EXTRACTPATH' - Consider checking file/folder permissions"
		notiShow "$(strFix "$NOTY_STEAMDECK_EXTRACTFAIL" "$ARCHIVENAME" "$EXTRACTPATH")" "X"
		strFix "$NOTY_STEAMDECK_EXTRACTFAIL" "$ARCHIVENAME" "$EXTRACTPATH"
		return 1;
	fi
}

function checkSteamDeckDependencies {

	function installDependencyVersionFromURL {
		local DEPCMD="$1"
		local DEPFILENAME="$2"
		local DEPDIR="$3"
		local REPOURL="$4"
		local CHECKCMD
		CHECKCMD="$($DEPCMD &> /dev/null --version && echo "OK" || echo "NOK")"


		# The check now says, don't update dependencies if all of these conditions are true:
		#  1. The dependency file exists
		#  2. The dependency can actually be ran, confirming it is a valid file
		#  3. SteamTinkerLaunch has not updated or autoupdater isn't enabled, so there would be no change in dependency version and thus no need to update
		# If any of these are false, we need to check our dependencies (if a file is missing we would need to update, or if it cannot be used we need to update, and also if STL updated we may need a newer version, so update).
		if [[ -f "$(command -v "$DEPCMD")" && "$CHECKCMD" = "OK" ]] \
		&& ! ( [ "$STEAMDECK_AUTOUP" -eq 1 ] && checkSteamDeckSTLUpdated ); then
			writelog "INFO" "${FUNCNAME[0]} - Using '$DEPCMD' binary found in path: '$(command -v "$DEPCMD")'"
			echo "Dependency '$DEPCMD' already installed, nothing to do."
		else
			writelog "INFO" "${FUNCNAME[0]} - Downloading $DEPCMD version automatically from URL '$REPOURL'"
			writelog "INFO" "${FUNCNAME[0]} - curl -Lq \"$REPOURL\" -o \"$DEPDIR/$DEPFILENAME\""

			notiShow "$(strFix "$NOTY_STEAMDECK_DEPSDOWNLOAD" "$DEPCMD")" "X"
			strFix "$NOTY_STEAMDECK_DEPSDOWNLOAD" "$DEPCMD"

			fetchAndExtractDependency "$REPOURL" "$DEPDIR" "$DEPFILENAME" || steamDeckInstallFail

			STEAMDECKWASUPDATE=1
		fi
	}

	# if this really changes, it could be grepped directly from /etc/pacman.d/mirrorlist as well: (was previously used for wget, keeping commented in case we need it in future)
	#SDREPO="https://steamdeck-packages.steamos.cloud/archlinux-mirror/extra/os/x86_64/"
	# ARCHURL="https://archlinux.org/packages/community/x86_64"
	ARCHARCHIVEURL="https://archive.archlinux.org/packages"

	INNOEXTRACTVERS="1.9-11"
	INNOEXTRACTFILE="$INNOEXTRACT-$INNOEXTRACTVERS-x86_64.pkg.tar.zst"
	INNOEXTRACTURL="$ARCHARCHIVEURL/i/$INNOEXTRACT/$INNOEXTRACTFILE"

	CABEXTRACTVERS="1.9.1-2"
	CABEXTRACTFILE="$CABEXTRACT-$CABEXTRACTVERS-x86_64.pkg.tar.zst"
	CABEXTRACTURL="$ARCHARCHIVEURL/c/$CABEXTRACT/$CABEXTRACTFILE"

	printf '\n'

	installDependencyVersionFromURL "$INNOEXTRACT" "$INNOEXTRACTFILE" "$STLDEPS" "$INNOEXTRACTURL" || steamDeckInstallFail
	installDependencyVersionFromURL "$CABEXTRACT" "$CABEXTRACTFILE" "$STLDEPS" "$CABEXTRACTURL" || steamDeckInstallFail

	updateSteamDeckLastVers # If everything went well, we update the lastversion file

	if [ -f "$(command -v "yad")" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Using yad binary found in path: '$(command -v "yad")'"
		echo "Dependency 'yad' already installed, nothing to do."
		# Force update of global config file to add Yad path if it exists in $HOME/stl/deps/yad/bin
		touch "$FUPDATE"
		updateConfigEntry "YAD" "$( command -v "yad" )" "$STLDEFGLOBALCFG"
	else
		printf '\n'
		writelog "INFO" "${FUNCNAME[0]} - Using yad app image"
		setYadBin "ai" "sd" || steamDeckInstallFail
	fi
}

function installFilesSteamDeck {
	local INSTALLEDPROGVERS
	local SCRIPTDIR

	if [[ "$INTERNETCONNECTION" -eq 1 ]]; then
		# Notification for either updating or downloading STL on Deck
		if [ -d "$SYSTEMSTLCFGDIR/.git" ]; then
			notiShow "$NOTY_STEAMDECK_UPDATE" "X"
			echo "$NOTY_STEAMDECK_UPDATE"
			STEAMDECKWASUPDATE=1
		else
			notiShow "$NOTY_STEAMDECK_DOWNLOAD" "X"
			echo "$NOTY_STEAMDECK_DOWNLOAD"
		fi

		# Attempt to get update files from Git
		if ! gitUpdate "$PREFIX" "$PROJECTPAGE"; then
			writelog "WARN" "${FUNCNAME[0]} - Could not clone/pull changes from git repo, doing some checks"
			if ! ping -q -c1 archlinux.org &>/dev/null; then
				writelog "WARN" "${FUNCNAME[0]} - Could not ping Arch Linux website, maybe there is a connectivity problem - Install / Update may fail"
			else
				writelog "WARN" "${FUNCNAME[0]} - Looks like GitHub is ok, attempting to update via a fresh clone to '${PREFIX}-temp'"
			fi

			if ! gitUpdate "${PREFIX}-temp" "$PROJECTPAGE"; then
				writelog "WARN" "${FUNCNAME[0]} - Still failed to download from GitHub, though it should be up -Not trying to update again, maybe something is wrong with install files"
				echo "Could not pull down changes from Git, even though GitHub should be up. Not attempting another update right now, maybe try again later or try a fresh install if the issue persists."
			else
				# TODO clean this up

				# Currently we assume the removal of the existing files and moving of the new files will succeed ok
				# There could be a very rare instance where due to permission faults or somethimg, this fails
				# We aren't worried about this for now, but a PR might be welcome on this in future :-)
				writelog "INFO" "${FUNCNAME[0]} - Fresh clone succeeded ok, overwriting existing installation with fresh clone"

				# This was disabled because I thought it caused issues on Steam Deck
				# It needs testing before it can be safely enabled, if we even want to re-enable this functionality

				# rm -rf "$PREFIX"
				# mv "${PREFIX}-temp" "$PREFIX"
				# rm -rf "${PREFIX}-temp"

				writelog "INFO" "${FUNCNAME[0]} - Successfully pulled updated changes!"
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - Fetching from git seems to have succeeded ok."
		fi
	else
		# Get version of existing STL install, if present, and compare with our version
		writelog "WARN" "${FUNCNAME[0]} - No Internet Connection detected, cannot clone Git repo - Attempting to manually install"

		SCRIPTDIR="$( realpath "$0" )"
		SCRIPTDIR="${SCRIPTDIR%/*}"

		# Offline installation of STL was previously installed/attempted - With check to try and ensure scriptdir is a valid STL install and not a standalone script
		if ! [ -d "$SCRIPTDIR/lang" ] && ! [ -d "$SCRIPTDIR/misc" ] && ! [ -d "$SCRIPTDIR/guicfgs" ]; then
			writelog "WARN" "${FUNCNAME[0]} - Script dir '$SCRIPTDIR' does not look like a valid SteamTinkerLaunch installation directory! Not copying files in case this script is not in a proper STL folder"
			echo "WARNING: Not updating offline filees - It looks like you're trying to install SteamTinkerLaunch as a standalone script outside of its downloaded files."
		elif [ -f "$PREFIX/steamtinkerlaunch" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Found existing SteamTinkerLaunch files at '$PREFIX', checking if we need to update"
			INSTALLEDPROGVERS="$( grep -i "^PROGVERS=.*." "$PREFIX/steamtinkerlaunch" | cut -d '"' -f 2 )"
			writelog "INFO" "${FUNCNAME[0]} - Currently installed STL version: $INSTALLEDPROGVERS"
			writelog "INFO" "${FUNCNAME[0]} - This script's STL version: $PROGVERS"
			# Check if we actually need to update (running script ver > currently installed script ver)
			# Not a fool-proof test, sometimes PROGVERS isn't bumped, but a user can always manually copy the files if they want to - We'll assume they downloaded the latest version ahead of time and want to manually install that
			if [[ "$PROGVERS" > "$INSTALLEDPROGVERS" ]]; then
				writelog "INFO" "${FUNCNAME[0]} - Existing SteamTinkerLaunch installation is older than current version - Installation will continue by copying downloaded files at '$SCRIPTDIR' to '$PREFIX'"
				echo "Updating SteamTinkerLaunch to '$PROGVERS' by copying installation files to '$PREFIX' for offline installation..."
				cp -R "$SCRIPTDIR"/* "$PREFIX"
				STEAMDECKWASUPDATE=1
			else
				writelog "INFO" "${FUNCNAME[0]} - Existing SteamTinkerLaunch installation is newer than or same as current version - No code to update"
				echo "Existing SteamTinkerLaunch install is up-to-date, verifying dependencies..."
				STEAMDECKDIDINSTALL=0
			fi
		else
			# No existing STL installation - Let's create one using the files downloaded with the script currently running!
			writelog "INFO" "${FUNCNAME[0]} - No existing STL installation found - Installation will continue by copying downloaded files at '$SCRIPTDIR' to '$PREFIX'"
			echo "No existing STL installation found, so copying installation files to '$PREFIX' for offline installation..."
			cp -R "$SCRIPTDIR"/* "$PREFIX"
		fi
	fi
}

function steamdedeckt {
	if [ -f "/etc/os-release" ] && grep -q "steamdeck" "/etc/os-release"; then
		ONSTEAMDECK=1

		export STEAMDECKWASUPDATE=0
		export STEAMDECKDIDINSTALL=1
		export STEAMDECKSTEAMRUN=0  # Stores if we're running STL when Steam opens on Steam Deck

		writelog "INFO" "${FUNCNAME[0]} - Seems like we have a Steam Deck here - making some specific settings"

		STLBASE="/home/deck/$SHOSTL"
		export PREFIX="$STLBASE/prefix"
		SYSTEMSTLCFGDIR="$PREFIX"
		STLDEPS="$STLBASE/deps"

		mkProjDir "$PREFIX"
		mkProjDir "$STLDEPS"

		if [ -z "$SUSDA" ]; then
			setSteamPaths
		fi

		# Show icon and title for notifier
		if ! [ -f "$STLICON" ]; then
			SCRIPTDIR="$( realpath "$0" )"
			SCRIPTDIR="${SCRIPTDIR%/*}"
			STLICON="$SCRIPTDIR/misc/steamtinkerlaunch.svg"
		fi
		export NOTYARGS="-i $STLICON -a $PROGNAME"

		# Differentiate between Game Mode and Desktop Mode on Steam Deck
		if grep -q "generate-drm-mode" <<< "$(pgrep -a "$GAMESCOPE")"; then
			writelog "INFO" "${FUNCNAME[0]} - Detected '$GAMESCOPE' running 'forced' - assuming we're running in Game Mode"
			FIXGAMESCOPE=1
		else
			writelog "INFO" "${FUNCNAME[0]} - Did not detect a running '$GAMESCOPE' process - assuming we're running in Desktop Mode"

			SMALLDESK=1
		fi

		writelog "INFO" "${FUNCNAME[0]} - Set 'FIXGAMESCOPE' to '$FIXGAMESCOPE'"
		writelog "INFO" "${FUNCNAME[0]} - Set 'SMALLDESK' to '$SMALLDESK'"

		INTERNETCONNECTION=1

		# Check if we're running STL on a Steam first launch
		# Assume Steam if all of these are true:
		# - We already have an STL installation (assume install if script in prefix)
		# - The AppID is a placeholder ID (not running a game)
		# - The script is running from CompatibilityTools.d
		if [ -f "$PREFIX/steamtinkerlaunch" ] && [ "$AID" = "$PLACEHOLDERAID" ] && [[ ${0^^} == *"${CTD^^}"* ]]; then
			writelog "INFO" "${FUNCNAME[0]} - Looks like we're running through Steam on a Steam Deck, we don't want to do any updating here!"
			STEAMDECKSTEAMRUN=1
		elif [[ ${0^^} == *"${PREFIX^^}"* ]] && ! [ "$AID" = "$PLACEHOLDERAID" ]; then
			# Launching a game proper through Steam would mean the dir we're launching from (${0}) would be the compattool dir
			#
			# The check for the AID != PlaceholderAID means we can update if we're just running the script from the prefix (i.e., we double clicked it)
			# But if we have an actual game AppID and we're running from the prefix, we're probably being called from an extra dialog on the main menu, which means we don't want to update (and this stops notifier spam too)
			writelog "INFO" "${FUNCNAME[0]} - Looks like we have a game but we're running from the Steam Deck install Prefix, not doing any updating here!"
			STEAMDECKSTEAMRUN=1
		else
			notiShow "$NOTY_STEAMDECK_INSTALL" "X"
			echo "$NOTY_STEAMDECK_INSTALL on Steam Deck"

			if ! (ping -q -c1 archlinux.org &>/dev/null || ping -q -c1 google.com &>/dev/null); then
				INTERNETCONNECTION=0
				writelog "WARN" "${FUNCNAME[0]} - No Internet Connection detected, attempting to install SteamTinkerLaunch offline - This may not succeed!"
				writelog "WARN" "${FUNCNAME[0]} - Make sure you have either manually installed all dependencies, or added all manual dependency archives to '$STLDEPS'"
				notiShow "$NOTY_STEAMDECK_NOINTERNET" "X"
				echo "$NOTY_STEAMDECK_NOINTERNET"
			fi
		fi

		export STLSDPATH="${STLDEPS}/usr/bin"
		export PATH="$PATH:$STLSDPATH"

		if [ "$STEAMDECKSTEAMRUN" -eq 0 ]; then
			installFilesSteamDeck
			checkSteamDeckLastVers
			checkSteamDeckDependencies

			# Don't remove dependencies offline
			if [ "$INTERNETCONNECTION" -eq 1 ]; then
				find "$STLDEPS" -type f \( -name "*.zst" -o -name "*.gz" -o -name ".*" \) -exec rm {} \;
			fi

			# update/set compatibility tool to git stl:
			if [ "$INFLATPAK" -eq 0 ]; then
				notiShow "$NOTY_STEAMDECK_ADDCOMPAT" "X"
				CompatTool "add" "$PREFIX/$PROGCMD" >/dev/null
			fi

			GTKCSSFILE="$HOME/.config/gtk-3.0/gtk.css"

			if [ ! -f "$GTKCSSFILE" ] ; then
				writelog "SKIP" "${FUNCNAME[0]} - '$GTKCSSFILE' does not exist - skipping"
			else
				if grep -q "scrollbar" "$GTKCSSFILE"; then
					writelog "SKIP" "${FUNCNAME[0]} - found a scrollbar entry in '$GTKCSSFILE'"
				else
					writelog "INFO" "${FUNCNAME[0]} - backup '$GTKCSSFILE' to '${GTKCSSFILE}_ORIGNAL'"
					cp "$GTKCSSFILE" "${GTKCSSFILE}_ORIGNAL"

					# NOTE: This styles most, but not all, UI elements on Steam Deck
					# It makes the scrollbar wider and easier to grab, and it adds a right margin so UI elements aren't covered by the scollbar
					# However currently the UI is not as uniform on Steam Deck, because file choosers and text fields don't have this margin
					# PRs are welcome to apply styling to these elements :-)
					writelog "INFO" "${FUNCNAME[0]} - adding bigger scrollbar and customising some other UI elements using '$GTKCSSFILE'"
					{
						echo ".scrollbar.vertical slider,"
						echo "scrollbar.vertical slider {"
						echo "min-width: 15px;"
						echo "}"
						echo "spinbutton, combobox button {"
						echo "margin-right: 20px;"
						echo "}"
					} >> "$GTKCSSFILE"
				fi
			fi
		else
			writelog "INFO" "${FUNCNAME[0]} - Seems like we're being run by Steam here, not doing any installation steps"
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - Not on Steam Deck I guess"
	fi
}

function restoreGtkCss {
	if [ "$ONSTEAMDECK" -eq 1 ]; then
		GTKCSSFILE="$HOME/.config/gtk-3.0/gtk.css"
		if [ -f "${GTKCSSFILE}_ORIGNAL" ] ; then
			writelog "INFO" "${FUNCNAME[0]} - recovering original gtk.css from '${GTKCSSFILE}_ORIGNAL'"
			mv "${GTKCSSFILE}_ORIGNAL" "$GTKCSSFILE"
		fi
	fi
}

function checkSteamDeckLastVers {
	# This function just makes sure that the 'lastvers' file exists at the defined path
	# If it does not we set it and clear the deps to ensure it starts downloading the libs at the right version
	if ! [ -f "$STLSTEAMDECKLASTVERS" ]; then
		clearDeckDeps
		echo "$PROGVERS" > "$STLSTEAMDECKLASTVERS"
	fi
}

function updateSteamDeckLastVers {
	# This function updates the 'lastvers' file after a dependency update if there was a version change
	if checkSteamDeckSTLUpdated; then
		echo "$PROGVERS" > "$STLSTEAMDECKLASTVERS"
	fi
}

function prepareSteamDeckCompatInfo {
	if [ "$AID" -eq "$PLACEHOLDERAID" ]; then
		writelog "SKIP" "${FUNCNAME[0]} - AppID '$AID' is placeholder AppID ('$PLACEHOLDERAID') -- Not fetching Steam Deck compatibility info"
		return
	fi

	if [ ! -x "$(command -v "$JQ")" ]; then
		writelog "WARN" "${FUNCNAME[0]} - Can't get Steam Deck compatibility information because '$JQ' is not installed"
		return 1
	fi

	if [ "$DLSTEAMDECKCOMPATINFO" -eq 1 ]; then
		mapfile -d ";" -t -O "${#DECKCOMPATARR[@]}" DECKCOMPATARR <<< "$( getSteamDeckCompatInfo "$AID" )"
		unset "DECKCOMPATARR[-1]"

		if [ "${#DECKCOMPATARR[@]}" -eq "0" ]; then
			writelog "INFO" "${FUNCNAME[0]} - No compatibility information available for '$AID' - Is this AppID definitely correct?"
		else
			# HTML symbols for Verified (checkmark), Playable (circled question mark) and Unsupported (no-entry sign)
			COMPATMARK=""
			case ${DECKCOMPATARR[0]} in
				*"Verified"*) COMPATMARK="&#10003;" ;;
				*"Playable"*) COMPATMARK="&#128712;" ;;
				*"Unsupported"*) COMPATMARK="&#128683;" ;;
			esac
			STEAMDECKCOMPATRATING="${DECKCOMPATARR[0]} ${COMPATMARK}"
		fi
	else
		writelog "INFO" "${FUNCNAME[0]} - DLSTEAMDECKCOMPATINFO is '$DLSTEAMDECKCOMPATINFO' - Not fetching Steam Deck compatability info"
	fi
}

# Fetches compatibility information about a game directly from the Steam store endpoint
# TODO maybe store `display_type` to differentiate what each string refers to, e.g.:
#    - 4 means Verified
#    - 3 means Playable
#    - ???
#    - 1 is the grey subtext for small notes about controllers and internet access
function getSteamDeckCompatInfo {
	if [ ! -x "$(command -v "$JQ")" ]; then
		writelog "WARN" "${FUNCNAME[0]} - Can't get Steam Deck compatibility information because '$JQ' is not installed"
		return 1
	fi

	if [ -z "$1" ]; then
		echo "No AppID given, you need to pass the AppID of the game you want to check the compatibility of"
		writelog "ERROR" "${FUNCNAME[0]} - Need a valid AppID to check the Steam Deck compatibility information"
		return 1
	fi

    AID="$1"
	mkProjDir "$STLGDECKCOMPAT"

	# Unofficial documentation for this endpoint is available here: https://github.com/Revadike/InternalSteamWebAPI/wiki/Get-Deck-Compatibility-Report
    DECKCOMPATENDPOINT="https://store.steampowered.com/saleaction/ajaxgetdeckappcompatibilityreport?nAppID="
    COMPATFILE="$STLGDECKCOMPAT/${AID}-deckcompatrating.json"

	# Check if we can access Steam before fetching the Deck compatibility info
    COMPATINFO=""
	if ! ping -q -c1 store.steampowered.com &>/dev/null; then
		writelog "INFO" "${FUNCNAME[0]} - Looks like we can't access Steam, not removing any Steam Deck compatibility files"

		# No existing file and offline, so skip
		if ! [ -f "$COMPATFILE" ]; then
			writelog "WARN" "${FUNCNAME[0]} - Looks like we can't contact Steam and there is no known Steam Deck compatibility information file at '$COMPATFILE' - Skipping this step since we won't be able to retrieve any useful data"
			return 1
		else
			writelog "INFO" "${FUNCNAME[0]} - Existing file found at '$COMPATFILE', Steam Deck compatibility information will be taken from this since we can't contact Steam - The information here could be outdated depending on when it was fetched!"
		fi
	else
		# Remove existing file
		if [ -f "$COMPATFILE" ]; then
			if ! "$JQ" -e '.success' "$COMPATFILE" 1>/dev/null; then
				writelog "INFO" "${FUNCNAME[0]} - File '$COMPATFILE' containing Steam Deck compatibility information already exists, however it looks like it failed last time - Removing it so we can attempt to redownload it"
				rm "$COMPATFILE"
			elif [ "$(( $(date +"%s") - $(stat -c "%Y" "$COMPATFILE") ))" -gt "86400" ]; then  # Todo maybe let the user define this in the Global Menu (default right now is >  1 day old)
				writelog "INFO" "${FUNCNAME[0]} - File '$COMPATFILE' is older than 1 day - Removing it so we can update in case the compatibility rating has changed"
				rm "$COMPATFILE"
			fi
		fi

		# If the file doesn't exist at all and we can connect to Steam, create it from the response
		if ! [ -f "$COMPATFILE" ]; then
			writelog "INFO" "${FUNCNAME[0]} - Fetching Steam Deck compatibility information for '$AID' from Steam store endpoint with '${DECKCOMPATENDPOINT}${AID}'"
			if ! "$WGET" -q "${DECKCOMPATENDPOINT}${AID}" -O "$COMPATFILE" 2> >(grep -v "SSL_INIT"); then
				# Failed to fetch for some reason, skip getting Deck compat info
				writelog "INFO" "${FUNCNAME[0]} - Failed to fetch Steam Deck compatibility information from Steam store endpoint with '${DECKCOMPATENDPOINT}${AID}'"
				return 1
			fi
		else
			# File exists, don't redownload
			writelog "INFO" "${FUNCNAME[0]} - Steam Deck compatibility rating file '$COMPATFILE' already exists - not redownloading"
		fi
	fi

	# We were able to hit the endpoint successfully, but check if the response success is valid!
	RESULTSLENGTH="$( "$JQ" -e '.results | length' "$COMPATFILE" )"
    if (( RESULTSLENGTH > 0 )); then
        writelog "INFO" "${FUNCNAME[0]} - Successfully retrieved Steam Deck compatility information - Stored to '$COMPATFILE'"

        # Get Verified/Playable/Unsupported/Unknown
        DECKCOMPATRATING="$( "$JQ" -e '.results.resolved_category' "$COMPATFILE" )"
        case $DECKCOMPATRATING in
            1) COMPATTEXT="$STEAMDECKCOMPAT_UNSUPPORTED" ;;
            2) COMPATTEXT="$STEAMDECKCOMPAT_PLAYABLE" ;;
            3) COMPATTEXT="$STEAMDECKCOMPAT_VERIFIED" ;;
            *) COMPATTEXT="$STEAMDECKCOMPAT_UNKNOWN" ;;
        esac
        COMPATINFO+="${COMPATTEXT};"

		writelog "INFO" "${FUNCNAME[0]} - Successfully retrieved Steam Deck compatibility rating of '$COMPATTEXT' for '$AID'"
		writelog "INFO" "${FUNCNAME[0]} - Converting language tokens for Steam Deck compatibility criteria into SteamTinkerLaunch translated strings"

		# Turn language tokens like '#SteamDeckVerified_TestResult_blah' into an actual translated string from the translation files -- Language strings credited to: https://github.com/SteamDatabase/SteamTracking
        for LANGTOKEN in $( "$JQ" -r '.results.resolved_items[].loc_token' "$COMPATFILE" ); do
            LANGTOKEN="$( echo "$LANGTOKEN" | xargs )"
            if [ -n "$LANGTOKEN" ]; then
				OGLANGTOKEN="$LANGTOKEN"
				# Convert token to uppercase and remove hash at beginning
                LANGTOKEN="${LANGTOKEN^^}"
                LANGTOKEN="${LANGTOKEN//#/}"

				# Get associated translation variable from translation files to match this translated string
				STLLANGTOKEN="${!LANGTOKEN}"
				if [ -n "$STLLANGTOKEN" ]; then
					COMPATINFO+="\"$STLLANGTOKEN\";"
				else
					writelog "WARN" "${FUNCNAME[0]} - Could not find translation string for returned language token '$OGLANGTOKEN' - Seems like we need to update the translation files to include a new string!"
				fi
            else
                writelog "INFO" "${FUNCNAME[0]} - Skipping blank LANGTOKEN '$LANGTOKEN' - though this should probably not happen!"
            fi
        done

		DEVELOPERCOMMENTSURL="$( "$JQ" -e '.results.steam_deck_blog_url' "$COMPATFILE" )"
		if [ "${#DEVELOPERCOMMENTSURL}" -gt 2 ]; then  # DEVELOPERCOMMENTSURL will always have quotes around it (e.g. `""` for blank), so the minimum length will always be 2 since it will be 2 for a blank entry because of the set of quotes
			writelog "INFO" "${FUNCNAME[0]} - Seems like the developer has a blog post with some comments on this game's compatibility, appending it... (length is ${#DEVELOPERCOMMENTSURL}"
			COMPATINFO+="$( strFix "$STEAMDECKVERIFIED_CUSTOMRESULT_DEVPOST" "$DEVELOPERCOMMENTSURL" );"
		else
			writelog "INFO" "${FUNCNAME[0]} - Seems like no developer comments were left for this game - This is fine as most games don't have this, so skipping"
		fi
		writelog "INFO" "${FUNCNAME[0]} - Finished setting Steam Deck compatibility criteria strings"
    else
		# We were able to get a response from the endpoint, but the results body was blank so we can't get any response information - Not sure when this would happen outside of Non-Steam Games
		writelog "WARN" "${FUNCNAME[0]} - No compatibility information available for '$AID' - Maybe the AppID is invalid?"
		rm "$COMPATFILE"
		return 1
    fi

	echo "$COMPATINFO"
}

# This function will check if the version in `$STLSTEAMDECKLASTVERS` does not match the current version
# For example, if we had v14.0 installed before, 'lastvers' would have v14.0
# Then, if we were updating to v15.0, 'lastvers' would be 'v14.0' but 'PROGVERS' would be 'v15.0'
function checkSteamDeckSTLUpdated {
	# This is how updateCfgFile gets the config file version, so re-use it
	CHECKLASTVERSSTEAMDECK="$( cat "$STLSTEAMDECKLASTVERS" )"
	if [ "$CHECKLASTVERSSTEAMDECK" = "$PROGVERS" ]; then
		writelog "INFO" "${FUNCNAME[0]} - Last known SteamTinkerLaunch install version ('$CHECKLASTVERSSTEAMDECK') and the current version ('$PROGVERS') match -- There has been no update since last launch"
		return 1
	else
		writelog "INFO" "${FUNCNAME[0]} - Last known SteamTinkerLaunch install version ('$CHECKLASTVERSSTEAMDECK') and the current version ('$PROGVERS') do NOT match -- It seems there has been an update!"
		return 0
	fi
}

# Clear dependencies in /home/deck/stl/deps so that they can be redownloaded the next time SteamTinkerLaunch is ran on Steam Deck
# This also serves as a quickfix for #719, as dependencies an be quickly removed and then re-downloaded to ensure better compatibility with SteamOS updates
# In future, STL should auto-bump these somehow
function clearDeckDeps {
	if [ "$ONSTEAMDECK" -eq 1 ]; then
		# This should be set by steamdedeckt, the regex check at the end is just for extra peace of mind that we don't rm -rf an incorrect directory!
		if [ -n "$STLDEPS" ] && [ -d "$STLDEPS" ]  && [[ $STLDEPS == *"deps"* ]]; then
			writelog "INFO" "${FUNCNAME[0]} - Removing '$STLDEPS' directory"
			rm -rf "$STLDEPS"
			writelog "INFO" "${FUNCNAME[0]} - Successfully removed '$STLDEPS'"
			echo "Removed Steam Deck dependencies, they will be re-downloaded on next launch."
		else
			writelog "SKIP" "${FUNCNAME[0]} - Could not find STL Steam Deck dependencies directory, STLDEPS is '$STLDEPS' - Nothing to do."
			echo "Could not find STL Steam Deck dependencies directory, skipping"
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - Not on Steam Deck, nothing to do"
		echo "Not on Steam Deck, nothing to do."
	fi
}

### STEAM DECK END

function setflatpak {
	if [ -n "$FLATPAK_ID" ] && [ "$FLATPAK_ID" == "com.valvesoftware.Steam" ]; then
		writelog "INFO" "${FUNCNAME[0]} - seems like flatpak is used, because variable 'FLATPAK_ID' exists and points to 'com.valvesoftware.Steam'"
		INFLATPAK=1
	else
		writelog "INFO" "${FUNCNAME[0]} - started $PROGNAME from ${0}"
	fi
}

# GDK_BACKEND can be either x11 or wayland -- User may want, in some instances, to force X11 over defaulting to Wayland for compatibility
# Option to force Yad to use XWayland is on Global Menu
function setGDKBackend {
	if [ "$XDG_SESSION_TYPE" == "wayland" ] || [ -z "$XDG_SESSION_TYPE" ]; then
		if [ "$YADFORCEXWAYLAND" -eq 1 ]; then
			writelog "INFO" "${FUNCNAME[0]} - XDG_SESSION_TYPE is either Wayland or undefined ('$XDG_SESSION_TYPE'), and the user chose to force XWayland, so setting GDK_BACKEND=x11"
			export GDK_BACKEND=x11  # May affect other programs launched with STL but we'll see, we may need a way to store the OG value and default it back somehow if this causes problems i.e. when Wine gets Wayland support, or for native games that may use this
		fi
	else
		writelog "SKIP" "${FUNCNAME[0]} - XDG_SESSION_TYPE is defined and is not Wayland, it is '$XDG_SESSION_TYPE' - No need to set GDK_BACKEND=x11 as it will already default to X11"
	fi
}

# This has the side effect of being called everytime on a local install run, but since the scriptdir of a local install can change, It Has To Be This Way
function setLocalInstall {
	SCRIPTDIR="$( realpath "$0" )"
	SCRIPTDIR="${SCRIPTDIR%/*}"
	# If not on Steam Deck or Flatpak, and we don't have a system config directory, assume we have a non-root local install and set/update config folder structure to match
	if [ "$ONSTEAMDECK" -eq 0 ] && [ ! -d "$SYSTEMSTLCFGDIR" ] && [ "$INFLATPAK" -eq 0 ]; then  # Check if "$SYSTEMSTLCFGDIR" doesn't exist because the user could be running the script for testing but have a global install
		writelog "INFO" "${FUNCNAME[0]} - Looks like we have a non-root local install here - Updating paths..."
		# If no "$SYSTEMSTLCFGDIR" directory, assume it was looking in `/usr/something` for configs (where they would be on a root install) - but since that folder doesn't exist, set it to the scriptdir
		SYSTEMSTLCFGDIR="$SCRIPTDIR"
	else
		writelog "INFO" "${FUNCNAME[0]} - Looks like we don't have a local non-root install"
	fi

	# Only update if we have an existing config file, don't call `updateConfigEntry` on non-existent file
	# Running this block here means we can update existing configs if the user installs system-wide again later or switches between
	if [ -f "$STLDEFGLOBALCFG" ]; then
		touch "$FUPDATE"
		updateConfigEntry "GLOBALCOLLECTIONDIR" "$SYSTEMSTLCFGDIR/collections" "$STLDEFGLOBALCFG"
		updateConfigEntry "GLOBALMISCDIR" "$SYSTEMSTLCFGDIR/misc" "$STLDEFGLOBALCFG"
		updateConfigEntry "GLOBALSBSTWEAKS" "$SYSTEMSTLCFGDIR/sbstweaks" "$STLDEFGLOBALCFG"
		updateConfigEntry "GLOBALTWEAKS" "$SYSTEMSTLCFGDIR/tweaks" "$STLDEFGLOBALCFG"
		updateConfigEntry "GLOBALEVALDIR" "$SYSTEMSTLCFGDIR/eval" "$STLDEFGLOBALCFG"
		updateConfigEntry "GLOBALSTLLANGDIR" "$SYSTEMSTLCFGDIR/lang" "$STLDEFGLOBALCFG"
		updateConfigEntry "GLOBALSTLGUIDIR" "$SYSTEMSTLCFGDIR/guicfgs" "$STLDEFGLOBALCFG"
	fi
}

function removeEmptyFiles {
	REFCHECKDIR="$1"
	writelog "INFO" "${FUNCNAME[0]} - Removing empty files from '$REFCHECKDIR'"
	for STLFILE in "$REFCHECKDIR/"*; do
		if ! [ -s "$STLFILE" ]; then
			rm "$STLFILE" &>/dev/null
		fi
	done
}

# Determine if we have a Non-Steam Game based on some observed behaviour:
# - All Steam applications pass a SteamAppId and SteamOverlayGameId environment variable
# - These two values are equal for Steam games/apps (Native+Proton)
#   - There is also SteamGameId, which is the same for Steam games/apps (SteamAppId == SteamGameId == SteamOverlayGameId)
# - For Non-Steam Games, SteamAppId and SteamGameId are equal, but SteamOverlayGameId is different and a very long number compared to regular Non-Steam AppIds
#   - This option is still passed even if the Steam Overlay is disabled
# - Therefore if SteamAppId and SteamOverlayGameId don't match, we can assume we have a Non-Steam Game
#   - There is a chance these values could match if a user somehow manages to accidentally (or purposefully) set the values to be the same, but in 99% of cases this should be sufficient
# - When launching games with SteamTinkerLaunch from the commandline, SteamOverlayGameId probably won't be set
function haveNonSteamGame {
	# shellcheck disable=SC2154		# SteamOverlayGameId comes from Steam
	if [ -z "$SteamOverlayGameId" ]; then
		return 1  # No SteamOverlayGameId, prevents false-positive when running from commandline
	elif [ "$SteamAppId" != "$SteamOverlayGameId" ]; then
		return 0
	else
		return 1
	fi
}

##################

function main {

	initShmStl
	restoreGtkCss
	rm "$TEMPLOG" "$WINRESLOG" "$PRELOG" "$APPMALOG" "$GGDLOG" 2>/dev/null
	touch "$PRELOG"
	mkProjDir "$LOGDIRID"
	setflatpak

	USS="${PREFIX}/share/steam"
	if [ "$INFLATPAK" -eq 1 ]; then
		USS="/app/share/steam"
	fi
	SYSSTEAMCOMPATOOLS="$USS/$CTD"

	SCRIPTDIR="$( realpath "$0" )"
	SCRIPTDIR="${SCRIPTDIR%/*}"

	initAID "$@"
	setAIDCfgs

	writelog "INFO" "${FUNCNAME[0]} - Current SteamTinkerLaunch working directory is '$( pwd )'"

	# Respect language choice asap in running (uses correct language during Steam Deck install)
	# Try and load either from langfile directory or `lang=` argument (prioritising lang arguments) - Default if we don't get a valid `$STLLANG`
	loadLanguage "$@"
	if [ -z "$STLLANG" ]; then
		loadLangFile "$STLDEFLANG"
	fi

	# Check quiet mode to hide notifier
	STLQUIET=0
	if echo "$@" | grep -qow '\-q'; then
		writelog "INFO" "${FUNCNAME[0]} - Quiet mode enabled with '-q', suppressing notifier for this execution"
		export STLQUIET=1
		USENOTIFIER=0
	fi

	steamdedeckt
	setLocalInstall
	getCurrentCommandline "$@"  # Maybe pass args with removed '-q' flag in the above if block
	saveOrgVars
	emptyVars "O"

	if haveNonSteamGame && [ -n "$SteamAppId" ]; then
		writelog "WARN" "${FUNCNAME[0]} - Looks like we have a Non-Steam Game but we have SteamAppId defined -- This has been observed to cause crashes, please remove it from your game folder!"
	elif haveNonSteamGame ; then
		writelog "INFO" "${FUNCNAME[0]} - Looks like we have a Non-Steam Game here, no extra steps but if this is NOT a Non-Steam Game, please report this incorrect detection as a bug"
	fi

    # Notify success on Steam Deck
	if [ "$ONSTEAMDECK" -eq 1 ] && [ "$STEAMDECKSTEAMRUN" -eq 0 ]; then
		printf '\n'
		if [ "$STEAMDECKDIDINSTALL" -eq 1 ]; then
			INSTALLEDPROGVERS="$( grep -i "^PROGVERS=.*." "$PREFIX/steamtinkerlaunch" | cut -d '"' -f 2 )"
			if [ "$STEAMDECKWASUPDATE" -eq 1 ]; then
				strFix "$NOTY_STEAMDECK_UPDATE_SUCCESS!" "$INSTALLEDPROGVERS"  # Update success w/ version
				notiShow "$(strFix "$NOTY_STEAMDECK_UPDATE_SUCCESS" "$INSTALLEDPROGVERS")" "X"
			else
				strFix "$NOTY_STEAMDECK_INSTALL_SUCCESS!" "$INSTALLEDPROGVERS"  # Install success w/ version
				notiShow "$(strFix "$NOTY_STEAMDECK_INSTALL_SUCCESS" "$INSTALLEDPROGVERS")" "X"
			fi
		else
			# Show install finished if no STL install files were modified (currently only for offline installs where existing install == install files)
			echo "$NOTY_STEAMDECK_INSTALL_FINISH!"
			notiShow "$NOTY_STEAMDECK_INSTALL_FINISH" "X"
		fi
	fi

	writelog "START" "######### Initializing Game Launch $AID using $PROGNAME $PROGVERS #########" "P"

	if [ -f "$STLDEFGLOBALCFG" ] && grep -q "^RESETLOG=\"1\"" "$STLDEFGLOBALCFG"; then
		if [ -f "$PRELOG" ]; then
			mv "$PRELOG" "$LOGFILE"
		else
			rmOldLog
			rm "$PRELOG" 2>/dev/null
		fi
		writelog "INFO" "${FUNCNAME[0]} - Starting with a clean log"  # from here the '$LOGFILE' is written directly
	fi

	writelog "INFO" "${FUNCNAME[0]} - Start creating default configs"
	createDefaultCfgs "$@"

	listSteamLibraries
	setSteamLibraryPaths

	writelog "INFO" "${FUNCNAME[0]} - Checking internal dependencies:"
	checkIntDeps "$@"

	writelog "INFO" "${FUNCNAME[0]} - Initializing first Proton:"
	initFirstProton

	writelog "INFO" "${FUNCNAME[0]} - Initializing default window resolution"
	setInitWinXY

	GREETING="$( getSeasonalGreeting )"
	writelog "INFO" "${FUNCNAME[0]} - ${GREETING:-Welcome to SteamTinkerLaunch}"

	removeEmptyFiles "$STLAPPINFOIDDIR"  # Remove appinfo files that are 0 bytes (i.e. Non-Steam Games)
	removeEmptyFiles "$STLGHEADD"  # Remove appinfo files that are 0 bytes (i.e. Non-Steam Games)

	setGDKBackend

	if [ -z "$1" ]; then
		writelog "INFO" "${FUNCNAME[0]} - No arguments provided. See '$PROGCMD --help' for possible command line parameters" "E"
	else
		writelog "INFO" "${FUNCNAME[0]} - Checking command line: incoming arguments '${*}'"
		writelog "INFO" "${FUNCNAME[0]} - Checking command line: first argument '${1}'"

		if [ -n "$SteamAppId" ] && [ "$SteamAppId" -eq "0" ]; then
			if grep -q "\"$1\"" <<< "$(sed -n "/^#STARTCMDLINE/,/^#ENDCMDLINE/p;/^#ENDCMDLINE/q" "$0" | grep if)"; then
				writelog "INFO" "${FUNCNAME[0]} - Seems like a '$PROGCMD'-internal command was started - checking the command line"
				commandline "$@"
			else
				setCustomGameVars "$@"
				if [ -n "$ISGAME" ]; then
					if [ "$ISGAME" -eq 2 ] || [ "$ISGAME" -eq 3 ]; then
						prepareLaunch
					fi
				else
					writelog "ERROR" "${FUNCNAME[0]} - Unknown command '$*'" "E"
				fi
			fi
		elif grep -q "$SAC" <<< "$@" || grep -q "$L2EA" <<< "$@"; then
			# We check if incoming commands contain 'steamapps/common' to interpret them as game launch commands
			# But if $1 is a known command in this list, explicitly pass it to 'commandline' as we know it is NOT a game command
			# This prevents commands which contain 'steamapps/common' ANYWHERE in their command (including as paths as parameters to other flags) from being interpreted as a game launch
			#
			# If $1 is a known steamtinkerlaunch command (with 'steamtinkerlaunch otr', $1 would be 'otr') then intervene and force this to the 'commandline' function as it is a known steamtinkerlaunch command
			STLINCOMINGSKIPCOMMANDS="otr|onetimerun"

			if grep -q "update" <<< "$@" || grep -q "^play" <<< "$@" ; then
				commandline "$@"
			# HACK: Since we check for steamapps/common ($SAC), commands which contain this (such as a one-time run path) will incorrectly get triggered as a game launch
			#       As a workaround, skip interpreting a hardcoded set of commands as start parameters and pass directly to commandline (i.e. if we have 'otr' as our first option, pass down to commandline function and run otr)
			#
			#       We also check to make sure the first argument doesn't contain any slashes (i.e. game start commands' first argument could be a path, so it would contain a slash)
			#       This allows us to distinguish between '/home/otr' which could be a game launch command, and the 'steamtinkerlaunch otr' command (where $1 is 'otr')
			elif grep -qwE "${STLINCOMINGSKIPCOMMANDS}" <<< "${1}" && [[ "${1}" != *"/"* ]]; then
				commandline "$@"
			else
				# If we get here, this should be an actual game start command!
				setGameVars "$@"
				if [ "$ISGAME" -eq 2 ] || [ "$ISGAME" -eq 3 ]; then
					prepareLaunch
				else
					writelog "INFO" "${FUNCNAME[0]} - Unknown parameter '${ORGGCMD[*]}'" "E"
				fi
			fi
		else
			commandline "$@"
		fi
	fi

	restoreGtkCss
}

if [ "$EUID" = 0 ]; then
	echo "'$PROGCMD' is not meant to be run as root - Exiting"
else
	main "$@"
fi
