source2rosetta/CONTRIBUTING.md
Kamal Tufekcic a2922b8bad
All checks were successful
CI / fuzz (push) Successful in 1m41s
CI / lint (push) Successful in 16s
CI / test (push) Successful in 22s
initial commit
2026-07-27 10:12:04 +03:00

4.4 KiB

Contributing gamedata

Most functions are derived automatically. When source2rosetta can't locate one — a newly interesting function, or one whose signature drifted past the model — you can contribute its locator directly. A contribution is just a name + how to find it, dated to the build you saw it on. The derive merges it into the catalogue, validates it against a live server, and tracks it forward across future builds like any first-party entry.

Add a contribution

Drop a JSON file into the folder for your game:

mappings/contributions/<game_key>/<anything>.json      csgo  → CS2
                                                        dota  → Dota 2

Every file is a JSON array of entries:

[
  {
    "name": "CCSPlayer_WeaponServices::BumpWeapon",
    "kind": "vtable-offset",
    "value": "27",
    "date": "2026-07-09"
  },
  {
    "name": "CTakeDamageInfo::CTakeDamageInfo",
    "kind": "signature",
    "value": "49 BB ? ? ? ? ? ? ? ? 55",
    "date": "2026-07-09"
  }
]
field meaning
name the function, Class::Method for a virtual, a bare name otherwise.
kind "signature" (a non-virtual, located by bytes) or "vtable-offset" (a virtual, located by slot).
value for a signature, a space-separated byte pattern with ? wildcards; for an offset, the vtable slot index as a string.
date the build you observed it on, YYYY-MM-DD. This anchors the observation so the derive can chain the offset / re-locate the signature forward from there.

All four fields are required. Files are merged in sorted filename order; a malformed entry is skipped with a warning (it never breaks the build), so keep one logical group per file.

What happens to it

  1. Merge — the derive folds your entries into the catalogue for that game (contributions are never written into the historical corpus model — they live only in this folder).
  2. Validateproduce launches a vanilla server and checks your locator against live memory: a signature must resolve to executable code, an offset must land on a real vtable slot, and a representative call must return cleanly. Passing entries ship; failing ones are reported, not silently dropped.
  3. Track forward — because the entry is dated, later builds re-locate it automatically (offset chaining / string-anchor backfill), so one contribution keeps paying off across updates instead of needing a re-submit each patch.

Getting the value

  • vtable-offset — the slot index of a virtual method. Read it off a class's RTTI vtable with any Source-2 class dumper, or from an existing entry for a neighbouring method on the same class.
  • signature — a unique byte pattern at the function's prologue. Keep wildcards (?) on relative offsets / addresses so the pattern survives minor recompiles.

Then let the tool check it for you: add the entry, run produce against the current build (omit --game-dir for a fast offline check), and look for your name in the output. It lands in core if it resolved, or in unresolved with a reason if it did not — every catalogue entry is accounted for in one of the tiers, so a contribution never disappears silently.

Prefer an offset over a signature when the function is virtual — it's RTTI-derived and far more stable across builds.

Show it holds across builds (optional)

A one-build entry is fine; one that has held for hundreds of builds is stronger. For a vtable-offset, backfill chains your slot back through the shipped model's alignment history — no build binaries needed, just the model file:

// mine.json
[{ "name": "CCSPlayer_WeaponServices::BumpWeapon", "class": "CCSPlayer_WeaponServices", "slot": 27 }]
source2rosetta --game cs2 backfill --corpus-model model-cs2.json --names mine.json --out history.json
backfill: 1 names over 0 corpus builds / 343 model builds
  graduates: 1 DEEP first-class (>=50% coverage, >=0.9 consistent)

history.json then lists the slot at every build the class appears in, plus a consistency score. A deep, consistent timeline is a strong signal the entry is solid — it's exactly what promotes a name to first-class regardless of how it was first found. (A signature can be back-filled too, but that half re-locates the anchor string in every raw build, so it needs the full build corpus the maintainers keep — the offset half above works from the published model alone.)