diff --git a/Il2CppInspector.Common/Plugins/Internal/PluginManager.cs b/Il2CppInspector.Common/Plugins/Internal/PluginManager.cs index 0656cba..644906a 100644 --- a/Il2CppInspector.Common/Plugins/Internal/PluginManager.cs +++ b/Il2CppInspector.Common/Plugins/Internal/PluginManager.cs @@ -20,6 +20,13 @@ using Il2CppInspector.PluginAPI.V100; namespace Il2CppInspector { + public enum OptionBehaviour + { + None, + IgnoreInvalid, + NoValidation + } + // Internal settings for a plugin public class ManagedPlugin { @@ -40,6 +47,34 @@ namespace Il2CppInspector // The current stack trace of the plugin internal Stack StackTrace = new Stack(); + + // Get options as dictionary + public Dictionary GetOptions() + => Plugin.Options.ToDictionary(o => o.Name, o => o.Value); + + // Set options to values with optional validation behaviour + public void SetOptions(Dictionary 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 diff --git a/Il2CppInspector.GUI/App.xaml.cs b/Il2CppInspector.GUI/App.xaml.cs index f62c124..e246270 100644 --- a/Il2CppInspector.GUI/App.xaml.cs +++ b/Il2CppInspector.GUI/App.xaml.cs @@ -67,6 +67,7 @@ namespace Il2CppInspectorGUI public List Options { get; set; } }; + // Save state for plugin options private class PluginOptionState : IPluginOption { public string Name { get; set; } @@ -139,6 +140,7 @@ namespace Il2CppInspectorGUI var loadedPlugins = PluginManager.AsInstance.ManagedPlugins; foreach (var savedState in savedPluginState.Reverse()) { if (loadedPlugins.FirstOrDefault(p => p.Plugin.Id == savedState.Plugin.Id) is ManagedPlugin managedPlugin) { + // Re-order to match saved order loadedPlugins.Remove(managedPlugin); loadedPlugins.Insert(0, managedPlugin); @@ -147,28 +149,24 @@ namespace Il2CppInspectorGUI managedPlugin.Enabled = savedState.Enabled; // Set options - if (savedState.Plugin.Options != null) + if (savedState.Plugin.Options != null) { + var options = new Dictionary(); + foreach (var savedOption in savedState.Plugin.Options) if (managedPlugin.Plugin.Options.FirstOrDefault(o => o.Name == savedOption.Name) is IPluginOption option) { - try { - // Ignore validation - var cond = option.If; - option.If = () => false; - if (savedOption.Value == null) - option.Value = null; - else - option.Value = (savedOption.Value, ((JsonElement) savedOption.Value).ValueKind) switch { - (var v, JsonValueKind.String) => v.ToString(), - (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 { } + if (savedOption.Value == null) + options.Add(option.Name, null); + else + options.Add(option.Name, (savedOption.Value, ((JsonElement) savedOption.Value).ValueKind) switch { + (var v, JsonValueKind.String) => v.ToString(), + (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") + }); } + managedPlugin.SetOptions(options, OptionBehaviour.NoValidation); + } } } @@ -186,7 +184,7 @@ namespace Il2CppInspectorGUI 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; public bool IsExtractedFromPackage { get => isExtractedFromPackage; @@ -197,10 +195,14 @@ namespace Il2CppInspectorGUI } } + // Load options for the current image public LoadOptions ImageLoadOptions { get; private set; } + // Application models for the current image public List AppModels { get; } = new List(); + // The last exception thrown + // Reading the exception clears it private Exception _lastException; public Exception LastException { get { @@ -236,6 +238,7 @@ namespace Il2CppInspectorGUI PluginManager.StatusHandler += (s, e) => StatusUpdate(e.Plugin, "[" + e.Plugin.Name + "]\n" + e.Text); } + // Reset image load options to their defaults public void ResetLoadOptions() { ImageLoadOptions = new LoadOptions { ImageBase = 0xffffffff_ffffffff, diff --git a/Il2CppInspector.GUI/PluginConfigurationDialog.xaml.cs b/Il2CppInspector.GUI/PluginConfigurationDialog.xaml.cs index d494006..3112386 100644 --- a/Il2CppInspector.GUI/PluginConfigurationDialog.xaml.cs +++ b/Il2CppInspector.GUI/PluginConfigurationDialog.xaml.cs @@ -117,6 +117,9 @@ namespace Il2CppInspectorGUI 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() { // Adapted from https://stackoverflow.com/a/18008545 foreach (var item in lstOptions.Items) { @@ -145,17 +148,12 @@ namespace Il2CppInspectorGUI ManagedPlugin = plugin; // Copy current options - OriginalOptions = new Dictionary(Plugin.Options.ToDictionary(o => o.Name, o => o.Value)); + OriginalOptions = plugin.GetOptions(); // Validate options once they have loaded 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 private void btnFilePathSelector_Click(object sender, RoutedEventArgs e) { @@ -217,6 +215,34 @@ namespace Il2CppInspectorGUI 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 private void Window_Closing(object sender, CancelEventArgs e) { @@ -236,18 +262,6 @@ namespace Il2CppInspectorGUI 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 private void valueControl_Changed(object sender, RoutedEventArgs e) { // Ignore changes when listbox is first populated @@ -268,18 +282,5 @@ namespace Il2CppInspectorGUI 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; - } } } diff --git a/Il2CppInspector.GUI/PluginManagerDialog.xaml b/Il2CppInspector.GUI/PluginManagerDialog.xaml index a7f5b44..672ac54 100644 --- a/Il2CppInspector.GUI/PluginManagerDialog.xaml +++ b/Il2CppInspector.GUI/PluginManagerDialog.xaml @@ -1,4 +1,4 @@ - + WindowStartupLocation="CenterOwner" + Closing="Window_Closing">