using System; using System.Collections.Generic; using System.Linq; using DatReaderWriter; using DatReaderWriter.DBObjs; using DatReaderWriter.Options; using DatReaderWriter.Types; using Xunit; using Xunit.Abstractions; namespace AcDream.App.Tests.UI; /// /// Campaign CH slice CH2, task C.7: a one-shot diagnostic sweep of every /// installed for an element of retail's SpewBox /// class (0x10000016gmSpewBoxUI::GetUIElementType @0x004D5AA0). /// Not a gate/conformance test — just a discovery tool, per the research /// doc's §8.1/§8.2 open question. Skips (does not fail) when the installed /// DAT directory is unavailable, matching every other /// ContentConformanceDats.ResolveDatDir()-gated diagnostic in this /// tree. /// /// /// RESULT (2026-08-09, run against the installed client_portal.dat): /// the installed DAT's entire LayoutDesc id space is the CLOSED range /// 0x21000000-0x21000075 (118 possible ids, 101 populated — /// sanity-checked by confirming 3 independently-known ids, 0x2100002E /// character window / 0x21000023 inventory / 0x21000016 toolbar prototype, /// are all present). The sweep is therefore EXHAUSTIVE, and it finds /// ZERO elements of class 0x10000016 anywhere in any of the /// 101 layouts, and LayoutDesc 0x10000012 — the id the decompiled /// CreateChildElementByEnum(null, 0x10000012, 0x1000004A) call /// appears to reference — exists but has zero top-level elements (it is /// not the per-line template catalog; that id must resolve through a /// different mechanism than a direct dats.Get<LayoutDesc> hit, /// which this slice did not crack). Conclusion: gmSpewBoxUI is /// mounted directly from C++ code in gmClient's HUD registration /// block (research doc §1.1) rather than resolved from any authored /// LayoutDesc tree — its position/extent/font/color/max-items are NOT /// recoverable via this dump technique. Every value the SpewBox /// presentation uses below is therefore an invented placeholder with its /// own divergence-register row, exactly as the research doc's §3.2 /// PRESENTATION-UNKNOWN section predicted. /// /// public sealed class SpewBoxLayoutDumpDiagnostic { private readonly ITestOutputHelper _out; public SpewBoxLayoutDumpDiagnostic(ITestOutputHelper output) => _out = output; private const uint SpewBoxElementClass = 0x10000016u; private const uint ListBoxElementClass = 0x10000049u; private const uint LineTemplateLayoutEnum = 0x10000012u; private const uint LineTemplateElementId = 0x1000004Au; private const uint ListBoxMaxItemsProperty = 0x10000028u; private static string? ResolveDatDir() { var fromEnv = System.Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR"); if (!string.IsNullOrWhiteSpace(fromEnv) && System.IO.Directory.Exists(fromEnv)) return fromEnv; var def = System.IO.Path.Combine( System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile), "Documents", "Asheron's Call"); return System.IO.Directory.Exists(def) ? def : null; } [Fact] public void SweepInstalledLayoutDescs_ForSpewBoxElementClass() { string? datDir = ResolveDatDir(); if (datDir is null) { _out.WriteLine("SKIP: installed retail DAT directory is unavailable."); return; } using var dats = new DatCollection(datDir, DatAccessType.Read); int scanned = 0; var hits = new List<(uint LayoutId, ElementDesc Element)>(); var allIds = dats.GetAllIdsOfType().ToList(); _out.WriteLine($"Portal.GetAllIdsOfType count: {dats.Portal.GetAllIdsOfType().Count()}"); _out.WriteLine($"HighRes.GetAllIdsOfType count: {dats.HighRes.GetAllIdsOfType().Count()}"); _out.WriteLine($"Contains known char-window 0x2100002E: {allIds.Contains(0x2100002Eu)}"); _out.WriteLine($"Contains known inventory 0x21000023: {allIds.Contains(0x21000023u)}"); _out.WriteLine($"Contains known toolbar 0x21000016: {allIds.Contains(0x21000016u)}"); _out.WriteLine($"Min id: 0x{allIds.Min():X8} Max id: 0x{allIds.Max():X8}"); foreach (uint layoutId in allIds) { scanned++; if (!dats.Portal.TryGet(layoutId, out LayoutDesc? ld) || ld is null) continue; foreach (var kv in ld.Elements) { var found = FindByType(kv.Value, SpewBoxElementClass); if (found is not null) hits.Add((layoutId, found)); } } _out.WriteLine($"Scanned {scanned} installed LayoutDescs."); _out.WriteLine($"Elements of class 0x{SpewBoxElementClass:X8} (gmSpewBoxUI): {hits.Count}"); foreach (var (layoutId, element) in hits) { _out.WriteLine( $" LayoutDesc 0x{layoutId:X8} -> element 0x{element.ElementId:X8} " + $"pos=({element.X},{element.Y}) size=({element.Width}x{element.Height}) " + $"zLevel={element.ZLevel} readOrder={element.ReadOrder} " + $"children={element.Children.Count}"); var listBox = FindByType(element, ListBoxElementClass); if (listBox is not null) { _out.WriteLine( $" ListBox child 0x{listBox.ElementId:X8} " + $"pos=({listBox.X},{listBox.Y}) size=({listBox.Width}x{listBox.Height})"); DumpProperties(listBox, " "); } else { _out.WriteLine(" (no ListBox child of class 0x10000049 found)"); } } // Also probe the presumed shared line-template catalog directly — // gmSpewBoxUI::Update's CreateChildElementByEnum(null, 0x10000012, // 0x1000004A) call implies LayoutDesc 0x10000012 hosts a template // catalog with a top-level entry 0x1000004A. if (dats.Portal.TryGet(LineTemplateLayoutEnum, out LayoutDesc? templateLd) && templateLd is not null) { _out.WriteLine($"LayoutDesc 0x{LineTemplateLayoutEnum:X8} exists ({templateLd.Elements.Count} top-level elements)."); if (templateLd.Elements.TryGetValue(LineTemplateElementId, out ElementDesc? lineTemplate)) { _out.WriteLine( $" Line template 0x{LineTemplateElementId:X8}: type=0x{lineTemplate.Type:X8} " + $"pos=({lineTemplate.X},{lineTemplate.Y}) size=({lineTemplate.Width}x{lineTemplate.Height})"); DumpProperties(lineTemplate, " "); } else { _out.WriteLine($" No top-level element 0x{LineTemplateElementId:X8} in that LayoutDesc."); } } else { _out.WriteLine($"LayoutDesc 0x{LineTemplateLayoutEnum:X8} does not exist in the installed DAT."); } // Informational only — this is a discovery sweep, not a pass/fail gate. // The findings are transcribed into WeenieErrorMessages/SpewBoxController // doc comments and the divergence register by hand after reading this // output; there is nothing here worth asserting on. } private static ElementDesc? FindByType(ElementDesc d, uint type) { if (d.Type == type) return d; foreach (var kv in d.Children) { var found = FindByType(kv.Value, type); if (found is not null) return found; } return null; } private static void DumpProperties(ElementDesc d, string indent) { if (d.StateDesc?.Properties is null) { System.Console.WriteLine($"{indent}(no direct-state properties)"); return; } foreach (var (propertyId, property) in d.StateDesc.Properties) { System.Console.WriteLine($"{indent}property 0x{propertyId:X8} = {Describe(property)}" + (propertyId == ListBoxMaxItemsProperty ? " <-- MaxConcurrentItems" : "")); } } private static string Describe(object property) => property switch { DatReaderWriter.Types.EnumBaseProperty e => $"Enum({e.Value})", DatReaderWriter.Types.DataIdBaseProperty did => $"DataId(0x{did.Value:X8})", DatReaderWriter.Types.ArrayBaseProperty arr => $"Array[{arr.Value.Count}]({string.Join(", ", arr.Value.Select(Describe))})", _ => property.ToString() ?? "?", }; }