//
// 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 Optional WrapperFunction { get; }
///
public TFunction OriginalFunction
{
get
{
if (_originalFunction is null)
{
_originalFunction = WrapWithCalling(Hook.OriginalFunction.GetWrapper());
}
return _originalFunction;
}
}
///
public event EventHandler? Called;
///
public bool IsUsable => WrapperFunction.IsPresent;
///
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;
}
}