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.ComponentModel;
using System.Collections.Generic;
using System.ComponentModel;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
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.SNES;
using BizHawk.Emulation.Cores.PCEngine;
using BizHawk.Emulation.Cores.Consoles.Sega.gpgx;
using BizHawk.Emulation.Cores.Sega.MasterSystem;
using BizHawk.Emulation.Cores.WonderSwan;
using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES;
namespace BizHawk.Client.Common
{
[Description("A library for interacting with the currently loaded emulator core")]
public sealed class 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)
{
LogCallback = logCallback;
@ -27,415 +47,248 @@ namespace BizHawk.Client.Common
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 YieldCallback { get; set; }
public void DisplayVsync(bool enabled)
{
EmuStatic.DisplayVsync(enabled);
}
public void DisplayVsync(bool enabled) => Global.Config.VSync = enabled;
public void FrameAdvance()
{
FrameAdvanceCallback();
}
public void FrameAdvance() => FrameAdvanceCallback();
public int FrameCount()
{
return Emulator.Frame;
}
public int FrameCount() => Emulator.Frame;
public object Disassemble(uint pc, string name = "")
{
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)
{
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()
{
var table = new Dictionary<string, ulong>();
try
{
if (DebuggableCore == null)
if (DebuggableCore != null)
{
throw new NotImplementedException();
}
foreach (var kvp in DebuggableCore.GetCpuFlagsAndRegisters())
{
table[kvp.Key] = kvp.Value.Value;
var table = new Dictionary<string, ulong>();
foreach (var kvp in DebuggableCore.GetCpuFlagsAndRegisters()) table[kvp.Key] = kvp.Value.Value;
return table;
}
}
catch (NotImplementedException)
{
LogCallback($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.GetCpuFlagsAndRegisters)}()");
}
return table;
catch (NotImplementedException) {}
LogCallback($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.GetCpuFlagsAndRegisters)}()");
return new Dictionary<string, ulong>();
}
public void SetRegister(string register, int value)
{
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()
{
try
{
if (DebuggableCore == null)
{
throw new NotImplementedException();
}
return DebuggableCore.TotalExecutedCycles;
}
catch (NotImplementedException)
{
LogCallback($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.TotalExecutedCycles)}()");
return 0;
if (DebuggableCore != null) return DebuggableCore.TotalExecutedCycles;
}
catch (NotImplementedException) {}
LogCallback($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.TotalExecutedCycles)}()");
return default;
}
public string GetSystemId()
{
return EmuStatic.GetSystemId();
}
public string GetSystemId() => Global.Game.System;
public bool IsLagged()
{
if (InputPollableCore != null)
{
return InputPollableCore.IsLagFrame;
}
if (InputPollableCore != null) return InputPollableCore.IsLagFrame;
LogCallback($"Can not get lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
return false;
}
public void SetIsLagged(bool value = true)
{
if (InputPollableCore != null)
{
InputPollableCore.IsLagFrame = value;
}
else
{
LogCallback($"Can not set lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
}
if (InputPollableCore != null) InputPollableCore.IsLagFrame = value;
else LogCallback($"Can not set lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
}
public int LagCount()
{
if (InputPollableCore != null)
{
return InputPollableCore.LagCount;
}
if (InputPollableCore != null) return InputPollableCore.LagCount;
LogCallback($"Can not get lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
return 0;
return default;
}
public void SetLagCount(int count)
{
if (InputPollableCore != null)
{
InputPollableCore.LagCount = count;
}
else
{
LogCallback($"Can not set lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
}
if (InputPollableCore != null) 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)
{
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();
s.DrawBGA = args[0];
s.DrawBGB = args[1];
s.DrawBGW = args[2];
s.DrawObj = args[3];
gpgx.PutSettings(s);
var s = core.GetSettings();
s.ShowBG1_0 = s.ShowBG1_1 = GetSetting(args, 0);
s.ShowBG2_0 = s.ShowBG2_1 = GetSetting(args, 1);
s.ShowBG3_0 = s.ShowBG3_1 = GetSetting(args, 2);
s.ShowBG4_0 = s.ShowBG4_1 = GetSetting(args, 3);
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)
{
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)
void SetCygne(WonderSwan ws)
{
var s = ws.GetSettings();
s.EnableSprites = GetSetting(0, args);
s.EnableFG = GetSetting(1, args);
s.EnableBG = GetSetting(2, args);
s.EnableSprites = GetSetting(args, 0);
s.EnableFG = GetSetting(args, 1);
s.EnableBG = GetSetting(args, 2);
ws.PutSettings(s);
}
}
private static bool GetSetting(int index, bool[] settings)
{
return index >= settings.Length || settings[index];
void SetGPGX(GPGX core)
{
var s = core.GetSettings();
s.DrawBGA = GetSetting(args, 0);
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 BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
@ -10,53 +9,23 @@ namespace BizHawk.Client.Common
[OptionalService]
private IBoardInfo BoardInfo { get; set; }
public string GetRomName()
{
return Global.Game?.Name ?? "";
}
public string GetRomName() => Global.Game?.Name ?? "";
public string GetRomHash()
{
return Global.Game?.Hash ?? "";
}
public string GetRomHash() => Global.Game?.Hash ?? "";
public bool InDatabase()
{
if (Global.Game != null)
{
return !Global.Game.NotInDatabase;
}
public bool InDatabase() => Global.Game?.NotInDatabase == false;
return false;
}
public string GetStatus() => Global.Game?.Status.ToString();
public string GetStatus()
{
return Global.Game?.Status.ToString();
}
public bool IsStatusBad() => Global.Game?.IsRomStatusBad() != false;
public bool IsStatusBad()
{
return Global.Game?.IsRomStatusBad() ?? true;
}
public string GetBoardType()
{
return BoardInfo?.BoardName ?? "";
}
public string GetBoardType() => BoardInfo?.BoardName ?? "";
public Dictionary<string, string> GetOptions()
{
var options = new Dictionary<string, string>();
if (Global.Game != null)
{
foreach (var option in Global.Game.GetOptionsDict())
{
options[option.Key] = option.Value;
}
}
if (Global.Game == null) return options;
foreach (var option in Global.Game.GetOptionsDict()) options[option.Key] = option.Value;
return options;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -2,8 +2,6 @@
using System.Collections.Generic;
using System.IO;
using BizHawk.Client.Common;
namespace BizHawk.Client.Common
{
public sealed class MovieApi : IInputMovie
@ -17,99 +15,9 @@ namespace BizHawk.Client.Common
private readonly Action<string> LogCallback;
private static class MoviePluginStatic
{
public static string Filename()
{
return Global.MovieSession.Movie.Filename;
}
public bool StartsFromSavestate() => Global.MovieSession.Movie.IsActive && Global.MovieSession.Movie.StartsFromSavestate;
public static bool GetReadOnly()
{
return Global.MovieSession.ReadOnly;
}
public static ulong GetRerecordCount()
{
return Global.MovieSession.Movie.Rerecords;
}
public static bool GetRerecordCounting()
{
return Global.MovieSession.Movie.IsCountingRerecords;
}
public static bool IsLoaded()
{
return Global.MovieSession.Movie.IsActive;
}
public static double Length()
{
return Global.MovieSession.Movie.FrameCount;
}
public static string Mode()
{
if (Global.MovieSession.Movie.IsFinished)
{
return "FINISHED";
}
if (Global.MovieSession.Movie.IsPlaying)
{
return "PLAY";
}
if (Global.MovieSession.Movie.IsRecording)
{
return "RECORD";
}
return "INACTIVE";
}
public static void SetReadOnly(bool readOnly)
{
Global.MovieSession.ReadOnly = readOnly;
}
public static void 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 bool StartsFromSaveram() => Global.MovieSession.Movie.IsActive && Global.MovieSession.Movie.StartsFromSaveRam;
public Dictionary<string, dynamic> GetInput(int frame)
{
@ -118,161 +26,102 @@ namespace BizHawk.Client.Common
LogCallback("No movie loaded");
return null;
}
var input = new Dictionary<string, dynamic>();
var adapter = Global.MovieSession.Movie.GetInputState(frame);
if (adapter == null)
{
LogCallback("Can't get input of the last frame of the movie. Use the previous frame");
return null;
}
foreach (var button in adapter.Definition.BoolButtons)
{
input[button] = adapter.IsPressed(button);
}
foreach (var button in adapter.Definition.FloatControls)
{
input[button] = adapter.GetFloat(button);
}
var input = new Dictionary<string, dynamic>();
foreach (var button in adapter.Definition.BoolButtons) input[button] = adapter.IsPressed(button);
foreach (var button in adapter.Definition.FloatControls) input[button] = adapter.GetFloat(button);
return input;
}
public string GetInputAsMnemonic(int frame)
{
if (Global.MovieSession.Movie.IsActive && frame < Global.MovieSession.Movie.InputLogLength)
{
var lg = Global.MovieSession.LogGeneratorInstance();
lg.SetSource(Global.MovieSession.Movie.GetInputState(frame));
return lg.GenerateLogEntry();
}
return "";
if (!Global.MovieSession.Movie.IsActive || frame >= Global.MovieSession.Movie.InputLogLength) return string.Empty;
var lg = Global.MovieSession.LogGeneratorInstance();
lg.SetSource(Global.MovieSession.Movie.GetInputState(frame));
return lg.GenerateLogEntry();
}
public void Save(string filename = "")
public void Save(string filename = null)
{
if (!Global.MovieSession.Movie.IsActive)
{
return;
}
if (!Global.MovieSession.Movie.IsActive) return;
if (!string.IsNullOrEmpty(filename))
{
filename += $".{Global.MovieSession.Movie.PreferredExtension}";
var test = new FileInfo(filename);
if (test.Exists)
if (new FileInfo(filename).Exists)
{
LogCallback($"File {filename} already exists, will not overwrite");
return;
}
Global.MovieSession.Movie.Filename = filename;
}
Global.MovieSession.Movie.Save();
}
public Dictionary<string,string> GetHeader()
public Dictionary<string, string> GetHeader()
{
var table = new Dictionary<string,string>();
if (Global.MovieSession.Movie.IsActive)
{
foreach (var kvp in Global.MovieSession.Movie.HeaderEntries)
{
table[kvp.Key] = kvp.Value;
}
}
var table = new Dictionary<string, string>();
if (!Global.MovieSession.Movie.IsActive) return table;
foreach (var kvp in Global.MovieSession.Movie.HeaderEntries) table[kvp.Key] = kvp.Value;
return table;
}
public List<string> GetComments()
{
var list = new List<string>(Global.MovieSession.Movie.Comments.Count);
if (Global.MovieSession.Movie.IsActive)
{
for (int i = 0; i < Global.MovieSession.Movie.Comments.Count; i++)
{
list[i] = Global.MovieSession.Movie.Comments[i];
}
}
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];
return list;
}
public List<string> GetSubtitles()
{
var list = new List<string>(Global.MovieSession.Movie.Subtitles.Count);
if (Global.MovieSession.Movie.IsActive)
{
for (int i = 0; i < Global.MovieSession.Movie.Subtitles.Count; i++)
{
list[i] = Global.MovieSession.Movie.Subtitles[i].ToString();
}
}
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();
return list;
}
public string Filename()
{
return MoviePluginStatic.Filename();
}
public string Filename() => Global.MovieSession.Movie.Filename;
public bool GetReadOnly()
{
return MoviePluginStatic.GetReadOnly();
}
public bool GetReadOnly() => Global.MovieSession.ReadOnly;
public ulong GetRerecordCount()
{
return MoviePluginStatic.GetRerecordCount();
}
public ulong GetRerecordCount() => Global.MovieSession.Movie.Rerecords;
public bool GetRerecordCounting()
{
return MoviePluginStatic.GetRerecordCounting();
}
public bool GetRerecordCounting() => Global.MovieSession.Movie.IsCountingRerecords;
public bool IsLoaded()
{
return MoviePluginStatic.IsLoaded();
}
public bool IsLoaded() => Global.MovieSession.Movie.IsActive;
public double Length()
{
return MoviePluginStatic.Length();
}
public double Length() => Global.MovieSession.Movie.FrameCount;
public string Mode()
{
return MoviePluginStatic.Mode();
}
public string Mode() => Global.MovieSession.Movie.IsFinished
? "FINISHED"
: Global.MovieSession.Movie.IsPlaying
? "PLAY"
: Global.MovieSession.Movie.IsRecording
? "RECORD"
: "INACTIVE";
public void SetReadOnly(bool readOnly)
{
MoviePluginStatic.SetReadOnly(readOnly);
}
public void SetReadOnly(bool readOnly) => Global.MovieSession.ReadOnly = readOnly;
public void SetRerecordCount(ulong count) => Global.MovieSession.Movie.Rerecords = count;
public void SetRerecordCounting(bool counting)
{
MoviePluginStatic.SetRerecordCounting(counting);
}
public void SetRerecordCounting(bool counting) => Global.MovieSession.Movie.IsCountingRerecords = counting;
public void Stop()
{
MoviePluginStatic.Stop();
}
public void Stop() => Global.MovieSession.Movie.Stop();
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;
namespace BizHawk.Client.Common
{
public sealed class SqlApi : ISql
{
SQLiteConnection _dbConnection;
private SQLiteConnection _dbConnection;
public string CreateDatabase(string name)
{
try
{
SQLiteConnection.CreateFile(name);
return "Database Created Successfully";
}
catch (SQLiteException sqlEx)
{
return sqlEx.Message;
}
return "Database Created Successfully";
}
public string OpenDatabase(string name)
{
try
{
var connBuilder = new SQLiteConnectionStringBuilder
{
DataSource = name,
Version = 3,
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
SyncMode = SynchronizationModes.Off // This shortens the delay for do synchronous calls.
};
_dbConnection = new SQLiteConnection(connBuilder.ToString());
_dbConnection = new SQLiteConnection(
new SQLiteConnectionStringBuilder {
DataSource = name,
Version = 3,
JournalMode = SQLiteJournalModeEnum.Wal, // Allows for reads and writes to happen at the same time
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.
}.ToString()
);
_dbConnection.Open();
_dbConnection.Close();
return "Database Opened Successfully";
}
catch (SQLiteException sqlEx)
{
return sqlEx.Message;
}
_dbConnection?.Close();
return "Database Opened Successfully";
}
public string WriteCommand(string query = "")
public string WriteCommand(string query = null)
{
if (string.IsNullOrWhiteSpace(query))
{
return "query is empty";
}
if (string.IsNullOrWhiteSpace(query)) return "query is empty";
if (_dbConnection == null) return "Database not open.";
string result;
try
{
_dbConnection.Open();
var command = new SQLiteCommand(query, _dbConnection);
command.ExecuteNonQuery();
_dbConnection.Close();
return "Command ran successfully";
}
catch (NullReferenceException)
{
return "Database not open.";
new SQLiteCommand(query, _dbConnection).ExecuteNonQuery();
result = "Command ran successfully";
}
catch (SQLiteException sqlEx)
{
_dbConnection.Close();
return sqlEx.Message;
result = sqlEx.Message;
}
_dbConnection.Close();
return result;
}
public dynamic ReadCommand(string query = "")
public dynamic ReadCommand(string query = null)
{
if (string.IsNullOrWhiteSpace(query))
{
return "query is empty";
}
if (string.IsNullOrWhiteSpace(query)) return "query is empty";
if (_dbConnection == null) return "Database not open.";
dynamic result;
try
{
var table = new Dictionary<string, object>();
_dbConnection.Open();
string sql = $"PRAGMA read_uncommitted =1;{query}";
using var command = new SQLiteCommand(sql, _dbConnection);
SQLiteDataReader reader = command.ExecuteReader();
bool rows = reader.HasRows;
long rowCount = 0;
var columns = new List<string>();
for (int i = 0; i < reader.FieldCount; ++i) //Add all column names into list
using var command = new SQLiteCommand($"PRAGMA read_uncommitted =1;{query}", _dbConnection);
using var reader = command.ExecuteReader();
if (reader.HasRows)
{
columns.Add(reader.GetName(i));
}
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; ++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;
var table = new Dictionary<string, object>();
while (reader.Read())
{
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++;
}
rowCount += 1;
reader.Close();
result = table;
}
reader.Close();
_dbConnection.Close();
if (rows == false)
else
{
return "No rows found";
result = "No rows found";
}
return table;
}
catch (NullReferenceException)
{
return "Database not opened.";
}
catch (SQLiteException sqlEx)
{
_dbConnection.Close();
return sqlEx.Message;
result = sqlEx.Message;
}
_dbConnection.Close();
return result;
}
}
}

View File

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

View File

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

View File

@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Drawing.Text;
using System.IO;
using System.Windows.Forms;
using BizHawk.Client.ApiHawk;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
@ -15,13 +16,6 @@ namespace BizHawk.Client.EmuHawk
{
[RequiredService]
private IEmulator Emulator { get; set; }
private Color _defaultForeground = Color.White;
private Color? _defaultBackground;
private Color? _defaultTextBackground = Color.FromArgb(128, 0, 0, 0);
private int _defaultPixelFont = 1; // gens
private Padding _padding = new Padding(0);
private ImageAttributes _attributes = new ImageAttributes();
private System.Drawing.Drawing2D.CompositingMode _compositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
public GuiApi(Action<string> logCallback)
{
@ -32,38 +26,57 @@ namespace BizHawk.Client.EmuHawk
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;
#region Gui API
public void ToggleCompositingMode()
{
_compositingMode = 1 - _compositingMode;
}
private SolidBrush GetBrush(Color color) => _solidBrushes.TryGetValue(color, out var b) ? b : (_solidBrushes[color] = new SolidBrush(color));
public ImageAttributes GetAttributes()
{
return _attributes;
}
public void SetAttributes(ImageAttributes a)
{
_attributes = a;
}
private Pen GetPen(Color color) => _pens.TryGetValue(color, out var p) ? p : (_pens[color] = new Pen(color));
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();
}
foreach (var brush in _pens.Values)
{
brush.Dispose();
var transform = g.Transform;
transform.Translate(-tx, -ty);
g.Transform = transform;
}
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)
{
try
@ -79,82 +92,19 @@ namespace BizHawk.Client.EmuHawk
public void DrawFinish()
{
if (_GUISurface != null)
{
GlobalWin.DisplayManager.UnlockLuaSurface(_GUISurface);
}
if (_GUISurface != null) GlobalWin.DisplayManager.UnlockLuaSurface(_GUISurface);
_GUISurface = null;
}
#endregion
#region Helpers
private readonly Dictionary<string, Image> _imageCache = new Dictionary<string, Image>();
private readonly Dictionary<Color, SolidBrush> _solidBrushes = new Dictionary<Color, SolidBrush>();
private readonly Dictionary<Color, Pen> _pens = new Dictionary<Color, Pen>();
private 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;
}
public void SetPadding(int all) => _padding = new Padding(all);
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)
{
Pen p;
if (!_pens.TryGetValue(color, out p))
{
p = new Pen(color);
_pens[color] = p;
}
public void SetPadding(int l, int t, int r, int b) => _padding = new Padding(l, t, r, b);
return p;
}
public Padding GetPadding() => _padding;
private Graphics GetGraphics()
{
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 AddMessage(string message) => GlobalWin.OSD.AddMessage(message);
public void ClearGraphics()
{
@ -162,27 +112,15 @@ namespace BizHawk.Client.EmuHawk
DrawFinish();
}
public void ClearText()
{
GlobalWin.OSD.ClearGuiText();
}
public void ClearText() => GlobalWin.OSD.ClearGuiText();
public void SetDefaultForegroundColor(Color color)
{
_defaultForeground = color;
}
public void SetDefaultForegroundColor(Color color) => _defaultForeground = color;
public void SetDefaultBackgroundColor(Color color)
{
_defaultBackground = color;
}
public void SetDefaultBackgroundColor(Color color) => _defaultBackground = color;
public Color? GetDefaultTextBackground() => _defaultTextBackground;
public void SetDefaultTextBackground(Color color)
{
_defaultTextBackground = color;
}
public void SetDefaultTextBackground(Color color) => _defaultTextBackground = color;
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)
{
using (var g = GetGraphics())
try
{
try
{
g.CompositingMode = _compositingMode;
g.DrawBezier(GetPen(color ?? _defaultForeground), p1, p2, p3, p4);
}
catch (Exception)
{
return;
}
using var g = GetGraphics();
g.CompositingMode = _compositingMode;
g.DrawBezier(GetPen(color ?? _defaultForeground), p1, p2, p3, p4);
}
catch (Exception)
{
// ignored
}
}
public void DrawBeziers(Point[] points, Color? color = null)
{
using (var g = GetGraphics())
try
{
try
{
g.CompositingMode = _compositingMode;
g.DrawBeziers(GetPen(color ?? _defaultForeground), points);
}
catch (Exception)
{
return;
}
using var g = GetGraphics();
g.CompositingMode = _compositingMode;
g.DrawBeziers(GetPen(color ?? _defaultForeground), points);
}
catch (Exception)
{
// ignored
}
}
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;
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));
}
w = x2 - x;
}
catch (Exception)
else
{
// need to stop the script from here
return;
x2 = x - x2;
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)
{
using (var g = GetGraphics())
try
{
try
{
var bg = background ?? _defaultBackground;
if (bg.HasValue)
{
var brush = GetBrush(bg.Value);
g.FillEllipse(brush, x, y, width, height);
}
g.CompositingMode = _compositingMode;
g.DrawEllipse(GetPen(line ?? _defaultForeground), x, y, width, height);
}
catch (Exception)
{
// need to stop the script from here
return;
}
using var g = GetGraphics();
var bg = background ?? _defaultBackground;
if (bg != null) g.FillEllipse(GetBrush(bg.Value), x, y, width, height);
g.CompositingMode = _compositingMode;
g.DrawEllipse(GetPen(line ?? _defaultForeground), x, y, width, height);
}
catch (Exception)
{
// need to stop the script from here
}
}
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))
{
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)
if (!File.Exists(path))
{
AddMessage($"File not found: {path}");
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}");
return;
}
using (var g = GetGraphics())
{
Image img;
if (_imageCache.ContainsKey(path))
{
img = _imageCache[path];
}
else
{
img = Image.FromFile(path);
if (cache)
{
_imageCache.Add(path, img);
}
}
var destRect = new Rectangle(x, y, width ?? img.Width, height ?? img.Height);
g.CompositingMode = _compositingMode;
g.DrawImage(img, destRect, 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, _attributes);
}
using var g = GetGraphics();
var isCached = _imageCache.ContainsKey(path);
var img = isCached ? _imageCache[path] : Image.FromFile(path);
if (!isCached && cache) _imageCache[path] = img;
g.CompositingMode = _compositingMode;
g.DrawImage(
img,
new Rectangle(x, y, width ?? img.Width, height ?? img.Height),
0,
0,
img.Width,
img.Height,
GraphicsUnit.Pixel,
_attributes
);
}
public void ClearImageCache()
{
foreach (var image in _imageCache)
{
image.Value.Dispose();
}
foreach (var image in _imageCache) image.Value.Dispose();
_imageCache.Clear();
}
@ -383,34 +284,25 @@ namespace BizHawk.Client.EmuHawk
LogCallback($"File not found: {path}");
return;
}
using (var g = GetGraphics())
{
Image img;
if (_imageCache.ContainsKey(path))
{
img = _imageCache[path];
}
else
{
img = Image.FromFile(path);
_imageCache.Add(path, img);
}
var destRect = new Rectangle(dest_x, dest_y, dest_width ?? source_width, dest_height ?? source_height);
g.CompositingMode = _compositingMode;
g.DrawImage(img, destRect, source_x, source_y, source_width, source_height, GraphicsUnit.Pixel, _attributes);
}
using var g = GetGraphics();
g.CompositingMode = _compositingMode;
g.DrawImage(
_imageCache.TryGetValue(path, out var img) ? img : (_imageCache[path] = Image.FromFile(path)),
new Rectangle(dest_x, dest_y, dest_width ?? source_width, dest_height ?? source_height),
source_x,
source_y,
source_width,
source_height,
GraphicsUnit.Pixel,
_attributes
);
}
public void DrawLine(int x1, int y1, int x2, int y2, Color? color = null)
{
using (var g = GetGraphics())
{
g.CompositingMode = _compositingMode;
g.DrawLine(GetPen(color ?? _defaultForeground), x1, y1, x2, y2);
}
using var g = GetGraphics();
g.CompositingMode = _compositingMode;
g.DrawLine(GetPen(color ?? _defaultForeground), x1, y1, x2, y2);
}
public void DrawAxis(int x, int y, int size, Color? color = null)
@ -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)
{
using (var g = GetGraphics())
{
g.CompositingMode = _compositingMode;
var bg = background ?? _defaultBackground;
if (bg.HasValue)
{
var brush = GetBrush(bg.Value);
g.FillPie(brush, x, y, width, height, startangle, sweepangle);
}
g.DrawPie(GetPen(line ?? _defaultForeground), x + 1, y + 1, width - 1, height - 1, startangle, sweepangle);
}
using var g = GetGraphics();
g.CompositingMode = _compositingMode;
var bg = background ?? _defaultBackground;
if (bg != null) g.FillPie(GetBrush(bg.Value), x, y, width, height, startangle, sweepangle);
g.DrawPie(GetPen(line ?? _defaultForeground), x + 1, y + 1, width - 1, height - 1, startangle, sweepangle);
}
public void DrawPixel(int x, int y, Color? color = null)
{
using (var g = GetGraphics())
try
{
try
{
g.DrawLine(GetPen(color ?? _defaultForeground), x, y, x + 0.1F, y);
}
catch (Exception)
{
return;
}
using var g = GetGraphics();
g.DrawLine(GetPen(color ?? _defaultForeground), x, y, x + 0.1F, y);
}
catch (Exception)
{
// ignored
}
}
public void DrawPolygon(Point[] points, Color? line = null, Color? background = null)
{
using (var g = GetGraphics())
try
{
try
{
g.DrawPolygon(GetPen(line ?? _defaultForeground), points);
var bg = background ?? _defaultBackground;
if (bg.HasValue)
{
g.FillPolygon(GetBrush(bg.Value), points);
}
}
catch (Exception)
{
return;
}
using var g = GetGraphics();
g.DrawPolygon(GetPen(line ?? _defaultForeground), points);
var bg = background ?? _defaultBackground;
if (bg != null) g.FillPolygon(GetBrush(bg.Value), points);
}
catch (Exception)
{
// ignored
}
}
public void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null)
{
using (var g = GetGraphics())
{
var w = Math.Max(width, 0.1F);
var h = Math.Max(height, 0.1F);
g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, w, h);
var bg = background ?? _defaultBackground;
if (bg.HasValue)
{
g.FillRectangle(GetBrush(bg.Value), x + 1, y + 1, Math.Max(w - 1, 0), Math.Max(h - 1, 0));
}
}
using var g = GetGraphics();
var w = Math.Max(width, 0.1F);
var h = Math.Max(height, 0.1F);
g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, w, h);
var bg = background ?? _defaultBackground;
if (bg != null) g.FillRectangle(GetBrush(bg.Value), x + 1, y + 1, Math.Max(w - 1, 0), Math.Max(h - 1, 0));
}
public void DrawString(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, int? fontsize = null,
string fontfamily = null, string fontstyle = null, string horizalign = null, string vertalign = null)
public void DrawString(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, int? fontsize = null, string fontfamily = null, string fontstyle = null, string horizalign = null, string vertalign = null)
{
using (var g = GetGraphics())
try
{
try
var family = fontfamily != null ? new FontFamily(fontfamily) : FontFamily.GenericMonospace;
var fstyle = fontstyle?.ToLower() switch
{
var family = FontFamily.GenericMonospace;
if (fontfamily != null)
{
family = new FontFamily(fontfamily);
}
"bold" => FontStyle.Bold,
"italic" => FontStyle.Italic,
"strikethrough" => FontStyle.Strikeout,
"underline" => FontStyle.Underline,
_ => FontStyle.Regular
};
var fstyle = FontStyle.Regular;
if (fontstyle != null)
{
switch (fontstyle.ToLower())
{
default:
case "regular":
break;
case "bold":
fstyle = FontStyle.Bold;
break;
case "italic":
fstyle = FontStyle.Italic;
break;
case "strikethrough":
fstyle = FontStyle.Strikeout;
break;
case "underline":
fstyle = FontStyle.Underline;
break;
}
}
using var g = GetGraphics();
// The text isn't written out using GenericTypographic, so measuring it using GenericTypographic seemed to make it worse.
// And writing it out with GenericTypographic just made it uglier. :p
var f = new StringFormat(StringFormat.GenericDefault);
var font = new Font(family, fontsize ?? 12, fstyle, GraphicsUnit.Pixel);
Size sizeOfText = g.MeasureString(message, font, 0, f).ToSize();
if (horizalign != null)
{
switch (horizalign.ToLower())
{
default:
case "left":
break;
case "center":
case "middle":
x -= sizeOfText.Width / 2;
break;
case "right":
x -= sizeOfText.Width;
break;
}
}
// The text isn't written out using GenericTypographic, so measuring it using GenericTypographic seemed to make it worse.
// And writing it out with GenericTypographic just made it uglier. :p
var font = new Font(family, fontsize ?? 12, fstyle, GraphicsUnit.Pixel);
var sizeOfText = g.MeasureString(message, font, 0, new StringFormat(StringFormat.GenericDefault)).ToSize();
if (vertalign != null)
{
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)
switch (horizalign?.ToLower())
{
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)
{
using (var g = GetGraphics())
try
{
try
int index;
switch (fontfamily)
{
var index = 0;
if (string.IsNullOrEmpty(fontfamily))
{
index = _defaultPixelFont;
}
else
{
switch (fontfamily)
case "fceux":
case "0":
index = 0;
break;
case "gens":
case "1":
index = 1;
break;
default:
if (!string.IsNullOrEmpty(fontfamily)) // not a typo
{
case "fceux":
case "0":
index = 0;
break;
case "gens":
case "1":
index = 1;
break;
default:
LogCallback($"Unable to find font family: {fontfamily}");
return;
LogCallback($"Unable to find font family: {fontfamily}");
return;
}
}
var f = new StringFormat(StringFormat.GenericTypographic)
{
FormatFlags = StringFormatFlags.MeasureTrailingSpaces
};
var font = new Font(GlobalWin.DisplayManager.CustomFonts.Families[index], 8, FontStyle.Regular, GraphicsUnit.Pixel);
Size sizeOfText = g.MeasureString(message, font, 0, f).ToSize();
var rect = new Rectangle(new Point(x, y), sizeOfText + new Size(1, 0));
if (backcolor.HasValue) g.FillRectangle(GetBrush(backcolor.Value), rect);
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
g.DrawString(message, font, GetBrush(forecolor ?? _defaultForeground), x, y);
}
catch (Exception)
{
return;
index = _defaultPixelFont;
break;
}
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)
{
var a = 0;
int a = default;
if (!string.IsNullOrEmpty(anchor))
{
switch (anchor)
a = anchor switch
{
case "0":
case "topleft":
a = 0;
break;
case "1":
case "topright":
a = 1;
break;
case "2":
case "bottomleft":
a = 2;
break;
case "3":
case "bottomright":
a = 3;
break;
}
"0" => 0,
"topleft" => 0,
"1" => 1,
"topright" => 1,
"2" => 2,
"bottomleft" => 2,
"3" => 3,
"bottomright" => 3,
_ => default
};
}
else
{
x -= Emulator.CoreComm.ScreenLogicalOffsetX;
y -= Emulator.CoreComm.ScreenLogicalOffsetY;
}
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.Collections.Generic;
using System.Windows.Forms;
using BizHawk.Client.ApiHawk;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
public sealed class InputApi : IInput
{
public InputApi() : base()
{ }
public Dictionary<string,bool> Get()
public Dictionary<string, bool> Get()
{
var buttons = new Dictionary<string, bool>();
foreach (var kvp in Global.ControllerInputCoalescer.BoolButtons().Where(kvp => kvp.Value))
{
buttons[kvp.Key] = true;
}
foreach (var kvp in Global.ControllerInputCoalescer.BoolButtons().Where(kvp => kvp.Value)) buttons[kvp.Key] = true;
return buttons;
}
public Dictionary<string, dynamic> GetMouse()
{
var buttons = new Dictionary<string, dynamic>();
// TODO - need to specify whether in "emu" or "native" coordinate space.
var p = GlobalWin.DisplayManager.UntransformPoint(Control.MousePosition);
buttons["X"] = p.X;

View File

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using BizHawk.Client.ApiHawk;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
@ -23,32 +21,21 @@ namespace BizHawk.Client.EmuHawk
if (!File.Exists(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)
{
if (slotNum >= 0 && slotNum <= 9)
{
GlobalWin.MainForm.LoadQuickSave($"QuickSave{slotNum}", true, suppressOSD);
}
if (0 <= slotNum && slotNum <= 9) GlobalWin.MainForm.LoadQuickSave($"QuickSave{slotNum}", true, suppressOSD);
}
public void Save(string path, bool suppressOSD)
{
GlobalWin.MainForm.SaveState(path, path, true, suppressOSD);
}
public void Save(string path, bool suppressOSD) => GlobalWin.MainForm.SaveState(path, path, true, suppressOSD);
public void SaveSlot(int slotNum, bool suppressOSD)
{
if (slotNum >= 0 && slotNum <= 9)
{
GlobalWin.MainForm.SaveQuickSave($"QuickSave{slotNum}", true, suppressOSD);
}
if (0 <= slotNum && slotNum <= 9) GlobalWin.MainForm.SaveQuickSave($"QuickSave{slotNum}", true, suppressOSD);
}
}
}

View File

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

View File

@ -21,9 +21,7 @@ namespace BizHawk.Client.EmuHawk
public override string Name => "gui";
#region Gui API
public void Dispose() => APIs.Gui.Dispose();
public bool HasLuaSurface => 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.")]
public void DrawFinish() => APIs.Gui.DrawFinish();
public bool HasLuaSurface => APIs.Gui.HasGUISurface;
#endregion
[LuaMethodExample("gui.addmessage( \"Some message\" );")]
[LuaMethod("addmessage", "Adds a message to the OSD's message area")]
public void AddMessage(string message) => APIs.Gui.AddMessage(message);
@ -194,5 +188,7 @@ namespace BizHawk.Client.EmuHawk
canvas.Show();
return Lua.TableFromObject(canvas);
}
public void Dispose() => APIs.Gui.Dispose();
}
}