diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBA/Meteor.cs b/BizHawk.Emulation/Consoles/Nintendo/GBA/Meteor.cs index 96e8ea0a20..f0bef366ab 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/GBA/Meteor.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/GBA/Meteor.cs @@ -245,6 +245,22 @@ namespace BizHawk.Emulation.Consoles.Nintendo.GBA _MemoryDomains.Add(sb); } + public void GetGPUMemoryAreas(out IntPtr vram, out IntPtr palram, out IntPtr oam, out IntPtr mmio) + { + IntPtr _vram = LibMeteor.libmeteor_getmemoryarea(LibMeteor.MemoryArea.vram); + IntPtr _palram = LibMeteor.libmeteor_getmemoryarea(LibMeteor.MemoryArea.palram); + IntPtr _oam = LibMeteor.libmeteor_getmemoryarea(LibMeteor.MemoryArea.oam); + IntPtr _mmio = LibMeteor.libmeteor_getmemoryarea(LibMeteor.MemoryArea.io); + + if (_vram == IntPtr.Zero || _palram == IntPtr.Zero || _oam == IntPtr.Zero || _mmio == IntPtr.Zero) + throw new Exception("libmeteor_getmemoryarea() failed!"); + + vram = _vram; + palram = _palram; + oam = _oam; + mmio = _mmio; + } + #endregion /// like libsnes, the library is single-instance diff --git a/BizHawk.Emulation/Util.cs b/BizHawk.Emulation/Util.cs index 3499d7d31a..a79cc58410 100644 --- a/BizHawk.Emulation/Util.cs +++ b/BizHawk.Emulation/Util.cs @@ -118,6 +118,10 @@ namespace BizHawk return (b & (1 << index)) != 0; } + public static bool Bit(this ushort b, int index) + { + return (b & (1 << index)) != 0; + } public static string GetPrecedingString(this string str, string value) { diff --git a/BizHawk.MultiClient/BizHawk.MultiClient.csproj b/BizHawk.MultiClient/BizHawk.MultiClient.csproj index f4b2ecff5f..1e02806e28 100644 --- a/BizHawk.MultiClient/BizHawk.MultiClient.csproj +++ b/BizHawk.MultiClient/BizHawk.MultiClient.csproj @@ -226,6 +226,12 @@ GBAGPUView.cs + + Form + + + MobileBmpView.cs + Component @@ -500,6 +506,9 @@ GBAGPUView.cs + + MobileBmpView.cs + CGBColorChooserForm.cs diff --git a/BizHawk.MultiClient/GBAtools/GBAGPUView.Designer.cs b/BizHawk.MultiClient/GBAtools/GBAGPUView.Designer.cs index 817c4f6388..70422c79ae 100644 --- a/BizHawk.MultiClient/GBAtools/GBAGPUView.Designer.cs +++ b/BizHawk.MultiClient/GBAtools/GBAGPUView.Designer.cs @@ -28,26 +28,63 @@ /// private void InitializeComponent() { + this.listBoxWidgets = new System.Windows.Forms.ListBox(); + this.panel1 = new System.Windows.Forms.Panel(); this.label1 = new System.Windows.Forms.Label(); + this.buttonShowWidget = new System.Windows.Forms.Button(); this.SuspendLayout(); // + // listBoxWidgets + // + this.listBoxWidgets.Location = new System.Drawing.Point(12, 25); + this.listBoxWidgets.Name = "listBoxWidgets"; + this.listBoxWidgets.Size = new System.Drawing.Size(120, 160); + this.listBoxWidgets.TabIndex = 0; + this.listBoxWidgets.DoubleClick += new System.EventHandler(this.listBoxWidgets_DoubleClick); + // + // panel1 + // + this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.panel1.Location = new System.Drawing.Point(138, 12); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(486, 381); + this.panel1.TabIndex = 1; + // // label1 // this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(42, 21); + this.label1.Location = new System.Drawing.Point(12, 9); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(115, 13); - this.label1.TabIndex = 0; - this.label1.Text = "Doesn\'t exist yet, sorry!"; + this.label1.Size = new System.Drawing.Size(92, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Available widgets:"; + // + // buttonShowWidget + // + this.buttonShowWidget.Location = new System.Drawing.Point(29, 191); + this.buttonShowWidget.Name = "buttonShowWidget"; + this.buttonShowWidget.Size = new System.Drawing.Size(75, 23); + this.buttonShowWidget.TabIndex = 3; + this.buttonShowWidget.Text = "Show >>"; + this.buttonShowWidget.UseVisualStyleBackColor = true; + this.buttonShowWidget.Click += new System.EventHandler(this.buttonShowWidget_Click); // // GBAGPUView // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(292, 273); + this.ClientSize = new System.Drawing.Size(636, 405); + this.Controls.Add(this.buttonShowWidget); this.Controls.Add(this.label1); + this.Controls.Add(this.panel1); + this.Controls.Add(this.listBoxWidgets); this.Name = "GBAGPUView"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; this.Text = "GBA GPU Viewer"; + this.Load += new System.EventHandler(this.GBAGPUView_Load); this.ResumeLayout(false); this.PerformLayout(); @@ -55,6 +92,10 @@ #endregion + private System.Windows.Forms.ListBox listBoxWidgets; + private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button buttonShowWidget; + } } \ No newline at end of file diff --git a/BizHawk.MultiClient/GBAtools/GBAGPUView.cs b/BizHawk.MultiClient/GBAtools/GBAGPUView.cs index 9796d3d9ba..553ff75cdd 100644 --- a/BizHawk.MultiClient/GBAtools/GBAGPUView.cs +++ b/BizHawk.MultiClient/GBAtools/GBAGPUView.cs @@ -6,20 +6,204 @@ using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; +using BizHawk.MultiClient.GBtools; namespace BizHawk.MultiClient.GBAtools { public partial class GBAGPUView : Form { + Emulation.Consoles.Nintendo.GBA.GBA gba; + + // emulator memory areas + IntPtr vram; + IntPtr oam; + IntPtr mmio; + IntPtr palram; + // color conversion to RGB888 + int[] ColorConversion; + + MobileBmpView bg0, bg1, bg2, bg3; + public GBAGPUView() { InitializeComponent(); + // TODO: hook up something + + // we do this twice to avoid having to & 0x7fff with every color + int[] tmp = Emulation.Consoles.GB.GBColors.GetLut(Emulation.Consoles.GB.GBColors.ColorType.vivid); + ColorConversion = new int[65536]; + Buffer.BlockCopy(tmp, 0, ColorConversion, 0, sizeof(int) * tmp.Length); + Buffer.BlockCopy(tmp, 0, ColorConversion, sizeof(int) * tmp.Length, sizeof(int) * tmp.Length); + /* + for (int i = 1; i <= 2; i++) + { + Form foobar = new Form(); + foobar.Text = "foo" + i; + foobar.TopLevel = false; + //foobar.TopLevelControl = this; + foobar.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow; + //foobar.SetBounds(10, 10, 100, 100); + foobar.Location = new Point(100, 100); + foobar.Size = new System.Drawing.Size(100, 100); + //foobar.BackColor = Color.Black; + Controls.Add(foobar); + + int j = i; + + //foobar.Activated += (o, e) => MessageBox.Show("activated" + j); + //foobar.Enter += (o, e) => MessageBox.Show("enter" + j); + //foobar.Leave += (o, e) => MessageBox.Show("leave" + j); + foobar.MouseEnter += (o, e) => foobar.Activate(); + + foobar.Controls.Add(new Button()); + foobar.Show(); + } + */ + + GenerateWidgets(); + } + + #region drawing primitives + + unsafe void DrawTile16(int* dest, int pitch, byte* tile, ushort *palette, bool hflip, bool vflip) + { + if (vflip) + { + dest += pitch * 7; + pitch = -pitch; + } + for (int y = 0; y < 8; y++) + { + for (int i = 0; i < 4; i++) + { + *dest++ = ColorConversion[palette[*tile & 15]]; + *dest++ = ColorConversion[palette[*tile >> 4]]; + tile++; + } + dest -= 8; + dest += pitch; + } + } + + unsafe void DrawTextNameTable16(int* dest, int pitch, ushort* nametable, byte* tiles) + { + for (int ty = 0; ty < 32; ty++) + { + for (int tx = 0; tx < 32; tx++) + { + ushort ntent = *nametable++; + DrawTile16(dest, pitch, tiles + (ntent & 1023) * 32, (ushort*)palram + (ntent >> 12 << 4), ntent.Bit(10), ntent.Bit(11)); + dest += 8; + } + dest -= 256; + dest += 8 * pitch; + } + } + + unsafe void DrawTextNameTable(int* dest, int pitch, ushort* nametable, byte* tiles, bool eightbit) + { + if (eightbit) + ; + else + DrawTextNameTable16(dest, pitch, nametable, tiles); + } + + unsafe void DrawTextBG(int n, MobileBmpView mbv) + { + ushort bgcnt = ((ushort*)mmio)[4 + n]; + int ssize = bgcnt >> 14; + switch (ssize) + { + case 0: mbv.ChangeAllSizes(256, 256); break; + case 1: mbv.ChangeAllSizes(512, 256); break; + case 2: mbv.ChangeAllSizes(256, 512); break; + case 3: mbv.ChangeAllSizes(512, 512); break; + } + Bitmap bmp = mbv.bmpView.bmp; + var lockdata = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + + int* pixels = (int*)lockdata.Scan0; + int pitch = lockdata.Stride / sizeof(int); + + byte* tiles = (byte*)vram + ((bgcnt & 0xc) << 12); + + ushort *nametable = (ushort*)vram + ((bgcnt & 0x1f00) << 2); + + bool eightbit = bgcnt.Bit(7); + + switch (ssize) + { + case 0: + DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit); + break; + case 1: + DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit); + pixels += 256; + nametable += 1024; + DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit); + break; + case 2: + DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit); + pixels += pitch * 256; + nametable += 1024; + DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit); + break; + case 3: + DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit); + pixels += 256; + nametable += 1024; + DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit); + pixels -= 256; + pixels += pitch * 256; + nametable += 1024; + DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit); + pixels += 256; + nametable += 1024; + DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit); + break; + } + + bmp.UnlockBits(lockdata); + mbv.bmpView.Refresh(); + } + + + #endregion + + MobileBmpView MakeWidget(string text, int w, int h) + { + var mbv = new MobileBmpView(); + mbv.Text = text; + mbv.TopLevel = false; + mbv.ChangeViewSize(w, h); + mbv.bmpView.Clear(); + mbv.FormClosing += delegate(object sender, FormClosingEventArgs e) + { + e.Cancel = true; + listBoxWidgets.Items.Add(sender); + (sender as Form).Hide(); + }; + panel1.Controls.Add(mbv); + listBoxWidgets.Items.Add(mbv); + return mbv; + } + + void GenerateWidgets() + { + listBoxWidgets.BeginUpdate(); + bg0 = MakeWidget("Background 0", 256, 256); + bg1 = MakeWidget("Background 1", 256, 256); + bg2 = MakeWidget("Background 2", 256, 256); + bg3 = MakeWidget("Background 3", 256, 256); + listBoxWidgets.EndUpdate(); } public void Restart() { - if (Global.Emulator is Emulation.Consoles.Nintendo.GBA.GBA) + gba = Global.Emulator as Emulation.Consoles.Nintendo.GBA.GBA; + if (gba != null) { + gba.GetGPUMemoryAreas(out vram, out palram, out oam, out mmio); } else { @@ -28,9 +212,56 @@ namespace BizHawk.MultiClient.GBAtools } } + unsafe void DrawEverything() + { + ushort dispcnt = ((ushort*)mmio)[0]; + + int bgmode = dispcnt & 7; + switch (bgmode) + { + case 0: + if (bg0.ShouldDraw) DrawTextBG(0, bg0); + if (bg1.ShouldDraw) DrawTextBG(1, bg1); + if (bg2.ShouldDraw) DrawTextBG(2, bg2); + if (bg3.ShouldDraw) DrawTextBG(3, bg3); + break; + } + + } + /// belongs in ToolsBefore public void UpdateValues() { + if (!this.IsHandleCreated || this.IsDisposed) + return; + if (gba != null) + { + DrawEverything(); + } + } + + private void GBAGPUView_Load(object sender, EventArgs e) + { + Restart(); + } + + void ShowSelectedWidget() + { + if (listBoxWidgets.SelectedItem != null) + { + (listBoxWidgets.SelectedItem as MobileBmpView).Show(); + listBoxWidgets.Items.RemoveAt(listBoxWidgets.SelectedIndex); + } + } + + private void buttonShowWidget_Click(object sender, EventArgs e) + { + ShowSelectedWidget(); + } + + private void listBoxWidgets_DoubleClick(object sender, EventArgs e) + { + ShowSelectedWidget(); } } } diff --git a/BizHawk.MultiClient/GBAtools/MobileBmpView.Designer.cs b/BizHawk.MultiClient/GBAtools/MobileBmpView.Designer.cs new file mode 100644 index 0000000000..99a564205f --- /dev/null +++ b/BizHawk.MultiClient/GBAtools/MobileBmpView.Designer.cs @@ -0,0 +1,61 @@ +namespace BizHawk.MultiClient.GBAtools +{ + partial class MobileBmpView + { + /// + /// 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.bmpView1 = new BizHawk.MultiClient.GBtools.BmpView(); + this.SuspendLayout(); + // + // bmpView1 + // + this.bmpView1.Location = new System.Drawing.Point(0, 0); + this.bmpView1.Name = "bmpView1"; + this.bmpView1.Size = new System.Drawing.Size(64, 64); + this.bmpView1.TabIndex = 0; + this.bmpView1.Text = "bmpView1"; + // + // MobileBmpView + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(292, 273); + this.Controls.Add(this.bmpView1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "MobileBmpView"; + this.Text = "MobileBmpView"; + this.ResumeLayout(false); + + } + + #endregion + + private GBtools.BmpView bmpView1; + } +} \ No newline at end of file diff --git a/BizHawk.MultiClient/GBAtools/MobileBmpView.cs b/BizHawk.MultiClient/GBAtools/MobileBmpView.cs new file mode 100644 index 0000000000..0267df488f --- /dev/null +++ b/BizHawk.MultiClient/GBAtools/MobileBmpView.cs @@ -0,0 +1,49 @@ +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; + +namespace BizHawk.MultiClient.GBAtools +{ + public partial class MobileBmpView : Form + { + public MobileBmpView() + { + InitializeComponent(); + } + + public GBtools.BmpView bmpView { get { return bmpView1; } } + + [Browsable(false)] + public bool ShouldDraw { get { return this.Visible; } } + + public override string ToString() + { + return Text; + } + + public void ChangeViewSize(Size size) + { + bmpView1.Size = size; + this.ClientSize = size; + } + public void ChangeViewSize(int w, int h) + { + ChangeViewSize(new Size(w, h)); + } + public void ChangeAllSizes(int w, int h) + { + ChangeViewSize(w, h); + bmpView1.ChangeBitmapSize(w, h); + } + public void ChangeAllSizes(Size size) + { + ChangeViewSize(size); + bmpView1.ChangeBitmapSize(size); + } + } +} diff --git a/BizHawk.MultiClient/GBAtools/MobileBmpView.resx b/BizHawk.MultiClient/GBAtools/MobileBmpView.resx new file mode 100644 index 0000000000..29dcb1b3a3 --- /dev/null +++ b/BizHawk.MultiClient/GBAtools/MobileBmpView.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