2014-08-23 19:06:37 +00:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Reflection ;
using BizHawk.Emulation.Common ;
namespace BizHawk.Emulation.Cores
{
2014-08-24 01:52:37 +00:00
/// <summary>
/// finds and instantiates IEmulator cores
/// </summary>
2014-08-23 19:06:37 +00:00
public class CoreInventory
{
2020-02-08 16:07:54 +00:00
private readonly Dictionary < string , List < Core > > _systems = new Dictionary < string , List < Core > > ( ) ;
2014-08-23 19:06:37 +00:00
public class Core
{
2020-02-08 16:07:54 +00:00
// expected names and types of the parameters
private static readonly Dictionary < string , Type > ParamTypes = new Dictionary < string , Type > ( ) ;
// map parameter names to locations in the constructor
private readonly Dictionary < string , int > _paramMap = new Dictionary < string , int > ( ) ;
static Core ( )
{
var pp = typeof ( Core ) . GetMethod ( "Create" ) ? . GetParameters ( ) ;
if ( pp ! = null )
{
foreach ( var p in pp )
{
ParamTypes . Add ( p . Name . ToLowerInvariant ( ) , p . ParameterType ) ;
}
}
}
2014-08-23 19:06:37 +00:00
2020-02-08 16:07:54 +00:00
public Core ( string name , Type type , ConstructorInfo ctor )
2014-08-23 19:06:37 +00:00
{
2020-02-08 16:07:54 +00:00
Name = name ;
Type = type ;
CTor = ctor ;
2014-08-23 19:06:37 +00:00
var pp = CTor . GetParameters ( ) ;
for ( int i = 0 ; i < pp . Length ; i + + )
{
var p = pp [ i ] ;
2020-02-08 16:07:54 +00:00
string pName = p . Name . ToLowerInvariant ( ) ;
if ( ! ParamTypes . TryGetValue ( pName , out _ ) )
{
2019-04-03 16:41:18 +00:00
throw new InvalidOperationException ( $"Unexpected parameter name {p.Name} in constructor for {Type}" ) ;
2020-02-08 16:07:54 +00:00
}
2015-06-13 18:01:26 +00:00
2020-02-08 16:07:54 +00:00
// disabling the type check here doesn't really hurt anything, because the Invoke call will still catch any forbidden casts
2015-06-13 18:01:26 +00:00
// it does allow us to write "MySettingsType settings" instead of "object settings"
2020-02-08 16:07:54 +00:00
// if (expectedType != p.ParameterType)
2019-04-03 16:41:18 +00:00
// throw new InvalidOperationException($"Unexpected type mismatch in parameter {p.Name} in constructor for {Type}");
2020-02-08 16:07:54 +00:00
_paramMap . Add ( pName , i ) ;
2014-08-23 19:06:37 +00:00
}
}
2020-02-08 16:07:54 +00:00
public string Name { get ; }
public Type Type { get ; }
public ConstructorInfo CTor { get ; }
2014-08-23 19:06:37 +00:00
2020-02-08 16:07:54 +00:00
private void Bp ( object [ ] parameters , string name , object value )
2014-08-23 19:06:37 +00:00
{
2020-02-08 16:07:54 +00:00
if ( _paramMap . TryGetValue ( name , out var i ) )
{
2014-08-23 19:06:37 +00:00
parameters [ i ] = value ;
2020-02-08 16:07:54 +00:00
}
2014-08-23 19:06:37 +00:00
}
2014-08-24 01:52:37 +00:00
/// <summary>
2020-02-08 16:07:54 +00:00
/// Instantiate an emulator core
2014-08-24 01:52:37 +00:00
/// </summary>
2014-08-23 19:06:37 +00:00
public IEmulator Create
(
CoreComm comm ,
GameInfo game ,
byte [ ] rom ,
2014-09-12 01:02:09 +00:00
byte [ ] file ,
2014-08-23 19:06:37 +00:00
bool deterministic ,
object settings ,
2020-02-08 16:07:54 +00:00
object syncSettings
2014-08-23 19:06:37 +00:00
)
{
2020-02-08 16:07:54 +00:00
object [ ] o = new object [ _paramMap . Count ] ;
Bp ( o , "comm" , comm ) ;
Bp ( o , "game" , game ) ;
Bp ( o , "rom" , rom ) ;
Bp ( o , "file" , file ) ;
Bp ( o , "deterministic" , deterministic ) ;
Bp ( o , "settings" , settings ) ;
Bp ( o , "syncsettings" , syncSettings ) ;
2014-08-23 19:06:37 +00:00
return ( IEmulator ) CTor . Invoke ( o ) ;
}
}
2020-02-08 16:07:54 +00:00
private void ProcessConstructor ( Type type , string system , CoreAttribute coreAttr , ConstructorInfo cons )
2014-08-23 19:06:37 +00:00
{
2020-02-08 16:07:54 +00:00
Core core = new Core ( coreAttr . CoreName , type , cons ) ;
if ( ! _systems . TryGetValue ( system , out var ss ) )
2014-08-23 19:06:37 +00:00
{
ss = new List < Core > ( ) ;
2020-02-08 16:07:54 +00:00
_systems . Add ( system , ss ) ;
2014-08-23 19:06:37 +00:00
}
2020-02-08 16:07:54 +00:00
2014-08-23 19:06:37 +00:00
ss . Add ( core ) ;
}
2014-08-24 01:52:37 +00:00
/// <summary>
/// find a core matching a particular game.system
/// </summary>
2014-08-23 19:06:37 +00:00
public Core this [ string system ]
{
get
{
2020-02-08 16:07:54 +00:00
List < Core > ss = _systems [ system ] ;
2014-08-23 19:06:37 +00:00
if ( ss . Count ! = 1 )
2020-02-08 16:07:54 +00:00
{
2014-08-23 19:06:37 +00:00
throw new InvalidOperationException ( "Ambiguous core selection!" ) ;
2020-02-08 16:07:54 +00:00
}
2014-08-23 19:06:37 +00:00
return ss [ 0 ] ;
}
}
2014-08-24 01:52:37 +00:00
/// <summary>
2020-02-08 16:07:54 +00:00
/// find a core matching a particular game.system with a particular CoreAttributes.Name
2014-08-24 01:52:37 +00:00
/// </summary>
2014-08-23 19:06:37 +00:00
public Core this [ string system , string core ]
{
get
{
2020-02-08 16:07:54 +00:00
List < Core > ss = _systems [ system ] ;
2014-08-23 19:06:37 +00:00
foreach ( Core c in ss )
{
if ( c . Name = = core )
2020-02-08 16:07:54 +00:00
{
2014-08-23 19:06:37 +00:00
return c ;
2020-02-08 16:07:54 +00:00
}
2014-08-23 19:06:37 +00:00
}
2020-02-08 16:07:54 +00:00
throw new InvalidOperationException ( "No such core!" ) ;
2014-11-23 22:20:55 +00:00
}
}
2014-08-24 01:52:37 +00:00
/// <summary>
2020-02-08 16:07:54 +00:00
/// create a core inventory, collecting all IEmulators from some assemblies
2014-08-24 01:52:37 +00:00
/// </summary>
2014-08-23 19:06:37 +00:00
public CoreInventory ( IEnumerable < Assembly > assys )
{
foreach ( var assy in assys )
{
foreach ( var typ in assy . GetTypes ( ) )
{
2017-06-17 14:07:02 +00:00
if ( ! typ . IsAbstract & & typ . GetInterfaces ( ) . Contains ( typeof ( IEmulator ) ) )
2014-08-23 19:06:37 +00:00
{
2020-02-08 16:07:54 +00:00
var coreAttr = typ . GetCustomAttributes ( typeof ( CoreAttribute ) , false ) ;
if ( coreAttr . Length ! = 1 )
2019-03-28 03:18:58 +00:00
throw new InvalidOperationException ( $"{nameof(IEmulator)} {typ} without {nameof(CoreAttribute)}s!" ) ;
2014-08-23 19:06:37 +00:00
var cons = typ . GetConstructors ( BindingFlags . Public | BindingFlags . Instance )
2019-10-13 15:50:57 +00:00
. Where ( c = > c . GetCustomAttributes ( typeof ( CoreConstructorAttribute ) , false ) . Length > 0 ) ;
2014-08-23 19:06:37 +00:00
foreach ( var con in cons )
{
foreach ( string system in ( ( CoreConstructorAttribute ) con . GetCustomAttributes ( typeof ( CoreConstructorAttribute ) , false ) [ 0 ] ) . Systems )
{
2020-02-08 16:07:54 +00:00
ProcessConstructor ( typ , system , ( CoreAttribute ) coreAttr [ 0 ] , con ) ;
2014-08-23 19:06:37 +00:00
}
}
}
}
}
}
public static readonly CoreInventory Instance = new CoreInventory ( new [ ] { typeof ( CoreInventory ) . Assembly } ) ;
}
[AttributeUsage(AttributeTargets.Constructor)]
2020-02-18 15:10:24 +00:00
public sealed class CoreConstructorAttribute : Attribute
2014-08-23 19:06:37 +00:00
{
private readonly List < string > _systems = new List < string > ( ) ;
2020-02-08 16:07:54 +00:00
2020-02-18 15:10:24 +00:00
/// <remarks>TODO neither array nor <see cref="IEnumerable{T}"/> is the correct collection to be using here, try <see cref="IReadOnlyList{T}"/>/<see cref="IReadOnlyCollection{T}"/> instead</remarks>
public CoreConstructorAttribute ( string [ ] systems )
2014-08-23 19:06:37 +00:00
{
2020-02-08 16:07:54 +00:00
_systems . AddRange ( systems ) ;
2014-08-23 19:06:37 +00:00
}
2020-02-08 16:07:54 +00:00
2020-02-18 15:10:24 +00:00
public CoreConstructorAttribute ( string system )
{
_systems . Add ( system ) ;
}
2020-02-08 16:07:54 +00:00
public IEnumerable < string > Systems = > _systems ;
2014-08-23 19:06:37 +00:00
}
}