fix #396 crash: map WaitDialog class type 0x19 to UiDialogRoot; dialog-open failure lands on the contracted refusal path

The user's first click on a mapping button killed the client: the live
dialog catalog's wait root 0x31 carries retail class type 0x19 (WaitDialog),
which DatWidgetFactory left unmapped, so the root built as a plain
UiDatElement and RetailWaitDialogView's ctor threw out of UiButton.OnClick
into the render loop. The unit test missed it by standing the confirmation
fixture (type 0x13, mapped) in for the wait root — the structural-false-
negative class again. Pins: DatWidgetFactoryTests theory for both dialog
root types, plus an installed-DAT UiDialogRoot/0x3D/0x3E assertion in the
env-gated keyboard probe. OpenCaptureInstructions now converts a dialog
construction failure into its contracted 0-return (log + capture refused,
retail's own OpenMapWarnDialog failure shape) instead of crashing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-14 13:48:29 +02:00
parent 30fa6ee507
commit 2a81e813cb
4 changed files with 44 additions and 1 deletions

View file

@ -27,6 +27,20 @@ public class DatWidgetFactoryTests
Assert.IsType<UiRadar>(e);
}
// ── Dialog roots: retail class types 0x13/0x19 → UiDialogRoot ────────────
[Theory]
[InlineData(0x13u)] // ConfirmationDialog (catalog root 0x15)
[InlineData(0x19u)] // WaitDialog (catalog root 0x31 — OP8 #396's live
// crash: unmapped type built a plain UiDatElement and
// RetailWaitDialogView's ctor threw out of OnClick)
public void DialogRootTypes_MakeUiDialogRoot(uint type)
{
var e = DatWidgetFactory.Create(
new ElementInfo { Type = type, Width = 800, Height = 600 }, NoTex, null);
Assert.IsType<UiDialogRoot>(e);
}
// ── Test 2: Unknown type → UiDatElement fallback ─────────────────────────
[Fact]

View file

@ -276,7 +276,19 @@ public sealed class KeyboardConfigLiveMountProbeTests
if (waitInfo is null)
Console.WriteLine("[kbwait] wait root 0x31 -> IMPORT MISSING from 0x2100003C");
else
{
DumpFontDids(waitInfo, 0);
// The 2026-08-14 live crash pin: the wait root (retail class
// type 0x19) must build as a UiDialogRoot with the shared
// popup/message pair — the unit suite's fixture only carries
// the confirmation subtree, so the installed-DAT shape is
// asserted here.
ImportedLayout waitBuilt = LayoutImporter.Build(
waitInfo, _ => (0u, 0, 0), null, null, strings.Resolve);
Assert.IsType<UiDialogRoot>(waitBuilt.Root);
Assert.NotNull(waitBuilt.FindElement(0x3Du));
Assert.IsType<UiText>(waitBuilt.FindElement(0x3Eu));
}
}
}