Delegate ClientApi implementation to an instantiable class
The new class, EmuClientApi, is abstracted as IEmuClient, but this interface is not an IExternalApi. This is because a future commit will add a ctor not compatible with the current instantiation code in ApiManager.
This commit is contained in:
parent
8c06de56c2
commit
f78af85cc6
|
@ -0,0 +1,225 @@
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
|
namespace BizHawk.Client.Common
|
||||||
|
{
|
||||||
|
public interface IEmuClient
|
||||||
|
{
|
||||||
|
SystemInfo RunningSystem { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs before a quickload is done (just after user has pressed the shortcut button
|
||||||
|
/// or has click on the item menu)
|
||||||
|
/// </summary>
|
||||||
|
event BeforeQuickLoadEventHandler BeforeQuickLoad;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs before a quicksave is done (just after user has pressed the shortcut button
|
||||||
|
/// or has click on the item menu)
|
||||||
|
/// </summary>
|
||||||
|
event BeforeQuickSaveEventHandler BeforeQuickSave;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a ROM is successfully loaded
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler RomLoaded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a savestate is successfully loaded
|
||||||
|
/// </summary>
|
||||||
|
event StateLoadedEventHandler StateLoaded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a savestate is successfully saved
|
||||||
|
/// </summary>
|
||||||
|
event StateSavedEventHandler StateSaved;
|
||||||
|
|
||||||
|
int BorderHeight();
|
||||||
|
|
||||||
|
int BorderWidth();
|
||||||
|
|
||||||
|
int BufferHeight();
|
||||||
|
|
||||||
|
int BufferWidth();
|
||||||
|
|
||||||
|
void ClearAutohold();
|
||||||
|
|
||||||
|
void CloseEmulator();
|
||||||
|
|
||||||
|
void CloseEmulatorWithCode(int exitCode);
|
||||||
|
|
||||||
|
void CloseRom();
|
||||||
|
|
||||||
|
void DisplayMessages(bool value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// THE FrameAdvance stuff
|
||||||
|
/// </summary>
|
||||||
|
void DoFrameAdvance();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// THE FrameAdvance stuff
|
||||||
|
/// Auto unpause emulation
|
||||||
|
/// </summary>
|
||||||
|
void DoFrameAdvanceAndUnpause();
|
||||||
|
|
||||||
|
void EnableRewind(bool enabled);
|
||||||
|
|
||||||
|
void FrameSkip(int numFrames);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="Joypad"/> for specified player
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="player">Player (one based) you want current inputs</param>
|
||||||
|
/// <returns>A <see cref="Joypad"/> populated with current inputs</returns>
|
||||||
|
/// <exception cref="IndexOutOfRangeException">Raised when you specify a player less than 1 or greater than maximum allows (see SystemInfo class to get this information)</exception>
|
||||||
|
Joypad GetInput(int player);
|
||||||
|
|
||||||
|
bool GetSoundOn();
|
||||||
|
|
||||||
|
int GetTargetScanlineIntensity();
|
||||||
|
|
||||||
|
int GetWindowSize();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use with <see cref="SeekFrame(int)"/> for CamHack.
|
||||||
|
/// Refer to <c>MainForm.InvisibleEmulation</c> for the workflow details.
|
||||||
|
/// </summary>
|
||||||
|
void InvisibleEmulation(bool invisible);
|
||||||
|
|
||||||
|
bool IsPaused();
|
||||||
|
|
||||||
|
bool IsSeeking();
|
||||||
|
|
||||||
|
bool IsTurbo();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load a savestate specified by its name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Savestate friendly name</param>
|
||||||
|
void LoadState(string name);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised before a quickload is done (just after pressing shortcut button)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">Object who raised the event</param>
|
||||||
|
/// <param name="quickSaveSlotName">Slot used for quickload</param>
|
||||||
|
/// <param name="eventHandled">A boolean that can be set if users want to handle save themselves; if so, BizHawk won't do anything</param>
|
||||||
|
void OnBeforeQuickLoad(object sender, string quickSaveSlotName, out bool eventHandled);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised before a quicksave is done (just after pressing shortcut button)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">Object who raised the event</param>
|
||||||
|
/// <param name="quickSaveSlotName">Slot used for quicksave</param>
|
||||||
|
/// <param name="eventHandled">A boolean that can be set if users want to handle save themselves; if so, BizHawk won't do anything</param>
|
||||||
|
void OnBeforeQuickSave(object sender, string quickSaveSlotName, out bool eventHandled);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raise when a rom is successfully Loaded
|
||||||
|
/// </summary>
|
||||||
|
void OnRomLoaded(IEmulator emu);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raise when a state is loaded
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">Object who raised the event</param>
|
||||||
|
/// <param name="stateName">User friendly name for saved state</param>
|
||||||
|
void OnStateLoaded(object sender, string stateName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raise when a state is saved
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">Object who raised the event</param>
|
||||||
|
/// <param name="stateName">User friendly name for saved state</param>
|
||||||
|
void OnStateSaved(object sender, string stateName);
|
||||||
|
|
||||||
|
void OpenRom(string path);
|
||||||
|
|
||||||
|
void Pause();
|
||||||
|
|
||||||
|
void PauseAv();
|
||||||
|
|
||||||
|
void RebootCore();
|
||||||
|
|
||||||
|
void SaveRam();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save a state with specified name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Savestate friendly name</param>
|
||||||
|
void SaveState(string name);
|
||||||
|
|
||||||
|
int ScreenHeight();
|
||||||
|
|
||||||
|
void Screenshot(string path = null);
|
||||||
|
|
||||||
|
void ScreenshotToClipboard();
|
||||||
|
|
||||||
|
int ScreenWidth();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use with <see cref="InvisibleEmulation(bool)"/> for CamHack.
|
||||||
|
/// Refer to <c>MainForm.InvisibleEmulation</c> for the workflow details.
|
||||||
|
/// </summary>
|
||||||
|
void SeekFrame(int frame);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the extra padding added to the 'native' surface so that you can draw HUD elements in predictable placements
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">Left padding</param>
|
||||||
|
/// <param name="top">Top padding</param>
|
||||||
|
/// <param name="right">Right padding</param>
|
||||||
|
/// <param name="bottom">Bottom padding</param>
|
||||||
|
void SetExtraPadding(int left, int top = 0, int right = 0, int bottom = 0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the extra padding added to the 'native' surface so that you can draw HUD elements in predictable placements
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">Left padding</param>
|
||||||
|
/// <param name="top">Top padding</param>
|
||||||
|
/// <param name="right">Right padding</param>
|
||||||
|
/// <param name="bottom">Bottom padding</param>
|
||||||
|
void SetGameExtraPadding(int left, int top = 0, int right = 0, int bottom = 0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set inputs in specified <see cref="Joypad"/> to specified player
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="player">Player (one based) whom inputs must be set</param>
|
||||||
|
/// <param name="joypad"><see cref="Joypad"/> with inputs</param>
|
||||||
|
/// <exception cref="IndexOutOfRangeException">Raised when you specify a player less than 1 or greater than maximum allows (see SystemInfo class to get this information)</exception>
|
||||||
|
/// <remarks>Still have some strange behaviour with multiple inputs; so this feature is still in beta</remarks>
|
||||||
|
void SetInput(int player, Joypad joypad);
|
||||||
|
|
||||||
|
void SetScreenshotOSD(bool value);
|
||||||
|
|
||||||
|
void SetSoundOn(bool enable);
|
||||||
|
|
||||||
|
void SetTargetScanlineIntensity(int val);
|
||||||
|
|
||||||
|
void SetWindowSize(int size);
|
||||||
|
|
||||||
|
void SpeedMode(int percent);
|
||||||
|
|
||||||
|
void TogglePause();
|
||||||
|
|
||||||
|
Point TransformPoint(Point point);
|
||||||
|
|
||||||
|
void Unpause();
|
||||||
|
|
||||||
|
void UnpauseAv();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resume the emulation
|
||||||
|
/// </summary>
|
||||||
|
void UnpauseEmulation();
|
||||||
|
|
||||||
|
void UpdateEmulatorAndVP(IEmulator emu = null);
|
||||||
|
|
||||||
|
int Xpos();
|
||||||
|
|
||||||
|
int Ypos();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using BizHawk.Client.Common;
|
|
||||||
using BizHawk.Common;
|
using BizHawk.Common;
|
||||||
|
|
||||||
namespace BizHawk.Client.EmuHawk
|
namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class holds a joypad for any type of console
|
/// This class holds a joypad for any type of console
|
||||||
|
@ -20,7 +20,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// <param name="system">What <see cref="SystemInfo"/> this <see cref="Joypad"/> is used for</param>
|
/// <param name="system">What <see cref="SystemInfo"/> this <see cref="Joypad"/> is used for</param>
|
||||||
/// <param name="player">Which player this controller is assigned to</param>
|
/// <param name="player">Which player this controller is assigned to</param>
|
||||||
/// <exception cref="IndexOutOfRangeException"><paramref name="player"/> not in range <c>1..max</c> where <c>max</c> is <paramref name="system"/>.<see cref="SystemInfo.MaxControllers"/></exception>
|
/// <exception cref="IndexOutOfRangeException"><paramref name="player"/> not in range <c>1..max</c> where <c>max</c> is <paramref name="system"/>.<see cref="SystemInfo.MaxControllers"/></exception>
|
||||||
internal Joypad(SystemInfo system, int player)
|
public Joypad(SystemInfo system, int player)
|
||||||
{
|
{
|
||||||
if (!1.RangeTo(system.MaxControllers).Contains(player))
|
if (!1.RangeTo(system.MaxControllers).Contains(player))
|
||||||
{
|
{
|
|
@ -1,13 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace BizHawk.Client.EmuHawk
|
namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class holds event data for BeforeQuickLoad event
|
/// This class holds event data for BeforeQuickLoad event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class BeforeQuickLoadEventArgs : EventArgs
|
public sealed class BeforeQuickLoadEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
internal BeforeQuickLoadEventArgs(string name)
|
public BeforeQuickLoadEventArgs(string name)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class BeforeQuickSaveEventArgs : EventArgs
|
public sealed class BeforeQuickSaveEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
internal BeforeQuickSaveEventArgs(string name)
|
public BeforeQuickSaveEventArgs(string name)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// Initialize a new instance of <see cref="StateLoadedEventArgs"/>
|
/// Initialize a new instance of <see cref="StateLoadedEventArgs"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stateName">User friendly name of loaded state</param>
|
/// <param name="stateName">User friendly name of loaded state</param>
|
||||||
internal StateLoadedEventArgs(string stateName)
|
public StateLoadedEventArgs(string stateName)
|
||||||
{
|
{
|
||||||
Name = stateName;
|
Name = stateName;
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// Initialize a new instance of <see cref="StateSavedEventArgs"/>
|
/// Initialize a new instance of <see cref="StateSavedEventArgs"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stateName">User friendly name of loaded state</param>
|
/// <param name="stateName">User friendly name of loaded state</param>
|
||||||
internal StateSavedEventArgs(string stateName)
|
public StateSavedEventArgs(string stateName)
|
||||||
{
|
{
|
||||||
Name = stateName;
|
Name = stateName;
|
||||||
}
|
}
|
|
@ -1,449 +1,211 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
|
|
||||||
using BizHawk.Client.Common;
|
using BizHawk.Client.Common;
|
||||||
using BizHawk.Common;
|
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
using BizHawk.Emulation.Cores.Nintendo.Gameboy;
|
|
||||||
using BizHawk.Emulation.Cores.PCEngine;
|
|
||||||
using BizHawk.Emulation.Cores.Sega.MasterSystem;
|
|
||||||
|
|
||||||
// ReSharper disable UnusedMember.Global
|
|
||||||
namespace BizHawk.Client.EmuHawk
|
namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// This class contains some methods that
|
|
||||||
/// interact with BizHawk client
|
|
||||||
/// </summary>
|
|
||||||
public static class ClientApi
|
public static class ClientApi
|
||||||
{
|
{
|
||||||
private static IEmulator Emulator { get; set; }
|
/// <inheritdoc cref="IEmuClient.DoFrameAdvance"/>
|
||||||
|
public static SystemInfo RunningSystem => GlobalWin.ClientApi.RunningSystem;
|
||||||
|
|
||||||
private static IVideoProvider VideoProvider { get; set; }
|
/// <inheritdoc cref="IEmuClient.BeforeQuickLoad"/>
|
||||||
|
public static event BeforeQuickLoadEventHandler BeforeQuickLoad
|
||||||
private static readonly IReadOnlyCollection<JoypadButton> JoypadButtonsArray = Enum.GetValues(typeof(JoypadButton)).Cast<JoypadButton>().ToList(); //TODO can the return of GetValues be cast to JoypadButton[]? --yoshi
|
|
||||||
|
|
||||||
internal static readonly BizHawkSystemIdToEnumConverter SystemIdConverter = new BizHawkSystemIdToEnumConverter();
|
|
||||||
|
|
||||||
private static readonly JoypadStringToEnumConverter JoypadConverter = new JoypadStringToEnumConverter();
|
|
||||||
|
|
||||||
private static List<Joypad> _allJoyPads;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs before a quickload is done (just after user has pressed the shortcut button
|
|
||||||
/// or has click on the item menu)
|
|
||||||
/// </summary>
|
|
||||||
public static event BeforeQuickLoadEventHandler BeforeQuickLoad;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs before a quicksave is done (just after user has pressed the shortcut button
|
|
||||||
/// or has click on the item menu)
|
|
||||||
/// </summary>
|
|
||||||
public static event BeforeQuickSaveEventHandler BeforeQuickSave;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when a ROM is successfully loaded
|
|
||||||
/// </summary>
|
|
||||||
public static event EventHandler RomLoaded;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when a savestate is successfully loaded
|
|
||||||
/// </summary>
|
|
||||||
public static event StateLoadedEventHandler StateLoaded;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when a savestate is successfully saved
|
|
||||||
/// </summary>
|
|
||||||
public static event StateSavedEventHandler StateSaved;
|
|
||||||
|
|
||||||
public static void UpdateEmulatorAndVP(IEmulator emu = null)
|
|
||||||
{
|
{
|
||||||
Emulator = emu;
|
add => GlobalWin.ClientApi.BeforeQuickLoad += value;
|
||||||
VideoProvider = emu.AsVideoProviderOrDefault();
|
remove => GlobalWin.ClientApi.BeforeQuickLoad -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.BeforeQuickSave"/>
|
||||||
/// THE FrameAdvance stuff
|
public static event BeforeQuickSaveEventHandler BeforeQuickSave
|
||||||
/// </summary>
|
|
||||||
public static void DoFrameAdvance()
|
|
||||||
{
|
{
|
||||||
GlobalWin.MainForm.FrameAdvance();
|
add => GlobalWin.ClientApi.BeforeQuickSave += value;
|
||||||
GlobalWin.MainForm.StepRunLoop_Throttle();
|
remove => GlobalWin.ClientApi.BeforeQuickSave -= value;
|
||||||
GlobalWin.MainForm.Render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.RomLoaded"/>
|
||||||
/// THE FrameAdvance stuff
|
public static event EventHandler RomLoaded
|
||||||
/// Auto unpause emulation
|
|
||||||
/// </summary>
|
|
||||||
public static void DoFrameAdvanceAndUnpause()
|
|
||||||
{
|
{
|
||||||
DoFrameAdvance();
|
add => GlobalWin.ClientApi.RomLoaded += value;
|
||||||
UnpauseEmulation();
|
remove => GlobalWin.ClientApi.RomLoaded -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.StateLoaded"/>
|
||||||
/// Use with <see cref="InvisibleEmulation(bool)"/> for CamHack.
|
public static event StateLoadedEventHandler StateLoaded
|
||||||
/// Refer to <see cref="MainForm.InvisibleEmulation"/> for the workflow details.
|
|
||||||
/// </summary>
|
|
||||||
public static void SeekFrame(int frame)
|
|
||||||
{
|
{
|
||||||
var wasPaused = GlobalWin.MainForm.EmulatorPaused;
|
add => GlobalWin.ClientApi.StateLoaded += value;
|
||||||
while (Emulator.Frame != frame) GlobalWin.MainForm.SeekFrameAdvance();
|
remove => GlobalWin.ClientApi.StateLoaded -= value;
|
||||||
if (!wasPaused) GlobalWin.MainForm.UnpauseEmulator();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.StateSaved"/>
|
||||||
/// Use with <see cref="SeekFrame(int)"/> for CamHack.
|
public static event StateSavedEventHandler StateSaved
|
||||||
/// Refer to <see cref="MainForm.InvisibleEmulation"/> for the workflow details.
|
|
||||||
/// </summary>
|
|
||||||
public static void InvisibleEmulation(bool invisible) => GlobalWin.MainForm.InvisibleEmulation = invisible;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a <see cref="Joypad"/> for specified player
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="player">Player (one based) you want current inputs</param>
|
|
||||||
/// <returns>A <see cref="Joypad"/> populated with current inputs</returns>
|
|
||||||
/// <exception cref="IndexOutOfRangeException">Raised when you specify a player less than 1 or greater than maximum allows (see SystemInfo class to get this information)</exception>
|
|
||||||
public static Joypad GetInput(int player)
|
|
||||||
{
|
{
|
||||||
if (!1.RangeTo(RunningSystem.MaxControllers).Contains(player))
|
add => GlobalWin.ClientApi.StateSaved += value;
|
||||||
throw new IndexOutOfRangeException($"{RunningSystem.DisplayName} does not support {player} controller(s)");
|
remove => GlobalWin.ClientApi.StateSaved -= value;
|
||||||
GetAllInputs();
|
|
||||||
return _allJoyPads[player - 1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.BorderHeight"/>
|
||||||
/// Load a savestate specified by its name
|
public static int BorderHeight() => GlobalWin.ClientApi.BorderHeight();
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">Savestate friendly name</param>
|
|
||||||
public static void LoadState(string name) => GlobalWin.MainForm.LoadState(Path.Combine(GlobalWin.Config.PathEntries.SaveStateAbsolutePath(GlobalWin.Game.System), $"{name}.State"), name, suppressOSD: false);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.BorderWidth"/>
|
||||||
/// Raised before a quickload is done (just after pressing shortcut button)
|
public static int BorderWidth() => GlobalWin.ClientApi.BorderWidth();
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender">Object who raised the event</param>
|
|
||||||
/// <param name="quickSaveSlotName">Slot used for quickload</param>
|
|
||||||
/// <param name="eventHandled">A boolean that can be set if users want to handle save themselves; if so, BizHawk won't do anything</param>
|
|
||||||
public static void OnBeforeQuickLoad(object sender, string quickSaveSlotName, out bool eventHandled)
|
|
||||||
{
|
|
||||||
if (BeforeQuickLoad == null)
|
|
||||||
{
|
|
||||||
eventHandled = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var e = new BeforeQuickLoadEventArgs(quickSaveSlotName);
|
|
||||||
BeforeQuickLoad(sender, e);
|
|
||||||
eventHandled = e.Handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IEmuClient.BufferHeight"/>
|
||||||
|
public static int BufferHeight() => GlobalWin.ClientApi.BufferHeight();
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.BufferWidth"/>
|
||||||
/// Raised before a quicksave is done (just after pressing shortcut button)
|
public static int BufferWidth() => GlobalWin.ClientApi.BufferWidth();
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender">Object who raised the event</param>
|
|
||||||
/// <param name="quickSaveSlotName">Slot used for quicksave</param>
|
|
||||||
/// <param name="eventHandled">A boolean that can be set if users want to handle save themselves; if so, BizHawk won't do anything</param>
|
|
||||||
public static void OnBeforeQuickSave(object sender, string quickSaveSlotName, out bool eventHandled)
|
|
||||||
{
|
|
||||||
if (BeforeQuickSave == null)
|
|
||||||
{
|
|
||||||
eventHandled = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var e = new BeforeQuickSaveEventArgs(quickSaveSlotName);
|
|
||||||
BeforeQuickSave(sender, e);
|
|
||||||
eventHandled = e.Handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IEmuClient.ClearAutohold"/>
|
||||||
|
public static void ClearAutohold() => GlobalWin.ClientApi.ClearAutohold();
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.CloseEmulator"/>
|
||||||
/// Raise when a state is loaded
|
public static void CloseEmulator() => GlobalWin.ClientApi.CloseEmulator();
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender">Object who raised the event</param>
|
|
||||||
/// <param name="stateName">User friendly name for saved state</param>
|
|
||||||
public static void OnStateLoaded(object sender, string stateName) => StateLoaded?.Invoke(sender, new StateLoadedEventArgs(stateName));
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.CloseEmulatorWithCode"/>
|
||||||
/// Raise when a state is saved
|
public static void CloseEmulatorWithCode(int exitCode) => GlobalWin.ClientApi.CloseEmulatorWithCode(exitCode);
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender">Object who raised the event</param>
|
|
||||||
/// <param name="stateName">User friendly name for saved state</param>
|
|
||||||
public static void OnStateSaved(object sender, string stateName) => StateSaved?.Invoke(sender, new StateSavedEventArgs(stateName));
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.CloseRom"/>
|
||||||
/// Raise when a rom is successfully Loaded
|
public static void CloseRom() => GlobalWin.ClientApi.CloseRom();
|
||||||
/// </summary>
|
|
||||||
public static void OnRomLoaded(IEmulator emu)
|
|
||||||
{
|
|
||||||
Emulator = emu;
|
|
||||||
VideoProvider = emu.AsVideoProviderOrDefault();
|
|
||||||
RomLoaded?.Invoke(null, EventArgs.Empty);
|
|
||||||
|
|
||||||
try
|
/// <inheritdoc cref="IEmuClient.DisplayMessages"/>
|
||||||
{
|
public static void DisplayMessages(bool value) => GlobalWin.ClientApi.DisplayMessages(value);
|
||||||
_allJoyPads = new List<Joypad>(RunningSystem.MaxControllers);
|
|
||||||
for (var i = 1; i <= RunningSystem.MaxControllers; i++)
|
|
||||||
_allJoyPads.Add(new Joypad(RunningSystem, i));
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine("Apihawk is garbage and may not work this session.");
|
|
||||||
Console.Error.WriteLine(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.DoFrameAdvance"/>
|
||||||
/// Save a state with specified name
|
public static void DoFrameAdvance() => GlobalWin.ClientApi.DoFrameAdvance();
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">Savestate friendly name</param>
|
|
||||||
public static void SaveState(string name) => GlobalWin.MainForm.SaveState(Path.Combine(GlobalWin.Config.PathEntries.SaveStateAbsolutePath(GlobalWin.Game.System), $"{name}.State"), name, fromLua: false);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.DoFrameAdvanceAndUnpause"/>
|
||||||
/// Sets the extra padding added to the 'native' surface so that you can draw HUD elements in predictable placements
|
public static void DoFrameAdvanceAndUnpause() => GlobalWin.ClientApi.DoFrameAdvanceAndUnpause();
|
||||||
/// </summary>
|
|
||||||
/// <param name="left">Left padding</param>
|
|
||||||
/// <param name="top">Top padding</param>
|
|
||||||
/// <param name="right">Right padding</param>
|
|
||||||
/// <param name="bottom">Bottom padding</param>
|
|
||||||
public static void SetGameExtraPadding(int left, int top = 0, int right = 0, int bottom = 0)
|
|
||||||
{
|
|
||||||
GlobalWin.DisplayManager.GameExtraPadding = new Padding(left, top, right, bottom);
|
|
||||||
GlobalWin.MainForm.FrameBufferResized();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.EnableRewind"/>
|
||||||
/// Sets the extra padding added to the 'native' surface so that you can draw HUD elements in predictable placements
|
public static void EnableRewind(bool enabled) => GlobalWin.ClientApi.EnableRewind(enabled);
|
||||||
/// </summary>
|
|
||||||
/// <param name="left">Left padding</param>
|
|
||||||
/// <param name="top">Top padding</param>
|
|
||||||
/// <param name="right">Right padding</param>
|
|
||||||
/// <param name="bottom">Bottom padding</param>
|
|
||||||
public static void SetExtraPadding(int left, int top = 0, int right = 0, int bottom = 0)
|
|
||||||
{
|
|
||||||
GlobalWin.DisplayManager.ClientExtraPadding = new Padding(left, top, right, bottom);
|
|
||||||
GlobalWin.MainForm.FrameBufferResized();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.FrameSkip"/>
|
||||||
/// Set inputs in specified <see cref="Joypad"/> to specified player
|
public static void FrameSkip(int numFrames) => GlobalWin.ClientApi.FrameSkip(numFrames);
|
||||||
/// </summary>
|
|
||||||
/// <param name="player">Player (one based) whom inputs must be set</param>
|
|
||||||
/// <param name="joypad"><see cref="Joypad"/> with inputs</param>
|
|
||||||
/// <exception cref="IndexOutOfRangeException">Raised when you specify a player less than 1 or greater than maximum allows (see SystemInfo class to get this information)</exception>
|
|
||||||
/// <remarks>Still have some strange behaviour with multiple inputs; so this feature is still in beta</remarks>
|
|
||||||
public static void SetInput(int player, Joypad joypad)
|
|
||||||
{
|
|
||||||
if (!1.RangeTo(RunningSystem.MaxControllers).Contains(player)) throw new IndexOutOfRangeException($"{RunningSystem.DisplayName} does not support {player} controller(s)");
|
|
||||||
|
|
||||||
if (joypad.Inputs == 0)
|
/// <inheritdoc cref="IEmuClient.GetInput"/>
|
||||||
{
|
public static Joypad GetInput(int player) => GlobalWin.ClientApi.GetInput(player);
|
||||||
GlobalWin.InputManager.AutofireStickyXorAdapter.ClearStickies();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var button in JoypadButtonsArray.Where(button => joypad.Inputs.HasFlag(button)))
|
|
||||||
{
|
|
||||||
GlobalWin.InputManager.AutofireStickyXorAdapter.SetSticky(
|
|
||||||
RunningSystem == SystemInfo.GB
|
|
||||||
? $"{JoypadConverter.ConvertBack(button, RunningSystem)}"
|
|
||||||
: $"P{player} {JoypadConverter.ConvertBack(button, RunningSystem)}",
|
|
||||||
isSticky: true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if false // Using this breaks joypad usage (even in UI); have to figure out why
|
/// <inheritdoc cref="IEmuClient.GetSoundOn"/>
|
||||||
if ((RunningSystem.AvailableButtons & JoypadButton.AnalogStick) == JoypadButton.AnalogStick)
|
public static bool GetSoundOn() => GlobalWin.ClientApi.GetSoundOn();
|
||||||
{
|
|
||||||
for (var i = 1; i <= RunningSystem.MaxControllers; i++)
|
|
||||||
{
|
|
||||||
GlobalWin.InputManager.AutofireStickyXorAdapter.SetAxis($"P{i} X Axis", _allJoyPads[i - 1].AnalogX);
|
|
||||||
GlobalWin.InputManager.AutofireStickyXorAdapter.SetAxis($"P{i} Y Axis", _allJoyPads[i - 1].AnalogY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.GetTargetScanlineIntensity"/>
|
||||||
/// Resume the emulation
|
public static int GetTargetScanlineIntensity() => GlobalWin.ClientApi.GetTargetScanlineIntensity();
|
||||||
/// </summary>
|
|
||||||
public static void UnpauseEmulation() => GlobalWin.MainForm.UnpauseEmulator();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IEmuClient.GetWindowSize"/>
|
||||||
/// Gets all current inputs for each joypad and store
|
public static int GetWindowSize() => GlobalWin.ClientApi.GetWindowSize();
|
||||||
/// them in <see cref="Joypad"/> class collection
|
|
||||||
/// </summary>
|
|
||||||
private static void GetAllInputs()
|
|
||||||
{
|
|
||||||
var joypadAdapter = GlobalWin.InputManager.AutofireStickyXorAdapter;
|
|
||||||
|
|
||||||
var pressedButtons = joypadAdapter.Definition.BoolButtons.Where(b => joypadAdapter.IsPressed(b));
|
/// <inheritdoc cref="IEmuClient.InvisibleEmulation"/>
|
||||||
|
public static void InvisibleEmulation(bool invisible) => GlobalWin.ClientApi.InvisibleEmulation(invisible);
|
||||||
|
|
||||||
foreach (var j in _allJoyPads) j.ClearInputs();
|
/// <inheritdoc cref="IEmuClient.IsPaused"/>
|
||||||
|
public static bool IsPaused() => GlobalWin.ClientApi.IsPaused();
|
||||||
|
|
||||||
Parallel.ForEach(pressedButtons, button =>
|
/// <inheritdoc cref="IEmuClient.IsSeeking"/>
|
||||||
{
|
public static bool IsSeeking() => GlobalWin.ClientApi.IsSeeking();
|
||||||
if (RunningSystem == SystemInfo.GB) _allJoyPads[0].AddInput(JoypadConverter.Convert(button));
|
|
||||||
else if (int.TryParse(button.Substring(1, 2), out var player)) _allJoyPads[player - 1].AddInput(JoypadConverter.Convert(button.Substring(3)));
|
|
||||||
});
|
|
||||||
|
|
||||||
if ((RunningSystem.AvailableButtons & JoypadButton.AnalogStick) == JoypadButton.AnalogStick)
|
/// <inheritdoc cref="IEmuClient.IsTurbo"/>
|
||||||
{
|
public static bool IsTurbo() => GlobalWin.ClientApi.IsTurbo();
|
||||||
for (var i = 1; i <= RunningSystem.MaxControllers; i++)
|
|
||||||
{
|
|
||||||
_allJoyPads[i - 1].AnalogX = joypadAdapter.AxisValue($"P{i} X Axis");
|
|
||||||
_allJoyPads[i - 1].AnalogY = joypadAdapter.AxisValue($"P{i} Y Axis");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CloseEmulator() => GlobalWin.MainForm.CloseEmulator();
|
/// <inheritdoc cref="IEmuClient.LoadState"/>
|
||||||
|
public static void LoadState(string name) => GlobalWin.ClientApi.LoadState(name);
|
||||||
|
|
||||||
public static void CloseEmulatorWithCode(int exitCode) => GlobalWin.MainForm.CloseEmulator(exitCode);
|
/// <inheritdoc cref="IEmuClient.OnBeforeQuickLoad"/>
|
||||||
|
public static void OnBeforeQuickLoad(object sender, string quickSaveSlotName, out bool eventHandled) => GlobalWin.ClientApi.OnBeforeQuickLoad(sender, quickSaveSlotName, out eventHandled);
|
||||||
|
|
||||||
public static int BorderHeight() => GlobalWin.DisplayManager.TransformPoint(new Point(0, 0)).Y;
|
/// <inheritdoc cref="IEmuClient.OnBeforeQuickSave"/>
|
||||||
|
public static void OnBeforeQuickSave(object sender, string quickSaveSlotName, out bool eventHandled) => GlobalWin.ClientApi.OnBeforeQuickSave(sender, quickSaveSlotName, out eventHandled);
|
||||||
|
|
||||||
public static int BorderWidth() => GlobalWin.DisplayManager.TransformPoint(new Point(0, 0)).X;
|
/// <inheritdoc cref="IEmuClient.OnRomLoaded"/>
|
||||||
|
public static void OnRomLoaded(IEmulator emu) => GlobalWin.ClientApi.OnRomLoaded(emu);
|
||||||
|
|
||||||
public static int BufferHeight() => VideoProvider.BufferHeight;
|
/// <inheritdoc cref="IEmuClient.OnStateLoaded"/>
|
||||||
|
public static void OnStateLoaded(object sender, string stateName) => GlobalWin.ClientApi.OnStateLoaded(sender, stateName);
|
||||||
|
|
||||||
public static int BufferWidth() => VideoProvider.BufferWidth;
|
/// <inheritdoc cref="IEmuClient.OnStateSaved"/>
|
||||||
|
public static void OnStateSaved(object sender, string stateName) => GlobalWin.ClientApi.OnStateSaved(sender, stateName);
|
||||||
|
|
||||||
public static void ClearAutohold() => GlobalWin.MainForm.ClearHolds();
|
/// <inheritdoc cref="IEmuClient.OpenRom"/>
|
||||||
|
public static void OpenRom(string path) => GlobalWin.ClientApi.OpenRom(path);
|
||||||
|
|
||||||
public static void CloseRom() => GlobalWin.MainForm.CloseRom();
|
/// <inheritdoc cref="IEmuClient.Pause"/>
|
||||||
|
public static void Pause() => GlobalWin.ClientApi.Pause();
|
||||||
|
|
||||||
public static void DisplayMessages(bool value) => GlobalWin.Config.DisplayMessages = value;
|
/// <inheritdoc cref="IEmuClient.PauseAv"/>
|
||||||
|
public static void PauseAv() => GlobalWin.ClientApi.PauseAv();
|
||||||
|
|
||||||
public static void EnableRewind(bool enabled) => GlobalWin.MainForm.EnableRewind(enabled);
|
/// <inheritdoc cref="IEmuClient.RebootCore"/>
|
||||||
|
public static void RebootCore() => GlobalWin.ClientApi.RebootCore();
|
||||||
|
|
||||||
public static void FrameSkip(int numFrames)
|
/// <inheritdoc cref="IEmuClient.SaveRam"/>
|
||||||
{
|
public static void SaveRam() => GlobalWin.ClientApi.SaveRam();
|
||||||
if (numFrames > 0)
|
|
||||||
{
|
|
||||||
GlobalWin.Config.FrameSkip = numFrames;
|
|
||||||
GlobalWin.MainForm.FrameSkipMessage();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine("Invalid frame skip value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetTargetScanlineIntensity() => GlobalWin.Config.TargetScanlineFilterIntensity;
|
/// <inheritdoc cref="IEmuClient.SaveState"/>
|
||||||
|
public static void SaveState(string name) => GlobalWin.ClientApi.SaveState(name);
|
||||||
|
|
||||||
public static int GetWindowSize() => GlobalWin.Config.TargetZoomFactors[Emulator.SystemId];
|
/// <inheritdoc cref="IEmuClient.ScreenHeight"/>
|
||||||
|
public static int ScreenHeight() => GlobalWin.ClientApi.ScreenHeight();
|
||||||
|
|
||||||
public static void SetSoundOn(bool enable)
|
/// <inheritdoc cref="IEmuClient.Screenshot"/>
|
||||||
{
|
public static void Screenshot(string path = null) => GlobalWin.ClientApi.Screenshot(path);
|
||||||
if (enable != GlobalWin.Config.SoundEnabled) GlobalWin.MainForm.ToggleSound();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool GetSoundOn() => GlobalWin.Config.SoundEnabled;
|
/// <inheritdoc cref="IEmuClient.ScreenshotToClipboard"/>
|
||||||
|
public static void ScreenshotToClipboard() => GlobalWin.ClientApi.ScreenshotToClipboard();
|
||||||
|
|
||||||
public static bool IsPaused() => GlobalWin.MainForm.EmulatorPaused;
|
/// <inheritdoc cref="IEmuClient.ScreenWidth"/>
|
||||||
|
public static int ScreenWidth() => GlobalWin.ClientApi.ScreenWidth();
|
||||||
|
|
||||||
public static bool IsTurbo() => GlobalWin.MainForm.IsTurboing;
|
/// <inheritdoc cref="IEmuClient.SeekFrame"/>
|
||||||
|
public static void SeekFrame(int frame) => GlobalWin.ClientApi.SeekFrame(frame);
|
||||||
|
|
||||||
public static bool IsSeeking() => GlobalWin.MainForm.IsSeeking;
|
/// <inheritdoc cref="IEmuClient.SetExtraPadding"/>
|
||||||
|
public static void SetExtraPadding(int left, int top = 0, int right = 0, int bottom = 0) => GlobalWin.ClientApi.SetExtraPadding(left, top, right, bottom);
|
||||||
|
|
||||||
public static void OpenRom(string path) => GlobalWin.MainForm.LoadRom(path, new MainForm.LoadRomArgs { OpenAdvanced = OpenAdvancedSerializer.ParseWithLegacy(path) });
|
/// <inheritdoc cref="IEmuClient.SetGameExtraPadding"/>
|
||||||
|
public static void SetGameExtraPadding(int left, int top = 0, int right = 0, int bottom = 0) => GlobalWin.ClientApi.SetGameExtraPadding(left, top, right, bottom);
|
||||||
|
|
||||||
public static void Pause() => GlobalWin.MainForm.PauseEmulator();
|
/// <inheritdoc cref="IEmuClient.SetInput"/>
|
||||||
|
public static void SetInput(int player, Joypad joypad) => GlobalWin.ClientApi.SetInput(player, joypad);
|
||||||
|
|
||||||
public static void PauseAv() => GlobalWin.MainForm.PauseAvi = true;
|
/// <inheritdoc cref="IEmuClient.SetScreenshotOSD"/>
|
||||||
|
public static void SetScreenshotOSD(bool value) => GlobalWin.ClientApi.SetScreenshotOSD(value);
|
||||||
|
|
||||||
public static void RebootCore() => GlobalWin.MainForm.RebootCore();
|
/// <inheritdoc cref="IEmuClient.SetSoundOn"/>
|
||||||
|
public static void SetSoundOn(bool enable) => GlobalWin.ClientApi.SetSoundOn(enable);
|
||||||
|
|
||||||
public static void SaveRam() => GlobalWin.MainForm.FlushSaveRAM();
|
/// <inheritdoc cref="IEmuClient.SetTargetScanlineIntensity"/>
|
||||||
|
public static void SetTargetScanlineIntensity(int val) => GlobalWin.ClientApi.SetTargetScanlineIntensity(val);
|
||||||
|
|
||||||
public static int ScreenHeight() => GlobalWin.MainForm.PresentationPanel.NativeSize.Height;
|
/// <inheritdoc cref="IEmuClient.SetWindowSize"/>
|
||||||
|
public static void SetWindowSize(int size) => GlobalWin.ClientApi.SetWindowSize(size);
|
||||||
|
|
||||||
public static void Screenshot(string path = null)
|
/// <inheritdoc cref="IEmuClient.SpeedMode"/>
|
||||||
{
|
public static void SpeedMode(int percent) => GlobalWin.ClientApi.SpeedMode(percent);
|
||||||
if (path == null) GlobalWin.MainForm.TakeScreenshot();
|
|
||||||
else GlobalWin.MainForm.TakeScreenshot(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ScreenshotToClipboard() => GlobalWin.MainForm.TakeScreenshotToClipboard();
|
/// <inheritdoc cref="IEmuClient.TogglePause"/>
|
||||||
|
public static void TogglePause() => GlobalWin.ClientApi.TogglePause();
|
||||||
|
|
||||||
public static void SetTargetScanlineIntensity(int val) => GlobalWin.Config.TargetScanlineFilterIntensity = val;
|
/// <inheritdoc cref="IEmuClient.TransformPoint"/>
|
||||||
|
public static Point TransformPoint(Point point) => GlobalWin.ClientApi.TransformPoint(point);
|
||||||
|
|
||||||
public static void SetScreenshotOSD(bool value) => GlobalWin.Config.ScreenshotCaptureOsd = value;
|
/// <inheritdoc cref="IEmuClient.Unpause"/>
|
||||||
|
public static void Unpause() => GlobalWin.ClientApi.Unpause();
|
||||||
|
|
||||||
public static int ScreenWidth() => GlobalWin.MainForm.PresentationPanel.NativeSize.Width;
|
/// <inheritdoc cref="IEmuClient.UnpauseAv"/>
|
||||||
|
public static void UnpauseAv() => GlobalWin.ClientApi.UnpauseAv();
|
||||||
|
|
||||||
public static void SetWindowSize(int size)
|
/// <inheritdoc cref="IEmuClient.UnpauseEmulation"/>
|
||||||
{
|
public static void UnpauseEmulation() => GlobalWin.ClientApi.UnpauseEmulation();
|
||||||
if (size == 1 || size == 2 || size == 3 || size == 4 || size == 5 || size == 10)
|
|
||||||
{
|
|
||||||
GlobalWin.Config.TargetZoomFactors[Emulator.SystemId] = size;
|
|
||||||
GlobalWin.MainForm.FrameBufferResized();
|
|
||||||
GlobalWin.OSD.AddMessage($"Window size set to {size}x");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine("Invalid window size");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SpeedMode(int percent)
|
/// <inheritdoc cref="IEmuClient.UpdateEmulatorAndVP"/>
|
||||||
{
|
public static void UpdateEmulatorAndVP(IEmulator emu = null) => GlobalWin.ClientApi.UpdateEmulatorAndVP(emu);
|
||||||
if (percent.StrictlyBoundedBy(0.RangeTo(6400))) GlobalWin.MainForm.ClickSpeedItem(percent);
|
|
||||||
else Console.WriteLine("Invalid speed value");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void TogglePause() => GlobalWin.MainForm.TogglePause();
|
/// <inheritdoc cref="IEmuClient.Xpos"/>
|
||||||
|
public static int Xpos() => GlobalWin.ClientApi.Xpos();
|
||||||
|
|
||||||
public static Point TransformPoint(Point point) => GlobalWin.DisplayManager.TransformPoint(point);
|
/// <inheritdoc cref="IEmuClient.Ypos"/>
|
||||||
|
public static int Ypos() => GlobalWin.ClientApi.Ypos();
|
||||||
public static void Unpause() => GlobalWin.MainForm.UnpauseEmulator();
|
|
||||||
|
|
||||||
public static void UnpauseAv() => GlobalWin.MainForm.PauseAvi = false;
|
|
||||||
|
|
||||||
public static int Xpos() => GlobalWin.MainForm.DesktopLocation.X;
|
|
||||||
|
|
||||||
public static int Ypos() => GlobalWin.MainForm.DesktopLocation.Y;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets current emulated system
|
|
||||||
/// </summary>
|
|
||||||
public static SystemInfo RunningSystem
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
switch (GlobalWin.Emulator.SystemId)
|
|
||||||
{
|
|
||||||
case "PCE" when GlobalWin.Emulator is PCEngine pceHawk:
|
|
||||||
return pceHawk.Type switch
|
|
||||||
{
|
|
||||||
NecSystemType.TurboGrafx => SystemInfo.PCE,
|
|
||||||
NecSystemType.TurboCD => SystemInfo.PCECD,
|
|
||||||
NecSystemType.SuperGrafx => SystemInfo.SGX,
|
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
|
||||||
};
|
|
||||||
case "PCE":
|
|
||||||
return SystemInfo.PCE; // not always accurate, but anyone wanting accuracy has probably figured out how to use IEmu.GetSystemId()
|
|
||||||
case "SMS":
|
|
||||||
var sms = (SMS) GlobalWin.Emulator;
|
|
||||||
return sms.IsSG1000
|
|
||||||
? SystemInfo.SG
|
|
||||||
: sms.IsGameGear
|
|
||||||
? SystemInfo.GG
|
|
||||||
: SystemInfo.SMS;
|
|
||||||
case "GB":
|
|
||||||
if (GlobalWin.Emulator is Gameboy gb) return gb.IsCGBMode() ? SystemInfo.GBC : SystemInfo.GB;
|
|
||||||
return SystemInfo.DualGB;
|
|
||||||
default:
|
|
||||||
return SystemInfo.FindByCoreSystem(SystemIdConverter.Convert(GlobalWin.Emulator.SystemId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,340 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
using BizHawk.Client.Common;
|
||||||
|
using BizHawk.Common;
|
||||||
|
using BizHawk.Emulation.Common;
|
||||||
|
using BizHawk.Emulation.Cores.Nintendo.Gameboy;
|
||||||
|
using BizHawk.Emulation.Cores.PCEngine;
|
||||||
|
using BizHawk.Emulation.Cores.Sega.MasterSystem;
|
||||||
|
|
||||||
|
namespace BizHawk.Client.EmuHawk
|
||||||
|
{
|
||||||
|
public class EmuClientApi : IEmuClient
|
||||||
|
{
|
||||||
|
private List<Joypad> _allJoyPads;
|
||||||
|
|
||||||
|
private IEmulator Emulator { get; set; }
|
||||||
|
|
||||||
|
private readonly IReadOnlyCollection<JoypadButton> JoypadButtonsArray = Enum.GetValues(typeof(JoypadButton)).Cast<JoypadButton>().ToList(); //TODO can the return of GetValues be cast to JoypadButton[]? --yoshi
|
||||||
|
|
||||||
|
private readonly JoypadStringToEnumConverter JoypadConverter = new JoypadStringToEnumConverter();
|
||||||
|
|
||||||
|
public SystemInfo RunningSystem
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (GlobalWin.Emulator.SystemId)
|
||||||
|
{
|
||||||
|
case "PCE" when GlobalWin.Emulator is PCEngine pceHawk:
|
||||||
|
return pceHawk.Type switch
|
||||||
|
{
|
||||||
|
NecSystemType.TurboGrafx => SystemInfo.PCE,
|
||||||
|
NecSystemType.TurboCD => SystemInfo.PCECD,
|
||||||
|
NecSystemType.SuperGrafx => SystemInfo.SGX,
|
||||||
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
|
};
|
||||||
|
case "PCE":
|
||||||
|
return SystemInfo.PCE; // not always accurate, but anyone wanting accuracy has probably figured out how to use IEmu.GetSystemId()
|
||||||
|
case "SMS":
|
||||||
|
var sms = (SMS) GlobalWin.Emulator;
|
||||||
|
return sms.IsSG1000
|
||||||
|
? SystemInfo.SG
|
||||||
|
: sms.IsGameGear
|
||||||
|
? SystemInfo.GG
|
||||||
|
: SystemInfo.SMS;
|
||||||
|
case "GB":
|
||||||
|
if (GlobalWin.Emulator is Gameboy gb) return gb.IsCGBMode() ? SystemInfo.GBC : SystemInfo.GB;
|
||||||
|
return SystemInfo.DualGB;
|
||||||
|
default:
|
||||||
|
return SystemInfo.FindByCoreSystem(SystemIdConverter.Convert(GlobalWin.Emulator.SystemId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal readonly BizHawkSystemIdToEnumConverter SystemIdConverter = new BizHawkSystemIdToEnumConverter();
|
||||||
|
|
||||||
|
private IVideoProvider VideoProvider { get; set; }
|
||||||
|
|
||||||
|
public event BeforeQuickLoadEventHandler BeforeQuickLoad;
|
||||||
|
|
||||||
|
public event BeforeQuickSaveEventHandler BeforeQuickSave;
|
||||||
|
|
||||||
|
public event EventHandler RomLoaded;
|
||||||
|
|
||||||
|
public event StateLoadedEventHandler StateLoaded;
|
||||||
|
|
||||||
|
public event StateSavedEventHandler StateSaved;
|
||||||
|
|
||||||
|
public int BorderHeight() => GlobalWin.DisplayManager.TransformPoint(new Point(0, 0)).Y;
|
||||||
|
|
||||||
|
public int BorderWidth() => GlobalWin.DisplayManager.TransformPoint(new Point(0, 0)).X;
|
||||||
|
|
||||||
|
public int BufferHeight() => VideoProvider.BufferHeight;
|
||||||
|
|
||||||
|
public int BufferWidth() => VideoProvider.BufferWidth;
|
||||||
|
|
||||||
|
public void ClearAutohold() => GlobalWin.MainForm.ClearHolds();
|
||||||
|
|
||||||
|
public void CloseEmulator() => GlobalWin.MainForm.CloseEmulator();
|
||||||
|
|
||||||
|
public void CloseEmulatorWithCode(int exitCode) => GlobalWin.MainForm.CloseEmulator(exitCode);
|
||||||
|
|
||||||
|
public void CloseRom() => GlobalWin.MainForm.CloseRom();
|
||||||
|
|
||||||
|
public void DisplayMessages(bool value) => GlobalWin.Config.DisplayMessages = value;
|
||||||
|
|
||||||
|
public void DoFrameAdvance()
|
||||||
|
{
|
||||||
|
GlobalWin.MainForm.FrameAdvance();
|
||||||
|
GlobalWin.MainForm.StepRunLoop_Throttle();
|
||||||
|
GlobalWin.MainForm.Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DoFrameAdvanceAndUnpause()
|
||||||
|
{
|
||||||
|
DoFrameAdvance();
|
||||||
|
UnpauseEmulation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnableRewind(bool enabled) => GlobalWin.MainForm.EnableRewind(enabled);
|
||||||
|
|
||||||
|
public void FrameSkip(int numFrames)
|
||||||
|
{
|
||||||
|
if (numFrames > 0)
|
||||||
|
{
|
||||||
|
GlobalWin.Config.FrameSkip = numFrames;
|
||||||
|
GlobalWin.MainForm.FrameSkipMessage();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Invalid frame skip value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetAllInputs()
|
||||||
|
{
|
||||||
|
var joypadAdapter = GlobalWin.InputManager.AutofireStickyXorAdapter;
|
||||||
|
|
||||||
|
var pressedButtons = joypadAdapter.Definition.BoolButtons.Where(b => joypadAdapter.IsPressed(b));
|
||||||
|
|
||||||
|
foreach (var j in _allJoyPads) j.ClearInputs();
|
||||||
|
|
||||||
|
Parallel.ForEach(pressedButtons, button =>
|
||||||
|
{
|
||||||
|
if (RunningSystem == SystemInfo.GB) _allJoyPads[0].AddInput(JoypadConverter.Convert(button));
|
||||||
|
else if (int.TryParse(button.Substring(1, 2), out var player)) _allJoyPads[player - 1].AddInput(JoypadConverter.Convert(button.Substring(3)));
|
||||||
|
});
|
||||||
|
|
||||||
|
if ((RunningSystem.AvailableButtons & JoypadButton.AnalogStick) == JoypadButton.AnalogStick)
|
||||||
|
{
|
||||||
|
for (var i = 1; i <= RunningSystem.MaxControllers; i++)
|
||||||
|
{
|
||||||
|
_allJoyPads[i - 1].AnalogX = joypadAdapter.AxisValue($"P{i} X Axis");
|
||||||
|
_allJoyPads[i - 1].AnalogY = joypadAdapter.AxisValue($"P{i} Y Axis");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Joypad GetInput(int player)
|
||||||
|
{
|
||||||
|
if (!1.RangeTo(RunningSystem.MaxControllers).Contains(player))
|
||||||
|
throw new IndexOutOfRangeException($"{RunningSystem.DisplayName} does not support {player} controller(s)");
|
||||||
|
GetAllInputs();
|
||||||
|
return _allJoyPads[player - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetSoundOn() => GlobalWin.Config.SoundEnabled;
|
||||||
|
|
||||||
|
public int GetTargetScanlineIntensity() => GlobalWin.Config.TargetScanlineFilterIntensity;
|
||||||
|
|
||||||
|
public int GetWindowSize() => GlobalWin.Config.TargetZoomFactors[Emulator.SystemId];
|
||||||
|
|
||||||
|
public void InvisibleEmulation(bool invisible) => GlobalWin.MainForm.InvisibleEmulation = invisible;
|
||||||
|
|
||||||
|
public bool IsPaused() => GlobalWin.MainForm.EmulatorPaused;
|
||||||
|
|
||||||
|
public bool IsSeeking() => GlobalWin.MainForm.IsSeeking;
|
||||||
|
|
||||||
|
public bool IsTurbo() => GlobalWin.MainForm.IsTurboing;
|
||||||
|
|
||||||
|
public void LoadState(string name) => GlobalWin.MainForm.LoadState(Path.Combine(GlobalWin.Config.PathEntries.SaveStateAbsolutePath(GlobalWin.Game.System), $"{name}.State"), name, suppressOSD: false);
|
||||||
|
|
||||||
|
public void OnBeforeQuickLoad(object sender, string quickSaveSlotName, out bool eventHandled)
|
||||||
|
{
|
||||||
|
if (BeforeQuickLoad == null)
|
||||||
|
{
|
||||||
|
eventHandled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var e = new BeforeQuickLoadEventArgs(quickSaveSlotName);
|
||||||
|
BeforeQuickLoad(sender, e);
|
||||||
|
eventHandled = e.Handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnBeforeQuickSave(object sender, string quickSaveSlotName, out bool eventHandled)
|
||||||
|
{
|
||||||
|
if (BeforeQuickSave == null)
|
||||||
|
{
|
||||||
|
eventHandled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var e = new BeforeQuickSaveEventArgs(quickSaveSlotName);
|
||||||
|
BeforeQuickSave(sender, e);
|
||||||
|
eventHandled = e.Handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnRomLoaded(IEmulator emu)
|
||||||
|
{
|
||||||
|
Emulator = emu;
|
||||||
|
VideoProvider = emu.AsVideoProviderOrDefault();
|
||||||
|
RomLoaded?.Invoke(null, EventArgs.Empty);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_allJoyPads = new List<Joypad>(RunningSystem.MaxControllers);
|
||||||
|
for (var i = 1; i <= RunningSystem.MaxControllers; i++)
|
||||||
|
_allJoyPads.Add(new Joypad(RunningSystem, i));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("Apihawk is garbage and may not work this session.");
|
||||||
|
Console.Error.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnStateLoaded(object sender, string stateName) => StateLoaded?.Invoke(sender, new StateLoadedEventArgs(stateName));
|
||||||
|
|
||||||
|
public void OnStateSaved(object sender, string stateName) => StateSaved?.Invoke(sender, new StateSavedEventArgs(stateName));
|
||||||
|
|
||||||
|
public void OpenRom(string path) => GlobalWin.MainForm.LoadRom(path, new MainForm.LoadRomArgs { OpenAdvanced = OpenAdvancedSerializer.ParseWithLegacy(path) });
|
||||||
|
|
||||||
|
public void Pause() => GlobalWin.MainForm.PauseEmulator();
|
||||||
|
|
||||||
|
public void PauseAv() => GlobalWin.MainForm.PauseAvi = true;
|
||||||
|
|
||||||
|
public void RebootCore() => GlobalWin.MainForm.RebootCore();
|
||||||
|
|
||||||
|
public void SaveRam() => GlobalWin.MainForm.FlushSaveRAM();
|
||||||
|
|
||||||
|
public void SaveState(string name) => GlobalWin.MainForm.SaveState(Path.Combine(GlobalWin.Config.PathEntries.SaveStateAbsolutePath(GlobalWin.Game.System), $"{name}.State"), name, fromLua: false);
|
||||||
|
|
||||||
|
public int ScreenHeight() => GlobalWin.MainForm.PresentationPanel.NativeSize.Height;
|
||||||
|
|
||||||
|
public void Screenshot(string path)
|
||||||
|
{
|
||||||
|
if (path == null) GlobalWin.MainForm.TakeScreenshot();
|
||||||
|
else GlobalWin.MainForm.TakeScreenshot(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ScreenshotToClipboard() => GlobalWin.MainForm.TakeScreenshotToClipboard();
|
||||||
|
|
||||||
|
public int ScreenWidth() => GlobalWin.MainForm.PresentationPanel.NativeSize.Width;
|
||||||
|
|
||||||
|
public void SeekFrame(int frame)
|
||||||
|
{
|
||||||
|
var wasPaused = GlobalWin.MainForm.EmulatorPaused;
|
||||||
|
while (Emulator.Frame != frame) GlobalWin.MainForm.SeekFrameAdvance();
|
||||||
|
if (!wasPaused) GlobalWin.MainForm.UnpauseEmulator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetExtraPadding(int left, int top, int right, int bottom)
|
||||||
|
{
|
||||||
|
GlobalWin.DisplayManager.ClientExtraPadding = new Padding(left, top, right, bottom);
|
||||||
|
GlobalWin.MainForm.FrameBufferResized();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetGameExtraPadding(int left, int top, int right, int bottom)
|
||||||
|
{
|
||||||
|
GlobalWin.DisplayManager.GameExtraPadding = new Padding(left, top, right, bottom);
|
||||||
|
GlobalWin.MainForm.FrameBufferResized();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetInput(int player, Joypad joypad)
|
||||||
|
{
|
||||||
|
if (!1.RangeTo(RunningSystem.MaxControllers).Contains(player)) throw new IndexOutOfRangeException($"{RunningSystem.DisplayName} does not support {player} controller(s)");
|
||||||
|
|
||||||
|
if (joypad.Inputs == 0)
|
||||||
|
{
|
||||||
|
GlobalWin.InputManager.AutofireStickyXorAdapter.ClearStickies();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var button in JoypadButtonsArray.Where(button => joypad.Inputs.HasFlag(button)))
|
||||||
|
{
|
||||||
|
GlobalWin.InputManager.AutofireStickyXorAdapter.SetSticky(
|
||||||
|
RunningSystem == SystemInfo.GB
|
||||||
|
? $"{JoypadConverter.ConvertBack(button, RunningSystem)}"
|
||||||
|
: $"P{player} {JoypadConverter.ConvertBack(button, RunningSystem)}",
|
||||||
|
isSticky: true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if false // Using this breaks joypad usage (even in UI); have to figure out why
|
||||||
|
if ((RunningSystem.AvailableButtons & JoypadButton.AnalogStick) == JoypadButton.AnalogStick)
|
||||||
|
{
|
||||||
|
for (var i = 1; i <= RunningSystem.MaxControllers; i++)
|
||||||
|
{
|
||||||
|
GlobalWin.InputManager.AutofireStickyXorAdapter.SetAxis($"P{i} X Axis", _allJoyPads[i - 1].AnalogX);
|
||||||
|
GlobalWin.InputManager.AutofireStickyXorAdapter.SetAxis($"P{i} Y Axis", _allJoyPads[i - 1].AnalogY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetScreenshotOSD(bool value) => GlobalWin.Config.ScreenshotCaptureOsd = value;
|
||||||
|
|
||||||
|
public void SetSoundOn(bool enable)
|
||||||
|
{
|
||||||
|
if (enable != GlobalWin.Config.SoundEnabled) GlobalWin.MainForm.ToggleSound();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTargetScanlineIntensity(int val) => GlobalWin.Config.TargetScanlineFilterIntensity = val;
|
||||||
|
|
||||||
|
public void SetWindowSize(int size)
|
||||||
|
{
|
||||||
|
if (size == 1 || size == 2 || size == 3 || size == 4 || size == 5 || size == 10)
|
||||||
|
{
|
||||||
|
GlobalWin.Config.TargetZoomFactors[Emulator.SystemId] = size;
|
||||||
|
GlobalWin.MainForm.FrameBufferResized();
|
||||||
|
GlobalWin.OSD.AddMessage($"Window size set to {size}x");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Invalid window size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SpeedMode(int percent)
|
||||||
|
{
|
||||||
|
if (percent.StrictlyBoundedBy(0.RangeTo(6400))) GlobalWin.MainForm.ClickSpeedItem(percent);
|
||||||
|
else Console.WriteLine("Invalid speed value");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TogglePause() => GlobalWin.MainForm.TogglePause();
|
||||||
|
|
||||||
|
public Point TransformPoint(Point point) => GlobalWin.DisplayManager.TransformPoint(point);
|
||||||
|
|
||||||
|
public void Unpause() => GlobalWin.MainForm.UnpauseEmulator();
|
||||||
|
|
||||||
|
public void UnpauseAv() => GlobalWin.MainForm.PauseAvi = false;
|
||||||
|
|
||||||
|
public void UnpauseEmulation() => GlobalWin.MainForm.UnpauseEmulator();
|
||||||
|
|
||||||
|
public void UpdateEmulatorAndVP(IEmulator emu)
|
||||||
|
{
|
||||||
|
Emulator = emu;
|
||||||
|
VideoProvider = emu.AsVideoProviderOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Xpos() => GlobalWin.MainForm.DesktopLocation.X;
|
||||||
|
|
||||||
|
public int Ypos() => GlobalWin.MainForm.DesktopLocation.Y;
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,5 +47,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
public static GameInfo Game { get; set; }
|
public static GameInfo Game { get; set; }
|
||||||
public static IMovieSession MovieSession { get; set; }
|
public static IMovieSession MovieSession { get; set; }
|
||||||
public static InputManager InputManager { get; } = new InputManager();
|
public static InputManager InputManager { get; } = new InputManager();
|
||||||
|
|
||||||
|
public static EmuClientApi ClientApi { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -275,6 +275,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
public MainForm(string[] args)
|
public MainForm(string[] args)
|
||||||
{
|
{
|
||||||
GlobalWin.MainForm = this;
|
GlobalWin.MainForm = this;
|
||||||
|
GlobalWin.ClientApi = new EmuClientApi();
|
||||||
|
|
||||||
//do this threaded stuff early so it has plenty of time to run in background
|
//do this threaded stuff early so it has plenty of time to run in background
|
||||||
Database.InitializeDatabase(Path.Combine(PathUtils.ExeDirectoryPath, "gamedb", "gamedb.txt"));
|
Database.InitializeDatabase(Path.Combine(PathUtils.ExeDirectoryPath, "gamedb", "gamedb.txt"));
|
||||||
|
|
|
@ -99,7 +99,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
item.Tag = (externalToolFile.Location, entryPoint.FullName); // Tag set => no errors (show custom icon even when disabled)
|
item.Tag = (externalToolFile.Location, entryPoint.FullName); // Tag set => no errors (show custom icon even when disabled)
|
||||||
if (applicabilityAttrs.Count == 1)
|
if (applicabilityAttrs.Count == 1)
|
||||||
{
|
{
|
||||||
var system = ClientApi.SystemIdConverter.Convert(GlobalWin.Emulator.SystemId);
|
var system = GlobalWin.ClientApi.SystemIdConverter.Convert(GlobalWin.Emulator.SystemId);
|
||||||
if (applicabilityAttrs[0].NotApplicableTo(system))
|
if (applicabilityAttrs[0].NotApplicableTo(system))
|
||||||
{
|
{
|
||||||
item.ToolTipText = system == CoreSystem.Null
|
item.ToolTipText = system == CoreSystem.Null
|
||||||
|
|
Loading…
Reference in New Issue