From e929d8d97ec6ddc8ed0217e64cf4803b11cdd80a Mon Sep 17 00:00:00 2001 From: Katy Coe Date: Fri, 25 Dec 2020 22:29:31 +0100 Subject: [PATCH] Plugins: Add LoadPipelineStarting / LoadPipelineEnding, rename IsHandled to FullyProcessed --- .../IL2CPP/Il2CppInspector.cs | 13 ++++++++- Il2CppInspector.Common/IL2CPP/Metadata.cs | 8 +++-- .../Plugins/API/V100/ILoadPipeline.cs | 13 +++++++++ .../Plugins/API/V100/PluginEventInfo.cs | 22 ++++++++++---- Il2CppInspector.Common/Plugins/PluginHooks.cs | 8 ++++- .../Plugins/PluginManager.cs | 2 +- Il2CppInspector.GUI/App.xaml.cs | 29 +++++++------------ 7 files changed, 66 insertions(+), 29 deletions(-) diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs index 91a441c..943d04f 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs @@ -572,6 +572,15 @@ namespace Il2CppInspector } // Multi-image binaries may contain more than one Il2Cpp image + var inspectors = LoadFromStream(stream, metadata, statusCallback); + + Console.SetOut(stdout); + + return inspectors; + } + + public static List LoadFromStream(IFileFormatStream stream, Metadata metadata, EventHandler statusCallback = null) { + var processors = new List(); foreach (var image in stream.Images) { Console.WriteLine("Container format: " + image.Format); @@ -598,7 +607,9 @@ namespace Il2CppInspector } } - Console.SetOut(stdout); + // Plugin hook LoadPipelineEnding + PluginHooks.LoadPipelineEnding(processors); + return processors; } diff --git a/Il2CppInspector.Common/IL2CPP/Metadata.cs b/Il2CppInspector.Common/IL2CPP/Metadata.cs index 7ec3816..1612095 100644 --- a/Il2CppInspector.Common/IL2CPP/Metadata.cs +++ b/Il2CppInspector.Common/IL2CPP/Metadata.cs @@ -54,6 +54,10 @@ namespace Il2CppInspector // Initialize metadata object from a stream public static Metadata FromStream(MemoryStream stream, EventHandler statusCallback = null) { + // TODO: This should really be placed before the Metadata object is created, + // but for now this ensures it is called regardless of which client is in use + PluginHooks.LoadPipelineStarting(); + var metadata = new Metadata(statusCallback); stream.Position = 0; stream.CopyTo(metadata); @@ -173,7 +177,7 @@ namespace Il2CppInspector // Get all metadata strings var pluginGetStringsResult = PluginHooks.GetStrings(this); - if (pluginGetStringsResult.IsHandled) + if (pluginGetStringsResult.FullyProcessed && !pluginGetStringsResult.IsInvalid) Strings = pluginGetStringsResult.Strings; else { @@ -243,7 +247,7 @@ namespace Il2CppInspector // Get all string literals var pluginGetStringLiteralsResult = PluginHooks.GetStringLiterals(this); - if (pluginGetStringLiteralsResult.IsHandled) + if (pluginGetStringLiteralsResult.FullyProcessed) StringLiterals = pluginGetStringLiteralsResult.StringLiterals.ToArray(); else { diff --git a/Il2CppInspector.Common/Plugins/API/V100/ILoadPipeline.cs b/Il2CppInspector.Common/Plugins/API/V100/ILoadPipeline.cs index cac8f48..f16768c 100644 --- a/Il2CppInspector.Common/Plugins/API/V100/ILoadPipeline.cs +++ b/Il2CppInspector.Common/Plugins/API/V100/ILoadPipeline.cs @@ -6,6 +6,7 @@ using NoisyCowStudios.Bin2Object; using Il2CppInspector.Reflection; +using System.Collections.Generic; namespace Il2CppInspector.PluginAPI.V100 { @@ -16,6 +17,11 @@ namespace Il2CppInspector.PluginAPI.V100 /// public interface ILoadPipeline { + /// + /// A new load task is about to start. Perform per-load initialization here + /// + void LoadPipelineStarting(PluginLoadPipelineStartingEventInfo info) { } + /// /// Process global-metadata.dat when it is first opened as a sequence of bytes /// Seek cursor will be at the start of the file @@ -42,6 +48,13 @@ namespace Il2CppInspector.PluginAPI.V100 /// void PostProcessPackage(Il2CppInspector package, PluginPostProcessPackageEventInfo data) { } + /// + /// The current load task has finished. Perform per-load teardown code here + /// One Il2CppInspector per sub-image in the binary is supplied + /// We have closed all open files but the .NET type model has not been created yet + /// + void LoadPipelineEnding(List packages, PluginLoadPipelineEndingEventInfo info) { } + /// /// Post-process the .NET type model to make changes after it has been fully created /// diff --git a/Il2CppInspector.Common/Plugins/API/V100/PluginEventInfo.cs b/Il2CppInspector.Common/Plugins/API/V100/PluginEventInfo.cs index a9eeb1d..8b03d73 100644 --- a/Il2CppInspector.Common/Plugins/API/V100/PluginEventInfo.cs +++ b/Il2CppInspector.Common/Plugins/API/V100/PluginEventInfo.cs @@ -12,7 +12,7 @@ namespace Il2CppInspector.PluginAPI.V100 { /// /// Object which allows plugins to report on what has happened during a call - /// Changes made to this object propagate to the next plugin in the call chain until IsHandled is set to true + /// Changes made to this object propagate to the next plugin in the call chain until FullyProcessed is set to true /// public class PluginEventInfo { @@ -21,25 +21,25 @@ namespace Il2CppInspector.PluginAPI.V100 /// Generally, this will prevent other plugins from processing the data /// Note that this should be set even if the processed data was invalid () /// If this is not set, the same event will be raised on the next available plugin - /// Note that you can still do processing but set IsHandled to false to allow additional processing from other plugins + /// Note that you can still do processing but set FullyProcessed to false to allow additional processing from other plugins /// - public bool IsHandled { get; set; } = false; + public bool FullyProcessed { get; set; } = false; /// - /// A plugin should set this when IsHandled = true but the data it processed was invalid, for example if the processing gave an unexpected result + /// A plugin should set this when the data it processed was invalid, for example if the processing gave an unexpected result /// public bool IsInvalid { get; set; } = false; /// /// A plugin should set this when it has directly modified the provided data structure (object) - /// This can be set even if IsHandled = false to indicate that changes have been made but more plugins can still be called + /// This can be set even if FullyProcessed = false to indicate that changes have been made but more plugins can still be called /// Should be set to false if you have only queried (performed reads) on the data without changing it /// public bool IsDataModified { get; set; } = false; /// /// A plugin should set this when it has directly modified the supplied or underlying stream for the metadata or binary - /// This can be set even if IsHandled = false to indicate that changes have been made but more plugins can still be called + /// This can be set even if FullyProcessed = false to indicate that changes have been made but more plugins can still be called /// Should be set to false if you have only queried (performed reads) on the stream without changing it /// public bool IsStreamModified { get; set; } = false; @@ -50,6 +50,11 @@ namespace Il2CppInspector.PluginAPI.V100 public PluginErrorEventArgs Error { get; set; } = null; } + /// + /// Event info for LoadPipelineStarting + /// + public class PluginLoadPipelineStartingEventInfo : PluginEventInfo { } + /// /// Event info for PreProcessMetadata /// @@ -102,4 +107,9 @@ namespace Il2CppInspector.PluginAPI.V100 /// Event info for PostProcessTypeModel /// public class PluginPostProcessTypeModelEventInfo : PluginEventInfo { } + + /// + /// Event info for LoadPipelineEnding + /// + public class PluginLoadPipelineEndingEventInfo : PluginEventInfo { } } diff --git a/Il2CppInspector.Common/Plugins/PluginHooks.cs b/Il2CppInspector.Common/Plugins/PluginHooks.cs index 7810cc5..0e67868 100644 --- a/Il2CppInspector.Common/Plugins/PluginHooks.cs +++ b/Il2CppInspector.Common/Plugins/PluginHooks.cs @@ -4,6 +4,7 @@ All rights reserved. */ +using System.Collections.Generic; using Il2CppInspector.Reflection; using NoisyCowStudios.Bin2Object; @@ -16,6 +17,9 @@ namespace Il2CppInspector // Does not include hooks that should be called individually, eg. OptionsChanged internal static class PluginHooks { + public static PluginLoadPipelineStartingEventInfo LoadPipelineStarting() + => PluginManager.Try((p, e) => p.LoadPipelineStarting(e)); + public static PluginPreProcessMetadataEventInfo PreProcessMetadata(BinaryObjectStream stream) => PluginManager.Try((p, e) => { stream.Position = 0; @@ -34,8 +38,10 @@ namespace Il2CppInspector public static PluginPostProcessPackageEventInfo PostProcessPackage(Il2CppInspector package) => PluginManager.Try((p, e) => p.PostProcessPackage(package, e)); + public static PluginLoadPipelineEndingEventInfo LoadPipelineEnding(List packages) + => PluginManager.Try((p, e) => p.LoadPipelineEnding(packages, e)); + public static PluginPostProcessTypeModelEventInfo PostProcessTypeModel(TypeModel typeModel) => PluginManager.Try((p, e) => p.PostProcessTypeModel(typeModel, e)); - } } diff --git a/Il2CppInspector.Common/Plugins/PluginManager.cs b/Il2CppInspector.Common/Plugins/PluginManager.cs index dd883d0..8864103 100644 --- a/Il2CppInspector.Common/Plugins/PluginManager.cs +++ b/Il2CppInspector.Common/Plugins/PluginManager.cs @@ -258,7 +258,7 @@ namespace Il2CppInspector try { action(p, eventInfo); - if (eventInfo.IsHandled) + if (eventInfo.FullyProcessed) break; } catch (Exception ex) { diff --git a/Il2CppInspector.GUI/App.xaml.cs b/Il2CppInspector.GUI/App.xaml.cs index f8e4e67..d974684 100644 --- a/Il2CppInspector.GUI/App.xaml.cs +++ b/Il2CppInspector.GUI/App.xaml.cs @@ -88,7 +88,7 @@ namespace Il2CppInspectorGUI MessageBox.Show("Could not update plugin options. " + e.Error.Exception.Message, "Plugin error"); else MessageBox.Show($"The plugin {e.Error.Plugin.Name} encountered an error while executing {e.Error.Operation}: {e.Error.Exception.Message}." - + Environment.NewLine + Environment.NewLine + "The application will continue but may not behave as expected.", "Plugin error"); + + Environment.NewLine + Environment.NewLine + "Plugin has been disabled.", "Plugin error"); }; PluginManager.StatusHandler += (s, e) => StatusUpdate(e.Plugin, "[" + e.Plugin.Name + "]\n" + e.Text); @@ -163,26 +163,19 @@ namespace Il2CppInspectorGUI throw new InvalidOperationException("Could not find any binary images in the file"); } - // Multi-image binaries may contain more than one Il2Cpp image + // Get an Il2CppInspector for each image + var inspectors = Inspector.LoadFromStream(stream, metadata, StatusUpdate); + AppModels.Clear(); - foreach (var image in stream.Images) { - // Architecture-agnostic load attempt - try { - // If we can't load the IL2CPP data here, it's probably packed or obfuscated; ignore it - if (Il2CppBinary.Load(image, metadata, StatusUpdate) is Il2CppBinary binary) { - var inspector = new Inspector(binary, metadata); - // Build type model - OnStatusUpdate?.Invoke(this, $"Building .NET type model for {image.Format}/{image.Arch} image"); - var typeModel = new TypeModel(inspector); + foreach (var inspector in inspectors) { + // Build type model + OnStatusUpdate?.Invoke(this, $"Building .NET type model for {inspector.BinaryImage.Format}/{inspector.BinaryImage.Arch} image"); + var typeModel = new TypeModel(inspector); - // Initialize (but don't build) application model - // We will build the model after the user confirms the Unity version and target compiler - AppModels.Add(new AppModel(typeModel, makeDefaultBuild: false)); - } - } - // Unsupported architecture; ignore it - catch (NotImplementedException) { } + // Initialize (but don't build) application model + // We will build the model after the user confirms the Unity version and target compiler + AppModels.Add(new AppModel(typeModel, makeDefaultBuild: false)); } if (!AppModels.Any()) { throw new InvalidOperationException("Could not auto-detect any IL2CPP binary images in the file. This may mean the binary file is packed, encrypted or obfuscated, that the file is not an IL2CPP image or that Il2CppInspector was not able to automatically find the required data. Please check the binary file in a disassembler to ensure that it is an unencrypted IL2CPP binary before submitting a bug report!");