// // 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); } } }