#!/usr/bin/env bash # Dialog accessibility export DIALOGOPTS='--no-lines --visit-items' # 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 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 hexdump -n 2 -v -e '/1 "%02X"' "${cache}/${dest}" | grep -q "4D5A" downloadError=$? ;; "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=1 else downloadError=0 fi ;; esac if [[ $downloadError -ne 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"*) ;& "smashathon"*) ;& "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/:/ }: ${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 } 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 # Temporary work around for winetricks git bugs. Requires winetricks be installed from package manager. /usr/bin/winetricks "$@" return # 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://github.com/RHVoice/alan-eng/releases/download/4.0/RHVoice-voice-English-Alan-v4.0.2016.21-setup.exe" [bdl]="https://github.com/RHVoice/bdl-eng/releases/download/4.1/RHVoice-voice-English-Bdl-v4.1.2016.21-setup.exe" [clb]="https://github.com/RHVoice/clb-eng/releases/download/4.0/RHVoice-voice-English-Clb-v4.0.2016.21-setup.exe" [lyubov]="https://rhvoice.eu-central-1.linodeobjects.com/RHVoice-voice-English-Lyubov-v4.0.2008.15-setup.exe" [slt]="https://github.com/RHVoice/slt-eng/releases/download/4.1/RHVoice-voice-English-Slt-v4.1.2016.21-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 -name "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]}" == "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") local game="" 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 ]] || [[ $menuCode -eq 255 ]]; then exit 0 elif [[ $menuCode -eq 3 ]]; then source .includes/help.sh # Make available in this function 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 # 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 export cache="${XDG_CACHE_HOME:-$HOME/.cache}/audiogame-manager" export 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" # Source helper functions source .includes/functions.sh source .includes/help.sh source .includes/update.sh # Check minimum requirements check_requirements || exit 1 # Check for updates update # 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) ./.includes/checkup.sh;; 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