Implement script-configured input bindings
This commit is contained in:
@@ -9,7 +9,7 @@ Struct `EngineCtx`, size `0xa1000`. Applied to the Ghidra `/v2` image (dispatch-
|
||||
| `0x40c` | `sys4ini_count` | `int` | SYS4INI record count |
|
||||
| `0x410` | `archive_name_table` | `void*` | archive-name table base (arc_id*0x100 indexes it) |
|
||||
| `0x414` | `sys4ini_records` | `void*` | SYS4INI 80-byte record base {name[64],arc_id,file_number,offset,size}; record = base + id*0x50 |
|
||||
| `0x814` | `input_action_count` | `int` | logical input callback count (0..31); op 0xfe sets it and ops 0xff/0x100 bound their action-mask scan by it |
|
||||
| `0x814` | `input_action_count` | `int` | logical action count (0..31); op 0xfe sets it, op 0x100 scans actions below it and uses callback slot count itself when the polled mask is empty |
|
||||
| `0x898` | `joystick_physical_button_count` | `int` | WinMM JOYCAPS physical button count returned by op 0x106 |
|
||||
| `0x89c` | `joystick_button_map` | `int` | base of 32-entry logical button-slot to physical joystick-button table; op 0x107 writes it, slot N emits action N+4 |
|
||||
| `0x135c` | `mouse_button_action_map` | `int` | base of physical mouse-button to logical button-slot table; op 0x10b writes it and polling adds 4 to obtain the action index |
|
||||
@@ -71,6 +71,8 @@ Struct `EngineCtx`, size `0xa1000`. Applied to the Ghidra `/v2` image (dispatch-
|
||||
| `0x5f72c` | `adv_wait_indicator_frame` | `int` | current animated input-wait indicator frame, reset to zero when the service is armed |
|
||||
| `0x5f730` | `adv_wait_indicator_terminal_frame` | `int` | terminal frame returned by the indicator layout during ADV wait setup |
|
||||
| `0x5f734` | `adv_wait_indicator_enabled_value` | `int` | raw enable operand last written by op 0x1ce; nonzero arms the ADV wait-indicator service |
|
||||
| `0x6da6c` | `input_callback_scan_index` | `int` | op 0xff resets this to zero; op 0x100 advances it while scanning set action bits below input_action_count |
|
||||
| `0x6da70` | `input_action_count_snapshot` | `int` | op 0xff copies input_action_count here when beginning a callback poll/dispatch pass |
|
||||
| `0x6da78` | `message_skip_activation_guard` | `int` | set when op 0x88 enables Skip; suppresses the enabling click from immediately becoming a hotspot activation |
|
||||
| `0x6da80` | `message_skip_cancel_click_phase` | `int` | press/release phase used only when set:CancelMesSkipOnClick is nonzero |
|
||||
| `0x6da84` | `adv_wait_indicator_layout_slot` | `int` | ADV text layout whose wait indicator is armed or erased by input-wait setup and op 0x1ce |
|
||||
|
||||
@@ -2273,7 +2273,8 @@ poll/dispatch ops `0xff` and `0x100`. These calls are effectful engine configura
|
||||
|
||||
- `op_0xfe_set_input_action_count@0x421390` stores an unsigned count below 32 at EngineCtx `+0x814` and
|
||||
throws script error `0x10005` for an invalid count. SYSTEM4 sets 10, so callback dispatch scans actions
|
||||
0 through 9.
|
||||
0 through 9. If the polled mask is empty, `op_0x100_dispatch_joy_callbacks@0x416f00` instead invokes
|
||||
callback slot 10 and resumes after itself; that slot is the native no-input/release path, not action 10.
|
||||
- `op_0x107_map_joystick_button@0x421550` writes physical joystick button numbers into the 32-entry
|
||||
EngineCtx `+0x89c` table. Joystick axes directly emit actions 0=up, 1=right, 2=down, 3=left; table slot
|
||||
N emits action N+4. SYSTEM4 maps physical buttons 0,3,2,1,6,7 to actions 4 through 9.
|
||||
@@ -2282,22 +2283,25 @@ poll/dispatch ops `0xff` and `0x100`. These calls are effectful engine configura
|
||||
making left mouse action 4 by default; SYSTEM4 `(3,1)` maps right mouse to action 7.
|
||||
- `op_0x10c_map_keyboard_scancode@0x421730` translates a DirectInput DIK scan code through EngineCtx
|
||||
`+0x1828` to a Win32 virtual key, then writes the action into the 256-entry table at `+0x1428`.
|
||||
Initialization prebinds the arrow keys to actions 0=up, 1=right, 2=down, 3=left. SYSTEM4 adds action 4
|
||||
= Z/Enter, 5 = Space, 6 = C/LeftCtrl, 7 = X, 8 = PageUp, and 9 = PageDown.
|
||||
Initialization sets count 7 and prebinds 0=up, 1=right, 2=down, 3=left, 4=Enter, 5=Space, and
|
||||
6=Backspace. SYSTEM4 raises the count to 10 and adds Z to action 4, C/LeftCtrl to action 6, X to action
|
||||
7, PageUp to action 8, and PageDown to action 9; the earlier Enter/Space/Backspace mappings remain.
|
||||
|
||||
`input_poll_action_mask@0x4608b0` combines
|
||||
`input_poll_keyboard_action_bits@0x4601a0`, `input_poll_mouse_action_bits@0x460240`, and
|
||||
`input_poll_joystick_action_bits@0x460380`. The first three logical actions are therefore not a generic
|
||||
Godot UI ordering; they are the native engine ABI established by the input manager and refined by scripts.
|
||||
|
||||
The current port bypasses this configuration. `godot/Main.cs` feeds `ui_down`, `ui_left`, `ui_up`, and
|
||||
`ui_right` directly into indices 0,1,2,3, then maps `ui_accept`/`ui_cancel` to 4/5. The VM also scans all
|
||||
32 callback indices instead of the configured count. As a result the directional ordering differs from
|
||||
native, auxiliary actions 6 through 9 and native right-mouse action 7 are not physically reachable through
|
||||
the SYSTEM4 bindings, and the four configuration opcodes still fall back. A faithful implementation should
|
||||
add one engine-owned input-binding model shared by these four handlers and `0xff`/`0x100`, then translate
|
||||
Godot key, mouse, and joy events through it. This is frontend input configuration only: it needs no profile
|
||||
storage, boot seed, or game-specific conditional.
|
||||
The port now models this as one process-owned `InputBindings` service. It starts with the native seven
|
||||
keyboard defaults, the four configuration handlers mutate it, and `0xff` combines live keyboard-VK,
|
||||
left/right mouse, joystick-axis, and joystick-button state with the narrow logical injection used by tests.
|
||||
`0x100` scans only actions below the configured count, resumes on itself for simultaneous held actions, and
|
||||
uses callback slot `count` only for an empty mask. Godot translates layout-independent physical keys to the
|
||||
native Win32 VK namespace and sends physical mouse/standard joy events through the service; it no longer
|
||||
assigns Godot's `ui_*` actions directly to AGE indices. Direct-scene diagnostics replay the same 16 immediate
|
||||
SYSTEM4 configuration calls through `InputBindingBootstrap`, while natural boot executes the real opcodes.
|
||||
This remains frontend input configuration only: it needs no profile storage, boot seed, or game-specific
|
||||
conditional.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -721,19 +721,19 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
|
||||
- **evidence:** Ghidra /v2: op_0xfb_register_joy_callback@0x421270 bounds-checks operand 1 to 0..31 and stores operand 2 in the current script frame's 33-entry callback table. Ops 0xff/0x100 poll and dispatch this table; HISTORY.BIN and HIDEWIN.BIN register indices 0..10.
|
||||
|
||||
### 0xfe `set-input-action-count` (u0041E360, argc 1)
|
||||
- **summary:** (count) - set the number of logical input actions scanned and dispatched by ops 0xff/0x100.
|
||||
- **summary:** (count) - set the logical action scan bound; op 0x100 uses callback slot count itself when the polled mask is empty.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0xfe_set_input_action_count@0x421390 accepts unsigned values below 32, stores the count at EngineCtx+0x814, and throws script error 0x10005 otherwise. op_0xff_poll_joy_callback_input passes the input-manager subobject to input_poll_action_mask@0x4608b0 with this count as its scan bound; op_0x100_dispatch_joy_callbacks rejects selected action indices at or above the same count. SYSTEM4@0x1cf sets 10, enabling logical actions 0..9 before configuring joystick, mouse, and keyboard mappings.
|
||||
- **evidence:** Ghidra /v2: op_0xfe_set_input_action_count@0x421390 accepts unsigned values below 32, stores the count at EngineCtx+0x814, and throws script error 0x10005 otherwise. op_0x100_dispatch_joy_callbacks scans set mask bits only below this count; when op 0xff produces an empty mask, it instead dispatches the callback table entry at index count and resumes after op 0x100. SYSTEM4@0x1cf sets 10, enabling held logical actions 0..9 and reserving callback slot 10 for the no-input/release path before configuring joystick, mouse, and keyboard mappings.
|
||||
|
||||
### 0xff `poll-joy-callback-input` (u00415A10, argc 0)
|
||||
- **summary:** Poll the current joy/input callback bitmask and initialize the per-dispatch scan state.
|
||||
- **summary:** Poll the configured keyboard/mouse/joystick action mask and initialize the op 0x100 dispatch scan.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0xff_poll_joy_callback_input@0x416eb0 clears the pending input mask, fills it through input_poll_action_mask@0x4608b0, resets the scan index, and snapshots the current input selector. That poller combines configured keyboard, mouse-action, and joystick bits, bounded by EngineCtx+0x814 input_action_count. input_poll_mouse_action_bits@0x460240 maps VK_LBUTTON/VK_RBUTTON through mouse_button_action_map and adds 4; initialization leaves both slots zero, so left defaults to action 4, while SYSTEM4 op 0x10b remaps right to action 7. It pairs with op 0x100.
|
||||
- **evidence:** Ghidra /v2: op_0xff_poll_joy_callback_input@0x416eb0 clears the pending input mask, fills it through input_poll_action_mask@0x4608b0, resets EngineCtx+0x6da6c input_callback_scan_index, and copies input_action_count to +0x6da70. The poller combines configured keyboard, mouse-action, and joystick bits; the joystick button-slot loop uses input_action_count, while op 0x100 applies the final action scan bound. input_poll_mouse_action_bits@0x460240 maps VK_LBUTTON/VK_RBUTTON through mouse_button_action_map and adds 4; initialization leaves both slots zero, so left defaults to action 4, while SYSTEM4 op 0x10b remaps right to action 7.
|
||||
|
||||
### 0x100 `dispatch-joy-callbacks` (u00415A60, argc 0)
|
||||
- **summary:** Dispatch registered callbacks for the current or pending joy/input selection.
|
||||
- **summary:** Dispatch each set logical action below the configured count, or callback slot count when no action is held.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0x100_dispatch_joy_callbacks@0x416f00 scans the bitmask captured by op 0xff (or uses the current selector when no mask is present), bounded by EngineCtx+0x814 input_action_count; it pushes a return PC and jumps through the current frame's callback table populated by op 0xfb.
|
||||
- **evidence:** Ghidra /v2: op_0x100_dispatch_joy_callbacks@0x416f00 scans the bitmask captured by op 0xff from EngineCtx+0x6da6c up to but excluding EngineCtx+0x814 input_action_count. Each set action resumes at op 0x100 so simultaneous bits continue dispatching. If the mask is empty, it dispatches callback table slot input_action_count and resumes after op 0x100. Callback targets come from the current frame's table populated by op 0xfb; SYSTEM4's count 10 therefore makes slot 10 the native no-input/release callback rather than an eleventh action bit.
|
||||
|
||||
### 0x101 `reset-message-skip-input` (u00415BF0, argc 0)
|
||||
- **summary:** Reset transient message-skip/input service state after an ADV chrome action without clearing op 0x88's persistent all-message Skip flag.
|
||||
@@ -768,7 +768,7 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
|
||||
### 0x10c `map-keyboard-scancode` (u0041E5E0, argc 2)
|
||||
- **summary:** (action)(dik_scan_code) - map a DirectInput keyboard scan code to a logical input action.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0x10c_map_keyboard_scancode@0x421730 requires unsigned operand1<32 or throws script error 0x10005. It translates operand 2 through EngineCtx+0x1828, the DIK-to-Win32-VK table built by input_initialize_dik_to_vk_table@0x45fc60, then writes the action to EngineCtx+0x1428[VK]. input_poll_keyboard_action_bits@0x4601a0 polls 256 virtual keys and emits the configured action bits. input_manager_initialize_defaults@0x460630 prebinds arrow keys as 0=up, 1=right, 2=down, 3=left. SYSTEM4 additionally maps action 4 to Z/Enter, 5 to Space, 6 to C/LeftCtrl, 7 to X, 8 to PageUp, and 9 to PageDown.
|
||||
- **evidence:** Ghidra /v2: op_0x10c_map_keyboard_scancode@0x421730 requires unsigned operand1<32 or throws script error 0x10005. It translates operand 2 through EngineCtx+0x1828, the DIK-to-Win32-VK table built by input_initialize_dik_to_vk_table@0x45fc60, then writes the action to EngineCtx+0x1428[VK]. input_poll_keyboard_action_bits@0x4601a0 polls 256 virtual keys and emits the configured action bits. input_manager_initialize_defaults@0x460630 starts with count 7 and maps 0=Up, 1=Right, 2=Down, 3=Left, 4=Enter, 5=Space, and 6=Backspace. SYSTEM4 raises the count to 10 and adds Z to action 4, C/LeftCtrl to 6, X to 7, PageUp to 8, and PageDown to 9 without erasing the earlier mappings.
|
||||
|
||||
### 0x10d `consume-mouse-wheel-delta` (u00415F10, argc 1)
|
||||
- **summary:** (out) - return the accumulated signed mouse-wheel delta and clear it.
|
||||
|
||||
@@ -2170,3 +2170,23 @@ claimed by this investigation.
|
||||
|
||||
**Next:** implement that input-binding service as one coherent slice with focused mapping, bounds, and
|
||||
simultaneous-input regressions, then manually exercise TITLE/ROOM/ADV keyboard and mouse paths.
|
||||
|
||||
### Slice B1 SYSTEM4 logical input bindings implemented (2026-07-21)
|
||||
|
||||
`InputBindings` now owns AGE's process-wide physical-to-logical map. Its constructor reproduces native
|
||||
count 7 and the retained defaults Up/Right/Down/Left, Enter, Space, and Backspace. Opcodes
|
||||
`0xfe`/`0x107`/`0x10b`/`0x10c` mutate the action bound, joystick slots, mouse slots, and DIK-translated
|
||||
Win32-VK map. Godot supplies physical key, left/right mouse, standardized joy-button, and primary-axis
|
||||
state instead of hardcoded `ui_*` indices; direct-scene diagnostics replay SYSTEM4's 16 immediate mapping
|
||||
calls through a bounded bootstrap while natural boot executes them normally.
|
||||
|
||||
The implementation also corrects `0x100`'s release boundary after a final native trace: set bits dispatch
|
||||
only below `input_action_count` and resume at `0x100` for simultaneous inputs, while an empty mask dispatches
|
||||
callback slot `input_action_count` and resumes after the opcode. SYSTEM4's value 10 therefore reserves slot
|
||||
10 as the no-input/release callback rather than exposing action bit 10. The old synthetic release-bit queue
|
||||
is no longer used by Godot. Focused tests cover native defaults, all four setters, the real SYSTEM4 block,
|
||||
empty-mask dispatch, simultaneous/bounded actions, and native range failures.
|
||||
|
||||
**Next:** manually exercise TITLE, ROOM, and ADV with keyboard, left/right mouse, and—if available—a
|
||||
controller. A clean check should confirm native direction order, Z/Enter/Space behavior, right-click action
|
||||
7, and release re-arming before selecting another reached Phase-B cluster.
|
||||
|
||||
@@ -286,9 +286,10 @@ boundaries. `AdvTextHistory`, VM dispatch, the direct-scene SYSTEM4 bootstrap, a
|
||||
label geometry now share that script-owned state; the former slot-1 hardcoding is removed. The next
|
||||
reached SYSTEM4 cluster is also resolved: `0xfe` establishes ten logical input actions, while `0x107`,
|
||||
`0x10b`, and `0x10c` configure joystick-button, mouse-button, and DIK keyboard mappings consumed by
|
||||
`0xff`/`0x100`. The port currently bypasses that script-owned map with six hardcoded Godot action indices
|
||||
and scans 32 callbacks, so the next coherent slice is an engine-owned input-binding model plus these four
|
||||
handlers and physical Godot event translation. This is independent of profile/save ownership. Closing
|
||||
`0xff`/`0x100`. The port now owns the native seven-action defaults, executes all four setters, translates
|
||||
Godot physical key/mouse/joy events through the resulting map, scans only actions below the configured
|
||||
count, and invokes callback slot `count` for the empty-mask release path. Direct-scene diagnostics replay
|
||||
SYSTEM4's same 16 immediate configuration calls. This is independent of profile/save ownership. Closing
|
||||
Godot currently releases a parked ADV wait before process teardown, so the page map may contain one trailing
|
||||
shutdown-only page; the final timeline `input-wait` is the authoritative stop.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user