Document SYS4 logical input bindings

This commit is contained in:
gamer147
2026-07-21 10:07:21 -04:00
parent d0b2d31d2e
commit 313d57f01f
8 changed files with 156 additions and 55 deletions

View File

@@ -23,11 +23,41 @@ name = "sys4ini_records"
type = "void*"
note = "SYS4INI 80-byte record base {name[64],arc_id,file_number,offset,size}; record = base + id*0x50"
[[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
name = "message_skip_enabled"
type = "int"
note = "persistent all-message Skip flag; op 0x88 writes it and adv_interpreter_tick injects input bit 0x40 while nonzero"
[[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
name = "mouse_wheel_delta"
type = "int"

View File

@@ -2245,18 +2245,18 @@ argc = 1
abi_source = "kelebek+decode-validated"
[opcode.semantics]
name = "u0041E360"
category = "unknown"
summary = ""
name = "set-input-action-count"
category = "input"
summary = "(count) - set the number of logical input actions scanned and dispatched by ops 0xff/0x100."
noop_headless = false
source = "kelebek"
confidence = "low"
source = "investigation"
confidence = "high"
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]]
i = 1
role = ""
role = "logical action count"
observed_types = ["imm"]
[[opcode]]
@@ -2273,7 +2273,7 @@ noop_headless = false
source = "investigation"
confidence = "high"
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]]
op = 0x100
@@ -2289,7 +2289,7 @@ noop_headless = false
source = "investigation"
confidence = "high"
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]]
op = 0x101
@@ -2314,23 +2314,23 @@ argc = 2
abi_source = "kelebek+decode-validated"
[opcode.semantics]
name = "u0041E500"
category = "unknown"
summary = ""
name = "map-joystick-button"
category = "input"
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
source = "kelebek"
confidence = "low"
source = "investigation"
confidence = "high"
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]]
i = 1
role = ""
role = "logical button slot (action minus 4)"
observed_types = ["imm"]
[[opcode.semantics.args]]
i = 2
role = ""
role = "physical joystick button number"
observed_types = ["imm"]
[[opcode]]
@@ -2347,7 +2347,7 @@ noop_headless = false
source = "investigation"
confidence = "high"
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]]
i = 1
@@ -2413,23 +2413,23 @@ argc = 2
abi_source = "kelebek+decode-validated"
[opcode.semantics]
name = "u0041E5A0"
category = "unknown"
summary = ""
name = "map-mouse-button"
category = "input"
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
source = "kelebek"
confidence = "low"
source = "investigation"
confidence = "high"
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]]
i = 1
role = ""
role = "logical button slot (action minus 4)"
observed_types = ["imm"]
[[opcode.semantics.args]]
i = 2
role = ""
role = "physical mouse button index"
observed_types = ["imm"]
[[opcode]]
@@ -2439,23 +2439,23 @@ argc = 2
abi_source = "kelebek+decode-validated"
[opcode.semantics]
name = "u0041E5E0"
category = "unknown"
summary = ""
name = "map-keyboard-scancode"
category = "input"
summary = "(action)(dik_scan_code) - map a DirectInput keyboard scan code to a logical input action."
noop_headless = false
source = "kelebek"
confidence = "low"
source = "investigation"
confidence = "high"
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]]
i = 1
role = ""
role = "logical input action"
observed_types = ["imm"]
[[opcode.semantics.args]]
i = 2
role = ""
role = "DirectInput DIK scan code"
observed_types = ["imm"]
[[opcode]]