Merge pull request from upthorn/master

Extend APIHawk with a lua-like system of api libraries
This commit is contained in:
adelikat 2019-01-19 17:24:08 -06:00 committed by GitHub
commit 5b26300e50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 4493 additions and 42 deletions

1
.gitignore vendored
View File

@ -73,3 +73,4 @@ ExternalCoreProjects/Virtu/bin/*.*
libsnes/vs2015/libsnes.VC.db
waterbox/**/*.wbx
waterbox/**/*.wbx.in
/BizHawkTool_template.zip

View File

@ -37,6 +37,10 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data.SQLite, Version=1.0.105.2, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Users\uptho\Documents\BizHawk-2.3\dll\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
@ -48,6 +52,19 @@
<ItemGroup>
<Compile Include="Attributes\BizHawkExternalToolUsageAttribute.cs" />
<Compile Include="Attributes\BizHawkExternalToolAttribute.cs" />
<Compile Include="Classes\ApiInjector.cs" />
<Compile Include="Classes\Api\EmuApi.cs" />
<Compile Include="Classes\Api\GameInfoApi.cs" />
<Compile Include="Classes\Api\MemApi.cs" />
<Compile Include="Classes\Api\MemApiBase.cs" />
<Compile Include="Classes\Api\PluginBase.cs" />
<Compile Include="Classes\Api\JoypadApi.cs" />
<Compile Include="Classes\Api\MemEventsApi.cs" />
<Compile Include="Classes\Api\MemorySaveStateApi.cs" />
<Compile Include="Classes\Api\MovieApi.cs" />
<Compile Include="Classes\Api\SqlApi.cs" />
<Compile Include="Classes\Api\UserDataApi.cs" />
<Compile Include="Classes\BasicApiProvider.cs" />
<Compile Include="Classes\BizHawkSystemIdToCoreSystemEnumConverter.cs" />
<Compile Include="Classes\Events\EventArgs\BeforeQuickLoadEventArgs.cs" />
<Compile Include="Classes\Events\EventArgs\BeforeQuickSaveEventArgs.cs" />
@ -62,7 +79,25 @@
<Compile Include="Enums\BizHawkExternalToolUsage.cs" />
<Compile Include="Classes\ClientApi.cs" />
<Compile Include="Classes\ExternalToolManager.cs" />
<Compile Include="Interfaces\Api\IComm.cs" />
<Compile Include="Interfaces\Api\IInput.cs" />
<Compile Include="Interfaces\Api\ITool.cs" />
<Compile Include="Interfaces\Api\ISaveState.cs" />
<Compile Include="Interfaces\Api\IUserData.cs" />
<Compile Include="Interfaces\Api\ISql.cs" />
<Compile Include="Interfaces\Api\IMovie.cs" />
<Compile Include="Interfaces\Api\IMemorySavestate.cs" />
<Compile Include="Interfaces\Api\IMemEvents.cs" />
<Compile Include="Interfaces\Api\IEmu.cs" />
<Compile Include="Interfaces\Api\IExternalApi.cs" />
<Compile Include="Interfaces\Api\IJoypad.cs" />
<Compile Include="Interfaces\IExternalApiProvider.cs" />
<Compile Include="Interfaces\IExternalToolForm.cs" />
<Compile Include="Interfaces\Api\IGameInfo.cs" />
<Compile Include="Interfaces\Api\IGui.cs" />
<Compile Include="Interfaces\Api\IMem.cs" />
<Compile Include="Interfaces\IPlugin.cs" />
<Compile Include="Interfaces\Api\IApiContainer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
@ -95,4 +130,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View File

@ -0,0 +1,459 @@
using System;
using System.ComponentModel;
using System.Collections.Generic;
using BizHawk.Client.Common;
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.ApiHawk
{
[Description("A library for interacting with the currently loaded emulator core")]
public sealed class EmuApi : IEmu
{
private static class EmuStatic
{
public static void DisplayVsync(bool enabled)
{
Global.Config.VSync = enabled;
}
public static string GetSystemId()
{
return Global.Game.System;
}
public static void LimitFramerate(bool enabled)
{
Global.Config.ClockThrottle = enabled;
}
public static void MinimizeFrameskip(bool enabled)
{
Global.Config.AutoMinimizeSkipping = enabled;
}
}
[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 EmuApi()
{ }
public void DisplayVsync(bool enabled)
{
EmuStatic.DisplayVsync(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];
}
int l;
var d = DisassemblableCore.Disassemble(domain, pc, out 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 string GetSystemId()
{
return EmuStatic.GetSystemId();
}
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 void LimitFramerate(bool enabled)
{
EmuStatic.LimitFramerate(enabled);
}
public void MinimizeFrameskip(bool enabled)
{
EmuStatic.MinimizeFrameskip(enabled);
}
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 "";
}
public object GetSettings()
{
if (Emulator is GPGX)
{
var gpgx = Emulator as GPGX;
return gpgx.GetSettings();
}
else if (Emulator is LibsnesCore)
{
var snes = Emulator as LibsnesCore;
return snes.GetSettings();
}
else if (Emulator is NES)
{
var nes = Emulator as NES;
return nes.GetSettings();
}
else if (Emulator is QuickNES)
{
var quicknes = Emulator as QuickNES;
return quicknes.GetSettings();
}
else if (Emulator is PCEngine)
{
var pce = Emulator as PCEngine;
return pce.GetSettings();
}
else if (Emulator is SMS)
{
var sms = Emulator as SMS;
return sms.GetSettings();
}
else if (Emulator is WonderSwan)
{
var ws = Emulator as WonderSwan;
return ws.GetSettings();
}
else
{
return null;
}
}
public bool PutSettings(object settings)
{
if (Emulator is GPGX)
{
var gpgx = Emulator as GPGX;
return gpgx.PutSettings(settings as GPGX.GPGXSettings);
}
else if (Emulator is LibsnesCore)
{
var snes = Emulator as LibsnesCore;
return snes.PutSettings(settings as LibsnesCore.SnesSettings);
}
else if (Emulator is NES)
{
var nes = Emulator as NES;
return nes.PutSettings(settings as NES.NESSettings);
}
else if (Emulator is QuickNES)
{
var quicknes = Emulator as QuickNES;
return quicknes.PutSettings(settings as QuickNES.QuickNESSettings);
}
else if (Emulator is PCEngine)
{
var pce = Emulator as PCEngine;
return pce.PutSettings(settings as PCEngine.PCESettings);
}
else if (Emulator is SMS)
{
var sms = Emulator as SMS;
return sms.PutSettings(settings as SMS.SMSSettings);
}
else if (Emulator is WonderSwan)
{
var ws = Emulator as WonderSwan;
return ws.PutSettings(settings as WonderSwan.Settings);
}
else
{
return false;
}
}
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;
}
}
}

View File

@ -0,0 +1,86 @@
using System.Collections.Generic;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.ApiHawk
{
public sealed class GameInfoApi : IGameInfo
{
[OptionalService]
private IBoardInfo BoardInfo { get; set; }
public GameInfoApi()
{ }
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,222 @@
using System;
using System.Collections.Generic;
using BizHawk.Client.Common;
namespace BizHawk.Client.ApiHawk
{
public sealed class JoypadApi : IJoypad
{
public JoypadApi()
{ }
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);
}
}
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*/
}
}
public void Set(string button, bool? state = null, int? controller = null)
{
try
{
var toPress = button;
if (controller.HasValue)
{
toPress = "P" + controller + " " + button;
}
if (state.HasValue)
Global.LuaAndAdaptor.SetButton(toPress, state.Value);
else
Global.LuaAndAdaptor.UnSet(toPress);
Global.ActiveController.Overrides(Global.LuaAndAdaptor);
}
catch
{
/*Eat it*/
}
}
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*/
}
}
public void SetAnalog(string control, float? value = null, object controller = null)
{
try
{
if (controller == null)
{
Global.StickyXORAdapter.SetFloat(control, value);
}
else
{
Global.StickyXORAdapter.SetFloat("P" + controller + " " + control, value);
}
}
catch
{
/*Eat it*/
}
}
}
}

View File

@ -0,0 +1,288 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
using BizHawk.Common.BufferExtensions;
namespace BizHawk.Client.ApiHawk
{
public sealed class MemApi : MemApiBase, IMem
{
private MemoryDomain _currentMemoryDomain;
private bool _isBigEndian = false;
public MemApi()
: base()
{
}
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 void SetBigEndian(bool enabled = true)
{
_isBigEndian = enabled;
}
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(long 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(long addr, int size, string domain = null)
{
if (_isBigEndian) return ReadSignedBig(addr, size, domain);
else return ReadSignedLittle(addr, size, domain);
}
private uint ReadUnsigned(long addr, int size, string domain = null)
{
if (_isBigEndian) return ReadUnsignedBig(addr, size, domain);
else return ReadUnsignedLittle(addr, size, domain);
}
private void WriteSigned(long addr, int value, int size, string domain = null)
{
if (_isBigEndian) WriteSignedBig(addr, value, size, domain);
else WriteSignedLittle(addr, value, size, domain);
}
private void WriteUnsigned(long 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(long addr, string domain = null)
{
return ReadUnsignedByte(addr, domain);
}
public void WriteByte(long addr, uint value, string domain = null)
{
WriteUnsignedByte(addr, value, domain);
}
public new List<byte> ReadByteRange(long addr, int length, string domain = null)
{
return base.ReadByteRange(addr, length, domain);
}
public new void WriteByteRange(long addr, List<byte> memoryblock, string domain = null)
{
base.WriteByteRange(addr, memoryblock, domain);
}
public float ReadFloat(long addr, string domain = null)
{
return base.ReadFloat(addr, _isBigEndian, domain);
}
public void WriteFloat(long addr, double value, string domain = null)
{
base.WriteFloat(addr, value, _isBigEndian, domain);
}
#endregion
#region 1 Byte
public int ReadS8(long addr, string domain = null)
{
return (sbyte)ReadUnsignedByte(addr, domain);
}
public uint ReadU8(long addr, string domain = null)
{
return (byte)ReadUnsignedByte(addr, domain);
}
public void WriteS8(long addr, int value, string domain = null)
{
WriteSigned(addr, value, 1, domain);
}
public void WriteU8(long addr, uint value, string domain = null)
{
WriteUnsignedByte(addr, value, domain);
}
#endregion
#region 2 Byte
public int ReadS16(long addr, string domain = null)
{
return (short)ReadSigned(addr, 2, domain);
}
public void WriteS16(long addr, int value, string domain = null)
{
WriteSigned(addr, value, 2, domain);
}
public uint ReadU16(long addr, string domain = null)
{
return (ushort)ReadUnsigned(addr, 2, domain);
}
public void WriteU16(long addr, uint value, string domain = null)
{
WriteUnsigned(addr, value, 2, domain);
}
#endregion
#region 3 Byte
public int ReadS24(long addr, string domain = null)
{
return ReadSigned(addr, 3, domain);
}
public void WriteS24(long addr, int value, string domain = null)
{
WriteSigned(addr, value, 3, domain);
}
public uint ReadU24(long addr, string domain = null)
{
return ReadUnsigned(addr, 3, domain);
}
public void WriteU24(long addr, uint value, string domain = null)
{
WriteUnsigned(addr, value, 3, domain);
}
#endregion
#region 4 Byte
public int ReadS32(long addr, string domain = null)
{
return ReadSigned(addr, 4, domain);
}
public void WriteS32(long addr, int value, string domain = null)
{
WriteSigned(addr, value, 4, domain);
}
public uint ReadU32(long addr, string domain = null)
{
return ReadUnsigned(addr, 4, domain);
}
public void WriteU32(long addr, uint value, string domain = null)
{
WriteUnsigned(addr, value, 4, domain);
}
#endregion
}
}

View File

@ -0,0 +1,240 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
namespace BizHawk.Client.ApiHawk
{
/// <summary>
/// Base class for the Memory and MainMemory plugin libraries
/// </summary>
public abstract class MemApiBase : IExternalApi
{
[RequiredService]
protected IEmulator Emulator { get; set; }
[OptionalService]
protected IMemoryDomains MemoryDomainCore { get; set; }
protected abstract MemoryDomain Domain { get; }
protected MemApiBase()
{ }
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(long 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(long 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(long addr, int size, string domain = null)
{
return U2S(ReadUnsignedLittle(addr, size, domain), size);
}
protected uint ReadUnsignedLittle(long 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(long addr, int size, string domain = null)
{
return U2S(ReadUnsignedBig(addr, size, domain), size);
}
protected uint ReadUnsignedBig(long 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(long addr, int v, int size, string domain = null)
{
WriteUnsignedLittle(addr, (uint)v, size, domain);
}
protected void WriteUnsignedLittle(long 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(long addr, int v, int size, string domain = null)
{
WriteUnsignedBig(addr, (uint)v, size, domain);
}
protected void WriteUnsignedBig(long 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(long 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(long 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(long 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(long 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

@ -0,0 +1,45 @@
using System;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
namespace BizHawk.Client.ApiHawk
{
public sealed class MemEventsApi : IMemEvents
{
[RequiredService]
private IDebuggable DebuggableCore { get; set; }
public MemEventsApi () : base()
{ }
public void AddReadCallback(Action cb, uint address, string domain)
{
if (DebuggableCore.MemoryCallbacksAvailable())
{
DebuggableCore.MemoryCallbacks.Add(new MemoryCallback(domain, MemoryCallbackType.Read, "Plugin Hook", cb, address, null));
}
}
public void AddWriteCallback(Action cb, uint address, string domain)
{
if (DebuggableCore.MemoryCallbacksAvailable())
{
DebuggableCore.MemoryCallbacks.Add(new MemoryCallback(domain, MemoryCallbackType.Write, "Plugin Hook", cb, address, null));
}
}
public void AddExecCallback(Action cb, uint address, string domain)
{
if (DebuggableCore.MemoryCallbacksAvailable() && DebuggableCore.MemoryCallbacks.ExecuteCallbacksAvailable)
{
DebuggableCore.MemoryCallbacks.Add(new MemoryCallback(domain, MemoryCallbackType.Execute, "Plugin Hook", cb, address, null));
}
}
public void RemoveMemoryCallback(Action cb)
{
if (DebuggableCore.MemoryCallbacksAvailable())
{
DebuggableCore.MemoryCallbacks.Remove(cb);
}
}
}
}

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.IO;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.ApiHawk
{
public sealed class MemorySaveStateApi : IMemorySaveState
{
public MemorySaveStateApi()
{ }
[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,288 @@
using System;
using System.Collections.Generic;
using System.IO;
using BizHawk.Client.Common;
namespace BizHawk.Client.ApiHawk
{
public sealed class MovieApi : IMovie
{
private static class MoviePluginStatic
{
public static string Filename()
{
return Global.MovieSession.Movie.Filename;
}
public static bool GetReadOnly()
{
return Global.MovieSession.ReadOnly;
}
public static ulong GetRerecordCount()
{
return Global.MovieSession.Movie.Rerecords;
}
public static bool GetRerecordCounting()
{
return Global.MovieSession.Movie.IsCountingRerecords;
}
public static bool IsLoaded()
{
return Global.MovieSession.Movie.IsActive;
}
public static double Length()
{
return Global.MovieSession.Movie.FrameCount;
}
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";
}
public static void SetReadOnly(bool readOnly)
{
Global.MovieSession.ReadOnly = readOnly;
}
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;
}
public static void SetRerecordCounting(bool counting)
{
Global.MovieSession.Movie.IsCountingRerecords = counting;
}
public static void Stop()
{
Global.MovieSession.Movie.Stop();
}
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;
}
}
public MovieApi()
{ }
public bool StartsFromSavestate()
{
return Global.MovieSession.Movie.IsActive && Global.MovieSession.Movie.StartsFromSavestate;
}
public bool StartsFromSaveram()
{
return Global.MovieSession.Movie.IsActive && Global.MovieSession.Movie.StartsFromSaveRam;
}
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;
}
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 "";
}
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();
}
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;
}
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;
}
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;
}
public string Filename()
{
return MoviePluginStatic.Filename();
}
public bool GetReadOnly()
{
return MoviePluginStatic.GetReadOnly();
}
public ulong GetRerecordCount()
{
return MoviePluginStatic.GetRerecordCount();
}
public bool GetRerecordCounting()
{
return MoviePluginStatic.GetRerecordCounting();
}
public bool IsLoaded()
{
return MoviePluginStatic.IsLoaded();
}
public double Length()
{
return MoviePluginStatic.Length();
}
public string Mode()
{
return MoviePluginStatic.Mode();
}
public void SetReadOnly(bool readOnly)
{
MoviePluginStatic.SetReadOnly(readOnly);
}
public void SetRerecordCount(double count)
{
MoviePluginStatic.SetRerecordCount(count);
}
public void SetRerecordCounting(bool counting)
{
MoviePluginStatic.SetRerecordCounting(counting);
}
public void Stop()
{
MoviePluginStatic.Stop();
}
public double GetFps()
{
return MoviePluginStatic.GetFps();
}
}
}

View File

@ -0,0 +1,50 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Client.ApiHawk
{
public abstract class PluginBase : IPlugin
{
/// <summary>
/// The base class from which all
/// plugins will be derived
///
/// Actual plugins should implement
/// one of the below callback methods
/// or register memory callbacks in
/// their Init function.
/// </summary>
protected IApiContainer _api;
public PluginBase() { }
public abstract string Name { get; }
public abstract string Description { get; }
public bool Enabled => Running;
public bool Paused => !Running;
public bool Running { get; set; }
public void Stop()
{
Running = false;
}
public void Toggle()
{
Running = !Running;
}
public virtual void PreFrameCallback() { }
public virtual void PostFrameCallback() { }
public virtual void SaveStateCallback(string name) { }
public virtual void LoadStateCallback(string name) { }
public virtual void InputPollCallback() { }
public virtual void ExitCallback() { }
public virtual void Init (IApiContainer api)
{
_api = api;
Running = true;
}
}
}

View File

@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.SQLite;
namespace BizHawk.Client.ApiHawk
{
public sealed class SqlApi : ISql
{
public SqlApi() : base()
{ }
SQLiteConnection m_dbConnection;
string connectionString;
public string CreateDatabase(string name)
{
try
{
SQLiteConnection.CreateFile(name);
return "Database Created Successfully";
}
catch (SQLiteException sqlEX)
{
return sqlEX.Message;
}
}
public string OpenDatabase(string name)
{
try
{
SQLiteConnectionStringBuilder connBuilder = new SQLiteConnectionStringBuilder();
connBuilder.DataSource = name;
connBuilder.Version = 3; //SQLite version
connBuilder.JournalMode = SQLiteJournalModeEnum.Wal; //Allows for reads and writes to happen at the same time
connBuilder.DefaultIsolationLevel = System.Data.IsolationLevel.ReadCommitted; //This only helps make the database lock left. May be pointless now
connBuilder.SyncMode = SynchronizationModes.Off; //This shortens the delay for do synchronous calls.
m_dbConnection = new SQLiteConnection(connBuilder.ToString());
connectionString = connBuilder.ToString();
m_dbConnection.Open();
m_dbConnection.Close();
return "Database Opened Successfully";
}
catch (SQLiteException sqlEX)
{
return sqlEX.Message;
}
}
public string WriteCommand(string query = "")
{
if (query == "")
{
return "query is empty";
}
try
{
m_dbConnection.Open();
string sql = query;
SQLiteCommand command = new SQLiteCommand(sql, m_dbConnection);
command.ExecuteNonQuery();
m_dbConnection.Close();
return "Command ran successfully";
}
catch (NullReferenceException nullEX)
{
return "Database not open.";
}
catch (SQLiteException sqlEX)
{
m_dbConnection.Close();
return sqlEX.Message;
}
}
public dynamic ReadCommand(string query = "")
{
if (query == "")
{
return "query is empty";
}
try
{
var table = new Dictionary<string, object>();
m_dbConnection.Open();
string sql = "PRAGMA read_uncommitted =1;" + query;
SQLiteCommand command = new SQLiteCommand(sql, m_dbConnection);
SQLiteDataReader reader = command.ExecuteReader();
bool rows = reader.HasRows;
long rowCount = 0;
var columns = new List<string>();
for (int i = 0; i < reader.FieldCount; ++i) //Add all column names into list
{
columns.Add(reader.GetName(i));
}
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; ++i)
{
table[columns[i] + " " + rowCount.ToString()] = reader.GetValue(i);
}
rowCount += 1;
}
reader.Close();
m_dbConnection.Close();
if (rows == false)
{
return "No rows found";
}
return table;
}
catch (NullReferenceException)
{
return "Database not opened.";
}
catch (SQLiteException sqlEX)
{
m_dbConnection.Close();
return sqlEX.Message;
}
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.ComponentModel;
using BizHawk.Client.Common;
namespace BizHawk.Client.ApiHawk
{
public sealed class UserDataApi : IUserData
{
public UserDataApi() : base()
{ }
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,79 @@
using System;
using System.Linq;
using BizHawk.Common.ReflectionExtensions;
namespace BizHawk.Client.ApiHawk
{
/// <summary>
/// injects Apis into other classes
/// </summary>
public static class ApiInjector
{
/// <summary>
/// clears all Apis from a target
/// </summary>
public static void ClearApis(object target)
{
Type targetType = target.GetType();
object[] tmp = new object[1];
foreach (var propinfo in
targetType.GetPropertiesWithAttrib(typeof(RequiredApiAttribute))
.Concat(targetType.GetPropertiesWithAttrib(typeof(OptionalApiAttribute))))
{
propinfo.GetSetMethod(true).Invoke(target, tmp);
}
}
/// <summary>
/// Feeds the target its required Apis.
/// </summary>
/// <returns>false if update failed</returns>
public static bool UpdateApis(IExternalApiProvider source, object target)
{
Type targetType = target.GetType();
object[] tmp = new object[1];
foreach (var propinfo in targetType.GetPropertiesWithAttrib(typeof(RequiredApiAttribute)))
{
tmp[0] = source.GetApi(propinfo.PropertyType);
if (tmp[0] == null)
{
return false;
}
propinfo.GetSetMethod(true).Invoke(target, tmp);
}
foreach (var propinfo in targetType.GetPropertiesWithAttrib(typeof(OptionalApiAttribute)))
{
tmp[0] = source.GetApi(propinfo.PropertyType);
propinfo.GetSetMethod(true).Invoke(target, tmp);
}
return true;
}
/// <summary>
/// Determines whether a target is available, considering its dependencies
/// and the Apis provided by the emulator core.
/// </summary>
public static bool IsAvailable(IExternalApiProvider source, Type targetType)
{
return targetType.GetPropertiesWithAttrib(typeof(RequiredApiAttribute))
.Select(pi => pi.PropertyType)
.All(source.HasApi);
}
}
[AttributeUsage(AttributeTargets.Property)]
public class RequiredApiAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
public class OptionalApiAttribute : Attribute
{
}
}

View File

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace BizHawk.Client.ApiHawk
{
/// <summary>
/// A generic implementation of IExternalApi provider that provides
/// this functionality to any core.
/// The provider will scan an IExternal and register all IExternalApis
/// that the core object itself implements. In addition it provides
/// a Register() method to allow the core to pass in any additional Apis
/// </summary>
/// <seealso cref="IExternalApiProvider"/>
public class BasicApiProvider : IExternalApiProvider
{
private readonly Dictionary<Type, IExternalApi> _Apis = new Dictionary<Type, IExternalApi>();
public BasicApiProvider(IApiContainer container)
{
// simplified logic here doesn't scan for possible Apis; just adds what it knows is implemented by the PluginApi
// this removes the possibility of automagically picking up a Api in a nested class, (find the type, then
// find the field), but we're going to keep such logic out of the basic provider. Anything the passed
// container doesn't implement directly needs to be added with Register()
// this also fully allows apis that are not IExternalApi
var libs = container.Libraries;
_Apis = libs;
}
/// <summary>
/// the client can call this to register an additional Api
/// </summary>
/// <typeparam name="T">The <seealso cref="IExternalApi"/> to register</typeparam>
public void Register<T>(T api)
where T : IExternalApi
{
if (api == null)
{
throw new ArgumentNullException(nameof(api));
}
_Apis[typeof(T)] = api;
}
public T GetApi<T>()
where T : IExternalApi
{
return (T)GetApi(typeof(T));
}
public object GetApi(Type t)
{
IExternalApi Api;
KeyValuePair<Type, IExternalApi>[] k = _Apis.Where(kvp => t.IsAssignableFrom(kvp.Key)).ToArray();
if (k.Length > 0)
{
return k[0].Value;
}
return null;
}
public bool HasApi<T>()
where T : IExternalApi
{
return HasApi(typeof(T));
}
public bool HasApi(Type t)
{
return _Apis.ContainsKey(t);
}
public IEnumerable<Type> AvailableApis
{
get
{
return _Apis.Select(d => d.Key);
}
}
}
}

View File

@ -5,6 +5,7 @@ using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Forms;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Nintendo.Gameboy;
using BizHawk.Emulation.Cores.PCEngine;
using BizHawk.Emulation.Cores.Sega.MasterSystem;
@ -21,6 +22,9 @@ namespace BizHawk.Client.ApiHawk
{
#region Fields
private static IEmulator Emulator;
private static IVideoProvider VideoProvider;
private static readonly Assembly clientAssembly;
private static readonly object clientMainFormInstance;
private static readonly Type mainFormClass;
@ -68,24 +72,57 @@ namespace BizHawk.Client.ApiHawk
mainFormClass = clientAssembly.GetType("BizHawk.Client.EmuHawk.MainForm");
}
public static void UpdateEmulatorAndVP(IEmulator emu = null)
{
Emulator = emu;
VideoProvider = Emulation.Common.IEmulatorExtensions.Extensions.AsVideoProviderOrDefault(emu);
}
#endregion
#region Methods
#region Helpers
private static void InvokeMainFormMethod(string name, dynamic[] paramList = null)
{
List<Type> typeList = new List<Type>();
MethodInfo method;
if (paramList != null)
{
foreach (var obj in paramList)
{
typeList.Add(obj.GetType());
}
method = mainFormClass.GetMethod(name, typeList.ToArray());
}
else method = mainFormClass.GetMethod(name);
method.Invoke(clientMainFormInstance, paramList);
}
private static object GetMainFormField(string name)
{
return mainFormClass.GetField(name);
}
private static void SetMainFormField(string name, object value)
{
mainFormClass.GetField(name).SetValue(clientMainFormInstance, value);
}
#endregion
#region Public
/// <summary>
/// THE FrameAdvance stuff
/// </summary>
public static void DoFrameAdvance()
{
MethodInfo method = mainFormClass.GetMethod("FrameAdvance");
method.Invoke(clientMainFormInstance, null);
InvokeMainFormMethod("FrameAdvance", null);
method = mainFormClass.GetMethod("StepRunLoop_Throttle", BindingFlags.NonPublic | BindingFlags.Instance);
method.Invoke(clientMainFormInstance, null);
InvokeMainFormMethod("StepRunLoop_Throttle", null);
method = mainFormClass.GetMethod("Render", BindingFlags.NonPublic | BindingFlags.Instance);
method.Invoke(clientMainFormInstance, null);
InvokeMainFormMethod("Render", null);
}
/// <summary>
@ -124,8 +161,7 @@ namespace BizHawk.Client.ApiHawk
/// <param name="name">Savetate friendly name</param>
public static void LoadState(string name)
{
MethodInfo method = mainFormClass.GetMethod("LoadState");
method.Invoke(clientMainFormInstance, new object[] { Path.Combine(PathManager.GetSaveStatePath(Global.Game), string.Format("{0}.{1}", name, "State")), name, false, false });
InvokeMainFormMethod("LoadState", new object[] { Path.Combine(PathManager.GetSaveStatePath(Global.Game), string.Format("{0}.{1}", name, "State")), name, false, false });
}
@ -194,12 +230,11 @@ namespace BizHawk.Client.ApiHawk
/// <summary>
/// Raise when a rom is successfully Loaded
/// </summary>
public static void OnRomLoaded()
public static void OnRomLoaded(IEmulator emu)
{
if (RomLoaded != null)
{
RomLoaded(null, EventArgs.Empty);
}
Emulator = emu;
VideoProvider = Emulation.Common.IEmulatorExtensions.Extensions.AsVideoProviderOrDefault(emu);
RomLoaded?.Invoke(null, EventArgs.Empty);
allJoypads = new List<Joypad>(RunningSystem.MaxControllers);
for (int i = 1; i <= RunningSystem.MaxControllers; i++)
@ -215,10 +250,55 @@ namespace BizHawk.Client.ApiHawk
/// <param name="name">Savetate friendly name</param>
public static void SaveState(string name)
{
MethodInfo method = mainFormClass.GetMethod("SaveState");
method.Invoke(clientMainFormInstance, new object[] { Path.Combine(PathManager.GetSaveStatePath(Global.Game), string.Format("{0}.{1}", name, "State")), name, false });
InvokeMainFormMethod("SaveState", new object[] { Path.Combine(PathManager.GetSaveStatePath(Global.Game), string.Format("{0}.{1}", name, "State")), name, false });
}
/// <summary>
/// Sets the extra padding added to the 'native' surface so that you can draw HUD elements in predictable placements
/// </summary>
/// <param name="left">Left padding</param>
/// <param name="top">Top padding</param>
/// <param name="right">Right padding</param>
/// <param name="bottom">Bottom padding</param>
public static void SetGameExtraPadding(int left, int top, int right, int bottom)
{
FieldInfo f = clientAssembly.GetType("BizHawk.Client.EmuHawk.GlobalWin").GetField("DisplayManager");
object displayManager = f.GetValue(null);
f = f.FieldType.GetField("GameExtraPadding");
f.SetValue(displayManager, new Padding(left, top, right, bottom));
InvokeMainFormMethod("FrameBufferResized");
}
/// <summary>
/// Sets the extra padding added to the 'native' surface so that you can draw HUD elements in predictable placements
/// </summary>
/// <param name="left">Left padding</param>
public static void SetGameExtraPadding(int left)
{
SetGameExtraPadding(left, 0, 0, 0);
}
/// <summary>
/// Sets the extra padding added to the 'native' surface so that you can draw HUD elements in predictable placements
/// </summary>
/// <param name="left">Left padding</param>
/// <param name="top">Top padding</param>
public static void SetGameExtraPadding(int left, int top)
{
SetGameExtraPadding(left, top, 0, 0);
}
/// <summary>
/// Sets the extra padding added to the 'native' surface so that you can draw HUD elements in predictable placements
/// </summary>
/// <param name="left">Left padding</param>
/// <param name="top">Top padding</param>
/// <param name="right">Right padding</param>
public static void SetGameExtraPadding(int left, int top, int right)
{
SetGameExtraPadding(left, top, right, 0);
}
/// <summary>
/// Sets the extra padding added to the 'native' surface so that you can draw HUD elements in predictable placements
@ -234,8 +314,7 @@ namespace BizHawk.Client.ApiHawk
f = f.FieldType.GetField("ClientExtraPadding");
f.SetValue(displayManager, new Padding(left, top, right, bottom));
MethodInfo resize = mainFormClass.GetMethod("FrameBufferResized");
resize.Invoke(clientMainFormInstance, null);
InvokeMainFormMethod("FrameBufferResized");
}
/// <summary>
@ -268,7 +347,6 @@ namespace BizHawk.Client.ApiHawk
SetExtraPadding(left, top, right, 0);
}
/// <summary>
/// Set inputs in specified <see cref="Joypad"/> to specified player
/// </summary>
@ -327,8 +405,7 @@ namespace BizHawk.Client.ApiHawk
/// </summary>
public static void UnpauseEmulation()
{
MethodInfo method = mainFormClass.GetMethod("UnpauseEmulator");
method.Invoke(clientMainFormInstance, null);
InvokeMainFormMethod("UnpauseEmulator", null);
}
#endregion Public
@ -375,6 +452,275 @@ namespace BizHawk.Client.ApiHawk
}
}
public static void CloseEmulator()
{
InvokeMainFormMethod("CloseEmulator");
}
public static void CloseEmulatorWithCode(int exitCode)
{
InvokeMainFormMethod("CloseEmulator", new object[] {exitCode});
}
public static int BorderHeight()
{
var point = new System.Drawing.Point(0, 0);
Type t = clientAssembly.GetType("BizHawk.Client.EmuHawk.GlobalWin");
FieldInfo f = t.GetField("DisplayManager");
object displayManager = f.GetValue(null);
MethodInfo m = t.GetMethod("TransFormPoint");
point = (System.Drawing.Point) m.Invoke(displayManager, new object[] { point });
return point.Y;
}
public static int BorderWidth()
{
var point = new System.Drawing.Point(0, 0);
Type t = clientAssembly.GetType("BizHawk.Client.EmuHawk.GlobalWin");
FieldInfo f = t.GetField("DisplayManager");
object displayManager = f.GetValue(null);
MethodInfo m = t.GetMethod("TransFormPoint");
point = (System.Drawing.Point)m.Invoke(displayManager, new object[] { point });
return point.X;
}
public static int BufferHeight()
{
return VideoProvider.BufferHeight;
}
public static int BufferWidth()
{
return VideoProvider.BufferWidth;
}
public static void ClearAutohold()
{
InvokeMainFormMethod("ClearHolds");
}
public static void CloseRom()
{
InvokeMainFormMethod("CloseRom");
}
public static void DisplayMessages(bool value)
{
Global.Config.DisplayMessages = value;
}
public static void EnableRewind(bool enabled)
{
InvokeMainFormMethod("EnableRewind", new object[] {enabled});
}
public static void FrameSkip(int numFrames)
{
if (numFrames > 0)
{
Global.Config.FrameSkip = numFrames;
InvokeMainFormMethod("FrameSkipMessage");
}
else
{
Console.WriteLine("Invalid frame skip value");
}
}
public static int GetTargetScanlineIntensity()
{
return Global.Config.TargetScanlineFilterIntensity;
}
public static int GetWindowSize()
{
return Global.Config.TargetZoomFactors[Emulator.SystemId];
}
public static void SetSoundOn(bool enable)
{
Global.Config.SoundEnabled = enable;
}
public static bool GetSoundOn()
{
return Global.Config.SoundEnabled;
}
public static bool IsPaused()
{
return (bool) GetMainFormField("EmulatorPaused");
}
public static bool IsTurbo()
{
return (bool)GetMainFormField("IsTurboing");
}
public static bool IsSeeking()
{
return (bool)GetMainFormField("IsSeeking");
}
public static void OpenRom(string path)
{
var ioa = OpenAdvancedSerializer.ParseWithLegacy(path);
Type t = clientAssembly.GetType("BizHawk.Client.EmuHawk.GlobalWin.MainForm.LoadRomArgs");
object o = Activator.CreateInstance(t);
t.GetField("OpenAdvanced").SetValue(o, ioa);
InvokeMainFormMethod("LoadRom", new object[] {path, o});
}
public static void Pause()
{
InvokeMainFormMethod("PauseEmulator");
}
public static void PauseAv()
{
SetMainFormField("PauseAvi", true);
}
public static void RebootCore()
{
InvokeMainFormMethod("RebootCore");
}
public static void SaveRam()
{
InvokeMainFormMethod("FlushSaveRAM");
}
public static int ScreenHeight()
{
Type t = GetMainFormField("PresentationPanel").GetType();
object o = GetMainFormField("PresentationPanel");
o = t.GetField("NativeSize").GetValue(o);
t = t.GetField("NativeSize").GetType();
return (int) t.GetField("Height").GetValue(o);
}
public static void Screenshot(string path = null)
{
if (path == null)
{
InvokeMainFormMethod("TakeScreenshot");
}
else
{
InvokeMainFormMethod("TakeScreenshot", new object[] {path});
}
}
public static void ScreenshotToClipboard()
{
InvokeMainFormMethod("TakeScreenshotToClipboard");
}
public static void SetTargetScanlineIntensity(int val)
{
Global.Config.TargetScanlineFilterIntensity = val;
}
public static void SetScreenshotOSD(bool value)
{
Global.Config.Screenshot_CaptureOSD = value;
}
public static int ScreenWidth()
{
Type t = GetMainFormField("PresentationPanel").GetType();
object o = GetMainFormField("PresentationPanel");
o = t.GetField("NativeSize").GetValue(o);
t = t.GetField("NativeSize").GetType();
return (int) t.GetField("Width").GetValue(o);
}
public static void SetWindowSize(int size)
{
if (size == 1 || size == 2 || size == 3 || size == 4 || size == 5 || size == 10)
{
Global.Config.TargetZoomFactors[Emulator.SystemId] = size;
InvokeMainFormMethod("FrameBufferResized");
Type t = clientAssembly.GetType("BizHawk.Client.EmuHawk.GlobalWin");
FieldInfo f = t.GetField("OSD");
object osd = f.GetValue(null);
t = f.GetType();
MethodInfo m = t.GetMethod("AddMessage");
m.Invoke(osd, new Object[] { "Window size set to " + size + "x" });
}
else
{
Console.WriteLine("Invalid window size");
}
}
public static void SpeedMode(int percent)
{
if (percent > 0 && percent < 6400)
{
InvokeMainFormMethod("ClickSpeedItem", new object[] {percent});
}
else
{
Console.WriteLine("Invalid speed value");
}
}
public static void TogglePause()
{
InvokeMainFormMethod("TogglePause");
}
public static int TransformPointX(int x)
{
var point = new System.Drawing.Point(x, 0);
Type t = clientAssembly.GetType("BizHawk.Client.EmuHawk.GlobalWin");
FieldInfo f = t.GetField("DisplayManager");
object displayManager = f.GetValue(null);
MethodInfo m = t.GetMethod("TransFormPoint");
point = (System.Drawing.Point)m.Invoke(displayManager, new object[] { point });
return point.X;
}
public static int TransformPointY(int y)
{
var point = new System.Drawing.Point(0, y);
Type t = clientAssembly.GetType("BizHawk.Client.EmuHawk.GlobalWin");
FieldInfo f = t.GetField("DisplayManager");
object displayManager = f.GetValue(null);
MethodInfo m = t.GetMethod("TransFormPoint");
point = (System.Drawing.Point)m.Invoke(displayManager, new object[] { point });
return point.Y;
}
public static void Unpause()
{
InvokeMainFormMethod("UnpauseEmulator");
}
public static void UnpauseAv()
{
SetMainFormField("PauseAvi", false);
}
public static int Xpos()
{
object o = GetMainFormField("DesktopLocation");
Type t = mainFormClass.GetField("DesktopLocation").GetType();
return (int)t.GetField("X").GetValue(o);
}
public static int Ypos()
{
object o = GetMainFormField("DesktopLocation");
Type t = mainFormClass.GetField("DesktopLocation").GetType();
return (int)t.GetField("Y").GetValue(o);
}
#endregion
#region Properties
@ -427,11 +773,11 @@ namespace BizHawk.Client.ApiHawk
}
else
{
return SystemInfo.DualGB;
return SystemInfo.DualGB;
}
default:
return SystemInfo.FindByCoreSystem(SystemIdConverter.Convert(Global.Emulator.SystemId));
return SystemInfo.FindByCoreSystem(SystemIdConverter.Convert(Global.Emulator.SystemId));
}
}
}

View File

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
namespace BizHawk.Client.ApiHawk
{
public interface IApiContainer
{
Dictionary<Type, IExternalApi> Libraries { get; set; }
}
}

View File

@ -0,0 +1,35 @@
namespace BizHawk.Client.ApiHawk
{
public interface IComm : IExternalApi
{
#region Sockets
string SocketServerScreenShot();
string SocketServerScreenShotResponse();
string SocketServerSend(string SendString);
string SocketServerResponse();
bool SocketServerSuccessful();
void SocketServerSetTimeout(int timeout);
#endregion
#region MemoryMappedFiles
void MmfSetFilename(string filename);
string MmfSetFilename();
int MmfScreenshot();
int MmfWrite(string mmf_filename, string outputString);
string MmfRead(string mmf_filename, int expectedSize);
#endregion
#region HTTP
string HttpTest();
string HttpTestGet();
string HttpGet(string url);
string HttpPost(string url, string payload);
string HttpPostScreenshot();
void HttpSetTimeout(int timeout);
void HttpSetPostUrl(string url);
void HttpSetGetUrl(string url);
string HttpGetPostUrl();
string HttpGetGetUrl();
#endregion
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
namespace BizHawk.Client.ApiHawk
{
public interface IEmu : IExternalApi
{
Action FrameAdvanceCallback { get; set; }
Action YieldCallback { get; set; }
void DisplayVsync(bool enabled);
void FrameAdvance();
int FrameCount();
object Disassemble(uint pc, string name = "");
ulong? GetRegister(string name);
Dictionary<string, ulong> GetRegisters();
void SetRegister(string register, int value);
long TotalExecutedycles();
string GetSystemId();
bool IsLagged();
void SetIsLagged(bool value = true);
int LagCount();
void SetLagCount(int count);
void LimitFramerate(bool enabled);
void MinimizeFrameskip(bool enabled);
void Yield();
string GetDisplayType();
string GetBoardName();
object GetSettings();
bool PutSettings(object settings);
void SetRenderPlanes(params bool[] param);
}
}

View File

@ -0,0 +1,10 @@
namespace BizHawk.Client.ApiHawk
{
/// <summary>
/// This interface specifies that a client exposes a given interface, such as <seealso cref="IDebuggable"/>,
/// for use by external tools.
/// </summary>
public interface IExternalApi
{
}
}

View File

@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace BizHawk.Client.ApiHawk
{
public interface IGameInfo : IExternalApi
{
string GetRomName();
string GetRomHash();
bool InDatabase();
string GetStatus();
bool IsStatusBad();
string GetBoardType();
Dictionary<string, string> GetOptions();
}
}

View File

@ -0,0 +1,51 @@
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace BizHawk.Client.ApiHawk
{
public interface IGui : IExternalApi
{
#region Gui API
void ToggleCompositingMode();
ImageAttributes GetAttributes();
void SetAttributes(ImageAttributes a);
void DrawNew(string name, bool? clear = true);
void DrawFinish();
bool HasGUISurface { get; }
#endregion
#region Helpers
void SetPadding(int all);
void SetPadding(int x, int y);
void SetPadding(int l, int t, int r, int b);
Padding GetPadding();
#endregion
void AddMessage(string message);
void ClearGraphics();
void ClearText();
void SetDefaultForegroundColor(Color color);
void SetDefaultBackgroundColor(Color color);
void SetDefaultTextBackground(Color color);
void SetDefaultPixelFont(string fontfamily);
void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = null);
void DrawBeziers(Point[] points, Color? color = null);
void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null);
void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null);
void DrawIcon(string path, int x, int y, int? width = null, int? height = null);
void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true);
void ClearImageCache();
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);
void DrawLine(int x1, int y1, int x2, int y2, Color? color = null);
void DrawAxis(int x, int y, int size, Color? color = null);
void DrawPie(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null, Color? background = null);
void DrawPixel(int x, int y, Color? color = null);
void DrawPolygon(Point[] points, Color? line = null, Color? background = null);
void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null);
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);
void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null);
void Text(int x, int y, string message, Color? forecolor = null, string anchor = null);
}
}

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace BizHawk.Client.ApiHawk
{
public interface IInput : IExternalApi
{
Dictionary<string, bool> Get();
Dictionary<string, dynamic> GetMouse();
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace BizHawk.Client.ApiHawk
{
public interface IJoypad : IExternalApi
{
Dictionary<string, dynamic> Get(int? controller = null);
// TODO: what about float controls?
Dictionary<string, dynamic> GetImmediate();
void SetFromMnemonicStr(string inputLogEntry);
void Set(Dictionary<string, bool> buttons, int? controller = null);
void Set(string button, bool? state = null, int? controller = null);
void SetAnalog(Dictionary<string, float> controls, object controller = null);
void SetAnalog(string control, float? value = null, object controller = null);
}
}

View File

@ -0,0 +1,56 @@
using System.Collections.Generic;
namespace BizHawk.Client.ApiHawk
{
public interface IMem : IExternalApi
{
void SetBigEndian(bool enabled = true);
#region Domains
List<string> GetMemoryDomainList();
uint GetMemoryDomainSize(string name = "");
string GetCurrentMemoryDomain();
uint GetCurrentMemoryDomainSize();
bool UseMemoryDomain(string domain);
string HashRegion(long addr, int count, string domain = null);
#endregion
#region Read
#region Special and Legacy Methods
uint ReadByte(long addr, string domain = null);
List<byte> ReadByteRange(long addr, int length, string domain = null);
float ReadFloat(long addr, string domain = null);
#endregion
#region Signed
int ReadS8(long addr, string domain = null);
int ReadS16(long addr, string domain = null);
int ReadS24(long addr, string domain = null);
int ReadS32(long addr, string domain = null);
#endregion
#region Unsigned
uint ReadU8(long addr, string domain = null);
uint ReadU16(long addr, string domain = null);
uint ReadU24(long addr, string domain = null);
uint ReadU32(long addr, string domain = null);
#endregion
#endregion
#region Write
#region Special and Legacy Methods
void WriteByte(long addr, uint value, string domain = null);
void WriteByteRange(long addr, List<byte> memoryblock, string domain = null);
void WriteFloat(long addr, double value, string domain = null);
#endregion
#region Signed
void WriteS8(long addr, int value, string domain = null);
void WriteS16(long addr, int value, string domain = null);
void WriteS24(long addr, int value, string domain = null);
void WriteS32(long addr, int value, string domain = null);
#endregion
#region Unigned
void WriteU8(long addr, uint value, string domain = null);
void WriteU16(long addr, uint value, string domain = null);
void WriteU24(long addr, uint value, string domain = null);
void WriteU32(long addr, uint value, string domain = null);
#endregion
#endregion
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace BizHawk.Client.ApiHawk
{
public interface IMemEvents : IExternalApi
{
void AddReadCallback(Action cb, uint address, string domain);
void AddWriteCallback(Action cb, uint address, string domain);
void AddExecCallback(Action cb, uint address, string domain);
void RemoveMemoryCallback(Action cb);
}
}

View File

@ -0,0 +1,10 @@
namespace BizHawk.Client.ApiHawk
{
public interface IMemorySaveState : IExternalApi
{
string SaveCoreStateToMemory();
void LoadCoreStateFromMemory(string identifier);
void DeleteState(string identifier);
void ClearInMemoryStates();
}
}

View File

@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace BizHawk.Client.ApiHawk
{
public interface IMovie : IExternalApi
{
bool StartsFromSavestate();
bool StartsFromSaveram();
string Filename();
Dictionary<string, dynamic> GetInput(int frame);
string GetInputAsMnemonic(int frame);
bool GetReadOnly();
ulong GetRerecordCount();
bool GetRerecordCounting();
bool IsLoaded();
double Length();
string Mode();
void Save(string filename = "");
void SetReadOnly(bool readOnly);
void SetRerecordCount(double count);
void SetRerecordCounting(bool counting);
void Stop();
double GetFps();
Dictionary<string, string> GetHeader();
List<string> GetComments();
List<string> GetSubtitles();
}
}

View File

@ -0,0 +1,10 @@
namespace BizHawk.Client.ApiHawk
{
public interface ISaveState : IExternalApi
{
void Load(string path);
void LoadSlot(int slotNum);
void Save(string path);
void SaveSlot(int slotNum);
}
}

View File

@ -0,0 +1,10 @@
namespace BizHawk.Client.ApiHawk
{
public interface ISql : IExternalApi
{
string CreateDatabase(string name);
string OpenDatabase(string name);
string WriteCommand(string query = "");
dynamic ReadCommand(string query = "");
}
}

View File

@ -0,0 +1,16 @@
using System;
namespace BizHawk.Client.ApiHawk
{
public interface ITool : IExternalApi
{
Type GetTool(string name);
object CreateInstance(string name);
void OpenCheats();
void OpenHexEditor();
void OpenRamWatch();
void OpenRamSearch();
void OpenTasStudio();
void OpenToolBox();
void OpenTraceLogger();
}
}

View File

@ -0,0 +1,11 @@
namespace BizHawk.Client.ApiHawk
{
public interface IUserData : IExternalApi
{
void Set(string name, object value);
object Get(string key);
void Clear();
bool Remove(string key);
bool ContainsKey(string key);
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
namespace BizHawk.Client.ApiHawk
{
/// <summary>
/// This interface defines the mechanism by which External tools can retrieve <seealso cref="IExternalApi" />
/// from a client implementation
/// An implementation should collect all available IExternalApi instances.
/// This interface defines only the external interaction. This interface does not specify the means
/// by which a api provider will be populated with available apis. However, an implementation
/// by design must provide this mechanism
/// </summary>
/// <seealso cref="IExternalApi"/>
public interface IExternalApiProvider
{
/// <summary>e
/// Returns whether or not T is available
/// </summary>
/// <typeparam name="T">The <seealso cref="IExternalApi" /> to check</typeparam>
bool HasApi<T>() where T : IExternalApi;
/// <summary>
/// Returns whether or not t is available
/// </summary>
bool HasApi(Type t);
/// <summary>
/// Returns an instance of T if T is available
/// Else returns null
/// </summary>
/// <typeparam name="T">The requested <seealso cref="IExternalApi" /></typeparam>
T GetApi<T>() where T : IExternalApi;
/// <summary>
/// Returns an instance of t if t is available
/// Else returns null
/// </summary>
object GetApi(Type t);
/// <summary>
/// Gets a list of all currently registered Apis available to be retrieved
/// </summary>
IEnumerable<Type> AvailableApis { get; }
}
}

View File

@ -0,0 +1,12 @@
namespace BizHawk.Client.ApiHawk
{
interface IPlugin
{
void PreFrameCallback();
void PostFrameCallback();
void SaveStateCallback(string name);
void LoadStateCallback(string name);
void InputPollCallback();
void Init(IApiContainer api);
}
}

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" />
@ -244,6 +245,7 @@
<Compile Include="movie\tasproj\TasMovieRecord.cs" />
<Compile Include="movie\tasproj\TasStateManagerSettings.cs" />
<Compile Include="NESGameGenieEncoderDecoder.cs" />
<Compile Include="OpenAdvanced.cs" />
<Compile Include="PathManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="QuickBmpFile.cs" />

View File

@ -10,7 +10,7 @@ using Newtonsoft.Json;
//this file contains some cumbersome self-"serialization" in order to gain a modicum of control over what the serialized output looks like
//I don't want them to look like crufty json
namespace BizHawk.Client.EmuHawk
namespace BizHawk.Client.Common
{
public interface IOpenAdvanced
{
@ -74,7 +74,7 @@ namespace BizHawk.Client.EmuHawk
}
}
class OpenAdvanced_Libretro : IOpenAdvanced, IOpenAdvancedLibretro
public class OpenAdvanced_Libretro : IOpenAdvanced, IOpenAdvancedLibretro
{
public OpenAdvanced_Libretro()
{
@ -103,7 +103,7 @@ namespace BizHawk.Client.EmuHawk
public string CorePath { get { return token.CorePath; } set { token.CorePath = value; } }
}
class OpenAdvanced_LibretroNoGame : IOpenAdvanced, IOpenAdvancedLibretro
public class OpenAdvanced_LibretroNoGame : IOpenAdvanced, IOpenAdvancedLibretro
{
//you might think ideally we'd fetch the libretro core name from the core info inside it
//but that would involve spinning up excess libretro core instances, which probably isnt good for stability, no matter how much we wish otherwise, not to mention slow.
@ -140,7 +140,7 @@ namespace BizHawk.Client.EmuHawk
public string CorePath { get { return _corePath; } set { _corePath = value; } }
}
class OpenAdvanced_OpenRom : IOpenAdvanced
public class OpenAdvanced_OpenRom : IOpenAdvanced
{
public OpenAdvanced_OpenRom()
{}

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

@ -217,9 +217,9 @@ namespace BizHawk.Client.Common
case DisplayType.Hex:
return val.ToHexString(8);
case DisplayType.FixedPoint_20_12:
return $"{val / 4096.0:0.######}";
return $"{(int)val / 4096.0:0.######}";
case DisplayType.FixedPoint_16_16:
return $"{val / 65536.0:0.######}";
return $"{(int)val / 65536.0:0.######}";
case DisplayType.Float:
var bytes = BitConverter.GetBytes(val);
var _float = BitConverter.ToSingle(bytes, 0);

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using BizHawk.Client.ApiHawk;
namespace BizHawk.Client.EmuHawk
{
public sealed class ApiContainer : IApiContainer
{
public IComm Comm => (IComm)Libraries[typeof(CommApi)];
public IEmu Emu => (IEmu)Libraries[typeof(EmuApi)];
public IGameInfo GameInfo => (IGameInfo)Libraries[typeof(GameInfoApi)];
public IGui Gui => (IGui)Libraries[typeof(GuiApi)];
public IInput Input => (IInput)Libraries[typeof(InputApi)];
public IJoypad Joypad => (IJoypad)Libraries[typeof(JoypadApi)];
public IMem Mem => (IMem)Libraries[typeof(MemApi)];
public IMemEvents MemEvents => (IMemEvents)Libraries[typeof(MemEventsApi)];
public IMemorySaveState MemorySaveState => (IMemorySaveState)Libraries[typeof(MemorySaveStateApi)];
public IMovie Movie => (IMovie)Libraries[typeof(MovieApi)];
public ISaveState SaveState => (ISaveState)Libraries[typeof(SaveStateApi)];
public ISql Sql => (ISql)Libraries[typeof(SqlApi)];
public ITool Tool => (ITool)Libraries[typeof(ToolApi)];
public IUserData UserData => (IUserData)Libraries[typeof(UserDataApi)];
public Dictionary<Type, IExternalApi> Libraries { get; set; }
public ApiContainer(Dictionary<Type, IExternalApi> libs)
{
Libraries = libs;
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using BizHawk.Common.ReflectionExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Client.ApiHawk;
namespace BizHawk.Client.EmuHawk
{
public static class ApiManager
{
private static ApiContainer container;
private static void Register(IEmulatorServiceProvider serviceProvider)
{
// Register external apis
var apis = Assembly
.Load("BizHawk.Client.ApiHawk")
.GetTypes()
.Where(t => typeof(IExternalApi).IsAssignableFrom(t))
.Where(t => t.IsSealed)
.Where(t => ServiceInjector.IsAvailable(serviceProvider, t))
.ToList();
apis.AddRange(
Assembly
.GetAssembly(typeof(ApiContainer))
.GetTypes()
.Where(t => typeof(IExternalApi).IsAssignableFrom(t))
.Where(t => t.IsSealed)
.Where(t => ServiceInjector.IsAvailable(serviceProvider, t)));
foreach (var api in apis)
{
var instance = (IExternalApi)Activator.CreateInstance(api);
ServiceInjector.UpdateServices(serviceProvider, instance);
Libraries.Add(api, instance);
}
container = new ApiContainer(Libraries);
GlobalWin.ApiProvider = new BasicApiProvider(container);
}
private static readonly Dictionary<Type, IExternalApi> Libraries = new Dictionary<Type, IExternalApi>();
public static void Restart(IEmulatorServiceProvider newServiceProvider)
{
Libraries.Clear();
Register(newServiceProvider);
}
}
}

View File

@ -0,0 +1,121 @@
using System;
using System.ComponentModel;
using BizHawk.Emulation.Common;
using BizHawk.Client.ApiHawk;
using System.Text;
using System.Collections.Generic;
using System.Net.Http;
using System.Windows.Forms;
namespace BizHawk.Client.EmuHawk
{
public sealed class CommApi : IComm
{
[RequiredService]
private IEmulator Emulator { get; set; }
[RequiredService]
private IVideoProvider VideoProvider { get; set; }
public CommApi() : base()
{ }
public string SocketServerScreenShot()
{
return GlobalWin.socketServer.SendScreenshot();
}
public string SocketServerScreenShotResponse()
{
return GlobalWin.socketServer.SendScreenshot(1000).ToString();
}
public string SocketServerSend(string SendString)
{
return "Sent : " + GlobalWin.socketServer.SendString(SendString).ToString() + " bytes";
}
public string SocketServerResponse()
{
return GlobalWin.socketServer.ReceiveMessage();
}
public bool SocketServerSuccessful()
{
return GlobalWin.socketServer.Successful();
}
public void SocketServerSetTimeout(int timeout)
{
GlobalWin.socketServer.SetTimeout(timeout);
}
// All MemoryMappedFile related methods
public void MmfSetFilename(string filename)
{
GlobalWin.memoryMappedFiles.SetFilename(filename);
}
public string MmfSetFilename()
{
return GlobalWin.memoryMappedFiles.GetFilename();
}
public int MmfScreenshot()
{
return GlobalWin.memoryMappedFiles.ScreenShotToFile();
}
public int MmfWrite(string mmf_filename, string outputString)
{
return GlobalWin.memoryMappedFiles.WriteToFile(mmf_filename, Encoding.ASCII.GetBytes(outputString));
}
public string MmfRead(string mmf_filename, int expectedSize)
{
return GlobalWin.memoryMappedFiles.ReadFromFile(mmf_filename, expectedSize).ToString();
}
// All HTTP related methods
public string HttpTest()
{
var list = new StringBuilder();
list.AppendLine(GlobalWin.httpCommunication.TestGet());
list.AppendLine(GlobalWin.httpCommunication.SendScreenshot());
list.AppendLine("done testing");
return list.ToString();
}
public string HttpTestGet()
{
return GlobalWin.httpCommunication.TestGet();
}
public string HttpGet(string url)
{
return GlobalWin.httpCommunication.ExecGet(url);
}
public string HttpPost(string url, string payload)
{
return GlobalWin.httpCommunication.ExecPost(url, payload);
}
public string HttpPostScreenshot()
{
return GlobalWin.httpCommunication.SendScreenshot();
}
public void HttpSetTimeout(int timeout)
{
GlobalWin.httpCommunication.SetTimeout(timeout);
}
public void HttpSetPostUrl(string url)
{
GlobalWin.httpCommunication.SetPostUrl(url);
}
public void HttpSetGetUrl(string url)
{
GlobalWin.httpCommunication.SetGetUrl(url);
}
public string HttpGetPostUrl()
{
return GlobalWin.httpCommunication.GetPostUrl();
}
public string HttpGetGetUrl()
{
return GlobalWin.httpCommunication.GetGetUrl();
}
}
}

View File

@ -0,0 +1,655 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.IO;
using BizHawk.Client.ApiHawk;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{
public sealed class GuiApi : IGui
{
[RequiredService]
private IEmulator Emulator { get; set; }
private Color _defaultForeground = Color.White;
private Color? _defaultBackground;
private Color? _defaultTextBackground = Color.FromArgb(128, 0, 0, 0);
private int _defaultPixelFont = 1; // gens
private Padding _padding = new Padding(0);
private ImageAttributes _attributes = new ImageAttributes();
private System.Drawing.Drawing2D.CompositingMode _compositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
public GuiApi()
{ }
private DisplaySurface _GUISurface = null;
public bool HasGUISurface => _GUISurface != null;
#region Gui API
public void ToggleCompositingMode()
{
_compositingMode = 1 - _compositingMode;
}
public ImageAttributes GetAttributes()
{
return _attributes;
}
public void SetAttributes(ImageAttributes a)
{
_attributes = a;
}
public void Dispose()
{
foreach (var brush in _solidBrushes.Values)
{
brush.Dispose();
}
foreach (var brush in _pens.Values)
{
brush.Dispose();
}
}
public void DrawNew(string name, bool? clear = true)
{
try
{
DrawFinish();
_GUISurface = GlobalWin.DisplayManager.LockLuaSurface(name, clear ?? true);
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.ToString());
}
}
public void DrawFinish()
{
if (_GUISurface != null)
{
GlobalWin.DisplayManager.UnlockLuaSurface(_GUISurface);
}
_GUISurface = null;
}
#endregion
#region Helpers
private readonly Dictionary<string, Image> _imageCache = new Dictionary<string, Image>();
private readonly Dictionary<Color, SolidBrush> _solidBrushes = new Dictionary<Color, SolidBrush>();
private readonly Dictionary<Color, Pen> _pens = new Dictionary<Color, Pen>();
private SolidBrush GetBrush(Color color)
{
SolidBrush b;
if (!_solidBrushes.TryGetValue(color, out b))
{
b = new SolidBrush(color);
_solidBrushes[color] = b;
}
return b;
}
private Pen GetPen(Color color)
{
Pen p;
if (!_pens.TryGetValue(color, out p))
{
p = new Pen(color);
_pens[color] = p;
}
return p;
}
private 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;
}
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 void AddMessage(string message)
{
GlobalWin.OSD.AddMessage(message);
}
public void ClearGraphics()
{
_GUISurface.Clear();
DrawFinish();
}
public void ClearText()
{
GlobalWin.OSD.ClearGUIText();
}
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.CompositingMode = _compositingMode;
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.CompositingMode = _compositingMode;
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
{
float w;
float h;
if (x < x2)
{
w = x2 - x;
}
else
{
x2 = x - x2;
x -= x2;
w = Math.Max(x2, 0.1f);
}
if (y < y2)
{
h = y2 - y;
}
else
{
y2 = y - y2;
y -= y2;
h = Math.Max(y2, 0.1f);
}
g.CompositingMode = _compositingMode;
g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, w, h);
var bg = background ?? _defaultBackground;
if (bg.HasValue)
{
g.FillRectangle(GetBrush(bg.Value), x + 1, y + 1, Math.Max(w - 1, 0), Math.Max(h - 1, 0));
}
}
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.CompositingMode = _compositingMode;
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.CompositingMode = _compositingMode;
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);
}
}
var destRect = new Rectangle(x, y, width ?? img.Width, height ?? img.Height);
g.CompositingMode = _compositingMode;
g.DrawImage(img, destRect, 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, _attributes);
}
}
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.CompositingMode = _compositingMode;
g.DrawImage(img, destRect, source_x, source_y, source_width, source_height, GraphicsUnit.Pixel, _attributes);
}
}
public void DrawLine(int x1, int y1, int x2, int y2, Color? color = null)
{
using (var g = GetGraphics())
{
g.CompositingMode = _compositingMode;
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())
{
g.CompositingMode = _compositingMode;
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())
{
var w = Math.Max(width, 0.1F);
var h = Math.Max(height, 0.1F);
g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, w, h);
var bg = background ?? _defaultBackground;
if (bg.HasValue)
{
g.FillRectangle(GetBrush(bg.Value), x + 1, y + 1, Math.Max(w - 1, 0), Math.Max(h - 1, 0));
}
}
}
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 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));
if (backcolor.HasValue) g.FillRectangle(GetBrush(backcolor.Value), rect);
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
g.DrawString(message, font, GetBrush(forecolor ?? _defaultForeground), x, y);
}
catch (Exception)
{
return;
}
}
}
public 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);
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Windows.Forms;
using BizHawk.Client.ApiHawk;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
public sealed class InputApi : IInput
{
public InputApi() : base()
{ }
public Dictionary<string,bool> Get()
{
var buttons = new Dictionary<string, bool>();
foreach (var kvp in Global.ControllerInputCoalescer.BoolButtons().Where(kvp => kvp.Value))
{
buttons[kvp.Key] = true;
}
return buttons;
}
public Dictionary<string, dynamic> GetMouse()
{
var buttons = new Dictionary<string, dynamic>();
// TODO - need to specify whether in "emu" or "native" coordinate space.
var p = GlobalWin.DisplayManager.UntransformPoint(Control.MousePosition);
buttons["X"] = p.X;
buttons["Y"] = p.Y;
buttons[MouseButtons.Left.ToString()] = (Control.MouseButtons & MouseButtons.Left) != 0;
buttons[MouseButtons.Middle.ToString()] = (Control.MouseButtons & MouseButtons.Middle) != 0;
buttons[MouseButtons.Right.ToString()] = (Control.MouseButtons & MouseButtons.Right) != 0;
buttons[MouseButtons.XButton1.ToString()] = (Control.MouseButtons & MouseButtons.XButton1) != 0;
buttons[MouseButtons.XButton2.ToString()] = (Control.MouseButtons & MouseButtons.XButton2) != 0;
buttons["Wheel"] = GlobalWin.MainForm.MouseWheelTracker;
return buttons;
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.IO;
using BizHawk.Client.ApiHawk;
namespace BizHawk.Client.EmuHawk
{
public sealed class SaveStateApi : ISaveState
{
public SaveStateApi() : base()
{ }
public void Load(string path)
{
if (!File.Exists(path))
{
Console.WriteLine($"could not find file: {path}");
}
else
{
GlobalWin.MainForm.LoadState(path, Path.GetFileName(path), true);
}
}
public void LoadSlot(int slotNum)
{
if (slotNum >= 0 && slotNum <= 9)
{
GlobalWin.MainForm.LoadQuickSave("QuickSave" + slotNum, true);
}
}
public void Save(string path)
{
GlobalWin.MainForm.SaveState(path, path, true);
}
public void SaveSlot(int slotNum)
{
if (slotNum >= 0 && slotNum <= 9)
{
GlobalWin.MainForm.SaveQuickSave("QuickSave" + slotNum);
}
}
}
}

View File

@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Client.ApiHawk;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
public sealed class ToolApi : ITool
{
private class ToolStatic
{
public Type GetTool(string name)
{
var toolType = ReflectionUtil.GetTypeByName(name)
.FirstOrDefault(x => typeof(IToolForm).IsAssignableFrom(x) && !x.IsInterface);
if (toolType != null)
{
GlobalWin.Tools.Load(toolType);
}
var selectedTool = GlobalWin.Tools.AvailableTools
.FirstOrDefault(tool => tool.GetType().Name.ToLower() == name.ToLower());
if (selectedTool != null)
{
return selectedTool;
}
return null;
}
public object CreateInstance(string name)
{
var possibleTypes = ReflectionUtil.GetTypeByName(name);
if (possibleTypes.Any())
{
return Activator.CreateInstance(possibleTypes.First());
}
return null;
}
public static void OpenCheats()
{
GlobalWin.Tools.Load<Cheats>();
}
public static void OpenHexEditor()
{
GlobalWin.Tools.Load<HexEditor>();
}
public static void OpenRamWatch()
{
GlobalWin.Tools.LoadRamWatch(loadDialog: true);
}
public static void OpenRamSearch()
{
GlobalWin.Tools.Load<RamSearch>();
}
public static void OpenTasStudio()
{
GlobalWin.Tools.Load<TAStudio>();
}
public static void OpenToolBox()
{
GlobalWin.Tools.Load<ToolBox>();
}
public static void OpenTraceLogger()
{
GlobalWin.Tools.Load<TraceLogger>();
}
}
[RequiredService]
private static IEmulator Emulator { get; set; }
[RequiredService]
private static IVideoProvider VideoProvider { get; set; }
public ToolApi()
{ }
public Type GetTool(string name)
{
var toolType = ReflectionUtil.GetTypeByName(name)
.FirstOrDefault(x => typeof(IToolForm).IsAssignableFrom(x) && !x.IsInterface);
if (toolType != null)
{
GlobalWin.Tools.Load(toolType);
}
var selectedTool = GlobalWin.Tools.AvailableTools
.FirstOrDefault(tool => tool.GetType().Name.ToLower() == name.ToLower());
if (selectedTool != null)
{
return selectedTool;
}
return null;
}
public object CreateInstance(string name)
{
var possibleTypes = ReflectionUtil.GetTypeByName(name);
if (possibleTypes.Any())
{
return Activator.CreateInstance(possibleTypes.First());
}
return null;
}
public void OpenCheats()
{
ToolStatic.OpenCheats();
}
public void OpenHexEditor()
{
ToolStatic.OpenHexEditor();
}
public void OpenRamWatch()
{
ToolStatic.OpenRamWatch();
}
public void OpenRamSearch()
{
ToolStatic.OpenRamSearch();
}
public void OpenTasStudio()
{
ToolStatic.OpenTasStudio();
}
public void OpenToolBox()
{
ToolStatic.OpenToolBox();
}
public void OpenTraceLogger()
{
ToolStatic.OpenTraceLogger();
}
}
}

View File

@ -661,6 +661,13 @@
</Compile>
<Compile Include="GLManager.cs" />
<Compile Include="GlobalWin.cs" />
<Compile Include="Api\ApiContainer.cs" />
<Compile Include="Api\Libraries\ToolApi.cs" />
<Compile Include="Api\Libraries\GuiApi.cs" />
<Compile Include="Api\Libraries\InputApi.cs" />
<Compile Include="Api\Libraries\SaveStateAPI.cs" />
<Compile Include="Api\ApiManager.cs" />
<Compile Include="Api\Libraries\CommApi.cs" />
<!--<Compile Include="Input\GamePad.cs" Condition=" '$(OS)' == 'Windows_NT' " />-->
<Compile Include="Input\GamePad.cs" />
<Compile Include="Input\GamePad360.cs" />
@ -734,7 +741,6 @@
<Compile Include="NameStateForm.Designer.cs">
<DependentUpon>NameStateForm.cs</DependentUpon>
</Compile>
<Compile Include="OpenAdvanced.cs" />
<Compile Include="OpenAdvancedChooser.cs">
<SubType>Form</SubType>
</Compile>

View File

@ -1,4 +1,5 @@
using BizHawk.Bizware.BizwareGL;
using BizHawk.Client.ApiHawk;
// ReSharper disable StyleCop.SA1401
namespace BizHawk.Client.EmuHawk
@ -7,6 +8,7 @@ namespace BizHawk.Client.EmuHawk
{
public static MainForm MainForm;
public static ToolManager Tools;
public static BasicApiProvider ApiProvider;
/// <summary>
/// the IGL to be used for rendering

View File

@ -3783,6 +3783,7 @@ namespace BizHawk.Client.EmuHawk
}
}
ApiManager.Restart(Emulator.ServiceProvider);
GlobalWin.Tools.Restart();
if (Global.Config.LoadCheatFileByGame)
@ -3831,7 +3832,7 @@ namespace BizHawk.Client.EmuHawk
}
}
ClientApi.OnRomLoaded();
ClientApi.OnRomLoaded(Emulator);
return true;
}
else
@ -3842,10 +3843,11 @@ namespace BizHawk.Client.EmuHawk
// The ROM has been loaded by a recursive invocation of the LoadROM method.
if (!(Emulator is NullEmulator))
{
ClientApi.OnRomLoaded();
ClientApi.OnRomLoaded(Emulator);
return true;
}
ClientApi.UpdateEmulatorAndVP(Emulator);
HandlePlatformMenus();
_stateSlots.Clear();
UpdateStatusSlots();
@ -3935,6 +3937,7 @@ namespace BizHawk.Client.EmuHawk
var coreComm = CreateCoreComm();
CoreFileProvider.SyncCoreCommInputSignals(coreComm);
Emulator = new NullEmulator(coreComm, Global.Config.GetCoreSettings<NullEmulator>());
ClientApi.UpdateEmulatorAndVP(Emulator);
Global.ActiveController = new Controller(NullController.Instance.Definition);
Global.AutoFireController = _autofireNullControls;
RewireSound();
@ -3957,6 +3960,7 @@ namespace BizHawk.Client.EmuHawk
Global.Game = GameInfo.NullInstance;
GlobalWin.Tools.Restart();
ApiManager.Restart(Emulator.ServiceProvider);
RewireSound();
Text = "BizHawk" + (VersionInfo.DeveloperBuild ? " (interim) " : "");
HandlePlatformMenus();

View File

@ -6,7 +6,7 @@ using System.Reflection;
using System.Windows.Forms;
using BizHawk.Emulation.Common;
using BizHawk.Client.Common;
using BizHawk.Client.ApiHawk;
namespace BizHawk.Client.EmuHawk
{
@ -66,6 +66,8 @@ namespace BizHawk.Client.EmuHawk
continue;
if (!ServiceInjector.IsAvailable(Emulator.ServiceProvider, t))
continue;
// if (!ApiInjector.IsAvailable(, t))
// continue;
var instance = Activator.CreateInstance(t);

View File

@ -7,6 +7,7 @@ using System.Reflection;
using System.ComponentModel;
using System.Windows.Forms;
using BizHawk.Client.ApiHawk;
using BizHawk.Client.Common;
using BizHawk.Client.EmuHawk;
using BizHawk.Client.EmuHawk.CoreExtensions;
@ -123,6 +124,11 @@ namespace BizHawk.Client.EmuHawk
(newTool as Form).Owner = GlobalWin.MainForm;
}
if (isExternal)
{
ApiInjector.UpdateApis(GlobalWin.ApiProvider, newTool);
}
ServiceInjector.UpdateServices(Global.Emulator.ServiceProvider, newTool);
string toolType = typeof(T).ToString();
@ -493,6 +499,8 @@ namespace BizHawk.Client.EmuHawk
if ((tool.IsHandleCreated && !tool.IsDisposed) || tool is RamWatch) // Hack for RAM Watch - in display watches mode it wants to keep running even closed, it will handle disposed logic
{
if (tool is IExternalToolForm)
ApiInjector.UpdateApis(GlobalWin.ApiProvider, tool);
tool.Restart();
}
}

View File

@ -110,4 +110,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View File

@ -121,6 +121,10 @@
<Compile Include="Sound\Utilities\Waves.cs" />
<Compile Include="SystemLookup.cs" />
<Compile Include="TextState.cs" />
<Compile Include="WorkingTypes\wshort.cs" />
<Compile Include="WorkingTypes\wushort.cs" />
<Compile Include="WorkingTypes\wsbyte.cs" />
<Compile Include="WorkingTypes\wbyte.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BizHawk.Common\BizHawk.Common.csproj">

View File

@ -0,0 +1,116 @@
using System;
using System.Globalization;
using System.Security;
namespace BizHawk.Emulation.Common.WorkingTypes
{
//
// Summary:
// Represents an 8-bit unsigned integer, that is capable of arithmetic without making you weep.
// Also provides all the base functionality of the standard C# Byte by calling its methods where relevant.
public unsafe class wbyte : IComparable, IFormattable, IComparable<wbyte>, IEquatable<wbyte>
{
private Byte val;
public const Byte MaxValue = Byte.MaxValue;
public const Byte MinValue = Byte.MinValue;
public static implicit operator wbyte(ulong value)
{
return new wbyte(value);
}
public static implicit operator wbyte(wushort value)
{
return new wbyte(value);
}
public static implicit operator byte(wbyte value)
{
return value.val;
}
public wbyte()
{
}
public wbyte(ulong value)
{
val = (Byte)(value & 0xFF);
}
public wbyte(long value)
{
val = (Byte)(value & 0xFF);
}
public wbyte(double value)
{
val = (Byte)(((long)value) & 0xFF);
}
public static wbyte Parse(string s, NumberStyles style, IFormatProvider provider)
{
return (ulong)Byte.Parse(s, style, provider);
}
public static wbyte Parse(string s, IFormatProvider provider)
{
return (ulong)Byte.Parse(s, provider);
}
public static wbyte Parse(string s)
{
return (ulong)Byte.Parse(s);
}
public static wbyte Parse(string s, NumberStyles style)
{
return (ulong)Byte.Parse(s, style);
}
public static bool TryParse(string s, out wbyte result)
{
result = new wbyte();
return byte.TryParse(s, out result.val);
}
public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out wbyte result)
{
result = new wbyte();
return byte.TryParse(s, style, provider, out result.val);
}
public int CompareTo(wbyte value)
{
return val.CompareTo(value.val);
}
public int CompareTo(object value)
{
return val.CompareTo(value);
}
public override bool Equals(object obj)
{
return val.Equals(obj);
}
public bool Equals(wbyte obj)
{
return val.Equals(obj);
}
public override int GetHashCode()
{
return val.GetHashCode();
}
public TypeCode GetTypeCode()
{
return val.GetTypeCode();
}
[SecuritySafeCritical]
public string ToString(string format, IFormatProvider provider)
{
return val.ToString(format, provider);
}
[SecuritySafeCritical]
public override string ToString()
{
return val.ToString();
}
[SecuritySafeCritical]
public string ToString(string format)
{
return val.ToString(format);
}
[SecuritySafeCritical]
public string ToString(IFormatProvider provider)
{
return val.ToString(provider);
}
}
}

View File

@ -0,0 +1,111 @@
using System;
using System.Globalization;
using System.Security;
namespace BizHawk.Emulation.Common.WorkingTypes
{
//
// Summary:
// Represents an 8-bit unsigned integer, that is capable of arithmetic without making you weep.
// Also provides all the base functionality of the standard C# SByte by calling its methods where relevant.
public unsafe class wsbyte : IComparable, IFormattable, IComparable<wsbyte>, IEquatable<wsbyte>
{
private SByte val;
public const SByte MaxValue = SByte.MaxValue;
public const SByte MinValue = SByte.MinValue;
public static implicit operator wsbyte(long value)
{
return new wsbyte(value);
}
public static implicit operator SByte(wsbyte value)
{
return value.val;
}
public wsbyte()
{
}
public wsbyte(long value)
{
val = (SByte)(value & 0xFF);
}
public wsbyte(ulong value)
{
val = (SByte)(value & 0xFF);
}
public wsbyte(double value)
{
val = (SByte)(((ulong)value) & 0xFF);
}
public static wsbyte Parse(string s, NumberStyles style, IFormatProvider provider)
{
return (long)SByte.Parse(s, style, provider);
}
public static wsbyte Parse(string s, IFormatProvider provider)
{
return (long)SByte.Parse(s, provider);
}
public static wsbyte Parse(string s)
{
return (long)SByte.Parse(s);
}
public static wsbyte Parse(string s, NumberStyles style)
{
return (long)SByte.Parse(s, style);
}
public static bool TryParse(string s, out wsbyte result)
{
result = new wsbyte();
return SByte.TryParse(s, out result.val);
}
public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out wsbyte result)
{
result = new wsbyte();
return SByte.TryParse(s, style, provider, out result.val);
}
public int CompareTo(wsbyte value)
{
return val.CompareTo(value.val);
}
public int CompareTo(object value)
{
return val.CompareTo(value);
}
public override bool Equals(object obj)
{
return val.Equals(obj);
}
public bool Equals(wsbyte obj)
{
return val.Equals(obj);
}
public override int GetHashCode()
{
return val.GetHashCode();
}
public TypeCode GetTypeCode()
{
return val.GetTypeCode();
}
[SecuritySafeCritical]
public string ToString(string format, IFormatProvider provider)
{
return val.ToString(format, provider);
}
[SecuritySafeCritical]
public override string ToString()
{
return val.ToString();
}
[SecuritySafeCritical]
public string ToString(string format)
{
return val.ToString(format);
}
[SecuritySafeCritical]
public string ToString(IFormatProvider provider)
{
return val.ToString(provider);
}
}
}

View File

@ -0,0 +1,111 @@
using System;
using System.Globalization;
using System.Security;
namespace BizHawk.Emulation.Common.WorkingTypes
{
//
// Summary:
// Represents an 16-bit unsigned integer, that is capable of arithmetic without making you weep.
// Also provides all the base functionality of the standard C# Int16 by calling its methods where relevant.
public unsafe class wshort : IComparable, IFormattable, IComparable<wshort>, IEquatable<wshort>
{
private Int16 val;
public const Int16 MaxValue = Int16.MaxValue;
public const Int16 MinValue = Int16.MinValue;
public static implicit operator wshort(long value)
{
return new wshort(value);
}
public static implicit operator Int16(wshort value)
{
return value.val;
}
public wshort()
{
}
public wshort(long value)
{
val = (Int16)(value & 0xFFFF);
}
public wshort(ulong value)
{
val = (Int16)(value & 0xFFFF);
}
public wshort(double value)
{
val = (Int16)(((ulong)value) & 0xFFFF);
}
public static wshort Parse(string s, NumberStyles style, IFormatProvider provider)
{
return (long)Int16.Parse(s, style, provider);
}
public static wshort Parse(string s, IFormatProvider provider)
{
return (long)Int16.Parse(s, provider);
}
public static wshort Parse(string s)
{
return (long)Int16.Parse(s);
}
public static wshort Parse(string s, NumberStyles style)
{
return (long)Int16.Parse(s, style);
}
public static bool TryParse(string s, out wshort result)
{
result = new wshort();
return Int16.TryParse(s, out result.val);
}
public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out wshort result)
{
result = new wshort();
return Int16.TryParse(s, style, provider, out result.val);
}
public int CompareTo(wshort value)
{
return val.CompareTo(value.val);
}
public int CompareTo(object value)
{
return val.CompareTo(value);
}
public override bool Equals(object obj)
{
return val.Equals(obj);
}
public bool Equals(wshort obj)
{
return val.Equals(obj);
}
public override int GetHashCode()
{
return val.GetHashCode();
}
public TypeCode GetTypeCode()
{
return val.GetTypeCode();
}
[SecuritySafeCritical]
public string ToString(string format, IFormatProvider provider)
{
return val.ToString(format, provider);
}
[SecuritySafeCritical]
public override string ToString()
{
return val.ToString();
}
[SecuritySafeCritical]
public string ToString(string format)
{
return val.ToString(format);
}
[SecuritySafeCritical]
public string ToString(IFormatProvider provider)
{
return val.ToString(provider);
}
}
}

View File

@ -0,0 +1,116 @@
using System;
using System.Globalization;
using System.Security;
namespace BizHawk.Emulation.Common.WorkingTypes
{
//
// Summary:
// Represents an 16-bit unsigned integer, that is capable of arithmetic without making you weep.
// Also provides all the base functionality of the standard C# UInt16 by calling its methods where relevant.
public unsafe class wushort : IComparable, IFormattable, IComparable<wushort>, IEquatable<wushort>
{
private UInt16 val;
public const UInt16 MaxValue = UInt16.MaxValue;
public const UInt16 MinValue = UInt16.MinValue;
public static implicit operator wushort(ulong value)
{
return new wushort(value);
}
public static implicit operator wushort(wbyte value)
{
return new wushort(value);
}
public static implicit operator UInt16(wushort value)
{
return value.val;
}
public wushort()
{
}
public wushort(ulong value)
{
val = (UInt16)(value & 0xFFFF);
}
public wushort(long value)
{
val = (UInt16)(value & 0xFFFF);
}
public wushort(double value)
{
val = (UInt16)(((long)value) & 0xFFFF);
}
public static wushort Parse(string s, NumberStyles style, IFormatProvider provider)
{
return (uint)UInt16.Parse(s, style, provider);
}
public static wushort Parse(string s, IFormatProvider provider)
{
return (uint)UInt16.Parse(s, provider);
}
public static wushort Parse(string s)
{
return (uint)UInt16.Parse(s);
}
public static wushort Parse(string s, NumberStyles style)
{
return (uint)UInt16.Parse(s, style);
}
public static bool TryParse(string s, out wushort result)
{
result = new wushort();
return ushort.TryParse(s, out result.val);
}
public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out wushort result)
{
result = new wushort();
return ushort.TryParse(s, style, provider, out result.val);
}
public int CompareTo(wushort value)
{
return val.CompareTo(value.val);
}
public int CompareTo(object value)
{
return val.CompareTo(value);
}
public override bool Equals(object obj)
{
return val.Equals(obj);
}
public bool Equals(wushort obj)
{
return val.Equals(obj);
}
public override int GetHashCode()
{
return val.GetHashCode();
}
public TypeCode GetTypeCode()
{
return val.GetTypeCode();
}
[SecuritySafeCritical]
public string ToString(string format, IFormatProvider provider)
{
return val.ToString(format, provider);
}
[SecuritySafeCritical]
public override string ToString()
{
return val.ToString();
}
[SecuritySafeCritical]
public string ToString(string format)
{
return val.ToString(format);
}
[SecuritySafeCritical]
public string ToString(IFormatProvider provider)
{
return val.ToString(provider);
}
}
}

View File

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio 15
VisualStudioVersion = 15.0.27703.2047
MinimumVisualStudioVersion = 12.0.31101.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Version", "Version\Version.csproj", "{0CE8B337-08E3-4602-BF10-C4D4C75D2F13}"
EndProject
@ -264,6 +264,9 @@ Global
{B95649F5-A0AE-41EB-B62B-578A2AFF5E18} = {B51F1139-3D2C-41BE-A762-EF1F9B41EACA}
{8E2F11F2-3955-4382-8C3A-CEBA1276CAEA} = {B51F1139-3D2C-41BE-A762-EF1F9B41EACA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1A77376C-2741-489C-90E1-03E415910B65}
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = BizHawk.Client.EmuHawk\BizHawk.Client.EmuHawk.csproj
EndGlobalSection

View File

@ -860,7 +860,7 @@ namespace BizHawk.Bizware.BizwareGL.Drivers.OpenTK
_rsBlendNormal = new CacheBlendState(
true,
BlendingFactorSrc.SrcAlpha, BlendEquationMode.FuncAdd, BlendingFactorDest.OneMinusSrcAlpha,
BlendingFactorSrc.One, BlendEquationMode.FuncAdd, BlendingFactorDest.Zero);
BlendingFactorSrc.One, BlendEquationMode.Max, BlendingFactorDest.One);
}
CacheBlendState _rsBlendNoneVerbatim, _rsBlendNoneOpaque, _rsBlendNormal;

View File

@ -368,7 +368,7 @@ namespace BizHawk.Bizware.BizwareGL.Drivers.SlimDX
_rsBlendNormal = new CacheBlendState(
true,
gl.BlendingFactorSrc.SrcAlpha, gl.BlendEquationMode.FuncAdd, gl.BlendingFactorDest.OneMinusSrcAlpha,
gl.BlendingFactorSrc.One, gl.BlendEquationMode.FuncAdd, gl.BlendingFactorDest.Zero);
gl.BlendingFactorSrc.One, gl.BlendEquationMode.Max, gl.BlendingFactorDest.One);
}
CacheBlendState _rsBlendNoneVerbatim, _rsBlendNoneOpaque, _rsBlendNormal;

View File

@ -287,7 +287,7 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary>
public void DiscardAlpha()
{
HasAlpha = false;
//HasAlpha = false;
}
void LoadInternal(Stream stream, sd.Bitmap bitmap, BitmapLoadOptions options)

@ -1 +1 @@
Subproject commit 7432d1f40808b22d3ef7e403cf7ae45b1061dcd7
Subproject commit 57ba873c5e117a42f94299cb7ddaa1066249b416