using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading.Tasks; using System.Windows.Forms; using BizHawk.Client.Common; using BizHawk.Emulation.Cores.Nintendo.Gameboy; using BizHawk.Emulation.Cores.PCEngine; using BizHawk.Emulation.Cores.Sega.MasterSystem; namespace BizHawk.Client.ApiHawk { /// /// This class contains some methods that /// interract with BizHawk client /// public static class ClientApi { #region Fields private static readonly Assembly clientAssembly; private static readonly object clientMainFormInstance; private static readonly Type mainFormClass; private static readonly Array joypadButtonsArray = Enum.GetValues(typeof(JoypadButton)); internal static readonly BizHawkSystemIdToEnumConverter SystemIdConverter = new BizHawkSystemIdToEnumConverter(); internal static readonly JoypadStringToEnumConverter JoypadConverter = new JoypadStringToEnumConverter(); public static event EventHandler RomLoaded; private static List allJoypads; #endregion #region cTor(s) /// /// Static stuff initilization /// static ClientApi() { clientAssembly = Assembly.GetEntryAssembly(); clientMainFormInstance = clientAssembly.GetType("BizHawk.Client.EmuHawk.GlobalWin").GetField("MainForm").GetValue(null); mainFormClass = clientAssembly.GetType("BizHawk.Client.EmuHawk.MainForm"); } #endregion #region Methods #region Public /// /// THE FrameAdvance stuff /// public static void DoFrameAdvance() { MethodInfo method = mainFormClass.GetMethod("FrameAdvance"); method.Invoke(clientMainFormInstance, null); method = mainFormClass.GetMethod("StepRunLoop_Throttle", BindingFlags.NonPublic | BindingFlags.Instance); method.Invoke(clientMainFormInstance, null); method = mainFormClass.GetMethod("Render", BindingFlags.NonPublic | BindingFlags.Instance); method.Invoke(clientMainFormInstance, null); } /// /// THE FrameAdvance stuff /// Auto unpause emulation /// public static void DoFrameAdvanceAndUnpause() { DoFrameAdvance(); UnpauseEmulation(); } /// /// Gets a for specified player /// /// Player (one based) you want current inputs /// A populated with current inputs /// Raised when you specify a player less than 1 or greater than maximum allows (see SystemInfo class to get this information) public static Joypad GetInput(int player) { if (player < 1 || player > RunningSystem.MaxControllers) { throw new IndexOutOfRangeException(string.Format("{0} does not support {1} controller(s)", RunningSystem.DisplayName, player)); } else { GetAllInputs(); return allJoypads[player - 1]; } } /// /// Raise when a rom is successfully Loaded /// public static void OnRomLoaded() { if (RomLoaded != null) { RomLoaded(null, EventArgs.Empty); } allJoypads = new List(RunningSystem.MaxControllers); for (int i = 1; i <= RunningSystem.MaxControllers; i++) { allJoypads.Add(new Joypad(RunningSystem, i)); } } /// /// Sets the extra padding added to the 'native' surface so that you can draw HUD elements in predictable placements /// /// Left padding /// Top padding /// Right padding /// Bottom padding public static void SetExtraPadding(int left, int top, int right, int bottom) { FieldInfo f = clientAssembly.GetType("BizHawk.Client.EmuHawk.GlobalWin").GetField("DisplayManager"); object displayManager = f.GetValue(null); f = f.FieldType.GetField("ClientExtraPadding"); f.SetValue(displayManager, new Padding(left, top, right, bottom)); MethodInfo resize = mainFormClass.GetMethod("FrameBufferResized"); resize.Invoke(clientMainFormInstance, null); } /// /// Sets the extra padding added to the 'native' surface so that you can draw HUD elements in predictable placements /// /// Left padding public static void SetExtraPadding(int left) { SetExtraPadding(left, 0, 0, 0); } /// /// Sets the extra padding added to the 'native' surface so that you can draw HUD elements in predictable placements /// /// Left padding /// Top padding public static void SetExtraPadding(int left, int top) { SetExtraPadding(left, top, 0, 0); } /// /// Sets the extra padding added to the 'native' surface so that you can draw HUD elements in predictable placements /// /// Left padding /// Top padding /// Right padding public static void SetExtraPadding(int left, int top, int right) { SetExtraPadding(left, top, right, 0); } /// /// Set inputs in specified to specified player /// /// Player (one based) whom inputs must be set /// with inputs /// Raised when you specify a player less than 1 or greater than maximum allows (see SystemInfo class to get this information) /// Still have some strange behaviour with multiple inputs; so this feature is still in beta public static void SetInput(int player, Joypad joypad) { if (player < 1 || player > RunningSystem.MaxControllers) { throw new IndexOutOfRangeException(string.Format("{0} does not support {1} controller(s)", RunningSystem.DisplayName, player)); } else { if (joypad.Inputs == 0) { AutoFireStickyXorAdapter joypadAdaptor = Global.AutofireStickyXORAdapter; joypadAdaptor.ClearStickies(); } else { foreach (JoypadButton button in joypadButtonsArray) { if (joypad.Inputs.HasFlag(button)) { AutoFireStickyXorAdapter joypadAdaptor = Global.AutofireStickyXORAdapter; if (RunningSystem == SystemInfo.GB) { joypadAdaptor.SetSticky(string.Format("{0}", JoypadConverter.ConvertBack(button, RunningSystem)), true); } else { joypadAdaptor.SetSticky(string.Format("P{0} {1}", player, JoypadConverter.ConvertBack(button, RunningSystem)), true); } } } } //Using this break joypad usage (even in UI); have to figure out why /*if ((RunningSystem.AvailableButtons & JoypadButton.AnalogStick) == JoypadButton.AnalogStick) { AutoFireStickyXorAdapter joypadAdaptor = Global.AutofireStickyXORAdapter; for (int i = 1; i <= RunningSystem.MaxControllers; i++) { joypadAdaptor.SetFloat(string.Format("P{0} X Axis", i), allJoypads[i - 1].AnalogX); joypadAdaptor.SetFloat(string.Format("P{0} Y Axis", i), allJoypads[i - 1].AnalogY); } }*/ } } /// /// Resume the emulation /// public static void UnpauseEmulation() { MethodInfo method = mainFormClass.GetMethod("UnpauseEmulator"); method.Invoke(clientMainFormInstance, null); } #endregion Public /// /// Gets all current inputs for each joypad and store /// them in class collection /// private static void GetAllInputs() { AutoFireStickyXorAdapter joypadAdaptor = Global.AutofireStickyXORAdapter; IEnumerable pressedButtons = from button in joypadAdaptor.Type.BoolButtons where joypadAdaptor[button] select button; foreach (Joypad j in allJoypads) { j.ClearInputs(); } Parallel.ForEach(pressedButtons, button => { int player; if (RunningSystem == SystemInfo.GB) { allJoypads[0].AddInput(JoypadConverter.Convert(button)); } else { if (int.TryParse(button.Substring(1, 2), out player)) { allJoypads[player - 1].AddInput(JoypadConverter.Convert(button.Substring(3))); } } }); if ((RunningSystem.AvailableButtons & JoypadButton.AnalogStick) == JoypadButton.AnalogStick) { for (int i = 1; i <= RunningSystem.MaxControllers; i++) { allJoypads[i - 1].AnalogX = joypadAdaptor.GetFloat(string.Format("P{0} X Axis", i)); allJoypads[i - 1].AnalogY = joypadAdaptor.GetFloat(string.Format("P{0} Y Axis", i)); } } } #endregion #region Properties /// /// Gets current emulated system /// public static SystemInfo RunningSystem { get { switch (Global.Emulator.SystemId) { case "PCE": if (((PCEngine)Global.Emulator).Type == NecSystemType.TurboGrafx) { return SystemInfo.PCE; } else if (((PCEngine)Global.Emulator).Type == NecSystemType.SuperGrafx) { return SystemInfo.SGX; } else { return SystemInfo.PCECD; } case "SMS": if (((SMS)Global.Emulator).IsSG1000) { return SystemInfo.SG; } else if (((SMS)Global.Emulator).IsGameGear) { return SystemInfo.GG; } else { return SystemInfo.SMS; } case "GB": if (Global.Emulator is Gameboy) { return SystemInfo.GB; } else if (Global.Emulator is GBColors) { return SystemInfo.GBC; } else { return SystemInfo.DualGB; } default: return SystemInfo.FindByCoreSystem(SystemIdConverter.Convert(Global.Emulator.SystemId)); } } } #endregion } }