def func(name, appid, compat_tool_name=None, library_dir=None, add_prefix=True, required_tool_app=None): if not library_dir: steamapps_dir = steam_dir / "steamapps" else: steamapps_dir = library_dir / "steamapps" install_path = steamapps_dir / "common" / name install_path.mkdir(parents=True) if required_tool_app: (install_path / "toolmanifest.vdf").write_text( vdf.dumps({ "manifest": { "require_tool_appid": required_tool_app.appid } })) (steamapps_dir / "appmanifest_{}.acf".format(appid)).write_text( vdf.dumps({ "AppState": { "appid": str(appid), "name": name, "installdir": name } })) # Add Wine prefix if add_prefix: (steamapps_dir / "compatdata" / str(appid) / "pfx").mkdir(parents=True) (steamapps_dir / "compatdata" / str(appid) / "pfx.lock").touch() # Set the preferred Proton installation for the app if provided if compat_tool_name: steam_config = vdf.loads(steam_config_path.read_text()) steam_config["InstallConfigStore"]["Software"]["Valve"]["Steam"][ "CompatToolMapping"][str(appid)] = { "name": compat_tool_name, "config": "", "Priority": "250" } steam_config_path.write_text(vdf.dumps(steam_config)) steam_app = SteamApp(name=name, appid=appid, install_path=str(steamapps_dir / "common" / name), prefix_path=str(steamapps_dir / "compatdata" / str(appid) / "pfx")) if required_tool_app: # In actual code, `required_tool_app` attribute is populated later # when we have retrieved all Steam apps and can find the # corresponding app using its app ID steam_app.required_tool_app = required_tool_app return steam_app
def func(name, compat_tool_dir=None): if not compat_tool_dir: compat_tool_dir = \ steam_dir.parent / "root" / "compatibilitytools.d" / name else: compat_tool_dir = compat_tool_dir / name compat_tool_dir.mkdir(parents=True, exist_ok=True) (compat_tool_dir / "proton").touch() (compat_tool_dir / "proton").chmod(0o744) (compat_tool_dir / "dist" / "bin").mkdir(parents=True) (compat_tool_dir / "dist" / "bin" / "wine").touch() (compat_tool_dir / "dist" / "bin" / "wineserver").touch() (compat_tool_dir / "compatibilitytool.vdf").write_text( vdf.dumps({ "compatibilitytools": { "compat_tools": { name: { "install_path": ".", "display_name": name, "from_oslist": "windows", "to_oslist": "linux" } } } }) ) return SteamApp( name=name, install_path=str(compat_tool_dir) )
def test_steam_app_from_appmanifest_permission_denied( self, steam_app_factory, caplog, monkeypatch): """ Test trying to read a SteamApp manifest that the user doesn't have read permission for """ def _mock_read_text(self, encoding=None): """ Mock `pathlib.Path.read_text` that mimics a failure due to insufficient permissions """ raise PermissionError("Permission denied") steam_app = steam_app_factory(name="Fake game", appid=10) appmanifest_path = \ Path(steam_app.install_path).parent.parent / "appmanifest_10.acf" monkeypatch.setattr("pathlib.Path.read_text", _mock_read_text) assert not SteamApp.from_appmanifest(path=appmanifest_path, steam_lib_paths=[]) record = caplog.records[-1] assert record.getMessage() == ( "Skipping appmanifest {} due to insufficient permissions".format( str(appmanifest_path)))
def test_steam_app_from_appmanifest_empty(self, steam_app_factory): steam_app = steam_app_factory(name="Fake game", appid=10) appmanifest_path = \ Path(steam_app.install_path).parent.parent / "appmanifest_10.acf" appmanifest_path.write_text("") # Empty appmanifest file is ignored assert not SteamApp.from_appmanifest(path=str(appmanifest_path), steam_lib_paths=[])
def test_steam_app_from_appmanifest_invalid(self, steam_app_factory, content): steam_app = steam_app_factory(name="Fake game", appid=10) appmanifest_path = \ Path(steam_app.install_path).parent.parent / "appmanifest_10.acf" appmanifest_path.write_bytes(content) # Invalid appmanifest file is ignored assert not SteamApp.from_appmanifest(path=appmanifest_path, steam_lib_paths=[])
def test_steam_app_from_appmanifest_empty(self, steam_app_factory): """ Try to deserialize an empty appmanifest and check that no SteamApp is returned """ steam_app = steam_app_factory(name="Fake game", appid=10) appmanifest_path = \ Path(steam_app.install_path).parent.parent / "appmanifest_10.acf" appmanifest_path.write_text("") # Empty appmanifest file is ignored assert not SteamApp.from_appmanifest(path=appmanifest_path, steam_lib_paths=[])
def test_steam_app_from_appmanifest(self, steam_app_factory, steam_dir): """ Create a SteamApp from an appmanifest file """ steam_app = steam_app_factory(name="Fake game", appid=10) appmanifest_path = \ Path(steam_app.install_path).parent.parent / "appmanifest_10.acf" steam_app = SteamApp.from_appmanifest( path=str(appmanifest_path), steam_lib_paths=[str(steam_dir / "steam" / "steamapps")]) assert steam_app.name == "Fake game" assert steam_app.appid == 10
def func(name, appid, compat_tool_name=None, library_dir=None, add_prefix=True): if not library_dir: steamapps_dir = steam_dir / "steamapps" else: steamapps_dir = library_dir / "steamapps" (steamapps_dir / "common" / name).mkdir(parents=True) (steamapps_dir / "appmanifest_{}.acf".format(appid)).write_text( vdf.dumps({ "AppState": { "appid": str(appid), "name": name, "installdir": name } })) # Add Wine prefix if add_prefix: (steamapps_dir / "compatdata" / str(appid) / "pfx").mkdir(parents=True) (steamapps_dir / "compatdata" / str(appid) / "pfx.lock").touch() # Set the preferred Proton installation for the app if provided if compat_tool_name: steam_config = vdf.loads(steam_config_path.read_text()) steam_config["InstallConfigStore"]["Software"]["Valve"]["Steam"][ "CompatToolMapping"][str(appid)] = { "name": compat_tool_name, "config": "", "Priority": "250" } steam_config_path.write_text(vdf.dumps(steam_config)) return SteamApp(name=name, appid=appid, install_path=str(steamapps_dir / "common" / name), prefix_path=str(steamapps_dir / "compatdata" / str(appid) / "pfx"))
def func(name): tool_dir = steam_dir.parent / "root" / "compatibilitytools.d" / name tool_dir.mkdir(parents=True) (tool_dir / "proton").touch() (tool_dir / "proton").chmod(0o744) (tool_dir / "compatibilitytool.vdf").write_text( vdf.dumps({ "compatibilitytools": { "compat_tools": { name: { "install_path": ".", "display_name": name, "from_oslist": "windows", "to_oslist": "linux" } } } })) return SteamApp(name=name, install_path=str(tool_dir))
def test_steam_app_from_appmanifest_corrupted_toolmanifest( self, steam_runtime_soldier, proton_factory, caplog, content): """ Test trying to a SteamApp manifest from an incomplete Proton installation with an empty or corrupted toolmanifest.vdf file """ proton_app = proton_factory(name="Proton 5.13", appid=10, compat_tool_name="proton_513", required_tool_app=steam_runtime_soldier) # Empty the "toolmanifest.vdf" file (proton_app.install_path / "toolmanifest.vdf").write_bytes(content) assert not SteamApp.from_appmanifest( path=proton_app.install_path.parent.parent / "appmanifest_10.acf", steam_lib_paths=[]) assert len(caplog.records) == 1 record = caplog.records[0] assert "Tool manifest for Proton 5.13 is empty" in record.message
def test_steam_app_userconfig_name(self, steam_app_factory): """ Try creating a SteamApp from an older version of the app manifest which contains the application name in a different field See GitHub issue #103 for details """ steam_app = steam_app_factory(name="Fake game", appid=10) appmanifest_path = \ Path(steam_app.install_path).parent.parent / "appmanifest_10.acf" data = vdf.loads(appmanifest_path.read_text()) # Older installations store the name in `userconfig/name` instead del data["AppState"]["name"] data["AppState"]["userconfig"] = {"name": "Fake game"} appmanifest_path.write_text(vdf.dumps(data)) app = SteamApp.from_appmanifest(path=appmanifest_path, steam_lib_paths=[]) assert app.name == "Fake game"