Skip to content

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

MilestoneWhat landed
Uncooked asset mirrordata/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 guardrsmm pack refuses files byte-identical to the original (--allow-vanilla for personal backups), preventing accidental redistribution of copyrighted assets.
Mass decompile46,963 functions decompiled; symbols + strings exported to JSON. See RE pipeline.
Pattern resolver + rsmm.call53,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 mappedThe 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:

  • oCEntitySettings tree (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 (1111bbaa BEGIN / 2222bbaa END) — already parsed/emitted reliably by the .ot decoder.
  • A fresh 16-byte entity GUID and the cooked-encoded output path.
  1. Decode a minimal single-button entity (e.g. Option_ApplyButton.entity.ot) to map each component class’s byte layout.
  2. Extend the .ot re-encoder for ButtonUiSettings + LabelUiSettings.
  3. Emit Mods_List.entity.ot with N rows, each bound to a key like RSMM_Mod_Slot_0.
  4. Write those keys with mod names from mods/*/manifest.toml.
  5. 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:

  1. Inject before anti-tamper init via a TLS-callback DLL (runs before the exe entry point) — confirm the first integrity sweep happens after.
  2. Hook outside .text — redirect through Vulkan loader / Steam SDK / FMOD pointers we own indirectly, without touching the game image.
  3. 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 doctor should hash the installed exe against the image_base + filesize recorded in the pattern DB header and warn when rsmm.resolve may 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).