From cff9921ba2ab6628bd027cdd60dc6ab5788ab0c2 Mon Sep 17 00:00:00 2001 From: anoyetta Date: Tue, 7 May 2019 01:08:14 +0900 Subject: [PATCH 01/24] day1, up2 --- .../kagami/ViewModels/KagamiConfigViewModel.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/source/kagami/ViewModels/KagamiConfigViewModel.cs b/source/kagami/ViewModels/KagamiConfigViewModel.cs index d6b9bd2..2811172 100644 --- a/source/kagami/ViewModels/KagamiConfigViewModel.cs +++ b/source/kagami/ViewModels/KagamiConfigViewModel.cs @@ -1,4 +1,6 @@ -using Prism.Commands; +using System; +using Advanced_Combat_Tracker; +using Prism.Commands; using Prism.Mvvm; namespace kagami.ViewModels @@ -13,6 +15,13 @@ public KagamiOverlayConfig Config set => this.SetProperty(ref this.config, value); } + private static readonly System.Windows.Forms.OpenFileDialog OpenFileDialog = new System.Windows.Forms.OpenFileDialog() + { + RestoreDirectory = true, + Filter = "All Files (*.*)|*.*", + InitialDirectory = KagamiAddon.Instance.ResourcesDirectory, + }; + private DelegateCommand browseUrlCommand; public DelegateCommand BrowseUrlCommand => @@ -20,6 +29,11 @@ public KagamiOverlayConfig Config private void ExecuteBrowseUrlCommand() { + var result = OpenFileDialog.ShowDialog(ActGlobals.oFormActMain); + if (result == System.Windows.Forms.DialogResult.OK) + { + this.Config.Url = new Uri(OpenFileDialog.FileName).AbsoluteUri; + } } } } From 2caaac57e6251d5d1ade4d6c23acb6e26ca740c9 Mon Sep 17 00:00:00 2001 From: anoyetta Date: Tue, 7 May 2019 19:14:03 +0900 Subject: [PATCH 02/24] day2 --- source/kagami/Common/ThreadWorker.cs | 125 ++++++++++++++++++++ source/kagami/Helpers/FFXIVPluginHelper.cs | 123 +++++++++++++++++++ source/kagami/Helpers/SharlayanHelper.cs | 102 ++++++++++++++++ source/kagami/KagamiAddon.cs | 33 +++++- source/kagami/KagamiOverlay.cs | 2 + source/kagami/KagamiOverlayConfig.cs | 63 ++++++++-- source/kagami/Logger.cs | 17 +++ source/kagami/Models/ActionCategory.cs | 22 ++++ source/kagami/Models/ActionEchoModel.cs | 77 ++++++++++++ source/kagami/Models/ActionEchoesModel.cs | 95 +++++++++++++++ source/kagami/Models/NetworkAbilityModel.cs | 81 +++++++++++++ source/kagami/Views/KagamiConfigView.xaml | 84 +++++++++++-- source/kagami/kagami.csproj | 12 +- 13 files changed, 813 insertions(+), 23 deletions(-) create mode 100644 source/kagami/Common/ThreadWorker.cs create mode 100644 source/kagami/Helpers/FFXIVPluginHelper.cs create mode 100644 source/kagami/Helpers/SharlayanHelper.cs create mode 100644 source/kagami/Logger.cs create mode 100644 source/kagami/Models/ActionCategory.cs create mode 100644 source/kagami/Models/ActionEchoModel.cs create mode 100644 source/kagami/Models/ActionEchoesModel.cs create mode 100644 source/kagami/Models/NetworkAbilityModel.cs diff --git a/source/kagami/Common/ThreadWorker.cs b/source/kagami/Common/ThreadWorker.cs new file mode 100644 index 0000000..7a2907f --- /dev/null +++ b/source/kagami/Common/ThreadWorker.cs @@ -0,0 +1,125 @@ +using System; +using System.Threading; + +namespace kagami.Helpers.Common +{ + public class ThreadWorker + { + private volatile bool isAbort; + private Thread thread; + + /// + /// コンストラクタ + /// + /// + /// 定期的に実行するアクション + /// + /// インターバル。ミリ秒 + public ThreadWorker( + Action doWorkAction, + double interval, + string name = "", + ThreadPriority priority = ThreadPriority.Normal) + { + this.DoWorkAction = doWorkAction; + this.Interval = interval; + this.Name = name; + this.Priority = priority; + } + + public Action DoWorkAction { get; set; } + + public double Interval { get; set; } + + public string Name { get; set; } + + public ThreadPriority Priority { get; private set; } + + public bool IsRunning { get; private set; } + + public static ThreadWorker Run( + Action doWorkAction, + double interval, + string name = "", + ThreadPriority priority = ThreadPriority.Normal) + { + var worker = new ThreadWorker(doWorkAction, interval, name); + worker.Run(); + return worker; + } + + public bool Abort( + int timeout = 0) + { + var result = false; + + this.isAbort = true; + + if (timeout == 0) + { + timeout = (int)this.Interval; + } + + if (this.thread != null) + { + this.thread.Join(timeout); + if (this.thread.IsAlive) + { + this.thread.Abort(); + result = true; + } + + this.thread = null; + } + + this.IsRunning = false; + + Logger.Info($"ThreadWorker - {this.Name} end.{(result ? " aborted" : string.Empty)}"); + + return result; + } + + public void Run() + { + this.isAbort = false; + + this.thread = new Thread(this.DoWorkLoop); + this.thread.IsBackground = true; + this.thread.Priority = this.Priority; + this.thread.Start(); + + this.IsRunning = true; + } + + private void DoWorkLoop() + { + Thread.Sleep((int)this.Interval); + Logger.Info($"ThreadWorker - {this.Name} start."); + + while (!this.isAbort) + { + try + { + this.DoWorkAction?.Invoke(); + } + catch (ThreadAbortException) + { + this.isAbort = true; + Logger.Info($"ThreadWorker - {this.Name} abort."); + break; + } + catch (Exception ex) + { + Logger.Error($"ThreadWorker - {this.Name} error. {ex.ToString()}"); + } + + if (this.isAbort) + { + break; + } + + Thread.Sleep((int)this.Interval); + } + } + } +} diff --git a/source/kagami/Helpers/FFXIVPluginHelper.cs b/source/kagami/Helpers/FFXIVPluginHelper.cs new file mode 100644 index 0000000..05102d2 --- /dev/null +++ b/source/kagami/Helpers/FFXIVPluginHelper.cs @@ -0,0 +1,123 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Threading; +using Advanced_Combat_Tracker; +using kagami.Helpers.Common; + +namespace kagami.Helpers +{ + public class FFXIVPluginHelper + { + #region Singleton + + private static readonly Lazy LazyInstance = new Lazy(() => new FFXIVPluginHelper()); + + public static FFXIVPluginHelper Instance => LazyInstance.Value; + + private FFXIVPluginHelper() + { + } + + #endregion Singleton + + private ThreadWorker attachWorker; + + private dynamic ffxivPlugin; + private dynamic ffxivPluginConfig; + private dynamic ffxivPluginLogParse; + + public Process FFXIVProcess => this.ffxivPluginConfig?.Process; + + public string FFXIVPluginLanguage => this.ffxivPluginLogParse?.Settings?.LanguageID ?? 0 switch + { + 1 => "English", + 2 => "French", + 3 => "German", + 4 => "Japanese", + _ => "English", + }; + + private static readonly double AttachSubscribeInterval = 3000; + + public void Start() + { + this.attachWorker = new ThreadWorker(() => + { + if (ActGlobals.oFormActMain == null) + { + return; + } + + if (this.ffxivPlugin == null) + { + this.ffxivPlugin = ( + from x in ActGlobals.oFormActMain.ActPlugins + where + x.pluginFile.Name.ToUpper().Contains("FFXIV_ACT_Plugin".ToUpper()) && + x.lblPluginStatus.Text.ToUpper().Contains("FFXIV Plugin Started.".ToUpper()) + select + x.pluginObj).FirstOrDefault(); + } + + if (this.ffxivPlugin != null && + this.ffxivPluginConfig == null) + { + var fi = this.ffxivPlugin.GetType().GetField( + "_Memory", + BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance); + var memory = fi?.GetValue(this.ffxivPlugin); + if (memory == null) + { + return; + } + + fi = memory.GetType().GetField( + "_config", + BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance); + this.ffxivPluginConfig = fi?.GetValue(memory); + + Logger.Info("FFXIV_ACT_Plugin.Config attached."); + } + + if (this.ffxivPlugin != null && + this.ffxivPluginLogParse == null) + { + var fi = this.ffxivPlugin.GetType().GetField( + "_LogParse", + BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance); + + this.ffxivPluginLogParse = fi?.GetValue(this.ffxivPlugin); + + Logger.Info("FFXIV_ACT_Plugin.LogParse attached."); + } + + if (this.ffxivPlugin != null && + this.ffxivPluginConfig != null && + this.ffxivPluginLogParse != null) + { + Thread.Sleep(TimeSpan.FromMilliseconds(AttachSubscribeInterval)); + } + }, + AttachSubscribeInterval, + "FFXIV_ACT_Plugin Subscriber", + ThreadPriority.Lowest); + + this.attachWorker.Run(); + } + + public void Stop() + { + if (this.attachWorker != null) + { + this.attachWorker.Abort(); + this.attachWorker = null; + } + + this.ffxivPlugin = null; + this.ffxivPluginConfig = null; + this.ffxivPluginLogParse = null; + } + } +} diff --git a/source/kagami/Helpers/SharlayanHelper.cs b/source/kagami/Helpers/SharlayanHelper.cs new file mode 100644 index 0000000..b29dc91 --- /dev/null +++ b/source/kagami/Helpers/SharlayanHelper.cs @@ -0,0 +1,102 @@ +using System; +using System.Diagnostics; +using System.Threading; +using kagami.Helpers.Common; +using Sharlayan; +using Sharlayan.Models; +using Sharlayan.Utilities; + +namespace kagami.Helpers +{ + public class SharlayanHelper + { + #region Singleton + + private static readonly Lazy LazyInstance = new Lazy(() => new SharlayanHelper()); + + public static SharlayanHelper Instance => LazyInstance.Value; + + private SharlayanHelper() + { + } + + #endregion Singleton + + public Sharlayan.Models.XIVDatabase.ActionItem GetActionInfo(uint id) => ActionLookup.GetActionInfo(id); + + public Sharlayan.Core.CurrentPlayer GetCurrentPlayer() => Reader.GetCurrentPlayer()?.CurrentPlayer; + + private ThreadWorker processSubscriber; + private static readonly double ProcessSubscribeInterval = 3000; + + public void Start() + { + lock (this) + { + if (this.processSubscriber == null) + { + this.processSubscriber = new ThreadWorker( + this.DetectFFXIVProcess, + ProcessSubscribeInterval, + "Sharlayan Process Subscriber", + ThreadPriority.Lowest); + } + } + } + + public void Stop() + { + lock (this) + { + if (this.processSubscriber != null) + { + this.processSubscriber.Abort(); + this.processSubscriber = null; + } + } + } + + private Process currentFFXIVProcess; + private string currentFFXIVLanguage; + + private void DetectFFXIVProcess() + { + var ffxiv = FFXIVPluginHelper.Instance.FFXIVProcess; + var ffxivLanguage = FFXIVPluginHelper.Instance.FFXIVPluginLanguage; + + if (ffxiv == null) + { + return; + } + + lock (this) + { + if (!MemoryHandler.Instance.IsAttached || + this.currentFFXIVProcess == null || + this.currentFFXIVProcess?.Id != ffxiv?.Id || + this.currentFFXIVLanguage != ffxivLanguage) + { + this.currentFFXIVProcess = ffxiv; + this.currentFFXIVLanguage = ffxivLanguage; + + if (MemoryHandler.Instance.IsAttached) + { + MemoryHandler.Instance.UnsetProcess(); + } + + var model = new ProcessModel + { + Process = ffxiv, + IsWin64 = true + }; + + MemoryHandler.Instance.SetProcess( + model, + ffxivLanguage); + + Logger.Info("Sharlayan attached."); + } + } + } + } +} diff --git a/source/kagami/KagamiAddon.cs b/source/kagami/KagamiAddon.cs index 0cfce48..16267ee 100644 --- a/source/kagami/KagamiAddon.cs +++ b/source/kagami/KagamiAddon.cs @@ -1,7 +1,9 @@ using System; using System.IO; using System.Reflection; +using System.Threading.Tasks; using System.Windows.Forms; +using kagami.Helpers; using Prism.Mvvm; using RainbowMage.OverlayPlugin; @@ -36,7 +38,15 @@ public string UpdateMessage public KagamiOverlayConfig Config { get => this.config; - set => this.SetProperty(ref this.config, value); + private set => this.SetProperty(ref this.config, value); + } + + private KagamiOverlay overlay; + + public KagamiOverlay Overlay + { + get => this.overlay; + private set => this.SetProperty(ref this.overlay, value); } public KagamiAddon() @@ -99,11 +109,30 @@ Assembly tryLoadAssembly( public IOverlayConfig CreateOverlayConfigInstance(string name) => this.Config = new KagamiOverlayConfig(name); - public IOverlay CreateOverlayInstance(IOverlayConfig config) => new KagamiOverlay(config as KagamiOverlayConfig); + public IOverlay CreateOverlayInstance(IOverlayConfig config) + { + this.Overlay = new KagamiOverlay(config as KagamiOverlayConfig); + this.Initialize(); + return this.Overlay; + } public void Dispose() { + FFXIVPluginHelper.Instance.Stop(); + SharlayanHelper.Instance.Stop(); + AppDomain.CurrentDomain.AssemblyResolve -= this.CurrentDomain_AssemblyResolve; } + + private void Initialize() + { + Task.Run(async () => + { + await Task.Delay(500); + + FFXIVPluginHelper.Instance.Start(); + SharlayanHelper.Instance.Start(); + }); + } } } diff --git a/source/kagami/KagamiOverlay.cs b/source/kagami/KagamiOverlay.cs index 681e57b..fbfb1f5 100644 --- a/source/kagami/KagamiOverlay.cs +++ b/source/kagami/KagamiOverlay.cs @@ -6,6 +6,8 @@ public class KagamiOverlay : OverlayBase { public KagamiOverlay(KagamiOverlayConfig config) : base(config, config.Name) { + // Loggerにコールバックを仕込む + Logger.LogCallback += this.Log; } protected override void Update() diff --git a/source/kagami/KagamiOverlayConfig.cs b/source/kagami/KagamiOverlayConfig.cs index 9519251..2ea1674 100644 --- a/source/kagami/KagamiOverlayConfig.cs +++ b/source/kagami/KagamiOverlayConfig.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.IO; using System.Runtime.CompilerServices; using System.Xml.Serialization; using RainbowMage.OverlayPlugin; @@ -21,7 +22,7 @@ public KagamiOverlayConfig(string name) } private KagamiOverlayConfig() - : base(null) + : this(null) { } @@ -30,15 +31,57 @@ private KagamiOverlayConfig() private void SubscribeBasePropertiesChanged() { - this.VisibleChanged += (_, __) => this.RaisePropertyChanged(nameof(this.IsVisible)); - this.ClickThruChanged += (_, __) => this.RaisePropertyChanged(nameof(this.IsClickThru)); - this.UrlChanged += (_, __) => this.RaisePropertyChanged(nameof(this.Url)); - this.MaxFrameRateChanged += (_, __) => this.RaisePropertyChanged(nameof(this.MaxFrameRate)); - this.GlobalHotkeyEnabledChanged += (_, __) => this.RaisePropertyChanged(nameof(this.GlobalHotkeyEnabled)); - this.GlobalHotkeyChanged += (_, __) => this.RaisePropertyChanged(nameof(this.GlobalHotkey)); - this.GlobalHotkeyModifiersChanged += (_, __) => this.RaisePropertyChanged(nameof(this.GlobalHotkeyModifiers)); - this.LockChanged += (_, __) => this.RaisePropertyChanged(nameof(this.IsLocked)); - this.GlobalHotkeyTypeChanged += (_, __) => this.RaisePropertyChanged(nameof(this.GlobalHotkeyType)); + this.VisibleChanged += (x, _) => (x as KagamiOverlayConfig).RaisePropertyChanged(nameof(this.IsVisible)); + this.ClickThruChanged += (x, _) => (x as KagamiOverlayConfig).RaisePropertyChanged(nameof(this.IsClickThru)); + this.UrlChanged += (x, _) => (x as KagamiOverlayConfig).RaisePropertyChanged(nameof(this.Url)); + this.MaxFrameRateChanged += (x, _) => (x as KagamiOverlayConfig).RaisePropertyChanged(nameof(this.MaxFrameRate)); + this.GlobalHotkeyEnabledChanged += (x, _) => (x as KagamiOverlayConfig).RaisePropertyChanged(nameof(this.GlobalHotkeyEnabled)); + this.GlobalHotkeyChanged += (x, _) => (x as KagamiOverlayConfig).RaisePropertyChanged(nameof(this.GlobalHotkey)); + this.GlobalHotkeyModifiersChanged += (x, _) => (x as KagamiOverlayConfig).RaisePropertyChanged(nameof(this.GlobalHotkeyModifiers)); + this.LockChanged += (x, _) => (x as KagamiOverlayConfig).RaisePropertyChanged(nameof(this.IsLocked)); + this.GlobalHotkeyTypeChanged += (x, _) => (x as KagamiOverlayConfig).RaisePropertyChanged(nameof(this.GlobalHotkeyType)); + } + + private int bufferSizeOfActionEcho = 30; + + public int BufferSizeOfActionEcho + { + get => this.bufferSizeOfActionEcho; + set => this.SetProperty(ref this.bufferSizeOfActionEcho, value); + } + + private int pollingInterval = 50; + + public int PollingInterval + { + get => this.pollingInterval; + set => this.SetProperty(ref this.pollingInterval, value); + } + + private string logDirectory = Path.GetFullPath(Path.Combine( + KagamiAddon.Instance.ResourcesDirectory, + "..")); + + public string LogDirectory + { + get => this.logDirectory; + set => this.SetProperty(ref this.logDirectory, value); + } + + private bool isGhostMode = false; + + public bool IsGhostMode + { + get => this.isGhostMode; + set => this.SetProperty(ref this.isGhostMode, value); + } + + private string ghostLogFile = string.Empty; + + public string GhostLogFile + { + get => this.ghostLogFile; + set => this.SetProperty(ref this.ghostLogFile, value); } #region INotifyPropertyChanged diff --git a/source/kagami/Logger.cs b/source/kagami/Logger.cs new file mode 100644 index 0000000..9ad7f25 --- /dev/null +++ b/source/kagami/Logger.cs @@ -0,0 +1,17 @@ +using RainbowMage.OverlayPlugin; + +namespace kagami +{ + public static class Logger + { + public delegate void LogDelegate(LogLevel level, string message, params object[] args); + + public static LogDelegate LogCallback { get; set; } + + public static void Error(string mesasge, params object[] args) => LogCallback?.Invoke(LogLevel.Error, mesasge); + + public static void Warn(string mesasge, params object[] args) => LogCallback?.Invoke(LogLevel.Warning, mesasge); + + public static void Info(string mesasge, params object[] args) => LogCallback?.Invoke(LogLevel.Info, mesasge); + } +} diff --git a/source/kagami/Models/ActionCategory.cs b/source/kagami/Models/ActionCategory.cs new file mode 100644 index 0000000..9c906cd --- /dev/null +++ b/source/kagami/Models/ActionCategory.cs @@ -0,0 +1,22 @@ +namespace kagami.Models +{ + public enum ActionCategory + { + Unknown = 0, + AutoAttack, + Spell, + Weaponskill, + Ability, + Item, + DoLAbility, + DoHAbility, + Event, + LimitBreak, + System, + Artillery, + Mount, + Glamour, + ItemManipulaction, + AdrenalineRush, + } +} diff --git a/source/kagami/Models/ActionEchoModel.cs b/source/kagami/Models/ActionEchoModel.cs new file mode 100644 index 0000000..f8beb6f --- /dev/null +++ b/source/kagami/Models/ActionEchoModel.cs @@ -0,0 +1,77 @@ +using System; +using Newtonsoft.Json; +using Prism.Mvvm; + +namespace kagami.Models +{ + public class ActionEchoModel : BindableBase + { + private static long currentSequence = 0; + private static readonly object SequenceLocker = new object(); + + public ActionEchoModel() + { + lock (SequenceLocker) + { + this.Seq = ++currentSequence; + } + } + + [JsonProperty("seq")] + public long Seq { get; private set; } + + private DateTime timestamp; + + [JsonProperty("timestamp")] + public DateTime Timestamp + { + get => this.timestamp; + set => this.SetProperty(ref this.timestamp, value); + } + + private string actor; + + [JsonProperty("actor")] + public string Actor + { + get => this.actor; + set => this.SetProperty(ref this.actor, value); + } + + private uint id; + + [JsonProperty("id")] + public uint ID + { + get => this.id; + set => this.SetProperty(ref this.id, value); + } + + private string name; + + [JsonProperty("name")] + public string Name + { + get => this.name; + set => this.SetProperty(ref this.name, value); + } + + private ActionCategory category; + + [JsonProperty("category")] + public ActionCategory Category + { + get => this.category; + set => this.SetProperty(ref this.category, value); + } + + private float recastTime; + + [JsonProperty("recastTime")] + public float RecastTime + { + get => this.recastTime; + set => this.SetProperty(ref this.recastTime, value); + } + } +} diff --git a/source/kagami/Models/ActionEchoesModel.cs b/source/kagami/Models/ActionEchoesModel.cs new file mode 100644 index 0000000..7799939 --- /dev/null +++ b/source/kagami/Models/ActionEchoesModel.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Advanced_Combat_Tracker; +using Newtonsoft.Json; + +namespace kagami.Models +{ + public class ActionEchoesModel + { + #region Singleton + + private static readonly Lazy LazyInstance = new Lazy(() => new ActionEchoesModel()); + + public static ActionEchoesModel Instance => LazyInstance.Value; + + private ActionEchoesModel() + { + } + + #endregion Singleton + + [JsonIgnore] + public KagamiOverlayConfig Config => KagamiAddon.Instance.Config; + + [JsonProperty("player")] + public string PlayerName { get; set; } + + [JsonProperty("job")] + public string PlayerJob { get; set; } + + [JsonProperty("zone")] + public string Zone => ActGlobals.oFormActMain?.CurrentZone ?? string.Empty; + + [JsonProperty("time")] + public DateTime Time { get; private set; } + + private readonly List echoes = new List(5120); + + [JsonIgnore] + public IReadOnlyList Echoes => this.echoes; + + [JsonProperty("actions")] + public IEnumerable Actions + { + get + { + var source = default(IEnumerable); + lock (this.echoes) + { + source = this.echoes.ToArray(); + } + + var result = ( + from x in source + where + x.Timestamp <= this.Time && + x.Timestamp >= this.Time.AddSeconds(this.Config.BufferSizeOfActionEcho * -1) + select + x).ToArray(); + + return result; + } + } + + public void Clear() + { + lock (this.echoes) + { + this.echoes.Clear(); + } + } + + public void Add(ActionEchoModel model) + { + lock (this.echoes) + { + this.echoes.Add(model); + } + } + + public void AddRange(IEnumerable models) + { + lock (this.echoes) + { + this.echoes.AddRange(models); + } + } + + public string ParseJson() + { + return string.Empty; + } + } +} diff --git a/source/kagami/Models/NetworkAbilityModel.cs b/source/kagami/Models/NetworkAbilityModel.cs new file mode 100644 index 0000000..737f063 --- /dev/null +++ b/source/kagami/Models/NetworkAbilityModel.cs @@ -0,0 +1,81 @@ +using System; +using System.Globalization; +using Prism.Mvvm; + +namespace kagami.Models +{ + public class NetworkAbilityModel : BindableBase + { + public static readonly int Code = 21; + + public static string HexCode => Code.ToString("X2"); + + private string log; + + public string Log + { + get => this.log; + set => this.SetProperty(ref this.log, value); + } + + private DateTime timestamp; + + public DateTime PropertyName + { + get => this.timestamp; + set => this.SetProperty(ref this.timestamp, value); + } + + private DateTime logTimestamp; + + public DateTime LogTimestamp + { + get => this.logTimestamp; + set => this.SetProperty(ref this.logTimestamp, value); + } + + private string actor; + + public string Actor + { + get => this.actor; + set => this.SetProperty(ref this.actor, value); + } + + private uint actionID; + + public uint ActionID + { + get => this.actionID; + set => this.SetProperty(ref this.actionID, value); + } + + private string action; + + public string Action + { + get => this.action; + set => this.SetProperty(ref this.action, value); + } + + public void AnalyzeLog() + { + if (string.IsNullOrEmpty(this.log)) + { + return; + } + + var values = this.log.Split(':'); + + this.Actor = values.Length > 2 ? values[2] : string.Empty; + + var idText = values.Length > 3 ? values[3] : string.Empty; + if (uint.TryParse(idText, NumberStyles.HexNumber, NumberFormatInfo.CurrentInfo, out uint id)) + { + this.ActionID = id; + } + + this.Action = values.Length > 4 ? values[4] : string.Empty; + } + } +} diff --git a/source/kagami/Views/KagamiConfigView.xaml b/source/kagami/Views/KagamiConfigView.xaml index 5f7d415..f350023 100644 --- a/source/kagami/Views/KagamiConfigView.xaml +++ b/source/kagami/Views/KagamiConfigView.xaml @@ -9,7 +9,7 @@ xmlns:prism="http://prismlibrary.com/" xmlns:toolkit="http://schemas.xceed.com/wpf/xaml/toolkit" mc:Ignorable="d" - d:DesignHeight="450" d:DesignWidth="800" + d:DesignWidth="800" Focusable="False" IsTabStop="False" FontFamily="Consolas" @@ -42,10 +42,15 @@ - - - + + + +