//
// CharacterBinding.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.Diagnostics;
using NosSmooth.LocalBinding.Errors;
using NosSmooth.LocalBinding.Options;
using Reloaded.Hooks.Definitions;
using Reloaded.Hooks.Definitions.X86;
using Remora.Results;
namespace NosSmooth.LocalBinding.Objects;
///
/// The nostale binding of a character.
///
public class CharacterBinding
{
[Function
(
new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx },
FunctionAttribute.Register.eax,
FunctionAttribute.StackCleanup.Callee
)]
private delegate void WalkDelegate(IntPtr characterObject, int position, int unknown = 1);
///
/// Create the network binding with finding the network object and functions.
///
/// The binding manager.
/// The options for the binding.
/// A network binding or an error.
public static Result Create(NosBindingManager bindingManager, CharacterBindingOptions options)
{
var process = Process.GetCurrentProcess();
var characterObjectAddress = bindingManager.Scanner.CompiledFindPattern(options.CharacterObjectPattern);
if (!characterObjectAddress.Found)
{
return new BindingNotFoundError(options.CharacterObjectPattern, "CharacterBinding");
}
var walkFunctionAddress = bindingManager.Scanner.CompiledFindPattern(options.WalkFunctionPattern);
if (!walkFunctionAddress.Found)
{
return new BindingNotFoundError(options.WalkFunctionPattern, "CharacterBinding.Walk");
}
var walkFunction = bindingManager.Hooks.CreateFunction
(walkFunctionAddress.Offset + (int)process.MainModule!.BaseAddress);
var walkWrapper = walkFunction.GetWrapper();
var binding = new CharacterBinding
(
bindingManager,
(IntPtr)(characterObjectAddress.Offset + (int)process.MainModule!.BaseAddress + 0x06),
walkWrapper
);
if (options.HookWalk)
{
binding._walkHook = walkFunction
.Hook(binding.WalkDetour);
binding._originalWalk = binding._walkHook.OriginalFunction;
}
binding._walkHook?.Activate();
return binding;
}
private readonly NosBindingManager _bindingManager;
private readonly IntPtr _characterAddress;
private IHook? _walkHook;
private WalkDelegate _originalWalk;
private CharacterBinding
(
NosBindingManager bindingManager,
IntPtr characterAddress,
WalkDelegate originalWalk
)
{
_bindingManager = bindingManager;
_characterAddress = characterAddress;
_originalWalk = originalWalk;
}
///
/// Event that is called when walk was called by NosTale.
///
///
/// The walk must be hooked for this event to be called.
///
public event Func? WalkCall;
///
/// Disable all the hooks that are currently enabled.
///
/// A result that may or may not have succeeded.
public Result DisableHooks()
{
_walkHook?.Disable();
return Result.FromSuccess();
}
private IntPtr GetCharacterAddress()
{
IntPtr characterAddress = _characterAddress;
_bindingManager.Memory.Read(characterAddress, out characterAddress);
_bindingManager.Memory.Read(characterAddress, out characterAddress);
return characterAddress;
}
///
/// Walk to the given position.
///
/// The x coordinate.
/// The y coordinate.
/// A result that may or may not have succeeded.
public Result Walk(ushort x, ushort y)
{
int param = (y << 16) | x;
_originalWalk(GetCharacterAddress(), param);
return Result.FromSuccess();
}
private void WalkDetour(IntPtr characterObject, int position, int unknown)
{
var result = WalkCall?.Invoke((ushort)(position & 0xFFFF), (ushort)((position >> 16) & 0xFFFF));
if (result ?? true)
{
_originalWalk(characterObject, position, unknown);
}
}
}