diff --git a/src/proxy/proxypython.cpp b/src/proxy/proxypython.cpp index da98fa2..47691ed 100644 --- a/src/proxy/proxypython.cpp +++ b/src/proxy/proxypython.cpp @@ -104,6 +104,8 @@ ProxyPython::ProxyPython() ProxyPython::~ProxyPython() { + delete m_Runner; + if (!m_TempRunnerFile.isEmpty()) { ::FreeLibrary(m_RunnerLib); QFile(m_TempRunnerFile).remove(); diff --git a/src/runner/CMakeLists.txt b/src/runner/CMakeLists.txt index cac7f40..6515a8e 100644 --- a/src/runner/CMakeLists.txt +++ b/src/runner/CMakeLists.txt @@ -23,3 +23,29 @@ endif() requires_project(game_features) requires_library(python) + +add_filter(NAME src/converters GROUPS + converters + pythonutils + shared_ptr_converter + tuple_helper + variant_helper +) + +add_filter(NAME src/runner GROUPS + pythonrunner + pylogger +) + +add_filter(NAME src/utils GROUPS + error + gilock + sipapiaccess +) + +add_filter(NAME src/wrappers GROUPS + gamefeatureswrappers + proxypluginwrappers + pythonwrapperutilities + uibasewrappers +) diff --git a/src/runner/gamefeatureswrappers.cpp b/src/runner/gamefeatureswrappers.cpp index 2bf4b47..f83dd2a 100644 --- a/src/runner/gamefeatureswrappers.cpp +++ b/src/runner/gamefeatureswrappers.cpp @@ -126,15 +126,9 @@ std::vector ModDataContentWrapper::getContentsFor(std::shared_ptr(this, m_SaveGames[file], "getSaveGameInfo", file); -} - -SaveGameInfoWrapper::MissingAssets SaveGameInfoWrapper::getMissingAssets(QString const & file) const -{ - return basicWrapperFunctionImplementation(this, "getMissingAssets", file); + return basicWrapperFunctionImplementation(this, "getMissingAssets", boost::ref(save)); } MOBase::ISaveGameInfoWidget* SaveGameInfoWrapper::getSaveGameWidget(QWidget* parent) const @@ -142,10 +136,6 @@ MOBase::ISaveGameInfoWidget* SaveGameInfoWrapper::getSaveGameWidget(QWidget* par return basicWrapperFunctionImplementation(this, m_SaveGameWidget, "getSaveGameWidget", parent); } -bool SaveGameInfoWrapper::hasScriptExtenderSave(QString const & file) const -{ - return basicWrapperFunctionImplementation(this, "hasScriptExtenderSave", file); -} /// end SaveGameInfo Wrapper ///////////////////////////// /// ScriptExtender Wrapper @@ -170,9 +160,9 @@ QString ScriptExtenderWrapper::loaderPath() const return basicWrapperFunctionImplementation(this, "loaderPath"); } -QStringList ScriptExtenderWrapper::saveGameAttachmentExtensions() const +QString ScriptExtenderWrapper::savegameExtension() const { - return basicWrapperFunctionImplementation(this, "saveGameAttachmentExtensions"); + return basicWrapperFunctionImplementation(this, "savegameExtension"); } bool ScriptExtenderWrapper::isInstalled() const @@ -316,7 +306,7 @@ void registerGameFeaturesPythonConverters() .def("getContentsFor", bpy::pure_virtual(&ModDataContent::getContentsFor), bpy::arg("filetree")) ; - bpy::class_("Content", + bpy::class_("Content", bpy::init>((bpy::arg("id"), "name", "icon", bpy::arg("filter_only") = false))) .add_property("id", &ModDataContent::Content::id) .add_property("name", &ModDataContent::Content::name) @@ -327,12 +317,9 @@ void registerGameFeaturesPythonConverters() } bpy::class_("SaveGameInfo") - .def("getSaveGameInfo", bpy::pure_virtual(&SaveGameInfo::getSaveGameInfo), bpy::return_value_policy(), - bpy::arg("filepath")) - .def("getMissingAssets", bpy::pure_virtual(&SaveGameInfo::getMissingAssets), bpy::arg("filepath")) - .def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), bpy::return_value_policy(), + .def("getMissingAssets", bpy::pure_virtual(&SaveGameInfo::getMissingAssets), bpy::arg("save")) + .def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), bpy::return_value_policy(), bpy::arg("parent"), "[optional]") - .def("hasScriptExtenderSave", bpy::pure_virtual(&SaveGameInfo::hasScriptExtenderSave), bpy::arg("filepath")) ; bpy::class_("ScriptExtender") @@ -340,7 +327,7 @@ void registerGameFeaturesPythonConverters() .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("savegameExtension", bpy::pure_virtual(&ScriptExtender::savegameExtension)) .def("isInstalled", bpy::pure_virtual(&ScriptExtender::isInstalled)) .def("getExtenderVersion", bpy::pure_virtual(&ScriptExtender::getExtenderVersion)) .def("getArch", bpy::pure_virtual(&ScriptExtender::getArch)) diff --git a/src/runner/gamefeatureswrappers.h b/src/runner/gamefeatureswrappers.h index 606b560..7155d8c 100644 --- a/src/runner/gamefeatureswrappers.h +++ b/src/runner/gamefeatureswrappers.h @@ -106,10 +106,8 @@ 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 MissingAssets getMissingAssets(MOBase::ISaveGame const& save) const override; virtual MOBase::ISaveGameInfoWidget *getSaveGameWidget(QWidget *parent = 0) const override; - virtual bool hasScriptExtenderSave(QString const &file) const override; private: // We need to keep the python objects alive: @@ -127,7 +125,7 @@ public: virtual QString PluginPath() const override; virtual QString loaderName() const override; virtual QString loaderPath() const override; - virtual QStringList saveGameAttachmentExtensions() const override; + virtual QString savegameExtension() const override; virtual bool isInstalled() const override; virtual QString getExtenderVersion() const override; virtual WORD getArch() const override; diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 2a1efd3..3d6e99e 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -5,6 +5,7 @@ #include #include "pythonwrapperutilities.h" +#include "uibasewrappers.h" #include #include @@ -71,11 +72,11 @@ QList class_name::settings() const \ QString class_name::localizedName_Default() const { return IPlugin::localizedName(); } \ QString class_name::master_Default() const { return IPlugin::master(); } \ BOOST_PP_EXPR_IF(include_requirements, \ - QList class_name::requirements() const { \ - return basicWrapperFunctionImplementationWithDefault>( \ - this, &class_name::requirements_Default, m_Requirements, "requirements"); \ + std::vector> class_name::requirements() const { \ + return basicWrapperFunctionImplementationWithDefault>>( \ + this, &class_name::requirements_Default, "requirements"); \ } \ - QList class_name::requirements_Default() const { return IPlugin::requirements(); }) + std::vector> class_name::requirements_Default() const { return IPlugin::requirements(); }) #define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(class_name) COMMON_I_PLUGIN_WRAPPER_DEFINITIONS_IMPL(class_name, 1) @@ -147,14 +148,11 @@ void IPluginGameWrapper::initializeProfile(const QDir & directory, ProfileSettin basicWrapperFunctionImplementation(this, "initializeProfile", directory, settings); } -QString IPluginGameWrapper::savegameExtension() const +std::vector> IPluginGameWrapper::listSaves(QDir folder) const { - return basicWrapperFunctionImplementation(this, "savegameExtension"); -} - -QString IPluginGameWrapper::savegameSEExtension() const -{ - return basicWrapperFunctionImplementation(this, "savegameSEExtension"); + // Why do I not need to hold python references here? Is it because those are wrapped + // in shared_ptr? + return basicWrapperFunctionImplementation>>(this, "listSaves", folder); } bool IPluginGameWrapper::isInstalled() const diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index 892314d..1d71f2e 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -19,8 +19,6 @@ // The wrapper for IPluginGame cannot override requirements() since it's final, // so we need to be able to exclude the declarations. #define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(include_requirements) \ - BOOST_PP_EXPR_IF(include_requirements, \ - private: mutable boost::python::object m_Requirements; ) \ public: \ virtual bool init(MOBase::IOrganizer *moInfo) override; \ virtual QString name() const override; \ @@ -33,8 +31,8 @@ virtual QList settings() const override; \ QString localizedName_Default() const; \ QString master_Default() const; \ BOOST_PP_EXPR_IF(include_requirements, \ - virtual QList requirements() const override; \ - QList requirements_Default() const;) + virtual std::vector> requirements() const override; \ + std::vector> requirements_Default() const;) #define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(1) @@ -104,8 +102,7 @@ public: virtual void detectGame() 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 std::vector> listSaves(QDir folder) const override; virtual bool isInstalled() const override; virtual QIcon gameIcon() const override; virtual QDir gameDirectory() const override; diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index e238b58..6fc0df6 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -39,6 +39,7 @@ #include "tuple_helper.h" #include "variant_helper.h" #include "converters.h" +#include "shared_ptr_converter.h" #include "pylogger.h" using namespace MOBase; @@ -93,6 +94,12 @@ BOOST_PYTHON_MODULE(mobase) bpy::register_ptr_to_python>(); bpy::implicitly_convertible, std::shared_ptr>(); + utils::shared_ptr_from_python>(); + bpy::register_ptr_to_python>(); + + utils::shared_ptr_from_python>(); + bpy::register_ptr_to_python>(); + // Containers: utils::register_sequence_container>(); utils::register_sequence_container>(); @@ -100,12 +107,13 @@ BOOST_PYTHON_MODULE(mobase) utils::register_sequence_container>(); utils::register_sequence_container>(); utils::register_sequence_container>(); - utils::register_sequence_container>(); utils::register_sequence_container(); utils::register_sequence_container>(); utils::register_sequence_container>(); utils::register_sequence_container>(); // Required for QVariant since this is QVariantList. - utils::register_sequence_container>>(); + utils::register_sequence_container>>(); + utils::register_sequence_container>>(); + utils::register_sequence_container>>(); utils::register_sequence_container>(); utils::register_sequence_container>(); @@ -250,12 +258,12 @@ BOOST_PYTHON_MODULE(mobase) .def("process", &ExecutableForcedLoadSetting::process) ; - bpy::class_, ISaveGameWrapper*, boost::noncopyable>("ISaveGame") - .def("getFilename", bpy::pure_virtual(&ISaveGame::getFilename)) + bpy::class_, boost::noncopyable>("ISaveGame") + .def("getFilepath", bpy::pure_virtual(&ISaveGame::getFilepath)) .def("getCreationTime", bpy::pure_virtual(&ISaveGame::getCreationTime)) + .def("getName", bpy::pure_virtual(&ISaveGame::getName)) .def("getSaveGroupIdentifier", bpy::pure_virtual(&ISaveGame::getSaveGroupIdentifier)) .def("allFiles", bpy::pure_virtual(&ISaveGame::allFiles)) - .def("hasScriptExtenderFile", bpy::pure_virtual(&ISaveGame::hasScriptExtenderFile)) ; // See Q_DELEGATE for more details. @@ -268,7 +276,7 @@ BOOST_PYTHON_MODULE(mobase) // Plugin requirements: auto iPluginRequirementClass = bpy::class_< - IPluginRequirementWrapper, bpy::bases<>, IPluginRequirementWrapper*, boost::noncopyable>("IPluginRequirement"); + IPluginRequirementWrapper, bpy::bases<>, boost::noncopyable>("IPluginRequirement"); { bpy::scope scope = iPluginRequirementClass; @@ -278,7 +286,7 @@ BOOST_PYTHON_MODULE(mobase) .def("longDescription", &IPluginRequirement::Problem::longDescription); iPluginRequirementClass - .def("check", bpy::pure_virtual(&IPluginRequirement::check)) + .def("check", bpy::pure_virtual(&IPluginRequirement::check), bpy::arg("organizer")) ; } @@ -286,26 +294,24 @@ BOOST_PYTHON_MODULE(mobase) // pluginDependency .def("pluginDependency", +[](QStringList const& pluginNames) { return PluginRequirementFactory::pluginDependency(pluginNames); - }, bpy::return_value_policy(), bpy::arg("plugins")) + }, bpy::arg("plugins")) .def("pluginDependency", +[](QString const& pluginName) { return PluginRequirementFactory::pluginDependency(pluginName); - }, bpy::return_value_policy(), bpy::arg("plugin")) + }, bpy::arg("plugin")) .staticmethod("pluginDependency") // gameDependency .def("gameDependency", +[](QStringList const& gameNames) { return PluginRequirementFactory::gameDependency(gameNames); - }, bpy::return_value_policy(), bpy::arg("games")) + }, bpy::arg("games")) .def("gameDependency", +[](QString const& gameNames) { return PluginRequirementFactory::gameDependency(gameNames); - }, bpy::return_value_policy(), bpy::arg("game")) + }, bpy::arg("game")) .staticmethod("gameDependency") // diagnose - .def("diagnose", &PluginRequirementFactory::diagnose, - bpy::return_value_policy(), bpy::arg("diagnose")) + .def("diagnose", &PluginRequirementFactory::diagnose, bpy::arg("diagnose")) .staticmethod("diagnose") // basic - .def("basic", &PluginRequirementFactory::basic, - bpy::return_value_policy(), (bpy::arg("checker"), "description")) + .def("basic", &PluginRequirementFactory::basic, (bpy::arg("checker"), "description")) .staticmethod("basic"); bpy::class_("FileInfo", bpy::init<>()) @@ -706,7 +712,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_, boost::noncopyable>("GuessedString") .def(bpy::init<>()) - .def(bpy::init((bpy::arg("value"), "quality"))) + .def(bpy::init((bpy::arg("value"), bpy::arg("quality") = EGuessQuality::GUESS_USER))) .def("update", static_cast& (GuessedValue::*)(const QString&)>(&GuessedValue::update), bpy::return_self<>(), bpy::arg("value")) @@ -840,7 +846,7 @@ BOOST_PYTHON_MODULE(mobase) .def("init", bpy::pure_virtual(&MOBase::IPlugin::init), bpy::arg("organizer")) .def("name", bpy::pure_virtual(&MOBase::IPlugin::name)) .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginWrapper::localizedName_Default) - .def("master", &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default) + .def("master", &MOBase::IPlugin::master, &IPluginWrapper::master_Default) .def("author", bpy::pure_virtual(&MOBase::IPlugin::author)) .def("description", bpy::pure_virtual(&MOBase::IPlugin::description)) .def("version", bpy::pure_virtual(&MOBase::IPlugin::version)) @@ -850,7 +856,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_, boost::noncopyable>("IPluginDiagnose") .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginDiagnoseWrapper::localizedName_Default) - .def("master", &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default) + .def("master", &MOBase::IPlugin::master, &IPluginDiagnoseWrapper::master_Default) .def("requirements", &MOBase::IPlugin::requirements, &IPluginDiagnoseWrapper::requirements_Default) .def("activeProblems", bpy::pure_virtual(&MOBase::IPluginDiagnose::activeProblems)) @@ -877,7 +883,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_, boost::noncopyable>("IPluginFileMapper") .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginFileMapperWrapper::localizedName_Default) - .def("master", &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default) + .def("master", &MOBase::IPlugin::master, &IPluginFileMapperWrapper::master_Default) .def("requirements", &MOBase::IPlugin::requirements, &IPluginFileMapperWrapper::requirements_Default) .def("mappings", bpy::pure_virtual(&MOBase::IPluginFileMapper::mappings)) ; @@ -912,13 +918,12 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_, boost::noncopyable>("IPluginGame") .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginGameWrapper::localizedName_Default) - .def("master", &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default) + .def("master", &MOBase::IPlugin::master, &IPluginGameWrapper::master_Default) .def("detectGame", bpy::pure_virtual(&MOBase::IPluginGame::detectGame)) .def("gameName", bpy::pure_virtual(&MOBase::IPluginGame::gameName)) .def("initializeProfile", bpy::pure_virtual(&MOBase::IPluginGame::initializeProfile), (bpy::arg("directory"), "settings")) - .def("savegameExtension", bpy::pure_virtual(&MOBase::IPluginGame::savegameExtension)) - .def("savegameSEExtension", bpy::pure_virtual(&MOBase::IPluginGame::savegameSEExtension)) + .def("listSaves", bpy::pure_virtual(&MOBase::IPluginGame::listSaves), bpy::arg("folder")) .def("isInstalled", bpy::pure_virtual(&MOBase::IPluginGame::isInstalled)) .def("gameIcon", bpy::pure_virtual(&MOBase::IPluginGame::gameIcon)) .def("gameDirectory", bpy::pure_virtual(&MOBase::IPluginGame::gameDirectory)) @@ -1010,7 +1015,7 @@ BOOST_PYTHON_MODULE(mobase) .def("onInstallationStart", &IPluginInstaller::onInstallationStart, (bpy::arg("archive"), bpy::arg("reinstallation"), bpy::arg("current_mod"))) .def("onInstallationEnd", &IPluginInstaller::onInstallationEnd, (bpy::arg("result"), bpy::arg("new_mod"))) .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginInstallerSimpleWrapper::localizedName_Default) - .def("master", &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default) + .def("master", &MOBase::IPlugin::master, &IPluginInstallerSimpleWrapper::master_Default) .def("requirements", &MOBase::IPlugin::requirements, &IPluginInstallerSimpleWrapper::requirements_Default) // Note: Keeping the variant here even if we always return a tuple to be consistent with the wrapper and @@ -1028,7 +1033,7 @@ BOOST_PYTHON_MODULE(mobase) .def("onInstallationStart", &IPluginInstaller::onInstallationStart, (bpy::arg("archive"), bpy::arg("reinstallation"), bpy::arg("current_mod"))) .def("onInstallationEnd", &IPluginInstaller::onInstallationEnd, (bpy::arg("result"), bpy::arg("new_mod"))) .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginInstallerCustomWrapper::localizedName_Default) - .def("master", &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default) + .def("master", &MOBase::IPlugin::master, &IPluginInstallerCustomWrapper::master_Default) .def("requirements", &MOBase::IPlugin::requirements, &IPluginInstallerCustomWrapper::requirements_Default) // Needs to add both otherwize boost does not understand: @@ -1042,7 +1047,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_, boost::noncopyable>("IPluginModPage") .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginModPageWrapper::localizedName_Default) - .def("master", &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default) + .def("master", &MOBase::IPlugin::master, &IPluginModPageWrapper::master_Default) .def("requirements", &MOBase::IPlugin::requirements, &IPluginModPageWrapper::requirements_Default) .def("displayName", bpy::pure_virtual(&IPluginModPage::displayName)) @@ -1056,7 +1061,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_, boost::noncopyable>("IPluginPreview") .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginPreviewWrapper::localizedName_Default) - .def("master", &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default) + .def("master", &MOBase::IPlugin::master, &IPluginPreviewWrapper::master_Default) .def("requirements", &MOBase::IPlugin::requirements, &IPluginPreviewWrapper::requirements_Default) .def("supportedExtensions", bpy::pure_virtual(&IPluginPreview::supportedExtensions)) @@ -1098,6 +1103,8 @@ class PythonRunner : public IPythonRunner public: PythonRunner(); + ~PythonRunner(); + bool initPython(const QString& pythonDir); QList instantiate(const QString& pluginName); bool isPythonInstalled() const; @@ -1148,6 +1155,12 @@ PythonRunner::PythonRunner() m_PythonHome = new wchar_t[MAX_PATH + 1]; } +PythonRunner::~PythonRunner() { + // We need the GIL lock when destroying Python objects. + GILock lock; + m_PythonObjects.clear(); +} + static const char *argv0 = "ModOrganizer.exe"; struct PrintWrapper diff --git a/src/runner/pythonrunner.h b/src/runner/pythonrunner.h index 73de994..c494ae1 100644 --- a/src/runner/pythonrunner.h +++ b/src/runner/pythonrunner.h @@ -13,6 +13,8 @@ public: virtual QList instantiate(const QString &pluginName) = 0; virtual bool isPythonInstalled() const = 0; virtual bool isPythonVersionSupported() const = 0; + + virtual ~IPythonRunner() { } }; diff --git a/src/runner/shared_ptr_converter.h b/src/runner/shared_ptr_converter.h new file mode 100644 index 0000000..8c3cf83 --- /dev/null +++ b/src/runner/shared_ptr_converter.h @@ -0,0 +1,101 @@ +#ifndef PYTHONRUNNER_SHARED_PTR_CONVERTER_H +#define PYTHONRUNNER_SHARED_PTR_CONVERTER_H + +#include + +#include "error.h" +#include "gilock.h" + +namespace utils { + + // Shared pointers are handled in a special way by Boost.Python since they hold + // the wrapped Python object and only release it when the ref counter of the shared + // ptr drops to 0 using shared_ptr_deleter. + // + // Unfortunately for us, this will happen outside of the Python proxy for some objects + // and thus without the GIL lock, making everything crash, so we need a custom deleter + // that holds the GIL while releasing the lock. + // + // Note that this is only useful for Python -> C++ conversion, and without this, Boost + // will automatically wrapped the pointer. The C++ -> Python conversion is handled + // separately by boost::python::register_ptr_to_python. + // + // This is an open Boost.Python problem: https://github.com/boostorg/python/pull/11 + + template + struct shared_ptr_from_python; + + namespace details { + + struct shared_ptr_deleter_with_gil_lock : boost::python::converter::shared_ptr_deleter { + + using shared_ptr_deleter::shared_ptr_deleter; + + void operator()(void const* o) { + GILock lock; + shared_ptr_deleter::operator()(o); + } + + }; + + template + struct shared_ptr_void; + + template + struct shared_ptr_void> { using type = std::shared_ptr; }; + + template + struct shared_ptr_void> { using type = boost::shared_ptr; }; + + template + using shared_ptr_void_t = typename shared_ptr_void::type; + + } + + template + struct shared_ptr_from_python + { + using T = typename SharedPtr::element_type; + + shared_ptr_from_python() + { + using namespace boost::python; + converter::registry::insert(&convertible, &construct, type_id() +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES + , &converter::expected_from_python_type_direct::get_pytype +#endif + ); + } + + private: + static void* convertible(PyObject* p) + { + if (p == Py_None) + return p; + + return boost::python::converter::get_lvalue_from_python(p, boost::python::converter::registered::converters); + } + + static void construct(PyObject* source, boost::python::converter::rvalue_from_python_stage1_data* data) + { + using namespace boost::python; + void* const storage = ((converter::rvalue_from_python_storage*)data)->storage.bytes; + // Deal with the "None" case. + if (data->convertible == source) + new (storage) SharedPtr(); + else + { + details::shared_ptr_void_t hold_convertible_ref_count( + (void*)0, details::shared_ptr_deleter_with_gil_lock(handle<>(borrowed(source)))); + // use aliasing constructor + new (storage) SharedPtr(hold_convertible_ref_count, + static_cast(data->convertible)); + } + + data->convertible = storage; + } + }; + +} + +#endif \ No newline at end of file diff --git a/src/runner/uibasewrappers.h b/src/runner/uibasewrappers.h index 448575d..95e878a 100644 --- a/src/runner/uibasewrappers.h +++ b/src/runner/uibasewrappers.h @@ -41,11 +41,15 @@ public: static constexpr const char* className = "ISaveGameWrapper"; using boost::python::wrapper::get_override; - virtual QString getFilename() const override { return basicWrapperFunctionImplementation(this, "getFilename"); }; + virtual QString getFilepath() const override { return basicWrapperFunctionImplementation(this, "getFilepath"); }; virtual QDateTime getCreationTime() const override { return basicWrapperFunctionImplementation(this, "getCreationTime"); }; + virtual QString getName() const override { return basicWrapperFunctionImplementation(this, "getName"); }; 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"); }; + +protected: + + friend class IPluginGameWrapper; }; // This needs a wrapper but currently I have no idea how to expose this properly to python: @@ -58,7 +62,7 @@ public: // Bring the constructor: using ISaveGameInfoWidget::ISaveGameInfoWidget; - virtual void setSave(QString const& save) override { basicWrapperFunctionImplementation(this, "setSave", save); }; + virtual void setSave(MOBase::ISaveGame const& save) override { basicWrapperFunctionImplementation(this, "setSave", boost::ref(save)); }; }; #endif // UIBASEWRAPPERS_H