re: the pad word is REMAPPED -- my own bit table was mislabelled throughout

input-pad-read-path.md says of the word the C_PAD_DECODER reads: "There is
no shift and no remap on the way in -- the bit positions are XINPUT's own."
That is wrong. sub_8220D500 rebuilds the word out of XINPUT_GAMEPAD before
anything else sees it, into the game's own numbering.

  bits  0-3   A B X Y
  bits  4-7   left stick  UP DOWN LEFT RIGHT   (+/-20000 of 32767)
  bits  8-11  right stick UP DOWN LEFT RIGHT
  bits 12-15  D-pad       UP DOWN LEFT RIGHT
  bits 16-17  START, BACK
  bits 18-19  LB, RB
  bits 20-21  LT, RT  -- digital, threshold >220 of 255
  bits 22-23  L3, R3

Extracted mechanically from the image, no row typed by hand. The control is
the shape of the result: the 24 assignments land on bits 0..23, each used
exactly once, none repeated. A misdecode does not produce a bijection over a
contiguous range, and coincidence does not put the stick and D-pad
directions in the same order in two aligned nibbles.

So every mask in that page's tables names the wrong button. The 0xE000 x18
site, read there as "B | X | Y", is "D-pad DOWN | LEFT | RIGHT" -- eighteen
sites testing a menu cursor, which is what 18 sites should be.

And its headline negative is REFUTED: "LB and RB are not menu inputs" is
false. They are bound at config fields this+0x70 and this+0x84, LT/RT at
+0x74/+0x80. The negative was searched for 0x0100/0x0200 -- LB and RB in
XINPUT's numbering -- in a word where they live at 0x40000/0x80000. Right
function, right buttons, wrong bit positions, so it could only come back
empty. A negative is only as good as the numbering it was searched in.

Also decodes the ring record: +12 HELD, +16 PRESSED, +20 RELEASED, +28/+32
raw trigger bytes. Edge and level are one struct four bytes apart, which
displaces that page's guess that press-vs-hold was split between GetState
and the XamInputGetKeystrokeEx queue.

The superset claim in that page survives and is untouched: sub_82457038
really does compare every XINPUT_GAMEPAD field, and it really is
XINPUT-layout. This page depends on it.

Not decoded: which output bit means which ACTION, and per-screen sets. 5 of
18 output-bit sites did not resolve to a pad guard, so the output map is a
lower bound -- in particular "START is not tested" is NOT claimed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t
This commit is contained in:
sylph-decoder
2026-09-01 18:33:42 +00:00
parent 0eb5637329
commit 1e388d0176
7 changed files with 377 additions and 1 deletions

View File

@@ -499,6 +499,70 @@ constructed in `sub_8220B610` and released in `sub_821A6470`. A *decoder* betwee
`wButtons` and the menus is where repeat timing, edge detection and remapping
live. That is the next read.
---
## 🔴🔴 2026-09-01 (seventh) — **THE PAD BIT NUMBERING IS NOT XINPUT'S.** I gave you a wrong table
[`../re/input-button-numbering-is-remapped.md`](../re/input-button-numbering-is-remapped.md)
· [remap](../re/data/input-ring-word-remap.txt)
· [output map](../re/data/input-decoder-output-map.txt)
· [record layout](../re/data/input-ring-record-layout.txt)
I told you the game reads `wButtons` with *"no shift and no remap — the bit
positions are XINPUT's own."* **That is wrong.** `sub_8220D500` rebuilds the word
out of `XINPUT_GAMEPAD` first, into the game's own numbering:
| ring bits | are | not |
|---|---|---|
| 0–3 | **A B X Y** | ~~D-pad~~ |
| 4–7 | **left stick UP DOWN LEFT RIGHT** (±20000 of 32767) | ~~START/BACK/thumbs~~ |
| 8–11 | **right stick UP DOWN LEFT RIGHT** | — |
| 12–15 | **D-pad UP DOWN LEFT RIGHT** | ~~A B X Y~~ |
| 16–17 | **START, BACK** | — |
| 18–19 | **LB, RB** | — |
| 20–21 | **LT, RT** — digital at **> 220** of 255 | — |
| 22–23 | **L3, R3** | — |
Extracted mechanically, and the control is the shape: bits **0…23, each used
exactly once, none repeated**. A misdecode does not produce a clean bijection.
### 🔴 And "LB and RB are not menu inputs" — which I told you — is FALSE
They are bound at config fields `this+0x70` and `this+0x84`, and LT/RT at
`+0x74`/`+0x80`. **My negative was searched at XINPUT's bit positions in a word
that does not use them**, so it could only ever come back empty. If you dropped
LB/RB bindings on my say-so, put them back.
### The part you can use immediately: edge and level are one struct
| ring record | is |
|---|---|
| `+12` | buttons **HELD** (level) |
| `+16` | buttons **PRESSED** this frame (rising edge) |
| `+20` | buttons **RELEASED** this frame (falling edge) |
| `+28` / `+32` | `bLeftTrigger` / `bRightTrigger`, **raw 0…255** |
I previously guessed that press-vs-hold was split between `XamInputGetState` and
the `XamInputGetKeystrokeEx` queue. It is not — the game computes both, four
bytes apart, and picks per action. **You do not need a keystroke queue.**
📌 **And this is the mechanism behind play-test finding 2.** The game digitises
the left stick to four direction bits at a **61 % deflection threshold**. It never
sees a velocity, so it cannot move a cursor at a speed. One bit, one step.
### The complete input set, every row ⟨image⟩ — decoded, none guessed
A B X Y · D-pad ×4 · START · BACK · LB · RB · LT · RT · L3 · R3 · left stick ×4 ·
right stick ×4. **No field of `XINPUT_GAMEPAD` is dropped.**
### What I still owe you
* ❔ **Which output bit means which ACTION** (confirm / cancel / up / down). The
decoder's output word is `this+0x24C` in its own numbering; naming those bits
needs the layer above, and I have not read it.
* ❔ **Per-screen sets.** This is the game-wide layer.
* 🟡 5 of 18 output-bit sites did not resolve to a pad guard, so the output map is
a **lower bound**. In particular I am **not** claiming START is untested.
---
## ✅✅ 2026-09-01 (sixth) — **THE BLUR IS A TEXTURE.** `palogo_*_eff.t32` is a baked 10-px glow