// // SceneManagerBinding.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 NosSmooth.LocalBinding.Structs; using Reloaded.Hooks; using Reloaded.Hooks.Definitions; using Reloaded.Hooks.Definitions.X86; using Reloaded.Memory.Buffers.Internal.Kernel32; 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 SceneManagerBinding { [Function ( new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx }, FunctionAttribute.Register.ecx, FunctionAttribute.StackCleanup.Callee )] private delegate void FocusEntityDelegate(IntPtr outputStringPtr, IntPtr entityPtr); /// /// Create the scene manager binding. /// /// The binding manager. /// The scene manager. /// The options for the binding. /// A network binding or an error. public static Result Create (NosBindingManager bindingManager, SceneManager sceneManager, SceneManagerBindingOptions bindingOptions) { var process = Process.GetCurrentProcess(); var focusEntityAddress = bindingManager.Scanner.CompiledFindPattern(bindingOptions.FocusEntityPattern); if (!focusEntityAddress.Found) { return new BindingNotFoundError(bindingOptions.FocusEntityPattern, "SceneManagerBinding.FocusEntity"); } var focusEntityFunction = bindingManager.Hooks.CreateFunction (focusEntityAddress.Offset + (int)process.MainModule!.BaseAddress + 0x04); var focusEntityWrapper = focusEntityFunction.GetWrapper(); var binding = new SceneManagerBinding ( bindingManager, sceneManager, focusEntityWrapper ); if (bindingOptions.HookFocusEntity) { binding._focusHook = focusEntityFunction.Hook(binding.FocusEntityDetour); binding._originalFocusEntity = binding._focusHook.OriginalFunction; } binding._focusHook?.Activate(); return binding; } private readonly NosBindingManager _bindingManager; private FocusEntityDelegate _originalFocusEntity; private IHook? _focusHook; private SceneManagerBinding ( NosBindingManager bindingManager, SceneManager sceneManager, FocusEntityDelegate originalFocusEntity ) { _originalFocusEntity = originalFocusEntity; _bindingManager = bindingManager; SceneManager = sceneManager; } /// /// 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; /// /// Gets the scene manager object. /// public SceneManager SceneManager { get; } /// /// Focus the entity with the given id. /// /// The id of the entity. /// A result that may or may not have succeeded. public Result FocusEntity(int id) { var entityResult = FindEntity(id); if (!entityResult.IsSuccess) { return Result.FromError(entityResult); } return FocusEntity(entityResult.Entity); } /// /// Focus the entity. /// /// The entity. /// A result that may or may not have succeeded. public Result FocusEntity(MapBaseObj? entity) => FocusEntity(entity?.Address ?? IntPtr.Zero); /// /// Focus the entity. /// /// The entity address. /// A result that may or may not have succeeded. public Result FocusEntity(IntPtr entityAddress) { try { _originalFocusEntity(IntPtr.Zero, entityAddress); } catch (Exception e) { return e; } return Result.FromSuccess(); } /// /// Find the given entity address. /// /// The id of the entity. /// The pointer to the entity or an error. public Result FindEntity(int id) { if (id == 0) { return Result.FromSuccess(null); } var manager = SceneManager; var item = manager.ItemList.FirstOrDefault(x => x.Id == id); if (item is not null) { return item; } var monster = manager.MonsterList.FirstOrDefault(x => x.Id == id); if (monster is not null) { return monster; } var npc = manager.NpcList.FirstOrDefault(x => x.Id == id); if (npc is not null) { return npc; } var player = manager.PlayerList.FirstOrDefault(x => x.Id == id); if (player is not null) { return player; } return new NotFoundError($"Could not find entity with id {id}"); } private void FocusEntityDetour(IntPtr outputStringPtr, IntPtr entityId) { MapBaseObj? obj = null; if (entityId != IntPtr.Zero) { obj = new MapBaseObj(_bindingManager.Memory, entityId); } var result = EntityFocus?.Invoke(obj); if (result ?? true) { _originalFocusEntity(outputStringPtr, entityId); } } }