~ruther/NosSmooth

f6304c81af31a0efff0db7724afab78972567b5e — František Boháček 3 years ago e1ab9a1
feat(localbinding): add binding of scene manager
M Local/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs => Local/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs +1 -0
@@ 30,6 30,7 @@ public static class ServiceCollectionExtensions
        return serviceCollection
            .AddSingleton<NosBindingManager>()
            .AddSingleton(p => p.GetRequiredService<NosBindingManager>().Character)
            .AddSingleton(p => p.GetRequiredService<NosBindingManager>().SceneManager)
            .AddSingleton(p => p.GetRequiredService<NosBindingManager>().Network);
    }
}
\ No newline at end of file

M Local/NosSmooth.LocalBinding/NosBindingManager.cs => Local/NosSmooth.LocalBinding/NosBindingManager.cs +31 -1
@@ 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;

    /// <summary>
    /// Initializes a new instance of the <see cref="NosBindingManager"/> class.
    /// </summary>
    /// <param name="characterBindingOptions">The character binding options.</param>
    /// <param name="networkBindingOptions">The network binding options.</param>
    /// <param name="sceneManagerBindingOptions">The scene manager binding options.</param>
    public NosBindingManager
    (
        IOptions<CharacterBindingOptions> characterBindingOptions,
        IOptions<NetworkBindingOptions> networkBindingOptions
        IOptions<NetworkBindingOptions> networkBindingOptions,
        IOptions<SceneManagerBindingOptions> 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;
    }

    /// <summary>


@@ 97,6 102,24 @@ public class NosBindingManager : IDisposable
    }

    /// <summary>
    /// Gets the character binding.
    /// </summary>
    /// <exception cref="InvalidOperationException">Thrown if the manager is not initialized yet.</exception>
    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;
        }
    }

    /// <summary>
    /// Initialize the existing bindings and hook NosTale functions.
    /// </summary>
    /// <returns>A result that may or may not have succeeded.</returns>


@@ 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();
    }


A Local/NosSmooth.LocalBinding/Objects/SceneManagerBinding.cs => Local/NosSmooth.LocalBinding/Objects/SceneManagerBinding.cs +216 -0
@@ 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;

/// <summary>
/// The nostale binding of a scene manager.
/// </summary>
/// <remarks>
/// The scene manager holds addresses to entities, mouse position, ....
/// </remarks>
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);

    /// <summary>
    /// Create the scene manager binding.
    /// </summary>
    /// <param name="bindingManager">The binding manager.</param>
    /// <param name="bindingOptions">The options for the binding.</param>
    /// <returns>A network binding or an error.</returns>
    public static Result<SceneManagerBinding> 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<FocusEntityDelegate>
            (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<FocusEntityDelegate>? _focusHook;

    private SceneManagerBinding
    (
        NosBindingManager bindingManager,
        IntPtr sceneManagerAddress,
        FocusEntityDelegate originalFocusEntity
    )
    {
        _originalFocusEntity = originalFocusEntity;
        _bindingManager = bindingManager;
        _sceneManagerAddress = sceneManagerAddress;
    }

    /// <summary>
    /// Event that is called when entity focus was called by NosTale.
    /// </summary>
    /// <remarks>
    /// The focus entity must be hooked for this event to be called.
    /// </remarks>
    public event Func<MapBaseObj?, bool>? EntityFocus;

    /// <summary>
    /// Gets the scene manager object.
    /// </summary>
    public SceneManager SceneManager => new SceneManager(_bindingManager.Memory, GetSceneManagerAddress());

    private IntPtr GetSceneManagerAddress()
    {
        IntPtr sceneManagerAddress = _sceneManagerAddress;
        _bindingManager.Memory.Read(sceneManagerAddress, out sceneManagerAddress);

        return sceneManagerAddress;
    }

    /// <summary>
    /// Focus the entity with the given id.
    /// </summary>
    /// <param name="id">The id of the entity.</param>
    /// <returns>A result that may or may not have succeeded.</returns>
    public Result FocusEntity(int id)
    {
        var entityResult = FindEntity(id);
        if (!entityResult.IsSuccess)
        {
            return Result.FromError(entityResult);
        }

        return FocusEntity(entityResult.Entity);
    }

    /// <summary>
    /// Focus the entity.
    /// </summary>
    /// <param name="entity">The entity.</param>
    /// <returns>A result that may or may not have succeeded.</returns>
    public Result FocusEntity(MapBaseObj? entity)
        => FocusEntity(entity?.Address ?? IntPtr.Zero);

    /// <summary>
    /// Focus the entity.
    /// </summary>
    /// <param name="entityAddress">The entity address.</param>
    /// <returns>A result that may or may not have succeeded.</returns>
    public Result FocusEntity(IntPtr entityAddress)
    {
        try
        {
            _originalFocusEntity(IntPtr.Zero, entityAddress);
        }
        catch (Exception e)
        {
            return e;
        }

        return Result.FromSuccess();
    }

    /// <summary>
    /// Find the given entity address.
    /// </summary>
    /// <param name="id">The id of the entity.</param>
    /// <returns>The pointer to the entity or an error.</returns>
    public Result<MapBaseObj?> FindEntity(int id)
    {
        if (id == 0)
        {
            return Result<MapBaseObj?>.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

A Local/NosSmooth.LocalBinding/Options/SceneManagerBindingOptions.cs => Local/NosSmooth.LocalBinding/Options/SceneManagerBindingOptions.cs +35 -0
@@ 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;

/// <summary>
/// Options for <see cref="SceneManagerBinding"/>.
/// </summary>
public class SceneManagerBindingOptions
{
    /// <summary>
    /// Gets or sets the pattern to find the scene manager object at.
    /// </summary>
    /// <remarks>
    /// The address of the object is direct pointer to the scene manager.
    /// </remarks>
    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";

    /// <summary>
    /// Gets or sets the pattern to find the focus entity method at.
    /// </summary>
    public string FocusEntityPattern { get; set; }
        = "73 00 00 00 55 8b ec b9 05 00 00 00";

    /// <summary>
    /// Gets or sets whether to hook the Focus entity function.
    /// </summary>
    public bool HookFocusEntity { get; set; } = true;
}
\ No newline at end of file

A Local/NosSmooth.LocalBinding/Structs/MapBaseObj.cs => Local/NosSmooth.LocalBinding/Structs/MapBaseObj.cs +46 -0
@@ 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;

/// <summary>
/// Base map object. Common for players, monsters, npcs.
/// </summary>
public class MapBaseObj
{
    private readonly IMemory _memory;
    private readonly IntPtr _mapObjPointer;

    /// <summary>
    /// Initializes a new instance of the <see cref="MapBaseObj"/> class.
    /// </summary>
    /// <param name="memory">The memory.</param>
    /// <param name="mapObjPointer">The map object pointer.</param>
    public MapBaseObj(IMemory memory, IntPtr mapObjPointer)
    {
        _memory = memory;
        _mapObjPointer = mapObjPointer;
    }

    /// <summary>
    /// Gets the pointer to the object.
    /// </summary>
    public IntPtr Address => _mapObjPointer;

    /// <summary>
    /// Gets the id of the entity.
    /// </summary>
    public long Id
    {
        get
        {
            _memory.SafeRead(_mapObjPointer + 0x08, out int id);
            return id;
        }
    }
}
\ No newline at end of file

A Local/NosSmooth.LocalBinding/Structs/MapObjBaseList.cs => Local/NosSmooth.LocalBinding/Structs/MapObjBaseList.cs +112 -0
@@ 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;

/// <summary>
/// List of map objects.
/// </summary>
public class MapObjBaseList : IEnumerable<MapBaseObj>
{
    private readonly IMemory _memory;
    private readonly IntPtr _objListPointer;
    private readonly ArrayPtr<IntPtr> _objList;

    /// <summary>
    /// Initializes a new instance of the <see cref="MapObjBaseList"/> class.
    /// </summary>
    /// <param name="memory">The memory.</param>
    /// <param name="objListPointer">The object list pointer.</param>
    public MapObjBaseList(IMemory memory, IntPtr objListPointer)
    {
        memory.Read(objListPointer + 0x04, out IntPtr arrayFirst);
        _objList = new ArrayPtr<IntPtr>((ulong)arrayFirst, source: memory);
        _memory = memory;
        _objListPointer = objListPointer;
    }

    /// <summary>
    /// Gets the element at the given index.
    /// </summary>
    /// <param name="index">The index of the element.</param>
    /// <exception cref="IndexOutOfRangeException">Thrown if the index is not in the bounds of the array.</exception>
    public MapBaseObj this[int index]
    {
        get
        {
            if (index >= Length || index < 0)
            {
                throw new IndexOutOfRangeException();
            }

            return new MapBaseObj(_memory, _objList[index]);
        }
    }

    /// <summary>
    /// Gets the length of the array.
    /// </summary>
    public int Length
    {
        get
        {
            _memory.SafeRead(_objListPointer + 0x08, out int length);
            return length;
        }
    }

    /// <inheritdoc/>
    public IEnumerator<MapBaseObj> GetEnumerator()
    {
        return new MapObjBaseEnumerator(this);
    }

    /// <inheritdoc/>
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    private class MapObjBaseEnumerator : IEnumerator<MapBaseObj>
    {
        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

A Local/NosSmooth.LocalBinding/Structs/SceneManager.cs => Local/NosSmooth.LocalBinding/Structs/SceneManager.cs +55 -0
@@ 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;

/// <summary>
/// Represents nostale scene manager struct.
/// </summary>
public class SceneManager
{
    private readonly IMemory _memory;
    private readonly IntPtr _sceneManager;

    /// <summary>
    /// Initializes a new instance of the <see cref="SceneManager"/> class.
    /// </summary>
    /// <param name="memory">The memory.</param>
    /// <param name="sceneManager">The pointer to the scene manager.</param>
    public SceneManager(IMemory memory, IntPtr sceneManager)
    {
        _memory = memory;
        _sceneManager = sceneManager;
    }

    /// <summary>
    /// Gets the player list.
    /// </summary>
    public MapObjBaseList PlayerList => new MapObjBaseList(_memory, ReadPtr(_sceneManager + 0xC));

    /// <summary>
    /// Gets the monster list.
    /// </summary>
    public MapObjBaseList MonsterList => new MapObjBaseList(_memory, ReadPtr(_sceneManager + 0x10));

    /// <summary>
    /// Gets the npc list.
    /// </summary>
    public MapObjBaseList NpcList => new MapObjBaseList(_memory, ReadPtr(_sceneManager + 0x14));

    /// <summary>
    /// Gets the item list.
    /// </summary>
    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

M Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs => Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs +12 -0
@@ 36,4 36,16 @@ public static class ServiceCollectionExtensions

        return serviceCollection;
    }

    /// <summary>
    /// Adds packet interceptor.
    /// </summary>
    /// <param name="serviceCollection">The service collection.</param>
    /// <typeparam name="TInterceptor">The type of the interceptor.</typeparam>
    /// <returns>The collection.</returns>
    public static IServiceCollection AddPacketInterceptor<TInterceptor>(this IServiceCollection serviceCollection)
        where TInterceptor : class, IPacketInterceptor
    {
        return serviceCollection.AddSingleton<IPacketInterceptor, TInterceptor>();
    }
}
\ No newline at end of file

Do not follow this link