Skip to content

Try FText::ToString by symbol first, and record why it does not save us - #89

Merged
rouges78 merged 1 commit into
mainfrom
claude/ftext-symbol-resolution
Aug 21, 2026
Merged

Try FText::ToString by symbol first, and record why it does not save us#89
rouges78 merged 1 commit into
mainfrom
claude/ftext-symbol-resolution

Conversation

@rouges78

Copy link
Copy Markdown
Owner

A symbol is deterministic: it is either there and correct, or absent. That is strictly better than a byte signature, so it now runs first, with the pattern scan as fallback.

It does not fix Father's Day, and it cannot. Symbol resolution needs the engine in DLLs, and a monolithic Shipping build — the commercial norm — has no UE symbols at all. That is measured, not assumed:

exports in the exe 312, every one a driver hint (NvOptimusEnablement, ags*)
UE symbols none
PDB named in the header, not shipped
DLLs beside the exe turbojpeg.dll only
modules interrogated at runtime 142, zero hits

So the value here is for non-monolithic and editor builds, plus a failure that now says what it did. Commercial games still fall through to the pattern.

The module count is not decoration

Without it, a zero cannot separate "looked everywhere, not there" from "the snapshot failed and I looked at nothing" — two diagnoses leading to two different investigations. It is the same trap as the connesso via IPC line recorded in #88: reads like success, is half the chain.

The check that confirms the enumeration actually works: compare against (Get-Process ...).Modules.Count. 142 against 142.

Why the ambiguity is not fixable by a longer signature

The four candidates are measured and written into the log — four distinct functions sharing a prologue, diverging at byte 13, inside the 17 the signature covers:

# RVA after the prologue
1 0x1CADD20 call [rax+0x28], returns rax+0x68
2 0x1D04720 call [rax+0x48], returns [rbx+0x60] (int32)
3 0x1D047B0 same as #2 but [rbx+0x64] — twin accessors
4 0x3074940 mov rcx,[rax+0x90], calls through r8

Lengthening the signature means retuning it against the four games where it is already unique — with no ground truth to say which candidate is even the right one, because there are no symbols here to compare against. Guessing is exactly what this source refuses to do, and refusing remains correct: a wrong hook in a shipping game is a crash or silent heap corruption.

The entry names the promising direction instead: anchoring on a string reference — find a known datum in .rdata and walk back to the code that uses it — which does not depend on how the compiler laid out a prologue.

Verification

Re-ran on Father's Day with real injection: symbol path exercised (142 modules, no hit), pattern still correctly refuses, GDI fallback activates, game responsive, no regression. Both architectures rebuilt.

🤖 Generated with Claude Code

A symbol is deterministic: it is either there and correct, or absent.
That is strictly better than a byte signature, so it now runs first, and
the pattern scan stays as the fallback. ResolveFTextToStringBySymbol
looks for ?ToString@FText@@QEBAAEBVFString@@xz across every loaded
module via Toolhelp.

It does not fix Father's Day, and it cannot: symbol resolution needs the
engine in DLLs, and a monolithic Shipping build has no UE symbols at all.
Measured rather than assumed — the exe carries 312 exports and every one
is a driver hint (NvOptimusEnablement, ags*), the PDB is named in the
header but not shipped, and the only DLL beside it is turbojpeg. At
runtime the resolver interrogates 142 modules and hits nothing.

That module count is logged on purpose. Without it, a zero cannot
separate "looked everywhere, not there" from "the snapshot failed and I
looked at nothing" — the same trap as the "connesso via IPC" line, which
reads like success and is half the chain. The check that confirms it is
comparing against (Get-Process ...).Modules.Count: 142 against 142.

So the value here is for non-monolithic and editor builds, plus a
failure that now says what it did. Commercial games still fall through
to the pattern.

The four ambiguous candidates are measured and written down: four
distinct functions sharing a prologue, diverging at byte 13 — inside the
17 the signature covers. Lengthening the signature would mean retuning it
against the four games where it is already unique, with no ground truth
to say which candidate is even the right one. The entry names the
promising direction instead: anchoring on a string reference rather than
a prologue.

Verified on the real game: no regression, fallback chain intact, game
responsive, both architectures rebuilt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@rouges78
rouges78 merged commit 124fc90 into main Aug 21, 2026
7 checks passed
@rouges78
rouges78 deleted the claude/ftext-symbol-resolution branch August 21, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant