libloot

Introduction

LOOT is a utility that helps users avoid serious conflicts between their mods by setting their plugins in an optimal load order. It also provides tens of thousands of plugin-specific messages, including usage notes, requirements, incompatibilities, bug warnings and installation mistake notifications, and thousands of Bash Tag suggestions.

This metadata that LOOT supplies is stored in its masterlist, which is maintained by the LOOT team using information provided by mod authors and users. Users can also add to and modify the metadata used by LOOT through the use of userlist files. libloot provides a way for third-party developers to access this metadata for use in their own programs.

Miscellaneous Details

String Encoding

  • All output strings are encoded in UTF-8.

  • Metadata files are written encoded in UTF-8.

  • Input strings are expected to be encoded in UTF-8.

  • Metadata files read are expected to be encoded in UTF-8.

  • File paths are case-sensitive if and only if the underlying file system is case-sensitive.

Language Codes

All language strings in the API are codes of the form ll or ll_CC, where ll is an ISO 639-1 language code and CC is an ISO 3166 country code. For example, the default language for metadata message content is English, identified by the code en, and Brazilian Portuguese is pt_BR.

Errors

All errors encountered are thrown as exceptions that inherit from std::exception.

Metadata Files

LOOT stores plugin metadata in YAML files. It distinguishes between masterlist and userlist files: each game has a single masterlist, which is a public, curated metadata store, and each LOOT user has a private userlist, which can contain metadata added by the user. The two files use the same syntax, but metadata in the userlist extends or replaces metadata sourced from the masterlist.

LOOT’s plugin metadata can be conditional, eg. a plugin may require a patch only if another plugin is also present. The API’s LoadLists() method parses metadata files into memory, but does not evaluate these conditions, so the loaded metadata may contain metadata that is invalid for the installed game that the loot::DatabaseInterface object being operated on was created for.

Caching

All unevaluated metadata is cached between calls to LoadLists().

Plugin content is cached between calls to LoadPlugins() and SortPlugins().

Load order is cached between calls to LoadPlugins(), SortPlugins() and LoadCurrentLoadOrderState().

Performance

Loading metadata lists is a relatively costly operation, as is updating the masterlist (which involves loading it).

Sorting plugins is expensive, as it involves loading all the content of all the plugins, apart from the game’s main master file, which is skipped as an optimisation (it doesn’t depend on anything else and is much bigger than any other plugin, so is unnecessary and slow to load).

Getting plugin metadata once loaded is cheap, as is getting a masterlist’s revision.

Loading the current load order state is relatively cheap and can take < 1 ms depending on hardware and the size of the load order, but involves filesystem access and should not be done more often than necessary to avoid a performance impact.

LOOT’s Sorting Algorithm

LOOT’s sorting algorithm consists of four stages:

Load plugin data

In this first stage, the plugins to be sorted are parsed and their record IDs (which are FormIDs for all games apart from Morrowind) are stored. Parsing is multithreaded by dividing the plugins into buckets with roughly equal total file sizes, and loading each bucket’s plugins in a separate thread. The number of buckets created is equal to the number of concurrent threads that are hardware-supported (e.g. a dual-core CPU without hyperthreading may report that it supports two threads).

When parsing plugins, all subrecords are skipped over for efficiency, apart from the subrecords of the TES4 header record.

Create plugin graph vertices

Once loaded, a directed graph is created and the plugins are added to it in lexicographical order as vertices. Any metadata a plugin has in the masterlist and userlist are then merged into its vertex’s data store. Plugin group dependencies are also resolved and added as group-derived plugins.

Create plugin graph edges

In this section, the terms vertex and plugin are used interchangeably, and the iteration order ‘for each plugin’ is the order in which the vertices were added to the graph.

For each plugin:

  1. If the plugin is a master file, add edges going to all non-master files. If the plugin is a non-master file, add edges coming from all master files.

  2. Add edges coming from all the plugin’s masters. Missing masters have no edges added.

  3. Add edges coming from all the plugin’s requirements. Missing requirements have no edges added.

  4. Add edges coming from all the plugin’s load after files that are installed plugins.

Group-derived interdependencies are then evaluated. Each plugin’s group-derived plugins are iterated over and individually checked to see if adding an edge from the group-derived plugin to the plugin would cause a cycle, and if not the edge is recorded. Once all potential edges have been checked, the recorded edges are added to the graph.

Plugin overlap edges are then added. Two plugins overlap if they contain the same record, i.e. if they both edit the same record or if one edits a record the other plugin adds.

For each plugin, skip it if it overrides no records, otherwise iterate over all other plugins.

  • If the plugin and other plugin override the same number of records, or do not overlap, skip the other plugin.

  • Otherwise, add an edge from the plugin which overrides more records to the plugin that overrides fewer records, unless that edge would cause a cycle.

For Morrowind, identifying which records override others requires all of a plugin’s masters to be installed, so if a plugin has missing masters, its total record count is used in place of its override record count.

Finally, tie-break edges are added to ensure that sorting is consistent. For each plugin, iterate over all other plugins and add an edge between each pair of plugins in the direction given by the tie-break comparison function, unless that edge would cause a cycle.

The tie-break comparison function compares current plugin load order positions, falling back to plugin names.

  • If both plugins have positions in the current load order, the function preserves their existing relative order.

  • If one plugin has a position and the other does not, the edge added goes from the plugin with a position to the plugin without a position.

  • If neither plugin has a load order position, a case-insensitive lexicographical comparison of their filenames without file extensions is used to decide their order.

Topologically sort the plugin graph

Note that edges for explicit interdependencies are the only edges allowed to create cycles: this is because the first step of this stage is to check the plugin graph for cycles, and throw an error if any are encountered, so that metadata (or indeed plugin data) that cause them can be corrected.

Once the graph is confirmed to be cycle-free, a topological sort is performed on the graph, outputting a list of plugins in their newly-sorted load order.

API Reference

Enumerations

enum loot::EdgeType

An enum representing the different possible types of interactions between plugins or groups.

Values:

hardcoded
masterFlag
master
masterlistRequirement
userRequirement
masterlistLoadAfter
userLoadAfter
group
overlap
tieBreak
enum loot::GameType

Codes used to create database handles for specific games.

Values:

tes4

The Elder Scrolls IV: Oblivion

tes5

The Elder Scrolls V: Skyrim

fo3

Fallout 3

fonv

Fallout: New Vegas

fo4

Fallout 4

tes5se

The Elder Scrolls V: Skyrim Special Edition

fo4vr

Fallout 4 VR

tes5vr

Skyrim VR

tes3

The Elder Scrolls III: Morrowind

enum loot::LogLevel

Codes used to specify different levels of API logging.

Values:

trace
debug
info
warning
error
fatal
enum loot::MessageType

Codes used to indicate the type of a message.

Values:

say

A notification message that is of no significant severity.

warn

A warning message, used to indicate that an issue may be present that the user may wish to act on.

error

An error message, used to indicate that an issue that requires user action is present.

Public-Field Data Structures

struct MasterlistInfo

A structure that holds data about a masterlist’s source control revision.

Public Members

std::string revision_id

The revision hash for the masterlist. If the masterlist doesn’t exist, or there is no Git repository at its location, this will be empty.

std::string revision_date

A pointer to a string containing the ISO 8601 formatted revision date, ie. YYYY-MM-DD. If the masterlist doesn’t exist, or there is no Git repository at its location, this will be empty.

bool is_modified

true if the masterlist has been edited since the outputted revision, or false if it is at exactly the revision given.

struct SimpleMessage

A structure that holds the type of a message and the message string itself.

Public Members

MessageType type

The type of the message.

std::string language

The language the message string is written in.

std::string text

The message string, which may be formatted using GitHub Flavored Markdown.

std::string condition

The message’s condition string.

Functions

void loot::SetLoggingCallback(std::function<void(LogLevel, const char *)> callback)

Set the callback function that is called when logging.

If this function is not called, the default behaviour is to print messages to the console.

Parameters
  • callback: The function called when logging. The first parameter is the level of the message being logged, and the second is the message.

bool loot::IsCompatible(const unsigned int major, const unsigned int minor, const unsigned int patch)

Checks for API compatibility.

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.

Return

True if the API versions are compatible, false otherwise.

Parameters
  • major: The major version number to check.

  • minor: The minor version number to check.

  • patch: The patch version number to check.

void loot::InitialiseLocale(const std::string &id = "")

Initialise the current global locale using the given ID.

This sets the global locale up so that the library’s UTF-8 support can function.

Parameters
  • id: A locale ID. The default value is a blank string, which will use the system default locale.

std::shared_ptr<GameInterface> loot::CreateGameHandle(const GameType game, const std::filesystem::path &game_path, const std::filesystem::path &game_local_path = "")

Initialise a new game handle.

Creates a handle for a game, which is then used by all game-specific functions.

Return

The new game handle.

Parameters
  • game: A game code for which to create the handle.

  • game_path: The relative or absolute path to the directory containing the game’s executable.

  • game_local_path: The relative or absolute path to the game’s folder in %LOCALAPPDATA% or an empty path. If an empty path, the API will attempt to look up the path that %LOCALAPPDATA% corresponds to. This parameter is provided so that systems lacking that environmental variable (eg. Linux) can still use the API.

Interfaces

class DatabaseInterface

The interface provided by API’s database handle.

Data Reading & Writing

virtual void LoadLists(const std::filesystem::path &masterlist_path, const std::filesystem::path &userlist_path = "") = 0

Loads the masterlist and userlist from the paths specified.

Can be called multiple times, each time replacing the previously-loaded data.

Parameters
  • masterlist_path: The relative or absolute path to the masterlist file that should be loaded.

  • 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.

virtual void WriteUserMetadata(const std::filesystem::path &outputFile, const bool overwrite) const = 0

Writes a metadata file containing all loaded user-added metadata.

Parameters
  • outputFile: The path to which the file shall be written.

  • 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

Writes a minimal metadata file that only contains plugins with Bash Tag suggestions and/or dirty info, plus the suggestions and info themselves.

Parameters
  • outputFile: The path to which the file shall be written.

  • overwrite: If false and outputFile already exists, no data will be written. Otherwise, data will be written.

Masterlist Update

virtual bool UpdateMasterlist(const std::filesystem::path &masterlist_path, const std::string &remote_url, const std::string &remote_branch) = 0

Update the given masterlist.

Uses Git to update the given masterlist to a given remote. If the masterlist doesn’t exist, this will create it. This function also initialises a Git repository in the given masterlist’s parent folder. If the masterlist was not already up-to-date, it will be re-loaded, but not re-evaluated.

If a Git repository is already present, it will be used to perform a diff-only update, but if for any reason a fast-forward merge update is not possible, the existing repository will be deleted and a new repository cloned from the given remote.

Return

true if the masterlist was updated. false if no update was necessary, ie. it was already up-to-date. If true, the masterlist will have been re-loaded, but will need to be re-evaluated separately.

Parameters
  • masterlist_path: The relative or absolute path to the masterlist file that should be updated. The filename must match the filename of the masterlist file in the given remote repository, otherwise it will not be updated correctly. Although LOOT itself expects this filename to be “masterlist.yaml”, the API does not check for any specific filename.

  • remote_url: The URL of the remote from which to fetch updates. This can also be a relative or absolute path to a local repository.

  • remote_branch: The branch of the remote from which to apply updates. LOOT’s official masterlists are versioned using separate branches for each new version of the masterlist syntax, so if you’re using them, check their repositories to see which is the latest release branch.

virtual MasterlistInfo GetMasterlistRevision(const std::filesystem::path &masterlist_path, const bool get_short_id) const = 0

Get the given masterlist’s revision.

Getting a masterlist’s revision is only possible if it is found inside a local Git repository.

Return

The revision data.

Parameters
  • masterlist_path: The relative or absolute path to the masterlist file that should be queried.

  • get_short_id: If true, the shortest unique hexadecimal revision hash that is at least 7 characters long will be outputted. Otherwise, the full 40 character hash will be outputted.

virtual bool IsLatestMasterlist(const std::filesystem::path &masterlist_path, const std::string &branch) const = 0

Check if the given masterlist is the latest available for a given branch.

Return

True if the masterlist revision matches the latest masterlist revision for the given branch, and false otherwise.

Parameters
  • masterlist_path: The relative or absolute path to the masterlist file for which the latest revision should be obtained. It needs to be in a local Git repository.

  • branch: The branch to check against.

Non-plugin Data Access

virtual std::set<std::string> GetKnownBashTags() const = 0

Gets the Bash Tags that are listed in the loaded metadata lists.

Bash Tag suggestions can include plugins not in this list.

Return

A set of Bash Tag names.

virtual std::vector<Message> GetGeneralMessages(bool evaluateConditions = false) const = 0

Get all general messages listen in the loaded metadata lists.

Return

A vector of messages supplied in the metadata lists but not attached to any particular plugin.

Parameters
  • 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.

virtual std::unordered_set<Group> GetGroups(bool includeUserMetadata = true) const = 0

Gets the groups that are defined in the loaded metadata lists.

Return

An unordered set of Group objects.

Parameters
  • 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.

virtual std::unordered_set<Group> GetUserGroups() const = 0

Gets the groups that are defined or extended in the loaded userlist.

Return

An unordered set of Group objects.

virtual void SetUserGroups(const std::unordered_set<Group> &groups) = 0

Sets the group definitions to store in the userlist, overwriting any existing definitions there.

Parameters
  • groups: The unordered set of Group objects to set.

virtual std::vector<Vertex> GetGroupsPath(const std::string &fromGroupName, const std::string &toGroupName) const = 0

Get the “shortest” path between the two given groups according to their load after metadata.

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.

Return

A vector of Vertex elements representing the path from the source group to the destination group, or an empty vector if no path exists.

Parameters
  • fromGroupName: The name of the source group, that loads earlier.

  • toGroupName: The name of the destination group, that loads later.

Plugin Data Access

virtual std::optional<PluginMetadata> GetPluginMetadata(const std::string &plugin, bool includeUserMetadata = true, bool evaluateConditions = false) const = 0

Get all a plugin’s loaded metadata.

Return

If the plugin has metadata, an optional containing that metadata, otherwise an optional containing no value.

Parameters
  • plugin: The filename of the plugin to look up metadata for.

  • 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.

  • 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.

virtual std::optional<PluginMetadata> GetPluginUserMetadata(const std::string &plugin, bool evaluateConditions = false) const = 0

Get a plugin’s metadata loaded from the given userlist.

Return

If the plugin has user-added metadata, an optional containing that metadata, otherwise an optional containing no value.

Parameters
  • plugin: The filename of the plugin to look up user-added metadata for.

  • 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.

virtual void SetPluginUserMetadata(const PluginMetadata &pluginMetadata) = 0

Sets a plugin’s user metadata, overwriting any existing user metadata.

Parameters
  • pluginMetadata: The user metadata you want to set, with plugin.Name() being the filename of the plugin the metadata is for.

virtual void DiscardPluginUserMetadata(const std::string &plugin) = 0

Discards all loaded user metadata for the plugin with the given filename.

Parameters
  • plugin: The filename of the plugin for which all user-added metadata should be deleted.

virtual void DiscardAllUserMetadata() = 0

Discards all loaded user metadata for all plugins, and any user-added general messages and known bash tags.

class GameInterface

The interface provided for accessing game-specific functionality.

Metadata Access

virtual std::shared_ptr<DatabaseInterface> GetDatabase() = 0

Get the database interface used for accessing metadata-related functionality.

Return

A shared pointer to the game’s DatabaseInterface

Plugin Data Access

virtual bool IsValidPlugin(const std::string &plugin) const = 0

Check if a file is a valid plugin.

The validity check is not exhaustive: it checks that the file extension is .esm or .esp (after trimming any .ghost extension), and that the TES4 header can be parsed.

Return

True if the file is a valid plugin, false otherwise.

Parameters
  • plugin: The filename of the file to check.

virtual void LoadPlugins(const std::vector<std::string> &plugins, bool loadHeadersOnly) = 0

Parses plugins and loads their data.

Any previously-loaded plugin data is discarded when this function is called.

Parameters
  • plugins: The filenames of the plugins to load.

  • loadHeadersOnly: If true, only the plugins’ TES4 headers are loaded. If false, all records in the plugins are parsed, apart from the main master file if it has been identified by a previous call to IdentifyMainMasterFile().

virtual std::shared_ptr<const PluginInterface> GetPlugin(const std::string &pluginName) const = 0

Get data for a loaded plugin.

Return

A shared pointer to a const PluginInterface implementation. The pointer is null if the given plugin has not been loaded.

Parameters
  • pluginName: The filename of the plugin to get data for.

virtual std::set<std::shared_ptr<const PluginInterface>> GetLoadedPlugins() const = 0

Get a set of const references to all loaded plugins’ PluginInterface objects.

Return

A set of const PluginInterface references. The references remain valid until the LoadPlugins() or SortPlugins() functions are next called or this GameInterface is destroyed.

Sorting

virtual void IdentifyMainMasterFile(const std::string &masterFile) = 0

Identify the game’s main master file.

When sorting, LOOT always only loads the headers of the game’s main master file as a performance optimisation.

virtual std::vector<std::string> SortPlugins(const std::vector<std::string> &plugins) = 0

Calculates a new load order for the game’s installed plugins (including inactive plugins) and outputs the sorted order.

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.

Return

A vector of the given plugin filenames in their sorted load order.

Parameters
  • plugins: A vector of filenames of the plugins to sort.

Load Order Interaction

virtual void LoadCurrentLoadOrderState() = 0

Load the current load order state, discarding any previously held state.

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 bool IsPluginActive(const std::string &plugin) const = 0

Check if a plugin is active.

Return

True if the plugin is active, false otherwise.

Parameters
  • plugin: The filename of the plugin for which to check the active state.

virtual std::vector<std::string> GetLoadOrder() const = 0

Get the current load order.

Return

A vector of plugin filenames in their load order.

virtual void SetLoadOrder(const std::vector<std::string> &loadOrder) = 0

Set the game’s load order.

Parameters
  • loadOrder: A vector of plugin filenames sorted in the load order to set.

class PluginInterface

Represents a plugin file that has been parsed by LOOT.

Public Functions

virtual std::string GetName() const = 0

Get the plugin’s filename.

Return

The plugin filename.

virtual float GetHeaderVersion() 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 NaN if the field could not be found.

virtual std::optional<std::string> GetVersion() 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::vector<std::string> GetMasters() const = 0

Get the plugin’s masters.

Return

The plugin’s masters in the same order they are listed in the file.

virtual std::set<Tag> GetBashTags() 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::optional<uint32_t> GetCRC() 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 bool IsMaster() const = 0

Check if the plugin’s master flag is set.

Return

True if the master flag is set, false otherwise.

virtual bool IsLightMaster() const = 0

Check if the plugin is a light master.

Return

True if plugin is a light master, false otherwise.

virtual bool IsValidAsLightMaster() const = 0

Check if the plugin is or would be valid as a light master.

Return

True if the plugin is a valid light master or would be a valid light master, false otherwise.

virtual bool IsEmpty() 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 LoadsArchive() 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 DoFormIDsOverlap(const PluginInterface &plugin) const = 0

Check if two plugins contain a record with the same ID.

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.

Parameters
  • plugin: The other plugin to check for overlap with.

Classes

class ConditionalMetadata

A base class for metadata that can be conditional based on the result of evaluating a condition string.

Subclassed by File, Message, Tag

Public Functions

ConditionalMetadata()

Construct a ConditionalMetadata object with an empty condition string.

Return

A ConditionalMetadata object.

ConditionalMetadata(const std::string &condition)

Construct a ConditionalMetadata object with a given condition string.

Return

A ConditionalMetadata object.

Parameters
  • condition: A condition string, as defined in the LOOT metadata syntax documentation.

bool IsConditional() const

Check if the condition string is non-empty.

Return

True if the condition string is not empty, false otherwise.

void ParseCondition() const

Check if the condition string is syntactically valid.

Throws a ConditionSyntaxError if the condition string’s syntax is not valid.

std::string GetCondition() const

Get the condition string.

Return

The object’s condition string.

class File : public ConditionalMetadata

Represents a file in a game’s Data folder, including files in subdirectories.

Public Functions

File()

Construct a File with blank name, display and condition strings.

Return

A File object.

File(const std::string &name, const std::string &display = "", const std::string &condition = "")

Construct a File with the given name, display name and condition strings.

Return

A File object.

Parameters
  • name: The filename of the file.

  • display: The name to be displayed for the file in messages.

  • condition: The File’s condition string.

bool operator<(const File &rhs) const

A less-than operator implemented with no semantics so that File objects can be stored in sets.

Return

True if this File’s name is case-insensitively lexicographically less than the given File’s name, false otherwise.

bool operator==(const File &rhs) const

Check if two File objects are equal by comparing their filenames.

Return

True if the filenames are case-insensitively equal, false otherwise.

std::string GetName() const

Get the filename of the file.

Return

The file’s filename.

std::string GetDisplayName() const

Get the display name of the file.

Return

The file’s display name.

class Group

Represents a group to which plugin metadata objects can belong.

Public Functions

Group()

Construct a Group with the name “default” and an empty set of groups to load after.

Return

A Group object.

Group(const std::string &name, const std::unordered_set<std::string> &afterGroups = {}, const std::string &description = "")

Construct a Group with the given name, description and set of groups to load after.

Return

A Group object.

Parameters
  • name: The group name.

  • afterGroups: The names of groups this group loads after.

  • description: A description of the group.

bool operator==(const Group &rhs) const

Check if two Group objects are equal by comparing their names.

Return

True if the names are case-sensitively equal, false otherwise.

std::string GetName() const

Get the name of the group.

Return

The group’s name.

std::string GetDescription() const

Get the description of the group.

Return

The group’s description.

std::unordered_set<std::string> GetAfterGroups() const

Get the set of groups this group loads after.

Return

A set of group names.

class Location

Represents a URL at which the parent plugin can be found.

Public Functions

Location()

Construct a Location with empty URL and name strings.

Return

A Location object.

Location(const std::string &url, const std::string &name = "")

Construct a Location with the given URL and name.

Return

A Location object.

Parameters
  • url: The URL at which the plugin can be found.

  • name: A name for the URL, eg. the page or site name.

bool operator<(const Location &rhs) const

A less-than operator implemented with no semantics so that Location objects can be stored in sets.

Return

True if this Location’s URL string is lexicographically less than the given Location’s URL string, false otherwise.

bool operator==(const Location &rhs) const

Check if two Location objects are equal by comparing their URLs.

Return

True if the URL strings are equal, false otherwise.

std::string GetURL() const

Get the object’s URL.

Return

A URL string.

std::string GetName() const

Get the object’s name.

Return

The name of the location.

class LootVersion

A purely static class that provides information about the version of libloot that is being run.

Public Static Functions

static std::string GetVersionString()

Get the API version as a string.

Return

A string of the form “major.minor.patch”.

Public Static Attributes

const unsigned int major

The major version number.

const unsigned int minor

The minor version number.

const unsigned int patch

The patch version number.

const std::string revision

The source control revision that the API was built from.

class MessageContent

Represents a message’s localised text content.

Public Functions

MessageContent()

Construct a MessageContent object with an empty English message string.

Return

A MessageContent object.

MessageContent(const std::string &text, const std::string &language = defaultLanguage)

Construct a Message object with the given text in the given language.

Return

A MessageContent object.

Parameters
  • text: The message text.

  • language: The language that the message is written in.

std::string GetText() const

Get the message text.

Return

A string containing the message text.

std::string GetLanguage() const

Get the message language.

Return

A code representing the language that the message is written in.

bool operator<(const MessageContent &rhs) const

A less-than operator implemented with no semantics so that MessageContent objects can be stored in sets.

Return

True if this MessageContent’s text is lexicographically less than the given MessageContent’s text, false otherwise.

bool operator==(const MessageContent &rhs) const

Check if two MessageContent objects are equal by comparing their texts.

Return

True if the texts are equal, false otherwise.

Public Static Functions

static MessageContent Choose(const std::vector<MessageContent> content, const std::string &language)

Choose a MessageContent object from a vector given a language.

Return

A MessageContent object. If the given vector is empty, a default-constructed MessageContent is returned.

Parameters
  • content: The MessageContent objects to choose between.

  • language: The language code for the preferred language to select. If no message in the preferred language is present, the English MessageContent will be returned.

Public Static Attributes

const std::string defaultLanguage

The code for the default language assumed for message content, which is English.

class Message : public ConditionalMetadata

Represents a message with localisable text content.

Public Functions

Message()

Construct a Message object of type ‘say’ with blank content and condition strings.

Return

A Message object.

Message(const MessageType type, const std::string &content, const std::string &condition = "")

Construct a Message object with the given type, English content and condition string.

Return

A Message object.

Parameters
  • type: The message type.

  • content: The English message content text.

  • condition: A condition string.

Message(const MessageType type, const std::vector<MessageContent> &content, const std::string &condition = "")

Construct a Message object with the given type, content and condition string.

Return

A Message object.

Parameters
  • type: The message type.

  • content: The message content. If multilingual, one language must be English.

  • condition: A condition string.

bool operator<(const Message &rhs) const

A less-than operator implemented with no semantics so that Message objects can be stored in sets.

Return

Returns true if this Message’s content is lexicographically less than the given Message’s content, and false otherwise.

bool operator==(const Message &rhs) const

Check if two Message objects are equal by comparing their content.

Return

True if the contents are equal, false otherwise.

MessageType GetType() const

Get the message type.

Return

The message type.

std::vector<MessageContent> GetContent() const

Get the message content.

Return

The message’s MessageContent objects.

MessageContent GetContent(const std::string &language) const

Get the message content given a language.

Return

A MessageContent object for the preferred language, or for English if a MessageContent object is not available for the given language.

Parameters
  • language: The preferred language for the message content.

SimpleMessage ToSimpleMessage(const std::string &language) const

Get the message as a SimpleMessage given a language.

Return

A SimpleMessage object for the preferred language, or for English if message text is not available for the given language.

Parameters
  • language: The preferred language for the message content.

class PluginCleaningData

Represents data identifying the plugin under which it is stored as dirty or clean.

Public Functions

PluginCleaningData()

Construct a PluginCleaningData object with zero CRC, ITM count, deleted reference count and deleted navmesh count values, an empty utility string and no info.

Return

A PluginCleaningData object.

PluginCleaningData(uint32_t crc, const std::string &utility)

Construct a PluginCleaningData object with the given CRC and utility, zero ITM count, deleted reference count and deleted navmesh count values and no info.

Return

A PluginCleaningData object.

Parameters
  • crc: The CRC of a plugin.

  • utility: The utility that the plugin cleanliness was checked with.

PluginCleaningData(uint32_t crc, const std::string &utility, const std::vector<MessageContent> &info, unsigned int itm, unsigned int ref, unsigned int nav)

Construct a PluginCleaningData object with the given values.

Return

A PluginCleaningData object.

Parameters
  • crc: A clean or dirty plugin’s CRC.

  • utility: The utility that the plugin cleanliness was checked with.

  • info: A vector of localised information message strings about the plugin cleanliness.

  • itm: The number of Identical To Master records found in the plugin.

  • ref: The number of deleted references found in the plugin.

  • nav: The number of deleted navmeshes found in the plugin.

bool operator<(const PluginCleaningData &rhs) const

A less-than operator implemented with no semantics so that PluginCleaningData objects can be stored in sets.

Return

True if this PluginCleaningData’s CRC is less than the given PluginCleaningData’s CRC, false otherwise.

bool operator==(const PluginCleaningData &rhs) const

Check if two PluginCleaningData objects are equal by comparing their CRCs.

Return

True if the CRCs are equal, false otherwise.

uint32_t GetCRC() const

Get the CRC that identifies the plugin that the cleaning data is for.

Return

A CRC-32 checksum.

unsigned int GetITMCount() const

Get the number of Identical To Master records in the plugin.

Return

The number of Identical To Master records in the plugin.

unsigned int GetDeletedReferenceCount() const

Get the number of deleted references in the plugin.

Return

The number of deleted references in the plugin.

unsigned int GetDeletedNavmeshCount() const

Get the number of deleted navmeshes in the plugin.

Return

The number of deleted navmeshes in the plugin.

std::string GetCleaningUtility() 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 Markdown-formatted URL to the utility’s download location.

std::vector<MessageContent> GetInfo() 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.

MessageContent ChooseInfo(const std::string &language) const

Choose an info MessageContent object given a preferred language.

Return

The MessageContent object for the preferred language, or if one does not exist, the English-language MessageContent object.

Parameters
  • language: The preferred language’s code.

class PluginMetadata

Represents a plugin’s metadata.

Public Functions

PluginMetadata()

Construct a PluginMetadata object with a blank plugin name and no metadata.

Return

A PluginMetadata object.

PluginMetadata(const std::string &name)

Construct a PluginMetadata object with no metadata for a plugin with the given filename.

Return

A PluginMetadata object.

Parameters
  • name: The filename of the plugin that the object is constructed for.

void MergeMetadata(const PluginMetadata &plugin)

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 enabled state is replaced by the given object’s state. This object’s group is replaced by the given object’s group if the latter is explicit.

Parameters
  • plugin: The plugin metadata to merge.

PluginMetadata NewMetadata(const PluginMetadata &plugin) const

Get metadata in this object that isn’t present in the given PluginMetadata object.

Return

A PluginMetadata object containing the metadata in this object that is not in the given object. The returned object inherits this object’s enabled state and group.

Parameters

std::string GetName() const

Get the plugin name.

Return

The plugin name.

std::string GetLowercasedName() const

Get the lowercased plugin name.

Return

The lowercased plugin name.

std::string GetNormalizedName() const

Get the plugin name, normalized to be suitable for case-insensitive filename comparison.

Return

The normalized plugin name.

bool IsEnabled() const

Check if the plugin metadata is enabled for use during sorting.

Return

True if the metadata will be used during sorting, false otherwise.

std::optional<std::string> GetGroup() 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.

std::set<File> GetLoadAfterFiles() const

Get the plugins that the plugin must load after.

Return

The plugins that the plugin must load after.

std::set<File> GetRequirements() const

Get the files that the plugin requires to be installed.

Return

The files that the plugin requires to be installed.

std::set<File> GetIncompatibilities() const

Get the files that the plugin is incompatible with.

Return

The files that the plugin is incompatible with.

std::vector<Message> GetMessages() const

Get the plugin’s messages.

Return

The plugin’s messages.

std::set<Tag> GetTags() const

Get the plugin’s Bash Tag suggestions.

Return

The plugin’s Bash Tag suggestions.

std::set<PluginCleaningData> GetDirtyInfo() const

Get the plugin’s dirty plugin information.

Return

The PluginCleaningData objects that identify the plugin as dirty.

std::set<PluginCleaningData> GetCleanInfo() const

Get the plugin’s clean plugin information.

Return

The PluginCleaningData objects that identify the plugin as clean.

std::set<Location> GetLocations() const

Get the locations at which this plugin can be found.

Return

The locations at which this plugin can be found.

std::vector<SimpleMessage> GetSimpleMessages(const std::string &language) const

Get the plugin’s messages as SimpleMessage objects for the given language.

Return

The plugin’s messages as SimpleMessage objects.

Parameters
  • language: The language to create the SimpleMessage objects for.

void SetEnabled(const bool enabled)

Set whether the plugin metadata is enabled for use during sorting or not.

Parameters
  • enabled: The value to set.

void SetGroup(const std::string &group)

Set the plugin’s group.

Parameters
  • group: The name of the group this plugin belongs to.

void UnsetGroup()

Unsets the plugin’s group.

void SetLoadAfterFiles(const std::set<File> &after)

Set the files that the plugin must load after.

Parameters
  • after: The files to set.

void SetRequirements(const std::set<File> &requirements)

Set the files that the plugin requires to be installed.

Parameters
  • requirements: The files to set.

void SetIncompatibilities(const std::set<File> &incompatibilities)

Set the files that the plugin must load after.

Parameters
  • incompatibilities: The files to set.

void SetMessages(const std::vector<Message> &messages)

Set the plugin’s messages.

Parameters
  • messages: The messages to set.

void SetTags(const std::set<Tag> &tags)

Set the plugin’s Bash Tag suggestions.

Parameters
  • tags: The Bash Tag suggestions to set.

void SetDirtyInfo(const std::set<PluginCleaningData> &info)

Set the plugin’s dirty information.

Parameters
  • info: The dirty information to set.

void SetCleanInfo(const std::set<PluginCleaningData> &info)

Set the plugin’s clean information.

Parameters
  • info: The clean information to set.

void SetLocations(const std::set<Location> &locations)

Set the plugin’s locations.

Parameters
  • locations: The locations to set.

bool HasNameOnly() const

Check if no plugin metadata is set.

Return

True if the group is implicit and the metadata containers are all empty, false otherwise.

bool IsRegexPlugin() const

Check if the plugin name is a regular expression.

Return

True if the plugin name contains any of the characters :\*?|, false otherwise.

bool operator==(const PluginMetadata &rhs) const

Check if two PluginMetadata objects are equal by comparing their name values.

Return

True if the plugin names are case-insensitively equal, false otherwise.

bool operator!=(const PluginMetadata &rhs) const

Check if two PluginMetadata objects are not equal by comparing their name values.

Return

True if the plugin names are not case-insensitively equal, false otherwise.

bool operator==(const std::string &rhs) const

Check if object’s name value is equal to the given string.

Return

True if the plugin name is case-insensitively equal to the given string, false otherwise.

bool operator!=(const std::string &rhs) const

Check if object’s name value is not equal to the given string.

Return

True if the plugin name is not case-insensitively equal to the given string, false otherwise.

class Tag : public ConditionalMetadata

Represents a Bash Tag suggestion for a plugin.

Public Functions

Tag()

Construct a Tag object with an empty tag name suggested for addition, with an empty condition string.

Return

A Tag object.

Tag(const std::string &tag, const bool isAddition = true, const std::string &condition = "")

Construct a Tag object with the given name, for addition or removal, with the given condition string.

Return

A Tag object.

Parameters
  • tag: The name of the Bash Tag.

  • isAddition: True if the tag should be added, false if it should be removed.

  • condition: A condition string.

bool operator<(const Tag &rhs) const

A less-than operator implemented with no semantics so that Tag objects can be stored in sets.

Return

True if this Tag is suggested for addition and the other is not. If both Tags are suggested for addition or both are suggested for removal, returns true if this Tag’s name is lexicographically less than the given Tag’s name, false otherwise.

bool operator==(const Tag &rhs) const

Check if two Tag objects are equal.

Return

True if both Tags are suggested for addition or both are suggested for removal, and the Tag names are equal, false otherwise.

bool IsAddition() const

Check if the tag should be added.

Return

True if the tag should be added, false if it should be removed.

std::string GetName() const

Get the tag’s name.

Return

The tag’s name.

class Vertex

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.

Public Functions

Vertex(std::string name)

Construct a Vertex with the given name and no out edge.

Parameters
  • name: The name of the plugin or group that this vertex represents.

Vertex(std::string name, EdgeType outEdgeType)

Construct a Vertex with the given name and out edge type.

Parameters
  • name: The name of the plugin or group that this vertex represents.

  • outEdgeType: The type of the edge going out from this vertex.

std::string GetName() const

Get the name of the plugin or group.

Return

The name of the plugin or group.

std::optional<EdgeType> GetTypeOfEdgeToNextVertex() const

Get the type of the edge going to the next vertex.

Each edge goes from the vertex that loads earlier to the vertex that loads later.

Return

The edge type.

Exceptions

class CyclicInteractionError : public runtime_error

An exception class thrown if a cyclic interaction is detected when sorting a load order.

Public Functions

CyclicInteractionError(std::vector<Vertex> cycle)

Construct an exception detailing a plugin or group graph cycle.

Parameters
  • cycle: A representation of the cyclic path.

std::vector<Vertex> GetCycle()

Get a representation of the cyclic path.

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.

class GitStateError : public logic_error

An exception class thrown if an error occurs when performing an operation on a Git repository due to invalid state.

class ConditionSyntaxError : public runtime_error

An exception class thrown if invalid syntax is encountered when parsing a metadata condition.

class FileAccessError : public runtime_error

An exception class thrown if an error is encountered while reading or writing a file.

class UndefinedGroupError : public runtime_error

An exception class thrown if group is referenced but is undefined.

Public Functions

UndefinedGroupError(const std::string &groupName)

Construct an exception for an undefined group.

Parameters
  • groupName: The name of the group that is undefined.

std::string GetGroupName()

Get the name of the undefined group.

Return

A group name.

Error Categories

LOOT uses error category objects to identify errors with codes that originate in lower-level libraries.

const std::error_category &loot::libloadorder_category()

Get the error category that can be used to identify system_error exceptions that are due to libloadorder errors.

Return

A reference to the static object of unspecified runtime type, derived from std::error_category.

const std::error_category &loot::libgit2_category()

Get the error category that can be used to identify system_error exceptions that are due to libgit2 errors.

Return

A reference to the static object of unspecified runtime type, derived from std::error_category.

Credits

libloot is written by Ortham in C++ and makes use of the Boost, esplugin, libgit2, libloadorder, loot-condition-interpreter, spdlog and yaml-cpp libraries. The copyright licenses for all of these and libloot itself in Copyright License Texts.

Version History

0.14.10 - 2019-09-06

Changed

  • Improved the sorting process for Morrowind. Previously, sorting was unable to determine if a Morrowind plugin contained any records overriding those of its masters, and so added no overlap edges between Morrowind plugins when sorting. Sorting now counts override records by comparing plugins against their masters, giving the same results as for other games.

    However, unlike for other games, this requires all a plugin’s masters to be installed. If a plugin’s masters are missing, the plugin’s total record count will be used as if it was the plugin’s override record count to ensure that sorting can still proceed, albeit with potentially reduced accuracy.

  • Updated libgit2 to v0.28.3.

0.14.9 - 2019-07-23

Fixed

  • Regular expressions in condition strings are now prefixed with ^ and suffixed with $ before evaluation to ensure that only exact matches to the given expression are found. Via loot-condition-interpreter.

Changed

  • Updated loot-condition-interpreter to v2.0.0.

0.14.8 - 2019-06-30

Fixed

  • Evaluating version() and product_version() conditions will no longer error if the given executable has no version fields. Instead, it will be evaluated as having no version. Via loot-condition-interpreter.

  • Sorting would not preserve the existing relative positions of plugins that had no relative positioning enforced by plugin data or metadata, if one or both of their filenames were not case-sensitively equal to their entries in plugins.txt / loadorder.txt. Load order position comparison is now correctly case-insensitive.

Changed

  • Improved load order sorting performance.

  • Updated loot-condition-interpreter to v2.0.0.

0.14.7 - 2019-06-13

Fixed

  • Filename comparisons on Windows now has the same locale-invariant case insensitivity behaviour as Windows itself, instead of being locale-dependent.

  • Filename comparisons on Linux now use ICU case folding to give locale-invariant results that are much closer to Windows’ case insensitivity, though still not identical.

Changed

  • Updated libgit2 to v0.28.2.

0.14.6 - 2019-04-24

Added

  • Support for TES III: Morrowind using GameType::tes3. The sorting process for Morrowind is slightly different than for other games, because LOOT cannot currently detect when plugins overlap. As a result, LOOT is much less likely to suggest load order changes.

Changed

  • Updated esplugin to v2.1.2.

  • Updated loot-condition-interpreter to v1.3.0.

Fixed

  • LOOT would unnecessarily ignore intermediate plugins in a non-master to master cycle involving groups, leading to unexpected results when sorting plugins.

0.14.5 - 2019-02-27

Changed

  • Updated libgit2 to v0.28.1.

  • Updated libloadorder to v12.0.1.

  • Updated spdlog to v1.3.1.

Fixed

  • HearthFires.esm was not recognised as a hardcoded plugin on case-sensitive filesystems, causing a cyclic interaction error when sorting Skyrim or Skyrim SE (via libloadorder).

0.14.4 - 2019-01-27

Added

0.14.3 - 2019-01-27

Changed

  • Condition parsing now errors if it does not consume the whole condition string. Via loot-condition-interpreter.

  • Removed a few unhelpful log statements and changed the verbosity level of others.

  • Updated loot-condition-interpreter to v1.2.2.

Fixed

  • Conditions were not parsed past the first instance of file(<regex>), active(<regex>), many(<regex>) or many_active(<regex>). Via loot-condition-interpreter.

  • loot::CreateGameHandle() could crash when trying to check if the given paths are symlinks. If a check fails, LOOT will assume the path is not a symlink.

0.14.2 - 2019-01-20

Changed

  • Updated loot-condition-interpreter to v1.2.1.

  • Updated spdlog to v1.3.0.

Fixed

  • An error when loading plugins with a file present in the plugins directory that has a filename containing characters that cannot be represented in the system code page.

  • An error when trying to read the version of an executable that does not have a US English version information resource. Executable versions are now read from the file’s first version information resource, whatever its language. Via loot-condition-interpreter.

0.14.1 - 2018-12-23

Changed

  • Updated loot-condition-interpreter to v1.2.0.

Fixed

  • Product version conditions read from executables’ VS_FIXEDFILEINFO structure, so the versions read did not match the versions displayed by Windows’ File Explorer. Product versions are now read from executables’ VS_VERSIONINFO structure, using the ProductVersion key. Via loot-condition-interpreter.

  • The release date in the metadata syntax changelog for v0.14 was “Unreleased”.

0.14.0 - 2018-12-09

Added

  • GetHeaderVersion() to get the value of the version field in the HEDR subrecord of a plugin’s TES4 record.

  • IsValidAsLightMaster() to check if a light master is valid or if a non-light-master plugin would be valid with the light master flag or .esl extension. Validity is defined as having no new records with a FormID object index greater than 0xFFF.

  • GetGroupsPath() to return the path between two given groups that maximises the user metadata and minimises the masterlist metadata involved.

  • loot::Vertex to represent a plugin or group vertex in a sorting graph path.

  • loot::EdgeType to represent the type of the edge between two vertices in a sorting graph. Each edge type indicates the type of data it was sourced from.

Changed

  • Renamed the library from “the LOOT API” to “libloot” to avoid confusion between the name of the library and the API that it provides. The library filename is changed so that the loot_api part is now loot, e.g. loot.dll on Windows and libloot.so on Linux.

  • CyclicInteractionError() has had its constructor and methods completely replaced to provide a more detailed and flexible representation of the cyclic path that it reports.

  • UndefinedGroupError::getGroupName() has been renamed to UndefinedGroupError::GetGroupName() for consistency with other API method names.

  • LootVersion::string() has been renamed to LootVersion::GetVersionString() for consistency with other API method names.

  • GetPluginMetadata() and GetPluginUserMetadata() now return std::optional<PluginMetadata> to differentiate metadata being found or not. Note that the PluginMetadata value may still return true for HasNameOnly() if a metadata entry exists but has no content other than the plugin name.

  • GetGroup() now returns std::optional<std::string> to indicate when there is no group metadata explicitly set, to simplify distinguishing between explicit and implicit default group membership.

  • GetVersion() now returns std::optional<std::string> to differentiate between there being no version and the version being an empty string, though the latter should never occur.

  • GetCRC() now returns std::optional<uint32_t> to differentiate between there being no CRC calculated and the CRC somehow being zero (which should never occur).

  • Filesystem paths are now represented in the API by std::filesystem::path values instead of std::string values. This affects the following functions:

  • The metadata condition parsing, evaluation and caching code and the pseudosem dependency have been replaced by a dependency on loot-condition-interpreter, which provides more granular caching and more opportunity for future enhancements.

  • The API now supports v0.14 of the metadata syntax.

  • Updated C++ version required to C++17. This means that Windows builds now require the MSVC 2017 runtime redistributable to be installed.

  • Updated esplugin to v2.1.1.

  • Updated libloadorder to v12.0.0.

  • Updated libgit2 to v0.27.7.

  • Updated spdlog to v1.2.1.

Removed

  • PluginInterface::GetLowercasedName(), as the case folding behaviour LOOT uses is not necessarily appropriate for all use cases, so it’s up to the client to lowercase according to their own needs.

Fixed

  • BSAs/BA2s loaded by non-ASCII plugins for Oblivion, Fallout 3, Fallout: New Vegas and Fallout 4 may not have been detected due to incorrect case-insensitivity handling.

  • Fixed incorrect case-insensitivity handling for non-ASCII plugin filenames and File metadata names.

  • FileVersion and ProductVersion properties were not set in the DLL since v0.11.0.

  • Path equivalence checks could be inaccurate as they were using case-insensitive string comparisons, which may not match filesystem behaviour. Filesystem equivalence checks are now used to improve correctness.

  • Errors due to filesystem permissions when cloning a new masterlist repository into an existing game directory. Deleting the temporary directory is now deferred until after its contents have been copied into the game directory, and if an error is encountered when deleting the temporary directory, it is logged but does not cause the masterlist update to fail.

  • An error creating a game handle for Skyrim if loadorder.txt is not encoded in UTF-8. In this case, libloadorder will now fall back to interpreting its contents as encoded in Windows-1252, to match the behaviour when reading the load order state.

0.13.8 - 2018-09-24

Fixed

  • Filesystem errors when trying to set permissions during a masterlist update that clones a new repository.

0.13.7 - 2018-09-10

Changed

  • Significantly improve plugin loading performance by scanning for BSAs/BA2s once instead of for each plugin.

  • Improve performance of metadata evaluation by caching CRCs with the same cache lifetime as condition results.

  • Improve performance of sorting when it involves long plugin interaction chains.

  • Updated esplugin to v2.0.1.

  • Updated libgit2 to v0.27.4.

  • Updated libloadorder v11.4.1.

  • Updated spdlog to v1.1.0.

  • Updated yaml-cpp to 0.6.2+merge-key-support.2.

Fixed

  • Fallout 4’s DLCUltraHighResolution.esm is now handled as a hardcoded plugin (via libloadorder).

0.13.6 - 2018-06-29

Changed

  • Tweaked masterlist repository cloning to avoid undefined behaviour.

  • Updated Boost to v1.67.0.

  • Updated esplugin to v2.0.0.

  • Updated libgit2 to v0.27.2.

  • Updated libloadorder to v11.4.0.

0.13.5 - 2018-06-02

Changed

  • Sorting now enforces hardcoded plugin positions, sourcing them through libloadorder. This avoids the need for often very verbose metadata entries, particularly for Creation Club plugins.

  • Updated libgit2 to v0.27.1. This includes a security fix for CVE-2018-11235, but LOOT API’s usage is not susceptible. libgit2 is not susceptible to CVE-2018-11233, another Git vulnerability which was published on the same day.

  • Updated libloadorder to v11.3.0.

  • Updated spdlog to v0.17.0.

  • Updated esplugin to v1.0.10.

0.13.4 - 2018-06-02

Fixed

  • NewMetadata() now uses the passed plugin’s group if the calling plugin’s group is implicit, and sets the group to be implicit if the two plugins’ groups are equal.

0.13.3 - 2018-05-26

Changed

  • Improved cycle avoidance when resolving evaluating plugin groups during sorting. If enforcing the group difference between two plugins would cause a cycle and one of the plugins’ groups is the default group, that plugin’s group will be ignored for all plugins in groups between default and the other plugin’s group.

  • The masterlist repository cloning process no longer moves LOOT’s game folders, so if something goes wrong the process fails more safely.

  • The LOOT API is now built with debugging information on Windows, and its PDB is included in build archives.

  • Updated libloadorder to v11.2.2.

Fixed

  • Various filesystem-related issues that could be encountered when updating masterlists, including failure due to file handles being left open while attempting to remove.

  • Building the esplugin and libloadorder dependencies using Rust 1.26.0, which included a regression to workspace builds.

0.13.2 - 2018-04-29

Changed

  • Updated libloadorder to v11.2.1.

Fixed

  • Incorrect load order positions were given for light-master-flagged .esp plugins when getting the load order (via libloadorder).

0.13.1 - 2018-04-09

Added

  • Support for Skyrim VR using GameType::tes5vr.

Changed

  • Updated libloadorder to v11.2.0.

0.13.0 - 2018-04-02

Added

Changed

Removed

  • Support for local and global plugin priorities.

    • Removed the loot::Priority class.

    • Removed PluginMetadata::GetLocalPriority(), PluginMetadata::GetGlobalPriority(), PluginMetadata::SetLocalPriority() and PluginMetadata::SetGlobalPriority()

    • Priorities are no longer taken into account when sorting plugins.

Fixed

  • An error when applying a load order for Morrowind, Oblivion, Fallout 3 or Fallout: New Vegas when a plugin had a timestamp earlier than 1970-01-01 00:00:00 UTC (via libloadorder).

  • An error when loading the current load order for Skyrim with a loadorder.txt incorrectly encoded in Windows-1252 (via libloadorder).

0.12.5 - 2018-02-17

Changed

  • Updated esplugin to v1.0.9.

  • Updated libgit2 to v0.26.3. This enables TLS 1.2 support on Windows 7, so users shouldn’t need to manually enable it themselves.

0.12.4 - 2018-02-17

Fixed

  • Loading or saving a load order could be very slow because the plugins directory was scanned recursively, which is unnecessary. In the reported case, this fix caused saving a load order to go from 23 seconds to 43 milliseconds (via libloadorder).

  • Plugin parsing errors were being logged with trace severity, they are now logged as errors.

  • Saving a load order for Oblivion, Fallout 3 or Fallout: New Vegas now updates plugin access times to the current time for correctness (via libloadorder).

Changed

  • GameInterface::SetLoadOrder() now errors if passed a load order that does not contain all installed plugins. The previous behaviour was to append any missing plugins, but this was undefined and could cause unexpected results (via libloadorder).

  • Performance improvements for load order operations, benchmarked at 2x to 150x faster (via libloadorder).

  • Updated mentions of libespm in error messages to mention esplugin instead.

  • Updated libloadorder to v11.0.1.

  • Updated spdlog to v0.16.3.

0.12.3 - 2018-02-04

Added

  • Support for Fallout 4 VR via the new loot::GameType::fo4vr game type.

Fixed

  • loot::CreateGameHandle() no longer accepts an empty game path string, and no longer has a default value for its game path parameter, as using an empty string as the game path is invalid and always causes an exception to be thrown.

Changed

  • Added an empty string as the default value of loot::InitialiseLocale()’s string parameter.

  • Updated esplugin to v1.0.8.

  • Updated libloadorder to v10.1.0.

0.12.2 - 2017-12-24

Fixed

  • Plugins with a .esp file extension that have the light master flag set are no longer treated as masters when sorting, so they can have other .esp files as masters without causing cyclic interaction sorting errors.

Changed

  • Downgraded Boost to 1.63.0 to take advantage of pre-built binaries on AppVeyor.

0.12.1 - 2017-11-23

Added

  • Support for identifying Creation Club plugins using Skyrim.ccc and Fallout4.ccc (via libloadorder).

Changed

  • Update esplugin to v1.0.7.

  • Update libloadorder to v10.0.4.

0.12.0 - 2017-11-03

Added

Changed

  • LoadPlugins() now loads the current load order state before loading plugins.

  • Added a condition string field to SimpleMessage.

  • Replaced libespm dependency with esplugin v1.0.6. This significantly improves safety and sorting performance, especially for large load orders.

  • Updated libloadorder to v10.0.3. This significantly improves safety and the performance of load order operations, at the expense of exposing cache management to the client.

  • Replaced Boost.Log with spdlog v0.14.0, removing dependencies on several other Boost libraries in the process.

  • Updated libgit2 to v0.26.0.

  • Update Boost to v1.65.1.

Removed

  • DatabaseInterface::EvalLists() as it was superseded in v0.11.0 by the ability to evaluate conditions when getting general messages and individual plugins’ metadata, which is more efficient.

  • SetLoggingVerbosity() and SetLogFile() as they have been superseded by the new loot::SetLoggingCallback() function.

  • The loot/yaml/* headers containing LOOT’s internal YAML conversion functions are no longer exposed alongside the API headers.

  • The loot/windows_encoding_converters.h header is no longer exposed alongside the API headers.

Fixed

  • Formatting in metadata documentation.

  • Saving metadata wrote entries in an inconsistent order.

  • Clang build errors.

0.11.1 - 2017-06-19

Fixed

  • A crash would occur when loading an plugin that had invalid data past its header. Such plugins are now just silently ignored.

  • loot::CreateGameHandle() would not resolve game or local data paths that are junction links correctly, which caused problems later when trying to perform actions such as loading plugins.

  • Performing a masterlist update on a branch where the remote and local histories had diverged would fail. The existing local branch is now discarded and the remote branch checked out anew, as intended.

0.11.0 - 2017-05-13

Added

Changed

  • Renamed loot::CreateDatabase() to loot::CreateGameHandle(), and changed its signature so that it returns a shared pointer to a loot::GameInterface instead of a shared pointer to a loot::DatabaseInterface.

  • Moved SortPlugins() into loot::GameInterface.

  • Some loot::DatabaseInterface methods are now const:

  • LOOT’s internal YAML conversion functions have been refactored into the include/loot/yaml directory, but they are not really part of the API. They’re only exposed so that they can be shared between the API and LOOT application without introducing another component.

  • LOOT’s internal string encoding conversion functions have been refactored into the include/loot/windows_encoding_converters.h header, but are not really part of the API. They’re only exposed so that they can be shared between the API and LOOT application without introducing another component.

  • Metadata is now cached more efficiently, reducing the API’s memory footprint.

  • Log timestamps now have microsecond precision.

  • Updated to libgit2 v0.25.1.

  • Refactored code only useful to the LOOT application out of the API internals and into the application source code.

Removed

  • DatabaseInterface::GetPluginTags(), DatabaseInterface::GetPluginMessages() and DatabaseInterface::GetPluginCleanliness() have been removed as they have been superseded by DatabaseInterface::GetPluginMetadata().

  • The GameDetectionError class, as it is no longer thrown by the API.

  • The PluginTags struct, as it is no longer used.

  • The LanguageCode enum, as the API now uses ISO language codes directly instead.

  • The PluginCleanliness enum. as it’s no longer used. Plugin cleanliness should now be checked by getting a plugin’s evaluated metadata and checking if any dirty info is present. If none is present, the cleanliness is unknown. If dirty info is present, check if any of the English info strings contain the text “Do not clean”: if not, the plugin is dirty.

  • The LOOT API no longer caches the load order, as this is already done more accurately by libloadorder (which is used internally).

Fixed

  • Libgit2 error details were not being logged.

  • A FileAccessError was thrown when the masterlist path was an empty string. The API now just skips trying to load the masterlist in this case.

  • Updating the masterlist did not update the cached metadata, requiring a call to LoadLists().

  • The reference documentation was broken due to an incompatibility between Sphinx 1.5.x and Breathe 4.4.

0.10.3 - 2017-01-08

Added

  • Automated 64-bit API builds.

Changed

  • Replaced std::invalid_argument exceptions thrown during condition evaluation with ConditionSyntaxError exceptions.

  • Improved robustness of error handling when calculating file CRCs.

Fixed

  • Documentation was not generated correctly for enums, exceptions and structs exposed by the API.

  • Added missing documentation for CyclicInteractionError methods.

0.10.2 - 2016-12-03

Changed

  • Updated libgit2 to 0.24.3.

Fixed

  • A crash could occur if some plugins that are hardcoded to always load were missing. Fixed by updating to libloadorder v9.5.4.

  • Plugin cleaning metadata with no info value generated a warning message with no text.

0.10.1 - 2016-11-12

No API changes.

0.10.0 - 2016-11-06

Added

  • Support for TES V: Skyrim Special Edition.

Changed

  • Completely rewrote the API as a C++ API. The C API has been reimplemented as a wrapper around the C++ API, and can be found in a separate repository.

  • Windows builds now have a runtime dependency on the MSVC 2015 runtime redistributable.

  • Rewrote the API documentation, which is now hosted online at Read The Docs.

  • The Windows release archive includes the .lib file for compile-time linking.

  • LOOT now supports v0.10 of the metadata syntax. This breaks compatibility with existing syntax. See the syntax version history for the details.

  • Updated libgit2 to 0.24.2.

Removed

  • The loot_get_tag_map() function has no equivalent in the new C++ API as it is obsolete.

  • The loot_apply_load_order() function has no equivalent in the new C++ API as it just passed through to libloadorder, which clients can use directly instead.

Fixed

  • Database creation was failing when passing paths to symlinks that point to the game and/or game local paths.

  • Cached plugin CRCs causing checksum conditions to always evaluate to false.

  • Updating the masterlist when the user’s TEMP and TMP environmental variables point to a different drive than the one LOOT is installed on.

0.9.2 - 2016-08-03

Changed

  • libespm (2.5.5) and Pseudosem (1.1.0) dependencies have been updated to the versions given in brackets.

Fixed

  • The packaging script used to create API archives was packaging the wrong binary, which caused the v0.9.0 and v0.9.1 API releases to actually be re-releases of a snapshot build made at some point between v0.8.1 and v0.9.0: the affected API releases were taken offline once this was discovered.

  • loot_get_plugin_tags() remembering results and including them in the results of subsequent calls.

  • An error occurred when the user’s temporary files directory didn’t exist and updating the masterlist tried to create a directory there.

  • Errors when reading some Oblivion plugins during sorting, including the official DLC.

0.9.1 - 2016-06-23

No API changes.

0.9.0 - 2016-05-21

Changed

  • Moved API header location to the more standard include/loot/api.h.

  • Documented LOOT’s masterlist versioning system.

  • Made all API outputs fully const to make it clear they should not be modified and to avoid internal const casting.

  • The loot_db type is now an opaque struct, and functions that used to take it as a value now take a pointer to it.

Removed

  • The loot_cleanup() function, as the one string it used to destroy is now stored on the stack and so destroyed when the API is unloaded.

  • The loot_lang_any constant. The loot_lang_english constant should be used instead.

0.8.1 - 2015-09-27

Changed

  • Safety checks are now performed on file paths when parsing conditions (paths must not reference a location outside the game folder).

  • Updated Boost (1.59.0), libgit2 (0.23.2) and CEF (branch 2454) dependencies.

Fixed

  • A crash when loading plugins due to lack of thread safety.

  • The masterlist updater and validator not checking for valid condition and regex syntax.

  • The masterlist updater not working correctly on Windows Vista.

0.8.0 - 2015-07-22

Added

  • Support for metadata syntax v0.8.

Changed

  • Improved plugin loading performance for computers with weaker multithreading capabilities (eg. non-hyperthreaded dual-core or single-core CPUs).

  • LOOT no longer outputs validity warnings for inactive plugins.

  • Updated libgit2 to v0.23.0.

Fixed

  • Many miscellaneous bugs, including initialisation crashes and incorrect metadata input/output handling.

  • LOOT silently discarding some non-unique metadata: an error will now occur when loading or attempting to apply such metadata.

  • LOOT’s version comparison behaviour for a wide variety of version string formats.

0.7.1 - 2015-06-22

Fixed

  • “No existing load order position” errors when sorting.

  • Output of Bash Tag removal suggestions in loot_write_minimal_list().

0.7.0 - 2015-05-20

Initial API release.

Introduction

The metadata syntax is what LOOT’s masterlists and userlists are written in. If you know YAML, good news: the syntax is essentially just YAML 1.2. If you don’t know YAML, then its Wikipedia page is a good introduction. All you really need to know is:

  • How lists and associative arrays (key-value maps) are written.

  • That whitespace is important, and that only normal spaces (ie. no non-breaking spaces or tabs) count as such.

  • That data entries that are siblings must be indented by the same amount, and child data nodes must be indented further than their parents (see the example later in this document if you don’t understand).

  • That YAML files must be written in a Unicode encoding.

  • That each key in a key-value map must only appear once per map object.

An important point that is more specific to how LOOT uses YAML:

  • Strings are case-sensitive, apart from file paths, regular expressions and checksums.

  • File paths are evaluated relative to the game’s Data folder.

  • File paths cannot reference a path outside of the game’s folder structure, ie. they cannot contain the substring ../../.

In this document, where a value’s type is given as X list this is equivalent to a YAML sequence of values which are of the data type X. Where a value’s type is given as X set, this is equivalent to a YAML sequence of unique values which are of the data type X. Uniqueness is determined using the equality criteria for that data type. All the non-standard data types that LOOT’s metadata syntax uses have their equality criteria defined later in this document.

Metadata File Structure

The root of a metadata file is a key-value map. LOOT will recognise the following keys, none of which are required. Other keys may also be present, but are not processed by LOOT.

bash_tags

string list

A list of Bash Tags that are supported by the masterlist’s game. These Bash Tags are used to provide autocomplete suggestions in LOOT’s metadata editor.

globals

message list

A list of message data structures for messages that are displayed independently of any plugin.

groups

group set

A set of group data structures that represent the groups that plugins can belong to.

plugins

plugin list and plugin set

The plugin data structures that hold all the plugin metadata within the file. It is a mixture of a list and a set because no non-regex plugin value may be equal to any other non-regex plugin value , but there may be any number of equal regex plugin values, and non-regex plugin values may be equal to regex plugin values. If multiple plugin values match a single plugin, their metadata is merged in the order the values are listed, and as defined in Merging Behaviour.

The message and plugin data structures are detailed in the next section.

Example

bash_tags:
  - 'C.Climate'
  - 'Relev'

globals:
  - type: say
    content: 'You are using the latest version of LOOT.'
    condition: 'version("LOOT", "0.5.0.0", ==)'

groups:
  - name: 'Map Markers'
    after:
      - 'default'

plugins:
  - name: 'Armamentarium.esm'
    tag:
      - Relev
  - name: 'ArmamentariumFran.esm'
    tag:
      - Relev
  - name: 'Beautiful People 2ch-Ed.esm'
    tag:
      - Eyes
      - Graphics
      - Hair
      - R.Relations
  - name: 'More Map Markers.esp'
    group: 'Map Markers'

Data Structures

LOOT expects metadata to be laid out using a certain set of data structures, described in this section.

Tag

LOOT metadata files can contain suggestions for the addition or removal of Bash Tags, and this is the structure used for them. It has two forms: a key-value string map and a scalar string.

Map Form

name

Required. A Bash Tag, prefixed with a minus sign if it is suggested for removal.

condition

A condition string that is evaluated to determine whether this Bash Tag should be suggested: if it evaluates to true, the Tag is suggested, otherwise it is ignored. See Condition Strings for details. If undefined, defaults to an empty string.

Scalar Form

The scalar form is simply the value of the map form’s name key. Using the scalar form is equivalent to using the map form with an undefined condition key.

Equality

Two tag data structures are equal if the values of their name keys are identical.

Examples

Scalar form:

Relations

Map form:

name: -Relations
condition: 'file("Mart''s Monster Mod for OOO.esm") or file("FCOM_Convergence.esm")'

File

This structure can be used to hold file paths. It has two forms: a key-value string map and a scalar string.

Map Form

name

Required. An exact (ie. not regex) file path or name.

display

A substitute string to be displayed instead of the file path in any generated messages, eg. the name of the mod the file belongs to. If undefined, the name key’s value is used.

condition

A condition string that is evaluated to determine whether this file data should be used: if it evaluates to true, the data is used, otherwise it is ignored. See Condition Strings for details.

Scalar Form

The scalar form is simply the value of the map form’s name key. Using the scalar form is equivalent to using the map form with undefined display and condition keys.

Equality

Two file data structures are equal if the lowercased values of their name keys are identical.

Examples

Scalar form:

'../obse_loader.exe'

Map form:

name: '../obse_loader.exe'
condition: 'version("../obse_loader.exe", "0.0.18.0", &gt;=)'
display: 'OBSE v18+'

Group

Groups represent sets of plugins, and are a way to concisely and extensibly load sets of plugins after other sets of plugins.

This structure can be used to hold group definitions. It is a key-value map.

name

string

Required. A case-sensitive name that identifies the group.

description

string

A description of the group, e.g. what sort of plugins it contains. If undefined, the description is an empty string.

after

string set

The names of groups that this group loads after. Group names are case-sensitive. If undefined, the set is empty. The named groups must be defined when LOOT sorts plugins, but they don’t need to be defined in the same metadata file.

Sorting errors will occur if:

  • A group loads after another group that does not exist.

  • Group loading is cyclic (e.g. A loads after B and B loads after A).

Merging Groups

When a group definition for an already-defined group is encountered, the description field is replaced if the new value is not an empty string, and the after sets of the two definitions are merged.

The default Group

There is one predefined group named default that all plugins belong to by default. It is defined with an empty after set, as no other predefined groups exist for it to load after.

Like any other group, the default group can be redefined to add group names to its after set.

Equality

Two group data structures are equal if the values of their name keys are identical.

Examples

# Create a group for map marker plugins that loads after the predefined
# 'default' group.
name: 'Map Markers'
description: 'A group for map marker plugins that need to load late.'
after:
  - 'default'
# Extend the predefined 'default' group to load after an 'Unofficial Patches'
# group that is defined elsewhere.
name: 'default'
after:
  - 'Unofficial Patches'

Localised Content

The localised content data structure is a key-value string map.

text

Required. The actual message content string.

lang

Required. The language that text is written in, given as a code of the form ll or ll_CC, where ll is an ISO 639-1 language code and CC is an ISO 3166 country code. For example,

Language

Code

Brazilian Portuguese

pt_BR

Chinese

zh_CN

Danish

da

English

en

Finnish

fi

French

fr

German

de

Korean

ko

Polish

pl

Russian

ru

Spanish

es

Swedish

sv

Message

Messages are given as key-value maps.

type

string

Required. The type string can be one of three keywords.

say

A generic message, useful for miscellaneous notes.

warn

A warning message, describing a non-critical issue with the user’s mods (eg. dirty mods).

error

An error message, decribing a critical installation issue (eg. missing masters, corrupt plugins).

content

string or localised content list

Required. Either simply a string, or a list of localised content data structures. If the latter, one of the structures must be for English.

condition

string

A condition string that is evaluated to determine whether the message should be displayed: if it evaluates to true, the message is displayed, otherwise it is not. See Condition Strings for details.

subs

string list

A list of strings to be substituted into the message content string. The content string must use numbered specifiers (%1%, %2%, etc.), where the numbers correspond to the position of the substitution string in this list to use, to denote where these strings are to be substituted.

Message Formatting

LOOT supports formatting of messages using GitHub Flavored Markdown. Support is provided by the Marked library (v0.3). Strings that get substituted into messages, such as file display names and cleaning data utility strings, also support the same formatting options.

Language Support

If a message’s content value is a string, the message will use the string as its content if displayed. Otherwise, the first localised content structure with a language that matches LOOT’s current language will be used as the message’s content if displayed. If there are no matches, then the first structure in English will be used.

Equality

The equality of two message data structures is determined by comparing the values of their content keys. If a content key is a string, it is treated as a localised content list containing a single English-language string. The two message data structures are then equal if their localised content lists are identical.

Examples

Translations by Google

type: say
condition: 'file("foo.esp")'
content:
  - lang: en
    text: 'An example link: <http://www.example.com>'
  - lang: ru
    text: 'Это пример ссылки: <http://www.example.com>'

would be displayed as

отмечать: Это пример ссылки: http://www.example.com

if the current language was Russian and foo.esp was installed, while

type: say
content: 'An alternative [example link](http://www.example.com), with no translations.'

would be displayed as

отмечать: An alternative example link, with no translations.

In English,

type: say
content: 'A newer version of %1% [is available](%2%).'
subs:
  - 'this plugin'
  - 'http://www.example.com'

would be displayed as

Note: A newer version of this plugin is available.

Location

This data structure is used to hold information on where a plugin is hosted online. It has two forms: a key-value string map and a scalar string.

Map Form

link

Required. A URL at which the plugin is found.

name

A descriptive name for the URL, which may be used as hyperlink text. If undefined, defaults to an empty string.

Scalar Form

The scalar form is simply the value of the map form’s link key. Using the scalar form is equivalent to using the map form with an undefined name key.

Equality

Two location data structures are equal if the values of their link keys are identical.

Examples

Scalar form:

'http://skyrim.nexusmods.com/mods/19/'

Map form:

link: 'https://steamcommunity.com/sharedfiles/filedetails/?id=419668499'
name: 'Unofficial Skyrim Patch on Steam Workshop'

Cleaning Data

This structure holds information on which versions of a plugin are dirty or clean, and if dirty, how many identical-to-master records, deleted records and deleted navmeshes (if applicable) it contains. Cleaning data is given as a key-value map.

crc

hexadecimal integer

Required. The CRC-32 checksum of the plugin. If the plugin is dirty, this needs to be the CRC of the plugin before before cleaning. LOOT displays the CRCs of installed plugins in its report. The 8-character CRC should be preceded by 0x so that it is interpreted correctly.

util

string

Required. The utility that was used to check the plugin for dirty edits. If available, the version of the utility used should also be included (e.g. TES5Edit v3.11).

info

string or localised content list

A message that will be displayed to the user. If a localised content list is provided, one of the structures must be for English. This is only used if the plugin is dirty, and is intended for providing cleaning instructions to the user. If undefined, defaults to an empty string.

itm

integer

The number of identical-to-master records reported for the dirty plugin. If undefined, defaults to zero.

udr

integer

The number of undeleted records reported for the dirty plugin. If undefined, defaults to zero.

nav

integer

The number of deleted navmeshes reported for the dirty plugin. If undefined, defaults to zero.

Equality

Two cleaning data structures are equal if the values of their crc keys are identical.

Examples

A dirty plugin:

crc: 0x3DF62ABC
util: '[TES5Edit](http://www.nexusmods.com/skyrim/mods/25859) v3.1.1'
info: 'A cleaning guide is available [here](http://www.creationkit.com/index.php?title=TES5Edit_Cleaning_Guide_-_TES5Edit).'
itm: 4
udr: 160

A clean plugin:

crc: 0x2ABC3DF6
util: '[TES5Edit](http://www.nexusmods.com/skyrim/mods/25859) v3.1.1'

Plugin

This is the structure that brings all the others together, and forms the main component of a metadata file. It is a key-value map.

name

string

Required. Can be an exact plugin filename or a regular expression plugin filename. If the filename contains any of the characters :\*?|, the string will be treated as a regular expression, otherwise it will be treated as an exact filename. For example, Example\.esm will be treated as a regular expression, as it contains a \ character.

Regular expression plugin filenames must be written in modified ECMAScript syntax.

enabled

boolean

Enables or disables use of the plugin object. Used for user rules, but no reason to use it in the masterlist. If unspecified, defaults to true.

group

string

The name of the group the plugin belongs to. If unspecified, defaults to default.

The named group must be exist when LOOT sorts plugins, but doesn’t need to be defined in the same metadata file. If at sort time the group does not exist, a sorting error will occur.

The plugin must load after all the plugins in the groups its group is defined to load after, resolving them recursively. An exception exists if doing so would introduce a cyclic dependency between two plugins without any other group loading rules applied.

For example, if for plugins A.esp, B.esp, C.esp and D.esp:

  • B.esp has A.esp as a master

  • A.esp is in group A

  • B.esp and C.esp are in the default group

  • D.esp is in group D

  • group A loads after the default group

  • the default group loads after group D

Then the load order must be D.esp, C.esp, A.esp, B.esp. Although A.esp’s group must load after B.esp’s group, this would cause a cycle between A.esp and B.esp, so the requirement is ignored for that pair of plugins.

However, if for plugins A.esp, B.esp and C.esp in groups of the same names:

  1. group B loads after group A

  2. group C loads after group B

  3. A.esp has C.esp as a master

This will cause a sorting error, as neither group rule introduces a cyclic dependency when combined in isolation with the third rule, but having all three rules applied causes a cycle.

after

file set

Plugins that this plugin must load after, but which are not dependencies. Used to resolve specific compatibility issues. If undefined, the set is empty.

req

file set

Files that this plugin requires to be present. This plugin will load after any plugins listed. If any of these files are missing, an error message will be displayed. Intended for use specifying implicit dependencies, as LOOT will detect a plugin’s explicit masters itself. If undefined, the set is empty.

inc

file set

Files that this plugin is incompatible with. If any of these files are present, an error message will be displayed. If undefined, the set is empty.

msg

message list

The messages attached to this plugin. The messages will be displayed in the order that they are listed. If undefined, the list is empty.

tag

tag set

Bash Tags suggested for this plugin. If a Bash Tag is suggested for both addition and removal, the latter will override the former when the list is evaluated. If undefined, the set is empty.

url

location set

An unordered set of locations for this plugin. If the same version can be found at multiple locations, only one location should be recorded. If undefined, the set is empty. This metadata is not currently used by LOOT.

dirty

cleaning data set

An unordered set of cleaning data structures for this plugin, identifying dirty plugins.

clean

cleaning data set

An unordered set of cleaning data structures for this plugin, identifying clean plugins. The itm, udr and nav fields are unused in this context, as they’re assumed to be zero.

Equality

The equality of two plugin data structures is determined by comparing the values of their name keys.

  • If neither or both values are regular expressions, then the plugin data structures are equal if the lowercased values are identical.

  • If one value is a regular expression, then the plugin data structures are equal if the other value is an exact match for it.

Merging Behaviour

Key

Merge Behaviour (merging B into A)

name

Not merged.

enabled

Replaced by B’s value.

group

Replaced by B’s value.

after

Merged. If B’s file set contains an item that is equal to one already present in A’s file set, B’s item is discarded.

req

Merged. If B’s file set contains an item that is equal to one already present in A’s file set, B’s item is discarded.

inc

Merged. If B’s file set contains an item that is equal to one already present in A’s file set, B’s item is discarded.

msg

Merged. If B’s message list contains an item that is equal to one already present in A’s message list, B’s item is discarded.

tag

Merged.If B’s tag set contains an item that is equal to one already present in A’s tag set, B’s item is discarded.

url

Merged. If B’s location set contains an item that is equal to one already present in A’s location set, B’s item is discarded.

dirty

Merged.If B’s dirty data set contain an item that is equal to one already present in A’s dirty data set, B’s item is discarded.

clean

Merged. If B’s clean data set contain an item that is equal to one already present in A’s clean data set, B’s item is discarded.

Examples

name: 'Oscuro''s_Oblivion_Overhaul.esm'
req:
  - 'Oblivion.esm'  # Don't do this, Oblivion.esm is a master of Oscuro's_Oblivion_Overhaul.esm, so LOOT already knows it's required.
  - name: 'example.esp'
    display: '[Example Mod](http://www.example.com)'
    condition: 'version("Oscuro''s_Oblivion_Overhaul.esm", "15.0", ==)'
tag:
  - Actors.Spells
  - Graphics
  - Invent
  - Relations
  - Scripts
  - Stats
  - name: -Relations
    condition: 'file("Mart''s Monster Mod for OOO.esm") or file("FCOM_Convergence.esm")'
msg:
  - type: say
    content: 'Do not clean. "Dirty" edits are intentional and required for the mod to function.'

Condition Strings

Condition strings can be used to ensure that data is only acted on by LOOT under certain circumstances. They are very similar to boolean conditional expressions in programming languages such as Python, though more limited.

Omitting optional parentheses (see below), their EBNF grammar is:

compound_condition ::=  condition, { ( logical_and | logical_or ), condition }
condition          ::=  [ logical_not ], function
logical_and        ::=  "and"
logical_or         ::=  "or"
logical_not        ::=  "not"

Types

file_path

A double-quoted file path, or "LOOT", which references the LOOT executable being run.

regular_expression

A double-quoted file path, with a regular expression in place of a filename. The path must use / for directory separators, not \. The regular expression must be written in a modified Perl syntax.

Only the filename path component will be evaluated as a regular expression. For example, given the regex file path Meshes/Resources(1|2)/(upperclass)?table.nif, LOOT will look for a file named table.nif or upperclasstable.nif in the Meshes\Resources(1|2) folder, rather than looking in the Meshes\Resources1 and Meshes\Resources2 folders.

checksum

A string of hexadecimal digits representing an unsigned integer that is the data checksum of a file. LOOT displays the checksums of plugins in its user interface after running.

version

A double-quoted string of characters representing the version of a plugin or executable. LOOT displays the versions of plugins in its user interface after running.

comparison_operator

One of the following comparison operators.

==

Is equal to

!=

Is not equal to

<

Is less than

>

Is greater than

<=

Is less than or equal to

>=

Is greater than or equal to

Functions

There are several conditions that can be tested for using the functions detailed below. All functions return a boolean. For functions that take a path or regex, the argument is treated as regex if it contains any of the characters :\*?|.

file(file_path path)

Returns true if path is installed, and false otherwise.

file(regular_expression regex)

Returns true if a file matching regex is found, and false otherwise.

active(file_path path)

Returns true if path is an active plugin, and false otherwise.

active(regular_expression regex)

Returns true if an active plugin matching regex is found, and false otherwise.

many(regular_expression regex)

Returns true if more than one file matching regex is found, and false otherwise.

many_active(regular_expression regex)

Returns true if more than one active plugin matching regex is found, and false otherwise.

checksum(file_path path, checksum expected_checksum)

Returns true if the calculated CRC-32 checksum of path matches expected_checksum, and false otherwise. Returns false if path does not exist.

version(file_path path, version given_version, comparison_operator comparator)

Returns true if the boolean expression:

actual_version comparator given_version

(where actual version is the version read from path) holds true, and false otherwise. If path is a plugin, its version is read from its description field. If path is not a plugin, it will be assumed to be an executable (e.g. *.exe or *.dll), and its version is read from its File Version field. If path does not exist or does not have a version number, its version is assumed to be 0. If path isn’t a plugin or an executable, an error will occur.

The comparison uses the precedence rules defined by Semantic Versioning, extended to allow leading zeroes, an arbitrary number of release version numbers, case-insensitivity and a wider range of separator characters.

product_version(file_path path, version given_version, comparison_operator comparator)

Returns true if the boolean expression:

actual_version comparator given_version

(where actual version is the version read from path) holds true, and false otherwise. path must be an executable (e.g. *.exe or *.dll), and its version is read from its Product Version field. If path does not exist or does not have a version number, its version is assumed to be 0. If path is not an executable, an error will occur.

The comparison uses the precedence rules defined by Semantic Versioning, extended to allow leading zeroes, an arbitrary number of release version numbers, case-insensitivity and a wider range of separator characters.

Logical Operators

The and, or and not operators have their usual definitions, except that the not operator only ever operates on the result of the function immediately following it.

Order of Evaluation

Condition strings are evaluated according to the usual C-style operator precedence rules, and parentheses can be used to override these rules. For example:

function and function or not function

is evaluated as:

( function and function ) or ( not function )

but:

function and ( function or not function )

is evaluated as:

function and ( function or ( not function ) )

Parentheses cannot be used between a not operator and the function following it.

Performance

LOOT caches the results of condition evaluations. A regular expression check will still take longer than a file check though, so use the former only when appropriate to do so.

Version History

The version history of the metadata syntax is given below.

0.14 - 2018-12-09

Added

  • The Group data structure now has a description key that takes a string value.

  • The condition function product_version(file_path path, version given_version, comparison_operator comparator), which checks against the Product Version field of an executable.

Changed

  • clean and dirty metadata are now allowed in regex plugin entries.

  • Location, Message, MessageContent and Tag equality comparisons are now case-sensitive.

  • Regular expressions in condition strings now use a modified Perl grammar instead of a modified ECMAScript grammar. Plugin object name fields still use the modified ECMAScript grammar for regex values. To improve portability and avoid mistakes, it’s best to stick to using the subset of regular expression features that are common to both grammars.

Removed

  • The change in regular expression grammar means that the following regular expression features are no longer supported in condition strings:

    • \c<letter> control code escape sequences, use \x<hex> instead

    • The \0 null escape sequence, - use \x00 instead

    • The [:d:], [:w:] and [:s:] character classes, use [:digit:], [:alnum:] and [:space:] instead respectively.

    • \<number> backreferences

    • (?=<subpattern>) and (?!<subpattern>) positive and negative lookahead

0.13 - 2018-04-02

Added

  • The Group data structure.

  • The groups list to the root of the metadata file format.

  • The group key to the plugin data structure.

Removed

  • The priority field from the plugin data structure.

  • The global_priority field from the plugin data structure.

0.10 - 2016-11-06

Added

  • The clean key to the plugin data structure.

  • The global_priority field to the plugin data structure.

  • The many_active() condition function.

  • The info key to the cleaning data structure.

Changed

  • Renamed the str key in the localised content data structure to text .

  • The priority field of the plugin data structure now stores values between -127 and 127 inclusive.

  • Regular expressions no longer accept \ as a directory separator: / must now be used.

  • The file() condition function now also accepts a regular expression.

  • The active() condition function to also accept a regular expression.

  • Renamed the dirty info data structure to the cleaning data structure.

Removed

  • The regex() condition function, as it has been obsoleted by the file() function’s new regex support.

0.8 - 2015-07-22

Added

  • The name key to the location data structure.

  • The many("regex") condition function.

  • The documentation now defines the equality criteria for all of the metadata syntax’s non-standard data structures.

Changed

  • Detection of regular expression plugin entries. Previously, a plugin entry was treated as having a regular expression filename if the filename ended with \.esp or \.esp . Now, a plugin entry is treated as having a regular expression filename if the filename contains one or more of :\*?| .

Removed

  • Removed the ver key in the location data structure.

Fixed

  • The documentation gave the values of the after , req , inc , tag , url and dirty keys as lists, when they have always been sets.

0.7 - 2015-05-20

Added

  • The message string substitution key, i.e. sub , in the message data structure.

  • Support for YAML merge keys, i.e. << .

Changed

  • Messages may now be formatted using most of GitHub Flavored Markdown, minus the GitHub-specific features (like @mentions, issue/repo linking and emoji).

0.6 - 2014-07-05

No changes.

0.5 - 2014-03-31

Initial release.