audiogame-manager/audiogame-manager.sh

1285 lines
48 KiB
Bash
Executable File

#!/usr/bin/env bash
license() {
cat << EOF
■The contents of this file are subject to the Common Public Attribution
License Version 1.0 (the ■License■); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
https://opensource.org/licenses/CPAL-1.0. The License is based on the Mozilla Public License Version
1.1 but Sections 14 and 15 have been added to cover use of software over a
computer network and provide for limited attribution for the Original
Developer. In addition, Exhibit A has been modified to be consistent with
Exhibit B.
Software distributed under the License is distributed on an ■AS IS■ basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is audiogame manager.
The Original Developer is not the Initial Developer and is . If
left blank, the Original Developer is the Initial Developer.
The Initial Developer of the Original Code is Billy "Storm Dragon" Wolfe. All portions of
the code written by Billy Wolfe are Copyright (c) 2020, 2024. All Rights
Reserved.
Contributor Michael Taboada.
Attribution Copyright Notice: Audiogame manager copyright 2020 Storm Dragon. All rights reserved.
Attribution Phrase (not exceeding 10 words): A Stormux project
Attribution URL: https://stormgames.wolfe.casa
Graphic Image as provided in the Covered Code, if any.
Display of Attribution Information is required in Larger
Works which are defined in the CPAL as a work which combines Covered Code
or portions thereof with code not governed by the terms of the CPAL.
EOF
}
# Dialog accessibility
export DIALOGOPTS='--no-lines --visit-items'
# Alerts, for when user needs to read something.
alert() {
play -qnV0 synth 3 pluck D3 pluck A3 pluck D4 pluck F4 pluck A4 delay 0 .1 .2 .3 .4 remix - chorus 0.9 0.9 38 0.75 0.3 0.5 -t
echo
read -rp "Press enter to continue." continue
}
# Check for latest news
check_news() {
# For use by update scripts that want to source functions in this file.
[[ "$agmNoLaunch" == "true" ]] && return
trap return INT
# url for news file
local newsFile="https://stormgames.wolfe.casa/media/agm.ogg"
local newsPath="${configFile%/*.conf}/.news"
local newsTag="$(curl --connect-timeout 5 -sI "$newsFile" | grep -i '^etag: "' | cut -d '"' -f2)"
if [[ -z "${newsTag}" ]]; then
return
fi
local newsOldTag="$(cat "$newsPath" 2> /dev/null)"
if [[ "$newsTag" != "$newsOldTag" ]]; then
dialog --yes-label 'Play' \
--no-label 'Later' \
--backtitle 'Audiogame Manager News' \
--yesno 'Audiogame manager news is available. Please use left and right arrows to navigate and enter to confirm.' -1 -1 || return
sox -qV0 "$newsFile" -d &> /dev/null
echo -n "$newsTag" > "$newsPath"
fi
}
# Automatic update function
# Automatic update function
update() {
if ! [[ -d ".git" ]]; then
return
fi
local url="$(git ls-remote --get-url)"
if [[ "$url" =~ ^ssh://|git@|gitea@ ]] || [[ -z "$url" ]]; then
return
fi
git remote update &> /dev/null
local upstream='@{u}'
local home="$(git rev-parse @)"
local remote="$(git rev-parse "$upstream")"
if [[ "$home" == "$remote" ]]; then
return
fi
dialog --backtitle "Audiogame Manager" \
--yesno "Updates are available. Would you like to update now?" -1 -1 --stdout || return
{ git pull
git log '@{1}..' --pretty=format:'%an: %s'; }
exit $?
}
# Function to open urls across OS.
open_url() {
if [[ "$(uname)" == "Darwin" ]]; then
open "${*}" 2> /dev/null
else
xdg-open "${*}" 2> /dev/null
fi
}
# Create desktop launcher file
desktop_launcher() {
local desktopFile="${HOME}/audiogame-manager.desktop"
if [[ -e "${desktopFile}" ]]; then
echo "the file ${desktopFile} exists. Cannot create the launcher."
exit 1
fi
local dotDesktop
local terminal
# Try to find an accessible terminal
for i in mate-terminal lxterminal terminator gnome-terminal ; do
if command -v $i &> /dev/null ; then
terminal="$i"
break
fi
done
dotDesktop=('[Desktop Entry]'
'Name=Audiogame manager'
'GenericName=Audiogame Manager'
'Comment=Play audio games'
"Exec=${terminal} -t \"Audiogame Manager\" -e \"/usr/bin/bash -c 'nohup $(readlink -e "$0") 2> /dev/null'\""
'Terminal=false'
'Type=Application'
'StartupNotify=false'
'Keywords=game;'
'Categories=Game;'
'Version=1.0')
for i in "${dotDesktop[@]}" ; do
echo "$i" >> "${desktopFile}"
done
desktop-file-install --dir "${HOME}/.local/share/applications" -m 755 "${desktopFile}"
xdg-desktop-icon install ~/.local/share/applications/audiogame-manager.desktop
rm "${desktopFile}"
exit 0
}
# Wine configuration section
checklist() {
declare -a errorList
declare -a packageList
if [[ $# -eq 0 ]]; then
echo "Checking your system..."
echo
fi
if command -v wine &> /dev/null ; then
[[ $# -eq 0 ]] && echo "Wine is installed."
else
errorList+=("Critical: Wine is not installed. You will not be able to play any games.")
fi
packageList+=("wine")
if command -v curl &> /dev/null ; then
[[ $# -eq 0 ]] && echo "Curl is installed."
else
errorList+=("Critical: Curl is not installed. Critical functionality will not work.")
fi
packageList+=("curl")
if command -v dialog &> /dev/null ; then
[[ $# -eq 0 ]] && echo "Dialog is installed."
else
errorList+=("Critical: Dialog is not installed. You will not be able to install, launch, or remove any games.")
fi
packageList+=("dialog")
for i in 7z cabextract unzip xz ; do
if command -v $i &> /dev/null ; then
[[ $# -eq 0 ]] && echo "${i^} is installed."
else
errorList+=("Critical: ${i^} is not installed. You will not be able to install some games or their components.")
fi
packageList+=("$i")
done
if command -v gawk &> /dev/null ; then
[[ $# -eq 0 ]] && echo "Gawk is installed."
else
errorList+=("Warning: gawk is not installed. Game removal with -r will not work.")
fi
packageList+=("gawk")
if command -v ocrdesktop &> /dev/null ; then
[[ $# -eq 0 ]] && echo "Ocrdesktop is installed."
else
errorList+=("Warning: ocrdesktop is not installed. It can help if the installer gets stuck to figure out what is happening.")
fi
packageList+=("ocrdesktop")
if command -v qjoypad &> /dev/null ; then
[[ $# -eq 0 ]] && echo "Qjoypad is installed."
else
errorList+=("Warning: qjoypad is not installed. Qjoypad allows you to play keyboard only games with a gamepad.")
fi
packageList+=("qjoypad")
if command -v sox &> /dev/null ; then
[[ $# -eq 0 ]] && echo "Sox is installed."
else
errorList+=("Warning: Sox is not installed. Audio will not work.")
fi
packageList+=("sox")
if command -v trans &> /dev/null ; then
[[ $# -eq 0 ]] && echo "Translate-shell is installed."
else
errorList+=("Warning: translate-shell is not installed. Games that require translation will not be translated.")
fi
packageList+=("translate-shell")
if command -v sqlite3 &> /dev/null ; then
[[ $# -eq 0 ]] && echo "Sqlite3 is installed."
else
errorList+=("Warning: sqlite is not installed. Required for games that need to be translated.")
fi
if command -v perl &> /dev/null ; then
[[ $# -eq 0 ]] && echo "Perl is installed."
else
errorList+=("Warning: perl is not installed. Required for games that need to be translated.")
fi
packageList+=("perl")
packageList+=("sqlite")
if command -v w3m &> /dev/null ; then
[[ $# -eq 0 ]] && echo "W3m is installed."
else
errorList+=("Warning: w3m is not installed. W3m is used to view game documentation.")
fi
packageList+=("w3m")
if command -v xclip &> /dev/null ; then
[[ $# -eq 0 ]] && echo "Xclip is installed."
else
errorList+=("Warning: Xclip is not installed. Some games may not speak or register properly.")
fi
packageList+=("xclip")
if command -v xdotool &> /dev/null ; then
[[ $# -eq 0 ]] && echo "Xdotool is installed."
else
errorList+=("Warning: Xdotool is not installed. Some installers may not work or may need manual intervention.")
fi
packageList+=("xdotool")
# Show the results
if [[ $# -ne 0 ]]; then
for i in "${packageList[@]}" ; do
echo "$i"
done | sort
exit 0
fi
if [[ ${#errorList[@]} -eq 0 ]]; then
echo "No problems found, you are good to go."
exit 0
fi
echo "Errors detected, here is a list along with the severity."
echo "Note that errors marked critical mean that you will not be able to install and play games until they are resolved."
for i in "${errorList[@]}" ; do
echo "$i"
done
exit 0
}
clear_cache() {
local answer
if [[ ! -d "${cache}" ]]; then
echo "No cache found at ${cache}."
return
fi
while ! [[ "${answer,,}" =~ ^yes$|^no$ ]]; do
echo "This will delete all contents of ${cache}. Are you sure you want to continue?"
echo "Please type yes or no."
echo
read -r answer
done
if [[ "$answer" == "no" ]]; then
return
fi
# All safety checks done. Delete the cache.
rm -rfv "${cache}"
echo "Cache deleted."
}
download() {
local source=($@)
for i in "${source[@]}" ; do
local dest="${i##*/}"
dest="${dest//%20/ }"
dest="${dest#*\?filename=}"
dest="${dest%\?*}"
# Remove the destination file if it is empty.
[[ -s "${cache}/${dest}" ]] || rm -f "${cache}/${dest}" 2> /dev/null
if [[ "${redownload}" == "true" ]] && [[ -e "${cache}/${dest}" ]]; then
rm -v "${cache}/${dest}"
fi
# Skip if the item is in cache.
[[ -e "${cache}/${dest}" ]] && continue
{ if ! curl -L4 -C - --retry 10 --output "${cache}/${dest}" "${i}" ; then
echo "Could not download \"$i\"..."
exit 1
fi; } | dialog --backtitle "Audio Game Manager" \
--progressbox "Downloading \"$dest\" from \"$i\"" -1 -1
local downloadError=1
case "${dest##*.}" in
"pk3"|"zip")
unzip -tq "${cache}/${dest}" | dialog --backtitle "Audio Game Manager" \
--progressbox "Validating ${dest##*.} file" -1 -1 --stdout
downloadError=$?
;;
"7z")
7z t "${cache}/${dest}" | dialog --backtitle "Audio Game Manager" \
--progressbox "Validating 7z file" -1 -1 --stdout
downloadError=$?
;;
"exe")
# Check if it's a valid Windows executable by looking at the MZ header
if ! hexdump -n 2 -v -e '/1 "%02X"' "${cache}/${dest}" | grep -q "4D5A"; then
downloadError=0
fi
;;
"wad")
if [[ "$(file -b --mime-type "${cache}/${dest}")" != "application/octet-stream" ]]; then
downloadError=0
fi
;;
*)
# Add HTML check for other file types
if file -b "${cache}/${dest}" | grep -q "HTML document" ; then
echo "File not found: \"$i\" (HTML document probably 404)"
downloadError=0
fi
;;
esac
if [[ $downloadError -eq 0 ]]; then
rm -fv "${cache}/${dest}"
dialog --backtitle "Audio Game Manager" \
--infobox "Error downloading \"${dest}\". Installation cannot continue." -1 -1 --stdout
alert
exit 1
fi
done
}
get_bottle() {
# Handles games that use the same wine bottle
case "${game}" in
# Aprone (Jeremy Kaldobsky) games.
"castaways"*) ;&
"castaways-2"*) ;&
"daytona-and-the-book-of-gold"*) ;&
"dog-who-hates-toast"*) ;&
"lunimals"*) ;&
"paw-prints"*) ;&
"penta-path"*) ;&
"preludeamals"*) ;&
"puzzle-divided"*) ;&
"rettou"*) ;&
"revelation"*) ;&
"swamp"*) ;&
"tarot-assistant"*) ;&
"triple-triad"*)
export WINEPREFIX="${HOME}/.local/wine/aprone" ;;
"bg-"*) export WINEPREFIX="${HOME}/.local/wine/bg";;
# draconis games
"esp-pinball-classic"*) ;&
"esp-pinball-extreme"*) ;&
"esp-pinball-party-pack"*) ;&
"silver-dollar"*) ;&
"monkey-business"*) ;&
"alien-outback"*) ;&
"dyna-man"*) ;&
"change-reaction"*) ;&
"ten-pin-alley"*) export WINEPREFIX="${HOME}/.local/wine/draconis";;
# l-works games group
"duck-hunt"*) ;&
"judgement-day"*) ;&
"lockpick"*) ;&
"pigeon-panic"*) ;&
"super-egg-hunt"*) ;&
"super-liam"*) ;&
"the-great-toy-robbery"*) export WINEPREFIX="${HOME}/.local/wine/l-works";;
# Nyanchan games group
"bokurano-daibouken"*) ;&
"laser-breakout"*) ;&
"marina-break"*) ;&
"mp5"*) ;&
"screaming-strike-2"*) ;&
"world-of-war"*) export WINEPREFIX="${HOME}/.local/wine/nyanchan";;
# Oriol Gomez games group
"bombercats"*) ;&
"copter-mission"*) ;&
"danger-on-the-wheel"*) ;&
"death-on-the-road"*) ;&
"fuck-that-bird"*) ;&
"hammer-of-glory"*) ;&
"insect-therapy"*) ;&
"rhythm-rage"*) ;&
"run-for-your-life"*) ;&
"thief"*) ;&
"villains-from-beyond"*) export WINEPREFIX="${HOME}/.local/wine/oriol-gomez";;
# pbgames group
"dark-destroyer"*) ;&
"PBGames TMP") export WINEPREFIX="$HOME/.local/wine/pbgames" ;;
# tunmi13 games group
"battle-of-the-hunter"*) ;&
"clashes-of-the-sky"*) ;&
"challenge-of-the-horse"*) export WINEPREFIX="${HOME}/.local/wine/tunmi13";;
# tunmi13-64bit games group
"battlefield-2d"*) ;&
"haunted-party"*) ;&
"skateboarder-pro"*) export WINEPREFIX="${HOME}/.local/wine/tunmi13-64bit";;
# Dan Z games group
"lost"*) ;&
"maze-craze"*) ;&
"super-deekout"*)
export norh="true"
export WINEPREFIX="$HOME/.local/wine/dan-z"
;;
*) export WINEPREFIX="${HOME}/.local/wine/${game%|*}";;
esac
# Wine version for bottles
if [[ "$game" =~ entombed ]]; then
install_wine "6.18" "32"
fi
if [[ "$game" =~ rs-games ]]; then
install_wine "7.0" "32"
fi
if [[ "$game" =~ shadow-line ]]; then
install_wine "7.7" "32"
fi
}
get_installer() {
trap "exit 0" SIGINT
# If the file is in cache nothing else needs to be done.
if [[ -f "${cache}/$1" ]]; then
return
fi
# Create message for dialog.
local message="Make sure $1 is available in either your Downloads or Desktop directory and press enter to continue."
if [[ -n "$2" ]]; then
message+="\n\nThe last good known URL for $game is:"
message+="\n$2"
fi
if echo "$2" | xclip -selection clipboard 2> /dev/null ; then
message+="\n\nThe URL has been copied to the clipboard."
fi
dialog --ok-label "Continue" \
--backtitle "Audiogame Manager" \
--msgbox "$message" -1 -1
# Search the Desktop and Downloads directories for the installation file
for i in ~/Downloads ~/Desktop ; do
find $i -type f -name "$1" -exec cp -v {} "${cache}/" \;
done
# If the file is still not available abort.
if [[ ! -f "${cache}/$1" ]]; then
echo "couldn't find $1. Please download the file and try again."
exit 1
fi
}
get_steam() {
# Arguments: $1 id of item for download, $2 url for game
trap "exit 0" SIGINT
echo "manual intervention required."
alert
dialog --backtitle "Audiogame Manager" \
--yes-label "Continue with Steam" \
--no-label "Install manually" \
--extra-button \
--extra-label "Exit" \
--yesno "To install the game manually, place files in \"${WINEPREFIX}/drive_c/Program Files/${game}\"" -1 -1 --stdout
case $? in
0) echo "The next steps will install through steamcmd." ;;
1)
mkdir -p "${WINEPREFIX}/drive_c/Program Files/${game}"
dialog --backtitle "Audiogame Manager" \
--msgbox "Place game files in \"${WINEPREFIX}/drive_c/Program Files/${game}\" and press enter to continue." -1 -1 --stdout
return
;;
*) exit 0 ;;
esac
# Check for steamcmd
if ! command -v steamcmd &> /dev/null ; then
dialog --backtitle "Audiogame Manager" \
--infobox "This installer requires steamcmd. Please install steamcmd and try again." -1 -1
exit 1
fi
# Create message for dialog.
local message="Make sure ${game} is available in your Steam library and press enter to continue. The URL for ${game} is $2"
if echo "$2" | xclip -selection clipboard 2> /dev/null ; then
message+="\n\nThe URL has been copied to the clipboard."
fi
dialog --ok-label "Continue" \
--backtitle "Audiogame Manager" \
--msgbox "$message" -1 -1
# Get Steam user name.
steamUser="$(dialog --ok-label "Continue" \
--backtitle "Audiogame Manager" \
--inputbox "Please enter your Steam user name:" -1 -1 --stdout)"
# Download the game
mkdir -p "${WINEPREFIX}/drive_c/Program Files/${game}"
steamcmd +@sSteamCmdForcePlatformType windows +force_install_dir "${WINEPREFIX}/drive_c/Program Files/$game" +login "$steamUser" +app_update "$1" +quit || { dialog --backtitle "Audiogame Manager" \
--infobox "Something went wrong. Please make sure you have a stable internet connection, and if the problem persists, contact audiogame-manager's developers." -1 -1
exit 1; }
}
help() {
echo "${0##*/}"
echo "Released under the terms of the Common Public Attribution License Version 1.0"
echo -e "This is a Stormux project: https://stormux.org\n"
echo -e "Usage:\n"
echo "With no arguments, open the game launcher."
for i in "${!command[@]}" ; do
echo "-${i/:/ <parameter>}: ${command[${i}]}"
done | sort
echo
echo "Some settings that are often used can be stored in a settings.conf file."
echo "If wanted, place it at the following location:"
echo "${configFile%/*}/settings.conf"
echo "The syntax is variable=\"value\""
echo
echo "ipfsGateway=\"https://ipfs.stormux.org\" # Gateway to be used for ipfs downloads."
echo "noCache=\"true\" # Do not keep downloaded items in the cache."
echo "noqjoypad=\"true\" # Do not launch qjoypad."
echo "norh=\"true\" # Do not install RHVoice."
echo "redownload=\"true\" # Redownload sources, do not use the version stored in cache."
echo "voiceName=\"voicename\" # Select the voice to be installed (default Bdl)."
echo "defaultVoice=\"voicename\" # Select the default voice to use for the bottle, e.g. MSMike or RHVoice."
echo "defaultRate=\"Default voice rate for the bottle, default 7, may not work in all games. Values 1-9 or A."
echo "winedebug=\"flag(s)\" # Set wine debug flags, useful for development."
exit 0
}
documentation() {
if [[ "$2" == "Become a Patron" ]]; then
return
fi
if [[ "$2" == "Donate" ]]; then
return
fi
if ! command -v w3m &> /dev/null ; then
echo "This feature of audiogame-manager requires w3m. Please install it before continuing."
exit 1
fi
get_bottle "$1"
echo "Loading documentation, please wait..."
# Try to find documentation based on common naming conventions.
local gamePath="$(winepath -u "$2" 2> /dev/null)"
gamePath="${gamePath%/*}"
local gameDoc="$(find "$gamePath" -type f -iname 'user_manual.htm*' -or -iname 'user manual.htm*' -or -iname '*user guide.htm*' | head -1)"
# Game name specific docs, add the name to the for loop.
if [[ -z "$gameDoc" ]]; then
for i in "troopanum.txt" "superdeekout.txt" scw.html ; do
gameDoc="$(find "$gamePath" -type f -iname "$i" -or -iname 'manual.htm' | head -1)"
done
fi
if [[ -z "$gameDoc" ]]; then
gameDoc="$(find "$gamePath" -type f -path '*/Manual/index.html' | head -1)"
fi
if [[ -z "$gameDoc" ]]; then
gameDoc="$(find "$gamePath" -type f -iname '[A-Z]*Help.htm' -or -iname '[A-Z]*Help.html' | head -1)"
fi
if [[ -z "$gameDoc" ]]; then
gameDoc="$(find "$gamePath" -type f -iname 'manual.html' -or -iname 'manual.htm' | head -1)"
fi
if [[ -z "$gameDoc" ]]; then
gameDoc="$(find "$gamePath" -type f -iname 'en.html' -or -iname 'en.htm' | head -1)"
fi
if [[ -z "$gameDoc" ]]; then
gameDoc="$(find "$gamePath" -type f -iname 'readme.html' -or -iname 'readme.htm' | head -1)"
fi
if [[ -z "$gameDoc" ]]; then
gameDoc="$(find "$gamePath" -type f -iname 'manual.txt' | head -1)"
fi
if [[ -z "$gameDoc" ]]; then
gameDoc="$(find "$gamePath" -type f -iname 'readme.txt' -or -iname 'help.txt' | head -1)"
fi
if [[ -z "$gameDoc" ]]; then
gameDoc="$(find "$gamePath" -type f -iname '*.url' -exec grep -i 'url=' {} \; | grep -iv 'score' | head -1)"
gameDoc="${gameDoc#*=}"
gameDoc="${gameDoc//[[:cntrl:]]/}"
fi
# Display documentation if available.
if [[ -n "$gameDoc" ]]; then
w3m "$gameDoc"
else
echo "No documentation found."
fi
exit 0
}
install_wine() {
# Requires wine version, e.g. 7.7 and architecture, 32|64
if [[ $# -ne 2 ]]; then
exit 1
fi
# Figure out wineInstallationPath
wineInstallationPath="${XDG_DATA_HOME:-$HOME/.local/share}/audiogame-manager/wine_$2/$1"
export wine="${wineInstallationPath}/bin/wine"
# If the path exists, wine should already be installed.
# Just make sure we didn't wind up with a empty directory for some reason
rmdir "${wineInstallationPath}" 2> /dev/null
if [[ -d "${wineInstallationPath}" ]]; then
return
fi
mkdir -p "${wineInstallationPath}" 2> /dev/null
# This probably does not need to be cached, so download to tmp.
installationFile="$(mktemp)"
local v=$2
v="${v/32/x86}"
v="${v/64/x64}" # Probably wrong, so just a place holder.
# If this goes wrong, bail out
set -e
{ curl -L --output "${installationFile}" "https://www.playonlinux.com/wine/binaries/phoenicis/upstream-linux-x86/PlayOnLinux-wine-${1}-upstream-linux-${v}.tar.gz"
tar xf "${installationFile}" -C "${wineInstallationPath}"; } | dialog --progressbox "Installing $2 bit Wine version $1." -1 -1
set +e
}
unix2dos() {
if [[ $# -eq 0 ]]; then
echo "Usage: unix2dos file(s)."
exit 1
fi
for file in "${@}" ; do
sed -i 's/$/\r/' "${file}"
done
}
winetricks() {
# Report used packages to the winetricks maintainer so he knows they are being used.
if ! [[ -e "${XDG_CACHE_HOME:-$HOME/.cache}/winetricks/track_usage" ]]; then
mkdir -p "${XDG_CACHE_HOME:-$HOME/.cache}/winetricks/"
echo "1" > "${XDG_CACHE_HOME:-$HOME/.cache}/winetricks/track_usage"
fi
# Download or update agm's copy of winetricks
if [[ ! -e "${winetricksPath}/winetricks" ]]; then
checkWinetricksUpdate="true"
download "https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks"
mv "${cache}/winetricks" "${winetricksPath}"
chmod 755 "${winetricksPath}/winetricks"
else
if [[ "$checkWinetricksUpdate" != "true" ]]; then
checkWinetricksUpdate="true"
${winetricksPath}/winetricks --self-update
fi
fi
# Run the requested winetricks parameters
if command -v FEXLoader &> /dev/null ; then
WINE="" FEXLoader -- ${winetricksPath}/winetricks "$@"
else
${winetricksPath}/winetricks "$@"
fi
}
install_rhvoice() {
if [[ -d "$HOME/.local/wine/${bottle}/drive_c/Program Files/Olga Yakovleva/" ]]; then
return
fi
if [[ "$norh" == "true" ]]; then
# Try to prevent the user from breaking speech
# Also useful for games that do not work with RHVoice
if [[ "${defaultVoice}" == "RHVoice" ]]; then
unset defaultVoice
fi
return
fi
declare -A RHVoice=(
[alan]="https://rhvoice.eu-central-1.linodeobjects.com/RHVoice-voice-English-Alan-v4.0.2008.15-setup.exe"
[bdl]="https://rhvoice.eu-central-1.linodeobjects.com/RHVoice-voice-English-Bdl-v4.1.2008.15-setup.exe"
[clb]="https://rhvoice.eu-central-1.linodeobjects.com/RHVoice-voice-English-Clb-v4.0.2008.15-setup.exe"
[lyubov]="https://rhvoice.eu-central-1.linodeobjects.com/RHVoice-voice-English-Lyubov-v4.0.2008.15-setup.exe"
[slt]="https://rhvoice.eu-central-1.linodeobjects.com/RHVoice-voice-English-Slt-v4.0.2008.15-setup.exe"
)
voiceName="${voiceName:-bdl}"
voiceName="${voiceName,,}"
if [[ "${RHVoice[${voiceName}]}" == "" ]]; then
echo "Invalid RHVoice name specified, defaulting to Bdl."
voiceName="bdl"
fi
local voiceFile="${RHVoice[${voiceName}]##*/}"
download "${RHVoice[${voiceName}]}"
winetricks -q win8
echo "Installing RHVoice ${voiceName^}..."
${wine} "${cache}/${voiceFile}" &
sleep 20
${wine}server -k
}
install_wine_bottle() {
# 32 bit installations work best and are the default here, if you need to override it, do it in the game specific installation steps.
export WINEARCH="${WINEARCH:-win32}"
# Figure out if we are using a specific version of wine
export wine="${wine:-$(command -v wine)}"
# Set the WINE and WINESERVER environmental variables so winetricks will use the right installation.
export WINE="${wine}"
export WINESERVER="${wine}server"
if [[ -z "$bottle" ]]; then
local bottle="${game,,}"
bottle="${bottle//[[:space:]]/-}"
if [[ -d "$HOME/.local/wine/${bottle}" ]]; then
echo "$HOME/.local/wine/${bottle} exists. Please remove it before running this installer."
exit 1
fi
fi
export WINEPREFIX="$HOME/.local/wine/${bottle}"
# Arguments to the function are dependancies to be installed.
# Get location of mono and gecko.
monoPath="$(find /usr/share/wine/ -maxdepth 1 -type d -name mono 2> /dev/null)"
geckoPath="$(find /usr/share/wine/ -maxdepth 1 -type d "gecko" 2> /dev/null)"
if [[ -z "$monoPath" ]]; then
download 'http://dl.winehq.org/wine/wine-mono/6.0.0/wine-mono-6.0.0-x86.msi'
monoPath="${cache}/wine-mono-6.0.0-x86.msi"
fi
if [[ -z "$geckoPath" ]]; then
download 'http://dl.winehq.org/wine/wine-gecko/2.40/wine_gecko-2.40-x86.msi'
geckoPath="${cache}/wine_gecko-2.40-x86.msi"
fi
# This is in a brace list to pipe through dialog.
{ echo -n "Using "
${wine} --version
DISPLAY="" ${wine}boot -u
${wine} msiexec /i z:"$monoPath" /quiet
${wine} msiexec /i z:"$geckoPath" /quiet
if [[ "${*}" =~ (speechsdk|sapi) ]]; then
install_rhvoice
fi
if [[ "${WINEARCH}" == "win64" ]]; then
download "https://github.com/RastislavKish/nvda2speechd/releases/download/v0.1/nvda2speechd"
fi
if [[ "${WINEARCH}" == "win64" ]] && [[ ! -f "${XDG_DATA_HOME:-$HOME/.local/share}/audiogame-manager/nvda2speechd" ]]; then
cp "${cache}/nvda2speechd" "${XDG_DATA_HOME:-$HOME/.local/share}/audiogame-manager/nvda2speechd"
chmod +x "${XDG_DATA_HOME:-$HOME/.local/share}/audiogame-manager/nvda2speechd"
fi
winetricks -q isolate_home $@ ${winVer:-winxp} ${winetricksSettings}; } | dialog --progressbox "Installing wine bottle, please wait..." -1 -1
# make it easy for game scripts to know which version of wine to use.
echo "WINE=\"${WINE}\"" > "$HOME/.local/wine/${bottle}/agm.conf"
echo "WINESERVER=\"${WINESERVER}\"" >> "$HOME/.local/wine/${bottle}/agm.conf"
# If default voice is set, change it for the bottle
if [[ ${#defaultVoice} -ge 2 ]] && [[ "${*}" =~ (speechsdk|sapi) ]]; then
echo "Setting default voice for bottle \"${bottle}\" to \"${defaultVoice}\"."
"${0%/*}/speech/set-voice.sh" -b "${bottle}" -r "${defaultRate:-7}" -v "${defaultVoice}"
fi
}
# Install games
game_installer() {
export LANG="en_US.UTF-8"
# Get list of installed games from config file
mapfile -t installedGames < <(sed -e '/^$/d' -e '/^[[:space:]]*#/d' "${configFile}" 2> /dev/null | cut -d '|' -f3)
# Create the menu of available games by reading from .install directory
declare -a menuList
# Get all .sh files from .install directory, excluding those starting with # as first line.
mapfile -t sortedGames < <(for f in "${0%/*}/.install/"*.sh; do
# Skip if first line starts with #
[[ $(head -n1 "$f") == "#"* ]] && continue
echo "${f##*/%.sh}"
done | sort)
for i in "${sortedGames[@]}"; do
local menuItem="${i%.sh}"
menuItem="${menuItem##*/}"
# Check if game is already installed
for j in "${installedGames[@]}"; do
if [[ "$j" == "$menuItem" ]]; then
unset menuItem
break
fi
done
# Add to menu if not installed
if [[ -n "$menuItem" ]]; then
menuList+=("$menuItem" "$menuItem")
fi
done
# If all games are installed, exit
if [[ ${#menuList[@]} -eq 0 ]]; then
echo "All games are already installed."
exit 0
fi
menuList+=("Donate" "Donate")
menuList+=("Become a Patron" "Become a Patron")
# Show game selection dialog
game="$(dialog --backtitle "Audio Game Installer" \
--clear \
--ok-label "Install" \
--no-tags \
--menu "Please select a game to install" 0 0 0 "${menuList[@]}" --stdout)"
[[ $? -ne 0 ]] && exit 0
# Handle selection
if [[ -n "$game" ]]; then
case "$game" in
"Donate")
open_url "https://ko-fi.com/stormux"
exit 0
;;
"Become a Patron")
open_url "https://patreon.com/stormux"
exit 0
;;
*)
# Convert game name to filename format
local installScript="${0%/*}/.install/${game}.sh"
# Check if install script exists
if [[ -f "$installScript" ]]; then
# Source and execute the install script
source "$installScript"
else
dialog --backtitle "Audio Game Installer" \
--msgbox "Installation script not found for ${game}" -1 -1
exit 1
fi
;;
esac
fi
}
# remove games
game_removal() {
if [[ "$(uname -m)" == "aarch64" ]]; then
export wine="${wine:-/usr/bin/wine}"
else
export wine="${wine:-/usr/bin/wine}"
fi
mapfile -t lines < <(sed -e '/^$/d' -e '/^[[:space:]]*#/d' "${configFile}" 2> /dev/null)
if [[ ${#lines} -eq 0 ]]; then
echo "No games found."
exit 0
fi
# Create the menu of installed games
declare -a menuList
for i in "${lines[@]}" ; do
IFS='|' read -ra gameInfo <<< "$i"
menuList+=("${gameInfo[0]}|${gameInfo[1]}" "${gameInfo[2]}")
done
menuList+=("Donate" "Donate")
menuList+=("Become a Patron" "Become a Patron")
local game
game="$(dialog --backtitle "Audio Game Removal" \
--clear \
--ok-label "Remove" \
--no-tags \
--menu "Please select a game to delete" 0 0 0 "${menuList[@]}" --stdout)"
if [[ ${#game} -gt 0 ]]; then
if [[ "$game" == "Donate" ]]; then
open_url "https://ko-fi.com/stormux"
exit 0
fi
if [[ "$game" == "Become a Patron" ]]; then
open_url "https://2mb.games/product/2mb-patron/"
exit 0
fi
local winePath="${game#*|}"
export winePath="${winePath%\\*.exe}"
local wineExec="${game#*|}"
wineExec="${wineExec%|*}"
wineExec="${wineExec##*\\}"
# Confirm removal
get_bottle "${game%|*}"
# Make sure the game can be handled by remove
if [[ "${HOME}/.local/wine/${game%|*}" == "${WINEPREFIX}" ]]; then
read -rp "To remove the wine bottle \"${WINEPREFIX##*/}\" and all of its contents press enter. To cancel press control+c. " continue
# kill any previous existing wineservers for this prefix in case they didn't shut down properly.
${wine}server -k
# remove the game
rm -rf "${WINEPREFIX}"
else
read -rp "This bottle \"${WINEPREFIX##*/}\" contains multiple entries, so only the game will be removed. To continue press enter. To cancel press control+c. " continue
rm -rf "$(winepath "${winePath}")"
fi
# remove the launcher
gawk -i inplace -vLine="${game//\\/\\\\}" '!index($0,Line)' "$configFile"
echo "The selected item has been deleted."
fi
exit 0
}
# kill games that are stuck
kill_game() {
if [[ "$(uname -m)" == "aarch64" ]]; then
export wine="${wine:-/usr/bin/wine}"
else
export wine="${wine:-/usr/bin/wine}"
fi
mapfile -t lines < <(sed '/^$/d' "${configFile}" 2> /dev/null)
if [[ ${#lines} -eq 0 ]]; then
echo "No games found."
exit 0
fi
# Create the menu of installed games
declare -a menuList
for i in "${lines[@]}" ; do
IFS='|' read -ra gameInfo <<< "$i"
menuList+=("${gameInfo[0]}|${gameInfo[1]}" "${gameInfo[2]}")
done
menuList+=("Donate" "Donate")
menuList+=("Become a Patron" "Become a Patron")
local game="$(dialog --backtitle "Audio Game Killer" \
--clear \
--ok-label "Kill" \
--no-tags \
--menu "Please select a game to force stop" 0 0 0 "${menuList[@]}" --stdout)"
if [[ ${#game} -gt 0 ]]; then
if [[ "$game" == "Donate" ]]; then
open_url "https://ko-fi.com/stormux"
exit 0
fi
if [[ "$game" == "Become a Patron" ]]; then
open_url "https://2mb.games/product/2mb-patron/"
exit 0
fi
local winePath="${game#*|}"
winePath="${winePath%\\*.exe}"
local wineExec="${game#*|}"
wineExec="${wineExec%|*}"
wineExec="${wineExec##*\\}"
# kill the wine server.
get_bottle "${game%|*}"
${wine}server -k
echo "The selected game has been stopped."
fi
exit 0
}
# for games that require custom scripts before launch or custom launch parameters
custom_launch_parameters() {
if [[ "${game[0]}" == "dragon-pong" ]]; then
"${0%/*}/speech/speak_window_title.sh" DragonPong.exe &
pushd "$(winepath "$winePath")"
wine "$wineExec"
popd
exit 0
fi
if [[ "${game[0]}" == "executioner's-rage" ]]; then
find "${WINEPREFIX}/drive_c/Program Files" -type f -name 'nvdaControllerClient64.dll' -exec cp -v "${cache}/nvda2speechd64.dll" "{}" \;
fi
if [[ "${game[0]}" == "laser-breakout" ]]; then
"${0%/*}/speech/speak_window_title.sh" play.exe &
fi
if [[ "${game[0]}" == "mist-world" ]]; then
"${0%/*}/speech/speak_window_title.sh" mw.exe &
fi
if [[ "${game[0]}" == "bokurano-daibouken-2" ]]; then
"${0%/*}/speech/clipboard_translator.sh" play.exe bokurano-daibouken2 &
fi
if [[ "${game[0]}" == "bokurano-daibouken" ]]; then
"${0%/*}/speech/clipboard_translator.sh" play.exe bokurano-daibouken &
fi
if [[ "${game[0]}" =~ "bokurano-daibouken-3" ]]; then
dictPath="$(winepath "${winePath}")"
if [[ -r "${cache}/bk3-dict.dat" ]] && [[ ! -d "${dictPath}/dict" ]]; then
cp "${cache}/bk3-dict.dat" "${dictPath}/dict.dat"
fi
if [[ -d "${dictPath}/dict" ]]; then
if [[ ! -e "${dictPath}/data/nvdaControllerClient.dll" ]]; then
cp "${cache}/nvda2speechd32.dll" "${dictPath}/data/nvdaControllerClient.dll"
fi
fi
if [[ ! -d "${dictPath}/dict" ]] && [[ ! -r "${cache}/bk3-dict.dat" ]]; then
find "${WINEPREFIX}/drive_c/nyanchangame/bk3" -type f -name 'nvdaControllerClient.dll' -exec rm -v "{}" \;
"${0%/*}/speech/clipboard_translator.sh" play.exe bokurano-daibouken3 &
fi
fi
if [[ "${game[0]}" == "bop-it-emulator" ]]; then
"${0%/*}/speech/speak_window_title.sh" bop.exe &
fi
if [[ "${game[0]}" == "road-to-rage" ]]; then
"${0%/*}/speech/speak_window_title.sh" trtr.exe &
fi
if [[ "${game[0]}" == "sequence-storm" ]]; then
"${0%/*}/speech/clipboard_reader.sh" SequenceStorm &
fi
if [[ "${game[0]}" == "shadow-line" ]]; then
find "${WINEPREFIX}/drive_c/" -type f -name 'nvdaControllerClient.dll' -exec rm -v "{}" \;
"${0%/*}/speech/clipboard_translator.sh" play_sr.exe shadow-line &
fi
if [[ "${game[0]}" == "sketchbook" ]]; then
find "${WINEPREFIX}" -type f -name 'nvdaControllerClient32.dll' -exec cp -v "${cache}/nvdaControllerClient32.dll" "{}" \;
fi
if [[ "${game[0]}" == "audiodisc" ]]; then
wine "$winePath\\$wineExec"
exit 0
fi
if [[ "${game[0]}" == "audioquake" ]]; then
wine "$winePath\\$wineExec"
exit 0
fi
if [[ "${game[0]}" == "screaming-strike-2" ]]; then
pushd "$(winepath "$winePath")"
${wine} "$wineExec"
popd
exit 0
fi
if [[ "${game[0]}" == "warsim" ]]; then
pushd "$(winepath "${game[1]%\\*}")"
wine "${game[1]##*\\}"
popd
exit 0
fi
if [[ "${game[0]}" == "interceptor" ]]; then
pushd "$(winepath "$winePath")"
wine "$wineExec"
popd
exit 0
fi
if [[ -d "${WINEPREFIX}/drive_c/windows/syswow64" ]]; then
# switch to wine64 for 64 bit prefix.
[[ "${wine}" == "/usr/bin/wine" ]] && export wine="/usr/bin/wine64"
fi
}
# Process game launcher flags
process_launcher-flags() {
flags=("${game[@]:3}")
for i in "${flags[@]}" ; do
if [[ "${i}" =~ ^export\ [a-zA-Z_][a-zA-Z0-9_]*=\'?.*\'?$ ]]; then
eval "${i}"
fi
done
}
create_game_array() {
# Game array 0 bottle, 1 path, 2 title, 3+ flags
for i in "${lines[@]}" ; do
if [[ "${game}" =~ ^${i} ]]; then
# This is weird. Why do I have to set game to i before making the array?
game="$i"
IFS='|' read -ra game <<< "$i"
break
fi
done
}
# launch games that are installed
game_launcher() {
# For use by update scripts that want to source functions in this file.
[[ "$agmNoLaunch" == "true" ]] && return
pgrep -u "$USER" nvda2speechd &> /dev/null || {
if [[ -x ${XDG_DATA_HOME:-$HOME/.local/share}/audiogame-manager/nvda2speechd ]]; then
if command -v FEXLoader &> /dev/null ; then
FEXLoader -- ${XDG_DATA_HOME:-$HOME/.local/share}/audiogame-manager/nvda2speechd &> /dev/null &
else
${XDG_DATA_HOME:-$HOME/.local/share}/audiogame-manager/nvda2speechd &> /dev/null &
fi
fi; }
mapfile -t lines < <(sed -e '/^$/d' -e '/^ *#/d' "${configFile}" 2> /dev/null)
if [[ ${#lines} -eq 0 ]]; then
echo "Install some games first."
exit 0
fi
if [[ $# -eq 0 ]]; then
# Create the menu of installed games
declare -a menuList
for i in "${lines[@]}" ; do
IFS='|' read -ra gameInfo <<< "$i"
menuList+=("${gameInfo[0]}|${gameInfo[1]}" "${gameInfo[2]}")
done
menuList+=("Donate" "Donate")
menuList+=("Become a Patron" "Become a Patron")
game="$(dialog --backtitle "Audio Game Launcher" \
--clear \
--extra-button \
--extra-label "Documentation" \
--ok-label "Launch" \
--no-tags \
--menu "Please select a game to play" 0 0 0 "${menuList[@]}" --stdout)"
local menuCode=$?
if [[ $menuCode -eq 1 ]]; then
exit 0
elif [[ $menuCode -eq 3 ]]; then
documentation "$game" "$(echo "$game" | cut -d '|' -f2)"
fi
create_game_array
else
create_game_array
if [[ -z "$game" ]]; then
echo "Game $1 not found."
exit 1
fi
fi
if [[ ${#game[@]} -gt 0 ]]; then
if [[ "${game[0]}" == "Donate" ]]; then
open_url "https://ko-fi.com/stormux"
exit 0
fi
if [[ "${game[0]}" == "Become a Patron" ]]; then
open_url "https://2mb.games/product/2mb-patron/"
exit 0
fi
get_bottle "${game[0]}"
# make sure wine is actually set to something
if [[ "$(uname -m)" == "aarch64" ]]; then
export wine="${wine:-/usr/bin/wine}"
else
export wine="${wine:-/usr/bin/wine}"
fi
echo -n "launching "
${wine} --version
# kill any previous existing wineservers for this prefix in case they didn't shut down properly.
${wine}server -k
# launch the game
if command -v qjoypad &> /dev/null ; then
mkdir -p ~/.qjoypad3
touch "${HOME}/.qjoypad3/${game[2]}.lyt"
# A | separated list of games that should not start with qjoypad.
noQjoypadGames="A Hero's Call"
if [[ "${noqjoypad}" != "true" ]] && ! [[ "${game[2]}" =~ ${noQjoypadGames} ]]; then
if pgrep qjoypad &> /dev/null ; then
qjoypad -T "${game[2]}" 2> /dev/null
else
qjoypad -T "${game[2]}" 2> /dev/null &
fi
fi
fi
process_launcher-flags
custom_launch_parameters
${wine:-/usr/bin/wine} start /d "${game[1]%\\*}" "${game[1]##*\\}" /realtime
fi
exit 0
}
# main script
#functions
add_launcher() {
local launchSettings="${game,,}"
launchSettings="${launchSettings//[[:space:]]/-}|${1}|${game}"
shift
while [[ $# -gt 0 ]]; do
launchSettings+="|$1"
shift
done
if ! grep -F -q -x "${launchSettings}" "${configFile}" 2> /dev/null ; then
echo "${launchSettings}" >> "${configFile}"
sort -o "${configFile}" "${configFile}"
# Remove .lnk files because they don't work.
find ~/Desktop -type f -iname '*.lnk' -exec bash -c '
for f ; do
mimeType="$(file -b "$f")"
mimeType="${mimeType%%,*}"
if [[ "$mimeType" == "MS Windows shortcut" ]]; then
rm -v "$f"
fi
done' _ {} +
if [[ "${noCache}" == "true" ]]; then
rm -f "${cache}/${1##*\\}"
fi
fi
}
trap "exit 0" SIGINT
# Check for updates
update
# If display isn't set assume we are launching from console and an X environment is running using display :0
if [[ -z "$DISPLAY" ]]; then
export DISPLAY=":0"
fi
# Settings file
cache="${XDG_CACHE_HOME:-$HOME/.cache}/audiogame-manager"
configFile="${XDG_CONFIG_HOME:-$HOME/.config}/storm-games/audiogame-manager/games.conf"
mkdir -p "${cache}"
mkdir -p "${configFile%/*}"
# Create the path for AGM's helper programs.
# Originally this was only winetricks, thus the name, and I'm too lazy to change it now.
winetricksPath="${XDG_DATA_HOME:-$HOME/.local/share}/audiogame-manager"
mkdir -p "${winetricksPath}"
# Load any arguments from settings.conf file
if [[ -r "${configFile%/*}/settings.conf" ]]; then
source "${configFile%/*}/settings.conf"
fi
# Update the cache for older versions of audiogame-manager
if [[ -d "${configFile%/*}/cache" ]]; then
{ mv -v "${configFile%/*}/cache/"* "${cache}"
rmdir -v "${configFile%/*}/cache/"; } | dialog \
--backtitle "Audiogame Manager" --progressbox "Updating cache, please wait..." -1 -1
fi
checkWinetricksUpdate="false"
# Turn off debug messages
export WINEDEBUG="${winedebug:--all}"
# Compatibility with box86
export BOX86_NOBANNER=1
# During installation, you can set winVer to the versions available.
# To set winetricks arguments, such as virtual desktop, set the winetricksSettings variable.
# Example: winetricksSettings="vd=1024x768"
# Files are cached unless -N no cache is set.
unset noCache
# Manual installation is not default, make sure it's unset
unset manualInstall
unset version
# ipfs gateway
export ipfsGateway="${ipfsGateway:-https://ipfs.stormux.org}"
export nvdaControllerClientDll="${ipfsGateway}/ipfs/QmWu7YdSbKMk1Qm5DKvEA5hk1YuAK8wVkwhDf2CsmPkmF1?filename=nvdaControllerClient32.dll"
# Make sure the minimum of curl, sox, wine, and winetricks are installed or fex-emu on aarch64
if [[ "$(uname -m)" == "aarch64" ]]; then
minimumDependencies=("FEXLoader")
wine="FEXLoader -- /usr/bin/wine"
else
minimumDependencies=("curl" "sox" "wine")
fi
for i in "${minimumDependencies[@]}" ; do
if ! command -v $i &> /dev/null ; then
echo "Please install $i before continuing."
exit 1
fi
done
# Get latest news if available
check_news
# With no arguments, open the game launcher.
if [[ $# -eq 0 ]]; then
game_launcher
fi
# Array of command line arguments
declare -A command=(
[c]="Check your system for necessary components."
[C]="Clear the cache. All game installers will be deleted."
[D]="Create desktop shortcut. You can launch audiogame-manager from the desktop or applications menu."
[d]="Debug mode, wine will be much more verbose when games are installed with this flag."
[h]="This help screen."
[i]="Install games."
[I:]="Noninteractive game installation."
[k]="Kill a running game that is stuck."
[L]="Display license information."
[l:]="Launch given game without interactive audiogame-manager menu specified by its wine bottle."
[N]="No cache, delete the installer after it has been extracted."
[n]="No RHVoice, do not install RHVoice when the game is installed."
[P]="Print a list of packages required by audiogame-manager."
[q]="No qjoypad. Does not launch qjoypad if it is detected."
[R]="Redownload. Removes old versions of packages from cache before installing."
[r]="Remove a game. This will delete all game data."
[S:]="Speech rate. Requires -V voice name, the default is 7. Values 0-9 or A."
[t]="Total games. Show how many games are currently available."
[v:]="Select the voice to be installed, default is Bdl. Options are alan, bdl, clb, or slt."
[V:]="Select the default voice for a bottle."
)
# Convert the keys of the associative array to a format usable by getopts
args="${!command[*]}"
args="${args//[[:space:]]/}"
while getopts "${args}" i ; do
case "$i" in
c) checklist;;
C) clear_cache;;
D) desktop_launcher;;
d)
unset WINEDEBUG
game_installer
;;
h) help;;
i) game_installer;;
I)
game="${OPTARG}"
break;;
k) kill_game;;
L) license;;
l) game_launcher "${OPTARG}";;
N) noCache="true";;
n) norh="true";;
P) checklist quiet;;
q)
noqjoypad="true"
game_launcher;;
R) redownload="true";;
r) game_removal;;
S) defaultRate="${OPTARG}";;
t)
gameCount=$(find .install -type f -iname "*.sh" | wc -l)
dialog --backtitle "Linux Game Manager" \
--infobox "There are currently ${gameCount} games available." -1 -1
exit 0
;;
v) voiceName="${OPTARG}";;
V) defaultVoice="${OPTARG}";;
esac
done
[[ ${#game} -lt 1 ]] && exit 0
[[ "$agmNoLaunch" != "true" ]] && winetricks sandbox
[[ "$agmNoLaunch" != "true" ]] && exit 0