Extract VM movie opcode handler

This commit is contained in:
gamer147
2026-08-02 23:34:22 -04:00
parent 738129faa1
commit ee4a89ca1c
4 changed files with 124 additions and 87 deletions

View File

@@ -2567,59 +2567,12 @@ public sealed partial class VirtualMachine
case "get-initial-root-run": // 0x130 (out)
Write(a[0], _initialRootRun ? 1 : 0);
return pc + 1;
case "play-modal-movie-to-surface": // 0x20f (packed resource)(surface)(movie flags)
{
long resourceId = Read(a[0]);
int surfaceSlot = (int)Read(a[1]);
// Modal and non-modal paths share packed resolution and retained-surface composition.
// The distinct host entry point owns only 0x20f's blocking lifecycle. Native attaches its
// renderer to the existing mutable target; it does not reload that surface with resource
// id/movie bytes or apply color key 0. Keep the created surface's no-key state so MPEG
// black remains opaque.
_host.PlayModalMovieToSurface(resourceId, surfaceSlot, Read(a[2]));
return pc + 1;
}
case "u004221A0": // pre-reference compatibility
case "play-movie-to-surface": // 0x236 (resource)(surface)(movie flags)(start delay ms)
{
long resourceId = Read(a[0]);
int surfaceSlot = (int)Read(a[1]);
// Native SC0000's warm-engine trace evaluates this existing site as surface 0. The bounded
// single-scene bootstrap assigns its logical layer slot 5, which the immediately following
// 0x34/0x35 static loads reuse and would therefore evict the movie before presentation.
// Reproduce the native site assignment without changing the general surface allocator.
if (ins.Offset == 0x13c8 && _cur.Script.Name.StartsWith("SC0000", StringComparison.OrdinalIgnoreCase)
&& surfaceSlot != 0)
{
int logicalLayer = (int)Globals.GetValueOrDefault(0x62450);
long movieHandle = Globals.GetValueOrDefault(0x62455 + logicalLayer);
Gfx.RemapObjectSurface(movieHandle, surfaceSlot, 0);
surfaceSlot = 0;
}
// Graph construction is synchronous. Keep the existing created surface blank during that
// boundary; publishing the movie resource first would let a concurrent compositor mistake
// the MPEG payload's .AGF name for a still image before the host registers/decodes it.
long? stopTimeMs = _host.PlayMovieToSurface(resourceId, surfaceSlot, Read(a[2]), Read(a[3]));
// Native replaces the pixels of the already-created surface without changing its resource
// identity or color key. The host's playback-to-surface binding resolves the live frame.
// Native never encounters a missing system decoder for shipped assets. If a host backend
// cannot initialize one, model the valid movie as completing immediately: BTL feeds this
// value into its effect timeline, where zero is a safe duration and -1 is not meaningful.
Gfx.SetMovieStopTime(surfaceSlot, stopTimeMs ?? 0);
return pc + 1; // native cmd size 9 resumes at the next instruction; playback is asynchronous
}
case "u00422B80": // pre-reference compatibility
case "play-movie-to-surface-at-position": // 0x241 (+ initial position ms)
{
long resourceId = Read(a[0]);
int surfaceSlot = unchecked((int)Read(a[1]));
long? stopTimeMs = _host.PlayMovieToSurfaceAtPosition(
resourceId, surfaceSlot, Read(a[2]), Read(a[3]), Read(a[4]));
// As with 0x236, seeking changes decoder position but not the graph's stop metadata.
Gfx.SetMovieStopTime(surfaceSlot, stopTimeMs ?? 0);
return pc + 1;
}
// ---- gfx command-buffer ops (VM-internal GfxState; docs/engine-re.md op-contract table) ----
case "play-modal-movie-to-surface":
case "u004221A0":
case "play-movie-to-surface":
case "u00422B80":
case "play-movie-to-surface-at-position":
return StepMovie(label, ins, pc);
case "query-gfx-object?": // 0x215 (out)(handle) -> slot | -1
if (_diagSetTexture) // reuse the flag: show what the slot query returns (grey-BG slot dig)
{
@@ -2687,28 +2640,10 @@ public sealed partial class VirtualMachine
else Write(a[0], 1); // native missing-object path leaves output operands untouched
return pc + 1;
}
case "u00422930": // pre-reference compatibility
case "query-surface-stop-time-ms": // 0x23f (out_stop_time_ms)(surface_slot)
{
int surfaceSlot = (int)Read(a[1]);
if (!Gfx.TryGetMovieStopTime(surfaceSlot, out long? stopTimeMs))
{
Write(a[0], -1); // native null CMovieToTexture slot
return pc + 1;
}
if (!stopTimeMs.HasValue)
{
_host.ReportWarning(
$"movie stop-time unavailable {_cur.Script.Name}@0x{ins.Offset:x} " +
$"surface={surfaceSlot}; returning -1");
Write(a[0], -1);
return pc + 1;
}
Write(a[0], stopTimeMs.Value);
return pc + 1;
}
case "query-movie-surface-active": // 0x23a (out)(surface slot)
Write(a[0], _host.IsMovieSurfaceActive((int)Read(a[1])) ? 1 : 0); return pc + 1;
case "u00422930":
case "query-surface-stop-time-ms":
case "query-movie-surface-active":
return StepMovie(label, ins, pc);
case "sample-frame-time": // 0x23c: previous <- current; current <- monotonic time
Gfx.SampleFrameTime(_host.InputClockMilliseconds); return pc + 1;
case "set-gfx-geom3-c": // 0x1ff: set current translation matrix
@@ -2772,14 +2707,8 @@ public sealed partial class VirtualMachine
Gfx.ResetAnimClock(); return pc + 1;
case "set-gfx-animation-service-flags": // 0x24e: bit 1 suppresses op 0x243
Gfx.SetAnimationServiceFlags(Read(a[0])); return pc + 1;
case "play-movie-mask-transition": // 0x24d: captured retained range + movie green-channel mask
_host.PlayMovieMaskTransition(Gfx, new MovieMaskTransitionRequest(
Read(a[0]), unchecked((int)Read(a[1])),
Read(a[2]), unchecked((int)Read(a[3])),
unchecked((int)Read(a[4])), unchecked((int)Read(a[5])),
unchecked((int)Read(a[6])), unchecked((int)Read(a[7])),
Read(a[8]), Read(a[9]), Read(a[10]), Read(a[11])));
return pc + 1;
case "play-movie-mask-transition":
return StepMovie(label, ins, pc);
case "queue-surface-alpha-transition": // 0x223: target surface crossfade over two object ranges
Gfx.QueueSurfaceAlphaTransition(Read(a[0]), (int)Read(a[1]), Read(a[2]), (int)Read(a[3]),
Read(a[4]), (int)Read(a[5]), Read(a[6]), Read(a[7])); return pc + 1;