mirror of
https://github.com/tcsenpai/UWINE.git
synced 2025-06-07 03:55:21 +00:00
Merge pull request #23 from R1kaB3rN/args
Change usage for Python rewrite
This commit is contained in:
commit
4f64389ec4
1396
gamelauncher_test.py
1396
gamelauncher_test.py
File diff suppressed because it is too large
Load Diff
108
ulwgl-run
108
ulwgl-run
@ -1,108 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# use for debug only.
|
|
||||||
# set -x
|
|
||||||
|
|
||||||
if [ -z "$1" ] || [ -z "$WINEPREFIX" ] || [ -z "$GAMEID" ] || [ -z "$PROTONPATH" ]; then
|
|
||||||
echo "Usage: WINEPREFIX=<wine-prefix-path> GAMEID=<ulwgl-id> PROTONPATH=<proton-version-path> ./gamelauncher.sh <executable-path> <arguments>"
|
|
||||||
echo "Ex:"
|
|
||||||
echo "WINEPREFIX=$HOME/Games/epic-games-store GAMEID=egs PROTONPATH=\"$HOME/.steam/steam/compatibilitytools.d/GE-Proton8-28\" ./gamelauncher.sh \"$HOME/Games/epic-games-store/drive_c/Program Files (x86)/Epic Games/Launcher/Portal/Binaries/Win32/EpicGamesLauncher.exe\" \"-opengl -SkipBuildPatchPrereq\""
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
me="$(readlink -f "$0")"
|
|
||||||
here="${me%/*}"
|
|
||||||
|
|
||||||
if [ "$WINEPREFIX" ]; then
|
|
||||||
if [ ! -d "$WINEPREFIX" ]; then
|
|
||||||
mkdir -p "$WINEPREFIX"
|
|
||||||
export PROTON_DLL_COPY="*"
|
|
||||||
fi
|
|
||||||
if [ ! -d "$WINEPREFIX"/pfx ]; then
|
|
||||||
ln -s "$WINEPREFIX" "$WINEPREFIX"/pfx > /dev/null 2>&1
|
|
||||||
fi
|
|
||||||
if [ ! -f "$WINEPREFIX"/tracked_files ]; then
|
|
||||||
touch "$WINEPREFIX"/tracked_files
|
|
||||||
fi
|
|
||||||
if [ ! -f "$WINEPREFIX/dosdevices/" ]; then
|
|
||||||
mkdir -p "$WINEPREFIX"/dosdevices
|
|
||||||
ln -s "../drive_c" "$WINEPREFIX/dosdevices/c:" > /dev/null 2>&1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$PROTONPATH" ]; then
|
|
||||||
if [ ! -d "$PROTONPATH" ]; then
|
|
||||||
echo "ERROR: $PROTONPATH is invalid, aborting!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
export ULWGL_ID="$GAMEID"
|
|
||||||
export STEAM_COMPAT_APP_ID="0"
|
|
||||||
numcheck='^[0-9]+$'
|
|
||||||
if echo "$ULWGL_ID" | cut -d "-" -f 2 | grep -Eq "$numcheck"; then
|
|
||||||
STEAM_COMPAT_APP_ID=$(echo "$ULWGL_ID" | cut -d "-" -f 2)
|
|
||||||
export STEAM_COMPAT_APP_ID
|
|
||||||
fi
|
|
||||||
export SteamAppId="$STEAM_COMPAT_APP_ID"
|
|
||||||
export SteamGameId="$STEAM_COMPAT_APP_ID"
|
|
||||||
|
|
||||||
# TODO: Ideally this should be the main game install path, which is often, but not always the path of the game's executable.
|
|
||||||
if [ -z "$STEAM_COMPAT_INSTALL_PATH" ]; then
|
|
||||||
exepath="$(readlink -f "$1")"
|
|
||||||
gameinstallpath="${exepath%/*}"
|
|
||||||
export STEAM_COMPAT_INSTALL_PATH="$gameinstallpath"
|
|
||||||
fi
|
|
||||||
|
|
||||||
compat_lib_path=$(findmnt -T "$STEAM_COMPAT_INSTALL_PATH" | tail -n 1 | awk '{ print $1 }')
|
|
||||||
if [ "$compat_lib_path" != "/" ]; then
|
|
||||||
export STEAM_COMPAT_LIBRARY_PATHS="${STEAM_COMPAT_LIBRARY_PATHS:+"${STEAM_COMPAT_LIBRARY_PATHS}:"}$compat_lib_path"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$STEAM_RUNTIME_LIBRARY_PATH" ]; then
|
|
||||||
# The following info taken from steam ~/.local/share/ubuntu12_32/steam-runtime/run.sh
|
|
||||||
host_library_paths=
|
|
||||||
exit_status=0
|
|
||||||
ldconfig_output=$(/sbin/ldconfig -XNv 2> /dev/null; exit $?) || exit_status=$?
|
|
||||||
if [ $exit_status != 0 ]; then
|
|
||||||
echo "Warning: An unexpected error occurred while executing \"/sbin/ldconfig -XNv\", the exit status was $exit_status"
|
|
||||||
fi
|
|
||||||
|
|
||||||
while read -r line; do
|
|
||||||
# If line starts with a leading / and contains :, it's a new path prefix
|
|
||||||
case "$line" in /*:*)
|
|
||||||
library_path_prefix=$(echo "$line" | cut -d: -f1)
|
|
||||||
host_library_paths=$host_library_paths$library_path_prefix:
|
|
||||||
esac
|
|
||||||
done <<EOLDCONFIG
|
|
||||||
$ldconfig_output
|
|
||||||
EOLDCONFIG
|
|
||||||
|
|
||||||
host_library_paths="${LD_LIBRARY_PATH:+"${LD_LIBRARY_PATH}:"}$host_library_paths"
|
|
||||||
steam_runtime_library_paths="${STEAM_COMPAT_INSTALL_PATH}:${host_library_paths}"
|
|
||||||
export STEAM_RUNTIME_LIBRARY_PATH="$steam_runtime_library_paths"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$PROTON_VERB" ]; then
|
|
||||||
export PROTON_VERB="waitforexitandrun"
|
|
||||||
fi
|
|
||||||
|
|
||||||
export STEAM_COMPAT_CLIENT_INSTALL_PATH=''
|
|
||||||
export STEAM_COMPAT_DATA_PATH="$WINEPREFIX"
|
|
||||||
export STEAM_COMPAT_SHADER_PATH="$STEAM_COMPAT_DATA_PATH"/shadercache
|
|
||||||
|
|
||||||
export PROTON_CRASH_REPORT_DIR='/tmp/ULWGL_crashreports'
|
|
||||||
export FONTCONFIG_PATH=''
|
|
||||||
|
|
||||||
export EXE="$1"
|
|
||||||
if [ "$EXE" = "createprefix" ]; then
|
|
||||||
# Hack, leave empty.
|
|
||||||
# forces proton to create a prefix without actually running anything.
|
|
||||||
EXE=""
|
|
||||||
fi
|
|
||||||
shift 1
|
|
||||||
|
|
||||||
export STEAM_COMPAT_TOOL_PATHS="$PROTONPATH:$here"
|
|
||||||
export STEAM_COMPAT_MOUNTS="$PROTONPATH:$here"
|
|
||||||
|
|
||||||
"$here"/ULWGL "--verb=$PROTON_VERB" -- "$PROTONPATH"/proton "$PROTON_VERB" "$EXE" "$@"
|
|
108
ulwgl-run-posix
Executable file
108
ulwgl-run-posix
Executable file
@ -0,0 +1,108 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# use for debug only.
|
||||||
|
# set -x
|
||||||
|
|
||||||
|
if [ -z "$1" ] || [ -z "$WINEPREFIX" ] || [ -z "$GAMEID" ] || [ -z "$PROTONPATH" ]; then
|
||||||
|
echo "Usage: WINEPREFIX=<wine-prefix-path> GAMEID=<ulwgl-id> PROTONPATH=<proton-version-path> ./gamelauncher.sh <executable-path> <arguments>"
|
||||||
|
echo "Ex:"
|
||||||
|
echo "WINEPREFIX=$HOME/Games/epic-games-store GAMEID=egs PROTONPATH=\"$HOME/.steam/steam/compatibilitytools.d/GE-Proton8-28\" ./gamelauncher.sh \"$HOME/Games/epic-games-store/drive_c/Program Files (x86)/Epic Games/Launcher/Portal/Binaries/Win32/EpicGamesLauncher.exe\" \"-opengl -SkipBuildPatchPrereq\""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
me="$(readlink -f "$0")"
|
||||||
|
here="${me%/*}"
|
||||||
|
|
||||||
|
if [ "$WINEPREFIX" ]; then
|
||||||
|
if [ ! -d "$WINEPREFIX" ]; then
|
||||||
|
mkdir -p "$WINEPREFIX"
|
||||||
|
export PROTON_DLL_COPY="*"
|
||||||
|
fi
|
||||||
|
if [ ! -d "$WINEPREFIX"/pfx ]; then
|
||||||
|
ln -s "$WINEPREFIX" "$WINEPREFIX"/pfx > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
if [ ! -f "$WINEPREFIX"/tracked_files ]; then
|
||||||
|
touch "$WINEPREFIX"/tracked_files
|
||||||
|
fi
|
||||||
|
if [ ! -f "$WINEPREFIX/dosdevices/" ]; then
|
||||||
|
mkdir -p "$WINEPREFIX"/dosdevices
|
||||||
|
ln -s "../drive_c" "$WINEPREFIX/dosdevices/c:" > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$PROTONPATH" ]; then
|
||||||
|
if [ ! -d "$PROTONPATH" ]; then
|
||||||
|
echo "ERROR: $PROTONPATH is invalid, aborting!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
export ULWGL_ID="$GAMEID"
|
||||||
|
export STEAM_COMPAT_APP_ID="0"
|
||||||
|
numcheck='^[0-9]+$'
|
||||||
|
if echo "$ULWGL_ID" | cut -d "-" -f 2 | grep -Eq "$numcheck"; then
|
||||||
|
STEAM_COMPAT_APP_ID=$(echo "$ULWGL_ID" | cut -d "-" -f 2)
|
||||||
|
export STEAM_COMPAT_APP_ID
|
||||||
|
fi
|
||||||
|
export SteamAppId="$STEAM_COMPAT_APP_ID"
|
||||||
|
export SteamGameId="$STEAM_COMPAT_APP_ID"
|
||||||
|
|
||||||
|
# TODO: Ideally this should be the main game install path, which is often, but not always the path of the game's executable.
|
||||||
|
if [ -z "$STEAM_COMPAT_INSTALL_PATH" ]; then
|
||||||
|
exepath="$(readlink -f "$1")"
|
||||||
|
gameinstallpath="${exepath%/*}"
|
||||||
|
export STEAM_COMPAT_INSTALL_PATH="$gameinstallpath"
|
||||||
|
fi
|
||||||
|
|
||||||
|
compat_lib_path=$(findmnt -T "$STEAM_COMPAT_INSTALL_PATH" | tail -n 1 | awk '{ print $1 }')
|
||||||
|
if [ "$compat_lib_path" != "/" ]; then
|
||||||
|
export STEAM_COMPAT_LIBRARY_PATHS="${STEAM_COMPAT_LIBRARY_PATHS:+"${STEAM_COMPAT_LIBRARY_PATHS}:"}$compat_lib_path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$STEAM_RUNTIME_LIBRARY_PATH" ]; then
|
||||||
|
# The following info taken from steam ~/.local/share/ubuntu12_32/steam-runtime/run.sh
|
||||||
|
host_library_paths=
|
||||||
|
exit_status=0
|
||||||
|
ldconfig_output=$(/sbin/ldconfig -XNv 2> /dev/null; exit $?) || exit_status=$?
|
||||||
|
if [ $exit_status != 0 ]; then
|
||||||
|
echo "Warning: An unexpected error occurred while executing \"/sbin/ldconfig -XNv\", the exit status was $exit_status"
|
||||||
|
fi
|
||||||
|
|
||||||
|
while read -r line; do
|
||||||
|
# If line starts with a leading / and contains :, it's a new path prefix
|
||||||
|
case "$line" in /*:*)
|
||||||
|
library_path_prefix=$(echo "$line" | cut -d: -f1)
|
||||||
|
host_library_paths=$host_library_paths$library_path_prefix:
|
||||||
|
esac
|
||||||
|
done <<EOLDCONFIG
|
||||||
|
$ldconfig_output
|
||||||
|
EOLDCONFIG
|
||||||
|
|
||||||
|
host_library_paths="${LD_LIBRARY_PATH:+"${LD_LIBRARY_PATH}:"}$host_library_paths"
|
||||||
|
steam_runtime_library_paths="${STEAM_COMPAT_INSTALL_PATH}:${host_library_paths}"
|
||||||
|
export STEAM_RUNTIME_LIBRARY_PATH="$steam_runtime_library_paths"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$PROTON_VERB" ]; then
|
||||||
|
export PROTON_VERB="waitforexitandrun"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export STEAM_COMPAT_CLIENT_INSTALL_PATH=''
|
||||||
|
export STEAM_COMPAT_DATA_PATH="$WINEPREFIX"
|
||||||
|
export STEAM_COMPAT_SHADER_PATH="$STEAM_COMPAT_DATA_PATH"/shadercache
|
||||||
|
|
||||||
|
export PROTON_CRASH_REPORT_DIR='/tmp/ULWGL_crashreports'
|
||||||
|
export FONTCONFIG_PATH=''
|
||||||
|
|
||||||
|
export EXE="$1"
|
||||||
|
if [ "$EXE" = "createprefix" ]; then
|
||||||
|
# Hack, leave empty.
|
||||||
|
# forces proton to create a prefix without actually running anything.
|
||||||
|
EXE=""
|
||||||
|
fi
|
||||||
|
shift 1
|
||||||
|
|
||||||
|
export STEAM_COMPAT_TOOL_PATHS="$PROTONPATH:$here"
|
||||||
|
export STEAM_COMPAT_MOUNTS="$PROTONPATH:$here"
|
||||||
|
|
||||||
|
"$here"/ULWGL "--verb=$PROTON_VERB" -- "$PROTONPATH"/proton "$PROTON_VERB" "$EXE" "$@"
|
@ -3,7 +3,7 @@ from pathlib import Path
|
|||||||
from typing import Dict, Set
|
from typing import Dict, Set
|
||||||
|
|
||||||
|
|
||||||
def enable_steam_game_drive(env: Dict[str, str]):
|
def enable_steam_game_drive(env: Dict[str, str]) -> Dict[str, str]:
|
||||||
"""Enable Steam Game Drive functionality.
|
"""Enable Steam Game Drive functionality.
|
||||||
|
|
||||||
Expects STEAM_COMPAT_INSTALL_PATH to be set
|
Expects STEAM_COMPAT_INSTALL_PATH to be set
|
@ -2,83 +2,102 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import argparse
|
import argparse
|
||||||
from argparse import ArgumentParser, _ArgumentGroup, Namespace
|
from argparse import ArgumentParser, Namespace
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import tomllib
|
import tomllib
|
||||||
from typing import Dict, Any, List, Set
|
from typing import Dict, Any, List, Set, Union, Tuple
|
||||||
import gamelauncher_plugins
|
import ulwgl_plugins
|
||||||
from re import match
|
from re import match
|
||||||
|
|
||||||
# TODO: Only set the environment variables that are not empty
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
def parse_args() -> Namespace: # noqa: D103
|
def parse_args() -> Union[Namespace, Tuple[str, List[str]]]: # noqa: D103
|
||||||
stores: List[str] = [
|
opt_args: Set[str] = {"--help", "-h", "--config"}
|
||||||
"amazon",
|
|
||||||
"battlenet",
|
|
||||||
"ea",
|
|
||||||
"egs",
|
|
||||||
"gog",
|
|
||||||
"humble",
|
|
||||||
"itchio",
|
|
||||||
"ubisoft",
|
|
||||||
]
|
|
||||||
exe: str = Path(__file__).name
|
exe: str = Path(__file__).name
|
||||||
usage: str = """
|
usage: str = f"""
|
||||||
example usage:
|
example usage:
|
||||||
{} --config example.toml
|
WINEPREFIX= GAMEID= PROTONPATH= {exe} /home/foo/example.exe
|
||||||
{} --config /home/foo/example.toml --options '-opengl'
|
WINEPREFIX= GAMEID= PROTONPATH= {exe} /home/foo/example.exe -opengl
|
||||||
WINEPREFIX= GAMEID= PROTONPATH= {} --exe /home/foo/example.exe --options '-opengl'
|
WINEPREFIX= GAMEID= PROTONPATH= {exe} ""
|
||||||
WINEPREFIX= GAMEID= PROTONPATH= {} --exe /home/foo/example.exe --store gog
|
WINEPREFIX= GAMEID= PROTONPATH= PROTON_VERB= {exe} /home/foo/example.exe
|
||||||
WINEPREFIX= GAMEID= PROTONPATH= {} --exe ""
|
WINEPREFIX= GAMEID= PROTONPATH= STORE= {exe} /home/foo/example.exe
|
||||||
WINEPREFIX= GAMEID= PROTONPATH= {} --exe /home/foo/example.exe --verb waitforexitandrun
|
{exe} --config /home/foo/example.toml
|
||||||
""".format(exe, exe, exe, exe, exe, exe)
|
"""
|
||||||
|
|
||||||
parser: ArgumentParser = argparse.ArgumentParser(
|
parser: ArgumentParser = argparse.ArgumentParser(
|
||||||
description="Unified Linux Wine Game Launcher",
|
description="Unified Linux Wine Game Launcher",
|
||||||
epilog=usage,
|
epilog=usage,
|
||||||
formatter_class=argparse.RawTextHelpFormatter,
|
formatter_class=argparse.RawTextHelpFormatter,
|
||||||
)
|
)
|
||||||
group: _ArgumentGroup = parser.add_mutually_exclusive_group(required=True)
|
parser.add_argument("--config", help="path to TOML file")
|
||||||
group.add_argument("--config", help="path to TOML file")
|
|
||||||
group.add_argument(
|
|
||||||
"--exe",
|
|
||||||
help="path to game executable\npass an empty string to create a prefix",
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--verb",
|
|
||||||
help="a verb to pass to Proton (default: waitforexitandrun)",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--options",
|
|
||||||
help="launch options for game executable\nNOTE: options must be wrapped in quotes",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--store",
|
|
||||||
help=f"the store of the game executable\nNOTE: will override the store specified in config\nexamples: {stores}",
|
|
||||||
)
|
|
||||||
|
|
||||||
return parser.parse_args(sys.argv[1:])
|
if not sys.argv[1:]:
|
||||||
|
err: str = "Please see project README.md for more info and examples.\nhttps://github.com/Open-Wine-Components/ULWGL-launcher"
|
||||||
|
parser.print_help()
|
||||||
|
raise SystemExit(err)
|
||||||
|
|
||||||
|
if sys.argv[1:][0] in opt_args:
|
||||||
|
return parser.parse_args(sys.argv[1:])
|
||||||
|
|
||||||
|
return (sys.argv[1], sys.argv[2:])
|
||||||
|
|
||||||
|
|
||||||
def _setup_pfx(path: str) -> None:
|
def setup_pfx(path: str) -> None:
|
||||||
"""Create a symlink to the WINE prefix and tracked_files file."""
|
"""Create a symlink to the WINE prefix and tracked_files file."""
|
||||||
if not (Path(path + "/pfx")).expanduser().is_symlink():
|
if not (Path(path + "/pfx")).expanduser().is_symlink():
|
||||||
# When creating the symlink, we want it to be in expanded form when passed unexpanded paths
|
# When creating the symlink, we want it to be in expanded form when passed unexpanded paths
|
||||||
# Example: pfx -> /home/.wine
|
# Example: pfx -> /home/foo/.wine
|
||||||
# NOTE: When parsing a config file, an error can be raised if the prefix doesn't already exist
|
# NOTE: When parsing a config file, an error can be raised if the prefix doesn't already exist
|
||||||
Path(path + "/pfx").expanduser().symlink_to(Path(path).expanduser())
|
Path(path + "/pfx").expanduser().symlink_to(Path(path).expanduser())
|
||||||
Path(path + "/tracked_files").expanduser().touch()
|
Path(path + "/tracked_files").expanduser().touch()
|
||||||
|
|
||||||
|
|
||||||
def check_env(env: Dict[str, str]) -> Dict[str, str]:
|
def check_env(
|
||||||
|
env: Dict[str, str], toml: Dict[str, Any] = None
|
||||||
|
) -> Union[Dict[str, str], Dict[str, Any]]:
|
||||||
"""Before executing a game, check for environment variables and set them.
|
"""Before executing a game, check for environment variables and set them.
|
||||||
|
|
||||||
WINEPREFIX, GAMEID and PROTONPATH are strictly required.
|
WINEPREFIX, GAMEID and PROTONPATH are strictly required.
|
||||||
"""
|
"""
|
||||||
|
if toml:
|
||||||
|
# Check for required or empty key/value pairs when reading a TOML config
|
||||||
|
# NOTE: Casing matters in the config and we don't check if the game id is set
|
||||||
|
table: str = "ulwgl"
|
||||||
|
required_keys: List[str] = ["proton", "prefix", "exe"]
|
||||||
|
|
||||||
|
if table not in toml:
|
||||||
|
err: str = f"Table '{table}' in TOML is not defined."
|
||||||
|
raise ValueError(err)
|
||||||
|
|
||||||
|
for key in required_keys:
|
||||||
|
if key not in toml[table]:
|
||||||
|
err: str = f"The following key in table '{table}' is required: {key}"
|
||||||
|
raise ValueError(err)
|
||||||
|
|
||||||
|
# Raise an error for executables that do not exist
|
||||||
|
# One case this can happen is when game options are appended at the end of the exe
|
||||||
|
# Users should use launch_args for that
|
||||||
|
if key == "exe" and not Path(toml[table][key]).expanduser().is_file():
|
||||||
|
val: str = toml[table][key]
|
||||||
|
err: str = f"Value for key '{key}' in TOML is not a file: {val}"
|
||||||
|
raise FileNotFoundError(err)
|
||||||
|
|
||||||
|
# The proton and wine prefix need to be folders
|
||||||
|
if (
|
||||||
|
key == "proton" and not Path(toml[table][key]).expanduser().is_dir()
|
||||||
|
) or (key == "prefix" and not Path(toml[table][key]).expanduser().is_dir()):
|
||||||
|
dir: str = Path(toml[table][key]).expanduser().as_posix()
|
||||||
|
err: str = f"Value for key '{key}' in TOML is not a directory: {dir}"
|
||||||
|
raise NotADirectoryError(err)
|
||||||
|
|
||||||
|
# Check for empty keys
|
||||||
|
for key, val in toml[table].items():
|
||||||
|
if not val and isinstance(val, str):
|
||||||
|
err: str = f"Value is empty for '{key}' in TOML.\nPlease specify a value or remove the following entry:\n{key} = {val}"
|
||||||
|
raise ValueError(err)
|
||||||
|
|
||||||
|
return toml
|
||||||
|
|
||||||
if "WINEPREFIX" not in os.environ:
|
if "WINEPREFIX" not in os.environ:
|
||||||
err: str = "Environment variable not set or not a directory: WINEPREFIX"
|
err: str = "Environment variable not set or not a directory: WINEPREFIX"
|
||||||
raise ValueError(err)
|
raise ValueError(err)
|
||||||
@ -99,30 +118,68 @@ def check_env(env: Dict[str, str]) -> Dict[str, str]:
|
|||||||
err: str = "Environment variable not set or not a directory: PROTONPATH"
|
err: str = "Environment variable not set or not a directory: PROTONPATH"
|
||||||
raise ValueError(err)
|
raise ValueError(err)
|
||||||
env["PROTONPATH"] = os.environ["PROTONPATH"]
|
env["PROTONPATH"] = os.environ["PROTONPATH"]
|
||||||
env["STEAM_COMPAT_INSTALL_PATH"] = os.environ["PROTONPATH"]
|
|
||||||
|
|
||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
def set_env(env: Dict[str, str], args: Namespace) -> Dict[str, str]:
|
def set_env(
|
||||||
|
env: Dict[str, str], args: Union[Namespace, Tuple[str, List[str]]]
|
||||||
|
) -> Dict[str, str]:
|
||||||
"""Set various environment variables for the Steam RT.
|
"""Set various environment variables for the Steam RT.
|
||||||
|
|
||||||
Expects to be invoked if not reading a TOML file
|
Filesystem paths will be formatted and expanded as POSIX
|
||||||
"""
|
"""
|
||||||
_setup_pfx(env["WINEPREFIX"])
|
verbs: Set[str] = {
|
||||||
is_create_prefix: bool = False
|
"waitforexitandrun",
|
||||||
|
"run",
|
||||||
|
"runinprefix",
|
||||||
|
"destroyprefix",
|
||||||
|
"getcompatpath",
|
||||||
|
"getnativepath",
|
||||||
|
}
|
||||||
|
|
||||||
if not getattr(args, "exe", None):
|
# PROTON_VERB
|
||||||
is_create_prefix = True
|
# For invalid Proton verbs, just assign the waitforexitandrun
|
||||||
|
if "PROTON_VERB" in os.environ and os.environ["PROTON_VERB"] in verbs:
|
||||||
|
env["PROTON_VERB"] = os.environ["PROTON_VERB"]
|
||||||
|
else:
|
||||||
|
env["PROTON_VERB"] = "waitforexitandrun"
|
||||||
|
|
||||||
# Sets the environment variables: EXE
|
# EXE
|
||||||
for arg, val in vars(args).items():
|
# Empty string for EXE will be used to create a prefix
|
||||||
if arg == "exe" and not is_create_prefix:
|
if isinstance(args, tuple) and isinstance(args[0], str) and not args[0]:
|
||||||
# NOTE: options can possibly be appended at the end
|
env["EXE"] = ""
|
||||||
env["EXE"] = val
|
env["STEAM_COMPAT_INSTALL_PATH"] = ""
|
||||||
elif arg == "options" and val and not is_create_prefix:
|
env["PROTON_VERB"] = "waitforexitandrun"
|
||||||
# NOTE: assume it's space separated
|
elif isinstance(args, tuple):
|
||||||
env["EXE"] = env["EXE"] + " " + " ".join(val.split(" "))
|
env["EXE"] = Path(args[0]).expanduser().as_posix()
|
||||||
|
env["STEAM_COMPAT_INSTALL_PATH"] = Path(env["EXE"]).parent.as_posix()
|
||||||
|
else:
|
||||||
|
# Config branch
|
||||||
|
env["EXE"] = Path(env["EXE"]).expanduser().as_posix()
|
||||||
|
env["STEAM_COMPAT_INSTALL_PATH"] = Path(env["EXE"]).parent.as_posix()
|
||||||
|
|
||||||
|
if "STORE" in os.environ:
|
||||||
|
env["STORE"] = os.environ["STORE"]
|
||||||
|
|
||||||
|
# ULWGL_ID
|
||||||
|
env["ULWGL_ID"] = env["GAMEID"]
|
||||||
|
env["STEAM_COMPAT_APP_ID"] = "0"
|
||||||
|
|
||||||
|
if match(r"^ulwgl-[\d\w]+$", env["ULWGL_ID"]):
|
||||||
|
env["STEAM_COMPAT_APP_ID"] = env["ULWGL_ID"][env["ULWGL_ID"].find("-") + 1 :]
|
||||||
|
env["SteamAppId"] = env["STEAM_COMPAT_APP_ID"]
|
||||||
|
env["SteamGameId"] = env["SteamAppId"]
|
||||||
|
|
||||||
|
# PATHS
|
||||||
|
env["WINEPREFIX"] = Path(env["WINEPREFIX"]).expanduser().as_posix()
|
||||||
|
env["PROTONPATH"] = Path(env["PROTONPATH"]).expanduser().as_posix()
|
||||||
|
env["STEAM_COMPAT_DATA_PATH"] = env["WINEPREFIX"]
|
||||||
|
env["STEAM_COMPAT_SHADER_PATH"] = env["STEAM_COMPAT_DATA_PATH"] + "/shadercache"
|
||||||
|
env["STEAM_COMPAT_TOOL_PATHS"] = (
|
||||||
|
env["PROTONPATH"] + ":" + Path(__file__).parent.as_posix()
|
||||||
|
)
|
||||||
|
env["STEAM_COMPAT_MOUNTS"] = env["STEAM_COMPAT_TOOL_PATHS"]
|
||||||
|
|
||||||
return env
|
return env
|
||||||
|
|
||||||
@ -149,62 +206,35 @@ def set_env_toml(env: Dict[str, str], args: Namespace) -> Dict[str, str]:
|
|||||||
with Path(path_config).open(mode="rb") as file:
|
with Path(path_config).open(mode="rb") as file:
|
||||||
toml = tomllib.load(file)
|
toml = tomllib.load(file)
|
||||||
|
|
||||||
if not (
|
check_env(env, toml)
|
||||||
Path(toml["ulwgl"]["prefix"]).expanduser().is_dir()
|
|
||||||
or Path(toml["ulwgl"]["proton"]).expanduser().is_dir()
|
|
||||||
):
|
|
||||||
err: str = "Value for 'prefix' or 'proton' in TOML is not a directory."
|
|
||||||
raise NotADirectoryError(err)
|
|
||||||
|
|
||||||
# Set the values read from TOML to environment variables
|
|
||||||
# If necessary, raise an error on invalid inputs
|
|
||||||
for key, val in toml["ulwgl"].items():
|
for key, val in toml["ulwgl"].items():
|
||||||
# Handle cases for empty values
|
|
||||||
if not val and isinstance(val, str):
|
|
||||||
err: str = f'Value is empty for key in TOML: {key}\nPlease specify a value or remove the following entry:\n{key} = "{val}"'
|
|
||||||
raise ValueError(err)
|
|
||||||
if key == "prefix":
|
if key == "prefix":
|
||||||
env["WINEPREFIX"] = val
|
env["WINEPREFIX"] = val
|
||||||
_setup_pfx(val)
|
|
||||||
elif key == "game_id":
|
elif key == "game_id":
|
||||||
env["GAMEID"] = val
|
env["GAMEID"] = val
|
||||||
elif key == "proton":
|
elif key == "proton":
|
||||||
env["PROTONPATH"] = val
|
env["PROTONPATH"] = val
|
||||||
env["STEAM_COMPAT_INSTALL_PATH"] = val
|
|
||||||
elif key == "store":
|
elif key == "store":
|
||||||
env["STORE"] = val
|
env["STORE"] = val
|
||||||
elif key == "exe":
|
elif key == "exe":
|
||||||
# Raise an error for executables that do not exist
|
|
||||||
# One case this can happen is when game options are appended at the end of the exe
|
|
||||||
if not Path(val).expanduser().is_file():
|
|
||||||
err: str = "Value for key 'exe' in TOML is not a file."
|
|
||||||
raise FileNotFoundError(err)
|
|
||||||
|
|
||||||
# It's possible for users to pass values to --options
|
|
||||||
# Add any if they exist
|
|
||||||
if toml.get("ulwgl").get("launch_args"):
|
if toml.get("ulwgl").get("launch_args"):
|
||||||
env["EXE"] = val + " " + " ".join(toml.get("ulwgl").get("launch_args"))
|
env["EXE"] = val + " " + " ".join(toml.get("ulwgl").get("launch_args"))
|
||||||
else:
|
else:
|
||||||
env["EXE"] = val
|
env["EXE"] = val
|
||||||
|
|
||||||
if getattr(args, "options", None):
|
|
||||||
# Assume space separated options and just trust it
|
|
||||||
env["EXE"] = (
|
|
||||||
env["EXE"]
|
|
||||||
+ " "
|
|
||||||
+ " ".join(getattr(args, "options", None).split(" "))
|
|
||||||
)
|
|
||||||
|
|
||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
def build_command(env: Dict[str, str], command: List[str], verb: str) -> List[str]:
|
def build_command(
|
||||||
|
env: Dict[str, str], command: List[str], opts: List[str] = None
|
||||||
|
) -> List[str]:
|
||||||
"""Build the command to be executed."""
|
"""Build the command to be executed."""
|
||||||
paths: List[Path] = [
|
paths: List[Path] = [
|
||||||
Path(Path().home().as_posix() + "/.local/share/ULWGL/ULWGL"),
|
Path(Path().home().as_posix() + "/.local/share/ULWGL/ULWGL"),
|
||||||
Path(Path(__file__).cwd().as_posix() + "/ULWGL"),
|
Path(Path(__file__).cwd().as_posix() + "/ULWGL"),
|
||||||
]
|
]
|
||||||
entry_point: str = ""
|
entry_point: str = ""
|
||||||
|
verb: str = env["PROTON_VERB"]
|
||||||
|
|
||||||
# Find the ULWGL script in $HOME/.local/share then cwd
|
# Find the ULWGL script in $HOME/.local/share then cwd
|
||||||
for path in paths:
|
for path in paths:
|
||||||
@ -230,6 +260,9 @@ def build_command(env: Dict[str, str], command: List[str], verb: str) -> List[st
|
|||||||
[Path(env.get("PROTONPATH") + "/proton").as_posix(), verb, env.get("EXE")]
|
[Path(env.get("PROTONPATH") + "/proton").as_posix(), verb, env.get("EXE")]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if opts:
|
||||||
|
command.extend([*opts])
|
||||||
|
|
||||||
return command
|
return command
|
||||||
|
|
||||||
|
|
||||||
@ -253,68 +286,32 @@ def main() -> None: # noqa: D103
|
|||||||
"SteamGameId": "",
|
"SteamGameId": "",
|
||||||
"STEAM_RUNTIME_LIBRARY_PATH": "",
|
"STEAM_RUNTIME_LIBRARY_PATH": "",
|
||||||
"STORE": "",
|
"STORE": "",
|
||||||
|
"PROTON_VERB": "",
|
||||||
|
"ULWGL_ID": "",
|
||||||
}
|
}
|
||||||
command: List[str] = []
|
command: List[str] = []
|
||||||
verb: str = "waitforexitandrun"
|
args: Union[Namespace, Tuple[str, List[str]]] = parse_args()
|
||||||
# Represents a valid list of current supported Proton verbs
|
opts: List[str] = None
|
||||||
verbs: Set[str] = {
|
|
||||||
"waitforexitandrun",
|
|
||||||
"run",
|
|
||||||
"runinprefix",
|
|
||||||
"destroyprefix",
|
|
||||||
"getcompatpath",
|
|
||||||
"getnativepath",
|
|
||||||
}
|
|
||||||
args: Namespace = parse_args()
|
|
||||||
|
|
||||||
if getattr(args, "config", None):
|
if isinstance(args, Namespace):
|
||||||
set_env_toml(env, args)
|
set_env_toml(env, args)
|
||||||
else:
|
else:
|
||||||
|
# Reference the game options
|
||||||
|
opts = args[1]
|
||||||
check_env(env)
|
check_env(env)
|
||||||
set_env(env, args)
|
|
||||||
|
|
||||||
if getattr(args, "verb", None) and getattr(args, "verb", None) in verbs:
|
setup_pfx(env["WINEPREFIX"])
|
||||||
verb = getattr(args, "verb", None)
|
set_env(env, args)
|
||||||
|
|
||||||
if getattr(args, "store", None):
|
# Game Drive
|
||||||
env["STORE"] = getattr(args, "store", None)
|
ulwgl_plugins.enable_steam_game_drive(env)
|
||||||
|
|
||||||
env["ULWGL_ID"] = env["GAMEID"]
|
# Set all environment variables
|
||||||
env["STEAM_COMPAT_APP_ID"] = "0"
|
|
||||||
|
|
||||||
if match(r"^ulwgl-[\d\w]+$", env["ULWGL_ID"]):
|
|
||||||
env["STEAM_COMPAT_APP_ID"] = env["ULWGL_ID"][env["ULWGL_ID"].find("-") + 1 :]
|
|
||||||
|
|
||||||
env["SteamAppId"] = env["STEAM_COMPAT_APP_ID"]
|
|
||||||
env["SteamGameId"] = env["SteamAppId"]
|
|
||||||
env["WINEPREFIX"] = Path(env["WINEPREFIX"]).expanduser().as_posix()
|
|
||||||
env["PROTONPATH"] = Path(env["PROTONPATH"]).expanduser().as_posix()
|
|
||||||
env["STEAM_COMPAT_DATA_PATH"] = env["WINEPREFIX"]
|
|
||||||
env["STEAM_COMPAT_SHADER_PATH"] = env["STEAM_COMPAT_DATA_PATH"] + "/shadercache"
|
|
||||||
env["STEAM_COMPAT_INSTALL_PATH"] = Path(env["EXE"]).parent.expanduser().as_posix()
|
|
||||||
env["EXE"] = Path(env["EXE"]).expanduser().as_posix()
|
|
||||||
env["STEAM_COMPAT_TOOL_PATHS"] = (
|
|
||||||
env["PROTONPATH"] + ":" + Path(__file__).parent.as_posix()
|
|
||||||
)
|
|
||||||
env["STEAM_COMPAT_MOUNTS"] = env["STEAM_COMPAT_TOOL_PATHS"]
|
|
||||||
|
|
||||||
# Create an empty Proton prefix when asked
|
|
||||||
if not getattr(args, "exe", None) and not getattr(args, "config", None):
|
|
||||||
env["EXE"] = ""
|
|
||||||
env["STEAM_COMPAT_INSTALL_PATH"] = ""
|
|
||||||
verb = "waitforexitandrun"
|
|
||||||
|
|
||||||
# Game Drive functionality
|
|
||||||
gamelauncher_plugins.enable_steam_game_drive(env)
|
|
||||||
|
|
||||||
# Set all environment variable
|
|
||||||
# NOTE: `env` after this block should be read only
|
# NOTE: `env` after this block should be read only
|
||||||
for key, val in env.items():
|
for key, val in env.items():
|
||||||
print(f"Setting environment variable: {key}={val}")
|
|
||||||
os.environ[key] = val
|
os.environ[key] = val
|
||||||
|
|
||||||
build_command(env, command, verb)
|
build_command(env, command, opts)
|
||||||
print(f"The following command will be executed: {command}")
|
|
||||||
subprocess.run(command, check=True, stdout=subprocess.PIPE, text=True)
|
subprocess.run(command, check=True, stdout=subprocess.PIPE, text=True)
|
||||||
|
|
||||||
|
|
1080
ulwgl_test.py
Normal file
1080
ulwgl_test.py
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user