aboutsummaryrefslogtreecommitdiff
path: root/libs/basic_games/games/oblivion_remaster/paks/widget.py
diff options
context:
space:
mode:
Diffstat (limited to 'libs/basic_games/games/oblivion_remaster/paks/widget.py')
-rw-r--r--libs/basic_games/games/oblivion_remaster/paks/widget.py213
1 files changed, 213 insertions, 0 deletions
diff --git a/libs/basic_games/games/oblivion_remaster/paks/widget.py b/libs/basic_games/games/oblivion_remaster/paks/widget.py
new file mode 100644
index 0000000..efc3038
--- /dev/null
+++ b/libs/basic_games/games/oblivion_remaster/paks/widget.py
@@ -0,0 +1,213 @@
+from functools import cmp_to_key
+from pathlib import Path
+from typing import cast
+
+from PyQt6.QtCore import QDir, QFileInfo
+from PyQt6.QtWidgets import QGridLayout, QWidget
+
+import mobase
+
+from ....basic_features.utils import is_directory
+from .model import PaksModel
+from .view import PaksView
+
+
+def pak_sort(a: tuple[str, str], b: tuple[str, str]) -> int:
+ a_pak, a_str = a[0], a[1] or a[0]
+ b_pak, b_str = b[0], b[1] or b[0]
+
+ a_pak_ends_p = a_pak.casefold().endswith("_p")
+ b_pak_ends_p = b_pak.casefold().endswith("_p")
+
+ if a_pak_ends_p == b_pak_ends_p:
+ if a_str.casefold() <= b_str.casefold():
+ return 1
+ return -1
+ elif a_pak_ends_p:
+ return 1
+ elif b_pak_ends_p:
+ return -1
+ return 0
+
+
+class PaksTabWidget(QWidget):
+ def __init__(self, parent: QWidget | None, organizer: mobase.IOrganizer):
+ super().__init__(parent)
+ self._organizer = organizer
+ self._view = PaksView(self)
+ self._layout = QGridLayout(self)
+ self._layout.addWidget(self._view)
+ self._model = PaksModel(self._view, organizer)
+ self._view.setModel(self._model)
+ self._model.dataChanged.connect(self.write_paks_list) # type: ignore
+ self._view.data_dropped.connect(self.write_paks_list) # type: ignore
+ organizer.onProfileChanged(lambda profile_a, profile_b: self._parse_pak_files())
+ organizer.modList().onModInstalled(lambda mod: self._parse_pak_files())
+ organizer.modList().onModRemoved(lambda mod: self._parse_pak_files())
+ organizer.modList().onModStateChanged(lambda mods: self._parse_pak_files())
+ self._parse_pak_files()
+
+ def load_paks_list(self) -> list[str]:
+ profile = QDir(self._organizer.profilePath())
+ paks_txt = QFileInfo(profile.absoluteFilePath("paks.txt"))
+ paks_list: list[str] = []
+ if paks_txt.exists():
+ with open(paks_txt.absoluteFilePath(), "r") as paks_file:
+ for line in paks_file:
+ paks_list.append(line.strip())
+ return paks_list
+
+ def write_paks_list(self):
+ profile = QDir(self._organizer.profilePath())
+ paks_txt = QFileInfo(profile.absoluteFilePath("paks.txt"))
+ with open(paks_txt.absoluteFilePath(), "w") as paks_file:
+ for _, pak in sorted(self._model.paks.items()):
+ name, _, _, _ = pak
+ paks_file.write(f"{name}\n")
+ self.write_pak_files()
+
+ def write_pak_files(self):
+ for index, pak in sorted(self._model.paks.items()):
+ _, _, current_path, target_path = pak
+ if current_path and current_path != target_path:
+ path_dir = Path(current_path)
+ target_dir = Path(target_path)
+ if not target_dir.exists():
+ target_dir.mkdir(parents=True, exist_ok=True)
+ if path_dir.exists():
+ for pak_file in path_dir.glob("*.pak"):
+ ucas_file = pak_file.with_suffix(".ucas")
+ utoc_file = pak_file.with_suffix(".utoc")
+ for file in (pak_file, ucas_file, utoc_file):
+ if not file.exists():
+ continue
+ try:
+ file.rename(target_dir.joinpath(file.name))
+ except FileExistsError:
+ pass
+ data = self._model.paks[index]
+ self._model.paks[index] = (
+ data[0],
+ data[1],
+ data[3],
+ data[3],
+ )
+ break
+ if not list(path_dir.iterdir()):
+ path_dir.rmdir()
+
+ def _shake_paks(self, sorted_paks: dict[str, str]) -> list[str]:
+ shaken_paks: list[str] = []
+ shaken_paks_p: list[str] = []
+ paks_list = self.load_paks_list()
+ for pak in paks_list:
+ if pak in sorted_paks.keys():
+ if pak.casefold().endswith("_p"):
+ shaken_paks_p.append(pak)
+ else:
+ shaken_paks.append(pak)
+ sorted_paks.pop(pak)
+ for pak in sorted_paks.keys():
+ if pak.casefold().endswith("_p"):
+ shaken_paks_p.append(pak)
+ else:
+ shaken_paks.append(pak)
+ return shaken_paks + shaken_paks_p
+
+ def _parse_pak_files(self):
+ from ...game_oblivion_remaster import OblivionRemasteredGame
+
+ mods = self._organizer.modList().allMods()
+ paks: dict[str, str] = {}
+ pak_paths: dict[str, tuple[str, str]] = {}
+ pak_source: dict[str, str] = {}
+ for mod in mods:
+ mod_item = self._organizer.modList().getMod(mod)
+ if not self._organizer.modList().state(mod) & mobase.ModState.ACTIVE:
+ continue
+ filetree = mod_item.fileTree()
+ pak_mods = filetree.find("Paks/~mods")
+ if not pak_mods:
+ pak_mods = filetree.find("Root/OblivionRemastered/Content/Paks/~mods")
+ if isinstance(pak_mods, mobase.IFileTree):
+ for entry in pak_mods:
+ if is_directory(entry):
+ if "magicloader" in entry.name().casefold():
+ continue
+ for sub_entry in entry:
+ if (
+ sub_entry.isFile()
+ and sub_entry.suffix().casefold() == "pak"
+ ):
+ pak_name = sub_entry.name()[
+ : -1 - len(sub_entry.suffix())
+ ]
+ paks[pak_name] = entry.name()
+ pak_paths[pak_name] = (
+ mod_item.absolutePath()
+ + "/"
+ + cast(mobase.IFileTree, sub_entry.parent()).path(
+ "/"
+ ),
+ mod_item.absolutePath() + "/" + pak_mods.path("/"),
+ )
+ pak_source[pak_name] = mod_item.name()
+ else:
+ if entry.suffix().casefold() == "pak":
+ pak_name = entry.name()[: -1 - len(entry.suffix())]
+ paks[pak_name] = ""
+ pak_paths[pak_name] = (
+ mod_item.absolutePath()
+ + "/"
+ + cast(mobase.IFileTree, entry.parent()).path("/"),
+ mod_item.absolutePath() + "/" + pak_mods.path("/"),
+ )
+ pak_source[pak_name] = mod_item.name()
+ game = self._organizer.managedGame()
+ if isinstance(game, OblivionRemasteredGame):
+ pak_mods = QFileInfo(game.paksDirectory().absoluteFilePath("~mods"))
+ if pak_mods.exists() and pak_mods.isDir():
+ for entry in QDir(pak_mods.absoluteFilePath()).entryInfoList(
+ QDir.Filter.Dirs | QDir.Filter.Files | QDir.Filter.NoDotAndDotDot
+ ):
+ if entry.isDir():
+ if "magicloader" in entry.completeBaseName().casefold():
+ continue
+ for sub_entry in QDir(entry.absoluteFilePath()).entryInfoList(
+ QDir.Filter.Files
+ ):
+ if (
+ sub_entry.isFile()
+ and sub_entry.suffix().casefold() == "pak"
+ ):
+ pak_name = sub_entry.completeBaseName()
+ paks[pak_name] = entry.completeBaseName()
+ pak_paths[pak_name] = (
+ sub_entry.absolutePath(),
+ pak_mods.absolutePath(),
+ )
+ pak_source[pak_name] = "Game Directory"
+ else:
+ if entry.suffix().casefold() == "pak":
+ pak_name = entry.completeBaseName()
+ paks[pak_name] = ""
+ pak_paths[pak_name] = (
+ entry.absolutePath(),
+ pak_mods.absolutePath(),
+ )
+ pak_source[pak_name] = "Game Directory"
+ sorted_paks = dict(sorted(paks.items(), key=cmp_to_key(pak_sort)))
+ shaken_paks: list[str] = self._shake_paks(sorted_paks)
+ final_paks: dict[str, tuple[str, str, str]] = {}
+ pak_index = 8999
+ for pak in shaken_paks:
+ target_dir = pak_paths[pak][1] + "/" + str(pak_index).zfill(4)
+ final_paks[pak] = (pak_source[pak], pak_paths[pak][0], target_dir)
+ pak_index -= 1
+ new_data_paks: dict[int, tuple[str, str, str, str]] = {}
+ i = 0
+ for pak, data in final_paks.items():
+ source, current_path, target_path = data
+ new_data_paks[i] = (pak, source, current_path, target_path)
+ i += 1
+ self._model.set_paks(new_data_paks)