From 637952f360d28ef6e203ae5b0c57e1896698c0f5 Mon Sep 17 00:00:00 2001 From: Katy Coe Date: Sat, 19 Dec 2020 20:49:39 +0100 Subject: [PATCH] GUI: Implement plugin interface --- Il2CppInspector.GUI/App.xaml.cs | 24 ++- Il2CppInspector.GUI/EqualityConverter.cs | 30 +++ .../HexStringValueConverter.cs | 11 +- .../Il2CppInspector.GUI.csproj | 3 +- Il2CppInspector.GUI/LoadOptionsDialog.xaml.cs | 7 +- Il2CppInspector.GUI/MainWindow.xaml | 9 +- Il2CppInspector.GUI/MainWindow.xaml.cs | 22 ++- .../PluginConfigurationDialog.xaml | 186 ++++++++++++++++++ .../PluginConfigurationDialog.xaml.cs | 93 +++++++++ Il2CppInspector.GUI/PluginManagerDialog.xaml | 139 +++++++++++++ .../PluginManagerDialog.xaml.cs | 112 +++++++++++ 11 files changed, 621 insertions(+), 15 deletions(-) create mode 100644 Il2CppInspector.GUI/EqualityConverter.cs create mode 100644 Il2CppInspector.GUI/PluginConfigurationDialog.xaml create mode 100644 Il2CppInspector.GUI/PluginConfigurationDialog.xaml.cs create mode 100644 Il2CppInspector.GUI/PluginManagerDialog.xaml create mode 100644 Il2CppInspector.GUI/PluginManagerDialog.xaml.cs diff --git a/Il2CppInspector.GUI/App.xaml.cs b/Il2CppInspector.GUI/App.xaml.cs index 48219ed..c78e7d3 100644 --- a/Il2CppInspector.GUI/App.xaml.cs +++ b/Il2CppInspector.GUI/App.xaml.cs @@ -1,5 +1,8 @@ -// Copyright (c) 2020 Katy Coe - https://www.djkaty.com - https://github.com/djkaty -// All rights reserved +/* + Copyright 2020 Katy Coe - http://www.djkaty.com - https://github.com/djkaty + + All rights reserved. +*/ using System; using System.Collections.Generic; @@ -22,7 +25,7 @@ namespace Il2CppInspectorGUI /// public partial class App : Application, INotifyPropertyChanged { - // Catch unhandled exceptions for debugging startup failures + // Catch unhandled exceptions for debugging startup failures and plugins public App() : base() { var np = Environment.NewLine + Environment.NewLine; @@ -30,7 +33,11 @@ namespace Il2CppInspectorGUI MessageBox.Show(e.Exception.GetType() + ": " + e.Exception.Message + np + e.Exception.StackTrace + np + "More details may follow in subsequent error boxes", - "Oopsie... Il2CppInspector could not start up"); + "Il2CppInspector encountered a fatal error"); + + if (e.Exception is FileNotFoundException fe) + MessageBox.Show("Missing file: " + fe.FileName, "Additional error information"); + var ex = e.Exception; while (ex.InnerException != null) { MessageBox.Show(ex.GetType() + ": " + ex.Message + np @@ -68,7 +75,16 @@ namespace Il2CppInspectorGUI // Initialization entry point protected override void OnStartup(StartupEventArgs e) { + // Set contents of load options window ResetLoadOptions(); + + // Set handlers for plugin manager + PluginManager.ErrorHandler += (s, e) => { + MessageBox.Show($"The plugin {e.Plugin.Name} encountered an error while executing {e.Operation}: {e.Exception.Message}." + + Environment.NewLine + Environment.NewLine + "The application will continue but may not behave as expected.", "Plugin error"); + }; + + PluginManager.StatusHandler += (s, e) => StatusUpdate(e.Plugin, "Plugin " + e.Plugin.Name + ": " + e.Text); } public void ResetLoadOptions() { diff --git a/Il2CppInspector.GUI/EqualityConverter.cs b/Il2CppInspector.GUI/EqualityConverter.cs new file mode 100644 index 0000000..2adf684 --- /dev/null +++ b/Il2CppInspector.GUI/EqualityConverter.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2020 Katy Coe - https://www.djkaty.com - https://github.com/djkaty +// All rights reserved + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows; +using System.Windows.Data; + +namespace Il2CppInspector.GUI +{ + // Adapted from https://stackoverflow.com/a/37307169 and https://stackoverflow.com/a/28316967 + internal class EqualityConverter : IMultiValueConverter + { + public object TrueValue { get; set; } + public object FalseValue { get; set; } + + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { + if (values.Length < 2) + return FalseValue; + + return values[0].Equals(values[1]) ? TrueValue : FalseValue; + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { + throw new NotImplementedException(); + } + } +} diff --git a/Il2CppInspector.GUI/HexStringValueConverter.cs b/Il2CppInspector.GUI/HexStringValueConverter.cs index d1ce1a4..a506b8a 100644 --- a/Il2CppInspector.GUI/HexStringValueConverter.cs +++ b/Il2CppInspector.GUI/HexStringValueConverter.cs @@ -16,7 +16,16 @@ namespace Il2CppInspector.GUI if (value == null || targetType != typeof(string)) return DependencyProperty.UnsetValue; - return ((ulong) value).ToString("x16"); + return value switch { + ulong n => n.ToString("x16"), + long n => n.ToString("x16"), + uint n => n.ToString("x8"), + int n => n.ToString("x8"), + ushort n => n.ToString("x4"), + short n => n.ToString("x4"), + byte n => n.ToString("x2"), + _ => throw new NotImplementedException("Unknown number format") + }; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { diff --git a/Il2CppInspector.GUI/Il2CppInspector.GUI.csproj b/Il2CppInspector.GUI/Il2CppInspector.GUI.csproj index d5df7c6..8dd6d85 100644 --- a/Il2CppInspector.GUI/Il2CppInspector.GUI.csproj +++ b/Il2CppInspector.GUI/Il2CppInspector.GUI.csproj @@ -4,7 +4,8 @@ WinExe netcoreapp3.1 true - true + + false win-x64 true Il2CppInspector diff --git a/Il2CppInspector.GUI/LoadOptionsDialog.xaml.cs b/Il2CppInspector.GUI/LoadOptionsDialog.xaml.cs index 201a561..6bd9d81 100644 --- a/Il2CppInspector.GUI/LoadOptionsDialog.xaml.cs +++ b/Il2CppInspector.GUI/LoadOptionsDialog.xaml.cs @@ -1,5 +1,8 @@ -// Copyright (c) 2020 Katy Coe - https://www.djkaty.com - https://github.com/djkaty -// All rights reserved +/* + Copyright 2020 Katy Coe - http://www.djkaty.com - https://github.com/djkaty + + All rights reserved. +*/ using System; using System.Collections.Generic; diff --git a/Il2CppInspector.GUI/MainWindow.xaml b/Il2CppInspector.GUI/MainWindow.xaml index c60e808..5999b6f 100644 --- a/Il2CppInspector.GUI/MainWindow.xaml +++ b/Il2CppInspector.GUI/MainWindow.xaml @@ -434,8 +434,13 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Il2CppInspector.GUI/PluginConfigurationDialog.xaml.cs b/Il2CppInspector.GUI/PluginConfigurationDialog.xaml.cs new file mode 100644 index 0000000..db1ab11 --- /dev/null +++ b/Il2CppInspector.GUI/PluginConfigurationDialog.xaml.cs @@ -0,0 +1,93 @@ +/* + Copyright 2020 Katy Coe - http://www.djkaty.com - https://github.com/djkaty + + All rights reserved. +*/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Microsoft.Win32; +using Il2CppInspectorGUI; +using System.Windows.Forms.Design; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using System.Linq; +using Il2CppInspector.PluginAPI.V100; +using static Il2CppInspector.PluginManager; + +namespace Il2CppInspector.GUI +{ + // Class which selects the correct control to display for each plugin option + public class OptionTemplateSelector : DataTemplateSelector + { + public DataTemplate TextTemplate { get; set; } + public DataTemplate FilePathTemplate { get; set; } + public DataTemplate NumberTemplate { get; set; } + public DataTemplate BooleanTemplate { get; set; } + public DataTemplate ChoiceTemplate { get; set; } + + // Use some fancy reflection to get the right template property + public override DataTemplate SelectTemplate(object item, DependencyObject container) { + var option = (IPluginOption) item; + return (DataTemplate) GetType().GetProperty(option.GetType().Name.Split("`")[0]["PluginOption".Length..] + "Template").GetValue(this); + } + } + + /// + /// Interaction logic for PluginConfigurationDialog.xaml + /// + public partial class PluginConfigurationDialog : Window + { + // Item to configure + public IPlugin Plugin { get; } + + public PluginConfigurationDialog(IPlugin plugin) { + InitializeComponent(); + DataContext = this; + Plugin = plugin; + } + + private void okButton_Click(object sender, RoutedEventArgs e) { + // Closes dialog box automatically + DialogResult = true; + } + + // Select a file path + private void btnFilePathSelector_Click(object sender, RoutedEventArgs e) { + + var option = (PluginOptionFilePath) ((Button) sender).DataContext; + + var openFileDialog = new OpenFileDialog { + Title = option.Description, + Filter = "All files (*.*)|*.*", + FileName = option.Value, + CheckFileExists = true + }; + + if (openFileDialog.ShowDialog() == true) { + option.Value = openFileDialog.FileName; + + // This spaghetti saves us from implementing INotifyPropertyChanged on Plugin.Options + // (we don't want to expose WPF stuff in our SDK) + // Will break if we change the format of the FilePathDataTemplate too much + var tb = ((DockPanel) ((Button) sender).Parent).Children.OfType().First(n => n.Name == "txtFilePathSelector"); + tb.Text = option.Value; + } + } + + // Only allow hex characters in hex string + private void txtHexString_PreviewTextInput(object sender, TextCompositionEventArgs e) { + e.Handled = !Regex.IsMatch(e.Text, @"[A-Fa-f0-9]"); + } + } +} diff --git a/Il2CppInspector.GUI/PluginManagerDialog.xaml b/Il2CppInspector.GUI/PluginManagerDialog.xaml new file mode 100644 index 0000000..a7f5b44 --- /dev/null +++ b/Il2CppInspector.GUI/PluginManagerDialog.xaml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/Il2CppInspector.GUI/PluginManagerDialog.xaml.cs b/Il2CppInspector.GUI/PluginManagerDialog.xaml.cs new file mode 100644 index 0000000..0788a9f --- /dev/null +++ b/Il2CppInspector.GUI/PluginManagerDialog.xaml.cs @@ -0,0 +1,112 @@ +/* + Copyright 2020 Katy Coe - http://www.djkaty.com - https://github.com/djkaty + + All rights reserved. +*/ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace Il2CppInspector.GUI +{ + /// + /// Interaction logic for PluginManagerDialog.xaml + /// + public partial class PluginManagerDialog : Window + { + public PluginManagerDialog() { + InitializeComponent(); + DataContext = PluginManager.AsInstance; + + // Set default re-order button state + lstPlugins_SelectionChanged(null, null); + } + + private void okButton_Click(object sender, RoutedEventArgs e) { + DialogResult = true; + } + + // Reload list of plugins and reset settings + private void refreshButton_Click(object sender, RoutedEventArgs e) { + PluginManager.Reload(); + } + + // Open configuration for specific plugin + private void btnConfig_Click(object sender, RoutedEventArgs e) { + var plugin = (ManagedPlugin) ((Button) sender).DataContext; + + var configDlg = new PluginConfigurationDialog(plugin.Plugin); + configDlg.Owner = this; + configDlg.ShowDialog(); + } + + // Re-ordering controls + private void lstPlugins_SelectionChanged(object sender, SelectionChangedEventArgs e) { + var index = lstPlugins.SelectedIndex; + + btnTop.IsEnabled = btnUp.IsEnabled = index > 0; + btnBottom.IsEnabled = btnDown.IsEnabled = index > -1 && index < lstPlugins.Items.Count - 1; + } + + private void btnUp_Click(object sender, RoutedEventArgs e) { + var plugins = PluginManager.AsInstance.ManagedPlugins; + + var index = lstPlugins.SelectedIndex; + var item = (ManagedPlugin) lstPlugins.SelectedItem; + + plugins.Remove(item); + plugins.Insert(index - 1, item); + lstPlugins.SelectedIndex = index - 1; + } + + private void btnDown_Click(object sender, RoutedEventArgs e) { + var plugins = PluginManager.AsInstance.ManagedPlugins; + + var index = lstPlugins.SelectedIndex; + var item = (ManagedPlugin) lstPlugins.SelectedItem; + + plugins.Remove(item); + plugins.Insert(index + 1, item); + lstPlugins.SelectedIndex = index + 1; + } + + private void btnTop_Click(object sender, RoutedEventArgs e) { + var plugins = PluginManager.AsInstance.ManagedPlugins; + + var index = lstPlugins.SelectedIndex; + var item = (ManagedPlugin) lstPlugins.SelectedItem; + + plugins.Remove(item); + plugins.Insert(0, item); + lstPlugins.SelectedIndex = 0; + } + + private void btnBottom_Click(object sender, RoutedEventArgs e) { + var plugins = PluginManager.AsInstance.ManagedPlugins; + + var index = lstPlugins.SelectedIndex; + var item = (ManagedPlugin) lstPlugins.SelectedItem; + + plugins.Remove(item); + plugins.Add(item); + lstPlugins.SelectedIndex = plugins.Count - 1; + } + + /// Get plugins button + private void getPluginsButton_Click(object sender, RoutedEventArgs e) { + Process.Start(new ProcessStartInfo { FileName = @"https://github.com/djkaty/Il2CppInspectorPlugins", UseShellExecute = true }); + } + + } +}