Skip to content

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> … or docs(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.