Correct ADV Hide Window restore behavior
This commit is contained in:
@@ -95,6 +95,7 @@ public class HotspotInputTests
|
||||
public VirtualMachine Vm = null!;
|
||||
private long _now;
|
||||
public int HideLoopSleeps;
|
||||
public bool HideReturned;
|
||||
public override long InputClockMilliseconds => _now;
|
||||
|
||||
public override void Sleep(long duration)
|
||||
@@ -104,7 +105,17 @@ public class HotspotInputTests
|
||||
if (duration <= 1 && ++HideLoopSleeps == 2)
|
||||
{
|
||||
Vm.UpdateMouseButtonState(0x1, false); // release the x=772 activation click
|
||||
Vm.UpdateMouseButtonState(0x2, true); // native right-click close/restore gesture
|
||||
Vm.QueueInputCallback(10); // common release callback arms HIDEWIN input
|
||||
}
|
||||
else if (duration <= 1 && HideLoopSleeps == 3)
|
||||
{
|
||||
Vm.UpdateMouseButtonState(0x1, true); // next primary click is generic action index 4
|
||||
Vm.QueueInputCallback(4);
|
||||
}
|
||||
else if (duration <= 1 && HideLoopSleeps == 4)
|
||||
{
|
||||
Vm.UpdateMouseButtonState(0x1, false);
|
||||
Vm.QueueInputCallback(10);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +126,7 @@ public class HotspotInputTests
|
||||
Vm.UpdateMouseButtonState(0x1, true);
|
||||
Assert.True(Vm.TryActivatePointer(772, 572));
|
||||
while (serviceInputCallback()) { }
|
||||
HideReturned = true;
|
||||
throw new StopAtFirstWaitException();
|
||||
}
|
||||
}
|
||||
@@ -341,10 +353,12 @@ public class HotspotInputTests
|
||||
|
||||
Assert.Throws<StopAtFirstWaitException>(() => vm.Run());
|
||||
|
||||
Assert.True(host.HideLoopSleeps >= 2,
|
||||
Assert.True(host.HideLoopSleeps >= 4,
|
||||
$"hide sleeps={host.HideLoopSleeps}; halt={vm.HaltReason}; frames={string.Join(',', trace.Events.Where(e => e.Kind == Age.Engine.Diagnostics.TraceEventKind.FrameEnter).Select(e => e.Name))}; tail={string.Join(',', trace.Events.Where(e => e.Kind == Age.Engine.Diagnostics.TraceEventKind.Step).TakeLast(30).Select(e => $"{e.Ins!.Offset:x}:{e.Opcode:x}"))}");
|
||||
Assert.Contains(trace.Events, e => e.Kind == Age.Engine.Diagnostics.TraceEventKind.FrameEnter
|
||||
&& e.Name?.EndsWith("HIDEWIN.BIN", StringComparison.OrdinalIgnoreCase) == true);
|
||||
Assert.True(host.HideReturned);
|
||||
Assert.Equal(new[] { true, false }, host.AdvPagePresentationSuspended);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -383,7 +397,8 @@ public class HotspotInputTests
|
||||
(0x55, new[] { G(0x162), I(1) }),
|
||||
(0x7c, Array.Empty<Operand>()),
|
||||
}, Array.Empty<string>());
|
||||
var vm = new VirtualMachine(script, table, new RecordingHost());
|
||||
var host = new RecordingHost();
|
||||
var vm = new VirtualMachine(script, table, host);
|
||||
|
||||
vm.Run();
|
||||
|
||||
@@ -391,9 +406,45 @@ public class HotspotInputTests
|
||||
Assert.Equal(1, vm.Globals.GetValueOrDefault(0x161));
|
||||
Assert.Equal(1, vm.Globals.GetValueOrDefault(0x162));
|
||||
Assert.Equal(1, vm.Globals.GetValueOrDefault(0x163));
|
||||
Assert.Equal(new[] { true, false }, host.AdvPagePresentationSuspended);
|
||||
Assert.Equal("exit", vm.HaltReason);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BitSetAndReset_UseBitIndicesRatherThanLiteralMasks()
|
||||
{
|
||||
var table = OpcodeTableJson.Load(Paths.OpcodesJson);
|
||||
var script = ScriptAssembler.Assemble(table, "BIT_INDEX", new List<(int, Operand[])>
|
||||
{
|
||||
(0x135, new[] { G(0x168), I(1) }),
|
||||
(0x135, new[] { G(0x168), I(4) }),
|
||||
(0x136, new[] { G(0x168), I(1) }),
|
||||
(0x2, Array.Empty<Operand>()),
|
||||
}, Array.Empty<string>());
|
||||
var vm = new VirtualMachine(script, table, new RecordingHost());
|
||||
|
||||
vm.Run();
|
||||
|
||||
Assert.Equal(0x10, vm.Globals.GetValueOrDefault(0x168));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BitSet_RejectsNativeOutOfRangeIndex()
|
||||
{
|
||||
var table = OpcodeTableJson.Load(Paths.OpcodesJson);
|
||||
var script = ScriptAssembler.Assemble(table, "BIT_RANGE", new List<(int, Operand[])>
|
||||
{
|
||||
(0x135, new[] { G(0x169), I(32) }),
|
||||
(0x2, Array.Empty<Operand>()),
|
||||
}, Array.Empty<string>());
|
||||
var vm = new VirtualMachine(script, table, new RecordingHost());
|
||||
|
||||
vm.Run();
|
||||
|
||||
Assert.Equal("bit-index-out-of-range:32", vm.HaltReason);
|
||||
Assert.Equal(0, vm.Globals.GetValueOrDefault(0x169));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MouseCallback_UsesLivePointerAndButtonState()
|
||||
{
|
||||
|
||||
@@ -26,12 +26,15 @@ internal class RecordingHost : IHost
|
||||
public readonly List<(long Resource, int Surface, long Flags, long SyncMask)> Movies = new();
|
||||
public readonly List<bool> MessageSkipChanges = new();
|
||||
public readonly List<long> CursorResources = new();
|
||||
public readonly List<bool> AdvPagePresentationSuspended = new();
|
||||
public int CursorClearCount;
|
||||
public void ShowText(int offset, string text) => Lines.Add((offset, text));
|
||||
public void SetAdvTextCursor(int layoutSlot, int x, int y) => TextCursors.Add((layoutSlot, x, y));
|
||||
public void DrawStringToSurface(int surfaceSlot, int x, int y, string text)
|
||||
=> SurfaceStrings.Add((surfaceSlot, x, y, text));
|
||||
public void ConfigureAdvWaitIndicator(AdvWaitIndicatorConfig config) => WaitIndicators.Add(config);
|
||||
public void SetAdvPagePresentationSuspended(bool suspended)
|
||||
=> AdvPagePresentationSuspended.Add(suspended);
|
||||
public void WaitForInput() => Waits++;
|
||||
public virtual void WaitForInput(int layoutSlot, Func<bool> serviceInputCallback)
|
||||
{
|
||||
|
||||
@@ -18,6 +18,10 @@ public interface IHost
|
||||
void SetAdvTextCursor(int layoutSlot, int x, int y) { }
|
||||
void DrawStringToSurface(int surfaceSlot, int x, int y, string text) { }
|
||||
void ConfigureAdvWaitIndicator(AdvWaitIndicatorConfig config) { }
|
||||
// Op 0x199 temporarily yields the active ADV page into its registered hide-window coroutine.
|
||||
// The retained scene continues to render, but the text layout and its wait marker are suspended
|
||||
// until op 0x7c restores the saved page PC.
|
||||
void SetAdvPagePresentationSuspended(bool suspended) { }
|
||||
void WaitForInput();
|
||||
void WaitForInput(int layoutSlot) => WaitForInput();
|
||||
// Interactive hosts service script callbacks on the VM thread while the enclosing ADV page remains
|
||||
|
||||
@@ -330,8 +330,18 @@ public sealed class VirtualMachine
|
||||
LookupStore(a[0], BaseAddr(a[1]) + Read(a[2])); return pc + 1;
|
||||
case "lookup-array-2d":
|
||||
LookupStore(a[0], BaseAddr(a[1]) + Read(a[2]) * Read(a[3]) + Read(a[4])); return pc + 1;
|
||||
case "bit-set": Write(a[0], Read(a[0]) | Read(a[1])); return pc + 1;
|
||||
case "bit-reset": Write(a[0], Read(a[0]) & ~Read(a[1])); return pc + 1;
|
||||
case "bit-set":
|
||||
{
|
||||
long bit = Read(a[1]);
|
||||
if ((ulong)bit >= 32) { HaltReason ??= $"bit-index-out-of-range:{bit}"; return HALT; }
|
||||
Write(a[0], Read(a[0]) | (1L << (int)bit)); return pc + 1;
|
||||
}
|
||||
case "bit-reset":
|
||||
{
|
||||
long bit = Read(a[1]);
|
||||
if ((ulong)bit >= 32) { HaltReason ??= $"bit-index-out-of-range:{bit}"; return HALT; }
|
||||
Write(a[0], Read(a[0]) & ~(1L << (int)bit)); return pc + 1;
|
||||
}
|
||||
case "check-bit": Write(a[0], (Read(a[1]) >> (int)(Read(a[2]) & 31)) & 1); return pc + 1;
|
||||
case "copy-to-global": Write(a[0], Read(a[1])); return pc + 1;
|
||||
case "jmp": return _cur.Script.IndexByOffset.GetValueOrDefault((int)a[0].Value, pc + 1);
|
||||
@@ -357,6 +367,7 @@ public sealed class VirtualMachine
|
||||
{
|
||||
_cur.CoroutineResumePc = pc + 1;
|
||||
_cur.CoroutineYieldActive = true;
|
||||
_host.SetAdvPagePresentationSuspended(true);
|
||||
targetOffset = _cur.CoroutineYieldHandlerA;
|
||||
}
|
||||
else targetOffset = _cur.CoroutineYieldHandlerB;
|
||||
@@ -371,6 +382,7 @@ public sealed class VirtualMachine
|
||||
{
|
||||
_cur.CoroutineResumePc = null;
|
||||
_cur.CoroutineYieldActive = false;
|
||||
_host.SetAdvPagePresentationSuspended(false);
|
||||
return resumePc;
|
||||
}
|
||||
return pc + 1; // cold bounded scene-entry path
|
||||
|
||||
Reference in New Issue
Block a user