2022-04-21 23:57:05 +02:00
#include "pythonrunner.h"
#pragma warning( disable : 4100 )
#pragma warning( disable : 4996 )
#include <idownloadmanager.h>
#include <ifiletree.h>
#include <iinstallationmanager.h>
#include <imodinterface.h>
#include <imodlist.h>
#include <imodrepositorybridge.h>
#include <iplugin.h>
#include <iplugingame.h>
#include <iplugininstaller.h>
#include <ipluginlist.h>
#include <iplugintool.h>
#include <iprofile.h>
#include <log.h>
#include <report.h>
#include <utility.h>
#include <Windows.h>
#include <QDir>
#include <QFile>
#include <QCoreApplication>
#include <QWidget>
#include <variant>
#include <tuple>
#ifndef Q_MOC_RUN
#include <pybind11/pybind11.h>
#include <pybind11/embed.h>
// #include <boost/python.hpp>
// #include <boost/mp11.hpp>
#endif
#include "converters.h"
#include "error.h"
// #include "gamefeatureswrappers.h"
// #include "proxypluginwrappers.h"
2022-04-18 22:40:36 +02:00
#include "pyfiletree.h"
#include "pythonutils.h"
2022-04-21 23:57:05 +02:00
// #include "shared_ptr_converter.h"
// #include "sipApiAccess.h"
// #include "tuple_helper.h"
// #include "uibasewrappers.h"
// #include "variant_helper.h"
// #include "widgets.h"
using namespace MOBase ;
// namespace bpy = boost::python;
// namespace mp11 = boost::mp11;
namespace py = pybind11 ;
/**
2022-04-18 22:40:36 +02:00
* This macro should be used within a py::class_ declaration and will define two
2022-04-21 23:57:05 +02:00
* methods: __getattr__ and Name, where Name will simply return the object as a QClass*
* object, while __getattr__ will delegate to the underlying QClass object when required.
*
* This allow access to Qt interface for object exposed using boost::python (e.g., signals,
* methods from QObject or QWidget, etc.).
*/
#define Q_DELEGATE(Class, QClass, Name) \
2022-04-18 22:40:36 +02:00
.def(Name, +[](Class* w) -> QClass* { return w; }, py::return_value_policy<py::reference_existing_object>()) \
.def("__getattr__", +[](Class* w, py::str str) -> py::object { \
return py::object{ (QClass*)w }.attr(str); \
2022-04-21 23:57:05 +02:00
})
PYBIND11_MODULE ( mobase , m )
{
py :: module_ :: import ( "PyQt5.QtCore" );
py :: module_ :: import ( "PyQt5.QtWidgets" );
py :: detail :: type_caster < QString > t1 ;
py :: detail :: type_caster < QVariant > t2 ;
m . def ( "testQStringList" , []( QStringList const & list ) {
QStringList res = list ;
for ( QString & value : res ) {
value = value + "_CPP" ;
}
return res ;
});
m . def ( "testQMap1" , []( QMap < QString , QString > const & map ) {
QMap < QString , int > res ;
for ( auto it = map . begin (); it != map . end (); ++ it ) {
res [ it . key ()] = it . value (). size ();
}
return res ;
});
m . def ( "testQMap2" , []( QMap < QString , int > const & map ) {
QMap < QString , QString > res ;
for ( auto it = map . begin (); it != map . end (); ++ it ) {
res [ it . key ()] = QString :: number ( it . value ());
}
return res ;
});
m . def ( "testEnum" , []( QMessageBox :: StandardButton button ) {
return py :: make_tuple ( button , QMessageBox :: Icon :: Information );
});
m . def ( "testPixmap" , []( QPixmap const & pixmap ) {
return pixmap . size ();
});
// utils::register_qstring_converter();
// utils::register_qvariant_converter();
// utils::register_qclass_converter<QObject>();
// utils::register_qclass_converter<QDateTime>();
// utils::register_qclass_converter<QDir>();
// utils::register_qclass_converter<QFileInfo>();
// utils::register_qclass_converter<QWidget>();
// utils::register_qclass_converter<QMainWindow>();
// utils::register_qclass_converter<QIcon>();
// utils::register_qclass_converter<QSize>();
// utils::register_qclass_converter<QUrl>();
// // QFlags:
// utils::register_qflags_converter<IPluginList::PluginStates>();
// utils::register_qflags_converter<IPluginGame::ProfileSettings>();
// utils::register_qflags_converter<IModList::ModStates>();
// // Enums:
// utils::register_enum_converter<QMessageBox::StandardButton>();
// utils::register_enum_converter<QMessageBox::Icon>();
// // Pointers:
2022-04-18 22:40:36 +02:00
// py::register_ptr_to_python<std::shared_ptr<FileTreeEntry>>();
// py::register_ptr_to_python<std::shared_ptr<const FileTreeEntry>>();
// py::implicitly_convertible<std::shared_ptr<FileTreeEntry>, std::shared_ptr<const FileTreeEntry>>();
// py::register_ptr_to_python<std::shared_ptr<IFileTree>>();
// py::register_ptr_to_python<std::shared_ptr<const IFileTree>>();
// py::implicitly_convertible<std::shared_ptr<IFileTree>, std::shared_ptr<const IFileTree>>();
2022-04-21 23:57:05 +02:00
// utils::shared_ptr_from_python<std::shared_ptr<const ISaveGame>>();
2022-04-18 22:40:36 +02:00
// py::register_ptr_to_python<std::shared_ptr<const ISaveGame>>();
2022-04-21 23:57:05 +02:00
// utils::shared_ptr_from_python<std::shared_ptr<const IPluginRequirement>>();
2022-04-18 22:40:36 +02:00
// py::register_ptr_to_python<std::shared_ptr<const IPluginRequirement>>();
2022-04-21 23:57:05 +02:00
// // Containers:
// utils::register_sequence_container<std::vector<int>>();
// utils::register_sequence_container<std::vector<unsigned int>>();
// utils::register_sequence_container<QList<ExecutableInfo>>();
// utils::register_sequence_container<QList<ExecutableForcedLoadSetting>>();
// utils::register_sequence_container<QList<PluginSetting>>();
// utils::register_sequence_container<QList<ModRepositoryFileInfo>>();
// utils::register_sequence_container<QStringList>();
// utils::register_sequence_container<QList<QString>>();
// utils::register_sequence_container<QList<QFileInfo>>();
// utils::register_sequence_container<QList<IOrganizer::FileInfo>>();
// utils::register_sequence_container<QList<QVariant>>(); // Required for QVariant since this is QVariantList.
// utils::register_sequence_container<std::vector<std::shared_ptr<const FileTreeEntry>>>();
// utils::register_sequence_container<std::vector<std::shared_ptr<const ISaveGame>>>();
// utils::register_sequence_container<std::vector<std::shared_ptr<const IPluginRequirement>>>();
// utils::register_sequence_container<std::vector<ModDataContent::Content>>();
// utils::register_sequence_container<std::vector<Mapping>>();
// utils::register_sequence_container<std::vector<TaskDialogButton>>();
// utils::register_set_container<std::set<QString>>();
// utils::register_associative_container<QMap<QString, QVariant>>(); // Required for QVariant since this is QVariantMap.
// utils::register_associative_container<QMap<QString, QStringList>>();
// utils::register_associative_container<std::map<QString, IModList::ModStates>>();
// utils::register_associative_container<std::map<QString, QVariant>>();
// utils::register_associative_container<IFileTree::OverwritesType>();
// utils::register_optional<std::optional<IPluginRequirement::Problem>>();
// // Tuple:
2022-04-18 22:40:36 +02:00
// py::register_tuple<std::tuple<bool, DWORD>>(); // IOrganizer::waitForApplication
// py::register_tuple<std::tuple<bool, bool>>(); // IProfile::invalidationActive
// py::register_tuple<std::tuple<IPluginInstaller::EInstallResult, QString, int>>();
// py::register_tuple<std::tuple<IPluginInstaller::EInstallResult, std::shared_ptr<IFileTree>, QString, int>>();
// py::register_tuple<std::tuple<QString, QString>>();
2022-04-21 23:57:05 +02:00
// // Variants:
2022-04-18 22:40:36 +02:00
// py::register_variant<std::variant<
2022-04-21 23:57:05 +02:00
// IPluginInstaller::EInstallResult,
// std::shared_ptr<IFileTree>,
// std::tuple<IPluginInstaller::EInstallResult, std::shared_ptr<IFileTree>, QString, int>>>();
2022-04-18 22:40:36 +02:00
// py::register_variant<std::variant<IFileTree::OverwritesType, std::size_t>>();
// py::register_variant<std::variant<QString, bool>>();
// py::register_variant<std::variant<QString, std::tuple<QString, QString>>>();
// py::register_variant<std::variant<QString, GuessedValue<QString>>>();
2022-04-21 23:57:05 +02:00
// // Functions:
// utils::register_functor_converter<void()>(); // converter for the onRefreshed-callback
// utils::register_functor_converter<void(const QString&)>();
// utils::register_functor_converter<void(int)>();
// utils::register_functor_converter<void(const QString&, unsigned int)>();
// utils::register_functor_converter<void(const QString&, int, int)>(); // converter for the onModMoved-callback and onPluginMoved callbacks
// utils::register_functor_converter<void(const std::map<QString, IModList::ModStates>&)>(); // converter for the onModStateChanged-callback (IModList)
// utils::register_functor_converter<void(const std::map<QString, IPluginList::PluginStates>&)>(); // converter for the onPluginStateChanged-callback (IPluginList)
// utils::register_functor_converter<void(const QString&, const QString&, const QVariant&, const QVariant&)>();
// utils::register_functor_converter<void(QMainWindow*)>();
2022-04-18 22:40:36 +02:00
// utils::register_functor_converter<void(IProfile*), py::pointer_wrapper<IProfile*>>();
// utils::register_functor_converter<void(IProfile*, const QString&, const QString&), py::pointer_wrapper<IProfile*>>();
// utils::register_functor_converter<void(IProfile*, IProfile*), py::pointer_wrapper<IProfile*>>();
2022-04-21 23:57:05 +02:00
// utils::register_functor_converter<bool(const QString&)>();
// utils::register_functor_converter<IFileTree::WalkReturn(const QString&, std::shared_ptr<const FileTreeEntry>)>();
// utils::register_functor_converter<bool(const IOrganizer::FileInfo&), boost::reference_wrapper<const IOrganizer::FileInfo&>>();
// utils::register_functor_converter<bool(std::shared_ptr<FileTreeEntry> const&)>();
// utils::register_functor_converter<std::variant<QString, bool>(QString const&)>();
2022-04-18 22:40:36 +02:00
// utils::register_functor_converter<void(IModInterface *), py::pointer_wrapper<IModInterface*>>();
// utils::register_functor_converter<bool(IOrganizer*), py::pointer_wrapper<IOrganizer*>>();
// utils::register_functor_converter<void(const IPlugin*), py::pointer_wrapper<const IPlugin*>>();
2022-04-21 23:57:05 +02:00
// // This one is kept for backward-compatibility while we deprecate onModStateChanged for singl mod.
// utils::register_functor_converter<void(const QString&, IModList::ModStates)>(); // converter for the onModStateChanged-callback (IModList).
// utils::register_functor_converter<void(const QString&, IPluginList::PluginStates)>(); // converter for the onPluginStateChanged-callback (IPluginList).
// //
// // Class declarations:
// //
2022-04-18 22:40:36 +02:00
// py::enum_<MOBase::VersionInfo::ReleaseType>("ReleaseType")
2022-04-21 23:57:05 +02:00
// .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)
// ;
2022-04-18 22:40:36 +02:00
// py::enum_<MOBase::VersionInfo::VersionScheme>("VersionScheme")
2022-04-21 23:57:05 +02:00
// .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)
// ;
2022-04-18 22:40:36 +02:00
// py::class_<VersionInfo>("VersionInfo")
// .def(py::init<QString, VersionInfo::VersionScheme>(
// (py::arg("value"), py::arg("scheme") = VersionInfo::SCHEME_DISCOVER)))
2022-04-21 23:57:05 +02:00
// // Note: Order of the two init<> below is important because ReleaseType is a simple enum with an
// // implicit int conversion.
2022-04-18 22:40:36 +02:00
// .def(py::init<int, int, int, int, VersionInfo::ReleaseType>(
// (py::arg("major"), "minor", "subminor", "subsubminor", py::arg("release_type") = VersionInfo::RELEASE_FINAL)))
// .def(py::init<int, int, int, VersionInfo::ReleaseType>(
// (py::arg("major"), "minor", "subminor", py::arg("release_type") = VersionInfo::RELEASE_FINAL)))
2022-04-21 23:57:05 +02:00
// .def("clear", &VersionInfo::clear)
// .def("parse", &VersionInfo::parse,
2022-04-18 22:40:36 +02:00
// (py::arg("value"), py::arg("scheme") = VersionInfo::SCHEME_DISCOVER, py::arg("is_manual") = false))
2022-04-21 23:57:05 +02:00
// .def("canonicalString", &VersionInfo::canonicalString)
2022-04-18 22:40:36 +02:00
// .def("displayString", &VersionInfo::displayString, py::arg("forced_segments") = 2)
2022-04-21 23:57:05 +02:00
// .def("isValid", &VersionInfo::isValid)
// .def("scheme", &VersionInfo::scheme)
// .def("__str__", &VersionInfo::canonicalString)
2022-04-18 22:40:36 +02:00
// .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)
2022-04-21 23:57:05 +02:00
// ;
2022-04-18 22:40:36 +02:00
// py::class_<PluginSetting>(
// "PluginSetting", py::init<const QString&, const QString&, const QVariant&>(
// (py::arg("key"), "description", "default_value")))
2022-04-21 23:57:05 +02:00
// .def_readwrite("key", &PluginSetting::key)
// .def_readwrite("description", &PluginSetting::description)
// .def_readwrite("default_value", &PluginSetting::defaultValue);
2022-04-18 22:40:36 +02:00
// py::class_<ExecutableInfo>("ExecutableInfo",
// py::init<const QString&, const QFileInfo&>((py::arg("title"), "binary")))
// .def("withArgument", &ExecutableInfo::withArgument, py::return_self<>(), py::arg("argument"))
// .def("withWorkingDirectory", &ExecutableInfo::withWorkingDirectory, py::return_self<>(), py::arg("directory"))
// .def("withSteamAppId", &ExecutableInfo::withSteamAppId, py::return_self<>(), py::arg("app_id"))
// .def("asCustom", &ExecutableInfo::asCustom, py::return_self<>())
2022-04-21 23:57:05 +02:00
// .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)
// ;
2022-04-18 22:40:36 +02:00
// py::class_<ExecutableForcedLoadSetting>("ExecutableForcedLoadSetting",
// py::init<const QString&, const QString&>((py::arg("process"), "library")))
// .def("withForced", &ExecutableForcedLoadSetting::withForced, py::return_self<>(), py::arg("forced"))
// .def("withEnabled", &ExecutableForcedLoadSetting::withEnabled, py::return_self<>(), py::arg("enabled"))
2022-04-21 23:57:05 +02:00
// .def("enabled", &ExecutableForcedLoadSetting::enabled)
// .def("forced", &ExecutableForcedLoadSetting::forced)
// .def("library", &ExecutableForcedLoadSetting::library)
// .def("process", &ExecutableForcedLoadSetting::process)
// ;
2022-04-18 22:40:36 +02:00
// py::class_<ISaveGameWrapper, py::bases<>, boost::noncopyable>("ISaveGame")
// .def("getFilepath", py::pure_virtual(&ISaveGame::getFilepath))
// .def("getCreationTime", py::pure_virtual(&ISaveGame::getCreationTime))
// .def("getName", py::pure_virtual(&ISaveGame::getName))
// .def("getSaveGroupIdentifier", py::pure_virtual(&ISaveGame::getSaveGroupIdentifier))
// .def("allFiles", py::pure_virtual(&ISaveGame::allFiles))
2022-04-21 23:57:05 +02:00
// ;
// // See Q_DELEGATE for more details.
2022-04-18 22:40:36 +02:00
// py::class_<ISaveGameInfoWidgetWrapper, py::bases<>, ISaveGameInfoWidgetWrapper*, boost::noncopyable>(
// "ISaveGameInfoWidget", py::init<py::optional<QWidget*>>(py::arg("parent")))
// .def("setSave", py::pure_virtual(&ISaveGameInfoWidget::setSave), py::arg("save"))
2022-04-21 23:57:05 +02:00
// Q_DELEGATE(ISaveGameInfoWidget, QWidget, "_widget")
// ;
// // Plugin requirements:
2022-04-18 22:40:36 +02:00
// auto iPluginRequirementClass = py::class_<
// IPluginRequirementWrapper, py::bases<>, boost::noncopyable>("IPluginRequirement");
2022-04-21 23:57:05 +02:00
// {
2022-04-18 22:40:36 +02:00
// py::scope scope = iPluginRequirementClass;
2022-04-21 23:57:05 +02:00
2022-04-18 22:40:36 +02:00
// py::class_<IPluginRequirement::Problem>("Problem",
// py::init<QString, QString>((py::arg("short_description"), py::arg("long_description") = "")))
2022-04-21 23:57:05 +02:00
// .def("shortDescription", &IPluginRequirement::Problem::shortDescription)
// .def("longDescription", &IPluginRequirement::Problem::longDescription);
// iPluginRequirementClass
2022-04-18 22:40:36 +02:00
// .def("check", py::pure_virtual(&IPluginRequirement::check), py::arg("organizer"))
2022-04-21 23:57:05 +02:00
// ;
// }
2022-04-18 22:40:36 +02:00
// py::class_<PluginRequirementFactory, boost::noncopyable>("PluginRequirementFactory")
2022-04-21 23:57:05 +02:00
// // pluginDependency
// .def("pluginDependency", +[](QStringList const& pluginNames) {
// return PluginRequirementFactory::pluginDependency(pluginNames);
2022-04-18 22:40:36 +02:00
// }, py::arg("plugins"))
2022-04-21 23:57:05 +02:00
// .def("pluginDependency", +[](QString const& pluginName) {
// return PluginRequirementFactory::pluginDependency(pluginName);
2022-04-18 22:40:36 +02:00
// }, py::arg("plugin"))
2022-04-21 23:57:05 +02:00
// .staticmethod("pluginDependency")
// // gameDependency
// .def("gameDependency", +[](QStringList const& gameNames) {
// return PluginRequirementFactory::gameDependency(gameNames);
2022-04-18 22:40:36 +02:00
// }, py::arg("games"))
2022-04-21 23:57:05 +02:00
// .def("gameDependency", +[](QString const& gameNames) {
// return PluginRequirementFactory::gameDependency(gameNames);
2022-04-18 22:40:36 +02:00
// }, py::arg("game"))
2022-04-21 23:57:05 +02:00
// .staticmethod("gameDependency")
// // diagnose
2022-04-18 22:40:36 +02:00
// .def("diagnose", &PluginRequirementFactory::diagnose, py::arg("diagnose"))
2022-04-21 23:57:05 +02:00
// .staticmethod("diagnose")
// // basic
2022-04-18 22:40:36 +02:00
// .def("basic", &PluginRequirementFactory::basic, (py::arg("checker"), "description"))
2022-04-21 23:57:05 +02:00
// .staticmethod("basic");
2022-04-18 22:40:36 +02:00
// py::class_<IOrganizer::FileInfo>("FileInfo", py::init<>())
2022-04-21 23:57:05 +02:00
// .add_property("filePath",
// +[](const IOrganizer::FileInfo& info) { return info.filePath; },
// +[](IOrganizer::FileInfo& info, QString value) { info.filePath = value; })
// .add_property("archive",
// +[](const IOrganizer::FileInfo& info) { return info.archive; },
// +[](IOrganizer::FileInfo& info, QString value) { info.archive = value; })
// .add_property("origins",
// +[](const IOrganizer::FileInfo& info) { return info.origins; },
// +[](IOrganizer::FileInfo& info, QStringList value) { info.origins = value; })
// ;
2022-04-18 22:40:36 +02:00
// py::class_<IOrganizer, boost::noncopyable>("IOrganizer", py::no_init)
// .def("createNexusBridge", &IOrganizer::createNexusBridge, py::return_value_policy<py::reference_existing_object>())
2022-04-21 23:57:05 +02:00
// .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::appVersion)
2022-04-18 22:40:36 +02:00
// .def("createMod", &IOrganizer::createMod, py::return_value_policy<py::reference_existing_object>(), py::arg("name"))
// .def("getGame", &IOrganizer::getGame, py::return_value_policy<py::reference_existing_object>(), py::arg("name"))
// .def("modDataChanged", &IOrganizer::modDataChanged, py::arg("mod"))
// .def("isPluginEnabled", +[](IOrganizer* o, IPlugin* plugin) { return o->isPluginEnabled(plugin); }, py::arg("plugin"))
// .def("isPluginEnabled", +[](IOrganizer* o, QString const& plugin) { return o->isPluginEnabled(plugin); }, py::arg("plugin"))
// .def("pluginSetting", &IOrganizer::pluginSetting, (py::arg("plugin_name"), "key"))
// .def("setPluginSetting", &IOrganizer::setPluginSetting, (py::arg("plugin_name"), "key", "value"))
// .def("persistent", &IOrganizer::persistent, (py::arg("plugin_name"), "key", py::arg("default") = QVariant()))
// .def("setPersistent", &IOrganizer::setPersistent, (py::arg("plugin_name"), "key", "value", py::arg("sync") = true))
2022-04-21 23:57:05 +02:00
// .def("pluginDataPath", &IOrganizer::pluginDataPath)
2022-04-18 22:40:36 +02:00
// .def("installMod", &IOrganizer::installMod, py::return_value_policy<py::reference_existing_object>(), (py::arg("filename"), py::arg("name_suggestion") = ""))
// .def("resolvePath", &IOrganizer::resolvePath, py::arg("filename"))
// .def("listDirectories", &IOrganizer::listDirectories, py::arg("directory"))
2022-04-21 23:57:05 +02:00
// // Provide multiple overloads of findFiles:
// .def("findFiles", +[](const IOrganizer* o, QString const& p, std::function<bool(QString const&)> f) { return o->findFiles(p, f); },
2022-04-18 22:40:36 +02:00
// (py::arg("path"), "filter"))
2022-04-21 23:57:05 +02:00
// // In C++, it is possible to create a QStringList implicitly from a single QString. This 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, QString const& p, const QStringList& gf) { return o->findFiles(p, gf); },
2022-04-18 22:40:36 +02:00
// (py::arg("path"), "patterns"))
2022-04-21 23:57:05 +02:00
// .def("findFiles", +[](const IOrganizer* o, QString const& p, const QString& f) { return o->findFiles(p, QStringList{ f }); },
2022-04-18 22:40:36 +02:00
// (py::arg("path"), "pattern"))
2022-04-21 23:57:05 +02:00
2022-04-18 22:40:36 +02:00
// .def("getFileOrigins", &IOrganizer::getFileOrigins, py::arg("filename"))
// .def("findFileInfos", &IOrganizer::findFileInfos, (py::arg("path"), "filter"))
2022-04-21 23:57:05 +02:00
// .def("virtualFileTree", &IOrganizer::virtualFileTree)
2022-04-18 22:40:36 +02:00
// .def("downloadManager", &IOrganizer::downloadManager, py::return_value_policy<py::reference_existing_object>())
// .def("pluginList", &IOrganizer::pluginList, py::return_value_policy<py::reference_existing_object>())
// .def("modList", &IOrganizer::modList, py::return_value_policy<py::reference_existing_object>())
// .def("profile", &IOrganizer::profile, py::return_value_policy<py::reference_existing_object>())
2022-04-21 23:57:05 +02:00
// // 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 QString& executable, const QStringList& args, const QString& cwd, const QString& profile,
// const QString& forcedCustomOverwrite, bool ignoreCustomOverwrite) {
// return (std::uintptr_t) o->startApplication(executable, args, cwd, profile, forcedCustomOverwrite, ignoreCustomOverwrite);
2022-04-18 22:40:36 +02:00
// }, (py::arg("executable"), (py::arg("args") = QStringList()), (py::arg("cwd") = ""), (py::arg("profile") = ""),
// (py::arg("forcedCustomOverwrite") = ""), (py::arg("ignoreCustomOverwrite") = false)), py::return_value_policy<py::return_by_value>())
2022-04-21 23:57:05 +02:00
// .def("waitForApplication", +[](IOrganizer *o, std::uintptr_t handle, bool refresh) {
// DWORD returnCode;
// bool result = o->waitForApplication((HANDLE)handle, refresh, &returnCode);
// return std::make_tuple(result, returnCode);
2022-04-18 22:40:36 +02:00
// }, (py::arg("handle"), py::arg("refresh") = true))
// .def("refresh", &IOrganizer::refresh, (py::arg("save_changes") = true))
// .def("managedGame", &IOrganizer::managedGame, py::return_value_policy<py::reference_existing_object>())
2022-04-21 23:57:05 +02:00
2022-04-18 22:40:36 +02:00
// .def("onAboutToRun", &IOrganizer::onAboutToRun, py::arg("callback"))
// .def("onFinishedRun", &IOrganizer::onFinishedRun, py::arg("callback"))
// .def("onUserInterfaceInitialized", &IOrganizer::onUserInterfaceInitialized, py::arg("callback"))
// .def("onProfileCreated", &IOrganizer::onProfileCreated, py::arg("callback"))
// .def("onProfileRenamed", &IOrganizer::onProfileRenamed, py::arg("callback"))
// .def("onProfileRemoved", &IOrganizer::onProfileRemoved, py::arg("callback"))
// .def("onProfileChanged", &IOrganizer::onProfileChanged, py::arg("callback"))
2022-04-21 23:57:05 +02:00
2022-04-18 22:40:36 +02:00
// .def("onPluginSettingChanged", &IOrganizer::onPluginSettingChanged, py::arg("callback"))
2022-04-21 23:57:05 +02:00
// .def("onPluginEnabled", +[](IOrganizer* o, std::function<void(const IPlugin*)> const& func) {
// o->onPluginEnabled(func);
2022-04-18 22:40:36 +02:00
// }, py::arg("callback"))
2022-04-21 23:57:05 +02:00
// .def("onPluginEnabled", +[](IOrganizer* o, QString const& name, std::function<void()> const& func) {
// o->onPluginEnabled(name, func);
2022-04-18 22:40:36 +02:00
// }, (py::arg("name"), py::arg("callback")))
2022-04-21 23:57:05 +02:00
// .def("onPluginDisabled", +[](IOrganizer* o, std::function<void(const IPlugin*)> const& func) {
// o->onPluginDisabled(func);
2022-04-18 22:40:36 +02:00
// }, py::arg("callback"))
2022-04-21 23:57:05 +02:00
// .def("onPluginDisabled", +[](IOrganizer* o, QString const& name, std::function<void()> const& func) {
// o->onPluginDisabled(name, func);
2022-04-18 22:40:36 +02:00
// }, (py::arg("name"), py::arg("callback")))
2022-04-21 23:57:05 +02:00
// // DEPRECATED:
// .def("getMod", +[](IOrganizer* o, QString const& name) {
// utils::show_deprecation_warning("getMod",
// "IOrganizer::getMod(str) is deprecated, use IModList::getMod(str) instead.");
// return o->modList()->getMod(name);
2022-04-18 22:40:36 +02:00
// }, py::return_value_policy<py::reference_existing_object>(), py::arg("name"))
2022-04-21 23:57:05 +02:00
// .def("removeMod", +[](IOrganizer* o, IModInterface *mod) {
// utils::show_deprecation_warning("removeMod",
// "IOrganizer::removeMod(IModInterface) is deprecated, use IModList::removeMod(IModInterface) instead.");
// return o->modList()->removeMod(mod);
2022-04-18 22:40:36 +02:00
// }, py::arg("mod"))
2022-04-21 23:57:05 +02:00
// .def("modsSortedByProfilePriority", +[](IOrganizer* o) {
// utils::show_deprecation_warning("modsSortedByProfilePriority",
// "IOrganizer::modsSortedByProfilePriority() is deprecated, use IModList::allModsByProfilePriority() instead.");
// return o->modList()->allModsByProfilePriority();
// })
// .def("refreshModList", +[](IOrganizer* o, bool s) {
// utils::show_deprecation_warning("refreshModList",
// "IOrganizer::refreshModList(bool) is deprecated, use IOrganizer::refresh(bool) instead.");
// o->refresh(s);
2022-04-18 22:40:36 +02:00
// }, (py::arg("save_changes") = true))
2022-04-21 23:57:05 +02:00
// .def("onModInstalled", +[](IOrganizer* organizer, const std::function<void(QString const&)>& func) {
// utils::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()); });;
2022-04-18 22:40:36 +02:00
// }, py::arg("callback"))
2022-04-21 23:57:05 +02:00
// .def("getPluginDataPath", &IOrganizer::getPluginDataPath)
// .staticmethod("getPluginDataPath")
// ;
2022-04-18 22:40:36 +02:00
mo2 :: python :: add_ifiletree_bindings ( m );
2022-04-21 23:57:05 +02:00
2022-04-18 22:40:36 +02:00
py :: class_ < IProfile > ( m , "IProfile" )
2022-04-21 23:57:05 +02:00
. 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 py :: make_tuple ( active , supported );
})
. def ( "absoluteIniFilePath" , & IProfile :: absoluteIniFilePath , py :: arg ( "inifile" ))
;
2022-04-18 22:40:36 +02:00
// py::class_<IModRepositoryBridge, boost::noncopyable>("IModRepositoryBridge", py::no_init)
// .def("requestDescription", &IModRepositoryBridge::requestDescription, (py::arg("game_name"), "mod_id", "user_data"))
// .def("requestFiles", &IModRepositoryBridge::requestFiles, (py::arg("game_name"), "mod_id", "user_data"))
// .def("requestFileInfo", &IModRepositoryBridge::requestFileInfo, (py::arg("game_name"), "mod_id", "file_id", "user_data"))
// .def("requestDownloadURL", &IModRepositoryBridge::requestDownloadURL, (py::arg("game_name"), "mod_id", "file_id", "user_data"))
// .def("requestToggleEndorsement", &IModRepositoryBridge::requestToggleEndorsement, (py::arg("game_name"), "mod_id", "mod_version", "endorse", "user_data"))
2022-04-21 23:57:05 +02:00
// Q_DELEGATE(IModRepositoryBridge, QObject, "_object")
// ;
2022-04-18 22:40:36 +02:00
// py::class_<ModRepositoryFileInfo>("ModRepositoryFileInfo", py::no_init)
// .def(py::init<const ModRepositoryFileInfo &>(py::arg("other")))
// .def(py::init<py::optional<QString, int, int>>((py::arg("game_name"), "mod_id", "file_id")))
2022-04-21 23:57:05 +02:00
// .def("__str__", &ModRepositoryFileInfo::toString)
2022-04-18 22:40:36 +02:00
// .def("createFromJson", &ModRepositoryFileInfo::createFromJson, py::arg("data")).staticmethod("createFromJson")
2022-04-21 23:57:05 +02:00
// .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)
// ;
2022-04-18 22:40:36 +02:00
// py::class_<IDownloadManager, boost::noncopyable>("IDownloadManager", py::no_init)
// .def("startDownloadURLs", &IDownloadManager::startDownloadURLs, py::arg("urls"))
// .def("startDownloadNexusFile", &IDownloadManager::startDownloadNexusFile, (py::arg("mod_id"), "file_id"))
// .def("downloadPath", &IDownloadManager::downloadPath, py::arg("id"))
// .def("onDownloadComplete", &IDownloadManager::onDownloadComplete, py::arg("callback"))
// .def("onDownloadPaused", &IDownloadManager::onDownloadPaused, py::arg("callback"))
// .def("onDownloadFailed", &IDownloadManager::onDownloadFailed, py::arg("callback"))
// .def("onDownloadRemoved", &IDownloadManager::onDownloadRemoved, py::arg("callback"))
2022-04-21 23:57:05 +02:00
// ;
2022-04-18 22:40:36 +02:00
// py::class_<IInstallationManager, boost::noncopyable>("IInstallationManager", py::no_init)
2022-04-21 23:57:05 +02:00
// .def("getSupportedExtensions", &IInstallationManager::getSupportedExtensions)
2022-04-18 22:40:36 +02:00
// .def("extractFile", &IInstallationManager::extractFile, (py::arg("entry"), py::arg("silent") = false))
// .def("extractFiles", &IInstallationManager::extractFiles, (py::arg("entries"), py::arg("silent") = false))
2022-04-21 23:57:05 +02:00
// .def("createFile", +[](IInstallationManager* m, std::shared_ptr<const MOBase::FileTreeEntry> entry) {
// return m->createFile(utils::clean_shared_ptr(entry));
2022-04-18 22:40:36 +02:00
// }, py::arg("entry"))
2022-04-21 23:57:05 +02:00
// // accept both QString and GuessedValue<QString> since the conversion is not automatic in Python, and
// // return a tuple to get back the mod name and the mod ID
// .def("installArchive", +[](IInstallationManager* m, std::variant<QString, GuessedValue<QString>> modName, QString archive, int modId) {
// GuessedValue<QString> tmp;
// if (auto* p = std::get_if<QString>(&modName)) {
// tmp = *p;
// }
// else {
// tmp = std::get<GuessedValue<QString>>(modName);
// }
// auto result = m->installArchive(tmp, archive, modId);
// return std::make_tuple(result, static_cast<QString>(tmp), modId);
2022-04-18 22:40:36 +02:00
// }, (py::arg("mod_name"), "archive", py::arg("mod_id") = 0))
2022-04-21 23:57:05 +02:00
// ;
2022-04-18 22:40:36 +02:00
py :: enum_ < EndorsedState > ( m , "EndorsedState" )
2022-04-21 23:57:05 +02:00
. value ( "ENDORSED_FALSE" , EndorsedState :: ENDORSED_FALSE )
. value ( "ENDORSED_TRUE" , EndorsedState :: ENDORSED_TRUE )
. value ( "ENDORSED_UNKNOWN" , EndorsedState :: ENDORSED_UNKNOWN )
. value ( "ENDORSED_NEVER" , EndorsedState :: ENDORSED_NEVER )
;
2022-04-18 22:40:36 +02:00
py :: enum_ < TrackedState > ( m , "TrackedState" )
2022-04-21 23:57:05 +02:00
. value ( "TRACKED_FALSE" , TrackedState :: TRACKED_FALSE )
. value ( "TRACKED_TRUE" , TrackedState :: TRACKED_TRUE )
. value ( "TRACKED_UNKNOWN" , TrackedState :: TRACKED_UNKNOWN )
;
2022-04-18 22:40:36 +02:00
// py::class_<IModInterface, boost::noncopyable>("IModInterface", py::no_init)
2022-04-21 23:57:05 +02:00
// .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("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)
2022-04-18 22:40:36 +02:00
// .def("setVersion", &IModInterface::setVersion, py::arg("version"))
// .def("setNewestVersion", &IModInterface::setNewestVersion, py::arg("version"))
// .def("setIsEndorsed", &IModInterface::setIsEndorsed, py::arg("endorsed"))
// .def("setNexusID", &IModInterface::setNexusID, py::arg("nexus_id"))
// .def("addNexusCategory", &IModInterface::addNexusCategory, py::arg("category_id"))
// .def("addCategory", &IModInterface::addCategory, py::arg("name"))
// .def("removeCategory", &IModInterface::removeCategory, py::arg("name"))
// .def("setGameName", &IModInterface::setGameName, py::arg("name"))
// .def("setUrl", &IModInterface::setUrl, py::arg("url"))
// .def("pluginSetting", &IModInterface::pluginSetting, (py::arg("plugin_name"), "key", py::arg("default") = QVariant()))
// .def("pluginSettings", &IModInterface::pluginSettings, py::arg("plugin_name"))
// .def("setPluginSetting", &IModInterface::setPluginSetting, (py::arg("plugin_name"), "key", py::arg("value")))
// .def("clearPluginSettings", &IModInterface::clearPluginSettings, py::arg("plugin_name"))
2022-04-21 23:57:05 +02:00
// ;
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 )
;
2022-04-18 22:40:36 +02:00
// py::class_<MOBase::GuessedValue<QString>, boost::noncopyable>("GuessedString")
// .def(py::init<>())
// .def(py::init<QString const&, EGuessQuality>((py::arg("value"), py::arg("quality") = EGuessQuality::GUESS_USER)))
2022-04-21 23:57:05 +02:00
// .def("update",
// static_cast<GuessedValue<QString>& (GuessedValue<QString>::*)(const QString&)>(&GuessedValue<QString>::update),
2022-04-18 22:40:36 +02:00
// py::return_self<>(), py::arg("value"))
2022-04-21 23:57:05 +02:00
// .def("update",
// static_cast<GuessedValue<QString>& (GuessedValue<QString>::*)(const QString&, EGuessQuality)>(&GuessedValue<QString>::update),
2022-04-18 22:40:36 +02:00
// py::return_self<>(), (py::arg("value"), "quality"))
2022-04-21 23:57:05 +02:00
// // Methods to simulate the assignment operator:
// .def("reset", +[](GuessedValue<QString>* gv) {
2022-04-18 22:40:36 +02:00
// *gv = GuessedValue<QString>(); }, py::return_self<>())
2022-04-21 23:57:05 +02:00
// .def("reset", +[](GuessedValue<QString>* gv, const QString& value, EGuessQuality eq) {
2022-04-18 22:40:36 +02:00
// *gv = GuessedValue<QString>(value, eq); }, py::return_self<>(), (py::arg("value"), "quality"))
2022-04-21 23:57:05 +02:00
// .def("reset", +[](GuessedValue<QString>* gv, const GuessedValue<QString>& other) {
2022-04-18 22:40:36 +02:00
// *gv = other; }, py::return_self<>(), py::arg("other"))
2022-04-21 23:57:05 +02:00
// // Use an intermediate lambda to avoid having to register the std::function conversion:
// .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);
// });
2022-04-18 22:40:36 +02:00
// }, py::arg("filter"))
2022-04-21 23:57:05 +02:00
// // This makes a copy in python but it more practical than exposing an iterator:
2022-04-18 22:40:36 +02:00
// .def("variants", &GuessedValue<QString>::variants, py::return_value_policy<py::copy_const_reference>())
// .def("__str__", &MOBase::GuessedValue<QString>::operator const QString&, py::return_value_policy<py::copy_const_reference>())
2022-04-21 23:57:05 +02:00
// ;
py :: enum_ < IPluginList :: PluginState > ( m , "PluginState" )
. 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 )
;
m . def ( "testFlags1" , []( IPluginList :: PluginStates states ) {
std :: vector < std :: string > res ;
if ( states . testFlag ( IPluginList :: STATE_MISSING )) {
res . push_back ( "missing" );
}
if ( states . testFlag ( IPluginList :: STATE_INACTIVE )) {
res . push_back ( "inactive" );
}
if ( states . testFlag ( IPluginList :: STATE_ACTIVE )) {
res . push_back ( "active" );
}
return res ;
});
m . def ( "testFlags2" , []( QStringList const & states ) {
IPluginList :: PluginStates res ;
if ( states . contains ( "missing" )) {
res |= IPluginList :: STATE_MISSING ;
}
if ( states . contains ( "inactive" )) {
res |= IPluginList :: STATE_INACTIVE ;
}
if ( states . contains ( "active" )) {
res |= IPluginList :: STATE_ACTIVE ;
}
return res ;
});
2022-04-18 22:40:36 +02:00
// py::class_<IPluginList, boost::noncopyable>("IPluginList", py::no_init)
// .def("state", &MOBase::IPluginList::state, py::arg("name"))
// .def("priority", &MOBase::IPluginList::priority, py::arg("name"))
// .def("setPriority", &MOBase::IPluginList::setPriority, (py::arg("name"), "priority"))
// .def("loadOrder", &MOBase::IPluginList::loadOrder, py::arg("name"))
// .def("isMaster", &MOBase::IPluginList::isMaster, py::arg("name"))
// .def("masters", &MOBase::IPluginList::masters, py::arg("name"))
// .def("origin", &MOBase::IPluginList::origin, py::arg("name"))
// .def("onRefreshed", &MOBase::IPluginList::onRefreshed, py::arg("callback"))
// .def("onPluginMoved", &MOBase::IPluginList::onPluginMoved, py::arg("callback"))
2022-04-21 23:57:05 +02:00
// // Kept but deprecated for backward compatibility:
// .def("onPluginStateChanged", +[](IPluginList* modList, const std::function<void(const QString&, IPluginList::PluginStates)>& fn) {
// utils::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);
// }
// });
2022-04-18 22:40:36 +02:00
// }, py::arg("callback"))
// .def("onPluginStateChanged", &MOBase::IPluginList::onPluginStateChanged, py::arg("callback"))
2022-04-21 23:57:05 +02:00
// .def("pluginNames", &MOBase::IPluginList::pluginNames)
2022-04-18 22:40:36 +02:00
// .def("setState", &MOBase::IPluginList::setState, (py::arg("name"), "state"))
// .def("setLoadOrder", &MOBase::IPluginList::setLoadOrder, py::arg("loadorder"))
2022-04-21 23:57:05 +02:00
// ;
2022-04-18 22:40:36 +02:00
// py::enum_<IModList::ModState>("ModState")
2022-04-21 23:57:05 +02:00
// .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)
// ;
2022-04-18 22:40:36 +02:00
// py::class_<IModList, boost::noncopyable>("IModList", py::no_init)
// .def("displayName", &MOBase::IModList::displayName, py::arg("name"))
2022-04-21 23:57:05 +02:00
// .def("allMods", &MOBase::IModList::allMods)
2022-04-18 22:40:36 +02:00
// .def("allModsByProfilePriority", &MOBase::IModList::allModsByProfilePriority, py::arg("profile") = py::ptr((IProfile*)nullptr))
2022-04-21 23:57:05 +02:00
2022-04-18 22:40:36 +02:00
// .def("getMod", &MOBase::IModList::getMod, py::return_value_policy<py::reference_existing_object>(), py::arg("name"))
// .def("removeMod", &MOBase::IModList::removeMod, py::arg("mod"))
// .def("renameMod", &MOBase::IModList::renameMod, py::return_value_policy<py::reference_existing_object>(), (py::arg("mod"), py::arg("name")))
2022-04-21 23:57:05 +02:00
2022-04-18 22:40:36 +02:00
// .def("state", &MOBase::IModList::state, py::arg("name"))
2022-04-21 23:57:05 +02:00
// .def("setActive",
2022-04-18 22:40:36 +02:00
// static_cast<int(IModList::*)(QStringList const&, bool)>(&MOBase::IModList::setActive), (py::arg("names"), "active"))
2022-04-21 23:57:05 +02:00
// .def("setActive",
2022-04-18 22:40:36 +02:00
// static_cast<bool(IModList::*)(QString const&, bool)>(&MOBase::IModList::setActive), (py::arg("name"), "active"))
// .def("priority", &MOBase::IModList::priority, py::arg("name"))
// .def("setPriority", &MOBase::IModList::setPriority, (py::arg("name"), "priority"))
2022-04-21 23:57:05 +02:00
// // Kept but deprecated for backward compatibility:
// .def("onModStateChanged", +[](IModList* modList, const std::function<void(const QString&, IModList::ModStates)>& fn) {
// utils::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);
// }
// });
2022-04-18 22:40:36 +02:00
// }, py::arg("callback"))
2022-04-21 23:57:05 +02:00
2022-04-18 22:40:36 +02:00
// .def("onModInstalled", &MOBase::IModList::onModInstalled, py::arg("callback"))
// .def("onModRemoved", &MOBase::IModList::onModRemoved, py::arg("callback"))
// .def("onModStateChanged", &MOBase::IModList::onModStateChanged, py::arg("callback"))
// .def("onModMoved", &MOBase::IModList::onModMoved, py::arg("callback"))
2022-04-21 23:57:05 +02:00
// ;
// // Note: localizedName, master, requirements and enabledByDefault have to go in all the plugin wrappers declaration,
// // since the default functions are specific to each wrapper, otherwise in turns into an
// // infinite recursion mess.
2022-04-18 22:40:36 +02:00
// py::class_<IPluginWrapper, boost::noncopyable>("IPlugin")
// .def("init", py::pure_virtual(&MOBase::IPlugin::init), py::arg("organizer"))
// .def("name", py::pure_virtual(&MOBase::IPlugin::name))
2022-04-21 23:57:05 +02:00
// .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginWrapper::localizedName_Default)
// .def("master", &MOBase::IPlugin::master, &IPluginWrapper::master_Default)
2022-04-18 22:40:36 +02:00
// .def("author", py::pure_virtual(&MOBase::IPlugin::author))
// .def("description", py::pure_virtual(&MOBase::IPlugin::description))
// .def("version", py::pure_virtual(&MOBase::IPlugin::version))
2022-04-21 23:57:05 +02:00
// .def("requirements", &MOBase::IPlugin::requirements, &IPluginWrapper::requirements_Default)
2022-04-18 22:40:36 +02:00
// .def("settings", py::pure_virtual(&MOBase::IPlugin::settings))
2022-04-21 23:57:05 +02:00
// .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, &IPluginWrapper::enabledByDefault_Default)
// ;
2022-04-18 22:40:36 +02:00
// py::class_<IPluginDiagnoseWrapper, py::bases<IPlugin>, boost::noncopyable>("IPluginDiagnose")
2022-04-21 23:57:05 +02:00
// .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginDiagnoseWrapper::localizedName_Default)
// .def("master", &MOBase::IPlugin::master, &IPluginDiagnoseWrapper::master_Default)
// .def("requirements", &MOBase::IPlugin::requirements, &IPluginDiagnoseWrapper::requirements_Default)
// .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, &IPluginDiagnoseWrapper::enabledByDefault_Default)
2022-04-18 22:40:36 +02:00
// .def("activeProblems", py::pure_virtual(&MOBase::IPluginDiagnose::activeProblems))
// .def("shortDescription", py::pure_virtual(&MOBase::IPluginDiagnose::shortDescription), py::arg("key"))
// .def("fullDescription", py::pure_virtual(&MOBase::IPluginDiagnose::fullDescription), py::arg("key"))
// .def("hasGuidedFix", py::pure_virtual(&MOBase::IPluginDiagnose::hasGuidedFix), py::arg("key"))
// .def("startGuidedFix", py::pure_virtual(&MOBase::IPluginDiagnose::startGuidedFix), py::arg("key"))
2022-04-21 23:57:05 +02:00
// .def("_invalidate", &IPluginDiagnoseWrapper::invalidate)
// ;
2022-04-18 22:40:36 +02:00
// py::class_<Mapping>("Mapping", py::init<>())
// .def("__init__", py::make_constructor(+[](QString src, QString dst, bool dir, bool crt) -> Mapping* {
2022-04-21 23:57:05 +02:00
// return new Mapping{ src, dst, dir, crt };
2022-04-18 22:40:36 +02:00
// }, py::default_call_policies(),
// (py::arg("source"), py::arg("destination"), py::arg("is_directory"), py::arg("create_target") = false)))
2022-04-21 23:57:05 +02:00
// .def_readwrite("source", &Mapping::source)
// .def_readwrite("destination", &Mapping::destination)
// .def_readwrite("isDirectory", &Mapping::isDirectory)
// .def_readwrite("createTarget", &Mapping::createTarget)
// .def("__str__", +[](Mapping * m) {
// return fmt::format(L"Mapping({}, {}, {}, {})", m->source.toStdWString(), m->destination.toStdWString(), m->isDirectory, m->createTarget);
// })
// ;
2022-04-18 22:40:36 +02:00
// py::class_<IPluginFileMapperWrapper, py::bases<IPlugin>, boost::noncopyable>("IPluginFileMapper")
2022-04-21 23:57:05 +02:00
// .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginFileMapperWrapper::localizedName_Default)
// .def("master", &MOBase::IPlugin::master, &IPluginFileMapperWrapper::master_Default)
// .def("requirements", &MOBase::IPlugin::requirements, &IPluginFileMapperWrapper::requirements_Default)
// .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, &IPluginFileMapperWrapper::enabledByDefault_Default)
2022-04-18 22:40:36 +02:00
// .def("mappings", py::pure_virtual(&MOBase::IPluginFileMapper::mappings))
2022-04-21 23:57:05 +02:00
// ;
2022-04-18 22:40:36 +02:00
// py::enum_<MOBase::IPluginGame::LoadOrderMechanism>("LoadOrderMechanism")
2022-04-21 23:57:05 +02:00
// .value("FileTime", MOBase::IPluginGame::LoadOrderMechanism::FileTime)
// .value("PluginsTxt", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt)
// .value("FILE_TIME", MOBase::IPluginGame::LoadOrderMechanism::FileTime)
// .value("PLUGINS_TXT", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt)
// ;
2022-04-18 22:40:36 +02:00
// py::enum_<MOBase::IPluginGame::SortMechanism>("SortMechanism")
2022-04-21 23:57:05 +02:00
// .value("NONE", MOBase::IPluginGame::SortMechanism::NONE)
// .value("MLOX", MOBase::IPluginGame::SortMechanism::MLOX)
// .value("BOSS", MOBase::IPluginGame::SortMechanism::BOSS)
// .value("LOOT", MOBase::IPluginGame::SortMechanism::LOOT)
// ;
// // This doesn't actually do the conversion, but might be convenient for accessing the names for enum bits
2022-04-18 22:40:36 +02:00
// py::enum_<MOBase::IPluginGame::ProfileSetting>("ProfileSetting")
2022-04-21 23:57:05 +02:00
// .value("mods", MOBase::IPluginGame::MODS)
// .value("configuration", MOBase::IPluginGame::CONFIGURATION)
// .value("savegames", MOBase::IPluginGame::SAVEGAMES)
// .value("preferDefaults", MOBase::IPluginGame::PREFER_DEFAULTS)
// .value("MODS", MOBase::IPluginGame::MODS)
// .value("CONFIGURATION", MOBase::IPluginGame::CONFIGURATION)
// .value("SAVEGAMES", MOBase::IPluginGame::SAVEGAMES)
// .value("PREFER_DEFAULTS", MOBase::IPluginGame::PREFER_DEFAULTS)
// ;
2022-04-18 22:40:36 +02:00
// py::class_<IPluginGameWrapper, py::bases<IPlugin>, boost::noncopyable>("IPluginGame")
2022-04-21 23:57:05 +02:00
// .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginGameWrapper::localizedName_Default)
// .def("master", &MOBase::IPlugin::master, &IPluginGameWrapper::master_Default)
2022-04-18 22:40:36 +02:00
// .def("detectGame", py::pure_virtual(&MOBase::IPluginGame::detectGame))
// .def("gameName", py::pure_virtual(&MOBase::IPluginGame::gameName))
// .def("initializeProfile", py::pure_virtual(&MOBase::IPluginGame::initializeProfile), (py::arg("directory"), "settings"))
// .def("listSaves", py::pure_virtual(&MOBase::IPluginGame::listSaves), py::arg("folder"))
// .def("isInstalled", py::pure_virtual(&MOBase::IPluginGame::isInstalled))
// .def("gameIcon", py::pure_virtual(&MOBase::IPluginGame::gameIcon))
// .def("gameDirectory", py::pure_virtual(&MOBase::IPluginGame::gameDirectory))
// .def("dataDirectory", py::pure_virtual(&MOBase::IPluginGame::dataDirectory))
// .def("setGamePath", py::pure_virtual(&MOBase::IPluginGame::setGamePath), py::arg("path"))
// .def("documentsDirectory", py::pure_virtual(&MOBase::IPluginGame::documentsDirectory))
// .def("savesDirectory", py::pure_virtual(&MOBase::IPluginGame::savesDirectory))
// .def("executables", py::pure_virtual(&MOBase::IPluginGame::executables))
// .def("executableForcedLoads", py::pure_virtual(&MOBase::IPluginGame::executableForcedLoads))
// .def("steamAPPId", py::pure_virtual(&MOBase::IPluginGame::steamAPPId))
// .def("primaryPlugins", py::pure_virtual(&MOBase::IPluginGame::primaryPlugins))
// .def("gameVariants", py::pure_virtual(&MOBase::IPluginGame::gameVariants))
// .def("setGameVariant", py::pure_virtual(&MOBase::IPluginGame::setGameVariant), py::arg("variant"))
// .def("binaryName", py::pure_virtual(&MOBase::IPluginGame::binaryName))
// .def("gameShortName", py::pure_virtual(&MOBase::IPluginGame::gameShortName))
// .def("primarySources", py::pure_virtual(&MOBase::IPluginGame::primarySources))
// .def("validShortNames", py::pure_virtual(&MOBase::IPluginGame::validShortNames))
// .def("gameNexusName", py::pure_virtual(&MOBase::IPluginGame::gameNexusName))
// .def("iniFiles", py::pure_virtual(&MOBase::IPluginGame::iniFiles))
// .def("DLCPlugins", py::pure_virtual(&MOBase::IPluginGame::DLCPlugins))
// .def("CCPlugins", py::pure_virtual(&MOBase::IPluginGame::CCPlugins))
// .def("loadOrderMechanism", py::pure_virtual(&MOBase::IPluginGame::loadOrderMechanism))
// .def("sortMechanism", py::pure_virtual(&MOBase::IPluginGame::sortMechanism))
// .def("nexusModOrganizerID", py::pure_virtual(&MOBase::IPluginGame::nexusModOrganizerID))
// .def("nexusGameID", py::pure_virtual(&MOBase::IPluginGame::nexusGameID))
// .def("looksValid", py::pure_virtual(&MOBase::IPluginGame::looksValid), py::arg("directory"))
// .def("gameVersion", py::pure_virtual(&MOBase::IPluginGame::gameVersion))
// .def("getLauncherName", py::pure_virtual(&MOBase::IPluginGame::getLauncherName))
2022-04-21 23:57:05 +02:00
// .def("featureList", +[](MOBase::IPluginGame* p) {
// // Constructing a dict from class name to actual object:
2022-04-18 22:40:36 +02:00
// py::dict dict;
2022-04-21 23:57:05 +02:00
// mp11::mp_for_each<
// // Must user pointers because mp_for_each construct object:
// mp11::mp_transform<std::add_pointer_t, MpGameFeaturesList>
// >([&](auto* pt) {
// using T = std::remove_pointer_t<decltype(pt)>;
2022-04-18 22:40:36 +02:00
// typename py::reference_existing_object::apply<T*>::type converter;
2022-04-21 23:57:05 +02:00
// // Retrieve the python class object:
2022-04-18 22:40:36 +02:00
// const py::converter::registration* registration = py::converter::registry::query(py::type_id<T>());
// py::object key = py::object(py::handle<>(py::borrowed(registration->get_class_object())));
2022-04-21 23:57:05 +02:00
// // Set the object:
2022-04-18 22:40:36 +02:00
// dict[key] = py::handle<>(converter(p->feature<T>()));
2022-04-21 23:57:05 +02:00
// });
// return dict;
// })
2022-04-18 22:40:36 +02:00
// .def("feature", +[](MOBase::IPluginGame* p, py::object clsObj) {
// py::object feature;
2022-04-21 23:57:05 +02:00
// mp11::mp_for_each<
// // Must user pointers because mp_for_each construct object:
// mp11::mp_transform<std::add_pointer_t, MpGameFeaturesList>
// >([&](auto* pt) {
// using T = std::remove_pointer_t<decltype(pt)>;
2022-04-18 22:40:36 +02:00
// typename py::reference_existing_object::apply<T*>::type converter;
2022-04-21 23:57:05 +02:00
// // Retrieve the python class object:
2022-04-18 22:40:36 +02:00
// const py::converter::registration* registration = py::converter::registry::query(py::type_id<T>());
2022-04-21 23:57:05 +02:00
// if (clsObj.ptr() == (PyObject*) registration->get_class_object()) {
2022-04-18 22:40:36 +02:00
// feature = py::object(py::handle<>(converter(p->feature<T>())));
2022-04-21 23:57:05 +02:00
// }
// });
// return feature;
2022-04-18 22:40:36 +02:00
// }, py::arg("feature_type"))
2022-04-21 23:57:05 +02:00
// ;
2022-04-18 22:40:36 +02:00
// py::enum_<MOBase::IPluginInstaller::EInstallResult>("InstallResult")
2022-04-21 23:57:05 +02:00
// .value("SUCCESS", MOBase::IPluginInstaller::RESULT_SUCCESS)
// .value("FAILED", MOBase::IPluginInstaller::RESULT_FAILED)
// .value("CANCELED", MOBase::IPluginInstaller::RESULT_CANCELED)
// .value("MANUAL_REQUESTED", MOBase::IPluginInstaller::RESULT_MANUALREQUESTED)
// .value("NOT_ATTEMPTED", MOBase::IPluginInstaller::RESULT_NOTATTEMPTED)
// ;
2022-04-18 22:40:36 +02:00
// py::class_<IPluginInstaller, py::bases<IPlugin>, boost::noncopyable>("IPluginInstaller", py::no_init)
// .def("isArchiveSupported", &IPluginInstaller::isArchiveSupported, py::arg("tree"))
2022-04-21 23:57:05 +02:00
// .def("priority", &IPluginInstaller::priority)
2022-04-18 22:40:36 +02:00
// .def("onInstallationStart", &IPluginInstaller::onInstallationStart, (py::arg("archive"), py::arg("reinstallation"), py::arg("current_mod")))
// .def("onInstallationEnd", &IPluginInstaller::onInstallationEnd, (py::arg("result"), py::arg("new_mod")))
2022-04-21 23:57:05 +02:00
// .def("isManualInstaller", &IPluginInstaller::isManualInstaller)
2022-04-18 22:40:36 +02:00
// .def("setParentWidget", &IPluginInstaller::setParentWidget, py::arg("parent"))
// .def("setInstallationManager", &IPluginInstaller::setInstallationManager, py::arg("manager"))
2022-04-21 23:57:05 +02:00
// ;
2022-04-18 22:40:36 +02:00
// py::class_<IPluginInstallerSimpleWrapper, py::bases<IPluginInstaller>, boost::noncopyable>("IPluginInstallerSimple")
// .def("onInstallationStart", &IPluginInstaller::onInstallationStart, (py::arg("archive"), py::arg("reinstallation"), py::arg("current_mod")))
// .def("onInstallationEnd", &IPluginInstaller::onInstallationEnd, (py::arg("result"), py::arg("new_mod")))
2022-04-21 23:57:05 +02:00
// .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginInstallerSimpleWrapper::localizedName_Default)
// .def("master", &MOBase::IPlugin::master, &IPluginInstallerSimpleWrapper::master_Default)
// .def("requirements", &MOBase::IPlugin::requirements, &IPluginInstallerSimpleWrapper::requirements_Default)
// .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, &IPluginInstallerSimpleWrapper::enabledByDefault_Default)
// // Note: Keeping the variant here even if we always return a tuple to be consistent with the wrapper and
// // have proper stubs generation.
// .def("install", +[](IPluginInstallerSimple* p, GuessedValue<QString>& modName, std::shared_ptr<IFileTree>& tree, QString& version, int& nexusID)
// -> std::variant<IPluginInstaller::EInstallResult, std::shared_ptr<IFileTree>, std::tuple<IPluginInstaller::EInstallResult, std::shared_ptr<IFileTree>, QString, int>> {
// auto result = p->install(modName, tree, version, nexusID);
// return std::make_tuple(result, tree, version, nexusID);
2022-04-18 22:40:36 +02:00
// }, (py::arg("name"), "tree", "version", "nexus_id"))
// .def("_parentWidget", &IPluginInstallerSimpleWrapper::parentWidget, py::return_value_policy<py::return_by_value>())
// .def("_manager", &IPluginInstallerSimpleWrapper::manager, py::return_value_policy<py::reference_existing_object>())
2022-04-21 23:57:05 +02:00
// ;
2022-04-18 22:40:36 +02:00
// py::class_<IPluginInstallerCustomWrapper, py::bases<IPluginInstaller>, boost::noncopyable>("IPluginInstallerCustom")
// .def("onInstallationStart", &IPluginInstaller::onInstallationStart, (py::arg("archive"), py::arg("reinstallation"), py::arg("current_mod")))
// .def("onInstallationEnd", &IPluginInstaller::onInstallationEnd, (py::arg("result"), py::arg("new_mod")))
2022-04-21 23:57:05 +02:00
// .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginInstallerCustomWrapper::localizedName_Default)
// .def("master", &MOBase::IPlugin::master, &IPluginInstallerCustomWrapper::master_Default)
// .def("requirements", &MOBase::IPlugin::requirements, &IPluginInstallerCustomWrapper::requirements_Default)
// .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, &IPluginInstallerCustomWrapper::enabledByDefault_Default)
// // Needs to add both otherwize boost does not understand:
2022-04-18 22:40:36 +02:00
// .def("isArchiveSupported", &IPluginInstaller::isArchiveSupported, py::arg("tree"))
// .def("isArchiveSupported", &IPluginInstallerCustom::isArchiveSupported, py::arg("archive_name"))
2022-04-21 23:57:05 +02:00
// .def("supportedExtensions", &IPluginInstallerCustom::supportedExtensions)
2022-04-18 22:40:36 +02:00
// .def("install", &IPluginInstallerCustom::install, (py::arg("mod_name"), "game_name", "archive_name", "version", "nexus_id"))
// .def("_parentWidget", &IPluginInstallerSimpleWrapper::parentWidget, py::return_value_policy<py::return_by_value>())
// .def("_manager", &IPluginInstallerCustomWrapper::manager, py::return_value_policy<py::reference_existing_object>())
2022-04-21 23:57:05 +02:00
// ;
2022-04-18 22:40:36 +02:00
// py::class_<IPluginModPageWrapper, py::bases<IPlugin>, boost::noncopyable>("IPluginModPage")
2022-04-21 23:57:05 +02:00
// .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginModPageWrapper::localizedName_Default)
// .def("master", &MOBase::IPlugin::master, &IPluginModPageWrapper::master_Default)
// .def("requirements", &MOBase::IPlugin::requirements, &IPluginModPageWrapper::requirements_Default)
// .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, &IPluginModPageWrapper::enabledByDefault_Default)
2022-04-18 22:40:36 +02:00
// .def("displayName", py::pure_virtual(&IPluginModPage::displayName))
// .def("icon", py::pure_virtual(&IPluginModPage::icon))
// .def("pageURL", py::pure_virtual(&IPluginModPage::pageURL))
// .def("useIntegratedBrowser", py::pure_virtual(&IPluginModPage::useIntegratedBrowser))
// .def("handlesDownload", py::pure_virtual(&IPluginModPage::handlesDownload), (py::arg("page_url"), "download_url", "fileinfo"))
// .def("setParentWidget", &IPluginModPage::setParentWidget, &IPluginModPageWrapper::setParentWidget_Default, py::arg("parent"))
// .def("_parentWidget", &IPluginModPageWrapper::parentWidget, py::return_value_policy<py::return_by_value>())
2022-04-21 23:57:05 +02:00
// ;
2022-04-18 22:40:36 +02:00
// py::class_<IPluginPreviewWrapper, py::bases<IPlugin>, boost::noncopyable>("IPluginPreview")
2022-04-21 23:57:05 +02:00
// .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginPreviewWrapper::localizedName_Default)
// .def("master", &MOBase::IPlugin::master, &IPluginPreviewWrapper::master_Default)
// .def("requirements", &MOBase::IPlugin::requirements, &IPluginPreviewWrapper::requirements_Default)
// .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, &IPluginPreviewWrapper::enabledByDefault_Default)
2022-04-18 22:40:36 +02:00
// .def("supportedExtensions", py::pure_virtual(&IPluginPreview::supportedExtensions))
// .def("genFilePreview", py::pure_virtual(&IPluginPreview::genFilePreview), py::return_value_policy<py::return_by_value>(),
// (py::arg("filename"), "max_size"))
2022-04-21 23:57:05 +02:00
// ;
2022-04-18 22:40:36 +02:00
// py::class_<IPluginToolWrapper, py::bases<IPlugin>, boost::noncopyable>("IPluginTool")
2022-04-21 23:57:05 +02:00
// .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginToolWrapper::localizedName_Default)
// .def("master", &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default)
// .def("requirements", &MOBase::IPlugin::requirements, &IPluginToolWrapper::requirements_Default)
// .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, &IPluginToolWrapper::enabledByDefault_Default)
2022-04-18 22:40:36 +02:00
// .def("displayName", py::pure_virtual(&IPluginTool::displayName))
// .def("tooltip", py::pure_virtual(&IPluginTool::tooltip))
// .def("icon", py::pure_virtual(&IPluginTool::icon))
// .def("display", py::pure_virtual(&IPluginTool::display))
// .def("setParentWidget", &IPluginTool::setParentWidget, &IPluginToolWrapper::setParentWidget_Default, py::arg("parent"))
// .def("_parentWidget", &IPluginToolWrapper::parentWidget, py::return_value_policy<py::return_by_value>())
2022-04-21 23:57:05 +02:00
// ;
// registerGameFeaturesPythonConverters();
m . def ( "getFileVersion" , & MOBase :: getFileVersion , py :: arg ( "filepath" ));
m . def ( "getProductVersion" , & MOBase :: getProductVersion , py :: arg ( "executable" ));
m . def ( "getIconForExecutable" , & MOBase :: iconForExecutable , py :: arg ( "executable" ));
2022-04-18 22:40:36 +02:00
// py::object widgets(py::borrowed(PyImport_AddModule("mobase.widgets")));
// py::scope().attr("widgets") = widgets;
2022-04-21 23:57:05 +02:00
// {
2022-04-18 22:40:36 +02:00
// py::scope w_ = widgets;
2022-04-21 23:57:05 +02:00
// register_widgets();
// }
// Expose MoVariant: MoVariant is a fake object whose only purpose is to be used as a type-hint
// on the python side (e.g., def foo(x: mobase.MoVariant)). The real MoVariant is defined in the
// generated stubs, since it's only relevant when doing type-checking, but this needs to be defined,
// otherwise MoVariant is not found when actually running plugins through MO2, making them crash.
m . attr ( "MoVariant" ) = py :: none ();
}
/**
*
*/
class PythonRunner : public IPythonRunner
{
public :
PythonRunner ();
~ PythonRunner ();
bool initPython ();
QList < QObject *> load ( const QString & identifier );
void unload ( const QString & identifier );
bool isPythonInitialized () const ;
bool isPythonVersionSupported () const ;
private :
void initPath ();
/**
* @brief Ensure that the given folder is in sys.path.
*/
void ensureFolderInPath ( QString folder );
/**
* @brief Append the underlying object of the given python object to the
* interface list if it is an instance (pointer) of the given type.
*
* @param obj The object to check.
* @param interfaces The list to append the object to.
*
*/
template < class T >
void appendIfInstance ( py :: object const & obj , QList < QObject *>& interfaces );
private :
// For each "identifier" (python file or python module folder), contains the list
// of python objects to keep "alive" during the execution.
std :: unordered_map < QString , std :: vector < py :: object >> m_PythonObjects ;
};
IPythonRunner * CreatePythonRunner ()
{
std :: unique_ptr < PythonRunner > result = std :: make_unique < PythonRunner > ();
if ( result -> initPython ()) {
return result . release ();
}
else {
return nullptr ;
}
}
PythonRunner :: PythonRunner ()
{
}
PythonRunner ::~ PythonRunner () {
// We need the GIL lock when destroying Python objects.
py :: gil_scoped_acquire lock ;
// m_Interpreter.reset();
// Boost.Python does not handle cyclic garbace collection, so we need to release
// everything hold by the objects before deleting the objects themselves:
// for (auto& [name, objects] : m_PythonObjects) {
// for (auto& obj : objects) {
// obj.attr("__dict__").attr("clear")();
// }
// }
// m_PythonObjects.clear();
}
static const char * argv0 = "ModOrganizer.exe" ;
struct PrintWrapper
{
void write ( const char * message )
{
buffer << message ;
if ( buffer . tellp () != 0 && buffer . str (). back () == '\n' )
{
// actually put the string in a variable so it doesn't get destroyed as soon as we get a pointer to its data
std :: string string = buffer . str (). substr ( 0 , buffer . str (). length () - 1 );
qDebug (). nospace (). noquote () << string . c_str ();
buffer = std :: stringstream ();
}
}
std :: stringstream buffer ;
};
// ErrWrapper is in error.h
PYBIND11_MODULE ( moprivate , m )
{
py :: class_ < PrintWrapper > ( m , "PrintWrapper" )
. def ( py :: init <> ())
. def ( "write" , & PrintWrapper :: write );
py :: class_ < ErrWrapper > ( m , "ErrWrapper" )
. def ( py :: init <> ())
. def_static ( "instance" , & ErrWrapper :: instance , py :: return_value_policy :: reference )
. def ( "write" , & ErrWrapper :: write )
. def ( "startRecordingExceptionMessage" , & ErrWrapper :: startRecordingExceptionMessage )
. def ( "stopRecordingExceptionMessage" , & ErrWrapper :: stopRecordingExceptionMessage )
. def ( "getLastExceptionMessage" , & ErrWrapper :: getLastExceptionMessage );
2022-04-18 22:40:36 +02:00
// expose a function to create a particular tree, only for debugging purpose, not
// in mobase.
mo2 :: python :: add_make_tree_function ( m );
2022-04-21 23:57:05 +02:00
}
bool PythonRunner :: initPython ()
{
if ( Py_IsInitialized ())
return true ;
try {
// we initialize the interpreter "the old way" because scoped_interpreter does not
// seem to work well with PyQt
wchar_t argBuffer [ MAX_PATH ];
const size_t cSize = strlen ( argv0 ) + 1 ;
mbstowcs ( argBuffer , argv0 , MAX_PATH );
Py_SetProgramName ( argBuffer );
PyImport_AppendInittab ( "mobase" , & PyInit_mobase );
PyImport_AppendInittab ( "moprivate" , & PyInit_moprivate );
Py_OptimizeFlag = 2 ;
Py_NoSiteFlag = 1 ;
initPath ();
Py_InitializeEx ( 0 );
if ( ! Py_IsInitialized ()) {
return false ;
}
py :: module_ mainModule = py :: module_ :: import ( "__main__" );
py :: object mainNamespace = mainModule . attr ( "__dict__" );
mainNamespace [ "sys" ] = py :: module_ :: import ( "sys" );
mainNamespace [ "moprivate" ] = py :: module_ :: import ( "moprivate" );
py :: module_ :: import ( "site" );
py :: exec ( "sys.stdout = moprivate.PrintWrapper() \n "
"sys.stderr = moprivate.ErrWrapper.instance() \n "
"sys.excepthook = lambda x, y, z: sys.__excepthook__(x, y, z) \n " ,
mainNamespace );
mainNamespace [ "mobase" ] = py :: module_ :: import ( "mobase" );
2022-04-18 22:40:36 +02:00
mo2 :: details :: configure_python_logging ( mainNamespace [ "mobase" ]);
2022-04-21 23:57:05 +02:00
return true ;
} catch ( const py :: error_already_set & ) {
// construct an error to extract the message
pyexcept :: PythonError err ;
MOBase :: log :: error ( "failed to init python: {}" , err . what ());
return false ;
}
}
void PythonRunner :: initPath ()
{
static QStringList paths = {
QCoreApplication :: applicationDirPath () + "/pythoncore.zip" ,
QCoreApplication :: applicationDirPath () + "/pythoncore" ,
IOrganizer :: getPluginDataPath ()
};
Py_SetPath ( paths . join ( ';' ). toStdWString (). c_str ());
}
void PythonRunner :: ensureFolderInPath ( QString folder ) {
py :: module_ sys = py :: module_ :: import ( "sys" );
py :: list sysPath = sys . attr ( "path" );
// Converting to QStringList for Qt::CaseInsensitive and because .index()
// raise an exception:
const QStringList currentPath = sysPath . cast < QStringList > ();
if ( ! currentPath . contains ( folder , Qt :: CaseInsensitive )) {
sysPath . insert ( 0 , folder );
}
}
template < class T >
void PythonRunner :: appendIfInstance ( py :: object const & obj , QList < QObject *> & interfaces ) {
if ( py :: isinstance < T *> ( obj )) {
interfaces . append ( obj . cast < T *> ());
}
}
QList < QObject *> PythonRunner :: load ( const QString & identifier )
{
py :: gil_scoped_acquire lock ;
// `pluginName` can either be a python file (single-file plugin or a folder (whole module).
//
// For whole module, we simply add the parent folder to path, then we load the module with a simple
2022-04-18 22:40:36 +02:00
// py::import, and we retrieve the associated __dict__ from which we extract either createPlugin or
2022-04-21 23:57:05 +02:00
// createPlugins.
//
// For single file, we need to use py::eval_file, and we will use the context (global variables)
// from __main__ (already contains mobase, and other required module). Since the context is shared
// between called of `instantiate`, we need to make sure to remove createPlugin(s) from previous call.
try {
// Dictionary that will contain createPlugin() or createPlugins().
py :: dict moduleDict ;
if ( identifier . endsWith ( ".py" )) {
py :: object mainModule = py :: module_ :: import ( "__main__" );
py :: dict moduleNamespace = mainModule . attr ( "__dict__" );
std :: string temp = ToString ( identifier );
py :: eval_file ( temp . c_str (), moduleNamespace ). is_none ();
moduleDict = moduleNamespace ;
}
else {
// Retrieve the module name:
QStringList parts = identifier . split ( "/" );
std :: string moduleName = ToString ( parts . takeLast ());
ensureFolderInPath ( parts . join ( "/" ));
moduleDict = py :: module_ :: import ( moduleName . c_str ()). attr ( "__dict__" );
}
if ( py :: len ( moduleDict ) == 0 ) {
MOBase :: log :: error ( "No plugins found in {}." , identifier );
return {};
}
// Create the plugins:
std :: vector < py :: object > plugins ;
if ( moduleDict . contains ( "createPlugin" )) {
plugins . push_back ( moduleDict [ "createPlugin" ]());
// Clear for future call
PyDict_DelItemString ( moduleDict . ptr (), "createPlugin" );
}
else if ( moduleDict . contains ( "createPlugins" )) {
py :: object pyPlugins = moduleDict [ "createPlugins" ]();
if ( ! PySequence_Check ( pyPlugins . ptr ())) {
MOBase :: log :: error ( "Plugin {}: createPlugins must return a list." , identifier );
}
else {
py :: list pyList ( pyPlugins );
int nPlugins = py :: len ( pyList );
for ( int i = 0 ; i < nPlugins ; ++ i ) {
plugins . push_back ( pyList [ i ]);
}
}
// Clear for future call
PyDict_DelItemString ( moduleDict . ptr (), "createPlugins" );
}
else {
MOBase :: log :: error ( "Plugin {}: missing a createPlugin(s) function." , identifier );
}
// If we have no plugins, there was an issue, and we already logged the problem:
if ( plugins . empty ()) {
return QList < QObject *> ();
}
QList < QObject *> allInterfaceList ;
for ( py :: object pluginObj : plugins ) {
// Add the plugin to keep it alive:
m_PythonObjects [ identifier ]. push_back ( pluginObj );
QList < QObject *> interfaceList ;
// appendIfInstance<IPluginGame>(pluginObj, interfaceList);
// Must try the wrapper because it's only a plugin extension interface in C++, so doesn't extend QObject
// appendIfInstance<IPluginDiagnoseWrapper>(pluginObj, interfaceList);
// Must try the wrapper because it's only a plugin extension interface in C++, so doesn't extend QObject
// appendIfInstance<IPluginFileMapperWrapper>(pluginObj, interfaceList);
// appendIfInstance<IPluginInstallerCustom>(pluginObj, interfaceList);
// appendIfInstance<IPluginInstallerSimple>(pluginObj, interfaceList);
// appendIfInstance<IPluginModPage>(pluginObj, interfaceList);
// appendIfInstance<IPluginPreview>(pluginObj, interfaceList);
// appendIfInstance<IPluginTool>(pluginObj, interfaceList);
if ( interfaceList . isEmpty ()) {
// appendIfInstance<IPluginWrapper>(pluginObj, interfaceList);
}
if ( interfaceList . isEmpty ()) {
MOBase :: log :: error ( "Plugin {}: no plugin interface implemented." , identifier );
}
// Append the plugins to the main list:
allInterfaceList . append ( interfaceList );
}
return allInterfaceList ;
}
catch ( const py :: error_already_set & ) {
MOBase :: log :: error ( "Failed to import plugin from {}." , identifier );
throw pyexcept :: PythonError ();
}
}
void PythonRunner :: unload ( const QString & identifier )
{
auto it = m_PythonObjects . find ( identifier );
if ( it != m_PythonObjects . end ()) {
py :: gil_scoped_acquire lock ;
if ( ! identifier . endsWith ( ".py" )) {
// At this point, the identifier is the full path to the module.
QDir folder ( identifier );
// We want to "unload" (remove from sys.modules) modules that come
// from this plugin (whose __path__ points under this module, including
// the module of the plugin itself).
py :: object sys = py :: module_ :: import ( "sys" );
py :: dict modules = sys . attr ( "modules" );
py :: list keys = modules . attr ( "keys" )();
for ( std :: size_t i = 0 ; i < py :: len ( keys ); ++ i ) {
py :: object mod = modules [ keys [ i ]];
if ( PyObject_HasAttrString ( mod . ptr (), "__path__" )) {
QString mpath = mod . attr ( "__path__" )[ 0 ]. cast < QString > ();
if ( ! folder . relativeFilePath ( mpath ). startsWith ( ".." )) {
// If the path is under identifier, we need to unload it.
log :: debug ( "Unloading module {} from {} for {}." , keys [ i ]. cast < std :: string > (), mpath , identifier );
PyDict_DelItem ( modules . ptr (), keys [ i ]. ptr ());
}
}
}
}
// Boost.Python does not handle cyclic garbace collection, so we need to release
// everything hold by the objects before deleting the objects themselves (done when
// erasing from m_PythonObjects).
for ( auto & obj : it -> second ) {
obj . attr ( "__dict__" ). attr ( "clear" )();
}
log :: debug ( "Deleting {} python objects for {}." , it -> second . size (), identifier );
m_PythonObjects . erase ( it );
}
}
bool PythonRunner :: isPythonInitialized () const
{
return Py_IsInitialized () != 0 ;
}