Correct ADV Hide Window restore behavior
This commit is contained in:
@@ -1308,6 +1308,30 @@ pointer/button reads, cursor host forwarding, and real CUR decoding.
|
||||
Validation is engine 175/175, opcode/global generator tests and lints clean, zero-warning Godot build, and
|
||||
threaded `SELFTEST OK`.
|
||||
|
||||
**Manual-validation correction (2026-07-18).** Native `bit-set`/`bit-reset` operands are bit indices, not
|
||||
literal masks: HIDEWIN sets index 1 at `0x13d`, tests mask `0x2` at `0x146`, and clears index 1 at `0x154`.
|
||||
The old VM interpretation wrote mask `0x1`, so the right-button release edge could never reach the restore
|
||||
path. `/v2` confirms the generic semantics in `op_0x135_handler@0x4296c0` and
|
||||
`op_0x136_handler@0x429730`: `value |= 1 << index` and `value &= ~(1 << index)`, with indices 0..31 valid.
|
||||
The raw op-`0x108` channel therefore supplies `0x1` for `VK_LBUTTON` and `0x2` for `VK_RBUTTON`, making a
|
||||
right-button release one direct restore route. That is not the complete input model, however. Native
|
||||
`input_poll_mouse_action_bits@0x460240` also maps the physical left/right buttons through configurable
|
||||
logical actions (defaults 0/1) to callback indices 4/5, and HIDEWIN registers both indices to its restore
|
||||
callback. A completed ordinary left click therefore restores the textbox as observed in the original game.
|
||||
The raw left-button edge branch only supports moving an oversized retained display object while held; it is
|
||||
not evidence that dungeon gameplay camera panning is enabled during an ordinary 800x600 VN scene.
|
||||
|
||||
The manual run also exposed that ADV text and the op-`0x72` wait indicator are currently Godot presentation
|
||||
overlays rather than retained texture objects. The shipped handler correctly faded the textbox/chrome, but
|
||||
those overlays ignored the op-`0x199` coroutine lifecycle and remained above the scene. The host now suspends
|
||||
both when the first `0x199` saves/yields the active ADV page and restores them only when op `0x7c` restores
|
||||
that saved page PC. The enclosing input wait remains parked throughout, so HIDEWIN continues to own mouse and
|
||||
mapped action input instead of an overlay click accidentally advancing dialogue.
|
||||
The Godot input adapter now queues the native primary-action callback while the ADV page is suspended and
|
||||
does not release the enclosing dialogue wait; otherwise the restoring click would also advance the page.
|
||||
The `/v2` bit and mouse-input helpers are annotated and saved. Validation: engine 177/177, opcode tests/lints
|
||||
and vm0 RECOVER clean, zero-warning Godot build, and threaded `SELFTEST OK`.
|
||||
|
||||
### ADV retained text — ops `0x7a` / `0x204` and show-text publication (2026-07-10)
|
||||
|
||||
The SC0000 textbox uses two related native paths under the text manager at `ctx+0x14940`:
|
||||
|
||||
@@ -82,6 +82,18 @@
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0x1bd_play_history_voice@0x420920 stops/replaces the active voice, starts operand 1 through the native voice service when Skip is inactive (or queues it while Skip is active), records the replay in the message voice state when enabled, and sets adv_auto_voice_pending when playback exists. HISTORY.BIN obtains the id from retained text-record metadata before invoking this opcode.
|
||||
|
||||
## compute
|
||||
|
||||
### 0x135 `bit-set` (bit-set, argc 2)
|
||||
- **summary:** (value)(bit_index) - set the indexed bit in the destination integer.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0x135_handler@0x4296c0 fetches operand 2 as an unsigned bit index, rejects values >=32 through the native script-error path, fetches operand 1, and writes value | (1 << index). HIDEWIN.BIN sets index 1 at 0x13d and later tests mask 0x2 at 0x146.
|
||||
|
||||
### 0x136 `bit-reset` (bit-reset, argc 2)
|
||||
- **summary:** (value)(bit_index) - clear the indexed bit in the destination integer.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0x136_handler@0x429730 fetches operand 2 as an unsigned bit index, rejects values >=32 through the native script-error path, fetches operand 1, and writes value & ~(1 << index). HIDEWIN.BIN clears index 1 at 0x154 after testing mask 0x2.
|
||||
|
||||
## control
|
||||
|
||||
### 0x3 `call-script` (call-script, argc 1)
|
||||
@@ -432,8 +444,8 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
|
||||
|
||||
### 0xff `poll-joy-callback-input` (u00415A10, argc 0)
|
||||
- **summary:** Poll the current joy/input callback bitmask and initialize the per-dispatch scan state.
|
||||
- **grounding:** source=investigation, confidence=med
|
||||
- **evidence:** Ghidra /v2: op_0xff_poll_joy_callback_input@0x416eb0 clears the pending input mask, fills it through the input poller at 0x4608b0, resets the scan index, and snapshots the current input selector. It pairs with op 0x100.
|
||||
- **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.
|
||||
|
||||
### 0x100 `dispatch-joy-callbacks` (u00415A60, argc 0)
|
||||
- **summary:** Dispatch registered callbacks for the current or pending joy/input selection.
|
||||
@@ -448,7 +460,7 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
|
||||
### 0x108 `get-mouse-button-state` (u00415E70, argc 1)
|
||||
- **summary:** (out) - return the current mouse-button state bitmask.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0x108_get_mouse_button_state@0x428b60 fills a local through the mouse-state helper at 0x4602e0 and writes it to operand 1. HIDEWIN.BIN uses bit 0x1 for left-click edge interaction and bit 0x2 for its close/restore gesture; HISTORY.BIN likewise tests individual bits for transitions.
|
||||
- **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.
|
||||
|
||||
### 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.
|
||||
@@ -842,14 +854,6 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=low
|
||||
|
||||
### 0x135 `bit-set` (bit-set, argc 2)
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=med
|
||||
|
||||
### 0x136 `bit-reset` (bit-reset, argc 2)
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=med
|
||||
|
||||
### 0x137 `u0041F1C0` (u0041F1C0, argc 1)
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=low
|
||||
|
||||
@@ -1620,3 +1620,20 @@ threaded `SELFTEST OK`.
|
||||
**Next:** manually validate x=772 hide/restore and edge cursors in a normal SC0000 run. The next development
|
||||
slice should be Read-message Skip's profile-wide ReadTextDB model; History can then reuse both that
|
||||
message-completion seam and the generic callback/input layer landed here.
|
||||
|
||||
**Manual-validation correction (2026-07-18).** The textbox faded through the shipped retained-graphics path,
|
||||
but the port's separate ADV text Label and wait-indicator TextureRect stayed visible. Op `0x199` now suspends
|
||||
those two page overlays for the lifetime of the yielded ADV coroutine, and op `0x7c` restores them with the
|
||||
saved page. The failed restore interaction was a shared opcode error: `bit-set`/`bit-reset` take a bit index,
|
||||
not a literal mask, so HIDEWIN's set-index-1/test-`0x2` raw right-button release never matched. Both the C# VM
|
||||
and the Python oracle now use indexed-bit semantics. Follow-up original-game validation corrected an
|
||||
over-narrow input conclusion: AGE separately maps physical left/right mouse buttons into logical callback
|
||||
indices 4/5, and HIDEWIN registers those actions to restore as well. Godot now queues the primary action for
|
||||
a left click while the ADV page is suspended and keeps that click from advancing the enclosing dialogue
|
||||
wait. The real-script regression restores through the left-action callback without hitting the step limit
|
||||
and records the overlay suspend/restore sequence.
|
||||
Validation: engine 177/177, opcode tests/lints and vm0 RECOVER clean, zero-warning Godot build, and threaded
|
||||
`SELFTEST OK`; `/v2` bit handlers are annotated and saved.
|
||||
|
||||
**Next:** manually recheck x=772 with the original left-click restore gesture, then continue with the
|
||||
profile-wide ReadTextDB slice if the visual lifecycle now matches.
|
||||
|
||||
@@ -61,7 +61,7 @@ All opcode knowledge (ABI, semantics, provenance, `depends_on`) is hand-edited *
|
||||
|
||||
| Tool | Purpose | Run | Reads → Writes |
|
||||
|---|---|---|---|
|
||||
| `vm0.py` | Headless Python bytecode VM (Phase A0 execution-model prototype; reuses `sys4load`). | `--test` (RECOVER unit test) · `--sweep [N]` (oracle coverage) · `--scene NAME` · `--settex NAME` (set-texture resId trace + exec trace) · `<file.BIN>` | corpus → stdout; `build/vm0-trace.json`; `build/settex-<NAME>.json` |
|
||||
| `vm0.py` | Headless Python bytecode VM (Phase A0 execution-model prototype; reuses `sys4load`; native `bit-set`/`bit-reset` operands are bounded bit indices). | `--test` (RECOVER unit test) · `--sweep [N]` (oracle coverage) · `--scene NAME` · `--settex NAME` (set-texture resId trace + exec trace) · `<file.BIN>` | corpus → stdout; `build/vm0-trace.json`; `build/settex-<NAME>.json` |
|
||||
| `scene_opcode_coverage.py` | Per-scene opcode completeness gauge: histograms a scene's static opcodes and classifies each **impl** / **safe-noop** / **GAP** (effectful op the VM silently stubs). Implemented set parsed from `VirtualMachine.cs` `case` arms; metadata from `opcodes.json`. Surfaces the concrete rendering/feature holes so a half-drawn scene reads as "N ops still stubbed", not "mystery". | `scene_opcode_coverage.py [SCENE …]` (default SC0000) | corpus, `build/opcodes.json`, `engine/…/VirtualMachine.cs`, `build/callscript-names.json` → ⚙ `build/scene-opcode-coverage/<SCENE>.md` + stdout |
|
||||
| `correlate_scope.py` | Align the VM's `set-texture(resId)` trace with the game's Frida load order → tag each load's DATA2 package, flag package transitions, dump the significant ops in each transition span (the **scope selector** hunt). | `correlate_scope.py <SCENE>` | `build/settex-<SCENE>.json` + `build/frida-load-order-result.json` + index → stdout |
|
||||
| `diff_optrace.py` | **Differential offset-path oracle** (`docs/engine-re.md`): diff the engine's executed offset path (`trace_engine_ops.py`) against the VM's (`Age.Cli trace --trace-json`) → first divergence = the mis-modeled branch/op/state, with opcode + ±3 ops of context. Identifies the scene's codebase by longest-common-prefix; filters the VM trace to argc≥1 (operand-capture parity). Pure core unit-tested (`test_diff_optrace.py`). | `py -3.11 -X utf8 tools/diff_optrace.py SC0000 [--full]` | `build/engine-optrace.jsonl` + `build/vm-optrace.json` + disasm → stdout |
|
||||
|
||||
Reference in New Issue
Block a user