//
// 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 options for the binding.
/// A network binding or an error.
public static Result Create
(NosBindingManager bindingManager, SceneManagerBindingOptions bindingOptions)
{
var process = Process.GetCurrentProcess();
var sceneManagerObjectAddress = bindingManager.Scanner.CompiledFindPattern
(bindingOptions.SceneManagerObjectPattern);
if (!sceneManagerObjectAddress.Found)
{
return new BindingNotFoundError(bindingOptions.SceneManagerObjectPattern, "SceneManagerBinding");
}
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,
(IntPtr)(sceneManagerObjectAddress.Offset + (int)process.MainModule!.BaseAddress + 0x01),
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 readonly IntPtr _sceneManagerAddress;
private FocusEntityDelegate _originalFocusEntity;
private IHook? _focusHook;
private SceneManagerBinding
(
NosBindingManager bindingManager,
IntPtr sceneManagerAddress,
FocusEntityDelegate originalFocusEntity
)
{
_originalFocusEntity = originalFocusEntity;
_bindingManager = bindingManager;
_sceneManagerAddress = sceneManagerAddress;
}
///
/// 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 => new SceneManager(_bindingManager.Memory, GetSceneManagerAddress());
private IntPtr GetSceneManagerAddress()
{
IntPtr sceneManagerAddress = _sceneManagerAddress;
_bindingManager.Memory.Read(sceneManagerAddress, out sceneManagerAddress);
return sceneManagerAddress;
}
///
/// 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);
}
}
}