Skip to content

Commit 76ab51a

Browse files
committed
add bg3 tool plugins for conveniently invoking quick actions in ui
1 parent a0119a8 commit 76ab51a

12 files changed

Lines changed: 205 additions & 107 deletions

__init__.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
import glob
44
import importlib
55
import os
6+
import pathlib
67
import site
78
import sys
89
import typing
910

11+
from PyQt6.QtCore import qWarning
12+
from mobase import IPlugin
13+
1014
from .basic_game import BasicGame
1115
from .basic_game_ini import BasicIniGame
1216

@@ -18,7 +22,7 @@
1822

1923
def createPlugins():
2024
# List of game class from python:
21-
game_plugins: typing.List[BasicGame] = []
25+
game_plugins: typing.List[IPlugin] = []
2226

2327
# We are going to list all game plugins:
2428
curpath = os.path.abspath(os.path.dirname(__file__))
@@ -58,5 +62,21 @@ def createPlugins():
5862
"Failed to instantiate {}: {}".format(name, e),
5963
file=sys.stderr,
6064
)
65+
for path in pathlib.Path(escaped_games_path).rglob("plugins/__init__.py"):
66+
module_path = "." + os.path.relpath(path.parent, curpath).replace(os.sep, ".")
67+
try:
68+
module = importlib.import_module(module_path, __package__)
69+
if hasattr(module, "createPlugins") and callable(
70+
getattr(module, "createPlugins")
71+
):
72+
game_plugins.extend(getattr(module, "createPlugins")())
73+
if hasattr(module, "createPlugin") and callable(
74+
getattr(module, "createPlugin")
75+
):
76+
game_plugins.append(getattr(module, "createPlugin")())
77+
except ImportError as e:
78+
qWarning(f"Error importing module {module_path}: {e}")
79+
except Exception as e:
80+
qWarning(f"Error calling function createPlugin(s) in {module_path}: {e}")
6181

6282
return game_plugins

games/baldursgate3/bg3_utils.py

Lines changed: 4 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import functools
2-
import json
3-
import os
42
import shutil
53
import typing
64
from pathlib import Path
75

86
import mobase
9-
import yaml
107
from PyQt6.QtCore import (
118
QCoreApplication,
129
QDir,
@@ -59,7 +56,7 @@ def __init__(self, name: str):
5956
self._name = name
6057
from . import lslib_retriever, pak_parser
6158

62-
self._lslib_retriever = lslib_retriever.LSLibRetriever(self)
59+
self.lslib_retriever = lslib_retriever.LSLibRetriever(self)
6360
self._pak_parser = pak_parser.BG3PakParser(self)
6461

6562
def init(self, organizer: mobase.IOrganizer):
@@ -150,7 +147,6 @@ def create_progress_window(
150147

151148
def on_user_interface_initialized(self, window: QMainWindow) -> None:
152149
self.main_window = window
153-
pass
154150

155151
def on_settings_changed(
156152
self,
@@ -161,24 +157,7 @@ def on_settings_changed(
161157
) -> None:
162158
if self._name != plugin_name:
163159
return
164-
if new and setting == "check_for_lslib_updates":
165-
try:
166-
self._lslib_retriever.download_lslib_if_missing()
167-
finally:
168-
self._set_setting(setting, False)
169-
elif new and setting == "force_reparse_metadata":
170-
try:
171-
self.construct_modsettings_xml(
172-
exec_path="bin/bg3", force_reparse_metadata=True
173-
)
174-
finally:
175-
self._set_setting(setting, False)
176-
elif new and setting == "convert_jsons_to_yaml":
177-
try:
178-
self._convert_jsons_to_yaml()
179-
finally:
180-
self._set_setting(setting, False)
181-
elif setting in {
160+
if setting in {
182161
"extract_full_package",
183162
"autobuild_paks",
184163
"remove_extracted_metadata",
@@ -197,7 +176,7 @@ def construct_modsettings_xml(
197176
) -> bool:
198177
if (
199178
"bin/bg3" not in exec_path
200-
or not self._lslib_retriever.download_lslib_if_missing()
179+
or not self.lslib_retriever.download_lslib_if_missing()
201180
):
202181
return True
203182
active_mods = self.active_mods()
@@ -257,41 +236,6 @@ def retrieve_mod_metadata_in_new_thread(mod: mobase.IModInterface):
257236
shutil.copy(self.modsettings_path, self.modsettings_backup)
258237
return True
259238

260-
def _convert_jsons_to_yaml(self):
261-
qInfo("converting all json files to yaml")
262-
active_mods = self.active_mods()
263-
progress = self.create_progress_window(
264-
"Converting all json files to yaml", len(active_mods) + 1
265-
)
266-
for mod in active_mods:
267-
_convert_jsons_in_dir_to_yaml(Path(mod.absolutePath()))
268-
progress.setValue(progress.value() + 1)
269-
QApplication.processEvents()
270-
if progress.wasCanceled():
271-
qWarning("conversion canceled by user")
272-
return
273-
_convert_jsons_in_dir_to_yaml(self.overwrite_path)
274-
progress.setValue(len(active_mods) + 1)
275-
QApplication.processEvents()
276-
progress.close()
277-
278239
def on_mod_installed(self, mod: mobase.IModInterface) -> None:
279-
if self._lslib_retriever.download_lslib_if_missing():
240+
if self.lslib_retriever.download_lslib_if_missing():
280241
self._pak_parser.get_metadata_for_files_in_mod(mod, True)
281-
282-
283-
def _convert_jsons_in_dir_to_yaml(path: Path):
284-
for file in list(path.rglob("*.json")):
285-
converted_path = file.parent / file.name.replace(".json", ".yaml")
286-
try:
287-
if not converted_path.exists() or os.path.getmtime(file) > os.path.getmtime(
288-
converted_path
289-
):
290-
with open(file, "r") as json_file:
291-
with open(converted_path, "w") as yaml_file:
292-
yaml.dump(
293-
json.load(json_file), yaml_file, indent=2, sort_keys=False
294-
)
295-
qInfo(f"Converted {file} to YAML")
296-
except OSError as e:
297-
qWarning(f"Error accessing file {converted_path}: {e}")

games/baldursgate3/lslib_retriever.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,8 @@ def _needed_lslib_files(self):
3333
}
3434
}
3535

36-
def download_lslib_if_missing(self):
37-
if not self._utils.get_setting("check_for_lslib_updates") and all(
38-
x.exists() for x in self._needed_lslib_files
39-
):
36+
def download_lslib_if_missing(self, force: bool = False) -> bool:
37+
if not force and all(x.exists() for x in self._needed_lslib_files):
4038
return True
4139
try:
4240
self._utils.tools_dir.mkdir(exist_ok=True, parents=True)
@@ -110,7 +108,11 @@ def reporthook(block_num: int, block_size: int, total_size: int) -> None:
110108
qDebug(f"Download failed: {e}")
111109
err = QMessageBox(self._utils.main_window)
112110
err.setIcon(QMessageBox.Icon.Critical)
113-
err.setText(f"Failed to download LSLib tools:\n{traceback.format_exc()}")
111+
err.setText(
112+
self._utils.tr(
113+
f"Failed to download LSLib tools:\n{traceback.format_exc()}"
114+
)
115+
)
114116
err.exec()
115117
return False
116118
try:
@@ -145,7 +147,11 @@ def reporthook(block_num: int, block_size: int, total_size: int) -> None:
145147
qDebug(f"Extraction failed: {e}")
146148
err = QMessageBox(self._utils.main_window)
147149
err.setIcon(QMessageBox.Icon.Critical)
148-
err.setText(f"Failed to extract LSLib tools:\n{traceback.format_exc()}")
150+
err.setText(
151+
self._utils.tr(
152+
f"Failed to extract LSLib tools:\n{traceback.format_exc()}"
153+
)
154+
)
149155
err.exec()
150156
return False
151157
return True
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import mobase
2+
3+
from .check_for_lslib_updates_plugin import BG3ToolCheckForLsLibUpdates
4+
from .convert_jsons_to_yaml_plugin import BG3ToolConvertJsonsToYaml
5+
from .reparse_pak_metadata_plugin import BG3ToolReparsePakMetadata
6+
7+
8+
def createPlugins() -> list[mobase.IPluginTool]:
9+
return [
10+
BG3ToolCheckForLsLibUpdates(),
11+
BG3ToolReparsePakMetadata(),
12+
BG3ToolConvertJsonsToYaml(),
13+
]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from pathlib import Path
2+
3+
import mobase
4+
from PyQt6.QtCore import QCoreApplication
5+
from PyQt6.QtGui import QIcon
6+
7+
8+
class BG3ToolPlugin(mobase.IPluginTool):
9+
icon_file = desc = sub_name = ""
10+
11+
def __init__(self):
12+
super().__init__()
13+
self._pluginName = self._displayName = "BG3 Tools"
14+
self._pluginVersion = mobase.VersionInfo(1, 0, 0)
15+
16+
def init(self, organizer: mobase.IOrganizer) -> bool:
17+
self._organizer = organizer
18+
return True
19+
20+
def version(self):
21+
return self._pluginVersion
22+
23+
def author(self):
24+
return "daescha"
25+
26+
def name(self):
27+
return f"{self._pluginName}: {self.sub_name}"
28+
29+
def displayName(self):
30+
return f"{self._displayName}/{self.sub_name}"
31+
32+
def tooltip(self):
33+
return self.description()
34+
35+
def enabledByDefault(self):
36+
return self._organizer.managedGame().name() == "Baldur's Gate 3 Plugin"
37+
38+
def settings(self) -> list[mobase.PluginSetting]:
39+
return []
40+
41+
def display(self):
42+
pass
43+
44+
def icon(self) -> QIcon:
45+
return QIcon(str(Path(__file__).parent / "icons" / self.icon_file))
46+
47+
def description(self) -> str:
48+
return QCoreApplication.translate(self._pluginName, self.desc)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from .bg3_tool_plugin import BG3ToolPlugin
2+
3+
4+
class BG3ToolCheckForLsLibUpdates(BG3ToolPlugin):
5+
icon_file = "ui-update.ico"
6+
sub_name = "Check For LsLib Updates"
7+
desc = "Check to see if there has been a new release of LSLib and create download dialog if so."
8+
9+
def display(self):
10+
from ...game_baldursgate3 import BG3Game
11+
12+
game_plugin = self._organizer.managedGame()
13+
if isinstance(game_plugin, BG3Game):
14+
game_plugin.utils.lslib_retriever.download_lslib_if_missing(True)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import json
2+
import os
3+
from pathlib import Path
4+
5+
from PyQt6.QtCore import qWarning, qInfo
6+
from PyQt6.QtWidgets import QApplication
7+
8+
from .bg3_tool_plugin import BG3ToolPlugin
9+
10+
11+
class BG3ToolConvertJsonsToYaml(BG3ToolPlugin):
12+
icon_file = "ui-next.ico"
13+
sub_name = "Convert JSONS to YAML"
14+
desc = "Convert all jsons in active mods to yaml immediately."
15+
16+
def display(self):
17+
from ...game_baldursgate3 import BG3Game
18+
19+
game_plugin = self._organizer.managedGame()
20+
if not isinstance(game_plugin, BG3Game):
21+
return
22+
utils = game_plugin.utils
23+
qInfo("converting all json files to yaml")
24+
active_mods = utils.active_mods()
25+
progress = utils.create_progress_window(
26+
"Converting all json files to yaml", len(active_mods) + 1
27+
)
28+
for mod in active_mods:
29+
_convert_jsons_in_dir_to_yaml(Path(mod.absolutePath()))
30+
progress.setValue(progress.value() + 1)
31+
QApplication.processEvents()
32+
if progress.wasCanceled():
33+
qWarning("conversion canceled by user")
34+
return
35+
_convert_jsons_in_dir_to_yaml(utils.overwrite_path)
36+
progress.setValue(len(active_mods) + 1)
37+
QApplication.processEvents()
38+
progress.close()
39+
40+
41+
def _convert_jsons_in_dir_to_yaml(path: Path):
42+
import yaml
43+
44+
for file in list(path.rglob("*.json")):
45+
converted_path = file.parent / file.name.replace(".json", ".yaml")
46+
try:
47+
if not converted_path.exists() or os.path.getmtime(file) > os.path.getmtime(
48+
converted_path
49+
):
50+
with open(file, "r") as json_file:
51+
with open(converted_path, "w") as yaml_file:
52+
yaml.dump(
53+
json.load(json_file), yaml_file, indent=2, sort_keys=False
54+
)
55+
qInfo(f"Converted {file} to YAML")
56+
except OSError as e:
57+
qWarning(f"Error accessing file {converted_path}: {e}")
108 KB
Binary file not shown.
112 KB
Binary file not shown.
118 KB
Binary file not shown.

0 commit comments

Comments
 (0)