Item 2: retail's portal-space "In Portal Space..." notice is the SpewBox (ECM_UI::SendNotice_DisplayStringInfo(0x1A,...) -> AddTextToScroll(str, 0x1A, 1, 0), hardcoded to the SpewBox per the decomp), not a dedicated centered overlay. PortalWaitNoticeController and its lease are deleted; PortalTunnelPresentation's per-rotation-segment cadence now writes straight into RuntimeCommunicationState.AddText(ClientLocal) -- the SpewBox's own dedupe-at-index-0 handles the repetition exactly as retail's does. Register row AP-184 records the surface fix and the AP-178 scope extension. Items 4+5: /help text was partially fabricated -- the user caught the "/help death" meta-message. Generalized tools/pdb-extract/sweep_weenie_strings.py to decode narrow PStringBase<char> literals (the ClientCommunicationSystem::Help* family's shape) alongside its original UTF-16LE support, then swept every HelpXxxGroup function's exact byte extent against the PDB-paired acclient.exe. 4 of 7 group topics (death/status/text/allegiances) are now complete verbatim listings; the other 3 (channels/chatting/commands) keep an honest UNVERIFIED note citing HelpStupidChannelHack @0x0056f290 (a genuinely undecodable BN-mislabeled-fragment mechanism) instead of the old fabricated sentinel. 7 of ~35 channel one-liners are also now verbatim. ISSUES.md #364 tracks the remainder; RetailCommandHelpTableTests.cs pins every result byte-exact. Item 1: jump-in-air refusal still silent live is NOT reproduced and NOT speculatively fixed. Exhaustive static re-audit found the mechanism correct by construction (single-writer OnWalkable, exactly-once-per-frame Update()/Capture(), no interfering edge-history resets). A live headless repro (new jump-probe bot policy, real ACE connect) was blocked -- probeaccount2 has no character, and the graphical client already owned testaccount this session so the task's own fallback rule forbade using it. Two temporary probes are left behind ACDREAM_PROBE_JUMP=1 (blocked entirely in Headless by the existing multi-session static-state guard -- graphical-only for the next round). Item 3 confirmed fixed, no regression. Item 6 (resize: no diagonal cursors, cannot grow Y from bottom-right) folded into CH6a's existing scope. Full Release suite: 12,267 passed / 4 skipped / 0 failed (up from 12,221/4/0). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| check_exe_pdb.py | ||
| check_function_map.py | ||
| dump_pdb_info.py | ||
| pdb_extract.py | ||
| README.md | ||
| sweep_weenie_strings.py | ||
pdb-extract — pure-Python MSF 7.00 PDB extractor
Reads refs/acclient.pdb (Sept 2013 EoR build, 28 MB) and writes two
grep-friendly JSON sidecars to docs/research/named-retail/:
symbols.json— 18,366 named public function symbols from the PDB's S_PUB32 records. Each entry hasaddress(image VA),name(MSVC-demangledClass::Methodform), andmangled(raw C++ ABI symbol for callers that need exact mangling).types.json— 5,371 unique named struct/class type records from the TPI stream (LF_CLASS / LF_STRUCTURE). Each entry hasname,size(bytes), andkind(classorstruct).
Usage
py tools\pdb-extract\pdb_extract.py refs\acclient.pdb
Runs in <1 second. No external dependencies — uses Python stdlib only.
Schema
symbols.json:
[
{
"address": "0x00594570",
"name": "CEnchantmentRegistry::EnchantAttribute",
"mangled": "?EnchantAttribute@CEnchantmentRegistry@@QBEHKAAK@Z"
},
...
]
types.json:
[
{
"name": "CEnchantmentRegistry",
"size": 32,
"kind": "class"
},
...
]
Workflow integration
The committed JSON sidecars are the named-retail counterpart to the
acclient_2013_pseudo_c.txt text dump. Pseudo-C is for reading
function bodies; symbols.json is for programmatic lookups. Use
jq to query:
# Find a function by exact name
cat docs/research/named-retail/symbols.json | jq '.[] | select(.name == "CEnchantmentRegistry::EnchantAttribute")'
# Find all functions on a class
cat docs/research/named-retail/symbols.json | jq '.[] | select(.name | startswith("CACQualities::"))'
# Reverse lookup by address (e.g. mid-body fix-up)
cat docs/research/named-retail/symbols.json | jq '.[] | select(.address == "0x00594570")'
# Find a type by name
cat docs/research/named-retail/types.json | jq '.[] | select(.name == "Enchantment")'
Address mapping caveat
The PDB is from the Sept 2013 EoR build. Addresses generally match the
binary used to produce our docs/research/decompiled/ Ghidra chunks
within ~0xC00 bytes (different build runs of the same source revision).
When using symbols.json to correct entries in
acclient_function_map.md, match by name, not by raw address.
Implementation notes
The script is a self-contained MSF 7.00 reader. References used:
- LLVM PDB documentation (https://llvm.org/docs/PDB/) — file format spec
- Microsoft
pdbparse(community) — implementation cross-check
Streams consumed:
- 3 (DBI) — parses the header to extract the symbol-record stream index + the optional debug-header sub-stream's section-headers index.
- 9 (section headers) — parses
IMAGE_SECTION_HEADERentries to build a section-base table for VA computation. - 8 (sym record stream) — iterates records, picks
S_PUB32with thePUBSYM_FLAG_CODEbit set, computesVA = section_base + offset. - 2 (TPI) — iterates type records, picks
LF_CLASS/LF_STRUCTUREthat aren't forward-declared, parses size leaf + name.
The MSVC name demangler (_demangle) is best-effort: handles the
common ?Method@Class@Outer@@<sig> patterns, constructors (??0),
and destructors (??1). Returns the mangled string unchanged for
operator overloads (??2, ??3), vtables (??_), and other forms
where a partial demangle would be misleading. Both name (demangled)
and mangled (raw) are emitted in symbols.json so consumers can
choose.
When to regenerate
- Whenever
refs/acclient.pdbis updated (rare). - Whenever
pdb_extract.pyis changed (e.g. better demangler, more type info recovery).
The output JSONs are committed because they're stable + small (~3 MB combined) and grep-faster than re-parsing the PDB on every session.
Future work (out of scope here)
The current types.json only carries name + size. A more ambitious
version would walk LF_FIELDLIST records to recover field names +
offsets + types — giving us a JSON-encoded acclient.h. Not done yet
because acclient.h already exists committed at
docs/research/named-retail/acclient.h. Consider this if a future
panel needs offsetof() at runtime.