Code commit
This commit is contained in:
59
CustomizePlus/Core/Services/BackupService.cs
Normal file
59
CustomizePlus/Core/Services/BackupService.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Log;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace CustomizePlus.Core.Services;
|
||||
|
||||
public class BackupService
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
private readonly FilenameService _filenameService;
|
||||
private readonly DirectoryInfo _configDirectory;
|
||||
private readonly IReadOnlyList<FileInfo> _fileNames;
|
||||
|
||||
public BackupService(Logger logger, FilenameService filenameService)
|
||||
{
|
||||
_logger = logger;
|
||||
_filenameService = filenameService;
|
||||
_fileNames = PluginFiles(_filenameService);
|
||||
_configDirectory = new DirectoryInfo(_filenameService.ConfigDirectory);
|
||||
Backup.CreateAutomaticBackup(logger, _configDirectory, _fileNames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a permanent backup with a given name for migrations.
|
||||
/// </summary>
|
||||
public void CreateMigrationBackup(string name)
|
||||
=> Backup.CreatePermanentBackup(_logger, _configDirectory, _fileNames, name);
|
||||
|
||||
/// <summary>
|
||||
/// Create backup for all version 3 configuration files
|
||||
/// </summary>
|
||||
public void CreateV3Backup(string name = "v3_to_v4_migration")
|
||||
{
|
||||
var list = new List<FileInfo>(16) { new(_filenameService.ConfigFile) };
|
||||
list.AddRange(Directory.EnumerateFiles(_filenameService.ConfigDirectory, "*.profile", SearchOption.TopDirectoryOnly).Select(x => new FileInfo(x)));
|
||||
|
||||
Backup.CreatePermanentBackup(_logger, _configDirectory, list, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collect all relevant files for plugin configuration.
|
||||
/// </summary>
|
||||
private static IReadOnlyList<FileInfo> PluginFiles(FilenameService fileNames)
|
||||
{
|
||||
var list = new List<FileInfo>(16)
|
||||
{
|
||||
new(fileNames.ConfigFile),
|
||||
new(fileNames.ProfileFileSystem),
|
||||
new(fileNames.TemplateFileSystem)
|
||||
};
|
||||
|
||||
list.AddRange(fileNames.Profiles());
|
||||
list.AddRange(fileNames.Templates());
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
182
CustomizePlus/Core/Services/CommandService.cs
Normal file
182
CustomizePlus/Core/Services/CommandService.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Plugin.Services;
|
||||
using OtterGui.Classes;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OtterGui.Log;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using CustomizePlus.Profiles;
|
||||
using CustomizePlus.Game.Services;
|
||||
using CustomizePlus.UI.Windows.MainWindow.Tabs.Templates;
|
||||
using CustomizePlus.UI.Windows.MainWindow;
|
||||
|
||||
namespace CustomizePlus.Core.Services;
|
||||
|
||||
public class CommandService : IDisposable
|
||||
{
|
||||
private readonly ProfileManager _profileManager;
|
||||
private readonly GameObjectService _gameObjectService;
|
||||
private readonly ICommandManager _commandManager;
|
||||
private readonly Logger _logger;
|
||||
private readonly ChatService _chatService;
|
||||
private readonly MainWindow _mainWindow;
|
||||
private readonly BoneEditorPanel _boneEditorPanel;
|
||||
private readonly MessageService _messageService;
|
||||
|
||||
private static readonly string[] Commands = new[] { "/customize", "/c+" };
|
||||
|
||||
public CommandService(
|
||||
ProfileManager profileManager,
|
||||
GameObjectService gameObjectService,
|
||||
ICommandManager commandManager,
|
||||
MainWindow mainWindow,
|
||||
ChatService chatService,
|
||||
BoneEditorPanel boneEditorPanel,
|
||||
Logger logger,
|
||||
MessageService messageService)
|
||||
{
|
||||
_profileManager = profileManager;
|
||||
_gameObjectService = gameObjectService;
|
||||
_commandManager = commandManager;
|
||||
_logger = logger;
|
||||
_chatService = chatService;
|
||||
_mainWindow = mainWindow;
|
||||
_boneEditorPanel = boneEditorPanel;
|
||||
_messageService = messageService;
|
||||
|
||||
foreach (var command in Commands)
|
||||
{
|
||||
_commandManager.AddHandler(command, new CommandInfo(OnMainCommand) { HelpMessage = "Toggles main plugin window if no commands passed. Use \"/customize help\" for list of available commands." });
|
||||
}
|
||||
|
||||
chatService.PrintInChat($"Started!"); //safe to assume at this point we have successfully initialized
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var command in Commands)
|
||||
{
|
||||
_commandManager.RemoveHandler(command);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMainCommand(string command, string arguments)
|
||||
{
|
||||
if (_boneEditorPanel.IsEditorActive)
|
||||
{
|
||||
_messageService.NotificationMessage("Customize+ commands cannot be used when editor is active", NotificationType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var argumentList = arguments.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
var argument = argumentList.Length == 2 ? argumentList[1] : string.Empty;
|
||||
|
||||
if (arguments.Length > 0)
|
||||
{
|
||||
switch (argumentList[0].ToLowerInvariant())
|
||||
{
|
||||
case "apply":
|
||||
Apply(argument);
|
||||
return;
|
||||
case "toggle":
|
||||
Apply(argument, true);
|
||||
return;
|
||||
default:
|
||||
case "help":
|
||||
PrintHelp(argumentList[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_mainWindow.Toggle();
|
||||
}
|
||||
|
||||
private bool PrintHelp(string argument)
|
||||
{
|
||||
if (!string.Equals(argument, "help", StringComparison.OrdinalIgnoreCase) && argument != "?")
|
||||
_chatService.PrintInChat(new SeStringBuilder().AddText("The given argument ").AddRed(argument, true)
|
||||
.AddText(" is not valid. Valid arguments are:").BuiltString);
|
||||
else
|
||||
_chatService.PrintInChat(new SeStringBuilder().AddText("Valid arguments for /customize are:").BuiltString);
|
||||
|
||||
_chatService.PrintInChat(new SeStringBuilder().AddCommand("apply", "Applies a given profile for a given character. Use without arguments for help.")
|
||||
.BuiltString);
|
||||
_chatService.PrintInChat(new SeStringBuilder().AddCommand("toggle", "Toggles a given profile for a given character. Use without arguments for help.")
|
||||
.BuiltString);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Apply(string argument, bool toggle = false)
|
||||
{
|
||||
var argumentList = argument.Split(',', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
if (argumentList.Length != 2)
|
||||
{
|
||||
_chatService.PrintInChat(new SeStringBuilder().AddText($"Usage: /customize {(toggle ? "toggle" : "apply")} ").AddBlue("Character Name", true)
|
||||
.AddText(",")
|
||||
.AddRed("Profile Name", true)
|
||||
.BuiltString);
|
||||
_chatService.PrintInChat(new SeStringBuilder().AddText(" 》 ")
|
||||
.AddBlue("Character Name", true).AddText("can be either full character name or one of the following:").BuiltString);
|
||||
_chatService.PrintInChat(new SeStringBuilder().AddText(" 》 To apply to yourself: ").AddBlue("<me>").AddText(", ").AddBlue("self").BuiltString);
|
||||
_chatService.PrintInChat(new SeStringBuilder().AddText(" 》 To apply to target: ").AddBlue("<t>").AddText(", ").AddBlue("target").BuiltString);
|
||||
return;
|
||||
}
|
||||
|
||||
string charaName = "", profName = "";
|
||||
|
||||
try
|
||||
{
|
||||
(charaName, profName) = argumentList switch { var a => (a[0].Trim(), a[1].Trim()) };
|
||||
|
||||
charaName = charaName switch
|
||||
{
|
||||
"<me>" => _gameObjectService.GetCurrentPlayerName() ?? string.Empty,
|
||||
"self" => _gameObjectService.GetCurrentPlayerName() ?? string.Empty,
|
||||
"<t>" => _gameObjectService.GetCurrentPlayerTargetName() ?? string.Empty,
|
||||
"target" => _gameObjectService.GetCurrentPlayerTargetName() ?? string.Empty,
|
||||
_ => charaName,
|
||||
};
|
||||
|
||||
if (!_profileManager.Profiles.Any())
|
||||
{
|
||||
_chatService.PrintInChat(
|
||||
$"Can't {(toggle ? "toggle" : "apply")} profile \"{profName}\" for character \"{charaName}\" because no profiles exist", ChatService.ChatMessageColor.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_profileManager.Profiles.Count(x => x.Name == profName && x.CharacterName == charaName) > 1)
|
||||
{
|
||||
_logger.Warning(
|
||||
$"Found more than one profile matching profile \"{profName}\" and character \"{charaName}\". Using first match.");
|
||||
}
|
||||
|
||||
var outProf = _profileManager.Profiles.FirstOrDefault(x => x.Name == profName && x.CharacterName == charaName);
|
||||
|
||||
if (outProf == null)
|
||||
{
|
||||
_chatService.PrintInChat(
|
||||
$"Can't {(toggle ? "toggle" : "apply")} profile \"{(string.IsNullOrWhiteSpace(profName) ? "empty (none provided)" : profName)}\" " +
|
||||
$"for Character \"{(string.IsNullOrWhiteSpace(charaName) ? "empty (none provided)" : charaName)}\"\n" +
|
||||
"Check if the profile and character names were provided correctly and said profile exists for chosen character", ChatService.ChatMessageColor.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!toggle)
|
||||
_profileManager.SetEnabled(outProf, true);
|
||||
else
|
||||
_profileManager.SetEnabled(outProf, !outProf.Enabled);
|
||||
|
||||
_chatService.PrintInChat(
|
||||
$"{outProf.Name} was successfully {(toggle ? "toggled" : "applied")} for {outProf.CharacterName}", ChatService.ChatMessageColor.Info);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_chatService.PrintInChat($"Error while {(toggle ? "toggling" : "applying")} profile, details are available in dalamud log", ChatService.ChatMessageColor.Error);
|
||||
_logger.Error($"Error {(toggle ? "toggling" : "applying")} profile by command: \n" +
|
||||
$"Profile name \"{(string.IsNullOrWhiteSpace(profName) ? "empty (none provided)" : profName)}\"\n" +
|
||||
$"Character name \"{(string.IsNullOrWhiteSpace(charaName) ? "empty (none provided)" : charaName)}\"\n" +
|
||||
$"Error: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
93
CustomizePlus/Core/Services/DalamudServices.cs
Normal file
93
CustomizePlus/Core/Services/DalamudServices.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace CustomizePlus.Core.Services;
|
||||
|
||||
public class DalamudServices
|
||||
{
|
||||
[PluginService]
|
||||
[RequiredVersion("1.0")]
|
||||
public DalamudPluginInterface PluginInterface { get; private set; } = null!;
|
||||
|
||||
[PluginService]
|
||||
[RequiredVersion("1.0")]
|
||||
public ISigScanner SigScanner { get; private set; } = null!;
|
||||
|
||||
[PluginService]
|
||||
public IFramework Framework { get; private set; } = null!;
|
||||
|
||||
[PluginService]
|
||||
[RequiredVersion("1.0")]
|
||||
public IObjectTable ObjectTable { get; private set; } = null!;
|
||||
|
||||
[PluginService]
|
||||
[RequiredVersion("1.0")]
|
||||
public ICommandManager CommandManager { get; private set; } = null!;
|
||||
|
||||
[PluginService]
|
||||
[RequiredVersion("1.0")]
|
||||
public IChatGui ChatGui { get; private set; } = null!;
|
||||
|
||||
[PluginService]
|
||||
[RequiredVersion("1.0")]
|
||||
public IClientState ClientState { get; private set; } = null!;
|
||||
|
||||
[PluginService]
|
||||
[RequiredVersion("1.0")]
|
||||
public IGameGui GameGui { get; private set; } = null!;
|
||||
|
||||
[PluginService]
|
||||
[RequiredVersion("1.0")]
|
||||
internal IGameInteropProvider Hooker { get; private set; } = null!;
|
||||
|
||||
[PluginService]
|
||||
[RequiredVersion("1.0")]
|
||||
public IKeyState KeyState { get; private set; } = null!;
|
||||
|
||||
//GameData
|
||||
[PluginService]
|
||||
[RequiredVersion("1.0")]
|
||||
public IDataManager DataManager { get; private set; } = null!;
|
||||
|
||||
[PluginService]
|
||||
[RequiredVersion("1.0")]
|
||||
public IPluginLog PluginLog { get; private set; } = null!;
|
||||
|
||||
/*[PluginService]
|
||||
[RequiredVersion("1.0")]
|
||||
public ICondition Condition { get; private set; } = null!;*/
|
||||
|
||||
[PluginService]
|
||||
[RequiredVersion("1.0")]
|
||||
public ITargetManager TargetManager { get; private set; } = null!;
|
||||
|
||||
public DalamudServices(DalamudPluginInterface pluginInterface)
|
||||
{
|
||||
pluginInterface.Inject(this);
|
||||
}
|
||||
|
||||
public void AddServices(IServiceCollection services)
|
||||
{
|
||||
services
|
||||
.AddSingleton(PluginInterface)
|
||||
.AddSingleton(SigScanner)
|
||||
.AddSingleton(Framework)
|
||||
.AddSingleton(ObjectTable)
|
||||
.AddSingleton(CommandManager)
|
||||
.AddSingleton(ChatGui)
|
||||
.AddSingleton(ClientState)
|
||||
.AddSingleton(GameGui)
|
||||
.AddSingleton(Hooker)
|
||||
.AddSingleton(KeyState)
|
||||
.AddSingleton(this)
|
||||
.AddSingleton(PluginInterface.UiBuilder)
|
||||
.AddSingleton(DataManager)
|
||||
.AddSingleton(PluginLog)
|
||||
//.AddSingleton(Condition)
|
||||
.AddSingleton(TargetManager);
|
||||
}
|
||||
}
|
||||
77
CustomizePlus/Core/Services/FantasiaPlusDetectService.cs
Normal file
77
CustomizePlus/Core/Services/FantasiaPlusDetectService.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using CustomizePlus.UI.Windows;
|
||||
using Dalamud.Plugin;
|
||||
using OtterGui.Log;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Timers;
|
||||
|
||||
namespace CustomizePlus.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Detects is Fantasia+ is installed and shows a message if it is. The check is performed every 15 seconds.
|
||||
/// </summary>
|
||||
public class FantasiaPlusDetectService : IDisposable
|
||||
{
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly PopupSystem _popupSystem;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private Timer? _checkTimer = null;
|
||||
|
||||
/// <summary>
|
||||
/// Note: if this is set to true then this is locked until the plugin or game is restarted
|
||||
/// </summary>
|
||||
public bool IsFantasiaPlusInstalled { get; private set; }
|
||||
|
||||
public FantasiaPlusDetectService(DalamudPluginInterface pluginInterface, PopupSystem popupSystem, Logger logger)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_popupSystem = popupSystem;
|
||||
_logger = logger;
|
||||
|
||||
_popupSystem.RegisterPopup("fantasia_detected_warn", "Customize+ detected that you have Fantasia+ installed.\nPlease delete or turn it off and restart your game to use Customize+.");
|
||||
|
||||
if (CheckFantasiaPlusPresence())
|
||||
{
|
||||
_popupSystem.ShowPopup("fantasia_detected_warn");
|
||||
_logger.Error("Fantasia+ detected during startup, plugin will be locked");
|
||||
}
|
||||
else
|
||||
{
|
||||
_checkTimer = new Timer(15 * 1000);
|
||||
_checkTimer.Elapsed += CheckTimerOnElapsed;
|
||||
_checkTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if Fantasia+ is installed and loaded
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool CheckFantasiaPlusPresence()
|
||||
{
|
||||
if (IsFantasiaPlusInstalled)
|
||||
return true;
|
||||
|
||||
IsFantasiaPlusInstalled = _pluginInterface.InstalledPlugins.Any(pluginInfo => pluginInfo is { InternalName: "FantasiaPlus", IsLoaded: true });
|
||||
|
||||
return IsFantasiaPlusInstalled;
|
||||
}
|
||||
|
||||
private void CheckTimerOnElapsed(object? sender, ElapsedEventArgs e)
|
||||
{
|
||||
if (CheckFantasiaPlusPresence())
|
||||
{
|
||||
_popupSystem.ShowPopup("fantasia_detected_warn");
|
||||
_checkTimer!.Stop();
|
||||
_checkTimer?.Dispose();
|
||||
_logger.Error("Fantasia+ detected by timer, plugin will be locked");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_checkTimer != null)
|
||||
_checkTimer.Dispose();
|
||||
}
|
||||
}
|
||||
57
CustomizePlus/Core/Services/FilenameService.cs
Normal file
57
CustomizePlus/Core/Services/FilenameService.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using Dalamud.Plugin;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using CustomizePlus.Profiles.Data;
|
||||
|
||||
namespace CustomizePlus.Core.Services;
|
||||
|
||||
public class FilenameService
|
||||
{
|
||||
public readonly string ConfigDirectory;
|
||||
public readonly string ConfigFile;
|
||||
public readonly string ProfileDirectory;
|
||||
public readonly string ProfileFileSystem;
|
||||
public readonly string TemplateDirectory;
|
||||
public readonly string TemplateFileSystem;
|
||||
|
||||
public FilenameService(DalamudPluginInterface pi)
|
||||
{
|
||||
ConfigDirectory = pi.ConfigDirectory.FullName;
|
||||
ConfigFile = pi.ConfigFile.FullName;
|
||||
ProfileDirectory = Path.Combine(ConfigDirectory, "profiles");
|
||||
ProfileFileSystem = Path.Combine(ConfigDirectory, "profile_sort_order.json");
|
||||
TemplateDirectory = Path.Combine(ConfigDirectory, "templates");
|
||||
TemplateFileSystem = Path.Combine(ConfigDirectory, "template_sort_order.json");
|
||||
}
|
||||
|
||||
public IEnumerable<FileInfo> Templates()
|
||||
{
|
||||
if (!Directory.Exists(TemplateDirectory))
|
||||
yield break;
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(TemplateDirectory, "*.json", SearchOption.TopDirectoryOnly))
|
||||
yield return new FileInfo(file);
|
||||
}
|
||||
|
||||
public string TemplateFile(Guid id)
|
||||
=> Path.Combine(TemplateDirectory, $"{id}.json");
|
||||
|
||||
public string TemplateFile(Templates.Data.Template template)
|
||||
=> TemplateFile(template.UniqueId);
|
||||
|
||||
public IEnumerable<FileInfo> Profiles()
|
||||
{
|
||||
if (!Directory.Exists(ProfileDirectory))
|
||||
yield break;
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(ProfileDirectory, "*.json", SearchOption.TopDirectoryOnly))
|
||||
yield return new FileInfo(file);
|
||||
}
|
||||
|
||||
public string ProfileFile(Guid id)
|
||||
=> Path.Combine(ProfileDirectory, $"{id}.json");
|
||||
|
||||
public string ProfileFile(Profile profile)
|
||||
=> ProfileFile(profile.UniqueId);
|
||||
}
|
||||
156
CustomizePlus/Core/Services/HookingService.cs
Normal file
156
CustomizePlus/Core/Services/HookingService.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Plugin.Services;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using OtterGui.Log;
|
||||
using CustomizePlus.Core.Data;
|
||||
using CustomizePlus.Game.Services;
|
||||
using CustomizePlus.Configuration.Data;
|
||||
using CustomizePlus.Profiles;
|
||||
using CustomizePlus.Armatures.Services;
|
||||
using CustomizePlus.GameData.Data;
|
||||
|
||||
namespace CustomizePlus.Core.Services;
|
||||
|
||||
public class HookingService : IDisposable
|
||||
{
|
||||
private readonly PluginConfiguration _configuration;
|
||||
private readonly ISigScanner _sigScanner;
|
||||
private readonly IGameInteropProvider _hooker;
|
||||
private readonly ProfileManager _profileManager;
|
||||
private readonly ArmatureManager _armatureManager;
|
||||
private readonly GameStateService _gameStateService;
|
||||
private readonly FantasiaPlusDetectService _fantasiaPlusDetectService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private Hook<RenderDelegate>? _renderManagerHook;
|
||||
private Hook<GameObjectMovementDelegate>? _gameObjectMovementHook;
|
||||
|
||||
private delegate nint RenderDelegate(nint a1, nint a2, int a3, int a4);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
||||
private delegate void GameObjectMovementDelegate(nint gameObject);
|
||||
|
||||
public HookingService(
|
||||
PluginConfiguration configuration,
|
||||
ISigScanner sigScanner,
|
||||
IGameInteropProvider hooker,
|
||||
ProfileManager profileManager,
|
||||
ArmatureManager armatureManager,
|
||||
GameStateService gameStateService,
|
||||
FantasiaPlusDetectService fantasiaPlusDetectService,
|
||||
Logger logger)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_sigScanner = sigScanner;
|
||||
_hooker = hooker;
|
||||
_profileManager = profileManager;
|
||||
_armatureManager = armatureManager;
|
||||
_gameStateService = gameStateService;
|
||||
_fantasiaPlusDetectService = fantasiaPlusDetectService;
|
||||
_logger = logger;
|
||||
|
||||
ReloadHooks();
|
||||
}
|
||||
|
||||
public void ReloadHooks()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_configuration.PluginEnabled)
|
||||
{
|
||||
if (_renderManagerHook == null)
|
||||
{
|
||||
var renderAddress = _sigScanner.ScanText(Constants.RenderHookAddress);
|
||||
_renderManagerHook = _hooker.HookFromAddress<RenderDelegate>(renderAddress, OnRender);
|
||||
_logger.Debug("Render hook established");
|
||||
}
|
||||
|
||||
if (_gameObjectMovementHook == null)
|
||||
{
|
||||
var movementAddress = _sigScanner.ScanText(Constants.MovementHookAddress);
|
||||
_gameObjectMovementHook = _hooker.HookFromAddress<GameObjectMovementDelegate>(movementAddress, OnGameObjectMove);
|
||||
_logger.Debug("Movement hook established");
|
||||
}
|
||||
|
||||
_logger.Debug("Hooking render & movement functions");
|
||||
_renderManagerHook.Enable();
|
||||
_gameObjectMovementHook.Enable();
|
||||
|
||||
_logger.Debug("Hooking render manager");
|
||||
_renderManagerHook.Enable();
|
||||
|
||||
//Send current player's profile update message to IPC
|
||||
//IPCManager.OnProfileUpdate(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug("Unhooking...");
|
||||
_renderManagerHook?.Disable();
|
||||
_gameObjectMovementHook?.Disable();
|
||||
_renderManagerHook?.Disable();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error($"Failed to hook Render::Manager::Render {e}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private nint OnRender(nint a1, nint a2, int a3, int a4)
|
||||
{
|
||||
if (_renderManagerHook == null)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
if (_fantasiaPlusDetectService.IsFantasiaPlusInstalled)
|
||||
{
|
||||
_logger.Error($"Fantasia+ detected, disabling all hooks");
|
||||
_renderManagerHook?.Disable();
|
||||
_gameObjectMovementHook.Disable();
|
||||
|
||||
return _renderManagerHook.Original(a1, a2, a3, a4);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_armatureManager.OnRender();
|
||||
_profileManager.OnRender();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error($"Error in Customize+ render hook {e}");
|
||||
_renderManagerHook?.Disable();
|
||||
}
|
||||
|
||||
return _renderManagerHook.Original(a1, a2, a3, a4);
|
||||
}
|
||||
|
||||
private unsafe void OnGameObjectMove(nint gameObjectPtr)
|
||||
{
|
||||
// Call the original function.
|
||||
_gameObjectMovementHook.Original(gameObjectPtr);
|
||||
|
||||
// If GPose and a 3rd-party posing service are active simultneously, abort
|
||||
if (_gameStateService.GameInPosingMode())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var actor = (Actor)gameObjectPtr;
|
||||
if (actor.Valid)
|
||||
_armatureManager.OnGameObjectMove((Actor)gameObjectPtr);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_gameObjectMovementHook?.Disable();
|
||||
_gameObjectMovementHook?.Dispose();
|
||||
|
||||
_renderManagerHook?.Disable();
|
||||
_renderManagerHook?.Dispose();
|
||||
}
|
||||
}
|
||||
17
CustomizePlus/Core/Services/SaveService.cs
Normal file
17
CustomizePlus/Core/Services/SaveService.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Log;
|
||||
|
||||
namespace CustomizePlus.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Any file type that we want to save via SaveService.
|
||||
/// </summary>
|
||||
public interface ISavable : ISavable<FilenameService>
|
||||
{ }
|
||||
|
||||
public sealed class SaveService : SaveServiceBase<FilenameService>
|
||||
{
|
||||
public SaveService(Logger logger, FrameworkManager framework, FilenameService fileNames)
|
||||
: base(logger, framework, fileNames)
|
||||
{ }
|
||||
}
|
||||
Reference in New Issue
Block a user