2016-03-16 22:06:53 +00:00
using System ;
2016-03-26 22:07:44 +00:00
using System.Collections.Generic ;
using System.Linq ;
2016-03-16 22:06:53 +00:00
using System.Reflection ;
2016-03-26 22:07:44 +00:00
using System.Threading.Tasks ;
2016-04-06 20:10:36 +00:00
using System.Windows.Forms ;
2016-03-26 22:07:44 +00:00
using BizHawk.Client.Common ;
using BizHawk.Emulation.Cores.Nintendo.Gameboy ;
using BizHawk.Emulation.Cores.PCEngine ;
using BizHawk.Emulation.Cores.Sega.MasterSystem ;
2016-05-29 14:50:16 +00:00
using BizHawk.Client.ApiHawk.Classes.Events ;
using System.IO ;
2016-03-16 22:06:53 +00:00
namespace BizHawk.Client.ApiHawk
{
/// <summary>
/// This class contains some methods that
/// interract with BizHawk client
/// </summary>
public static class ClientApi
{
#region Fields
private static readonly Assembly clientAssembly ;
2016-04-03 16:17:22 +00:00
private static readonly object clientMainFormInstance ;
private static readonly Type mainFormClass ;
2016-04-06 20:10:36 +00:00
private static readonly Array joypadButtonsArray = Enum . GetValues ( typeof ( JoypadButton ) ) ;
2016-04-03 16:17:22 +00:00
2016-03-26 22:07:44 +00:00
internal static readonly BizHawkSystemIdToEnumConverter SystemIdConverter = new BizHawkSystemIdToEnumConverter ( ) ;
internal static readonly JoypadStringToEnumConverter JoypadConverter = new JoypadStringToEnumConverter ( ) ;
2016-03-20 22:05:38 +00:00
2016-03-26 22:07:44 +00:00
private static List < Joypad > allJoypads ;
2016-05-29 14:50:16 +00:00
/// <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 succesfully loaded
/// </summary>
public static event EventHandler RomLoaded ;
/// <summary>
/// Occurs when a savestate is sucessfully loaded
/// </summary>
public static event StateLoadedEventHandler StateLoaded ;
/// <summary>
/// Occurs when a savestate is successfully saved
/// </summary>
public static event StateSavedEventHandler StateSaved ;
2016-03-16 22:06:53 +00:00
#endregion
#region cTor ( s )
2016-04-03 16:17:22 +00:00
/// <summary>
/// Static stuff initilization
/// </summary>
2016-03-16 22:06:53 +00:00
static ClientApi ( )
{
clientAssembly = Assembly . GetEntryAssembly ( ) ;
2016-04-03 16:17:22 +00:00
clientMainFormInstance = clientAssembly . GetType ( "BizHawk.Client.EmuHawk.GlobalWin" ) . GetField ( "MainForm" ) . GetValue ( null ) ;
mainFormClass = clientAssembly . GetType ( "BizHawk.Client.EmuHawk.MainForm" ) ;
2016-03-16 22:06:53 +00:00
}
#endregion
#region Methods
2016-03-26 22:07:44 +00:00
#region Public
2016-03-23 22:05:05 +00:00
/// <summary>
/// THE FrameAdvance stuff
/// </summary>
2016-03-26 22:07:44 +00:00
public static void DoFrameAdvance ( )
2016-03-20 22:05:38 +00:00
{
2016-04-03 16:17:22 +00:00
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 ) ;
2016-03-26 22:07:44 +00:00
}
/// <summary>
/// THE FrameAdvance stuff
/// Auto unpause emulation
/// </summary>
public static void DoFrameAdvanceAndUnpause ( )
{
2016-04-03 16:17:22 +00:00
DoFrameAdvance ( ) ;
UnpauseEmulation ( ) ;
2016-03-26 22:07:44 +00:00
}
/// <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 ( 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 ] ;
}
}
2016-03-20 22:05:38 +00:00
2016-05-29 14:50:16 +00:00
/// <summary>
/// Load a savestate specified by its name
/// </summary>
/// <param name="name">Savetate friendly name</param>
public static void LoadState ( string name )
{
MethodInfo method = mainFormClass . GetMethod ( "LoadState" ) ;
method . Invoke ( clientMainFormInstance , new object [ ] { Path . Combine ( PathManager . GetSaveStatePath ( Global . Game ) , string . Format ( "{0}.{1}" , name , "State" ) ) , name , false , false } ) ;
}
/// <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>
public static void OnBeforeQuickLoad ( object sender , string quickSaveSlotName , out bool eventHandled )
{
eventHandled = false ;
if ( BeforeQuickLoad ! = null )
{
BeforeQuickLoadEventArgs e = new BeforeQuickLoadEventArgs ( quickSaveSlotName ) ;
BeforeQuickLoad ( sender , e ) ;
eventHandled = e . Handled ;
}
}
/// <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>
public static void OnBeforeQuickSave ( object sender , string quickSaveSlotName , out bool eventHandled )
{
eventHandled = false ;
if ( BeforeQuickSave ! = null )
{
BeforeQuickSaveEventArgs e = new BeforeQuickSaveEventArgs ( quickSaveSlotName ) ;
BeforeQuickSave ( sender , e ) ;
eventHandled = e . Handled ;
}
}
/// <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>
public static void OnStateLoaded ( object sender , string stateName )
{
if ( StateLoaded ! = null )
{
StateLoaded ( sender , new StateLoadedEventArgs ( 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>
public static void OnStateSaved ( object sender , string stateName )
{
if ( StateSaved ! = null )
{
StateSaved ( sender , new StateSavedEventArgs ( stateName ) ) ;
}
}
2016-03-16 22:06:53 +00:00
/// <summary>
/// Raise when a rom is successfully Loaded
/// </summary>
public static void OnRomLoaded ( )
{
2016-03-20 22:05:38 +00:00
if ( RomLoaded ! = null )
2016-03-16 22:06:53 +00:00
{
RomLoaded ( null , EventArgs . Empty ) ;
}
2016-03-26 22:07:44 +00:00
allJoypads = new List < Joypad > ( RunningSystem . MaxControllers ) ;
for ( int i = 1 ; i < = RunningSystem . MaxControllers ; i + + )
{
allJoypads . Add ( new Joypad ( RunningSystem , i ) ) ;
}
2016-03-16 22:06:53 +00:00
}
2016-03-26 22:07:44 +00:00
2016-05-29 14:50:16 +00:00
/// <summary>
/// Save a state with specified name
/// </summary>
/// <param name="name">Savetate friendly name</param>
public static void SaveState ( string name )
{
MethodInfo method = mainFormClass . GetMethod ( "SaveState" ) ;
method . Invoke ( clientMainFormInstance , new object [ ] { Path . Combine ( PathManager . GetSaveStatePath ( Global . Game ) , string . Format ( "{0}.{1}" , name , "State" ) ) , name , false } ) ;
}
2016-03-16 22:06:53 +00:00
/// <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>
public static void SetExtraPadding ( int left , int top , int right , int bottom )
{
2016-04-06 20:10:36 +00:00
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 ) ) ;
2016-04-03 16:17:22 +00:00
MethodInfo resize = mainFormClass . GetMethod ( "FrameBufferResized" ) ;
2016-04-06 20:10:36 +00:00
resize . Invoke ( clientMainFormInstance , null ) ;
2016-03-16 22:06:53 +00:00
}
/// <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>
public static void SetExtraPadding ( int left )
{
SetExtraPadding ( left , 0 , 0 , 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>
public static void SetExtraPadding ( int left , int top )
{
SetExtraPadding ( left , top , 0 , 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>
public static void SetExtraPadding ( int left , int top , int right )
{
SetExtraPadding ( left , top , right , 0 ) ;
}
2016-03-26 22:07:44 +00:00
2016-03-20 22:05:38 +00:00
/// <summary>
2016-03-26 22:07:44 +00:00
/// Set inputs in specified <see cref="Joypad"/> to specified player
2016-03-20 22:05:38 +00:00
/// </summary>
2016-03-26 22:07:44 +00:00
/// <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>
2016-04-06 20:10:36 +00:00
/// <remarks>Still have some strange behaviour with multiple inputs; so this feature is still in beta</remarks>
2016-03-26 22:07:44 +00:00
public static void SetInput ( int player , Joypad joypad )
2016-03-20 22:05:38 +00:00
{
2016-03-26 22:07:44 +00:00
if ( player < 1 | | player > RunningSystem . MaxControllers )
2016-03-20 22:05:38 +00:00
{
2016-03-26 22:07:44 +00:00
throw new IndexOutOfRangeException ( string . Format ( "{0} does not support {1} controller(s)" , RunningSystem . DisplayName , player ) ) ;
}
else
{
2016-04-03 16:17:22 +00:00
if ( joypad . Inputs = = 0 )
2016-03-26 22:07:44 +00:00
{
2016-04-03 16:17:22 +00:00
AutoFireStickyXorAdapter joypadAdaptor = Global . AutofireStickyXORAdapter ;
joypadAdaptor . ClearStickies ( ) ;
}
else
{
2016-04-06 20:10:36 +00:00
foreach ( JoypadButton button in joypadButtonsArray )
2016-03-26 22:07:44 +00:00
{
2016-04-03 16:17:22 +00:00
if ( joypad . Inputs . HasFlag ( button ) )
{
AutoFireStickyXorAdapter joypadAdaptor = Global . AutofireStickyXORAdapter ;
2016-04-06 20:10:36 +00:00
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 ) ;
}
2016-04-03 16:17:22 +00:00
}
2016-03-26 22:07:44 +00:00
}
}
2016-04-06 20:10:36 +00:00
//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 ) ;
}
} * /
2016-03-26 22:07:44 +00:00
}
}
2016-03-20 22:05:38 +00:00
2016-03-26 22:07:44 +00:00
/// <summary>
/// Resume the emulation
/// </summary>
public static void UnpauseEmulation ( )
2016-04-06 20:10:36 +00:00
{
2016-04-03 16:17:22 +00:00
MethodInfo method = mainFormClass . GetMethod ( "UnpauseEmulator" ) ;
method . Invoke ( clientMainFormInstance , null ) ;
2016-03-20 22:05:38 +00:00
}
2016-03-26 22:07:44 +00:00
#endregion Public
2016-03-20 22:05:38 +00:00
/// <summary>
2016-03-26 22:07:44 +00:00
/// Gets all current inputs for each joypad and store
/// them in <see cref="Joypad"/> class collection
2016-03-20 22:05:38 +00:00
/// </summary>
2016-03-26 22:07:44 +00:00
private static void GetAllInputs ( )
2016-03-20 22:05:38 +00:00
{
2016-03-26 22:07:44 +00:00
AutoFireStickyXorAdapter joypadAdaptor = Global . AutofireStickyXORAdapter ;
2016-03-20 22:05:38 +00:00
2016-12-12 18:30:32 +00:00
IEnumerable < string > pressedButtons = from button in joypadAdaptor . Definition . BoolButtons
2016-12-14 20:12:16 +00:00
where joypadAdaptor . IsPressed ( button )
2016-03-26 22:07:44 +00:00
select button ;
2016-03-20 22:05:38 +00:00
2016-03-26 22:07:44 +00:00
foreach ( Joypad j in allJoypads )
{
j . ClearInputs ( ) ;
}
2016-03-20 22:05:38 +00:00
2016-03-26 22:07:44 +00:00
Parallel . ForEach < string > ( pressedButtons , button = >
{
int player ;
2016-04-06 20:10:36 +00:00
if ( RunningSystem = = SystemInfo . GB )
2016-03-26 22:07:44 +00:00
{
2016-04-06 20:10:36 +00:00
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 ) ) ) ;
}
2016-03-26 22:07:44 +00:00
}
} ) ;
if ( ( RunningSystem . AvailableButtons & JoypadButton . AnalogStick ) = = JoypadButton . AnalogStick )
{
2016-04-06 20:10:36 +00:00
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 ) ) ;
}
2016-03-20 22:05:38 +00:00
}
}
#endregion
#region Properties
/// <summary>
2016-03-26 22:07:44 +00:00
/// Gets current emulated system
2016-03-20 22:05:38 +00:00
/// </summary>
2016-03-26 22:07:44 +00:00
public static SystemInfo RunningSystem
2016-03-20 22:05:38 +00:00
{
get
{
2016-03-26 22:07:44 +00:00
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 ) ) ;
}
2016-03-20 22:05:38 +00:00
}
}
2016-03-16 22:06:53 +00:00
#endregion
}
}