using System.IO; using AcDream.App.UI.Layout; using DatReaderWriter; using DatReaderWriter.Options; namespace AcDream.App.Tests.UI.Layout; /// /// Campaign FA lane-A unknown U2 (docs/research/2026-08-11-fa-panel-structure.md /// §8): which of the 16 gmPanelUI page-stack slots host the Fellowship and /// Allegiance panels, and each slot's authored P0x10000029 panel id. Dumps the /// full 16-row table against the live DATs — closing U2 (and the ten /// unidentified RetailPanelCatalog entries) in one pass. Signature elements /// identify the two FA slots definitively: 0x1000026F (fellowship name field) /// and 0x10000263 (swear-allegiance button). Env-gated like the other probes. /// public sealed class FaPanelSlotProbeTests { [Fact] public void ProbePanelSlotTable() { if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_LIVE_MOUNT") != "1") return; var datDir = Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR") ?? Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Documents", "Asheron's Call"); using var dats = new DatCollection(datDir, DatAccessType.Read); // The 16 authored slot ids, in gmPanelUI::SetupChildren call order // (lane-A doc §2.2), plus the conspicuously-absent 0x10000186. foreach (uint slotId in new[] { 0x1000018Bu, 0x1000018Fu, 0x1000018Eu, 0x10000559u, 0x1000018Cu, 0x10000182u, 0x1000018Du, 0x10000190u, 0x10000184u, 0x10000185u, 0x10000181u, 0x10000189u, 0x10000183u, 0x1000018Au, 0x10000187u, 0x10000188u, 0x10000186u, }) { ElementInfo? slot = LayoutImporter.ImportInfos(dats, 0x2100006Eu, slotId); if (slot is null) { Console.WriteLine($"[faslot] 0x{slotId:X8} -> IMPORT NULL"); continue; } string panelId = slot.TryGetEffectiveProperty(0x10000029u, out var p) ? $"{p.UnsignedValue} (kind={p.Kind})" : "ABSENT"; bool hasFellowshipName = FindInfo(slot, 0x1000026Fu); // Allegiance signature set (lane A/C ids): swear 0x10000263, // break 0x10000264, kick 0x10000265, row texts 0x10000268/69, // and the doubled 0x10000492 monarch/patron child. bool hasAllegiance = FindInfo(slot, 0x10000263u) || FindInfo(slot, 0x10000264u) || FindInfo(slot, 0x10000265u) || FindInfo(slot, 0x10000268u) || FindInfo(slot, 0x10000269u) || FindInfo(slot, 0x10000492u); string family = hasFellowshipName ? " <= FELLOWSHIP" : hasAllegiance ? " <= ALLEGIANCE" : string.Empty; Console.WriteLine( $"[faslot] 0x{slotId:X8} panelId={panelId} type={slot.Type} " + $"({slot.X},{slot.Y} {slot.Width}x{slot.Height}) " + $"children={slot.Children.Count}{family}"); // The unidentified slots: dump two levels of child ids so the // allegiance panel can be identified by SHAPE if its recovered // element ids are absent from the base-merge. bool unidentified = slotId is 0x1000018Fu; if (unidentified) { foreach (ElementInfo c in slot.Children) { Console.WriteLine( $"[faslot] child 0x{c.Id:X8} type={c.Type} ({c.X},{c.Y} {c.Width}x{c.Height}) kids={c.Children.Count}"); foreach (ElementInfo g in c.Children) Console.WriteLine( $"[faslot] g 0x{g.Id:X8} type={g.Type} ({g.X},{g.Y} {g.Width}x{g.Height})"); } } } } private static bool FindInfo(ElementInfo info, uint id) { if (info.Id == id) return true; foreach (ElementInfo c in info.Children) if (FindInfo(c, id)) return true; return false; } }