Initial commit of new UI c# component
This commit is contained in:
8
Il2CppInspector.Redux.GUI/AppJsonSerializerContext.cs
Normal file
8
Il2CppInspector.Redux.GUI/AppJsonSerializerContext.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.GUI;
|
||||||
|
|
||||||
|
[JsonSerializable(typeof(string))]
|
||||||
|
[JsonSerializable(typeof(List<string>))]
|
||||||
|
[JsonSerializable(typeof(Dictionary<string, string>))]
|
||||||
|
internal partial class AppJsonSerializerContext : JsonSerializerContext;
|
||||||
21
Il2CppInspector.Redux.GUI/DictionaryExtensions.cs
Normal file
21
Il2CppInspector.Redux.GUI/DictionaryExtensions.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
namespace Il2CppInspector.Redux.GUI;
|
||||||
|
|
||||||
|
public static class DictionaryExtensions
|
||||||
|
{
|
||||||
|
public static bool GetAsBooleanOrDefault(this Dictionary<string, string> dict, string key, bool defaultValue)
|
||||||
|
{
|
||||||
|
if (dict.TryGetValue(key, out var value) && bool.TryParse(value, out var boolResult))
|
||||||
|
return boolResult;
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T GetAsEnumOrDefault<T>(this Dictionary<string, string> dict, string key, T defaultValue)
|
||||||
|
where T : struct, Enum
|
||||||
|
{
|
||||||
|
if (dict.TryGetValue(key, out var value) && Enum.TryParse<T>(value, out var enumResult))
|
||||||
|
return enumResult;
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
47
Il2CppInspector.Redux.GUI/Il2CppHub.cs
Normal file
47
Il2CppInspector.Redux.GUI/Il2CppHub.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.GUI;
|
||||||
|
|
||||||
|
internal class Il2CppHub : Hub
|
||||||
|
{
|
||||||
|
public UiContext State
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!Context.Items.TryGetValue("context", out var context)
|
||||||
|
|| context is not UiContext ctx)
|
||||||
|
{
|
||||||
|
Context.Items["context"] = ctx = new UiContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UiClient Client => new(Clients.Caller);
|
||||||
|
|
||||||
|
public async Task OnUiLaunched()
|
||||||
|
{
|
||||||
|
await State.Initialize(Client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SubmitInputFiles(List<string> inputFiles)
|
||||||
|
{
|
||||||
|
await State.LoadInputFiles(Client, inputFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task QueueExport(string exportTypeId, string outputDirectory, Dictionary<string, string> settings)
|
||||||
|
{
|
||||||
|
await State.QueueExport(Client, exportTypeId, outputDirectory, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartExport()
|
||||||
|
{
|
||||||
|
await State.StartExport(Client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<string>> GetPotentialUnityVersions()
|
||||||
|
{
|
||||||
|
return await State.GetPotentialUnityVersions();
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Il2CppInspector.Redux.GUI/Il2CppInspector.Redux.GUI.csproj
Normal file
17
Il2CppInspector.Redux.GUI/Il2CppInspector.Redux.GUI.csproj
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
|
<PublishAot>false</PublishAot>
|
||||||
|
<!-- todo: enable this once the app is aot compliant! -->
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Bin2Object\Bin2Object\Bin2Object.csproj" />
|
||||||
|
<ProjectReference Include="..\Il2CppInspector.Common\Il2CppInspector.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
23
Il2CppInspector.Redux.GUI/LoadingSession.cs
Normal file
23
Il2CppInspector.Redux.GUI/LoadingSession.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
namespace Il2CppInspector.Redux.GUI;
|
||||||
|
|
||||||
|
public class LoadingSession : IAsyncDisposable
|
||||||
|
{
|
||||||
|
private readonly UiClient _client;
|
||||||
|
|
||||||
|
private LoadingSession(UiClient client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<LoadingSession> Start(UiClient client)
|
||||||
|
{
|
||||||
|
await client.BeginLoading();
|
||||||
|
return new LoadingSession(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
await _client.FinishLoading();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
79
Il2CppInspector.Redux.GUI/Outputs/CSharpStubOutput.cs
Normal file
79
Il2CppInspector.Redux.GUI/Outputs/CSharpStubOutput.cs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
using Il2CppInspector.Model;
|
||||||
|
using Il2CppInspector.Outputs;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.GUI.Outputs;
|
||||||
|
|
||||||
|
public class CSharpStubOutput : IOutputFormatProvider
|
||||||
|
{
|
||||||
|
public static string Id => "cs";
|
||||||
|
|
||||||
|
private enum CSharpLayout
|
||||||
|
{
|
||||||
|
SingleFile,
|
||||||
|
Namespace,
|
||||||
|
Assembly,
|
||||||
|
Class,
|
||||||
|
Tree
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TypeSortingMode
|
||||||
|
{
|
||||||
|
Alphabetical,
|
||||||
|
TypeDefinitionIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Settings(Dictionary<string, string> settings)
|
||||||
|
{
|
||||||
|
public readonly CSharpLayout Layout = settings.GetAsEnumOrDefault("layout", CSharpLayout.SingleFile);
|
||||||
|
public readonly bool FlattenHierarchy = settings.GetAsBooleanOrDefault("flatten", false);
|
||||||
|
public readonly TypeSortingMode SortingMode = settings.GetAsEnumOrDefault("sorting", TypeSortingMode.Alphabetical);
|
||||||
|
public readonly bool SuppressMetadata = settings.GetAsBooleanOrDefault("suppressmetadata", false);
|
||||||
|
public readonly bool MustCompile = settings.GetAsBooleanOrDefault("compilable", false);
|
||||||
|
public readonly bool SeperateAssemblyAttributes = settings.GetAsBooleanOrDefault("seperateassemblyattributes", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Export(AppModel model, UiClient client, string outputPath, Dictionary<string, string> settingsDict)
|
||||||
|
{
|
||||||
|
var settings = new Settings(settingsDict);
|
||||||
|
|
||||||
|
var writer = new CSharpCodeStubs(model.TypeModel)
|
||||||
|
{
|
||||||
|
SuppressMetadata = settings.SuppressMetadata,
|
||||||
|
MustCompile = settings.MustCompile
|
||||||
|
};
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Writing C# type definitions");
|
||||||
|
|
||||||
|
switch (settings.Layout, settings.SortingMode)
|
||||||
|
{
|
||||||
|
case (CSharpLayout.SingleFile, TypeSortingMode.TypeDefinitionIndex):
|
||||||
|
writer.WriteSingleFile(outputPath, info => info.Index);
|
||||||
|
break;
|
||||||
|
case (CSharpLayout.SingleFile, TypeSortingMode.Alphabetical):
|
||||||
|
writer.WriteSingleFile(outputPath, info => info.Name);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (CSharpLayout.Namespace, TypeSortingMode.TypeDefinitionIndex):
|
||||||
|
writer.WriteFilesByNamespace(outputPath, info => info.Index, settings.FlattenHierarchy);
|
||||||
|
break;
|
||||||
|
case (CSharpLayout.Namespace, TypeSortingMode.Alphabetical):
|
||||||
|
writer.WriteFilesByNamespace(outputPath, info => info.Name, settings.FlattenHierarchy);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (CSharpLayout.Assembly, TypeSortingMode.TypeDefinitionIndex):
|
||||||
|
writer.WriteFilesByAssembly(outputPath, info => info.Index, settings.SeperateAssemblyAttributes);
|
||||||
|
break;
|
||||||
|
case (CSharpLayout.Assembly, TypeSortingMode.Alphabetical):
|
||||||
|
writer.WriteFilesByAssembly(outputPath, info => info.Name, settings.SeperateAssemblyAttributes);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (CSharpLayout.Class, _):
|
||||||
|
writer.WriteFilesByClass(outputPath, settings.FlattenHierarchy);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (CSharpLayout.Tree, _):
|
||||||
|
writer.WriteFilesByClassTree(outputPath, settings.SeperateAssemblyAttributes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
Il2CppInspector.Redux.GUI/Outputs/CppScaffoldingOutput.cs
Normal file
31
Il2CppInspector.Redux.GUI/Outputs/CppScaffoldingOutput.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using Il2CppInspector.Cpp;
|
||||||
|
using Il2CppInspector.Cpp.UnityHeaders;
|
||||||
|
using Il2CppInspector.Model;
|
||||||
|
using Il2CppInspector.Outputs;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.GUI.Outputs;
|
||||||
|
|
||||||
|
public class CppScaffoldingOutput : IOutputFormatProvider
|
||||||
|
{
|
||||||
|
public static string Id => "cppscaffolding";
|
||||||
|
|
||||||
|
private class Settings(Dictionary<string, string> settings)
|
||||||
|
{
|
||||||
|
public readonly string UnityVersion = settings.GetValueOrDefault("unityversion", "");
|
||||||
|
public readonly CppCompilerType Compiler = settings.GetAsEnumOrDefault("compiler", CppCompilerType.GCC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Export(AppModel model, UiClient client, string outputPath, Dictionary<string, string> settingsDict)
|
||||||
|
{
|
||||||
|
var settings = new Settings(settingsDict);
|
||||||
|
|
||||||
|
await client.ShowLogMessage($"Bulding application model for Unity {settings.UnityVersion}/{settings.Compiler}");
|
||||||
|
model.Build(new UnityVersion(settings.UnityVersion), settings.Compiler);
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Generating C++ scaffolding");
|
||||||
|
var scaffolding = new CppScaffolding(model);
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Writing C++ scaffolding");
|
||||||
|
scaffolding.Write(outputPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
using Il2CppInspector.Cpp;
|
||||||
|
using Il2CppInspector.Cpp.UnityHeaders;
|
||||||
|
using Il2CppInspector.Model;
|
||||||
|
using Il2CppInspector.Outputs;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.GUI.Outputs;
|
||||||
|
|
||||||
|
public class DisassemblerMetadataOutput : IOutputFormatProvider
|
||||||
|
{
|
||||||
|
public static string Id => "disassemblermetadata";
|
||||||
|
|
||||||
|
private enum DisassemblerType
|
||||||
|
{
|
||||||
|
IDA,
|
||||||
|
Ghidra,
|
||||||
|
BinaryNinja,
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Settings(Dictionary<string, string> dict)
|
||||||
|
{
|
||||||
|
public readonly DisassemblerType Disassembler = dict.GetAsEnumOrDefault("disassembler", DisassemblerType.IDA);
|
||||||
|
public readonly string UnityVersion = dict.GetValueOrDefault("unityversion", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Export(AppModel model, UiClient client, string outputPath, Dictionary<string, string> settingsDict)
|
||||||
|
{
|
||||||
|
var settings = new Settings(settingsDict);
|
||||||
|
|
||||||
|
await client.ShowLogMessage($"Bulding application model for Unity {settings.UnityVersion}/{CppCompilerType.GCC}");
|
||||||
|
model.Build(new UnityVersion(settings.UnityVersion), CppCompilerType.GCC);
|
||||||
|
|
||||||
|
var headerPath = Path.Join(outputPath, "il2cpp.h");
|
||||||
|
{
|
||||||
|
await client.ShowLogMessage("Generating C++ types");
|
||||||
|
var cppScaffolding = new CppScaffolding(model, useBetterArraySize: true);
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Writing C++ types");
|
||||||
|
cppScaffolding.WriteTypes(headerPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadataPath = Path.Join(outputPath, "il2cpp.json");
|
||||||
|
{
|
||||||
|
await client.ShowLogMessage("Generating disassembler metadata");
|
||||||
|
var jsonMetadata = new JSONMetadata(model);
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Writing disassembler metadata");
|
||||||
|
jsonMetadata.Write(metadataPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.Disassembler != DisassemblerType.None)
|
||||||
|
{
|
||||||
|
var scriptPath = Path.Join(outputPath, "il2cpp.py");
|
||||||
|
await client.ShowLogMessage($"Generating python script for {settings.Disassembler}");
|
||||||
|
var script = new PythonScript(model);
|
||||||
|
|
||||||
|
await client.ShowLogMessage($"Writing python script for {settings.Disassembler}");
|
||||||
|
script.WriteScriptToFile(scriptPath, settings.Disassembler.ToString(), headerPath, metadataPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
Il2CppInspector.Redux.GUI/Outputs/DummyDllOutput.cs
Normal file
27
Il2CppInspector.Redux.GUI/Outputs/DummyDllOutput.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using Il2CppInspector.Model;
|
||||||
|
using Il2CppInspector.Outputs;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.GUI.Outputs;
|
||||||
|
|
||||||
|
public class DummyDllOutput : IOutputFormatProvider
|
||||||
|
{
|
||||||
|
public static string Id => "dummydlls";
|
||||||
|
|
||||||
|
private class Settings(Dictionary<string, string> dict)
|
||||||
|
{
|
||||||
|
public readonly bool SuppressMetadata = dict.GetAsBooleanOrDefault("suppressmetadata", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Export(AppModel model, UiClient client, string outputPath, Dictionary<string, string> settingsDict)
|
||||||
|
{
|
||||||
|
var outputSettings = new Settings(settingsDict);
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Generating .NET dummy assemblies");
|
||||||
|
var shims = new AssemblyShims(model.TypeModel)
|
||||||
|
{
|
||||||
|
SuppressMetadata = outputSettings.SuppressMetadata
|
||||||
|
};
|
||||||
|
|
||||||
|
shims.Write(outputPath, client.EventHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
Il2CppInspector.Redux.GUI/Outputs/IOutputFormat.cs
Normal file
14
Il2CppInspector.Redux.GUI/Outputs/IOutputFormat.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Il2CppInspector.Model;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.GUI.Outputs;
|
||||||
|
|
||||||
|
public interface IOutputFormat
|
||||||
|
{
|
||||||
|
public Task Export(AppModel model, UiClient client, string outputPath,
|
||||||
|
Dictionary<string, string> settingsDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IOutputFormatProvider : IOutputFormat
|
||||||
|
{
|
||||||
|
public static abstract string Id { get; }
|
||||||
|
}
|
||||||
38
Il2CppInspector.Redux.GUI/Outputs/OutputFormatRegistry.cs
Normal file
38
Il2CppInspector.Redux.GUI/Outputs/OutputFormatRegistry.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
namespace Il2CppInspector.Redux.GUI.Outputs;
|
||||||
|
|
||||||
|
public static class OutputFormatRegistry
|
||||||
|
{
|
||||||
|
public static IEnumerable<string> AvailableOutputFormats => OutputFormats.Keys;
|
||||||
|
|
||||||
|
private static readonly Dictionary<string, IOutputFormat> OutputFormats = [];
|
||||||
|
|
||||||
|
public static void RegisterOutputFormat<T>() where T : IOutputFormatProvider, new()
|
||||||
|
{
|
||||||
|
if (OutputFormats.ContainsKey(T.Id))
|
||||||
|
throw new InvalidOperationException("An output format with this id was already registered.");
|
||||||
|
|
||||||
|
OutputFormats[T.Id] = new T();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IOutputFormat GetOutputFormat(string id)
|
||||||
|
{
|
||||||
|
if (!OutputFormats.TryGetValue(id, out var format))
|
||||||
|
throw new ArgumentException($"Failed to find output format for id {id}", nameof(id));
|
||||||
|
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegisterBuiltinOutputFormats()
|
||||||
|
{
|
||||||
|
RegisterOutputFormat<CSharpStubOutput>();
|
||||||
|
RegisterOutputFormat<VsSolutionOutput>();
|
||||||
|
RegisterOutputFormat<DummyDllOutput>();
|
||||||
|
RegisterOutputFormat<DisassemblerMetadataOutput>();
|
||||||
|
RegisterOutputFormat<CppScaffoldingOutput>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static OutputFormatRegistry()
|
||||||
|
{
|
||||||
|
RegisterBuiltinOutputFormats();
|
||||||
|
}
|
||||||
|
}
|
||||||
29
Il2CppInspector.Redux.GUI/Outputs/VsSolutionOutput.cs
Normal file
29
Il2CppInspector.Redux.GUI/Outputs/VsSolutionOutput.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Il2CppInspector.Model;
|
||||||
|
using Il2CppInspector.Outputs;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.GUI.Outputs;
|
||||||
|
|
||||||
|
public class VsSolutionOutput : IOutputFormatProvider
|
||||||
|
{
|
||||||
|
public static string Id => "vssolution";
|
||||||
|
|
||||||
|
private class Settings(Dictionary<string, string> settings)
|
||||||
|
{
|
||||||
|
public readonly string UnityPath = settings.GetValueOrDefault("unitypath", "");
|
||||||
|
public readonly string UnityAssembliesPath = settings.GetValueOrDefault("assembliespath", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Export(AppModel model, UiClient client, string outputPath, Dictionary<string, string> settingsDict)
|
||||||
|
{
|
||||||
|
var settings = new Settings(settingsDict);
|
||||||
|
|
||||||
|
var writer = new CSharpCodeStubs(model.TypeModel)
|
||||||
|
{
|
||||||
|
MustCompile = true,
|
||||||
|
SuppressMetadata = true
|
||||||
|
};
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Writing Visual Studio solution");
|
||||||
|
writer.WriteSolution(outputPath, settings.UnityPath, settings.UnityAssembliesPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
54
Il2CppInspector.Redux.GUI/PathHeuristics.cs
Normal file
54
Il2CppInspector.Redux.GUI/PathHeuristics.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
namespace Il2CppInspector.Redux.GUI;
|
||||||
|
|
||||||
|
public static class PathHeuristics
|
||||||
|
{
|
||||||
|
private static readonly string[] AllowedMetadataExtensionComponents =
|
||||||
|
[
|
||||||
|
"dat", "dec"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] AllowedMetadataNameComponents =
|
||||||
|
[
|
||||||
|
"metadata"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] AllowedBinaryPathComponents =
|
||||||
|
[
|
||||||
|
"GameAssembly",
|
||||||
|
"il2cpp",
|
||||||
|
"UnityFramework"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] AllowedBinaryExtensionComponents =
|
||||||
|
[
|
||||||
|
"dll", "so", "exe", "bin", "prx", "sprx", "dylib"
|
||||||
|
];
|
||||||
|
|
||||||
|
public static bool IsMetadataPath(string path)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(path);
|
||||||
|
if (AllowedMetadataExtensionComponents.Any(extension.Contains))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var filename = Path.GetFileNameWithoutExtension(path);
|
||||||
|
if (AllowedMetadataNameComponents.Any(filename.Contains))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsBinaryPath(string path)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(path);
|
||||||
|
|
||||||
|
// empty to allow macho binaries which do not have an extension
|
||||||
|
if (extension == "" || AllowedBinaryExtensionComponents.Any(extension.Contains))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var filename = Path.GetFileNameWithoutExtension(path);
|
||||||
|
if (AllowedBinaryPathComponents.Any(filename.Contains))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
Il2CppInspector.Redux.GUI/Program.cs
Normal file
54
Il2CppInspector.Redux.GUI/Program.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using Il2CppInspector.Redux.GUI;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateSlimBuilder(args);
|
||||||
|
|
||||||
|
builder.Services.ConfigureHttpJsonOptions(options =>
|
||||||
|
{
|
||||||
|
options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddSignalR(config =>
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
config.EnableDetailedErrors = true;
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.Configure<JsonHubProtocolOptions>(options =>
|
||||||
|
{
|
||||||
|
options.PayloadSerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddDefaultPolicy(policy =>
|
||||||
|
{
|
||||||
|
policy.SetIsOriginAllowed(origin => origin.StartsWith("http://localhost") || origin.StartsWith("http://tauri.localhost"))
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.WithMethods("GET", "POST")
|
||||||
|
.AllowCredentials();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<UiProcessService>();
|
||||||
|
builder.Services.AddSingleton<IHostedService>(p => p.GetRequiredService<UiProcessService>());
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
app.UseCors();
|
||||||
|
|
||||||
|
app.MapHub<Il2CppHub>("/il2cpp");
|
||||||
|
|
||||||
|
await app.StartAsync();
|
||||||
|
|
||||||
|
var serverUrl = app.Urls.First();
|
||||||
|
var port = new Uri(serverUrl).Port;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine($"Listening on port {port}");
|
||||||
|
#else
|
||||||
|
app.Services.GetRequiredService<UiProcessService>().LaunchUiProcess(port);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
await app.WaitForShutdownAsync();
|
||||||
15
Il2CppInspector.Redux.GUI/Properties/launchSettings.json
Normal file
15
Il2CppInspector.Redux.GUI/Properties/launchSettings.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "todos",
|
||||||
|
"applicationUrl": "http://localhost:5154",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
Il2CppInspector.Redux.GUI/UiClient.cs
Normal file
44
Il2CppInspector.Redux.GUI/UiClient.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.GUI;
|
||||||
|
|
||||||
|
public class UiClient(ISingleClientProxy client)
|
||||||
|
{
|
||||||
|
private EventHandler<string>? _handler;
|
||||||
|
|
||||||
|
public EventHandler<string> EventHandler
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
_handler ??= (_, status) =>
|
||||||
|
{
|
||||||
|
#pragma warning disable CS4014
|
||||||
|
ShowLogMessage(status);
|
||||||
|
#pragma warning restore CS4014
|
||||||
|
};
|
||||||
|
|
||||||
|
return _handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ShowLogMessage(string message, CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(ShowLogMessage), message, cancellationToken);
|
||||||
|
|
||||||
|
public async Task BeginLoading(CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(BeginLoading), cancellationToken);
|
||||||
|
|
||||||
|
public async Task FinishLoading(CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(FinishLoading), cancellationToken);
|
||||||
|
|
||||||
|
public async Task ShowInfoToast(string message, CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(ShowInfoToast), message, cancellationToken);
|
||||||
|
|
||||||
|
public async Task ShowSuccessToast(string message, CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(ShowSuccessToast), message, cancellationToken);
|
||||||
|
|
||||||
|
public async Task ShowErrorToast(string message, CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(ShowErrorToast), message, cancellationToken);
|
||||||
|
|
||||||
|
public async Task OnImportCompleted(CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(OnImportCompleted), cancellationToken);
|
||||||
|
}
|
||||||
216
Il2CppInspector.Redux.GUI/UiContext.cs
Normal file
216
Il2CppInspector.Redux.GUI/UiContext.cs
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Il2CppInspector.Cpp.UnityHeaders;
|
||||||
|
using Il2CppInspector.Model;
|
||||||
|
using Il2CppInspector.Redux.GUI.Outputs;
|
||||||
|
using Il2CppInspector.Reflection;
|
||||||
|
using Inspector = Il2CppInspector.Il2CppInspector;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.GUI;
|
||||||
|
|
||||||
|
public class UiContext
|
||||||
|
{
|
||||||
|
private const string BugReportSuffix =
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
If you believe this is a bug in Il2CppInspectorRedux, please use the CLI version to generate the complete output and paste it when filing a bug report.
|
||||||
|
Do not send a screenshot of this error!
|
||||||
|
""";
|
||||||
|
|
||||||
|
private Metadata? _metadata;
|
||||||
|
private IFileFormatStream? _binary;
|
||||||
|
private readonly List<AppModel> _appModels = [];
|
||||||
|
private readonly List<UnityHeaders> _potentialUnityVersions = [];
|
||||||
|
|
||||||
|
private readonly LoadOptions _loadOptions = new();
|
||||||
|
|
||||||
|
private readonly List<(string FormatId, string OutputDirectory, Dictionary<string, string> Settings)> _queuedExports = [];
|
||||||
|
|
||||||
|
private async Task<bool> TryLoadMetadataFromStream(UiClient client, MemoryStream stream)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_metadata = Metadata.FromStream(stream, client.EventHandler);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await client.ShowErrorToast($"{e.Message}{BugReportSuffix}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> TryLoadBinaryFromStream(UiClient client, MemoryStream stream)
|
||||||
|
{
|
||||||
|
await client.ShowLogMessage("Processing binary");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var file = FileFormatStream.Load(stream, _loadOptions, client.EventHandler);
|
||||||
|
|
||||||
|
if (file == null)
|
||||||
|
throw new InvalidOperationException("Failed to determine binary file format.");
|
||||||
|
|
||||||
|
if (file.NumImages == 0)
|
||||||
|
throw new InvalidOperationException("Failed to find any binary images in the file");
|
||||||
|
|
||||||
|
_binary = file;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await client.ShowErrorToast($"{e.Message}{BugReportSuffix}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> TryInitializeInspector(UiClient client)
|
||||||
|
{
|
||||||
|
Debug.Assert(_binary != null);
|
||||||
|
Debug.Assert(_metadata != null);
|
||||||
|
|
||||||
|
_appModels.Clear();
|
||||||
|
|
||||||
|
var inspectors = Inspector.LoadFromStream(_binary, _metadata, client.EventHandler);
|
||||||
|
|
||||||
|
if (inspectors.Count == 0)
|
||||||
|
{
|
||||||
|
await client.ShowErrorToast(
|
||||||
|
"""
|
||||||
|
Failed to auto-detect any IL2CPP binary images in the provided files.
|
||||||
|
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!
|
||||||
|
""");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var inspector in inspectors)
|
||||||
|
{
|
||||||
|
await client.ShowLogMessage(
|
||||||
|
$"Building .NET type model for {inspector.BinaryImage.Format}/{inspector.BinaryImage.Arch} image");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var typeModel = new TypeModel(inspector);
|
||||||
|
|
||||||
|
// Just create the app model, do not initialize it - this is done lazily depending on the exports
|
||||||
|
_appModels.Add(new AppModel(typeModel, makeDefaultBuild: false));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await client.ShowErrorToast($"Failed to build type model: {e.Message}{BugReportSuffix}");
|
||||||
|
|
||||||
|
// Clear out failed metadata and binary so subsequent loads do not use any stale data.
|
||||||
|
_metadata = null;
|
||||||
|
_binary = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_potentialUnityVersions.Clear();
|
||||||
|
_potentialUnityVersions.AddRange(UnityHeaders.GuessHeadersForBinary(_appModels[0].Package.Binary));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Initialize(UiClient client, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await client.ShowSuccessToast("SignalR initialized!", cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LoadInputFiles(UiClient client, List<string> inputFiles,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await using (await LoadingSession.Start(client))
|
||||||
|
{
|
||||||
|
var streams = Inspector.GetStreamsFromPackage(inputFiles);
|
||||||
|
if (streams != null)
|
||||||
|
{
|
||||||
|
// The input files contained a package that provides the metadata and binary.
|
||||||
|
// Use these instead of parsing all files individually.
|
||||||
|
if (!await TryLoadMetadataFromStream(client, streams.Value.Metadata))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!await TryLoadBinaryFromStream(client, streams.Value.Binary))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var inputFile in inputFiles)
|
||||||
|
{
|
||||||
|
if (_metadata != null && _binary != null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
await client.ShowLogMessage($"Processing {inputFile}", cancellationToken);
|
||||||
|
|
||||||
|
var data = await File.ReadAllBytesAsync(inputFile, cancellationToken);
|
||||||
|
var stream = new MemoryStream(data);
|
||||||
|
|
||||||
|
if ( _metadata == null && PathHeuristics.IsMetadataPath(inputFile))
|
||||||
|
{
|
||||||
|
if (await TryLoadMetadataFromStream(client, stream))
|
||||||
|
{
|
||||||
|
await client.ShowSuccessToast($"Loaded metadata from {inputFile}", cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_binary == null && PathHeuristics.IsBinaryPath(inputFile))
|
||||||
|
{
|
||||||
|
stream.Position = 0;
|
||||||
|
_loadOptions.BinaryFilePath = inputFile;
|
||||||
|
|
||||||
|
if (await TryLoadBinaryFromStream(client, stream))
|
||||||
|
{
|
||||||
|
await client.ShowSuccessToast($"Loaded binary from {inputFile}", cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_metadata != null && _binary != null)
|
||||||
|
{
|
||||||
|
if (await TryInitializeInspector(client))
|
||||||
|
{
|
||||||
|
await client.ShowSuccessToast("Successfully loaded IL2CPP data!", cancellationToken);
|
||||||
|
await client.OnImportCompleted(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task QueueExport(UiClient client, string exportFormatId, string outputDirectory,
|
||||||
|
Dictionary<string, string> settings, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
_queuedExports.Add((exportFormatId, outputDirectory, settings));
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartExport(UiClient client, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
// todo: support different app model selection (when loading packages)
|
||||||
|
Debug.Assert(_appModels.Count > 0);
|
||||||
|
|
||||||
|
await using (await LoadingSession.Start(client))
|
||||||
|
{
|
||||||
|
var model = _appModels[0];
|
||||||
|
|
||||||
|
foreach (var queuedExport in _queuedExports)
|
||||||
|
{
|
||||||
|
var outputFormat = OutputFormatRegistry.GetOutputFormat(queuedExport.FormatId);
|
||||||
|
await outputFormat.Export(model, client, queuedExport.OutputDirectory, queuedExport.Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
_queuedExports.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.ShowSuccessToast("Export finished", cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<List<string>> GetPotentialUnityVersions()
|
||||||
|
{
|
||||||
|
return Task.FromResult(_potentialUnityVersions.Select(x => x.VersionRange.Min.ToString()).ToList());
|
||||||
|
}
|
||||||
|
}
|
||||||
32
Il2CppInspector.Redux.GUI/UiProcessService.cs
Normal file
32
Il2CppInspector.Redux.GUI/UiProcessService.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.GUI;
|
||||||
|
|
||||||
|
public class UiProcessService(IHostApplicationLifetime lifetime) : BackgroundService
|
||||||
|
{
|
||||||
|
private const string UiExecutableName = "Il2CppInspector.Redux.GUI.UI.exe";
|
||||||
|
|
||||||
|
private Process? _uiProcess;
|
||||||
|
|
||||||
|
public void LaunchUiProcess(int port)
|
||||||
|
{
|
||||||
|
_uiProcess = Process.Start(new ProcessStartInfo(UiExecutableName, [port.ToString()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
while (_uiProcess == null)
|
||||||
|
await Task.Delay(TimeSpan.FromMilliseconds(10), stoppingToken);
|
||||||
|
|
||||||
|
await _uiProcess.WaitForExitAsync(stoppingToken);
|
||||||
|
lifetime.StopApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (_uiProcess is { HasExited: false })
|
||||||
|
_uiProcess.Kill();
|
||||||
|
|
||||||
|
return base.StopAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Il2CppInspector.Redux.GUI/appsettings.Development.json
Normal file
8
Il2CppInspector.Redux.GUI/appsettings.Development.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Il2CppInspector.Redux.GUI/appsettings.json
Normal file
9
Il2CppInspector.Redux.GUI/appsettings.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
@@ -44,6 +44,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VersionedSerialization", "V
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VersionedSerialization.Generator", "VersionedSerialization.Generator\VersionedSerialization.Generator.csproj", "{6FF1F0C0-374A-4B7E-B173-697605679AF6}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VersionedSerialization.Generator", "VersionedSerialization.Generator\VersionedSerialization.Generator.csproj", "{6FF1F0C0-374A-4B7E-B173-697605679AF6}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInspector.Redux.GUI", "Il2CppInspector.Redux.GUI\Il2CppInspector.Redux.GUI.csproj", "{CB6CE40B-0805-49B1-82DD-4CAAE58F9D6E}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -71,11 +73,15 @@ Global
|
|||||||
{A24D77DA-8A64-4AD3-956A-677A96F20373}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{A24D77DA-8A64-4AD3-956A-677A96F20373}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{A24D77DA-8A64-4AD3-956A-677A96F20373}.Release|Any CPU.Build.0 = Release|Any CPU
|
{A24D77DA-8A64-4AD3-956A-677A96F20373}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{803C3421-1907-4114-8B6B-F5E1789FD6A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{803C3421-1907-4114-8B6B-F5E1789FD6A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{803C3421-1907-4114-8B6B-F5E1789FD6A6}.Release|Any CPU.ActiveCfg = Release|AnyCPU
|
{803C3421-1907-4114-8B6B-F5E1789FD6A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{803C3421-1907-4114-8B6B-F5E1789FD6A6}.Release|Any CPU.Build.0 = Release|AnyCPU
|
{803C3421-1907-4114-8B6B-F5E1789FD6A6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{6FF1F0C0-374A-4B7E-B173-697605679AF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{6FF1F0C0-374A-4B7E-B173-697605679AF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{6FF1F0C0-374A-4B7E-B173-697605679AF6}.Release|Any CPU.ActiveCfg = Release|AnyCPU
|
{6FF1F0C0-374A-4B7E-B173-697605679AF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{6FF1F0C0-374A-4B7E-B173-697605679AF6}.Release|Any CPU.Build.0 = Release|AnyCPU
|
{6FF1F0C0-374A-4B7E-B173-697605679AF6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{CB6CE40B-0805-49B1-82DD-4CAAE58F9D6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CB6CE40B-0805-49B1-82DD-4CAAE58F9D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CB6CE40B-0805-49B1-82DD-4CAAE58F9D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CB6CE40B-0805-49B1-82DD-4CAAE58F9D6E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
Reference in New Issue
Block a user