diff --git a/UI/Debugger/Utilities/ContextMenuAction.cs b/UI/Debugger/Utilities/ContextMenuAction.cs index 35e39d3ad..19b7f884a 100644 --- a/UI/Debugger/Utilities/ContextMenuAction.cs +++ b/UI/Debugger/Utilities/ContextMenuAction.cs @@ -835,6 +835,12 @@ public enum ActionType [IconFile("HdPack")] CopyToHdPackFormat, + + [IconFile("CheatCode")] + CopyTileMemory, + + [IconFile("Paste")] + PasteTileMemory, [IconFile("Find")] CheatDatabase, diff --git a/UI/Debugger/Utilities/MemCopyHelper.cs b/UI/Debugger/Utilities/MemCopyHelper.cs new file mode 100644 index 000000000..a5f01294d --- /dev/null +++ b/UI/Debugger/Utilities/MemCopyHelper.cs @@ -0,0 +1,77 @@ +using Avalonia; +using Mesen.Interop; +using Mesen.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Tmds.DBus; + +namespace Mesen.Debugger.Utilities +{ + public static class MemCopyHelper + { + public static string ToString(AddressInfo tileAddr, int len) + { + StringBuilder sb = new StringBuilder(); + + if(tileAddr.Type == MemoryType.NesPpuMemory) { + tileAddr = DebugApi.GetAbsoluteAddress(tileAddr); + } + + for(int i = 0; i < len; i++) { + if(sb.Length > 0) { + sb.Append(" "); + } + sb.Append(DebugApi.GetMemoryValue(tileAddr.Type, (uint)(tileAddr.Address + i)).ToString("X2")); + } + + return sb.ToString(); + } + + public static int GetBytesPerTile(TileFormat format) + { + int bitsPerPixel = format.GetBitsPerPixel(); + PixelSize tileSize = format.GetTileSize(); + int bytesPerTile = tileSize.Width * tileSize.Height * bitsPerPixel / 8; + + return bytesPerTile; + } + + public static void CopyTileMem(int address, MemoryType memoryType, TileFormat format) + { + int bytesPerTile = GetBytesPerTile(format); + + AddressInfo addr = new AddressInfo() { Address = address, Type = memoryType }; + string tileMem = MemCopyHelper.ToString(addr, bytesPerTile); + + if(tileMem.Length > 0) { + ApplicationHelper.GetMainWindow()?.Clipboard?.SetTextAsync(tileMem); + } + } + + public static void PasteTileMem(int address, MemoryType memoryType, TileFormat format) + { + var clipboard = ApplicationHelper.GetMainWindow()?.Clipboard; + if(clipboard != null) { + string? text = Task.Run(() => clipboard?.GetTextAsync()).GetAwaiter().GetResult(); + if(text != null) { + text = text.Replace("\n", "").Replace("\r", "").Replace(" ", ""); + int bytesPerTile = GetBytesPerTile(format); + int charsPerTile = bytesPerTile * 2; + string pattern = $"^[A-F0-9]{{{charsPerTile}}}$"; + + if(Regex.IsMatch(text, pattern, RegexOptions.IgnoreCase)) { + byte[] pastedData = HexUtilities.HexToArray(text); + for(int i = 0; i < bytesPerTile; i++) { + DebugApi.SetMemoryValue(memoryType, (uint)(address + i), (byte)pastedData[i]); + } + } + } + } + } + } +} diff --git a/UI/Debugger/ViewModels/TileViewerViewModel.cs b/UI/Debugger/ViewModels/TileViewerViewModel.cs index fb3fee4d6..3be539421 100644 --- a/UI/Debugger/ViewModels/TileViewerViewModel.cs +++ b/UI/Debugger/ViewModels/TileViewerViewModel.cs @@ -186,7 +186,7 @@ public TileViewerViewModel(CpuType cpuType, PictureViewer picViewer, Window? wnd } } }, - new ContextMenuSeparator() { IsVisible = () => CpuType == CpuType.Nes }, + new ContextMenuSeparator(), new ContextMenuAction() { ActionType = ActionType.CopyToHdPackFormat, IsVisible = () => CpuType == CpuType.Nes, @@ -197,6 +197,28 @@ public TileViewerViewModel(CpuType cpuType, PictureViewer picViewer, Window? wnd HdPackCopyHelper.CopyToHdPackFormat(address, Config.Source, RawPalette, SelectedPalette, false); } } + }, + new ContextMenuAction() { + ActionType = ActionType.CopyTileMemory, + Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.Copy), + IsEnabled = () => GetSelectedTileAddress() >= 0, + OnClick = () => { + int address = GetSelectedTileAddress(); + if(address >= 0) { + MemCopyHelper.CopyTileMem(address, Config.Source, Config.Format); + } + } + }, + new ContextMenuAction() { + ActionType = ActionType.PasteTileMemory, + Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.Paste), + IsEnabled = () => GetSelectedTileAddress() >= 0, + OnClick = () => { + int address = GetSelectedTileAddress(); + if(address >= 0) { + MemCopyHelper.PasteTileMem(address, Config.Source, Config.Format); + } + } } }); diff --git a/UI/Localization/resources.en.xml b/UI/Localization/resources.en.xml index e390e7814..39e32ebe6 100644 --- a/UI/Localization/resources.en.xml +++ b/UI/Localization/resources.en.xml @@ -3296,6 +3296,8 @@ E Reset access stats Copy tile (HD pack format) + Copy tile (memory) + Paste tile (memory) Cheat database Use bilinear interpolation