Extract VM ADV service opcode handler

This commit is contained in:
gamer147
2026-08-03 00:12:19 -04:00
parent 0df1b1856f
commit eb8986a38e
4 changed files with 87 additions and 33 deletions

View File

@@ -192,6 +192,9 @@ text style and glyph-delay control, direct surface-string and retained numeric-g
text/wait-object bindings; text history and input/skip/auto services remain separate. text/wait-object bindings; text history and input/skip/auto services remain separate.
`engine/Age.Engine/Vm/VirtualMachine.TextHistory.cs` owns history recording control, metadata append/navigation, `engine/Age.Engine/Vm/VirtualMachine.TextHistory.cs` owns history recording control, metadata append/navigation,
retained history rendering, metadata/voice lookup, and history backlog clearing. retained history rendering, metadata/voice lookup, and history backlog clearing.
`engine/Age.Engine/Vm/VirtualMachine.AdvServices.cs` owns persistent/active message-skip control, read-skip
settings and queries, auto-message state/timing, and per-message voice/skip reset opcode dispatch; shared state
and refresh helpers remain in the VM coordinator because live text and input paths also consume them.
The disposable `build/page-map-<SCENE>.jsonl` files are produced by editor/development Godot runs and map The disposable `build/page-map-<SCENE>.jsonl` files are produced by editor/development Godot runs and map
runtime ADV page ordinals to their authoritative script offsets for `tools/locate_page.py`. Packaged exports runtime ADV page ordinals to their authoritative script offsets for `tools/locate_page.py`. Packaged exports

View File

@@ -674,6 +674,13 @@ do not mix mechanical moves with semantic changes.
at their existing positions and routes them through guarded `StepTextHistory`; live ADV text remains with at their existing positions and routes them through guarded `StepTextHistory`; live ADV text remains with
`StepAdvText`, while skip/auto-message services remain outside both handlers. Runtime validation remains green. `StepAdvText`, while skip/auto-message services remain outside both handlers. Runtime validation remains green.
The ninth bounded `VirtualMachine.Step` extraction moved persistent/active message-skip control, read-skip
settings and queries, auto-message state/timing, and per-message voice/skip reset dispatch into
`engine/Age.Engine/Vm/VirtualMachine.AdvServices.cs`. The top-level dispatcher retains all labels and aliases
at their existing positions and routes them through guarded `StepAdvService`; shared state and refresh helpers
remain in the VM coordinator because live-text and input paths also consume them. Runtime validation remains
green.
**Gate:** no externally visible behavior or command changes; generated artifacts are byte-identical where **Gate:** no externally visible behavior or command changes; generated artifacts are byte-identical where
deterministic, and the corresponding engine, Python, Godot, and corpus validations remain green after deterministic, and the corresponding engine, Python, Godot, and corpus validations remain green after
each domain move. each domain move.
@@ -1046,8 +1053,8 @@ layer's rendering diverges from ADV; save layout.
Continue step 2 of the **codebase consolidation** maintenance slice: behavior-neutral physical splits backed Continue step 2 of the **codebase consolidation** maintenance slice: behavior-neutral physical splits backed
by the tracked launcher and layered validation driver. With the planned `Main`, `GodotAdvHost`, and `GfxState` by the tracked launcher and layered validation driver. With the planned `Main`, `GodotAdvHost`, and `GfxState`
domains isolated and the audio, movie, surface/texture, retained-object, animation, presentation, ADV-text, and domains isolated and the audio, movie, surface/texture, retained-object, animation, presentation, ADV-text, and
text-history `VirtualMachine.Step` families routed through domain handlers, extract the ADV skip/auto-message text-history `VirtualMachine.Step` families routed through domain handlers, with ADV skip/auto-message services
service opcode family next without replacing the proven dispatcher or changing public types, commands, and now isolated as well, extract the interactive input/hotspot/cursor callback opcode family next without replacing
generated output. the proven dispatcher or changing public types, commands, and generated output.
Concrete playthrough blockers may still preempt this bounded maintenance work; the consolidation effort does Concrete playthrough blockers may still preempt this bounded maintenance work; the consolidation effort does
not replace Phase B gameplay validation or the open cross-platform gates. not replace Phase B gameplay validation or the open cross-platform gates.

View File

@@ -0,0 +1,72 @@
using Age.Engine.Model;
namespace Age.Engine.Vm;
public sealed partial class VirtualMachine
{
private int StepAdvService(string label, IReadOnlyList<Operand> a, int pc)
{
switch (label)
{
case "u0041B290":
case "set-message-skip": // 0x88: persistent all-message fast-forward service state
_messageSkipEnabled = Read(a[0]) != 0;
_messageSkipServiceActive = _messageSkipEnabled;
_host.SetMessageSkipActive(_messageSkipServiceActive);
return pc + 1;
case "u00414E50": // 0x19a: persistent state used by the SO001 active overlay
Write(a[0], _messageSkipEnabled ? 1 : 0); return pc + 1;
case "u00414E80":
case "suspend-adv-skip-service": // 0x19b: preserve the toggle while leaving ADV presentation
_messageSkipServiceActive = false;
_host.SetMessageSkipActive(false);
return pc + 1;
case "u00414EC0":
case "resume-adv-skip-service": // 0x19c: recompute active fast-forward on ADV entry
_messageSkipServiceActive =
_messageSkipEnabled || _advReadSkipState || _host.IsAdvReadSkipActive;
_host.SetMessageSkipActive(_messageSkipServiceActive);
RefreshPhysicalMessageSkipState();
return pc + 1;
case "get-message-skip": // 0x1c7: persistent Skip or host-supplied Ctrl fast-forward
// Native persistent state and the independently polled physical action-6 channel both
// re-arm the transient run-state bit consumed by this query.
Write(a[0], _messageSkipServiceActive || _host.IsMessageSkipActive ? 1 : 0); return pc + 1;
case "get-adv-read-skip-state": // 0x1cc: per-message read/click skip service state
case "get-adv-service-state": // compatibility with pre-recovery generated tables
Write(a[0], _advReadSkipState || _host.IsAdvReadSkipActive ? 1 : 0); return pc + 1;
case "u0041B9B0":
case "set-read-message-skip": // 0x1ca: engine setting message:ReadTextSkip
_sharedProfile.ReadMessageSkipEnabled = Read(a[0]) != 0;
RefreshAdvReadSkipState();
return pc + 1;
case "u00414FD0":
case "get-read-message-skip": // 0x1cb
Write(a[0], _sharedProfile.ReadMessageSkipEnabled ? 1 : 0);
return pc + 1;
case "u00414F60":
case "get-auto-message": // 0x1b6: VM service state used by the ADV redraw callback
Write(a[0], _autoMessageEnabled ? 1 : 0); return pc + 1;
case "u0041B640":
case "set-auto-message": // 0x1b7
_autoMessageEnabled = Read(a[0]) != 0; return pc + 1;
case "u0041B670":
case "get-auto-message-time": // 0x1b8 (selector 0=post-voice Time0, 1=unvoiced Time1, out)
Write(a[1], Read(a[0]) == 0 ? _autoMessageTime0Ms : _autoMessageTime1Ms); return pc + 1;
case "u0041B710":
case "set-auto-message-time": // 0x1b9 (selector, milliseconds)
if (Read(a[0]) == 0) _autoMessageTime0Ms = Read(a[1]);
else if (Read(a[0]) == 1) _autoMessageTime1Ms = Read(a[1]);
return pc + 1;
case "u00415670":
case "block-mark":
case "reset-message-voice-state": // 0x1bc resets native per-message voice/queued-voice state
_autoVoicePending = false; return pc + 1;
case "u00415BF0":
case "reset-message-skip-input": // 0x101 clears transient input/run bits, not op 0x88 state
return pc + 1;
default:
throw new InvalidOperationException($"Non-ADV-service opcode routed to ADV-service handler: {label}");
}
}
}

View File

@@ -2206,58 +2206,30 @@ public sealed partial class VirtualMachine
} }
case "u0041B290": case "u0041B290":
case "set-message-skip": // 0x88: persistent all-message fast-forward service state case "set-message-skip": // 0x88: persistent all-message fast-forward service state
_messageSkipEnabled = Read(a[0]) != 0;
_messageSkipServiceActive = _messageSkipEnabled;
_host.SetMessageSkipActive(_messageSkipServiceActive);
return pc + 1;
case "u00414E50": // 0x19a: persistent state used by the SO001 active overlay case "u00414E50": // 0x19a: persistent state used by the SO001 active overlay
Write(a[0], _messageSkipEnabled ? 1 : 0); return pc + 1;
case "u00414E80": case "u00414E80":
case "suspend-adv-skip-service": // 0x19b: preserve the toggle while leaving ADV presentation case "suspend-adv-skip-service": // 0x19b: preserve the toggle while leaving ADV presentation
_messageSkipServiceActive = false;
_host.SetMessageSkipActive(false);
return pc + 1;
case "u00414EC0": case "u00414EC0":
case "resume-adv-skip-service": // 0x19c: recompute active fast-forward on ADV entry case "resume-adv-skip-service": // 0x19c: recompute active fast-forward on ADV entry
_messageSkipServiceActive =
_messageSkipEnabled || _advReadSkipState || _host.IsAdvReadSkipActive;
_host.SetMessageSkipActive(_messageSkipServiceActive);
RefreshPhysicalMessageSkipState();
return pc + 1;
case "get-message-skip": // 0x1c7: persistent Skip or host-supplied Ctrl fast-forward case "get-message-skip": // 0x1c7: persistent Skip or host-supplied Ctrl fast-forward
// Native persistent state and the independently polled physical action-6 channel both
// re-arm the transient run-state bit consumed by this query.
Write(a[0], _messageSkipServiceActive || _host.IsMessageSkipActive ? 1 : 0); return pc + 1;
case "get-adv-read-skip-state": // 0x1cc: per-message read/click skip service state case "get-adv-read-skip-state": // 0x1cc: per-message read/click skip service state
case "get-adv-service-state": // compatibility with pre-recovery generated tables case "get-adv-service-state": // compatibility with pre-recovery generated tables
Write(a[0], _advReadSkipState || _host.IsAdvReadSkipActive ? 1 : 0); return pc + 1;
case "u0041B9B0": case "u0041B9B0":
case "set-read-message-skip": // 0x1ca: engine setting message:ReadTextSkip case "set-read-message-skip": // 0x1ca: engine setting message:ReadTextSkip
_sharedProfile.ReadMessageSkipEnabled = Read(a[0]) != 0;
RefreshAdvReadSkipState();
return pc + 1;
case "u00414FD0": case "u00414FD0":
case "get-read-message-skip": // 0x1cb case "get-read-message-skip": // 0x1cb
Write(a[0], _sharedProfile.ReadMessageSkipEnabled ? 1 : 0);
return pc + 1;
case "u00414F60": case "u00414F60":
case "get-auto-message": // 0x1b6: VM service state used by the ADV redraw callback case "get-auto-message": // 0x1b6: VM service state used by the ADV redraw callback
Write(a[0], _autoMessageEnabled ? 1 : 0); return pc + 1;
case "u0041B640": case "u0041B640":
case "set-auto-message": // 0x1b7 case "set-auto-message": // 0x1b7
_autoMessageEnabled = Read(a[0]) != 0; return pc + 1;
case "u0041B670": case "u0041B670":
case "get-auto-message-time": // 0x1b8 (selector 0=post-voice Time0, 1=unvoiced Time1, out) case "get-auto-message-time": // 0x1b8 (selector 0=post-voice Time0, 1=unvoiced Time1, out)
Write(a[1], Read(a[0]) == 0 ? _autoMessageTime0Ms : _autoMessageTime1Ms); return pc + 1;
case "u0041B710": case "u0041B710":
case "set-auto-message-time": // 0x1b9 (selector, milliseconds) case "set-auto-message-time": // 0x1b9 (selector, milliseconds)
if (Read(a[0]) == 0) _autoMessageTime0Ms = Read(a[1]);
else if (Read(a[0]) == 1) _autoMessageTime1Ms = Read(a[1]);
return pc + 1;
case "u00415670": case "u00415670":
case "block-mark": case "block-mark":
case "reset-message-voice-state": // 0x1bc resets native per-message voice/queued-voice state case "reset-message-voice-state": // 0x1bc resets native per-message voice/queued-voice state
_autoVoicePending = false; return pc + 1; return StepAdvService(label, a, pc);
case "set-text-history-recording": case "set-text-history-recording":
case "append-text-history-metadata": case "append-text-history-metadata":
case "step-text-history": case "step-text-history":
@@ -2308,7 +2280,7 @@ public sealed partial class VirtualMachine
return StepAdvText(label, ins, pc); return StepAdvText(label, ins, pc);
case "u00415BF0": case "u00415BF0":
case "reset-message-skip-input": // 0x101 clears transient input/run bits, not op 0x88 state case "reset-message-skip-input": // 0x101 clears transient input/run bits, not op 0x88 state
return pc + 1; return StepAdvService(label, a, pc);
case "end-text-line": case "end-text-line":
case "set-font": case "set-font":
case "comment": case "display-furigana": case "dev_ukn": case "comment": case "display-furigana": case "dev_ukn":