diff --git a/rare/commands/launcher/lgd_helper.py b/rare/commands/launcher/lgd_helper.py
index 48ee17888e..595800a6be 100644
--- a/rare/commands/launcher/lgd_helper.py
+++ b/rare/commands/launcher/lgd_helper.py
@@ -11,6 +11,7 @@
from PySide6.QtCore import QProcess, QProcessEnvironment
from rare.models.game_slim import RareGameSlim
+from rare.utils.compat.utils import create_compat_users
from rare.utils.paths import setup_compat_shaders_dir
logger = getLogger("RareLauncherUtils")
@@ -155,21 +156,21 @@ def prepare_process(command: List[str], environment: Dict) -> Tuple[str, List[st
environ = environment.copy()
# Sanity check environment (mostly for Linux)
- command_line = shlex.join(command)
- if os.environ.get("XDG_CURRENT_DESKTOP", None) == "gamescope" or "gamescope" in command_line:
- # disable mangohud in gamescope
- environ["MANGOHUD"] = "0"
# ensure shader compat dirs exist
if platform.system() in {"Linux", "FreeBSD"}:
- environ["UMU_USE_STEAM"] = "1"
+ command_line = shlex.join(command)
+ if os.environ.get("XDG_CURRENT_DESKTOP", None) == "gamescope" or "gamescope" in command_line:
+ # disable mangohud in gamescope
+ environ["MANGOHUD"] = "0"
if "STEAM_COMPAT_CLIENT_INSTALL_PATH" not in environ:
environ["STEAM_COMPAT_CLIENT_INSTALL_PATH"] = ""
- if "WINEPREFIX" in environ and not os.path.isdir(environ["WINEPREFIX"]):
- os.makedirs(environ["WINEPREFIX"], exist_ok=True)
if "STEAM_COMPAT_DATA_PATH" in environ:
compat_pfx = os.path.join(environ["STEAM_COMPAT_DATA_PATH"], "pfx")
- if not os.path.isdir(compat_pfx):
- os.makedirs(compat_pfx, exist_ok=True)
+ os.makedirs(compat_pfx, exist_ok=True)
+ create_compat_users(compat_pfx)
+ if "WINEPREFIX" in environ and not os.path.isdir(environ["WINEPREFIX"]):
+ os.makedirs(environ["WINEPREFIX"], exist_ok=True)
+ create_compat_users(environ["WINEPREFIX"])
if "STEAM_COMPAT_SHADER_PATH" in environ:
environ.update(setup_compat_shaders_dir(environ["STEAM_COMPAT_SHADER_PATH"]))
environ["WINEDLLOVERRIDES"] = environ.get("WINEDLLOVERRIDES", "") + ";lsteamclient=d;"
diff --git a/rare/components/__init__.py b/rare/components/__init__.py
index e34f31cc70..9c460997c3 100644
--- a/rare/components/__init__.py
+++ b/rare/components/__init__.py
@@ -73,22 +73,22 @@ def relogin(self):
@Slot()
def launch_app(self):
self.launch_dialog = LaunchDialog(self.rcore, parent=None)
- self.launch_dialog.rejected.connect(self.__on_exit_app)
+ self.launch_dialog.rejected.connect(self._on_exit_app)
# lk: the reason we use the `start_app` signal here instead of accepted, is to keep the dialog
# until the main window has been created, then we call `accept()` to close the dialog
- self.launch_dialog.start_app.connect(self.__on_start_app)
+ self.launch_dialog.start_app.connect(self._on_start_app)
self.launch_dialog.start_app.connect(self.launch_dialog.accept)
self.launch_dialog.login()
@Slot()
- def __on_start_app(self):
+ def _on_start_app(self):
self.relogin_timer = QTimer(self)
self.relogin_timer.setTimerType(Qt.TimerType.VeryCoarseTimer)
self.relogin_timer.timeout.connect(self.relogin)
self.poke_timer()
self.main_window = RareWindow(self.settings, self.rcore)
- self.main_window.exit_app.connect(self.__on_exit_app)
+ self.main_window.exit_app.connect(self._on_exit_app)
if (not self.args.silent) and (not self.settings.get_value(app_settings.sys_tray_start)):
self.main_window.show()
@@ -96,11 +96,11 @@ def __on_start_app(self):
if self.args.test_start:
self.main_window.close()
self.main_window = None
- self.__on_exit_app(0)
+ self._on_exit_app(0)
@Slot()
@Slot(int)
- def __on_exit_app(self, exit_code=0):
+ def _on_exit_app(self, exit_code=0):
threadpool = QThreadPool.globalInstance()
threadpool.waitForDone()
if self.relogin_timer is not None:
@@ -118,7 +118,7 @@ def __on_exit_app(self, exit_code=0):
self.exit(exit_code)
-def start(args) -> int:
+def start(args: Namespace) -> int:
while True:
QApplication.setAttribute(Qt.ApplicationAttribute.AA_EnableHighDpiScaling, True)
QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseHighDpiPixmaps, True)
diff --git a/rare/components/dialogs/install/dialog.py b/rare/components/dialogs/install/dialog.py
index 592b5c1684..70d261760a 100644
--- a/rare/components/dialogs/install/dialog.py
+++ b/rare/components/dialogs/install/dialog.py
@@ -52,9 +52,9 @@ def __init__(self, settings: RareAppSettings, rgame: "RareGame", options: Instal
self.core = rgame.core
self.rgame = rgame
- self.__options: InstallOptionsModel = options
- self.__download: Optional[InstallDownloadModel] = None
- self.__queue_item: Optional[InstallQueueItemModel] = None
+ self._options: InstallOptionsModel = options
+ self._download: Optional[InstallDownloadModel] = None
+ self._queue_item: Optional[InstallQueueItemModel] = None
self.selectable = InstallDialogSelective(rgame, parent=self)
self.selectable.stateChanged.connect(self._on_option_changed)
@@ -86,11 +86,11 @@ def __init__(self, settings: RareAppSettings, rgame: "RareGame", options: Instal
self.install_dir_edit = PathEdit(
path=options.base_path,
file_mode=QFileDialog.FileMode.Directory,
- edit_func=self.__install_dir_edit_callback,
- save_func=self.__install_dir_save_callback,
+ edit_func=self._install_dir_edit_callback,
+ save_func=self._install_dir_save_callback,
parent=self,
)
- self.install_dir_edit.validationFinished.connect(self.__on_install_dir_validation)
+ self.install_dir_edit.validationFinished.connect(self._on_install_dir_validation)
self.ui.main_layout.setWidget(
self.ui.main_layout.getWidgetPosition(self.ui.install_dir_label)[0],
QFormLayout.ItemRole.FieldRole,
@@ -145,13 +145,9 @@ def __init__(self, settings: RareAppSettings, rgame: "RareGame", options: Instal
self.advanced.ui.download_only_check.setChecked(options.no_install)
self.advanced.ui.download_only_check.checkStateChanged.connect(self._on_option_changed_no_reload)
- self.reset_install_dir(self.ui.platform_combo.currentIndex())
- self.selectable.update_list(self.ui.platform_combo.currentText())
- self.check_incompatible_platform(self.ui.platform_combo.currentIndex())
-
self.accept_button.setEnabled(False)
- if self.__options.overlay:
+ if self._options.overlay:
self.ui.platform_label.setEnabled(False)
self.ui.platform_combo.setEnabled(False)
self.advanced.ui.ignore_space_label.setEnabled(False)
@@ -171,7 +167,7 @@ def __init__(self, settings: RareAppSettings, rgame: "RareGame", options: Instal
self.advanced.ui.install_prereqs_label.setEnabled(False)
self.advanced.ui.install_prereqs_check.setEnabled(False)
self.advanced.ui.install_prereqs_check.checkStateChanged.connect(self._on_option_changed_no_reload)
- self.advanced.ui.install_prereqs_check.setChecked(self.__options.install_prereqs)
+ self.advanced.ui.install_prereqs_check.setChecked(self._options.install_prereqs)
# lk: set object names for CSS properties
self.accept_button.setText(header)
@@ -190,7 +186,11 @@ def showEvent(self, a0: QShowEvent) -> None:
return super().showEvent(a0)
def execute(self):
- if self.__options.silent:
+ self.reset_install_dir(self.ui.platform_combo.currentIndex())
+ self.check_incompatible_platform(self.ui.platform_combo.currentIndex())
+ self.selectable.update_list(self.ui.platform_combo.currentText())
+
+ if self._options.silent:
self.get_download_info()
else:
self.action_handler()
@@ -215,31 +215,31 @@ def check_incompatible_platform(self, index: int):
self.set_error_labels()
def get_options(self):
- base_path = os.path.join(self.install_dir_edit.text(), ".overlay" if self.__options.overlay else "")
+ base_path = os.path.join(self.install_dir_edit.text(), ".overlay" if self._options.overlay else "")
# TODO: investigate if this check is needed
if self.rgame.is_installed or self.rgame.is_dlc:
- self.__options.base_path = ""
+ self._options.base_path = ""
else:
- self.__options.base_path = base_path
- self.__options.platform = self.ui.platform_combo.currentText()
- self.__options.create_shortcut = self.ui.shortcut_check.isChecked()
- self.__options.max_workers = self.advanced.ui.max_workers_spin.value()
- self.__options.shared_memory = self.advanced.ui.max_memory_spin.value()
- self.__options.read_files = self.advanced.ui.read_files_check.isChecked()
- self.__options.always_use_signed_urls = self.advanced.ui.use_signed_urls_check.isChecked()
- self.__options.order_opt = self.advanced.ui.dl_optimizations_check.isChecked()
- self.__options.force = self.advanced.ui.force_download_check.isChecked()
- self.__options.ignore_space = self.advanced.ui.ignore_space_check.isChecked()
- self.__options.no_install = self.advanced.ui.download_only_check.isChecked()
- self.__options.install_prereqs = self.advanced.ui.install_prereqs_check.isChecked()
- self.__options.install_tag = self.selectable.install_tags()
- self.__options.reset_sdl = True
+ self._options.base_path = base_path
+ self._options.platform = self.ui.platform_combo.currentText()
+ self._options.create_shortcut = self.ui.shortcut_check.isChecked()
+ self._options.max_workers = self.advanced.ui.max_workers_spin.value()
+ self._options.shared_memory = self.advanced.ui.max_memory_spin.value()
+ self._options.read_files = self.advanced.ui.read_files_check.isChecked()
+ self._options.always_use_signed_urls = self.advanced.ui.use_signed_urls_check.isChecked()
+ self._options.order_opt = self.advanced.ui.dl_optimizations_check.isChecked()
+ self._options.force = self.advanced.ui.force_download_check.isChecked()
+ self._options.ignore_space = self.advanced.ui.ignore_space_check.isChecked()
+ self._options.no_install = self.advanced.ui.download_only_check.isChecked()
+ self._options.install_prereqs = self.advanced.ui.install_prereqs_check.isChecked()
+ self._options.install_tag = self.selectable.enabled_tags()
+ self._options.reset_sdl = True
def get_download_info(self):
- self.__download = None
- info_worker = InstallInfoWorker(self.core, self.__options)
- info_worker.signals.result.connect(self.__on_worker_result)
- info_worker.signals.failed.connect(self.__on_worker_failed)
+ self._download = None
+ info_worker = InstallInfoWorker(self.core, self._options)
+ info_worker.signals.result.connect(self._on_worker_result)
+ info_worker.signals.failed.connect(self._on_worker_failed)
self.threadpool.start(info_worker)
def action_handler(self):
@@ -260,15 +260,15 @@ def _on_option_changed(self):
@Slot(Qt.CheckState)
def _on_option_changed_no_reload(self, state: Qt.CheckState):
if self.sender() is self.advanced.ui.download_only_check:
- self.__options.no_install = state != Qt.CheckState.Unchecked
+ self._options.no_install = state != Qt.CheckState.Unchecked
elif self.sender() is self.ui.shortcut_check:
self.settings.set_value(app_settings.create_shortcut, state != Qt.CheckState.Unchecked)
- self.__options.create_shortcut = state != Qt.CheckState.Unchecked
+ self._options.create_shortcut = state != Qt.CheckState.Unchecked
elif self.sender() is self.advanced.ui.install_prereqs_check:
- self.__options.install_prereqs = state != Qt.CheckState.Unchecked
+ self._options.install_prereqs = state != Qt.CheckState.Unchecked
@staticmethod
- def __install_dir_edit_callback(path: str) -> Tuple[bool, str, int]:
+ def _install_dir_edit_callback(path: str) -> Tuple[bool, str, int]:
if not path:
return False, path, IndicatorReasonsCommon.IS_EMPTY
try:
@@ -281,14 +281,14 @@ def __install_dir_edit_callback(path: str) -> Tuple[bool, str, int]:
return False, path, IndicatorReasonsCommon.DIR_NOT_EXISTS
return True, path, IndicatorReasonsCommon.VALID
- def __install_dir_save_callback(self, path: str):
+ def _install_dir_save_callback(self, path: str):
if not os.path.exists(path):
return
_, _, free_space = shutil.disk_usage(path)
self.ui.available_space_text.setText(format_size(free_space))
@Slot(bool, str)
- def __on_install_dir_validation(self, is_valid: bool, reason: str):
+ def _on_install_dir_validation(self, is_valid: bool, reason: str):
self.accept_button.setEnabled(False)
self.action_button.setEnabled(is_valid and not self.active())
if not is_valid:
@@ -307,9 +307,9 @@ def same_platform(download: InstallDownloadModel) -> bool:
return False
@Slot(InstallDownloadModel)
- def __on_worker_result(self, download: InstallDownloadModel):
+ def _on_worker_result(self, download: InstallDownloadModel):
self.setActive(False)
- self.__download = download
+ self._download = download
download_size = download.analysis.dl_size
install_size = download.analysis.install_size
# install_size = self.dl_item.download.analysis.disk_space_delta
@@ -330,28 +330,30 @@ def __on_worker_result(self, download: InstallDownloadModel):
self.advanced.ui.install_prereqs_check.setEnabled(has_prereqs)
self.advanced.ui.install_prereqs_check.setChecked(has_prereqs and self.same_platform(download))
- # new_manifest_data, _, _ = self.core.get_cdn_manifest(download.game, download.igame.platform, self.__options.disable_https)
+ # TODO: there is information about the files to be downloaded in analres, don't fetch the manifest again
+ # TODO: see if you can re-use the one from selective downloads
+ # new_manifest_data, _, _ = self.core.get_cdn_manifest(download.game, download.igame.platform, self._options.disable_https)
# new_manifest = self.core.load_manifest(new_manifest_data)
# self.file_filters.clear()
# for e in new_manifest.file_manifest_list.elements:
# self.file_filters.add_item(e.filename.lower())
- if self.__options.silent:
+ if self._options.silent:
self.accept()
@Slot(str)
- def __on_worker_failed(self, message: str):
+ def _on_worker_failed(self, message: str):
self.setActive(False)
error_text = self.tr("Error")
self.set_size_labels(error_text, error_text)
self.set_error_labels(error_text, message)
self.action_button.setEnabled(self.options_changed)
self.accept_button.setEnabled(False)
- if self.__options.silent:
+ if self._options.silent:
self.open()
@staticmethod
- def __set_size_label(label: QLabel, value: Union[int, float, str]):
+ def _set_size_label(label: QLabel, value: Union[int, float, str]):
is_numeric = isinstance(value, (int, float))
font = label.font()
font.setBold(is_numeric)
@@ -361,8 +363,8 @@ def __set_size_label(label: QLabel, value: Union[int, float, str]):
label.setText(text)
def set_size_labels(self, download: Union[int, float, str], install: Union[int, float, str]):
- self.__set_size_label(self.ui.download_size_text, download)
- self.__set_size_label(self.ui.install_size_text, install)
+ self._set_size_label(self.ui.download_size_text, download)
+ self._set_size_label(self.ui.install_size_text, install)
def set_error_labels(self, label: str = "", message: str = ""):
self.ui.warning_label.setVisible(bool(label))
@@ -373,11 +375,11 @@ def set_error_labels(self, label: str = "", message: str = ""):
def done_handler(self):
self.threadpool.clear()
self.threadpool.waitForDone()
- self.result_ready.emit(self.__queue_item)
+ self.result_ready.emit(self._queue_item)
- # lk: __download is already set at this point so just do nothing.
+ # lk: _download is already set at this point so just do nothing.
def accept_handler(self):
- self.__queue_item = InstallQueueItemModel(options=self.__options, download=self.__download)
+ self._queue_item = InstallQueueItemModel(options=self._options, download=self._download)
def reject_handler(self):
- self.__queue_item = InstallQueueItemModel(options=self.__options, download=None)
+ self._queue_item = InstallQueueItemModel(options=self._options, download=None)
diff --git a/rare/components/dialogs/install/selective.py b/rare/components/dialogs/install/selective.py
index cf79b4e4f0..4f016f11c1 100644
--- a/rare/components/dialogs/install/selective.py
+++ b/rare/components/dialogs/install/selective.py
@@ -26,40 +26,44 @@ class SelectiveWidget(QWidget):
def __init__(self, rgame: RareGame, platform: str, parent=None):
super().__init__(parent=parent)
+ self._has_tags = False
main_layout = QVBoxLayout(self)
- main_layout.setSpacing(0)
+ main_layout.setSpacing(2)
core = rgame.core
config_tags = core.lgd.config.get(rgame.app_name, "install_tags", fallback=None)
config_disable_sdl = core.lgd.config.getboolean(rgame.app_name, "disable_sdl", fallback=False)
- sdl_name = get_sdl_appname(rgame.app_name)
- if not config_disable_sdl and sdl_name is not None:
- sdl_data = core.get_sdl_data(sdl_name, platform=platform)
- if sdl_data:
- for tag, info in sdl_data.items():
- cb = InstallTagCheckBox(info["name"].strip(), info["description"].strip(), info["tags"])
- if tag == "__required":
+
+ sdl_data = rgame.sdl_data(platform)
+ if not config_disable_sdl and sdl_data:
+ for group, info in sdl_data.items():
+ cb = InstallTagCheckBox(info["name"].strip(), info["description"].strip(), info["tags"])
+ if group == "__required":
+ cb.setChecked(True)
+ cb.setDisabled(True)
+ if config_tags is not None:
+ if all(tag in config_tags for tag in info["tags"]):
cb.setChecked(True)
- cb.setDisabled(True)
- if config_tags is not None:
- if all(elem in config_tags for elem in info["tags"]):
- cb.setChecked(True)
- cb.stateChanged.connect(self.stateChanged)
- main_layout.addWidget(cb)
- self.parent().setDisabled(False)
+ cb.stateChanged.connect(self.stateChanged)
+ main_layout.addWidget(cb)
+ self._has_tags = True
else:
- self.parent().setDisabled(True)
+ self._has_tags = False
- def install_tags(self):
- install_tags = [""]
+ def enabled_tags(self) -> List[str]:
+ install_tags = set()
for cb in self.findChildren(InstallTagCheckBox, options=Qt.FindChildOption.FindDirectChildrenOnly):
if data := cb.isChecked():
# noinspection PyTypeChecker
- install_tags.extend(data)
+ install_tags.update(data)
+ install_tags = ["", *install_tags]
return install_tags
+ def supports_tags(self) -> bool:
+ return self._has_tags
+
class InstallDialogSelective(CollapsibleFrame):
stateChanged: Signal = Signal()
@@ -68,7 +72,7 @@ def __init__(self, rgame: RareGame, parent=None):
super(InstallDialogSelective, self).__init__(parent=parent)
title = self.tr("Optional downloads")
self.setTitle(title)
- self.setEnabled(bool(rgame.sdl_name))
+ self.setEnabled(False)
self.widget: SelectiveWidget = None
self.rgame = rgame
@@ -78,11 +82,12 @@ def update_list(self, platform: str):
self.widget.disconnect(self.widget)
self.widget.deleteLater()
self.widget = SelectiveWidget(self.rgame, platform, parent=self)
+ self.setEnabled(self.widget.supports_tags())
self.widget.stateChanged.connect(self.stateChanged)
self.setWidget(self.widget)
- def install_tags(self):
- return self.widget.install_tags()
+ def enabled_tags(self) -> List[str]:
+ return self.widget.enabled_tags()
__all__ = ["InstallDialogSelective", "SelectiveWidget"]
diff --git a/rare/components/tabs/downloads/thread.py b/rare/components/tabs/downloads/thread.py
index 3a4386c40b..acaaae4c88 100644
--- a/rare/components/tabs/downloads/thread.py
+++ b/rare/components/tabs/downloads/thread.py
@@ -190,6 +190,7 @@ def chunk_url_sign_thread():
result.shortcut = not self.item.options.update and self.item.options.create_shortcut
result.folder_name = self.rgame.folder_name
+ return
finally:
ticket_thread.stop = True
sign_thread.stop = True
diff --git a/rare/components/tabs/library/details/cloud_saves.py b/rare/components/tabs/library/details/cloud_saves.py
index 2dfe6d326a..2fa3431490 100644
--- a/rare/components/tabs/library/details/cloud_saves.py
+++ b/rare/components/tabs/library/details/cloud_saves.py
@@ -68,7 +68,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.cloud_save_path_edit = PathEdit(
path="",
file_mode=QFileDialog.FileMode.Directory,
- placeholder=self.tr('Use "Calculate path" or "Browse" ...'),
+ placeholder=self.tr('Use "Resolve path" or "Browse" ...'),
edit_func=self.edit_save_path,
save_func=self.save_save_path,
)
diff --git a/rare/components/tabs/library/details/details.py b/rare/components/tabs/library/details/details.py
index 1626e9a120..7d28108ec6 100644
--- a/rare/components/tabs/library/details/details.py
+++ b/rare/components/tabs/library/details/details.py
@@ -184,7 +184,7 @@ def __on_verify(self):
self.tr("Installation path for {} does not exist. Cannot continue.").format(self.rgame.app_title),
)
return
- if self.rgame.sdl_name is not None:
+ if self.rgame.sdl_available:
selective_dialog = SelectiveDialog(self.rgame, parent=self)
selective_dialog.result_ready.connect(self.verify_game)
selective_dialog.open()
@@ -362,7 +362,7 @@ def __update_widget(self):
self.ui.import_button.setEnabled((not self.rgame.is_installed or self.rgame.is_non_asset) and self.rgame.is_idle)
self.ui.modify_button.setEnabled(
- self.rgame.is_installed and (not self.rgame.is_non_asset) and self.rgame.is_idle and self.rgame.sdl_name is not None
+ self.rgame.is_installed and (not self.rgame.is_non_asset) and self.rgame.is_idle and self.rgame.sdl_available
)
self.ui.verify_button.setEnabled(self.rgame.is_installed and (not self.rgame.is_non_asset) and self.rgame.is_idle)
diff --git a/rare/components/tabs/settings/widgets/overlay.py b/rare/components/tabs/settings/widgets/overlay.py
index b8e87b0a3b..dc68dd49f7 100644
--- a/rare/components/tabs/settings/widgets/overlay.py
+++ b/rare/components/tabs/settings/widgets/overlay.py
@@ -151,9 +151,9 @@ def __init__(self, parent=None):
# self.values: Dict[str, Union[OverlayLineEdit, OverlayComboBox]] = {}
self.ui.options_group.setTitle(self.tr("Custom options"))
- self.ui.overlay_state_combo.currentIndexChanged.connect(self.__update_settings)
+ self.ui.overlay_state_combo.currentIndexChanged.connect(self._update_settings)
- self.environ_changed.connect(self.__update_current_value)
+ self.environ_changed.connect(self._update_current_value)
def setupWidget(
self,
@@ -174,26 +174,26 @@ def setupWidget(
self.ui.overlay_state_label.setText(label)
- for i, widget in enumerate(grid_map):
+ for idx, widget in enumerate(grid_map):
widget.setParent(self.ui.options_group)
- self.ui.options_grid.addWidget(widget, i // 4, i % 4)
+ self.ui.options_grid.addWidget(widget, idx // 4, idx % 4)
# self.checkboxes[widget.option] = widget
self.option_widgets.append(widget)
- widget.stateChanged.connect(self.__update_settings)
+ widget.stateChanged.connect(self._update_settings)
for widget, label in form_map:
widget.setParent(self.ui.options_group)
self.ui.options_form.addRow(label, widget)
# self.values[widget.option] = widget
self.option_widgets.append(widget)
- widget.valueChanged.connect(self.__update_settings)
+ widget.valueChanged.connect(self._update_settings)
@abstractmethod
def update_settings_override(self, state: ActivationStates):
raise NotImplementedError
@Slot()
- def __update_settings(self):
+ def _update_settings(self):
current_state = self.ui.overlay_state_combo.currentData(Qt.ItemDataRole.UserRole)
self.ui.options_group.setEnabled(current_state == ActivationStates.CUSTOM)
@@ -212,14 +212,13 @@ def __update_settings(self):
self.ui.options_group.setDisabled(False)
# custom options
options = (name for widget in self.option_widgets if (name := widget.getValue()) is not None)
-
config.set_envvar(self.app_name, self.config_key, self.separator.join(options))
self.environ_changed.emit(self.config_key)
self.update_settings_override(current_state)
@Slot()
- def __update_current_value(self):
+ def _update_current_value(self):
self.ui.current_value_info.setText(config.get_envvar_with_global(self.app_name, self.config_key, ""))
def setCurrentState(self, state: ActivationStates):
@@ -262,7 +261,7 @@ def showEvent(self, a0: QShowEvent):
)
self.ui.options_group.blockSignals(False)
- self.__update_current_value()
+ self._update_current_value()
return super().showEvent(a0)
diff --git a/rare/components/tabs/settings/widgets/proton.py b/rare/components/tabs/settings/widgets/proton.py
index 0ba250f098..4e73a75e43 100644
--- a/rare/components/tabs/settings/widgets/proton.py
+++ b/rare/components/tabs/settings/widgets/proton.py
@@ -16,7 +16,7 @@
from rare.models.wrapper import Wrapper, WrapperType
from rare.shared import RareCore
from rare.shared.wrappers import Wrappers
-from rare.utils import config_helper as lgd_conf
+from rare.utils import config_helper as lgd_config
from rare.utils.compat import steam
from rare.utils.paths import proton_compat_dir
from rare.widgets.indicator_edit import IndicatorReasonsCommon, PathEdit
@@ -49,18 +49,18 @@ def __init__(self, rcore: RareCore, parent=None):
self.setTitle(self.tr("Proton"))
self.tool_combo = QComboBox(self)
- self.tool_combo.currentIndexChanged.connect(self.__on_tool_changed)
+ self.tool_combo.currentIndexChanged.connect(self._on_tool_changed)
self.compat_combo = QComboBox(self)
self.compat_combo.addItem(self.tr("Shared"), ProtonSettings.CompatLocation.SHARED)
self.compat_combo.addItem(self.tr("Isolated"), ProtonSettings.CompatLocation.ISOLATED)
self.compat_combo.addItem(self.tr("Custom"), ProtonSettings.CompatLocation.CUSTOM)
- self.compat_combo.currentIndexChanged.connect(self.__on_compat_changed)
+ self.compat_combo.currentIndexChanged.connect(self._on_compat_changed)
self.compat_edit = PathEdit(
file_mode=QFileDialog.FileMode.Directory,
- edit_func=self.proton_prefix_edit,
- save_func=self.proton_prefix_save,
+ edit_func=self._proton_prefix_edit,
+ save_func=self._proton_prefix_save,
placeholder=self.tr("Please select path for proton prefix"),
parent=self,
)
@@ -127,7 +127,7 @@ def showEvent(self, a0: QShowEvent) -> None:
self.tool_combo.blockSignals(False)
enabled = bool(self.tool_combo.currentData(Qt.ItemDataRole.UserRole))
- compat_path = lgd_conf.get_compat_data_path(self.app_name, fallback="")
+ compat_path = lgd_config.get_compat_data_path(self.app_name, fallback="")
self.compat_combo.blockSignals(True)
compat_location = self._update_compat_folder(compat_path)
@@ -142,7 +142,7 @@ def showEvent(self, a0: QShowEvent) -> None:
return super().showEvent(a0)
@Slot(int)
- def __on_tool_changed(self, index):
+ def _on_tool_changed(self, index):
steam_tool: Union[steam.ProtonTool, steam.CompatibilityTool] = self.tool_combo.itemData(index, Qt.ItemDataRole.UserRole)
steam_environ = steam.get_steam_environment(steam_tool, self.compat_edit.text())
@@ -156,7 +156,7 @@ def __on_tool_changed(self, index):
steam_environ["STEAM_COMPAT_INSTALL_PATH"] = install_path
steam_environ["STEAM_COMPAT_LIBRARY_PATHS"] = library_paths
for key, value in steam_environ.items():
- lgd_conf.adjust_envvar(self.app_name, key, value)
+ lgd_config.adjust_envvar(self.app_name, key, value)
self.environ_changed.emit(key)
wrappers = self.wrappers.get_wrappers(self.app_name)
@@ -178,7 +178,7 @@ def __on_tool_changed(self, index):
self.compat_edit.setEnabled(steam_tool is not None)
compat_path = ""
if steam_tool:
- compat_path = lgd_conf.get_compat_data_path(self.app_name, fallback="")
+ compat_path = lgd_config.get_compat_data_path(self.app_name, fallback="")
if not compat_path:
compat_path = str(self._get_compat_path(ProtonSettings.CompatLocation.NONE))
self._update_compat_folder(compat_path)
@@ -186,10 +186,10 @@ def __on_tool_changed(self, index):
self.compat_tool_enabled.emit(steam_tool is not None, compat_path)
@Slot(int)
- def __on_compat_changed(self, index):
+ def _on_compat_changed(self, index):
compat_location: ProtonSettings.CompatLocation = self.compat_combo.itemData(index, Qt.ItemDataRole.UserRole)
compat_path = str(self._get_compat_path(compat_location))
- lgd_conf.adjust_compat_data_path(self.app_name, compat_path)
+ lgd_config.adjust_compat_data_path(self.app_name, compat_path)
self.compat_edit.setText(compat_path)
self.compat_edit.setEnabled(
compat_location
@@ -200,17 +200,23 @@ def __on_compat_changed(self, index):
)
@staticmethod
- def proton_prefix_edit(text: str) -> Tuple[bool, str, int]:
+ def _proton_prefix_edit(text: str) -> Tuple[bool, str, int]:
if not text:
return False, text, IndicatorReasonsCommon.IS_EMPTY
if os.path.isdir(text):
- if os.listdir(text) and not os.path.exists(os.path.join(text, "pfx")):
- return False, text, IndicatorReasonsCommon.DIR_NOT_EMPTY
- return True, text, IndicatorReasonsCommon.VALID
+ dir_list = os.listdir(text)
+ if not dir_list:
+ return True, text, IndicatorReasonsCommon.VALID
+ if any(
+ (x in dir_list) for x in
+ ("pfx", "shadercache", "dosdevices", "drive_c", "system.reg", "user.reg", "userdef.reg")
+ ):
+ return True, text, IndicatorReasonsCommon.VALID
+ return False, text, IndicatorReasonsCommon.DIR_NOT_EMPTY
else:
return False, text, IndicatorReasonsCommon.DIR_NOT_EXISTS
- def proton_prefix_save(self, text: str):
- lgd_conf.adjust_compat_data_path(self.app_name, text)
+ def _proton_prefix_save(self, text: str):
+ lgd_config.adjust_compat_data_path(self.app_name, text)
self.environ_changed.emit("STEAM_COMPAT_DATA_PATH")
self.compat_path_changed.emit(text)
diff --git a/rare/components/tabs/settings/widgets/runner.py b/rare/components/tabs/settings/widgets/runner.py
index 7ebe18cff1..3f2c50b575 100644
--- a/rare/components/tabs/settings/widgets/runner.py
+++ b/rare/components/tabs/settings/widgets/runner.py
@@ -1,11 +1,15 @@
+import os.path
import platform as pf
+from getpass import getuser
from typing import Type, TypeVar
-from PySide6.QtCore import Qt, Signal, Slot
-from PySide6.QtWidgets import QCheckBox, QFormLayout, QGroupBox, QVBoxLayout
+from PySide6.QtCore import Qt, QUrl, Signal, Slot
+from PySide6.QtGui import QDesktopServices
+from PySide6.QtWidgets import QCheckBox, QFormLayout, QGroupBox, QHBoxLayout, QPushButton, QVBoxLayout
from rare.models.settings import RareAppSettings, app_settings
from rare.shared import RareCore
+from rare.utils import config_helper as lgd_config
from .wine import WineSettings
@@ -33,20 +37,11 @@ def __init__(
self.setTitle(self.tr("Compatibility"))
- # self.compat_label = QLabel(self.tr("Runner"))
- # self.compat_combo = QComboBox(self)
- # self.compat_stack = QStackedWidget(self)
-
self.main_layout = QVBoxLayout(self)
self.wine = wine_widget(settings, rcore, self)
self.wine.environ_changed.connect(self.environ_changed)
self.main_layout.addWidget(self.wine)
- # self.compat_layout = QFormLayout(self.compat)
- # self.compat_layout.setWidget(0, QFormLayout.ItemRole.LabelRole, self.compat_label)
- # self.compat_layout.setWidget(0, QFormLayout.ItemRole.FieldRole, self.compat_combo)
- # self.compat_layout.setWidget(1, QFormLayout.ItemRole.SpanningRole, self.compat_stack)
- # self.compat_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.FieldsStayAtSizeHint)
self.ctool = False
if proton_widget is not None:
@@ -55,7 +50,18 @@ def __init__(
self.ctool.compat_tool_enabled.connect(self.wine.compat_tool_enabled)
self.ctool.compat_path_changed.connect(self.wine.compat_path_changed)
self.main_layout.addWidget(self.ctool)
- # self.ctool.compat_tool_enabled.connect(self.compat_tool_enabled)
+
+ self.pfx_folder_button = QPushButton(self.tr("Open prefix folder"), self)
+ self.pfx_folder_button.clicked.connect(self._open_pfx_folder)
+ self.usr_folder_button = QPushButton(self.tr("Open user folder"), self)
+ self.usr_folder_button.clicked.connect(self._open_usr_folder)
+ self.winetricks_button = QPushButton(self.tr("Run winetricks"), self)
+
+ self.button_layout = QHBoxLayout()
+ self.button_layout.addWidget(self.pfx_folder_button)
+ self.button_layout.addWidget(self.usr_folder_button)
+ self.button_layout.addWidget(self.winetricks_button)
+ self.button_layout.setAlignment(Qt.AlignmentFlag.AlignRight)
font = self.font()
font.setItalic(True)
@@ -64,12 +70,8 @@ def __init__(
self.shader_cache_check.setChecked(self.settings.get_value(app_settings.local_shader_cache))
self.shader_cache_check.checkStateChanged.connect(self._shader_cache_check_changed)
- # wine_index = self.compat_stack.addWidget(self.wine)
- # self.compat_combo.addItem("Wine", wine_index)
- # proton_index = self.compat_stack.addWidget(self.proton_tool)
- # self.compat_combo.addItem("Proton", proton_index)
-
self.form_layout = QFormLayout()
+ self.form_layout.addRow(self.tr(""), self.button_layout)
self.form_layout.addRow(self.tr("Shader cache"), self.shader_cache_check)
self.form_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
self.form_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
@@ -80,5 +82,26 @@ def __init__(
def _shader_cache_check_changed(self, state: Qt.CheckState):
self.settings.set_value(app_settings.local_shader_cache, state != Qt.CheckState.Unchecked)
+ @Slot()
+ def _open_pfx_folder(self):
+ QDesktopServices.openUrl(
+ QUrl.fromLocalFile(lgd_config.get_wine_prefix_with_global(self.app_name))
+ )
+
+ @Slot()
+ def _open_usr_folder(self):
+ path = os.path.join(
+ lgd_config.get_wine_prefix_with_global(self.app_name), "drive_c", "users", getuser()
+ )
+ if not os.path.exists(path):
+ path = os.path.join(
+ lgd_config.get_wine_prefix_with_global(self.app_name), "drive_c", "users", "steamuser"
+ )
+ QDesktopServices.openUrl(path)
+
+ @Slot()
+ def _run_winetricks(self):
+ pass
+
RunnerSettingsType = TypeVar("RunnerSettingsType", bound=RunnerSettingsBase)
diff --git a/rare/components/tabs/settings/widgets/wine.py b/rare/components/tabs/settings/widgets/wine.py
index 8fcb2c022b..8b9077503c 100644
--- a/rare/components/tabs/settings/widgets/wine.py
+++ b/rare/components/tabs/settings/widgets/wine.py
@@ -8,7 +8,7 @@
from rare.models.settings import RareAppSettings
from rare.shared import RareCore
-from rare.utils import config_helper as lgd_conf
+from rare.utils import config_helper as lgd_config
from rare.widgets.indicator_edit import IndicatorReasonsCommon, PathEdit
logger = getLogger("WineSettings")
@@ -54,7 +54,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
layout = QFormLayout(self)
layout.addRow(self.tr("Executable"), self.wine_execut_edit)
- layout.addRow(self.tr("Prefix"), self.wine_prefix_edit)
+ layout.addRow(self.tr("Prefix folder"), self.wine_prefix_edit)
layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow)
layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
layout.setFormAlignment(Qt.AlignmentFlag.AlignLeading | Qt.AlignmentFlag.AlignVCenter)
@@ -64,7 +64,7 @@ def __update_widget(self):
self.wine_prefix_edit.setText(self.load_prefix())
_ = QSignalBlocker(self.wine_execut_edit)
self.wine_execut_edit.setText(self.load_execut())
- self.setDisabled(lgd_conf.get_boolean(self.app_name, "no_wine", fallback=False))
+ self.setDisabled(lgd_config.get_boolean(self.app_name, "no_wine", fallback=False))
def showEvent(self, a0: QShowEvent):
if a0.spontaneous():
@@ -74,47 +74,48 @@ def showEvent(self, a0: QShowEvent):
@Slot(str)
def compat_path_changed(self, text: str):
- self.wine_prefix_edit.setText(os.path.join(text, "pfx") if text else text)
+ path = os.path.join(text, "pfx") if text else text
+ self.wine_prefix_edit.setText(path)
@Slot(bool)
def compat_tool_enabled(self, enabled: bool, path: str):
old_wine_execut = self.settings.value(f"{self.app_name}/wine_execut", defaultValue=None)
old_wine_prefix = self.settings.value(f"{self.app_name}/wine_prefix", defaultValue=None)
if enabled:
- wine_execut = lgd_conf.get_option(self.app_name, "wine_executable", "")
- wine_prefix = lgd_conf.get_wine_prefix(self.app_name, "")
+ wine_execut = lgd_config.get_option(self.app_name, "wine_executable", "")
+ wine_prefix = lgd_config.get_wine_prefix(self.app_name, "")
if old_wine_execut is None:
self.settings.setValue(f"{self.app_name}/wine_execut", wine_execut)
if old_wine_prefix is None:
self.settings.setValue(f"{self.app_name}/wine_prefix", wine_prefix)
self.wine_execut_edit.setText("")
self.wine_prefix_edit.setText(os.path.join(path, "pfx"))
- lgd_conf.set_boolean(self.app_name, "no_wine", True)
+ lgd_config.set_boolean(self.app_name, "no_wine", True)
else:
self.settings.remove(f"{self.app_name}/wine_execut")
self.settings.remove(f"{self.app_name}/wine_prefix")
self.wine_execut_edit.setText(old_wine_execut)
self.wine_prefix_edit.setText(old_wine_prefix)
- lgd_conf.remove_option(self.app_name, "no_wine")
+ lgd_config.remove_option(self.app_name, "no_wine")
self.setDisabled(enabled)
def load_prefix(self) -> str:
if self.app_name is None:
raise RuntimeError
- return lgd_conf.get_wine_prefix(self.app_name, "")
+ return lgd_config.get_wine_prefix(self.app_name, "")
def save_prefix(self, path: str) -> None:
if self.app_name is None:
raise RuntimeError
- lgd_conf.adjust_wine_prefix(self.app_name, path)
+ lgd_config.adjust_wine_prefix(self.app_name, path)
self.environ_changed.emit("WINEPREFIX")
def load_execut(self) -> str:
if self.app_name is None:
raise RuntimeError
- return lgd_conf.get_option(self.app_name, "wine_executable", "")
+ return lgd_config.get_option(self.app_name, "wine_executable", "")
def save_execut(self, text: str) -> None:
if self.app_name is None:
raise RuntimeError
- lgd_conf.adjust_option(self.app_name, "wine_executable", text)
+ lgd_config.adjust_option(self.app_name, "wine_executable", text)
diff --git a/rare/lgndr/cli.py b/rare/lgndr/cli.py
index b259ea204c..7a6c2c2d5a 100644
--- a/rare/lgndr/cli.py
+++ b/rare/lgndr/cli.py
@@ -348,12 +348,14 @@ def chunk_url_sign_thread():
self.install_game_cleanup(game, igame, args.repair_mode, args.repair_file)
logger.info(f'Finished installation process in {end_t - start_t:.02f} seconds.')
+
+ return ret
finally:
ticket_thread.stop = True
sign_thread.stop = True
ticket_thread.join()
sign_thread.join()
- return ret
+
@unlock_installed.__func__
def install_game_cleanup(self, game: Game, igame: InstalledGame, repair_mode: bool = False, repair_file: str = '') -> None:
diff --git a/rare/models/game.py b/rare/models/game.py
index 06012d7266..7f5cb2855f 100644
--- a/rare/models/game.py
+++ b/rare/models/game.py
@@ -10,6 +10,7 @@
from legendary.lfs import eos
from legendary.models.game import Game, InstalledGame
+from legendary.utils.selective_dl import get_sdl_appname
from PySide6.QtCore import QProcess, QRunnable, QThreadPool, Slot
from PySide6.QtGui import QPixmap
@@ -117,8 +118,8 @@ def __init__(
self.__steam_grade_pending: bool = False
self.game_process = GameProcess(self.game)
- self.game_process.launched.connect(self.__game_launched)
- self.game_process.finished.connect(self.__game_finished)
+ self.game_process.launched.connect(self._game_launched)
+ self.game_process.finished.connect(self._game_finished)
if self.is_installed and not self.is_dlc:
self.game_process.connect_to_server(on_startup=True)
@@ -165,7 +166,7 @@ def del_worker(self, worker: QRunnable):
self.state = RareGame.State.IDLE
@Slot(int)
- def __game_launched(self, code: int):
+ def _game_launched(self, code: int):
self.state = RareGame.State.RUNNING
self.metadata.last_played = datetime.now(timezone.utc)
if code == GameProcess.Code.ON_STARTUP:
@@ -174,7 +175,7 @@ def __game_launched(self, code: int):
self.signals.game.launched.emit(self.app_name)
@Slot(int)
- def __game_finished(self, exit_code: int):
+ def _game_finished(self, exit_code: int):
if exit_code == GameProcess.Code.ON_STARTUP:
return
if self.supports_cloud_saves:
@@ -341,7 +342,7 @@ def set_installed(self, installed: bool) -> None:
self.core.egstore_delete(self.igame)
self.igame = None
self.signals.game.uninstalled.emit(self.app_name)
- self.__update_pixmap()
+ self._update_pixmap()
@property
def can_run_offline(self) -> bool:
@@ -524,6 +525,44 @@ def eulas(self) -> List:
return not_accepted_eulas
+ def sdl_data(self, platform: str) -> Optional[Dict[str, Dict]]:
+ sdl_data = {}
+
+ sdl_name = get_sdl_appname(self.app_name)
+ if data := self.core.get_sdl_data(sdl_name, platform=platform):
+ sdl_data.update(data)
+ known_install_tags = set()
+ if sdl_data:
+ known_install_tags = set(tag for _, info in sdl_data.items() for tag in info["tags"])
+
+ if self.igame is not None and not self.has_update:
+ manifest_data = self.core.lgd.load_manifest(self.app_name, self.igame.version, self.igame.platform)
+ else:
+ manifest_data, _, _ = self.core.get_cdn_manifest(self.game, platform)
+ manifest = self.core.load_manifest(manifest_data)
+ manifest_install_tags = set()
+ for fm in manifest.file_manifest_list.elements:
+ for tag in fm.install_tags:
+ manifest_install_tags.add(tag)
+
+ extra_install_tags = manifest_install_tags.difference(known_install_tags)
+ for extra_tag in extra_install_tags:
+ sdl_data[extra_tag] = {"name": extra_tag, "description": "", "tags": [extra_tag]}
+
+ return sdl_data
+
+ @property
+ def sdl_available(self) -> bool:
+ if self.igame is not None:
+ manifest_data = self.core.lgd.load_manifest(self.app_name, self.igame.version, self.igame.platform)
+ manifest = self.core.load_manifest(manifest_data)
+ manifest_install_tags = set()
+ for fm in manifest.file_manifest_list.elements:
+ for tag in fm.install_tags:
+ manifest_install_tags.add(tag)
+ return bool(manifest_install_tags)
+ return get_sdl_appname(self.app_name) is not None
+
def reset_steam_date(self):
self.metadata.steam_date = datetime.min.replace(tzinfo=timezone.utc)
self.signals.widget.refresh.emit()
@@ -598,7 +637,7 @@ def set_origin_attributes(self, path: str, size: int = 0) -> None:
self.signals.game.installed.emit(self.app_name)
else:
self.signals.game.uninstalled.emit(self.app_name)
- self.__update_pixmap()
+ self._update_pixmap()
@property
def can_launch(self) -> bool:
@@ -614,7 +653,7 @@ def get_pixmap(self, preset: ImageSize.Preset, color=True) -> QPixmap:
return self.image_manager.get_pixmap(self.app_name, preset, color)
@Slot()
- def __update_pixmap(self):
+ def _update_pixmap(self):
self.has_pixmap = self.image_manager.has_pixmaps(self.app_name)
if self.has_pixmap:
self.signals.widget.refresh.emit()
@@ -622,10 +661,10 @@ def __update_pixmap(self):
def load_pixmaps(self):
"""Do not call this function, call set_pixmap instead. This is only used for initial image loading"""
if not self.has_pixmap:
- self.image_manager.download_image(self.game, self.__update_pixmap, 0, False)
+ self.image_manager.download_image(self.game, self._update_pixmap, 0, False)
def refresh_pixmap(self):
- self.image_manager.download_image(self.game, self.__update_pixmap, 0, True)
+ self.image_manager.download_image(self.game, self._update_pixmap, 0, True)
@property
def __install_base_path(self) -> str:
@@ -690,7 +729,7 @@ def uninstall(self) -> bool:
UninstallOptionsModel(
app_name=self.app_name,
keep_folder=self.is_dlc,
- keep_config=self.sdl_name is not None or self.is_dlc or platform.system() not in {"Windows"},
+ keep_config=self.sdl_available or self.is_dlc or platform.system() not in {"Windows"},
))
return True
@@ -763,10 +802,10 @@ def __init__(
self.image_manager = image_manager
self.igame: Optional[InstalledGame] = self.core.lgd.get_overlay_install_info()
- self.image_manager.download_image(game, self.__update_pixmap, 0, False)
+ self.image_manager.download_image(game, self._update_pixmap, 0, False)
@Slot()
- def __update_pixmap(self):
+ def _update_pixmap(self):
self.has_pixmap = self.image_manager.has_pixmaps(self.app_name)
if self.has_pixmap:
self.signals.widget.refresh.emit()
diff --git a/rare/models/game_slim.py b/rare/models/game_slim.py
index 98e4e27e48..270f0ed0cd 100644
--- a/rare/models/game_slim.py
+++ b/rare/models/game_slim.py
@@ -4,11 +4,10 @@
from datetime import datetime
from enum import IntEnum
from logging import getLogger
-from typing import List, Optional, Tuple
+from typing import Dict, List, Optional, Tuple
from legendary.lfs import eos
from legendary.models.game import Game, InstalledGame, SaveGameFile, SaveGameStatus
-from legendary.utils.selective_dl import get_sdl_appname
from PySide6.QtCore import QObject, Signal
from rare.lgndr.core import LegendaryCore
@@ -203,10 +202,6 @@ def is_launchable_addon(self) -> bool:
except AttributeError:
return False
- @property
- def sdl_name(self) -> Optional[str]:
- return get_sdl_appname(self.app_name)
-
@property
def version(self) -> str:
"""!
diff --git a/rare/resources/static_css/__init__.py b/rare/resources/static_css/__init__.py
index 0d68eed80b..5b6d4b6d35 100644
--- a/rare/resources/static_css/__init__.py
+++ b/rare/resources/static_css/__init__.py
@@ -1,69 +1,70 @@
# Resource object code (Python 3)
# Created by: object code
-# Created by: The Resource Compiler for Qt version 6.9.1
+# Created by: The Resource Compiler for Qt version 6.10.1
# WARNING! All changes made in this file will be lost!
from PySide6 import QtCore
qt_resource_data = b"\
-\x00\x00\x03\x80\
+\x00\x00\x03\x82\
\x00\
-\x00\x0f\xa5x\x9c\xbdW\xdf\x8f\xd2@\x10~\xe7\xaf\xd8\
-\x83\x90\xe8E\xb0?h\x81\x1a_N\x8d\xb9\xe4N\xbd\
-\x80\xfa`|\xd8v\x87vse\xb7\xd9n\x054\xfe\
-\xefni\x81\x16\xda\x05\x8dJ\x08]vg\xbf\x99\xf9\
-f:\xb3\xfb\xfc\x1a\xcd#\x9a\xa2\x05\x8d\x01\xa9'\xce\
-$\x1f\x84\xc0@`\x09\x04-\x04_\xa2n*71\
-\xa4\x11\x80\x1c&\x9b\xee\x10\xbd~\x8f\xde\xbd\x9f\xa37\
-\xafo\xe7WWW\xe8\xfay\xa7\xf3\xf0!K\xa3\x9b\
-LJ\xce\xd0\x8f\x0eR\x9f%e\x83\x08h\x18I\x0f\
-Y\xc3\xb1\x03\xeb\x17\x9d\x9fJN\xf0P@\x9a\xde`\
-\xa1\x17\x9c%\x94\xdd\xf0\xb5^h\xcey|\x89\xd6;\
-\xca\xe0\x0d\xa1R/\xf5\x8a/}~V\xe7\xc1\xd3/\
-\x8b\x18\xcb\x97])2\xe8~-7\xf9\x5c\x10\x10\x1e\
-2\x12%}\x98\x18\x08Lh\x96z\xc8\xd9\xcf\xe3\xe0\
-1\x14\x18\x1f\xa6\x9dK;\xd92\xd3L\xa7\x02\x1a\xff\
+\xbbSZ\xa0\x85v@\xa3n\x08t\xe7\xe3\x9c{\xcf\
+\xbd\xbdw\xe6\xd9\x0d\x9aG4E\x0b\x1a\x03R\xbf8\
+\x93|\x10\x02\x03\x81%\x10\xb4\x10|\x89\xba\xa9\xdc\xc4\
+\x90F\x00r\x98l\xbaC\xf4\xea\x1e\xbd\xbf\x9f\xa3\xd7\
+\xaf\xde\xce\xaf\xae\xae\xd0\xcd\xb3N\xe7\xe1C\x96F\xb7\
+\x99\x94\x9c\xa1\x1f\x1d\xa4\xfe\x96\x94\x0d\x22\xa0a$=\
+d\x0d\xc7\x0e\xac\x9fw~\xaau\x82\x87\x02\xd2\xf4\x16\
+\x0b\xfd\xc2YB\xd9-_\xeb\x17\xcd9\x8f/a}\
+G\x19\xbc&T\xeaW\xbd\xe4K\x9f\x9f\xe5\x19\
+k\xd1G\xee9tBS\xec\xc7*o[\x08\xec@\
+K`MN\x09>2z^\x98\x5c\x16\x0d\xb2[N\
+k\x91u\xd2\xe4\xc2h\xf0sa\xce\xe3\xeb\xc5\xc9\xa5\
+\xd1P\xe4\xd2\x9cR|\x02A\x17\x1b\x9d2\xae\xef\x8c\
+\xad\xa0\x15\xd6v\xac\xc0tu\xb0:YFc\x1b\x9b\
+\xa4\xddf\xdf\xb2MS\x0b~F\x93\xd2\xba6\xfc\x91\
+I\x0c8\xc6\xbfS\xf6\xea\x14\xb1\x0c\xd7q\xdb\x151\
+\x0d\xdb\xb2O\x149\x80\xea\xf40\xdd\x91=\x9a\xb4'\
+8\xb1&\x96\xaf\x81\xd6\xab\xb1\xb3\xac\x0d\xdd\xb7,k\
+T)V\x0f\x19d\xf0\x99\x8bG\x10\xd5\x9aU\xa2\xae\
+(\x91\x91\x87\xcc\xa32Y\xd6\xae\x94\xc7\x944\xd6O\
+;Yk8\xbe\xac\xb6\xcfr\x93\xc0\x8bn\x11\xe9b\
+\xf6\xa8>\xffA~^@\x98K\xa9\xa7\xbb<\xf8\x17\
+\xd0\xbd\x8cyFf\x1b\x16\x9cq\x91\xb8S\xb7=+\
+\xec\xb1\xed\xd8\xd5\xb8\xed:\xf4\xe5A\xdb\xf7\xb6~C\
+\xb7\xf1yL\xaa\xcd\x89~W\xf15\xddDV\xfbZ\
+\xc0\xd9gJB\x903\x89eV\xa3n\x01+\xcd_\
+ET\x82\xb6\xa1\x1aOQ\xf1q\xb7\xbdTcv9\
+,y2\x88a!\xf7\xf3\xa6y\xba@\xe4\xf6\xd4W\
+(o\x0a\x1f*\xee\xdcQF\x8b\xa7z\xc7V\x96=\
+\xb1\xec\x89j\xf2\xe5\xd7\xf5\xdf\xf5\xc1\xe7\xea\x85^\xd6\
+\xdd\x986.\xa9;2\xed7FeNe\x0c\xff$\
+(F\xfd|s`T\x07;I\x93\x86\xc3\xce\x1f\x80\
+W\xcf\x0b{\x86\x86\x1a\xbd\xcd\xec\xa2\xf6 ?V\xe0\
+\x8dR\x9bF\xffw\x82\xa5\xe7\xd7\x94\xf3P\xe0ME\
+\x9bw4\x95\x97G\xa3a\xdb?|\xb5\xc6Gi\xb9\
+=\xfe\x9e\x15\xd2\xde\x95\x91\x04\x13BY\xb8MW\xb5\
+uh\xc3\xb2>!\x0a\x13\xcb\x99&I\xfeF\xba\xfc\
+w/*yqp\xa5\x96\x97\x12\xd6r\xa0\x8e\xf1!\
+\xf3P\x8e\xdb\xe8\xbc\xf6V\x90_\x92\xb0\xafn[\xbd\
+;LY\xf1\xe8y\x12\xfb\xcd\xdd\xbezq9\x91K\
+\x0a\xcc\xd2\x04\x0b`\xf2\xd8\xfe\x19\xc4\x10\xc8O\x14V\
+\x9a\xb7\x8bq\xd6\x1a\x0a\x0dv\xf1\xb3\xbb\xc2\xd5\x91\xf5\
+8\x8d~\x95\xe1(G\xf2k_\x04\xc1\xa3\xba\xf6\xf5\
+\xde\xe0%\xccq\xb8\xfb\x7fw\x0d\xc4\x22\xa4\xec\xb4\xeb\
+\xfd\xee\x09\xa64\xf0\xf2L\xda\x1fv~\x01\xd3a\xec\
+\xde\
"
qt_resource_name = b"\
@@ -83,7 +84,7 @@
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x1a\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x01\x9a\xa3;L\x07\
+\x00\x00\x01\x9c]]\x00Y\
"
def qInitResources():
diff --git a/rare/resources/static_css/stylesheet.py b/rare/resources/static_css/stylesheet.py
index c229e9e5cc..82f56713ff 100644
--- a/rare/resources/static_css/stylesheet.py
+++ b/rare/resources/static_css/stylesheet.py
@@ -56,19 +56,19 @@ def css_name(widget: Union[wrappertype, QObject, Type], subwidget: str = ""):
# [Un]InstallButton
style.QPushButton["#InstallButton"].setValues(
- borderColor=QColor(0, 180, 0).name(), backgroundColor=QColor(0, 120, 0).name()
+ borderColor=QColor(0, 180, 0).name(), backgroundColor=QColor(0, 100, 0).name()
)
style.QPushButton["#InstallButton"].hover.setValues(
- borderColor=QColor(0, 135, 0).name(), backgroundColor=QColor(0, 90, 0).name()
+ borderColor=QColor(0, 135, 0).name(), backgroundColor=QColor(0, 70, 0).name()
)
style.QPushButton["#InstallButton"].disabled.setValues(
borderColor=QColor(0, 60, 0).name(), backgroundColor=QColor(0, 40, 0).name()
)
style.QPushButton["#UninstallButton"].setValues(
- borderColor=QColor(180, 0, 0).name(), backgroundColor=QColor(120, 0, 0).name()
+ borderColor=QColor(180, 0, 0).name(), backgroundColor=QColor(100, 0, 0).name()
)
style.QPushButton["#UninstallButton"].hover.setValues(
- borderColor=QColor(135, 0, 0).name(), backgroundColor=QColor(90, 0, 0).name()
+ borderColor=QColor(135, 0, 0).name(), backgroundColor=QColor(70, 0, 0).name()
)
style.QPushButton["#UninstallButton"].disabled.setValues(
borderColor=QColor(60, 0, 0).name(), backgroundColor=QColor(40, 0, 0).name()
diff --git a/rare/resources/static_css/stylesheet.qss b/rare/resources/static_css/stylesheet.qss
index a304b43600..f9f70906e5 100644
--- a/rare/resources/static_css/stylesheet.qss
+++ b/rare/resources/static_css/stylesheet.qss
@@ -30,11 +30,11 @@ QLabel#InfoLabel {
}
QPushButton#InstallButton {
border-color: #00b400;
- background-color: #007800;
+ background-color: #006400;
}
QPushButton#InstallButton:hover {
border-color: #008700;
- background-color: #005a00;
+ background-color: #004600;
}
QPushButton#InstallButton:disabled {
border-color: #003c00;
@@ -42,11 +42,11 @@ QPushButton#InstallButton:disabled {
}
QPushButton#UninstallButton {
border-color: #b40000;
- background-color: #780000;
+ background-color: #640000;
}
QPushButton#UninstallButton:hover {
border-color: #870000;
- background-color: #5a0000;
+ background-color: #460000;
}
QPushButton#UninstallButton:disabled {
border-color: #3c0000;
diff --git a/rare/utils/compat/steam.py b/rare/utils/compat/steam.py
index 9258c7ecd9..9e0d694353 100644
--- a/rare/utils/compat/steam.py
+++ b/rare/utils/compat/steam.py
@@ -256,7 +256,7 @@ def find_steam_tools(steam_path: str, library: str) -> List[ProtonTool]:
def find_compatibility_tools(steam_path: str) -> List[CompatibilityTool]:
compatibilitytools_paths = {
- data_dir().joinpath("compatibilitytools").as_posix(),
+ data_dir().joinpath("tools").as_posix(),
os.path.expanduser("~/.local/share/umu/compatibilitytools"),
os.path.expanduser(os.path.join(steam_path, "compatibilitytools.d")),
os.path.expanduser("~/.steam/compatibilitytools.d"),
diff --git a/rare/utils/compat/utils.py b/rare/utils/compat/utils.py
index 414f155e30..1c7a692e2d 100644
--- a/rare/utils/compat/utils.py
+++ b/rare/utils/compat/utils.py
@@ -2,6 +2,7 @@
import platform as pf
import subprocess
from configparser import ConfigParser
+from getpass import getuser
from logging import getLogger
from typing import Dict, List, Mapping, Tuple
@@ -174,3 +175,19 @@ def get_host_environment(app_environment: Dict, silent: bool = False) -> Dict:
# lk: pressure-vessel complains about this but it doesn't fail due to it
# _environ["DISPLAY"] = ""
return _environ
+
+
+def create_compat_users(pfx: str):
+ pfx_users = os.path.join(pfx, "drive_c", "users")
+ os.makedirs(pfx_users, exist_ok=True)
+ steam_user = os.path.join(pfx_users, "steamuser")
+ unix_user = os.path.join(pfx_users, getuser())
+ if not os.path.exists(steam_user) and not os.path.exists(unix_user):
+ os.makedirs(steam_user, exist_ok=True)
+ pwd = os.getcwd()
+ os.chdir(pfx_users)
+ if os.path.exists(steam_user) and not os.path.exists(unix_user):
+ os.symlink("steamuser", getuser())
+ if not os.path.exists(steam_user) and os.path.exists(unix_user):
+ os.symlink(getuser(), "steamuser")
+ os.chdir(pwd)
diff --git a/rare/widgets/indicator_edit.py b/rare/widgets/indicator_edit.py
index 69b3b07f8f..e01c878668 100644
--- a/rare/widgets/indicator_edit.py
+++ b/rare/widgets/indicator_edit.py
@@ -371,6 +371,7 @@ def __init__(
)
self.setObjectName(type(self).__name__)
self.line_edit.setMinimumSize(QSize(250, 0))
+ self.line_edit.setReadOnly(True)
self.path_select = QPushButton(self)
self.path_select.setObjectName(f"{type(self).__name__}Button")
layout = self.layout()