UWINE/ulwgl_plugins.py
2024-02-21 20:44:45 -08:00

130 lines
4.6 KiB
Python

import os
from pathlib import Path
from typing import Dict, Set, Any, List
from argparse import Namespace
def set_env_toml(env: Dict[str, str], args: Namespace) -> Dict[str, str]:
"""Read a TOML file then sets the environment variables for the Steam RT.
In the TOML file, certain keys map to Steam RT environment variables. For example:
proton -> $PROTONPATH
prefix -> $WINEPREFIX
game_id -> $GAMEID
exe -> $EXE
At the moment we expect the tables: 'ulwgl'
"""
try:
import tomllib
except ModuleNotFoundError:
msg: str = "tomllib requires Python 3.11"
raise ModuleNotFoundError(msg)
toml: Dict[str, Any] = None
path_config: str = Path(getattr(args, "config", None)).expanduser().as_posix()
if not Path(path_config).is_file():
msg: str = "Path to configuration is not a file: " + getattr(
args, "config", None
)
raise FileNotFoundError(msg)
with Path(path_config).open(mode="rb") as file:
toml = tomllib.load(file)
_check_env_toml(env, toml)
for key, val in toml["ulwgl"].items():
if key == "prefix":
env["WINEPREFIX"] = val
elif key == "game_id":
env["GAMEID"] = val
elif key == "proton":
env["PROTONPATH"] = val
elif key == "store":
env["STORE"] = val
elif key == "exe":
if toml.get("ulwgl").get("launch_args"):
env["EXE"] = val + " " + " ".join(toml.get("ulwgl").get("launch_args"))
else:
env["EXE"] = val
return env
def _check_env_toml(env: Dict[str, str], toml: Dict[str, Any]):
"""Check for required or empty key/value pairs when reading a TOML config.
NOTE: Casing matters in the config and we do not 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
def enable_steam_game_drive(env: Dict[str, str]) -> Dict[str, str]:
"""Enable Steam Game Drive functionality.
Expects STEAM_COMPAT_INSTALL_PATH to be set
STEAM_RUNTIME_LIBRARY_PATH will not be set if the exe directory does not exist
"""
paths: Set[str] = set()
root: Path = Path("/")
# Check for mount points going up toward the root
# NOTE: Subvolumes can be mount points
for path in Path(env["STEAM_COMPAT_INSTALL_PATH"]).parents:
if path.is_mount() and path != root:
if env["STEAM_COMPAT_LIBRARY_PATHS"]:
env["STEAM_COMPAT_LIBRARY_PATHS"] = (
env["STEAM_COMPAT_LIBRARY_PATHS"] + ":" + path.as_posix()
)
else:
env["STEAM_COMPAT_LIBRARY_PATHS"] = path.as_posix()
break
if "LD_LIBRARY_PATH" in os.environ:
paths.add(Path(os.environ["LD_LIBRARY_PATH"]).as_posix())
if env["STEAM_COMPAT_INSTALL_PATH"]:
paths.add(env["STEAM_COMPAT_INSTALL_PATH"])
# Hard code for now because these paths seem to be pretty standard
# This way we avoid shelling to ldconfig
paths.add("/usr/lib")
paths.add("/usr/lib32")
env["STEAM_RUNTIME_LIBRARY_PATH"] = ":".join(list(paths))
return env