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:
parent
08d0f462fc
commit
0fcb6cbaa7
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue