// // NosBindingManager.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 Microsoft.Extensions.Options; using NosSmooth.LocalBinding.Errors; using NosSmooth.LocalBinding.Objects; using NosSmooth.LocalBinding.Options; using Reloaded.Hooks; using Reloaded.Hooks.Definitions; using Reloaded.Memory.Sigscan; using Reloaded.Memory.Sources; using Remora.Results; namespace NosSmooth.LocalBinding; /// /// Nostale entity binding manager. /// public class NosBindingManager : IDisposable { private readonly NosBrowserManager _browserManager; private readonly PetManagerBindingOptions _petManagerBindingOptions; private readonly CharacterBindingOptions _characterBindingOptions; private readonly NetworkBindingOptions _networkBindingOptions; private UnitManagerBindingOptions _unitManagerBindingOptions; private NetworkBinding? _networkBinding; private PlayerManagerBinding? _characterBinding; private UnitManagerBinding? _unitManagerBinding; private PetManagerBinding? _petManagerBinding; /// /// Initializes a new instance of the class. /// /// The NosTale browser manager. /// The character binding options. /// The network binding options. /// The scene manager binding options. /// The pet manager binding options. public NosBindingManager ( NosBrowserManager browserManager, IOptions characterBindingOptions, IOptions networkBindingOptions, IOptions sceneManagerBindingOptions, IOptions petManagerBindingOptions ) { _browserManager = browserManager; Hooks = new ReloadedHooks(); Memory = new Memory(); Scanner = new Scanner(Process.GetCurrentProcess(), Process.GetCurrentProcess().MainModule); _characterBindingOptions = characterBindingOptions.Value; _networkBindingOptions = networkBindingOptions.Value; _unitManagerBindingOptions = sceneManagerBindingOptions.Value; _petManagerBindingOptions = petManagerBindingOptions.Value; } /// /// Gets the memory scanner. /// internal Scanner Scanner { get; } /// /// Gets the reloaded hooks. /// internal IReloadedHooks Hooks { get; } /// /// Gets the current process memory. /// internal IMemory Memory { get; } /// /// Gets the network binding. /// /// Thrown if the manager is not initialized yet. public NetworkBinding Network { get { if (_networkBinding is null) { throw new InvalidOperationException ( "Could not get network. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?" ); } return _networkBinding; } } /// /// Gets the character binding. /// /// Thrown if the manager is not initialized yet. public PlayerManagerBinding PlayerManager { get { if (_characterBinding is null) { throw new InvalidOperationException ( "Could not get character. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?" ); } return _characterBinding; } } /// /// Gets the character binding. /// /// Thrown if the manager is not initialized yet. public UnitManagerBinding UnitManager { get { if (_unitManagerBinding is null) { throw new InvalidOperationException ( "Could not get scene manager. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?" ); } return _unitManagerBinding; } } /// /// Gets the pet manager binding. /// /// Thrown if the manager is not initialized yet. public PetManagerBinding PetManager { get { if (_petManagerBinding is null) { throw new InvalidOperationException ( "Could not get pet manager. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?" ); } return _petManagerBinding; } } /// /// Initialize the existing bindings and hook NosTale functions. /// /// A result that may or may not have succeeded. public IResult Initialize() { List errorResults = new List(); var browserInitializationResult = _browserManager.Initialize(); if (!browserInitializationResult.IsSuccess) { if (browserInitializationResult.Error is NotNostaleProcessError) { return browserInitializationResult; } errorResults.Add(browserInitializationResult); } try { var network = NetworkBinding.Create(this, _networkBindingOptions); if (!network.IsSuccess) { errorResults.Add ( Result.FromError ( new CouldNotInitializeModuleError(typeof(NetworkBinding), network.Error), network ) ); } _networkBinding = network.Entity; } catch (Exception e) { errorResults.Add( Result.FromError ( new CouldNotInitializeModuleError(typeof(NetworkBinding), new ExceptionError(e)), (Result)new ExceptionError(e) )); } try { var playerManagerBinding = PlayerManagerBinding.Create ( this, _browserManager.PlayerManager, _characterBindingOptions ); if (!playerManagerBinding.IsSuccess) { errorResults.Add ( Result.FromError ( new CouldNotInitializeModuleError(typeof(PlayerManagerBinding), playerManagerBinding.Error), playerManagerBinding ) ); } _characterBinding = playerManagerBinding.Entity; } catch (Exception e) { errorResults.Add ( Result.FromError ( new CouldNotInitializeModuleError(typeof(PlayerManagerBinding), new ExceptionError(e)), (Result)new ExceptionError(e) ) ); } try { var unitManagerBinding = UnitManagerBinding.Create ( this, _unitManagerBindingOptions ); if (!unitManagerBinding.IsSuccess) { errorResults.Add ( Result.FromError ( new CouldNotInitializeModuleError(typeof(UnitManagerBinding), unitManagerBinding.Error), unitManagerBinding ) ); } _unitManagerBinding = unitManagerBinding.Entity; } catch (Exception e) { errorResults.Add ( Result.FromError ( new CouldNotInitializeModuleError(typeof(UnitManagerBinding), new ExceptionError(e)), (Result)new ExceptionError(e) ) ); } try { var petManagerBinding = PetManagerBinding.Create ( this, _browserManager.PetManagerList, _petManagerBindingOptions ); if (!petManagerBinding.IsSuccess) { errorResults.Add ( Result.FromError ( new CouldNotInitializeModuleError(typeof(PetManagerBinding), petManagerBinding.Error), petManagerBinding ) ); } _petManagerBinding = petManagerBinding.Entity; } catch (Exception e) { errorResults.Add ( Result.FromError ( new CouldNotInitializeModuleError(typeof(UnitManagerBinding), new ExceptionError(e)), (Result)new ExceptionError(e) ) ); } return errorResults.Count switch { 0 => Result.FromSuccess(), 1 => errorResults[0], _ => (Result)new AggregateError(errorResults) }; } /// /// Disable the currently enabled nostale hooks. /// /// A result that may or may not have succeeded. public Result DisableHooks() { if (_networkBinding is not null) { var result = _networkBinding.DisableHooks(); if (!result.IsSuccess) { return result; } } return Result.FromSuccess(); } /// public void Dispose() { Scanner.Dispose(); DisableHooks(); } /// /// Create a hook object for the given pattern. /// /// The name of the binding. /// The callback function to call instead of the original one. /// The pattern. /// The offset from the pattern. /// Whether to activate the hook. /// The type of the function. /// The hook object or an error. internal Result> CreateHookFromPattern ( string name, TFunction callbackFunction, string pattern, int offset = 0, bool enableHook = true ) { var walkFunctionAddress = Scanner.CompiledFindPattern(pattern); if (!walkFunctionAddress.Found) { return new BindingNotFoundError(pattern, "PetManagerBinding.PetWalk"); } try { var hook = Hooks.CreateHook ( callbackFunction, walkFunctionAddress.Offset + (int)_browserManager.Process.MainModule!.BaseAddress + offset ); if (enableHook) { hook.Activate(); } return Result>.FromSuccess(hook); } catch (Exception e) { return e; } } }