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