diff --git a/src/proxy/plugin_python_en.ts b/src/proxy/plugin_python_en.ts index e4e2f66..99302dd 100644 --- a/src/proxy/plugin_python_en.ts +++ b/src/proxy/plugin_python_en.ts @@ -14,48 +14,48 @@ - + ModOrganizer path contains a semicolon - + Python DLL not found - + Invalid Python DLL - + Initializing Python failed - - + + invalid problem key %1 - + The path to Mod Organizer (%1) contains a semicolon. <br>While this is legal on NTFS drives, many softwares do not handle it correctly.<br>Unfortunately MO depends on libraries that seem to fall into that group.<br>As a result the python plugin cannot be loaded, and the only solution we canoffer is to remove the semicolon or move MO to a path without a semicolon. - + The Python plugin DLL was not found, maybe your antivirus deleted it. Re-installing MO2 might fix the problem. - + The Python plugin DLL is invalid, maybe your antivirus is blocking it. Re-installing MO2 and adding exclusions for it to your AV might fix the problem. - + The initialization of the Python plugin DLL failed, unfortunately without any details. diff --git a/src/proxy/proxypython.cpp b/src/proxy/proxypython.cpp index 377d277..035063c 100644 --- a/src/proxy/proxypython.cpp +++ b/src/proxy/proxypython.cpp @@ -153,15 +153,11 @@ QStringList ProxyPython::pluginList(const QDir& pluginPath) const QString name = iter.next(); QFileInfo info = iter.fileInfo(); - if (info.fileName() == "pyCfg.py" || info.fileName() == "installer_wizard") { + if (info.isFile() && name.endsWith(".py")) { result.append(name); } - - if (info.isFile() && name.endsWith(".py")) { - // result.append(name); - } else if (info.isDir() && QDir(info.absoluteFilePath()).exists("__init__.py")) { - // result.append(name); + result.append(name); } } diff --git a/src/proxy/proxypython.h b/src/proxy/proxypython.h index 249d198..9da1b9d 100644 --- a/src/proxy/proxypython.h +++ b/src/proxy/proxypython.h @@ -25,59 +25,56 @@ along with python proxy plugin. If not, see . #include -#include #include +#include #include - -class ProxyPython : public QObject, public MOBase::IPluginProxy, public MOBase::IPluginDiagnose -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginProxy MOBase::IPluginDiagnose) -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) - Q_PLUGIN_METADATA(IID "org.tannin.ProxyPython" FILE "proxypython.json") +class ProxyPython : public QObject, + public MOBase::IPluginProxy, + public MOBase::IPluginDiagnose { + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginProxy MOBase::IPluginDiagnose) +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + Q_PLUGIN_METADATA(IID "org.mo2.ProxyPython") #endif public: - ProxyPython(); + ProxyPython(); - virtual bool init(MOBase::IOrganizer *moInfo); - virtual QString name() const override; - virtual QString localizedName() const override; - virtual QString author() const override; - virtual QString description() const override; - virtual MOBase::VersionInfo version() const override; - virtual QList settings() const override; + virtual bool init(MOBase::IOrganizer* moInfo); + virtual QString name() const override; + virtual QString localizedName() const override; + virtual QString author() const override; + virtual QString description() const override; + virtual MOBase::VersionInfo version() const override; + virtual QList settings() const override; - QStringList pluginList(const QDir& pluginPath) const override; - QList load(const QString& identifier) override; - void unload(const QString& identifier) override; + QStringList pluginList(const QDir& pluginPath) const override; + QList load(const QString& identifier) override; + void unload(const QString& identifier) override; -public: // IPluginDiagnose - - virtual std::vector activeProblems() const override; - virtual QString shortDescription(unsigned int key) const override; - virtual QString fullDescription(unsigned int key) const override; - virtual bool hasGuidedFix(unsigned int key) const override; - virtual void startGuidedFix(unsigned int key) const override; +public: // IPluginDiagnose + virtual std::vector activeProblems() const override; + virtual QString shortDescription(unsigned int key) const override; + virtual QString fullDescription(unsigned int key) const override; + virtual bool hasGuidedFix(unsigned int key) const override; + virtual void startGuidedFix(unsigned int key) const override; private: + MOBase::IOrganizer* m_MOInfo; + HMODULE m_RunnerLib; + std::unique_ptr m_Runner; - MOBase::IOrganizer *m_MOInfo; - HMODULE m_RunnerLib; - std::unique_ptr m_Runner; - - enum class FailureType : unsigned int { - NONE = 0, - SEMICOLON = 1, - DLL_NOT_FOUND = 2, - INVALID_DLL = 3, - INITIALIZATION = 4 - }; - - FailureType m_LoadFailure; + enum class FailureType : unsigned int { + NONE = 0, + SEMICOLON = 1, + DLL_NOT_FOUND = 2, + INVALID_DLL = 3, + INITIALIZATION = 4 + }; + FailureType m_LoadFailure; }; -#endif // PROXYPYTHON_H +#endif // PROXYPYTHON_H diff --git a/src/proxy/proxypython.json b/src/proxy/proxypython.json deleted file mode 100644 index 69a88e3..0000000 --- a/src/proxy/proxypython.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/src/runner-pybind11/CMakeLists.txt b/src/runner-pybind11/CMakeLists.txt index 4a01831..5468256 100644 --- a/src/runner-pybind11/CMakeLists.txt +++ b/src/runner-pybind11/CMakeLists.txt @@ -1,14 +1,11 @@ cmake_minimum_required(VERSION 3.16) -# need to find Boost here with a dummy component to get Boost_LIBRARY_DIRS -find_package(Boost COMPONENTS thread REQUIRED) - pybind11_add_module(pythonrunner SHARED) mo2_configure_library(pythonrunner WARNINGS OFF AUTOMOC ON TRANSLATIONS OFF - PRIVATE_DEPENDS uibase boost Qt::Core + PRIVATE_DEPENDS uibase Qt::Core ) set_target_properties(pythonrunner PROPERTIES @@ -19,11 +16,6 @@ target_link_libraries(pythonrunner PRIVATE pybind11::embed) target_include_directories(pythonrunner PRIVATE ${PYTHON_ROOT}/Include) # this is kind of broken but it only works with this... -target_link_directories(pythonrunner - PRIVATE - ${Boost_LIBRARY_DIRS}) -target_compile_options(pythonrunner - PRIVATE $<$:/MP>) target_compile_definitions(pythonrunner PRIVATE QT_NO_KEYWORDS PYTHONRUNNER_LIBRARY) mo2_install_target(pythonrunner INSTALLDIR bin/plugins/data) diff --git a/src/runner-pybind11/converters-old.h b/src/runner-pybind11/converters-old.h deleted file mode 100644 index d941eaf..0000000 --- a/src/runner-pybind11/converters-old.h +++ /dev/null @@ -1,669 +0,0 @@ -#ifndef PYTHON_CONVERTERS_OLD_HPP -#define PYTHON_CONVERTERS_OLD_HPP - -#include -#include -#include -#include -#include -#include - -// sip and qt slots seems to conflict -#include - -// Include the container converters from utils: -#include "pythonutils.h" - -namespace utils { - - namespace bpy = boost::python; - - namespace QString_converter { - - /** - * We need this since sip does not expose QString but uses standard python str. - */ - struct QString_to_python_str { - static PyObject* convert(const QString& str) - { - // It's safer to explicitly convert to unicode as if we don't, this can - // return either str or unicode without it being easy to know which to - // expect - bpy::object pyStr = bpy::object(qUtf8Printable(str)); - if (SIPBytes_Check(pyStr.ptr())) - pyStr = pyStr.attr("decode")("utf-8"); - return bpy::incref(pyStr.ptr()); - } - }; - - struct QString_from_python_str { - - static void* convertible(PyObject* objPtr) - { - return SIPBytes_Check(objPtr) || PyUnicode_Check(objPtr) ? objPtr - : nullptr; - } - - static void construct(PyObject* objPtr, - bpy::converter::rvalue_from_python_stage1_data* data) - { - // Ensure the string uses 8-bit characters - PyObject* strPtr = - PyUnicode_Check(objPtr) ? PyUnicode_AsUTF8String(objPtr) : objPtr; - - // Extract the character data from the python string - const char* value = SIPBytes_AsString(strPtr); - assert(value != nullptr); - - // allocate storage - void* storage = - ((bpy::converter::rvalue_from_python_storage*)data) - ->storage.bytes; - - // construct QString in the allocated memory - new (storage) QString(value); - - data->convertible = storage; - - // Deallocate local copy if one was made - if (strPtr != objPtr) - Py_DecRef(strPtr); - } - }; - - } // namespace QString_converter - - namespace Enum_converter { - - /** - * - */ - template - struct Enum_to_int { - static PyObject* convert(const Enum& flags) - { - return bpy::incref(bpy::object(static_cast(flags)).ptr()); - } - }; - - template - struct Enum_from_python_obj { - - static void* convertible(PyObject* objPtr) - { - return SIPLong_Check(objPtr) ? objPtr : nullptr; - } - - static void construct(PyObject* objPtr, - bpy::converter::rvalue_from_python_stage1_data* data) - { - int intVersion = (int)SIPLong_AsLong(objPtr); - void* storage = - ((bpy::converter::rvalue_from_python_storage*)data) - ->storage.bytes; - new (storage) Enum(static_cast(intVersion)); - data->convertible = storage; - } - }; - - } // namespace Enum_converter - - namespace QFlags_converter { - - /** - * - */ - template - struct QFlags_to_int { - static PyObject* convert(const QFlags& flags) - { - return bpy::incref(bpy::object(static_cast(flags)).ptr()); - } - }; - - template - struct QFlags_from_python_obj { - - static void* convertible(PyObject* objPtr) - { - return SIPLong_Check(objPtr) ? objPtr : nullptr; - } - - static void construct(PyObject* objPtr, - bpy::converter::rvalue_from_python_stage1_data* data) - { - int intVersion = (int)SIPLong_AsLong(objPtr); - T tVersion = (T)intVersion; - void* storage = - ((bpy::converter::rvalue_from_python_storage>*)data) - ->storage.bytes; - new (storage) QFlags(tVersion); - - data->convertible = storage; - } - }; - - } // namespace QFlags_converter - - namespace QVariant_converter { - - struct QVariant_to_python_obj { - static PyObject* convert(const QVariant& var) - { - switch (var.type()) { - case QVariant::Invalid: - return bpy::incref(Py_None); - case QVariant::Int: - return SIPLong_FromLong(var.toInt()); - case QVariant::UInt: - return PyLong_FromUnsignedLong(var.toUInt()); - case QVariant::Bool: - return PyBool_FromLong(var.toBool()); - case QVariant::String: - return bpy::incref(bpy::object(var.toString()).ptr()); - // We need to check for StringList here because these are not considered - // List since List is QList will StringList is QList: - case QVariant::StringList: - return bpy::incref(bpy::object(var.toStringList()).ptr()); - case QVariant::List: { - return bpy::incref(bpy::object(var.toList()).ptr()); - } break; - case QVariant::Map: { - return bpy::incref(bpy::object(var.toMap()).ptr()); - } break; - default: { - PyErr_Format(PyExc_TypeError, "type unsupported: %d", var.type()); - throw bpy::error_already_set(); - } break; - } - } - }; - - struct QVariant_from_python_obj { - - static void* convertible(PyObject* objPtr) - { - if (!SIPBytes_Check(objPtr) && !PyUnicode_Check(objPtr) && - !PyLong_Check(objPtr) && !PyBool_Check(objPtr) && - !PyList_Check(objPtr) && !PyDict_Check(objPtr) && - objPtr != Py_None) { - return nullptr; - } - return objPtr; - } - - template - static void - constructVariant(const T& value, - bpy::converter::rvalue_from_python_stage1_data* data) - { - void* storage = - ((bpy::converter::rvalue_from_python_storage*)data) - ->storage.bytes; - - new (storage) QVariant(value); - - data->convertible = storage; - } - - static void - constructVariant(bpy::converter::rvalue_from_python_stage1_data* data) - { - void* storage = - ((bpy::converter::rvalue_from_python_storage*)data) - ->storage.bytes; - - new (storage) QVariant(); - - data->convertible = storage; - } - - static void construct(PyObject* objPtr, - bpy::converter::rvalue_from_python_stage1_data* data) - { - if (PyList_Check(objPtr)) { - // We could check if all the elements can be converted to QString - // and store a QStringList in the QVariant but I am not sure that is - // really useful. - constructVariant(bpy::extract(objPtr)(), data); - } - else if (objPtr == Py_None) { - constructVariant(data); - } - else if (PyDict_Check(objPtr)) { - constructVariant(bpy::extract(objPtr)(), data); - } - else if (SIPBytes_Check(objPtr) || PyUnicode_Check(objPtr)) { - constructVariant(bpy::extract(objPtr)(), data); - } - // PyBools will also return true for SIPLong_Check but not the other way - // around, so the order here is relevant. - else if (PyBool_Check(objPtr)) { - constructVariant(bpy::extract(objPtr)(), data); - } - else if (SIPLong_Check(objPtr)) { - // QVariant doesn't have long. It has int or long long. Given that - // on m/s, long is 32 bits for 32- and 64- bit code... - constructVariant(bpy::extract(objPtr)(), data); - } - else { - PyErr_SetString(PyExc_TypeError, "type unsupported"); - throw bpy::error_already_set(); - } - } - }; - - } // namespace QVariant_converter - - namespace QClass_converter { - - template - struct MetaData; - - template <> - struct MetaData { - static const char* className() { return "QObject"; } - }; - template <> - struct MetaData { - static const char* className() { return "QWidget"; } - }; - template <> - struct MetaData { - static const char* className() { return "QMainWindow"; } - }; - template <> - struct MetaData { - static const char* className() { return "QDateTime"; } - }; - template <> - struct MetaData { - static const char* className() { return "QDir"; } - }; - template <> - struct MetaData { - static const char* className() { return "QFileInfo"; } - }; - template <> - struct MetaData { - static const char* className() { return "QIcon"; } - }; - template <> - struct MetaData { - static const char* className() { return "QSize"; } - }; - template <> - struct MetaData { - static const char* className() { return "QUrl"; } - }; - template <> - struct MetaData { - static const char* className() { return "QVariant"; } - }; - - template - struct QClass_converters { - struct QClass_to_PyQt { - template - static typename std::enable_if_t, T*> - getSafeCopy(T* qClass) - { - return new T(*qClass); - } - - template - static typename std::enable_if_t, T*> - getSafeCopy(T* qClass) - { - return qClass; - } - - static PyObject* convert(const T& object) - { - const sipTypeDef* type = - sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); - if (type == nullptr) { - return bpy::incref(Py_None); - } - - PyObject* sipObj = sipAPIAccess::sipAPI()->api_convert_from_type( - (void*)getSafeCopy((T*)&object), type, 0); - if (sipObj == nullptr) { - return bpy::incref(Py_None); - } - - if (std::is_copy_constructible_v) - // Ensure Python deletes the C++ component - sipAPIAccess::sipAPI()->api_transfer_back(sipObj); - - return bpy::incref(sipObj); - } - - static PyObject* convert(T* object) - { - if (object == nullptr) { - return bpy::incref(Py_None); - } - - const sipTypeDef* type = - sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); - if (type == nullptr) { - return bpy::incref(Py_None); - } - - PyObject* sipObj = sipAPIAccess::sipAPI()->api_convert_from_type( - getSafeCopy(object), type, 0); - if (sipObj == nullptr) { - return bpy::incref(Py_None); - } - - if (std::is_copy_constructible_v) - // Ensure Python deletes the C++ component - sipAPIAccess::sipAPI()->api_transfer_back(sipObj); - - return bpy::incref(sipObj); - } - - static PyObject* convert(const T* object) - { - return convert((T*)object); - } - - static PyTypeObject const* get_pytype() - { - const sipTypeDef* type = - sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); - if (type == nullptr) { - return bpy::incref(Py_None); - } - return bpy::incref(type->td_py_type); - } - }; - - static void* QClass_from_PyQt(PyObject* objPtr) - { - // This would transfer responsibility for deconstructing the object to - // C++, but Boost assumes l-value converters (such as this) don't do - // that Instead, this should be called within the wrappers for functions - // which return deletable pointers. - // sipAPI()->api_transfer_to(objPtr, Py_None); - if (PyObject_TypeCheck( - objPtr, sipAPIAccess::sipAPI()->api_simplewrapper_type)) { - sipSimpleWrapper* wrapper; - wrapper = reinterpret_cast(objPtr); - return wrapper->data; - } - else if (PyObject_TypeCheck(objPtr, - sipAPIAccess::sipAPI()->api_wrapper_type)) { - sipWrapper* wrapper; - wrapper = reinterpret_cast(objPtr); - return wrapper->super.data; - } - return nullptr; - } - }; - - } // namespace QClass_converter - - namespace details { - - inline bool has_arity(PyObject* object, std::size_t arity) - { - // Mostly from https://stackoverflow.com/a/36143796/2666289 - bpy::object fn(bpy::handle<>(bpy::borrowed(object))); - - auto inspect = bpy::import("inspect"); - auto arg_spec = inspect.attr("getfullargspec")(fn); - bpy::object args = arg_spec.attr("args"), - varargs = arg_spec.attr("varargs"), - defaults = arg_spec.attr("defaults"); - - auto args_count = args ? bpy::len(args) : 0; - auto defaults_count = defaults ? bpy::len(defaults) : 0; - - if (static_cast(inspect.attr("ismethod")(fn)) && - fn.attr("__self__")) { - --args_count; - } - - auto required_count = args_count - defaults_count; - - return required_count <= - arity // Cannot require more parameters than given, - && - (args_count >= arity || varargs); // Must accept enough parameters. - } - - template - struct wrap_impl; - - template <> - struct wrap_impl<> { - template - static decltype(auto) apply(T&& t) - { - return std::forward(t); - } - }; - - template - struct wrap_impl, Ws...> : public wrap_impl<> { - using wrap_impl<>::apply; - - static auto apply(T t) { return boost::ref(t); } - }; - - template - struct wrap_impl, Ws...> - : public wrap_impl<> { - using wrap_impl<>::apply; - - static auto apply(T t) { return bpy::ptr(t); } - }; - - template - struct wrap_impl { - template - static decltype(auto) apply(T&& t) - { - return wrap::apply(std::forward(t)); - } - }; - - } // namespace details - - /** - * @brief Convert a python callable to a valid C++ Callable object. Also works - * for None. - */ - template - struct Functor_converter; - - template - struct Functor_converter { - - template - static decltype(auto) wrap(T&& t) - { - return details::wrap_impl::apply(std::forward(t)); - } - - struct FunctorWrapper { - FunctorWrapper(boost::python::object callable) : m_Callable(callable) {} - - ~FunctorWrapper() - { - GILock lock; - m_Callable = bpy::object(); - } - - R operator()(Args... params) - { - GILock lock; - try { - if constexpr (std::is_same_v) { - m_Callable(wrap(params)...); - } - else { - return bpy::extract(m_Callable(wrap(params)...)); - } - } - catch (const boost::python::error_already_set&) { - throw pyexcept::PythonError(""); - } - catch (...) { - throw pyexcept::UnknownException(); - } - } - - boost::python::object m_Callable; - }; - - static void* convertible(PyObject* object) - { - // We allow None here, we will just default-construct a std::function: - if (object == Py_None) { - return object; - } - - // Otherwize we check that we have a callable object: - if (!PyCallable_Check(object) || - !details::has_arity(object, sizeof...(Args))) { - return nullptr; - } - return object; - } - - static void construct(PyObject* object, - bpy::converter::rvalue_from_python_stage1_data* data) - { - bpy::object callable(bpy::handle<>(bpy::borrowed(object))); - void* storage = ((bpy::converter::rvalue_from_python_storage< - std::function>*)data) - ->storage.bytes; - if (callable.is_none()) { - new (storage) std::function{}; - } - else { - new (storage) std::function(FunctorWrapper(callable)); - } - data->convertible = storage; - } - }; - - /** - * @brief Call policy that automatically downcast shared pointer of type FromType - * to shared pointer of type ToType. - */ - template - struct DowncastConverter { - - bool convertible() const { return true; } - - inline PyObject* operator()(std::shared_ptr p) const - { - if (p == nullptr) { - return bpy::detail::none(); - } - else { - auto downcast_p = std::dynamic_pointer_cast(p); - bpy::object p_value = - downcast_p == nullptr ? bpy::object{p} : bpy::object{downcast_p}; - return bpy::incref(p_value.ptr()); - } - } - - inline PyTypeObject const* get_pytype() const - { - return bpy::converter::registered_pytype::get_pytype(); - } - }; - - template - struct downcast_return { - - template - struct apply_; - - template - struct apply_> { - static_assert( - std::is_convertible_v, std::shared_ptr>); - using type = DowncastConverter; - }; - - template - using apply = apply_>; - }; - - // Functions: - inline void register_qstring_converter() - { - using namespace QString_converter; - bpy::to_python_converter(); - bpy::converter::registry::push_back(&QString_from_python_str::convertible, - &QString_from_python_str::construct, - bpy::type_id()); - } - - inline void register_qvariant_converter() - { - using namespace QVariant_converter; - bpy::to_python_converter(); - bpy::converter::registry::push_back(&QVariant_from_python_obj::convertible, - &QVariant_from_python_obj::construct, - bpy::type_id()); - } - - template - inline void register_qflags_converter() - { - using T = typename Flags::enum_type; - using namespace QFlags_converter; - bpy::to_python_converter>(); - bpy::converter::registry::push_back(&QFlags_from_python_obj::convertible, - &QFlags_from_python_obj::construct, - bpy::type_id()); - } - - template - inline void register_enum_converter() - { - using namespace Enum_converter; - bpy::to_python_converter>(); - bpy::converter::registry::push_back(&Enum_from_python_obj::convertible, - &Enum_from_python_obj::construct, - bpy::type_id()); - } - - template - inline void register_qclass_converter() - { - using Converter = QClass_converter::QClass_converters; - bpy::converter::registry::insert(&Converter::QClass_from_PyQt, - bpy::type_id()); - bpy::to_python_converter(); - bpy::to_python_converter(); - bpy::to_python_converter(); - } - - /** - * @brief Register a functor converter. - * - * @tparam Fn The function type to register. - * @tparam Wrappers... A list of wrapper (boost::python::pointer_wrapper or - * boost::reference_wrapper) indicating if parameters of the given (wrapped) type - * must be wrapped. - */ - template - inline void register_functor_converter() - { - using Converter = Functor_converter; - bpy::converter::registry::push_back(&Converter::convertible, - &Converter::construct, - bpy::type_id>()); - } - -} // namespace utils - -#endif diff --git a/src/runner-pybind11/gamefeatureswrappers.cpp b/src/runner-pybind11/gamefeatureswrappers.cpp deleted file mode 100644 index 93e2b9e..0000000 --- a/src/runner-pybind11/gamefeatureswrappers.cpp +++ /dev/null @@ -1,390 +0,0 @@ -#include "gamefeatureswrappers.h" - -#include -#include - -#include -#include -#include -#include -#include - -#include "pythonwrapperutilities.h" -#include "shared_ptr_converter.h" - -///////////////////////////// -/// BSAInvalidation Wrapper - -bool BSAInvalidationWrapper::isInvalidationBSA(const QString& bsaName) -{ - return basicWrapperFunctionImplementation(this, "isInvalidationBSA", bsaName); -} - -void BSAInvalidationWrapper::deactivate(MOBase::IProfile* profile) -{ - return basicWrapperFunctionImplementation(this, "deactivate", - boost::python::ptr(profile)); -} - -void BSAInvalidationWrapper::activate(MOBase::IProfile* profile) -{ - return basicWrapperFunctionImplementation(this, "activate", - boost::python::ptr(profile)); -} - -bool BSAInvalidationWrapper::prepareProfile(MOBase::IProfile* profile) -{ - return basicWrapperFunctionImplementation(this, "prepareProfile", - boost::python::ptr(profile)); -} -/// end BSAInvalidation Wrapper -///////////////////////////// -/// DataArchives Wrapper - -QStringList DataArchivesWrapper::vanillaArchives() const -{ - return basicWrapperFunctionImplementation(this, "vanillaArchives"); -} - -QStringList DataArchivesWrapper::archives(const MOBase::IProfile* profile) const -{ - return basicWrapperFunctionImplementation(this, "archives", - boost::python::ptr(profile)); -} - -void DataArchivesWrapper::addArchive(MOBase::IProfile* profile, int index, - const QString& archiveName) -{ - return basicWrapperFunctionImplementation( - this, "addArchive", boost::python::ptr(profile), index, archiveName); -} - -void DataArchivesWrapper::removeArchive(MOBase::IProfile* profile, - const QString& archiveName) -{ - return basicWrapperFunctionImplementation( - this, "removeArchive", boost::python::ptr(profile), archiveName); -} -/// end DataArchives Wrapper -///////////////////////////// -/// GamePlugins Wrapper - -void GamePluginsWrapper::writePluginLists(const MOBase::IPluginList* pluginList) -{ - return basicWrapperFunctionImplementation(this, "writePluginLists", - boost::python::ptr(pluginList)); -} - -void GamePluginsWrapper::readPluginLists(MOBase::IPluginList* pluginList) -{ - return basicWrapperFunctionImplementation(this, "readPluginLists", - boost::python::ptr(pluginList)); -} - -QStringList GamePluginsWrapper::getLoadOrder() -{ - return basicWrapperFunctionImplementation(this, "getLoadOrder"); -} - -bool GamePluginsWrapper::lightPluginsAreSupported() -{ - return basicWrapperFunctionImplementation(this, "lightPluginsAreSupported"); -} - -/// end GamePlugins Wrapper -///////////////////////////// -/// LocalSavegames Wrapper - -MappingType LocalSavegamesWrapper::mappings(const QDir& profileSaveDir) const -{ - return basicWrapperFunctionImplementation(this, "mappings", - profileSaveDir); -} - -bool LocalSavegamesWrapper::prepareProfile(MOBase::IProfile* profile) -{ - return basicWrapperFunctionImplementation(this, "prepareProfile", - boost::python::ptr(profile)); -} - -/// end LocalSavegames Wrapper -///////////////////////////// -/// ModDataChecker Wrapper - -ModDataChecker::CheckReturn ModDataCheckerWrapper::dataLooksValid( - std::shared_ptr fileTree) const -{ - return basicWrapperFunctionImplementation(this, "dataLooksValid", - fileTree); -} - -std::shared_ptr -ModDataCheckerWrapper::fix(std::shared_ptr fileTree) const -{ - return utils::clean_shared_ptr(basicWrapperFunctionImplementationWithDefault< - std::shared_ptr>( - this, - [](auto&&... args) { - return nullptr; - }, - "fix", fileTree)); -} - -/// end ModDataChecker Wrapper -///////////////////////////// -/// ModDataContent Wrapper - -std::vector ModDataContentWrapper::getAllContents() const -{ - return basicWrapperFunctionImplementation>(this, - "getAllContents"); -} -std::vector ModDataContentWrapper::getContentsFor( - std::shared_ptr fileTree) const -{ - return basicWrapperFunctionImplementation>(this, "getContentsFor", - fileTree); -} - -/// end ModDataContent Wrapper -///////////////////////////// -/// SaveGameInfo Wrapper - -SaveGameInfoWrapper::MissingAssets -SaveGameInfoWrapper::getMissingAssets(MOBase::ISaveGame const& save) const -{ - return basicWrapperFunctionImplementation( - this, "getMissingAssets", boost::ref(save)); -} - -MOBase::ISaveGameInfoWidget* -SaveGameInfoWrapper::getSaveGameWidget(QWidget* parent) const -{ - return basicWrapperFunctionImplementation( - this, m_SaveGameWidget, "getSaveGameWidget", parent); -} - -/// end SaveGameInfo Wrapper -///////////////////////////// -/// ScriptExtender Wrapper - -QString ScriptExtenderWrapper::BinaryName() const -{ - return basicWrapperFunctionImplementation(this, "BinaryName"); -} - -QString ScriptExtenderWrapper::PluginPath() const -{ - return basicWrapperFunctionImplementation(this, "PluginPath"); -} - -QString ScriptExtenderWrapper::loaderName() const -{ - return basicWrapperFunctionImplementation(this, "loaderName"); -} - -QString ScriptExtenderWrapper::loaderPath() const -{ - return basicWrapperFunctionImplementation(this, "loaderPath"); -} - -QString ScriptExtenderWrapper::savegameExtension() const -{ - return basicWrapperFunctionImplementation(this, "savegameExtension"); -} - -bool ScriptExtenderWrapper::isInstalled() const -{ - return basicWrapperFunctionImplementation(this, "isInstalled"); -} - -QString ScriptExtenderWrapper::getExtenderVersion() const -{ - return basicWrapperFunctionImplementation(this, "getExtenderVersion"); -} - -WORD ScriptExtenderWrapper::getArch() const -{ - return basicWrapperFunctionImplementation(this, "getArch"); -} - -/// end ScriptExtender Wrapper -///////////////////////////// -/// UnmanagedMods Wrapper - -QStringList UnmanagedModsWrapper::mods(bool onlyOfficial) const -{ - return basicWrapperFunctionImplementation(this, "mods", onlyOfficial); -} - -QString UnmanagedModsWrapper::displayName(const QString& modName) const -{ - return basicWrapperFunctionImplementation(this, "displayName", modName); -} - -QFileInfo UnmanagedModsWrapper::referenceFile(const QString& modName) const -{ - return basicWrapperFunctionImplementation(this, "referenceFile", - modName); -} - -QStringList UnmanagedModsWrapper::secondaryFiles(const QString& modName) const -{ - return basicWrapperFunctionImplementation(this, "secondaryFiles", - modName); -} -/// end UnmanagedMods Wrapper -///////////////////////////// - -game_features_map_from_python::game_features_map_from_python() -{ - boost::python::converter::registry::push_back( - &convertible, &construct, - boost::python::type_id>()); -} - -void* game_features_map_from_python::convertible(PyObject* objPtr) -{ - return PyDict_Check(objPtr) ? objPtr : nullptr; -} - -template -void insertGameFeature(std::map& map, - const boost::python::object& pyObject) -{ - map[std::type_index(typeid(T))] = boost::python::extract(pyObject)(); -} - -void game_features_map_from_python::construct( - PyObject* objPtr, boost::python::converter::rvalue_from_python_stage1_data* data) -{ - void* storage = ((boost::python::converter::rvalue_from_python_storage< - std::map>*)data) - ->storage.bytes; - std::map* result = - new (storage) std::map(); - boost::python::dict source( - boost::python::handle<>(boost::python::borrowed(objPtr))); - boost::python::list keys = source.keys(); - int len = boost::python::len(keys); - for (int i = 0; i < len; ++i) { - boost::python::object pyKey = keys[i]; - boost::python::object pyValue = source[pyKey]; - - boost::mp11::mp_for_each< - // Must user pointers because mp_for_each construct object: - boost::mp11::mp_transform>( - [&](auto* pt) { - using T = std::remove_pointer_t; - boost::python::extract extract(pyValue); - if (extract.check()) { - (*result)[std::type_index(typeid(T))] = extract(); - } - }); - } - - data->convertible = storage; -} - -void registerGameFeaturesPythonConverters() -{ - namespace bpy = boost::python; - - game_features_map_from_python(); - - // Features require defs for all methods as Python can access C++ features - bpy::class_("BSAInvalidation") - .def("isInvalidationBSA", - bpy::pure_virtual(&BSAInvalidation::isInvalidationBSA), bpy::arg("name")) - .def("deactivate", bpy::pure_virtual(&BSAInvalidation::deactivate), - bpy::arg("profile")) - .def("activate", bpy::pure_virtual(&BSAInvalidation::activate), - bpy::arg("profile")); - - bpy::class_("DataArchives") - .def("vanillaArchives", bpy::pure_virtual(&DataArchives::vanillaArchives)) - .def("archives", bpy::pure_virtual(&DataArchives::archives), - bpy::arg("profile")) - .def("addArchive", bpy::pure_virtual(&DataArchives::addArchive), - (bpy::arg("profile"), "index", "name")) - .def("removeArchive", bpy::pure_virtual(&DataArchives::removeArchive), - (bpy::arg("profile"), "name")); - - bpy::class_("GamePlugins") - .def("writePluginLists", bpy::pure_virtual(&GamePlugins::writePluginLists), - bpy::arg("plugin_list")) - .def("readPluginLists", bpy::pure_virtual(&GamePlugins::readPluginLists), - bpy::arg("plugin_list")) - .def("getLoadOrder", bpy::pure_virtual(&GamePlugins::getLoadOrder)) - .def("lightPluginsAreSupported", - bpy::pure_virtual(&GamePlugins::lightPluginsAreSupported)); - - bpy::class_("LocalSavegames") - .def("mappings", bpy::pure_virtual(&LocalSavegames::mappings), - bpy::arg("profile_save_dir")) - .def("prepareProfile", bpy::pure_virtual(&LocalSavegames::prepareProfile), - bpy::arg("profile")); - - auto modDataCheckerClass = - bpy::class_("ModDataChecker"); - { - bpy::scope scope = modDataCheckerClass; - - bpy::enum_("CheckReturn") - .value("INVALID", ModDataChecker::CheckReturn::INVALID) - .value("FIXABLE", ModDataChecker::CheckReturn::FIXABLE) - .value("VALID", ModDataChecker::CheckReturn::VALID) - .export_values(); - - modDataCheckerClass - .def("dataLooksValid", bpy::pure_virtual(&ModDataChecker::dataLooksValid), - bpy::arg("filetree")) - .def("fix", bpy::pure_virtual(&ModDataChecker::fix), bpy::arg("filetree")); - } - - { - bpy::scope scope = - bpy::class_("ModDataContent") - .def("getAllContents", - bpy::pure_virtual(&ModDataContent::getAllContents)) - .def("getContentsFor", - bpy::pure_virtual(&ModDataContent::getContentsFor), - bpy::arg("filetree")); - - bpy::class_( - "Content", - bpy::init>( - (bpy::arg("id"), "name", "icon", bpy::arg("filter_only") = false))) - .add_property("id", &ModDataContent::Content::id) - .add_property("name", &ModDataContent::Content::name) - .add_property("icon", &ModDataContent::Content::icon) - .def("isOnlyForFilter", &ModDataContent::Content::isOnlyForFilter); - } - - bpy::class_("SaveGameInfo") - .def("getMissingAssets", bpy::pure_virtual(&SaveGameInfo::getMissingAssets), - bpy::arg("save")) - .def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), - bpy::return_value_policy(), bpy::arg("parent"), - "[optional]"); - - bpy::class_("ScriptExtender") - .def("BinaryName", bpy::pure_virtual(&ScriptExtender::BinaryName)) - .def("PluginPath", bpy::pure_virtual(&ScriptExtender::PluginPath)) - .def("loaderName", bpy::pure_virtual(&ScriptExtender::loaderName)) - .def("loaderPath", bpy::pure_virtual(&ScriptExtender::loaderPath)) - .def("savegameExtension", bpy::pure_virtual(&ScriptExtender::savegameExtension)) - .def("isInstalled", bpy::pure_virtual(&ScriptExtender::isInstalled)) - .def("getExtenderVersion", - bpy::pure_virtual(&ScriptExtender::getExtenderVersion)) - .def("getArch", bpy::pure_virtual(&ScriptExtender::getArch)); - - bpy::class_("UnmanagedMods") - .def("mods", bpy::pure_virtual(&UnmanagedMods::mods), bpy::arg("official_only")) - .def("displayName", bpy::pure_virtual(&UnmanagedMods::displayName), - bpy::arg("mod_name")) - .def("referenceFile", bpy::pure_virtual(&UnmanagedMods::referenceFile), - bpy::arg("mod_name")) - .def("secondaryFiles", bpy::pure_virtual(&UnmanagedMods::secondaryFiles), - bpy::arg("mod_name")); -} diff --git a/src/runner-pybind11/gamefeatureswrappers.h b/src/runner-pybind11/gamefeatureswrappers.h deleted file mode 100644 index 3cd94e5..0000000 --- a/src/runner-pybind11/gamefeatureswrappers.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef GAMEFEATURESWRAPPERS_H -#define GAMEFEATURESWRAPPERS_H - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// this might need turning off if Q_MOC_RUN is defined -#include -#include - -// This is a simple MPL list that contains all the game features in one place: -using MpGameFeaturesList = - boost::mp11::mp_list; - -///////////////////////////// -/// Wrapper declarations - -class BSAInvalidationWrapper : public BSAInvalidation, - public boost::python::wrapper { -public: - static constexpr const char* className = "BSAInvalidationWrapper"; - using boost::python::wrapper::get_override; - - virtual bool isInvalidationBSA(const QString& bsaName) override; - virtual void deactivate(MOBase::IProfile* profile) override; - virtual void activate(MOBase::IProfile* profile) override; - virtual bool prepareProfile(MOBase::IProfile* profile) override; -}; - -class DataArchivesWrapper : public DataArchives, - public boost::python::wrapper { -public: - static constexpr const char* className = "DataArchivesWrapper"; - using boost::python::wrapper::get_override; - - virtual QStringList vanillaArchives() const override; - virtual QStringList archives(const MOBase::IProfile* profile) const override; - virtual void addArchive(MOBase::IProfile* profile, int index, - const QString& archiveName) override; - virtual void removeArchive(MOBase::IProfile* profile, - const QString& archiveName) override; -}; - -class GamePluginsWrapper : public GamePlugins, - public boost::python::wrapper { -public: - static constexpr const char* className = "GamePluginsWrapper"; - using boost::python::wrapper::get_override; - - virtual void writePluginLists(const MOBase::IPluginList* pluginList) override; - virtual void readPluginLists(MOBase::IPluginList* pluginList) override; - virtual QStringList getLoadOrder() override; - virtual bool lightPluginsAreSupported() override; -}; - -class LocalSavegamesWrapper : public LocalSavegames, - public boost::python::wrapper { -public: - static constexpr const char* className = "LocalSavegamesWrapper"; - using boost::python::wrapper::get_override; - - virtual MappingType mappings(const QDir& profileSaveDir) const override; - virtual bool prepareProfile(MOBase::IProfile* profile) override; -}; - -class ModDataCheckerWrapper : public ModDataChecker, - public boost::python::wrapper { -public: - static constexpr const char* className = "ModDataCheckerWrapper"; - using boost::python::wrapper::get_override; - - virtual CheckReturn - dataLooksValid(std::shared_ptr fileTree) const override; - virtual std::shared_ptr - fix(std::shared_ptr fileTree) const override; -}; - -class ModDataContentWrapper : public ModDataContent, - public boost::python::wrapper { -public: - static constexpr const char* className = "ModDataContentWrapper"; - using boost::python::wrapper::get_override; - - virtual std::vector getAllContents() const override; - virtual std::vector - getContentsFor(std::shared_ptr fileTree) const override; -}; - -class SaveGameInfoWrapper : public SaveGameInfo, - public boost::python::wrapper { -public: - static constexpr const char* className = "SaveGameInfoWrapper"; - using boost::python::wrapper::get_override; - - virtual MissingAssets - getMissingAssets(MOBase::ISaveGame const& save) const override; - virtual MOBase::ISaveGameInfoWidget* - getSaveGameWidget(QWidget* parent = 0) const override; - -private: - // We need to keep the python objects alive: - mutable std::map m_SaveGames; - mutable boost::python::object m_SaveGameWidget; -}; - -class ScriptExtenderWrapper : public ScriptExtender, - public boost::python::wrapper { -public: - static constexpr const char* className = "ScriptExtenderWrapper"; - using boost::python::wrapper::get_override; - - virtual QString BinaryName() const override; - virtual QString PluginPath() const override; - virtual QString loaderName() const override; - virtual QString loaderPath() const override; - virtual QString savegameExtension() const override; - virtual bool isInstalled() const override; - virtual QString getExtenderVersion() const override; - virtual WORD getArch() const override; -}; - -class UnmanagedModsWrapper : public UnmanagedMods, - public boost::python::wrapper { -public: - static constexpr const char* className = "UnmanagedModsWrapper"; - using boost::python::wrapper::get_override; - - virtual QStringList mods(bool onlyOfficial) const override; - virtual QString displayName(const QString& modName) const override; - virtual QFileInfo referenceFile(const QString& modName) const override; - virtual QStringList secondaryFiles(const QString& modName) const override; -}; - -/// end Wrapper declarations -///////////////////////////// - -struct game_features_map_from_python { - game_features_map_from_python(); - static void* convertible(PyObject* objPtr); - static void - construct(PyObject* objPtr, - boost::python::converter::rvalue_from_python_stage1_data* data); -}; - -void registerGameFeaturesPythonConverters(); - -#endif // GAMEFEATURESWRAPPERS_H diff --git a/src/runner-pybind11/gilock.cpp b/src/runner-pybind11/gilock.cpp deleted file mode 100644 index 7cdca42..0000000 --- a/src/runner-pybind11/gilock.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "gilock.h" - -GILock::GILock() -{ - m_State = PyGILState_Ensure(); -} - -GILock::~GILock() -{ - PyErr_Clear(); - PyGILState_Release(m_State); -} diff --git a/src/runner-pybind11/gilock.h b/src/runner-pybind11/gilock.h deleted file mode 100644 index 61fd6ee..0000000 --- a/src/runner-pybind11/gilock.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef GILOCK_H -#define GILOCK_H - -#ifndef Q_MOC_RUN -#include -#endif // Q_MOC_RUN - -class GILock { -public: - GILock(); - ~GILock(); - -private: - PyGILState_STATE m_State; -}; - -#endif // GILOCK_H diff --git a/src/runner-pybind11/proxypluginwrappers.cpp b/src/runner-pybind11/proxypluginwrappers.cpp deleted file mode 100644 index de0c938..0000000 --- a/src/runner-pybind11/proxypluginwrappers.cpp +++ /dev/null @@ -1,568 +0,0 @@ -#include "proxypluginwrappers.h" - -#include "gilock.h" -#include -#include - -#include "pythonwrapperutilities.h" -#include "shared_ptr_converter.h" - -#include -#include - -namespace boost { - // See bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2852624 -#if (_MSC_VER == 1900) - template <> - const volatile MOBase::IOrganizer* get_pointer(const volatile MOBase::IOrganizer* p) - { - return p; - } - template <> - const volatile MOBase::IModInterface* - get_pointer(const volatile MOBase::IModInterface* p) - { - return p; - } - template <> - const volatile MOBase::IPluginGame* - get_pointer(const volatile MOBase::IPluginGame* p) - { - return p; - } - template <> - const volatile MOBase::IProfile* get_pointer(const volatile MOBase::IProfile* p) - { - return p; - } - template <> - const volatile MOBase::IModList* get_pointer(const volatile MOBase::IModList* p) - { - return p; - } - template <> - const volatile MOBase::IPluginList* - get_pointer(const volatile MOBase::IPluginList* p) - { - return p; - } - template <> - const volatile MOBase::IDownloadManager* - get_pointer(const volatile MOBase::IDownloadManager* p) - { - return p; - } - template <> - const volatile MOBase::IModRepositoryBridge* - get_pointer(const volatile MOBase::IModRepositoryBridge* p) - { - return p; - } -#endif -} // namespace boost - -using namespace MOBase; - -// See COMMON_I_PLUGIN_WRAPPER_DECLARATIONS__IMPL in proxypluginwrappers.h for -// explanation on the "include_requirements". -#define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS_IMPL(class_name, include_requirements) \ - bool class_name::init(MOBase::IOrganizer* moInfo) \ - { \ - return basicWrapperFunctionImplementation(this, "init", \ - boost::python::ptr(moInfo)); \ - } \ - \ - QString class_name::name() const \ - { \ - return basicWrapperFunctionImplementation(this, "name"); \ - } \ - \ - QString class_name::localizedName() const \ - { \ - return basicWrapperFunctionImplementationWithDefault( \ - this, &class_name::localizedName_Default, "localizedName"); \ - } \ - \ - QString class_name::master() const \ - { \ - return basicWrapperFunctionImplementationWithDefault( \ - this, &class_name::master_Default, "master"); \ - } \ - \ - QString class_name::author() const \ - { \ - return basicWrapperFunctionImplementation(this, "author"); \ - } \ - \ - QString class_name::description() const \ - { \ - return basicWrapperFunctionImplementation(this, "description"); \ - } \ - \ - MOBase::VersionInfo class_name::version() const \ - { \ - return basicWrapperFunctionImplementation(this, \ - "version"); \ - } \ - \ - QList class_name::settings() const \ - { \ - return basicWrapperFunctionImplementation>( \ - this, "settings"); \ - } \ - QString class_name::localizedName_Default() const \ - { \ - return IPlugin::localizedName(); \ - } \ - QString class_name::master_Default() const { return IPlugin::master(); } \ - BOOST_PP_EXPR_IF( \ - include_requirements, \ - std::vector> \ - class_name::requirements() const { \ - return basicWrapperFunctionImplementationWithDefault< \ - std::vector>>( \ - this, &class_name::requirements_Default, "requirements"); \ - } std::vector> \ - class_name::requirements_Default() const { \ - return IPlugin::requirements(); \ - } bool class_name::enabledByDefault() const { \ - return basicWrapperFunctionImplementationWithDefault( \ - this, &class_name::enabledByDefault_Default, \ - "enabledByDefault"); \ - } bool class_name::enabledByDefault_Default() \ - const { return IPlugin::enabledByDefault(); }) - -#define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(class_name) \ - COMMON_I_PLUGIN_WRAPPER_DEFINITIONS_IMPL(class_name, 1) - -/// end COMMON_I_PLUGIN_WRAPPER_DEFINITIONS -///////////////////////////// -/// IPlugin Wrapper - -COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginWrapper) -/// end IPlugin Wrapper -///////////////////////////////////// -/// IPluginDiagnose Wrapper - -COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginDiagnoseWrapper) - -std::vector IPluginDiagnoseWrapper::activeProblems() const -{ - return basicWrapperFunctionImplementation>( - this, "activeProblems"); -} - -QString IPluginDiagnoseWrapper::shortDescription(unsigned int key) const -{ - return basicWrapperFunctionImplementation(this, "shortDescription", key); -} - -QString IPluginDiagnoseWrapper::fullDescription(unsigned int key) const -{ - return basicWrapperFunctionImplementation(this, "fullDescription", key); -} - -bool IPluginDiagnoseWrapper::hasGuidedFix(unsigned int key) const -{ - return basicWrapperFunctionImplementation(this, "hasGuidedFix", key); -} - -void IPluginDiagnoseWrapper::startGuidedFix(unsigned int key) const -{ - basicWrapperFunctionImplementation(this, "startGuidedFix", key); -} - -/// end IPluginDiagnose Wrapper -///////////////////////////////////// -/// IPluginFileMapper Wrapper - -COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginFileMapperWrapper) - -MappingType IPluginFileMapperWrapper::mappings() const -{ - return basicWrapperFunctionImplementation(this, "mappings"); -} -/// end IPluginFileMapper Wrapper -///////////////////////////////////// -/// IPluginGame Wrapper - -void IPluginGameWrapper::detectGame() -{ - return basicWrapperFunctionImplementation(this, "detectGame"); -} - -QString IPluginGameWrapper::gameName() const -{ - return basicWrapperFunctionImplementation(this, "gameName"); -} - -void IPluginGameWrapper::initializeProfile(const QDir& directory, - ProfileSettings settings) const -{ - basicWrapperFunctionImplementation(this, "initializeProfile", directory, - settings); -} - -std::vector> -IPluginGameWrapper::listSaves(QDir folder) const -{ - // Why do I not need to hold python references here? Is it because those are wrapped - // in shared_ptr? - return basicWrapperFunctionImplementation< - std::vector>>(this, "listSaves", - folder); -} - -bool IPluginGameWrapper::isInstalled() const -{ - return basicWrapperFunctionImplementation(this, "isInstalled"); -} - -QIcon IPluginGameWrapper::gameIcon() const -{ - return basicWrapperFunctionImplementation(this, "gameIcon"); -} - -QDir IPluginGameWrapper::gameDirectory() const -{ - return basicWrapperFunctionImplementation(this, "gameDirectory"); -} - -QDir IPluginGameWrapper::dataDirectory() const -{ - return basicWrapperFunctionImplementation(this, "dataDirectory"); -} - -void IPluginGameWrapper::setGamePath(const QString& path) -{ - basicWrapperFunctionImplementation(this, "setGamePath", path); -} - -QDir IPluginGameWrapper::documentsDirectory() const -{ - return basicWrapperFunctionImplementation(this, "documentsDirectory"); -} - -QDir IPluginGameWrapper::savesDirectory() const -{ - return basicWrapperFunctionImplementation(this, "savesDirectory"); -} - -QList IPluginGameWrapper::executables() const -{ - return basicWrapperFunctionImplementation>( - this, "executables"); -} - -QList -IPluginGameWrapper::executableForcedLoads() const -{ - return basicWrapperFunctionImplementation< - QList>(this, "executableForcedLoads"); -} - -QString IPluginGameWrapper::steamAPPId() const -{ - return basicWrapperFunctionImplementation(this, "steamAPPId"); -} - -QStringList IPluginGameWrapper::primaryPlugins() const -{ - return basicWrapperFunctionImplementation(this, "primaryPlugins"); -} - -QStringList IPluginGameWrapper::gameVariants() const -{ - return basicWrapperFunctionImplementation(this, "gameVariants"); -} - -void IPluginGameWrapper::setGameVariant(const QString& variant) -{ - basicWrapperFunctionImplementation(this, "setGameVariant", variant); -} - -QString IPluginGameWrapper::binaryName() const -{ - return basicWrapperFunctionImplementation(this, "binaryName"); -} - -QString IPluginGameWrapper::gameShortName() const -{ - return basicWrapperFunctionImplementation(this, "gameShortName"); -} - -QStringList IPluginGameWrapper::primarySources() const -{ - return basicWrapperFunctionImplementation(this, "primarySources"); -} - -QStringList IPluginGameWrapper::validShortNames() const -{ - return basicWrapperFunctionImplementation(this, "validShortNames"); -} - -QString IPluginGameWrapper::gameNexusName() const -{ - return basicWrapperFunctionImplementation(this, "gameNexusName"); -} - -QStringList IPluginGameWrapper::iniFiles() const -{ - return basicWrapperFunctionImplementation(this, "iniFiles"); -} - -QStringList IPluginGameWrapper::DLCPlugins() const -{ - return basicWrapperFunctionImplementation(this, "DLCPlugins"); -} - -QStringList IPluginGameWrapper::CCPlugins() const -{ - return basicWrapperFunctionImplementation(this, "CCPlugins"); -} - -IPluginGame::LoadOrderMechanism IPluginGameWrapper::loadOrderMechanism() const -{ - return basicWrapperFunctionImplementation( - this, "loadOrderMechanism"); -} - -IPluginGame::SortMechanism IPluginGameWrapper::sortMechanism() const -{ - return basicWrapperFunctionImplementation( - this, "sortMechanism"); -} - -int IPluginGameWrapper::nexusModOrganizerID() const -{ - return basicWrapperFunctionImplementation(this, "nexusModOrganizerID"); -} - -int IPluginGameWrapper::nexusGameID() const -{ - return basicWrapperFunctionImplementation(this, "nexusGameID"); -} - -bool IPluginGameWrapper::looksValid(QDir const& dir) const -{ - return basicWrapperFunctionImplementation(this, "looksValid", dir); -} - -QString IPluginGameWrapper::gameVersion() const -{ - return basicWrapperFunctionImplementation(this, "gameVersion"); -} - -QString IPluginGameWrapper::getLauncherName() const -{ - return basicWrapperFunctionImplementation(this, "getLauncherName"); -} - -COMMON_I_PLUGIN_WRAPPER_DEFINITIONS_IMPL(IPluginGameWrapper, 0) - -std::map IPluginGameWrapper::featureList() const -{ - return basicWrapperFunctionImplementation>( - this, "_featureList"); -} -/// end IPluginGame Wrapper -///////////////////////////////////// -/// IPluginInstaller macro - -#define COMMON_I_PLUGIN_INSTALLER_WRAPPER_DEFINITIONS(class_name) \ - unsigned int class_name::priority() const \ - { \ - return basicWrapperFunctionImplementation(this, "priority"); \ - } \ - bool class_name::isManualInstaller() const \ - { \ - return basicWrapperFunctionImplementation(this, "isManualInstaller"); \ - } \ - void class_name::onInstallationStart(QString const& archive, bool reinstallation, \ - MOBase::IModInterface* currentMod) \ - { \ - basicWrapperFunctionImplementationWithDefault( \ - this, &class_name::onInstallationStart_Default, "onInstallationStart", \ - archive, reinstallation, boost::python::ptr(currentMod)); \ - } \ - void class_name::onInstallationEnd(EInstallResult result, \ - MOBase::IModInterface* newMod) \ - { \ - basicWrapperFunctionImplementationWithDefault( \ - this, &class_name::onInstallationEnd_Default, "onInstallationEnd", result, \ - boost::python::ptr(newMod)); \ - } \ - bool class_name::isArchiveSupported(std::shared_ptr tree) const \ - { \ - return basicWrapperFunctionImplementation(this, "isArchiveSupported", \ - tree); \ - } - -/// end IPluginInstaller macro -///////////////////////////////////// -/// IPluginInstaller Wrapper - -COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginInstallerSimpleWrapper) -COMMON_I_PLUGIN_INSTALLER_WRAPPER_DEFINITIONS(IPluginInstallerSimpleWrapper) - -IPluginInstaller::EInstallResult -IPluginInstallerSimpleWrapper::install(GuessedValue& modName, - std::shared_ptr& tree, - QString& version, int& nexusID) -{ - namespace bpy = boost::python; - - using return_type = - std::variant, - std::tuple, QString, int>>; - auto ret = basicWrapperFunctionImplementation( - this, "install", boost::ref(modName), tree, version, nexusID); - - auto result = std::visit( - [&](auto const& t) { - using type = std::decay_t; - if constexpr (std::is_same_v) { - return t; - } - else if constexpr (std::is_same_v>) { - tree = t; - return IPluginInstaller::RESULT_SUCCESS; - } - else if constexpr (std::is_same_v< - type, std::tuple, QString, - int>>) { - tree = std::get<1>(t); - version = std::get<2>(t); - nexusID = std::get<3>(t); - return std::get<0>(t); - } - }, - ret); - - tree = utils::clean_shared_ptr(tree); - return result; -} - -/// end IPluginInstallerSimple Wrapper -///////////////////////////////////// -/// IPluginInstallerCustom Wrapper -COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginInstallerCustomWrapper) -COMMON_I_PLUGIN_INSTALLER_WRAPPER_DEFINITIONS(IPluginInstallerCustomWrapper) - -bool IPluginInstallerCustomWrapper::isArchiveSupported(const QString& archiveName) const -{ - return basicWrapperFunctionImplementation(this, "isArchiveSupported", - archiveName); -} - -std::set IPluginInstallerCustomWrapper::supportedExtensions() const -{ - return basicWrapperFunctionImplementation>(this, - "supportedExtensions"); -} - -IPluginInstaller::EInstallResult -IPluginInstallerCustomWrapper::install(GuessedValue& modName, QString gameName, - const QString& archiveName, - const QString& version, int modID) -{ - // Note: This requires far more less trouble than the "Simple" installer version - // since 1) there is no tree and 2) there version and modId cannot be modified: - return basicWrapperFunctionImplementation( - this, "install", boost::ref(modName), gameName, archiveName, version, modID); -} - -/// end IPluginInstallerCustom Wrapper -///////////////////////////// -/// IPluginModPage Wrapper - -COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginModPageWrapper) - -QString IPluginModPageWrapper::displayName() const -{ - return basicWrapperFunctionImplementation(this, "displayName"); -} - -QIcon IPluginModPageWrapper::icon() const -{ - return basicWrapperFunctionImplementation(this, "icon"); -} - -QUrl IPluginModPageWrapper::pageURL() const -{ - return basicWrapperFunctionImplementation(this, "pageURL"); -} - -bool IPluginModPageWrapper::useIntegratedBrowser() const -{ - return basicWrapperFunctionImplementation(this, "useIntegratedBrowser"); -} - -bool IPluginModPageWrapper::handlesDownload( - const QUrl& pageURL, const QUrl& downloadURL, - MOBase::ModRepositoryFileInfo& fileInfo) const -{ - return basicWrapperFunctionImplementation(this, "handlesDownload", pageURL, - downloadURL, fileInfo); -} - -void IPluginModPageWrapper::setParentWidget(QWidget* widget) -{ - basicWrapperFunctionImplementationWithDefault( - this, &IPluginModPageWrapper::setParentWidget_Default, "setParentWidget", - widget); -} -/// end IPluginModPage Wrapper -///////////////////////////// -/// IPluginPreview Wrapper - -COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginPreviewWrapper) - -std::set IPluginPreviewWrapper::supportedExtensions() const -{ - return basicWrapperFunctionImplementation>(this, - "supportedExtensions"); -} - -QWidget* IPluginPreviewWrapper::genFilePreview(const QString& fileName, - const QSize& maxSize) const -{ - // We need responsibility for deleting the QWidget to be transferred to C++: - return wrapperFunctionImplementationWithApiTransfer( - this, "genFilePreview", fileName, maxSize); -} -/// end IPluginPreview Wrapper -///////////////////////////// -/// IPluginTool Wrapper - -COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginToolWrapper) - -QString IPluginToolWrapper::displayName() const -{ - return basicWrapperFunctionImplementation(this, "displayName"); -} - -QString IPluginToolWrapper::tooltip() const -{ - return basicWrapperFunctionImplementation(this, "tooltip"); -} - -QIcon IPluginToolWrapper::icon() const -{ - return basicWrapperFunctionImplementation(this, "icon"); -} - -void IPluginToolWrapper::setParentWidget(QWidget* parent) -{ - basicWrapperFunctionImplementationWithDefault( - this, &IPluginToolWrapper::setParentWidget_Default, "setParentWidget", parent); -} - -void IPluginToolWrapper::display() const -{ - basicWrapperFunctionImplementation(this, "display"); -} - -/// end IPluginTool Wrapper diff --git a/src/runner-pybind11/proxypluginwrappers.h b/src/runner-pybind11/proxypluginwrappers.h deleted file mode 100644 index dd582aa..0000000 --- a/src/runner-pybind11/proxypluginwrappers.h +++ /dev/null @@ -1,299 +0,0 @@ -#ifndef PROXYPLUGINWRAPPERS_H -#define PROXYPLUGINWRAPPERS_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef Q_MOC_RUN -#include -#include -#endif - -// The wrapper for IPluginGame cannot override requirements or enabledByDefault since -// they're final, so we need to be able to exclude the declarations. -#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(include_requirements) \ -public: \ - virtual bool init(MOBase::IOrganizer* moInfo) override; \ - virtual QString name() const override; \ - virtual QString localizedName() const override; \ - virtual QString master() const override; \ - virtual QString author() const override; \ - virtual QString description() const override; \ - virtual MOBase::VersionInfo version() const override; \ - virtual QList settings() const override; \ - QString localizedName_Default() const; \ - QString master_Default() const; \ - BOOST_PP_EXPR_IF( \ - include_requirements, \ - virtual std::vector> \ - requirements() const override; \ - std::vector> \ - requirements_Default() const; \ - virtual bool enabledByDefault() const override; \ - bool enabledByDefault_Default() const;) - -#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS \ - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(1) - -// Even though the base interface is not a QObject, this has to be because we have no -// way to pass Mod Organizer a plugin that implements multiple interfaces. QObject must -// be the first base class because moc assumes the first base class is a QObject -class IPluginWrapper : public QObject, - public MOBase::IPlugin, - public boost::python::wrapper { - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin) - - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS -public: - static constexpr const char* className = "IPluginWrapper"; - using boost::python::wrapper::get_override; -}; - -// Even though the base interface is not an IPlugin or QObject, this has to be because -// we have no way to pass Mod Organizer a plugin that implements multiple interfaces. -// QObject must be the first base class because moc assumes the first base class is a -// QObject -class IPluginDiagnoseWrapper : public QObject, - public MOBase::IPluginDiagnose, - public MOBase::IPlugin, - public boost::python::wrapper { - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginDiagnose) - -public: - static constexpr const char* className = "IPluginDiagnoseWrapper"; - using boost::python::wrapper::get_override; - - // Bring in public scope: - using IPluginDiagnose::invalidate; - - virtual std::vector activeProblems() const override; - virtual QString shortDescription(unsigned int key) const override; - virtual QString fullDescription(unsigned int key) const override; - virtual bool hasGuidedFix(unsigned int key) const override; - virtual void startGuidedFix(unsigned int key) const override; - - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS -}; - -// Even though the base interface is not an IPlugin or QObject, this has to be because -// we have no way to pass Mod Organizer a plugin that implements multiple interfaces. -// QObject must be the first base class because moc assumes the first base class is a -// QObject -class IPluginFileMapperWrapper - : public QObject, - public MOBase::IPluginFileMapper, - public MOBase::IPlugin, - public boost::python::wrapper { - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginFileMapper) - -public: - static constexpr const char* className = "IPluginFileMapperWrapper"; - using boost::python::wrapper::get_override; - - virtual MappingType mappings() const override; - - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS -}; - -class IPluginGameWrapper : public MOBase::IPluginGame, - public boost::python::wrapper { - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame) - -public: - static constexpr const char* className = "IPluginGameWrapper"; - using boost::python::wrapper::get_override; - - virtual void detectGame() override; - virtual QString gameName() const override; - virtual void initializeProfile(const QDir& directory, - ProfileSettings settings) const override; - virtual std::vector> - listSaves(QDir folder) const override; - virtual bool isInstalled() const override; - virtual QIcon gameIcon() const override; - virtual QDir gameDirectory() const override; - virtual QDir dataDirectory() const override; - virtual void setGamePath(const QString& path) override; - virtual QDir documentsDirectory() const override; - virtual QDir savesDirectory() const override; - virtual QList executables() const override; - virtual QList - executableForcedLoads() const override; - virtual QString steamAPPId() const override; - virtual QStringList primaryPlugins() const override; - virtual QStringList gameVariants() const override; - virtual void setGameVariant(const QString& variant) override; - virtual QString binaryName() const override; - virtual QString gameShortName() const override; - virtual QStringList primarySources() const override; - virtual QStringList validShortNames() const override; - virtual QString gameNexusName() const override; - virtual QStringList iniFiles() const override; - virtual QStringList DLCPlugins() const override; - virtual QStringList CCPlugins() const override; - virtual LoadOrderMechanism loadOrderMechanism() const override; - virtual SortMechanism sortMechanism() const override; - virtual int nexusModOrganizerID() const override; - virtual int nexusGameID() const override; - virtual bool looksValid(QDir const& dir) const override; - virtual QString gameVersion() const override; - virtual QString getLauncherName() const override; - - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(0) - -protected: - // Apparently, Python developers interpret an underscore in a function name as it - // being protected - virtual std::map featureList() const override; - - // Thankfully, the default implementation of the templated 'T *feature()' function - // should allow us to get away without overriding it. -}; - -#define COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS \ -public: \ - using IPluginInstaller::parentWidget; \ - using IPluginInstaller::manager; \ - virtual unsigned int priority() const override; \ - virtual bool isManualInstaller() const override; \ - virtual void onInstallationStart(QString const& archive, bool reinstallation, \ - MOBase::IModInterface* currentMod) override; \ - void onInstallationStart_Default(QString const& archive, bool reinstallation, \ - MOBase::IModInterface* currentMod) \ - { \ - return IPluginInstaller::onInstallationStart(archive, reinstallation, \ - currentMod); \ - } \ - virtual void onInstallationEnd(EInstallResult result, \ - MOBase::IModInterface* newMod) override; \ - void onInstallationEnd_Default(EInstallResult result, \ - MOBase::IModInterface* newMod) \ - { \ - return IPluginInstaller::onInstallationEnd(result, newMod); \ - } \ - virtual bool isArchiveSupported(std::shared_ptr tree) \ - const override; - -class IPluginInstallerSimpleWrapper - : public MOBase::IPluginInstallerSimple, - public boost::python::wrapper { - Q_OBJECT - Q_INTERFACES( - MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerSimple) - - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS - COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS - -public: - static constexpr const char* className = "IPluginInstallerSimpleWrapper"; - using boost::python::wrapper::get_override; - - virtual EInstallResult install(MOBase::GuessedValue& modName, - std::shared_ptr& tree, - QString& version, int& nexusID) override; -}; - -class IPluginInstallerCustomWrapper - : public MOBase::IPluginInstallerCustom, - public boost::python::wrapper { - Q_OBJECT - Q_INTERFACES( - MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom) - - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS - - COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS - -public: - static constexpr const char* className = "IPluginInstallerCustomWrapper"; - using boost::python::wrapper::get_override; - - virtual bool isArchiveSupported(const QString& archiveName) const override; - virtual std::set supportedExtensions() const override; - virtual EInstallResult install(MOBase::GuessedValue& modName, - QString gameName, const QString& archiveName, - const QString& version, int modID) override; -}; - -class IPluginModPageWrapper : public MOBase::IPluginModPage, - public boost::python::wrapper { - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginModPage) - - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS -public: - static constexpr const char* className = "IPluginModPageWrapper"; - using boost::python::wrapper::get_override; - - // Bring in public scope: - using IPluginModPage::parentWidget; - - virtual QString displayName() const override; - virtual QIcon icon() const override; - virtual QUrl pageURL() const override; - virtual bool useIntegratedBrowser() const override; - virtual bool - handlesDownload(const QUrl& pageURL, const QUrl& downloadURL, - MOBase::ModRepositoryFileInfo& fileInfo) const override; - virtual void setParentWidget(QWidget* widget) override; - - void setParentWidget_Default(QWidget* parent) - { - IPluginModPage::setParentWidget(parent); - } -}; - -class IPluginPreviewWrapper : public MOBase::IPluginPreview, - public boost::python::wrapper { - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginPreview) - - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS -public: - static constexpr const char* className = "IPluginPreviewWrapper"; - using boost::python::wrapper::get_override; - - virtual std::set supportedExtensions() const override; - virtual QWidget* genFilePreview(const QString& fileName, - const QSize& maxSize) const override; -}; - -class IPluginToolWrapper : public MOBase::IPluginTool, - public boost::python::wrapper { - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool) - - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS -public: - static constexpr const char* className = "IPluginToolWrapper"; - using boost::python::wrapper::get_override; - - // Bring in public scope: - using IPluginTool::parentWidget; - - virtual QString displayName() const override; - virtual QString tooltip() const override; - virtual QIcon icon() const override; - virtual void setParentWidget(QWidget* parent) override; - - void setParentWidget_Default(QWidget* parent) - { - IPluginTool::setParentWidget(parent); - } - -public Q_SLOTS: - virtual void display() const override; -}; - -#endif // PROXYPLUGINWRAPPERS_H diff --git a/src/runner-pybind11/pybind11_qt/details/pybind11_qt_qlist.h b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_qlist.h new file mode 100644 index 0000000..1322149 --- /dev/null +++ b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_qlist.h @@ -0,0 +1,50 @@ +#ifndef PYTHON_PYBIND11_QT_DETAILS_QLIST_HPP +#define PYTHON_PYBIND11_QT_DETAILS_QLIST_HPP + +#include +#include + +namespace pybind11::detail::qt { + + // helper class for QList to construct from any proper iterable + // + template + struct qlist_caster { + using value_conv = make_caster; + + bool load(handle src, bool convert) + { + if (!isinstance(src) || isinstance(src) || + isinstance(src)) { + return false; + } + auto s = reinterpret_borrow(src); + value.clear(); + + if (isinstance(src)) { + value.reserve(s.cast().size()); + } + for (auto it : s) { + value_conv conv; + if (!conv.load(it, convert)) { + return false; + } + value.push_back(cast_op(std::move(conv))); + } + return true; + } + + template + static handle cast(T&& src, return_value_policy policy, handle parent) + { + return list_caster, Value>{}.cast(std::forward(src), policy, + parent); + } + + PYBIND11_TYPE_CASTER(Type, const_name("Iterable[") + value_conv::name + + const_name("]")); + }; + +} // namespace pybind11::detail::qt + +#endif diff --git a/src/runner-pybind11/pybind11_qt/pybind11_qt_containers.h b/src/runner-pybind11/pybind11_qt/pybind11_qt_containers.h index a950c37..4c916e6 100644 --- a/src/runner-pybind11/pybind11_qt/pybind11_qt_containers.h +++ b/src/runner-pybind11/pybind11_qt/pybind11_qt_containers.h @@ -11,45 +11,45 @@ // this needs to be included here to get proper QVariantList and QVariantMap #include "details/pybind11_qt_qmap.h" #include "pybind11_qt_basic.h" +#include "details/pybind11_qt_qlist.h" namespace pybind11::detail { // QList // template - struct type_caster> : list_caster, T> { + struct type_caster> : qt::qlist_caster, T> { }; // QSet // - // template - // struct type_caster> : set_caster, T> { - // }; + template + struct type_caster> : set_caster, T> { + }; // QMap // - // template - // struct type_caster> : qt::qmap_caster, K, V> { - // }; + template + struct type_caster> : qt::qmap_caster, K, V> { + }; // QStringList // template <> - struct type_caster : list_caster { + struct type_caster : qt::qlist_caster { }; // QVariantList // - // template <> - // struct type_caster : list_caster { - // }; + template <> + struct type_caster : qt::qlist_caster { + }; // QVariantMap // - // template <> - // struct type_caster - // : qt::qmap_caster { - // }; + template <> + struct type_caster : qt::qmap_caster { + }; } // namespace pybind11::detail diff --git a/src/runner-pybind11/pythonrunner_en.ts b/src/runner-pybind11/pythonrunner_en.ts deleted file mode 100644 index ac63bc9..0000000 --- a/src/runner-pybind11/pythonrunner_en.ts +++ /dev/null @@ -1,17 +0,0 @@ - - - - - QObject - - - An unexpected C++ exception was thrown in python code. - - - - - An unknown exception was thrown in python code. - - - - diff --git a/src/runner-pybind11/pythonwrapperutilities.h b/src/runner-pybind11/pythonwrapperutilities.h deleted file mode 100644 index bcd42de..0000000 --- a/src/runner-pybind11/pythonwrapperutilities.h +++ /dev/null @@ -1,202 +0,0 @@ -#ifndef PYTHONWRAPPERUTILITIES_H -#define PYTHONWRAPPERUTILITIES_H - -#include - -#include - -#include -#include - -#include "error.h" -#include "gilock.h" -// #include "pybind11_qt/pybind11_qt.h" - -namespace details { - - /** - * @brief Common stuffs for all basicWrapperFunction methods. - */ - template - ReturnType wrapperFunctionImplementation(WrapperTypePtr wrapper, bool apiTransfer, - Fn fn, boost::python::object* objPtr, - const char* methodName, Args... args) - { - boost::python::override implementation = [&]() { - GILock lock; - return wrapper->get_override(methodName); - }(); - if (!implementation) { - if constexpr (std::is_same_v) { - throw pyexcept::MissingImplementation(wrapper->className, methodName); - } - else { - return std::invoke(fn, wrapper, args...); - } - } - - GILock lock; - try { - boost::python::object result = implementation(args...); - if (objPtr) { - *objPtr = result; - } - else if (apiTransfer) { - // pybind11::detail::qt::sipAPI()->api_transfer_to(result.ptr(), - // Py_None); - } - if constexpr (!std::is_same_v) { - return boost::python::extract(result)(); - } - } - catch (const boost::python::error_already_set&) { - throw pyexcept::PythonError(""); - } - catch (...) { - throw pyexcept::UnknownException(); - } - } - -} // namespace details - -/** - * @brief Call the given method on the wrapper with the given arguments, with - * proper exception handling. - * - * @param wrapper The wrapper object to use to retrieve the python method. Must - * have a publicly available `className` attribute. - * @param methodName The name of the method. - * @param args... Arguments for the method. - * - * @return the result of calling the given Python method on the wrapper. - * - * @throw pyexcept::MissingImplementation if the method does not exist. - * @throw pyexcept::PythonError if an error occurs while executing the python - * method. - * @throw pyexecpt::UnknownException if an unknown error occurs. - */ -template -ReturnType basicWrapperFunctionImplementation(const WrapperType* wrapper, - const char* methodName, Args... args) -{ - return details::wrapperFunctionImplementation( - wrapper, false, nullptr, nullptr, methodName, args...); -} - -/** - * @brief Call the given method on the wrapper with the given arguments, with - * proper exception handling, and store the intermediate result in the given - * python object. - * - * @param wrapper The wrapper object to use to retrieve the python method. Must - * have a publicly available `className` attribute. - * @param ref Python object to which the result of `get_override()` should be - * stored. - * @param methodName The name of the method. - * @param args... Arguments for the method. - * - * @return the result of calling the given Python method on the wrapper. - * - * @throw pyexcept::MissingImplementation if the method does not exist. - * @throw pyexcept::PythonError if an error occurs while executing the python - * method. - * @throw pyexecpt::UnknownException if an unknown error occurs. - */ -template -ReturnType basicWrapperFunctionImplementation(const WrapperType* wrapper, - boost::python::object& ref, - const char* methodName, Args... args) -{ - return details::wrapperFunctionImplementation( - wrapper, false, nullptr, &ref, methodName, args...); -} - -/** - * @brief Call the given method on the wrapper with the given arguments, with - * proper exception handling, and transfer the responsibility of the returned - * object to the C++ side. - * - * @param wrapper The wrapper object to use to retrieve the python method. Must - * have a publicly available `className` attribute. - * @param methodName The name of the method. - * @param args... Arguments for the method. - * - * @return the result of calling the given Python method on the wrapper. - * - * @throw pyexcept::MissingImplementation if the method does not exist. - * @throw pyexcept::PythonError if an error occurs while executing the python - * method. - * @throw pyexecpt::UnknownException if an unknown error occurs. - */ -template -ReturnType wrapperFunctionImplementationWithApiTransfer(const WrapperType* wrapper, - const char* methodName, - Args... args) -{ - return details::wrapperFunctionImplementation( - wrapper, true, nullptr, nullptr, methodName, args...); -} - -/** - * @brief Call the given method on the wrapper with the given arguments, with - * proper exception handling, falling back to the given function if the method - * does not exist. - * - * @param wrapper The wrapper object to use to retrieve the python method. Must - * have a publicly available `className` attribute. - * @param fn The function to call if the method does not exists. - * @param methodName The name of the method. - * @param args... Arguments for the method. - * - * Note: `fn` does not have to be a member-function of `wrapper` but - * `std::invoke(fn, wrapper, args...)` must be valid. - * - * @return the result of calling the given Python method on the wrapper. - * - * @throw pyexcept::PythonError if an error occurs while executing the python - * method. - * @throw pyexecpt::UnknownException if an unknown error occurs. - */ -template -ReturnType basicWrapperFunctionImplementationWithDefault(WrapperTypePtr wrapper, Fn fn, - const char* methodName, - Args... args) -{ - return details::wrapperFunctionImplementation( - wrapper, false, fn, nullptr, methodName, args...); -} - -/** - * @brief Call the given method on the wrapper with the given arguments, with - * proper exception handling, and store the intermediate result in the given - * python object, falling back to the given function if the method does not - * exist. - * - * @param wrapper The wrapper object to use to retrieve the python method. Must - * have a publicly available `className` attribute. - * @param fn The function to call if the method does not exists. - * @param ref Python object to which the result of `get_override()` should be - * stored. - * @param methodName The name of the method. - * @param args... Arguments for the method. - * - * Note: `fn` does not have to be a member-function of `wrapper` but - * `std::invoke(fn, wrapper, args...)` must be valid. - * - * @return the result of calling the given Python method on the wrapper. - * - * @throw pyexcept::PythonError if an error occurs while executing the python - * method. - * @throw pyexecpt::UnknownException if an unknown error occurs. - */ -template -ReturnType -basicWrapperFunctionImplementationWithDefault(const WrapperType* wrapper, Fn fn, - boost::python::object& ref, - const char* methodName, Args... args) -{ - return details::wrapperFunctionImplementation(wrapper, false, fn, &ref, - methodName, args...); -} - -#endif // PYTHONWRAPPERUTILITIES_H diff --git a/src/runner-pybind11/shared_ptr_converter.h b/src/runner-pybind11/shared_ptr_converter.h deleted file mode 100644 index 41f6857..0000000 --- a/src/runner-pybind11/shared_ptr_converter.h +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef PYTHONRUNNER_SHARED_PTR_CONVERTER_H -#define PYTHONRUNNER_SHARED_PTR_CONVERTER_H - -#include - -#include "error.h" -#include "gilock.h" - -namespace utils { - - // Shared pointers are handled in a special way by Boost.Python since they hold - // the wrapped Python object and only release it when the ref counter of the shared - // ptr drops to 0 using shared_ptr_deleter. - // - // Unfortunately for us, this will happen outside of the Python proxy for some - // objects and thus without the GIL lock, making everything crash, so we need a - // custom deleter that holds the GIL while releasing the lock. - // - // Note that this is only useful for Python -> C++ conversion, and without this, - // Boost will automatically wrapped the pointer. The C++ -> Python conversion is - // handled separately by boost::python::register_ptr_to_python. - // - // This is an open Boost.Python problem: https://github.com/boostorg/python/pull/11 - - template - struct shared_ptr_from_python; - - namespace details { - - struct shared_ptr_deleter_with_gil_lock - : boost::python::converter::shared_ptr_deleter { - - using shared_ptr_deleter::shared_ptr_deleter; - - void operator()(void const* o) - { - GILock lock; - shared_ptr_deleter::operator()(o); - } - }; - - template - struct shared_ptr_void; - - template - struct shared_ptr_void> { - using type = std::shared_ptr; - }; - - template - struct shared_ptr_void> { - using type = boost::shared_ptr; - }; - - template - using shared_ptr_void_t = typename shared_ptr_void::type; - - } // namespace details - - template - struct shared_ptr_from_python { - using T = typename SharedPtr::element_type; - - shared_ptr_from_python() - { - using namespace boost::python; - converter::registry::insert( - &convertible, &construct, type_id() -#ifndef BOOST_PYTHON_NO_PY_SIGNATURES - , - &converter::expected_from_python_type_direct::get_pytype -#endif - ); - } - - private: - static void* convertible(PyObject* p) - { - if (p == Py_None) - return p; - - return boost::python::converter::get_lvalue_from_python( - p, boost::python::converter::registered::converters); - } - - static void - construct(PyObject* source, - boost::python::converter::rvalue_from_python_stage1_data* data) - { - using namespace boost::python; - void* const storage = - ((converter::rvalue_from_python_storage*)data) - ->storage.bytes; - // Deal with the "None" case. - if (data->convertible == source) - new (storage) SharedPtr(); - else { - details::shared_ptr_void_t hold_convertible_ref_count( - (void*)0, details::shared_ptr_deleter_with_gil_lock( - handle<>(borrowed(source)))); - // use aliasing constructor - new (storage) SharedPtr(hold_convertible_ref_count, - static_cast(data->convertible)); - } - - data->convertible = storage; - } - }; - - // release the bpy::object associated with the deleter of the given shared_ptr, - // if the given shared_ptr has a Boost.Python deleter - // - // this should only be used when returning from Python objects that have been - // created on the C++ side, e.g. if IFileTree.createOrphanTree() from Python and - // then return the tree - // - // for reason yet to be known, Boost.Python had a custom deleter in this case that - // tries to delete the bpy::object and fails, so we have to release the object - // manually - // - template - SharedPtr clean_shared_ptr(SharedPtr&& ptr) - { - if (auto* d = get_deleter(ptr); - d != nullptr) { - // we cannot do a proper reset() here, even with the GIL lock, for unknown - // reason, so we only release - // - // this might create lost references to Python object but this should not - // happen too often so hopefully it's not a big issue - // - d->owner.release(); - } - return ptr; - } - -} // namespace utils - -#endif \ No newline at end of file diff --git a/src/runner-pybind11/wrappers/game_features.cpp b/src/runner-pybind11/wrappers/game_features.cpp index 40b6dae..d8e77a7 100644 --- a/src/runner-pybind11/wrappers/game_features.cpp +++ b/src/runner-pybind11/wrappers/game_features.cpp @@ -1,5 +1,7 @@ #include "wrappers.h" +#include + #include #include #include @@ -7,8 +9,18 @@ #include "../pybind11_qt/pybind11_qt.h" +#include +#include + +#include +#include +#include +#include #include +#include +#include #include +#include #include "pyfiletree.h" @@ -18,6 +30,85 @@ using namespace pybind11::literals; namespace mo2::python { + class PyBSAInvalidation : public BSAInvalidation { + public: + bool isInvalidationBSA(const QString& bsaName) override + { + PYBIND11_OVERRIDE_PURE(bool, BSAInvalidation, isInvalidationBSA, bsaName); + } + void deactivate(MOBase::IProfile* profile) override + { + PYBIND11_OVERRIDE_PURE(void, BSAInvalidation, deactivate, profile); + } + void activate(MOBase::IProfile* profile) override + { + PYBIND11_OVERRIDE_PURE(void, BSAInvalidation, activate, profile); + } + bool prepareProfile(MOBase::IProfile* profile) override + { + PYBIND11_OVERRIDE_PURE(bool, BSAInvalidation, prepareProfile, profile); + } + }; + + class PyDataArchives : public DataArchives { + public: + QStringList vanillaArchives() const override + { + PYBIND11_OVERRIDE_PURE(QStringList, DataArchives, vanillaArchives, ); + } + QStringList archives(const MOBase::IProfile* profile) const override + { + PYBIND11_OVERRIDE_PURE(QStringList, DataArchives, archives, profile); + } + void addArchive(MOBase::IProfile* profile, int index, + const QString& archiveName) override + { + PYBIND11_OVERRIDE_PURE(void, DataArchives, addArchive, profile, index, + archiveName); + } + void removeArchive(MOBase::IProfile* profile, + const QString& archiveName) override + { + PYBIND11_OVERRIDE_PURE(void, DataArchives, removeArchive, profile, + archiveName); + } + }; + + class PyGamePlugins : public GamePlugins { + public: + void writePluginLists(const MOBase::IPluginList* pluginList) override + { + PYBIND11_OVERRIDE_PURE(void, GamePlugins, writePluginLists, pluginList); + } + void readPluginLists(MOBase::IPluginList* pluginList) override + { + // TODO: cannot update plugin list or create one from Python so this is + // useless + PYBIND11_OVERRIDE_PURE(void, GamePlugins, readPluginLists, pluginList); + } + QStringList getLoadOrder() override + { + PYBIND11_OVERRIDE_PURE(QStringList, GamePlugins, getLoadOrder, ); + } + bool lightPluginsAreSupported() override + { + PYBIND11_OVERRIDE_PURE(bool, GamePlugins, lightPluginsAreSupported, ); + } + }; + + class PyLocalSavegames : public LocalSavegames { + public: + MappingType mappings(const QDir& profileSaveDir) const override + { + PYBIND11_OVERRIDE_PURE(MappingType, LocalSavegames, mappings, + profileSaveDir); + } + bool prepareProfile(MOBase::IProfile* profile) override + { + PYBIND11_OVERRIDE_PURE(bool, LocalSavegames, prepareProfile, profile); + } + }; + class PyModDataChecker : public ModDataChecker { public: CheckReturn @@ -35,6 +126,37 @@ namespace mo2::python { } }; + class PyModDataContent : public ModDataContent { + public: + std::vector getAllContents() const override + { + PYBIND11_OVERRIDE_PURE(std::vector, ModDataContent, + getAllContents, ); + ; + } + std::vector + getContentsFor(std::shared_ptr fileTree) const override + { + PYBIND11_OVERRIDE_PURE(std::vector, ModDataContent, getContentsFor, + fileTree); + } + }; + + class PySaveGameInfo : public SaveGameInfo { + public: + MissingAssets getMissingAssets(MOBase::ISaveGame const& save) const override + { + PYBIND11_OVERRIDE_PURE(MissingAssets, SaveGameInfo, getMissingAssets, + &save); + } + ISaveGameInfoWidget* getSaveGameWidget(QWidget* parent = 0) const override + { + // TODO: transfer ownership + PYBIND11_OVERRIDE_PURE(ISaveGameInfoWidget*, SaveGameInfo, + getSaveGameWidget, parent); + } + }; + class PyScriptExtender : public ScriptExtender { public: QString BinaryName() const override @@ -78,8 +200,62 @@ namespace mo2::python { } }; + class PyPyUnmanagedMods : public UnmanagedMods { + public: + QStringList mods(bool onlyOfficial) const override + { + PYBIND11_OVERRIDE_PURE(QStringList, UnmanagedMods, mods, onlyOfficial); + } + QString displayName(const QString& modName) const override + { + PYBIND11_OVERRIDE_PURE(QString, UnmanagedMods, displayName, modName); + } + QFileInfo referenceFile(const QString& modName) const override + { + PYBIND11_OVERRIDE_PURE(QFileInfo, UnmanagedMods, referenceFile, modName); + } + QStringList secondaryFiles(const QString& modName) const override + { + PYBIND11_OVERRIDE_PURE(QStringList, UnmanagedMods, secondaryFiles, modName); + } + }; + void add_game_feature_bindings(pybind11::module_ m) { + // BSAInvalidation + + py::class_(m, "BSAInvalidation") + .def(py::init<>()) + .def("isInvalidationBSA", &BSAInvalidation::isInvalidationBSA, "name"_a) + .def("deactivate", &BSAInvalidation::deactivate, "profile"_a) + .def("activate", &BSAInvalidation::activate, "profile"_a); + + // DataArchives + + py::class_(m, "DataArchives") + .def(py::init<>()) + .def("vanillaArchives", &DataArchives::vanillaArchives) + .def("archives", &DataArchives::archives, "profile"_a) + .def("addArchive", &DataArchives::addArchive, + ("profile"_a, "index", "name")) + .def("removeArchive", &DataArchives::removeArchive, "profile"_a, "name"_a); + + // GamePlugins + + py::class_(m, "GamePlugins") + .def(py::init<>()) + .def("writePluginLists", &GamePlugins::writePluginLists, "plugin_list"_a) + .def("readPluginLists", &GamePlugins::readPluginLists, "plugin_list"_a) + .def("getLoadOrder", &GamePlugins::getLoadOrder) + .def("lightPluginsAreSupported", &GamePlugins::lightPluginsAreSupported); + + // LocalSavegames + + py::class_(m, "LocalSavegames") + .def(py::init<>()) + .def("mappings", &LocalSavegames::mappings, "profile_save_dir"_a) + .def("prepareProfile", &LocalSavegames::prepareProfile, "profile"_a); + // ModDataChecker py::class_ pyModDataChecker(m, @@ -95,6 +271,30 @@ namespace mo2::python { .def("dataLooksValid", &ModDataChecker::dataLooksValid, "filetree"_a) .def("fix", &ModDataChecker::fix, "filetree"_a); + // ModDataContent + py::class_ pyModDataContent(m, + "ModDataContent"); + + py::class_(pyModDataContent, "Content") + .def(py::init(), "id"_a, "name"_a, "icon"_a, + "filter_only"_a = false) + .def_property_readonly("id", &ModDataContent::Content::id) + .def_property_readonly("name", &ModDataContent::Content::name) + .def_property_readonly("icon", &ModDataContent::Content::icon) + .def("isOnlyForFilter", &ModDataContent::Content::isOnlyForFilter); + + pyModDataContent.def(py::init<>()) + .def("getAllContents", &ModDataContent::getAllContents) + .def("getContentsFor", &ModDataContent::getContentsFor, "filetree"_a); + + // SaveGameInfo + + py::class_(m, "SaveGameInfo") + .def(py::init<>()) + .def("getMissingAssets", &SaveGameInfo::getMissingAssets, "save"_a) + .def("getSaveGameWidget", &SaveGameInfo::getSaveGameWidget, + py::return_value_policy::reference, "parent"_a, "[optional]"); + // ScriptExtender py::class_(m, "ScriptExtender") @@ -107,6 +307,81 @@ namespace mo2::python { .def("isInstalled", &ScriptExtender::isInstalled) .def("getExtenderVersion", &ScriptExtender::getExtenderVersion) .def("getArch", &ScriptExtender::getArch); + + // UnmanagedMods + + py::class_(m, "UnmanagedMods") + .def(py::init<>()) + .def("mods", &UnmanagedMods::mods, "official_only"_a) + .def("displayName", &UnmanagedMods::displayName, "mod_name"_a) + .def("referenceFile", &UnmanagedMods::referenceFile, "mod_name"_a) + .def("secondaryFiles", &UnmanagedMods::secondaryFiles, "mod_name"_a); + } + +} // namespace mo2::python + +namespace mo2::python { + + class GameFeaturesHelper { + using GameFeatures = std::tuple< + // BSAInvalidation, DataArchives, GamePlugins, LocalSavegames, + ModDataChecker, + // ModDataContent, SaveGameInfo, + ScriptExtender + // , UnmanagedMods + >; + + template + static void helper(F&& f, std::index_sequence) + { + (f(static_cast*>(nullptr)), ...); + } + + public: + // apply the function f on a null-pointer of type Feature* for each game + // feature + template + static void apply(F&& f) + { + helper(f, std::make_index_sequence>{}); + } + }; + + pybind11::object extract_feature(IPluginGame const& game, pybind11::object type) + { + py::object py_feature = py::none(); + GameFeaturesHelper::apply([&](Feature* feature) { + if (py::type::of().is(type)) { + py_feature = py::cast(game.feature(), + py::return_value_policy::reference); + } + }); + return py_feature; + } + + pybind11::dict extract_feature_list(IPluginGame const& game) + { + // constructing a dict from class name to actual object + py::dict dict; + GameFeaturesHelper::apply([&](Feature* feature) { + dict[py::type::of()] = + py::cast(game.feature(), py::return_value_policy::reference); + }); + return dict; + } + + std::map + convert_feature_list(py::dict const& py_features) + { + std::map features; + GameFeaturesHelper::apply([&](Feature* feature) { + const auto py_type = py::type::of(); + if (py_features.contains(py_type)) { + features[std::type_index(typeid(Feature))] = + py_features[py_type].cast(); + } + }); + return features; } } // namespace mo2::python diff --git a/src/runner-pybind11/wrappers/pyplugins.cpp b/src/runner-pybind11/wrappers/pyplugins.cpp index 77bf97f..1b13505 100644 --- a/src/runner-pybind11/wrappers/pyplugins.cpp +++ b/src/runner-pybind11/wrappers/pyplugins.cpp @@ -4,188 +4,103 @@ #include "pyplugins.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - namespace py = pybind11; using namespace pybind11::literals; using namespace MOBase; namespace mo2::python { - class GameFeaturesHelper { - using GameFeatures = std::tuple< - // BSAInvalidation, DataArchives, GamePlugins, LocalSavegames, - ModDataChecker, - // ModDataContent, SaveGameInfo, - ScriptExtender - // , UnmanagedMods - >; + std::map PyPluginGame::featureList() const + { + py::dict pyFeatures = [this]() { + PYBIND11_OVERRIDE_PURE(py::dict, IPluginGame, featureList, ); + }(); - template - static void helper(F&& f, std::index_sequence) - { - (f(static_cast*>(nullptr)), ...); - } - - public: - // apply the function f on a null-pointer of type Feature* for each game - // feature - template - static void apply(F&& f) - { - helper(f, std::make_index_sequence>{}); - } - }; + return convert_feature_list(pyFeatures); + } // this one is kind of big so it has its own function void add_iplugingame_bindings(pybind11::module_ m) { - py::enum_(m, "LoadOrderMechanism") - .value("FileTime", MOBase::IPluginGame::LoadOrderMechanism::FileTime) - .value("PluginsTxt", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt) + py::enum_(m, "LoadOrderMechanism") + .value("FileTime", IPluginGame::LoadOrderMechanism::FileTime) + .value("PluginsTxt", IPluginGame::LoadOrderMechanism::PluginsTxt) - .value("FILE_TIME", MOBase::IPluginGame::LoadOrderMechanism::FileTime) - .value("PLUGINS_TXT", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt); + .value("FILE_TIME", IPluginGame::LoadOrderMechanism::FileTime) + .value("PLUGINS_TXT", IPluginGame::LoadOrderMechanism::PluginsTxt); - py::enum_(m, "SortMechanism") - .value("NONE", MOBase::IPluginGame::SortMechanism::NONE) - .value("MLOX", MOBase::IPluginGame::SortMechanism::MLOX) - .value("BOSS", MOBase::IPluginGame::SortMechanism::BOSS) - .value("LOOT", MOBase::IPluginGame::SortMechanism::LOOT); + py::enum_(m, "SortMechanism") + .value("NONE", IPluginGame::SortMechanism::NONE) + .value("MLOX", IPluginGame::SortMechanism::MLOX) + .value("BOSS", IPluginGame::SortMechanism::BOSS) + .value("LOOT", IPluginGame::SortMechanism::LOOT); // this does not actually do the conversion, but might be convenient // for accessing the names for enum bits - py::enum_(m, "ProfileSetting") - .value("mods", MOBase::IPluginGame::MODS) - .value("configuration", MOBase::IPluginGame::CONFIGURATION) - .value("savegames", MOBase::IPluginGame::SAVEGAMES) - .value("preferDefaults", MOBase::IPluginGame::PREFER_DEFAULTS) + py::enum_(m, "ProfileSetting") + .value("mods", IPluginGame::MODS) + .value("configuration", IPluginGame::CONFIGURATION) + .value("savegames", IPluginGame::SAVEGAMES) + .value("preferDefaults", 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); + .value("MODS", IPluginGame::MODS) + .value("CONFIGURATION", IPluginGame::CONFIGURATION) + .value("SAVEGAMES", IPluginGame::SAVEGAMES) + .value("PREFER_DEFAULTS", IPluginGame::PREFER_DEFAULTS); - py::class_>( - m, "IPluginGame") + py::class_>( + m, "IPluginGame", py::multiple_inheritance()) + .def(py::init<>()) - .def("featureList", - [](MOBase::IPluginGame* p) { - // constructing a dict from class name to actual object - py::dict dict; - GameFeaturesHelper::apply( - [p, &dict](Feature* feature) { - const auto name = py::type::of().attr("__name__"); - dict[name] = py::cast(p->feature(), - py::return_value_policy::reference); - }); - return dict; - }) + .def("featureList", &extract_feature_list) + .def("feature", &extract_feature, "feature_type"_a, + py::return_value_policy::reference) - .def( - "feature", - [](MOBase::IPluginGame* p, py::object clsObj) { - py::object py_feature = py::none(); - - GameFeaturesHelper::apply([p, &clsObj, &py_feature]( - Feature* feature) { - if (py::type::of().is(clsObj)) { - py_feature = py::cast(p->feature(), - py::return_value_policy::reference); - } - }); - return py_feature; - }, - "feature_type"_a, py::return_value_policy::reference) - - // .def("localizedName", &MOBase::IPlugin::localizedName, - // &IPluginGameWrapper::localizedName_Default) .def("master", - // &MOBase::IPlugin::master, &IPluginGameWrapper::master_Default) - - // .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", &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)) - // - ; + .def("detectGame", &IPluginGame::detectGame) + .def("gameName", &IPluginGame::gameName) + .def("initializeProfile", &IPluginGame::initializeProfile, "directory"_a, + "settings"_a) + .def("listSaves", &IPluginGame::listSaves, "folder"_a) + .def("isInstalled", &IPluginGame::isInstalled) + .def("gameIcon", &IPluginGame::gameIcon) + .def("gameDirectory", &IPluginGame::gameDirectory) + .def("dataDirectory", &IPluginGame::dataDirectory) + .def("setGamePath", &IPluginGame::setGamePath, "path"_a) + .def("documentsDirectory", &IPluginGame::documentsDirectory) + .def("savesDirectory", &IPluginGame::savesDirectory) + .def("executables", &IPluginGame::executables) + .def("executableForcedLoads", &IPluginGame::executableForcedLoads) + .def("steamAPPId", &IPluginGame::steamAPPId) + .def("primaryPlugins", &IPluginGame::primaryPlugins) + .def("gameVariants", &IPluginGame::gameVariants) + .def("setGameVariant", &IPluginGame::setGameVariant, "variant"_a) + .def("binaryName", &IPluginGame::binaryName) + .def("gameShortName", &IPluginGame::gameShortName) + .def("primarySources", &IPluginGame::primarySources) + .def("validShortNames", &IPluginGame::validShortNames) + .def("gameNexusName", &IPluginGame::gameNexusName) + .def("iniFiles", &IPluginGame::iniFiles) + .def("DLCPlugins", &IPluginGame::DLCPlugins) + .def("CCPlugins", &IPluginGame::CCPlugins) + .def("loadOrderMechanism", &IPluginGame::loadOrderMechanism) + .def("sortMechanism", &IPluginGame::sortMechanism) + .def("nexusModOrganizerID", &IPluginGame::nexusModOrganizerID) + .def("nexusGameID", &IPluginGame::nexusGameID) + .def("looksValid", &IPluginGame::looksValid, "directory"_a) + .def("gameVersion", &IPluginGame::gameVersion) + .def("getLauncherName", &IPluginGame::getLauncherName); } // multiple installers void add_iplugininstaller_bindings(pybind11::module_ m) { - py::enum_(m, "InstallResult") - .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); + py::enum_(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); // this is bind but should not be inherited in Python - does not make sense, // having it makes it simpler to bind the Simple and Custom installers @@ -240,16 +155,16 @@ namespace mo2::python { py::class_>( m, "IPluginBase", py::multiple_inheritance()) .def(py::init<>()) - .def("init", &MOBase::IPlugin::init, "organizer"_a) - .def("name", &MOBase::IPlugin::name) - .def("localizedName", &MOBase::IPlugin::localizedName) - .def("master", &MOBase::IPlugin::master) - .def("author", &MOBase::IPlugin::author) - .def("description", &MOBase::IPlugin::description) - .def("version", &MOBase::IPlugin::version) - .def("requirements", &MOBase::IPlugin::requirements) - .def("settings", &MOBase::IPlugin::settings) - .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault); + .def("init", &IPlugin::init, "organizer"_a) + .def("name", &IPlugin::name) + .def("localizedName", &IPlugin::localizedName) + .def("master", &IPlugin::master) + .def("author", &IPlugin::author) + .def("description", &IPlugin::description) + .def("version", &IPlugin::version) + .def("requirements", &IPlugin::requirements) + .def("settings", &IPlugin::settings) + .def("enabledByDefault", &IPlugin::enabledByDefault); py::class_>(m, "IPlugin", @@ -266,14 +181,11 @@ namespace mo2::python { std::unique_ptr>( m, "IPluginDiagnose", py::multiple_inheritance()) .def(py::init<>()) - .def("activeProblems", &MOBase::IPluginDiagnose::activeProblems) - .def("shortDescription", &MOBase::IPluginDiagnose::shortDescription, - py::arg("key")) - .def("fullDescription", &MOBase::IPluginDiagnose::fullDescription, - py::arg("key")) - .def("hasGuidedFix", &MOBase::IPluginDiagnose::hasGuidedFix, py::arg("key")) - .def("startGuidedFix", &MOBase::IPluginDiagnose::startGuidedFix, - py::arg("key")) + .def("activeProblems", &IPluginDiagnose::activeProblems) + .def("shortDescription", &IPluginDiagnose::shortDescription, "key"_a) + .def("fullDescription", &IPluginDiagnose::fullDescription, "key"_a) + .def("hasGuidedFix", &IPluginDiagnose::hasGuidedFix, "key"_a) + .def("startGuidedFix", &IPluginDiagnose::startGuidedFix, "key"_a) .def("_invalidate", &PyPluginDiagnose::invalidate); py::class_(plugin_obj); helper.append_if_instance(plugin_obj); - // helper.append_if_instance(plugin_obj); + helper.append_if_instance(plugin_obj); + + // we need to check the two installer types because IPluginInstaller does not + // inherit QObject, and the trampoline do not have a common ancestor helper.append_if_instance(plugin_obj); helper.append_if_instance(plugin_obj); if (helper.objects.isEmpty()) { - if (py::isinstance(plugin_obj)) { - helper.objects.append(plugin_obj.cast()); - } + helper.append_if_instance(plugin_obj); } return helper.objects; diff --git a/src/runner-pybind11/wrappers/pyplugins.h b/src/runner-pybind11/wrappers/pyplugins.h index 6d3c680..61654de 100644 --- a/src/runner-pybind11/wrappers/pyplugins.h +++ b/src/runner-pybind11/wrappers/pyplugins.h @@ -27,14 +27,9 @@ namespace mo2::python { using namespace MOBase; - class IPyPlugin : public QObject, public IPlugin {}; - - class IPyPluginFileMapper : public IPyPlugin, public IPluginFileMapper {}; - - class IPyPluginDiagnose : public IPyPlugin, public IPluginDiagnose {}; - + // we need two base trampoline because IPluginGame has some final methods. template - class PyPluginBase : public PluginBase { + class PyPluginBaseNoFinal : public PluginBase { public: using PluginBase::PluginBase; @@ -54,11 +49,6 @@ namespace mo2::python { { PYBIND11_OVERRIDE(QString, PluginBase, master, ); } - std::vector> requirements() const - { - PYBIND11_OVERRIDE(std::vector>, - PluginBase, requirements, ); - } QString author() const override { PYBIND11_OVERRIDE_PURE(QString, PluginBase, author, ); @@ -75,12 +65,32 @@ namespace mo2::python { { PYBIND11_OVERRIDE_PURE(QList, PluginBase, settings, ); } + }; + + template + class PyPluginBase : public PyPluginBaseNoFinal { + public: + using PyPluginBaseNoFinal::PyPluginBaseNoFinal; + + std::vector> requirements() const + { + PYBIND11_OVERRIDE(std::vector>, + PluginBase, requirements, ); + } bool enabledByDefault() const override { PYBIND11_OVERRIDE(bool, PluginBase, enabledByDefault, ); } }; + // these classes do not inherit IPlugin or QObject so we need intermediate class to + // get proper bindings + class IPyPlugin : public QObject, public IPlugin {}; + class IPyPluginFileMapper : public IPyPlugin, public IPluginFileMapper {}; + class IPyPluginDiagnose : public IPyPlugin, public IPluginDiagnose {}; + + // PyXXX classes - trampoline classes for the plugins + class PyPlugin : public PyPluginBase { Q_OBJECT Q_INTERFACES(MOBase::IPlugin) @@ -331,6 +341,150 @@ namespace mo2::python { } }; + // game + class PyPluginGame : public PyPluginBaseNoFinal { + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame) + public: + void detectGame() override + { + PYBIND11_OVERRIDE_PURE(void, IPluginGame, detectGame, ); + } + QString gameName() const override + { + PYBIND11_OVERRIDE_PURE(QString, IPluginGame, gameName, ); + } + void initializeProfile(const QDir& directory, + ProfileSettings settings) const override + { + PYBIND11_OVERRIDE_PURE(void, IPluginGame, initializeProfile, directory, + settings); + } + std::vector> + listSaves(QDir folder) const override + { + PYBIND11_OVERRIDE_PURE(std::vector>, + IPluginGame, listSaves, folder); + } + bool isInstalled() const override + { + PYBIND11_OVERRIDE_PURE(bool, IPluginGame, isInstalled, ); + } + QIcon gameIcon() const override + { + PYBIND11_OVERRIDE_PURE(QIcon, IPluginGame, gameIcon, ); + } + QDir gameDirectory() const override + { + PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, gameDirectory, ); + } + QDir dataDirectory() const override + { + PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, dataDirectory, ); + } + void setGamePath(const QString& path) override + { + PYBIND11_OVERRIDE_PURE(void, IPluginGame, setGamePath, path); + } + QDir documentsDirectory() const override + { + PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, documentsDirectory, ); + } + QDir savesDirectory() const override + { + PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, savesDirectory, ); + } + QList executables() const override + { + PYBIND11_OVERRIDE_PURE(QList, IPluginGame, executables, ); + } + QList executableForcedLoads() const override + { + PYBIND11_OVERRIDE_PURE(QList, IPluginGame, + executableForcedLoads, ); + } + QString steamAPPId() const override + { + PYBIND11_OVERRIDE_PURE(QString, IPluginGame, steamAPPId, ); + } + QStringList primaryPlugins() const override + { + PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, primaryPlugins, ); + } + QStringList gameVariants() const override + { + PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, gameVariants, ); + } + void setGameVariant(const QString& variant) override + { + PYBIND11_OVERRIDE_PURE(void, IPluginGame, setGameVariant, variant); + } + QString binaryName() const override + { + PYBIND11_OVERRIDE_PURE(QString, IPluginGame, binaryName, ); + } + QString gameShortName() const override + { + PYBIND11_OVERRIDE_PURE(QString, IPluginGame, gameShortName, ); + } + QStringList primarySources() const override + { + PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, primarySources, ); + } + QStringList validShortNames() const override + { + PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, validShortNames, ); + } + QString gameNexusName() const override + { + PYBIND11_OVERRIDE_PURE(QString, IPluginGame, gameNexuesName, ); + } + QStringList iniFiles() const override + { + PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, iniFiles, ); + } + QStringList DLCPlugins() const override + { + PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, DLCPlugins, ); + } + QStringList CCPlugins() const override + { + PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, CCPlugins, ); + } + LoadOrderMechanism loadOrderMechanism() const override + { + PYBIND11_OVERRIDE_PURE(LoadOrderMechanism, IPluginGame, + loadOrderMechanism, ); + } + SortMechanism sortMechanism() const override + { + PYBIND11_OVERRIDE_PURE(SortMechanism, IPluginGame, sortMechanism, ); + } + int nexusModOrganizerID() const override + { + PYBIND11_OVERRIDE_PURE(int, IPluginGame, nexusModOrganizerID, ); + } + int nexusGameID() const override + { + PYBIND11_OVERRIDE_PURE(int, IPluginGame, nexusGameID, ); + } + bool looksValid(QDir const& dir) const override + { + PYBIND11_OVERRIDE_PURE(bool, IPluginGame, looksValid, dir); + } + QString gameVersion() const override + { + PYBIND11_OVERRIDE_PURE(QString, IPluginGame, gameVersion, ); + } + QString getLauncherName() const override + { + PYBIND11_OVERRIDE_PURE(QString, IPluginGame, getLauncherName, ); + } + + protected: + std::map featureList() const override; + }; + } // namespace mo2::python #endif diff --git a/src/runner-pybind11/wrappers/wrappers.cpp b/src/runner-pybind11/wrappers/wrappers.cpp index 501dbe6..b5a4a3b 100644 --- a/src/runner-pybind11/wrappers/wrappers.cpp +++ b/src/runner-pybind11/wrappers/wrappers.cpp @@ -80,7 +80,7 @@ namespace mo2::python { // ISaveGame // - py::class_(m, "ISaveGame") + py::class_>(m, "ISaveGame") .def(py::init<>()) .def("getFilepath", &ISaveGame::getFilepath) .def("getCreationTime", &ISaveGame::getCreationTime) diff --git a/src/runner-pybind11/wrappers/wrappers.h b/src/runner-pybind11/wrappers/wrappers.h index f0347fc..bef34c8 100644 --- a/src/runner-pybind11/wrappers/wrappers.h +++ b/src/runner-pybind11/wrappers/wrappers.h @@ -1,11 +1,17 @@ #ifndef PYTHON_WRAPPERS_WRAPPERS_H #define PYTHON_WRAPPERS_WRAPPERS_H +#include +#include +#include + #include #include #include +#include + namespace mo2::python { /** @@ -59,6 +65,38 @@ namespace mo2::python { */ void add_game_feature_bindings(pybind11::module_ m); + /** + * @brief Create the game feature corresponding to the given Python type from the + * given game. + * + * @param game Game plugin to extract the feature from. + * @param type Type of the feature to extract. + * + * @return the feature from the game, or None is the game as no such feature. + */ + pybind11::object extract_feature(MOBase::IPluginGame const& game, + pybind11::object type); + + /** + * @brief Create Python dictionary mapping game feature classes to the game feature + * instances for the given game. + * + * @param game Game plugin to extract features from. + * + * @return a python dictionary mapping feature types (in Python) to feature objects. + */ + pybind11::dict extract_feature_list(MOBase::IPluginGame const& game); + + /** + * @brief Convert the given python map of features to a C++ one. + * + * @param py_features Python features to convert (type to feature). + * + * @return the map of features. + */ + std::map + convert_feature_list(pybind11::dict const& py_features); + } // namespace mo2::python #endif // PYTHON_WRAPPERS_WRAPPERS_H