diff --git a/.gitignore b/.gitignore index 8e59a94..cf71be7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -CMakeLists.txt.user edit -build -std*.log -*.bak +CMakeLists.txt.user +/msbuild.log +/*std*.log +/*build diff --git a/src/proxy/CMakeLists.txt b/src/proxy/CMakeLists.txt index 05b526a..4df9b28 100644 --- a/src/proxy/CMakeLists.txt +++ b/src/proxy/CMakeLists.txt @@ -45,11 +45,13 @@ TARGET_LINK_LIBRARIES(${PROJ_NAME} ${Boost_LIBRARIES} uibase) -IF(MSVC) +IF (MSVC) + SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES COMPILE_FLAGS "/std:c++latest") +ENDIF() +IF (MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 4) + # 32 bits SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES LINK_FLAGS "/LARGEADDRESSAWARE") -ELSE(MSVC) - SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES LINK_FLAGS "-std=c++11") -ENDIF(MSVC) +ENDIF() IF (NOT "${OPTIMIZE_COMPILE_FLAGS}" STREQUAL "") SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES COMPILE_FLAGS_RELWITHDEBINFO ${OPTIMIZE_COMPILE_FLAGS}) diff --git a/src/proxy/proxypluginwrappers.cpp b/src/proxy/proxypluginwrappers.cpp deleted file mode 100644 index b9db93b..0000000 --- a/src/proxy/proxypluginwrappers.cpp +++ /dev/null @@ -1,213 +0,0 @@ -#include "proxypluginwrappers.h" -#include -#include -//#include "error.h" -#include "gilock.h" - -namespace bpy = boost::python; - -using namespace MOBase; - - -#define PYCATCH catch (const bpy::error_already_set &) { reportPythonError(); throw MyException("unhandled exception"); }\ - catch (...) { throw MyException("An unknown exception was thrown in python code"); } - - -///////////////////////////// -/// IPluginTool Wrapper - - -bool IPluginToolWrapper::init(MOBase::IOrganizer *moInfo) -{ - try { - return this->get_override("init")(bpy::ptr(moInfo)); - } PYCATCH; -} - -QString IPluginToolWrapper::name() const -{ - try { - return this->get_override("name")(); - } PYCATCH; -} - -QString IPluginToolWrapper::author() const -{ - try { - return this->get_override("author")(); - } PYCATCH; -} - -QString IPluginToolWrapper::description() const -{ - try { - return this->get_override("description")(); - } PYCATCH; -} - -MOBase::VersionInfo IPluginToolWrapper::version() const -{ - try { - return this->get_override("version")(); - } PYCATCH; -} - -bool IPluginToolWrapper::isActive() const -{ - try { - return this->get_override("isActive")(); - } PYCATCH; -} - -QList IPluginToolWrapper::settings() const -{ - try { - return this->get_override("settings")(); - } PYCATCH; -} - -QString IPluginToolWrapper::displayName() const -{ - try { - return this->get_override("displayName")(); - } PYCATCH; -} - -QString IPluginToolWrapper::tooltip() const -{ - try { - return this->get_override("tooltip")(); - } PYCATCH; -} - -QIcon IPluginToolWrapper::icon() const -{ - try { - return this->get_override("icon")(); - } PYCATCH; -} - -void IPluginToolWrapper::setParentWidget(QWidget *parent) -{ - try { - this->get_override("setParentWidget")(parent); - } PYCATCH; -} - -void IPluginToolWrapper::display() const -{ - try { - GILock lock; - - this->get_override("display")(); - } PYCATCH; -} - -/// end IPluginTool Wrapper -///////////////////////////////////// -/// IPluginInstallerCustom Wrapper - - -bool IPluginInstallerCustomWrapper::init(MOBase::IOrganizer *moInfo) -{ - try { - return this->get_override("init")(bpy::ptr(moInfo)); - } PYCATCH; -} - -QString IPluginInstallerCustomWrapper::name() const -{ - try { - return this->get_override("name")(); - } PYCATCH; -} - -QString IPluginInstallerCustomWrapper::author() const -{ - try { - return this->get_override("author")(); - } PYCATCH; -} - -QString IPluginInstallerCustomWrapper::description() const -{ - try { - return this->get_override("description")(); - } PYCATCH; -} - -MOBase::VersionInfo IPluginInstallerCustomWrapper::version() const -{ - try { - return this->get_override("version")(); - } PYCATCH; -} - -bool IPluginInstallerCustomWrapper::isActive() const -{ - try { - return this->get_override("isActive")(); - } PYCATCH; -} - -QList IPluginInstallerCustomWrapper::settings() const -{ - try { - return this->get_override("settings")(); - } PYCATCH; -} - -unsigned int IPluginInstallerCustomWrapper::priority() const -{ - try { - return this->get_override("priority")(); - } PYCATCH; -} - -bool IPluginInstallerCustomWrapper::isManualInstaller() const -{ - try { - return this->get_override("isManualInstaller")(); - } PYCATCH; -} - -bool IPluginInstallerCustomWrapper::isArchiveSupported(const DirectoryTree &tree) const -{ - try { - //return this->get_override("isArchiveSupported")(tree); - return false; - } PYCATCH; -} - -bool IPluginInstallerCustomWrapper::isArchiveSupported(const QString &archiveName) const -{ - try { - return this->get_override("isArchiveSupported")(archiveName); - } PYCATCH; -} - -std::set IPluginInstallerCustomWrapper::supportedExtensions() const -{ - try { - return this->get_override("supportedExtensions")(); - } PYCATCH; -} - - -IPluginInstaller::EInstallResult IPluginInstallerCustomWrapper::install(GuessedValue &modName, const QString &archiveName, - const QString &version, int modID) -{ - try { - return this->get_override("install")(modName, archiveName, version, modID); - } PYCATCH; -} - - -void IPluginInstallerCustomWrapper::setParentWidget(QWidget *parent) -{ - try { - this->get_override("setParentWidget")(parent); - } PYCATCH; -} - - diff --git a/src/proxy/proxypluginwrappers.h b/src/proxy/proxypluginwrappers.h deleted file mode 100644 index 49e87df..0000000 --- a/src/proxy/proxypluginwrappers.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef PROXYPLUGINWRAPPERS_H -#define PROXYPLUGINWRAPPERS_H - - -#include -#include -#include -#include - -#ifndef Q_MOC_RUN -#include -#endif - - -class IPluginWrapper : public boost::python::wrapper -{ -public: - virtual bool init(MOBase::IOrganizer *moInfo); - virtual QString name() const; - virtual QString author() const; - virtual QString description() const; - virtual MOBase::VersionInfo version() const; - virtual bool isActive() const; - virtual QList settings() const; -}; - - -class IPluginToolWrapper: public MOBase::IPluginTool, public boost::python::wrapper -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool) - -public: - virtual bool init(MOBase::IOrganizer *moInfo); - virtual QString name() const; - virtual QString author() const; - virtual QString description() const; - virtual MOBase::VersionInfo version() const; - virtual bool isActive() const; - virtual QList settings() const; - - virtual QString displayName() const; - virtual QString tooltip() const; - virtual QIcon icon() const; - virtual void setParentWidget(QWidget *parent); - -public slots: - virtual void display() const; -}; - - -class IPluginInstallerCustomWrapper: public MOBase::IPluginInstallerCustom, public boost::python::wrapper -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom) - -public: - virtual bool init(MOBase::IOrganizer *moInfo); - virtual QString name() const; - virtual QString author() const; - virtual QString description() const; - virtual MOBase::VersionInfo version() const; - virtual bool isActive() const; - virtual QList settings() const; - - virtual unsigned int priority() const; - virtual bool isManualInstaller() const; - virtual bool isArchiveSupported(const MOBase::DirectoryTree &tree) const; - virtual bool isArchiveSupported(const QString &archiveName) const; - virtual std::set supportedExtensions() const; - virtual EInstallResult install(MOBase::GuessedValue &modName, const QString &archiveName, - const QString &version, int modID); - virtual void setParentWidget(QWidget *parent); - -}; - - - - -#endif // PROXYPLUGINWRAPPERS_H diff --git a/src/proxy/proxypython.cpp b/src/proxy/proxypython.cpp index b28b2a8..d4fe3d6 100644 --- a/src/proxy/proxypython.cpp +++ b/src/proxy/proxypython.cpp @@ -225,13 +225,13 @@ QStringList ProxyPython::pluginList(const QString &pluginPath) const } -QObject *ProxyPython::instantiate(const QString &pluginName) +QList ProxyPython::instantiate(const QString &pluginName) { if (m_Runner != nullptr) { - QObject *result = m_Runner->instantiate(pluginName); + QList result = m_Runner->instantiate(pluginName); return result; } else { - return nullptr; + return QList(); } } diff --git a/src/proxy/proxypython.h b/src/proxy/proxypython.h index e3c8c23..a41f856 100644 --- a/src/proxy/proxypython.h +++ b/src/proxy/proxypython.h @@ -49,7 +49,7 @@ public: virtual QList settings() const; QStringList pluginList(const QString &pluginPath) const; - QObject *instantiate(const QString &pluginName); + QList instantiate(const QString &pluginName); /** * @return the parent widget for newly created dialogs diff --git a/src/proxy/pythonpluginwrapper.cpp b/src/proxy/pythonpluginwrapper.cpp deleted file mode 100644 index 2208f46..0000000 --- a/src/proxy/pythonpluginwrapper.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "pythonpluginwrapper.h" - - -using boost::python::extract; -using boost::python::handle_exception; - - -PythonPluginWrapper::PythonPluginWrapper(const boost::python::object &initFunction, - const boost::python::object &nameFunction, - const boost::python::object &authorFunction, - const boost::python::object &descriptionFunction, - const boost::python::object &versionFunction, - const boost::python::object &isActiveFunction, - const boost::python::object &settingsFunction) - : m_InitFunction(initFunction) - , m_NameFunction(nameFunction) - , m_AuthorFunction(authorFunction) - , m_DescriptionFunction(descriptionFunction) - , m_VersionFunction(versionFunction) - , m_IsActiveFunction(isActiveFunction) - , m_SettingsFunction(settingsFunction) -{ -} - - -void PythonPluginWrapper::reportPythonError() const -{ - if (PyErr_Occurred()) { - PyErr_Print(); - } else { - qCritical("An unexpected C++ exception was thrown in python code"); - } -} - - -bool PythonPluginWrapper::init(MOBase::IOrganizer *moInfo) -{ - try { - return boost::python::extract(m_InitFunction(boost::python::ptr(moInfo))); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return false; - } -} - -QString PythonPluginWrapper::name() const -{ - try { - return boost::python::extract(m_NameFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return QString(); - } -} - -QString PythonPluginWrapper::author() const -{ - try { - return boost::python::extract(m_AuthorFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return QString(); - } -} - -QString PythonPluginWrapper::description() const -{ - try { - return boost::python::extract(m_DescriptionFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return QString(); - } -} - -MOBase::VersionInfo PythonPluginWrapper::version() const -{ - try { - return boost::python::extract(m_VersionFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return MOBase::VersionInfo(); - } -} - -bool PythonPluginWrapper::isActive() const -{ - try { - return boost::python::extract(m_IsActiveFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return false; - } -} - -QList PythonPluginWrapper::settings() const -{ - QList result; - try { - boost::python::object l = m_SettingsFunction(); - if (!l.is_none()) { -// boost::python::list l = extract(temp); - for (int i = 0; i < boost::python::len(l); ++i) { - result.append(extract(l[i])); - } - } - } catch (const boost::python::error_already_set&) { - reportPythonError(); - } - - return result; -} diff --git a/src/proxy/pythonpluginwrapper.h b/src/proxy/pythonpluginwrapper.h deleted file mode 100644 index 19c93a9..0000000 --- a/src/proxy/pythonpluginwrapper.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef PYTHONPLUGINWRAPPER_H -#define PYTHONPLUGINWRAPPER_H - -#include -#ifndef Q_MOC_RUN -#include -#endif - -class PythonPluginWrapper : public virtual MOBase::IPlugin -{ - -public: - PythonPluginWrapper(const boost::python::object &initFunction, - const boost::python::object &nameFunction, - const boost::python::object &authorFunction, - const boost::python::object &descriptionFunction, - const boost::python::object &versionFunction, - const boost::python::object &isActiveFunction, - const boost::python::object &settingsFunction); - - virtual bool init(MOBase::IOrganizer *moInfo); - virtual QString name() const; - virtual QString author() const; - virtual QString description() const; - virtual MOBase::VersionInfo version() const; - virtual bool isActive() const; - virtual QList settings() const; - -protected: - void reportPythonError() const; - -private: - - boost::python::object m_InitFunction; - boost::python::object m_NameFunction; - boost::python::object m_AuthorFunction; - boost::python::object m_DescriptionFunction; - boost::python::object m_VersionFunction; - boost::python::object m_IsActiveFunction; - boost::python::object m_SettingsFunction; - -}; - -#endif // PYTHONPLUGINWRAPPER_H diff --git a/src/proxy/pythontoolwrapper.cpp b/src/proxy/pythontoolwrapper.cpp deleted file mode 100644 index 8690dff..0000000 --- a/src/proxy/pythontoolwrapper.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "pythontoolwrapper.h" -#include -#include - -using boost::python::extract; -using boost::python::handle_exception; - -PythonToolWrapper::PythonToolWrapper(const boost::python::object &initFunction, - const boost::python::object &nameFunction, - const boost::python::object &authorFunction, - const boost::python::object &descriptionFunction, - const boost::python::object &versionFunction, - const boost::python::object &isActiveFunction, - const boost::python::object &settingsFunction, - const boost::python::object &displayNameFunction, - const boost::python::object &tooltipFunction, - const boost::python::object &iconFunction, - const boost::python::object &displayFunction, - const boost::python::object &setParentWidget) - : PythonPluginWrapper(initFunction, nameFunction, authorFunction, descriptionFunction, - versionFunction, isActiveFunction, settingsFunction) - , m_DisplayNameFunction(displayNameFunction) - , m_ToolTipFunction(tooltipFunction) - , m_IconFunction(iconFunction) - , m_DisplayFunction(displayFunction) - , m_SetParentWidget(setParentWidget) -{ -} - - - -QString PythonToolWrapper::displayName() const -{ - try { - return boost::python::extract(m_DisplayNameFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return QString(); - } -} - -QString PythonToolWrapper::tooltip() const -{ - try { - return boost::python::extract(m_ToolTipFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return QString(); - } -} - -QIcon PythonToolWrapper::icon() const -{ - try { - std::vector rows; - boost::python::object l = m_IconFunction(); - if (!l.is_none()) { - for (int i = 0; i < boost::python::len(l); ++i) { - rows.push_back(extract(l[i])); - } - return QIcon(QPixmap(&rows[0])); - } else { - return QIcon(); - } - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return QIcon(); - } -} - -void PythonToolWrapper::display() const -{ - try { - m_SetParentWidget(parentWidget()); - - m_DisplayFunction(); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - } -} diff --git a/src/proxy/pythontoolwrapper.h b/src/proxy/pythontoolwrapper.h deleted file mode 100644 index a5e2a45..0000000 --- a/src/proxy/pythontoolwrapper.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef PYTHONTOOLWRAPPER_H -#define PYTHONTOOLWRAPPER_H - - -#include -#ifndef Q_MOC_RUN -#include -#endif - -#include "pythonpluginwrapper.h" - - -class PythonToolWrapper : public QObject, public MOBase::IPluginTool, public PythonPluginWrapper -{ - - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool) - -public: - PythonToolWrapper(const boost::python::object &initFunction, - const boost::python::object &nameFunction, - const boost::python::object &authorFunction, - const boost::python::object &descriptionFunction, - const boost::python::object &versionFunction, - const boost::python::object &isActiveFunction, - const boost::python::object &settingsFunction, - const boost::python::object &displayNameFunction, - const boost::python::object &tooltipFunction, - const boost::python::object &iconFunction, - const boost::python::object &displayFunction, - const boost::python::object &setParentWidget); - - using PythonPluginWrapper::init; - using PythonPluginWrapper::name; - - virtual QString displayName() const; - virtual QString tooltip() const; - virtual QIcon icon() const; - -public slots: - - virtual void display() const; - -private: - - boost::python::object m_DisplayNameFunction; - boost::python::object m_ToolTipFunction; - boost::python::object m_IconFunction; - boost::python::object m_DisplayFunction; - - boost::python::object m_SetParentWidget; -}; - - - -#endif // PYTHONTOOLWRAPPER_H diff --git a/src/runner/CMakeLists.txt b/src/runner/CMakeLists.txt index ab234c6..8750dd7 100644 --- a/src/runner/CMakeLists.txt +++ b/src/runner/CMakeLists.txt @@ -34,6 +34,7 @@ SET(project_path "${default_project_path}" CACHE PATH "path to the other mo proj SET(lib_path "${project_path}/../../install/libs") INCLUDE_DIRECTORIES(${project_path}/uibase/src + ${project_path}/game_features/src ${PYTHON_ROOT}/Include ${SIP_ROOT} ${PYTHON_ROOT}/PC) @@ -50,11 +51,13 @@ TARGET_LINK_LIBRARIES(${PROJ_NAME} ADD_DEFINITIONS(-DPYTHONRUNNER_LIBRARY) -IF(MSVC) - SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES LINK_FLAGS "/LARGEADDRESSAWARE") -ELSE(MSVC) - SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES LINK_FLAGS "-std=c++11") -ENDIF(MSVC) +IF (MSVC) + SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES COMPILE_FLAGS "/std:c++latest") +ENDIF() +IF (MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 4) + # 32 bits + SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES LINK_FLAGS "/LARGEADDRESSAWARE") +ENDIF() IF (NOT "${OPTIMIZE_COMPILE_FLAGS}" STREQUAL "") SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES COMPILE_FLAGS_RELWITHDEBINFO ${OPTIMIZE_COMPILE_FLAGS}) diff --git a/src/runner/gamefeatureswrappers.cpp b/src/runner/gamefeatureswrappers.cpp new file mode 100644 index 0000000..380458d --- /dev/null +++ b/src/runner/gamefeatureswrappers.cpp @@ -0,0 +1,282 @@ +#include "gamefeatureswrappers.h" + +#include + +#include + +#include +#include +#include +#include + +#include "gilock.h" +#include "pythonwrapperutilities.h" + +///////////////////////////// +/// BSAInvalidation Wrapper + + +bool BSAInvalidationWrapper::isInvalidationBSA(const QString &bsaName) +{ + return basicWrapperFunctionImplementation(this, "isInvalidationBSA", bsaName); +} + +void BSAInvalidationWrapper::deactivate(MOBase::IProfile *profile) +{ + basicWrapperFunctionImplementation(this, "deactivate", boost::python::ptr(profile)); +} + +void BSAInvalidationWrapper::activate(MOBase::IProfile *profile) +{ + return basicWrapperFunctionImplementation(this, "activate", 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)); +} +/// end GamePlugins Wrapper +///////////////////////////// +/// LocalSavegames Wrapper + + +MappingType LocalSavegamesWrapper::mappings(const QDir & profileSaveDir) const +{ + return basicWrapperFunctionImplementation(this, "mappings", profileSaveDir); +} + +void LocalSavegamesWrapper::prepareProfile(MOBase::IProfile * profile) +{ + return basicWrapperFunctionImplementation(this, "prepareProfile", boost::python::ptr(profile)); +} +/// end LocalSavegames Wrapper +///////////////////////////// +/// SaveGameInfo Wrapper + + +MOBase::ISaveGame const * SaveGameInfoWrapper::getSaveGameInfo(QString const & file) const +{ + return basicWrapperFunctionImplementation(this, "getSaveGameInfo", file); +} + +SaveGameInfoWrapper::MissingAssets SaveGameInfoWrapper::getMissingAssets(QString const & file) const +{ + return basicWrapperFunctionImplementation(this, "getMissingAssets", file); +} + +MOBase::ISaveGameInfoWidget * SaveGameInfoWrapper::getSaveGameWidget(QWidget * parent) const +{ + qCritical("Calling method with unimplemented from_python converter."); + return basicWrapperFunctionImplementation(this, "getSaveGameWidget", boost::python::ptr(parent)); +} + +bool SaveGameInfoWrapper::hasScriptExtenderSave(QString const & file) const +{ + return basicWrapperFunctionImplementation(this, "hasScriptExtenderSave", file); +} +/// 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"); +} + +QStringList ScriptExtenderWrapper::saveGameAttachmentExtensions() const +{ + return basicWrapperFunctionImplementation(this, "saveGameAttachmentExtensions"); +} + +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 +///////////////////////////// + +template +void insertGameFeature(std::map &map, const boost::python::object &pyObject) +{ + map[std::type_index(typeid(T))] = boost::python::extract(pyObject)(); +} + +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; +} + +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>*)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]; + // pyKey should be a Boost.Python.class corresponding to a game feature. + std::string className = boost::python::extract(pyKey.attr("__name__"))(); + if (className == "BSAInvalidation") + insertGameFeature(*result, source[pyKey]); + else if (className == "DataArchives") + insertGameFeature(*result, source[pyKey]); + else if (className == "GamePlugins") + insertGameFeature(*result, source[pyKey]); + else if (className == "LocalSavegames") + insertGameFeature(*result, source[pyKey]); + else if (className == "SaveGameInfo") + insertGameFeature(*result, source[pyKey]); + else if (className == "ScriptExtender") + insertGameFeature(*result, source[pyKey]); + else if (className == "UnmanagedMods") + insertGameFeature(*result, source[pyKey]); + } + + 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)) + .def("deactivate", bpy::pure_virtual(&BSAInvalidation::deactivate)) + .def("activate", bpy::pure_virtual(&BSAInvalidation::activate)) + ; + + bpy::class_("DataArchives") + .def("vanillaArchives", bpy::pure_virtual(&DataArchives::vanillaArchives)) + .def("archives", bpy::pure_virtual(&DataArchives::archives)) + .def("addArchive", bpy::pure_virtual(&DataArchives::addArchive)) + .def("removeArchive", bpy::pure_virtual(&DataArchives::removeArchive)) + ; + + bpy::class_("GamePlugins") + .def("writePluginLists", bpy::pure_virtual(&GamePlugins::writePluginLists)) + .def("readPluginLists", bpy::pure_virtual(&GamePlugins::readPluginLists)) + ; + + bpy::class_("LocalSavegames") + .def("mappings", bpy::pure_virtual(&LocalSavegames::mappings)) + .def("prepareProfile", bpy::pure_virtual(&LocalSavegames::prepareProfile)) + ; + + bpy::class_("SaveGameInfo") + .def("getSaveGameInfo", bpy::pure_virtual(&SaveGameInfo::getSaveGameInfo), bpy::return_value_policy()) + .def("getMissingAssets", bpy::pure_virtual(&SaveGameInfo::getMissingAssets)) + .def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), bpy::return_value_policy()) + .def("hasScriptExtenderSave", bpy::pure_virtual(&SaveGameInfo::hasScriptExtenderSave)) + ; + + 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("saveGameAttachmentExtensions", bpy::pure_virtual(&ScriptExtender::saveGameAttachmentExtensions)) + .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)) + .def("displayName", bpy::pure_virtual(&UnmanagedMods::displayName)) + .def("referenceFile", bpy::pure_virtual(&UnmanagedMods::referenceFile)) + .def("secondaryFiles", bpy::pure_virtual(&UnmanagedMods::secondaryFiles)) + ; +} diff --git a/src/runner/gamefeatureswrappers.h b/src/runner/gamefeatureswrappers.h new file mode 100644 index 0000000..e62bde0 --- /dev/null +++ b/src/runner/gamefeatureswrappers.h @@ -0,0 +1,113 @@ +#ifndef GAMEFEATURESWRAPPERS_H +#define GAMEFEATURESWRAPPERS_H + +#include +#include +#include +#include +#include +#include +#include + +// this might need turning off if Q_MOC_RUN is defined +#include + +///////////////////////////// +/// 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; +}; + +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; +}; + +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 void prepareProfile(MOBase::IProfile *profile) override; +}; + +class SaveGameInfoWrapper : public SaveGameInfo, public boost::python::wrapper +{ +public: + static constexpr const char* className = "SaveGameInfoWrapper"; + using boost::python::wrapper::get_override; + + virtual MOBase::ISaveGame const *getSaveGameInfo(QString const &file) const override; + virtual MissingAssets getMissingAssets(QString const &file) const override; + virtual MOBase::ISaveGameInfoWidget *getSaveGameWidget(QWidget *parent = 0) const override; + virtual bool hasScriptExtenderSave(QString const &file) const override; +}; + +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 QStringList saveGameAttachmentExtensions() 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/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 104575b..d8b4672 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -1,8 +1,10 @@ #include "proxypluginwrappers.h" -#include -#include "error.h" + #include "gilock.h" +#include #include +#include "pythonwrapperutilities.h" +#include "sipApiAccess.h" namespace boost { @@ -21,256 +23,398 @@ namespace boost using namespace MOBase; -#define PYCATCH catch (const boost::python::error_already_set &) { reportPythonError(); throw MyException("unhandled exception"); }\ - catch (...) { throw MyException("An unknown exception was thrown in python code"); } +#define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(class_name) \ +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::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"); \ +} \ + \ +bool class_name::isActive() const \ +{ \ + return basicWrapperFunctionImplementation(this, "isActive"); \ +} \ + \ +QList class_name::settings() const \ +{ \ + return basicWrapperFunctionImplementation>(this, "settings"); \ +} +/// end COMMON_I_PLUGIN_WRAPPER_DEFINITIONS ///////////////////////////// /// IPlugin Wrapper -bool IPluginWrapper::init(MOBase::IOrganizer *moInfo) -{ - try { - return this->get_override("init")(boost::python::ptr(moInfo)); - } PYCATCH; -} -QString IPluginWrapper::name() const -{ - try { - return this->get_override("name")().as(); - } PYCATCH; -} -QString IPluginWrapper::author() const -{ - try { - return this->get_override("author")().as(); - } PYCATCH; -} - -QString IPluginWrapper::description() const -{ - try { - return this->get_override("description")().as(); - } PYCATCH; -} - -MOBase::VersionInfo IPluginWrapper::version() const -{ - try { - return this->get_override("version")().as(); - } PYCATCH; -} - -bool IPluginWrapper::isActive() const -{ - try { - return this->get_override("isActive")().as(); - } PYCATCH; -} - -QList IPluginWrapper::settings() const -{ - try { - return this->get_override("settings")().as>(); - } PYCATCH; -} +COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginWrapper) /// end IPlugin Wrapper -///////////////////////////// -/// IPluginTool Wrapper +///////////////////////////////////// +/// IPluginDiagnose Wrapper -bool IPluginToolWrapper::init(MOBase::IOrganizer *moInfo) +COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginDiagnoseWrapper) + +std::vector IPluginDiagnoseWrapper::activeProblems() const { - try { - return this->get_override("init")(boost::python::ptr(moInfo)); - } PYCATCH; + return basicWrapperFunctionImplementation>(this, "activeProblems"); } -QString IPluginToolWrapper::name() const +QString IPluginDiagnoseWrapper::shortDescription(unsigned int key) const { - try { - return this->get_override("name")().as(); - } PYCATCH; + return basicWrapperFunctionImplementation(this, "shortDescription", key); } -QString IPluginToolWrapper::author() const +QString IPluginDiagnoseWrapper::fullDescription(unsigned int key) const { - try { - return this->get_override("author")().as(); - } PYCATCH; + return basicWrapperFunctionImplementation(this, "fullDescription", key); } -QString IPluginToolWrapper::description() const +bool IPluginDiagnoseWrapper::hasGuidedFix(unsigned int key) const { - try { - return this->get_override("description")().as(); - } PYCATCH; + return basicWrapperFunctionImplementation(this, "hasGuidedFix", key); } -MOBase::VersionInfo IPluginToolWrapper::version() const +void IPluginDiagnoseWrapper::startGuidedFix(unsigned int key) const { - try { - return this->get_override("version")().as(); - } PYCATCH; + basicWrapperFunctionImplementation(this, "startGuidedFix", key); } -bool IPluginToolWrapper::isActive() const +void IPluginDiagnoseWrapper::invalidate() { - try { - return this->get_override("isActive")().as(); - } PYCATCH; + IPluginDiagnose::invalidate(); +} +/// end IPluginDiagnose Wrapper +///////////////////////////////////// +/// IPluginFileMapper Wrapper + + +COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginFileMapperWrapper) + +MappingType IPluginFileMapperWrapper::mappings() const +{ + return basicWrapperFunctionImplementation(this, "mappings"); +} +/// end IPluginFileMapper Wrapper +///////////////////////////////////// +/// IPluginGame Wrapper + + +QString IPluginGameWrapper::gameName() const +{ + return basicWrapperFunctionImplementation(this, "gameName"); } -QList IPluginToolWrapper::settings() const +void IPluginGameWrapper::initializeProfile(const QDir & directory, ProfileSettings settings) const { - try { - return this->get_override("settings")().as>(); - } PYCATCH; + basicWrapperFunctionImplementation(this, "initializeProfile", directory, settings); } -QString IPluginToolWrapper::displayName() const +QString IPluginGameWrapper::savegameExtension() const { - try { - return this->get_override("displayName")().as(); - } PYCATCH; + return basicWrapperFunctionImplementation(this, "savegameExtension"); } -QString IPluginToolWrapper::tooltip() const +QString IPluginGameWrapper::savegameSEExtension() const { - try { - return this->get_override("tooltip")().as(); - } PYCATCH; + return basicWrapperFunctionImplementation(this, "savegameSEExtension"); } -QIcon IPluginToolWrapper::icon() const +bool IPluginGameWrapper::isInstalled() const { - try { - return this->get_override("icon")().as(); - } PYCATCH; + return basicWrapperFunctionImplementation(this, "isInstalled"); } -void IPluginToolWrapper::setParentWidget(QWidget *parent) +QIcon IPluginGameWrapper::gameIcon() const { - try { - this->get_override("setParentWidget")(parent); - } PYCATCH; + return basicWrapperFunctionImplementation(this, "gameIcon"); } -void IPluginToolWrapper::display() const +QDir IPluginGameWrapper::gameDirectory() const { - try { - GILock lock; - - this->get_override("display")(); - } PYCATCH; + return basicWrapperFunctionImplementation(this, "gameDirectory"); } -/// end IPluginTool Wrapper +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"); +} + +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(IPluginGameWrapper) + +std::map IPluginGameWrapper::featureList() const +{ + return basicWrapperFunctionImplementation>(this, "_featureList"); +} +/// end IPluginGame Wrapper ///////////////////////////////////// /// IPluginInstallerCustom Wrapper -bool IPluginInstallerCustomWrapper::init(MOBase::IOrganizer *moInfo) -{ - try { - return this->get_override("init")(boost::python::ptr(moInfo)); - } PYCATCH; -} - -QString IPluginInstallerCustomWrapper::name() const -{ - try { - return this->get_override("name")().as(); - } PYCATCH; -} - -QString IPluginInstallerCustomWrapper::author() const -{ - try { - return this->get_override("author")().as(); - } PYCATCH; -} - -QString IPluginInstallerCustomWrapper::description() const -{ - try { - return this->get_override("description")().as(); - } PYCATCH; -} - -MOBase::VersionInfo IPluginInstallerCustomWrapper::version() const -{ - try { - return this->get_override("version")(); - } PYCATCH; -} - -bool IPluginInstallerCustomWrapper::isActive() const -{ - try { - return this->get_override("isActive")(); - } PYCATCH; -} - -QList IPluginInstallerCustomWrapper::settings() const -{ - try { - return this->get_override("settings")().as>(); - } PYCATCH; -} +COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginInstallerCustomWrapper) unsigned int IPluginInstallerCustomWrapper::priority() const { - try { - return this->get_override("priority")(); - } PYCATCH; + return basicWrapperFunctionImplementation(this, "priority"); } bool IPluginInstallerCustomWrapper::isManualInstaller() const { - try { - return this->get_override("isManualInstaller")(); - } PYCATCH; + return basicWrapperFunctionImplementation(this, "isManualInstaller"); } -bool IPluginInstallerCustomWrapper::isArchiveSupported(const DirectoryTree &) const +bool IPluginInstallerCustomWrapper::isArchiveSupported(const DirectoryTree &archiveTree) const { - try { - //return this->get_override("isArchiveSupported")(tree); - return false; - } PYCATCH; + //return basicWrapperFunctionImplementation(this, "isArchiveSupported", archiveTree); + // This was a stub implementation when I got here, and a real one won't compile. + return false; } bool IPluginInstallerCustomWrapper::isArchiveSupported(const QString &archiveName) const { - try { - return this->get_override("isArchiveSupported")(archiveName); - } PYCATCH; + return basicWrapperFunctionImplementation(this, "isArchiveSupported", archiveName); } std::set IPluginInstallerCustomWrapper::supportedExtensions() const { - try { - return this->get_override("supportedExtensions")().as>(); - } PYCATCH; + return basicWrapperFunctionImplementation>(this, "supportedExtensions"); } - -IPluginInstaller::EInstallResult IPluginInstallerCustomWrapper::install(GuessedValue &modName, const QString &archiveName, +IPluginInstaller::EInstallResult IPluginInstallerCustomWrapper::install(GuessedValue &modName, QString gameName, const QString &archiveName, const QString &version, int modID) { - try { - return this->get_override("install")(modName, archiveName, version, modID); - } PYCATCH; + return basicWrapperFunctionImplementation(this, "install", modName, gameName, archiveName, version, modID); } void IPluginInstallerCustomWrapper::setParentWidget(QWidget *parent) { - try { - this->get_override("setParentWidget")(parent); - } PYCATCH; + basicWrapperFunctionImplementation(this, "setParentWidget", parent); +} +/// 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) +{ + basicWrapperFunctionImplementation(this, "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 +{ + // This is complicated, so we can't use the basic implementation + try { + boost::python::override implementation = this->get_override("genFilePreview"); + if (!implementation) + throw MissingImplementation(this->className, "genFilePreview"); + boost::python::object pyVersion = implementation(fileName, maxSize); + // We need responsibility for deleting the QWidget to be transferred to C++ + sipAPI()->api_transfer_to(pyVersion.ptr(), 0); + return boost::python::extract(pyVersion)(); + } PYCATCH; +} +/// 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) +{ + basicWrapperFunctionImplementation(this, "setParentWidget", parent); +} + +void IPluginToolWrapper::display() const +{ + basicWrapperFunctionImplementation(this, "display"); +} + +/// end IPluginTool Wrapper diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index 6131a24..989705b 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -2,27 +2,185 @@ #define PROXYPLUGINWRAPPERS_H -#include +#include +#include +#include #include #include +#include +#include +#include #ifndef Q_MOC_RUN #include #endif +#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS public: \ +virtual bool init(MOBase::IOrganizer *moInfo) override; \ +virtual QString name() const override; \ +virtual QString author() const override; \ +virtual QString description() const override; \ +virtual MOBase::VersionInfo version() const override; \ +virtual bool isActive() const override; \ +virtual QList settings() const override; + + class IPluginWrapper : public MOBase::IPlugin, public boost::python::wrapper { Q_INTERFACES(MOBase::IPlugin) + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS public: - virtual bool init(MOBase::IOrganizer *moInfo); - virtual QString name() const; - virtual QString author() const; - virtual QString description() const; - virtual MOBase::VersionInfo version() const; - virtual bool isActive() const; - virtual QList settings() const; + 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; + + 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; + // Other functions exist, but shouldn't need wrapping as a default implementation exists + // This was protected, but Python doesn't have that, so it needs making public + virtual void invalidate(); + + 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 QString gameName() const override; + virtual void initializeProfile(const QDir &directory, ProfileSettings settings) const override; + virtual QString savegameExtension() const override; + virtual QString savegameSEExtension() 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 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 + +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. +}; + + +class IPluginInstallerCustomWrapper : public MOBase::IPluginInstallerCustom, public boost::python::wrapper +{ + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom) + + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS +public: + static constexpr const char* className = "IPluginInstallerCustomWrapper"; + using boost::python::wrapper::get_override; + + virtual unsigned int priority() const; + virtual bool isManualInstaller() const; + virtual bool isArchiveSupported(const MOBase::DirectoryTree &tree) const; + virtual bool isArchiveSupported(const QString &archiveName) const; + virtual std::set supportedExtensions() const; + virtual EInstallResult install(MOBase::GuessedValue &modName, QString gameName, const QString &archiveName, + const QString &version, int modID); + virtual void setParentWidget(QWidget *parent); + +}; + + +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; + + 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; +}; + + +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; }; @@ -31,14 +189,10 @@ class IPluginToolWrapper: public MOBase::IPluginTool, public boost::python::wrap Q_OBJECT Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool) + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS public: - virtual bool init(MOBase::IOrganizer *moInfo); - virtual QString name() const; - virtual QString author() const; - virtual QString description() const; - virtual MOBase::VersionInfo version() const; - virtual bool isActive() const; - virtual QList settings() const; + static constexpr const char* className = "IPluginToolWrapper"; + using boost::python::wrapper::get_override; virtual QString displayName() const; virtual QString tooltip() const; @@ -50,32 +204,6 @@ public slots: }; -class IPluginInstallerCustomWrapper: public MOBase::IPluginInstallerCustom, public boost::python::wrapper -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom) - -public: - virtual bool init(MOBase::IOrganizer *moInfo); - virtual QString name() const; - virtual QString author() const; - virtual QString description() const; - virtual MOBase::VersionInfo version() const; - virtual bool isActive() const; - virtual QList settings() const; - - virtual unsigned int priority() const; - virtual bool isManualInstaller() const; - virtual bool isArchiveSupported(const MOBase::DirectoryTree &tree) const; - virtual bool isArchiveSupported(const QString &archiveName) const; - virtual std::set supportedExtensions() const; - virtual EInstallResult install(MOBase::GuessedValue &modName, const QString &archiveName, - const QString &version, int modID); - virtual void setParentWidget(QWidget *parent); - -}; - - #endif // PROXYPLUGINWRAPPERS_H diff --git a/src/runner/pythonpluginwrapper.cpp b/src/runner/pythonpluginwrapper.cpp deleted file mode 100644 index 1c0677e..0000000 --- a/src/runner/pythonpluginwrapper.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "pythonpluginwrapper.h" - - -using boost::python::extract; -using boost::python::handle_exception; - - -PythonPluginWrapper::PythonPluginWrapper(const boost::python::object &initFunction, - const boost::python::object &nameFunction, - const boost::python::object &authorFunction, - const boost::python::object &descriptionFunction, - const boost::python::object &versionFunction, - const boost::python::object &isActiveFunction, - const boost::python::object &settingsFunction) - : m_InitFunction(initFunction) - , m_NameFunction(nameFunction) - , m_AuthorFunction(authorFunction) - , m_DescriptionFunction(descriptionFunction) - , m_VersionFunction(versionFunction) - , m_IsActiveFunction(isActiveFunction) - , m_SettingsFunction(settingsFunction) -{ -} - - -void PythonPluginWrapper::reportPythonError() const -{ - if (PyErr_Occurred()) { - PyErr_Print(); - } else { - qCritical("An unexpected C++ exception was thrown in python code"); - } -} - - -bool PythonPluginWrapper::init(MOBase::IOrganizer *moInfo) -{ - try { - return boost::python::extract(m_InitFunction(boost::python::ptr(moInfo))); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return false; - } -} - -QString PythonPluginWrapper::name() const -{ - try { - return boost::python::extract(m_NameFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return QString(); - } -} - -QString PythonPluginWrapper::author() const -{ - try { - return boost::python::extract(m_AuthorFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return QString(); - } -} - -QString PythonPluginWrapper::description() const -{ - try { - return boost::python::extract(m_DescriptionFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return QString(); - } -} - -MOBase::VersionInfo PythonPluginWrapper::version() const -{ - try { - return boost::python::extract(m_VersionFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return MOBase::VersionInfo(); - } -} - -bool PythonPluginWrapper::isActive() const -{ - try { - return boost::python::extract(m_IsActiveFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return false; - } -} - -QList PythonPluginWrapper::settings() const -{ - QList result; - try { - boost::python::object l = m_SettingsFunction(); - if (!l.is_none()) { - for (int i = 0; i < boost::python::len(l); ++i) { - result.append(extract(l[i])); - } - } - } catch (const boost::python::error_already_set&) { - reportPythonError(); - } - - return result; -} diff --git a/src/runner/pythonpluginwrapper.h b/src/runner/pythonpluginwrapper.h deleted file mode 100644 index f797d90..0000000 --- a/src/runner/pythonpluginwrapper.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef PYTHONPLUGINWRAPPER_H -#define PYTHONPLUGINWRAPPER_H - -#include -#ifndef Q_MOC_RUN -#include -#endif - -class PythonPluginWrapper : public virtual MOBase::IPlugin -{ - -public: - PythonPluginWrapper(const boost::python::object &initFunction, - const boost::python::object &nameFunction, - const boost::python::object &authorFunction, - const boost::python::object &descriptionFunction, - const boost::python::object &versionFunction, - const boost::python::object &isActiveFunction, - const boost::python::object &settingsFunction); - - virtual bool init(MOBase::IOrganizer *moInfo); - virtual QString name() const; - virtual QString author() const; - virtual QString description() const; - virtual MOBase::VersionInfo version() const; - virtual bool isActive() const; - virtual QList settings() const; - -protected: - - void reportPythonError() const; - -private: - - boost::python::object m_InitFunction; - boost::python::object m_NameFunction; - boost::python::object m_AuthorFunction; - boost::python::object m_DescriptionFunction; - boost::python::object m_VersionFunction; - boost::python::object m_IsActiveFunction; - boost::python::object m_SettingsFunction; - -}; - -#endif // PYTHONPLUGINWRAPPER_H diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 2e901c0..d529965 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -3,14 +3,14 @@ #pragma warning( disable : 4100 ) #pragma warning( disable : 4996 ) -#include "iplugingame.h" +#include +#include #include #include -#include -#include #include "uibasewrappers.h" -#include "pythonpluginwrapper.h" #include "proxypluginwrappers.h" +#include "gamefeatureswrappers.h" +#include "sipApiAccess.h" #include #include @@ -37,7 +37,7 @@ class PythonRunner : public IPythonRunner public: PythonRunner(const MOBase::IOrganizer *moInfo); bool initPython(const QString &pythonDir); - QObject *instantiate(const QString &pluginName); + QList instantiate(const QString &pluginName); bool isPythonInstalled() const; bool isPythonVersionSupported() const; @@ -69,26 +69,15 @@ using namespace MOBase; namespace bpy = boost::python; -struct ModRepositoryFileInfo_to_python_dict -{ - static PyObject *convert(const ModRepositoryFileInfo &info) { - PyObject *res = PyDict_New(); - PyDict_SetItemString(res, "uri", bpy::incref(bpy::object(info.uri).ptr())); - PyDict_SetItemString(res, "name", bpy::incref(bpy::object(info.name).ptr())); - PyDict_SetItemString(res, "description", bpy::incref(bpy::object(info.description.toUtf8().constData()).ptr())); - PyDict_SetItemString(res, "categoryID", PyLong_FromLong(info.categoryID)); - PyDict_SetItemString(res, "fileID", PyLong_FromLong(info.fileID)); - PyDict_SetItemString(res, "fileSize", PyLong_FromLong(static_cast(info.fileSize))); - PyDict_SetItemString(res, "version", bpy::incref(bpy::object(info.version).ptr())); - return bpy::incref(res); - } -}; - struct QString_to_python_str { static PyObject *convert(const QString &str) { - return bpy::incref(bpy::object(str.toUtf8().constData()).ptr()); + // 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(str.toUtf8().constData()); + if (PyString_Check(pyStr.ptr())) + pyStr = pyStr.attr("decode")("utf-8"); + return bpy::incref(pyStr.ptr()); } }; @@ -183,6 +172,55 @@ struct GuessedValue_converters }; +template +struct QMap_converters +{ + struct QMap_to_python + { + static PyObject *convert(const QMap &map) { + bpy::dict result; + QMapIterator iter(map); + while (iter.hasNext()) { + iter.next(); + result[bpy::object(iter.key())] = bpy::object(iter.value()); + } + return bpy::incref(result.ptr()); + } + }; + + struct QMap_from_python + { + QMap_from_python() { + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); + } + + static void *convertible(PyObject *objPtr) { + return PyDict_Check(objPtr) ? objPtr : nullptr; + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { + void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; + QMap *result = new (storage) QMap(); + bpy::dict source(bpy::handle<>(bpy::borrowed(objPtr))); + bpy::list keys = source.keys(); + int len = bpy::len(keys); + for (int i = 0; i < len; ++i) { + bpy::object pyKey = keys[i]; + (*result)[bpy::extract(pyKey)] = bpy::extract(source[pyKey]); + } + + data->convertible = storage; + } + }; + + QMap_converters() + { + QMap_from_python(); + bpy::to_python_converter, QMap_to_python >(); + } +}; + + struct QVariant_to_python_obj { static PyObject *convert(const QVariant &var) { @@ -190,24 +228,12 @@ struct QVariant_to_python_obj case QVariant::Int: return PyLong_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().toUtf8().constData()).ptr()); + case QVariant::String: return bpy::incref(bpy::object(var.toString()).ptr()); case QVariant::List: { - QVariantList list = var.toList(); - PyObject *result = PyList_New(list.count()); - for (QVariant var : list) { - PyList_Append(result, convert(var)); - } - return result; + return bpy::incref(bpy::object(var.toList()).ptr()); } break; case QVariant::Map: { - QVariantMap map = var.toMap(); - PyObject *result = PyDict_New(); - QMapIterator iter(map); - while (iter.hasNext()) { - iter.next(); - PyDict_SetItem(result, convert(iter.key()), convert(iter.value())); - } - return result; + return bpy::incref(bpy::object(var.toMap()).ptr()); } break; default: { PyErr_Format(PyExc_TypeError, "type unsupported: %d", var.type()); @@ -241,49 +267,21 @@ struct QVariant_from_python_obj data->convertible = storage; } - static QVariant variantFromPyObject(PyObject *objPtr) { - QVariant result; - if (PyList_Check(objPtr)) { - QVariantList resultList; - for (int i = 0; i < PyList_Size(objPtr); ++i) { - resultList.append(variantFromPyObject(PyList_GetItem(objPtr, i))); - } - result = resultList; - } else if (PyString_Check(objPtr)) { - result = PyString_AsString(objPtr); - } else if (PyBool_Check(objPtr)) { - result = (objPtr == Py_True); - } else if (PyInt_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... - result = static_cast(PyInt_AsLong(objPtr)); - } else { - PyErr_SetString(PyExc_TypeError, "type unsupported"); - throw bpy::error_already_set(); - } - return result; - } - static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { // PyBools will also return true for PyInt_Check but not the other way around, so the order // here is relevant if (PyList_Check(objPtr)) { - QVariantList result; - for (int i = 0; i < PyList_Size(objPtr); ++i) { - result.append(variantFromPyObject(PyList_GetItem(objPtr, i))); - } - constructVariant(result, data); - } else if (PyString_Check(objPtr)) { - const char *value = PyString_AsString(objPtr); - constructVariant(value, data); + constructVariant(bpy::extract(objPtr)(), data); + } else if (PyDict_Check(objPtr)) { + constructVariant(bpy::extract(objPtr)(), data); + } else if (PyString_Check(objPtr) || PyUnicode_Check(objPtr)) { + constructVariant(bpy::extract(objPtr)(), data); } else if (PyBool_Check(objPtr)) { - bool value = (objPtr == Py_True); - constructVariant(value, data); + constructVariant(bpy::extract(objPtr)(), data); } else if (PyInt_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... - int value = static_cast(PyInt_AsLong(objPtr)); - constructVariant(value, data); + constructVariant(bpy::extract(objPtr)(), data); } else { PyErr_SetString(PyExc_TypeError, "type unsupported"); throw bpy::error_already_set(); @@ -341,6 +339,55 @@ struct QList_from_python_obj }; +template +struct std_vector_to_python_list +{ + static PyObject *convert(const std::vector &vector) + { + bpy::list pyList; + + try { + for (const T &item : vector) + pyList.append(item); + } + catch (const bpy::error_already_set&) { + reportPythonError(); + } + + return bpy::incref(pyList.ptr()); + } +}; + + +template +struct std_vector_from_python_obj +{ + std_vector_from_python_obj() { + bpy::converter::registry::push_back( + &convertible, + &construct, + bpy::type_id >()); + } + + static void* convertible(PyObject *objPtr) { + if (PyList_Check(objPtr)) return objPtr; + return nullptr; + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { + void *storage = ((bpy::converter::rvalue_from_python_storage >*)data)->storage.bytes; + std::vector *result = new (storage) std::vector(); + bpy::list source(bpy::handle<>(bpy::borrowed(objPtr))); + int length = bpy::len(source); + for (int i = 0; i < length; ++i) { + result->push_back(bpy::extract(source[i])); + } + + data->convertible = storage; + } +}; + + template struct QFlags_from_python_obj { @@ -396,17 +443,6 @@ struct stdset_from_python_list }; -static const sipAPIDef *sipAPI() -{ - static const sipAPIDef *sipApi = nullptr; - if (sipApi == nullptr) { - sipApi = (const sipAPIDef *)PyCapsule_Import("sip._C_API", 0); - } - - return sipApi; -} - - struct IModRepositoryBridge_to_python { static PyObject *convert(IModRepositoryBridge *bridge) @@ -425,8 +461,13 @@ template <> struct MetaData { static const char *className template <> struct MetaData { static const char *className() { return "QObject"; } }; 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 "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 "QStringList"; } }; +template <> struct MetaData { static const char *className() { return "QUrl"; } }; template <> struct MetaData { static const char *className() { return "QVariant"; } }; @@ -458,17 +499,33 @@ 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 = sipAPI()->api_find_type(MetaData::className()); if (type == nullptr) { return bpy::incref(Py_None); } - PyObject *sipObj = sipAPI()->api_convert_from_type((void*)(&object), type, 0); + PyObject *sipObj = 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 + sipAPI()->api_transfer_back(sipObj); + return bpy::incref(sipObj); } @@ -482,11 +539,15 @@ struct QClass_converters return bpy::incref(Py_None); } - PyObject *sipObj = sipAPI()->api_convert_from_type(object, type, 0); + PyObject *sipObj = 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 + sipAPI()->api_transfer_back(sipObj); + return bpy::incref(sipObj); } @@ -497,12 +558,21 @@ struct QClass_converters static void *QClass_from_PyQt(PyObject *objPtr) { - if (!PyObject_TypeCheck(objPtr, sipAPI()->api_wrapper_type)) { + if (!PyObject_TypeCheck(objPtr, sipAPI()->api_simplewrapper_type)) { + if (std::is_same_v) + { + // QStringLists aren't wrapped by PyQt - regular Python string/unicode lists are used instead + bpy::extract> extractor(objPtr); + if (extractor.check()) + return new QStringList(extractor()); + } PyErr_SetString(PyExc_TypeError, "type not wrapped"); bpy::throw_error_already_set(); } - sipAPI()->api_transfer_to(objPtr, 0); + // 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, 0); sipSimpleWrapper *wrapper = reinterpret_cast(objPtr); return wrapper->data; @@ -565,7 +635,9 @@ struct QInterface_converters bpy::throw_error_already_set(); } - sipAPI()->api_transfer_to(objPtr, 0); + // 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, 0); sipSimpleWrapper *wrapper = reinterpret_cast(objPtr); return wrapper->data; @@ -594,6 +666,7 @@ int getArgCount(PyObject *object) { return result; } +template struct Functor0_converter { @@ -602,9 +675,9 @@ struct Functor0_converter FunctorWrapper(boost::python::object callable) : m_Callable(callable) { } - void operator()() { + RET operator()() { GILock lock; - m_Callable(); + return (RET) m_Callable(); } boost::python::object m_Callable; @@ -612,7 +685,7 @@ struct Functor0_converter Functor0_converter() { - bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); } static void *convertible(PyObject *object) @@ -627,14 +700,55 @@ struct Functor0_converter 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>*)data)->storage.bytes; - new (storage) std::function(FunctorWrapper(callable)); + void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; + new (storage) std::function(FunctorWrapper(callable)); data->convertible = storage; } }; -template +template +struct Functor1_converter +{ + + struct FunctorWrapper + { + FunctorWrapper(boost::python::object callable) : m_Callable(callable) { + } + + RET operator()(const PAR1 ¶m1) { + GILock lock; + return (RET) m_Callable(param1); + } + + boost::python::object m_Callable; + }; + + Functor1_converter() + { + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); + } + + static void *convertible(PyObject *object) + { + if (!PyCallable_Check(object) + || (getArgCount(object) != 1)) { + 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>*)data)->storage.bytes; + new (storage) std::function(FunctorWrapper(callable)); + data->convertible = storage; + } +}; + + +template struct Functor2_converter { @@ -643,9 +757,9 @@ struct Functor2_converter FunctorWrapper(boost::python::object callable) : m_Callable(callable) { } - void operator()(const PAR1 ¶m1, const PAR2 ¶m2) { + RET operator()(const PAR1 ¶m1, const PAR2 ¶m2) { GILock lock; - m_Callable(param1, param2); + return (RET) m_Callable(param1, param2); } boost::python::object m_Callable; @@ -653,7 +767,7 @@ struct Functor2_converter Functor2_converter() { - bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); } static void *convertible(PyObject *object) @@ -668,8 +782,8 @@ struct Functor2_converter 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>*)data)->storage.bytes; - new (storage) std::function(FunctorWrapper(callable)); + void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; + new (storage) std::function(FunctorWrapper(callable)); data->convertible = storage; } }; @@ -688,9 +802,14 @@ BOOST_PYTHON_MODULE(mobase) QString_from_python_str(); //QClass_converters(); + QClass_converters(); + QClass_converters(); + QClass_converters(); QClass_converters(); QClass_converters(); + QClass_converters(); QClass_converters(); + QClass_converters(); QInterface_converters(); @@ -711,14 +830,7 @@ BOOST_PYTHON_MODULE(mobase) .value("decimalmark", MOBase::VersionInfo::SCHEME_DECIMALMARK) .value("numbersandletters", MOBase::VersionInfo::SCHEME_NUMBERSANDLETTERS) .value("date", MOBase::VersionInfo::SCHEME_DATE) - ; - - bpy::enum_("InstallResult") - .value("success", MOBase::IPluginInstaller::RESULT_SUCCESS) - .value("failed", MOBase::IPluginInstaller::RESULT_FAILED) - .value("canceled", MOBase::IPluginInstaller::RESULT_CANCELED) - .value("manualRequested", MOBase::IPluginInstaller::RESULT_MANUALREQUESTED) - .value("notAttempted", MOBase::IPluginInstaller::RESULT_NOTATTEMPTED) + .value("literal", MOBase::VersionInfo::SCHEME_LITERAL) ; bpy::class_("VersionInfo") @@ -726,21 +838,64 @@ BOOST_PYTHON_MODULE(mobase) .def(bpy::init()) .def(bpy::init()) .def(bpy::init()) + .def(bpy::init()) + .def(bpy::init()) + .def("clear", &VersionInfo::clear) .def("parse", &VersionInfo::parse) .def("canonicalString", &VersionInfo::canonicalString) + .def("displayString", &VersionInfo::displayString) + .def("isValid", &VersionInfo::isValid) + .def("scheme", &VersionInfo::scheme) + .def(bpy::self < bpy::self) + .def(bpy::self > bpy::self) + .def(bpy::self <= bpy::self) + .def(bpy::self >= bpy::self) + .def(bpy::self != bpy::self) + .def(bpy::self == bpy::self) ; bpy::class_("PluginSetting", bpy::init()); + bpy::class_("ExecutableInfo", bpy::init()) + .def("withArgument", &ExecutableInfo::withArgument, bpy::return_value_policy()) + .def("withWorkingDirectory", &ExecutableInfo::withWorkingDirectory, bpy::return_value_policy()) + .def("withSteamAppId", &ExecutableInfo::withSteamAppId, bpy::return_value_policy()) + .def("asCustom", &ExecutableInfo::asCustom, bpy::return_value_policy()) + .def("isValid", &ExecutableInfo::isValid) + .def("title", &ExecutableInfo::title) + .def("binary", &ExecutableInfo::binary) + .def("arguments", &ExecutableInfo::arguments) + .def("workingDirectory", &ExecutableInfo::workingDirectory) + .def("steamAppID", &ExecutableInfo::steamAppID) + .def("isCustom", &ExecutableInfo::isCustom) + ; + + bpy::class_("ISaveGame") + .def("getFilename", bpy::pure_virtual(&ISaveGame::getFilename)) + .def("getCreationTime", bpy::pure_virtual(&ISaveGame::getCreationTime)) + .def("getSaveGroupIdentifier", bpy::pure_virtual(&ISaveGame::getSaveGroupIdentifier)) + .def("allFiles", bpy::pure_virtual(&ISaveGame::allFiles)) + .def("hasScriptExtenderFile", bpy::pure_virtual(&ISaveGame::hasScriptExtenderFile)) + ; + + // TODO: ISaveGameInfoWidget bindings + + Functor1_converter(); + Functor1_converter(); + Functor1_converter(); + Functor2_converter(); bpy::class_("IOrganizer") .def("createNexusBridge", bpy::pure_virtual(&IOrganizer::createNexusBridge), bpy::return_value_policy()) .def("profileName", bpy::pure_virtual(&IOrganizer::profileName)) .def("profilePath", bpy::pure_virtual(&IOrganizer::profilePath)) .def("downloadsPath", bpy::pure_virtual(&IOrganizer::downloadsPath)) + .def("overwritePath", bpy::pure_virtual(&IOrganizer::overwritePath)) + .def("basePath", bpy::pure_virtual(&IOrganizer::basePath)) .def("appVersion", bpy::pure_virtual(&IOrganizer::appVersion)) .def("getMod", bpy::pure_virtual(&IOrganizer::getMod), bpy::return_value_policy()) .def("createMod", bpy::pure_virtual(&IOrganizer::createMod), bpy::return_value_policy()) + .def("getGame", bpy::pure_virtual(&IOrganizer::getGame), bpy::return_value_policy()) .def("removeMod", bpy::pure_virtual(&IOrganizer::removeMod)) .def("modDataChanged", bpy::pure_virtual(&IOrganizer::modDataChanged)) .def("pluginSetting", bpy::pure_virtual(&IOrganizer::pluginSetting)) @@ -749,16 +904,21 @@ BOOST_PYTHON_MODULE(mobase) .def("setPersistent", bpy::pure_virtual(&IOrganizer::setPersistent)) .def("pluginDataPath", bpy::pure_virtual(&IOrganizer::pluginDataPath)) .def("installMod", bpy::pure_virtual(&IOrganizer::installMod),(bpy::arg("nameSuggestion")=""), bpy::return_value_policy()) + .def("resolvePath", bpy::pure_virtual(&IOrganizer::resolvePath)) + .def("listDirectories", bpy::pure_virtual(&IOrganizer::listDirectories)) + .def("findFiles", bpy::pure_virtual(&IOrganizer::findFiles)) + .def("getFileOrigins", bpy::pure_virtual(&IOrganizer::getFileOrigins)) + .def("findFileInfos", bpy::pure_virtual(&IOrganizer::findFileInfos)) .def("downloadManager", bpy::pure_virtual(&IOrganizer::downloadManager), bpy::return_value_policy()) .def("pluginList", bpy::pure_virtual(&IOrganizer::pluginList), bpy::return_value_policy()) .def("modList", bpy::pure_virtual(&IOrganizer::modList), bpy::return_value_policy()) - .def("startApplication", bpy::pure_virtual(&IOrganizer::startApplication), bpy::return_value_policy()) + .def("profile", bpy::pure_virtual(&IOrganizer::profile), bpy::return_value_policy()) + .def("startApplication", bpy::pure_virtual(&IOrganizer::startApplication), ((bpy::arg("args")=QStringList()), (bpy::arg("cwd")=""), (bpy::arg("profile")="")), bpy::return_value_policy()) //.def("waitForApplication", bpy::pure_virtual(&IOrganizer::waitForApplication), (bpy::arg("exitCode")=nullptr), bpy::return_value_policy()) + .def("onModInstalled", bpy::pure_virtual(&IOrganizer::onModInstalled)) .def("onAboutToRun", bpy::pure_virtual(&IOrganizer::onAboutToRun)) .def("onFinishedRun", bpy::pure_virtual(&IOrganizer::onFinishedRun)) - .def("onModInstalled", bpy::pure_virtual(&IOrganizer::onModInstalled)) - .def("refreshModList", bpy::pure_virtual(&IOrganizer::refreshModList)) - .def("profile", bpy::pure_virtual(&IOrganizer::profile), bpy::return_value_policy()) + .def("refreshModList", bpy::pure_virtual(&IOrganizer::refreshModList), (bpy::arg("saveChanges")=true)) .def("managedGame", bpy::pure_virtual(&IOrganizer::managedGame), bpy::return_value_policy()) .def("modsSortedByProfilePriority", bpy::pure_virtual(&IOrganizer::modsSortedByProfilePriority)) ; @@ -791,6 +951,29 @@ BOOST_PYTHON_MODULE(mobase) .def("requestToggleEndorsement", bpy::pure_virtual(&IModRepositoryBridge::requestToggleEndorsement)) ; + bpy::class_("ModRepositoryFileInfo") + .def(bpy::init()) + .def(bpy::init>()) + .def("toString", &ModRepositoryFileInfo::toString) + .def("createFromJson", &ModRepositoryFileInfo::createFromJson).staticmethod("createFromJson") + .def_readwrite("name", &ModRepositoryFileInfo::name) + .def_readwrite("uri", &ModRepositoryFileInfo::uri) + .def_readwrite("description", &ModRepositoryFileInfo::description) + .def_readwrite("version", &ModRepositoryFileInfo::version) + .def_readwrite("newestVersion", &ModRepositoryFileInfo::newestVersion) + .def_readwrite("categoryID", &ModRepositoryFileInfo::categoryID) + .def_readwrite("modName", &ModRepositoryFileInfo::modName) + .def_readwrite("gameName", &ModRepositoryFileInfo::gameName) + .def_readwrite("modID", &ModRepositoryFileInfo::modID) + .def_readwrite("fileID", &ModRepositoryFileInfo::fileID) + .def_readwrite("fileSize", &ModRepositoryFileInfo::fileSize) + .def_readwrite("fileName", &ModRepositoryFileInfo::fileName) + .def_readwrite("fileCategory", &ModRepositoryFileInfo::fileCategory) + .def_readwrite("fileTime", &ModRepositoryFileInfo::fileTime) + .def_readwrite("repository", &ModRepositoryFileInfo::repository) + .def_readwrite("userData", &ModRepositoryFileInfo::userData) + ; + bpy::class_("IDownloadManager") .def("startDownloadURLs", bpy::pure_virtual(&IDownloadManager::startDownloadURLs)) .def("startDownloadNexusFile", bpy::pure_virtual(&IDownloadManager::startDownloadNexusFile)) @@ -835,19 +1018,9 @@ BOOST_PYTHON_MODULE(mobase) .def("variants", &MOBase::GuessedValue::variants, bpy::return_value_policy()) ; - bpy::class_("IPlugin"); - - bpy::class_, boost::noncopyable>("IPluginTool") - .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginTool::setParentWidget)) - ; - - bpy::class_("IPluginInstallerCustom") - .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginInstallerCustom::setParentWidget)) - ; - bpy::to_python_converter>(); QFlags_from_python_obj(); - Functor0_converter(); // converter for the onRefreshed-callback + Functor0_converter(); // converter for the onRefreshed-callback bpy::class_("IPluginList") .def("state", bpy::pure_virtual(&MOBase::IPluginList::state)) @@ -865,7 +1038,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::to_python_converter>(); QFlags_from_python_obj(); - Functor2_converter(); // converter for the onModStateChanged-callback + Functor2_converter(); // converter for the onModStateChanged-callback bpy::class_("IModList") .def("displayName", bpy::pure_virtual(&MOBase::IModList::displayName)) @@ -878,11 +1051,40 @@ BOOST_PYTHON_MODULE(mobase) .def("onModMoved", bpy::pure_virtual(&MOBase::IModList::onModMoved)) ; + bpy::class_("IPlugin"); + + bpy::class_("IPluginDiagnose") + .def("activeProblems", bpy::pure_virtual(&MOBase::IPluginDiagnose::activeProblems)) + .def("shortDescription", bpy::pure_virtual(&MOBase::IPluginDiagnose::shortDescription)) + .def("fullDescription", bpy::pure_virtual(&MOBase::IPluginDiagnose::fullDescription)) + .def("hasGuidedFix", bpy::pure_virtual(&MOBase::IPluginDiagnose::hasGuidedFix)) + .def("startGuidedFix", bpy::pure_virtual(&MOBase::IPluginDiagnose::startGuidedFix)) + .def("_invalidate", &IPluginDiagnoseWrapper::invalidate) + ; + + bpy::class_("Mapping") + .def_readwrite("source", &Mapping::source) + .def_readwrite("destination", &Mapping::destination) + .def_readwrite("isDirectory", &Mapping::isDirectory) + .def_readwrite("createTarget", &Mapping::createTarget) + ; + + bpy::class_("IPluginFileMapper") + .def("mappings", bpy::pure_virtual(&MOBase::IPluginFileMapper::mappings)) + ; + bpy::enum_("LoadOrderMechanism") .value("FileTime", MOBase::IPluginGame::LoadOrderMechanism::FileTime) .value("PluginsTxt", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt) ; + bpy::enum_("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) + ; + // This doesn't actually do the conversion, but might be convenient for accessing the names for enum bits bpy::enum_("ProfileSetting") .value("mods", MOBase::IPluginGame::MODS) @@ -913,11 +1115,14 @@ BOOST_PYTHON_MODULE(mobase) .def("setGameVariant", bpy::pure_virtual(&MOBase::IPluginGame::setGameVariant)) .def("binaryName", bpy::pure_virtual(&MOBase::IPluginGame::binaryName)) .def("gameShortName", bpy::pure_virtual(&MOBase::IPluginGame::gameShortName)) + .def("primarySources", bpy::pure_virtual(&MOBase::IPluginGame::primarySources)) + .def("validShortNames", bpy::pure_virtual(&MOBase::IPluginGame::validShortNames)) .def("gameNexusName", bpy::pure_virtual(&MOBase::IPluginGame::gameNexusName)) .def("iniFiles", bpy::pure_virtual(&MOBase::IPluginGame::iniFiles)) .def("DLCPlugins", bpy::pure_virtual(&MOBase::IPluginGame::DLCPlugins)) .def("CCPlugins", bpy::pure_virtual(&MOBase::IPluginGame::CCPlugins)) .def("loadOrderMechanism", bpy::pure_virtual(&MOBase::IPluginGame::loadOrderMechanism)) + .def("sortMechanism", bpy::pure_virtual(&MOBase::IPluginGame::sortMechanism)) .def("nexusModOrganizerID", bpy::pure_virtual(&MOBase::IPluginGame::nexusModOrganizerID)) .def("nexusGameID", bpy::pure_virtual(&MOBase::IPluginGame::nexusGameID)) .def("looksValid", bpy::pure_virtual(&MOBase::IPluginGame::looksValid)) @@ -933,21 +1138,65 @@ BOOST_PYTHON_MODULE(mobase) .def("isActive", bpy::pure_virtual(&MOBase::IPluginGame::isActive)) .def("settings", bpy::pure_virtual(&MOBase::IPluginGame::settings)) + // The syntax has to differ slightly from C++ because these are templated + .def("featureBSAInvalidation", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureDataArchives", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureGamePlugins", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureLocalSavegames", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureSaveGameInfo", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureScriptExtender", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureUnmanagedMods", &MOBase::IPluginGame::feature, bpy::return_value_policy()) ; - bpy::class_("QDir") - .def("absolutePath", &QDir::absolutePath) - ; + bpy::enum_("InstallResult") + .value("success", MOBase::IPluginInstaller::RESULT_SUCCESS) + .value("failed", MOBase::IPluginInstaller::RESULT_FAILED) + .value("canceled", MOBase::IPluginInstaller::RESULT_CANCELED) + .value("manualRequested", MOBase::IPluginInstaller::RESULT_MANUALREQUESTED) + .value("notAttempted", MOBase::IPluginInstaller::RESULT_NOTATTEMPTED) + ; + + bpy::class_("IPluginInstallerCustom") + .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginInstallerCustom::setParentWidget)) + ; + + bpy::class_("IPluginModPage") + .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginModPage::setParentWidget)) + ; + + bpy::class_("IPluginPreview") + ; + + bpy::class_, boost::noncopyable>("IPluginTool") + .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginTool::setParentWidget)) + ; GuessedValue_converters(); - bpy::to_python_converter(); + //bpy::to_python_converter(); + QList_from_python_obj(); QList_from_python_obj(); bpy::to_python_converter, QList_to_python_list >(); + QList_from_python_obj(); + bpy::to_python_converter, + QList_to_python_list>(); + QList_from_python_obj(); + bpy::to_python_converter, + QList_to_python_list>(); + + QMap_converters(); + QMap_converters(); + + std_vector_from_python_obj(); + std_vector_from_python_obj(); + bpy::to_python_converter, + std_vector_to_python_list>(); stdset_from_python_list(); + + registerGameFeaturesPythonConverters(); } @@ -1019,7 +1268,7 @@ bool handled_exec_file(bpy::str filename, bpy::object globals = bpy::object(), b bpy::extract extr(var); \ if (extr.check()) { \ QObject *res = extr; \ - return res; \ + interfaceList.append(res); \ }\ } while (false) @@ -1042,7 +1291,7 @@ void PythonRunner::initPath(bpy::object &moduleNamespace) } -QObject *PythonRunner::instantiate(const QString &pluginName) +QList PythonRunner::instantiate(const QString &pluginName) { try { GILock lock; @@ -1056,19 +1305,28 @@ QObject *PythonRunner::instantiate(const QString &pluginName) std::string temp = ToString(pluginName); if (handled_exec_file(temp.c_str(), moduleNamespace)) { reportPythonError(); - return nullptr; + return QList(); } m_PythonObjects[pluginName] = moduleNamespace["createPlugin"](); bpy::object pluginObj = m_PythonObjects[pluginName]; - TRY_PLUGIN_TYPE(IPluginInstallerCustom, pluginObj); - TRY_PLUGIN_TYPE(IPluginTool, pluginObj); + QList interfaceList; TRY_PLUGIN_TYPE(IPluginGame, pluginObj); + // Must try the wrapper because it's only a plugin extension interface in C++, so doesn't extend QObject + TRY_PLUGIN_TYPE(IPluginDiagnoseWrapper, pluginObj); + // Must try the wrapper because it's only a plugin extension interface in C++, so doesn't extend QObject + TRY_PLUGIN_TYPE(IPluginFileMapperWrapper, pluginObj); + TRY_PLUGIN_TYPE(IPluginInstallerCustom, pluginObj); + TRY_PLUGIN_TYPE(IPluginModPage, pluginObj); + TRY_PLUGIN_TYPE(IPluginPreview, pluginObj); + TRY_PLUGIN_TYPE(IPluginTool, pluginObj); + + return interfaceList; } catch (const bpy::error_already_set&) { qWarning("failed to run python script \"%s\"", qPrintable(pluginName)); reportPythonError(); } - return nullptr; + return QList(); } bool PythonRunner::isPythonInstalled() const diff --git a/src/runner/pythonrunner.h b/src/runner/pythonrunner.h index 4ef771a..ae5d620 100644 --- a/src/runner/pythonrunner.h +++ b/src/runner/pythonrunner.h @@ -10,7 +10,7 @@ class IPythonRunner { public: - virtual QObject *instantiate(const QString &pluginName) = 0; + virtual QList instantiate(const QString &pluginName) = 0; virtual bool isPythonInstalled() const = 0; virtual bool isPythonVersionSupported() const = 0; }; diff --git a/src/runner/pythontoolwrapper.cpp b/src/runner/pythontoolwrapper.cpp deleted file mode 100644 index 8690dff..0000000 --- a/src/runner/pythontoolwrapper.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "pythontoolwrapper.h" -#include -#include - -using boost::python::extract; -using boost::python::handle_exception; - -PythonToolWrapper::PythonToolWrapper(const boost::python::object &initFunction, - const boost::python::object &nameFunction, - const boost::python::object &authorFunction, - const boost::python::object &descriptionFunction, - const boost::python::object &versionFunction, - const boost::python::object &isActiveFunction, - const boost::python::object &settingsFunction, - const boost::python::object &displayNameFunction, - const boost::python::object &tooltipFunction, - const boost::python::object &iconFunction, - const boost::python::object &displayFunction, - const boost::python::object &setParentWidget) - : PythonPluginWrapper(initFunction, nameFunction, authorFunction, descriptionFunction, - versionFunction, isActiveFunction, settingsFunction) - , m_DisplayNameFunction(displayNameFunction) - , m_ToolTipFunction(tooltipFunction) - , m_IconFunction(iconFunction) - , m_DisplayFunction(displayFunction) - , m_SetParentWidget(setParentWidget) -{ -} - - - -QString PythonToolWrapper::displayName() const -{ - try { - return boost::python::extract(m_DisplayNameFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return QString(); - } -} - -QString PythonToolWrapper::tooltip() const -{ - try { - return boost::python::extract(m_ToolTipFunction()); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return QString(); - } -} - -QIcon PythonToolWrapper::icon() const -{ - try { - std::vector rows; - boost::python::object l = m_IconFunction(); - if (!l.is_none()) { - for (int i = 0; i < boost::python::len(l); ++i) { - rows.push_back(extract(l[i])); - } - return QIcon(QPixmap(&rows[0])); - } else { - return QIcon(); - } - } catch (const boost::python::error_already_set&) { - reportPythonError(); - return QIcon(); - } -} - -void PythonToolWrapper::display() const -{ - try { - m_SetParentWidget(parentWidget()); - - m_DisplayFunction(); - } catch (const boost::python::error_already_set&) { - reportPythonError(); - } -} diff --git a/src/runner/pythontoolwrapper.h b/src/runner/pythontoolwrapper.h deleted file mode 100644 index 0e07c13..0000000 --- a/src/runner/pythontoolwrapper.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef PYTHONTOOLWRAPPER_H -#define PYTHONTOOLWRAPPER_H - -#include -#ifndef Q_MOC_RUN -#include -#endif - -#include "pythonpluginwrapper.h" - - -class PythonToolWrapper : public MOBase::IPluginTool, public PythonPluginWrapper -{ - - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool) - -public: - PythonToolWrapper(const boost::python::object &initFunction, - const boost::python::object &nameFunction, - const boost::python::object &authorFunction, - const boost::python::object &descriptionFunction, - const boost::python::object &versionFunction, - const boost::python::object &isActiveFunction, - const boost::python::object &settingsFunction, - const boost::python::object &displayNameFunction, - const boost::python::object &tooltipFunction, - const boost::python::object &iconFunction, - const boost::python::object &displayFunction, - const boost::python::object &setParentWidget); - - using PythonPluginWrapper::init; - using PythonPluginWrapper::name; - - virtual QString displayName() const; - virtual QString tooltip() const; - virtual QIcon icon() const; - -public slots: - - virtual void display() const; - -private: - - boost::python::object m_DisplayNameFunction; - boost::python::object m_ToolTipFunction; - boost::python::object m_IconFunction; - boost::python::object m_DisplayFunction; - - boost::python::object m_SetParentWidget; -}; - - - -#endif // PYTHONTOOLWRAPPER_H diff --git a/src/runner/pythonwrapperutilities.h b/src/runner/pythonwrapperutilities.h new file mode 100644 index 0000000..86317e0 --- /dev/null +++ b/src/runner/pythonwrapperutilities.h @@ -0,0 +1,32 @@ +#ifndef PYTHONWRAPPERUTILITIES_H +#define PYTHONWRAPPERUTILITIES_H + +#include + +#include "error.h" + +class MissingImplementation : public MOBase::MyException { +public: + MissingImplementation(QString className, QString methodName) : MyException("Python class implementing \"" + + className + + "\" has no implementation of method \"" + + methodName + "\"") {} +}; + +#define PYCATCH catch (const boost::python::error_already_set &) { reportPythonError(); throw MOBase::MyException("unhandled exception"); }\ + catch (const MissingImplementation &missingImplementationException) { throw missingImplementationException; }\ + catch (...) { throw MOBase::MyException("An unknown exception was thrown in python code"); } + +template +ReturnType basicWrapperFunctionImplementation(const WrapperType *wrapper, const char *methodName, Args... args) +{ + try { + GILock lock; + boost::python::override implementation = wrapper->get_override(methodName); + if (!implementation) + throw MissingImplementation(wrapper->className, methodName); + return implementation(args...).as(); + } PYCATCH; +} + +#endif // PYTHONWRAPPERUTILITIES_H diff --git a/src/runner/sipapiaccess.h b/src/runner/sipapiaccess.h new file mode 100644 index 0000000..c9c95d0 --- /dev/null +++ b/src/runner/sipapiaccess.h @@ -0,0 +1,16 @@ +#ifndef SIPAPIACCESS_H +#define SIPAPIACCESS_H + +#include + +static const sipAPIDef *sipAPI() +{ + static const sipAPIDef *sipApi = nullptr; + if (sipApi == nullptr) { + sipApi = (const sipAPIDef *)PyCapsule_Import("sip._C_API", 0); + } + + return sipApi; +} + +#endif // SIPAPIACCESS_H diff --git a/src/runner/uibasewrappers.h b/src/runner/uibasewrappers.h index 14b6473..c1c2e9c 100644 --- a/src/runner/uibasewrappers.h +++ b/src/runner/uibasewrappers.h @@ -21,8 +21,12 @@ #include #include #include +#include +#include + #include "error.h" #include "gilock.h" +#include "pythonwrapperutilities.h" extern MOBase::IOrganizer *s_Organizer; @@ -53,14 +57,14 @@ public: delete m_Wrapped; } - void requestDescription(int modID, QVariant userData) - { m_Wrapped->requestDescription(modID, userData); } - void requestFiles(int modID, QVariant userData) - { m_Wrapped->requestFiles(modID, userData); } - void requestFileInfo(int modID, int fileID, QVariant userData) - { m_Wrapped->requestFileInfo(modID, fileID, userData); } - void requestToggleEndorsement(int modID, bool endorse, QVariant userData) - { m_Wrapped->requestToggleEndorsement(modID, endorse, userData); } + void requestDescription(QString gameName, int modID, QVariant userData) + { m_Wrapped->requestDescription(gameName, modID, userData); } + void requestFiles(QString gameName, int modID, QVariant userData) + { m_Wrapped->requestFiles(gameName, modID, userData); } + void requestFileInfo(QString gameName, int modID, int fileID, QVariant userData) + { m_Wrapped->requestFileInfo(gameName, modID, fileID, userData); } + void requestToggleEndorsement(QString gameName, int modID, bool endorse, QVariant userData) + { m_Wrapped->requestToggleEndorsement(gameName, modID, endorse, userData); } void onFilesAvailable(boost::python::object callback) { m_FilesAvailableHandler = callback; @@ -236,6 +240,10 @@ struct IOrganizerWrapper : MOBase::IOrganizer, { return this->get_override("createMod")(name); } + virtual MOBase::IPluginGame *getGame(const QString &gameName) const override + { + return this->get_override("getGame")(gameName); + } virtual bool removeMod(MOBase::IModInterface *mod) override { return this->get_override("removeMod")(mod); @@ -373,11 +381,11 @@ struct IDownloadManagerWrapper: MOBase::IDownloadManager, boost::python::wrapper struct IModRepositoryBridgeWrapper: MOBase::IModRepositoryBridge, boost::python::wrapper { - virtual void requestDescription(int modID, QVariant userData) { this->get_override("requestDescription")(modID, userData); } - virtual void requestFiles(int modID, QVariant userData) { this->get_override("requestFiles")(modID, userData); } - virtual void requestFileInfo(int modID, int fileID, QVariant userData) { this->get_override("requestFileInfo")(modID, fileID, userData); } - virtual void requestDownloadURL(int modID, int fileID, QVariant userData) { this->get_override("requestDownloadURL")(modID, fileID, userData); } - virtual void requestToggleEndorsement(int modID, bool endorse, QVariant userData) { this->get_override("requestToggleEndorsement")(modID, endorse, userData); } + virtual void requestDescription(QString gameName, int modID, QVariant userData) { this->get_override("requestDescription")(gameName, modID, userData); } + virtual void requestFiles(QString gameName, int modID, QVariant userData) { this->get_override("requestFiles")(gameName, modID, userData); } + virtual void requestFileInfo(QString gameName, int modID, int fileID, QVariant userData) { this->get_override("requestFileInfo")(gameName, modID, fileID, userData); } + virtual void requestDownloadURL(QString gameName, int modID, int fileID, QVariant userData) { this->get_override("requestDownloadURL")(gameName, modID, fileID, userData); } + virtual void requestToggleEndorsement(QString gameName, int modID, bool endorse, QVariant userData) { this->get_override("requestToggleEndorsement")(gameName, modID, endorse, userData); } }; struct IInstallationManagerWrapper: MOBase::IInstallationManager, boost::python::wrapper @@ -434,51 +442,20 @@ struct IModListWrapper: MOBase::IModList, boost::python::wrapper { - virtual QString gameName() const override { return this->get_override("gameName")(); } - virtual void initializeProfile(const QDir &directory, ProfileSettings settings) const override { - this->get_override("initializeProfile")(directory, settings); - } - virtual QString savegameExtension() const override { return this->get_override("savegameExtension")(); } - virtual QString savegameSEExtension() const override { return this->get_override("savegameSEExtension")(); } - virtual bool isInstalled() const override { return this->get_override("isInstalled")(); } - virtual QIcon gameIcon() const override { return this->get_override("gameIcon")(); } - virtual QDir gameDirectory() const override { return this->get_override("gameDirectory")(); } - virtual QDir dataDirectory() const override { return this->get_override("dataDirectory")(); } - virtual void setGamePath(const QString &path) override { this->get_override("setGamePath")(path); } - virtual QDir documentsDirectory() const override { return this->get_override("documentsDirectory")(); } - virtual QDir savesDirectory() const override { return this->get_override("savesDirectory")(); } - virtual QList executables() const override { return this->get_override("executables")(); } - virtual QString steamAPPId() const override { return this->get_override("steamAPPId")(); } - virtual QStringList primaryPlugins() const override { return this->get_override("primaryPlugins")(); } - virtual QStringList gameVariants() const override { return this->get_override("gameVariants")(); } - virtual void setGameVariant(const QString &variant) override { this->get_override("setGameVariant")(variant); } - virtual QString binaryName() const override { return this->get_override("binaryName")(); } - virtual QString gameShortName() const override { return this->get_override("gameShortName")(); } - virtual QString gameNexusName() const override { return this->get_override("gameNexusName")(); } - virtual QStringList iniFiles() const override { return this->get_override("iniFiles")(); } - virtual QStringList DLCPlugins() const override { return this->get_override("DLCPlugins")(); } - virtual QStringList CCPlugins() const override { return this->get_override("CCPlugins")(); } - virtual LoadOrderMechanism loadOrderMechanism() const override { return this->get_override("loadorderMechanism")(); } - virtual int nexusModOrganizerID() const override { return this->get_override("nexusModOrganizerID")(); } - virtual int nexusGameID() const override { return this->get_override("nexusGameID")(); } - virtual bool looksValid(QDir const &dir) const override { return this->get_override("looksValid")(dir); } - virtual QString gameVersion() const override { return this->get_override("gameVersion")(); } - virtual QString getLauncherName() const override { return this->get_override("getLauncherName")(); } - - //Plugin interface. Could this bit be implemented just once? - virtual bool init(MOBase::IOrganizer *moInfo) override { return this->get_override("init")(moInfo); } - virtual QString name() const override { return this->get_override("name")(); } - virtual QString author() const override { return this->get_override("author")(); } - virtual QString description() const override { return this->get_override("description")(); } - virtual MOBase::VersionInfo version() const override { return this->get_override("version")(); } - virtual bool isActive() const override { return this->get_override("isActive")(); } - virtual QList settings() const override { return this->get_override("settings")(); } - -protected: - - virtual std::map featureList() const override { return this->get_override("featureList")(); } +// This needs to be extendable in Python, so actually needs a wrapper +// Everything else probably doesn't +class ISaveGameWrapper : public MOBase::ISaveGame, public boost::python::wrapper +{ +public: + static constexpr const char* className = "ISaveGameWrapper"; + using boost::python::wrapper::get_override; + virtual QString getFilename() const override { return basicWrapperFunctionImplementation(this, "getFilename"); }; + virtual QDateTime getCreationTime() const override { return basicWrapperFunctionImplementation(this, "getCreationTime"); }; + virtual QString getSaveGroupIdentifier() const override { return basicWrapperFunctionImplementation(this, "getSaveGroupIdentifier"); }; + virtual QStringList allFiles() const override { return basicWrapperFunctionImplementation(this, "allFiles"); }; + virtual bool hasScriptExtenderFile() const override { return basicWrapperFunctionImplementation(this, "hasScriptExtenderFile"); }; }; + #endif // UIBASEWRAPPERS_H