diff --git a/PrefPro.sln b/PrefPro.sln index d35122c..c40c60c 100644 --- a/PrefPro.sln +++ b/PrefPro.sln @@ -7,18 +7,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrefPro", "PrefPro\PrefPro. EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU + Debug|Any CPU = Debug|x64 + Release|Any CPU = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|Any CPU.Build.0 = Release|Any CPU - {4FEC9558-EB25-419F-B86E-51B8CFDA32B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4FEC9558-EB25-419F-B86E-51B8CFDA32B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4FEC9558-EB25-419F-B86E-51B8CFDA32B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4FEC9558-EB25-419F-B86E-51B8CFDA32B7}.Release|Any CPU.Build.0 = Release|Any CPU + {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|Any CPU.ActiveCfg = Debug|x64 + {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|Any CPU.Build.0 = Debug|x64 + {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|Any CPU.ActiveCfg = Release|x64 + {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|Any CPU.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PrefPro/ActuallyRawPayload.cs b/PrefPro/ActuallyRawPayload.cs deleted file mode 100644 index 9035917..0000000 --- a/PrefPro/ActuallyRawPayload.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.IO; -using Dalamud.Game.Text.SeStringHandling; - -namespace PrefPro -{ - public class ActuallyRawPayload : Payload - { - public override PayloadType Type => PayloadType.Unknown; - private byte[] data; - - public ActuallyRawPayload(byte[] bytes) - { - data = bytes; - } - - protected override byte[] EncodeImpl() - { - return data; - } - - protected override void DecodeImpl(BinaryReader reader, long endOfStream) - { - throw new NotSupportedException("Decoding an ActuallyRawPayload is not supported."); - } - } -} \ No newline at end of file diff --git a/PrefPro/CodeUtil.cs b/PrefPro/CodeUtil.cs deleted file mode 100644 index b7d18c4..0000000 --- a/PrefPro/CodeUtil.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using Iced.Intel; - -namespace PrefPro; - -public class CodeUtil -{ - public static bool TryGetStaticAddressFromPtr(nint instructionAddressNint, out IntPtr result) - { - try - { - result = GetStaticAddressFromPtr(instructionAddressNint); - return true; - } - catch (KeyNotFoundException) - { - result = IntPtr.Zero; - return false; - } - } - - public static unsafe IntPtr GetStaticAddressFromPtr(nint instructionAddressNint) - { - var instructionAddress = (byte*)instructionAddressNint; - try - { - var reader = new UnsafeCodeReader(instructionAddress, 64); - var decoder = Decoder.Create(64, reader, (ulong)instructionAddress, DecoderOptions.AMD); - while (reader.CanReadByte) - { - var instruction = decoder.Decode(); - if (instruction.IsInvalid) continue; - if (instruction.Op0Kind is OpKind.Memory || instruction.Op1Kind is OpKind.Memory) - { - return (IntPtr)instruction.MemoryDisplacement64; - } - } - } - catch - { - // ignored - } - - throw new KeyNotFoundException($"Can't find any referenced address at the given pointer."); - } - - private unsafe class UnsafeCodeReader : CodeReader - { - private readonly int length; - private readonly byte* address; - private int pos; - - public UnsafeCodeReader(byte* address, int length) - { - this.length = length; - this.address = address; - } - - public bool CanReadByte => this.pos < this.length; - - public override int ReadByte() - { - if (this.pos >= this.length) return -1; - return *(this.address + this.pos++); - } - } -} \ No newline at end of file diff --git a/PrefPro/Configuration.cs b/PrefPro/Configuration.cs index 9458a00..fac3c5e 100644 --- a/PrefPro/Configuration.cs +++ b/PrefPro/Configuration.cs @@ -1,167 +1,160 @@ using Dalamud.Configuration; -using System; -using System.Collections.Generic; -using Dalamud.Game.ClientState.Objects.Enums; using Newtonsoft.Json; using PrefPro.Settings; +using System; +using System.Collections.Generic; + +namespace PrefPro; -namespace PrefPro +[Serializable] +public class Configuration : IPluginConfiguration { - [Serializable] - public class Configuration : IPluginConfiguration + public int Version { get; set; } = 1; + + public struct ConfigHolder { - public int Version { get; set; } = 1; - - public struct ConfigHolder - { - public bool Enabled; - public string Name; - public NameSetting FullName; - public NameSetting FirstName; - public NameSetting LastName; - public GenderSetting Gender; - public RaceSetting Race; - public TribeSetting Tribe; - } + public bool Enabled; + public string Name; + public NameSetting FullName; + public NameSetting FirstName; + public NameSetting LastName; + public GenderSetting Gender; + public RaceSetting Race; + public TribeSetting Tribe; + } - public Dictionary Configs { get; set; } = new Dictionary(); + public Dictionary Configs { get; set; } = []; - [JsonIgnore] - public bool Enabled - { - get => GetOrDefault().Enabled; - set - { - var config = GetOrDefault(); - config.Enabled = value; - Configs[_prefPro.CurrentPlayerContentId] = config; - } - } - [JsonIgnore] - public string Name + [JsonIgnore] + public bool Enabled + { + get => GetOrDefault().Enabled; + set { - get => GetOrDefault().Name; - set - { - var config = GetOrDefault(); - config.Name = value; - Configs[_prefPro.CurrentPlayerContentId] = config; - } + var config = GetOrDefault(); + config.Enabled = value; + Configs[PlayerApi.ContentId] = config; } - [JsonIgnore] - public NameSetting FullName + } + [JsonIgnore] + public string Name + { + get => GetOrDefault().Name; + set { - get => GetOrDefault().FullName; - set - { - var config = GetOrDefault(); - config.FullName = value; - Configs[_prefPro.CurrentPlayerContentId] = config; - } + var config = GetOrDefault(); + config.Name = value; + Configs[PlayerApi.ContentId] = config; } - [JsonIgnore] - public NameSetting FirstName + } + [JsonIgnore] + public NameSetting FullName + { + get => GetOrDefault().FullName; + set { - get => GetOrDefault().FirstName; - set - { - var config = GetOrDefault(); - config.FirstName = value; - Configs[_prefPro.CurrentPlayerContentId] = config; - } + var config = GetOrDefault(); + config.FullName = value; + Configs[PlayerApi.ContentId] = config; } - [JsonIgnore] - public NameSetting LastName + } + [JsonIgnore] + public NameSetting FirstName + { + get => GetOrDefault().FirstName; + set { - get => GetOrDefault().LastName; - set - { - var config = GetOrDefault(); - config.LastName = value; - Configs[_prefPro.CurrentPlayerContentId] = config; - } + var config = GetOrDefault(); + config.FirstName = value; + Configs[PlayerApi.ContentId] = config; } - [JsonIgnore] - public GenderSetting Gender + } + [JsonIgnore] + public NameSetting LastName + { + get => GetOrDefault().LastName; + set { - get => GetOrDefault().Gender; - set - { - var config = GetOrDefault(); - config.Gender = value; - Configs[_prefPro.CurrentPlayerContentId] = config; - } + var config = GetOrDefault(); + config.LastName = value; + Configs[PlayerApi.ContentId] = config; } - [JsonIgnore] - public RaceSetting Race + } + [JsonIgnore] + public GenderSetting Gender + { + get => GetOrDefault().Gender; + set { - get => GetOrDefault().Race; - set - { - var config = GetOrDefault(); - config.Race = value; - Configs[_prefPro.CurrentPlayerContentId] = config; - } + var config = GetOrDefault(); + config.Gender = value; + Configs[PlayerApi.ContentId] = config; } - [JsonIgnore] - public TribeSetting Tribe + } + [JsonIgnore] + public RaceSetting Race + { + get => GetOrDefault().Race; + set { - get => GetOrDefault().Tribe; - set - { - var config = GetOrDefault(); - config.Tribe = value; - Configs[_prefPro.CurrentPlayerContentId] = config; - } + var config = GetOrDefault(); + config.Race = value; + Configs[PlayerApi.ContentId] = config; } - - public int GetGender() + } + [JsonIgnore] + public TribeSetting Tribe + { + get => GetOrDefault().Tribe; + set { - switch (Gender) - { - case GenderSetting.Male: - return 0; - case GenderSetting.Female: - return 1; - case GenderSetting.Random: - var ret = new Random().Next(0, 2); - return ret; - case GenderSetting.Model: - return DalamudApi.ClientState.LocalPlayer?.Customize[(int)CustomizeIndex.Gender] ?? 0; - } - return 0; + var config = GetOrDefault(); + config.Tribe = value; + Configs[PlayerApi.ContentId] = config; } - - [NonSerialized] private PrefPro _prefPro; + } + + public int GetGender() + { + return Gender switch + { + GenderSetting.Male => 0, + GenderSetting.Female => 1, + GenderSetting.Random => new Random().Next(0, 2), + GenderSetting.Model => PlayerApi.Sex, + _ => 0, + }; + } + + [NonSerialized] private PrefPro _prefPro = null!; - public void Initialize(PrefPro prefPro) - { - _prefPro = prefPro; - } + public void Initialize(PrefPro prefPro) + { + _prefPro = prefPro; + } - public ConfigHolder GetOrDefault() + public ConfigHolder GetOrDefault() + { + bool result = Configs.TryGetValue(PlayerApi.ContentId, out var holder); + if (!result) { - bool result = Configs.TryGetValue(_prefPro.CurrentPlayerContentId, out var holder); - if (!result) + var ch = new ConfigHolder { - var ch = new ConfigHolder - { - Name = _prefPro.PlayerName, - FullName = NameSetting.FirstLast, - FirstName = NameSetting.FirstOnly, - LastName = NameSetting.LastOnly, - Gender = GenderSetting.Model, - Enabled = false - }; - return ch; - } - return holder; + Name = PlayerApi.CharacterName, + FullName = NameSetting.FirstLast, + FirstName = NameSetting.FirstOnly, + LastName = NameSetting.LastOnly, + Gender = GenderSetting.Model, + Enabled = false + }; + return ch; } + return holder; + } - public void Save() - { - DalamudApi.PluginInterface.SavePluginConfig(this); - _prefPro.NameHandlerCache.Refresh(); - } + public void Save() + { + DalamudApi.PluginInterface.SavePluginConfig(this); + _prefPro.NameHandlerCache.Refresh(); } } diff --git a/PrefPro/DalamudApi.cs b/PrefPro/DalamudApi.cs index 9c065d5..b1d020f 100644 --- a/PrefPro/DalamudApi.cs +++ b/PrefPro/DalamudApi.cs @@ -1,6 +1,4 @@ -using Dalamud.Game; -using Dalamud.Game.ClientState.Objects; -using Dalamud.IoC; +using Dalamud.IoC; using Dalamud.Plugin; using Dalamud.Plugin.Services; @@ -10,31 +8,10 @@ public class DalamudApi { public static void Initialize(IDalamudPluginInterface pluginInterface) => pluginInterface.Create(); - // [PluginService] public static IAetheryteList AetheryteList { get; private set; } = null; - // [PluginService] public static IBuddyList BuddyList { get; private set; } = null; - // [PluginService] public static IChatGui ChatGui { get; private set; } = null; - [PluginService] public static IClientState ClientState { get; private set; } = null; - [PluginService] public static ICommandManager CommandManager { get; private set; } = null; - // [PluginService] public static ICondition Condition { get; private set; } = null; - [PluginService] public static IDalamudPluginInterface PluginInterface { get; private set; } = null; - // [PluginService] public static IDataManager DataManager { get; private set; } = null; - // [PluginService] public static IDtrBar DtrBar { get; private set; } = null; - // [PluginService] public static IFateTable FateTable { get; private set; } = null; - // [PluginService] public static IFlyTextGui FlyTextGui { get; private set; } = null; - [PluginService] public static IFramework Framework { get; private set; } = null; - // [PluginService] public static IGameGui GameGui { get; private set; } = null; - // [PluginService] public static IGameNetwork GameNetwork { get; private set; } = null; - // [PluginService] public static IGamepadState GamePadState { get; private set; } = null; - // [PluginService] public static IJobGauges JobGauges { get; private set; } = null; - // [PluginService] public static IKeyState KeyState { get; private set; } = null; - // [PluginService] public static ILibcFunction LibcFunction { get; private set; } = null; - // [PluginService] public static IObjectTable ObjectTable { get; private set; } = null; - // [PluginService] public static IPartyFinderGui PartyFinderGui { get; private set; } = null; - // [PluginService] public static IPartyList PartyList { get; private set; } = null; - [PluginService] public static ISigScanner SigScanner { get; private set; } = null; - // [PluginService] public static ITargetManager TargetManager { get; private set; } = null; - // [PluginService] public static IToastGui ToastGui { get; private set; } = null; - [PluginService] public static IGameInteropProvider Hooks { get; private set; } = null; - [PluginService] public static IPluginLog PluginLog { get; private set; } = null; - // [PluginService] public static ITitleScreenMenu TitleScreenMenu { get; private set; } = null; + [PluginService] public static IClientState ClientState { get; private set; } = null!; + [PluginService] public static ICommandManager CommandManager { get; private set; } = null!; + [PluginService] public static IDalamudPluginInterface PluginInterface { get; private set; } = null!; + [PluginService] public static IGameInteropProvider Hooks { get; private set; } = null!; + [PluginService] public static IGameConfig GameConfig { get; private set; } = null!; + [PluginService] public static IPluginLog PluginLog { get; private set; } = null!; } \ No newline at end of file diff --git a/PrefPro/LuaHandler.cs b/PrefPro/LuaHandler.cs index c67e9ff..5a61e9b 100644 --- a/PrefPro/LuaHandler.cs +++ b/PrefPro/LuaHandler.cs @@ -1,142 +1,132 @@ -using System; -using System.Globalization; -using Dalamud.Hooking; +using Dalamud.Hooking; using FFXIVClientStructs.FFXIV.Client.System.Framework; +using System; namespace PrefPro; public unsafe class LuaHandler : IDisposable -{ - public delegate nuint LuaFunction(nuint a1); - - private Hook _getRace; - private Hook _getSex; - private Hook _getTribe; - - private byte* _luaRacePtr; - private byte* _luaSexPtr; - private byte* _luaTribePtr; - - private bool _initialized = false; - - private readonly Configuration _configuration; - - public LuaHandler(Configuration configuration) - { - _configuration = configuration; - - try - { - Initialize(); - } - catch (Exception e) - { - DalamudApi.PluginLog.Error(e, "PrefPro Lua Handler initialization failed."); - } - } - - private void Initialize() - { - if (_initialized) return; - - var raceFunctionAddress = GetAddress("return Pc.GetRace"); - var sexFunctionAddress = GetAddress("return Pc.GetSex"); - var tribeFunctionAddress = GetAddress("return Pc.GetTribe"); - - _getRace = DalamudApi.Hooks.HookFromAddress(raceFunctionAddress, RaceFunctionDetour); - _getSex = DalamudApi.Hooks.HookFromAddress(sexFunctionAddress, SexFunctionDetour); - _getTribe = DalamudApi.Hooks.HookFromAddress(tribeFunctionAddress, TribeFunctionDetour); - - _luaRacePtr = (byte*)CodeUtil.GetStaticAddressFromPtr(raceFunctionAddress + 0x32); - _luaSexPtr = (byte*)CodeUtil.GetStaticAddressFromPtr(sexFunctionAddress + 0x32); - _luaTribePtr = (byte*)CodeUtil.GetStaticAddressFromPtr(tribeFunctionAddress + 0x32); - - DalamudApi.PluginLog.Debug($"[LuaHandler] Race function address: {raceFunctionAddress:X}"); - DalamudApi.PluginLog.Debug($"[LuaHandler] Sex function address: {sexFunctionAddress:X}"); - DalamudApi.PluginLog.Debug($"[LuaHandler] Tribe function address: {tribeFunctionAddress:X}"); - - DalamudApi.PluginLog.Debug($"[LuaHandler] Race data address: {(nint) _luaRacePtr:X}"); - DalamudApi.PluginLog.Debug($"[LuaHandler] Sex data address: {(nint) _luaSexPtr:X}"); - DalamudApi.PluginLog.Debug($"[LuaHandler] Tribe data address: {(nint) _luaTribePtr:X}"); - - _getRace.Enable(); - _getSex.Enable(); - _getTribe.Enable(); - - _initialized = true; - } - - public void Dispose() - { - _getRace?.Dispose(); - _getSex?.Dispose(); - _getTribe?.Dispose(); - } - - private nint GetAddress(string code) { - var l = Framework.Instance()->LuaState.State; - l->luaL_loadbuffer(code, code.Length, "test_chunk"); - if (l->lua_pcall(0, 1, 0) != 0) - throw new Exception(l->lua_tostring(-1)); - var luaFunc = *(nint*)l->index2adr(-1); - l->lua_pop(1); - return *(nint*)(luaFunc + 0x20); - } - - private nuint RaceFunctionDetour(nuint a1) - { - try - { - Initialize(); - var oldRace = *_luaRacePtr; - *_luaRacePtr = (byte)_configuration.Race; - DalamudApi.PluginLog.Debug($"[RaceFunctionDetour] oldRace: {oldRace} race: {(byte)_configuration.Race}"); - var ret = _getRace.Original(a1); - *_luaRacePtr = oldRace; - return ret; - } - catch (Exception e) - { - DalamudApi.PluginLog.Error(e, "PrefPro Exception"); - return _getRace.Original(a1); - } - } - - private nuint SexFunctionDetour(nuint a1) - { - try - { - Initialize(); - var oldSex = *_luaSexPtr; - *_luaSexPtr = (byte)_configuration.GetGender(); - DalamudApi.PluginLog.Debug($"[SexFunctionDetour] oldSex: {oldSex} sex: {(byte)_configuration.GetGender()}"); - var ret = _getSex.Original(a1); - *_luaSexPtr = oldSex; - return ret; - } - catch (Exception e) - { - DalamudApi.PluginLog.Error(e, "PrefPro Exception"); - return _getSex.Original(a1); - } - } - - private nuint TribeFunctionDetour(nuint a1) - { - try - { - Initialize(); - var oldTribe = *_luaTribePtr; - *_luaTribePtr = (byte)_configuration.Tribe; - DalamudApi.PluginLog.Debug($"[TribeFunctionDetour] oldTribe: {oldTribe} sex: {(byte)_configuration.Tribe}"); - var ret = _getTribe.Original(a1); - *_luaTribePtr = oldTribe; - return ret; - } - catch (Exception e) - { - DalamudApi.PluginLog.Error(e, "PrefPro Exception"); - return _getTribe.Original(a1); - } - } +{ + public delegate nuint LuaFunction(nuint a1); + + private readonly Configuration _configuration; + + private Hook? _getRace; + private Hook? _getSex; + private Hook? _getTribe; + + private bool _initialized = false; + + public LuaHandler(Configuration configuration) + { + _configuration = configuration; + + try + { + Initialize(); + } + catch (Exception e) + { + DalamudApi.PluginLog.Error(e, "PrefPro Lua Handler initialization failed."); + } + } + + private void Initialize() + { + if (_initialized) return; + + var raceFunctionAddress = GetAddress("return Pc.GetRace"); + var sexFunctionAddress = GetAddress("return Pc.GetSex"); + var tribeFunctionAddress = GetAddress("return Pc.GetTribe"); + + _getRace = DalamudApi.Hooks.HookFromAddress(raceFunctionAddress, RaceFunctionDetour); + _getSex = DalamudApi.Hooks.HookFromAddress(sexFunctionAddress, SexFunctionDetour); + _getTribe = DalamudApi.Hooks.HookFromAddress(tribeFunctionAddress, TribeFunctionDetour); + + DalamudApi.PluginLog.Debug($"[LuaHandler] Race function address: {raceFunctionAddress:X}"); + DalamudApi.PluginLog.Debug($"[LuaHandler] Sex function address: {sexFunctionAddress:X}"); + DalamudApi.PluginLog.Debug($"[LuaHandler] Tribe function address: {tribeFunctionAddress:X}"); + + _getRace.Enable(); + _getSex.Enable(); + _getTribe.Enable(); + + _initialized = true; + } + + public void Dispose() + { + _getRace?.Dispose(); + _getSex?.Dispose(); + _getTribe?.Dispose(); + + GC.SuppressFinalize(this); + } + + private nint GetAddress(string code) + { + var l = Framework.Instance()->LuaState.State; + l->luaL_loadbuffer(code, code.Length, "test_chunk"); + if (l->lua_pcall(0, 1, 0) != 0) + throw new Exception(l->lua_tostring(-1)); + var luaFunc = *(nint*)l->index2adr(-1); + l->lua_pop(1); + return *(nint*)(luaFunc + 0x20); + } + + private nuint RaceFunctionDetour(nuint a1) + { + try + { + Initialize(); + var oldRace = PlayerApi.Race; + PlayerApi.Race = (byte)_configuration.Race; + DalamudApi.PluginLog.Debug($"[RaceFunctionDetour] oldRace: {oldRace} race: {(byte)_configuration.Race}"); + var ret = _getRace!.Original(a1); + PlayerApi.Race = oldRace; + return ret; + } + catch (Exception e) + { + DalamudApi.PluginLog.Error(e, "PrefPro Exception"); + return _getRace!.Original(a1); + } + } + + private nuint SexFunctionDetour(nuint a1) + { + try + { + Initialize(); + var oldSex = PlayerApi.Sex; + PlayerApi.Sex = (byte)_configuration.GetGender(); + DalamudApi.PluginLog.Debug($"[SexFunctionDetour] oldSex: {oldSex} sex: {(byte)_configuration.GetGender()}"); + var ret = _getSex!.Original(a1); + PlayerApi.Sex = oldSex; + return ret; + } + catch (Exception e) + { + DalamudApi.PluginLog.Error(e, "PrefPro Exception"); + return _getSex!.Original(a1); + } + } + + private nuint TribeFunctionDetour(nuint a1) + { + try + { + Initialize(); + var oldTribe = PlayerApi.Tribe; + PlayerApi.Tribe = (byte)_configuration.Tribe; + DalamudApi.PluginLog.Debug($"[TribeFunctionDetour] oldTribe: {oldTribe} tribe: {(byte)_configuration.Tribe}"); + var ret = _getTribe!.Original(a1); + PlayerApi.Tribe = oldTribe; + return ret; + } + catch (Exception e) + { + DalamudApi.PluginLog.Error(e, "PrefPro Exception"); + return _getTribe!.Original(a1); + } + } } \ No newline at end of file diff --git a/PrefPro/NameHandlerCache.cs b/PrefPro/NameHandlerCache.cs index b89cd78..6414419 100644 --- a/PrefPro/NameHandlerCache.cs +++ b/PrefPro/NameHandlerCache.cs @@ -1,51 +1,58 @@ -using Dalamud.Game.Text.SeStringHandling.Payloads; -using Dalamud.Plugin.Services; using PrefPro.Settings; +using System; namespace PrefPro; -public class NameHandlerCache +public class NameHandlerCache : IDisposable { private readonly Configuration _configuration; - private string? _playerName; - public HandlerConfig Config { get; private set; } = HandlerConfig.None; + private HandlerConfig _config = HandlerConfig.None; public NameHandlerCache(Configuration configuration) { _configuration = configuration; + + DalamudApi.ClientState.Login += OnLogin; DalamudApi.ClientState.Logout += OnLogout; - DalamudApi.Framework.Update += FrameworkOnUpdate; } - private void FrameworkOnUpdate(IFramework framework) + public void Dispose() { - if (DalamudApi.ClientState.IsLoggedIn && DalamudApi.ClientState.LocalPlayer is { } localPlayer) { - DalamudApi.Framework.Update -= FrameworkOnUpdate; - _playerName = localPlayer.Name.TextValue; + DalamudApi.ClientState.Login -= OnLogin; + DalamudApi.ClientState.Logout -= OnLogout; + + GC.SuppressFinalize(this); + } + + public HandlerConfig GetConfig() + { + if (_config == HandlerConfig.None) Refresh(); - } + + return _config; + } + + private void OnLogin() + { + Refresh(); } private void OnLogout(int type, int code) { - if (_playerName != null) { - _playerName = null; - Config = HandlerConfig.None; - DalamudApi.Framework.Update += FrameworkOnUpdate; - } + _config = HandlerConfig.None; } public void Refresh() { - if (_playerName != null) { - Config = CreateConfig(_configuration, _playerName); - } + if (DalamudApi.ClientState.IsLoggedIn) + _config = CreateConfig(_configuration); } - private static HandlerConfig CreateConfig(Configuration config, string playerName) + private static HandlerConfig CreateConfig(Configuration config) { var data = new HandlerConfig(); + var playerName = PlayerApi.CharacterName; if (config.Name != playerName) { @@ -68,9 +75,9 @@ private static HandlerConfig CreateConfig(Configuration config, string playerNam { data.Apply = true; - data.NameFull = new TextPayload(GetNameText(playerName, config.Name, config.FullName)); - data.NameFirst = new TextPayload(GetNameText(playerName, config.Name, config.FirstName)); - data.NameLast = new TextPayload(GetNameText(playerName, config.Name, config.LastName)); + data.NameFull = GetNameText(playerName, config.Name, config.FullName); + data.NameFirst = GetNameText(playerName, config.Name, config.FirstName); + data.NameLast = GetNameText(playerName, config.Name, config.LastName); } return data; @@ -78,7 +85,8 @@ private static HandlerConfig CreateConfig(Configuration config, string playerNam private static string GetNameText(string playerName, string configName, NameSetting setting) { - switch (setting) { + switch (setting) + { case NameSetting.FirstLast: return configName; case NameSetting.FirstOnly: @@ -100,9 +108,9 @@ public class HandlerConfig public bool ApplyFull; public bool ApplyFirst; public bool ApplyLast; - public TextPayload NameFull = null!; - public TextPayload NameFirst = null!; - public TextPayload NameLast = null!; + public string NameFull = string.Empty; + public string NameFirst = string.Empty; + public string NameLast = string.Empty; public static readonly HandlerConfig None = new() { diff --git a/PrefPro/PlayerApi.cs b/PrefPro/PlayerApi.cs new file mode 100644 index 0000000..9bddc9e --- /dev/null +++ b/PrefPro/PlayerApi.cs @@ -0,0 +1,12 @@ +using FFXIVClientStructs.FFXIV.Client.Game.UI; + +namespace PrefPro; + +public unsafe static class PlayerApi +{ + public static string CharacterName => PlayerState.Instance()->CharacterNameString; + public static ref ulong ContentId => ref PlayerState.Instance()->ContentId; + public static ref byte Sex => ref PlayerState.Instance()->Sex; + public static ref byte Race => ref PlayerState.Instance()->Race; + public static ref byte Tribe => ref PlayerState.Instance()->Tribe; +} diff --git a/PrefPro/PluginUI.cs b/PrefPro/PluginUI.cs index f6973a4..fe888f0 100644 --- a/PrefPro/PluginUI.cs +++ b/PrefPro/PluginUI.cs @@ -1,338 +1,323 @@ -using ImGuiNET; +using Dalamud.Interface.Utility; +using ImGuiNET; +using PrefPro.Settings; using System; +using System.Collections.Generic; using System.Numerics; using System.Text.RegularExpressions; -using Dalamud.Interface.Utility; -using PrefPro.Settings; - -namespace PrefPro -{ - class PluginUI : IDisposable - { - private readonly Configuration _configuration; - private readonly PrefPro _prefPro; - private bool _settingsVisible; - private const string FirstLastDesc = "First name, then last"; - private const string FirstOnlyDesc = "First name only"; - private const string LastOnlyDesc = "Last name only"; - private const string LastFirstDesc = "Last name, then first"; +namespace PrefPro; - private const string MaleDesc = "Male"; - private const string FemaleDesc = "Female"; - private const string RandomDesc = "Random gender"; - private const string ModelDesc = "Model gender"; +public partial class PluginUI : IDisposable +{ + [GeneratedRegex("[^A-Za-z'\\-\\s{1}]")] + private static partial Regex CleanNameRegex(); + + private readonly Configuration _configuration; - private string _tmpFirstName = ""; - private string _tmpLastName = ""; + private bool _settingsVisible; - public bool SettingsVisible + private string _tmpFirstName = string.Empty; + private string _tmpLastName = string.Empty; + + public PluginUI(Configuration configuration) + { + this._configuration = configuration; + + DalamudApi.PluginInterface.UiBuilder.Draw += Draw; + DalamudApi.PluginInterface.UiBuilder.OpenConfigUi += OpenConfigUI; + } + + public void Dispose() + { + DalamudApi.PluginInterface.UiBuilder.Draw -= Draw; + DalamudApi.PluginInterface.UiBuilder.OpenConfigUi -= OpenConfigUI; + + GC.SuppressFinalize(this); + } + + public bool SettingsVisible + { + get => _settingsVisible; + // set => _settingsVisible = value; + set { - get => _settingsVisible; - // set => _settingsVisible = value; - set + if (value) { - if (value) - { - var split = _configuration.Name.Split(' '); - _tmpFirstName = split[0]; - _tmpLastName = split[1]; - } - _settingsVisible = value; + var split = _configuration.Name.Split(' '); + _tmpFirstName = split[0]; + _tmpLastName = split[1]; } + _settingsVisible = value; } + } + + private void OpenConfigUI() + { + SettingsVisible = true; + } - public PluginUI(Configuration configuration, PrefPro prefPro) - { - _configuration = configuration; - _prefPro = prefPro; - } - - public void Dispose() - { - - } - - public void Draw() - { - DrawSettingsWindow(); - } + public void Draw() + { + if (!SettingsVisible) return; - public void DrawSettingsWindow() + var height = 340; + var width = _configuration.Gender == GenderSetting.Random ? 390 : 360; + var size = new Vector2(height, width) * ImGui.GetIO().FontGlobalScale; + ImGui.SetNextWindowSize(size, ImGuiCond.FirstUseEver); + if (ImGui.Begin("PrefPro Config", ref _settingsVisible, ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)) { - if (!SettingsVisible) return; + var enabled = _configuration.Enabled; + var currentGender = _configuration.Gender; + var currentRace = _configuration.Race; + var currentTribe = _configuration.Tribe; - var height = 340; - var width = _configuration.Gender == GenderSetting.Random ? 390 : 360; - var size = new Vector2(height, width) * ImGui.GetIO().FontGlobalScale; - ImGui.SetNextWindowSize(size, ImGuiCond.FirstUseEver); - if (ImGui.Begin("PrefPro Config", ref _settingsVisible, ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)) + if (currentRace == RaceSetting.Unknown || currentTribe == TribeSetting.Unknown) { - var enabled = _configuration.Enabled; - var currentGender = _configuration.Gender; - var currentRace = _configuration.Race; - var currentTribe = _configuration.Tribe; - - if (currentRace == RaceSetting.Unknown || currentTribe == TribeSetting.Unknown) - { - currentRace = _prefPro.PlayerRace; - currentTribe = _prefPro.PlayerTribe; - _configuration.Race = currentRace; - _configuration.Tribe = currentTribe; - _configuration.Save(); - } - - var nameFull = _configuration.FullName; - var nameFirst = _configuration.FirstName; - var nameLast = _configuration.LastName; - - if (ImGui.Checkbox("Enable PrefPro", ref enabled)) - { - _configuration.Enabled = enabled; - _configuration.Save(); - } + currentRace = (RaceSetting)PlayerApi.Race; + currentTribe = (TribeSetting)PlayerApi.Tribe; + _configuration.Race = currentRace; + _configuration.Tribe = currentTribe; + _configuration.Save(); + } + + var nameFull = _configuration.FullName; + var nameFirst = _configuration.FirstName; + var nameLast = _configuration.LastName; + + if (ImGui.Checkbox("Enable PrefPro", ref enabled)) + { + _configuration.Enabled = enabled; + _configuration.Save(); + } - if (ImGui.CollapsingHeader("Developer note regarding they/them pronouns")) - { - ImGui.TextWrapped("PrefPro currently cannot and will never have support for they/them pronouns. " + - "This is entirely due to technical limitations and the amount of work for such a feature. " + - "This would require rewriting most dialogue in the game across all languages as well as upkeep on new patches."); - ImGui.End(); - return; - } - - ImGui.Text("For name replacement, PrefPro should use the name..."); - ImGui.Indent(10f * ImGuiHelpers.GlobalScale); - ImGui.PushItemWidth(105f * ImGuiHelpers.GlobalScale); - ImGui.InputText("##newFirstName", ref _tmpFirstName, 15); - ImGui.SameLine(); - ImGui.InputText("##newLastName", ref _tmpLastName, 15); - ImGui.PopItemWidth(); - ImGui.PushItemWidth(20f * ImGuiHelpers.GlobalScale); - ImGui.SameLine(); - if (ImGui.Button("Set##prefProNameSet")) - { - string setName = SanitizeName(_tmpFirstName, _tmpLastName); - _configuration.Name = setName; - var split = setName.Split(' '); - _tmpFirstName = split[0]; - _tmpLastName = split[1]; - _configuration.Save(); - } - ImGui.SameLine(); - if (ImGui.Button("Reset##prefProNameReset")) - { - string resetName = _prefPro.PlayerName; - _configuration.Name = resetName; - var split = resetName.Split(' '); - _tmpFirstName = split[0]; - _tmpLastName = split[1]; - _configuration.Save(); - } - ImGui.PopItemWidth(); - ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); - - ImGui.Text("When NPCs and dialogue use my full name, instead use..."); - ImGui.Indent(10f * ImGuiHelpers.GlobalScale); - ImGui.PushItemWidth(300f * ImGuiHelpers.GlobalScale); - if (ImGui.BeginCombo("##fullnameCombo", GetNameOptionDescriptor(nameFull))) - { - if (ImGui.Selectable(FirstLastDesc)) - _configuration.FullName = NameSetting.FirstLast; - if (ImGui.Selectable(FirstOnlyDesc)) - _configuration.FullName = NameSetting.FirstOnly; - if (ImGui.Selectable(LastOnlyDesc)) - _configuration.FullName = NameSetting.LastOnly; - if (ImGui.Selectable(LastFirstDesc)) - _configuration.FullName = NameSetting.LastFirst; - _configuration.Save(); - ImGui.EndCombo(); - } - ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); - - ImGui.Text("When NPCs and dialogue use my first name, instead use..."); - ImGui.Indent(10f * ImGuiHelpers.GlobalScale); - if (ImGui.BeginCombo("##firstNameCombo", GetNameOptionDescriptor(nameFirst))) - { - if (ImGui.Selectable(FirstLastDesc)) - _configuration.FirstName = NameSetting.FirstLast; - if (ImGui.Selectable(FirstOnlyDesc)) - _configuration.FirstName = NameSetting.FirstOnly; - if (ImGui.Selectable(LastOnlyDesc)) - _configuration.FirstName = NameSetting.LastOnly; - if (ImGui.Selectable(LastFirstDesc)) - _configuration.FirstName = NameSetting.LastFirst; - _configuration.Save(); - ImGui.EndCombo(); - } - ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); - - ImGui.Text("When NPCs and dialogue use my last name, instead use..."); - ImGui.Indent(10f * ImGuiHelpers.GlobalScale); - if (ImGui.BeginCombo("##lastNameCombo", GetNameOptionDescriptor(nameLast))) - { - if (ImGui.Selectable(FirstLastDesc)) - _configuration.LastName = NameSetting.FirstLast; - if (ImGui.Selectable(FirstOnlyDesc)) - _configuration.LastName = NameSetting.FirstOnly; - if (ImGui.Selectable(LastOnlyDesc)) - _configuration.LastName = NameSetting.LastOnly; - if (ImGui.Selectable(LastFirstDesc)) - _configuration.LastName = NameSetting.LastFirst; - _configuration.Save(); - ImGui.EndCombo(); - } - ImGui.PopItemWidth(); - ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); + if (ImGui.CollapsingHeader("Developer note regarding they/them pronouns")) + { + ImGui.TextWrapped("PrefPro currently cannot and will never have support for they/them pronouns. " + + "This is entirely due to technical limitations and the amount of work for such a feature. " + + "This would require rewriting most dialogue in the game across all languages as well as upkeep on new patches."); + ImGui.End(); + return; + } + + ImGui.Text("For name replacement, PrefPro should use the name..."); + ImGui.Indent(10f * ImGuiHelpers.GlobalScale); + ImGui.PushItemWidth(105f * ImGuiHelpers.GlobalScale); + ImGui.InputText("##newFirstName", ref _tmpFirstName, 15); + ImGui.SameLine(); + ImGui.InputText("##newLastName", ref _tmpLastName, 15); + ImGui.PopItemWidth(); + ImGui.PushItemWidth(20f * ImGuiHelpers.GlobalScale); + ImGui.SameLine(); + if (ImGui.Button("Set##prefProNameSet")) + { + string setName = SanitizeName(_tmpFirstName, _tmpLastName); + _configuration.Name = setName; + var split = setName.Split(' '); + _tmpFirstName = split[0]; + _tmpLastName = split[1]; + _configuration.Save(); + } + ImGui.SameLine(); + if (ImGui.Button("Reset##prefProNameReset")) + { + string resetName = PlayerApi.CharacterName; + _configuration.Name = resetName; + var split = resetName.Split(' '); + _tmpFirstName = split[0]; + _tmpLastName = split[1]; + _configuration.Save(); + } + ImGui.PopItemWidth(); + ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); + + ImGui.Text("When NPCs and dialogue use my full name, instead use..."); + ImGui.Indent(10f * ImGuiHelpers.GlobalScale); + ImGui.PushItemWidth(300f * ImGuiHelpers.GlobalScale); + if (DrawSettingSelector("##fullNameCombo", _configuration.FullName, out var newFullName)) + { + _configuration.FullName = newFullName; + _configuration.Save(); + } + ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); + + ImGui.Text("When NPCs and dialogue use my first name, instead use..."); + ImGui.Indent(10f * ImGuiHelpers.GlobalScale); + if (DrawSettingSelector("##firstNameCombo", _configuration.FirstName, out var newFirstName)) + { + _configuration.FirstName = newFirstName; + _configuration.Save(); + } + ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); + + ImGui.Text("When NPCs and dialogue use my last name, instead use..."); + ImGui.Indent(10f * ImGuiHelpers.GlobalScale); + if (DrawSettingSelector("##lastNameCombo", _configuration.LastName, out var newLastName)) + { + _configuration.LastName = newLastName; + _configuration.Save(); + } + ImGui.PopItemWidth(); + ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); - ImGui.TextWrapped("When NPCs and dialogue use gendered text, refer to me as if my character is..."); + ImGui.TextWrapped("When NPCs and dialogue use gendered text, refer to me as if my character is..."); - ImGui.Indent(10f * ImGuiHelpers.GlobalScale); - ImGui.PushItemWidth(140 * ImGuiHelpers.GlobalScale); - if (ImGui.BeginCombo("##prefProComboBox:", GetGenderOptionDescriptor(currentGender))) - { - if (ImGui.Selectable(MaleDesc)) - _configuration.Gender = GenderSetting.Male; - if (ImGui.Selectable(FemaleDesc)) - _configuration.Gender = GenderSetting.Female; - if (ImGui.Selectable(RandomDesc)) - _configuration.Gender = GenderSetting.Random; - if (ImGui.Selectable(ModelDesc)) - _configuration.Gender = GenderSetting.Model; - _configuration.Save(); - ImGui.EndCombo(); - } - if (_configuration.Gender == GenderSetting.Random) - ImGui.TextWrapped("Please note that the gender used in text may not match the gender used in voiceovers."); - ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); - - ImGui.TextWrapped("When NPCs and dialogue refer to my race, refer to me as if my character is..."); + ImGui.Indent(10f * ImGuiHelpers.GlobalScale); + ImGui.PushItemWidth(140 * ImGuiHelpers.GlobalScale); + if (DrawSettingSelector("##genderCombo", _configuration.Gender, out var newGender)) + { + _configuration.Gender = newGender; + _configuration.Save(); + } + if (_configuration.Gender == GenderSetting.Random) + ImGui.TextWrapped("Please note that the gender used in text may not match the gender used in voiceovers."); + ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); + + ImGui.TextWrapped("When NPCs and dialogue refer to my race, refer to me as if my character is..."); - ImGui.Indent(10f * ImGuiHelpers.GlobalScale); - ImGui.PushItemWidth(140 * ImGuiHelpers.GlobalScale); - if (ImGui.BeginCombo("##raceComboBox", GetRaceOptionDescriptor(currentRace))) - { - var values = Enum.GetValues(); - for (int i = 1; i < values.Length; i++) - { - var value = values[i]; - if (ImGui.Selectable(GetRaceOptionDescriptor(value))) - { - _configuration.Race = value; - _configuration.Tribe = (TribeSetting) (i * 2 - 1); - _configuration.Save(); - } - } - ImGui.EndCombo(); - } - ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); - - ImGui.TextWrapped("When NPCs and dialogue refer to my tribe, refer to me as if my character is..."); + ImGui.Indent(10f * ImGuiHelpers.GlobalScale); + ImGui.PushItemWidth(140 * ImGuiHelpers.GlobalScale); + if (DrawSettingSelector("##raceCombo", _configuration.Race, out var newRace, 1)) + { + _configuration.Race = newRace; + _configuration.Save(); + } + ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); + + ImGui.TextWrapped("When NPCs and dialogue refer to my tribe, refer to me as if my character is..."); - ImGui.Indent(10f * ImGuiHelpers.GlobalScale); - ImGui.PushItemWidth(200 * ImGuiHelpers.GlobalScale); - if (ImGui.BeginCombo("##tribeComboBox", GetTribeOptionDescriptor(currentTribe))) - { - var values = Enum.GetValues(); - for (int i = 1; i < values.Length; i++) - { - var value = values[i]; - if (ImGui.Selectable(GetTribeOptionDescriptor(value))) - { - _configuration.Tribe = value; - _configuration.Save(); - } - } - ImGui.EndCombo(); - } - ImGui.PopItemWidth(); - ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); + ImGui.Indent(10f * ImGuiHelpers.GlobalScale); + ImGui.PushItemWidth(200 * ImGuiHelpers.GlobalScale); + if (DrawSettingSelector("##tribeCombo", _configuration.Tribe, out var newTribe, 1)) + { + _configuration.Tribe = newTribe; + _configuration.Save(); } - ImGui.End(); + ImGui.PopItemWidth(); + ImGui.Indent(-10f * ImGuiHelpers.GlobalScale); } - - private string SanitizeName(string first, string last) - { - string newFirst = first; - string newLast = last; - - // Save the last valid name for fail cases - string lastValid = _configuration.Name; - - if (newFirst.Length > 15 || newLast.Length > 15) - return lastValid; - string combined = $"{newFirst}{newLast}"; - if (combined.Length > 20) - return lastValid; + ImGui.End(); + } + + private bool DrawSettingSelector(string key, T current, out T newValue, int startIndex = 0) where T : struct, Enum + { + var changed = false; + newValue = current; + + if (ImGui.BeginCombo(key, GetOptionDescriptor(current))) + { + var values = Enum.GetValues(); + for (int i = startIndex; i < values.Length; i++) + { + var value = values[i]; + var selected = EqualityComparer.Default.Equals(current, value); + if (selected) ImGui.SetItemDefaultFocus(); + if (ImGui.Selectable(GetOptionDescriptor(value), selected)) + { + changed = true; + newValue = value; + } + } + ImGui.EndCombo(); + } + + return changed; + } + + private string SanitizeName(string first, string last) + { + var newFirst = first; + var newLast = last; + + // Save the last valid name for fail cases + var lastValid = _configuration.Name; - newFirst = Regex.Replace(newFirst, "[^A-Za-z'\\-\\s{1}]", ""); - newLast = Regex.Replace(newLast, "[^A-Za-z'\\-\\s{1}]", ""); + if (newFirst.Length > 15 || newLast.Length > 15) + return lastValid; - return $"{newFirst} {newLast}"; - } + var combined = $"{newFirst}{newLast}"; + if (combined.Length > 20) + return lastValid; - private string GetNameOptionDescriptor(NameSetting setting) + newFirst = CleanNameRegex().Replace(newFirst, ""); + newLast = CleanNameRegex().Replace(newLast, ""); + + return $"{newFirst} {newLast}"; + } + + private string GetOptionDescriptor(Enum current) + { + return current switch + { + NameSetting ns => GetNameOptionDescriptor(ns), + GenderSetting gs => GetGenderOptionDescriptor(gs), + RaceSetting rs => GetRaceOptionDescriptor(rs), + TribeSetting ts => GetTribeOptionDescriptor(ts), + _ => string.Empty, + }; + } + + private string GetNameOptionDescriptor(NameSetting setting) + { + return setting switch { - return setting switch - { - NameSetting.FirstLast => FirstLastDesc, - NameSetting.FirstOnly => FirstOnlyDesc, - NameSetting.LastOnly => LastOnlyDesc, - NameSetting.LastFirst => LastFirstDesc, - _ => "" - }; - } + NameSetting.FirstLast => "First name, then last", + NameSetting.FirstOnly => "First name only", + NameSetting.LastOnly => "Last name only", + NameSetting.LastFirst => "Last name, then first", + _ => "" + }; + } - private string GetGenderOptionDescriptor(GenderSetting setting) + private string GetGenderOptionDescriptor(GenderSetting setting) + { + return setting switch { - return setting switch - { - GenderSetting.Male => MaleDesc, - GenderSetting.Female => FemaleDesc, - GenderSetting.Random => RandomDesc, - GenderSetting.Model => ModelDesc, - _ => "" - }; - } - - private string GetRaceOptionDescriptor(RaceSetting setting) + GenderSetting.Male => "Male", + GenderSetting.Female => "Female", + GenderSetting.Random => "Random gender", + GenderSetting.Model => "Model gender", + _ => "" + }; + } + + private string GetRaceOptionDescriptor(RaceSetting setting) + { + return setting switch { - return setting switch - { - RaceSetting.Hyur => "Hyur", - RaceSetting.Elezen => "Elezen", - RaceSetting.Lalafell => "Lalafell", - RaceSetting.Miqote => "Miqo'te", - RaceSetting.Roegadyn => "Roegadyn", - RaceSetting.AuRa => "Au Ra", - RaceSetting.Hrothgar => "Hrothgar", - RaceSetting.Viera => "Viera", - _ => "", - }; - } + RaceSetting.Hyur => "Hyur", + RaceSetting.Elezen => "Elezen", + RaceSetting.Lalafell => "Lalafell", + RaceSetting.Miqote => "Miqo'te", + RaceSetting.Roegadyn => "Roegadyn", + RaceSetting.AuRa => "Au Ra", + RaceSetting.Hrothgar => "Hrothgar", + RaceSetting.Viera => "Viera", + _ => "", + }; + } - private string GetTribeOptionDescriptor(TribeSetting setting) + private string GetTribeOptionDescriptor(TribeSetting setting) + { + return setting switch { - return setting switch - { - TribeSetting.Midlander => "Midlander", - TribeSetting.Highlander => "Highlander", - TribeSetting.Wildwood => "Wildwood", - TribeSetting.Duskwight => "Duskwight", - TribeSetting.Plainsfolk => "Plainsfolk", - TribeSetting.Dunesfolk => "Dunesfolk", - TribeSetting.SeekerOfTheSun => "Seeker of the Sun", - TribeSetting.KeeperOfTheMoon => "Keeper of the Moon", - TribeSetting.SeaWolf => "Sea Wolf", - TribeSetting.Hellsguard => "Hellsguard", - TribeSetting.Raen => "Raen", - TribeSetting.Xaela => "Xaela", - TribeSetting.Helions => "Helions", - TribeSetting.TheLost => "The Lost", - TribeSetting.Rava => "Rava", - TribeSetting.Veena => "Veena", - _ => "", - }; - } - } + TribeSetting.Midlander => "Midlander", + TribeSetting.Highlander => "Highlander", + TribeSetting.Wildwood => "Wildwood", + TribeSetting.Duskwight => "Duskwight", + TribeSetting.Plainsfolk => "Plainsfolk", + TribeSetting.Dunesfolk => "Dunesfolk", + TribeSetting.SeekerOfTheSun => "Seeker of the Sun", + TribeSetting.KeeperOfTheMoon => "Keeper of the Moon", + TribeSetting.SeaWolf => "Sea Wolf", + TribeSetting.Hellsguard => "Hellsguard", + TribeSetting.Raen => "Raen", + TribeSetting.Xaela => "Xaela", + TribeSetting.Helions => "Helions", + TribeSetting.TheLost => "The Lost", + TribeSetting.Rava => "Rava", + TribeSetting.Veena => "Veena", + _ => "", + }; + } } diff --git a/PrefPro/PrefPro.cs b/PrefPro/PrefPro.cs index 295953a..312d995 100644 --- a/PrefPro/PrefPro.cs +++ b/PrefPro/PrefPro.cs @@ -1,301 +1,253 @@ -using Dalamud.Game.Command; -using Dalamud.Plugin; -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using Dalamud.Game.ClientState.Objects.Enums; -using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Game.Text.SeStringHandling.Payloads; -using Dalamud.Hooking; -using FFXIVClientStructs.FFXIV.Client.System.Framework; -using FFXIVClientStructs.FFXIV.Client.System.String; -using FFXIVClientStructs.FFXIV.Client.UI.Misc; -using FFXIVClientStructs.STD; -using PrefPro.Settings; -using System.Runtime.CompilerServices; - -namespace PrefPro; - -public unsafe class PrefPro : IDalamudPlugin -{ - private const string CommandName = "/prefpro"; - - private readonly Configuration _configuration; - private readonly PluginUI _ui; - - //reEncode[1] == 0x29 && reEncode[2] == 0x3 && reEncode[3] == 0xEB && reEncode[4] == 0x2 - private static readonly byte[] FullNameBytes = {0x02, 0x29, 0x03, 0xEB, 0x02, 0x03}; - private static readonly byte[] FirstNameBytes = {0x02, 0x2C, 0x0D, 0xFF, 0x07, 0x02, 0x29, 0x03, 0xEB, 0x02, 0x03, 0xFF, 0x02, 0x20, 0x02, 0x03}; - private static readonly byte[] LastNameBytes = {0x02, 0x2C, 0x0D, 0xFF, 0x07, 0x02, 0x29, 0x03, 0xEB, 0x02, 0x03, 0xFF, 0x02, 0x20, 0x03, 0x03}; - - private delegate int GetStringPrototype(RaptureTextModule* textModule, byte* text, void* decoder, Utf8String* stringStruct); - private readonly Hook _getStringHook; - - private delegate int GetCutVoGenderPrototype(void* a1, void* a2); - private readonly Hook _getCutVoGenderHook; - - private delegate int GetCutVoLangPrototype(void* a1); - private readonly GetCutVoLangPrototype _getCutVoLang; - - private delegate byte GetLuaVarPrototype(nint poolBase, nint a2, nint a3); - private readonly Hook _getLuaVarHook; - - public readonly NameHandlerCache NameHandlerCache; - - public string PlayerName => DalamudApi.ClientState.LocalPlayer?.Name.ToString(); - public int PlayerGender => DalamudApi.ClientState.LocalPlayer?.Customize[(int)CustomizeIndex.Gender] ?? 0; - public RaceSetting PlayerRace => (RaceSetting) (DalamudApi.ClientState.LocalPlayer?.Customize[(int)CustomizeIndex.Race] ?? 0); - public TribeSetting PlayerTribe => (TribeSetting) (DalamudApi.ClientState.LocalPlayer?.Customize[(int)CustomizeIndex.Tribe] ?? 0); - - public ulong CurrentPlayerContentId => DalamudApi.ClientState.LocalContentId; - - private uint _frameworkLangCallOffset = 0; - private LuaHandler _luaHandler; - - public PrefPro(IDalamudPluginInterface pi - ) - { - DalamudApi.Initialize(pi); - - _configuration = DalamudApi.PluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); - _configuration.Initialize(this); - - NameHandlerCache = new NameHandlerCache(_configuration); - - _ui = new PluginUI(_configuration, this); - - DalamudApi.CommandManager.AddHandler(CommandName, new CommandInfo(OnCommand) - { - HelpMessage = "Display the PrefPro configuration interface.", - }); - - var getStringStr = "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 49 8B F9 49 8B F0 48 8B EA 48 8B D9 75 09 48 8B 01 FF 90"; - var getStringPtr = DalamudApi.SigScanner.ScanText(getStringStr); - _getStringHook = DalamudApi.Hooks.HookFromAddress(getStringPtr, GetStringDetour); - - var getCutVoGender = "E8 ?? ?? ?? ?? 49 8B 17 85 DB"; - var getCutVoGenderPtr = DalamudApi.SigScanner.ScanText(getCutVoGender); - _getCutVoGenderHook = DalamudApi.Hooks.HookFromAddress(getCutVoGenderPtr, GetCutVoGenderDetour); - - var getCutVoLang = "E8 ?? ?? ?? ?? 49 63 56 1C"; - var getCutVoLangPtr = DalamudApi.SigScanner.ScanText(getCutVoLang); - _getCutVoLang = Marshal.GetDelegateForFunctionPointer(getCutVoLangPtr); - - var getLuaVar = "E8 ?? ?? ?? ?? 48 85 DB 74 1B 48 8D 8F"; - var getLuaVarPtr = DalamudApi.SigScanner.ScanText(getLuaVar); - _getLuaVarHook = DalamudApi.Hooks.HookFromAddress(getLuaVarPtr, GetLuaVarDetour); - - // var frameworkLangCallOffsetStr = "48 8B 88 ?? ?? ?? ?? E8 ?? ?? ?? ?? 4C 63 7E 24"; - // var frameworkLangCallOffsetPtr = DalamudApi.SigScanner.ScanText(frameworkLangCallOffsetStr); - // _frameworkLangCallOffset = *(uint*)(frameworkLangCallOffsetPtr + 3); - // DalamudApi.PluginLog.Verbose($"framework lang call offset {_frameworkLangCallOffset} {_frameworkLangCallOffset:X}"); - - _luaHandler = new LuaHandler(_configuration); - - // TODO: Include? no idea - // if (frameworkLangCallOffset is < 10000 or > 14000) - // { - // PluginLog.Error("Framework language call offset is invalid. The plugin will be disabled."); - // throw new InvalidOperationException(); - // } - - _getStringHook.Enable(); - _getCutVoGenderHook.Enable(); - _getLuaVarHook.Enable(); - - DalamudApi.PluginInterface.UiBuilder.Draw += DrawUI; - DalamudApi.PluginInterface.UiBuilder.OpenConfigUi += DrawConfigUI; - DalamudApi.ClientState.Login += OnLogin; - } - - private void OnLogin() - { - _luaHandler = new LuaHandler(_configuration); - } - - public void Dispose() - { - _ui.Dispose(); - - _getStringHook?.Disable(); - _getStringHook?.Dispose(); - _getCutVoGenderHook?.Disable(); - _getCutVoGenderHook?.Dispose(); - _luaHandler?.Dispose(); - _getLuaVarHook?.Dispose(); - - DalamudApi.CommandManager.RemoveHandler(CommandName); - } - - private int GetStringDetour(RaptureTextModule* raptureTextModule, byte* text, void* unknown2, Utf8String* output) - { - // DalamudApi.PluginLog.Verbose($"[getStringDetour] {(nuint)raptureTextModule:X2} {(nuint)text:X2} {(nuint)unknown2:X2} {(nuint)stringStruct:X2}"); - if (!_configuration.Enabled) - return _getStringHook.Original(raptureTextModule, text, unknown2, output); - - var decoderParams = ***(StdDeque***) ((nuint) RaptureTextModule.Instance() + 0x40); - - var raceParam = decoderParams[70]; - var oldRace = raceParam.Value; - if (raceParam.Self == 0) - return _getStringHook.Original(raptureTextModule, text, unknown2, output); - var racePtr = (ulong*) raceParam.Self; - *racePtr = (ulong)_configuration.Race; - - var genderParam = decoderParams[3]; - var oldGender = genderParam.Value; - if (genderParam.Self == 0) - return _getStringHook.Original(raptureTextModule, text, unknown2, output); - var genderPtr = (ulong*)genderParam.Self; - *genderPtr = (ulong)_configuration.GetGender(); - - HandleName(ref text); - var result = _getStringHook.Original(raptureTextModule, text, unknown2, output); - // Marshal.FreeHGlobal((IntPtr)text); - - raceParam = decoderParams[70]; - racePtr = (ulong*) raceParam.Self; - *racePtr = oldRace; - - genderParam = decoderParams[3]; - genderPtr = (ulong*)genderParam.Self; - *genderPtr = oldGender; - - return result; - } - - private byte GetLuaVarDetour(nint poolBase, IntPtr a2, IntPtr a3) - { - var oldGender = GetLuaVarGender(poolBase); - var newGender = _configuration.GetGender(); - SetLuaVarGender(poolBase, newGender); - var returnValue = _getLuaVarHook.Original(poolBase, a2, a3); - SetLuaVarGender(poolBase, oldGender); - return returnValue; - } - - private int GetLuaVarGender(nint poolBase) - { - var genderVarId = 0x1B; - var gender = *(int*)(poolBase + 4 * genderVarId); - return gender; - } - - private void SetLuaVarGender(nint poolBase, int gender) - { - var genderVarId = 0x1B; - *(int*)(poolBase + 4 * genderVarId) = gender; - } - - private int GetCutVoGenderDetour(void* a1, void* a2) - { - var originalRet = _getCutVoGenderHook.Original(a1, a2); - // DalamudApi.PluginLog.Verbose($"[GetCutVoGenderDetour] original returned {originalRet}"); - - if (!_configuration.Enabled) - return originalRet; - - var lang = GetCutVoLang(); - // DalamudApi.PluginLog.Verbose($"[GetCutVoGenderDetour] Lang returned {lang}"); - - var v1 = *(int*) ((ulong)a2 + 28); - var v2 = 12 * lang; - var v3 = *(int*) ((ulong)a2 + (ulong)v1 + (ulong)v2); - - if (v3 == 1) - { - // DalamudApi.PluginLog.Verbose($"[GetCutVoGenderDetour] v3 is 1"); - return 0; - } - - return _configuration.GetGender(); - } - - private int GetCutVoLang() - { - // var offs = *(void**) ((nint)Framework.Instance() + (int) _frameworkLangCallOffset); - // DalamudApi.PluginLog.Verbose($"[GetCutVoLang] {(ulong) offs} {(ulong) offs:X}"); - return _getCutVoLang(Framework.Instance()->EnvironmentManager); - } - - /** - * This function is still necessary because of the name options provided in earlier versions. - * So, we will never really be able to get rid of the string parsing in PrefPro. - */ - private void HandleName(ref byte* ptr) - { - var data = NameHandlerCache.Config; - if (!data.Apply) - return; - - var bytes = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(ptr); - if (!bytes.Contains((byte)0x02)) - return; - - var parsed = SeString.Parse(ptr, bytes.Length); - - var payloads = parsed.Payloads; - var numPayloads = payloads.Count; - var replaced = false; - for (var payloadIndex = 0; payloadIndex < numPayloads; payloadIndex++) - { - var payload = payloads[payloadIndex]; - if (payload.Type == PayloadType.Unknown) { - var payloadBytes = payload.Encode(); - if (data.ApplyFull && ByteArrayEquals(payloadBytes, FullNameBytes)) - { - payloads[payloadIndex] = data.NameFull; - replaced = true; - } - else if (data.ApplyFirst && ByteArrayEquals(payloadBytes, FirstNameBytes)) - { - payloads[payloadIndex] = data.NameFirst; - replaced = true; - } - else if (data.ApplyLast && ByteArrayEquals(payloadBytes, LastNameBytes)) - { - payloads[payloadIndex] = data.NameLast; - replaced = true; - } - } - } - - if (!replaced) return; - - var src = parsed.EncodeWithNullTerminator(); - var srcLength = src.Length; - var destLength = bytes.Length + 1; // Add 1 for null terminator not included in Span - - if (srcLength <= destLength) - { - src.CopyTo(new Span(ptr, destLength)); - } - else - { - var newStr = (byte*) Marshal.AllocHGlobal(srcLength); - src.CopyTo(new Span(newStr, srcLength)); - ptr = newStr; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool ByteArrayEquals(ReadOnlySpan a1, ReadOnlySpan a2) - { - return a1.SequenceEqual(a2); - } - - private void OnCommand(string command, string args) - { - _ui.SettingsVisible = true; - } - - private void DrawUI() - { - _ui.Draw(); - } - - private void DrawConfigUI() - { - _ui.SettingsVisible = true; - } +using Dalamud.Game.Command; +using Dalamud.Hooking; +using Dalamud.Plugin; +using Dalamud.Utility; +using FFXIVClientStructs.FFXIV.Client.System.Framework; +using FFXIVClientStructs.FFXIV.Client.System.String; +using FFXIVClientStructs.FFXIV.Component.Text; +using FFXIVClientStructs.STD; +using InteropGenerator.Runtime; +using Lumina.Text; +using Lumina.Text.Expressions; +using Lumina.Text.Payloads; +using Lumina.Text.ReadOnly; +using System; + +namespace PrefPro; + +public unsafe class PrefPro : IDalamudPlugin +{ + private const string CommandName = "/prefpro"; + + private readonly Configuration _configuration; + private readonly PluginUI _ui; + private readonly LuaHandler _luaHandler; + + private readonly Hook _formatStringHook; + + private delegate int GetCutVoGenderDelegate(nint a1, nint a2); + private readonly Hook _getCutVoGenderHook; + + private delegate byte GetLuaVarDelegate(nint poolBase, nint a2, nint a3); + private readonly Hook _getLuaVarHook; + + public readonly NameHandlerCache NameHandlerCache; + + public PrefPro(IDalamudPluginInterface pi) + { + DalamudApi.Initialize(pi); + + _configuration = DalamudApi.PluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); + _configuration.Initialize(this); + + _ui = new PluginUI(_configuration); + _luaHandler = new LuaHandler(_configuration); + NameHandlerCache = new NameHandlerCache(_configuration); + + DalamudApi.CommandManager.AddHandler(CommandName, new CommandInfo(OnCommand) + { + HelpMessage = "Display the PrefPro configuration interface.", + }); + + _formatStringHook = DalamudApi.Hooks.HookFromSignature( + "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 49 8B F9 49 8B F0 48 8B EA 48 8B D9 75 09 48 8B 01 FF 90", + FormatStringDetour); + + _getCutVoGenderHook = DalamudApi.Hooks.HookFromSignature( + "E8 ?? ?? ?? ?? 49 8B 17 85 DB", + GetCutVoGenderDetour); + + _getLuaVarHook = DalamudApi.Hooks.HookFromSignature( + "E8 ?? ?? ?? ?? 48 85 DB 74 1B 48 8D 8F", + GetLuaVarDetour); + + // var frameworkLangCallOffsetStr = "48 8B 88 ?? ?? ?? ?? E8 ?? ?? ?? ?? 4C 63 7E 24"; + // var frameworkLangCallOffsetPtr = DalamudApi.SigScanner.ScanText(frameworkLangCallOffsetStr); + // _frameworkLangCallOffset = *(uint*)(frameworkLangCallOffsetPtr + 3); + // DalamudApi.PluginLog.Verbose($"framework lang call offset {_frameworkLangCallOffset} {_frameworkLangCallOffset:X}"); + + // TODO: Include? no idea + // if (frameworkLangCallOffset is < 10000 or > 14000) + // { + // PluginLog.Error("Framework language call offset is invalid. The plugin will be disabled."); + // throw new InvalidOperationException(); + // } + + _formatStringHook.Enable(); + _getCutVoGenderHook.Enable(); + _getLuaVarHook.Enable(); + } + + public void Dispose() + { + DalamudApi.CommandManager.RemoveHandler(CommandName); + + _formatStringHook?.Disable(); + _formatStringHook?.Dispose(); + _getCutVoGenderHook?.Disable(); + _getCutVoGenderHook?.Dispose(); + _luaHandler?.Dispose(); + _getLuaVarHook?.Dispose(); + + GC.SuppressFinalize(this); + } + + private void OnCommand(string command, string args) + { + _ui.SettingsVisible = true; + } + + /** + * This function is still necessary because of the name options provided in earlier versions. + * So, we will never really be able to get rid of the string parsing in PrefPro. + */ + private bool FormatStringDetour(TextModule* thisPtr, CStringPointer input, StdDeque* localParameters, Utf8String* output) + { + if (!_configuration.Enabled) + goto originalFormatString; + + var data = NameHandlerCache.GetConfig(); + if (!data.Apply) + goto originalFormatString; + + var seString = input.AsReadOnlySeStringSpan(); + if (seString.IsEmpty || seString.IsTextOnly()) + goto originalFormatString; + + ref var decoderParams = ref thisPtr->GlobalParameters; + if (decoderParams.Count < 70) + goto originalFormatString; + + ref var raceParam = ref decoderParams[70]; + if (raceParam.ValuePtr == null) + goto originalFormatString; + + ref var genderParam = ref decoderParams[3]; + if (genderParam.ValuePtr == null) + goto originalFormatString; + + var oldRace = raceParam.IntValue; + raceParam.IntValue = (int)_configuration.Race; + + var oldGender = genderParam.IntValue; + genderParam.IntValue = _configuration.GetGender(); + + var sb = SeStringBuilder.SharedPool.Get(); + + try + { + foreach (var payload in seString) + { + if (data.ApplyFull && ShouldHandleStringPayload(payload)) + { + sb.Append(data.NameFull); + } + else if (data.ApplyFirst && ShouldHandleSplitPayload(payload, 1)) + { + sb.Append(data.NameFirst); + } + else if (data.ApplyLast && ShouldHandleSplitPayload(payload, 2)) + { + sb.Append(data.NameLast); + } + else + { + sb.Append(payload); + } + } + + fixed (byte* newInput = sb.GetViewAsSpan()) + return _formatStringHook.Original(thisPtr, newInput, localParameters, output); + } + catch (Exception ex) + { + DalamudApi.PluginLog.Error(ex, "PrefPro Exception"); + } + finally + { + SeStringBuilder.SharedPool.Return(sb); + + raceParam.IntValue = oldRace; + genderParam.IntValue = oldGender; + } + + originalFormatString: + return _formatStringHook.Original(thisPtr, input, localParameters, output); + } + + private byte GetLuaVarDetour(nint poolBase, nint a2, nint a3) + { + var oldGender = GetLuaVarGender(poolBase); + var newGender = _configuration.GetGender(); + SetLuaVarGender(poolBase, newGender); + var returnValue = _getLuaVarHook.Original(poolBase, a2, a3); + SetLuaVarGender(poolBase, oldGender); + return returnValue; + } + + private int GetLuaVarGender(nint poolBase) + { + var genderVarId = 0x1B; + var gender = *(int*)(poolBase + 4 * genderVarId); + return gender; + } + + private void SetLuaVarGender(nint poolBase, int gender) + { + var genderVarId = 0x1B; + *(int*)(poolBase + 4 * genderVarId) = gender; + } + + private int GetCutVoGenderDetour(nint a1, nint a2) + { + var originalRet = _getCutVoGenderHook.Original(a1, a2); + // DalamudApi.PluginLog.Verbose($"[GetCutVoGenderDetour] original returned {originalRet}"); + + if (!_configuration.Enabled) + return originalRet; + + // see Client::System::Framework::EnvironmentManager.GetCutsceneLanguage + var lang = (uint)Framework.Instance()->EnvironmentManager->CutsceneMovieVoice; + if (lang == uint.MaxValue) + lang = DalamudApi.GameConfig.System.GetUInt("CutsceneMovieVoice"); + if (lang == uint.MaxValue) + lang = DalamudApi.GameConfig.System.GetUInt("Language"); + + // DalamudApi.PluginLog.Verbose($"[GetCutVoGenderDetour] Lang returned {lang}"); + + if (*(int*)(a2 + *(int*)(a2 + 0x1C) + (12 * lang)) != 1) + return _configuration.GetGender(); + + return 0; + } + + // + private static bool ShouldHandleStringPayload(ReadOnlySePayloadSpan payload) + { + return payload.Type == ReadOnlySePayloadType.Macro + && payload.MacroCode == MacroCode.String + && payload.TryGetExpression(out var expr1) + && expr1.TryGetParameterExpression(out var expressionType, out var operand) + && expressionType == (byte)ExpressionType.GlobalString + && operand.TryGetInt(out var gstrIndex) + && gstrIndex == 1; + } + + // , ,index)> + private static bool ShouldHandleSplitPayload(ReadOnlySePayloadSpan payload, int splitIndex) + { + if (payload.Type == ReadOnlySePayloadType.Macro + && payload.MacroCode == MacroCode.Split + && payload.TryGetExpression(out var expr1, out var _, out var expr3) + && expr1.TryGetString(out var text) + && text.PayloadCount == 1 + && expr3.TryGetInt(out var index) + && index == splitIndex) + { + var enu = text.GetEnumerator(); + return enu.MoveNext() && ShouldHandleStringPayload(enu.Current); + } + + return false; + } } \ No newline at end of file diff --git a/PrefPro/PrefPro.csproj b/PrefPro/PrefPro.csproj index c175c35..4e228f1 100644 --- a/PrefPro/PrefPro.csproj +++ b/PrefPro/PrefPro.csproj @@ -26,10 +26,7 @@ - - $(DalamudLibPath)Iced.dll - False - + diff --git a/PrefPro/Settings/GenderSetting.cs b/PrefPro/Settings/GenderSetting.cs index ed10820..8d9961a 100644 --- a/PrefPro/Settings/GenderSetting.cs +++ b/PrefPro/Settings/GenderSetting.cs @@ -1,9 +1,9 @@ namespace PrefPro.Settings; public enum GenderSetting -{ - Male, - Female, - Random, - Model +{ + Male, + Female, + Random, + Model } \ No newline at end of file diff --git a/PrefPro/Settings/NameSetting.cs b/PrefPro/Settings/NameSetting.cs index cf959da..f7eb7f8 100644 --- a/PrefPro/Settings/NameSetting.cs +++ b/PrefPro/Settings/NameSetting.cs @@ -1,9 +1,9 @@ namespace PrefPro.Settings; public enum NameSetting -{ - FirstLast, - FirstOnly, - LastOnly, - LastFirst +{ + FirstLast, + FirstOnly, + LastOnly, + LastFirst } \ No newline at end of file diff --git a/PrefPro/Settings/RaceSetting.cs b/PrefPro/Settings/RaceSetting.cs index 1c8a812..4c649ae 100644 --- a/PrefPro/Settings/RaceSetting.cs +++ b/PrefPro/Settings/RaceSetting.cs @@ -1,14 +1,14 @@ namespace PrefPro.Settings; public enum RaceSetting -{ - Unknown, - Hyur, - Elezen, - Lalafell, - Miqote, - Roegadyn, - AuRa, - Hrothgar, - Viera, +{ + Unknown, + Hyur, + Elezen, + Lalafell, + Miqote, + Roegadyn, + AuRa, + Hrothgar, + Viera, } \ No newline at end of file diff --git a/PrefPro/Settings/TribeSetting.cs b/PrefPro/Settings/TribeSetting.cs index d9584a2..4245370 100644 --- a/PrefPro/Settings/TribeSetting.cs +++ b/PrefPro/Settings/TribeSetting.cs @@ -1,22 +1,22 @@ namespace PrefPro.Settings; public enum TribeSetting -{ - Unknown, - Midlander, - Highlander, - Wildwood, - Duskwight, - Plainsfolk, - Dunesfolk, - SeekerOfTheSun, - KeeperOfTheMoon, - SeaWolf, - Hellsguard, - Raen, - Xaela, - Helions, - TheLost, - Rava, - Veena, +{ + Unknown, + Midlander, + Highlander, + Wildwood, + Duskwight, + Plainsfolk, + Dunesfolk, + SeekerOfTheSun, + KeeperOfTheMoon, + SeaWolf, + Hellsguard, + Raen, + Xaela, + Helions, + TheLost, + Rava, + Veena, } \ No newline at end of file diff --git a/PrefPro/UnknownStruct.cs b/PrefPro/UnknownStruct.cs deleted file mode 100644 index 38b5f3c..0000000 --- a/PrefPro/UnknownStruct.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Runtime.InteropServices; - -namespace PrefPro; - -[StructLayout(LayoutKind.Explicit, Size = 0x20)] -public struct UnknownStruct -{ - [FieldOffset(0x00)] public ulong Value; - [FieldOffset(0x08)] public nuint Self; - [FieldOffset(0x16)] public sbyte Status; -} \ No newline at end of file