From 19357a73452718aeba2572d1b2829bd584dc2559 Mon Sep 17 00:00:00 2001 From: bonbon Date: Wed, 24 Jan 2024 11:21:22 +0900 Subject: [PATCH 01/11] HttpClient improvement (#302) --- OverlayPlugin.Updater/HttpClientWrapper.cs | 49 ++++++++++++++-------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/OverlayPlugin.Updater/HttpClientWrapper.cs b/OverlayPlugin.Updater/HttpClientWrapper.cs index 8938f1d6b..cfc7b7725 100644 --- a/OverlayPlugin.Updater/HttpClientWrapper.cs +++ b/OverlayPlugin.Updater/HttpClientWrapper.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; +using System.Linq; using System.Net.Http; using System.Reflection; using System.Threading; @@ -10,6 +12,11 @@ namespace RainbowMage.OverlayPlugin.Updater public static class HttpClientWrapper { public delegate bool ProgressInfoCallback(long resumed, long dltotal, long dlnow, long ultotal, long ulnow); + private static readonly HttpClient client = new HttpClient(); + static HttpClientWrapper() + { + client.DefaultRequestHeaders.Add("User-Agent", "OverlayPlugin/OverlayPlugin v" + Assembly.GetExecutingAssembly().GetName().Version.ToString()); + } public static void Init(string pluginDirectory) { @@ -24,24 +31,27 @@ public static string Get(string url) public static string Get(string url, Dictionary headers, string downloadDest, ProgressInfoCallback infoCb, bool resume) { - var client = new HttpClient(); - client.DefaultRequestHeaders.Add("User-Agent", "OverlayPlugin/OverlayPlugin v" + Assembly.GetExecutingAssembly().GetName().Version.ToString()); - - foreach (var key in headers.Keys) - { - client.DefaultRequestHeaders.Add(key, headers[key]); - } - var completionLock = new object(); string result = null; Exception error = null; var retry = false; + var request = new HttpRequestMessage() + { + RequestUri = new Uri(url), + Method = HttpMethod.Get, + }; + + foreach (var key in headers.Keys) + { + request.Headers.Add(key, headers[key]); + } + Action action = (async () => { try { - var response = await client.GetAsync(url); + var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); if (downloadDest == null) { @@ -50,28 +60,31 @@ public static string Get(string url, Dictionary headers, string else { var buffer = new byte[40 * 1024]; - var length = 0; + long length = 0; - IEnumerable lengthValues; - if (response.Headers.TryGetValues("Content-Length", out lengthValues)) + long? nLength = response.Content.Headers.ContentLength; + if (nLength.HasValue) { - int.TryParse(lengthValues.GetEnumerator().Current, out length); + length = nLength.Value; } using (var writer = File.Open(downloadDest, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read)) - // FIXME: ReadAsStreamAsync() waits until the download finishes before returning. - // This breaks progress reporting and makes it impossible to abort running downloads. using (var body = await response.Content.ReadAsStreamAsync()) { var stop = false; + var pos = 0; while (!stop) { var read = await body.ReadAsync(buffer, 0, buffer.Length); - if (read == 0) + if (read != 0) + { + writer.Write(buffer, 0, read); + pos += read; + } + else break; - writer.Write(buffer, 0, read); - if (infoCb != null && infoCb(0, length, body.Position, 0, 0)) + if (infoCb != null && infoCb(0, length, pos, 0, 0)) break; } } From c00b682c561a4f36b448c9762283d656e73d7b7b Mon Sep 17 00:00:00 2001 From: Makar Date: Tue, 13 Feb 2024 01:40:00 -0600 Subject: [PATCH 02/11] Update opcodes for Global 6.57 (#303) --- OverlayPlugin.Core/resources/opcodes.jsonc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/OverlayPlugin.Core/resources/opcodes.jsonc b/OverlayPlugin.Core/resources/opcodes.jsonc index f4158e808..18197a225 100644 --- a/OverlayPlugin.Core/resources/opcodes.jsonc +++ b/OverlayPlugin.Core/resources/opcodes.jsonc @@ -19,18 +19,18 @@ } }, // Global region - "2024.01.06.0000.0000": { - // Version: 6.55 + "2024.02.05.0000.0000": { + // Version: 6.57 "MapEffect": { - "opcode": 544, + "opcode": 141, "size": 11 }, "CEDirector": { - "opcode": 414, + "opcode": 984, "size": 16 }, "RSVData": { - "opcode": 806, + "opcode": 625, "size": 1080 } }, From 7728ab927c99fb5dcf24bb23d1fafae556a13d11 Mon Sep 17 00:00:00 2001 From: xpdota Date: Thu, 15 Feb 2024 18:48:21 -0800 Subject: [PATCH 03/11] Fix generated overlay URL when using '*' as the websocket bind IP (#305) Fix * IP address generated URL --- OverlayPlugin.Core/Controls/WSConfigPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OverlayPlugin.Core/Controls/WSConfigPanel.cs b/OverlayPlugin.Core/Controls/WSConfigPanel.cs index 381ef46be..591b9bceb 100644 --- a/OverlayPlugin.Core/Controls/WSConfigPanel.cs +++ b/OverlayPlugin.Core/Controls/WSConfigPanel.cs @@ -352,7 +352,7 @@ private void cbOverlay_SelectedIndexChanged(object sender, EventArgs e) hostUrl += "ws://"; } - if (_config.WSServerIP == "0.0.0.0") + if (_config.WSServerIP == "0.0.0.0" || _config.WSServerIP == "*") { hostUrl += "127.0.0.1"; } From 8c597c77a414bbbe9ba8d9fcd488c304b4e8e202 Mon Sep 17 00:00:00 2001 From: xpdota Date: Fri, 16 Feb 2024 14:37:17 -0800 Subject: [PATCH 04/11] Also fix generated URL for ipv6 wildcard (#306) --- OverlayPlugin.Core/Controls/WSConfigPanel.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OverlayPlugin.Core/Controls/WSConfigPanel.cs b/OverlayPlugin.Core/Controls/WSConfigPanel.cs index 591b9bceb..74b1bb8da 100644 --- a/OverlayPlugin.Core/Controls/WSConfigPanel.cs +++ b/OverlayPlugin.Core/Controls/WSConfigPanel.cs @@ -356,6 +356,10 @@ private void cbOverlay_SelectedIndexChanged(object sender, EventArgs e) { hostUrl += "127.0.0.1"; } + else if (_config.WSServerIP == "[::]") + { + hostUrl += "[::1]"; + } else { hostUrl += _config.WSServerIP; From 2cacafb76d1bca4d8c102792e83db046d29f2e54 Mon Sep 17 00:00:00 2001 From: valarnin Date: Mon, 19 Feb 2024 14:05:57 -0500 Subject: [PATCH 05/11] Adjust ffxiv clientstructs build (#307) Pre-build FFXIVClientStructs in build.ps1 to avoid build issues that only happen in GH actions --- build.ps1 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.ps1 b/build.ps1 index f79184028..79e30599a 100644 --- a/build.ps1 +++ b/build.ps1 @@ -23,6 +23,12 @@ try { $ENV:PATH = "C:\Program Files\7-Zip;${ENV:PATH}"; } + if ($ENV:CI -eq "true" -and $ENV:GITHUB_RUN_ID -ne $null) { + echo "==> Running as a GitHub Action, pre-running clientstructs scripts" + + .\tools\strip-clientstructs.ps1 + } + if ($ci) { echo "==> Continuous integration flag set. Building Debug..." dotnet publish -c debug From 44c8f2d0b21583638e1bb61d935690494824510d Mon Sep 17 00:00:00 2001 From: valarnin Date: Mon, 19 Feb 2024 18:12:54 -0500 Subject: [PATCH 06/11] Fix .NET analyzers and cleanup build warnings (#283) --- .github/workflows/build-artifact.yml | 2 +- .github/workflows/release.yml | 2 +- AddonExample/AddonExample.csproj | 1 - AddonExample/AddonExampleEventSource.cs | 9 ++- Directory.Build.props | 3 +- HtmlRenderer/NativeMethods.cs | 2 +- HtmlRenderer/OverlayControl.cs | 14 ++--- HtmlRenderer/Renderer.cs | 18 +++++- OverlayPlugin.Core/Controls/ControlPanel.cs | 8 ++- .../Controls/NewOverlayDialog.cs | 4 ++ OverlayPlugin.Core/Controls/WSConfigPanel.cs | 8 +++ OverlayPlugin.Core/EventSourceBase.cs | 14 ++++- OverlayPlugin.Core/KeyboardHook.cs | 23 +++----- .../Aggro/AggroMemoryManager.cs | 2 +- .../AtkStage/AtkStageMemoryManager.cs | 2 +- .../Combatant/CombatantMemoryManager.cs | 2 +- .../Combatant/LineCombatant.cs | 27 +++++++-- .../Enmity/EnmityMemoryManager.cs | 2 +- .../EnmityHud/EnmityHudMemoryManager.cs | 2 +- .../InCombat/InCombatMemoryManager.cs | 2 +- .../MemoryProcessors/Party/PartyMemory.cs | 1 - .../Party/PartyMemoryManager.cs | 2 +- .../Target/TargetMemoryManager.cs | 2 +- OverlayPlugin.Core/NativeMethods.cs | 9 +++ .../NetworkProcessors/LineAbilityExtra.cs | 2 +- .../NetworkProcessors/LineRSV.cs | 1 - OverlayPlugin.Core/OverlayBase.cs | 58 +++++++++++++------ OverlayPlugin.Core/OverlayHider.cs | 27 ++++++++- OverlayPlugin.Core/OverlayZCorrector.cs | 27 ++++++++- .../Overlays/LabelOverlayConfigPanel.cs | 5 ++ .../Overlays/MiniParseConfigPanel.cs | 4 ++ .../Overlays/MiniParseOverlay.cs | 9 ++- OverlayPlugin.Core/PluginConfig.cs | 7 ++- OverlayPlugin.Core/PluginMain.cs | 27 ++++++++- OverlayPlugin.Core/WebSocket/WSServer.cs | 23 +++++++- OverlayPlugin.Updater/HttpClientWrapper.cs | 3 + OverlayPlugin.Updater/Installer.cs | 22 ++++++- OverlayPlugin/PluginLoader.cs | 22 ++++++- .../StripFFXIVClientStructs.cs | 11 ---- tools/VSBuildDeps/VSBuildDeps.csproj | 7 ++- 40 files changed, 321 insertions(+), 95 deletions(-) diff --git a/.github/workflows/build-artifact.yml b/.github/workflows/build-artifact.yml index 88a7ccc74..e7d8f3b7d 100644 --- a/.github/workflows/build-artifact.yml +++ b/.github/workflows/build-artifact.yml @@ -42,7 +42,7 @@ jobs: path: | ./Thirdparty ./OverlayPlugin.Core/Thirdparty - key: ${{ runner.os }}-overlayplugin-${{ hashFiles('./tools/fetch_deps.ps1', './DEPS.json') }} + key: ${{ runner.os }}-overlayplugin-${{ hashFiles('./tools/fetch_deps.ps1', './DEPS.json', './tools/StripFFXIVClientStructs/StripFFXIVClientStructs/StripFFXIVClientStructs.cs') }} restore-keys: | ${{ runner.os }}-overlayplugin- # Fetch dependencies only if cache-hit is false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 07adf863e..164127d83 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,7 +47,7 @@ jobs: path: | ./Thirdparty ./OverlayPlugin.Core/Thirdparty - key: ${{ runner.os }}-overlayplugin-${{ hashFiles('./tools/fetch_deps.ps1', './DEPS.json') }} + key: ${{ runner.os }}-overlayplugin-${{ hashFiles('./tools/fetch_deps.ps1', './DEPS.json', './tools/StripFFXIVClientStructs/StripFFXIVClientStructs/StripFFXIVClientStructs.cs') }} restore-keys: | ${{ runner.os }}-overlayplugin- # Fetch dependencies only if cache-hit is false diff --git a/AddonExample/AddonExample.csproj b/AddonExample/AddonExample.csproj index dd833cbe1..91b321912 100644 --- a/AddonExample/AddonExample.csproj +++ b/AddonExample/AddonExample.csproj @@ -4,7 +4,6 @@ AddonExample AddonExample Copyright © 2015 - MinimumRecommendedRules.ruleset ..\out\$(Configuration)\addons\ diff --git a/AddonExample/AddonExampleEventSource.cs b/AddonExample/AddonExampleEventSource.cs index e13139845..dc801d363 100644 --- a/AddonExample/AddonExampleEventSource.cs +++ b/AddonExample/AddonExampleEventSource.cs @@ -106,9 +106,14 @@ protected override void Update() })); } - public override void Dispose() + protected override void Dispose(bool disposing) { - base.Dispose(); + if (disposing && originalTimer != null) + { + originalTimer.Dispose(); + originalTimer = null; + } + base.Dispose(disposing); } // Sends an event called |event_name| to javascript, with an event.detail that contains diff --git a/Directory.Build.props b/Directory.Build.props index 36394d474..4b3fbfd72 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,7 +3,8 @@ net48 Copyright © RainbowMage 2015, Kuriyama hibiya 2016, ngld 2019, OverlayPlugin Team 2022 - MinimumRecommendedRules.ruleset + false + true 0.19.26 0.19.26 false diff --git a/HtmlRenderer/NativeMethods.cs b/HtmlRenderer/NativeMethods.cs index 6ccbbbed8..ab435c773 100644 --- a/HtmlRenderer/NativeMethods.cs +++ b/HtmlRenderer/NativeMethods.cs @@ -227,7 +227,7 @@ uint uFlags // ウィンドウ位置のオプション [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); - [DllImport("kernel32.dll", SetLastError = true)] + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern int GetModuleFileName(IntPtr hModule, StringBuilder lpFilename, int nSize); [DllImport("user32.dll")] diff --git a/HtmlRenderer/OverlayControl.cs b/HtmlRenderer/OverlayControl.cs index d8c4377dc..037621533 100644 --- a/HtmlRenderer/OverlayControl.cs +++ b/HtmlRenderer/OverlayControl.cs @@ -131,14 +131,14 @@ public void Reload() /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { - if (this.Renderer != null) + if (disposing) { - this.Renderer.Dispose(); - } - - if (disposing && (components != null)) - { - components.Dispose(); + Renderer?.Dispose(); + Renderer = null; + components?.Dispose(); + components = null; + surfaceBuffer?.Dispose(); + surfaceBuffer = null; } base.Dispose(disposing); } diff --git a/HtmlRenderer/Renderer.cs b/HtmlRenderer/Renderer.cs index 873a712ed..ce409e50b 100644 --- a/HtmlRenderer/Renderer.cs +++ b/HtmlRenderer/Renderer.cs @@ -400,12 +400,24 @@ public Bitmap Screenshot() return null; } + private bool _disposed = false; + public void Dispose() { - if (this._browser != null) + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) { - this._browser.Dispose(); - this._browser = null; + if (disposing) + { + this._browser?.Dispose(); + this._browser = null; + } + _disposed = true; } } diff --git a/OverlayPlugin.Core/Controls/ControlPanel.cs b/OverlayPlugin.Core/Controls/ControlPanel.cs index a5a1bcd2f..4419f488a 100644 --- a/OverlayPlugin.Core/Controls/ControlPanel.cs +++ b/OverlayPlugin.Core/Controls/ControlPanel.cs @@ -17,6 +17,10 @@ public partial class ControlPanel : UserControl { TinyIoCContainer _container; ILogger _logger; + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Usage", + "CA2213:Disposable fields should be disposed", + Justification = "_pluginMain is disposed of by TinyIoCContainer")] PluginMain _pluginMain; IPluginConfig _config; Registry _registry; @@ -94,7 +98,9 @@ protected override void Dispose(bool disposing) { if (disposing) { - if (components != null) components.Dispose(); + components?.Dispose(); + _generalTab?.Dispose(); + _eventTab?.Dispose(); _registry.EventSourceRegistered -= AddEventSourceTab; _logger.ClearListener(); } diff --git a/OverlayPlugin.Core/Controls/NewOverlayDialog.cs b/OverlayPlugin.Core/Controls/NewOverlayDialog.cs index b2e042189..7f6b72755 100644 --- a/OverlayPlugin.Core/Controls/NewOverlayDialog.cs +++ b/OverlayPlugin.Core/Controls/NewOverlayDialog.cs @@ -17,6 +17,10 @@ public partial class NewOverlayDialog : Form public ValidateNameDelegate NameValidator { get; set; } public IOverlay SelectedOverlay { get; private set; } + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Usage", + "CA2213:Disposable fields should be disposed", + Justification = "pluginMain is disposed of by TinyIoCContainer")] private PluginMain pluginMain; private Registry registry; private ILogger logger; diff --git a/OverlayPlugin.Core/Controls/WSConfigPanel.cs b/OverlayPlugin.Core/Controls/WSConfigPanel.cs index 74b1bb8da..3c835f3d5 100644 --- a/OverlayPlugin.Core/Controls/WSConfigPanel.cs +++ b/OverlayPlugin.Core/Controls/WSConfigPanel.cs @@ -23,7 +23,15 @@ public partial class WSConfigPanel : UserControl const string NGROK_CHOCO_SCRIPT = "https://raw.githubusercontent.com/ngrok/choco-ngrok/main/tools/chocolateyinstall.ps1"; IPluginConfig _config; + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Usage", + "CA2213:Disposable fields should be disposed", + Justification = "_server is disposed of by TinyIoCContainer")] WSServer _server; + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Usage", + "CA2213:Disposable fields should be disposed", + Justification = "_plugin is disposed of by TinyIoCContainer")] PluginMain _plugin; Registry _registry; Process _ngrok; diff --git a/OverlayPlugin.Core/EventSourceBase.cs b/OverlayPlugin.Core/EventSourceBase.cs index 477e06fdd..eee925f6b 100644 --- a/OverlayPlugin.Core/EventSourceBase.cs +++ b/OverlayPlugin.Core/EventSourceBase.cs @@ -71,9 +71,19 @@ protected void Log(LogLevel level, string message, params object[] args) logger.Log(level, message, args); } - public virtual void Dispose() + public void Dispose() { - timer?.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing && timer != null) + { + timer.Dispose(); + timer = null; + } } public virtual void Start() diff --git a/OverlayPlugin.Core/KeyboardHook.cs b/OverlayPlugin.Core/KeyboardHook.cs index de36351ea..413cabb8c 100644 --- a/OverlayPlugin.Core/KeyboardHook.cs +++ b/OverlayPlugin.Core/KeyboardHook.cs @@ -7,15 +7,6 @@ namespace RainbowMage.OverlayPlugin { public sealed class KeyboardHook : NativeWindow, IDisposable { - // Registers a hot key with Windows. - [DllImport("user32.dll")] - private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); - // Unregisters the hot key with Windows. - [DllImport("user32.dll")] - private static extern bool UnregisterHotKey(IntPtr hWnd, int id); - - private static int WM_HOTKEY = 0x0312; - private Dictionary _hotkeys = new Dictionary(); private ILogger _logger; @@ -28,7 +19,7 @@ protected override void WndProc(ref Message m) base.WndProc(ref m); // check if we got a hot key pressed. - if (m.Msg == WM_HOTKEY && _hotkeys.TryGetValue((int)m.LParam, out HotKeyInfo info)) + if (m.Msg == NativeMethods.WM_HOTKEY && _hotkeys.TryGetValue((int)m.LParam, out HotKeyInfo info)) { foreach (var cb in info.Callbacks) { @@ -58,7 +49,7 @@ public void RegisterHotKey(ModifierKeys modifier, Keys key, Action callback) _hotkeys[lookupKey] = new HotKeyInfo(); // register the hot key. - if (!RegisterHotKey(Handle, _hotkeys[lookupKey].Id, (uint)modifier, (uint)key)) + if (!NativeMethods.RegisterHotKey(Handle, _hotkeys[lookupKey].Id, (uint)modifier, (uint)key)) { _hotkeys.Remove(lookupKey); throw new InvalidOperationException("Couldn’t register the hot key."); @@ -77,7 +68,7 @@ public void UnregisterHotKey(ModifierKeys modifier, Keys key, Action callback) if (info.Callbacks.Count < 1) { - if (UnregisterHotKey(Handle, info.Id)) + if (NativeMethods.UnregisterHotKey(Handle, info.Id)) { _hotkeys.Remove(lookupKey); } @@ -96,7 +87,7 @@ public void UnregisterHotKey(Action callback) pair.Value.Callbacks.Remove(callback); if (pair.Value.Callbacks.Count < 1) { - if (UnregisterHotKey(Handle, pair.Value.Id)) + if (NativeMethods.UnregisterHotKey(Handle, pair.Value.Id)) { toRemove.Add(pair.Key); } @@ -114,7 +105,7 @@ public void DisableHotKeys() { foreach (var pair in _hotkeys) { - if (!UnregisterHotKey(Handle, pair.Value.Id)) + if (!NativeMethods.UnregisterHotKey(Handle, pair.Value.Id)) { _logger.Log(LogLevel.Error, Resources.UnregisterHotkeyError, pair.Key); } @@ -128,7 +119,7 @@ public void EnableHotKeys() uint modifier = (uint)pair.Key & 0xFFFF; uint key = (uint)(pair.Key >> 16) & 0xFFFF; - if (!RegisterHotKey(Handle, pair.Value.Id, modifier, key)) + if (!NativeMethods.RegisterHotKey(Handle, pair.Value.Id, modifier, key)) { _logger.Log(LogLevel.Error, Resources.RegisterHotkeyError, modifier, key); } @@ -142,7 +133,7 @@ public void Dispose() // unregister all the registered hot keys. foreach (var info in _hotkeys) { - UnregisterHotKey(Handle, info.Value.Id); + NativeMethods.UnregisterHotKey(Handle, info.Value.Id); } // dispose the native window. diff --git a/OverlayPlugin.Core/MemoryProcessors/Aggro/AggroMemoryManager.cs b/OverlayPlugin.Core/MemoryProcessors/Aggro/AggroMemoryManager.cs index ea1e4c2a6..ee1fdd23c 100644 --- a/OverlayPlugin.Core/MemoryProcessors/Aggro/AggroMemoryManager.cs +++ b/OverlayPlugin.Core/MemoryProcessors/Aggro/AggroMemoryManager.cs @@ -51,7 +51,7 @@ public bool IsValid() return true; } - Version IVersionedMemory.GetVersion() + public Version GetVersion() { if (!IsValid()) return null; diff --git a/OverlayPlugin.Core/MemoryProcessors/AtkStage/AtkStageMemoryManager.cs b/OverlayPlugin.Core/MemoryProcessors/AtkStage/AtkStageMemoryManager.cs index d59c40dbd..5ba8872f7 100644 --- a/OverlayPlugin.Core/MemoryProcessors/AtkStage/AtkStageMemoryManager.cs +++ b/OverlayPlugin.Core/MemoryProcessors/AtkStage/AtkStageMemoryManager.cs @@ -52,7 +52,7 @@ public bool IsValid() return true; } - Version IVersionedMemory.GetVersion() + public Version GetVersion() { if (!IsValid()) return null; diff --git a/OverlayPlugin.Core/MemoryProcessors/Combatant/CombatantMemoryManager.cs b/OverlayPlugin.Core/MemoryProcessors/Combatant/CombatantMemoryManager.cs index 8ebb2b6f4..0fbfac786 100644 --- a/OverlayPlugin.Core/MemoryProcessors/Combatant/CombatantMemoryManager.cs +++ b/OverlayPlugin.Core/MemoryProcessors/Combatant/CombatantMemoryManager.cs @@ -58,7 +58,7 @@ public bool IsValid() return true; } - Version IVersionedMemory.GetVersion() + public Version GetVersion() { if (!IsValid()) return null; diff --git a/OverlayPlugin.Core/MemoryProcessors/Combatant/LineCombatant.cs b/OverlayPlugin.Core/MemoryProcessors/Combatant/LineCombatant.cs index dd0d80271..df101eacd 100644 --- a/OverlayPlugin.Core/MemoryProcessors/Combatant/LineCombatant.cs +++ b/OverlayPlugin.Core/MemoryProcessors/Combatant/LineCombatant.cs @@ -12,7 +12,7 @@ namespace RainbowMage.OverlayPlugin.MemoryProcessors.Combatant { - public class LineCombatant + public class LineCombatant : IDisposable { public const uint LogFileLineID = 261; private ILogger logger; @@ -169,6 +169,7 @@ private class CombatantStateInfo private Func logWriter; private CancellationTokenSource cancellationToken; + private bool _disposed; public LineCombatant(TinyIoCContainer container) { @@ -218,10 +219,7 @@ public LineCombatant(TinyIoCContainer container) ~LineCombatant() { - if (cancellationToken != null) - { - cancellationToken.Cancel(); - } + Dispose(false); } private void PollCombatants() @@ -475,5 +473,24 @@ private enum CombatantMemoryChangeType Remove, Change, } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + cancellationToken?.Cancel(); + cancellationToken?.Dispose(); + } + _disposed = true; + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } } diff --git a/OverlayPlugin.Core/MemoryProcessors/Enmity/EnmityMemoryManager.cs b/OverlayPlugin.Core/MemoryProcessors/Enmity/EnmityMemoryManager.cs index 8218315c4..ea8ecf8c0 100644 --- a/OverlayPlugin.Core/MemoryProcessors/Enmity/EnmityMemoryManager.cs +++ b/OverlayPlugin.Core/MemoryProcessors/Enmity/EnmityMemoryManager.cs @@ -51,7 +51,7 @@ public bool IsValid() return true; } - Version IVersionedMemory.GetVersion() + public Version GetVersion() { if (!IsValid()) return null; diff --git a/OverlayPlugin.Core/MemoryProcessors/EnmityHud/EnmityHudMemoryManager.cs b/OverlayPlugin.Core/MemoryProcessors/EnmityHud/EnmityHudMemoryManager.cs index 6ab58b15d..2a73a5be4 100644 --- a/OverlayPlugin.Core/MemoryProcessors/EnmityHud/EnmityHudMemoryManager.cs +++ b/OverlayPlugin.Core/MemoryProcessors/EnmityHud/EnmityHudMemoryManager.cs @@ -53,7 +53,7 @@ public bool IsValid() return true; } - Version IVersionedMemory.GetVersion() + public Version GetVersion() { if (!IsValid()) return null; diff --git a/OverlayPlugin.Core/MemoryProcessors/InCombat/InCombatMemoryManager.cs b/OverlayPlugin.Core/MemoryProcessors/InCombat/InCombatMemoryManager.cs index 8e1b4af49..5b3378aa4 100644 --- a/OverlayPlugin.Core/MemoryProcessors/InCombat/InCombatMemoryManager.cs +++ b/OverlayPlugin.Core/MemoryProcessors/InCombat/InCombatMemoryManager.cs @@ -51,7 +51,7 @@ public bool IsValid() return true; } - Version IVersionedMemory.GetVersion() + public Version GetVersion() { if (!IsValid()) return null; diff --git a/OverlayPlugin.Core/MemoryProcessors/Party/PartyMemory.cs b/OverlayPlugin.Core/MemoryProcessors/Party/PartyMemory.cs index db2a74d47..866cca8b8 100644 --- a/OverlayPlugin.Core/MemoryProcessors/Party/PartyMemory.cs +++ b/OverlayPlugin.Core/MemoryProcessors/Party/PartyMemory.cs @@ -49,7 +49,6 @@ public abstract class PartyMemory protected IntPtr partyInstanceAddress = IntPtr.Zero; - private long partySingletonAddressInstance; protected Func GetGroupManagerAddress; diff --git a/OverlayPlugin.Core/MemoryProcessors/Party/PartyMemoryManager.cs b/OverlayPlugin.Core/MemoryProcessors/Party/PartyMemoryManager.cs index 181fc5740..2f354a5c4 100644 --- a/OverlayPlugin.Core/MemoryProcessors/Party/PartyMemoryManager.cs +++ b/OverlayPlugin.Core/MemoryProcessors/Party/PartyMemoryManager.cs @@ -53,7 +53,7 @@ public bool IsValid() return true; } - Version IVersionedMemory.GetVersion() + public Version GetVersion() { if (!IsValid()) return null; diff --git a/OverlayPlugin.Core/MemoryProcessors/Target/TargetMemoryManager.cs b/OverlayPlugin.Core/MemoryProcessors/Target/TargetMemoryManager.cs index c65b9c852..02269f01a 100644 --- a/OverlayPlugin.Core/MemoryProcessors/Target/TargetMemoryManager.cs +++ b/OverlayPlugin.Core/MemoryProcessors/Target/TargetMemoryManager.cs @@ -57,7 +57,7 @@ public bool IsValid() return true; } - Version IVersionedMemory.GetVersion() + public Version GetVersion() { if (!IsValid()) return null; diff --git a/OverlayPlugin.Core/NativeMethods.cs b/OverlayPlugin.Core/NativeMethods.cs index 019375ce8..d65ed14aa 100644 --- a/OverlayPlugin.Core/NativeMethods.cs +++ b/OverlayPlugin.Core/NativeMethods.cs @@ -302,6 +302,15 @@ uint uFlags // ウィンドウ位置のオプション public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, IntPtr nSize, ref IntPtr lpNumberOfBytesRead); #pragma warning restore 0649 + + // Registers a hot key with Windows. + [DllImport("user32.dll")] + public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); + // Unregisters the hot key with Windows. + [DllImport("user32.dll")] + public static extern bool UnregisterHotKey(IntPtr hWnd, int id); + + public static int WM_HOTKEY = 0x0312; } [Flags] diff --git a/OverlayPlugin.Core/NetworkProcessors/LineAbilityExtra.cs b/OverlayPlugin.Core/NetworkProcessors/LineAbilityExtra.cs index 6a0a30f34..4c2fab919 100644 --- a/OverlayPlugin.Core/NetworkProcessors/LineAbilityExtra.cs +++ b/OverlayPlugin.Core/NetworkProcessors/LineAbilityExtra.cs @@ -311,7 +311,7 @@ private unsafe void MessageReceived(string id, long epoch, byte[] message) { logWriter(string.Format(CultureInfo.InvariantCulture, "{0:X8}|{1:X4}|{2:X8}|{3}||||", - sourceId, globalEffectCounter, (int)LineSubType.ERROR), serverTime); + sourceId, abilityId, globalEffectCounter, (int)LineSubType.ERROR), serverTime); return; } diff --git a/OverlayPlugin.Core/NetworkProcessors/LineRSV.cs b/OverlayPlugin.Core/NetworkProcessors/LineRSV.cs index 9e9185ac4..10b1016de 100644 --- a/OverlayPlugin.Core/NetworkProcessors/LineRSV.cs +++ b/OverlayPlugin.Core/NetworkProcessors/LineRSV.cs @@ -5,7 +5,6 @@ namespace RainbowMage.OverlayPlugin.NetworkProcessors { - [Serializable] [StructLayout(LayoutKind.Explicit, Size = structSize, Pack = 1)] internal unsafe struct RSV_v62 { diff --git a/OverlayPlugin.Core/OverlayBase.cs b/OverlayPlugin.Core/OverlayBase.cs index 2fd27e68b..acd297481 100644 --- a/OverlayPlugin.Core/OverlayBase.cs +++ b/OverlayPlugin.Core/OverlayBase.cs @@ -43,8 +43,13 @@ public abstract class OverlayBase : IOverlay, IEventReceiver, IApiBase /// プラグインの設定を取得します。 /// public IPluginConfig PluginConfig { get; private set; } + + // TODO: Disable CA1033 due to potentially requiring a breaking change? + // See https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1033 +#pragma warning disable CA1033 IOverlayConfig IOverlay.Config { get => Config; set => Config = (TConfig)value; } IntPtr IOverlay.Handle { get => Overlay == null ? IntPtr.Zero : Overlay.Handle; } +#pragma warning restore CA1033 public bool Visible { @@ -86,9 +91,12 @@ protected OverlayBase(TConfig config, string name, TinyIoCContainer container) } } + // TODO: Disable CA2214 for now, due to potential breaking change required to fix it +#pragma warning disable CA2214 // Do not call overridable methods in constructors InitializeOverlay(); InitializeTimer(); InitializeConfigHandlers(); +#pragma warning restore CA2214 // Do not call overridable methods in constructors UpdateHotKey(); } @@ -304,32 +312,46 @@ protected virtual void InitializeConfigHandlers() /// /// オーバーレイのインスタンスを破棄します。 /// - public virtual void Dispose() + public void Dispose() { - try + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + private bool _disposed = false; + + protected virtual void Dispose(bool disposing) + { + if (_disposed) { - dispatcher.UnsubscribeAll(this); + return; + } - if (this.timer != null) - { - this.timer.Stop(); - } - if (this.Overlay != null) + if (disposing) + { + try { - this.Overlay.Close(); - this.Overlay.Dispose(); - } + dispatcher.UnsubscribeAll(this); + + timer?.Stop(); + timer?.Dispose(); + + Overlay?.Close(); + Overlay?.Dispose(); - var hook = container.Resolve(); - foreach (var cb in hotKeyCallbacks) + var hook = container.Resolve(); + foreach (var cb in hotKeyCallbacks) + { + hook.UnregisterHotKey(cb); + } + } + catch (Exception ex) { - hook.UnregisterHotKey(cb); + Log(LogLevel.Error, "Dispose: {0}", ex); } } - catch (Exception ex) - { - Log(LogLevel.Error, "Dispose: {0}", ex); - } + + _disposed = true; } public virtual void Navigate(string url) diff --git a/OverlayPlugin.Core/OverlayHider.cs b/OverlayPlugin.Core/OverlayHider.cs index 9709beb82..56485154b 100644 --- a/OverlayPlugin.Core/OverlayHider.cs +++ b/OverlayPlugin.Core/OverlayHider.cs @@ -12,17 +12,22 @@ namespace RainbowMage.OverlayPlugin { - class OverlayHider + class OverlayHider : IDisposable { private bool gameActive = true; private bool inCutscene = false; private bool inCombat = false; private IPluginConfig config; private ILogger logger; + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Usage", + "CA2213:Disposable fields should be disposed", + Justification = "main is disposed of by TinyIoCContainer")] private PluginMain main; private FFXIVRepository repository; private int ffxivPid = -1; private Timer focusTimer; + private bool _disposed; public OverlayHider(TinyIoCContainer container) { @@ -154,5 +159,25 @@ private void CombatStatusChanged(object sender, InCombatArgs args) UpdateOverlays(); } } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + focusTimer?.Stop(); + focusTimer?.Dispose(); + focusTimer = null; + } + _disposed = true; + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } } diff --git a/OverlayPlugin.Core/OverlayZCorrector.cs b/OverlayPlugin.Core/OverlayZCorrector.cs index 6b7ee68f2..a8a93f0ef 100644 --- a/OverlayPlugin.Core/OverlayZCorrector.cs +++ b/OverlayPlugin.Core/OverlayZCorrector.cs @@ -8,13 +8,18 @@ namespace RainbowMage.OverlayPlugin { - class OverlayZCorrector + class OverlayZCorrector : IDisposable { + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Usage", + "CA2213:Disposable fields should be disposed", + Justification = "main is disposed of by TinyIoCContainer")] private PluginMain main; private ILogger logger; private Timer timer; private FFXIVRepository repository; private Process xivProc; + private bool _disposed; public OverlayZCorrector(TinyIoCContainer container) { @@ -73,5 +78,25 @@ private void EnsureOverlaysAreOverGame(object _) // logger.Log(LogLevel.Debug, $"ZReorder: Took {watch.Elapsed.TotalSeconds}s."); } + + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + timer?.Dispose(); + timer = null; + } + _disposed = true; + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } } diff --git a/OverlayPlugin.Core/Overlays/LabelOverlayConfigPanel.cs b/OverlayPlugin.Core/Overlays/LabelOverlayConfigPanel.cs index 6e2a21003..3b7823a92 100644 --- a/OverlayPlugin.Core/Overlays/LabelOverlayConfigPanel.cs +++ b/OverlayPlugin.Core/Overlays/LabelOverlayConfigPanel.cs @@ -14,6 +14,11 @@ public partial class LabelOverlayConfigPanel : UserControl { private LabelOverlayConfig config; private LabelOverlay overlay; + + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Usage", + "CA2213:Disposable fields should be disposed", + Justification = "keyboardHook is disposed of by TinyIoCContainer")] private readonly KeyboardHook keyboardHook; public LabelOverlayConfigPanel(TinyIoCContainer container, LabelOverlay overlay) diff --git a/OverlayPlugin.Core/Overlays/MiniParseConfigPanel.cs b/OverlayPlugin.Core/Overlays/MiniParseConfigPanel.cs index 99de1889d..1e3185106 100644 --- a/OverlayPlugin.Core/Overlays/MiniParseConfigPanel.cs +++ b/OverlayPlugin.Core/Overlays/MiniParseConfigPanel.cs @@ -14,6 +14,10 @@ public partial class MiniParseConfigPanel : UserControl { private MiniParseOverlayConfig config; private MiniParseOverlay overlay; + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Usage", + "CA2213:Disposable fields should be disposed", + Justification = "keyboardHook is disposed of by TinyIoCContainer")] private readonly KeyboardHook keyboardHook; static readonly List> hotkeyTypeDict = new List>() diff --git a/OverlayPlugin.Core/Overlays/MiniParseOverlay.cs b/OverlayPlugin.Core/Overlays/MiniParseOverlay.cs index f9d842310..ca14da9b9 100644 --- a/OverlayPlugin.Core/Overlays/MiniParseOverlay.cs +++ b/OverlayPlugin.Core/Overlays/MiniParseOverlay.cs @@ -97,10 +97,13 @@ public MiniParseOverlay(MiniParseOverlayConfig config, string name, TinyIoCConta Overlay.Renderer.BrowserConsoleLog += Renderer_BrowserConsoleLog; } - public override void Dispose() + protected override void Dispose(bool disposing) { - base.Dispose(); - previewTimer?.Dispose(); + if (disposing) + { + previewTimer?.Dispose(); + } + base.Dispose(disposing); } public override Control CreateConfigControl() diff --git a/OverlayPlugin.Core/PluginConfig.cs b/OverlayPlugin.Core/PluginConfig.cs index 596527929..cae87f963 100644 --- a/OverlayPlugin.Core/PluginConfig.cs +++ b/OverlayPlugin.Core/PluginConfig.cs @@ -19,6 +19,8 @@ namespace RainbowMage.OverlayPlugin public class PluginConfig : IPluginConfig { const string BACKUP_SUFFIX = ".backup"; + [NonSerialized] + [JsonIgnore] private TinyIoCContainer _container; [JsonIgnore] @@ -183,6 +185,9 @@ public string Token } } + // TODO: This isn't being serialized to JSON (and JToken isn't serializable directly) + // so this isn't being cached locally anymore? + [NonSerialized] private JToken _cachedOpcodeFile = new JObject(); public JToken CachedOpcodeFile { @@ -352,7 +357,7 @@ public PluginConfig(string configPath, TinyIoCContainer container) } else { - throw ex; + throw; } } } diff --git a/OverlayPlugin.Core/PluginMain.cs b/OverlayPlugin.Core/PluginMain.cs index ef5c425c1..664794dc3 100644 --- a/OverlayPlugin.Core/PluginMain.cs +++ b/OverlayPlugin.Core/PluginMain.cs @@ -27,7 +27,7 @@ namespace RainbowMage.OverlayPlugin { - public class PluginMain + public class PluginMain : IDisposable { private TinyIoCContainer _container; private ILogger _logger; @@ -41,6 +41,7 @@ public class PluginMain Timer initTimer; Timer configSaveTimer; private bool clearCache = false; + private bool _disposed; internal PluginConfig Config { get; private set; } internal List Overlays { get; private set; } @@ -587,5 +588,29 @@ private static string GetConfigPath(bool xml = false) return path; } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + controlPanel?.Dispose(); + wsTabPage?.Dispose(); + wsConfigPanel?.Dispose(); + initTimer?.Stop(); + initTimer?.Dispose(); + configSaveTimer?.Stop(); + configSaveTimer?.Dispose(); + } + _disposed = true; + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } } diff --git a/OverlayPlugin.Core/WebSocket/WSServer.cs b/OverlayPlugin.Core/WebSocket/WSServer.cs index 413fdc96f..35256c141 100644 --- a/OverlayPlugin.Core/WebSocket/WSServer.cs +++ b/OverlayPlugin.Core/WebSocket/WSServer.cs @@ -15,17 +15,17 @@ namespace RainbowMage.OverlayPlugin { - public class WSServer + public class WSServer : IDisposable { TinyIoCContainer _container; ILogger _logger; WebSocketServer _server; IPluginConfig _cfg; - PluginMain _plugin; List _connections = new List(); bool _failed = false; public EventHandler OnStateChanged; + private bool _disposed; interface IWSConnection : IEventReceiver { @@ -77,7 +77,6 @@ public WSServer(TinyIoCContainer container) _container = container; _logger = container.Resolve(); _cfg = container.Resolve(); - _plugin = container.Resolve(); } public void Start() @@ -521,5 +520,23 @@ public StateChangedArgs(bool Running, bool Failed) this.Failed = Failed; } } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _server?.Dispose(); + } + _disposed = true; + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } } diff --git a/OverlayPlugin.Updater/HttpClientWrapper.cs b/OverlayPlugin.Updater/HttpClientWrapper.cs index cfc7b7725..4592e83cb 100644 --- a/OverlayPlugin.Updater/HttpClientWrapper.cs +++ b/OverlayPlugin.Updater/HttpClientWrapper.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net.Http; using System.Reflection; +using System.Runtime.Serialization; using System.Threading; namespace RainbowMage.OverlayPlugin.Updater @@ -144,5 +145,7 @@ public HttpClientException(bool retry, string message, Exception innerException) { this.Retry = retry; } + + protected HttpClientException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } } } diff --git a/OverlayPlugin.Updater/Installer.cs b/OverlayPlugin.Updater/Installer.cs index 24a91b8d1..8ce6fbe38 100644 --- a/OverlayPlugin.Updater/Installer.cs +++ b/OverlayPlugin.Updater/Installer.cs @@ -13,7 +13,7 @@ namespace RainbowMage.OverlayPlugin.Updater { - public class Installer + public class Installer : IDisposable { const uint FILE_OVERWRITE_RETRIES = 10; const int FILE_OVERWRITE_WAIT = 300; @@ -22,6 +22,7 @@ public class Installer public string TempDir { get; private set; } string _destDir = null; CancellationToken _token = CancellationToken.None; + private bool _disposed; public ProgressDisplay Display => _display; @@ -483,5 +484,24 @@ public void Cleanup() } } } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _display?.Dispose(); + } + + _disposed = true; + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } } diff --git a/OverlayPlugin/PluginLoader.cs b/OverlayPlugin/PluginLoader.cs index e73916600..12af60c94 100644 --- a/OverlayPlugin/PluginLoader.cs +++ b/OverlayPlugin/PluginLoader.cs @@ -14,7 +14,7 @@ namespace RainbowMage.OverlayPlugin { - public class PluginLoader : IActPluginV1 + public class PluginLoader : IActPluginV1, IDisposable { PluginMain pluginMain; Logger logger; @@ -23,6 +23,7 @@ public class PluginLoader : IActPluginV1 TabPage pluginScreenSpace; Label pluginStatusText; bool initFailed = false; + private bool _disposed; public TinyIoCContainer Container { get; private set; } @@ -163,5 +164,24 @@ private string GetCefPath() { return Path.Combine(ActGlobals.oFormActMain.AppDataFolder.FullName, "OverlayPluginCef", Environment.Is64BitProcess ? "x64" : "x86"); } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + pluginMain.Dispose(); + Container.Dispose(); + } + _disposed = true; + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } } diff --git a/tools/StripFFXIVClientStructs/StripFFXIVClientStructs/StripFFXIVClientStructs.cs b/tools/StripFFXIVClientStructs/StripFFXIVClientStructs/StripFFXIVClientStructs.cs index 80b816a29..4ffff36c0 100644 --- a/tools/StripFFXIVClientStructs/StripFFXIVClientStructs/StripFFXIVClientStructs.cs +++ b/tools/StripFFXIVClientStructs/StripFFXIVClientStructs/StripFFXIVClientStructs.cs @@ -74,7 +74,6 @@ class StripFFXIVClientStructs private static string[] GlobalUsings = new string[] { "System.Runtime.InteropServices", "FFXIVClientStructs.{0}.STD", - "FFXIVClientStructs.{0}.FFXIV.Client.Graphics", }; // Entries in this dictionary will have their types remapped to concrete types. @@ -99,11 +98,6 @@ public unsafe struct StdSet{0} {{ public Node* Head; public ulong Count; - public ref struct Enumerator - {{ - private readonly Node* _head; - private Node* _current; - }} [StructLayout(LayoutKind.Sequential)] public struct Node @@ -138,11 +132,6 @@ public unsafe struct StdMap{0}{2} {{ public Node* Head; public ulong Count; - public ref struct Enumerator - {{ - private readonly Node* _head; - private Node* _current; - }} [StructLayout(LayoutKind.Sequential)] public struct Node diff --git a/tools/VSBuildDeps/VSBuildDeps.csproj b/tools/VSBuildDeps/VSBuildDeps.csproj index 37f9d1964..0b8126d00 100644 --- a/tools/VSBuildDeps/VSBuildDeps.csproj +++ b/tools/VSBuildDeps/VSBuildDeps.csproj @@ -1,4 +1,4 @@ - + {6423EE85-3E94-4290-8699-BF403245501F} OverlayPlugin.VSBuildDeps @@ -23,6 +23,9 @@ - + + false + false + \ No newline at end of file From 9b4352dbf0b6a3f27ba508062742de6b0c42485e Mon Sep 17 00:00:00 2001 From: valarnin Date: Mon, 19 Feb 2024 23:40:22 -0500 Subject: [PATCH 07/11] Fix #254 (again) (#308) --- OverlayPlugin.Core/Controls/ControlPanel.cs | 28 ++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/OverlayPlugin.Core/Controls/ControlPanel.cs b/OverlayPlugin.Core/Controls/ControlPanel.cs index 4419f488a..a5d492905 100644 --- a/OverlayPlugin.Core/Controls/ControlPanel.cs +++ b/OverlayPlugin.Core/Controls/ControlPanel.cs @@ -110,22 +110,22 @@ protected override void Dispose(bool disposing) private void AddLogEntry(LogEntry entry) { - var msg = $"[{entry.Time}] {entry.Level}: {entry.Message}" + Environment.NewLine; - - if (!logConnected) - { - // Remove the error message about the log not being connected since it is now. - logConnected = true; - logBox.Text = ""; - } - else if (logBox.TextLength > 200 * 1024) - { - logBox.Text = "============ LOG TRUNCATED ==============\nThe log was truncated to reduce memory usage.\n=========================================\n" + msg; - return; - } - Action appendText = () => { + var msg = $"[{entry.Time}] {entry.Level}: {entry.Message}" + Environment.NewLine; + + if (!logConnected) + { + // Remove the error message about the log not being connected since it is now. + logConnected = true; + logBox.Text = ""; + } + else if (logBox.TextLength > 200 * 1024) + { + logBox.Text = "============ LOG TRUNCATED ==============\nThe log was truncated to reduce memory usage.\n=========================================\n" + msg; + return; + } + if (checkBoxFollowLog.Checked) { logBox.AppendText(msg); From e540bfd0dd2df3189854b78af2d6ad20d45ad9db Mon Sep 17 00:00:00 2001 From: valarnin Date: Mon, 19 Feb 2024 23:45:23 -0500 Subject: [PATCH 08/11] Fix release workflow (#309) * Copying some logic from cactbot to fix release workflow --- .github/workflows/release.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 164127d83..40a613a77 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,7 @@ jobs: runs-on: windows-latest outputs: version: ${{ steps.determine_version.outputs.version }} + do_release: ${{ steps.check_tag.outputs.do_release }} steps: - uses: actions/checkout@v2 with: @@ -24,19 +25,26 @@ jobs: [xml]$csprojcontents = Get-Content -Path "Directory.Build.props"; $version = $csprojcontents.Project.PropertyGroup.AssemblyVersion; $version = ($version | Out-String).Trim() - echo "::set-output name=version::$version" + echo "version=$version" >> $env:GITHUB_OUTPUT - name: Check Tag Exists + id: check_tag shell: bash run: | if [ $(git tag -l "v${{ steps.determine_version.outputs.version }}") ]; then - echo "Error: Existing git tag found. Exiting..." - exit 1 + echo "Existing git tag found. Exiting..." + echo "do_release=false" >> $GITHUB_OUTPUT + else + echo "Version bump detected - ${{ steps.determine_version.outputs.version }}" + echo "do_release=true" >> $GITHUB_OUTPUT fi create_release: needs: [validate_tag] runs-on: windows-latest + if: | + github.repository == 'OverlayPlugin/OverlayPlugin' && + needs.validate_tag.outputs.do_release == 'true' steps: - uses: actions/checkout@v2 From 813dafb7ad4436e9ef4428cc3714369cf72a8123 Mon Sep 17 00:00:00 2001 From: Wexx <86693821+wexxlee@users.noreply.github.com> Date: Mon, 19 Feb 2024 23:10:43 -0800 Subject: [PATCH 09/11] Update base README.md (#310) * Update README --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fc527047f..c2641320d 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,13 @@ Plugin to show customizable overlays for Advanced Combat Tracker. -## Download +## Download / Installation -You can download release and pre-release archives from the [release page][releases]. +To install OverlayPlugin, please use the `Get Plugins` button in ACT. +For more detailed instructions, please consult the [FFXIV ACT Setup Guide](https://overlayplugin.github.io/docs/setup/). + +If you want to download current or prior versions of OverlayPlugin, +binaries and source code are available on the [release page][releases]. ## System requirements @@ -23,13 +27,13 @@ We also have [instructions for users who want to display their overlay in OBS](h ## Overlays -* [A list of popular overlays](https://gist.github.com/ngld/e2217563bbbe1750c0917217f136687d#overlays) +* [A list of popular overlays](https://overlayplugin.github.io/docs/faq/#overlays) * [Documentation for creating your own](https://overlayplugin.github.io/OverlayPlugin/devs/) ## Troubleshooting -[Check the ACT Discord's FAQ for troubleshooting steps](https://gist.github.com/ngld/e2217563bbbe1750c0917217f136687d) +[Check the ACT Discord's FAQ for troubleshooting steps](https://overlayplugin.github.io/docs/faq/) ## How to build From c24eda1957955776c7c69355082dd31a171c452e Mon Sep 17 00:00:00 2001 From: valarnin Date: Fri, 23 Feb 2024 16:03:33 -0500 Subject: [PATCH 10/11] Add Job Gauge event (#300) --- .../EventSources/FFXIVRequiredEventSource.cs | 66 +++ .../MemoryProcessors/JobGauge/JobGauge.cs | 120 +++++ .../MemoryProcessors/JobGauge/JobGauge655.cs | 73 +++ .../JobGauge/JobGauge655ClientStructs.cs | 429 ++++++++++++++++++ .../JobGauge/JobGaugeMemoryManager.cs | 426 +++++++++++++++++ OverlayPlugin.Core/PluginMain.cs | 2 + 6 files changed, 1116 insertions(+) create mode 100644 OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGauge.cs create mode 100644 OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGauge655.cs create mode 100644 OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGauge655ClientStructs.cs create mode 100644 OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGaugeMemoryManager.cs diff --git a/OverlayPlugin.Core/EventSources/FFXIVRequiredEventSource.cs b/OverlayPlugin.Core/EventSources/FFXIVRequiredEventSource.cs index 3511718db..6af88b68f 100644 --- a/OverlayPlugin.Core/EventSources/FFXIVRequiredEventSource.cs +++ b/OverlayPlugin.Core/EventSources/FFXIVRequiredEventSource.cs @@ -7,12 +7,14 @@ // For some reason this using is required by the github build? using System.Reflection; using System.Runtime.CompilerServices; +using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Advanced_Combat_Tracker; using FFXIV_ACT_Plugin.Common; using Newtonsoft.Json.Linq; using RainbowMage.OverlayPlugin.MemoryProcessors.Combatant; +using RainbowMage.OverlayPlugin.MemoryProcessors.JobGauge; using RainbowMage.OverlayPlugin.MemoryProcessors.Party; using RainbowMage.OverlayPlugin.NetworkProcessors; using PluginCombatant = FFXIV_ACT_Plugin.Common.Models.Combatant; @@ -35,10 +37,17 @@ partial class FFXIVRequiredEventSource : EventSourceBase private const string OnlineStatusChangedEvent = "OnlineStatusChanged"; private const string PartyChangedEvent = "PartyChanged"; + private const string JobGaugeChangedEvent = "JobGaugeChanged"; private FFXIVRepository repository; private ICombatantMemory combatantMemory; private IPartyMemory partyMemory; + private IJobGaugeMemory jobGaugeMemory; + + private CancellationTokenSource cancellationToken; + + // In milliseconds + private const int PollingRate = 50; // Event Source @@ -57,10 +66,16 @@ public FFXIVRequiredEventSource(TinyIoCContainer container) : base(container) Log(LogLevel.Error, "Could not construct FFXIVRequiredEventSource: Missing partyMemory"); } + if (!container.TryResolve(out jobGaugeMemory)) + { + Log(LogLevel.Error, "Could not construct FFXIVRequiredEventSource: Missing jobGaugeMemory"); + } + // These events need to deliver cached values to new subscribers. RegisterCachedEventTypes(new List { OnlineStatusChangedEvent, PartyChangedEvent, + JobGaugeChangedEvent, }); RegisterEventHandler("getLanguage", (msg) => @@ -134,6 +149,10 @@ public FFXIVRequiredEventSource(TinyIoCContainer container) : base(container) DispatchAndCacheEvent(obj); }; + + cancellationToken = new CancellationTokenSource(); + + Task.Run(PollJobGauge, cancellationToken.Token); } } @@ -402,6 +421,53 @@ public override void Start() this.timer.Change(0, updateInterval); } + public override void Stop() + { + base.Stop(); + if (cancellationToken != null) + { + cancellationToken.Cancel(); + } + } + private void PollJobGauge() + { + IJobGauge lastJobGauge = null; + while (!cancellationToken.IsCancellationRequested) + { + var now = DateTime.Now; + + if (HasSubscriber(JobGaugeChangedEvent)) + { + var jobGauge = jobGaugeMemory.GetJobGauge(); + + if (jobGauge != null) + { + if (!jobGauge.Equals(lastJobGauge)) + { + lastJobGauge = jobGauge; + var obj = JObject.FromObject(jobGauge); + obj["type"] = JobGaugeChangedEvent; + + DispatchAndCacheEvent(obj); + } + } + } + + // Wait for next poll + var delay = PollingRate - (int)Math.Ceiling((DateTime.Now - now).TotalMilliseconds); + if (delay > 0) + { + Thread.Sleep(delay); + } + else + { + // If we're lagging enough to not have a sleep duration, delay by PollingRate to reduce lag + Thread.Sleep(PollingRate); + } + } + } + + protected override void Update() { if (partyMemory != null) diff --git a/OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGauge.cs b/OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGauge.cs new file mode 100644 index 000000000..faf12c303 --- /dev/null +++ b/OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGauge.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; + +namespace RainbowMage.OverlayPlugin.MemoryProcessors.JobGauge +{ + public abstract class JobGaugeMemory : IJobGaugeMemory + { + public struct JobGaugeImpl : IJobGauge + { + [JsonIgnore] + public JobGaugeJob job; + [JsonIgnore] + public IBaseJobGauge data; + [JsonIgnore] + public byte[] rawData; + [JsonIgnore] + public object baseObject; + + public JobGaugeJob Job => job; + public IBaseJobGauge Data => data; + public int[] RawData => rawData.Select((b) => (int)b).ToArray(); + public object BaseObject => baseObject; + + public bool Equals(IJobGauge obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + + var objCast = obj; + + if (objCast.Job != Job) return false; + var objRawData = objCast.RawData; + var rawData = RawData; + if (objRawData.Length != rawData.Length) return false; + for (int i = 0; i < objRawData.Length; ++i) + { + if (objRawData[i] != rawData[i]) return false; + } + return true; + } + } + + protected FFXIVMemory memory; + private ILogger logger; + + protected IntPtr jobGaugeAddress = IntPtr.Zero; + + private string jobDataSignature; + private int jobDataSignatureOffset; + + public JobGaugeMemory(TinyIoCContainer container, string jobDataSignature, int jobDataSignatureOffset) + { + this.jobDataSignature = jobDataSignature; + this.jobDataSignatureOffset = jobDataSignatureOffset; + logger = container.Resolve(); + memory = container.Resolve(); + } + + private void ResetPointers() + { + jobGaugeAddress = IntPtr.Zero; + } + + private bool HasValidPointers() + { + if (jobGaugeAddress == IntPtr.Zero) + return false; + return true; + } + + public bool IsValid() + { + if (!memory.IsValid()) + return false; + + if (!HasValidPointers()) + return false; + + return true; + } + + public void ScanPointers() + { + ResetPointers(); + if (!memory.IsValid()) + return; + + List fail = new List(); + + List list = memory.SigScan(jobDataSignature, jobDataSignatureOffset, true); + if (list != null && list.Count > 0) + { + jobGaugeAddress = list[0]; + } + else + { + jobGaugeAddress = IntPtr.Zero; + fail.Add(nameof(jobGaugeAddress)); + } + + logger.Log(LogLevel.Debug, "jobGaugeAddress: 0x{0:X}", jobGaugeAddress.ToInt64()); + + if (fail.Count == 0) + { + logger.Log(LogLevel.Info, $"Found job Gauge memory via {GetType().Name}."); + return; + } + + logger.Log(LogLevel.Error, $"Failed to find job Gauge memory via {GetType().Name}: {string.Join(", ", fail)}."); + return; + } + + public abstract Version GetVersion(); + public abstract IJobGauge GetJobGauge(); + } +} diff --git a/OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGauge655.cs b/OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGauge655.cs new file mode 100644 index 000000000..a4073765e --- /dev/null +++ b/OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGauge655.cs @@ -0,0 +1,73 @@ +using System; +using System.Runtime.InteropServices; + +namespace RainbowMage.OverlayPlugin.MemoryProcessors.JobGauge +{ + interface IJobGaugeMemory655 : IJobGaugeMemory { } + + partial class JobGaugeMemory655 : JobGaugeMemory, IJobGaugeMemory655 + { + private static string jobDataSignature = "488B3D????????33ED"; + private static int jobDataSignatureOffset = -6; + + public JobGaugeMemory655(TinyIoCContainer container) + : base(container, jobDataSignature, jobDataSignatureOffset) + { } + + public override Version GetVersion() + { + return new Version(6, 5, 5); + } + + public override IJobGauge GetJobGauge() + { + if (!IsValid()) return null; + + var jobGaugeManager = GetJobGaugeManager(); + + var ret = new JobGaugeImpl(); + + ret.baseObject = jobGaugeManager; + ret.job = (JobGaugeJob)jobGaugeManager.ClassJobID; + ret.rawData = jobGaugeManager.GetRawGaugeData; + + switch (ret.job) + { + case JobGaugeJob.WHM: ret.data = jobGaugeManager.WhiteMage; break; + case JobGaugeJob.SCH: ret.data = jobGaugeManager.Scholar; break; + case JobGaugeJob.AST: ret.data = jobGaugeManager.Astrologian; break; + case JobGaugeJob.SGE: ret.data = jobGaugeManager.Sage; break; + + case JobGaugeJob.BRD: ret.data = jobGaugeManager.Bard; break; + case JobGaugeJob.MCH: ret.data = jobGaugeManager.Machinist; break; + case JobGaugeJob.DNC: ret.data = jobGaugeManager.Dancer; break; + + case JobGaugeJob.BLM: ret.data = jobGaugeManager.BlackMage; break; + case JobGaugeJob.SMN: ret.data = jobGaugeManager.Summoner; break; + case JobGaugeJob.RDM: ret.data = jobGaugeManager.RedMage; break; + + case JobGaugeJob.MNK: ret.data = jobGaugeManager.Monk; break; + case JobGaugeJob.DRG: ret.data = jobGaugeManager.Dragoon; break; + case JobGaugeJob.NIN: ret.data = jobGaugeManager.Ninja; break; + case JobGaugeJob.SAM: ret.data = jobGaugeManager.Samurai; break; + case JobGaugeJob.RPR: ret.data = jobGaugeManager.Reaper; break; + + case JobGaugeJob.DRK: ret.data = jobGaugeManager.DarkKnight; break; + case JobGaugeJob.PLD: ret.data = jobGaugeManager.Paladin; break; + case JobGaugeJob.WAR: ret.data = jobGaugeManager.Warrior; break; + case JobGaugeJob.GNB: ret.data = jobGaugeManager.Gunbreaker; break; + } + + return ret; + } + + private unsafe JobGaugeManager GetJobGaugeManager() + { + var rawData = memory.GetByteArray(jobGaugeAddress, sizeof(JobGaugeManager)); + fixed (byte* buffer = rawData) + { + return (JobGaugeManager)Marshal.PtrToStructure(new IntPtr(buffer), typeof(JobGaugeManager)); + } + } + } +} diff --git a/OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGauge655ClientStructs.cs b/OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGauge655ClientStructs.cs new file mode 100644 index 000000000..68995473c --- /dev/null +++ b/OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGauge655ClientStructs.cs @@ -0,0 +1,429 @@ +using System.Runtime.InteropServices; +using Newtonsoft.Json; + +namespace RainbowMage.OverlayPlugin.MemoryProcessors.JobGauge +{ + partial class JobGaugeMemory655 : JobGaugeMemory, IJobGaugeMemory655 + { + // Due to lack of multi-version support in FFXIVClientStructs, we need to duplicate these structures here per-version + // We use FFXIVClientStructs versions of the structs because they have more required details than FFXIV_ACT_Plugin's struct definitions + #region FFXIVClientStructs structs + [StructLayout(LayoutKind.Explicit, Size = 0x60)] + public unsafe partial struct JobGaugeManager + { + [JsonIgnore] + [FieldOffset(0x00)] public JobGauge* CurrentGauge; + + [FieldOffset(0x08)] public JobGauge EmptyGauge; + + [FieldOffset(0x08)] public WhiteMageGauge WhiteMage; + [FieldOffset(0x08)] public ScholarGauge Scholar; + [FieldOffset(0x08)] public AstrologianGauge Astrologian; + [FieldOffset(0x08)] public SageGauge Sage; + + [FieldOffset(0x08)] public BardGauge Bard; + [FieldOffset(0x08)] public MachinistGauge Machinist; + [FieldOffset(0x08)] public DancerGauge Dancer; + + [FieldOffset(0x08)] public BlackMageGauge BlackMage; + [FieldOffset(0x08)] public SummonerGauge Summoner; + [FieldOffset(0x08)] public RedMageGauge RedMage; + + [FieldOffset(0x08)] public MonkGauge Monk; + [FieldOffset(0x08)] public DragoonGauge Dragoon; + [FieldOffset(0x08)] public NinjaGauge Ninja; + [FieldOffset(0x08)] public SamuraiGauge Samurai; + [FieldOffset(0x08)] public ReaperGauge Reaper; + + [FieldOffset(0x08)] public DarkKnightGauge DarkKnight; + [FieldOffset(0x08)] public PaladinGauge Paladin; + [FieldOffset(0x08)] public WarriorGauge Warrior; + [FieldOffset(0x08)] public GunbreakerGauge Gunbreaker; + + [JsonIgnore] + [FieldOffset(0x10)] public fixed byte RawGaugeData[8]; + + [FieldOffset(0x58)] public byte ClassJobID; + + public byte[] GetRawGaugeData => new byte[] { + RawGaugeData[0], RawGaugeData[1], RawGaugeData[2], RawGaugeData[3], + RawGaugeData[4], RawGaugeData[5], RawGaugeData[6], RawGaugeData[7] + }; + } + [StructLayout(LayoutKind.Explicit, Size = 0x08)] + public struct JobGauge : IBaseJobGauge + { + // empty base class for other gauges, this only has the vtable + } + + #region Healer + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct WhiteMageGauge : IBaseWhiteMageGauge + { + [FieldOffset(0x0A)] public short LilyTimer; + [FieldOffset(0x0C)] public byte Lily; + [FieldOffset(0x0D)] public byte BloodLily; + + short IBaseWhiteMageGauge.LilyTimer => LilyTimer; + + byte IBaseWhiteMageGauge.Lily => Lily; + + byte IBaseWhiteMageGauge.BloodLily => BloodLily; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct ScholarGauge : IBaseScholarGauge + { + [FieldOffset(0x08)] public byte Aetherflow; + [FieldOffset(0x09)] public byte FairyGauge; + [FieldOffset(0x0A)] public short SeraphTimer; + [FieldOffset(0x0C)] public byte DismissedFairy; + + byte IBaseScholarGauge.Aetherflow => Aetherflow; + + byte IBaseScholarGauge.FairyGauge => FairyGauge; + + short IBaseScholarGauge.SeraphTimer => SeraphTimer; + + byte IBaseScholarGauge.DismissedFairy => DismissedFairy; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public unsafe struct AstrologianGauge : IBaseAstrologianGauge + { + [FieldOffset(0x08)] public short Timer; + [FieldOffset(0x0D)] public byte Card; + [FieldOffset(0x0E)] public byte Seals; // 6 bits, 0,1-3,1-3,1-3 depending on astrosign + + public AstrologianCard CurrentCard => (AstrologianCard)Card; + + public AstrologianSeal[] CurrentSeals => new[] + { + (AstrologianSeal)(3 & (this.Seals >> 0)), + (AstrologianSeal)(3 & (this.Seals >> 2)), + (AstrologianSeal)(3 & (this.Seals >> 4)), + }; + + short IBaseAstrologianGauge.Timer => Timer; + + byte IBaseAstrologianGauge.Card => Card; + + byte IBaseAstrologianGauge.Seals => Seals; + + AstrologianCard IBaseAstrologianGauge.CurrentCard => CurrentCard; + + AstrologianSeal[] IBaseAstrologianGauge.CurrentSeals => CurrentSeals; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct SageGauge : IBaseSageGauge + { + [FieldOffset(0x08)] public short AddersgallTimer; + [FieldOffset(0x0A)] public byte Addersgall; + [FieldOffset(0x0B)] public byte Addersting; + [FieldOffset(0x0C)] public byte Eukrasia; + + public bool EukrasiaActive => Eukrasia > 0; + + short IBaseSageGauge.AddersgallTimer => AddersgallTimer; + + byte IBaseSageGauge.Addersgall => Addersgall; + + byte IBaseSageGauge.Addersting => Addersting; + + byte IBaseSageGauge.Eukrasia => Eukrasia; + } + + #endregion + + #region MagicDPS + + [StructLayout(LayoutKind.Explicit, Size = 0x30)] + public struct BlackMageGauge : IBaseBlackMageGauge + { + [FieldOffset(0x08)] public short EnochianTimer; + [FieldOffset(0x0A)] public short ElementTimeRemaining; + [FieldOffset(0x0C)] public sbyte ElementStance; + [FieldOffset(0x0D)] public byte UmbralHearts; + [FieldOffset(0x0E)] public byte PolyglotStacks; + [FieldOffset(0x0F)] public EnochianFlags EnochianFlags; + + public int UmbralStacks => ElementStance >= 0 ? 0 : ElementStance * -1; + public int AstralStacks => ElementStance <= 0 ? 0 : ElementStance; + public bool EnochianActive => EnochianFlags.HasFlag(EnochianFlags.Enochian); + public bool ParadoxActive => EnochianFlags.HasFlag(EnochianFlags.Paradox); + + short IBaseBlackMageGauge.EnochianTimer => EnochianTimer; + + short IBaseBlackMageGauge.ElementTimeRemaining => ElementTimeRemaining; + + sbyte IBaseBlackMageGauge.ElementStance => ElementStance; + + byte IBaseBlackMageGauge.UmbralHearts => UmbralHearts; + + byte IBaseBlackMageGauge.PolyglotStacks => PolyglotStacks; + + EnochianFlags IBaseBlackMageGauge.EnochianFlags => EnochianFlags; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct SummonerGauge : IBaseSummonerGauge + { + [FieldOffset(0x8)] public ushort SummonTimer; // millis counting down + [FieldOffset(0xA)] public ushort AttunementTimer; // millis counting down + [FieldOffset(0xC)] public byte ReturnSummon; // Pet sheet (23=Carbuncle, the only option now) + [FieldOffset(0xD)] public byte ReturnSummonGlam; // PetMirage sheet + [FieldOffset(0xE)] public byte Attunement; // Count of "Attunement cost" resource + [FieldOffset(0xF)] public AetherFlags AetherFlags; // bitfield + + ushort IBaseSummonerGauge.SummonTimer => SummonTimer; + + ushort IBaseSummonerGauge.AttunementTimer => AttunementTimer; + + byte IBaseSummonerGauge.ReturnSummon => ReturnSummon; + + byte IBaseSummonerGauge.ReturnSummonGlam => ReturnSummonGlam; + + byte IBaseSummonerGauge.Attunement => Attunement; + + AetherFlags IBaseSummonerGauge.AetherFlags => AetherFlags; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x50)] + public struct RedMageGauge : IBaseRedMageGauge + { + [FieldOffset(0x08)] public byte WhiteMana; + [FieldOffset(0x09)] public byte BlackMana; + [FieldOffset(0x0A)] public byte ManaStacks; + + byte IBaseRedMageGauge.WhiteMana => WhiteMana; + + byte IBaseRedMageGauge.BlackMana => BlackMana; + + byte IBaseRedMageGauge.ManaStacks => ManaStacks; + } + + #endregion + + #region RangeDPS + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct BardGauge : IBaseBardGauge + { + [FieldOffset(0x08)] public ushort SongTimer; + [FieldOffset(0x0C)] public byte Repertoire; + [FieldOffset(0x0D)] public byte SoulVoice; + [FieldOffset(0x0E)] public SongFlags SongFlags; // bitfield + + ushort IBaseBardGauge.SongTimer => SongTimer; + + byte IBaseBardGauge.Repertoire => Repertoire; + + byte IBaseBardGauge.SoulVoice => SoulVoice; + + SongFlags IBaseBardGauge.SongFlags => SongFlags; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct MachinistGauge : IBaseMachinistGauge + { + [FieldOffset(0x08)] public short OverheatTimeRemaining; + [FieldOffset(0x0A)] public short SummonTimeRemaining; + [FieldOffset(0x0C)] public byte Heat; + [FieldOffset(0x0D)] public byte Battery; + [FieldOffset(0x0E)] public byte LastSummonBatteryPower; + [FieldOffset(0x0F)] public byte TimerActive; + + short IBaseMachinistGauge.OverheatTimeRemaining => OverheatTimeRemaining; + + short IBaseMachinistGauge.SummonTimeRemaining => SummonTimeRemaining; + + byte IBaseMachinistGauge.Heat => Heat; + + byte IBaseMachinistGauge.Battery => Battery; + + byte IBaseMachinistGauge.LastSummonBatteryPower => LastSummonBatteryPower; + + byte IBaseMachinistGauge.TimerActive => TimerActive; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public unsafe struct DancerGauge : IBaseDancerGauge + { + [FieldOffset(0x08)] public byte Feathers; + [FieldOffset(0x09)] public byte Esprit; + [FieldOffset(0x0A)] public fixed byte DanceSteps[4]; + [FieldOffset(0x0E)] public byte StepIndex; + + public DanceStep CurrentStep => (DanceStep)(StepIndex >= 4 ? 0 : DanceSteps[StepIndex]); + + byte IBaseDancerGauge.Feathers => Feathers; + + byte IBaseDancerGauge.Esprit => Esprit; + + byte[] IBaseDancerGauge.DanceSteps => new byte[] { DanceSteps[0], DanceSteps[1], DanceSteps[2], DanceSteps[3] }; + + byte IBaseDancerGauge.StepIndex => StepIndex; + } + + #endregion + + #region MeleeDPS + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct MonkGauge : IBaseMonkGauge + { + [FieldOffset(0x08)] public byte Chakra; // Chakra count + + [FieldOffset(0x09)] + public BeastChakraType BeastChakra1; // CoeurlChakra = 1, RaptorChakra = 2, OpoopoChakra = 3 (only one value) + + [FieldOffset(0x0A)] + public BeastChakraType BeastChakra2; // CoeurlChakra = 1, RaptorChakra = 2, OpoopoChakra = 3 (only one value) + + [FieldOffset(0x0B)] + public BeastChakraType BeastChakra3; // CoeurlChakra = 1, RaptorChakra = 2, OpoopoChakra = 3 (only one value) + + [FieldOffset(0x0C)] public NadiFlags Nadi; // LunarNadi = 2, SolarNadi = 4 (If both then 2+4=6) + [FieldOffset(0x0E)] public ushort BlitzTimeRemaining; // 20 seconds + + public BeastChakraType[] BeastChakra => new[] { BeastChakra1, BeastChakra2, BeastChakra3 }; + + byte IBaseMonkGauge.Chakra => Chakra; + + BeastChakraType IBaseMonkGauge.BeastChakra1 => BeastChakra1; + + BeastChakraType IBaseMonkGauge.BeastChakra2 => BeastChakra2; + + BeastChakraType IBaseMonkGauge.BeastChakra3 => BeastChakra3; + + NadiFlags IBaseMonkGauge.Nadi => Nadi; + + ushort IBaseMonkGauge.BlitzTimeRemaining => BlitzTimeRemaining; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct DragoonGauge : IBaseDragoonGauge + { + [FieldOffset(0x08)] public short LotdTimer; + [FieldOffset(0x0A)] public byte LotdState; // This seems to only ever be 0 or 2 now + [FieldOffset(0x0B)] public byte EyeCount; + [FieldOffset(0x0C)] public byte FirstmindsFocusCount; + + short IBaseDragoonGauge.LotdTimer => LotdTimer; + + byte IBaseDragoonGauge.LotdState => LotdState; + + byte IBaseDragoonGauge.EyeCount => EyeCount; + + byte IBaseDragoonGauge.FirstmindsFocusCount => FirstmindsFocusCount; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct NinjaGauge : IBaseNinjaGauge + { + [FieldOffset(0x08)] public ushort HutonTimer; + [FieldOffset(0x0A)] public byte Ninki; + [FieldOffset(0x0B)] public byte HutonManualCasts; + + ushort IBaseNinjaGauge.HutonTimer => HutonTimer; + + byte IBaseNinjaGauge.Ninki => Ninki; + + byte IBaseNinjaGauge.HutonManualCasts => HutonManualCasts; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct SamuraiGauge : IBaseSamuraiGauge + { + [FieldOffset(0x0A)] public KaeshiAction Kaeshi; + [FieldOffset(0x0B)] public byte Kenki; + [FieldOffset(0x0C)] public byte MeditationStacks; + [FieldOffset(0x0D)] public SenFlags SenFlags; + + KaeshiAction IBaseSamuraiGauge.Kaeshi => Kaeshi; + + byte IBaseSamuraiGauge.Kenki => Kenki; + + byte IBaseSamuraiGauge.MeditationStacks => MeditationStacks; + + SenFlags IBaseSamuraiGauge.SenFlags => SenFlags; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct ReaperGauge : IBaseReaperGauge + { + [FieldOffset(0x08)] public byte Soul; + [FieldOffset(0x09)] public byte Shroud; + [FieldOffset(0x0A)] public ushort EnshroudedTimeRemaining; + [FieldOffset(0x0C)] public byte LemureShroud; + [FieldOffset(0x0D)] public byte VoidShroud; + + byte IBaseReaperGauge.Soul => Soul; + + byte IBaseReaperGauge.Shroud => Shroud; + + ushort IBaseReaperGauge.EnshroudedTimeRemaining => EnshroudedTimeRemaining; + + byte IBaseReaperGauge.LemureShroud => LemureShroud; + + byte IBaseReaperGauge.VoidShroud => VoidShroud; + } + + #endregion + + #region Tanks + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct DarkKnightGauge : IBaseDarkKnightGauge + { + [FieldOffset(0x08)] public byte Blood; + [FieldOffset(0x0A)] public ushort DarksideTimer; + [FieldOffset(0x0C)] public byte DarkArtsState; + [FieldOffset(0x0E)] public ushort ShadowTimer; + + byte IBaseDarkKnightGauge.Blood => Blood; + + ushort IBaseDarkKnightGauge.DarksideTimer => DarksideTimer; + + byte IBaseDarkKnightGauge.DarkArtsState => DarkArtsState; + + ushort IBaseDarkKnightGauge.ShadowTimer => ShadowTimer; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct PaladinGauge : IBasePaladinGauge + { + [FieldOffset(0x08)] public byte OathGauge; + + byte IBasePaladinGauge.OathGauge => OathGauge; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct WarriorGauge : IBaseWarriorGauge + { + [FieldOffset(0x08)] public byte BeastGauge; + + byte IBaseWarriorGauge.BeastGauge => BeastGauge; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x10)] + public struct GunbreakerGauge : IBaseGunbreakerGauge + { + [FieldOffset(0x08)] public byte Ammo; + [FieldOffset(0x0A)] public short MaxTimerDuration; + [FieldOffset(0x0C)] public byte AmmoComboStep; + + byte IBaseGunbreakerGauge.Ammo => Ammo; + + short IBaseGunbreakerGauge.MaxTimerDuration => MaxTimerDuration; + + byte IBaseGunbreakerGauge.AmmoComboStep => AmmoComboStep; + } + + #endregion + + #endregion + } +} diff --git a/OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGaugeMemoryManager.cs b/OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGaugeMemoryManager.cs new file mode 100644 index 000000000..29604a9b4 --- /dev/null +++ b/OverlayPlugin.Core/MemoryProcessors/JobGauge/JobGaugeMemoryManager.cs @@ -0,0 +1,426 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using FFXIVClientStructs.Global.FFXIV.Client.Game.Gauge; +#if !DEBUG +using Newtonsoft.Json; +#endif + +namespace RainbowMage.OverlayPlugin.MemoryProcessors.JobGauge +{ + public enum JobGaugeJob : byte + { + None = 0, + GLA = 1, + PGL = 2, + MRD = 3, + LNC = 4, + ARC = 5, + CNJ = 6, + THM = 7, + CRP = 8, + BSM = 9, + ARM = 10, + GSM = 11, + LTW = 12, + WVR = 13, + ALC = 14, + CUL = 15, + MIN = 16, + BTN = 17, + FSH = 18, + PLD = 19, + MNK = 20, + WAR = 21, + DRG = 22, + BRD = 23, + WHM = 24, + BLM = 25, + ACN = 26, + SMN = 27, + SCH = 28, + ROG = 29, + NIN = 30, + MCH = 31, + DRK = 32, + AST = 33, + SAM = 34, + RDM = 35, + BLU = 36, + GNB = 37, + DNC = 38, + RPR = 39, + SGE = 40, + } + + public interface IJobGauge : IEquatable + { + JobGaugeJob Job { get; } + IBaseJobGauge Data { get; } + int[] RawData { get; } +#if !DEBUG + [JsonIgnore] +#endif + object BaseObject { get; } + } + + public interface IJobGaugeMemory : IVersionedMemory + { + IJobGauge GetJobGauge(); + } + + class JobGaugeMemoryManager : IJobGaugeMemory + { + private readonly TinyIoCContainer container; + private readonly FFXIVRepository repository; + protected readonly ILogger logger; + private IJobGaugeMemory memory = null; + + public JobGaugeMemoryManager(TinyIoCContainer container) + { + this.container = container; + container.Register(); + repository = container.Resolve(); + logger = container.Resolve(); + + var memory = container.Resolve(); + memory.RegisterOnProcessChangeHandler(FindMemory); + } + + private void FindMemory(object sender, Process p) + { + memory = null; + if (p == null) + { + return; + } + ScanPointers(); + } + + public void ScanPointers() + { + List candidates = new List(); + candidates.Add(container.Resolve()); + memory = FFXIVMemory.FindCandidate(candidates, repository.GetMachinaRegion()); + } + + public bool IsValid() + { + if (memory == null || !memory.IsValid()) + { + return false; + } + return true; + } + + Version IVersionedMemory.GetVersion() + { + if (!IsValid()) + return null; + return memory.GetVersion(); + } + + public IJobGauge GetJobGauge() + { + if (!IsValid()) + return null; + return memory.GetJobGauge(); + } + } + + public interface IBaseJobGauge + { + } + + #region Healer + + public interface IBaseWhiteMageGauge : IBaseJobGauge + { + short LilyTimer { get; } + byte Lily { get; } + byte BloodLily { get; } + } + + public interface IBaseScholarGauge : IBaseJobGauge + { + byte Aetherflow { get; } + byte FairyGauge { get; } + short SeraphTimer { get; } + byte DismissedFairy { get; } + } + + public interface IBaseAstrologianGauge : IBaseJobGauge + { + short Timer { get; } + byte Card { get; } + byte Seals { get; } + + AstrologianCard CurrentCard { get; } + + AstrologianSeal[] CurrentSeals { get; } + } + + public interface IBaseSageGauge : IBaseJobGauge + { + short AddersgallTimer { get; } + byte Addersgall { get; } + byte Addersting { get; } + byte Eukrasia { get; } + + bool EukrasiaActive { get; } + } + + #endregion + + #region MagicDPS + + public interface IBaseBlackMageGauge : IBaseJobGauge + { + short EnochianTimer { get; } + short ElementTimeRemaining { get; } + sbyte ElementStance { get; } + byte UmbralHearts { get; } + byte PolyglotStacks { get; } + EnochianFlags EnochianFlags { get; } + + int UmbralStacks { get; } + int AstralStacks { get; } + bool EnochianActive { get; } + bool ParadoxActive { get; } + } + + public interface IBaseSummonerGauge : IBaseJobGauge + { + ushort SummonTimer { get; } + ushort AttunementTimer { get; } + byte ReturnSummon { get; } + byte ReturnSummonGlam { get; } + byte Attunement { get; } + AetherFlags AetherFlags { get; } + } + + public interface IBaseRedMageGauge : IBaseJobGauge + { + byte WhiteMana { get; } + byte BlackMana { get; } + byte ManaStacks { get; } + } + + #endregion + + #region RangeDPS + + public interface IBaseBardGauge : IBaseJobGauge + { + ushort SongTimer { get; } + byte Repertoire { get; } + byte SoulVoice { get; } + SongFlags SongFlags { get; } + } + + public interface IBaseMachinistGauge : IBaseJobGauge + { + short OverheatTimeRemaining { get; } + short SummonTimeRemaining { get; } + byte Heat { get; } + byte Battery { get; } + byte LastSummonBatteryPower { get; } + byte TimerActive { get; } + } + + public interface IBaseDancerGauge : IBaseJobGauge + { + byte Feathers { get; } + byte Esprit { get; } + byte[] DanceSteps { get; } + byte StepIndex { get; } + + DanceStep CurrentStep { get; } + } + + #endregion + + #region MeleeDPS + + public interface IBaseMonkGauge : IBaseJobGauge + { + byte Chakra { get; } + + BeastChakraType BeastChakra1 { get; } + + BeastChakraType BeastChakra2 { get; } + + BeastChakraType BeastChakra3 { get; } + + NadiFlags Nadi { get; } + ushort BlitzTimeRemaining { get; } + + BeastChakraType[] BeastChakra { get; } + } + + public interface IBaseDragoonGauge : IBaseJobGauge + { + short LotdTimer { get; } + byte LotdState { get; } + byte EyeCount { get; } + byte FirstmindsFocusCount { get; } + } + + public interface IBaseNinjaGauge : IBaseJobGauge + { + ushort HutonTimer { get; } + byte Ninki { get; } + byte HutonManualCasts { get; } + } + + public interface IBaseSamuraiGauge : IBaseJobGauge + { + KaeshiAction Kaeshi { get; } + byte Kenki { get; } + byte MeditationStacks { get; } + SenFlags SenFlags { get; } + } + + public interface IBaseReaperGauge : IBaseJobGauge + { + byte Soul { get; } + byte Shroud { get; } + ushort EnshroudedTimeRemaining { get; } + byte LemureShroud { get; } + byte VoidShroud { get; } + } + + #endregion + + #region Tanks + + public interface IBaseDarkKnightGauge : IBaseJobGauge + { + byte Blood { get; } + ushort DarksideTimer { get; } + byte DarkArtsState { get; } + ushort ShadowTimer { get; } + } + + public interface IBasePaladinGauge : IBaseJobGauge + { + byte OathGauge { get; } + } + + public interface IBaseWarriorGauge : IBaseJobGauge + { + byte BeastGauge { get; } + } + + public interface IBaseGunbreakerGauge : IBaseJobGauge + { + byte Ammo { get; } + short MaxTimerDuration { get; } + byte AmmoComboStep { get; } + } + + public enum AstrologianCard + { + None = 0, + Balance = 1, + Bole = 2, + Arrow = 3, + Spear = 4, + Ewer = 5, + Spire = 6, + Lord = 0x70, + Lady = 0x80 + } + + public enum AstrologianSeal + { + Solar = 1, + Lunar = 2, + Celestial = 3 + } + + public enum DanceStep : byte + { + Finish = 0, + Emboite = 1, + Entrechat = 2, + Jete = 3, + Pirouette = 4 + } + + [Flags] + public enum EnochianFlags : byte + { + None = 0, + Enochian = 1, + Paradox = 2 + } + + public enum KaeshiAction : byte + { + Higanbana = 1, + Goken = 2, + Setsugekka = 3, + Namikiri = 4 + } + + [Flags] + public enum SenFlags : byte + { + None = 0, + Setsu = 1 << 0, + Getsu = 1 << 1, + Ka = 1 << 2 + } + + [Flags] + public enum SongFlags : byte + { + None = 0, + MagesBallad = 1 << 0, + ArmysPaeon = 1 << 1, + WanderersMinuet = MagesBallad | ArmysPaeon, + MagesBalladLastPlayed = 1 << 2, + ArmysPaeonLastPlayed = 1 << 3, + WanderersMinuetLastPlayed = MagesBalladLastPlayed | ArmysPaeonLastPlayed, + MagesBalladCoda = 1 << 4, + ArmysPaeonCoda = 1 << 5, + WanderersMinuetCoda = 1 << 6 + } + + [Flags] + public enum AetherFlags : byte + { + None = 0, + Aetherflow1 = 1 << 0, + Aetherflow2 = 1 << 1, + Aetherflow = Aetherflow1 | Aetherflow2, + IfritAttuned = 1 << 2, + TitanAttuned = 1 << 3, + GarudaAttuned = TitanAttuned | IfritAttuned, + PhoenixReady = 1 << 4, + IfritReady = 1 << 5, + TitanReady = 1 << 6, + GarudaReady = 1 << 7 + } + + public enum BeastChakraType : byte + { + None = 0, + Coeurl = 1, + OpoOpo = 2, + Raptor = 3 + } + + [Flags] + public enum NadiFlags : byte + { + Lunar = 2, + Solar = 4 + } + + #endregion +} diff --git a/OverlayPlugin.Core/PluginMain.cs b/OverlayPlugin.Core/PluginMain.cs index 664794dc3..e223bb0a0 100644 --- a/OverlayPlugin.Core/PluginMain.cs +++ b/OverlayPlugin.Core/PluginMain.cs @@ -20,6 +20,7 @@ using RainbowMage.OverlayPlugin.MemoryProcessors.Enmity; using RainbowMage.OverlayPlugin.MemoryProcessors.EnmityHud; using RainbowMage.OverlayPlugin.MemoryProcessors.InCombat; +using RainbowMage.OverlayPlugin.MemoryProcessors.JobGauge; using RainbowMage.OverlayPlugin.MemoryProcessors.Party; using RainbowMage.OverlayPlugin.MemoryProcessors.Target; using RainbowMage.OverlayPlugin.NetworkProcessors; @@ -277,6 +278,7 @@ public void InitPlugin(TabPage pluginScreenSpace, Label pluginStatusText) _container.Register(); _container.Register(); _container.Register(); + _container.Register(); _container.Register(new OverlayPluginLogLines(_container)); } From d63b93966fe7d73c790e0105214efe4af78883b5 Mon Sep 17 00:00:00 2001 From: valarnin Date: Sun, 25 Feb 2024 16:43:01 -0500 Subject: [PATCH 11/11] Sync translations (#311) --- .../Controls/WSConfigPanel.fr-FR.resx | 3 +++ .../BuiltinEventConfigPanel.fr-FR.resx | 7 +++-- .../LabelOverlayConfigPanel.fr-FR.resx | 6 ++--- .../Overlays/LogParseConfigPanel.fr-FR.resx | 2 +- .../Overlays/MiniParseConfigPanel.fr-FR.resx | 12 ++++----- .../Overlays/SpellTimerConfigPanel.fr-FR.resx | 2 +- OverlayPlugin.Core/Resources.fr-FR.resx | 26 +++++++++---------- .../CefMissingTab.fr-FR.resx | 7 +++-- .../ProgressDisplay.fr-FR.resx | 2 +- OverlayPlugin.Updater/Resources.fr-FR.resx | 24 ++++++++++++----- .../UpdateQuestionForm.fr-FR.resx | 2 +- 11 files changed, 56 insertions(+), 37 deletions(-) diff --git a/OverlayPlugin.Core/Controls/WSConfigPanel.fr-FR.resx b/OverlayPlugin.Core/Controls/WSConfigPanel.fr-FR.resx index abaf9395f..1b69dd414 100644 --- a/OverlayPlugin.Core/Controls/WSConfigPanel.fr-FR.resx +++ b/OverlayPlugin.Core/Controls/WSConfigPanel.fr-FR.resx @@ -191,4 +191,7 @@ Overlay Partagé : Si vous démarrez WSServer via cet onglet, vous pouvez utilis Stop + + Jeton : + \ No newline at end of file diff --git a/OverlayPlugin.Core/EventSources/BuiltinEventConfigPanel.fr-FR.resx b/OverlayPlugin.Core/EventSources/BuiltinEventConfigPanel.fr-FR.resx index 364e50d0b..e82cbb28a 100644 --- a/OverlayPlugin.Core/EventSources/BuiltinEventConfigPanel.fr-FR.resx +++ b/OverlayPlugin.Core/EventSources/BuiltinEventConfigPanel.fr-FR.resx @@ -133,15 +133,18 @@ Trier par - Arrêt du log après un wipe + Fin du combat après un wipe AVERTISSEMENT : Options expérimentales ci-dessous - Si vous y touchez, vous confirmez que vous pouvez résoudre vous-même les problèmes que vous rencontrez et que vous ne demanderez pas d'aide. - Arrêt du log si hors combat + Arrêter le log si hors combat Écrire les lignes de détection des cinématiques + + Écrire des lignes de log additionnelles + \ No newline at end of file diff --git a/OverlayPlugin.Core/Overlays/LabelOverlayConfigPanel.fr-FR.resx b/OverlayPlugin.Core/Overlays/LabelOverlayConfigPanel.fr-FR.resx index 103dd9857..b4eab0b96 100644 --- a/OverlayPlugin.Core/Overlays/LabelOverlayConfigPanel.fr-FR.resx +++ b/OverlayPlugin.Core/Overlays/LabelOverlayConfigPanel.fr-FR.resx @@ -119,7 +119,7 @@ - Activer raccourci + Activer le raccourci Recharger l'overlay @@ -134,7 +134,7 @@ Ouvrir DevTools - Afficher texte en tant qu'HTML + Afficher le texte en tant qu'HTML Bloquer l'overlay @@ -149,7 +149,7 @@ URL - Activer cliquer à travers + Cliquer à travers Touche de raccourci diff --git a/OverlayPlugin.Core/Overlays/LogParseConfigPanel.fr-FR.resx b/OverlayPlugin.Core/Overlays/LogParseConfigPanel.fr-FR.resx index 5e0ed4ec1..4eba1e400 100644 --- a/OverlayPlugin.Core/Overlays/LogParseConfigPanel.fr-FR.resx +++ b/OverlayPlugin.Core/Overlays/LogParseConfigPanel.fr-FR.resx @@ -146,7 +146,7 @@ Ouvrir DevTools - Activer cliquer à travers + Cliquer à travers Recharger l'overlay diff --git a/OverlayPlugin.Core/Overlays/MiniParseConfigPanel.fr-FR.resx b/OverlayPlugin.Core/Overlays/MiniParseConfigPanel.fr-FR.resx index 9110c0cfb..c6d4e3519 100644 --- a/OverlayPlugin.Core/Overlays/MiniParseConfigPanel.fr-FR.resx +++ b/OverlayPlugin.Core/Overlays/MiniParseConfigPanel.fr-FR.resx @@ -122,10 +122,10 @@ ... - Cette option est seulement nécessaire si la skin de l'overlay n'est pas compatible d'origine avec OverlayPlugin + Cette option est seulement nécessaire si l'apparence (skin) de l'overlay n'est pas compatible d'origine avec OverlayPlugin. - Utilisez cette option si vous ne trouvez ou ne pouvez pas redimensionner l'overlay + Utilisez cette option si vous ne trouvez ou ne pouvez pas redimensionner l'overlay. Ouvrir DevTools @@ -146,13 +146,13 @@ Forcer le fond en blanc - Activer cliquer à travers + Cliquer à travers Ne pas accepter le focus pour cet overlay - RAZ Position + Remise à zéro de la position Rafraichissement max. @@ -170,7 +170,7 @@ Cet overlay nécessite ACTWebSocket - RAZ + Remise à zéro Raccourci @@ -203,7 +203,7 @@ Zoom - Si un overlay est bloqué, sa position devient fixe et vous ne pourrez pas le bouger avant de l'avoir débloqué + Si un overlay est bloqué, sa position devient fixe et vous ne pourrez pas le bouger avant de l'avoir débloqué. Désactiver un overlay réduit la charge CPU mais arrête également toutes ses fonctionnalités (sons, etc.). diff --git a/OverlayPlugin.Core/Overlays/SpellTimerConfigPanel.fr-FR.resx b/OverlayPlugin.Core/Overlays/SpellTimerConfigPanel.fr-FR.resx index 68ef9def3..9a8aafa44 100644 --- a/OverlayPlugin.Core/Overlays/SpellTimerConfigPanel.fr-FR.resx +++ b/OverlayPlugin.Core/Overlays/SpellTimerConfigPanel.fr-FR.resx @@ -149,7 +149,7 @@ ... - Activer cliquer à travers + Cliquer à travers Touche de raccourci diff --git a/OverlayPlugin.Core/Resources.fr-FR.resx b/OverlayPlugin.Core/Resources.fr-FR.resx index 3fe69c7a1..993bf0deb 100644 --- a/OverlayPlugin.Core/Resources.fr-FR.resx +++ b/OverlayPlugin.Core/Resources.fr-FR.resx @@ -125,13 +125,13 @@ Souscription pour un évènement manquant "{0}"! - Impossible de récupérer FFXIV DataRepository: {0} + Impossible de récupérer FFXIV DataRepository : {0} WS : L'arrêt a échoué. {0} - InitializeOverlay: Le fichier local {0} n'existe pas ! + InitializeOverlay : Le fichier local {0} n'existe pas ! Impossible de procéder à la déinscription : {0} @@ -164,13 +164,13 @@ Clic à travers - {0}: Souscription à {1} + {0} : Souscription à {1} Compatibilité ACTWS activé. - Le nom doit être unique + Le nom doit être unique. Sélectionnez un type d'overlay. @@ -185,22 +185,22 @@ Impossible d'enoyer le message : {0} - Le nom ne doit pas être vide ou composé seulement d'espaces + Le nom ne doit pas être vide ou composé seulement d'espaces. - Impossible de trier la liste par {0}: {1} + Impossible de trier la liste par {0} : {1} Appel à un handler manquant "{0}"! - WS : Chargement du certificat SSL {0}... + WS : Chargement du certificat SSL {0}... Impossible de supprimer le raccourci {0} dans DisableHotKeys() ! - Echec de la configuration réseau du parser : {0} + Échec de la configuration réseau du parser : {0} Impossible d'enregistrer le raccourci : {0}! @@ -216,16 +216,16 @@ </p> - WS : Impossible de démarrer : {0} + WS : Démarrage impossible : {0} Données non valides reçues : {0}; {1} - Impossible de récupérer FFXIV DataSubscription: {0} + Impossible de récupérer FFXIV DataSubscription : {0} - InitializeOverlay: erreur d'analyse URI ! Reconfigurez l'URL. (Config.Url = {0}): {1} + InitializeOverlay : Erreur d'analyse URI ! Reconfigurez l'URL. (Config.Url = {0}): {1} Addon {0} incompatible. Mettez à jour dès que possible ! @@ -251,7 +251,7 @@ Ce lien peut ne pas fonctionner si l'overlay ne supporte pas ACTWebSocket. - Impossible de trouver le plugin FFXIV ou Machina. C'est seulement un problème si vous utilisez ACT avec FFXIV. + Impossible de trouver le plugin FFXIV ou Machina. C'est un problème seulement si vous utilisez ACT avec FFXIV. Changement de mode activé @@ -263,7 +263,7 @@ WS : L'appel du gestionnaire a échoué : {0} - FFXIVCustomLogLines: Échec du chargement de la ligne du log réservé + FFXIVCustomLogLines: Échec du chargement de la ligne du log réservé : {0} Source de l'événement diff --git a/OverlayPlugin.Updater/CefMissingTab.fr-FR.resx b/OverlayPlugin.Updater/CefMissingTab.fr-FR.resx index c524ceef3..39b62e9e5 100644 --- a/OverlayPlugin.Updater/CefMissingTab.fr-FR.resx +++ b/OverlayPlugin.Updater/CefMissingTab.fr-FR.resx @@ -148,9 +148,12 @@ Extraction du fichier téléchargé - Arrêter de parser si hors-combat + Arrêter le log si hors combat - Arrêter le combat ACT après un wipe + Fin du combat après un wipe + + + Log : \ No newline at end of file diff --git a/OverlayPlugin.Updater/ProgressDisplay.fr-FR.resx b/OverlayPlugin.Updater/ProgressDisplay.fr-FR.resx index 1b0f4a681..aa0444417 100644 --- a/OverlayPlugin.Updater/ProgressDisplay.fr-FR.resx +++ b/OverlayPlugin.Updater/ProgressDisplay.fr-FR.resx @@ -125,5 +125,5 @@ Annuler - Mise à jour OverlayPlugin + Mise à jour d'OverlayPlugin \ No newline at end of file diff --git a/OverlayPlugin.Updater/Resources.fr-FR.resx b/OverlayPlugin.Updater/Resources.fr-FR.resx index 1536aaa47..5b4129562 100644 --- a/OverlayPlugin.Updater/Resources.fr-FR.resx +++ b/OverlayPlugin.Updater/Resources.fr-FR.resx @@ -128,7 +128,7 @@ Mise à jour réalisée avec succès. Redémarrez ACT pour charger la nouvelle version. - Vous êtes déjà sur la dernière version. + Vous utilisez déjà la dernière version. Extraction des fichiers ... @@ -152,7 +152,7 @@ [{0}/{1}] : Démarrage du téléchargement ... - Reprise du téléchargement + Reprise du téléchargement ... Téléchargement échoué : {0} @@ -184,7 +184,7 @@ Ce composant est nécessaire pour OverlayPlugin. L'installer maintenant ? - Impossible de détecter la version : + Impossible de détecter la version : {0} @@ -197,7 +197,7 @@ Ce composant est nécessaire pour OverlayPlugin. L'installer maintenant ?Téléchargement de "{0}" dans {1} ... - Nouvel essai dans 1 seconde. + Nouvel essai dans une seconde. [{0}/{1}] : Effectué. @@ -215,7 +215,7 @@ Ce composant est nécessaire pour OverlayPlugin. L'installer maintenant ?Impossible de mettre à jour le plugin. Il est possible qu'il ne démarre pas au prochain lancement d'ACT. Réessayer ? - Impossible de remplacer l'ancier répertoire : {0} + Impossible de remplacer l'ancien répertoire : {0} Sauvegarde des anciens fichiers ... @@ -224,7 +224,7 @@ Ce composant est nécessaire pour OverlayPlugin. L'installer maintenant ?[{0}/{1}] : Préparation de l'extraction ... - Ecrasement des anciens fichiers ... + Écrasement des anciens fichiers ... En attente de l'installeur pour quitter ... @@ -256,7 +256,7 @@ Relancez la mise à jour ou réinstallez le plugin, sinon il pourrait ne pas sur Suppression du répertoire de sauvegarde ... - Nombre maximum d'essais dépassés + Nombre de tentative maximum dépassé. Annulation ... @@ -279,4 +279,14 @@ Soyez sûr de charger OverlayPlugin avant d'autres plugins qui pourraient égale Ignore la mise à jour automatique car OverlayPlugin est un dépôt .git + + Aucune tentative restante, la mise à jour a échoué. + + + Impossible d'écraser {0}, nouvelle tentative... +{1} + + + {0} a été mis à jour avec succès. + \ No newline at end of file diff --git a/OverlayPlugin.Updater/UpdateQuestionForm.fr-FR.resx b/OverlayPlugin.Updater/UpdateQuestionForm.fr-FR.resx index e43d6c99a..6700c472f 100644 --- a/OverlayPlugin.Updater/UpdateQuestionForm.fr-FR.resx +++ b/OverlayPlugin.Updater/UpdateQuestionForm.fr-FR.resx @@ -125,7 +125,7 @@ Installer la mise à jour - {0}: Une nouvelle version est disponible. Voulez-vous l'installer maintenant ? + Une nouvelle version de {0} est disponible. Voulez-vous l'installer maintenant ? Annuler