#include "wrappers.h" #include #include "../pybind11_all.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "pyfiletree.h" namespace py = pybind11; using namespace MOBase; using namespace pybind11::literals; namespace mo2::python { class PyBSAInvalidation : public BSAInvalidation { public: bool isInvalidationBSA(const QString& bsaName) override { PYBIND11_OVERRIDE_PURE(bool, BSAInvalidation, isInvalidationBSA, bsaName); } void deactivate(MOBase::IProfile* profile) override { PYBIND11_OVERRIDE_PURE(void, BSAInvalidation, deactivate, profile); } void activate(MOBase::IProfile* profile) override { PYBIND11_OVERRIDE_PURE(void, BSAInvalidation, activate, profile); } bool prepareProfile(MOBase::IProfile* profile) override { PYBIND11_OVERRIDE_PURE(bool, BSAInvalidation, prepareProfile, profile); } }; class PyDataArchives : public DataArchives { public: QStringList vanillaArchives() const override { PYBIND11_OVERRIDE_PURE(QStringList, DataArchives, vanillaArchives, ); } QStringList archives(const MOBase::IProfile* profile) const override { PYBIND11_OVERRIDE_PURE(QStringList, DataArchives, archives, profile); } void addArchive(MOBase::IProfile* profile, int index, const QString& archiveName) override { PYBIND11_OVERRIDE_PURE(void, DataArchives, addArchive, profile, index, archiveName); } void removeArchive(MOBase::IProfile* profile, const QString& archiveName) override { PYBIND11_OVERRIDE_PURE(void, DataArchives, removeArchive, profile, archiveName); } }; class PyGamePlugins : public GamePlugins { public: void writePluginLists(const MOBase::IPluginList* pluginList) override { PYBIND11_OVERRIDE_PURE(void, GamePlugins, writePluginLists, pluginList); } void readPluginLists(MOBase::IPluginList* pluginList) override { // TODO: cannot update plugin list or create one from Python so this is // useless PYBIND11_OVERRIDE_PURE(void, GamePlugins, readPluginLists, pluginList); } QStringList getLoadOrder() override { PYBIND11_OVERRIDE_PURE(QStringList, GamePlugins, getLoadOrder, ); } bool lightPluginsAreSupported() override { PYBIND11_OVERRIDE(bool, GamePlugins, lightPluginsAreSupported, ); } bool mediumPluginsAreSupported() override { PYBIND11_OVERRIDE(bool, GamePlugins, mediumPluginsAreSupported, ); } bool blueprintPluginsAreSupported() override { PYBIND11_OVERRIDE(bool, GamePlugins, blueprintPluginsAreSupported, ); } }; class PyLocalSavegames : public LocalSavegames { public: MappingType mappings(const QDir& profileSaveDir) const override { PYBIND11_OVERRIDE_PURE(MappingType, LocalSavegames, mappings, profileSaveDir); } bool prepareProfile(MOBase::IProfile* profile) override { PYBIND11_OVERRIDE_PURE(bool, LocalSavegames, prepareProfile, profile); } }; class PyModDataChecker : public ModDataChecker { public: CheckReturn dataLooksValid(std::shared_ptr fileTree) const override { PYBIND11_OVERRIDE_PURE(CheckReturn, ModDataChecker, dataLooksValid, fileTree); } std::shared_ptr fix(std::shared_ptr fileTree) const override { PYBIND11_OVERRIDE(std::shared_ptr, ModDataChecker, fix, fileTree); } }; class PyModDataContent : public ModDataContent { public: std::vector getAllContents() const override { PYBIND11_OVERRIDE_PURE(std::vector, ModDataContent, getAllContents, ); ; } std::vector getContentsFor(std::shared_ptr fileTree) const override { PYBIND11_OVERRIDE_PURE(std::vector, ModDataContent, getContentsFor, fileTree); } }; class PySaveGameInfo : public SaveGameInfo { public: MissingAssets getMissingAssets(MOBase::ISaveGame const& save) const override { PYBIND11_OVERRIDE_PURE(MissingAssets, SaveGameInfo, getMissingAssets, &save); } ISaveGameInfoWidget* getSaveGameWidget(QWidget* parent = 0) const override { PYBIND11_OVERRIDE_PURE(ISaveGameInfoWidget*, SaveGameInfo, getSaveGameWidget, parent); } }; class PyScriptExtender : public ScriptExtender { public: QString BinaryName() const override { PYBIND11_OVERRIDE_PURE(QString, ScriptExtender, binaryName, ); } QString PluginPath() const override { PYBIND11_OVERRIDE_PURE(DirectoryWrapper, ScriptExtender, pluginPath, ); } QString loaderName() const override { PYBIND11_OVERRIDE_PURE(QString, ScriptExtender, loaderName, ); } QString loaderPath() const override { PYBIND11_OVERRIDE_PURE(FileWrapper, ScriptExtender, loaderPath, ); } QString savegameExtension() const override { PYBIND11_OVERRIDE_PURE(QString, ScriptExtender, savegameExtension, ); } bool isInstalled() const override { PYBIND11_OVERRIDE_PURE(bool, ScriptExtender, isInstalled, ); } QString getExtenderVersion() const override { PYBIND11_OVERRIDE_PURE(QString, ScriptExtender, getExtenderVersion, ); } WORD getArch() const override { PYBIND11_OVERRIDE_PURE(WORD, ScriptExtender, getArch, ); } }; class PyUnmanagedMods : public UnmanagedMods { public: QStringList mods(bool onlyOfficial) const override { PYBIND11_OVERRIDE_PURE(QStringList, UnmanagedMods, mods, onlyOfficial); } QString displayName(const QString& modName) const override { PYBIND11_OVERRIDE_PURE(QString, UnmanagedMods, displayName, modName); } QFileInfo referenceFile(const QString& modName) const override { PYBIND11_OVERRIDE_PURE(FileWrapper, UnmanagedMods, referenceFile, modName); } QStringList secondaryFiles(const QString& modName) const override { return toQStringList([&] { PYBIND11_OVERRIDE_PURE(QList, UnmanagedMods, secondaryFiles, modName); }()); } }; void add_game_feature_bindings(pybind11::module_ m) { // this is just to allow accepting GameFeature in function, we do not expose // anything from game feature to Python since typeInfo() is useless in Python // py::class_>(m, "GameFeature"); // BSAInvalidation py::class_>(m, "BSAInvalidation") .def(py::init<>()) .def("isInvalidationBSA", &BSAInvalidation::isInvalidationBSA, "name"_a) .def("deactivate", &BSAInvalidation::deactivate, "profile"_a) .def("activate", &BSAInvalidation::activate, "profile"_a); // DataArchives py::class_>(m, "DataArchives") .def(py::init<>()) .def("vanillaArchives", &DataArchives::vanillaArchives) .def("archives", &DataArchives::archives, "profile"_a) .def("addArchive", &DataArchives::addArchive, "profile"_a, "index"_a, "name"_a) .def("removeArchive", &DataArchives::removeArchive, "profile"_a, "name"_a); // GamePlugins py::class_>(m, "GamePlugins") .def(py::init<>()) .def("writePluginLists", &GamePlugins::writePluginLists, "plugin_list"_a) .def("readPluginLists", &GamePlugins::readPluginLists, "plugin_list"_a) .def("getLoadOrder", &GamePlugins::getLoadOrder) .def("lightPluginsAreSupported", &GamePlugins::lightPluginsAreSupported) .def("mediumPluginsAreSupported", &GamePlugins::mediumPluginsAreSupported) .def("blueprintPluginsAreSupported", &GamePlugins::blueprintPluginsAreSupported); // LocalSavegames py::class_>(m, "LocalSavegames") .def(py::init<>()) .def("mappings", &LocalSavegames::mappings, "profile_save_dir"_a) .def("prepareProfile", &LocalSavegames::prepareProfile, "profile"_a); // ModDataChecker py::class_> pyModDataChecker(m, "ModDataChecker"); py::enum_(pyModDataChecker, "CheckReturn") .value("INVALID", ModDataChecker::CheckReturn::INVALID) .value("FIXABLE", ModDataChecker::CheckReturn::FIXABLE) .value("VALID", ModDataChecker::CheckReturn::VALID) .export_values(); pyModDataChecker.def(py::init<>()) .def("dataLooksValid", &ModDataChecker::dataLooksValid, "filetree"_a) .def("fix", &ModDataChecker::fix, "filetree"_a); // ModDataContent py::class_> pyModDataContent(m, "ModDataContent"); py::class_(pyModDataContent, "Content") .def(py::init(), "id"_a, "name"_a, "icon"_a, "filter_only"_a = false) .def_property_readonly("id", &ModDataContent::Content::id) .def_property_readonly("name", &ModDataContent::Content::name) .def_property_readonly("icon", &ModDataContent::Content::icon) .def("isOnlyForFilter", &ModDataContent::Content::isOnlyForFilter); pyModDataContent.def(py::init<>()) .def("getAllContents", &ModDataContent::getAllContents) .def("getContentsFor", &ModDataContent::getContentsFor, "filetree"_a); // SaveGameInfo py::class_>(m, "SaveGameInfo") .def(py::init<>()) .def("getMissingAssets", &SaveGameInfo::getMissingAssets, "save"_a) .def("getSaveGameWidget", &SaveGameInfo::getSaveGameWidget, py::return_value_policy::reference, "parent"_a); // ScriptExtender py::class_>(m, "ScriptExtender") .def(py::init<>()) .def("binaryName", &ScriptExtender::BinaryName) .def("pluginPath", wrap_return_for_directory(&ScriptExtender::PluginPath)) .def("loaderName", &ScriptExtender::loaderName) .def("loaderPath", wrap_return_for_filepath(&ScriptExtender::loaderPath)) .def("savegameExtension", &ScriptExtender::savegameExtension) .def("isInstalled", &ScriptExtender::isInstalled) .def("getExtenderVersion", &ScriptExtender::getExtenderVersion) .def("getArch", &ScriptExtender::getArch); // UnmanagedMods py::class_>(m, "UnmanagedMods") .def(py::init<>()) .def("mods", &UnmanagedMods::mods, "official_only"_a) .def("displayName", &UnmanagedMods::displayName, "mod_name"_a) .def("referenceFile", wrap_return_for_filepath(&UnmanagedMods::referenceFile), "mod_name"_a) .def( "secondaryFiles", [](UnmanagedMods* m, const QString& modName) -> QList { auto result = m->secondaryFiles(modName); return {result.begin(), result.end()}; }, "mod_name"_a); } void add_igamefeatures_classes(py::module_ m) { py::class_(m, "IGameFeatures") .def("registerFeature", py::overload_cast, int, bool>(&IGameFeatures::registerFeature), "games"_a, "feature"_a, "priority"_a, "replace"_a = false) .def("registerFeature", py::overload_cast, int, bool>(&IGameFeatures::registerFeature), "game"_a, "feature"_a, "priority"_a, "replace"_a = false) .def("registerFeature", py::overload_cast, int, bool>( &IGameFeatures::registerFeature), "feature"_a, "priority"_a, "replace"_a = false) .def("unregisterFeature", &IGameFeatures::unregisterFeature, "feature"_a) .def("unregisterFeatures", &unregister_feature, "feature_type"_a) .def("gameFeature", &extract_feature, "feature_type"_a, py ::return_value_policy::reference); } } // namespace mo2::python namespace mo2::python { class GameFeaturesHelper { template static void helper(F&& f, std::index_sequence) { (f(static_cast< std::tuple_element_t>( nullptr)), ...); } public: // apply the function f on a null-pointer of type Feature* for each game // feature template static void apply(F&& f) { helper(f, std::make_index_sequence< std::tuple_size_v>{}); } }; pybind11::object extract_feature(IGameFeatures const& gameFeatures, pybind11::object type) { py::object py_feature = py::none(); GameFeaturesHelper::apply([&](Feature*) { if (py::type::of().is(type)) { py_feature = py::cast(gameFeatures.gameFeature(), py::return_value_policy::reference); } }); return py_feature; } int unregister_feature(MOBase::IGameFeatures& gameFeatures, pybind11::object type) { int count = 0; GameFeaturesHelper::apply([&](Feature*) { if (py::type::of().is(type)) { count = gameFeatures.unregisterFeatures(); } }); return count; } } // namespace mo2::python