diff --git a/YaeAchievement.SourceGeneration/YaeAchievement.SourceGeneration/MinHookAttachGenerator.cs b/YaeAchievement.SourceGeneration/YaeAchievement.SourceGeneration/MinHookAttachGenerator.cs new file mode 100644 index 0000000..e2b1b3b --- /dev/null +++ b/YaeAchievement.SourceGeneration/YaeAchievement.SourceGeneration/MinHookAttachGenerator.cs @@ -0,0 +1,104 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace YaeAchievement.SourceGeneration; + +[Generator(LanguageNames.CSharp)] +public sealed class MinHookAttachGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + IncrementalValueProvider> provider = context.SyntaxProvider.CreateSyntaxProvider(Filter, Transform).Collect(); + context.RegisterSourceOutput(provider, Generate); + } + + private static bool Filter(SyntaxNode node, CancellationToken token) + { + return node is InvocationExpressionSyntax + { + Expression: MemberAccessExpressionSyntax + { + Expression: IdentifierNameSyntax { Identifier.Text: "MinHook" }, + Name.Identifier.Text: "Attach" + } + }; + } + + private static AttachInfo Transform(GeneratorSyntaxContext context, CancellationToken token) + { + InvocationExpressionSyntax invocation = (InvocationExpressionSyntax)context.Node; + SeparatedSyntaxList args = invocation.ArgumentList.Arguments; + if (args.Count is not 3) + { + return null; + } + + string type = context.SemanticModel.GetTypeInfo(args[0].Expression).Type?.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); + + if (string.IsNullOrEmpty(type)) + { + return null; + } + + return new() + { + MinimallyQualifiedType = type, + }; + } + + private static void Generate(SourceProductionContext context, ImmutableArray infoArray) + { + CompilationUnitSyntax unit = CompilationUnit() + .WithMembers(List( + [ + FileScopedNamespaceDeclaration(ParseName("Yae.Utilities")), + ClassDeclaration("MinHook") + .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.PartialKeyword))) + .WithMembers(List(GenerateMethods(infoArray))) + ])); + + context.AddSource("MinHook.Attach.g.cs", unit.NormalizeWhitespace().ToFullString()); + } + + private static IEnumerable GenerateMethods(ImmutableArray infoArray) + { + foreach (AttachInfo info in infoArray) + { + TypeSyntax type = ParseTypeName(info.MinimallyQualifiedType); + + yield return MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier("Attach")) + .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.UnsafeKeyword))) + .WithParameterList(ParameterList(SeparatedList( + [ + Parameter(Identifier("origin")).WithType(type), + Parameter(Identifier("handler")).WithType(type), + Parameter(Identifier("trampoline")).WithType(type).WithModifiers(TokenList(Token(SyntaxKind.OutKeyword))) + ]))) + .WithBody(Block(List( + [ + ExpressionStatement(InvocationExpression(IdentifierName("Attach")) + .WithArgumentList(ArgumentList(SeparatedList( + [ + Argument(CastExpression(IdentifierName("nint"), IdentifierName("origin"))), + Argument(CastExpression(IdentifierName("nint"), IdentifierName("handler"))), + Argument(DeclarationExpression(IdentifierName("nint"), SingleVariableDesignation(Identifier("trampoline1")))) + .WithRefKindKeyword(Token(SyntaxKind.OutKeyword)) + ])))), + ExpressionStatement(AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + IdentifierName("trampoline"), + CastExpression(type, IdentifierName("trampoline1")))) + ]))); + } + } + + private record AttachInfo + { + public required string MinimallyQualifiedType { get; init; } + } +} \ No newline at end of file diff --git a/YaeAchievement.SourceGeneration/YaeAchievement.SourceGeneration/YaeAchievement.SourceGeneration.csproj b/YaeAchievement.SourceGeneration/YaeAchievement.SourceGeneration/YaeAchievement.SourceGeneration.csproj new file mode 100644 index 0000000..d120c9d --- /dev/null +++ b/YaeAchievement.SourceGeneration/YaeAchievement.SourceGeneration/YaeAchievement.SourceGeneration.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + preview + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/YaeAchievement.slnx b/YaeAchievement.slnx index dbf91b7..4009dc2 100644 --- a/YaeAchievement.slnx +++ b/YaeAchievement.slnx @@ -1,4 +1,5 @@ - - - + + + + \ No newline at end of file diff --git a/YaeAchievementLib/YaeAchievementLib.csproj b/YaeAchievementLib/YaeAchievementLib.csproj index e6ef156..15c31c9 100644 --- a/YaeAchievementLib/YaeAchievementLib.csproj +++ b/YaeAchievementLib/YaeAchievementLib.csproj @@ -25,20 +25,20 @@ - - - - - + + + + + - + - - + + @@ -54,11 +54,18 @@ - + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + diff --git a/YaeAchievementLib/src/Utilities/Native.cs b/YaeAchievementLib/src/Utilities/Native.cs index 9549d6a..cdf16af 100644 --- a/YaeAchievementLib/src/Utilities/Native.cs +++ b/YaeAchievementLib/src/Utilities/Native.cs @@ -159,30 +159,6 @@ internal static partial class MinHook { } } - // todo: auto gen - public static unsafe void Attach(delegate*unmanaged origin, delegate*unmanaged handler, out delegate*unmanaged trampoline) { - Attach((nint) origin, (nint) handler, out var trampoline1); - trampoline = (delegate*unmanaged) trampoline1; - } - - // todo: auto gen - public static unsafe void Attach(delegate*unmanaged origin, delegate*unmanaged handler, out delegate*unmanaged trampoline) { - Attach((nint) origin, (nint) handler, out var trampoline1); - trampoline = (delegate*unmanaged) trampoline1; - } - - // todo: auto gen - public static unsafe void Attach(delegate*unmanaged origin, delegate*unmanaged handler, out delegate*unmanaged trampoline) { - Attach((nint) origin, (nint) handler, out var trampoline1); - trampoline = (delegate*unmanaged) trampoline1; - } - - // todo: auto gen - public static unsafe void Attach(delegate*unmanaged origin, delegate*unmanaged handler, out delegate*unmanaged trampoline) { - Attach((nint) origin, (nint) handler, out var trampoline1); - trampoline = (delegate*unmanaged) trampoline1; - } - public static void Attach(nint origin, nint handler, out nint trampoline) { uint result; if ((result = MinHookCreate(origin, handler, out trampoline)) != 0) {