Files
gregExtractor/Services/ExtractorService.cs
T

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,
};
}
}