mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
682 lines
23 KiB
C#
682 lines
23 KiB
C#
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Markup.Xaml;
|
|
using Avalonia.Threading;
|
|
using System;
|
|
using Mesen.Debugger.Controls;
|
|
using Mesen.Debugger.ViewModels;
|
|
using Mesen.Interop;
|
|
using System.ComponentModel;
|
|
using Avalonia.Interactivity;
|
|
using Mesen.Debugger.Utilities;
|
|
using System.IO;
|
|
using Mesen.Utilities;
|
|
using Mesen.Config;
|
|
using Mesen.Debugger.Labels;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using Mesen.Localization;
|
|
using Avalonia.Input;
|
|
using Mesen.Debugger.Disassembly;
|
|
|
|
namespace Mesen.Debugger.Windows
|
|
{
|
|
public class MemoryToolsWindow : MesenWindow, INotificationHandler
|
|
{
|
|
private HexEditor _editor;
|
|
private MemoryToolsViewModel _model;
|
|
private MemoryViewerFindWindow? _searchWnd;
|
|
private Point _prevMousePos;
|
|
private int _prevByteOffset;
|
|
private DynamicTooltip? _prevTooltip;
|
|
|
|
public MemoryToolsWindow()
|
|
{
|
|
InitializeComponent();
|
|
#if DEBUG
|
|
this.AttachDevTools();
|
|
#endif
|
|
|
|
_editor = this.GetControl<HexEditor>("Hex");
|
|
_model = new MemoryToolsViewModel(_editor);
|
|
DataContext = _model;
|
|
|
|
if(Design.IsDesignMode) {
|
|
return;
|
|
}
|
|
|
|
_model.Config.LoadWindowSettings(this);
|
|
_editor.ByteUpdated += editor_ByteUpdated;
|
|
_editor.PointerMoved += editor_PointerMoved;
|
|
}
|
|
|
|
public static void ShowInMemoryTools(MemoryType memType, int address)
|
|
{
|
|
MemoryToolsWindow wnd = DebugWindowManager.GetOrOpenDebugWindow(() => new MemoryToolsWindow());
|
|
wnd.SetCursorPosition(memType, address);
|
|
}
|
|
|
|
public void SetCursorPosition(MemoryType memType, int address)
|
|
{
|
|
if(_model.AvailableMemoryTypes.Contains(memType)) {
|
|
_model.Config.MemoryType = memType;
|
|
_editor.SetCursorPosition(address, scrollToTop: true);
|
|
_editor.Focus();
|
|
}
|
|
}
|
|
|
|
protected override void OnClosing(WindowClosingEventArgs e)
|
|
{
|
|
base.OnClosing(e);
|
|
_model.Config.SaveWindowSettings(this);
|
|
ConfigManager.Config.Debug.HexEditor = _model.Config;
|
|
}
|
|
|
|
protected override void OnOpened(EventArgs e)
|
|
{
|
|
base.OnOpened(e);
|
|
InitializeActions();
|
|
_editor.Focus();
|
|
}
|
|
|
|
protected override void OnGotFocus(GotFocusEventArgs e)
|
|
{
|
|
base.OnGotFocus(e);
|
|
if(FocusManager?.GetFocusedElement() == this) {
|
|
//Focus on editor whenever the window itself is focused
|
|
_editor.Focus();
|
|
}
|
|
}
|
|
|
|
private void InitializeComponent()
|
|
{
|
|
AvaloniaXamlLoader.Load(this);
|
|
}
|
|
|
|
private void OnSettingsClick(object sender, RoutedEventArgs e)
|
|
{
|
|
_model.Config.ShowOptionPanel = !_model.Config.ShowOptionPanel;
|
|
}
|
|
|
|
private void editor_PointerMoved(object? sender, PointerEventArgs e)
|
|
{
|
|
Point point = e.GetPosition(_editor);
|
|
if(point == _prevMousePos) {
|
|
return;
|
|
}
|
|
_prevMousePos = point;
|
|
|
|
int byteOffset = _model.Config.ShowTooltips ? _editor.GetByteOffset(point) : -1;
|
|
if(byteOffset >= 0) {
|
|
if(byteOffset != _prevByteOffset) {
|
|
CpuType cpuType = _model.Config.MemoryType.ToCpuType();
|
|
|
|
AddressInfo addr = new AddressInfo() { Address = byteOffset, Type = _model.Config.MemoryType };
|
|
AddressInfo relAddr;
|
|
AddressInfo absAddr;
|
|
if(addr.Type.IsRelativeMemory()) {
|
|
relAddr = addr;
|
|
absAddr = DebugApi.GetAbsoluteAddress(relAddr);
|
|
} else {
|
|
absAddr = addr;
|
|
relAddr = DebugApi.GetRelativeAddress(absAddr, cpuType);
|
|
}
|
|
|
|
CodeLabel? label = LabelManager.GetLabel(addr);
|
|
LocationInfo locInfo = new() {
|
|
RelAddress = relAddr.Address >= 0 ? relAddr : null,
|
|
AbsAddress = absAddr.Address >= 0 ? absAddr : null,
|
|
Label = label,
|
|
LabelAddressOffset = label?.Length > 1 && absAddr.Address >= label.Address ? (absAddr.Address - (int)label.Address) : null
|
|
};
|
|
|
|
_prevTooltip = CodeTooltipHelper.GetCodeAddressTooltip(cpuType, locInfo, !addr.Type.IsRelativeMemory());
|
|
TooltipHelper.ShowTooltip(_editor, _prevTooltip, 15);
|
|
} else {
|
|
TooltipHelper.ShowTooltip(_editor, _prevTooltip, 15);
|
|
}
|
|
} else {
|
|
_prevTooltip = null;
|
|
TooltipHelper.HideTooltip(_editor);
|
|
}
|
|
|
|
_prevByteOffset = byteOffset;
|
|
}
|
|
|
|
private void editor_ByteUpdated(object? sender, ByteUpdatedEventArgs e)
|
|
{
|
|
if(e.Values != null) {
|
|
DebugApi.SetMemoryValues(_model.Config.MemoryType, (uint)e.ByteOffset, e.Values, e.Values.Length);
|
|
} else if(e.Value != null) {
|
|
if(e.Length > 0) {
|
|
byte[] data = new byte[e.Length];
|
|
Array.Fill<byte>(data, e.Value.Value);
|
|
DebugApi.SetMemoryValues(_model.Config.MemoryType, (uint)e.ByteOffset, data, data.Length);
|
|
} else {
|
|
DebugApi.SetMemoryValue(_model.Config.MemoryType, (uint)e.ByteOffset, e.Value.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void InitializeActions()
|
|
{
|
|
DebugConfig cfg = ConfigManager.Config.Debug;
|
|
|
|
_model.AddDisposables(DebugShortcutManager.CreateContextMenu(_editor, new ContextMenuAction[] {
|
|
GetMarkSelectionAction(),
|
|
new ContextMenuSeparator(),
|
|
GetAddWatchAction(),
|
|
GetEditBreakpointAction(),
|
|
GetEditLabelAction(),
|
|
new ContextMenuSeparator(),
|
|
GetViewInDebuggerAction(),
|
|
GetViewInMemoryAction(),
|
|
new ContextMenuSeparator() { IsVisible = () => _model.Config.MemoryType.SupportsFreezeAddress() },
|
|
GetFreezeAction(ActionType.FreezeMemory, DebuggerShortcut.MemoryViewer_Freeze),
|
|
GetFreezeAction(ActionType.UnfreezeMemory, DebuggerShortcut.MemoryViewer_Unfreeze),
|
|
new ContextMenuSeparator(),
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.Undo,
|
|
IsEnabled = () => DebugApi.HasUndoHistory(),
|
|
Shortcut = () => cfg.Shortcuts.Get(DebuggerShortcut.Undo),
|
|
OnClick = () => {
|
|
if(DebugApi.HasUndoHistory()){
|
|
DebugApi.PerformUndo();
|
|
_editor.InvalidateVisual();
|
|
}
|
|
}
|
|
},
|
|
new ContextMenuSeparator(),
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.Copy,
|
|
IsEnabled = () => _editor.SelectionLength > 0,
|
|
OnClick = () => _editor.CopySelection(),
|
|
Shortcut = () => cfg.Shortcuts.Get(DebuggerShortcut.Copy)
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.Paste,
|
|
OnClick = () => _editor.PasteSelection(),
|
|
Shortcut = () => cfg.Shortcuts.Get(DebuggerShortcut.Paste)
|
|
},
|
|
new ContextMenuSeparator(),
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.SelectAll,
|
|
OnClick = () => _editor.SelectAll(),
|
|
Shortcut = () => cfg.Shortcuts.Get(DebuggerShortcut.SelectAll)
|
|
},
|
|
}));
|
|
|
|
_model.FileMenuItems = _model.AddDisposables(new List<ContextMenuAction>() {
|
|
GetImportAction(),
|
|
GetExportAction(),
|
|
new ContextMenuSeparator(),
|
|
SaveRomActionHelper.GetSaveRomAction(this),
|
|
SaveRomActionHelper.GetSaveRomAsAction(this),
|
|
SaveRomActionHelper.GetSaveEditsAsIpsAction(this),
|
|
new ContextMenuSeparator(),
|
|
GetResetAccessCountersAction(),
|
|
new ContextMenuSeparator(),
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.LoadTblFile,
|
|
OnClick = async () => {
|
|
string? filename = await FileDialogHelper.OpenFile(null, this, FileDialogHelper.TblExt);
|
|
if(filename != null) {
|
|
string[] tblData = File.ReadAllLines(filename);
|
|
_model.TblConverter = TblLoader.Load(tblData);
|
|
DebugWorkspaceManager.Workspace.TblMappings = tblData;
|
|
}
|
|
}
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.ResetTblMappings,
|
|
OnClick = () => {
|
|
_model.TblConverter = null;
|
|
DebugWorkspaceManager.Workspace.TblMappings = Array.Empty<string>();
|
|
}
|
|
},
|
|
new ContextMenuSeparator(),
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.Exit,
|
|
OnClick = () => Close()
|
|
}
|
|
});
|
|
|
|
_model.ViewMenuItems = _model.AddDisposables(new List<ContextMenuAction>() {
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.ShowSettingsPanel,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.ToggleSettingsPanel),
|
|
IsSelected = () => _model.Config.ShowOptionPanel,
|
|
OnClick = () => _model.Config.ShowOptionPanel = !_model.Config.ShowOptionPanel
|
|
},
|
|
});
|
|
|
|
_model.SearchMenuItems = _model.AddDisposables(new List<ContextMenuAction>() {
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToAddress,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.GoToAddress),
|
|
OnClick = async () => {
|
|
MemoryType memType = _model.Config.MemoryType;
|
|
int? address = await new GoToWindow(memType.ToCpuType(), memType, DebugApi.GetMemorySize(memType) - 1).ShowCenteredDialog<int?>(this);
|
|
if(address != null) {
|
|
_editor.SetCursorPosition(address.Value, scrollToTop: true);
|
|
_editor.Focus();
|
|
}
|
|
}
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToAll,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.GoToAll),
|
|
OnClick = async () => {
|
|
GoToDestination? dest = await GoToAllWindow.Open(this, _model.Config.MemoryType.ToCpuType(), GoToAllOptions.ShowOutOfScope, DebugWorkspaceManager.SymbolProvider);
|
|
if(dest?.RelativeAddress?.Type == _model.Config.MemoryType) {
|
|
SetCursorPosition(dest.RelativeAddress.Value.Type, dest.RelativeAddress.Value.Address);
|
|
} else if(dest?.AbsoluteAddress != null) {
|
|
SetCursorPosition(dest.AbsoluteAddress.Value.Type, dest.AbsoluteAddress.Value.Address);
|
|
}
|
|
}
|
|
},
|
|
new ContextMenuSeparator(),
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.Find,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.Find),
|
|
OnClick = () => OpenSearchWindow()
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.FindPrev,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.FindPrev),
|
|
OnClick = () => Find(SearchDirection.Backward)
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.FindNext,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.FindNext),
|
|
OnClick = () => Find(SearchDirection.Forward)
|
|
},
|
|
new ContextMenuSeparator(),
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.NavigateTo,
|
|
SubActions = new List<object> {
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToPrevRead,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_GoToPrevRead),
|
|
OnClick = () => _model.NavigateTo(NavType.PrevRead)
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToNextRead,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_GoToNextRead),
|
|
OnClick = () => _model.NavigateTo(NavType.NextRead)
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToPrevWrite,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_GoToPrevWrite),
|
|
OnClick = () => _model.NavigateTo(NavType.PrevWrite)
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToNextWrite,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_GoToNextWrite),
|
|
OnClick = () => _model.NavigateTo(NavType.NextWrite)
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToPrevExec,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_GoToPrevExec),
|
|
OnClick = () => _model.NavigateTo(NavType.PrevExec)
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToNextExec,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_GoToNextExec),
|
|
OnClick = () => _model.NavigateTo(NavType.NextExec)
|
|
},
|
|
new ContextMenuSeparator(),
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToPrevCode,
|
|
IsEnabled = () => _model.Config.MemoryType.SupportsCdl(),
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_GoToPrevCode),
|
|
OnClick = () => _model.NavigateTo(NavType.PrevCode)
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToNextCode,
|
|
IsEnabled = () => _model.Config.MemoryType.SupportsCdl(),
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_GoToNextCode),
|
|
OnClick = () => _model.NavigateTo(NavType.NextCode)
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToPrevData,
|
|
IsEnabled = () => _model.Config.MemoryType.SupportsCdl(),
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_GoToPrevData),
|
|
OnClick = () => _model.NavigateTo(NavType.PrevData)
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToNextData,
|
|
IsEnabled = () => _model.Config.MemoryType.SupportsCdl(),
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_GoToNextData),
|
|
OnClick = () => _model.NavigateTo(NavType.NextData)
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToPrevUnknown,
|
|
IsEnabled = () => _model.Config.MemoryType.SupportsCdl(),
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_GoToPrevUnknown),
|
|
OnClick = () => _model.NavigateTo(NavType.PrevUnknown)
|
|
},
|
|
new ContextMenuAction() {
|
|
ActionType = ActionType.GoToNextUnknown,
|
|
IsEnabled = () => _model.Config.MemoryType.SupportsCdl(),
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_GoToNextUnknown),
|
|
OnClick = () => _model.NavigateTo(NavType.NextUnknown)
|
|
},
|
|
}
|
|
}
|
|
});
|
|
|
|
_model.ToolbarItems = _model.AddDisposables(new List<ContextMenuAction>() {
|
|
GetImportAction(),
|
|
GetExportAction(),
|
|
new ContextMenuSeparator(),
|
|
GetResetAccessCountersAction()
|
|
});
|
|
|
|
DebugShortcutManager.RegisterActions(this, _model.FileMenuItems);
|
|
DebugShortcutManager.RegisterActions(this, _model.ViewMenuItems);
|
|
DebugShortcutManager.RegisterActions(this, _model.SearchMenuItems);
|
|
}
|
|
|
|
private void OpenSearchWindow()
|
|
{
|
|
if(_searchWnd == null) {
|
|
_searchWnd = new MemoryViewerFindWindow(_model.Search, _model);
|
|
_searchWnd.Closed += (s, e) => _searchWnd = null;
|
|
_searchWnd.ShowCenteredWithParent(this);
|
|
} else {
|
|
_searchWnd.BringToFront();
|
|
}
|
|
}
|
|
|
|
private void Find(SearchDirection direction)
|
|
{
|
|
if(!_model.Search.IsValid) {
|
|
OpenSearchWindow();
|
|
} else {
|
|
_model.Find(direction);
|
|
}
|
|
}
|
|
|
|
private ContextMenuAction GetViewInMemoryAction()
|
|
{
|
|
AddressInfo? GetAddress()
|
|
{
|
|
MemoryType memType = _model.Config.MemoryType;
|
|
if(_editor.SelectionLength <= 1) {
|
|
if(memType.IsRelativeMemory()) {
|
|
AddressInfo absAddr = DebugApi.GetAbsoluteAddress(new AddressInfo() { Address = _model.SelectionStart, Type = memType });
|
|
return absAddr.Address >= 0 ? absAddr : null;
|
|
} else {
|
|
AddressInfo relAddr = DebugApi.GetRelativeAddress(new AddressInfo() { Address = _model.SelectionStart, Type = memType }, memType.ToCpuType());
|
|
return relAddr.Address >= 0 ? relAddr : null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
return new ContextMenuAction() {
|
|
ActionType = ActionType.ViewInMemoryViewer,
|
|
DynamicText = () => ResourceHelper.GetMessage("ViewInAction", (GetAddress() is AddressInfo addr) ? ResourceHelper.GetEnumText(addr.Type) : "[n/a]"),
|
|
IsVisible = () => GetAddress() != null,
|
|
HintText = () => "$" + _editor.SelectionStart.ToString("X2"),
|
|
OnClick = () => {
|
|
if(GetAddress() is AddressInfo addr) {
|
|
SetCursorPosition(addr.Type, addr.Address);
|
|
}
|
|
},
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_ViewInMemoryType)
|
|
};
|
|
}
|
|
|
|
private ContextMenuAction GetViewInDebuggerAction()
|
|
{
|
|
AddressInfo? GetAddress()
|
|
{
|
|
MemoryType memType = _model.Config.MemoryType;
|
|
if(_editor.SelectionLength <= 1 && !memType.IsPpuMemory()) {
|
|
AddressInfo relAddr;
|
|
if(!memType.IsRelativeMemory()) {
|
|
relAddr = DebugApi.GetRelativeAddress(new AddressInfo() { Address = _model.SelectionStart, Type = memType }, memType.ToCpuType());
|
|
return relAddr.Address >= 0 ? relAddr : null;
|
|
} else {
|
|
return new AddressInfo() { Address = _model.SelectionStart, Type = memType };
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
return new ContextMenuAction() {
|
|
ActionType = ActionType.ViewInDebugger,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_ViewInDebugger),
|
|
IsEnabled = () => GetAddress() != null,
|
|
HintText = () => "$" + _editor.SelectionStart.ToString("X2"),
|
|
OnClick = () => {
|
|
AddressInfo? relAddr = GetAddress();
|
|
if(relAddr?.Address >= 0) {
|
|
CpuType cpuType = relAddr.Value.Type.ToCpuType();
|
|
DebuggerWindow.OpenWindowAtAddress(cpuType, relAddr.Value.Address);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
private ContextMenuAction GetResetAccessCountersAction()
|
|
{
|
|
return new ContextMenuAction() {
|
|
ActionType = ActionType.ResetAccessCounters,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_ResetAccessCounters),
|
|
OnClick = () => {
|
|
DebugApi.ResetMemoryAccessCounts();
|
|
_editor.InvalidateVisual();
|
|
}
|
|
};
|
|
}
|
|
|
|
private ContextMenuAction GetImportAction()
|
|
{
|
|
return new ContextMenuAction() {
|
|
ActionType = ActionType.Import,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_Import),
|
|
IsEnabled = () => !_model.Config.MemoryType.IsRelativeMemory(),
|
|
OnClick = () => Import()
|
|
};
|
|
}
|
|
|
|
private ContextMenuAction GetExportAction()
|
|
{
|
|
return new ContextMenuAction() {
|
|
ActionType = ActionType.Export,
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_Export),
|
|
OnClick = () => Export()
|
|
};
|
|
}
|
|
|
|
public async void Import()
|
|
{
|
|
string? filename = await FileDialogHelper.OpenFile(ConfigManager.DebuggerFolder, this, FileDialogHelper.DmpExt);
|
|
if(filename != null) {
|
|
byte[]? dmpData = FileHelper.ReadAllBytes(filename);
|
|
if(dmpData != null) {
|
|
DebugApi.SetMemoryState(_model.Config.MemoryType, dmpData, dmpData.Length);
|
|
}
|
|
}
|
|
}
|
|
|
|
public async void Export()
|
|
{
|
|
string name = EmuApi.GetRomInfo().GetRomName() + " - " + _model.Config.MemoryType.ToString() + ".dmp";
|
|
string? filename = await FileDialogHelper.SaveFile(ConfigManager.DebuggerFolder, name, this, FileDialogHelper.DmpExt);
|
|
if(filename != null) {
|
|
FileHelper.WriteAllBytes(filename, DebugApi.GetMemoryState(_model.Config.MemoryType));
|
|
}
|
|
}
|
|
|
|
private ContextMenuAction GetEditLabelAction()
|
|
{
|
|
AddressInfo? GetAddress(MemoryType memType, int address)
|
|
{
|
|
if(memType.IsRelativeMemory()) {
|
|
AddressInfo relAddress = new AddressInfo() {
|
|
Address = address,
|
|
Type = memType
|
|
};
|
|
|
|
AddressInfo absAddress = DebugApi.GetAbsoluteAddress(relAddress);
|
|
return absAddress.Address >= 0 && absAddress.Type.SupportsLabels() ? absAddress : null;
|
|
} else {
|
|
return memType.SupportsLabels() ? new AddressInfo() { Address = address, Type = memType } : null;
|
|
}
|
|
}
|
|
|
|
return new ContextMenuAction() {
|
|
ActionType = ActionType.EditLabel,
|
|
HintText = () => "$" + _editor.SelectionStart.ToString("X2"),
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_EditLabel),
|
|
|
|
IsEnabled = () => GetAddress(_model.Config.MemoryType, _editor.SelectionStart) != null,
|
|
|
|
OnClick = () => {
|
|
AddressInfo? addr = GetAddress(_model.Config.MemoryType, _editor.SelectionStart);
|
|
if(addr == null) {
|
|
return;
|
|
}
|
|
|
|
CodeLabel? label = LabelManager.GetLabel((uint)addr.Value.Address, addr.Value.Type);
|
|
if(label == null) {
|
|
int length = 1;
|
|
if(_editor.SelectionLength > 1) {
|
|
int end = addr.Value.Address + _editor.SelectionLength;
|
|
int memSize = DebugApi.GetMemorySize(addr.Value.Type);
|
|
if(end >= memSize) {
|
|
end = memSize;
|
|
}
|
|
length = end - addr.Value.Address;
|
|
}
|
|
|
|
label = new CodeLabel() {
|
|
Address = (uint)addr.Value.Address,
|
|
MemoryType = addr.Value.Type,
|
|
Length = (uint)length
|
|
};
|
|
}
|
|
|
|
LabelEditWindow.EditLabel(label.MemoryType.ToCpuType(), this, label);
|
|
}
|
|
};
|
|
}
|
|
|
|
private ContextMenuAction GetAddWatchAction()
|
|
{
|
|
return new ContextMenuAction() {
|
|
ActionType = ActionType.AddWatch,
|
|
HintText = () => GetAddressRange(),
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_AddToWatch),
|
|
|
|
IsEnabled = () => _model.Config.MemoryType.SupportsWatch(),
|
|
|
|
OnClick = () => {
|
|
string[] toAdd = Enumerable.Range(_editor.SelectionStart, Math.Max(1, _editor.SelectionLength)).Select((num) => $"[${num.ToString("X2")}]").ToArray();
|
|
WatchManager.GetWatchManager(_model.Config.MemoryType.ToCpuType()).AddWatch(toAdd);
|
|
}
|
|
};
|
|
}
|
|
|
|
private ContextMenuAction GetEditBreakpointAction()
|
|
{
|
|
return new ContextMenuAction() {
|
|
ActionType = ActionType.EditBreakpoint,
|
|
HintText = () => GetAddressRange(),
|
|
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.MemoryViewer_EditBreakpoint),
|
|
|
|
OnClick = async () => {
|
|
uint startAddress = (uint)_editor.SelectionStart;
|
|
uint endAddress = (uint)(_editor.SelectionStart + Math.Max(1, _editor.SelectionLength) - 1);
|
|
|
|
MemoryType memType = _model.Config.MemoryType;
|
|
Breakpoint? bp = BreakpointManager.GetMatchingBreakpoint(startAddress, endAddress, memType);
|
|
CpuType cpuType = memType.ToCpuType();
|
|
if(bp == null) {
|
|
bp = new Breakpoint() {
|
|
MemoryType = memType,
|
|
CpuType = cpuType,
|
|
StartAddress = startAddress,
|
|
EndAddress = endAddress,
|
|
BreakOnWrite = true,
|
|
BreakOnRead = true
|
|
};
|
|
if(bp.SupportsExec) {
|
|
bp.BreakOnExec = true;
|
|
}
|
|
}
|
|
|
|
bool result = await BreakpointEditWindow.EditBreakpointAsync(bp, this);
|
|
if(result && DebugWindowManager.GetDebugWindow<DebuggerWindow>(x => x.CpuType == cpuType) == null) {
|
|
DebuggerWindow.GetOrOpenWindow(cpuType);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
private ContextMenuAction GetFreezeAction(ActionType action, DebuggerShortcut shortcut)
|
|
{
|
|
DebugConfig cfg = ConfigManager.Config.Debug;
|
|
return new ContextMenuAction() {
|
|
ActionType = action,
|
|
HintText = () => GetAddressRange(),
|
|
IsVisible = () => _model.Config.MemoryType.SupportsFreezeAddress(),
|
|
OnClick = () => {
|
|
UInt32 start = (UInt32)_editor.SelectionStart;
|
|
UInt32 end = (UInt32)(_editor.SelectionStart + Math.Max(1, _editor.SelectionLength) - 1);
|
|
DebugApi.UpdateFrozenAddresses(_model.Config.MemoryType.ToCpuType(), start, end, action == ActionType.FreezeMemory);
|
|
},
|
|
Shortcut = () => cfg.Shortcuts.Get(shortcut)
|
|
};
|
|
}
|
|
|
|
private ContextMenuAction GetMarkSelectionAction()
|
|
{
|
|
return MarkSelectionHelper.GetAction(
|
|
() => _model.Config.MemoryType,
|
|
() => _model.SelectionStart,
|
|
() => _model.SelectionStart + Math.Max(0, _model.SelectionLength - 1),
|
|
() => { }
|
|
);
|
|
}
|
|
|
|
private string GetAddressRange()
|
|
{
|
|
string address = "$" + _editor.SelectionStart.ToString("X2");
|
|
if(_editor.SelectionLength > 1) {
|
|
address += "-$" + (_editor.SelectionStart + _editor.SelectionLength - 1).ToString("X2");
|
|
}
|
|
return address;
|
|
}
|
|
|
|
public void ProcessNotification(NotificationEventArgs e)
|
|
{
|
|
switch(e.NotificationType) {
|
|
case ConsoleNotificationType.CodeBreak:
|
|
case ConsoleNotificationType.StateLoaded:
|
|
Dispatcher.UIThread.Post(() => {
|
|
_editor.InvalidateVisual();
|
|
});
|
|
break;
|
|
|
|
case ConsoleNotificationType.PpuFrameDone:
|
|
if(!ToolRefreshHelper.LimitFps(this, 80)) {
|
|
Dispatcher.UIThread.Post(() => {
|
|
_editor.InvalidateVisual();
|
|
});
|
|
}
|
|
break;
|
|
|
|
case ConsoleNotificationType.GameLoaded:
|
|
Dispatcher.UIThread.Post(() => {
|
|
_model.UpdateAvailableMemoryTypes();
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|