mirror of
https://github.com/aclist/dztui.git
synced 2026-11-02 16:12:11 +01:00
Compare commits
21
Commits
7.0.0b16
...
525de56036
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
525de56036 | ||
|
|
7edb66c311 | ||
|
|
dc2a635d2f | ||
|
|
8528300851 | ||
|
|
daa4aa0059 | ||
|
|
71a0481600 | ||
|
|
ba1b980a7f | ||
|
|
1dc20b838d | ||
|
|
0c6421fa63 | ||
|
|
a4f9a67da1 | ||
|
|
5c07a77b63 | ||
|
|
91773c933c | ||
|
|
6a30ed44fc | ||
|
|
bbaec35a5f | ||
|
|
c46477f0a7 | ||
|
|
f47446a2e1 | ||
|
|
cdb311cb1f | ||
|
|
11cdcb62b2 | ||
|
|
1913f69e7b | ||
|
|
8c2cea0fdc | ||
|
|
8c29b9b35a |
No files matched your search
+26
-10
@@ -12,6 +12,7 @@ from typing import Any
|
|||||||
from dzgui.api.steam import find_user_id_32
|
from dzgui.api.steam import find_user_id_32
|
||||||
from dzgui.const.constants import APP_NAME, APP_NAME_LOWER, IMAGES_PATH
|
from dzgui.const.constants import APP_NAME, APP_NAME_LOWER, IMAGES_PATH
|
||||||
from dzgui.util.dirs import copy_dzgui_to_xdg_data
|
from dzgui.util.dirs import copy_dzgui_to_xdg_data
|
||||||
|
from dzgui.util.strings import unknown
|
||||||
|
|
||||||
logger = logging.getLogger(APP_NAME)
|
logger = logging.getLogger(APP_NAME)
|
||||||
|
|
||||||
@@ -46,6 +47,25 @@ class Shortcuts:
|
|||||||
wrapped_exe = f'"{exe}"'
|
wrapped_exe = f'"{exe}"'
|
||||||
return appname + wrapped_exe
|
return appname + wrapped_exe
|
||||||
|
|
||||||
|
def find_appname_by_unsigned_id(self, appid: int) -> str:
|
||||||
|
# NOTE: bitmask signed int back to 32-bit CRC
|
||||||
|
# varying client versions treat case sensitivity differently
|
||||||
|
try:
|
||||||
|
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
|
||||||
|
|
||||||
def get_shortcuts(self) -> Any:
|
def get_shortcuts(self) -> Any:
|
||||||
return self.shortcuts
|
return self.shortcuts
|
||||||
|
|
||||||
@@ -111,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"] = ""
|
||||||
@@ -134,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
|
||||||
|
|||||||
+21
-9
@@ -32,23 +32,25 @@ from dzgui.const.endpoints import (
|
|||||||
)
|
)
|
||||||
from dzgui.strings import wizard
|
from dzgui.strings import wizard
|
||||||
from dzgui.util.bash import concat_bash_args
|
from dzgui.util.bash import concat_bash_args
|
||||||
from dzgui.util.strings import unknown
|
|
||||||
|
|
||||||
logger = logging.getLogger(APP_NAME)
|
logger = logging.getLogger(APP_NAME)
|
||||||
|
|
||||||
|
|
||||||
class AppNotInstalledError(Exception):
|
class AppNotInstalledError(Exception):
|
||||||
"""App not present in user's libraryfolders"""
|
"""App not present in user's libraryfolders"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AppMovedError(Exception):
|
class AppMovedError(Exception):
|
||||||
"""VDF points to a nonexistent location on disk"""
|
"""VDF points to a nonexistent location on disk"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class VDFLoadError(Exception):
|
class VDFLoadError(Exception):
|
||||||
"""Malformed VDF or JSON conversion"""
|
"""Malformed VDF or JSON conversion"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -203,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:
|
||||||
@@ -322,9 +331,12 @@ def get_running_app() -> int | None:
|
|||||||
|
|
||||||
|
|
||||||
def get_app_allows_downloads(path: Path, appid: int) -> bool:
|
def get_app_allows_downloads(path: Path, appid: int) -> bool:
|
||||||
root_path = get_app_path(path, appid)
|
try:
|
||||||
acf = root_path.joinpath(f"steamapps/appmanifest_{appid}.acf")
|
root_path = get_app_path(path, appid)
|
||||||
flag = ACF(acf).get_allows_downloads()
|
acf = root_path.joinpath(f"steamapps/appmanifest_{appid}.acf")
|
||||||
|
flag = ACF(acf).get_allows_downloads()
|
||||||
|
except AppNotInstalledError:
|
||||||
|
flag = 0
|
||||||
match flag:
|
match flag:
|
||||||
# NOTE: adheres to global client setting
|
# NOTE: adheres to global client setting
|
||||||
case 0:
|
case 0:
|
||||||
@@ -403,13 +415,13 @@ def get_app_path(folders_path: Path, appid: int) -> Path:
|
|||||||
return Path(app_path)
|
return Path(app_path)
|
||||||
|
|
||||||
|
|
||||||
def get_app_name(appid: int) -> str:
|
def get_app_name(appid: int) -> str | None:
|
||||||
payload = {"appids": [appid]}
|
payload = {"appids": [appid]}
|
||||||
res = requests.get(APP_DETAILS, params=payload)
|
res = requests.get(APP_DETAILS, params=payload)
|
||||||
if res.status_code != 200:
|
if res.status_code != 200:
|
||||||
return unknown
|
return None
|
||||||
try:
|
try:
|
||||||
return str(res.json()[str(appid)]["data"]["name"])
|
return str(res.json()[str(appid)]["data"]["name"])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(e)
|
logger.debug(e)
|
||||||
return unknown
|
return None
|
||||||
@@ -21,7 +21,7 @@ def write_desktop_file(exe_path: Path) -> Path:
|
|||||||
Terminal=false
|
Terminal=false
|
||||||
Exec={exe_path}
|
Exec={exe_path}
|
||||||
Name=DZGUI
|
Name=DZGUI
|
||||||
Comment=dzgui
|
Comment=DayZ server browser and mod manager
|
||||||
Icon={icon}
|
Icon={icon}
|
||||||
Categories=Game"""
|
Categories=Game"""
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING
|
|||||||
|
|
||||||
import dzgui.api.pefile as PeFile
|
import dzgui.api.pefile as PeFile
|
||||||
import dzgui.api.servers as Servers
|
import dzgui.api.servers as Servers
|
||||||
|
from dzgui.api.shortcuts import Shortcuts
|
||||||
|
|
||||||
from dzgui.api.steam import (
|
from dzgui.api.steam import (
|
||||||
connect,
|
connect,
|
||||||
@@ -175,6 +176,12 @@ class ConnectionManager:
|
|||||||
if running_app is not None:
|
if running_app is not None:
|
||||||
allows_dl = get_app_allows_downloads(steam_path, running_app)
|
allows_dl = get_app_allows_downloads(steam_path, running_app)
|
||||||
running_app_name = get_app_name(running_app)
|
running_app_name = get_app_name(running_app)
|
||||||
|
# NOTE: for out-of-range appid, assume NSG.
|
||||||
|
# dzgui.api.steam.get_app_name() will return None
|
||||||
|
if running_app_name is None:
|
||||||
|
s = Shortcuts(steam_path)
|
||||||
|
# NOTE: returns "Unknown" if unparseable
|
||||||
|
running_app_name = s.find_appname_by_unsigned_id(running_app)
|
||||||
allows_downloads = (allows_dl, running_app_name)
|
allows_downloads = (allows_dl, running_app_name)
|
||||||
else:
|
else:
|
||||||
allows_downloads = (True, "")
|
allows_downloads = (True, "")
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ from typing import Literal
|
|||||||
|
|
||||||
api_filter = r"(.*&key=)([^&]*)(.*)"
|
api_filter = r"(.*&key=)([^&]*)(.*)"
|
||||||
home_filter = r"(/home/)([^\s'\/]*)(.*)"
|
home_filter = r"(/home/)([^\s'\/]*)(.*)"
|
||||||
|
user_filter = r"(.*Steam/userdata/)([^/]*)(.*)"
|
||||||
REDACTED = r"\1REDACTED\3"
|
REDACTED = r"\1REDACTED\3"
|
||||||
REDACTION_PATTERNS = [api_filter, home_filter]
|
REDACTION_PATTERNS = [api_filter, home_filter, user_filter]
|
||||||
|
|
||||||
|
|
||||||
def redact_home(text: str) -> str:
|
def redact_home(text: str) -> str:
|
||||||
|
|||||||
@@ -162,7 +162,8 @@ class APIEntry(Gtk.Box):
|
|||||||
self.entry.connect("icon-release", self._on_icon_release)
|
self.entry.connect("icon-release", self._on_icon_release)
|
||||||
self.entry.connect("activate", self._on_field_activated)
|
self.entry.connect("activate", self._on_field_activated)
|
||||||
|
|
||||||
self.submit = Gtk.Button(label="Submit")
|
# TODO: strings
|
||||||
|
self.submit = Gtk.Button(label="Validate")
|
||||||
self.submit.set_sensitive(False)
|
self.submit.set_sensitive(False)
|
||||||
self.submit.connect("clicked", self._on_submit)
|
self.submit.connect("clicked", self._on_submit)
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,13 @@ class PageNum(Enum):
|
|||||||
FINAL = 8
|
FINAL = 8
|
||||||
|
|
||||||
|
|
||||||
|
class OptionalPageMixin:
|
||||||
|
"""Marks optional pages as advanceable"""
|
||||||
|
|
||||||
|
def _on_map(self, page: "ScrolledWizardPage") -> None:
|
||||||
|
EMITTER.emit("step_complete")
|
||||||
|
|
||||||
|
|
||||||
class DescriptionArea(Gtk.Box):
|
class DescriptionArea(Gtk.Box):
|
||||||
def __init__(self, text: str):
|
def __init__(self, text: str):
|
||||||
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
||||||
@@ -91,6 +98,7 @@ class ScrolledWizardPage(Gtk.ScrolledWindow):
|
|||||||
margin_top=50,
|
margin_top=50,
|
||||||
spacing=20,
|
spacing=20,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.add(self.box)
|
self.add(self.box)
|
||||||
self.prog = Progress()
|
self.prog = Progress()
|
||||||
self.box.pack_end(self.prog, expand=False, fill=False, padding=0)
|
self.box.pack_end(self.prog, expand=False, fill=False, padding=0)
|
||||||
@@ -215,7 +223,7 @@ class APIValidationPage(ScrolledWizardPage):
|
|||||||
self.spinner.stop()
|
self.spinner.stop()
|
||||||
|
|
||||||
|
|
||||||
class BMValidationPage(APIValidationPage):
|
class BMValidationPage(OptionalPageMixin, APIValidationPage): # type: ignore
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
enum=PageNum.BM_API,
|
enum=PageNum.BM_API,
|
||||||
@@ -224,6 +232,7 @@ class BMValidationPage(APIValidationPage):
|
|||||||
link=BM_API_SETUP,
|
link=BM_API_SETUP,
|
||||||
func=self._validate,
|
func=self._validate,
|
||||||
)
|
)
|
||||||
|
self.connect("map", self._on_map)
|
||||||
|
|
||||||
@call_on_thread("", show_dialog=False)
|
@call_on_thread("", show_dialog=False)
|
||||||
def _validate(self, key: str) -> None:
|
def _validate(self, key: str) -> None:
|
||||||
@@ -444,6 +453,7 @@ class Assistant(Gtk.Assistant):
|
|||||||
else:
|
else:
|
||||||
self.set_default_size(1500, 900)
|
self.set_default_size(1500, 900)
|
||||||
|
|
||||||
|
self.is_binary = False if os.getenv("PYAPP") is None else True
|
||||||
self.config_path = XDG.config
|
self.config_path = XDG.config
|
||||||
|
|
||||||
self.config_values: dict[str, Any] = config_boilerplate
|
self.config_values: dict[str, Any] = config_boilerplate
|
||||||
@@ -484,7 +494,7 @@ class Assistant(Gtk.Assistant):
|
|||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
# NOTE: disabled for now on system-provided packages
|
# NOTE: disabled for now on system-provided packages
|
||||||
if isinstance(page, ShortcutCreationPage) and os.getenv("PYAPP") is None:
|
if isinstance(page, ShortcutCreationPage) and not self.is_binary:
|
||||||
continue
|
continue
|
||||||
self._add_page(page, page.get_page_type())
|
self._add_page(page, page.get_page_type())
|
||||||
|
|
||||||
@@ -499,32 +509,33 @@ class Assistant(Gtk.Assistant):
|
|||||||
|
|
||||||
def _advance_page(self, index: int) -> int:
|
def _advance_page(self, index: int) -> int:
|
||||||
page = self.get_nth_page(index)
|
page = self.get_nth_page(index)
|
||||||
# TODO: use enums/isinstance
|
|
||||||
match page:
|
match page:
|
||||||
case self.page1:
|
case IntroductionPage():
|
||||||
pass
|
pass
|
||||||
case self.page2:
|
case ConfigMigrationPage():
|
||||||
if self.page2.is_migrated():
|
if page.is_migrated():
|
||||||
steam_path = lookup(self.config_path, Preferences.DEFAULT)
|
steam_path = lookup(self.config_path, Preferences.DEFAULT)
|
||||||
self.page7.set_steam_path(steam_path)
|
self.page7.set_steam_path(steam_path)
|
||||||
return self.get_n_pages() - 2
|
offset = 1 if not self.is_binary else 2
|
||||||
case self.page3:
|
self.setup_complete = True
|
||||||
|
return self.get_n_pages() - offset
|
||||||
|
case SteamPathPage():
|
||||||
self.config_values["default_steam_path"] = page.get_path_from_radio()
|
self.config_values["default_steam_path"] = page.get_path_from_radio()
|
||||||
case self.page4:
|
case SteamValidationPage():
|
||||||
self.config_values["steam_api"] = page.get_api_key()
|
self.config_values["steam_api"] = page.get_api_key()
|
||||||
case self.page5:
|
case BMValidationPage():
|
||||||
self.config_values["bm_api"] = page.get_api_key()
|
self.config_values["bm_api"] = page.get_api_key()
|
||||||
# NOTE: collects config values before advancing to last page
|
case PreferencesPage():
|
||||||
case self.page6:
|
# NOTE: collects config values before advancing to last page
|
||||||
name, use_miles, client = self.page6.get_prefs()
|
name, use_miles, client = self.page6.get_prefs()
|
||||||
self.config_values["name"] = name
|
self.config_values["name"] = name
|
||||||
self.config_values["use_miles"] = use_miles
|
self.config_values["use_miles"] = use_miles
|
||||||
self.config_values["client"] = client
|
self.config_values["client"] = client
|
||||||
self.write_config()
|
self.write_config()
|
||||||
self.page7.set_steam_path(self.config_values["default_steam_path"])
|
self.page7.set_steam_path(self.config_values["default_steam_path"])
|
||||||
case self.page7:
|
|
||||||
self.page7.create_shortcuts()
|
|
||||||
self.setup_complete = True
|
self.setup_complete = True
|
||||||
|
case ShortcutCreationPage():
|
||||||
|
page.create_shortcuts()
|
||||||
case _:
|
case _:
|
||||||
raise AttributeError("Trying to advance a non-canonical page")
|
raise AttributeError("Trying to advance a non-canonical page")
|
||||||
return index + 1
|
return index + 1
|
||||||
@@ -568,11 +579,7 @@ class Assistant(Gtk.Assistant):
|
|||||||
bar.set_fraction(fraction)
|
bar.set_fraction(fraction)
|
||||||
bar.set_text(f"{page_num}/{total}")
|
bar.set_text(f"{page_num}/{total}")
|
||||||
|
|
||||||
# NOTE: disable forward action
|
if not isinstance(page, IntroductionPage):
|
||||||
# TODO: use page enums
|
|
||||||
if page == self.page5:
|
|
||||||
return
|
|
||||||
if page != self.page1:
|
|
||||||
EMITTER.emit("step_pending")
|
EMITTER.emit("step_pending")
|
||||||
|
|
||||||
|
|
||||||
@@ -599,7 +606,7 @@ class CheckboxWithLabel(Gtk.Box):
|
|||||||
self.button.set_active(state)
|
self.button.set_active(state)
|
||||||
|
|
||||||
|
|
||||||
class ShortcutCreationPage(ScrolledWizardPage):
|
class ShortcutCreationPage(OptionalPageMixin, ScrolledWizardPage): # type: ignore
|
||||||
def __init__(self, shortcut: Path) -> None:
|
def __init__(self, shortcut: Path) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
enum=PageNum.SHORTCUTS,
|
enum=PageNum.SHORTCUTS,
|
||||||
@@ -642,9 +649,6 @@ class ShortcutCreationPage(ScrolledWizardPage):
|
|||||||
self.desktop_checkbox.set_active(state)
|
self.desktop_checkbox.set_active(state)
|
||||||
self.desktop_checkbox.set_sensitive(state)
|
self.desktop_checkbox.set_sensitive(state)
|
||||||
|
|
||||||
def _on_map(self, page: "ScrolledWizardPage") -> None:
|
|
||||||
EMITTER.emit("step_complete")
|
|
||||||
|
|
||||||
def set_steam_path(self, path: Path) -> None:
|
def set_steam_path(self, path: Path) -> None:
|
||||||
self.steam_path = path
|
self.steam_path = path
|
||||||
|
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ class PreConnectionAssistant(Gtk.Box):
|
|||||||
)
|
)
|
||||||
|
|
||||||
allows_dl, running_app = prereqs.allows_downloads
|
allows_dl, running_app = prereqs.allows_downloads
|
||||||
if allows_dl is False:
|
if len(prereqs.mods) > 0 and allows_dl is False:
|
||||||
msg = (
|
msg = (
|
||||||
f"The game '{running_app}' is currently running in Steam, but background downloads are not enabled.\n"
|
f"The game '{running_app}' is currently running in Steam, but background downloads are not enabled.\n"
|
||||||
"Either stop the game first, or update your global Steam settings or the game's local settings.\n"
|
"Either stop the game first, or update your global Steam settings or the game's local settings.\n"
|
||||||
|
|||||||
+3
-2
@@ -4,7 +4,7 @@ description = "DayZ server browser and mod manager for Linux"
|
|||||||
authors = [
|
authors = [
|
||||||
{name = "aclist"}
|
{name = "aclist"}
|
||||||
]
|
]
|
||||||
version = "7.0.0b16"
|
version = "7.0.0b18"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
license-files = ["LICENSE"]
|
license-files = ["LICENSE"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -107,11 +107,12 @@ config-settings-package = { pygobject-stubs = { config = "Gtk3,Gdk3,Soup2" } }
|
|||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
markers = [
|
markers = [
|
||||||
|
"apitest: checks remote endpoints",
|
||||||
"config: config file keys/values",
|
"config: config file keys/values",
|
||||||
"mods: tests mod metadata/link creation",
|
"mods: tests mod metadata/link creation",
|
||||||
"pefile: validate PE files",
|
"pefile: validate PE files",
|
||||||
"post_install: requires a completed installation",
|
"post_install: requires a completed installation",
|
||||||
"redact: log redaction mechanisms",
|
"redact: log redaction mechanisms",
|
||||||
"slow: long-running tests",
|
"slow: long-running tests",
|
||||||
"webtest: checks remote endpoints"
|
"webtest: depends on remote endpoint",
|
||||||
]
|
]
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
"users"
|
||||||
|
{
|
||||||
|
"0"
|
||||||
|
{
|
||||||
|
"AccountName" "STEAMUSER"
|
||||||
|
"PersonaName" "STEAMUSER"
|
||||||
|
"RememberPassword" "0"
|
||||||
|
"WantsOfflineMode" "0"
|
||||||
|
"SkipOfflineModeWarning" "0"
|
||||||
|
"AutoLogin" "1"
|
||||||
|
"Timestamp" "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
"users"
|
||||||
|
{
|
||||||
|
"0"
|
||||||
|
{
|
||||||
|
"AccountName" "STEAMUSER"
|
||||||
|
"PersonaName" "STEAMUSER"
|
||||||
|
"RememberPassword" "0"
|
||||||
|
"WantsOfflineMode" "0"
|
||||||
|
"SkipOfflineModeWarning" "0"
|
||||||
|
"AllowAutoLogin" "1"
|
||||||
|
"MostRecent" "1"
|
||||||
|
"Timestamp" "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,14 @@ class RecordsListHandler(logging.Handler):
|
|||||||
"Error in directory: '/home/SENSITIVE_USERNAME'",
|
"Error in directory: '/home/SENSITIVE_USERNAME'",
|
||||||
"Error in directory: '/home/REDACTED'",
|
"Error in directory: '/home/REDACTED'",
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"User directory: /home/user/.local/share/Steam/userdata/999999/grid",
|
||||||
|
"User directory: /home/REDACTED/.local/share/Steam/userdata/REDACTED/grid",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"User directory: /drive/.local/share/Steam/userdata/999999",
|
||||||
|
"User directory: /drive/.local/share/Steam/userdata/REDACTED",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_log_redaction(log_error: str, expect: str) -> None:
|
def test_log_redaction(log_error: str, expect: str) -> None:
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -12,7 +12,7 @@ from dzgui.api.steam import (
|
|||||||
|
|
||||||
from dzgui.config.query import get_config
|
from dzgui.config.query import get_config
|
||||||
from dzgui.config.xdg import get_xdg_paths, parse_filepaths
|
from dzgui.config.xdg import get_xdg_paths, parse_filepaths
|
||||||
from dzgui.const.constants import APPID_DAYZ, LIBRARYFOLDERS_PATH
|
from dzgui.const.constants import APPID_DAYZ
|
||||||
|
|
||||||
from tests.fixtures import fixture_path
|
from tests.fixtures import fixture_path
|
||||||
|
|
||||||
|
|||||||
+32
-8
@@ -30,33 +30,33 @@ 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
|
||||||
|
|
||||||
|
|
||||||
def test_wrap_exe(dummy_app) -> None:
|
def test_wrap_exe(dummy_app: dict[str, str]) -> 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: dict[str, str]) -> None:
|
||||||
s = Shortcuts(Path(""))
|
s = Shortcuts(Path(""))
|
||||||
s.add_shortcut(*dummy_app.values())
|
s.add_shortcut(*dummy_app.values())
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
def test_save_shortcut(dummy_app) -> None:
|
def test_save_shortcut(dummy_app: dict[str, str]) -> None:
|
||||||
s = Shortcuts(Path(""))
|
s = Shortcuts(Path(""))
|
||||||
s.add_shortcut(*dummy_app.values())
|
s.add_shortcut(*dummy_app.values())
|
||||||
with tempfile.NamedTemporaryFile() as f:
|
with tempfile.NamedTemporaryFile() as f:
|
||||||
@@ -65,3 +65,27 @@ def test_save_shortcut(dummy_app) -> None:
|
|||||||
s.save_shortcuts()
|
s.save_shortcuts()
|
||||||
s._load_shortcuts(tmp)
|
s._load_shortcuts(tmp)
|
||||||
assert len(s.shortcuts["shortcuts"]) == 1
|
assert len(s.shortcuts["shortcuts"]) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_shortcut_crc(dummy_app: dict[str, str]) -> None:
|
||||||
|
s = Shortcuts(Path(""))
|
||||||
|
s.add_shortcut(*dummy_app.values())
|
||||||
|
for key in s.shortcuts["shortcuts"].keys():
|
||||||
|
entry = s.shortcuts["shortcuts"][key]
|
||||||
|
name = entry["AppName"]
|
||||||
|
exe = entry["Exe"]
|
||||||
|
uid = name + exe
|
||||||
|
bpid = s.gen_bpid(uid)
|
||||||
|
assert entry["appid"] & 0xFFFFFFFF == bpid
|
||||||
|
|
||||||
|
|
||||||
|
def test_reverse_crc(dummy_app: dict[str, str]) -> None:
|
||||||
|
s = Shortcuts(Path(""))
|
||||||
|
s.add_shortcut(*dummy_app.values())
|
||||||
|
for key in s.shortcuts["shortcuts"].keys():
|
||||||
|
entry = s.shortcuts["shortcuts"][key]
|
||||||
|
name = entry["AppName"]
|
||||||
|
exe = entry["Exe"]
|
||||||
|
uid = name + exe
|
||||||
|
bpid = s.gen_bpid(uid)
|
||||||
|
assert s.find_appname_by_unsigned_id(bpid) == name
|
||||||
Reference in new issue
Block a user