Odel
rpf

rpf

Local
@molchalih1RustApache-2.0Updated 3 days ago

Read and write RAGE Package Format archives

rpfd

release rust 1.98+ platforms

A dependency-light Rust toolchain for reading, editing and rebuilding RPF7 archives — the dlc.rpf files RAGE Multiplayer and FiveM servers hand to their clients, and the archives Grand Theft Auto V ships. One command-line binary with no runtime prerequisite, and the same code behind a JSON-RPC daemon and a VS Code extension.

Formats

VersionVariantUsed bySupport
RPF7OPEN, unencryptedRAGE MP and FiveM server packsRead, write
RPF7AES-256, 0x0FFFFFF9GTA V, nested archives such as des_* and script_*Read, write — key from the game executable
RPF7AES-256, 0x0FFFFFF7Rockstar Games LauncherRead, write — key from Launcher.exe
RPF7NG, 0x0FEFFFFFGTA V Legacy and Enhanced, every top-level archiveRead, write — both need a memory image
RPF8Red Dead Redemption 2Not supported
RPF6Red Dead Redemption, 2010 and 2023Not supported
RPF4Max Payne 3Not supported
RPF3GTA IV audio, Midnight Club: LANot supported
RPF2GTA IV, main archivesNot supported
RPF0Table TennisNot supported

Pre-RPF7 attributions are read from other implementations, not measured here; docs/rpf-format.md records them and where they disagree. Every non-RPF7 version is recognised by its magic word and refused by number.

Encryption is per entry as well as per archive: the tag covers the table of contents and the names blob, and each entry's row says whether its payload is under the transform.

Installing

INSTALL.md is the page for this, and it answers three separate questions: how to install the editor extension from a .vsix, how to register the MCP server with an editor or an agent, and where to put the binary if that is all you want. Prebuilt binaries for macOS, Windows and Linux are attached to each release.

To build from source instead — the toolchain is pinned in rust-toolchain.toml:

git clone <this repository> && cd rpf
cargo build --release          # target/release/rpf

Encrypted archives need key material, extracted from your own installation:

$ rpf keys extract GTA5.exe         # the AES key and the hash lookup table
$ rpf keys extract Launcher.exe     # the second AES key, carried by no game executable
$ rpf keys extract <memory image>   # the NG expanded keys and decrypt tables

No key material is bundled here, and none ever will be. Each source is cached under the hash of its own bytes in ./keys, which every command consults without a flag; --cache-dir selects another cache, the one way to keep several installations apart, and rpf keys invalidate empties one. No command prints a key — only offsets, lengths, counts and paths. NG material stands in the clear only in a memory image of a running game, so that is the sole route to an NG archive; obtaining one is out of scope here. An archive whose material is absent fails rather than guessing.

Usage

List an archive, descending into the archives inside it:

$ rpf ls -R dlc.rpf
binary    xml          2199  content.xml
directory -               3  data
binary    xml          5100  data/vehicles.meta
directory -               2  x64
directory -               1  x64/vehiclemods
binary    -         2544128  x64/vehiclemods/meringls63amg24_mods.rpf
resource  -          262144  x64/vehiclemods/meringls63amg24_mods.rpf/meringls63amg24_brabus_diffuser_1.yft
...

The second column says what an entry holds; pso, rbf and meta have an XML view:

$ rpf cat --as xml des_hosp_ceil2.rpf des_hosp_ceil2.ytyp
<?xml version="1.0" encoding="UTF-8"?>
<hash_D98BB561 pso:struct="hash_D98BB561">
  <hash_018A3B1B pso:array="atarray">
    <pso:item pso:struct="hash_82D6FC83">
      <hash_BF74DFA7 pso:float="100.0"/>
      <hash_D9EF8236 pso:float3="-2.80584, -2.95097, 0.0"/>
...

rpf put --as xml writes an edited document back in the entry's own encoding. Ask what a write costs, then make it:

$ rpf put dlc.rpf data/vehicles.meta edited.meta --dry-run
would patch 1632 bytes in place at 2048 (room for 2048)

$ rpf put dlc.rpf data/vehicles.meta edited.meta
patched 1632 bytes in place at 2048 (room for 2048)

put --create, rm, mv and mkdir move every offset after the header, so they always rebuild and say so first. A rebuild is atomic: a scratch file beside the archive replaces the original in one step.

Take an archive apart, build it back, check the result:

$ rpf extract dlc.rpf tree/
7 files and 3 directories into tree/

$ rpf pack tree/ rebuilt.rpf
11 entries, 65160704 bytes

$ rpf verify rebuilt.rpf --against tree/
27 entries read back; 7 of 7 recorded checksums checked against tree/
20 entries carry no recorded checksum: an entry inside a nested archive is covered by the checksum of the entry that holds it

Every reporting command takes --json; rpf --help lists the rest. clients/agent/README.md is the page for driving the tool from a program: the JSON shapes, the failure object, and cat --out for a payload nobody is going to read.

The daemon

rpf serve --stdio speaks JSON-RPC, one object per line. It answers everything the binary does, holds edits until commit, and reports a long rebuild's progress as cancellable notifications.

$ echo '{"jsonrpc":"2.0","id":1,"method":"open","params":{"path":"/tmp/dlc.rpf"}}' | rpf serve --stdio
{"id":1,"jsonrpc":"2.0","result":{"entries":11,"handle":1,"len":144504832,"path":"/private/tmp/dlc.rpf"}}

The editor extension

clients/vscode mounts an archive as a workspace folder: files open, edit and save like any other, and a nested archive is a folder inside a folder. The archive is written by one explicit act, previewed as patch or rebuild. See clients/vscode/README.md.

Exit codes

Stable, so a caller can classify a failure without reading the message. The daemon reports the same numbers as a JSON-RPC error.code, with a symbolic error.data.reason.

CodeMeaning
0Everything worked
1A failure with no better classification
2The arguments were wrong
3The path is not in the archive
4The archive is malformed or does not decompress as it promises
5The archive needs key material that is not available
6The request or its input was wrong, and the tool declined to act
7Reading or writing failed
8The caller stopped the operation part-way
9This build cannot do it

Building and testing

cargo build --release
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all

The suite needs no game data and passes without it, skipping what it cannot reach. Four variables point it at real data: RPF_CORPUS at a directory of archives, RPF_GAME_EXE at game executables, RPF_GAME_IMAGE at one memory image of a running game, and RPF_METADATA at metadata payloads already out of their archives, as tools/metadata-dump writes them. Each has a companion — RPF_REQUIRE_CORPUS and its three siblings — turning its own skips into failures.

DR-N, cited throughout the source and the documents, names a decision record under docs/decisions/, kept in the working tree and not distributed.

Licence

MIT OR Apache-2.0, at your option. See LICENSE-MIT and LICENSE-APACHE.