using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using BizHawk.Emulation.Cores.Sega.MasterSystem; using BizHawk.Common; using BizHawk.Client.Common; using System.Drawing.Imaging; namespace BizHawk.Client.EmuHawk { public partial class SmsVDPViewer : Form, IToolForm { private VDP vdp; int palindex = 0; public SmsVDPViewer() { InitializeComponent(); bmpViewTiles.ChangeBitmapSize(256, 128); bmpViewPalette.ChangeBitmapSize(16, 2); bmpViewBG.ChangeBitmapSize(256, 256); Restart(); } unsafe static void Draw8x8(byte* src, int* dest, int pitch, int* pal) { int inc = pitch - 8; dest -= inc; for (int i = 0; i < 64; i++) { if ((i & 7) == 0) dest += inc; *dest++ = pal[*src++]; } } unsafe static void Draw8x8hv(byte* src, int* dest, int pitch, int* pal, bool hflip, bool vflip) { int incx = hflip ? -1 : 1; int incy = vflip ? -pitch : pitch; if (hflip) dest -= incx * 7; if (vflip) dest -= incy * 7; incy -= incx * 8; for (int j = 0; j < 8; j++) { for (int i = 0; i < 8; i++) { *dest = pal[*src++]; dest += incx; } dest += incy; } } unsafe void DrawTiles(int *pal) { var lockdata = bmpViewTiles.bmp.LockBits(new Rectangle(0, 0, 256, 128), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); int* dest = (int*)lockdata.Scan0; int pitch = lockdata.Stride / sizeof(int); fixed (byte* src = vdp.PatternBuffer) { for (int tile = 0; tile < 512; tile++) { int srcaddr = tile * 64; int tx = tile & 31; int ty = tile >> 5; int destaddr = ty * 8 * pitch + tx * 8; Draw8x8(src + srcaddr, dest + destaddr, pitch, pal); } } bmpViewTiles.bmp.UnlockBits(lockdata); bmpViewTiles.Refresh(); } unsafe void DrawBG(int* pal) { int bgheight = vdp.FrameHeight == 192 ? 224 : 256; int maxtile = bgheight * 4; if (bgheight != bmpViewBG.bmp.Height) { bmpViewBG.Height = bgheight; bmpViewBG.ChangeBitmapSize(256, bgheight); } var lockdata = bmpViewBG.bmp.LockBits(new Rectangle(0, 0, 256, bgheight), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); int* dest = (int*)lockdata.Scan0; int pitch = lockdata.Stride / sizeof(int); fixed (byte* src = vdp.PatternBuffer) fixed (byte* vram = vdp.VRAM) { short* map = (short*)(vram + vdp.CalcNameTableBase()); for (int tile = 0; tile < maxtile; tile++) { short bgent = *map++; bool hflip = (bgent & 1 << 9) != 0; bool vflip = (bgent & 1 << 10) != 0; int* tpal = pal + ((bgent & 1 << 11) >> 7); int srcaddr = (bgent & 511) * 64; int tx = tile & 31; int ty = tile >> 5; int destaddr = ty * 8 * pitch + tx * 8; Draw8x8hv(src + srcaddr, dest + destaddr, pitch, tpal, hflip, vflip); } } bmpViewBG.bmp.UnlockBits(lockdata); bmpViewBG.Refresh(); } unsafe void DrawPal(int* pal) { var lockdata = bmpViewPalette.bmp.LockBits(new Rectangle(0, 0, 16, 2), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); int* dest = (int*)lockdata.Scan0; int pitch = lockdata.Stride / sizeof(int); for (int j = 0; j < 2; j++) { for (int i = 0; i < 16; i++) { *dest++ = *pal++; } dest -= 16; dest += pitch; } bmpViewPalette.bmp.UnlockBits(lockdata); bmpViewPalette.Refresh(); } public void UpdateValues() { unsafe { fixed (int* pal = vdp.Palette) { DrawTiles(pal + palindex * 16); DrawBG(pal); DrawPal(pal); } } } public void Restart() { if (!(Global.Emulator is SMS)) { Close(); return; } vdp = (Global.Emulator as SMS).Vdp; UpdateValues(); } public bool AskSave() { return true; } public bool UpdateBefore { get { return true; } } private void bmpViewPalette_MouseClick(object sender, MouseEventArgs e) { int p = Math.Min(Math.Max(e.Y / 16, 0), 1); palindex = p; unsafe { fixed (int* pal = vdp.Palette) { DrawTiles(pal + palindex * 16); } } } 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) { var bv = found as BmpView; Clipboard.SetImage(bv.bmp); } } } private void RefreshFloatingWindowControl() { Owner = Global.Config.SmsVdpSettings.FloatingWindow ? null : GlobalWin.MainForm; } protected override void OnShown(EventArgs e) { RefreshFloatingWindowControl(); base.OnShown(e); } private void CloseMenuItem_Click(object sender, EventArgs e) { Close(); } private void OptionsSubMenu_DropDownOpened(object sender, EventArgs e) { AutoloadMenuItem.Checked = Global.Config.SmsVdpAutoLoad; SaveWindowPositionMenuItem.Checked = Global.Config.SmsVdpSettings.SaveWindowPosition; AlwaysOnTopMenuItem.Checked = Global.Config.SmsVdpSettings.TopMost; FloatingWindowMenuItem.Checked = Global.Config.SmsVdpSettings.FloatingWindow; } private void SaveWindowPositionMenuItem_Click(object sender, EventArgs e) { Global.Config.SmsVdpSettings.SaveWindowPosition ^= true; } private void AlwaysOnTopMenuItem_Click(object sender, EventArgs e) { TopMost = Global.Config.SmsVdpSettings.TopMost ^= true; } private void FloatingWindowMenuItem_Click(object sender, EventArgs e) { Global.Config.SmsVdpSettings.FloatingWindow ^= true; RefreshFloatingWindowControl(); } private void VDPViewer_Load(object sender, EventArgs e) { TopMost = Global.Config.SmsVdpSettings.TopMost; if (Global.Config.SmsVdpSettings.UseWindowPosition) { Location = Global.Config.SmsVdpSettings.WindowPosition; } } private void AutoloadMenuItem_Click(object sender, EventArgs e) { Global.Config.SmsVdpAutoLoad ^= true; } private void saveTilesScreenshotToolStripMenuItem_Click(object sender, EventArgs e) { bmpViewTiles.SaveFile(); } private void savePalettesScrenshotToolStripMenuItem_Click(object sender, EventArgs e) { bmpViewPalette.SaveFile(); } private void saveBGScreenshotToolStripMenuItem_Click(object sender, EventArgs e) { bmpViewBG.SaveFile(); } } }