141 lines
5.2 KiB
C#
141 lines
5.2 KiB
C#
using gregExtractor.Models;
|
|
|
|
namespace gregExtractor.Services;
|
|
|
|
public sealed class ExtractorService
|
|
{
|
|
public async Task<IReadOnlyList<HookDefinition>> ExtractAsync(
|
|
string il2CppDir,
|
|
HookClassifier classifier,
|
|
IProgress<string>? progress,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(il2CppDir) || !Directory.Exists(il2CppDir))
|
|
throw new DirectoryNotFoundException($"IL2CPP directory not found: {il2CppDir}");
|
|
|
|
string assemblyPath = Path.Combine(il2CppDir, "Assembly-CSharp.dll");
|
|
if (!File.Exists(assemblyPath))
|
|
throw new FileNotFoundException("Assembly-CSharp.dll was not found.", assemblyPath);
|
|
|
|
return await Task.Run(() => ExtractInternal(assemblyPath, il2CppDir, classifier, progress, cancellationToken), cancellationToken)
|
|
.ConfigureAwait(false);
|
|
}
|
|
|
|
private static IReadOnlyList<HookDefinition> ExtractInternal(
|
|
string assemblyPath,
|
|
string il2CppDir,
|
|
HookClassifier classifier,
|
|
IProgress<string>? progress,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var resolver = new DefaultAssemblyResolver();
|
|
resolver.AddSearchDirectory(il2CppDir);
|
|
|
|
foreach (string directory in Directory.EnumerateDirectories(il2CppDir))
|
|
resolver.AddSearchDirectory(directory);
|
|
|
|
var readerParameters = new ReaderParameters
|
|
{
|
|
AssemblyResolver = resolver,
|
|
ReadingMode = ReadingMode.Deferred,
|
|
ReadSymbols = false,
|
|
};
|
|
|
|
progress?.Report("Loading Assembly-CSharp.dll via Mono.Cecil...");
|
|
|
|
using AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyPath, readerParameters);
|
|
var hooks = new List<HookDefinition>(capacity: 8192);
|
|
|
|
foreach (TypeDefinition type in assembly.MainModule.Types)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
if (!type.IsPublic || type.IsInterface)
|
|
continue;
|
|
|
|
string className = type.Name;
|
|
string namespaceName = type.Namespace;
|
|
|
|
foreach (MethodDefinition method in type.Methods)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
if (!method.IsPublic)
|
|
continue;
|
|
|
|
if (method.IsConstructor || method.IsSpecialName)
|
|
continue;
|
|
|
|
if (!method.Parameters.Any() && method.ReturnType.FullName.Equals("System.Void", StringComparison.Ordinal))
|
|
continue;
|
|
|
|
var parameters = method.Parameters
|
|
.Select(parameter => new HookParameter(parameter.Name, NormalizeTypeName(parameter.ParameterType)))
|
|
.ToArray();
|
|
|
|
string returnType = NormalizeTypeName(method.ReturnType);
|
|
bool isVoid = string.Equals(returnType, "void", StringComparison.OrdinalIgnoreCase);
|
|
string group = classifier.Classify(className, method.Name);
|
|
|
|
hooks.Add(new HookDefinition(
|
|
Group: group,
|
|
Namespace: namespaceName,
|
|
ClassName: className,
|
|
MethodName: method.Name,
|
|
ReturnType: returnType,
|
|
IsVoid: isVoid,
|
|
Parameters: parameters));
|
|
}
|
|
}
|
|
|
|
progress?.Report($"Extraction finished. Hooks found: {hooks.Count}");
|
|
|
|
return hooks
|
|
.OrderBy(hook => hook.Group, StringComparer.OrdinalIgnoreCase)
|
|
.ThenBy(hook => hook.ClassName, StringComparer.OrdinalIgnoreCase)
|
|
.ThenBy(hook => hook.MethodName, StringComparer.OrdinalIgnoreCase)
|
|
.ToArray();
|
|
}
|
|
|
|
private static string NormalizeTypeName(TypeReference typeReference)
|
|
{
|
|
if (typeReference.IsByReference && typeReference is ByReferenceType byReferenceType)
|
|
return NormalizeTypeName(byReferenceType.ElementType);
|
|
|
|
if (typeReference is GenericInstanceType genericInstanceType)
|
|
{
|
|
string genericName = genericInstanceType.Name;
|
|
int backtick = genericName.IndexOf('`');
|
|
if (backtick >= 0)
|
|
genericName = genericName[..backtick];
|
|
|
|
string genericArgs = string.Join(", ", genericInstanceType.GenericArguments.Select(NormalizeTypeName));
|
|
return $"{genericName}<{genericArgs}>";
|
|
}
|
|
|
|
if (typeReference.IsArray && typeReference is ArrayType arrayType)
|
|
return $"{NormalizeTypeName(arrayType.ElementType)}[]";
|
|
|
|
return typeReference.FullName switch
|
|
{
|
|
"System.Void" => "void",
|
|
"System.Boolean" => "bool",
|
|
"System.Byte" => "byte",
|
|
"System.SByte" => "sbyte",
|
|
"System.Int16" => "short",
|
|
"System.UInt16" => "ushort",
|
|
"System.Int32" => "int",
|
|
"System.UInt32" => "uint",
|
|
"System.Int64" => "long",
|
|
"System.UInt64" => "ulong",
|
|
"System.Single" => "float",
|
|
"System.Double" => "double",
|
|
"System.Decimal" => "decimal",
|
|
"System.String" => "string",
|
|
"System.Char" => "char",
|
|
"System.Object" => "object",
|
|
_ => typeReference.Name,
|
|
};
|
|
}
|
|
}
|