Roadmap
This is the project’s working roadmap. It tracks the big reverse-engineering and tooling milestones — not every commit.
Status key: Shipped done and verified · In progress partially working · Blocked waiting on a hard dependency · Planned designed, not started.
Shipped Shipped
| Milestone | What landed |
|---|---|
| Uncooked asset mirror | data/uncooked/ extract pipeline — textures decoded (BC1/BC3/BC5/RGBA8 → PNG, 4,776 PNGs) plus 15,431 raw .gen + 15,362 structural sidecars. See Uncooked assets. |
| Pack guard | rsmm pack refuses files byte-identical to the original (--allow-vanilla for personal backups), preventing accidental redistribution of copyrighted assets. |
| Mass decompile | 46,963 functions decompiled; symbols + strings exported to JSON. See RE pipeline. |
Pattern resolver + rsmm.call | 53,427 byte-pattern signatures in data/function_patterns.json (99.50% self-validate). Lua mods can call any of ~53k game functions by name, plus memory read/write. See Calling game functions. |
| Seed surface mapped | The built-in Forced seed option (oe::UIntGameOption, id 0x1949b098) located with its enable bool. ExampleSeedPin pins a run’s RNG for speedruns. See Seed + mapgen. |
In-game “Mods” UI
Giving the mod manager a presence inside Ravenswatch’s own menus. This is the deepest RE thread, split into three stages.
Slot redirect — own entity in the Social book Shipped
A live loader hook (hook_engine.cpp) intercepts the engine’s entity-path
lookup, detects a fake path (…\Social\Mods_List.entity.ot), and returns the
cached handle of a chosen substitute entity instead of a path-miss. Verified
in-game: slot 7 renders a distinct, fully interactive entity.
What it proved: we can render a slot-sized page we control inside the Social
book, and clicks in that slot already translate to disk writes that toggle a
mod’s enabled flag.
Custom Mods_List.entity.ot In progress
A byte-clone of a vanilla list is on disk at the encoded path, registered in
asset_map.json, and the loader now resolves it to a real handle. Remaining
work is the entity binary schema so we can place rows we own:
oCEntitySettingstree (base) + the UI component classes for a minimal static-button list:WindowUiSettings(panel),ButtonUiSettings(row),LabelUiSettings(text), picture/label descs, navigable zone, and a picker that binds a label to a text-bank key.- Section markers (
1111bbaaBEGIN /2222bbaaEND) — already parsed/emitted reliably by the.otdecoder. - A fresh 16-byte entity GUID and the cooked-encoded output path.
- Decode a minimal single-button entity (e.g.
Option_ApplyButton.entity.ot) to map each component class’s byte layout. - Extend the
.otre-encoder forButtonUiSettings+LabelUiSettings. - Emit
Mods_List.entity.otwith N rows, each bound to a key likeRSMM_Mod_Slot_0. - Write those keys with mod names from
mods/*/manifest.toml. - Drop the cooked file at the encoded path; the live hook resolves it directly.
Real “Mods” menu button In progress
A structural patch (make_real_menu_button_mod.py) already clones the Discord
button so the main menu carries 12 picker entries (was 10) and 68 sections (was
66), and it round-trips cleanly through the decoder. Awaiting in-game checks:
is the button visible, does it collide with Discord’s position, and what label
does it inherit. Follow-up: pin down the position field in
oCEntityCpntEntitySpawnerSettings and offset the clone.
Acceptance criteria: a new visible main-menu button (own label from a text
bank we control), click opens our UI or a controlled engine event, fully
reversible via ./rsmm restore --all with zero trace.
Hook API (rsmm.hook) Blocked
rsmm.call covers invoking game functions; the missing surface is
intercepting them (observer/mutator callbacks). The MinHook engine is wired but
every hookpoint tried so far crashes the game under its anti-tamper integrity
check.
Paths forward:
- Inject before anti-tamper init via a TLS-callback DLL (runs before the exe entry point) — confirm the first integrity sweep happens after.
- Hook outside
.text— redirect through Vulkan loader / Steam SDK / FMOD pointers we own indirectly, without touching the game image. - Indirect-call trap — resolve the target and trap only when called from
rsmm.call_hooked(), non-invasive to the game image.
Until one lands, rsmm.hook returns an explicit “not supported on this title”
error so mods fail at write-time, not run-time.
Pattern-DB self-rebuild on game update Planned
When Steam updates Ravenswatch, the signatures in function_patterns.json must
be regenerated against the new exe. Missing UX:
./rsmm doctorshould hash the installed exe against theimage_base + filesizerecorded in the pattern DB header and warn whenrsmm.resolvemay degrade../rsmm rebuild-fn-patterns— one-shot wrapper that re-runs the Ghidra dump + pattern generation + resolver self-test and reports the unique-rate (~30 min per regen).