Skip to content

About

Decrypting RPG Maker game assets (XP / VX / VX Ace / MV / MZ).

Topics

Resources

Contributing

Stars

126 stars

Watchers

1 watching

Forks

Latest commit

Β 

History

87 Commits

Folders and files

Repository files navigation

πŸ—οΈ rpgm-decrypt

One small binary that unlocks the assets out of any RPG Maker game.

XP Β· VX Β· VX Ace Β· MV Β· MZ β€” all five engine generations, one command, no install.

License Formats Built with Single binary Fuzzed Mutation Verified


⚑ See it work

rpgm-decrypt decrypting a real game from the command line
$ rpgm-decrypt ./MyGame ./decrypted

[key]  found encryptionKey in www/js/System.json
  +  Title.png_   detected as MV
  >  Title.png_   ->  decrypted/www/img/Title.png   [MV]
  +  Battle.ogg_  detected as MV
  >  Battle.ogg_  ->  decrypted/www/audio/Battle.ogg [MV]
  ...

=== summary ===
scanned:      1873
decrypted:    1869
failed:          0
key source:   www/js/System.json
duration:     4.2s
by format:    MV=1869

Need machine-readable output for a script? Add --report-format json:

{
  "scanned": 1873,
  "decrypted": 1869,
  "failed": 0,
  "key_source": "www/js/System.json",
  "per_format": { "MV": 1869 },
  "duration_s": 4.2
}

Tip

You don't install anything. Download one file, run it. No Python, no .NET, no Node β€” the binary carries everything it needs inside.

Important

Use it on content you're allowed to touch. Recovering your own assets, a lost encryption key, an authorized translation, or game preservation β€” all fine. Decrypting someone else's work to steal it is not. The license is permissive; your responsibility isn't.


✨ What you get

Feature In plain words
🧩 All 5 formats .rgssad / .rgss2a / .rgss3a (XP/VX/VX Ace) and MV/MZ (.png_, .ogg_, .rpgmvp, .pak) β€” one tool, not five.
πŸ”‘ Finds the key for you Reads System.json / scans rpg_core.js automatically. No key to hunt down by hand.
πŸ“¦ One self-contained binary Copy it to a clean machine and it just runs. Zero dependencies.
πŸ›‘οΈ Safe by construction Hostile/corrupt input never crashes it (proven by fuzzing) and can't escape your output folder (Zip-Slip blocked).
πŸ€– Script-friendly --report-format json + NDJSON logs pipe straight into jq.
πŸ§ͺ Provably correct core Key functions carry formal contracts (Gospel); core safety properties are machine-checked (Why3 / Z3).

πŸš€ Quick start

1. Download the binary for your OS from the Releases page.

2. Run it on a game folder:

# simplest form β€” output goes next to the game folder
rpgm-decrypt ./MyGame

# pick your own output folder
rpgm-decrypt ./MyGame ./decrypted

# only the decrypted assets, not a full game copy
rpgm-decrypt ./MyGame --assets-only

# look but don't write anything (see what it WOULD do)
rpgm-decrypt ./MyGame --dry-run

That's it. By default you get a full, ready-to-run copy of the game: every file is mirrored into the output folder, the encrypted assets are decrypted in place (.rpgmvp/.png_ β†’ .png, etc.), and System.json's encryption flags are cleared β€” so the copy just runs, with all the .json/.txt/script files left intact. Want only the decrypted media instead? Add --assets-only.

πŸͺŸ Easiest way on Windows (no terminal)

Not comfortable with the command line? The Windows download includes a small helper, decrypt.bat:

  1. Download rpgm-decrypt-windows-x64.zip and extract the whole folder (keep rpgm-decrypt.exe, zlib1.dll and decrypt.bat together).
  2. Drag your game folder onto decrypt.bat.
  3. Done β€” a full, ready-to-run decrypted copy of the game appears in a new folder next to your game, named <your-game>-decrypted. Everything else (.json, .txt, scripts, the exe) is copied across untouched, so you can run or edit the copy directly.

No install, no typing. (zlib1.dll is just the standard compression library Windows needs to read .pak archives β€” keep it next to the .exe.)

Drag a game folder onto decrypt.bat and it decrypts automatically

πŸ–₯️ GUI for Windows (Ruby + GTK3)

Prefer clicking to typing? There is a separate, optional GUI build β€” rpgm-decrypt-gui-windows-x64.zip β€” that wraps the same OCaml decrypter behind a native GTK3 window. It is a self-contained hybrid: the OCaml binary does the decrypting, the Ruby GUI just drives it and shows live progress.

Download rpgm-decrypt-gui-windows-x64.zip from the Releases page, extract it (the archive unpacks into a single rpgm-decrypt-gui\ folder β€” put it anywhere you can write, like Desktop or Documents), and double-click rpgm-decrypt-gui.exe. Everything ships inside: rpgm-decrypt.exe, zlib1.dll, the GTK3 runtime and a README.txt quick start. No Ruby, no GTK, no other installs needed.

The GUI is versioned on its own line (independent of the core): the current release is GUI 1.0.0, and the version is shown under the window title.

Tip

Windows SmartScreen may show "Windows protected your PC" because the exe is unsigned. Click More info β†’ Run anyway. This is normal for open-source builds without a paid code-signing certificate.

The GUI has two modes:

Mode What it does
Whole game Decrypt a whole game folder (full mirror copy, or Assets-only). Same result as the CLI. Live progress bar + log.
Single file List the game's files, pick one encrypted asset (.png_ / .rpgmvp / .ogg_ / …), decrypt just that file, and restore it in place β€” replace the encrypted original with the open one (the original is saved as <file>.bak first). Great for poking at a single sprite/audio without decrypting the whole game.

The window also exposes the core's --dry-run mode (scan and report without writing anything), a Stop button that terminates a running job, --password-file candidate key lists, a confirmation before merging into a non-empty output folder, an Open output folder shortcut, and Save log to dump the progress log to a file.

Note

Point-decrypt is a convenience, not a re-encrypter. It decrypts the game into a temporary folder, grabs the one file you asked for, then deletes the rest β€” so the OCaml core is never modified. "Restore in place" puts the open (decrypted) file back; if you want the game re-encrypted you'd use the CLI normally. Archives (.rgssad / .rgss2a / .rgss3a) are not supported in point mode β€” use Whole game for those.

Tip

The GUI and the CLI ship as separate assets. Keep using the CLI (or decrypt.bat) if you just want a full decrypted copy; grab the GUI when you want a window and per-file control.

Note

First launch asks you to star β˜… the repository. The GUI is free and open source; on the first run it shows a one-time "support the project" screen: sign in with GitHub (a 3-step device flow β€” the code opens in your browser, no password is typed into the app), star the repository, and the GUI unlocks forever on that computer β€” the activation is stored locally and works offline from then on. Identity is verified against the signed-in account, so starring with someone else's nickname is not possible.


πŸ”‘ How it finds the key (no flags needed)

When you don't pass a key, rpgm-decrypt looks for it the way the engine stores it:

1.  www/js/System.json   β†’  read "encryptionKey"   (the normal case)
2.  www/data/System.json β†’  same, alternate layout
3.  www/js/rpg_core.js   β†’  extract the 32-hex key literal (never runs the JS)
4.  *.js sweep           β†’  last resort, scan every script for the key

Note

Can't find it automatically (custom build, stripped files)? Hand it the key:

rpgm-decrypt ./MyGame --password deadbeef00112233445566778899aabb
rpgm-decrypt ./MyGame --password-file keys.txt      # try a list, first match wins
rpgm-decrypt ./MyGame --vxace-seed 1a2b3c4d          # verify VX Ace archive seed

βš™οΈ Options

Flag Default What it does
--password <hex32> β€” One key, 32 hex chars (16 bytes).
--password-file <path> β€” Newline-separated candidate keys; first that works wins.
--vxace-seed <8hex> β€” Verify the embedded RPG Maker VX Ace seed; MV/MZ still need --password.
--assets-only off Write only the decrypted assets, not a full game copy.
--log-format human|json human Per-file progress on stderr (json = one NDJSON event per line).
--report-format human|json human Final summary on stdout.
--dry-run off Walk + detect + classify, but write nothing.
--quiet off Hide per-file progress, show only the summary.
-h, --help β€” Show help.
--version β€” Version + supported formats.

Exit codes

Code Meaning
0 βœ… Everything decrypted.
2 ⚠️ Usage error (bad args).
3 πŸ’½ I/O error (can't read input / write output).
4 πŸ”‘ No key could be recovered.
5 🟑 Partial β€” at least one file failed (details in the report).

πŸ—οΈ Built like it matters

Most decrypters are a quick script. This one is engineered as a real product β€” that's the part a senior engineer notices:

Discipline What it means here
🧬 Pure functional core Narrow .mli per module, 103 behavioural checks + 12 QCheck property tests, mutation-tested (7/7 historical mutants killed).
πŸ’₯ Fuzzed against chaos 21M+ mutated/garbage inputs, 0 crashes β€” plus coverage-guided afl-fuzz in CI. It does not fall over on broken files.
πŸ“ Property-based tests Invariants, not examples: "XOR is its own inverse for any key & data", "a path can never escape the output root".
πŸ”’ Formally verified core The parser carries Gospel contracts; its key safety properties β€” bounds, little-endian decode, and the no-path-escape (Zip-Slip) invariant β€” are machine-checked by Why3 / Z3. (details)
🧹 Zero-warning, formatted, clean-room Builds warning-as-error, ocamlformat-canonical, Apache-2.0, no decompiled code.
πŸ” CI on every push Linux + Windows build/test, security scanners (SAST, Secret scanning), fuzzing, verification.

Caution

The decrypter is built for robustness on adversarial input on purpose: it parses file formats produced by other programs, so it treats every byte as untrusted. That's why the parser is fuzzed and formally verified rather than just "tested".


πŸ“Š Test & verification reports

Code coverage

Coverage

Generated by bisect_ppx (OCaml's coverage tool) over the historical 92 behavioural checks + 12 QCheck properties. The committed snapshot predates the additional regression checks now in test/test.ml; regenerate it before using the percentages as a current measurement. The numbers split into two tiers: the core decryption logic (XOR crypto, key derivation, the archive parsers) is 80–100% covered, while the orchestration / I-O modules that talk to the disk/JSON/regex (report, key_discovery, log) sit at 36–52%.

File Coverage Covered / Total
lib/vxace_key.ml 100.0% 35 / 35
lib/io.ml 100.0% 4 / 4
lib/crypto.ml 95.4% 104 / 109
lib/rgssad_core.ml 87.0% 67 / 77
lib/walk.ml 81.1% 30 / 37
lib/vxace.ml 80.4% 37 / 46
lib/mv.ml 75.7% 56 / 74
lib/dispatch.ml 63.2% 55 / 87
lib/mz.ml 63.0% 17 / 27
lib/report.ml 51.8% 184 / 355
lib/key_discovery.ml 50.3% 80 / 159
lib/types.ml 45.8% 11 / 24
lib/log.ml 36.4% 24 / 66
lib/xp.ml 33.3% 1 / 3
lib/vx.ml 33.3% 1 / 3
Project 63.8% 706 / 1106

xp.ml / vx.ml are thin wrappers over rgssad_core (one-line parse functions); their low counts reflect the wrapper delegation, not untested logic β€” the shared parser is 87.9% covered.

Why the pure core is 80–100% but I/O is 36–41% (in plain words)

Think of the tool in two layers:

  • The pure core β€” the actual decryption: XOR math, key derivation, the archive parsers, the file-format classifiers. This is pure logic: bytes in, bytes out, no disk, no network. It is exactly what the test suite exercises (103 behavioural checks + 12 property tests feed it thousands of crafted inputs in-process), so the crypto/parser core ends up 80–100% covered. This is the part that must be correct β€” and it is the part that is also fuzzed and formally verified.

  • The I/O modules β€” report.run (walks the game folder, writes the decrypted mirror tree), key_discovery (reads System.json, scans rpg_core.js), log (formats output). These only do real work when pointed at an actual RPG Maker game on disk β€” hundreds of files, real JSON, real directory structures. That kind of load can't run inside the in-process test suite, and we deliberately do not commit real game files into the repo (licensing cleanliness β€” see CONTRIBUTING.md). So the suite exercises only the fast/synthetic paths of these modules, leaving their full end-to-end branches uncounted. Hence 36–52%.

This is not a gap in the decryption correctness β€” the core that does the decrypting is nearly fully covered, fuzzed, and proven. It is a measurement artifact: the I/O layer's real workload needs real games, which live outside the repo. A CI run against a real (private, sanitized) game fixture would lift these numbers; it is just not something we keep in version control.

Full browsable report: ocaml/coverage/html/index.html Β· machine-readable: ocaml/coverage/coverage.cobertura.xml Β· raw summary: ocaml/coverage/summary.txt.

How coverage is generated
opam install bisect_ppx            # OCaml ≀ 5.3 (use a separate switch on 5.5)
cd ocaml
dune build --instrument-with bisect_ppx --force test/test.exe test/prop/prop.exe
BISECT_FILE=$PWD/_build/cov dune exec --instrument-with bisect_ppx --force test/test.exe
BISECT_FILE=$PWD/_build/cov dune exec --instrument-with bisect_ppx --force test/prop/prop.exe
bisect-ppx-report summary --per-file _build/*.coverage
bisect-ppx-report cobertura coverage.cobertura.xml _build/*.coverage
bisect-ppx-report html _build/*.coverage -o coverage/html

The ocaml-coverage GitLab CI job (when: manual) reproduces this and exposes the report as a downloadable artifact. bisect_ppx 2.8.x emits html / cobertura / coveralls / summary (it dropped native LCOV output; Cobertura XML is the machine-readable interchange format here).

Mutation testing

Mutation score

A directed smoke campaign: 7 single-edit mutants on the verified core, each rebuilt and run against the historical 92 behavioural checks + 12 QCheck properties. First wave found 2 real coverage gaps (high bytes of read_u32_le, the ogg extension map); after targeted test additions, all 7 are killed.

# Mutant Result
M1 crypto: xor key index i mod klen β†’ i mod (klen+1) πŸ”΄ killed
M2 crypto: xor output size n β†’ n+1 πŸ”΄ killed
M3 vxace_key: derive_master_key +3 β†’ +4 πŸ”΄ killed
M4 rgssad_core: read_u32_le byte1 lsl 8 β†’ lsl 16 πŸ”΄ killed
M5 report: safe_join full = root β†’ full = full πŸ”΄ killed
M6 dispatch: choose_output_extension png β†’ .pngx πŸ”΄ killed
M7 dispatch: choose_output_extension ogg β†’ .png πŸ”΄ killed

Full report with the survivor analysis and the test additions that closed the gaps: ocaml/MUTATION_REPORT.md.


πŸ”§ Build from source

OCaml (flagship β€” single native binary)
cd ocaml
dune build --profile release
./_build/default/bin/main.exe --version

For a portable, statically-linked Linux binary, build on Alpine (musl) with dune build --profile static.

Run the tests
# OCaml: 103 behavioural checks + 12 QCheck property tests
cd ocaml && dune exec --profile release test/test.exe

# QCheck properties (needs `opam install qcheck`)
cd ocaml && dune exec --profile release test/prop/prop.exe

Test fixtures are generated synthetically at test time β€” we never commit real RPG Maker game bytes, to keep the repo licensing-clean.

Project layout
ocaml/                  OCaml flagship β€” single native binary
  lib/                    pure core + narrow .mli (Gospel specs)
  bin/                    CLI front-end
  test/  test/prop/       behavioural checks + QCheck properties
  fuzz/                   afl-instrumented fuzz target
  proofs/                 Why3 + Z3 deductive verification (Zip-Slip, bounds)
  coverage/               bisect_ppx coverage report (HTML + Cobertura XML)
  MUTATION_REPORT.md      mutation testing campaign (7/7 killed)
gui/                   Windows GUI (Ruby + GTK3) β€” hybrid front-end over the OCaml binary
  main.rb                GTK3 window: "Whole game" + point-decrypt "Single file"
  build_windows.ps1      OCRA bundle β†’ rpgm-decrypt-gui-windows-x64.zip
.gitlab-ci.yml          test + fuzz + verification farm
.github/workflows/      non-release verification + tag-triggered releases

πŸ“œ License

Apache-2.0 β€” free to use, modify, and ship. See LICENSE.

Clean-room implementation: built from public format documentation and community wikis, no decompiled engine code. Every magic-byte constant is cited in the source.


Found a game it can't crack? Open an issue with the game layout and the failing file β€” real bugs get fixed.

Built turn-key, spec-to-shipped. Every claim here is backed by a test, a fuzz run, or a proof.

About

Decrypting RPG Maker game assets (XP / VX / VX Ace / MV / MZ).

Topics

Resources

Contributing

Stars

126 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages