Files
linux-game-manager/linux-game-manager.sh

898 lines
31 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 linux-game-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. All Rights
Reserved.
Contributor Michael Taboada.
Attribution Copyright Notice: linux-game-manager copyright 2022 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'
# UI wrapper functions for dialog/yad switching
# Automatically switches between dialog (console) and yad (GUI) based on DISPLAY environment
# Wrapper function for menu selection
# Usage: ui_menu "title" "backtitle" "text" option1 "description1" option2 "description2" ...
ui_menu() {
local title="$1"
local back_title="$2"
local text="$3"
shift 3
if [[ "$dialog_type" == "yad" ]]; then
# Build yad list format: Display only, then map back to value
local yad_list=""
declare -A value_map
while [[ $# -gt 0 ]]; do
local option="$1"
local description="$2"
value_map["$description"]="$option"
if [[ -n "$yad_list" ]]; then
yad_list="$yad_list\n"
fi
yad_list="${yad_list}${description}"
shift 2
done
local selected_description
selected_description=$(echo -e "$yad_list" | yad --list \
--title="$title" \
--text="$text" \
--column="Option" \
--no-headers \
--selectable-labels \
--search-column=1 \
--height=400 \
--width=600)
# Strip trailing newline, pipe, and any other whitespace
selected_description=$(echo "$selected_description" | tr -d '\n\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/|$//')
# Return the mapped value
if [[ -n "$selected_description" ]]; then
echo "${value_map["$selected_description"]}"
fi
else
# Build dialog menu format with mapping (same approach as yad)
local dialog_args=()
declare -A value_map
while [[ $# -gt 0 ]]; do
local option="$1"
local description="$2"
value_map["$description"]="$option"
dialog_args+=("$description" "$description")
shift 2
done
local selected_description
selected_description=$(dialog --backtitle "$back_title" \
--title "$title" \
--no-tags \
--menu "$text" 0 0 0 \
"${dialog_args[@]}" \
--stdout)
# Return the mapped value
if [[ -n "$selected_description" ]]; then
echo "${value_map["$selected_description"]}"
fi
fi
}
# Wrapper function for message box
# Usage: ui_msgbox "title" "backtitle" "text"
ui_msgbox() {
local title="$1"
local back_title="$2"
local text="$3"
if [[ "$dialog_type" == "yad" ]]; then
yad --form \
--title="$title" \
--field="$text:LBL" \
--selectable-labels \
--button="OK:0" \
--width=400
else
dialog --backtitle "$back_title" \
--title "$title" \
--msgbox "$text" 0 0
fi
}
# Wrapper function for yes/no dialog
# Usage: ui_yesno "title" "backtitle" "text"
ui_yesno() {
local title="$1"
local back_title="$2"
local text="$3"
if [[ "$dialog_type" == "yad" ]]; then
yad --form \
--title="$title" \
--field="$text:LBL" \
--selectable-labels \
--button="Yes:0" \
--button="No:1" \
--width=400
else
dialog --backtitle "$back_title" \
--title "$title" \
--yesno "$text" 0 0
fi
}
# Wrapper function for info box (non-blocking message)
# Usage: ui_infobox "title" "backtitle" "text"
ui_infobox() {
local title="$1"
local back_title="$2"
local text="$3"
if [[ "$dialog_type" == "yad" ]]; then
# For yad, we'll use a notification since infobox is non-blocking
yad --notification \
--text="$text" \
--timeout=3
else
dialog --backtitle "$back_title" \
--title "$title" \
--infobox "$text" 0 0
fi
}
# Wrapper function for progress box
# Usage: command | ui_progressbox "title" "text"
ui_progressbox() {
local title="$1"
local text="$2"
if [[ "$dialog_type" == "yad" ]]; then
# Start audio feedback for accessibility
local beep_pid
local yad_pid
# Cleanup function for traps
cleanup_progress() {
[[ -n "$beep_pid" ]] && kill "$beep_pid" 2>/dev/null
[[ -n "$yad_pid" ]] && kill "$yad_pid" 2>/dev/null
}
# Set trap to ensure cleanup on interruption
trap cleanup_progress EXIT INT TERM
if command -v sox >/dev/null 2>&1; then
{
while true; do
# Generate a short, pleasant progress beep (440Hz for 0.1s)
sox -q -n -d synth 0.1 sine 440 vol 0.3 2>/dev/null
sleep 2 # Beep every 2 seconds
done
} &
beep_pid=$!
fi
# Start visual progress dialog with auto-close
yad --progress \
--title="$title" \
--text="$text" \
--auto-close \
--pulsate \
--width=400 \
--height=100 &
yad_pid=$!
# Process command output
local line_count=0
while IFS= read -r line; do
((line_count++))
done < <(stdbuf -oL cat)
# Clean up background processes
cleanup_progress
trap - EXIT INT TERM # Remove traps
else
dialog --title "$title" \
--progressbox "$text" 20 70
fi
}
# Simple progress box that just shows the operation is running (no command output)
# Usage: command | ui_simple_progressbox "title" "text"
ui_simple_progressbox() {
local title="$1"
local text="$2"
if [[ "$dialog_type" == "yad" ]]; then
# Start audio feedback for accessibility
local beep_pid
local yad_pid
# Cleanup function for traps
cleanup_simple_progress() {
[[ -n "$beep_pid" ]] && kill "$beep_pid" 2>/dev/null
[[ -n "$yad_pid" ]] && kill "$yad_pid" 2>/dev/null
}
# Set trap to ensure cleanup on interruption
trap cleanup_simple_progress EXIT INT TERM
if command -v sox >/dev/null 2>&1; then
{
while true; do
# Generate a short, pleasant progress beep (440Hz for 0.1s)
sox -q -n -d synth 0.1 sine 440 vol 0.3 2>/dev/null
sleep 2 # Beep every 2 seconds
done
} &
beep_pid=$!
fi
# Show progress dialog with pulsating
yad --progress \
--title="$title" \
--text="$text" \
--auto-close \
--pulsate \
--width=400 \
--height=100 &
yad_pid=$!
# Read from stdin and discard, but keep dialogs open until command finishes
cat > /dev/null
# Clean up background processes
cleanup_simple_progress
trap - EXIT INT TERM # Remove traps
else
dialog --title "$title" \
--progressbox "$text" 20 70
fi
}
# Check for updates
check_update() {
if ! [[ -d ".git" ]]; then
return
fi
local url
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
local remote
home="$(git rev-parse @)"
remote="$(git rev-parse "$upstream")"
if [[ "$home" == "$remote" ]]; then
return
fi
ui_yesno "Linux Game Manager" "Linux Game Manager" "Updates are available. Would you like to update now?" || return
# Perform git pull quietly, then show only the important changes
local update_result
git pull --quiet >/dev/null 2>&1
update_result=$?
# Get the important information: commit messages and summary
local changes_summary
changes_summary=$({
echo "UPDATE COMPLETED"
echo
echo "Recent changes:"
git log '@{1}..' --pretty=format:'• %an: %s' | tac
echo
echo "Update summary:"
git diff --stat '@{1}..' | tail -1
} 2>/dev/null)
# Show only the meaningful update information
ui_msgbox "Update Complete" "Linux Game Manager" "$changes_summary"
exit $update_result
}
# Check architecture compatibility
check_architecture() {
if command -v FEXLoader &> /dev/null ; then
export fex="FEXLoader -- "
return
fi
local architecture="$(uname -m)"
for i in "$@" ; do
if [[ "${architecture}" == "$i" ]]; then
return
fi
done
ui_infobox "Linux Game Manager" "Linux Game Manager" "This game is not compatible with $architecture architecture."
exit 1
}
# Check dependencies required for games
check_dependencies() {
local dependencies
for i in "${@}"; do
if [[ "$i" =~ ^python- ]]; then
if ! python3 -c "import ${i#*:}" &> /dev/null ; then
dependencies+=("${i%:*}")
fi
elif ! command -v "$i" > /dev/null 2>&1 ; then
dependencies+=("$i")
fi
done
if [[ "${#dependencies[@]}" -eq 0 ]]; then
return
fi
local missing_deps="missing dependencies. Please install the following:\n\n"
for i in "${dependencies[@]}" ; do
missing_deps+="$i\n"
done
ui_msgbox "Linux Game Manager" "Linux Game Manager" "$missing_deps"
exit 1
}
# Function to open a terminal emulator
terminal_emulator() {
# Arguments workingDirectory, rest of arguments
local workingDir="$1"
shift
terminals=(
"lxterminal"
"mate-terminal"
"gnome-terminal"
)
for i in "${terminals[@]}" ; do
if command $i --working-directory="${workingDir}" -e "$@" ; then
return
fi
done
local terminal_msg="No suitable terminal emulators found, please install one of:\n\n"
for i in "${terminals[@]}" ; do
terminal_msg+="$i\n"
done
ui_msgbox "Linux Game Manager" "Linux Game Manager" "$terminal_msg"
}
# Function to open urls
open_url() {
xdg-open "${*}" 2> /dev/null
}
# Create desktop launcher file
desktop_launcher() {
local desktopFile="${HOME}/linux-game-manager.desktop"
if [[ -e "${desktopFile}" ]]; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "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 2>&1 ; then
terminal="$i"
break
fi
done
dotDesktop=('[Desktop Entry]'
'Name=Linux game manager'
'GenericName=Linux game Manager'
'Comment=Install and launch games that are accessible to the blind'
"Exec=${terminal} -t \"Linux Game Manager\" -e \"/usr/bin/bash -c 'pushd $(readlink -e "${0%/*}");nohup ./"${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/linux-game-manager.desktop
rm "${desktopFile}"
exit 0
}
# 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
ui_msgbox "Linux Game Manager" "Linux Game Manager" "Press OK to continue."
}
clear_cache() {
if [[ ! -d "${cache}" ]]; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "No cache found at ${cache}."
return
fi
if ! ui_yesno "Linux Game Manager" "Linux Game Manager" "This will delete all contents of ${cache}. Are you sure you want to continue?"; then
return
fi
# All safety checks done. Delete the cache.
rm -rfv "${cache}" | ui_progressbox "Linux Game Manager" "Clearing cache..."
ui_msgbox "Linux Game Manager" "Linux Game Manager" "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
ui_msgbox "Linux Game Manager" "Linux Game Manager" "Could not download \"$i\"..."
exit 1
fi; } | ui_progressbox "Linux Game Manager" "Downloading \"$dest\" from \"$i\""
local downloadError=1
case "${dest##*.}" in
"pk3"|"zip")
unzip -tq "${cache}/${dest}" | ui_progressbox "Linux Game Manager" "Validating ${dest##*.} file"
downloadError=$?
;;
"7z")
7z t "${cache}/${dest}" | ui_progressbox "Linux Game Manager" "Validating 7z file"
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
downloadError=1
fi
;;
esac
if [[ $downloadError -ne 0 ]]; then
rm -fv "${cache}/${dest}"
ui_infobox "Linux Game Manager" "Linux Game Manager" "Error downloading \"${dest}\". Installation cannot continue."
alert
exit 1
fi
done
}
download_named() {
# Only needed if url breaks the name, e.g. downloads/?filename=1234
# Required arguments: filename url
# Only works with one file at a time.
local dest="$1"
# Validate arguments
if [[ -z "$dest" ]] || [[ -z "$2" ]]; then
return 1
fi
# Remove the destination file if it is empty.
test -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.
test -e "${cache}/${dest}" && return
if ! curl -L4 --output "${cache}/${dest}" "${2}" ; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "Could not download \"$dest\"..."
exit 1
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
alert
ui_msgbox "Linux Game Manager" "Linux Game Manager" "$message"
# Search the Desktop and Downloads directories for the installation file
for i in ~/Downloads ~/Desktop ; do
find $i -type f -name "$1" -exec mv -v {} "${cache}/" \;
done
# If the file is still not available abort.
if [[ ! -f "${cache}/$1" ]]; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "Couldn't find $1. Please download the file and try again."
exit 1
fi
}
help() {
local help_text="${0##*/}\nReleased under the terms of the Common Public Attribution License Version 1.0\nThis is a Stormux project: https://stormux.org\n\nUsage:\n\nWith no arguments, open the game launcher.\n\n"
# Add command options
for i in "${!command[@]}" ; do
help_text+="-${i/:/ <parameter>}: ${command[${i}]}\n"
done
help_text+="\nSome settings that are often used can be stored in a settings.conf file.\nIf wanted, place it at the following location:\n${configFile%/*}/settings.conf\nThe syntax is variable=\"value\"\n\n"
help_text+="doomLanguage=\"en\" # 2 letter language code for translation.\n"
help_text+="ipfsGateway=\"https://ipfs.stormux.org\" # Gateway to be used for ipfs downloads.\n"
help_text+="noCache=\"true\" # Do not keep downloaded items in the cache.\n"
help_text+="spd_module=<module_name>\" # set speech-dispatcher module.\n"
help_text+="spd_pitch=<number>\" # set speech-dispatcher speech pitch.\n"
help_text+="spd_rate=<number>\" # set speech-dispatcher speech rate.\n"
help_text+="spd_voice=<voice_name>\" # set speech-dispatcher voice. Be sure module is correct.\n"
help_text+="spd_volume=<number>\" # set speech-dispatcher speech volume.\n\n"
help_text+="INSTALLER SCRIPT DIALOG FUNCTIONS:\n"
help_text+="ui_msgbox \"title\" \"backtitle\" \"message\" - Show information message\n"
help_text+="ui_yesno \"title\" \"backtitle\" \"question\" - Ask yes/no question\n"
help_text+="ui_inputbox \"title\" \"backtitle\" \"prompt\" \"default\" - Get text input\n"
help_text+="ui_menu \"title\" \"backtitle\" \"prompt\" option1 \"desc1\" ... - Show menu\n"
help_text+="ui_progressbox \"title\" \"text\" - Progress dialog for piped commands"
ui_msgbox "Linux Game Manager Help" "Linux Game Manager" "$help_text"
exit 0
}
# main script
# Install games
game_installer() {
# 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 #, and sort them
mapfile -t sortedGames < <(for f in "${0%/*}/.install/"*.sh; do
# Skip if first line starts with #
[[ $(head -n1 "$f") == "#"* ]] && continue
# Output filename without .sh extension
echo "${f##*/}"
done | sort)
for i in "${sortedGames[@]}"; do
local menuItem="${i%.sh}"
# 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
ui_msgbox "Linux Game Manager" "Linux Game Manager" "All games are already installed."
exit 0
fi
# Add donation option at the end
menuList+=("Donate" "Donate")
# Show game selection dialog
game="$(ui_menu "Game Installer" "Game Installer" "Please select a game to install" "${menuList[@]}")"
# Handle selection
if [[ -z "$game" ]]; then
exit 0
fi
if [[ -n "$game" ]]; then
if [[ "$game" == "Donate" ]]; then
open_url "https://ko-fi.com/stormux"
exit 0
fi
# 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"
# Show success message
ui_msgbox "Game Installer" "Game Installer" "${game} has been installed."
else
ui_msgbox "Game Installer" "Game Installer" "Installation script not found for ${game}"
exit 1
fi
fi
if [[ -e "${0%/*}/.launch/${game}.game" ]] && ! [[ -L "${0%/*}/.launch/${game}.sh" ]]; then
ln -srf "${0%/*}/.launch/${game}.game" "${0%/*}/.launch/${game}.sh"
fi
exit 0
}
# remove games
game_removal() {
# Initialize array for menu construction
mapfile -t menuList < <(
if [[ -d ".launch" ]]; then
find -L "${0%/*}/.launch" -maxdepth 1 -type f -iname "*.sh" -print0 | sort -z | xargs -0 bash -c '
for f; do
name="${f##*/}"
echo "$f"
echo "${name%.sh}"
done' _
fi
)
if [[ ${#menuList} -eq 0 ]]; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "No games found."
exit 0
fi
# Create the menu of installed games
# menuList has alternating full_path, display_name pairs
declare -a menuArgs
for ((i=0; i<${#menuList[@]}; i+=2)); do
menuArgs+=("${menuList[i]}" "${menuList[i+1]}")
done
local selectedGame
selectedGame=$(ui_menu "Linux Game Manager" "Linux Game Manager" "Please select a game to delete" "${menuArgs[@]}")
exitCode=$?
[[ $exitCode -ne 0 ]] || [[ -z "$selectedGame" ]] && exit 0
# Get the actual game file paths
local gameName="${selectedGame##*/}"
gameName="${gameName%.sh}"
local gameFile="$(readlink -f "${0%/*}/.launch/${gameName}.sh")"
# Get the actual installation path from the .game file
local gameInstallPath
gameInstallPath="$(grep -F "installPath" "$gameFile" | grep -v 'pushd' | head -n1)"
gameInstallPath="${gameInstallPath#*/}"
gameInstallPath="${installPath}/${gameInstallPath%/*}"
if [[ -z "$gameInstallPath" ]] || [[ "${gameInstallPath%%/}" == "$installPath" ]]; then
# No install path found, just remove from list
ui_yesno "Linux Game Manager" "Linux Game Manager" "This will remove the game from your game list, but will not remove any files. Do you want to continue?" || exit 0
# Remove only the .sh symlink
rm -fv "${0%/*}/.launch/${gameName}.sh" | \
ui_progressbox "Linux Game Manager" "Removing game from list..."
# Show success message
ui_msgbox "Linux Game Manager" "Linux Game Manager" "${gameName} has been removed from the list."
else
# Found install path, can remove game files
ui_yesno "Linux Game Manager" "Linux Game Manager" "This will remove the directory \"${gameInstallPath}\" and all of its contents. Do you want to continue?" || exit 0
# Remove the game directory and symlink
{ rm -rfv "${gameInstallPath}"
rm -fv "${0%/*}/.launch/${gameName}.sh";
} | ui_progressbox "Linux Game Manager" "Removing game..."
# Show success message
ui_msgbox "Linux Game Manager" "Linux Game Manager" "${gameName} has been removed."
fi
exit 0
}
# update games
game_update() {
mapfile -t lines < <(find .update -type f -iname '*.sh' 2> /dev/null)
if [[ ${#lines} -eq 0 ]]; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "No games found."
exit 0
fi
# Create the menu of updatable games
declare -a menuList
for i in "${lines[@]}" ; do
menuList+=("${i}" "${i##*/}")
done
menuList+=("Donate" "Donate")
menuList+=("Become a Patron" "Become a Patron")
local game="$(ui_menu "Audio Game Updater" "Audio Game Updater" "Please select a game to update" "${menuList[@]}")"
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
# Get the game name for success message
local updateGameName="${game##*/}"
updateGameName="${updateGameName%.sh}"
source "${game}"
else
exit 0
fi
run_update
# Show success message
ui_msgbox "Audio Game Updater" "Audio Game Updater" "${updateGameName} has been updated."
exit 0
}
# launch games that are installed
game_launcher() {
# Initialize array for menu construction
mapfile -t menuList < <(
if [[ -d ".launch" ]]; then
find -L "${0%/*}/.launch" -maxdepth 1 -type f -iname "*.sh" -print0 | sort -z | xargs -0 bash -c '
for f; do
[[ $(head -n1 "$f") =~ ^#$ ]] && continue
name="${f##*/}"
echo "$f"
echo "${name%.sh}"
done' _
fi
)
if [[ ${#menuList} -eq 0 ]]; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "No games found."
exit 0
fi
# Create the menu of all games
# menuList has alternating full_path, display_name pairs
declare -a menuArgs
for ((i=0; i<${#menuList[@]}; i+=2)); do
menuArgs+=("${menuList[i]}" "${menuList[i+1]}")
done
selectedGame="$(ui_menu "Linux Game Launcher" "Linux Game Launcher" "Please select a game to play" "${menuArgs[@]}")"
local menuCode=$?
if [[ $menuCode -ne 0 ]] || [[ -z "$selectedGame" ]]; then
exit 0
fi
. "${selectedGame}"
exit 0
}
migrate_launcher() {
# Check if config file exists
[[ -f "${configFile}" ]] || return
# Process each line of the config file
while IFS= read -r line; do
# Skip empty lines
[[ -z "${line}" ]] && continue
# Extract game name and path
gameName="${line%|*}"
# Create launcher script if it doesn't exist
if [[ ! -L "${0%/*}/.launch/${gameName}.sh" ]]; then
ln -srf "${0%/*}/.launch/${gameName}.game" "${0%/*}/.launch/${gameName}.sh"
fi
done < <(sed '/^$/d' "${configFile}")
# Move the old config file and notify user
mv "${configFile}" "${configFile}.bak"
ui_msgbox "Linux Game Manager" "Linux Game manager" "Games have been converted to the new launch system.\nThe old game launch information has been moved to ${configFile}.bak\nIf everything works like you expect, feel free to delete ${configFile}"
}
# Detect dialog interface type BEFORE potentially setting DISPLAY
# This must happen before we modify DISPLAY to preserve console detection
if [[ -z "$DISPLAY" ]]; then
dialog_type="dialog"
else
dialog_type="yad"
fi
# If display isn't set assume we are launching from console and an X environment is running using display :0
# Warning, launching games from console is not recommended.
if [[ -z "$DISPLAY" ]]; then
export DISPLAY=":0"
fi
# Settings file
cache="${XDG_CACHE_HOME:-$HOME/.cache}/linux-game-manager"
configFile="${XDG_CONFIG_HOME:-$HOME/.config}/storm-games/linux-game-manager/games.conf"
mkdir -p "${cache}"
mkdir -p "${configFile%/*}"
# Load any arguments from settings.conf file
if [[ -r "${configFile%/*}/settings.conf" ]]; then
source "${configFile%/*}/settings.conf"
fi
unset noCache
export doomLanguage="${doomLanguage:-en}"
export installPath="${HOME}/.local/games"
export ipfsGateway="${ipfsGateway:-https://ipfs.stormux.org}"
export spd_module="${spd_module:+ -o ${spd_module}}"
export spd_pitch="${spd_pitch:+ -p ${spd_pitch}}"
export spd_rate="${spd_rate:+ -r ${spd_rate}}"
export spd_voice="${spd_voice:+ -y ${spd_voice}}"
export spd_volume="${spd_volume:+ -i ${spd_volume}}"
mkdir -p "${installPath}"
migrate_launcher
# Check for required packages
requiredPackages=(
"7z"
"curl"
"dialog"
"yad"
"unzip"
)
for i in "${requiredPackages[@]}" ; do
if ! command -v "$i" > /dev/null 2>&1 ; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "Please install ${i/7z/p7zip} before continuing."
exit 1
fi
done
check_update
# With no arguments, open the game launcher.
if [[ $# -eq 0 ]]; then
game_launcher
fi
# Array of command line arguments
declare -A command=(
[C]="Clear the cache. All game installers will be deleted."
[D]="Create desktop shortcut. You can launch Linux Game Manager from the desktop or applications menu."
[h]="This help screen."
[i]="Install games."
[L]="Display license information."
[N]="No cache, delete the installer after it has been extracted."
[R]="Redownload. Removes old versions of packages from cache before installing."
[r]="Remove game. Remove a game and its menu entry."
[t]="Total games. Show how many games are currently available."
[u]="Update games. Run available game update scripts."
)
# 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) clear_cache ;;
D) desktop_launcher ;;
h) help ;;
i) game_installer ;;
L) license ;;
N) noCache="true" ;;
R) redownload="true" ;;
r) game_removal ;;
t)
gameCount=$(find .install -type f -iname "*.sh" | wc -l)
ui_infobox "Linux Game Manager" "Linux Game Manager" "There are currently ${gameCount} games available."
exit 0
;;
u) game_update ;;
esac
done
exit 0