718 lines
16 KiB
C#
718 lines
16 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace BizHawk.MultiClient
|
|
{
|
|
public enum atype { BYTE, WORD, DWORD, SEPARATOR }; //TODO: more custom types too like 12.4 and 24.12 fixed point
|
|
public enum asigned { SIGNED, UNSIGNED, HEX };
|
|
public enum prevDef { LASTSEARCH, ORIGINAL, LASTFRAME, LASTCHANGE };
|
|
|
|
/// <summary>
|
|
/// An object that represent a ram address and related properties
|
|
/// </summary>
|
|
public class Watch
|
|
{
|
|
public Watch()
|
|
{
|
|
address = 0;
|
|
value = 0;
|
|
type = atype.BYTE;
|
|
signed = asigned.UNSIGNED;
|
|
bigendian = true;
|
|
notes = "";
|
|
changecount = 0;
|
|
prev = 0;
|
|
original = 0;
|
|
lastchange = 0;
|
|
lastsearch = 0;
|
|
}
|
|
|
|
public Watch(Watch w)
|
|
{
|
|
address = w.address;
|
|
value = w.value;
|
|
type = w.type;
|
|
signed = w.signed;
|
|
bigendian = w.bigendian;
|
|
notes = w.notes;
|
|
changecount = w.changecount;
|
|
prev = w.prev;
|
|
original = w.original;
|
|
lastchange = w.lastchange;
|
|
lastsearch = w.lastsearch;
|
|
}
|
|
|
|
public Watch(int Address, int Value, atype Type, asigned Signed, bool BigEndian, string Notes)
|
|
{
|
|
address = Address;
|
|
value = Value;
|
|
type = Type;
|
|
signed = Signed;
|
|
bigendian = BigEndian;
|
|
notes = Notes;
|
|
changecount = 0;
|
|
prev = Value;
|
|
original = Value;
|
|
lastchange = Value;
|
|
lastsearch = Value;
|
|
}
|
|
public int address { get; set; }
|
|
public int value { get; set; } //Current value
|
|
public int prev { get; set; }
|
|
public int original { get; set; }
|
|
public int lastchange { get; set; }
|
|
public int lastsearch { get; set; }
|
|
public int diffPrev { get { return value - prev; } }
|
|
public int diffOriginal { get { return value - original; } }
|
|
public int diffLastChange { get { return value - lastchange; } }
|
|
public int diffLastSearch { get { return value - lastsearch; } }
|
|
public atype type { get; set; } //Address type (byte, word, dword, etc
|
|
public asigned signed { get; set; } //Signed/Unsigned?
|
|
public bool bigendian { get; set; }
|
|
public string notes { get; set; } //User notes
|
|
public int changecount { get; set; }
|
|
|
|
|
|
public bool SetTypeByChar(char c) //b = byte, w = word, d = dword
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'b':
|
|
type = atype.BYTE;
|
|
return true;
|
|
case 'w':
|
|
type = atype.WORD;
|
|
return true;
|
|
case 'd':
|
|
type = atype.DWORD;
|
|
return true;
|
|
case 'S':
|
|
type = atype.SEPARATOR;
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public char GetTypeByChar()
|
|
{
|
|
switch (type)
|
|
{
|
|
case atype.BYTE:
|
|
return 'b';
|
|
case atype.WORD:
|
|
return 'w';
|
|
case atype.DWORD:
|
|
return 'd';
|
|
case atype.SEPARATOR:
|
|
return 'S';
|
|
default:
|
|
return 'b'; //Just in case
|
|
}
|
|
}
|
|
|
|
public bool SetSignedByChar(char c) //s = signed, u = unsigned, h = hex
|
|
{
|
|
switch (c)
|
|
{
|
|
case 's':
|
|
signed = asigned.SIGNED;
|
|
return true;
|
|
case 'u':
|
|
signed = asigned.UNSIGNED;
|
|
return true;
|
|
case 'h':
|
|
signed = asigned.HEX;
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public char GetSignedByChar()
|
|
{
|
|
switch (signed)
|
|
{
|
|
case asigned.SIGNED:
|
|
return 's';
|
|
case asigned.UNSIGNED:
|
|
return 'u';
|
|
case asigned.HEX:
|
|
return 'h';
|
|
default:
|
|
return 's'; //Just in case
|
|
}
|
|
}
|
|
|
|
public void PeekAddress(MemoryDomain domain)
|
|
{
|
|
if (type == atype.SEPARATOR)
|
|
return;
|
|
|
|
prev = value;
|
|
|
|
switch (type)
|
|
{
|
|
case atype.BYTE:
|
|
value = domain.PeekByte(address);
|
|
break;
|
|
case atype.WORD:
|
|
if (bigendian)
|
|
{
|
|
value = 0;
|
|
value |= domain.PeekByte(address) << 8;
|
|
value |= domain.PeekByte(address + 1);
|
|
}
|
|
else
|
|
{
|
|
value = 0;
|
|
value |= domain.PeekByte(address);
|
|
value |= domain.PeekByte(address + 1) << 8;
|
|
}
|
|
break;
|
|
case atype.DWORD:
|
|
if (bigendian)
|
|
{
|
|
value = 0;
|
|
value |= domain.PeekByte(address) << 24;
|
|
value |= domain.PeekByte(address + 1) << 16;
|
|
value |= domain.PeekByte(address + 2) << 8;
|
|
value |= domain.PeekByte(address + 3) << 0;
|
|
}
|
|
else
|
|
{
|
|
value = 0;
|
|
value |= domain.PeekByte(address) << 0;
|
|
value |= domain.PeekByte(address + 1) << 8;
|
|
value |= domain.PeekByte(address + 2) << 16;
|
|
value |= domain.PeekByte(address + 3) << 24;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (value != prev)
|
|
{
|
|
lastchange = prev;
|
|
changecount++;
|
|
}
|
|
}
|
|
|
|
private void PokeByte(MemoryDomain domain)
|
|
{
|
|
domain.PokeByte(address, (byte)value);
|
|
}
|
|
|
|
private void PokeWord(MemoryDomain domain)
|
|
{
|
|
if (bigendian)
|
|
{
|
|
domain.PokeByte(address + 0, (byte)(value >> 8));
|
|
domain.PokeByte(address + 1, (byte)(value));
|
|
}
|
|
else
|
|
{
|
|
domain.PokeByte(address + 0, (byte)(value));
|
|
domain.PokeByte(address + 1, (byte)(value >> 8));
|
|
}
|
|
}
|
|
|
|
private void PokeDWord(MemoryDomain domain)
|
|
{
|
|
if (bigendian)
|
|
{
|
|
domain.PokeByte(address + 0, (byte)(value << 24));
|
|
domain.PokeByte(address + 1, (byte)(value << 16));
|
|
domain.PokeByte(address + 2, (byte)(value << 8));
|
|
domain.PokeByte(address + 3, (byte)(value));
|
|
}
|
|
else
|
|
{
|
|
domain.PokeByte(address + 0, (byte)(value));
|
|
domain.PokeByte(address + 1, (byte)(value << 8));
|
|
domain.PokeByte(address + 2, (byte)(value << 16));
|
|
domain.PokeByte(address + 3, (byte)(value << 24));
|
|
}
|
|
}
|
|
|
|
public void PokeAddress(MemoryDomain domain)
|
|
{
|
|
if (type == atype.SEPARATOR)
|
|
return;
|
|
|
|
switch (type)
|
|
{
|
|
case atype.BYTE:
|
|
PokeByte(domain);
|
|
break;
|
|
case atype.WORD:
|
|
PokeWord(domain);
|
|
break;
|
|
case atype.DWORD:
|
|
PokeDWord(domain);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public uint UnsignedVal(int val)
|
|
{
|
|
switch (type)
|
|
{
|
|
case atype.BYTE:
|
|
return (uint)(byte)val;
|
|
case atype.WORD:
|
|
return (uint)(ushort)val;
|
|
}
|
|
return (uint)val;
|
|
}
|
|
|
|
public int SignedVal(int val)
|
|
{
|
|
switch (type)
|
|
{
|
|
case atype.BYTE:
|
|
return (int)(sbyte)val;
|
|
case atype.WORD:
|
|
return (int)(short)val;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
if (type == atype.SEPARATOR)
|
|
return "----";
|
|
|
|
StringBuilder str = new StringBuilder(notes);
|
|
str.Append(": ");
|
|
str.Append(ValToString(value));
|
|
return str.ToString();
|
|
}
|
|
|
|
public string ValToString(int val)
|
|
{
|
|
if (type == atype.SEPARATOR)
|
|
return "";
|
|
else
|
|
{
|
|
switch (signed)
|
|
{
|
|
default:
|
|
case asigned.UNSIGNED:
|
|
return UnsignedVal(val).ToString();
|
|
case asigned.SIGNED:
|
|
return SignedVal(val).ToString();
|
|
case asigned.HEX:
|
|
switch (type)
|
|
{
|
|
default:
|
|
case atype.BYTE:
|
|
return String.Format("{0:X2}", val);
|
|
case atype.WORD:
|
|
return String.Format("{0:X4}", val);
|
|
case atype.DWORD:
|
|
return String.Format("{0:X8}", val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public string DiffToString(int diff)
|
|
{
|
|
string converted = diff.ToString();
|
|
if (diff >= 0)
|
|
converted = "+" + converted;
|
|
return converted;
|
|
}
|
|
|
|
public string ValueToString()
|
|
{
|
|
return ValToString(value);
|
|
}
|
|
|
|
public string PrevToString()
|
|
{
|
|
return ValToString(prev);
|
|
}
|
|
|
|
public string OriginalToString()
|
|
{
|
|
return ValToString(original);
|
|
}
|
|
|
|
public string LastChangeToString()
|
|
{
|
|
return ValToString(lastchange);
|
|
}
|
|
|
|
public string LastSearchToString()
|
|
{
|
|
return ValToString(lastsearch);
|
|
}
|
|
|
|
public string DiffPrevToString()
|
|
{
|
|
return DiffToString(prev);
|
|
}
|
|
|
|
public string DiffOriginalToString()
|
|
{
|
|
return DiffToString(original);
|
|
}
|
|
|
|
public string DiffLastChangeToString()
|
|
{
|
|
return DiffToString(lastchange);
|
|
}
|
|
|
|
public string DiffLastSearchToString()
|
|
{
|
|
return DiffToString(lastsearch);
|
|
}
|
|
|
|
private int ComparePrevious(Watch Other, prevDef previous)
|
|
{
|
|
switch (previous)
|
|
{
|
|
case prevDef.LASTSEARCH:
|
|
return CompareLastSearch(Other);
|
|
case prevDef.ORIGINAL:
|
|
return CompareOriginal(Other);
|
|
default:
|
|
case prevDef.LASTFRAME:
|
|
return ComparePrev(Other);
|
|
case prevDef.LASTCHANGE:
|
|
return CompareLastChange(Other);
|
|
}
|
|
}
|
|
|
|
private int CompareDiff(Watch Other, prevDef previous)
|
|
{
|
|
switch (previous)
|
|
{
|
|
case prevDef.LASTSEARCH:
|
|
return CompareDiffLastSearch(Other);
|
|
case prevDef.ORIGINAL:
|
|
return CompareDiffOriginal(Other);
|
|
default:
|
|
case prevDef.LASTFRAME:
|
|
return CompareDiffPrev(Other);
|
|
case prevDef.LASTCHANGE:
|
|
return CompareDiffLastChange(Other);
|
|
}
|
|
}
|
|
|
|
private int CompareAddress(Watch Other)
|
|
{
|
|
if (this.address < Other.address)
|
|
return -1;
|
|
else if (this.address > Other.address)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
private int CompareValue(Watch Other)
|
|
{
|
|
if (signed == asigned.SIGNED)
|
|
{
|
|
if (SignedVal(this.value) < SignedVal(Other.value))
|
|
return -1;
|
|
else if (SignedVal(this.value) > SignedVal(Other.value))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
if (UnsignedVal(this.value) < UnsignedVal(Other.value))
|
|
return -1;
|
|
else if (UnsignedVal(this.value) > UnsignedVal(Other.value))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
private int ComparePrev(Watch Other)
|
|
{
|
|
if (signed == asigned.SIGNED)
|
|
{
|
|
if (SignedVal(this.prev) < SignedVal(Other.prev))
|
|
return -1;
|
|
else if (SignedVal(this.prev) > SignedVal(Other.prev))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
if (UnsignedVal(this.prev) < UnsignedVal(Other.prev))
|
|
return -1;
|
|
else if (UnsignedVal(this.prev) > UnsignedVal(Other.prev))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
private int CompareOriginal(Watch Other)
|
|
{
|
|
if (signed == asigned.SIGNED)
|
|
{
|
|
if (SignedVal(this.original) < SignedVal(Other.original))
|
|
return -1;
|
|
else if (SignedVal(this.original) > SignedVal(Other.original))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
if (UnsignedVal(this.original) < UnsignedVal(Other.original))
|
|
return -1;
|
|
else if (UnsignedVal(this.original) > UnsignedVal(Other.original))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
private int CompareLastChange(Watch Other)
|
|
{
|
|
if (signed == asigned.SIGNED)
|
|
{
|
|
if (SignedVal(this.lastchange) < SignedVal(Other.lastchange))
|
|
return -1;
|
|
else if (SignedVal(this.lastchange) > SignedVal(Other.lastchange))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
if (UnsignedVal(this.lastchange) < UnsignedVal(Other.lastchange))
|
|
return -1;
|
|
else if (UnsignedVal(this.lastchange) > UnsignedVal(Other.lastchange))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
private int CompareLastSearch(Watch Other)
|
|
{
|
|
if (signed == asigned.SIGNED)
|
|
{
|
|
if (SignedVal(this.lastsearch) < SignedVal(Other.lastsearch))
|
|
return -1;
|
|
else if (SignedVal(this.lastsearch) > SignedVal(Other.lastsearch))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
if (UnsignedVal(this.lastsearch) < UnsignedVal(Other.lastsearch))
|
|
return -1;
|
|
else if (UnsignedVal(this.lastsearch) > UnsignedVal(Other.lastsearch))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
private int CompareDiffPrev(Watch Other)
|
|
{
|
|
if (this.diffPrev < Other.diffPrev)
|
|
return -1;
|
|
else if (this.diffPrev > Other.diffPrev)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
private int CompareDiffOriginal(Watch Other)
|
|
{
|
|
if (this.diffOriginal < Other.diffOriginal)
|
|
return -1;
|
|
else if (this.diffOriginal > Other.diffOriginal)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
private int CompareDiffLastChange(Watch Other)
|
|
{
|
|
if (this.diffLastChange < Other.diffLastChange)
|
|
return -1;
|
|
else if (this.diffLastChange > Other.diffLastChange)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
private int CompareDiffLastSearch(Watch Other)
|
|
{
|
|
if (this.diffLastSearch < Other.diffLastSearch)
|
|
return -1;
|
|
else if (this.diffLastSearch > Other.diffLastSearch)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
private int CompareChanges(Watch Other)
|
|
{
|
|
if (this.changecount < Other.changecount)
|
|
return -1;
|
|
else if (this.changecount > Other.changecount)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
private int CompareNotes(Watch Other)
|
|
{
|
|
if (this.notes == null & Other.notes == null)
|
|
return 0;
|
|
else if (this.notes == null)
|
|
return -1;
|
|
else if (Other.notes == null)
|
|
return 1;
|
|
else
|
|
return this.notes.CompareTo(Other.notes);
|
|
}
|
|
|
|
public int CompareTo(Watch Other, string parameter, prevDef previous)
|
|
{
|
|
int compare = 0;
|
|
if (parameter == "Address")
|
|
{
|
|
compare = CompareAddress(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareValue(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareChanges(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = ComparePrevious(Other, previous);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareDiff(Other, previous);
|
|
if (compare == 0)
|
|
compare = CompareNotes(Other);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (parameter == "Value")
|
|
{
|
|
compare = CompareValue(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareAddress(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareChanges(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = ComparePrevious(Other, previous);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareDiff(Other, previous);
|
|
if (compare == 0)
|
|
compare = CompareNotes(Other);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (parameter == "Changes")
|
|
{
|
|
compare = CompareChanges(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareAddress(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareValue(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = ComparePrevious(Other, previous);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareDiff(Other, previous);
|
|
if (compare == 0)
|
|
compare = CompareNotes(Other);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (parameter == "Prev")
|
|
{
|
|
compare = ComparePrevious(Other, previous);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareAddress(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareValue(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareChanges(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareDiff(Other, previous);
|
|
if (compare == 0)
|
|
compare = CompareNotes(Other);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (parameter == "Diff")
|
|
{
|
|
compare = CompareDiff(Other, previous);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareAddress(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareValue(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareChanges(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = ComparePrevious(Other, previous);
|
|
if (compare == 0)
|
|
compare = CompareNotes(Other);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (parameter == "Notes")
|
|
{
|
|
compare = CompareNotes(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareAddress(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareValue(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = CompareChanges(Other);
|
|
if (compare == 0)
|
|
{
|
|
compare = ComparePrevious(Other, previous);
|
|
if (compare == 0)
|
|
compare = CompareDiff(Other, previous);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return compare;
|
|
}
|
|
}
|
|
}
|