2014-10-31 15:46:13 +00:00
using System ;
using System.Linq ;
using System.Reflection ;
namespace BizHawk.Emulation.Common
{
2016-03-02 02:10:09 +00:00
/// <summary>
/// This service provides mechanism for the client to set sync and non-sync related settings to the core
2017-04-19 13:31:48 +00:00
/// Settings are settings that can change during the lifetime of the core and do not affect potential movie sync
2016-03-02 02:10:09 +00:00
/// Sync Settings do not change during the lifetime of the core and affect movie sync
/// If available, Sync settings are stored in movie files and automatically applied when the movie is loaded
/// If this service is available the client can provide UI for the user to manage these settings
/// </summary>
/// <typeparam name="TSettings">The Type of the object that represent regular settings (settings that can be changed during the lifespan of a core instance</typeparam>
2016-12-13 21:56:20 +00:00
/// <typeparam name="TSync">The Type of the object that represents sync settings (settings that can not change during the lifespan of the core and are required for movie sync</typeparam>
2016-02-29 00:03:01 +00:00
public interface ISettable < TSettings , TSync > : IEmulatorService
{
// in addition to these methods, it's expected that the constructor or Load() method
// will take a Settings and SyncSettings object to set the initial state of the core
// (if those are null, default settings are to be used)
/// <summary>
/// get the current core settings, excepting movie settings. should be a clone of the active in-core object.
/// VERY IMPORTANT: changes to the object returned by this function SHOULD NOT have any effect on emulation
/// (unless the object is later passed to PutSettings)
/// </summary>
2017-04-19 14:41:26 +00:00
/// <returns>a JSON serializable object</returns>
2016-02-29 00:03:01 +00:00
TSettings GetSettings ( ) ;
/// <summary>
/// get the current core settings that affect movie sync. these go in movie 2.0 files, so don't make the JSON too extravagant, please
/// should be a clone of the active in-core object.
/// VERY IMPORTANT: changes to the object returned by this function MUST NOT have any effect on emulation
/// (unless the object is later passed to PutSyncSettings)
/// </summary>
2017-04-19 14:41:26 +00:00
/// <returns>a JSON serializable object</returns>
2016-02-29 00:03:01 +00:00
TSync GetSyncSettings ( ) ;
/// <summary>
/// change the core settings, excepting movie settings
/// </summary>
/// <param name="o">an object of the same type as the return for GetSettings</param>
/// <returns>true if a core reboot will be required to make the changes effective</returns>
bool PutSettings ( TSettings o ) ;
/// <summary>
/// changes the movie-sync relevant settings. THIS SHOULD NEVER BE CALLED WHILE RECORDING
/// if it is called while recording, the core need not guarantee continued determinism
/// </summary>
/// <param name="o">an object of the same type as the return for GetSyncSettings</param>
/// <returns>true if a core reboot will be required to make the changes effective</returns>
bool PutSyncSettings ( TSync o ) ;
}
2014-11-24 18:50:18 +00:00
/// <summary>
/// serves as a shim between strongly typed ISettable and consumers
/// </summary>
2014-10-31 15:46:13 +00:00
public class SettingsAdapter
{
public SettingsAdapter ( IEmulator e )
{
2017-04-19 13:31:48 +00:00
_emu = e ;
2014-10-31 15:46:13 +00:00
Type impl = e . GetType ( ) . GetInterfaces ( )
2017-04-19 13:31:48 +00:00
. FirstOrDefault ( t = > t . IsGenericType & & t . GetGenericTypeDefinition ( ) = = typeof ( ISettable < , > ) ) ;
2014-10-31 15:46:13 +00:00
if ( impl = = null )
{
HasSettings = false ;
HasSyncSettings = false ;
}
else
{
var tt = impl . GetGenericArguments ( ) ;
2017-04-19 13:31:48 +00:00
var settingtype = tt [ 0 ] ;
var synctype = tt [ 1 ] ;
2014-10-31 15:46:13 +00:00
HasSettings = settingtype ! = typeof ( object ) ; // object is used for a placeholder where an emu doesn't have both s and ss
HasSyncSettings = synctype ! = typeof ( object ) ;
if ( HasSettings )
{
2017-04-19 13:31:48 +00:00
_gets = impl . GetMethod ( "GetSettings" ) ;
_puts = impl . GetMethod ( "PutSettings" ) ;
2014-10-31 15:46:13 +00:00
}
2017-04-19 13:31:48 +00:00
2014-10-31 15:46:13 +00:00
if ( HasSyncSettings )
{
2017-04-19 13:31:48 +00:00
_getss = impl . GetMethod ( "GetSyncSettings" ) ;
_putss = impl . GetMethod ( "PutSyncSettings" ) ;
2014-10-31 15:46:13 +00:00
}
}
}
2017-04-19 13:31:48 +00:00
private readonly IEmulator _emu ;
2014-10-31 15:46:13 +00:00
2017-04-19 13:31:48 +00:00
public bool HasSettings { get ; }
public bool HasSyncSettings { get ; }
2014-10-31 15:46:13 +00:00
2017-04-19 13:31:48 +00:00
private readonly object [ ] _tmp1 = new object [ 1 ] ;
private readonly object [ ] _tmp0 = new object [ 0 ] ;
2014-10-31 15:46:13 +00:00
2017-04-20 00:34:30 +00:00
private readonly MethodInfo _gets ;
2017-04-19 13:31:48 +00:00
private readonly MethodInfo _puts ;
private readonly MethodInfo _getss ;
private readonly MethodInfo _putss ;
2014-10-31 15:46:13 +00:00
public object GetSettings ( )
{
if ( ! HasSettings )
2016-02-29 00:03:01 +00:00
{
2014-10-31 15:46:13 +00:00
throw new InvalidOperationException ( ) ;
2016-02-29 00:03:01 +00:00
}
2017-04-19 13:31:48 +00:00
return _gets . Invoke ( _emu , _tmp0 ) ;
2014-10-31 15:46:13 +00:00
}
public object GetSyncSettings ( )
{
if ( ! HasSyncSettings )
2016-02-29 00:03:01 +00:00
{
2014-10-31 15:46:13 +00:00
throw new InvalidOperationException ( ) ;
2016-02-29 00:03:01 +00:00
}
2017-04-19 13:31:48 +00:00
return _getss . Invoke ( _emu , _tmp0 ) ;
2014-10-31 15:46:13 +00:00
}
public bool PutSettings ( object o )
{
if ( ! HasSettings )
2016-02-29 00:03:01 +00:00
{
2014-10-31 15:46:13 +00:00
throw new InvalidOperationException ( ) ;
2016-02-29 00:03:01 +00:00
}
2017-04-19 13:31:48 +00:00
_tmp1 [ 0 ] = o ;
return ( bool ) _puts . Invoke ( _emu , _tmp1 ) ;
2014-10-31 15:46:13 +00:00
}
public bool PutSyncSettings ( object o )
{
if ( ! HasSyncSettings )
2016-02-29 00:03:01 +00:00
{
2014-10-31 15:46:13 +00:00
throw new InvalidOperationException ( ) ;
2016-02-29 00:03:01 +00:00
}
2017-04-19 13:31:48 +00:00
_tmp1 [ 0 ] = o ;
return ( bool ) _putss . Invoke ( _emu , _tmp1 ) ;
2014-10-31 15:46:13 +00:00
}
}
}