aboutsummaryrefslogtreecommitdiff
path: root/libs/plugin_python/src/mobase/wrappers/basic_classes.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/plugin_python/src/mobase/wrappers/basic_classes.cpp')
-rw-r--r--libs/plugin_python/src/mobase/wrappers/basic_classes.cpp952
1 files changed, 952 insertions, 0 deletions
diff --git a/libs/plugin_python/src/mobase/wrappers/basic_classes.cpp b/libs/plugin_python/src/mobase/wrappers/basic_classes.cpp
new file mode 100644
index 0000000..ea69a46
--- /dev/null
+++ b/libs/plugin_python/src/mobase/wrappers/basic_classes.cpp
@@ -0,0 +1,952 @@
+#include "wrappers.h"
+
+#include "../pybind11_all.h"
+
+#include <format>
+#include <memory>
+
+#include <pybind11_utils/generator.h>
+#include <uibase/executableinfo.h>
+#include <uibase/filemapping.h>
+#include <uibase/game_features/igamefeatures.h>
+#include <uibase/guessedvalue.h>
+#include <uibase/idownloadmanager.h>
+#include <uibase/iexecutable.h>
+#include <uibase/iexecutableslist.h>
+#include <uibase/iinstallationmanager.h>
+#include <uibase/iinstance.h>
+#include <uibase/iinstancemanager.h>
+#include <uibase/imodinterface.h>
+#include <uibase/imodrepositorybridge.h>
+#include <uibase/imoinfo.h>
+#include <uibase/iplugin.h>
+#include <uibase/iplugindiagnose.h>
+#include <uibase/iplugingame.h>
+#include <uibase/ipluginlist.h>
+#include <uibase/pluginsetting.h>
+#include <uibase/versioninfo.h>
+
+#include "../deprecation.h"
+#include "pyfiletree.h"
+
+using namespace MOBase;
+
+namespace mo2::python {
+
+ namespace py = pybind11;
+
+ using namespace pybind11::literals;
+
+ void add_versioninfo_classes(py::module_ m)
+ {
+ // Version
+ py::class_<Version> pyVersion(m, "Version");
+
+ py::enum_<Version::ReleaseType>(pyVersion, "ReleaseType")
+ .value("DEVELOPMENT", Version::Development)
+ .value("ALPHA", Version::Alpha)
+ .value("BETA", Version::Beta)
+ .value("RELEASE_CANDIDATE", Version::ReleaseCandidate)
+ .export_values();
+
+ py::enum_<Version::ParseMode>(pyVersion, "ParseMode")
+ .value("SEMVER", Version::ParseMode::SemVer)
+ .value("MO2", Version::ParseMode::MO2);
+
+ py::enum_<Version::FormatMode>(pyVersion, "FormatMode", py::arithmetic{})
+ .value("FORCE_SUBPATCH", Version::FormatMode::ForceSubPatch)
+ .value("NO_SEPARATOR", Version::FormatMode::NoSeparator)
+ .value("SHORT_ALPHA_BETA", Version::FormatMode::ShortAlphaBeta)
+ .value("NO_METADATA", Version::FormatMode::NoMetadata)
+ .value("CONDENSED",
+ static_cast<Version::FormatMode>(Version::FormatCondensed.toInt()))
+ .export_values()
+ .def("__xor__",
+ py::overload_cast<Version::FormatMode, Version::FormatModes>(
+ &operator^))
+ .def("__and__",
+ py::overload_cast<Version::FormatMode, Version::FormatModes>(
+ &operator&))
+ .def("__or__", py::overload_cast<Version::FormatMode, Version::FormatModes>(
+ &operator|))
+ .def("__rxor__",
+ py::overload_cast<Version::FormatMode, Version::FormatModes>(
+ &operator^))
+ .def("__rand__",
+ py::overload_cast<Version::FormatMode, Version::FormatModes>(
+ &operator&))
+ .def("__ror__",
+ py::overload_cast<Version::FormatMode, Version::FormatModes>(
+ &operator|));
+
+ pyVersion
+ .def_static("parse", &Version::parse, "value"_a,
+ "mode"_a = Version::ParseMode::SemVer)
+ .def(py::init<int, int, int, QString>(), "major"_a, "minor"_a, "patch"_a,
+ "metadata"_a = "")
+ .def(py::init<int, int, int, int, QString>(), "major"_a, "minor"_a,
+ "patch"_a, "subpatch"_a, "metadata"_a = "")
+ .def(py::init<int, int, int, Version::ReleaseType, QString>(), "major"_a,
+ "minor"_a, "patch"_a, "type"_a, "metadata"_a = "")
+ .def(py::init<int, int, int, int, Version::ReleaseType, QString>(),
+ "major"_a, "minor"_a, "patch"_a, "subpatch"_a, "type"_a,
+ "metadata"_a = "")
+ .def(py::init<int, int, int, Version::ReleaseType, int, QString>(),
+ "major"_a, "minor"_a, "patch"_a, "type"_a, "prerelease"_a,
+ "metadata"_a = "")
+ .def(py::init<int, int, int, int, Version::ReleaseType, int, QString>(),
+ "major"_a, "minor"_a, "patch"_a, "subpatch"_a, "type"_a,
+ "prerelease"_a, "metadata"_a = "")
+ .def(py::init<int, int, int, int,
+ std::vector<std::variant<int, Version::ReleaseType>>,
+ QString>(),
+ "major"_a, "minor"_a, "patch"_a, "subpatch"_a, "prereleases"_a,
+ "metadata"_a = "")
+ .def("isPreRelease", &Version::isPreRelease)
+ .def_property_readonly("major", &Version::major)
+ .def_property_readonly("minor", &Version::minor)
+ .def_property_readonly("patch", &Version::patch)
+ .def_property_readonly("subpatch", &Version::subpatch)
+ .def_property_readonly("prereleases", &Version::preReleases)
+ .def_property_readonly("build_metadata", &Version::buildMetadata)
+ .def("string", &Version::string, "mode"_a = Version::FormatModes{})
+ .def("__str__",
+ [](Version const& version) {
+ return version.string(Version::FormatCondensed);
+ })
+ .def(py::self < py::self)
+ .def(py::self > py::self)
+ .def(py::self <= py::self)
+ .def(py::self >= py::self)
+ .def(py::self != py::self)
+ .def(py::self == py::self);
+
+ // VersionInfo
+ py::enum_<MOBase::VersionInfo::ReleaseType>(m, "ReleaseType")
+ .value("final", MOBase::VersionInfo::RELEASE_FINAL)
+ .value("candidate", MOBase::VersionInfo::RELEASE_CANDIDATE)
+ .value("beta", MOBase::VersionInfo::RELEASE_BETA)
+ .value("alpha", MOBase::VersionInfo::RELEASE_ALPHA)
+ .value("prealpha", MOBase::VersionInfo::RELEASE_PREALPHA)
+
+ .value("FINAL", MOBase::VersionInfo::RELEASE_FINAL)
+ .value("CANDIDATE", MOBase::VersionInfo::RELEASE_CANDIDATE)
+ .value("BETA", MOBase::VersionInfo::RELEASE_BETA)
+ .value("ALPHA", MOBase::VersionInfo::RELEASE_ALPHA)
+ .value("PRE_ALPHA", MOBase::VersionInfo::RELEASE_PREALPHA);
+
+ py::enum_<MOBase::VersionInfo::VersionScheme>(m, "VersionScheme")
+ .value("discover", MOBase::VersionInfo::SCHEME_DISCOVER)
+ .value("regular", MOBase::VersionInfo::SCHEME_REGULAR)
+ .value("decimalmark", MOBase::VersionInfo::SCHEME_DECIMALMARK)
+ .value("numbersandletters", MOBase::VersionInfo::SCHEME_NUMBERSANDLETTERS)
+ .value("date", MOBase::VersionInfo::SCHEME_DATE)
+ .value("literal", MOBase::VersionInfo::SCHEME_LITERAL)
+
+ .value("DISCOVER", MOBase::VersionInfo::SCHEME_DISCOVER)
+ .value("REGULAR", MOBase::VersionInfo::SCHEME_REGULAR)
+ .value("DECIMAL_MARK", MOBase::VersionInfo::SCHEME_DECIMALMARK)
+ .value("NUMBERS_AND_LETTERS", MOBase::VersionInfo::SCHEME_NUMBERSANDLETTERS)
+ .value("DATE", MOBase::VersionInfo::SCHEME_DATE)
+ .value("LITERAL", MOBase::VersionInfo::SCHEME_LITERAL);
+
+ py::class_<VersionInfo>(m, "VersionInfo")
+ .def(py::init<>())
+ .def(py::init<QString, VersionInfo::VersionScheme>(), "value"_a,
+ "scheme"_a = VersionInfo::SCHEME_DISCOVER)
+ // note: order of the two init<> below is important because
+ // ReleaseType is a simple enum with an implicit int conversion.
+ .def(py::init<int, int, int, int, VersionInfo::ReleaseType>(), "major"_a,
+ "minor"_a, "subminor"_a, "subsubminor"_a,
+ "release_type"_a = VersionInfo::RELEASE_FINAL)
+ .def(py::init<int, int, int, VersionInfo::ReleaseType>(), "major"_a,
+ "minor"_a, "subminor"_a, "release_type"_a = VersionInfo::RELEASE_FINAL)
+ .def("clear", &VersionInfo::clear)
+ .def("parse", &VersionInfo::parse, "value"_a,
+ "scheme"_a = VersionInfo::SCHEME_DISCOVER, "is_manual"_a = false)
+ .def("canonicalString", &VersionInfo::canonicalString)
+ .def("displayString", &VersionInfo::displayString, "forced_segments"_a = 2)
+ .def("isValid", &VersionInfo::isValid)
+ .def("scheme", &VersionInfo::scheme)
+ .def("__str__", &VersionInfo::canonicalString)
+ .def(py::self < py::self)
+ .def(py::self > py::self)
+ .def(py::self <= py::self)
+ .def(py::self >= py::self)
+ .def(py::self != py::self)
+ .def(py::self == py::self);
+ }
+
+ void add_executable_classes(py::module_ m)
+ {
+ py::class_<ExecutableInfo>(m, "ExecutableInfo")
+ .def(py::init<const QString&, const FileWrapper&>(), "title"_a, "binary"_a)
+ .def("withArgument", &ExecutableInfo::withArgument, "argument"_a)
+ .def("withWorkingDirectory",
+ wrap_for_directory(&ExecutableInfo::withWorkingDirectory),
+ "directory"_a)
+ .def("withSteamAppId", &ExecutableInfo::withSteamAppId, "app_id"_a)
+ .def("asCustom", &ExecutableInfo::asCustom)
+ .def("isValid", &ExecutableInfo::isValid)
+ .def("title", &ExecutableInfo::title)
+ .def("binary", &ExecutableInfo::binary)
+ .def("arguments", &ExecutableInfo::arguments)
+ .def("workingDirectory", &ExecutableInfo::workingDirectory)
+ .def("steamAppID", &ExecutableInfo::steamAppID)
+ .def("isCustom", &ExecutableInfo::isCustom);
+
+ py::class_<ExecutableForcedLoadSetting>(m, "ExecutableForcedLoadSetting")
+ .def(py::init<const QString&, const QString&>(), "process"_a, "library"_a)
+ .def("withForced", &ExecutableForcedLoadSetting::withForced, "forced"_a)
+ .def("withEnabled", &ExecutableForcedLoadSetting::withEnabled, "enabled"_a)
+ .def("enabled", &ExecutableForcedLoadSetting::enabled)
+ .def("forced", &ExecutableForcedLoadSetting::forced)
+ .def("library", &ExecutableForcedLoadSetting::library)
+ .def("process", &ExecutableForcedLoadSetting::process);
+
+ py::class_<IExecutable>(m, "IExecutable")
+ .def("title", &IExecutable::title)
+ .def("binaryInfo", &IExecutable::binaryInfo)
+ .def("arguments", &IExecutable::arguments)
+ .def("steamAppID", &IExecutable::steamAppID)
+ .def("workingDirectory", &IExecutable::workingDirectory)
+ .def("isShownOnToolbar", &IExecutable::isShownOnToolbar)
+ .def("usesOwnIcon", &IExecutable::usesOwnIcon)
+ .def("minimizeToSystemTray", &IExecutable::minimizeToSystemTray)
+ .def("hide", &IExecutable::hide);
+
+ py::class_<IExecutablesList>(m, "IExecutablesList")
+ .def("executables",
+ [](IExecutablesList* executablesList) {
+ return make_generator(executablesList->executables(),
+ py::return_value_policy::reference);
+ })
+ .def("getByTitle", &IExecutablesList::getByTitle, "title"_a)
+ .def("getByBinary", &IExecutablesList::getByBinary, "info"_a)
+ .def("titleExists", &IExecutablesList::contains, "title"_a);
+ }
+
+ void add_modinterface_classes(py::module_ m)
+ {
+ py::enum_<EndorsedState>(m, "EndorsedState", py::arithmetic())
+ .value("ENDORSED_FALSE", EndorsedState::ENDORSED_FALSE)
+ .value("ENDORSED_TRUE", EndorsedState::ENDORSED_TRUE)
+ .value("ENDORSED_UNKNOWN", EndorsedState::ENDORSED_UNKNOWN)
+ .value("ENDORSED_NEVER", EndorsedState::ENDORSED_NEVER);
+
+ py::enum_<TrackedState>(m, "TrackedState", py::arithmetic())
+ .value("TRACKED_FALSE", TrackedState::TRACKED_FALSE)
+ .value("TRACKED_TRUE", TrackedState::TRACKED_TRUE)
+ .value("TRACKED_UNKNOWN", TrackedState::TRACKED_UNKNOWN);
+
+ py::class_<IModInterface>(m, "IModInterface")
+ .def("name", &IModInterface::name)
+ .def("absolutePath", &IModInterface::absolutePath)
+
+ .def("comments", &IModInterface::comments)
+ .def("notes", &IModInterface::notes)
+ .def("gameName", &IModInterface::gameName)
+ .def("repository", &IModInterface::repository)
+ .def("nexusId", &IModInterface::nexusId)
+ .def("version", &IModInterface::version)
+ .def("newestVersion", &IModInterface::newestVersion)
+ .def("ignoredVersion", &IModInterface::ignoredVersion)
+ .def("installationFile", &IModInterface::installationFile)
+ .def("converted", &IModInterface::converted)
+ .def("validated", &IModInterface::validated)
+ .def("color", &IModInterface::color)
+ .def("url", &IModInterface::url)
+ .def("primaryCategory", &IModInterface::primaryCategory)
+ .def("categories", &IModInterface::categories)
+ .def("author", &IModInterface::author)
+ .def("uploader", &IModInterface::uploader)
+ .def("uploaderUrl", &IModInterface::uploaderUrl)
+ .def("trackedState", &IModInterface::trackedState)
+ .def("endorsedState", &IModInterface::endorsedState)
+ .def("fileTree", &IModInterface::fileTree)
+ .def("isOverwrite", &IModInterface::isOverwrite)
+ .def("isBackup", &IModInterface::isBackup)
+ .def("isSeparator", &IModInterface::isSeparator)
+ .def("isForeign", &IModInterface::isForeign)
+
+ .def("setVersion", &IModInterface::setVersion, "version"_a)
+ .def("setNewestVersion", &IModInterface::setNewestVersion, "version"_a)
+ .def("setIsEndorsed", &IModInterface::setIsEndorsed, "endorsed"_a)
+ .def("setNexusID", &IModInterface::setNexusID, "nexus_id"_a)
+ .def("addNexusCategory", &IModInterface::addNexusCategory, "category_id"_a)
+ .def("addCategory", &IModInterface::addCategory, "name"_a)
+ .def("removeCategory", &IModInterface::removeCategory, "name"_a)
+ .def("setGameName", &IModInterface::setGameName, "name"_a)
+ .def("setUrl", &IModInterface::setUrl, "url"_a)
+ .def("pluginSetting", &IModInterface::pluginSetting, "plugin_name"_a,
+ "key"_a, "default"_a = QVariant())
+ .def("pluginSettings", &IModInterface::pluginSettings, "plugin_name"_a)
+ .def("setPluginSetting", &IModInterface::setPluginSetting, "plugin_name"_a,
+ "key"_a, "value"_a)
+ .def("clearPluginSettings", &IModInterface::clearPluginSettings,
+ "plugin_name"_a);
+ }
+
+ void add_modrepository_classes(py::module_ m)
+ {
+ py::class_<IModRepositoryBridge> iModRepositoryBridge(m,
+ "IModRepositoryBridge");
+ iModRepositoryBridge
+ .def("requestDescription", &IModRepositoryBridge::requestDescription,
+ "game_name"_a, "mod_id"_a, "user_data"_a)
+ .def("requestFiles", &IModRepositoryBridge::requestFiles, "game_name"_a,
+ "mod_id"_a, "user_data"_a)
+ .def("requestFileInfo", &IModRepositoryBridge::requestFileInfo,
+ "game_name"_a, "mod_id"_a, "file_id"_a, "user_data"_a)
+ .def("requestDownloadURL", &IModRepositoryBridge::requestDownloadURL,
+ "game_name"_a, "mod_id"_a, "file_id"_a, "user_data"_a)
+ .def("requestToggleEndorsement",
+ &IModRepositoryBridge::requestToggleEndorsement, "game_name"_a,
+ "mod_id"_a, "mod_version"_a, "endorse"_a, "user_data"_a);
+
+ py::qt::add_qt_delegate<QObject>(iModRepositoryBridge, "_object");
+
+ py::class_<ModRepositoryFileInfo>(m, "ModRepositoryFileInfo")
+ .def(py::init<const ModRepositoryFileInfo&>(), "other"_a)
+ .def(py::init<QString, int, int>(), "game_name"_a = "", "mod_id"_a = 0,
+ "file_id"_a = 0)
+ .def("__str__", &ModRepositoryFileInfo::toString)
+ .def_static("createFromJson", &ModRepositoryFileInfo::createFromJson,
+ "data"_a)
+ .def_readwrite("name", &ModRepositoryFileInfo::name)
+ .def_readwrite("uri", &ModRepositoryFileInfo::uri)
+ .def_readwrite("description", &ModRepositoryFileInfo::description)
+ .def_readwrite("version", &ModRepositoryFileInfo::version)
+ .def_readwrite("newestVersion", &ModRepositoryFileInfo::newestVersion)
+ .def_readwrite("categoryID", &ModRepositoryFileInfo::categoryID)
+ .def_readwrite("modName", &ModRepositoryFileInfo::modName)
+ .def_readwrite("gameName", &ModRepositoryFileInfo::gameName)
+ .def_readwrite("modID", &ModRepositoryFileInfo::modID)
+ .def_readwrite("fileID", &ModRepositoryFileInfo::fileID)
+ .def_readwrite("fileSize", &ModRepositoryFileInfo::fileSize)
+ .def_readwrite("fileName", &ModRepositoryFileInfo::fileName)
+ .def_readwrite("fileCategory", &ModRepositoryFileInfo::fileCategory)
+ .def_readwrite("fileTime", &ModRepositoryFileInfo::fileTime)
+ .def_readwrite("repository", &ModRepositoryFileInfo::repository)
+ .def_readwrite("userData", &ModRepositoryFileInfo::userData)
+ .def_readwrite("author", &ModRepositoryFileInfo::author)
+ .def_readwrite("uploader", &ModRepositoryFileInfo::uploader)
+ .def_readwrite("uploaderUrl", &ModRepositoryFileInfo::uploaderUrl);
+ }
+
+ void add_guessedstring_classes(py::module_ m)
+ {
+ py::enum_<MOBase::EGuessQuality>(m, "GuessQuality")
+ .value("INVALID", MOBase::GUESS_INVALID)
+ .value("FALLBACK", MOBase::GUESS_FALLBACK)
+ .value("GOOD", MOBase::GUESS_GOOD)
+ .value("META", MOBase::GUESS_META)
+ .value("PRESET", MOBase::GUESS_PRESET)
+ .value("USER", MOBase::GUESS_USER);
+
+ py::class_<MOBase::GuessedValue<QString>>(m, "GuessedString")
+ .def(py::init<>())
+ .def(py::init<QString const&, EGuessQuality>(), "value"_a,
+ "quality"_a = EGuessQuality::GUESS_USER)
+ .def("update",
+ py::overload_cast<const QString&>(&GuessedValue<QString>::update),
+ "value"_a)
+ .def("update",
+ py::overload_cast<const QString&, EGuessQuality>(
+ &GuessedValue<QString>::update),
+ "value"_a, "quality"_a)
+
+ // Methods to simulate the assignment operator:
+ .def("reset",
+ [](GuessedValue<QString>* gv) {
+ *gv = GuessedValue<QString>();
+ return gv;
+ })
+ .def(
+ "reset",
+ [](GuessedValue<QString>* gv, const QString& value, EGuessQuality eq) {
+ *gv = GuessedValue<QString>(value, eq);
+ return gv;
+ },
+ "value"_a, "quality"_a)
+ .def(
+ "reset",
+ [](GuessedValue<QString>* gv, const GuessedValue<QString>& other) {
+ *gv = other;
+ return gv;
+ },
+ "other"_a)
+
+ // use an intermediate lambda because we cannot have a function with a
+ // non-const reference in Python - in Python, the function should returned a
+ // bool or the modified value
+ .def(
+ "setFilter",
+ [](GuessedValue<QString>* gv,
+ std::function<std::variant<QString, bool>(QString const&)> fn) {
+ gv->setFilter([fn](QString& s) {
+ auto ret = fn(s);
+ return std::visit(
+ [&s](auto v) {
+ if constexpr (std::is_same_v<decltype(v), QString>) {
+ s = v;
+ return true;
+ }
+ else if constexpr (std::is_same_v<decltype(v), bool>) {
+ return v;
+ }
+ },
+ ret);
+ });
+ },
+ "filter"_a)
+
+ // this makes a copy in python but it more practical than
+ // exposing an iterator
+ .def("variants", &GuessedValue<QString>::variants)
+ .def("__str__", &MOBase::GuessedValue<QString>::operator const QString&);
+
+ // implicit conversion from QString - this allows passing Python string to
+ // function expecting GuessedValue<QString>
+ py::implicitly_convertible<QString, GuessedValue<QString>>();
+ }
+
+ void add_ipluginlist_classes(py::module_ m)
+ {
+ py::enum_<IPluginList::PluginState>(m, "PluginState", py::arithmetic())
+ .value("missing", IPluginList::STATE_MISSING)
+ .value("inactive", IPluginList::STATE_INACTIVE)
+ .value("active", IPluginList::STATE_ACTIVE)
+
+ .value("MISSING", IPluginList::STATE_MISSING)
+ .value("INACTIVE", IPluginList::STATE_INACTIVE)
+ .value("ACTIVE", IPluginList::STATE_ACTIVE);
+
+ py::class_<IPluginList>(m, "IPluginList")
+ .def("state", &IPluginList::state, "name"_a)
+ .def("priority", &IPluginList::priority, "name"_a)
+ .def("setPriority", &IPluginList::setPriority, "name"_a, "priority"_a)
+ .def("loadOrder", &IPluginList::loadOrder, "name"_a)
+ .def("masters", &IPluginList::masters, "name"_a)
+ .def("origin", &IPluginList::origin, "name"_a)
+ .def("onRefreshed", &IPluginList::onRefreshed, "callback"_a)
+ .def("onPluginMoved", &IPluginList::onPluginMoved, "callback"_a)
+
+ .def("isMasterFlagged", &IPluginList::isMasterFlagged, "name"_a)
+ .def("hasMasterExtension", &IPluginList::hasMasterExtension, "name"_a)
+ .def("isMediumFlagged", &IPluginList::isMediumFlagged, "name"_a)
+ .def("isLightFlagged", &IPluginList::isLightFlagged, "name"_a)
+ .def("isBlueprintFlagged", &IPluginList::isBlueprintFlagged, "name"_a)
+ .def("hasLightExtension", &IPluginList::hasLightExtension, "name"_a)
+ .def("hasNoRecords", &IPluginList::hasNoRecords, "name"_a)
+
+ .def("formVersion", &IPluginList::formVersion, "name"_a)
+ .def("headerVersion", &IPluginList::headerVersion, "name"_a)
+ .def("author", &IPluginList::author, "name"_a)
+ .def("description", &IPluginList::description, "name"_a)
+
+ // Kept but deprecated for backward compatibility:
+ .def(
+ "onPluginStateChanged",
+ [](IPluginList* modList,
+ const std::function<void(const QString&, IPluginList::PluginStates)>&
+ fn) {
+ mo2::python::show_deprecation_warning(
+ "onPluginStateChanged",
+ "onPluginStateChanged(Callable[[str, "
+ "IPluginList.PluginStates], None]) is deprecated, "
+ "use onPluginStateChanged(Callable[[Dict[str, "
+ "IPluginList.PluginStates], None]) instead.");
+ return modList->onPluginStateChanged([fn](auto const& map) {
+ for (const auto& entry : map) {
+ fn(entry.first, entry.second);
+ }
+ });
+ },
+ "callback"_a)
+ .def(
+ "isMaster",
+ [](IPluginList* modList, QString const& name) {
+ mo2::python::show_deprecation_warning(
+ "isMaster",
+ "isMaster(str) is deprecated, use isMasterFlagged() or "
+ "hasMasterExtension() instead.");
+ return modList->isMasterFlagged(name);
+ },
+ "name"_a)
+ .def("onPluginStateChanged", &MOBase::IPluginList::onPluginStateChanged,
+ "callback"_a)
+ .def("pluginNames", &MOBase::IPluginList::pluginNames)
+ .def("setState", &MOBase::IPluginList::setState, "name"_a, "state"_a)
+ .def("setLoadOrder", &MOBase::IPluginList::setLoadOrder, "loadorder"_a);
+ }
+
+ void add_imodlist_classes(py::module_ m)
+ {
+ py::enum_<IModList::ModState>(m, "ModState", py::arithmetic())
+ .value("exists", IModList::STATE_EXISTS)
+ .value("active", IModList::STATE_ACTIVE)
+ .value("essential", IModList::STATE_ESSENTIAL)
+ .value("empty", IModList::STATE_EMPTY)
+ .value("endorsed", IModList::STATE_ENDORSED)
+ .value("valid", IModList::STATE_VALID)
+ .value("alternate", IModList::STATE_ALTERNATE)
+
+ .value("EXISTS", IModList::STATE_EXISTS)
+ .value("ACTIVE", IModList::STATE_ACTIVE)
+ .value("ESSENTIAL", IModList::STATE_ESSENTIAL)
+ .value("EMPTY", IModList::STATE_EMPTY)
+ .value("ENDORSED", IModList::STATE_ENDORSED)
+ .value("VALID", IModList::STATE_VALID)
+ .value("ALTERNATE", IModList::STATE_ALTERNATE);
+
+ py::class_<IModList>(m, "IModList")
+ .def("displayName", &MOBase::IModList::displayName, "name"_a)
+ .def("allMods", &MOBase::IModList::allMods)
+ .def("allModsByProfilePriority",
+ &MOBase::IModList::allModsByProfilePriority,
+ "profile"_a = static_cast<IProfile*>(nullptr))
+ .def("getMod", &MOBase::IModList::getMod,
+ py::return_value_policy::reference, "name"_a)
+ .def("removeMod", &MOBase::IModList::removeMod, "mod"_a)
+ .def("renameMod", &MOBase::IModList::renameMod,
+ py::return_value_policy::reference, "mod"_a, "name"_a)
+
+ .def("state", &MOBase::IModList::state, "name"_a)
+ .def("setActive",
+ py::overload_cast<QStringList const&, bool>(
+ &MOBase::IModList::setActive),
+ "names"_a, "active"_a)
+ .def("setActive",
+ py::overload_cast<QString const&, bool>(&MOBase::IModList::setActive),
+ "name"_a, "active"_a)
+ .def("priority", &MOBase::IModList::priority, "name"_a)
+ .def("setPriority", &MOBase::IModList::setPriority, "name"_a, "priority"_a)
+
+ // kept but deprecated for backward compatibility
+ .def(
+ "onModStateChanged",
+ [](IModList* modList,
+ const std::function<void(const QString&, IModList::ModStates)>& fn) {
+ mo2::python::show_deprecation_warning(
+ "onModStateChanged",
+ "onModStateChanged(Callable[[str, IModList.ModStates], None]) "
+ "is deprecated, "
+ "use onModStateChanged(Callable[[Dict[str, "
+ "IModList.ModStates], None]) instead.");
+ return modList->onModStateChanged([fn](auto const& map) {
+ for (const auto& entry : map) {
+ fn(entry.first, entry.second);
+ }
+ });
+ },
+ "callback"_a)
+
+ .def("onModInstalled", &MOBase::IModList::onModInstalled, "callback"_a)
+ .def("onModRemoved", &MOBase::IModList::onModRemoved, "callback"_a)
+ .def("onModStateChanged", &MOBase::IModList::onModStateChanged,
+ "callback"_a)
+ .def("onModMoved", &MOBase::IModList::onModMoved, "callback"_a);
+ }
+
+ void add_iorganizer_classes(py::module_ m)
+ {
+ // define INVALID_HANDLE_VALUE for startApplication, etc.
+ m.attr("INVALID_HANDLE_VALUE") = py::int_((std::uintptr_t)INVALID_HANDLE_VALUE);
+
+ py::class_<IOrganizer::FileInfo>(m, "FileInfo")
+ .def(py::init<>())
+ .def_readwrite("filePath", &IOrganizer::FileInfo::filePath)
+ .def_readwrite("archive", &IOrganizer::FileInfo::archive)
+ .def_readwrite("origins", &IOrganizer::FileInfo::origins);
+
+ py::class_<IOrganizer>(m, "IOrganizer")
+ .def("createNexusBridge", &IOrganizer::createNexusBridge,
+ py::return_value_policy::reference)
+ .def("instanceName", &IOrganizer::instanceName)
+ .def("profileName", &IOrganizer::profileName)
+ .def("profilePath", &IOrganizer::profilePath)
+ .def("downloadsPath", &IOrganizer::downloadsPath)
+ .def("overwritePath", &IOrganizer::overwritePath)
+ .def("basePath", &IOrganizer::basePath)
+ .def("modsPath", &IOrganizer::modsPath)
+ .def("appVersion",
+ [](IOrganizer& o) {
+ mo2::python::show_deprecation_warning(
+ "appVersion", "IOrganizer::appVersion() is deprecated, use "
+ "IOrganizer::version() instead.");
+#pragma warning(push)
+#pragma warning(disable : 4996)
+ return o.appVersion();
+#pragma warning(pop)
+ })
+ .def("version", &IOrganizer::version)
+ .def("createMod", &IOrganizer::createMod,
+ py::return_value_policy::reference, "name"_a)
+ .def("getGame", &IOrganizer::getGame, py::return_value_policy::reference,
+ "name"_a)
+ .def("modDataChanged", &IOrganizer::modDataChanged, "mod"_a)
+ .def("isPluginEnabled",
+ py::overload_cast<IPlugin*>(&IOrganizer::isPluginEnabled, py::const_),
+ "plugin"_a)
+ .def("isPluginEnabled",
+ py::overload_cast<QString const&>(&IOrganizer::isPluginEnabled,
+ py::const_),
+ "plugin"_a)
+ .def("pluginSetting", &IOrganizer::pluginSetting, "plugin_name"_a, "key"_a)
+ .def("setPluginSetting", &IOrganizer::setPluginSetting, "plugin_name"_a,
+ "key"_a, "value"_a)
+ .def("persistent", &IOrganizer::persistent, "plugin_name"_a, "key"_a,
+ "default"_a = QVariant())
+ .def("setPersistent", &IOrganizer::setPersistent, "plugin_name"_a, "key"_a,
+ "value"_a, "sync"_a = true)
+ .def("pluginDataPath", &IOrganizer::pluginDataPath)
+ .def("installMod", wrap_for_filepath<1>(&IOrganizer::installMod),
+ py::return_value_policy::reference, "filename"_a,
+ "name_suggestion"_a = "")
+ .def("resolvePath", wrap_for_filepath(&IOrganizer::resolvePath),
+ "filename"_a)
+ .def("listDirectories", &IOrganizer::listDirectories, "directory"_a)
+
+ // "provide multiple overloads of findFiles
+ .def(
+ "findFiles",
+ [](const IOrganizer* o, DirectoryWrapper const& p,
+ std::function<bool(QString const&)> const& f) {
+ return o->findFiles(p, f);
+ },
+ "path"_a, "filter"_a)
+
+ // in C++, it is possible to create a QStringList implicitly from
+ // a single QString, in Python is not possible with the current
+ // converters in python (and I do not think it is a good idea to
+ // have it everywhere), but here it is nice to be able to
+ // pass a single string, so we add an extra overload
+ //
+ // important: the order matters, because a Python string can be
+ // converted to a QStringList since it is a sequence of
+ // single-character strings:
+ .def(
+ "findFiles",
+ [](const IOrganizer* o, DirectoryWrapper const& p,
+ const QStringList& gf) {
+ return o->findFiles(p, gf);
+ },
+ "path"_a, "patterns"_a)
+ .def(
+ "findFiles",
+ [](const IOrganizer* o, DirectoryWrapper const& p, const QString& f) {
+ return o->findFiles(p, QStringList{f});
+ },
+ "path"_a, "pattern"_a)
+
+ .def("getFileOrigins", &IOrganizer::getFileOrigins, "filename"_a)
+ .def("findFileInfos", wrap_for_directory(&IOrganizer::findFileInfos),
+ "path"_a, "filter"_a)
+
+ .def("virtualFileTree", &IOrganizer::virtualFileTree)
+
+ .def("instanceManager", &IOrganizer::instanceManager,
+ py::return_value_policy::reference)
+ .def("downloadManager", &IOrganizer::downloadManager,
+ py::return_value_policy::reference)
+ .def("pluginList", &IOrganizer::pluginList,
+ py::return_value_policy::reference)
+ .def("modList", &IOrganizer::modList, py::return_value_policy::reference)
+ .def("executablesList", &IOrganizer::executablesList,
+ py::return_value_policy::reference)
+ .def("gameFeatures", &IOrganizer::gameFeatures,
+ py::return_value_policy::reference)
+ .def("profile", &IOrganizer::profile)
+ .def("profileNames", &IOrganizer::profileNames)
+ .def("getProfile", &IOrganizer::getProfile, "name"_a)
+
+ // custom implementation for startApplication and
+ // waitForApplication because 1) HANDLE (= void*) is not properly
+ // converted from/to python, and 2) we need to convert the by-ptr
+ // argument to a return-tuple for waitForApplication
+ .def(
+ "startApplication",
+ [](IOrganizer* o, const FileWrapper& executable,
+ const QStringList& args, const DirectoryWrapper& cwd,
+ const QString& profile, const QString& forcedCustomOverwrite,
+ bool ignoreCustomOverwrite) -> std::uintptr_t {
+ return (std::uintptr_t)o->startApplication(
+ executable, args, cwd, profile, forcedCustomOverwrite,
+ ignoreCustomOverwrite);
+ },
+ "executable"_a, "args"_a = QStringList(), "cwd"_a = "",
+ "profile"_a = "", "forcedCustomOverwrite"_a = "",
+ "ignoreCustomOverwrite"_a = false)
+ .def(
+ "waitForApplication",
+ [](IOrganizer* o, std::uintptr_t handle, bool refresh) {
+ DWORD returnCode;
+ bool result =
+ o->waitForApplication((HANDLE)handle, refresh, &returnCode);
+
+ // we force signed return code because it's probably what's expected
+ // in Python
+ return std::make_tuple(
+ result, static_cast<std::make_signed_t<DWORD>>(returnCode));
+ },
+ "handle"_a, "refresh"_a = true)
+
+ .def("refresh", &IOrganizer::refresh, "save_changes"_a = true)
+ .def("managedGame", &IOrganizer::managedGame,
+ py::return_value_policy::reference)
+
+ .def("onAboutToRun",
+ py::overload_cast<std::function<bool(QString const&, const QDir&,
+ const QString&)> const&>(
+ &IOrganizer::onAboutToRun),
+ "callback"_a)
+ .def("onAboutToRun",
+ py::overload_cast<std::function<bool(QString const&)> const&>(
+ &IOrganizer::onAboutToRun),
+ "callback"_a)
+ .def("onFinishedRun", &IOrganizer::onFinishedRun, "callback"_a)
+ .def("onUserInterfaceInitialized", &IOrganizer::onUserInterfaceInitialized,
+ "callback"_a)
+ .def("onNextRefresh", &IOrganizer::onNextRefresh, "callback"_a,
+ "immediate_if_possible"_a = true)
+ .def("onProfileCreated", &IOrganizer::onProfileCreated, "callback"_a)
+ .def("onProfileRenamed", &IOrganizer::onProfileRenamed, "callback"_a)
+ .def("onProfileRemoved", &IOrganizer::onProfileRemoved, "callback"_a)
+ .def("onProfileChanged", &IOrganizer::onProfileChanged, "callback"_a)
+
+ .def("onPluginSettingChanged", &IOrganizer::onPluginSettingChanged,
+ "callback"_a)
+ .def(
+ "onPluginEnabled",
+ [](IOrganizer* o, std::function<void(const IPlugin*)> const& func) {
+ o->onPluginEnabled(func);
+ },
+ "callback"_a)
+ .def(
+ "onPluginEnabled",
+ [](IOrganizer* o, QString const& name,
+ std::function<void()> const& func) {
+ o->onPluginEnabled(name, func);
+ },
+ "name"_a, "callback"_a)
+ .def(
+ "onPluginDisabled",
+ [](IOrganizer* o, std::function<void(const IPlugin*)> const& func) {
+ o->onPluginDisabled(func);
+ },
+ "callback"_a)
+ .def(
+ "onPluginDisabled",
+ [](IOrganizer* o, QString const& name,
+ std::function<void()> const& func) {
+ o->onPluginDisabled(name, func);
+ },
+ "name"_a, "callback"_a)
+
+ // DEPRECATED:
+ .def(
+ "getMod",
+ [](IOrganizer* o, QString const& name) {
+ mo2::python::show_deprecation_warning(
+ "getMod", "IOrganizer::getMod(str) is deprecated, use "
+ "IModList::getMod(str) instead.");
+ return o->modList()->getMod(name);
+ },
+ py::return_value_policy::reference, "name"_a)
+ .def(
+ "removeMod",
+ [](IOrganizer* o, IModInterface* mod) {
+ mo2::python::show_deprecation_warning(
+ "removeMod",
+ "IOrganizer::removeMod(IModInterface) is deprecated, use "
+ "IModList::removeMod(IModInterface) instead.");
+ return o->modList()->removeMod(mod);
+ },
+ "mod"_a)
+ .def("modsSortedByProfilePriority",
+ [](IOrganizer* o) {
+ mo2::python::show_deprecation_warning(
+ "modsSortedByProfilePriority",
+ "IOrganizer::modsSortedByProfilePriority() is deprecated, use "
+ "IModList::allModsByProfilePriority() instead.");
+ return o->modList()->allModsByProfilePriority();
+ })
+ .def(
+ "refreshModList",
+ [](IOrganizer* o, bool s) {
+ mo2::python::show_deprecation_warning(
+ "refreshModList",
+ "IOrganizer::refreshModList(bool) is deprecated, use "
+ "IOrganizer::refresh(bool) instead.");
+ o->refresh(s);
+ },
+ "save_changes"_a = true)
+ .def(
+ "onModInstalled",
+ [](IOrganizer* organizer,
+ const std::function<void(QString const&)>& func) {
+ mo2::python::show_deprecation_warning(
+ "onModInstalled",
+ "IOrganizer::onModInstalled(Callable[[str], None]) is "
+ "deprecated, "
+ "use IModList::onModInstalled(Callable[[IModInterface], None]) "
+ "instead.");
+ return organizer->modList()->onModInstalled(
+ [func](MOBase::IModInterface* m) {
+ func(m->name());
+ });
+ ;
+ },
+ "callback"_a)
+
+ .def_static("getPluginDataPath", &IOrganizer::getPluginDataPath);
+ }
+
+ void add_iinstance_manager_classes(py::module_ m)
+ {
+ py::class_<IInstance, std::shared_ptr<IInstance>>(m, "IInstance")
+ .def("displayName", &IInstance::displayName)
+ .def("gameName", &IInstance::gameName)
+ .def("gameDirectory", &IInstance::gameDirectory)
+ .def("isPortable", &IInstance::isPortable);
+
+ py::class_<IInstanceManager>(m, "IInstanceManager")
+ .def("currentInstance", &IInstanceManager::currentInstance)
+ .def("globalInstancePaths", &IInstanceManager::globalInstancePaths)
+ .def("getGlobalInstance", &IInstanceManager::getGlobalInstance);
+ }
+
+ void add_idownload_manager_classes(py::module_ m)
+ {
+ py::class_<IDownloadManager>(m, "IDownloadManager")
+ .def("startDownloadURLs", &IDownloadManager::startDownloadURLs, "urls"_a)
+ .def("startDownloadNexusFile", &IDownloadManager::startDownloadNexusFile,
+ "mod_id"_a, "file_id"_a)
+ .def("startDownloadNexusFileForGame",
+ &IDownloadManager::startDownloadNexusFileForGame, "game_name"_a,
+ "mod_id"_a, "file_id"_a)
+ .def("downloadPath", &IDownloadManager::downloadPath, "id"_a)
+ .def("onDownloadComplete", &IDownloadManager::onDownloadComplete,
+ "callback"_a)
+ .def("onDownloadPaused", &IDownloadManager::onDownloadPaused, "callback"_a)
+ .def("onDownloadFailed", &IDownloadManager::onDownloadFailed, "callback"_a)
+ .def("onDownloadRemoved", &IDownloadManager::onDownloadRemoved,
+ "callback"_a);
+ }
+
+ void add_iinstallation_manager_classes(py::module_ m)
+ {
+ // add this here to get proper typing
+ py::enum_<IPluginInstaller::EInstallResult>(m, "InstallResult")
+ .value("SUCCESS", IPluginInstaller::RESULT_SUCCESS)
+ .value("FAILED", IPluginInstaller::RESULT_FAILED)
+ .value("CANCELED", IPluginInstaller::RESULT_CANCELED)
+ .value("MANUAL_REQUESTED", IPluginInstaller::RESULT_MANUALREQUESTED)
+ .value("NOT_ATTEMPTED", IPluginInstaller::RESULT_NOTATTEMPTED);
+
+ py::class_<IInstallationManager>(m, "IInstallationManager")
+ .def("getSupportedExtensions",
+ &IInstallationManager::getSupportedExtensions)
+ .def("extractFile", &IInstallationManager::extractFile, "entry"_a,
+ "silent"_a = false)
+ .def("extractFiles", &IInstallationManager::extractFiles, "entries"_a,
+ "silent"_a = false)
+ .def("createFile", &IInstallationManager::createFile, "entry"_a)
+
+ // return a tuple to get back the mod name and the mod ID
+ .def(
+ "installArchive",
+ [](IInstallationManager* m, GuessedValue<QString> modName,
+ FileWrapper archive, int modId) {
+ auto result = m->installArchive(modName, archive, modId);
+ return std::make_tuple(result, static_cast<QString>(modName),
+ modId);
+ },
+ "mod_name"_a, "archive"_a, "mod_id"_a = 0);
+ }
+
+ void add_basic_bindings(py::module_ m)
+ {
+ add_versioninfo_classes(m);
+ add_executable_classes(m);
+ add_guessedstring_classes(m);
+
+ add_ifiletree_bindings(m);
+
+ add_modinterface_classes(m);
+ add_modrepository_classes(m);
+
+ py::class_<PluginSetting>(m, "PluginSetting")
+ .def(py::init<const QString&, const QString&, const QVariant&>(), "key"_a,
+ "description"_a, "default_value"_a)
+ .def_readwrite("key", &PluginSetting::key)
+ .def_readwrite("description", &PluginSetting::description)
+ .def_readwrite("default_value", &PluginSetting::defaultValue);
+
+ py::class_<PluginRequirementFactory>(m, "PluginRequirementFactory")
+ // pluginDependency
+ .def_static("pluginDependency",
+ py::overload_cast<QStringList const&>(
+ &PluginRequirementFactory::pluginDependency),
+ "plugins"_a)
+ .def_static("pluginDependency",
+ py::overload_cast<QString const&>(
+ &PluginRequirementFactory::pluginDependency),
+ "plugin"_a)
+ // gameDependency
+ .def_static("gameDependency",
+ py::overload_cast<QStringList const&>(
+ &PluginRequirementFactory::gameDependency),
+ "games"_a)
+ .def_static("gameDependency",
+ py::overload_cast<QString const&>(
+ &PluginRequirementFactory::gameDependency),
+ "game"_a)
+ // diagnose
+ .def_static("diagnose", &PluginRequirementFactory::diagnose, "diagnose"_a)
+ // basic
+ .def_static("basic", &PluginRequirementFactory::basic, "checker"_a,
+ "description"_a);
+
+ py::class_<Mapping>(m, "Mapping")
+ .def(py::init<>())
+ .def(py::init([](QString src, QString dst, bool dir, bool crt) -> Mapping {
+ return {src, dst, dir, crt};
+ }),
+ "source"_a, "destination"_a, "is_directory"_a,
+ "create_target"_a = false)
+ .def_readwrite("source", &Mapping::source)
+ .def_readwrite("destination", &Mapping::destination)
+ .def_readwrite("isDirectory", &Mapping::isDirectory)
+ .def_readwrite("createTarget", &Mapping::createTarget)
+ .def("__str__", [](Mapping const& m) {
+ return std::format(L"Mapping({}, {}, {}, {})", m.source.toStdWString(),
+ m.destination.toStdWString(), m.isDirectory,
+ m.createTarget);
+ });
+
+ // must be done BEFORE imodlist because there is a default argument to a
+ // IProfile* in the modlist class
+ py::class_<IProfile, std::shared_ptr<IProfile>>(m, "IProfile")
+ .def("name", &IProfile::name)
+ .def("absolutePath", &IProfile::absolutePath)
+ .def("localSavesEnabled", &IProfile::localSavesEnabled)
+ .def("localSettingsEnabled", &IProfile::localSettingsEnabled)
+ .def("invalidationActive",
+ [](const IProfile* p) {
+ bool supported;
+ bool active = p->invalidationActive(&supported);
+ return std::make_tuple(active, supported);
+ })
+ .def("absoluteIniFilePath", &IProfile::absoluteIniFilePath, "inifile"_a);
+
+ add_ipluginlist_classes(m);
+ add_imodlist_classes(m);
+ add_iinstance_manager_classes(m);
+ add_idownload_manager_classes(m);
+ add_iinstallation_manager_classes(m);
+ add_iorganizer_classes(m);
+ }
+
+} // namespace mo2::python