#pragma warning( push ) #pragma warning( disable : 4100 ) // a lot of unreferenced formal parameters from boost libraries #include "proxypython.h" #include #pragma warning( pop ) #include #include #include #include #include #include "proxypluginwrappers.h" #include "uibasewrappers.h" #include "error.h" // sip and qt slots seems to conflict #include using namespace MOBase; namespace bpy = boost::python; MOBase::IOrganizer *s_Organizer = NULL; 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(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()); } }; 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) ? objPtr : NULL; } static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { // Extract the character data from the python string const char* value = PyString_AsString(objPtr); assert(value != NULL); // 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 NULL; } } 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>(); } }; struct QVariant_to_python_obj { static PyObject *convert(const QVariant &var) { switch (var.type()) { case QVariant::Int: return PyLong_FromLong(var.toInt()); case QVariant::Bool: return PyBool_FromLong(var.toBool()); case QVariant::String: return bpy::incref(bpy::object(var.toString().toUtf8().constData()).ptr()); case QVariant::List: { QVariantList list = var.toList(); PyObject *result = PyList_New(list.count()); foreach (QVariant var, list) { PyList_Append(result, convert(var)); } return result; } break; default: { PyErr_SetString(PyExc_TypeError, "type unsupported"); 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 NULL; } 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 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)) { result = 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); } else if (PyBool_Check(objPtr)) { bool value = (objPtr == Py_True); constructVariant(value, data); } else if (PyInt_Check(objPtr)) { long value = PyInt_AsLong(objPtr); constructVariant(value, 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 { foreach (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 NULL; } 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 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 NULL; } 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; } }; static const sipAPIDef *sipAPI() { static const sipAPIDef *sipApi = NULL; if (sipApi == NULL) { sipApi = (const sipAPIDef *)PyCapsule_Import("sip._C_API", 0); } return sipApi; } template struct MetaData; template <> struct MetaData { static const char *className() { return "MOBase::INexusBridge"; } }; 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 "QIcon"; } }; template <> struct MetaData { static const char *className() { return "QVariant"; } }; template PyObject *toPyQt(T *objPtr) { if (objPtr == NULL) { qDebug("no input object"); return bpy::incref(Py_None); } const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); if (type == NULL) { 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 == NULL) { qDebug("failed to convert"); return bpy::incref(Py_None); } return bpy::incref(sipObj); } template struct QClass_converters { struct QClass_to_PyQt { static PyObject *convert(const T &object) { const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); if (type == NULL) { return bpy::incref(Py_None); } PyObject *sipObj = sipAPI()->api_convert_from_type((void*)(&object), type, 0); if (sipObj == NULL) { return bpy::incref(Py_None); } return bpy::incref(sipObj); } static PyObject *convert(T *object) { if (object == NULL) { return bpy::incref(Py_None); } const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); if (type == NULL) { return bpy::incref(Py_None); } PyObject *sipObj = sipAPI()->api_convert_from_type(object, type, 0); if (sipObj == NULL) { return bpy::incref(Py_None); } 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_wrapper_type)) { PyErr_SetString(PyExc_TypeError, "type not wrapped"); bpy::throw_error_already_set(); } 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 == NULL) { return bpy::incref(Py_None); } PyObject *sipObj = sipAPI()->api_convert_from_type((void*)(&object), type, 0); if (sipObj == NULL) { return bpy::incref(Py_None); } return bpy::incref(sipObj); } static PyObject *convert(T *object) { if (object == NULL) { return bpy::incref(Py_None); } const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); if (type == NULL) { return bpy::incref(Py_None); } PyObject *sipObj = sipAPI()->api_convert_from_type(object, type, 0); if (sipObj == NULL) { 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(); } 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(); } }; 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(); QInterface_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_("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::enum_("GameType") .value("oblivion", MOBase::IGameInfo::TYPE_OBLIVION) .value("fallout3", MOBase::IGameInfo::TYPE_FALLOUT3) .value("falloutnv", MOBase::IGameInfo::TYPE_FALLOUTNV) .value("skyrim", MOBase::IGameInfo::TYPE_SKYRIM); bpy::class_("VersionInfo") .def(bpy::init()) .def("parse", &MOBase::VersionInfo::parse) .def("canonicalString", &MOBase::VersionInfo::canonicalString); bpy::class_("PluginSetting", bpy::init()); bpy::class_("GameInfo") .def("type", bpy::pure_virtual(&MOBase::IGameInfo::type)) .def("path", bpy::pure_virtual(&MOBase::IGameInfo::path)) .def("binaryName", bpy::pure_virtual(&MOBase::IGameInfo::binaryName)); bpy::class_("IOrganizer") .def("gameInfo", bpy::pure_virtual(&MOBase::IOrganizer::gameInfo), bpy::return_value_policy()) //.def("createNexusBridge", bpy::pure_virtual(&MOBase::IOrganizer::createNexusBridge), bpy::return_value_policy()) .def("profileName", bpy::pure_virtual(&MOBase::IOrganizer::profileName)) .def("profilePath", bpy::pure_virtual(&MOBase::IOrganizer::profilePath)) .def("downloadsPath", bpy::pure_virtual(&MOBase::IOrganizer::downloadsPath)) .def("appVersion", bpy::pure_virtual(&MOBase::IOrganizer::appVersion)) .def("getMod", bpy::pure_virtual(&MOBase::IOrganizer::getMod), bpy::return_value_policy()) .def("createMod", bpy::pure_virtual(&MOBase::IOrganizer::createMod), bpy::return_value_policy()) .def("removeMod", bpy::pure_virtual(&MOBase::IOrganizer::removeMod)) .def("modDataChanged", bpy::pure_virtual(&MOBase::IOrganizer::modDataChanged)) .def("pluginSetting", bpy::pure_virtual(&IOrganizer::pluginSetting)) .def("pluginDataPath", bpy::pure_virtual(&IOrganizer::pluginDataPath)) .def("installMod", bpy::pure_virtual(&IOrganizer::installMod)) .def("downloadManager", bpy::pure_virtual(&IOrganizer::downloadManager), bpy::return_value_policy()); bpy::class_("ModRepositoryBridge") .def("requestDescription", &ModRepositoryBridgeWrapper::requestDescription) .def("requestFiles", &ModRepositoryBridgeWrapper::requestFiles) .def("requestFileInfo", &ModRepositoryBridgeWrapper::requestFileInfo) .def("requestDownloadURL", &ModRepositoryBridgeWrapper::requestDownloadURL) .def("requestToggleEndorsement", &ModRepositoryBridgeWrapper::requestToggleEndorsement) .def("onFilesAvailable", &ModRepositoryBridgeWrapper::onFilesAvailable) .def("onRequestFailed", &ModRepositoryBridgeWrapper::onRequestFailed); 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::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::class_("IPluginTool") .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginTool::setParentWidget)); bpy::class_("IPluginInstallerCustom") .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginInstallerCustom::setParentWidget)); GuessedValue_converters(); bpy::to_python_converter(); QList_from_python_obj(); bpy::to_python_converter, QList_to_python_list >(); stdset_from_python_list(); } static char* argv0 = "ModOrganizer.exe"; ProxyPython::ProxyPython() : m_MOInfo(NULL) { try { PyImport_AppendInittab("mobase", &initmobase); Py_Initialize(); qDebug("Python: %s", Py_GetVersion()); PySys_SetArgv(0, &argv0); bpy::object main_module = bpy::import("__main__"); bpy::object main_namespace = main_module.attr("__dict__"); main_namespace["cStringIO"] = bpy::import("cStringIO"); main_namespace["sys"] = bpy::import("sys"); bpy::exec("s_ErrIO = cStringIO.StringIO()\n" "sys.stderr = s_ErrIO", main_namespace); } 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"); } } } bool ProxyPython::init(IOrganizer *moInfo) { m_MOInfo = moInfo; s_Organizer = moInfo; return true; } QString ProxyPython::name() const { return tr("Python Proxy"); } QString ProxyPython::author() const { return "Tannin"; } QString ProxyPython::description() const { return tr("Proxy Plugin to allow plugins written in python to be loaded"); } VersionInfo ProxyPython::version() const { return VersionInfo(1, 0, 0, VersionInfo::RELEASE_FINAL); } bool ProxyPython::isActive() const { return true; } QList ProxyPython::settings() const { return QList(); } QStringList ProxyPython::pluginList(const QString &pluginPath) const { QDirIterator iter(pluginPath, QStringList("*.py")); QStringList result; while (iter.hasNext()) { result.append(iter.next()); } return result; } bool handled_exec_file(bpy::str filename, bpy::object globals = bpy::object(), bpy::object locals = bpy::object()) { return bpy::handle_exception(boost::bind(bpy::exec_file, filename, globals, locals)); } #define TRY_PLUGIN_TYPE(type, var) do { \ bpy::extract extr(var); \ if (extr.check()) { \ QObject *res = extr; \ return res; \ }\ } while (false) QObject *ProxyPython::instantiate(const QString &pluginName) { try { GILock lock; bpy::object main_module = bpy::import("__main__"); bpy::object main_namespace = main_module.attr("__dict__"); bpy::object mobase_module((bpy::handle<>(PyImport_ImportModule("mobase")))); main_namespace["sys"] = bpy::import("sys"); main_namespace["mobase"] = mobase_module; QString appendDataPath = QString("sys.path.append(\"%1\")").arg(m_MOInfo->pluginDataPath()); bpy::eval(appendDataPath.toUtf8().constData(), main_namespace); std::string temp = ToString(pluginName); if (handled_exec_file(temp.c_str(), main_namespace)) { reportPythonError(); return NULL; } m_PythonObjects[pluginName] = main_namespace["createPlugin"](); bpy::object pluginObj = m_PythonObjects[pluginName]; TRY_PLUGIN_TYPE(IPluginInstallerCustom, pluginObj); TRY_PLUGIN_TYPE(IPluginTool, pluginObj); } catch (const bpy::error_already_set&) { qWarning("failed to run python script \"%s\"", qPrintable(pluginName)); reportPythonError(); } return NULL; } #if QT_VERSION < QT_VERSION_CHECK(5,0,0) Q_EXPORT_PLUGIN2(proxyPython, ProxyPython) #endif