Skip to content

Commit

Permalink
Refactor external tool attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
YoshiRulz committed Jan 31, 2020
1 parent 3143c38 commit 593e1ef
Show file tree
Hide file tree
Showing 16 changed files with 242 additions and 316 deletions.
73 changes: 16 additions & 57 deletions BizHawk.Client.ApiHawk/Attributes/BizHawkExternalToolAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,67 +1,26 @@
using System;

using BizHawk.Client.Common;

namespace BizHawk.Client.ApiHawk
{
/// <summary>
/// This class holds logic interaction for the ExternalToolAttribute
/// This attribute helps BizHawk to handle ExternalTools
/// </summary>
/// <remarks>This class needs to be in the assembly or old tools will throw on load instead of being recognised as old.</remarks>
[AttributeUsage(AttributeTargets.Assembly)]
[Obsolete("last used in 2.4, use [ExternalTool] instead")]
public sealed class BizHawkExternalToolAttribute : Attribute
{
#region cTor(s)

/// <summary>
/// Initialize a new instance of <see cref="BizHawkExternalToolAttribute"/>
/// </summary>
/// <param name="name">Tool's name</param>
/// <param name="description">Small description about the tool itself</param>
/// <param name="iconResourceName">Icon embedded resource name</param>
public BizHawkExternalToolAttribute(string name, string description, string iconResourceName)
{
Name = name;
Description = description;
IconResourceName = iconResourceName;
}

/// <summary>
/// Initialize a new instance of <see cref="BizHawkExternalToolAttribute"/>
/// </summary>
/// <param name="name">Tool's name</param>
/// <param name="description">Small description about the tool itself</param>
public BizHawkExternalToolAttribute(string name, string description)
: this(name, description, "")
{ }

/// <summary>
/// Initialize a new instance of <see cref="BizHawkExternalToolAttribute"/>
/// </summary>
/// <param name="name">Tool's name</param>
public BizHawkExternalToolAttribute(string name)
:this(name, "", "")
{}

#endregion

#region Properties

/// <summary>
/// Gets tool's friendly name
/// </summary>
public string Name { get; }

/// <summary>
/// Gets tool's description
/// </summary>
public string Description { get; }


/// <summary>
/// Get the name of the embedded resource icon
/// </summary>
/// <remarks>Don't forget to set compile => Embedded resource to the icon file in your project</remarks>
public string IconResourceName { get; }
public BizHawkExternalToolAttribute(string name, string description, string iconResourceName) {}
public BizHawkExternalToolAttribute(string name, string description) {}
public BizHawkExternalToolAttribute(string name) {}
}

#endregion
/// <inheritdoc cref="BizHawkExternalToolAttribute"/>
[AttributeUsage(AttributeTargets.Assembly)]
[Obsolete("last used in 2.4, use [ExternalToolApplicability.*] instead")]
public sealed class BizHawkExternalToolUsageAttribute : Attribute
{
public BizHawkExternalToolUsageAttribute(BizHawkExternalToolUsage usage, CoreSystem system, string gameHash) {}
public BizHawkExternalToolUsageAttribute(BizHawkExternalToolUsage usage, CoreSystem system) {}
public BizHawkExternalToolUsageAttribute() {}
}
}

This file was deleted.

133 changes: 55 additions & 78 deletions BizHawk.Client.ApiHawk/Classes/ExternalToolManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,101 +71,78 @@ private static void BuildToolStrip()
}
}

/// <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="IExternalToolForm"/>)</remarks>
/// <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 <c>(string, string)</c>;
/// the first is the assembly path (<paramref name="fileName"/>) and the second is the <see cref="Type.FullName"/> of the entry point form's type
/// </returns>
private static ToolStripMenuItem GenerateToolTipFromFileName(string fileName)
{
ToolStripMenuItem item = null;

if (fileName == null) throw new Exception();
var item = new ToolStripMenuItem(Path.GetFileName(fileName)) { Enabled = false };
try
{
if (!OSTailoredCode.IsUnixHost) MotWHack.RemoveMOTW(fileName);
var externalToolFile = Assembly.LoadFrom(fileName);
object[] attributes = externalToolFile.GetCustomAttributes(typeof(BizHawkExternalToolAttribute), false);
if (attributes != null && attributes.Count() == 1)
var entryPoint = externalToolFile.GetTypes()
.SingleOrDefault(t => typeof(IExternalToolForm).IsAssignableFrom(t) && t.GetCustomAttributes().OfType<ExternalToolAttribute>().Any());
#pragma warning disable CS0618
if (entryPoint == null) throw new ExternalToolAttribute.MissingException(externalToolFile.GetCustomAttributes().OfType<BizHawkExternalToolAttribute>().Any());
#pragma warning restore CS0618

var allAttrs = entryPoint.GetCustomAttributes().ToList();
var applicabilityAttrs = allAttrs.OfType<ExternalToolApplicabilityAttributeBase>().ToList();
if (applicabilityAttrs.Count > 1) throw new ExternalToolApplicabilityAttributeBase.DuplicateException();

var toolAttribute = allAttrs.OfType<ExternalToolAttribute>().First();
var embeddedIconAttr = allAttrs.OfType<ExternalToolEmbeddedIconAttribute>().FirstOrDefault();
if (embeddedIconAttr != null)
{
BizHawkExternalToolAttribute attribute = (BizHawkExternalToolAttribute)attributes[0];
item = new ToolStripMenuItem(attribute.Name) { ToolTipText = attribute.Description };
if (attribute.IconResourceName != "")
{
Stream s = externalToolFile.GetManifestResourceStream($"{externalToolFile.GetName().Name}.{attribute.IconResourceName}");
if (s != null)
{
item.Image = new Bitmap(s);
}
}

var availTypes = externalToolFile.GetTypes();
var customFormType = availTypes.FirstOrDefault(t => t.FullName == "BizHawk.Client.EmuHawk.CustomMainForm")
?? availTypes.SingleOrDefault(t => typeof(IExternalToolForm).IsAssignableFrom(t) && t.CustomAttributes.Any(cad => cad.AttributeType == typeof(ExternalToolEntryPointFormAttribute)));
if (customFormType == null)
var rawIcon = externalToolFile.GetManifestResourceStream(embeddedIconAttr.ResourcePath);
if (rawIcon != null) item.Image = new Bitmap(rawIcon);
}
item.Text = toolAttribute.Name;
item.Tag = (externalToolFile.Location, entryPoint.FullName); // Tag set => no errors (show custom icon even when disabled)
if (applicabilityAttrs.Count == 1)
{
var system = ClientApi.SystemIdConverter.Convert(Global.Emulator.SystemId);
if (applicabilityAttrs[0].NotApplicableTo(system))
{
item.ToolTipText = "Does not have a correctly-formatted CustomMainForm or a form decorated with [ExternalToolEntryPointForm]";
item.Enabled = false;
item.ToolTipText = system == CoreSystem.Null
? "This tool doesn't work when no rom is loaded"
: "This tool doesn't work with this system";
return item;
}
item.Tag = (fileName, customFormType?.FullName);

attributes = externalToolFile.GetCustomAttributes(typeof(BizHawkExternalToolUsageAttribute), false);
if (attributes != null && attributes.Length == 1)
if (applicabilityAttrs[0].NotApplicableTo(Global.Game.Hash, system))
{
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;
}
item.ToolTipText = "This tool doesn't work with this game";
return item;
}
}
else
{
item = new ToolStripMenuItem(externalToolFile.GetName().Name)
{
ToolTipText = "BizHawkExternalTool attribute hasn't been found", 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 additional information. Don't think it can be useful for released versions
catch (ReflectionTypeLoadException ex)
item.Enabled = true;
if (!string.IsNullOrWhiteSpace(toolAttribute.Description)) item.ToolTipText = toolAttribute.Description;
return item;
}
catch (Exception e)
{
foreach (Exception e in ex.LoaderExceptions)
#if DEBUG
if (e is ReflectionTypeLoadException rtle)
{
Debug.WriteLine(e.Message);
foreach (var e1 in rtle.LoaderExceptions) Debug.WriteLine(e1.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

item.ToolTipText = 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.",
ExternalToolAttribute.MissingException e1 => e1.OldAttributeFound
? "The assembly doesn't contain a class implementing IExternalToolForm and annotated with [ExternalTool].\nHowever, the assembly itself is annotated with [BizHawkExternalTool], which is now deprecated. Has the tool been updated since BizHawk 2.4?"
: "The assembly doesn't contain a class implementing IExternalToolForm and annotated with [ExternalTool].",
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 item;
}

Expand Down
Loading

0 comments on commit 593e1ef

Please sign in to comment.