#!/usr/bin/env bash # Dialog accessibility export DIALOGOPTS='--no-lines --visit-items' # 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() { source .includes/bottle.sh 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() { source .includes/bottle.sh 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 game="$1" for i in "${lines[@]}" ; do # Only compare the launcher section j="${game#*|}" k="${i#*|}" k="${k%%|*}" if [[ "$j" == "$k" ]]; then 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 source .includes/bottle.sh 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 "$game" else create_game_array "$game" 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 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/bottle.sh # Also sourced in functions that need it source .includes/desktop.sh 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" ]] && exit 0