BizHawk/BizHawk.Client.Common/tools/Watch.cs

1018 lines
21 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public abstract class Watch
{
public enum WatchSize { Byte = 1, Word = 2, DWord = 4, Separator = 0 }
public enum DisplayType { Separator, Signed, Unsigned, Hex, Binary, FixedPoint_12_4, FixedPoint_20_12, Float }
public enum PreviousType { Original = 0, LastSearch = 1, LastFrame = 2, LastChange = 3 }
public static string DisplayTypeToString(DisplayType type)
{
switch (type)
{
default:
return type.ToString();
case DisplayType.FixedPoint_12_4:
return "Fixed Point 12.4";
case DisplayType.FixedPoint_20_12:
return "Fixed Point 20.12";
}
}
public static DisplayType StringToDisplayType(string name)
{
switch (name)
{
default:
return (DisplayType)Enum.Parse(typeof(DisplayType), name);
case "Fixed Point 12.4":
return DisplayType.FixedPoint_12_4;
case "Fixed Point 20.12":
return DisplayType.FixedPoint_20_12;
}
}
protected int _address;
protected MemoryDomain _domain;
protected DisplayType _type;
protected bool _bigEndian;
protected int _changecount;
protected string _notes = string.Empty;
public abstract int? Value { get; }
public abstract string ValueString { get; }
public abstract WatchSize Size { get; }
public abstract int? Previous { get; }
public abstract string PreviousStr { get; }
public abstract void ResetPrevious();
public abstract bool Poke(string value);
public virtual DisplayType Type { get { return _type; } set { _type = value; } }
public virtual bool BigEndian { get { return _bigEndian; } set { _bigEndian = value; } }
public MemoryDomain Domain { get { return _domain; } }
public string DomainName { get { return _domain != null ? _domain.Name : String.Empty; } }
public virtual int? Address { get { return _address; } }
public virtual string AddressString { get { return _address.ToString(AddressFormatStr); } }
public virtual bool IsSeparator { get { return false; } }
public char SizeAsChar
{
get
{
switch (Size)
{
default:
case WatchSize.Separator:
return 'S';
case WatchSize.Byte:
return 'b';
case WatchSize.Word:
return 'w';
case WatchSize.DWord:
return 'd';
}
}
}
public static WatchSize SizeFromChar(char c)
{
switch (c)
{
default:
case 'S':
return WatchSize.Separator;
case 'b':
return WatchSize.Byte;
case 'w':
return WatchSize.Word;
case 'd':
return WatchSize.DWord;
}
}
public char TypeAsChar
{
get
{
switch (Type)
{
default:
case DisplayType.Separator:
return '_';
case DisplayType.Unsigned:
return 'u';
case DisplayType.Signed:
return 's';
case DisplayType.Hex:
return 'h';
case DisplayType.Binary:
return 'b';
case DisplayType.FixedPoint_12_4:
return '1';
case DisplayType.FixedPoint_20_12:
return '2';
case DisplayType.Float:
return 'f';
}
}
}
public static DisplayType DisplayTypeFromChar(char c)
{
switch (c)
{
default:
case '_':
return DisplayType.Separator;
case 'u':
return DisplayType.Unsigned;
case 's':
return DisplayType.Signed;
case 'h':
return DisplayType.Hex;
case 'b':
return DisplayType.Binary;
case '1':
return DisplayType.FixedPoint_12_4;
case '2':
return DisplayType.FixedPoint_20_12;
case 'f':
return DisplayType.Float;
}
}
public string AddressFormatStr
{
get
{
if (_domain != null)
{
return "X" + IntHelpers.GetNumDigits(this._domain.Size - 1);
}
return string.Empty;
}
}
protected byte GetByte()
{
return _domain.PeekByte(_address % _domain.Size);
}
protected ushort GetWord()
{
return _domain.PeekWord(_address % _domain.Size, _bigEndian); // TODO: % size stil lisn't correct since it could be the last byte of the domain
}
protected uint GetDWord()
{
return _domain.PeekDWord(_address % _domain.Size, _bigEndian); // TODO: % size stil lisn't correct since it could be the last byte of the domain
}
protected void PokeByte(byte val)
{
_domain.PokeByte(_address % _domain.Size, val);
}
protected void PokeWord(ushort val)
{
_domain.PokeWord(_address % _domain.Size, val, _bigEndian); // TODO: % size stil lisn't correct since it could be the last byte of the domain
}
protected void PokeDWord(uint val)
{
_domain.PokeDWord(_address % _domain.Size, val, _bigEndian); // TODO: % size stil lisn't correct since it could be the last byte of the domain
}
public void ClearChangeCount() { _changecount = 0; }
public string Notes { get { return _notes; } set { _notes = value; } }
public static Watch GenerateWatch(MemoryDomain domain, int address, WatchSize size, DisplayType type, string notes, bool bigEndian)
{
switch (size)
{
default:
case WatchSize.Separator:
return SeparatorWatch.Instance;
case WatchSize.Byte:
return new ByteWatch(domain, address, type, bigEndian, notes);
case WatchSize.Word:
return new WordWatch(domain, address, type, bigEndian, notes);
case WatchSize.DWord:
return new DWordWatch(domain, address, type, bigEndian, notes);
}
}
public static Watch GenerateWatch(MemoryDomain domain, int address, WatchSize size, DisplayType type, bool bigendian, int prev, int changecount)
{
switch (size)
{
default:
case WatchSize.Separator:
return SeparatorWatch.Instance;
case WatchSize.Byte:
return new ByteWatch(domain, address, type, bigendian, (byte)prev, changecount);
case WatchSize.Word:
return new WordWatch(domain, address, type, bigendian, (ushort)prev, changecount);
case WatchSize.DWord:
return new DWordWatch(domain, address, type, bigendian, (uint)prev, changecount);
}
}
public static List<DisplayType> AvailableTypes(WatchSize size)
{
switch (size)
{
default:
case WatchSize.Separator:
return SeparatorWatch.ValidTypes;
case WatchSize.Byte:
return ByteWatch.ValidTypes;
case WatchSize.Word:
return WordWatch.ValidTypes;
case WatchSize.DWord:
return DWordWatch.ValidTypes;
}
}
public int ChangeCount { get { return _changecount; } }
public abstract string Diff { get; }
public abstract void Update();
public override bool Equals(object obj)
{
if (obj is Watch)
{
var watch = obj as Watch;
return this.Domain == watch.Domain &&
this.Address == watch.Address &&
this.Size == watch.Size &&
this.Type == watch.Type &&
this.Notes == watch.Notes;
}
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() + this.Address ?? 0;
}
public static bool operator ==(Watch a, Watch b)
{
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
{
return false;
}
return a.Equals(b);
}
public static bool operator !=(Watch a, Watch b)
{
return !(a == b);
}
public static bool operator ==(Watch 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 !=(Watch a, Cheat b)
{
return !(a == b);
}
}
public sealed class SeparatorWatch : Watch
{
public static SeparatorWatch Instance
{
get { return new SeparatorWatch(); }
}
public override int? Address
{
get { return null; }
}
public override int? Value
{
get { return null; }
}
public override int? Previous
{
get { return null; }
}
public override string AddressString
{
get { return string.Empty; }
}
public override string ValueString
{
get { return string.Empty; }
}
public override string PreviousStr
{
get { return string.Empty; }
}
public override string ToString()
{
return "----";
}
public override bool IsSeparator
{
get { return true; }
}
public override WatchSize Size
{
get { return WatchSize.Separator; }
}
public static List<DisplayType> ValidTypes
{
get { return new List<DisplayType> { DisplayType.Separator }; }
}
public override DisplayType Type
{
get { return DisplayType.Separator; }
}
public override bool Poke(string value)
{
return false;
}
public override void ResetPrevious()
{
return;
}
public override string Diff { get { return string.Empty; } }
public override void Update() { return; }
}
public sealed class ByteWatch : Watch
{
private byte _previous;
private byte _value;
public ByteWatch(MemoryDomain domain, int address, DisplayType type, bool bigEndian, string notes)
{
_address = address;
_domain = domain;
_value = _previous = GetByte();
if (AvailableTypes(WatchSize.Byte).Contains(type))
{
_type = type;
}
_bigEndian = bigEndian;
if (notes != null)
{
Notes = notes;
}
}
public ByteWatch(MemoryDomain domain, int address, DisplayType type, bool bigEndian, byte prev, int changeCount, string notes = null)
: this(domain, address, type, bigEndian, notes)
{
_previous = prev;
_changecount = changeCount;
}
public override int? Address
{
get { return _address; }
}
public override int? Value
{
get { return GetByte(); }
}
public override string ValueString
{
get { return FormatValue(GetByte()); }
}
public override int? Previous
{
get { return _previous; }
}
public override string PreviousStr
{
get { return FormatValue(_previous); }
}
public override void ResetPrevious()
{
_previous = GetByte();
}
public override string ToString()
{
return Notes + ": " + ValueString;
}
public override bool IsSeparator
{
get { return false; }
}
public override WatchSize Size
{
get { return WatchSize.Byte; }
}
public static List<DisplayType> ValidTypes
{
get
{
return new List<DisplayType>
{
DisplayType.Unsigned, DisplayType.Signed, DisplayType.Hex, DisplayType.Binary
};
}
}
public string FormatValue(byte val)
{
switch (Type)
{
default:
case DisplayType.Unsigned:
return val.ToString();
case DisplayType.Signed:
return ((sbyte)val).ToString();
case DisplayType.Hex:
return val.ToHexString(2);
case DisplayType.Binary:
return Convert.ToString(val, 2).PadLeft(8, '0').Insert(4, " ");
}
}
public override bool Poke(string value)
{
try
{
byte val = 0;
switch (Type)
{
case DisplayType.Unsigned:
if (InputValidate.IsUnsigned(value))
{
val = (byte)int.Parse(value);
}
else
{
return false;
}
break;
case DisplayType.Signed:
if (InputValidate.IsSigned(value))
{
val = (byte)(sbyte)int.Parse(value);
}
else
{
return false;
}
break;
case DisplayType.Hex:
if (InputValidate.IsHex(value))
{
val = (byte)int.Parse(value, NumberStyles.HexNumber);
}
else
{
return false;
}
break;
case DisplayType.Binary:
if (InputValidate.IsBinary(value))
{
val = (byte)Convert.ToInt32(value, 2);
}
else
{
return false;
}
break;
}
PokeByte(val);
return true;
}
catch
{
return false;
}
}
public override string Diff
{
get
{
var diff = string.Empty;
var diffVal = _value - _previous;
if (diffVal > 0)
{
diff = "+";
}
else if (diffVal < 0)
{
diff = "-";
}
return diff + FormatValue((byte)(_previous - _value));
}
}
public override void Update()
{
switch (Global.Config.RamWatchDefinePrevious)
{
case PreviousType.Original:
return;
case PreviousType.LastChange:
var temp = _value;
_value = GetByte();
if (_value != temp)
{
_previous = _value;
_changecount++;
}
break;
case PreviousType.LastFrame:
_previous = _value;
_value = GetByte();
if (_value != Previous)
{
_changecount++;
}
break;
}
}
}
public sealed class WordWatch : Watch
{
private ushort _previous;
private ushort _value;
public WordWatch(MemoryDomain domain, int address, DisplayType type, bool bigEndian, string notes)
{
_domain = domain;
_address = address;
_value = _previous = GetWord();
if (AvailableTypes(WatchSize.Word).Contains(type))
{
_type = type;
}
_bigEndian = bigEndian;
if (notes != null)
{
Notes = notes;
}
}
public WordWatch(MemoryDomain domain, int address, DisplayType type, bool bigEndian, ushort prev, int changeCount, string notes = null)
: this(domain, address, type, bigEndian, notes)
{
_previous = prev;
_changecount = changeCount;
}
public override int? Value
{
get { return GetWord(); }
}
public override int? Previous
{
get { return _previous; }
}
public override string PreviousStr
{
get { return FormatValue(_previous); }
}
public override void ResetPrevious()
{
_previous = GetWord();
}
public override WatchSize Size
{
get { return WatchSize.Word; }
}
public static List<DisplayType> ValidTypes
{
get
{
return new List<DisplayType>
{
DisplayType.Unsigned, DisplayType.Signed, DisplayType.Hex, DisplayType.FixedPoint_12_4, DisplayType.Binary
};
}
}
public override string ValueString
{
get { return FormatValue(GetWord()); }
}
public override string ToString()
{
return Notes + ": " + ValueString;
}
public string FormatValue(ushort val)
{
switch (Type)
{
default:
case DisplayType.Unsigned:
return val.ToString();
case DisplayType.Signed:
return ((short)val).ToString();
case DisplayType.Hex:
return val.ToHexString(4);
case DisplayType.FixedPoint_12_4:
return string.Format("{0:F4}", val / 16.0);
case DisplayType.Binary:
return Convert.ToString(val, 2).PadLeft(16, '0').Insert(8, " ").Insert(4, " ").Insert(14, " ");
}
}
public override bool Poke(string value)
{
try
{
ushort val = 0;
switch (Type)
{
case DisplayType.Unsigned:
if (InputValidate.IsUnsigned(value))
{
val = (ushort)int.Parse(value);
}
else
{
return false;
}
break;
case DisplayType.Signed:
if (InputValidate.IsSigned(value))
{
val = (ushort)(short)int.Parse(value);
}
else
{
return false;
}
break;
case DisplayType.Hex:
if (InputValidate.IsHex(value))
{
val = (ushort)int.Parse(value, NumberStyles.HexNumber);
}
else
{
return false;
}
break;
case DisplayType.Binary:
if (InputValidate.IsBinary(value))
{
val = (ushort)Convert.ToInt32(value, 2);
}
else
{
return false;
}
break;
case DisplayType.FixedPoint_12_4:
if (InputValidate.IsFixedPoint(value))
{
val = (ushort)(double.Parse(value) * 16.0);
}
else
{
return false;
}
break;
}
PokeWord(val);
return true;
}
catch
{
return false;
}
}
public override string Diff
{
get { return FormatValue((ushort)(_previous - _value)); }
}
public override void Update()
{
switch (Global.Config.RamWatchDefinePrevious)
{
case PreviousType.Original:
return;
case PreviousType.LastChange:
var temp = _value;
_value = GetWord();
if (_value != temp)
{
_previous = temp;
_changecount++;
}
break;
case PreviousType.LastFrame:
_previous = _value;
_value = GetWord();
if (_value != Previous)
{
_changecount++;
}
break;
}
}
}
public sealed class DWordWatch : Watch
{
private uint _value;
private uint _previous;
public DWordWatch(MemoryDomain domain, int address, DisplayType type, bool bigEndian, string notes)
{
_domain = domain;
_address = address;
_value = _previous = GetDWord();
if (AvailableTypes(WatchSize.DWord).Contains(type))
{
_type = type;
}
_bigEndian = bigEndian;
if (notes != null)
{
Notes = notes;
}
}
public DWordWatch(MemoryDomain domain, int address, DisplayType type, bool bigEndian, uint prev, int changeCount, string notes = null)
: this(domain, address, type, bigEndian, notes)
{
_previous = prev;
_changecount = changeCount;
_type = type;
_bigEndian = bigEndian;
}
public override int? Value
{
get { return (int)GetDWord(); }
}
public override int? Previous
{
get { return (int)_previous; }
}
public override string PreviousStr
{
get { return FormatValue(_previous); }
}
public override void ResetPrevious()
{
_previous = GetWord();
}
public override WatchSize Size
{
get { return WatchSize.DWord; }
}
public static List<DisplayType> ValidTypes
{
get
{
return new List<DisplayType>
{
DisplayType.Unsigned, DisplayType.Signed, DisplayType.Hex, DisplayType.FixedPoint_20_12, DisplayType.Float
};
}
}
public override string ValueString
{
get { return FormatValue(GetDWord()); }
}
public override string ToString()
{
return Notes + ": " + ValueString;
}
public string FormatValue(uint val)
{
switch (Type)
{
default:
case DisplayType.Unsigned:
return val.ToString();
case DisplayType.Signed:
return ((int)val).ToString();
case DisplayType.Hex:
return val.ToHexString(8);
case DisplayType.FixedPoint_20_12:
return string.Format("{0:0.######}", val / 4096.0);
case DisplayType.Float:
var bytes = BitConverter.GetBytes(val);
var _float = BitConverter.ToSingle(bytes, 0);
//return string.Format("{0:0.######}", _float);
return _float.ToString(); // adelikat: decided that we like sci notation instead of spooky rounding
}
}
public override bool Poke(string value)
{
try
{
uint val = 0;
switch (Type)
{
case DisplayType.Unsigned:
if (InputValidate.IsUnsigned(value))
{
val = (uint)int.Parse(value);
}
else
{
return false;
}
break;
case DisplayType.Signed:
if (InputValidate.IsSigned(value))
{
val = (uint)int.Parse(value);
}
else
{
return false;
}
break;
case DisplayType.Hex:
if (InputValidate.IsHex(value))
{
val = (uint)int.Parse(value, NumberStyles.HexNumber);
}
else
{
return false;
}
break;
case DisplayType.FixedPoint_20_12:
if (InputValidate.IsFixedPoint(value))
{
val = (uint)(int)(double.Parse(value) * 4096.0);
}
else
{
return false;
}
break;
case DisplayType.Float:
if (InputValidate.IsFloat(value))
{
var bytes = BitConverter.GetBytes(float.Parse(value));
val = BitConverter.ToUInt32(bytes, 0);
}
else
{
return false;
}
break;
}
PokeDWord(val);
return true;
}
catch
{
return false;
}
}
public override string Diff
{
get { return FormatValue(_previous - _value); }
}
public override void Update()
{
switch (Global.Config.RamWatchDefinePrevious)
{
case PreviousType.Original:
return;
case PreviousType.LastChange:
var temp = _value;
_value = GetDWord();
if (_value != temp)
{
_previous = _value;
_changecount++;
}
break;
case PreviousType.LastFrame:
_previous = _value;
_value = GetDWord();
if (_value != Previous)
{
_changecount++;
}
break;
}
}
}
}