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