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:
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"
Types
- filesystem_path
A double-quoted filesystem path, or
"LOOT"
, which resolves to LOOT.exe in the current working directory. Bear in mind that LOOT.exe may not be present if the condition is being evaluated by an application other than LOOT.
- file_path
A double-quoted file path, or
"LOOT"
, which resolves to LOOT.exe in the current working directory. Bear in mind that LOOT.exe may not be present if the condition is being evaluated by an application other than LOOT.
- 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 namedtable.nif
orupperclasstable.nif
in theMeshes\Resources(1|2)
folder, rather than looking in theMeshes\Resources1
andMeshes\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(filesystem_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.
- 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
orproduct_version
functions.
- 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.
- is_master(file_path path)
Returns true if
path
is an installed master plugin, and false otherwise.
- checksum(file_path path, checksum expected_checksum)
Returns true if the calculated CRC-32 checksum of
path
matchesexpected_checksum
, and false otherwise. Returns false ifpath
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 frompath
) 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 condition evaluates to true for the!=
,<
and<=
comparators, i.e. a missing version is always less than the given version.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.
- 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 frompath
) 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 condition evaluates to true for the!=
,<
and<=
comparators, i.e. a missing version is always less than the given version.If
path
is not a readable executable, an error will occur.
The supported version syntax and precedence rules are detailed in the section below.
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 treat1.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 than1.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
and1.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]
, and1.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 to1.0.0-beta
before being compared against1.0.1-beta
, and the result is that1.0.1-beta
is greater than1-beta
.Release IDs may be separated by a period (
.
) or a comma (,
). For example,1.2.3.4
and1,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
and1.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
and1.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 than1.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 than1.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 than1.1A
and1.1A
is greater than1.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
and0.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.