2017-04-15 19:53:02 +00:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Drawing ;
using System.IO ;
using System.Linq ;
using System.Reflection ;
using System.Windows.Forms ;
using BizHawk.Client.Common ;
namespace BizHawk.Client.ApiHawk
{
/// <summary>
/// This static class handle all ExternalTools
/// </summary>
public static class ExternalToolManager
{
#region Fields
private static FileSystemWatcher directoryMonitor ;
private static List < ToolStripMenuItem > menuItems = new List < ToolStripMenuItem > ( ) ;
#endregion
#region cTor ( s )
/// <summary>
/// Initilization
/// </summary>
static ExternalToolManager ( )
{
if ( ! Directory . Exists ( Global . Config . PathEntries [ "Global" , "External Tools" ] . Path ) )
2017-02-22 00:23:02 +00:00
{
2017-04-15 19:53:02 +00:00
Directory . CreateDirectory ( Global . Config . PathEntries [ "Global" , "External Tools" ] . Path ) ;
}
directoryMonitor = new FileSystemWatcher ( Global . Config . PathEntries [ "Global" , "External Tools" ] . Path , "*.dll" ) ;
directoryMonitor . IncludeSubdirectories = false ;
directoryMonitor . NotifyFilter = NotifyFilters . LastAccess | NotifyFilters . LastWrite | NotifyFilters . FileName ;
directoryMonitor . Filter = "*.dll" ;
directoryMonitor . Created + = new FileSystemEventHandler ( DirectoryMonitor_Created ) ;
directoryMonitor . EnableRaisingEvents = true ;
ClientApi . RomLoaded + = delegate { BuildToolStrip ( ) ; } ;
BuildToolStrip ( ) ;
}
#endregion
#region Methods
/// <summary>
/// Build the toolstrip menu
/// </summary>
private static void BuildToolStrip ( )
{
menuItems . Clear ( ) ;
if ( Directory . Exists ( directoryMonitor . Path ) )
{
DirectoryInfo dInfo = new DirectoryInfo ( directoryMonitor . Path ) ;
foreach ( FileInfo fi in dInfo . GetFiles ( "*.dll" ) )
{
menuItems . Add ( GenerateToolTipFromFileName ( fi . FullName ) ) ;
}
}
}
/// <summary>
/// Generate a <see cref="ToolStripMenuItem"/> from an
/// external tool dll.
/// The assembly must have <see cref="BizHawkExternalToolAttribute"/> in its
/// assembly attributes
/// </summary>
/// <param name="fileName">File that will be reflected</param>
/// <returns>A new <see cref="ToolStripMenuItem"/>; assembly path can be found in the Tag property</returns>
/// <remarks>For the moment, you could only load a dll that have a form (which implements <see cref="BizHawk.Client.EmuHawk.IExternalToolForm"/>)</remarks>
private static ToolStripMenuItem GenerateToolTipFromFileName ( string fileName )
{
Type customFormType ;
Assembly externalToolFile ;
ToolStripMenuItem item = null ;
try
{
BizHawk . Common . Win32Hacks . RemoveMOTW ( fileName ) ;
externalToolFile = Assembly . LoadFrom ( fileName ) ;
object [ ] attributes = externalToolFile . GetCustomAttributes ( typeof ( BizHawkExternalToolAttribute ) , false ) ;
if ( attributes ! = null & & attributes . Count ( ) = = 1 )
{
BizHawkExternalToolAttribute attribute = ( BizHawkExternalToolAttribute ) attributes [ 0 ] ;
item = new ToolStripMenuItem ( attribute . Name ) ;
item . ToolTipText = attribute . Description ;
2017-05-10 11:45:23 +00:00
if ( attribute . IconResourceName ! = "" )
2017-04-15 19:53:02 +00:00
{
2019-03-20 05:52:54 +00:00
Stream s = externalToolFile . GetManifestResourceStream ( $"{externalToolFile.GetName().Name}.{attribute.IconResourceName}" ) ;
2017-04-15 19:53:02 +00:00
if ( s ! = null )
{
item . Image = new Bitmap ( s ) ;
}
}
customFormType = externalToolFile . GetTypes ( ) . FirstOrDefault < Type > ( t = > t ! = null & & t . FullName = = "BizHawk.Client.EmuHawk.CustomMainForm" ) ;
if ( customFormType = = null )
{
item . ToolTipText = "Does not have a CustomMainForm" ;
item . Enabled = false ;
}
item . Tag = fileName ;
attributes = externalToolFile . GetCustomAttributes ( typeof ( BizHawkExternalToolUsageAttribute ) , false ) ;
if ( attributes ! = null & & attributes . Count ( ) = = 1 )
{
BizHawkExternalToolUsageAttribute attribute2 = ( BizHawkExternalToolUsageAttribute ) attributes [ 0 ] ;
if ( Global . Emulator . SystemId = = "NULL" & & attribute2 . ToolUsage ! = BizHawkExternalToolUsage . Global )
{
item . ToolTipText = "This tool doesn't work if nothing is loaded" ;
item . Enabled = false ;
}
else if ( attribute2 . ToolUsage = = BizHawkExternalToolUsage . EmulatorSpecific & & Global . Emulator . SystemId ! = ClientApi . SystemIdConverter . ConvertBack ( attribute2 . System ) )
{
item . ToolTipText = "This tool doesn't work for current system" ;
item . Enabled = false ;
}
else if ( attribute2 . ToolUsage = = BizHawkExternalToolUsage . GameSpecific & & Global . Game . Hash ! = attribute2 . GameHash )
{
item . ToolTipText = "This tool doesn't work for current game" ;
item . Enabled = false ;
}
}
}
else
{
item = new ToolStripMenuItem ( externalToolFile . GetName ( ) . Name ) ;
item . ToolTipText = "BizHawkExternalTool attribute hasn't been found" ;
item . Enabled = false ;
}
}
catch ( BadImageFormatException )
{
item = new ToolStripMenuItem ( fileName ) ;
item . ToolTipText = "This is not an assembly" ;
item . Enabled = false ;
}
#if DEBUG //I added special debug stuff to get additionnal informations. Don(t think it can be usefull for released versions
catch ( ReflectionTypeLoadException ex )
{
foreach ( Exception e in ex . LoaderExceptions )
{
Debug . WriteLine ( e . Message ) ;
}
item . ToolTipText = "Something goes wrong while trying to load" ;
item . Enabled = false ;
}
#else
catch ( ReflectionTypeLoadException )
{
item . ToolTipText = "Something goes wrong while trying to load" ;
item . Enabled = false ;
}
#endif
return item ;
}
/// <summary>
/// This event is raised when we add a dll file into
/// the external tools path.
/// It will automatically load the assembly and add it into the list
/// </summary>
/// <param name="sender">Object that raised the event</param>
/// <param name="e">Event arguments</param>
private static void DirectoryMonitor_Created ( object sender , FileSystemEventArgs e )
{
menuItems . Add ( GenerateToolTipFromFileName ( e . FullPath ) ) ;
}
#endregion
#region Properties
/// <summary>
/// Gets a prebuild <see cref="ToolStripMenuItem"/>
/// This list auto-updated by the <see cref="ExternalToolManager"/> itself
/// </summary>
public static IEnumerable < ToolStripMenuItem > ToolStripMenu
{
get
{
return menuItems ;
}
}
#endregion
}
}