Symbol rename workflow#
Repeatable process for renaming Ghidra FUN_* / DAT_* / LAB_* symbols in
src/LORDS2.EXE.c. See symbol-map.md
for the canonical rename table and AGENTS.md for naming rules.
Batch size#
Work in batches of 5–20 symbols. Smaller batches are easier to review; larger batches increase the risk of misnamed clusters.
Steps#
1. Pick a cluster#
Choose functions by call graph or subsystem, not at random:
- Callees of an already-named
lotr2_*function - Shared string tables (
.wav,.smk,.256, dialog resources) - Win32 / COM API fingerprints (
DialogBoxParamA,DirectDrawCreate, etc.) - Address locality (e.g.
0x431xxx→ audio)
Do not rename isolated leaf functions until their parent cluster is understood.
2. Gather evidence#
Record at least two of the following before assigning a name:
- Imported API or COM vtable offset
- String literal or file extension
- Caller/callee link to a named
lotr2_*symbol - Global read/write tied to a recovered struct (
docs/structs/)
For networking symbols, follow
.cursor/skills/protocol-reverse/SKILL.md
before naming packet handlers.
3. Document in symbol-map.md#
Add a row to symbol-map.md:
| `FUN_00431b10` | 0x431b10 | `lotr2_audio_play_wav` | Checks lotr2_audio_lpds; loads WAV; IDirectSound buffer create/play |
Update the Progress header counts after each batch.
4. Extend the apply script#
Add (old, new, kind) tuples to RENAMES in
tools/ghidra/apply_anchor_renames.py.
Keep entries grouped by subsystem with comments. Kinds: fun, dat, lab.
The script must stay in sync with symbol-map.md — it is the single apply path.
5. Apply and verify#
python3 tools/ghidra/apply_anchor_renames.py
Verify each batch:
# Old symbol should appear only in provenance comments
grep 'FUN_00431b10' src/LORDS2.EXE.c
# Provenance present at definition
grep 'was FUN_00431b10' src/LORDS2.EXE.c
The script replaces all occurrences (calls, thunks, globals) and injects
// was FUN_… @ 0x… above definitions. Provenance comment lines are never
modified by the replace pass (see safe_replace in the script).
To find the next cluster from a named anchor:
python3 tools/ghidra/list_unnamed_callees.py lotr2_game_sim_tick
6. Update MODULE.md#
Add or update Key entry points in the relevant
docs/architecture/*/MODULE.md file. Create a new module doc when
a cluster reaches ~10–20 named symbols.
7. Commit#
- Prefix:
src: rename <area> …ordocs(arch): symbol-map <area> pass - One batch per commit when possible
- Note Ghidra re-export impact if addresses may shift
Naming convention#
| Old | New | Example |
|---|---|---|
FUN_00401234 |
lotr2_<area>_<purpose> |
lotr2_audio_play_wav |
DAT_00501234 |
lotr2_<area>_<name> |
lotr2_audio_dsb_playback |
LAB_00401234 |
lotr2_<area>_<purpose> |
lotr2_ui_wndproc |
| MSVC runtime | crt_<purpose> |
crt_output_format |
thunk_FUN_* |
thunk_<newname> |
Automatic via global replace |
Provenance (required on first rename):
// was FUN_00431b10 @ 0x00431b10
undefined4 __cdecl lotr2_audio_play_wav(char *param_1, int param_2, int param_3)
Keep provenance comments until symbols are stable across Ghidra re-exports.
Do not#
- Bulk-rename without evidence
- Refactor decompiled control flow
- Change wire-format layouts without protocol review
Ghidra re-export#
On re-export, use provenance comments to re-apply names. Update
docs/METADATA.md with export date and program hash.
GoG parallel track (Lords2-gog.exe.c)#
Retail src/LORDS2.EXE.c is the canonical name source.
GoG src/Lords2-gog.exe.c uses different VAs; sync
names by subsystem with behavioral matching — not by retail address.
Each phase writes its own report and JSON (see gog-symbol-map.md index). The mapper uses hybrid merge: fingerprint/call-graph matching plus provenance from symbols already applied in the GoG export (UI phase seeds call-graph propagation for later phases).
Map#
python3 tools/ghidra/map_gog_symbols.py --subsystem ui
python3 tools/ghidra/map_gog_symbols.py --subsystem startup
Defaults: docs/architecture/gog-symbol-map-<subsystem>.md and
tools/ghidra/gog-symbol-map-<subsystem>.json.
Startup cluster (--subsystem startup) covers lotr2_game_*, lotr2_asset_*,
lotr2_audio_*, and crt_* — winmain callees plus call-graph/fingerprint
propagation from UI anchors.
Apply#
python3 tools/ghidra/apply_gog_renames.py --subsystem startup
python3 tools/ghidra/apply_gog_renames.py --all # ui → startup → game → gfx → map → combat
# Agent loop (dynamic wake every 3m): tools/ghidra/gog_sync_loop_wake.sh
# Skill: .cursor/skills/gog-sync-loop/SKILL.md — progress: gog-sync-progress.md
Uses shared helpers in tools/ghidra/rename_utils.py.
Provenance records GoG old symbols:
// was FUN_004975d3 @ 0x004975d3
undefined4 lotr2_game_init_phase1(void)
Verify:
grep 'FUN_004975d3' src/Lords2-gog.exe.c # provenance only
grep 'lotr2_game_init_phase1' src/Lords2-gog.exe.c # definition + call sites
grep 'lotr2_winmain' -A40 src/Lords2-gog.exe.c # startup callees named
When several retail symbols alias one GoG stub (empty spin/yield hooks), only one
lotr2_* name is applied per GoG address; see GoG alias notes in
gog-symbol-map-ui.md.
Net (lotr2_net_*) is deferred on GoG until ASIGS/DirectPlay equivalents are mapped.