From e70b506ec4ad046bbb02a8495c936640165b92e6 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 29 Jul 2026 10:34:03 -0400 Subject: [PATCH] Map legacy surface fades and cyclic reset --- docs/engine-re.md | 44 +++++++++++++++ docs/opcode-reference.md | 57 ++++++++++--------- docs/phase-b-framework.md | 18 ++++-- tools/age_opcodes_himegari.py | 5 ++ vm-map/opcodes.toml | 100 ++++++++++++++++++++-------------- 5 files changed, 155 insertions(+), 69 deletions(-) diff --git a/docs/engine-re.md b/docs/engine-re.md index fbec96e..6a492b2 100644 --- a/docs/engine-re.md +++ b/docs/engine-re.md @@ -1355,6 +1355,24 @@ channel, samples the exact triangular phase on the shared frame clock, and compo T(anchor)`. It remains frame-driven while visible and survives native-style object cloning independently of the immediate and delayed one-shot scale fields. +**Cyclic-channel reset follow-up (2026-07-29):** opcode `0x230` is the shared reset for this looping +animation family. `op_0x230_reset_gfx_cyclic_animations@0x423ba0` takes one retained-object handle and +calls `gfx_object_reset_cyclic_animation_channels@0x47ee60`. The worker gets or creates the object, clears +active flag bit 2, and zeroes five start/period pairs: + +| channel field | start | period | +|---|---:|---:| +| looping color | `obj+0x20c` | `obj+0x220` | +| cyclic scale | `obj+0x210` | `obj+0x224` | +| cyclic rotation | `obj+0x214` | `obj+0x228` | +| second cyclic matrix | `obj+0x218` | `obj+0x22c` | +| looping source rectangle | `obj+0x21c` | `obj+0x230` | + +It does not change the object's current/base transforms. Five DEBUGADV calls reset an effect object before +demonstrating a new looping asset; FIELD's sole ordinary call resets the temporary moving-unit handle +between its one-shot translation setup and `0x239` source-cell animation. This is a bounded retained-state +operation and is the recommended follow-up after the black-fade pair below. + **Follow-up resolution (2026-07-10):** `0x21f` is the one-shot axis-angle channel and is implemented with affine rasterization. `0x223` is **not affine**: `gfx_queue_surface_alpha_transition` (`0x47f440`) inserts a type-0 command-map record keyed by arg 1: start `+4`, delay/duration `+8/+0xc`, target surface slot `+0x10`, @@ -3154,6 +3172,32 @@ retained objects when `0x20c` targets an offscreen surface, composites the captu source on the shared frame clock, blocks the VM through the endpoint, and only then permits the following surface release/root reload. Headless hosts retain their non-rendering no-op policy. +#### Single-surface black/white fade family — `0x21`–`0x24` (2026-07-29) + +The widest remaining opcode `0x22` is not a new renderer. It and `0x21` are the single-surface siblings of +the already implemented mode-4 `0x25` transition. Dispatch-table resolution gives: + +| op | handler | transition mode | endpoint | +|---|---|---:|---| +| `0x21` | `op_0x21_fade_surface_in_from_black@0x41cb00` | 0 | captured surface | +| `0x22` | `op_0x22_fade_surface_out_to_black@0x41cbc0` | 1 | black | +| `0x23` | `op_0x23_fade_surface_in_from_white@0x41cc80` | 2 | captured surface | +| `0x24` | `op_0x24_fade_surface_out_to_white@0x41cd40` | 3 | white | + +All four take `(surface_slot, timing_argument)`, set run-state bit 8, and use the exact `0x25` timing +conversion: arguments up to 64 use `argument` milliseconds per tick with alpha step 16; larger arguments +use `argument/16` milliseconds with step 1. Modes 0/2 start with an opaque solid scratch surface over the +captured frame and lower its alpha to zero. Modes 1/3 keep the captured frame opaque and raise the +black/white scratch alpha to 255. Re-entry finalizes the named endpoint before script execution resumes. + +Himegari uses only the black pair: three `0x21` calls and eight `0x22` calls, all `(1,30)` and therefore +about 480 ms. The scripts first select surface 1, clear it, publish a complete retained frame through +`0x20c`, restore the backbuffer, then fade. `0x21` is the menu-entry reveal; `0x22` is the corresponding +menu/scene exit to black. Together they close 11 of the 39 remaining skipped instructions across seven +distinct scripts. Implementation should extend the existing `LegacyScreenTransition` host path with an +explicit solid-color endpoint; treating a missing numbered surface as black is unsafe because the current +mode-4 fallback snapshots the live retained frame. + The same manual pass exposed a resource-addressing issue after `0x6c` was fixed: ROOM did execute `play-voice`, but `GodotAdvHost` attempted only inferred SC-section resolution. ROOM's voice operands `0x3365..0x3376` are universal packed SYS4INI indices (for example `0x3365` is `EUA0016.OGG`). The first diff --git a/docs/opcode-reference.md b/docs/opcode-reference.md index 35801b8..2d471f2 100644 --- a/docs/opcode-reference.md +++ b/docs/opcode-reference.md @@ -652,13 +652,37 @@ SC0000 label_1235a reaches this when 0x1c7/0x1cc are zero. Native run-state bit ## draw +### 0x21 `fade-surface-in-from-black` (u00418860, argc 2) +- **summary:** (surface_slot)(timing_argument) — block while fading from black to a captured full-frame surface. The captured surface remains the terminal frame. +- **grounding:** source=investigation, confidence=high +- **depends on:** 0x20c, 0x20d +- **evidence:** Ghidra /v2: dispatch slot 0x21 resolves to op_0x21_fade_surface_in_from_black@0x41cb00. First entry sets run-state bit 8, converts timing <=64 to interval=argument ms and alpha step=16 (otherwise interval=argument/16 and step=1), then starts screen_transition_begin@0x439da0 mode 0 with the operand surface. screen_transition_tick@0x43a7a0 draws that surface below an opaque-black scratch surface whose alpha falls 255->0; re-entry finalizes the operand surface. Corpus: 3 sites in EVOLVE, SELSTAGE, and STUDY, all (1,30), approximately 480 ms. + +### 0x22 `fade-surface-out-to-black` (u00418920, argc 2) +- **summary:** (surface_slot)(timing_argument) — block while fading a captured full-frame surface to black. Black remains the terminal frame. +- **grounding:** source=investigation, confidence=high +- **depends on:** 0x20c, 0x20d +- **evidence:** Ghidra /v2: dispatch slot 0x22 resolves to op_0x22_fade_surface_out_to_black@0x41cbc0. It uses the same timer/alpha-step conversion as 0x21 and 0x25, then starts screen_transition_begin@0x439da0 mode 1. screen_transition_tick@0x43a7a0 keeps the operand surface opaque and raises the black scratch-surface alpha 0->255; re-entry finalizes a black endpoint. Corpus: 8 sites across ALCHEMY, CHMENU, EVOLVE, FORT, SELSTAGE, STUDY, and SUMMON, all (1,30), approximately 480 ms. + +### 0x23 `fade-surface-in-from-white` (u004189D0, argc 2) +- **summary:** (surface_slot)(timing_argument) — broader-AGE mode-2 sibling of 0x21: block while fading from white to a captured full-frame surface. +- **grounding:** source=investigation, confidence=high +- **depends on:** 0x20c, 0x20d +- **evidence:** Ghidra /v2: dispatch slot 0x23 resolves to op_0x23_fade_surface_in_from_white@0x41cc80 and starts screen_transition_begin@0x439da0 mode 2 with the same timing conversion as 0x21/0x22/0x25. Not observed in Himegari's script corpus. + +### 0x24 `fade-surface-out-to-white` (u00418A90, argc 2) +- **summary:** (surface_slot)(timing_argument) — broader-AGE mode-3 sibling of 0x22: block while fading a captured full-frame surface to white. +- **grounding:** source=investigation, confidence=high +- **depends on:** 0x20c, 0x20d +- **evidence:** Ghidra /v2: dispatch slot 0x24 resolves to op_0x24_fade_surface_out_to_white@0x41cd40 and starts screen_transition_begin@0x439da0 mode 3 with the same timing conversion as 0x21/0x22/0x25. Not observed in Himegari's script corpus. + ### 0x25 `crossfade-surfaces` (u00418B40, argc 3) - **summary:** (source_surface)(target_surface)(interval_argument) — blocking legacy full-frame mode-4 alpha transition. The native engine composites the captured target surface over the captured source while an 8-bit alpha accumulator advances from 0 to 256. - **grounding:** source=investigation, confidence=high - **depends on:** 0x20c, 0x20d - **evidence:** Ghidra /v2: dispatch handler op_0x25_handler@0x41ce00 sets run-state bit 8, converts operand 3 to timer interval/alpha step (<=64: interval=arg ms and step=16; >64: interval=arg/16 ms and step=1), calls screen_transition_begin@0x439da0 with mode 4, and later screen_transition_finalize@0x4399c0. engine_main_tick_with_exception_policy polls interval_timer_poll_elapsed_steps@0x44d080 and calls screen_transition_tick@0x43a7a0; mode 4 draws source then target with progress alpha and commits target at 0x100. Thus ROOM argument 10 is about 160 ms and argument 30 about 480 ms. ROOM sites: 0x12f,0x7c6,0x856 use (1,2,10); 0x8dc uses (1,2,30). The step branch was corrected after manual timing validation; /v2 annotated and saved 2026-07-21. -The handler uses an alpha step of 1 and timer interval=argument when argument <=64. Above 64 it uses step=16 and interval=argument/16. The main loop polls that timer, advances by skipped intervals plus the current interval, and does not resume the script until the target endpoint has been presented. ROOM uses (1,2,10) for button/character entry and exit fades and (1,2,30) for the final fade before returning to TITLE. The port captures retained-frame snapshots whenever op 0x20c presents to a selected offscreen render target, then reproduces this blocking alpha lifecycle in the interactive host. +The handler uses an alpha step of 16 and timer interval=argument when argument <=64. Above 64 it uses step=1 and interval=argument/16. The main loop polls that timer, advances by skipped intervals plus the current interval, and does not resume the script until the target endpoint has been presented. ROOM uses (1,2,10) for button/character entry and exit fades and (1,2,30) for the final fade before returning to TITLE. The port captures retained-frame snapshots whenever op 0x20c presents to a selected offscreen render target, then reproduces this blocking alpha lifecycle in the interactive host. ### 0x80 `set-default-gfx-object-slot` (u0041AF00, argc 1) - **summary:** (slot) - select the retained graphics-object slot used when op 0x1d9 receives explicit slot zero. @@ -783,7 +807,7 @@ The worker clips the paired source and destination rectangles against both surfa - **summary:** Present the composited frame; label_1235a uses this on the read/message-skip branch to expose the completed foreground endpoint immediately. - **grounding:** source=investigation, confidence=high - **depends on:** 0x223, 0x1c7, 0x1cc -- **depended on by:** 0x25, 0x20d, 0x223 +- **depended on by:** 0x21, 0x22, 0x23, 0x24, 0x25, 0x20d, 0x223 - **evidence:** Ghidra: dispatch table FUN_00413860 param_1[0x26e9f]=gfx_op_0x20c_present_frame; 0x26e9f-0x26c93=0x20c. 2026-07-08. Native handler gfx_op_0x20c_present_frame -> gfx_render_frame @0x4820b0. This is an explicit retained-state publication boundary, not a continuously visible object-store mutation. The read/message-skip branch resets the animation service then presents; the port publishes and snaps pending 0x223 state here. Normal playback branches to 0x21c, which owns repeated render/wait/resume. Headless hosts remain non-blocking. @@ -792,7 +816,7 @@ Native handler gfx_op_0x20c_present_frame -> gfx_render_frame @0x4820b0. This is - **summary:** Select an offscreen surface slot as Direct3D render target 0, or restore the device backbuffer when the operand is at least 1000. - **grounding:** source=investigation, confidence=high - **depends on:** 0x20c, 0x1c7, 0x1cc -- **depended on by:** 0x25 +- **depended on by:** 0x21, 0x22, 0x23, 0x24, 0x25 - **evidence:** Ghidra /v2: op_0x20d_select_render_target@0x422e10 passes operand 1 and retained-gfx owner ctx+0x46614 to retained_gfx_select_render_target@0x479660. Its D3D calls resolve texture level 0 or backbuffer 0, then invoke device vtable +0x94 SetRenderTarget and store selected slot at owner+0xb530. DATA1: 113 calls in 24 scripts. For slots below 1000 the native worker obtains that surface's level-0 D3D texture surface and calls IDirect3DDevice9::SetRenderTarget(0,...). The >=1000 path obtains backbuffer 0 and records current target -1. Himegari commonly follows a freshly created blank surface selection with 0x20e before drawing into it. @@ -909,6 +933,11 @@ Implemented through IHost.PlayModalMovieToSurface. Its operand uses the same nat - **grounding:** source=investigation, confidence=high - **evidence:** Ghidra /v2: gfx_op_0x22f_set_position_anim@0x423b00 fetches x/y/z as floats and passes all five operands to gfx_worker_set_translation@0x472e90. The worker builds the current translation matrix with x/y/z and stores operand 2 in the channel record. This is distinct from the shared range transform at 0x229. The C# handler sets V24 directly and is therefore position-correct but does not yet model operand 2. +### 0x230 `reset-gfx-cyclic-animations` (u00421E70, argc 1) +- **summary:** (handle) — get or create a retained gfx object, clear its cyclic-animation active flag, and zero all five looping-channel start/period pairs without changing current/base transforms. +- **grounding:** source=investigation, confidence=high +- **evidence:** Ghidra /v2: op_0x230_reset_gfx_cyclic_animations@0x423ba0 fetches the handle and calls gfx_object_reset_cyclic_animation_channels@0x47ee60. The worker clears object flag bit 2 and zeroes starts obj+0x20c/+0x210/+0x214/+0x218/+0x21c plus periods obj+0x220/+0x224/+0x228/+0x22c/+0x230, covering looping color, scale, rotation, the second cyclic matrix, and source-rectangle channels. Corpus: five DEBUGADV effect demonstrations plus one FIELD movement setup before 0x239. + ### 0x231 `animate-gfx-srcrect-loop` (u00421EA0, argc 4) - **summary:** (handle)(frame_period_ms)(frame_count)(column_count) — loop row-major through the spritesheet. Every sample preserves draw-texture's source-rectangle width/height; frame=floor((shared_frame_time-object_start)/frame_period)%frame_count, src offset=(frame%columns*width, frame/columns*height). All objects use the retained manager's shared current/previous millisecond timestamps but retain their own start and period. The native consumer raises redraw dirty only when the current and previous samples select different cells. Worker gfx_worker_anim_srcrect @0x47eec0; consumer gfx_object_anim_interpolate @0x473ed0. - **grounding:** source=investigation, confidence=high @@ -1343,24 +1372,6 @@ Port status (2026-07-24): implemented through the same profile-lifetime setting - **grounding:** source=kelebek, confidence=low - **evidence:** Not observed in Himegari's script corpus; ABI label/argc come from Kelebek's AGE table. -### 0x21 `u00418860` (u00418860, argc 2) -- **summary:** — -- **grounding:** source=kelebek, confidence=low - -### 0x22 `u00418920` (u00418920, argc 2) -- **summary:** — -- **grounding:** source=kelebek, confidence=low - -### 0x23 `u004189D0` (u004189D0, argc 2) -- **summary:** Broader AGE-catalog compatibility stub; the port currently traces and skips it. -- **grounding:** source=kelebek, confidence=low -- **evidence:** Not observed in Himegari's script corpus; ABI label/argc come from Kelebek's AGE table. - -### 0x24 `u00418A90` (u00418A90, argc 2) -- **summary:** Broader AGE-catalog compatibility stub; the port currently traces and skips it. -- **grounding:** source=kelebek, confidence=low -- **evidence:** Not observed in Himegari's script corpus; ABI label/argc come from Kelebek's AGE table. - ### 0x26 `u00418C00` (u00418C00, argc 4) - **summary:** Broader AGE-catalog compatibility stub; the port currently traces and skips it. - **grounding:** source=kelebek, confidence=low @@ -2103,10 +2114,6 @@ Port status (2026-07-24): implemented through the same profile-lifetime setting - **grounding:** source=kelebek, confidence=low - **evidence:** Not observed in Himegari's script corpus; ABI label/argc come from Kelebek's AGE table. -### 0x230 `u00421E70` (u00421E70, argc 1) -- **summary:** — -- **grounding:** source=kelebek, confidence=low - ### 0x235 `u00422100` (u00422100, argc 5) - **summary:** Broader AGE-catalog compatibility stub; the port currently traces and skips it. - **grounding:** source=kelebek, confidence=low diff --git a/docs/phase-b-framework.md b/docs/phase-b-framework.md index 14eb8a5..03f4e68 100644 --- a/docs/phase-b-framework.md +++ b/docs/phase-b-framework.md @@ -823,9 +823,9 @@ writer updates only the nine audio keys and preserves every unrelated option. Ra native `2 ↔ -1` band toggling. Godot applies gains through its nested audio buses, so master and category volume compose, and route changes immediately stop/mute active playback according to the native worker distinctions. All 37 CONFIG sites now dispatch, -reducing the effectful fallthrough inventory from 22 to 18 distinct opcodes. Seven focused tests cover +reducing the effectful fallthrough inventory from 22 to 18 distinct opcodes. Eight focused tests cover native defaults, category isolation, getter/setter round trips, idempotent routes, cross-VM state, invalid -selectors, path resolution, CP932 INI preservation, and persistence. All 475 engine tests, the +selectors, native and redirected path resolution, CP932 INI preservation, and persistence. All 476 engine tests, the warning-free Godot build, opcode lint, and the Himegari-targeted threaded selftest pass. **Post-tranche rerank:** a fresh 481-script aggregate confirms exactly 18 effectful gaps totaling only @@ -834,8 +834,18 @@ warning-free Godot build, opcode lint, and the Himegari-targeted threaded selfte sites. CONFIG itself is now 1659/1665 instructions implemented, with four unrelated calls remaining (`0x142` twice, `0xb7`, and `0xb8`). -**NEXT:** investigate `0x22` as the widest remaining independent opcode, with `0x230` as the next -candidate if `0x22` proves to own a larger subsystem. +**Next-opcode investigation complete (2026-07-29):** `0x22` is the mode-1 half of AGE's blocking +single-surface black-fade pair, not a larger subsystem. `0x21(surface,timing)` fades black to the captured +surface; `0x22(surface,timing)` fades the captured surface to black. They share `0x25`'s already-modeled +timer conversion and full-frame snapshot lifecycle. All 11 combined Himegari sites use `(1,30)`, about +480 ms, across menu entry/exit paths. The existing `LegacyScreenTransition` host seam can own the pair, +but it needs an explicit black endpoint rather than its mode-4 missing-surface fallback. + +The follow-up `0x230(handle)` is also decoded: it clears the retained object's cyclic active flag and five +looping-channel start/period pairs without changing current/base transforms. Its six calls are five +DEBUGADV effect demonstrations plus one FIELD movement setup. + +**NEXT:** implement `0x21` and `0x22` together as one black-fade slice, then implement `0x230`. ## Later Phase B breadth diff --git a/tools/age_opcodes_himegari.py b/tools/age_opcodes_himegari.py index bc04e53..569fc6d 100644 --- a/tools/age_opcodes_himegari.py +++ b/tools/age_opcodes_himegari.py @@ -4,6 +4,10 @@ from __future__ import annotations INFERRED: dict[int, dict] = { + 0x21: dict(name='fade-surface-in-from-black', category='draw', noop=False, confidence='high', source='investigation', summary='(surface_slot)(timing_argument) — block while fading from black to a captured full-frame surface. The captured surface remains the terminal frame.'), + 0x22: dict(name='fade-surface-out-to-black', category='draw', noop=False, confidence='high', source='investigation', summary='(surface_slot)(timing_argument) — block while fading a captured full-frame surface to black. Black remains the terminal frame.'), + 0x23: dict(name='fade-surface-in-from-white', category='draw', noop=False, confidence='high', source='investigation', summary='(surface_slot)(timing_argument) — broader-AGE mode-2 sibling of 0x21: block while fading from white to a captured full-frame surface.'), + 0x24: dict(name='fade-surface-out-to-white', category='draw', noop=False, confidence='high', source='investigation', summary='(surface_slot)(timing_argument) — broader-AGE mode-3 sibling of 0x22: block while fading a captured full-frame surface to white.'), 0x25: dict(name='crossfade-surfaces', category='draw', noop=False, confidence='high', source='investigation', summary='(source_surface)(target_surface)(interval_argument) — blocking legacy full-frame mode-4 alpha transition. The native engine composites the captured target surface over the captured source while an 8-bit alpha accumulator advances from 0 to 256.'), 0x60: dict(name='random-modulo', category='compute', noop=False, confidence='high', source='investigation', summary="(destination)(bound) — write CRT rand() % bound. A bound of zero first writes zero and then raises the engine's script error."), 0x6c: dict(name='zero-int-range', category='compute', noop=False, confidence='high', source='investigation', summary='(destination)(count) — write logical integer zero to count consecutive VM cells beginning at the resolved destination.'), @@ -72,6 +76,7 @@ INFERRED: dict[int, dict] = { 0x21b: dict(name='line-id?', category='marker', noop=True, confidence='med', source='harness', summary='1 imm; mov->0x21b->stmt-end; near save/load-messkip — likely line/stmt id, verify not msg-control'), 0x228: dict(name='query-gfx-translation-target', category='draw', noop=False, confidence='high', source='investigation', summary='0x228 query translation target (succ)(handle)(outX)(outY)(outZ): clone the retained object, decompose its target translation matrix at obj+0x17c, and return matrix translation obj+0x1ac/+0x1b0/+0x1b4. Returns succ=0 when found; when absent, writes succ=1 and leaves outputs untouched. The C# VM queries TranslationTarget independently of V24. See docs/engine-re.md §SC0000 anim cluster.'), 0x22f: dict(name='set-gfx-position-current', category='draw', noop=False, confidence='high', source='investigation', summary="(handle)(control)(x)(y)(z) — set the object's current/base translation matrix. The native worker also retains operand 2 as channel control state; the port currently applies x/y/z but ignores that control operand."), + 0x230: dict(name='reset-gfx-cyclic-animations', category='draw', noop=False, confidence='high', source='investigation', summary='(handle) — get or create a retained gfx object, clear its cyclic-animation active flag, and zero all five looping-channel start/period pairs without changing current/base transforms.'), 0x231: dict(name='animate-gfx-srcrect-loop', category='draw', noop=False, confidence='high', source='investigation', summary="(handle)(frame_period_ms)(frame_count)(column_count) — loop row-major through the spritesheet. Every sample preserves draw-texture's source-rectangle width/height; frame=floor((shared_frame_time-object_start)/frame_period)%frame_count, src offset=(frame%columns*width, frame/columns*height). All objects use the retained manager's shared current/previous millisecond timestamps but retain their own start and period. The native consumer raises redraw dirty only when the current and previous samples select different cells. Worker gfx_worker_anim_srcrect @0x47eec0; consumer gfx_object_anim_interpolate @0x473ed0."), 0x232: dict(name='animate-gfx-color-loop', category='draw', noop=False, confidence='high', source='investigation', summary='0x232 anim-color (handle)(period)(alpha)(color): ping-pong the temporary packed ARGB passed to the normal object blit. Handler resolves negative alpha/RGB from static color obj+0x60 and clamps alpha above 255. Blend selector obj+0x30 is unchanged: mode 0 keeps default blending (animated alpha is inert; RGB is vertex modulation), while mode 1 uses sampled ARGB alpha as the SRCALPHA scale for additive composition. Fresh static color is 0xffffffff. The C# VM resolves sentinels and consumes sampled ARGB through the unchanged mode-specific path. See docs/engine-re.md §SC0000 anim cluster.'), 0x239: dict(name='animate-gfx-srcrect-target', category='draw', noop=False, confidence='high', source='investigation', summary='(handle)(delay_ms)(duration_ms)(frame_count)(column_count)(target_frame) — one-shot row-major source-rectangle cell channel. Worker gfx_worker_set_srcrect_cell @0x47ed90 stores timing at obj+0x48/+0x5c, layout at +0x238/+0x23c, and target at +0x234. C# currently retains the endpoint cell immediately.'), diff --git a/vm-map/opcodes.toml b/vm-map/opcodes.toml index 6856568..2621355 100644 --- a/vm-map/opcodes.toml +++ b/vm-map/opcodes.toml @@ -200,23 +200,23 @@ argc = 2 abi_source = "kelebek+decode-validated" [opcode.semantics] -name = "u00418860" -category = "unknown" -summary = "" +name = "fade-surface-in-from-black" +category = "draw" +summary = "(surface_slot)(timing_argument) — block while fading from black to a captured full-frame surface. The captured surface remains the terminal frame." noop_headless = false -source = "kelebek" -confidence = "low" -depends_on = [] -evidence = "" +source = "investigation" +confidence = "high" +depends_on = [0x20c, 0x20d] +evidence = "Ghidra /v2: dispatch slot 0x21 resolves to op_0x21_fade_surface_in_from_black@0x41cb00. First entry sets run-state bit 8, converts timing <=64 to interval=argument ms and alpha step=16 (otherwise interval=argument/16 and step=1), then starts screen_transition_begin@0x439da0 mode 0 with the operand surface. screen_transition_tick@0x43a7a0 draws that surface below an opaque-black scratch surface whose alpha falls 255->0; re-entry finalizes the operand surface. Corpus: 3 sites in EVOLVE, SELSTAGE, and STUDY, all (1,30), approximately 480 ms." [[opcode.semantics.args]] i = 1 -role = "" +role = "captured full-frame surface slot" observed_types = ["imm"] [[opcode.semantics.args]] i = 2 -role = "" +role = "transition timer interval / accelerated duration argument" observed_types = ["imm"] [[opcode]] @@ -226,23 +226,23 @@ argc = 2 abi_source = "kelebek+decode-validated" [opcode.semantics] -name = "u00418920" -category = "unknown" -summary = "" +name = "fade-surface-out-to-black" +category = "draw" +summary = "(surface_slot)(timing_argument) — block while fading a captured full-frame surface to black. Black remains the terminal frame." noop_headless = false -source = "kelebek" -confidence = "low" -depends_on = [] -evidence = "" +source = "investigation" +confidence = "high" +depends_on = [0x20c, 0x20d] +evidence = "Ghidra /v2: dispatch slot 0x22 resolves to op_0x22_fade_surface_out_to_black@0x41cbc0. It uses the same timer/alpha-step conversion as 0x21 and 0x25, then starts screen_transition_begin@0x439da0 mode 1. screen_transition_tick@0x43a7a0 keeps the operand surface opaque and raises the black scratch-surface alpha 0->255; re-entry finalizes a black endpoint. Corpus: 8 sites across ALCHEMY, CHMENU, EVOLVE, FORT, SELSTAGE, STUDY, and SUMMON, all (1,30), approximately 480 ms." [[opcode.semantics.args]] i = 1 -role = "" +role = "captured full-frame surface slot" observed_types = ["imm"] [[opcode.semantics.args]] i = 2 -role = "" +role = "transition timer interval / accelerated duration argument" observed_types = ["imm"] [[opcode]] @@ -255,7 +255,7 @@ abi_source = "kelebek+decode-validated" name = "crossfade-surfaces" category = "draw" summary = "(source_surface)(target_surface)(interval_argument) — blocking legacy full-frame mode-4 alpha transition. The native engine composites the captured target surface over the captured source while an 8-bit alpha accumulator advances from 0 to 256." -details = "The handler uses an alpha step of 1 and timer interval=argument when argument <=64. Above 64 it uses step=16 and interval=argument/16. The main loop polls that timer, advances by skipped intervals plus the current interval, and does not resume the script until the target endpoint has been presented. ROOM uses (1,2,10) for button/character entry and exit fades and (1,2,30) for the final fade before returning to TITLE. The port captures retained-frame snapshots whenever op 0x20c presents to a selected offscreen render target, then reproduces this blocking alpha lifecycle in the interactive host." +details = "The handler uses an alpha step of 16 and timer interval=argument when argument <=64. Above 64 it uses step=1 and interval=argument/16. The main loop polls that timer, advances by skipped intervals plus the current interval, and does not resume the script until the target endpoint has been presented. ROOM uses (1,2,10) for button/character entry and exit fades and (1,2,30) for the final fade before returning to TITLE. The port captures retained-frame snapshots whenever op 0x20c presents to a selected offscreen render target, then reproduces this blocking alpha lifecycle in the interactive host." noop_headless = false source = "investigation" confidence = "high" @@ -6072,18 +6072,18 @@ argc = 1 abi_source = "kelebek+decode-validated" [opcode.semantics] -name = "u00421E70" -category = "unknown" -summary = "" +name = "reset-gfx-cyclic-animations" +category = "draw" +summary = "(handle) — get or create a retained gfx object, clear its cyclic-animation active flag, and zero all five looping-channel start/period pairs without changing current/base transforms." noop_headless = false -source = "kelebek" -confidence = "low" +source = "investigation" +confidence = "high" depends_on = [] -evidence = "" +evidence = "Ghidra /v2: op_0x230_reset_gfx_cyclic_animations@0x423ba0 fetches the handle and calls gfx_object_reset_cyclic_animation_channels@0x47ee60. The worker clears object flag bit 2 and zeroes starts obj+0x20c/+0x210/+0x214/+0x218/+0x21c plus periods obj+0x220/+0x224/+0x228/+0x22c/+0x230, covering looping color, scale, rotation, the second cyclic matrix, and source-rectangle channels. Corpus: five DEBUGADV effect demonstrations plus one FIELD movement setup before 0x239." [[opcode.semantics.args]] i = 1 -role = "" +role = "retained gfx object handle" observed_types = ["g-int", "l-int"] [[opcode]] @@ -7262,14 +7262,24 @@ observed_in_himegari = false abi_source = "kelebek" [opcode.semantics] -name = "u004189D0" -category = "unknown" -summary = "Broader AGE-catalog compatibility stub; the port currently traces and skips it." +name = "fade-surface-in-from-white" +category = "draw" +summary = "(surface_slot)(timing_argument) — broader-AGE mode-2 sibling of 0x21: block while fading from white to a captured full-frame surface." noop_headless = false -source = "kelebek" -confidence = "low" -depends_on = [] -evidence = "Not observed in Himegari's script corpus; ABI label/argc come from Kelebek's AGE table." +source = "investigation" +confidence = "high" +depends_on = [0x20c, 0x20d] +evidence = "Ghidra /v2: dispatch slot 0x23 resolves to op_0x23_fade_surface_in_from_white@0x41cc80 and starts screen_transition_begin@0x439da0 mode 2 with the same timing conversion as 0x21/0x22/0x25. Not observed in Himegari's script corpus." + +[[opcode.semantics.args]] +i = 1 +role = "captured full-frame surface slot" +observed_types = [] + +[[opcode.semantics.args]] +i = 2 +role = "transition timer interval / accelerated duration argument" +observed_types = [] [[opcode]] op = 0x24 @@ -7279,14 +7289,24 @@ observed_in_himegari = false abi_source = "kelebek" [opcode.semantics] -name = "u00418A90" -category = "unknown" -summary = "Broader AGE-catalog compatibility stub; the port currently traces and skips it." +name = "fade-surface-out-to-white" +category = "draw" +summary = "(surface_slot)(timing_argument) — broader-AGE mode-3 sibling of 0x22: block while fading a captured full-frame surface to white." noop_headless = false -source = "kelebek" -confidence = "low" -depends_on = [] -evidence = "Not observed in Himegari's script corpus; ABI label/argc come from Kelebek's AGE table." +source = "investigation" +confidence = "high" +depends_on = [0x20c, 0x20d] +evidence = "Ghidra /v2: dispatch slot 0x24 resolves to op_0x24_fade_surface_out_to_white@0x41cd40 and starts screen_transition_begin@0x439da0 mode 3 with the same timing conversion as 0x21/0x22/0x25. Not observed in Himegari's script corpus." + +[[opcode.semantics.args]] +i = 1 +role = "captured full-frame surface slot" +observed_types = [] + +[[opcode.semantics.args]] +i = 2 +role = "transition timer interval / accelerated duration argument" +observed_types = [] [[opcode]] op = 0x26