// // CancelableNostaleHook.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.ComponentModel; using System.Diagnostics; using NosSmooth.LocalBinding.Extensions; using Remora.Results; namespace NosSmooth.LocalBinding.Hooks.Implementations; /// /// A hook of a NosTale function /// that may be cancelled (not propagated to NosTale) when fired. /// /// The function delegate. /// A wrapper function that abstracts the call to original function. May get the neccessary object to call the function and accept only relevant arguments. /// The event args used in case of a call. public abstract class CancelableNostaleHook : INostaleHook where TFunction : Delegate where TWrapperFunction : Delegate where TEventArgs : CancelEventArgs { /// /// Creates the hook instance. /// /// The binding manager to create the hook. /// Create new hook object. /// The function that obtains detour. /// The options. /// The hook type. /// The hook, or failed result. protected static Result CreateHook(NosBindingManager bindingManager, Func @new, Func detour, HookOptions options) where T : CancelableNostaleHook { var nosHook = @new(); var hookResult = bindingManager.CreateCustomAsmHookFromPattern (nosHook.Name, detour(nosHook), options); if (!hookResult.IsDefined(out var hook)) { return Result.FromError(hookResult); } nosHook.Hook = hook; return nosHook; } private TFunction? _originalFunction; /// /// Gets the hook. /// protected NosAsmHook Hook { get; set; } = null!; /// /// Set to true at start of WrapWithCalling, set to false at end of WrapWithCalling. /// protected bool CallingFromNosSmooth { get; set; } /// public bool IsEnabled => Hook.Hook.IsEnabled; /// public abstract TWrapperFunction WrapperFunction { get; } /// public TFunction OriginalFunction { get { if (_originalFunction is null) { _originalFunction = WrapWithCalling(Hook.OriginalFunction.GetWrapper()); } return _originalFunction; } } /// public event EventHandler? Called; /// public abstract string Name { get; } /// public Result Enable() { Hook.Hook.EnableOrActivate(); return Result.FromSuccess(); } /// public Result Disable() { Hook.Hook.Disable(); return Result.FromSuccess(); } /// /// Wrap the target function with setting CallingFromNosSmooth to true. /// /// /// protected MyFun WrapWithCalling(MyFun fun) { /// return (a, b) => { /// CallingFromNosSmooth = true; /// var res = fun(a, b); /// CallingFromNosSmooth = false; /// return res; /// } /// }. /// /// The function to wrap. /// The wrapped function. protected abstract TFunction WrapWithCalling(TFunction function); /// /// Calls the event, returns whether to proceed. /// /// The event arguments. /// Whether to proceed. protected nuint HandleCall(TEventArgs args) { if (CallingFromNosSmooth) { // this is a call from NosSmooth, do not invoke the event. return 1; } Called?.Invoke(this, args); return args.Cancel ? 0 : (nuint)1; } }