using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Mesen.GUI.Controls; using Mesen.GUI.Forms; using Mesen.GUI.Config; namespace Mesen.GUI.Debugger.Controls { public partial class ctrlMemoryAccessCounters : BaseControl { private MemoryCountData[] _data; private AddressType _memoryType = AddressType.InternalRam; private SortType _sortType = SortType.Address; public ctrlMemoryAccessCounters() { InitializeComponent(); this.toolTip.SetToolTip(chkHighlightUninitRead, "The uninitialized memory reads highlight will only be accurate if the debugger was active when the game was loaded (or if the game has been power cycled since)"); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); if(!IsDesignMode) { InitMemoryTypeDropdown(); cboSort.SelectedIndex = 0; InitShortcuts(); } } private void InitShortcuts() { mnuCopy.InitShortcut(this, nameof(DebuggerShortcutsConfig.Copy)); mnuSelectAll.InitShortcut(this, nameof(DebuggerShortcutsConfig.SelectAll)); } public void InitMemoryTypeDropdown() { cboMemoryType.SelectedIndexChanged -= cboMemoryType_SelectedIndexChanged; AddressType originalValue = cboMemoryType.GetEnumValue(); cboMemoryType.BeginUpdate(); cboMemoryType.Items.Clear(); cboMemoryType.Items.Add(ResourceHelper.GetEnumText(AddressType.InternalRam)); if(InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom) > 0) { cboMemoryType.Items.Add(ResourceHelper.GetEnumText(AddressType.PrgRom)); } if(InteropEmu.DebugGetMemorySize(DebugMemoryType.WorkRam) > 0) { cboMemoryType.Items.Add(ResourceHelper.GetEnumText(AddressType.WorkRam)); } if(InteropEmu.DebugGetMemorySize(DebugMemoryType.SaveRam) > 0) { cboMemoryType.Items.Add(ResourceHelper.GetEnumText(AddressType.SaveRam)); } cboMemoryType.SelectedIndex = 0; cboMemoryType.SetEnumValue(originalValue); cboMemoryType.SelectedIndexChanged += cboMemoryType_SelectedIndexChanged; cboMemoryType.EndUpdate(); UpdateMemoryType(); } public Font BaseFont { get { return ctrlScrollableTextbox.BaseFont; } set { ctrlScrollableTextbox.BaseFont = value; } } public int TextZoom { get { return ctrlScrollableTextbox.TextZoom; } set { ctrlScrollableTextbox.TextZoom = value; } } public void RefreshData() { int[] readCounts = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Read, false); int[] writeCounts = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Write, false); int[] execCounts = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Exec, false); int[] uninitReads = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Read, true); List addresses = new List(readCounts.Length); List content = new List(readCounts.Length); if(_data == null || _data.Length != readCounts.Length) { _data = new MemoryCountData[readCounts.Length]; for(int i = 0; i < readCounts.Length; i++) { _data[i] = new MemoryCountData(); } } for(int i = 0; i < readCounts.Length; i++) { _data[i].Address = i; _data[i].ReadCount = readCounts[i]; _data[i].WriteCount = writeCounts[i]; _data[i].ExecCount = execCounts[i]; _data[i].UninitRead = uninitReads[i] > 0; } MemoryCountData[] data = new MemoryCountData[readCounts.Length]; Array.Copy(_data, data, readCounts.Length); switch(_sortType) { case SortType.Address: break; case SortType.Read: Array.Sort(data.Select((e) => -e.ReadCount).ToArray(), data); break; case SortType.Write: Array.Sort(data.Select((e) => -e.WriteCount).ToArray(), data); break; case SortType.Exec: Array.Sort(data.Select((e) => -e.ExecCount).ToArray(), data); break; case SortType.UninitRead: Array.Sort(data.Select((e) => e.UninitRead ? -e.ReadCount : (Int32.MaxValue - e.ReadCount)).ToArray(), data); break; } bool hideUnusedAddresses = chkHideUnusedAddresses.Checked; for(int i = 0; i < readCounts.Length; i++) { if(!hideUnusedAddresses || !data[i].Empty) { addresses.Add(data[i].Address); content.Add(data[i].Content); } } if(chkHighlightUninitRead.Checked) { ctrlScrollableTextbox.StyleProvider = new LineStyleProvider(new HashSet(data.Where((e) => e.UninitRead).Select((e) => e.Address))); } else { ctrlScrollableTextbox.StyleProvider = null; } ctrlScrollableTextbox.Header = " " + "Read".PadRight(12) + "Write".PadRight(12) + "Execute"; ctrlScrollableTextbox.LineNumbers = addresses.ToArray(); ctrlScrollableTextbox.TextLines = content.ToArray(); } private class LineStyleProvider : ctrlTextbox.ILineStyleProvider { HashSet _addresses = new HashSet(); public LineStyleProvider(HashSet addresses) { _addresses = addresses; } public string GetLineComment(int lineIndex) { return null; } public LineProperties GetLineStyle(int cpuAddress, int lineIndex) { if(_addresses.Contains(cpuAddress)) { return new LineProperties() { TextBgColor = Color.LightCoral }; } return null; } } private void cboMemoryType_SelectedIndexChanged(object sender, EventArgs e) { UpdateMemoryType(); } private void UpdateMemoryType() { _memoryType = cboMemoryType.GetEnumValue(); RefreshData(); } private void cboSort_SelectedIndexChanged(object sender, EventArgs e) { _sortType = (SortType)cboSort.SelectedIndex; RefreshData(); } private void chkOption_CheckedChanged(object sender, EventArgs e) { RefreshData(); } private void btnReset_Click(object sender, EventArgs e) { InteropEmu.DebugResetMemoryAccessCounts(); RefreshData(); } public void GoToAddress() { ctrlScrollableTextbox.GoToAddress(); } private enum SortType { Address = 0, Read = 1, Write = 2, Exec = 3, UninitRead = 4, } private class MemoryCountData { private bool _needRecalc = true; private int _readCount = 0; private int _writeCount = 0; private int _execCount = 0; private string _content = string.Empty; public int Address { get; set; } public int ReadCount { get { return _readCount; } set { if(this._readCount!=value) { this._readCount = value; this._needRecalc = true; }; } } public int WriteCount { get { return _writeCount; } set { if(this._writeCount!=value) { this._writeCount = value; this._needRecalc = true; }; } } public int ExecCount { get { return _execCount; } set { if(this._execCount!=value) { this._execCount = value; this._needRecalc = true; }; } } public bool Empty { get { return ReadCount == 0 && WriteCount == 0 && ExecCount == 0; } } public string Content { get { if(this._needRecalc) { _content = " " + (_readCount == 0 ? "0" : _readCount.ToString()).PadRight(12) + (_writeCount == 0 ? "0" : _writeCount.ToString()).PadRight(12) + (_execCount == 0 ? "0" : _execCount.ToString()); _needRecalc = false; } return _content; } } public bool UninitRead { get; set; } } private void mnuCopy_Click(object sender, EventArgs e) { ctrlScrollableTextbox.CopySelection(true, false, false); } private void mnuSelectAll_Click(object sender, EventArgs e) { ctrlScrollableTextbox.SelectAll(); } } }