From f6304c81af31a0efff0db7724afab78972567b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Boh=C3=A1=C4=8Dek?= Date: Thu, 20 Jan 2022 19:26:32 +0100 Subject: [PATCH] feat(localbinding): add binding of scene manager --- .../Extensions/ServiceCollectionExtensions.cs | 1 + .../NosBindingManager.cs | 32 ++- .../Objects/SceneManagerBinding.cs | 216 ++++++++++++++++++ .../Options/SceneManagerBindingOptions.cs | 35 +++ .../Structs/MapBaseObj.cs | 46 ++++ .../Structs/MapObjBaseList.cs | 112 +++++++++ .../Structs/SceneManager.cs | 55 +++++ .../Extensions/ServiceCollectionExtensions.cs | 12 + 8 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 Local/NosSmooth.LocalBinding/Objects/SceneManagerBinding.cs create mode 100644 Local/NosSmooth.LocalBinding/Options/SceneManagerBindingOptions.cs create mode 100644 Local/NosSmooth.LocalBinding/Structs/MapBaseObj.cs create mode 100644 Local/NosSmooth.LocalBinding/Structs/MapObjBaseList.cs create mode 100644 Local/NosSmooth.LocalBinding/Structs/SceneManager.cs diff --git a/Local/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs b/Local/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs index 1db8c2e..9eafe6f 100644 --- a/Local/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs +++ b/Local/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs @@ -30,6 +30,7 @@ public static class ServiceCollectionExtensions return serviceCollection .AddSingleton() .AddSingleton(p => p.GetRequiredService().Character) + .AddSingleton(p => p.GetRequiredService().SceneManager) .AddSingleton(p => p.GetRequiredService().Network); } } \ No newline at end of file diff --git a/Local/NosSmooth.LocalBinding/NosBindingManager.cs b/Local/NosSmooth.LocalBinding/NosBindingManager.cs index 3c82b8f..252458e 100644 --- a/Local/NosSmooth.LocalBinding/NosBindingManager.cs +++ b/Local/NosSmooth.LocalBinding/NosBindingManager.cs @@ -23,19 +23,23 @@ public class NosBindingManager : IDisposable { private readonly CharacterBindingOptions _characterBindingOptions; private readonly NetworkBindingOptions _networkBindingOptions; + private SceneManagerBindingOptions _sceneManagerBindingOptions; private NetworkBinding? _networkBinding; private CharacterBinding? _characterBinding; + private SceneManagerBinding? _sceneManagerBinding; /// /// Initializes a new instance of the class. /// /// The character binding options. /// The network binding options. + /// The scene manager binding options. public NosBindingManager ( IOptions characterBindingOptions, - IOptions networkBindingOptions + IOptions networkBindingOptions, + IOptions sceneManagerBindingOptions ) { Hooks = new ReloadedHooks(); @@ -43,6 +47,7 @@ public class NosBindingManager : IDisposable Scanner = new Scanner(Process.GetCurrentProcess(), Process.GetCurrentProcess().MainModule); _characterBindingOptions = characterBindingOptions.Value; _networkBindingOptions = networkBindingOptions.Value; + _sceneManagerBindingOptions = sceneManagerBindingOptions.Value; } /// @@ -96,6 +101,24 @@ public class NosBindingManager : IDisposable } } + /// + /// Gets the character binding. + /// + /// Thrown if the manager is not initialized yet. + public SceneManagerBinding SceneManager + { + get + { + if (_sceneManagerBinding is null) + { + throw new InvalidOperationException + ("Could not get scene manager. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?"); + } + + return _sceneManagerBinding; + } + } + /// /// Initialize the existing bindings and hook NosTale functions. /// @@ -116,6 +139,13 @@ public class NosBindingManager : IDisposable } _characterBinding = character.Entity; + var sceneManager = SceneManagerBinding.Create(this, _sceneManagerBindingOptions); + if (!sceneManager.IsSuccess) + { + return Result.FromError(sceneManager); + } + _sceneManagerBinding = sceneManager.Entity; + return Result.FromSuccess(); } diff --git a/Local/NosSmooth.LocalBinding/Objects/SceneManagerBinding.cs b/Local/NosSmooth.LocalBinding/Objects/SceneManagerBinding.cs new file mode 100644 index 0000000..9fee27c --- /dev/null +++ b/Local/NosSmooth.LocalBinding/Objects/SceneManagerBinding.cs @@ -0,0 +1,216 @@ +// +// 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); + } + } +} \ No newline at end of file diff --git a/Local/NosSmooth.LocalBinding/Options/SceneManagerBindingOptions.cs b/Local/NosSmooth.LocalBinding/Options/SceneManagerBindingOptions.cs new file mode 100644 index 0000000..fdd9266 --- /dev/null +++ b/Local/NosSmooth.LocalBinding/Options/SceneManagerBindingOptions.cs @@ -0,0 +1,35 @@ +// +// SceneManagerBindingOptions.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 NosSmooth.LocalBinding.Objects; + +namespace NosSmooth.LocalBinding.Options; + +/// +/// Options for . +/// +public class SceneManagerBindingOptions +{ + /// + /// Gets or sets the pattern to find the scene manager object at. + /// + /// + /// The address of the object is direct pointer to the scene manager. + /// + public string SceneManagerObjectPattern { get; set; } + = "FF ?? ?? ?? ?? ?? FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF"; + + /// + /// Gets or sets the pattern to find the focus entity method at. + /// + public string FocusEntityPattern { get; set; } + = "73 00 00 00 55 8b ec b9 05 00 00 00"; + + /// + /// Gets or sets whether to hook the Focus entity function. + /// + public bool HookFocusEntity { get; set; } = true; +} \ No newline at end of file diff --git a/Local/NosSmooth.LocalBinding/Structs/MapBaseObj.cs b/Local/NosSmooth.LocalBinding/Structs/MapBaseObj.cs new file mode 100644 index 0000000..4ac3a2a --- /dev/null +++ b/Local/NosSmooth.LocalBinding/Structs/MapBaseObj.cs @@ -0,0 +1,46 @@ +// +// MapBaseObj.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 Reloaded.Memory.Sources; + +namespace NosSmooth.LocalBinding.Structs; + +/// +/// Base map object. Common for players, monsters, npcs. +/// +public class MapBaseObj +{ + private readonly IMemory _memory; + private readonly IntPtr _mapObjPointer; + + /// + /// Initializes a new instance of the class. + /// + /// The memory. + /// The map object pointer. + public MapBaseObj(IMemory memory, IntPtr mapObjPointer) + { + _memory = memory; + _mapObjPointer = mapObjPointer; + } + + /// + /// Gets the pointer to the object. + /// + public IntPtr Address => _mapObjPointer; + + /// + /// Gets the id of the entity. + /// + public long Id + { + get + { + _memory.SafeRead(_mapObjPointer + 0x08, out int id); + return id; + } + } +} \ No newline at end of file diff --git a/Local/NosSmooth.LocalBinding/Structs/MapObjBaseList.cs b/Local/NosSmooth.LocalBinding/Structs/MapObjBaseList.cs new file mode 100644 index 0000000..66755f2 --- /dev/null +++ b/Local/NosSmooth.LocalBinding/Structs/MapObjBaseList.cs @@ -0,0 +1,112 @@ +// +// MapObjBaseList.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.Collections; +using Reloaded.Memory.Pointers; +using Reloaded.Memory.Sources; + +namespace NosSmooth.LocalBinding.Structs; + +/// +/// List of map objects. +/// +public class MapObjBaseList : IEnumerable +{ + private readonly IMemory _memory; + private readonly IntPtr _objListPointer; + private readonly ArrayPtr _objList; + + /// + /// Initializes a new instance of the class. + /// + /// The memory. + /// The object list pointer. + public MapObjBaseList(IMemory memory, IntPtr objListPointer) + { + memory.Read(objListPointer + 0x04, out IntPtr arrayFirst); + _objList = new ArrayPtr((ulong)arrayFirst, source: memory); + _memory = memory; + _objListPointer = objListPointer; + } + + /// + /// Gets the element at the given index. + /// + /// The index of the element. + /// Thrown if the index is not in the bounds of the array. + public MapBaseObj this[int index] + { + get + { + if (index >= Length || index < 0) + { + throw new IndexOutOfRangeException(); + } + + return new MapBaseObj(_memory, _objList[index]); + } + } + + /// + /// Gets the length of the array. + /// + public int Length + { + get + { + _memory.SafeRead(_objListPointer + 0x08, out int length); + return length; + } + } + + /// + public IEnumerator GetEnumerator() + { + return new MapObjBaseEnumerator(this); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + private class MapObjBaseEnumerator : IEnumerator + { + private readonly MapObjBaseList _list; + private int _index; + + public MapObjBaseEnumerator(MapObjBaseList list) + { + _index = -1; + _list = list; + } + + public bool MoveNext() + { + if (_list.Length > _index + 1) + { + _index++; + return true; + } + + return false; + } + + public void Reset() + { + _index = -1; + } + + public MapBaseObj Current => _list[_index]; + + object IEnumerator.Current => Current; + + public void Dispose() + { + } + } +} \ No newline at end of file diff --git a/Local/NosSmooth.LocalBinding/Structs/SceneManager.cs b/Local/NosSmooth.LocalBinding/Structs/SceneManager.cs new file mode 100644 index 0000000..6d876aa --- /dev/null +++ b/Local/NosSmooth.LocalBinding/Structs/SceneManager.cs @@ -0,0 +1,55 @@ +// +// SceneManager.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 Reloaded.Memory.Sources; + +namespace NosSmooth.LocalBinding.Structs; + +/// +/// Represents nostale scene manager struct. +/// +public class SceneManager +{ + private readonly IMemory _memory; + private readonly IntPtr _sceneManager; + + /// + /// Initializes a new instance of the class. + /// + /// The memory. + /// The pointer to the scene manager. + public SceneManager(IMemory memory, IntPtr sceneManager) + { + _memory = memory; + _sceneManager = sceneManager; + } + + /// + /// Gets the player list. + /// + public MapObjBaseList PlayerList => new MapObjBaseList(_memory, ReadPtr(_sceneManager + 0xC)); + + /// + /// Gets the monster list. + /// + public MapObjBaseList MonsterList => new MapObjBaseList(_memory, ReadPtr(_sceneManager + 0x10)); + + /// + /// Gets the npc list. + /// + public MapObjBaseList NpcList => new MapObjBaseList(_memory, ReadPtr(_sceneManager + 0x14)); + + /// + /// Gets the item list. + /// + public MapObjBaseList ItemList => new MapObjBaseList(_memory, ReadPtr(_sceneManager + 0x18)); + + private IntPtr ReadPtr(IntPtr ptr) + { + _memory.Read(ptr, out IntPtr read); + return read; + } +} \ No newline at end of file diff --git a/Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs b/Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs index c8e1fbb..47d882c 100644 --- a/Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs +++ b/Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs @@ -36,4 +36,16 @@ public static class ServiceCollectionExtensions return serviceCollection; } + + /// + /// Adds packet interceptor. + /// + /// The service collection. + /// The type of the interceptor. + /// The collection. + public static IServiceCollection AddPacketInterceptor(this IServiceCollection serviceCollection) + where TInterceptor : class, IPacketInterceptor + { + return serviceCollection.AddSingleton(); + } } \ No newline at end of file -- 2.48.1