BizHawk/BizHawk.Client.EmuHawk/tools/Genesis/VDPViewer.cs

217 lines
5.4 KiB
C#

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Consoles.Sega.gpgx;
using BizHawk.Common;
namespace BizHawk.Client.EmuHawk
{
public partial class GenVdpViewer : Form, IToolFormAutoConfig
{
[RequiredService]
private GPGX Emu { get; set; }
private GPGX.VDPView _view;
private int _palIndex;
protected override Point ScrollToControl(Control activeControl)
{
// Returning the current location prevents the panel from scrolling to the active control when the panel loses and regains focus
return DisplayRectangle.Location;
}
public GenVdpViewer()
{
InitializeComponent();
bmpViewTiles.ChangeBitmapSize(512, 256);
bmpViewPal.ChangeBitmapSize(16, 4);
}
private static unsafe void DrawTile(int* dest, int pitch, byte* src, int* pal)
{
for (int j = 0; j < 8; j++)
{
*dest++ = pal[*src++];
*dest++ = pal[*src++];
*dest++ = pal[*src++];
*dest++ = pal[*src++];
*dest++ = pal[*src++];
*dest++ = pal[*src++];
*dest++ = pal[*src++];
*dest++ = pal[*src++];
dest += pitch - 8;
}
}
private static unsafe void DrawNameTable(LibGPGX.VDPNameTable nt, ushort* vram, byte* tiles, int* pal, BmpView bv)
{
ushort* nametable = vram + nt.Baseaddr / 2;
int tileW = nt.Width;
int tileH = nt.Height;
Size pixSize = new Size(tileW * 8, tileH * 8);
bv.Size = pixSize;
bv.ChangeBitmapSize(pixSize);
var lockData = bv.BMP.LockBits(new Rectangle(Point.Empty, pixSize), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
int pitch = lockData.Stride / sizeof(int);
int* dest = (int*)lockData.Scan0;
for (int tileY = 0; tileY < tileH; tileY++)
{
for (int tileX = 0; tileX < tileW; tileX++)
{
ushort bgent = *nametable++;
int palidx = bgent >> 9 & 0x30;
int tileent = bgent & 0x1fff; // h and v flip are stored separately in cache
DrawTile(dest, pitch, tiles + tileent * 64, pal + palidx);
dest += 8;
}
dest -= 8 * tileW;
dest += 8 * pitch;
}
bv.BMP.UnlockBits(lockData);
bv.Refresh();
}
unsafe void DrawPalettes(int* pal)
{
var lockData = bmpViewPal.BMP.LockBits(new Rectangle(0, 0, 16, 4), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
int pitch = lockData.Stride / sizeof(int);
int* dest = (int*)lockData.Scan0;
for (int j = 0; j < 4; j++)
{
for (int i = 0; i < 16; i++)
*dest++ = *pal++;
dest += pitch - 16;
}
bmpViewPal.BMP.UnlockBits(lockData);
bmpViewPal.Refresh();
}
unsafe void DrawTiles()
{
var lockData = bmpViewTiles.BMP.LockBits(new Rectangle(0, 0, 512, 256), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
int pitch = lockData.Stride / sizeof(int);
int* dest = (int*)lockData.Scan0;
byte* src = (byte*)_view.PatternCache;
int* pal = 0x10 * _palIndex + (int*)_view.ColorCache;
for (int tile = 0; tile < 2048;)
{
DrawTile(dest, pitch, src, pal);
dest += 8;
src += 64;
tile++;
if ((tile & 63) == 0)
dest += 8 * pitch - 512;
}
bmpViewTiles.BMP.UnlockBits(lockData);
bmpViewTiles.Refresh();
}
public void NewUpdate(ToolFormUpdateType type) { }
public unsafe void UpdateValues()
{
if (Emu == null)
{
return;
}
using ((_view = Emu.UpdateVDPViewContext()).EnterExit())
{
int* pal = (int*)_view.ColorCache;
DrawPalettes(pal);
DrawTiles();
ushort* vramNt = (ushort*)_view.VRAM;
byte* tiles = (byte*)_view.PatternCache;
DrawNameTable(_view.NTA, vramNt, tiles, pal, bmpViewNTA);
DrawNameTable(_view.NTB, vramNt, tiles, pal, bmpViewNTB);
DrawNameTable(_view.NTW, vramNt, tiles, pal, bmpViewNTW);
_view = null;
}
}
public void FastUpdate()
{
// Do nothing
}
public void Restart()
{
UpdateValues();
}
public bool AskSaveChanges() => true;
public bool UpdateBefore => true;
private void bmpViewPal_MouseClick(object sender, MouseEventArgs e)
{
int idx = e.Y / 16;
idx = Math.Min(3, Math.Max(idx, 0));
_palIndex = idx;
UpdateValues();
}
private void VDPViewer_KeyDown(object sender, KeyEventArgs e)
{
if (ModifierKeys.HasFlag(Keys.Control) && e.KeyCode == Keys.C)
{
// find the control under the mouse
Point m = Cursor.Position;
Control top = this;
Control found;
do
{
found = top.GetChildAtPoint(top.PointToClient(m));
top = found;
} while (found != null && found.HasChildren);
if (found is BmpView bv)
{
Clipboard.SetImage(bv.BMP);
}
}
}
private void SaveBGAScreenshotToolStripMenuItem_Click(object sender, EventArgs e)
{
bmpViewNTA.SaveFile();
}
private void SaveBGBScreenshotToolStripMenuItem_Click(object sender, EventArgs e)
{
bmpViewNTB.SaveFile();
}
private void SaveTilesScreenshotToolStripMenuItem_Click(object sender, EventArgs e)
{
bmpViewTiles.SaveFile();
}
private void SaveWindowScreenshotToolStripMenuItem_Click(object sender, EventArgs e)
{
bmpViewNTW.SaveFile();
}
private void SavePaletteScreenshotToolStripMenuItem_Click(object sender, EventArgs e)
{
bmpViewPal.SaveFile();
}
private void CloseMenuItem_Click(object sender, EventArgs e)
{
Close();
}
}
}