Document SYS4 logical input bindings
This commit is contained in:
@@ -9,7 +9,13 @@ Struct `EngineCtx`, size `0xa1000`. Applied to the Ghidra `/v2` image (dispatch-
|
|||||||
| `0x40c` | `sys4ini_count` | `int` | SYS4INI record count |
|
| `0x40c` | `sys4ini_count` | `int` | SYS4INI record count |
|
||||||
| `0x410` | `archive_name_table` | `void*` | archive-name table base (arc_id*0x100 indexes it) |
|
| `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 |
|
| `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 |
|
||||||
|
| `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 |
|
||||||
| `0x13dc` | `message_skip_enabled` | `int` | persistent all-message Skip flag; op 0x88 writes it and adv_interpreter_tick injects input bit 0x40 while nonzero |
|
| `0x13dc` | `message_skip_enabled` | `int` | persistent all-message Skip flag; op 0x88 writes it and adv_interpreter_tick injects input bit 0x40 while nonzero |
|
||||||
|
| `0x1428` | `keyboard_vk_action_map` | `int` | base of 256-entry Win32 virtual-key to logical action table consumed by keyboard polling |
|
||||||
|
| `0x1828` | `dik_to_vk_table` | `int` | base of 256-entry DirectInput DIK scan-code to Win32 virtual-key translation used by op 0x10c |
|
||||||
| `0x1c34` | `mouse_wheel_delta` | `int` | signed WM_MOUSEWHEEL delta accumulated by age_main_window_proc; op 0x10d returns and clears it |
|
| `0x1c34` | `mouse_wheel_delta` | `int` | signed WM_MOUSEWHEEL delta accumulated by age_main_window_proc; op 0x10d returns and clears it |
|
||||||
| `0x3028` | `alt_pack_table` | `int` | call-script high-byte alternate pack table (unused by corpus) |
|
| `0x3028` | `alt_pack_table` | `int` | call-script high-byte alternate pack table (unused by corpus) |
|
||||||
| `0x4d7c` | `shared_profile_state` | `void*` | embedded shared SAVE.DAT state object; owns profile integer/settings tables and container timing metadata |
|
| `0x4d7c` | `shared_profile_state` | `void*` | embedded shared SAVE.DAT state object; owns profile integer/settings tables and container timing metadata |
|
||||||
|
|||||||
@@ -2266,6 +2266,41 @@ is script-owned state, with no boot seed or game-specific coordinates in the run
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Logical input-action configuration — `0xfe` / `0x107` / `0x10b` / `0x10c` (2026-07-21)
|
||||||
|
|
||||||
|
SYSTEM4's adjacent input setup block defines the logical action namespace later consumed by callback
|
||||||
|
poll/dispatch ops `0xff` and `0x100`. These calls are effectful engine configuration, not declarations:
|
||||||
|
|
||||||
|
- `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.
|
||||||
|
- `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.
|
||||||
|
- `op_0x10b_map_mouse_button@0x4216f0` writes a logical button slot into the table at EngineCtx `+0x135c`,
|
||||||
|
indexed by physical mouse button. Polling adds four to the stored slot. Initialization zeroes this map,
|
||||||
|
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.
|
||||||
|
|
||||||
|
`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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Native walls backlog (targets for this loop)
|
## Native walls backlog (targets for this loop)
|
||||||
|
|
||||||
- ~~**call-script dispatch**~~ — **SOLVED** (above): `call-script <id>` = raw SYS4INI file index.
|
- ~~**call-script dispatch**~~ — **SOLVED** (above): `call-script <id>` = raw SYS4INI file index.
|
||||||
|
|||||||
@@ -720,25 +720,35 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
|
|||||||
- **grounding:** source=investigation, confidence=high
|
- **grounding:** source=investigation, confidence=high
|
||||||
- **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.
|
- **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.
|
||||||
|
- **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.
|
||||||
|
|
||||||
### 0xff `poll-joy-callback-input` (u00415A10, argc 0)
|
### 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 current joy/input callback bitmask and initialize the per-dispatch scan state.
|
||||||
- **grounding:** source=investigation, confidence=high
|
- **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. input_poll_mouse_action_bits@0x460240 maps VK_LBUTTON to logical bit mouse_map[0]+4 (default index 4) and VK_RBUTTON to mouse_map[1]+4 (default index 5). 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 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.
|
||||||
|
|
||||||
### 0x100 `dispatch-joy-callbacks` (u00415A60, argc 0)
|
### 0x100 `dispatch-joy-callbacks` (u00415A60, argc 0)
|
||||||
- **summary:** Dispatch registered callbacks for the current or pending joy/input selection.
|
- **summary:** Dispatch registered callbacks for the current or pending joy/input selection.
|
||||||
- **grounding:** source=investigation, confidence=high
|
- **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), 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 (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.
|
||||||
|
|
||||||
### 0x101 `reset-message-skip-input` (u00415BF0, argc 0)
|
### 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.
|
- **summary:** Reset transient message-skip/input service state after an ADV chrome action without clearing op 0x88's persistent all-message Skip flag.
|
||||||
- **grounding:** source=investigation, confidence=med
|
- **grounding:** source=investigation, confidence=med
|
||||||
- **evidence:** Ghidra /v2: op_0x101_reset_message_skip_input@0x4170a0 resets the input state rooted at ctx+0xa0ce8, clears run-state bit 0x08000000, zeroes ctx+0xa0ce8, and writes ctx+0x6da74=1 / ctx+0x6da80=0. It does not touch ctx+0x13dc or ctx+0x550fc, so adv_interpreter_tick re-injects Skip on the following tick while persistent state remains enabled. The Auto, Message-skip, Read-skip, and Hide-window callbacks invoke it after their 100 ms cursor re-arm sequence.
|
- **evidence:** Ghidra /v2: op_0x101_reset_message_skip_input@0x4170a0 resets the input state rooted at ctx+0xa0ce8, clears run-state bit 0x08000000, zeroes ctx+0xa0ce8, and writes ctx+0x6da74=1 / ctx+0x6da80=0. It does not touch ctx+0x13dc or ctx+0x550fc, so adv_interpreter_tick re-injects Skip on the following tick while persistent state remains enabled. The Auto, Message-skip, Read-skip, and Hide-window callbacks invoke it after their 100 ms cursor re-arm sequence.
|
||||||
|
|
||||||
|
### 0x107 `map-joystick-button` (u0041E500, argc 2)
|
||||||
|
- **summary:** (button_slot)(physical_button) - map a logical button slot to a physical joystick button; slot N emits logical action N+4.
|
||||||
|
- **grounding:** source=investigation, confidence=high
|
||||||
|
- **evidence:** Ghidra /v2: op_0x107_map_joystick_button@0x421550 writes operand 2 to EngineCtx+0x89c+operand1*4 when unsigned operand1<32, otherwise it is a no-op. input_poll_joystick_action_bits@0x460380 emits axes as actions 0=up, 1=right, 2=down, 3=left and mapped button slot N as action N+4. SYSTEM4 maps slots 0..5 to physical buttons 0,3,2,1,6,7, producing actions 4..9.
|
||||||
|
|
||||||
### 0x108 `get-mouse-button-state` (u00415E70, argc 1)
|
### 0x108 `get-mouse-button-state` (u00415E70, argc 1)
|
||||||
- **summary:** (out) - return the current mouse-button state bitmask.
|
- **summary:** (out) - return the current mouse-button state bitmask.
|
||||||
- **grounding:** source=investigation, confidence=high
|
- **grounding:** source=investigation, confidence=high
|
||||||
- **evidence:** Ghidra /v2: op_0x108_get_mouse_button_state@0x428b60 fills a local through input_poll_raw_mouse_buttons@0x4602e0 and writes it to operand 1. The raw mapping is VK_LBUTTON -> 0x1 and VK_RBUTTON -> 0x2. This is distinct from op 0xff's logical action mask: the default mouse mapping also exposes left/right as callback indices 4/5, both registered by HIDEWIN to its close/restore callback.
|
- **evidence:** Ghidra /v2: op_0x108_get_mouse_button_state@0x428b60 fills a local through input_poll_raw_mouse_buttons@0x4602e0 and writes it to operand 1. The raw mapping is VK_LBUTTON -> 0x1 and VK_RBUTTON -> 0x2. This is distinct from op 0xff's configured logical action mask: left defaults to action 4, while SYSTEM4 maps right to action 7 with op 0x10b. HIDEWIN registers both logical actions to its close/restore callback.
|
||||||
|
|
||||||
### 0x109 `get-cursor-virtual` (u00415EC0, argc 2)
|
### 0x109 `get-cursor-virtual` (u00415EC0, argc 2)
|
||||||
- **summary:** (out_x)(out_y) - read the OS cursor and convert it into AGE's virtual-screen coordinates.
|
- **summary:** (out_x)(out_y) - read the OS cursor and convert it into AGE's virtual-screen coordinates.
|
||||||
@@ -750,6 +760,16 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
|
|||||||
- **grounding:** source=investigation, confidence=high
|
- **grounding:** source=investigation, confidence=high
|
||||||
- **evidence:** Ghidra /v2: op_0x10a_set_cursor_virtual@0x421590 maps virtual coordinates through the active VirtualFullScreen geometry and calls SetCursorPos. SC0000 alternates the cursor by one vertical pixel after state-changing ADV button clicks so the hover state re-enters cleanly.
|
- **evidence:** Ghidra /v2: op_0x10a_set_cursor_virtual@0x421590 maps virtual coordinates through the active VirtualFullScreen geometry and calls SetCursorPos. SC0000 alternates the cursor by one vertical pixel after state-changing ADV button clicks so the hover state re-enters cleanly.
|
||||||
|
|
||||||
|
### 0x10b `map-mouse-button` (u0041E5A0, argc 2)
|
||||||
|
- **summary:** (button_slot)(physical_button) - map a physical mouse button to a logical button slot; polling emits the slot as logical action slot+4.
|
||||||
|
- **grounding:** source=investigation, confidence=high
|
||||||
|
- **evidence:** Ghidra /v2: op_0x10b_map_mouse_button@0x4216f0 writes operand 1 to EngineCtx+0x135c+operand2*4 when unsigned operand1<32; it does not validate the physical-button operand. input_poll_mouse_action_bits@0x460240 checks left/right physical indices 0/1, reads this map, adds 4, and sets that logical action bit. Initialization zeroes the map, so left defaults to action 4. SYSTEM4@0x218 uses (3,1), mapping right mouse to action 7.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
### 0x10d `consume-mouse-wheel-delta` (u00415F10, argc 1)
|
### 0x10d `consume-mouse-wheel-delta` (u00415F10, argc 1)
|
||||||
- **summary:** (out) - return the accumulated signed mouse-wheel delta and clear it.
|
- **summary:** (out) - return the accumulated signed mouse-wheel delta and clear it.
|
||||||
- **grounding:** source=investigation, confidence=high
|
- **grounding:** source=investigation, confidence=high
|
||||||
@@ -997,22 +1017,6 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
|
|||||||
- **summary:** —
|
- **summary:** —
|
||||||
- **grounding:** source=kelebek, confidence=low
|
- **grounding:** source=kelebek, confidence=low
|
||||||
|
|
||||||
### 0xfe `u0041E360` (u0041E360, argc 1)
|
|
||||||
- **summary:** —
|
|
||||||
- **grounding:** source=kelebek, confidence=low
|
|
||||||
|
|
||||||
### 0x107 `u0041E500` (u0041E500, argc 2)
|
|
||||||
- **summary:** —
|
|
||||||
- **grounding:** source=kelebek, confidence=low
|
|
||||||
|
|
||||||
### 0x10b `u0041E5A0` (u0041E5A0, argc 2)
|
|
||||||
- **summary:** —
|
|
||||||
- **grounding:** source=kelebek, confidence=low
|
|
||||||
|
|
||||||
### 0x10c `u0041E5E0` (u0041E5E0, argc 2)
|
|
||||||
- **summary:** —
|
|
||||||
- **grounding:** source=kelebek, confidence=low
|
|
||||||
|
|
||||||
### 0x12c `lookup-array-2d` (lookup-array-2d, argc 5)
|
### 0x12c `lookup-array-2d` (lookup-array-2d, argc 5)
|
||||||
- **summary:** —
|
- **summary:** —
|
||||||
- **grounding:** source=kelebek, confidence=med
|
- **grounding:** source=kelebek, confidence=med
|
||||||
|
|||||||
@@ -2152,3 +2152,21 @@ by this investigation.
|
|||||||
storage backend, seed, or script-specific behavior.
|
storage backend, seed, or script-specific behavior.
|
||||||
|
|
||||||
Validation: opcode source regenerated; opcode tests/lint and `git diff --check` are clean.
|
Validation: opcode source regenerated; opcode tests/lint and `git diff --check` are clean.
|
||||||
|
|
||||||
|
### Slice B1 SYSTEM4 logical input bindings investigated (2026-07-21)
|
||||||
|
|
||||||
|
Native RE resolves SYSTEM4's adjacent `0xfe`/`0x107`/`0x10b`/`0x10c` block as the configuration layer for
|
||||||
|
the logical action mask consumed by `0xff`/`0x100`. `0xfe` sets a ten-action dispatch bound. Native default
|
||||||
|
arrows use 0=up, 1=right, 2=down, 3=left; keyboard mappings then assign Z/Enter, Space, C/LeftCtrl, X,
|
||||||
|
PageUp, and PageDown to actions 4 through 9. Joystick axes use the same directional actions, six physical
|
||||||
|
buttons map to actions 4 through 9, left mouse defaults to action 4, and SYSTEM4 remaps right mouse to 7.
|
||||||
|
|
||||||
|
The port presently sends six Godot UI actions directly to logical indices in a different directional
|
||||||
|
order and lets `0x100` scan all 32 slots. The next implementation should therefore be one generic,
|
||||||
|
engine-owned physical-to-logical binding service: model native defaults, implement all four setters, bound
|
||||||
|
callback polling/dispatch by the configured count, and route Godot key/mouse/joy events through the model.
|
||||||
|
It requires no save/profile decision, seed, or Himegari-specific branch. No runtime implementation is
|
||||||
|
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.
|
||||||
|
|||||||
@@ -284,8 +284,12 @@ SYSTEM4's paired `0x79`/`0x1c1` setup is now natively resolved and implemented:
|
|||||||
cursor restored by later layout resets, while `0x1c1` configures layout-local right/bottom overflow
|
cursor restored by later layout resets, while `0x1c1` configures layout-local right/bottom overflow
|
||||||
boundaries. `AdvTextHistory`, VM dispatch, the direct-scene SYSTEM4 bootstrap, and ordinary/history Godot
|
boundaries. `AdvTextHistory`, VM dispatch, the direct-scene SYSTEM4 bootstrap, and ordinary/history Godot
|
||||||
label geometry now share that script-owned state; the former slot-1 hardcoding is removed. The next
|
label geometry now share that script-owned state; the former slot-1 hardcoding is removed. The next
|
||||||
natural Game Start diagnostic should identify the next reached effectful fallback. Closing Godot currently
|
reached SYSTEM4 cluster is also resolved: `0xfe` establishes ten logical input actions, while `0x107`,
|
||||||
releases a parked ADV wait before process teardown, so the page map may contain one trailing
|
`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
|
||||||
|
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.
|
shutdown-only page; the final timeline `input-wait` is the authoritative stop.
|
||||||
|
|
||||||
## Stage B2 — Faithful full boot
|
## Stage B2 — Faithful full boot
|
||||||
|
|||||||
@@ -29,12 +29,16 @@ INFERRED: dict[int, dict] = {
|
|||||||
0xd5: dict(name='run-timed-callback-sequence', category='control', noop=False, confidence='high', source='investigation', summary='Start or service the frame-local timed sequence, dispatching scheduled local callbacks through the penultimate entry and selecting the catch-up target when the following deadline is already late.'),
|
0xd5: dict(name='run-timed-callback-sequence', category='control', noop=False, confidence='high', source='investigation', summary='Start or service the frame-local timed sequence, dispatching scheduled local callbacks through the penultimate entry and selecting the catch-up target when the following deadline is already late.'),
|
||||||
0xd9: dict(name='clear-run-state-0x1000', category='control', noop=True, confidence='high', source='investigation', summary='Clear native run/service bit 0x1000; if the secondary context is active, clear the same bit there. SC0000 executes it once after the initial SFX-channel reset, with no VM-visible result.'),
|
0xd9: dict(name='clear-run-state-0x1000', category='control', noop=True, confidence='high', source='investigation', summary='Clear native run/service bit 0x1000; if the secondary context is active, clear the same bit there. SC0000 executes it once after the initial SFX-channel reset, with no VM-visible result.'),
|
||||||
0xfb: dict(name='register-joy-callback', category='input', noop=False, confidence='high', source='investigation', summary='(input_index)(target_pc) - register one of 32 per-frame joy/input callback targets.'),
|
0xfb: dict(name='register-joy-callback', category='input', noop=False, confidence='high', source='investigation', summary='(input_index)(target_pc) - register one of 32 per-frame joy/input callback targets.'),
|
||||||
|
0xfe: dict(name='set-input-action-count', category='input', noop=False, confidence='high', source='investigation', summary='(count) - set the number of logical input actions scanned and dispatched by ops 0xff/0x100.'),
|
||||||
0xff: dict(name='poll-joy-callback-input', category='input', noop=False, confidence='high', source='investigation', summary='Poll the current joy/input callback bitmask and initialize the per-dispatch scan state.'),
|
0xff: dict(name='poll-joy-callback-input', category='input', noop=False, confidence='high', source='investigation', summary='Poll the current joy/input callback bitmask and initialize the per-dispatch scan state.'),
|
||||||
0x100: dict(name='dispatch-joy-callbacks', category='input', noop=False, confidence='high', source='investigation', summary='Dispatch registered callbacks for the current or pending joy/input selection.'),
|
0x100: dict(name='dispatch-joy-callbacks', category='input', noop=False, confidence='high', source='investigation', summary='Dispatch registered callbacks for the current or pending joy/input selection.'),
|
||||||
0x101: dict(name='reset-message-skip-input', category='input', noop=False, confidence='med', source='investigation', summary="Reset transient message-skip/input service state after an ADV chrome action without clearing op 0x88's persistent all-message Skip flag."),
|
0x101: dict(name='reset-message-skip-input', category='input', noop=False, confidence='med', source='investigation', summary="Reset transient message-skip/input service state after an ADV chrome action without clearing op 0x88's persistent all-message Skip flag."),
|
||||||
|
0x107: dict(name='map-joystick-button', category='input', noop=False, confidence='high', source='investigation', summary='(button_slot)(physical_button) - map a logical button slot to a physical joystick button; slot N emits logical action N+4.'),
|
||||||
0x108: dict(name='get-mouse-button-state', category='input', noop=False, confidence='high', source='investigation', summary='(out) - return the current mouse-button state bitmask.'),
|
0x108: dict(name='get-mouse-button-state', category='input', noop=False, confidence='high', source='investigation', summary='(out) - return the current mouse-button state bitmask.'),
|
||||||
0x109: dict(name='get-cursor-virtual', category='input', noop=False, confidence='high', source='investigation', summary="(out_x)(out_y) - read the OS cursor and convert it into AGE's virtual-screen coordinates."),
|
0x109: dict(name='get-cursor-virtual', category='input', noop=False, confidence='high', source='investigation', summary="(out_x)(out_y) - read the OS cursor and convert it into AGE's virtual-screen coordinates."),
|
||||||
0x10a: dict(name='set-cursor-virtual', category='input', noop=False, confidence='high', source='investigation', summary='(x)(y) - convert AGE virtual-screen coordinates to client/screen coordinates and move the OS cursor.'),
|
0x10a: dict(name='set-cursor-virtual', category='input', noop=False, confidence='high', source='investigation', summary='(x)(y) - convert AGE virtual-screen coordinates to client/screen coordinates and move the OS cursor.'),
|
||||||
|
0x10b: dict(name='map-mouse-button', category='input', noop=False, confidence='high', source='investigation', summary='(button_slot)(physical_button) - map a physical mouse button to a logical button slot; polling emits the slot as logical action slot+4.'),
|
||||||
|
0x10c: dict(name='map-keyboard-scancode', category='input', noop=False, confidence='high', source='investigation', summary='(action)(dik_scan_code) - map a DirectInput keyboard scan code to a logical input action.'),
|
||||||
0x10d: dict(name='consume-mouse-wheel-delta', category='input', noop=False, confidence='high', source='investigation', summary='(out) - return the accumulated signed mouse-wheel delta and clear it.'),
|
0x10d: dict(name='consume-mouse-wheel-delta', category='input', noop=False, confidence='high', source='investigation', summary='(out) - return the accumulated signed mouse-wheel delta and clear it.'),
|
||||||
0x140: dict(name='coroutine-label-yield', category='control', noop=False, confidence='med', source='investigation', summary="(out)(name_str)(sub_str)(in) — scene-coroutine LOOP ITERATOR / labeled yield. Handler copies name/sub strings + the int operand and calls the NATIVE video/transition service (*DAT_005c6018)(8, ctx[0x54fe8], &{name,sub,in}); writes the returned PC-like value to operand 1. In SC0000 label_462 'ループ開始' (@0x46d): `out=G[0x6be]=LABEL('J',G[0x6be])`; loop runs the intro-setup body (incl. call label_125bd = slot-table fill G[0x3239..0x324e]=4..11) and jmps back until out==G[0x6c3] (a per-scene exit-PC immediate) → mov aba5c 0 → content. The gate G[0xaba5c]==1 that opens this loop is NATIVE scene-entry state (no script sets it to 1). DAT_005c6018 is runtime-resolved (all xrefs READ) = SAME class as the DirectDraw workers we don't model. PORT = HOST-MODEL IMPLEMENTED: synthesize the ADV scene-entry gate, run the LABEL/J setup body once, then return the structurally discovered per-scene terminal; do not emulate the video service. See engine-re.md §Scene-coroutine framework."),
|
0x140: dict(name='coroutine-label-yield', category='control', noop=False, confidence='med', source='investigation', summary="(out)(name_str)(sub_str)(in) — scene-coroutine LOOP ITERATOR / labeled yield. Handler copies name/sub strings + the int operand and calls the NATIVE video/transition service (*DAT_005c6018)(8, ctx[0x54fe8], &{name,sub,in}); writes the returned PC-like value to operand 1. In SC0000 label_462 'ループ開始' (@0x46d): `out=G[0x6be]=LABEL('J',G[0x6be])`; loop runs the intro-setup body (incl. call label_125bd = slot-table fill G[0x3239..0x324e]=4..11) and jmps back until out==G[0x6c3] (a per-scene exit-PC immediate) → mov aba5c 0 → content. The gate G[0xaba5c]==1 that opens this loop is NATIVE scene-entry state (no script sets it to 1). DAT_005c6018 is runtime-resolved (all xrefs READ) = SAME class as the DirectDraw workers we don't model. PORT = HOST-MODEL IMPLEMENTED: synthesize the ADV scene-entry gate, run the LABEL/J setup body once, then return the structurally discovered per-scene terminal; do not emulate the video service. See engine-re.md §Scene-coroutine framework."),
|
||||||
0x199: dict(name='yield-adv-coroutine', category='control', noop=False, confidence='high', source='investigation', summary='Yield/re-enter the registered ADV coroutine handler. The fifth standard chrome button uses this transition to enter the HIDEWIN/window-hidden flow.'),
|
0x199: dict(name='yield-adv-coroutine', category='control', noop=False, confidence='high', source='investigation', summary='Yield/re-enter the registered ADV coroutine handler. The fifth standard chrome button uses this transition to enter the HIDEWIN/window-hidden flow.'),
|
||||||
|
|||||||
@@ -23,11 +23,41 @@ name = "sys4ini_records"
|
|||||||
type = "void*"
|
type = "void*"
|
||||||
note = "SYS4INI 80-byte record base {name[64],arc_id,file_number,offset,size}; record = base + id*0x50"
|
note = "SYS4INI 80-byte record base {name[64],arc_id,file_number,offset,size}; record = base + id*0x50"
|
||||||
[[field]]
|
[[field]]
|
||||||
|
offset = 0x814
|
||||||
|
name = "input_action_count"
|
||||||
|
type = "int"
|
||||||
|
note = "logical input callback count (0..31); op 0xfe sets it and ops 0xff/0x100 bound their action-mask scan by it"
|
||||||
|
[[field]]
|
||||||
|
offset = 0x898
|
||||||
|
name = "joystick_physical_button_count"
|
||||||
|
type = "int"
|
||||||
|
note = "WinMM JOYCAPS physical button count returned by op 0x106"
|
||||||
|
[[field]]
|
||||||
|
offset = 0x89c
|
||||||
|
name = "joystick_button_map"
|
||||||
|
type = "int"
|
||||||
|
note = "base of 32-entry logical button-slot to physical joystick-button table; op 0x107 writes it, slot N emits action N+4"
|
||||||
|
[[field]]
|
||||||
|
offset = 0x135c
|
||||||
|
name = "mouse_button_action_map"
|
||||||
|
type = "int"
|
||||||
|
note = "base of physical mouse-button to logical button-slot table; op 0x10b writes it and polling adds 4 to obtain the action index"
|
||||||
|
[[field]]
|
||||||
offset = 0x13dc
|
offset = 0x13dc
|
||||||
name = "message_skip_enabled"
|
name = "message_skip_enabled"
|
||||||
type = "int"
|
type = "int"
|
||||||
note = "persistent all-message Skip flag; op 0x88 writes it and adv_interpreter_tick injects input bit 0x40 while nonzero"
|
note = "persistent all-message Skip flag; op 0x88 writes it and adv_interpreter_tick injects input bit 0x40 while nonzero"
|
||||||
[[field]]
|
[[field]]
|
||||||
|
offset = 0x1428
|
||||||
|
name = "keyboard_vk_action_map"
|
||||||
|
type = "int"
|
||||||
|
note = "base of 256-entry Win32 virtual-key to logical action table consumed by keyboard polling"
|
||||||
|
[[field]]
|
||||||
|
offset = 0x1828
|
||||||
|
name = "dik_to_vk_table"
|
||||||
|
type = "int"
|
||||||
|
note = "base of 256-entry DirectInput DIK scan-code to Win32 virtual-key translation used by op 0x10c"
|
||||||
|
[[field]]
|
||||||
offset = 0x1c34
|
offset = 0x1c34
|
||||||
name = "mouse_wheel_delta"
|
name = "mouse_wheel_delta"
|
||||||
type = "int"
|
type = "int"
|
||||||
|
|||||||
@@ -2245,18 +2245,18 @@ argc = 1
|
|||||||
abi_source = "kelebek+decode-validated"
|
abi_source = "kelebek+decode-validated"
|
||||||
|
|
||||||
[opcode.semantics]
|
[opcode.semantics]
|
||||||
name = "u0041E360"
|
name = "set-input-action-count"
|
||||||
category = "unknown"
|
category = "input"
|
||||||
summary = ""
|
summary = "(count) - set the number of logical input actions scanned and dispatched by ops 0xff/0x100."
|
||||||
noop_headless = false
|
noop_headless = false
|
||||||
source = "kelebek"
|
source = "investigation"
|
||||||
confidence = "low"
|
confidence = "high"
|
||||||
depends_on = []
|
depends_on = []
|
||||||
evidence = ""
|
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."
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 1
|
i = 1
|
||||||
role = ""
|
role = "logical action count"
|
||||||
observed_types = ["imm"]
|
observed_types = ["imm"]
|
||||||
|
|
||||||
[[opcode]]
|
[[opcode]]
|
||||||
@@ -2273,7 +2273,7 @@ noop_headless = false
|
|||||||
source = "investigation"
|
source = "investigation"
|
||||||
confidence = "high"
|
confidence = "high"
|
||||||
depends_on = []
|
depends_on = []
|
||||||
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. input_poll_mouse_action_bits@0x460240 maps VK_LBUTTON to logical bit mouse_map[0]+4 (default index 4) and VK_RBUTTON to mouse_map[1]+4 (default index 5). 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 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."
|
||||||
|
|
||||||
[[opcode]]
|
[[opcode]]
|
||||||
op = 0x100
|
op = 0x100
|
||||||
@@ -2289,7 +2289,7 @@ noop_headless = false
|
|||||||
source = "investigation"
|
source = "investigation"
|
||||||
confidence = "high"
|
confidence = "high"
|
||||||
depends_on = []
|
depends_on = []
|
||||||
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), 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 (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."
|
||||||
|
|
||||||
[[opcode]]
|
[[opcode]]
|
||||||
op = 0x101
|
op = 0x101
|
||||||
@@ -2314,23 +2314,23 @@ argc = 2
|
|||||||
abi_source = "kelebek+decode-validated"
|
abi_source = "kelebek+decode-validated"
|
||||||
|
|
||||||
[opcode.semantics]
|
[opcode.semantics]
|
||||||
name = "u0041E500"
|
name = "map-joystick-button"
|
||||||
category = "unknown"
|
category = "input"
|
||||||
summary = ""
|
summary = "(button_slot)(physical_button) - map a logical button slot to a physical joystick button; slot N emits logical action N+4."
|
||||||
noop_headless = false
|
noop_headless = false
|
||||||
source = "kelebek"
|
source = "investigation"
|
||||||
confidence = "low"
|
confidence = "high"
|
||||||
depends_on = []
|
depends_on = []
|
||||||
evidence = ""
|
evidence = "Ghidra /v2: op_0x107_map_joystick_button@0x421550 writes operand 2 to EngineCtx+0x89c+operand1*4 when unsigned operand1<32, otherwise it is a no-op. input_poll_joystick_action_bits@0x460380 emits axes as actions 0=up, 1=right, 2=down, 3=left and mapped button slot N as action N+4. SYSTEM4 maps slots 0..5 to physical buttons 0,3,2,1,6,7, producing actions 4..9."
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 1
|
i = 1
|
||||||
role = ""
|
role = "logical button slot (action minus 4)"
|
||||||
observed_types = ["imm"]
|
observed_types = ["imm"]
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 2
|
i = 2
|
||||||
role = ""
|
role = "physical joystick button number"
|
||||||
observed_types = ["imm"]
|
observed_types = ["imm"]
|
||||||
|
|
||||||
[[opcode]]
|
[[opcode]]
|
||||||
@@ -2347,7 +2347,7 @@ noop_headless = false
|
|||||||
source = "investigation"
|
source = "investigation"
|
||||||
confidence = "high"
|
confidence = "high"
|
||||||
depends_on = []
|
depends_on = []
|
||||||
evidence = "Ghidra /v2: op_0x108_get_mouse_button_state@0x428b60 fills a local through input_poll_raw_mouse_buttons@0x4602e0 and writes it to operand 1. The raw mapping is VK_LBUTTON -> 0x1 and VK_RBUTTON -> 0x2. This is distinct from op 0xff's logical action mask: the default mouse mapping also exposes left/right as callback indices 4/5, both registered by HIDEWIN to its close/restore callback."
|
evidence = "Ghidra /v2: op_0x108_get_mouse_button_state@0x428b60 fills a local through input_poll_raw_mouse_buttons@0x4602e0 and writes it to operand 1. The raw mapping is VK_LBUTTON -> 0x1 and VK_RBUTTON -> 0x2. This is distinct from op 0xff's configured logical action mask: left defaults to action 4, while SYSTEM4 maps right to action 7 with op 0x10b. HIDEWIN registers both logical actions to its close/restore callback."
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 1
|
i = 1
|
||||||
@@ -2413,23 +2413,23 @@ argc = 2
|
|||||||
abi_source = "kelebek+decode-validated"
|
abi_source = "kelebek+decode-validated"
|
||||||
|
|
||||||
[opcode.semantics]
|
[opcode.semantics]
|
||||||
name = "u0041E5A0"
|
name = "map-mouse-button"
|
||||||
category = "unknown"
|
category = "input"
|
||||||
summary = ""
|
summary = "(button_slot)(physical_button) - map a physical mouse button to a logical button slot; polling emits the slot as logical action slot+4."
|
||||||
noop_headless = false
|
noop_headless = false
|
||||||
source = "kelebek"
|
source = "investigation"
|
||||||
confidence = "low"
|
confidence = "high"
|
||||||
depends_on = []
|
depends_on = []
|
||||||
evidence = ""
|
evidence = "Ghidra /v2: op_0x10b_map_mouse_button@0x4216f0 writes operand 1 to EngineCtx+0x135c+operand2*4 when unsigned operand1<32; it does not validate the physical-button operand. input_poll_mouse_action_bits@0x460240 checks left/right physical indices 0/1, reads this map, adds 4, and sets that logical action bit. Initialization zeroes the map, so left defaults to action 4. SYSTEM4@0x218 uses (3,1), mapping right mouse to action 7."
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 1
|
i = 1
|
||||||
role = ""
|
role = "logical button slot (action minus 4)"
|
||||||
observed_types = ["imm"]
|
observed_types = ["imm"]
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 2
|
i = 2
|
||||||
role = ""
|
role = "physical mouse button index"
|
||||||
observed_types = ["imm"]
|
observed_types = ["imm"]
|
||||||
|
|
||||||
[[opcode]]
|
[[opcode]]
|
||||||
@@ -2439,23 +2439,23 @@ argc = 2
|
|||||||
abi_source = "kelebek+decode-validated"
|
abi_source = "kelebek+decode-validated"
|
||||||
|
|
||||||
[opcode.semantics]
|
[opcode.semantics]
|
||||||
name = "u0041E5E0"
|
name = "map-keyboard-scancode"
|
||||||
category = "unknown"
|
category = "input"
|
||||||
summary = ""
|
summary = "(action)(dik_scan_code) - map a DirectInput keyboard scan code to a logical input action."
|
||||||
noop_headless = false
|
noop_headless = false
|
||||||
source = "kelebek"
|
source = "investigation"
|
||||||
confidence = "low"
|
confidence = "high"
|
||||||
depends_on = []
|
depends_on = []
|
||||||
evidence = ""
|
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."
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 1
|
i = 1
|
||||||
role = ""
|
role = "logical input action"
|
||||||
observed_types = ["imm"]
|
observed_types = ["imm"]
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 2
|
i = 2
|
||||||
role = ""
|
role = "DirectInput DIK scan code"
|
||||||
observed_types = ["imm"]
|
observed_types = ["imm"]
|
||||||
|
|
||||||
[[opcode]]
|
[[opcode]]
|
||||||
|
|||||||
Reference in New Issue
Block a user