mirror of
https://github.com/ModOrganizer2/modorganizer-basic_games.git
synced 2026-07-27 14:07:29 -07:00
208 lines
7.8 KiB
Python
208 lines
7.8 KiB
Python
from functools import cmp_to_key
|
|
from typing import Sequence
|
|
|
|
from PyQt6.QtCore import (
|
|
QByteArray,
|
|
QCoreApplication,
|
|
QDateTime,
|
|
QFile,
|
|
QFileInfo,
|
|
QStringConverter,
|
|
QStringEncoder,
|
|
qCritical,
|
|
qWarning,
|
|
)
|
|
|
|
import mobase
|
|
|
|
|
|
class OblivionRemasteredGamePlugins(mobase.GamePlugins):
|
|
"""
|
|
Reimplementation of GameGamebryo "GamePlugins" code, in the Skyrim style.
|
|
|
|
Should properly account for disabled plugins and the loadorder.txt profile file.
|
|
"""
|
|
|
|
def __init__(self, organizer: mobase.IOrganizer):
|
|
super().__init__()
|
|
self._last_read = QDateTime().currentDateTime()
|
|
self._organizer = organizer
|
|
# Not currently used. These plugins exist in the base game but are not enabled by default.
|
|
self._plugin_blacklist = ["TamrielLevelledRegion.esp", "AltarGymNavigation.esp"]
|
|
|
|
def writePluginLists(self, plugin_list: mobase.IPluginList) -> None:
|
|
if not self._last_read.isValid():
|
|
return
|
|
self.writePluginList(
|
|
plugin_list, self._organizer.profile().absolutePath() + "/plugins.txt"
|
|
)
|
|
self.writeLoadOrderList(
|
|
plugin_list, self._organizer.profile().absolutePath() + "/loadorder.txt"
|
|
)
|
|
self._last_read = QDateTime.currentDateTime()
|
|
|
|
def readPluginLists(self, plugin_list: mobase.IPluginList) -> None:
|
|
load_order_path = self._organizer.profile().absolutePath() + "/loadorder.txt"
|
|
load_order = self.readLoadOrderList(plugin_list, load_order_path)
|
|
plugin_list.setLoadOrder(load_order)
|
|
self.readPluginList(plugin_list)
|
|
self._last_read = QDateTime.currentDateTime()
|
|
|
|
def getLoadOrder(self) -> Sequence[str]:
|
|
load_order_path = self._organizer.profile().absolutePath() + "/loadorder.txt"
|
|
plugins_path = self._organizer.profile().absolutePath() + "/plugins.txt"
|
|
|
|
load_order_is_new = (
|
|
not self._last_read.isValid()
|
|
or not QFileInfo(load_order_path).exists()
|
|
or QFileInfo(load_order_path).lastModified() > self._last_read
|
|
)
|
|
plugins_is_new = (
|
|
not self._last_read.isValid()
|
|
or QFileInfo(plugins_path).lastModified() > self._last_read
|
|
)
|
|
|
|
if load_order_is_new or not plugins_is_new:
|
|
return self.readLoadOrderList(self._organizer.pluginList(), load_order_path)
|
|
else:
|
|
return self.readPluginList(self._organizer.pluginList())
|
|
|
|
def writePluginList(self, plugin_list: mobase.IPluginList, filePath: str):
|
|
self.writeList(plugin_list, filePath, False)
|
|
|
|
def writeLoadOrderList(self, plugin_list: mobase.IPluginList, filePath: str):
|
|
self.writeList(plugin_list, filePath, True)
|
|
|
|
def writeList(
|
|
self, plugin_list: mobase.IPluginList, filePath: str, load_order: bool
|
|
):
|
|
plugins_file = open(filePath, "w")
|
|
encoder = (
|
|
QStringEncoder(QStringConverter.Encoding.Utf8)
|
|
if load_order
|
|
else QStringEncoder(QStringConverter.Encoding.System)
|
|
)
|
|
plugins_text = "# This file was automatically generated by Mod Organizer.\n"
|
|
invalid_filenames = False
|
|
written_count = 0
|
|
plugins = plugin_list.pluginNames()
|
|
plugins_sorted = sorted(
|
|
plugins,
|
|
key=cmp_to_key(
|
|
lambda lhs, rhs: plugin_list.priority(lhs) - plugin_list.priority(rhs)
|
|
),
|
|
)
|
|
for plugin_name in plugins_sorted:
|
|
if (
|
|
load_order
|
|
or plugin_list.state(plugin_name) == mobase.PluginState.ACTIVE
|
|
):
|
|
result = encoder.encode(plugin_name)
|
|
if encoder.hasError():
|
|
invalid_filenames = True
|
|
qCritical("invalid plugin name %s" % plugin_name)
|
|
plugins_text += result.data().decode() + "\n"
|
|
written_count += 1
|
|
|
|
if invalid_filenames:
|
|
qCritical(
|
|
QCoreApplication.translate(
|
|
"MainWindow",
|
|
"Some of your plugins have invalid names! These "
|
|
+ "plugins can not be loaded by the game. Please see "
|
|
+ "mo_interface.log for a list of affected plugins "
|
|
+ "and rename them.",
|
|
)
|
|
)
|
|
|
|
if written_count == 0:
|
|
qWarning(
|
|
"plugin list would be empty, this is almost certainly wrong. Not saving."
|
|
)
|
|
else:
|
|
plugins_file.write(plugins_text)
|
|
plugins_file.close()
|
|
|
|
def readLoadOrderList(
|
|
self, plugin_list: mobase.IPluginList, file_path: str
|
|
) -> list[str]:
|
|
plugin_names = [
|
|
plugin for plugin in self._organizer.managedGame().primaryPlugins()
|
|
]
|
|
plugin_lookup: set[str] = set()
|
|
for name in plugin_names:
|
|
if name.lower() not in plugin_lookup:
|
|
plugin_lookup.add(name.lower())
|
|
|
|
try:
|
|
with open(file_path) as file:
|
|
for line in file:
|
|
if line.startswith("#"):
|
|
continue
|
|
plugin_file = line.rstrip("\n")
|
|
if plugin_file.lower() not in plugin_lookup:
|
|
plugin_lookup.add(plugin_file.lower())
|
|
plugin_names.append(plugin_file)
|
|
except FileNotFoundError:
|
|
return self.readPluginList(plugin_list)
|
|
|
|
return plugin_names
|
|
|
|
def readPluginList(self, plugin_list: mobase.IPluginList) -> list[str]:
|
|
plugins = [plugin for plugin in plugin_list.pluginNames()]
|
|
sorted_plugins: list[str] = []
|
|
primary = [plugin for plugin in self._organizer.managedGame().primaryPlugins()]
|
|
primary_lower = [plugin.lower() for plugin in primary]
|
|
for plugin_name in primary:
|
|
if plugin_list.state(plugin_name) != mobase.PluginState.MISSING:
|
|
plugin_list.setState(plugin_name, mobase.PluginState.ACTIVE)
|
|
sorted_plugins.append(plugin_name)
|
|
plugin_remove = [
|
|
plugin for plugin in plugins if plugin.lower() in primary_lower
|
|
]
|
|
for plugin in plugin_remove:
|
|
plugins.remove(plugin)
|
|
|
|
plugins_txt_exists = True
|
|
file_path = self._organizer.profile().absolutePath() + "/plugins.txt"
|
|
file = QFile(file_path)
|
|
if not file.open(QFile.OpenModeFlag.ReadOnly):
|
|
plugins_txt_exists = False
|
|
if file.size() == 0:
|
|
plugins_txt_exists = False
|
|
if plugins_txt_exists:
|
|
while not file.atEnd():
|
|
line = file.readLine()
|
|
file_plugin_name = QByteArray()
|
|
if line.size() > 0 and line.at(0).decode() != "#":
|
|
encoder = QStringEncoder(QStringEncoder.Encoding.System)
|
|
file_plugin_name = encoder.encode(line.trimmed().data().decode())
|
|
if file_plugin_name.size() > 0:
|
|
if file_plugin_name.data().decode().lower() in [
|
|
plugin.lower() for plugin in plugins
|
|
]:
|
|
plugin_list.setState(
|
|
file_plugin_name.data().decode(), mobase.PluginState.ACTIVE
|
|
)
|
|
sorted_plugins.append(file_plugin_name.data().decode())
|
|
plugins.remove(file_plugin_name.data().decode())
|
|
|
|
file.close()
|
|
|
|
for plugin_name in plugins:
|
|
plugin_list.setState(plugin_name, mobase.PluginState.INACTIVE)
|
|
else:
|
|
for plugin_name in plugins:
|
|
plugin_list.setState(plugin_name, mobase.PluginState.INACTIVE)
|
|
|
|
return sorted_plugins + plugins
|
|
|
|
def lightPluginsAreSupported(self) -> bool:
|
|
return False
|
|
|
|
def mediumPluginsAreSupported(self) -> bool:
|
|
return False
|
|
|
|
def blueprintPluginsAreSupported(self) -> bool:
|
|
return False
|