using System.Text.Json;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using XFEEDToolBox.Controls;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Rendering;
using XFEEDToolBox.Views;
namespace XFEEDToolBox;
public partial class App
{
// This fixture only displays original icon resources in an isolated offline
// instance. It cannot bind a flight driver or read the player's Journal.
private static async Task RunGameIconProbeAsync(string reportPath)
{
var directory = Path.GetDirectoryName(Path.GetFullPath(reportPath))!;
Directory.CreateDirectory(directory);
var samples = new List<object>();
var refitSources = new List<string>();
string? failure = null;
static void Require(bool value, string message)
{
if (!value) throw new InvalidOperationException(message);
}
try
{
Require(ViewModel.OfflinePreview && Runtime.FlightDriver is null && Runtime.Journal.Sequence == 0,
"Game icon fixture requires isolated --offline-preview.");
MainWindow.AppWindow.Move(new(-30000, -30000));
MainWindow.AppWindow.Resize(new(2560, 2200));
MainWindow.AppWindow.Show(false);
var shell = AppShellPage.Current!;
shell.Navigate(typeof(CurrentSystemPage));
await Task.Delay(200);
var page = (CurrentSystemPage)shell.CurrentPage!;
var types = new (int Kind, SystemLocationCategory Category, string Name)[]
{
(1, SystemLocationCategory.Star, "HIP 12099"),
(19, SystemLocationCategory.Asteroid, "HIP 12099 A Belt Cluster 2"),
(3, SystemLocationCategory.Planet, "HIP 12099 1"),
(2, SystemLocationCategory.LandablePlanet, "HIP 12099 1 b"),
(30, SystemLocationCategory.Settlement, "Jameson Crash Site"),
(18, SystemLocationCategory.Station, "Ray Enterprise"),
(14, SystemLocationCategory.Carrier, "SPACEBALL1 | L1NK"),
(22, SystemLocationCategory.Signal, "$USS_DegradedEmissions;"),
(23, SystemLocationCategory.PointOfInterest, "$MULTIPLAYER_SCENARIO42_TITLE;"),
(0, SystemLocationCategory.PointOfInterest, "Interest point"),
(15, SystemLocationCategory.Carrier, "Squadron carrier")
};
var places = types.Select((x, i) => new SystemLocation(i + 1, (ulong)i + 100, x.Name, "", x.Kind,
x.Category, null, null, (i + 1) * 299792458d, i == 5, "icon-fixture-" + i)).ToArray();
ViewModel.ApplyCurrentSystemSnapshot(new(1, "HIP 12099", 1, 1, DateTimeOffset.UtcNow, places));
await Task.Delay(300);
page.UpdateLayout();
var list = Descendants(page).OfType<ListView>().Single();
foreach (var row in ViewModel.CurrentSystemLocations)
{
var container = list.ContainerFromItem(row) as FrameworkElement;
Require(container is not null, "Location fixture did not fit in its viewport.");
var icon = Descendants(container).OfType<GameIcon>().First();
var name = Descendants(container).OfType<TextBlock>().First(x => x.Text == row.Name);
var distance = Descendants(container).OfType<TextBlock>().First(x => x.Text == row.Distance);
var iconPosition = icon.TransformToVisual(page).TransformPoint(new());
var namePosition = name.TransformToVisual(page).TransformPoint(new());
var distancePosition = distance.TransformToVisual(page).TransformPoint(new());
Require(icon.IconKey == row.IconKey && icon.ActualWidth > 0 && icon.ActualHeight > 0,
"Location's actual XAML icon binding did not load.");
Require(iconPosition.X + icon.ActualWidth <= namePosition.X && namePosition.X + name.ActualWidth <= distancePosition.X,
"Location icon, name or distance overlaps.");
samples.Add(new { row.Name, kind = row.Record.NativeKind, icon = icon.IconKey.ToString(),
iconX = iconPosition.X, iconY = iconPosition.Y, icon.ActualWidth, icon.ActualHeight,
nameX = namePosition.X, nameRight = namePosition.X + name.ActualWidth, distanceX = distancePosition.X });
}
await CapturePageAsync(page, Path.Combine(directory, "current-system-original-icons.png"));
shell.Navigate(typeof(RefitPage));
await Task.Delay(200);
var refit = (RefitPage)shell.CurrentPage!;
var categories = (ListView)refit.FindName("RefitCategoryList");
foreach (var category in ViewModel.RefitCategories)
{
var container = categories.ContainerFromItem(category);
var source = Descendants(container).OfType<Image>().Select(x => x.Source).OfType<SvgImageSource>().Single();
Require(source.UriSource.AbsoluteUri == category.Icon, "Refit category did not bind its original game SVG.");
refitSources.Add(source.UriSource.AbsoluteUri);
}
var tabs = (SelectorBar)refit.FindName("InspectorTabs");
tabs.SelectedItem = tabs.Items[1];
await Task.Delay(100);
refit.UpdateLayout();
Require(tabs.ActualHeight <= 100 && Descendants(tabs).OfType<ImageIcon>().All(x => x.ActualWidth <= 24 && x.ActualHeight <= 24),
"Refit's engineer icon expanded the inspector tab instead of keeping its icon size.");
await CapturePageAsync(refit, Path.Combine(directory, "refit-original-icons.png"));
shell.Navigate(typeof(CurrentSystemPage));
await Task.Delay(100);
// Render the actual filter content without showing a Popup, which
// could be repositioned by Windows onto the player's foreground.
var filter = (Flyout)Descendants(page).OfType<DropDownButton>().Single().Flyout;
var filterContent = (FrameworkElement)filter.Content;
filter.Content = null;
var panel = new StackPanel { Spacing = 16 };
panel.Children.Add(new TextBlock { Text = "当前星系 · 游戏原始筛选图标", FontSize = 24 });
panel.Children.Add(filterContent);
var filterPage = new Page { Background = page.Background,
Content = new Border { Padding = new(32), Child = panel, Background = (Brush)Current.Resources["PanelBrush"] } };
MainWindow.AppWindow.Resize(new(900, 1450));
MainWindow.Content = filterPage;
await CapturePageAsync(filterPage, Path.Combine(directory, "current-system-filters.png"));
Require(Descendants(filterContent).OfType<GameIcon>().Count() == 10, "A filter category has no original icon.");
var grid = new Grid { ColumnSpacing = 16, RowSpacing = 10, Margin = new(28) };
for (var column = 0; column < 6; column++) grid.ColumnDefinitions.Add(new());
var icons = Enum.GetValues<GameUiIcon>();
for (var index = 0; index < icons.Length; index++)
{
if (index % 6 == 0) grid.RowDefinitions.Add(new() { Height = GridLength.Auto });
var card = new StackPanel { Spacing = 5, Margin = new(4), HorizontalAlignment = HorizontalAlignment.Stretch };
card.Children.Add(new GameIcon { IconKey = icons[index], Width = 46, Height = 46, HorizontalAlignment = HorizontalAlignment.Center });
card.Children.Add(new TextBlock { Text = icons[index].ToString(), FontSize = 12, TextAlignment = TextAlignment.Center,
MaxWidth = 180, TextWrapping = TextWrapping.Wrap, HorizontalAlignment = HorizontalAlignment.Center });
Grid.SetColumn(card, index % 6); Grid.SetRow(card, index / 6); grid.Children.Add(card);
}
grid.RowDefinitions.Add(new() { Height = GridLength.Auto });
var rating = new RatingControl { MaxRating = 5, Value = 3, ItemInfo = new RatingItemImageInfo {
Image = new BitmapImage(new("ms-appx:///Assets/GameIcons/EngineeringGradeSelected.png")),
UnsetImage = new BitmapImage(new("ms-appx:///Assets/GameIcons/EngineeringGradeEmpty.png")) } };
Grid.SetRow(rating, grid.RowDefinitions.Count - 1); Grid.SetColumnSpan(rating, 6); grid.Children.Add(rating);
var catalogue = new Page { Background = (Brush)Current.Resources["PanelBrush"], Content = grid };
MainWindow.AppWindow.Resize(new(2200, 1800));
MainWindow.Content = catalogue;
await CapturePageAsync(catalogue, Path.Combine(directory, "game-icon-catalogue.png"));
Require(Descendants(catalogue).OfType<GameIcon>().Count() == icons.Length, "Incomplete original icon catalogue.");
}
catch (Exception error) { failure = error.ToString(); }
await File.WriteAllTextAsync(reportPath, JsonSerializer.Serialize(new { samples, refitSources, failure, uiError = ViewModel.ErrorMessage,
fixtureOnly = true, gameChannelBound = Runtime.FlightDriver is not null, journalEvents = Runtime.Journal.Sequence },
new JsonSerializerOptions { WriteIndented = true }));
MainWindow.Close();
}
}
using System.Text.Json;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using XFEEDToolBox.Controls;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Rendering;
using XFEEDToolBox.Views;
namespace XFEEDToolBox;
public partial class App
{
// This fixture only displays original icon resources in an isolated offline
// instance. It cannot bind a flight driver or read the player's Journal.
private static async Task RunGameIconProbeAsync(string reportPath)
{
var directory = Path.GetDirectoryName(Path.GetFullPath(reportPath))!;
Directory.CreateDirectory(directory);
var samples = new List<object>();
var refitSources = new List<string>();
string? failure = null;
static void Require(bool value, string message)
{
if (!value) throw new InvalidOperationException(message);
}
try
{
Require(ViewModel.OfflinePreview && Runtime.FlightDriver is null && Runtime.Journal.Sequence == 0,
"Game icon fixture requires isolated --offline-preview.");
MainWindow.AppWindow.Move(new(-30000, -30000));
MainWindow.AppWindow.Resize(new(2560, 2200));
MainWindow.AppWindow.Show(false);
var shell = AppShellPage.Current!;
shell.Navigate(typeof(CurrentSystemPage));
await Task.Delay(200);
var page = (CurrentSystemPage)shell.CurrentPage!;
var types = new (int Kind, SystemLocationCategory Category, string Name)[]
{
(1, SystemLocationCategory.Star, "HIP 12099"),
(19, SystemLocationCategory.Asteroid, "HIP 12099 A Belt Cluster 2"),
(3, SystemLocationCategory.Planet, "HIP 12099 1"),
(2, SystemLocationCategory.LandablePlanet, "HIP 12099 1 b"),
(30, SystemLocationCategory.Settlement, "Jameson Crash Site"),
(18, SystemLocationCategory.Station, "Ray Enterprise"),
(14, SystemLocationCategory.Carrier, "SPACEBALL1 | L1NK"),
(22, SystemLocationCategory.Signal, "$USS_DegradedEmissions;"),
(23, SystemLocationCategory.PointOfInterest, "$MULTIPLAYER_SCENARIO42_TITLE;"),
(0, SystemLocationCategory.PointOfInterest, "Interest point"),
(15, SystemLocationCategory.Carrier, "Squadron carrier")
};
var places = types.Select((x, i) => new SystemLocation(i + 1, (ulong)i + 100, x.Name, "", x.Kind,
x.Category, null, null, (i + 1) * 299792458d, i == 5, "icon-fixture-" + i)).ToArray();
ViewModel.ApplyCurrentSystemSnapshot(new(1, "HIP 12099", 1, 1, DateTimeOffset.UtcNow, places));
await Task.Delay(300);
page.UpdateLayout();
var list = Descendants(page).OfType<ListView>().Single();
foreach (var row in ViewModel.CurrentSystemLocations)
{
var container = list.ContainerFromItem(row) as FrameworkElement;
Require(container is not null, "Location fixture did not fit in its viewport.");
var icon = Descendants(container).OfType<GameIcon>().First();
var name = Descendants(container).OfType<TextBlock>().First(x => x.Text == row.Name);
var distance = Descendants(container).OfType<TextBlock>().First(x => x.Text == row.Distance);
var iconPosition = icon.TransformToVisual(page).TransformPoint(new());
var namePosition = name.TransformToVisual(page).TransformPoint(new());
var distancePosition = distance.TransformToVisual(page).TransformPoint(new());
Require(icon.IconKey == row.IconKey && icon.ActualWidth > 0 && icon.ActualHeight > 0,
"Location's actual XAML icon binding did not load.");
Require(iconPosition.X + icon.ActualWidth <= namePosition.X && namePosition.X + name.ActualWidth <= distancePosition.X,
"Location icon, name or distance overlaps.");
samples.Add(new { row.Name, kind = row.Record.NativeKind, icon = icon.IconKey.ToString(),
iconX = iconPosition.X, iconY = iconPosition.Y, icon.ActualWidth, icon.ActualHeight,
nameX = namePosition.X, nameRight = namePosition.X + name.ActualWidth, distanceX = distancePosition.X });
}
await CapturePageAsync(page, Path.Combine(directory, "current-system-original-icons.png"));
shell.Navigate(typeof(RefitPage));
await Task.Delay(200);
var refit = (RefitPage)shell.CurrentPage!;
var categories = (ListView)refit.FindName("RefitCategoryList");
foreach (var category in ViewModel.RefitCategories)
{
var container = categories.ContainerFromItem(category);
var source = Descendants(container).OfType<Image>().Select(x => x.Source).OfType<SvgImageSource>().Single();
Require(source.UriSource.AbsoluteUri == category.Icon, "Refit category did not bind its original game SVG.");
refitSources.Add(source.UriSource.AbsoluteUri);
}
var tabs = (SelectorBar)refit.FindName("InspectorTabs");
tabs.SelectedItem = tabs.Items[1];
await Task.Delay(100);
refit.UpdateLayout();
Require(tabs.ActualHeight <= 100 && Descendants(tabs).OfType<ImageIcon>().All(x => x.ActualWidth <= 24 && x.ActualHeight <= 24),
"Refit's engineer icon expanded the inspector tab instead of keeping its icon size.");
await CapturePageAsync(refit, Path.Combine(directory, "refit-original-icons.png"));
shell.Navigate(typeof(CurrentSystemPage));
await Task.Delay(100);
// Render the actual filter content without showing a Popup, which
// could be repositioned by Windows onto the player's foreground.
var filter = (Flyout)Descendants(page).OfType<DropDownButton>().Single().Flyout;
var filterContent = (FrameworkElement)filter.Content;
filter.Content = null;
var panel = new StackPanel { Spacing = 16 };
panel.Children.Add(new TextBlock { Text = "当前星系 · 游戏原始筛选图标", FontSize = 24 });
panel.Children.Add(filterContent);
var filterPage = new Page { Background = page.Background,
Content = new Border { Padding = new(32), Child = panel, Background = (Brush)Current.Resources["PanelBrush"] } };
MainWindow.AppWindow.Resize(new(900, 1450));
MainWindow.Content = filterPage;
await CapturePageAsync(filterPage, Path.Combine(directory, "current-system-filters.png"));
Require(Descendants(filterContent).OfType<GameIcon>().Count() == 10, "A filter category has no original icon.");
var grid = new Grid { ColumnSpacing = 16, RowSpacing = 10, Margin = new(28) };
for (var column = 0; column < 6; column++) grid.ColumnDefinitions.Add(new());
var icons = Enum.GetValues<GameUiIcon>();
for (var index = 0; index < icons.Length; index++)
{
if (index % 6 == 0) grid.RowDefinitions.Add(new() { Height = GridLength.Auto });
var card = new StackPanel { Spacing = 5, Margin = new(4), HorizontalAlignment = HorizontalAlignment.Stretch };
card.Children.Add(new GameIcon { IconKey = icons[index], Width = 46, Height = 46, HorizontalAlignment = HorizontalAlignment.Center });
card.Children.Add(new TextBlock { Text = icons[index].ToString(), FontSize = 12, TextAlignment = TextAlignment.Center,
MaxWidth = 180, TextWrapping = TextWrapping.Wrap, HorizontalAlignment = HorizontalAlignment.Center });
Grid.SetColumn(card, index % 6); Grid.SetRow(card, index / 6); grid.Children.Add(card);
}
grid.RowDefinitions.Add(new() { Height = GridLength.Auto });
var rating = new RatingControl { MaxRating = 5, Value = 3, ItemInfo = new RatingItemImageInfo {
Image = new BitmapImage(new("ms-appx:///Assets/GameIcons/EngineeringGradeSelected.png")),
UnsetImage = new BitmapImage(new("ms-appx:///Assets/GameIcons/EngineeringGradeEmpty.png")) } };
Grid.SetRow(rating, grid.RowDefinitions.Count - 1); Grid.SetColumnSpan(rating, 6); grid.Children.Add(rating);
var catalogue = new Page { Background = (Brush)Current.Resources["PanelBrush"], Content = grid };
MainWindow.AppWindow.Resize(new(2200, 1800));
MainWindow.Content = catalogue;
await CapturePageAsync(catalogue, Path.Combine(directory, "game-icon-catalogue.png"));
Require(Descendants(catalogue).OfType<GameIcon>().Count() == icons.Length, "Incomplete original icon catalogue.");
}
catch (Exception error) { failure = error.ToString(); }
await File.WriteAllTextAsync(reportPath, JsonSerializer.Serialize(new { samples, refitSources, failure, uiError = ViewModel.ErrorMessage,
fixtureOnly = true, gameChannelBound = Runtime.FlightDriver is not null, journalEvents = Runtime.Journal.Sequence },
new JsonSerializerOptions { WriteIndented = true }));
MainWindow.Close();
}
}