Revert RAM Search refactor (PR #3954)

reverts 1a9e5e52f1
This commit is contained in:
YoshiRulz 2024-08-15 01:27:02 +10:00
parent 0f22ef2ad0
commit 6b160d9bd0
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
8 changed files with 472 additions and 199 deletions

View File

@ -6,22 +6,43 @@ 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<IMiniWatch> ToBytes(this IEnumerable<long> addresses, SearchEngineSettings settings)
=> addresses.ToDetailedBytes(settings.Domain);
=> settings.IsDetailed()
? addresses.ToDetailedBytes(settings.Domain)
: addresses.ToBytes(settings.Domain);
public static IEnumerable<IMiniWatch> ToWords(this IEnumerable<long> addresses, SearchEngineSettings settings)
=> addresses.ToDetailedWords(settings.Domain, settings.BigEndian);
=> settings.IsDetailed()
? addresses.ToDetailedWords(settings.Domain, settings.BigEndian)
: addresses.ToWords(settings.Domain, settings.BigEndian);
public static IEnumerable<IMiniWatch> ToDWords(this IEnumerable<long> addresses, SearchEngineSettings settings)
=> addresses.ToDetailedDWords(settings.Domain, settings.BigEndian);
=> settings.IsDetailed()
? addresses.ToDetailedDWords(settings.Domain, settings.BigEndian)
: addresses.ToDWords(settings.Domain, settings.BigEndian);
private static IEnumerable<IMiniWatch> ToDetailedBytes(this IEnumerable<long> addresses, MemoryDomain domain)
private static IEnumerable<IMiniWatch> ToBytes(this IEnumerable<long> addresses, MemoryDomain domain)
=> addresses.Select(a => new MiniByteWatch(domain, a));
private static IEnumerable<IMiniWatch> ToDetailedWords(this IEnumerable<long> addresses, MemoryDomain domain, bool bigEndian)
private static IEnumerable<IMiniWatch> ToDetailedBytes(this IEnumerable<long> addresses, MemoryDomain domain)
=> addresses.Select(a => new MiniByteWatchDetailed(domain, a));
private static IEnumerable<IMiniWatch> ToWords(this IEnumerable<long> addresses, MemoryDomain domain, bool bigEndian)
=> addresses.Select(a => new MiniWordWatch(domain, a, bigEndian));
private static IEnumerable<IMiniWatch> ToDetailedDWords(this IEnumerable<long> addresses, MemoryDomain domain, bool bigEndian)
private static IEnumerable<IMiniWatch> ToDetailedWords(this IEnumerable<long> addresses, MemoryDomain domain, bool bigEndian)
=> addresses.Select(a => new MiniWordWatchDetailed(domain, a, bigEndian));
private static IEnumerable<IMiniWatch> ToDWords(this IEnumerable<long> addresses, MemoryDomain domain, bool bigEndian)
=> addresses.Select(a => new MiniDWordWatch(domain, a, bigEndian));
private static IEnumerable<IMiniWatch> ToDetailedDWords(this IEnumerable<long> addresses, MemoryDomain domain, bool bigEndian)
=> addresses.Select(a => new MiniDWordWatchDetailed(domain, a, bigEndian));
}
}

View File

@ -4,172 +4,130 @@ namespace BizHawk.Client.Common.RamSearchEngine
{
/// <summary>
/// Represents a Ram address for watching in the <see cref="RamSearchEngine" />
/// With the minimal details necessary for searching
/// </summary>
internal interface IMiniWatch
{
long Address { get; }
long Previous { get; } // do not store sign extended variables in here.
long Current { get; }
int ChangeCount { get; }
void ClearChangeCount();
void SetPreviousToCurrent();
void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian);
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 = _current = GetByte(domain);
}
public void SetPreviousToCurrent()
{
_previous = _current;
_previous = GetByte(Address, domain);
}
public long Previous => _previous;
public long Current => _current;
public int ChangeCount { get; private set; }
public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
public bool IsValid(MemoryDomain domain)
{
var newValue = GetByte(domain);
if (newValue != _current)
{
ChangeCount++;
if (type == PreviousType.LastChange)
{
_previous = _current;
}
}
if (type == PreviousType.LastFrame)
_previous = _current;
_current = newValue;
return IsValid(Address, domain);
}
public void ClearChangeCount() => ChangeCount = 0;
public bool IsValid(MemoryDomain domain) => Address < domain.Size;
public byte GetByte(MemoryDomain domain)
public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
{
return IsValid(domain) ? domain.PeekByte(Address) : (byte)0;
_previous = GetByte(Address, 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))
{
return 0;
}
return domain.PeekByte(address);
}
}
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 = _current = GetUshort(domain, bigEndian);
}
public void SetPreviousToCurrent()
{
_previous = _current;
_previous = GetUshort(Address, domain, bigEndian);
}
public long Previous => _previous;
public long Current => _current;
public int ChangeCount { get; private set; }
public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
{
var newValue = GetUshort(domain, bigEndian);
if (newValue != _current)
{
ChangeCount++;
if (type == PreviousType.LastChange)
{
_previous = _current;
}
}
if (type == PreviousType.LastFrame)
_previous = _current;
_current = newValue;
_previous = GetUshort(Address, domain, bigEndian);
}
public void ClearChangeCount() => ChangeCount = 0;
public bool IsValid(MemoryDomain domain) => Address < domain.Size - 1;
private ushort GetUshort(MemoryDomain domain, bool bigEndian)
public bool IsValid(MemoryDomain domain)
{
return IsValid(domain) ? domain.PeekUshort(Address, bigEndian) : (ushort)0;
return IsValid(Address, domain);
}
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))
{
return 0;
}
return domain.PeekUshort(address, bigEndian);
}
}
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 = _current = GetUint(domain, bigEndian);
}
public void SetPreviousToCurrent()
{
_previous = _current;
_previous = GetUint(Address, domain, bigEndian);
}
public long Previous => _previous;
public long Current => _current;
public int ChangeCount { get; private set; }
public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
{
var newValue = GetUint(domain, bigEndian);
if (newValue != _current)
{
ChangeCount++;
if (type == PreviousType.LastChange)
{
_previous = _current;
}
}
if (type == PreviousType.LastFrame)
_previous = _current;
_current = newValue;
_previous = GetUint(Address, domain, bigEndian);
}
public void ClearChangeCount() => ChangeCount = 0;
public bool IsValid(MemoryDomain domain) => Address < domain.Size - 3;
private uint GetUint(MemoryDomain domain, bool bigEndian)
public bool IsValid(MemoryDomain domain)
{
return IsValid(domain) ? domain.PeekUint(Address, bigEndian) : 0;
return IsValid(Address, domain);
}
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))
{
return 0;
}
return domain.PeekUint(address, bigEndian);
}
}
}

View File

@ -0,0 +1,183 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common.RamSearchEngine
{
/// <summary>
/// Represents a <see cref="IMiniWatch" /> 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
/// </summary>
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);
}
}

View File

@ -20,9 +20,10 @@ namespace BizHawk.Client.Common.RamSearchEngine
private Compare _compareTo = Compare.Previous;
private IMiniWatch[] _watchList = [ ];
private IMiniWatch[] _watchList = Array.Empty<IMiniWatch>();
private readonly SearchEngineSettings _settings;
private readonly UndoHistory<IEnumerable<IMiniWatch>> _history = new(true, [ ]); //TODO use IList instead of IEnumerable and stop calling `.ToArray()` (i.e. cloning) on reads and writes?
private readonly UndoHistory<IEnumerable<IMiniWatch>> _history = new UndoHistory<IEnumerable<IMiniWatch>>(true, new List<IMiniWatch>()); //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
public RamSearchEngine(SearchEngineSettings settings, IMemoryDomains memoryDomains)
{
@ -63,13 +64,46 @@ namespace BizHawk.Client.Common.RamSearchEngine
{
default:
case WatchSize.Byte:
for (var i = 0; i < _watchList.Length; i++) _watchList[i] = new MiniByteWatch(domain, i);
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);
}
break;
case WatchSize.Word:
for (var i = 0; i < _watchList.Length; i++) _watchList[i] = new MiniWordWatch(domain, i * stepSize, _settings.BigEndian);
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);
}
}
break;
case WatchSize.DWord:
for (var i = 0; i < _watchList.Length; i++) _watchList[i] = new MiniDWordWatch(domain, i * stepSize, _settings.BigEndian);
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);
}
}
break;
}
}
@ -87,14 +121,12 @@ namespace BizHawk.Client.Common.RamSearchEngine
"",
0,
_watchList[index].Previous,
_settings.IsDetailed() ? _watchList[index].ChangeCount : 0);
_settings.IsDetailed() ? ((IMiniWatchDetails)_watchList[index]).ChangeCount : 0);
public int DoSearch(bool updatePrevious)
public int DoSearch()
{
int before = _watchList.Length;
Update(updatePrevious);
using (Domain.EnterExit())
{
_watchList = _compareTo switch
@ -106,6 +138,11 @@ namespace BizHawk.Client.Common.RamSearchEngine
Compare.Difference => CompareDifference(_watchList).ToArray(),
_ => ComparePrevious(_watchList).ToArray()
};
if (_settings.PreviousType == PreviousType.LastSearch)
{
SetPreviousToCurrent();
}
}
if (UndoEnabled)
@ -116,11 +153,11 @@ namespace BizHawk.Client.Common.RamSearchEngine
return before - _watchList.Length;
}
public bool Preview(int index)
public bool Preview(long address)
{
var addressWatch = _watchList[index];
addressWatch.Update(PreviousType.Original, _settings.Domain, _settings.BigEndian);
IMiniWatch[] listOfOne = [ addressWatch ];
var listOfOne = Enumerable.Repeat(_isSorted
? _watchList.BinarySearch(w => w.Address, address)
: _watchList.FirstOrDefault(w => w.Address == address), 1);
return _compareTo switch
{
@ -166,12 +203,13 @@ namespace BizHawk.Client.Common.RamSearchEngine
/// </remarks>
public int? DifferentBy { get; set; }
public void Update(bool updatePrevious)
public void Update()
{
if (!_settings.IsDetailed()) return;
using var @lock = _settings.Domain.EnterExit();
foreach (var watch in _watchList)
foreach (IMiniWatchDetails watch in _watchList)
{
watch.Update(updatePrevious ? _settings.PreviousType : PreviousType.Original, _settings.Domain, _settings.BigEndian);
watch.Update(_settings.PreviousType, _settings.Domain, _settings.BigEndian);
}
}
@ -179,18 +217,26 @@ namespace BizHawk.Client.Common.RamSearchEngine
public void SetEndian(bool bigEndian) => _settings.BigEndian = bigEndian;
public void SetPreviousType(PreviousType type) => _settings.PreviousType = type;
/// <exception cref="InvalidOperationException"><see cref="Mode"/> is <see cref="SearchMode.Fast"/> and <paramref name="type"/> is <see cref="PreviousType.LastFrame"/></exception>
public void SetPreviousType(PreviousType type)
{
if (_settings.IsFastMode() && type == PreviousType.LastFrame)
{
throw new InvalidOperationException();
}
public void SetMode(SearchMode mode) => _settings.Mode = mode;
_settings.PreviousType = type;
}
public void SetPreviousToCurrent()
{
Array.ForEach(_watchList, static w => w.SetPreviousToCurrent());
Array.ForEach(_watchList, w => w.SetPreviousToCurrent(_settings.Domain, _settings.BigEndian));
}
public void ClearChangeCounts()
{
foreach (var watch in _watchList)
if (!_settings.IsDetailed()) return;
foreach (var watch in _watchList.Cast<IMiniWatchDetails>())
{
watch.ClearChangeCount();
}
@ -238,26 +284,33 @@ 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 => w.Current, reverse).ToArray();
_watchList = _watchList.OrderBy(w => GetValue(w.Address), reverse).ToArray();
break;
case WatchList.Prev:
_watchList = _watchList.OrderBy(w => w.Previous, reverse).ToArray();
break;
case WatchList.ChangesCol:
_watchList = _watchList.OrderBy(w => w.ChangeCount, reverse).ToArray();
if (!_settings.IsDetailed()) break;
_watchList = _watchList
.Cast<IMiniWatchDetails>()
.OrderBy(w => w.ChangeCount, reverse)
.Cast<IMiniWatch>()
.ToArray();
break;
case WatchList.Diff:
_watchList = _watchList.OrderBy(w => w.Current - w.Previous, reverse).ToArray();
_watchList = _watchList.OrderBy(w => GetValue(w.Address) - w.Previous, reverse).ToArray();
break;
}
}
@ -306,52 +359,52 @@ namespace BizHawk.Client.Common.RamSearchEngine
{
default:
case ComparisonOperator.Equal:
return watchList.Where(w => SignExtendAsNeeded(w.Current) == SignExtendAsNeeded(w.Previous));
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) == SignExtendAsNeeded(w.Previous));
case ComparisonOperator.NotEqual:
return watchList.Where(w => SignExtendAsNeeded(w.Current) != SignExtendAsNeeded(w.Previous));
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) != SignExtendAsNeeded(w.Previous));
case ComparisonOperator.GreaterThan:
return watchList.Where(w => SignExtendAsNeeded(w.Current) > SignExtendAsNeeded(w.Previous));
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) > SignExtendAsNeeded(w.Previous));
case ComparisonOperator.GreaterThanEqual:
return watchList.Where(w => SignExtendAsNeeded(w.Current) >= SignExtendAsNeeded(w.Previous));
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) >= SignExtendAsNeeded(w.Previous));
case ComparisonOperator.LessThan:
return watchList.Where(w => SignExtendAsNeeded(w.Current) < SignExtendAsNeeded(w.Previous));
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) < SignExtendAsNeeded(w.Previous));
case ComparisonOperator.LessThanEqual:
return watchList.Where(w => SignExtendAsNeeded(w.Current) <= SignExtendAsNeeded(w.Previous));
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) <= SignExtendAsNeeded(w.Previous));
case ComparisonOperator.DifferentBy:
if (DifferentBy is not int differentBy) throw new InvalidOperationException();
return watchList.Where(w =>
differentBy == Math.Abs(SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous)));
differentBy == Math.Abs(SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous)));
}
}
switch (Operator)
{
default:
case ComparisonOperator.Equal:
return watchList.Where(w => ReinterpretAsF32(w.Current).HawkFloatEquality(ReinterpretAsF32(w.Previous)));
return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)).HawkFloatEquality(ReinterpretAsF32(w.Previous)));
case ComparisonOperator.NotEqual:
return watchList.Where(w => !ReinterpretAsF32(w.Current).HawkFloatEquality(ReinterpretAsF32(w.Previous)));
return watchList.Where(w => !ReinterpretAsF32(GetValue(w.Address)).HawkFloatEquality(ReinterpretAsF32(w.Previous)));
case ComparisonOperator.GreaterThan:
return watchList.Where(w => ReinterpretAsF32(w.Current) > ReinterpretAsF32(w.Previous));
return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)) > ReinterpretAsF32(w.Previous));
case ComparisonOperator.GreaterThanEqual:
return watchList.Where(w =>
{
var val = ReinterpretAsF32(w.Current);
var val = ReinterpretAsF32(GetValue(w.Address));
var prev = ReinterpretAsF32(w.Previous);
return val > prev || val.HawkFloatEquality(prev);
});
case ComparisonOperator.LessThan:
return watchList.Where(w => ReinterpretAsF32(w.Current) < ReinterpretAsF32(w.Previous));
return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)) < ReinterpretAsF32(w.Previous));
case ComparisonOperator.LessThanEqual:
return watchList.Where(w =>
{
var val = ReinterpretAsF32(w.Current);
var val = ReinterpretAsF32(GetValue(w.Address));
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(w.Current) - ReinterpretAsF32(w.Previous))
return watchList.Where(w => Math.Abs(ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous))
.HawkFloatEquality(differentByF));
}
}
@ -365,21 +418,21 @@ namespace BizHawk.Client.Common.RamSearchEngine
{
default:
case ComparisonOperator.Equal:
return watchList.Where(w => SignExtendAsNeeded(w.Current) == SignExtendAsNeeded(compareValue));
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) == SignExtendAsNeeded(compareValue));
case ComparisonOperator.NotEqual:
return watchList.Where(w => SignExtendAsNeeded(w.Current) != SignExtendAsNeeded(compareValue));
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) != SignExtendAsNeeded(compareValue));
case ComparisonOperator.GreaterThan:
return watchList.Where(w => SignExtendAsNeeded(w.Current) > SignExtendAsNeeded(compareValue));
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) > SignExtendAsNeeded(compareValue));
case ComparisonOperator.GreaterThanEqual:
return watchList.Where(w => SignExtendAsNeeded(w.Current) >= SignExtendAsNeeded(compareValue));
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) >= SignExtendAsNeeded(compareValue));
case ComparisonOperator.LessThan:
return watchList.Where(w => SignExtendAsNeeded(w.Current) < SignExtendAsNeeded(compareValue));
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) < SignExtendAsNeeded(compareValue));
case ComparisonOperator.LessThanEqual:
return watchList.Where(w => SignExtendAsNeeded(w.Current) <= SignExtendAsNeeded(compareValue));
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) <= SignExtendAsNeeded(compareValue));
case ComparisonOperator.DifferentBy:
if (DifferentBy is not int differentBy) throw new InvalidOperationException();
return watchList.Where(w =>
differentBy == Math.Abs(SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(compareValue)));
differentBy == Math.Abs(SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(compareValue)));
}
}
var compareValueF = ReinterpretAsF32(compareValue);
@ -387,29 +440,29 @@ namespace BizHawk.Client.Common.RamSearchEngine
{
default:
case ComparisonOperator.Equal:
return watchList.Where(w => ReinterpretAsF32(w.Current).HawkFloatEquality(compareValueF));
return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)).HawkFloatEquality(compareValueF));
case ComparisonOperator.NotEqual:
return watchList.Where(w => !ReinterpretAsF32(w.Current).HawkFloatEquality(compareValueF));
return watchList.Where(w => !ReinterpretAsF32(GetValue(w.Address)).HawkFloatEquality(compareValueF));
case ComparisonOperator.GreaterThan:
return watchList.Where(w => ReinterpretAsF32(w.Current) > compareValueF);
return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)) > compareValueF);
case ComparisonOperator.GreaterThanEqual:
return watchList.Where(w =>
{
var val = ReinterpretAsF32(w.Current);
var val = ReinterpretAsF32(GetValue(w.Address));
return val > compareValueF || val.HawkFloatEquality(compareValueF);
});
case ComparisonOperator.LessThan:
return watchList.Where(w => ReinterpretAsF32(w.Current) < compareValueF);
return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)) < compareValueF);
case ComparisonOperator.LessThanEqual:
return watchList.Where(w =>
{
var val = ReinterpretAsF32(w.Current);
var val = ReinterpretAsF32(GetValue(w.Address));
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(w.Current) - compareValueF)
return watchList.Where(w => Math.Abs(ReinterpretAsF32(GetValue(w.Address)) - compareValueF)
.HawkFloatEquality(differentByF));
}
}
@ -440,25 +493,40 @@ namespace BizHawk.Client.Common.RamSearchEngine
private IEnumerable<IMiniWatch> CompareChanges(IEnumerable<IMiniWatch> watchList)
{
if (CompareValue is not long compareValue) throw new InvalidOperationException();
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?
switch (Operator)
{
default:
case ComparisonOperator.Equal:
return watchList.Where(w => w.ChangeCount == compareValue);
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => w.ChangeCount == compareValue);
case ComparisonOperator.NotEqual:
return watchList.Where(w => w.ChangeCount != compareValue);
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => w.ChangeCount != compareValue);
case ComparisonOperator.GreaterThan:
return watchList.Where(w => w.ChangeCount > compareValue);
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => w.ChangeCount > compareValue);
case ComparisonOperator.GreaterThanEqual:
return watchList.Where(w => w.ChangeCount >= compareValue);
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => w.ChangeCount >= compareValue);
case ComparisonOperator.LessThan:
return watchList.Where(w => w.ChangeCount < compareValue);
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => w.ChangeCount < compareValue);
case ComparisonOperator.LessThanEqual:
return watchList.Where(w => w.ChangeCount <= compareValue);
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => w.ChangeCount <= compareValue);
case ComparisonOperator.DifferentBy:
if (DifferentBy is not int differentBy) throw new InvalidOperationException();
return watchList.Where(w => Math.Abs(w.ChangeCount - compareValue) == differentBy);
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => Math.Abs(w.ChangeCount - compareValue) == differentBy);
}
}
@ -471,21 +539,21 @@ namespace BizHawk.Client.Common.RamSearchEngine
{
default:
case ComparisonOperator.Equal:
return watchList.Where(w => SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) == compareValue);
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) == compareValue);
case ComparisonOperator.NotEqual:
return watchList.Where(w => SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) != compareValue);
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) != compareValue);
case ComparisonOperator.GreaterThan:
return watchList.Where(w => SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) > compareValue);
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) > compareValue);
case ComparisonOperator.GreaterThanEqual:
return watchList.Where(w => SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) >= compareValue);
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) >= compareValue);
case ComparisonOperator.LessThan:
return watchList.Where(w => SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) < compareValue);
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) < compareValue);
case ComparisonOperator.LessThanEqual:
return watchList.Where(w => SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) <= compareValue);
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) <= compareValue);
case ComparisonOperator.DifferentBy:
if (DifferentBy is not int differentBy) throw new InvalidOperationException();
return watchList.Where(w =>
differentBy == Math.Abs(SignExtendAsNeeded(w.Current) - SignExtendAsNeeded(w.Previous) - compareValue));
differentBy == Math.Abs(SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) - compareValue));
}
}
var compareValueF = ReinterpretAsF32(compareValue);
@ -493,29 +561,29 @@ namespace BizHawk.Client.Common.RamSearchEngine
{
default:
case ComparisonOperator.Equal:
return watchList.Where(w => (ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous)).HawkFloatEquality(compareValueF));
return watchList.Where(w => (ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous)).HawkFloatEquality(compareValueF));
case ComparisonOperator.NotEqual:
return watchList.Where(w => !(ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous)).HawkFloatEquality(compareValueF));
return watchList.Where(w => !(ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous)).HawkFloatEquality(compareValueF));
case ComparisonOperator.GreaterThan:
return watchList.Where(w => ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous) > compareValueF);
return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous) > compareValueF);
case ComparisonOperator.GreaterThanEqual:
return watchList.Where(w =>
{
var diff = ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous);
var diff = ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous);
return diff > compareValueF || diff.HawkFloatEquality(compareValueF);
});
case ComparisonOperator.LessThan:
return watchList.Where(w => ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous) < compareValueF);
return watchList.Where(w => ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous) < compareValueF);
case ComparisonOperator.LessThanEqual:
return watchList.Where(w =>
{
var diff = ReinterpretAsF32(w.Current) - ReinterpretAsF32(w.Previous);
var diff = ReinterpretAsF32(GetValue(w.Address)) - 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(w.Current) - ReinterpretAsF32(w.Previous) - compareValueF)
return watchList.Where(w => Math.Abs(ReinterpretAsF32(GetValue(w.Address)) - ReinterpretAsF32(w.Previous) - compareValueF)
.HawkFloatEquality(differentByF));
}
}
@ -536,12 +604,24 @@ 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
};
}

View File

@ -8,25 +8,28 @@ 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; } = WatchDisplayType.Unsigned;
public WatchDisplayType Type { get; set; }
public bool BigEndian { get; set; }
public PreviousType PreviousType { get; set; } = PreviousType.LastFrame;
public PreviousType PreviousType { get; set; }
public bool UseUndoHistory { get; set; }
public SearchMode Mode { get; set; }
}
public static class SearchEngineSettingsExtensions

View File

@ -3,6 +3,7 @@
public enum PreviousType
{
Original = 0,
LastSearch = 1,
LastFrame = 2,
LastChange = 3,
}

View File

@ -72,6 +72,7 @@ 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();
@ -400,12 +401,18 @@ 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";
@ -1130,6 +1137,7 @@ 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;
@ -1183,4 +1191,4 @@ namespace BizHawk.Client.EmuHawk
private ToolStripMenuItemEx SelectAllMenuItem;
private ToolStripMenuItemEx SearchMenuItem;
}
}
}

View File

@ -188,7 +188,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(index);
var isWeeded = Settings.PreviewMode && !_forcePreviewClear && _searches.Preview(search.Address);
if (!search.IsValid)
{
@ -270,6 +270,8 @@ namespace BizHawk.Client.EmuHawk
{
if (_searches.Count > 0)
{
_searches.Update();
if (_autoSearch)
{
if (InputPollableCore != null && Settings.AutoSearchTakeLagFramesIntoAccount && InputPollableCore.IsLagFrame)
@ -278,24 +280,21 @@ namespace BizHawk.Client.EmuHawk
}
else
{
DoSearch(true);
DoSearch();
}
}
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 +521,14 @@ namespace BizHawk.Client.EmuHawk
}
}
public void DoSearch(bool updatePrevious = false)
public void DoSearch()
{
_searches.CompareValue = CompareToValue;
_searches.DifferentBy = DifferentByValue;
_searches.Operator = Operator;
_searches.CompareTo = Compare;
var removed = _searches.DoSearch(updatePrevious);
var removed = _searches.DoSearch();
UpdateList();
SetRemovedMessage(removed);
ToggleSearchDependentToolBarItems();
@ -588,6 +587,7 @@ namespace BizHawk.Client.EmuHawk
&& _settings.IsDetailed())
{
_settings.Mode = SearchMode.Fast;
SetReboot(true);
MessageLabel.Text = "Large domain, switching to fast mode";
}
}
@ -753,7 +753,6 @@ 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,6 +763,7 @@ namespace BizHawk.Client.EmuHawk
ChangesMenuItem.Checked = true;
ColumnToggleCallback();
SetReboot(true);
}
private ToolStripMenuItem ChangesMenuItem
@ -782,7 +782,11 @@ namespace BizHawk.Client.EmuHawk
private void SetToFastMode()
{
_settings.Mode = SearchMode.Fast;
_searches.SetMode(SearchMode.Fast);
if (_settings.PreviousType == PreviousType.LastFrame || _settings.PreviousType == PreviousType.LastChange)
{
SetPreviousType(PreviousType.LastSearch);
}
NumberOfChangesRadio.Enabled = false;
NumberOfChangesBox.Enabled = false;
@ -798,6 +802,7 @@ namespace BizHawk.Client.EmuHawk
ChangesMenuItem.Checked = false;
ColumnToggleCallback();
SetReboot(true);
}
private void RemoveAddresses()
@ -1079,6 +1084,7 @@ 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;
@ -1086,6 +1092,9 @@ namespace BizHawk.Client.EmuHawk
switch (_settings.PreviousType)
{
default:
case PreviousType.LastSearch:
Previous_LastSearchMenuItem.Checked = true;
break;
case PreviousType.LastFrame:
PreviousFrameMenuItem.Checked = true;
break;
@ -1096,6 +1105,9 @@ 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)
@ -1134,6 +1146,11 @@ 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);
@ -1152,6 +1169,8 @@ namespace BizHawk.Client.EmuHawk
private void SearchSubMenu_DropDownOpened(object sender, EventArgs e)
{
ClearChangeCountsMenuItem.Enabled = _settings.IsDetailed();
RemoveMenuItem.Enabled =
AddToRamWatchMenuItem.Enabled =
WatchListView.AnyRowsSelected;