aboutsummaryrefslogtreecommitdiff
path: root/libs/basic_games/games/baldursgate3/lslib_retriever.py
diff options
context:
space:
mode:
Diffstat (limited to 'libs/basic_games/games/baldursgate3/lslib_retriever.py')
-rw-r--r--libs/basic_games/games/baldursgate3/lslib_retriever.py193
1 files changed, 0 insertions, 193 deletions
diff --git a/libs/basic_games/games/baldursgate3/lslib_retriever.py b/libs/basic_games/games/baldursgate3/lslib_retriever.py
deleted file mode 100644
index 8b0e1dc..0000000
--- a/libs/basic_games/games/baldursgate3/lslib_retriever.py
+++ /dev/null
@@ -1,193 +0,0 @@
-import json
-import shutil
-import traceback
-import urllib.request
-import zipfile
-from functools import cached_property
-
-from PyQt6.QtCore import qDebug, qWarning
-from PyQt6.QtWidgets import QApplication, QMessageBox
-
-from . import bg3_utils
-
-
-class LSLibRetriever:
- def __init__(self, utils: bg3_utils.BG3Utils):
- self._utils = utils
-
- # Files that MUST be present for Divine to work.
- _REQUIRED_FILES = {
- "Divine.dll",
- "Divine.exe",
- "LSLib.dll",
- }
-
- # Additional files that may or may not be in a given LSLib release.
- _OPTIONAL_FILES = {
- "CommandLineArgumentsParser.dll",
- "Divine.dll.config",
- "Divine.runtimeconfig.json",
- "K4os.Compression.LZ4.dll",
- "K4os.Compression.LZ4.Streams.dll",
- "LSLibNative.dll",
- "LZ4.dll",
- "Newtonsoft.Json.dll",
- "System.IO.Hashing.dll",
- "ZstdSharp.dll",
- }
-
- @cached_property
- def _needed_lslib_files(self):
- return {
- self._utils.tools_dir / x
- for x in self._REQUIRED_FILES | self._OPTIONAL_FILES
- }
-
- def download_lslib_if_missing(self, force: bool = False) -> bool:
- # Only check required files to avoid re-downloading when optional
- # files were not present in the archive.
- required_paths = {
- self._utils.tools_dir / x for x in self._REQUIRED_FILES
- }
- if not force and all(x.exists() for x in required_paths):
- return True
- try:
- self._utils.tools_dir.mkdir(exist_ok=True, parents=True)
- downloaded = False
-
- def reporthook(block_num: int, block_size: int, total_size: int) -> None:
- if total_size > 0:
- progress.setValue(
- min(int(block_num * block_size * 100 / total_size), 100)
- )
- QApplication.processEvents()
-
- with urllib.request.urlopen(
- "https://api.github.com/repos/Norbyte/lslib/releases/latest"
- ) as response:
- assets = json.loads(response.read().decode("utf-8"))["assets"][0]
- zip_path = self._utils.tools_dir / assets["name"]
- if not zip_path.exists():
- old_archives = list(self._utils.tools_dir.glob("*.zip"))
- msg_box = QMessageBox(self._utils.main_window)
- msg_box.setWindowTitle(
- self._utils.tr("Baldur's Gate 3 Plugin - Missing dependencies")
- )
- if old_archives:
- msg_box.setText(self._utils.tr("LSLib update available."))
- else:
- msg_box.setText(
- self._utils.tr(
- "LSLib tools are missing.\nThese are necessary for the plugin to create the load order file for BG3."
- )
- )
- msg_box.addButton(
- self._utils.tr("Download"),
- QMessageBox.ButtonRole.DestructiveRole,
- )
- exit_btn = msg_box.addButton(
- self._utils.tr("Exit"), QMessageBox.ButtonRole.ActionRole
- )
- msg_box.setIcon(QMessageBox.Icon.Warning)
- msg_box.exec()
-
- if msg_box.clickedButton() == exit_btn:
- if not old_archives:
- err = QMessageBox(self._utils.main_window)
- err.setIcon(QMessageBox.Icon.Critical)
- err.setText(
- "LSLib tools are required for the proper generation of the modsettings.xml file, file will not be generated"
- )
- err.exec()
- return False
- else:
- progress = self._utils.create_progress_window(
- "Downloading LSLib", 100, cancelable=False
- )
- urllib.request.urlretrieve(
- assets["browser_download_url"], str(zip_path), reporthook
- )
- progress.close()
- downloaded = True
- for archive in old_archives:
- archive.unlink()
- old_archives = []
- else:
- old_archives = []
- new_msg = QMessageBox(self._utils.main_window)
- new_msg.setIcon(QMessageBox.Icon.Information)
- new_msg.setText(
- self._utils.tr("Latest version of LSLib already downloaded!")
- )
- new_msg.exec()
-
- except Exception as e:
- qDebug(f"Download failed: {e}")
- err = QMessageBox(self._utils.main_window)
- err.setIcon(QMessageBox.Icon.Critical)
- err.setText(
- self._utils.tr(
- f"Failed to download LSLib tools:\n{traceback.format_exc()}"
- )
- )
- err.exec()
- return False
- try:
- if old_archives:
- zip_path = sorted(old_archives)[-1]
- if old_archives or not downloaded:
- dialog_message = "Ensuring all necessary LSLib files have been extracted from archive..."
- win_title = "Verifying LSLib files"
- else:
- dialog_message = "Extracting/Updating LSLib files..."
- win_title = "Extracting LSLib"
- x_progress = self._utils.create_progress_window(
- win_title, len(self._needed_lslib_files), msg=dialog_message
- )
- with zipfile.ZipFile(zip_path, "r") as zip_ref:
- # Build a lookup from basename → zip entry so we can handle
- # any zip directory layout (Packed/Tools/, Tools/, flat, etc.)
- zip_names = zip_ref.namelist()
- entry_map: dict[str, str] = {}
- for zn in zip_names:
- base = zn.rsplit("/", 1)[-1] if "/" in zn else zn
- if base:
- entry_map[base] = zn
-
- for file in self._needed_lslib_files:
- if downloaded or not file.exists():
- zip_entry = entry_map.get(file.name)
- if zip_entry is None:
- if file.name in self._REQUIRED_FILES:
- raise KeyError(
- f"Required file '{file.name}' not found in archive"
- )
- qDebug(f"LSLib: optional file '{file.name}' not in archive, skipping")
- continue
- extracted = zip_ref.extract(zip_entry, self._utils.tools_dir)
- # Move from nested zip path to flat tools_dir
- if extracted != str(file):
- shutil.move(extracted, file)
- x_progress.setValue(x_progress.value() + 1)
- QApplication.processEvents()
- if x_progress.wasCanceled():
- qWarning("processing canceled by user")
- return False
- x_progress.close()
- # Clean up any nested directories left from extraction
- for subdir in ("Packed", "Tools"):
- nested = self._utils.tools_dir / subdir
- if nested.is_dir():
- shutil.rmtree(nested, ignore_errors=True)
- except Exception as e:
- qDebug(f"Extraction failed: {e}")
- err = QMessageBox(self._utils.main_window)
- err.setIcon(QMessageBox.Icon.Critical)
- err.setText(
- self._utils.tr(
- f"Failed to extract LSLib tools:\n{traceback.format_exc()}"
- )
- )
- err.exec()
- return False
- return True