408 lines
7.6 KiB
C#
408 lines
7.6 KiB
C#
using System.Linq;
|
|
|
|
using BizHawk.Emulation.Common;
|
|
|
|
namespace BizHawk.Client.Common
|
|
{
|
|
public class Cheat
|
|
{
|
|
private readonly Watch _watch;
|
|
private int? _compare;
|
|
private int _val;
|
|
private bool _enabled;
|
|
|
|
public Cheat(Watch watch, int value, int? compare = null, bool enabled = true)
|
|
{
|
|
_enabled = enabled;
|
|
_watch = watch;
|
|
_compare = compare;
|
|
_val = value;
|
|
|
|
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
|
|
{
|
|
get { return new Cheat(SeparatorWatch.Instance, 0, null, false); }
|
|
}
|
|
|
|
public bool IsSeparator
|
|
{
|
|
get { return _watch.IsSeparator; }
|
|
}
|
|
|
|
public bool Enabled
|
|
{
|
|
get { return !IsSeparator && _enabled; }
|
|
}
|
|
|
|
public long? Address
|
|
{
|
|
get { return _watch.Address; }
|
|
}
|
|
|
|
public int? Value
|
|
{
|
|
get { return IsSeparator ? (int?)null : _val; }
|
|
}
|
|
|
|
public bool? BigEndian
|
|
{
|
|
get { return IsSeparator ? (bool?)null : _watch.BigEndian; }
|
|
}
|
|
|
|
public int? Compare
|
|
{
|
|
get { return _compare.HasValue && !IsSeparator ? _compare : null; }
|
|
}
|
|
|
|
public MemoryDomain Domain
|
|
{
|
|
get { return _watch.Domain; }
|
|
}
|
|
|
|
public WatchSize Size
|
|
{
|
|
get { return _watch.Size; }
|
|
}
|
|
|
|
public char SizeAsChar
|
|
{
|
|
get { return _watch.SizeAsChar; }
|
|
}
|
|
|
|
public DisplayType Type
|
|
{
|
|
get { return _watch.Type; }
|
|
}
|
|
|
|
public char TypeAsChar
|
|
{
|
|
get { return _watch.TypeAsChar; }
|
|
}
|
|
|
|
public string Name
|
|
{
|
|
get { return IsSeparator ? string.Empty : _watch.Notes; }
|
|
}
|
|
|
|
public string AddressStr
|
|
{
|
|
get { return _watch.AddressString; }
|
|
}
|
|
|
|
public string ValueStr
|
|
{
|
|
get
|
|
{
|
|
switch (_watch.Size)
|
|
{
|
|
default:
|
|
case WatchSize.Separator:
|
|
return string.Empty;
|
|
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 string.Empty;
|
|
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 string.Empty;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (_compare.Value == _watch.ValueNoFreeze)
|
|
{
|
|
_watch.Poke(GetStringForPulse(_val));
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
else if (addr == (_watch.Address) + 1)
|
|
{
|
|
return (byte)((_val >> 16) & 0xFF);
|
|
}
|
|
else 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()
|
|
{
|
|
if (Changed != null)
|
|
{
|
|
Changed(this);
|
|
}
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (obj is Watch)
|
|
{
|
|
var watch = obj as Watch;
|
|
return this.Domain == watch.Domain && this.Address == watch.Address;
|
|
}
|
|
|
|
if (obj is Cheat)
|
|
{
|
|
var cheat = obj as Cheat;
|
|
return this.Domain == cheat.Domain && this.Address == cheat.Address;
|
|
}
|
|
|
|
return base.Equals(obj);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return this.Domain.GetHashCode() + (int)(this.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);
|
|
}
|
|
}
|
|
}
|