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); } } }