Initial API Work

This commit is contained in:
upthorn 2018-08-31 21:21:34 -07:00
parent e3736bdaa7
commit cdb205e696
14 changed files with 2263 additions and 2 deletions

View File

@ -64,6 +64,7 @@
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@ -131,6 +132,16 @@
<Compile Include="FrameworkFastZipWriter.cs" />
<Compile Include="FrameworkZipWriter.cs" />
<Compile Include="Global.cs" />
<Compile Include="plugins\PluginLibrary.Emu.cs" />
<Compile Include="plugins\PluginLibrary.Gameinfo.cs" />
<Compile Include="plugins\PluginLibrary.GUIDraw.cs" />
<Compile Include="plugins\PluginLibrary.Joypad.cs" />
<Compile Include="plugins\PluginLibrary.Memory.cs" />
<Compile Include="plugins\PluginLibrary.MemorySavestate.cs" />
<Compile Include="plugins\PluginLibrary.Movie.cs" />
<Compile Include="plugins\PluginLibrary.UserData.cs" />
<Compile Include="plugins\PluginMemoryBase.cs" />
<Compile Include="plugins\PluginBase.cs" />
<Compile Include="inputAdapters\AutoPattern.cs" />
<Compile Include="inputAdapters\BitwiseAdapters.cs" />
<Compile Include="inputAdapters\ClickyVirtualPadController.cs" />

View File

@ -1,9 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.SQLite;
using NLua;
using System.Collections.Generic;
namespace BizHawk.Client.Common
{

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
namespace BizHawk.Client.Common
{
public abstract class PluginBase
{
[RequiredService]
private IEmulator Emulator { get; set; }
[RequiredService]
private IMemoryDomains Domains { get; set; }
[OptionalService]
private IInputPollable InputPollableCore { get; set; }
[OptionalService]
private IDebuggable DebuggableCore { get; set; }
public abstract void PreFrameCallback();
public abstract void PostFrameCallback();
public abstract void SaveStateCallback(string name);
public abstract void LoadStateCallback(string name);
public abstract void InputPollCallback();
protected virtual void AddReadCallback(Action cb, uint address, string domain)
{
if (DebuggableCore.MemoryCallbacksAvailable())
{
DebuggableCore.MemoryCallbacks.Add(new MemoryCallback(domain, MemoryCallbackType.Read, "Lua Hook", cb, address, null));
}
}
protected virtual void AddWriteCallback(Action cb, uint address, string domain)
{
if (DebuggableCore.MemoryCallbacksAvailable())
{
DebuggableCore.MemoryCallbacks.Add(new MemoryCallback(domain, MemoryCallbackType.Write, "Lua Hook", cb, address, null));
}
}
protected virtual void AddExecCallback(Action cb, uint address, string domain)
{
if (DebuggableCore.MemoryCallbacksAvailable() && DebuggableCore.MemoryCallbacks.ExecuteCallbacksAvailable)
{
DebuggableCore.MemoryCallbacks.Add(new MemoryCallback(domain, MemoryCallbackType.Execute, "Lua Hook", cb, address, null));
}
}
protected virtual void RemoveMemoryCallback(Action cb)
{
if (DebuggableCore.MemoryCallbacksAvailable())
{
DebuggableCore.MemoryCallbacks.Remove(cb);
}
}
}
}

View File

@ -0,0 +1,353 @@
using System;
using System.ComponentModel;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
using BizHawk.Emulation.Cores.Nintendo.NES;
using BizHawk.Emulation.Cores.Nintendo.SNES;
using BizHawk.Emulation.Cores.PCEngine;
using BizHawk.Emulation.Cores.Consoles.Sega.gpgx;
using BizHawk.Emulation.Cores.Sega.MasterSystem;
using BizHawk.Emulation.Cores.WonderSwan;
using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES;
namespace BizHawk.Client.Common
{
[Description("A library for interacting with the currently loaded emulator core")]
public sealed class EmulatorPluginLibrary
{
[RequiredService]
private IEmulator Emulator { get; set; }
[OptionalService]
private IDebuggable DebuggableCore { get; set; }
[OptionalService]
private IDisassemblable DisassemblableCore { get; set; }
[OptionalService]
private IMemoryDomains MemoryDomains { get; set; }
[OptionalService]
private IInputPollable InputPollableCore { get; set; }
[OptionalService]
private IRegionable RegionableCore { get; set; }
[OptionalService]
private IBoardInfo BoardInfo { get; set; }
public Action FrameAdvanceCallback { get; set; }
public Action YieldCallback { get; set; }
public EmulatorPluginLibrary()
{ }
public static void DisplayVsync(bool enabled)
{
Global.Config.VSync = enabled;
}
public void FrameAdvance()
{
FrameAdvanceCallback();
}
public int FrameCount()
{
return Emulator.Frame;
}
public object Disassemble(uint pc, string name = "")
{
try
{
if (DisassemblableCore == null)
{
throw new NotImplementedException();
}
MemoryDomain domain = MemoryDomains.SystemBus;
if (!string.IsNullOrEmpty(name))
{
domain = MemoryDomains[name];
}
var d = DisassemblableCore.Disassemble(domain, pc, out int l);
return new { disasm = d, length = l };
}
catch (NotImplementedException)
{
Console.WriteLine($"Error: {Emulator.Attributes().CoreName} does not yet implement disassemble()");
return null;
}
}
public ulong? GetRegister(string name)
{
try
{
if (DebuggableCore == null)
{
throw new NotImplementedException();
}
var registers = DebuggableCore.GetCpuFlagsAndRegisters();
ulong? value = null;
if (registers.ContainsKey(name)) value = registers[name].Value;
return value;
}
catch (NotImplementedException)
{
Console.WriteLine($"Error: {Emulator.Attributes().CoreName} does not yet implement getregister()");
return null;
}
}
public Dictionary<string, ulong> GetRegisters()
{
var table = new Dictionary<string, ulong>();
try
{
if (DebuggableCore == null)
{
throw new NotImplementedException();
}
foreach (var kvp in DebuggableCore.GetCpuFlagsAndRegisters())
{
table[kvp.Key] = kvp.Value.Value;
}
}
catch (NotImplementedException)
{
Console.WriteLine($"Error: {Emulator.Attributes().CoreName} does not yet implement getregisters()");
}
return table;
}
public void SetRegister(string register, int value)
{
try
{
if (DebuggableCore == null)
{
throw new NotImplementedException();
}
DebuggableCore.SetCpuRegister(register, value);
}
catch (NotImplementedException)
{
Console.WriteLine($"Error: {Emulator.Attributes().CoreName} does not yet implement setregister()");
}
}
public long TotalExecutedycles()
{
try
{
if (DebuggableCore == null)
{
throw new NotImplementedException();
}
return DebuggableCore.TotalExecutedCycles;
}
catch (NotImplementedException)
{
Console.WriteLine($"Error: {Emulator.Attributes().CoreName} does not yet implement totalexecutedcycles()");
return 0;
}
}
public static string GetSystemId()
{
return Global.Game.System;
}
public bool IsLagged()
{
if (InputPollableCore != null)
{
return InputPollableCore.IsLagFrame;
}
Console.WriteLine($"Can not get lag information, {Emulator.Attributes().CoreName} does not implement IInputPollable");
return false;
}
public void SetIsLagged(bool value = true)
{
if (InputPollableCore != null)
{
InputPollableCore.IsLagFrame = value;
}
else
{
Console.WriteLine($"Can not set lag information, {Emulator.Attributes().CoreName} does not implement IInputPollable");
}
}
public int LagCount()
{
if (InputPollableCore != null)
{
return InputPollableCore.LagCount;
}
Console.WriteLine($"Can not get lag information, {Emulator.Attributes().CoreName} does not implement IInputPollable");
return 0;
}
public void SetLagCount(int count)
{
if (InputPollableCore != null)
{
InputPollableCore.LagCount = count;
}
else
{
Console.WriteLine($"Can not set lag information, {Emulator.Attributes().CoreName} does not implement IInputPollable");
}
}
public static void LimitFramerate(bool enabled)
{
Global.Config.ClockThrottle = enabled;
}
public static void MinimizeFrameskip(bool enabled)
{
Global.Config.AutoMinimizeSkipping = enabled;
}
public void SetRenderPlanes(params bool[] luaParam)
{
if (Emulator is GPGX)
{
var gpgx = Emulator as GPGX;
var s = gpgx.GetSettings();
s.DrawBGA = luaParam[0];
s.DrawBGB = luaParam[1];
s.DrawBGW = luaParam[2];
s.DrawObj = luaParam[3];
gpgx.PutSettings(s);
}
else if (Emulator is LibsnesCore)
{
var snes = Emulator as LibsnesCore;
var s = snes.GetSettings();
s.ShowBG1_0 = s.ShowBG1_1 = luaParam[0];
s.ShowBG2_0 = s.ShowBG2_1 = luaParam[1];
s.ShowBG3_0 = s.ShowBG3_1 = luaParam[2];
s.ShowBG4_0 = s.ShowBG4_1 = luaParam[3];
s.ShowOBJ_0 = luaParam[4];
s.ShowOBJ_1 = luaParam[5];
s.ShowOBJ_2 = luaParam[6];
s.ShowOBJ_3 = luaParam[7];
snes.PutSettings(s);
}
else if (Emulator is NES)
{
// in the future, we could do something more arbitrary here.
// but this isn't any worse than the old system
var nes = Emulator as NES;
var s = nes.GetSettings();
s.DispSprites = luaParam[0];
s.DispBackground = luaParam[1];
nes.PutSettings(s);
}
else if (Emulator is QuickNES)
{
var quicknes = Emulator as QuickNES;
var s = quicknes.GetSettings();
// this core doesn't support disabling BG
bool showsp = GetSetting(0, luaParam);
if (showsp && s.NumSprites == 0)
{
s.NumSprites = 8;
}
else if (!showsp && s.NumSprites > 0)
{
s.NumSprites = 0;
}
quicknes.PutSettings(s);
}
else if (Emulator is PCEngine)
{
var pce = Emulator as PCEngine;
var s = pce.GetSettings();
s.ShowOBJ1 = GetSetting(0, luaParam);
s.ShowBG1 = GetSetting(1, luaParam);
if (luaParam.Length > 2)
{
s.ShowOBJ2 = GetSetting(2, luaParam);
s.ShowBG2 = GetSetting(3, luaParam);
}
pce.PutSettings(s);
}
else if (Emulator is SMS)
{
var sms = Emulator as SMS;
var s = sms.GetSettings();
s.DispOBJ = GetSetting(0, luaParam);
s.DispBG = GetSetting(1, luaParam);
sms.PutSettings(s);
}
else if (Emulator is WonderSwan)
{
var ws = Emulator as WonderSwan;
var s = ws.GetSettings();
s.EnableSprites = GetSetting(0, luaParam);
s.EnableFG = GetSetting(1, luaParam);
s.EnableBG = GetSetting(2, luaParam);
ws.PutSettings(s);
}
}
private static bool GetSetting(int index, bool[] settings)
{
if (index < settings.Length)
{
return settings[index];
}
return true;
}
public void Yield()
{
YieldCallback();
}
public string GetDisplayType()
{
if (RegionableCore != null)
{
return RegionableCore.Region.ToString();
}
return "";
}
public string GetBoardName()
{
if (BoardInfo != null)
{
return BoardInfo.BoardName;
}
return "";
}
}
}

View File

@ -0,0 +1,498 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public abstract class GUIDrawPluginLibrary
{
[RequiredService]
protected IEmulator Emulator { get; set; }
public GUIDrawPluginLibrary()
{
}
public bool HasGUISurface = false;
protected Color _defaultForeground = Color.White;
protected Color? _defaultBackground;
protected Color? _defaultTextBackground = Color.FromArgb(128, 0, 0, 0);
protected int _defaultPixelFont = 1; // gens
protected Padding _padding = new Padding(0);
#region Gui API
public virtual void Dispose()
{
foreach (var brush in _solidBrushes.Values)
{
brush.Dispose();
}
foreach (var brush in _pens.Values)
{
brush.Dispose();
}
}
public abstract void DrawNew(string name, bool? clear = true);
public abstract void DrawFinish();
#endregion
#region Helpers
protected readonly Dictionary<string, Image> _imageCache = new Dictionary<string, Image>();
protected readonly Dictionary<Color, SolidBrush> _solidBrushes = new Dictionary<Color, SolidBrush>();
protected readonly Dictionary<Color, Pen> _pens = new Dictionary<Color, Pen>();
protected SolidBrush GetBrush(Color color)
{
SolidBrush b;
if (!_solidBrushes.TryGetValue(color, out b))
{
b = new SolidBrush(color);
_solidBrushes[color] = b;
}
return b;
}
protected Pen GetPen(Color color)
{
Pen p;
if (!_pens.TryGetValue(color, out p))
{
p = new Pen(color);
_pens[color] = p;
}
return p;
}
protected abstract Graphics GetGraphics();
public void SetPadding(int all)
{
_padding = new Padding(all);
}
public void SetPadding(int x, int y)
{
_padding = new Padding(x / 2, y / 2, x / 2 + x & 1, y / 2 + y & 1);
}
public void SetPadding(int l,int t,int r, int b)
{
_padding = new Padding(l, t, r, b);
}
public Padding GetPadding()
{
return _padding;
}
#endregion
public abstract void AddMessage(string message);
public abstract void ClearGraphics();
public abstract void ClearText();
public void SetDefaultForegroundColor(Color color)
{
_defaultForeground = color;
}
public void SetDefaultBackgroundColor(Color color)
{
_defaultBackground = color;
}
public void SetDefaultTextBackground(Color color)
{
_defaultTextBackground = color;
}
public void SetDefaultPixelFont(string fontfamily)
{
switch (fontfamily)
{
case "fceux":
case "0":
_defaultPixelFont = 0;
break;
case "gens":
case "1":
_defaultPixelFont = 1;
break;
default:
Console.WriteLine($"Unable to find font family: {fontfamily}");
return;
}
}
public void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = null)
{
using (var g = GetGraphics())
{
try
{
g.DrawBezier(GetPen(color ?? _defaultForeground), p1, p2, p3, p4);
}
catch (Exception)
{
return;
}
}
}
public void DrawBeziers(Point[] points, Color? color = null)
{
using (var g = GetGraphics())
{
try
{
g.DrawBeziers(GetPen(color ?? _defaultForeground), points);
}
catch (Exception)
{
return;
}
}
}
public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null)
{
using (var g = GetGraphics())
{
try
{
if (x < x2)
{
x2 = Math.Abs(x - x2);
}
else
{
x2 = x - x2;
x -= x2;
}
if (y < y2)
{
y2 = Math.Abs(y - y2);
}
else
{
y2 = y - y2;
y -= y2;
}
g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, x2, y2);
var bg = background ?? _defaultBackground;
if (bg.HasValue)
{
g.FillRectangle(GetBrush(bg.Value), x + 1, y + 1, x2 - 1, y2 - 1);
}
}
catch (Exception)
{
// need to stop the script from here
return;
}
}
}
public void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null)
{
using (var g = GetGraphics())
{
try
{
var bg = background ?? _defaultBackground;
if (bg.HasValue)
{
var brush = GetBrush(bg.Value);
g.FillEllipse(brush, x, y, width, height);
}
g.DrawEllipse(GetPen(line ?? _defaultForeground), x, y, width, height);
}
catch (Exception)
{
// need to stop the script from here
return;
}
}
}
public void DrawIcon(string path, int x, int y, int? width = null, int? height = null)
{
using (var g = GetGraphics())
{
try
{
if (!File.Exists(path))
{
AddMessage("File not found: " + path);
return;
}
Icon icon;
if (width.HasValue && height.HasValue)
{
icon = new Icon(path, width.Value, height.Value);
}
else
{
icon = new Icon(path);
}
g.DrawIcon(icon, x, y);
}
catch (Exception)
{
return;
}
}
}
public void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true)
{
if (!File.Exists(path))
{
Console.WriteLine("File not found: " + path);
return;
}
using (var g = GetGraphics())
{
Image img;
if (_imageCache.ContainsKey(path))
{
img = _imageCache[path];
}
else
{
img = Image.FromFile(path);
if (cache)
{
_imageCache.Add(path, img);
}
}
g.DrawImage(img, x, y, width ?? img.Width, height ?? img.Height);
}
}
public void ClearImageCache()
{
foreach (var image in _imageCache)
{
image.Value.Dispose();
}
_imageCache.Clear();
}
public void DrawImageRegion(string path, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null)
{
if (!File.Exists(path))
{
Console.WriteLine("File not found: " + path);
return;
}
using (var g = GetGraphics())
{
Image img;
if (_imageCache.ContainsKey(path))
{
img = _imageCache[path];
}
else
{
img = Image.FromFile(path);
_imageCache.Add(path, img);
}
var destRect = new Rectangle(dest_x, dest_y, dest_width ?? source_width, dest_height ?? source_height);
g.DrawImage(img, destRect, source_x, source_y, source_width, source_height, GraphicsUnit.Pixel);
}
}
public void DrawLine(int x1, int y1, int x2, int y2, Color? color = null)
{
using (var g = GetGraphics())
{
g.DrawLine(GetPen(color ?? _defaultForeground), x1, y1, x2, y2);
}
}
public void DrawAxis(int x, int y, int size, Color? color = null)
{
DrawLine(x + size, y, x - size, y, color ?? _defaultForeground);
DrawLine(x, y + size, x, y - size, color ?? _defaultForeground);
}
public void DrawPie(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null, Color? background = null)
{
using (var g = GetGraphics())
{
var bg = background ?? _defaultBackground;
if (bg.HasValue)
{
var brush = GetBrush(bg.Value);
g.FillPie(brush, x, y, width, height, startangle, sweepangle);
}
g.DrawPie(GetPen(line ?? _defaultForeground), x + 1, y + 1, width - 1, height - 1, startangle, sweepangle);
}
}
public void DrawPixel(int x, int y, Color? color = null)
{
using (var g = GetGraphics())
{
try
{
g.DrawLine(GetPen(color ?? _defaultForeground), x, y, x + 0.1F, y);
}
catch (Exception)
{
return;
}
}
}
public void DrawPolygon(Point[] points, Color? line = null, Color? background = null)
{
using (var g = GetGraphics())
{
try
{
g.DrawPolygon(GetPen(line ?? _defaultForeground), points);
var bg = background ?? _defaultBackground;
if (bg.HasValue)
{
g.FillPolygon(GetBrush(bg.Value), points);
}
}
catch (Exception)
{
return;
}
}
}
public void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null)
{
using (var g = GetGraphics())
{
g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, width, height);
var bg = background ?? _defaultBackground;
if (bg.HasValue)
{
g.FillRectangle(GetBrush(bg.Value), x + 1, y + 1, width - 1, height - 1);
}
}
}
public void DrawString(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, int? fontsize = null,
string fontfamily = null, string fontstyle = null, string horizalign = null, string vertalign = null)
{
using (var g = GetGraphics())
{
try
{
var family = FontFamily.GenericMonospace;
if (fontfamily != null)
{
family = new FontFamily(fontfamily);
}
var fstyle = FontStyle.Regular;
if (fontstyle != null)
{
switch (fontstyle.ToLower())
{
default:
case "regular":
break;
case "bold":
fstyle = FontStyle.Bold;
break;
case "italic":
fstyle = FontStyle.Italic;
break;
case "strikethrough":
fstyle = FontStyle.Strikeout;
break;
case "underline":
fstyle = FontStyle.Underline;
break;
}
}
// The text isn't written out using GenericTypographic, so measuring it using GenericTypographic seemed to make it worse.
// And writing it out with GenericTypographic just made it uglier. :p
var f = new StringFormat(StringFormat.GenericDefault);
var font = new Font(family, fontsize ?? 12, fstyle, GraphicsUnit.Pixel);
Size sizeOfText = g.MeasureString(message, font, 0, f).ToSize();
if (horizalign != null)
{
switch (horizalign.ToLower())
{
default:
case "left":
break;
case "center":
x -= sizeOfText.Width / 2;
break;
case "right":
x -= sizeOfText.Width;
break;
}
}
if (vertalign != null)
{
switch (vertalign.ToLower())
{
default:
case "bottom":
break;
case "middle":
y -= sizeOfText.Height / 2;
break;
case "top":
y -= sizeOfText.Height;
break;
}
}
var bg = backcolor ?? _defaultBackground;
if (bg.HasValue)
{
for (var xd = -1; xd <= 1; xd++)
{
for (var yd = -1; yd <= 1; yd++)
{
g.DrawString(message, font, GetBrush(bg.Value), x+xd, y+yd);
}
}
}
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
g.DrawString(message, font, GetBrush(forecolor ?? _defaultForeground), x, y);
}
catch (Exception)
{
return;
}
}
}
public abstract void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null);
public abstract void Text(int x, int y, string message, Color? forecolor = null, string anchor = null);
}
}

View File

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public sealed class GameInfoPluginLibrary
{
[OptionalService]
private IBoardInfo BoardInfo { get; set; }
public GameInfoPluginLibrary()
{ }
public string GetRomName()
{
if (Global.Game != null)
{
return Global.Game.Name ?? "";
}
return "";
}
public string GetRomHash()
{
if (Global.Game != null)
{
return Global.Game.Hash ?? "";
}
return "";
}
public bool InDatabase()
{
if (Global.Game != null)
{
return !Global.Game.NotInDatabase;
}
return false;
}
public string GetStatus()
{
if (Global.Game != null)
{
return Global.Game.Status.ToString();
}
return "";
}
public bool IsStatusBad()
{
if (Global.Game != null)
{
return Global.Game.IsRomStatusBad();
}
return true;
}
public string GetBoardType()
{
return BoardInfo?.BoardName ?? "";
}
public Dictionary<string, string> GetOptions()
{
var options = new Dictionary<string, string>();
if (Global.Game != null)
{
foreach (var option in Global.Game.GetOptionsDict())
{
options[option.Key] = option.Value;
}
}
return options;
}
}
}

View File

@ -0,0 +1,186 @@
using System;
using System.Collections.Generic;
namespace BizHawk.Client.Common
{
public sealed class JoypadPluginLibrary
{
public JoypadPluginLibrary() { }
public Dictionary<string,dynamic> Get(int? controller = null)
{
var buttons = new Dictionary<string, dynamic>();
var adaptor = Global.AutofireStickyXORAdapter;
foreach (var button in adaptor.Source.Definition.BoolButtons)
{
if (!controller.HasValue)
{
buttons[button] = adaptor.IsPressed(button);
}
else if (button.Length >= 3 && button.Substring(0, 2) == "P" + controller)
{
buttons[button.Substring(3)] = adaptor.IsPressed("P" + controller + " " + button.Substring(3));
}
}
foreach (var button in adaptor.Source.Definition.FloatControls)
{
if (controller == null)
{
buttons[button] = adaptor.GetFloat(button);
}
else if (button.Length >= 3 && button.Substring(0, 2) == "P" + controller)
{
buttons[button.Substring(3)] = adaptor.GetFloat("P" + controller + " " + button.Substring(3));
}
}
buttons["clear"] = null;
buttons["getluafunctionslist"] = null;
buttons["output"] = null;
return buttons;
}
// TODO: what about float controls?
public Dictionary<string, dynamic> GetImmediate()
{
var buttons = new Dictionary<string, dynamic>();
var adaptor = Global.ActiveController;
foreach (var button in adaptor.Definition.BoolButtons)
{
buttons[button] = adaptor.IsPressed(button);
}
foreach (var button in adaptor.Definition.FloatControls)
{
buttons[button] = adaptor.GetFloat(button);
}
return buttons;
}
public void SetFromMnemonicStr(string inputLogEntry)
{
try
{
var lg = Global.MovieSession.MovieControllerInstance();
lg.SetControllersAsMnemonic(inputLogEntry);
foreach (var button in lg.Definition.BoolButtons)
{
Global.LuaAndAdaptor.SetButton(button, lg.IsPressed(button));
}
foreach (var floatButton in lg.Definition.FloatControls)
{
Global.LuaAndAdaptor.SetFloat(floatButton, lg.GetFloat(floatButton));
}
}
catch (Exception)
{
Console.WriteLine("invalid mnemonic string: " + inputLogEntry);
}
}
[LuaMethodExample("joypad.set( { [\"Left\"] = true, [ \"A\" ] = true, [ \"B\" ] = true } );")]
[LuaMethod("set", "sets the given buttons to their provided values for the current frame")]
public void Set(Dictionary<string,bool> buttons, int? controller = null)
{
try
{
foreach (var button in buttons.Keys)
{
var invert = false;
bool? theValue;
var theValueStr = buttons[button].ToString();
if (!string.IsNullOrWhiteSpace(theValueStr))
{
if (theValueStr.ToLower() == "false")
{
theValue = false;
}
else if (theValueStr.ToLower() == "true")
{
theValue = true;
}
else
{
invert = true;
theValue = null;
}
}
else
{
theValue = null;
}
var toPress = button.ToString();
if (controller.HasValue)
{
toPress = "P" + controller + " " + button;
}
if (!invert)
{
if (theValue.HasValue) // Force
{
Global.LuaAndAdaptor.SetButton(toPress, theValue.Value);
Global.ActiveController.Overrides(Global.LuaAndAdaptor);
}
else // Unset
{
Global.LuaAndAdaptor.UnSet(toPress);
Global.ActiveController.Overrides(Global.LuaAndAdaptor);
}
}
else // Inverse
{
Global.LuaAndAdaptor.SetInverse(toPress);
Global.ActiveController.Overrides(Global.LuaAndAdaptor);
}
}
}
catch
{
/*Eat it*/
}
}
[LuaMethodExample("joypad.setanalog( { [ \"Tilt X\" ] = true, [ \"Tilt Y\" ] = false } );")]
[LuaMethod("setanalog", "sets the given analog controls to their provided values for the current frame. Note that unlike set() there is only the logic of overriding with the given value.")]
public void SetAnalog(Dictionary<string,float> controls, object controller = null)
{
try
{
foreach (var name in controls.Keys)
{
var theValueStr = controls[name].ToString();
float? theValue = null;
if (!string.IsNullOrWhiteSpace(theValueStr))
{
float f;
if (float.TryParse(theValueStr, out f))
{
theValue = f;
}
}
if (controller == null)
{
Global.StickyXORAdapter.SetFloat(name.ToString(), theValue);
}
else
{
Global.StickyXORAdapter.SetFloat("P" + controller + " " + name, theValue);
}
}
}
catch
{
/*Eat it*/
}
}
}
}

View File

@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
using BizHawk.Common.BufferExtensions;
namespace BizHawk.Client.Common
{
public sealed class MemoryPluginLibrary : PluginMemoryBase
{
private MemoryDomain _currentMemoryDomain;
private bool _isBigEndian;
public MemoryPluginLibrary(bool Big = false)
: base()
{
_isBigEndian = Big;
}
protected override MemoryDomain Domain
{
get
{
if (MemoryDomainCore != null)
{
if (_currentMemoryDomain == null)
{
_currentMemoryDomain = MemoryDomainCore.HasSystemBus
? MemoryDomainCore.SystemBus
: MemoryDomainCore.MainMemory;
}
return _currentMemoryDomain;
}
var error = $"Error: {Emulator.Attributes().CoreName} does not implement memory domains";
Console.WriteLine(error);
throw new NotImplementedException(error);
}
}
#region Unique Library Methods
public List<string> GetMemoryDomainList()
{
var list = new List<string>();
foreach (var domain in DomainList)
{
list.Add(domain.Name);
}
return list;
}
public uint GetMemoryDomainSize(string name = "")
{
if (string.IsNullOrEmpty(name))
{
return (uint)Domain.Size;
}
return (uint)DomainList[VerifyMemoryDomain(name)].Size;
}
public string GetCurrentMemoryDomain()
{
return Domain.Name;
}
public uint GetCurrentMemoryDomainSize()
{
return (uint)Domain.Size;
}
public bool UseMemoryDomain(string domain)
{
try
{
if (DomainList[domain] != null)
{
_currentMemoryDomain = DomainList[domain];
return true;
}
Console.WriteLine($"Unable to find domain: {domain}");
return false;
}
catch // Just in case
{
Console.WriteLine($"Unable to find domain: {domain}");
}
return false;
}
public string HashRegion(int addr, int count, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
// checks
if (addr < 0 || addr >= d.Size)
{
string error = $"Address {addr} is outside the bounds of domain {d.Name}";
Console.WriteLine(error);
throw new ArgumentOutOfRangeException(error);
}
if (addr + count > d.Size)
{
string error = $"Address {addr} + count {count} is outside the bounds of domain {d.Name}";
Console.WriteLine(error);
throw new ArgumentOutOfRangeException(error);
}
byte[] data = new byte[count];
for (int i = 0; i < count; i++)
{
data[i] = d.PeekByte(addr + i);
}
using (var hasher = System.Security.Cryptography.SHA256.Create())
{
return hasher.ComputeHash(data).BytesToHexString();
}
}
#endregion
#region Endian Handling
private int ReadSigned(int addr, int size, string domain = null)
{
if (_isBigEndian) return ReadSignedBig(addr, size, domain);
else return ReadSignedLittle(addr, size, domain);
}
private uint ReadUnsigned(int addr, int size, string domain = null)
{
if (_isBigEndian) return ReadUnsignedBig(addr, size, domain);
else return ReadUnsignedLittle(addr, size, domain);
}
private void WriteSigned(int addr, int value, int size, string domain = null)
{
if (_isBigEndian) WriteSignedBig(addr, value, size, domain);
else WriteSignedLittle(addr, value, size, domain);
}
private void WriteUnsigned(int addr, uint value, int size, string domain = null)
{
if (_isBigEndian) WriteUnsignedBig(addr, value, size, domain);
else WriteUnsignedLittle(addr, value, size, domain);
}
#endregion
#region Common Special and Legacy Methods
public uint ReadByte(int addr, string domain = null)
{
return ReadUnsignedByte(addr, domain);
}
public void WriteByte(int addr, uint value, string domain = null)
{
WriteUnsignedByte(addr, value, domain);
}
public new List<byte> ReadByteRange(int addr, int length, string domain = null)
{
return base.ReadByteRange(addr, length, domain);
}
public new void WriteByteRange(int addr, List<byte> memoryblock, string domain = null)
{
base.WriteByteRange(addr, memoryblock, domain);
}
public float ReadFloat(int addr, string domain = null)
{
return base.ReadFloat(addr, _isBigEndian, domain);
}
public void WriteFloat(int addr, double value, string domain = null)
{
base.WriteFloat(addr, value, _isBigEndian, domain);
}
#endregion
#region 1 Byte
public int ReadS8(int addr, string domain = null)
{
return (sbyte)ReadUnsignedByte(addr, domain);
}
public void WriteS8(int addr, uint value, string domain = null)
{
WriteUnsignedByte(addr, value, domain);
}
public uint ReadU8(int addr, string domain = null)
{
return ReadUnsignedByte(addr, domain);
}
public void WriteU8(int addr, uint value, string domain = null)
{
WriteUnsignedByte(addr, value, domain);
}
#endregion
#region 2 Byte
public int ReadS16(int addr, string domain = null)
{
return ReadSigned(addr, 2, domain);
}
public void WriteS16(int addr, int value, string domain = null)
{
WriteSigned(addr, value, 2, domain);
}
public uint ReadU16(int addr, string domain = null)
{
return ReadUnsigned(addr, 2, domain);
}
public void WriteU16(int addr, uint value, string domain = null)
{
WriteUnsigned(addr, value, 2, domain);
}
#endregion
#region 3 Byte
public int ReadS24(int addr, string domain = null)
{
return ReadSigned(addr, 3, domain);
}
public void WriteS24(int addr, int value, string domain = null)
{
WriteSigned(addr, value, 3, domain);
}
public uint ReadU24(int addr, string domain = null)
{
return ReadUnsigned(addr, 3, domain);
}
public void WriteU24(int addr, uint value, string domain = null)
{
WriteUnsigned(addr, value, 3, domain);
}
#endregion
#region 4 Byte
public int ReadS32(int addr, string domain = null)
{
return ReadSigned(addr, 4, domain);
}
public void WriteS32(int addr, int value, string domain = null)
{
WriteSigned(addr, value, 4, domain);
}
public uint ReadU32(int addr, string domain = null)
{
return ReadUnsigned(addr, 4, domain);
}
public void WriteU32(int addr, uint value, string domain = null)
{
WriteUnsigned(addr, value, 4, domain);
}
#endregion
}
}

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.IO;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public sealed class MemorySavestatePluginLibrary
{
public MemorySavestatePluginLibrary()
{ }
[RequiredService]
private IStatable StatableCore { get; set; }
private readonly Dictionary<Guid, byte[]> _memorySavestates = new Dictionary<Guid, byte[]>();
public string SaveCoreStateToMemory()
{
var guid = Guid.NewGuid();
var bytes = (byte[])StatableCore.SaveStateBinary().Clone();
_memorySavestates.Add(guid, bytes);
return guid.ToString();
}
public void LoadCoreStateFromMemory(string identifier)
{
var guid = new Guid(identifier);
try
{
var state = _memorySavestates[guid];
using (var ms = new MemoryStream(state))
using (var br = new BinaryReader(ms))
{
StatableCore.LoadStateBinary(br);
}
}
catch
{
Console.WriteLine("Unable to find the given savestate in memory");
}
}
public void DeleteState(string identifier)
{
var guid = new Guid(identifier);
_memorySavestates.Remove(guid);
}
public void ClearInMemoryStates()
{
_memorySavestates.Clear();
}
}
}

View File

@ -0,0 +1,263 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace BizHawk.Client.Common
{
public sealed class MoviePluginLibrary
{
public MoviePluginLibrary()
{ }
[LuaMethodExample("if ( movie.startsfromsavestate( ) ) then\r\n\tconsole.log( \"Returns whether or not the movie is a savestate-anchored movie\" );\r\nend;")]
[LuaMethod("startsfromsavestate", "Returns whether or not the movie is a savestate-anchored movie")]
public bool StartsFromSavestate()
{
return Global.MovieSession.Movie.IsActive && Global.MovieSession.Movie.StartsFromSavestate;
}
[LuaMethodExample("if ( movie.startsfromsaveram( ) ) then\r\n\tconsole.log( \"Returns whether or not the movie is a saveram-anchored movie\" );\r\nend;")]
[LuaMethod("startsfromsaveram", "Returns whether or not the movie is a saveram-anchored movie")]
public bool StartsFromSaveram()
{
return Global.MovieSession.Movie.IsActive && Global.MovieSession.Movie.StartsFromSaveRam;
}
[LuaMethodExample("local stmovfil = movie.filename( );")]
[LuaMethod("filename", "Returns the file name including path of the currently loaded movie")]
public static string Filename()
{
return Global.MovieSession.Movie.Filename;
}
[LuaMethodExample("local nlmovget = movie.getinput( 500 );")]
[LuaMethod("getinput", "Returns a table of buttons pressed on a given frame of the loaded movie")]
public Dictionary<string, dynamic> GetInput(int frame)
{
if (!Global.MovieSession.Movie.IsActive)
{
Console.WriteLine("No movie loaded");
return null;
}
var input = new Dictionary<string, dynamic>();
var adapter = Global.MovieSession.Movie.GetInputState(frame);
if (adapter == null)
{
Console.WriteLine("Can't get input of the last frame of the movie. Use the previous frame");
return null;
}
foreach (var button in adapter.Definition.BoolButtons)
{
input[button] = adapter.IsPressed(button);
}
foreach (var button in adapter.Definition.FloatControls)
{
input[button] = adapter.GetFloat(button);
}
return input;
}
[LuaMethodExample("local stmovget = movie.getinputasmnemonic( 500 );")]
[LuaMethod("getinputasmnemonic", "Returns the input of a given frame of the loaded movie in a raw inputlog string")]
public string GetInputAsMnemonic(int frame)
{
if (Global.MovieSession.Movie.IsActive && frame < Global.MovieSession.Movie.InputLogLength)
{
var lg = Global.MovieSession.LogGeneratorInstance();
lg.SetSource(Global.MovieSession.Movie.GetInputState(frame));
return lg.GenerateLogEntry();
}
return "";
}
[LuaMethodExample("if ( movie.getreadonly( ) ) then\r\n\tconsole.log( \"Returns true if the movie is in read-only mode, false if in read+write\" );\r\nend;")]
[LuaMethod("getreadonly", "Returns true if the movie is in read-only mode, false if in read+write")]
public static bool GetReadOnly()
{
return Global.MovieSession.ReadOnly;
}
[LuaMethodExample("local ulmovget = movie.getrerecordcount();")]
[LuaMethod("getrerecordcount", "Gets the rerecord count of the current movie.")]
public static ulong GetRerecordCount()
{
return Global.MovieSession.Movie.Rerecords;
}
[LuaMethodExample("if ( movie.getrerecordcounting( ) ) then\r\n\tconsole.log( \"Returns whether or not the current movie is incrementing rerecords on loadstate\" );\r\nend;")]
[LuaMethod("getrerecordcounting", "Returns whether or not the current movie is incrementing rerecords on loadstate")]
public static bool GetRerecordCounting()
{
return Global.MovieSession.Movie.IsCountingRerecords;
}
[LuaMethodExample("if ( movie.isloaded( ) ) then\r\n\tconsole.log( \"Returns true if a movie is loaded in memory ( play, record, or finished modes ), false if not ( inactive mode )\" );\r\nend;")]
[LuaMethod("isloaded", "Returns true if a movie is loaded in memory (play, record, or finished modes), false if not (inactive mode)")]
public static bool IsLoaded()
{
return Global.MovieSession.Movie.IsActive;
}
[LuaMethodExample("local domovlen = movie.length( );")]
[LuaMethod("length", "Returns the total number of frames of the loaded movie")]
public static double Length()
{
return Global.MovieSession.Movie.FrameCount;
}
[LuaMethodExample("local stmovmod = movie.mode( );")]
[LuaMethod("mode", "Returns the mode of the current movie. Possible modes: \"PLAY\", \"RECORD\", \"FINISHED\", \"INACTIVE\"")]
public static string Mode()
{
if (Global.MovieSession.Movie.IsFinished)
{
return "FINISHED";
}
if (Global.MovieSession.Movie.IsPlaying)
{
return "PLAY";
}
if (Global.MovieSession.Movie.IsRecording)
{
return "RECORD";
}
return "INACTIVE";
}
[LuaMethodExample("movie.save( \"C:\\moviename.ext\" );")]
[LuaMethod("save", "Saves the current movie to the disc. If the filename is provided (no extension or path needed), the movie is saved under the specified name to the current movie directory. The filename may contain a subdirectory, it will be created if it doesn't exist. Existing files won't get overwritten.")]
public void Save(string filename = "")
{
if (!Global.MovieSession.Movie.IsActive)
{
return;
}
if (!string.IsNullOrEmpty(filename))
{
filename += "." + Global.MovieSession.Movie.PreferredExtension;
var test = new FileInfo(filename);
if (test.Exists)
{
Console.WriteLine($"File {filename} already exists, will not overwrite");
return;
}
Global.MovieSession.Movie.Filename = filename;
}
Global.MovieSession.Movie.Save();
}
[LuaMethodExample("movie.setreadonly( false );")]
[LuaMethod("setreadonly", "Sets the read-only state to the given value. true for read only, false for read+write")]
public static void SetReadOnly(bool readOnly)
{
Global.MovieSession.ReadOnly = readOnly;
}
[LuaMethodExample("movie.setrerecordcount( 20.0 );")]
[LuaMethod("setrerecordcount", "Sets the rerecord count of the current movie.")]
public static void SetRerecordCount(double count)
{
// Lua numbers are always double, integer precision holds up
// to 53 bits, so throw an error if it's bigger than that.
const double PrecisionLimit = 9007199254740992d;
if (count > PrecisionLimit)
{
throw new Exception("Rerecord count exceeds Lua integer precision.");
}
Global.MovieSession.Movie.Rerecords = (ulong)count;
}
[LuaMethodExample("movie.setrerecordcounting( true );")]
[LuaMethod("setrerecordcounting", "Sets whether or not the current movie will increment the rerecord counter on loadstate")]
public static void SetRerecordCounting(bool counting)
{
Global.MovieSession.Movie.IsCountingRerecords = counting;
}
[LuaMethodExample("movie.stop( );")]
[LuaMethod("stop", "Stops the current movie")]
public static void Stop()
{
Global.MovieSession.Movie.Stop();
}
[LuaMethodExample("local domovget = movie.getfps( );")]
[LuaMethod("getfps", "If a movie is loaded, gets the frames per second used by the movie to determine the movie length time")]
public static double GetFps()
{
if (Global.MovieSession.Movie.IsActive)
{
var movie = Global.MovieSession.Movie;
var system = movie.HeaderEntries[HeaderKeys.PLATFORM];
var pal = movie.HeaderEntries.ContainsKey(HeaderKeys.PAL) &&
movie.HeaderEntries[HeaderKeys.PAL] == "1";
return new PlatformFrameRates()[system, pal];
}
return 0.0;
}
[LuaMethodExample("local nlmovget = movie.getheader( );")]
[LuaMethod("getheader", "If a movie is active, will return the movie header as a lua table")]
public Dictionary<string,string> GetHeader()
{
var table = new Dictionary<string,string>();
if (Global.MovieSession.Movie.IsActive)
{
foreach (var kvp in Global.MovieSession.Movie.HeaderEntries)
{
table[kvp.Key] = kvp.Value;
}
}
return table;
}
[LuaMethodExample("local nlmovget = movie.getcomments( );")]
[LuaMethod("getcomments", "If a movie is active, will return the movie comments as a lua table")]
public List<string> GetComments()
{
var list = new List<string>(Global.MovieSession.Movie.Comments.Count);
if (Global.MovieSession.Movie.IsActive)
{
for (int i = 0; i < Global.MovieSession.Movie.Comments.Count; i++)
{
list[i] = Global.MovieSession.Movie.Comments[i];
}
}
return list;
}
[LuaMethodExample("local nlmovget = movie.getsubtitles( );")]
[LuaMethod("getsubtitles", "If a movie is active, will return the movie subtitles as a lua table")]
public List<string> GetSubtitles()
{
var list = new List<string>(Global.MovieSession.Movie.Subtitles.Count);
if (Global.MovieSession.Movie.IsActive)
{
for (int i = 0; i < Global.MovieSession.Movie.Subtitles.Count; i++)
{
list[i] = Global.MovieSession.Movie.Subtitles[i].ToString();
}
}
return list;
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.ComponentModel;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
public sealed class UserDataPluginLibrary
{
public UserDataPluginLibrary()
{ }
public void Set(string name, object value)
{
if (value != null)
{
var t = value.GetType();
if (!t.IsPrimitive && t != typeof(string))
{
throw new InvalidOperationException("Invalid type for userdata");
}
}
Global.UserBag[name] = value;
}
public object Get(string key)
{
if (Global.UserBag.ContainsKey(key))
{
return Global.UserBag[key];
}
return null;
}
public void Clear()
{
Global.UserBag.Clear();
}
public bool Remove(string key)
{
return Global.UserBag.Remove(key);
}
public bool ContainsKey(string key)
{
return Global.UserBag.ContainsKey(key);
}
}
}

View File

@ -0,0 +1,242 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
namespace BizHawk.Client.Common
{
/// <summary>
/// Base class for the Memory and MainMemory plugin libraries
/// </summary>
public abstract class PluginMemoryBase
{
[RequiredService]
protected IEmulator Emulator { get; set; }
[OptionalService]
protected IMemoryDomains MemoryDomainCore { get; set; }
protected abstract MemoryDomain Domain { get; }
protected PluginMemoryBase()
{
}
protected IMemoryDomains DomainList
{
get
{
if (MemoryDomainCore != null)
{
return MemoryDomainCore;
}
var error = $"Error: {Emulator.Attributes().CoreName} does not implement memory domains";
Console.WriteLine(error);
throw new NotImplementedException(error);
}
}
public string VerifyMemoryDomain(string domain)
{
try
{
if (DomainList[domain] == null)
{
Console.WriteLine($"Unable to find domain: {domain}, falling back to current");
return Domain.Name;
}
return domain;
}
catch // Just in case
{
Console.WriteLine($"Unable to find domain: {domain}, falling back to current");
}
return Domain.Name;
}
protected uint ReadUnsignedByte(int addr, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (addr < d.Size)
{
return d.PeekByte(addr);
}
Console.WriteLine("Warning: attempted read of " + addr + " outside the memory size of " + d.Size);
return 0;
}
protected void WriteUnsignedByte(int addr, uint v, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (d.CanPoke())
{
if (addr < d.Size)
{
d.PokeByte(addr, (byte)v);
}
else
{
Console.WriteLine("Warning: attempted write to " + addr + " outside the memory size of " + d.Size);
}
}
else
{
Console.WriteLine($"Error: the domain {d.Name} is not writable");
}
}
protected static int U2S(uint u, int size)
{
var s = (int)u;
s <<= 8 * (4 - size);
s >>= 8 * (4 - size);
return s;
}
protected int ReadSignedLittle(int addr, int size, string domain = null)
{
return U2S(ReadUnsignedLittle(addr, size, domain), size);
}
protected uint ReadUnsignedLittle(int addr, int size, string domain = null)
{
uint v = 0;
for (var i = 0; i < size; ++i)
{
v |= ReadUnsignedByte(addr + i, domain) << (8 * i);
}
return v;
}
protected int ReadSignedBig(int addr, int size, string domain = null)
{
return U2S(ReadUnsignedBig(addr, size, domain), size);
}
protected uint ReadUnsignedBig(int addr, int size, string domain = null)
{
uint v = 0;
for (var i = 0; i < size; ++i)
{
v |= ReadUnsignedByte(addr + i, domain) << (8 * (size - 1 - i));
}
return v;
}
protected void WriteSignedLittle(int addr, int v, int size, string domain = null)
{
WriteUnsignedLittle(addr, (uint)v, size, domain);
}
protected void WriteUnsignedLittle(int addr, uint v, int size, string domain = null)
{
for (var i = 0; i < size; ++i)
{
WriteUnsignedByte(addr + i, (v >> (8 * i)) & 0xFF, domain);
}
}
protected void WriteSignedBig(int addr, int v, int size, string domain = null)
{
WriteUnsignedBig(addr, (uint)v, size, domain);
}
protected void WriteUnsignedBig(int addr, uint v, int size, string domain = null)
{
for (var i = 0; i < size; ++i)
{
WriteUnsignedByte(addr + i, (v >> (8 * (size - 1 - i))) & 0xFF, domain);
}
}
#region public Library implementations
protected List<byte> ReadByteRange(int addr, int length, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
var lastAddr = length + addr;
var list = new List<byte>();
for (; addr <= lastAddr; addr++)
{
if (addr < d.Size)
list.Add(d.PeekByte(addr));
else {
Console.WriteLine("Warning: Attempted read " + addr + " outside memory domain size of " + d.Size + " in readbyterange()");
list.Add(0);
}
}
return list;
}
protected void WriteByteRange(int addr, List<byte> memoryblock, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (d.CanPoke())
{
for (var i = 0; i < memoryblock.Count; i++)
{
if (addr < d.Size)
{
d.PokeByte(addr++, memoryblock[i]);
}
else
{
Console.WriteLine("Warning: Attempted write " + addr + " outside memory domain size of " + d.Size + " in writebyterange()");
}
}
}
else
{
Console.WriteLine($"Error: the domain {d.Name} is not writable");
}
}
protected float ReadFloat(int addr, bool bigendian, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (addr < d.Size)
{
var val = d.PeekUint(addr, bigendian);
var bytes = BitConverter.GetBytes(val);
return BitConverter.ToSingle(bytes, 0);
}
Console.WriteLine("Warning: Attempted read " + addr + " outside memory size of " + d.Size);
return 0;
}
protected void WriteFloat(int addr, double value, bool bigendian, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (d.CanPoke())
{
if (addr < d.Size)
{
var dv = (float)value;
var bytes = BitConverter.GetBytes(dv);
var v = BitConverter.ToUInt32(bytes, 0);
d.PokeUint(addr, v, bigendian);
}
else
{
Console.WriteLine("Warning: Attempted write " + addr + " outside memory size of " + d.Size);
}
}
else
{
Console.WriteLine($"Error: the domain {Domain.Name} is not writable");
}
}
#endregion
}
}

View File

@ -634,6 +634,8 @@
</Compile>
<Compile Include="GLManager.cs" />
<Compile Include="GlobalWin.cs" />
<Compile Include="Plugins\Libraries\GUIDrawLibrary.cs" />
<Compile Include="Plugins\Gen_MD\Ecco2\Ecco2Gui.cs" />
<!--<Compile Include="Input\GamePad.cs" Condition=" '$(OS)' == 'Windows_NT' " />-->
<Compile Include="Input\GamePad.cs" />
<Compile Include="Input\GamePad360.cs" />

View File

@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using BizHawk.Emulation.Common;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
public sealed class GUIDrawLibrary : GUIDrawPluginLibrary
{
public GUIDrawLibrary()
{
}
private DisplaySurface _GUISurface = null;
#region Gui API
public override void DrawNew(string name, bool? clear = true)
{
try
{
DrawFinish();
_GUISurface = GlobalWin.DisplayManager.LockLuaSurface(name, clear ?? true);
HasGUISurface = (_GUISurface != null);
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.ToString());
}
}
public override void DrawFinish()
{
if (_GUISurface != null)
{
GlobalWin.DisplayManager.UnlockLuaSurface(_GUISurface);
}
_GUISurface = null;
HasGUISurface = false;
}
#endregion
#region Helpers
protected override Graphics GetGraphics()
{
var g = _GUISurface == null ? Graphics.FromImage(new Bitmap(1,1)) : _GUISurface.GetGraphics();
// we don't like CoreComm, right? Someone should find a different way to do this then.
var tx = Emulator.CoreComm.ScreenLogicalOffsetX;
var ty = Emulator.CoreComm.ScreenLogicalOffsetY;
if (tx != 0 || ty != 0)
{
var transform = g.Transform;
transform.Translate(-tx, -ty);
g.Transform = transform;
}
return g;
}
#endregion
public override void AddMessage(string message)
{
GlobalWin.OSD.AddMessage(message);
}
public override void ClearGraphics()
{
_GUISurface.Clear();
DrawFinish();
}
public override void ClearText()
{
GlobalWin.OSD.ClearGUIText();
}
public override void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null)
{
using (var g = GetGraphics())
{
try
{
var index = 0;
if (string.IsNullOrEmpty(fontfamily))
{
index = _defaultPixelFont;
}
else
{
switch (fontfamily)
{
case "fceux":
case "0":
index = 0;
break;
case "gens":
case "1":
index = 1;
break;
default:
Console.WriteLine($"Unable to find font family: {fontfamily}");
return;
}
}
var f = new StringFormat(StringFormat.GenericTypographic)
{
FormatFlags = StringFormatFlags.MeasureTrailingSpaces
};
var font = new Font(GlobalWin.DisplayManager.CustomFonts.Families[index], 8, FontStyle.Regular, GraphicsUnit.Pixel);
Size sizeOfText = g.MeasureString(message, font, 0, f).ToSize();
var rect = new Rectangle(new Point(x, y), sizeOfText + new Size(1, 0));
g.FillRectangle(GetBrush(backcolor ?? _defaultTextBackground.Value), rect);
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
g.DrawString(message, font, GetBrush(forecolor ?? _defaultForeground), x, y);
}
catch (Exception)
{
return;
}
}
}
public override void Text(int x, int y, string message, Color? forecolor = null, string anchor = null)
{
var a = 0;
if (!string.IsNullOrEmpty(anchor))
{
switch (anchor)
{
case "0":
case "topleft":
a = 0;
break;
case "1":
case "topright":
a = 1;
break;
case "2":
case "bottomleft":
a = 2;
break;
case "3":
case "bottomright":
a = 3;
break;
}
}
else
{
x -= Emulator.CoreComm.ScreenLogicalOffsetX;
y -= Emulator.CoreComm.ScreenLogicalOffsetY;
}
GlobalWin.OSD.AddGUIText(message, x, y, Color.Black, forecolor ?? Color.White, a);
}
}
}