2019-03-23 15:39:21 -04:00
|
|
|
|
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
|
|
|
|
|
{
|
2019-04-21 20:18:32 -04:00
|
|
|
|
public partial class ctrlPaletteViewer : PictureBox
|
2019-03-23 15:39:21 -04:00
|
|
|
|
{
|
2019-03-30 13:42:44 -04:00
|
|
|
|
public delegate void SelectionChangedHandler();
|
|
|
|
|
public event SelectionChangedHandler SelectionChanged;
|
|
|
|
|
|
2019-03-23 15:39:21 -04:00
|
|
|
|
private byte[] _cgRam;
|
|
|
|
|
private Bitmap _paletteImage;
|
2019-04-21 20:18:32 -04:00
|
|
|
|
private int _selectedPalette = 0;
|
|
|
|
|
private PaletteSelectionMode _selectionMode = PaletteSelectionMode.None;
|
2019-05-05 00:36:15 -04:00
|
|
|
|
private int _paletteScale = 16;
|
2019-03-23 15:39:21 -04:00
|
|
|
|
|
|
|
|
|
public ctrlPaletteViewer()
|
|
|
|
|
{
|
2019-04-21 20:18:32 -04:00
|
|
|
|
this.SetStyle(ControlStyles.Selectable, true);
|
2019-05-05 00:36:15 -04:00
|
|
|
|
this.PaletteScale = 16;
|
|
|
|
|
}
|
2019-03-23 15:39:21 -04:00
|
|
|
|
|
2019-05-05 00:36:15 -04:00
|
|
|
|
public int PaletteScale
|
|
|
|
|
{
|
|
|
|
|
get { return _paletteScale; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_paletteScale = value;
|
2019-06-24 11:45:40 -04:00
|
|
|
|
_paletteImage = new Bitmap(PaletteScale * 16, PaletteScale * 16, PixelFormat.Format32bppPArgb);
|
2019-05-05 00:36:15 -04:00
|
|
|
|
}
|
2019-04-21 20:18:32 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int SelectedPalette
|
|
|
|
|
{
|
|
|
|
|
get { return _selectedPalette; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
int maxPalette = 0;
|
|
|
|
|
switch(this.SelectionMode) {
|
|
|
|
|
case PaletteSelectionMode.SingleColor: maxPalette = 255; break;
|
|
|
|
|
case PaletteSelectionMode.FourColors: maxPalette = 63; break;
|
|
|
|
|
case PaletteSelectionMode.SixteenColors: maxPalette = 15; break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int newPalette = Math.Max(0, Math.Min(value, maxPalette));
|
|
|
|
|
|
|
|
|
|
if(newPalette != _selectedPalette) {
|
|
|
|
|
_selectedPalette = newPalette;
|
|
|
|
|
SelectionChanged?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public PaletteSelectionMode SelectionMode
|
|
|
|
|
{
|
|
|
|
|
get { return _selectionMode; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_selectionMode = value;
|
|
|
|
|
this.SelectedPalette = _selectedPalette;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
|
|
|
|
|
{
|
|
|
|
|
switch(keyData) {
|
|
|
|
|
case Keys.Left:
|
|
|
|
|
this.SelectedPalette--;
|
|
|
|
|
RefreshViewer();
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case Keys.Right:
|
|
|
|
|
this.SelectedPalette++;
|
|
|
|
|
RefreshViewer();
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case Keys.Down:
|
|
|
|
|
switch(this.SelectionMode) {
|
|
|
|
|
case PaletteSelectionMode.SingleColor: this.SelectedPalette+=16; break;
|
|
|
|
|
case PaletteSelectionMode.FourColors: this.SelectedPalette+=4; break;
|
|
|
|
|
case PaletteSelectionMode.SixteenColors: this.SelectedPalette++; break;
|
|
|
|
|
}
|
|
|
|
|
RefreshViewer();
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case Keys.Up:
|
|
|
|
|
switch(this.SelectionMode) {
|
|
|
|
|
case PaletteSelectionMode.SingleColor: this.SelectedPalette-=16; break;
|
|
|
|
|
case PaletteSelectionMode.FourColors: this.SelectedPalette-=4; break;
|
|
|
|
|
case PaletteSelectionMode.SixteenColors: this.SelectedPalette--; break;
|
|
|
|
|
}
|
|
|
|
|
RefreshViewer();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return base.ProcessCmdKey(ref msg, keyData);
|
2019-03-23 15:39:21 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-30 13:42:44 -04:00
|
|
|
|
public byte[] CgRam
|
|
|
|
|
{
|
|
|
|
|
get { return _cgRam; }
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-23 15:39:21 -04:00
|
|
|
|
public void RefreshData()
|
|
|
|
|
{
|
2020-05-18 16:10:53 -04:00
|
|
|
|
if(EmuApi.GetRomInfo().CoprocessorType == CoprocessorType.Gameboy) {
|
|
|
|
|
_cgRam = GetGameboyPalette();
|
|
|
|
|
} else {
|
|
|
|
|
_cgRam = DebugApi.GetMemoryState(SnesMemoryType.CGRam);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static byte[] GetGameboyPalette()
|
|
|
|
|
{
|
|
|
|
|
byte[] cgRam = new byte[512];
|
|
|
|
|
|
|
|
|
|
//Generate a fake SNES-like palette based on the gameboy PPU state
|
2020-05-19 21:31:33 -04:00
|
|
|
|
GbState state = DebugApi.GetState().Gameboy;
|
|
|
|
|
GbPpuState ppu = state.Ppu;
|
2020-05-18 16:10:53 -04:00
|
|
|
|
|
2020-05-19 21:31:33 -04:00
|
|
|
|
if(state.Type == GbType.Cgb) {
|
|
|
|
|
for(int i = 0; i < 8 * 4; i++) {
|
|
|
|
|
cgRam[i * 2] = (byte)(ppu.CgbBgPalettes[i] & 0xFF);
|
|
|
|
|
cgRam[i * 2 + 1] = (byte)(ppu.CgbBgPalettes[i] >> 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < 8 * 4; i++) {
|
|
|
|
|
cgRam[128 + i * 2] = (byte)(ppu.CgbObjPalettes[i] & 0xFF);
|
|
|
|
|
cgRam[128 + i * 2 + 1] = (byte)(ppu.CgbObjPalettes[i] >> 8);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
byte[,] paletteBytes = new byte[4,2] {
|
|
|
|
|
{ 0xFF, 0x7F}, {0x18,0x63}, {0x8C, 0x31}, {0,0}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Action<byte, UInt16> setPalette = (byte pal, UInt16 offset) => {
|
|
|
|
|
cgRam[offset] = paletteBytes[pal & 0x03, 0];
|
|
|
|
|
cgRam[offset+1] = paletteBytes[pal & 0x03, 1];
|
|
|
|
|
cgRam[offset+2] = paletteBytes[(pal >> 2) & 0x03, 0];
|
|
|
|
|
cgRam[offset+3] = paletteBytes[(pal >> 2) & 0x03, 1];
|
|
|
|
|
cgRam[offset+4] = paletteBytes[(pal >> 4) & 0x03, 0];
|
|
|
|
|
cgRam[offset+5] = paletteBytes[(pal >> 4) & 0x03, 1];
|
|
|
|
|
cgRam[offset+6] = paletteBytes[(pal >> 6) & 0x03, 0];
|
|
|
|
|
cgRam[offset+7] = paletteBytes[(pal >> 6) & 0x03, 1];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
setPalette(ppu.BgPalette, 0);
|
|
|
|
|
setPalette(ppu.ObjPalette0, 32);
|
|
|
|
|
setPalette(ppu.ObjPalette1, 64);
|
|
|
|
|
setPalette(0xE4, 96);
|
|
|
|
|
}
|
2020-05-18 16:10:53 -04:00
|
|
|
|
return cgRam;
|
2019-03-23 15:39:21 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-30 13:42:44 -04:00
|
|
|
|
private uint To8Bit(int color)
|
|
|
|
|
{
|
|
|
|
|
return (uint)((color << 3) + (color >> 2));
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-21 20:18:32 -04:00
|
|
|
|
public uint ToArgb(int rgb555)
|
|
|
|
|
{
|
2019-03-30 13:42:44 -04:00
|
|
|
|
uint b = To8Bit(rgb555 >> 10);
|
|
|
|
|
uint g = To8Bit((rgb555 >> 5) & 0x1F);
|
|
|
|
|
uint r = To8Bit(rgb555 & 0x1F);
|
|
|
|
|
|
|
|
|
|
return (0xFF000000 | (r << 16) | (g << 8) | b);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-23 15:39:21 -04:00
|
|
|
|
public void RefreshViewer()
|
|
|
|
|
{
|
|
|
|
|
UInt32[] argbPalette = new UInt32[256];
|
|
|
|
|
for(int i = 0; i < 256; i++) {
|
2019-03-30 13:42:44 -04:00
|
|
|
|
argbPalette[i] = ToArgb(_cgRam[i * 2] | _cgRam[i * 2 + 1] << 8);
|
2019-03-23 15:39:21 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using(Graphics g = Graphics.FromImage(_paletteImage)) {
|
|
|
|
|
GCHandle handle = GCHandle.Alloc(argbPalette, GCHandleType.Pinned);
|
2019-06-24 11:45:40 -04:00
|
|
|
|
Bitmap source = new Bitmap(16, 16, 16 * 4, PixelFormat.Format32bppPArgb, handle.AddrOfPinnedObject());
|
2019-03-23 15:39:21 -04:00
|
|
|
|
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;
|
2019-03-30 13:42:44 -04:00
|
|
|
|
if(SelectionMode == PaletteSelectionMode.SingleColor) {
|
|
|
|
|
g.DrawRectangle(pen, (SelectedPalette & 0x0F) * PaletteScale, (SelectedPalette / 16) * PaletteScale, PaletteScale, PaletteScale);
|
|
|
|
|
} else if(SelectionMode == PaletteSelectionMode.FourColors) {
|
2019-03-23 15:39:21 -04:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-05 00:36:15 -04:00
|
|
|
|
this.Image = _paletteImage;
|
2019-04-21 20:18:32 -04:00
|
|
|
|
this.Size = _paletteImage.Size;
|
|
|
|
|
this.Invalidate();
|
2019-03-23 15:39:21 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-21 20:18:32 -04:00
|
|
|
|
protected override void OnMouseClick(MouseEventArgs e)
|
2019-03-23 15:39:21 -04:00
|
|
|
|
{
|
2019-04-21 20:18:32 -04:00
|
|
|
|
base.OnMouseClick(e);
|
|
|
|
|
|
2019-03-23 15:39:21 -04:00
|
|
|
|
int paletteIndex = 0;
|
2019-03-30 13:42:44 -04:00
|
|
|
|
if(SelectionMode == PaletteSelectionMode.SingleColor) {
|
|
|
|
|
paletteIndex = (e.Y / PaletteScale) * 16 + (e.X / PaletteScale);
|
|
|
|
|
} else if(SelectionMode == PaletteSelectionMode.FourColors) {
|
2019-03-23 15:39:21 -04:00
|
|
|
|
paletteIndex += e.X / (4 * PaletteScale);
|
|
|
|
|
paletteIndex += (e.Y / PaletteScale) * 4;
|
|
|
|
|
} else if(SelectionMode == PaletteSelectionMode.SixteenColors) {
|
|
|
|
|
paletteIndex = (e.Y / PaletteScale);
|
|
|
|
|
}
|
|
|
|
|
SelectedPalette = paletteIndex;
|
2019-03-30 13:42:44 -04:00
|
|
|
|
|
|
|
|
|
RefreshViewer();
|
2019-04-21 20:18:32 -04:00
|
|
|
|
|
|
|
|
|
this.Focus();
|
2019-03-23 15:39:21 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum PaletteSelectionMode
|
|
|
|
|
{
|
|
|
|
|
None,
|
2019-03-30 13:42:44 -04:00
|
|
|
|
SingleColor,
|
2019-03-23 15:39:21 -04:00
|
|
|
|
FourColors,
|
|
|
|
|
SixteenColors
|
|
|
|
|
}
|
|
|
|
|
}
|