diff --git a/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatch.cs b/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatch.cs
new file mode 100644
index 0000000000..cb5f59d5e6
--- /dev/null
+++ b/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatch.cs
@@ -0,0 +1,72 @@
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Client.Common
+{
+ ///
+ /// Represents a Ram address for watching in the
+ /// With the minimal details necessary for searching
+ ///
+ internal interface IMiniWatch
+ {
+ long Address { get; }
+ long Previous { get; } // do not store sign extended variables in here.
+ void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian);
+ }
+
+ internal sealed class MiniByteWatch : IMiniWatch
+ {
+ public long Address { get; }
+ private byte _previous;
+
+ public MiniByteWatch(MemoryDomain domain, long addr)
+ {
+ Address = addr;
+ _previous = domain.PeekByte(Address % domain.Size);
+ }
+
+ public long Previous => _previous;
+
+ public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
+ {
+ _previous = domain.PeekByte(Address % domain.Size);
+ }
+ }
+
+ internal sealed class MiniWordWatch : IMiniWatch
+ {
+ public long Address { get; }
+ private ushort _previous;
+
+ public MiniWordWatch(MemoryDomain domain, long addr, bool bigEndian)
+ {
+ Address = addr;
+ _previous = domain.PeekUshort(Address % domain.Size, bigEndian);
+ }
+
+ public long Previous => _previous;
+
+ public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
+ {
+ _previous = domain.PeekUshort(Address, bigEndian);
+ }
+ }
+
+ internal sealed class MiniDWordWatch : IMiniWatch
+ {
+ public long Address { get; }
+ private uint _previous;
+
+ public MiniDWordWatch(MemoryDomain domain, long addr, bool bigEndian)
+ {
+ Address = addr;
+ _previous = domain.PeekUint(Address % domain.Size, bigEndian);
+ }
+
+ public long Previous => _previous;
+
+ public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
+ {
+ _previous = domain.PeekUint(Address, bigEndian);
+ }
+ }
+}
diff --git a/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatchDetails.cs b/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatchDetails.cs
new file mode 100644
index 0000000000..cc0f5a38f3
--- /dev/null
+++ b/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatchDetails.cs
@@ -0,0 +1,177 @@
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Client.Common
+{
+ ///
+ /// Represents a but with added details
+ /// to do change tracking. These types add more information but at a cost of
+ /// having to poll the ram address on every update
+ ///
+ internal interface IMiniWatchDetails : IMiniWatch
+ {
+ int ChangeCount { get; }
+
+ void ClearChangeCount();
+ void Update(PreviousType type, MemoryDomain domain, bool bigEndian);
+ }
+
+ internal sealed class MiniByteWatchDetailed : IMiniWatchDetails
+ {
+ public long Address { get; }
+
+ private byte _previous;
+ private byte _prevFrame;
+
+ public MiniByteWatchDetailed(MemoryDomain domain, long addr)
+ {
+ Address = addr;
+ SetPreviousToCurrent(domain, false);
+ }
+
+ public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
+ {
+ _previous = _prevFrame = domain.PeekByte(Address % domain.Size);
+ }
+
+ public long Previous => _previous;
+
+ public int ChangeCount { get; private set; }
+
+ public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
+ {
+ var value = domain.PeekByte(Address % domain.Size);
+
+ if (value != _prevFrame)
+ {
+ ChangeCount++;
+ }
+
+ switch (type)
+ {
+ case PreviousType.Original:
+ case PreviousType.LastSearch:
+ break;
+ case PreviousType.LastFrame:
+ _previous = _prevFrame;
+ break;
+ case PreviousType.LastChange:
+ if (_prevFrame != value)
+ {
+ _previous = _prevFrame;
+ }
+
+ break;
+ }
+
+ _prevFrame = value;
+ }
+
+ public void ClearChangeCount() => ChangeCount = 0;
+ }
+
+ internal sealed class MiniWordWatchDetailed : IMiniWatch, IMiniWatchDetails
+ {
+ public long Address { get; }
+
+ private ushort _previous;
+ private ushort _prevFrame;
+
+ public MiniWordWatchDetailed(MemoryDomain domain, long addr, bool bigEndian)
+ {
+ Address = addr;
+ SetPreviousToCurrent(domain, bigEndian);
+ }
+
+ public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
+ {
+ _previous = _prevFrame = domain.PeekUshort(Address % domain.Size, bigEndian);
+ }
+
+ public long Previous => _previous;
+
+ public int ChangeCount { get; private set; }
+
+ public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
+ {
+ var value = domain.PeekUshort(Address % domain.Size, bigEndian);
+ if (value != Previous)
+ {
+ ChangeCount++;
+ }
+
+ switch (type)
+ {
+ case PreviousType.Original:
+ case PreviousType.LastSearch:
+ break;
+ case PreviousType.LastFrame:
+ _previous = _prevFrame;
+ break;
+ case PreviousType.LastChange:
+ if (_prevFrame != value)
+ {
+ _previous = _prevFrame;
+ }
+
+ break;
+ }
+
+ _prevFrame = value;
+ }
+
+ public void ClearChangeCount() => ChangeCount = 0;
+ }
+
+ internal sealed class MiniDWordWatchDetailed : IMiniWatch, IMiniWatchDetails
+ {
+ public long Address { get; }
+
+ private uint _previous;
+ private uint _prevFrame;
+
+ public MiniDWordWatchDetailed(MemoryDomain domain, long addr, bool bigEndian)
+ {
+ Address = addr;
+ SetPreviousToCurrent(domain, bigEndian);
+ }
+
+ public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
+ {
+ _previous = _prevFrame = domain.PeekUint(Address % domain.Size, bigEndian);
+ }
+
+ public long Previous => (int)_previous;
+
+ public int ChangeCount { get; private set; }
+
+ public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
+ {
+ var value = domain.PeekUint(Address % domain.Size, bigEndian);
+ if (value != Previous)
+ {
+ ChangeCount++;
+ }
+
+ switch (type)
+ {
+ case PreviousType.Original:
+ case PreviousType.LastSearch:
+ break;
+ case PreviousType.LastFrame:
+ _previous = _prevFrame;
+ break;
+ case PreviousType.LastChange:
+ if (_prevFrame != value)
+ {
+ _previous = _prevFrame;
+ }
+
+ break;
+ }
+
+ _prevFrame = value;
+ }
+
+ public void ClearChangeCount() => ChangeCount = 0;
+ }
+}
diff --git a/BizHawk.Client.Common/tools/RamSearchEngine.cs b/BizHawk.Client.Common/tools/RamSearchEngine/RamSearchEngine.cs
similarity index 79%
rename from BizHawk.Client.Common/tools/RamSearchEngine.cs
rename to BizHawk.Client.Common/tools/RamSearchEngine/RamSearchEngine.cs
index 0bc8882e40..ca3c041a11 100644
--- a/BizHawk.Client.Common/tools/RamSearchEngine.cs
+++ b/BizHawk.Client.Common/tools/RamSearchEngine/RamSearchEngine.cs
@@ -1,1059 +1,814 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-using BizHawk.Common;
-using BizHawk.Common.CollectionExtensions;
-using BizHawk.Emulation.Common;
-
-// ReSharper disable PossibleInvalidCastExceptionInForeachLoop
-namespace BizHawk.Client.Common
-{
- public class RamSearchEngine
- {
- public enum ComparisonOperator
- {
- Equal, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual, NotEqual, DifferentBy
- }
-
- public enum Compare
- {
- Previous, SpecificValue, SpecificAddress, Changes, Difference
- }
-
- private Compare _compareTo = Compare.Previous;
-
- private List _watchList = new List();
- private readonly Settings _settings;
- private readonly UndoHistory _history = new UndoHistory(true);
- private bool _isSorted = true; // Tracks whether or not the list is sorted by address, if it is, binary search can be used for finding watches
-
- public RamSearchEngine(Settings settings, IMemoryDomains memoryDomains)
- {
- _settings = new Settings(memoryDomains)
- {
- Mode = settings.Mode,
- Domain = settings.Domain,
- Size = settings.Size,
- CheckMisAligned = settings.CheckMisAligned,
- Type = settings.Type,
- BigEndian = settings.BigEndian,
- PreviousType = settings.PreviousType
- };
- }
-
- public RamSearchEngine(Settings settings, IMemoryDomains memoryDomains, Compare compareTo, long? compareValue, int? differentBy)
- : this(settings, memoryDomains)
- {
- _compareTo = compareTo;
- DifferentBy = differentBy;
- CompareValue = compareValue;
- }
-
- #region API
-
- public IEnumerable OutOfRangeAddress => _watchList
- .Where(watch => watch.Address >= Domain.Size)
- .Select(watch => watch.Address);
-
- public void Start()
- {
- _history.Clear();
- var domain = _settings.Domain;
- var listSize = domain.Size;
- if (!_settings.CheckMisAligned)
- {
- listSize /= (int)_settings.Size;
- }
-
- _watchList = new List((int)listSize);
-
- switch (_settings.Size)
- {
- default:
- case WatchSize.Byte:
- if (_settings.Mode == Settings.SearchMode.Detailed)
- {
- for (int i = 0; i < domain.Size; i++)
- {
- _watchList.Add(new MiniByteWatchDetailed(domain, i));
- }
- }
- else
- {
- for (int i = 0; i < domain.Size; i++)
- {
- _watchList.Add(new MiniByteWatch(domain, i));
- }
- }
-
- break;
- case WatchSize.Word:
- if (_settings.Mode == Settings.SearchMode.Detailed)
- {
- for (int i = 0; i < domain.Size - 1; i += _settings.CheckMisAligned ? 1 : 2)
- {
- _watchList.Add(new MiniWordWatchDetailed(domain, i, _settings.BigEndian));
- }
- }
- else
- {
- for (int i = 0; i < domain.Size - 1; i += _settings.CheckMisAligned ? 1 : 2)
- {
- _watchList.Add(new MiniWordWatch(domain, i, _settings.BigEndian));
- }
- }
-
- break;
- case WatchSize.DWord:
- if (_settings.Mode == Settings.SearchMode.Detailed)
- {
- for (int i = 0; i < domain.Size - 3; i += _settings.CheckMisAligned ? 1 : 4)
- {
- _watchList.Add(new MiniDWordWatchDetailed(domain, i, _settings.BigEndian));
- }
- }
- else
- {
- for (int i = 0; i < domain.Size - 3; i += _settings.CheckMisAligned ? 1 : 4)
- {
- _watchList.Add(new MiniDWordWatch(domain, i, _settings.BigEndian));
- }
- }
-
- break;
- }
- }
-
- ///
- /// Exposes the current watch state based on index
- ///
- public Watch this[int index]
- {
- get
- {
- if (_settings.Mode == Settings.SearchMode.Detailed)
- {
- return Watch.GenerateWatch(
- _settings.Domain,
- _watchList[index].Address,
- _settings.Size,
- _settings.Type,
- _settings.BigEndian,
- "",
- 0,
- _watchList[index].Previous,
- ((IMiniWatchDetails)_watchList[index]).ChangeCount);
- }
-
- return Watch.GenerateWatch(
- _settings.Domain,
- _watchList[index].Address,
- _settings.Size,
- _settings.Type,
- _settings.BigEndian,
- "",
- 0,
- _watchList[index].Previous);
- }
- }
-
- public int DoSearch()
- {
- int before = _watchList.Count;
-
- _watchList = _compareTo switch
- {
- Compare.Previous => ComparePrevious(_watchList).ToList(),
- Compare.SpecificValue => CompareSpecificValue(_watchList).ToList(),
- Compare.SpecificAddress => CompareSpecificAddress(_watchList).ToList(),
- Compare.Changes => CompareChanges(_watchList).ToList(),
- Compare.Difference => CompareDifference(_watchList).ToList(),
- _ => ComparePrevious(_watchList).ToList()
- };
-
- if (_settings.PreviousType == PreviousType.LastSearch)
- {
- SetPreviousToCurrent();
- }
-
- if (UndoEnabled)
- {
- _history.AddState(_watchList);
- }
-
- return before - _watchList.Count;
- }
-
- public bool Preview(long address)
- {
- var listOfOne = Enumerable.Repeat(_isSorted
- ? _watchList.BinarySearch(w => w.Address, address)
- : _watchList.FirstOrDefault(w => w.Address == address), 1);
-
- return _compareTo switch
- {
- Compare.Previous => !ComparePrevious(listOfOne).Any(),
- Compare.SpecificValue => !CompareSpecificValue(listOfOne).Any(),
- Compare.SpecificAddress => !CompareSpecificAddress(listOfOne).Any(),
- Compare.Changes => !CompareChanges(listOfOne).Any(),
- Compare.Difference => !CompareDifference(listOfOne).Any(),
- _ => !ComparePrevious(listOfOne).Any()
- };
- }
-
- public int Count => _watchList.Count;
-
- public Settings.SearchMode Mode => _settings.Mode;
-
- public MemoryDomain Domain => _settings.Domain;
-
- /// (from setter) is and is not
- public Compare CompareTo
- {
- get => _compareTo;
-
- set
- {
- if (CanDoCompareType(value))
- {
- _compareTo = value;
- }
- else
- {
- throw new InvalidOperationException();
- }
- }
- }
-
- public long? CompareValue { get; set; }
-
- public ComparisonOperator Operator { get; set; }
-
- // zero 07-sep-2014 - this isn't ideal. but don't bother changing it (to a long, for instance) until it can support floats. maybe store it as a double here.
- public int? DifferentBy { get; set; }
-
- public void Update()
- {
- if (_settings.Mode == Settings.SearchMode.Detailed)
- {
- foreach (IMiniWatchDetails watch in _watchList)
- {
- watch.Update(_settings.PreviousType, _settings.Domain, _settings.BigEndian);
- }
- }
- }
-
- public void SetType(DisplayType type)
- {
- _settings.Type = type;
- }
-
- public void SetEndian(bool bigEndian)
- {
- _settings.BigEndian = bigEndian;
- }
-
- /// is and is
- public void SetPreviousType(PreviousType type)
- {
- if (_settings.Mode == Settings.SearchMode.Fast)
- {
- if (type == PreviousType.LastFrame)
- {
- throw new InvalidOperationException();
- }
- }
-
- _settings.PreviousType = type;
- }
-
- public void SetPreviousToCurrent()
- {
- _watchList.ForEach(w => w.SetPreviousToCurrent(_settings.Domain, _settings.BigEndian));
- }
-
- public void ClearChangeCounts()
- {
- if (_settings.Mode == Settings.SearchMode.Detailed)
- {
- foreach (var watch in _watchList.Cast())
- {
- watch.ClearChangeCount();
- }
- }
- }
-
- ///
- /// Remove a set of watches
- /// However, this should not be used with large data sets (100k or more) as it uses a contains logic to perform the task
- ///
- public void RemoveSmallWatchRange(IEnumerable watches)
- {
- if (UndoEnabled)
- {
- _history.AddState(_watchList);
- }
-
- var addresses = watches.Select(w => w.Address);
- _watchList.RemoveAll(w => addresses.Contains(w.Address));
- }
-
- public void RemoveRange(IEnumerable indices)
- {
- if (UndoEnabled)
- {
- _history.AddState(_watchList);
- }
-
- var removeList = indices.Select(i => _watchList[i]); // This will fail after int.MaxValue but RAM Search fails on domains that large anyway
- _watchList = _watchList.Except(removeList).ToList();
- }
-
- public void AddRange(List addresses, bool append)
- {
- if (!append)
- {
- _watchList.Clear();
- }
-
- switch (_settings.Size)
- {
- default:
- case WatchSize.Byte:
- if (_settings.Mode == Settings.SearchMode.Detailed)
- {
- foreach (var addr in addresses)
- {
- _watchList.Add(new MiniByteWatchDetailed(_settings.Domain, addr));
- }
- }
- else
- {
- foreach (var addr in addresses)
- {
- _watchList.Add(new MiniByteWatch(_settings.Domain, addr));
- }
- }
-
- break;
- case WatchSize.Word:
- if (_settings.Mode == Settings.SearchMode.Detailed)
- {
- foreach (var addr in addresses)
- {
- _watchList.Add(new MiniWordWatchDetailed(_settings.Domain, addr, _settings.BigEndian));
- }
- }
- else
- {
- foreach (var addr in addresses)
- {
- _watchList.Add(new MiniWordWatch(_settings.Domain, addr, _settings.BigEndian));
- }
- }
-
- break;
- case WatchSize.DWord:
- if (_settings.Mode == Settings.SearchMode.Detailed)
- {
- foreach (var addr in addresses)
- {
- _watchList.Add(new MiniDWordWatchDetailed(_settings.Domain, addr, _settings.BigEndian));
- }
- }
- else
- {
- foreach (var addr in addresses)
- {
- _watchList.Add(new MiniDWordWatch(_settings.Domain, addr, _settings.BigEndian));
- }
- }
-
- break;
- }
- }
-
- public void Sort(string column, bool reverse)
- {
- _isSorted = false;
- switch (column)
- {
- case WatchList.ADDRESS:
- if (reverse)
- {
- _watchList = _watchList.OrderByDescending(w => w.Address).ToList();
- }
- else
- {
- _watchList = _watchList.OrderBy(w => w.Address).ToList();
- _isSorted = true;
- }
-
- break;
- case WatchList.VALUE:
- _watchList = reverse
- ? _watchList.OrderByDescending(w => GetValue(w.Address)).ToList()
- : _watchList.OrderBy(w => GetValue(w.Address)).ToList();
-
- break;
- case WatchList.PREV:
- _watchList = reverse
- ? _watchList.OrderByDescending(w => w.Previous).ToList()
- : _watchList.OrderBy(w => w.Previous).ToList();
-
- break;
- case WatchList.CHANGES:
- if (_settings.Mode == Settings.SearchMode.Detailed)
- {
- if (reverse)
- {
- _watchList = _watchList
- .Cast()
- .OrderByDescending(w => w.ChangeCount)
- .Cast().ToList();
- }
- else
- {
- _watchList = _watchList
- .Cast()
- .OrderBy(w => w.ChangeCount)
- .Cast().ToList();
- }
- }
-
- break;
- case WatchList.DIFF:
- _watchList = reverse
- ? _watchList.OrderByDescending(w => GetValue(w.Address) - w.Previous).ToList()
- : _watchList.OrderBy(w => GetValue(w.Address) - w.Previous).ToList();
-
- break;
- }
- }
-
- #endregion
-
- #region Undo API
-
- public bool UndoEnabled { get; set; }
-
-
- public bool CanUndo => UndoEnabled && _history.CanUndo;
-
- public bool CanRedo => UndoEnabled && _history.CanRedo;
-
- public void ClearHistory()
- {
- _history.Clear();
- }
-
- public int Undo()
- {
- int origCount = _watchList.Count;
- if (UndoEnabled)
- {
- _watchList = _history.Undo().ToList();
- return _watchList.Count - origCount;
- }
-
- return _watchList.Count;
- }
-
- public int Redo()
- {
- int origCount = _watchList.Count;
- if (UndoEnabled)
- {
- _watchList = _history.Redo().ToList();
- return origCount - _watchList.Count;
- }
-
- return _watchList.Count;
- }
-
- #endregion
-
- #region Comparisons
-
- private IEnumerable ComparePrevious(IEnumerable watchList)
- {
- switch (Operator)
- {
- default:
- case ComparisonOperator.Equal:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) == ToFloat(w.Previous))
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) == SignExtendAsNeeded(w.Previous));
- case ComparisonOperator.NotEqual:
- return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) != SignExtendAsNeeded(w.Previous));
- case ComparisonOperator.GreaterThan:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) > ToFloat(w.Previous))
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) > SignExtendAsNeeded(w.Previous));
- case ComparisonOperator.GreaterThanEqual:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) >= ToFloat(w.Previous))
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) >= SignExtendAsNeeded(w.Previous));
-
- case ComparisonOperator.LessThan:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) < ToFloat(w.Previous))
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) < SignExtendAsNeeded(w.Previous));
- case ComparisonOperator.LessThanEqual:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) <= ToFloat(w.Previous))
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) <= SignExtendAsNeeded(w.Previous));
- case ComparisonOperator.DifferentBy:
- if (DifferentBy.HasValue)
- {
- var differentBy = DifferentBy.Value;
- if (_settings.Type == DisplayType.Float)
- {
- return watchList.Where(w => ToFloat(GetValue(w.Address)) + differentBy == ToFloat(w.Previous)
- || ToFloat(GetValue(w.Address)) - differentBy == ToFloat(w.Previous));
- }
-
- return watchList.Where(w =>
- {
- long val = SignExtendAsNeeded(GetValue(w.Address));
- long prev = SignExtendAsNeeded(w.Previous);
- return val + differentBy == prev
- || val - differentBy == prev;
- });
- }
- else
- {
- throw new InvalidOperationException();
- }
- }
- }
-
- private IEnumerable CompareSpecificValue(IEnumerable watchList)
- {
- if (CompareValue.HasValue)
- {
- var compareValue = CompareValue.Value;
- switch (Operator)
- {
- default:
- case ComparisonOperator.Equal:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) == ToFloat(compareValue))
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) == SignExtendAsNeeded(CompareValue.Value));
- case ComparisonOperator.NotEqual:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) != ToFloat(compareValue))
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) != SignExtendAsNeeded(compareValue));
- case ComparisonOperator.GreaterThan:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) > ToFloat(compareValue))
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) > SignExtendAsNeeded(compareValue));
- case ComparisonOperator.GreaterThanEqual:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) >= ToFloat(compareValue))
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) >= SignExtendAsNeeded(compareValue));
- case ComparisonOperator.LessThan:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) < ToFloat(compareValue))
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) < SignExtendAsNeeded(compareValue));
-
- case ComparisonOperator.LessThanEqual:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) <= ToFloat(compareValue))
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) <= SignExtendAsNeeded(compareValue));
-
- case ComparisonOperator.DifferentBy:
- if (DifferentBy.HasValue)
- {
- var differentBy = DifferentBy.Value;
- if (_settings.Type == DisplayType.Float)
- {
- return watchList.Where(w => ToFloat(GetValue(w.Address)) + differentBy == compareValue
- || ToFloat(GetValue(w.Address)) - differentBy == compareValue);
- }
-
- return watchList.Where(w
- => SignExtendAsNeeded(GetValue(w.Address)) + differentBy == compareValue
- || SignExtendAsNeeded(GetValue(w.Address)) - differentBy == compareValue);
- }
-
- throw new InvalidOperationException();
- }
- }
-
- throw new InvalidOperationException();
- }
-
- private IEnumerable CompareSpecificAddress(IEnumerable watchList)
- {
- if (CompareValue.HasValue)
- {
- var compareValue = CompareValue.Value;
- switch (Operator)
- {
- default:
- case ComparisonOperator.Equal:
- return watchList.Where(w => w.Address == compareValue);
- case ComparisonOperator.NotEqual:
- return watchList.Where(w => w.Address != compareValue);
- case ComparisonOperator.GreaterThan:
- return watchList.Where(w => w.Address > compareValue);
- case ComparisonOperator.GreaterThanEqual:
- return watchList.Where(w => w.Address >= compareValue);
- case ComparisonOperator.LessThan:
- return watchList.Where(w => w.Address < compareValue);
- case ComparisonOperator.LessThanEqual:
- return watchList.Where(w => w.Address <= compareValue);
- case ComparisonOperator.DifferentBy:
- if (DifferentBy.HasValue)
- {
- return watchList.Where(w => w.Address + DifferentBy.Value == compareValue
- || w.Address - DifferentBy.Value == compareValue);
- }
-
- throw new InvalidOperationException();
- }
- }
-
- throw new InvalidOperationException();
- }
-
- private IEnumerable CompareChanges(IEnumerable watchList)
- {
- if (_settings.Mode == Settings.SearchMode.Detailed && CompareValue.HasValue)
- {
- var compareValue = CompareValue.Value;
- switch (Operator)
- {
- default:
- case ComparisonOperator.Equal:
- return watchList
- .Cast()
- .Where(w => w.ChangeCount == compareValue)
- .Cast();
- case ComparisonOperator.NotEqual:
- return watchList
- .Cast()
- .Where(w => w.ChangeCount != compareValue)
- .Cast();
- case ComparisonOperator.GreaterThan:
- return watchList
- .Cast()
- .Where(w => w.ChangeCount > compareValue)
- .Cast();
- case ComparisonOperator.GreaterThanEqual:
- return watchList
- .Cast()
- .Where(w => w.ChangeCount >= compareValue)
- .Cast();
- case ComparisonOperator.LessThan:
- return watchList
- .Cast()
- .Where(w => w.ChangeCount < compareValue)
- .Cast();
- case ComparisonOperator.LessThanEqual:
- return watchList
- .Cast()
- .Where(w => w.ChangeCount <= compareValue)
- .Cast();
- case ComparisonOperator.DifferentBy:
- if (DifferentBy.HasValue)
- {
- return watchList
- .Cast()
- .Where(w => w.ChangeCount + DifferentBy.Value == compareValue
- || w.ChangeCount - DifferentBy.Value == compareValue)
- .Cast();
- }
-
- throw new InvalidOperationException();
- }
- }
-
- throw new InvalidCastException();
- }
-
- private IEnumerable CompareDifference(IEnumerable watchList)
- {
- if (CompareValue.HasValue)
- {
- var compareValue = CompareValue.Value;
- switch (Operator)
- {
- default:
- case ComparisonOperator.Equal:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) - ToFloat(w.Previous) == compareValue)
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) == compareValue);
- case ComparisonOperator.NotEqual:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) - w.Previous != compareValue)
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) != compareValue);
- case ComparisonOperator.GreaterThan:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) - w.Previous > compareValue)
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) > compareValue);
- case ComparisonOperator.GreaterThanEqual:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) - w.Previous >= compareValue)
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) >= compareValue);
- case ComparisonOperator.LessThan:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) - w.Previous < compareValue)
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) < compareValue);
- case ComparisonOperator.LessThanEqual:
- return _settings.Type == DisplayType.Float
- ? watchList.Where(w => ToFloat(GetValue(w.Address)) - w.Previous <= compareValue)
- : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) <= compareValue);
- case ComparisonOperator.DifferentBy:
- if (DifferentBy.HasValue)
- {
- var differentBy = DifferentBy.Value;
- if (_settings.Type == DisplayType.Float)
- {
- return watchList.Where(w => ToFloat(GetValue(w.Address)) - w.Previous + differentBy == compareValue
- || ToFloat(GetValue(w.Address)) - w.Previous - differentBy == w.Previous);
- }
-
- return watchList.Where(w
- => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) + differentBy == compareValue
- || SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) - differentBy == compareValue);
- }
-
- throw new InvalidOperationException();
- }
- }
-
- throw new InvalidCastException();
- }
-
- #endregion
-
- #region Private parts
-
- private static float ToFloat(long val)
- {
- var bytes = BitConverter.GetBytes((int)val);
- return BitConverter.ToSingle(bytes, 0);
- }
-
- private long SignExtendAsNeeded(long val)
- {
- if (_settings.Type != DisplayType.Signed)
- {
- return val;
- }
-
- return _settings.Size switch
- {
- WatchSize.Byte => (sbyte) val,
- WatchSize.Word => (short) val,
- WatchSize.DWord => (int) val,
- _ => (sbyte) val
- };
- }
-
- private long GetValue(long addr)
- {
- // do not return sign extended variables from here.
- return _settings.Size switch
- {
- WatchSize.Byte => _settings.Domain.PeekByte(addr % Domain.Size),
- WatchSize.Word => _settings.Domain.PeekUshort(addr % Domain.Size, _settings.BigEndian),
- WatchSize.DWord => _settings.Domain.PeekUint(addr % Domain.Size, _settings.BigEndian),
- _ => _settings.Domain.PeekByte(addr % Domain.Size)
- };
- }
-
- private bool CanDoCompareType(Compare compareType)
- {
- return _settings.Mode switch
- {
- Settings.SearchMode.Detailed => true,
- Settings.SearchMode.Fast => (compareType != Compare.Changes),
- _ => true
- };
- }
-
- #endregion
-
- #region Classes
-
- private interface IMiniWatch
- {
- long Address { get; }
- long Previous { get; } // do not store sign extended variables in here.
- void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian);
- }
-
- private interface IMiniWatchDetails
- {
- int ChangeCount { get; }
-
- void ClearChangeCount();
- void Update(PreviousType type, MemoryDomain domain, bool bigEndian);
- }
-
- private sealed class MiniByteWatch : IMiniWatch
- {
- public long Address { get; }
- private byte _previous;
-
- public MiniByteWatch(MemoryDomain domain, long addr)
- {
- Address = addr;
- _previous = domain.PeekByte(Address % domain.Size);
- }
-
- public long Previous => _previous;
-
- public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
- {
- _previous = domain.PeekByte(Address % domain.Size);
- }
- }
-
- private sealed class MiniWordWatch : IMiniWatch
- {
- public long Address { get; }
- private ushort _previous;
-
- public MiniWordWatch(MemoryDomain domain, long addr, bool bigEndian)
- {
- Address = addr;
- _previous = domain.PeekUshort(Address % domain.Size, bigEndian);
- }
-
- public long Previous => _previous;
-
- public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
- {
- _previous = domain.PeekUshort(Address, bigEndian);
- }
- }
-
- private sealed class MiniDWordWatch : IMiniWatch
- {
- public long Address { get; }
- private uint _previous;
-
- public MiniDWordWatch(MemoryDomain domain, long addr, bool bigEndian)
- {
- Address = addr;
- _previous = domain.PeekUint(Address % domain.Size, bigEndian);
- }
-
- public long Previous => _previous;
-
- public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
- {
- _previous = domain.PeekUint(Address, bigEndian);
- }
- }
-
- private sealed class MiniByteWatchDetailed : IMiniWatch, IMiniWatchDetails
- {
- public long Address { get; }
-
- private byte _previous;
- private byte _prevFrame;
-
- public MiniByteWatchDetailed(MemoryDomain domain, long addr)
- {
- Address = addr;
- SetPreviousToCurrent(domain, false);
- }
-
- public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
- {
- _previous = _prevFrame = domain.PeekByte(Address % domain.Size);
- }
-
- public long Previous => _previous;
-
- public int ChangeCount { get; private set; }
-
- public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
- {
- var value = domain.PeekByte(Address % domain.Size);
-
- if (value != _prevFrame)
- {
- ChangeCount++;
- }
-
- switch (type)
- {
- case PreviousType.Original:
- case PreviousType.LastSearch:
- break;
- case PreviousType.LastFrame:
- _previous = _prevFrame;
- break;
- case PreviousType.LastChange:
- if (_prevFrame != value)
- {
- _previous = _prevFrame;
- }
-
- break;
- }
-
- _prevFrame = value;
- }
-
- public void ClearChangeCount()
- {
- ChangeCount = 0;
- }
- }
-
- private sealed class MiniWordWatchDetailed : IMiniWatch, IMiniWatchDetails
- {
- public long Address { get; }
-
- private ushort _previous;
- private ushort _prevFrame;
-
- public MiniWordWatchDetailed(MemoryDomain domain, long addr, bool bigEndian)
- {
- Address = addr;
- SetPreviousToCurrent(domain, bigEndian);
- }
-
- public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
- {
- _previous = _prevFrame = domain.PeekUshort(Address % domain.Size, bigEndian);
- }
-
- public long Previous => _previous;
-
- public int ChangeCount { get; private set; }
-
- public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
- {
- var value = domain.PeekUshort(Address % domain.Size, bigEndian);
- if (value != Previous)
- {
- ChangeCount++;
- }
-
- switch (type)
- {
- case PreviousType.Original:
- case PreviousType.LastSearch:
- break;
- case PreviousType.LastFrame:
- _previous = _prevFrame;
- break;
- case PreviousType.LastChange:
- if (_prevFrame != value)
- {
- _previous = _prevFrame;
- }
-
- break;
- }
-
- _prevFrame = value;
- }
-
- public void ClearChangeCount()
- {
- ChangeCount = 0;
- }
- }
-
- private sealed class MiniDWordWatchDetailed : IMiniWatch, IMiniWatchDetails
- {
- public long Address { get; }
-
- private uint _previous;
- private uint _prevFrame;
-
- public MiniDWordWatchDetailed(MemoryDomain domain, long addr, bool bigEndian)
- {
- Address = addr;
- SetPreviousToCurrent(domain, bigEndian);
- }
-
- public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
- {
- _previous = _prevFrame = domain.PeekUint(Address % domain.Size, bigEndian);
- }
-
- public long Previous => (int)_previous;
-
- public int ChangeCount { get; private set; }
-
- public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
- {
- var value = domain.PeekUint(Address % domain.Size, bigEndian);
- if (value != Previous)
- {
- ChangeCount++;
- }
-
- switch (type)
- {
- case PreviousType.Original:
- case PreviousType.LastSearch:
- break;
- case PreviousType.LastFrame:
- _previous = _prevFrame;
- break;
- case PreviousType.LastChange:
- if (_prevFrame != value)
- {
- _previous = _prevFrame;
- }
-
- break;
- }
-
- _prevFrame = value;
- }
-
- public void ClearChangeCount()
- {
- ChangeCount = 0;
- }
- }
-
- public class Settings
- {
- public Settings(IMemoryDomains memoryDomains)
- {
- BigEndian = memoryDomains.MainMemory.EndianType == MemoryDomain.Endian.Big;
- Size = (WatchSize)memoryDomains.MainMemory.WordSize;
- Type = DisplayType.Unsigned;
- Mode = memoryDomains.MainMemory.Size > 1024 * 1024
- ? SearchMode.Fast
- : SearchMode.Detailed;
-
- Domain = memoryDomains.MainMemory;
- CheckMisAligned = false;
- PreviousType = PreviousType.LastSearch;
- }
-
- /*Require restart*/
- public enum SearchMode
- {
- Fast, Detailed
- }
-
- public SearchMode Mode { get; set; }
- public MemoryDomain Domain { get; set; }
- public WatchSize Size { get; set; }
- public bool CheckMisAligned { get; set; }
-
- /*Can be changed mid-search*/
- public DisplayType Type { get; set; }
- public bool BigEndian { get; set; }
- public PreviousType PreviousType { get; set; }
- }
-
- #endregion
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using BizHawk.Common;
+using BizHawk.Common.CollectionExtensions;
+using BizHawk.Emulation.Common;
+
+// ReSharper disable PossibleInvalidCastExceptionInForeachLoop
+namespace BizHawk.Client.Common
+{
+ public class RamSearchEngine
+ {
+ public enum ComparisonOperator
+ {
+ Equal, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual, NotEqual, DifferentBy
+ }
+
+ public enum Compare
+ {
+ Previous, SpecificValue, SpecificAddress, Changes, Difference
+ }
+
+ private Compare _compareTo = Compare.Previous;
+
+ private List _watchList = new List();
+ private readonly Settings _settings;
+ private readonly UndoHistory _history = new UndoHistory(true);
+ private bool _isSorted = true; // Tracks whether or not the list is sorted by address, if it is, binary search can be used for finding watches
+
+ public RamSearchEngine(Settings settings, IMemoryDomains memoryDomains)
+ {
+ _settings = new Settings(memoryDomains)
+ {
+ Mode = settings.Mode,
+ Domain = settings.Domain,
+ Size = settings.Size,
+ CheckMisAligned = settings.CheckMisAligned,
+ Type = settings.Type,
+ BigEndian = settings.BigEndian,
+ PreviousType = settings.PreviousType
+ };
+ }
+
+ public RamSearchEngine(Settings settings, IMemoryDomains memoryDomains, Compare compareTo, long? compareValue, int? differentBy)
+ : this(settings, memoryDomains)
+ {
+ _compareTo = compareTo;
+ DifferentBy = differentBy;
+ CompareValue = compareValue;
+ }
+
+ #region API
+
+ public IEnumerable OutOfRangeAddress => _watchList
+ .Where(watch => watch.Address >= Domain.Size)
+ .Select(watch => watch.Address);
+
+ public void Start()
+ {
+ _history.Clear();
+ var domain = _settings.Domain;
+ var listSize = domain.Size;
+ if (!_settings.CheckMisAligned)
+ {
+ listSize /= (int)_settings.Size;
+ }
+
+ _watchList = new List((int)listSize);
+
+ switch (_settings.Size)
+ {
+ default:
+ case WatchSize.Byte:
+ if (_settings.Mode == Settings.SearchMode.Detailed)
+ {
+ for (int i = 0; i < domain.Size; i++)
+ {
+ _watchList.Add(new MiniByteWatchDetailed(domain, i));
+ }
+ }
+ else
+ {
+ for (int i = 0; i < domain.Size; i++)
+ {
+ _watchList.Add(new MiniByteWatch(domain, i));
+ }
+ }
+
+ break;
+ case WatchSize.Word:
+ if (_settings.Mode == Settings.SearchMode.Detailed)
+ {
+ for (int i = 0; i < domain.Size - 1; i += _settings.CheckMisAligned ? 1 : 2)
+ {
+ _watchList.Add(new MiniWordWatchDetailed(domain, i, _settings.BigEndian));
+ }
+ }
+ else
+ {
+ for (int i = 0; i < domain.Size - 1; i += _settings.CheckMisAligned ? 1 : 2)
+ {
+ _watchList.Add(new MiniWordWatch(domain, i, _settings.BigEndian));
+ }
+ }
+
+ break;
+ case WatchSize.DWord:
+ if (_settings.Mode == Settings.SearchMode.Detailed)
+ {
+ for (int i = 0; i < domain.Size - 3; i += _settings.CheckMisAligned ? 1 : 4)
+ {
+ _watchList.Add(new MiniDWordWatchDetailed(domain, i, _settings.BigEndian));
+ }
+ }
+ else
+ {
+ for (int i = 0; i < domain.Size - 3; i += _settings.CheckMisAligned ? 1 : 4)
+ {
+ _watchList.Add(new MiniDWordWatch(domain, i, _settings.BigEndian));
+ }
+ }
+
+ break;
+ }
+ }
+
+ ///
+ /// Exposes the current watch state based on index
+ ///
+ public Watch this[int index]
+ {
+ get
+ {
+ if (_settings.Mode == Settings.SearchMode.Detailed)
+ {
+ return Watch.GenerateWatch(
+ _settings.Domain,
+ _watchList[index].Address,
+ _settings.Size,
+ _settings.Type,
+ _settings.BigEndian,
+ "",
+ 0,
+ _watchList[index].Previous,
+ ((IMiniWatchDetails)_watchList[index]).ChangeCount);
+ }
+
+ return Watch.GenerateWatch(
+ _settings.Domain,
+ _watchList[index].Address,
+ _settings.Size,
+ _settings.Type,
+ _settings.BigEndian,
+ "",
+ 0,
+ _watchList[index].Previous);
+ }
+ }
+
+ public int DoSearch()
+ {
+ int before = _watchList.Count;
+
+ _watchList = _compareTo switch
+ {
+ Compare.Previous => ComparePrevious(_watchList).ToList(),
+ Compare.SpecificValue => CompareSpecificValue(_watchList).ToList(),
+ Compare.SpecificAddress => CompareSpecificAddress(_watchList).ToList(),
+ Compare.Changes => CompareChanges(_watchList).ToList(),
+ Compare.Difference => CompareDifference(_watchList).ToList(),
+ _ => ComparePrevious(_watchList).ToList()
+ };
+
+ if (_settings.PreviousType == PreviousType.LastSearch)
+ {
+ SetPreviousToCurrent();
+ }
+
+ if (UndoEnabled)
+ {
+ _history.AddState(_watchList);
+ }
+
+ return before - _watchList.Count;
+ }
+
+ public bool Preview(long address)
+ {
+ var listOfOne = Enumerable.Repeat(_isSorted
+ ? _watchList.BinarySearch(w => w.Address, address)
+ : _watchList.FirstOrDefault(w => w.Address == address), 1);
+
+ return _compareTo switch
+ {
+ Compare.Previous => !ComparePrevious(listOfOne).Any(),
+ Compare.SpecificValue => !CompareSpecificValue(listOfOne).Any(),
+ Compare.SpecificAddress => !CompareSpecificAddress(listOfOne).Any(),
+ Compare.Changes => !CompareChanges(listOfOne).Any(),
+ Compare.Difference => !CompareDifference(listOfOne).Any(),
+ _ => !ComparePrevious(listOfOne).Any()
+ };
+ }
+
+ public int Count => _watchList.Count;
+
+ public Settings.SearchMode Mode => _settings.Mode;
+
+ public MemoryDomain Domain => _settings.Domain;
+
+ /// (from setter) is and is not
+ public Compare CompareTo
+ {
+ get => _compareTo;
+
+ set
+ {
+ if (CanDoCompareType(value))
+ {
+ _compareTo = value;
+ }
+ else
+ {
+ throw new InvalidOperationException();
+ }
+ }
+ }
+
+ public long? CompareValue { get; set; }
+
+ public ComparisonOperator Operator { get; set; }
+
+ // zero 07-sep-2014 - this isn't ideal. but don't bother changing it (to a long, for instance) until it can support floats. maybe store it as a double here.
+ public int? DifferentBy { get; set; }
+
+ public void Update()
+ {
+ if (_settings.Mode == Settings.SearchMode.Detailed)
+ {
+ foreach (IMiniWatchDetails watch in _watchList)
+ {
+ watch.Update(_settings.PreviousType, _settings.Domain, _settings.BigEndian);
+ }
+ }
+ }
+
+ public void SetType(DisplayType type)
+ {
+ _settings.Type = type;
+ }
+
+ public void SetEndian(bool bigEndian)
+ {
+ _settings.BigEndian = bigEndian;
+ }
+
+ /// is and is
+ public void SetPreviousType(PreviousType type)
+ {
+ if (_settings.Mode == Settings.SearchMode.Fast)
+ {
+ if (type == PreviousType.LastFrame)
+ {
+ throw new InvalidOperationException();
+ }
+ }
+
+ _settings.PreviousType = type;
+ }
+
+ public void SetPreviousToCurrent()
+ {
+ _watchList.ForEach(w => w.SetPreviousToCurrent(_settings.Domain, _settings.BigEndian));
+ }
+
+ public void ClearChangeCounts()
+ {
+ if (_settings.Mode == Settings.SearchMode.Detailed)
+ {
+ foreach (var watch in _watchList.Cast())
+ {
+ watch.ClearChangeCount();
+ }
+ }
+ }
+
+ ///
+ /// Remove a set of watches
+ /// However, this should not be used with large data sets (100k or more) as it uses a contains logic to perform the task
+ ///
+ public void RemoveSmallWatchRange(IEnumerable watches)
+ {
+ if (UndoEnabled)
+ {
+ _history.AddState(_watchList);
+ }
+
+ var addresses = watches.Select(w => w.Address);
+ _watchList.RemoveAll(w => addresses.Contains(w.Address));
+ }
+
+ public void RemoveRange(IEnumerable indices)
+ {
+ if (UndoEnabled)
+ {
+ _history.AddState(_watchList);
+ }
+
+ var removeList = indices.Select(i => _watchList[i]); // This will fail after int.MaxValue but RAM Search fails on domains that large anyway
+ _watchList = _watchList.Except(removeList).ToList();
+ }
+
+ public void AddRange(List addresses, bool append)
+ {
+ if (!append)
+ {
+ _watchList.Clear();
+ }
+
+ switch (_settings.Size)
+ {
+ default:
+ case WatchSize.Byte:
+ if (_settings.Mode == Settings.SearchMode.Detailed)
+ {
+ foreach (var addr in addresses)
+ {
+ _watchList.Add(new MiniByteWatchDetailed(_settings.Domain, addr));
+ }
+ }
+ else
+ {
+ foreach (var addr in addresses)
+ {
+ _watchList.Add(new MiniByteWatch(_settings.Domain, addr));
+ }
+ }
+
+ break;
+ case WatchSize.Word:
+ if (_settings.Mode == Settings.SearchMode.Detailed)
+ {
+ foreach (var addr in addresses)
+ {
+ _watchList.Add(new MiniWordWatchDetailed(_settings.Domain, addr, _settings.BigEndian));
+ }
+ }
+ else
+ {
+ foreach (var addr in addresses)
+ {
+ _watchList.Add(new MiniWordWatch(_settings.Domain, addr, _settings.BigEndian));
+ }
+ }
+
+ break;
+ case WatchSize.DWord:
+ if (_settings.Mode == Settings.SearchMode.Detailed)
+ {
+ foreach (var addr in addresses)
+ {
+ _watchList.Add(new MiniDWordWatchDetailed(_settings.Domain, addr, _settings.BigEndian));
+ }
+ }
+ else
+ {
+ foreach (var addr in addresses)
+ {
+ _watchList.Add(new MiniDWordWatch(_settings.Domain, addr, _settings.BigEndian));
+ }
+ }
+
+ break;
+ }
+ }
+
+ public void Sort(string column, bool reverse)
+ {
+ _isSorted = false;
+ switch (column)
+ {
+ case WatchList.ADDRESS:
+ if (reverse)
+ {
+ _watchList = _watchList.OrderByDescending(w => w.Address).ToList();
+ }
+ else
+ {
+ _watchList = _watchList.OrderBy(w => w.Address).ToList();
+ _isSorted = true;
+ }
+
+ break;
+ case WatchList.VALUE:
+ _watchList = reverse
+ ? _watchList.OrderByDescending(w => GetValue(w.Address)).ToList()
+ : _watchList.OrderBy(w => GetValue(w.Address)).ToList();
+
+ break;
+ case WatchList.PREV:
+ _watchList = reverse
+ ? _watchList.OrderByDescending(w => w.Previous).ToList()
+ : _watchList.OrderBy(w => w.Previous).ToList();
+
+ break;
+ case WatchList.CHANGES:
+ if (_settings.Mode == Settings.SearchMode.Detailed)
+ {
+ if (reverse)
+ {
+ _watchList = _watchList
+ .Cast()
+ .OrderByDescending(w => w.ChangeCount)
+ .Cast().ToList();
+ }
+ else
+ {
+ _watchList = _watchList
+ .Cast()
+ .OrderBy(w => w.ChangeCount)
+ .Cast().ToList();
+ }
+ }
+
+ break;
+ case WatchList.DIFF:
+ _watchList = reverse
+ ? _watchList.OrderByDescending(w => GetValue(w.Address) - w.Previous).ToList()
+ : _watchList.OrderBy(w => GetValue(w.Address) - w.Previous).ToList();
+
+ break;
+ }
+ }
+
+ #endregion
+
+ #region Undo API
+
+ public bool UndoEnabled { get; set; }
+
+
+ public bool CanUndo => UndoEnabled && _history.CanUndo;
+
+ public bool CanRedo => UndoEnabled && _history.CanRedo;
+
+ public void ClearHistory()
+ {
+ _history.Clear();
+ }
+
+ public int Undo()
+ {
+ int origCount = _watchList.Count;
+ if (UndoEnabled)
+ {
+ _watchList = _history.Undo().ToList();
+ return _watchList.Count - origCount;
+ }
+
+ return _watchList.Count;
+ }
+
+ public int Redo()
+ {
+ int origCount = _watchList.Count;
+ if (UndoEnabled)
+ {
+ _watchList = _history.Redo().ToList();
+ return origCount - _watchList.Count;
+ }
+
+ return _watchList.Count;
+ }
+
+ #endregion
+
+ #region Comparisons
+
+ private IEnumerable ComparePrevious(IEnumerable watchList)
+ {
+ switch (Operator)
+ {
+ default:
+ case ComparisonOperator.Equal:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) == ToFloat(w.Previous))
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) == SignExtendAsNeeded(w.Previous));
+ case ComparisonOperator.NotEqual:
+ return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) != SignExtendAsNeeded(w.Previous));
+ case ComparisonOperator.GreaterThan:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) > ToFloat(w.Previous))
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) > SignExtendAsNeeded(w.Previous));
+ case ComparisonOperator.GreaterThanEqual:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) >= ToFloat(w.Previous))
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) >= SignExtendAsNeeded(w.Previous));
+
+ case ComparisonOperator.LessThan:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) < ToFloat(w.Previous))
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) < SignExtendAsNeeded(w.Previous));
+ case ComparisonOperator.LessThanEqual:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) <= ToFloat(w.Previous))
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) <= SignExtendAsNeeded(w.Previous));
+ case ComparisonOperator.DifferentBy:
+ if (DifferentBy.HasValue)
+ {
+ var differentBy = DifferentBy.Value;
+ if (_settings.Type == DisplayType.Float)
+ {
+ return watchList.Where(w => ToFloat(GetValue(w.Address)) + differentBy == ToFloat(w.Previous)
+ || ToFloat(GetValue(w.Address)) - differentBy == ToFloat(w.Previous));
+ }
+
+ return watchList.Where(w =>
+ {
+ long val = SignExtendAsNeeded(GetValue(w.Address));
+ long prev = SignExtendAsNeeded(w.Previous);
+ return val + differentBy == prev
+ || val - differentBy == prev;
+ });
+ }
+ else
+ {
+ throw new InvalidOperationException();
+ }
+ }
+ }
+
+ private IEnumerable CompareSpecificValue(IEnumerable watchList)
+ {
+ if (CompareValue.HasValue)
+ {
+ var compareValue = CompareValue.Value;
+ switch (Operator)
+ {
+ default:
+ case ComparisonOperator.Equal:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) == ToFloat(compareValue))
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) == SignExtendAsNeeded(CompareValue.Value));
+ case ComparisonOperator.NotEqual:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) != ToFloat(compareValue))
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) != SignExtendAsNeeded(compareValue));
+ case ComparisonOperator.GreaterThan:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) > ToFloat(compareValue))
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) > SignExtendAsNeeded(compareValue));
+ case ComparisonOperator.GreaterThanEqual:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) >= ToFloat(compareValue))
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) >= SignExtendAsNeeded(compareValue));
+ case ComparisonOperator.LessThan:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) < ToFloat(compareValue))
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) < SignExtendAsNeeded(compareValue));
+
+ case ComparisonOperator.LessThanEqual:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) <= ToFloat(compareValue))
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) <= SignExtendAsNeeded(compareValue));
+
+ case ComparisonOperator.DifferentBy:
+ if (DifferentBy.HasValue)
+ {
+ var differentBy = DifferentBy.Value;
+ if (_settings.Type == DisplayType.Float)
+ {
+ return watchList.Where(w => ToFloat(GetValue(w.Address)) + differentBy == compareValue
+ || ToFloat(GetValue(w.Address)) - differentBy == compareValue);
+ }
+
+ return watchList.Where(w
+ => SignExtendAsNeeded(GetValue(w.Address)) + differentBy == compareValue
+ || SignExtendAsNeeded(GetValue(w.Address)) - differentBy == compareValue);
+ }
+
+ throw new InvalidOperationException();
+ }
+ }
+
+ throw new InvalidOperationException();
+ }
+
+ private IEnumerable CompareSpecificAddress(IEnumerable watchList)
+ {
+ if (CompareValue.HasValue)
+ {
+ var compareValue = CompareValue.Value;
+ switch (Operator)
+ {
+ default:
+ case ComparisonOperator.Equal:
+ return watchList.Where(w => w.Address == compareValue);
+ case ComparisonOperator.NotEqual:
+ return watchList.Where(w => w.Address != compareValue);
+ case ComparisonOperator.GreaterThan:
+ return watchList.Where(w => w.Address > compareValue);
+ case ComparisonOperator.GreaterThanEqual:
+ return watchList.Where(w => w.Address >= compareValue);
+ case ComparisonOperator.LessThan:
+ return watchList.Where(w => w.Address < compareValue);
+ case ComparisonOperator.LessThanEqual:
+ return watchList.Where(w => w.Address <= compareValue);
+ case ComparisonOperator.DifferentBy:
+ if (DifferentBy.HasValue)
+ {
+ return watchList.Where(w => w.Address + DifferentBy.Value == compareValue
+ || w.Address - DifferentBy.Value == compareValue);
+ }
+
+ throw new InvalidOperationException();
+ }
+ }
+
+ throw new InvalidOperationException();
+ }
+
+ private IEnumerable CompareChanges(IEnumerable watchList)
+ {
+ if (_settings.Mode == Settings.SearchMode.Detailed && CompareValue.HasValue)
+ {
+ var compareValue = CompareValue.Value;
+ switch (Operator)
+ {
+ default:
+ case ComparisonOperator.Equal:
+ return watchList
+ .Cast()
+ .Where(w => w.ChangeCount == compareValue)
+ .Cast();
+ case ComparisonOperator.NotEqual:
+ return watchList
+ .Cast()
+ .Where(w => w.ChangeCount != compareValue)
+ .Cast();
+ case ComparisonOperator.GreaterThan:
+ return watchList
+ .Cast()
+ .Where(w => w.ChangeCount > compareValue)
+ .Cast();
+ case ComparisonOperator.GreaterThanEqual:
+ return watchList
+ .Cast()
+ .Where(w => w.ChangeCount >= compareValue)
+ .Cast();
+ case ComparisonOperator.LessThan:
+ return watchList
+ .Cast()
+ .Where(w => w.ChangeCount < compareValue)
+ .Cast();
+ case ComparisonOperator.LessThanEqual:
+ return watchList
+ .Cast()
+ .Where(w => w.ChangeCount <= compareValue)
+ .Cast();
+ case ComparisonOperator.DifferentBy:
+ if (DifferentBy.HasValue)
+ {
+ return watchList
+ .Cast()
+ .Where(w => w.ChangeCount + DifferentBy.Value == compareValue
+ || w.ChangeCount - DifferentBy.Value == compareValue)
+ .Cast();
+ }
+
+ throw new InvalidOperationException();
+ }
+ }
+
+ throw new InvalidCastException();
+ }
+
+ private IEnumerable CompareDifference(IEnumerable watchList)
+ {
+ if (CompareValue.HasValue)
+ {
+ var compareValue = CompareValue.Value;
+ switch (Operator)
+ {
+ default:
+ case ComparisonOperator.Equal:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) - ToFloat(w.Previous) == compareValue)
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) == compareValue);
+ case ComparisonOperator.NotEqual:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) - w.Previous != compareValue)
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) != compareValue);
+ case ComparisonOperator.GreaterThan:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) - w.Previous > compareValue)
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) > compareValue);
+ case ComparisonOperator.GreaterThanEqual:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) - w.Previous >= compareValue)
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) >= compareValue);
+ case ComparisonOperator.LessThan:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) - w.Previous < compareValue)
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) < compareValue);
+ case ComparisonOperator.LessThanEqual:
+ return _settings.Type == DisplayType.Float
+ ? watchList.Where(w => ToFloat(GetValue(w.Address)) - w.Previous <= compareValue)
+ : watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) <= compareValue);
+ case ComparisonOperator.DifferentBy:
+ if (DifferentBy.HasValue)
+ {
+ var differentBy = DifferentBy.Value;
+ if (_settings.Type == DisplayType.Float)
+ {
+ return watchList.Where(w => ToFloat(GetValue(w.Address)) - w.Previous + differentBy == compareValue
+ || ToFloat(GetValue(w.Address)) - w.Previous - differentBy == w.Previous);
+ }
+
+ return watchList.Where(w
+ => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) + differentBy == compareValue
+ || SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) - differentBy == compareValue);
+ }
+
+ throw new InvalidOperationException();
+ }
+ }
+
+ throw new InvalidCastException();
+ }
+
+ #endregion
+
+ #region Private parts
+
+ private static float ToFloat(long val)
+ {
+ var bytes = BitConverter.GetBytes((int)val);
+ return BitConverter.ToSingle(bytes, 0);
+ }
+
+ private long SignExtendAsNeeded(long val)
+ {
+ if (_settings.Type != DisplayType.Signed)
+ {
+ return val;
+ }
+
+ return _settings.Size switch
+ {
+ WatchSize.Byte => (sbyte) val,
+ WatchSize.Word => (short) val,
+ WatchSize.DWord => (int) val,
+ _ => (sbyte) val
+ };
+ }
+
+ private long GetValue(long addr)
+ {
+ // do not return sign extended variables from here.
+ return _settings.Size switch
+ {
+ WatchSize.Byte => _settings.Domain.PeekByte(addr % Domain.Size),
+ WatchSize.Word => _settings.Domain.PeekUshort(addr % Domain.Size, _settings.BigEndian),
+ WatchSize.DWord => _settings.Domain.PeekUint(addr % Domain.Size, _settings.BigEndian),
+ _ => _settings.Domain.PeekByte(addr % Domain.Size)
+ };
+ }
+
+ private bool CanDoCompareType(Compare compareType)
+ {
+ return _settings.Mode switch
+ {
+ Settings.SearchMode.Detailed => true,
+ Settings.SearchMode.Fast => (compareType != Compare.Changes),
+ _ => true
+ };
+ }
+
+ #endregion
+
+ public class Settings
+ {
+ public Settings(IMemoryDomains memoryDomains)
+ {
+ BigEndian = memoryDomains.MainMemory.EndianType == MemoryDomain.Endian.Big;
+ Size = (WatchSize)memoryDomains.MainMemory.WordSize;
+ Type = DisplayType.Unsigned;
+ Mode = memoryDomains.MainMemory.Size > 1024 * 1024
+ ? SearchMode.Fast
+ : SearchMode.Detailed;
+
+ Domain = memoryDomains.MainMemory;
+ CheckMisAligned = false;
+ PreviousType = PreviousType.LastSearch;
+ }
+
+ /*Require restart*/
+ public enum SearchMode
+ {
+ Fast, Detailed
+ }
+
+ public SearchMode Mode { get; set; }
+ public MemoryDomain Domain { get; set; }
+ public WatchSize Size { get; set; }
+ public bool CheckMisAligned { get; set; }
+
+ /*Can be changed mid-search*/
+ public DisplayType Type { get; set; }
+ public bool BigEndian { get; set; }
+ public PreviousType PreviousType { get; set; }
+ }
+ }
+}