Skip to content
openmw-config

Library active

openmw-config

Read, compose and write OpenMW's openmw.cfg chains from Rust, or from Lua embedded in a Rust host.

cargo add openmw-config
Stable
3.0.2 2026-09-30
Package
Rust crate
License
MIT OR Apache-2.0

Overview

OpenMW does not read one openmw.cfg. It reads a chain of them: a root config beside the executable or in the system’s config directory, which points with config= at the user’s, which may point further. Each file adds data directories, content files and fallback settings, and any of them can replace= what the files before it said. Tools that open the user’s openmw.cfg directly see only the last link, and get the load order wrong on every packaged install.

openmw-config reads the chain the way OpenMW does, and gives you the result as one configuration you can inspect, change and save. Every setting remembers the file it came from and the comments above it, so saving writes each change back to the file that owns it and leaves the rest of the chain alone.

Every file OpenMW reads, as one configuration

The crate walks the same chain OpenMW does, keeps track of which file said what, and writes changes back to the file that owns them.

  1. source

    Root openmw.cfg

    Beside the executable, or the platform's global config. A package's root usually ends with config="?userconfig?".

  2. tool

    The chain

    config= entries load depth first, as OpenMW's loader walks them. replace= discards what came before, tokens resolve, relative paths anchor to their own file.

  3. result

    OpenMWConfiguration

    Content files, data directories, archives, fallback settings and unknown keys, each tagged with its source file and comment.

source

User openmw.cfg

The last file loaded: the one the launcher edits, and the one save_user() writes.

service

Lua scripts

A Rust host registers openmwConfig, and scripts get the same model through a camelCase API.

How the pieces relate
  • Root openmw.cfg The chain
  • The chain OpenMWConfiguration
  • OpenMWConfiguration save_user() User openmw.cfg
  • OpenMWConfiguration create_lua_module Lua scripts

What it does#

  • Loads like OpenMW. Root discovery through OPENMW_CONFIG, OPENMW_CONFIG_DIR, the executable’s directory and the global config; config= depth first, as OpenMW’s loader walks it; every replace= form; the ?local?, ?global?, ?userdata? and ?userconfig? tokens, Flatpak included.
  • Saves without damage. Comments, unknown keys and each value’s own spelling survive a round trip. Every write goes to a temporary file beside the target, which then replaces it.
  • Exports for importers. A flattened copy of the whole chain, with resolved paths and no chain entries, that means the same thing wherever you write it.
  • Speaks Luau. Behind the luau feature, a Rust host composes an l3i extension into its runtime plan and scripts get the same configuration through a camelCase API, typed.
  • Tells you where it went wrong. Parse errors name the file and line; config_chain() lists every file it loaded and every config= target it skipped.
use openmw_config::{ConfigError, OpenMWConfiguration};

fn main() -> Result<(), ConfigError> {
    let mut config = OpenMWConfiguration::from_env()?;

    for plugin in config.content_files_iter() {
        println!("{}", plugin.value());
    }

    config.add_content_file("Better Balmora.esp")?;
    config.save_user()
}

Documentation#

This site is the crate’s documentation, its API reference included.

  • Start here: add the crate, load the chain, change it and save it.
  • Guide: loading, how chains resolve, editing, saving, paths, and embedding Luau.
  • Rust API and Lua API: every type, method and function, with what it does to your files.

openmw-config models openmw.cfg only; settings.cfg is not handled yet. It is not affiliated with the OpenMW project.

Install

Rust crate · 3.0.2

Add it with Cargofrom crates.io, 3.0.2 today

From your project's directory:

cargo add openmw-config

Or by hand, in Cargo.toml:

[dependencies]
openmw-config = "3.0.2"

Cargo reads a bare version as "this or any compatible newer release" and records the exact one in Cargo.lock.

From the repositorythe default branch, ahead of the latest release
[dependencies]
openmw-config = { git = "https://github.com/DreamWeave-MP/Openmw_Config" }

Cargo pins the commit it fetched in Cargo.lock; cargo update -p openmw-config moves it forward.

Compatibility

Anything not listed here is untested.

Works better with

  • vfstool_lib not on the network

    Builds OpenMW's virtual file system from the data directories this crate resolves.

  1. 3.0.2 stable 2026-09-30

    Built on l3i 1.1.0.

    Changed

    • l3i 1.1.0 comes from crates.io; the @dream/openmw-config module is unchanged.
    openmw-config 3.0.2 104.83 kB · crates.io
    View on crates.io
    Verify · sha256 a8982aa239db…ea3d914e
    File
    openmw-config-3.0.2.crate
    Size
    107346 bytes
    SHA-256
    a8982aa239db62d6c8d0c28d56f0af05dd114803b78c4e473755185eea3d914e

    The same digest as the crates.io index's checksum, which Cargo checks on every download.

  2. 3.0.1 stable 2026-09-29

    The Luau bindings are an l3i extension with live views and typed rows, the loader reads config= chains exactly as OpenMW’s engine does, and the site is built with the DreamWeave Mod Template.

    Breaking changes

    • The luau feature makes the crate an l3i extension, openmw_config::luau::extension(), providing the module @dream/openmw-config, with live views over the loaded configuration and typed rows. create_lua_module, the mlua types and the standalone-lua feature are gone.
    • Building with the luau feature needs l3i’s toolchain: clang, lld and cross-language thin LTO. Rust 1.92.
    • The loader matches OpenMW’s engine: config= entries load depth first, each config= directory is read once, a config= directory without an openmw.cfg stays in the chain, the user config directory is created on load, replace= applies per config over the configs loaded before it, replace=config keeps the root and the config= entries still to load, replace=replace discards the replace= lines of earlier configs, replace= values match option names exactly, and resources=, user-data=, data-local= and encoding= are left alone by replace=.
    • The crate’s internal macros are no longer exported.
    • Built, released and documented with the DreamWeave Mod Template; the site replaces the rustdoc pages.

    Added

    • Typed game setting values and setting metadata, SettingValue exported so predicates can name what they are given, and the meta() and typed accessors every setting type has.
    • The views declare their element types, so strict scripts read them directly; items are pushed by value.
    • A Criterion benchmark for the Luau boundary.

    Changed

    • List positions are indexed, game settings iterate without cloning, settings are no longer formatted on load, and the lookup indexes hash with FxHash.
    • l3i 1.0.0 comes from crates.io.

    Fixed

    • save_user, save_subconfig and Display keep replace= lines; list replacements and removals persist through replace= lines in the user config, and a parent’s entries stay in the parent’s file when the user config replaces a list.
    • The injected resources/vfs data directory is no longer written back as a data= line on every save; the directories resources= and data-local= add stay in step with them.
    • Windows and macOS fall back to the user config; the io::Error behind ConfigError::Io is reachable through source().
    • add_archive_file returns CannotAddArchiveFile on a duplicate; replace=fallback-archive is read and written under the option name OpenMW uses.
    • set_game_setting writes its comments as comment lines, and the comments and blank lines that end a config file survive the settings added after them.
    • The setting indexes are rebuilt when singleton settings change and after data directories are injected; a parent’s relative data directory is written resolved when the user config copies it; a parent’s resources=, user-data=, data-local= or encoding= stays in place when the user sets one.
    • The live-view generation counter advances without wrapping, so a stale row can never read as current again.
    openmw-config 3.0.1 104.84 kB · crates.io
    View on crates.io
    Verify · sha256 f0fe76e8e1e0…3bda9562
    File
    openmw-config-3.0.1.crate
    Size
    107355 bytes
    SHA-256
    f0fe76e8e1e03e0651bad34b5989d61a6524e3b36372eb77da89f1363bda9562

    The same digest as the crates.io index's checksum, which Cargo checks on every download.

  3. 2.0.1 stable 2026-09-27

    Changed

    • Relicensed under MIT OR Apache-2.0.

    Known issues

    • save_user, save_subconfig and Display drop replace= lines, so a saved user config merges its parents’ entries back in on the next load. Fixed on the default branch.
    • A config with resources= writes the injected resources/vfs directory back as a data= line, and each save and reload adds another copy. Fixed on the default branch.
    openmw-config 2.0.1 84.73 kB · crates.io
    View on crates.io
    Verify · sha256 b0413b8898c3…2bee2f33
    File
    openmw-config-2.0.1.crate
    Size
    86762 bytes
    SHA-256
    b0413b8898c3277e267c033b95d5b343d9c1ebb692d17f99a4af25232bee2f33

    The same digest as the crates.io index's checksum, which Cargo checks on every download.

Credits