ApiHawk refactoring and cleanup

MemApi.ReadByteRange now returns the requested number of bytes and not 1 extra,
MemApi.ReadByteRange now warns only once per call for addrs outside range,
MemApi.ReadByteRange now warns for negative addrs (once per call; previous
implementation passed negative addresses to PeekByte), MemApi.WriteByteRange now
warns only once per call for addrs outside range, MemApi.WriteByteRange now
warns for negative addrs (once per call; previous implementation passed negative
addresses to PokeByte)
This commit is contained in:
YoshiRulz 2019-12-17 02:39:04 +10:00
parent 08d0f462fc
commit 0fcb6cbaa7
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
15 changed files with 819 additions and 1862 deletions

View File

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

View File

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

View File

@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using BizHawk.Client.Common;
namespace BizHawk.Client.Common namespace BizHawk.Client.Common
{ {
public sealed class JoypadApi : IJoypad public sealed class JoypadApi : IJoypad
@ -16,195 +14,93 @@ namespace BizHawk.Client.Common
private readonly Action<string> LogCallback; private readonly Action<string> LogCallback;
public Dictionary<string,dynamic> Get(int? controller = null) public Dictionary<string, dynamic> Get(int? controller = null)
{ {
var buttons = new Dictionary<string, dynamic>();
var adapter = Global.AutofireStickyXORAdapter; var adapter = Global.AutofireStickyXORAdapter;
var buttons = new Dictionary<string, dynamic>();
foreach (var button in adapter.Source.Definition.BoolButtons) foreach (var button in adapter.Source.Definition.BoolButtons)
{ {
if (!controller.HasValue) if (controller == null) buttons[button] = adapter.IsPressed(button);
else if (button.Length > 2 && button.Substring(0, 2) == $"P{controller}")
{ {
buttons[button] = adapter.IsPressed(button); var sub = button.Substring(3);
} buttons[sub] = adapter.IsPressed($"P{controller} {sub}");
else if (button.Length >= 3 && button.Substring(0, 2) == $"P{controller}")
{
buttons[button.Substring(3)] = adapter.IsPressed($"P{controller} {button.Substring(3)}");
} }
} }
foreach (var button in adapter.Source.Definition.FloatControls) foreach (var button in adapter.Source.Definition.FloatControls)
{ {
if (controller == null) if (controller == null) buttons[button] = adapter.GetFloat(button);
else if (button.Length > 2 && button.Substring(0, 2) == $"P{controller}")
{ {
buttons[button] = adapter.GetFloat(button); var sub = button.Substring(3);
} buttons[sub] = adapter.GetFloat($"P{controller} {sub}");
else if (button.Length >= 3 && button.Substring(0, 2) == $"P{controller}")
{
buttons[button.Substring(3)] = adapter.GetFloat($"P{controller} {button.Substring(3)}");
} }
} }
return buttons; return buttons;
} }
public Dictionary<string, dynamic> GetImmediate() public Dictionary<string, dynamic> GetImmediate()
{ {
var buttons = new Dictionary<string, dynamic>();
var adapter = Global.ActiveController; var adapter = Global.ActiveController;
foreach (var button in adapter.Definition.BoolButtons) var buttons = new Dictionary<string, dynamic>();
{ foreach (var button in adapter.Definition.BoolButtons) buttons[button] = adapter.IsPressed(button);
buttons[button] = adapter.IsPressed(button); foreach (var button in adapter.Definition.FloatControls) buttons[button] = adapter.GetFloat(button);
}
foreach (var button in adapter.Definition.FloatControls)
{
buttons[button] = adapter.GetFloat(button);
}
return buttons; return buttons;
} }
public void SetFromMnemonicStr(string inputLogEntry) public void SetFromMnemonicStr(string inputLogEntry)
{ {
var lg = Global.MovieSession.MovieControllerInstance();
try try
{ {
var lg = Global.MovieSession.MovieControllerInstance();
lg.SetControllersAsMnemonic(inputLogEntry); lg.SetControllersAsMnemonic(inputLogEntry);
foreach (var button in lg.Definition.BoolButtons)
{
Global.ButtonOverrideAdaptor.SetButton(button, lg.IsPressed(button));
}
foreach (var floatButton in lg.Definition.FloatControls)
{
Global.ButtonOverrideAdaptor.SetFloat(floatButton, lg.GetFloat(floatButton));
}
} }
catch (Exception) catch (Exception)
{ {
LogCallback($"invalid mnemonic string: {inputLogEntry}"); LogCallback($"invalid mnemonic string: {inputLogEntry}");
return;
} }
foreach (var button in lg.Definition.BoolButtons) Global.ButtonOverrideAdaptor.SetButton(button, lg.IsPressed(button));
foreach (var floatButton in lg.Definition.FloatControls) Global.ButtonOverrideAdaptor.SetFloat(floatButton, lg.GetFloat(floatButton));
} }
public void Set(Dictionary<string,bool> buttons, int? controller = null) public void Set(Dictionary<string, bool> buttons, int? controller = null)
{ {
try foreach (var button in Global.ActiveController.Definition.BoolButtons)
{ {
foreach (var button in buttons.Keys) Set(button, buttons.TryGetValue(button, out var state) ? state : default, controller);
{
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;
if (controller.HasValue)
{
toPress = $"P{controller} {button}";
}
if (!invert)
{
if (theValue.HasValue) // Force
{
Global.ButtonOverrideAdaptor.SetButton(toPress, theValue.Value);
Global.ActiveController.Overrides(Global.ButtonOverrideAdaptor);
}
else // Unset
{
Global.ButtonOverrideAdaptor.UnSet(toPress);
Global.ActiveController.Overrides(Global.ButtonOverrideAdaptor);
}
}
else // Inverse
{
Global.ButtonOverrideAdaptor.SetInverse(toPress);
Global.ActiveController.Overrides(Global.ButtonOverrideAdaptor);
}
}
}
catch
{
/*Eat it*/
} }
} }
public void Set(string button, bool? state = null, int? controller = null) public void Set(string button, bool? state = null, int? controller = null)
{ {
try try
{ {
var toPress = button; var buttonToSet = controller == null ? button : $"P{controller} {button}";
if (controller.HasValue) if (state == null) Global.ButtonOverrideAdaptor.UnSet(buttonToSet);
{ else Global.ButtonOverrideAdaptor.SetButton(buttonToSet, state.Value);
toPress = $"P{controller} {button}";
}
if (state.HasValue)
Global.ButtonOverrideAdaptor.SetButton(toPress, state.Value);
else
Global.ButtonOverrideAdaptor.UnSet(toPress);
Global.ActiveController.Overrides(Global.ButtonOverrideAdaptor); Global.ActiveController.Overrides(Global.ButtonOverrideAdaptor);
} }
catch catch
{ {
/*Eat it*/ // ignored
} }
} }
public void SetAnalog(Dictionary<string,float> controls, object controller = null)
public void SetAnalog(Dictionary<string, float> controls, object controller = null)
{ {
try foreach (var kvp in controls) SetAnalog(kvp.Key, kvp.Value, controller);
{
foreach (var name in controls.Keys)
{
var theValueStr = controls[name].ToString();
float? theValue = null;
if (!string.IsNullOrWhiteSpace(theValueStr))
{
if (float.TryParse(theValueStr, out var f))
{
theValue = f;
}
}
Global.StickyXORAdapter.SetFloat(controller == null ? name : $"P{controller} {name}", theValue);
}
}
catch
{
/*Eat it*/
}
} }
public void SetAnalog(string control, float? value = null, object controller = null) public void SetAnalog(string control, float? value = null, object controller = null)
{ {
try try
{ {
Global.StickyXORAdapter.SetFloat(controller == null Global.StickyXORAdapter.SetFloat(controller == null ? control : $"P{controller} {control}", value);
? control
: $"P{controller} {control}", value);
} }
catch catch
{ {
/*Eat it*/ // ignored
} }
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using BizHawk.Common.BufferExtensions; using BizHawk.Common.BufferExtensions;
@ -16,47 +17,6 @@ namespace BizHawk.Client.Common
[OptionalService] [OptionalService]
private IMemoryDomains MemoryDomainCore { get; set; } private IMemoryDomains MemoryDomainCore { get; set; }
private MemoryDomain _currentMemoryDomain;
private 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";
LogCallback(error);
throw new NotImplementedException(error);
}
}
private bool _isBigEndian;
private IMemoryDomains DomainList
{
get
{
if (MemoryDomainCore != null)
{
return MemoryDomainCore;
}
var error = $"Error: {Emulator.Attributes().CoreName} does not implement memory domains";
LogCallback(error);
throw new NotImplementedException(error);
}
}
public MemApi(Action<string> logCallback) public MemApi(Action<string> logCallback)
{ {
LogCallback = logCallback; LogCallback = logCallback;
@ -66,64 +26,92 @@ namespace BizHawk.Client.Common
private readonly Action<string> LogCallback; private readonly Action<string> LogCallback;
private string VerifyMemoryDomain(string domain) private bool _isBigEndian;
private MemoryDomain _currentMemoryDomain;
private MemoryDomain Domain
{ {
try get
{ {
if (DomainList[domain] == null) MemoryDomain LazyInit()
{ {
LogCallback($"Unable to find domain: {domain}, falling back to current"); if (MemoryDomainCore == null)
return Domain.Name; {
var error = $"Error: {Emulator.Attributes().CoreName} does not implement memory domains";
LogCallback(error);
throw new NotImplementedException(error);
}
return MemoryDomainCore.HasSystemBus ? MemoryDomainCore.SystemBus : MemoryDomainCore.MainMemory;
} }
_currentMemoryDomain ??= LazyInit();
return domain; return _currentMemoryDomain;
} }
catch // Just in case set => _currentMemoryDomain = value;
}
private IMemoryDomains DomainList
{
get
{ {
LogCallback($"Unable to find domain: {domain}, falling back to current"); if (MemoryDomainCore == null)
{
var error = $"Error: {Emulator.Attributes().CoreName} does not implement memory domains";
LogCallback(error);
throw new NotImplementedException(error);
}
return MemoryDomainCore;
} }
}
return Domain.Name; private MemoryDomain NamedDomainOrCurrent(string name)
{
if (!string.IsNullOrEmpty(name))
{
try
{
var found = DomainList[name];
if (found != null) return found;
}
catch
{
// ignored
}
LogCallback($"Unable to find domain: {name}, falling back to current");
}
return Domain;
} }
private uint ReadUnsignedByte(long addr, string domain = null) private uint ReadUnsignedByte(long addr, string domain = null)
{ {
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)]; var d = NamedDomainOrCurrent(domain);
if (addr < d.Size) if (addr >= d.Size)
{ {
return d.PeekByte(addr); LogCallback($"Warning: attempted read of {addr} outside the memory size of {d.Size}");
return default;
} }
return d.PeekByte(addr);
LogCallback($"Warning: attempted read of {addr} outside the memory size of {d.Size}");
return 0;
} }
private void WriteUnsignedByte(long addr, uint v, string domain = null) private void WriteUnsignedByte(long addr, uint v, string domain = null)
{ {
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)]; var d = NamedDomainOrCurrent(domain);
if (d.CanPoke()) if (!d.CanPoke())
{
if (addr < d.Size)
{
d.PokeByte(addr, (byte)v);
}
else
{
LogCallback($"Warning: attempted write to {addr} outside the memory size of {d.Size}");
}
}
else
{ {
LogCallback($"Error: the domain {d.Name} is not writable"); LogCallback($"Error: the domain {d.Name} is not writable");
return;
} }
if (addr >= d.Size)
{
LogCallback($"Warning: attempted write to {addr} outside the memory size of {d.Size}");
return;
}
d.PokeByte(addr, (byte) v);
} }
private static int U2S(uint u, int size) private static int U2S(uint u, int size)
{ {
var s = (int)u; var sh = 8 * (4 - size);
s <<= 8 * (4 - size); return ((int) u << sh) >> sh;
s >>= 8 * (4 - size);
return s;
} }
#region Endian Handling #region Endian Handling
@ -131,60 +119,32 @@ namespace BizHawk.Client.Common
private uint ReadUnsignedLittle(long addr, int size, string domain = null) private uint ReadUnsignedLittle(long addr, int size, string domain = null)
{ {
uint v = 0; uint v = 0;
for (var i = 0; i < size; ++i) for (var i = 0; i < size; i++) v |= ReadUnsignedByte(addr + i, domain) << (8 * i);
{
v |= ReadUnsignedByte(addr + i, domain) << (8 * i);
}
return v; return v;
} }
private uint ReadUnsignedBig(long addr, int size, string domain = null) private uint ReadUnsignedBig(long addr, int size, string domain = null)
{ {
uint v = 0; uint v = 0;
for (var i = 0; i < size; ++i) for (var i = 0; i < size; i++) v |= ReadUnsignedByte(addr + i, domain) << (8 * (size - 1 - i));
{
v |= ReadUnsignedByte(addr + i, domain) << (8 * (size - 1 - i));
}
return v; return v;
} }
private void WriteUnsignedLittle(long addr, uint v, int size, string domain = null) private void WriteUnsignedLittle(long addr, uint v, int size, string domain = null)
{ {
for (var i = 0; i < size; ++i) for (var i = 0; i < size; i++) WriteUnsignedByte(addr + i, (v >> (8 * i)) & 0xFF, domain);
{
WriteUnsignedByte(addr + i, (v >> (8 * i)) & 0xFF, domain);
}
} }
private void WriteUnsignedBig(long addr, uint v, int size, string domain = null) private void WriteUnsignedBig(long addr, uint v, int size, string domain = null)
{ {
for (var i = 0; i < size; ++i) for (var i = 0; i < size; i++) WriteUnsignedByte(addr + i, (v >> (8 * (size - 1 - i))) & 0xFF, domain);
{
WriteUnsignedByte(addr + i, (v >> (8 * (size - 1 - i))) & 0xFF, domain);
}
} }
private int ReadSigned(long addr, int size, string domain = null) private int ReadSigned(long addr, int size, string domain = null) => U2S(ReadUnsigned(addr, size, domain), size);
{
return _isBigEndian
? U2S(ReadUnsignedBig(addr, size, domain), size)
: U2S(ReadUnsignedLittle(addr, size, domain), size);
}
private uint ReadUnsigned(long addr, int size, string domain = null) private uint ReadUnsigned(long addr, int size, string domain = null) => _isBigEndian ? ReadUnsignedBig(addr, size, domain) : ReadUnsignedLittle(addr, size, domain);
{
return _isBigEndian
? ReadUnsignedBig(addr, size, domain)
: ReadUnsignedLittle(addr, size, domain);
}
private void WriteSigned(long addr, int value, int size, string domain = null) private void WriteSigned(long addr, int value, int size, string domain = null) => WriteUnsigned(addr, (uint) value, size, domain);
{
if (_isBigEndian) WriteUnsignedBig(addr, (uint)value, size, domain);
else WriteUnsignedLittle(addr, (uint)value, size, domain);
}
private void WriteUnsigned(long addr, uint value, int size, string domain = null) private void WriteUnsigned(long addr, uint value, int size, string domain = null)
{ {
@ -196,88 +156,57 @@ namespace BizHawk.Client.Common
#region Unique Library Methods #region Unique Library Methods
public void SetBigEndian(bool enabled = true) public void SetBigEndian(bool enabled = true) => _isBigEndian = enabled;
{
_isBigEndian = enabled;
}
public List<string> GetMemoryDomainList() public List<string> GetMemoryDomainList()
{ {
var list = new List<string>(); var list = new List<string>();
foreach (var domain in DomainList) list.Add(domain.Name);
foreach (var domain in DomainList)
{
list.Add(domain.Name);
}
return list; return list;
} }
public uint GetMemoryDomainSize(string name = "") public uint GetMemoryDomainSize(string name = null) => (uint) NamedDomainOrCurrent(name).Size;
{
if (string.IsNullOrEmpty(name))
{
return (uint)Domain.Size;
}
return (uint)DomainList[VerifyMemoryDomain(name)].Size; public string GetCurrentMemoryDomain() => Domain.Name;
}
public string GetCurrentMemoryDomain() public uint GetCurrentMemoryDomainSize() => (uint) Domain.Size;
{
return Domain.Name;
}
public uint GetCurrentMemoryDomainSize()
{
return (uint)Domain.Size;
}
public bool UseMemoryDomain(string domain) public bool UseMemoryDomain(string domain)
{ {
try try
{ {
if (DomainList[domain] != null) var found = DomainList[domain];
if (found != null)
{ {
_currentMemoryDomain = DomainList[domain]; Domain = found;
return true; return true;
} }
LogCallback($"Unable to find domain: {domain}");
return false;
} }
catch // Just in case catch
{ {
LogCallback($"Unable to find domain: {domain}"); // ignored
} }
LogCallback($"Unable to find domain: {domain}");
return false; return false;
} }
public string HashRegion(long addr, int count, string domain = null) public string HashRegion(long addr, int count, string domain = null)
{ {
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)]; var d = NamedDomainOrCurrent(domain);
// checks
if (addr < 0 || addr >= d.Size) if (addr < 0 || addr >= d.Size)
{ {
string error = $"Address {addr} is outside the bounds of domain {d.Name}"; var error = $"Address {addr} is outside the bounds of domain {d.Name}";
LogCallback(error); LogCallback(error);
throw new ArgumentOutOfRangeException(error); throw new ArgumentOutOfRangeException(error);
} }
if (addr + count > d.Size) if (addr + count > d.Size)
{ {
string error = $"Address {addr} + count {count} is outside the bounds of domain {d.Name}"; var error = $"Address {addr} + count {count} is outside the bounds of domain {d.Name}";
LogCallback(error); LogCallback(error);
throw new ArgumentOutOfRangeException(error); throw new ArgumentOutOfRangeException(error);
} }
var data = new byte[count];
byte[] data = new byte[count]; for (var i = 0; i < count; i++) data[i] = d.PeekByte(addr + i);
for (int i = 0; i < count; i++)
{
data[i] = d.PeekByte(addr + i);
}
using var hasher = SHA256.Create(); using var hasher = SHA256.Create();
return hasher.ComputeHash(data).BytesToHexString(); return hasher.ComputeHash(data).BytesToHexString();
} }
@ -286,188 +215,111 @@ namespace BizHawk.Client.Common
#region Common Special and Legacy Methods #region Common Special and Legacy Methods
public uint ReadByte(long addr, string domain = null) public uint ReadByte(long addr, string domain = null) => ReadUnsignedByte(addr, domain);
{
return ReadUnsignedByte(addr, domain);
}
public void WriteByte(long addr, uint value, string domain = null) public void WriteByte(long addr, uint value, string domain = null) => WriteUnsignedByte(addr, value, domain);
{
WriteUnsignedByte(addr, value, domain);
}
public List<byte> ReadByteRange(long addr, int length, string domain = null) public List<byte> ReadByteRange(long addr, int length, string domain = null)
{ {
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)]; var d = NamedDomainOrCurrent(domain);
var lastAddr = length + addr; if (addr < 0) LogCallback($"Warning: Attempted reads on addresses {addr}..-1 outside range of domain {d.Name} in {nameof(ReadByteRange)}()");
var list = new List<byte>(); var lastReqAddr = addr + length - 1;
for (; addr <= lastAddr; addr++) var indexAfterLast = Math.Min(lastReqAddr, d.Size - 1) - addr + 1;
{ var bytes = new byte[length];
if (addr < d.Size) for (var i = addr < 0 ? -addr : 0; i != indexAfterLast; i++) bytes[i] = d.PeekByte(addr + i);
list.Add(d.PeekByte(addr)); if (lastReqAddr >= d.Size) LogCallback($"Warning: Attempted reads on addresses {d.Size}..{lastReqAddr} outside range of domain {d.Name} in {nameof(ReadByteRange)}()");
else { return bytes.ToList();
LogCallback($"Warning: Attempted read {addr} outside memory domain size of {d.Size} in {nameof(ReadByteRange)}()");
list.Add(0);
}
}
return list;
} }
public void WriteByteRange(long addr, List<byte> memoryblock, string domain = null) public void WriteByteRange(long addr, List<byte> memoryblock, string domain = null)
{ {
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)]; var d = NamedDomainOrCurrent(domain);
if (d.CanPoke()) if (!d.CanPoke())
{
foreach (var m in memoryblock)
{
if (addr < d.Size)
{
d.PokeByte(addr++, m);
}
else
{
LogCallback($"Warning: Attempted write {addr} outside memory domain size of {d.Size} in {nameof(WriteByteRange)}()");
}
}
}
else
{ {
LogCallback($"Error: the domain {d.Name} is not writable"); LogCallback($"Error: the domain {d.Name} is not writable");
return;
} }
if (addr < 0) LogCallback($"Warning: Attempted reads on addresses {addr}..-1 outside range of domain {d.Name} in {nameof(WriteByteRange)}()");
var lastReqAddr = addr + memoryblock.Count - 1;
var indexAfterLast = Math.Min(lastReqAddr, d.Size - 1) - addr + 1;
for (var i = addr < 0 ? (int) -addr : 0; i != indexAfterLast; i++) d.PokeByte(addr + i, memoryblock[i]);
if (lastReqAddr >= d.Size) LogCallback($"Warning: Attempted reads on addresses {d.Size}..{lastReqAddr} outside range of domain {d.Name} in {nameof(WriteByteRange)}()");
} }
public float ReadFloat(long addr, string domain = null) public float ReadFloat(long addr, string domain = null)
{ {
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)]; var d = NamedDomainOrCurrent(domain);
if (addr < d.Size) if (addr >= d.Size)
{ {
var val = d.PeekUint(addr, _isBigEndian); LogCallback($"Warning: Attempted read {addr} outside memory size of {d.Size}");
var bytes = BitConverter.GetBytes(val); return default;
return BitConverter.ToSingle(bytes, 0);
} }
return BitConverter.ToSingle(BitConverter.GetBytes(d.PeekUint(addr, _isBigEndian)), 0);
LogCallback($"Warning: Attempted read {addr} outside memory size of {d.Size}");
return 0;
} }
public void WriteFloat(long addr, double value, string domain = null) public void WriteFloat(long addr, double value, string domain = null)
{ {
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)]; var d = NamedDomainOrCurrent(domain);
if (d.CanPoke()) 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, _isBigEndian);
}
else
{
LogCallback($"Warning: Attempted write {addr} outside memory size of {d.Size}");
}
}
else
{ {
LogCallback($"Error: the domain {Domain.Name} is not writable"); LogCallback($"Error: the domain {Domain.Name} is not writable");
return;
} }
if (addr >= d.Size)
{
LogCallback($"Warning: Attempted write {addr} outside memory size of {d.Size}");
return;
}
d.PokeUint(addr, BitConverter.ToUInt32(BitConverter.GetBytes((float) value), 0), _isBigEndian);
} }
#endregion #endregion
#region 1 Byte #region 1 Byte
public int ReadS8(long addr, string domain = null) public int ReadS8(long addr, string domain = null) => (sbyte) ReadUnsignedByte(addr, domain);
{
return (sbyte)ReadUnsignedByte(addr, domain);
}
public uint ReadU8(long addr, string domain = null) public uint ReadU8(long addr, string domain = null) => (byte) ReadUnsignedByte(addr, domain);
{
return (byte)ReadUnsignedByte(addr, domain);
}
public void WriteS8(long addr, int value, string domain = null) public void WriteS8(long addr, int value, string domain = null) => WriteSigned(addr, value, 1, domain);
{
WriteSigned(addr, value, 1, domain);
}
public void WriteU8(long addr, uint value, string domain = null) public void WriteU8(long addr, uint value, string domain = null) => WriteUnsignedByte(addr, value, domain);
{
WriteUnsignedByte(addr, value, domain);
}
#endregion #endregion
#region 2 Byte #region 2 Byte
public int ReadS16(long addr, string domain = null) public int ReadS16(long addr, string domain = null) => (short) ReadSigned(addr, 2, domain);
{
return (short)ReadSigned(addr, 2, domain);
}
public void WriteS16(long addr, int value, string domain = null) public uint ReadU16(long addr, string domain = null) => (ushort) ReadUnsigned(addr, 2, domain);
{
WriteSigned(addr, value, 2, domain);
}
public uint ReadU16(long addr, string domain = null) public void WriteS16(long addr, int value, string domain = null) => WriteSigned(addr, value, 2, domain);
{
return (ushort)ReadUnsigned(addr, 2, domain); public void WriteU16(long addr, uint value, string domain = null) => WriteUnsigned(addr, value, 2, domain);
}
public void WriteU16(long addr, uint value, string domain = null)
{
WriteUnsigned(addr, value, 2, domain);
}
#endregion #endregion
#region 3 Byte #region 3 Byte
public int ReadS24(long addr, string domain = null) public int ReadS24(long addr, string domain = null) => ReadSigned(addr, 3, domain);
{
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) public uint ReadU24(long addr, string domain = null) => ReadUnsigned(addr, 3, domain);
{
return ReadUnsigned(addr, 3, domain);
}
public void WriteU24(long addr, uint value, string domain = null) public void WriteS24(long addr, int value, string domain = null) => WriteSigned(addr, value, 3, domain);
{
WriteUnsigned(addr, value, 3, domain); public void WriteU24(long addr, uint value, string domain = null) => WriteUnsigned(addr, value, 3, domain);
}
#endregion #endregion
#region 4 Byte #region 4 Byte
public int ReadS32(long addr, string domain = null) public int ReadS32(long addr, string domain = null) => ReadSigned(addr, 4, domain);
{
return ReadSigned(addr, 4, domain);
}
public void WriteS32(long addr, int value, string domain = null) public uint ReadU32(long addr, string domain = null) => ReadUnsigned(addr, 4, domain);
{
WriteSigned(addr, value, 4, domain);
}
public uint ReadU32(long addr, string domain = null) public void WriteS32(long addr, int value, string domain = null) => WriteSigned(addr, value, 4, domain);
{
return ReadUnsigned(addr, 4, domain);
}
public void WriteU32(long addr, uint value, string domain = null) public void WriteU32(long addr, uint value, string domain = null) => WriteUnsigned(addr, value, 4, domain);
{
WriteUnsigned(addr, value, 4, domain);
}
#endregion #endregion
} }

View File

@ -1,6 +1,4 @@
using System; using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions; using BizHawk.Emulation.Common.IEmulatorExtensions;
namespace BizHawk.Client.Common namespace BizHawk.Client.Common
@ -10,9 +8,6 @@ namespace BizHawk.Client.Common
[RequiredService] [RequiredService]
private IDebuggable DebuggableCore { get; set; } private IDebuggable DebuggableCore { get; set; }
public MemEventsApi () : base()
{ }
public void AddReadCallback(MemoryCallbackDelegate cb, uint? address, string domain) public void AddReadCallback(MemoryCallbackDelegate cb, uint? address, string domain)
{ {
if (DebuggableCore.MemoryCallbacksAvailable()) if (DebuggableCore.MemoryCallbacksAvailable())
@ -20,6 +15,7 @@ namespace BizHawk.Client.Common
DebuggableCore.MemoryCallbacks.Add(new MemoryCallback(domain, MemoryCallbackType.Read, "Plugin Hook", cb, address, null)); DebuggableCore.MemoryCallbacks.Add(new MemoryCallback(domain, MemoryCallbackType.Read, "Plugin Hook", cb, address, null));
} }
} }
public void AddWriteCallback(MemoryCallbackDelegate cb, uint? address, string domain) public void AddWriteCallback(MemoryCallbackDelegate cb, uint? address, string domain)
{ {
if (DebuggableCore.MemoryCallbacksAvailable()) if (DebuggableCore.MemoryCallbacksAvailable())
@ -27,6 +23,7 @@ namespace BizHawk.Client.Common
DebuggableCore.MemoryCallbacks.Add(new MemoryCallback(domain, MemoryCallbackType.Write, "Plugin Hook", cb, address, null)); DebuggableCore.MemoryCallbacks.Add(new MemoryCallback(domain, MemoryCallbackType.Write, "Plugin Hook", cb, address, null));
} }
} }
public void AddExecCallback(MemoryCallbackDelegate cb, uint? address, string domain) public void AddExecCallback(MemoryCallbackDelegate cb, uint? address, string domain)
{ {
if (DebuggableCore.MemoryCallbacksAvailable() && DebuggableCore.MemoryCallbacks.ExecuteCallbacksAvailable) if (DebuggableCore.MemoryCallbacksAvailable() && DebuggableCore.MemoryCallbacks.ExecuteCallbacksAvailable)
@ -34,12 +31,10 @@ namespace BizHawk.Client.Common
DebuggableCore.MemoryCallbacks.Add(new MemoryCallback(domain, MemoryCallbackType.Execute, "Plugin Hook", cb, address, null)); DebuggableCore.MemoryCallbacks.Add(new MemoryCallback(domain, MemoryCallbackType.Execute, "Plugin Hook", cb, address, null));
} }
} }
public void RemoveMemoryCallback(MemoryCallbackDelegate cb) public void RemoveMemoryCallback(MemoryCallbackDelegate cb)
{ {
if (DebuggableCore.MemoryCallbacksAvailable()) if (DebuggableCore.MemoryCallbacksAvailable()) DebuggableCore.MemoryCallbacks.Remove(cb);
{
DebuggableCore.MemoryCallbacks.Remove(cb);
}
} }
} }
} }

View File

@ -8,6 +8,9 @@ namespace BizHawk.Client.Common
{ {
public sealed class MemorySaveStateApi : IMemorySaveState public sealed class MemorySaveStateApi : IMemorySaveState
{ {
[RequiredService]
private IStatable StatableCore { get; set; }
public MemorySaveStateApi(Action<string> logCallback) public MemorySaveStateApi(Action<string> logCallback)
{ {
LogCallback = logCallback; LogCallback = logCallback;
@ -17,30 +20,21 @@ namespace BizHawk.Client.Common
private readonly Action<string> LogCallback; private readonly Action<string> LogCallback;
[RequiredService]
private IStatable StatableCore { get; set; }
private readonly Dictionary<Guid, byte[]> _memorySavestates = new Dictionary<Guid, byte[]>(); private readonly Dictionary<Guid, byte[]> _memorySavestates = new Dictionary<Guid, byte[]>();
public string SaveCoreStateToMemory() public string SaveCoreStateToMemory()
{ {
var guid = Guid.NewGuid(); var guid = Guid.NewGuid();
var bytes = (byte[])StatableCore.SaveStateBinary().Clone(); _memorySavestates.Add(guid, (byte[]) StatableCore.SaveStateBinary().Clone());
_memorySavestates.Add(guid, bytes);
return guid.ToString(); return guid.ToString();
} }
public void LoadCoreStateFromMemory(string identifier) public void LoadCoreStateFromMemory(string identifier)
{ {
var guid = new Guid(identifier); var guid = new Guid(identifier);
try try
{ {
var state = _memorySavestates[guid]; using var ms = new MemoryStream(_memorySavestates[guid]);
using var ms = new MemoryStream(state);
using var br = new BinaryReader(ms); using var br = new BinaryReader(ms);
StatableCore.LoadStateBinary(br); StatableCore.LoadStateBinary(br);
} }
@ -50,15 +44,8 @@ namespace BizHawk.Client.Common
} }
} }
public void DeleteState(string identifier) public void DeleteState(string identifier) => _memorySavestates.Remove(new Guid(identifier));
{
var guid = new Guid(identifier);
_memorySavestates.Remove(guid);
}
public void ClearInMemoryStates() public void ClearInMemoryStates() => _memorySavestates.Clear();
{
_memorySavestates.Clear();
}
} }
} }

View File

@ -2,8 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using BizHawk.Client.Common;
namespace BizHawk.Client.Common namespace BizHawk.Client.Common
{ {
public sealed class MovieApi : IInputMovie public sealed class MovieApi : IInputMovie
@ -17,99 +15,9 @@ namespace BizHawk.Client.Common
private readonly Action<string> LogCallback; private readonly Action<string> LogCallback;
private static class MoviePluginStatic public bool StartsFromSavestate() => Global.MovieSession.Movie.IsActive && Global.MovieSession.Movie.StartsFromSavestate;
{
public static string Filename()
{
return Global.MovieSession.Movie.Filename;
}
public static bool GetReadOnly() public bool StartsFromSaveram() => Global.MovieSession.Movie.IsActive && Global.MovieSession.Movie.StartsFromSaveRam;
{
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 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 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) public Dictionary<string, dynamic> GetInput(int frame)
{ {
@ -118,161 +26,102 @@ namespace BizHawk.Client.Common
LogCallback("No movie loaded"); LogCallback("No movie loaded");
return null; return null;
} }
var input = new Dictionary<string, dynamic>();
var adapter = Global.MovieSession.Movie.GetInputState(frame); var adapter = Global.MovieSession.Movie.GetInputState(frame);
if (adapter == null) if (adapter == null)
{ {
LogCallback("Can't get input of the last frame of the movie. Use the previous frame"); LogCallback("Can't get input of the last frame of the movie. Use the previous frame");
return null; return null;
} }
var input = new Dictionary<string, dynamic>();
foreach (var button in adapter.Definition.BoolButtons) foreach (var button in adapter.Definition.BoolButtons) input[button] = adapter.IsPressed(button);
{ foreach (var button in adapter.Definition.FloatControls) input[button] = adapter.GetFloat(button);
input[button] = adapter.IsPressed(button);
}
foreach (var button in adapter.Definition.FloatControls)
{
input[button] = adapter.GetFloat(button);
}
return input; return input;
} }
public string GetInputAsMnemonic(int frame) public string GetInputAsMnemonic(int frame)
{ {
if (Global.MovieSession.Movie.IsActive && frame < Global.MovieSession.Movie.InputLogLength) if (!Global.MovieSession.Movie.IsActive || frame >= Global.MovieSession.Movie.InputLogLength) return string.Empty;
{ var lg = Global.MovieSession.LogGeneratorInstance();
var lg = Global.MovieSession.LogGeneratorInstance(); lg.SetSource(Global.MovieSession.Movie.GetInputState(frame));
lg.SetSource(Global.MovieSession.Movie.GetInputState(frame)); return lg.GenerateLogEntry();
return lg.GenerateLogEntry();
}
return "";
} }
public void Save(string filename = "") public void Save(string filename = null)
{ {
if (!Global.MovieSession.Movie.IsActive) if (!Global.MovieSession.Movie.IsActive) return;
{
return;
}
if (!string.IsNullOrEmpty(filename)) if (!string.IsNullOrEmpty(filename))
{ {
filename += $".{Global.MovieSession.Movie.PreferredExtension}"; filename += $".{Global.MovieSession.Movie.PreferredExtension}";
var test = new FileInfo(filename); if (new FileInfo(filename).Exists)
if (test.Exists)
{ {
LogCallback($"File {filename} already exists, will not overwrite"); LogCallback($"File {filename} already exists, will not overwrite");
return; return;
} }
Global.MovieSession.Movie.Filename = filename; Global.MovieSession.Movie.Filename = filename;
} }
Global.MovieSession.Movie.Save(); Global.MovieSession.Movie.Save();
} }
public Dictionary<string,string> GetHeader() public Dictionary<string, string> GetHeader()
{ {
var table = new Dictionary<string,string>(); var table = new Dictionary<string, string>();
if (Global.MovieSession.Movie.IsActive) if (!Global.MovieSession.Movie.IsActive) return table;
{ foreach (var kvp in Global.MovieSession.Movie.HeaderEntries) table[kvp.Key] = kvp.Value;
foreach (var kvp in Global.MovieSession.Movie.HeaderEntries)
{
table[kvp.Key] = kvp.Value;
}
}
return table; return table;
} }
public List<string> GetComments() public List<string> GetComments()
{ {
var list = new List<string>(Global.MovieSession.Movie.Comments.Count); var list = new List<string>(Global.MovieSession.Movie.Comments.Count);
if (Global.MovieSession.Movie.IsActive) if (!Global.MovieSession.Movie.IsActive) return list;
{ for (var i = 0; i < Global.MovieSession.Movie.Comments.Count; i++) list[i] = Global.MovieSession.Movie.Comments[i];
for (int i = 0; i < Global.MovieSession.Movie.Comments.Count; i++)
{
list[i] = Global.MovieSession.Movie.Comments[i];
}
}
return list; return list;
} }
public List<string> GetSubtitles() public List<string> GetSubtitles()
{ {
var list = new List<string>(Global.MovieSession.Movie.Subtitles.Count); var list = new List<string>(Global.MovieSession.Movie.Subtitles.Count);
if (Global.MovieSession.Movie.IsActive) if (!Global.MovieSession.Movie.IsActive) return list;
{ for (var i = 0; i < Global.MovieSession.Movie.Subtitles.Count; i++) list[i] = Global.MovieSession.Movie.Subtitles[i].ToString();
for (int i = 0; i < Global.MovieSession.Movie.Subtitles.Count; i++)
{
list[i] = Global.MovieSession.Movie.Subtitles[i].ToString();
}
}
return list; return list;
} }
public string Filename() public string Filename() => Global.MovieSession.Movie.Filename;
{
return MoviePluginStatic.Filename();
}
public bool GetReadOnly() public bool GetReadOnly() => Global.MovieSession.ReadOnly;
{
return MoviePluginStatic.GetReadOnly();
}
public ulong GetRerecordCount() public ulong GetRerecordCount() => Global.MovieSession.Movie.Rerecords;
{
return MoviePluginStatic.GetRerecordCount();
}
public bool GetRerecordCounting() public bool GetRerecordCounting() => Global.MovieSession.Movie.IsCountingRerecords;
{
return MoviePluginStatic.GetRerecordCounting();
}
public bool IsLoaded() public bool IsLoaded() => Global.MovieSession.Movie.IsActive;
{
return MoviePluginStatic.IsLoaded();
}
public double Length() public double Length() => Global.MovieSession.Movie.FrameCount;
{
return MoviePluginStatic.Length();
}
public string Mode() public string Mode() => Global.MovieSession.Movie.IsFinished
{ ? "FINISHED"
return MoviePluginStatic.Mode(); : Global.MovieSession.Movie.IsPlaying
} ? "PLAY"
: Global.MovieSession.Movie.IsRecording
? "RECORD"
: "INACTIVE";
public void SetReadOnly(bool readOnly) public void SetReadOnly(bool readOnly) => Global.MovieSession.ReadOnly = readOnly;
{
MoviePluginStatic.SetReadOnly(readOnly);
}
public void SetRerecordCount(ulong count) => Global.MovieSession.Movie.Rerecords = count; public void SetRerecordCount(ulong count) => Global.MovieSession.Movie.Rerecords = count;
public void SetRerecordCounting(bool counting) public void SetRerecordCounting(bool counting) => Global.MovieSession.Movie.IsCountingRerecords = counting;
{
MoviePluginStatic.SetRerecordCounting(counting);
}
public void Stop() public void Stop() => Global.MovieSession.Movie.Stop();
{
MoviePluginStatic.Stop();
}
public double GetFps() public double GetFps()
{ {
return MoviePluginStatic.GetFps(); var movie = Global.MovieSession.Movie;
if (!movie.IsActive) return default;
return new PlatformFrameRates()[
movie.HeaderEntries[HeaderKeys.PLATFORM],
movie.HeaderEntries.TryGetValue(HeaderKeys.PAL, out var isPal) && isPal == "1"
];
} }
} }
} }

View File

@ -1,127 +1,103 @@
using System; using System.Collections.Generic;
using System.Collections.Generic; using System.Data;
using System.Data.SQLite; using System.Data.SQLite;
namespace BizHawk.Client.Common namespace BizHawk.Client.Common
{ {
public sealed class SqlApi : ISql public sealed class SqlApi : ISql
{ {
SQLiteConnection _dbConnection; private SQLiteConnection _dbConnection;
public string CreateDatabase(string name) public string CreateDatabase(string name)
{ {
try try
{ {
SQLiteConnection.CreateFile(name); SQLiteConnection.CreateFile(name);
return "Database Created Successfully";
} }
catch (SQLiteException sqlEx) catch (SQLiteException sqlEx)
{ {
return sqlEx.Message; return sqlEx.Message;
} }
return "Database Created Successfully";
} }
public string OpenDatabase(string name) public string OpenDatabase(string name)
{ {
try try
{ {
var connBuilder = new SQLiteConnectionStringBuilder _dbConnection = new SQLiteConnection(
{ new SQLiteConnectionStringBuilder {
DataSource = name, DataSource = name,
Version = 3, Version = 3,
JournalMode = SQLiteJournalModeEnum.Wal, // Allows for reads and writes to happen at the same time JournalMode = SQLiteJournalModeEnum.Wal, // Allows for reads and writes to happen at the same time
DefaultIsolationLevel = System.Data.IsolationLevel.ReadCommitted, // This only helps make the database lock left. May be pointless now DefaultIsolationLevel = IsolationLevel.ReadCommitted, // This only helps make the database lock left. May be pointless now
SyncMode = SynchronizationModes.Off // This shortens the delay for do synchronous calls. SyncMode = SynchronizationModes.Off // This shortens the delay for do synchronous calls.
}; }.ToString()
);
_dbConnection = new SQLiteConnection(connBuilder.ToString());
_dbConnection.Open(); _dbConnection.Open();
_dbConnection.Close();
return "Database Opened Successfully";
} }
catch (SQLiteException sqlEx) catch (SQLiteException sqlEx)
{ {
return sqlEx.Message; return sqlEx.Message;
} }
_dbConnection?.Close();
return "Database Opened Successfully";
} }
public string WriteCommand(string query = "") public string WriteCommand(string query = null)
{ {
if (string.IsNullOrWhiteSpace(query)) if (string.IsNullOrWhiteSpace(query)) return "query is empty";
{ if (_dbConnection == null) return "Database not open.";
return "query is empty"; string result;
}
try try
{ {
_dbConnection.Open(); _dbConnection.Open();
var command = new SQLiteCommand(query, _dbConnection); new SQLiteCommand(query, _dbConnection).ExecuteNonQuery();
command.ExecuteNonQuery(); result = "Command ran successfully";
_dbConnection.Close();
return "Command ran successfully";
}
catch (NullReferenceException)
{
return "Database not open.";
} }
catch (SQLiteException sqlEx) catch (SQLiteException sqlEx)
{ {
_dbConnection.Close(); result = sqlEx.Message;
return sqlEx.Message;
} }
_dbConnection.Close();
return result;
} }
public dynamic ReadCommand(string query = "") public dynamic ReadCommand(string query = null)
{ {
if (string.IsNullOrWhiteSpace(query)) if (string.IsNullOrWhiteSpace(query)) return "query is empty";
{ if (_dbConnection == null) return "Database not open.";
return "query is empty"; dynamic result;
}
try try
{ {
var table = new Dictionary<string, object>();
_dbConnection.Open(); _dbConnection.Open();
string sql = $"PRAGMA read_uncommitted =1;{query}"; using var command = new SQLiteCommand($"PRAGMA read_uncommitted =1;{query}", _dbConnection);
using var command = new SQLiteCommand(sql, _dbConnection); using var reader = command.ExecuteReader();
SQLiteDataReader reader = command.ExecuteReader(); if (reader.HasRows)
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)); var columns = new string[reader.FieldCount];
} for (int i = 0, l = reader.FieldCount; i < l; i++) columns[i] = reader.GetName(i);
long rowCount = 0;
while (reader.Read()) var table = new Dictionary<string, object>();
{ while (reader.Read())
for (int i = 0; i < reader.FieldCount; ++i)
{ {
table[$"{columns[i]} {rowCount}"] = reader.GetValue(i); for (int i = 0, l = reader.FieldCount; i < l; i++) table[$"{columns[i]} {rowCount}"] = reader.GetValue(i);
rowCount++;
} }
reader.Close();
rowCount += 1; result = table;
} }
else
reader.Close();
_dbConnection.Close();
if (rows == false)
{ {
return "No rows found"; result = "No rows found";
} }
return table;
}
catch (NullReferenceException)
{
return "Database not opened.";
} }
catch (SQLiteException sqlEx) catch (SQLiteException sqlEx)
{ {
_dbConnection.Close(); result = sqlEx.Message;
return sqlEx.Message;
} }
_dbConnection.Close();
return result;
} }
} }
} }

View File

@ -1,47 +1,25 @@
using System; using System;
using BizHawk.Client.Common;
namespace BizHawk.Client.Common namespace BizHawk.Client.Common
{ {
public sealed class UserDataApi : IUserData public sealed class UserDataApi : IUserData
{ {
public UserDataApi() : base()
{ }
public void Set(string name, object value) public void Set(string name, object value)
{ {
if (value != null) if (value != null)
{ {
var t = value.GetType(); var t = value.GetType();
if (!t.IsPrimitive && t != typeof(string)) if (!t.IsPrimitive && t != typeof(string)) throw new InvalidOperationException("Invalid type for userdata");
{
throw new InvalidOperationException("Invalid type for userdata");
}
} }
Global.UserBag[name] = value; Global.UserBag[name] = value;
} }
public object Get(string key) public object Get(string key) => Global.UserBag.TryGetValue(key, out var value) ? value : null;
{
return Global.UserBag.ContainsKey(key)
? Global.UserBag[key]
: null;
}
public void Clear() public void Clear() => Global.UserBag.Clear();
{
Global.UserBag.Clear();
}
public bool Remove(string key) public bool Remove(string key) => Global.UserBag.Remove(key);
{
return Global.UserBag.Remove(key);
}
public bool ContainsKey(string key) public bool ContainsKey(string key) => Global.UserBag.ContainsKey(key);
{
return Global.UserBag.ContainsKey(key);
}
} }
} }

View File

@ -1,153 +1,69 @@
using System; using System.Text;
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;
using BizHawk.Client.Common; using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk namespace BizHawk.Client.EmuHawk
{ {
public sealed class CommApi : IComm public sealed class CommApi : IComm
{ {
[RequiredService] public string SocketServerScreenShot() => GlobalWin.socketServer.SendScreenshot();
private IEmulator Emulator { get; set; }
[RequiredService] public string SocketServerScreenShotResponse() => GlobalWin.socketServer.SendScreenshot(1000);
private IVideoProvider VideoProvider { get; set; }
public CommApi() : base() public string SocketServerSend(string SendString) => $"Sent : {GlobalWin.socketServer.SendString(SendString)} bytes";
{ }
public string SocketServerScreenShot() public string SocketServerResponse() => GlobalWin.socketServer.ReceiveMessage();
{
return GlobalWin.socketServer.SendScreenshot();
}
public string SocketServerScreenShotResponse()
{
return GlobalWin.socketServer.SendScreenshot(1000).ToString();
}
public string SocketServerSend(string SendString) public bool SocketServerSuccessful() => GlobalWin.socketServer.Successful();
{
return $"Sent : {GlobalWin.socketServer.SendString(SendString)} bytes";
}
public string SocketServerResponse()
{
return GlobalWin.socketServer.ReceiveMessage();
}
public bool SocketServerSuccessful() public void SocketServerSetTimeout(int timeout) => GlobalWin.socketServer.SetTimeout(timeout);
{
return GlobalWin.socketServer.Successful();
}
public void SocketServerSetTimeout(int timeout)
{
GlobalWin.socketServer.SetTimeout(timeout);
}
public void SocketServerSetIp(string ip) public void SocketServerSetIp(string ip) => GlobalWin.socketServer.Ip = ip;
{
GlobalWin.socketServer.Ip = ip;
}
public void SetSocketServerPort(int port) public void SetSocketServerPort(int port) => GlobalWin.socketServer.Port = port;
{
GlobalWin.socketServer.Port = port;
}
public string SocketServerGetIp() public string SocketServerGetIp() => GlobalWin.socketServer.Ip;
{
return GlobalWin.socketServer.Ip;
}
public int SocketServerGetPort() public int SocketServerGetPort() => GlobalWin.socketServer.Port;
{
return GlobalWin.socketServer.Port;
}
public string SocketServerGetInfo() public string SocketServerGetInfo() => GlobalWin.socketServer.GetInfo();
{
return GlobalWin.socketServer.GetInfo();
}
// All MemoryMappedFile related methods #region MemoryMappedFile
public void MmfSetFilename(string filename)
{
GlobalWin.memoryMappedFiles.Filename = filename;
}
public string MmfGetFilename() public void MmfSetFilename(string filename) => GlobalWin.memoryMappedFiles.Filename = filename;
{
return GlobalWin.memoryMappedFiles.Filename;
}
public int MmfScreenshot() public string MmfGetFilename() => GlobalWin.memoryMappedFiles.Filename;
{
return GlobalWin.memoryMappedFiles.ScreenShotToFile();
}
public int MmfWrite(string mmf_filename, string outputString) public int MmfScreenshot() => GlobalWin.memoryMappedFiles.ScreenShotToFile();
{
return GlobalWin.memoryMappedFiles.WriteToFile(mmf_filename, Encoding.ASCII.GetBytes(outputString));
}
public string MmfRead(string mmf_filename, int expectedSize) public int MmfWrite(string mmf_filename, string outputString) => GlobalWin.memoryMappedFiles.WriteToFile(mmf_filename, Encoding.ASCII.GetBytes(outputString));
{
return GlobalWin.memoryMappedFiles.ReadFromFile(mmf_filename, expectedSize);
}
public string MmfRead(string mmf_filename, int expectedSize) => GlobalWin.memoryMappedFiles.ReadFromFile(mmf_filename, expectedSize);
// All HTTP related methods #endregion
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) #region HTTP
{
return GlobalWin.httpCommunication.ExecPost(url, payload); public string HttpTest() => string.Join("\n", GlobalWin.httpCommunication.TestGet(), GlobalWin.httpCommunication.SendScreenshot(), "done testing");
}
public string HttpPostScreenshot() public string HttpTestGet() => GlobalWin.httpCommunication.TestGet();
{
return GlobalWin.httpCommunication.SendScreenshot(); public string HttpGet(string url) => GlobalWin.httpCommunication.ExecGet(url);
}
public void HttpSetTimeout(int timeout) public string HttpPost(string url, string payload) => GlobalWin.httpCommunication.ExecPost(url, payload);
{
GlobalWin.httpCommunication.SetTimeout(timeout); public string HttpPostScreenshot() => GlobalWin.httpCommunication.SendScreenshot();
}
public void HttpSetPostUrl(string url) public void HttpSetTimeout(int timeout) => GlobalWin.httpCommunication.SetTimeout(timeout);
{
GlobalWin.httpCommunication.PostUrl = url; public void HttpSetPostUrl(string url) => GlobalWin.httpCommunication.PostUrl = url;
}
public void HttpSetGetUrl(string url) public void HttpSetGetUrl(string url) => GlobalWin.httpCommunication.GetUrl = url;
{
GlobalWin.httpCommunication.GetUrl = url; public string HttpGetPostUrl() => GlobalWin.httpCommunication.PostUrl;
}
public string HttpGetPostUrl() public string HttpGetGetUrl() => GlobalWin.httpCommunication.GetUrl;
{
return GlobalWin.httpCommunication.PostUrl; #endregion
}
public string HttpGetGetUrl()
{
return GlobalWin.httpCommunication.GetUrl;
}
} }
} }

View File

@ -1,11 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Windows.Forms; using System.Drawing.Text;
using System.IO; using System.IO;
using System.Windows.Forms;
using BizHawk.Client.ApiHawk;
using BizHawk.Client.Common; using BizHawk.Client.Common;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
@ -15,13 +16,6 @@ namespace BizHawk.Client.EmuHawk
{ {
[RequiredService] [RequiredService]
private IEmulator Emulator { get; set; } 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(Action<string> logCallback) public GuiApi(Action<string> logCallback)
{ {
@ -32,38 +26,57 @@ namespace BizHawk.Client.EmuHawk
private readonly Action<string> LogCallback; private readonly Action<string> LogCallback;
private DisplaySurface _GUISurface = null; private readonly Dictionary<string, Image> _imageCache = new Dictionary<string, Image>();
private readonly Bitmap _nullGraphicsBitmap = new Bitmap(1, 1);
private readonly Dictionary<Color, Pen> _pens = new Dictionary<Color, Pen>();
private readonly Dictionary<Color, SolidBrush> _solidBrushes = new Dictionary<Color, SolidBrush>();
private ImageAttributes _attributes = new ImageAttributes();
private CompositingMode _compositingMode = CompositingMode.SourceOver;
private Color? _defaultBackground;
private Color _defaultForeground = Color.White;
private int _defaultPixelFont = 1; // = "gens"
private Color? _defaultTextBackground = Color.FromArgb(128, 0, 0, 0);
private DisplaySurface _GUISurface;
private Padding _padding = new Padding(0);
public bool HasGUISurface => _GUISurface != null; public bool HasGUISurface => _GUISurface != null;
#region Gui API private SolidBrush GetBrush(Color color) => _solidBrushes.TryGetValue(color, out var b) ? b : (_solidBrushes[color] = new SolidBrush(color));
public void ToggleCompositingMode()
{
_compositingMode = 1 - _compositingMode;
}
public ImageAttributes GetAttributes() private Pen GetPen(Color color) => _pens.TryGetValue(color, out var p) ? p : (_pens[color] = new Pen(color));
{
return _attributes;
}
public void SetAttributes(ImageAttributes a)
{
_attributes = a;
}
public void Dispose() private Graphics GetGraphics()
{ {
foreach (var brush in _solidBrushes.Values) var g = _GUISurface?.GetGraphics() ?? Graphics.FromImage(_nullGraphicsBitmap);
// 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)
{ {
brush.Dispose(); var transform = g.Transform;
} transform.Translate(-tx, -ty);
g.Transform = transform;
foreach (var brush in _pens.Values)
{
brush.Dispose();
} }
return g;
} }
public void ToggleCompositingMode() => _compositingMode = 1 - _compositingMode;
public ImageAttributes GetAttributes() => _attributes;
public void SetAttributes(ImageAttributes a) => _attributes = a;
public void DrawNew(string name, bool clear) public void DrawNew(string name, bool clear)
{ {
try try
@ -79,82 +92,19 @@ namespace BizHawk.Client.EmuHawk
public void DrawFinish() public void DrawFinish()
{ {
if (_GUISurface != null) if (_GUISurface != null) GlobalWin.DisplayManager.UnlockLuaSurface(_GUISurface);
{
GlobalWin.DisplayManager.UnlockLuaSurface(_GUISurface);
}
_GUISurface = null; _GUISurface = null;
} }
#endregion
#region Helpers public void SetPadding(int all) => _padding = new Padding(all);
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 readonly Bitmap _nullGraphicsBitmap = new Bitmap(1, 1);
private SolidBrush GetBrush(Color color)
{
SolidBrush b;
if (!_solidBrushes.TryGetValue(color, out b))
{
b = new SolidBrush(color);
_solidBrushes[color] = b;
}
return b; public void SetPadding(int x, int y) => _padding = new Padding(x / 2, y / 2, x / 2 + x & 1, y / 2 + y & 1);
}
private Pen GetPen(Color color) public void SetPadding(int l, int t, int r, int b) => _padding = new Padding(l, t, r, b);
{
Pen p;
if (!_pens.TryGetValue(color, out p))
{
p = new Pen(color);
_pens[color] = p;
}
return p; public Padding GetPadding() => _padding;
}
private Graphics GetGraphics() public void AddMessage(string message) => GlobalWin.OSD.AddMessage(message);
{
var g = _GUISurface == null ? Graphics.FromImage(_nullGraphicsBitmap) : _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() public void ClearGraphics()
{ {
@ -162,27 +112,15 @@ namespace BizHawk.Client.EmuHawk
DrawFinish(); DrawFinish();
} }
public void ClearText() public void ClearText() => GlobalWin.OSD.ClearGuiText();
{
GlobalWin.OSD.ClearGuiText();
}
public void SetDefaultForegroundColor(Color color) public void SetDefaultForegroundColor(Color color) => _defaultForeground = color;
{
_defaultForeground = color;
}
public void SetDefaultBackgroundColor(Color color) public void SetDefaultBackgroundColor(Color color) => _defaultBackground = color;
{
_defaultBackground = color;
}
public Color? GetDefaultTextBackground() => _defaultTextBackground; public Color? GetDefaultTextBackground() => _defaultTextBackground;
public void SetDefaultTextBackground(Color color) public void SetDefaultTextBackground(Color color) => _defaultTextBackground = color;
{
_defaultTextBackground = color;
}
public void SetDefaultPixelFont(string fontfamily) public void SetDefaultPixelFont(string fontfamily)
{ {
@ -204,135 +142,108 @@ namespace BizHawk.Client.EmuHawk
public void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = null) public void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = null)
{ {
using (var g = GetGraphics()) try
{ {
try using var g = GetGraphics();
{ g.CompositingMode = _compositingMode;
g.CompositingMode = _compositingMode; g.DrawBezier(GetPen(color ?? _defaultForeground), p1, p2, p3, p4);
g.DrawBezier(GetPen(color ?? _defaultForeground), p1, p2, p3, p4); }
} catch (Exception)
catch (Exception) {
{ // ignored
return;
}
} }
} }
public void DrawBeziers(Point[] points, Color? color = null) public void DrawBeziers(Point[] points, Color? color = null)
{ {
using (var g = GetGraphics()) try
{ {
try using var g = GetGraphics();
{ g.CompositingMode = _compositingMode;
g.CompositingMode = _compositingMode; g.DrawBeziers(GetPen(color ?? _defaultForeground), points);
g.DrawBeziers(GetPen(color ?? _defaultForeground), points); }
} catch (Exception)
catch (Exception) {
{ // ignored
return;
}
} }
} }
public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null) public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null)
{ {
using (var g = GetGraphics()) try
{ {
try float w;
if (x < x2)
{ {
float w; w = x2 - x;
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) else
{ {
// need to stop the script from here x2 = x - x2;
return; x -= x2;
w = Math.Max(x2, 0.1f);
} }
float h;
if (y < y2)
{
h = y2 - y;
}
else
{
y2 = y - y2;
y -= y2;
h = Math.Max(y2, 0.1f);
}
using var g = GetGraphics();
g.CompositingMode = _compositingMode;
g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, w, h);
var bg = background ?? _defaultBackground;
if (bg != null) 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
} }
} }
public void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null) public void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null)
{ {
using (var g = GetGraphics()) try
{ {
try using var g = GetGraphics();
{ var bg = background ?? _defaultBackground;
var bg = background ?? _defaultBackground; if (bg != null) g.FillEllipse(GetBrush(bg.Value), x, y, width, height);
if (bg.HasValue) g.CompositingMode = _compositingMode;
{ g.DrawEllipse(GetPen(line ?? _defaultForeground), x, y, width, height);
var brush = GetBrush(bg.Value); }
g.FillEllipse(brush, x, y, width, height); catch (Exception)
} {
// need to stop the script from here
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) public void DrawIcon(string path, int x, int y, int? width = null, int? height = null)
{ {
using (var g = GetGraphics()) try
{ {
try if (!File.Exists(path))
{
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)
{ {
AddMessage($"File not found: {path}");
return; return;
} }
using var g = GetGraphics();
g.CompositingMode = _compositingMode;
g.DrawIcon(
width != null && height != null
? new Icon(path, width.Value, height.Value)
: new Icon(path),
x,
y
);
}
catch (Exception)
{
// ignored
} }
} }
@ -343,36 +254,26 @@ namespace BizHawk.Client.EmuHawk
LogCallback($"File not found: {path}"); LogCallback($"File not found: {path}");
return; return;
} }
using var g = GetGraphics();
using (var g = GetGraphics()) var isCached = _imageCache.ContainsKey(path);
{ var img = isCached ? _imageCache[path] : Image.FromFile(path);
Image img; if (!isCached && cache) _imageCache[path] = img;
if (_imageCache.ContainsKey(path)) g.CompositingMode = _compositingMode;
{ g.DrawImage(
img = _imageCache[path]; img,
} new Rectangle(x, y, width ?? img.Width, height ?? img.Height),
else 0,
{ 0,
img = Image.FromFile(path); img.Width,
if (cache) img.Height,
{ GraphicsUnit.Pixel,
_imageCache.Add(path, img); _attributes
} );
}
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() public void ClearImageCache()
{ {
foreach (var image in _imageCache) foreach (var image in _imageCache) image.Value.Dispose();
{
image.Value.Dispose();
}
_imageCache.Clear(); _imageCache.Clear();
} }
@ -383,34 +284,25 @@ namespace BizHawk.Client.EmuHawk
LogCallback($"File not found: {path}"); LogCallback($"File not found: {path}");
return; return;
} }
using var g = GetGraphics();
using (var g = GetGraphics()) g.CompositingMode = _compositingMode;
{ g.DrawImage(
Image img; _imageCache.TryGetValue(path, out var img) ? img : (_imageCache[path] = Image.FromFile(path)),
if (_imageCache.ContainsKey(path)) new Rectangle(dest_x, dest_y, dest_width ?? source_width, dest_height ?? source_height),
{ source_x,
img = _imageCache[path]; source_y,
} source_width,
else source_height,
{ GraphicsUnit.Pixel,
img = Image.FromFile(path); _attributes
_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) public void DrawLine(int x1, int y1, int x2, int y2, Color? color = null)
{ {
using (var g = GetGraphics()) using var g = GetGraphics();
{ g.CompositingMode = _compositingMode;
g.CompositingMode = _compositingMode; g.DrawLine(GetPen(color ?? _defaultForeground), x1, y1, x2, y2);
g.DrawLine(GetPen(color ?? _defaultForeground), x1, y1, x2, y2);
}
} }
public void DrawAxis(int x, int y, int size, Color? color = null) public void DrawAxis(int x, int y, int size, Color? color = null)
@ -421,246 +313,191 @@ namespace BizHawk.Client.EmuHawk
public void DrawPie(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null, Color? background = null) 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()) using var g = GetGraphics();
{ g.CompositingMode = _compositingMode;
g.CompositingMode = _compositingMode; var bg = background ?? _defaultBackground;
var bg = background ?? _defaultBackground; if (bg != null) g.FillPie(GetBrush(bg.Value), x, y, width, height, startangle, sweepangle);
if (bg.HasValue) g.DrawPie(GetPen(line ?? _defaultForeground), x + 1, y + 1, width - 1, height - 1, startangle, sweepangle);
{
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) public void DrawPixel(int x, int y, Color? color = null)
{ {
using (var g = GetGraphics()) try
{ {
try using var g = GetGraphics();
{ g.DrawLine(GetPen(color ?? _defaultForeground), x, y, x + 0.1F, y);
g.DrawLine(GetPen(color ?? _defaultForeground), x, y, x + 0.1F, y); }
} catch (Exception)
catch (Exception) {
{ // ignored
return;
}
} }
} }
public void DrawPolygon(Point[] points, Color? line = null, Color? background = null) public void DrawPolygon(Point[] points, Color? line = null, Color? background = null)
{ {
using (var g = GetGraphics()) try
{ {
try using var g = GetGraphics();
{ g.DrawPolygon(GetPen(line ?? _defaultForeground), points);
g.DrawPolygon(GetPen(line ?? _defaultForeground), points); var bg = background ?? _defaultBackground;
var bg = background ?? _defaultBackground; if (bg != null) g.FillPolygon(GetBrush(bg.Value), points);
if (bg.HasValue) }
{ catch (Exception)
g.FillPolygon(GetBrush(bg.Value), points); {
} // ignored
}
catch (Exception)
{
return;
}
} }
} }
public void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null) public void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null)
{ {
using (var g = GetGraphics()) using var g = GetGraphics();
{ var w = Math.Max(width, 0.1F);
var w = Math.Max(width, 0.1F); var h = Math.Max(height, 0.1F);
var h = Math.Max(height, 0.1F); g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, w, h);
g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, w, h); var bg = background ?? _defaultBackground;
var bg = background ?? _defaultBackground; if (bg != null) g.FillRectangle(GetBrush(bg.Value), x + 1, y + 1, Math.Max(w - 1, 0), Math.Max(h - 1, 0));
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, 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)
string fontfamily = null, string fontstyle = null, string horizalign = null, string vertalign = null)
{ {
using (var g = GetGraphics()) try
{ {
try var family = fontfamily != null ? new FontFamily(fontfamily) : FontFamily.GenericMonospace;
var fstyle = fontstyle?.ToLower() switch
{ {
var family = FontFamily.GenericMonospace; "bold" => FontStyle.Bold,
if (fontfamily != null) "italic" => FontStyle.Italic,
{ "strikethrough" => FontStyle.Strikeout,
family = new FontFamily(fontfamily); "underline" => FontStyle.Underline,
} _ => FontStyle.Regular
};
var fstyle = FontStyle.Regular; using var g = GetGraphics();
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. // 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 // 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);
var font = new Font(family, fontsize ?? 12, fstyle, GraphicsUnit.Pixel); var sizeOfText = g.MeasureString(message, font, 0, new StringFormat(StringFormat.GenericDefault)).ToSize();
Size sizeOfText = g.MeasureString(message, font, 0, f).ToSize();
if (horizalign != null)
{
switch (horizalign.ToLower())
{
default:
case "left":
break;
case "center":
case "middle":
x -= sizeOfText.Width / 2;
break;
case "right":
x -= sizeOfText.Width;
break;
}
}
if (vertalign != null) switch (horizalign?.ToLower())
{
switch (vertalign.ToLower())
{
default:
case "top":
break;
case "center":
case "middle":
y -= sizeOfText.Height / 2;
break;
case "bottom":
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; default:
case "left":
break;
case "center":
case "middle":
x -= sizeOfText.Width / 2;
break;
case "right":
x -= sizeOfText.Width;
break;
} }
switch (vertalign?.ToLower())
{
default:
case "top":
break;
case "center":
case "middle":
y -= sizeOfText.Height / 2;
break;
case "bottom":
y -= sizeOfText.Height;
break;
}
var bg = backcolor ?? _defaultBackground;
if (bg != null)
{
var brush = GetBrush(bg.Value);
for (var xd = -1; xd <= 1; xd++) for (var yd = -1; yd <= 1; yd++)
{
g.DrawString(message, font, brush, x + xd, y + yd);
}
}
g.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
g.DrawString(message, font, GetBrush(forecolor ?? _defaultForeground), x, y);
}
catch (Exception)
{
// ignored
} }
} }
public void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null) public void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null)
{ {
using (var g = GetGraphics()) try
{ {
try int index;
switch (fontfamily)
{ {
var index = 0; case "fceux":
if (string.IsNullOrEmpty(fontfamily)) case "0":
{ index = 0;
index = _defaultPixelFont; break;
} case "gens":
else case "1":
{ index = 1;
switch (fontfamily) break;
default:
if (!string.IsNullOrEmpty(fontfamily)) // not a typo
{ {
case "fceux": LogCallback($"Unable to find font family: {fontfamily}");
case "0": return;
index = 0;
break;
case "gens":
case "1":
index = 1;
break;
default:
LogCallback($"Unable to find font family: {fontfamily}");
return;
} }
} index = _defaultPixelFont;
break;
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;
} }
using var g = GetGraphics();
var font = new Font(GlobalWin.DisplayManager.CustomFonts.Families[index], 8, FontStyle.Regular, GraphicsUnit.Pixel);
var sizeOfText = g.MeasureString(
message,
font,
0,
new StringFormat(StringFormat.GenericTypographic) { FormatFlags = StringFormatFlags.MeasureTrailingSpaces }
).ToSize();
if (backcolor.HasValue) g.FillRectangle(GetBrush(backcolor.Value), new Rectangle(new Point(x, y), sizeOfText + new Size(1, 0)));
g.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
g.DrawString(message, font, GetBrush(forecolor ?? _defaultForeground), x, y);
}
catch (Exception)
{
// ignored
} }
} }
public void Text(int x, int y, string message, Color? forecolor = null, string anchor = null) public void Text(int x, int y, string message, Color? forecolor = null, string anchor = null)
{ {
var a = 0; int a = default;
if (!string.IsNullOrEmpty(anchor)) if (!string.IsNullOrEmpty(anchor))
{ {
switch (anchor) a = anchor switch
{ {
case "0": "0" => 0,
case "topleft": "topleft" => 0,
a = 0; "1" => 1,
break; "topright" => 1,
case "1": "2" => 2,
case "topright": "bottomleft" => 2,
a = 1; "3" => 3,
break; "bottomright" => 3,
case "2": _ => default
case "bottomleft": };
a = 2;
break;
case "3":
case "bottomright":
a = 3;
break;
}
} }
else else
{ {
x -= Emulator.CoreComm.ScreenLogicalOffsetX; x -= Emulator.CoreComm.ScreenLogicalOffsetX;
y -= Emulator.CoreComm.ScreenLogicalOffsetY; y -= Emulator.CoreComm.ScreenLogicalOffsetY;
} }
GlobalWin.OSD.AddGuiText(message, x, y, Color.Black, forecolor ?? Color.White, a); GlobalWin.OSD.AddGuiText(message, x, y, Color.Black, forecolor ?? Color.White, a);
} }
public void Dispose()
{
foreach (var brush in _solidBrushes.Values) brush.Dispose();
foreach (var brush in _pens.Values) brush.Dispose();
}
} }
} }

View File

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

View File

@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using BizHawk.Client.ApiHawk;
using BizHawk.Client.Common; using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk namespace BizHawk.Client.EmuHawk
@ -23,32 +21,21 @@ namespace BizHawk.Client.EmuHawk
if (!File.Exists(path)) if (!File.Exists(path))
{ {
LogCallback($"could not find file: {path}"); LogCallback($"could not find file: {path}");
return;
} }
else GlobalWin.MainForm.LoadState(path, Path.GetFileName(path), true, suppressOSD);
{
GlobalWin.MainForm.LoadState(path, Path.GetFileName(path), true, suppressOSD);
}
} }
public void LoadSlot(int slotNum, bool suppressOSD) public void LoadSlot(int slotNum, bool suppressOSD)
{ {
if (slotNum >= 0 && slotNum <= 9) if (0 <= slotNum && slotNum <= 9) GlobalWin.MainForm.LoadQuickSave($"QuickSave{slotNum}", true, suppressOSD);
{
GlobalWin.MainForm.LoadQuickSave($"QuickSave{slotNum}", true, suppressOSD);
}
} }
public void Save(string path, bool suppressOSD) public void Save(string path, bool suppressOSD) => GlobalWin.MainForm.SaveState(path, path, true, suppressOSD);
{
GlobalWin.MainForm.SaveState(path, path, true, suppressOSD);
}
public void SaveSlot(int slotNum, bool suppressOSD) public void SaveSlot(int slotNum, bool suppressOSD)
{ {
if (slotNum >= 0 && slotNum <= 9) if (0 <= slotNum && slotNum <= 9) GlobalWin.MainForm.SaveQuickSave($"QuickSave{slotNum}", true, suppressOSD);
{
GlobalWin.MainForm.SaveQuickSave($"QuickSave{slotNum}", true, suppressOSD);
}
} }
} }
} }

View File

@ -1,162 +1,38 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Client.ApiHawk;
using BizHawk.Client.Common; using BizHawk.Client.Common;
using BizHawk.Common;
namespace BizHawk.Client.EmuHawk namespace BizHawk.Client.EmuHawk
{ {
public sealed class ToolApi : ITool 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) public Type GetTool(string name)
{ {
var toolType = ReflectionUtil.GetTypeByName(name) var toolType = ReflectionUtil.GetTypeByName(name).FirstOrDefault(x => typeof(IToolForm).IsAssignableFrom(x) && !x.IsInterface);
.FirstOrDefault(x => typeof(IToolForm).IsAssignableFrom(x) && !x.IsInterface); if (toolType != null) GlobalWin.Tools.Load(toolType);
return GlobalWin.Tools.AvailableTools.FirstOrDefault(tool => tool.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
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) public object CreateInstance(string name)
{ {
var possibleTypes = ReflectionUtil.GetTypeByName(name); var found = ReflectionUtil.GetTypeByName(name).FirstOrDefault();
return found != null ? Activator.CreateInstance(found) : null;
if (possibleTypes.Any())
{
return Activator.CreateInstance(possibleTypes.First());
}
return null;
} }
public void OpenCheats() public void OpenCheats() => GlobalWin.Tools.Load<Cheats>();
{
ToolStatic.OpenCheats();
}
public void OpenHexEditor() public void OpenHexEditor() => GlobalWin.Tools.Load<HexEditor>();
{
ToolStatic.OpenHexEditor();
}
public void OpenRamWatch() public void OpenRamWatch() => GlobalWin.Tools.LoadRamWatch(loadDialog: true);
{
ToolStatic.OpenRamWatch();
}
public void OpenRamSearch() public void OpenRamSearch() => GlobalWin.Tools.Load<RamSearch>();
{
ToolStatic.OpenRamSearch();
}
public void OpenTasStudio() public void OpenTasStudio() => GlobalWin.Tools.Load<TAStudio>();
{
ToolStatic.OpenTasStudio();
}
public void OpenToolBox() public void OpenToolBox() => GlobalWin.Tools.Load<ToolBox>();
{
ToolStatic.OpenToolBox();
}
public void OpenTraceLogger() public void OpenTraceLogger() => GlobalWin.Tools.Load<TraceLogger>();
{
ToolStatic.OpenTraceLogger();
}
} }
} }

View File

@ -21,9 +21,7 @@ namespace BizHawk.Client.EmuHawk
public override string Name => "gui"; public override string Name => "gui";
#region Gui API public bool HasLuaSurface => APIs.Gui.HasGUISurface;
public void Dispose() => APIs.Gui.Dispose();
public bool SurfaceIsNull => !APIs.Gui.HasGUISurface; public bool SurfaceIsNull => !APIs.Gui.HasGUISurface;
@ -35,10 +33,6 @@ namespace BizHawk.Client.EmuHawk
[LuaMethod("DrawFinish", "Finishes drawing to the current lua surface and causes it to get displayed.")] [LuaMethod("DrawFinish", "Finishes drawing to the current lua surface and causes it to get displayed.")]
public void DrawFinish() => APIs.Gui.DrawFinish(); public void DrawFinish() => APIs.Gui.DrawFinish();
public bool HasLuaSurface => APIs.Gui.HasGUISurface;
#endregion
[LuaMethodExample("gui.addmessage( \"Some message\" );")] [LuaMethodExample("gui.addmessage( \"Some message\" );")]
[LuaMethod("addmessage", "Adds a message to the OSD's message area")] [LuaMethod("addmessage", "Adds a message to the OSD's message area")]
public void AddMessage(string message) => APIs.Gui.AddMessage(message); public void AddMessage(string message) => APIs.Gui.AddMessage(message);
@ -194,5 +188,7 @@ namespace BizHawk.Client.EmuHawk
canvas.Show(); canvas.Show();
return Lua.TableFromObject(canvas); return Lua.TableFromObject(canvas);
} }
public void Dispose() => APIs.Gui.Dispose();
} }
} }