// // UnitManagerBinding.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.Extensions; using NosSmooth.LocalBinding.Options; using NosSmooth.LocalBinding.Structs; using Reloaded.Hooks.Definitions; using Reloaded.Hooks.Definitions.X86; using Reloaded.Hooks.Internal.Testing; using Remora.Results; namespace NosSmooth.LocalBinding.Objects; /// /// The nostale binding of a scene manager. /// /// /// The scene manager holds addresses to entities, mouse position, .... /// public class UnitManagerBinding { [Function ( new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx }, FunctionAttribute.Register.eax, FunctionAttribute.StackCleanup.Callee, new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp } )] private delegate nuint FocusEntityDelegate(nuint unitManagerPtr, nuint entityPtr); /// /// Create the scene manager binding. /// /// The binding manager. /// The options for the binding. /// A network binding or an error. public static Result Create (NosBindingManager bindingManager, UnitManagerBindingOptions bindingOptions) { var process = Process.GetCurrentProcess(); var unitManagerStaticAddress = bindingManager.Scanner.FindPattern(bindingOptions.UnitManagerPattern); if (!unitManagerStaticAddress.Found) { return new BindingNotFoundError(bindingOptions.UnitManagerPattern, "UnitManagerBinding.UnitManager"); } var binding = new UnitManagerBinding ( bindingManager, (int)process.MainModule!.BaseAddress + unitManagerStaticAddress.Offset, bindingOptions.UnitManagerOffsets ); var entityFocusHookResult = bindingManager.CreateCustomAsmHookFromPattern ("UnitManager.EntityFocus", binding.FocusEntityDetour, bindingOptions.EntityFocusHook); if (!entityFocusHookResult.IsDefined(out var entityFocusHook)) { return Result.FromError(entityFocusHookResult); } binding._focusHook = entityFocusHook; return binding; } private readonly int _staticUnitManagerAddress; private readonly int[] _unitManagerOffsets; private readonly NosBindingManager _bindingManager; private NosAsmHook _focusHook = null!; private bool _callingFocus; private UnitManagerBinding ( NosBindingManager bindingManager, int staticUnitManagerAddress, int[] unitManagerOffsets ) { _bindingManager = bindingManager; _staticUnitManagerAddress = staticUnitManagerAddress; _unitManagerOffsets = unitManagerOffsets; } /// /// Gets the address of unit manager. /// public nuint Address => _bindingManager.Memory.FollowStaticAddressOffsets (_staticUnitManagerAddress, _unitManagerOffsets); /// /// Event that is called when entity focus was called by NosTale. /// /// /// The focus entity must be hooked for this event to be called. /// public event Func? EntityFocus; /// /// Focus the entity. /// /// The entity. /// A result that may or may not have succeeded. public Result FocusEntity(MapBaseObj? entity) => FocusEntity(entity?.Address ?? nuint.Zero); /// /// Disable all UnitManager hooks. /// public void DisableHooks() { _focusHook.Hook.Disable(); } /// /// Enable all UnitManager hooks. /// public void EnableHooks() { _focusHook.Hook.EnableOrActivate(); } /// /// Focus the entity. /// /// The entity address. /// A result that may or may not have succeeded. public Result FocusEntity(nuint entityAddress) { try { _callingFocus = true; _focusHook.OriginalFunction.GetWrapper()(Address, entityAddress); _callingFocus = false; } catch (Exception e) { return e; } return Result.FromSuccess(); } private nuint FocusEntityDetour(nuint unitManagerPtr, nuint entityId) { if (_callingFocus) { // in case this is being called from UnitManagerBinding.FocusEntity, // do not handle. return 1; } MapBaseObj? obj = null; if (entityId != nuint.Zero) { obj = new MapBaseObj(_bindingManager.Memory, entityId); } return (EntityFocus?.Invoke(obj) ?? true) ? (nuint)1 : 0; } }