mirror of
https://github.com/aclist/dztui.git
synced 2026-08-29 03:06:56 +02:00
fix: handle alternate client versions when parsing vdf
This commit is contained in:
parent
bbaec35a5f
commit
6a30ed44fc
@ -49,9 +49,21 @@ class Shortcuts:
|
|||||||
|
|
||||||
def find_appname_by_unsigned_id(self, appid: int) -> str:
|
def find_appname_by_unsigned_id(self, appid: int) -> str:
|
||||||
# NOTE: bitmask signed int back to 32-bit CRC
|
# NOTE: bitmask signed int back to 32-bit CRC
|
||||||
for key in self.shortcuts["shortcuts"].keys():
|
# varying client versions treat case sensitivity differently
|
||||||
if self.shortcuts["shortcuts"][key]["appid"] & 0xFFFFFFFF == appid:
|
try:
|
||||||
return str(self.shortcuts["shortcuts"][key]["appname"])
|
for key in self.shortcuts["shortcuts"].keys():
|
||||||
|
if self.shortcuts["shortcuts"][key]["appid"] & 0xFFFFFFFF == appid:
|
||||||
|
try:
|
||||||
|
return str(self.shortcuts["shortcuts"][key]["appname"])
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(e)
|
||||||
|
try:
|
||||||
|
return str(self.shortcuts["shortcuts"][key]["AppName"])
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(e)
|
||||||
|
return unknown
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(e)
|
||||||
return unknown
|
return unknown
|
||||||
|
|
||||||
def get_shortcuts(self) -> Any:
|
def get_shortcuts(self) -> Any:
|
||||||
@ -119,22 +131,18 @@ class Shortcuts:
|
|||||||
https://developer.valvesoftware.com/wiki/Add_Non-Steam_Game
|
https://developer.valvesoftware.com/wiki/Add_Non-Steam_Game
|
||||||
https://developer.valvesoftware.com/wiki/Steam_Library_Shortcuts
|
https://developer.valvesoftware.com/wiki/Steam_Library_Shortcuts
|
||||||
|
|
||||||
Wiki variously lists keys with title case and lowercase, but keys actually do
|
Key case is not internally consistent and varies between client versions
|
||||||
not seem to be case-sensitive. Some entries generated by Steam do not match the wiki
|
Most keys use Pascal case, but some do not
|
||||||
|
|
||||||
Keys are entered into the dictionary in a linear insertion order
|
|
||||||
|
|
||||||
# TODO: try to replicate key case sensitivity as it is created by Steam with generic shortcut
|
|
||||||
|
|
||||||
appid: signed int CRC
|
appid: signed int CRC
|
||||||
exe: absolute path to the executable, must be wrapped in literal quotes
|
Exe: absolute path to the executable, must be wrapped in literal quotes
|
||||||
StartDir: directory the executable starts in, generally the parent
|
StartDir: directory the executable starts in, generally the parent
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NEW_ENTRY: dict[str, Any] = {}
|
NEW_ENTRY: dict[str, Any] = {}
|
||||||
NEW_ENTRY["appid"] = meta.appid
|
NEW_ENTRY["appid"] = meta.appid
|
||||||
NEW_ENTRY["appname"] = meta.appname
|
NEW_ENTRY["AppName"] = meta.appname
|
||||||
NEW_ENTRY["exe"] = f'"{meta.exe_path}"'
|
NEW_ENTRY["Exe"] = f'"{meta.exe_path}"'
|
||||||
NEW_ENTRY["StartDir"] = f"{meta.start_dir}"
|
NEW_ENTRY["StartDir"] = f"{meta.start_dir}"
|
||||||
NEW_ENTRY["icon"] = meta.icon
|
NEW_ENTRY["icon"] = meta.icon
|
||||||
NEW_ENTRY["ShortcutPath"] = ""
|
NEW_ENTRY["ShortcutPath"] = ""
|
||||||
@ -142,7 +150,7 @@ class Shortcuts:
|
|||||||
NEW_ENTRY["IsHidden"] = 0
|
NEW_ENTRY["IsHidden"] = 0
|
||||||
NEW_ENTRY["AllowDesktopConfig"] = 1
|
NEW_ENTRY["AllowDesktopConfig"] = 1
|
||||||
NEW_ENTRY["AllowOverlay"] = 1
|
NEW_ENTRY["AllowOverlay"] = 1
|
||||||
NEW_ENTRY["openvr"] = 0
|
NEW_ENTRY["OpenVR"] = 0
|
||||||
NEW_ENTRY["Devkit"] = 0
|
NEW_ENTRY["Devkit"] = 0
|
||||||
NEW_ENTRY["DevkitGameID"] = ""
|
NEW_ENTRY["DevkitGameID"] = ""
|
||||||
NEW_ENTRY["DevkitOverrideAppID"] = 0
|
NEW_ENTRY["DevkitOverrideAppID"] = 0
|
||||||
|
|||||||
@ -205,21 +205,28 @@ def launch_offline(
|
|||||||
proc = subprocess.run([*client_args, *params])
|
proc = subprocess.run([*client_args, *params])
|
||||||
return proc.returncode
|
return proc.returncode
|
||||||
|
|
||||||
|
def find_loginusers(path: Path) -> Path:
|
||||||
|
return path / "config" / "loginusers.vdf"
|
||||||
|
|
||||||
def find_user_id(path: Path) -> str | None:
|
def find_user_id(path: Path) -> str | None:
|
||||||
resolved_path = path / "config" / "loginusers.vdf"
|
resolved_path = find_loginusers(path)
|
||||||
try:
|
try:
|
||||||
with open(resolved_path, "r") as f:
|
with open(resolved_path, "r") as f:
|
||||||
v = vdf.load(f)
|
v = vdf.load(f)
|
||||||
|
# NOTE: beta client
|
||||||
|
# /package/beta
|
||||||
|
if len(v["users"]) == 1:
|
||||||
|
return str(list(v["users"].keys())[0])
|
||||||
for user in v["users"]:
|
for user in v["users"]:
|
||||||
if v["users"][user]["MostRecent"] == "1":
|
if v["users"][user]["MostRecent"] == "1":
|
||||||
return str(user)
|
return str(user)
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warn(e)
|
logger.debug(e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def find_user_id_32(path: Path) -> int:
|
def find_user_id_32(path: Path) -> int:
|
||||||
uid = find_user_id(path)
|
uid = find_user_id(path)
|
||||||
if uid is None:
|
if uid is None:
|
||||||
|
|||||||
13
tests/fixtures/api/loginusers_beta_client.vdf
vendored
Normal file
13
tests/fixtures/api/loginusers_beta_client.vdf
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
"users"
|
||||||
|
{
|
||||||
|
"0"
|
||||||
|
{
|
||||||
|
"AccountName" "STEAMUSER"
|
||||||
|
"PersonaName" "STEAMUSER"
|
||||||
|
"RememberPassword" "0"
|
||||||
|
"WantsOfflineMode" "0"
|
||||||
|
"SkipOfflineModeWarning" "0"
|
||||||
|
"AutoLogin" "1"
|
||||||
|
"Timestamp" "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
tests/fixtures/api/loginusers_legacy_client.vdf
vendored
Normal file
14
tests/fixtures/api/loginusers_legacy_client.vdf
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
"users"
|
||||||
|
{
|
||||||
|
"0"
|
||||||
|
{
|
||||||
|
"AccountName" "STEAMUSER"
|
||||||
|
"PersonaName" "STEAMUSER"
|
||||||
|
"RememberPassword" "0"
|
||||||
|
"WantsOfflineMode" "0"
|
||||||
|
"SkipOfflineModeWarning" "0"
|
||||||
|
"AllowAutoLogin" "1"
|
||||||
|
"MostRecent" "1"
|
||||||
|
"Timestamp" "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
25
tests/fixtures/api/loginusers_legacy_client_multiple.vdf
vendored
Normal file
25
tests/fixtures/api/loginusers_legacy_client_multiple.vdf
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
"users"
|
||||||
|
{
|
||||||
|
"0"
|
||||||
|
{
|
||||||
|
"AccountName" "STEAMUSER"
|
||||||
|
"PersonaName" "STEAMUSER"
|
||||||
|
"RememberPassword" "0"
|
||||||
|
"WantsOfflineMode" "0"
|
||||||
|
"SkipOfflineModeWarning" "0"
|
||||||
|
"AllowAutoLogin" "1"
|
||||||
|
"MostRecent" "0"
|
||||||
|
"Timestamp" "0"
|
||||||
|
}
|
||||||
|
"1"
|
||||||
|
{
|
||||||
|
"AccountName" "STEAMUSER2"
|
||||||
|
"PersonaName" "STEAMUSER2"
|
||||||
|
"RememberPassword" "0"
|
||||||
|
"WantsOfflineMode" "0"
|
||||||
|
"SkipOfflineModeWarning" "0"
|
||||||
|
"AllowAutoLogin" "1"
|
||||||
|
"MostRecent" "1"
|
||||||
|
"Timestamp" "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
25
tests/test_loginusers.py
Normal file
25
tests/test_loginusers.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from dzgui.api.steam import find_user_id
|
||||||
|
from tests.fixtures import fixture_path
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.apitest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"fixture, expect",
|
||||||
|
[
|
||||||
|
("api/loginusers_legacy_client.vdf", 0),
|
||||||
|
("api/loginusers_legacy_client_multiple.vdf", 1),
|
||||||
|
("api/loginusers_beta_client.vdf", 0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_loginusers(monkeypatch, fixture: str, expect: int) -> None:
|
||||||
|
def mock_loginusers(path: Path) -> Path:
|
||||||
|
return fixture_path(fixture)
|
||||||
|
|
||||||
|
monkeypatch.setattr("dzgui.api.steam.find_loginusers", mock_loginusers)
|
||||||
|
uid = find_user_id(Path(""))
|
||||||
|
assert int(uid) == expect
|
||||||
@ -30,9 +30,9 @@ def test_no_shortcuts(monkeypatch) -> None:
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def dummy_app() -> None:
|
def dummy_app() -> None:
|
||||||
d = {
|
d = {
|
||||||
"appname": "TEST APP",
|
"AppName": "TEST APP",
|
||||||
"StartDir": "TEST_DIR",
|
"StartDir": "TEST_DIR",
|
||||||
"exe": "TEST_DIR/TEST_EXE.EXE",
|
"Exe": "TEST_DIR/TEST_EXE.EXE",
|
||||||
"icon": "IMAGES_DIR/TEST_IMAGE.PNG",
|
"icon": "IMAGES_DIR/TEST_IMAGE.PNG",
|
||||||
}
|
}
|
||||||
return d
|
return d
|
||||||
@ -41,8 +41,8 @@ def dummy_app() -> None:
|
|||||||
def test_wrap_exe(dummy_app) -> None:
|
def test_wrap_exe(dummy_app) -> None:
|
||||||
s = Shortcuts(Path(""))
|
s = Shortcuts(Path(""))
|
||||||
s.add_shortcut(*dummy_app.values())
|
s.add_shortcut(*dummy_app.values())
|
||||||
assert s.shortcuts["shortcuts"]["0"]["exe"][0] == '"'
|
assert s.shortcuts["shortcuts"]["0"]["Exe"][0] == '"'
|
||||||
assert s.shortcuts["shortcuts"]["0"]["exe"][-1] == '"'
|
assert s.shortcuts["shortcuts"]["0"]["Exe"][-1] == '"'
|
||||||
|
|
||||||
|
|
||||||
def test_add_shortcut(dummy_app) -> None:
|
def test_add_shortcut(dummy_app) -> None:
|
||||||
@ -51,7 +51,7 @@ def test_add_shortcut(dummy_app) -> None:
|
|||||||
new = s.shortcuts["shortcuts"]
|
new = s.shortcuts["shortcuts"]
|
||||||
ind = str(len(new) - 1)
|
ind = str(len(new) - 1)
|
||||||
for k, v in dummy_app.items():
|
for k, v in dummy_app.items():
|
||||||
if k == "exe":
|
if k == "Exe":
|
||||||
v = f'"{v}"'
|
v = f'"{v}"'
|
||||||
assert new[ind][k] == v
|
assert new[ind][k] == v
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user