diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3828bd7..0bc8ef8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -51,7 +51,7 @@ add_subdirectory(${PYBIND11_EXTRACTED_PATH})
ExternalProject_Add(loot-api-c++
PREFIX "external"
- URL "https://github.com/loot/loot/releases/download/0.10.2/loot-api_0.10.2-0-g9eb7d26_dev.7z"
+ URL "https://github.com/loot/loot-api/releases/download/0.11.0/loot_api-0.11.0-0-g8864f78_dev-win32.7z"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
@@ -79,7 +79,9 @@ ExternalProject_Add(test-masterlist
# Python Module
#######################################
-pybind11_add_module(loot_api "${CMAKE_SOURCE_DIR}/src/main.cpp" "${CMAKE_BINARY_DIR}/generated/wrapper_version.cpp")
+pybind11_add_module(loot_api "${CMAKE_SOURCE_DIR}/src/main.cpp"
+ "${CMAKE_SOURCE_DIR}/src/convenience.cpp"
+ "${CMAKE_BINARY_DIR}/generated/wrapper_version.cpp")
add_dependencies(loot_api loot-api-c++ test-masterlist)
diff --git a/README.md b/README.md
index 6b5fed5..6f23f23 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ LOOT API Python Module
[](https://travis-ci.org/loot/loot-api-python)
[](http://loot-api-python.readthedocs.io/)
-A Python module that wraps the LOOT API, generated by [pybind11](https://github.com/pybind/pybind11).
+A Python module that wraps the LOOT API, generated by [pybind11](https://github.com/pybind/pybind11). Not everything in the API is exposed: coverage can be extended on request (or by pull request).
## Downloads
diff --git a/src/convenience.cpp b/src/convenience.cpp
new file mode 100644
index 0000000..2d58786
--- /dev/null
+++ b/src/convenience.cpp
@@ -0,0 +1,79 @@
+/* LOOT
+
+A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
+Fallout: New Vegas.
+
+Copyright (C) 2014-2017 Oliver Hamlet
+
+This file is part of LOOT.
+
+LOOT is free software: you can redistribute
+it and/or modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, either version 3 of
+the License, or (at your option) any later version.
+
+LOOT is distributed in the hope that it will
+be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with LOOT. If not, see
+.
+*/
+
+#include "convenience.h"
+
+#include
+
+namespace loot {
+std::shared_ptr CreateDatabase(const GameType game, const std::string & game_path, const std::string & game_local_path) {
+ auto gameHandle = CreateGameHandle(game, game_path, game_local_path);
+
+ return gameHandle->GetDatabase();
+}
+PluginTags GetPluginTags(const std::shared_ptr db, const std::string& plugin) {
+ PluginTags tags;
+
+ auto metadata = db->GetPluginMetadata(plugin, false, false);
+ for (const auto &tag : metadata.GetTags()) {
+ if (tag.IsAddition())
+ tags.added.insert(tag.GetName());
+ else
+ tags.removed.insert(tag.GetName());
+ }
+
+ metadata = db->GetPluginUserMetadata(plugin, false);
+ tags.userlist_modified = !metadata.GetTags().empty();
+ for (const auto &tag : metadata.GetTags()) {
+ if (tag.IsAddition())
+ tags.added.insert(tag.GetName());
+ else
+ tags.removed.insert(tag.GetName());
+ }
+
+ return tags;
+}
+
+PluginCleanliness GetPluginCleanliness(const std::shared_ptr db, const std::string& plugin) {
+ auto metadata = db->GetPluginMetadata(plugin, true, false);
+
+ if (metadata.GetDirtyInfo().empty()) {
+ if (metadata.GetCleanInfo().empty()) {
+ return PluginCleanliness::unknown;
+ } else {
+ return PluginCleanliness::clean;
+ }
+ } else if (!metadata.GetCleanInfo().empty()) {
+ return PluginCleanliness::unknown;
+ }
+
+ for (const auto& info : metadata.GetDirtyInfo()) {
+ if (info.ChooseInfo("en").GetText().find("Do not clean") != std::string::npos) {
+ return PluginCleanliness::do_not_clean;
+ }
+ }
+
+ return PluginCleanliness::unknown;
+}
+}
diff --git a/src/convenience.h b/src/convenience.h
new file mode 100644
index 0000000..b19484a
--- /dev/null
+++ b/src/convenience.h
@@ -0,0 +1,46 @@
+/* LOOT
+
+A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
+Fallout: New Vegas.
+
+Copyright (C) 2014-2017 Oliver Hamlet
+
+This file is part of LOOT.
+
+LOOT is free software: you can redistribute
+it and/or modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, either version 3 of
+the License, or (at your option) any later version.
+
+LOOT is distributed in the hope that it will
+be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with LOOT. If not, see
+.
+*/
+
+#ifndef LOOT_API_PYTHON_CONVENIENCE
+#define LOOT_API_PYTHON_CONVENIENCE
+
+#include
+
+#include
+#include
+
+#include "plugin_cleanliness.h"
+#include "plugin_tags.h"
+
+namespace loot {
+std::shared_ptr CreateDatabase(const GameType game,
+ const std::string& game_path = "",
+ const std::string& game_local_path = "");
+
+PluginTags GetPluginTags(const std::shared_ptr db, const std::string& plugin);
+
+PluginCleanliness GetPluginCleanliness(const std::shared_ptr db, const std::string& plugin);
+}
+
+#endif
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index e71ca47..afdea1f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -26,29 +26,16 @@
#include
#include
+#include "convenience.h"
#include "wrapper_version.h"
-PYBIND11_PLUGIN(loot_api) {
- using loot::CreateDatabase;
- using loot::DatabaseInterface;
- using loot::IsCompatible;
- using loot::GameType;
- using loot::LanguageCode;
- using loot::LootVersion;
- using loot::MessageType;
- using loot::MasterlistInfo;
- using loot::PluginCleanliness;
- using loot::SimpleMessage;
- using loot::PluginTags;
- using loot::WrapperVersion;
-
- using pybind11::arg;
- using pybind11::enum_;
- using pybind11::class_;
- using pybind11::metaclass;
-
- pybind11::module module("loot_api", "A Python module that wraps the LOOT API, generated by pybind11.");
+using pybind11::arg;
+using pybind11::enum_;
+using pybind11::class_;
+using pybind11::metaclass;
+namespace loot {
+void bindEnums(pybind11::module& module) {
enum_(module, "GameType")
.value("tes4", GameType::tes4)
.value("tes5", GameType::tes5)
@@ -57,18 +44,10 @@ PYBIND11_PLUGIN(loot_api) {
.value("fonv", GameType::fonv)
.value("fo4", GameType::fo4);
- enum_(module, "LanguageCode")
- .value("english", LanguageCode::english)
- .value("spanish", LanguageCode::spanish)
- .value("russian", LanguageCode::russian)
- .value("french", LanguageCode::french)
- .value("chinese", LanguageCode::chinese)
- .value("polish", LanguageCode::polish)
- .value("brazilian_portuguese", LanguageCode::brazilian_portuguese)
- .value("finnish", LanguageCode::finnish)
- .value("german", LanguageCode::german)
- .value("danish", LanguageCode::danish)
- .value("korean", LanguageCode::korean);
+ enum_(module, "LogVerbosity")
+ .value("off", LogVerbosity::off)
+ .value("trace", LogVerbosity::trace)
+ .value("warning", LogVerbosity::warning);
enum_(module, "MessageType")
.value("say", MessageType::say)
@@ -80,13 +59,15 @@ PYBIND11_PLUGIN(loot_api) {
.value("dirty", PluginCleanliness::dirty)
.value("do_not_clean", PluginCleanliness::do_not_clean)
.value("unknown", PluginCleanliness::unknown);
+}
+void bindMetadataClasses(pybind11::module& module) {
class_(module, "MasterlistInfo")
.def_readwrite("revision_id", &MasterlistInfo::revision_id)
.def_readwrite("revision_date", &MasterlistInfo::revision_date)
.def_readwrite("is_modified", &MasterlistInfo::is_modified);
- class_(module, "Message")
+ class_(module, "SimpleMessage")
.def_readwrite("type", &SimpleMessage::type)
.def_readwrite("language", &SimpleMessage::language)
.def_readwrite("text", &SimpleMessage::text);
@@ -96,6 +77,11 @@ PYBIND11_PLUGIN(loot_api) {
.def_readwrite("removed", &PluginTags::removed)
.def_readwrite("userlist_modified", &PluginTags::userlist_modified);
+ class_(module, "PluginMetadata")
+ .def("get_simple_messages", &PluginMetadata::GetSimpleMessages);
+}
+
+void bindVersionClasses(pybind11::module& module) {
class_(module, "Version", metaclass())
.def_readonly_static("major", &LootVersion::major)
.def_readonly_static("minor", &LootVersion::minor)
@@ -109,21 +95,48 @@ PYBIND11_PLUGIN(loot_api) {
.def_readonly_static("patch", &WrapperVersion::patch)
.def_readonly_static("revision", &WrapperVersion::revision)
.def_static("string", WrapperVersion::string);
+}
+void bindInterfaceClasses(pybind11::module& module) {
class_>(module, "DatabaseInterface")
.def("load_lists", &DatabaseInterface::LoadLists, arg("masterlist_path"), arg("userlist_path") = "")
.def("eval_lists", &DatabaseInterface::EvalLists)
- .def("sort_plugins", &DatabaseInterface::SortPlugins)
.def("update_masterlist", &DatabaseInterface::UpdateMasterlist)
.def("get_masterlist_revision", &DatabaseInterface::GetMasterlistRevision)
- .def("get_plugin_tags", &DatabaseInterface::GetPluginTags)
- .def("get_plugin_messages", &DatabaseInterface::GetPluginMessages)
- .def("get_plugin_cleanliness", &DatabaseInterface::GetPluginCleanliness)
+ .def("get_plugin_metadata", &DatabaseInterface::GetPluginMetadata,
+ arg("plugin"),
+ arg("includeUserMetadata") = true,
+ arg("evaluateConditions") = false)
+ .def("get_plugin_tags", &GetPluginTags)
+ .def("get_plugin_cleanliness", &GetPluginCleanliness)
.def("write_minimal_list", &DatabaseInterface::WriteMinimalList);
+}
+
+void bindClasses(pybind11::module& module) {
+ bindMetadataClasses(module);
+ bindVersionClasses(module);
+ bindInterfaceClasses(module);
+}
+
+void bindFunctions(pybind11::module& module) {
+ module.def("set_logging_verbosity", &SetLoggingVerbosity);
+
+ module.def("set_log_file", &SetLogFile);
module.def("is_compatible", &IsCompatible);
+ module.def("initialise_locale", &InitialiseLocale);
+
module.def("create_database", &CreateDatabase, arg("game"), arg("game_path") = "", arg("game_local_path") = "");
+}
+}
+
+PYBIND11_PLUGIN(loot_api) {
+ pybind11::module module("loot_api", "A Python module that wraps the LOOT API, generated by pybind11.");
+
+ loot::bindEnums(module);
+ loot::bindClasses(module);
+ loot::bindFunctions(module);
return module.ptr();
}
diff --git a/src/plugin_cleanliness.h b/src/plugin_cleanliness.h
new file mode 100644
index 0000000..58f4a0b
--- /dev/null
+++ b/src/plugin_cleanliness.h
@@ -0,0 +1,52 @@
+/* LOOT
+
+ A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
+ Fallout: New Vegas.
+
+ Copyright (C) 2012-2016 WrinklyNinja
+
+ This file is part of LOOT.
+
+ LOOT is free software: you can redistribute
+ it and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ LOOT is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with LOOT. If not, see
+ .
+ */
+#ifndef LOOT_PLUGIN_CLEANLINESS
+#define LOOT_PLUGIN_CLEANLINESS
+
+/**
+ * The namespace used by the LOOT API.
+ */
+namespace loot {
+/**
+ * @brief Codes used to indicate the cleanliness of a plugin according to the
+ * information contained within the loaded masterlist/userlist.
+ */
+enum struct PluginCleanliness : unsigned int {
+ /** Indicates that the plugin is clean. */
+ clean,
+ /** Indicates that the plugin is dirty. */
+ dirty,
+ /**
+ * Indicates that the plugin contains dirty edits, but that they are
+ * part of the plugin's intended functionality and should not be removed.
+ */
+ do_not_clean,
+ /**
+ * Indicates that no data is available on whether the plugin is dirty or not.
+ */
+ unknown,
+};
+}
+
+#endif
diff --git a/src/plugin_tags.h b/src/plugin_tags.h
new file mode 100644
index 0000000..ef45b5d
--- /dev/null
+++ b/src/plugin_tags.h
@@ -0,0 +1,58 @@
+/* LOOT
+
+ A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
+ Fallout: New Vegas.
+
+ Copyright (C) 2012-2016 WrinklyNinja
+
+ This file is part of LOOT.
+
+ LOOT is free software: you can redistribute
+ it and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ LOOT is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with LOOT. If not, see
+ .
+ */
+#ifndef LOOT_PLUGIN_TAGS
+#define LOOT_PLUGIN_TAGS
+
+#include
+#include
+
+namespace loot {
+/**
+ * @brief A structure that holds data about the Bash Tag suggestions made by
+ * LOOT for a plugin.
+ */
+struct PluginTags {
+ inline PluginTags() : userlist_modified(false) {}
+
+ /**
+ * @brief A set of Bash Tag names suggested for addition to the specified
+ * plugin. Empty if no Bash Tag additions are suggested.
+ */
+ std::set added;
+
+ /**
+ * @brief A set of Bash Tag names suggested for removal from the specified
+ * plugin. Empty if no Bash Tag removals are suggested.
+ */
+ std::set removed;
+
+ /**
+ * @brief `true` if the Bash Tag suggestions were modified by data in the
+ * userlist, `false` otherwise.
+ */
+ bool userlist_modified;
+};
+}
+
+#endif
diff --git a/src/test.py b/src/test.py
index 779111d..2ba192e 100644
--- a/src/test.py
+++ b/src/test.py
@@ -7,11 +7,16 @@ import unittest
from loot_api import Version
from loot_api import WrapperVersion
from loot_api import GameType
-from loot_api import LanguageCode
-from loot_api import Message
+from loot_api import LogVerbosity
+from loot_api import SimpleMessage
from loot_api import MessageType
from loot_api import create_database
from loot_api import is_compatible
+from loot_api import set_logging_verbosity
+from loot_api import initialise_locale
+
+set_logging_verbosity(LogVerbosity.off)
+initialise_locale("")
class GameFixture(unittest.TestCase):
game_path = os.path.join(u'.', u'Oblivion')
@@ -33,14 +38,14 @@ class GameFixture(unittest.TestCase):
class TestLootApi(GameFixture):
def test_is_compatible(self):
self.assertFalse(is_compatible(0, 9, 0))
- self.assertTrue(is_compatible(0, 10, 0))
+ self.assertTrue(is_compatible(0, 11, 0))
def test_version(self):
self.assertEqual(Version.major, 0)
- self.assertEqual(Version.minor, 10)
- self.assertEqual(Version.patch, 2)
+ self.assertEqual(Version.minor, 11)
+ self.assertEqual(Version.patch, 0)
self.assertNotEqual(Version.revision, u'')
- self.assertEqual(Version.string(), "0.10.2")
+ self.assertEqual(Version.string(), "0.11.0")
def test_wrapper_version(self):
self.assertEqual(WrapperVersion.major, 1)
@@ -96,18 +101,10 @@ class TestDatabaseInterface(GameFixture):
]))
self.assertEqual(tags.removed, set([u'C.Water']))
-# cProfile.runctx('self.test_get_plugin_tags_performance()', globals(), locals(), sort='cumtime')
-
-# def test_get_plugin_tags_performance(self):
-# self.db.load_lists(self.masterlist_path, u'')
-#
-# for i in xrange(200000):
-# tags = self.db.get_plugin_tags(u'Unofficial Oblivion Patch.esp')
-
def test_get_plugin_messages(self):
self.db.load_lists(self.masterlist_path, u'')
- messages = self.db.get_plugin_messages(u'Oblivion.esm', LanguageCode.english)
+ messages = self.db.get_plugin_metadata(u'Oblivion.esm').get_simple_messages(u'en')
self.assertEqual(len(messages), 1)
self.assertEqual(messages[0].type, MessageType.error)