415 lines
8.6 KiB
C#
415 lines
8.6 KiB
C#
using BizHawk.Emulation.Common;
|
|
|
|
namespace BizHawk.Client.Common
|
|
{
|
|
public class Cheat
|
|
{
|
|
public enum COMPARISONTYPE
|
|
{
|
|
NONE,
|
|
EQUAL,
|
|
GREATER_THAN,
|
|
GREATER_THAN_OR_EQUAL,
|
|
LESS_THAN,
|
|
LESS_THAN_OR_EQUAL,
|
|
NOT_EQUAL
|
|
}
|
|
|
|
private readonly Watch _watch;
|
|
private int? _compare;
|
|
private int _val;
|
|
private bool _enabled;
|
|
private readonly COMPARISONTYPE _comparisonType;
|
|
|
|
|
|
public Cheat(Watch watch, int value, int? compare = null, bool enabled = true, COMPARISONTYPE comparisonType = COMPARISONTYPE.NONE)
|
|
{
|
|
_enabled = enabled;
|
|
_watch = watch;
|
|
_compare = compare;
|
|
_val = value;
|
|
_comparisonType = comparisonType;
|
|
|
|
Pulse();
|
|
}
|
|
|
|
public Cheat(Cheat cheat)
|
|
{
|
|
if (cheat.IsSeparator)
|
|
{
|
|
_enabled = false;
|
|
_watch = SeparatorWatch.Instance;
|
|
_compare = null;
|
|
}
|
|
else
|
|
{
|
|
_enabled = cheat.Enabled;
|
|
_watch = Watch.GenerateWatch(
|
|
cheat.Domain,
|
|
cheat.Address ?? 0,
|
|
cheat.Size,
|
|
cheat.Type,
|
|
cheat.BigEndian ?? false,
|
|
cheat.Name);
|
|
_compare = cheat.Compare;
|
|
_val = cheat.Value ?? 0;
|
|
|
|
Pulse();
|
|
}
|
|
}
|
|
|
|
public delegate void CheatEventHandler(object sender);
|
|
public event CheatEventHandler Changed;
|
|
|
|
public static Cheat Separator => new Cheat(SeparatorWatch.Instance, 0, null, false);
|
|
|
|
public bool IsSeparator => _watch.IsSeparator;
|
|
|
|
public bool Enabled => !IsSeparator && _enabled;
|
|
|
|
public long? Address => _watch.Address;
|
|
|
|
public int? Value => IsSeparator ? (int?)null : _val;
|
|
|
|
public bool? BigEndian => IsSeparator ? (bool?)null : _watch.BigEndian;
|
|
|
|
public int? Compare => _compare.HasValue && !IsSeparator ? _compare : null;
|
|
|
|
public MemoryDomain Domain => _watch.Domain;
|
|
|
|
public WatchSize Size => _watch.Size;
|
|
|
|
public char SizeAsChar => _watch.SizeAsChar;
|
|
|
|
public DisplayType Type => _watch.Type;
|
|
|
|
public char TypeAsChar => _watch.TypeAsChar;
|
|
|
|
public string Name => IsSeparator ? "" : _watch.Notes;
|
|
|
|
public string AddressStr => _watch.AddressString;
|
|
|
|
public string ValueStr
|
|
{
|
|
get
|
|
{
|
|
switch (_watch.Size)
|
|
{
|
|
default:
|
|
case WatchSize.Separator:
|
|
return "";
|
|
case WatchSize.Byte:
|
|
return (_watch as ByteWatch).FormatValue((byte)_val);
|
|
case WatchSize.Word:
|
|
return (_watch as WordWatch).FormatValue((ushort)_val);
|
|
case WatchSize.DWord:
|
|
return (_watch as DWordWatch).FormatValue((uint)_val);
|
|
}
|
|
}
|
|
}
|
|
|
|
public string CompareStr
|
|
{
|
|
get
|
|
{
|
|
if (_compare.HasValue)
|
|
{
|
|
switch (_watch.Size)
|
|
{
|
|
default:
|
|
case WatchSize.Separator:
|
|
return "";
|
|
case WatchSize.Byte:
|
|
return (_watch as ByteWatch).FormatValue((byte)_compare.Value);
|
|
case WatchSize.Word:
|
|
return (_watch as WordWatch).FormatValue((ushort)_compare.Value);
|
|
case WatchSize.DWord:
|
|
return (_watch as DWordWatch).FormatValue((uint)_compare.Value);
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
}
|
|
|
|
public COMPARISONTYPE ComparisonType => _comparisonType;
|
|
|
|
public void Enable(bool handleChange = true)
|
|
{
|
|
if (!IsSeparator)
|
|
{
|
|
var wasEnabled = _enabled;
|
|
_enabled = true;
|
|
if (!wasEnabled && handleChange)
|
|
{
|
|
Changes();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Disable(bool handleChange = true)
|
|
{
|
|
if (!IsSeparator)
|
|
{
|
|
var wasEnabled = _enabled;
|
|
_enabled = false;
|
|
if (wasEnabled && handleChange)
|
|
{
|
|
Changes();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Toggle(bool handleChange = true)
|
|
{
|
|
if (!IsSeparator)
|
|
{
|
|
_enabled ^= true;
|
|
if (handleChange)
|
|
{
|
|
Changes();
|
|
}
|
|
}
|
|
}
|
|
|
|
private string GetStringForPulse(int val)
|
|
{
|
|
if (_watch.Type == DisplayType.Hex)
|
|
{
|
|
return val.ToString("X8");
|
|
}
|
|
|
|
return val.ToString();
|
|
}
|
|
|
|
public void Pulse()
|
|
{
|
|
if (!IsSeparator && _enabled)
|
|
{
|
|
if (_compare.HasValue)
|
|
{
|
|
switch (_comparisonType)
|
|
{
|
|
default:
|
|
case COMPARISONTYPE.NONE: // This should never happen, but it's here just in case. adelikat: And yet it does! Cheat Code converter doesn't do this. Changing this to default to equal since 99.9999% of all cheats are going to be equals
|
|
case COMPARISONTYPE.EQUAL:
|
|
if (_compare.Value == _watch.ValueNoFreeze)
|
|
{
|
|
_watch.Poke(GetStringForPulse(_val));
|
|
}
|
|
break;
|
|
case COMPARISONTYPE.GREATER_THAN:
|
|
if (_compare.Value > _watch.ValueNoFreeze)
|
|
{
|
|
_watch.Poke(GetStringForPulse(_val));
|
|
}
|
|
break;
|
|
case COMPARISONTYPE.GREATER_THAN_OR_EQUAL:
|
|
if (_compare.Value >= _watch.ValueNoFreeze)
|
|
{
|
|
_watch.Poke(GetStringForPulse(_val));
|
|
}
|
|
break;
|
|
case COMPARISONTYPE.LESS_THAN:
|
|
if (_compare.Value < _watch.ValueNoFreeze)
|
|
{
|
|
_watch.Poke(GetStringForPulse(_val));
|
|
}
|
|
break;
|
|
case COMPARISONTYPE.LESS_THAN_OR_EQUAL:
|
|
if (_compare.Value <= _watch.ValueNoFreeze)
|
|
{
|
|
_watch.Poke(GetStringForPulse(_val));
|
|
}
|
|
break;
|
|
case COMPARISONTYPE.NOT_EQUAL:
|
|
if (_compare.Value != _watch.ValueNoFreeze)
|
|
{
|
|
_watch.Poke(GetStringForPulse(_val));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (_watch.Size)
|
|
{
|
|
case WatchSize.Byte:
|
|
_watch.Poke((_watch as ByteWatch).FormatValue((byte)_val));
|
|
break;
|
|
case WatchSize.Word:
|
|
_watch.Poke((_watch as WordWatch).FormatValue((ushort)_val));
|
|
break;
|
|
case WatchSize.DWord:
|
|
_watch.Poke((_watch as DWordWatch).FormatValue((uint)_val));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool Contains(long addr)
|
|
{
|
|
switch (_watch.Size)
|
|
{
|
|
default:
|
|
case WatchSize.Separator:
|
|
return false;
|
|
case WatchSize.Byte:
|
|
return _watch.Address == addr;
|
|
case WatchSize.Word:
|
|
return (addr == _watch.Address) || (addr == _watch.Address + 1);
|
|
case WatchSize.DWord:
|
|
return (addr == _watch.Address) || (addr == _watch.Address + 1) ||
|
|
(addr == _watch.Address + 2) || (addr == _watch.Address + 3);
|
|
}
|
|
}
|
|
|
|
public byte? GetByteVal(long addr)
|
|
{
|
|
if (!Contains(addr))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
switch (_watch.Size)
|
|
{
|
|
default:
|
|
case WatchSize.Separator:
|
|
case WatchSize.Byte:
|
|
return (byte?)_val;
|
|
case WatchSize.Word:
|
|
if (addr == _watch.Address)
|
|
{
|
|
return (byte)(_val >> 8);
|
|
}
|
|
|
|
return (byte)(_val & 0xFF);
|
|
case WatchSize.DWord:
|
|
if (addr == _watch.Address)
|
|
{
|
|
return (byte)((_val >> 24) & 0xFF);
|
|
}
|
|
|
|
if (addr == _watch.Address + 1)
|
|
{
|
|
return (byte)((_val >> 16) & 0xFF);
|
|
}
|
|
|
|
if (addr == _watch.Address + 2)
|
|
{
|
|
return (byte)((_val >> 8) & 0xFF);
|
|
}
|
|
|
|
return (byte)(_val & 0xFF);
|
|
}
|
|
}
|
|
|
|
public void PokeValue(int val)
|
|
{
|
|
if (!IsSeparator)
|
|
{
|
|
_val = val;
|
|
}
|
|
}
|
|
|
|
public void Increment()
|
|
{
|
|
if (!IsSeparator)
|
|
{
|
|
_val++;
|
|
if (_val > _watch.MaxValue)
|
|
{
|
|
_val = 0;
|
|
}
|
|
|
|
Pulse();
|
|
Changes();
|
|
}
|
|
}
|
|
|
|
public void Decrement()
|
|
{
|
|
if (!IsSeparator)
|
|
{
|
|
_val--;
|
|
if ((uint)_val > _watch.MaxValue)
|
|
{
|
|
_val = (int)_watch.MaxValue;
|
|
}
|
|
|
|
Pulse();
|
|
Changes();
|
|
}
|
|
}
|
|
|
|
public void SetType(DisplayType type)
|
|
{
|
|
if (_watch.IsDiplayTypeAvailable(type))
|
|
{
|
|
_watch.Type = type;
|
|
Changes();
|
|
}
|
|
}
|
|
|
|
private void Changes()
|
|
{
|
|
Changed?.Invoke(this);
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (obj is Watch)
|
|
{
|
|
var watch = obj as Watch;
|
|
return Domain == watch.Domain && Address == watch.Address;
|
|
}
|
|
|
|
if (obj is Cheat)
|
|
{
|
|
var cheat = obj as Cheat;
|
|
return Domain == cheat.Domain && Address == cheat.Address;
|
|
}
|
|
|
|
return base.Equals(obj);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return Domain.GetHashCode() + (int)(Address ?? 0);
|
|
}
|
|
|
|
public static bool operator ==(Cheat a, Cheat b)
|
|
{
|
|
// If one is null, but not both, return false.
|
|
if (((object)a == null) || ((object)b == null))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return a.Domain == b.Domain && a.Address == b.Address;
|
|
}
|
|
|
|
public static bool operator !=(Cheat a, Cheat b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
public static bool operator ==(Cheat a, Watch b)
|
|
{
|
|
// If one is null, but not both, return false.
|
|
if (((object)a == null) || ((object)b == null))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return a.Domain == b.Domain && a.Address == b.Address;
|
|
}
|
|
|
|
public static bool operator !=(Cheat a, Watch b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
}
|
|
}
|