Skip to content

[dev] Fomod for Devs

Infernio edited this page Feb 7, 2022 · 2 revisions

This document aims to be a comprehensive guide on installing packages with the fomod format. It is divided into three sections, Structure, Metadata and Installation, with the Installation section further subdivided into stages that must be followed sequentially.

Structure

The mandatory structure for a fomod installation is quite simple. A basic example is shown here:

MyPackage
├── fomod
│   ├── info.xml
│   └── ModuleConfig.xml
├── example.plugin
└── readme.txt

In the root of MyPackage there must exist a folder named fomod and inside this folder there must exist two files, named info.xml and ModuleConfig.xml. Since Windows ignores filename case, it should also be ignored by the installer.

Everything else is up to the user, with no limitations on where plugins or data files are located, even within the fomod folder.

Metadata

All metadata for the installation should be collected from the info.xml file. It's recommended this collection be done alongside the installation process but it's not required it be done at all.

There is no official schema for this file, meaning there is no defined format. This means there may be tags and/or values that differ from the ones that are listed here. The possibility of adapting to the actual tags on info.xml is left to the installer authors discretion.

The most common tags present in info.xml are:

  • Name
  • Author
  • Version
  • Description
  • Website

The values are all in each element's text.

Installation

The installation procedure is entirely contained in the ModuleConfig.xml file and is governed by a schema. Everything is under a config tag, whose job is to serve as the root element and nothing else. This tag can have up to 6 child-tags, each one can be used at most once:

Only moduleName is required to always appear, but without at least some of the others the installer does nothing.

The moduleName tag is required and it's up to the installer's author whether to use this tag's text or info.xml's Name for the package's name. This tag also has the optional attributes colour (has a rbg 6-digit value, default 000000) and position (accepts Left, Right and RightOfImage, default Left).

The moduleImage tag is optional and it holds the package's main image. Possible examples of usage include a splash screen or as the background for the installer. It has the optional attributes path (the path to the image, relative from the package root), showImage (whether to show the image, accepts a boolean, default true), showFade (whether to show a fade effect, accepts a boolean, default true) and height (image's height, accepts positive and negative integers, negative integers mean adjusting height automatically, default -1).

Common "structures" to multiple tags and/or situations are going to be described in the following subsections. These can be skipped and read whenever they are relevant to the installation stage at the time.

Dependencies Check

The tags dependencies, visible and moduleDependencies all follow this structure.

This structure is essentially a tree of dependencies or conditions that evaluate to a boolean, according to it's type attribute.

If type is "And" (the default) then all top-level conditions need to evaluate to true for this structure to be true. If type is "Or" then only one top-level condition needs to be true for this to be true.

There are 6 different types of conditions avilable, they can apppear in any order and in any amount.

fileDependency checks the state of a file or folder in the installation folder. The file to check is in the file attribute and the state to compare against in in the state attribute. The possible states are:

  • Missing - file doesn't exist in the installation folder.
  • Inactive - file exists, but will not be loaded into the game.
  • Active - file exists and will be loaded into the game.

flagDependency checks the value of a flag. The flag name is in attribute flag and the value to compare against is in the attribute value.

gameDependency checks the game version (current game version should be equal or higher than this). The version to compare against is in the attribute version.

fommDependency checks the FOMM version (current FOMM version should be equal or higher than this). The version to compare against is in the attribute version.

Note that all mod managers besides FOMM can't really implement this, for obvious reasons. WB simply evaluates all fommDependency conditions to true.

foseDependency checks the script extender version (current SE version should be equal or higher than this). The version to compare against is in the attribute version.

The schema does not mention what should be done if there is no script extender for this game (e.g. Subnautica). In that situation, WB evaluates all foseDependency conditions to true.

dependencies, the last possible condition, is this struture, allowing for nested conditions.

File List

The tags requiredInstallFiles and files follow this structure.

This structure has no possible attributes and contains a list of files and/or folders to install to target directories. The possible children are file and folder. At least one of them must be present and they may be in any order.

Both file and folder tags have the exact same attributes:

  • source [required] - a path to a file or folder relative to the package's root.
  • destination - path to the target folder, relative to the installation folder's root. Default is empty.
  • alwaysInstall - a boolean, if true will always install regardless of user interaction. Default is false.
  • installIfUsable - a boolean, if true and structure is within a plugin, always install if plugin is not NotUsable. Default is false.
  • priority - accepts integers, default 0. A higher number indicates the file or folder should be installed AFTER the items with lower priorities.

Ordered List

The tags installSteps, optionalFileGroups and plugins follow this structure.

This structure is essentially just an ordered list of a given child tag. The child tag varies but their order is given by the order attribute, possible values are Ascending, Descending and Explicit, defaults to Ascending.

The children are ordered by their name attribute: alphabetically if Ascending, reverse alphabetical if Descending and whatever order their in in the xml file if Explicit.

Installation Requirements

The moduleDependencies tag holds a set of requirements to start the installer.

This tag has already been described in Dependencies Check. If these dependencies evaluate to true, continue with the installer, otherwise exit (preferably with a message to the user).

Pre-GUI

The requiredInstallFiles tag is a File List, which files are always installed regardless of the user's choices in the GUI.

GUI

The installSteps tag is the only portion of the installer that needs user interaction and, therefore, a GUI. This is an Ordered List of installer pages, or steps (installStep).

Each installStep should therefore be treated as single page in the installer. To know whether it should be rendered (or even parsed) it may have the visible child tag - this is a Dependencies Check and this page should be shown to the user whenever it evaluates true.

Since installStep is the child of an Ordered List, it has a name attribute - it's a simple string that could also be used for a page title.

Lastly, it has the optionalFileGroups child - another Ordered List, this time of groups. These are a subdivision of the choices whithin page, they're quite useful for having a user make several choices in a single page.

group, expectedly, has a name attribute similar to installStep's. It also has a type attribute, whose value determines the type of choice the user has to make in this group. They're mostly self-explanatory:

  • SelectAtLeastOne - checkboxes are useful, make sure the user has selected at least one.
  • SelectAtMostOne - more checkboxes, add some logic to uncheck all other boxes on selection.
  • SelectExactlyOne - radio buttons are your friends.
  • SelectAll - No need to give the user any choice here, auto select and lock all checkboxes.
  • SelectAny - checkboxes with no restrictions.

Finally, group has the plugins child - one last Ordered List, this time of, you guessed it, plugins. These are the choices available to the user and there are many useful and interesting children here.

Let's get the attribute out of the way, name, same deal here. So, first child, description, its text is the choice's description. image, optional, has an attribute path that contains the relative path (from the package's root) to the image associated with the choice. files, a File List. And now, conditionFlags.

Remember the flags mentioned in flagDependency back there in Dependencies Check? This is where you set them. It has the flag children, where the attribute name (a string) provides the flag's name and the text provides the value. An empty text is also valid (unsets the flag). Be sure to store the flags and values globally, they'll also be used later in Post-Gui.

At least one of either files or conditionFlags should be present for the choice to matter (otherwise even if the user chooses it, nothing happens).

typeDescriptor, where it's decided the type of the choice, such as whether the user even has a choice (it could be unusable or required). The possible types are:

  • Required - this choice MUST be made. It's pre-selected and the user can't unselect it.
  • Optional - the regular variety, choice is up to the user.
  • Recommended - it's pre-selected, but user can unselect. It's left up to the implementation the behaviour in case there are multiple recommended in an SelectExactlyOne group.
  • NotUsable - user cannot choose this, lock this choice.
  • CouldBeUsable - choosing this should give the user a warning to check whether or not this option is safe to use.

It may have one of two children:

  • type - the thankfully simple one, has the attribute name which straightforwardly sets the type.
  • dependencyType - here the choice's type can be decided via patterns.

On dependencyType choices, the author doesn't set the type directly but has it depend on a number of factors. The two children this tag has are defaultType, where you set the type (exactly like in type) to be used when no pattern matches, and patterns, which is just a list of patterns.

Each of these patterns has two children: dependencies, a Dependencies Check, and type, same as the type before, which is the type to set for this choice when dependencies evaluates to true. You should take this list of patterns explicitly and go down the list, evaluate each and set the type to the first one that matches.

All that's all for the GUI stage of the installer. For a quick recap:

  1. Get the page list and order them;
  2. For each page:
    1. Check if it should be rendered;
    2. If so:
      1. Get the page name;
      2. Get the group list and order them.
  3. For each group:
    1. Get the name and type;
    2. Get the choice list and order them.
  4. For each choice:
    1. Get the name, description and image;
    2. Get the type;
    3. If the choice is selectable, get the files to install and the flags to (un)set.
  5. At the end of this stage, you should have a list of files/folders to install and flag/value key-value pairs.

Post-GUI

Once the user has made their choices, the last stage is with the conditionalFileInstalls. This stage will install files based on multiple conditions and works essentially like a matrix.

The conditionalFileInstalls tag requires the patterns tag which holds an explicit list of pattern children. Much like with typeDescriptor, the installer should go through each of these patterns in order, try to match their dependencies child (a Dependencies Check) and if it evaluates to true, install the files in the files child (a File List).

Clone this wiki locally