***************** 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: .. productionlist:: expression: condition, { logical_or, compound_condition } compound_condition: condition, { logical_and, condition } condition: ( [ logical_not ], function ) | ( [ logical_not ], "(", expression, ")" ) logical_and: "and" logical_or: "or" logical_not: "not" .. _EBNF: https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form Spaces, tabs, carriage returns and line feeds (i.e. line breaks) can be used on either side of: - ``logical_and``, as defined above - ``logical_or``, as defined above - ``logical_not``, as defined above - parentheses around conditions and expressions - commas separating function arguments Types ===== .. describe:: filesystem_path A double-quoted filesystem path. .. describe:: file_path A double-quoted file path. .. describe:: file_size A string of decimal digits representing an unsigned integer number of bytes. .. describe:: regular_expression A double-quoted regular expression that is case-insensitive and Unicode-aware. .. note:: The regular expression is currently expected to be written in a `modified Perl `_ syntax. The specific syntax and features supported are an implementation detail that may change without changing the masterlist syntax version, so it's best to restrict regexes to the common subset that is supported across several regex flavours and implementations. .. describe:: regular_expression_path A double-quoted file path, with a regular expression in place of a filename. The path must use ``/`` for directory separators, not ``\``. The filename regular expression is prefixed with ``^`` and suffixed with ``$`` before evaluation to ensure that it only matches whole filenames. 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. .. describe:: 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. .. describe:: 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. .. describe:: comparison_operator One of the following comparison operators. .. describe:: == Is equal to .. describe:: != Is not equal to .. describe:: < Is less than .. describe:: > Is greater than .. describe:: <= Is less than or equal to .. describe:: >= 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 ``:\*?|``. .. describe:: file(filesystem_path path) Returns true if ``path`` is installed, and false otherwise. .. describe:: file(regular_expression_path regex) Returns true if a file matching ``regex`` is found, and false otherwise. .. describe:: file_size(file_path path, file_size size) Returns true if the file at the given ``path`` has the given ``size``, and false otherwise. .. describe:: readable(filesystem_path path) Returns true if ``path`` is a readable directory or file, and false otherwise. This is particularly useful when writing conditions for games that are available from the Microsoft Store and/or Xbox app, as games installed using them have executables that have heavily restricted permissions, and attempts to read them result in permission denied errors. You can use this function to guard against such errors by calling it before the ``checksum``, ``version`` or ``product_version`` functions. .. describe:: active(file_path path) Returns true if ``path`` is an active plugin, and false otherwise. .. describe:: active(regular_expression_path regex) Returns true if an active plugin matching ``regex`` is found, and false otherwise. .. describe:: many(regular_expression_path regex) Returns true if more than one file matching ``regex`` is found, and false otherwise. .. describe:: many_active(regular_expression_path regex) Returns true if more than one active plugin matching ``regex`` is found, and false otherwise. .. describe:: is_master(file_path path) Returns true if ``path`` is an installed master plugin, and false otherwise. This returns false for all OpenMW plugins, as OpenMW does not force master plugins to load before others. .. describe:: is_executable(file_path path) Returns true if ``path`` is a Windows executable (PE) file, and false otherwise. .. describe:: 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. .. describe:: version(file_path path, comparison_operator comparator, version given_version) 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, the function returns false. * If ``path`` is not readable or is not a plugin or an executable, an error will occur. The supported version syntax and precedence rules are detailed in the section below. .. describe:: version(file_path path, version given_version, comparison_operator comparator) An older, deprecated form of ``version(file_path path, comparison_operator comparator, version given_version)``. .. describe:: product_version(file_path path, comparison_operator comparator, version given_version) 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, the function returns false. * If ``path`` is not a readable executable, an error will occur. The supported version syntax and precedence rules are detailed in the section below. .. describe:: product_version(file_path path, version given_version, comparison_operator comparator) An older, deprecated form of ``product_version(file_path path, comparison_operator comparator, version given_version)``. .. describe:: filename_version(regular_expression_path path, comparison_operator comparator, version given_version) The regex in ``path`` must contain a single capturing group. Returns true if a file matching ``path`` is found for which the boolean expression:: actual_version comparator given_version (where ``actual_version`` is the value captured by the regex) holds true, and false otherwise. If the contents of the parent directory of the filename component of ``path`` can't be read, an error will occur. If the function can't find a version to compare against the given file path (e.g. because there is no matching filename in the parent path), it always returns false. .. describe:: filename_version(regular_expression_path path, version given_version, comparison_operator comparator) An older, deprecated form of ``filename_version(regular_expression_path path, comparison_operator comparator, version given_version)``. .. describe:: description_contains(file_path path, regular_expression regex) Returns true if ``path`` is a plugin file with a description that contains text that matches ``regex``, and false otherwise (including if the path does not exist, is not a plugin, or has no description). Version Syntax & Comparison Rules --------------------------------- Version parsing and comparison is compatible with `Semantic Versioning `_, with the following exceptions: * Pre-release identifiers may not include hyphens (``-``), as they are treated as separators. For example, a SemVer-compliant parser would treat ``1.0.0-alpha.1.x-y-z.--`` as ``([1, 0, 0], ["alpha", 1, "x-y-z", "--"])`` but libloot treats it as ``([1, 0, 0], ["alpha", 1, "x", "y", "z", "", ""])``. * Identifiers that contain non-digit characters are lowercased before being compared lexically, so that their comparison is case-insensitive instead of case-sensitive. For example, SemVer specifies that ``1.0.0-alpha`` is greater than ``1.0.0-Beta``, but libloot compares them with the opposite result. These exceptions are necessary to support an extended range of real-world versions that do not conform to SemVer. The supported extensions are: * Leading zeroes are allowed and ignored in major, minor and patch version numbers and numeric pre-release IDs. For example, ``01.02.03`` and ``1.2.3`` are equal. * An arbitrary number of version numbers is allowed. To support this, the major, minor and patch version numbers are treated as a sequence of numeric release IDs, and any subsequent version numbers are just additional release IDs that get appended to the sequence. For example, ``1.2.3`` may be represented as the sequence ``[1, 2, 3]``, and ``1.2.3.4`` would be represented as ``[1, 2, 3, 4]``. If two versions with a different number of release identifiers are compared, the version with fewer release identifiers is padded with zero values until they are the same length. Each release identifier in one version is then compared against the release identifier in the same position in the other version. For example, ``1-beta`` is padded to ``1.0.0-beta`` before being compared against ``1.0.1-beta``, and the result is that ``1.0.1-beta`` is greater than ``1-beta``. * Release IDs may be separated by a period (``.``) or a comma (``,``). For example, ``1.2.3.4`` and ``1,2,3,4`` are equal. * The separator between release IDs and pre-release IDs may be a hyphen (``-``), a space (" "), a colon (``:``) or an underscore (``_``). For example, ``1.2.3-alpha``, ``1.2.3 alpha``, ``1.2.3:alpha`` and ``1.2.3_alpha`` are all equal. * Pre-release IDs may be separated by a period (``.``), a hyphen (``-``), a space (" "), a colon (``:``) or an underscore (``_``). For example, ``1.2.3-alpha.1``, ``1.2.3-alpha-1``, ``1.2.3-alpha 1``, ``1.2.3-alpha:1`` and ``1.2.3-alpha_1`` are all equal. * Non-numeric release IDs are allowed. A non-numeric release ID may contain any character (not just ASCII characters) that is not one of the separators listed above or a plus sign (``+``). For example, ``0.78b.1`` is allowed. Non-numeric release IDs use the same comparison rules as non-numeric pre-release IDs, with the exception that a non-numeric release ID is not always greater than a numeric release ID: * If the non-numeric release ID has no leading digits, it is greater than the numeric release ID. For example, ``1.A`` is greater than ``1.1``. * If the non-numeric release ID has leading digits, they are parsed as a number, and this is compared against the numeric release ID: * If the two numbers are equal then the non-numeric release ID is greater than the numeric release ID. For example, ``1.1A`` is greater than ``1.1``. * Otherwise, the result of comparing the two numbers is used as the result of comparing the two release IDs. For example, ``1.2`` is greater than ``1.1A`` and ``1.1A`` is greater than ``1.0``. * Pre-release IDs may contain any character (not just ASCII characters) that is not one of the pre-release ID separators listed above or a plus sign (``+``). * Before non-numeric IDs (release or pre-release) are compared, they are lowercased according to Unicode's lowercasing rules. * As a special case, version strings that are four comma-and-space-separated sequences of digits are interpreted as if the comma-and-space separators were periods (``.``). For example, ``0, 2, 0, 12`` and ``0.2.0.12`` are equal. Logical Operators ================= The ``and``, ``or`` and ``not`` operators have their usual definitions. 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 ) ) 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.