1
0
Fork 0
mirror of synced 2024-06-02 10:44:40 +12:00
Rare/rare/components/tabs/games/game_info/game_settings.py
aznd 9b59707a10
Unify strings formatting (#158)
* Part 1: Unifying strings

* Part 2: Unifying strings

* Part 3: Unifying strings

* Fix missing close bracket

* Remove unneeded str()
2022-02-01 22:29:34 +01:00

412 lines
17 KiB
Python

import os
import platform
from logging import getLogger
from typing import Tuple
from PyQt5.QtCore import QSettings, QThreadPool, Qt
from PyQt5.QtWidgets import (
QWidget,
QFileDialog,
QMessageBox,
QLabel,
QPushButton,
QSizePolicy,
)
from legendary.core import LegendaryCore
from legendary.models.game import InstalledGame, Game
from rare.components.tabs.settings.linux import LinuxSettings
from rare.ui.components.tabs.games.game_info.game_settings import Ui_GameSettings
from rare.utils.extra_widgets import PathEdit
from rare.utils.utils import WineResolver, get_raw_save_path
from rare.utils.utils import icon
logger = getLogger("GameSettings")
def find_proton_wrappers():
possible_proton_wrappers = []
compatibilitytools_dirs = [
os.path.expanduser("~/.steam/steam/steamapps/common"),
"/usr/share/steam/compatibilitytools.d",
os.path.expanduser("~/.steam/compatibilitytools.d"),
os.path.expanduser("~/.steam/root/compatibilitytools.d"),
]
for c in compatibilitytools_dirs:
if os.path.exists(c):
for i in os.listdir(c):
proton = os.path.join(c, i, "proton")
compatibilitytool = os.path.join(c, i, "compatibilitytool.vdf")
toolmanifest = os.path.join(c, i, "toolmanifest.vdf")
if os.path.exists(proton) and (
os.path.exists(compatibilitytool) or os.path.exists(toolmanifest)
):
wrapper = f'"{proton}" run'
possible_proton_wrappers.append(wrapper)
if not possible_proton_wrappers:
logger.warning("Unable to find any Proton version")
return possible_proton_wrappers
class GameSettings(QWidget, Ui_GameSettings):
game: Game
igame: InstalledGame
# variable to no update when changing game
change = False
def __init__(self, core: LegendaryCore, parent):
super(GameSettings, self).__init__(parent=parent)
self.setupUi(self)
self.core = core
self.settings = QSettings()
self.cloud_save_path_edit = PathEdit(
"",
file_type=QFileDialog.DirectoryOnly,
ph_text=self.tr("Cloud save path"),
edit_func=lambda text: (os.path.exists(text), text, PathEdit.reasons.dir_not_exist),
save_func=self.save_save_path,
)
self.cloud_gb.layout().addRow(
QLabel(self.tr("Save path")), self.cloud_save_path_edit
)
self.compute_save_path_button = QPushButton(
icon("fa.magic"), self.tr("Auto compute save path")
)
self.compute_save_path_button.setSizePolicy(
QSizePolicy.Maximum, QSizePolicy.Fixed
)
self.compute_save_path_button.clicked.connect(self.compute_save_path)
self.cloud_gb.layout().addRow(None, self.compute_save_path_button)
self.offline.currentIndexChanged.connect(
lambda x: self.update_combobox(x, "offline")
)
self.skip_update.currentIndexChanged.connect(
lambda x: self.update_combobox(x, "skip_update_check")
)
self.cloud_sync.stateChanged.connect(
lambda: self.settings.setValue(
f"{self.game.app_name}/auto_sync_cloud", self.cloud_sync.isChecked()
)
)
self.launch_params.textChanged.connect(
lambda x: self.save_line_edit("start_params", x)
)
self.wrapper.textChanged.connect(lambda x: self.save_line_edit("wrapper", x))
self.override_exe_edit.textChanged.connect(
lambda x: self.save_line_edit("override_exe", x)
)
if platform.system() != "Windows":
self.possible_proton_wrappers = find_proton_wrappers()
self.proton_wrapper.addItems(self.possible_proton_wrappers)
self.proton_wrapper.currentIndexChanged.connect(self.change_proton)
self.proton_prefix = PathEdit(
file_type=QFileDialog.DirectoryOnly,
edit_func=self.proton_prefix_edit,
save_func=self.proton_prefix_save,
)
self.proton_prefix_layout.addWidget(self.proton_prefix)
self.linux_settings = LinuxAppSettings()
# FIXME: Remove the spacerItem and margins from the linux settings
# FIXME: This should be handled differently at soem point in the future
self.linux_settings.layout().setContentsMargins(0, 0, 0, 0)
for item in [
self.linux_settings.layout().itemAt(idx)
for idx in range(self.linux_settings.layout().count())
]:
if item.spacerItem():
self.linux_settings.layout().removeItem(item)
del item
# FIXME: End of FIXME
self.linux_settings_contents_layout.addWidget(self.linux_settings)
self.linux_settings_contents_layout.setAlignment(Qt.AlignTop)
else:
self.linux_settings_scroll.setVisible(False)
self.game_settings_layout.setAlignment(Qt.AlignTop)
def compute_save_path(self):
if (
self.core.is_installed(self.game.app_name)
and self.game.supports_cloud_saves
):
try:
new_path = self.core.get_save_path(self.game.app_name)
except Exception as e:
logger.warning(str(e))
self.cloud_save_path_edit.setText(self.tr("Loading"))
self.cloud_save_path_edit.setDisabled(True)
self.compute_save_path_button.setDisabled(True)
resolver = WineResolver(
get_raw_save_path(self.game), self.game.app_name, self.core
)
app_name = self.game.app_name[:]
resolver.signals.result_ready.connect(
lambda x: self.wine_resolver_finished(x, app_name)
)
QThreadPool.globalInstance().start(resolver)
return
else:
self.cloud_save_path_edit.setText(new_path)
def wine_resolver_finished(self, path, app_name):
logger.info(
f"Wine resolver finished for {app_name}. Computed save path: {path}"
)
if app_name == self.game.app_name:
self.cloud_save_path_edit.setDisabled(False)
self.compute_save_path_button.setDisabled(False)
if path and not os.path.exists(path):
try:
os.makedirs(path)
except PermissionError:
self.cloud_save_path_edit.setText("")
QMessageBox.warning(
None,
"Error",
self.tr(
"Error while launching {}. No permission to create {}"
).format(self.game.app_title, path),
)
return
if not path:
self.cloud_save_path_edit.setText("")
return
self.cloud_save_path_edit.setText(path)
elif path:
igame = self.core.get_installed_game(app_name)
igame.save_path = path
self.core.lgd.set_installed_game(app_name, igame)
def save_save_path(self, text):
if self.game.supports_cloud_saves and self.change:
self.igame.save_path = text
self.core.lgd.set_installed_game(self.igame.app_name, self.igame)
def save_line_edit(self, option, value):
if value:
if self.game.app_name not in self.core.lgd.config.sections():
self.core.lgd.config.add_section(self.game.app_name)
self.core.lgd.config.set(self.game.app_name, option, value)
else:
if (
self.core.lgd.config.has_section(self.game.app_name)
and self.core.lgd.config.get(
f"{self.game.app_name}", option, fallback=None
)
is not None
):
self.core.lgd.config.remove_option(self.game.app_name, option)
if not self.core.lgd.config[self.game.app_name]:
self.core.lgd.config.remove_section(self.game.app_name)
self.core.lgd.save_config()
if option == "wine_prefix":
if self.game.supports_cloud_saves:
self.compute_save_path()
def update_combobox(self, i, option):
if self.change:
# remove section
if i:
if self.game.app_name not in self.core.lgd.config.sections():
self.core.lgd.config.add_section(self.game.app_name)
if i == 1:
self.core.lgd.config.set(self.game.app_name, option, "true")
if i == 2:
self.core.lgd.config.set(self.game.app_name, option, "false")
else:
if self.game.app_name in self.core.lgd.config.sections():
if self.core.lgd.config.get(
f"{self.game.app_name}", option, fallback=False
):
self.core.lgd.config.remove_option(self.game.app_name, option)
if not self.core.lgd.config[self.game.app_name]:
self.core.lgd.config.remove_section(self.game.app_name)
self.core.lgd.save_config()
def change_proton(self, i):
if self.change:
# Dont use Proton
if i == 0:
if f"{self.game.app_name}" in self.core.lgd.config.sections():
if self.core.lgd.config.get(
f"{self.game.app_name}", "wrapper", fallback=False
):
self.core.lgd.config.remove_option(
self.game.app_name, "wrapper"
)
if self.core.lgd.config.get(
f"{self.game.app_name}", "no_wine", fallback=False
):
self.core.lgd.config.remove_option(
self.game.app_name, "no_wine"
)
if not self.core.lgd.config[self.game.app_name]:
self.core.lgd.config.remove_section(self.game.app_name)
if f"{self.game.app_name}.env" in self.core.lgd.config.sections():
if self.core.lgd.config.get(
f"{self.game.app_name}.env",
"STEAM_COMPAT_DATA_PATH",
fallback=False,
):
self.core.lgd.config.remove_option(
f"{self.game.app_name}.env", "STEAM_COMPAT_DATA_PATH"
)
if not self.core.lgd.config[f"{self.game.app_name}.env"]:
self.core.lgd.config.remove_section(f"{self.game.app_name}.env")
self.proton_prefix.setEnabled(False)
# lk: TODO: This has to be fixed properly.
# lk: It happens because of the widget update. Mask it for now behind disabling the save button
self.wrapper.setText(
self.core.lgd.config.get(
f"{self.game.app_name}", "wrapper", fallback=""
)
)
self.wrapper.setEnabled(True)
self.linux_settings.wine_groupbox.setEnabled(True)
else:
self.proton_prefix.setEnabled(True)
self.wrapper.setEnabled(False)
self.linux_settings.wine_groupbox.setEnabled(False)
wrapper = self.possible_proton_wrappers[i - 1]
if self.game.app_name not in self.core.lgd.config.sections():
self.core.lgd.config[self.game.app_name] = {}
if f"{self.game.app_name}.env" not in self.core.lgd.config.sections():
self.core.lgd.config[f"{self.game.app_name}.env"] = {}
self.core.lgd.config.set(self.game.app_name, "wrapper", wrapper)
self.core.lgd.config.set(self.game.app_name, "no_wine", "true")
self.core.lgd.config.set(
f"{self.game.app_name}.env",
"STEAM_COMPAT_DATA_PATH",
os.path.expanduser("~/.proton"),
)
self.proton_prefix.setText(os.path.expanduser("~/.proton"))
# Dont use Wine
self.linux_settings.wine_exec.setText("")
self.linux_settings.wine_prefix.setText("")
self.core.lgd.save_config()
def proton_prefix_edit(self, text: str) -> Tuple[bool, str, str]:
if not text:
text = os.path.expanduser("~/.proton")
return True, text, ""
parent = os.path.dirname(text)
return os.path.exists(parent), text, PathEdit.reasons.dir_not_exist
def proton_prefix_save(self, text: str):
self.core.lgd.config.set(
+ f"{self.game.app_name}.env", "STEAM_COMPAT_DATA_PATH", text
)
self.core.lgd.save_config()
def update_game(self, app_name: str):
self.change = False
self.game = self.core.get_game(app_name)
self.igame = self.core.get_installed_game(self.game.app_name)
if self.igame:
if self.igame.can_run_offline:
offline = self.core.lgd.config.get(
self.game.app_name, "offline", fallback="unset"
)
if offline == "true":
self.offline.setCurrentIndex(1)
elif offline == "false":
self.offline.setCurrentIndex(2)
else:
self.offline.setCurrentIndex(0)
self.offline.setEnabled(True)
else:
self.offline.setEnabled(False)
else:
self.offline.setEnabled(False)
skip_update = self.core.lgd.config.get(
self.game.app_name, "skip_update_check", fallback="unset"
)
if skip_update == "true":
self.skip_update.setCurrentIndex(1)
elif skip_update == "false":
self.skip_update.setCurrentIndex(2)
else:
self.skip_update.setCurrentIndex(0)
wrapper = self.core.lgd.config.get(self.game.app_name, "wrapper", fallback="")
self.wrapper.setText(wrapper)
self.game_title.setText(f"<h2>{self.game.app_title}</h2>")
if platform.system() != "Windows":
self.linux_settings.update_game(app_name)
if self.igame and self.igame.platform == "Mac":
self.linux_settings_scroll.setVisible(False)
else:
self.linux_settings_scroll.setVisible(True)
proton = self.core.lgd.config.get(
f"{app_name}", "wrapper", fallback=""
).replace('"', "")
if proton and "proton" in proton:
self.proton_prefix.setEnabled(True)
self.proton_wrapper.setCurrentText(
f'"{proton.replace(" run", "")}" run'
)
proton_prefix = self.core.lgd.config.get(
f"{app_name}.env",
"STEAM_COMPAT_DATA_PATH",
fallback=self.tr("Please select path for proton prefix"),
)
self.proton_prefix.setText(proton_prefix)
self.wrapper.setEnabled(False)
self.linux_settings.wine_groupbox.setEnabled(False)
else:
self.proton_wrapper.setCurrentIndex(0)
self.proton_prefix.setEnabled(False)
self.wrapper.setEnabled(True)
self.linux_settings.wine_groupbox.setEnabled(True)
if not self.game.supports_cloud_saves:
self.cloud_gb.setEnabled(False)
self.cloud_save_path_edit.setText("")
else:
self.cloud_gb.setEnabled(True)
sync_cloud = self.settings.value(
f"{self.game.app_name}/auto_sync_cloud", True, bool
)
self.cloud_sync.setChecked(sync_cloud)
if self.igame.save_path:
self.cloud_save_path_edit.setText(self.igame.save_path)
else:
self.cloud_save_path_edit.setText("")
self.launch_params.setText(
self.core.lgd.config.get(self.game.app_name, "start_params", fallback="")
)
self.override_exe_edit.setText(
self.core.lgd.config.get(self.game.app_name, "override_exe", fallback="")
)
self.change = True
class LinuxAppSettings(LinuxSettings):
def __init__(self):
super(LinuxAppSettings, self).__init__("app")
def update_game(self, app_name):
self.name = app_name
self.wine_prefix.setText(self.load_prefix())
self.wine_exec.setText(self.load_setting(self.name, "wine_executable"))
self.dxvk.name = app_name
self.dxvk.load_settings()