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

@ -122,6 +122,7 @@ public static class DatWidgetFactory
11 => BuildScrollbar(info, resolve), // UIElement_Scrollbar (reg :124137)
12 => BuildText(info, resolve, elementFont, stringResolve), // UIElement_Text
0x13 => new UiDialogRoot(), // ConfirmationDialog
0x19 => new UiDialogRoot(), // WaitDialog (catalog root 0x31 — OP8 #396)
0x10000031u => new UiItemList(resolve), // UIElement_ItemList — toolbar/inventory/paperdoll slots
0x10000035u => BuildCheckbox(
info, resolve, elementFont, fontResolve, stringResolve), // UIOption_Checkbox

View file

@ -2651,7 +2651,23 @@ public sealed class RetailUiRuntime : IDisposable
// convention DatWidgetFactory/IndicatorDetailText already
// unescape for other DAT-authored strings.
text = text.Replace("\\n", "\n", StringComparison.Ordinal);
return DialogFactory.MakeWait(text, queueKey: 0x10000001u);
try
{
return DialogFactory.MakeWait(text, queueKey: 0x10000001u);
}
catch (Exception failure)
{
// Retail's OpenMapWarnDialog returns 0 on failure and
// InitiateBinding then refuses to arm capture — a
// malformed catalog must land on that contracted path,
// not escape UiButton.OnClick into the render loop
// (the 2026-08-14 live crash: wait root type 0x19 was
// unmapped and the view ctor threw).
Console.WriteLine(
"[UI] keyboard config: capture-instruction dialog failed to "
+ $"build — capture refused. {failure.Message}");
return 0u;
}
},
CloseCaptureInstructions: context =>
DialogFactory?.CloseDialog(context)),