docs(gfx): settle render drift as native cmd-buffer op 0x215 (Ghidra), align docs
Read op 0x215's real handler FUN_0042a0b0 (resolved via the dispatch table ctx[0x26c93+op]; Kelebek's 0x421160 is VA-drift). It writes cmd-type 5 into the current gfx-object record and returns a std::map::find over an engine-internal registry populated by sibling gfx ops (0x1a2 hash insert). The return is native command-buffer state, not the VM global bank -> seeding story-state cannot fix the drift. Verdict: (b) a genuine native op, NOT (a) state-divergence. Reconcile the previously contradictory drift accounts onto one canonical home (engine-re.md op 0x215), with opcodes.toml carrying the opcode-level semantics and phase-a-slice-plan / tools-reference / frida README corrected to point at it instead of repeating the disproven state-divergence conclusion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -136,6 +136,49 @@ decision→scene (scenes are just `SCxxxx.BIN` records loaded by their SYS4INI i
|
||||
|
||||
---
|
||||
|
||||
### op `0x215` (`query-gfx-object?`) is a native command-buffer op — settles the render drift as (b) (2026-07-07)
|
||||
|
||||
**This is the canonical account of the background/sprite "drift" bug** (background pinned off-centre /
|
||||
bottom-right, rest grey — `Screenshot 2026-07-06 211353.png`). It supersedes the earlier "drift =
|
||||
state-divergence, seed state and it's fixed" conclusion in `docs/phase-a-slice-plan.md` and the status
|
||||
memory, which are corrected to point here.
|
||||
|
||||
Resolved via the dispatch table (`ctx[0x26c93 + 0x215]`): the registration routine `FUN_00413860` stores
|
||||
`[ESI + 0x9baa0] = 0x42a0b0`, so op `0x215`'s **real handler is `FUN_0042a0b0`**. (Kelebek's `0x421160` is
|
||||
VA-drift — it lands inside the unrelated `FUN_00421090`. Same lesson as `0x1a2`: never trust a Kelebek raw VA.)
|
||||
|
||||
`FUN_0042a0b0(ctx)` does exactly two things:
|
||||
1. **`*(ctx + 0x53d88 + ctx[0x53d14]*0x78) = 5`** — writes **cmd-type 5** into the *current* gfx-object
|
||||
record. A **command-buffer registration** side-effect, directly parallel to op `0x1a2` (`FUN_0042d360`)
|
||||
writing cmd-type 3. So `0x215` is part of the gfx command-buffer subsystem, not a pure query.
|
||||
2. **`out = FUN_0047f280(FUN_0041b940(2))`** — `FUN_0041b940(2)` fetches operand 2 (the bytecode handle
|
||||
key); `FUN_0047f280` is a **`std::map::find`** over an engine-internal associative registry, returning
|
||||
the mapped value or **`0xffffffff` (not-found)**; `FUN_00425fb0(1, out)` writes it to operand 1. That
|
||||
registry is **populated by sibling gfx ops** — op `0x1a2`'s handler builds a `"%c%8.8x"` key and calls
|
||||
`FUN_0042cf70`, an open-addressing hash **insert** into the same kind of store.
|
||||
|
||||
**(a) vs (b) — the verdict is (b).** The value `0x215` returns is **native command-buffer state**: "has a
|
||||
gfx object already been registered under this handle?" (`≥0` = existing → use its slot; `-1` = new). That
|
||||
state lives in the engine's own registry, maintained by the gfx ops, **not in the VM global bank**. So
|
||||
**seeding story-state globals cannot reproduce it** — the drift is *not* the Phase-B state-divergence
|
||||
problem. Stubbing `0x215` returns a constant → `label_12649`'s slot-select always takes one branch → every
|
||||
draw collapses onto slot 0 → the anchor-preserve math measures foreign-sized textures → cumulative drift.
|
||||
|
||||
**Why the prior "state-divergence" conclusion was wrong.** It was grounded in `capture_gfx_objects.py`,
|
||||
which polled the object-*record* array (`[esi+0x53d64]`) at ~2/s and saw only 3 persistent UI objects, "0
|
||||
CG objects." But (i) the branch is driven by the **map lookup** (a different structure the poll never
|
||||
observed), and (ii) command-buffer records are **transient** — a 2/s poll can't prove CG records weren't
|
||||
used. Absence in that capture ≠ absence of the native path.
|
||||
|
||||
**The fix is tractable and Frida-free.** (b) does *not* mean an opaque native state machine. The subsystem
|
||||
is a **modelable data structure**: an object-record array (slot / geometry / cmd-type per object) plus a
|
||||
handle→object registry (a hash map). The gfx ops are inserts/queries/writes against these, and the inserts
|
||||
are **bytecode-driven** — so a faithful host-side model, with the gfx ops (`0x1a2`, `0x215`, and the
|
||||
`0x212–0x21a` family) *executed* instead of stubbed, rebuilds the state from the same scripts. The opcode-
|
||||
level summary lives in `vm-map/opcodes.toml` op `0x215`.
|
||||
|
||||
---
|
||||
|
||||
## Native walls backlog (targets for this loop)
|
||||
|
||||
- ~~**call-script dispatch**~~ — **SOLVED** (above): `call-script <id>` = raw SYS4INI file index.
|
||||
@@ -144,4 +187,8 @@ decision→scene (scenes are just `SCxxxx.BIN` records loaded by their SYS4INI i
|
||||
value is turned into a scene *id* (a caller of SCJUMP; re-aimed away from `u00428010`).
|
||||
- **op `0x60`** (`u0041A270`) — the rand-like value gating 1732/1755 SCJUMP decisions.
|
||||
- **gfx command-buffer** — the `0x212–0x21a` positioned-object subsystem (`scjump`-unrelated; the
|
||||
rendering drift).
|
||||
rendering drift). **Understood, not unidentified** (see the op `0x215` finding above): it's a native
|
||||
record array + a handle→object hash registry that the gfx ops insert/query/write. The drift verdict is
|
||||
settled — (b), a genuine native op, not state-divergence. Remaining work is *implementation*, not RE:
|
||||
model the registry + record array host-side and execute the gfx ops (`0x1a2`/`0x215`/`0x212–0x21a`)
|
||||
instead of stubbing — static, Frida-free.
|
||||
|
||||
@@ -110,9 +110,9 @@ This also names the whole call graph statically (build/callscript-names.json).
|
||||
- **evidence:** SC0000 label_12649: set-texture(resId,slot) then 0x208(slot)->w,h feeds w/2 horizontal-center + foot-anchor subtraction into draw-texture dst; stubbing yields 0x0 sizes / off-center draws
|
||||
|
||||
### 0x215 `query-gfx-object?` (u00421160, argc 2)
|
||||
- **summary:** 0x215 (out)(handle_id) — queries the native graphics-object manager by element handle-id (the value in 0x62455[idx], often +1/+2 for a sub-element); writes the object's slot/status into `out`, sign-tested (gre/lt 0) to drive label_12649's slot-select branch and set the working slot G[0x62452]. KEYSTONE for per-object slot selection — stubbing it collapses every draw onto slot 0, so the anchor-preserve geometry reads foreign-sized textures → cumulative bg/sprite drift (see docs/phase-a-slice-plan.md A2b-Geometry). Reads native object-manager state (NOT VM-computable). Exact return semantics: RE via unicorn (native handler @0x421160).
|
||||
- **grounding:** source=investigation, confidence=med
|
||||
- **evidence:** SC0000 label_12649 (0x12670) + label_123ef (0x12419/0x12450): called with 0x62455[idx] handle-ids (±offset); result gre/lt 0 branches slot-select and feeds ui-elem?(0x1f7)/set-texture slot. Record table 0x3239 (label_125bd @0x0050f) assigns per-object slots 4..13. Handles are the 0xcf08/0xe678/0xd6d8 element-id family.
|
||||
- **summary:** 0x215 (out)(handle_id) — native graphics command-buffer op. Real handler FUN_0042a0b0 (Ghidra-resolved via the dispatch table ctx[0x26c93+op]; Kelebek's 0x421160 is VA-drift, lands in an unrelated fn). Does TWO things: (1) writes cmd-type 5 into the CURRENT gfx-object record `[ctx+0x53d88 + ctx[0x53d14]*0x78]` (a command-buffer registration, parallel to op 0x1a2→type 3); (2) returns `out = map.find(handle_id)` over an engine-internal associative registry (found value, else 0xffffffff=not-found), sign-tested (gre/lt 0) to drive label_12649's slot-select branch + set working slot G[0x62452]. So `out` is NATIVE COMMAND-BUFFER STATE (the registry is populated by sibling gfx ops — op 0x1a2→FUN_0042cf70 is the hash insert), NOT the VM global bank → seeding story-state CANNOT reproduce it. Stubbed → constant return → every draw collapses to slot 0 → anchor-preserve reads foreign-sized textures → the cumulative bg/sprite drift. SETTLES the drift as (b) a genuine native op, NOT (a) state-divergence. Faithful fix = model the gfx command-buffer (record array + handle→object registry) and run the gfx ops instead of stubbing — static/Frida-free (handlers now readable; inserts are bytecode-driven). Full decode + verdict: docs/engine-re.md (op 0x215 section).
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra: real handler FUN_0042a0b0 = {*(ctx+0x53d88+ctx[0x53d14]*0x78)=5; out=FUN_0047f280(FUN_0041b940(2))}. FUN_0047f280 = std::map::find (returns mapped value or 0xffffffff); FUN_0041b940(2) = operand-fetch of operand 2 (the handle key); FUN_00425fb0(1,val) = operand-write to `out`. Registry populated by op 0x1a2 handler FUN_0042d360 → FUN_0042cf70 (open-addressing hash insert). Bytecode sites: SC0000 label_12649 (0x12670) + label_123ef (0x12419/0x12450), handle-ids from 0x62455[idx] (±offset); result gre/lt 0 branches slot-select. Record table 0x3239 (label_125bd @0x0050f) assigns per-object slots 4..13.
|
||||
|
||||
### 0x217 `gfx-geom?` (u004211E0, argc 4)
|
||||
- **summary:** 4 global-ints; part of a 0x217/0x218/0x21a geometry chain
|
||||
|
||||
@@ -273,28 +273,28 @@ measured `0×0`, and the anchor-preserve math (`base' = center − (w_new/2, h_n
|
||||
corruption. Fix: seed `_slotDims[0] = (800,600)` (and record `create-texture(w,h)` dims) so the first CG's
|
||||
anchor stays an identity. This is the faithful stand-in for the skipped boot-time primary-surface creation.
|
||||
|
||||
**Post-opening bg/sprite drift — RESOLVED as a STATE-DIVERGENCE artifact, NOT a missing native op
|
||||
(2026-07-06/07).** Symptom: everything blits through slot 0 as an immediate-mode canvas; the anchor-preserve
|
||||
base globals **accumulate drift** across differently-sized textures (`BG030A→(300,500)`, next→`(450,100)`,
|
||||
→`(800,350)`… marching bottom-right). We reverse-engineered the whole chain (systematic-debugging):
|
||||
1. Root cause traced to **`0x215` = native graphics-object query** (opcodes.toml `query-gfx-object?`), which we
|
||||
stub → `label_12649` takes the wrong branch → all draws collapse onto slot 0 → anchor-preserve reads foreign
|
||||
textures → drift.
|
||||
2. **Engine now statically analyzable (major, general unlock):** `SYS4AB.BIN` = `XOR-0xFF(AGE.EXE)` (dead end),
|
||||
but `AGE.EXE` unpacks **in-place at 0x400000** in the live process → `tools/frida/dump_engine.py` →
|
||||
`build/engine-dump/` (validated via AGF-decoder landmark; interpreter confirmed to run from the module, so
|
||||
handlers are hookable). Handler ABI + object-record layout (`[esi+0x53d64]`, 120B/rec, cmd-type at rec+0x24)
|
||||
decoded. See `docs/vm-mapping-plan.md` appendix + `tools/frida/README.md`.
|
||||
3. **Live capture verdict (the resolution):** `tools/frida/capture_gfx_objects.py` polled the object-record
|
||||
array through the **real** opening — it held only **3 persistent UI objects, ZERO CG objects**. So the real
|
||||
game does **not** draw the opening CGs via the `0x212–0x21a` positioned-object path our headless VM uses;
|
||||
with proper state it takes a different (direct) branch that we already render correctly. **⇒ the drift is
|
||||
downstream of our unseeded headless VM taking `label_12649`'s else-branch (compute-from-drifting-base) where
|
||||
the real game hits the if-branch (stored/record geometry). The fix is the Phase B state/choices-flow work,
|
||||
not a separate native-op subsystem.** Seeding real per-scene/object state makes `label_12649` branch right.
|
||||
Fades/alpha (`AE*`, `0x202/0x203`) + green chromakey + true multi-surface remain deferred; the compositor is
|
||||
built to accept alpha later. The full-screen opening path is correct and unaffected. **Native gfx-op modeling
|
||||
is only needed for scenes that genuinely use runtime-positioned sprites — revisit later with the dump in hand.**
|
||||
**Post-opening bg/sprite drift — root cause SETTLED as (b) a native command-buffer op via Ghidra
|
||||
(2026-07-07). ⚠ Corrects an earlier wrong "state-divergence" verdict here.** Symptom (screenshot
|
||||
`Screenshot 2026-07-06 211353.png`): everything blits through slot 0 as an immediate-mode canvas; the
|
||||
anchor-preserve base globals **accumulate drift** across differently-sized textures (`BG030A→(300,500)`,
|
||||
next→`(450,100)`, →`(800,350)`… marching bottom-right; the background ends up pinned off-centre / bottom-right
|
||||
with the rest of the screen grey). Root cause = the stubbed native op **`0x215`** collapsing every draw onto
|
||||
slot 0 (its return drives `label_12649`'s slot-select).
|
||||
|
||||
**The canonical decode + verdict now lives in `docs/engine-re.md` (op `0x215` section)** — don't duplicate it
|
||||
here. In brief: `0x215`'s real handler `FUN_0042a0b0` (Ghidra) writes cmd-type 5 into the current gfx-object
|
||||
record and returns a **`std::map::find`** over an engine-internal command-buffer registry (populated by sibling
|
||||
gfx ops like `0x1a2`). That return is **native command-buffer state, not the VM global bank** → seeding
|
||||
story-state **cannot** fix it. So this is **(b) a genuine native op**, *not* (a) the Phase-B state-divergence
|
||||
problem. The prior conclusion in this doc — grounded in a 2/s `capture_gfx_objects.py` poll of the object-*record*
|
||||
array — was wrong: it observed the wrong structure (not the lookup map) and can't rule out transient records.
|
||||
|
||||
**Fix (Frida-free, implementation not RE):** model the gfx command-buffer host-side (record array +
|
||||
handle→object hash registry) and *execute* the gfx ops (`0x1a2`/`0x215`/`0x212–0x21a`) instead of stubbing;
|
||||
the inserts are bytecode-driven, so the state rebuilds from the same scripts. Fades/alpha (`AE*`,
|
||||
`0x202/0x203`) + green chromakey + true multi-surface remain deferred; the compositor is built to accept alpha
|
||||
later. The full-screen event-CG opening path (`EV052*` at `(0,0)`) is correct and unaffected — the drift is
|
||||
specifically the `BG*`/sprite draws routed through `label_12649`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ texture ops (no GPU context) — run windowed for real scenes. User args (after
|
||||
| `tools/frida/find_global_by_sequence.py` | Runtime-global RE (SHELVED): differential resId value-scan + stability filter. Finds stack proxies; proved `G[0x62424]` is a transient arg-register. | `py -3.11 -u -X utf8 tools/frida/find_global_by_sequence.py [pid]` | running game + index → stdout |
|
||||
| `tools/frida/dump_engine.py` | ★ **Dump the UNPACKED engine code** from the live process for offline static RE (native handlers). `AGE.EXE` unpacks in-place at `0x400000`; Kelebek VAs map `VA−0x400000` = file-off. Validated via the AGF-decoder landmark `+0x74f1f`. | `py -3.11 -u -X utf8 tools/frida/dump_engine.py [pid]` | running game → `build/engine-dump/{manifest.json,range_<base>.bin}` |
|
||||
| `tools/frida/probe_handlers.py` | Probe which region the interpreter executes from (module vs heap). Confirmed: **operand-fetch `+0x1b940` fires ~8500/s ⇒ interpreter runs from the module `0x400000`** (handlers hookable by dump address). | `py -3.11 -u -X utf8 tools/frida/probe_handlers.py [pid]` | running game → stdout (per-hook fire counts) |
|
||||
| `tools/frida/capture_gfx_objects.py` | Capture the native gfx object-manager state: grab engine ctx (`esi` via operand-fetch `ecx`), poll the object-record array `[esi+0x53d64]` (20×120B; `field[0]=0xffffffff`=free, cmd-type at rec+0x24). **Finding: the real opening uses ZERO CG records ⇒ the bg/sprite drift is a state-divergence artifact, not a missing op.** | `py -3.11 -u -X utf8 tools/frida/capture_gfx_objects.py [pid] [secs]` | running game → `build/gfx-objects.jsonl` |
|
||||
| `tools/frida/capture_gfx_objects.py` | Capture the native gfx object-manager state: grab engine ctx (`esi` via operand-fetch `ecx`), poll the object-record array `[esi+0x53d64]` (20×120B; `field[0]=0xffffffff`=free, cmd-type at rec+0x24). **⚠ Its "0 CG records ⇒ drift is state-divergence" reading was DISPROVEN** (Ghidra: op 0x215 read settles the drift as a native command-buffer op — `docs/engine-re.md`; the poll observed the record array, not the lookup map that drives the branch, and cmd-buffer records are transient). Kept as a runtime-observation tool. | `py -3.11 -u -X utf8 tools/frida/capture_gfx_objects.py [pid] [secs]` | running game → `build/gfx-objects.jsonl` |
|
||||
|
||||
*(Static disassembly of `build/engine-dump/range_00400000.bin` uses **capstone** — `py -3.11 -m pip install capstone`; VA `X` → file offset `X−0x400000`.)*
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ INFERRED: dict[int, dict] = {
|
||||
0x1ff: dict(name='draw?', category='draw', noop=False, confidence='low', source='inference', summary='4 args (global+imms); follows 0x217, then call'),
|
||||
0x202: dict(name='draw-blit?', category='draw', noop=False, confidence='med', source='inference', summary='5 args (coords/sizes); preceded by coord arithmetic, near draw ops'),
|
||||
0x203: dict(name='draw?', category='draw', noop=False, confidence='med', source='inference', summary='4 args; chains with 0x202/draw-texture'),
|
||||
0x215: dict(name='query-gfx-object?', category='draw', noop=False, confidence='med', source='investigation', summary="0x215 (out)(handle_id) — queries the native graphics-object manager by element handle-id (the value in 0x62455[idx], often +1/+2 for a sub-element); writes the object's slot/status into `out`, sign-tested (gre/lt 0) to drive label_12649's slot-select branch and set the working slot G[0x62452]. KEYSTONE for per-object slot selection — stubbing it collapses every draw onto slot 0, so the anchor-preserve geometry reads foreign-sized textures → cumulative bg/sprite drift (see docs/phase-a-slice-plan.md A2b-Geometry). Reads native object-manager state (NOT VM-computable). Exact return semantics: RE via unicorn (native handler @0x421160)."),
|
||||
0x215: dict(name='query-gfx-object?', category='draw', noop=False, confidence='high', source='investigation', summary="0x215 (out)(handle_id) — native graphics command-buffer op. Real handler FUN_0042a0b0 (Ghidra-resolved via the dispatch table ctx[0x26c93+op]; Kelebek's 0x421160 is VA-drift, lands in an unrelated fn). Does TWO things: (1) writes cmd-type 5 into the CURRENT gfx-object record `[ctx+0x53d88 + ctx[0x53d14]*0x78]` (a command-buffer registration, parallel to op 0x1a2→type 3); (2) returns `out = map.find(handle_id)` over an engine-internal associative registry (found value, else 0xffffffff=not-found), sign-tested (gre/lt 0) to drive label_12649's slot-select branch + set working slot G[0x62452]. So `out` is NATIVE COMMAND-BUFFER STATE (the registry is populated by sibling gfx ops — op 0x1a2→FUN_0042cf70 is the hash insert), NOT the VM global bank → seeding story-state CANNOT reproduce it. Stubbed → constant return → every draw collapses to slot 0 → anchor-preserve reads foreign-sized textures → the cumulative bg/sprite drift. SETTLES the drift as (b) a genuine native op, NOT (a) state-divergence. Faithful fix = model the gfx command-buffer (record array + handle→object registry) and run the gfx ops instead of stubbing — static/Frida-free (handlers now readable; inserts are bytecode-driven). Full decode + verdict: docs/engine-re.md (op 0x215 section)."),
|
||||
0x217: dict(name='gfx-geom?', category='draw', noop=False, confidence='low', source='inference', summary='4 global-ints; part of a 0x217/0x218/0x21a geometry chain'),
|
||||
0x218: dict(name='gfx-geom?', category='draw', noop=False, confidence='low', source='inference', summary='4 global-ints; chains with 0x21a/0x217'),
|
||||
0x21a: dict(name='gfx-geom?', category='draw', noop=False, confidence='low', source='inference', summary='4 global-ints; chains with 0x218/0x217'),
|
||||
|
||||
@@ -28,7 +28,8 @@ Prereq: `py -3.11 -m pip install frida` (core only — `frida-tools` CLI is not
|
||||
region (~30 MB, nonstable base). Attach → it enumerates ranges, dumps the module image + every r-x
|
||||
range ≥ 1 MB (chunked) → `build/engine-dump/{manifest.json,range_<base>.bin}`, and prints the
|
||||
landmark bytes at `AGE.EXE+0x74f1f` to validate. Then disassemble (capstone) and locate a handler
|
||||
(e.g. `0x215` @ Kelebek VA `0x421160`) via the opcode dispatch table. Attach by pid.
|
||||
(e.g. `0x215` → real handler `0x42a0b0`, resolved via the opcode dispatch table — Kelebek's `0x421160`
|
||||
is VA-drift, an unrelated fn). Attach by pid.
|
||||
- **`capture_load_order.py`** — ★ the working asset-resolution capture. Hooks `ReadFile` on
|
||||
`DATA2.ALF`; each asset load starts with header reads **at its exact archive offset**, so exact-start
|
||||
reads give the clean per-asset **load order** (→ names via `build/asset-index.json`). `--analyze`
|
||||
|
||||
@@ -5249,12 +5249,12 @@ abi_source = "kelebek+decode-validated"
|
||||
[opcode.semantics]
|
||||
name = "query-gfx-object?"
|
||||
category = "draw"
|
||||
summary = "0x215 (out)(handle_id) — queries the native graphics-object manager by element handle-id (the value in 0x62455[idx], often +1/+2 for a sub-element); writes the object's slot/status into `out`, sign-tested (gre/lt 0) to drive label_12649's slot-select branch and set the working slot G[0x62452]. KEYSTONE for per-object slot selection — stubbing it collapses every draw onto slot 0, so the anchor-preserve geometry reads foreign-sized textures → cumulative bg/sprite drift (see docs/phase-a-slice-plan.md A2b-Geometry). Reads native object-manager state (NOT VM-computable). Exact return semantics: RE via unicorn (native handler @0x421160)."
|
||||
summary = "0x215 (out)(handle_id) — native graphics command-buffer op. Real handler FUN_0042a0b0 (Ghidra-resolved via the dispatch table ctx[0x26c93+op]; Kelebek's 0x421160 is VA-drift, lands in an unrelated fn). Does TWO things: (1) writes cmd-type 5 into the CURRENT gfx-object record `[ctx+0x53d88 + ctx[0x53d14]*0x78]` (a command-buffer registration, parallel to op 0x1a2→type 3); (2) returns `out = map.find(handle_id)` over an engine-internal associative registry (found value, else 0xffffffff=not-found), sign-tested (gre/lt 0) to drive label_12649's slot-select branch + set working slot G[0x62452]. So `out` is NATIVE COMMAND-BUFFER STATE (the registry is populated by sibling gfx ops — op 0x1a2→FUN_0042cf70 is the hash insert), NOT the VM global bank → seeding story-state CANNOT reproduce it. Stubbed → constant return → every draw collapses to slot 0 → anchor-preserve reads foreign-sized textures → the cumulative bg/sprite drift. SETTLES the drift as (b) a genuine native op, NOT (a) state-divergence. Faithful fix = model the gfx command-buffer (record array + handle→object registry) and run the gfx ops instead of stubbing — static/Frida-free (handlers now readable; inserts are bytecode-driven). Full decode + verdict: docs/engine-re.md (op 0x215 section)."
|
||||
noop_headless = false
|
||||
source = "investigation"
|
||||
confidence = "med"
|
||||
confidence = "high"
|
||||
depends_on = []
|
||||
evidence = "SC0000 label_12649 (0x12670) + label_123ef (0x12419/0x12450): called with 0x62455[idx] handle-ids (±offset); result gre/lt 0 branches slot-select and feeds ui-elem?(0x1f7)/set-texture slot. Record table 0x3239 (label_125bd @0x0050f) assigns per-object slots 4..13. Handles are the 0xcf08/0xe678/0xd6d8 element-id family."
|
||||
evidence = "Ghidra: real handler FUN_0042a0b0 = {*(ctx+0x53d88+ctx[0x53d14]*0x78)=5; out=FUN_0047f280(FUN_0041b940(2))}. FUN_0047f280 = std::map::find (returns mapped value or 0xffffffff); FUN_0041b940(2) = operand-fetch of operand 2 (the handle key); FUN_00425fb0(1,val) = operand-write to `out`. Registry populated by op 0x1a2 handler FUN_0042d360 → FUN_0042cf70 (open-addressing hash insert). Bytecode sites: SC0000 label_12649 (0x12670) + label_123ef (0x12419/0x12450), handle-ids from 0x62455[idx] (±offset); result gre/lt 0 branches slot-select. Record table 0x3239 (label_125bd @0x0050f) assigns per-object slots 4..13."
|
||||
|
||||
[[opcode.semantics.args]]
|
||||
i = 1
|
||||
|
||||
Reference in New Issue
Block a user