fomod

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2026 License: MIT Imports: 16 Imported by: 0

README

go-fomod

Go


A reader and evaluator for FOMOD installers: parses ModuleConfig.xml, evaluates its conditions and turns choices into the file copy list. Package fomod, module github.com/Rethunk-Tech/go-fomod.

It copies no files, lays nothing out on disk and never looks at the game folder: the caller hands it an fs.FS and the installed game's file states. Nexus archives ship configs in UTF-8, UTF-16 and Windows-1252 whatever they declare, so the parser decides the encoding from the bytes and ignores the declaration.

Quick start

go get github.com/Rethunk-Tech/go-fomod

A worked example and the contracts: HUMANS.md.

Features

  • Open and Find locate and parse fomod/ModuleConfig.xml in any fs.FS, at the root or one folder down.
  • Parse reads UTF-8, UTF-16 and Windows-1252 configs, with or without a BOM or a correct declaration.
  • Resolve turns choices into the copy list, sorted by priority; Match, Unanswered and Keep replay recorded choices.
  • Steps, groups and plugins come back in their declared order, already sorted.
  • VisibleSteps, FlagsFrom and PluginType give a wizard what to show under the current answers.
  • The config read is capped, entities are never expanded and nesting is bounded.

Documentation

Doc Audience
HUMANS.md Running and using the package
AGENTS.md Internals, invariants, and why
CONTRIBUTING.md Process — commits, tests, docs
SECURITY.md Vulnerability disclosure
CHANGELOG.md Release notes

License

MIT — see LICENSE. © 2026 Rethunk.Tech, LLC.

Documentation

Overview

Package fomod reads FOMOD installers: it parses fomod/ModuleConfig.xml, evaluates its flag, file and game-version conditions, and turns a set of choices into the file and folder copy operations the installer declares. It copies nothing and reads no disk beyond the fs.FS it is handed.

Index

Constants

View Source
const (
	SelectExactlyOne = "SelectExactlyOne"
	SelectAtMostOne  = "SelectAtMostOne"
	SelectAtLeastOne = "SelectAtLeastOne"
	SelectAll        = "SelectAll"
	SelectAny        = "SelectAny"
)
View Source
const (
	TypeRequired      = "Required"
	TypeOptional      = "Optional"
	TypeRecommended   = "Recommended"
	TypeNotUsable     = "NotUsable"
	TypeCouldBeUsable = "CouldBeUsable"
)
View Source
const (
	FileActive   = "Active"
	FileInactive = "Inactive"
	FileMissing  = "Missing"
)
View Source
const (
	OrderAscending  = "Ascending"
	OrderDescending = "Descending"
	OrderExplicit   = "Explicit"
)

Order values of the installSteps, optionalFileGroups and plugins order attribute. Parse has already applied it to the lists it returns: Explicit keeps document order, the others sort by name.

Variables

View Source
var ErrTooLarge = errors.New("fomod: config exceeds the read cap")

ErrTooLarge is returned by Open for a config larger than the read cap.

Functions

func Find

func Find(fsys fs.FS) (string, error)

Find returns the slash path, relative to fsys, of fomod/ModuleConfig.xml, or "" when there is none. The fomod folder may sit at the root or one folder down; names are matched case-insensitively, preferring an exact spelling.

func FlagsFrom

func FlagsFrom(cfg Config, choices Choices, ctx EvalContext) map[string]string

FlagsFrom returns condition flags set by the selected plugins, in step order.

func Match

func Match(cfg Config, choices Choices, ctx EvalContext) bool

Match reports whether choices still name plugins that exist and satisfy each visible group's type.

func PluginType

func PluginType(p Plugin, flags map[string]string, ctx EvalContext) string

PluginType is the effective type of a plugin under flags and file dependencies.

func Unanswered

func Unanswered(old, cfg Config, choices Choices, ctx EvalContext) bool

Unanswered reports whether cfg shows a group the user picks from that neither old nor choices knew, so replaying choices made against old would decide it without asking.

Types

type Choices

type Choices map[string]map[string][]string

Choices is install step name → group name → selected plugin names.

func Keep

func Keep(cfg Config, choices Choices) Choices

Keep returns the choices that still name a plugin of cfg. A step and group name that cfg repeats is kept once, so Keep is idempotent.

type Composite

type Composite struct {
	Operator string
	Files    []FileDep
	Flags    []FlagDep
	Games    []GameDep
	Nested   []Composite
}

func (Composite) Eval

func (c Composite) Eval(flags map[string]string, ctx EvalContext) bool

Eval reports whether the composite dependency holds.

func (*Composite) UnmarshalXML

func (c *Composite) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error

type Conditional

type Conditional struct {
	When  Composite
	Files []Op
}

type Config

type Config struct {
	ModuleName   string
	Required     []Op
	Steps        []Step
	StepOrder    string
	Conditionals []Conditional
}

Config is a parsed ModuleConfig.xml.

func Open

func Open(fsys fs.FS) (cfg Config, file string, ok bool, err error)

Open finds and parses the config in fsys. ok is false when there is none; file is the path Find returned. A config over the read cap fails with ErrTooLarge.

func Parse

func Parse(data []byte) (Config, error)

Parse reads the bytes of a ModuleConfig.xml document. The encoding comes from the bytes (decodeXML, then Windows-1252 for anything still not UTF-8); the declaration is ignored because real ones are often wrong.

type EvalContext

type EvalContext struct {
	// Files answers fileDependency checks; nil reports every file FileMissing.
	Files FileIndex
	// GameVersion is the installed game's version, compared with each gameDependency minimum by go-semverish: a missing
	// number counts as 0. "" means unknown, and any gameDependency is then unmet; a version either side that does not
	// parse counts as met.
	GameVersion string
}

EvalContext holds inputs for FOMOD dependency checks.

type FileDep

type FileDep struct {
	File  string
	State string
}

func (*FileDep) UnmarshalXML

func (f *FileDep) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error

type FileIndex

type FileIndex func(name string) string

FileIndex reports the state of a dependency file in the installed game: FileActive, FileInactive or FileMissing.

type FlagDep

type FlagDep struct {
	Flag  string
	Value string
}

func (*FlagDep) UnmarshalXML

func (f *FlagDep) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error

type FlagSet

type FlagSet struct {
	Name  string
	Value string
}

type GameDep

type GameDep struct {
	Version string
}

func (*GameDep) UnmarshalXML

func (g *GameDep) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error

type Group

type Group struct {
	Name    string
	Type    string
	Plugins []Plugin
	// PluginOrder is the order attribute of this group's plugins.
	PluginOrder string
}

type Op

type Op struct {
	Source string
	// Destination is nil when the config names none, which installs at the source's own path; "" is the install
	// folder itself.
	Destination *string
	Priority    int
	Folder      bool
	// Always and IfUsable install a plugin's file even when the plugin is not chosen, IfUsable only while the plugin's
	// type is not NotUsable.
	Always, IfUsable bool
}

Op is one file or folder the installer copies from the archive into the install root.

func Resolve

func Resolve(cfg Config, choices Choices, ctx EvalContext) []Op

Resolve lists file operations for required files, selected plugins, and matching conditional installs.

type Plugin

type Plugin struct {
	Name        string
	Description string
	Image       string
	Files       []Op
	Flags       []FlagSet
	TypeName    string
	DefaultType string
	Patterns    []TypePattern
}

type Step

type Step struct {
	Name    string
	Visible Composite
	Groups  []Group
	// GroupOrder is the order attribute of this step's optionalFileGroups.
	GroupOrder string
}

func VisibleSteps

func VisibleSteps(cfg Config, flags map[string]string, ctx EvalContext) []Step

VisibleSteps are install steps whose visible condition holds.

type TypePattern

type TypePattern struct {
	Type Composite
	Name string
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL