source2rosetta/crates/source2rosetta-core
Kamal Tufekcic c458b4cb50
All checks were successful
CI / lint (push) Successful in 17s
CI / fuzz (push) Successful in 1m52s
CI / test (push) Successful in 24s
read what the binary says about itself: names, signatures, prototypes; gen v2
2026-07-29 20:09:21 +03:00
..
src read what the binary says about itself: names, signatures, prototypes; gen v2 2026-07-29 20:09:21 +03:00
Cargo.toml read what the binary says about itself: names, signatures, prototypes; gen v2 2026-07-29 20:09:21 +03:00
README.md read what the binary says about itself: names, signatures, prototypes; gen v2 2026-07-29 20:09:21 +03:00

source2rosetta-gen

Render a published source2rosetta gamedata release into whatever format your framework reads. source2rosetta does the hard part — deriving CS2 / Dota 2 gamedata from the stripped engine and validating it on a live server — and publishes a small set of JSON files per game. source2rosetta-gen turns those into CounterStrikeSharp, Metamod/SourceMod, ModSharp, Swiftly, Plugify, or a typed C# SDK, locally, in a second.

It's deliberately tiny: it links only source2rosetta-core (serde + the format emitters) — no ELF reader, no disassembler, no ptrace. So a consumer who "just wants the files" downloads one release + this small binary and generates exactly what they need, instead of every format being pre-baked into the release.

Get it

Grab the prebuilt source2rosetta-gen from the release page, or build it from source:

cargo build --release -p source2rosetta-core
# -> target/release/source2rosetta-gen

(The gen binary lives in the source2rosetta-core crate, so a plain cargo build --release at the repo root does not build it — use -p source2rosetta-core or --workspace.)

Use it

Three of the published artifacts are gen inputs, one per -- flag:

  • gamedata-<game>.json (--from) — the derived gamedata (function signatures + vtable offsets), tiered by confidence.
  • netvars-<game>.json (--netvars) — the typed schema (field offsets + runtime types, plus the class base graph and per-type sizes).
  • abi-<game>.json (--abi) — declared parameter and return types, each re-judged against the footprint measured in that build. This is what a function TAKES, as opposed to where it is. See Call shapes.

bindings-<game>.json ships beside them and gen does not render it — it is not locator data. It is what the binary declares about itself, in five sections: Pulse bindings (display name, description, call policy, and each binding's typed signature), entity-IO inputs and outputs, map-classname → C++ class, and console commands. Plain JSON, readable as-is.

Then point gen at whichever you need and pick a --format. Output goes to --out, or stdout if omitted.

# CounterStrikeSharp combined gamedata (the default)
source2rosetta-gen --from gamedata-cs2.json --format cssharp   --out gamedata.json

# Metamod / SourceMod gamedata VDF (one .games.txt)
source2rosetta-gen --from gamedata-cs2.json --format metamod   --out csgo.games.txt

# Swiftly / ModSharp / Plugify gamedata
source2rosetta-gen --from gamedata-cs2.json --format swiftly   --out gamedata.json

# Typed C# SDK from the schema — one `static class` per engine class, `const` field offsets + types
source2rosetta-gen --netvars netvars-cs2.json --format cs-sdk  --out Schema.cs

# Flat netvar offset map (class -> field -> offset)
source2rosetta-gen --netvars netvars-cs2.json --format netvars --out netvars.json

Formats

--format needs output
cssharp (default) --from CounterStrikeSharp combined gamedata — JSONC: banner comments mean a strict JSON parser will reject it
metamod --from Metamod:Source / SourceMod gamedata VDF (.games.txt)
modsharp --from ModSharp gamedata JSON
swiftly --from Swiftly gamedata JSON — signature entries only; vtable-offset entries are omitted, because that framework takes offsets through a separate file
plugify --from Plugify gamedata JSON
model --from the selected tiers flattened to one name → locator map (format-neutral; not a re-readable monolith)
cs-sdk --netvars typed C# SDK — static class per schema class, const int field offsets tagged with their type
netvars --netvars flat schema map, { class: { field: offset } }

Call shapes — --abi abi-<game>.json

The same framework ids, a different input: --abi renders how to call a function rather than where it is. The input picks the family, so --abi … --format cssharp emits typed call sites while --from … --format cssharp emits the gamedata those calls resolve through.

All five framework ids work here, exactly as they do for --from:

source2rosetta-gen --abi abi-cs2.json --format cssharp  --out RosettaFunctions.cs
source2rosetta-gen --abi abi-cs2.json --format metamod  --out rosetta_prototypes.h
source2rosetta-gen --abi abi-cs2.json --format modsharp --out RosettaCalls.cs
source2rosetta-gen --abi abi-cs2.json --format swiftly  --out prototypes.json
source2rosetta-gen --abi abi-cs2.json --format plugify  --out prototypes.json
--format output
cssharp C# MemoryFunction* fields (signature) / VirtualFunction* factories (vtable slot)
metamod C++ header of using X_t = RET (*)(…), plus an X_vtidx constant for a slot — Metamod plugins are C++ and take the declared types verbatim
modsharp C# [AddressKey] interface for its Roslyn generator (signature) + a vtable-dispatch class (slot)
swiftly JSON per-function type descriptors ({"args":"ppf","ret":"v","call":"address"})
plugify JSON runtime type arrays ({"paramTypes":["pointer","string","float"],"retType":"void"})

Source for the two C# targets and for C++ because their type lists are compile-time; data for Swiftly and Plugify because theirs are resolved at runtime.

The two locator forms are not interchangeable, and every output distinguishes them. A signature resolves to one address; a vtable slot is entered through the object, so the framework reaches it by a different call entirely — VirtualFunctionVoid(instance, slot) rather than GameData.GetSignature(key), GetVFuncIndex rather than GetAddress, (*(void***)self)[idx] rather than a scanned pointer. Roughly a quarter of a LIVE-derived manifest's call sites are vtable-located, so binding them all through the signature path would look up keys that live in the gamedata's offsets section and never in its signatures one.

An offline-derived manifest emits none through the vtable path at all: a slot is recorded only once live validation has confirmed it is really a vtable slot and not a carried member offset, so an offline run states no slot rather than guess one. Same artifact shape, fewer vtable call sites — worth knowing before diffing two manifests produced different ways.

The receiver is always in the type list. Where the declaration came from an Itanium-mangled symbol this is invisible, so it is prepended, spelled from the function's own class (CBaseEntity*, not void*) and marked [this] in the C++ header. It is a real register in the call frame — leaving it out shifts every argument by one.

Only functions the deriver could stand behind are emitted: status: verified or lower-bound (the declaration passes registers the callee never reads and contradicts it in none — safe to call, and marked as such in every output), a receiver settled by evidence (the declaration names it, a live-validated vtable slot proves it, or the measurement independently agrees), and every parameter mappable onto an ABI class. A function whose return nobody declared is still emitted — otherwise Dota would lose 2,304 of its 3,732 call sites — but it is marked as such in every output (prose in the generated source, ret_declared / retDeclared in the data), and the value is documented as the raw return register rather than a typed result.

Confidence tier

The gamedata formats (the --from ones) take a --tier, cumulative and defaulting to high_confidence:

--tier includes
core only the guaranteed, first-class entries
high_confidence (default) core + the promoted (verified-name) entries
experimental the above + every graded name guess (each has a resolvable locator, but an unverified name)
# only the rock-solid set:
source2rosetta-gen --from gamedata-cs2.json --format cssharp --tier core --out gamedata.json

The schema formats (cs-sdk, netvars) ignore --tier.


Part of source2rosetta · AGPL-3.0.