openmw_config
openmw_config is a lightweight Rust crate that provides a simple, idiomatic API for reading, composing, and writing OpenMW configuration files. It closely matches OpenMW's own configuration parser, supporting configuration chains, directory tokens, and value replacement semantics. For comprehensive VFS coverage, combine with vfstool_lib.
- Why Use It
- Features
- Rust Quick Start
- Lua Quick Start
- Rust Usage
- Lua Bindings (
mlua) - Advanced Behavior
- Quality & Testing
- Manual Diagnostics
- Compatibility Guarantees
- Known Limitations
Why Use It
- OpenMW-accurate semantics - models
config=traversal,replace=*behavior, and token expansion (?local?,?global?,?userdata?,?userconfig?) to match real parser behavior. - Safe persistence model -
save_user()andsave_subconfig()use atomic write semantics to avoid partial writes. - Integration-friendly API - ergonomic Rust API plus embedded Lua host bindings via
mlua, with a camelCase-only Lua surface. - Diagnostics and predictability - line-aware parse errors, explicit chain introspection, and deterministic roundtrip serialization.
Features
- Accurate parsing - mirrors OpenMW's config resolution, including
config=,replace=, and tokens like?local?,?global?,?userdata?, and?userconfig?. - Multi-file chains - multiple
openmw.cfgfiles are merged according to OpenMW's rules; last-defined wins. - Round-trip serialization -
DisplayonOpenMWConfigurationemits a validopenmw.cfg, preserving comments. - Dependency-light core - Unix/macOS path resolution and env expansion are implemented with
std; Windows default paths use Known Folder APIs viawindows-sys(Windows-only target dep). Lua support is optional via theluafeature.
Rust Quick Start
Load the active config chain, inspect values, mutate, and save in a few lines:
[]
= "1"
use OpenMWConfiguration;
See Rust Usage and API Overview for more patterns.
Lua Quick Start
Embed openmwConfig into a host-created Lua state:
[]
= { = "1", = ["lua"] }
= { = "0.10", = false, = ["luajit52", "vendored"] }
use Lua;
use create_lua_module;
This is embedded-host integration, not a standalone require("openmw_config") Lua module.
See Lua Bindings (mlua) for the full Lua API surface.
Rust Usage
Loading a specific config
new() accepts either a directory containing openmw.cfg or a direct path to the file:
use PathBuf;
use OpenMWConfiguration;
// From a directory
let config = new?;
// From a file path
let config = new?;
# Ok::
Modifying and saving
use PathBuf;
use OpenMWConfiguration;
let mut config = new?;
// Replace all content files
config.set_content_files;
// Add a single plugin (errors if already present)
config.add_content_file?;
// Replace all data directories
config.set_data_directories;
// Replace all fallback archives
config.set_fallback_archives;
// Write the user config back to disk
config.save_user?;
# Ok::
Serialization
OpenMWConfiguration implements Display, which produces a valid openmw.cfg string with
comments preserved:
use OpenMWConfiguration;
let config = new?;
println!;
# Ok::
API Overview
| Core Rust API | Description |
|---|---|
OpenMWConfiguration::from_env() / OpenMWConfiguration::new(path) |
Load from env/defaults or explicit file/directory path |
root_config_file() / root_config_dir() |
Root config file and parent directory |
user_config_ref() / user_config_path() |
Resolve highest-priority user config |
sub_configs() / config_chain() |
Traverse effective subconfigs and parser-order chain events |
content_files_iter() / groundcover_iter() / fallback_archives_iter() |
Read loaded file collections |
data_directories_iter() / game_settings() / get_game_setting(key) |
Read resolved directories and fallback= settings |
add_* / remove_* / set_* methods |
Mutate loaded values |
save_user() / save_subconfig(path) |
Persist changes using atomic writes |
default_* and try_default_* free functions |
Resolve default config/user paths (panic or fallible variants) |
create_lua_module(lua) (with lua feature) |
Build a Lua table for embedded host integration |
For the complete API surface (including helper structs and all methods), see
docs.rs/openmw-config.
Task-oriented map:
- Load config state -
OpenMWConfiguration::from_env(),OpenMWConfiguration::new(path) - Inspect chain resolution -
sub_configs(),config_chain(),user_config_path() - Edit plugin/data lists -
add_*,remove_*,set_*method families - Read/write settings -
game_settings(),get_game_setting(key),set_game_setting(...) - Persist safely -
save_user(),save_subconfig(path)
Advanced Behavior
- Config chains -
sub_configs()walks theconfig=entries that were loaded. The last entry is the user config; everything above it is read-only from OpenMW's perspective. - Replace semantics -
replace=content,replace=data, etc. are honoured during load, exactly as OpenMW handles them.replace=configresets earlier settings and queuedconfig=entries from the same parse scope before continuing. - Token expansion -
?local?,?global?,?userdata?, and?userconfig?indata=paths are expanded to platform-correct directories at load time.
Flatpak and token-resolution controls:
OPENMW_CONFIG_USING_FLATPAK- if set to any value, Flatpak path mode is enabled.- Auto-detection also enables Flatpak mode when
FLATPAK_IDis set or/.flatpak-infoexists. OPENMW_FLATPAK_ID- optional app-id override (falls back toFLATPAK_ID, thenorg.openmw.OpenMW).OPENMW_GLOBAL_PATH- optional override for the?global?token target.- In Flatpak mode,
?userconfig?and?userdata?resolve to~/.var/app/<app-id>/config/openmwand~/.var/app/<app-id>/data/openmwrespectively.
config_chain() provides parser-order traversal details, including skipped missing subconfigs:
use ;
let config = new?;
for entry in config.config_chain
# Ok::
Lua Bindings (mlua)
- Public Lua methods/functions are intentionally camelCase only.
luafeature: embeds vendoredLuaJITwith 5.2 compatibility (luajit52+vendored).
Module exports (openmwConfig):
| Lua function | Returns | Notes |
|---|---|---|
fromEnv() |
config userdata |
Loads using OPENMW_CONFIG / OPENMW_CONFIG_DIR semantics |
new(pathOrNil) |
config userdata |
pathOrNil may be file path, dir path, or nil |
defaultConfigPath() |
string |
Platform default config dir |
defaultUserDataPath() |
string |
Platform default userdata dir |
defaultDataLocalPath() |
string |
Platform default data-local dir |
defaultLocalPath() |
string |
Path backing the ?local? token |
defaultGlobalPath() |
string |
Path backing the ?global? token (throws on unsupported platforms) |
tryDefaultConfigPath() |
`(string | nil, string |
tryDefaultUserDataPath() |
`(string | nil, string |
tryDefaultLocalPath() |
`(string | nil, string |
tryDefaultGlobalPath() |
`(string | nil, string |
version |
string field |
Crate version string |
config userdata methods:
| Lua method | Returns | Notes |
|---|---|---|
rootConfigFile() / rootConfigDir() |
string |
Resolved root file/path |
isUserConfig() |
boolean |
Whether root is already highest-priority config |
userConfigPath() |
string |
Highest-priority config directory |
userConfig() |
config userdata |
Returns a user-config-focused clone |
toString() |
string |
Serialized openmw.cfg output |
subConfigs() |
string[] |
Effective loaded config= directories |
configChain() |
table[] |
Rows: { path, depth, status }, status is loaded or skippedMissing |
contentFiles() / groundcoverFiles() / fallbackArchives() |
string[] |
Collection snapshots |
dataDirectories() |
string[] |
Resolved data= directories |
gameSettings() |
table[] |
Rows: { key, value, kind } |
getGameSetting(key) |
`table | nil` |
userData() / resources() / dataLocal() / encoding() |
`string | nil` |
hasContentFile(name) / hasGroundcoverFile(name) / hasArchiveFile(name) / hasDataDir(path) |
boolean |
Presence checks |
addContentFile(name) / addGroundcoverFile(name) / addArchiveFile(name) / addDataDirectory(path) |
nil |
Mutating append operations |
removeContentFile(name) / removeGroundcoverFile(name) / removeArchiveFile(name) / removeDataDirectory(path) |
nil |
Mutating remove operations |
setContentFiles(listOrNil) / setFallbackArchives(listOrNil) / setDataDirectories(listOrNil) |
nil |
Replaces full collection, nil clears |
setGameSetting(value, sourcePathOrNil, commentOrNil) / setGameSettings(listOrNil) |
nil |
Fallback setters |
setUserData(pathOrNil) / setResources(pathOrNil) / setDataLocal(pathOrNil) / setEncoding(valueOrNil) |
nil |
Singleton setters, nil clears |
saveUser() / saveSubconfig(path) |
nil |
Write to user config or loaded subconfig |
Host Integration (Embedded Lua)
This crate's Lua support is host-embedded: your Rust application creates a Lua state and injects
the openmwConfig table for scripts to consume. For setup and registration, use
Lua Quick Start.
Typical mutation and persistence flow from Lua:
local cfg = openmwConfig.
cfg:
cfg:
cfg:
cfg:
Notes
- Lua API naming is
camelCaseonly. - Most method failures throw Lua runtime errors (
pcall-friendly). tryDefaultConfigPath(),tryDefaultUserDataPath(),tryDefaultLocalPath(), andtryDefaultGlobalPath()return(value, err)tuples instead of throwing.- This is not a standalone Lua module distribution (
require("openmw_config")); integration is via Rust host registration.
Lua Stability Contract
- Across 1.x releases, the documented
openmwConfig.*constructors/default-path helpers andcfg:*read, mutate, and save method families are intended to remain stable. - Table shapes are stable:
configChain()rows:{ path, depth, status }gameSettings()/getGameSetting()rows:{ key, value, kind }
statusis one ofloadedorskippedMissing.kindis one ofColor,String,Float, orInt.nilinputs are used to clear optional settings in setter methods.
Quality & Testing
- Unit and integration tests cover parser behavior across config chains, including
replace=configqueue semantics and missing subconfig traversal outcomes. - Roundtrip behavior is validated to preserve comments and fallback lexical forms where relevant.
- Parse diagnostics include line context on malformed input variants for easier debugging.
- CI/local lint posture is strict:
cargo clippy --all-targets --features lua -- -W clippy::pedantic -D warnings. - Lua integration tests validate module exports, mutation flows, persistence, and error behavior.
Manual Diagnostics
The repository includes an ignored integration test that dumps the resolved real-world config chain to a local file for inspection.
- Test:
dump_real_config_chain_to_repo_local_file - Source:
tests/integration_manual_chain_dump.rs - Output file:
real_config_chain_paths.txt(repo root) - Purpose: verify chain resolution against your actual platform/user setup
Run it manually:
Notes:
- Run this when validating chain resolution on an actual machine/profile setup.
- This test is intentionally ignored in normal test runs and CI.
- Output format is one absolute
openmw.cfgpath per line, in traversal order. - It writes a local artifact intended for debugging and verification.
Compatibility Guarantees
- Public APIs follow semver: breaking changes land only in a new major version.
- MSRV is declared in
Cargo.tomland may change only in a semver-compatible release with notes. openmw.cfgbehavior aims to match OpenMW docs for chain traversal and replace semantics.- Unknown keys are preserved during parse/serialize roundtrips.
Known Limitations
settings.cfghandling is intentionally deferred to a post-1.0 release.- This crate models
openmw.cfgbehavior only; it does not implement the entire OpenMW config stack.
Reference
OpenMW configuration documentation
See CHANGELOG.md for release history.
openmw-config is not affiliated with the OpenMW project.
Support
Has openmw-config been useful to you?
If so, please consider amplifying the signal through my ko-fi.
Thank you for using openmw-config.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.