What 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_VP8format. - 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
- Installation — get RPFM on your machine.
- First-time configuration — point RPFM at your games so the smart features can do their job.
- The main window — orient yourself in the UI.
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
- Download the latest
rpfm-vX.Y.Z-x86_64-pc-windows-msvc.zipfrom the releases page. - 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.
- 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.

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
/dataand/contentdirectories. 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.

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:
- Install the Assembly Kit for the game from Steam (look for “Assembly Kit” or “Mod Tools” in the game’s tools list).
- In Preferences → Paths, set the Assembly Kit path for the game.
- 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
- Menu bar. Top of the window. Houses every global action — see below.
- Tab bar. Sits under the menu bar. One tab per open file.
- 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.
- 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.
- Editor area. The big central region. Whatever tab is active here drives most of what you can do.
- 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.
- 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
| Menu | What it covers |
|---|---|
| Pack | Pack 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. |
| MyMod | The 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?. |
| View | Toggle the bottom and side docks (Diagnostics, Global Search, References, Quick Notes, Dependencies). |
| Game Selected | Switch the active Total War game, launch it, open its data / Assembly Kit folders, open RPFM’s config folder, and regenerate the dependencies cache. |
| Tools | Integrated tools that span multiple files: Translator, Faction Painter, Unit Editor. |
| About | Manual, repo, Patreon, Discord, version info, check for updates. |
| Debug | Hidden 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:
| Action | When 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 Packs | Open 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
| Action | Effect |
|---|---|
| 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 All | Save 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.
Navigating
- 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) orCtrl(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:
- 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.
- 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:
- 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).
- In your new Pack, open the Dependency Manager and declare any parent mods you depend on.
- 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
.packfile 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 type | Editor chapter | UI today |
|---|---|---|
| DB table | DB tables | Full grid editor |
| Loc | Loc files | Full grid editor |
| Text & scripts | Text & scripts | KTextEditor (full) |
| Image (DDS, PNG…) | Images & DDS | Viewer only (replace via tree) |
| Video (CA_VP8) | Video | Metadata + IVF round-trip |
| AnimPack | AnimPack | Inner tree + per-file editors |
| AnimsTable | Animations | JSON debug view |
| AnimFragmentBattle | Animations | Structured form view |
| MatchedCombat | Animations | JSON debug view |
| Portrait Settings | Portrait Settings | Full structured editor |
Audio (.wav, .ogg…) | Audio | Play/stop player |
| RigidModel | RigidModel | Metadata + glTF export (read-only) |
| UnitVariant | Specialised editors | Full structured editor |
| Atlas | Specialised editors | Full grid editor |
| ESF (saves, startpos) | Specialised editors | Tree editor (debug-gated) |
| BMD | Specialised editors | JSON text editor |
| UIC | Specialised editors | Read-only text dump |
| Group formations | Specialised editors | JSON 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.

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+Fopens 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+Tabmove between columns;Entercommits.- 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:
| Type | Editor |
|---|---|
| Boolean | Checkbox in the cell. |
| Integers/floats | Numeric editor with format validation; rejects bad input. |
| String | Inline text editor. |
| ColourRGB | Inline RGB editor + colour picker popup. |
| Reference (FK) | Auto-completing combo box pulling values from the referenced table. |
| Sequence | Sub-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.
- Select the cell whose key you want to rename.
- Right-click → Cascade Edition.
- RPFM finds every reference to the old key across the active Pack and its parents, and asks for confirmation.
- 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.
- About → Check Updates to pull the latest schemas (along with the program, lua autogen, and old-AK updates).
- 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.

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:
- Open the mod in RPFM.
- 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.

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.

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:
- Extract it from the Pack (Pack tree → Extract).
- Open it in your image editor of choice (GIMP, Photoshop, Krita…).
- Save it back to disk.
- 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.

What you can do
- Inspect stream metadata: width, height, frame count, framerate, codec version (
v0orv1). - 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:
- Convert the file to IVF (button in the editor toolbar).
- 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 - 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”.

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.

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.

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 likecampaign_character_arts_tables.art_set_idreference. 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_idisn’t declared incampaign_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(orvariant_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
.bnkfrom 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.

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_rendererCargo 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_rendererin 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
.esffalls 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:
| Format | Editor chapter |
|---|---|
| AnimPack | AnimPack |
| AnimsTable | Animations |
| AnimFragmentBattle | Animations |
| MatchedCombat | Animations |
.anim raw stream | No 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.

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.
SequenceU32columns are recursive: au32count 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):
| Category | Sample checks |
|---|---|
| Tables | Outdated 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-level | Invalid Pack name, invalid file names, missing loc data for referenced keys, ITM (identical-to-master) files, files overwriting vanilla unintentionally, duplicate files. |
| Portrait Settings | Art sets / variants not declared in the corresponding tables, missing texture references, datacored portrait_settings files. |
| AnimFragmentBattle | Missing locomotion graphs, missing animation files, missing sound files, missing metadata file. |
| Text | Invalid loc-key references in scripts. |
| Dependencies | Pack declares a parent that doesn’t exist on disk. |
| Config | Missing 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.
Triggering a reference search
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 Source —
PackFile,ParentFiles,GameFiles, orAssKitFiles. - 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).

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
.packfiles 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-projectin the folder. - Create VSCode Project — drops
.vscode/extensions.jsonin the folder, recommending the Lua and Code Runner extensions. - When either is enabled, RPFM also expects a
.luarc.jsonto live there (auto-added to the import-ignore list).
- Create Sublime Text Project — drops a
- Create Git Repository With GitIgnore group (checkable) — when enabled, RPFM
git inits the folder and writes a.gitignore. You can either type your own.gitignorecontents 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:
- Creates
<MyMod base>/<game key>/<name>/on disk. - Initialises
.git,.gitignore,.vscode/,<name>.sublime-projectas opted in. - Creates an empty Pack and saves it as
<name>.packinside the folder. - Stores the import-ignore list in the Pack’s settings.
- Switches the active Game Selected to match the MyMod’s game.
- 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
- 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.
- Translate row by row, or using one of the translation integrations.
- Generate the translated loc. When you save, the Translator writes a translated
.locfile into the Pack at the right path:text/!!!!!!translated_locs.locfor Warhammer 1 and newer (except Thrones of Britannia), ortext/localisation.locfor Thrones of Britannia and older games. The translation works in-game immediately. - 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:
- Find the JSON in
<config>/translations_local/<game>/<pack>/. - Submit it to the Translation Hub as an issue or PR.
- Once accepted, any Runcher user with Enable Translations turned on will get the translation automatically applied at launch — without altering the mod, and with
outdatedlines 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
- Open the Pack you want to add faction colour overrides to.
- Open Faction Painter.
- Pick the faction in the list.
- Adjust the colours (or hit Restore Vanilla Values to baseline first).
- 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_tablesrow. - The
land_units_tablesrow. - The unit’s variant mesh references (
unit_variants_tablesetc., 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:
- Open the Pack.
- Open the Loc files.
- Manually translate the loc values.
- 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:
- Open the Pack.
- Export every Loc to TSV.
- Use a translation tool to work on the TSV.
- Import the locs back.
- 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:
- Open the Pack you want to translate.
- Tools → Translator.
- Translate the lines marked Needs Retranslation.
- Hit Accept.
- 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
| Column | Meaning |
|---|---|
| Key | Loc key |
| Needs Retranslation? | Checked when the line needs work — either it’s new, or the source text has changed since the previous translation. |
| Removed | The line is no longer in the mod. The translator hides these by default. |
| Original Value | The source text (usually English). |
| Translated Value | Your 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
- OpenAI:
- 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(ortext/localisation.locfor 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
.xmlfiles inmap/folders, remove unused.xmlfiles in the prefab folder, Remove.agffiles, Remove.model_statisticsfiles (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:
@tablecontains new rows and rows intended to overwrite vanilla. To change a vanilla unit’s stats, you put the modified row here.ztablecontains 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:
| Column | Description |
|---|---|
table_name | The table to delete a row from. |
key | The 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:
- Right-click your Pack root → Optimize PackFile (or Save Pack For Release if you also want the optimiser to save the result).
- In the optimiser dialog, tick Import datacores into
twad_key_deletesunder the Table section. - 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:
- Right-click your Pack → Create ▸ New DB, type
twad_key_deletes_tablesas the table name, and create it once. Don’t edit it manually. - Open the source table (the one with the rows you want gone).
- Select the rows.
- Right-click → Add Selection to Key Deletes → pick your
twad_key_deletestable.
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_unitsrow that’s still referenced byunits_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 validatetwad_key_deletesreferences. 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:45127by 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_libandrpfm_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:
| Endpoint | Method | Purpose |
|---|---|---|
/ws | GET | WebSocket upgrade. Carries the JSON command/response protocol. |
/sessions | GET | REST: 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
Commandmessages and matchesResponsemessages 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 — what happens on connect, disconnect, reconnect.
- WebSocket protocol — message envelope, serialization conventions, in-flight requests.
- MCP interface — what tools the MCP endpoint exposes.
- Client example — a concrete TypeScript / C# client implementation.
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
-
Connect. A client opens
/ws(or connects to/mcp) without a session ID. The server allocates a newSessionId, spins up a background thread, and immediately sends an unsolicitedSessionConnectedresponse so the client can stash the ID for later reconnection.{ "id": 0, "data": { "SessionConnected": 12345 } } -
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). -
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. -
Graceful disconnect. Send
ClientDisconnectingbefore closing your socket and the session is removed immediately, telemetry is flushed, and the background thread exits.{ "id": 99, "data": "ClientDisconnecting" } -
Empty manager → process exit. When the last session goes away the
rpfm_serverprocess 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:
| Field | Description |
|---|---|
session_id | Unique identifier. |
connection_count | Number of active WebSocket connections to this session. |
timeout_remaining_secs | Seconds until the session is cleaned up. Only set while disconnected. |
is_shutting_down | true when the session has been marked for shutdown. |
pack_names | Names 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>
}
| Field | Type | Description |
|---|---|---|
id | number | Unique request ID. The server echoes it in the response. Use 0 only for unsolicited server-initiated messages. |
data | object or string | The 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 shape | JSON serialization | Example |
|---|---|---|
| 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.
| Value | Description |
|---|---|
"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.
| Field | Type | Description |
|---|---|---|
path | string | Internal path within the Pack |
container_name | string or null | Name of the containing Pack (null if unknown) |
timestamp | number or null | Last modification timestamp |
file_type | string | File 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).
| Field | Type | Description |
|---|---|---|
file_name | string | Name of the Pack file |
file_path | string | Full path to the Pack file on disk |
pfh_version | PFHVersion | PFH format version |
pfh_file_type | PFHFileType | Pack file type |
bitmask | PFHFlags | PFH flags bitmask (u32) |
compress | CompressionFormat | Compression format |
timestamp | number | Pack 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.
| Field | Type | Description |
|---|---|---|
asskit_tables | RFileInfo[] | Assembly Kit table files |
vanilla_packed_files | RFileInfo[] | Vanilla game files |
parent_packed_files | RFileInfo[] | 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.
| Field | Type | Description |
|---|---|---|
session_id | number | Unique session identifier |
connection_count | number | Number of active WebSocket connections |
timeout_remaining_secs | number or null | Seconds until timeout (null if connections exist) |
is_shutting_down | boolean | Whether the session is shutting down |
pack_names | string[] | 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:
| Variant | Payload | Description |
|---|---|---|
AnimPack | string | File name |
DB | [string, string, number] | [file_name, table_name, version] |
Loc | string | Table name |
PortraitSettings | [string, number, [string, string][]] | [name, version, clone_entries] |
Text | [string, string] | [file_name, text_format] |
VMD | string | File name |
WSModel | string | File 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.
| Field | Type | Description |
|---|---|---|
format | string | Video format enum value |
version | number | Video format version |
codec_four_cc | string | Codec FourCC identifier |
width | number | Video width in pixels |
height | number | Video height in pixels |
num_frames | number | Total number of frames |
framerate | number | Frames 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:
| Value | Description |
|---|---|
"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:
| Value | Description |
|---|---|
"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:
| Value | Description |
|---|---|
"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:
| Value | Description |
|---|---|
"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:
| Bit | Flag | Meaning |
|---|---|---|
0x0000_0100 | HAS_EXTENDED_HEADER | Pack has an extended header |
0x0000_0080 | HAS_ENCRYPTED_INDEX | The file index is encrypted |
0x0000_0040 | HAS_INDEX_WITH_TIMESTAMPS | Index entries include timestamps |
0x0000_0010 | HAS_ENCRYPTED_DATA | File 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:
| Value | Description |
|---|---|
"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:
| Variant | Payload | Description |
|---|---|---|
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:
| Variant | Payload | Description |
|---|---|---|
Boolean | boolean | Boolean value |
F32 | number | 32-bit float |
F64 | number | 64-bit float |
I16 | number | 16-bit integer |
I32 | number | 32-bit integer |
I64 | number | 64-bit integer |
ColourRGB | string | RGB colour string |
StringU8 | string | UTF-8 string |
StringU16 | string | UTF-16 string |
OptionalI16 | number | Optional 16-bit integer |
OptionalI32 | number | Optional 32-bit integer |
OptionalI64 | number | Optional 64-bit integer |
OptionalStringU8 | string | Optional UTF-8 string |
OptionalStringU16 | string | Optional UTF-16 string |
SequenceU16 | number[] | Raw bytes for a nested sequence (u16-prefixed count) |
SequenceU32 | number[] | 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.
| Field | Type | Description |
|---|---|---|
table_name | string | Table type identifier (e.g. "units_tables") |
definition | Definition | Schema definition for this table |
definition_patch | DefinitionPatch | Runtime schema modifications |
table_data | DecodedData[][] | Row data (outer = rows, inner = columns) |
altered | boolean | Whether 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.
| Field | Type | Description |
|---|---|---|
path | string | Path of the file within a container |
timestamp | number or null | Last modified timestamp (Unix epoch) |
file_type | FileType | Detected or specified file type |
container_name | string or null | Name of the source container |
data | unknown | Internal 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.
| Field | Type | Description |
|---|---|---|
field_name | string | Name of the column these references are for |
referenced_table_is_ak_only | boolean | Whether the referenced table only exists in the AK |
referenced_column_is_localised | boolean | Whether the referenced column is localised |
data | Record<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.
| Field | Type | Description |
|---|---|---|
mysterious_byte | boolean | Boolean flag (setting to 0 can crash WH2) |
guid | string | GUID for this table instance (empty for older games) |
table | TableInMemory | The 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.
| Field | Type | Description |
|---|---|---|
table | TableInMemory | Table data with key, text, and tooltip columns |
interface Loc {
table: TableInMemory;
}
public class Loc
{
public TableInMemory Table { get; set; }
}
Text
Decoded text file.
| Field | Type | Description |
|---|---|---|
encoding | TextEncoding | Character encoding of the file |
format | TextFormat | Detected file format |
contents | string | Decoded 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:
| Value | Description |
|---|---|
"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(inrpfm_lib::files::text). This doc uses the more descriptive nameTextEncodingfor 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:
| Value | Description |
|---|---|
"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.
| Field | Type | Description |
|---|---|---|
data | number[] | Original raw image data in native format |
converted_data | number[] or null | PNG-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.
| Field | Type | Description |
|---|---|---|
version | number | File format version (6, 7, or 8) |
uk_1 | number | Unknown field |
skeleton_id | string | Skeleton identifier for animation (empty if static) |
lods | unknown[] | 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.
| Field | Type | Description |
|---|---|---|
signature | string | Format signature (CAAB, CBAB, etc.) |
unknown_1 | number | Unknown header field, typically 0 |
creation_date | number | Creation timestamp |
root_node | unknown | Root 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.
| Field | Type | Description |
|---|---|---|
serialise_version | number | File format version (23-27) |
| (other fields) | unknown | Complex 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.
| Field | Type | Description |
|---|---|---|
version | number | File format version (2 or 4) |
entries | unknown[] | List of animation entries |
skeleton_name | string | Name of the skeleton |
subversion | number | Format 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.
| Field | Type | Description |
|---|---|---|
version | number | File format version (currently 2) |
entries | unknown[] | 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.
| Field | Type | Description |
|---|---|---|
version | number | File format version (currently 1) |
unknown | number | Unknown field |
entries | unknown[] | 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.
| Field | Type | Description |
|---|---|---|
data | number[] | Raw binary audio data |
interface Audio {
data: number[];
}
public class Audio
{
public byte[] Data { get; set; }
}
GroupFormations
Decoded GroupFormations file.
| Field | Type | Description |
|---|---|---|
formations | unknown[] | List of formation definitions |
interface GroupFormations {
formations: unknown[];
}
public class GroupFormations
{
public List<object> Formations { get; set; }
}
MatchedCombat
Decoded MatchedCombat file.
| Field | Type | Description |
|---|---|---|
version | number | File format version (1 or 3) |
entries | unknown[] | 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.
| Field | Type | Description |
|---|---|---|
version | number | Format version (1 or 4) |
entries | unknown[] | 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.
| Field | Type | Description |
|---|---|---|
version | number | Format version number |
source_is_xml | boolean | Whether decoded from XML (true) or binary (false) |
comment | string | Optional comment/description |
precache_condition | string | Condition for precaching |
hierarchy | Record<string, unknown> | Tree structure of UI element relationships |
components | Record<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.
| Field | Type | Description |
|---|---|---|
version | number | Version of the UnitVariant |
unknown_1 | number | Unknown field |
categories | unknown[] | 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:
| Value | Description |
|---|---|
"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.
| Field | Type | Description |
|---|---|---|
name | string | Field name |
field_type | FieldType | Data type |
is_key | boolean | Part of the table’s primary key |
default_value | string or null | Default value for new rows |
is_filename | boolean | Whether this field contains a filename/path |
filename_relative_path | string or null | Semicolon-separated relative paths for file lookup |
is_reference | [string, string] or null | Foreign key: [table_name, column_name] |
lookup | string[] or null | Additional columns to show from the referenced table |
description | string | Human-readable description |
ca_order | number | Position in CA’s Assembly Kit editor (-1 = unknown) |
is_bitwise | number | Number of boolean columns to split this field into |
enum_values | Record<number, string> | Named enum values (integer key to string name) |
is_part_of_colour | number or null | RGB 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.
| Field | Type | Description |
|---|---|---|
version | number | Version number (-1 = fake, 0 = unversioned, 1+ = versioned) |
fields | Field[] | Fields in binary encoding order (see note below) |
localised_fields | Field[] | Fields extracted to LOC files during export |
localised_key_order | number[] | Order of key fields for constructing localisation keys |
Note: The
fieldslist 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 theFieldsProcessedcommand, passing theDefinitionas 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.
| Field | Type | Description |
|---|---|---|
version | number | Schema format version (currently 5) |
definitions | Record<string, Definition[]> | Table name to version definitions |
patches | Record<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.
| Field | Type | Description |
|---|---|---|
settings_text | Record<string, string> | Multi-line text settings (e.g., ignore lists) |
settings_string | Record<string, string> | Single-line string settings |
settings_bool | Record<string, boolean> | Boolean flags |
settings_number | Record<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.
| Field | Type | Description |
|---|---|---|
id | number | Unique note identifier |
message | string | Note content/body |
url | string or null | Optional URL associated with the note |
path | string | Path 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.
| Field | Type | Description |
|---|---|---|
pack_remove_itm_files | boolean | Remove files unchanged from vanilla |
db_import_datacores_into_twad_key_deletes | boolean | Import datacored tables into twad_key_deletes |
db_optimize_datacored_tables | boolean | Optimize datacored tables (not recommended) |
table_remove_duplicated_entries | boolean | Remove duplicated rows from DB and Loc files |
table_remove_itm_entries | boolean | Remove Identical To Master rows |
table_remove_itnr_entries | boolean | Remove Identical To New Row rows |
table_remove_empty_file | boolean | Remove empty DB and Loc files |
text_remove_unused_xml_map_folders | boolean | Remove unused XML files in map folders |
text_remove_unused_xml_prefab_folder | boolean | Remove unused XML files in the prefab folder |
text_remove_agf_files | boolean | Remove unused AGF files |
text_remove_model_statistics_files | boolean | Remove unused model_statistics files |
pts_remove_unused_art_sets | boolean | Remove unused art sets in Portrait Settings |
pts_remove_unused_variants | boolean | Remove unused variants from Portrait Settings art sets |
pts_remove_empty_masks | boolean | Remove empty masks in Portrait Settings |
pts_remove_empty_file | boolean | Remove 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.
| Field | Type | Description |
|---|---|---|
bool | Record<string, boolean> | Boolean settings |
i32 | Record<string, number> | Signed 32-bit integer settings |
f32 | Record<string, number> | 32-bit float settings |
string | Record<string, string> | String settings |
raw_data | Record<string, number[]> | Raw byte-array settings |
vec_string | Record<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.
| Field | Type | Description |
|---|---|---|
key | string | The Loc key identifying this string |
value_original | string | Original text in the base language |
value_translated | string | Translated text in the target language |
needs_retranslation | boolean | Whether the source text has changed since translation |
removed | boolean | Whether 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.
| Field | Type | Description |
|---|---|---|
language | string | Target language code (e.g. "es", "de") |
pack_name | string | Name of the pack |
translations | Record<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.
| Field | Type | Description |
|---|---|---|
folders_ignored | string[] | Folder paths excluded from checks |
files_ignored | string[] | File paths excluded from checks |
fields_ignored | string[] | Table fields excluded ("table_name/field_name") |
diagnostics_ignored | string[] | Diagnostic type identifiers to skip |
results | DiagnosticType[] | 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:
| Variant | Payload | Description |
|---|---|---|
AnimFragmentBattle | AnimFragmentBattleDiagnostic | Issues in an AnimFragmentBattle file |
Config | ConfigDiagnostic | Pack-level configuration issue |
Dependency | DependencyDiagnostic | Broken/missing dependency reference |
DB | TableDiagnostic | Issue in a DB table |
Loc | TableDiagnostic | Issue in a Loc file |
Pack | PackDiagnostic | Pack-wide issue |
PortraitSettings | PortraitSettingsDiagnostic | Issue in a PortraitSettings file |
Text | TextDiagnostic | Issue 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:
| Variant | Payload | Description |
|---|---|---|
NewBetaUpdate | string | New beta version available |
NewStableUpdate | string | New stable version available |
NewUpdateHotfix | string | New 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.):
| Value | Description |
|---|---|
"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:
| Variant | Payload | Description |
|---|---|---|
Pack | string | A 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.
| Field | Type | Description |
|---|---|---|
pattern | string | Text pattern or regex to search for |
replace_text | string | Replacement text |
case_sensitive | boolean | Whether the search is case-sensitive |
use_regex | boolean | Whether the pattern is a regular expression |
sources | SearchSource[] | One or more data sources to search |
search_on | SearchOn | Which file types to search |
matches | Matches | Results from the most recent search |
game_key | string | Game 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.
| Field | Type | Description |
|---|---|---|
anim | UnknownMatches[] | Matches in animation files |
anim_fragment_battle | AnimFragmentBattleMatches[] | Matches in battle animation fragments |
anim_pack | UnknownMatches[] | Matches in animation packs |
anims_table | UnknownMatches[] | Matches in animation tables |
atlas | AtlasMatches[] | Matches in atlases |
audio | UnknownMatches[] | Matches in audio files |
bmd | UnknownMatches[] | Matches in BMD files |
db | TableMatches[] | Matches in DB tables |
esf | UnknownMatches[] | Matches in ESF files |
group_formations | UnknownMatches[] | Matches in group formations |
image | UnknownMatches[] | Matches in images |
loc | TableMatches[] | Matches in Loc tables |
matched_combat | UnknownMatches[] | Matches in matched combat files |
pack | UnknownMatches[] | Matches in nested Pack files |
portrait_settings | PortraitSettingsMatches[] | Matches in portrait settings |
rigid_model | RigidModelMatches[] | Matches in RigidModel files |
sound_bank | UnknownMatches[] | Matches in sound banks |
text | TextMatches[] | Matches in text files (Lua, XML, …) |
uic | UnknownMatches[] | Matches in UIC files |
unit_variant | UnitVariantMatches[] | Matches in UnitVariant files |
unknown | UnknownMatches[] | Matches in unclassified files |
video | UnknownMatches[] | Matches in video files |
schema | SchemaMatches | Matches 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:
| Field | Type | Description |
|---|---|---|
path | string | Internal path of the file the matches came from |
source | SearchSource | Data source the file was searched in |
container_name | string | Name of the containing Pack (or empty) |
matches | <Match>[] | Match entries — type depends on file kind |
TableMatch (used for DB and Loc files):
| Field | Type | Description |
|---|---|---|
column_name | string | Column where the match is |
column_number | number | Logical column index (-1 if hidden) |
row_number | number | Row number (-1 if hidden by filter) |
start | number | Byte offset where match starts |
end | number | Byte offset where match ends |
text | string | Contents of the matched cell |
TextMatch (used for text files):
| Field | Type | Description |
|---|---|---|
row | number | Row of the first character |
start | number | Byte offset where match starts |
end | number | Byte offset where match ends |
text | string | Line containing the match |
UnknownMatch (used for binary/unknown files):
| Field | Type | Description |
|---|---|---|
pos | number | Byte position of the match |
len | number | Length of the matched pattern in bytes |
AnimFragmentBattleMatch (used for AnimFragmentBattle files):
| Field | Type | Description |
|---|---|---|
skeleton_name | boolean | Match is in the skeleton name |
table_name | boolean | Match is in the table name |
mount_table_name | boolean | Match is in the mount table name |
unmount_table_name | boolean | Match is in the unmount table name |
locomotion_graph | boolean | Match is in the locomotion graph |
entry | [number, [number, boolean, boolean, boolean] or null, boolean, boolean, boolean, boolean, boolean] or null | Entry match details |
start | number | Byte offset where match starts |
end | number | Byte offset where match ends |
text | string | The matched text |
AtlasMatch (used for Atlas files — same structure as TableMatch):
| Field | Type | Description |
|---|---|---|
column_name | string | Column where the match is |
column_number | number | Logical column index |
row_number | number | Row number of the match |
start | number | Byte offset where match starts |
end | number | Byte offset where match ends |
text | string | Contents of the matched cell |
PortraitSettingsMatch (used for PortraitSettings files):
| Field | Type | Description |
|---|---|---|
entry | number | Index of the entry |
id | boolean | Match is in the id field |
camera_settings_head | boolean | Match is in head camera skeleton node |
camera_settings_body | boolean | Match is in body camera skeleton node |
variant | [number, boolean, boolean, boolean, boolean, boolean] or null | Variant match details |
start | number | Byte offset where match starts |
end | number | Byte offset where match ends |
text | string | The matched text |
RigidModelMatch (used for RigidModel files):
| Field | Type | Description |
|---|---|---|
skeleton_id | boolean | Match is in the skeleton id |
mesh_value | [number, number] or null | LOD and mesh index, or null |
mesh_name | boolean | Match is in the mesh name |
mesh_mat_name | boolean | Match is in the material name |
mesh_textute_directory | boolean | Match is in the texture directory |
mesh_filters | boolean | Match is in the mesh filters |
mesh_att_point_name | number or null | Attachment point index with match |
mesh_texture_path | number or null | Texture path index with match |
start | number | Byte offset where match starts |
end | number | Byte offset where match ends |
text | string | The matched text |
UnitVariantMatch (used for UnitVariant files):
| Field | Type | Description |
|---|---|---|
entry | number | Index of the entry |
name | boolean | Match is in the name |
variant | [number, boolean, boolean] or null | Variant match details |
start | number | Byte offset where match starts |
end | number | Byte offset where match ends |
text | string | The matched text |
SchemaMatch (used for schema searches):
| Field | Type | Description |
|---|---|---|
table_name | string | The table name |
version | number | Version of the matched definition |
column | number | Column index of the match |
column_name | string | Full 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.
| Parameter | Type | Description |
|---|---|---|
paths | string[] | 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to save |
path | string | Destination 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to clean |
path | string | Destination 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to query |
paths | string[] | 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to query |
path | string | Internal 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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).
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to modify |
type | PFHFileType | New 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to modify |
enabled | boolean | New 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to modify |
format | CompressionFormat | New 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to optimize |
options | OptimizerOptions | Optimization 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
game_key | string | Game identifier (e.g., "warhammer_3") |
rebuild | boolean | Whether 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
path | string | Internal path for the file |
spec | NewFile | File 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
source_paths | string[] | Filesystem paths to add |
dest_paths | ContainerPath[] | Destination paths inside the pack |
ignore_paths | string[] or null | Paths 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack containing the file |
path | string | Internal path |
source | DataSource | Data 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
path | string | Internal path |
data | RFileDecoded | Decoded 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to modify |
paths | ContainerPath[] | 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.
| Parameter | Type | Description |
|---|---|---|
sources | Record<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.
| Parameter | Type | Description |
|---|---|---|
sources | Record<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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
dest_path | string | Destination 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to modify |
paths | ContainerPath[] | 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to extract from |
paths_by_source | Record<DataSource, ContainerPath[]> | Files grouped by data source |
dest_path | string | Filesystem destination |
as_tsv | boolean | Export 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to check |
path | string | Folder 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to check |
path | string | File 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to query |
path | string | Internal 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.
| Parameter | Type | Description |
|---|---|---|
target_key | string | Destination pack key |
source_key | string | Source pack key |
paths | ContainerPath[] | 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack containing animpack |
animpack_path | string | Path to the AnimPack |
paths | ContainerPath[] | 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
source | DataSource | Data source |
animpack_path | string | Path to the AnimPack |
paths | ContainerPath[] | 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack containing animpack |
animpack_path | string | Path to the AnimPack |
paths | ContainerPath[] | 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
sources | Record<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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
files | RFile[] | Files to save |
optimize | boolean | Run 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.
| Parameter | Type | Description |
|---|---|---|
path | ContainerPath | Path 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.
| Parameter | Type | Description |
|---|---|---|
rebuild_all | boolean | true = 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.
| Parameter | Type | Description |
|---|---|---|
require_asskit | boolean | Check 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
table_name | string | Table 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.
| Parameter | Type | Description |
|---|---|---|
table_name | string | Table 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack containing the files |
paths | ContainerPath[] | Files to merge |
merged_path | string | Destination path for result |
delete_sources | boolean | Delete 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack containing table |
path | ContainerPath | Table 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
paths | ContainerPath[] | Paths to retrieve |
lowercase | boolean | Normalize 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to search |
config | GlobalSearch | Search 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to modify |
config | GlobalSearch | Search config |
matches | MatchHolder[] | 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to modify |
config | GlobalSearch | Search 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to query |
table_name | string | Name of the table |
definition | Definition | Table definition |
force_local | boolean | Force 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to search |
table_columns | Record<string, string[]> | Map of table name to column names |
search_value | string | Value 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; }
}
Navigation Commands
GoToDefinition
Go to the definition of a table reference.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to search |
table_name | string | Table name |
column_name | string | Column name |
values | string[] | 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to search |
loc_key | string | Loc 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to search |
loc_key | string | Loc 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to modify |
table | string | Table name |
definition | Definition | Table 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack containing file |
path | string | Internal file path |
format | SupportedFormats | Target 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.
| Parameter | Type | Description |
|---|---|---|
schema | Schema | Complete 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to clean |
paths | ContainerPath[] | 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.
| Parameter | Type | Description |
|---|---|---|
table_name | string | Table 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.
| Parameter | Type | Description |
|---|---|---|
table_name | string | Table name |
version | number | Version 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.
| Parameter | Type | Description |
|---|---|---|
table_name | string | Table name |
version | number | Version 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.
| Parameter | Type | Description |
|---|---|---|
table_name | string | Referenced table |
definition | Definition | Table 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).
| Parameter | Type | Description |
|---|---|---|
definition | Definition | Definition 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack containing table |
path | string | Internal table path |
dest | string | Filesystem output path |
source | DataSource | Data 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
path | string | Internal destination path |
tsv_path | string | Filesystem 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack containing file |
source | DataSource | Data source |
path | ContainerPath | File 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
path | string | Internal path |
ext_path | string | External 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.
| Parameter | Type | Description |
|---|---|---|
diagnostics_ignored | string[] | Diagnostic type identifiers to skip during the check |
check_ak | boolean | Also 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.
| Parameter | Type | Description |
|---|---|---|
diagnostics | Diagnostics | Existing diagnostics state |
paths | ContainerPath[] | Paths to re-check |
check_ak | boolean | Check 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to modify |
line | string | Diagnostic 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to modify |
settings | PackSettings | New 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to query |
path | string | Path 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
note | Note | Note 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to modify |
path | string | Note path |
note_id | number | Note 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.
| Parameter | Type | Description |
|---|---|---|
patches | Record<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.
| Parameter | Type | Description |
|---|---|---|
table_name | string | Table 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.
| Parameter | Type | Description |
|---|---|---|
table_name | string | Table name |
field_name | string | Field 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.
| Parameter | Type | Description |
|---|---|---|
patches | Record<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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
mod_name | string | Name of the mod |
game_key | string | Target game |
sublime | boolean | Create Sublime Text project |
vscode | boolean | Create VS Code project |
gitignore | string or null | Gitignore 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to modify |
mode | OperationalMode | New 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to query |
language | string | Language 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
campaign_id | string | Campaign identifier |
process_hlp_spd | boolean | Process 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
campaign_id | string | Campaign identifier |
process_hlp_spd | boolean | Process 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
campaign_id | string | Campaign identifier |
process_hlp_spd | boolean | Process 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to modify |
starting_id | number | Starting ID |
offset | number | ID 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.
| Parameter | Type | Description |
|---|---|---|
skeleton_name | string | Skeleton 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.
| Parameter | Type | Description |
|---|---|---|
table_name | string | Table 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack to query |
table_name | string | Table 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
table_file_name | string | Table file name |
key_table_name | string | Key table name |
keys | string[] | 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Target pack |
tile_maps | string[] | 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.
| Parameter | Type | Description |
|---|---|---|
model | RigidModel | Model to export |
output_path | string | Output 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:
| Command | Response 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":
| Command | Value Type |
|---|---|
SettingsSetBool | boolean |
SettingsSetI32 | number |
SettingsSetF32 | number |
SettingsSetString | string |
SettingsSetPathBuf | string |
SettingsSetVecString | string[] |
SettingsSetVecRaw | number[] |
{ "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.
| Parameter | Type | Description |
|---|---|---|
path | string | Path 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 }:
| Command | Description |
|---|---|
ConfigPath | Config directory path |
AssemblyKitPath | Assembly Kit path for current game |
BackupAutosavePath | Backup autosave directory |
OldAkDataPath | Old AK data directory |
SchemasPath | Schemas directory |
TableProfilesPath | Table profiles directory |
TranslationsLocalPath | Translations local directory |
DependenciesCachePath | Dependencies 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).
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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.
| Parameter | Type | Description |
|---|---|---|
pack_key | string | Pack 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
| Response | Payload | Description |
|---|---|---|
Success | (none) | Operation completed successfully |
Error | string | Human-readable error message |
SessionConnected | number | Session 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]:
| Response | Payload 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
| Response | Payload Type | Description |
|---|---|---|
Bool | boolean | Boolean value |
F32 | number | 32-bit float |
I32 | number | 32-bit integer |
I32I32 | [number, number] | Pair of integers |
String | string | String value |
PathBuf | string | Filesystem path |
Collection Responses
| Response | Payload Type | Description |
|---|---|---|
VecBoolString | [boolean, string][] | Boolean-string pairs |
VecContainerPath | ContainerPath[] | 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. |
VecDefinition | Definition[] | List of definitions |
VecField | Field[] | List of fields |
VecNote | Note[] | List of notes |
VecRFile | RFile[] | List of raw files |
VecRFileInfo | RFileInfo[] | List of file metadata |
VecString | string[] | List of strings |
VecStringContainerInfo | [string, ContainerInfo][] | Pack key + metadata pairs |
VecU8 | number[] | Raw byte data |
HashSetString | string[] | Set of strings |
HashSetStringHashSetString | [string[], string[]] | Two sets of strings |
Compound Responses
| Response | Payload Type | Description |
|---|---|---|
APIResponse | APIResponse | Program update check result |
APIResponseGit | GitResponse | Git update check result |
CompressionFormat | CompressionFormat | Pack compression format |
CompressionFormatDependenciesInfo | [CompressionFormat, DependenciesInfo or null] | Format + optional dependencies info |
ContainerInfo | ContainerInfo | Pack metadata |
ContainerInfoVecRFileInfo | [ContainerInfo, RFileInfo[]] | Pack metadata + file list |
StringContainerInfo | [string, ContainerInfo] | Pack key + metadata |
DataSourceStringUsizeUsize | [DataSource, string, number, number] | Navigation result |
Definition | Definition | Table definition |
DependenciesInfo | DependenciesInfo | Dependencies information |
Diagnostics | Diagnostics | Diagnostics report |
GlobalSearchVecRFileInfo | [GlobalSearch, RFileInfo[]] | Search results + modified files |
HashMapDataSourceHashMapStringRFile | Record<DataSource, Record<string, RFile>> | Files by source and path |
HashMapDataSourceHashSetContainerPath | Record<DataSource, ContainerPath[]> | Paths by data source |
HashMapI32TableReferences | Record<number, TableReferences> | Column references by index |
HashMapStringHashMapStringVecString | Record<string, Record<string, string[]>> | Nested string maps |
I32I32VecStringVecString | [number, number, string[], string[]] | Version change result |
Note | Note | Single note |
OperationalMode | OperationalMode | Per-pack operational mode |
OptimizerOptions | OptimizerOptions | Optimizer configuration |
OptionContainerPath | ContainerPath or null | Optional container path |
OptionRFileInfo | RFileInfo or null | Optional file info |
OptionStringStringVecString | [string, string, string[]] or null | Optional loc source data |
PackSettings | PackSettings | Pack settings |
PackTranslation | PackTranslation | Translation data |
RFileDecoded | RFileDecoded | Decoded file content |
Schema | Schema | Full schema |
StringVecContainerPath | [string, ContainerPath[]] | String + path list |
StringVecPathBuf | [string, string[]] | String + filesystem paths |
Text | Text | Text 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
| Aspect | WebSocket (/ws) | MCP (/mcp) |
|---|---|---|
| Protocol | Custom JSON messages with id/data envelope | Standard MCP (JSON-RPC 2.0) |
| Transport | WebSocket | Streamable HTTP |
| Interaction model | Send Command, receive Response | Call named tools, receive JSON results |
| Session control | Manual via ?session_id= and ClientDisconnecting | Managed automatically by the MCP transport |
| Intended clients | Custom scripts, GUIs | AI 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
| Tool | Description | Arguments |
|---|---|---|
call_command | Call any IPC command directly (for commands not yet wrapped as named tools) | command: JSON string of the Command enum |
Pack Lifecycle
| Tool | Description | Key Arguments |
|---|---|---|
new_pack | Create a new empty PackFile | (none) |
open_packfiles | Open one or more PackFiles | paths: file paths |
save_packfile | Save a pack | pack_key |
close_pack | Close a pack without saving | pack_key |
close_all_packs | Close every open pack without saving | (none) |
save_pack_as | Save a pack to a new path | pack_key, path |
clean_and_save_pack_as | Save a clean copy (use if normal save fails) | pack_key, path |
trigger_backup_autosave | Trigger a backup autosave | pack_key |
load_all_ca_pack_files | Open all vanilla CA PackFiles for the selected game | (none) |
list_open_packs | List all open packs with their keys and metadata | (none) |
Pack Metadata
| Tool | Description | Key Arguments |
|---|---|---|
set_pack_file_type | Set the pack type (PFHFileType as JSON) | pack_key, pack_file_type |
change_compression_format | Change compression format | pack_key, format |
change_index_includes_timestamp | Toggle timestamp in pack index | pack_key, value |
get_pack_file_path | Get the file path of a pack | pack_key |
get_pack_file_name | Get the file name of a pack | pack_key |
get_pack_settings | Get pack settings | pack_key |
set_pack_settings | Set pack settings (PackSettings as JSON) | pack_key, settings |
get_dependency_pack_files_list | Get dependency pack list | pack_key |
set_dependency_pack_files_list | Set dependency pack list | pack_key, list |
File Operations
| Tool | Description | Key Arguments |
|---|---|---|
decode_packed_file | Decode a file from a pack | pack_key, path, source |
new_packed_file | Create a new file inside a pack | pack_key, path, new_file |
add_packed_files | Add files from disk to a pack | pack_key, source_paths, destination_paths |
add_packed_files_from_pack_file | Add files from another PackFile | pack_key, source_pack_path, container_paths |
add_packed_files_from_pack_file_to_animpack | Add files to an AnimPack | pack_key, animpack_path, container_paths |
add_packed_files_from_animpack | Add files from an AnimPack | pack_key, source, animpack_path, container_paths |
delete_packed_files | Delete files from a pack | pack_key, paths |
copy_packed_files | Copy paths into the internal clipboard | paths_by_pack |
cut_packed_files | Cut paths into the internal clipboard (removed from source on paste) | paths_by_pack |
paste_packed_files | Paste from the internal clipboard into a pack folder | pack_key, destination_path |
duplicate_packed_files | Duplicate files in-place within the same pack (numeric suffix added) | pack_key, paths |
delete_from_animpack | Delete files from an AnimPack | pack_key, animpack_path, container_paths |
extract_packed_files | Extract files to disk | pack_key, source_paths, destination_path, export_as_tsv |
rename_packed_files | Rename files in a pack | pack_key, renames |
save_packed_file_from_view | Save an edited decoded file back | pack_key, path, data |
save_packed_file_from_external_view | Save a file from an external program | pack_key, internal_path, external_path |
save_packed_files_to_pack_file_and_clean | Save files and optionally optimize | pack_key, files, optimize |
get_packed_file_raw_data | Get raw binary data of a file | pack_key, value |
open_packed_file_in_external_program | Open a file in an external program | pack_key, source, container_path |
open_containing_folder | Open the pack’s folder in file manager | pack_key |
clean_cache | Clean the decode cache | pack_key, paths |
folder_exists | Check if a folder exists in a pack | pack_key, value |
packed_file_exists | Check if a file exists in a pack | pack_key, value |
get_packed_files_info | Get info of one or more files | pack_key, values |
get_rfile_info | Get info of a single file | pack_key, value |
Game Selection
| Tool | Description | Key Arguments |
|---|---|---|
get_game_selected | Get the currently selected game | (none) |
set_game_selected | Set the current game | game_name, rebuild_dependencies |
Dependencies
| Tool | Description | Key Arguments |
|---|---|---|
generate_dependencies_cache | Generate the dependencies cache | (none) |
rebuild_dependencies | Rebuild dependencies | value (true = full) |
is_there_a_dependency_database | Check if a dependency database is loaded | value |
get_table_list_from_dependency_pack_file | Get table names from dependency packs | (none) |
get_custom_table_list | Get custom table names from schema | (none) |
get_table_version_from_dependency_pack_file | Get table version from dependencies | value |
get_table_definition_from_dependency_pack_file | Get table definition from dependencies | value |
get_tables_from_dependencies | Get table data by name | value |
import_dependencies_to_open_pack_file | Import files from dependencies | pack_key, paths |
get_rfiles_from_all_sources | Get files from all sources | paths, lowercase |
get_packed_files_names_starting_with_path_from_all_sources | Get file names under a path | path |
local_art_set_ids | Get local art set IDs | pack_key |
dependencies_art_set_ids | Get art set IDs from dependencies | (none) |
Search
| Tool | Description | Key Arguments |
|---|---|---|
global_search | Run a global search | pack_key, search |
global_search_replace_matches | Replace specific matches | pack_key, search, matches |
global_search_replace_all | Replace all matches | pack_key, search |
search_references | Find all references to a value | pack_key, reference_map, value |
get_reference_data_from_definition | Get reference data for columns | pack_key, table_name, definition, force |
go_to_definition | Go to a reference’s definition | pack_key, table_name, column_name, values |
go_to_loc | Go to a loc key’s location | pack_key, value |
get_source_data_from_loc_key | Get source data of a loc key | pack_key, value |
Schema
| Tool | Description | Key Arguments |
|---|---|---|
save_schema | Save a schema to disk | schema |
update_current_schema_from_asskit | Update schema from Assembly Kit | (none) |
update_schemas | Update schemas from remote repository | (none) |
is_schema_loaded | Check if a schema is loaded | (none) |
get_schema | Get the current schema | (none) |
definitions_by_table_name | Get definitions for a table | value |
definition_by_table_name_and_version | Get a definition by name and version | name, version |
delete_definition | Delete a definition | name, version |
referencing_columns_for_definition | Get columns referencing a table | table_name, definition |
fields_processed | Get processed fields from a definition | definition |
save_local_schema_patch | Save local schema patches | patches |
remove_local_schema_patches_for_table | Remove patches for a table | value |
remove_local_schema_patches_for_table_and_field | Remove patches for a field | key, value |
import_schema_patch | Import a schema patch | patches |
Table Operations
| Tool | Description | Key Arguments |
|---|---|---|
merge_files | Merge compatible tables into one | pack_key, paths, merged_path, delete_source |
update_table | Update a table to a newer version | pack_key, value |
cascade_edition | Cascade edit across referenced data | pack_key, table_name, definition, changes |
get_tables_by_table_name | Get table paths by name | pack_key, value |
add_keys_to_key_deletes | Add keys to key_deletes table | pack_key, table_file_name, key_table_name, keys |
export_tsv | Export a table to TSV | pack_key, tsv_path, table_path |
import_tsv | Import a TSV file to a table | pack_key, tsv_path, table_path |
Diagnostics
| Tool | Description | Key Arguments |
|---|---|---|
diagnostics_check | Run a full diagnostics check | ignored, check_ak_only_refs |
diagnostics_update | Update diagnostics for changed files | diagnostics, paths, check_ak_only_refs |
add_line_to_pack_ignored_diagnostics | Add to ignored diagnostics | pack_key, value |
get_missing_definitions | Export missing table definitions | pack_key |
Notes
| Tool | Description | Key Arguments |
|---|---|---|
notes_for_path | Get notes under a path | pack_key, value |
add_note | Add a note | pack_key, note |
delete_note | Delete a note | pack_key, path, id |
Optimization
| Tool | Description | Key Arguments |
|---|---|---|
optimize_pack_file | Optimize a pack | pack_key, options |
get_optimizer_options | Get default optimizer options | (none) |
Updates
| Tool | Description |
|---|---|
check_updates | Check for RPFM updates |
check_schema_updates | Check for schema updates |
check_lua_autogen_updates | Check for Lua autogen updates |
check_empire_and_napoleon_ak_updates | Check for Empire/Napoleon AK updates |
check_translations_updates | Check for translation updates |
update_lua_autogen | Update Lua autogen |
update_main_program | Update the program |
update_empire_and_napoleon_ak | Update Empire/Napoleon AK files |
update_translations | Update translations |
Settings
| Tool | Description | Key Arguments |
|---|---|---|
settings_get_bool | Get a boolean setting | value (key) |
settings_get_i32 | Get an i32 setting | value (key) |
settings_get_f32 | Get an f32 setting | value (key) |
settings_get_string | Get a string setting | value (key) |
settings_get_path_buf | Get a PathBuf setting | value (key) |
settings_get_vec_string | Get a Vec<String> setting | value (key) |
settings_get_vec_raw | Get a raw bytes setting | value (key) |
settings_get_all | Get all settings at once | (none) |
settings_set_bool | Set a boolean setting | key, value |
settings_set_i32 | Set an i32 setting | key, value |
settings_set_f32 | Set an f32 setting | key, value |
settings_set_string | Set a string setting | key, value |
settings_set_path_buf | Set a PathBuf setting | key, value |
settings_set_vec_string | Set a Vec<String> setting | key, value |
settings_set_vec_raw | Set a raw bytes setting | key, value |
backup_settings | Backup settings to memory | (none) |
clear_settings | Clear all settings | (none) |
restore_backup_settings | Restore settings from backup | (none) |
Path Queries
| Tool | Description |
|---|---|
config_path | Get the config path |
assembly_kit_path | Get the Assembly Kit path |
backup_autosave_path | Get the backup autosave path |
old_ak_data_path | Get the old AK data path |
schemas_path | Get the schemas path |
table_profiles_path | Get the table profiles path |
translations_local_path | Get the translations local path |
dependencies_cache_path | Get the dependencies cache path |
settings_clear_path | Clear a config path |
Specialized
| Tool | Description | Key Arguments |
|---|---|---|
open_pack_info | Get pack info and file list | pack_key |
initialize_my_mod_folder | Initialize a MyMod folder | name, game, sublime, vscode, gitignore |
live_export | Live export a pack for testing | pack_key |
patch_siege_ai | Patch SiegeAI for Warhammer maps | pack_key |
pack_map | Pack map tiles | pack_key, tile_maps, tiles |
generate_missing_loc_data | Generate missing loc entries | pack_key |
get_pack_translation | Get translation data | pack_key, language |
build_starpos_get_campaign_ids | Get campaign IDs for starpos | pack_key |
build_starpos_check_victory_conditions | Check victory conditions file | pack_key |
build_starpos | Build starpos (pre-processing) | pack_key, campaign_id, process_hlp_spd |
build_starpos_post | Build starpos (post-processing) | pack_key, campaign_id, process_hlp_spd |
build_starpos_cleanup | Clean up starpos temp files | pack_key, campaign_id, process_hlp_spd |
update_anim_ids | Update animation IDs | pack_key, starting_id, offset |
get_anim_paths_by_skeleton_name | Get anim paths by skeleton | value |
export_rigid_to_gltf | Export RigidModel to glTF | rigid_model, output_path |
set_video_format | Change video format | pack_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:
- Set the game:
set_game_selectedwith the game key andrebuild_dependencies: true - Open a pack:
open_packfileswith the file path(s) - Browse files:
open_pack_infoto get the pack’s file tree - Read data:
decode_packed_fileto decode individual files - Modify data:
save_packed_file_from_viewto save edited data back - Save:
save_packfileto 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
idfor correlation - The
SessionConnectedmessage arrives immediately on connection withid: 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:
- Connect to
ws://127.0.0.1:45127/wsusing any WebSocket library - Send JSON messages in the format
{ "id": <number>, "data": <command> } - Receive JSON messages and match responses by
id - Handle the
SessionConnectedmessage (id=0) on connect - Send
ClientDisconnectingbefore 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 paths — MyMod 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 channel —
StableorBeta. - 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
.bakfile 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) orCtrl+Shift+P(command palette) and type the name of what you want. If the action exists, you can launch it from there.
Pack Menu
| Action | Default |
|---|---|
| New Pack | Ctrl+N |
| Open Packs | Ctrl+O |
| Open & Merge Packs | Ctrl+Shift+O |
| Save All Packs | Ctrl+S |
| Install Pack | Ctrl+Shift+I |
| Uninstall Pack | Ctrl+Shift+U |
| Load All CA Packs | Ctrl+G |
| Settings | Ctrl+, |
| 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
| Action | Default |
|---|---|
| Close Tab | Ctrl+W |
| Previous Tab | Ctrl+Shift+Tab |
| Next Tab | Ctrl+Tab |
| Toggle Quick Notes | (unbound) |
| Import From Dependencies | (unbound) |
View menu
| Action | Default |
|---|---|
| Toggle Global Search | Ctrl+Shift+F |
| Toggle Pack Contents | (unbound) |
| Toggle Diagnostics | (unbound) |
| Toggle Dependencies | (unbound) |
| Toggle References | (unbound) |
Command palette
| Action | Default |
|---|---|
| Open file palette | Ctrl+P |
| Open command palette | Ctrl+Shift+P |
Pack tree (context-scoped)
| Action | Default |
|---|---|
| Add File | Ctrl+A |
| Add Folder | Ctrl+Shift+A |
| Add From Pack | Ctrl+Alt+A |
| New Folder | Ctrl+F |
| New DB | Ctrl+D |
| New Loc | Ctrl+L |
| New Text | Ctrl+T |
| New Quick File | Ctrl+Q |
| New AnimPack / Portrait Settings | (unbound) |
| Merge Files | Ctrl+M |
| Delete | Del |
| Extract | Ctrl+E |
| Rename / Move | Ctrl+R or F2 |
| Copy | Ctrl+C |
| Cut | Ctrl+X |
| Paste | Ctrl+V |
| Duplicate | Ctrl+Shift+D |
| Open with Decoder | Ctrl+J |
| Open with External Program | Ctrl+K |
| Open Pack Notes | Ctrl+Y |
| Expand All | Ctrl++ |
| Collapse All | Ctrl+- |
Table editor (DB / Loc)
| Action | Default |
|---|---|
| Add Row | Ctrl+Shift+A |
| Insert Row | Ctrl+I |
| Delete Row | Ctrl+Del |
| Delete Filtered-Out Rows | Ctrl+Shift+Del |
| Clone & Insert Row | Ctrl+D |
| Clone & Append Row | Ctrl+Shift+D |
| Copy | Ctrl+C |
| Copy as LUA Table | Ctrl+Shift+C |
| Paste | Ctrl+V |
| Paste as New Row | Ctrl+Shift+V |
| Rewrite Selection | Ctrl+Y |
| Invert Selection | Ctrl+- |
| Search | Ctrl+F |
| Undo / Redo | Ctrl+Z / Ctrl+Shift+Z |
| Smart Delete | Del |
| Find References / Go To Definition / Go To File / Patch Columns / Generate IDs / Import TSV / Export TSV / etc. | (unbound) |
Decoder
| Action | Default |
|---|---|
| Move Field Up | Ctrl+Up |
| Move Field Down | Ctrl+Down |
| Move Field Left | Ctrl+Left |
| Move Field Right | Ctrl+Right |
| Delete Field | Ctrl+Del |
| Delete Definition | Ctrl+Del |
| Load Definition | Ctrl+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.
| Game | Game key | Editing | Notes |
|---|---|---|---|
| Total War: Pharaoh – Dynasties | pharaoh_dynasties | Full | |
| Total War: Pharaoh | pharaoh | Full | |
| Total War: Warhammer 3 | warhammer_3 | Full | First game with twad_key_deletes (patch 6.3+). |
| Total War Saga: Troy | troy | Full | |
| Total War: Three Kingdoms | three_kingdoms | Full | |
| Total War: Warhammer 2 | warhammer_2 | Full | |
| Total War: Warhammer | warhammer | Full | |
| Total War Saga: Thrones of Britannia | thrones_of_britannia | Full | |
| Total War: Attila | attila | Full | |
| Total War: Rome 2 | rome_2 | Full | |
| Total War: Shogun 2 | shogun_2 | Full | |
| Total War: Napoleon | napoleon | Full | No Assembly Kit ever shipped — RPFM uses archived AK definitions. |
| Total War: Empire | empire | Full | No Assembly Kit ever shipped — RPFM uses archived AK definitions. |
| Total War: Arena | arena | Read-only | Discontinued; 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
Lzma1for non-table files. Warhammer 3 addsLz4andZstdon top ofLzma1. Anything older than Warhammer 1 supports no compression at all. twad_key_deletesis 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_*.csvshipped 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 formatched_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:
- Open one of the affected files in the DB Decoder.
- Adjust the column types until the first row decoded field shows sane values.
- Save the definition into the schema.
- Try to open the table again and see if it opens correctly.
- 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.FieldandFieldType— 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):
| Flag | Default | What it enables |
|---|---|---|
enable_tools | yes | Compiles the integrated Tools menu (Translator, Faction Painter, Unit Editor). |
support_model_renderer | no | 3D RigidModel preview. Requires the renderer library on the link path. |
support_uic | no | Propagates UIC parsing support through rpfm_lib. |
strict_subclasses_compilation | no | Forces a compilation failure if qt_rpfm_subclasses fails to build. |
only_for_the_brave | no | Exposes 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.