diff --git a/src/BizHawk.Client.Common/tools/RamSearchEngine/Extensions.cs b/src/BizHawk.Client.Common/tools/RamSearchEngine/Extensions.cs index fce2196973..d4b1f38159 100644 --- a/src/BizHawk.Client.Common/tools/RamSearchEngine/Extensions.cs +++ b/src/BizHawk.Client.Common/tools/RamSearchEngine/Extensions.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using BizHawk.Emulation.Common; @@ -7,43 +6,22 @@ namespace BizHawk.Client.Common.RamSearchEngine { internal static class Extensions { - public static float ToFloat(this long val) - { - var bytes = BitConverter.GetBytes((int)val); - return BitConverter.ToSingle(bytes, 0); - } - public static IEnumerable ToBytes(this IEnumerable addresses, SearchEngineSettings settings) - => settings.IsDetailed() - ? addresses.ToDetailedBytes(settings.Domain) - : addresses.ToBytes(settings.Domain); + => addresses.ToDetailedBytes(settings.Domain); public static IEnumerable ToWords(this IEnumerable addresses, SearchEngineSettings settings) - => settings.IsDetailed() - ? addresses.ToDetailedWords(settings.Domain, settings.BigEndian) - : addresses.ToWords(settings.Domain, settings.BigEndian); + => addresses.ToDetailedWords(settings.Domain, settings.BigEndian); public static IEnumerable ToDWords(this IEnumerable addresses, SearchEngineSettings settings) - => settings.IsDetailed() - ? addresses.ToDetailedDWords(settings.Domain, settings.BigEndian) - : addresses.ToDWords(settings.Domain, settings.BigEndian); - - private static IEnumerable ToBytes(this IEnumerable addresses, MemoryDomain domain) - => addresses.Select(a => new MiniByteWatch(domain, a)); + => addresses.ToDetailedDWords(settings.Domain, settings.BigEndian); private static IEnumerable ToDetailedBytes(this IEnumerable addresses, MemoryDomain domain) - => addresses.Select(a => new MiniByteWatchDetailed(domain, a)); - - private static IEnumerable ToWords(this IEnumerable addresses, MemoryDomain domain, bool bigEndian) - => addresses.Select(a => new MiniWordWatch(domain, a, bigEndian)); + => addresses.Select(a => new MiniByteWatch(domain, a)); private static IEnumerable ToDetailedWords(this IEnumerable addresses, MemoryDomain domain, bool bigEndian) - => addresses.Select(a => new MiniWordWatchDetailed(domain, a, bigEndian)); - - private static IEnumerable ToDWords(this IEnumerable addresses, MemoryDomain domain, bool bigEndian) - => addresses.Select(a => new MiniDWordWatch(domain, a, bigEndian)); + => addresses.Select(a => new MiniWordWatch(domain, a, bigEndian)); private static IEnumerable ToDetailedDWords(this IEnumerable addresses, MemoryDomain domain, bool bigEndian) - => addresses.Select(a => new MiniDWordWatchDetailed(domain, a, bigEndian)); + => addresses.Select(a => new MiniDWordWatch(domain, a, bigEndian)); } } diff --git a/src/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatch.cs b/src/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatch.cs index 1bd7b86966..33a37a2c93 100644 --- a/src/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatch.cs +++ b/src/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatch.cs @@ -4,130 +4,172 @@ namespace BizHawk.Client.Common.RamSearchEngine { /// /// 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); + long Current { get; } + int ChangeCount { get; } + void ClearChangeCount(); + void SetPreviousToCurrent(); bool IsValid(MemoryDomain domain); + void Update(PreviousType type, MemoryDomain domain, bool bigEndian); } internal sealed class MiniByteWatch : IMiniWatch { public long Address { get; } + private byte _previous; + private byte _current; public MiniByteWatch(MemoryDomain domain, long addr) { Address = addr; - _previous = GetByte(Address, domain); + _previous = _current = GetByte(domain); + } + + public void SetPreviousToCurrent() + { + _previous = _current; } public long Previous => _previous; + public long Current => _current; - public bool IsValid(MemoryDomain domain) - { - return IsValid(Address, domain); - } + public int ChangeCount { get; private set; } - public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian) + public void Update(PreviousType type, MemoryDomain domain, bool bigEndian) { - _previous = GetByte(Address, domain); - } + var newValue = GetByte(domain); - public static bool IsValid(long address, MemoryDomain domain) - { - return address < domain.Size; - } - - public static byte GetByte(long address, MemoryDomain domain) - { - if (!IsValid(address, domain)) + if (newValue != _current) { - return 0; + ChangeCount++; + if (type == PreviousType.LastChange) + { + _previous = _current; + } } - return domain.PeekByte(address); + if (type == PreviousType.LastFrame) + _previous = _current; + + _current = newValue; + } + + public void ClearChangeCount() => ChangeCount = 0; + + public bool IsValid(MemoryDomain domain) => Address < domain.Size; + + public byte GetByte(MemoryDomain domain) + { + return IsValid(domain) ? domain.PeekByte(Address) : (byte)0; } } internal sealed class MiniWordWatch : IMiniWatch { public long Address { get; } + private ushort _previous; + private ushort _current; public MiniWordWatch(MemoryDomain domain, long addr, bool bigEndian) { Address = addr; - _previous = GetUshort(Address, domain, bigEndian); + _previous = _current = GetUshort(domain, bigEndian); + } + + public void SetPreviousToCurrent() + { + _previous = _current; } public long Previous => _previous; + public long Current => _current; - public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian) - { - _previous = GetUshort(Address, domain, bigEndian); - } + public int ChangeCount { get; private set; } - public bool IsValid(MemoryDomain domain) + public void Update(PreviousType type, MemoryDomain domain, bool bigEndian) { - return IsValid(Address, domain); - } + var newValue = GetUshort(domain, bigEndian); - public static bool IsValid(long address, MemoryDomain domain) - { - return address < (domain.Size - 1); - } - - public static ushort GetUshort(long address, MemoryDomain domain, bool bigEndian) - { - if (!IsValid(address, domain)) + if (newValue != _current) { - return 0; + ChangeCount++; + if (type == PreviousType.LastChange) + { + _previous = _current; + } } - return domain.PeekUshort(address, bigEndian); + if (type == PreviousType.LastFrame) + _previous = _current; + + _current = newValue; + } + + public void ClearChangeCount() => ChangeCount = 0; + + public bool IsValid(MemoryDomain domain) => Address < domain.Size - 1; + + private ushort GetUshort(MemoryDomain domain, bool bigEndian) + { + return IsValid(domain) ? domain.PeekUshort(Address, bigEndian) : (ushort)0; } } internal sealed class MiniDWordWatch : IMiniWatch { public long Address { get; } + private uint _previous; + private uint _current; public MiniDWordWatch(MemoryDomain domain, long addr, bool bigEndian) { Address = addr; - _previous = GetUint(Address, domain, bigEndian); + _previous = _current = GetUint(domain, bigEndian); + } + + public void SetPreviousToCurrent() + { + _previous = _current; } public long Previous => _previous; + public long Current => _current; - public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian) - { - _previous = GetUint(Address, domain, bigEndian); - } + public int ChangeCount { get; private set; } - public bool IsValid(MemoryDomain domain) + public void Update(PreviousType type, MemoryDomain domain, bool bigEndian) { - return IsValid(Address, domain); - } + var newValue = GetUint(domain, bigEndian); - public static bool IsValid(long address, MemoryDomain domain) - { - return address < (domain.Size - 3); - } - - public static uint GetUint(long address, MemoryDomain domain, bool bigEndian) - { - if (!IsValid(address, domain)) + if (newValue != _current) { - return 0; + ChangeCount++; + if (type == PreviousType.LastChange) + { + _previous = _current; + } } - return domain.PeekUint(address, bigEndian); + if (type == PreviousType.LastFrame) + _previous = _current; + + _current = newValue; + } + + public void ClearChangeCount() => ChangeCount = 0; + + public bool IsValid(MemoryDomain domain) => Address < domain.Size - 3; + + private uint GetUint(MemoryDomain domain, bool bigEndian) + { + return IsValid(domain) ? domain.PeekUint(Address, bigEndian) : 0; } } } diff --git a/src/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatchDetails.cs b/src/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatchDetails.cs deleted file mode 100644 index 024eae8d78..0000000000 --- a/src/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatchDetails.cs +++ /dev/null @@ -1,183 +0,0 @@ -using BizHawk.Emulation.Common; - -namespace BizHawk.Client.Common.RamSearchEngine -{ - /// - /// 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 = MiniByteWatch.GetByte(Address, domain); - } - - public long Previous => _previous; - - public int ChangeCount { get; private set; } - - public void Update(PreviousType type, MemoryDomain domain, bool bigEndian) - { - var value = MiniByteWatch.GetByte(Address, domain); - - 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; - - public bool IsValid(MemoryDomain domain) => MiniByteWatch.IsValid(Address, domain); - } - - internal sealed class MiniWordWatchDetailed : 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 = MiniWordWatch.GetUshort(Address, domain, bigEndian); - } - - public long Previous => _previous; - - public int ChangeCount { get; private set; } - - public void Update(PreviousType type, MemoryDomain domain, bool bigEndian) - { - var value = MiniWordWatch.GetUshort(Address, domain, 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 bool IsValid(MemoryDomain domain) => MiniWordWatch.IsValid(Address, domain); - } - - internal sealed class MiniDWordWatchDetailed : 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 = MiniDWordWatch.GetUint(Address, domain, bigEndian); - } - - public long Previous => (int)_previous; - - public int ChangeCount { get; private set; } - - public void Update(PreviousType type, MemoryDomain domain, bool bigEndian) - { - var value = MiniDWordWatch.GetUint(Address, domain, 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 bool IsValid(MemoryDomain domain) => MiniDWordWatch.IsValid(Address, domain); - } -} diff --git a/src/BizHawk.Client.Common/tools/RamSearchEngine/RamSearchEngine.cs b/src/BizHawk.Client.Common/tools/RamSearchEngine/RamSearchEngine.cs index 77c74aeba1..5e4d0ebe80 100644 --- a/src/BizHawk.Client.Common/tools/RamSearchEngine/RamSearchEngine.cs +++ b/src/BizHawk.Client.Common/tools/RamSearchEngine/RamSearchEngine.cs @@ -21,10 +21,9 @@ namespace BizHawk.Client.Common.RamSearchEngine private Compare _compareTo = Compare.Previous; - private IMiniWatch[] _watchList = Array.Empty(); + private IMiniWatch[] _watchList = [ ]; private readonly SearchEngineSettings _settings; - private readonly UndoHistory> _history = new UndoHistory>(true, new List()); //TODO use IList instead of IEnumerable and stop calling `.ToArray()` (i.e. cloning) on reads and writes? - private bool _isSorted = true; // Tracks whether or not the array is sorted by address, if it is, binary search can be used for finding watches + private readonly UndoHistory> _history = new(true, [ ]); //TODO use IList instead of IEnumerable and stop calling `.ToArray()` (i.e. cloning) on reads and writes? public RamSearchEngine(SearchEngineSettings settings, IMemoryDomains memoryDomains) { @@ -65,46 +64,13 @@ namespace BizHawk.Client.Common.RamSearchEngine { default: case WatchSize.Byte: - if (_settings.IsDetailed()) - { - for (var i = 0; i < _watchList.Length; i++) _watchList[i] = new MiniByteWatchDetailed(domain, i); - } - else - { - for (var i = 0; i < _watchList.Length; i++) _watchList[i] = new MiniByteWatch(domain, i); - } + for (var i = 0; i < _watchList.Length; i++) _watchList[i] = new MiniByteWatch(domain, i); break; case WatchSize.Word: - if (_settings.IsDetailed()) - { - for (var i = 0; i < _watchList.Length; i++) - { - _watchList[i] = new MiniWordWatchDetailed(domain, i * stepSize, _settings.BigEndian); - } - } - else - { - for (var i = 0; i < _watchList.Length; i++) - { - _watchList[i] = new MiniWordWatch(domain, i * stepSize, _settings.BigEndian); - } - } + for (var i = 0; i < _watchList.Length; i++) _watchList[i] = new MiniWordWatch(domain, i * stepSize, _settings.BigEndian); break; case WatchSize.DWord: - if (_settings.IsDetailed()) - { - for (var i = 0; i < _watchList.Length; i++) - { - _watchList[i] = new MiniDWordWatchDetailed(domain, i * stepSize, _settings.BigEndian); - } - } - else - { - for (var i = 0; i < _watchList.Length; i++) - { - _watchList[i] = new MiniDWordWatch(domain, i * stepSize, _settings.BigEndian); - } - } + for (var i = 0; i < _watchList.Length; i++) _watchList[i] = new MiniDWordWatch(domain, i * stepSize, _settings.BigEndian); break; } } @@ -122,12 +88,14 @@ namespace BizHawk.Client.Common.RamSearchEngine "", 0, _watchList[index].Previous, - _settings.IsDetailed() ? ((IMiniWatchDetails)_watchList[index]).ChangeCount : 0); + _settings.IsDetailed() ? _watchList[index].ChangeCount : 0); - public int DoSearch() + public int DoSearch(bool updatePrevious) { int before = _watchList.Length; + Update(updatePrevious); + using (Domain.EnterExit()) { _watchList = _compareTo switch @@ -139,11 +107,6 @@ namespace BizHawk.Client.Common.RamSearchEngine Compare.Difference => CompareDifference(_watchList).ToArray(), _ => ComparePrevious(_watchList).ToArray() }; - - if (_settings.PreviousType == PreviousType.LastSearch) - { - SetPreviousToCurrent(); - } } if (UndoEnabled) @@ -154,11 +117,11 @@ namespace BizHawk.Client.Common.RamSearchEngine return before - _watchList.Length; } - public bool Preview(long address) + public bool Preview(int index) { - var listOfOne = Enumerable.Repeat(_isSorted - ? _watchList.BinarySearch(w => w.Address, address) - : _watchList.FirstOrDefault(w => w.Address == address), 1); + var addressWatch = _watchList[index]; + addressWatch.Update(PreviousType.Original, _settings.Domain, _settings.BigEndian); + IMiniWatch[] listOfOne = [ addressWatch ]; return _compareTo switch { @@ -204,13 +167,12 @@ namespace BizHawk.Client.Common.RamSearchEngine /// public int? DifferentBy { get; set; } - public void Update() + public void Update(bool updatePrevious) { - if (!_settings.IsDetailed()) return; using var @lock = _settings.Domain.EnterExit(); - foreach (IMiniWatchDetails watch in _watchList) + foreach (var watch in _watchList) { - watch.Update(_settings.PreviousType, _settings.Domain, _settings.BigEndian); + watch.Update(updatePrevious ? _settings.PreviousType : PreviousType.Original, _settings.Domain, _settings.BigEndian); } } @@ -218,26 +180,18 @@ namespace BizHawk.Client.Common.RamSearchEngine public void SetEndian(bool bigEndian) => _settings.BigEndian = bigEndian; - /// is and is - public void SetPreviousType(PreviousType type) - { - if (_settings.IsFastMode() && type == PreviousType.LastFrame) - { - throw new InvalidOperationException(); - } + public void SetPreviousType(PreviousType type) => _settings.PreviousType = type; - _settings.PreviousType = type; - } + public void SetMode(SearchMode mode) => _settings.Mode = mode; public void SetPreviousToCurrent() { - Array.ForEach(_watchList, w => w.SetPreviousToCurrent(_settings.Domain, _settings.BigEndian)); + Array.ForEach(_watchList, static w => w.SetPreviousToCurrent()); } public void ClearChangeCounts() { - if (!_settings.IsDetailed()) return; - foreach (var watch in _watchList.Cast()) + foreach (var watch in _watchList) { watch.ClearChangeCount(); } @@ -285,33 +239,26 @@ namespace BizHawk.Client.Common.RamSearchEngine }; _watchList = (append ? _watchList.Concat(list) : list).ToArray(); - _isSorted = false; //TODO can this be smarter, such as by inserting instead of appending? } public void Sort(string column, bool reverse) { - _isSorted = column == WatchList.Address && !reverse; switch (column) { case WatchList.Address: _watchList = _watchList.OrderBy(w => w.Address, reverse).ToArray(); break; case WatchList.Value: - _watchList = _watchList.OrderBy(w => GetValue(w.Address), reverse).ToArray(); + _watchList = _watchList.OrderBy(w => w.Current, reverse).ToArray(); break; case WatchList.Prev: _watchList = _watchList.OrderBy(w => w.Previous, reverse).ToArray(); break; case WatchList.ChangesCol: - if (!_settings.IsDetailed()) break; - _watchList = _watchList - .Cast() - .OrderBy(w => w.ChangeCount, reverse) - .Cast() - .ToArray(); + _watchList = _watchList.OrderBy(w => w.ChangeCount, reverse).ToArray(); break; case WatchList.Diff: - _watchList = _watchList.OrderBy(w => GetValue(w.Address) - w.Previous, reverse).ToArray(); + _watchList = _watchList.OrderBy(w => w.Current - w.Previous, reverse).ToArray(); break; } } @@ -360,52 +307,52 @@ namespace BizHawk.Client.Common.RamSearchEngine { default: case ComparisonOperator.Equal: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) == SignExtendAsNeeded(w.Previous)); + return watchList.Where(w => SignExtendAsNeeded(w.Current) == SignExtendAsNeeded(w.Previous)); case ComparisonOperator.NotEqual: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) != SignExtendAsNeeded(w.Previous)); + return watchList.Where(w => SignExtendAsNeeded(w.Current) != SignExtendAsNeeded(w.Previous)); case ComparisonOperator.GreaterThan: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) > SignExtendAsNeeded(w.Previous)); + return watchList.Where(w => SignExtendAsNeeded(w.Current) > SignExtendAsNeeded(w.Previous)); case ComparisonOperator.GreaterThanEqual: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) >= SignExtendAsNeeded(w.Previous)); + return watchList.Where(w => SignExtendAsNeeded(w.Current) >= SignExtendAsNeeded(w.Previous)); case ComparisonOperator.LessThan: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) < SignExtendAsNeeded(w.Previous)); + return watchList.Where(w => SignExtendAsNeeded(w.Current) < SignExtendAsNeeded(w.Previous)); case ComparisonOperator.LessThanEqual: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) <= SignExtendAsNeeded(w.Previous)); + return watchList.Where(w => SignExtendAsNeeded(w.Current) <= SignExtendAsNeeded(w.Previous)); case ComparisonOperator.DifferentBy: if (DifferentBy is not int differentBy) throw new InvalidOperationException(); return watchList.Where(w => - differentBy == Math.Abs(SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous))); + differentBy == Math.Abs(SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous))); } } switch (Operator) { default: case ComparisonOperator.Equal: - return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)).HawkFloatEquality(ReinterpretAsF32(w.Previous))); + return watchList.Where(w => ReinterpretAsF32(w.Current).HawkFloatEquality(ReinterpretAsF32(w.Previous))); case ComparisonOperator.NotEqual: - return watchList.Where(w => !ReinterpretAsF32(GetValue(w.Address)).HawkFloatEquality(ReinterpretAsF32(w.Previous))); + return watchList.Where(w => !ReinterpretAsF32(w.Current).HawkFloatEquality(ReinterpretAsF32(w.Previous))); case ComparisonOperator.GreaterThan: - return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)) > ReinterpretAsF32(w.Previous)); + return watchList.Where(w => ReinterpretAsF32(w.Current) > ReinterpretAsF32(w.Previous)); case ComparisonOperator.GreaterThanEqual: return watchList.Where(w => { - var val = ReinterpretAsF32(GetValue(w.Address)); + var val = ReinterpretAsF32(w.Current); var prev = ReinterpretAsF32(w.Previous); return val > prev || val.HawkFloatEquality(prev); }); case ComparisonOperator.LessThan: - return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)) < ReinterpretAsF32(w.Previous)); + return watchList.Where(w => ReinterpretAsF32(w.Current) < ReinterpretAsF32(w.Previous)); case ComparisonOperator.LessThanEqual: return watchList.Where(w => { - var val = ReinterpretAsF32(GetValue(w.Address)); + var val = ReinterpretAsF32(w.Current); var prev = ReinterpretAsF32(w.Previous); return val < prev || val.HawkFloatEquality(prev); }); case ComparisonOperator.DifferentBy: if (DifferentBy is not int differentBy) throw new InvalidOperationException(); var differentByF = ReinterpretAsF32(differentBy); - return watchList.Where(w => Math.Abs(ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous)) + return watchList.Where(w => Math.Abs(ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous)) .HawkFloatEquality(differentByF)); } } @@ -419,21 +366,21 @@ namespace BizHawk.Client.Common.RamSearchEngine { default: case ComparisonOperator.Equal: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) == SignExtendAsNeeded(compareValue)); + return watchList.Where(w => SignExtendAsNeeded(w.Current) == SignExtendAsNeeded(compareValue)); case ComparisonOperator.NotEqual: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) != SignExtendAsNeeded(compareValue)); + return watchList.Where(w => SignExtendAsNeeded(w.Current) != SignExtendAsNeeded(compareValue)); case ComparisonOperator.GreaterThan: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) > SignExtendAsNeeded(compareValue)); + return watchList.Where(w => SignExtendAsNeeded(w.Current) > SignExtendAsNeeded(compareValue)); case ComparisonOperator.GreaterThanEqual: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) >= SignExtendAsNeeded(compareValue)); + return watchList.Where(w => SignExtendAsNeeded(w.Current) >= SignExtendAsNeeded(compareValue)); case ComparisonOperator.LessThan: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) < SignExtendAsNeeded(compareValue)); + return watchList.Where(w => SignExtendAsNeeded(w.Current) < SignExtendAsNeeded(compareValue)); case ComparisonOperator.LessThanEqual: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) <= SignExtendAsNeeded(compareValue)); + return watchList.Where(w => SignExtendAsNeeded(w.Current) <= SignExtendAsNeeded(compareValue)); case ComparisonOperator.DifferentBy: if (DifferentBy is not int differentBy) throw new InvalidOperationException(); return watchList.Where(w => - differentBy == Math.Abs(SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(compareValue))); + differentBy == Math.Abs(SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(compareValue))); } } var compareValueF = ReinterpretAsF32(compareValue); @@ -441,29 +388,29 @@ namespace BizHawk.Client.Common.RamSearchEngine { default: case ComparisonOperator.Equal: - return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)).HawkFloatEquality(compareValueF)); + return watchList.Where(w => ReinterpretAsF32(w.Current).HawkFloatEquality(compareValueF)); case ComparisonOperator.NotEqual: - return watchList.Where(w => !ReinterpretAsF32(GetValue(w.Address)).HawkFloatEquality(compareValueF)); + return watchList.Where(w => !ReinterpretAsF32(w.Current).HawkFloatEquality(compareValueF)); case ComparisonOperator.GreaterThan: - return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)) > compareValueF); + return watchList.Where(w => ReinterpretAsF32(w.Current) > compareValueF); case ComparisonOperator.GreaterThanEqual: return watchList.Where(w => { - var val = ReinterpretAsF32(GetValue(w.Address)); + var val = ReinterpretAsF32(w.Current); return val > compareValueF || val.HawkFloatEquality(compareValueF); }); case ComparisonOperator.LessThan: - return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)) < compareValueF); + return watchList.Where(w => ReinterpretAsF32(w.Current) < compareValueF); case ComparisonOperator.LessThanEqual: return watchList.Where(w => { - var val = ReinterpretAsF32(GetValue(w.Address)); + var val = ReinterpretAsF32(w.Current); return val < compareValueF || val.HawkFloatEquality(compareValueF); }); case ComparisonOperator.DifferentBy: if (DifferentBy is not int differentBy) throw new InvalidOperationException(); var differentByF = ReinterpretAsF32(differentBy); - return watchList.Where(w => Math.Abs(ReinterpretAsF32(GetValue(w.Address)) - compareValueF) + return watchList.Where(w => Math.Abs(ReinterpretAsF32(w.Current) - compareValueF) .HawkFloatEquality(differentByF)); } } @@ -494,40 +441,25 @@ namespace BizHawk.Client.Common.RamSearchEngine private IEnumerable CompareChanges(IEnumerable watchList) { - if (!_settings.IsDetailed()) throw new InvalidCastException(); //TODO matches previous behaviour; was this intended to skip processing? --yoshi - if (CompareValue is not long compareValue) throw new InvalidCastException(); //TODO typo for IOE? + if (CompareValue is not long compareValue) throw new InvalidOperationException(); switch (Operator) { default: case ComparisonOperator.Equal: - return watchList - .Cast() - .Where(w => w.ChangeCount == compareValue); + return watchList.Where(w => w.ChangeCount == compareValue); case ComparisonOperator.NotEqual: - return watchList - .Cast() - .Where(w => w.ChangeCount != compareValue); + return watchList.Where(w => w.ChangeCount != compareValue); case ComparisonOperator.GreaterThan: - return watchList - .Cast() - .Where(w => w.ChangeCount > compareValue); + return watchList.Where(w => w.ChangeCount > compareValue); case ComparisonOperator.GreaterThanEqual: - return watchList - .Cast() - .Where(w => w.ChangeCount >= compareValue); + return watchList.Where(w => w.ChangeCount >= compareValue); case ComparisonOperator.LessThan: - return watchList - .Cast() - .Where(w => w.ChangeCount < compareValue); + return watchList.Where(w => w.ChangeCount < compareValue); case ComparisonOperator.LessThanEqual: - return watchList - .Cast() - .Where(w => w.ChangeCount <= compareValue); + return watchList.Where(w => w.ChangeCount <= compareValue); case ComparisonOperator.DifferentBy: if (DifferentBy is not int differentBy) throw new InvalidOperationException(); - return watchList - .Cast() - .Where(w => Math.Abs(w.ChangeCount - compareValue) == differentBy); + return watchList.Where(w => Math.Abs(w.ChangeCount - compareValue) == differentBy); } } @@ -540,21 +472,21 @@ namespace BizHawk.Client.Common.RamSearchEngine { default: case ComparisonOperator.Equal: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) == compareValue); + return watchList.Where(w => SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) == compareValue); case ComparisonOperator.NotEqual: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) != compareValue); + return watchList.Where(w => SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) != compareValue); case ComparisonOperator.GreaterThan: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) > compareValue); + return watchList.Where(w => SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) > compareValue); case ComparisonOperator.GreaterThanEqual: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) >= compareValue); + return watchList.Where(w => SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) >= compareValue); case ComparisonOperator.LessThan: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) < compareValue); + return watchList.Where(w => SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) < compareValue); case ComparisonOperator.LessThanEqual: - return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) <= compareValue); + return watchList.Where(w => SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) <= compareValue); case ComparisonOperator.DifferentBy: if (DifferentBy is not int differentBy) throw new InvalidOperationException(); return watchList.Where(w => - differentBy == Math.Abs(SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) - compareValue)); + differentBy == Math.Abs(SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) - compareValue)); } } var compareValueF = ReinterpretAsF32(compareValue); @@ -562,29 +494,29 @@ namespace BizHawk.Client.Common.RamSearchEngine { default: case ComparisonOperator.Equal: - return watchList.Where(w => (ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous)).HawkFloatEquality(compareValueF)); + return watchList.Where(w => (ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous)).HawkFloatEquality(compareValueF)); case ComparisonOperator.NotEqual: - return watchList.Where(w => !(ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous)).HawkFloatEquality(compareValueF)); + return watchList.Where(w => !(ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous)).HawkFloatEquality(compareValueF)); case ComparisonOperator.GreaterThan: - return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous) > compareValueF); + return watchList.Where(w => ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous) > compareValueF); case ComparisonOperator.GreaterThanEqual: return watchList.Where(w => { - var diff = ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous); + var diff = ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous); return diff > compareValueF || diff.HawkFloatEquality(compareValueF); }); case ComparisonOperator.LessThan: - return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous) < compareValueF); + return watchList.Where(w => ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous) < compareValueF); case ComparisonOperator.LessThanEqual: return watchList.Where(w => { - var diff = ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous); + var diff = ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous); return diff < compareValueF || diff.HawkFloatEquality(compareValueF); }); case ComparisonOperator.DifferentBy: if (DifferentBy is not int differentBy) throw new InvalidOperationException(); var differentByF = ReinterpretAsF32(differentBy); - return watchList.Where(w => Math.Abs(ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous) - compareValueF) + return watchList.Where(w => Math.Abs(ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous) - compareValueF) .HawkFloatEquality(differentByF)); } } @@ -605,24 +537,12 @@ namespace BizHawk.Client.Common.RamSearchEngine }; } - private long GetValue(long addr) - { - // do not return sign extended variables from here. - return _settings.Size switch - { - WatchSize.Byte => MiniByteWatch.GetByte(addr, Domain), - WatchSize.Word => MiniWordWatch.GetUshort(addr, Domain, _settings.BigEndian), - WatchSize.DWord => MiniDWordWatch.GetUint(addr, Domain, _settings.BigEndian), - _ => MiniByteWatch.GetByte(addr, Domain) - }; - } - private bool CanDoCompareType(Compare compareType) { return _settings.Mode switch { SearchMode.Detailed => true, - SearchMode.Fast => (compareType != Compare.Changes), + SearchMode.Fast => compareType != Compare.Changes, _ => true }; } diff --git a/src/BizHawk.Client.Common/tools/RamSearchEngine/SearchEngineSettings.cs b/src/BizHawk.Client.Common/tools/RamSearchEngine/SearchEngineSettings.cs index 5323c745f7..9944a90129 100644 --- a/src/BizHawk.Client.Common/tools/RamSearchEngine/SearchEngineSettings.cs +++ b/src/BizHawk.Client.Common/tools/RamSearchEngine/SearchEngineSettings.cs @@ -8,28 +8,25 @@ namespace BizHawk.Client.Common.RamSearchEngine { BigEndian = memoryDomains.MainMemory.EndianType == MemoryDomain.Endian.Big; Size = (WatchSize)memoryDomains.MainMemory.WordSize; - Type = WatchDisplayType.Unsigned; Mode = memoryDomains.MainMemory.Size > 1024 * 1024 ? SearchMode.Fast : SearchMode.Detailed; Domain = memoryDomains.MainMemory; - CheckMisAligned = false; - PreviousType = PreviousType.LastSearch; UseUndoHistory = useUndoHistory; } /*Require restart*/ - 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 WatchDisplayType Type { get; set; } + public WatchDisplayType Type { get; set; } = WatchDisplayType.Unsigned; public bool BigEndian { get; set; } - public PreviousType PreviousType { get; set; } + public PreviousType PreviousType { get; set; } = PreviousType.LastFrame; public bool UseUndoHistory { get; set; } + public SearchMode Mode { get; set; } } public static class SearchEngineSettingsExtensions diff --git a/src/BizHawk.Client.Common/tools/Watch/PreviousType.cs b/src/BizHawk.Client.Common/tools/Watch/PreviousType.cs index 7e9e5c03d6..2c65a5a322 100644 --- a/src/BizHawk.Client.Common/tools/Watch/PreviousType.cs +++ b/src/BizHawk.Client.Common/tools/Watch/PreviousType.cs @@ -2,9 +2,8 @@ { public enum PreviousType { - Original = 0, - LastSearch = 1, - LastFrame = 2, - LastChange = 3 + Original, + LastFrame, + LastChange, } } diff --git a/src/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.Designer.cs b/src/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.Designer.cs index 48569a7278..3eb467c51f 100644 --- a/src/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.Designer.cs +++ b/src/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.Designer.cs @@ -71,7 +71,6 @@ namespace BizHawk.Client.EmuHawk this.DisplayTypeSubMenu = new BizHawk.WinForms.Controls.ToolStripMenuItemEx(); this.toolStripSeparator1 = new BizHawk.WinForms.Controls.ToolStripSeparatorEx(); this.DefinePreviousValueSubMenu = new BizHawk.WinForms.Controls.ToolStripMenuItemEx(); - this.Previous_LastSearchMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx(); this.PreviousFrameMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx(); this.Previous_OriginalMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx(); this.Previous_LastChangeMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx(); @@ -397,18 +396,12 @@ namespace BizHawk.Client.EmuHawk // DefinePreviousValueSubMenu // this.DefinePreviousValueSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.Previous_LastSearchMenuItem, this.PreviousFrameMenuItem, this.Previous_OriginalMenuItem, this.Previous_LastChangeMenuItem}); this.DefinePreviousValueSubMenu.Text = "Define Previous Value"; this.DefinePreviousValueSubMenu.DropDownOpened += new System.EventHandler(this.DefinePreviousValueSubMenu_DropDownOpened); // - // Previous_LastSearchMenuItem - // - this.Previous_LastSearchMenuItem.Text = "Last &Search"; - this.Previous_LastSearchMenuItem.Click += new System.EventHandler(this.Previous_LastSearchMenuItem_Click); - // // PreviousFrameMenuItem // this.PreviousFrameMenuItem.Text = "&Previous Frame"; @@ -1132,7 +1125,6 @@ namespace BizHawk.Client.EmuHawk private BizHawk.WinForms.Controls.ToolStripSeparatorEx toolStripSeparator8; private BizHawk.WinForms.Controls.ToolStripMenuItemEx DefinePreviousValueSubMenu; private BizHawk.WinForms.Controls.ToolStripMenuItemEx PreviousFrameMenuItem; - private BizHawk.WinForms.Controls.ToolStripMenuItemEx Previous_LastSearchMenuItem; private BizHawk.WinForms.Controls.ToolStripMenuItemEx Previous_OriginalMenuItem; private System.Windows.Forms.GroupBox CompareToBox; private System.Windows.Forms.RadioButton DifferenceRadio; @@ -1185,4 +1177,4 @@ namespace BizHawk.Client.EmuHawk private BizHawk.WinForms.Controls.ToolStripMenuItemEx AutoSearchAccountForLagMenuItem; private ToolStripMenuItemEx SelectAllMenuItem; } -} \ No newline at end of file +} diff --git a/src/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.cs b/src/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.cs index 9ba5862b18..a5be0de254 100644 --- a/src/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.cs +++ b/src/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.cs @@ -189,7 +189,7 @@ namespace BizHawk.Client.EmuHawk var nextColor = Color.White; var search = _searches[index]; var isCheat = MainForm.CheatList.IsActive(_settings.Domain, search.Address); - var isWeeded = Settings.PreviewMode && !_forcePreviewClear && _searches.Preview(search.Address); + var isWeeded = Settings.PreviewMode && !_forcePreviewClear && _searches.Preview(index); if (!search.IsValid) { @@ -271,8 +271,6 @@ namespace BizHawk.Client.EmuHawk { if (_searches.Count > 0) { - _searches.Update(); - if (_autoSearch) { if (InputPollableCore != null && Settings.AutoSearchTakeLagFramesIntoAccount && InputPollableCore.IsLagFrame) @@ -281,21 +279,24 @@ namespace BizHawk.Client.EmuHawk } else { - DoSearch(); + DoSearch(true); } } + else if (_settings.IsDetailed()) + { + _searches.Update(true); + } _forcePreviewClear = false; WatchListView.RowCount = _searches.Count; } } + // TODO: this seems to be missing some logic from FrameUpdate that probably should exist here private void MinimalUpdate() { if (_searches.Count > 0) { - _searches.Update(); - if (_autoSearch) { DoSearch(); @@ -522,14 +523,14 @@ namespace BizHawk.Client.EmuHawk } } - public void DoSearch() + public void DoSearch(bool updatePrevious = false) { _searches.CompareValue = CompareToValue; _searches.DifferentBy = DifferentByValue; _searches.Operator = Operator; _searches.CompareTo = Compare; - var removed = _searches.DoSearch(); + var removed = _searches.DoSearch(updatePrevious); UpdateList(); SetRemovedMessage(removed); ToggleSearchDependentToolBarItems(); @@ -588,7 +589,6 @@ namespace BizHawk.Client.EmuHawk && _settings.IsDetailed()) { _settings.Mode = SearchMode.Fast; - SetReboot(true); MessageLabel.Text = "Large domain, switching to fast mode"; } } @@ -754,6 +754,7 @@ namespace BizHawk.Client.EmuHawk private void SetToDetailedMode() { _settings.Mode = SearchMode.Detailed; + _searches.SetMode(SearchMode.Detailed); NumberOfChangesRadio.Enabled = true; NumberOfChangesBox.Enabled = true; DifferenceRadio.Enabled = true; @@ -764,7 +765,6 @@ namespace BizHawk.Client.EmuHawk ChangesMenuItem.Checked = true; ColumnToggleCallback(); - SetReboot(true); } private ToolStripMenuItem ChangesMenuItem @@ -783,11 +783,7 @@ namespace BizHawk.Client.EmuHawk private void SetToFastMode() { _settings.Mode = SearchMode.Fast; - - if (_settings.PreviousType == PreviousType.LastFrame || _settings.PreviousType == PreviousType.LastChange) - { - SetPreviousType(PreviousType.LastSearch); - } + _searches.SetMode(SearchMode.Fast); NumberOfChangesRadio.Enabled = false; NumberOfChangesBox.Enabled = false; @@ -803,7 +799,6 @@ namespace BizHawk.Client.EmuHawk ChangesMenuItem.Checked = false; ColumnToggleCallback(); - SetReboot(true); } private void RemoveAddresses() @@ -1085,7 +1080,6 @@ namespace BizHawk.Client.EmuHawk private void DefinePreviousValueSubMenu_DropDownOpened(object sender, EventArgs e) { - Previous_LastSearchMenuItem.Checked = false; PreviousFrameMenuItem.Checked = false; Previous_OriginalMenuItem.Checked = false; Previous_LastChangeMenuItem.Checked = false; @@ -1093,9 +1087,6 @@ namespace BizHawk.Client.EmuHawk switch (_settings.PreviousType) { default: - case PreviousType.LastSearch: - Previous_LastSearchMenuItem.Checked = true; - break; case PreviousType.LastFrame: PreviousFrameMenuItem.Checked = true; break; @@ -1106,9 +1097,6 @@ namespace BizHawk.Client.EmuHawk Previous_LastChangeMenuItem.Checked = true; break; } - - PreviousFrameMenuItem.Enabled = _settings.IsDetailed(); - Previous_LastChangeMenuItem.Enabled = _settings.IsDetailed(); } private void DetailedMenuItem_Click(object sender, EventArgs e) @@ -1147,11 +1135,6 @@ namespace BizHawk.Client.EmuHawk SetPreviousType(PreviousType.LastFrame); } - private void Previous_LastSearchMenuItem_Click(object sender, EventArgs e) - { - SetPreviousType(PreviousType.LastSearch); - } - private void Previous_OriginalMenuItem_Click(object sender, EventArgs e) { SetPreviousType(PreviousType.Original); @@ -1170,8 +1153,6 @@ namespace BizHawk.Client.EmuHawk private void SearchSubMenu_DropDownOpened(object sender, EventArgs e) { - ClearChangeCountsMenuItem.Enabled = _settings.IsDetailed(); - RemoveMenuItem.Enabled = AddToRamWatchMenuItem.Enabled = WatchListView.AnyRowsSelected;