Move ExternalToolManager to Client.Common in preparation for testing external API.
This commit is contained in:
parent
bc7decff75
commit
30b3819cb1
|
@ -5,38 +5,59 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using System.Windows.Forms;
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public sealed class ExternalToolManager
|
||||
{
|
||||
public struct MenuItemInfo
|
||||
{
|
||||
private readonly string _asmChecksum;
|
||||
private readonly string _asmChecksum = "";
|
||||
|
||||
private readonly string _entryPointTypeName;
|
||||
private readonly string _entryPointTypeName = "";
|
||||
|
||||
private readonly ExternalToolManager _extToolMan;
|
||||
|
||||
private bool _skipExtToolWarning;
|
||||
private bool _skipExtToolWarning = false;
|
||||
|
||||
public readonly string AsmFilename;
|
||||
public readonly string AsmFilename = "";
|
||||
|
||||
public readonly string Text;
|
||||
|
||||
public readonly Bitmap Icon = null;
|
||||
|
||||
public readonly string ToolTip;
|
||||
|
||||
public readonly bool Enabled = false;
|
||||
|
||||
public MenuItemInfo(
|
||||
ExternalToolManager extToolMan,
|
||||
string asmChecksum,
|
||||
string asmFilename,
|
||||
string entryPointTypeName)
|
||||
string entryPointTypeName,
|
||||
string text,
|
||||
Bitmap icon,
|
||||
string toolTip,
|
||||
bool enabled)
|
||||
{
|
||||
_asmChecksum = asmChecksum;
|
||||
_entryPointTypeName = entryPointTypeName;
|
||||
_extToolMan = extToolMan;
|
||||
_skipExtToolWarning = _extToolMan._config.TrustedExtTools.TryGetValue(asmFilename, out var s) && s == _asmChecksum;
|
||||
AsmFilename = asmFilename;
|
||||
Text = text;
|
||||
Icon = icon;
|
||||
ToolTip = toolTip;
|
||||
Enabled = enabled;
|
||||
}
|
||||
|
||||
public MenuItemInfo(ExternalToolManager extToolMan, string text, string toolTip)
|
||||
{
|
||||
_extToolMan = extToolMan;
|
||||
Text = text;
|
||||
ToolTip = toolTip;
|
||||
}
|
||||
|
||||
public void TryLoad()
|
||||
|
@ -59,9 +80,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
private FileSystemWatcher DirectoryMonitor;
|
||||
|
||||
private readonly List<ToolStripMenuItem> MenuItems = new List<ToolStripMenuItem>();
|
||||
private readonly List<MenuItemInfo> MenuItems = new List<MenuItemInfo>();
|
||||
|
||||
internal readonly IList<string> PossibleExtToolTypeNames = new List<string>();
|
||||
public readonly IList<string> PossibleExtToolTypeNames = new List<string>();
|
||||
|
||||
public ExternalToolManager(
|
||||
Config config,
|
||||
|
@ -93,29 +114,22 @@ namespace BizHawk.Client.EmuHawk
|
|||
DirectoryMonitor.Created += DirectoryMonitor_Created;
|
||||
DirectoryMonitor.EnableRaisingEvents = true;
|
||||
}
|
||||
BuildToolStrip();
|
||||
BuildToolStripInfo();
|
||||
}
|
||||
|
||||
internal void BuildToolStrip()
|
||||
public void BuildToolStripInfo()
|
||||
{
|
||||
MenuItems.Clear();
|
||||
PossibleExtToolTypeNames.Clear();
|
||||
if (DirectoryMonitor == null) return;
|
||||
DirectoryInfo di = new(DirectoryMonitor.Path);
|
||||
if (!di.Exists) return;
|
||||
foreach (var fi in di.GetFiles("*.dll")) MenuItems.Add(GenerateToolTipFromFileName(fi.FullName));
|
||||
foreach (var fi in di.GetFiles("*.dll")) MenuItems.Add(GenerateMenuItemTextFromFileName(fi.FullName));
|
||||
}
|
||||
|
||||
/// <summary>Generates a <see cref="ToolStripMenuItem"/> from an assembly at <paramref name="fileName"/> containing an external tool.</summary>
|
||||
/// <returns>a <see cref="ToolStripMenuItem"/> with its <see cref="ToolStripItem.Tag"/> containing a <see cref="MenuItemInfo"/></returns>
|
||||
private ToolStripMenuItem GenerateToolTipFromFileName(string fileName)
|
||||
private MenuItemInfo GenerateMenuItemTextFromFileName(string fileName)
|
||||
{
|
||||
if (fileName == null) throw new Exception();
|
||||
var item = new ToolStripMenuItem(Path.GetFileName(fileName))
|
||||
{
|
||||
Enabled = false,
|
||||
Image = Properties.Resources.ExclamationRed,
|
||||
};
|
||||
try
|
||||
{
|
||||
if (!OSTailoredCode.IsUnixHost) MotWHack.RemoveMOTW(fileName);
|
||||
|
@ -135,42 +149,44 @@ namespace BizHawk.Client.EmuHawk
|
|||
foreach (var depFilename in toolAttribute.LoadAssemblyFiles) Assembly.LoadFrom($"{_config.PathEntries[PathEntryCollection.GLOBAL, "External Tools"].Path}/{depFilename}");
|
||||
}
|
||||
|
||||
item.Image = null; // no errors, remove error icon
|
||||
Bitmap image = null; // no errors, remove error icon
|
||||
var embeddedIconAttr = allAttrs.OfType<ExternalToolEmbeddedIconAttribute>().FirstOrDefault();
|
||||
if (embeddedIconAttr != null)
|
||||
{
|
||||
var rawIcon = externalToolFile.GetManifestResourceStream(embeddedIconAttr.ResourcePath);
|
||||
if (rawIcon != null) item.Image = new Bitmap(rawIcon);
|
||||
if (rawIcon != null) image = new Bitmap(rawIcon);
|
||||
}
|
||||
item.Text = toolAttribute.Name;
|
||||
MenuItemInfo menuItemInfo = new(
|
||||
this,
|
||||
asmChecksum: SHA1Checksum.ComputePrefixedHex(asmBytes),
|
||||
asmFilename: fileName,
|
||||
entryPointTypeName: entryPoint.FullName);
|
||||
item.Tag = menuItemInfo;
|
||||
item.Click += (_, _) => menuItemInfo.TryLoad();
|
||||
PossibleExtToolTypeNames.Add(entryPoint.AssemblyQualifiedName);
|
||||
bool enabled = true;
|
||||
string toolTip = "";
|
||||
if (applicabilityAttrs.Count is 1)
|
||||
{
|
||||
var (system, loadedRomHash) = _getLoadedRomInfoCallback();
|
||||
if (applicabilityAttrs[0].NotApplicableTo(system))
|
||||
{
|
||||
item.ToolTipText = system is VSystemID.Raw.NULL
|
||||
toolTip = system is VSystemID.Raw.NULL
|
||||
? "This tool doesn't work when no rom is loaded"
|
||||
: "This tool doesn't work with this system";
|
||||
return item;
|
||||
enabled = false;
|
||||
}
|
||||
if (applicabilityAttrs[0].NotApplicableTo(loadedRomHash, system))
|
||||
else if (applicabilityAttrs[0].NotApplicableTo(loadedRomHash, system))
|
||||
{
|
||||
item.ToolTipText = "This tool doesn't work with this game";
|
||||
return item;
|
||||
toolTip = "This tool doesn't work with this game";
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(toolAttribute.Description))
|
||||
toolTip = toolAttribute.Description;
|
||||
|
||||
item.Enabled = true;
|
||||
if (!string.IsNullOrWhiteSpace(toolAttribute.Description)) item.ToolTipText = toolAttribute.Description;
|
||||
return item;
|
||||
return new MenuItemInfo(
|
||||
this,
|
||||
asmChecksum: SHA1Checksum.ComputePrefixedHex(asmBytes),
|
||||
asmFilename: fileName,
|
||||
entryPointTypeName: entryPoint.FullName,
|
||||
text: toolAttribute.Name,
|
||||
icon: image,
|
||||
toolTip: toolTip,
|
||||
enabled: enabled);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -180,7 +196,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
foreach (var e1 in rtle.LoaderExceptions) Console.WriteLine(e1.Message);
|
||||
}
|
||||
#endif
|
||||
item.ToolTipText = e switch
|
||||
string text = Path.GetFileName(fileName);
|
||||
string toolTip = e switch
|
||||
{
|
||||
BadImageFormatException => "This assembly can't be loaded, probably because it's corrupt or targets an incompatible .NET runtime.",
|
||||
ExternalToolApplicabilityAttributeBase.DuplicateException => "The IExternalToolForm has conflicting applicability attributes.",
|
||||
|
@ -188,8 +205,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
ReflectionTypeLoadException => "Something went wrong while trying to load the assembly.",
|
||||
_ => $"An exception of type {e.GetType().FullName} was thrown while trying to load the assembly and look for an IExternalToolForm:\n{e.Message}"
|
||||
};
|
||||
return new MenuItemInfo(this, text, toolTip);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -201,10 +218,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
/// <param name="e">Event arguments</param>
|
||||
private void DirectoryMonitor_Created(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
MenuItems.Add(GenerateToolTipFromFileName(e.FullPath));
|
||||
MenuItems.Add(GenerateMenuItemTextFromFileName(e.FullPath));
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<ToolStripItem> ToolStripItems
|
||||
public IReadOnlyCollection<MenuItemInfo> ToolStripItems
|
||||
=> MenuItems;
|
||||
}
|
||||
}
|
|
@ -1182,7 +1182,14 @@ namespace BizHawk.Client.EmuHawk
|
|||
private void ExternalToolMenuItem_DropDownOpening(object sender, EventArgs e)
|
||||
{
|
||||
ExternalToolMenuItem.DropDownItems.Clear();
|
||||
ExternalToolMenuItem.DropDownItems.AddRange(ExtToolManager.ToolStripItems.ToArray());
|
||||
ExternalToolMenuItem.DropDownItems.AddRange(ExtToolManager.ToolStripItems.Select(static (info) =>
|
||||
{
|
||||
return new ToolStripMenuItem(info.Text, info.Icon, (_, _) => info.TryLoad())
|
||||
{
|
||||
ToolTipText = info.ToolTip,
|
||||
Enabled = info.Enabled,
|
||||
};
|
||||
}).ToArray());
|
||||
if (ExternalToolMenuItem.DropDownItems.Count == 0)
|
||||
{
|
||||
ExternalToolMenuItem.DropDownItems.Add("None");
|
||||
|
|
|
@ -193,7 +193,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
if (requestedExtToolDll != null)
|
||||
{
|
||||
var found = ExtToolManager.ToolStripItems.Where(static item => item.Enabled)
|
||||
.Select(static item => (ExternalToolManager.MenuItemInfo) item.Tag)
|
||||
.FirstOrNull(info => info.AsmFilename == requestedExtToolDll
|
||||
|| Path.GetFileName(info.AsmFilename) == requestedExtToolDll
|
||||
|| Path.GetFileNameWithoutExtension(info.AsmFilename) == requestedExtToolDll);
|
||||
|
@ -4000,7 +3999,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
|
||||
ExtToolManager.BuildToolStrip();
|
||||
ExtToolManager.BuildToolStripInfo();
|
||||
|
||||
EmuClient.OnRomLoaded();
|
||||
return true;
|
||||
|
@ -4009,7 +4008,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
// This shows up if there's a problem
|
||||
Tools.Restart(Config, Emulator, Game);
|
||||
ExtToolManager.BuildToolStrip();
|
||||
ExtToolManager.BuildToolStripInfo();
|
||||
OnRomChanged();
|
||||
return false;
|
||||
}
|
||||
|
@ -4142,7 +4141,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
DisplayManager.UpdateGlobals(Config, Emulator);
|
||||
InputManager.SyncControls(Emulator, MovieSession, Config);
|
||||
Tools.UpdateCheatRelatedTools(null, null);
|
||||
ExtToolManager.BuildToolStrip();
|
||||
ExtToolManager.BuildToolStripInfo();
|
||||
PauseOnFrame = null;
|
||||
CurrentlyOpenRom = null;
|
||||
CurrentlyOpenRomArgs = null;
|
||||
|
|
|
@ -74,11 +74,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
if (!tsi.Enabled) continue;
|
||||
ToolStripButton tsb = new() {
|
||||
DisplayStyle = ToolStripItemDisplayStyle.Image,
|
||||
Image = tsi.Image ?? IconMissingIcon.Value,
|
||||
Image = tsi.Icon ?? IconMissingIcon.Value,
|
||||
Text = tsi.Text,
|
||||
};
|
||||
var info = (ExternalToolManager.MenuItemInfo) tsi.Tag;
|
||||
tsb.Click += (_, _) => info.TryLoad();
|
||||
tsb.Click += (_, _) => tsi.TryLoad();
|
||||
ToolBoxStrip.Items.Add(tsb);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
IMovieSession movieSession,
|
||||
IGameInfo game)
|
||||
{
|
||||
|
||||
|
||||
_owner = owner;
|
||||
_config = config;
|
||||
_displayManager = displayManager;
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using BizHawk.Client.Common;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace BizHawk.Tests.Client.Common.Api
|
||||
{
|
||||
[TestClass]
|
||||
public class ExternalToolTests
|
||||
{
|
||||
[ClassInitialize]
|
||||
public static void TestInitialize(TestContext context)
|
||||
{
|
||||
// Move our .dll to a directory by itself, so that the ExternalToolManager will only find us.
|
||||
string asmName = Assembly.GetExecutingAssembly().Location;
|
||||
Directory.CreateDirectory("extTools");
|
||||
File.Copy(asmName, "extTools/ExternalToolTests.dll");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestExternalToolIsFound()
|
||||
{
|
||||
Config config = new Config();
|
||||
config.PathEntries.Paths.Find((e) => string.Equals(e.Type, "External Tools", System.StringComparison.Ordinal))!.Path = "./extTools";
|
||||
string t = config.PathEntries[PathEntryCollection.GLOBAL, "External Tools"].Path;
|
||||
ExternalToolManager manager = new ExternalToolManager(config, () => ("", ""), (p1, p2, p3) => true);
|
||||
|
||||
Assert.IsTrue(manager.ToolStripItems.Count != 0);
|
||||
var item = manager.ToolStripItems.First(static (info) => info.Text == "TEST");
|
||||
Assert.AreEqual("TEST", item.Text);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using BizHawk.Client.Common;
|
||||
|
||||
namespace BizHawk.Tests.Implementations
|
||||
{
|
||||
[ExternalTool("TEST")]
|
||||
public class TestExternalAPI : IExternalToolForm
|
||||
{
|
||||
public ApiContainer? _maybeAPIContainer { get; set; }
|
||||
private ApiContainer APIs
|
||||
=> _maybeAPIContainer!;
|
||||
|
||||
private int frameCount = 0;
|
||||
|
||||
public bool IsActive => true;
|
||||
|
||||
public bool IsLoaded => true;
|
||||
|
||||
public bool ContainsFocus => false;
|
||||
|
||||
|
||||
public bool AskSaveChanges() => true;
|
||||
public void Close() {}
|
||||
public bool Focus() => false;
|
||||
public void Restart() { }
|
||||
public void Show() { }
|
||||
public void UpdateValues(ToolFormUpdateType type)
|
||||
{
|
||||
if (type == ToolFormUpdateType.PostFrame)
|
||||
{
|
||||
frameCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue