diff --git a/cxx/.gitignore b/cxx/.gitignore
index e9881e13..796b96d1 100644
--- a/cxx/.gitignore
+++ b/cxx/.gitignore
@@ -1,2 +1 @@
/build
-/include
diff --git a/cxx/include/loot/api.h b/cxx/include/loot/api.h
new file mode 100644
index 00000000..33dd52ec
--- /dev/null
+++ b/cxx/include/loot/api.h
@@ -0,0 +1,116 @@
+/* LOOT
+
+ A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
+ Fallout: New Vegas.
+
+ Copyright (C) 2013-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_API_H
+#define LOOT_API_H
+
+#include
+#include
+#include
+#include
+
+#include "loot/api_decorator.h"
+#include "loot/enum/game_type.h"
+#include "loot/enum/log_level.h"
+#include "loot/exception/condition_syntax_error.h"
+#include "loot/exception/cyclic_interaction_error.h"
+#include "loot/exception/error_categories.h"
+#include "loot/exception/file_access_error.h"
+#include "loot/exception/undefined_group_error.h"
+#include "loot/game_interface.h"
+#include "loot/loot_version.h"
+
+namespace loot {
+/**
+ * @}
+ * @name Logging Functions
+ * @{
+ */
+
+/**
+ * @brief Set the callback function that is called when logging.
+ * @details If this function is not called, the default behaviour is to
+ * print messages to the console.
+ * @param callback
+ * The function called when logging. The first parameter is the
+ * level of the message being logged, and the second is the message.
+ */
+LOOT_API void SetLoggingCallback(
+ std::function callback);
+
+/**
+ * @}
+ * @name Version Functions
+ * @{
+ */
+
+/**
+ * @brief Checks for API compatibility.
+ * @details Checks whether the loaded API is compatible with the given
+ * version of the API, abstracting API stability policy away from
+ * clients. The version numbering used is major.minor.patch.
+ * @param major
+ * The major version number to check.
+ * @param minor
+ * The minor version number to check.
+ * @param patch
+ * The patch version number to check.
+ * @returns True if the API versions are compatible, false otherwise.
+ */
+LOOT_API bool IsCompatible(const unsigned int major,
+ const unsigned int minor,
+ const unsigned int patch);
+
+/**
+ * @}
+ * @name Lifecycle Management Functions
+ * @{
+ */
+
+/**
+ * @brief Initialise a new game handle.
+ * @details Creates a handle for a game, which is then used by all
+ * game-specific functions.
+ * @param game
+ * A game code for which to create the handle.
+ * @param game_path
+ * The relative or absolute path to the directory containing the
+ * game's executable.
+ * @param game_local_path
+ * The relative or absolute path to the game's local data folder, or an
+ * empty path. The local data folder is usually in `%%LOCALAPPDATA%`, but
+ * Morrowind has no local data folder and OpenMW's is in the user's
+ * My Games folder on Windows and in `$HOME/.config` on Linux. If an
+ * empty path is provided, the API will attempt to look up the relevant
+ * local data path, which may fail in some situations (e.g. when running
+ * libloot natively on Linux for a game other than Morrowind or OpenMW).
+ * @returns The new game handle.
+ */
+LOOT_API std::unique_ptr CreateGameHandle(
+ const GameType game,
+ const std::filesystem::path& game_path,
+ const std::filesystem::path& game_local_path = "");
+}
+
+#endif
diff --git a/cxx/include/loot/api_decorator.h b/cxx/include/loot/api_decorator.h
new file mode 100644
index 00000000..8a64d730
--- /dev/null
+++ b/cxx/include/loot/api_decorator.h
@@ -0,0 +1,44 @@
+/* LOOT
+
+ A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
+ Fallout: New Vegas.
+
+ Copyright (C) 2013-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_API_DECORATOR
+#define LOOT_API_DECORATOR
+
+/* set up dll import/export decorators
+ when compiling the dll on windows, ensure LOOT_EXPORT is defined. clients
+ that use this header do not need to define anything to import the symbols
+ properly. */
+#if defined(_WIN32)
+#ifdef LOOT_STATIC
+#define LOOT_API
+#elif defined LOOT_EXPORT
+#define LOOT_API __declspec(dllexport)
+#else
+#define LOOT_API __declspec(dllimport)
+#endif
+#else
+#define LOOT_API
+#endif
+
+#endif
diff --git a/cxx/include/loot/database_interface.h b/cxx/include/loot/database_interface.h
new file mode 100644
index 00000000..3ce1263e
--- /dev/null
+++ b/cxx/include/loot/database_interface.h
@@ -0,0 +1,235 @@
+/* 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_DATABASE_INTERFACE
+#define LOOT_DATABASE_INTERFACE
+
+#include
+#include
+#include
+#include
+
+#include "loot/exception/cyclic_interaction_error.h"
+#include "loot/metadata/group.h"
+#include "loot/metadata/message.h"
+#include "loot/metadata/plugin_metadata.h"
+
+namespace loot {
+/** @brief The interface provided by API's database handle. */
+class DatabaseInterface {
+public:
+ virtual ~DatabaseInterface() = default;
+
+ /**
+ * @name Data Reading & Writing
+ * @{
+ */
+
+ /**
+ * @brief Loads the masterlist, userlist and masterlist prelude from the
+ * paths specified.
+ * @details Can be called multiple times, each time replacing the
+ * previously-loaded data.
+ * @param masterlist_path
+ * The relative or absolute path to the masterlist file that should be
+ * loaded.
+ * @param userlist_path
+ * The relative or absolute path to the userlist file that should be
+ * loaded, or an empty path. If an empty path, no userlist will be
+ * loaded.
+ * @param masterlist_prelude_path
+ * The relative or absolute path to the masterlist prelude file that
+ * should be loaded. If an empty path, no masterlist prelude will be
+ * loaded.
+ */
+ virtual void LoadLists(
+ const std::filesystem::path& masterlist_path,
+ const std::filesystem::path& userlist_path = "",
+ const std::filesystem::path& masterlist_prelude_path = "") = 0;
+
+ /**
+ * Writes a metadata file containing all loaded user-added metadata.
+ * @param outputFile
+ * The path to which the file shall be written.
+ * @param overwrite
+ * If `false` and `outputFile` already exists, no data will be
+ * written. Otherwise, data will be written.
+ */
+ virtual void WriteUserMetadata(const std::filesystem::path& outputFile,
+ const bool overwrite) const = 0;
+
+ /**
+ * @brief Writes a minimal metadata file that only contains plugins with
+ * Bash Tag suggestions and/or dirty info, plus the suggestions and
+ * info themselves.
+ * @param outputFile
+ * The path to which the file shall be written.
+ * @param overwrite
+ * If `false` and `outputFile` already exists, no data will be
+ * written. Otherwise, data will be written.
+ */
+ virtual void WriteMinimalList(const std::filesystem::path& outputFile,
+ const bool overwrite) const = 0;
+
+ /**
+ * @}
+ * @name Non-plugin Data Access
+ * @{
+ */
+
+ /**
+ * @brief Gets the Bash Tags that are listed in the loaded metadata lists.
+ * @details Bash Tag suggestions can include Bash Tags not in this list.
+ * @returns A set of Bash Tag names.
+ */
+ virtual std::vector GetKnownBashTags() const = 0;
+
+ /**
+ * @brief Get all general messages listen in the loaded metadata lists.
+ * @param evaluateConditions
+ * If true, any metadata conditions are evaluated before the metadata
+ * is returned, otherwise unevaluated metadata is returned. Evaluating
+ * general message conditions also clears the condition cache before
+ * evaluating conditions.
+ * @returns A vector of messages supplied in the metadata lists but not
+ * attached to any particular plugin.
+ */
+ virtual std::vector GetGeneralMessages(
+ bool evaluateConditions = false) const = 0;
+
+ /**
+ * @brief Gets the groups that are defined in the loaded metadata lists.
+ * @param includeUserMetadata
+ * If true, any group metadata present in the userlist is included in
+ * the returned metadata, otherwise the metadata returned only includes
+ * metadata from the masterlist.
+ * @returns An vector of Group objects. Each Group's name is unique, if a
+ * group has masterlist and user metadata the two are merged into a
+ * single group object.
+ */
+ virtual std::vector GetGroups(
+ bool includeUserMetadata = true) const = 0;
+
+ /**
+ * @brief Gets the groups that are defined or extended in the loaded userlist.
+ * @returns An unordered set of Group objects.
+ */
+ virtual std::vector GetUserGroups() const = 0;
+
+ /**
+ * @brief Sets the group definitions to store in the userlist, overwriting any
+ * existing definitions there.
+ * @param groups
+ * The unordered set of Group objects to set.
+ */
+ virtual void SetUserGroups(const std::vector& groups) = 0;
+
+ /**
+ * @brief Get the "shortest" path between the two given groups according to
+ * their load after metadata.
+ * @details The "shortest" path is defined as the path that maximises the
+ * amount of user metadata involved while minimising the amount of
+ * masterlist metadata involved. It's not the path involving the
+ * fewest groups.
+ * @param fromGroupName
+ * The name of the source group, that loads earlier.
+ * @param toGroupName
+ * The name of the destination group, that loads later.
+ * @returns A vector of Vertex elements representing the path from the source
+ * group to the destination group, or an empty vector if no path
+ * exists.
+ */
+ virtual std::vector GetGroupsPath(
+ const std::string& fromGroupName,
+ const std::string& toGroupName) const = 0;
+
+ /**
+ * @}
+ * @name Plugin Data Access
+ * @{
+ */
+
+ /**
+ * @brief Get all a plugin's loaded metadata.
+ * @param plugin
+ * The filename of the plugin to look up metadata for.
+ * @param includeUserMetadata
+ * If true, any user metadata the plugin has is included in the
+ * returned metadata, otherwise the metadata returned only includes
+ * metadata from the masterlist.
+ * @param evaluateConditions
+ * If true, any metadata conditions are evaluated before the metadata
+ * is returned, otherwise unevaluated metadata is returned. Evaluating
+ * plugin metadata conditions does not clear the condition cache.
+ * @returns If the plugin has metadata, an optional containing that metadata,
+ * otherwise an optional containing no value.
+ */
+ virtual std::optional GetPluginMetadata(
+ const std::string& plugin,
+ bool includeUserMetadata = true,
+ bool evaluateConditions = false) const = 0;
+
+ /**
+ * @brief Get a plugin's metadata loaded from the given userlist.
+ * @param plugin
+ * The filename of the plugin to look up user-added metadata for.
+ * @param evaluateConditions
+ * If true, any metadata conditions are evaluated before the metadata
+ * is returned, otherwise unevaluated metadata is returned. Evaluating
+ * plugin metadata conditions does not clear the condition cache.
+ * @returns If the plugin has user-added metadata, an optional containing
+ * that metadata, otherwise an optional containing no value.
+ */
+ virtual std::optional GetPluginUserMetadata(
+ const std::string& plugin,
+ bool evaluateConditions = false) const = 0;
+
+ /**
+ * @brief Sets a plugin's user metadata, overwriting any existing user
+ * metadata.
+ * @param pluginMetadata
+ * The user metadata you want to set, with plugin.Name() being the
+ * filename of the plugin the metadata is for.
+ */
+ virtual void SetPluginUserMetadata(const PluginMetadata& pluginMetadata) = 0;
+
+ /**
+ * @brief Discards all loaded user metadata for the plugin with the given
+ * filename.
+ * @param plugin
+ * The filename of the plugin for which all user-added metadata
+ * should be deleted.
+ */
+ virtual void DiscardPluginUserMetadata(const std::string& plugin) = 0;
+
+ /**
+ * @brief Discards all loaded user metadata for all plugins, and any
+ * user-added general messages and known bash tags.
+ */
+ virtual void DiscardAllUserMetadata() = 0;
+
+ /** @} */
+};
+}
+
+#endif
diff --git a/cxx/include/loot/enum/edge_type.h b/cxx/include/loot/enum/edge_type.h
new file mode 100644
index 00000000..db7b135c
--- /dev/null
+++ b/cxx/include/loot/enum/edge_type.h
@@ -0,0 +1,53 @@
+/* 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_EDGE_TYPE
+#define LOOT_EDGE_TYPE
+
+/**
+ * The namespace used by libloot.
+ */
+namespace loot {
+/**
+ * @brief An enum representing the different possible types of interactions
+ * between plugins or groups.
+ */
+enum struct EdgeType : unsigned int {
+ hardcoded,
+ masterFlag,
+ master,
+ masterlistRequirement,
+ userRequirement,
+ masterlistLoadAfter,
+ userLoadAfter,
+ masterlistGroup,
+ userGroup,
+ recordOverlap,
+ assetOverlap,
+ tieBreak,
+ blueprintMaster,
+};
+}
+
+#endif
diff --git a/cxx/include/loot/enum/game_type.h b/cxx/include/loot/enum/game_type.h
new file mode 100644
index 00000000..ccec865c
--- /dev/null
+++ b/cxx/include/loot/enum/game_type.h
@@ -0,0 +1,59 @@
+/* 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_GAME_TYPE
+#define LOOT_GAME_TYPE
+
+/**
+ * The namespace used by libloot.
+ */
+namespace loot {
+/** @brief Codes used to create database handles for specific games. */
+enum struct GameType : unsigned int {
+ /** The Elder Scrolls IV: Oblivion */
+ tes4,
+ /** The Elder Scrolls V: Skyrim */
+ tes5,
+ /** Fallout 3 */
+ fo3,
+ /** Fallout: New Vegas */
+ fonv,
+ /** Fallout 4 */
+ fo4,
+ /** The Elder Scrolls V: Skyrim Special Edition */
+ tes5se,
+ /** Fallout 4 VR */
+ fo4vr,
+ /** Skyrim VR */
+ tes5vr,
+ /** The Elder Scrolls III: Morrowind */
+ tes3,
+ /** Starfield */
+ starfield,
+ /** OpenMW */
+ openmw
+};
+}
+
+#endif
diff --git a/cxx/include/loot/enum/log_level.h b/cxx/include/loot/enum/log_level.h
new file mode 100644
index 00000000..5fbb0e84
--- /dev/null
+++ b/cxx/include/loot/enum/log_level.h
@@ -0,0 +1,45 @@
+/* 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_LOG_LEVEL
+#define LOOT_LOG_LEVEL
+
+/**
+ * The namespace used by libloot.
+ */
+namespace loot {
+/**
+ * @brief Codes used to specify different levels of API logging.
+ */
+enum struct LogLevel : unsigned int {
+ trace,
+ debug,
+ info,
+ warning,
+ error,
+ fatal
+};
+}
+
+#endif
diff --git a/cxx/include/loot/enum/message_type.h b/cxx/include/loot/enum/message_type.h
new file mode 100644
index 00000000..c726b7c5
--- /dev/null
+++ b/cxx/include/loot/enum/message_type.h
@@ -0,0 +1,49 @@
+/* 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_MESSAGE_TYPE
+#define LOOT_MESSAGE_TYPE
+
+/**
+ * The namespace used by libloot.
+ */
+namespace loot {
+/** @brief Codes used to indicate the type of a message. */
+enum struct MessageType : unsigned int {
+ /** A notification message that is of no significant severity. */
+ say,
+ /**
+ * A warning message, used to indicate that an issue may be present that the
+ * user may wish to act on.
+ */
+ warn,
+ /**
+ * An error message, used to indicate that an issue that requires user action
+ * is present.
+ */
+ error,
+};
+}
+
+#endif
diff --git a/cxx/include/loot/exception/condition_syntax_error.h b/cxx/include/loot/exception/condition_syntax_error.h
new file mode 100644
index 00000000..105e0a86
--- /dev/null
+++ b/cxx/include/loot/exception/condition_syntax_error.h
@@ -0,0 +1,41 @@
+/* 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_EXCEPTION_CONDITION_SYNTAX_ERROR
+#define LOOT_EXCEPTION_CONDITION_SYNTAX_ERROR
+
+#include
+
+namespace loot {
+/**
+ * @brief An exception class thrown if invalid syntax is encountered when
+ * parsing a metadata condition.
+ */
+class ConditionSyntaxError : public std::system_error {
+public:
+ using std::system_error::system_error;
+};
+}
+
+#endif
diff --git a/cxx/include/loot/exception/cyclic_interaction_error.h b/cxx/include/loot/exception/cyclic_interaction_error.h
new file mode 100644
index 00000000..dea19fde
--- /dev/null
+++ b/cxx/include/loot/exception/cyclic_interaction_error.h
@@ -0,0 +1,61 @@
+/* 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_EXCEPTION_CYCLIC_INTERACTION_ERROR
+#define LOOT_EXCEPTION_CYCLIC_INTERACTION_ERROR
+
+#include
+#include
+
+#include "loot/api_decorator.h"
+#include "loot/vertex.h"
+
+namespace loot {
+/**
+ * @brief An exception class thrown if a cyclic interaction is detected when
+ * sorting a load order.
+ */
+class CyclicInteractionError : public std::runtime_error {
+public:
+ /**
+ * @brief Construct an exception detailing a plugin or group graph cycle.
+ * @param cycle A representation of the cyclic path.
+ */
+ LOOT_API CyclicInteractionError(std::vector cycle);
+
+ /**
+ * @brief Get a representation of the cyclic path.
+ * @details Each Vertex is the name of a graph element (plugin or group) and
+ * the type of the edge going to the next Vertex. The last Vertex
+ * has an edge going to the first Vertex.
+ * @return A vector of Vertex elements representing the cyclic path.
+ */
+ LOOT_API std::vector GetCycle() const;
+
+private:
+ std::vector cycle_;
+};
+}
+
+#endif
diff --git a/cxx/include/loot/exception/error_categories.h b/cxx/include/loot/exception/error_categories.h
new file mode 100644
index 00000000..710cfa5a
--- /dev/null
+++ b/cxx/include/loot/exception/error_categories.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_ERROR_CATEGORIES
+#define LOOT_ERROR_CATEGORIES
+
+#include
+
+#include "loot/api_decorator.h"
+
+namespace loot {
+/**
+ * @brief Get the error category that can be used to identify system_error
+ * exceptions that are due to esplugin errors.
+ * @returns A reference to the static object of unspecified runtime type,
+ * derived from std::error_category.
+ */
+LOOT_API const std::error_category& esplugin_category();
+
+/**
+ * @brief Get the error category that can be used to identify system_error
+ * exceptions that are due to libloadorder errors.
+ * @returns A reference to the static object of unspecified runtime type,
+ * derived from std::error_category.
+ */
+LOOT_API const std::error_category& libloadorder_category();
+
+/**
+ * @brief Get the error category that can be used to identify system_error
+ * exceptions that are due to loot condition interpreter errors.
+ * @returns A reference to the static object of unspecified runtime type,
+ * derived from std::error_category.
+ */
+LOOT_API const std::error_category& loot_condition_interpreter_category();
+}
+
+#endif
diff --git a/cxx/include/loot/exception/file_access_error.h b/cxx/include/loot/exception/file_access_error.h
new file mode 100644
index 00000000..af4ab237
--- /dev/null
+++ b/cxx/include/loot/exception/file_access_error.h
@@ -0,0 +1,41 @@
+/* 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_EXCEPTION_FILE_ACCESS_ERROR
+#define LOOT_EXCEPTION_FILE_ACCESS_ERROR
+
+#include
+
+namespace loot {
+/**
+ * @brief An exception class thrown if an error is encountered while reading or
+ * writing a file.
+ */
+class FileAccessError : public std::runtime_error {
+public:
+ using std::runtime_error::runtime_error;
+};
+}
+
+#endif
diff --git a/cxx/include/loot/exception/undefined_group_error.h b/cxx/include/loot/exception/undefined_group_error.h
new file mode 100644
index 00000000..008b3ae0
--- /dev/null
+++ b/cxx/include/loot/exception/undefined_group_error.h
@@ -0,0 +1,55 @@
+/* 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_EXCEPTION_UNDEFINED_GROUP_ERROR
+#define LOOT_EXCEPTION_UNDEFINED_GROUP_ERROR
+
+#include
+
+#include "loot/api_decorator.h"
+
+namespace loot {
+/**
+ * @brief An exception class thrown if group is referenced but is undefined.
+ */
+class UndefinedGroupError : public std::runtime_error {
+public:
+ /**
+ * @brief Construct an exception for an undefined group.
+ * @param groupName The name of the group that is undefined.
+ */
+ LOOT_API UndefinedGroupError(const std::string& groupName);
+
+ /**
+ * Get the name of the undefined group.
+ * @return A group name.
+ */
+ LOOT_API std::string GetGroupName() const;
+
+private:
+ std::string groupName_;
+};
+}
+
+#endif
diff --git a/cxx/include/loot/game_interface.h b/cxx/include/loot/game_interface.h
new file mode 100644
index 00000000..a0115027
--- /dev/null
+++ b/cxx/include/loot/game_interface.h
@@ -0,0 +1,241 @@
+/* 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_GAME_INTERFACE
+#define LOOT_GAME_INTERFACE
+
+#include "loot/database_interface.h"
+#include "loot/plugin_interface.h"
+
+namespace loot {
+/** @brief The interface provided for accessing game-specific functionality. */
+class GameInterface {
+public:
+ virtual ~GameInterface() = default;
+
+ /**
+ * @brief Get the game's type.
+ * @returns The game's type.
+ */
+ virtual GameType GetType() const = 0;
+
+ /**
+ * @brief Gets the currently-set additional data paths.
+ * @details The following games are configured with additional data paths by
+ * default:
+ * - Fallout 4, when installed from the Microsoft Store
+ * - Starfield
+ * - OpenMW
+ */
+ virtual std::vector GetAdditionalDataPaths() const = 0;
+
+ /**
+ * @brief Set additional data paths.
+ * @details The additional data paths are used when interacting with the load
+ * order, evaluating conditions and scanning for archives (BSA/BA2
+ * depending on the game). Additional data paths are used in the
+ * order they are given (except with OpenMW, which checks them in
+ * reverse order), and take precedence over the game's main data
+ * path.
+ */
+ virtual void SetAdditionalDataPaths(
+ const std::vector& additionalDataPaths) = 0;
+
+ /**
+ * @name Metadata Access
+ * @{
+ */
+
+ /**
+ * @brief Get the database interface used for accessing metadata-related
+ * functionality.
+ * @returns A reference to the game's DatabaseInterface. The reference remains
+ * valid for the lifetime of the GameInterface instance.
+ */
+ virtual DatabaseInterface& GetDatabase() = 0;
+
+ /**
+ * @brief Get the database interface used for accessing metadata-related
+ * functionality.
+ * @returns A reference to the game's DatabaseInterface. The reference remains
+ * valid for the lifetime of the GameInterface instance.
+ */
+ virtual const DatabaseInterface& GetDatabase() const = 0;
+
+ /**
+ * @}
+ * @name Plugin Data Access
+ * @{
+ */
+
+ /**
+ * @brief Check if a file is a valid plugin.
+ * @details The validity check is not exhaustive: it generally checks that the
+ * file is a valid plugin file extension for the game and that its
+ * header (if applicable) can be parsed.
+ * @param pluginPath
+ * The path to the file to check. Relative paths are resolved relative
+ * to the game's plugins directory, while absolute paths are used
+ * as given.
+ * @returns True if the file is a valid plugin, false otherwise.
+ */
+ virtual bool IsValidPlugin(const std::filesystem::path& pluginPath) const = 0;
+
+ /**
+ * @brief Parses plugins and loads their data.
+ * @details If a given plugin filename (or one that is case-insensitively
+ * equal) has already been loaded, its previously-loaded data
+ * data is discarded, invalidating any existing shared pointers to
+ * that plugin's PluginInterface object.
+ *
+ * If the game is Morrowind, OpenMW or Starfield, it's only valid to
+ * fully load a plugin if its masters are already loaded or included
+ * in the same input vector.
+ * @param pluginPaths
+ * The plugin paths to load. Relative paths are resolved relative to
+ * the game's plugins directory, while absolute paths are used as
+ * given. Each plugin filename must be unique within the vector.
+ * @param loadHeadersOnly
+ * If true, only the plugins' headers are loaded. If false, all records
+ * in the plugins are parsed.
+ */
+ virtual void LoadPlugins(
+ const std::vector& pluginPaths,
+ bool loadHeadersOnly) = 0;
+
+ /**
+ * @brief Clears the plugins loaded by previous calls to `LoadPlugins()`.
+ * @details This invalidates any PluginInterface pointers retrieved using
+ * `GetPlugin()` or `GetLoadedPlugins()`.
+ */
+ virtual void ClearLoadedPlugins() = 0;
+
+ /**
+ * @brief Get data for a loaded plugin.
+ * @param pluginName
+ * The filename of the plugin to get data for.
+ * @returns A shared pointer to a const PluginInterface implementation. The
+ * pointer is null if the given plugin has not been loaded. The
+ * pointer remains valid until the `ClearLoadedPlugins()` function
+ * is called, this GameInterface is destroyed, or until a plugin with
+ * a case-insensitively equal filename is loaded.
+ */
+ virtual const PluginInterface* GetPlugin(
+ const std::string& pluginName) const = 0;
+
+ /**
+ * @brief Get a set of const references to all loaded plugins' PluginInterface
+ * objects.
+ * @returns A set of shared pointers to const PluginInterface. The pointers
+ * remain valid until the `ClearLoadedPlugins()` function is called,
+ * this GameInterface is destroyed, or until a plugin with a
+ * case-insensitively equal filename is loaded.
+ */
+ virtual std::vector GetLoadedPlugins() const = 0;
+
+ /**
+ * @}
+ * @name Sorting
+ * @{
+ */
+
+ /**
+ * @brief Calculates a new load order for the game's installed plugins
+ * (including inactive plugins) and outputs the sorted order.
+ * @details Pulls metadata from the masterlist and userlist if they are
+ * loaded, and reads the contents of each plugin. No changes are
+ * applied to the load order used by the game. This function does
+ * not load or evaluate the masterlist or userlist.
+ * @param pluginPaths
+ * The plugins to sort, in their current load order. All given plugins
+ * must have been loaded using `LoadPlugins()`.
+ * @returns A vector of the given plugin filenames in their sorted load
+ * order.
+ */
+ virtual std::vector SortPlugins(
+ const std::vector& pluginFilenames) = 0;
+
+ /**
+ * @}
+ * @name Load Order Interaction
+ * @{
+ */
+
+ /**
+ *
+ * @brief Load the current load order state, discarding any previously held
+ * state.
+ * @details This function should be called whenever the load order or active
+ * state of plugins "on disk" changes, so that the cached state is
+ * updated to reflect the changes.
+ */
+ virtual void LoadCurrentLoadOrderState() = 0;
+
+ /**
+ * @brief Check if the load order is ambiguous.
+ * @details This checks that all plugins in the current load order state have
+ * a well-defined position in the "on disk" state, and that all data
+ * sources are consistent. If the load order is ambiguous, different
+ * applications may read different load orders from the same source
+ * data.
+ * @returns True if the load order is ambiguous, false otherwise.
+ */
+ virtual bool IsLoadOrderAmbiguous() const = 0;
+
+ /**
+ * @brief Gets the path to the file that holds the list of active plugins.
+ * @details The active plugins file path is often within the game's local
+ path, but its name and location varies by game and game
+ configuration, so this function exposes the path that libloot
+ uses.
+ * @returns The file path.
+ */
+ virtual std::filesystem::path GetActivePluginsFilePath() const = 0;
+
+ /**
+ * @brief Check if a plugin is active.
+ * @param plugin
+ * The filename of the plugin for which to check the active state.
+ * @returns True if the plugin is active, false otherwise.
+ */
+ virtual bool IsPluginActive(const std::string& plugin) const = 0;
+
+ /**
+ * @brief Get the current load order.
+ * @returns A vector of plugin filenames in their load order.
+ */
+ virtual std::vector GetLoadOrder() const = 0;
+
+ /**
+ * @brief Set the game's load order.
+ * @details There is no way to persist the load order of inactive OpenMW
+ * plugins, so setting an OpenMW load order will have no effect if
+ * the relative order of active plugins is unchanged.
+ * @param loadOrder
+ * A vector of plugin filenames sorted in the load order to set.
+ */
+ virtual void SetLoadOrder(const std::vector& loadOrder) = 0;
+};
+}
+
+#endif
diff --git a/cxx/include/loot/loot_version.h b/cxx/include/loot/loot_version.h
new file mode 100644
index 00000000..65e8916d
--- /dev/null
+++ b/cxx/include/loot/loot_version.h
@@ -0,0 +1,55 @@
+/* LOOT
+
+A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
+Fallout: New Vegas.
+
+Copyright (C) 2014-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_LOOT_VERSION
+#define LOOT_LOOT_VERSION
+
+#include
+
+#include "loot/api_decorator.h"
+
+namespace loot {
+/** @brief libloot's major version number. */
+inline constexpr unsigned int LIBLOOT_VERSION_MAJOR = 0;
+
+/** @brief libloot's minor version number. */
+inline constexpr unsigned int LIBLOOT_VERSION_MINOR = 25;
+
+/** @brief libloot's patch version number. */
+inline constexpr unsigned int LIBLOOT_VERSION_PATCH = 3;
+
+/**
+ * @brief Get the library version.
+ * @return A string of the form "major.minor.patch".
+ */
+LOOT_API std::string GetLiblootVersion();
+
+/**
+ * @brief Get the source control revision that libloot was built from.
+ * @return A string containing the revision ID.
+ */
+LOOT_API std::string GetLiblootRevision();
+}
+
+#endif
diff --git a/cxx/include/loot/metadata/conditional_metadata.h b/cxx/include/loot/metadata/conditional_metadata.h
new file mode 100644
index 00000000..cd47658b
--- /dev/null
+++ b/cxx/include/loot/metadata/conditional_metadata.h
@@ -0,0 +1,69 @@
+/* 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_METADATA_CONDITIONAL_METADATA
+#define LOOT_METADATA_CONDITIONAL_METADATA
+
+#include
+
+#include "loot/api_decorator.h"
+
+namespace loot {
+/**
+ * A base class for metadata that can be conditional based on the result of
+ * evaluating a condition string.
+ */
+class ConditionalMetadata {
+public:
+ /**
+ * Construct a ConditionalMetadata object with an empty condition string.
+ * @return A ConditionalMetadata object.
+ */
+ LOOT_API ConditionalMetadata() = default;
+
+ /**
+ * Construct a ConditionalMetadata object with a given condition string.
+ * @param condition
+ * A condition string, as defined in the LOOT metadata syntax
+ * documentation.
+ * @return A ConditionalMetadata object.
+ */
+ LOOT_API explicit ConditionalMetadata(const std::string& condition);
+
+ /**
+ * Check if the condition string is non-empty.
+ * @return True if the condition string is not empty, false otherwise.
+ */
+ LOOT_API bool IsConditional() const;
+
+ /**
+ * Get the condition string.
+ * @return The object's condition string.
+ */
+ LOOT_API std::string GetCondition() const;
+
+private:
+ std::string condition_;
+};
+}
+#endif
diff --git a/cxx/include/loot/metadata/file.h b/cxx/include/loot/metadata/file.h
new file mode 100644
index 00000000..e7d1934d
--- /dev/null
+++ b/cxx/include/loot/metadata/file.h
@@ -0,0 +1,137 @@
+/* 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_METADATA_FILE
+#define LOOT_METADATA_FILE
+
+#include
+
+#include "loot/api_decorator.h"
+#include "loot/metadata/conditional_metadata.h"
+#include "loot/metadata/filename.h"
+#include "loot/metadata/message_content.h"
+
+namespace loot {
+/**
+ * Represents a file in a game's Data folder, including files in subdirectories.
+ */
+class File : public ConditionalMetadata {
+public:
+ /**
+ * Construct a File with blank name, display and condition strings.
+ * @return A File object.
+ */
+ LOOT_API File() = default;
+
+ /**
+ * Construct a File with the given name, display name and condition strings.
+ * @param name
+ * The filename of the file.
+ * @param display
+ * The name to be displayed for the file in messages, formatted using
+ * CommonMark.
+ * @param condition
+ * The File's condition string.
+ * @param detail
+ * The detail message content, which may be appended to any messages
+ * generated for this file. If multilingual, one language must be
+ * English.
+ * @return A File object.
+ */
+ LOOT_API explicit File(const std::string& name,
+ const std::string& display = "",
+ const std::string& condition = "",
+ const std::vector& detail = {});
+
+ /**
+ * Get the filename of the file.
+ * @return The file's filename.
+ */
+ LOOT_API Filename GetName() const;
+
+ /**
+ * Get the display name of the file.
+ * @return The file's display name.
+ */
+ LOOT_API std::string GetDisplayName() const;
+
+ /**
+ * Get the detail message content of the file.
+ *
+ * If this file causes an error message to be displayed, the detail message
+ * content should be appended to that message, as it provides more detail
+ * about the error (e.g. suggestions for how to resolve it).
+ */
+ LOOT_API std::vector GetDetail() const;
+
+private:
+ Filename name_;
+ std::string display_;
+ std::vector detail_;
+};
+
+/**
+ * Check if two File objects are equal by comparing their fields.
+ * @returns True if the objects' fields are equal, false otherwise.
+ */
+LOOT_API bool operator==(const File& lhs, const File& rhs);
+
+/**
+ * Check if two File objects are not equal.
+ * @returns True if the File objects are not equal, false otherwise.
+ */
+LOOT_API bool operator!=(const File& lhs, const File& rhs);
+
+/**
+ * A less-than operator implemented with no semantics so that File objects can
+ * be stored in sets.
+ * @returns True if the first File is less than the second File, false
+ * otherwise.
+ */
+LOOT_API bool operator<(const File& lhs, const File& rhs);
+
+/**
+ * Check if the first File object is greater than the second File object.
+ * @returns True if the second File object is less than the first File object,
+ * false otherwise.
+ */
+LOOT_API bool operator>(const File& lhs, const File& rhs);
+
+/**
+ * Check if the first File object is less than or equal to the second File
+ * object.
+ * @returns True if the first File object is not greater than the second File
+ * object, false otherwise.
+ */
+LOOT_API bool operator<=(const File& lhs, const File& rhs);
+
+/**
+ * Check if the first File object is greater than or equal to the second File
+ * object.
+ * @returns True if the first File object is not less than the second File
+ * object, false otherwise.
+ */
+LOOT_API bool operator>=(const File& lhs, const File& rhs);
+}
+
+#endif
diff --git a/cxx/include/loot/metadata/filename.h b/cxx/include/loot/metadata/filename.h
new file mode 100644
index 00000000..b09fa2be
--- /dev/null
+++ b/cxx/include/loot/metadata/filename.h
@@ -0,0 +1,104 @@
+/* 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_METADATA_FILENAME
+#define LOOT_METADATA_FILENAME
+
+#include
+
+#include "loot/api_decorator.h"
+
+namespace loot {
+/**
+ * Represents a case-insensitive filename.
+ */
+class Filename {
+public:
+ /**
+ * Construct a Filename using an empty string.
+ * @return A Filename object.
+ */
+ LOOT_API Filename() = default;
+
+ /**
+ * Construct a Filename using the given string.
+ * @return A Filename object.
+ */
+ LOOT_API explicit Filename(const std::string& filename);
+
+ /**
+ * Get this Filename as a string.
+ */
+ LOOT_API explicit operator std::string() const;
+
+private:
+ std::string filename_;
+};
+
+/**
+ * Check if two Filename objects are equal by comparing their fields.
+ * @returns True if the filenames are case-insensitively equal and all other
+ * fields are case-sensitively equal, false otherwise.
+ */
+LOOT_API bool operator==(const Filename& lhs, const Filename& rhs);
+
+/**
+ * Check if two Filename objects are not equal.
+ * @returns True if the Filename objects are not equal, false otherwise.
+ */
+LOOT_API bool operator!=(const Filename& lhs, const Filename& rhs);
+
+/**
+ * A less-than operator implemented with no semantics so that Filename objects
+ * can be stored in sets.
+ * @returns True if this Filename is less than the given Filename, false
+ * otherwise.
+ */
+LOOT_API bool operator<(const Filename& lhs, const Filename& rhs);
+
+/**
+ * Check if the first Filename object is greater than the second Filename
+ * object.
+ * @returns True if the second Filename object is less than the first Filename
+ * object, false otherwise.
+ */
+LOOT_API bool operator>(const Filename& lhs, const Filename& rhs);
+
+/**
+ * Check if the first Filename object is less than or equal to the second
+ * Filename object.
+ * @returns True if the first Filename object is not greater than the second
+ * Filename object, false otherwise.
+ */
+LOOT_API bool operator<=(const Filename& lhs, const Filename& rhs);
+
+/**
+ * Check if the first Filename object is greater than or equal to the second
+ * Filename object.
+ * @returns True if the first Filename object is not less than the second
+ * Filename object, false otherwise.
+ */
+LOOT_API bool operator>=(const Filename& lhs, const Filename& rhs);
+}
+
+#endif
diff --git a/cxx/include/loot/metadata/group.h b/cxx/include/loot/metadata/group.h
new file mode 100644
index 00000000..adf78ce3
--- /dev/null
+++ b/cxx/include/loot/metadata/group.h
@@ -0,0 +1,134 @@
+/* LOOT
+
+ A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
+ Fallout: New Vegas.
+
+ Copyright (C) 2018 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_METADATA_GROUP
+#define LOOT_METADATA_GROUP
+
+#include
+#include
+
+#include "loot/api_decorator.h"
+
+namespace loot {
+/**
+ * Represents a group to which plugin metadata objects can belong.
+ */
+class Group {
+public:
+ /**
+ * The name of the group to which all plugins belong by default.
+ */
+ static constexpr const char* DEFAULT_NAME = "default";
+
+ /**
+ * Construct a Group with the name "default" and an empty set of groups to
+ * load after.
+ * @return A Group object.
+ */
+ LOOT_API Group() = default;
+
+ /**
+ * Construct a Group with the given name, description and set of groups to
+ * load after.
+ * @param name
+ * The group name.
+ * @param afterGroups
+ * The names of groups this group loads after.
+ * @param description
+ * A description of the group.
+ * @return A Group object.
+ */
+ LOOT_API explicit Group(const std::string& name,
+ const std::vector& afterGroups = {},
+ const std::string& description = "");
+
+ /**
+ * Get the name of the group.
+ * @return The group's name.
+ */
+ LOOT_API std::string GetName() const;
+
+ /**
+ * Get the description of the group.
+ * @return The group's description.
+ */
+ LOOT_API std::string GetDescription() const;
+
+ /**
+ * Get the set of groups this group loads after.
+ * @return A set of group names.
+ */
+ LOOT_API std::vector GetAfterGroups() const;
+
+private:
+ std::string name_{DEFAULT_NAME};
+ std::string description_;
+ std::vector afterGroups_;
+};
+
+/**
+ * Check if two Group objects are equal by comparing their names.
+ * @returns True if the objects' fields are equal, false otherwise.
+ */
+LOOT_API bool operator==(const Group& lhs, const Group& rhs);
+
+/**
+ * Check if two Group objects are not equal.
+ * @returns True if the Group objects are not equal, false otherwise.
+ */
+LOOT_API bool operator!=(const Group& lhs, const Group& rhs);
+
+/**
+ * A less-than operator implemented with no semantics so that Group objects
+ * can be stored in sets.
+ * @returns True if the first Group is less than the second Group, false
+ * otherwise.
+ */
+LOOT_API bool operator<(const Group& lhs, const Group& rhs);
+
+/**
+ * Check if the first Group object is greater than the second Group
+ * object.
+ * @returns True if the second Group object is less than the first Group
+ * object, false otherwise.
+ */
+LOOT_API bool operator>(const Group& lhs, const Group& rhs);
+
+/**
+ * Check if the first Group object is less than or equal to the second
+ * Group object.
+ * @returns True if the first Group object is not greater than the second
+ * Group object, false otherwise.
+ */
+LOOT_API bool operator<=(const Group& lhs, const Group& rhs);
+
+/**
+ * Check if the first Group object is greater than or equal to the second
+ * Group object.
+ * @returns True if the first Group object is not less than the second
+ * Group object, false otherwise.
+ */
+LOOT_API bool operator>=(const Group& lhs, const Group& rhs);
+}
+
+#endif
diff --git a/cxx/include/loot/metadata/location.h b/cxx/include/loot/metadata/location.h
new file mode 100644
index 00000000..66b319f7
--- /dev/null
+++ b/cxx/include/loot/metadata/location.h
@@ -0,0 +1,117 @@
+/* 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_METADATA_LOCATION
+#define LOOT_METADATA_LOCATION
+
+#include
+#include
+
+#include "loot/api_decorator.h"
+
+namespace loot {
+/**
+ * Represents a URL at which the parent plugin can be found.
+ */
+class Location {
+public:
+ /**
+ * Construct a Location with empty URL and name strings.
+ * @return A Location object.
+ */
+ LOOT_API Location() = default;
+
+ /**
+ * Construct a Location with the given URL and name.
+ * @param url
+ * The URL at which the plugin can be found.
+ * @param name
+ * A name for the URL, eg. the page or site name.
+ * @return A Location object.
+ */
+ LOOT_API explicit Location(const std::string& url,
+ const std::string& name = "");
+
+ /**
+ * Get the object's URL.
+ * @return A URL string.
+ */
+ LOOT_API std::string GetURL() const;
+
+ /**
+ * Get the object's name.
+ * @return The name of the location.
+ */
+ LOOT_API std::string GetName() const;
+
+private:
+ std::string url_;
+ std::string name_;
+};
+
+/**
+ * Check if two Location objects are equal by comparing their fields.
+ * @returns True if the objects' fields are equal, false otherwise.
+ */
+LOOT_API bool operator==(const Location& lhs, const Location& rhs);
+
+/**
+ * Check if two Location objects are not equal.
+ * @returns True if the Location objects are not equal, false otherwise.
+ */
+LOOT_API bool operator!=(const Location& lhs, const Location& rhs);
+
+/**
+ * A less-than operator implemented with no semantics so that Location objects
+ * can be stored in sets.
+ * @returns True if the first Location is less than the second Location, false
+ * otherwise.
+ */
+LOOT_API bool operator<(const Location& lhs, const Location& rhs);
+
+/**
+ * Check if the first Location object is greater than the second Location
+ * object.
+ * @returns True if the second Location object is less than the first Location
+ * object, false otherwise.
+ */
+LOOT_API bool operator>(const Location& lhs, const Location& rhs);
+
+/**
+ * Check if the first Location object is less than or equal to the second
+ * Location object.
+ * @returns True if the first Location object is not greater than the second
+ * Location object, false otherwise.
+ */
+LOOT_API bool operator<=(const Location& lhs, const Location& rhs);
+
+/**
+ * Check if the first Location object is greater than or equal to the second
+ * Location object.
+ * @returns True if the first Location object is not less than the second
+ * Location object, false otherwise.
+ */
+LOOT_API bool operator>=(const Location& lhs, const Location& rhs);
+}
+
+#endif
diff --git a/cxx/include/loot/metadata/message.h b/cxx/include/loot/metadata/message.h
new file mode 100644
index 00000000..735d59b1
--- /dev/null
+++ b/cxx/include/loot/metadata/message.h
@@ -0,0 +1,139 @@
+/* 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_METADATA_MESSAGE
+#define LOOT_METADATA_MESSAGE
+
+#include
+#include
+
+#include "loot/api_decorator.h"
+#include "loot/enum/message_type.h"
+#include "loot/metadata/conditional_metadata.h"
+#include "loot/metadata/message_content.h"
+
+namespace loot {
+/**
+ * Represents a message with localisable text content.
+ */
+class Message : public ConditionalMetadata {
+public:
+ /**
+ * Construct a Message object of type 'say' with blank content and condition
+ * strings.
+ * @return A Message object.
+ */
+ LOOT_API Message() = default;
+
+ /**
+ * Construct a Message object with the given type, English content and
+ * condition string.
+ * @param type
+ * The message type.
+ * @param content
+ * The English message content text.
+ * @param condition
+ * A condition string.
+ * @return A Message object.
+ */
+ LOOT_API explicit Message(const MessageType type,
+ const std::string& content,
+ const std::string& condition = "");
+
+ /**
+ * Construct a Message object with the given type, content and condition
+ * string.
+ * @param type
+ * The message type.
+ * @param content
+ * The message content. If multilingual, one language must be English.
+ * @param condition
+ * A condition string.
+ * @return A Message object.
+ */
+ LOOT_API explicit Message(const MessageType type,
+ const std::vector& content,
+ const std::string& condition = "");
+
+ /**
+ * Get the message type.
+ * @return The message type.
+ */
+ LOOT_API MessageType GetType() const;
+
+ /**
+ * Get the message content.
+ * @return The message's MessageContent objects.
+ */
+ LOOT_API std::vector GetContent() const;
+
+private:
+ MessageType type_{MessageType::say};
+ std::vector content_;
+};
+
+/**
+ * Check if two Message objects are equal by comparing their fields.
+ * @returns True if the objects' fields are equal, false otherwise.
+ */
+LOOT_API bool operator==(const Message& lhs, const Message& rhs);
+
+/**
+ * Check if two Message objects are not equal.
+ * @returns True if the Message objects are not equal, false otherwise.
+ */
+LOOT_API bool operator!=(const Message& lhs, const Message& rhs);
+
+/**
+ * A less-than operator implemented with no semantics so that Message objects
+ * can be stored in sets.
+ * @returns Returns true if the first Message is less than the second Message,
+ * and false otherwise.
+ */
+LOOT_API bool operator<(const Message& lhs, const Message& rhs);
+
+/**
+ * Check if the first Message object is greater than the second Message object.
+ * @returns True if the second Message object is less than the first Message
+ * object, false otherwise.
+ */
+LOOT_API bool operator>(const Message& lhs, const Message& rhs);
+
+/**
+ * Check if the first Message object is less than or equal to the second
+ * Message object.
+ * @returns True if the first Message object is not greater than the second
+ * Message object, false otherwise.
+ */
+LOOT_API bool operator<=(const Message& lhs, const Message& rhs);
+
+/**
+ * Check if the first Message object is greater than or equal to the second
+ * Message object.
+ * @returns True if the first Message object is not less than the second
+ * Message object, false otherwise.
+ */
+LOOT_API bool operator>=(const Message& lhs, const Message& rhs);
+}
+
+#endif
diff --git a/cxx/include/loot/metadata/message_content.h b/cxx/include/loot/metadata/message_content.h
new file mode 100644
index 00000000..9a3b4c07
--- /dev/null
+++ b/cxx/include/loot/metadata/message_content.h
@@ -0,0 +1,150 @@
+/* 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_METADATA_MESSAGE_CONTENT
+#define LOOT_METADATA_MESSAGE_CONTENT
+
+#include
+#include
+#include
+
+#include "loot/api_decorator.h"
+
+namespace loot {
+/**
+ * Represents a message's localised text content.
+ */
+class MessageContent {
+public:
+ /**
+ * The code for the default language assumed for message content, which is
+ * "en" (English).
+ */
+ static constexpr const char* DEFAULT_LANGUAGE = "en";
+
+ /**
+ * Construct a MessageContent object with an empty English message string.
+ * @return A MessageContent object.
+ */
+ LOOT_API MessageContent() = default;
+
+ /**
+ * Construct a Message object with the given text in the given language.
+ * @param text
+ * The message text.
+ * @param language
+ * The language that the message is written in.
+ * @return A MessageContent object.
+ */
+ LOOT_API explicit MessageContent(
+ const std::string& text,
+ const std::string& language = DEFAULT_LANGUAGE);
+
+ /**
+ * Get the message text.
+ * @return A string containing the message text.
+ */
+ LOOT_API std::string GetText() const;
+
+ /**
+ * Get the message language.
+ * @return A code representing the language that the message is written in.
+ */
+ LOOT_API std::string GetLanguage() const;
+
+private:
+ std::string text_;
+ std::string language_{DEFAULT_LANGUAGE};
+};
+
+/**
+ * Check if two MessageContent objects are equal by comparing their fields.
+ * @returns True if the objects' fields are equal, false otherwise.
+ */
+LOOT_API bool operator==(const MessageContent& lhs, const MessageContent& rhs);
+
+/**
+ * Check if two MessageContent objects are not equal.
+ * @returns True if the MessageContent objects are not equal, false otherwise.
+ */
+LOOT_API bool operator!=(const MessageContent& lhs, const MessageContent& rhs);
+
+/**
+ * A less-than operator implemented with no semantics so that MessageContent
+ * objects can be stored in sets.
+ * @returns True if the first MessageContent is less than the second
+ * MessageContent, false otherwise.
+ */
+LOOT_API bool operator<(const MessageContent& lhs, const MessageContent& rhs);
+
+/**
+ * Check if the first MessageContent object is greater than the second
+ * MessageContent object.
+ * @returns True if the second MessageContent object is less than the first
+ * MessageContent object, false otherwise.
+ */
+LOOT_API bool operator>(const MessageContent& lhs, const MessageContent& rhs);
+
+/**
+ * Check if the first MessageContent object is less than or equal to the second
+ * MessageContent object.
+ * @returns True if the first MessageContent object is not greater than the
+ * second MessageContent object, false otherwise.
+ */
+LOOT_API bool operator<=(const MessageContent& lhs, const MessageContent& rhs);
+
+/**
+ * Check if the first MessageContent object is greater than or equal to the
+ * second MessageContent object.
+ * @returns True if the first MessageContent object is not less than the second
+ * MessageContent object, false otherwise.
+ */
+LOOT_API bool operator>=(const MessageContent& lhs, const MessageContent& rhs);
+
+/**
+ * Choose a MessageContent object from a vector given a language.
+ * @param content
+ * The MessageContent objects to choose between.
+ * @param language
+ * The locale or language code for the preferred language to select.
+ * Locale codes are of the form `[language code]_[country code]`.
+ * @return A MessageContent object.
+ * * If the vector only contains a single element, that element is
+ * returned.
+ * * If content with a language that exactly matches the given locale
+ * or language code is present, that content is returned.
+ * * If a locale code is given and there is no exact match but content
+ * for that locale's language is present, that content is returned.
+ * * If a language code is given and there is no exact match but
+ * content for a locale in that langauge is present, that content is
+ * returned.
+ * * If no locale or language code matches are found and content in
+ * the default language is present, that content is returned.
+ * * Otherwise, an empty optional is returned.
+ */
+LOOT_API std::optional SelectMessageContent(
+ const std::vector content,
+ const std::string& language);
+}
+
+#endif
diff --git a/cxx/include/loot/metadata/plugin_cleaning_data.h b/cxx/include/loot/metadata/plugin_cleaning_data.h
new file mode 100644
index 00000000..8ed59662
--- /dev/null
+++ b/cxx/include/loot/metadata/plugin_cleaning_data.h
@@ -0,0 +1,188 @@
+/* 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_METADATA_PLUGIN_CLEANING_DATA
+#define LOOT_METADATA_PLUGIN_CLEANING_DATA
+
+#include
+#include
+
+#include "loot/api_decorator.h"
+#include "loot/metadata/message.h"
+
+namespace loot {
+/**
+ * Represents data identifying the plugin under which it is stored as dirty or
+ * clean.
+ */
+class PluginCleaningData {
+public:
+ /**
+ * Construct a PluginCleaningData object with zero CRC, ITM count, deleted
+ * reference count and deleted navmesh count values, an empty utility string
+ * and no detail.
+ * @return A PluginCleaningData object.
+ */
+ LOOT_API PluginCleaningData() = default;
+
+ /**
+ * Construct a PluginCleaningData object with the given CRC and utility,
+ * zero ITM count, deleted reference count and deleted navmesh count
+ * values and no detail.
+ * @param crc
+ * The CRC of a plugin.
+ * @param utility
+ * The utility that the plugin cleanliness was checked with.
+ * @return A PluginCleaningData object.
+ */
+ LOOT_API explicit PluginCleaningData(uint32_t crc,
+ const std::string& utility);
+
+ /**
+ * Construct a PluginCleaningData object with the given values.
+ * @param crc
+ * A clean or dirty plugin's CRC.
+ * @param utility
+ * The utility that the plugin cleanliness was checked with.
+ * @param detail
+ * A vector of localised information message strings about the plugin
+ * cleanliness.
+ * @param itm
+ * The number of Identical To Master records found in the plugin.
+ * @param ref
+ * The number of deleted references found in the plugin.
+ * @param nav
+ * The number of deleted navmeshes found in the plugin.
+ * @return A PluginCleaningData object.
+ */
+ LOOT_API explicit PluginCleaningData(
+ uint32_t crc,
+ const std::string& utility,
+ const std::vector& detail,
+ unsigned int itm,
+ unsigned int ref,
+ unsigned int nav);
+
+ /**
+ * Get the CRC that identifies the plugin that the cleaning data is for.
+ * @return A CRC-32 checksum.
+ */
+ LOOT_API uint32_t GetCRC() const;
+
+ /**
+ * Get the number of Identical To Master records in the plugin.
+ * @return The number of Identical To Master records in the plugin.
+ */
+ LOOT_API unsigned int GetITMCount() const;
+
+ /**
+ * Get the number of deleted references in the plugin.
+ * @return The number of deleted references in the plugin.
+ */
+ LOOT_API unsigned int GetDeletedReferenceCount() const;
+
+ /**
+ * Get the number of deleted navmeshes in the plugin.
+ * @return The number of deleted navmeshes in the plugin.
+ */
+ LOOT_API unsigned int GetDeletedNavmeshCount() const;
+
+ /**
+ * Get the name of the cleaning utility that was used to check the plugin.
+ * @return A cleaning utility name, possibly related information such as
+ * a version number and/or a CommonMark-formatted URL to the utility's
+ * download location.
+ */
+ LOOT_API std::string GetCleaningUtility() const;
+
+ /**
+ * Get any additional informative message content supplied with the cleaning
+ * data, eg. a link to a cleaning guide or information on wild edits or manual
+ * cleaning steps.
+ * @return A vector of localised MessageContent objects.
+ */
+ LOOT_API std::vector GetDetail() const;
+
+private:
+ uint32_t crc_{0};
+ unsigned int itm_{0};
+ unsigned int ref_{0};
+ unsigned int nav_{0};
+ std::string utility_;
+ std::vector detail_;
+};
+
+/**
+ * Check if two PluginCleaningData objects are equal by comparing their
+ * fields.
+ * @returns True if the objects' fields are equal, false otherwise.
+ */
+LOOT_API bool operator==(const PluginCleaningData& lhs,
+ const PluginCleaningData& rhs);
+
+/**
+ * Check if two MessageContent objects are not equal.
+ * @returns True if the MessageContent objects are not equal, false otherwise.
+ */
+LOOT_API bool operator!=(const PluginCleaningData& lhs,
+ const PluginCleaningData& rhs);
+
+/**
+ * A less-than operator implemented with no semantics so that
+ * PluginCleaningData objects can be stored in sets.
+ * @returns True if the first PluginCleaningData is less than the second
+ * PluginCleaningData, false otherwise.
+ */
+LOOT_API bool operator<(const PluginCleaningData& lhs,
+ const PluginCleaningData& rhs);
+
+/**
+ * Check if the first PluginCleaningData object is greater than the second
+ * PluginCleaningData object.
+ * @returns True if the second PluginCleaningData object is less than the first
+ * PluginCleaningData object, false otherwise.
+ */
+LOOT_API bool operator>(const PluginCleaningData& lhs,
+ const PluginCleaningData& rhs);
+
+/**
+ * Check if the first PluginCleaningData object is less than or equal to the
+ * second PluginCleaningData object.
+ * @returns True if the first PluginCleaningData object is not greater than the
+ * second PluginCleaningData object, false otherwise.
+ */
+LOOT_API bool operator<=(const PluginCleaningData& lhs,
+ const PluginCleaningData& rhs);
+
+/**
+ * Check if the first PluginCleaningData object is greater than or equal to the
+ * second PluginCleaningData object.
+ * @returns True if the first PluginCleaningData object is not less than the
+ * second PluginCleaningData object, false otherwise.
+ */
+LOOT_API bool operator>=(const PluginCleaningData& lhs,
+ const PluginCleaningData& rhs);
+}
+
+#endif
diff --git a/cxx/include/loot/metadata/plugin_metadata.h b/cxx/include/loot/metadata/plugin_metadata.h
new file mode 100644
index 00000000..da821a90
--- /dev/null
+++ b/cxx/include/loot/metadata/plugin_metadata.h
@@ -0,0 +1,254 @@
+/* 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_METADATA_PLUGIN_METADATA
+#define LOOT_METADATA_PLUGIN_METADATA
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "loot/api_decorator.h"
+#include "loot/metadata/file.h"
+#include "loot/metadata/location.h"
+#include "loot/metadata/message.h"
+#include "loot/metadata/plugin_cleaning_data.h"
+#include "loot/metadata/tag.h"
+
+namespace loot {
+/**
+ * Represents a plugin's metadata.
+ */
+class PluginMetadata {
+public:
+ /**
+ * Construct a PluginMetadata object with a blank plugin name and no metadata.
+ * @return A PluginMetadata object.
+ */
+ LOOT_API PluginMetadata() = default;
+
+ /**
+ * Construct a PluginMetadata object with no metadata for a plugin with the
+ * given filename.
+ * @param name
+ * The filename of the plugin that the object is constructed for.
+ * @return A PluginMetadata object.
+ */
+ LOOT_API explicit PluginMetadata(const std::string& name);
+
+ /**
+ * Merge metadata from the given PluginMetadata object into this object.
+ *
+ * If an equal metadata object already exists in this PluginMetadata object,
+ * it is not duplicated. This object's group is replaced by the given object's
+ * group if the latter is explicit.
+ * @param plugin
+ * The plugin metadata to merge.
+ */
+ LOOT_API void MergeMetadata(const PluginMetadata& plugin);
+
+ /**
+ * Get the plugin name.
+ * @return The plugin name.
+ */
+ LOOT_API std::string GetName() const;
+
+ /**
+ * Get the plugin's group.
+ * @return An optional containing the name of the group this plugin belongs to
+ * if it was explicitly set, otherwise an optional containing no
+ * value.
+ */
+ LOOT_API std::optional GetGroup() const;
+
+ /**
+ * Get the plugins that the plugin must load after.
+ * @return The plugins that the plugin must load after.
+ */
+ LOOT_API std::vector GetLoadAfterFiles() const;
+
+ /**
+ * Get the files that the plugin requires to be installed.
+ * @return The files that the plugin requires to be installed.
+ */
+ LOOT_API std::vector GetRequirements() const;
+
+ /**
+ * Get the files that the plugin is incompatible with.
+ * @return The files that the plugin is incompatible with.
+ */
+ LOOT_API std::vector GetIncompatibilities() const;
+
+ /**
+ * Get the plugin's messages.
+ * @return The plugin's messages.
+ */
+ LOOT_API std::vector GetMessages() const;
+
+ /**
+ * Get the plugin's Bash Tag suggestions.
+ * @return The plugin's Bash Tag suggestions.
+ */
+ LOOT_API std::vector GetTags() const;
+
+ /**
+ * Get the plugin's dirty plugin information.
+ * @return The PluginCleaningData objects that identify the plugin as dirty.
+ */
+ LOOT_API std::vector GetDirtyInfo() const;
+
+ /**
+ * Get the plugin's clean plugin information.
+ * @return The PluginCleaningData objects that identify the plugin as clean.
+ */
+ LOOT_API std::vector GetCleanInfo() const;
+
+ /**
+ * Get the locations at which this plugin can be found.
+ * @return The locations at which this plugin can be found.
+ */
+ LOOT_API std::vector GetLocations() const;
+
+ /**
+ * Set the plugin's group.
+ * @param group
+ * The name of the group this plugin belongs to.
+ */
+ LOOT_API void SetGroup(const std::string& group);
+
+ /**
+ * Unsets the plugin's group.
+ */
+ LOOT_API void UnsetGroup();
+
+ /**
+ * Set the files that the plugin must load after.
+ * @param after
+ * The files to set.
+ */
+ LOOT_API void SetLoadAfterFiles(const std::vector& after);
+
+ /**
+ * Set the files that the plugin requires to be installed.
+ * @param requirements
+ * The files to set.
+ */
+ LOOT_API void SetRequirements(const std::vector& requirements);
+
+ /**
+ * Set the files that the plugin must load after.
+ * @param incompatibilities
+ * The files to set.
+ */
+ LOOT_API void SetIncompatibilities(
+ const std::vector& incompatibilities);
+
+ /**
+ * Set the plugin's messages.
+ * @param messages
+ * The messages to set.
+ */
+ LOOT_API void SetMessages(const std::vector& messages);
+
+ /**
+ * Set the plugin's Bash Tag suggestions.
+ * @param tags
+ * The Bash Tag suggestions to set.
+ */
+ LOOT_API void SetTags(const std::vector& tags);
+
+ /**
+ * Set the plugin's dirty information.
+ * @param info
+ * The dirty information to set.
+ */
+ LOOT_API void SetDirtyInfo(const std::vector& info);
+
+ /**
+ * Set the plugin's clean information.
+ * @param info
+ * The clean information to set.
+ */
+ LOOT_API void SetCleanInfo(const std::vector& info);
+
+ /**
+ * Set the plugin's locations.
+ * @param locations
+ * The locations to set.
+ */
+ LOOT_API void SetLocations(const std::vector& locations);
+
+ /**
+ * Check if no plugin metadata is set.
+ * @return True if the group is implicit and the metadata containers are all
+ * empty, false otherwise.
+ */
+ LOOT_API bool HasNameOnly() const;
+
+ /**
+ * Check if the plugin name is a regular expression.
+ * @return True if the plugin name contains any of the characters `:\*?|`,
+ * false otherwise.
+ */
+ LOOT_API bool IsRegexPlugin() const;
+
+ /**
+ * Check if the given plugin name matches this plugin metadata object's
+ * name field.
+ *
+ * If the name field is a regular expression, the given plugin name will be
+ * matched against it, otherwise the strings will be compared
+ * case-insensitively. The given plugin name must be literal, i.e. not a
+ * regular expression.
+ * @returns True if the given plugin name matches this metadata's plugin
+ * name, false otherwise.
+ */
+ LOOT_API bool NameMatches(const std::string& pluginName) const;
+
+ /**
+ * @brief Serialises the plugin metadata as YAML.
+ * @returns The serialised plugin metadata.
+ */
+ LOOT_API std::string AsYaml() const;
+
+private:
+ std::string name_;
+ std::optional nameRegex_;
+
+ std::optional group_;
+ std::vector loadAfter_;
+ std::vector requirements_;
+ std::vector incompatibilities_;
+ std::vector messages_;
+ std::vector tags_;
+ std::vector dirtyInfo_;
+ std::vector cleanInfo_;
+ std::vector locations_;
+};
+}
+
+#endif
diff --git a/cxx/include/loot/metadata/tag.h b/cxx/include/loot/metadata/tag.h
new file mode 100644
index 00000000..1138b3cc
--- /dev/null
+++ b/cxx/include/loot/metadata/tag.h
@@ -0,0 +1,120 @@
+/* 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_METADATA_TAG
+#define LOOT_METADATA_TAG
+
+#include
+
+#include "loot/api_decorator.h"
+#include "loot/metadata/conditional_metadata.h"
+
+namespace loot {
+/**
+ * Represents a Bash Tag suggestion for a plugin.
+ */
+class Tag : public ConditionalMetadata {
+public:
+ /**
+ * Construct a Tag object with an empty tag name suggested for addition, with
+ * an empty condition string.
+ * @return A Tag object.
+ */
+ LOOT_API explicit Tag() = default;
+
+ /**
+ * Construct a Tag object with the given name, for addition or removal, with
+ * the given condition string.
+ * @param tag
+ * The name of the Bash Tag.
+ * @param isAddition
+ * True if the tag should be added, false if it should be removed.
+ * @param condition
+ * A condition string.
+ * @return A Tag object.
+ */
+ LOOT_API explicit Tag(const std::string& tag,
+ const bool isAddition = true,
+ const std::string& condition = "");
+
+ /**
+ * Check if the tag should be added.
+ * @return True if the tag should be added, false if it should be removed.
+ */
+ LOOT_API bool IsAddition() const;
+
+ /**
+ * Get the tag's name.
+ * @return The tag's name.
+ */
+ LOOT_API std::string GetName() const;
+
+private:
+ std::string name_;
+ bool addTag_{true};
+};
+
+/**
+ * Check if two Tag objects are equal.
+ * @returns True if the objects' fields are equal, false otherwise.
+ */
+LOOT_API bool operator==(const Tag& lhs, const Tag& rhs);
+
+/**
+ * Check if two Tag objects are not equal.
+ * @returns True if the Tag objects are not equal, false otherwise.
+ */
+LOOT_API bool operator!=(const Tag& lhs, const Tag& rhs);
+
+/**
+ * A less-than operator implemented with no semantics so that Tag objects
+ * can be stored in sets.
+ * @returns True if the first Tag is less than the second Tag, false otherwise.
+ */
+LOOT_API bool operator<(const Tag& lhs, const Tag& rhs);
+
+/**
+ * Check if the first Tag object is greater than the second Tag object.
+ * @returns True if the second Tag object is less than the first Tag object,
+ * false otherwise.
+ */
+LOOT_API bool operator>(const Tag& lhs, const Tag& rhs);
+
+/**
+ * Check if the first Tag object is less than or equal to the second Tag
+ * object.
+ * @returns True if the first Tag object is not greater than the second Tag
+ * object, false otherwise.
+ */
+LOOT_API bool operator<=(const Tag& lhs, const Tag& rhs);
+
+/**
+ * Check if the first Tag object is greater than or equal to the second Tag
+ * object.
+ * @returns True if the first Tag object is not less than the second Tag
+ * object, false otherwise.
+ */
+LOOT_API bool operator>=(const Tag& lhs, const Tag& rhs);
+}
+
+#endif
diff --git a/cxx/include/loot/plugin_interface.h b/cxx/include/loot/plugin_interface.h
new file mode 100644
index 00000000..4bec165d
--- /dev/null
+++ b/cxx/include/loot/plugin_interface.h
@@ -0,0 +1,178 @@
+/* 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_INTERFACE
+#define LOOT_PLUGIN_INTERFACE
+
+#include
+#include
+#include
+#include
+#include
+
+#include "loot/metadata/message.h"
+#include "loot/metadata/tag.h"
+
+namespace loot {
+/**
+ * Represents a plugin file that has been parsed by LOOT.
+ */
+class PluginInterface {
+public:
+ virtual ~PluginInterface() = default;
+
+ /**
+ * Get the plugin's filename.
+ * @return The plugin filename. If the plugin was ghosted when it was loaded,
+ * this filename will be without the .ghost suffix, unless the game is
+ * OpenMW, in which case ghosted plugins are not supported.
+ */
+ virtual std::string GetName() const = 0;
+
+ /**
+ * Get the value of the version field in the HEDR subrecord of the plugin's
+ * TES4 record.
+ * @return The value of the version field, or an empty optional if that value
+ * is NaN or could not be found.
+ */
+ virtual std::optional GetHeaderVersion() const = 0;
+
+ /**
+ * Get the plugin's version number from its description field.
+ *
+ * The description field may not contain a version number, or LOOT may be
+ * unable to detect it. The description field parsing may fail to extract the
+ * version number correctly, though it functions correctly in all known cases.
+ * @return An optional containing a version string if one is found, otherwise
+ * an optional containing no value.
+ */
+ virtual std::optional GetVersion() const = 0;
+
+ /**
+ * Get the plugin's masters.
+ * @return The plugin's masters in the same order they are listed in the file.
+ */
+ virtual std::vector GetMasters() const = 0;
+
+ /**
+ * Get any Bash Tags found in the plugin's description field.
+ * @return A set of Bash Tags. The order of elements in the set holds no
+ * semantics.
+ */
+ virtual std::vector GetBashTags() const = 0;
+
+ /**
+ * Get the plugin's CRC-32 checksum.
+ * @return An optional containing the plugin's CRC-32 checksum if the plugin
+ * has been fully loaded, otherwise an optional containing no value.
+ */
+ virtual std::optional GetCRC() const = 0;
+
+ /**
+ * Check if the plugin is a master plugin.
+ *
+ * What causes a plugin to be a master plugin varies by game, but is usually
+ * indicated by the plugin having its master flag set and/or by its file
+ * extension. However, OpenMW uses neither for determining plugins' load order
+ * so all OpenMW plugins are treated as non-masters.
+ *
+ * The term "master" is potentially confusing: a plugin A may not be a *master
+ * plugin*, but may still be a *master of* another plugin by being listed as
+ * such in that plugin's header record. Master plugins are sometimes referred
+ * to as *master files* or simply *masters*, while the other meaning is always
+ * referenced in relation to another plugin.
+ * @return True if the plugin is a master plugin, false otherwise.
+ */
+ virtual bool IsMaster() const = 0;
+
+ /**
+ * Check if the plugin is a light plugin.
+ * @return True if plugin is a light plugin, false otherwise.
+ */
+ virtual bool IsLightPlugin() const = 0;
+
+ /**
+ * Check if the plugin is a medium plugin.
+ * @return True if plugin is a medium plugin, false otherwise.
+ */
+ virtual bool IsMediumPlugin() const = 0;
+
+ /**
+ * Check if the plugin is an update plugin.
+ * @return True if plugin is an update plugin, false otherwise.
+ */
+ virtual bool IsUpdatePlugin() const = 0;
+
+ /**
+ * Check if the plugin is a blueprint plugin.
+ * @return True if plugin is a blueprint plugin, false otherwise.
+ */
+ virtual bool IsBlueprintPlugin() const = 0;
+
+ /**
+ * Check if the plugin is or would be valid as a light plugin.
+ * @return True if the plugin is a valid light plugin or would be a valid
+ * light plugin, false otherwise.
+ */
+ virtual bool IsValidAsLightPlugin() const = 0;
+
+ /**
+ * Check if the plugin is or would be valid as a medium plugin.
+ * @return True if the plugin is a valid medium plugin or would be a valid
+ * medium plugin, false otherwise.
+ */
+ virtual bool IsValidAsMediumPlugin() const = 0;
+
+ /**
+ * Check if the plugin is or would be valid as an update plugin.
+ * @return True if the plugin is a valid update plugin or would be a valid
+ * update plugin, false otherwise.
+ */
+ virtual bool IsValidAsUpdatePlugin() const = 0;
+
+ /**
+ * Check if the plugin contains any records other than its TES4 header.
+ * @return True if the plugin only contains a TES4 header, false otherwise.
+ */
+ virtual bool IsEmpty() const = 0;
+
+ /**
+ * Check if the plugin loads an archive (BSA/BA2 depending on the game).
+ * @return True if the plugin loads an archive, false otherwise.
+ */
+ virtual bool LoadsArchive() const = 0;
+
+ /**
+ * Check if two plugins contain a record with the same ID.
+ * @param plugin
+ * The other plugin to check for overlap with.
+ * @return True if the plugins both contain at least one record with the same
+ * ID, false otherwise. FormIDs are compared for all games apart from
+ * Morrowind, which doesn't have FormIDs and so has other identifying
+ * data compared.
+ */
+ virtual bool DoRecordsOverlap(const PluginInterface& plugin) const = 0;
+};
+}
+
+#endif
diff --git a/cxx/include/loot/vertex.h b/cxx/include/loot/vertex.h
new file mode 100644
index 00000000..861d652c
--- /dev/null
+++ b/cxx/include/loot/vertex.h
@@ -0,0 +1,74 @@
+/* LOOT
+
+A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
+Fallout: New Vegas.
+
+Copyright (C) 2014-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_VERTEX
+#define LOOT_VERTEX
+
+#include
+#include
+
+#include "loot/api_decorator.h"
+#include "loot/enum/edge_type.h"
+
+namespace loot {
+/**
+ * @brief A class representing a plugin or group vertex in a path, and the
+ type of the edge to the next vertex in the path if one exists.
+ */
+class Vertex {
+public:
+ /**
+ * @brief Construct a Vertex with the given name and no out edge.
+ * @param name The name of the plugin or group that this vertex represents.
+ */
+ LOOT_API explicit Vertex(std::string name);
+
+ /**
+ * @brief Construct a Vertex with the given name and out edge type.
+ * @param name The name of the plugin or group that this vertex represents.
+ * @param outEdgeType The type of the edge going out from this vertex.
+ */
+ LOOT_API explicit Vertex(std::string name, EdgeType outEdgeType);
+
+ /**
+ * @brief Get the name of the plugin or group.
+ * @return The name of the plugin or group.
+ */
+ LOOT_API std::string GetName() const;
+
+ /**
+ * @brief Get the type of the edge going to the next vertex.
+ * @details Each edge goes from the vertex that loads earlier to the vertex
+ * that loads later.
+ * @return The edge type.
+ */
+ LOOT_API std::optional GetTypeOfEdgeToNextVertex() const;
+
+private:
+ std::string name_;
+ std::optional outEdgeType_;
+};
+}
+
+#endif