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 { /// /// This static class handle all ExternalTools /// public static class ExternalToolManager { #region Fields private static FileSystemWatcher directoryMonitor; private static List menuItems = new List(); #endregion #region cTor(s) /// /// Initilization /// static ExternalToolManager() { if(!Directory.Exists(Global.Config.PathEntries["Global", "External Tools"].Path)) { 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 /// /// Build the toolstrip menu /// 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)); } } } /// /// Generate a from an /// external tool dll. /// The assembly must have in its /// assembly attributes /// /// File that will be reflected /// A new ; assembly path can be found in the Tag property /// For the moment, you could only load a dll that have a form (which implements ) 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; if (attribute.IconResourceName != "") { Stream s = externalToolFile.GetManifestResourceStream(string.Format("{0}.{1}", externalToolFile.GetName().Name, attribute.IconResourceName)); if (s != null) { item.Image = new Bitmap(s); } } customFormType = externalToolFile.GetTypes().FirstOrDefault(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; } /// /// 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 /// /// Object that raised the event /// Event arguments private static void DirectoryMonitor_Created(object sender, FileSystemEventArgs e) { menuItems.Add(GenerateToolTipFromFileName(e.FullPath)); } #endregion #region Properties /// /// Gets a prebuild /// This list auto-updated by the itself /// public static IEnumerable ToolStripMenu { get { return menuItems; } } #endregion } }