re: F1 -- traced C_PAD_RINGBUF; the queue naming inference was wrong
Continuing the F1 investigation rather than starting a fresh one. Last iteration left two competing hypotheses open (Keystroke-queue-driven vs polled-state-driven repeat) and flagged C_PAD_RINGBUF's producer as the cheapest thing to trace next -- named but not traced. Traced it this time: C_PAD_DECODER's own constructor (sub_8220B610) allocates C_PAD_RINGBUF (52-byte control struct, 1024-byte backing buffer, confirmed against its own Shift-JIS trace strings -- "C_PAD_RINGBUF initialization" and its allocation-error message). Its update function (sub_8220B8C0) takes the input-manager singleton as a parameter and reads the ring at offsets 12, 36, 40, 44 and 48 -- not just the one button word. Offsets 36-48 are four consecutive fields read together through the same int-to-double conversion an analog axis would use. XamInputGetKeystrokeEx has no field for a stick position, so a structure carrying four axis-shaped fields cannot be a keystroke queue -- it reads as a periodically-refreshed polled-state snapshot. My own prior reading of the "ring buffer" name as implying a queue was the wrong inference; refuted by tracing it, recorded either way per adversarial duty. This shifts the balance toward the second, previously-uncertain hypothesis: the file driver's GetState() was always capable of showing real repeat (no modification needed), and nav_repeat_and_b.py's null result is more likely a sampling artifact of its ~4-5 fps screen-diff detector than a structural driver limit. Revises "what would close it" accordingly -- re-run the existing draw-log position-tracking instrument, gated on the menu properly, before reaching for a driver change. Not found: the actual producer that writes into C_PAD_RINGBUF each frame -- narrowed to "reachable from the input-manager singleton fetch in sub_821A9DC8," not traced to completion. Still no number for issue #1; this narrows the path to one, further than last iteration but not there.
This commit is contained in:
@@ -6583,3 +6583,13 @@ and it competes with a second reading of the evidence that isn't resolved
|
|||||||
either (see the page for both). **Keep authoring against `-1.0` until a
|
either (see the page for both). **Keep authoring against `-1.0` until a
|
||||||
capture backs one of these**, same as before — this narrows why the number
|
capture backs one of these**, same as before — this narrows why the number
|
||||||
is missing, it doesn't supply one.
|
is missing, it doesn't supply one.
|
||||||
|
|
||||||
|
**2026-09-12 update, same page:** traced `C_PAD_RINGBUF` (the structure the
|
||||||
|
game's own pad decoder reads) and it carries four axis-shaped fields
|
||||||
|
alongside its button word — a shape only the **polled** controller state has
|
||||||
|
(analog sticks have no keystroke equivalent), not a keystroke queue as the
|
||||||
|
name alone had suggested. That favours the "the driver was always capable,
|
||||||
|
the coarse screen-diff detector undercounted" reading over the
|
||||||
|
Keystroke/400-100ms one, though neither is confirmed yet and the actual
|
||||||
|
producer still isn't found. Still no number — still don't take one from
|
||||||
|
here.
|
||||||
|
|||||||
@@ -108,44 +108,93 @@ longer hold through this same file driver and saw more than one step.
|
|||||||
|
|
||||||
Both routes converge on the same next step below.
|
Both routes converge on the same next step below.
|
||||||
|
|
||||||
|
## 2026-09-12 update — traced `C_PAD_RINGBUF`, and the naming inference was wrong
|
||||||
|
|
||||||
|
Last iteration's open question was whether `C_PAD_RINGBUF` is fed by the
|
||||||
|
keystroke ring or the polled state, and said its *name* suggested a queue.
|
||||||
|
Traced this time, ⟨image⟩, addresses re-verified against `/xenia-rs/sylpheed.db`
|
||||||
|
(not run against the raw `.pe` this pass — see reach below):
|
||||||
|
|
||||||
|
* `C_PAD_DECODER`'s own constructor (`sub_8220B610`) allocates
|
||||||
|
`C_PAD_RINGBUF` itself — a 52-byte control struct plus a 1024-byte backing
|
||||||
|
buffer (`リングバッファ確保エラー size=%d` / `C_PAD_RINGBUF 初期化`, both
|
||||||
|
read off the disc's own Shift-JIS strings) — and stores the pointer at
|
||||||
|
`this+76`.
|
||||||
|
* `C_PAD_DECODER`'s update (`sub_8220B8C0`) takes the **input-manager
|
||||||
|
singleton** (`sub_824574C0`, already named in
|
||||||
|
[`structures/title-a-press-fault.md`](structures/title-a-press-fault.md))
|
||||||
|
as its third argument, and reads `this+76`'s ring at offsets **12, 36, 40,
|
||||||
|
44 and 48** — not just the one button word `input-pad-read-path.md`'s
|
||||||
|
`sub_8220B8C0` excerpt showed. Offsets 36–48 are four consecutive 32-bit
|
||||||
|
fields, read together, converted through the same int→double stack
|
||||||
|
round-trip a stick axis conversion would use.
|
||||||
|
|
||||||
|
**That is the tell.** `XamInputGetKeystrokeEx` reports discrete digital
|
||||||
|
button edges — it has no field for an analog stick position. A structure
|
||||||
|
that carries four axis-shaped fields alongside a button word cannot be a
|
||||||
|
keystroke queue; it reads as a **periodically-refreshed input snapshot**
|
||||||
|
(buttons and both sticks together), which only the **polled**
|
||||||
|
`XINPUT_GAMEPAD` path has to offer. The "ring buffer" name is the authors'
|
||||||
|
own choice of implementation (a reusable slot, not a growing queue), not
|
||||||
|
evidence of an event stream — my prior reading of the name was the wrong
|
||||||
|
inference, now corrected by tracing it.
|
||||||
|
|
||||||
|
**This favours the second hypothesis in the section above**: `pad.py`'s
|
||||||
|
"longer holds auto-repeat" caution is more likely the real mechanism, and
|
||||||
|
`nav_repeat_and_b.py`'s null result is more likely a **sampling artifact**
|
||||||
|
of its coarse ~4–5 fps screen-diff detector than a structural driver limit.
|
||||||
|
It does not flip the Keystroke/SDL-driver prediction to false outright —
|
||||||
|
a menu could still layer Keystroke-driven navigation on top of a
|
||||||
|
polled-state decoder for other purposes — but the balance of evidence moved.
|
||||||
|
|
||||||
|
**Not found this pass, and still open:** the actual *producer* that writes
|
||||||
|
into `C_PAD_RINGBUF`'s offsets 12/36-48 each frame. `sub_8220B8C0` only
|
||||||
|
*reads* them; nothing in its own 5 856 bytes stores through the chased
|
||||||
|
`this+76` pointer, so some other function holds a reference to the same
|
||||||
|
ring instance. `sub_821A9DC8` (one of `sub_8220B8C0`'s three callers) fetches
|
||||||
|
the input-manager singleton immediately before each call, which is the
|
||||||
|
strongest lead for where to look next, not yet followed to its own producer.
|
||||||
|
|
||||||
## What is NOT established, and is the reach
|
## What is NOT established, and is the reach
|
||||||
|
|
||||||
* Whether `C_PAD_DECODER` (which the human's play-test almost certainly went
|
* The actual producer of `C_PAD_RINGBUF`'s contents, per the update above —
|
||||||
through, and which `pad-decoder-double-tap-not-key-repeat.md` already found
|
narrowed to "somewhere reachable from the input-manager singleton," not
|
||||||
has **no timer** on any direction path) reads its bits from the keystroke
|
found.
|
||||||
queue or from the polled `XINPUT_GAMEPAD` state. `input-pad-read-path.md`
|
|
||||||
names this exact gap: *"which of the two each menu action uses is not
|
|
||||||
decoded."* `C_PAD_RINGBUF`'s name suggests the former (a queue, matching
|
|
||||||
the keystroke ring's own `{ptr, count, capacity}` shape at `sub_82457038`
|
|
||||||
`r31+68`) but that is a naming inference, not a trace.
|
|
||||||
* Whether the menu's consumer of that ring treats a `REPEAT`-flagged
|
* Whether the menu's consumer of that ring treats a `REPEAT`-flagged
|
||||||
keystroke identically to a fresh `KEYDOWN`, or filters it out.
|
keystroke identically to a fresh `KEYDOWN` — now less likely to be the
|
||||||
|
relevant question at all, per the update above, but not ruled out.
|
||||||
* Which `--hid` driver `run-canary` uses by default, i.e. which of these two
|
* Which `--hid` driver `run-canary` uses by default, i.e. which of these two
|
||||||
numbers (none, or 400/100) the human's own play-test actually went through.
|
numbers (none, or 400/100) the human's own play-test actually went through.
|
||||||
|
|
||||||
Any of these could be wrong without changing the one thing this page does
|
Any of these could be wrong without changing the one thing this page does
|
||||||
establish: **the previous "none" result cannot stand as evidence either
|
establish: **the previous "none" result cannot stand as evidence either
|
||||||
way**, because its instrument was built to prevent repeat by design.
|
way**, because its instrument was built to prevent Keystroke repeat by
|
||||||
|
design, and now more plausibly was never testing the path that matters.
|
||||||
|
|
||||||
## What would close it
|
## What would close it
|
||||||
|
|
||||||
Two options, cheapest first:
|
Two options, cheapest first — and the 2026-09-12 update changes which one is
|
||||||
|
cheapest:
|
||||||
|
|
||||||
1. **Read, not run.** Trace `C_PAD_RINGBUF`'s producer (whatever writes
|
1. **Read, not run.** Finish tracing `C_PAD_RINGBUF`'s producer from
|
||||||
`this+12` that `sub_8220B8C0` masks) back to either the keystroke ring or
|
`sub_821A9DC8`'s input-manager fetch forward, to confirm it copies from
|
||||||
the polled state buffer. Settles the mechanism question with no emulator
|
the polled `XINPUT_GAMEPAD` fields rather than the keystroke ring.
|
||||||
time, and turns the 400/100 ms prediction into a decoded fact or rules it
|
Settles the mechanism with no emulator time. Given the axis-shaped fields
|
||||||
out, in either direction.
|
already found, this is now confirmation work, not a coin flip.
|
||||||
2. **Measure.** Add an opt-in repeat mode to `file_input_driver`'s
|
2. **Measure — and the existing driver may already be enough.** If the
|
||||||
`GetKeystroke()` (mirroring the SDL driver's `Waiting`/`Repeating` state
|
producer is polled-state as the update above now favours,
|
||||||
machine and the same two constants, default OFF so every other scripted
|
`file_input_driver`'s `GetState()` needs **no modification**: it already
|
||||||
script keeps its current one-event-per-press behaviour), boot to the main
|
holds a button continuously with no edge suppression. The likelier defect
|
||||||
menu via the established `boot_menu.sh`/`skip_intro.sh` gate, hold a
|
is the *detector*, not the driver — `nav_repeat_and_b.py`'s ~4–5 fps
|
||||||
direction, and read the cursor's position per frame off the draw log —
|
screen-diff undercounts a fast repeat. Re-run with the draw-log
|
||||||
the same instrument `f1-menu-repeat-harness-built-not-answered.md` already
|
position-tracking instrument `f1-menu-repeat-harness-built-not-answered.md`
|
||||||
validated for this exact purpose. Report frame counts at the achieved
|
already built and validated (per-frame cursor position, not a coarse
|
||||||
present rate, per `TEMPORAL-VERIFICATION.md`, not a 400/100 ms guess
|
pixel-diff), gated on the main menu via `boot_menu.sh`/`skip_intro.sh`
|
||||||
dressed as measured.
|
rather than blind sleeps, holding a direction through the plain
|
||||||
|
`--hid=file` driver as-is. Report frame counts at the achieved present
|
||||||
|
rate, per `TEMPORAL-VERIFICATION.md`. The opt-in Keystroke-repeat mode
|
||||||
|
this page originally proposed adding to the file driver is now a
|
||||||
|
fallback for if this comes back null again, not the first thing to try.
|
||||||
|
|
||||||
Not run this iteration — this is the static half, and it is large enough on
|
Not run this iteration — this is the static half, and it is large enough on
|
||||||
its own (a reversal of a standing claim) not to stack a build-and-boot unit
|
its own (a reversal of a standing claim) not to stack a build-and-boot unit
|
||||||
|
|||||||
Reference in New Issue
Block a user