Compare commits

..

31 Commits

Author SHA1 Message Date
Storm Dragon db508a46cd play.oriolgomez.com minigames added. 2026-04-27 13:20:50 -04:00
Storm Dragon b617aa6ea6 Updated the removal for The Omega Reach so it actually works now. 2026-04-18 20:16:42 -04:00
Storm Dragon 362feb044f Updated installer and launcher for The Omega Reach. 2026-04-18 20:06:19 -04:00
Storm Dragon 03e72a8263 For Top Speed always try to get a valid download link. 2026-04-14 00:12:51 -04:00
Storm Dragon ae37f1a71f For Top Speed always try to get a valid download link. 2026-04-13 19:16:42 -04:00
Storm Dragon 2ecc0240f9 Top Speed version updated to include Fixes for Linux systems. 2026-04-13 04:36:12 -04:00
Storm Dragon 1454407da4 Games "Top Speed" and "The Great Toy Robbery" added. For Top Speed, I found a couple of bugs, so the installed version may not work with your keyboard and may also crash on race launch. I have opened a pull request with these problems fixed, so hopefully it will work soon. 2026-04-12 23:29:12 -04:00
Storm Dragon 1d9ab477db Super Liam updated. Install verified working. 2026-04-11 20:25:35 -04:00
lilmike d0240d1408 Added Super Liam for linux 2026-04-11 12:04:39 -07:00
Storm Dragon 647e97a105 Added game The Omega Reach
Update the alert function to show the actual guidance in a single acknowledgement dialog instead of a generic OK prompt followed by a separate message.
2026-03-02 22:56:05 -05:00
Storm Dragon c86edee113 Fixed speech-dispatcher library in Play Palace. 2026-02-22 06:30:55 -05:00
Storm Dragon 1f7858e1fa Codex skill for development added. Tested by using it to set up Play Palace. 2026-02-21 19:19:54 -05:00
Storm Dragon efc5f20b5b Comment out games with #// instead of just # at top of file. 2026-02-21 16:31:06 -05:00
Storm Dragon 9b454388f5 Constant Motion Added. 2026-02-19 14:09:37 -05:00
lilmike 2c86ca3b21 Fix ssl verification failures for christmas dash 2025-12-26 01:17:32 -08:00
Storm Dragon 93b1f0ad2d Updated Doom installer. 2025-12-22 22:34:34 -05:00
Storm Dragon 5f1fc591f9 Christmas Dash added. 2025-12-22 17:57:52 -05:00
Storm Dragon be86f1e916 make sure update checks are skipped if on a tag instead of a branch. 2025-11-23 20:19:56 -05:00
Storm Dragon be63211e6b Make sure that lgm is not ran as root. 2025-11-08 12:47:48 -05:00
Storm Dragon 5316c5c113 Fixed a bug with the doom install location. 2025-11-03 17:25:29 -05:00
Storm Dragon dce6a39fee Installer updated to version 9 for Toby Doom. 2025-11-01 17:32:50 -04:00
Storm Dragon e985305929 Game "Wicked Quest" added. 2025-09-06 22:19:18 -04:00
Storm Dragon df6470e22f Properly use new line characters so new lines show up instead of \n 2025-08-06 15:10:40 -04:00
Storm Dragon 90fd40fb4a Switch to text-info for informational dialogs in yad. 2025-08-06 15:04:41 -04:00
Storm Dragon 94d9caa707 Confirmation messages added when a task has been completed, e.g. install, upgrade, remove. 2025-08-06 14:37:27 -04:00
Storm Dragon 17e7cac021 Improved accessibility of some yad dialogues. 2025-08-06 14:32:41 -04:00
Storm Dragon 59d3d288da More work on the new dialogue system. Game installers updated. 2025-08-06 14:28:56 -04:00
Storm Dragon 93f92b3c7b Stop error when exiting menus without making a selection. 2025-08-06 14:11:23 -04:00
Storm Dragon 7548db668a More improvements to ui dialogs. 2025-08-06 13:59:12 -04:00
Storm Dragon 502caea79c Add yad as a dependency to run LGM. 2025-08-06 13:44:40 -04:00
Storm Dragon 37a731bab6 Experimental: use yad for graphical menus that should work better with orca and Cthulhu screen readers. 2025-08-04 18:02:58 -04:00
40 changed files with 1450 additions and 256 deletions
@@ -0,0 +1,81 @@
---
name: linux-game-manager-dev
description: Develop and maintain Linux Game Manager in this repository. Use when adding or removing games, editing `.install`/`.launch`/`.update` scripts, changing `linux-game-manager.sh` installer/launcher/removal/update/CLI behavior, validating accessibility-sensitive flows, or refreshing project-local skill docs after functionality changes.
---
# Linux Game Manager Development
Use this skill to make safe, consistent changes to Linux Game Manager and keep the skill accurate as the codebase evolves.
## Fast Routing
1. If the task adds/removes/changes a game, read `references/game-extension.md`.
2. If the task changes orchestration/CLI/config behavior, read `references/core-map.md`.
3. If the task changes behavior and you are updating this skill, read `references/skill-maintenance.md`.
4. Run `python3 .codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py` before and after major game-catalog changes.
## Core Workflow
1. Inspect relevant behavior and identify affected surfaces (`.install`, `.launch`, `.update`, `linux-game-manager.sh`, `speech`, docs).
2. Implement with existing patterns and helper functions instead of inventing new flows.
3. Validate runtime behavior with CLI/manual checks:
- `./linux-game-manager.sh -i`
- `./linux-game-manager.sh -r`
- `./linux-game-manager.sh -u`
- `./linux-game-manager.sh -t`
4. Validate catalog consistency:
- `python3 .codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py`
5. If bash files were edited:
- Verify `shellcheck` is installed.
- If missing, stop and prompt the user to install it (see `references/tooling-prereqs.md`).
- Run shellcheck on edited files and fix all reported errors.
6. If functionality changed, refresh this skill using `references/skill-maintenance.md`.
## Non-Negotiable Rules
1. Keep installer and launcher base filenames identical:
- `.install/<Game Name>.sh`
- `.launch/<Game Name>.game`
2. Ensure launcher scripts include a concrete `installPath`-based path so removal can locate game data.
3. Define `run_update()` in any `.update/<Game>.sh` script.
4. Preserve accessibility-first interaction:
- Keep dialog/yad wrapper usage (`ui_*` helpers) consistent.
- Do not introduce keyboard traps.
- For GUI applications, do not add new `speech-dispatcher`/`spd-say` dependencies.
5. Keep new script variables camelCase, function names snake_case, class names PascalCase.
6. For ambiguous "disable game" requests, default to disabling installer visibility only and preserve launcher playability unless the user explicitly requests complete disable.
## Required Validation
1. Run catalog audit:
- `python3 .codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py`
2. Run manual command checks relevant to change scope:
- install flow: `./linux-game-manager.sh -i`
- launcher flow: `./linux-game-manager.sh`
- removal flow: `./linux-game-manager.sh -r`
- update flow: `./linux-game-manager.sh -u`
3. Run shellcheck on every edited bash script and fix all issues.
- If shellcheck is unavailable, prompt the user to install it before continuing.
4. Validate this skill structure after edits:
- `python3 /home/storm/.codex/skills/.system/skill-creator/scripts/quick_validate.py .codex/skills/linux-game-manager-dev`
## Mandatory Skill Maintenance
Treat this skill as stale immediately after any change to:
- `linux-game-manager.sh`
- Any file in `.install/`, `.launch/`, `.update/`, or `speech/`
- `README.md`
- Any change to game lifecycle expectations (install path rules, update hooks, CLI options, helper function contracts)
When stale, perform the full refresh workflow in `references/skill-maintenance.md` before completing the task.
## References
- `references/core-map.md`: Architecture and extension map.
- `references/game-extension.md`: Exact workflow for adding/removing/extending games.
- `references/skill-maintenance.md`: Critical instructions to keep this skill accurate after code changes.
- `references/tooling-prereqs.md`: Required tools and shellcheck installation prompts by distro.
## Scripts
- `scripts/audit_game_catalog.py`: Reports installer/launcher/update consistency and disabled entries.
@@ -0,0 +1,4 @@
interface:
display_name: "Linux Game Manager Dev"
short_description: "Add games and extend linux-game-manager safely"
default_prompt: "Use the project workflows to add games, modify manager behavior, validate accessibility-friendly flows, and keep this skill synchronized with code changes."
@@ -0,0 +1,81 @@
# Linux Game Manager Core Map
Last refreshed: 2026-04-14
## Top-Level Structure
- `linux-game-manager.sh`: Main orchestrator, UI wrappers, CLI option handling, install/remove/update flows, cache and settings behavior.
- `.install/`: Per-game installer scripts sourced by `game_installer`.
- `.launch/`: Per-game launch scripts (`.game`) and runnable entries (`.sh`, usually symlinks).
- `.update/`: Optional per-game update scripts expected to define `run_update()`.
- `speech/speak_window_title.sh`: Accessibility helper for announcing focused window titles.
- `README.md`: Project summary and high-level behavior notes.
- `.files/`: Game-specific auxiliary assets/scripts used by some installers or launchers.
## Catalog Snapshot
- Installers: 42 (`.install/*.sh`)
- Launcher definitions: 42 (`.launch/*.game`)
- Launcher runnable entries: 31 (`.launch/*.sh`, both symlinks and files)
- Update scripts: 5 (`.update/*.sh`)
Regenerate this snapshot with:
```bash
python3 .codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py
```
## Main Runtime Flows
1. **Install flow**
- `game_installer` builds menu from `.install/*.sh`.
- Selected installer is sourced in the current shell.
- Shared downloads try `curl` first, fall back to `wget` when available, validate archive payloads before install, and reject obvious HTML error pages for unknown file types.
- `download_named()` shares the same cache validation path as `download()`, so resolved asset URLs must still produce a valid payload for their file extension.
- If `.launch/<game>.game` exists and `.launch/<game>.sh` does not, the manager creates a symlink using the repository root, even if the installer changed directories.
2. **Launch flow**
- `game_launcher` enumerates `.launch/*.sh` entries.
- Entries with first line `#//` are skipped.
- Selected launcher is sourced/executed.
3. **Removal flow**
- `game_removal` resolves launcher entry to real script.
- It scans launcher script lines for a non-comment `${installPath}/...` path assignment and uses that to infer the directory to remove.
- If no `installPath` reference is found, only launcher entry is removed.
4. **Update flow**
- `game_update` lists `.update/*.sh`.
- Selected updater is sourced.
- `run_update()` is called and must exist.
## CLI Surface
- `-i`: install game
- `-r`: remove game
- `-u`: update game
- `-t`: show number of available games
- `-C`: clear cache
- `-D`: create desktop launcher
- `-L`: show license
- `-h`: show help
- `-N`: enable no-cache mode
- `-R`: force redownload mode
## Config and State
- Cache: `${XDG_CACHE_HOME:-$HOME/.cache}/linux-game-manager`
- Config base: `${XDG_CONFIG_HOME:-$HOME/.config}/storm-games/linux-game-manager`
- Settings overrides: `settings.conf` in config base
- Default install root: `${HOME}/.local/games`
## Contributor Pitfalls
- Mismatched `.install/<Game>.sh` vs `.launch/<Game>.game` names create orphaned or invisible entries.
- Missing `installPath` reference in launcher prevents full uninstall.
- `game_removal` expects a concrete `${installPath}/...` path in the launcher; indirect path construction can prevent removal from finding the install directory.
- Missing `run_update()` in `.update` scripts breaks update flow.
- Bypassing shared download helpers risks cache and validation regressions.
- Installers may change the working directory; repository-relative manager paths must therefore use the resolved script root rather than `${0%/*}`.
- Use `alert` with its message arguments instead of chaining `alert` and `ui_msgbox`, otherwise `yad` users can get an extra acknowledgement dialog that does not include the actual guidance.
- For `uv`-based Python games that need `speechd`, copy host Python `speechd` bindings into a game-local directory and export `PYTHONPATH` from the launcher.
@@ -0,0 +1,130 @@
# Game Extension Workflow
Use this workflow whenever adding, renaming, removing, or substantially changing a game.
## Add a New Game
1. Create installer script:
- Path: `.install/<Game Name>.sh`
- Keep first line active (do not start with `#//`).
- Reuse manager helpers (`check_architecture`, `check_dependencies`, `download`, `download_named`, `get_installer`, `ui_*`).
- Install into a deterministic path under `${installPath}`.
2. Create launcher definition:
- Path: `.launch/<Game Name>.game`
- Keep base filename identical to installer (`<Game Name>`).
- Include an explicit install-path variable based on `installPath`, for example:
```bash
gamePath="${installPath}/MyGame"
```
- Run the game from that path.
3. Optional update script:
- Path: `.update/<Game Name>.sh`
- Implement `run_update()` (required by update flow).
4. Let installer create runnable launcher entry:
- The manager auto-creates `.launch/<Game Name>.sh` symlink when installer completes and `.game` exists.
## Rename a Game Safely
1. Rename `.install/<Old>.sh` to `.install/<New>.sh`.
2. Rename `.launch/<Old>.game` to `.launch/<New>.game`.
3. Rename `.update/<Old>.sh` if present.
4. Remove stale `.launch/<Old>.sh` if still present.
5. Re-run catalog audit and manual checks.
## Remove a Game From Repo
1. Delete `.install/<Game>.sh`.
2. Delete `.launch/<Game>.game`.
3. Delete `.launch/<Game>.sh` if tracked.
4. Delete `.update/<Game>.sh` if present.
5. Run catalog audit to confirm no orphan entries remain.
## Disable Policy (Important)
When a request says "disable <game>" without more detail:
1. Default behavior: disable installs only.
- Add `#//` to first line of `.install/<Game Name>.sh`.
- Do not disable `.launch/<Game Name>.game` by default.
- This preserves playability for users who already have the game installed.
2. If user explicitly requests complete disable:
- Add `#//` to first line of both:
- `.install/<Game Name>.sh`
- `.launch/<Game Name>.game`
3. If wording is ambiguous but could imply full disable/removal:
- Ask a short clarification question before changing launcher/removal behavior.
## Manual Validation Checklist
1. Run installer flow:
```bash
./linux-game-manager.sh -i
```
2. Confirm launcher files exist:
```bash
ls ".launch/<Game Name>.game" ".launch/<Game Name>.sh"
```
3. Run launcher flow:
```bash
./linux-game-manager.sh
```
4. Run removal flow:
```bash
./linux-game-manager.sh -r
```
5. If updater exists, run update flow:
```bash
./linux-game-manager.sh -u
```
6. Run consistency audit:
```bash
python3 .codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py
```
## Script Authoring Rules
- Use camelCase variable names.
- Use snake_case for function names.
- Keep scripts robust when sourced in the manager shell.
- Avoid introducing unnecessary colorized output.
- For edited bash scripts, run shellcheck and fix all errors.
- If `shellcheck` is not installed, prompt the user to install it first (see `references/tooling-prereqs.md`).
## Pattern: uv + Host Python speechd Bindings
Use this pattern for Python games installed with `uv` when runtime speech support depends on `import speechd`.
1. In installer script:
- Check dependencies: `git`, `uv`, and host python binding import (`python-speechd:speechd`).
- Clone game repository into `${installPath}/<GameRepoDir>`.
- Run `uv sync` in the project directory that contains `pyproject.toml`.
- Resolve speechd source path from host Python via:
- `python3 -c 'import pathlib,speechd; print(pathlib.Path(speechd.__file__).resolve())'`
- Copy the module/package into a game-local directory such as:
- `${installPath}/<GameRepoDir>/<RuntimePath>/.host-python/`
- Fail with a clear message if import or copy fails.
2. In launcher script:
- Export `PYTHONPATH` with the game-local `.host-python` directory prepended.
- Launch via `uv run ...` from the same directory used for `uv sync`.
3. Removal safety:
- Ensure the launcher contains a concrete `${installPath}/...` path assignment so `game_removal` can infer the game root correctly.
@@ -0,0 +1,53 @@
# Skill Maintenance (Critical)
This skill is only trustworthy if it is refreshed immediately after behavior changes.
## Mandatory Refresh Triggers
Run this maintenance workflow whenever any of these change:
- `linux-game-manager.sh`
- Anything in `.install/`
- Anything in `.launch/`
- Anything in `.update/`
- Anything in `speech/`
- `README.md`
- Any game lifecycle contract (install path usage, symlink behavior, update function contracts, CLI flags)
## Refresh Workflow
1. Run catalog audit and capture output:
```bash
python3 .codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py
```
2. Re-check core flow definitions in `linux-game-manager.sh`:
```bash
rg -n "game_installer|game_launcher|game_removal|game_update|getopts|help\\(" linux-game-manager.sh
```
3. Update skill references:
- Update `references/core-map.md` catalog snapshot if counts changed.
- Update `references/core-map.md` flow descriptions if behavior changed.
- Update `references/game-extension.md` if onboarding or naming rules changed.
- Update this file if refresh triggers or process changed.
4. Validate the skill structure:
```bash
python3 /home/storm/.codex/skills/.system/skill-creator/scripts/quick_validate.py .codex/skills/linux-game-manager-dev
```
5. If any bash scripts changed, run shellcheck on edited files and fix all errors.
- If `shellcheck` is missing, pause and prompt the user to install it using `references/tooling-prereqs.md`.
## Completion Criteria
Do not consider maintenance complete until all are true:
1. Catalog audit shows no critical mismatches, and any warnings are reviewed.
2. All changed behavior is reflected in skill reference files.
3. Skill passes `quick_validate.py`.
4. Any edited bash scripts are shellcheck-clean.
@@ -0,0 +1,38 @@
# Tooling Prerequisites
## Required Tool for Bash Changes
- `shellcheck` is mandatory whenever editing any bash/sh file in this repository.
- If `shellcheck` is not available, prompt the user to install it before continuing validation.
Check availability:
```bash
command -v shellcheck
```
## Prompt Template
Use this exact style when missing:
`shellcheck is required for bash/sh edits in linux-game-manager. Please install it, then I will continue validation.`
## Install Commands by Common Distro Family
- Arch/Manjaro:
- `sudo pacman -S shellcheck`
- Debian/Ubuntu/Linux Mint/Pop!_OS:
- `sudo apt update && sudo apt install -y shellcheck`
- Fedora/RHEL/CentOS Stream:
- `sudo dnf install -y ShellCheck`
- openSUSE:
- `sudo zypper install -y ShellCheck`
- Alpine:
- `sudo apk add shellcheck`
If distro is unknown, ask the user what distribution they are on and provide the matching package command.
## Notes for LGM Collaboration
- Do not skip shellcheck for “small” bash changes.
- Fix all shellcheck errors; warnings may be suppressed only when required by sourced-global patterns and with a short comment.
@@ -0,0 +1,202 @@
#!/usr/bin/env python3
"""Audit Linux Game Manager catalog consistency."""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
def read_first_line(file_path: Path) -> str:
try:
with file_path.open("r", encoding="utf-8") as handle:
return handle.readline().strip()
except OSError:
return ""
def has_run_update(file_path: Path) -> bool:
try:
content = file_path.read_text(encoding="utf-8")
except OSError:
return False
return "run_update()" in content or "run_update ()" in content
def collect_names(file_paths: list[Path], suffix: str) -> list[str]:
return sorted(file_path.name[: -len(suffix)] for file_path in file_paths)
def count_disabled(file_paths: list[Path]) -> list[str]:
disabledNames = []
for file_path in file_paths:
if read_first_line(file_path).startswith("#//"):
disabledNames.append(file_path.name)
return sorted(disabledNames)
def build_report(repo_root: Path) -> dict:
installDir = repo_root / ".install"
launchDir = repo_root / ".launch"
updateDir = repo_root / ".update"
missingDirs = [
str(path.relative_to(repo_root))
for path in [installDir, launchDir, updateDir]
if not path.exists()
]
if missingDirs:
raise FileNotFoundError(
f"Expected directories not found: {', '.join(missingDirs)}"
)
installPaths = sorted(installDir.glob("*.sh"))
launchGamePaths = sorted(launchDir.glob("*.game"))
launchShPaths = sorted(launchDir.glob("*.sh"))
updatePaths = sorted(updateDir.glob("*.sh"))
installerNames = collect_names(installPaths, ".sh")
launcherGameNames = collect_names(launchGamePaths, ".game")
launcherShNames = collect_names(launchShPaths, ".sh")
launcherSymlinkCount = sum(1 for path in launchShPaths if path.is_symlink())
launcherFileCount = len(launchShPaths) - launcherSymlinkCount
missingLauncherGameForInstaller = sorted(
set(installerNames) - set(launcherGameNames)
)
missingInstallerForLauncherGame = sorted(
set(launcherGameNames) - set(installerNames)
)
missingLauncherShForLauncherGame = sorted(
set(launcherGameNames) - set(launcherShNames)
)
orphanLauncherSh = sorted(set(launcherShNames) - set(launcherGameNames))
updateWithoutRunFunction = sorted(
path.name for path in updatePaths if not has_run_update(path)
)
report = {
"repoRoot": str(repo_root),
"criticalMismatches": [
"missingLauncherGameForInstaller",
"updateScriptsMissingRunUpdate",
],
"warningMismatches": [
"missingInstallerForLauncherGame",
"missingLauncherShForLauncherGame",
"orphanLauncherSh",
],
"counts": {
"installers": len(installPaths),
"launcherGameDefinitions": len(launchGamePaths),
"launcherRunnableEntries": len(launchShPaths),
"launcherRunnableSymlinks": launcherSymlinkCount,
"launcherRunnableRegularFiles": launcherFileCount,
"updateScripts": len(updatePaths),
},
"disabledEntries": {
"installers": count_disabled(installPaths),
"launchers": count_disabled(launchShPaths),
},
"mismatches": {
"missingLauncherGameForInstaller": missingLauncherGameForInstaller,
"missingInstallerForLauncherGame": missingInstallerForLauncherGame,
"missingLauncherShForLauncherGame": missingLauncherShForLauncherGame,
"orphanLauncherSh": orphanLauncherSh,
"updateScriptsMissingRunUpdate": updateWithoutRunFunction,
},
}
return report
def print_human_report(report: dict) -> None:
counts = report["counts"]
mismatches = report["mismatches"]
criticalMismatches = report["criticalMismatches"]
warningMismatches = report["warningMismatches"]
disabledEntries = report["disabledEntries"]
print(f"Repository: {report['repoRoot']}")
print("Counts:")
print(f" Installers: {counts['installers']}")
print(f" Launcher .game files: {counts['launcherGameDefinitions']}")
print(f" Launcher .sh entries: {counts['launcherRunnableEntries']}")
print(f" Symlinks: {counts['launcherRunnableSymlinks']}")
print(f" Regular files: {counts['launcherRunnableRegularFiles']}")
print(f" Update scripts: {counts['updateScripts']}")
print("Disabled entries:")
print(f" Installers: {len(disabledEntries['installers'])}")
if disabledEntries["installers"]:
print(" " + ", ".join(disabledEntries["installers"]))
print(f" Launchers: {len(disabledEntries['launchers'])}")
if disabledEntries["launchers"]:
print(" " + ", ".join(disabledEntries["launchers"]))
print("Critical checks:")
for key in criticalMismatches:
values = mismatches[key]
print(f" {key}: {len(values)}")
if values:
print(" " + ", ".join(values))
print("Warnings:")
for key in warningMismatches:
values = mismatches[key]
print(f" {key}: {len(values)}")
if values:
print(" " + ", ".join(values))
criticalTotal = sum(len(mismatches[key]) for key in criticalMismatches)
if criticalTotal == 0:
print("Result: OK (no critical mismatches)")
else:
print("Result: ATTENTION REQUIRED (critical mismatches detected)")
def parse_args() -> argparse.Namespace:
defaultRepoRoot = Path(__file__).resolve().parents[4]
parser = argparse.ArgumentParser(
description="Audit installer/launcher/update consistency for linux-game-manager."
)
parser.add_argument(
"--repo-root",
default=str(defaultRepoRoot),
help=f"Repository root path (default: {defaultRepoRoot})",
)
parser.add_argument(
"--json",
action="store_true",
help="Emit JSON instead of human-readable text",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
repoRoot = Path(args.repo_root).resolve()
try:
report = build_report(repoRoot)
except FileNotFoundError as error:
print(str(error), file=sys.stderr)
return 2
if args.json:
print(json.dumps(report, indent=2, sort_keys=True))
else:
print_human_report(report)
criticalTotal = sum(
len(report["mismatches"][bucket]) for bucket in report["criticalMismatches"]
)
return 1 if criticalTotal else 0
if __name__ == "__main__":
raise SystemExit(main())
+1 -1
View File
@@ -1,3 +1,3 @@
check_dependencies git sox tt++ check_dependencies git sox tt++
git -C "${installPath}/" clone --recurse-submodules https://github.com/lilmike/tintin-alteraeon.git | \ git -C "${installPath}/" clone --recurse-submodules https://github.com/lilmike/tintin-alteraeon.git | \
dialog --progressbox "Installing \"${game}\", please wait..." -1 -1 ui_progressbox "Game Installer" "Installing \"${game}\", please wait..."
+9
View File
@@ -0,0 +1,9 @@
check_architecture x86_64
get_installer "christmas_dash_linux.zip" "https://tunmi13.itch.io/christmas-dash"
unzip -d "${installPath}" "${cache}/christmas_dash_linux.zip"
if [[ -d "${installPath}/Christmas Dash/christmas_dash_linux" ]]; then
rm -rf "${installPath}/christmas_dash_linux"
mv -v "${installPath}/Christmas Dash/christmas_dash_linux" "${installPath}/christmas_dash_linux"
rmdir "${installPath}/Christmas Dash" 2> /dev/null || true
fi
chmod +x "${installPath}/christmas_dash_linux/game.bin"
+7
View File
@@ -0,0 +1,7 @@
check_architecture x86_64
installPath="${installPath:-${HOME}/.local/games}"
cache="${cache:-${XDG_CACHE_HOME:-$HOME/.cache}/linux-game-manager}"
download "https://samtupy.com/games/cm.tar.gz"
mkdir -p "${installPath}/Constant Motion"
tar -xzf "${cache}/cm.tar.gz" -C "${installPath}/Constant Motion"
chmod +x "${installPath}/Constant Motion/cm"
+1 -1
View File
@@ -1,3 +1,3 @@
check_dependencies git sox tt++ check_dependencies git sox tt++
git -C "${installPath}/" clone --recurse-submodules https://github.com/lilmike/tintin-empiremud.git | \ git -C "${installPath}/" clone --recurse-submodules https://github.com/lilmike/tintin-empiremud.git | \
dialog --progressbox "Installing \"${game}\", please wait..." -1 -1 ui_progressbox "Game Installer" "Installing \"${game}\", please wait..."
+1 -1
View File
@@ -1,3 +1,3 @@
check_dependencies git opusdec sox tt++ check_dependencies git opusdec sox tt++
git -C "${installPath}/" clone https://git.2mb.codes/~stormdragon2976/tintin-endoftime | \ git -C "${installPath}/" clone https://git.2mb.codes/~stormdragon2976/tintin-endoftime | \
dialog --progressbox "Installing \"${game}\", please wait..." -1 -1 ui_progressbox "Game Installer" "Installing \"${game}\", please wait..."
+13 -26
View File
@@ -1,9 +1,8 @@
tobyVersion="8-0" tobyVersion="9.0"
mkdir -p "${installPath}/doom" mkdir -p "${installPath}/doom"
doomPath="$(find /usr/share -type d -name "doom" 2> /dev/null)" doomPath="$(find /usr/share -type d -name "doom" 2> /dev/null)"
if [[ ${#doomPath} -lt 5 ]]; then if [[ ${#doomPath} -lt 5 ]]; then
dialog --backtitle "Linux Game Manager" \ ui_yesno "Linux Game Manager" "Linux Game Manager" "Do you want Linux Game Manager to install freedoom and gzdoom for you? If you want to do this manually, select no." || exit 0
--yesno "Do you want Linux Game Manager to install freedoom and gzdoom for you? If you want to do this manually, select no." -1 -1 --stdout || exit 0
if command -v yay &> /dev/null ; then if command -v yay &> /dev/null ; then
yay -Sy --noconfirm --sudoloop freedoom gzdoom freedm blasphemer-wad yay -Sy --noconfirm --sudoloop freedoom gzdoom freedm blasphemer-wad
elif command -v slapt-src &> /dev/null ; then elif command -v slapt-src &> /dev/null ; then
@@ -13,37 +12,25 @@ if [[ ${#doomPath} -lt 5 ]]; then
sudo dnf -q -y install freedoom sudo dnf -q -y install freedoom
sudo dnf -q -y install gzdoom sudo dnf -q -y install gzdoom
else else
dialog --backtitle "Linux Game Manager" --msgbox "No supported package managers found. Please install the freedoom and gzdoom packages manually." -1 -1 ui_msgbox "Linux Game Manager" "Linux Game Manager" "No supported package managers found. Please install the freedoom and gzdoom packages manually."
exit 0 exit 0
fi fi
fi fi
doomPath="$(find /usr/share -type d -name "doom" 2> /dev/null | head -1)" doomPath="$(find /usr/share -type d -name "doom" 2> /dev/null | head -1)"
if ! [[ -e "${installPath}/doom/DoomMetalVol6.wad" ]] && ! [[ -e "${installPath}/doom/DoomMetalVol7.wad" ]]; then
alert
dialog --backtitle "Linux Game manager" \
--extra-button \
--yes-label "6" \
--no-label "7" \
--extra-label "None" \
--yesno "Would you like Doom Metal Volume 6 or 7? Use arrow keys to select your answer." \
-1 -1 --stdout
buttonCode=$?
if [[ $buttonCode -eq 0 ]]; then
download "${ipfsGateway}/ipfs/QmSzWKtP3wPvzn5GNd9F7n4RAhkFHxh2UHxXGefiAufwQW?filename=DoomMetalVol6.wad"
fi
if [[ $buttonCode -eq 1 ]]; then
download "${ipfsGateway}/ipfs/QmfXkz3tzicKGfhcYSiWUZkjkDKP2aVp53Y49n127wMr7D?filename=DoomMetalVol7.wad"
fi
fi
# The url breaks the normal download function # The url breaks the normal download function
download_named "keyshare-universal.pk3" "https://forum.zdoom.org/download/file.php?id=42262" download_named "keyshare-universal.pk3" "https://forum.zdoom.org/download/file.php?id=42262"
download "${ipfsGateway}/ipfs/QmRSyAGQPaRtWwH9aT87Q8rKini9GYXac92B9EkBZJtbJ1?filename=TobyAccessibilityMod_V${tobyVersion}.zip" download "${ipfsGateway}/ipfs/QmU6REFot8CGVSBy2YWQxnmXoh7M4EF74RxFtLzvtJxoGC?filename=toby_doom_${tobyVersion}_full.zip"
[[ -e "${cache}/DoomMetalVol6.wad" ]] && mv "${cache}/DoomMetalVol6.wad" "${installPath}/doom" unzip -n -d "${installPath}/doom" "${cache}/toby_doom_${tobyVersion}_full.zip"
[[ -e "${cache}/DoomMetalVol7.wad" ]] && mv "${cache}/DoomMetalVol7.wad" "${installPath}/doom" # Move contents from nested folder up one directory (including hidden files)
unzip -n -d "${installPath}/doom" "${cache}/TobyAccessibilityMod_V${tobyVersion}.zip" shopt -s dotglob
mv "${installPath}/doom/TobyAccessibilityMod_Version9"*/* "${installPath}/doom/"
shopt -u dotglob
# Remove the now-empty nested folder
rmdir "${installPath}/doom/TobyAccessibilityMod_Version9"*
#unzip -n -d "${installPath}/doom" "${cache}/OpMDK_ForV${tobyVersion}.zip" #unzip -n -d "${installPath}/doom" "${cache}/OpMDK_ForV${tobyVersion}.zip"
cp -v "${cache}/keyshare-universal.pk3" "${installPath}/doom" cp -v "${cache}/keyshare-universal.pk3" "${installPath}/doom"
rm -fv "${installPath}/doom/"*.{ahk,bat,exe,dll,ps1} rm -fv "${installPath}/doom/"*.{bat,exe,dll,ps1}
rm -rfv "${installPath}/doom/_internal"
#if [[ -e /usr/share/doom/blasphem.wad ]]; then #if [[ -e /usr/share/doom/blasphem.wad ]]; then
#ln -s /usr/share/doom/blasphem.wad "${installPath}/doom/" #ln -s /usr/share/doom/blasphem.wad "${installPath}/doom/"
#fi #fi
+1 -1
View File
@@ -1,3 +1,3 @@
check_dependencies git sox tt++ check_dependencies git sox tt++
git -C "${installPath}/" clone --recurse-submodules https://github.com:hjozwiak/tintin-kallisti-pack | \ git -C "${installPath}/" clone --recurse-submodules https://github.com:hjozwiak/tintin-kallisti-pack | \
dialog --progressbox "Installing \"${game}\", please wait..." -1 -1 ui_progressbox "Game Installer" "Installing \"${game}\", please wait..."
+69
View File
@@ -0,0 +1,69 @@
check_dependencies git uv python-speechd:speechd
# shellcheck disable=SC2154 # set by linux-game-manager.sh
gameRoot="${installPath}/PlayPalace11"
desktopPath="${gameRoot}/clients/desktop"
hostPythonDir="${desktopPath}/.host-python"
speechdFilePath=""
speechdCopySourcePath=""
if [[ -e "${gameRoot}" ]]; then
ui_msgbox "Game Installer" "Game Installer" "\"${gameRoot}\" already exists. Remove it first or choose a different game."
exit 1
fi
mkdir -p "${installPath}" || {
ui_msgbox "Game Installer" "Game Installer" "Could not create ${installPath}."
exit 1
}
if ! git -C "${installPath}" clone "https://github.com/XGDevGroup/PlayPalace11.git"; then
ui_msgbox "Game Installer" "Game Installer" "Could not clone Play Palace from GitHub."
exit 1
fi
if ! pushd "${desktopPath}" > /dev/null; then
ui_msgbox "Game Installer" "Game Installer" "Could not find desktop client path at ${desktopPath}."
exit 1
fi
if ! uv sync; then
popd > /dev/null || exit 1
ui_msgbox "Game Installer" "Game Installer" "uv sync failed for Play Palace."
exit 1
fi
popd > /dev/null || exit 1
speechdFilePath="$(python3 -c 'import pathlib,speechd; print(pathlib.Path(speechd.__file__).resolve())' 2> /dev/null)"
if [[ -z "${speechdFilePath}" ]]; then
ui_msgbox "Game Installer" "Game Installer" "Could not import python module speechd from host Python.\nInstall speech-dispatcher python bindings and try again."
exit 1
fi
if [[ "${speechdFilePath##*/}" == "__init__.py" ]]; then
speechdCopySourcePath="${speechdFilePath%/*}"
elif [[ "${speechdFilePath##*/}" == "speechd.py" ]]; then
speechdCopySourcePath="${speechdFilePath}"
else
speechdCopySourcePath="${speechdFilePath}"
fi
if ! [[ -r "${speechdCopySourcePath}" ]]; then
ui_msgbox "Game Installer" "Game Installer" "Could not read speechd python binding at ${speechdCopySourcePath}."
exit 1
fi
mkdir -p "${hostPythonDir}" || {
ui_msgbox "Game Installer" "Game Installer" "Could not create ${hostPythonDir}."
exit 1
}
if [[ -d "${speechdCopySourcePath}" ]]; then
if ! cp -a "${speechdCopySourcePath}" "${hostPythonDir}/"; then
ui_msgbox "Game Installer" "Game Installer" "Could not copy speechd package into ${hostPythonDir}."
exit 1
fi
elif ! cp -a "${speechdCopySourcePath}" "${hostPythonDir}/speechd.py"; then
ui_msgbox "Game Installer" "Game Installer" "Could not copy speechd module into ${hostPythonDir}."
exit 1
fi
+29 -39
View File
@@ -1,47 +1,34 @@
# shellcheck shell=bash disable=SC2154 # installPath and cache are set by linux-game-manager.sh
check_architecture x86_64 check_architecture x86_64
alert alert "Game Installer" "Game Installer" "Please note this requires the game to be available either in your Steam library or as the installer purchased from gog.com.\n\nIf using the gog.com installer, please use the default path when prompted."
dialog --backtitle "Linux Game manager" \ installMethod=$(ui_menu "Installation Method" "Installation Method" "Select installation method" "gog" "GOG" "steam" "Steam")
--msgbox \
"Please note this requires the game to be available either in your Steam library\nor as the installer purchased from gog.com.\nIf using the gog.com installer, please use the default path when prompted." -1 -1 --stdout
dialog --backtitle "Linux Game Manager" \
--yes-label "GOG" \
--no-label "Steam" \
--yesno "Select installation method" -1 -1 --stdout
installationMethod=$?
appId="646570" appId="646570"
gogFileName="slay_the_spire_2020_12_15_8735c9fe3cc2280b76aa3ec47c953352a7df1f65_43444.sh" gogFileName="slay_the_spire_2020_12_15_8735c9fe3cc2280b76aa3ec47c953352a7df1f65_43444.sh"
if [[ $installMethod -eq 1 ]]; then if [[ "$installMethod" == "steam" ]]; then
# Steam Installation # Steam Installation
check_dependencies steamcmd check_dependencies steamcmd
echo "Please enter Steam user name:" steamUser=$(ui_inputbox "Game Installer" "Game Installer" "Please enter Steam user name:" "")
read -er steamUser if ! steamcmd +@sSteamCmdForcePlatformType linux +force_install_dir "${HOME}/.local/games/SlayTheSpire" +login "$steamUser" +app_update "$appId" +quit; then
steamcmd +@sSteamCmdForcePlatformType linux +force_install_dir "${HOME}/.local/games/SlayTheSpire" +login "$steamUser" +app_update "$appId" +quit ui_msgbox "Game Installer" "Game Installer" "Error installing game through Steam."
if [[ $? -ne 0 ]]; then
dialog --backtitle "Linux Game Manager" \
--msgbox "Error installing game through Steam." -1 -1 --stdout
exit 1 exit 1
fi fi
else else
# GOG Installation # GOG Installation
get_installer "$gogFileName" "https://www.gog.com/en/game/slay_the_spire" get_installer "$gogFileName" "https://www.gog.com/en/game/slay_the_spire"
DISPLAY="" find ~/Downloads -maxdepth 1 -type f -name "$gogFileName" -exec bash "{}" \; || if DISPLAY="" find ~/Downloads -maxdepth 1 -type f -name "$gogFileName" -exec bash "{}" \; ||
DISPLAY="" find ~/Desktop -maxdepth 1 -type f -name "$gogFileName" -exec bash "{}" \; DISPLAY="" find ~/Desktop -maxdepth 1 -type f -name "$gogFileName" -exec bash "{}" \;; then
if [[ $? -eq 0 ]]; then
ln -sf "${HOME}/GOG Games/Slay the Spire/game" "${installPath}/SlayTheSpire" || ln -sf "${HOME}/GOG Games/Slay the Spire/game" "${installPath}/SlayTheSpire" ||
{ dialog --backtitle "Linux Game Manager" \ { ui_msgbox "Game Installer" "Game Installer" "Error creating link to game directory."
--msgbox "Error creating link to game directory." -1 -1 --stdout
exit 1; } exit 1; }
else else
dialog --backtitle "Linux Game Manager" \ ui_msgbox "Game Installer" "Game Installer" "Error installing game from GOG installer."
--msgbox "Error installing game from GOG installer." -1 -1 --stdout
exit 1 exit 1
fi fi
fi fi
# Move files into place # Move files into place
mkdir -p "${HOME}/.config/ModTheSpire" mkdir -p "${HOME}/.config/ModTheSpire"
if [[ -f ~"/.config/ModTheSpire/mod_lists.json" ]]; then if [[ -f ~"/.config/ModTheSpire/mod_lists.json" ]]; then
dialog --backtitle "Linux Game manager" \ ui_yesno "Game Installer" "Game Installer" "Existing mod_lists.json file found. Would you like to replace it?" &&
--yesno "Existing mod_lists.json file found. Would you like to replace it?" -1 -1 --stdout &&
cp -v .files/SlayTheSpire/mod_lists.json "${HOME}/.config/ModTheSpire/mod_lists.json" cp -v .files/SlayTheSpire/mod_lists.json "${HOME}/.config/ModTheSpire/mod_lists.json"
else else
cp -v .files/SlayTheSpire/mod_lists.json "${HOME}/.config/ModTheSpire/mod_lists.json" cp -v .files/SlayTheSpire/mod_lists.json "${HOME}/.config/ModTheSpire/mod_lists.json"
@@ -56,26 +43,29 @@ declare -A modsMap=(
[achievement enabler]=1692554109 [achievement enabler]=1692554109
[say the spire]=2239220106 [say the spire]=2239220106
) )
installString="" declare -a installArgs=()
for modId in ${modsMap[@]} ; do for modId in "${modsMap[@]}" ; do
installString="$installString +workshop_download_item $appId $modId" installArgs+=("+workshop_download_item" "$appId" "$modId")
done done
steamcmd +@sSteamCmdForcePlatformType linux +force_install_dir "${HOME}/.local/games/SlayTheSpire/" +login anonymous $installString +quit if ! steamcmd +@sSteamCmdForcePlatformType linux +force_install_dir "${HOME}/.local/games/SlayTheSpire/" +login anonymous "${installArgs[@]}" +quit; then
if [[ $? -ne 0 ]]; then ui_msgbox "Game Installer" "Game Installer" "Error installing required mods. Some accessibility features may not be available."
dialog --backtitle "Linux Game Manager" \
--msgbox "Error installing required mods. Some accessibility features may not be available." -1 -1 --stdout
exit 1 exit 1
fi fi
mkdir -p "$HOME/.local/games/SlayTheSpire/mods" mkdir -p "$HOME/.local/games/SlayTheSpire/mods"
for modName in "${!modsMap[@]}" ; do for modName in "${!modsMap[@]}" ; do
if [[ "$modName" == "mod the spire" ]]; then if [[ "$modName" == "mod the spire" ]]; then
ln -sr "$HOME/.local/games/SlayTheSpire/steamapps/workshop/content/$appId/${modsMap[$modName]}"/* "$HOME/.local/games/SlayTheSpire/" if ! ln -sr "$HOME/.local/games/SlayTheSpire/steamapps/workshop/content/$appId/${modsMap[$modName]}"/* "$HOME/.local/games/SlayTheSpire/"; then
if [[ "$modName" == "say the spire" ]]; then
ui_msgbox "Game Installer" "Game Installer" "Error installing SayTheSpire mod. Screen reader support will not be available."
exit 1
fi
fi
else else
ln -sr "$HOME/.local/games/SlayTheSpire/steamapps/workshop/content/$appId/${modsMap[$modName]}"/* "$HOME/.local/games/SlayTheSpire/mods/" if ! ln -sr "$HOME/.local/games/SlayTheSpire/steamapps/workshop/content/$appId/${modsMap[$modName]}"/* "$HOME/.local/games/SlayTheSpire/mods/"; then
fi if [[ "$modName" == "say the spire" ]]; then
if [[ $? -ne 0 ]] && [[ "$modName" == "say the spire" ]]; then ui_msgbox "Game Installer" "Game Installer" "Error installing SayTheSpire mod. Screen reader support will not be available."
dialog --backtitle "Linux Game Manager" \ exit 1
--msgbox "Error installing SayTheSpire mod. Screen reader support will not be available." -1 -1 --stdout fi
exit 1 fi
fi fi
done done
+1 -1
View File
@@ -1,3 +1,3 @@
check_dependencies python-ansimarkup:ansimarkup check_dependencies python-ansimarkup:ansimarkup
git -C "${installPath}/" clone https://github.com/Difio3333/slaythetext.git | \ git -C "${installPath}/" clone https://github.com/Difio3333/slaythetext.git | \
dialog --progressbox "Installing \"${game}\", please wait..." -1 -1 ui_progressbox "Game Installer" "Installing \"${game}\", please wait..."
+4 -6
View File
@@ -1,18 +1,16 @@
# shellcheck shell=bash disable=SC2154 # installPath and cache are set by linux-game-manager.sh
check_architecture x86_64 check_architecture x86_64
DISPLAY="" DISPLAY=""
echo "Please select the default path when prompted by the installer." alert "Game Installer" "Game Installer" "Please select the default path when prompted by the installer."
alert
get_installer "stardew_valley_1_5_6_1988831614_53040.sh" "https://www.gog.com/game/stardew_valley" get_installer "stardew_valley_1_5_6_1988831614_53040.sh" "https://www.gog.com/game/stardew_valley"
bash "${cache}/stardew_valley_1_5_6_1988831614_53040.sh" || bash "${cache}/stardew_valley_1_5_6_1988831614_53040.sh" ||
{ echo "Error installing game." { ui_msgbox "Game Installer" "Game Installer" "Error installing game."
exit 1; } exit 1; }
smapiVersion="3.18.4" smapiVersion="3.18.4"
download "https://github.com/Pathoschild/SMAPI/releases/download/${smapiVersion}/SMAPI-${smapiVersion}-installer.zip" "https://stormgames.wolfe.casa/downloads/stardew-valley/Mods.tar.xz" download "https://github.com/Pathoschild/SMAPI/releases/download/${smapiVersion}/SMAPI-${smapiVersion}-installer.zip" "https://stormgames.wolfe.casa/downloads/stardew-valley/Mods.tar.xz"
smapiTmp="$(mktemp -d)" smapiTmp="$(mktemp -d)"
unzip "${cache}/SMAPI-${smapiVersion}-installer.zip" -d "$smapiTmp" unzip "${cache}/SMAPI-${smapiVersion}-installer.zip" -d "$smapiTmp"
echo "Preparing to install mods. Please select the same path as before when prompted." alert "Game Installer" "Game Installer" "Preparing to install mods. Please select the same path as before when prompted.\n\nWhen it says SMAPI has been installed, press enter to finish installation."
echo "When it says SMAPI has been installed, press enter to finish installation."
alert
bash "${smapiTmp}/SMAPI ${smapiVersion} installer/install on Linux.sh" bash "${smapiTmp}/SMAPI ${smapiVersion} installer/install on Linux.sh"
ln -sf "${HOME}/GOG Games/Stardew Valley/game" "${installPath}/StardewValley" ln -sf "${HOME}/GOG Games/Stardew Valley/game" "${installPath}/StardewValley"
tar -xvf "${cache}/Mods.tar.xz" -C "${installPath}/StardewValley/" tar -xvf "${cache}/Mods.tar.xz" -C "${installPath}/StardewValley/"
+1 -1
View File
@@ -1,3 +1,3 @@
check_dependencies git sox tt++ check_dependencies git sox tt++
git -C "${installPath}/" clone --recurse-submodules https://github.com/stormdragon2976/tintin-stickmud.git | \ git -C "${installPath}/" clone --recurse-submodules https://github.com/stormdragon2976/tintin-stickmud.git | \
dialog --progressbox "Installing \"${game}\", please wait..." -1 -1 ui_progressbox "Game Installer" "Installing \"${game}\", please wait..."
+4
View File
@@ -0,0 +1,4 @@
# shellcheck shell=bash disable=SC2154 # installPath and cache are set by linux-game-manager.sh
check_architecture x86_64
download "https://l-works.net/files/SuperLiam_linux.tar.gz"
tar -xzf "$cache/SuperLiam_linux.tar.gz" -C "${installPath}"
+4
View File
@@ -0,0 +1,4 @@
# shellcheck shell=bash disable=SC2154 # installPath and cache are set by linux-game-manager.sh
check_architecture x86_64
download "https://l-works.net/files/tgtr_linux.tar.gz"
tar -xzf "$cache/tgtr_linux.tar.gz" -C "${installPath}"
+7
View File
@@ -0,0 +1,7 @@
# shellcheck shell=bash disable=SC2154 # installPath and cache are set by linux-game-manager.sh
check_architecture x86_64
get_installer "the-omega-reach-linux-x64.zip" "https://shiftbacktick.itch.io/the-omega-reach"
mkdir -p "${installPath}/Omega Reach"
unzip -d "${installPath}/Omega Reach" "${cache}/the-omega-reach-linux-x64.zip"
chmod +x "${installPath}/Omega Reach/the-omega-reach"
chmod +x "${installPath}/Omega Reach/chrome-sandbox"
+2 -4
View File
@@ -1,8 +1,6 @@
# shellcheck shell=bash disable=SC2154 # installPath and cache are set by linux-game-manager.sh
check_architecture x86_64 check_architecture x86_64
get_installer "Ttc v3.2 linux.zip" "https://renzivan.itch.io/the-tornado-chicken" get_installer "Ttc v3.2 linux.zip" "https://renzivan.itch.io/the-tornado-chicken"
unzip -d "${installPath}/The Tornado Chicken" "${cache}/Ttc v3.2 linux.zip" unzip -d "${installPath}/The Tornado Chicken" "${cache}/Ttc v3.2 linux.zip"
chmod +x "${installPath}/The Tornado Chicken/Ttc" chmod +x "${installPath}/The Tornado Chicken/Ttc"
echo "Note: When the game first starts it will be playing very loud music with the built in suboptimal voice." alert "Game Installer" "Game Installer" "Note: When the game first starts it will be playing very loud music with the built in suboptimal voice.\n\nYou are recommended to press page down several times to turn down the music, then go to settings and enable speech with screen reader, using speech dispatcher.\n\nYou may then press page up to set the music to your liking."
echo "You are recommended to press page down several times to turn down the music, then go to settings and enable speech with screen reader, using speech dispatcher."
echo "You may then press page up to set the music to your liking."
alert
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# shellcheck disable=SC2154 # set by linux-game-manager.sh
check_architecture x86_64
releaseMetadataFile="TopSpeed-release-build.json"
releaseMetadataPath="${cache}/${releaseMetadataFile}"
releaseMetadataUrl="https://api.github.com/repos/diamondStar35/top_speed/releases/tags/release-build"
assetPattern='^TopSpeed-linux-x64-Release-v-.*\.zip$'
rm -f "${releaseMetadataPath}"
download_named "${releaseMetadataFile}" "${releaseMetadataUrl}"
assetName="$(jq -r --arg assetPattern "${assetPattern}" '.assets[] | select(.name | test($assetPattern)) | .name' "${releaseMetadataPath}" | head -n 1)"
assetUrl="$(jq -r --arg assetPattern "${assetPattern}" '.assets[] | select(.name | test($assetPattern)) | .browser_download_url' "${releaseMetadataPath}" | head -n 1)"
if [[ -z "${assetName}" ]] || [[ -z "${assetUrl}" ]] || [[ "${assetName}" == "null" ]] || [[ "${assetUrl}" == "null" ]]; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "Unable to find the latest Top Speed Linux download in the GitHub release metadata."
exit 1
fi
download_named "${assetName}" "${assetUrl}"
mkdir -p "${installPath}/TopSpeed"
unzip -o -d "${installPath}/TopSpeed" "${cache}/${assetName}"
chmod +x "${installPath}/TopSpeed/TopSpeed"
+2 -2
View File
@@ -1,7 +1,7 @@
# shellcheck shell=bash disable=SC2154 # installPath and cache are set by linux-game-manager.sh
check_architecture x86_64 check_architecture x86_64
check_dependencies dmidecode check_dependencies dmidecode
get_installer "upheaval-linux.zip" "https://leonegaming.itch.io/upheaval" get_installer "upheaval-linux.zip" "https://leonegaming.itch.io/upheaval"
mkdir -p "${installPath}/Upheaval_Gui" mkdir -p "${installPath}/Upheaval_Gui"
unzip -d "${installPath}/Upheaval_Gui" "${cache}/upheaval-linux.zip" unzip -d "${installPath}/Upheaval_Gui" "${cache}/upheaval-linux.zip"
echo "To enable accessibility, press shift t when the game starts." alert "Game Installer" "Game Installer" "To enable accessibility, press shift t when the game starts."
alert
+2 -4
View File
@@ -1,8 +1,6 @@
# shellcheck shell=bash disable=SC2154 # installPath and cache are set by linux-game-manager.sh
check_architecture x86_64 check_architecture x86_64
get_installer "Wp v3.0 linux.zip" "https://psyra-productions.itch.io/wheels-of-prio" get_installer "Wp v3.0 linux.zip" "https://psyra-productions.itch.io/wheels-of-prio"
unzip -d "${installPath}/Wheels of Prio" "${cache}/Wp v3.0 linux.zip" unzip -d "${installPath}/Wheels of Prio" "${cache}/Wp v3.0 linux.zip"
chmod +x "${installPath}/Wheels of Prio/Wp" chmod +x "${installPath}/Wheels of Prio/Wp"
echo "Note: When the game first starts it will be playing very loud music with the built in suboptimal voice." alert "Game Installer" "Game Installer" "Note: When the game first starts it will be playing very loud music with the built in suboptimal voice.\n\nYou are recommended to press page down several times to turn down the music, then go to settings and enable speech with screen reader, using speech dispatcher.\n\nYou may then press page up to set the music to your liking."
echo "You are recommended to press page down several times to turn down the music, then go to settings and enable speech with screen reader, using speech dispatcher."
echo "You may then press page up to set the music to your liking."
alert
+12
View File
@@ -0,0 +1,12 @@
local arch=$(uname -m)
local gameName
if [[ "$arch" == "aarch64" ]]; then
gameName="wicked_quest-linux-aarch64.zip"
else
gameName="wicked_quest-linux-x86_64.zip"
fi
get_installer "$gameName" "https://stormdragon2976.itch.io/wicked-quest"
mkdir -p "${installPath}/Wicked_Quest"
unzip -d "${installPath}/" "${cache}/$gameName"
+1 -1
View File
@@ -4,7 +4,7 @@ if [[ "${architecture}" == "aarch64" ]]; then
elif [[ "${architecture}" == "x86_64" ]]; then elif [[ "${architecture}" == "x86_64" ]]; then
pkgname="Zombowl-Linux-X86_64.zip" pkgname="Zombowl-Linux-X86_64.zip"
else else
echo "Architecture ${architecture} is not supported." ui_msgbox "Game Installer" "Game Installer" "Architecture ${architecture} is not supported."
exit 1 exit 1
fi fi
get_installer "${pkgname}" "https://stormdragon2976.itch.io/zombowl" get_installer "${pkgname}" "https://stormdragon2976.itch.io/zombowl"
+14
View File
@@ -0,0 +1,14 @@
gameDir="${installPath}/christmas_dash_linux"
gameExecutable="${gameDir}/game.bin"
if [[ ! -d "$gameDir" ]]; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "Christmas Dash is not installed."
return 1
fi
if [[ ! -x "$gameExecutable" ]]; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "Unable to find ${gameExecutable}."
return 1
fi
pushd "${gameDir}"
SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \
REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \
"${gameExecutable}"
+4
View File
@@ -0,0 +1,4 @@
check_architecture x86_64
gameExecutable="${installPath}/Constant Motion/cm"
pushd "${installPath}/Constant Motion"
${fex}"${gameExecutable}"
+1
View File
@@ -0,0 +1 @@
open_url "https://play.oriolgomez.com/"
+18
View File
@@ -0,0 +1,18 @@
check_dependencies uv
# shellcheck disable=SC2154 # set by linux-game-manager.sh
gamePath="${installPath}/PlayPalace11/clients"
desktopPath="${gamePath}/desktop"
hostPythonDir="${desktopPath}/.host-python"
if ! [[ -d "${desktopPath}" ]]; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "Play Palace is not installed at ${desktopPath}."
exit 1
fi
if [[ -d "${hostPythonDir}" ]]; then
export PYTHONPATH="${hostPythonDir}${PYTHONPATH:+:${PYTHONPATH}}"
fi
pushd "${desktopPath}" || exit 1
uv run python client.py
+4
View File
@@ -0,0 +1,4 @@
check_architecture x86_64
gamePath="${installPath}/Super Liam"
pushd "${gamePath}"
${fex}./SuperLiam
+4
View File
@@ -0,0 +1,4 @@
check_architecture x86_64
gamePath="${installPath}/The Great Toy Robbery"
pushd "${gamePath}"
${fex}./tgtr
+4
View File
@@ -0,0 +1,4 @@
# shellcheck shell=bash disable=SC2154 # installPath is set by linux-game-manager.sh
gamePath="${installPath}/Omega Reach"
pushd "${gamePath}" || return 1
"${gamePath}/the-omega-reach"
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
# shellcheck disable=SC2154 # set by linux-game-manager.sh
gameExecutable="${installPath}/TopSpeed/TopSpeed"
gameDir="${gameExecutable%/*}"
if [[ ! -d "$gameDir" ]]; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "Top Speed is not installed."
return 1
fi
if [[ ! -x "$gameExecutable" ]]; then
ui_msgbox "Linux Game Manager" "Linux Game Manager" "Unable to find ${gameExecutable}."
return 1
fi
pushd "${gameDir}" || return 1
"${gameExecutable}"
+7
View File
@@ -0,0 +1,7 @@
local arch=$(uname -m)
if [[ "$arch" == "aarch64" ]]; then
pushd "${installPath}/wicked_quest-linux-aarch64"
else
pushd "${installPath}/wicked_quest-linux-x86_64"
fi
./wicked_quest
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
run_update() {
local installPath="${HOME}/.local/games"
local arch
arch=$(uname -m)
local gameName
local cache="${HOME}/.cache/lgm"
if [[ "$arch" == "aarch64" ]]; then
gameName="wicked_quest-linux-aarch64.zip"
else
gameName="wicked_quest-linux-x86_64.zip"
fi
# Look for the game file in common download locations
for i in ~/Downloads ~/Desktop ; do
find $i -type f -name "$gameName" -exec mv -v {} "${cache}/" \;
done
# If the file is still not available abort.
if [[ ! -f "${cache}/$gameName" ]]; then
dialog --backtitle "Linux Game Manager" --msgbox "Couldn't find $gameName. Please download the file from https://stormdragon2976.itch.io/wicked-quest and try again." -1 -1
exit 1
fi
unzip -od "${installPath}/" "${cache}/$gameName"
}
+9
View File
@@ -7,6 +7,15 @@ Installer and launcher for accessible to the blind games that run natively in Li
As the project grows, more dependencies will most likely be added. When LGM is launched, it will check to make sure it has the required dependencies for it to do the work of downloading and extracting the game. Note that because every distro has a different package manager, dependencies for games themselves will not be resolved. Please consult the game's documentation to find out what is needed for the game to run. As the project grows, more dependencies will most likely be added. When LGM is launched, it will check to make sure it has the required dependencies for it to do the work of downloading and extracting the game. Note that because every distro has a different package manager, dependencies for games themselves will not be resolved. Please consult the game's documentation to find out what is needed for the game to run.
Current base dependencies checked on launch: `7z`, `curl`, `dialog`, `jq`, `yad`, and `unzip`.
## Disabling game entries
To disable a game entry without deleting its script, set the first line of the script to start with `#//`.
- This applies to both `.install/*.sh` and `.launch/*.sh`.
- A normal comment like `# comment` on the first line does not disable an entry.
## FAQ ## FAQ
+558 -167
View File
@@ -1,5 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
scriptRoot="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
license() { license() {
cat << EOF cat << EOF
■The contents of this file are subject to the Common Public Attribution ■The contents of this file are subject to the Common Public Attribution
@@ -44,28 +46,293 @@ EOF
# Dialog accessibility # Dialog accessibility
export DIALOGOPTS='--no-lines --visit-items' 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
echo -e "$text" | yad --text-info \
--title="$title" \
--show-cursor \
--button="OK:0" \
--width=600 \
--height=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 for updates
check_update() { check_update() {
local url="$(git ls-remote --get-url)" if ! [[ -d ".git" ]]; then
if [[ "$url" =~ ^[[:alnum:]]+@ ]] || [[ -z "$url" ]]; then
return return
fi fi
git remote update > /dev/null 2>&1 # Check if we're in a detached HEAD state
if ! git symbolic-ref -q HEAD &> /dev/null; 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 upstream='@{u}'
local home="$(git rev-parse @)" local home
local remote="$(git rev-parse "$upstream")" local remote
home="$(git rev-parse @)"
remote="$(git rev-parse "$upstream")"
if [[ "$home" == "$remote" ]]; then if [[ "$home" == "$remote" ]]; then
return return
fi fi
dialog --backtitle "Linux Game manager" \ ui_yesno "Linux Game Manager" "Linux Game Manager" "Updates are available. Would you like to update now?" || return
--yesno "Updates are available. Would you like to update now?" -1 -1 --stdout || return
# Store the current commit before pulling # Perform git pull quietly, then show only the important changes
local beforePull=$(git rev-parse HEAD) local update_result
git pull git pull --quiet >/dev/null 2>&1
# Show changes between the stored commit and current HEAD update_result=$?
git log "$beforePull..HEAD" --pretty=format:'%an: %s' | tac
exit $? # 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
} }
@@ -75,14 +342,14 @@ check_architecture() {
export fex="FEXLoader -- " export fex="FEXLoader -- "
return return
fi fi
local architecture="$(uname -m)" local architecture
architecture="$(uname -m)"
for i in "$@" ; do for i in "$@" ; do
if [[ "${architecture}" == "$i" ]]; then if [[ "${architecture}" == "$i" ]]; then
return return
fi fi
done done
dialog --backtitle "Linux Game Manager" \ ui_infobox "Linux Game Manager" "Linux Game Manager" "This game is not compatible with $architecture architecture."
--infobox "This game is not compatible with $architecture architecture." -1 -1
exit 1 exit 1
} }
@@ -101,11 +368,11 @@ check_dependencies() {
if [[ "${#dependencies[@]}" -eq 0 ]]; then if [[ "${#dependencies[@]}" -eq 0 ]]; then
return return
fi fi
echo "missing dependencies. Please install the following:" local missing_deps="missing dependencies. Please install the following:\n\n"
echo
for i in "${dependencies[@]}" ; do for i in "${dependencies[@]}" ; do
echo "$i" missing_deps+="$i\n"
done done
ui_msgbox "Linux Game Manager" "Linux Game Manager" "$missing_deps"
exit 1 exit 1
} }
@@ -124,10 +391,11 @@ terminal_emulator() {
return return
fi fi
done done
echo "No suitable terminal emulators found, please install one of:" local terminal_msg="No suitable terminal emulators found, please install one of:\n\n"
for i in "${terminals[@]}" ; do for i in "${terminals[@]}" ; do
echo "$i" terminal_msg+="$i\n"
done done
ui_msgbox "Linux Game Manager" "Linux Game Manager" "$terminal_msg"
} }
# Function to open urls # Function to open urls
@@ -139,10 +407,12 @@ open_url() {
desktop_launcher() { desktop_launcher() {
local desktopFile="${HOME}/linux-game-manager.desktop" local desktopFile="${HOME}/linux-game-manager.desktop"
if [[ -e "${desktopFile}" ]]; then if [[ -e "${desktopFile}" ]]; then
echo "the file ${desktopFile} exists. Cannot create the launcher." ui_msgbox "Linux Game Manager" "Linux Game Manager" "The file ${desktopFile} exists. Cannot create the launcher."
exit 1 exit 1
fi fi
local dotDesktop local dotDesktop
local execLine
local launchDirectory
local terminal local terminal
# Try to find an accessible terminal # Try to find an accessible terminal
for i in mate-terminal lxterminal terminator gnome-terminal ; do for i in mate-terminal lxterminal terminator gnome-terminal ; do
@@ -151,11 +421,13 @@ desktop_launcher() {
break break
fi fi
done done
launchDirectory="${scriptRoot}"
printf -v execLine "Exec=%s -t \"Linux Game Manager\" -e \"/usr/bin/bash -c 'pushd %q;nohup ./%q 2> /dev/null'\"" "${terminal}" "${launchDirectory}" "${0##*/}"
dotDesktop=('[Desktop Entry]' dotDesktop=('[Desktop Entry]'
'Name=Linux game manager' 'Name=Linux game manager'
'GenericName=Linux game Manager' 'GenericName=Linux game Manager'
'Comment=Install and launch games that are accessible to the blind' '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'\"" "${execLine}"
'Terminal=false' 'Terminal=false'
'Type=Application' 'Type=Application'
'StartupNotify=false' 'StartupNotify=false'
@@ -173,33 +445,101 @@ desktop_launcher() {
# Alerts, for when user needs to read something. # Alerts, for when user needs to read something.
alert() { alert() {
local title="Linux Game Manager"
local backTitle="Linux Game Manager"
local message="Press OK to continue."
if [[ $# -eq 1 ]]; then
message="$1"
elif [[ $# -eq 2 ]]; then
title="$1"
backTitle="$1"
message="$2"
elif [[ $# -ge 3 ]]; then
title="$1"
backTitle="$2"
shift 2
message="$*"
fi
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 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 ui_msgbox "${title}" "${backTitle}" "${message}"
read -rp "Press enter to continue." continue
} }
clear_cache() { clear_cache() {
local answer
if [[ ! -d "${cache}" ]]; then if [[ ! -d "${cache}" ]]; then
echo "No cache found at ${cache}." ui_msgbox "Linux Game Manager" "Linux Game Manager" "No cache found at ${cache}."
return return
fi fi
while ! [[ "${answer,,}" =~ ^yes$|^no$ ]]; do if ! ui_yesno "Linux Game Manager" "Linux Game Manager" "This will delete all contents of ${cache}. Are you sure you want to continue?"; then
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 return
fi fi
# All safety checks done. Delete the cache. # All safety checks done. Delete the cache.
rm -rfv "${cache}" rm -rfv "${cache}" | ui_progressbox "Linux Game Manager" "Clearing cache..."
echo "Cache deleted." ui_msgbox "Linux Game Manager" "Linux Game Manager" "Cache deleted."
}
download_with_fallback() {
local outputPath="$1"
local sourceUrl="$2"
local firstExitCode=0
local secondExitCode=0
local thirdExitCode=0
local wgetExitCode=0
local errorFile=""
downloadAttemptExitCodes=""
downloadErrorLog=""
if ! errorFile="$(mktemp)"; then
return 1
fi
if curl -L4 -C - --retry 10 --output "${outputPath}" "${sourceUrl}" 2> "${errorFile}"; then
rm -f "${errorFile}"
return 0
fi
firstExitCode=$?
# Some hosts reject resume requests. Retry from scratch.
rm -f "${outputPath}"
if curl -L4 --retry 10 --output "${outputPath}" "${sourceUrl}" 2>> "${errorFile}"; then
rm -f "${errorFile}"
return 0
fi
secondExitCode=$?
# If IPv4-only fails, retry allowing either IP family.
rm -f "${outputPath}"
if curl -L --retry 10 --output "${outputPath}" "${sourceUrl}" 2>> "${errorFile}"; then
rm -f "${errorFile}"
return 0
fi
thirdExitCode=$?
if command -v wget > /dev/null 2>&1; then
rm -f "${outputPath}"
if wget --tries=10 --output-document="${outputPath}" "${sourceUrl}" 2>> "${errorFile}"; then
rm -f "${errorFile}"
return 0
fi
wgetExitCode=$?
fi
downloadAttemptExitCodes="curl: ${firstExitCode}, ${secondExitCode}, ${thirdExitCode}"
if [[ "${wgetExitCode}" -ne 0 ]]; then
downloadAttemptExitCodes+="; wget: ${wgetExitCode}"
fi
if cp -f "${errorFile}" "${cache}/curl-error.log" 2> /dev/null; then
downloadErrorLog="${cache}/curl-error.log"
rm -f "${errorFile}"
else
downloadErrorLog="${errorFile}"
fi
return 1
} }
download() { download() {
local source=($@) local source=("$@")
for i in "${source[@]}" ; do for i in "${source[@]}" ; do
local dest="${i##*/}" local dest="${i##*/}"
dest="${dest//%20/ }" dest="${dest//%20/ }"
@@ -212,56 +552,69 @@ download() {
fi fi
# Skip if the item is in cache. # Skip if the item is in cache.
[[ -e "${cache}/${dest}" ]] && continue [[ -e "${cache}/${dest}" ]] && continue
{ if ! curl -L4 -C - --retry 10 --output "${cache}/${dest}" "${i}" ; then { if ! download_with_fallback "${cache}/${dest}" "${i}" ; then
echo "Could not download \"$i\"..." ui_msgbox "Linux Game Manager" "Linux Game Manager" "Could not download \"$i\"...\n\ndownload exit codes: ${downloadAttemptExitCodes}\nError details: ${downloadErrorLog}"
exit 1 exit 1
fi; } | dialog --backtitle "Linux Game Manager" \ fi; } | ui_progressbox "Linux Game Manager" "Downloading \"$dest\" from \"$i\""
--progressbox "Downloading \"$dest\" from \"$i\"" -1 -1 validate_downloaded_cache_file "${dest}"
local downloadError=1
case "${dest##*.}" in
"pk3"|"zip")
unzip -tq "${cache}/${dest}" | dialog --backtitle "Linux Game Manager" \
--progressbox "Validating ${dest##*.} file" -1 -1 --stdout
downloadError=$?
;;
"7z")
7z t "${cache}/${dest}" | dialog --backtitle "Linux 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
downloadError=1
fi
;;
esac
if [[ $downloadError -ne 0 ]]; then
rm -fv "${cache}/${dest}"
dialog --backtitle "Linux Game Manager" \
--infobox "Error downloading \"${dest}\". Installation cannot continue." -1 -1 --stdout
alert
exit 1
fi
done done
} }
validate_downloaded_cache_file() {
local dest="$1"
local downloadError=0
case "${dest##*.}" in
"pk3"|"zip")
if ! (
set -o pipefail
unzip -tq "${cache}/${dest}" | ui_progressbox "Linux Game Manager" "Validating ${dest##*.} file"
); then
downloadError=1
fi
;;
"7z")
if ! (
set -o pipefail
7z t "${cache}/${dest}" | ui_progressbox "Linux Game Manager" "Validating 7z file"
); then
downloadError=1
fi
;;
"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
;;
*)
if file -b "${cache}/${dest}" | grep -q "HTML document" ; then
downloadError=1
else
downloadError=0
fi
;;
esac
if [[ ${downloadError} -ne 0 ]]; then
rm -fv "${cache}/${dest}"
alert "Linux Game Manager" "Linux Game Manager" "Error downloading \"${dest}\". Installation cannot continue."
exit 1
fi
}
download_named() { download_named() {
# Only needed if url breaks the name, e.g. downloads/?filename=1234 # Only needed if url breaks the name, e.g. downloads/?filename=1234
# Required arguments: filename url # Required arguments: filename url
# Only works with one file at a time. # Only works with one file at a time.
local dest="$1" local dest="$1"
# Validate arguments
if [[ -z "$dest" ]] || [[ -z "$2" ]]; then
return 1
fi
# Remove the destination file if it is empty. # Remove the destination file if it is empty.
test -s "${cache}/${dest}" || rm -f "${cache}/${dest}" 2> /dev/null test -s "${cache}/${dest}" || rm -f "${cache}/${dest}" 2> /dev/null
if [[ "${redownload}" == "true" ]] && [[ -e "${cache}/${dest}" ]]; then if [[ "${redownload}" == "true" ]] && [[ -e "${cache}/${dest}" ]]; then
@@ -269,10 +622,11 @@ download_named() {
fi fi
# Skip if the item is in cache. # Skip if the item is in cache.
test -e "${cache}/${dest}" && return test -e "${cache}/${dest}" && return
if ! curl -L4 --output "${cache}/${dest}" "${2}" ; then if ! download_with_fallback "${cache}/${dest}" "${2}" ; then
echo "Could not download \"$dest\"..." ui_msgbox "Linux Game Manager" "Linux Game Manager" "Could not download \"$dest\"...\n\ndownload exit codes: ${downloadAttemptExitCodes}\nError details: ${downloadErrorLog}"
exit 1 exit 1
fi fi
validate_downloaded_cache_file "${dest}"
} }
get_installer() { get_installer() {
@@ -290,49 +644,49 @@ get_installer() {
if echo "$2" | xclip -selection clipboard 2> /dev/null ; then if echo "$2" | xclip -selection clipboard 2> /dev/null ; then
message+="\n\nThe URL has been copied to the clipboard." message+="\n\nThe URL has been copied to the clipboard."
fi fi
echo "Manual intervention required..." alert "Linux Game Manager" "Linux Game Manager" "$message"
alert
dialog --ok-label "Continue" \
--backtitle "Linux Game Manager" \
--msgbox "$message" -1 -1
# Search the Desktop and Downloads directories for the installation file # Search the Desktop and Downloads directories for the installation file
for i in ~/Downloads ~/Desktop ; do for i in ~/Downloads ~/Desktop ; do
find $i -type f -name "$1" -exec mv -v {} "${cache}/" \; find "$i" -type f -name "$1" -exec mv -v {} "${cache}/" \;
done done
# If the file is still not available abort. # If the file is still not available abort.
if [[ ! -f "${cache}/$1" ]]; then if [[ ! -f "${cache}/$1" ]]; then
echo "couldn't find $1. Please download the file and try again." ui_msgbox "Linux Game Manager" "Linux Game Manager" "Couldn't find $1. Please download the file and try again."
exit 1 exit 1
fi fi
} }
help() { help() {
echo "${0##*/}" 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"
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" # Add command options
echo -e "Usage:\n"
echo "With no arguments, open the game launcher."
for i in "${!command[@]}" ; do for i in "${!command[@]}" ; do
echo "-${i/:/ <parameter>}: ${command[${i}]}" help_text+="-${i/:/ <parameter>}: ${command[${i}]}\n"
done | sort done
echo
echo "Some settings that are often used can be stored in a settings.conf file." 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"
echo "If wanted, place it at the following location:" help_text+="doomLanguage=\"en\" # 2 letter language code for translation.\n"
echo "${configFile%/*}/settings.conf" help_text+="ipfsGateway=\"https://ipfs.stormux.org\" # Gateway to be used for ipfs downloads.\n"
echo "The syntax is variable=\"value\"" help_text+="noCache=\"true\" # Do not keep downloaded items in the cache.\n"
echo help_text+="spd_module=<module_name>\" # set speech-dispatcher module.\n"
echo "doomLanguage=\"en\" # 2 letter language code for translation." help_text+="spd_pitch=<number>\" # set speech-dispatcher speech pitch.\n"
echo "ipfsGateway=\"https://ipfs.stormux.org\" # Gateway to be used for ipfs downloads." help_text+="spd_rate=<number>\" # set speech-dispatcher speech rate.\n"
echo "noCache=\"true\" # Do not keep downloaded items in the cache." help_text+="spd_voice=<voice_name>\" # set speech-dispatcher voice. Be sure module is correct.\n"
echo "spd_module=\<module_name>\" # set speech-dispatcher module." help_text+="spd_volume=<number>\" # set speech-dispatcher speech volume.\n\n"
echo "spd_pitch=\<number>\" # set speech-dispatcher speech pitch." help_text+="INSTALLER SCRIPT DIALOG FUNCTIONS:\n"
echo "spd_rate=\<number>\" # set speech-dispatcher speech rate." help_text+="alert [\"title\" \"backtitle\"] \"message\" - Play alert sound and show acknowledgement dialog\n"
echo "spd_voice=\<voice_name>\" # set speech-dispatcher voice. Be sure module is correct." help_text+="ui_msgbox \"title\" \"backtitle\" \"message\" - Show information message\n"
echo "spd_volume=\<number>\" # set speech-dispatcher speech volume." 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 exit 0
} }
# main script # main script
@@ -340,10 +694,11 @@ help() {
game_installer() { game_installer() {
# Create the menu of available games by reading from .install directory # Create the menu of available games by reading from .install directory
declare -a menuList declare -a menuList
# Get all .sh files from .install directory, excluding those starting with #, and sort them # shellcheck disable=SC2154 # installedGames may be populated by sourced config or launch migration
mapfile -t sortedGames < <(for f in "${0%/*}/.install/"*.sh; do # Get all .sh files from .install directory, excluding those starting with #//, and sort them
# Skip if first line starts with # mapfile -t sortedGames < <(for f in "${scriptRoot}/.install/"*.sh; do
[[ $(head -n1 "$f") == "#"* ]] && continue # Skip if first line starts with #//
[[ $(head -n1 "$f") == "#//"* ]] && continue
# Output filename without .sh extension # Output filename without .sh extension
echo "${f##*/}" echo "${f##*/}"
done | sort) done | sort)
@@ -363,36 +718,38 @@ game_installer() {
done done
# If all games are installed, exit # If all games are installed, exit
if [[ ${#menuList[@]} -eq 0 ]]; then if [[ ${#menuList[@]} -eq 0 ]]; then
echo "All games are already installed." ui_msgbox "Linux Game Manager" "Linux Game Manager" "All games are already installed."
exit 0 exit 0
fi fi
# Add donation option at the end # Add donation option at the end
menuList+=("Donate" "Donate") menuList+=("Donate" "Donate")
# Show game selection dialog # Show game selection dialog
game="$(dialog --backtitle "Game Installer" \ game="$(ui_menu "Game Installer" "Game Installer" "Please select a game to install" "${menuList[@]}")"
--clear \
--no-tags \
--menu "Please select a game to install" 0 0 0 "${menuList[@]}" --stdout)"
# Handle selection # Handle selection
if [[ -z "$game" ]]; then
exit 0
fi
if [[ -n "$game" ]]; then if [[ -n "$game" ]]; then
if [[ "$game" == "Donate" ]]; then if [[ "$game" == "Donate" ]]; then
open_url "https://ko-fi.com/stormux" open_url "https://ko-fi.com/stormux"
exit 0 exit 0
fi fi
# Convert game name to filename format # Convert game name to filename format
local installScript="${0%/*}/.install/${game}.sh" local installScript="${scriptRoot}/.install/${game}.sh"
# Check if install script exists # Check if install script exists
if [[ -f "$installScript" ]]; then if [[ -f "$installScript" ]]; then
# Source and execute the install script # Source and execute the install script
# shellcheck disable=SC1090 # install scripts are selected dynamically from .install
source "$installScript" source "$installScript"
# Show success message
ui_msgbox "Game Installer" "Game Installer" "${game} has been installed."
else else
dialog --backtitle "Game Installer" \ ui_msgbox "Game Installer" "Game Installer" "Installation script not found for ${game}"
--msgbox "Installation script not found for ${game}" -1 -1
exit 1 exit 1
fi fi
fi fi
if [[ -e "${0%/*}/.launch/${game}.game" ]] && ! [[ -L "${0%/*}/.launch/${game}.sh" ]]; then if [[ -e "${scriptRoot}/.launch/${game}.game" ]] && ! [[ -L "${scriptRoot}/.launch/${game}.sh" ]]; then
ln -srf "${0%/*}/.launch/${game}.game" "${0%/*}/.launch/${game}.sh" ln -srf "${scriptRoot}/.launch/${game}.game" "${scriptRoot}/.launch/${game}.sh"
fi fi
exit 0 exit 0
} }
@@ -403,7 +760,8 @@ game_removal() {
# Initialize array for menu construction # Initialize array for menu construction
mapfile -t menuList < <( mapfile -t menuList < <(
if [[ -d ".launch" ]]; then if [[ -d ".launch" ]]; then
find -L "${0%/*}/.launch" -maxdepth 1 -type f -iname "*.sh" -print0 | sort -z | xargs -0 bash -c ' # shellcheck disable=SC2016 # variables expand in the child bash, not this single-quoted string
find -L "${scriptRoot}/.launch" -maxdepth 1 -type f -iname "*.sh" -print0 | sort -z | xargs -0 bash -c '
for f; do for f; do
name="${f##*/}" name="${f##*/}"
echo "$f" echo "$f"
@@ -412,44 +770,51 @@ game_removal() {
fi fi
) )
if [[ ${#menuList} -eq 0 ]]; then if [[ ${#menuList} -eq 0 ]]; then
dialog --backtitle "Linux Game Manager" \ ui_msgbox "Linux Game Manager" "Linux Game Manager" "No games found."
--msgbox "No games found." -1 -1
exit 0 exit 0
fi fi
# Create the menu of installed games # 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 local selectedGame
selectedGame=$(dialog --backtitle "Linux Game Manager" \ selectedGame=$(ui_menu "Linux Game Manager" "Linux Game Manager" "Please select a game to delete" "${menuArgs[@]}")
--clear \
--no-tags \
--menu "Please select a game to delete" 0 0 0 "${menuList[@]}" --stdout)
exitCode=$? exitCode=$?
[[ $exitCode -ne 0 ]] && exit 0 [[ $exitCode -ne 0 ]] || [[ -z "$selectedGame" ]] && exit 0
# Get the actual game file paths # Get the actual game file paths
local gameName="${selectedGame##*/}" local gameName="${selectedGame##*/}"
gameName="${gameName%.sh}" gameName="${gameName%.sh}"
local gameFile="$(readlink -f "${0%/*}/.launch/${gameName}.sh")" local gameFile
gameFile="$(readlink -f "${scriptRoot}/.launch/${gameName}.sh")"
# Get the actual installation path from the .game file # Get the actual installation path from the .game file
local gameInstallPath local gameInstallPath
gameInstallPath="$(grep -F "installPath" "$gameFile" | grep -v 'pushd' | head -n1)" while IFS= read -r line; do
gameInstallPath="${gameInstallPath#*/}" [[ "${line}" == \#* ]] && continue
gameInstallPath="${installPath}/${gameInstallPath%/*}" [[ "${line}" == *'pushd'* ]] && continue
if [[ "${line}" =~ \$\{installPath\}/([^\"]+) ]]; then
gameInstallPath="${installPath}/${BASH_REMATCH[1]}"
break
fi
done < "${gameFile}"
if [[ -z "$gameInstallPath" ]] || [[ "${gameInstallPath%%/}" == "$installPath" ]]; then if [[ -z "$gameInstallPath" ]] || [[ "${gameInstallPath%%/}" == "$installPath" ]]; then
# No install path found, just remove from list # No install path found, just remove from list
dialog --backtitle "Linux Game Manager" \ 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
--yesno "This will remove the game from your game list, but will not remove any files. Do you want to continue?" -1 -1 || exit 0
# Remove only the .sh symlink # Remove only the .sh symlink
rm -fv "${0%/*}/.launch/${gameName}.sh" | \ rm -fv "${scriptRoot}/.launch/${gameName}.sh" | \
dialog --backtitle "Linux Game Manager" \ ui_progressbox "Linux Game Manager" "Removing game from list..."
--progressbox "Removing game from list..." -1 -1 # Show success message
ui_msgbox "Linux Game Manager" "Linux Game Manager" "${gameName} has been removed from the list."
else else
# Found install path, can remove game files # Found install path, can remove game files
dialog --backtitle "Linux Game Manager" \ 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
--yesno "This will remove the directory \"${gameInstallPath}\" and all of its contents. Do you want to continue?" -1 -1 || exit 0
# Remove the game directory and symlink # Remove the game directory and symlink
{ rm -rfv "${gameInstallPath}" { rm -rfv "${gameInstallPath}"
rm -fv "${0%/*}/.launch/${gameName}.sh"; rm -fv "${scriptRoot}/.launch/${gameName}.sh";
} | dialog --backtitle "Linux Game Manager" \ } | ui_progressbox "Linux Game Manager" "Removing game..."
--progressbox "Removing game..." -1 -1 # Show success message
ui_msgbox "Linux Game Manager" "Linux Game Manager" "${gameName} has been removed."
fi fi
exit 0 exit 0
} }
@@ -459,7 +824,7 @@ game_removal() {
game_update() { game_update() {
mapfile -t lines < <(find .update -type f -iname '*.sh' 2> /dev/null) mapfile -t lines < <(find .update -type f -iname '*.sh' 2> /dev/null)
if [[ ${#lines} -eq 0 ]]; then if [[ ${#lines} -eq 0 ]]; then
echo "No games found." ui_msgbox "Linux Game Manager" "Linux Game Manager" "No games found."
exit 0 exit 0
fi fi
# Create the menu of updatable games # Create the menu of updatable games
@@ -469,10 +834,8 @@ game_update() {
done done
menuList+=("Donate" "Donate") menuList+=("Donate" "Donate")
menuList+=("Become a Patron" "Become a Patron") menuList+=("Become a Patron" "Become a Patron")
local game="$(dialog --backtitle "Audio Game Updater" \ local game
--clear \ game="$(ui_menu "Audio Game Updater" "Audio Game Updater" "Please select a game to update" "${menuList[@]}")"
--no-tags \
--menu "Please select a game to update" 0 0 0 "${menuList[@]}" --stdout)"
if [[ ${#game} -gt 0 ]]; then if [[ ${#game} -gt 0 ]]; then
if [[ "$game" == "Donate" ]]; then if [[ "$game" == "Donate" ]]; then
open_url "https://ko-fi.com/stormux" open_url "https://ko-fi.com/stormux"
@@ -482,9 +845,17 @@ game_update() {
open_url "https://2mb.games/product/2mb-patron/" open_url "https://2mb.games/product/2mb-patron/"
exit 0 exit 0
fi fi
# Get the game name for success message
local updateGameName="${game##*/}"
updateGameName="${updateGameName%.sh}"
# shellcheck disable=SC1090 # update scripts are selected dynamically from .update
source "${game}"
else
exit 0
fi fi
source "${game}"
run_update run_update
# Show success message
ui_msgbox "Audio Game Updater" "Audio Game Updater" "${updateGameName} has been updated."
exit 0 exit 0
} }
@@ -494,9 +865,10 @@ game_launcher() {
# Initialize array for menu construction # Initialize array for menu construction
mapfile -t menuList < <( mapfile -t menuList < <(
if [[ -d ".launch" ]]; then if [[ -d ".launch" ]]; then
find -L "${0%/*}/.launch" -maxdepth 1 -type f -iname "*.sh" -print0 | sort -z | xargs -0 bash -c ' # shellcheck disable=SC2016 # variables expand in the child bash, not this single-quoted string
find -L "${scriptRoot}/.launch" -maxdepth 1 -type f -iname "*.sh" -print0 | sort -z | xargs -0 bash -c '
for f; do for f; do
[[ $(head -n1 "$f") =~ ^#$ ]] && continue [[ $(head -n1 "$f") == "#//"* ]] && continue
name="${f##*/}" name="${f##*/}"
echo "$f" echo "$f"
echo "${name%.sh}" echo "${name%.sh}"
@@ -504,19 +876,21 @@ game_launcher() {
fi fi
) )
if [[ ${#menuList} -eq 0 ]]; then if [[ ${#menuList} -eq 0 ]]; then
dialog --backtitle "Linux Game Manager" \ ui_msgbox "Linux Game Manager" "Linux Game Manager" "No games found."
--msgbox "No games found." 5 20
exit 0 exit 0
fi fi
# Create the menu of all games # Create the menu of all games
selectedGame="$(dialog --backtitle "Linux Game Launcher" \ # menuList has alternating full_path, display_name pairs
--clear \ declare -a menuArgs
--no-tags \ for ((i=0; i<${#menuList[@]}; i+=2)); do
--menu "Please select a game to play" 0 0 0 "${menuList[@]}" --stdout)" 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=$? local menuCode=$?
if [[ $menuCode -eq 1 ]]; then if [[ $menuCode -ne 0 ]] || [[ -z "$selectedGame" ]]; then
exit 0 exit 0
fi fi
# shellcheck disable=SC1090 # launcher scripts are selected dynamically from .launch
. "${selectedGame}" . "${selectedGame}"
exit 0 exit 0
} }
@@ -532,17 +906,30 @@ migrate_launcher() {
# Extract game name and path # Extract game name and path
gameName="${line%|*}" gameName="${line%|*}"
# Create launcher script if it doesn't exist # Create launcher script if it doesn't exist
if [[ ! -L "${0%/*}/.launch/${gameName}.sh" ]]; then if [[ ! -L "${scriptRoot}/.launch/${gameName}.sh" ]]; then
ln -srf "${0%/*}/.launch/${gameName}.game" "${0%/*}/.launch/${gameName}.sh" ln -srf "${scriptRoot}/.launch/${gameName}.game" "${scriptRoot}/.launch/${gameName}.sh"
fi fi
done < <(sed '/^$/d' "${configFile}") done < <(sed '/^$/d' "${configFile}")
# Move the old config file and notify user # Move the old config file and notify user
mv "${configFile}" "${configFile}.bak" mv "${configFile}" "${configFile}.bak"
dialog --backtitle "Linux Game manager" --msgbox \ 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}"
"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}" -1 -1
} }
# Make sure this is not ran as root
if [[ "$(whoami)" == "root" ]]; then
echo "Please do not run ${0##*/} as root."
exit 1
fi
# 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 # 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. # Warning, launching games from console is not recommended.
if [[ -z "$DISPLAY" ]]; then if [[ -z "$DISPLAY" ]]; then
@@ -553,8 +940,10 @@ cache="${XDG_CACHE_HOME:-$HOME/.cache}/linux-game-manager"
configFile="${XDG_CONFIG_HOME:-$HOME/.config}/storm-games/linux-game-manager/games.conf" configFile="${XDG_CONFIG_HOME:-$HOME/.config}/storm-games/linux-game-manager/games.conf"
mkdir -p "${cache}" mkdir -p "${cache}"
mkdir -p "${configFile%/*}" mkdir -p "${configFile%/*}"
declare -a installedGames=()
# Load any arguments from settings.conf file # Load any arguments from settings.conf file
if [[ -r "${configFile%/*}/settings.conf" ]]; then if [[ -r "${configFile%/*}/settings.conf" ]]; then
# shellcheck disable=SC1091 # settings.conf is optional and resolved from the runtime config directory
source "${configFile%/*}/settings.conf" source "${configFile%/*}/settings.conf"
fi fi
unset noCache unset noCache
@@ -575,11 +964,13 @@ requiredPackages=(
"7z" "7z"
"curl" "curl"
"dialog" "dialog"
"jq"
"yad"
"unzip" "unzip"
) )
for i in "${requiredPackages[@]}" ; do for i in "${requiredPackages[@]}" ; do
if ! command -v $i > /dev/null 2>&1 ; then if ! command -v "$i" > /dev/null 2>&1 ; then
echo "Please install ${i/7z/p7zip} before continuing." ui_msgbox "Linux Game Manager" "Linux Game Manager" "Please install ${i/7z/p7zip} before continuing."
exit 1 exit 1
fi fi
done done
@@ -618,13 +1009,13 @@ while getopts "${args}" i ; do
r) game_removal ;; r) game_removal ;;
t) t)
gameCount=$(find .install -type f -iname "*.sh" | wc -l) gameCount=$(find .install -type f -iname "*.sh" | wc -l)
dialog --backtitle "Linux Game Manager" \ ui_infobox "Linux Game Manager" "Linux Game Manager" "There are currently ${gameCount} games available."
--infobox "There are currently ${gameCount} games available." -1 -1
exit 0 exit 0
;; ;;
u) game_update ;; u) game_update ;;
esac esac
done done
[[ "${noCache}" == "true" ]] && :
exit 0 exit 0