#include "pythonrunner.h" #pragma warning( disable : 4100 ) #pragma warning( disable : 4996 ) #include #include #include #include #include "uibasewrappers.h" #include "proxypluginwrappers.h" #include "gamefeatureswrappers.h" #include "sipApiAccess.h" #include #include #include #include #include #include // sip and qt slots seems to conflict #include #ifndef Q_MOC_RUN #include #endif MOBase::IOrganizer *s_Organizer = nullptr; class PythonRunner : public IPythonRunner { public: PythonRunner(const MOBase::IOrganizer *moInfo); bool initPython(const QString &pythonDir); QList instantiate(const QString &pluginName); bool isPythonInstalled() const; bool isPythonVersionSupported() const; private: void initPath(boost::python::object &moduleNamespace); private: std::map m_PythonObjects; const MOBase::IOrganizer *m_MOInfo; char *m_PythonHome; }; IPythonRunner *CreatePythonRunner(MOBase::IOrganizer *moInfo, const QString &pythonDir) { s_Organizer = moInfo; PythonRunner *result = new PythonRunner(moInfo); if (result->initPython(pythonDir)) { return result; } else { delete result; return nullptr; } } using namespace MOBase; namespace bpy = boost::python; struct QString_to_python_str { static PyObject *convert(const QString &str) { // It's safer to explicitly convert to unicode as if we don't, this can return either str or unicode without it being easy to know which to expect bpy::object pyStr = bpy::object(str.toUtf8().constData()); if (PyString_Check(pyStr.ptr())) pyStr = pyStr.attr("decode")("utf-8"); return bpy::incref(pyStr.ptr()); } }; template struct QFlags_to_int { static PyObject *convert(const QFlags &flags) { return bpy::incref(bpy::object(static_cast(flags)).ptr()); } }; struct QString_from_python_str { QString_from_python_str() { bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id()); } static void *convertible(PyObject *objPtr) { return PyString_Check(objPtr) || PyUnicode_Check(objPtr) ? objPtr : nullptr; } static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { // Ensure the string uses 8-bit characters PyObject *strPtr = PyUnicode_Check(objPtr) ? PyUnicode_AsUTF8String(objPtr) : objPtr; // Extract the character data from the python string const char* value = PyString_AsString(strPtr); // Deallocate local copy if one was made if (strPtr != objPtr) Py_DecRef(strPtr); assert(value != nullptr); // allocate storage void *storage = ((bpy::converter::rvalue_from_python_storage*)data)->storage.bytes; // construct QString in the allocated mr new (storage) QString(value); data->convertible = storage; } }; template struct GuessedValue_converters { struct GuessedValue_to_python { static PyObject *convert(const GuessedValue &var) { bpy::list result; const std::set &values = var.variants(); for (auto iter = values.begin(); iter != values.end(); ++iter) { result.append(bpy::make_tuple(*iter, GUESS_GOOD)); } return bpy::incref(result.ptr()); } }; struct GuessedValue_from_python { GuessedValue_from_python() { bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id >()); } static void *convertible(PyObject *objPtr) { if PyList_Check(objPtr) { return objPtr; } else { 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; GuessedValue *result = new (storage) GuessedValue(); bpy::list source(bpy::handle<>(bpy::borrowed(objPtr))); int length = bpy::len(source); for (int i = 0; i < length; ++i) { bpy::tuple cell = bpy::extract(source[i]); result->update(bpy::extract(cell[0]), bpy::extract(cell[1])); } data->convertible = storage; } }; GuessedValue_converters() { GuessedValue_from_python(); bpy::to_python_converter, GuessedValue_to_python>(); } }; 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) { switch (var.type()) { 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()).ptr()); case QVariant::List: { return bpy::incref(bpy::object(var.toList()).ptr()); } break; case QVariant::Map: { return bpy::incref(bpy::object(var.toMap()).ptr()); } break; default: { PyErr_Format(PyExc_TypeError, "type unsupported: %d", var.type()); throw bpy::error_already_set(); } break; } } }; struct QVariant_from_python_obj { QVariant_from_python_obj() { bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id()); } static void *convertible(PyObject *objPtr) { if (!PyString_Check(objPtr) && !PyInt_Check(objPtr) && !PyBool_Check(objPtr) && !PyList_Check(objPtr)) { return nullptr; } return objPtr; } template static void constructVariant(const T &value, bpy::converter::rvalue_from_python_stage1_data *data) { void* storage = ((bpy::converter::rvalue_from_python_storage*)data)->storage.bytes; new (storage) QVariant(value); data->convertible = storage; } static void 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)) { 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)) { 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... constructVariant(bpy::extract(objPtr)(), data); } else { PyErr_SetString(PyExc_TypeError, "type unsupported"); throw bpy::error_already_set(); } } }; template struct QList_to_python_list { static PyObject *convert(const QList &list) { bpy::list pyList; try { for (const T &item : list) { pyList.append(item); } } catch (const bpy::error_already_set&) { reportPythonError(); } PyObject *res = bpy::incref(pyList.ptr()); return res; } }; template struct QList_from_python_obj { QList_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; QList *result = new (storage) QList(); bpy::list source(bpy::handle<>(bpy::borrowed(objPtr))); int length = bpy::len(source); for (int i = 0; i < length; ++i) { result->append(bpy::extract(source[i])); } data->convertible = storage; } }; 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 { QFlags_from_python_obj() { bpy::converter::registry::push_back( &convertible, &construct, bpy::type_id>()); } static void* convertible(PyObject *objPtr) { return PyInt_Check(objPtr) ? objPtr : nullptr; } static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { int intVersion = (int)PyInt_AsLong(objPtr); T tVersion = (T)intVersion; void *storage = ((bpy::converter::rvalue_from_python_storage> *)data)->storage.bytes; new (storage) QFlags(tVersion); data->convertible = storage; } }; template struct stdset_from_python_list { stdset_from_python_list() { 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::set *result = new (storage) std::set(); bpy::list source(bpy::handle<>(bpy::borrowed(objPtr))); int length = bpy::len(source); for (int i = 0; i < length; ++i) { result->insert(bpy::extract(source[i])); } data->convertible = storage; } }; struct IModRepositoryBridge_to_python { static PyObject *convert(IModRepositoryBridge *bridge) { ModRepositoryBridgeWrapper wrapper(bridge); return bpy::incref(bpy::object(wrapper).ptr()); } }; template struct MetaData; 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 "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"; } }; template PyObject *toPyQt(T *objPtr) { if (objPtr == nullptr) { qDebug("no input object"); return bpy::incref(Py_None); } const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); if (type == nullptr) { qDebug("failed to determine type: %s", MetaData::className()); return bpy::incref(Py_None); } PyObject *sipObj = sipAPI()->api_convert_from_type(objPtr, type, 0); if (sipObj == nullptr) { qDebug("failed to convert"); return bpy::incref(Py_None); } return bpy::incref(sipObj); } template struct QClass_converters { struct QClass_to_PyQt { template static typename std::enable_if_t, T*> getSafeCopy(T *qClass) { return new T(*qClass); } template static typename std::enable_if_t, T*> getSafeCopy(T *qClass) { return qClass; } static PyObject *convert(const T &object) { const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); if (type == nullptr) { return bpy::incref(Py_None); } 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); } static PyObject *convert(T *object) { if (object == nullptr) { return bpy::incref(Py_None); } const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); if (type == nullptr) { return bpy::incref(Py_None); } 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); } static PyObject *convert(const T *object) { return convert((T*)object); } }; static void *QClass_from_PyQt(PyObject *objPtr) { 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(); } // 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; } QClass_converters() { bpy::converter::registry::insert(&QClass_from_PyQt, bpy::type_id()); bpy::to_python_converter(); bpy::to_python_converter(); bpy::to_python_converter(); } }; template struct QInterface_converters { struct QInterface_to_PyQt { 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); if (sipObj == nullptr) { return bpy::incref(Py_None); } return bpy::incref(sipObj); } static PyObject *convert(T *object) { if (object == nullptr) { return bpy::incref(Py_None); } const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); if (type == nullptr) { return bpy::incref(Py_None); } PyObject *sipObj = sipAPI()->api_convert_from_type(object, type, 0); if (sipObj == nullptr) { return bpy::incref(Py_None); } return bpy::incref(sipObj); } static PyObject *convert(const T *object) { return convert((T*)object); } }; static void *QInterface_from_PyQt(PyObject *objPtr) { if (!PyObject_TypeCheck(objPtr, sipAPI()->api_wrapper_type)) { bpy::throw_error_already_set(); } // 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; } QInterface_converters() { bpy::converter::registry::insert(&QInterface_from_PyQt, bpy::type_id()); bpy::to_python_converter(); bpy::to_python_converter(); } }; int getArgCount(PyObject *object) { int result = 0; PyObject *funcCode = PyObject_GetAttrString(object, "func_code"); if (funcCode) { PyObject *argCount = PyObject_GetAttrString(funcCode, "co_argcount"); if(argCount) { result = PyInt_AsLong(argCount); Py_DECREF(argCount); } Py_DECREF(funcCode); } return result; } template struct Functor0_converter { struct FunctorWrapper { FunctorWrapper(boost::python::object callable) : m_Callable(callable) { } RET operator()() { GILock lock; return (RET) m_Callable(); } boost::python::object m_Callable; }; Functor0_converter() { bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); } static void *convertible(PyObject *object) { if (!PyCallable_Check(object) || (getArgCount(object) != 0)) { 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 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 { struct FunctorWrapper { FunctorWrapper(boost::python::object callable) : m_Callable(callable) { } RET operator()(const PAR1 ¶m1, const PAR2 ¶m2) { GILock lock; return (RET) m_Callable(param1, param2); } boost::python::object m_Callable; }; Functor2_converter() { bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); } static void *convertible(PyObject *object) { if (!PyCallable_Check(object) || (getArgCount(object) != 2)) { 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; } }; BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(updateWithQuality, MOBase::GuessedValue::update, 2, 2) BOOST_PYTHON_MODULE(mobase) { PyEval_InitThreads(); bpy::to_python_converter(); QVariant_from_python_obj(); bpy::to_python_converter(); QString_from_python_str(); //QClass_converters(); QClass_converters(); QClass_converters(); QClass_converters(); QClass_converters(); QClass_converters(); QClass_converters(); QClass_converters(); QClass_converters(); QInterface_converters(); bpy::def("toPyQt", &toPyQt); bpy::def("toPyQt", &toPyQt); bpy::enum_("ReleaseType") .value("final", MOBase::VersionInfo::RELEASE_FINAL) .value("candidate", MOBase::VersionInfo::RELEASE_CANDIDATE) .value("beta", MOBase::VersionInfo::RELEASE_BETA) .value("alpha", MOBase::VersionInfo::RELEASE_ALPHA) .value("prealpha", MOBase::VersionInfo::RELEASE_PREALPHA) ; bpy::enum_("VersionScheme") .value("discover", MOBase::VersionInfo::SCHEME_DISCOVER) .value("regular", MOBase::VersionInfo::SCHEME_REGULAR) .value("decimalmark", MOBase::VersionInfo::SCHEME_DECIMALMARK) .value("numbersandletters", MOBase::VersionInfo::SCHEME_NUMBERSANDLETTERS) .value("date", MOBase::VersionInfo::SCHEME_DATE) .value("literal", MOBase::VersionInfo::SCHEME_LITERAL) ; bpy::class_("VersionInfo") .def(bpy::init()) .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("removeMod", bpy::pure_virtual(&IOrganizer::removeMod)) .def("modDataChanged", bpy::pure_virtual(&IOrganizer::modDataChanged)) .def("pluginSetting", bpy::pure_virtual(&IOrganizer::pluginSetting)) .def("setPluginSetting", bpy::pure_virtual(&IOrganizer::pluginSetting)) .def("persistent", bpy::pure_virtual(&IOrganizer::persistent)) .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("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("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)) ; bpy::class_("IProfile") .def("name", bpy::pure_virtual(&IProfile::name)) .def("absolutePath", bpy::pure_virtual(&IProfile::absolutePath)) .def("localSavesEnabled", bpy::pure_virtual(&IProfile::localSavesEnabled)) .def("localSettingsEnabled", bpy::pure_virtual(&IProfile::localSettingsEnabled)) ; bpy::class_("ModRepositoryBridge") .def(bpy::init()) .def("requestDescription", &ModRepositoryBridgeWrapper::requestDescription) .def("requestFiles", &ModRepositoryBridgeWrapper::requestFiles) .def("requestFileInfo", &ModRepositoryBridgeWrapper::requestFileInfo) .def("requestToggleEndorsement", &ModRepositoryBridgeWrapper::requestToggleEndorsement) .def("onFilesAvailable", &ModRepositoryBridgeWrapper::onFilesAvailable) .def("onFileInfoAvailable", &ModRepositoryBridgeWrapper::onFileInfoAvailable) .def("onDescriptionAvailable", &ModRepositoryBridgeWrapper::onDescriptionAvailable) .def("onEndorsementToggled", &ModRepositoryBridgeWrapper::onEndorsementToggled) .def("onRequestFailed", &ModRepositoryBridgeWrapper::onRequestFailed) ; bpy::class_("IModRepositoryBridge") .def("requestDescription", bpy::pure_virtual(&IModRepositoryBridge::requestDescription)) .def("requestFiles", bpy::pure_virtual(&IModRepositoryBridge::requestFiles)) .def("requestFileInfo", bpy::pure_virtual(&IModRepositoryBridge::requestFileInfo)) .def("requestDownloadURL", bpy::pure_virtual(&IModRepositoryBridge::requestDownloadURL)) .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)) .def("downloadPath", bpy::pure_virtual(&IDownloadManager::downloadPath)) ; bpy::class_("IInstallationManager") .def("extractFile", bpy::pure_virtual(&IInstallationManager::extractFile)) .def("extractFiles", bpy::pure_virtual(&IInstallationManager::extractFiles)) .def("installArchive", bpy::pure_virtual(&IInstallationManager::installArchive)) .def("setURL", bpy::pure_virtual(&IInstallationManager::setURL)) ; bpy::class_("IModInterface") .def("name", bpy::pure_virtual(&IModInterface::name)) .def("absolutePath", bpy::pure_virtual(&IModInterface::absolutePath)) .def("setVersion", bpy::pure_virtual(&IModInterface::setVersion)) .def("setNewestVersion", bpy::pure_virtual(&IModInterface::setNewestVersion)) .def("setIsEndorsed", bpy::pure_virtual(&IModInterface::setIsEndorsed)) .def("setNexusID", bpy::pure_virtual(&IModInterface::setNexusID)) .def("addNexusCategory", bpy::pure_virtual(&IModInterface::addNexusCategory)) .def("addCategory", bpy::pure_virtual(&IModInterface::addCategory)) .def("removeCategory", bpy::pure_virtual(&IModInterface::removeCategory)) .def("categories", bpy::pure_virtual(&IModInterface::categories)) .def("setName", bpy::pure_virtual(&IModInterface::setName)) .def("remove", bpy::pure_virtual(&IModInterface::remove)) ; bpy::enum_("GuessQuality") .value("invalid", MOBase::GUESS_INVALID) .value("fallback", MOBase::GUESS_FALLBACK) .value("good", MOBase::GUESS_GOOD) .value("meta", MOBase::GUESS_META) .value("preset", MOBase::GUESS_PRESET) .value("user", MOBase::GUESS_USER) ; bpy::class_, boost::noncopyable>("GuessedString") .def("update", static_cast &(GuessedValue::*)(const QString&, EGuessQuality)>(&GuessedValue::update), bpy::return_value_policy(), updateWithQuality()) .def("variants", &MOBase::GuessedValue::variants, bpy::return_value_policy()) ; bpy::to_python_converter>(); QFlags_from_python_obj(); Functor0_converter(); // converter for the onRefreshed-callback bpy::class_("IPluginList") .def("state", bpy::pure_virtual(&MOBase::IPluginList::state)) .def("priority", bpy::pure_virtual(&MOBase::IPluginList::priority)) .def("loadOrder", bpy::pure_virtual(&MOBase::IPluginList::loadOrder)) .def("isMaster", bpy::pure_virtual(&MOBase::IPluginList::isMaster)) .def("masters", bpy::pure_virtual(&MOBase::IPluginList::masters)) .def("origin", bpy::pure_virtual(&MOBase::IPluginList::origin)) .def("onRefreshed", bpy::pure_virtual(&MOBase::IPluginList::onRefreshed)) .def("onPluginMoved", bpy::pure_virtual(&MOBase::IPluginList::onPluginMoved)) .def("pluginNames", bpy::pure_virtual(&MOBase::IPluginList::pluginNames)) .def("setState", bpy::pure_virtual(&MOBase::IPluginList::setState)) .def("setLoadOrder", bpy::pure_virtual(&MOBase::IPluginList::setLoadOrder)) ; bpy::to_python_converter>(); QFlags_from_python_obj(); Functor2_converter(); // converter for the onModStateChanged-callback bpy::class_("IModList") .def("displayName", bpy::pure_virtual(&MOBase::IModList::displayName)) .def("allMods", bpy::pure_virtual(&MOBase::IModList::allMods)) .def("state", bpy::pure_virtual(&MOBase::IModList::state)) .def("setActive", bpy::pure_virtual(&MOBase::IModList::setActive)) .def("priority", bpy::pure_virtual(&MOBase::IModList::priority)) .def("setPriority", bpy::pure_virtual(&MOBase::IModList::setPriority)) .def("onModStateChanged", bpy::pure_virtual(&MOBase::IModList::onModStateChanged)) .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) .value("configuration", MOBase::IPluginGame::CONFIGURATION) .value("savegames", MOBase::IPluginGame::SAVEGAMES) .value("preferDefaults", MOBase::IPluginGame::PREFER_DEFAULTS) ; bpy::to_python_converter>(); QFlags_from_python_obj(); bpy::class_("IPluginGame") .def("gameName", bpy::pure_virtual(&MOBase::IPluginGame::gameName)) .def("initializeProfile", bpy::pure_virtual(&MOBase::IPluginGame::initializeProfile)) .def("savegameExtension", bpy::pure_virtual(&MOBase::IPluginGame::savegameExtension)) .def("savegameSEExtension", bpy::pure_virtual(&MOBase::IPluginGame::savegameSEExtension)) .def("isInstalled", bpy::pure_virtual(&MOBase::IPluginGame::isInstalled)) .def("gameIcon", bpy::pure_virtual(&MOBase::IPluginGame::gameIcon)) .def("gameDirectory", bpy::pure_virtual(&MOBase::IPluginGame::gameDirectory)) .def("dataDirectory", bpy::pure_virtual(&MOBase::IPluginGame::dataDirectory)) .def("setGamePath", bpy::pure_virtual(&MOBase::IPluginGame::setGamePath)) .def("documentsDirectory", bpy::pure_virtual(&MOBase::IPluginGame::documentsDirectory)) .def("savesDirectory", bpy::pure_virtual(&MOBase::IPluginGame::savesDirectory)) .def("executables", bpy::pure_virtual(&MOBase::IPluginGame::executables)) .def("steamAPPId", bpy::pure_virtual(&MOBase::IPluginGame::steamAPPId)) .def("primaryPlugins", bpy::pure_virtual(&MOBase::IPluginGame::primaryPlugins)) .def("gameVariants", bpy::pure_virtual(&MOBase::IPluginGame::gameVariants)) .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("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)) .def("gameVersion", bpy::pure_virtual(&MOBase::IPluginGame::gameVersion)) .def("getLauncherName", bpy::pure_virtual(&MOBase::IPluginGame::getLauncherName)) //Plugin interface. .def("init", bpy::pure_virtual(&MOBase::IPluginGame::init)) .def("name", bpy::pure_virtual(&MOBase::IPluginGame::name)) .def("author", bpy::pure_virtual(&MOBase::IPluginGame::author)) .def("description", bpy::pure_virtual(&MOBase::IPluginGame::description)) .def("version", bpy::pure_virtual(&MOBase::IPluginGame::version)) .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::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(); 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(); } PythonRunner::PythonRunner(const MOBase::IOrganizer *moInfo) : m_MOInfo(moInfo) { m_PythonHome = new char[MAX_PATH + 1]; } static const char *argv0 = "ModOrganizer.exe"; bool PythonRunner::initPython(const QString &pythonPath) { try { if (!pythonPath.isEmpty() && !QFile::exists(pythonPath + "/python.exe")) { return false; } strncpy(m_PythonHome, pythonPath.toUtf8().constData(), MAX_PATH); if (!pythonPath.isEmpty()) { Py_SetPythonHome(m_PythonHome); } char argBuffer[MAX_PATH]; strcpy(argBuffer, argv0); Py_SetProgramName(argBuffer); PyImport_AppendInittab("mobase", &initmobase); Py_OptimizeFlag = 2; Py_NoSiteFlag = 1; Py_InitializeEx(0); if (!Py_IsInitialized()) { return false; } PySys_SetArgv(0, (char**)&argBuffer); bpy::object mainModule = bpy::import("__main__"); bpy::object mainNamespace = mainModule.attr("__dict__"); mainNamespace["sys"] = bpy::import("sys"); initPath(mainNamespace); bpy::import("site"); mainNamespace["cStringIO"] = bpy::import("cStringIO"); bpy::exec("s_ErrIO = cStringIO.StringIO()\n" "sys.stderr = s_ErrIO", mainNamespace); return true; } catch (const bpy::error_already_set&) { qDebug("failed to init python"); PyErr_Print(); if (PyErr_Occurred()) { PyErr_Print(); } else { qCritical("An unexpected C++ exception was thrown in python code"); } return false; } } bool handled_exec_file(bpy::str filename, bpy::object globals = bpy::object(), bpy::object locals = bpy::object()) { return bpy::handle_exception(std::bind(bpy::exec_file, filename, globals, locals)); } #define TRY_PLUGIN_TYPE(type, var) do { \ bpy::extract extr(var); \ if (extr.check()) { \ QObject *res = extr; \ interfaceList.append(res); \ }\ } while (false) void PythonRunner::initPath(bpy::object &moduleNamespace) { static QString paths[] = { m_MOInfo->pluginDataPath(), QCoreApplication::applicationDirPath(), QCoreApplication::applicationDirPath() + "/python27.zip", QCoreApplication::applicationDirPath() + "/python27.zip/Lib", QCoreApplication::applicationDirPath() + "/python27.zip/DLLs", QCoreApplication::applicationDirPath() + "/python27.zip/Lib/site-packages", }; for (int i = 0; i < sizeof(paths) / sizeof(QString); ++i) { QString expr = QString("sys.path.insert(%1, \"%2\")").arg(i).arg(paths[i]); bpy::eval(expr.toUtf8().constData(), moduleNamespace); } } QList PythonRunner::instantiate(const QString &pluginName) { try { GILock lock; bpy::object mainModule = bpy::import("__main__"); bpy::object moduleNamespace = mainModule.attr("__dict__"); bpy::object sys = bpy::import("sys"); moduleNamespace["sys"] = sys; moduleNamespace["mobase"] = bpy::import("mobase"); std::string temp = ToString(pluginName); if (handled_exec_file(temp.c_str(), moduleNamespace)) { reportPythonError(); return QList(); } m_PythonObjects[pluginName] = moduleNamespace["createPlugin"](); bpy::object pluginObj = m_PythonObjects[pluginName]; 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 QList(); } bool PythonRunner::isPythonInstalled() const { return Py_IsInitialized() != 0; } bool PythonRunner::isPythonVersionSupported() const { const char *version = Py_GetVersion(); return strstr(version, "2.7") == version; }