Mod Format

A mod is a folder inside Mods/ containing a mod.toml manifest, plus any combination of an overrides/ directory, a main.lua script, and loose content files. mod_loader::discover() scans Mods/ at startup, parses each mod.toml into a ModConfig, and sorts the results into dependency order.

Mods/
└── ExampleMod/              ← the folder name is the mod's key
    ├── mod.toml
    ├── main.lua             ← optional, runs after the layout resolves
    ├── CookedPCConsole/     ← optional, any content_path target
    └── overrides/
        └── UI_Loading_SF_LOC_INT/
            ├── UI_Loading_SF_LOC_INT.namemap
            ├── UI_Loading_SF_LOC_INT.newexports
            └── ChaletComprime.bin

The folder name matters

The mod's key — used in Mods/cu3ml.toml to remember whether it's enabled — is the folder name, not the name field. Renaming the folder resets its enable state.

mod.toml

The parser is a small hand-written TOML subset: top-level key = "value" pairs, string arrays, and the array-of-tables and tables listed below. Comments start with #.

name = "ExampleMod"
version = "1.0.0"
author = "deadYokai"
description = "Replaces UI_Loading.ChaletComprime-CologneEighty with a custom font"

# repeat this key once per directory to register with the package cache
content_path = "CookedPCConsole"

[dependencies]
requires = ["SomeOtherMod", "AnotherMod"]

[[patch.replace]]
original = "UI_Loading.ChaletComprime-CologneEighty"
replacement = "MyFonts.MyReplacementFont"

[[patch.spawn]]
class = "Engine.StaticMeshActor"
package = "MyPackage"
name = "MyActor"
Key Table Notes
name top level Display name, shown in the log and manager. Falls back to the folder name if omitted
version top level Free-form string
author top level Free-form string
description top level Free-form string, shown in the manager's detail pane
content_path top level Directory relative to the mod folder, registered with GPackageFileCache so the engine can find loose packages. Repeat the key to add more than one
requires [dependencies] Array of mod names (the name field, not folder names) this mod must load after
original / replacement [[patch.replace]] Rewrites a StaticLoadObject path — see below
class / package / name [[patch.spawn]] Parsed and counted, but not acted on by the current loader

[[patch.replace]]

Each entry installs a redirect in the StaticLoadObject hook. When the game asks for original by path, CU3ML passes replacement through to the engine instead, with a thread-local guard so the substituted load can't re-enter the hook. Entries missing either field are logged and skipped, and if a mod set has no replaces at all the hook isn't installed.

[dependencies]

requires names other mods that must be initialized first. The loader topologically sorts everything it discovered; unknown names are warned about and ignored, and a dependency cycle is reported with the mods involved appended in discovery order rather than aborting the boot. The resulting order is what main.lua scripts run in.

overrides/

Either overrides/ or Overrides/ is accepted. Each subfolder is named after the UE3 package it targets, and holds the files for that package:

File Purpose
<Anything>.bin Raw serialized export payload. The filename stem is the lookup key matched at Preload time
<PackageName>.namemap One name per line, mapping the tool's local FName indices onto the target linker's name table. Missing names are appended to the live linker
<PackageName>.newexports Optional. Declares brand-new exports to append to the package's export map

At runtime override_loader::discover() walks every enabled mod, indexes each package folder into OverrideRecords (key, payload bytes, name table), and installs the Preload hook. When the game preloads an export whose key matches, the record's bytes are deserialized in place of the original — the linker's name map is temporarily shadowed during the read so the mod's names resolve against the live table.

Without a .namemap the loader still serves the payload but logs that FName remapping is disabled, which is almost always wrong for real objects.

.newexports

A tab-separated table with 11 columns and # comments. Lines are sorted by index and appended to the package's ExportMap, letting a mod add objects the shipped package never contained:

Col Field
1 export index
2 object path
3 class name
4 class index
5 outer index
6 super index
7 archetype index
8 object flags
9 export flags
10 serial size
11 .bin filename backing the export

Malformed rows are logged with a line number and skipped. If the injection point would leave a gap in the export map, the loader refuses rather than writing an export the .bin can't reference.

Note

Overrides are matched by key at Preload time, in memory. The original .upk on disk is never opened for writing.

main.lua

If a mod folder contains main.lua, it runs once after the UE3 layout is resolved and hooks are committed, in dependency order. The mod's own folder is prepended to package.path, so require works for files shipped alongside it. See Lua Scripting.

Building overrides

The easiest way to produce .bin / .namemap / .newexports sets is the pack-mod command, check ue3-tools/Building Mods.