I forgot to push changes, and it's been a while. I'm pretty sure this is game fixes.

This commit is contained in:
Storm Dragon
2026-07-27 01:41:47 -04:00
parent 92745d8af8
commit 8943688a43
7 changed files with 219 additions and 349 deletions
+26
View File
@@ -44,6 +44,32 @@ install_proton_bottle() {
fi
}
install_proton_winetricks_verb() {
local verbName="$1"
local output=""
local status=0
local hadErrexit="false"
require_umu || return 1
if [[ $- == *e* ]]; then
hadErrexit="true"
fi
set +e
output="$(umu-run winetricks -q "$verbName" 2>&1)"
status=$?
if [[ "$hadErrexit" == "true" ]]; then
set -e
fi
printf '%s\n' "$output"
if [[ "$status" -ne 0 ]]; then
if grep -F -q "winetricks verb '${verbName}' is already installed" <<< "$output"; then
return 0
fi
return "$status"
fi
}
umu_windows_path_to_unix() {
local windowsPath="$1"
local relativePath=""
+41 -9
View File
@@ -1,9 +1,41 @@
download "https://blindgamers.com/downloads/a-heros-call-freeware.zip" "https://stormgames.wolfe.casa/downloads/nvdaControllerClient32.dll"
export winVer="win7"
export winetricksSettings="vd=1024x768"
install_wine_bottle
# Dotnet is evil. That is all.
LC_ALL=C DISPLAY="" winetricks -q dotnet462 xna40
wineserver -k # Really!
install_with_progress unzip "Extracting game files..." -d "$WINEPREFIX/drive_c/Program Files" "${cache}/a-heros-call-freeware.zip"
add_launcher "c:\Program Files\a-heros-call\A Hero's Call.exe"
# shellcheck shell=bash disable=SC2154 # cache, game, and helper functions are set by audiogame-manager.
export game="A Hero's Call"
herosCallGameId="a-heros-call"
herosCallPath="c:\\Program Files\\a-heros-call\\A Hero's Call.exe"
download "https://blindgamers.com/downloads/a-heros-call-freeware.zip" \
"https://stormgames.wolfe.casa/downloads/nvdaControllerClient32.dll"
export PROTON_USE_XALIA=0
install_proton_bottle "$herosCallGameId"
alert "A Hero's Call" "A Hero's Call" "If you hear a window open during installation, or if the installer seems to be taking a long time, try pressing enter to see if it will continue."
{
echo "# Installing A Hero's Call dependencies..."
WINETRICKS_FORCE=1 install_proton_winetricks_verb speechsdk
install_proton_winetricks_verb corefonts
install_proton_winetricks_verb dotnet462
install_proton_winetricks_verb xna40
install_proton_winetricks_verb win7
echo "# Extracting game files..."
mkdir -p "${WINEPREFIX}/drive_c/Program Files"
unzip -oq "${cache}/a-heros-call-freeware.zip" -d "${WINEPREFIX}/drive_c/Program Files"
echo "# Installation complete"
} | agm_progressbox "Installing Game" "Installing A Hero's Call with UMU/Proton (this may take several minutes)..."
stop_umu_bottle
herosCallInstallDir="${WINEPREFIX}/drive_c/Program Files/a-heros-call"
if [[ ! -f "${herosCallInstallDir}/A Hero's Call.exe" ]]; then
agm_msgbox "A Hero's Call" "A Hero's Call" "A Hero's Call did not install to the expected location."
exit 1
fi
cp "${cache}/nvdaControllerClient32.dll" "${herosCallInstallDir}/nvdaControllerClient32.dll"
mkdir -p "${herosCallInstallDir}/lib/Tolk"
cp "${cache}/nvdaControllerClient32.dll" "${herosCallInstallDir}/lib/Tolk/nvdaControllerClient32.dll"
add_umu_launcher "$herosCallGameId" "$herosCallPath" "export PROTON_USE_XALIA=0"
+92 -26
View File
@@ -1,28 +1,94 @@
#// Borken, candidate for removal
export WINEARCH="win64" # Migrated to wine64 with WINETRICKS_FORCE=1 - complex .NET dependencies, test carefully
download "http://blind-games.com/newentombed/EntombedSetup.exe" "https://download.microsoft.com/download/E/C/1/EC1B2340-67A0-4B87-85F0-74D987A27160/SSCERuntime-ENU.exe" "https://stormgames.wolfe.casa/downloads/Entombed.exe.config" "https://stormgames.wolfe.casa/downloads/mfplat.dll"
export winVer="win7"
install_wine_bottle sapi msvcrt40 gdiplus ie7 wmp11 mf
# Ok, more dotnet.
LC_ALL=C DISPLAY="" winetricks -q dotnet40 xna40
wineserver -k # Sigh.
mkdir -p "${WINEPREFIX}/drive_c/temp"
pushd "${WINEPREFIX}/drive_c/temp"
install_with_progress 7z "Extracting game files..." x "${cache}/SSCERuntime-ENU.exe"
wine msiexec /i "${WINEPREFIX}/drive_c/temp/SSCERuntime_x86-ENU.msi" /q
rm *
popd
pushd "${WINEPREFIX}/drive_c/Program Files/Microsoft SQL Server Compact Edition/v3.5"
wine regsvr32 sqlceoledb35.dll
wine regsvr32 sqlceca35.dll
popd
wine "${cache}/EntombedSetup.exe" /silent
pushd "${WINEPREFIX}/drive_c/Program Files/Entombed"
cp ../Microsoft\ SQL\ Server\ Compact\ Edition/v3.5/Private/System.Data.SqlServerCe.Entity.dll ../Microsoft\ SQL\ Server\ Compact\ Edition/v3.5/Private/System.Data.SqlServerCe.dll .
cp ../Microsoft\ SQL\ Server\ Compact\ Edition/v3.5/sql* .
cp "${cache}/Entombed.exe.config" .
popd
if [ ! -f "${WINEPREFIX}/drive_c/windows/system32/mfplat.dll" ] ; then
# shellcheck shell=bash disable=SC2154 # cache, game, and helper functions are set by audiogame-manager.
export game="Entombed"
entombedGameId="entombed"
entombedPath='c:\Program Files (x86)\Entombed\Entombed.exe'
download "http://blind-games.com/newentombed/EntombedSetup.exe" \
"https://download.microsoft.com/download/E/C/1/EC1B2340-67A0-4B87-85F0-74D987A27160/SSCERuntime-ENU.exe" \
"https://stormgames.wolfe.casa/downloads/Entombed.exe.config" \
"https://stormgames.wolfe.casa/downloads/mfplat.dll"
export PROTON_USE_XALIA=0
install_proton_bottle "$entombedGameId"
entombedProfileSid="$(sed -n 's/^;; All keys relative to REGISTRY\\\\User\\\\//p' "${WINEPREFIX}/user.reg" | head -n 1)"
if [[ -n "$entombedProfileSid" ]]; then
umu-run reg add "HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\${entombedProfileSid}" /v ProfileImagePath /t REG_SZ /d "C:\\users\\steamuser" /f
fi
mkdir -p \
"${WINEPREFIX}/drive_c/users/steamuser/AppData/Local/IsolatedStorage" \
"${WINEPREFIX}/drive_c/users/steamuser/AppData/Roaming/IsolatedStorage"
alert "Entombed" "Entombed" "If you hear a window open during installation, or if the installer seems to be taking a long time, try pressing enter to see if it will continue."
{
echo "# Installing Entombed dependencies..."
WINETRICKS_FORCE=1 install_proton_winetricks_verb speechsdk
install_proton_winetricks_verb msvcrt40
install_proton_winetricks_verb gdiplus
install_proton_winetricks_verb wmp11
install_proton_winetricks_verb mf
install_proton_winetricks_verb dotnet40
install_proton_winetricks_verb xna40
install_proton_winetricks_verb win7
echo "# Extracting SQL Server Compact runtime..."
entombedTempDir="${WINEPREFIX}/drive_c/temp"
mkdir -p "$entombedTempDir"
7z x -y "-o${entombedTempDir}" "${cache}/SSCERuntime-ENU.exe"
echo "# Installing SQL Server Compact runtime..."
umu-run msiexec /i "c:\\temp\\SSCERuntime_x86-ENU.msi" /q
umu-run regsvr32 "c:\\Program Files\\Microsoft SQL Server Compact Edition\\v3.5\\sqlceoledb35.dll"
umu-run regsvr32 "c:\\Program Files\\Microsoft SQL Server Compact Edition\\v3.5\\sqlceca35.dll"
echo "# Installing Entombed..."
umu-run "${cache}/EntombedSetup.exe" /silent
find "$entombedTempDir" -mindepth 1 -maxdepth 1 -exec rm -rf "{}" +
echo "# Installation complete"
} | agm_progressbox "Installing Game" "Installing Entombed with UMU/Proton (this may take several minutes)..."
stop_umu_bottle
entombedInstallDir=""
for entombedCandidateDir in \
"${WINEPREFIX}/drive_c/Program Files (x86)/Entombed" \
"${WINEPREFIX}/drive_c/Program Files/Entombed"; do
if [[ -f "${entombedCandidateDir}/Entombed.exe" ]]; then
entombedInstallDir="$entombedCandidateDir"
break
fi
done
if [[ -z "$entombedInstallDir" ]]; then
agm_msgbox "Entombed" "Entombed" "Entombed did not install to the expected location."
exit 1
fi
entombedSqlDir=""
for entombedCandidateSqlDir in \
"${WINEPREFIX}/drive_c/Program Files (x86)/Microsoft SQL Server Compact Edition/v3.5" \
"${WINEPREFIX}/drive_c/Program Files/Microsoft SQL Server Compact Edition/v3.5"; do
if [[ -d "$entombedCandidateSqlDir" ]]; then
entombedSqlDir="$entombedCandidateSqlDir"
break
fi
done
if [[ -z "$entombedSqlDir" ]]; then
agm_msgbox "Entombed" "Entombed" "SQL Server Compact v3.5 did not install to the expected location."
exit 1
fi
cp "${entombedSqlDir}/Private/System.Data.SqlServerCe.Entity.dll" "$entombedInstallDir/"
cp "${entombedSqlDir}/Private/System.Data.SqlServerCe.dll" "$entombedInstallDir/"
cp "${entombedSqlDir}"/sql* "$entombedInstallDir/"
cp "${cache}/Entombed.exe.config" "${entombedInstallDir}/Entombed.exe.config"
if [[ ! -f "${WINEPREFIX}/drive_c/windows/system32/mfplat.dll" ]]; then
cp "${cache}/mfplat.dll" "${WINEPREFIX}/drive_c/windows/system32/"
fi
add_launcher "c:\Program Files (x86)\Entombed\Entombed.exe"
add_umu_launcher "$entombedGameId" "$entombedPath" "export PROTON_USE_XALIA=0"
-265
View File
@@ -1,265 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
**Critical**: Be sure to keep this file up to date with new changes.
## Project Overview
**Audiogame Manager** is a comprehensive bash-based installer and launcher system for Windows audio games running under Wine on Linux. The project focuses on accessibility, providing speech synthesis support and screen reader compatibility for audio games designed for blind and visually impaired users.
## Architecture
### Core Components
- **Main Script**: `audiogame-manager.sh` - Entry point providing interactive menus for game installation, launching, and management with Wine32/64 bottle support
- **Includes Directory**: `.includes/` - Modular helper scripts containing core functionality
- `functions.sh` - Core utilities (download, cache management, validation, requirements checking)
- `dialog-interface.sh` - UI abstraction layer supporting both console (dialog) and GUI (yad) modes
- `bottle.sh` - Wine bottle management, RHVoice installation, and prefix management
- `checkup.sh` - System dependency validation
- `help.sh` - Documentation system
- `desktop.sh` - Desktop integration
- `update.sh` - Auto-update mechanisms
- **Game Scripts**: `game-scripts/` - Individual game update/launch scripts
- **Install Scripts**: `.install/` - Game installation scripts (100+ games supported)
- **Speech Integration**: `speech/` - TTS and accessibility tools
- `set-voice.sh` - Voice configuration with test functionality
- Supports multiple TTS engines (RHVoice, SAPI, Cepstral)
- Per-bottle voice configuration system
- NVDA screen reader compatibility through custom DLL
- **Wine Integration**: `wine/` - Wine setup utilities including bottle creation and dependency installation
- `mkwine.sh` - Wine bottle creation
- Distribution-specific dependency installers
### Wine Architecture
The project uses Wine with a unified architecture:
- **Wine64 Only**: All games use wine64+WOW64 exclusively - it runs both 32-bit and 64-bit applications efficiently
- **Wine32 ELIMINATED**: As of 2025-12-06, wine32 bottle is no longer created or used
- **Unified bottle**: Single wine64 prefix (stored in `~/.local/wine64`) for ALL games, including SAPI games
- **Custom bottles**: Can be stored in `~/.local/share/audiogame-manager/wineBottles/` with per-bottle configurations in `~/.config/audiogame-manager/`
- **IMPORTANT**: Never create game-specific wine directories like `~/.local/winegamename` - use the standard bottle system
#### SAPI and Speech SDK Support (WINETRICKS_FORCE=1)
**Discovery (2025-12-06)**: Setting `WINETRICKS_FORCE=1` enables reliable speechsdk installation in wine64+WOW64 bottles, eliminating the need for wine32 for most SAPI-dependent games.
**Implementation:**
- `audiogame-manager.sh:96` - wine64 bottle creation includes speechsdk with `WINETRICKS_FORCE=1`
- `audiogame-manager.sh:99` - Restores win10 after speechsdk (see caveat below)
- `.includes/bottle.sh:176-181` - Only speechsdk installations use `WINETRICKS_FORCE=1` (other deps use regular winetricks)
- Both bottles install Microsoft Mike as the default SAPI voice automatically
**CAVEAT**: The `speechsdk` winetricks verb sets `w_set_winver winxp` at the end, trampling any previously set Windows version. Always call `winetricks win10` AFTER installing speechsdk.
**Migrated SAPI Games (wine64):**
- Bloodshed - Tested and confirmed working
- Kitchensinc Games - Tested and confirmed working (VB6)
- Oh Shit - Migrated, requires testing
- Dog Who Hates Toast - Migrated, requires testing (VB6)
- Lunimals - Migrated, requires testing (VB6)
- VIP Mud - Migrated, requires testing (VB6)
- Entombed - Migrated, requires testing (complex .NET dependencies)
- Skateboarder Pro - Already configured for wine64, now functional
- Three D velocity - Already configured for wine64, now functional
**Migrated Self-Voicing Games (wine64):**
- Shadow Line - Migrated, requires Windows 8 via per-app versioning
- Villains From Beyond - Tested and confirmed working
#### Per-Application Windows Version
**Problem (2025-12-08)**: Some games require specific Windows versions (e.g., Shadow Line needs win8) but with a unified bottle, `winetricks winXX` changes the version globally for ALL games.
**Solution**: Use Wine's per-application settings via registry keys:
```
[HKEY_CURRENT_USER\Software\Wine\AppDefaults\executable.exe]
"Version"="win8"
```
**Implementation:**
- Game installers set `export winVer="win8"` (or win7, win10)
- `install_wine_bottle()` stores this in `gameWinVer` (doesn't apply globally)
- `add_launcher()` calls `set_app_winver()` which writes the per-exe registry key
- Located in `.includes/bottle.sh:186-209` (`set_app_winver` function)
**Architecture Selection Logic:**
- Games explicitly setting `WINEARCH=win64` will use wine64 (new behavior)
- Games passing `sapi` dependency use wine64 with speechsdk pre-installed
- Legacy games passing `speechsdk` dependency still use wine32 for compatibility
- Default architecture is wine64 for all new games
### Interface System
Dialog interface automatically detects environment:
- Console mode: Uses `dialog` for accessibility
- GUI mode: Uses `yad` when DISPLAY is available
- All UI functions are prefixed with `agm_` (audiogame manager)
## Development Commands
### System Check
```bash
./audiogame-manager.sh -c # Check system requirements and dependencies
bash -n audiogame-manager.sh # Basic syntax check
```
### Installation and Usage
```bash
./audiogame-manager.sh # Launch installed games menu
./audiogame-manager.sh -i # Install games (interactive menu)
./audiogame-manager.sh -I "Game Name" # Install a game noninteractively
./audiogame-manager.sh -h # Show help
```
### Wine Bottle Management
```bash
./wine/mkwine.sh [bottle_name] [architecture] # Create a new Wine bottle
```
### Voice and Speech Testing
```bash
./speech/set-voice.sh # Configure and test voice settings
./speech/set-voice.sh [bottle_name] # Configure voice for a specific bottle
```
### Dependency Management
```bash
# Check all required packages
./.includes/checkup.sh packages
# Install Wine dependencies for different distros
./wine/install-dependencies-arch.sh
./wine/install-dependencies-debian.sh
```
### Testing All Include Files
```bash
for f in .includes/*.sh; do bash -n "$f"; done
```
## Key Patterns and Conventions
### Coding Style - **EXTREMELY IMPORTANT - MUST BE FOLLOWED**
- **Variables**: Use camelCase for variable names (e.g., `gameTitle`, `wineBottle`, `installPath`)
- **CRITICAL**: ALL variables must use camelCase - no exceptions
- Examples: `selectedGame`, `wineArch`, `installPath`, `downloadUrl`
- Never use: `selected_game`, `wine_arch`, `install_path`, `download_url`
- **Functions**: Use snake_case for function names (e.g., `install_game`, `create_wine_bottle`, `check_dependencies`)
- **CRITICAL**: ALL functions must use snake_case - no exceptions
- Examples: `get_wine_bottle()`, `process_launcher_flags()`, `download_file()`
- Never use: `getWineBottle()`, `processLauncherFlags()`, `downloadFile()`
- **Shebang**: Use `#!/bin/bash` for all bash scripts
- **CRITICAL**: .sh files in .install/ are game installers, not typical bash scripts If they contain # on the first line they are disabled and will not show up in audiogame-manager
- **Sourcing pattern**:
- **Main script** (`audiogame-manager.sh`): Use `source "${scriptDir}/.includes/file.sh"` (scriptDir is defined at line 4)
- **Subdirectory scripts** (game-scripts/, wine/, speech/): Use `source "${0%/*}/../.includes/file.sh"`
- **CRITICAL**: Never use relative paths like `source .includes/file.sh` - these fail when the current working directory differs from the script location
- **Indentation**: Use consistent indentation (tabs or spaces, follow existing file patterns)
- When fixing code, correct any indentation inconsistencies to match the established style
### Error Handling
- Functions return 0 for success, non-zero for failure
- Use consistent exit codes throughout scripts
- Provide clear error messages with context
- Critical errors vs warnings are clearly distinguished in checkup system
- Progress feedback is provided for all long-running operations
- Always clean up temporary files on exit
- Log important operations for debugging
### File Structure
- Game installers follow naming convention: `.install/Game Name.sh`
- **IMPORTANT**: Games starting with hash (#) are commented out even if it's a comment
- First line of a game installer may not start with # unless we're excluding it
- Cache directory: `~/.local/share/audiogame-manager/cache/`
- Wine bottles: `~/.local/wine32/` and `~/.local/wine64/`
- Custom bottles: `~/.local/share/audiogame-manager/wineBottles/`
- Configuration: `~/.config/audiogame-manager/`
### UI Functions
All dialog functions in `.includes/dialog-interface.sh` follow pattern:
- `agm_menu()` - Selection menus
- `agm_msgbox()` - Message display
- `agm_yesno()` - Confirmation dialogs
- `agm_progressbox()` - Progress display
- Functions automatically adapt to console/GUI environment
### Game Integration
- Each game has both an installer (`.install/`) and optional update script (`game-scripts/`)
- Games are categorized by engine type and requirements
- SAPI games automatically use Wine32, others use Wine64
- Games are tracked in configuration files with Wine bottle associations
- Each game can use different Wine bottles with specific configurations
### Game Installation Pattern
1. Check system requirements and dependencies
2. Create or verify Wine bottle
3. Install Wine dependencies via winetricks
4. Download and validate game files (use checksums when available)
5. Configure speech synthesis (typically RHVoice)
6. Add game entry to launcher configuration
### Accessibility Features
- Sound alerts using `sox` for important notifications
- Screen reader compatibility through proper dialog usage
- Keyboard navigation support throughout interface
- Voice testing integrated into setup process
## Dependencies
### Critical (Required)
- wine, curl, dialog, sox
- Archive tools: 7z, cabextract, unzip, xz
- winetricks (for Wine component management)
### Optional (Warnings if missing)
- gawk (for game removal)
- ocrdesktop (installer debugging)
- qjoypad (gamepad support)
- translate-shell, sqlite3, perl (translation features)
- w3m (documentation viewing)
- xclip, xdotool (X11 integration)
### Platform Support
- x86_64 Linux (primary)
- aarch64 with FEX-Emu (alternative Wine implementation)
## Testing
- System requirements: `./audiogame-manager.sh -c`
- Voice functionality: Use built-in voice test in `set-voice.sh`
- Game installation: Test with simple games first before complex ones
- Wine bottle integrity: Check `~/.local/wine32/system.reg` and `~/.local/wine64/system.reg` exist
- Test with different Wine versions when modifying bottle creation
- Verify speech synthesis functionality after TTS changes
- Test game installation scripts in clean environments
- Check both 32-bit and 64-bit compatibility where applicable
## Recent Refactor Notes (2025)
### Major Refactor Status
The project has undergone a significant refactor to modularize functionality. **Status: Largely Complete**
### Key Fixes Applied
1. **Function naming**: Fixed `process_launcher-flags()``process_launcher_flags()` in `audiogame-manager.sh:270`
2. **Variable scope**: Added `export game` in main script so `.includes/bottle.sh` functions can access it
3. **Installation logic**: Completed the `-I` option implementation for noninteractive game installation
4. **Code deduplication**: Removed duplicate `check_news` and launcher logic from `update.sh`
5. **Include sourcing**: Fixed all relative path sourcing (e.g., `source .includes/bottle.sh`) to use `${scriptDir}` to ensure desktop launchers and execution from any working directory works correctly
### Critical Variable Handling
- **`$game` variable**: Must be exported when set (line 489 in main script) for bottle.sh functions to work
- **`agmNoLaunch` variable**: Used to prevent main script execution when sourced by other scripts
- **Bottle names**: Derived from game names, converted to lowercase with spaces replaced by hyphens
### Important Patterns
- **Noninteractive installation**: The `-I` option sources the appropriate `.install/GameName.sh` script
- **Variable scope**: Include files rely on exported variables from main script
- **Wine bottle logic**: Game-specific wine versions are handled in `bottle.sh:get_bottle()`
### Common Issues to Watch For
1. **Variable exports**: Ensure variables are exported when needed by include files
2. **Function naming**: Use snake_case for functions, camelCase for variables
3. **Path quoting**: Always quote paths that might contain spaces
4. **Duplicate logic**: Check main script vs includes to avoid redundant code
+5 -6
View File
@@ -687,12 +687,11 @@ create_game_array() {
# Game array 0 bottle, 1 path, 2 title, 3+ flags
local selectedGame="$1"
for i in "${lines[@]}" ; do
# Only compare the launcher section
local j="${selectedGame#*|}"
local k="${i#*|}"
k="${k%%|*}"
if [[ "$j" == "$k" ]]; then
IFS='|' read -ra game <<< "$i"
local selectedLauncher="${selectedGame#*|}"
local candidate=()
IFS='|' read -ra candidate <<< "$i"
if [[ "$selectedLauncher" == "${candidate[1]}" || "$selectedGame" == "${candidate[2]}" ]]; then
game=("${candidate[@]}")
break
fi
done
+36 -43
View File
@@ -7,23 +7,30 @@
# set -Eeuo pipefail # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/#:~:text=set%20%2Du,is%20often%20highly%20desirable%20behavior.
shopt -s expand_aliases
# Debug logging
#DEBUG_LOG="${XDG_CACHE_HOME:-$HOME/.cache}/audiogame-manager/translator-debug.log"
#exec 2>>"$DEBUG_LOG"
#echo "=== Translator started at $(date) ===" >&2
#echo "Args: $@" >&2
#set -x
if [[ $# -ne 2 ]]; then
echo "Usage: $0 \"application name\" \"file name\"."
exit 1
fi
scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
pythonScript="${scriptDir}/translate_clipboard.py"
if [[ -z "${AGM_TRANSLATOR_FORCE_SHELL:-}" ]] && command -v python3 &> /dev/null && [[ -f "$pythonScript" ]]; then
exec python3 "$pythonScript" "$@"
fi
is_app_running() {
local appPattern="$1"
local selfPid="$$"
pgrep -af -u "$USER" "$appPattern" \
| awk -v selfPid="$selfPid" '$1 != selfPid && $0 !~ /clipboard_translator.sh/ { found=1 } END { exit !found }'
}
# Wait for the application to start
while ! pgrep -f -u "$USER" "$1" &> /dev/null ; do
while ! is_app_running "$1"; do
sleep 0.05
done
fileName="${2,,}"
fileName="${fileName//[[:space:]]/-}.sqlite"
translationFile="${XDG_CACHE_HOME:-$HOME/.cache}/audiogame-manager/${fileName}"
@@ -44,26 +51,8 @@ CREATE INDEX translations_text_idx ON translations (text);
EOF
fi
# Define a function to safely query the database
query_database() {
local dbFile="$1"
local sqlQuery="$2"
sqlite3 -line "$dbFile" "$sqlQuery"
}
# Define a function to safely insert into the database
insert_database() {
local dbFile="$1"
local text="$2"
local translation="$3"
# Use sqlite3 .import feature which is more robust for special characters
echo "$text|$translation" | sqlite3 -separator "|" "$dbFile" ".import /dev/stdin temp_import"
sqlite3 "$dbFile" "INSERT OR IGNORE INTO translations SELECT * FROM temp_import; DROP TABLE IF EXISTS temp_import;"
}
# Read so long as the application is running
while pgrep -f -u "$USER" "$1" &> /dev/null ; do
while is_app_running "$1"; do
sleep 0.05
text="$(xclip -d "${DISPLAY:-:0}" -selection clipboard -o 2> /dev/null)"
if [[ -f ~/.agmsilent ]]; then
@@ -80,46 +69,50 @@ while pgrep -f -u "$USER" "$1" &> /dev/null ; do
# https://en.wikipedia.org/wiki/Unicode_equivalence#Combining_and_precomposed_characters
# https://www.effectiveperlprogramming.com/2011/09/normalize-your-perl-source/
alias nfc="perl -MUnicode::Normalize -CS -ne 'print NFC(\$_)'" # composed characters
nfc() {
perl -MUnicode::Normalize -CS -ne 'print NFC($_)'
}
# Normalize different unicode space characters to the same space
# https://stackoverflow.com/a/43640405
alias normalizeSpaces="perl -CSDA -plE 's/[^\\S\\t]/ /g'"
alias normalizeUnicode="normalizeSpaces | nfc"
normalize_spaces() {
perl -CSDA -plE 's/[^\S\t]/ /g'
}
# Remove zero-width spaces (U+200B) that some games insert between characters
strip_zwsp() {
perl -CSDA -plE 's/\x{200B}//g'
}
normalize_unicode() {
strip_zwsp | normalize_spaces | nfc
}
# Normalize text
normalizedText="$(echo "$text" | normalizeUnicode)"
normalizedText="$(echo "$text" | normalize_unicode)"
# Create a temporary database for import
sqlite3 "$translationFile" "CREATE TABLE IF NOT EXISTS temp_import(text TEXT, translation TEXT);"
# Check if we already have a translation
translated=$(sqlite3 "$translationFile" "SELECT translation FROM translations WHERE text = '$normalizedText' LIMIT 1;" 2>/dev/null)
echo "DEBUG: Database lookup result: '$translated'" >&2
if [[ -z "$translated" ]]; then
echo "DEBUG: No cached translation, calling trans command" >&2
# Get translation from the trans utility
translated="$(trans -no-autocorrect -no-warn -brief "$normalizedText" | head -1 | sed 's/\s*$//' | normalizeUnicode)"
echo "DEBUG: trans returned: '$translated'" >&2
translated="$(trans -no-autocorrect -no-warn -brief "$normalizedText" | head -1 | sed 's/\s*$//' | normalize_unicode)"
if [[ -n "$translated" ]]; then
# Insert using echo piping to avoid escaping issues
echo "$normalizedText|$translated" | sqlite3 -separator "|" "$translationFile" ".import /dev/stdin temp_import"
sqlite3 "$translationFile" "INSERT OR IGNORE INTO translations SELECT * FROM temp_import; DELETE FROM temp_import;"
echo "DEBUG: Saved to database" >&2
fi
fi
# If we got a translation, speak it
if [[ -n "$translated" ]]; then
echo "DEBUG: Speaking translation: '$translated'" >&2
spd-say -- "$translated"
else
echo "DEBUG: No translation available, speaking original: '$text'" >&2
spd-say -- "$text"
spd-say -- "$normalizedText"
fi
# Clear clipboard
echo "" | xclip -d "${DISPLAY:-:0}" -selection clipboard 2> /dev/null
done
+19
View File
@@ -23,6 +23,10 @@ printf '%s|%s|%s|%s\n' "$WINEPREFIX" "$GAMEID" "${STORE:-}" "$*" >> "$UMU_STUB_L
if [[ "${1:-}" == "" ]]; then
mkdir -p "$WINEPREFIX/drive_c"
fi
if [[ "${1:-}" == "winetricks" && "${3:-}" == "already-installed" ]]; then
printf "winetricks verb 'already-installed' is already installed\n" >&2
exit 1
fi
STUB
chmod +x "${testRoot}/bin/umu-run"
@@ -90,6 +94,13 @@ test_add_umu_launcher_records_backend_and_game_id() {
assert_file_contains "$configFile" 'umu|c:\Program Files (x86)\GalaxyLaboratory\ShadowRine_FullVoice\play_sr.exe|Shadow Line|export umuGameId=shadow-line' "UMU launcher entry is recorded"
}
test_add_umu_launcher_records_extra_environment_flags() {
get_umu_bottle "entombed"
game="Entombed"
add_umu_launcher "entombed" 'c:\Program Files (x86)\Entombed\Entombed.exe' "export PROTON_USE_XALIA=0"
assert_file_contains "$configFile" 'umu|c:\Program Files (x86)\Entombed\Entombed.exe|Entombed|export umuGameId=entombed|export PROTON_USE_XALIA=0' "UMU launcher entry records extra environment flags"
}
test_run_umu_game_uses_converted_path() {
get_umu_bottle "shadow-line"
mkdir -p "${WINEPREFIX}/drive_c/Program Files (x86)/GalaxyLaboratory/ShadowRine_FullVoice"
@@ -113,8 +124,16 @@ test_install_crlf_file_normalizes_line_endings() {
fi
}
test_install_proton_winetricks_verb_ignores_already_installed() {
get_umu_bottle "shadow-line"
install_proton_winetricks_verb "already-installed"
}
test_get_umu_bottle_sets_environment
test_add_umu_launcher_records_backend_and_game_id
test_add_umu_launcher_records_extra_environment_flags
test_run_umu_game_uses_converted_path
test_install_crlf_file_normalizes_line_endings
test_install_proton_winetricks_verb_ignores_already_installed
printf 'UMU backend tests passed\n'