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