Implement looping SFX and immediate rotation opcodes
This commit is contained in:
@@ -2672,9 +2672,9 @@ atlas cell. Frame selection now treats the terminal value as the exclusive frame
|
||||
Keeping this 30x27 overlay outside the software backbuffer preserves the static-wait compositor optimization.
|
||||
Full SYSTEM4 replay remains Phase-B work; the shortcut carries the same state in the meantime.
|
||||
|
||||
### SC0000 native SFX / BGM-fade family — `0xb4`/`0xb5`/`0xb6`/`0xc2`/`0xd9` (2026-07-11)
|
||||
### SC0000 native SFX / BGM-fade family — `0xb4`/`0xb5`/`0xb6`/`0xba`/`0xc2`/`0xd9` (2026-07-11)
|
||||
|
||||
The three SFX opcodes are a retained channel lifecycle, not immediate fire-and-forget calls. Handler
|
||||
The four SFX opcodes are a retained channel lifecycle, not immediate fire-and-forget calls. Handler
|
||||
resolution and the saved `/v2` names are:
|
||||
|
||||
- `0xb4` `op_0xb4_sfx_load@0x4201d0` -> `sfx_channel_load@0x482500`: `(packed_raw_resource_id, channel)`
|
||||
@@ -2682,10 +2682,13 @@ resolution and the saved `/v2` names are:
|
||||
when it is nonzero, then replaces the channel decoder/buffer without starting it. The manager supports
|
||||
13 slots (`0..12`); SC0000 deliberately resets and uses the `0..9` subset.
|
||||
- `0xb5` `op_0xb5_sfx_start_once@0x420210` -> `sfx_channel_start@0x4825d0`: starts the loaded channel with
|
||||
logical loop mode 0. Adjacent op `0xba`, not this slice, passes mode 1.
|
||||
logical loop mode 0.
|
||||
- `0xb6` `op_0xb6_sfx_release@0x420250` -> `sfx_channel_release@0x482600` ->
|
||||
`sound_buffer_destroy@0x4831a0`: stop/release and clear the retained resource/decoder; empty release is
|
||||
safe. SC0000's `0x62b..0x646` and `0x120d..0x1228` are ten-channel reset sweeps.
|
||||
- `0xba` `op_0xba_sfx_start_loop@0x420290` -> the same `sfx_channel_start@0x4825d0`: starts the loaded
|
||||
channel with logical loop mode 1. `sound_stream_fill_quarter` rewinds the decoder at EOF until a later
|
||||
load or release replaces the channel. It is not an alias of the one-shot `0xb5`.
|
||||
|
||||
`sound_decode_channel@0x483360` selects the decoder by file signature, constructs a DirectSound buffer, and
|
||||
installs four quarter-buffer notifications. `sound_buffer_start@0x484270` primes the ring and synchronously
|
||||
@@ -2768,6 +2771,12 @@ overwritten to `-80 dB`; a later voice temporarily wrote the audible 50-percent
|
||||
the erroneous silent level afterward. This introduces no profile record, boot seed, or persistence backend.
|
||||
A future unified settings backend can replace the registered defaults without changing the opcode/host seam.
|
||||
|
||||
**Loop-start closeout (2026-07-28).** A complete Himegari-corpus scan found `0xba` at 334 sites in 100
|
||||
scripts, and every site immediately follows `0xb4` with the same literal channel 9 for persistent UI/ambient
|
||||
sound. The port now forwards the native start mode through its host seam and sets
|
||||
Godot's WAV loop mode before playback; `0xb5` explicitly supplies mode 0 and `0xba` supplies mode 1. The
|
||||
focused VM regression covers both modes, and the renamed/commented `/v2` handler is saved.
|
||||
|
||||
### Scene-entry state snapshot — auto-seeding single-scene runs (2026-07-09)
|
||||
|
||||
**Problem the oracle surfaced:** single-scene VM runs diverge from the engine because they lack the
|
||||
|
||||
@@ -201,6 +201,10 @@ The requested CP932 face is copied into the primary LOGFONT lfFaceName and AGE a
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra op 0xb6 handler 0x420250 -> sfx_channel_release@0x482600 -> sound_buffer_destroy@0x4831a0, which releases the per-channel object under its critical section and clears the slot. Native trace captured SC0000's channels 0..9 release sweep in consecutive calls.
|
||||
|
||||
### 0xba `sfx-start-loop` (sfx-start-loop, argc 1)
|
||||
- **summary:** (channel) — start the already-loaded sound-effect channel with logical loop mode 1; the streaming decoder rewinds at EOF until the channel is replaced or released.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
|
||||
### 0xbf `play-bgm` (play-bgm, argc 1)
|
||||
- **summary:** Play background music by id. BGM is addressed by DIRECT LITERAL NAME: id -> BGM{id:03d}.OGG (in DATA3), unlike ordinary resource operands, which are universal packed SYS4INI/AAI ids. E.g. play-bgm 5 -> BGM005. Reasserting the facade's current track id is idempotent and preserves playback position; a different id starts the new track, while fade-to-zero clears the current id so a later request can start it again.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
@@ -702,7 +706,7 @@ The handler clears the map embedded at retained-gfx owner+0x408, resets its coun
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: handler gfx_op_0x1fd_set_vec_scaled@0x422650 fetches operands 2..4, divides each by the 100.0 constant, and calls gfx_object_set_scale_current@0x47e6b0. The worker gets/creates the object, marks obj+0x68, and calls matrix4_make_scale on obj+0x6c. SC0000 sets AE001D handles to 210/210/100 and 240/240/100; without this setter their 800x800 alpha circles remain below the viewport. Both functions annotated and /v2 saved 2026-07-11.
|
||||
|
||||
### 0x1fe `set-current-rotation-axis-angle` (u004206C0, argc 5)
|
||||
### 0x1fe `set-current-rotation-axis-angle` (set-current-rotation-axis-angle, argc 5)
|
||||
- **summary:** (handle)(axis_x)(axis_y)(axis_z)(angle_degrees) — immediately replace the retained object's current axis-angle rotation matrix. This is the direct-current companion to op 0x21f's delayed one-shot rotation target.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0x1fe_set_rotation_current@0x422700 converts operands 2..5 to float and calls gfx_object_set_rotation_current@0x47e720. The worker gets/creates the handle, marks transform state at obj+0x68, stores current axis obj+0x1ec..0x1f4 and angle degrees obj+0x204, converts degrees to radians with pi/180, builds the current matrix at obj+0xec through matrix4_make_axis_angle, and sets retained-gfx redraw dirty owner+0xb558 (EngineCtx+0x51b6c). The finite-channel consumer composes this current matrix between scale and translation. Corpus: 186/187 calls use Z axis (0,0,1); the lone DEBUG call uses Y axis (0,1,0).
|
||||
@@ -1203,6 +1207,7 @@ Port status (2026-07-24): implemented through the same profile-lifetime setting
|
||||
### 0x2 `exit` (exit, argc 0)
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=med
|
||||
- **evidence:** Ghidra /v2: op_0xba_sfx_start_loop@0x420290 records a 3-dword instruction, fetches channel operand 1, and calls sfx_channel_start@0x4825d0 with logical loop mode 1. The same worker receives mode 0 from op 0xb5. sound_stream_fill_quarter@0x483b70 rewinds the decoder at EOF only for mode 1. All 334 Himegari sites pass literal channel 9 immediately after op 0xb4 loads that channel.
|
||||
|
||||
### 0x4 `u00417E30` (u00417E30, argc 2)
|
||||
- **summary:** Broader AGE-catalog compatibility stub; the port currently traces and skips it.
|
||||
@@ -1660,10 +1665,6 @@ Port status (2026-07-24): implemented through the same profile-lifetime setting
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=low
|
||||
|
||||
### 0xba `u0041D0B0` (u0041D0B0, argc 1)
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=low
|
||||
|
||||
### 0xbb `u0041D250` (u0041D250, argc 1)
|
||||
- **summary:** Broader AGE-catalog compatibility stub; the port currently traces and skips it.
|
||||
- **grounding:** source=kelebek, confidence=low
|
||||
|
||||
@@ -788,6 +788,26 @@ Linux/macOS FFmpeg builds and packaging smoke gates when work returns to cross-p
|
||||
Post-cleanup validation passes all 405 engine tests, the warning-free Godot build, threaded selftest, opcode
|
||||
lint/tooling, and the 213/213 installed video/audio corpus gate.
|
||||
|
||||
**CORPUS-WIDE OPCODE HARDENING PASS STARTED (2026-07-28).**
|
||||
With numbered-save implementation complete and further live save testing explicitly deferred, the active
|
||||
breadth pass now ranks every Himegari-observed VM fallthrough across all 481 decoded scripts. The starting
|
||||
inventory has only 25 distinct effectful gaps. The first tranche closes the two widest independent holes:
|
||||
|
||||
- `0xba` occurs 334 times in 100 scripts. Native `op_0xba_sfx_start_loop@0x420290` calls the same retained
|
||||
SFX start worker as one-shot `0xb5`, but with logical loop mode 1. The host seam now preserves that mode
|
||||
and Godot rewinds the WAV stream at EOF.
|
||||
- `0x1fe` occurs 187 times in 47 scripts. Native
|
||||
`op_0x1fe_set_rotation_current@0x422700` immediately replaces the retained object's current axis-angle
|
||||
rotation. The existing compositor already sampled that field; the VM now writes it directly.
|
||||
|
||||
Together these handlers close 521 formerly skipped instructions and reduce the corpus-wide effectful
|
||||
fallthrough inventory from 25 to 23 distinct opcodes. Focused regressions distinguish one-shot/looping SFX
|
||||
starts and prove immediate rotation does not arm the delayed `0x21f` channel. The opcode registry and
|
||||
generated references are current, and the native `/v2` image is annotated and saved. **NEXT OPCODE STEP:**
|
||||
rank the remaining 23 by systemic state risk, beginning with the 31-site retained-animation opcode `0x233`
|
||||
and the CONFIG audio/settings cluster (`0xc5`, `0xc6`, `0x1ba`), rather than treating low-frequency
|
||||
developer-only calls as equally urgent.
|
||||
|
||||
## Later Phase B breadth
|
||||
|
||||
**INIT data-semantics side track started (2026-07-22).** Before naming more gameplay state, the static
|
||||
|
||||
Reference in New Issue
Block a user