Plugins/GUI: Tidy up

This commit is contained in:
Katy Coe
2020-12-29 21:05:33 +01:00
parent 466531f3bd
commit 3e3818a2ed
5 changed files with 100 additions and 56 deletions

View File

@@ -20,6 +20,13 @@ using Il2CppInspector.PluginAPI.V100;
namespace Il2CppInspector namespace Il2CppInspector
{ {
public enum OptionBehaviour
{
None,
IgnoreInvalid,
NoValidation
}
// Internal settings for a plugin // Internal settings for a plugin
public class ManagedPlugin public class ManagedPlugin
{ {
@@ -40,6 +47,34 @@ namespace Il2CppInspector
// The current stack trace of the plugin // The current stack trace of the plugin
internal Stack<string> StackTrace = new Stack<string>(); internal Stack<string> StackTrace = new Stack<string>();
// Get options as dictionary
public Dictionary<string, object> GetOptions()
=> Plugin.Options.ToDictionary(o => o.Name, o => o.Value);
// Set options to values with optional validation behaviour
public void SetOptions(Dictionary<string, object> options, OptionBehaviour behaviour = OptionBehaviour.None) {
foreach (var option in options)
// Throw an exception on the first invalid option
if (behaviour == OptionBehaviour.None)
this[option.Key] = option.Value;
// Don't set invalid options but don't throw an exception either
else if (behaviour == OptionBehaviour.IgnoreInvalid)
try {
this[option.Key] = option.Value;
} catch { }
// Force set options with no validation
else if (behaviour == OptionBehaviour.NoValidation) {
if (Plugin.Options.FirstOrDefault(o => o.Name == option.Key) is IPluginOption target) {
var validationCondition = target.If;
target.If = () => false;
target.Value = option.Value;
target.If = validationCondition;
}
}
}
} }
// Event arguments for error handler // Event arguments for error handler

View File

@@ -67,6 +67,7 @@ namespace Il2CppInspectorGUI
public List<IPluginOption> Options { get; set; } public List<IPluginOption> Options { get; set; }
}; };
// Save state for plugin options
private class PluginOptionState : IPluginOption private class PluginOptionState : IPluginOption
{ {
public string Name { get; set; } public string Name { get; set; }
@@ -139,6 +140,7 @@ namespace Il2CppInspectorGUI
var loadedPlugins = PluginManager.AsInstance.ManagedPlugins; var loadedPlugins = PluginManager.AsInstance.ManagedPlugins;
foreach (var savedState in savedPluginState.Reverse()) { foreach (var savedState in savedPluginState.Reverse()) {
if (loadedPlugins.FirstOrDefault(p => p.Plugin.Id == savedState.Plugin.Id) is ManagedPlugin managedPlugin) { if (loadedPlugins.FirstOrDefault(p => p.Plugin.Id == savedState.Plugin.Id) is ManagedPlugin managedPlugin) {
// Re-order to match saved order // Re-order to match saved order
loadedPlugins.Remove(managedPlugin); loadedPlugins.Remove(managedPlugin);
loadedPlugins.Insert(0, managedPlugin); loadedPlugins.Insert(0, managedPlugin);
@@ -147,28 +149,24 @@ namespace Il2CppInspectorGUI
managedPlugin.Enabled = savedState.Enabled; managedPlugin.Enabled = savedState.Enabled;
// Set options // Set options
if (savedState.Plugin.Options != null) if (savedState.Plugin.Options != null) {
var options = new Dictionary<string, object>();
foreach (var savedOption in savedState.Plugin.Options) foreach (var savedOption in savedState.Plugin.Options)
if (managedPlugin.Plugin.Options.FirstOrDefault(o => o.Name == savedOption.Name) is IPluginOption option) { if (managedPlugin.Plugin.Options.FirstOrDefault(o => o.Name == savedOption.Name) is IPluginOption option) {
try { if (savedOption.Value == null)
// Ignore validation options.Add(option.Name, null);
var cond = option.If; else
option.If = () => false; options.Add(option.Name, (savedOption.Value, ((JsonElement) savedOption.Value).ValueKind) switch {
if (savedOption.Value == null) (var v, JsonValueKind.String) => v.ToString(),
option.Value = null; (var v, JsonValueKind.Number) => Convert.ChangeType(v.ToString(), option.Value.GetType()),
else (var v, JsonValueKind.True) => true,
option.Value = (savedOption.Value, ((JsonElement) savedOption.Value).ValueKind) switch { (var v, JsonValueKind.False) => false,
(var v, JsonValueKind.String) => v.ToString(), _ => throw new ArgumentException("Unsupported JSON type")
(var v, JsonValueKind.Number) => Convert.ChangeType(v.ToString(), option.Value.GetType()), });
(var v, JsonValueKind.True) => true,
(var v, JsonValueKind.False) => false,
_ => throw new ArgumentException("Unsupported JSON type")
};
option.If = cond;
}
// Ignore invalid values
catch { }
} }
managedPlugin.SetOptions(options, OptionBehaviour.NoValidation);
}
} }
} }
@@ -186,7 +184,7 @@ namespace Il2CppInspectorGUI
private Metadata metadata; private Metadata metadata;
// True if we extracted from an APK, IPA, zip file etc. // True if we extracted the current workload from an APK, IPA, zip file etc.
private bool isExtractedFromPackage; private bool isExtractedFromPackage;
public bool IsExtractedFromPackage { public bool IsExtractedFromPackage {
get => isExtractedFromPackage; get => isExtractedFromPackage;
@@ -197,10 +195,14 @@ namespace Il2CppInspectorGUI
} }
} }
// Load options for the current image
public LoadOptions ImageLoadOptions { get; private set; } public LoadOptions ImageLoadOptions { get; private set; }
// Application models for the current image
public List<AppModel> AppModels { get; } = new List<AppModel>(); public List<AppModel> AppModels { get; } = new List<AppModel>();
// The last exception thrown
// Reading the exception clears it
private Exception _lastException; private Exception _lastException;
public Exception LastException { public Exception LastException {
get { get {
@@ -236,6 +238,7 @@ namespace Il2CppInspectorGUI
PluginManager.StatusHandler += (s, e) => StatusUpdate(e.Plugin, "[" + e.Plugin.Name + "]\n" + e.Text); PluginManager.StatusHandler += (s, e) => StatusUpdate(e.Plugin, "[" + e.Plugin.Name + "]\n" + e.Text);
} }
// Reset image load options to their defaults
public void ResetLoadOptions() { public void ResetLoadOptions() {
ImageLoadOptions = new LoadOptions { ImageLoadOptions = new LoadOptions {
ImageBase = 0xffffffff_ffffffff, ImageBase = 0xffffffff_ffffffff,

View File

@@ -117,6 +117,9 @@ namespace Il2CppInspectorGUI
ValidateAllOptions(); ValidateAllOptions();
} }
// Force each ListBoxItem to set its source property
// This will force errors to appear in the dialog box when exceptions are thrown
// Only needed when the window first opens or the plugin object changes
private void ValidateAllOptions() { private void ValidateAllOptions() {
// Adapted from https://stackoverflow.com/a/18008545 // Adapted from https://stackoverflow.com/a/18008545
foreach (var item in lstOptions.Items) { foreach (var item in lstOptions.Items) {
@@ -145,17 +148,12 @@ namespace Il2CppInspectorGUI
ManagedPlugin = plugin; ManagedPlugin = plugin;
// Copy current options // Copy current options
OriginalOptions = new Dictionary<string, object>(Plugin.Options.ToDictionary(o => o.Name, o => o.Value)); OriginalOptions = plugin.GetOptions();
// Validate options once they have loaded // Validate options once they have loaded
lstOptions.ItemContainerGenerator.StatusChanged += OptionsListBoxStatusChanged; lstOptions.ItemContainerGenerator.StatusChanged += OptionsListBoxStatusChanged;
} }
private void okButton_Click(object sender, RoutedEventArgs e) {
// Close dialog box but call OnClosing first to validate all the options
DialogResult = true;
}
// Select a file path // Select a file path
private void btnFilePathSelector_Click(object sender, RoutedEventArgs e) { private void btnFilePathSelector_Click(object sender, RoutedEventArgs e) {
@@ -217,6 +215,34 @@ namespace Il2CppInspectorGUI
e.Handled = !Regex.IsMatch(e.Text, @"[A-Fa-f0-9]"); e.Handled = !Regex.IsMatch(e.Text, @"[A-Fa-f0-9]");
} }
// Reset a plugin's settings
private void resetButton_Click(object sender, RoutedEventArgs e) {
// Replace plugin object with a new one (updates ManagedPlugin.Plugin)
PluginManager.Reset(Plugin);
// Validate options once they have loaded
lstOptions.ItemContainerGenerator.StatusChanged += OptionsListBoxStatusChanged;
// Replace options in ListBox
lstOptions.ItemsSource = Plugin.Options;
}
// Close dialog box but call OnClosing first to validate all the options
private void okButton_Click(object sender, RoutedEventArgs e) {
DialogResult = true;
}
// Close dialog box, reverting changes
private void cancelButton_Click(object sender, RoutedEventArgs e) {
// Revert changes
ManagedPlugin.SetOptions(OriginalOptions, OptionBehaviour.IgnoreInvalid);
// Replace options in ListBox
lstOptions.ItemsSource = Plugin.Options;
DialogResult = false;
}
// Check options validity before allowing the dialog to close either by clicking OK or the close icon // Check options validity before allowing the dialog to close either by clicking OK or the close icon
private void Window_Closing(object sender, CancelEventArgs e) { private void Window_Closing(object sender, CancelEventArgs e) {
@@ -236,18 +262,6 @@ namespace Il2CppInspectorGUI
e.Cancel = true; e.Cancel = true;
} }
// Reset a plugin's settings
private void resetButton_Click(object sender, RoutedEventArgs e) {
// Get new context (updates ManagedPlugin)
PluginManager.Reset(Plugin);
// Validate options once they have loaded
lstOptions.ItemContainerGenerator.StatusChanged += OptionsListBoxStatusChanged;
// Replace options in ListBox
lstOptions.ItemsSource = Plugin.Options;
}
// Force all the If evaluations on each option to be re-evaluated each time an option is changed // Force all the If evaluations on each option to be re-evaluated each time an option is changed
private void valueControl_Changed(object sender, RoutedEventArgs e) { private void valueControl_Changed(object sender, RoutedEventArgs e) {
// Ignore changes when listbox is first populated // Ignore changes when listbox is first populated
@@ -268,18 +282,5 @@ namespace Il2CppInspectorGUI
ValidateAllOptions(); ValidateAllOptions();
} }
private void cancelButton_Click(object sender, RoutedEventArgs e) {
// Revert changes
foreach (var option in OriginalOptions)
try {
ManagedPlugin[option.Key] = option.Value;
} catch { }
// Replace options in ListBox
lstOptions.ItemsSource = Plugin.Options;
DialogResult = false;
}
} }
} }

View File

@@ -1,4 +1,4 @@
<Window x:Class="Il2CppInspector.GUI.PluginManagerDialog" <Window x:Class="Il2CppInspectorGUI.PluginManagerDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -6,7 +6,8 @@
xmlns:local="clr-namespace:Il2CppInspector.GUI" xmlns:local="clr-namespace:Il2CppInspector.GUI"
mc:Ignorable="d" mc:Ignorable="d"
Title="Plugin Manager" Height="450" Width="800" Title="Plugin Manager" Height="450" Width="800"
WindowStartupLocation="CenterOwner"> WindowStartupLocation="CenterOwner"
Closing="Window_Closing">
<Window.Resources> <Window.Resources>
<!-- Configure ListBox to display plugins nicely --> <!-- Configure ListBox to display plugins nicely -->
<Style x:Key="ConfigItemStyle" TargetType="{x:Type ListBoxItem}"> <Style x:Key="ConfigItemStyle" TargetType="{x:Type ListBoxItem}">

View File

@@ -17,8 +17,9 @@ using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Shapes; using System.Windows.Shapes;
using Il2CppInspector;
namespace Il2CppInspector.GUI namespace Il2CppInspectorGUI
{ {
/// <summary> /// <summary>
/// Interaction logic for PluginManagerDialog.xaml /// Interaction logic for PluginManagerDialog.xaml
@@ -33,10 +34,13 @@ namespace Il2CppInspector.GUI
lstPlugins_SelectionChanged(null, null); lstPlugins_SelectionChanged(null, null);
} }
// Save options whether the user clicked OK or the close icon
private void Window_Closing(object sender, CancelEventArgs e) {
((Il2CppInspectorGUI.App) Application.Current).SaveOptions();
}
private void okButton_Click(object sender, RoutedEventArgs e) { private void okButton_Click(object sender, RoutedEventArgs e) {
DialogResult = true; DialogResult = true;
((Il2CppInspectorGUI.App) Application.Current).SaveOptions();
} }
// Reload list of plugins and reset settings // Reload list of plugins and reset settings