using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Diagnostics;
using BizHawk.Common.NumberExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
///
/// This class holds a watch i.e. something inside a identified by an address
/// with a specific size (8, 16 or 32bits).
/// This is an abstract class
///
[DebuggerDisplay("Note={Notes}, Value={ValueString}")]
public abstract partial class Watch
: IEquatable,
IEquatable,
IComparable
{
#region Fields
protected long _address;
protected MemoryDomain _domain;
protected DisplayType _type;
protected WatchSize _size;
protected bool _bigEndian;
protected string _notes;
protected int _changecount = 0;
#endregion
#region cTor(s)
///
/// Initialize a new instance of
///
/// where you want to track
/// The address you want to track
/// A (byte, word, double word)
/// How you you want to display the value See
/// Specify the endianess. true for big endian
/// A custom note about the
/// Occurs when a is incompatible with the
protected Watch(MemoryDomain domain, long address, WatchSize size, DisplayType type, bool bigEndian, string note)
{
if (IsDiplayTypeAvailable(type))
{
this._domain = domain;
this._address = address;
this._size = size;
this._type = type;
this._bigEndian = bigEndian;
this._notes = note;
return;
}
else
{
throw new ArgumentException(string.Format("DisplayType {0} is invalid for this type of Watch", type.ToString()), "type");
}
}
#endregion
#region Methods
#region Static
///
/// Generate sa from a given string
/// String is tab separate
///
/// Entire string, tab seperated for each value Order is:
///
/// -
/// 0x00
/// Address in hexadecimal
///
/// -
/// b,w or d
/// The , byte, word or double word
/// s, u, h, b, 1, 2, 3, f
/// The signed, unsigned,etc...
///
/// -
/// 0 or 1
/// Big endian or not
///
/// -
/// RDRAM,ROM,...
/// The
///
/// -
/// Plain text
/// Notes
///
///
///
/// 's memory domain
/// A brand new
public static Watch FromString(string line, IMemoryDomains domains)
{
string[] parts = line.Split(new char[] { '\t' }, 6);
if (parts.Length < 6)
{
if (parts.Length >= 3 && parts[2] == "_")
{
return SeparatorWatch.Instance;
}
return null;
}
long address;
if (long.TryParse(parts[0], NumberStyles.HexNumber, CultureInfo.CurrentCulture, out address))
{
WatchSize size = Watch.SizeFromChar(parts[1][0]);
DisplayType type = Watch.DisplayTypeFromChar(parts[2][0]);
bool bigEndian = parts[3] == "0" ? false : true;
MemoryDomain domain = domains[parts[4]];
string notes = parts[5].Trim(new char[] { '\r', '\n' });
return Watch.GenerateWatch(
domain,
address,
size,
type,
bigEndian,
notes
);
}
else
{
return null;
}
}
///
/// Generates a new instance
/// Can be either , , or
///
/// The where you want to watch
/// The address into the
/// The size
/// How the watch will be displayed
/// Endianess (true for big endian)
/// A custom note about the
/// The current watch value
/// Previous value
/// Number of changes occurs in current
/// New instance. True type is depending of size parameter
public static Watch GenerateWatch(MemoryDomain domain, long address, WatchSize size, DisplayType type, bool bigEndian, string note, long value, long prev, int changeCount)
{
switch (size)
{
default:
case WatchSize.Separator:
return SeparatorWatch.Instance;
case WatchSize.Byte:
return new ByteWatch(domain, address, type, bigEndian, note, (byte)value, (byte)prev, changeCount);
case WatchSize.Word:
return new WordWatch(domain, address, type, bigEndian, note, (ushort)value, (ushort)prev, changeCount);
case WatchSize.DWord:
return new DWordWatch(domain, address, type, bigEndian, note, (uint)value, (uint)prev, changeCount);
}
}
///
/// Generates a new instance
/// Can be either , , or
///
/// The where you want to watch
/// The address into the
/// The size
/// How the watch will be displayed
/// Endianess (true for big endian)
/// A customp note about your watch
/// New instance. True type is depending of size parameter
public static Watch GenerateWatch(MemoryDomain domain, long address, WatchSize size, DisplayType type, bool bigEndian, string note)
{
return GenerateWatch(domain, address, size, type, bigEndian, note, 0, 0, 0);
}
///
/// Generates a new instance
/// Can be either , , or
///
/// The where you want to watch
/// The address into the
/// The size
/// How the watch will be displayed
/// Endianess (true for big endian)
/// New instance. True type is depending of size parameter
public static Watch GenerateWatch(MemoryDomain domain, long address, WatchSize size, DisplayType type, bool bigEndian)
{
return GenerateWatch(domain, address, size, type, bigEndian, string.Empty, 0, 0, 0);
}
#region Operators
///
/// Equality operator between two
///
/// First watch
/// Second watch
/// True if both watch are equals; otherwise, false
public static bool operator ==(Watch a, Watch b)
{
if (object.ReferenceEquals(a, null) || object.ReferenceEquals(b, null))
{
return false;
}
else if (object.ReferenceEquals(a, b))
{
return true;
}
else
{
return a.Equals(b);
}
}
///
/// Equality operator between a and a
///
/// The watch
/// The cheat
/// True if they are equals; otherwise, false
public static bool operator ==(Watch a, Cheat b)
{
if (object.ReferenceEquals(a, null) || object.ReferenceEquals(b, null))
{
return false;
}
else if (object.ReferenceEquals(a, b))
{
return true;
}
else
{
return a.Equals(b);
}
}
///
/// Inequality operator between two
///
/// First watch
/// Second watch
/// True if both watch are different; otherwise, false
public static bool operator !=(Watch a, Watch b)
{
return !(a == b);
}
///
/// Inequality operator between a and a
///
/// The watch
/// The cheat
/// True if they are different; otherwise, false
public static bool operator !=(Watch a, Cheat b)
{
return !(a == b);
}
///
/// Compare two together
///
/// First
/// Second
/// True if first is lesser than b; otherwise, false
/// Occurs when you try to compare two throughout different
public static bool operator <(Watch a, Watch b)
{
return a.CompareTo(b) < 0;
}
///
/// Compare two together
///
/// First
/// Second
/// True if first is greater than b; otherwise, false
/// Occurs when you try to compare two throughout different
public static bool operator >(Watch a, Watch b)
{
return a.CompareTo(b) > 0;
}
///
/// Compare two together
///
/// First
/// Second
/// True if first is lesser or equals to b; otherwise, false
/// Occurs when you try to compare two throughout different
public static bool operator <=(Watch a, Watch b)
{
return a.CompareTo(b) <= 0;
}
///
/// Compare two together
///
/// First
/// Second
/// True if first is greater or equals to b; otherwise, false
/// Occurs when you try to compare two throughout different
public static bool operator >=(Watch a, Watch b)
{
return a.CompareTo(b) >= 0;
}
#endregion Operators
#endregion Static
#region Abstracts
///
/// Gets a list a that can be used for this
///
/// An enumartion that contains all valid
public abstract IEnumerable AvailableTypes();
///
/// Resets the previous value; set it to the current one
///
public abstract void ResetPrevious();
///
/// Updates the Watch (read it from
///
public abstract void Update();
#endregion Abstracts
#region Protected
protected byte GetByte(bool bypassFreeze = false)
{
if (!bypassFreeze && Global.CheatList.IsActive(_domain, _address))
{
//LIAR logic
return Global.CheatList.GetByteValue(_domain, _address) ?? 0;
}
else
{
if (_domain.Size == 0)
{
return _domain.PeekByte(_address);
}
else
{
return _domain.PeekByte(_address % _domain.Size);
}
}
}
protected ushort GetWord(bool bypassFreeze = false)
{
if (!bypassFreeze && Global.CheatList.IsActive(_domain, _address))
{
//LIAR logic
return (ushort)(Global.CheatList.GetCheatValue(_domain, _address, WatchSize.Word) ?? 0);
}
else
{
if (_domain.Size == 0)
{
return _domain.PeekWord(_address, _bigEndian);
}
else
{
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(bool bypassFreeze = false)
{
if (!bypassFreeze && Global.CheatList.IsActive(_domain, _address))
{
//LIAR logic
return (uint)(Global.CheatList.GetCheatValue(_domain, _address, WatchSize.DWord) ?? 0);
}
else
{
if (_domain.Size == 0)
{
return _domain.PeekDWord(_address, _bigEndian); // TODO: % size stil lisn't correct since it could be the last byte of the domain
}
else
{
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)
{
if (_domain.Size == 0)
_domain.PokeByte(_address, val);
else _domain.PokeByte(_address % _domain.Size, val);
}
protected void PokeWord(ushort val)
{
if (_domain.Size == 0)
_domain.PokeWord(_address, val, _bigEndian); // TODO: % size stil lisn't correct since it could be the last byte of the domain
else _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)
{
if (_domain.Size == 0)
_domain.PokeDWord(_address, val, _bigEndian); // TODO: % size stil lisn't correct since it could be the last byte of the domain
else _domain.PokeDWord(_address % _domain.Size, val, _bigEndian); // TODO: % size stil lisn't correct since it could be the last byte of the domain
}
#endregion Protected
///
/// Sets the number of changes to 0
///
public void ClearChangeCount()
{
_changecount = 0;
}
#region IEquatable
///
/// Determines if this is equals to another
///
/// The to compare
/// True if both object are equals; otherwise, false
public bool Equals(Watch other)
{
if (object.ReferenceEquals(other, null))
{
return false;
}
else
{
return this._domain == other._domain &&
this._address == other._address &&
this._size == other._size;
}
}
#endregion IEquatable
#region IEquatable
///
/// Determines if this is equals to an instance of
///
/// The to compare
/// True if both object are equals; otherwise, false
public bool Equals(Cheat other)
{
return !object.ReferenceEquals(other, null) &&
this._domain == other.Domain &&
this._address == other.Address &&
this._size == other.Size;
}
#endregion IEquatable
#region IComparable
///
/// Compares two together and determine wich one comes first.
/// First we look the address and then the size
///
/// The other to compare to
/// 0 if they are equals, 1 if the other is greater, -1 if the other is lesser
/// Occurs when you try to compare two throughout different
public int CompareTo(Watch other)
{
if (this._domain != other._domain)
{
throw new InvalidOperationException("Watch cannot be compared through different domain");
}
if (this.Equals(other))
{
return 0;
}
else if (object.ReferenceEquals(other, null))
{
return 1;
}
else if (_address.Equals(other._address))
{
return ((int)_size).CompareTo((int)other._size);
}
else
{
return _address.CompareTo(other._address);
}
}
#endregion IComparable
///
/// Determines if this object is Equals to another
///
/// The object to compare
/// True if both object are equals; otherwise, false
public override bool Equals(object obj)
{
if (obj is Watch)
{
return Equals((Watch)obj);
}
else if (obj is Cheat)
{
return Equals((Cheat)obj);
}
else
{
return base.Equals(obj);
}
}
///
/// Hash the current watch and gets a unique value
///
/// that can serves as a unique representation of current Watch
public override int GetHashCode()
{
return this.Domain.GetHashCode() + (int)(this.Address);
}
///
/// Determines if the specified can be
/// used for the current
///
/// you want to check
///
public bool IsDiplayTypeAvailable(DisplayType type)
{
return AvailableTypes().Where(d => d == type).Any();
}
///
/// Transforms the current instance into a string
///
/// A representation of the current
public override string ToString()
{
return string.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}"
, Domain == null && Address == 0 ? "0" : Address.ToHexString((Domain.Size - 1).NumHexDigits())
, SizeAsChar
, TypeAsChar
, Convert.ToInt32(BigEndian)
, DomainName
, Notes.Trim('\r', '\n')
);
}
///
/// Transform the current instance into a displayable (short representation) string
/// It's used by the "Display on screen" option in the RamWatch window
///
/// A well formatted string representation
public virtual string ToDisplayString()
{
return string.Format("{0}: {1}", Notes, ValueString);
}
#endregion
#region Properties
#region Abstracts
///
/// Get a string representation of difference
/// between current value and the previous one
///
public abstract string Diff { get; }
///
/// Get the maximum possible value
///
public abstract uint MaxValue { get; }
///
/// Get the current value
///
public abstract int Value { get; }
///
/// Gets the current value
/// but with stuff I don't understand
///
/// zero 15-nov-2015 - bypass LIAR LOGIC, see fdc9ea2aa922876d20ba897fb76909bf75fa6c92 https://github.com/TASVideos/BizHawk/issues/326
public abstract int ValueNoFreeze { get; }
///
/// Get a string representation of the current value
///
public abstract string ValueString { get; }
///
/// Try to sets the value into the
/// at the current address
///
/// Value to set
/// True if value successfully sets; othewise, false
public abstract bool Poke(string value);
///
/// Get the previous value
///
public abstract int Previous { get; }
///
/// Get a string representation of the previous value
///
public abstract string PreviousStr { get; }
#endregion Abstracts
///
/// Gets the address in the
///
public long Address
{
get
{
return _address;
}
}
///
/// Gets the format tha should be used by string.Format()
///
private string AddressFormatStr
{
get
{
if (_domain != null)
{
return "X" + (_domain.Size - 1).NumHexDigits();
}
return string.Empty;
}
}
///
/// Gets the address in the formatted as string
///
public string AddressString
{
get
{
return _address.ToString(AddressFormatStr);
}
}
///
/// Gets or sets the endianess of current
/// True for big endian, flase for little endian
///
public bool BigEndian
{
get
{
return _bigEndian;
}
set
{
_bigEndian = value;
}
}
///
/// Gets the number of time tha value of current has changed
///
public int ChangeCount
{
get
{
return _changecount;
}
}
///
/// Gets or set the way current is displayed
///
/// Occurs when a is incompatible with the
public DisplayType Type
{
get
{
return _type;
}
set
{
if (IsDiplayTypeAvailable(value))
{
_type = value;
}
else
{
throw new ArgumentException(string.Format("DisplayType {0} is invalid for this type of Watch", value.ToString()));
}
}
}
///
/// Gets or sets current
///
public MemoryDomain Domain
{
get
{
return _domain;
}
internal set
{
if (_domain.Name == value.Name)
{
_domain = value;
}
else
{
throw new InvalidOperationException("You cannot set diffrent domain to a watch on the fly");
}
}
}
///
/// Gets the domain name of the current
/// It's the same of doing myWatch.Domain.Name
///
public string DomainName
{
get
{
if (_domain != null)
{
return _domain.Name;
}
else
{
return string.Empty;
}
}
}
///
/// Gets a value that defined if the current address is
/// well in the range of current
///
public bool IsOutOfRange
{
get
{
return !IsSeparator && (_domain.Size != 0 && _address >= _domain.Size);
}
}
///
/// Gets a value that defined if the current is actually a
///
public bool IsSeparator
{
get
{
return this is SeparatorWatch;
}
}
///
/// Gets or sets notes for current
///
public string Notes
{
get
{
return _notes;
}
set
{
_notes = value;
}
}
///
/// Gets the current size of the watch
///
public WatchSize Size
{
get
{
return _size;
}
}
#endregion
//TODO: Replace all the following stuff by implementing ISerializable
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";
case DisplayType.FixedPoint_16_16:
return "Fixed Point 16.16";
}
}
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;
case "Fixed Point 16.16":
return DisplayType.FixedPoint_16_16;
}
}
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.FixedPoint_16_16:
return '3';
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 '3':
return DisplayType.FixedPoint_16_16;
case 'f':
return DisplayType.Float;
}
}
}
}