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