#include "wrappers.h" #include #include "../pybind11_all.h" #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_PURE(bool, GamePlugins, lightPluginsAreSupported, ); } }; 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(FileWrapper, 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 PyPyUnmanagedMods : 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 { auto result = [&] { PYBIND11_OVERRIDE_PURE(QList, UnmanagedMods, secondaryFiles, modName); }(); return QList(result.begin(), result.end()); } }; void add_game_feature_bindings(pybind11::module_ m) { // 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", "name")) .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); // 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, "[optional]"); // ScriptExtender py::class_(m, "ScriptExtender") .def(py::init<>()) .def("BinaryName", &ScriptExtender::BinaryName) .def("PluginPath", &ScriptExtender::PluginPath) .def("loaderName", &ScriptExtender::loaderName) .def("loaderPath", &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", &UnmanagedMods::referenceFile, "mod_name"_a) .def("secondaryFiles", &UnmanagedMods::secondaryFiles, "mod_name"_a); } } // namespace mo2::python namespace mo2::python { class GameFeaturesHelper { using GameFeatures = std::tuple; template static void helper(F&& f, std::index_sequence) { (f(static_cast*>(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>{}); } }; pybind11::object extract_feature(IPluginGame const& game, pybind11::object type) { py::object py_feature = py::none(); GameFeaturesHelper::apply([&](Feature* feature) { if (py::type::of().is(type)) { py_feature = py::cast(game.feature(), py::return_value_policy::reference); } }); return py_feature; } pybind11::dict extract_feature_list(IPluginGame const& game) { // constructing a dict from class name to actual object py::dict dict; GameFeaturesHelper::apply([&](Feature* feature) { dict[py::type::of()] = py::cast(game.feature(), py::return_value_policy::reference); }); return dict; } std::map convert_feature_list(py::dict const& py_features) { std::map features; GameFeaturesHelper::apply([&](Feature* feature) { const auto py_type = py::type::of(); if (py_features.contains(py_type)) { features[std::type_index(typeid(Feature))] = py_features[py_type].cast(); } }); return features; } } // namespace mo2::python