diff --git a/Telesto/Context.cs b/Telesto/Context.cs new file mode 100644 index 0000000..c40fb1c --- /dev/null +++ b/Telesto/Context.cs @@ -0,0 +1,19 @@ +using Dalamud.Game.ClientState.Objects.Types; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Telesto +{ + + public class Context + { + + internal Doodle doo = null; + internal GameObject go = null; + + } + +} diff --git a/Telesto/Doodle.cs b/Telesto/Doodle.cs index 6cf8d60..8f1b061 100644 --- a/Telesto/Doodle.cs +++ b/Telesto/Doodle.cs @@ -31,7 +31,7 @@ internal enum CoordinateTypeEnum Waymark } - + internal Doodle doo { get; set; } internal CoordinateTypeEnum ct { get; set; } internal Vector3 cp { get; set; } @@ -48,16 +48,16 @@ internal Vector3 UnadjustedPosition(Plugin p) default: case CoordinateTypeEnum.Screen: return new Vector3( - (float)p.EvaluateNumericExpression(X), - (float)p.EvaluateNumericExpression(Y), - (float)p.EvaluateNumericExpression(Z) + (float)p.EvaluateNumericExpression(doo, X), + (float)p.EvaluateNumericExpression(doo, Y), + (float)p.EvaluateNumericExpression(doo, Z) ); break; case CoordinateTypeEnum.World: return new Vector3( - (float)p.EvaluateNumericExpression(X), - (float)p.EvaluateNumericExpression(Y), - (float)p.EvaluateNumericExpression(Z) + (float)p.EvaluateNumericExpression(doo, X), + (float)p.EvaluateNumericExpression(doo, Y), + (float)p.EvaluateNumericExpression(doo, Z) ); break; case CoordinateTypeEnum.Entity: @@ -81,9 +81,9 @@ internal Vector3 UnadjustedPosition(Plugin p) else { return new Vector3( - (float)p.EvaluateNumericExpression(X), - (float)p.EvaluateNumericExpression(Y), - (float)p.EvaluateNumericExpression(Z) + (float)p.EvaluateNumericExpression(doo, X), + (float)p.EvaluateNumericExpression(doo, Y), + (float)p.EvaluateNumericExpression(doo, Z) ); } break; @@ -119,16 +119,16 @@ internal void RefreshVector(Plugin p) { case CoordinateTypeEnum.Screen: cp = new Vector3( - (float)p.EvaluateNumericExpression(X), - (float)p.EvaluateNumericExpression(Y), - (float)p.EvaluateNumericExpression(Z) + (float)p.EvaluateNumericExpression(doo, X), + (float)p.EvaluateNumericExpression(doo, Y), + (float)p.EvaluateNumericExpression(doo, Z) ); break; case CoordinateTypeEnum.World: cp = p.TranslateToScreen( - p.EvaluateNumericExpression(X), - p.EvaluateNumericExpression(Y), - p.EvaluateNumericExpression(Z) + p.EvaluateNumericExpression(doo, X), + p.EvaluateNumericExpression(doo, Y), + p.EvaluateNumericExpression(doo, Z) ); break; case CoordinateTypeEnum.Entity: @@ -152,9 +152,9 @@ internal void RefreshVector(Plugin p) else { cp = p.TranslateToScreen( - p.EvaluateNumericExpression(X), - p.EvaluateNumericExpression(Y), - p.EvaluateNumericExpression(Z) + p.EvaluateNumericExpression(doo, X), + p.EvaluateNumericExpression(doo, Y), + p.EvaluateNumericExpression(doo, Z) ); } break; @@ -254,6 +254,7 @@ internal enum ExpiryTypeEnum internal Plugin p { get; set; } internal string Name { get; set; } + internal DateTime Created { get; set; } = DateTime.UtcNow; internal DateTime ExpiresAt { get; set; } = DateTime.UtcNow.AddSeconds(5); internal ExpiryTypeEnum ExpiresOn { get; set; } = ExpiryTypeEnum.Timed | ExpiryTypeEnum.NotLoggedIn | ExpiryTypeEnum.OnZoneChange | ExpiryTypeEnum.OnWipe; internal Vector4 col { get; set; } = new Vector4(); @@ -342,10 +343,10 @@ internal virtual bool Update() } } col = new Vector4( - (float)p.EvaluateNumericExpression(R), - (float)p.EvaluateNumericExpression(G), - (float)p.EvaluateNumericExpression(B), - (float)p.EvaluateNumericExpression(A) + (float)p.EvaluateNumericExpression(this, R), + (float)p.EvaluateNumericExpression(this, G), + (float)p.EvaluateNumericExpression(this, B), + (float)p.EvaluateNumericExpression(this, A) ); return true; } diff --git a/Telesto/Doodles/Arrow.cs b/Telesto/Doodles/Arrow.cs index 28df067..25978e7 100644 --- a/Telesto/Doodles/Arrow.cs +++ b/Telesto/Doodles/Arrow.cs @@ -37,12 +37,12 @@ internal override void Initialize(Dictionary d) { base.Initialize(d); Thickness = (d.ContainsKey("thickness") == true) ? d["thickness"].ToString() : "3"; - from = new Coordinate(); + from = new Coordinate() { doo = this }; if (d.ContainsKey("from") == true) { from.Initialize((Dictionary)d["from"]); } - to = new Coordinate(); + to = new Coordinate() { doo = this }; if (d.ContainsKey("to") == true) { to.Initialize((Dictionary)d["to"]); @@ -67,7 +67,7 @@ internal override bool Update() } from.RefreshVector(p); to.RefreshVector(p); - linechonk = (float)p.EvaluateNumericExpression(Thickness); + linechonk = (float)p.EvaluateNumericExpression(this, Thickness); return true; } diff --git a/Telesto/Doodles/Beam.cs b/Telesto/Doodles/Beam.cs index ae5809a..273e345 100644 --- a/Telesto/Doodles/Beam.cs +++ b/Telesto/Doodles/Beam.cs @@ -43,12 +43,12 @@ internal override void Initialize(Dictionary d) Thickness = (d.ContainsKey("thickness") == true) ? d["thickness"].ToString() : "3"; Width = (d.ContainsKey("width") == true) ? d["width"].ToString() : "1"; Length = (d.ContainsKey("length") == true) ? d["length"].ToString() : "1"; - from = new Coordinate(); + from = new Coordinate() { doo = this }; ; if (d.ContainsKey("from") == true) { from.Initialize((Dictionary)d["from"]); } - at = new Coordinate(); + at = new Coordinate() { doo = this }; if (d.ContainsKey("at") == true) { at.Initialize((Dictionary)d["at"]); @@ -73,9 +73,9 @@ internal override bool Update() } from.RefreshVector(p); at.RefreshVector(p); - linechonk = (float)p.EvaluateNumericExpression(Thickness); - widthchonk = (float)p.EvaluateNumericExpression(Width); - lengthchonk = (float)p.EvaluateNumericExpression(Length); + linechonk = (float)p.EvaluateNumericExpression(this, Thickness); + widthchonk = (float)p.EvaluateNumericExpression(this, Width); + lengthchonk = (float)p.EvaluateNumericExpression(this, Length); return true; } diff --git a/Telesto/Doodles/Circle.cs b/Telesto/Doodles/Circle.cs index 566fcec..2169744 100644 --- a/Telesto/Doodles/Circle.cs +++ b/Telesto/Doodles/Circle.cs @@ -42,7 +42,7 @@ internal override void Initialize(Dictionary d) filled = false; bool.TryParse(fill, out bool filledtemp); filled = filledtemp; - position = new Coordinate(); + position = new Coordinate() { doo = this }; if (d.ContainsKey("position") == true) { position.Initialize((Dictionary)d["position"]); @@ -66,8 +66,8 @@ internal override bool Update() return false; } position.RefreshVector(p); - radiuschonk = (float)p.EvaluateNumericExpression(Radius); - linechonk = (float)p.EvaluateNumericExpression(Thickness); + radiuschonk = (float)p.EvaluateNumericExpression(this, Radius); + linechonk = (float)p.EvaluateNumericExpression(this, Thickness); return true; } diff --git a/Telesto/Doodles/Line.cs b/Telesto/Doodles/Line.cs index 18b08d8..0c2d60e 100644 --- a/Telesto/Doodles/Line.cs +++ b/Telesto/Doodles/Line.cs @@ -27,12 +27,12 @@ internal override void Initialize(Dictionary d) { base.Initialize(d); Thickness = (d.ContainsKey("thickness") == true) ? d["thickness"].ToString() : "1"; - start = new Coordinate(); + start = new Coordinate() { doo = this }; if (d.ContainsKey("start") == true) { start.Initialize((Dictionary)d["start"]); } - end = new Coordinate(); + end = new Coordinate() { doo = this }; if (d.ContainsKey("end") == true) { end.Initialize((Dictionary)d["end"]); @@ -47,7 +47,7 @@ internal override bool Update() } start.RefreshVector(p); end.RefreshVector(p); - chonkiness = (float)p.EvaluateNumericExpression(Thickness); + chonkiness = (float)p.EvaluateNumericExpression(this, Thickness); return true; } diff --git a/Telesto/Doodles/Rectangle.cs b/Telesto/Doodles/Rectangle.cs index 436df8b..9e24876 100644 --- a/Telesto/Doodles/Rectangle.cs +++ b/Telesto/Doodles/Rectangle.cs @@ -41,12 +41,12 @@ internal override void Initialize(Dictionary d) filled = false; bool.TryParse(fill, out bool filledtemp); filled = filledtemp; - pos1 = new Coordinate(); + pos1 = new Coordinate() { doo = this }; if (d.ContainsKey("pos1") == true) { pos1.Initialize((Dictionary)d["pos1"]); } - pos2 = new Coordinate(); + pos2 = new Coordinate() { doo = this }; if (d.ContainsKey("pos2") == true) { pos2.Initialize((Dictionary)d["pos2"]); @@ -71,7 +71,7 @@ internal override bool Update() } pos1.RefreshVector(p); pos2.RefreshVector(p); - linechonk = (float)p.EvaluateNumericExpression(Thickness); + linechonk = (float)p.EvaluateNumericExpression(this, Thickness); return true; } diff --git a/Telesto/Doodles/Text.cs b/Telesto/Doodles/Text.cs index 8a22158..185c5cf 100644 --- a/Telesto/Doodles/Text.cs +++ b/Telesto/Doodles/Text.cs @@ -26,7 +26,7 @@ internal override void Initialize(Dictionary d) { base.Initialize(d); Size = (d.ContainsKey("size") == true) ? d["size"].ToString() : "10"; - position = new Coordinate(); + position = new Coordinate() { doo = this }; if (d.ContainsKey("position") == true) { position.Initialize((Dictionary)d["position"]); @@ -41,7 +41,7 @@ internal override bool Update() return false; } position.RefreshVector(p); - chonkiness = (float)p.EvaluateNumericExpression(Size); + chonkiness = (float)p.EvaluateNumericExpression(this, Size); return true; } diff --git a/Telesto/Doodles/Waymark.cs b/Telesto/Doodles/Waymark.cs index a3e7b23..aabdbf2 100644 --- a/Telesto/Doodles/Waymark.cs +++ b/Telesto/Doodles/Waymark.cs @@ -25,7 +25,7 @@ internal override Coordinate GetCoordinateByName(string id) internal override void Initialize(Dictionary d) { base.Initialize(d); - position = new Coordinate(); + position = new Coordinate() { doo = this }; if (d.ContainsKey("position") == true) { position.Initialize((Dictionary)d["position"]); diff --git a/Telesto/Endpoint.cs b/Telesto/Endpoint.cs index 6e75c4a..709136e 100644 --- a/Telesto/Endpoint.cs +++ b/Telesto/Endpoint.cs @@ -140,7 +140,7 @@ public void ThreadProc(object o) byte[] buf = Encoding.UTF8.GetBytes(resp); s.Write(buf, 0, buf.Length); } - plug._sentTelegrams++; + plug._sentResponses++; hctx.Response.StatusCode = 200; } catch (Exception ex) diff --git a/Telesto/Plugin.cs b/Telesto/Plugin.cs index 42f0da5..182f194 100644 --- a/Telesto/Plugin.cs +++ b/Telesto/Plugin.cs @@ -45,15 +45,6 @@ namespace Telesto public sealed class Plugin : IDalamudPlugin { - private class Subscription - { - - public string id { get; set; } - public string type { get; set; } - public string endpoint { get; set; } - - } - private class Response { @@ -230,19 +221,16 @@ public void Dispose() private DalamudPluginInterface _pi { get; init; } private CommandManager _cm { get; init; } - private ChatGui _cg { get; init; } + internal ChatGui _cg { get; init; } private GameGui _gg { get; init; } private ClientState _cs { get; init; } private ObjectTable _ot { get; init; } private PartyList _pl { get; init; } - private Dictionary TransformationIds = new Dictionary(); - private Dictionary HpValues = new Dictionary(); private Dictionary Subscriptions = new Dictionary(); private ManualResetEvent SendPendingEvent = new ManualResetEvent(false); private ManualResetEvent StopEvent = new ManualResetEvent(false); private Thread SendThread = null; - private bool _pollForTransformationIdChanges = false; - private bool _pollForHpChanges = false; + private bool _pollForMemory = false; private bool _sendThreadRunning = false; private DateTime _sendLastTimestamp = DateTime.Now; @@ -259,10 +247,12 @@ public void Dispose() private string _cfgHttpEndpoint = ""; private int _reqServed = 0; + internal int _sentResponses = 0; internal int _sentTelegrams = 0; private int _numDoodles = 0; internal static Regex rex = new Regex(@"\$\{(?[^\}\{\$]*)\}"); internal static Regex rexnum = new Regex(@"\$(?[0-9]+)"); + internal static Regex rexlidx = new Regex(@"(?[^\[]+)\[(?.+?)\]"); internal static Regex rexnump = new Regex(@"\[(?.+?)\]\.(?[a-zA-Z]+)"); private static MathParser mp = new MathParser(); @@ -471,9 +461,9 @@ private void DrawUI() { _cg.PrintError(String.Format("Exception in telegram processing: {0}", ex.Message)); } - if (_pollForTransformationIdChanges == true || _pollForHpChanges == true) + if (_pollForMemory == true) { - PollForCharacterChanges(_pollForTransformationIdChanges, _pollForHpChanges); + PollForMemoryChanges(); } if (_configOpen == false) { @@ -523,7 +513,7 @@ private void DrawUI() ImGui.Separator(); lock (Sends) { - ImGui.Text(String.Format("Sent telegrams: {0} ({1} queued, running={2}, last={3})", _sentTelegrams, Sends.Count, _sendThreadRunning, _sendLastTimestamp)); + ImGui.Text(String.Format("Sent: resp={0} tele={1} ({2} queued, running={3}, last={4})", _sentResponses, _sentTelegrams, Sends.Count, _sendThreadRunning, _sendLastTimestamp)); } lock (Subscriptions) { @@ -532,7 +522,7 @@ private void DrawUI() string typesdesc = String.Join(", ", types); ImGui.Text(String.Format("Active subs: {0} ({1})", activesubs, typesdesc)); } - ImGui.Text(String.Format("Active polls: trid={0} hp={1}", _pollForTransformationIdChanges, _pollForHpChanges)); + ImGui.Text(String.Format("Active polls: mem={0}", _pollForMemory)); if (ImGui.Button("Remove all subs")) { lock (Subscriptions) @@ -974,6 +964,8 @@ private string GetEntityProperty(GameObject go, string prop) { switch (prop.ToLower()) { + case "address": + return go.Address.ToString(); case "name": return go.Name.TextValue; case "job": @@ -1028,7 +1020,7 @@ private string GetEntityProperty(GameObject go, string prop) return ""; } - public string ExpandVariables(string expr) + public string ExpandVariables(Context ctx, string expr) { Match m, mx; string newexpr = expr; @@ -1059,6 +1051,21 @@ public string ExpandVariables(string expr) val = ((long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds).ToString(); found = true; } + else if (x.IndexOf("_addr") == 0) + { + mx = rexlidx.Match(x); + if (mx.Success == true) + { + string idx = mx.Groups["index"].Value; + switch (idx.ToLower()) + { + case "gameobject": + val = ctx.go != null ? ctx.go.Address.ToString() : "0"; + break; + } + found = true; + } + } else if (x.IndexOf("_ffxiventity") == 0) { mx = rexnump.Match(x); @@ -1090,9 +1097,19 @@ public string ExpandVariables(string expr) return newexpr; } - internal double EvaluateNumericExpression(string expr) + internal double EvaluateNumericExpression(Doodle dee, string expr) + { + return EvaluateNumericExpression(new Context() { doo = dee }, expr); + } + + internal double EvaluateNumericExpression(GameObject goo, string expr) { - string exp = ExpandVariables(expr == null ? "" : expr); + return EvaluateNumericExpression(new Context() { go = goo }, expr); + } + + internal double EvaluateNumericExpression(Context ctx, string expr) + { + string exp = ExpandVariables(ctx, expr == null ? "" : expr); lock (mp) { return mp.Parse(exp); @@ -1172,26 +1189,48 @@ private object HandleSubscribe(object o) string id = d["id"].ToString().ToLower(); string type = d["type"].ToString().ToLower(); string endpoint = d["endpoint"].ToString(); - lock (Subscriptions) + switch (type) { - Subscription s = null; - if (Subscriptions.ContainsKey(id) == true) - { - s = Subscriptions[id]; - if (s.type != type) + case "memory": { - TurnSubscriptionTypeOff(s.type); + string start = d["start"].ToString().ToLower(); + string length = d["length"].ToString().ToLower(); + string repr = d.ContainsKey("representation") == true ? d["representation"].ToString().ToLower() : ""; + lock (Subscriptions) + { + Subscription s = null; + Subscriptions.Memory sm = null; + if (Subscriptions.ContainsKey(id) == true) + { + s = Subscriptions[id]; + if (s.type != type) + { + TurnSubscriptionTypeOff(s.type); + s = null; + } + } + if (s == null) + { + sm = new Subscriptions.Memory(); + Subscriptions[id] = sm; + s = sm; + } + else + { + sm = (Subscriptions.Memory)s; + } + sm.start = start; + sm.length = length; + sm.endpoint = endpoint; + sm.representation = repr; + s.id = id; + s.type = type; + s.p = this; + s.first = true; + TurnSubscriptionTypeOn(s.type); + } } - } - else - { - s = new Subscription(); - Subscriptions[id] = s; - } - s.id = id; - s.type = type; - s.endpoint = endpoint; - TurnSubscriptionTypeOn(s.type); + break; } return null; } @@ -1224,11 +1263,8 @@ private void TurnSubscriptionTypeOn(string id) { switch (id) { - case "trid": - _pollForTransformationIdChanges = true; - break; - case "hp": - _pollForHpChanges = true; + case "memory": + _pollForMemory = true; break; } } @@ -1237,11 +1273,8 @@ private void TurnSubscriptionTypeOff(string id) { switch (id) { - case "trid": - _pollForTransformationIdChanges = false; - break; - case "hp": - _pollForHpChanges = false; + case "memory": + _pollForMemory = false; break; } } @@ -1326,117 +1359,58 @@ private void DrawDoodles() } } - private unsafe void PollForCharacterChanges(bool polltrid, bool pollhp) + private unsafe void PollForMemoryChanges() { + List firstsubs = new List(); + Context ctx = new Context(); foreach (GameObject go in _ot) - { - if (pollhp == true && go is BattleChara) + { + ctx.go = go; + foreach (Subscription s in Subscriptions.Values) { - BattleChara c = (BattleChara)go; - uint hp = c.CurrentHp; - lock (HpValues) + if (s.type != "memory") { - if (HpValues.ContainsKey(c.ObjectId) == false) - { - HpValues[c.ObjectId] = hp; - HpChangeNotify(go, 0, hp); - } - else - { - uint oldhp = HpValues[c.ObjectId]; - if (oldhp != hp) - { - HpValues[c.ObjectId] = hp; - HpChangeNotify(go, oldhp, hp); - } - } + continue; } - } - if (polltrid == true && _ot is Character) - { - Character c = (Character)go; - StructCharacter* cx = (StructCharacter*)c.Address; - short trid = cx->TransformationId; - lock (TransformationIds) + if (s.Refresh(ctx) == false) { - if (TransformationIds.ContainsKey(c.ObjectId) == false) - { - TransformationIds[c.ObjectId] = trid; - TransformationChangeNotify(go, 0, trid); - } - else + continue; + } + if (s.first == true) + { + if (firstsubs.Contains(s) == false) { - short oldtrid = TransformationIds[c.ObjectId]; - if (oldtrid != trid) - { - TransformationIds[c.ObjectId] = trid; - TransformationChangeNotify(go, oldtrid, trid); - } + firstsubs.Add(s); } + continue; } - } - } - } - - private void TransformationChangeNotify(GameObject go, short oldtrid, short newtrid) - { - List> notifs; - lock (Subscriptions) - { - notifs = (from ix in Subscriptions.Values where ix.type == "trid" select new Tuple(ix.id, ix.endpoint)).Distinct().ToList(); - } - foreach (var notif in notifs) - { - QueueSendTelegram( - notif.Item2, - JsonSerializer.Serialize( - new Notification() - { - id = 1, - version = 1, - notificationid = notif.Item1, - notificationtype = "trid", - payload = new PropertyChangeNotification() + s.GetRepresentation(ctx, out string oldrep, out string newrep); + Subscriptions.Memory sm = (Subscriptions.Memory)s; + //_cg.Print(String.Format("{0} {1}: old {2} new {3}", go.Address, go.Name.TextValue, oldrep, newrep)); + QueueSendTelegram( + s.endpoint, + JsonSerializer.Serialize( + new Notification() { - objectid = go.ObjectId.ToString("X8"), - name = go.Name.TextValue, - oldvalue = oldtrid.ToString(), - newvalue = newtrid.ToString() + id = 1, + version = 1, + notificationid = s.id, + notificationtype = "memory", + payload = new PropertyChangeNotification() + { + objectid = go.ObjectId.ToString("X8"), + name = go.Name.TextValue, + oldvalue = oldrep, + newvalue = newrep + } } - } - ) - ); - } - } - - private void HpChangeNotify(GameObject go, uint oldhp, uint newhp) - { - List> notifs; - lock (Subscriptions) - { - notifs = (from ix in Subscriptions.Values where ix.type == "hp" select new Tuple(ix.id, ix.endpoint)).Distinct().ToList(); + ) + ); + } } - foreach (var notif in notifs) + foreach (Subscription s in firstsubs) { - QueueSendTelegram( - notif.Item2, - JsonSerializer.Serialize( - new Notification() - { - id = 1, - version = 1, - notificationid = notif.Item1, - notificationtype = "hp", - payload = new PropertyChangeNotification() - { - objectid = go.ObjectId.ToString("X8"), - name = go.Name.TextValue, - oldvalue = oldhp.ToString(), - newvalue = newhp.ToString() - } - } - ) - ); + s.first = false; } } diff --git a/Telesto/Subscription.cs b/Telesto/Subscription.cs new file mode 100644 index 0000000..0d5412e --- /dev/null +++ b/Telesto/Subscription.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Telesto +{ + + internal abstract class Subscription + { + + internal Plugin p { get; set; } + public string id { get; set; } + public string type { get; set; } + public string endpoint { get; set; } + public bool first { get; set; } + + internal abstract bool Refresh(Context ctx); + internal abstract void GetRepresentation(Context ctx, out string oldrep, out string newrep); + + } + +} diff --git a/Telesto/Subscriptions/Memory.cs b/Telesto/Subscriptions/Memory.cs new file mode 100644 index 0000000..ed88f29 --- /dev/null +++ b/Telesto/Subscriptions/Memory.cs @@ -0,0 +1,147 @@ +using Dalamud.Game.ClientState.Objects.Types; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace Telesto.Subscriptions +{ + + internal class Memory : Subscription + { + + public string start { get; set; } + public string length { get; set; } + public string representation { get; set; } + + internal nint startaddr { get; set; } + internal int bytelen { get; set; } + + private class State + { + + public byte[] olddata { get; set; } + public byte[] newdata { get; set; } + + } + + private Dictionary states = new Dictionary(); + + internal override bool Refresh(Context ctx) + { + if (ctx.go == null) + { + return false; + } + nint baseaddr = ctx.go.Address; + State s = null; + if (states.ContainsKey(baseaddr) == true) + { + s = states[baseaddr]; + } + else + { + s = new State(); + states[baseaddr] = s; + } + s.olddata = s.newdata; + startaddr = (nint)p.EvaluateNumericExpression(ctx, start); + bytelen = (int)p.EvaluateNumericExpression(ctx, length); + byte[] tenp = new byte[bytelen]; + try + { + Marshal.Copy(startaddr, tenp, 0, bytelen); + } + catch (Exception) + { + return false; + } + s.newdata = tenp; + if (s.olddata == null) + { + return true; + } + if (s.olddata.Length != s.newdata.Length) + { + return true; + } + ReadOnlySpan a = s.olddata; + ReadOnlySpan b = s.newdata; + return (a.SequenceEqual(b) == false); + } + + internal override void GetRepresentation(Context ctx, out string oldrep, out string newrep) + { + if (ctx.go == null) + { + oldrep = ""; + newrep = ""; + return; + } + State s = null; + nint baseaddr = ctx.go.Address; + if (states.ContainsKey(baseaddr) == true) + { + s = states[baseaddr]; + } + else + { + oldrep = ""; + newrep = ""; + return; + } + string actrep = ""; + if (representation == "") + { + switch (bytelen) + { + case 1: + actrep = "byte"; + break; + case 2: + actrep = "ushort"; + break; + case 4: + actrep = "uint"; + break; + case 8: + actrep = "ulong"; + break; + default: + actrep = "string"; + break; + } + } + else + { + actrep = representation; + } + object convold = s.olddata != null ? ConvertToType(s.olddata, actrep) : null; + object convnew = ConvertToType(s.newdata, actrep); + oldrep = convold != null ? convold.ToString() : ""; + newrep = convnew.ToString(); + } + + internal object ConvertToType(byte[] buf, string type) + { + switch (type.ToLower()) + { + case "bool": return BitConverter.ToBoolean(buf); + case "byte": return buf[0]; + case "char": return BitConverter.ToChar(buf); + case "double": return BitConverter.ToDouble(buf); + case "short": return BitConverter.ToInt16(buf); + case "int": return BitConverter.ToInt32(buf); + case "long": return BitConverter.ToInt64(buf); + case "float": return BitConverter.ToSingle(buf); + case "ushort": return BitConverter.ToUInt16(buf); + case "uint": return BitConverter.ToUInt32(buf); + case "ulong": return BitConverter.ToUInt64(buf); + case "string": return BitConverter.ToString(buf); + } + return buf; + } + + } + +}