using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using BizHawk.Common.NumberExtensions; using BizHawk.Common.StringExtensions; using BizHawk.Emulation.Common; namespace BizHawk.Client.Common { /// /// This class holds a word (16 bits) /// public sealed class WordWatch : Watch { private ushort _previous; private ushort _value; /// /// Initializes a new instance of the class /// /// where you want to track /// The address you want to track /// How you you want to display the value See /// Specify the endianess. true for big endian /// A custom note about the /// Current value /// Previous value /// How many times value has changed /// Occurs when a is incompatible with internal WordWatch(MemoryDomain domain, long address, DisplayType type, bool bigEndian, string note, ushort value, ushort previous, int changeCount) : base(domain, address, WatchSize.Word, type, bigEndian, note) { _value = value == 0 ? GetWord() : value; _previous = previous; ChangeCount = changeCount; } /// /// Gets an Enumeration of s that are valid for a /// public static IEnumerable ValidTypes { get { yield return DisplayType.Unsigned; yield return DisplayType.Signed; yield return DisplayType.Hex; yield return DisplayType.Binary; yield return DisplayType.FixedPoint_12_4; } } /// /// Get a list a that can be used for this /// /// An enumeration that contains all valid public override IEnumerable AvailableTypes() { return ValidTypes; } /// /// Reset the previous value; set it to the current one /// public override void ResetPrevious() { _previous = GetWord(); } /// /// Try to sets the value into the /// at the current address /// /// Value to set /// True if value successfully sets; otherwise, false public override bool Poke(string value) { try { ushort val = 0; switch (Type) { case DisplayType.Unsigned: if (value.IsUnsigned()) { val = (ushort)int.Parse(value); } else { return false; } break; case DisplayType.Signed: if (value.IsSigned()) { val = (ushort)(short)int.Parse(value); } else { return false; } break; case DisplayType.Hex: if (value.IsHex()) { val = (ushort)int.Parse(value, NumberStyles.HexNumber); } else { return false; } break; case DisplayType.Binary: if (value.IsBinary()) { val = (ushort)Convert.ToInt32(value, 2); } else { return false; } break; case DisplayType.FixedPoint_12_4: if (value.IsFixedPoint()) { val = (ushort)(double.Parse(value) * 16.0); } else { return false; } break; } if (Global.CheatList.Contains(Domain, Address)) { var cheat = Global.CheatList.FirstOrDefault(c => c.Address == Address && c.Domain == Domain); if (cheat != (Cheat)null) { cheat.PokeValue(val); PokeWord(val); return true; } } PokeWord(val); return true; } catch { return false; } } /// /// Update the Watch (read it from /// 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; } } // TODO: Implements IFormattable 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 $"{val / 16.0:F4}"; case DisplayType.Binary: return Convert.ToString(val, 2).PadLeft(16, '0').Insert(8, " ").Insert(4, " ").Insert(14, " "); } } /// /// Get a string representation of difference /// between current value and the previous one /// public override string Diff { get { string diff = ""; int diffVal = _value - _previous; if (diffVal > 0) { diff = "+"; } else if (diffVal < 0) { diff = "-"; } return $"{diff}{((ushort)Math.Abs(diffVal))}"; } } /// /// Get the maximum possible value /// public override uint MaxValue => ushort.MaxValue; /// /// Gets the current value /// public override int Value => GetWord(); /// /// Gets the current value /// but with stuff I don't understand /// public override int ValueNoFreeze => GetWord(true); /// /// Get a string representation of the current value /// public override string ValueString => FormatValue(GetWord()); /// /// Get the previous value /// public override int Previous => _previous; /// /// Get a string representation of the previous value /// public override string PreviousStr => FormatValue(_previous); } }