From a02a8743d21046d010d8a95a6db244ac5fa60085 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 9 Oct 2025 23:13:27 -0400 Subject: [PATCH] Improved system for installing things not available on the system when built. --- home/stormux/.xinitrc | 15 +- usr/local/bin/stormux_install_helper.sh | 244 ++++++++++++++++++++ usr/share/stormux/downloadable_games.json | 22 ++ usr/share/stormux/installable_packages.json | 14 ++ 4 files changed, 287 insertions(+), 8 deletions(-) create mode 100755 usr/local/bin/stormux_install_helper.sh create mode 100644 usr/share/stormux/downloadable_games.json create mode 100644 usr/share/stormux/installable_packages.json diff --git a/home/stormux/.xinitrc b/home/stormux/.xinitrc index e48dac4..1cf3820 100755 --- a/home/stormux/.xinitrc +++ b/home/stormux/.xinitrc @@ -1,5 +1,8 @@ #!/usr/bin/env bash +# Source installation helper +source /usr/local/bin/stormux_install_helper.sh + # Function to run a game with FEXLoader fex_load() { local gameDir="$1" @@ -365,16 +368,12 @@ case "$GAME" in exec steam -bigpicture ;; "thunderbird") - if ! command -v thunderbird &> /dev/null; then - yay -Sy --noconfirm thunderbird - fi + auto_install "thunderbird" orca & exec thunderbird ;; "libreoffice") - if ! command -v libreoffice &> /dev/null; then - yay -Sy --noconfirm libreoffice-still - fi + auto_install "libreoffice" orca & exec libreoffice ;; @@ -436,10 +435,10 @@ case "$GAME" in run_native "wicked-quest" "wicked_quest.py" ;; "Wreckingball") - setup_and_run_downloadable_url "https://sightlesswolf.com/wreckingball.zip" "wreckingball" "wreckingball.exe" "https://sightlesswolf.com" "nvda2speechd" + auto_install "wreckingball" && run_wine "wreckingball" "wreckingball.exe" "nvda2speechd" ;; "Wreckingball (Pulped)") - setup_and_run_downloadable_url "https://sightlesswolf.com/wreckingball-pulped.zip" "wreckingball-pulped" "wreckingball.exe" "https://sightlesswolf.com" "nvda2speechd" + auto_install "wreckingball-pulped" && run_wine "wreckingball-pulped" "wreckingball.exe" "nvda2speechd" ;; "Kitchensinc") pushd "/home/stormux/.wine32/drive_c/Program Files/Kitchen's Sink" diff --git a/usr/local/bin/stormux_install_helper.sh b/usr/local/bin/stormux_install_helper.sh new file mode 100755 index 0000000..46e8f79 --- /dev/null +++ b/usr/local/bin/stormux_install_helper.sh @@ -0,0 +1,244 @@ +#!/usr/bin/env bash + +# Stormux Installation Helper +# Provides automatic installation with speech feedback and progress indicators + +# Configuration +GAMES_REGISTRY="/usr/share/stormux/downloadable_games.json" +PACKAGES_REGISTRY="/usr/share/stormux/installable_packages.json" +LOG_DIR="$HOME/Logs" +LOG_FILE="$LOG_DIR/installer.log" +INSTALL_BASE="$HOME/.local/games" +NVDA_DLL_SOURCE="$HOME/.local/games/nvda" + +# Initialize logging +init_logging() { + mkdir -p "$LOG_DIR" + echo "=== Installation started at $(date) ===" >> "$LOG_FILE" +} + +# Speech output +speak() { + local message="$1" + echo "$message" >> "$LOG_FILE" + spd-say "$message" 2>/dev/null || echo "$message" +} + +# Progress beep +progress_beep() { + # Simple beep using speaker-test or paplay + if command -v paplay &> /dev/null && [[ -f /usr/share/sounds/freedesktop/stereo/message.oga ]]; then + paplay /usr/share/sounds/freedesktop/stereo/message.oga 2>/dev/null & + fi +} + +# Download with progress feedback +download_with_progress() { + local url="$1" + local output="$2" + local name="$3" + + speak "Downloading $name" + echo "Downloading from: $url" >> "$LOG_FILE" + + # Start background process for progress beeps + ( + while kill -0 $$ 2>/dev/null; do + sleep 5 + progress_beep + done + ) & + local beep_pid=$! + + # Download with curl + if curl -L -f --progress-bar -o "$output" "$url" 2>> "$LOG_FILE"; then + kill $beep_pid 2>/dev/null + wait $beep_pid 2>/dev/null + echo "Download successful" >> "$LOG_FILE" + return 0 + else + kill $beep_pid 2>/dev/null + wait $beep_pid 2>/dev/null + echo "Download failed" >> "$LOG_FILE" + return 1 + fi +} + +# Replace NVDA DLLs in game directory +replace_nvda_dlls() { + local game_dir="$1" + + if [[ ! -d "$NVDA_DLL_SOURCE" ]]; then + echo "Warning: NVDA DLL source directory not found" >> "$LOG_FILE" + return 0 + fi + + # Replace all NVDA DLLs + { + find "$game_dir" -type f -name "nvdaControllerClient32.dll" -exec cp -v "$NVDA_DLL_SOURCE/nvdaControllerClient32.dll" '{}' \; + find "$game_dir" -type f -name "nvdaControllerClient64.dll" -exec cp -v "$NVDA_DLL_SOURCE/nvdaControllerClient64.dll" '{}' \; + find "$game_dir" -type f -name "nvdaControllerClient.dll" -exec cp -v "$NVDA_DLL_SOURCE/nvdaControllerClient64.dll" '{}' \; + } >> "$LOG_FILE" 2>&1 +} + +# Extract and setup game from ZIP +setup_game_zip() { + local zipfile="$1" + local game_dir="$2" + local game_name="$3" + + speak "Installing $game_name" + echo "Extracting to: $game_dir" >> "$LOG_FILE" + + # Remove old installation + rm -rf "$game_dir" + mkdir -p "$game_dir" + + # Check if zip has a single top-level directory + mapfile -t topDirs < <(unzip -l "$zipfile" 2>> "$LOG_FILE" | awk 'NR>3 {print $4}' | grep '/$' | cut -d/ -f1 | sort -u | grep -v '^$') + + if [[ ${#topDirs[@]} -eq 1 ]]; then + # Zip has a single top-level dir - extract to parent, then rename if needed + echo "Single top-level directory detected: ${topDirs[0]}" >> "$LOG_FILE" + if ! unzip -q "$zipfile" -d "$INSTALL_BASE" 2>> "$LOG_FILE"; then + echo "Extraction failed" >> "$LOG_FILE" + return 2 + fi + + # If the extracted dir name differs from target, rename it + if [[ "${topDirs[0]}" != "$(basename "$game_dir")" ]]; then + mv "$INSTALL_BASE/${topDirs[0]}" "$game_dir" 2>> "$LOG_FILE" + fi + else + # Multiple top-level entries - extract directly to game_dir + echo "Multiple top-level entries detected" >> "$LOG_FILE" + if ! unzip -q "$zipfile" -d "$game_dir" 2>> "$LOG_FILE"; then + echo "Extraction failed" >> "$LOG_FILE" + return 2 + fi + fi + + # Replace NVDA DLLs + replace_nvda_dlls "$game_dir" + + # Clean up temp file + rm -f "$zipfile" + + echo "Installation complete" >> "$LOG_FILE" + return 0 +} + +# Install system package via yay +install_system_package() { + local package="$1" + local name="$2" + + speak "Installing $name" + echo "Installing package: $package" >> "$LOG_FILE" + + if yay -Sy --noconfirm "$package" >> "$LOG_FILE" 2>&1; then + echo "Package installation successful" >> "$LOG_FILE" + return 0 + else + echo "Package installation failed" >> "$LOG_FILE" + speak "Installation of $name failed. Check logs." + return 1 + fi +} + +# Check if game is installed +is_game_installed() { + local game_dir="$1" + local executable="$2" + + [[ -e "$game_dir/$executable" ]] +} + +# Check if package is installed +is_package_installed() { + local verify_command="$1" + eval "$verify_command" &>/dev/null +} + +# Auto-install: Main function +# Usage: auto_install +# Returns: 0 if ready to launch, non-zero on failure +auto_install() { + local item_id="$1" + + init_logging + + # Try to find in games registry + if [[ -f "$GAMES_REGISTRY" ]]; then + local game_info + game_info=$(jq -r ".downloadable_games[\"$item_id\"] // empty" "$GAMES_REGISTRY" 2>/dev/null) + + if [[ -n "$game_info" ]]; then + local name url directory executable game_dir + name=$(echo "$game_info" | jq -r '.name') + url=$(echo "$game_info" | jq -r '.url') + directory=$(echo "$game_info" | jq -r '.directory') + executable=$(echo "$game_info" | jq -r '.executable') + game_dir="$INSTALL_BASE/$directory" + + # Check if already installed + if is_game_installed "$game_dir" "$executable"; then + echo "Game already installed: $name" >> "$LOG_FILE" + return 0 + fi + + # Download and install + local temp_zip="/tmp/${directory}_download.zip" + if download_with_progress "$url" "$temp_zip" "$name"; then + if setup_game_zip "$temp_zip" "$game_dir" "$name"; then + # Verify installation + if is_game_installed "$game_dir" "$executable"; then + speak "Installation complete" + return 0 + else + speak "Installation failed. Executable not found." + echo "Error: Executable not found after installation: $executable" >> "$LOG_FILE" + return 3 + fi + else + speak "Installation failed. Could not extract game." + return 2 + fi + else + speak "Download failed. Check your internet connection." + return 1 + fi + fi + fi + + # Try to find in packages registry + if [[ -f "$PACKAGES_REGISTRY" ]]; then + local package_info + package_info=$(jq -r ".installable_packages[\"$item_id\"] // empty" "$PACKAGES_REGISTRY" 2>/dev/null) + + if [[ -n "$package_info" ]]; then + local name package verify_command + name=$(echo "$package_info" | jq -r '.name') + package=$(echo "$package_info" | jq -r '.package') + verify_command=$(echo "$package_info" | jq -r '.verify_command') + + # Check if already installed + if is_package_installed "$verify_command"; then + echo "Package already installed: $name" >> "$LOG_FILE" + return 0 + fi + + # Install package + if install_system_package "$package" "$name"; then + speak "Installation complete" + return 0 + else + return 1 + fi + fi + fi + + # Item not found in any registry + echo "Error: Item not found in registries: $item_id" >> "$LOG_FILE" + return 4 +} diff --git a/usr/share/stormux/downloadable_games.json b/usr/share/stormux/downloadable_games.json new file mode 100644 index 0000000..eaf4b89 --- /dev/null +++ b/usr/share/stormux/downloadable_games.json @@ -0,0 +1,22 @@ +{ + "downloadable_games": { + "wreckingball": { + "name": "Wreckingball", + "section": "Arcade", + "url": "https://sightlesswolf.com/wreckingball.zip", + "directory": "wreckingball", + "executable": "wreckingball.exe", + "launch_mode": "wine", + "needs_nvda": "nvda2speechd" + }, + "wreckingball-pulped": { + "name": "Wreckingball (Pulped)", + "section": "Arcade", + "url": "https://sightlesswolf.com/wreckingball-pulped.zip", + "directory": "wreckingball-pulped", + "executable": "wreckingball.exe", + "launch_mode": "wine", + "needs_nvda": "nvda2speechd" + } + } +} \ No newline at end of file diff --git a/usr/share/stormux/installable_packages.json b/usr/share/stormux/installable_packages.json new file mode 100644 index 0000000..503be93 --- /dev/null +++ b/usr/share/stormux/installable_packages.json @@ -0,0 +1,14 @@ +{ + "installable_packages": { + "thunderbird": { + "name": "Thunderbird Email Client", + "package": "thunderbird", + "verify_command": "command -v thunderbird" + }, + "libreoffice": { + "name": "LibreOffice", + "package": "libreoffice-still", + "verify_command": "command -v libreoffice" + } + } +}