Tekken 3 updated. Should be self-updating from now on because it uses github releases.
This commit is contained in:
1 parent
7709ec83d8
commit
8e33ef63d0
6 files changed
+125
-186
No files matched your search
@@ -86,7 +86,7 @@ prepare_prism_bridge() {
|
||||
prepare_tekken3_prism_bridge() {
|
||||
local gameDir="$1"
|
||||
local prismHash
|
||||
# Access 1.0 and 1.0.1 ship this exact Prism 0.18.3 DLL.
|
||||
# Only this Prism 0.18.3 DLL is supported by the Tekken integration.
|
||||
prismHash="$(sha256sum "${gameDir}/T3Access/prism.dll")" || return 1
|
||||
if [[ "${prismHash%% *}" != "7c7d09c8c7306e8e1a0c46d603ccb2a391c194c77843400c11b746e7dd56a467" ]]; then
|
||||
echo "Tekken 3 Access has an unrecognized Prism DLL; update its AGM integration before launching." >&2
|
||||
|
||||
+13
-5
@@ -23,14 +23,22 @@ if ! tekkenSetupOutput="$(python3 "${scriptDir}/game-scripts/tekken3_setup.py" v
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Keep the base executable paired with the accessibility mod tested against it.
|
||||
# Keep the base executable pinned while resolving the latest stable access mod.
|
||||
tekkenPortCommit="b6e941378ad03dbd834fef340bd8d90bae6e430b"
|
||||
download "https://github.com/Muqaddas12/Tekken3-PC/archive/${tekkenPortCommit}.zip" \
|
||||
"https://github.com/BlindChallengerX/Tekken3-Access/releases/download/v1.0.1/Tekken3Access-1.0.1.zip"
|
||||
if ! tekkenSetupOutput="$(python3 "${scriptDir}/game-scripts/tekken3_setup.py" latest-release 2>&1)"; then
|
||||
alert "Tekken 3 release" "$tekkenSetupOutput"
|
||||
exit 1
|
||||
fi
|
||||
IFS=$'\t' read -r tekkenModVersion tekkenModUrl tekkenModHash <<< "$tekkenSetupOutput"
|
||||
if ! download "https://github.com/Muqaddas12/Tekken3-PC/archive/${tekkenPortCommit}.zip" "$tekkenModUrl"; then
|
||||
alert "Tekken 3 installation" "Could not download the Tekken 3 port or Access ${tekkenModVersion}."
|
||||
exit 1
|
||||
fi
|
||||
install_wine_bottle
|
||||
if ! tekkenSetupOutput="$(python3 "${scriptDir}/game-scripts/tekken3_setup.py" install \
|
||||
"$tekkenDiscDir" "${cache}/${tekkenPortCommit}.zip" "${cache}/Tekken3Access-1.0.1.zip" \
|
||||
"${WINEPREFIX}/drive_c/Program Files/Tekken 3 Access" 2>&1)"; then
|
||||
"$tekkenDiscDir" "${cache}/${tekkenPortCommit}.zip" "${cache}/${tekkenModUrl##*/}" \
|
||||
"${WINEPREFIX}/drive_c/Program Files/Tekken 3 Access" \
|
||||
--mod-sha256 "$tekkenModHash" --mod-version "$tekkenModVersion" 2>&1)"; then
|
||||
alert "Tekken 3 installation" "$tekkenSetupOutput"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -99,16 +99,19 @@ downloads. The disc files are read without modification, outside AGM's download
|
||||
cache. After installation, the extracted game data is sufficient for play.
|
||||
|
||||
The installer downloads the tested PC-port commit
|
||||
`b6e941378ad03dbd834fef340bd8d90bae6e430b` and Access 1.0.1, verifies the mod
|
||||
archive and the port executable/extractor, and uses native Linux Python 3 to
|
||||
extract game data. Allow approximately 1 GB for installed files, in addition to
|
||||
your original dump. Python 3 is an additional installer dependency.
|
||||
`b6e941378ad03dbd834fef340bd8d90bae6e430b` and the latest stable Access release
|
||||
from GitHub. It verifies the mod archive against GitHub's published SHA-256
|
||||
checksum, checks the supported mod layout and Prism DLL, verifies the pinned
|
||||
port executable/extractor, and uses native Linux Python 3 to extract game data.
|
||||
Allow approximately 1 GB for installed files, in addition to your original dump.
|
||||
Python 3 is an additional installer dependency.
|
||||
|
||||
Speech uses the same Prism 0.18.3 Speech Dispatcher Wine bridge as Arcadelux,
|
||||
with the build requirements listed above. The bridge is prepared on launch and
|
||||
cached for reuse. No NVDA replacement DLL or Windows Python is needed. Versions
|
||||
are pinned because the access mod hooks the port executable; upstream updates
|
||||
need a compatibility check before changing the pins.
|
||||
cached for reuse. No NVDA replacement DLL or Windows Python is needed. The base
|
||||
port stays pinned because the access mod hooks its executable. Mod releases are
|
||||
resolved automatically; a changed Prism DLL or unsupported archive layout stops
|
||||
installation or updating until AGM's integration is updated.
|
||||
|
||||
The game installs in `~/.local/wine64/drive_c/Program Files/Tekken 3 Access/`.
|
||||
Select **Tekken 3** in AGM to launch the access executable. It starts in a
|
||||
@@ -128,17 +131,25 @@ To update an existing AGM installation, close Tekken 3 and run from this checkou
|
||||
bash game-scripts/tekken3-update.sh
|
||||
```
|
||||
|
||||
This installs the verified Access 1.0.1 release without the original disc dump
|
||||
or another game-data extraction. It checks the installed port executable,
|
||||
This resolves the latest stable Access release on each run, without the original
|
||||
disc dump or another game-data extraction. It checks the installed port executable,
|
||||
preserves settings, controller selection, memory cards, logs and mod preferences,
|
||||
and refreshes the manual. Replaced files are backed up beside the game folder;
|
||||
the completion message gives the backup path. Future releases need a compatibility
|
||||
check before the pinned update is changed.
|
||||
the completion message gives the version and backup path. GitHub API failures,
|
||||
missing release assets or checksums, and incompatible archives stop the update;
|
||||
there is no fallback to a stale release. Routine mod releases need no version
|
||||
or checksum edits in AGM. A published checksum verifies the download's integrity,
|
||||
but does not establish that a future mod release works correctly under Wine.
|
||||
|
||||
In 1.0.1, Shift+F1 raises the distance hum volume and Control+F1 lowers it, with
|
||||
ten steps and zero for off. The chosen level is remembered. Fight sounds go
|
||||
quiet while the pause menu or command list is open and resume with the fight.
|
||||
|
||||
In 1.0.2, Shift+F2 raises music volume and Control+F2 lowers it. Level ten is
|
||||
the normal game volume; zero turns music off. The level is remembered and also
|
||||
applies to movie audio. Fighters' voices, hits, menu sounds and mod sounds keep
|
||||
their existing volume.
|
||||
|
||||
Before submitting changes, test installation with missing or incorrect disc
|
||||
files and with a valid USA dump. In the installed game, check the loaded
|
||||
announcement, menu and character selection, F1 distance/F2 health, fight sounds,
|
||||
@@ -150,6 +161,13 @@ After updating, check both hum-volume shortcuts, level zero, and persistence
|
||||
after restarting. Check sound suppression and resumption in a normal fight,
|
||||
Tekken Force and Tekken Ball, including the pause command list. Confirm your
|
||||
controller/keyboard selection and existing memory-card saves still work.
|
||||
Check both music-volume shortcuts, levels zero and ten, and persistence after
|
||||
restarting. Check music in fights and movies, and confirm voices, hits, menu
|
||||
sounds and mod sounds are unaffected.
|
||||
|
||||
Run the offline release, installer and updater checks with
|
||||
`python3 tests/tekken3_setup_tests.py`. These use temporary fixtures and do not
|
||||
access the network, start Wine or change installed games.
|
||||
|
||||
## AI-assisted contributions
|
||||
|
||||
|
||||
@@ -23,12 +23,17 @@ if ! mkdir -p "$cache"; then
|
||||
alert "Tekken 3 Update" "Could not create the download cache: ${cache}"
|
||||
exit 1
|
||||
fi
|
||||
if ! download "https://github.com/BlindChallengerX/Tekken3-Access/releases/download/v1.0.1/Tekken3Access-1.0.1.zip"; then
|
||||
alert "Tekken 3 Update" "Could not download Tekken 3 Access 1.0.1."
|
||||
if ! updateOutput="$(python3 "${scriptDir}/game-scripts/tekken3_setup.py" latest-release 2>&1)"; then
|
||||
alert "Tekken 3 Update" "$updateOutput"
|
||||
exit 1
|
||||
fi
|
||||
IFS=$'\t' read -r modVersion modUrl modHash <<< "$updateOutput"
|
||||
if ! download "$modUrl"; then
|
||||
alert "Tekken 3 Update" "Could not download Tekken 3 Access ${modVersion}."
|
||||
exit 1
|
||||
fi
|
||||
if ! updateOutput="$(python3 "${scriptDir}/game-scripts/tekken3_setup.py" update \
|
||||
"${cache}/Tekken3Access-1.0.1.zip" "$gamePath" 2>&1)"; then
|
||||
"${cache}/${modUrl##*/}" "$gamePath" --mod-sha256 "$modHash" --mod-version "$modVersion" 2>&1)"; then
|
||||
alert "Tekken 3 Update" "$updateOutput"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path, PurePosixPath
|
||||
import re
|
||||
@@ -11,13 +12,14 @@ import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from urllib.request import Request, urlopen
|
||||
from zipfile import BadZipFile, ZipFile
|
||||
|
||||
|
||||
PORT_COMMIT = "b6e941378ad03dbd834fef340bd8d90bae6e430b"
|
||||
PORT_EXE_HASH = "25d038f55d7a1c02a0e5bbcc97fe9b1738c8292198e7fa0d1b171b57129d18c0"
|
||||
SETUP_HASH = "c1e0430498b2f4212d7e04187e3ba822d3ac273de487cd35d93aa2ef823ac5aa"
|
||||
MOD_ARCHIVE_HASH = "7aacee1ce376dff83823a6fc9c6717ad31e94cd37fa85551a3a8e8048e709646"
|
||||
RELEASE_API = "https://api.github.com/repos/BlindChallengerX/Tekken3-Access/releases/latest"
|
||||
PRISM_HASH = "7c7d09c8c7306e8e1a0c46d603ccb2a391c194c77843400c11b746e7dd56a467"
|
||||
USA_EXE_HASH = "fbda8b68e5799dbef4af39a161783bc670c15b0aa0e87dce65e210717da19b8c"
|
||||
TRACK_SIZES = (632532768, 27701856, 28042896)
|
||||
@@ -26,6 +28,36 @@ CUE_NAME = "Tekken 3 (USA).cue"
|
||||
SECTOR_SIZE = 2352
|
||||
|
||||
|
||||
def parse_release(releaseInfo):
|
||||
if not isinstance(releaseInfo, dict) or releaseInfo.get("draft") is not False or releaseInfo.get("prerelease") is not False:
|
||||
raise ValueError("GitHub did not return a published stable Tekken 3 Access release.")
|
||||
versionMatch = re.fullmatch(r"v?(\d+(?:\.\d+)+)", str(releaseInfo.get("tag_name", "")))
|
||||
if not versionMatch:
|
||||
raise ValueError("The latest Tekken 3 Access release has an unsupported version tag.")
|
||||
version = versionMatch.group(1)
|
||||
archiveName = f"Tekken3Access-{version}.zip"
|
||||
assets = releaseInfo.get("assets")
|
||||
if not isinstance(assets, list):
|
||||
raise ValueError("The latest release has no download assets.")
|
||||
matchingAssets = [asset for asset in assets if isinstance(asset, dict) and asset.get("name") == archiveName]
|
||||
if len(matchingAssets) != 1:
|
||||
raise ValueError(f"The latest release must contain exactly one {archiveName} download.")
|
||||
asset = matchingAssets[0]
|
||||
downloadUrl = f"https://github.com/BlindChallengerX/Tekken3-Access/releases/download/{releaseInfo['tag_name']}/{archiveName}"
|
||||
if asset.get("browser_download_url") != downloadUrl:
|
||||
raise ValueError("The latest release has an unexpected download URL.")
|
||||
digestMatch = re.fullmatch(r"sha256:([0-9a-f]{64})", str(asset.get("digest", "")))
|
||||
if not digestMatch:
|
||||
raise ValueError("GitHub did not supply a SHA-256 checksum for the latest release. Nothing was installed.")
|
||||
return version, downloadUrl, digestMatch.group(1)
|
||||
|
||||
|
||||
def latest_release():
|
||||
request = Request(RELEASE_API, headers={"Accept": "application/vnd.github+json", "User-Agent": "audiogame-manager"})
|
||||
with urlopen(request, timeout=30) as response:
|
||||
return parse_release(json.load(response))
|
||||
|
||||
|
||||
def validate_disc(discDir):
|
||||
missingFiles = [name for name in (CUE_NAME, *TRACK_NAMES) if not (discDir / name).is_file()]
|
||||
if missingFiles:
|
||||
@@ -73,7 +105,26 @@ def extract_archive(archivePath, destination, stripRoot=None):
|
||||
|
||||
def verify_hash(path, expectedHash):
|
||||
if hashlib.sha256(path.read_bytes()).hexdigest() != expectedHash:
|
||||
raise ValueError(f"{path.name} does not match the tested version. Remove the cached download and retry.")
|
||||
raise ValueError(f"{path.name} does not match the expected checksum. Remove the cached download and retry.")
|
||||
|
||||
|
||||
def validate_mod(gameRoot):
|
||||
"""Accept the supported mod layout without replacing game data or user state."""
|
||||
modFiles = {
|
||||
"INSTALL.txt", "Play Tekken 3 Access.exe", "tekken 3 pc link.txt",
|
||||
"Use controller.bat", "Use keyboard.bat", "T3Access/T3Access.dll",
|
||||
"T3Access/prism.dll", "T3Access/README.txt", "T3Access/movies.txt",
|
||||
}
|
||||
for path in gameRoot.rglob("*"):
|
||||
if not path.is_file():
|
||||
continue
|
||||
relativePath = path.relative_to(gameRoot)
|
||||
if relativePath.as_posix() not in modFiles and not (relativePath.parent == Path("T3Access/ad") and relativePath.suffix == ".wav"):
|
||||
raise ValueError(f"Unsupported release file: {relativePath}. Update its AGM integration before installing this release.")
|
||||
for name in ("Play Tekken 3 Access.exe", "T3Access/T3Access.dll", "T3Access/README.txt"):
|
||||
if not (gameRoot / name).is_file():
|
||||
raise ValueError(f"The release is missing {name}.")
|
||||
verify_hash(gameRoot / "T3Access/prism.dll", PRISM_HASH)
|
||||
|
||||
|
||||
def write_manual(gameRoot):
|
||||
@@ -115,17 +166,14 @@ def validate_installation(destination):
|
||||
raise ValueError(f"The existing installation is missing {name}. Install Tekken 3 through AGM first.")
|
||||
|
||||
|
||||
def update_game(modArchive, destination):
|
||||
def update_game(modArchive, destination, modHash, modVersion):
|
||||
validate_installation(destination)
|
||||
verify_hash(modArchive, MOD_ARCHIVE_HASH)
|
||||
verify_hash(modArchive, modHash)
|
||||
with tempfile.TemporaryDirectory(prefix=".tekken3-update-", dir=destination.parent) as temporaryDir:
|
||||
gameRoot = Path(temporaryDir) / "mod"
|
||||
gameRoot.mkdir()
|
||||
extract_archive(modArchive, gameRoot)
|
||||
for name in ("Play Tekken 3 Access.exe", "T3Access/T3Access.dll", "T3Access/README.txt"):
|
||||
if not (gameRoot / name).is_file():
|
||||
raise ValueError(f"The update is missing {name}.")
|
||||
verify_hash(gameRoot / "T3Access/prism.dll", PRISM_HASH)
|
||||
validate_mod(gameRoot)
|
||||
write_manual(gameRoot)
|
||||
relativeFiles = sorted(path.relative_to(gameRoot) for path in gameRoot.rglob("*") if path.is_file())
|
||||
for relativePath in relativeFiles:
|
||||
@@ -169,12 +217,12 @@ def update_game(modArchive, destination):
|
||||
except OSError as restoreError:
|
||||
raise OSError(f"Update and restoration failed. Restore files from {backupRoot}. {restoreError}") from error
|
||||
raise OSError(f"Update failed; replaced files restored. Backup: {backupRoot}. {error}") from error
|
||||
print(f"Updated Tekken 3 Access to 1.0.1. Backup of replaced files: {backupRoot}")
|
||||
print(f"Updated Tekken 3 Access to {modVersion}. Backup of replaced files: {backupRoot}")
|
||||
|
||||
|
||||
def install_game(discDir, portArchive, modArchive, destination):
|
||||
def install_game(discDir, portArchive, modArchive, destination, modHash, modVersion):
|
||||
validate_disc(discDir)
|
||||
verify_hash(modArchive, MOD_ARCHIVE_HASH)
|
||||
verify_hash(modArchive, modHash)
|
||||
if destination.exists() and any(destination.iterdir()):
|
||||
raise ValueError(f"An installation already exists at {destination}. It was left untouched; back it up before reinstalling.")
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
@@ -184,7 +232,11 @@ def install_game(discDir, portArchive, modArchive, destination):
|
||||
extract_archive(portArchive, gameRoot, f"Tekken3-PC-{PORT_COMMIT}")
|
||||
verify_hash(gameRoot / "Tekken3.exe", PORT_EXE_HASH)
|
||||
verify_hash(gameRoot / "setup_game_data.py", SETUP_HASH)
|
||||
extract_archive(modArchive, gameRoot)
|
||||
modRoot = Path(temporaryDir) / "mod"
|
||||
modRoot.mkdir()
|
||||
extract_archive(modArchive, modRoot)
|
||||
validate_mod(modRoot)
|
||||
shutil.copytree(modRoot, gameRoot, dirs_exist_ok=True)
|
||||
subprocess.run([sys.executable, str(gameRoot / "setup_game_data.py"), str(discDir.resolve())], check=True)
|
||||
for requiredFile in ("Play Tekken 3 Access.exe", "T3Access/T3Access.dll", "T3Access/prism.dll", "game_data/disc.cue"):
|
||||
if not (gameRoot / requiredFile).is_file():
|
||||
@@ -196,12 +248,13 @@ def install_game(discDir, portArchive, modArchive, destination):
|
||||
if destination.exists():
|
||||
destination.rmdir() # Only an empty destination is accepted above.
|
||||
gameRoot.rename(destination)
|
||||
print(f"Installed Tekken 3 Access in {destination}")
|
||||
print(f"Installed Tekken 3 Access {modVersion} in {destination}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
subparsers.add_parser("latest-release")
|
||||
validateParser = subparsers.add_parser("validate-disc")
|
||||
validateParser.add_argument("discDir", type=Path)
|
||||
installedParser = subparsers.add_parser("validate-installation")
|
||||
@@ -212,17 +265,22 @@ def main():
|
||||
installParser = subparsers.add_parser("install")
|
||||
for name in ("discDir", "portArchive", "modArchive", "destination"):
|
||||
installParser.add_argument(name, type=Path)
|
||||
for commandParser in (updateParser, installParser):
|
||||
commandParser.add_argument("--mod-sha256", required=True)
|
||||
commandParser.add_argument("--mod-version", required=True)
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
if args.command == "validate-disc":
|
||||
if args.command == "latest-release":
|
||||
print("\t".join(latest_release()))
|
||||
elif args.command == "validate-disc":
|
||||
validate_disc(args.discDir)
|
||||
print("Tekken 3 (USA) disc files validated.")
|
||||
elif args.command == "validate-installation":
|
||||
validate_installation(args.destination)
|
||||
elif args.command == "update":
|
||||
update_game(args.modArchive, args.destination)
|
||||
update_game(args.modArchive, args.destination, args.mod_sha256, args.mod_version)
|
||||
else:
|
||||
install_game(args.discDir, args.portArchive, args.modArchive, args.destination)
|
||||
install_game(args.discDir, args.portArchive, args.modArchive, args.destination, args.mod_sha256, args.mod_version)
|
||||
except (OSError, ValueError, BadZipFile, subprocess.CalledProcessError) as error:
|
||||
print(f"Tekken 3 setup failed: {error}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repoRoot="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
testRoot="$(mktemp -d)"
|
||||
trap 'rm -rf "$testRoot"' EXIT
|
||||
|
||||
export HOME="${testRoot}/home"
|
||||
export XDG_DATA_HOME="${HOME}/.local/share"
|
||||
export XDG_CONFIG_HOME="${HOME}/.config"
|
||||
export XDG_CACHE_HOME="${HOME}/.cache"
|
||||
export DISPLAY=""
|
||||
export cache="${XDG_CACHE_HOME}/audiogame-manager"
|
||||
export configFile="${XDG_CONFIG_HOME}/storm-games/audiogame-manager/games.conf"
|
||||
export game="Shadow Line"
|
||||
export scriptDir="$repoRoot"
|
||||
mkdir -p "$cache" "${configFile%/*}" "${testRoot}/bin"
|
||||
touch "$configFile"
|
||||
|
||||
cat > "${testRoot}/bin/umu-run" <<'STUB'
|
||||
#!/usr/bin/env bash
|
||||
printf '%s|%s|%s|%s\n' "$WINEPREFIX" "$GAMEID" "${STORE:-}" "$*" >> "$UMU_STUB_LOG"
|
||||
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"
|
||||
|
||||
cat > "${testRoot}/bin/gamemoderun" <<'STUB'
|
||||
#!/usr/bin/env bash
|
||||
printf '%s\n' "$*" >> "$GAMEMODE_STUB_LOG"
|
||||
exec "$@"
|
||||
STUB
|
||||
chmod +x "${testRoot}/bin/gamemoderun"
|
||||
|
||||
cat > "${testRoot}/bin/wine" <<'STUB'
|
||||
#!/usr/bin/env bash
|
||||
if [[ "${1:-}" == "winepath" || "${1:-}" == "winepath.exe" ]]; then
|
||||
shift
|
||||
fi
|
||||
if [[ "${1:-}" == "-u" ]]; then
|
||||
input="$2"
|
||||
path="${input#c:\\}"
|
||||
path="${path//\\//}"
|
||||
printf '%s/drive_c/%s\n' "$WINEPREFIX" "$path"
|
||||
exit 0
|
||||
fi
|
||||
printf 'wine %s\n' "$*" >> "$WINE_STUB_LOG"
|
||||
STUB
|
||||
chmod +x "${testRoot}/bin/wine"
|
||||
|
||||
cat > "${testRoot}/bin/wineserver" <<'STUB'
|
||||
#!/usr/bin/env bash
|
||||
printf 'wineserver %s\n' "$*" >> "$WINE_STUB_LOG"
|
||||
STUB
|
||||
chmod +x "${testRoot}/bin/wineserver"
|
||||
|
||||
export PATH="${testRoot}/bin:$PATH"
|
||||
export UMU_STUB_LOG="${testRoot}/umu.log"
|
||||
export WINE_STUB_LOG="${testRoot}/wine.log"
|
||||
export GAMEMODE_STUB_LOG="${testRoot}/gamemode.log"
|
||||
|
||||
# shellcheck source=.includes/gamemode.sh
|
||||
source "${repoRoot}/.includes/gamemode.sh"
|
||||
# shellcheck source=.includes/proton.sh
|
||||
source "${repoRoot}/.includes/proton.sh"
|
||||
|
||||
assert_equals() {
|
||||
local expected="$1"
|
||||
local actual="$2"
|
||||
local message="$3"
|
||||
if [[ "$expected" != "$actual" ]]; then
|
||||
printf 'FAIL: %s\nexpected: %s\nactual: %s\n' "$message" "$expected" "$actual" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_file_contains() {
|
||||
local file="$1"
|
||||
local pattern="$2"
|
||||
local message="$3"
|
||||
if ! grep -Fq "$pattern" "$file"; then
|
||||
printf 'FAIL: %s\nmissing pattern: %s\nfile contents:\n' "$message" "$pattern" >&2
|
||||
cat "$file" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
test_get_umu_bottle_sets_environment() {
|
||||
get_umu_bottle "shadow-line"
|
||||
assert_equals "${XDG_DATA_HOME}/audiogame-manager/protonBottles/shadow-line" "$WINEPREFIX" "WINEPREFIX points at AGM proton bottle"
|
||||
assert_equals "shadow-line" "$GAMEID" "GAMEID is exported"
|
||||
assert_equals "none" "$STORE" "STORE defaults to none"
|
||||
assert_equals ":0" "$DISPLAY" "DISPLAY defaults to :0"
|
||||
}
|
||||
|
||||
test_add_umu_launcher_records_backend_and_game_id() {
|
||||
get_umu_bottle "shadow-line"
|
||||
add_umu_launcher "shadow-line" 'c:\Program Files (x86)\GalaxyLaboratory\ShadowRine_FullVoice\play_sr.exe'
|
||||
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"
|
||||
touch "${WINEPREFIX}/drive_c/Program Files (x86)/GalaxyLaboratory/ShadowRine_FullVoice/play_sr.exe"
|
||||
run_umu_game 'c:\Program Files (x86)\GalaxyLaboratory\ShadowRine_FullVoice\play_sr.exe'
|
||||
assert_file_contains "$GAMEMODE_STUB_LOG" "umu-run ${WINEPREFIX}/drive_c/Program Files (x86)/GalaxyLaboratory/ShadowRine_FullVoice/play_sr.exe" "UMU launches through GameMode"
|
||||
assert_file_contains "$UMU_STUB_LOG" "${WINEPREFIX}|shadow-line|none|${WINEPREFIX}/drive_c/Program Files (x86)/GalaxyLaboratory/ShadowRine_FullVoice/play_sr.exe" "UMU launches converted exe path"
|
||||
}
|
||||
|
||||
test_install_crlf_file_normalizes_line_endings() {
|
||||
local sourceFile="${testRoot}/language_en.dat"
|
||||
local destFile="${testRoot}/installed/language_en.dat"
|
||||
local expectedFile="${testRoot}/expected-language_en.dat"
|
||||
printf 'line one\nline two\n' > "$sourceFile"
|
||||
printf 'line one\r\nline two\r\n' > "$expectedFile"
|
||||
|
||||
install_crlf_file "$sourceFile" "$destFile"
|
||||
|
||||
if ! cmp -s "$expectedFile" "$destFile"; then
|
||||
printf 'FAIL: Translation file is installed with CRLF line endings\n' >&2
|
||||
exit 1
|
||||
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'
|
||||
Reference in new issue
Block a user