131 lines
5.8 KiB
C#
Executable File
131 lines
5.8 KiB
C#
Executable File
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using Dalamud.Game;
|
|
using Dalamud.Hooking;
|
|
using Dalamud.Logging;
|
|
|
|
namespace Siggingway;
|
|
|
|
/// <summary>
|
|
/// The main entry point for Siggingway.
|
|
/// </summary>
|
|
public static class Siggingway {
|
|
private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
|
|
|
|
/// <summary>
|
|
/// Initialises an object's fields and properties that are annotated with a
|
|
/// <see cref="SignatureAttribute"/>.
|
|
/// </summary>
|
|
/// <param name="scanner">the SigScanner to use for scanning for signatures</param>
|
|
/// <param name="self">the object to initialise</param>
|
|
/// <param name="log">if warnings should be logged using <see cref="PluginLog"/></param>
|
|
public static void Initialise(SigScanner scanner, object self, bool log = true) {
|
|
var selfType = self.GetType();
|
|
var fields = selfType.GetFields(Flags).Select(field => (IFieldOrPropertyInfo) new FieldInfoWrapper(field))
|
|
.Concat(selfType.GetProperties(Flags).Select(prop => new PropertyInfoWrapper(prop)))
|
|
.Select(field => (field, field.GetCustomAttribute<SignatureAttribute>()))
|
|
.Where(field => field.Item2 != null);
|
|
|
|
foreach (var (info, sig) in fields) {
|
|
IntPtr ptr;
|
|
var success = sig!.ScanType == ScanType.Text
|
|
? scanner.TryScanText(sig.Signature, out ptr)
|
|
: scanner.TryGetStaticAddressFromSig(sig.Signature, out ptr);
|
|
if (!success) {
|
|
if (log) {
|
|
PluginLog.Warning($"Failed to find {sig.ScanType} signature \"{info.Name}\" for {selfType.FullName} ({sig.Signature})");
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
void LogInvalid(string message) {
|
|
PluginLog.Warning($"Invalid Signature attribute for {selfType.FullName}.{info.Name}: {message}");
|
|
}
|
|
|
|
var actualType = info.ActualType;
|
|
PluginLog.Information($"actualType: {actualType}");
|
|
if (actualType.IsGenericType && actualType.GetGenericTypeDefinition() == typeof(Nullable<>)) {
|
|
// unwrap the nullable
|
|
actualType = actualType.GetGenericArguments()[0];
|
|
PluginLog.Information($"unwrapped actualType: {actualType}");
|
|
}
|
|
|
|
switch (sig.UseFlags) {
|
|
case SignatureUseFlags.Auto when actualType == typeof(IntPtr) || actualType.IsPointer || actualType.IsAssignableTo(typeof(Delegate)):
|
|
case SignatureUseFlags.Pointer: {
|
|
info.SetValue(self, ptr);
|
|
break;
|
|
}
|
|
case SignatureUseFlags.Auto when actualType.IsGenericType && actualType.GetGenericTypeDefinition() == typeof(Hook<>):
|
|
case SignatureUseFlags.Hook: {
|
|
if (!actualType.IsGenericType || actualType.GetGenericTypeDefinition() != typeof(Hook<>)) {
|
|
LogInvalid($"{actualType.Name} is not a Hook<T>");
|
|
continue;
|
|
}
|
|
|
|
var hookDelegateType = actualType.GenericTypeArguments[0];
|
|
|
|
Delegate? detour;
|
|
if (sig.DetourName == null) {
|
|
var matches = selfType.GetMethods(Flags)
|
|
.Select(method => method.IsStatic
|
|
? Delegate.CreateDelegate(hookDelegateType, method, false)
|
|
: Delegate.CreateDelegate(hookDelegateType, self, method, false))
|
|
.Where(del => del != null)
|
|
.ToArray();
|
|
if (matches.Length != 1) {
|
|
LogInvalid("Either found no matching detours or found more than one: specify a detour name");
|
|
continue;
|
|
}
|
|
|
|
detour = matches[0]!;
|
|
} else {
|
|
var method = selfType.GetMethod(sig.DetourName, Flags);
|
|
if (method == null) {
|
|
LogInvalid($"Could not find detour \"{sig.DetourName}\"");
|
|
continue;
|
|
}
|
|
|
|
var del = method.IsStatic
|
|
? Delegate.CreateDelegate(hookDelegateType, method, false)
|
|
: Delegate.CreateDelegate(hookDelegateType, self, method, false);
|
|
if (del == null) {
|
|
LogInvalid($"Method {sig.DetourName} was not compatible with delegate {hookDelegateType.Name}");
|
|
continue;
|
|
}
|
|
|
|
detour = del;
|
|
}
|
|
|
|
var ctor = actualType.GetConstructor(new[] { typeof(IntPtr), hookDelegateType });
|
|
if (ctor == null) {
|
|
PluginLog.Error("Error in Siggingway: could not find Hook constructor");
|
|
continue;
|
|
}
|
|
|
|
var hook = ctor.Invoke(new object?[] { ptr, detour });
|
|
info.SetValue(self, hook);
|
|
PluginLog.Information($"Hook {selfType.FullName}.{info.Name} set up");
|
|
|
|
break;
|
|
}
|
|
case SignatureUseFlags.Auto when actualType.IsPrimitive:
|
|
case SignatureUseFlags.Offset: {
|
|
var offset = Marshal.PtrToStructure(ptr + sig.Offset, actualType);
|
|
info.SetValue(self, offset);
|
|
|
|
break;
|
|
}
|
|
default: {
|
|
if (log) {
|
|
LogInvalid("could not detect desired signature use, set SignatureUseFlags manually");
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|