Implement ADV History voice replay
This commit is contained in:
@@ -1290,7 +1290,8 @@ The write path is part of ordinary ADV execution:
|
||||
layout slot—not a T1 anchor id—although T1 entries structurally target these reset sites. Its ReadTextDB
|
||||
snapshot/commit work remains a second responsibility.
|
||||
- op `0x6e`'s glyph builder appends normal text chunks with the active geometry/font/color state.
|
||||
- voice op `0xc4` appends a `0x40000000` record containing its stored pair.
|
||||
- voice op `0xc4` appends a `0x40000000` record containing `{voice_id,0}`; History replay op `0x1bd`
|
||||
uses the same writer with `{voice_id,1}` when recording is enabled.
|
||||
- op `0x1d2` appends a `0x20000000` record with operand 1 as the value and operand 2 as the metadata type.
|
||||
It was previously misclassified as a safe statement marker; 17,323 corpus uses make this a foundational
|
||||
correction.
|
||||
@@ -1301,7 +1302,8 @@ The write path is part of ordinary ADV execution:
|
||||
|
||||
The read side is the previously identified op-`0x1d0..0x1d4` family. Op `0x1d0` returns a logical entry's
|
||||
layout slot and first record index; op `0x1d1` renders records until the next group boundary; op `0x1d3`
|
||||
finds typed metadata; op `0x1d4` finds the voice pair; op `0x1bd` replays its voice id.
|
||||
finds typed metadata; op `0x1d4` finds the voice pair. `HISTORY.BIN` dispatches pair variant 1 through op
|
||||
`0x1bd` and variant 0 through ordinary op `0xc4`.
|
||||
|
||||
History is independent of `RT.DAT`, but native full save fidelity does serialize the live backlog.
|
||||
`text_history_serialize@0x451d00` writes the index and packed records/strings after context/numbered-save
|
||||
@@ -1399,8 +1401,16 @@ surfaces `0xc0/0xc1`; that existing exit boundary now tells the host to discard
|
||||
The semantic text/index records remain untouched, so reopening History rebuilds fresh rows while exiting
|
||||
cannot leave the old labels above the resumed ADV page.
|
||||
|
||||
History's remaining work is stored voice replay through `0x1bd` and the `0xd3/0xd4/0xd5` smooth-scroll
|
||||
callback scheduler. None changes backlog ownership or requires choosing a save/profile backend.
|
||||
Stored voice replay now follows the native split. Ordinary op `0xc4` and History op `0x1bd` share the
|
||||
section-manifest resolver, Skip replacement queue, and Auto voice-pending state, but carry variants 0 and 1
|
||||
respectively through `IHost.PlayVoice`. Both append their pair to the backlog when recording is enabled;
|
||||
History's surrounding `0x1bb(0)` suppression prevents the replay from recording itself. Native
|
||||
`voice_play_indexed_asset@0x488330` stores that variant in the channel-12 sound-buffer state before starting
|
||||
playback. Its precise audible meaning remains unproven, so the Godot host retains it through its queue and
|
||||
timeline rather than inventing different playback behavior.
|
||||
|
||||
History's remaining work is the `0xd3/0xd4/0xd5` smooth-scroll callback scheduler and its small supporting
|
||||
gaps. None changes backlog ownership or requires choosing a save/profile backend.
|
||||
|
||||
The original dependency order was **Hide Window first** to establish reusable callback/coroutine input, then
|
||||
Read-message Skip, then History after both the input layer and message-completion seam exist. Hide Window is
|
||||
|
||||
@@ -138,14 +138,14 @@
|
||||
- **evidence:** Ghidra op 0xc2 handler 0x4204c0 sets run-state 0x200, arms the service timer, and calls bgm_fade_arm@0x464830; bgm_fade_tick@0x464960 interpolates current/target percent and applies volume, releasing at target 0. Native SC0000 trace at 0x7c1/0x126c shows target 0, duration 3000, 1% ticks at about 30 ms.
|
||||
|
||||
### 0xc4 `play-voice` (play-voice, argc 1)
|
||||
- **summary:** Play a voice clip by id; id resolves via the SYS4INI section manifest -> files[section_base(scene)+id] (voice OGG in DATA1/DATA4). While all-message Skip is active, retain/replace the queued voice id instead of starting it; playback resumes from the latest queued id after Skip clears. Same resolver rule as set-texture (NOT play-bgm, which is direct-name BGM{id:03d}).
|
||||
- **summary:** Play a voice clip by id with native playback/history variant 0; id resolves via the SYS4INI section manifest -> files[section_base(scene)+id] (voice OGG in DATA1/DATA4). While all-message Skip is active, retain/replace the queued voice request instead of starting it; playback resumes from the latest queued request after Skip clears. Same resolver rule as set-texture (NOT play-bgm, which is direct-name BGM{id:03d}).
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** By-ear confirmed (2026-07-06): SC0000 prologue voices play on their lines via Godot AudioStreamPlayer. Off-by-one disproven structurally: manifest interleaves graphics/voice (files[35]=EV049AA, [36]=MAN999, [37]=EV052CA, [38]=SYL0001), so files[base+id] lands voices on OGGs while files[base+id-1] would land them on .AGF graphics (silent) -- and they play, so the offset is exactly 0. Lily's lines are correctly form-gated (G[0xa57/0xa58/0xa59]) and stay silent when no form flag is seeded -- not a bug. Ghidra /v2 op_0xc4_handler@0x420610: when run_state_flags bit 0x08000000 is clear it starts the voice immediately; while set it stores the latest id/zero arg at ctx+0x6dbf4/+0x6dbf8. adv_interpreter_tick starts and clears that deferred voice when Skip/read-skip input is no longer active.
|
||||
- **evidence:** By-ear confirmed (2026-07-06): SC0000 prologue voices play on their lines via Godot AudioStreamPlayer. Off-by-one disproven structurally: manifest interleaves graphics/voice (files[35]=EV049AA, [36]=MAN999, [37]=EV052CA, [38]=SYL0001), so files[base+id] lands voices on OGGs while files[base+id-1] would land them on .AGF graphics (silent) -- and they play, so the offset is exactly 0. Lily's lines are correctly form-gated (G[0xa57/0xa58/0xa59]) and stay silent when no form flag is seeded -- not a bug. Ghidra /v2 op_0xc4_handler@0x420610: when run_state_flags bit 0x08000000 is clear it calls voice_play_indexed_asset@0x488330 with variant 0 and records pair {id,0}; while Skip is active it stores the latest id/zero variant at ctx+0x6dbf4/+0x6dbf8. adv_interpreter_tick starts and clears that deferred voice when Skip/read-skip input is no longer active.
|
||||
|
||||
### 0x1bd `play-history-voice` (u0041D910, argc 1)
|
||||
- **summary:** (voice_id) - replay a voice id selected from the retained ADV text history, preserving normal Skip and Auto-voice state behavior.
|
||||
- **summary:** Replay a voice id selected from retained ADV text history using native playback/history variant 1, preserving normal Skip and Auto-voice state behavior.
|
||||
- **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.
|
||||
- **evidence:** Ghidra /v2: op_0x1bd_play_history_voice@0x420920 stops/replaces the active voice and calls voice_play_indexed_asset@0x488330 with operand 1 plus playback variant 1 when Skip is inactive (or queues that pair while Skip is active). When text-history recording is enabled it appends voice pair {id,1}; HISTORY.BIN suppresses recording with op 0x1bb while its UI is active, preventing replay clicks from recursively entering the backlog. The handler also sets adv_auto_voice_pending when playback exists. voice_play_indexed_asset retains the variant in sound-buffer state before starting channel 12; its exact audible meaning remains unproven.
|
||||
|
||||
## compute
|
||||
|
||||
|
||||
@@ -1811,3 +1811,27 @@ Validation: engine 198/198, zero-warning Godot build, threaded `SELFTEST OK`, an
|
||||
|
||||
**Next:** manually recheck the five History rows and overlay cleanup against the original. If they match, proceed
|
||||
with stored History voice replay (`0x1bd`), followed by `0xd3/0xd4/0xd5` smooth-scroll fidelity.
|
||||
|
||||
### ADV History stored voice replay implemented (2026-07-19)
|
||||
|
||||
The manual row/cleanup recheck passed, and stored History voice replay now uses the existing voice resolver,
|
||||
message-Skip replacement queue, and Auto voice-pending service. Native RE exposed a meaningful second value
|
||||
that the earlier one-id host API erased: ordinary `0xc4` calls the indexed voice service and records
|
||||
`{voice_id,0}`, while `0x1bd` uses and records `{voice_id,1}`. `IHost.PlayVoice` now carries this playback
|
||||
variant explicitly; Godot preserves it in queued requests and diagnostics while continuing to play the same
|
||||
resolved OGG until the native variant's audible interpretation is proven.
|
||||
|
||||
The generic opcode still appends `{voice_id,1}` when history capture is enabled, matching the native handler.
|
||||
During the real History menu, the existing `0x1bb(0)` suppression boundary prevents that replay from becoming
|
||||
a new backlog record. Focused tests cover the variant, Auto state, normal recording, and suppressed replay.
|
||||
HISTORY is now 70/78 distinct opcodes and 842/854 instructions handled or safe-noop; its eight remaining
|
||||
effectful gaps total 12 instructions.
|
||||
|
||||
Validation: 199 non-DirectShow engine cases pass, the isolated DirectShow movie case passes, opcode tests and
|
||||
lint are clean, vm0 RECOVER passes, the Godot build has zero warnings, and threaded `SELFTEST OK`. The full
|
||||
suite's DirectShow case still times out only when run among the complete suite; this pre-existing multimedia
|
||||
test interaction is unrelated to the History voice path. The `/v2` Ghidra image names/comments the shared
|
||||
indexed voice service and is saved.
|
||||
|
||||
**Next:** investigate and implement the `0xd3/0xd4/0xd5` smooth-scroll callback/interpolation family as a
|
||||
separate fidelity slice, including the remaining scheduler support it depends upon.
|
||||
|
||||
Reference in New Issue
Block a user