From 113d440b3d9c56c55615c97591f02fc17f5c077c Mon Sep 17 00:00:00 2001 From: Sour Date: Sat, 23 Mar 2019 15:39:21 -0400 Subject: [PATCH] Debugger: Added tile viewer tool --- Core/DebugTypes.h | 21 + Core/Debugger.cpp | 2 +- Core/PpuTools.cpp | 189 +++++++-- Core/PpuTools.h | 11 +- InteropDLL/DebugApiWrapper.cpp | 1 + UI/Debugger/DebugWindowManager.cs | 2 + .../PpuViewer/ctrlPaletteViewer.Designer.cs | 62 +++ UI/Debugger/PpuViewer/ctrlPaletteViewer.cs | 106 ++++++ UI/Debugger/PpuViewer/ctrlPaletteViewer.resx | 120 ++++++ .../PpuViewer/frmPaletteViewer.Designer.cs | 29 +- UI/Debugger/PpuViewer/frmPaletteViewer.cs | 65 +--- .../PpuViewer/frmTileViewer.Designer.cs | 360 ++++++++++++++++++ UI/Debugger/PpuViewer/frmTileViewer.cs | 205 ++++++++++ UI/Debugger/PpuViewer/frmTileViewer.resx | 123 ++++++ .../PpuViewer/frmTilemapViewer.Designer.cs | 1 - UI/Forms/frmMain.Designer.cs | 53 ++- UI/Forms/frmMain.cs | 6 + UI/Interop/DebugApi.cs | 29 ++ UI/UI.csproj | 18 + 19 files changed, 1263 insertions(+), 140 deletions(-) create mode 100644 UI/Debugger/PpuViewer/ctrlPaletteViewer.Designer.cs create mode 100644 UI/Debugger/PpuViewer/ctrlPaletteViewer.cs create mode 100644 UI/Debugger/PpuViewer/ctrlPaletteViewer.resx create mode 100644 UI/Debugger/PpuViewer/frmTileViewer.Designer.cs create mode 100644 UI/Debugger/PpuViewer/frmTileViewer.cs create mode 100644 UI/Debugger/PpuViewer/frmTileViewer.resx diff --git a/Core/DebugTypes.h b/Core/DebugTypes.h index 1e19281..b22fd5d 100644 --- a/Core/DebugTypes.h +++ b/Core/DebugTypes.h @@ -168,4 +168,25 @@ struct GetTilemapOptions bool ShowTileGrid; bool ShowScrollOverlay; +}; + +enum TileFormat +{ + Bpp2, + Bpp4, + Bpp8, + DirectColor, + Mode7, + Mode7DirectColor, +}; + +struct GetTileViewOptions +{ + TileFormat Format; + int32_t Width; + int32_t Palette; + SnesMemoryType MemoryType; + int32_t AddressOffset; + + bool ShowTileGrid; }; \ No newline at end of file diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index e670f06..fab08d3 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -38,7 +38,7 @@ Debugger::Debugger(shared_ptr console) _ppuTools.reset(new PpuTools(_console.get(), _ppu.get())); _eventManager.reset(new EventManager(this, _cpu.get(), _ppu.get())); - _cpuStepCount = 0; + _cpuStepCount = -1; _executionStopped = false; _breakRequestCount = 0; diff --git a/Core/PpuTools.cpp b/Core/PpuTools.cpp index 31270b9..4bba382 100644 --- a/Core/PpuTools.cpp +++ b/Core/PpuTools.cpp @@ -3,6 +3,8 @@ #include "Ppu.h" #include "DebugTypes.h" #include "Console.h" +#include "BaseCartridge.h" +#include "MemoryManager.h" #include "NotificationManager.h" PpuTools::PpuTools(Console *console, Ppu *ppu) @@ -11,27 +13,26 @@ PpuTools::PpuTools(Console *console, Ppu *ppu) _ppu = ppu; } -uint16_t PpuTools::GetTilePixelColor(const uint8_t bpp, const uint16_t pixelStart, const uint8_t shift) +uint8_t PpuTools::GetTilePixelColor(const uint8_t* ram, const uint32_t ramMask, const uint8_t bpp, const uint32_t pixelStart, const uint8_t shift) { - uint8_t *vram = _ppu->GetVideoRam(); - uint16_t color; + uint8_t color; if(bpp == 2) { - color = (((vram[pixelStart + 0] >> shift) & 0x01) << 0); - color |= (((vram[pixelStart + 1] >> shift) & 0x01) << 1); + color = (((ram[(pixelStart + 0) & ramMask] >> shift) & 0x01) << 0); + color |= (((ram[(pixelStart + 1) & ramMask] >> shift) & 0x01) << 1); } else if(bpp == 4) { - color = (((vram[pixelStart + 0] >> shift) & 0x01) << 0); - color |= (((vram[pixelStart + 1] >> shift) & 0x01) << 1); - color |= (((vram[pixelStart + 16] >> shift) & 0x01) << 2); - color |= (((vram[pixelStart + 17] >> shift) & 0x01) << 3); + color = (((ram[(pixelStart + 0) & ramMask] >> shift) & 0x01) << 0); + color |= (((ram[(pixelStart + 1) & ramMask] >> shift) & 0x01) << 1); + color |= (((ram[(pixelStart + 16) & ramMask] >> shift) & 0x01) << 2); + color |= (((ram[(pixelStart + 17) & ramMask] >> shift) & 0x01) << 3); } else if(bpp == 8) { - color = (((vram[pixelStart + 0] >> shift) & 0x01) << 0); - color |= (((vram[pixelStart + 1] >> shift) & 0x01) << 1); - color |= (((vram[pixelStart + 16] >> shift) & 0x01) << 2); - color |= (((vram[pixelStart + 17] >> shift) & 0x01) << 3); - color |= (((vram[pixelStart + 32] >> shift) & 0x01) << 4); - color |= (((vram[pixelStart + 33] >> shift) & 0x01) << 5); - color |= (((vram[pixelStart + 48] >> shift) & 0x01) << 6); - color |= (((vram[pixelStart + 49] >> shift) & 0x01) << 7); + color = (((ram[(pixelStart + 0) & ramMask] >> shift) & 0x01) << 0); + color |= (((ram[(pixelStart + 1) & ramMask] >> shift) & 0x01) << 1); + color |= (((ram[(pixelStart + 16) & ramMask] >> shift) & 0x01) << 2); + color |= (((ram[(pixelStart + 17) & ramMask] >> shift) & 0x01) << 3); + color |= (((ram[(pixelStart + 32) & ramMask] >> shift) & 0x01) << 4); + color |= (((ram[(pixelStart + 33) & ramMask] >> shift) & 0x01) << 5); + color |= (((ram[(pixelStart + 48) & ramMask] >> shift) & 0x01) << 6); + color |= (((ram[(pixelStart + 49) & ramMask] >> shift) & 0x01) << 7); } else { throw std::runtime_error("unsupported bpp"); } @@ -57,6 +58,137 @@ void PpuTools::BlendColors(uint8_t output[4], uint8_t input[4]) output[3] = 0xFF; } +uint32_t PpuTools::GetRgbPixelColor(uint8_t colorIndex, uint8_t palette, uint8_t bpp, bool directColorMode, uint16_t basePaletteOffset) +{ + uint16_t paletteColor; + if(bpp == 8 && directColorMode) { + paletteColor = ( + (((colorIndex & 0x07) | (palette & 0x01)) << 1) | + (((colorIndex & 0x38) | ((palette & 0x02) << 1)) << 3) | + (((colorIndex & 0xC0) | ((palette & 0x04) << 3)) << 7) + ); + } else { + uint8_t* cgram = _ppu->GetCgRam(); + uint16_t paletteRamOffset = basePaletteOffset + (palette * (1 << bpp) + colorIndex) * 2; + paletteColor = cgram[paletteRamOffset] | (cgram[paletteRamOffset + 1] << 8); + } + return ToArgb(paletteColor); +} + +void PpuTools::GetTileView(GetTileViewOptions options, uint32_t *outBuffer) +{ + uint8_t* ram; + uint32_t ramMask; + uint8_t bpp; + + bool directColor = false; + switch(options.Format) { + case TileFormat::Bpp2: bpp = 2; break; + case TileFormat::Bpp4: bpp = 4; break; + + case TileFormat::DirectColor: + directColor = true; + bpp = 8; + break; + + case TileFormat::Mode7: + bpp = 16; + break; + + case TileFormat::Mode7DirectColor: + directColor = true; + bpp = 16; + break; + + default: bpp = 8; break; + } + + switch(options.MemoryType) { + default: + case SnesMemoryType::VideoRam: + ramMask = Ppu::VideoRamSize - 1; + ram = _ppu->GetVideoRam(); + break; + + case SnesMemoryType::PrgRom: + ramMask = _console->GetCartridge()->DebugGetPrgRomSize() - 1; + ram = _console->GetCartridge()->DebugGetPrgRom(); + break; + + case SnesMemoryType::WorkRam: + ramMask = MemoryManager::WorkRamSize - 1; + ram = _console->GetMemoryManager()->DebugGetWorkRam(); + break; + } + + uint8_t* cgram = _ppu->GetCgRam(); + int bytesPerTile = 64 * bpp / 8; + int tileCount = 0x10000 / bytesPerTile; + + uint16_t bgColor = (cgram[1] << 8) | cgram[0]; + for(int i = 0; i < 512 * 512; i++) { + outBuffer[i] = ToArgb(bgColor); + } + + int rowCount = tileCount / options.Width; + + if(options.Format == TileFormat::Mode7 || options.Format == TileFormat::Mode7DirectColor) { + for(int row = 0; row < rowCount; row++) { + uint32_t baseOffset = row * bytesPerTile * options.Width + options.AddressOffset; + + for(int column = 0; column < options.Width; column++) { + uint32_t addr = baseOffset + bytesPerTile * column; + + for(int y = 0; y < 8; y++) { + uint32_t pixelStart = addr + y * 16; + for(int x = 0; x < 8; x++) { + uint8_t color = ram[(pixelStart + x * 2 + 1) & ramMask]; + + if(color != 0) { + uint32_t rgbColor; + if(directColor) { + rgbColor = ToArgb(((color & 0x07) << 2) | ((color & 0x38) << 4) | ((color & 0xC0) << 7)); + } else { + rgbColor = GetRgbPixelColor(color, 0, 8, false, 0); + } + outBuffer[((row * 8) + y) * (options.Width * 8) + column * 8 + x] = rgbColor; + } + } + } + } + } + } else { + for(int row = 0; row < rowCount; row++) { + uint32_t baseOffset = row * bytesPerTile * options.Width + options.AddressOffset; + + for(int column = 0; column < options.Width; column++) { + uint32_t addr = baseOffset + bytesPerTile * column; + + for(int y = 0; y < 8; y++) { + uint32_t pixelStart = addr + y * 2; + for(int x = 0; x < 8; x++) { + uint8_t color = GetTilePixelColor(ram, ramMask, bpp, pixelStart, 7 - x); + if(color != 0) { + outBuffer[((row * 8) + y) * (options.Width * 8) + column * 8 + x] = GetRgbPixelColor(color, options.Palette, bpp, directColor, 0); + } + } + } + } + } + } + + if(options.ShowTileGrid) { + constexpr uint32_t gridColor = 0xA0AAAAFF; + for(int j = 0; j < rowCount * 8; j++) { + for(int i = 0; i < options.Width * 8; i++) { + if((i & 0x07) == 0x07 || (j & 0x07) == 0x07) { + BlendColors((uint8_t*)&outBuffer[j*options.Width*8+i], (uint8_t*)&gridColor); + } + } + } + } +} + void PpuTools::GetTilemap(GetTilemapOptions options, uint32_t* outBuffer) { static constexpr uint8_t layerBpp[8][4] = { @@ -106,7 +238,7 @@ void PpuTools::GetTilemap(GetTilemapOptions options, uint32_t* outBuffer) tileIndex + (largeTileHeight ? ((row & 0x01) ? (vMirror ? 0 : 16) : (vMirror ? 16 : 0)) : 0) + (largeTileWidth ? ((column & 0x01) ? (hMirror ? 0 : 1) : (hMirror ? 1 : 0)) : 0) - ) & 0x3FF; + ) & 0x3FF; } for(int y = 0; y < 8; y++) { @@ -115,23 +247,10 @@ void PpuTools::GetTilemap(GetTilemapOptions options, uint32_t* outBuffer) for(int x = 0; x < 8; x++) { uint8_t shift = hMirror ? x : (7 - x); - uint16_t color = GetTilePixelColor(bpp, pixelStart, shift); + uint8_t color = GetTilePixelColor(vram, Ppu::VideoRamSize - 1, bpp, pixelStart, shift); if(color != 0) { - uint16_t paletteColor; - /*if(bpp == 8 && directColorMode) { - uint8_t palette = (vram[addr + 1] >> 2) & 0x07; - paletteColor = ( - (((color & 0x07) | (palette & 0x01)) << 1) | - (((color & 0x38) | ((palette & 0x02) << 1)) << 3) | - (((color & 0xC0) | ((palette & 0x04) << 3)) << 7) - ); - } else {*/ uint8_t palette = bpp == 8 ? 0 : (vram[addr + 1] >> 2) & 0x07; - uint16_t paletteRamOffset = basePaletteOffset + (palette * (1 << bpp) + color) * 2; - paletteColor = cgram[paletteRamOffset] | (cgram[paletteRamOffset + 1] << 8); - //} - - outBuffer[((row * 8) + y) * 512 + column * 8 + x] = ToArgb(paletteColor); + outBuffer[((row * 8) + y) * 512 + column * 8 + x] = GetRgbPixelColor(color, palette, bpp, false, basePaletteOffset); } } } @@ -139,7 +258,7 @@ void PpuTools::GetTilemap(GetTilemapOptions options, uint32_t* outBuffer) } if(options.ShowTileGrid) { - uint32_t gridColor = 0xA0AAAAFF; + constexpr uint32_t gridColor = 0xA0AAAAFF; for(int i = 0; i < 512 * 512; i++) { if((i & 0x07) == 0x07 || (i & 0x0E00) == 0x0E00) { BlendColors((uint8_t*)&outBuffer[i], (uint8_t*)&gridColor); @@ -148,7 +267,7 @@ void PpuTools::GetTilemap(GetTilemapOptions options, uint32_t* outBuffer) } if(options.ShowScrollOverlay) { - uint32_t overlayColor = 0x40FFFFFF; + constexpr uint32_t overlayColor = 0x40FFFFFF; for(int y = 0; y < 240; y++) { for(int x = 0; x < 256; x++) { int xPos = layer.HScroll + x; diff --git a/Core/PpuTools.h b/Core/PpuTools.h index 59d2017..01f39c0 100644 --- a/Core/PpuTools.h +++ b/Core/PpuTools.h @@ -1,9 +1,9 @@ #pragma once #include "stdafx.h" +#include "DebugTypes.h" class Ppu; class Console; -struct GetTilemapOptions; class PpuTools { @@ -12,14 +12,17 @@ private: Console *_console; unordered_map _updateTimings; - uint16_t GetTilePixelColor(const uint8_t bpp, const uint16_t pixelStart, const uint8_t shift); + uint8_t GetTilePixelColor(const uint8_t* ram, const uint32_t ramMask, const uint8_t bpp, const uint32_t pixelStart, const uint8_t shift); uint32_t ToArgb(uint16_t color); void BlendColors(uint8_t output[4], uint8_t input[4]); -public: - PpuTools(Console *console, Ppu* ppu); + uint32_t GetRgbPixelColor(uint8_t colorIndex, uint8_t palette, uint8_t bpp, bool directColorMode, uint16_t basePaletteOffset); +public: + PpuTools(Console *console, Ppu *ppu); + + void GetTileView(GetTileViewOptions options, uint32_t *outBuffer); void GetTilemap(GetTilemapOptions options, uint32_t *outBuffer); void SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint16_t cycle); diff --git a/InteropDLL/DebugApiWrapper.cpp b/InteropDLL/DebugApiWrapper.cpp index 60edc09..5e20fde 100644 --- a/InteropDLL/DebugApiWrapper.cpp +++ b/InteropDLL/DebugApiWrapper.cpp @@ -62,6 +62,7 @@ extern "C" DllExport void __stdcall SetMemoryValues(SnesMemoryType type, uint32_t address, uint8_t* data, int32_t length) { return GetDebugger()->GetMemoryDumper()->SetMemoryValues(type, address, data, length); } DllExport void __stdcall GetTilemap(GetTilemapOptions options, uint32_t *buffer) { GetDebugger()->GetPpuTools()->GetTilemap(options, buffer); } + DllExport void __stdcall GetTileView(GetTileViewOptions options, uint32_t *buffer) { GetDebugger()->GetPpuTools()->GetTileView(options, buffer); } DllExport void __stdcall SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint16_t cycle) { GetDebugger()->GetPpuTools()->SetViewerUpdateTiming(viewerId, scanline, cycle); } DllExport void __stdcall GetDebugEvents(DebugEventInfo *infoArray, uint32_t &maxEventCount, bool getPreviousFrameData) { GetDebugger()->GetEventManager()->GetEvents(infoArray, maxEventCount, getPreviousFrameData); } diff --git a/UI/Debugger/DebugWindowManager.cs b/UI/Debugger/DebugWindowManager.cs index cc1689c..7281f48 100644 --- a/UI/Debugger/DebugWindowManager.cs +++ b/UI/Debugger/DebugWindowManager.cs @@ -34,6 +34,7 @@ namespace Mesen.GUI.Debugger case DebugWindow.Debugger: frm = new frmDebugger(); frm.Icon = Properties.Resources.Debugger; break; case DebugWindow.TraceLogger: frm = new frmTraceLogger(); frm.Icon = Properties.Resources.LogWindow; break; case DebugWindow.MemoryTools: frm = new frmMemoryTools(); frm.Icon = Properties.Resources.CheatCode; break; + case DebugWindow.TileViewer: frm = new frmTileViewer(); frm.Icon = Properties.Resources.VerticalLayout; break; case DebugWindow.TilemapViewer: frm = new frmTilemapViewer(); frm.Icon = Properties.Resources.VideoOptions; break; case DebugWindow.PaletteViewer: frm = new frmPaletteViewer(); frm.Icon = Properties.Resources.VideoFilter; break; case DebugWindow.EventViewer: frm = new frmEventViewer(); frm.Icon = Properties.Resources.NesEventViewer; break; @@ -119,6 +120,7 @@ namespace Mesen.GUI.Debugger Debugger, MemoryTools, TraceLogger, + TileViewer, TilemapViewer, PaletteViewer, EventViewer diff --git a/UI/Debugger/PpuViewer/ctrlPaletteViewer.Designer.cs b/UI/Debugger/PpuViewer/ctrlPaletteViewer.Designer.cs new file mode 100644 index 0000000..2b4ad0e --- /dev/null +++ b/UI/Debugger/PpuViewer/ctrlPaletteViewer.Designer.cs @@ -0,0 +1,62 @@ +namespace Mesen.GUI.Debugger +{ + partial class ctrlPaletteViewer + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.picPalette = new Mesen.GUI.Controls.ctrlMesenPictureBox(); + ((System.ComponentModel.ISupportInitialize)(this.picPalette)).BeginInit(); + this.SuspendLayout(); + // + // picPalette + // + this.picPalette.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; + this.picPalette.Location = new System.Drawing.Point(0, 0); + this.picPalette.MinimumSize = new System.Drawing.Size(256, 256); + this.picPalette.Name = "picPalette"; + this.picPalette.Size = new System.Drawing.Size(256, 256); + this.picPalette.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.picPalette.TabIndex = 1; + this.picPalette.TabStop = false; + this.picPalette.MouseClick += new System.Windows.Forms.MouseEventHandler(this.picPalette_MouseClick); + // + // ctrlPaletteViewer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.picPalette); + this.Name = "ctrlPaletteViewer"; + this.Size = new System.Drawing.Size(256, 256); + ((System.ComponentModel.ISupportInitialize)(this.picPalette)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private GUI.Controls.ctrlMesenPictureBox picPalette; + } +} diff --git a/UI/Debugger/PpuViewer/ctrlPaletteViewer.cs b/UI/Debugger/PpuViewer/ctrlPaletteViewer.cs new file mode 100644 index 0000000..86b6835 --- /dev/null +++ b/UI/Debugger/PpuViewer/ctrlPaletteViewer.cs @@ -0,0 +1,106 @@ +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 System.Runtime.InteropServices; +using Mesen.GUI.Controls; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; + +namespace Mesen.GUI.Debugger +{ + public partial class ctrlPaletteViewer : BaseControl + { + private byte[] _cgRam; + private Bitmap _paletteImage; + + public int PaletteScale { get; set; } = 16; + public int SelectedPalette { get; private set; } = 0; + public PaletteSelectionMode SelectionMode { get; set; } = PaletteSelectionMode.None; + + public ctrlPaletteViewer() + { + InitializeComponent(); + if(IsDesignMode) { + return; + } + + _paletteImage = new Bitmap(PaletteScale * 16, PaletteScale * 16, PixelFormat.Format32bppArgb); + picPalette.Image = _paletteImage; + } + + public void RefreshData() + { + _cgRam = DebugApi.GetMemoryState(SnesMemoryType.CGRam); + } + + public void RefreshViewer() + { + Func to8Bit = (int color) => { return (uint)((color << 3) + (color >> 2)); }; + Func toArgb = (int rgb555) => { + uint b = to8Bit(rgb555 >> 10); + uint g = to8Bit((rgb555 >> 5) & 0x1F); + uint r = to8Bit(rgb555 & 0x1F); + + return (0xFF000000 | (r << 16) | (g << 8) | b); + }; + + UInt32[] argbPalette = new UInt32[256]; + for(int i = 0; i < 256; i++) { + argbPalette[i] = toArgb(_cgRam[i * 2] | _cgRam[i * 2 + 1] << 8); + } + + using(Graphics g = Graphics.FromImage(_paletteImage)) { + GCHandle handle = GCHandle.Alloc(argbPalette, GCHandleType.Pinned); + Bitmap source = new Bitmap(16, 16, 16 * 4, PixelFormat.Format32bppArgb, handle.AddrOfPinnedObject()); + try { + g.InterpolationMode = InterpolationMode.NearestNeighbor; + g.SmoothingMode = SmoothingMode.None; + g.PixelOffsetMode = PixelOffsetMode.Half; + + g.ScaleTransform(PaletteScale, PaletteScale); + g.DrawImage(source, 0, 0); + + g.ResetTransform(); + using(Pen pen = new Pen(Color.LightGray, 2)) { + pen.DashStyle = DashStyle.Dash; + if(SelectionMode == PaletteSelectionMode.FourColors) { + g.DrawRectangle(pen, (SelectedPalette & 0x03) * PaletteScale * 4, (SelectedPalette / 4) * PaletteScale, PaletteScale * 4, PaletteScale); + } else if(SelectionMode == PaletteSelectionMode.SixteenColors) { + g.DrawRectangle(pen, 0, SelectedPalette * PaletteScale, PaletteScale * 16, PaletteScale); + } + } + } finally { + handle.Free(); + } + } + + picPalette.Size = _paletteImage.Size; + picPalette.Invalidate(); + } + + private void picPalette_MouseClick(object sender, MouseEventArgs e) + { + int paletteIndex = 0; + if(SelectionMode == PaletteSelectionMode.FourColors) { + paletteIndex += e.X / (4 * PaletteScale); + paletteIndex += (e.Y / PaletteScale) * 4; + } else if(SelectionMode == PaletteSelectionMode.SixteenColors) { + paletteIndex = (e.Y / PaletteScale); + } + SelectedPalette = paletteIndex; + } + } + + public enum PaletteSelectionMode + { + None, + FourColors, + SixteenColors + } +} diff --git a/UI/Debugger/PpuViewer/ctrlPaletteViewer.resx b/UI/Debugger/PpuViewer/ctrlPaletteViewer.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/UI/Debugger/PpuViewer/ctrlPaletteViewer.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/UI/Debugger/PpuViewer/frmPaletteViewer.Designer.cs b/UI/Debugger/PpuViewer/frmPaletteViewer.Designer.cs index b92daf7..b2dcafc 100644 --- a/UI/Debugger/PpuViewer/frmPaletteViewer.Designer.cs +++ b/UI/Debugger/PpuViewer/frmPaletteViewer.Designer.cs @@ -27,24 +27,12 @@ /// private void InitializeComponent() { - this.picPalette = new Mesen.GUI.Controls.ctrlMesenPictureBox(); this.ctrlScanlineCycleSelect = new Mesen.GUI.Debugger.Controls.ctrlScanlineCycleSelect(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); - ((System.ComponentModel.ISupportInitialize)(this.picPalette)).BeginInit(); + this.ctrlPaletteViewer = new Mesen.GUI.Debugger.ctrlPaletteViewer(); this.tableLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // - // picPalette - // - this.picPalette.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; - this.picPalette.Location = new System.Drawing.Point(3, 3); - this.picPalette.MinimumSize = new System.Drawing.Size(256, 256); - this.picPalette.Name = "picPalette"; - this.picPalette.Size = new System.Drawing.Size(256, 256); - this.picPalette.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; - this.picPalette.TabIndex = 0; - this.picPalette.TabStop = false; - // // ctrlScanlineCycleSelect // this.ctrlScanlineCycleSelect.Dock = System.Windows.Forms.DockStyle.Bottom; @@ -58,7 +46,7 @@ this.tableLayoutPanel1.ColumnCount = 2; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel1.Controls.Add(this.picPalette, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.ctrlPaletteViewer, 0, 1); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; @@ -68,6 +56,13 @@ this.tableLayoutPanel1.Size = new System.Drawing.Size(485, 263); this.tableLayoutPanel1.TabIndex = 7; // + // ctrlPaletteViewer1 + // + this.ctrlPaletteViewer.Location = new System.Drawing.Point(3, 3); + this.ctrlPaletteViewer.Name = "ctrlPaletteViewer"; + this.ctrlPaletteViewer.Size = new System.Drawing.Size(256, 256); + this.ctrlPaletteViewer.TabIndex = 0; + // // frmPaletteViewer // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -76,17 +71,15 @@ this.Controls.Add(this.tableLayoutPanel1); this.Controls.Add(this.ctrlScanlineCycleSelect); this.Name = "frmPaletteViewer"; - this.Text = "Tilemap Viewer"; - ((System.ComponentModel.ISupportInitialize)(this.picPalette)).EndInit(); + this.Text = "Palette Viewer"; this.tableLayoutPanel1.ResumeLayout(false); this.ResumeLayout(false); } #endregion - - private GUI.Controls.ctrlMesenPictureBox picPalette; private Controls.ctrlScanlineCycleSelect ctrlScanlineCycleSelect; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private ctrlPaletteViewer ctrlPaletteViewer; } } \ No newline at end of file diff --git a/UI/Debugger/PpuViewer/frmPaletteViewer.cs b/UI/Debugger/PpuViewer/frmPaletteViewer.cs index 4382652..6bad33b 100644 --- a/UI/Debugger/PpuViewer/frmPaletteViewer.cs +++ b/UI/Debugger/PpuViewer/frmPaletteViewer.cs @@ -1,15 +1,5 @@ using Mesen.GUI.Forms; using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace Mesen.GUI.Debugger @@ -17,8 +7,6 @@ namespace Mesen.GUI.Debugger public partial class frmPaletteViewer : BaseForm { private NotificationListener _notifListener; - private byte[] _cgRam; - private Bitmap _paletteImage; public frmPaletteViewer() { @@ -35,13 +23,10 @@ namespace Mesen.GUI.Debugger _notifListener = new NotificationListener(); _notifListener.OnNotification += OnNotificationReceived; - _paletteImage = new Bitmap(256, 256, PixelFormat.Format32bppArgb); - picPalette.Image = _paletteImage; - ctrlScanlineCycleSelect.Initialize(241, 0); - RefreshData(); - RefreshViewer(); + ctrlPaletteViewer.RefreshData(); + ctrlPaletteViewer.RefreshViewer(); } protected override void OnFormClosed(FormClosedEventArgs e) @@ -55,53 +40,13 @@ namespace Mesen.GUI.Debugger switch(e.NotificationType) { case ConsoleNotificationType.ViewerRefresh: if(e.Parameter.ToInt32() == ctrlScanlineCycleSelect.ViewerId) { - RefreshData(); + ctrlPaletteViewer.RefreshData(); this.BeginInvoke((Action)(() => { - this.RefreshViewer(); + ctrlPaletteViewer.RefreshViewer(); })); } break; } } - - private void RefreshData() - { - _cgRam = DebugApi.GetMemoryState(SnesMemoryType.CGRam); - } - - private void RefreshViewer() - { - Func to8Bit = (int color) => { return (uint)((color << 3) + (color >> 2)); }; - Func toArgb = (int rgb555) => { - uint b = to8Bit(rgb555 >> 10); - uint g = to8Bit((rgb555 >> 5) & 0x1F); - uint r = to8Bit(rgb555 & 0x1F); - - return (0xFF000000 | (r << 16) | (g << 8) | b); - }; - - - UInt32[] argbPalette = new UInt32[256]; - for(int i = 0; i < 256; i++) { - argbPalette[i] = toArgb(_cgRam[i * 2] | _cgRam[i * 2 + 1] << 8); - } - - using(Graphics g = Graphics.FromImage(_paletteImage)) { - GCHandle handle = GCHandle.Alloc(argbPalette, GCHandleType.Pinned); - Bitmap source = new Bitmap(16, 16, 16 * 4, PixelFormat.Format32bppArgb, handle.AddrOfPinnedObject()); - try { - g.InterpolationMode = InterpolationMode.NearestNeighbor; - g.SmoothingMode = SmoothingMode.None; - g.PixelOffsetMode = PixelOffsetMode.Half; - - g.ScaleTransform(16, 16); - g.DrawImage(source, 0, 0); - } finally { - handle.Free(); - } - } - - picPalette.Invalidate(); - } } -} +} \ No newline at end of file diff --git a/UI/Debugger/PpuViewer/frmTileViewer.Designer.cs b/UI/Debugger/PpuViewer/frmTileViewer.Designer.cs new file mode 100644 index 0000000..11df82d --- /dev/null +++ b/UI/Debugger/PpuViewer/frmTileViewer.Designer.cs @@ -0,0 +1,360 @@ +namespace Mesen.GUI.Debugger +{ + partial class frmTileViewer + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.picTilemap = new Mesen.GUI.Controls.ctrlMesenPictureBox(); + this.ctrlScanlineCycleSelect = new Mesen.GUI.Debugger.Controls.ctrlScanlineCycleSelect(); + this.pnlTilemap = new System.Windows.Forms.Panel(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.cboMemoryType = new Mesen.GUI.Debugger.Controls.ComboBoxWithSeparator(); + this.nudBank = new Mesen.GUI.Controls.MesenNumericUpDown(); + this.lblBank = new System.Windows.Forms.Label(); + this.lblSource = new System.Windows.Forms.Label(); + this.lblColumns = new System.Windows.Forms.Label(); + this.chkShowTileGrid = new System.Windows.Forms.CheckBox(); + this.lblBpp = new System.Windows.Forms.Label(); + this.cboFormat = new System.Windows.Forms.ComboBox(); + this.nudColumns = new Mesen.GUI.Controls.MesenNumericUpDown(); + this.lblOffset = new System.Windows.Forms.Label(); + this.nudOffset = new Mesen.GUI.Controls.MesenNumericUpDown(); + this.ctrlPaletteViewer = new Mesen.GUI.Debugger.ctrlPaletteViewer(); + ((System.ComponentModel.ISupportInitialize)(this.picTilemap)).BeginInit(); + this.pnlTilemap.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // picTilemap + // + this.picTilemap.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; + this.picTilemap.Location = new System.Drawing.Point(0, 0); + this.picTilemap.Name = "picTilemap"; + this.picTilemap.Size = new System.Drawing.Size(512, 512); + this.picTilemap.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.picTilemap.TabIndex = 0; + this.picTilemap.TabStop = false; + this.picTilemap.DoubleClick += new System.EventHandler(this.picTilemap_DoubleClick); + // + // ctrlScanlineCycleSelect + // + this.ctrlScanlineCycleSelect.Dock = System.Windows.Forms.DockStyle.Bottom; + this.ctrlScanlineCycleSelect.Location = new System.Drawing.Point(0, 546); + this.ctrlScanlineCycleSelect.Name = "ctrlScanlineCycleSelect"; + this.ctrlScanlineCycleSelect.Size = new System.Drawing.Size(737, 28); + this.ctrlScanlineCycleSelect.TabIndex = 5; + // + // pnlTilemap + // + this.pnlTilemap.AutoScroll = true; + this.pnlTilemap.Controls.Add(this.picTilemap); + this.pnlTilemap.Dock = System.Windows.Forms.DockStyle.Fill; + this.pnlTilemap.Location = new System.Drawing.Point(3, 3); + this.pnlTilemap.MinimumSize = new System.Drawing.Size(512, 512); + this.pnlTilemap.Name = "pnlTilemap"; + this.pnlTilemap.Size = new System.Drawing.Size(531, 540); + this.pnlTilemap.TabIndex = 6; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.pnlTilemap, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 1, 0); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(737, 546); + this.tableLayoutPanel1.TabIndex = 7; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 2; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.Controls.Add(this.cboMemoryType, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.nudBank, 1, 3); + this.tableLayoutPanel2.Controls.Add(this.lblBank, 0, 3); + this.tableLayoutPanel2.Controls.Add(this.lblSource, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.lblColumns, 0, 2); + this.tableLayoutPanel2.Controls.Add(this.chkShowTileGrid, 0, 5); + this.tableLayoutPanel2.Controls.Add(this.lblBpp, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.cboFormat, 1, 1); + this.tableLayoutPanel2.Controls.Add(this.nudColumns, 1, 2); + this.tableLayoutPanel2.Controls.Add(this.lblOffset, 0, 4); + this.tableLayoutPanel2.Controls.Add(this.nudOffset, 1, 4); + this.tableLayoutPanel2.Controls.Add(this.ctrlPaletteViewer, 0, 6); + this.tableLayoutPanel2.Location = new System.Drawing.Point(540, 3); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 7; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(194, 457); + this.tableLayoutPanel2.TabIndex = 7; + // + // cboMemoryType + // + this.cboMemoryType.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; + this.cboMemoryType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboMemoryType.FormattingEnabled = true; + this.cboMemoryType.Location = new System.Drawing.Point(59, 3); + this.cboMemoryType.Name = "cboMemoryType"; + this.cboMemoryType.Size = new System.Drawing.Size(132, 21); + this.cboMemoryType.TabIndex = 11; + this.cboMemoryType.SelectedIndexChanged += new System.EventHandler(this.cboMemoryType_SelectedIndexChanged); + // + // nudBank + // + this.nudBank.DecimalPlaces = 0; + this.nudBank.Increment = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.nudBank.Location = new System.Drawing.Point(59, 84); + this.nudBank.Maximum = new decimal(new int[] { + 63, + 0, + 0, + 0}); + this.nudBank.MaximumSize = new System.Drawing.Size(10000, 21); + this.nudBank.Minimum = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.nudBank.MinimumSize = new System.Drawing.Size(0, 21); + this.nudBank.Name = "nudBank"; + this.nudBank.Size = new System.Drawing.Size(43, 21); + this.nudBank.TabIndex = 10; + this.nudBank.Value = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.nudBank.ValueChanged += new System.EventHandler(this.nudBank_ValueChanged); + // + // lblBank + // + this.lblBank.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblBank.AutoSize = true; + this.lblBank.Location = new System.Drawing.Point(3, 88); + this.lblBank.Name = "lblBank"; + this.lblBank.Size = new System.Drawing.Size(35, 13); + this.lblBank.TabIndex = 9; + this.lblBank.Text = "Bank:"; + // + // lblSource + // + this.lblSource.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblSource.AutoSize = true; + this.lblSource.Location = new System.Drawing.Point(3, 7); + this.lblSource.Name = "lblSource"; + this.lblSource.Size = new System.Drawing.Size(44, 13); + this.lblSource.TabIndex = 7; + this.lblSource.Text = "Source:"; + // + // lblColumns + // + this.lblColumns.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblColumns.AutoSize = true; + this.lblColumns.Location = new System.Drawing.Point(3, 61); + this.lblColumns.Name = "lblColumns"; + this.lblColumns.Size = new System.Drawing.Size(50, 13); + this.lblColumns.TabIndex = 3; + this.lblColumns.Text = "Columns:"; + // + // chkShowTileGrid + // + this.chkShowTileGrid.AutoSize = true; + this.tableLayoutPanel2.SetColumnSpan(this.chkShowTileGrid, 2); + this.chkShowTileGrid.Location = new System.Drawing.Point(3, 138); + this.chkShowTileGrid.Name = "chkShowTileGrid"; + this.chkShowTileGrid.Size = new System.Drawing.Size(89, 17); + this.chkShowTileGrid.TabIndex = 0; + this.chkShowTileGrid.Text = "Show tile grid"; + this.chkShowTileGrid.UseVisualStyleBackColor = true; + this.chkShowTileGrid.Click += new System.EventHandler(this.chkShowTileGrid_Click); + // + // lblBpp + // + this.lblBpp.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblBpp.AutoSize = true; + this.lblBpp.Location = new System.Drawing.Point(3, 34); + this.lblBpp.Name = "lblBpp"; + this.lblBpp.Size = new System.Drawing.Size(42, 13); + this.lblBpp.TabIndex = 1; + this.lblBpp.Text = "Format:"; + // + // cboFormat + // + this.cboFormat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboFormat.FormattingEnabled = true; + this.cboFormat.Items.AddRange(new object[] { + "2 BPP", + "4 BPP", + "8 BPP", + "8 BPP - Direct Color Mode", + "Mode 7", + "Mode 7 - Direct Color Mode"}); + this.cboFormat.Location = new System.Drawing.Point(59, 30); + this.cboFormat.Name = "cboFormat"; + this.cboFormat.Size = new System.Drawing.Size(132, 21); + this.cboFormat.TabIndex = 2; + this.cboFormat.SelectedIndexChanged += new System.EventHandler(this.cboBpp_SelectedIndexChanged); + // + // nudColumns + // + this.nudColumns.DecimalPlaces = 0; + this.nudColumns.Increment = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.nudColumns.Location = new System.Drawing.Point(59, 57); + this.nudColumns.Maximum = new decimal(new int[] { + 64, + 0, + 0, + 0}); + this.nudColumns.MaximumSize = new System.Drawing.Size(10000, 21); + this.nudColumns.Minimum = new decimal(new int[] { + 8, + 0, + 0, + 0}); + this.nudColumns.MinimumSize = new System.Drawing.Size(0, 21); + this.nudColumns.Name = "nudColumns"; + this.nudColumns.Size = new System.Drawing.Size(43, 21); + this.nudColumns.TabIndex = 4; + this.nudColumns.Value = new decimal(new int[] { + 32, + 0, + 0, + 0}); + this.nudColumns.ValueChanged += new System.EventHandler(this.nudColumns_ValueChanged); + // + // lblOffset + // + this.lblOffset.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblOffset.AutoSize = true; + this.lblOffset.Location = new System.Drawing.Point(3, 115); + this.lblOffset.Name = "lblOffset"; + this.lblOffset.Size = new System.Drawing.Size(38, 13); + this.lblOffset.TabIndex = 5; + this.lblOffset.Text = "Offset:"; + // + // nudOffset + // + this.nudOffset.DecimalPlaces = 0; + this.nudOffset.Increment = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.nudOffset.Location = new System.Drawing.Point(59, 111); + this.nudOffset.Maximum = new decimal(new int[] { + 63, + 0, + 0, + 0}); + this.nudOffset.MaximumSize = new System.Drawing.Size(10000, 21); + this.nudOffset.Minimum = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.nudOffset.MinimumSize = new System.Drawing.Size(0, 21); + this.nudOffset.Name = "nudOffset"; + this.nudOffset.Size = new System.Drawing.Size(43, 21); + this.nudOffset.TabIndex = 6; + this.nudOffset.Value = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.nudOffset.ValueChanged += new System.EventHandler(this.nudOffset_ValueChanged); + // + // ctrlPaletteViewer + // + this.tableLayoutPanel2.SetColumnSpan(this.ctrlPaletteViewer, 2); + this.ctrlPaletteViewer.Location = new System.Drawing.Point(3, 161); + this.ctrlPaletteViewer.Name = "ctrlPaletteViewer"; + this.ctrlPaletteViewer.PaletteScale = 11; + this.ctrlPaletteViewer.SelectionMode = Mesen.GUI.Debugger.PaletteSelectionMode.None; + this.ctrlPaletteViewer.Size = new System.Drawing.Size(176, 176); + this.ctrlPaletteViewer.TabIndex = 12; + // + // frmTileViewer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(737, 574); + this.Controls.Add(this.tableLayoutPanel1); + this.Controls.Add(this.ctrlScanlineCycleSelect); + this.Name = "frmTileViewer"; + this.Text = "Tile Viewer"; + ((System.ComponentModel.ISupportInitialize)(this.picTilemap)).EndInit(); + this.pnlTilemap.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private GUI.Controls.ctrlMesenPictureBox picTilemap; + private Controls.ctrlScanlineCycleSelect ctrlScanlineCycleSelect; + private System.Windows.Forms.Panel pnlTilemap; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.CheckBox chkShowTileGrid; + private GUI.Controls.MesenNumericUpDown nudBank; + private System.Windows.Forms.Label lblBank; + private System.Windows.Forms.Label lblSource; + private System.Windows.Forms.Label lblColumns; + private System.Windows.Forms.Label lblBpp; + private System.Windows.Forms.ComboBox cboFormat; + private GUI.Controls.MesenNumericUpDown nudColumns; + private System.Windows.Forms.Label lblOffset; + private GUI.Controls.MesenNumericUpDown nudOffset; + private Controls.ComboBoxWithSeparator cboMemoryType; + private ctrlPaletteViewer ctrlPaletteViewer; + } +} \ No newline at end of file diff --git a/UI/Debugger/PpuViewer/frmTileViewer.cs b/UI/Debugger/PpuViewer/frmTileViewer.cs new file mode 100644 index 0000000..885e0f8 --- /dev/null +++ b/UI/Debugger/PpuViewer/frmTileViewer.cs @@ -0,0 +1,205 @@ +using Mesen.GUI.Forms; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Drawing.Imaging; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Debugger +{ + public partial class frmTileViewer : BaseForm + { + private NotificationListener _notifListener; + private GetTileViewOptions _options; + private byte[] _tileData; + private Bitmap _tileImage; + private bool _zoomed; + + public frmTileViewer() + { + InitializeComponent(); + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + if(DesignMode) { + return; + } + + _notifListener = new NotificationListener(); + _notifListener.OnNotification += OnNotificationReceived; + + BaseConfigForm.InitializeComboBox(cboFormat, typeof(TileFormat)); + + _options.Format = TileFormat.Bpp4; + _options.Width = 32; + cboFormat.SetEnumValue(TileFormat.Bpp4); + ctrlPaletteViewer.SelectionMode = PaletteSelectionMode.SixteenColors; + + _tileImage = new Bitmap(512, 512, PixelFormat.Format32bppArgb); + picTilemap.Image = _tileImage; + + ctrlScanlineCycleSelect.Initialize(241, 0); + + InitMemoryTypeDropdown(); + + RefreshData(); + RefreshViewer(); + } + + protected override void OnFormClosed(FormClosedEventArgs e) + { + base.OnFormClosed(e); + _notifListener?.Dispose(); + } + + private void OnNotificationReceived(NotificationEventArgs e) + { + switch(e.NotificationType) { + case ConsoleNotificationType.ViewerRefresh: + if(e.Parameter.ToInt32() == ctrlScanlineCycleSelect.ViewerId) { + RefreshData(); + this.BeginInvoke((Action)(() => { + this.RefreshViewer(); + })); + } + break; + } + } + + private int GetBytesPerTile() + { + switch(_options.Format) { + case TileFormat.Bpp2: return 16; + case TileFormat.Bpp4: return 32; + + default: + case TileFormat.Bpp8: return 64; + + case TileFormat.Mode7: + case TileFormat.Mode7DirectColor: return 128; + } + } + + private void RefreshData() + { + _options.Palette = ctrlPaletteViewer.SelectedPalette; + _tileData = DebugApi.GetTileView(_options); + ctrlPaletteViewer.RefreshData(); + } + + private void RefreshViewer() + { + int tileCount = 0x10000 / GetBytesPerTile(); + + int mapWidth = _options.Width * 8; + int mapHeight = tileCount / _options.Width * 8; + + if(_tileImage.Width != mapWidth || _tileImage.Height != mapHeight) { + _tileImage = new Bitmap(mapWidth, mapHeight, PixelFormat.Format32bppArgb); + picTilemap.Image = _tileImage; + } + + using(Graphics g = Graphics.FromImage(_tileImage)) { + GCHandle handle = GCHandle.Alloc(_tileData, GCHandleType.Pinned); + Bitmap source = new Bitmap(mapWidth, mapHeight, 4 * mapWidth, PixelFormat.Format32bppArgb, handle.AddrOfPinnedObject()); + try { + g.DrawImage(source, 0, 0); + } finally { + handle.Free(); + } + } + + UpdateMapSize(); + picTilemap.Invalidate(); + + ctrlPaletteViewer.RefreshViewer(); + } + + private void UpdateMapSize() + { + int tileCount = 0x10000 / GetBytesPerTile(); + int mapWidth = _options.Width * 8; + int mapHeight = tileCount / _options.Width * 8; + + picTilemap.Width = _zoomed ? mapWidth * 2 : mapWidth; + picTilemap.Height = _zoomed ? mapHeight * 2 : mapHeight; + } + + private void InitMemoryTypeDropdown() + { + cboMemoryType.BeginUpdate(); + cboMemoryType.Items.Clear(); + + cboMemoryType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.VideoRam)); + cboMemoryType.Items.Add("-"); + cboMemoryType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.PrgRom)); + cboMemoryType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.WorkRam)); + + cboMemoryType.SelectedIndex = 0; + cboMemoryType.EndUpdate(); + } + + private void picTilemap_DoubleClick(object sender, EventArgs e) + { + _zoomed = !_zoomed; + UpdateMapSize(); + } + + private void chkShowTileGrid_Click(object sender, EventArgs e) + { + _options.ShowTileGrid = chkShowTileGrid.Checked; + } + + private void cboMemoryType_SelectedIndexChanged(object sender, EventArgs e) + { + _options.MemoryType = cboMemoryType.GetEnumValue(); + + bool isVram = _options.MemoryType == SnesMemoryType.VideoRam; + nudOffset.Visible = !isVram; + nudBank.Visible = !isVram; + lblOffset.Visible = !isVram; + lblBank.Visible = !isVram; + if(isVram) { + nudBank.Value = 0; + nudOffset.Value = 0; + } + } + + private void cboBpp_SelectedIndexChanged(object sender, EventArgs e) + { + _options.Format = cboFormat.GetEnumValue(); + if(_options.Format == TileFormat.Bpp2) { + ctrlPaletteViewer.SelectionMode = PaletteSelectionMode.FourColors; + } else if(_options.Format == TileFormat.Bpp4) { + ctrlPaletteViewer.SelectionMode = PaletteSelectionMode.SixteenColors; + } else { + ctrlPaletteViewer.SelectionMode = PaletteSelectionMode.None; + } + + _options.Palette = ctrlPaletteViewer.SelectedPalette; + } + + private void nudColumns_ValueChanged(object sender, EventArgs e) + { + _options.Width = (int)nudColumns.Value; + } + + private void nudBank_ValueChanged(object sender, EventArgs e) + { + _options.AddressOffset = (int)(nudBank.Value * 0x10000 + nudOffset.Value); + } + + private void nudOffset_ValueChanged(object sender, EventArgs e) + { + _options.AddressOffset = (int)(nudBank.Value * 0x10000 + nudOffset.Value); + } + } +} diff --git a/UI/Debugger/PpuViewer/frmTileViewer.resx b/UI/Debugger/PpuViewer/frmTileViewer.resx new file mode 100644 index 0000000..8766f29 --- /dev/null +++ b/UI/Debugger/PpuViewer/frmTileViewer.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/UI/Debugger/PpuViewer/frmTilemapViewer.Designer.cs b/UI/Debugger/PpuViewer/frmTilemapViewer.Designer.cs index ecf74b7..18b351d 100644 --- a/UI/Debugger/PpuViewer/frmTilemapViewer.Designer.cs +++ b/UI/Debugger/PpuViewer/frmTilemapViewer.Designer.cs @@ -51,7 +51,6 @@ // this.picTilemap.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; this.picTilemap.Location = new System.Drawing.Point(0, 0); - this.picTilemap.MinimumSize = new System.Drawing.Size(256, 256); this.picTilemap.Name = "picTilemap"; this.picTilemap.Size = new System.Drawing.Size(512, 512); this.picTilemap.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; diff --git a/UI/Forms/frmMain.Designer.cs b/UI/Forms/frmMain.Designer.cs index 8b5cbc5..3d5d19f 100644 --- a/UI/Forms/frmMain.Designer.cs +++ b/UI/Forms/frmMain.Designer.cs @@ -125,7 +125,11 @@ this.mnuDebugger = new System.Windows.Forms.ToolStripMenuItem(); this.mnuMemoryTools = new System.Windows.Forms.ToolStripMenuItem(); this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem12 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuTileViewer = new System.Windows.Forms.ToolStripMenuItem(); this.mnuTilemapViewer = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPaletteViewer = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem22 = new System.Windows.Forms.ToolStripSeparator(); this.mnuEventViewer = new System.Windows.Forms.ToolStripMenuItem(); this.mnuHelp = new System.Windows.Forms.ToolStripMenuItem(); this.mnuCheckForUpdates = new System.Windows.Forms.ToolStripMenuItem(); @@ -135,9 +139,6 @@ this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem(); this.pnlRenderer = new System.Windows.Forms.Panel(); this.ctrlRecentGames = new Mesen.GUI.Controls.ctrlRecentGames(); - this.mnuPaletteViewer = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem12 = new System.Windows.Forms.ToolStripSeparator(); - this.toolStripMenuItem22 = new System.Windows.Forms.ToolStripSeparator(); this.mnuMain.SuspendLayout(); this.pnlRenderer.SuspendLayout(); this.SuspendLayout(); @@ -852,6 +853,7 @@ this.mnuTraceLogger, this.toolStripMenuItem12, this.mnuTilemapViewer, + this.mnuTileViewer, this.mnuPaletteViewer, this.toolStripMenuItem22, this.mnuEventViewer}); @@ -883,6 +885,19 @@ this.mnuTraceLogger.Text = "Trace Logger"; this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click); // + // toolStripMenuItem12 + // + this.toolStripMenuItem12.Name = "toolStripMenuItem12"; + this.toolStripMenuItem12.Size = new System.Drawing.Size(152, 6); + // + // mnuTileViewer + // + this.mnuTileViewer.Image = global::Mesen.GUI.Properties.Resources.VerticalLayout; + this.mnuTileViewer.Name = "mnuTileViewer"; + this.mnuTileViewer.Size = new System.Drawing.Size(155, 22); + this.mnuTileViewer.Text = "Tile Viewer"; + this.mnuTileViewer.Click += new System.EventHandler(this.mnuTileViewer_Click); + // // mnuTilemapViewer // this.mnuTilemapViewer.Image = global::Mesen.GUI.Properties.Resources.VideoOptions; @@ -891,6 +906,19 @@ this.mnuTilemapViewer.Text = "Tilemap Viewer"; this.mnuTilemapViewer.Click += new System.EventHandler(this.mnuTilemapViewer_Click); // + // mnuPaletteViewer + // + this.mnuPaletteViewer.Image = global::Mesen.GUI.Properties.Resources.VideoFilter; + this.mnuPaletteViewer.Name = "mnuPaletteViewer"; + this.mnuPaletteViewer.Size = new System.Drawing.Size(155, 22); + this.mnuPaletteViewer.Text = "Palette Viewer"; + this.mnuPaletteViewer.Click += new System.EventHandler(this.mnuPaletteViewer_Click); + // + // toolStripMenuItem22 + // + this.toolStripMenuItem22.Name = "toolStripMenuItem22"; + this.toolStripMenuItem22.Size = new System.Drawing.Size(152, 6); + // // mnuEventViewer // this.mnuEventViewer.Image = global::Mesen.GUI.Properties.Resources.NesEventViewer; @@ -965,24 +993,6 @@ this.ctrlRecentGames.TabIndex = 1; this.ctrlRecentGames.Visible = false; // - // mnuPaletteViewer - // - this.mnuPaletteViewer.Image = global::Mesen.GUI.Properties.Resources.VideoFilter; - this.mnuPaletteViewer.Name = "mnuPaletteViewer"; - this.mnuPaletteViewer.Size = new System.Drawing.Size(155, 22); - this.mnuPaletteViewer.Text = "Palette Viewer"; - this.mnuPaletteViewer.Click += new System.EventHandler(this.mnuPaletteViewer_Click); - // - // toolStripMenuItem12 - // - this.toolStripMenuItem12.Name = "toolStripMenuItem12"; - this.toolStripMenuItem12.Size = new System.Drawing.Size(152, 6); - // - // toolStripMenuItem22 - // - this.toolStripMenuItem22.Name = "toolStripMenuItem22"; - this.toolStripMenuItem22.Size = new System.Drawing.Size(152, 6); - // // frmMain // this.AllowDrop = true; @@ -1116,5 +1126,6 @@ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem12; private System.Windows.Forms.ToolStripMenuItem mnuPaletteViewer; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem22; + private System.Windows.Forms.ToolStripMenuItem mnuTileViewer; } } \ No newline at end of file diff --git a/UI/Forms/frmMain.cs b/UI/Forms/frmMain.cs index b13106c..5a35d1d 100644 --- a/UI/Forms/frmMain.cs +++ b/UI/Forms/frmMain.cs @@ -184,6 +184,7 @@ namespace Mesen.GUI.Forms mnuMemoryTools.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenMemoryTools)); mnuEventViewer.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenEventViewer)); mnuTilemapViewer.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenTilemapViewer)); + mnuTileViewer.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenTileViewer)); mnuPaletteViewer.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenPaletteViewer)); mnuTraceLogger.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenTraceLogger)); @@ -292,6 +293,11 @@ namespace Mesen.GUI.Forms DebugWindowManager.OpenDebugWindow(DebugWindow.TilemapViewer); } + private void mnuTileViewer_Click(object sender, EventArgs e) + { + DebugWindowManager.OpenDebugWindow(DebugWindow.TileViewer); + } + private void mnuPaletteViewer_Click(object sender, EventArgs e) { DebugWindowManager.OpenDebugWindow(DebugWindow.PaletteViewer); diff --git a/UI/Interop/DebugApi.cs b/UI/Interop/DebugApi.cs index e778134..3681037 100644 --- a/UI/Interop/DebugApi.cs +++ b/UI/Interop/DebugApi.cs @@ -79,6 +79,14 @@ namespace Mesen.GUI return buffer; } + [DllImport(DllPath, EntryPoint = "GetTileView")] private static extern void GetTileViewWrapper(GetTileViewOptions options, [In, Out] byte[] buffer); + public static byte[] GetTileView(GetTileViewOptions options) + { + byte[] buffer = new byte[512 * 512 * 4]; + DebugApi.GetTileViewWrapper(options, buffer); + return buffer; + } + [DllImport(DllPath)] public static extern void SetViewerUpdateTiming(Int32 viewerId, Int32 scanline, Int32 cycle); [DllImport(DllPath)] private static extern UInt32 GetDebugEventCount([MarshalAs(UnmanagedType.I1)]bool getPreviousFrameData); @@ -286,6 +294,27 @@ namespace Mesen.GUI [MarshalAs(UnmanagedType.I1)] public bool ShowScrollOverlay; } + public struct GetTileViewOptions + { + public TileFormat Format; + public Int32 Width; + public Int32 Palette; + public SnesMemoryType MemoryType; + public Int32 AddressOffset; + + [MarshalAs(UnmanagedType.I1)] public bool ShowTileGrid; + } + + public enum TileFormat + { + Bpp2, + Bpp4, + Bpp8, + DirectColor, + Mode7, + Mode7DirectColor, + } + [Serializable] public struct InteropTraceLoggerOptions { diff --git a/UI/UI.csproj b/UI/UI.csproj index 2ffb82a..976d7eb 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -414,6 +414,12 @@ frmMemoryTools.cs + + UserControl + + + ctrlPaletteViewer.cs + UserControl @@ -426,6 +432,12 @@ frmPaletteViewer.cs + + Form + + + frmTileViewer.cs + Form @@ -712,12 +724,18 @@ frmMemoryTools.cs + + ctrlPaletteViewer.cs + ctrlScanlineCycleSelect.cs frmPaletteViewer.cs + + frmTileViewer.cs + frmTilemapViewer.cs