`scripts/dll-surface.py` answers "what might this DLL publish". For
nampower.dll there is a better question with an exact answer: the names
it registers sit contiguously in the string pool, in registration order,
bracketed by the DLL's own log lines — `Registering Custom Lua
functions` immediately before the first, `Registering Glue Lua
functions` before the last two. That is 60 functions plus 2 glue ones,
with no inference at all, where the pointer filter gives a candidate
list a reader still has to judge. The event table (59 names) and the
CVar names sit the same way, just before it.
A compiler emits one string literal per `lua_register(L, "<name>", fn)`
and pools them in source order, so one registration function's names
land in one run, and the log line that opens it is the literal printed
immediately before the first call. Look for the log line, then read
forward until the strings stop looking like identifiers.
Two things fell out of the same pool that corrected code already
written:
- The 44 `Usage:` strings are the argument contract, printed verbatim
when a call's arguments are wrong. `GetSpellRangeData(rangeIndex)`
takes a SpellRange.dbc row index, not a spell id, which this doc had
wrong; `GetSpellModifiers(spellId, modifierType)` enumerates all 26
modifier constants.
- `GetItemIconTexture(displayInfoId)` takes an ItemDisplayInfo row id,
the exact sibling of `GetSpellIconTexture(spellIconId)`. Ours took an
*item* id and resolved the display id itself, which is a different
function: upstream's addon layer keeps that hop in a wrapper of its
own (`NampowerAPI.lua:2648` is the pass-through, `:2665` is
`GetItemIconTextureByItemId`). So a caller handing us a real display
id got "Item not found in DBCache", and one handing an item id got an
icon the real DLL would not have answered. Fixed, and the test now
pins that an item id answers nothing — it populates the item cache on
purpose so the wrong reading would be visible if it came back.
With the table read, the surface is no longer a count of what a
heuristic flagged: 62 names, each in exactly one bucket (32 built, 8
withheld pending a decision that is not mine, 4 not ours to add, 18 to
build), with the `comm -23` that reproduces it. The two at the head of
the last bucket are the most-called nampower functions in the whole
corpus, and they are coupled: SuperCleveRoidMacros registers
SPELL_CAST_EVENT gated on `GetCurrentCastingInfo` existing, so defining
the function without firing the event would move it from never
registered to registered and silent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn