Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

What is RPFM?

This is RPFM

Rusted PackFile Manager (RPFM) is a modding tool for every Total War game from Empire (2009) onwards. It’s a Rust + Qt6 reimplementation of the original PackFile Manager (PFM), built to be fast, accurate and pleasant to spend hours in.

What it does

In modern Total War games, the data the engine utilizes lives inside .pack files: archives that bundle DB tables, localisation strings, scripts, models, textures, animations, sounds and more. Modding the game means producing your own Pack and telling the launcher to load it on top of the vanilla data.

RPFM is the editor where that Pack is built. It opens, inspects, edits and saves Packs, and ships editors/viewers for every common file type inside them:

  • DB tables — schema-aware grids with lookups, references, sorting, filtering, spreadsheet copy/paste and TSV import/export.
  • Loc files — localisation tables with TSV import/export.
  • Text & scripts — Lua, XML, JSON, HLSL and dozens of other text formats with KDE syntax highlighting.
  • Animations — AnimPack, AnimTable, AnimFragment, MatchedCombat.
  • Models & visuals — RigidModel, DDS/atlas images, Portrait Settings.
  • Video — the proprietary CA_VP8 format.
  • Specialised — ESF (saves and startpos), BMD (battle maps), and a few more.

Beyond editing individual files, RPFM understands a Pack as a whole and how it relates to the rest of the game data. That’s where the more interesting features live:

  • Diagnostics that catch invalid references, missing locs, broken portrait variants, animation gaps and dozens of other classes of mod bugs before the game does.
  • Global Search with regex across the open Pack — or across vanilla and parent mods too.
  • Dependency manager that keeps track of vanilla data and parent mods and powers reference lookups everywhere else.
  • Pack optimiser that strips ITM rows, datacore deletes and unused content to keep the final Pack lean.
  • MyMod workspaces that bundle a Pack with its assets and templates, with one-click install to the game folder.
  • Translator that makes keeping mod translations up to date painless.

How RPFM is built

RPFM is split into two cooperating processes:

  • rpfm_ui — the Qt6 desktop application you interact with. Everything you see — menus, editors, dialogs — lives here.
  • rpfm_server — a headless backend that does the heavy work: file I/O, schema decoding, diagnostics, search, dependency resolution. The UI launches it automatically.

This split exists because the same backend is also exposed over WebSocket and the Model Context Protocol, so AI tools and other clients can drive Total War file editing programmatically. If you’re a tool author, see the Server section.

A note on PFM

If you’ve used the original PackFile Manager, the menus and layouts will feel familiar — that’s deliberate. RPFM started as a straight reimplementation of PFM in Rust and Qt and has grown well past it, but the muscle memory should still mostly transfer.

Where to go next

You can always reach this manual from inside RPFM via About → Open RPFM Manual, or by clicking in the manual button in the welcome page.

Installation

RPFM ships pre-built for Windows and Linux. macOS isn’t supported yet — there’s no maintained build.

Windows

  1. Download the latest rpfm-vX.Y.Z-x86_64-pc-windows-msvc.zip from the releases page.
  2. Extract the archive anywhere — your Documents folder, a tools folder on a secondary drive, anywhere you like. RPFM is portable; it doesn’t need an installer.
  3. Run rpfm_ui.exe.

That’s it. The first launch will take you to first-time configuration.

Heads-up: Don’t extract the zip into a path that requires admin rights (e.g. Program Files). RPFM writes certain update-related files relative to where its files are, and a write-protected install path causes confusing errors.

Linux

Arch Linux and derivatives

The recommended install is the rpfm-bin package on the AUR:

# With your favourite AUR helper
paru -S rpfm-bin
# or yay -S rpfm-bin

There’s also rpfm-git if you want to build from the latest develop branch yourself.

Other distributions (Flatpak)

A Flatpak is the easiest way to run RPFM on any distro that supports Flatpak. The Flatpak bundles Qt6 and the KDE Frameworks RPFM needs, so you don’t have to install them yourself. Refer to the project’s releases page for the current Flatpak download.

Building from source

If your distro doesn’t have a maintained package and the Flatpak doesn’t fit, see Building from source.

macOS

There’s no maintained macOS build. The qt_* Qt6 bindings RPFM uses build on macOS in principle, but nobody is currently producing or testing macOS releases. If you want to take that on, contributions are welcome.

Updating

By default RPFM checks for updates on launch and shows a dialog when one is available. The update flow downloads the new version, replaces the binaries and restarts. Beta and stable channels are configurable from Preferences → Updates.

On Linux, in-app updates are disabled — your distribution’s package manager (or Flatpak) is the one in charge of updates. Update through the same channel you used to install.

Verifying it works

After launching for the first time you should see the welcome page, with quick links to the manual, recent Packs, and your update status. If you got that far, the install is good. Move on to first-time configuration.

First-time configuration

RPFM works on Pack files, but most of the interesting features — diagnostics, dependency lookups, MyMod, the optimiser — only work properly when RPFM knows where your games live and which schemas to use. The first-time configuration takes about a minute and unlocks everything.

Open Preferences (Edit → Preferences, or Ctrl+P then “Preferences”). Settings are laid out as a single scrollable list grouped into sections — Paths, General, Table, Debug, Diagnostics, Telemetry, AI — with a search field at the top you can use to jump straight to a setting by name.

Settings, or Preferences for the finolis

1. Set the game paths

In the Paths section, point RPFM at every Total War game install you care about. For each game, browse to the folder that contains the game’s executable (e.g. …/steamapps/common/Warhammer 3 for Warhammer 3).

On first launch RPFM scans your Steam libraries and fills in the game (and Assembly Kit) paths it finds automatically, so in most cases you just need to check the list and fill any gaps by hand. Autodetection only runs when a path is empty — it never overwrites one you’ve set yourself — and it only covers Steam installs, so non-Steam copies still need to be pointed at manually.

You don’t have to fill in all of them — only the ones you mod. Empty entries are fine.

RPFM uses these paths to:

  • Load vanilla data for reference lookups, diagnostics and ITM detection.
  • Run the optimizer and the Tools (Faction Painter, Unit Editor, Translator) against a real game install.
  • Resolve where MyMod installs its packs.

You can still switch to a game that has no path set, but anything that reads vanilla data (diagnostics, dependency cache, optimizer, Tools, MyMod install) will either fail or produce empty/false results until you point RPFM at the install.

2. Pick a default game

Still in Preferences, in the General section, set Default Game. RPFM will switch to that game on launch and treat its schema as the active one. You can change the active game later from the Game Selected menu — see The main window.

3. Set the MyMod and Secondary paths

Two workspace paths live in the Paths section:

  • MyMod Base Path — folder where all your MyMod projects live. RPFM will create one subfolder per game inside it (e.g. MyMod/warhammer_3/, MyMod/three_kingdoms/). If you don’t plan to use MyMod, you can skip this — but most non-trivial mod projects benefit from the workspace structure MyMod provides. See What is MyMod?.
  • Secondary Folder — folder where mod launchers (Runcher and any other launcher that supports this feature) store the mods they manage, outside the game’s /data and /content directories. Pointing RPFM at it populates the Pack → Open From Secondary submenu with the Packs in that folder, so you can open them without digging through the filesystem. Skip it if your launcher doesn’t support a secondary folder.

4. Download or check schemas

Schemas are the per-game definitions RPFM uses to decode DB tables. They don’t ship with the program — they’re pulled in through the Update Manager, and they need updating every time Creative Assembly patches a game.

From the menu bar, run About → Check Updates. The Update Manager handles the program itself, schemas, and a couple of other auxiliary data sets (Lua autogen, Empire/Napoleon AK) from one dialog, with a button per component; press the schemas one to fetch the latest set from the rpfm-schemas repo. Do this on first launch to get the initial schemas, and again whenever a Total War game gets a content patch.

5. (Optional) Generate the dependency cache

Diagnostics, references and the dependency manager are dramatically more useful with a dependencies cache of the vanilla game files for the active game. Generate it once per game:

Game Selected → Generate Dependencies Cache

Generating it can take a few minutes — RPFM is reading and indexing every vanilla Pack. The cache is automatically invalidated whenever the game updates, so you’ll need to regenerate it after every patch.

Game Selected menu, a classic

6. (Optional) Set up the Assembly Kit (where supported)

Some tables — units, buildings, technologies — only exist in the Assembly Kit and not in the game’s Pack files. If you want diagnostics and references to cover those too:

  1. Install the Assembly Kit for the game from Steam (look for “Assembly Kit” or “Mod Tools” in the game’s tools list).
  2. In Preferences → Paths, set the Assembly Kit path for the game.
  3. Regenerate the dependencies cache (Game Selected → Generate Dependencies Cache). When an AK path is set, its data is folded into the same cache — there’s no separate step.

The AK isn’t shipped for every game. For Empire and Napoleon there’s no AK to install, but archived AK definitions are available as an optional download through the Update Manager (About → Check Updates) — grab them from there if you mod either game.

7. (Optional) Telemetry & crash reports

RPFM ships with two opt-out toggles in Preferences → Telemetry:

  • Enable Usage Telemetry — anonymous counters of which actions get used. Helps prioritise development.
  • Enable Crash Reports — automatic upload of panic reports to Sentry when something crashes. Helps fix bugs.

Both are on by default. Turning either off takes effect immediately.

You’re set

That’s the configuration done. The rest of the manual covers what you can actually do with the program — start with The main window for an orientation, or jump to a specific editor if you know what you’re after.

The main window

A quick orientation tour. Once you know where everything lives, the rest of the manual will make sense as it points at this or that panel.

The pieces

  1. Menu bar. Top of the window. Houses every global action — see below.
  2. Tab bar. Sits under the menu bar. One tab per open file.
  3. Pack tree. Left dock, top half. Shows the files inside the open Packs. Right-click anything in the tree for context actions; double-click a file to open its editor in a new tab.
  4. Dependencies. Left dock, under the Pack tree. Shows the contents of the active game’s dependencies cache (vanilla + parent mod Packs), so you can browse them alongside your own files.
  5. Editor area. The big central region. Whatever tab is active here drives most of what you can do.
  6. Side and bottom panels.
    • Diagnostics — bottom dock, visible by default. Lint-style warnings for the open Packs.
    • Global Search — right dock, hidden by default. Toggle from View or with its shortcut.
    • References — bottom dock, hidden by default. Populated when you ask “what points at this row?” from an editor.
    • Quick Notes — not a dock. It’s a side panel inside each file editor, toggled per-tab.
  7. Status bar. Bottom strip. Shows short-lived status messages from the last operation.

The welcome page

When no Pack is open, the editor area is replaced by a welcome page with quick links to recent Packs, the manual, the GitHub project, the Patreon (wink wink), and some other useful stuff.

The command palette

Press Ctrl+P (or Ctrl+Shift+P for the action variant) anywhere in the app to open the command palette. It’s a fuzzy finder for:

  • Open files (jump to any file inside the Pack by typing part of its path).
  • Available commands (run any menu action by typing its name).

For most “where is the menu item that does X?” questions, the command palette is the answer.

The menu bar

MenuWhat it covers
PackPack lifecycle: new, open, save (single Pack or all open ones), save for release, close, recent files, open from content / autosave / data / secondary, session picker, Pack settings, quit.
MyModThe MyMod project workflow: open the MyMod folder, create a new MyMod, import / export all open MyMods in one go, and a per-game submenu listing the existing MyMods for that game so you can open them directly. Deleting a MyMod now lives in the Pack tree’s right-click menu (on a MyMod Pack’s root). See What is MyMod?.
ViewToggle the bottom and side docks (Diagnostics, Global Search, References, Quick Notes, Dependencies).
Game SelectedSwitch the active Total War game, launch it, open its data / Assembly Kit folders, open RPFM’s config folder, and regenerate the dependencies cache.
ToolsIntegrated tools that span multiple files: Translator, Faction Painter, Unit Editor.
AboutManual, repo, Patreon, Discord, version info, check for updates.
DebugHidden by default. Enable it from Preferences → Debug to expose lower-level commands useful for development.

Connecting the dots

If you want a focused walk-through of a particular task, the rest of this section covers Pack-level workflows:

For the editors themselves (DB, Loc, Text, etc.), see the Editors section. For everything Pack-relationship-related (search, diagnostics, references), see Search & analysis.

Opening, creating and saving Packs

RPFM can have multiple Packs open at once. Most operations are scoped to a single Pack — the one currently selected. The Pack menu is your starting point for everything in this chapter.

Opening a Pack

The Pack menu has several entry points depending on where the Pack lives:

ActionWhen to use it
Open Packs…Standard file picker. Pick one or several Packs from anywhere on disk.
Open and Merge Packs…Pick several Packs and merge their contents into a single new in-memory Pack. Useful when you want to see what a stack of mods looks like as a whole.
Load All CA PacksOpen every vanilla Pack of the active game in one go. Read-only browsing — you can’t save into a vanilla Pack.
Open Recent ▸Quick access to the last few Packs you opened.
Open from Content ▸Browse Packs in your …/Steam/steamapps/workshop/content/ for the active game.
Open from Data ▸Browse Packs in the active game’s data/ folder.
Open from Secondary ▸Browse Packs in the secondary mods folder you configured.
Open from Autosave ▸Recover a Pack from RPFM’s autosave folder.

You can also drag-and-drop .pack files onto the main window to open them.

Multi-pack at once. When several Packs are open, the tree shows them as siblings. Diagnostics, search and dependencies all work across the open set, which is how you debug interactions between mods or compare a mod against vanilla.

Creating a new Pack

Pack → New Pack creates an empty Pack in memory. It isn’t on disk until you save it. The new Pack defaults to type Mod for the active game; to change the type or compression, right-click the Pack’s root in the Pack tree and use the Change PackFile Type and Compression Format submenus before saving.

Pack types

RPFM supports every Pack type the engine understands. The most common are:

  • Mod — the standard type for player-made mods. This is what you want 99 % of the time.
  • Movie — official types used by Creative Assembly themselves. Only used in mods to load certain stuff.
  • Boot — loads first, before everything else. Used by official content; rarely appropriate for player mods.
  • Release, Patch — official types used by Creative Assembly. Don’t use these for your own work unless you know exactly why.

The active type can be changed from the Change PackFile Type submenu in the Pack tree’s right-click menu (on the Pack’s root).

Saving

ActionEffect
Save Pack ▸ <name>Save the named Pack back to its current path.
Save Pack As ▸ <name>Save the named Pack to a new path. The original path is forgotten.
Save Pack For Release ▸ <name>Runs the optimizer against the Pack and then saves it. Use this for the Pack you actually upload to the Workshop.
Save AllSave every open Pack that has unsaved changes.
Close Pack ▸ <name>Close the named Pack. Closes silently — there’s no “save first?” prompt, so save before closing if you have unsaved changes you want to keep.

If you have many Packs open, use Save All rather than going through them one at a time.

Compression

Packs can be compressed with LZMA1, LZ4 or ZSTD (game-dependent), or left uncompressed. Pick the format from the Compression Format submenu in the Pack tree’s right-click menu (on the Pack’s root). RPFM will only auto-compress tables for Warhammer 3 and newer — older games crash on compressed table data, so the option isn’t offered for them.

Sessions

rpfm_server (the headless backend the UI talks to) holds all of your currently open Packs — together with their dependencies — inside a single server-side session. Pack → Select Session… lets you reattach to that session if the UI was disconnected, getting every Pack back in one go — useful after a rpfm_ui crash where the server was still running. See Server overview for the details.

Autosave

RPFM autosaves the open Packs at the interval configured in Preferences → General → Autosave Interval. Recover from Pack → Open from Autosave. Setting the interval to 0 disables autosave.

What’s next

Now that you can move Packs in and out, the next chapter covers what to actually do with them: The Pack tree.

The Pack tree

The Pack tree is the left-hand panel listing the contents of every open Pack. Files are grouped by their in-Pack path (DB tables under db/, locs under text/, etc.). The tree is the entry point to almost every per-file action in RPFM.

  • Double-click a file to open its editor in a new tab.
  • Single-click to select, and to open its editor in a preview tab. Multi-select with Shift (range) or Ctrl (additive).
  • Right-click to open the context menu (covered below).
  • Filter with the search box at the bottom of the tree. Matches against file paths; the tree collapses to show only matching entries.

Modified or newly-added items are marked with a thin vertical line on the right side of their row. Two states are drawn:

  • Added — new file/folder since the Pack was opened (or since the last save).
  • Modified — file with unsaved changes in the Pack (or a folder containing modified children). Modified takes priority over Added when both apply.

Both colours are configurable in Preferences → Appearance (Added and Modified colours, with separate light/dark theme entries). Folders inherit the most “active” state of their children. MyMod Pack roots also get an italic “MyMod” label drawn on the right side of the row.

Context menu

Right-clicking a file or folder gives you the actions below. Most also have keyboard shortcuts you can rebind in Preferences → Shortcuts.

The actions are grouped under three submenus (Add…, Create…, Open…) plus a flat list of file operations and Pack-level actions.

Add…

  • Add File — pick one or several files from disk and add them to the Pack at the selected location.
  • Add Folder — recursively add every file under a folder. The directory structure is preserved.

Create…

  • New Folder — create an empty folder inside the Pack.
  • New AnimPack / DB / Loc / Portrait Settings / Text — create an empty file of the chosen type at the selected location.
  • New Quick File — create a file with its type autodetected, based on the folder you have selected.

Open…

  • Open with Decoder — open the file in the DB Decoder. Used only for decoding tables.
  • Open Dependency Manager — opens the in-Pack dependency manager. See Dependencies.
  • Open Containing Folder — opens the folder containing your pack in your file manager.
  • Open with External Program — open the file in the external program you have configured in your OS for its type. Edits are reloaded back into the Pack when you save in the external editor.
  • Open PackFile Settings — opens Pack settings for the Pack the selection belongs to.
  • Open Notes — opens the Notes view for the Pack.

File operations

  • Rename/Move — rename the file or folder, or move it to a different in-Pack path. Renaming a folder rewrites every contained path; references in DB tables are not updated automatically.
  • Delete — remove the selection from the Pack.
  • Extract — write the selected files out to disk, preserving their tree structure.
  • Copy Path — copy the in-Pack path to the system clipboard.
  • Copy / Cut / Paste — clipboard operations within the Pack tree (or back into the same Pack at a new location).
  • Duplicate — create a copy of the selected file inside the Pack.
  • Copy To Pack ▸ <other Pack> — copy the selection into another open Pack. Useful when refactoring or splitting mods.
  • Merge Tables — only enabled with multiple DB tables of the same definition (or multiple Loc files) selected. Combines them into one and removes the originals. Useful for collapsing many small tables into a single one.
  • Update Tables — update the tables in the pack to their latest version supported by the game.
  • Generate Missing Loc Data — generates Loc entries for fields in your pack that are missing them.

Pack-root actions

When you right-click the Pack root itself, additional Pack-level actions appear: Save Pack, Save Pack As…, Save Pack For Release…, Close Pack, Install / Uninstall, the Change PackFile Type submenu (Boot, Release, Patch, Mod, Movie, plus header flags), the Compression Format submenu (None, Lzma1, Lz4, Zstd), the Special Stuff actions for the active game (Optimize PackFile, Rescue PackFile, Build Starpos, Patch Siege AI, Live Export, Pack Map, Update Anim IDs), and MyMod actions when the Pack is a MyMod.

Multi-pack operations

Some context-menu actions know about every open Pack:

  • Copy To Pack ▸ lists every other open Pack.
  • Cut+Paste moves files within a Pack at a new location.

The tree itself only supports drag-drop within a single Pack. To move files between Packs, use Copy To Pack or Cut+Paste.

Tips

  • The filter bar accepts substrings and regex. Use it to surface every loc, every animation, every file under text/db/, etc.
  • Use Expand All / Collapse All at the bottom of the context menu to quickly fold or unfold the tree.

Pack settings & notes

Each Pack carries a small bundle of metadata that lives inside the Pack itself. These are spread across a few different views and menu actions in the UI; this page covers them.

Pack file type, compression, header flags

These live on the Pack root’s context menu (right-click the Pack in the tree). They are not inside the Pack Settings tab.

Change PackFile Type

A submenu listing the possible PFH file types: Boot, Release, Patch, Mod, Movie. Mods almost always want Mod (shows up in the launcher) or Movie (always-on, hidden from the launcher). The other three are CA-only types and should not be used for player mods.

The same submenu also exposes the header flags as checkable items: Index Includes Timestamp (editable), and Header Is Extended, Index Is Encrypted, Data Is Encrypted (read-only — RPFM cannot save encrypted Packs, but it can read them).

Compression Format

A submenu listing the supported compression formats: None, Lzma1, Lz4, Zstd. Which formats actually work depends on the active game. The selected format is applied the next time the Pack is saved.

Pack Settings tab

Open Pack Settings (Pack root context menu → Open ▸ Open PackFile Settings) opens the Pack Settings as a tab in the main view. It’s a flat scrolling form that holds RPFM-specific behavioural settings for the Pack:

  • PackedFiles to Ignore on Diagnostics Check — paths (one per line, # for comments) that the diagnostics tool should skip. Supports per-folder, per-file, per-field and per-diagnostic ignore patterns. Files in this list still contribute reference data — they just aren’t analysed.
  • Files to Ignore when Importing — paths to skip when importing from a MyMod folder. MyMod-only.
  • Disable Autosaves for this PackFile — turns off autosaving for just this Pack.
  • Do Not Generate Existing Locs — when checked, Generate Missing Locs skips Loc entries that already exist in vanilla or in parent files.

Click Apply Settings at the bottom to save changes back into the Pack.

The fields shown here are loaded dynamically from the Pack’s settings, so the exact list may grow over time as new options are added.

Notes

Notes in RPFM are structured — not a single free-form text area, but a list of short notes, each with a message and an optional URL. Notes can be attached to The Pack as a whole — created from the tree’s context menu → Open ▸ Open Notes with the Pack root selected (or with no file selected).

Notes are persisted inside the Pack, are visible in RPFM, and are ignored by the game.

Quick Notes panel

The active file editor includes a Quick Notes side panel that lists the notes attached to the open file. Toggle it from the file tab’s right-click menu → Toggle Quick Notes.

Where the data lives

Pack settings, notes and the dependency manager are stored in a small set of RPFM-reserved files inside the Pack (settings.rpfm_reserved, notes.rpfm_reserved, dependencies_manager_v2.rpfm_reserved). The game ignores these; other tools that don’t know about RPFM will see them as normal files. They round-trip cleanly between RPFM versions.

These reserved files are only written for Packs of type Mod or Movie. Switching the Pack to Boot, Release or Patch before saving therefore strips them.

Dependencies

“Dependencies” in RPFM means two related but distinct things:

  1. The vanilla game data plus any parent mods you tell RPFM to consider when looking up references, running diagnostics, detecting ITM rows, and so on. This is the dependency cache.
  2. The list of parent Packs declared inside your Pack. This is the dependency manager.

This chapter covers both.

The dependency cache

Most “smart” features in RPFM (reference lookups, table validation, ITM detection, datacore handling, the diagnostics panel, the optimiser, global search across vanilla, MyMod install) need to know what the vanilla game contains. RPFM keeps a cached snapshot of that data per-game.

Generating the cache

Game Selected → Generate Dependencies Cache builds (or refreshes) the cache for the currently active game. The generation can take a few minutes — RPFM is reading every vanilla Pack of the game. Re-run it whenever:

  • The game gets a content patch.
  • The schemas got updated.
  • The cache appears empty/missing files in the Dependencies panel.

The cache is stored under RPFM’s config directory and is per-game.

Assembly Kit data

For games with an Assembly Kit, the cache build also picks up AK-only tables (units, buildings, technologies, etc. that aren’t shipped inside Packs) and stores them alongside the vanilla data. This happens automatically as part of Generate Dependencies Cache — there is no separate “Generate AK Database” command.

For this to work, RPFM needs to know where the Assembly Kit is installed. Set the path in Preferences → Paths → Assembly Kit for each game you have one for. If the path isn’t configured, the cache is still built but RPFM warns that diagnostics may produce false positives because some reference tables won’t be available.

The Dependencies panel

The Dependencies dock (toggle from View → Toggle Dependencies Window) lets you browse:

  • Game Files — vanilla Packs, indexed.
  • Parent Files — files contributed by parent mods you’ve declared.
  • Assembly Kit Files — Assembly-Kit-only tables and locs.

You can open any vanilla / parent / AK file in a read-only editor — useful for checking how Creative Assembly does things, or comparing your overrides against their source.

The in-Pack dependency manager

A Pack can declare other Packs as parents. The game itself ignores this list, but RPFM uses it to identify the packs to load as part of the dependencies when opening the pack.

To edit a Pack’s declared parents, right-click the Pack in the tree → Open ▸ Open Dependency Manager. It opens as a tab in the main view.

The manager is a small two-column table:

  • Load before ingame? — checkbox. Forces the game to load the pack as dependency if present. Alters the load order and is not recommended to check it.
  • Pack — the parent Pack file name (e.g. parent_mod.pack).

Order can matter for some games — when in doubt, list parents in load order.

Setting parents matters for diagnostics. Without them declared, RPFM treats parent-mod content as missing and may report false positives in the diagnostics panel. If you’re modding on top of someone else’s mod, declare it.

Putting it together

A typical workflow when you start a new mod project:

  1. Make sure the dependencies cache is built for your game (and that the Assembly Kit path is set in Preferences if the game has one).
  2. In your new Pack, open the Dependency Manager and declare any parent mods you depend on.
  3. From here on, Diagnostics, References, DB editor lookups and the Optimiser will all work correctly against your specific load order.

Editors overview

Every supported file type opens in a dedicated view that knows how to decode, present and re-encode that format. Views live in tabs in the central panel; the Pack tree is where you launch them from.

How editors behave

  • Open by double-click. Double-clicking a file in the Pack tree opens it in a new tab. Re-opening an already-open file just brings its tab forward.
  • Tabs do not survive Pack switches. Editors stay open until you close them, or you close the Pack underneath. RPFM warns before destroying unsaved changes.
  • Save are usually on-edit, except in some editors. Editors save on edit the changes to the in-memory Pack, except some editors which have a save button at the bottom of the screen. You still need to save the Pack for changes to hit the .pack file on disk.
  • External editing. From the tree’s context menu, Open with External Program extracts the file into a temp folder and hands it to your OS’s default app for that file type. Edits round-trip back into the Pack when you save them externally. There is no per-extension configuration in RPFM Preferences — the OS decides which app opens what.

Supported file types

File typeEditor chapterUI today
DB tableDB tablesFull grid editor
LocLoc filesFull grid editor
Text & scriptsText & scriptsKTextEditor (full)
Image (DDS, PNG…)Images & DDSViewer only (replace via tree)
Video (CA_VP8)VideoMetadata + IVF round-trip
AnimPackAnimPackInner tree + per-file editors
AnimsTableAnimationsJSON debug view
AnimFragmentBattleAnimationsStructured form view
MatchedCombatAnimationsJSON debug view
Portrait SettingsPortrait SettingsFull structured editor
Audio (.wav, .ogg…)AudioPlay/stop player
RigidModelRigidModelMetadata + glTF export (read-only)
UnitVariantSpecialised editorsFull structured editor
AtlasSpecialised editorsFull grid editor
ESF (saves, startpos)Specialised editorsTree editor (debug-gated)
BMDSpecialised editorsJSON text editor
UICSpecialised editorsRead-only text dump
Group formationsSpecialised editorsJSON debug view

JSON debug view means the file decodes via the lib, gets serialised to pretty JSON, opens in a text editor, and you save it by hitting Save (the JSON is parsed back into the typed structure). It’s editable but it’s not a UI — handle with care.

For the live list of formats rpfm_lib understands and their lib-side capability, see the rpfm_lib::files API docs. A format with full lib support may still only have a debug view in the UI.

The DB Decoder

A separate, lower-level tool for reverse-engineering binary table layouts. Used when a game patch changes a table format and the schema needs an update. See The DB Decoder.

DB tables

The DB editor is the heart of RPFM. Every Total War mod that changes unit stats, tech effects, building chains, faction colours, ability cooldowns or hundreds of other things does it by editing rows in a DB table.

DB

What’s a DB table?

A binary file under db/<table_name>_tables/<file_name> inside a Pack. Each table has a schema-defined set of typed columns and a list of rows. RPFM resolves the schema for the active game on load, decodes the rows, and presents them as a grid you can edit like a spreadsheet.

Layout

The editor itself is just the grid plus a small status bar at the bottom (line counter, flagged-rows filter button). There is no top toolbar — every action lives in the grid’s right-click context menu, plus a couple of pop-up panels:

  • Filter bar at the bottom of the grid, with Use Regex and Case Sensitive toggles and a column-group selector. Filter matches across the selected column group.
  • Search panel (Ctrl+F) — a pop-up find/replace bar over the grid; column-aware. For project-wide replace use Global Search.
  • Sidebar panel — a per-column hide / freeze checkbox grid. Open it from the right-click menu’s Sidebar entry. Frozen columns stay pinned to the left of the grid as you scroll.

The grid’s vertical header (row numbers) is always visible; the horizontal header is visible by default but column visibility/freezing is driven from the Sidebar panel rather than from a header right-click menu.

Right-click context menu

Right-click anywhere in the grid for the full action set. Highlights:

  • Add Row / Insert Row / Delete Row / Delete Filtered-Out Rows
  • Clone ▸ Clone & Insert Row / Clone & Append Row
  • Copy ▸ Copy / Copy as LUA Table / Copy to Filter Value
  • Paste / Paste as New Row
  • Generate IDs, Rewrite Selection, Revert Values, Reset Selected Values, Invert Selection
  • Resize Columns
  • Import TSV / Export TSV
  • Search (Ctrl+F opens the search bar)
  • Sidebar (toggles the per-column hide/freeze panel)
  • Find References — opens the References panel for the selected cell.
  • Cascade Edition — rename a key everywhere it’s referenced; see below.
  • Patch Columns — write a schema patch for the selected column without modifying the schema itself.
  • Go To ▸ Go To Definition / Go To File / Go To Loc (when a matching loc column exists for the row).
  • New Profile — save the current column visibility / sort / filter as a named profile.
  • Undo / Redo.

Editing rows

The grid behaves like a spreadsheet:

  • Click to select, double-click to edit.
  • Tab / Shift+Tab move between columns; Enter commits.
  • Range selection with Shift-click; column / row selection by clicking the header.
  • Copy / Paste with Ctrl+C / Ctrl+V. RPFM supports pasting from Excel / LibreOffice / Google Sheets — copy cells from a spreadsheet and paste them straight in.
  • Sort by clicking a column header.

Type-aware editing

Each column edits according to its declared type:

TypeEditor
BooleanCheckbox in the cell.
Integers/floatsNumeric editor with format validation; rejects bad input.
StringInline text editor.
ColourRGBInline RGB editor + colour picker popup.
Reference (FK)Auto-completing combo box pulling values from the referenced table.
SequenceSub-table opened in a popup editor.

Lookups & references

When a column references another table (a foreign key), RPFM:

  • Shows the referenced value’s lookup field (e.g. the unit’s display name) as part of the cell.
  • Surfaces invalid references as red highlights and as Diagnostics entries.

The global default for “show lookups vs raw keys” is PackFile → Settings → Table → Enable Lookups.

Cascade Edition

Renaming a row’s key is normally a footgun: every other table that references that key will break. Cascade Edition does it safely.

  1. Select the cell whose key you want to rename.
  2. Right-click → Cascade Edition.
  3. RPFM finds every reference to the old key across the active Pack and its parents, and asks for confirmation.
  4. On confirm, the key is renamed everywhere atomically.

Use this whenever you rename a unit, building, faction, technology, or any other keyed entity that’s referenced by other tables.

TSV round-tripping

Export a DB table to TSV when you want to:

  • Edit it in a spreadsheet program with formulas.
  • Diff it in git as plain text.
  • Run a script that produces the table mechanically.

The TSV header carries enough metadata for RPFM to re-import the file as the right table type. Re-importing replaces the table’s content wholesale.

Patches

If a column needs different display behaviour (a different lookup, a tooltip, a default value, …) without changing the table’s column layout, a schema patch overrides the field metadata at runtime. Patches ship inline inside each per-game schema file — there’s no separate overlay file to load. The right-click Patch Column action saves your override into a local per-game patch file under your config directory, which gets layered on top of the schema-shipped patches the next time the schema is loaded.

When the schema is wrong

If a table refuses to decode or shows obviously wrong values, the schema for that table is likely out of date.

  1. About → Check Updates to pull the latest schemas (along with the program, lua autogen, and old-AK updates).
  2. If the table was changed recently and no schema is available yet, open the file in the DB Decoder to update the definition manually.

See also: Diagnostics, References, Global Search.

Loc files

Loc files (anything ending in .loc — vanilla puts them under text/, mods conventionally use text/db/ or text/) hold the localisation strings the game shows to the player. The Loc editor is a simplified DB editor with three columns: key, text, and tooltip.

Loc Editor

The columns

  • Key. The string the game uses to look up the entry. Must be unique within the Pack. Conventionally namespaced (e.g. units_descr_short_text_my_unit).
  • Text. The actual displayed string, in whatever language this loc file is for. Supports the engine’s inline tags ([[col:red]], [[/col]], etc.) — RPFM doesn’t validate them, but Diagnostics will catch obviously broken pairs.
  • Tooltip. Unknown. Historically called tooltip for some reason.

Editing

Same controls as the DB editor: row add/remove/clone, find & replace, filter, sort by column, paste from spreadsheets, TSV round-tripping. The text columns are wide by default since most rows have multi-word values.

Diagnostics for locs

Several entries in the Diagnostics panel target locs specifically:

  • Empty key / empty text — usually a typo or an accidental row.
  • Duplicated key — two rows with the same key in the same Loc file.
  • Invalid escape sequences — usually [[col:…]] tags without a matching closer.

Run diagnostics after a translation pass to catch the easy mistakes.

TSV workflow for translators

The recommended flow for translating a mod is:

  1. Open the mod in RPFM.
  2. Use Tools → Translator to extract translatable strings into a structured editor (see Translator) — this is the best path for full mod translations because it integrates with the Total War Translation Hub.

If you really want raw TSV instead, every Loc editor has Export TSV — translate in a spreadsheet, then Import TSV to bring the translation back in.

Text & scripts

Anything text-shaped opens in the text editor. RPFM uses KTextEditor under the hood, which means proper syntax highlighting, line numbers, find/replace, multi-cursor and the rest of the modern code-editor toolkit — for free, on every supported language.

Text Editor

What counts as “text”

RPFM auto-detects text-shaped files by extension. The editor handles, among others:

  • Scripts — Lua, Python, batch files, shell.
  • Markup — XML, HTML, JSON, YAML, TOML, INI.
  • Shaders — HLSL, GLSL, CG, FX.
  • CA-specific.battle_script, .twui, .twui.xml, .kfa, .kfc, .kfp, .tweak, .environment.
  • Plain.txt, .md, .csv (when not opened as a table).

For the full list, see the rpfm_lib::files::text module.

Features

The KTextEditor backend gives you:

  • Syntax highlighting for every detected language.
  • Find / replace (Ctrl+F / Ctrl+R) with regex support.
  • Multi-cursor / column selection (Ctrl+Alt+click).
  • Bookmarks and quick-jump.
  • Code folding for languages that support it.
  • Indent / unindent (Tab / Shift+Tab).
  • Open / save integration with the Pack — your edits go into the in-memory Pack on save.

External editor

If you’d rather use VS Code, Neovim or anything else, Open with External Program from the Pack tree’s right-click menu extracts the file to a temp folder and opens it with your OS’s default app for that extension; saving it externally pushes the change back into the Pack. There’s no per-extension override inside RPFM — the OS picks the app.

Images & DDS

The image editor is a viewer that handles most image formats CA uses, including DDS, PNG, JPG and the various atlas formats.

Image Viewer

What it does

  • Preview the image — most DDS formats decode to PNG for display, including the BC1–BC7 compressed variants.
  • Zoom with the mouse wheel; pan by dragging.

What it doesn’t do

There’s no in-app pixel editor. To edit an image:

  1. Extract it from the Pack (Pack tree → Extract).
  2. Open it in your image editor of choice (GIMP, Photoshop, Krita…).
  3. Save it back to disk.
  4. Add it back to the Pack at the same path (Pack tree → Add File…), or use Open with External Program to wire your editor up so the round-trip happens automatically.

Atlases

.atlas files are layout maps for sprite sheets — each entry references a region of an associated DDS. They open in their own simple table editor (under Specialised editors) rather than the image viewer.

Video (CA_VP8)

.ca_vp8 is a Creative Assembly–specific container around a VP8 video stream — used for movies, intros and similar. RPFM has full read/write support and can convert to and from the standard .ivf container.

Video Editor

What you can do

  • Inspect stream metadata: width, height, frame count, framerate, codec version (v0 or v1).
  • Convert to and from IVF — can convert CA_VP8 files to and from IVF files, editable with ffmpeg.
  • Check video integrity — verify the stream parses cleanly. Useful when debugging a video the game refuses to play.

Editing a video

There’s no in-app video editor. To re-encode a CA_VP8:

  1. Convert the file to IVF (button in the editor toolbar).
  2. Open the IVF in your video tool of choice and produce a new VP8 stream. FFmpeg works:
    ffmpeg -i input.mp4 -c:v vp8 -b:v 2M output.ivf
    
  3. Convert back from IVF in the editor to bake the new stream into the .ca_vp8.

AnimPack

An AnimPack (.animpack) is a container file CA uses to bundle animation-related game data — animation tables (AnimsTable), animation fragments (AnimFragmentBattle), matched-combat tables, and the raw .bin animation files referenced by them — into a single archive the engine can load efficiently. From a modder’s perspective it’s a “Pack inside a Pack”.

Animpack

Layout

The editor opens as a single tab with two side-by-side panels and an instructions banner across the top.

  • Left panel — a live view of the main contents tree of every open Pack. It mirrors what you see in the dockable Pack tree, including all open Packs simultaneously.
  • Right panel — the contents of the AnimPack itself. Read-only display: you can browse and filter, but you can’t open files into editors from here.

Each panel has its own filter line edit, case-sensitive toggle, auto-expand-matches toggle, and Expand-all / Collapse-all shortcuts.

What you can do

The editor exposes exactly three operation:

  • Copy a file into the AnimPack — double-click a file in the left panel. The file is copied from the open Pack into the AnimPack.
  • Copy a file out of the AnimPack — double-click a file in the right panel. The file is copied out of the AnimPack into the open Pack at the same path.
  • Delete a file from the AnimPack — select it on the right panel and press Del (the action is shortcut-bound; there is no visible context menu).

If the open AnimPack is from vanilla (GameFiles) or a parent mod (ParentFiles) rather than your own Pack, the copy-in action is silently disabled — you can only modify AnimPacks you own. Copy-out from a vanilla AnimPack into your own Pack is allowed.

Which Pack the operations target (multiple Packs open)

Every operation resolves the target Pack from the left panel’s own selection inside the AnimPack tab (or, if nothing is selected there, the first editable Pack in the panel). The dock’s selection is irrelevant here.

  • Copy in: source files come from the Pack of the file you double-clicked on the left panel, and the AnimPack inside that same Pack is what gets modified.
  • Copy out: the file is written into the Pack currently selected (or first) on the left panel of the AnimPack tab. If that Pack doesn’t have an AnimPack at the same path, the operation errors.
  • Delete: operates on the AnimPack inside the Pack currently selected (or first) on the left panel.

So to switch which Pack you’re working against, click a node belonging to that Pack inside the AnimPack tab’s left panel — not in the dockable contents tree.

Caveats

  • Renaming files inside an AnimPack is not supported in the editor — you’d have to copy out, rename in the parent Pack, copy back in, and delete the original.

Animations

Three closely related animation file types share a chapter but each have different UI maturity in RPFM today: AnimsTable, AnimFragmentBattle, and MatchedCombat.

If you’re new to TW animation modding, the practical order is: AnimsTable defines the animation library; AnimFragmentBattle says how those animations apply to a creature in battle; MatchedCombat orchestrates the synchronised “two units fighting each other” animations.

AnimsTable

UI is a JSON debug view: the file decodes via the lib, gets serialised to pretty JSON in a text editor, and saving parses the JSON back into the structured form. Round-trip works, but there is no row-grid UI yet — you edit JSON.

AnimFragmentBattle

A proper structured editor — the only one of the three that has a custom UI. The view exposes the fragment’s metadata (skeleton name, locomotion graphs, identity flags) as form widgets, and the per-entry table as an embedded grid.

Fragmented, like my heart

Diagnostics for AnimFragmentBattle catch:

  • Missing locomotion graph references.
  • Missing referenced animation files.
  • Missing sound files.

MatchedCombat

UI is a JSON debug view, same model as AnimsTable. The lib has different decode paths per game (Three Kingdoms, Warhammer 3, and a default for the rest) so the JSON shape varies, but the editor is the same generic JSON editor.

Anim & legacy formats

.anim files (the raw animation streams the others reference) currently have no UI viewer at all in RPFM. The lib has read support so they’re decodable programmatically, but double-clicking one in the tree will not open it.

Portrait Settings

Files matching *portrait_settings*.bin (typically under ui/) describe how a character’s portrait is composed in-game: which art set the character belongs to, which variants of that art set are available, the head-and-body camera setup for each entry, and which texture files supply the diffuse and mask layers for each variant. The Portrait Settings editor gives you a structured view instead of a binary blob.

Portrait Settings Editor

Layout

The tab is one widget split between a left list of entries and a right detailed view.

  • Entries list (left) — one row per “art set entry”, keyed by its art_set_id. The id is what tables like campaign_character_arts_tables.art_set_id reference. A filter line edit sits above the list.
  • Detailed view (right) — driven by the entry currently selected on the left. Contains, in order:
    • Head camera group box — z, y, yaw, pitch, distance, theta, phi, fov, skeleton node.
    • Body camera group box (checkable — toggle the title bar to enable/disable the body camera for this entry) — z, y, yaw, pitch, fov, skeleton node.
    • Variants group box — its own filtered list of variants belonging to this entry. Selecting a variant fills the rest of the panel.
    • Variant fields — diffuse file, mask 1 / 2 / 3 file paths (line edits), season (string), level (spinbox), age (spinbox), politician (checkbox), faction leader (checkbox).
    • Texture previews — four group boxes (diffuse, mask 1 / 2 / 3) that render the actual texture from the referenced path (with a fallback search that swaps /portholes/ for /units/). The previews refresh ~500ms after you finish editing the path.

Which fields are visible depends on the file’s binary version: older versions hide the fields that didn’t exist yet (e.g. version 1 has only the spherical head-camera fields and no body camera; version 4 omits season/level/age/politician/faction-leader and the spherical head fields). The editor adjusts visibility automatically on load.

Operations

All operations are right-click context menus on the two list views, not toolbar buttons. There is no toolbar.

On the entries list:

  • Add — create a new empty entry.
  • Clone — duplicate the selected entry (disabled until you have a selection).
  • Delete — remove the selected entry (disabled until you have a selection).
  • Delete filtered-out rows — once a filter is active in the entries list, delete every entry that the filter is currently hiding. Useful for trimming an entry set down to only the rows that match a pattern.

On the variants list:

  • Add, Clone, Delete — same semantics, scoped to the selected entry’s variants. There is no “delete filtered-out rows” on the variants list.

Beyond that:

  • Edit fields — change spinboxes, line edits, checkboxes directly. All changes are buffered into the selected list item and committed when you save the file. There is no separate “apply” step.
  • Filters — the entries filter and the variants filter are independent line edits with case-insensitive substring matching, debounced behind a short timer.

Diagnostics integration

The Portrait Settings checks in Diagnostics cover:

  • Datacored Portrait Settings file — the file is identical to a vanilla one and adds nothing.
  • Invalid Art Set Id — the entry’s art_set_id isn’t declared in campaign_character_arts_tables.art_set_id (across vanilla, parents, and the open Pack).
  • Invalid Variant Filename — a variant’s filename isn’t declared in variants_tables.variant_filename (or variant_name, as a fallback for older games).
  • Missing texture file — for each variant, separate checks for the diffuse and the three masks: the referenced path doesn’t exist anywhere in the open Pack, parents, or vanilla.

For a tidy mod, run diagnostics after a portrait pass — it’ll catch most of the small mistakes that produce missing-portrait icons in-game.

Audio

CA’s audio pipeline is built on Wwise and ships as several related artefacts inside Packs:

  • Sound banks (.bnk) — Wwise-format containers with the audio assets.
  • Sound bank databases — table-shaped indices that match bank IDs to game-side names.
  • Sound events — DB-shaped tables the rest of the game references when something needs to play.

In RPFM today the only dedicated UI is a play/stop media player for individual audio files (.wav, .ogg, …).

What you can do

  • Play / stop any audio file the editor recognises (the audio editor view is just two toolbar buttons).

What you can’t (yet)

  • Author or repackage a .bnk from raw audio inside RPFM.
  • Inspect a .bnk’s internal event list, listed assets, or other internal metadata.

For new audio content, use Asset Editor.

RigidModel

.rigid_model_v2 (RMV2) is CA’s in-engine 3D model format — used for units, monsters, buildings, props and effectively everything you see on the battle map. RPFM reads and writes versions 6, 7 and 8 of the format. Pre-RMV2 model formats (the ones in Empire and Napoleon) are not supported.

RigidModel Editor

Layout

The tab is a horizontal splitter: the editor form on the left, and (when enabled) the 3D preview on the right.

The editor form contains:

  • RMV group box — the format version combobox (8 / 7 / 6) and the Export to glTF button.
  • LOD tree — every LOD in the file as a top-level node (Lod 0, Lod 1, …); each LOD’s mesh blocks appear as children, labelled with the material name (e.g. Mesh Block 0 (Material: weighted_skin)). Selecting a LOD or a mesh block fills the detail panels on the right; switching selection auto-saves the previous selection’s edits back into the in-memory model.
  • Detailed view group box (per-LOD) — visibility distance, authored LOD number, quality level. Active when any LOD or mesh block is selected.
  • Mesh block group box (per-mesh-block) — mesh name, texture folder, shader name. Only active when a mesh block is selected, not the LOD root.
  • Textures table — two columns (texture_type, texture_path) listing the textures attached to the selected mesh block’s material. Editable like a regular table view.

Editing

The editor is editable, not read-only — for files inside your own Pack. When a file is loaded from GameFiles or ParentFiles, the save path is a no-op (you can change the fields, but they won’t be persisted).

What you can change from the UI:

  • Per-LOD: visibility distance, authored LOD number, quality level.
  • Per-mesh-block: mesh name, texture directory, shader filters.
  • Per-texture: texture type and path, via the textures table.
  • Per-file: format version (re-encode as 6, 7 or 8 via the combobox).

What is not editable from the UI: vertex data, bones, geometry, attachment points, custom material parameters, cloth/grass/collision-specific data. To make changes there, author the model in Blender / 3ds Max / Maya with the appropriate CA exporter and import the resulting .rigid_model_v2 into your Pack.

3D preview

The 3D preview is gated by both:

  • The support_model_renderer Cargo feature at build time. Default release builds may or may not include it depending on the platform — the renderer pulls in a separate native library that isn’t on every target.
  • A runtime setting (enable_renderer in preferences). Even when the build supports it, the renderer is only spun up when this setting is on.

When both conditions are met and the model loads, the renderer attaches as a second pane in the splitter and stays in sync with the file’s in-memory state on save and reload. Failures to load the renderer are non-fatal — the editor still works, just without the 3D pane.

glTF export

Export to glTF in the RMV group box converts the current in-memory model to glTF 2.0 (one scene per LOD) and writes it to a path you pick via a Save dialog. Useful for opening CA models in Blender or other standard 3D tooling.

The same conversion is available programmatically via rpfm_extensions::gltf for tools that want to batch-convert outside the UI.

Specialised editors

Several less-common file types have dedicated views that didn’t warrant their own chapter. Grouped here for completeness.

UnitVariant

Variant mesh definitions for units — which RigidModel goes with which faction colour scheme, kit, etc. The view is a proper structured editor: a categories list on the left, a variants list in the middle for the selected category, and a per-variant form on the right (mesh file, texture folder, an unknown numeric value). Add / clone / delete on both lists from their right-click menus.

A separate read-only JSON debug view for the same file type also exists behind a settings, in case you want to edit the file more freely.

Atlas

Sprite-sheet layout files. Each row defines a region (UV rectangle + name) within an associated texture. Atlas files open in the regular DB editor — full grid editing, TSV round-trip, the whole DB editor toolset.

ESF

.esf is CA’s binary format for save games and startpos files. Massive, deeply nested, and slow to parse. RPFM exposes it as a tree of nested nodes with typed leaves (ints, floats, strings, arrays).

Disabled by default. The ESF editor lives behind the Enable ESF Editor toggle in PackFile → Settings → Debug. Turn it on first; otherwise opening an .esf falls back to the JSON debug view.

  • Browse the full structure as a tree.
  • Edit leaf values in place via a side detail panel.
  • Filter within the loaded ESF (substring + regex toggle, plus an “auto-expand matches” option).

Writing is supported but slow on big files (a multi-hundred-MB save can take a few seconds). Most ESF editing is for startpos files in a campaign mod context.

BMD

Battle Map Definition files — the binary scene description for battle maps (terrain, props, lighting). The current view is a JSON text editor: the file decodes via the lib, gets serialised to pretty JSON, and saves parse the JSON back. Editable but unstructured.

Group formations

Formation definitions for unit groups (e.g. infantry block, cavalry charge wedge). UI is a JSON debug view — the lib supports full read/write for the per-game variants (Warhammer 3 / Three Kingdoms / Troy / Rome 2 / Shogun 2 each have their own decode path), but the editor is the generic JSON editor.

Animation file formats summary

For convenience, here’s where each animation-shaped format lives in the manual:

FormatEditor chapter
AnimPackAnimPack
AnimsTableAnimations
AnimFragmentBattleAnimations
MatchedCombatAnimations
.anim raw streamNo UI viewer today

The DB Decoder

The DB Decoder is a low-level tool for reverse-engineering or fixing a DB table’s binary layout. You only need it when:

  • A game patch changed a table’s columns and the schema is now wrong (rows decode as garbage, or fail outright).
  • You’re adding support for a previously unknown table.

For routine editing, you want the DB editor. The Decoder is a development tool.

Decoder

Opening the Decoder

Right-click a DB file in the Pack tree → Open ▸ Open with Decoder. The file opens in its own tab and the decoder takes over the whole tab.

Layout

The decoder isn’t a “preview-the-table” view. It’s a byte-walker: a cursor moves through the file, and you commit one field at a time to the schema. The tab is laid out as:

  • Hex pane (top-left) — three monospace columns: byte offsets, raw hex bytes, and the ASCII rendering. The header bytes are shaded one colour, the bytes already covered by the schema you’re building are shaded another (yellow on light themes), and the next byte the cursor is sitting on is shaded a third (magenta on light themes).
  • Fields table (top-right) — one row per field already committed to the schema. Columns include Field Name, Field Type, First Row Decoded (the value at that position in row 1 of the file), Is key?, Ref. to Table, Ref. to Column, Lookup Columns, Default Value, Is Filename, Filename Relative Path, CA Order, Description, Bitwise Fields, Enum Data, Is Part of Colour. Right-click a row for Move Up / Down / Left / Right / Delete.
  • Current Field Decoded (middle) — for the bytes at the current cursor position, this panel shows what they would decode to as every supported type, side by side. Each row is one type, with a line edit showing the candidate value and a Use this button that commits that type as the next field and advances the cursor.
  • PackedFile Info (right) — table type, an editable version (spinbox), and the entry count read from the header.
  • Versions list (right, bottom) — every other version of this table that the schema already knows about. Right-click for Load definition (replace the in-progress definition with that version’s) or Delete definition (remove that version from the schema).
  • Buttons (bottom row) — Import from Assembly Kit, Test Definition, Remove all fields, Finish it!.

There is no live preview of the decoded table.

Workflow

1. Read the header

DB files start with: an optional GUID block, an optional version block, a one-byte flag, and a u32 entry count. The decoder reads this for you on open and pre-shades that region in the hex pane — your cursor starts immediately after it.

2. Pick a starting point (optional)

Three shortcuts before you start decoding by hand:

  • Import from Assembly Kit — if the AK ships a definition for this table, load it and let the decoder render it. Most often the right starting point.
  • Versions list → Load definition — if a previous version of this table is in the schema, load it and only fix what changed (CA usually adds columns rather than reshuffling the layout).

3. Commit fields one at a time

For each field, look at the Current Field Decoded panel and pick the row whose value looks plausible (a string that reads as text; an integer in a believable range; a float that isn’t 1e-39). Hit Use this on that row. The cursor advances by the size of that type, the field appears in the fields table, and the panel updates with the next set of candidates.

The supported types are: Boolean, F32, F64, I16, I32, I64, OptionalI16, OptionalI32, OptionalI64, ColourRGB, StringU8, StringU16, OptionalStringU8, OptionalStringU16, SequenceU32. There is no I8, the colour type is ColourRGB (no alpha), and the only sequence variant is SequenceU32.

After committing, edit the row in the fields table to fill in the Field Name (this is what becomes the column header in the DB editor) and any other metadata you have — key flag, references to other tables, default value, etc.

If you commit the wrong type, right-click the field and use Move Up / Down to reorder, Delete to remove it (which rewinds the cursor by that field’s size), or just hit Remove all fields and start over.

4. Test

Test Definition re-decodes the current file with the in-progress definition. On success you get a “Seems ok.” dialog. On failure you get the decoder error, with extra context for incomplete decodes (so you can see how far it got before things went wrong).

The decoder doesn’t iterate over multiple files for you — to validate the definition against vanilla or other parent files, save it first, then open one of those files in the regular DB editor.

5. Save

Finish it! writes the definition into the active schema for the version shown in the PackedFile Info spinbox, and reloads any DB editor tabs already open on this table so they pick up the new definition.

6. Submit upstream

Schema changes saved locally only live in your install until you submit them. PR them to rpfm-schemas so everyone benefits — see Schemas & patches.

Tips

  • Most schema breakages are “CA inserted a new column” rather than a wholesale layout change. Load the previous version from the versions list and walk the cursor up to the suspected insertion point — the candidates panel will tell you where the layout diverges.
  • SequenceU32 columns are recursive: a u32 count followed by that many copies of an inner sub-row. Decode them last and expand them in the fields table to define the inner fields.

Global Search

The Global Search panel finds — and optionally replaces — text across every supported file type in the open Packs (and, optionally, in vanilla and parent mods). It’s the closest thing RPFM has to a project-wide grep.

Toggle the panel from View → Toggle Global Search Window (default shortcut Ctrl+Shift+F).

Pattern & options

  • Pattern. What to match. By default it’s a literal substring search.
  • Use Regex. Treat the pattern as a regular expression instead.
  • Case sensitive. Off by default.

There is no “match whole word” toggle — wrap the pattern in word boundaries yourself in regex mode (\bword\b) if you need that behaviour.

Sources

A search can target several sources independently:

  • Per-Pack checkboxes. One checkbox per open Pack appears under the Sources group; tick the ones you want included.
  • Game Files. Vanilla files for the active game (requires the dependencies cache).
  • Parent Files. Parent mods declared by the active Pack (see Dependencies).
  • Assembly Kit Files. AK-only tables and locs (requires the AK install path to be configured for the active game).

You can mix any combination. The result tree groups matches per source.

File types covered

The search has a per-file-type checkbox for every editable type RPFM knows about. There’s also a Search on All master toggle and a Search on All Common toggle (DB / Loc / Text — the day-to-day editable types).

For DB tables and Loc files the matches are cell-level with row context; for text-style files matches are line-level; for the structured types (Atlas, Portrait Settings, etc.) matches are field-level inside the file’s structure.

Working with results

Each match in the results tree is clickable: clicking a DB cell opens the table and selects the cell, clicking a Loc match opens the right loc file at the right key, and so on.

Multi-select results to scope a follow-up replace.

Replace

Two modes:

  • Replace match — replace the currently-selected match(es) with the replacement text.
  • Replace all matches — replace every match in the result set in one shot.

Replace operations only run on Packs (Game / Parent / AK sources are read-only and skipped automatically).

Replace is destructive. Make sure you’ve saved (or have a recent autosave) before doing a project-wide replace, especially with regex.

Diagnostics panel

Diagnostics is RPFM’s static analyser for Packs. It runs a battery of checks across the open Packs and surfaces structured warnings and errors — invalid references, missing locs, broken portrait variants, datacore mistakes, and dozens of other classes of mod bug.

Toggle the panel from View → Toggle Diagnostics Window.

What it checks

Diagnostics is grouped internally by check category (one variant of DiagnosticType per category):

CategorySample checks
TablesOutdated table version, invalid foreign-key references, empty rows, empty keys, duplicated rows, datacore vs. master mismatches, tables on the modding deny-list, table-name issues (trailing number, contains space), required fields left empty, altered / out-of-spec tables, dangling file-path field references, invalid escape sequences in text.
Pack-levelInvalid Pack name, invalid file names, missing loc data for referenced keys, ITM (identical-to-master) files, files overwriting vanilla unintentionally, duplicate files.
Portrait SettingsArt sets / variants not declared in the corresponding tables, missing texture references, datacored portrait_settings files.
AnimFragmentBattleMissing locomotion graphs, missing animation files, missing sound files, missing metadata file.
TextInvalid loc-key references in scripts.
DependenciesPack declares a parent that doesn’t exist on disk.
ConfigMissing or outdated dependency cache, dependency cache failed to load, wrong game path.

Every diagnostic carries a severity (Info, Warning, Error), a category, the file path it applies to, the row/cell/field it relates to, and a short description.

Running diagnostics

  • Run all — checks every file in every open Pack.
  • Run on open files — limited scope; useful in big Packs.
  • Auto-run — two togglable triggers in PackFile → Settings → Diagnostics:
    • Trigger diagnostics on Pack open
    • Trigger diagnostics on table edit (only fires while the diagnostics dock is visible)

There are no “on file add” or “on Pack save” auto-run triggers.

Triaging results

Double-click a diagnostic to jump to the offending file / row / cell. Right-click for the ignore actions:

  • Ignore Parent Folder / Ignore Field for Parent Folder — skip every diagnostic on every file in the parent folder, optionally narrowed to a specific field.
  • Ignore File / Ignore Field for File — skip every diagnostic on this file, optionally narrowed to a specific field.
  • Ignore Diagnostic for Parent Folder / Ignore Diagnostic in Field for Parent Folder — skip just this specific diagnostic type across the parent folder, optionally narrowed to a field.
  • Ignore Diagnostic for File / Ignore Diagnostic in Field for File — same, scoped to one file.
  • Ignore Diagnostic for Pack — skip this diagnostic type across the whole Pack.

There is no “Fix automatically” or “Copy diagnostic” action today — every fix is manual.

Where ignores live

Each ignore action appends one line to the Pack’s Files Ignored on Diagnostics Check setting (the multi-line text field exposed in the Pack Settings tab — see Pack settings & notes). The Pack Settings tab also documents the per-file / per-field / per-diagnostic syntax if you want to write entries by hand.

These ignores live inside the Pack itself, survive across RPFM restarts, and travel with the .pack to anyone else who opens it.

Reading severities

  • Error — something that will cause a problem in-game.
  • Warning — likely a problem, but might not be a problem in-game.
  • Info — heads-up; noteworthy but not necessarily wrong.

If you’re shipping a mod, aim for zero errors and as few warnings as possible. Info entries are usually fine to leave alone.

References panel

The References panel answers the question “where else is this referenced?” for any DB cell. It’s the modder’s “find all usages” — invaluable before deleting, renaming, or changing a row whose key something else depends on.

Toggle the panel from View → Toggle References Window.

In any DB editor, right-click a cell whose value is a key (typically a primary key or a foreign-key value) and choose Find References. The References panel populates with every other place that key appears — across the open Pack, parent mods, vanilla, and (where relevant) the Assembly Kit.

Reading the results

Each result is a row in a small table with these columns:

  • Data SourcePackFile, ParentFiles, GameFiles, or AssKitFiles.
  • Path — the in-Pack path of the file containing the reference.
  • Column Name — the column that’s referencing the key (DB-only; blank for loc/filename refs).
  • Row Number — the row containing the reference.

(There’s also a Column Number column, hidden by default — you can re-show it from the table header’s right-click menu if you need it.)

Click a result to jump to it.

Common workflows

  • Before deleting a row. Run a reference search on the row’s key. If anything in your Pack references it, you’ll need to clean those up first. References from vanilla / parent mods mean the row probably shouldn’t be deleted at all.
  • Before renaming a row. Same as above. If the references are all in your own Pack, Cascade Edition (right-click → Cascade Edition in the DB editor) can do the rename across them in one shot.
  • Auditing a new feature. After adding a new unit, run a reference search on its key to confirm it’s wired up everywhere it should be (cost table, recruitment table, faction availability, etc.).
  • Understanding a vanilla mechanic. Open a vanilla DB table from the Dependencies panel, reference-search a key that interests you, and follow the trail.

Limitations

References are computed from declared schema relationships — RPFM knows that column X in table A references column Y in table B because the schema says so. Hard-coded references in script files (Lua) or text-formatted UI files won’t show up unless the schema knows about them.

For text-only references (e.g. a Lua script that looks up a unit by string), use Global Search instead.

Quick Notes

The Quick Notes panel surfaces the active file’s notes inline at the side of the editor, so you don’t need to open the dedicated notes view every time you want to read or jot something down.

Toggle it from the file tab’s right-click menu → Toggle Quick Notes. There is no main-menu View entry for it, and no global shortcut by default — every editor tab gets its own panel that you turn on per-tab.

What notes actually are

Notes in RPFM are a structured list, not a single free-form text area. Each note has:

  • A short message (the body).
  • An optional URL (for linking out to a wiki page, issue tracker, etc.).
  • An implicit attachment: either to the Pack as a whole, or to a specific in-Pack path.

The Quick Notes panel for a file shows every note attached to that file’s path, plus a + to add a new one and right-click Edit / Delete on each entry.

What it shows

When the focused editor changes (e.g. you switch tabs), each tab’s Quick Notes panel is independent and shows the notes for that tab’s file. There’s no automatic fall-back to “Pack notes” — Pack notes are accessed through the dedicated Notes view (see below).

For files opened from the Dependencies panel (vanilla / parent / AK files) Quick Notes is read-only and effectively empty: notes only exist for files that live in an open Pack.

Difference from “Open Notes”

  • Open Notes (Pack tree context menu → Open ▸ Open Notes) opens a dedicated tab containing the same structured list view. Use this for browsing every note attached to a Pack, or for working with notes when you don’t have the relevant file open.
  • Quick Notes panel is the same view embedded inside the active editor tab so you can read / jot without leaving the file.

A reminder about Save for Release

Save for Release does not strip notes. It runs the optimiser dialog before saving but leaves the Pack’s notes file (notes.rpfm_reserved) intact for Mod and Movie Packs. Notes are dropped only if you change the Pack type to Boot / Release / Patch before saving — at which point the notes section, the dependency manager and the per-Pack settings are all skipped.

Notes are intended for your own bookkeeping; if you don’t want them shipping with the Pack, delete them or change the Pack type before release.

What is MyMod?

A MyMod is RPFM’s idea of a mod project — a Pack that lives in a structured folder on disk, with optional Lua / Sublime / VSCode / Git scaffolding around it, and a few extra menu actions you don’t get with a plain Pack.

You can absolutely mod without MyMod. But for any non-trivial project, MyMod gives you:

  • A predictable folder layout per game (<MyMod base>/<game key>/<mod name>/).
  • A spot to keep raw asset files (PSDs, source XMLs, scripts in their working form) alongside the Pack they end up in.
  • Optional Sublime / VSCode project scaffolding for Lua mods (via the tw_autogen Lua API definitions).
  • Optional Git repository initialised inside the MyMod folder, with a configurable .gitignore.
  • Per-MyMod Import (folder → Pack) and Export (Pack → folder) actions, so you can work on the mod’s contents as files on disk and then bundle them back up.
  • Per-MyMod Delete that removes the whole folder via the system trash.
  • Open and Install/Uninstall through dedicated menu entries.

The folder layout

When you create a MyMod, RPFM creates the folder and any opt-in scaffolding you ticked in the new-MyMod dialog:

<MyMod base>/<game key>/
├── <mod name>.pack                 the Pack itself (saved when you save the MyMod)
└── <mod name>/
    ├── .git/                       only if Git support was enabled
    ├── .gitignore                  only if Git support was enabled
    ├── .vscode/extensions.json     only if VSCode support was enabled
    └── <mod name>.sublime-project  only if Sublime support was enabled

The Pack itself also gets a per-Pack “Files Ignored on Import” setting populated from the dialog, so the Sublime/VSCode/Git scaffolding files are skipped when you later Import the folder back into the Pack.

The <MyMod base> is configured once in PackFile → Settings → Paths → Extra Paths → MyMod base path (First-time configuration). The game-key folders are created on demand the first time you save a MyMod for that game.

When to use MyMod (and when not to)

Use MyMod when:

  • You’re starting a new mod and want a clean workspace.
  • You want one-click Lua / VSCode / Sublime / Git scaffolding.
  • The mod has source assets you want to keep alongside the Pack and round-trip via Import/Export.

Skip MyMod when:

  • You’re making one-off edits to an existing Pack you already have on disk.
  • You’re inspecting / debugging someone else’s Pack.

Active MyMods are per-Pack

A Pack is in MyMod mode when its on-disk path lives inside the MyMod base folder under one of the game-key subfolders. The mode is tracked per-Pack on the server: open a normal Pack and a MyMod side by side, and the MyMod-only context-menu actions appear only when you right-click the MyMod’s root.

The MyMod menu in the menu bar is mostly about creating new MyMods, opening existing ones, and bulk-running Import/Export across every open MyMod. The per-Pack actions (single Import, single Export, Delete, Open MyMod Folder) live in the Pack root’s right-click menu.

Next

Creating and managing a MyMod

The MyMod actions are split between the MyMod menu in the menu bar (creation, opening, bulk operations) and the Pack root’s right-click menu (per-Pack operations on a MyMod).

MyMod menu

The MyMod menu

In the menu bar the MyMod menu has, in order:

  • Open MyMod Folder — opens the configured <MyMod base> in your file manager.
  • New MyMod — creates a new MyMod (covered below).
  • Import All Open MyMods — runs Import on every Pack currently open in MyMod mode.
  • Export All Open MyMods — runs Export on every Pack currently open in MyMod mode.
  • (separator)
  • <Game name> submenu — one per supported game, only visible if you have at least one MyMod for that game. Each submenu lists the .pack files inside <MyMod base>/<game key>/ as one click-to-open entry per mod.

New MyMod, Import All and Export All are disabled until a MyMod base path is configured in PackFile → Settings → Paths → Extra Paths.

Creating a MyMod

MyMod → New MyMod opens the new-MyMod dialog. The fields are:

  • Game — combobox of supported games (Arena is excluded). Defaults to the currently-selected game.
  • Mod Name — short identifier. Used as both the folder name and the Pack file name. No spaces allowed; the Accept button is disabled if the name contains spaces or is already in use.
  • Lua Support group:
    • Create Sublime Text Project — drops a <mod_name>.sublime-project in the folder.
    • Create VSCode Project — drops .vscode/extensions.json in the folder, recommending the Lua and Code Runner extensions.
    • When either is enabled, RPFM also expects a .luarc.json to live there (auto-added to the import-ignore list).
  • Create Git Repository With GitIgnore group (checkable) — when enabled, RPFM git inits the folder and writes a .gitignore. You can either type your own .gitignore contents in the GitIgnore Contents textedit, or tick Same as files ignored on import to mirror the import-ignore list.
  • Files Ignored on Import — paths (one per line, relative to the MyMod folder) that RPFM should skip when later running Import. The Sublime/VSCode/Git scaffolding paths are appended automatically.

On confirm, RPFM:

  1. Creates <MyMod base>/<game key>/<name>/ on disk.
  2. Initialises .git, .gitignore, .vscode/, <name>.sublime-project as opted in.
  3. Creates an empty Pack and saves it as <name>.pack inside the folder.
  4. Stores the import-ignore list in the Pack’s settings.
  5. Switches the active Game Selected to match the MyMod’s game.
  6. Opens the Pack in a new tab and marks it as a MyMod on the server.

Switching MyMods

Use the per-game submenus in MyMod → <Game name> → <mod name> to open an existing MyMod. The previously-open Packs (MyMods or otherwise) are left untouched — opening a MyMod just opens that Pack alongside the others.

Per-MyMod actions (right-click the Pack root)

When you right-click the root of a Pack that’s in MyMod mode, the context menu adds four MyMod-only entries (greyed out / hidden for non-MyMod Packs):

  • Import — folder → Pack for this MyMod. See Import / Export.
  • Export — Pack → folder for this MyMod. See Import / Export.
  • Delete Selected MyMod — moves the entire MyMod folder to the system trash after a confirmation. Cannot be undone from inside RPFM.
  • Open MyMod Folder — opens the MyMod’s folder in your file manager.

Installing to the game folder

Install / Uninstall are not MyMod-specific menu items — they’re plain Pack actions that show up in the Pack root’s right-click menu for any saved Pack:

  • Install copies <pack path> into <game install>/data/<pack name> so you can launch and test.
  • Uninstall removes that copy.

For a “release” install (with the optimiser dialog), use Pack → Save Pack For Release before installing — see Pack operations.

Import / Export

MyMod’s Import and Export operations move content between a .pack file and a folder of files on disk. They’re the bridge between “Pack as opaque archive” and “mod content as files I can keep in version control or run scripts against.”

Both operations are per-Pack and only available when the Pack is in MyMod mode. There are two ways to invoke each:

  • Right-click the Pack root in the tree and pick Import or Export — affects just that one MyMod.
  • MyMod → Import All Open MyMods / MyMod → Export All Open MyMods — runs the same operation across every Pack currently open in MyMod mode.

There is no single-Pack equivalent in the menu bar — only the per-Pack right-click and the bulk menu-bar entries.

Export — Pack → folder

Export writes every file inside the MyMod’s Pack out to disk under the MyMod folder, preserving the in-Pack tree as on-disk subfolders.

After export you’ll have something like:

<MyMod base>/<game>/<mod>/
├── <mod>.pack                  the Pack itself (unchanged)
├── db/
│   ├── units_tables/my_units
│   └── ...
├── text/
│   └── my_mod.loc
├── script/
│   └── ...
└── ...

DB tables export as TSV by default, and will be parsed from TSV on import automatically.

Why export?

  • Version control. Track files individually in git rather than as a single binary blob.
  • External tooling. Run a script that processes a folder of files (validators, formatters, generators).
  • Bulk renames or refactors that are easier to do with shell tools than with RPFM’s UI.
  • Diffing two Packs — export both and diff -r.

Import — folder → Pack

Import is the reverse: scan the MyMod folder, collect every file under it (excluding any path listed in the Pack’s Files Ignored on Import setting), and add them to the Pack. Files at the same in-Pack path are replaced with the on-disk version.

Import is additive. Files that exist in the Pack but no longer exist on disk are not deleted. If you’ve removed a file from the on-disk folder and want it gone from the Pack too, delete it from the Pack manually (right-click → Delete).

The ignore list

The Files Ignored on Import list is the per-Pack setting you (optionally) populated when you created the MyMod. It lives in the Pack itself and is editable later via Open PackFile Settings (Pack settings & notes).

The new-MyMod dialog also auto-appends entries for any Sublime / VSCode / Git scaffolding you opted in to, so you don’t accidentally import .git/, .vscode/, the Sublime project file, or .luarc.json back into the Pack.

Round-tripping

Export → edit on disk → Import is the standard “I want to script changes to this mod” workflow. As long as the on-disk layout matches the in-Pack layout, RPFM treats it as a faithful round-trip.

Importing a folder that was exported from a different RPFM install (e.g. via git from a teammate) works the same. The MyMod folder layout is the contract — and the per-Pack ignore list lives inside the Pack itself, so it travels with the .pack.

Caveats

  • Import is additive, not destructive. Removing a file from the on-disk folder won’t remove it from the Pack on the next Import — delete it from the Pack manually.
  • Binary files (DDS, RigidModel, AnimPack, BMD, …) are exported as-is. Edit them in the appropriate external tool, save them back to the same path, and re-import.

Translator

The Translator is RPFM’s dedicated tool for translating mods. It’s a structured editor on top of a Pack’s loc data, plus an integration with the Total War Translation Hub so the resulting translations can be shared and applied automatically by Runcher.

Open from Tools → Translator with a Pack open.

What it does

The Translator presents every translatable string in the Pack as a structured row, with:

  • Key — the loc key.
  • Source text — the original (typically English) text.
  • Translation — your translated text.
  • Needs retranslation — boolean. Set when the source text changed since the translation was last saved (the “outdated” state).
  • Removed — boolean. Set when the key was once translated but the mod no longer contains it (the “unused” state). The translation is kept in the JSON so it can come back if the key reappears.

The workflow

  1. Auto-translate from vanilla. For keys that exist in vanilla loc data and are unchanged, the Translator will auto-translate them using the vanilla translations, leaving the modded or altered lines to be translated.
  2. Translate row by row, or using one of the translation integrations.
  3. Generate the translated loc. When you save, the Translator writes a translated .loc file into the Pack at the right path: text/!!!!!!translated_locs.loc for Warhammer 1 and newer (except Thrones of Britannia), or text/localisation.loc for Thrones of Britannia and older games. The translation works in-game immediately.
  4. Persist the translation as JSON. The translation is also persisted to <config>/translations_local/<game>/<pack>/<LANGUAGE>.json. This is the file you contribute to the Translation Hub.

Sharing through the Translation Hub

Once you’ve finished a translation:

  1. Find the JSON in <config>/translations_local/<game>/<pack>/.
  2. Submit it to the Translation Hub as an issue or PR.
  3. Once accepted, any Runcher user with Enable Translations turned on will get the translation automatically applied at launch — without altering the mod, and with outdated lines silently ignored. No more “I installed a translation pack but the mod was updated and now half the lines are wrong.”

For step-by-step instructions including screenshots, see the Translating a mod tutorial.

Faction Painter

The Faction Painter lets you edit faction colour schemes — primary / secondary / tertiary banner colours, uniform colours, and the related per-faction display knobs — without manually grinding through factions_tables and the colour columns.

Open from Tools → Faction Painter with a Pack open.

Workflow

  1. Open the Pack you want to add faction colour overrides to.
  2. Open Faction Painter.
  3. Pick the faction in the list.
  4. Adjust the colours (or hit Restore Vanilla Values to baseline first).
  5. OK — RPFM writes the new values into the appropriate DB row in your Pack, creating it as an override if it didn’t exist already.

Edits are reflected immediately in any open DB editor for the affected tables (the tool tracks which paths it touched and reloads any matching open views after save).

Per-game caveats

Different games store faction colours in different tables. Faction Painter resolves the right table and column names per game from its game-config map, so the picker UI stays the same across games even when the underlying schema doesn’t.

Unit Editor

The Unit Editor is a higher-level alternative to editing units row-by-row across main_units_tables, land_units_tables, unit_variants_tables and the loc strings that name them. It gives you a single pane that pulls in the relevant rows from each table and presents the unit as a coherent thing.

Disabled by default. The Unit Editor lives behind the Enable Unit Editor debug toggle in PackFile → Settings → Debug. Turn it on first; the menu entry is greyed out otherwise. Treat it as a power-user / experimental tool.

Warhammer 2 and Warhammer 3 only. The tool’s game-config map only covers WH2 and WH3 today. The menu entry is enabled for any game once the toggle is on, but trying to open it on an unsupported game will fail.

Open from Tools → Unit Editor with a Pack open (and the debug toggle enabled).

What it covers

For each unit, the editor surfaces:

  • The main_units_tables row.
  • The land_units_tables row.
  • The unit’s variant mesh references (unit_variants_tables etc., via the bundled variant editor).
  • Loc strings (display name, description, short description, and — for WH2 — strengths_weaknesses_texts).

Editing any field writes back into the right DB row when you save. Cloning a unit propagates the related rows so they stay consistent.

Not currently covered, despite what older docs claimed: unit_stats_*_tables, cost / upkeep / recruitment-time tables, and the unit caps tables. If you need to tweak those, edit the tables directly in the DB editor.

Per-game scope

Like Faction Painter, the Unit Editor reads game-specific table and column names from its game-config map. Only Warhammer 2 and Warhammer 3 are wired up today.

Limitations

  • Add / delete are limited. The primary “add” mechanism is Clone an existing unit; there’s no “delete unit” action.
  • For wholesale unit-creation workflows (full kit replacement, animations, sound, custom stats), the conventional table-by-table approach is still required.

Tip

Cloning an existing unit and tweaking it is the most reliable way to add a new unit through this editor. Start from a vanilla unit close to what you want and go from there.

Translating a mod

There are three approaches to translating a Total War mod. RPFM supports all of them, but the third — using the integrated Translator — is the recommended one.

The simple way

The one new modders tend to do, because it’s the easiest:

  1. Open the Pack.
  2. Open the Loc files.
  3. Manually translate the loc values.
  4. Save the Pack.

It’s not recommended. When the mod gets an update, you have to retranslate everything, or hunt down the new and changed lines and translate just those. Both flows are painful and error-prone, and you usually end up with a translation that “works but is partially outdated”.

The TSV way

The one people that have been hit by the simple-way problems tend to do:

  1. Open the Pack.
  2. Export every Loc to TSV.
  3. Use a translation tool to work on the TSV.
  4. Import the locs back.
  5. Save the Pack.

There are many variants of this — from “autotranslate everything with Google” (please proofread) to advanced workflows that isolate new / changed lines and only update those.

Translations are easier to update and easier to keep tidy in version control, but the workflow takes more setup time.

The new way: the Translator

RPFM ships with an integrated Translator that handles every painful part of the other two:

  1. Open the Pack you want to translate.
  2. Tools → Translator.
  3. Translate the lines marked Needs Retranslation.
  4. Hit Accept.
  5. Save the Pack.

The Translator does the heavy lifting for you and fixes problems that have historically plagued translations:

  • Detects lines unchanged from the vanilla English text and translates them automatically using the vanilla translation in the target language.
  • When translating a submod, reuses translations from parent mods if they exist.
  • When updating a translation, marks lines that have been removed, lines that have been altered, and lines that are new.
  • Saves the translation in a structured form that’s easy to share and collaborate on.

This means: no time wasted updating a translation (open the updated mod in the Translator and accept), no lines stuck on old translations after an update, no terminology drift across files.

How to use the Translator

The translator window has two halves.

Left side: the loc list

ColumnMeaning
KeyLoc key
Needs Retranslation?Checked when the line needs work — either it’s new, or the source text has changed since the previous translation.
RemovedThe line is no longer in the mod. The translator hides these by default.
Original ValueThe source text (usually English).
Translated ValueYour translation.

Right side: the editor

At the top: a short instruction line and the target language (picked automatically based on the language you have the game on).

Auto-translation behaviour — what should happen when you select a new line:

  • Auto-translate with DeepL — uses DeepL for high-quality machine translation. Requires a DeepL API key set in PackFile → Settings → AI.
  • Auto-translate with AI — sends the line to any AI service that exposes the OpenAI chat-completions wire format, with optional context. Quality depends on the model you pick. Requires an API URL, API key and model in the same AI pane. Examples of supported endpoints:
    • OpenAI: https://api.openai.com/v1/chat/completions
    • Anthropic (OpenAI compat): https://api.anthropic.com/v1/chat/completions
    • Gemini (OpenAI compat): https://generativelanguage.googleapis.com/v1beta/openai/chat/completions
    • OpenRouter: https://openrouter.ai/api/v1/chat/completions
    • Local (Ollama, vLLM, LM Studio, …): http://127.0.0.1:.../v1/chat/completions
  • Auto-translate with Google Translate — mediocre but free.
  • Copy Source Value — leaves the source text in place; useful for terms that don’t translate.
  • Empty Translated Value — does nothing on selection; you write the translation from scratch.

When you save the current line:

  • Replicate Edit in Pre-Translated Lines — also overwrite previously-translated lines that share the same source text.
  • Only Edit the Current Line — disable cross-line replication entirely.

Below the behaviour selectors: the Original Value (raw + formatted preview) and the Translated Value (raw + formatted preview, with prev/next buttons).

The pattern is: select a line, the right side loads it, you edit, you select the next line, repeat. When you’re done, Accept writes the translation to two places:

  • Inside the open Pack, as text/!!!!!!translated_locs.loc (or text/localisation.loc for older games), so the translation works in-game immediately.
  • On disk, as <RPFM config>/translations_local/<game>/<pack>/<LANGUAGE>.json. This is the file you share.

Sharing a translation

Submit the JSON to the Total War Translation Hub as an issue or a PR.

Why share?

  • The Translator automatically searches the Hub for existing translations of mods you open. If someone translated this mod a year ago, you don’t start from scratch — you pick up where they left off.
  • Runcher (and any other launcher using TWPatcher) automatically pulls the Hub at launch and applies translations transparently — fixing the dreaded “no text when not English” bug along the way.

So sharing makes the translation future-proof and lets others use it without juggling extra packs or worrying about parent-mod compatibility.

Optimising a mod

Before you ship a release, optimise. RPFM’s optimiser strips dead weight from a Pack — duplicated rows, rows identical to vanilla, modding-tool byproducts, unused portrait variants — and the result is smaller, more compatible, and less likely to break neighbouring mods.

Why bother

  • Size. Removing dead rows shrinks the Pack — sometimes dramatically.
  • Compatibility. Duplicated and identical-to-vanilla rows tend to be left forgotten in mod packs (even when Diagnostics warns about them) and end up causing compatibility issues when another mod tries to modify them. Your mod accidentally overwrites changes for no reason.
  • Multi-language support. Identical-to-vanilla loc entries will overwrite the player’s localisation files if the player is not on English. So even though you’re not changing those lines, the optimiser-untouched Pack would silently turn them to English for non-English players. Bad.

How

Two right-click actions on the Pack root:

  • Optimize PackFile — opens the optimiser dialog, runs the steps you’ve ticked, applies the result to the in-memory Pack but doesn’t save. Lets you review the result before committing.
  • Save Pack For Release — opens the same optimiser dialog, runs it, and saves the result to disk. The shipping shortcut.

Both use the same OptimizerOptions under the hood. The dialog is where you fine-tune which steps run; your tick choices are persisted as defaults for the next invocation. There is no separate “Optimizer” pane in PackFile → Settings — the dialog itself is the configuration surface.

When

Right before you publish a release / update. Not earlier — the optimiser strips data, and that data is occasionally what you’re using as a reminder of intent during development.

What it actually does

The full step list lives in rpfm_extensions::optimizer, and the dialog groups them into four sections:

  • Pack — Remove ITM (identical-to-master) files, Apply compression.
  • Table (DB / Loc) — Import datacores into twad_key_deletes (Warhammer 3+, see Datacores), Optimize datacored tables, Remove duplicated entries, Remove ITM rows, Remove ITNR (identical to new row) rows, Remove empty files.
  • Text — Remove unused .xml files in map/ folders, remove unused .xml files in the prefab folder, Remove .agf files, Remove .model_statistics files (the last two are BOB exporter byproducts).
  • Portrait Settings — Remove unused art sets, Remove unused variants, Remove empty masks, Remove empty files.

Each step can be ticked or un-ticked from the dialog. The defaults are conservative — destructive steps like “Optimize datacored tables” and “Remove unused art sets / variants” are off out of the box.

Datacores (twad_key_deletes)

A short tour of an old, painful pattern in Total War modding — and how Warhammer 3 (patch 6.3+) and RPFM let you stop using it.

What’s a datacore?

To explain what a datacore is, first we need to explain how table rows are loaded by the engine. Imagine the following table layout:

  • db/whatever_tables/@table — your high-priority overrides.
  • db/whatever_tables/ztable — your low-priority overrides.
  • db/whatever_tables/data__ — the vanilla table (filename varies per game).

When loading in-game, the three are merged. When two rows of the merged table have the same key, only the first one wins. So:

  • @table contains new rows and rows intended to overwrite vanilla. To change a vanilla unit’s stats, you put the modified row here.
  • ztable contains new rows you want overridden by other mods if they happen to clash (vanilla fixes, etc.).
  • data__ is the vanilla data.

Notice the gap: there’s no clean way to remove a vanilla row. To delete a building effect, the historical workaround was to import the whole vanilla table into your mod with the offending row removed, then ship that. That’s a datacore.

Why datacores are bad

Imagine you have a fancy effects table and you want to remove one of the effects in a submod. You import the table into your submod, edit it, and it works. Until… the parent mod is updated to add new effects. Now your submodded table doesn’t have those new effects, and either you update your table with them or your submod silently strips them.

And what if another submod tries to remove a different effect? Highlander situation: there can only be one.

In summary: datacoring is sometimes useful, but it reduces compatibility, makes the mod way bigger than it needs to be, and increases maintenance burden. Avoid unless absolutely needed.

For Warhammer 3 from patch 6.3 onwards, you don’t have to.

Datacores BEGONE: twad_key_deletes_tables

Patch 6.3 introduced a new table type, twad_key_deletes_tables, with a simple shape:

ColumnDescription
table_nameThe table to delete a row from.
keyThe row’s primary key (or composite key).

The engine reads this table at runtime and deletes those rows from the named tables — replacing the need for datacores entirely.

There are three ways to use it.

1. Migrate existing datacores

If your mod already contains datacored tables and you want to convert them:

  1. Right-click your Pack root → Optimize PackFile (or Save Pack For Release if you also want the optimiser to save the result).
  2. In the optimiser dialog, tick Import datacores into twad_key_deletes under the Table section.
  3. Accept.

RPFM analyses your datacored tables, finds the rows that are deleted relative to vanilla / parent, and adds them as entries to a new twad_key_deletes table. After running this, verify the new table contains the right keys, then delete the datacores.

Rename the generated table. RPFM will overwrite it if you re-run the optimiser. Pick a stable name like mymod_deletes.

2. Add deletes from the source table

The clean ergonomic flow when you’re removing entries you can see in another open table:

  1. Right-click your Pack → Create ▸ New DB, type twad_key_deletes_tables as the table name, and create it once. Don’t edit it manually.
  2. Open the source table (the one with the rows you want gone).
  3. Select the rows.
  4. Right-click → Add Selection to Key Deletes → pick your twad_key_deletes table.

RPFM adds the right keys to the deletes table for you. No manual key copying — and especially no risk of mistyping a multi-key composite, which is the easiest way to add an entry that silently does nothing. The submenu is only enabled when the active game is Warhammer 3 and the destination table exists in the open Pack.

3. Edit the table directly (last resort)

If neither of the above fits — for example, you want to delete a row that doesn’t exist in any open table you can right-click — you can edit twad_key_deletes like any other DB table. This is the highest risk path: bad keys silently do nothing in-game and RPFM can’t yet tell when you’ve fat-fingered a key. Use sparingly.

Caveats

A few things to keep in mind:

  • Removing a referenced row crashes the game. If you delete a land_units row that’s still referenced by units_to_groupings, the game will crash on load — same as if you’d deleted the row in a datacore. Worse, the engine’s error dialog tends to point at the wrong Pack, and RPFM’s diagnostics don’t yet validate twad_key_deletes references. If a vanilla row is referenced from elsewhere, plan accordingly.
  • Load order matters. If your high-priority mod removes a row and a lower-priority mod re-adds it (or vice versa), the engine will reflect the load order — the deletion gets undone.

These aren’t reasons to avoid twad_key_deletes; they’re reasons to think before removing rows. Compared to datacores, it’s still a massive ergonomic win.

Server overview

rpfm_server is a standalone backend process that exposes RPFM’s functionality over a local network protocol. The desktop UI (rpfm_ui) is one client of this server, but it isn’t the only one — anything that speaks WebSocket and JSON, or anything that speaks the Model Context Protocol, can drive the same backend.

This section is for tool authors and integrators. For end-user workflows, you don’t need anything here.

Architecture

┌──────────────────────┐    WebSocket    ┌──────────────────────┐
│       rpfm_ui        │ ◀────────────▶  │     rpfm_server      │
│  (Qt6 desktop app)   │                 │   (this binary)      │
└──────────────────────┘                 └──────────┬───────────┘
                                                    │
┌──────────────────────┐    HTTP/SSE                │
│   MCP clients (AI)   │ ◀──────────▶  /mcp ◀───────┘
└──────────────────────┘
  • The server binds to 127.0.0.1:45127 by default.
  • All clients talk to the same backend, but each connection has its own session (own open Packs, own dependency cache, own settings view).
  • Behind the scenes the heavy lifting is in rpfm_lib and rpfm_extensions. The server is mostly an IPC gateway plus session bookkeeping.

What the server can do

Pretty much everything rpfm_ui can:

  • Open, save, close and inspect Packs.
  • Add, extract, rename, copy, delete, duplicate files.
  • Read and write every supported file type (DB tables, Loc, text, animations, …).
  • Run search, diagnostics, references against open Packs.
  • Manage dependencies, schemas, MyMods.
  • Drive the optimizer, the translator, startpos building, glTF export, etc.

The full surface is documented in Commands and Responses.

Endpoints

The server exposes three HTTP endpoints on 127.0.0.1:45127:

EndpointMethodPurpose
/wsGETWebSocket upgrade. Carries the JSON command/response protocol.
/sessionsGETREST: list every active session. Used by the UI session picker.
/mcp*MCP StreamableHttpService. Each client gets its own session and McpServer.

Two ways in

You’ll typically pick one of two integration paths:

  • WebSocket — for full programmatic access. You write client code that sends Command messages and matches Response messages by ID. See WebSocket protocol.
  • MCP — for AI agents and other clients that already speak MCP. The server exposes 150+ tools, plus prompts and resources. See MCP interface.

Under the hood both pathways use the same Session abstraction, but each connection gets its own isolated session with its own open Packs and dependency cache. WebSocket clients can reattach to an existing session by passing their previous session_id back on the handshake; MCP clients always start a fresh session per connection. Two clients running side by side don’t share state — they share the server process.

Spawning the server

rpfm_ui spawns rpfm_server automatically and the UI’s lifecycle owns the server’s. To run the server standalone (for tool development, automated tests, or to keep it warm between UI sessions):

./rpfm_server

The server logs to stderr and stays in the foreground until every session is gone.

Where to next

Sessions & connection lifecycle

Each WebSocket and each MCP connection lives inside a session. A session owns a dedicated background thread that processes commands serially against its in-memory state (open Packs, dependency cache, settings cache).

Sessions are isolated: open packs in one session aren’t visible in another. This is what makes “many UI clients (or MCP clients) talking to one server” safe.

Lifecycle

  1. Connect. A client opens /ws (or connects to /mcp) without a session ID. The server allocates a new SessionId, spins up a background thread, and immediately sends an unsolicited SessionConnected response so the client can stash the ID for later reconnection.

    { "id": 0, "data": { "SessionConnected": 12345 } }
    
  2. Reconnect. A client opens /ws?session_id=12345. If the session still exists and isn’t shutting down, the new socket adopts it with all in-memory state preserved (open Packs, loaded dependencies, settings cache).

  3. Disconnect. When the WebSocket drops without a Command::ClientDisconnecting, the session enters a 5-minute grace period (DEFAULT_SESSION_TIMEOUT_SECS = 300). Reconnecting cancels the timeout; otherwise the session and its background thread are torn down.

  4. Graceful disconnect. Send ClientDisconnecting before closing your socket and the session is removed immediately, telemetry is flushed, and the background thread exits.

    { "id": 99, "data": "ClientDisconnecting" }
    
  5. Empty manager → process exit. When the last session goes away the rpfm_server process exits, so no orphaned server lingers in the background.

Reconnection example

// First connect: store the session ID
const ws = new WebSocket("ws://127.0.0.1:45127/ws");
let sessionId: number | null = null;

ws.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  if (typeof msg.data === "object" && "SessionConnected" in msg.data) {
    sessionId = msg.data.SessionConnected;
    console.log("session", sessionId);
  }
};

// Later, after a network blip, reconnect with the stored ID:
const ws2 = new WebSocket(`ws://127.0.0.1:45127/ws?session_id=${sessionId}`);
using var ws = new ClientWebSocket();
await ws.ConnectAsync(new Uri("ws://127.0.0.1:45127/ws"), CancellationToken.None);

int? sessionId = null;
// ... receive the SessionConnected message and store sessionId ...

// Later, reconnect:
using var ws2 = new ClientWebSocket();
await ws2.ConnectAsync(
    new Uri($"ws://127.0.0.1:45127/ws?session_id={sessionId}"),
    CancellationToken.None);

Listing active sessions

The /sessions REST endpoint returns every live session as JSON:

curl http://127.0.0.1:45127/sessions
[
  {
    "session_id": 12345,
    "connection_count": 0,
    "timeout_remaining_secs": 180,
    "is_shutting_down": false,
    "pack_names": ["my_mod.pack"]
  },
  {
    "session_id": 12346,
    "connection_count": 1,
    "timeout_remaining_secs": null,
    "is_shutting_down": false,
    "pack_names": []
  }
]

Fields:

FieldDescription
session_idUnique identifier.
connection_countNumber of active WebSocket connections to this session.
timeout_remaining_secsSeconds until the session is cleaned up. Only set while disconnected.
is_shutting_downtrue when the session has been marked for shutdown.
pack_namesNames of the Packs currently open in this session.

The UI uses this endpoint to populate Pack → Select Session…, which lets a user reattach to a server session after the UI was killed or disconnected.

Per-session state

Each session has its own:

  • Open Packs (with their full in-memory contents and metadata).
  • Dependency cache view (game files, parent files, AK files).
  • Active game selection.

When you call something like Command::SetGameSelected, you’re changing it for your session, not globally.

WebSocket protocol

This page covers the protocol layer: how messages are framed, serialized and correlated. The full vocabulary lives in Shared types, Commands and Responses.

Connecting

The server listens on ws://127.0.0.1:45127/ws by default. Append ?session_id=<id> to reconnect to an existing session — see Sessions.

const ws = new WebSocket("ws://127.0.0.1:45127/ws");
using var ws = new ClientWebSocket();
await ws.ConnectAsync(new Uri("ws://127.0.0.1:45127/ws"), CancellationToken.None);

Immediately after the handshake the server pushes an unsolicited SessionConnected message with the session ID:

{ "id": 0, "data": { "SessionConnected": 42 } }

Message envelope

Every message — both directions — is wrapped in a Message envelope:

{
  "id": <number>,
  "data": <Command or Response>
}
FieldTypeDescription
idnumberUnique request ID. The server echoes it in the response. Use 0 only for unsolicited server-initiated messages.
dataobject or stringThe command or response payload.

Request-response correlation

The id lets multiple requests be in flight simultaneously. Match each response back to its originating request by id. A typical client maintains a Map<id, { resolve, reject }> of pending requests and resolves them as responses arrive.

Serialization conventions

All messages are JSON. The Rust enums backing Command and Response are serialized by serde with these rules:

Rust variant shapeJSON serializationExample
Unit variant"VariantName""NewPack"
Newtype variant{ "VariantName": value }{ "ClosePack": "my_mod.pack" }
Tuple variant{ "VariantName": [v1, v2, …] }{ "SavePackAs": ["key", "/path/to/file"] }

Struct variants use { "VariantName": { field: value, … } }.

This means the JSON shape closely mirrors how a Rust client would write the same request — handy when reading the Commands reference.

A complete round-trip

const ws = new WebSocket("ws://127.0.0.1:45127/ws");

let nextId = 1;
const pending = new Map<number, (resp: any) => void>();

function send(command: object | string): Promise<any> {
  const id = nextId++;
  return new Promise((resolve) => {
    pending.set(id, resolve);
    ws.send(JSON.stringify({ id, data: command }));
  });
}

ws.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  const cb = pending.get(msg.id);
  if (cb) {
    pending.delete(msg.id);
    cb(msg.data);
  }
};

ws.onopen = async () => {
  const resp = await send({ OpenPackFiles: ["/path/to/my_mod.pack"] });
  console.log("opened:", resp);
};
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;

using var ws = new ClientWebSocket();
await ws.ConnectAsync(new Uri("ws://127.0.0.1:45127/ws"), CancellationToken.None);

int nextId = 1;

async Task SendAsync(object command)
{
    var id = nextId++;
    var msg = JsonSerializer.Serialize(new { id, data = command });
    await ws.SendAsync(
        Encoding.UTF8.GetBytes(msg),
        WebSocketMessageType.Text, true, CancellationToken.None);
}

await SendAsync(new { OpenPackFiles = new[] { "/path/to/my_mod.pack" } });

For a fuller, production-shaped client see Client example.

Errors

Errors come back as the Error(String) response variant:

{ "id": 7, "data": { "Error": "Failed to open pack: …" } }

Treat any Error response as a rejection of the originating request. The error message is suitable to surface to a developer; for end-user UIs you’ll typically want to wrap it with friendlier copy.

Disconnecting cleanly

Send ClientDisconnecting before closing the socket so the server tears the session down immediately instead of waiting for the 5-minute timeout:

{ "id": 99, "data": "ClientDisconnecting" }

After sending, you can close the WebSocket. The server doesn’t reply.

What’s next

  • Shared types — every payload type referenced in Command / Response.
  • Commands — every variant the client can send.
  • Responses — every variant the server can send back.

Shared Types

This page documents all the data types used in the RPFM server protocol. These types appear as parameters in Commands and as payloads in Responses.

All types are serialized as JSON. Nullable fields use null when absent.


Core Types

DataSource

Discriminates where file data originates from. Serialized as a plain string.

ValueDescription
"PackFile"An open Pack file
"GameFiles"Vanilla game files
"ParentFiles"Parent mod files
"AssKitFiles"Assembly Kit files
"ExternalFile"External file on disk
type DataSource = "PackFile" | "GameFiles" | "ParentFiles" | "AssKitFiles" | "ExternalFile";
// Serialize as a plain string — idiomatic C# is an enum with [JsonStringEnumConverter]:
public enum DataSource
{
    PackFile,
    GameFiles,
    ParentFiles,
    AssKitFiles,
    ExternalFile,
}

ContainerPath

A file or folder path within a Pack. Serialized as a tagged enum:

{ "File": "db/units_tables/data" }
{ "Folder": "db/units_tables" }
type ContainerPath =
  | { File: string }
  | { Folder: string };
// Serialize as either { "File": "..." } or { "Folder": "..." }:
public abstract class ContainerPath { }
public class ContainerPathFile   : ContainerPath { public string File { get; set; } }
public class ContainerPathFolder : ContainerPath { public string Folder { get; set; } }

RFileInfo

Metadata about a packed file within a container.

FieldTypeDescription
pathstringInternal path within the Pack
container_namestring or nullName of the containing Pack (null if unknown)
timestampnumber or nullLast modification timestamp
file_typestringFile type enum value (e.g. "DB", "Loc", "Text")
interface RFileInfo {
  path: string;
  container_name: string | null;
  timestamp: number | null;
  file_type: string;
}
public class RFileInfo
{
    public string Path { get; set; }
    public string? ContainerName { get; set; }
    public long? Timestamp { get; set; }
    public string FileType { get; set; }
}

ContainerInfo

Reduced representation of a Pack file (container-level metadata).

FieldTypeDescription
file_namestringName of the Pack file
file_pathstringFull path to the Pack file on disk
pfh_versionPFHVersionPFH format version
pfh_file_typePFHFileTypePack file type
bitmaskPFHFlagsPFH flags bitmask (u32)
compressCompressionFormatCompression format
timestampnumberPack file timestamp (Unix epoch)
interface ContainerInfo {
  file_name: string;
  file_path: string;
  pfh_version: PFHVersion;
  pfh_file_type: PFHFileType;
  bitmask: PFHFlags;
  compress: CompressionFormat;
  timestamp: number;
}
public class ContainerInfo
{
    public string FileName { get; set; }
    public string FilePath { get; set; }
    public PFHVersion PfhVersion { get; set; }
    public PFHFileType PfhFileType { get; set; }
    public PFHFlags Bitmask { get; set; }
    public CompressionFormat Compress { get; set; }
    public ulong Timestamp { get; set; }
}

DependenciesInfo

Information about loaded dependency files, used to populate dependency tree views.

FieldTypeDescription
asskit_tablesRFileInfo[]Assembly Kit table files
vanilla_packed_filesRFileInfo[]Vanilla game files
parent_packed_filesRFileInfo[]Parent mod files
interface DependenciesInfo {
  asskit_tables: RFileInfo[];
  vanilla_packed_files: RFileInfo[];
  parent_packed_files: RFileInfo[];
}
public class DependenciesInfo
{
    public List<RFileInfo> AsskitTables { get; set; }
    public List<RFileInfo> VanillaPackedFiles { get; set; }
    public List<RFileInfo> ParentPackedFiles { get; set; }
}

SessionInfo

Information about an active session on the server. Returned by the GET /sessions REST endpoint.

FieldTypeDescription
session_idnumberUnique session identifier
connection_countnumberNumber of active WebSocket connections
timeout_remaining_secsnumber or nullSeconds until timeout (null if connections exist)
is_shutting_downbooleanWhether the session is shutting down
pack_namesstring[]Names of the open packs
interface SessionInfo {
  session_id: number;
  connection_count: number;
  timeout_remaining_secs: number | null;
  is_shutting_down: boolean;
  pack_names: string[];
}
public class SessionInfo
{
    public int SessionId { get; set; }
    public int ConnectionCount { get; set; }
    public double? TimeoutRemainingSecs { get; set; }
    public bool IsShuttingDown { get; set; }
    public List<string> PackNames { get; set; }
}

File Types

NewFile

Parameters for creating a new packed file. Serialized as a tagged enum — the variant name determines the file type:

VariantPayloadDescription
AnimPackstringFile name
DB[string, string, number][file_name, table_name, version]
LocstringTable name
PortraitSettings[string, number, [string, string][]][name, version, clone_entries]
Text[string, string][file_name, text_format]
VMDstringFile name
WSModelstringFile name

Example:

{ "DB": ["my_table", "units_tables", 4] }
{ "Text": ["script.lua", "Lua"] }
type NewFile =
  | { AnimPack: string }
  | { DB: [string, string, number] }
  | { Loc: string }
  | { PortraitSettings: [string, number, [string, string][]] }
  | { Text: [string, string] }
  | { VMD: string }
  | { WSModel: string };
// Tagged-enum payload — one class per variant:
public abstract class NewFile { }
public class NewFileAnimPack : NewFile { public string AnimPack { get; set; } }
public class NewFileDB : NewFile { public Tuple<string, string, int> DB { get; set; } }
public class NewFileLoc : NewFile { public string Loc { get; set; } }
public class NewFilePortraitSettings : NewFile
{
    public Tuple<string, uint, List<Tuple<string, string>>> PortraitSettings { get; set; }
}
public class NewFileText : NewFile { public Tuple<string, string> Text { get; set; } }  // second is TextFormat enum
public class NewFileVMD : NewFile { public string VMD { get; set; } }
public class NewFileWSModel : NewFile { public string WSModel { get; set; } }

VideoInfo

Metadata specific to video files.

FieldTypeDescription
formatstringVideo format enum value
versionnumberVideo format version
codec_four_ccstringCodec FourCC identifier
widthnumberVideo width in pixels
heightnumberVideo height in pixels
num_framesnumberTotal number of frames
frameratenumberFrames per second
interface VideoInfo {
  format: string;
  version: number;
  codec_four_cc: string;
  width: number;
  height: number;
  num_frames: number;
  framerate: number;
}
public class VideoInfo
{
    public string Format { get; set; }
    public int Version { get; set; }
    public string CodecFourCc { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
    public int NumFrames { get; set; }
    public double Framerate { get; set; }
}

FileType

Identifies the type of a packed file. Serialized as a plain string:

ValueDescription
"Anim"Animation file
"AnimFragmentBattle"Battle animation fragment
"AnimPack"Animation pack
"AnimsTable"Animation table
"Atlas"Sprite sheet atlas
"Audio"Audio file
"BMD"Battle map data
"BMDVegetation"Battle map vegetation data
"Dat"Generic data file
"DB"Database table
"ESF"Empire Save Format
"Font"Font file
"GroupFormations"Group formations
"HlslCompiled"Compiled HLSL shader
"Image"Image file (DDS, PNG, etc.)
"Loc"Localisation file
"MatchedCombat"Matched combat animations
"Pack"Nested Pack file
"PortraitSettings"Portrait settings
"RigidModel"3D model file
"SoundBank"Sound bank
"Text"Text file (Lua, XML, JSON, etc.)
"UIC"UI Component
"UnitVariant"Unit variant
"Video"Video file (CA_VP8)
"VMD"VMD text file
"WSModel"WSModel text file
"Unknown"Unrecognized file type
type FileType =
  | "Anim" | "AnimFragmentBattle" | "AnimPack" | "AnimsTable" | "Atlas" | "Audio"
  | "BMD" | "BMDVegetation" | "Dat" | "DB" | "ESF" | "Font" | "GroupFormations"
  | "HlslCompiled" | "Image" | "Loc" | "MatchedCombat" | "Pack" | "PortraitSettings"
  | "RigidModel" | "SoundBank" | "Text" | "UIC" | "UnitVariant" | "Video" | "VMD"
  | "WSModel" | "Unknown";
public enum FileType
{
    Anim, AnimFragmentBattle, AnimPack, AnimsTable, Atlas, Audio,
    BMD, BMDVegetation, Dat, DB, ESF, Font, GroupFormations,
    HlslCompiled, Image, Loc, MatchedCombat, Pack, PortraitSettings,
    RigidModel, SoundBank, Text, UIC, UnitVariant, Video, VMD,
    WSModel, Unknown,
}

CompressionFormat

Compression format for Pack files. Serialized as a plain string:

ValueDescription
"None"No compression
"Lzma1"LZMA1 compression
"Lz4"LZ4 compression
"Zstd"Zstandard compression
type CompressionFormat = "None" | "Lzma1" | "Lz4" | "Zstd";
public enum CompressionFormat { None, Lzma1, Lz4, Zstd }

PFHFileType

Pack file type. Serialized as a plain string:

ValueDescription
"Boot"Boot Pack
"Release"Release Pack
"Patch"Patch Pack
"Mod"Mod Pack
"Movie"Movie Pack
type PFHFileType = "Boot" | "Release" | "Patch" | "Mod" | "Movie";
public enum PFHFileType { Boot, Release, Patch, Mod, Movie }

PFHVersion

PFH (Pack file header) format version. Serialized as a plain string:

ValueDescription
"PFH0"Original format (Empire / Napoleon era)
"PFH2"Shogun 2 era
"PFH3"Rome 2 era
"PFH4"Attila / Warhammer era
"PFH5"Warhammer 2 / Three Kingdoms era
"PFH6"Warhammer 3 / Pharaoh / current era
type PFHVersion = "PFH0" | "PFH2" | "PFH3" | "PFH4" | "PFH5" | "PFH6";
public enum PFHVersion { PFH0, PFH2, PFH3, PFH4, PFH5, PFH6 }

PFHFlags

Pack file header flags. Serialized as a single integer (a u32 bitmask) with these bits:

BitFlagMeaning
0x0000_0100HAS_EXTENDED_HEADERPack has an extended header
0x0000_0080HAS_ENCRYPTED_INDEXThe file index is encrypted
0x0000_0040HAS_INDEX_WITH_TIMESTAMPSIndex entries include timestamps
0x0000_0010HAS_ENCRYPTED_DATAFile data is encrypted
// Raw u32 on the wire. Use bitwise-and against the constants to test flags.
type PFHFlags = number;

const PFH_HAS_EXTENDED_HEADER      = 0x0000_0100;
const PFH_HAS_ENCRYPTED_INDEX      = 0x0000_0080;
const PFH_HAS_INDEX_WITH_TIMESTAMPS = 0x0000_0040;
const PFH_HAS_ENCRYPTED_DATA       = 0x0000_0010;
[Flags]
public enum PFHFlags : uint
{
    None = 0,
    HasExtendedHeader      = 0x0000_0100,
    HasEncryptedIndex      = 0x0000_0080,
    HasIndexWithTimestamps = 0x0000_0040,
    HasEncryptedData       = 0x0000_0010,
}

SupportedFormats

Video container format. Serialized as a plain string:

ValueDescription
"CaVp8"Creative Assembly’s CA_VP8 container
"Ivf"Standard VP8 IVF container
type SupportedFormats = "CaVp8" | "Ivf";
public enum SupportedFormats { CaVp8, Ivf }

OperationalMode

Per-pack operational mode, controlling MyMod behaviour. Serialized as a tagged enum:

VariantPayloadDescription
MyMod[string, string][game_folder_name, mymod_pack_name]
Normal(none)Normal mode, no MyMod association

Example:

{ "MyMod": ["warhammer_2", "my_mymod.pack"] }
"Normal"
type OperationalMode =
  | { MyMod: [string, string] }
  | "Normal";
// Serialize as either { "MyMod": [string, string] } or the string "Normal".
public abstract class OperationalMode { }
public class OperationalModeMyMod : OperationalMode
{
    public string GameFolder { get; set; }
    public string MyModPackName { get; set; }
}
public class OperationalModeNormal : OperationalMode { }

Data Types

DecodedData

Cell data in a table. Serialized as a tagged enum — the variant name indicates the data type:

VariantPayloadDescription
BooleanbooleanBoolean value
F32number32-bit float
F64number64-bit float
I16number16-bit integer
I32number32-bit integer
I64number64-bit integer
ColourRGBstringRGB colour string
StringU8stringUTF-8 string
StringU16stringUTF-16 string
OptionalI16numberOptional 16-bit integer
OptionalI32numberOptional 32-bit integer
OptionalI64numberOptional 64-bit integer
OptionalStringU8stringOptional UTF-8 string
OptionalStringU16stringOptional UTF-16 string
SequenceU16number[]Raw bytes for a nested sequence (u16-prefixed count)
SequenceU32number[]Raw bytes for a nested sequence (u32-prefixed count)

Example:

{ "StringU8": "hello" }
{ "I32": 42 }
{ "Boolean": true }
type DecodedData =
  | { Boolean: boolean }
  | { F32: number } | { F64: number }
  | { I16: number } | { I32: number } | { I64: number }
  | { ColourRGB: string }
  | { StringU8: string } | { StringU16: string }
  | { OptionalI16: number } | { OptionalI32: number } | { OptionalI64: number }
  | { OptionalStringU8: string } | { OptionalStringU16: string }
  | { SequenceU16: number[] } | { SequenceU32: number[] };
// Tagged-enum — one class per variant. Branch on the JSON key.
public abstract class DecodedData { }
public class DecodedDataBoolean         : DecodedData { public bool Boolean { get; set; } }
public class DecodedDataF32             : DecodedData { public float F32 { get; set; } }
public class DecodedDataF64             : DecodedData { public double F64 { get; set; } }
public class DecodedDataI16             : DecodedData { public short I16 { get; set; } }
public class DecodedDataI32             : DecodedData { public int I32 { get; set; } }
public class DecodedDataI64             : DecodedData { public long I64 { get; set; } }
public class DecodedDataColourRGB       : DecodedData { public string ColourRGB { get; set; } }
public class DecodedDataStringU8        : DecodedData { public string StringU8 { get; set; } }
public class DecodedDataStringU16       : DecodedData { public string StringU16 { get; set; } }
public class DecodedDataOptionalI16     : DecodedData { public short OptionalI16 { get; set; } }
public class DecodedDataOptionalI32     : DecodedData { public int OptionalI32 { get; set; } }
public class DecodedDataOptionalI64     : DecodedData { public long OptionalI64 { get; set; } }
public class DecodedDataOptionalStringU8 : DecodedData { public string OptionalStringU8 { get; set; } }
public class DecodedDataOptionalStringU16: DecodedData { public string OptionalStringU16 { get; set; } }
public class DecodedDataSequenceU16     : DecodedData { public byte[] SequenceU16 { get; set; } }
public class DecodedDataSequenceU32     : DecodedData { public byte[] SequenceU32 { get; set; } }

TableInMemory

In-memory table data structure used by DB and Loc files.

FieldTypeDescription
table_namestringTable type identifier (e.g. "units_tables")
definitionDefinitionSchema definition for this table
definition_patchDefinitionPatchRuntime schema modifications
table_dataDecodedData[][]Row data (outer = rows, inner = columns)
alteredbooleanWhether data was altered during decoding
interface TableInMemory {
  table_name: string;
  definition: Definition;
  definition_patch: DefinitionPatch;
  table_data: DecodedData[][];
  altered: boolean;
}
public class TableInMemory
{
    public string TableName { get; set; }
    public Definition Definition { get; set; }
    public DefinitionPatch DefinitionPatch { get; set; }
    public List<List<DecodedData>> TableData { get; set; }
    public bool Altered { get; set; }
}

RFile

A raw packed file.

FieldTypeDescription
pathstringPath of the file within a container
timestampnumber or nullLast modified timestamp (Unix epoch)
file_typeFileTypeDetected or specified file type
container_namestring or nullName of the source container
dataunknownInternal data storage
interface RFile {
  path: string;
  timestamp: number | null;
  file_type: FileType;
  container_name: string | null;
  data: unknown;
}
public class RFile
{
    public string Path { get; set; }
    public long? Timestamp { get; set; }
    public FileType FileType { get; set; }
    public string? ContainerName { get; set; }
    public object Data { get; set; }
}

RFileDecoded

Decoded file content. Serialized as a tagged enum — the variant name indicates the file type. See Decoded File Types for each type’s structure.

Variants: Anim, AnimFragmentBattle, AnimPack, AnimsTable, Atlas, Audio, BMD, BMDVegetation, Dat, DB, ESF, Font, GroupFormations, HlslCompiled, Image, Loc, MatchedCombat, Pack, PortraitSettings, RigidModel, SoundBank, Text, UIC, UnitVariant, Unknown, Video, VMD, WSModel.

Example:

{ "DB": { "mysterious_byte": true, "guid": "", "table": { ... } } }
{ "Text": { "encoding": "Utf8Bom", "format": "Lua", "contents": "-- script" } }
// One variant per known file type. Most carry the decoded type described below;
// a handful (VMD, WSModel) wrap a Text payload.
type RFileDecoded =
  | { Anim: unknown } | { AnimFragmentBattle: AnimFragmentBattle }
  | { AnimPack: unknown } | { AnimsTable: AnimsTable }
  | { Atlas: Atlas } | { Audio: Audio }
  | { BMD: Bmd } | { BMDVegetation: unknown }
  | { Dat: unknown } | { DB: DB }
  | { ESF: ESF } | { Font: unknown }
  | { GroupFormations: GroupFormations } | { HlslCompiled: unknown }
  | { Image: Image } | { Loc: Loc }
  | { MatchedCombat: MatchedCombat } | { Pack: unknown }
  | { PortraitSettings: PortraitSettings } | { RigidModel: RigidModel }
  | { SoundBank: unknown } | { Text: Text }
  | { UIC: UIC } | { UnitVariant: UnitVariant }
  | { Unknown: unknown } | { Video: unknown }
  | { VMD: Text } | { WSModel: Text };
// Tagged enum — one wrapper class per variant. Only the most common ones shown.
public abstract class RFileDecoded { }
public class RFileDecodedDB : RFileDecoded { public DB DB { get; set; } }
public class RFileDecodedLoc : RFileDecoded { public Loc Loc { get; set; } }
public class RFileDecodedText : RFileDecoded { public Text Text { get; set; } }
public class RFileDecodedImage : RFileDecoded { public Image Image { get; set; } }
public class RFileDecodedRigidModel : RFileDecoded { public RigidModel RigidModel { get; set; } }
// ... one class per variant listed in the enum above.

TableReferences

Reference data for a column, used by lookup/autocomplete features.

FieldTypeDescription
field_namestringName of the column these references are for
referenced_table_is_ak_onlybooleanWhether the referenced table only exists in the AK
referenced_column_is_localisedbooleanWhether the referenced column is localised
dataRecord<string, string>Map of actual values to their display text
interface TableReferences {
  field_name: string;
  referenced_table_is_ak_only: boolean;
  referenced_column_is_localised: boolean;
  data: Record<string, string>;
}
public class TableReferences
{
    public string FieldName { get; set; }
    public bool ReferencedTableIsAkOnly { get; set; }
    public bool ReferencedColumnIsLocalised { get; set; }
    public Dictionary<string, string> Data { get; set; }
}

Decoded File Types

DB

Decoded database table file.

FieldTypeDescription
mysterious_bytebooleanBoolean flag (setting to 0 can crash WH2)
guidstringGUID for this table instance (empty for older games)
tableTableInMemoryThe table data including definition and rows
interface DB {
  mysterious_byte: boolean;
  guid: string;
  table: TableInMemory;
}
public class DB
{
    public bool MysteriousByte { get; set; }
    public string Guid { get; set; }
    public TableInMemory Table { get; set; }
}

Loc

Decoded localisation file.

FieldTypeDescription
tableTableInMemoryTable data with key, text, and tooltip columns
interface Loc {
  table: TableInMemory;
}
public class Loc
{
    public TableInMemory Table { get; set; }
}

Text

Decoded text file.

FieldTypeDescription
encodingTextEncodingCharacter encoding of the file
formatTextFormatDetected file format
contentsstringDecoded text contents
interface Text {
  encoding: TextEncoding;
  format: TextFormat;
  contents: string;
}
public class Text
{
    public TextEncoding Encoding { get; set; }
    public TextFormat Format { get; set; }
    public string Contents { get; set; }
}

TextEncoding

Character encoding of a text file’s contents. Serialized as a plain string:

ValueDescription
"Iso8859_1"ISO/IEC 8859-1 (Latin-1)
"Utf8"UTF-8 without BOM
"Utf8Bom"UTF-8 with BOM
"Utf16Le"UTF-16 little-endian

Rust source name: Encoding (in rpfm_lib::files::text). This doc uses the more descriptive name TextEncoding for clarity — the on-the-wire JSON is identical.

type TextEncoding = "Iso8859_1" | "Utf8" | "Utf8Bom" | "Utf16Le";
public enum TextEncoding { Iso8859_1, Utf8, Utf8Bom, Utf16Le }

TextFormat

Source-code format / syntax-highlighting language for a text file. Also used as the second element of the NewFile.Text payload. Serialized as a plain string:

ValueDescription
"Bat"Windows batch file
"Cpp"C / C++
"Css"CSS
"Hlsl"HLSL shader
"Html"HTML
"Js"JavaScript
"Json"JSON
"Lua"Lua
"Markdown"Markdown
"Plain"Plain text (no specific format)
"Python"Python
"Sql"SQL
"Xml"XML
"Yaml"YAML
type TextFormat =
  | "Bat" | "Cpp" | "Html" | "Hlsl" | "Json" | "Js" | "Css"
  | "Lua" | "Markdown" | "Plain" | "Python" | "Sql" | "Xml" | "Yaml";
public enum TextFormat { Bat, Cpp, Html, Hlsl, Json, Js, Css, Lua, Markdown, Plain, Python, Sql, Xml, Yaml }

Image

Decoded image file.

FieldTypeDescription
datanumber[]Original raw image data in native format
converted_datanumber[] or nullPNG-converted data for DDS textures (for viewing)
interface Image {
  data: number[];
  converted_data: number[] | null;
}
public class Image
{
    public byte[] Data { get; set; }
    public byte[]? ConvertedData { get; set; }
}

RigidModel

Decoded RigidModel (3D model) file.

FieldTypeDescription
versionnumberFile format version (6, 7, or 8)
uk_1numberUnknown field
skeleton_idstringSkeleton identifier for animation (empty if static)
lodsunknown[]LOD structures from highest to lowest quality
interface RigidModel {
  version: number;
  uk_1: number;
  skeleton_id: string;
  lods: unknown[];
}
public class RigidModel
{
    public uint Version { get; set; }
    public uint Uk1 { get; set; }
    public string SkeletonId { get; set; }
    public List<object> Lods { get; set; }
}

ESF

Decoded ESF (Empire Save Format) file.

FieldTypeDescription
signaturestringFormat signature (CAAB, CBAB, etc.)
unknown_1numberUnknown header field, typically 0
creation_datenumberCreation timestamp
root_nodeunknownRoot node of the data tree
interface ESF {
  signature: string;
  unknown_1: number;
  creation_date: number;
  root_node: unknown;
}
public class ESF
{
    public string Signature { get; set; }
    public uint Unknown1 { get; set; }
    public uint CreationDate { get; set; }
    public object RootNode { get; set; }
}

Bmd

Decoded BMD (Battle Map Data) file.

FieldTypeDescription
serialise_versionnumberFile format version (23-27)
(other fields)unknownComplex battlefield-related data
// Only the stable header field is typed; the rest is format-specific and opaque.
interface Bmd {
  serialise_version: number;
  [other: string]: unknown;
}
public class Bmd
{
    public uint SerialiseVersion { get; set; }
    // plus format-specific fields depending on serialise_version
}

AnimFragmentBattle

Decoded AnimFragmentBattle file.

FieldTypeDescription
versionnumberFile format version (2 or 4)
entriesunknown[]List of animation entries
skeleton_namestringName of the skeleton
subversionnumberFormat subversion (version 4 only)
interface AnimFragmentBattle {
  version: number;
  entries: unknown[];
  skeleton_name: string;
  subversion: number;
}
public class AnimFragmentBattle
{
    public uint Version { get; set; }
    public List<object> Entries { get; set; }
    public string SkeletonName { get; set; }
    public uint Subversion { get; set; }
}

AnimsTable

Decoded AnimsTable file.

FieldTypeDescription
versionnumberFile format version (currently 2)
entriesunknown[]List of animation table entries
interface AnimsTable {
  version: number;
  entries: unknown[];
}
public class AnimsTable
{
    public uint Version { get; set; }
    public List<object> Entries { get; set; }
}

Atlas

Decoded Atlas (sprite sheet) file.

FieldTypeDescription
versionnumberFile format version (currently 1)
unknownnumberUnknown field
entriesunknown[]List of sprite entries
interface Atlas {
  version: number;
  unknown: number;
  entries: unknown[];
}
public class Atlas
{
    public uint Version { get; set; }
    public uint Unknown { get; set; }
    public List<object> Entries { get; set; }
}

Audio

Decoded Audio file.

FieldTypeDescription
datanumber[]Raw binary audio data
interface Audio {
  data: number[];
}
public class Audio
{
    public byte[] Data { get; set; }
}

GroupFormations

Decoded GroupFormations file.

FieldTypeDescription
formationsunknown[]List of formation definitions
interface GroupFormations {
  formations: unknown[];
}
public class GroupFormations
{
    public List<object> Formations { get; set; }
}

MatchedCombat

Decoded MatchedCombat file.

FieldTypeDescription
versionnumberFile format version (1 or 3)
entriesunknown[]List of matched combat entries
interface MatchedCombat {
  version: number;
  entries: unknown[];
}
public class MatchedCombat
{
    public uint Version { get; set; }
    public List<object> Entries { get; set; }
}

PortraitSettings

Decoded PortraitSettings file.

FieldTypeDescription
versionnumberFormat version (1 or 4)
entriesunknown[]Portrait entries, one per art set
interface PortraitSettings {
  version: number;
  entries: unknown[];
}
public class PortraitSettings
{
    public uint Version { get; set; }
    public List<object> Entries { get; set; }
}

UIC

Decoded UIC (UI Component) file.

FieldTypeDescription
versionnumberFormat version number
source_is_xmlbooleanWhether decoded from XML (true) or binary (false)
commentstringOptional comment/description
precache_conditionstringCondition for precaching
hierarchyRecord<string, unknown>Tree structure of UI element relationships
componentsRecord<string, unknown>Map of component IDs to definitions
interface UIC {
  version: number;
  source_is_xml: boolean;
  comment: string;
  precache_condition: string;
  hierarchy: Record<string, unknown>;
  components: Record<string, unknown>;
}
public class UIC
{
    public uint Version { get; set; }
    public bool SourceIsXml { get; set; }
    public string Comment { get; set; }
    public string PrecacheCondition { get; set; }
    public Dictionary<string, object> Hierarchy { get; set; }
    public Dictionary<string, object> Components { get; set; }
}

UnitVariant

Decoded UnitVariant file.

FieldTypeDescription
versionnumberVersion of the UnitVariant
unknown_1numberUnknown field
categoriesunknown[]Variant categories
interface UnitVariant {
  version: number;
  unknown_1: number;
  categories: unknown[];
}
public class UnitVariant
{
    public uint Version { get; set; }
    public uint Unknown1 { get; set; }
    public List<object> Categories { get; set; }
}

Schema Types

FieldType

The data type of a field in a schema definition. Most values are plain strings; sequence types wrap a nested Definition:

ValueDescription
"Boolean"Boolean value
"F32"32-bit float
"F64"64-bit float
"I16"16-bit signed integer
"I32"32-bit signed integer
"I64"64-bit signed integer
"ColourRGB"RGB colour value
"StringU8"UTF-8 string (length-prefixed u8)
"StringU16"UTF-16 string (length-prefixed u16)
"OptionalI16"Optional 16-bit integer
"OptionalI32"Optional 32-bit integer
"OptionalI64"Optional 64-bit integer
"OptionalStringU8"Optional UTF-8 string
"OptionalStringU16"Optional UTF-16 string
{ "SequenceU16": Definition }Nested sequence (u16 count)
{ "SequenceU32": Definition }Nested sequence (u32 count)
type FieldType =
  | "Boolean" | "F32" | "F64" | "I16" | "I32" | "I64"
  | "ColourRGB" | "StringU8" | "StringU16"
  | "OptionalI16" | "OptionalI32" | "OptionalI64"
  | "OptionalStringU8" | "OptionalStringU16"
  | { SequenceU16: Definition }
  | { SequenceU32: Definition };
// Mix of plain-string and tagged-enum variants. Model it as a tagged union:
public abstract class FieldType { }
public class FieldTypePrimitive : FieldType { public string Kind { get; set; } }  // e.g. "I32"
public class FieldTypeSequenceU16 : FieldType { public Definition SequenceU16 { get; set; } }
public class FieldTypeSequenceU32 : FieldType { public Definition SequenceU32 { get; set; } }

Field

A single field descriptor within a Definition.

FieldTypeDescription
namestringField name
field_typeFieldTypeData type
is_keybooleanPart of the table’s primary key
default_valuestring or nullDefault value for new rows
is_filenamebooleanWhether this field contains a filename/path
filename_relative_pathstring or nullSemicolon-separated relative paths for file lookup
is_reference[string, string] or nullForeign key: [table_name, column_name]
lookupstring[] or nullAdditional columns to show from the referenced table
descriptionstringHuman-readable description
ca_ordernumberPosition in CA’s Assembly Kit editor (-1 = unknown)
is_bitwisenumberNumber of boolean columns to split this field into
enum_valuesRecord<number, string>Named enum values (integer key to string name)
is_part_of_colournumber or nullRGB colour group index (null if not a colour field)
interface Field {
  name: string;
  field_type: FieldType;
  is_key: boolean;
  default_value: string | null;
  is_filename: boolean;
  filename_relative_path: string | null;
  is_reference: [string, string] | null;
  lookup: string[] | null;
  description: string;
  ca_order: number;
  is_bitwise: number;
  enum_values: Record<number, string>;
  is_part_of_colour: number | null;
}
public class Field
{
    public string Name { get; set; }
    public FieldType FieldType { get; set; }
    public bool IsKey { get; set; }
    public string? DefaultValue { get; set; }
    public bool IsFilename { get; set; }
    public string? FilenameRelativePath { get; set; }
    public Tuple<string, string>? IsReference { get; set; }
    public List<string>? Lookup { get; set; }
    public string Description { get; set; }
    public short CaOrder { get; set; }
    public int IsBitwise { get; set; }
    public Dictionary<int, string> EnumValues { get; set; }
    public byte? IsPartOfColour { get; set; }
}

Definition

Schema definition for a specific version of a DB table.

FieldTypeDescription
versionnumberVersion number (-1 = fake, 0 = unversioned, 1+ = versioned)
fieldsField[]Fields in binary encoding order (see note below)
localised_fieldsField[]Fields extracted to LOC files during export
localised_key_ordernumber[]Order of key fields for constructing localisation keys

Note: The fields list is ordered to match the binary encoding of the table, which is not necessarily the order columns appear in the decoded/displayed data. To get fields in decoded column order (with bitwise expansion, enum conversion, and colour merging applied), use the FieldsProcessed command, passing the Definition as input.

interface Definition {
  version: number;
  fields: Field[];
  localised_fields: Field[];
  localised_key_order: number[];
}
public class Definition
{
    public int Version { get; set; }
    public List<Field> Fields { get; set; }
    public List<Field> LocalisedFields { get; set; }
    public List<uint> LocalisedKeyOrder { get; set; }
}

Schema

The full schema containing all table definitions for a game.

FieldTypeDescription
versionnumberSchema format version (currently 5)
definitionsRecord<string, Definition[]>Table name to version definitions
patchesRecord<string, DefinitionPatch>Table name to patches
interface Schema {
  version: number;
  definitions: Record<string, Definition[]>;
  patches: Record<string, DefinitionPatch>;
}
public class Schema
{
    public ushort Version { get; set; }
    public Dictionary<string, List<Definition>> Definitions { get; set; }
    public Dictionary<string, DefinitionPatch> Patches { get; set; }
}

DefinitionPatch

A patch applied to a schema definition. Serialized as a nested map:

{
  "field_name": {
    "property_name": "property_value"
  }
}

Type: Record<string, Record<string, string>>

type DefinitionPatch = Record<string, Record<string, string>>;
// DefinitionPatch is a type alias for the nested map:
using DefinitionPatch = System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<string, string>>;

Pack Settings Types

PackSettings

Per-pack configuration stored inside the Pack file.

FieldTypeDescription
settings_textRecord<string, string>Multi-line text settings (e.g., ignore lists)
settings_stringRecord<string, string>Single-line string settings
settings_boolRecord<string, boolean>Boolean flags
settings_numberRecord<string, number>Integer settings
interface PackSettings {
  settings_text: Record<string, string>;
  settings_string: Record<string, string>;
  settings_bool: Record<string, boolean>;
  settings_number: Record<string, number>;
}
public class PackSettings
{
    public Dictionary<string, string> SettingsText { get; set; }
    public Dictionary<string, string> SettingsString { get; set; }
    public Dictionary<string, bool> SettingsBool { get; set; }
    public Dictionary<string, int> SettingsNumber { get; set; }
}

Note

A note attached to a path within the Pack file.

FieldTypeDescription
idnumberUnique note identifier
messagestringNote content/body
urlstring or nullOptional URL associated with the note
pathstringPath within the Pack (empty string = global)
interface Note {
  id: number;
  message: string;
  url: string | null;
  path: string;
}
public class Note
{
    public ulong Id { get; set; }
    public string Message { get; set; }
    public string? Url { get; set; }
    public string Path { get; set; }
}

OptimizerOptions

Configuration for the pack optimizer.

FieldTypeDescription
pack_remove_itm_filesbooleanRemove files unchanged from vanilla
db_import_datacores_into_twad_key_deletesbooleanImport datacored tables into twad_key_deletes
db_optimize_datacored_tablesbooleanOptimize datacored tables (not recommended)
table_remove_duplicated_entriesbooleanRemove duplicated rows from DB and Loc files
table_remove_itm_entriesbooleanRemove Identical To Master rows
table_remove_itnr_entriesbooleanRemove Identical To New Row rows
table_remove_empty_filebooleanRemove empty DB and Loc files
text_remove_unused_xml_map_foldersbooleanRemove unused XML files in map folders
text_remove_unused_xml_prefab_folderbooleanRemove unused XML files in the prefab folder
text_remove_agf_filesbooleanRemove unused AGF files
text_remove_model_statistics_filesbooleanRemove unused model_statistics files
pts_remove_unused_art_setsbooleanRemove unused art sets in Portrait Settings
pts_remove_unused_variantsbooleanRemove unused variants from Portrait Settings art sets
pts_remove_empty_masksbooleanRemove empty masks in Portrait Settings
pts_remove_empty_filebooleanRemove empty Portrait Settings files
interface OptimizerOptions {
  pack_remove_itm_files: boolean;
  db_import_datacores_into_twad_key_deletes: boolean;
  db_optimize_datacored_tables: boolean;
  table_remove_duplicated_entries: boolean;
  table_remove_itm_entries: boolean;
  table_remove_itnr_entries: boolean;
  table_remove_empty_file: boolean;
  text_remove_unused_xml_map_folders: boolean;
  text_remove_unused_xml_prefab_folder: boolean;
  text_remove_agf_files: boolean;
  text_remove_model_statistics_files: boolean;
  pts_remove_unused_art_sets: boolean;
  pts_remove_unused_variants: boolean;
  pts_remove_empty_masks: boolean;
  pts_remove_empty_file: boolean;
}
public class OptimizerOptions
{
    public bool PackRemoveItmFiles { get; set; }
    public bool DbImportDatacoresIntoTwadKeyDeletes { get; set; }
    public bool DbOptimizeDatacoredTables { get; set; }
    public bool TableRemoveDuplicatedEntries { get; set; }
    public bool TableRemoveItmEntries { get; set; }
    public bool TableRemoveItnrEntries { get; set; }
    public bool TableRemoveEmptyFile { get; set; }
    public bool TextRemoveUnusedXmlMapFolders { get; set; }
    public bool TextRemoveUnusedXmlPrefabFolder { get; set; }
    public bool TextRemoveAgfFiles { get; set; }
    public bool TextRemoveModelStatisticsFiles { get; set; }
    public bool PtsRemoveUnusedArtSets { get; set; }
    public bool PtsRemoveUnusedVariants { get; set; }
    public bool PtsRemoveEmptyMasks { get; set; }
    public bool PtsRemoveEmptyFile { get; set; }
}

SettingsSnapshot

A full batch of settings. Returned by the SettingsGetAll command — much cheaper than round-tripping per-key getters when you need many settings at once. Each field is a map from setting key to value for one primitive type.

FieldTypeDescription
boolRecord<string, boolean>Boolean settings
i32Record<string, number>Signed 32-bit integer settings
f32Record<string, number>32-bit float settings
stringRecord<string, string>String settings
raw_dataRecord<string, number[]>Raw byte-array settings
vec_stringRecord<string, string[]>String-list settings
interface SettingsSnapshot {
  bool: Record<string, boolean>;
  i32: Record<string, number>;
  f32: Record<string, number>;
  string: Record<string, string>;
  raw_data: Record<string, number[]>;
  vec_string: Record<string, string[]>;
}
public class SettingsSnapshot
{
    public Dictionary<string, bool> Bool { get; set; }
    public Dictionary<string, int> I32 { get; set; }
    public Dictionary<string, float> F32 { get; set; }
    public Dictionary<string, string> String { get; set; }
    public Dictionary<string, byte[]> RawData { get; set; }
    public Dictionary<string, List<string>> VecString { get; set; }
}

Translation Types

Translation

A single translation entry for a Loc key.

FieldTypeDescription
keystringThe Loc key identifying this string
value_originalstringOriginal text in the base language
value_translatedstringTranslated text in the target language
needs_retranslationbooleanWhether the source text has changed since translation
removedbooleanWhether this string has been removed from the source pack
interface Translation {
  key: string;
  value_original: string;
  value_translated: string;
  needs_retranslation: boolean;
  removed: boolean;
}
public class Translation
{
    public string Key { get; set; }
    public string ValueOriginal { get; set; }
    public string ValueTranslated { get; set; }
    public bool NeedsRetranslation { get; set; }
    public bool Removed { get; set; }
}

PackTranslation

Translation data for a pack in a specific language.

FieldTypeDescription
languagestringTarget language code (e.g. "es", "de")
pack_namestringName of the pack
translationsRecord<string, Translation>Loc key to translation data
interface PackTranslation {
  language: string;
  pack_name: string;
  translations: Record<string, Translation>;
}
public class PackTranslation
{
    public string Language { get; set; }
    public string PackName { get; set; }
    public Dictionary<string, Translation> Translations { get; set; }
}

Diagnostics Types

Diagnostics

Diagnostics report configuration and results.

FieldTypeDescription
folders_ignoredstring[]Folder paths excluded from checks
files_ignoredstring[]File paths excluded from checks
fields_ignoredstring[]Table fields excluded ("table_name/field_name")
diagnostics_ignoredstring[]Diagnostic type identifiers to skip
resultsDiagnosticType[]Diagnostic results from the most recent check
interface Diagnostics {
  folders_ignored: string[];
  files_ignored: string[];
  fields_ignored: string[];
  diagnostics_ignored: string[];
  results: DiagnosticType[];
}
public class Diagnostics
{
    public List<string> FoldersIgnored { get; set; }
    public List<string> FilesIgnored { get; set; }
    public List<string> FieldsIgnored { get; set; }
    public List<string> DiagnosticsIgnored { get; set; }
    public List<DiagnosticType> Results { get; set; }
}

DiagnosticType

A single diagnostic result. Serialized as a tagged enum — the variant name indicates which kind of diagnostic fired:

VariantPayloadDescription
AnimFragmentBattleAnimFragmentBattleDiagnosticIssues in an AnimFragmentBattle file
ConfigConfigDiagnosticPack-level configuration issue
DependencyDependencyDiagnosticBroken/missing dependency reference
DBTableDiagnosticIssue in a DB table
LocTableDiagnosticIssue in a Loc file
PackPackDiagnosticPack-wide issue
PortraitSettingsPortraitSettingsDiagnosticIssue in a PortraitSettings file
TextTextDiagnosticIssue in a text file

Each payload is a struct with at least path: string and results: <SpecificDiagnosticReport>[] (the concrete inner fields vary by diagnostic kind and are treated as opaque by this doc). The full structures live in rpfm_extensions::diagnostics — e.g. TableDiagnostic, PackDiagnostic, ConfigDiagnostic, and so on. Clients that only need to display diagnostic results can round-trip the JSON without decoding these payloads.

// The inner payloads are left opaque here — their shape depends on the diagnostic kind.
type DiagnosticType =
  | { AnimFragmentBattle: unknown }
  | { Config: unknown }
  | { Dependency: unknown }
  | { DB: unknown }
  | { Loc: unknown }
  | { Pack: unknown }
  | { PortraitSettings: unknown }
  | { Text: unknown };
public abstract class DiagnosticType { }
public class DiagnosticTypeAnimFragmentBattle : DiagnosticType { public object AnimFragmentBattle { get; set; } }
public class DiagnosticTypeConfig             : DiagnosticType { public object Config { get; set; } }
public class DiagnosticTypeDependency         : DiagnosticType { public object Dependency { get; set; } }
public class DiagnosticTypeDB                 : DiagnosticType { public object DB { get; set; } }
public class DiagnosticTypeLoc                : DiagnosticType { public object Loc { get; set; } }
public class DiagnosticTypePack               : DiagnosticType { public object Pack { get; set; } }
public class DiagnosticTypePortraitSettings   : DiagnosticType { public object PortraitSettings { get; set; } }
public class DiagnosticTypeText               : DiagnosticType { public object Text { get; set; } }

Update Types

APIResponse

Response from a program update check. Serialized as a tagged enum:

VariantPayloadDescription
NewBetaUpdatestringNew beta version available
NewStableUpdatestringNew stable version available
NewUpdateHotfixstringNew hotfix available
NoUpdate(none)Already up to date
UnknownVersion(none)Current version could not be determined
type APIResponse =
  | { NewBetaUpdate: string }
  | { NewStableUpdate: string }
  | { NewUpdateHotfix: string }
  | "NoUpdate"
  | "UnknownVersion";
// Serialize as either a wrapped object { "<Variant>": "version" } or a bare string.
public abstract class APIResponse { }
public class APIResponseNewBetaUpdate   : APIResponse { public string NewBetaUpdate { get; set; } }
public class APIResponseNewStableUpdate : APIResponse { public string NewStableUpdate { get; set; } }
public class APIResponseNewUpdateHotfix : APIResponse { public string NewUpdateHotfix { get; set; } }
public class APIResponseNoUpdate        : APIResponse { }
public class APIResponseUnknownVersion  : APIResponse { }

GitResponse

Response from a git-based update check (schemas, translations, etc.):

ValueDescription
"NewUpdate"A new update is available on the remote
"NoUpdate"The local repository is up to date
"NoLocalFiles"No local copy exists (needs cloning)
"Diverged"Local and remote branches have diverged
type GitResponse = "NewUpdate" | "NoUpdate" | "NoLocalFiles" | "Diverged";
public enum GitResponse { NewUpdate, NoUpdate, NoLocalFiles, Diverged }

Search Types

SearchSource

Which data source to search. Serialized as a tagged enum — the Pack variant carries a pack key, the rest are plain strings:

VariantPayloadDescription
PackstringA specific open pack, identified by pack key
ParentFiles(none)Parent mod dependencies
GameFiles(none)Vanilla game files
AssKitFiles(none)Assembly Kit files

Example:

{ "Pack": "my_mod.pack" }
"GameFiles"
type SearchSource =
  | { Pack: string }
  | "ParentFiles"
  | "GameFiles"
  | "AssKitFiles";
// Serialize as either { "Pack": "pack_key" } or a bare string.
public abstract class SearchSource { }
public class SearchSourcePack        : SearchSource { public string Pack { get; set; } }
public class SearchSourceParentFiles : SearchSource { }
public class SearchSourceGameFiles   : SearchSource { }
public class SearchSourceAssKitFiles : SearchSource { }

SearchOn

Boolean flags for which file types to include in a search. Each field corresponds to a file type:

anim, anim_fragment_battle, anim_pack, anims_table, atlas, audio, bmd, db, esf, group_formations, image, loc, matched_combat, pack, portrait_settings, rigid_model, sound_bank, text, uic, unit_variant, unknown, video, schema

All fields are boolean.

interface SearchOn {
  anim: boolean;
  anim_fragment_battle: boolean;
  anim_pack: boolean;
  anims_table: boolean;
  atlas: boolean;
  audio: boolean;
  bmd: boolean;
  db: boolean;
  esf: boolean;
  group_formations: boolean;
  image: boolean;
  loc: boolean;
  matched_combat: boolean;
  pack: boolean;
  portrait_settings: boolean;
  rigid_model: boolean;
  sound_bank: boolean;
  text: boolean;
  uic: boolean;
  unit_variant: boolean;
  unknown: boolean;
  video: boolean;
  schema: boolean;
}
public class SearchOn
{
    public bool Anim { get; set; }
    public bool AnimFragmentBattle { get; set; }
    public bool AnimPack { get; set; }
    public bool AnimsTable { get; set; }
    public bool Atlas { get; set; }
    public bool Audio { get; set; }
    public bool Bmd { get; set; }
    public bool Db { get; set; }
    public bool Esf { get; set; }
    public bool GroupFormations { get; set; }
    public bool Image { get; set; }
    public bool Loc { get; set; }
    public bool MatchedCombat { get; set; }
    public bool Pack { get; set; }
    public bool PortraitSettings { get; set; }
    public bool RigidModel { get; set; }
    public bool SoundBank { get; set; }
    public bool Text { get; set; }
    public bool Uic { get; set; }
    public bool UnitVariant { get; set; }
    public bool Unknown { get; set; }
    public bool Video { get; set; }
    public bool Schema { get; set; }
}

GlobalSearch

Global search configuration and results.

FieldTypeDescription
patternstringText pattern or regex to search for
replace_textstringReplacement text
case_sensitivebooleanWhether the search is case-sensitive
use_regexbooleanWhether the pattern is a regular expression
sourcesSearchSource[]One or more data sources to search
search_onSearchOnWhich file types to search
matchesMatchesResults from the most recent search
game_keystringGame key for the files being searched
interface GlobalSearch {
  pattern: string;
  replace_text: string;
  case_sensitive: boolean;
  use_regex: boolean;
  sources: SearchSource[];
  search_on: SearchOn;
  matches: Matches;
  game_key: string;
}
public class GlobalSearch
{
    public string Pattern { get; set; }
    public string ReplaceText { get; set; }
    public bool CaseSensitive { get; set; }
    public bool UseRegex { get; set; }
    public List<SearchSource> Sources { get; set; }
    public SearchOn SearchOn { get; set; }
    public Matches Matches { get; set; }
    public string GameKey { get; set; }
}

Matches

The results of a global search, grouped by file type. Each field is an array of per-type match containers (defined in the next section) — except schema, which is a single SchemaMatches container.

FieldTypeDescription
animUnknownMatches[]Matches in animation files
anim_fragment_battleAnimFragmentBattleMatches[]Matches in battle animation fragments
anim_packUnknownMatches[]Matches in animation packs
anims_tableUnknownMatches[]Matches in animation tables
atlasAtlasMatches[]Matches in atlases
audioUnknownMatches[]Matches in audio files
bmdUnknownMatches[]Matches in BMD files
dbTableMatches[]Matches in DB tables
esfUnknownMatches[]Matches in ESF files
group_formationsUnknownMatches[]Matches in group formations
imageUnknownMatches[]Matches in images
locTableMatches[]Matches in Loc tables
matched_combatUnknownMatches[]Matches in matched combat files
packUnknownMatches[]Matches in nested Pack files
portrait_settingsPortraitSettingsMatches[]Matches in portrait settings
rigid_modelRigidModelMatches[]Matches in RigidModel files
sound_bankUnknownMatches[]Matches in sound banks
textTextMatches[]Matches in text files (Lua, XML, …)
uicUnknownMatches[]Matches in UIC files
unit_variantUnitVariantMatches[]Matches in UnitVariant files
unknownUnknownMatches[]Matches in unclassified files
videoUnknownMatches[]Matches in video files
schemaSchemaMatchesMatches in the schema (single container, not an array)
interface Matches {
  anim: UnknownMatches[];
  anim_fragment_battle: AnimFragmentBattleMatches[];
  anim_pack: UnknownMatches[];
  anims_table: UnknownMatches[];
  atlas: AtlasMatches[];
  audio: UnknownMatches[];
  bmd: UnknownMatches[];
  db: TableMatches[];
  esf: UnknownMatches[];
  group_formations: UnknownMatches[];
  image: UnknownMatches[];
  loc: TableMatches[];
  matched_combat: UnknownMatches[];
  pack: UnknownMatches[];
  portrait_settings: PortraitSettingsMatches[];
  rigid_model: RigidModelMatches[];
  sound_bank: UnknownMatches[];
  text: TextMatches[];
  uic: UnknownMatches[];
  unit_variant: UnitVariantMatches[];
  unknown: UnknownMatches[];
  video: UnknownMatches[];
  schema: SchemaMatches;
}
public class Matches
{
    public List<MatchContainer<UnknownMatch>> Anim { get; set; }
    public List<MatchContainer<AnimFragmentBattleMatch>> AnimFragmentBattle { get; set; }
    public List<MatchContainer<UnknownMatch>> AnimPack { get; set; }
    public List<MatchContainer<UnknownMatch>> AnimsTable { get; set; }
    public List<MatchContainer<AtlasMatch>> Atlas { get; set; }
    public List<MatchContainer<UnknownMatch>> Audio { get; set; }
    public List<MatchContainer<UnknownMatch>> Bmd { get; set; }
    public List<MatchContainer<TableMatch>> Db { get; set; }
    public List<MatchContainer<UnknownMatch>> Esf { get; set; }
    public List<MatchContainer<UnknownMatch>> GroupFormations { get; set; }
    public List<MatchContainer<UnknownMatch>> Image { get; set; }
    public List<MatchContainer<TableMatch>> Loc { get; set; }
    public List<MatchContainer<UnknownMatch>> MatchedCombat { get; set; }
    public List<MatchContainer<UnknownMatch>> Pack { get; set; }
    public List<MatchContainer<PortraitSettingsMatch>> PortraitSettings { get; set; }
    public List<MatchContainer<RigidModelMatch>> RigidModel { get; set; }
    public List<MatchContainer<UnknownMatch>> SoundBank { get; set; }
    public List<MatchContainer<TextMatch>> Text { get; set; }
    public List<MatchContainer<UnknownMatch>> Uic { get; set; }
    public List<MatchContainer<UnitVariantMatch>> UnitVariant { get; set; }
    public List<MatchContainer<UnknownMatch>> Unknown { get; set; }
    public List<MatchContainer<UnknownMatch>> Video { get; set; }
    public MatchContainer<SchemaMatch> Schema { get; set; }
}

Match Types

Search results use specialized match types per file format. All match containers share the same wrapper shape:

FieldTypeDescription
pathstringInternal path of the file the matches came from
sourceSearchSourceData source the file was searched in
container_namestringName of the containing Pack (or empty)
matches<Match>[]Match entries — type depends on file kind

TableMatch (used for DB and Loc files):

FieldTypeDescription
column_namestringColumn where the match is
column_numbernumberLogical column index (-1 if hidden)
row_numbernumberRow number (-1 if hidden by filter)
startnumberByte offset where match starts
endnumberByte offset where match ends
textstringContents of the matched cell

TextMatch (used for text files):

FieldTypeDescription
rownumberRow of the first character
startnumberByte offset where match starts
endnumberByte offset where match ends
textstringLine containing the match

UnknownMatch (used for binary/unknown files):

FieldTypeDescription
posnumberByte position of the match
lennumberLength of the matched pattern in bytes

AnimFragmentBattleMatch (used for AnimFragmentBattle files):

FieldTypeDescription
skeleton_namebooleanMatch is in the skeleton name
table_namebooleanMatch is in the table name
mount_table_namebooleanMatch is in the mount table name
unmount_table_namebooleanMatch is in the unmount table name
locomotion_graphbooleanMatch is in the locomotion graph
entry[number, [number, boolean, boolean, boolean] or null, boolean, boolean, boolean, boolean, boolean] or nullEntry match details
startnumberByte offset where match starts
endnumberByte offset where match ends
textstringThe matched text

AtlasMatch (used for Atlas files — same structure as TableMatch):

FieldTypeDescription
column_namestringColumn where the match is
column_numbernumberLogical column index
row_numbernumberRow number of the match
startnumberByte offset where match starts
endnumberByte offset where match ends
textstringContents of the matched cell

PortraitSettingsMatch (used for PortraitSettings files):

FieldTypeDescription
entrynumberIndex of the entry
idbooleanMatch is in the id field
camera_settings_headbooleanMatch is in head camera skeleton node
camera_settings_bodybooleanMatch is in body camera skeleton node
variant[number, boolean, boolean, boolean, boolean, boolean] or nullVariant match details
startnumberByte offset where match starts
endnumberByte offset where match ends
textstringThe matched text

RigidModelMatch (used for RigidModel files):

FieldTypeDescription
skeleton_idbooleanMatch is in the skeleton id
mesh_value[number, number] or nullLOD and mesh index, or null
mesh_namebooleanMatch is in the mesh name
mesh_mat_namebooleanMatch is in the material name
mesh_textute_directorybooleanMatch is in the texture directory
mesh_filtersbooleanMatch is in the mesh filters
mesh_att_point_namenumber or nullAttachment point index with match
mesh_texture_pathnumber or nullTexture path index with match
startnumberByte offset where match starts
endnumberByte offset where match ends
textstringThe matched text

UnitVariantMatch (used for UnitVariant files):

FieldTypeDescription
entrynumberIndex of the entry
namebooleanMatch is in the name
variant[number, boolean, boolean] or nullVariant match details
startnumberByte offset where match starts
endnumberByte offset where match ends
textstringThe matched text

SchemaMatch (used for schema searches):

FieldTypeDescription
table_namestringThe table name
versionnumberVersion of the matched definition
columnnumberColumn index of the match
column_namestringFull name of the matched column

All match container types (TableMatches, TextMatches, AnimFragmentBattleMatches, etc.) use the wrapper shape from the top of this section — { path, source, container_name, matches } — with matches being an array of the corresponding per-type match struct.

interface TableMatch {
  column_name: string;
  column_number: number;
  row_number: number;
  start: number;
  end: number;
  text: string;
}

interface TextMatch {
  row: number;
  start: number;
  end: number;
  text: string;
}

interface UnknownMatch {
  pos: number;
  len: number;
}

interface AnimFragmentBattleMatch {
  skeleton_name: boolean;
  table_name: boolean;
  mount_table_name: boolean;
  unmount_table_name: boolean;
  locomotion_graph: boolean;
  entry:
    | [number, [number, boolean, boolean, boolean] | null, boolean, boolean, boolean, boolean, boolean]
    | null;
  start: number;
  end: number;
  text: string;
}

interface AtlasMatch extends TableMatch {}  // same shape as TableMatch

interface PortraitSettingsMatch {
  entry: number;
  id: boolean;
  camera_settings_head: boolean;
  camera_settings_body: boolean;
  variant: [number, boolean, boolean, boolean, boolean, boolean] | null;
  start: number;
  end: number;
  text: string;
}

interface RigidModelMatch {
  skeleton_id: boolean;
  mesh_value: [number, number] | null;
  mesh_name: boolean;
  mesh_mat_name: boolean;
  mesh_textute_directory: boolean;
  mesh_filters: boolean;
  mesh_att_point_name: number | null;
  mesh_texture_path: number | null;
  start: number;
  end: number;
  text: string;
}

interface UnitVariantMatch {
  entry: number;
  name: boolean;
  variant: [number, boolean, boolean] | null;
  start: number;
  end: number;
  text: string;
}

interface SchemaMatch {
  table_name: string;
  version: number;
  column: number;
  column_name: string;
}

// The wrapper that groups matches for one file, used by every *Matches type:
interface MatchContainer<M> {
  path: string;
  source: SearchSource;
  container_name: string;
  matches: M[];
}
type TableMatches              = MatchContainer<TableMatch>;
type TextMatches               = MatchContainer<TextMatch>;
type UnknownMatches            = MatchContainer<UnknownMatch>;
type AnimFragmentBattleMatches = MatchContainer<AnimFragmentBattleMatch>;
type AtlasMatches              = MatchContainer<AtlasMatch>;
type PortraitSettingsMatches   = MatchContainer<PortraitSettingsMatch>;
type RigidModelMatches         = MatchContainer<RigidModelMatch>;
type UnitVariantMatches        = MatchContainer<UnitVariantMatch>;
type SchemaMatches             = MatchContainer<SchemaMatch>;
public class TableMatch
{
    public string ColumnName { get; set; }
    public uint ColumnNumber { get; set; }
    public long RowNumber { get; set; }
    public long Start { get; set; }
    public long End { get; set; }
    public string Text { get; set; }
}

public class TextMatch
{
    public ulong Row { get; set; }
    public long Start { get; set; }
    public long End { get; set; }
    public string Text { get; set; }
}

public class UnknownMatch
{
    public long Pos { get; set; }
    public long Len { get; set; }
}

// AtlasMatch has the same shape as TableMatch.
public class AtlasMatch : TableMatch { }

public class SchemaMatch
{
    public string TableName { get; set; }
    public int Version { get; set; }
    public int Column { get; set; }
    public string ColumnName { get; set; }
}

// AnimFragmentBattleMatch, PortraitSettingsMatch, RigidModelMatch and UnitVariantMatch
// follow the tables above; field-for-field translations are straightforward
// (booleans → bool, nullable tuples → Tuple<...>? or a named class).

// Wrapper shared by every *Matches type:
public class MatchContainer<M>
{
    public string Path { get; set; }
    public SearchSource Source { get; set; }
    public string ContainerName { get; set; }
    public List<M> Matches { get; set; }
}

MatchHolder

A tagged enum wrapping a single file type’s matches. The variant name indicates the file type:

{ "Db": { "path": "db/units_tables/data", "matches": [...] } }
{ "Text": { "path": "script/campaign/mod.lua", "matches": [...] } }

Variants: Anim, AnimFragmentBattle, AnimPack, AnimsTable, Atlas, Audio, Bmd, Db, Esf, GroupFormations, Image, Loc, MatchedCombat, Pack, PortraitSettings, RigidModel, SoundBank, Text, Uic, UnitVariant, Unknown, Video, Schema.

// Most variants carry an UnknownMatches; only the rich-formatted file types carry their own match container.
type MatchHolder =
  | { Anim: UnknownMatches }
  | { AnimFragmentBattle: AnimFragmentBattleMatches }
  | { AnimPack: UnknownMatches }
  | { AnimsTable: UnknownMatches }
  | { Atlas: AtlasMatches }
  | { Audio: UnknownMatches }
  | { Bmd: UnknownMatches }
  | { Db: TableMatches }
  | { Esf: UnknownMatches }
  | { GroupFormations: UnknownMatches }
  | { Image: UnknownMatches }
  | { Loc: TableMatches }
  | { MatchedCombat: UnknownMatches }
  | { Pack: UnknownMatches }
  | { PortraitSettings: PortraitSettingsMatches }
  | { RigidModel: RigidModelMatches }
  | { SoundBank: UnknownMatches }
  | { Text: TextMatches }
  | { Uic: UnknownMatches }
  | { UnitVariant: UnitVariantMatches }
  | { Unknown: UnknownMatches }
  | { Video: UnknownMatches }
  | { Schema: SchemaMatches };
// One wrapper class per variant. Only the most common ones shown — pattern is identical for the rest.
public abstract class MatchHolder { }
public class MatchHolderDb   : MatchHolder { public MatchContainer<TableMatch> Db { get; set; } }
public class MatchHolderLoc  : MatchHolder { public MatchContainer<TableMatch> Loc { get; set; } }
public class MatchHolderText : MatchHolder { public MatchContainer<TextMatch> Text { get; set; } }
public class MatchHolderSchema : MatchHolder { public MatchContainer<SchemaMatch> Schema { get; set; } }
// ... one class per variant listed above; most wrap MatchContainer<UnknownMatch>.

Commands

This page documents all commands that can be sent to the RPFM server. Each command is wrapped in a Message with a unique id.

Commands with no parameters are serialized as plain strings. Commands with parameters use the { "CommandName": params } format. See the serialization convention for details.

Most commands that operate on a specific pack take a pack_key string as their first parameter. The pack key is returned by OpenPackFiles or NewPack when you open or create a pack.


Lifecycle

Exit

Close the background thread. Do not use directly — the server manages this internally.

Response: None (breaks the server loop).

type ExitRequest = "Exit";
// No response — the server loop terminates.
// Request: send the literal string "Exit". No response.

ClientDisconnecting

Signal that the client is intentionally disconnecting. Allows the server to clean up the session immediately.

Response: "Success"

{ "id": 1, "data": "ClientDisconnecting" }
type ClientDisconnectingRequest = "ClientDisconnecting";
type ClientDisconnectingResponse = "Success";
// Request: send the literal string "ClientDisconnecting".
// Response: the literal string "Success".

PackFile Operations

NewPack

Create a new empty Pack.

Response: { String: string } — the assigned pack key.

{ "id": 1, "data": "NewPack" }
type NewPackRequest = "NewPack";
type NewPackResponse = { String: string };
// Request: send the literal string "NewPack".
public class NewPackResponse
{
    public string String { get; set; }
}

OpenPackFiles

Open one or more Pack files and merge them into the current session.

ParameterTypeDescription
pathsstring[]Filesystem paths to open

Response: { StringContainerInfo: [string, ContainerInfo] } — pack key and metadata.

{ "id": 1, "data": { "OpenPackFiles": ["/path/to/my_mod.pack"] } }
type OpenPackFilesRequest = { OpenPackFiles: string[] };
type OpenPackFilesResponse = { StringContainerInfo: [string, ContainerInfo] };
public class OpenPackFilesRequest
{
    public List<string> OpenPackFiles { get; set; }
}
public class OpenPackFilesResponse
{
    public Tuple<string, ContainerInfo> StringContainerInfo { get; set; }
}

LoadAllCAPackFiles

Open all CA Pack files for the selected game as one merged Pack.

Response: { StringContainerInfo: [string, ContainerInfo] }

{ "id": 1, "data": "LoadAllCAPackFiles" }
type LoadAllCAPackFilesRequest = "LoadAllCAPackFiles";
type LoadAllCAPackFilesResponse = { StringContainerInfo: [string, ContainerInfo] };
// Request: send the literal string "LoadAllCAPackFiles".
public class LoadAllCAPackFilesResponse
{
    public Tuple<string, ContainerInfo> StringContainerInfo { get; set; }
}

ListOpenPacks

List all currently open packs with their keys and metadata.

Response: { VecStringContainerInfo: [string, ContainerInfo][] }

{ "id": 1, "data": "ListOpenPacks" }
type ListOpenPacksRequest = "ListOpenPacks";
type ListOpenPacksResponse = { VecStringContainerInfo: [string, ContainerInfo][] };
// Request: send the literal string "ListOpenPacks".
public class ListOpenPacksResponse
{
    public List<Tuple<string, ContainerInfo>> VecStringContainerInfo { get; set; }
}

ClosePack

Close a specific open Pack.

ParameterTypeDescription
pack_keystringPack to close

Response: "Success"

{ "id": 1, "data": { "ClosePack": "my_mod.pack" } }
type ClosePackRequest = { ClosePack: string };
type ClosePackResponse = "Success";
public class ClosePackRequest
{
    public string ClosePack { get; set; }
}
// Response: the literal string "Success".

CloseAllPacks

Close all currently open Packs.

Response: "Success"

{ "id": 1, "data": "CloseAllPacks" }
type CloseAllPacksRequest = "CloseAllPacks";
type CloseAllPacksResponse = "Success";
// Request: send the literal string "CloseAllPacks".
// Response: the literal string "Success".

SavePack

Save a specific open Pack to disk.

ParameterTypeDescription
pack_keystringPack to save

Response: { ContainerInfo: ContainerInfo }

{ "id": 1, "data": { "SavePack": "my_mod.pack" } }
type SavePackRequest = { SavePack: string };
type SavePackResponse = { ContainerInfo: ContainerInfo };
public class SavePackRequest
{
    public string SavePack { get; set; }
}
public class SavePackResponse
{
    public ContainerInfo ContainerInfo { get; set; }
}

SavePackAs

Save a specific open Pack to a new path.

ParameterTypeDescription
pack_keystringPack to save
pathstringDestination path

Response: { ContainerInfo: ContainerInfo }

{ "id": 1, "data": { "SavePackAs": ["my_mod.pack", "/path/to/new_mod.pack"] } }
type SavePackAsRequest = { SavePackAs: [string, string] };
type SavePackAsResponse = { ContainerInfo: ContainerInfo };
public class SavePackAsRequest
{
    public Tuple<string, string> SavePackAs { get; set; }
}
public class SavePackAsResponse
{
    public ContainerInfo ContainerInfo { get; set; }
}

CleanAndSavePackAs

Clean a Pack from corrupted/undecoded files and save to disk. Only use if the Pack is otherwise unsaveable.

ParameterTypeDescription
pack_keystringPack to clean
pathstringDestination path

Response: { ContainerInfo: ContainerInfo }

{ "id": 1, "data": { "CleanAndSavePackAs": ["my_mod.pack", "/path/to/cleaned.pack"] } }
type CleanAndSavePackAsRequest = { CleanAndSavePackAs: [string, string] };
type CleanAndSavePackAsResponse = { ContainerInfo: ContainerInfo };
public class CleanAndSavePackAsRequest
{
    public Tuple<string, string> CleanAndSavePackAs { get; set; }
}
public class CleanAndSavePackAsResponse
{
    public ContainerInfo ContainerInfo { get; set; }
}

GetPackFileDataForTreeView

Get tree view data (container info and file list) for a specific pack.

ParameterTypeDescription
pack_keystringPack to query

Response: { ContainerInfoVecRFileInfo: [ContainerInfo, RFileInfo[]] }

{ "id": 1, "data": { "GetPackFileDataForTreeView": "my_mod.pack" } }
type GetPackFileDataForTreeViewRequest = { GetPackFileDataForTreeView: string };
type GetPackFileDataForTreeViewResponse = { ContainerInfoVecRFileInfo: [ContainerInfo, RFileInfo[]] };
public class GetPackFileDataForTreeViewRequest
{
    public string GetPackFileDataForTreeView { get; set; }
}
public class GetPackFileDataForTreeViewResponse
{
    public Tuple<ContainerInfo, List<RFileInfo>> ContainerInfoVecRFileInfo { get; set; }
}

GetPackedFilesInfo

Get metadata for one or more packed files by path.

ParameterTypeDescription
pack_keystringPack to query
pathsstring[]Internal file paths

Response: { VecRFileInfo: RFileInfo[] }

{ "id": 1, "data": { "GetPackedFilesInfo": ["my_mod.pack", ["db/units_tables/data"]] } }
type GetPackedFilesInfoRequest = { GetPackedFilesInfo: [string, string[]] };
type GetPackedFilesInfoResponse = { VecRFileInfo: RFileInfo[] };
public class GetPackedFilesInfoRequest
{
    public Tuple<string, List<string>> GetPackedFilesInfo { get; set; }
}
public class GetPackedFilesInfoResponse
{
    public List<RFileInfo> VecRFileInfo { get; set; }
}

GetRFileInfo

Get the info of a single packed file.

ParameterTypeDescription
pack_keystringPack to query
pathstringInternal file path

Response: { OptionRFileInfo: RFileInfo | null }

{ "id": 1, "data": { "GetRFileInfo": ["my_mod.pack", "db/units_tables/data"] } }
type GetRFileInfoRequest = { GetRFileInfo: [string, string] };
type GetRFileInfoResponse = { OptionRFileInfo: RFileInfo | null };
public class GetRFileInfoRequest
{
    public Tuple<string, string> GetRFileInfo { get; set; }
}
public class GetRFileInfoResponse
{
    public RFileInfo? OptionRFileInfo { get; set; }
}

GetPackFilePath

Get the filesystem path of a specific open Pack.

ParameterTypeDescription
pack_keystringPack to query

Response: { PathBuf: string }

type GetPackFilePathRequest = { GetPackFilePath: string };
type GetPackFilePathResponse = { PathBuf: string };
public class GetPackFilePathRequest
{
    public string GetPackFilePath { get; set; }
}
public class GetPackFilePathResponse
{
    public string PathBuf { get; set; }
}

GetPackFileName

Get the file name of a specific open Pack.

ParameterTypeDescription
pack_keystringPack to query

Response: { String: string }

type GetPackFileNameRequest = { GetPackFileName: string };
type GetPackFileNameResponse = { String: string };
public class GetPackFileNameRequest
{
    public string GetPackFileName { get; set; }
}
public class GetPackFileNameResponse
{
    public string String { get; set; }
}

SetPackFileType

Change the PFH type of a specific open Pack (e.g., Mod, Movie, Boot).

ParameterTypeDescription
pack_keystringPack to modify
typePFHFileTypeNew file type

Response: "Success"

type SetPackFileTypeRequest = { SetPackFileType: [string, PFHFileType] };
type SetPackFileTypeResponse = "Success";
public class SetPackFileTypeRequest
{
    public Tuple<string, string> SetPackFileType { get; set; }  // second is PFHFileType enum serialized as string
}
// Response: the literal string "Success".

ChangeIndexIncludesTimestamp

Toggle the “Index Includes Timestamp” flag for a specific pack.

ParameterTypeDescription
pack_keystringPack to modify
enabledbooleanNew flag value

Response: "Success"

type ChangeIndexIncludesTimestampRequest = { ChangeIndexIncludesTimestamp: [string, boolean] };
type ChangeIndexIncludesTimestampResponse = "Success";
public class ChangeIndexIncludesTimestampRequest
{
    public Tuple<string, bool> ChangeIndexIncludesTimestamp { get; set; }
}
// Response: the literal string "Success".

ChangeCompressionFormat

Change the compression format of a specific open Pack.

ParameterTypeDescription
pack_keystringPack to modify
formatCompressionFormatNew format

Response: { CompressionFormat: CompressionFormat } — actual format set (may differ if unsupported).

type ChangeCompressionFormatRequest = { ChangeCompressionFormat: [string, CompressionFormat] };
type ChangeCompressionFormatResponse = { CompressionFormat: CompressionFormat };
public class ChangeCompressionFormatRequest
{
    public Tuple<string, string> ChangeCompressionFormat { get; set; }  // second is CompressionFormat enum as string
}
public class ChangeCompressionFormatResponse
{
    public string CompressionFormat { get; set; }
}

OptimizePackFile

Run the optimizer over a specific open Pack.

ParameterTypeDescription
pack_keystringPack to optimize
optionsOptimizerOptionsOptimization config

Response: { HashSetStringHashSetString: [string[], string[]] } — deleted and added paths.

type OptimizePackFileRequest = { OptimizePackFile: [string, OptimizerOptions] };
type OptimizePackFileResponse = { HashSetStringHashSetString: [string[], string[]] };
public class OptimizePackFileRequest
{
    public Tuple<string, OptimizerOptions> OptimizePackFile { get; set; }
}
public class OptimizePackFileResponse
{
    public Tuple<HashSet<string>, HashSet<string>> HashSetStringHashSetString { get; set; }
}

PatchSiegeAI

Patch Siege AI for Warhammer siege maps in a specific pack.

ParameterTypeDescription
pack_keystringPack to patch

Response: { StringVecContainerPath: [string, ContainerPath[]] }

type PatchSiegeAIRequest = { PatchSiegeAI: string };
type PatchSiegeAIResponse = { StringVecContainerPath: [string, ContainerPath[]] };
public class PatchSiegeAIRequest
{
    public string PatchSiegeAI { get; set; }
}
public class PatchSiegeAIResponse
{
    public Tuple<string, List<ContainerPath>> StringVecContainerPath { get; set; }
}

Game Selection

GetGameSelected

Get the currently selected game key.

Response: { String: string }

{ "id": 1, "data": "GetGameSelected" }
type GetGameSelectedRequest = "GetGameSelected";
type GetGameSelectedResponse = { String: string };
// Request: send the literal string "GetGameSelected".
public class GetGameSelectedResponse
{
    public string String { get; set; }
}

SetGameSelected

Change the selected game. Optionally rebuilds dependencies.

ParameterTypeDescription
game_keystringGame identifier (e.g., "warhammer_3")
rebuildbooleanWhether to rebuild dependencies

Response: { CompressionFormatDependenciesInfo: [CompressionFormat, DependenciesInfo | null] }

{ "id": 1, "data": { "SetGameSelected": ["warhammer_3", true] } }
type SetGameSelectedRequest = { SetGameSelected: [string, boolean] };
type SetGameSelectedResponse = {
  CompressionFormatDependenciesInfo: [CompressionFormat, DependenciesInfo | null]
};
public class SetGameSelectedRequest
{
    public Tuple<string, bool> SetGameSelected { get; set; }
}
public class SetGameSelectedResponse
{
    public Tuple<string, DependenciesInfo?> CompressionFormatDependenciesInfo { get; set; }
}

GenerateDependenciesCache

Generate the dependencies cache for the currently selected game.

Response: { DependenciesInfo: DependenciesInfo }

type GenerateDependenciesCacheRequest = "GenerateDependenciesCache";
type GenerateDependenciesCacheResponse = { DependenciesInfo: DependenciesInfo };
// Request: send the literal string "GenerateDependenciesCache".
public class GenerateDependenciesCacheResponse
{
    public DependenciesInfo DependenciesInfo { get; set; }
}

UpdateCurrentSchemaFromAssKit

Update the current schema with data from the game’s Assembly Kit.

Response: "Success"

type UpdateCurrentSchemaFromAssKitRequest = "UpdateCurrentSchemaFromAssKit";
type UpdateCurrentSchemaFromAssKitResponse = "Success";
// Request: send the literal string "UpdateCurrentSchemaFromAssKit".
// Response: the literal string "Success".

PackedFile Operations

NewPackedFile

Create a new packed file inside a specific open Pack.

ParameterTypeDescription
pack_keystringTarget pack
pathstringInternal path for the file
specNewFileFile type specification

Response: "Success"

{ "id": 1, "data": { "NewPackedFile": ["my_mod.pack", "db/units_tables/data", { "DB": ["data", "units_tables", 4] }] } }
type NewPackedFileRequest = { NewPackedFile: [string, string, NewFile] };
type NewPackedFileResponse = "Success";
public class NewPackedFileRequest
{
    public Tuple<string, string, NewFile> NewPackedFile { get; set; }
}
// Response: the literal string "Success".

AddPackedFiles

Add files from the filesystem to a specific open Pack.

ParameterTypeDescription
pack_keystringTarget pack
source_pathsstring[]Filesystem paths to add
dest_pathsContainerPath[]Destination paths inside the pack
ignore_pathsstring[] or nullPaths to exclude (optional)

Response: { VecContainerPathOptionString: [ContainerPath[], string | null] } — added paths and optional error.

type AddPackedFilesRequest = {
  AddPackedFiles: [string, string[], ContainerPath[], string[] | null]
};
type AddPackedFilesResponse = {
  VecContainerPathOptionString: [ContainerPath[], string | null]
};
public class AddPackedFilesRequest
{
    public Tuple<string, List<string>, List<ContainerPath>, List<string>?> AddPackedFiles { get; set; }
}
public class AddPackedFilesResponse
{
    public Tuple<List<ContainerPath>, string?> VecContainerPathOptionString { get; set; }
}

DecodePackedFile

Decode a packed file for display.

ParameterTypeDescription
pack_keystringPack containing the file
pathstringInternal path
sourceDataSourceData source to decode from

Response: Type-specific (e.g., { DBRFileInfo: [DB, RFileInfo] }, { TextRFileInfo: [Text, RFileInfo] }, "Unknown", etc.)

{ "id": 1, "data": { "DecodePackedFile": ["my_mod.pack", "db/units_tables/data", "PackFile"] } }
type DecodePackedFileRequest = { DecodePackedFile: [string, string, DataSource] };
// Response is one of many variants depending on the file type:
type DecodePackedFileResponse =
  | { AnimFragmentBattleRFileInfo: [AnimFragmentBattle, RFileInfo] }
  | { AnimPackRFileInfo: [RFileInfo[], RFileInfo] }
  | { AnimsTableRFileInfo: [AnimsTable, RFileInfo] }
  | { AtlasRFileInfo: [Atlas, RFileInfo] }
  | { AudioRFileInfo: [Audio, RFileInfo] }
  | { BmdRFileInfo: [Bmd, RFileInfo] }
  | { DBRFileInfo: [DB, RFileInfo] }
  | { ESFRFileInfo: [ESF, RFileInfo] }
  | { GroupFormationsRFileInfo: [GroupFormations, RFileInfo] }
  | { ImageRFileInfo: [Image, RFileInfo] }
  | { LocRFileInfo: [Loc, RFileInfo] }
  | { MatchedCombatRFileInfo: [MatchedCombat, RFileInfo] }
  | { PortraitSettingsRFileInfo: [PortraitSettings, RFileInfo] }
  | { RigidModelRFileInfo: [RigidModel, RFileInfo] }
  | { TextRFileInfo: [Text, RFileInfo] }
  | { UICRFileInfo: [UIC, RFileInfo] }
  | { UnitVariantRFileInfo: [UnitVariant, RFileInfo] }
  | { VideoInfoRFileInfo: [VideoInfo, RFileInfo] }
  | { VMDRFileInfo: [Text, RFileInfo] }
  | { WSModelRFileInfo: [Text, RFileInfo] }
  | { Text: Text }
  | "Unknown";
public class DecodePackedFileRequest
{
    public Tuple<string, string, string> DecodePackedFile { get; set; }  // third is DataSource enum
}
// Response: deserialize into a class with nullable properties for each variant,
// or branch on the first JSON key. Example:
public class DecodePackedFileResponse
{
    public Tuple<DB, RFileInfo>? DBRFileInfo { get; set; }
    public Tuple<Text, RFileInfo>? TextRFileInfo { get; set; }
    public Tuple<Loc, RFileInfo>? LocRFileInfo { get; set; }
    // ... one property per decoded variant above.
}

SavePackedFileFromView

Save an edited packed file back to the Pack.

ParameterTypeDescription
pack_keystringTarget pack
pathstringInternal path
dataRFileDecodedDecoded file content

Response: "Success"

type SavePackedFileFromViewRequest = { SavePackedFileFromView: [string, string, RFileDecoded] };
type SavePackedFileFromViewResponse = "Success";
public class SavePackedFileFromViewRequest
{
    public Tuple<string, string, RFileDecoded> SavePackedFileFromView { get; set; }
}
// Response: the literal string "Success".

DeletePackedFiles

Delete packed files from a specific open Pack.

ParameterTypeDescription
pack_keystringPack to modify
pathsContainerPath[]Paths to delete

Response: { VecContainerPath: ContainerPath[] } — deleted paths.

{ "id": 1, "data": { "DeletePackedFiles": ["my_mod.pack", [{ "File": "db/units_tables/data" }]] } }
type DeletePackedFilesRequest = { DeletePackedFiles: [string, ContainerPath[]] };
type DeletePackedFilesResponse = { VecContainerPath: ContainerPath[] };
public class DeletePackedFilesRequest
{
    public Tuple<string, List<ContainerPath>> DeletePackedFiles { get; set; }
}
public class DeletePackedFilesResponse
{
    public List<ContainerPath> VecContainerPath { get; set; }
}

CopyPackedFiles

Copy one or more packed files to the server-side clipboard, so they can later be pasted into the same or a different pack with PastePackedFiles. Path references only — nothing is duplicated until paste.

ParameterTypeDescription
sourcesRecord<string, ContainerPath[]>Map of pack key to paths to copy from it

Response: "Success"

{ "id": 1, "data": { "CopyPackedFiles": { "my_mod.pack": [{ "File": "db/units_tables/data" }] } } }
type CopyPackedFilesRequest = { CopyPackedFiles: Record<string, ContainerPath[]> };
type CopyPackedFilesResponse = "Success";
public class CopyPackedFilesRequest
{
    public Dictionary<string, List<ContainerPath>> CopyPackedFiles { get; set; }
}
// Response: the literal string "Success".

CutPackedFiles

Same as CopyPackedFiles, but the source files will be deleted from their originating pack once PastePackedFiles runs.

ParameterTypeDescription
sourcesRecord<string, ContainerPath[]>Map of pack key to paths to cut from it

Response: "Success"

{ "id": 1, "data": { "CutPackedFiles": { "my_mod.pack": [{ "File": "db/units_tables/data" }] } } }
type CutPackedFilesRequest = { CutPackedFiles: Record<string, ContainerPath[]> };
type CutPackedFilesResponse = "Success";
public class CutPackedFilesRequest
{
    public Dictionary<string, List<ContainerPath>> CutPackedFiles { get; set; }
}
// Response: the literal string "Success".

PastePackedFiles

Paste packed files from the internal clipboard into a pack. Works for both copied and cut files — on a cut paste, the source files are removed as part of this call.

ParameterTypeDescription
pack_keystringTarget pack
dest_pathstringDestination folder path inside the target

Response: { VecContainerPathVecContainerPathString: [ContainerPath[], ContainerPath[], string] } — added paths, cut-deleted paths, source pack key.

{ "id": 1, "data": { "PastePackedFiles": ["my_mod.pack", "db/units_tables/"] } }
type PastePackedFilesRequest = { PastePackedFiles: [string, string] };
type PastePackedFilesResponse = {
  VecContainerPathVecContainerPathString: [ContainerPath[], ContainerPath[], string]
};
public class PastePackedFilesRequest
{
    public Tuple<string, string> PastePackedFiles { get; set; }
}
public class PastePackedFilesResponse
{
    public Tuple<List<ContainerPath>, List<ContainerPath>, string> VecContainerPathVecContainerPathString { get; set; }
}

DuplicatePackedFiles

Duplicate one or more packed files in place within the same pack. Each file is cloned with a numeric suffix appended to avoid name collisions.

ParameterTypeDescription
pack_keystringPack to modify
pathsContainerPath[]Paths to duplicate

Response: { VecContainerPath: ContainerPath[] } — new duplicated paths.

{ "id": 1, "data": { "DuplicatePackedFiles": ["my_mod.pack", [{ "File": "db/units_tables/data" }]] } }
type DuplicatePackedFilesRequest = { DuplicatePackedFiles: [string, ContainerPath[]] };
type DuplicatePackedFilesResponse = { VecContainerPath: ContainerPath[] };
public class DuplicatePackedFilesRequest
{
    public Tuple<string, List<ContainerPath>> DuplicatePackedFiles { get; set; }
}
public class DuplicatePackedFilesResponse
{
    public List<ContainerPath> VecContainerPath { get; set; }
}

ExtractPackedFiles

Extract packed files to the filesystem.

ParameterTypeDescription
pack_keystringPack to extract from
paths_by_sourceRecord<DataSource, ContainerPath[]>Files grouped by data source
dest_pathstringFilesystem destination
as_tsvbooleanExport tables as TSV

Response: { StringVecPathBuf: [string, string[]] }

{ "id": 1, "data": { "ExtractPackedFiles": ["my_mod.pack", { "PackFile": [{ "File": "db/units_tables/data" }] }, "/tmp/extract", false] } }
type ExtractPackedFilesRequest = {
  ExtractPackedFiles: [string, Record<DataSource, ContainerPath[]>, string, boolean]
};
type ExtractPackedFilesResponse = { StringVecPathBuf: [string, string[]] };
public class ExtractPackedFilesRequest
{
    public Tuple<string, Dictionary<string, List<ContainerPath>>, string, bool> ExtractPackedFiles { get; set; }
}
public class ExtractPackedFilesResponse
{
    public Tuple<string, List<string>> StringVecPathBuf { get; set; }
}

RenamePackedFiles

Rename packed files in a specific Pack.

ParameterTypeDescription
pack_keystringPack to modify
renames[ContainerPath, ContainerPath][]Array of [old_path, new_path] pairs

Response: { VecContainerPathContainerPath: [ContainerPath, ContainerPath][] }

type RenamePackedFilesRequest = {
  RenamePackedFiles: [string, [ContainerPath, ContainerPath][]]
};
type RenamePackedFilesResponse = {
  VecContainerPathContainerPath: [ContainerPath, ContainerPath][]
};
public class RenamePackedFilesRequest
{
    public Tuple<string, List<Tuple<ContainerPath, ContainerPath>>> RenamePackedFiles { get; set; }
}
public class RenamePackedFilesResponse
{
    public List<Tuple<ContainerPath, ContainerPath>> VecContainerPathContainerPath { get; set; }
}

FolderExists

Check if a folder exists in a specific open Pack.

ParameterTypeDescription
pack_keystringPack to check
pathstringFolder path

Response: { Bool: boolean }

type FolderExistsRequest = { FolderExists: [string, string] };
type FolderExistsResponse = { Bool: boolean };
public class FolderExistsRequest
{
    public Tuple<string, string> FolderExists { get; set; }
}
public class FolderExistsResponse
{
    public bool Bool { get; set; }
}

PackedFileExists

Check if a packed file exists in a specific open Pack.

ParameterTypeDescription
pack_keystringPack to check
pathstringFile path

Response: { Bool: boolean }

type PackedFileExistsRequest = { PackedFileExists: [string, string] };
type PackedFileExistsResponse = { Bool: boolean };
public class PackedFileExistsRequest
{
    public Tuple<string, string> PackedFileExists { get; set; }
}
public class PackedFileExistsResponse
{
    public bool Bool { get; set; }
}

GetPackedFileRawData

Get the raw binary data of a packed file.

ParameterTypeDescription
pack_keystringPack to query
pathstringInternal file path

Response: { VecU8: number[] }

type GetPackedFileRawDataRequest = { GetPackedFileRawData: [string, string] };
type GetPackedFileRawDataResponse = { VecU8: number[] };
public class GetPackedFileRawDataRequest
{
    public Tuple<string, string> GetPackedFileRawData { get; set; }
}
public class GetPackedFileRawDataResponse
{
    public byte[] VecU8 { get; set; }
}

AddPackedFilesFromPackFile

Copy packed files from one Pack into another.

ParameterTypeDescription
target_keystringDestination pack key
source_keystringSource pack key
pathsContainerPath[]Paths to copy

Response: { VecContainerPath: ContainerPath[] }

type AddPackedFilesFromPackFileRequest = {
  AddPackedFilesFromPackFile: [string, string, ContainerPath[]]
};
type AddPackedFilesFromPackFileResponse = { VecContainerPath: ContainerPath[] };
public class AddPackedFilesFromPackFileRequest
{
    public Tuple<string, string, List<ContainerPath>> AddPackedFilesFromPackFile { get; set; }
}
public class AddPackedFilesFromPackFileResponse
{
    public List<ContainerPath> VecContainerPath { get; set; }
}

AddPackedFilesFromPackFileToAnimpack

Copy packed files from the main Pack into an AnimPack.

ParameterTypeDescription
pack_keystringPack containing animpack
animpack_pathstringPath to the AnimPack
pathsContainerPath[]Paths to copy

Response: { VecContainerPath: ContainerPath[] }

type AddPackedFilesFromPackFileToAnimpackRequest = {
  AddPackedFilesFromPackFileToAnimpack: [string, string, ContainerPath[]]
};
type AddPackedFilesFromPackFileToAnimpackResponse = { VecContainerPath: ContainerPath[] };
public class AddPackedFilesFromPackFileToAnimpackRequest
{
    public Tuple<string, string, List<ContainerPath>> AddPackedFilesFromPackFileToAnimpack { get; set; }
}
public class AddPackedFilesFromPackFileToAnimpackResponse
{
    public List<ContainerPath> VecContainerPath { get; set; }
}

AddPackedFilesFromAnimpack

Copy packed files from an AnimPack into the main Pack.

ParameterTypeDescription
pack_keystringTarget pack
sourceDataSourceData source
animpack_pathstringPath to the AnimPack
pathsContainerPath[]Paths to copy

Response: { VecContainerPath: ContainerPath[] }

type AddPackedFilesFromAnimpackRequest = {
  AddPackedFilesFromAnimpack: [string, DataSource, string, ContainerPath[]]
};
type AddPackedFilesFromAnimpackResponse = { VecContainerPath: ContainerPath[] };
public class AddPackedFilesFromAnimpackRequest
{
    public Tuple<string, string, string, List<ContainerPath>> AddPackedFilesFromAnimpack { get; set; }  // second is DataSource enum
}
public class AddPackedFilesFromAnimpackResponse
{
    public List<ContainerPath> VecContainerPath { get; set; }
}

DeleteFromAnimpack

Delete packed files from an AnimPack.

ParameterTypeDescription
pack_keystringPack containing animpack
animpack_pathstringPath to the AnimPack
pathsContainerPath[]Paths to delete

Response: "Success"

type DeleteFromAnimpackRequest = {
  DeleteFromAnimpack: [string, string, ContainerPath[]]
};
type DeleteFromAnimpackResponse = "Success";
public class DeleteFromAnimpackRequest
{
    public Tuple<string, string, List<ContainerPath>> DeleteFromAnimpack { get; set; }
}
// Response: the literal string "Success".

ImportDependenciesToOpenPackFile

Import files from dependencies into a specific open Pack.

ParameterTypeDescription
pack_keystringTarget pack
sourcesRecord<DataSource, ContainerPath[]>Files to import by source

Response: { VecContainerPathVecString: [ContainerPath[], string[]] } — added paths, failed paths.

type ImportDependenciesToOpenPackFileRequest = {
  ImportDependenciesToOpenPackFile: [string, Record<DataSource, ContainerPath[]>]
};
type ImportDependenciesToOpenPackFileResponse = {
  VecContainerPathVecString: [ContainerPath[], string[]]
};
public class ImportDependenciesToOpenPackFileRequest
{
    public Tuple<string, Dictionary<string, List<ContainerPath>>> ImportDependenciesToOpenPackFile { get; set; }
}
public class ImportDependenciesToOpenPackFileResponse
{
    public Tuple<List<ContainerPath>, List<string>> VecContainerPathVecString { get; set; }
}

SavePackedFilesToPackFileAndClean

Save packed files to a specific Pack and optionally run optimizer.

ParameterTypeDescription
pack_keystringTarget pack
filesRFile[]Files to save
optimizebooleanRun optimizer

Response: { VecContainerPathVecContainerPath: [ContainerPath[], ContainerPath[]] } — added and deleted paths.

type SavePackedFilesToPackFileAndCleanRequest = {
  SavePackedFilesToPackFileAndClean: [string, RFile[], boolean]
};
type SavePackedFilesToPackFileAndCleanResponse = {
  VecContainerPathVecContainerPath: [ContainerPath[], ContainerPath[]]
};
public class SavePackedFilesToPackFileAndCleanRequest
{
    public Tuple<string, List<RFile>, bool> SavePackedFilesToPackFileAndClean { get; set; }
}
public class SavePackedFilesToPackFileAndCleanResponse
{
    public Tuple<List<ContainerPath>, List<ContainerPath>> VecContainerPathVecContainerPath { get; set; }
}

GetPackedFilesNamesStartingWitPathFromAllSources

Get all file names under a path from all dependency sources.

ParameterTypeDescription
pathContainerPathPath prefix

Response: { HashMapDataSourceHashSetContainerPath: Record<DataSource, ContainerPath[]> }

type GetPackedFilesNamesStartingWitPathFromAllSourcesRequest = {
  GetPackedFilesNamesStartingWitPathFromAllSources: ContainerPath
};
type GetPackedFilesNamesStartingWitPathFromAllSourcesResponse = {
  HashMapDataSourceHashSetContainerPath: Record<DataSource, ContainerPath[]>
};
public class GetPackedFilesNamesStartingWitPathFromAllSourcesRequest
{
    public ContainerPath GetPackedFilesNamesStartingWitPathFromAllSources { get; set; }
}
public class GetPackedFilesNamesStartingWitPathFromAllSourcesResponse
{
    public Dictionary<string, List<ContainerPath>> HashMapDataSourceHashSetContainerPath { get; set; }
}

Dependency Commands

RebuildDependencies

Rebuild the dependencies by combining deps from all open packs.

ParameterTypeDescription
rebuild_allbooleantrue = all dependencies, false = mod-specific

Response: { DependenciesInfo: DependenciesInfo }

{ "id": 1, "data": { "RebuildDependencies": true } }
type RebuildDependenciesRequest = { RebuildDependencies: boolean };
type RebuildDependenciesResponse = { DependenciesInfo: DependenciesInfo };
public class RebuildDependenciesRequest
{
    public bool RebuildDependencies { get; set; }
}
public class RebuildDependenciesResponse
{
    public DependenciesInfo DependenciesInfo { get; set; }
}

IsThereADependencyDatabase

Check if a dependency database is loaded.

ParameterTypeDescription
require_asskitbooleanCheck that AssKit data is included

Response: { Bool: boolean }

type IsThereADependencyDatabaseRequest = { IsThereADependencyDatabase: boolean };
type IsThereADependencyDatabaseResponse = { Bool: boolean };
public class IsThereADependencyDatabaseRequest
{
    public bool IsThereADependencyDatabase { get; set; }
}
public class IsThereADependencyDatabaseResponse
{
    public bool Bool { get; set; }
}

GetTableListFromDependencyPackFile

Get all DB table names from dependency Pack files.

Response: { VecString: string[] }

type GetTableListFromDependencyPackFileRequest = "GetTableListFromDependencyPackFile";
type GetTableListFromDependencyPackFileResponse = { VecString: string[] };
// Request: send the literal string "GetTableListFromDependencyPackFile".
public class GetTableListFromDependencyPackFileResponse
{
    public List<string> VecString { get; set; }
}

GetCustomTableList

Get custom table names (start_pos_, twad_ prefixes) from the schema.

Response: { VecString: string[] }

type GetCustomTableListRequest = "GetCustomTableList";
type GetCustomTableListResponse = { VecString: string[] };
// Request: send the literal string "GetCustomTableList".
public class GetCustomTableListResponse
{
    public List<string> VecString { get; set; }
}

LocalArtSetIds

Get local art set IDs from campaign_character_arts_tables in a specific pack.

ParameterTypeDescription
pack_keystringPack to query

Response: { HashSetString: string[] }

type LocalArtSetIdsRequest = { LocalArtSetIds: string };
type LocalArtSetIdsResponse = { HashSetString: string[] };
public class LocalArtSetIdsRequest
{
    public string LocalArtSetIds { get; set; }
}
public class LocalArtSetIdsResponse
{
    public HashSet<string> HashSetString { get; set; }
}

DependenciesArtSetIds

Get art set IDs from dependencies’ campaign_character_arts_tables.

Response: { HashSetString: string[] }

type DependenciesArtSetIdsRequest = "DependenciesArtSetIds";
type DependenciesArtSetIdsResponse = { HashSetString: string[] };
// Request: send the literal string "DependenciesArtSetIds".
public class DependenciesArtSetIdsResponse
{
    public HashSet<string> HashSetString { get; set; }
}

GetTableVersionFromDependencyPackFile

Get the version of a table from the dependency database.

ParameterTypeDescription
table_namestringTable to query

Response: { I32: number }

type GetTableVersionFromDependencyPackFileRequest = { GetTableVersionFromDependencyPackFile: string };
type GetTableVersionFromDependencyPackFileResponse = { I32: number };
public class GetTableVersionFromDependencyPackFileRequest
{
    public string GetTableVersionFromDependencyPackFile { get; set; }
}
public class GetTableVersionFromDependencyPackFileResponse
{
    public int I32 { get; set; }
}

GetTableDefinitionFromDependencyPackFile

Get the definition of a table from the dependency database.

ParameterTypeDescription
table_namestringTable to query

Response: { Definition: Definition }

type GetTableDefinitionFromDependencyPackFileRequest = { GetTableDefinitionFromDependencyPackFile: string };
type GetTableDefinitionFromDependencyPackFileResponse = { Definition: Definition };
public class GetTableDefinitionFromDependencyPackFileRequest
{
    public string GetTableDefinitionFromDependencyPackFile { get; set; }
}
public class GetTableDefinitionFromDependencyPackFileResponse
{
    public Definition Definition { get; set; }
}

MergeFiles

Merge multiple compatible tables into one.

ParameterTypeDescription
pack_keystringPack containing the files
pathsContainerPath[]Files to merge
merged_pathstringDestination path for result
delete_sourcesbooleanDelete source files after

Response: { String: string } — merged path.

type MergeFilesRequest = { MergeFiles: [string, ContainerPath[], string, boolean] };
type MergeFilesResponse = { String: string };
public class MergeFilesRequest
{
    public Tuple<string, List<ContainerPath>, string, bool> MergeFiles { get; set; }
}
public class MergeFilesResponse
{
    public string String { get; set; }
}

UpdateTable

Update a table to a newer schema version.

ParameterTypeDescription
pack_keystringPack containing table
pathContainerPathTable path

Response: { I32I32VecStringVecString: [old_ver, new_ver, deleted_fields, added_fields] }

type UpdateTableRequest = { UpdateTable: [string, ContainerPath] };
type UpdateTableResponse = {
  I32I32VecStringVecString: [number, number, string[], string[]]
};
public class UpdateTableRequest
{
    public Tuple<string, ContainerPath> UpdateTable { get; set; }
}
public class UpdateTableResponse
{
    public Tuple<int, int, List<string>, List<string>> I32I32VecStringVecString { get; set; }
}

GetDependencyPackFilesList

Get the list of Pack files marked as dependencies of a specific Pack.

ParameterTypeDescription
pack_keystringPack to query

Response: { VecBoolString: [boolean, string][] }[enabled, pack_name] pairs.

type GetDependencyPackFilesListRequest = { GetDependencyPackFilesList: string };
type GetDependencyPackFilesListResponse = { VecBoolString: [boolean, string][] };
public class GetDependencyPackFilesListRequest
{
    public string GetDependencyPackFilesList { get; set; }
}
public class GetDependencyPackFilesListResponse
{
    public List<Tuple<bool, string>> VecBoolString { get; set; }
}

SetDependencyPackFilesList

Set the list of Pack files marked as dependencies.

ParameterTypeDescription
pack_keystringPack to modify
deps[boolean, string][][enabled, pack_name] pairs

Response: "Success"

type SetDependencyPackFilesListRequest = {
  SetDependencyPackFilesList: [string, [boolean, string][]]
};
type SetDependencyPackFilesListResponse = "Success";
public class SetDependencyPackFilesListRequest
{
    public Tuple<string, List<Tuple<bool, string>>> SetDependencyPackFilesList { get; set; }
}
// Response: the literal string "Success".

GetRFilesFromAllSources

Get packed files from all known sources.

ParameterTypeDescription
pathsContainerPath[]Paths to retrieve
lowercasebooleanNormalize paths to lowercase

Response: { HashMapDataSourceHashMapStringRFile: Record<DataSource, Record<string, RFile>> }

type GetRFilesFromAllSourcesRequest = { GetRFilesFromAllSources: [ContainerPath[], boolean] };
type GetRFilesFromAllSourcesResponse = {
  HashMapDataSourceHashMapStringRFile: Record<DataSource, Record<string, RFile>>
};
public class GetRFilesFromAllSourcesRequest
{
    public Tuple<List<ContainerPath>, bool> GetRFilesFromAllSources { get; set; }
}
public class GetRFilesFromAllSourcesResponse
{
    public Dictionary<string, Dictionary<string, RFile>> HashMapDataSourceHashMapStringRFile { get; set; }
}

Search Commands

GlobalSearch

Perform a global search across a specific pack.

ParameterTypeDescription
pack_keystringPack to search
configGlobalSearchSearch configuration

Response: { GlobalSearchVecRFileInfo: [GlobalSearch, RFileInfo[]] }

{
  "id": 1,
  "data": {
    "GlobalSearch": ["my_mod.pack", {
      "pattern": "cavalry",
      "replace_text": "",
      "case_sensitive": false,
      "use_regex": false,
      "sources": [{ "Pack": "my_mod.pack" }],
      "search_on": { "db": true, "loc": true, "text": true },
      "matches": {},
      "game_key": "warhammer_3"
    }]
  }
}
type GlobalSearchRequest = { GlobalSearch: [string, GlobalSearch] };
type GlobalSearchResponse = { GlobalSearchVecRFileInfo: [GlobalSearch, RFileInfo[]] };
public class GlobalSearchRequest
{
    public Tuple<string, GlobalSearch> GlobalSearch { get; set; }
}
public class GlobalSearchResponse
{
    public Tuple<GlobalSearch, List<RFileInfo>> GlobalSearchVecRFileInfo { get; set; }
}

GlobalSearchReplaceMatches

Replace specific matches in a global search.

ParameterTypeDescription
pack_keystringPack to modify
configGlobalSearchSearch config
matchesMatchHolder[]Matches to replace

Response: { GlobalSearchVecRFileInfo: [GlobalSearch, RFileInfo[]] }

type GlobalSearchReplaceMatchesRequest = {
  GlobalSearchReplaceMatches: [string, GlobalSearch, MatchHolder[]]
};
type GlobalSearchReplaceMatchesResponse = {
  GlobalSearchVecRFileInfo: [GlobalSearch, RFileInfo[]]
};
public class GlobalSearchReplaceMatchesRequest
{
    public Tuple<string, GlobalSearch, List<MatchHolder>> GlobalSearchReplaceMatches { get; set; }
}
public class GlobalSearchReplaceMatchesResponse
{
    public Tuple<GlobalSearch, List<RFileInfo>> GlobalSearchVecRFileInfo { get; set; }
}

GlobalSearchReplaceAll

Replace all matches in a global search.

ParameterTypeDescription
pack_keystringPack to modify
configGlobalSearchSearch config

Response: { GlobalSearchVecRFileInfo: [GlobalSearch, RFileInfo[]] }

type GlobalSearchReplaceAllRequest = { GlobalSearchReplaceAll: [string, GlobalSearch] };
type GlobalSearchReplaceAllResponse = {
  GlobalSearchVecRFileInfo: [GlobalSearch, RFileInfo[]]
};
public class GlobalSearchReplaceAllRequest
{
    public Tuple<string, GlobalSearch> GlobalSearchReplaceAll { get; set; }
}
public class GlobalSearchReplaceAllResponse
{
    public Tuple<GlobalSearch, List<RFileInfo>> GlobalSearchVecRFileInfo { get; set; }
}

GetReferenceDataFromDefinition

Get reference data for columns in a table definition.

ParameterTypeDescription
pack_keystringPack to query
table_namestringName of the table
definitionDefinitionTable definition
force_localbooleanForce regeneration from local

Response: { HashMapI32TableReferences: Record<number, TableReferences> }

type GetReferenceDataFromDefinitionRequest = {
  GetReferenceDataFromDefinition: [string, string, Definition, boolean]
};
type GetReferenceDataFromDefinitionResponse = {
  HashMapI32TableReferences: Record<number, TableReferences>
};
public class GetReferenceDataFromDefinitionRequest
{
    public Tuple<string, string, Definition, bool> GetReferenceDataFromDefinition { get; set; }
}
public class GetReferenceDataFromDefinitionResponse
{
    public Dictionary<int, TableReferences> HashMapI32TableReferences { get; set; }
}

SearchReferences

Find all references to a value across tables in a specific pack.

ParameterTypeDescription
pack_keystringPack to search
table_columnsRecord<string, string[]>Map of table name to column names
search_valuestringValue to search for

Response: { VecDataSourceStringStringStringUsizeUsize: [DataSource, string, string, string, number, number][] }

Each tuple is (data_source, pack_key, path, column_name, column_number, row_number). pack_key identifies which open Pack the hit came from for PackFile results, so a client looking at multiple open Packs can route the navigation back to the right one. For ParentFiles and GameFiles hits, pack_key is an empty string — those are read from the dependency cache and aren’t tied to a specific open Pack.

type SearchReferencesRequest = {
  SearchReferences: [string, Record<string, string[]>, string]
};
type SearchReferencesResponse = {
  VecDataSourceStringStringStringUsizeUsize: [DataSource, string, string, string, number, number][]
};
public class SearchReferencesRequest
{
    public Tuple<string, Dictionary<string, List<string>>, string> SearchReferences { get; set; }
}
public class SearchReferencesResponse
{
    public List<Tuple<string, string, string, string, long, long>> VecDataSourceStringStringStringUsizeUsize { get; set; }
}

GoToDefinition

Go to the definition of a table reference.

ParameterTypeDescription
pack_keystringPack to search
table_namestringTable name
column_namestringColumn name
valuesstring[]Values to search for

Response: { DataSourceStringUsizeUsize: [DataSource, string, number, number] }

type GoToDefinitionRequest = { GoToDefinition: [string, string, string, string[]] };
type GoToDefinitionResponse = {
  DataSourceStringUsizeUsize: [DataSource, string, number, number]
};
public class GoToDefinitionRequest
{
    public Tuple<string, string, string, List<string>> GoToDefinition { get; set; }
}
public class GoToDefinitionResponse
{
    public Tuple<string, string, long, long> DataSourceStringUsizeUsize { get; set; }
}

GoToLoc

Navigate to a loc key’s location.

ParameterTypeDescription
pack_keystringPack to search
loc_keystringLoc key to find

Response: { DataSourceStringUsizeUsize: [DataSource, string, number, number] }

type GoToLocRequest = { GoToLoc: [string, string] };
type GoToLocResponse = {
  DataSourceStringUsizeUsize: [DataSource, string, number, number]
};
public class GoToLocRequest
{
    public Tuple<string, string> GoToLoc { get; set; }
}
public class GoToLocResponse
{
    public Tuple<string, string, long, long> DataSourceStringUsizeUsize { get; set; }
}

GetSourceDataFromLocKey

Get the source data (table, column, values) of a loc key.

ParameterTypeDescription
pack_keystringPack to search
loc_keystringLoc key to look up

Response: { OptionStringStringVecString: [string, string, string[]] | null }

type GetSourceDataFromLocKeyRequest = { GetSourceDataFromLocKey: [string, string] };
type GetSourceDataFromLocKeyResponse = {
  OptionStringStringVecString: [string, string, string[]] | null
};
public class GetSourceDataFromLocKeyRequest
{
    public Tuple<string, string> GetSourceDataFromLocKey { get; set; }
}
public class GetSourceDataFromLocKeyResponse
{
    public Tuple<string, string, List<string>>? OptionStringStringVecString { get; set; }
}

Cascade Edition

CascadeEdition

Trigger a cascade edition on all referenced data in a specific pack.

ParameterTypeDescription
pack_keystringPack to modify
tablestringTable name
definitionDefinitionTable definition
changes[Field, string, string][][field, old_value, new_value] tuples

Response: { VecContainerPathVecRFileInfo: [ContainerPath[], RFileInfo[]] }

type CascadeEditionRequest = {
  CascadeEdition: [string, string, Definition, [Field, string, string][]]
};
type CascadeEditionResponse = {
  VecContainerPathVecRFileInfo: [ContainerPath[], RFileInfo[]]
};
public class CascadeEditionRequest
{
    public Tuple<string, string, Definition, List<Tuple<Field, string, string>>> CascadeEdition { get; set; }
}
public class CascadeEditionResponse
{
    public Tuple<List<ContainerPath>, List<RFileInfo>> VecContainerPathVecRFileInfo { get; set; }
}

Video Commands

SetVideoFormat

Change the format of a ca_vp8 video packed file.

ParameterTypeDescription
pack_keystringPack containing file
pathstringInternal file path
formatSupportedFormatsTarget video format

Response: "Success"

type SetVideoFormatRequest = { SetVideoFormat: [string, string, SupportedFormats] };
type SetVideoFormatResponse = "Success";
public class SetVideoFormatRequest
{
    public Tuple<string, string, string> SetVideoFormat { get; set; }  // third is SupportedFormats enum
}
// Response: the literal string "Success".

Schema Commands

SaveSchema

Save a schema to disk.

ParameterTypeDescription
schemaSchemaComplete schema to save

Response: "Success"

type SaveSchemaRequest = { SaveSchema: Schema };
type SaveSchemaResponse = "Success";
public class SaveSchemaRequest
{
    public Schema SaveSchema { get; set; }
}
// Response: the literal string "Success".

CleanCache

Encode and clean the internal cache for specified paths.

ParameterTypeDescription
pack_keystringPack to clean
pathsContainerPath[]Paths to process

Response: "Success"

type CleanCacheRequest = { CleanCache: [string, ContainerPath[]] };
type CleanCacheResponse = "Success";
public class CleanCacheRequest
{
    public Tuple<string, List<ContainerPath>> CleanCache { get; set; }
}
// Response: the literal string "Success".

IsSchemaLoaded

Check if a schema is loaded in memory.

Response: { Bool: boolean }

type IsSchemaLoadedRequest = "IsSchemaLoaded";
type IsSchemaLoadedResponse = { Bool: boolean };
// Request: send the literal string "IsSchemaLoaded".
public class IsSchemaLoadedResponse
{
    public bool Bool { get; set; }
}

Schema

Get the currently loaded schema.

Response: { Schema: Schema }

type SchemaRequest = "Schema";
type SchemaResponse = { Schema: Schema };
// Request: send the literal string "Schema".
public class SchemaResponse
{
    public Schema Schema { get; set; }
}

DefinitionsByTableName

Get all definitions (all versions) for a table name.

ParameterTypeDescription
table_namestringTable to query

Response: { VecDefinition: Definition[] }

type DefinitionsByTableNameRequest = { DefinitionsByTableName: string };
type DefinitionsByTableNameResponse = { VecDefinition: Definition[] };
public class DefinitionsByTableNameRequest
{
    public string DefinitionsByTableName { get; set; }
}
public class DefinitionsByTableNameResponse
{
    public List<Definition> VecDefinition { get; set; }
}

DefinitionByTableNameAndVersion

Get a specific definition by table name and version.

ParameterTypeDescription
table_namestringTable name
versionnumberVersion number

Response: { Definition: Definition }

type DefinitionByTableNameAndVersionRequest = {
  DefinitionByTableNameAndVersion: [string, number]
};
type DefinitionByTableNameAndVersionResponse = { Definition: Definition };
public class DefinitionByTableNameAndVersionRequest
{
    public Tuple<string, int> DefinitionByTableNameAndVersion { get; set; }
}
public class DefinitionByTableNameAndVersionResponse
{
    public Definition Definition { get; set; }
}

DeleteDefinition

Delete a definition by table name and version.

ParameterTypeDescription
table_namestringTable name
versionnumberVersion number

Response: "Success"

type DeleteDefinitionRequest = { DeleteDefinition: [string, number] };
type DeleteDefinitionResponse = "Success";
public class DeleteDefinitionRequest
{
    public Tuple<string, int> DeleteDefinition { get; set; }
}
// Response: the literal string "Success".

ReferencingColumnsForDefinition

Get columns from other tables that reference a given table/definition.

ParameterTypeDescription
table_namestringReferenced table
definitionDefinitionTable definition

Response: { HashMapStringHashMapStringVecString: Record<string, Record<string, string[]>> }

type ReferencingColumnsForDefinitionRequest = {
  ReferencingColumnsForDefinition: [string, Definition]
};
type ReferencingColumnsForDefinitionResponse = {
  HashMapStringHashMapStringVecString: Record<string, Record<string, string[]>>
};
public class ReferencingColumnsForDefinitionRequest
{
    public Tuple<string, Definition> ReferencingColumnsForDefinition { get; set; }
}
public class ReferencingColumnsForDefinitionResponse
{
    public Dictionary<string, Dictionary<string, List<string>>> HashMapStringHashMapStringVecString { get; set; }
}

FieldsProcessed

Get the processed fields from a definition (bitwise expansion, enum conversion, colour merging applied).

ParameterTypeDescription
definitionDefinitionDefinition to process

Response: { VecField: Field[] }

type FieldsProcessedRequest = { FieldsProcessed: Definition };
type FieldsProcessedResponse = { VecField: Field[] };
public class FieldsProcessedRequest
{
    public Definition FieldsProcessed { get; set; }
}
public class FieldsProcessedResponse
{
    public List<Field> VecField { get; set; }
}

TSV Commands

ExportTSV

Export a table as a TSV file.

ParameterTypeDescription
pack_keystringPack containing table
pathstringInternal table path
deststringFilesystem output path
sourceDataSourceData source

Response: "Success"

type ExportTSVRequest = { ExportTSV: [string, string, string, DataSource] };
type ExportTSVResponse = "Success";
public class ExportTSVRequest
{
    public Tuple<string, string, string, string> ExportTSV { get; set; }  // fourth is DataSource enum
}
// Response: the literal string "Success".

ImportTSV

Import a TSV file as a table.

ParameterTypeDescription
pack_keystringTarget pack
pathstringInternal destination path
tsv_pathstringFilesystem TSV path

Response: { RFileDecoded: RFileDecoded }

type ImportTSVRequest = { ImportTSV: [string, string, string] };
type ImportTSVResponse = { RFileDecoded: RFileDecoded };
public class ImportTSVRequest
{
    public Tuple<string, string, string> ImportTSV { get; set; }
}
public class ImportTSVResponse
{
    public RFileDecoded RFileDecoded { get; set; }
}

External Program Commands

OpenContainingFolder

Open the folder containing a specific open Pack in the file manager.

ParameterTypeDescription
pack_keystringPack whose folder to open

Response: "Success"

type OpenContainingFolderRequest = { OpenContainingFolder: string };
type OpenContainingFolderResponse = "Success";
public class OpenContainingFolderRequest
{
    public string OpenContainingFolder { get; set; }
}
// Response: the literal string "Success".

OpenPackedFileInExternalProgram

Open a packed file in an external program.

ParameterTypeDescription
pack_keystringPack containing file
sourceDataSourceData source
pathContainerPathFile path

Response: { PathBuf: string } — extracted temporary path.

type OpenPackedFileInExternalProgramRequest = {
  OpenPackedFileInExternalProgram: [string, DataSource, ContainerPath]
};
type OpenPackedFileInExternalProgramResponse = { PathBuf: string };
public class OpenPackedFileInExternalProgramRequest
{
    public Tuple<string, string, ContainerPath> OpenPackedFileInExternalProgram { get; set; }  // second is DataSource enum
}
public class OpenPackedFileInExternalProgramResponse
{
    public string PathBuf { get; set; }
}

SavePackedFileFromExternalView

Save a packed file that was edited in an external program.

ParameterTypeDescription
pack_keystringTarget pack
pathstringInternal path
ext_pathstringExternal file path

Response: "Success"

type SavePackedFileFromExternalViewRequest = {
  SavePackedFileFromExternalView: [string, string, string]
};
type SavePackedFileFromExternalViewResponse = "Success";
public class SavePackedFileFromExternalViewRequest
{
    public Tuple<string, string, string> SavePackedFileFromExternalView { get; set; }
}
// Response: the literal string "Success".

Diagnostics Commands

DiagnosticsCheck

Run a full diagnostics check over every open pack.

ParameterTypeDescription
diagnostics_ignoredstring[]Diagnostic type identifiers to skip during the check
check_akbooleanAlso check Assembly Kit-only references

Response: { Diagnostics: Diagnostics }

type DiagnosticsCheckRequest = { DiagnosticsCheck: [string[], boolean] };
type DiagnosticsCheckResponse = { Diagnostics: Diagnostics };
public class DiagnosticsCheckRequest
{
    public Tuple<List<string>, bool> DiagnosticsCheck { get; set; }
}
public class DiagnosticsCheckResponse
{
    public Diagnostics Diagnostics { get; set; }
}

DiagnosticsUpdate

Run a partial diagnostics update on specific paths.

ParameterTypeDescription
diagnosticsDiagnosticsExisting diagnostics state
pathsContainerPath[]Paths to re-check
check_akbooleanCheck AssKit-only references

Response: { Diagnostics: Diagnostics }

type DiagnosticsUpdateRequest = {
  DiagnosticsUpdate: [Diagnostics, ContainerPath[], boolean]
};
type DiagnosticsUpdateResponse = { Diagnostics: Diagnostics };
public class DiagnosticsUpdateRequest
{
    public Tuple<Diagnostics, List<ContainerPath>, bool> DiagnosticsUpdate { get; set; }
}
public class DiagnosticsUpdateResponse
{
    public Diagnostics Diagnostics { get; set; }
}

AddLineToPackIgnoredDiagnostics

Add a line to a specific pack’s ignored diagnostics list.

ParameterTypeDescription
pack_keystringPack to modify
linestringDiagnostic key to ignore

Response: "Success"

type AddLineToPackIgnoredDiagnosticsRequest = {
  AddLineToPackIgnoredDiagnostics: [string, string]
};
type AddLineToPackIgnoredDiagnosticsResponse = "Success";
public class AddLineToPackIgnoredDiagnosticsRequest
{
    public Tuple<string, string> AddLineToPackIgnoredDiagnostics { get; set; }
}
// Response: the literal string "Success".

Pack Settings Commands

GetPackSettings

Get the settings of a specific open Pack.

ParameterTypeDescription
pack_keystringPack to query

Response: { PackSettings: PackSettings }

type GetPackSettingsRequest = { GetPackSettings: string };
type GetPackSettingsResponse = { PackSettings: PackSettings };
public class GetPackSettingsRequest
{
    public string GetPackSettings { get; set; }
}
public class GetPackSettingsResponse
{
    public PackSettings PackSettings { get; set; }
}

SetPackSettings

Set the settings of a specific open Pack.

ParameterTypeDescription
pack_keystringPack to modify
settingsPackSettingsNew settings

Response: "Success"

type SetPackSettingsRequest = { SetPackSettings: [string, PackSettings] };
type SetPackSettingsResponse = "Success";
public class SetPackSettingsRequest
{
    public Tuple<string, PackSettings> SetPackSettings { get; set; }
}
// Response: the literal string "Success".

Notes Commands

NotesForPath

Get all notes under a given path in a specific pack.

ParameterTypeDescription
pack_keystringPack to query
pathstringPath prefix

Response: { VecNote: Note[] }

type NotesForPathRequest = { NotesForPath: [string, string] };
type NotesForPathResponse = { VecNote: Note[] };
public class NotesForPathRequest
{
    public Tuple<string, string> NotesForPath { get; set; }
}
public class NotesForPathResponse
{
    public List<Note> VecNote { get; set; }
}

AddNote

Add a note to a specific pack.

ParameterTypeDescription
pack_keystringTarget pack
noteNoteNote to add

Response: { Note: Note }

type AddNoteRequest = { AddNote: [string, Note] };
type AddNoteResponse = { Note: Note };
public class AddNoteRequest
{
    public Tuple<string, Note> AddNote { get; set; }
}
public class AddNoteResponse
{
    public Note Note { get; set; }
}

DeleteNote

Delete a note from a specific pack.

ParameterTypeDescription
pack_keystringPack to modify
pathstringNote path
note_idnumberNote ID

Response: "Success"

type DeleteNoteRequest = { DeleteNote: [string, string, number] };
type DeleteNoteResponse = "Success";
public class DeleteNoteRequest
{
    public Tuple<string, string, ulong> DeleteNote { get; set; }
}
// Response: the literal string "Success".

Schema Patch Commands

SaveLocalSchemaPatch

Save local schema patches to disk.

ParameterTypeDescription
patchesRecord<string, DefinitionPatch>Table name to patches

Response: "Success"

type SaveLocalSchemaPatchRequest = { SaveLocalSchemaPatch: Record<string, DefinitionPatch> };
type SaveLocalSchemaPatchResponse = "Success";
public class SaveLocalSchemaPatchRequest
{
    public Dictionary<string, DefinitionPatch> SaveLocalSchemaPatch { get; set; }
}
// Response: the literal string "Success".

RemoveLocalSchemaPatchesForTable

Remove all local schema patches for a table.

ParameterTypeDescription
table_namestringTable name

Response: "Success"

type RemoveLocalSchemaPatchesForTableRequest = { RemoveLocalSchemaPatchesForTable: string };
type RemoveLocalSchemaPatchesForTableResponse = "Success";
public class RemoveLocalSchemaPatchesForTableRequest
{
    public string RemoveLocalSchemaPatchesForTable { get; set; }
}
// Response: the literal string "Success".

RemoveLocalSchemaPatchesForTableAndField

Remove local schema patches for a specific field in a table.

ParameterTypeDescription
table_namestringTable name
field_namestringField name

Response: "Success"

type RemoveLocalSchemaPatchesForTableAndFieldRequest = {
  RemoveLocalSchemaPatchesForTableAndField: [string, string]
};
type RemoveLocalSchemaPatchesForTableAndFieldResponse = "Success";
public class RemoveLocalSchemaPatchesForTableAndFieldRequest
{
    public Tuple<string, string> RemoveLocalSchemaPatchesForTableAndField { get; set; }
}
// Response: the literal string "Success".

ImportSchemaPatch

Import schema patches into local patches.

ParameterTypeDescription
patchesRecord<string, DefinitionPatch>Table name to patches

Response: "Success"

type ImportSchemaPatchRequest = { ImportSchemaPatch: Record<string, DefinitionPatch> };
type ImportSchemaPatchResponse = "Success";
public class ImportSchemaPatchRequest
{
    public Dictionary<string, DefinitionPatch> ImportSchemaPatch { get; set; }
}
// Response: the literal string "Success".

Loc Generation Commands

GenerateMissingLocData

Generate all missing loc entries for a specific open Pack.

ParameterTypeDescription
pack_keystringPack to generate for

Response: { VecContainerPath: ContainerPath[] }

type GenerateMissingLocDataRequest = { GenerateMissingLocData: string };
type GenerateMissingLocDataResponse = { VecContainerPath: ContainerPath[] };
public class GenerateMissingLocDataRequest
{
    public string GenerateMissingLocData { get; set; }
}
public class GenerateMissingLocDataResponse
{
    public List<ContainerPath> VecContainerPath { get; set; }
}

Update Commands

All update commands take no parameters (send the literal command name). The Check* variants return an APIResponse (the main program) or a GitResponse (git-backed repos: schemas, autogen, AK, translations). The Update* variants apply the update and return "Success".

// Shared shapes for every command in this section:
type CheckAPIRequest = "CheckUpdates";
type CheckAPIResponse = { APIResponse: APIResponse };

type CheckGitRequest =
  | "CheckSchemaUpdates"
  | "CheckLuaAutogenUpdates"
  | "CheckEmpireAndNapoleonAKUpdates"
  | "CheckTranslationsUpdates";
type CheckGitResponse = { APIResponseGit: GitResponse };

type UpdateApplyRequest =
  | "UpdateSchemas"
  | "UpdateMainProgram"
  | "UpdateLuaAutogen"
  | "UpdateEmpireAndNapoleonAK"
  | "UpdateTranslations";
type UpdateApplyResponse = "Success";
// Shared shapes for every command in this section:
public class CheckAPIResponse
{
    public APIResponse APIResponse { get; set; }
}
public class CheckGitResponse
{
    public GitResponse APIResponseGit { get; set; }
}
// Update apply requests take no parameters; the response is the literal string "Success".

CheckUpdates

Check if there is an RPFM update available.

Response: { APIResponse: APIResponse }

CheckSchemaUpdates

Check if there is a schema update available.

Response: { APIResponseGit: GitResponse }

UpdateSchemas

Download and apply schema updates.

Response: "Success"

UpdateMainProgram

Update RPFM to the latest version.

Response: "Success"

CheckLuaAutogenUpdates

Check for updates on the tw_autogen repository.

Response: { APIResponseGit: GitResponse }

UpdateLuaAutogen

Update the tw_autogen repository.

Response: "Success"

CheckEmpireAndNapoleonAKUpdates

Check for updates on the old Assembly Kit files repository.

Response: { APIResponseGit: GitResponse }

UpdateEmpireAndNapoleonAK

Update the old Assembly Kit files repository.

Response: "Success"

CheckTranslationsUpdates

Check for translation updates.

Response: { APIResponseGit: GitResponse }

UpdateTranslations

Update the translations repository.

Response: "Success"


MyMod Commands

InitializeMyModFolder

Initialize a MyMod folder structure.

ParameterTypeDescription
mod_namestringName of the mod
game_keystringTarget game
sublimebooleanCreate Sublime Text project
vscodebooleanCreate VS Code project
gitignorestring or nullGitignore content (null = no git)

Response: { PathBuf: string } — path to the new pack.

type InitializeMyModFolderRequest = {
  InitializeMyModFolder: [string, string, boolean, boolean, string | null]
};
type InitializeMyModFolderResponse = { PathBuf: string };
public class InitializeMyModFolderRequest
{
    public Tuple<string, string, bool, bool, string?> InitializeMyModFolder { get; set; }
}
public class InitializeMyModFolderResponse
{
    public string PathBuf { get; set; }
}

LiveExport

Live-export a specific Pack to the game’s data folder.

ParameterTypeDescription
pack_keystringPack to export

Response: "Success"

type LiveExportRequest = { LiveExport: string };
type LiveExportResponse = "Success";
public class LiveExportRequest
{
    public string LiveExport { get; set; }
}
// Response: the literal string "Success".

GetPackOperationalMode

Get the operational mode for a specific pack. This controls whether the pack is treated as a MyMod (with its game/pack association) or as a plain pack.

ParameterTypeDescription
pack_keystringPack to query

Response: { OperationalMode: OperationalMode } — see OperationalMode.

{ "id": 1, "data": { "GetPackOperationalMode": "my_mod.pack" } }
type GetPackOperationalModeRequest = { GetPackOperationalMode: string };
type GetPackOperationalModeResponse = { OperationalMode: OperationalMode };
public class GetPackOperationalModeRequest
{
    public string GetPackOperationalMode { get; set; }
}
public class GetPackOperationalModeResponse
{
    public OperationalMode OperationalMode { get; set; }
}

SetPackOperationalMode

Set the operational mode for a specific pack.

ParameterTypeDescription
pack_keystringPack to modify
modeOperationalModeNew operational mode

Response: "Success"

{ "id": 1, "data": { "SetPackOperationalMode": ["my_mod.pack", { "MyMod": ["warhammer_2", "my_mod.pack"] }] } }
type SetPackOperationalModeRequest = {
  SetPackOperationalMode: [string, OperationalMode]
};
type SetPackOperationalModeResponse = "Success";
public class SetPackOperationalModeRequest
{
    public Tuple<string, OperationalMode> SetPackOperationalMode { get; set; }
}
// Response: the literal string "Success".

Translation Commands

GetPackTranslation

Get pack translation data for a language from a specific pack.

ParameterTypeDescription
pack_keystringPack to query
languagestringLanguage code

Response: { PackTranslation: PackTranslation }

type GetPackTranslationRequest = { GetPackTranslation: [string, string] };
type GetPackTranslationResponse = { PackTranslation: PackTranslation };
public class GetPackTranslationRequest
{
    public Tuple<string, string> GetPackTranslation { get; set; }
}
public class GetPackTranslationResponse
{
    public PackTranslation PackTranslation { get; set; }
}

Starpos Commands

BuildStarpos

Build starpos (pre-processing step) for a specific pack.

ParameterTypeDescription
pack_keystringTarget pack
campaign_idstringCampaign identifier
process_hlp_spdbooleanProcess HLP/SPD data

Response: "Success"

type BuildStarposRequest = { BuildStarpos: [string, string, boolean] };
type BuildStarposResponse = "Success";
public class BuildStarposRequest
{
    public Tuple<string, string, bool> BuildStarpos { get; set; }
}
// Response: the literal string "Success".

BuildStarposPost

Build starpos (post-processing step) for a specific pack.

ParameterTypeDescription
pack_keystringTarget pack
campaign_idstringCampaign identifier
process_hlp_spdbooleanProcess HLP/SPD data

Response: { VecContainerPath: ContainerPath[] }

type BuildStarposPostRequest = { BuildStarposPost: [string, string, boolean] };
type BuildStarposPostResponse = { VecContainerPath: ContainerPath[] };
public class BuildStarposPostRequest
{
    public Tuple<string, string, bool> BuildStarposPost { get; set; }
}
public class BuildStarposPostResponse
{
    public List<ContainerPath> VecContainerPath { get; set; }
}

BuildStarposCleanup

Clean up starpos temporary files for a specific pack.

ParameterTypeDescription
pack_keystringTarget pack
campaign_idstringCampaign identifier
process_hlp_spdbooleanProcess HLP/SPD data

Response: "Success"

type BuildStarposCleanupRequest = { BuildStarposCleanup: [string, string, boolean] };
type BuildStarposCleanupResponse = "Success";
public class BuildStarposCleanupRequest
{
    public Tuple<string, string, bool> BuildStarposCleanup { get; set; }
}
// Response: the literal string "Success".

BuildStarposGetCampaingIds

Get campaign IDs available for starpos building from a specific pack.

ParameterTypeDescription
pack_keystringPack to query

Response: { HashSetString: string[] }

type BuildStarposGetCampaingIdsRequest = { BuildStarposGetCampaingIds: string };
type BuildStarposGetCampaingIdsResponse = { HashSetString: string[] };
public class BuildStarposGetCampaingIdsRequest
{
    public string BuildStarposGetCampaingIds { get; set; }
}
public class BuildStarposGetCampaingIdsResponse
{
    public HashSet<string> HashSetString { get; set; }
}

BuildStarposCheckVictoryConditions

Check if victory conditions file exists in a specific pack.

ParameterTypeDescription
pack_keystringPack to check

Response: "Success"

type BuildStarposCheckVictoryConditionsRequest = { BuildStarposCheckVictoryConditions: string };
type BuildStarposCheckVictoryConditionsResponse = "Success";
public class BuildStarposCheckVictoryConditionsRequest
{
    public string BuildStarposCheckVictoryConditions { get; set; }
}
// Response: the literal string "Success".

Animation Commands

UpdateAnimIds

Update animation IDs with an offset in a specific pack.

ParameterTypeDescription
pack_keystringPack to modify
starting_idnumberStarting ID
offsetnumberID offset

Response: { VecContainerPath: ContainerPath[] }

type UpdateAnimIdsRequest = { UpdateAnimIds: [string, number, number] };
type UpdateAnimIdsResponse = { VecContainerPath: ContainerPath[] };
public class UpdateAnimIdsRequest
{
    public Tuple<string, int, int> UpdateAnimIds { get; set; }
}
public class UpdateAnimIdsResponse
{
    public List<ContainerPath> VecContainerPath { get; set; }
}

GetAnimPathsBySkeletonName

Get animation paths by skeleton name.

ParameterTypeDescription
skeleton_namestringSkeleton name

Response: { HashSetString: string[] }

type GetAnimPathsBySkeletonNameRequest = { GetAnimPathsBySkeletonName: string };
type GetAnimPathsBySkeletonNameResponse = { HashSetString: string[] };
public class GetAnimPathsBySkeletonNameRequest
{
    public string GetAnimPathsBySkeletonName { get; set; }
}
public class GetAnimPathsBySkeletonNameResponse
{
    public HashSet<string> HashSetString { get; set; }
}

Table Commands

GetTablesFromDependencies

Get tables from dependencies by table name.

ParameterTypeDescription
table_namestringTable to query

Response: { VecRFile: RFile[] }

type GetTablesFromDependenciesRequest = { GetTablesFromDependencies: string };
type GetTablesFromDependenciesResponse = { VecRFile: RFile[] };
public class GetTablesFromDependenciesRequest
{
    public string GetTablesFromDependencies { get; set; }
}
public class GetTablesFromDependenciesResponse
{
    public List<RFile> VecRFile { get; set; }
}

GetTablesByTableName

Get table paths by table name from a specific Pack.

ParameterTypeDescription
pack_keystringPack to query
table_namestringTable name

Response: { VecString: string[] }

type GetTablesByTableNameRequest = { GetTablesByTableName: [string, string] };
type GetTablesByTableNameResponse = { VecString: string[] };
public class GetTablesByTableNameRequest
{
    public Tuple<string, string> GetTablesByTableName { get; set; }
}
public class GetTablesByTableNameResponse
{
    public List<string> VecString { get; set; }
}

AddKeysToKeyDeletes

Add keys to the key_deletes table in a specific pack.

ParameterTypeDescription
pack_keystringTarget pack
table_file_namestringTable file name
key_table_namestringKey table name
keysstring[]Keys to add

Response: { OptionContainerPath: ContainerPath | null }

type AddKeysToKeyDeletesRequest = {
  AddKeysToKeyDeletes: [string, string, string, string[]]
};
type AddKeysToKeyDeletesResponse = { OptionContainerPath: ContainerPath | null };
public class AddKeysToKeyDeletesRequest
{
    public Tuple<string, string, string, HashSet<string>> AddKeysToKeyDeletes { get; set; }
}
public class AddKeysToKeyDeletesResponse
{
    public ContainerPath? OptionContainerPath { get; set; }
}

Map Packing Commands

PackMap

Pack map tiles into a specific Pack.

ParameterTypeDescription
pack_keystringTarget pack
tile_mapsstring[]Tile map paths
tiles[string, string][][tile_path, tile_name] pairs

Response: { VecContainerPathVecContainerPath: [ContainerPath[], ContainerPath[]] } — added and deleted paths.

type PackMapRequest = {
  PackMap: [string, string[], [string, string][]]
};
type PackMapResponse = {
  VecContainerPathVecContainerPath: [ContainerPath[], ContainerPath[]]
};
public class PackMapRequest
{
    public Tuple<string, List<string>, List<Tuple<string, string>>> PackMap { get; set; }
}
public class PackMapResponse
{
    public Tuple<List<ContainerPath>, List<ContainerPath>> VecContainerPathVecContainerPath { get; set; }
}

3D Export Commands

ExportRigidToGltf

Export a RigidModel to glTF format.

ParameterTypeDescription
modelRigidModelModel to export
output_pathstringOutput file path

Response: "Success"

type ExportRigidToGltfRequest = { ExportRigidToGltf: [RigidModel, string] };
type ExportRigidToGltfResponse = "Success";
public class ExportRigidToGltfRequest
{
    public Tuple<RigidModel, string> ExportRigidToGltf { get; set; }
}
// Response: the literal string "Success".

Settings Commands

Settings Getters

All settings getters take a single key string parameter and return the value in a typed wrapper:

CommandResponse Type
SettingsGetBool{ Bool: boolean }
SettingsGetI32{ I32: number }
SettingsGetF32{ F32: number }
SettingsGetString{ String: string }
SettingsGetPathBuf{ PathBuf: string }
SettingsGetVecString{ VecString: string[] }
SettingsGetVecRaw{ VecU8: number[] }
{ "id": 1, "data": { "SettingsGetString": "game_selected" } }
// Shared shapes for every getter in this subsection:
type SettingsGetRequest =
  | { SettingsGetBool: string }
  | { SettingsGetI32: string }
  | { SettingsGetF32: string }
  | { SettingsGetString: string }
  | { SettingsGetPathBuf: string }
  | { SettingsGetVecString: string }
  | { SettingsGetVecRaw: string };

type SettingsGetResponse =
  | { Bool: boolean }
  | { I32: number }
  | { F32: number }
  | { String: string }
  | { PathBuf: string }
  | { VecString: string[] }
  | { VecU8: number[] };
// Request: { "SettingsGetBool": "some_key" } (or any of the other getter names).
// Response wrappers — one per getter:
public class SettingsGetBoolResponse    { public bool Bool { get; set; } }
public class SettingsGetI32Response     { public int I32 { get; set; } }
public class SettingsGetF32Response     { public float F32 { get; set; } }
public class SettingsGetStringResponse  { public string String { get; set; } }
public class SettingsGetPathBufResponse { public string PathBuf { get; set; } }
public class SettingsGetVecStringResponse { public List<string> VecString { get; set; } }
public class SettingsGetVecRawResponse  { public byte[] VecU8 { get; set; } }

SettingsGetAll

Get all settings at once (batch loading). Much more efficient than individual calls when you need several settings — one IPC round-trip instead of one per key.

Response: { SettingsAll: SettingsSnapshot } — see SettingsSnapshot for the field layout (bool / i32 / f32 / string / raw_data / vec_string maps).

{ "id": 1, "data": "SettingsGetAll" }
type SettingsGetAllRequest = "SettingsGetAll";
type SettingsGetAllResponse = { SettingsAll: SettingsSnapshot };
// Request: send the literal string "SettingsGetAll".
public class SettingsGetAllResponse
{
    public SettingsSnapshot SettingsAll { get; set; }
}

Settings Setters

All settings setters take a [key, value] tuple and return "Success":

CommandValue Type
SettingsSetBoolboolean
SettingsSetI32number
SettingsSetF32number
SettingsSetStringstring
SettingsSetPathBufstring
SettingsSetVecStringstring[]
SettingsSetVecRawnumber[]
{ "id": 1, "data": { "SettingsSetString": ["game_selected", "warhammer_3"] } }
// Shared shapes for every setter in this subsection:
type SettingsSetRequest =
  | { SettingsSetBool: [string, boolean] }
  | { SettingsSetI32: [string, number] }
  | { SettingsSetF32: [string, number] }
  | { SettingsSetString: [string, string] }
  | { SettingsSetPathBuf: [string, string] }
  | { SettingsSetVecString: [string, string[]] }
  | { SettingsSetVecRaw: [string, number[]] };

type SettingsSetResponse = "Success";
// Request wrappers — one per setter. Every response is the literal string "Success".
public class SettingsSetBoolRequest    { public Tuple<string, bool> SettingsSetBool { get; set; } }
public class SettingsSetI32Request     { public Tuple<string, int> SettingsSetI32 { get; set; } }
public class SettingsSetF32Request     { public Tuple<string, float> SettingsSetF32 { get; set; } }
public class SettingsSetStringRequest  { public Tuple<string, string> SettingsSetString { get; set; } }
public class SettingsSetPathBufRequest { public Tuple<string, string> SettingsSetPathBuf { get; set; } }
public class SettingsSetVecStringRequest { public Tuple<string, List<string>> SettingsSetVecString { get; set; } }
public class SettingsSetVecRawRequest  { public Tuple<string, byte[]> SettingsSetVecRaw { get; set; } }

SettingsClearPath

Clear a specific config path entry.

ParameterTypeDescription
pathstringPath to clear

Response: "Success"

type SettingsClearPathRequest = { SettingsClearPath: string };
type SettingsClearPathResponse = "Success";
public class SettingsClearPathRequest
{
    public string SettingsClearPath { get; set; }
}
// Response: the literal string "Success".

Path Commands

These commands return filesystem paths used by RPFM. All respond with { PathBuf: string }:

CommandDescription
ConfigPathConfig directory path
AssemblyKitPathAssembly Kit path for current game
BackupAutosavePathBackup autosave directory
OldAkDataPathOld AK data directory
SchemasPathSchemas directory
TableProfilesPathTable profiles directory
TranslationsLocalPathTranslations local directory
DependenciesCachePathDependencies cache directory
{ "id": 1, "data": "SchemasPath" }
// Shared shapes for every command in this section:
type PathCommandRequest =
  | "ConfigPath"
  | "AssemblyKitPath"
  | "BackupAutosavePath"
  | "OldAkDataPath"
  | "SchemasPath"
  | "TableProfilesPath"
  | "TranslationsLocalPath"
  | "DependenciesCachePath";
type PathCommandResponse = { PathBuf: string };
// Request: send the literal command name as a string.
public class PathCommandResponse
{
    public string PathBuf { get; set; }
}

Settings Backup Commands

BackupSettings

Backup current settings to memory (for restore on cancel).

Response: "Success"

type BackupSettingsRequest = "BackupSettings";
type BackupSettingsResponse = "Success";
// Request: send the literal string "BackupSettings".
// Response: the literal string "Success".

ClearSettings

Clear all settings and reset to defaults.

Response: "Success"

type ClearSettingsRequest = "ClearSettings";
type ClearSettingsResponse = "Success";
// Request: send the literal string "ClearSettings".
// Response: the literal string "Success".

RestoreBackupSettings

Restore settings from the in-memory backup.

Response: "Success"

type RestoreBackupSettingsRequest = "RestoreBackupSettings";
type RestoreBackupSettingsResponse = "Success";
// Request: send the literal string "RestoreBackupSettings".
// Response: the literal string "Success".

OptimizerOptions

Get the optimizer options configuration.

Response: { OptimizerOptions: OptimizerOptions }

type OptimizerOptionsRequest = "OptimizerOptions";
type OptimizerOptionsResponse = { OptimizerOptions: OptimizerOptions };
// Request: send the literal string "OptimizerOptions".
public class OptimizerOptionsResponse
{
    public OptimizerOptions OptimizerOptions { get; set; }
}

Debug Commands

GetMissingDefinitions

Export missing table definitions from a specific pack to a file (for debugging/development).

ParameterTypeDescription
pack_keystringPack to export from

Response: "Success"

type GetMissingDefinitionsRequest = { GetMissingDefinitions: string };
type GetMissingDefinitionsResponse = "Success";
public class GetMissingDefinitionsRequest
{
    public string GetMissingDefinitions { get; set; }
}
// Response: the literal string "Success".

Autosave Commands

TriggerBackupAutosave

Trigger an autosave backup of a specific Pack.

ParameterTypeDescription
pack_keystringPack to back up

Response: "Success"

type TriggerBackupAutosaveRequest = { TriggerBackupAutosave: string };
type TriggerBackupAutosaveResponse = "Success";
public class TriggerBackupAutosaveRequest
{
    public string TriggerBackupAutosave { get; set; }
}
// Response: the literal string "Success".

Responses

This page documents all possible responses from the RPFM server. Each response is wrapped in a Message with the same id as the originating command.

Responses follow the same serialization convention as commands:

  • Unit responses are plain strings: "Success"
  • Responses with data use a named wrapper: { "Bool": true }, { "Error": "File not found" }
{ "id": 1, "data": "Success" }
{ "id": 2, "data": { "Error": "File not found" } }
{ "id": 3, "data": { "ContainerInfoVecRFileInfo": [{ ... }, [{ ... }]] } }

Generic Responses

ResponsePayloadDescription
Success(none)Operation completed successfully
ErrorstringHuman-readable error message
SessionConnectednumberSession ID (unsolicited, sent on connect with id=0)
Unknown(none)Returned for unsupported/unrecognized file types

File-Type Decoded Responses

These are returned by DecodePackedFile. Each carries a tuple of [DecodedData, RFileInfo]:

ResponsePayload Type
AnimFragmentBattleRFileInfo[AnimFragmentBattle, RFileInfo]
AnimPackRFileInfo[RFileInfo[], RFileInfo]
AnimsTableRFileInfo[AnimsTable, RFileInfo]
AtlasRFileInfo[Atlas, RFileInfo]
AudioRFileInfo[Audio, RFileInfo]
BmdRFileInfo[Bmd, RFileInfo]
DBRFileInfo[DB, RFileInfo]
ESFRFileInfo[ESF, RFileInfo]
GroupFormationsRFileInfo[GroupFormations, RFileInfo]
ImageRFileInfo[Image, RFileInfo]
LocRFileInfo[Loc, RFileInfo]
MatchedCombatRFileInfo[MatchedCombat, RFileInfo]
PortraitSettingsRFileInfo[PortraitSettings, RFileInfo]
RigidModelRFileInfo[RigidModel, RFileInfo]
TextRFileInfo[Text, RFileInfo]
UICRFileInfo[UIC, RFileInfo]
UnitVariantRFileInfo[UnitVariant, RFileInfo]
VideoInfoRFileInfo[VideoInfo, RFileInfo]
VMDRFileInfo[Text, RFileInfo]
WSModelRFileInfo[Text, RFileInfo]

Example:

{
  "id": 5,
  "data": {
    "DBRFileInfo": [
      { "mysterious_byte": true, "guid": "", "table": { ... } },
      { "path": "db/units_tables/data", "container_name": "my_mod.pack", "timestamp": null, "file_type": "DB" }
    ]
  }
}

Scalar Responses

ResponsePayload TypeDescription
BoolbooleanBoolean value
F32number32-bit float
I32number32-bit integer
I32I32[number, number]Pair of integers
StringstringString value
PathBufstringFilesystem path

Collection Responses

ResponsePayload TypeDescription
VecBoolString[boolean, string][]Boolean-string pairs
VecContainerPathContainerPath[]List of container paths
VecContainerPathContainerPath[ContainerPath, ContainerPath][]Pairs of container paths (renames)
VecContainerPathOptionString[ContainerPath[], string or null]Paths with optional error message
VecContainerPathVecContainerPath[ContainerPath[], ContainerPath[]]Two lists of paths (added, deleted)
VecContainerPathVecContainerPathString[ContainerPath[], ContainerPath[], string]Paste result: added paths, cut-deleted paths, source pack key
VecContainerPathBTreeMapStringVecContainerPath[ContainerPath[], Record<string, ContainerPath[]>]Paths + grouping by pack key
VecContainerPathVecRFileInfo[ContainerPath[], RFileInfo[]]Paths and file info
VecContainerPathVecString[ContainerPath[], string[]]Paths and string list
VecDataSourceStringStringStringUsizeUsize[DataSource, string, string, string, number, number][]Reference search results: (data_source, pack_key, path, column_name, column_number, row_number). pack_key is empty for non-PackFile sources.
VecDefinitionDefinition[]List of definitions
VecFieldField[]List of fields
VecNoteNote[]List of notes
VecRFileRFile[]List of raw files
VecRFileInfoRFileInfo[]List of file metadata
VecStringstring[]List of strings
VecStringContainerInfo[string, ContainerInfo][]Pack key + metadata pairs
VecU8number[]Raw byte data
HashSetStringstring[]Set of strings
HashSetStringHashSetString[string[], string[]]Two sets of strings

Compound Responses

ResponsePayload TypeDescription
APIResponseAPIResponseProgram update check result
APIResponseGitGitResponseGit update check result
CompressionFormatCompressionFormatPack compression format
CompressionFormatDependenciesInfo[CompressionFormat, DependenciesInfo or null]Format + optional dependencies info
ContainerInfoContainerInfoPack metadata
ContainerInfoVecRFileInfo[ContainerInfo, RFileInfo[]]Pack metadata + file list
StringContainerInfo[string, ContainerInfo]Pack key + metadata
DataSourceStringUsizeUsize[DataSource, string, number, number]Navigation result
DefinitionDefinitionTable definition
DependenciesInfoDependenciesInfoDependencies information
DiagnosticsDiagnosticsDiagnostics report
GlobalSearchVecRFileInfo[GlobalSearch, RFileInfo[]]Search results + modified files
HashMapDataSourceHashMapStringRFileRecord<DataSource, Record<string, RFile>>Files by source and path
HashMapDataSourceHashSetContainerPathRecord<DataSource, ContainerPath[]>Paths by data source
HashMapI32TableReferencesRecord<number, TableReferences>Column references by index
HashMapStringHashMapStringVecStringRecord<string, Record<string, string[]>>Nested string maps
I32I32VecStringVecString[number, number, string[], string[]]Version change result
NoteNoteSingle note
OperationalModeOperationalModePer-pack operational mode
OptimizerOptionsOptimizerOptionsOptimizer configuration
OptionContainerPathContainerPath or nullOptional container path
OptionRFileInfoRFileInfo or nullOptional file info
OptionStringStringVecString[string, string, string[]] or nullOptional loc source data
PackSettingsPackSettingsPack settings
PackTranslationPackTranslationTranslation data
RFileDecodedRFileDecodedDecoded file content
SchemaSchemaFull schema
StringVecContainerPath[string, ContainerPath[]]String + path list
StringVecPathBuf[string, string[]]String + filesystem paths
TextTextText file content

Batch Settings Response

SettingsAll

Returned by SettingsGetAll. Wraps a SettingsSnapshot — a struct with six maps, one per primitive setting type:

{
  "SettingsAll": {
    "bool":       { "enable_telemetry": true },
    "i32":        { "autosave_interval": 300 },
    "f32":        { "ui_scale": 1.0 },
    "string":     { "game_selected": "warhammer_3" },
    "raw_data":   { "some_blob_setting": [0, 1, 2] },
    "vec_string": { "favourite_mods": ["a.pack", "b.pack"] }
  }
}

See SettingsSnapshot for the full field list.

MCP Interface

In addition to the WebSocket protocol, the RPFM Server exposes a Model Context Protocol (MCP) interface at /mcp. This allows AI assistants (such as Claude, Cursor, or any MCP-compatible client) to interact with RPFM programmatically using the standard MCP specification.

Transport

The MCP endpoint uses Streamable HTTP transport:

POST http://127.0.0.1:45127/mcp

Each MCP connection gets its own RPFM session, just like WebSocket connections.

How It Differs from WebSocket

AspectWebSocket (/ws)MCP (/mcp)
ProtocolCustom JSON messages with id/data envelopeStandard MCP (JSON-RPC 2.0)
TransportWebSocketStreamable HTTP
Interaction modelSend Command, receive ResponseCall named tools, receive JSON results
Session controlManual via ?session_id= and ClientDisconnectingManaged automatically by the MCP transport
Intended clientsCustom scripts, GUIsAI assistants and MCP-compatible tools

Both interfaces expose the same underlying functionality — every MCP tool maps to an internal Command and returns its Response serialized as JSON.

Connecting

Claude Desktop

Add the server to your claude_desktop_config.json:

{
  "mcpServers": {
    "rpfm": {
      "url": "http://127.0.0.1:45127/mcp"
    }
  }
}

Other MCP Clients

Any MCP client that supports Streamable HTTP transport can connect. Point it to http://127.0.0.1:45127/mcp.

Tool Reference

The MCP interface exposes 150 tools organized by category. Each tool accepts typed JSON arguments and returns the server’s Response serialized as JSON text.

Generic

ToolDescriptionArguments
call_commandCall any IPC command directly (for commands not yet wrapped as named tools)command: JSON string of the Command enum

Pack Lifecycle

ToolDescriptionKey Arguments
new_packCreate a new empty PackFile(none)
open_packfilesOpen one or more PackFilespaths: file paths
save_packfileSave a packpack_key
close_packClose a pack without savingpack_key
close_all_packsClose every open pack without saving(none)
save_pack_asSave a pack to a new pathpack_key, path
clean_and_save_pack_asSave a clean copy (use if normal save fails)pack_key, path
trigger_backup_autosaveTrigger a backup autosavepack_key
load_all_ca_pack_filesOpen all vanilla CA PackFiles for the selected game(none)
list_open_packsList all open packs with their keys and metadata(none)

Pack Metadata

ToolDescriptionKey Arguments
set_pack_file_typeSet the pack type (PFHFileType as JSON)pack_key, pack_file_type
change_compression_formatChange compression formatpack_key, format
change_index_includes_timestampToggle timestamp in pack indexpack_key, value
get_pack_file_pathGet the file path of a packpack_key
get_pack_file_nameGet the file name of a packpack_key
get_pack_settingsGet pack settingspack_key
set_pack_settingsSet pack settings (PackSettings as JSON)pack_key, settings
get_dependency_pack_files_listGet dependency pack listpack_key
set_dependency_pack_files_listSet dependency pack listpack_key, list

File Operations

ToolDescriptionKey Arguments
decode_packed_fileDecode a file from a packpack_key, path, source
new_packed_fileCreate a new file inside a packpack_key, path, new_file
add_packed_filesAdd files from disk to a packpack_key, source_paths, destination_paths
add_packed_files_from_pack_fileAdd files from another PackFilepack_key, source_pack_path, container_paths
add_packed_files_from_pack_file_to_animpackAdd files to an AnimPackpack_key, animpack_path, container_paths
add_packed_files_from_animpackAdd files from an AnimPackpack_key, source, animpack_path, container_paths
delete_packed_filesDelete files from a packpack_key, paths
copy_packed_filesCopy paths into the internal clipboardpaths_by_pack
cut_packed_filesCut paths into the internal clipboard (removed from source on paste)paths_by_pack
paste_packed_filesPaste from the internal clipboard into a pack folderpack_key, destination_path
duplicate_packed_filesDuplicate files in-place within the same pack (numeric suffix added)pack_key, paths
delete_from_animpackDelete files from an AnimPackpack_key, animpack_path, container_paths
extract_packed_filesExtract files to diskpack_key, source_paths, destination_path, export_as_tsv
rename_packed_filesRename files in a packpack_key, renames
save_packed_file_from_viewSave an edited decoded file backpack_key, path, data
save_packed_file_from_external_viewSave a file from an external programpack_key, internal_path, external_path
save_packed_files_to_pack_file_and_cleanSave files and optionally optimizepack_key, files, optimize
get_packed_file_raw_dataGet raw binary data of a filepack_key, value
open_packed_file_in_external_programOpen a file in an external programpack_key, source, container_path
open_containing_folderOpen the pack’s folder in file managerpack_key
clean_cacheClean the decode cachepack_key, paths
folder_existsCheck if a folder exists in a packpack_key, value
packed_file_existsCheck if a file exists in a packpack_key, value
get_packed_files_infoGet info of one or more filespack_key, values
get_rfile_infoGet info of a single filepack_key, value

Game Selection

ToolDescriptionKey Arguments
get_game_selectedGet the currently selected game(none)
set_game_selectedSet the current gamegame_name, rebuild_dependencies

Dependencies

ToolDescriptionKey Arguments
generate_dependencies_cacheGenerate the dependencies cache(none)
rebuild_dependenciesRebuild dependenciesvalue (true = full)
is_there_a_dependency_databaseCheck if a dependency database is loadedvalue
get_table_list_from_dependency_pack_fileGet table names from dependency packs(none)
get_custom_table_listGet custom table names from schema(none)
get_table_version_from_dependency_pack_fileGet table version from dependenciesvalue
get_table_definition_from_dependency_pack_fileGet table definition from dependenciesvalue
get_tables_from_dependenciesGet table data by namevalue
import_dependencies_to_open_pack_fileImport files from dependenciespack_key, paths
get_rfiles_from_all_sourcesGet files from all sourcespaths, lowercase
get_packed_files_names_starting_with_path_from_all_sourcesGet file names under a pathpath
local_art_set_idsGet local art set IDspack_key
dependencies_art_set_idsGet art set IDs from dependencies(none)
ToolDescriptionKey Arguments
global_searchRun a global searchpack_key, search
global_search_replace_matchesReplace specific matchespack_key, search, matches
global_search_replace_allReplace all matchespack_key, search
search_referencesFind all references to a valuepack_key, reference_map, value
get_reference_data_from_definitionGet reference data for columnspack_key, table_name, definition, force
go_to_definitionGo to a reference’s definitionpack_key, table_name, column_name, values
go_to_locGo to a loc key’s locationpack_key, value
get_source_data_from_loc_keyGet source data of a loc keypack_key, value

Schema

ToolDescriptionKey Arguments
save_schemaSave a schema to diskschema
update_current_schema_from_asskitUpdate schema from Assembly Kit(none)
update_schemasUpdate schemas from remote repository(none)
is_schema_loadedCheck if a schema is loaded(none)
get_schemaGet the current schema(none)
definitions_by_table_nameGet definitions for a tablevalue
definition_by_table_name_and_versionGet a definition by name and versionname, version
delete_definitionDelete a definitionname, version
referencing_columns_for_definitionGet columns referencing a tabletable_name, definition
fields_processedGet processed fields from a definitiondefinition
save_local_schema_patchSave local schema patchespatches
remove_local_schema_patches_for_tableRemove patches for a tablevalue
remove_local_schema_patches_for_table_and_fieldRemove patches for a fieldkey, value
import_schema_patchImport a schema patchpatches

Table Operations

ToolDescriptionKey Arguments
merge_filesMerge compatible tables into onepack_key, paths, merged_path, delete_source
update_tableUpdate a table to a newer versionpack_key, value
cascade_editionCascade edit across referenced datapack_key, table_name, definition, changes
get_tables_by_table_nameGet table paths by namepack_key, value
add_keys_to_key_deletesAdd keys to key_deletes tablepack_key, table_file_name, key_table_name, keys
export_tsvExport a table to TSVpack_key, tsv_path, table_path
import_tsvImport a TSV file to a tablepack_key, tsv_path, table_path

Diagnostics

ToolDescriptionKey Arguments
diagnostics_checkRun a full diagnostics checkignored, check_ak_only_refs
diagnostics_updateUpdate diagnostics for changed filesdiagnostics, paths, check_ak_only_refs
add_line_to_pack_ignored_diagnosticsAdd to ignored diagnosticspack_key, value
get_missing_definitionsExport missing table definitionspack_key

Notes

ToolDescriptionKey Arguments
notes_for_pathGet notes under a pathpack_key, value
add_noteAdd a notepack_key, note
delete_noteDelete a notepack_key, path, id

Optimization

ToolDescriptionKey Arguments
optimize_pack_fileOptimize a packpack_key, options
get_optimizer_optionsGet default optimizer options(none)

Updates

ToolDescription
check_updatesCheck for RPFM updates
check_schema_updatesCheck for schema updates
check_lua_autogen_updatesCheck for Lua autogen updates
check_empire_and_napoleon_ak_updatesCheck for Empire/Napoleon AK updates
check_translations_updatesCheck for translation updates
update_lua_autogenUpdate Lua autogen
update_main_programUpdate the program
update_empire_and_napoleon_akUpdate Empire/Napoleon AK files
update_translationsUpdate translations

Settings

ToolDescriptionKey Arguments
settings_get_boolGet a boolean settingvalue (key)
settings_get_i32Get an i32 settingvalue (key)
settings_get_f32Get an f32 settingvalue (key)
settings_get_stringGet a string settingvalue (key)
settings_get_path_bufGet a PathBuf settingvalue (key)
settings_get_vec_stringGet a Vec<String> settingvalue (key)
settings_get_vec_rawGet a raw bytes settingvalue (key)
settings_get_allGet all settings at once(none)
settings_set_boolSet a boolean settingkey, value
settings_set_i32Set an i32 settingkey, value
settings_set_f32Set an f32 settingkey, value
settings_set_stringSet a string settingkey, value
settings_set_path_bufSet a PathBuf settingkey, value
settings_set_vec_stringSet a Vec<String> settingkey, value
settings_set_vec_rawSet a raw bytes settingkey, value
backup_settingsBackup settings to memory(none)
clear_settingsClear all settings(none)
restore_backup_settingsRestore settings from backup(none)

Path Queries

ToolDescription
config_pathGet the config path
assembly_kit_pathGet the Assembly Kit path
backup_autosave_pathGet the backup autosave path
old_ak_data_pathGet the old AK data path
schemas_pathGet the schemas path
table_profiles_pathGet the table profiles path
translations_local_pathGet the translations local path
dependencies_cache_pathGet the dependencies cache path
settings_clear_pathClear a config path

Specialized

ToolDescriptionKey Arguments
open_pack_infoGet pack info and file listpack_key
initialize_my_mod_folderInitialize a MyMod foldername, game, sublime, vscode, gitignore
live_exportLive export a pack for testingpack_key
patch_siege_aiPatch SiegeAI for Warhammer mapspack_key
pack_mapPack map tilespack_key, tile_maps, tiles
generate_missing_loc_dataGenerate missing loc entriespack_key
get_pack_translationGet translation datapack_key, language
build_starpos_get_campaign_idsGet campaign IDs for starpospack_key
build_starpos_check_victory_conditionsCheck victory conditions filepack_key
build_starposBuild starpos (pre-processing)pack_key, campaign_id, process_hlp_spd
build_starpos_postBuild starpos (post-processing)pack_key, campaign_id, process_hlp_spd
build_starpos_cleanupClean up starpos temp filespack_key, campaign_id, process_hlp_spd
update_anim_idsUpdate animation IDspack_key, starting_id, offset
get_anim_paths_by_skeleton_nameGet anim paths by skeletonvalue
export_rigid_to_gltfExport RigidModel to glTFrigid_model, output_path
set_video_formatChange video formatpack_key, path, format

Response Format

All MCP tool responses are JSON-serialized versions of the server’s internal Response enum. The same serialization convention applies — refer to the Responses page for the full list of response types and their payloads.

Typical Workflow

A typical MCP session follows this pattern:

  1. Set the game: set_game_selected with the game key and rebuild_dependencies: true
  2. Open a pack: open_packfiles with the file path(s)
  3. Browse files: open_pack_info to get the pack’s file tree
  4. Read data: decode_packed_file to decode individual files
  5. Modify data: save_packed_file_from_view to save edited data back
  6. Save: save_packfile to write changes to disk

Example: Reading a DB Table

Call set_game_selected:

{ "game_name": "warhammer_3", "rebuild_dependencies": true }

Call open_packfiles:

{ "paths": ["/path/to/my_mod.pack"] }

The response contains the pack_key — use it in subsequent calls:

Call decode_packed_file:

{ "pack_key": "the_pack_key", "path": "db/units_tables/data", "source": "PackFile" }

The response will be a DBRFileInfo containing the decoded table data and file metadata.

Common pack_key Pattern

Most tools require a pack_key argument to identify which open pack to operate on. After opening a pack with open_packfiles, the response includes the key. You can also call list_open_packs at any time to see all open packs and their keys.

JSON String Arguments

Some tools accept complex types (like ContainerPath, Definition, GlobalSearch) as JSON strings. These must be passed as a serialized JSON string in the argument field — not as a nested object. For example:

{
  "pack_key": "my_pack",
  "paths": "[{\"File\": \"db/units_tables/data\"}]"
}

Refer to the Shared Types page for the structure of these types.

Client Example

This page provides a complete client implementation example in TypeScript. The same patterns apply to any language with WebSocket and JSON support.

TypeScript Client

The following class wraps the WebSocket connection and provides typed convenience methods for common operations.

Connection and Session Handling

class RpfmClient {
  private ws: WebSocket;
  private nextId = 1;
  private pending = new Map<number, {
    resolve: (resp: any) => void;
    reject: (err: Error) => void;
  }>();
  public sessionId: number | null = null;

  constructor(url = "ws://127.0.0.1:45127/ws") {
    this.ws = new WebSocket(url);
    this.ws.onmessage = (event) => {
      const msg = JSON.parse(event.data);

      // Handle SessionConnected (unsolicited, id=0)
      if (typeof msg.data === "object" && "SessionConnected" in msg.data) {
        this.sessionId = msg.data.SessionConnected;
        console.log(`Connected to session ${this.sessionId}`);
        return;
      }

      const handler = this.pending.get(msg.id);
      if (handler) {
        this.pending.delete(msg.id);
        if (typeof msg.data === "object" && "Error" in msg.data) {
          handler.reject(new Error(msg.data.Error));
        } else {
          handler.resolve(msg.data);
        }
      }
    };
  }

  send(command: object | string): Promise<any> {
    return new Promise((resolve, reject) => {
      const id = this.nextId++;
      this.pending.set(id, { resolve, reject });
      this.ws.send(JSON.stringify({ id, data: command }));
    });
  }
using System.Collections.Concurrent;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;

public class RpfmClient : IDisposable
{
    private readonly ClientWebSocket _ws = new();
    private int _nextId = 1;
    private readonly ConcurrentDictionary<int, TaskCompletionSource<JsonElement>> _pending = new();
    public int? SessionId { get; private set; }

    public async Task ConnectAsync(string url = "ws://127.0.0.1:45127/ws")
    {
        await _ws.ConnectAsync(new Uri(url), CancellationToken.None);
        _ = Task.Run(ReceiveLoop);
    }

    private async Task ReceiveLoop()
    {
        var buffer = new byte[65536];
        while (_ws.State == WebSocketState.Open)
        {
            var result = await _ws.ReceiveAsync(buffer, CancellationToken.None);
            var json = Encoding.UTF8.GetString(buffer, 0, result.Count);
            var msg = JsonDocument.Parse(json).RootElement;

            var data = msg.GetProperty("data");

            // Handle SessionConnected (unsolicited, id=0)
            if (data.ValueKind == JsonValueKind.Object
                && data.TryGetProperty("SessionConnected", out var sid))
            {
                SessionId = sid.GetInt32();
                Console.WriteLine($"Connected to session {SessionId}");
                continue;
            }

            var id = msg.GetProperty("id").GetInt32();
            if (_pending.TryRemove(id, out var tcs))
            {
                if (data.ValueKind == JsonValueKind.Object
                    && data.TryGetProperty("Error", out var err))
                {
                    tcs.SetException(new Exception(err.GetString()));
                }
                else
                {
                    tcs.SetResult(data);
                }
            }
        }
    }

    public async Task<JsonElement> SendAsync(object command)
    {
        var id = Interlocked.Increment(ref _nextId);
        var tcs = new TaskCompletionSource<JsonElement>();
        _pending[id] = tcs;

        var msg = JsonSerializer.Serialize(new { id, data = command });
        var bytes = Encoding.UTF8.GetBytes(msg);
        await _ws.SendAsync(bytes, WebSocketMessageType.Text, true, CancellationToken.None);

        return await tcs.Task;
    }

Key points:

  • Each request gets a unique id for correlation
  • The SessionConnected message arrives immediately on connection with id: 0
  • Error responses are automatically converted to rejected promises
  • Multiple requests can be in flight simultaneously

Typed Convenience Methods

  // --- Pack management ---

  async openPack(paths: string[]): Promise<[string, any]> {
    const resp = await this.send({ OpenPackFiles: paths });
    return resp.StringContainerInfo;
  }

  async listOpenPacks(): Promise<[string, any][]> {
    const resp = await this.send("ListOpenPacks");
    return resp.VecStringContainerInfo;
  }

  async closePack(packKey: string): Promise<void> {
    await this.send({ ClosePack: packKey });
  }

  async closeAllPacks(): Promise<void> {
    await this.send("CloseAllPacks");
  }

  async savePack(packKey: string): Promise<any> {
    const resp = await this.send({ SavePack: packKey });
    return resp.ContainerInfo;
  }

  async savePackAs(packKey: string, path: string): Promise<any> {
    const resp = await this.send({ SavePackAs: [packKey, path] });
    return resp.ContainerInfo;
  }

  // --- File operations ---

  async getTreeView(packKey: string): Promise<[any, any[]]> {
    const resp = await this.send({ GetPackFileDataForTreeView: packKey });
    return resp.ContainerInfoVecRFileInfo;
  }

  async decodeFile(packKey: string, path: string, source = "PackFile"): Promise<any> {
    return this.send({ DecodePackedFile: [packKey, path, source] });
  }

  async deleteFiles(packKey: string, paths: any[]): Promise<any[]> {
    const resp = await this.send({ DeletePackedFiles: [packKey, paths] });
    return resp.VecContainerPath;
  }

  async extractFiles(
    packKey: string,
    paths: Record<string, any[]>,
    destPath: string,
    asTsv = false,
  ): Promise<[string, string[]]> {
    const resp = await this.send({
      ExtractPackedFiles: [packKey, paths, destPath, asTsv]
    });
    return resp.StringVecPathBuf;
  }

  // --- Game selection ---

  async setGame(gameKey: string, rebuildDeps: boolean): Promise<void> {
    await this.send({ SetGameSelected: [gameKey, rebuildDeps] });
  }

  // --- Settings ---

  async getSetting(key: string): Promise<string> {
    const resp = await this.send({ SettingsGetString: key });
    return resp.String;
  }

  async getAllSettings(): Promise<{
    bools: Record<string, boolean>;
    ints: Record<string, number>;
    floats: Record<string, number>;
    strings: Record<string, string>;
  }> {
    const resp = await this.send("SettingsGetAll");
    const [bools, ints, floats, strings] = resp.SettingsAll;
    return { bools, ints, floats, strings };
  }

  // --- Lifecycle ---

  async disconnect(): Promise<void> {
    await this.send("ClientDisconnecting");
    this.ws.close();
  }
}
    // --- Pack management ---

    public async Task<JsonElement> OpenPackAsync(string[] paths)
    {
        var resp = await SendAsync(new { OpenPackFiles = paths });
        return resp.GetProperty("StringContainerInfo");
    }

    public async Task<JsonElement> ListOpenPacksAsync()
    {
        var resp = await SendAsync("ListOpenPacks");
        return resp.GetProperty("VecStringContainerInfo");
    }

    public async Task ClosePackAsync(string packKey)
    {
        await SendAsync(new { ClosePack = packKey });
    }

    public async Task CloseAllPacksAsync()
    {
        await SendAsync("CloseAllPacks");
    }

    public async Task<JsonElement> SavePackAsync(string packKey)
    {
        var resp = await SendAsync(new { SavePack = packKey });
        return resp.GetProperty("ContainerInfo");
    }

    public async Task<JsonElement> SavePackAsAsync(string packKey, string path)
    {
        var resp = await SendAsync(new { SavePackAs = new[] { packKey, path } });
        return resp.GetProperty("ContainerInfo");
    }

    // --- File operations ---

    public async Task<JsonElement> GetTreeViewAsync(string packKey)
    {
        var resp = await SendAsync(new { GetPackFileDataForTreeView = packKey });
        return resp.GetProperty("ContainerInfoVecRFileInfo");
    }

    public async Task<JsonElement> DecodeFileAsync(
        string packKey, string path, string source = "PackFile")
    {
        return await SendAsync(new { DecodePackedFile = new[] { packKey, path, source } });
    }

    public async Task<JsonElement> DeleteFilesAsync(string packKey, object[] paths)
    {
        var resp = await SendAsync(new { DeletePackedFiles = new object[] { packKey, paths } });
        return resp.GetProperty("VecContainerPath");
    }

    public async Task<JsonElement> ExtractFilesAsync(
        string packKey,
        Dictionary<string, object[]> paths,
        string destPath,
        bool asTsv = false)
    {
        var resp = await SendAsync(
            new { ExtractPackedFiles = new object[] { packKey, paths, destPath, asTsv } });
        return resp.GetProperty("StringVecPathBuf");
    }

    // --- Game selection ---

    public async Task SetGameAsync(string gameKey, bool rebuildDeps)
    {
        await SendAsync(new { SetGameSelected = new object[] { gameKey, rebuildDeps } });
    }

    // --- Settings ---

    public async Task<string> GetSettingAsync(string key)
    {
        var resp = await SendAsync(new { SettingsGetString = key });
        return resp.GetProperty("String").GetString()!;
    }

    public async Task<JsonElement> GetAllSettingsAsync()
    {
        var resp = await SendAsync("SettingsGetAll");
        return resp.GetProperty("SettingsAll");
    }

    // --- Lifecycle ---

    public async Task DisconnectAsync()
    {
        await SendAsync("ClientDisconnecting");
        await _ws.CloseAsync(
            WebSocketCloseStatus.NormalClosure, "done", CancellationToken.None);
    }

    public void Dispose() => _ws.Dispose();
}

Usage Example

async function main() {
  const client = new RpfmClient();

  // Wait for connection
  await new Promise<void>((resolve) => {
    client["ws"].onopen = () => resolve();
  });
  console.log(`Session ID: ${client.sessionId}`);

  // Select a game
  await client.setGame("warhammer_3", true);

  // Open a pack file
  const [packKey, containerInfo] = await client.openPack([
    "/home/user/mods/my_mod.pack"
  ]);
  console.log(`Opened pack: ${containerInfo.file_name} (key: ${packKey})`);

  // Get the file tree
  const [info, files] = await client.getTreeView(packKey);
  console.log(`Pack contains ${files.length} files`);

  // Decode a DB table
  const decoded = await client.decodeFile(
    packKey,
    "db/units_tables/data",
    "PackFile"
  );
  if ("DBRFileInfo" in decoded) {
    const [db, fileInfo] = decoded.DBRFileInfo;
    console.log(`Table: ${db.table.table_name}, rows: ${db.table.table_data.length}`);
  }

  // Extract files to disk
  const [extractPath, extractedFiles] = await client.extractFiles(
    packKey,
    { PackFile: [{ File: "db/units_tables/data" }] },
    "/tmp/extracted"
  );
  console.log(`Extracted ${extractedFiles.length} files to ${extractPath}`);

  // Save and disconnect
  await client.savePack(packKey);
  await client.disconnect();
}

main().catch(console.error);
using var client = new RpfmClient();
await client.ConnectAsync();

// Wait briefly for SessionConnected
await Task.Delay(500);
Console.WriteLine($"Session ID: {client.SessionId}");

// Select a game
await client.SetGameAsync("warhammer_3", true);

// Open a pack file
var packResult = await client.OpenPackAsync(new[] { "/home/user/mods/my_mod.pack" });
var packKey = packResult[0].GetString()!;
var containerInfo = packResult[1];
Console.WriteLine($"Opened pack: {containerInfo.GetProperty("file_name")} (key: {packKey})");

// Get the file tree
var treeView = await client.GetTreeViewAsync(packKey);
var files = treeView[1];
Console.WriteLine($"Pack contains {files.GetArrayLength()} files");

// Decode a DB table
var decoded = await client.DecodeFileAsync(packKey, "db/units_tables/data", "PackFile");
if (decoded.TryGetProperty("DBRFileInfo", out var dbResult))
{
    var db = dbResult[0];
    var table = db.GetProperty("table");
    Console.WriteLine(
        $"Table: {table.GetProperty("table_name")}, " +
        $"rows: {table.GetProperty("table_data").GetArrayLength()}");
}

// Extract files to disk
var extractResult = await client.ExtractFilesAsync(
    packKey,
    new Dictionary<string, object[]>
    {
        ["PackFile"] = new object[] { new { File = "db/units_tables/data" } }
    },
    "/tmp/extracted");
var extractPath = extractResult[0].GetString();
var extractedFiles = extractResult[1];
Console.WriteLine($"Extracted {extractedFiles.GetArrayLength()} files to {extractPath}");

// Save and disconnect
await client.SavePackAsync(packKey);
await client.DisconnectAsync();

Adapting to Other Languages

The protocol is language-agnostic. To implement a client in another language:

  1. Connect to ws://127.0.0.1:45127/ws using any WebSocket library
  2. Send JSON messages in the format { "id": <number>, "data": <command> }
  3. Receive JSON messages and match responses by id
  4. Handle the SessionConnected message (id=0) on connect
  5. Send ClientDisconnecting before closing the connection

The JSON serialization follows Rust’s serde conventions:

  • Unit variants: "VariantName"
  • Newtype variants: { "VariantName": value }
  • Tuple variants: { "VariantName": [v1, v2, ...] }

Settings & preferences

Open the Preferences dialog from PackFile → Settings (or via its shortcut, Ctrl+,).

Settings are stored on the server side and cached client-side, so they’re consistent across rpfm_ui runs and across multiple UIs talking to the same rpfm_server. Most changes take effect immediately; a few (notably language and other UI-layout settings) require a restart.

The dialog is a single scrollable form with a left-hand navigation pane that jumps to one of seven sections. The list below is a tour, not an exhaustive reference — for the full list of setting keys see the rpfm_ipc::settings_keys API docs.

Paths

The first section you’ll touch as a new user. Holds two groups:

  • Game paths — for each supported game, a collapsible “spoiler” with the game install path and (where applicable) the Assembly Kit install path.
  • Extra pathsMyMod base path (root folder for MyMod projects) and Secondary path (extra mods folder in addition to the game’s data/).

General

  • Language — RPFM’s UI language. Requires a restart.
  • Default game — which Total War game RPFM starts on.
  • Update channelStable or Beta.
  • Autosave amount and Autosave interval (in minutes).
  • Check for X on start toggles for program, schema, lua-autogen and old-AK updates.
  • Allow editing of CA Packfiles, Disable file previews, Start maximized.
  • Pack-tree behaviour toggles: expand on add, include base folder on add-from-folder, delete empty folders on delete, ignore game files in AK, multifolder file picker, drag-and-drop in the pack contents tree.

Table

UI behaviour for the table editors:

  • Adjust columns to content, Disable combos on tables, Extend last column, Tight table mode, Resize on edit.
  • Tables use old column order (and a TSV variant) — restores the legacy column ordering.
  • Disable UUID regeneration on DB tables — don’t auto-generate a new UUID on table save.
  • Use right-side markers, Enable lookups, Enable icons, Enable diff markers.
  • Colour pickers for Added, Modified, Error, Warning and Info marks (separate light/dark colour for each).

Debug

Hidden behind a Debug section header rather than a feature flag, but most of these only matter to developers:

  • Check for missing table definitions, Enable debug menu (exposes the hidden Debug menu in the menu bar), Enable unit editor, Enable ESF editor, Use debug view for Unit Variant, Enable renderer (only when built with support_model_renderer).
  • Use lazy loading — defer decoding files until they’re opened.
  • Action buttons: Clear Dependencies Cache Folder, Clear Autosave Folder, Clear Schema Folder, Clear Layout Settings, Add RPFM to Runcher Tools.

Diagnostics

  • Trigger diagnostics on Pack open.
  • Trigger diagnostics on table edit.

There are no per-check toggles in the Preferences dialog — per-diagnostic ignores are configured per Pack via the Pack Settings tab and via the Diagnostics panel’s right-click “Ignore…” actions.

Telemetry

  • Enable Usage Telemetry — opt-out anonymous action counters.
  • Enable Crash Reports — opt-out automatic crash report upload to Sentry.

Both default to on. Both take effect immediately. See Telemetry & crash reports for the full picture.

AI

API keys for the AI-backed features (used by the Translator, among others):

  • OpenAI API key.
  • DeepL API key.

Shortcuts

The Preferences dialog has a Shortcuts button at the bottom that opens a separate KDE-style shortcuts dialog where every action can be rebound. See Keyboard shortcuts.

Where settings live on disk

  • Server-side authoritative copy: <config>/settings.json. The exact <config> folder depends on platform (e.g. ~/.config/rpfm/ on Linux, %AppData%\rpfm\ on Windows). On debug builds, it’s the working directory.
  • Backup: a single .bak file is written next to it when the main file fails to load, so a corrupted settings file can be recovered manually.
  • UI-side cache: in-memory only — refreshed from the server on launch and on each preferences-saved event.

Keyboard shortcuts

RPFM lets you rebind every shortcut from PackFile → Settings → Shortcuts (the Preferences dialog has a Shortcuts button at the bottom). The dialog itself is the standard KDE KShortcutsDialog, which groups every action by collection and flags conflicts within the same scope.

The list below shows the defaults as defined in the source. Most shortcuts are configurable per scope, so a binding may legitimately fire in one widget (e.g. the table editor) and be unbound in another.

The command palette is the universal fallback. Press Ctrl+P (file palette) or Ctrl+Shift+P (command palette) and type the name of what you want. If the action exists, you can launch it from there.

Pack Menu

ActionDefault
New PackCtrl+N
Open PacksCtrl+O
Open & Merge PacksCtrl+Shift+O
Save All PacksCtrl+S
Install PackCtrl+Shift+I
Uninstall PackCtrl+Shift+U
Load All CA PacksCtrl+G
SettingsCtrl+,
Quit(unbound)

Save active Pack and Save Pack As are exposed via the Pack root context menu and the file-tab context menu rather than as global shortcuts.

File tabs

ActionDefault
Close TabCtrl+W
Previous TabCtrl+Shift+Tab
Next TabCtrl+Tab
Toggle Quick Notes(unbound)
Import From Dependencies(unbound)

View menu

ActionDefault
Toggle Global SearchCtrl+Shift+F
Toggle Pack Contents(unbound)
Toggle Diagnostics(unbound)
Toggle Dependencies(unbound)
Toggle References(unbound)

Command palette

ActionDefault
Open file paletteCtrl+P
Open command paletteCtrl+Shift+P

Pack tree (context-scoped)

ActionDefault
Add FileCtrl+A
Add FolderCtrl+Shift+A
Add From PackCtrl+Alt+A
New FolderCtrl+F
New DBCtrl+D
New LocCtrl+L
New TextCtrl+T
New Quick FileCtrl+Q
New AnimPack / Portrait Settings(unbound)
Merge FilesCtrl+M
DeleteDel
ExtractCtrl+E
Rename / MoveCtrl+R or F2
CopyCtrl+C
CutCtrl+X
PasteCtrl+V
DuplicateCtrl+Shift+D
Open with DecoderCtrl+J
Open with External ProgramCtrl+K
Open Pack NotesCtrl+Y
Expand AllCtrl++
Collapse AllCtrl+-

Table editor (DB / Loc)

ActionDefault
Add RowCtrl+Shift+A
Insert RowCtrl+I
Delete RowCtrl+Del
Delete Filtered-Out RowsCtrl+Shift+Del
Clone & Insert RowCtrl+D
Clone & Append RowCtrl+Shift+D
CopyCtrl+C
Copy as LUA TableCtrl+Shift+C
PasteCtrl+V
Paste as New RowCtrl+Shift+V
Rewrite SelectionCtrl+Y
Invert SelectionCtrl+-
SearchCtrl+F
Undo / RedoCtrl+Z / Ctrl+Shift+Z
Smart DeleteDel
Find References / Go To Definition / Go To File / Patch Columns / Generate IDs / Import TSV / Export TSV / etc.(unbound)

Decoder

ActionDefault
Move Field UpCtrl+Up
Move Field DownCtrl+Down
Move Field LeftCtrl+Left
Move Field RightCtrl+Right
Delete FieldCtrl+Del
Delete DefinitionCtrl+Del
Load DefinitionCtrl+L

Text editor

The text editor is a KTextEditor widget and honours your KDE keyboard configuration. RPFM removes its built-in file-save and file-save-as actions and re-binds save into the Pack via Ctrl+S. Find / replace use KTextEditor’s standard Ctrl+F / Ctrl+R. Everything else is KTextEditor-standard.

Customising

The Shortcuts dialog (PackFile → Settings → Shortcuts) shows every action grouped by collection (Pack Menu, View Menu, Pack Tree Context Menu, Table Editor, Decoder, etc.). Click a binding to edit it; conflicts within the same scope are flagged.

Bindings are persisted via KDE’s standard KActionCollection::writeSettings/readSettings, not in a single RPFM-owned file. On Linux that typically lands in ~/.config/<orgname>rc. To reset, open the Shortcuts dialog and use its built-in Defaults action.

Supported games

RPFM supports every Total War since Empire, with depth that varies depending on how interesting the game is to mod and how much the format has been reverse-engineered.

GameGame keyEditingNotes
Total War: Pharaoh – Dynastiespharaoh_dynastiesFull
Total War: PharaohpharaohFull
Total War: Warhammer 3warhammer_3FullFirst game with twad_key_deletes (patch 6.3+).
Total War Saga: TroytroyFull
Total War: Three Kingdomsthree_kingdomsFull
Total War: Warhammer 2warhammer_2Full
Total War: WarhammerwarhammerFull
Total War Saga: Thrones of Britanniathrones_of_britanniaFull
Total War: AttilaattilaFull
Total War: Rome 2rome_2Full
Total War: Shogun 2shogun_2Full
Total War: NapoleonnapoleonFullNo Assembly Kit ever shipped — RPFM uses archived AK definitions.
Total War: EmpireempireFullNo Assembly Kit ever shipped — RPFM uses archived AK definitions.
Total War: ArenaarenaRead-onlyDiscontinued; supports_editing is false, so RPFM can browse Arena Packs but won’t save them.

Game-specific quirks

A few rough edges worth knowing about:

  • Compression of DB tables is only safe on Warhammer 3 and newer; older games crash on compressed table data, so the compression code skips DB tables for them. Pharaoh / Pharaoh Dynasties / Three Kingdoms / Troy / Warhammer 2 still support Lzma1 for non-table files. Warhammer 3 adds Lz4 and Zstd on top of Lzma1. Anything older than Warhammer 1 supports no compression at all.
  • twad_key_deletes is Warhammer 3 (6.3+) only. See Datacores.
  • Animation ID lookups for Three Kingdoms, Troy, Warhammer 2 and Warhammer 3 use the per-game anim_ids_*.csv shipped with the schemas repo.
  • Group formations were rebuilt for Warhammer 3 and adopt different on-disk formats per game; RPFM keeps each variant straight.
  • MatchedCombat has different decode paths for Three Kingdoms vs Warhammer 3 vs the rest.

What “full” support means

For “Full” games, RPFM can:

  • Open and save Packs of every type the engine accepts.
  • Decode and re-encode every commonly-edited file format (DB, Loc, Text, AnimPack, Animations, Portrait Settings, Atlas, Group Formations).
  • Run diagnostics, global search, references, dependency analysis.
  • Drive the optimiser, the translator, and (where applicable) startpos build, map packing, and SiegeAI patching.

Some less-common formats (BMD, ESF, RigidModel) are read-only or partially supported across all games — see Editors overview for the per-format support level.

Vanilla data lookups

For every game with a configured install path, RPFM builds a dependency cache so that vanilla data is available for reference lookups, ITM detection, the optimiser, and more. The cache is per-game and needs regenerating when the game gets a content patch.

Schemas & patches

Schemas describe the binary layout of every supported DB table for every supported game. Without them, RPFM has no idea what a units_tables/data file looks like — it’s a binary blob.

Where schemas come from

Schemas live in the rpfm-schemas repository. About → Check Updates pulls the latest version from upstream over Git as part of the unified update check (it also covers the program itself, lua autogen and the legacy AK definitions). The Git update path is gated behind the integration_git Cargo feature, which is enabled by default in the shipped UI build.

Each schema is a RON file: schema_<game>.ron. Plus:

  • anim_ids_<game>.csv — per-game animation ID maps for matched_combat / anim_fragment_battle.

When to update schemas

When RPFM says there’s a new schema update available. There’s no benefit on using older versions of the schemas.

Patches

Schemas describe what a table is, and a large part of their information is extracted automatically from the Assembly Kit of the games. Patches are manually generated corrections for the schemas: lookup overrides, default values, tooltips, type-display hints, etc. They live alongside the schemas and overlay the base schema at runtime.

To contribute a patch upstream so everyone benefits, PR your changes to rpfm-schemas. The repo’s README covers the file format.

Adding or fixing a schema

When a game patch changes a table layout (new columns, reorder, type change), the existing schema is missing the new definitions and the tables won’t decode. Fix it:

  1. Open one of the affected files in the DB Decoder.
  2. Adjust the column types until the first row decoded field shows sane values.
  3. Save the definition into the schema.
  4. Try to open the table again and see if it opens correctly.
  5. PR your changes to rpfm-schemas.

API surface

If you’re consuming schemas from Rust, the rpfm_lib::schema module is the entry point. Key types:

  • Schema — the full per-game schema document.
  • Definition — a single table version’s definition.
  • Field and FieldType — column metadata.

Telemetry & crash reports

RPFM ships two pieces of opt-out telemetry, both controlled from Preferences → Telemetry:

  • Enable Usage Telemetry — anonymous counters of which actions get used.
  • Enable Crash Reports — automatic upload of panic reports and breadcrumbs to Sentry when RPFM crashes.

Both default to on. Both take effect immediately.

What gets sent

Usage telemetry

When enabled, RPFM increments per-action counters in memory and flushes them to Sentry as a single event when the program shuts down gracefully (or, on the server side, when a session disconnects). The events are tagged so server and UI activity can be told apart.

Each counter is just (action_name, count). There’s no payload, no path, no Pack name, no game, no user identifier — just “Open PackFile happened 7 times in this session”.

Examples of action names that actually appear in the codebase: Open PackFile Menu, Open PackedFile Full, Save Pack, Diagnostics Check, Cascade Edition, Optimize PackFile, Open In External Program, Merge Tables. They’re whatever string a rpfm_telemetry::track_action(...) call passes — there’s no central registry.

Crash reports

When enabled, RPFM:

  • Captures panics as they happen.
  • Writes a local crash report under the error/ folder.
  • Uploads the crash report to Sentry, along with breadcrumbs (a structured trail of recent operations in the session).
  • Tracks session start / session end so we can tell how often crashes occur per session.

Crash reports include the crashing thread’s stack trace, RPFM’s version, the OS family, and the breadcrumb trail. They do not include the contents of any open Pack, file paths, or anything else identifying.

In debug builds the Sentry guard is created with an empty DSN, so panics dump locally but don’t upload anywhere.

Local crash reports

Even with crash report uploads disabled, RPFM still writes panic reports locally under <config>/error/ as TOML files. They’re useful for filing bug reports yourself: when something crashes, look in error/, attach the file to a GitHub issue, and that gives the developers exactly what an automatic upload would have.

API surface

If you’re embedding rpfm_telemetry into your own RPFM-derived tool, the rpfm_telemetry API docs cover the full surface. The README in the crate has more on the design.

Building from source

If you want to compile RPFM yourself — to contribute, to enable optional features, or to run the latest develop — these are the per-platform instructions.

Windows

You’ll need:

  • Windows SDK.
  • Visual Studio Community 2022 with the MSVC C++ build tools workload.
  • Rust 1.81 (or newer) with the MSVC toolchain.
  • Craft from KDE.

Once Craft is installed, install RPFM’s KDE / Qt dependencies:

craft -i libs/qt6/qtimageformats
craft -i kimageformats
craft -i kwidgetsaddons
craft -i ktexteditor
craft -i kiconthemes
craft -i breeze-icons

KDE designer plugins

By default Craft builds KDE Frameworks with -DBUILD_DESIGNERPLUGIN=OFF. RPFM uses .ui templates with KDE widgets (KLineEdit, KComboBox, KMessageWidget, …) that are loaded at runtime via QUiLoader. Without the designer plugins, QUiLoader cannot instantiate these widget types and RPFM crashes on startup.

Edit the Craft blueprint at CraftRoot/etc/blueprints/locations/craft-blueprints-kde/kde/frameworks/frameworks.py and change BUILD_DESIGNERPLUGIN=OFF to BUILD_DESIGNERPLUGIN=ON:

self.subinfo.options.configure.args += [
    "-DBUILD_DESIGNERPLUGIN=ON",
    "-DBUILD_PYTHON_BINDINGS=OFF",
]

Then rebuild the relevant frameworks:

craft --fetch --unpack --configure --compile --install --qmerge kcompletion
craft --fetch --unpack --configure --compile --install --qmerge kwidgetsaddons

You should see kcompletion6widgets.dll and kwidgetsaddons6widgets.dll in CraftRoot/plugins/designer/.

Building

Open Craft’s terminal, move to RPFM’s source folder and run:

# Debug build:
cargo build

# Run rpfm_ui in debug mode (auto-builds rpfm_server first):
cargo run --bin rpfm_ui

# Release build:
cargo build --release

You can make any editor inherit Craft’s environment (and so be able to compile RPFM) by launching it from Craft’s terminal.

Linux

You’ll need:

  • CMake.
  • Rust 1.81 (or newer).
  • Qt6.
  • KDE Frameworks 6: KCompletion, KIconThemes, KTextEditor, KXmlGui, KWidgetsAddons.
  • xz, p7zip.

Then from the repo root:

# Debug build:
cargo build

# Run rpfm_ui in debug mode:
cargo run --bin rpfm_ui

# Release build:
cargo build --release

macOS

There’s no maintained macOS build. The qt_* Qt6 bindings RPFM uses build on macOS in principle, but nobody is currently producing or testing macOS builds. Contributions welcome.

Feature flags

Some features are gated behind cargo feature flags. Pass them with --features:

cargo run --bin rpfm_ui --features "enable_tools,support_uic"

Available flags (the up-to-date list lives in each crate’s Cargo.toml):

FlagDefaultWhat it enables
enable_toolsyesCompiles the integrated Tools menu (Translator, Faction Painter, Unit Editor).
support_model_rendererno3D RigidModel preview. Requires the renderer library on the link path.
support_uicnoPropagates UIC parsing support through rpfm_lib.
strict_subclasses_compilationnoForces a compilation failure if qt_rpfm_subclasses fails to build.
only_for_the_bravenoExposes experimental features in the About dialog.

Building the manual

To build this manual locally:

# Install mdbook + the langtabs preprocessor RPFM uses:
cargo install --locked mdbook mdbook-langtabs

# Build the docs and open them in a browser:
mdbook build --open

To build the full site (landing page + manual + cargo doc API reference), use the orchestration script in the repo root:

./build_site.sh                # full build
./build_site.sh --skip-api     # manual + landing only
./build_site.sh --skip-manual  # landing + API only

# Preview locally:
python3 -m http.server -d out 8000

Troubleshooting

  • Missing dll errors on Windows — You’re either not launching everything from Craft’s terminal, or you’re missing dependencies.

For anything else, the issue tracker is the right place.