using System.IO; using AcDream.App.UI; using AcDream.App.UI.Layout; using AcDream.Content; using DatReaderWriter; using DatReaderWriter.Options; using StringTable = DatReaderWriter.DBObjs.StringTable; namespace AcDream.App.Tests.UI.Layout; /// /// Installed-retail-DAT acceptance gate for LA8. Opt in with /// ACDREAM_PROBE_LIVE_MOUNT=1; ACDREAM_DAT_DIR can override the /// ordinary Documents/Asheron's Call location. Reads the DATs read-only. /// public sealed class CharacterManagementLiveDatTests { [InstalledDatFact] public void EnumTable5_ResolvesAndImportsTheExactRetailScreenAndDialogs() { string datDirectory = Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR") ?? Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Documents", "Asheron's Call"); using var dats = new DatCollection(datDirectory, DatAccessType.Read); const uint expectedLayoutDid = 0x21000004u; uint layoutDid = RetailDataIdResolver.Resolve( dats, CharacterManagementUiController.RootEnum, 5u); Assert.Equal(expectedLayoutDid, layoutDid); Console.WriteLine( "[LA8-DAT] category=5 enum=0x10000005 -> DID=0x21000004; " + "selected-root=0x1000039A"); ElementInfo rootInfo = Assert.IsType( LayoutImporter.ImportInfos( dats, layoutDid, CharacterManagementUiController.RootElementId)); Assert.Equal(800f, rootInfo.Width); Assert.Equal(600f, rootInfo.Height); Assert.Equal(8, rootInfo.Children.Count); Assert.Equal(0x06007576u, rootInfo.StateMedia[""].File); ImportedLayout screen = LayoutImporter.Build( rootInfo, _ => (0u, 0, 0), null, null, new DatStringResolver(dats).Resolve); var list = Assert.IsType(screen.FindElement( CharacterManagementUiController.ListElementId)); UiTemplateListEntry template = Assert.Single(list.Templates); Assert.Equal(expectedLayoutDid, template.TemplateLayoutId); Assert.Equal(0x100003A5u, template.TemplateElementId); AssertButton(screen, CharacterManagementUiController.CreateElementId, "Create Character"); AssertButton(screen, CharacterManagementUiController.EnterElementId, "ENTER"); AssertButton(screen, CharacterManagementUiController.DeleteElementId, "DELETE"); AssertButton(screen, CharacterManagementUiController.RestoreElementId, "RESTORE"); Assert.DoesNotContain( Descendants(screen.Root), static element => element is UiViewport); ElementInfo rowInfo = Assert.IsType( LayoutImporter.ImportInfos(dats, layoutDid, template.TemplateElementId)); Assert.Equal(1u, rowInfo.Type); Assert.Equal(160f, rowInfo.Width); Assert.Equal(16f, rowInfo.Height); Assert.Equal(0x40000009u, rowInfo.FontDid); Assert.Equal( [ UiButtonStateMachine.Normal, UiButtonStateMachine.NormalRollover, UiButtonStateMachine.NormalPressed, UiButtonStateMachine.Highlight, UiButtonStateMachine.HighlightRollover, uint.MaxValue, ], rowInfo.States.Keys.Order().ToArray()); uint dialogDid = RetailDataIdResolver.Resolve(dats, 2u, 5u); Assert.Equal(0x2100003Cu, dialogDid); ImportedLayout message = BuildSelected(dats, dialogDid, 0x24u); Assert.IsType(message.Root); Assert.IsType(message.FindElement(0x3Eu)); Assert.IsType(message.FindElement(0x26u)); ImportedLayout delete = BuildSelected(dats, dialogDid, 0x2Cu); Assert.IsType(delete.Root); Assert.IsType(delete.FindElement(0x2Cu)); Assert.IsType(delete.FindElement(0x2Eu)); Assert.IsType(delete.FindElement(0x2Fu)); var strings = new DatStringResolver(dats); const uint table = 0x23000002u; Assert.Equal("DELETE", Resolve(strings, table, "ID_CharacterManagement_DeleteCharacterResponse")); Assert.Equal("Please Wait", Resolve(strings, table, "ID_CharacterManagement_PleaseWait")); Assert.Equal("Entering World", Resolve(strings, table, "ID_Character_EnteringWorld")); string confirmation = Assert.IsType(strings.ResolveTemplate( table, "ID_CharacterManagement_DeleteCharacterConfirmation", new Dictionary { [DatStringResolver.PlayerVariable] = "Test Character", })); Assert.Contains("Test Character", confirmation); Assert.Contains("'DELETE'", confirmation); StringTable stringTable = Assert.IsType(dats.Get(table)); var deleteEntry = stringTable.Strings[ DatStringResolver.ComputeHash( "ID_CharacterManagement_DeleteCharacterConfirmation")]; Assert.Equal([DatStringResolver.PlayerVariable], deleteEntry.Variables); } private static ImportedLayout BuildSelected( IDatReaderWriter dats, uint layoutDid, uint rootId) { ElementInfo info = Assert.IsType( LayoutImporter.ImportInfos(dats, layoutDid, rootId)); return LayoutImporter.Build( info, _ => (0u, 0, 0), null, null, new DatStringResolver(dats).Resolve); } private static string Resolve( DatStringResolver strings, uint table, string key) => Assert.IsType(strings.Resolve( table, DatStringResolver.ComputeHash(key))); private static void AssertButton( ImportedLayout layout, uint elementId, string label) => Assert.Equal( label, Assert.IsType(layout.FindElement(elementId)).Label); private static IEnumerable Descendants(UiElement root) { yield return root; foreach (UiElement child in root.Children) foreach (UiElement descendant in Descendants(child)) yield return descendant; } } internal sealed class InstalledDatFactAttribute : FactAttribute { public InstalledDatFactAttribute() { if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_LIVE_MOUNT") != "1") { Skip = "Set ACDREAM_PROBE_LIVE_MOUNT=1 to run the installed-DAT LA8 gate."; return; } string datDirectory = Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR") ?? Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Documents", "Asheron's Call"); if (!File.Exists(Path.Combine(datDirectory, "client_portal.dat"))) Skip = $"Installed client_portal.dat is required at '{datDirectory}'."; } }