diff --git a/BizHawk.MultiClient/BizHawk.MultiClient.csproj b/BizHawk.MultiClient/BizHawk.MultiClient.csproj index cbc4444871..ba6997d78b 100644 --- a/BizHawk.MultiClient/BizHawk.MultiClient.csproj +++ b/BizHawk.MultiClient/BizHawk.MultiClient.csproj @@ -702,6 +702,7 @@ WatchEditor.cs + FFmpegWriterForm.cs diff --git a/BizHawk.MultiClient/BizHawk.MultiClient_v4.5.csproj b/BizHawk.MultiClient/BizHawk.MultiClient_v4.5.csproj index ec1746a5e4..7d23f5f1c2 100644 --- a/BizHawk.MultiClient/BizHawk.MultiClient_v4.5.csproj +++ b/BizHawk.MultiClient/BizHawk.MultiClient_v4.5.csproj @@ -704,6 +704,7 @@ WatchEditor.cs + FFmpegWriterForm.cs diff --git a/BizHawk.MultiClient/tools/Watch/NewRamPoke.cs b/BizHawk.MultiClient/tools/Watch/NewRamPoke.cs index e5dfe10fd8..8abdaf648f 100644 --- a/BizHawk.MultiClient/tools/Watch/NewRamPoke.cs +++ b/BizHawk.MultiClient/tools/Watch/NewRamPoke.cs @@ -65,6 +65,7 @@ namespace BizHawk.MultiClient DisplayTypeLabel.Text = Watch.DisplayTypeToString(_watchList[0].Type); BigEndianLabel.Text = _watchList[0].BigEndian ? "Big Endian" : "Little Endian"; SetTitle(); + SetValueBoxProperties(); } private void SetValueBoxProperties() @@ -187,6 +188,13 @@ namespace BizHawk.MultiClient e.Handled = true; } } + else if (e.KeyChar == '-') + { + if (ValueBox.Text.Contains('-')) + { + e.Handled = true; + } + } switch(_watchList[0].Type) { diff --git a/BizHawk.MultiClient/tools/Watch/NewRamWatch.cs b/BizHawk.MultiClient/tools/Watch/NewRamWatch.cs index ab72902cda..0e0463600c 100644 --- a/BizHawk.MultiClient/tools/Watch/NewRamWatch.cs +++ b/BizHawk.MultiClient/tools/Watch/NewRamWatch.cs @@ -1,8 +1,6 @@ using System; -using System.Collections; using System.Collections.Generic; using System.ComponentModel; -using System.Data; using System.Drawing; using System.Linq; using System.Text; @@ -21,8 +19,8 @@ namespace BizHawk.MultiClient public const string DOMAIN = "DomainColumn"; public const string NOTES = "NotesColumn"; - private Dictionary DefaultColumnWidths = new Dictionary() - { + private readonly Dictionary DefaultColumnWidths = new Dictionary + { { ADDRESS, 60 }, { VALUE, 59 }, { PREV, 59 }, @@ -34,8 +32,7 @@ namespace BizHawk.MultiClient private int defaultWidth; private int defaultHeight; - private WatchList Watches = new WatchList(Global.Emulator.MainMemory); - private string systemID = "NULL"; + private readonly WatchList Watches = new WatchList(Global.Emulator.MainMemory); private string _sortedColumn = ""; private bool _sortReverse = false; @@ -465,7 +462,7 @@ namespace BizHawk.MultiClient private void AddNewWatch() { - WatchEditor we = new WatchEditor() + WatchEditor we = new WatchEditor { InitialLocation = GetPromptPoint() }; @@ -489,7 +486,7 @@ namespace BizHawk.MultiClient if (indexes.Count > 0) { - WatchEditor we = new WatchEditor() + WatchEditor we = new WatchEditor { InitialLocation = GetPromptPoint(), }; @@ -528,8 +525,8 @@ namespace BizHawk.MultiClient { if (SelectedWatches.Any()) { - NewRamPoke poke = new NewRamPoke() - { + NewRamPoke poke = new NewRamPoke + { InitialLocation = GetPromptPoint() }; @@ -568,32 +565,14 @@ namespace BizHawk.MultiClient } } - private List SelectedIndexes - { - get - { - var selected = new List(); - ListView.SelectedIndexCollection indexes = WatchListView.SelectedIndices; - if (indexes.Count > 0) - { - foreach (int index in indexes) - { - selected.Add(Watches[index]); - } - } - return selected; - } - } - - private void AddColumn(string columnName, bool enabled) { if (enabled) { if (WatchListView.Columns[columnName] == null) { - ColumnHeader column = new ColumnHeader() - { + ColumnHeader column = new ColumnHeader + { Name = columnName, Text = columnName.Replace("Column", ""), Width = GetColumnWidth(columnName), @@ -1028,7 +1007,7 @@ namespace BizHawk.MultiClient { Size = new Size(defaultWidth, defaultHeight); - Global.Config.RamWatchColumnIndexes = new Dictionary() + Global.Config.RamWatchColumnIndexes = new Dictionary { { "AddressColumn", 0 }, { "ValueColumn", 1 }, diff --git a/BizHawk.MultiClient/tools/Watch/Watch.cs b/BizHawk.MultiClient/tools/Watch/Watch.cs index 85b26b5648..2037f3ef1c 100644 --- a/BizHawk.MultiClient/tools/Watch/Watch.cs +++ b/BizHawk.MultiClient/tools/Watch/Watch.cs @@ -1,11 +1,6 @@ using System; -using System.Text; using System.Globalization; -using System.Collections; using System.Collections.Generic; -using System.Linq; -using System.IO; -using System.Windows.Forms; namespace BizHawk.MultiClient { @@ -290,8 +285,6 @@ namespace BizHawk.MultiClient public class SeparatorWatch : Watch { - public SeparatorWatch() { } - public static SeparatorWatch Instance { get { return new SeparatorWatch(); } @@ -334,7 +327,7 @@ namespace BizHawk.MultiClient public static List ValidTypes { - get { return new List() { DisplayType.Separator }; } + get { return new List { DisplayType.Separator }; } } public override DisplayType Type @@ -390,7 +383,7 @@ namespace BizHawk.MultiClient { get { - return new List() + return new List { DisplayType.Unsigned, DisplayType.Signed, DisplayType.Hex, DisplayType.Binary }; @@ -403,7 +396,7 @@ namespace BizHawk.MultiClient { default: case DisplayType.Unsigned: - return ((byte)val).ToString(); + return val.ToString(); case DisplayType.Signed: return ((sbyte)val).ToString(); case DisplayType.Hex: @@ -567,7 +560,7 @@ namespace BizHawk.MultiClient { get { - return new List() + return new List { DisplayType.Unsigned, DisplayType.Signed, DisplayType.Hex, DisplayType.FixedPoint_12_4, DisplayType.Binary }; @@ -596,7 +589,7 @@ namespace BizHawk.MultiClient case DisplayType.Hex: return String.Format("{0:X4}", val); case DisplayType.FixedPoint_12_4: - return String.Format("{0:F4}", ((double)val / 16.0)); + 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, " "); } @@ -753,8 +746,8 @@ namespace BizHawk.MultiClient { get { - return new List() - { + return new List + { DisplayType.Unsigned, DisplayType.Signed, DisplayType.Hex, DisplayType.FixedPoint_20_12, DisplayType.Float }; } @@ -782,10 +775,10 @@ namespace BizHawk.MultiClient case DisplayType.Hex: return String.Format("{0:X8}", val); case DisplayType.FixedPoint_20_12: - return String.Format("{0:F5}", ((double)val / 4096.0)); + return String.Format("{0:F5}", (val / 4096.0)); case DisplayType.Float: byte[] bytes = BitConverter.GetBytes(val); - float _float = System.BitConverter.ToSingle(bytes, 0); + float _float = BitConverter.ToSingle(bytes, 0); return String.Format("{0:F6}", _float); } } @@ -883,7 +876,7 @@ namespace BizHawk.MultiClient public string Diff { - get { return FormatValue((uint)(_previous - _value)); } + get { return FormatValue(_previous - _value); } } public string Notes { get; set; } @@ -916,576 +909,4 @@ namespace BizHawk.MultiClient } } } - - public class WatchList : IEnumerable - { - private string _currentFilename = ""; - - public enum WatchPrevDef { LastSearch, Original, LastFrame, LastChange }; - - private List _watchList = new List(); - private MemoryDomain _domain = null; - - public WatchList(MemoryDomain domain) - { - _domain = domain; - } - - public IEnumerator GetEnumerator() - { - return _watchList.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public int Count - { - get { return _watchList.Count; } - } - - public Watch this[int index] - { - get - { - return _watchList[index]; - } - set - { - _watchList[index] = value; - } - } - - public int WatchCount - { - get - { - return _watchList.Count(w => !w.IsSeparator); - } - } - - public int ItemCount - { - get - { - return _watchList.Count; - } - } - - public void OrderWatches(string column, bool reverse) - { - switch (column) - { - case NewRamWatch.ADDRESS: - if (reverse) - { - _watchList = _watchList - .OrderByDescending(x => x.Address ?? 0) - .ThenBy(x => x.Domain.Name) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ThenBy(x => x.BigEndian) - .ToList(); - } - else - { - _watchList = _watchList - .OrderBy(x => x.Address ?? 0) - .ThenBy(x => x.Domain.Name) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ThenBy(x => x.BigEndian) - .ToList(); - } - break; - case NewRamWatch.VALUE: - if (reverse) - { - _watchList = _watchList - .OrderByDescending(x => x.Value ?? 0) - .ThenBy(x => x.Address ?? 0) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ThenBy(x => x.BigEndian) - .ToList(); - } - else - { - _watchList = _watchList - .OrderBy(x => x.Value ?? 0) - .ThenBy(x => x.Address ?? 0) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ThenBy(x => x.BigEndian) - .ToList(); - } - break; - case NewRamWatch.PREV: //Note: these only work if all entries are detailed objects! - if (reverse) - { - _watchList = _watchList - .OrderByDescending(x => (x as IWatchDetails).PreviousStr) - .ThenBy(x => x.Address ?? 0) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ToList(); - } - else - { - _watchList = _watchList - .OrderBy(x => (x as IWatchDetails).PreviousStr) - .ThenBy(x => x.Address ?? 0) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ToList(); - } - break; - case NewRamWatch.DIFF: - if (reverse) - { - _watchList = _watchList - .OrderByDescending(x => (x as IWatchDetails).Diff) - .ThenBy(x => x.Address ?? 0) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ToList(); - } - else - { - _watchList = _watchList - .OrderBy(x => (x as IWatchDetails).Diff) - .ThenBy(x => x.Address ?? 0) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ToList(); - } - break; - case NewRamWatch.CHANGES: - if (reverse) - { - _watchList = _watchList - .OrderByDescending(x => (x as IWatchDetails).ChangeCount) - .ThenBy(x => x.Address ?? 0) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ToList(); - } - else - { - _watchList = _watchList - .OrderBy(x => (x as IWatchDetails).ChangeCount) - .ThenBy(x => x.Address ?? 0) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ToList(); - } - break; - case NewRamWatch.DOMAIN: - if (reverse) - { - _watchList = _watchList - .OrderByDescending(x => x.Domain) - .ThenBy(x => x.Address ?? 0) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ThenBy(x => x.BigEndian) - .ToList(); - } - else - { - _watchList = _watchList - .OrderBy(x => x.Domain) - .ThenBy(x => x.Address ?? 0) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ThenBy(x => x.BigEndian) - .ToList(); - } - break; - case NewRamWatch.NOTES: - if (reverse) - { - _watchList = _watchList - .OrderByDescending(x => (x as IWatchDetails).Notes) - .ThenBy(x => x.Address ?? 0) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ToList(); - } - else - { - _watchList = _watchList - .OrderBy(x => (x as IWatchDetails).Notes) - .ThenBy(x => x.Address ?? 0) - .ThenBy(x => x.Size) - .ThenBy(x => x.Type) - .ToList(); - } - break; - } - } - - public string AddressFormatStr - { - get - { - if (_domain != null) - { - return "{0:X" + IntHelpers.GetNumDigits(_domain.Size - 1).ToString() + "}"; - } - else - { - return ""; - } - } - } - - public void Clear() - { - _watchList.Clear(); - Changes = false; - _currentFilename = ""; - } - - public MemoryDomain Domain { get { return _domain; } set { _domain = value; } } - - public void UpdateValues() - { - var detailedWatches = _watchList.OfType().ToList(); - foreach (var watch in detailedWatches) - { - watch.Update(); - } - } - - public void Add(Watch watch) - { - _watchList.Add(watch); - Changes = true; - } - - public void AddRange(IList watches) - { - _watchList.AddRange(watches); - Changes = true; - } - - public void Remove(Watch watch) - { - _watchList.Remove(watch); - Changes = true; - } - - public void Insert(int index, Watch watch) - { - _watchList.Insert(index, watch); - } - - public void ClearChangeCounts() - { - var detailedWatches = _watchList.OfType().ToList(); - foreach (var watch in detailedWatches) - { - watch.ClearChangeCount(); - } - } - - #region File handling logic - probably needs to be its own class - - public string CurrentFileName { get { return _currentFilename; } set { _currentFilename = value; } } - public bool Changes { get; set; } - - public bool Save() - { - bool result = false; - if (!String.IsNullOrWhiteSpace(CurrentFileName)) - { - result = SaveFile(); - } - else - { - result = SaveAs(); - } - - if (result) - { - Changes = false; - } - - return result; - } - - public bool Load(string path, bool details, bool append) - { - bool result = LoadFile(path, details, append); - - if (result) - { - if (append) - { - Changes = true; - } - else - { - CurrentFileName = path; - Changes = false; - } - } - - return result; - } - - public void Reload() - { - if (!String.IsNullOrWhiteSpace(CurrentFileName)) - { - LoadFile(CurrentFileName, true, false); - Changes = false; - } - } - - private bool SaveFile() - { - if (String.IsNullOrWhiteSpace(CurrentFileName)) - { - return false; - } - - using (StreamWriter sw = new StreamWriter(CurrentFileName)) - { - StringBuilder sb = new StringBuilder(); - sb - .Append("Domain ").AppendLine(_domain.Name) - .Append("SystemID ").AppendLine(Global.Emulator.SystemId); - - foreach (Watch w in _watchList) - { - sb - .Append(String.Format(AddressFormatStr, w.Address)).Append('\t') - .Append(w.SizeAsChar).Append('\t') - .Append(w.TypeAsChar).Append('\t') - .Append(w.BigEndian ? '1' : '0').Append('\t') - .Append(w.Domain.Name).Append('\t') - .Append(w is IWatchDetails ? (w as IWatchDetails).Notes : String.Empty) - .AppendLine(); - } - - sw.WriteLine(sb.ToString()); - } - - return true; - } - - public bool SaveAs() - { - var file = WatchCommon.GetSaveFileFromUser(CurrentFileName); - if (file != null) - { - CurrentFileName = file.FullName; - return SaveFile(); - } - else - { - return false; - } - } - - private bool LoadFile(string path, bool details, bool append) - { - string domain = ""; - var file = new FileInfo(path); - if (file.Exists == false) return false; - bool isBizHawkWatch = true; //Hack to support .wch files from other emulators - bool isOldBizHawkWatch = false; - using (StreamReader sr = file.OpenText()) - { - string line; - - if (append == false) - { - Clear(); - } - - while ((line = sr.ReadLine()) != null) - { - //.wch files from other emulators start with a number representing the number of watch, that line can be discarded here - //Any properly formatted line couldn't possibly be this short anyway, this also takes care of any garbage lines that might be in a file - if (line.Length < 5) - { - isBizHawkWatch = false; - continue; - } - - if (line.Length >= 6 && line.Substring(0, 6) == "Domain") - { - domain = line.Substring(7, line.Length - 7); - isBizHawkWatch = true; - } - - if (line.Length >= 8 && line.Substring(0, 8) == "SystemID") - { - continue; - } - - int numColumns = StringHelpers.HowMany(line, '\t'); - int startIndex; - if (numColumns == 5) - { - //If 5, then this is a post 1.0.5 .wch file - if (isBizHawkWatch) - { - //Do nothing here - } - else - { - startIndex = line.IndexOf('\t') + 1; - line = line.Substring(startIndex, line.Length - startIndex); //5 digit value representing the watch position number - } - } - else if (numColumns == 4) - { - isOldBizHawkWatch = true; - } - else //4 is 1.0.5 and earlier - { - continue; //If not 4, something is wrong with this line, ignore it - } - - - - //Temporary, rename if kept - int addr = 0; - Watch.WatchSize size = Watch.WatchSize.Separator; - Watch.DisplayType type = Watch.DisplayType.Unsigned; - bool bigEndian = false; - MemoryDomain memDomain = Global.Emulator.MainMemory; - string notes; - - string temp = line.Substring(0, line.IndexOf('\t')); - try - { - addr = Int32.Parse(temp, NumberStyles.HexNumber); - } - catch - { - continue; - } - - startIndex = line.IndexOf('\t') + 1; - line = line.Substring(startIndex, line.Length - startIndex); //Type - size = Watch.SizeFromChar(line[0]); - - - startIndex = line.IndexOf('\t') + 1; - line = line.Substring(startIndex, line.Length - startIndex); //Signed - type = Watch.DisplayTypeFromChar(line[0]); - - startIndex = line.IndexOf('\t') + 1; - line = line.Substring(startIndex, line.Length - startIndex); //Endian - try - { - startIndex = Int16.Parse(line[0].ToString()); - } - catch - { - continue; - } - if (startIndex == 0) - { - bigEndian = false; - } - else - { - bigEndian = true; - } - - if (isBizHawkWatch && !isOldBizHawkWatch) - { - startIndex = line.IndexOf('\t') + 1; - line = line.Substring(startIndex, line.Length - startIndex); //Domain - temp = line.Substring(0, line.IndexOf('\t')); - memDomain = Global.Emulator.MemoryDomains[GetDomainPos(temp)]; - } - - startIndex = line.IndexOf('\t') + 1; - notes = line.Substring(startIndex, line.Length - startIndex); //User notes - - Watch w = Watch.GenerateWatch(memDomain, addr, size, details); - w.BigEndian = bigEndian; - w.Type = type; - if (w is IWatchDetails) - { - (w as IWatchDetails).Notes = notes; - } - - _watchList.Add(w); - _domain = Global.Emulator.MemoryDomains[GetDomainPos(domain)]; - } - } - - return true; - } - - private static int GetDomainPos(string name) - { - //Attempts to find the memory domain by name, if it fails, it defaults to index 0 - for (int x = 0; x < Global.Emulator.MemoryDomains.Count; x++) - { - if (Global.Emulator.MemoryDomains[x].Name == name) - return x; - } - return 0; - } - - public static FileInfo GetFileFromUser(string currentFile) - { - var ofd = new OpenFileDialog(); - if (currentFile.Length > 0) - ofd.FileName = Path.GetFileNameWithoutExtension(currentFile); - ofd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.WatchPath, null); - ofd.Filter = "Watch Files (*.wch)|*.wch|All Files|*.*"; - ofd.RestoreDirectory = true; - - Global.Sound.StopSound(); - var result = ofd.ShowDialog(); - Global.Sound.StartSound(); - if (result != DialogResult.OK) - return null; - var file = new FileInfo(ofd.FileName); - return file; - } - - public static FileInfo GetSaveFileFromUser(string currentFile) - { - var sfd = new SaveFileDialog(); - if (currentFile.Length > 0) - { - sfd.FileName = Path.GetFileNameWithoutExtension(currentFile); - sfd.InitialDirectory = Path.GetDirectoryName(currentFile); - } - else if (!(Global.Emulator is NullEmulator)) - { - sfd.FileName = PathManager.FilesystemSafeName(Global.Game); - sfd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.WatchPath, null); - } - else - { - sfd.FileName = "NULL"; - sfd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.WatchPath, null); - } - sfd.Filter = "Watch Files (*.wch)|*.wch|All Files|*.*"; - sfd.RestoreDirectory = true; - Global.Sound.StopSound(); - var result = sfd.ShowDialog(); - Global.Sound.StartSound(); - if (result != DialogResult.OK) - return null; - var file = new FileInfo(sfd.FileName); - return file; - } - - #endregion - } } diff --git a/BizHawk.MultiClient/tools/Watch/WatchEditor.cs b/BizHawk.MultiClient/tools/Watch/WatchEditor.cs index 20db7124c2..632256689f 100644 --- a/BizHawk.MultiClient/tools/Watch/WatchEditor.cs +++ b/BizHawk.MultiClient/tools/Watch/WatchEditor.cs @@ -10,7 +10,7 @@ namespace BizHawk.MultiClient { public enum Mode { New, Duplicate, Edit }; - private List _watchList = new List(); + private readonly List _watchList = new List(); private Mode _mode = Mode.New; private bool _loading = true; private string _addressFormatStr = "{0:X2}"; @@ -102,13 +102,13 @@ namespace BizHawk.MultiClient switch(_mode) { default: - case WatchEditor.Mode.New: + case Mode.New: Text = "New Watch"; break; - case WatchEditor.Mode.Edit: + case Mode.Edit: Text = "Edit Watch" + (_watchList.Count > 1 ? "es" : ""); break; - case WatchEditor.Mode.Duplicate: + case Mode.Duplicate: Text = "Duplicate Watch"; break; } @@ -194,12 +194,9 @@ namespace BizHawk.MultiClient } } - var domain = Global.Emulator.MemoryDomains.FirstOrDefault(d => d.Name == DomainDropDown.SelectedItem.ToString()); - if (domain == null) - { - domain = Global.Emulator.MainMemory; - } - BigEndianCheckBox.Checked = domain.Endian == Endian.Big ? true : false; + var domain = Global.Emulator.MemoryDomains.FirstOrDefault(d => d.Name == DomainDropDown.SelectedItem.ToString()) ?? + Global.Emulator.MainMemory; + BigEndianCheckBox.Checked = domain.Endian == Endian.Big; } @@ -220,7 +217,7 @@ namespace BizHawk.MultiClient default: case Mode.New: var domain = Global.Emulator.MemoryDomains.FirstOrDefault(d => d.Name == DomainDropDown.SelectedItem.ToString()); - var address = (AddressBox as HexTextBox).ToInt(); + var address = AddressBox.ToInt(); var notes = NotesBox.Text; var type = Watch.StringToDisplayType(DisplayTypeDropDown.SelectedItem.ToString()); var bigendian = BigEndianCheckBox.Checked; @@ -283,7 +280,7 @@ namespace BizHawk.MultiClient (_watchList[0] as IWatchDetails).Notes = NotesBox.Text; } - if (_changedSize = true) + if (_changedSize) { for(int i = 0; i < _watchList.Count; i++) { @@ -313,7 +310,7 @@ namespace BizHawk.MultiClient { _watchList.ForEach(x => x.Type = Watch.StringToDisplayType(DisplayTypeDropDown.SelectedItem.ToString())); } - if (!(BigEndianCheckBox.CheckState == CheckState.Indeterminate)) + if (BigEndianCheckBox.CheckState != CheckState.Indeterminate) { _watchList.ForEach(x => x.BigEndian = BigEndianCheckBox.Checked); } diff --git a/BizHawk.MultiClient/tools/Watch/WatchList.cs b/BizHawk.MultiClient/tools/Watch/WatchList.cs new file mode 100644 index 0000000000..67893ff526 --- /dev/null +++ b/BizHawk.MultiClient/tools/Watch/WatchList.cs @@ -0,0 +1,579 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace BizHawk.MultiClient +{ + public class WatchList : IEnumerable + { + private string _currentFilename = ""; + + public enum WatchPrevDef { LastSearch, Original, LastFrame, LastChange }; + + private List _watchList = new List(); + private MemoryDomain _domain; + + public WatchList(MemoryDomain domain) + { + _domain = domain; + } + + public IEnumerator GetEnumerator() + { + return _watchList.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public int Count + { + get { return _watchList.Count; } + } + + public Watch this[int index] + { + get + { + return _watchList[index]; + } + set + { + _watchList[index] = value; + } + } + + public int WatchCount + { + get + { + return _watchList.Count(w => !w.IsSeparator); + } + } + + public int ItemCount + { + get + { + return _watchList.Count; + } + } + + public void OrderWatches(string column, bool reverse) + { + switch (column) + { + case NewRamWatch.ADDRESS: + if (reverse) + { + _watchList = _watchList + .OrderByDescending(x => x.Address ?? 0) + .ThenBy(x => x.Domain.Name) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ThenBy(x => x.BigEndian) + .ToList(); + } + else + { + _watchList = _watchList + .OrderBy(x => x.Address ?? 0) + .ThenBy(x => x.Domain.Name) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ThenBy(x => x.BigEndian) + .ToList(); + } + break; + case NewRamWatch.VALUE: + if (reverse) + { + _watchList = _watchList + .OrderByDescending(x => x.Value ?? 0) + .ThenBy(x => x.Address ?? 0) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ThenBy(x => x.BigEndian) + .ToList(); + } + else + { + _watchList = _watchList + .OrderBy(x => x.Value ?? 0) + .ThenBy(x => x.Address ?? 0) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ThenBy(x => x.BigEndian) + .ToList(); + } + break; + case NewRamWatch.PREV: //Note: these only work if all entries are detailed objects! + if (reverse) + { + _watchList = _watchList + .OrderByDescending(x => (x as IWatchDetails).PreviousStr) + .ThenBy(x => x.Address ?? 0) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ToList(); + } + else + { + _watchList = _watchList + .OrderBy(x => (x as IWatchDetails).PreviousStr) + .ThenBy(x => x.Address ?? 0) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ToList(); + } + break; + case NewRamWatch.DIFF: + if (reverse) + { + _watchList = _watchList + .OrderByDescending(x => (x as IWatchDetails).Diff) + .ThenBy(x => x.Address ?? 0) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ToList(); + } + else + { + _watchList = _watchList + .OrderBy(x => (x as IWatchDetails).Diff) + .ThenBy(x => x.Address ?? 0) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ToList(); + } + break; + case NewRamWatch.CHANGES: + if (reverse) + { + _watchList = _watchList + .OrderByDescending(x => (x as IWatchDetails).ChangeCount) + .ThenBy(x => x.Address ?? 0) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ToList(); + } + else + { + _watchList = _watchList + .OrderBy(x => (x as IWatchDetails).ChangeCount) + .ThenBy(x => x.Address ?? 0) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ToList(); + } + break; + case NewRamWatch.DOMAIN: + if (reverse) + { + _watchList = _watchList + .OrderByDescending(x => x.Domain) + .ThenBy(x => x.Address ?? 0) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ThenBy(x => x.BigEndian) + .ToList(); + } + else + { + _watchList = _watchList + .OrderBy(x => x.Domain) + .ThenBy(x => x.Address ?? 0) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ThenBy(x => x.BigEndian) + .ToList(); + } + break; + case NewRamWatch.NOTES: + if (reverse) + { + _watchList = _watchList + .OrderByDescending(x => (x as IWatchDetails).Notes) + .ThenBy(x => x.Address ?? 0) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ToList(); + } + else + { + _watchList = _watchList + .OrderBy(x => (x as IWatchDetails).Notes) + .ThenBy(x => x.Address ?? 0) + .ThenBy(x => x.Size) + .ThenBy(x => x.Type) + .ToList(); + } + break; + } + } + + public string AddressFormatStr + { + get + { + if (_domain != null) + { + return "{0:X" + IntHelpers.GetNumDigits(_domain.Size - 1).ToString() + "}"; + } + else + { + return ""; + } + } + } + + public void Clear() + { + _watchList.Clear(); + Changes = false; + _currentFilename = ""; + } + + public MemoryDomain Domain { get { return _domain; } set { _domain = value; } } + + public void UpdateValues() + { + var detailedWatches = _watchList.OfType().ToList(); + foreach (var watch in detailedWatches) + { + watch.Update(); + } + } + + public void Add(Watch watch) + { + _watchList.Add(watch); + Changes = true; + } + + public void AddRange(IList watches) + { + _watchList.AddRange(watches); + Changes = true; + } + + public void Remove(Watch watch) + { + _watchList.Remove(watch); + Changes = true; + } + + public void Insert(int index, Watch watch) + { + _watchList.Insert(index, watch); + } + + public void ClearChangeCounts() + { + var detailedWatches = _watchList.OfType().ToList(); + foreach (var watch in detailedWatches) + { + watch.ClearChangeCount(); + } + } + + #region File handling logic - probably needs to be its own class + + public string CurrentFileName { get { return _currentFilename; } set { _currentFilename = value; } } + public bool Changes { get; set; } + + public bool Save() + { + bool result; + if (!String.IsNullOrWhiteSpace(CurrentFileName)) + { + result = SaveFile(); + } + else + { + result = SaveAs(); + } + + if (result) + { + Changes = false; + } + + return result; + } + + public bool Load(string path, bool details, bool append) + { + bool result = LoadFile(path, details, append); + + if (result) + { + if (append) + { + Changes = true; + } + else + { + CurrentFileName = path; + Changes = false; + } + } + + return result; + } + + public void Reload() + { + if (!String.IsNullOrWhiteSpace(CurrentFileName)) + { + LoadFile(CurrentFileName, true, false); + Changes = false; + } + } + + private bool SaveFile() + { + if (String.IsNullOrWhiteSpace(CurrentFileName)) + { + return false; + } + + using (StreamWriter sw = new StreamWriter(CurrentFileName)) + { + StringBuilder sb = new StringBuilder(); + sb + .Append("Domain ").AppendLine(_domain.Name) + .Append("SystemID ").AppendLine(Global.Emulator.SystemId); + + foreach (Watch w in _watchList) + { + sb + .Append(String.Format(AddressFormatStr, w.Address)).Append('\t') + .Append(w.SizeAsChar).Append('\t') + .Append(w.TypeAsChar).Append('\t') + .Append(w.BigEndian ? '1' : '0').Append('\t') + .Append(w.Domain.Name).Append('\t') + .Append(w is IWatchDetails ? (w as IWatchDetails).Notes : String.Empty) + .AppendLine(); + } + + sw.WriteLine(sb.ToString()); + } + + return true; + } + + public bool SaveAs() + { + var file = WatchCommon.GetSaveFileFromUser(CurrentFileName); + if (file != null) + { + CurrentFileName = file.FullName; + return SaveFile(); + } + else + { + return false; + } + } + + private bool LoadFile(string path, bool details, bool append) + { + string domain = ""; + var file = new FileInfo(path); + if (file.Exists == false) return false; + bool isBizHawkWatch = true; //Hack to support .wch files from other emulators + bool isOldBizHawkWatch = false; + using (StreamReader sr = file.OpenText()) + { + string line; + + if (append == false) + { + Clear(); + } + + while ((line = sr.ReadLine()) != null) + { + //.wch files from other emulators start with a number representing the number of watch, that line can be discarded here + //Any properly formatted line couldn't possibly be this short anyway, this also takes care of any garbage lines that might be in a file + if (line.Length < 5) + { + isBizHawkWatch = false; + continue; + } + + if (line.Length >= 6 && line.Substring(0, 6) == "Domain") + { + domain = line.Substring(7, line.Length - 7); + isBizHawkWatch = true; + } + + if (line.Length >= 8 && line.Substring(0, 8) == "SystemID") + { + continue; + } + + int numColumns = StringHelpers.HowMany(line, '\t'); + int startIndex; + if (numColumns == 5) + { + //If 5, then this is a post 1.0.5 .wch file + if (isBizHawkWatch) + { + //Do nothing here + } + else + { + startIndex = line.IndexOf('\t') + 1; + line = line.Substring(startIndex, line.Length - startIndex); //5 digit value representing the watch position number + } + } + else if (numColumns == 4) + { + isOldBizHawkWatch = true; + } + else //4 is 1.0.5 and earlier + { + continue; //If not 4, something is wrong with this line, ignore it + } + + + + //Temporary, rename if kept + int addr; + bool bigEndian; + MemoryDomain memDomain = Global.Emulator.MainMemory; + + string temp = line.Substring(0, line.IndexOf('\t')); + try + { + addr = Int32.Parse(temp, NumberStyles.HexNumber); + } + catch + { + continue; + } + + startIndex = line.IndexOf('\t') + 1; + line = line.Substring(startIndex, line.Length - startIndex); //Type + Watch.WatchSize size = Watch.SizeFromChar(line[0]); + + + startIndex = line.IndexOf('\t') + 1; + line = line.Substring(startIndex, line.Length - startIndex); //Signed + Watch.DisplayType type = Watch.DisplayTypeFromChar(line[0]); + + startIndex = line.IndexOf('\t') + 1; + line = line.Substring(startIndex, line.Length - startIndex); //Endian + try + { + startIndex = Int16.Parse(line[0].ToString()); + } + catch + { + continue; + } + if (startIndex == 0) + { + bigEndian = false; + } + else + { + bigEndian = true; + } + + if (isBizHawkWatch && !isOldBizHawkWatch) + { + startIndex = line.IndexOf('\t') + 1; + line = line.Substring(startIndex, line.Length - startIndex); //Domain + temp = line.Substring(0, line.IndexOf('\t')); + memDomain = Global.Emulator.MemoryDomains[GetDomainPos(temp)]; + } + + startIndex = line.IndexOf('\t') + 1; + string notes = line.Substring(startIndex, line.Length - startIndex); + + Watch w = Watch.GenerateWatch(memDomain, addr, size, details); + w.BigEndian = bigEndian; + w.Type = type; + if (w is IWatchDetails) + { + (w as IWatchDetails).Notes = notes; + } + + _watchList.Add(w); + _domain = Global.Emulator.MemoryDomains[GetDomainPos(domain)]; + } + } + + return true; + } + + private static int GetDomainPos(string name) + { + //Attempts to find the memory domain by name, if it fails, it defaults to index 0 + for (int x = 0; x < Global.Emulator.MemoryDomains.Count; x++) + { + if (Global.Emulator.MemoryDomains[x].Name == name) + return x; + } + return 0; + } + + public static FileInfo GetFileFromUser(string currentFile) + { + var ofd = new OpenFileDialog(); + if (currentFile.Length > 0) + ofd.FileName = Path.GetFileNameWithoutExtension(currentFile); + ofd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.WatchPath, null); + ofd.Filter = "Watch Files (*.wch)|*.wch|All Files|*.*"; + ofd.RestoreDirectory = true; + + Global.Sound.StopSound(); + var result = ofd.ShowDialog(); + Global.Sound.StartSound(); + if (result != DialogResult.OK) + return null; + var file = new FileInfo(ofd.FileName); + return file; + } + + public static FileInfo GetSaveFileFromUser(string currentFile) + { + var sfd = new SaveFileDialog(); + if (currentFile.Length > 0) + { + sfd.FileName = Path.GetFileNameWithoutExtension(currentFile); + sfd.InitialDirectory = Path.GetDirectoryName(currentFile); + } + else if (!(Global.Emulator is NullEmulator)) + { + sfd.FileName = PathManager.FilesystemSafeName(Global.Game); + sfd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.WatchPath, null); + } + else + { + sfd.FileName = "NULL"; + sfd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.WatchPath, null); + } + sfd.Filter = "Watch Files (*.wch)|*.wch|All Files|*.*"; + sfd.RestoreDirectory = true; + Global.Sound.StopSound(); + var result = sfd.ShowDialog(); + Global.Sound.StartSound(); + if (result != DialogResult.OK) + return null; + var file = new FileInfo(sfd.FileName); + return file; + } + + #endregion + } +}