BizHawk/BizHawk.Client.EmuHawk/tools/ToolManager.cs

1030 lines
27 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
2014-12-15 03:19:23 +00:00
using System.Reflection;
2014-12-21 05:56:51 +00:00
using System.ComponentModel;
2017-05-22 17:51:34 +00:00
using System.Windows.Forms;
using BizHawk.Client.ApiHawk;
using BizHawk.Client.Common;
Add preliminary Unix compatibility (using Mono; resolves #1384) (#1380) * Move PlatformSpecificLinkedLibs and implementations to common and rename * Specify file ext. at LoadPlatformSpecific call site * Move Client.Common.Global.RunningOnUnix to PlatformLinkedLibSingleton * Inline var Resolver * Use PlatformLinkedLibManager internally * Move plugin load check to LinkedLibManager, use LinkedLibManager * Interpolate * Return exit code from dlclose/FreeLibrary * Skip all calls to externs in BlipBufDll when using mono * Use PlatformLinkedLibManager in SevenZipLibraryManager * Add expected return value to workaround (from testing on Win32) * Remove ".dll" from DllImport attr, remove temporary workaround, see desc. The library can be built by changing the output file name in `.../blip_buf/Makefile` to `libblip_buf.so`, and running `make`. It will be loaded if placed in the `.../output` folder. * Remove unused code, add TODO (this class is req. for Waterbox.PeWrapper) The TODO is to [rewrite with C#](https://docs.microsoft.com/en-us/dotnet/standard/io/memory-mapped-files) instead of importing from `kernel32.dll`. * Update OpenTK again but better (for #1384) * Add Mono run script * Add libblip_buf.so (temporary) Temporary because it should be a separate package which BizHawk depends on. * Add distro detection, add "already running" and "unknown distro" messages * Gray-out Lua Console on Unix * Extract superclass from EmuLuaLibrary, add shell implementation for Unix * Specify libdl version, Fedora doesn't have the versionless symlink * Remove empty `ToolStripMenuItem`, null `Text` caused crash on Unix * Transform OpenTK keyboard input into a `List<KeyEvent>` and read that Also fixes crash on rebind * Remove debug `using ...;`
2019-01-03 22:50:55 +00:00
using BizHawk.Client.EmuHawk;
using BizHawk.Client.EmuHawk.CoreExtensions;
Add preliminary Unix compatibility (using Mono; resolves #1384) (#1380) * Move PlatformSpecificLinkedLibs and implementations to common and rename * Specify file ext. at LoadPlatformSpecific call site * Move Client.Common.Global.RunningOnUnix to PlatformLinkedLibSingleton * Inline var Resolver * Use PlatformLinkedLibManager internally * Move plugin load check to LinkedLibManager, use LinkedLibManager * Interpolate * Return exit code from dlclose/FreeLibrary * Skip all calls to externs in BlipBufDll when using mono * Use PlatformLinkedLibManager in SevenZipLibraryManager * Add expected return value to workaround (from testing on Win32) * Remove ".dll" from DllImport attr, remove temporary workaround, see desc. The library can be built by changing the output file name in `.../blip_buf/Makefile` to `libblip_buf.so`, and running `make`. It will be loaded if placed in the `.../output` folder. * Remove unused code, add TODO (this class is req. for Waterbox.PeWrapper) The TODO is to [rewrite with C#](https://docs.microsoft.com/en-us/dotnet/standard/io/memory-mapped-files) instead of importing from `kernel32.dll`. * Update OpenTK again but better (for #1384) * Add Mono run script * Add libblip_buf.so (temporary) Temporary because it should be a separate package which BizHawk depends on. * Add distro detection, add "already running" and "unknown distro" messages * Gray-out Lua Console on Unix * Extract superclass from EmuLuaLibrary, add shell implementation for Unix * Specify libdl version, Fedora doesn't have the versionless symlink * Remove empty `ToolStripMenuItem`, null `Text` caused crash on Unix * Transform OpenTK keyboard input into a `List<KeyEvent>` and read that Also fixes crash on rebind * Remove debug `using ...;`
2019-01-03 22:50:55 +00:00
using BizHawk.Common;
using BizHawk.Common.ReflectionExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{
public class ToolManager
{
private readonly Form _owner;
// TODO: merge ToolHelper code where logical
// For instance, add an IToolForm property called UsesCheats, so that a UpdateCheatRelatedTools() method can update all tools of this type
// Also a UsesRam, and similar method
private readonly List<IToolForm> _tools = new List<IToolForm>();
/// <summary>
2017-05-22 17:51:34 +00:00
/// Initializes a new instance of the <see cref="ToolManager"/> class.
/// </summary>
/// <param name="owner">Form that handle the ToolManager</param>
public ToolManager(Form owner)
{
_owner = owner;
}
/// <summary>
2017-05-22 17:51:34 +00:00
/// Loads the tool dialog T (T must implements <see cref="IToolForm"/>) , if it does not exist it will be created, if it is already open, it will be focused
/// This method should be used only if you can't use the generic one
/// </summary>
/// <param name="toolType">Type of tool you want to load</param>
/// <param name="focus">Define if the tool form has to get the focus or not (Default is true)</param>
2017-05-22 17:51:34 +00:00
/// <returns>An instantiated <see cref="IToolForm"/></returns>
/// <exception cref="ArgumentException">Raised if <paramref name="toolType"/> can't be casted into IToolForm </exception>
internal IToolForm Load(Type toolType, bool focus = true)
{
if (!typeof(IToolForm).IsAssignableFrom(toolType))
{
2017-05-22 17:51:34 +00:00
throw new ArgumentException($"Type {toolType.Name} does not implement IToolForm.");
}
2017-05-22 17:51:34 +00:00
// The type[] in parameter is used to avoid an ambigous name exception
MethodInfo method = GetType().GetMethod("Load", new Type[] { typeof(bool) }).MakeGenericMethod(toolType);
return (IToolForm)method.Invoke(this, new object[] { focus });
}
/// <summary>
/// Loads the tool dialog T (T must implement <see cref="IToolForm"/>) , if it does not exist it will be created, if it is already open, it will be focused
/// </summary>
/// <typeparam name="T">Type of tool you want to load</typeparam>
/// <param name="focus">Define if the tool form has to get the focus or not (Default is true)</param>
2017-05-22 17:51:34 +00:00
/// <returns>An instantiated <see cref="IToolForm"/></returns>
public T Load<T>(bool focus = true)
where T : class, IToolForm
{
2017-05-10 11:45:23 +00:00
return Load<T>("", focus);
}
/// <summary>
/// Loads the tool dialog T (T must implement <see cref="IToolForm"/>) , if it does not exist it will be created, if it is already open, it will be focused
/// </summary>
/// <typeparam name="T">Type of tool you want to load</typeparam>
2017-05-22 17:51:34 +00:00
/// <param name="toolPath">Path to the .dll of the external tool</param>
/// <param name="focus">Define if the tool form has to get the focus or not (Default is true)</param>
2017-05-22 17:51:34 +00:00
/// <returns>An instantiated <see cref="IToolForm"/></returns>
public T Load<T>(string toolPath, bool focus = true)
where T : class, IToolForm
{
bool isExternal = typeof(T) == typeof(IExternalToolForm);
if (!IsAvailable<T>() && !isExternal)
{
return null;
}
T existingTool;
if (isExternal)
{
2017-05-22 17:51:34 +00:00
existingTool = (T)_tools.FirstOrDefault(t => t is T && t.GetType().Assembly.Location == toolPath);
}
else
{
2017-05-22 17:51:34 +00:00
existingTool = (T)_tools.FirstOrDefault(t => t is T);
}
if (existingTool != null)
{
if (existingTool.IsDisposed)
2013-11-02 20:13:53 +00:00
{
_tools.Remove(existingTool);
2013-11-02 20:13:53 +00:00
}
else
{
2014-12-19 22:21:25 +00:00
if (focus)
{
existingTool.Show();
existingTool.Focus();
}
return existingTool;
}
}
2013-11-02 20:13:53 +00:00
IToolForm newTool = CreateInstance<T>(toolPath);
if (newTool == null)
{
return null;
}
if (newTool is Form)
{
(newTool as Form).Owner = GlobalWin.MainForm;
}
if (isExternal)
{
ApiInjector.UpdateApis(GlobalWin.ApiProvider, newTool);
}
ServiceInjector.UpdateServices(Global.Emulator.ServiceProvider, newTool);
string toolType = typeof(T).ToString();
// auto settings
if (newTool is IToolFormAutoConfig)
2014-12-19 23:33:05 +00:00
{
ToolDialogSettings settings;
if (!Global.Config.CommonToolSettings.TryGetValue(toolType, out settings))
{
settings = new ToolDialogSettings();
Global.Config.CommonToolSettings[toolType] = settings;
}
2017-05-22 17:51:34 +00:00
AttachSettingHooks(newTool as IToolFormAutoConfig, settings);
2014-12-19 23:33:05 +00:00
}
// custom settings
if (HasCustomConfig(newTool))
{
Dictionary<string, object> settings;
if (!Global.Config.CustomToolSettings.TryGetValue(toolType, out settings))
{
settings = new Dictionary<string, object>();
Global.Config.CustomToolSettings[toolType] = settings;
}
2017-05-22 17:51:34 +00:00
InstallCustomConfig(newTool, settings);
}
2014-12-19 23:33:05 +00:00
newTool.Restart();
newTool.Show();
return (T)newTool;
}
2014-12-19 23:33:05 +00:00
public void AutoLoad()
{
var genericSettings = Global.Config.CommonToolSettings
.Where(kvp => kvp.Value.AutoLoad)
.Select(kvp => kvp.Key);
var customSettings = Global.Config.CustomToolSettings
2017-05-22 17:51:34 +00:00
.Where(list => list.Value.Any(kvp => kvp.Value is ToolDialogSettings && ((ToolDialogSettings)kvp.Value).AutoLoad))
.Select(kvp => kvp.Key);
var typeNames = genericSettings.Concat(customSettings);
foreach (var typename in typeNames)
2014-12-19 23:33:05 +00:00
{
// this type resolution might not be sufficient. more investigation is needed
Type t = Type.GetType(typename);
if (t == null)
{
Console.WriteLine("BENIGN: Couldn't find type {0}", typename);
}
else
{
Load(t, false);
}
}
}
private static void RefreshSettings(Form form, ToolStripItemCollection menu, ToolDialogSettings settings, int idx)
{
2017-05-22 17:51:34 +00:00
((ToolStripMenuItem)menu[idx + 0]).Checked = settings.SaveWindowPosition;
((ToolStripMenuItem)menu[idx + 1]).Checked = settings.TopMost;
((ToolStripMenuItem)menu[idx + 2]).Checked = settings.FloatingWindow;
((ToolStripMenuItem)menu[idx + 3]).Checked = settings.AutoLoad;
form.TopMost = settings.TopMost;
// do we need to do this OnShown() as well?
form.Owner = settings.FloatingWindow ? null : GlobalWin.MainForm;
}
private void AttachSettingHooks(IToolFormAutoConfig tool, ToolDialogSettings settings)
2014-12-19 23:33:05 +00:00
{
var form = (Form)tool;
2014-12-20 17:05:13 +00:00
ToolStripItemCollection dest = null;
var oldsize = form.Size; // this should be the right time to grab this size
2014-12-20 17:05:13 +00:00
foreach (Control c in form.Controls)
{
if (c is MenuStrip)
{
var ms = c as MenuStrip;
foreach (ToolStripMenuItem submenu in ms.Items)
{
if (submenu.Text.Contains("Settings"))
{
dest = submenu.DropDownItems;
2014-12-22 19:01:21 +00:00
dest.Add(new ToolStripSeparator());
2014-12-20 17:05:13 +00:00
break;
}
}
2017-05-22 17:51:34 +00:00
2014-12-20 17:05:13 +00:00
if (dest == null)
{
var submenu = new ToolStripMenuItem("&Settings");
ms.Items.Add(submenu);
dest = submenu.DropDownItems;
}
2017-05-22 17:51:34 +00:00
2014-12-20 17:05:13 +00:00
break;
}
}
2017-05-22 17:51:34 +00:00
2014-12-20 17:05:13 +00:00
if (dest == null)
2017-05-22 17:51:34 +00:00
{
2014-12-20 17:05:13 +00:00
throw new InvalidOperationException("IToolFormAutoConfig must have menu to bind to!");
2017-05-22 17:51:34 +00:00
}
2014-12-19 23:33:05 +00:00
2014-12-22 19:01:21 +00:00
int idx = dest.Count;
dest.Add("Save Window &Position");
dest.Add("Stay on &Top");
dest.Add("&Float from Parent");
dest.Add("&Autoload");
dest.Add("Restore &Defaults");
2014-12-19 23:33:05 +00:00
RefreshSettings(form, dest, settings, idx);
2014-12-19 23:33:05 +00:00
if (settings.UseWindowPosition && IsOnScreen(settings.TopLeft))
2014-12-19 23:33:05 +00:00
{
2015-01-01 21:01:42 +00:00
form.StartPosition = FormStartPosition.Manual;
2014-12-19 23:33:05 +00:00
form.Location = settings.WindowPosition;
}
2017-05-22 17:51:34 +00:00
2014-12-19 23:33:05 +00:00
if (settings.UseWindowSize)
{
if (form.FormBorderStyle == FormBorderStyle.Sizable || form.FormBorderStyle == FormBorderStyle.SizableToolWindow)
2017-05-22 17:51:34 +00:00
{
form.Size = settings.WindowSize;
2017-05-22 17:51:34 +00:00
}
2014-12-19 23:33:05 +00:00
}
form.FormClosing += (o, e) =>
{
if (form.WindowState == FormWindowState.Normal)
{
settings.Wndx = form.Location.X;
settings.Wndy = form.Location.Y;
2017-05-22 17:51:34 +00:00
if (settings.Wndx < 0)
{
settings.Wndx = 0;
}
if (settings.Wndy < 0)
{
settings.Wndy = 0;
}
settings.Width = form.Right - form.Left; // why not form.Size.Width?
settings.Height = form.Bottom - form.Top;
}
2014-12-19 23:33:05 +00:00
};
2014-12-22 19:01:21 +00:00
dest[idx + 0].Click += (o, e) =>
2014-12-19 23:33:05 +00:00
{
2017-05-22 17:51:34 +00:00
bool val = !((ToolStripMenuItem)o).Checked;
2014-12-19 23:33:05 +00:00
settings.SaveWindowPosition = val;
2017-05-22 17:51:34 +00:00
((ToolStripMenuItem)o).Checked = val;
2014-12-19 23:33:05 +00:00
};
2014-12-22 19:01:21 +00:00
dest[idx + 1].Click += (o, e) =>
2014-12-19 23:33:05 +00:00
{
2017-05-22 17:51:34 +00:00
bool val = !((ToolStripMenuItem)o).Checked;
2014-12-19 23:33:05 +00:00
settings.TopMost = val;
2017-05-22 17:51:34 +00:00
((ToolStripMenuItem)o).Checked = val;
2014-12-19 23:33:05 +00:00
form.TopMost = val;
};
2014-12-22 19:01:21 +00:00
dest[idx + 2].Click += (o, e) =>
2014-12-19 23:33:05 +00:00
{
2017-05-22 17:51:34 +00:00
bool val = !((ToolStripMenuItem)o).Checked;
2014-12-19 23:33:05 +00:00
settings.FloatingWindow = val;
2017-05-22 17:51:34 +00:00
((ToolStripMenuItem)o).Checked = val;
form.Owner = val ? null : _owner;
2014-12-19 23:33:05 +00:00
};
2014-12-22 19:01:21 +00:00
dest[idx + 3].Click += (o, e) =>
2014-12-19 23:33:05 +00:00
{
2017-05-22 17:51:34 +00:00
bool val = !((ToolStripMenuItem)o).Checked;
2014-12-19 23:33:05 +00:00
settings.AutoLoad = val;
2017-05-22 17:51:34 +00:00
((ToolStripMenuItem)o).Checked = val;
2014-12-19 23:33:05 +00:00
};
dest[idx + 4].Click += (o, e) =>
{
settings.RestoreDefaults();
RefreshSettings(form, dest, settings, idx);
form.Size = oldsize;
};
}
2014-12-19 23:33:05 +00:00
private static bool HasCustomConfig(IToolForm tool)
{
return tool.GetType().GetPropertiesWithAttrib(typeof(ConfigPersistAttribute)).Any();
2014-12-19 23:33:05 +00:00
}
private static void InstallCustomConfig(IToolForm tool, Dictionary<string, object> data)
{
Type type = tool.GetType();
var props = type.GetPropertiesWithAttrib(typeof(ConfigPersistAttribute)).ToList();
if (props.Count == 0)
2017-05-22 17:51:34 +00:00
{
return;
2017-05-22 17:51:34 +00:00
}
foreach (var prop in props)
{
object val;
if (data.TryGetValue(prop.Name, out val))
{
2014-12-21 05:56:51 +00:00
if (val is string && prop.PropertyType != typeof(string))
{
2014-12-21 05:56:51 +00:00
// if a type has a TypeConverter, and that converter can convert to string,
// that will be used in place of object markup by JSON.NET
// but that doesn't work with $type metadata, and JSON.NET fails to fall
// back on regular object serialization when needed. so try to undo a TypeConverter
// operation here
var converter = TypeDescriptor.GetConverter(prop.PropertyType);
2017-05-22 17:51:34 +00:00
val = converter.ConvertFromString(null, System.Globalization.CultureInfo.InvariantCulture, (string)val);
}
else if (!(val is bool) && prop.PropertyType.IsPrimitive)
{
// numeric constanst are similarly hosed
val = Convert.ChangeType(val, prop.PropertyType, System.Globalization.CultureInfo.InvariantCulture);
}
2017-05-22 17:51:34 +00:00
2014-12-21 05:56:51 +00:00
prop.SetValue(tool, val, null);
}
}
2017-05-22 17:51:34 +00:00
((Form)tool).FormClosing += (o, e) => SaveCustomConfig(tool, data, props);
}
private static void SaveCustomConfig(IToolForm tool, Dictionary<string, object> data, List<PropertyInfo> props)
{
data.Clear();
foreach (var prop in props)
{
data.Add(prop.Name, prop.GetValue(tool, BindingFlags.GetProperty, Type.DefaultBinder, null, System.Globalization.CultureInfo.InvariantCulture));
}
}
2014-12-19 23:33:05 +00:00
/// <summary>
/// Determines whether a given IToolForm is already loaded
/// </summary>
2017-05-22 17:51:34 +00:00
/// <typeparam name="T">Type of tool to check</typeparam>
public bool IsLoaded<T>() where T : IToolForm
{
2017-05-22 17:51:34 +00:00
var existingTool = _tools.FirstOrDefault(t => t is T);
if (existingTool != null)
{
return !existingTool.IsDisposed;
}
return false;
}
public static bool IsOnScreen(Point topLeft)
{
return Screen.AllScreens.Any(
screen => screen.WorkingArea.Contains(topLeft));
}
/// <summary>
/// Returns true if an instance of T exists
/// </summary>
2017-05-22 17:51:34 +00:00
/// <typeparam name="T">Type of tool to check</typeparam>
public bool Has<T>() where T : IToolForm
{
2017-05-22 17:51:34 +00:00
return _tools.Any(t => t is T && !t.IsDisposed);
}
/// <summary>
/// Gets the instance of T, or creates and returns a new instance
/// </summary>
2017-05-22 17:51:34 +00:00
/// <typeparam name="T">Type of tool to get</typeparam>
public IToolForm Get<T>() where T : class, IToolForm
{
2014-12-19 22:21:25 +00:00
return Load<T>(false);
}
public IEnumerable<Type> AvailableTools
{
get
{
return Assembly
.GetAssembly(typeof(IToolForm))
.GetTypes()
.Where(t => typeof(IToolForm).IsAssignableFrom(t))
.Where(t => !t.IsInterface)
2017-05-22 17:51:34 +00:00
.Where(IsAvailable);
}
}
public void UpdateBefore()
{
2017-05-22 17:51:34 +00:00
var beforeList = _tools.Where(t => t.UpdateBefore);
foreach (var tool in beforeList)
{
2017-05-22 17:51:34 +00:00
if (!tool.IsDisposed
|| (tool is RamWatch && Global.Config.DisplayRamWatch)) // RAM Watch hack, on screen display should run even if RAM Watch is closed
{
tool.UpdateValues();
}
}
2017-05-22 17:51:34 +00:00
foreach (var tool in _tools)
2017-05-22 17:51:34 +00:00
{
if (!tool.IsDisposed)
{
tool.NewUpdate(ToolFormUpdateType.PreFrame);
}
2017-05-22 17:51:34 +00:00
}
}
public void UpdateAfter()
{
2017-05-22 17:51:34 +00:00
var afterList = _tools.Where(t => !t.UpdateBefore);
foreach (var tool in afterList)
{
2017-05-22 17:51:34 +00:00
if (!tool.IsDisposed
|| (tool is RamWatch && Global.Config.DisplayRamWatch)) // RAM Watch hack, on screen display should run even if RAM Watch is closed
{
tool.UpdateValues();
}
}
foreach (var tool in _tools)
2017-05-22 17:51:34 +00:00
{
if (!tool.IsDisposed)
{
tool.NewUpdate(ToolFormUpdateType.PostFrame);
}
2017-05-22 17:51:34 +00:00
}
}
/// <summary>
/// Calls UpdateValues() on an instance of T, if it exists
/// </summary>
2017-05-22 17:51:34 +00:00
/// <typeparam name="T">Type of tool to update</typeparam>
public void UpdateValues<T>() where T : IToolForm
{
2017-05-22 17:51:34 +00:00
var tool = _tools.FirstOrDefault(t => t is T);
if (tool != null)
{
if (!tool.IsDisposed ||
2016-07-21 16:02:54 +00:00
(tool is RamWatch && Global.Config.DisplayRamWatch)) // RAM Watch hack, on screen display should run even if RAM Watch is closed
{
tool.UpdateValues();
}
}
}
public void Restart()
{
// If Cheat tool is loaded, restarting will restart the list too anyway
if (!GlobalWin.Tools.Has<Cheats>())
{
Global.CheatList.NewList(GenerateDefaultCheatFilename(), autosave: true);
}
var unavailable = new List<IToolForm>();
foreach (var tool in _tools)
{
if (ServiceInjector.IsAvailable(Global.Emulator.ServiceProvider, tool.GetType()))
{
ServiceInjector.UpdateServices(Global.Emulator.ServiceProvider, tool);
2016-07-21 16:02:54 +00:00
if ((tool.IsHandleCreated && !tool.IsDisposed) || tool is RamWatch) // Hack for RAM Watch - in display watches mode it wants to keep running even closed, it will handle disposed logic
{
if (tool is IExternalToolForm)
ApiInjector.UpdateApis(GlobalWin.ApiProvider, tool);
tool.Restart();
}
}
else
{
unavailable.Add(tool);
ServiceInjector.ClearServices(tool); // the services of the old emulator core are no longer valid on the tool
}
}
foreach (var tool in unavailable)
{
tool.Close();
_tools.Remove(tool);
}
}
/// <summary>
/// Calls Restart() on an instance of T, if it exists
/// </summary>
2017-05-22 17:51:34 +00:00
/// <typeparam name="T">Type of tool to restart</typeparam>
public void Restart<T>() where T : IToolForm
{
2017-05-22 17:51:34 +00:00
var tool = _tools.FirstOrDefault(t => t is T);
tool?.Restart();
}
/// <summary>
/// Runs AskSave on every tool dialog, false is returned if any tool returns false
/// </summary>
public bool AskSave()
{
if (Global.Config.SupressAskSave) // User has elected to not be nagged
{
return true;
}
return _tools
.Select(tool => tool.AskSaveChanges())
.All(result => result);
}
/// <summary>
/// Calls AskSave() on an instance of T, if it exists, else returns true
/// The caller should interpret false as cancel and will back out of the action that invokes this call
/// </summary>
2017-05-22 17:51:34 +00:00
/// <typeparam name="T">Type of tool</typeparam>
public bool AskSave<T>() where T : IToolForm
{
if (Global.Config.SupressAskSave) // User has elected to not be nagged
{
return true;
}
2017-05-22 17:51:34 +00:00
var tool = _tools.FirstOrDefault(t => t is T);
if (tool != null)
{
return tool.AskSaveChanges();
}
2017-05-22 17:51:34 +00:00
return false;
}
/// <summary>
/// If T exists, this call will close the tool, and remove it from memory
/// </summary>
2017-05-22 17:51:34 +00:00
/// <typeparam name="T">Type of tool to close</typeparam>
public void Close<T>() where T : IToolForm
{
2017-05-22 17:51:34 +00:00
var tool = _tools.FirstOrDefault(t => t is T);
if (tool != null)
{
tool.Close();
_tools.Remove(tool);
}
}
public void Close(Type toolType)
{
2017-05-22 17:51:34 +00:00
var tool = _tools.FirstOrDefault(toolType.IsInstanceOfType);
if (tool != null)
{
tool.Close();
_tools.Remove(tool);
}
}
public void Close()
{
2017-05-22 17:51:34 +00:00
_tools.ForEach(t => t.Close());
_tools.Clear();
2015-11-22 16:56:08 +00:00
}
/// <summary>
/// Create a new instance of an IToolForm and return it
/// </summary>
/// <typeparam name="T">Type of tool you want to create</typeparam>
2017-05-22 17:51:34 +00:00
/// <param name="dllPath">Path .dll for an external tool</param>
/// <returns>New instance of an IToolForm</returns>
private IToolForm CreateInstance<T>(string dllPath)
where T : IToolForm
{
return CreateInstance(typeof(T), dllPath);
2015-11-22 16:56:08 +00:00
}
/// <summary>
/// Create a new instance of an IToolForm and return it
/// </summary>
/// <param name="toolType">Type of tool you want to create</param>
/// <param name="dllPath">Path dll for an external tool</param>
/// <returns>New instance of an IToolForm</returns>
private IToolForm CreateInstance(Type toolType, string dllPath)
{
IToolForm tool;
2017-05-22 17:51:34 +00:00
// Specific case for custom tools
// TODO: Use AppDomain in order to be able to unload the assembly
// Hard stuff as we need a proxy object that inherit from MarshalByRefObject.
if (toolType == typeof(IExternalToolForm))
{
2017-05-22 17:51:34 +00:00
if (MessageBox.Show(
"Are you sure want to load this external tool?\r\nAccept ONLY if you trust the source and if you know what you're doing. In any other case, choose no.",
"Confirmm loading", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
try
2015-11-22 16:56:08 +00:00
{
tool = Activator.CreateInstanceFrom(dllPath, "BizHawk.Client.EmuHawk.CustomMainForm").Unwrap() as IExternalToolForm;
if (tool == null)
{
MessageBox.Show("It seems that the object CustomMainForm does not implement IExternalToolForm. Please review the code.", "No, no, no. Wrong Way !", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return null;
2015-11-22 16:56:08 +00:00
}
}
catch (MissingMethodException)
{
MessageBox.Show("It seems that the object CustomMainForm does not have a public default constructor. Please review the code.", "No, no, no. Wrong Way !", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return null;
}
catch (TypeLoadException)
{
MessageBox.Show("It seems that the object CustomMainForm does not exists. Please review the code.", "No, no, no. Wrong Way !", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return null;
}
}
else
{
return null;
}
}
else
{
tool = (IToolForm)Activator.CreateInstance(toolType);
}
// Add to our list of tools
_tools.Add(tool);
return tool;
}
public void UpdateToolsBefore(bool fromLua = false)
{
if (Has<LuaConsole>())
{
if (!fromLua)
{
LuaConsole.LuaImp.StartLuaDrawing();
}
}
UpdateBefore();
}
public void UpdateToolsAfter(bool fromLua = false)
{
if (!fromLua && Has<LuaConsole>())
{
LuaConsole.ResumeScripts(true);
}
GlobalWin.Tools.UpdateAfter();
if (Has<LuaConsole>())
{
if (!fromLua)
{
LuaConsole.LuaImp.EndLuaDrawing();
}
}
}
public void FastUpdateBefore()
{
2017-05-22 17:51:34 +00:00
var beforeList = _tools.Where(t => t.UpdateBefore);
foreach (var tool in beforeList)
{
2017-05-22 17:51:34 +00:00
if (!tool.IsDisposed
|| (tool is RamWatch && Global.Config.DisplayRamWatch)) // RAM Watch hack, on screen display should run even if RAM Watch is closed
{
tool.FastUpdate();
}
}
}
2018-03-09 10:35:10 +00:00
public void FastUpdateAfter(bool fromLua = false)
{
2018-03-09 10:35:10 +00:00
if (!fromLua && Global.Config.RunLuaDuringTurbo && Has<LuaConsole>())
{
LuaConsole.ResumeScripts(true);
}
2017-05-22 17:51:34 +00:00
var afterList = _tools.Where(t => !t.UpdateBefore);
foreach (var tool in afterList)
{
2017-05-22 17:51:34 +00:00
if (!tool.IsDisposed
|| (tool is RamWatch && Global.Config.DisplayRamWatch)) // RAM Watch hack, on screen display should run even if RAM Watch is closed
{
tool.FastUpdate();
}
}
if (Global.Config.RunLuaDuringTurbo && Has<LuaConsole>())
{
LuaConsole.LuaImp.EndLuaDrawing();
}
}
2015-01-01 00:06:00 +00:00
public bool IsAvailable<T>()
{
return IsAvailable(typeof(T));
}
public bool IsAvailable(Type t)
{
if (!ServiceInjector.IsAvailable(Global.Emulator.ServiceProvider, t))
{
return false;
}
Add preliminary Unix compatibility (using Mono; resolves #1384) (#1380) * Move PlatformSpecificLinkedLibs and implementations to common and rename * Specify file ext. at LoadPlatformSpecific call site * Move Client.Common.Global.RunningOnUnix to PlatformLinkedLibSingleton * Inline var Resolver * Use PlatformLinkedLibManager internally * Move plugin load check to LinkedLibManager, use LinkedLibManager * Interpolate * Return exit code from dlclose/FreeLibrary * Skip all calls to externs in BlipBufDll when using mono * Use PlatformLinkedLibManager in SevenZipLibraryManager * Add expected return value to workaround (from testing on Win32) * Remove ".dll" from DllImport attr, remove temporary workaround, see desc. The library can be built by changing the output file name in `.../blip_buf/Makefile` to `libblip_buf.so`, and running `make`. It will be loaded if placed in the `.../output` folder. * Remove unused code, add TODO (this class is req. for Waterbox.PeWrapper) The TODO is to [rewrite with C#](https://docs.microsoft.com/en-us/dotnet/standard/io/memory-mapped-files) instead of importing from `kernel32.dll`. * Update OpenTK again but better (for #1384) * Add Mono run script * Add libblip_buf.so (temporary) Temporary because it should be a separate package which BizHawk depends on. * Add distro detection, add "already running" and "unknown distro" messages * Gray-out Lua Console on Unix * Extract superclass from EmuLuaLibrary, add shell implementation for Unix * Specify libdl version, Fedora doesn't have the versionless symlink * Remove empty `ToolStripMenuItem`, null `Text` caused crash on Unix * Transform OpenTK keyboard input into a `List<KeyEvent>` and read that Also fixes crash on rebind * Remove debug `using ...;`
2019-01-03 22:50:55 +00:00
if (t == typeof(LuaConsole) && PlatformLinkedLibSingleton.RunningOnUnix) return false;
var tool = Assembly
.GetExecutingAssembly()
.GetTypes()
.FirstOrDefault(type => type == t);
if (tool == null) // This isn't a tool, must not be available
{
return false;
}
var attr = tool.GetCustomAttributes(false)
2017-07-12 19:44:14 +00:00
.OfType<ToolAttribute>()
.FirstOrDefault();
// start with the assumption that if no supported systems are mentioned and no unsupported cores are mentioned
// then this is available for all
bool supported = true;
if (attr?.SupportedSystems != null && attr.SupportedSystems.Any())
{
// supported systems are available
supported = attr.SupportedSystems.Contains(Global.Emulator.SystemId);
if (supported)
{
// check for a core not supported override
if (attr.UnsupportedCores.Contains(Global.Emulator.DisplayName()))
supported = false;
}
}
else if (attr?.UnsupportedCores != null && attr.UnsupportedCores.Any())
{
// no supported system specified, but unsupported cores are
if (attr.UnsupportedCores.Contains(Global.Emulator.DisplayName()))
supported = false;
}
return supported;
2015-01-01 00:06:00 +00:00
}
// Note: Referencing these properties creates an instance of the tool and persists it. They should be referenced by type if this is not desired
#region Tools
public RamWatch RamWatch
{
get
{
2017-05-22 17:51:34 +00:00
var tool = _tools.FirstOrDefault(t => t is RamWatch);
if (tool != null)
{
2013-11-02 20:13:53 +00:00
if (tool.IsDisposed)
{
_tools.Remove(tool);
}
else
{
return tool as RamWatch;
}
}
var newTool = new RamWatch();
_tools.Add(newTool);
return newTool;
2013-11-02 20:13:53 +00:00
}
}
public RamSearch RamSearch
{
get
{
2017-05-22 17:51:34 +00:00
var tool = _tools.FirstOrDefault(t => t is RamSearch);
2013-11-02 20:13:53 +00:00
if (tool != null)
{
2013-11-02 20:13:53 +00:00
if (tool.IsDisposed)
{
_tools.Remove(tool);
}
else
{
return tool as RamSearch;
}
}
2013-11-02 20:13:53 +00:00
var newTool = new RamSearch();
_tools.Add(newTool);
return newTool;
}
}
public Cheats Cheats
{
get
{
2017-05-22 17:51:34 +00:00
var tool = _tools.FirstOrDefault(t => t is Cheats);
if (tool != null)
{
if (tool.IsDisposed)
{
_tools.Remove(tool);
}
else
{
return tool as Cheats;
}
}
var newTool = new Cheats();
_tools.Add(newTool);
return newTool;
}
}
2013-11-02 20:25:53 +00:00
public HexEditor HexEditor
{
get
{
2017-05-22 17:51:34 +00:00
var tool = _tools.FirstOrDefault(t => t is HexEditor);
2013-11-02 20:25:53 +00:00
if (tool != null)
{
if (tool.IsDisposed)
{
_tools.Remove(tool);
}
else
{
return tool as HexEditor;
}
}
var newTool = new HexEditor();
_tools.Add(newTool);
return newTool;
2013-11-02 20:25:53 +00:00
}
}
public VirtualpadTool VirtualPad
2013-11-02 21:31:04 +00:00
{
get
{
2017-05-22 17:51:34 +00:00
var tool = _tools.FirstOrDefault(t => t is VirtualpadTool);
2013-11-02 21:31:04 +00:00
if (tool != null)
{
if (tool.IsDisposed)
{
_tools.Remove(tool);
}
else
{
return tool as VirtualpadTool;
2013-11-02 21:31:04 +00:00
}
}
var newTool = new VirtualpadTool();
_tools.Add(newTool);
return newTool;
}
}
public SNESGraphicsDebugger SNESGraphicsDebugger
{
get
{
2017-05-22 17:51:34 +00:00
var tool = _tools.FirstOrDefault(t => t is SNESGraphicsDebugger);
if (tool != null)
{
if (tool.IsDisposed)
{
_tools.Remove(tool);
}
else
{
return tool as SNESGraphicsDebugger;
}
}
var newTool = new SNESGraphicsDebugger();
_tools.Add(newTool);
return newTool;
2013-11-02 21:31:04 +00:00
}
}
public LuaConsole LuaConsole
{
get
{
2017-05-22 17:51:34 +00:00
var tool = _tools.FirstOrDefault(t => t is LuaConsole);
if (tool != null)
{
if (tool.IsDisposed)
{
_tools.Remove(tool);
}
else
{
return tool as LuaConsole;
}
}
var newTool = new LuaConsole();
_tools.Add(newTool);
return newTool;
}
}
public TAStudio TAStudio
{
get
{
// prevent nasty silent corruption
if (!GlobalWin.Tools.IsLoaded<TAStudio>())
2017-05-22 17:51:34 +00:00
{
System.Diagnostics.Debug.Fail("TAStudio does not exist!");
2017-05-22 17:51:34 +00:00
}
2017-05-22 17:51:34 +00:00
var tool = _tools.FirstOrDefault(t => t is TAStudio);
if (tool != null)
{
if (tool.IsDisposed)
{
_tools.Remove(tool);
}
else
{
return tool as TAStudio;
}
}
var newTool = new TAStudio();
_tools.Add(newTool);
return newTool;
}
}
#endregion
#region Specialized Tool Loading Logic
public void LoadRamWatch(bool loadDialog)
{
if (!IsLoaded<RamWatch>())
{
Load<RamWatch>();
}
2015-01-02 15:19:18 +00:00
if (IsAvailable<RamWatch>()) // Just because we attempted to load it, doesn't mean it was, the current core may not have the correct dependencies
{
if (Global.Config.RecentWatches.AutoLoad && !Global.Config.RecentWatches.Empty)
{
RamWatch.LoadFileFromRecent(Global.Config.RecentWatches.MostRecent);
}
if (!loadDialog)
{
Get<RamWatch>().Close();
}
}
}
public void LoadGameGenieEc()
{
if (GlobalWin.Tools.IsAvailable<GameShark>())
2018-11-12 23:12:00 +00:00
{
GlobalWin.Tools.Load<GameShark>();
}
}
#endregion
public static string GenerateDefaultCheatFilename()
{
var pathEntry = Global.Config.PathEntries[Global.Game.System, "Cheats"]
?? Global.Config.PathEntries[Global.Game.System, "Base"];
var path = PathManager.MakeAbsolutePath(pathEntry.Path, Global.Game.System);
var f = new FileInfo(path);
if (f.Directory != null && f.Directory.Exists == false)
{
f.Directory.Create();
}
2019-03-18 14:06:37 +00:00
return Path.Combine(path, $"{PathManager.FilesystemSafeName(Global.Game)}.cht");
}
}
}