2013-11-02 19:28:45 +00:00
using System ;
using System.Collections.Generic ;
2017-02-20 16:42:48 +00:00
using System.Drawing ;
2013-12-23 03:07:06 +00:00
using System.IO ;
2013-11-02 19:28:45 +00:00
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 ;
2014-11-30 18:35:25 +00:00
2018-12-22 18:40:30 +00:00
using BizHawk.Client.ApiHawk ;
2013-11-24 16:00:10 +00:00
using BizHawk.Client.Common ;
2018-09-10 19:08:44 +00:00
using BizHawk.Client.EmuHawk.CoreExtensions ;
2019-01-03 22:50:55 +00:00
using BizHawk.Common ;
using BizHawk.Common.ReflectionExtensions ;
using BizHawk.Emulation.Common ;
2014-12-17 18:17:16 +00:00
2013-11-03 03:54:37 +00:00
namespace BizHawk.Client.EmuHawk
2013-11-02 19:28:45 +00:00
{
public class ToolManager
{
2015-02-22 04:02:30 +00:00
private readonly Form _owner ;
2014-01-01 02:09:03 +00:00
// 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
2014-01-01 02:16:47 +00:00
private readonly List < IToolForm > _tools = new List < IToolForm > ( ) ;
2013-11-02 19:28:45 +00:00
/// <summary>
2017-05-22 17:51:34 +00:00
/// Initializes a new instance of the <see cref="ToolManager"/> class.
2013-11-02 19:28:45 +00:00
/// </summary>
2015-11-22 16:37:00 +00:00
/// <param name="owner">Form that handle the ToolManager</param>
public ToolManager ( Form owner )
2013-11-02 19:28:45 +00:00
{
2015-11-22 16:37:00 +00:00
_owner = owner ;
2014-12-14 02:15:19 +00:00
}
/// <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
2015-11-22 16:37:00 +00:00
/// This method should be used only if you can't use the generic one
2014-12-14 02:15:19 +00:00
/// </summary>
2015-11-22 16:37:00 +00:00
/// <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>
2019-10-27 14:34:32 +00:00
/// <exception cref="ArgumentException">Raised if <paramref name="toolType"/> can't cast into IToolForm </exception>
2015-11-22 16:37:00 +00:00
internal IToolForm Load ( Type toolType , bool focus = true )
2014-12-14 02:15:19 +00:00
{
if ( ! typeof ( IToolForm ) . IsAssignableFrom ( toolType ) )
2015-11-14 21:11:13 +00:00
{
2019-03-28 03:17:14 +00:00
throw new ArgumentException ( $"Type {toolType.Name} does not implement {nameof(IToolForm)}." ) ;
2015-11-22 16:37:00 +00:00
}
2017-05-22 17:51:34 +00:00
2019-12-02 00:05:20 +00:00
// The type[] in parameter is used to avoid an ambiguous name exception
2017-05-22 17:51:34 +00:00
MethodInfo method = GetType ( ) . GetMethod ( "Load" , new Type [ ] { typeof ( bool ) } ) . MakeGenericMethod ( toolType ) ;
return ( IToolForm ) method . Invoke ( this , new object [ ] { focus } ) ;
2015-11-22 16:37:00 +00:00
}
2014-12-14 02:15:19 +00:00
2015-11-22 16:37:00 +00:00
/// <summary>
2015-12-05 09:09:39 +00:00
/// 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
2015-11-22 16:37:00 +00:00
/// </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>
2015-11-22 16:37:00 +00:00
public T Load < T > ( bool focus = true )
where T : class , IToolForm
{
2017-05-10 11:45:23 +00:00
return Load < T > ( "" , focus ) ;
2015-11-22 16:37:00 +00:00
}
/// <summary>
2015-12-05 09:09:39 +00:00
/// 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
2015-11-22 16:37:00 +00:00
/// </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>
2015-11-22 16:37:00 +00:00
/// <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>
2015-11-22 16:37:00 +00:00
public T Load < T > ( string toolPath , bool focus = true )
where T : class , IToolForm
{
2015-12-30 10:00:56 +00:00
bool isExternal = typeof ( T ) = = typeof ( IExternalToolForm ) ;
if ( ! IsAvailable < T > ( ) & & ! isExternal )
2015-11-14 21:11:13 +00:00
{
2014-12-14 02:15:19 +00:00
return null ;
2015-11-14 21:11:13 +00:00
}
2014-12-14 02:15:19 +00:00
2015-12-30 10:00:56 +00:00
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 ) ;
2015-12-30 10:00:56 +00:00
}
else
{
2017-05-22 17:51:34 +00:00
existingTool = ( T ) _tools . FirstOrDefault ( t = > t is T ) ;
2015-12-30 10:00:56 +00:00
}
2014-12-14 02:15:19 +00:00
2015-12-04 13:10:04 +00:00
if ( existingTool ! = null )
2013-11-02 19:28:45 +00:00
{
2014-12-14 02:15:19 +00:00
if ( existingTool . IsDisposed )
2013-11-02 20:13:53 +00:00
{
2014-12-14 02:15:19 +00:00
_tools . Remove ( existingTool ) ;
2013-11-02 20:13:53 +00:00
}
2014-12-14 02:15:19 +00:00
else
{
2014-12-19 22:21:25 +00:00
if ( focus )
{
existingTool . Show ( ) ;
existingTool . Focus ( ) ;
}
2015-11-14 21:11:13 +00:00
2014-12-14 02:15:19 +00:00
return existingTool ;
}
}
2013-11-02 20:13:53 +00:00
2015-11-22 16:37:00 +00:00
IToolForm newTool = CreateInstance < T > ( toolPath ) ;
2014-12-14 01:25:28 +00:00
2015-11-22 16:37:00 +00:00
if ( newTool = = null )
{
return null ;
}
2015-10-07 21:54:57 +00:00
2019-12-02 00:05:20 +00:00
if ( newTool is Form form )
2015-02-22 04:02:30 +00:00
{
2019-12-13 21:44:50 +00:00
form . Owner = _owner ;
2015-02-22 04:02:30 +00:00
}
2018-12-22 18:40:30 +00:00
if ( isExternal )
{
ApiInjector . UpdateApis ( GlobalWin . ApiProvider , newTool ) ;
}
2014-12-17 18:17:16 +00:00
ServiceInjector . UpdateServices ( Global . Emulator . ServiceProvider , newTool ) ;
2015-11-22 16:37:00 +00:00
string toolType = typeof ( T ) . ToString ( ) ;
2014-12-13 22:32:08 +00:00
2014-12-20 21:49:53 +00:00
// auto settings
2019-12-02 00:05:20 +00:00
if ( newTool is IToolFormAutoConfig tool )
2014-12-19 23:33:05 +00:00
{
2019-12-02 00:05:20 +00:00
if ( ! Global . Config . CommonToolSettings . TryGetValue ( toolType , out var settings ) )
2014-12-20 21:49:53 +00:00
{
settings = new ToolDialogSettings ( ) ;
2015-11-22 16:37:00 +00:00
Global . Config . CommonToolSettings [ toolType ] = settings ;
2014-12-20 21:49:53 +00:00
}
2017-05-22 17:51:34 +00:00
2019-12-02 00:05:20 +00:00
AttachSettingHooks ( tool , settings ) ;
2014-12-19 23:33:05 +00:00
}
2014-12-20 21:49:53 +00:00
// custom settings
if ( HasCustomConfig ( newTool ) )
{
2019-10-29 17:50:18 +00:00
if ( ! Global . Config . CustomToolSettings . TryGetValue ( toolType , out var settings ) )
2014-12-20 21:49:53 +00:00
{
settings = new Dictionary < string , object > ( ) ;
2015-11-22 16:37:00 +00:00
Global . Config . CustomToolSettings [ toolType ] = settings ;
2014-12-20 21:49:53 +00:00
}
2017-05-22 17:51:34 +00:00
2014-12-20 21:49:53 +00:00
InstallCustomConfig ( newTool , settings ) ;
}
2014-12-19 23:33:05 +00:00
newTool . Restart ( ) ;
2019-11-04 04:30:05 +00:00
if ( OSTailoredCode . IsUnixHost & & newTool is RamSearch )
2019-05-20 13:32:46 +00:00
{
// the mono winforms implementation is buggy, skip to the return statement and call Show in MainForm instead
}
else
{
newTool . Show ( ) ;
}
2015-11-22 16:37:00 +00:00
return ( T ) newTool ;
2013-11-02 19:28:45 +00:00
}
2014-12-19 23:33:05 +00:00
public void AutoLoad ( )
{
2015-01-01 02:08:45 +00:00
var genericSettings = Global . Config . CommonToolSettings
. Where ( kvp = > kvp . Value . AutoLoad )
. Select ( kvp = > kvp . Key ) ;
var customSettings = Global . Config . CustomToolSettings
2019-12-17 19:45:55 +00:00
. Where ( list = > list . Value . Any ( kvp = > kvp . Value is ToolDialogSettings settings & & settings . AutoLoad ) )
2015-01-01 02:08:45 +00:00
. 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 ) ;
}
}
}
2019-12-17 19:45:55 +00:00
private void RefreshSettings ( Form form , ToolStripItemCollection menu , ToolDialogSettings settings , int idx )
2014-12-23 01:01:37 +00:00
{
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 ;
2014-12-23 01:01:37 +00:00
form . TopMost = settings . TopMost ;
// do we need to do this OnShown() as well?
2019-12-17 19:45:55 +00:00
form . Owner = settings . FloatingWindow ? null : _owner ;
2014-12-23 01:01:37 +00:00
}
2015-02-22 04:06:40 +00:00
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 ;
2019-12-13 21:44:50 +00:00
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 )
{
2019-12-13 21:44:50 +00:00
if ( c is MenuStrip ms )
2014-12-20 17:05:13 +00:00
{
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
{
2019-03-28 03:17:14 +00:00
throw new InvalidOperationException ( $"{nameof(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" ) ;
2014-12-23 01:01:37 +00:00
dest . Add ( "Restore &Defaults" ) ;
2014-12-19 23:33:05 +00:00
2014-12-23 01:01:37 +00:00
RefreshSettings ( form , dest , settings , idx ) ;
2014-12-19 23:33:05 +00:00
2017-02-20 16:42:48 +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 )
{
2014-12-20 17:20:56 +00:00
if ( form . FormBorderStyle = = FormBorderStyle . Sizable | | form . FormBorderStyle = = FormBorderStyle . SizableToolWindow )
2017-05-22 17:51:34 +00:00
{
2014-12-20 17:20:56 +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 ) = >
{
2015-09-05 21:05:14 +00:00
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 ;
}
2015-09-05 21:05:14 +00:00
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 ;
2015-02-22 04:06:40 +00:00
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
} ;
2014-12-23 01:01:37 +00:00
dest [ idx + 4 ] . Click + = ( o , e ) = >
{
settings . RestoreDefaults ( ) ;
RefreshSettings ( form , dest , settings , idx ) ;
2019-12-13 21:44:50 +00:00
form . Size = oldSize ;
2019-10-19 15:45:42 +00:00
form . GetType ( )
. GetMethodsWithAttrib ( typeof ( RestoreDefaultsAttribute ) )
. FirstOrDefault ( )
? . Invoke ( form , new object [ 0 ] ) ;
2014-12-23 01:01:37 +00:00
} ;
2014-12-20 21:49:53 +00:00
}
2014-12-19 23:33:05 +00:00
2014-12-20 21:49:53 +00:00
private static bool HasCustomConfig ( IToolForm tool )
{
return tool . GetType ( ) . GetPropertiesWithAttrib ( typeof ( ConfigPersistAttribute ) ) . Any ( ) ;
2014-12-19 23:33:05 +00:00
}
2014-12-20 21:49:53 +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
{
2014-12-20 21:49:53 +00:00
return ;
2017-05-22 17:51:34 +00:00
}
2014-12-20 21:49:53 +00:00
foreach ( var prop in props )
{
2019-12-21 23:45:11 +00:00
if ( data . TryGetValue ( prop . Name , out var val ) )
2014-12-20 21:49:53 +00:00
{
2019-12-02 00:05:20 +00:00
if ( val is string str & & prop . PropertyType ! = typeof ( string ) )
2014-12-20 21:49:53 +00:00
{
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 ) ;
2019-12-02 00:05:20 +00:00
val = converter . ConvertFromString ( null , System . Globalization . CultureInfo . InvariantCulture , str ) ;
2014-12-20 21:49:53 +00:00
}
2014-12-21 08:17:56 +00:00
else if ( ! ( val is bool ) & & prop . PropertyType . IsPrimitive )
{
2019-10-29 17:50:18 +00:00
// numeric constants are similarly hosed
2016-04-16 01:12:01 +00:00
val = Convert . ChangeType ( val , prop . PropertyType , System . Globalization . CultureInfo . InvariantCulture ) ;
2014-12-21 08:17:56 +00:00
}
2017-05-22 17:51:34 +00:00
2014-12-21 05:56:51 +00:00
prop . SetValue ( tool , val , null ) ;
2014-12-20 21:49:53 +00:00
}
}
2017-05-22 17:51:34 +00:00
( ( Form ) tool ) . FormClosing + = ( o , e ) = > SaveCustomConfig ( tool , data , props ) ;
2014-12-20 21:49:53 +00:00
}
private static void SaveCustomConfig ( IToolForm tool , Dictionary < string , object > data , List < PropertyInfo > props )
{
data . Clear ( ) ;
foreach ( var prop in props )
{
2016-04-16 01:12:01 +00:00
data . Add ( prop . Name , prop . GetValue ( tool , BindingFlags . GetProperty , Type . DefaultBinder , null , System . Globalization . CultureInfo . InvariantCulture ) ) ;
2014-12-20 21:49:53 +00:00
}
}
2014-12-19 23:33:05 +00:00
2014-03-01 19:16:17 +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>
2014-03-01 19:16:17 +00:00
public bool IsLoaded < T > ( ) where T : IToolForm
{
2017-05-22 17:51:34 +00:00
var existingTool = _tools . FirstOrDefault ( t = > t is T ) ;
2014-03-01 19:16:17 +00:00
if ( existingTool ! = null )
{
return ! existingTool . IsDisposed ;
}
return false ;
}
2017-02-20 16:42:48 +00:00
public static bool IsOnScreen ( Point topLeft )
{
return Screen . AllScreens . Any (
screen = > screen . WorkingArea . Contains ( topLeft ) ) ;
}
2013-11-02 19:28:45 +00:00
/// <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>
2013-11-02 19:28:45 +00:00
public bool Has < T > ( ) where T : IToolForm
{
2017-05-22 17:51:34 +00:00
return _tools . Any ( t = > t is T & & ! t . IsDisposed ) ;
2013-11-02 19:28:45 +00:00
}
/// <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>
2015-11-22 16:37:00 +00:00
public IToolForm Get < T > ( ) where T : class , IToolForm
2013-11-02 19:28:45 +00:00
{
2014-12-19 22:21:25 +00:00
return Load < T > ( false ) ;
2013-11-02 19:28:45 +00:00
}
2019-12-21 23:45:11 +00:00
public IEnumerable < Type > AvailableTools = > Assembly
. GetAssembly ( typeof ( IToolForm ) )
. GetTypes ( )
. Where ( t = > typeof ( IToolForm ) . IsAssignableFrom ( t ) )
. Where ( t = > ! t . IsInterface )
. Where ( IsAvailable ) ;
2013-11-02 19:28:45 +00:00
public void UpdateBefore ( )
{
2017-05-22 17:51:34 +00:00
var beforeList = _tools . Where ( t = > t . UpdateBefore ) ;
2013-11-02 19:28:45 +00:00
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
2013-11-07 20:33:29 +00:00
{
tool . UpdateValues ( ) ;
}
2013-11-02 19:28:45 +00:00
}
2017-05-22 17:51:34 +00:00
2016-08-13 20:31:04 +00:00
foreach ( var tool in _tools )
2017-05-22 17:51:34 +00:00
{
2017-07-09 18:18:59 +00:00
if ( ! tool . IsDisposed )
{
tool . NewUpdate ( ToolFormUpdateType . PreFrame ) ;
}
2017-05-22 17:51:34 +00:00
}
2013-11-02 19:28:45 +00:00
}
public void UpdateAfter ( )
{
2017-05-22 17:51:34 +00:00
var afterList = _tools . Where ( t = > ! t . UpdateBefore ) ;
2014-05-04 14:22:11 +00:00
foreach ( var tool in afterList )
2013-11-02 19:28:45 +00:00
{
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
2014-04-26 14:51:33 +00:00
{
tool . UpdateValues ( ) ;
}
2013-11-02 19:28:45 +00:00
}
2016-08-13 20:31:04 +00:00
foreach ( var tool in _tools )
2017-05-22 17:51:34 +00:00
{
2017-07-09 18:18:59 +00:00
if ( ! tool . IsDisposed )
{
tool . NewUpdate ( ToolFormUpdateType . PostFrame ) ;
}
2017-05-22 17:51:34 +00:00
}
2013-11-02 19:28:45 +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>
2013-11-02 19:28:45 +00:00
public void UpdateValues < T > ( ) where T : IToolForm
{
2017-05-22 17:51:34 +00:00
var tool = _tools . FirstOrDefault ( t = > t is T ) ;
2013-11-02 19:28:45 +00:00
if ( tool ! = null )
{
2015-01-02 15:27:54 +00:00
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
2015-01-02 15:27:54 +00:00
{
tool . UpdateValues ( ) ;
}
2013-11-02 19:28:45 +00:00
}
}
public void Restart ( )
{
2013-12-23 03:07:06 +00:00
// If Cheat tool is loaded, restarting will restart the list too anyway
2019-12-17 19:45:55 +00:00
if ( ! Has < Cheats > ( ) )
2013-12-23 03:07:06 +00:00
{
2014-04-26 17:28:43 +00:00
Global . CheatList . NewList ( GenerateDefaultCheatFilename ( ) , autosave : true ) ;
2013-12-23 03:07:06 +00:00
}
2014-12-13 22:32:08 +00:00
var unavailable = new List < IToolForm > ( ) ;
foreach ( var tool in _tools )
{
2014-12-17 18:17:16 +00:00
if ( ServiceInjector . IsAvailable ( Global . Emulator . ServiceProvider , tool . GetType ( ) ) )
2014-12-13 22:32:08 +00:00
{
2014-12-17 18:17:16 +00:00
ServiceInjector . UpdateServices ( Global . Emulator . ServiceProvider , tool ) ;
2016-08-28 16:07:26 +00:00
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
2014-12-17 03:21:32 +00:00
{
2018-12-22 18:40:30 +00:00
if ( tool is IExternalToolForm )
ApiInjector . UpdateApis ( GlobalWin . ApiProvider , tool ) ;
2014-12-17 03:21:32 +00:00
tool . Restart ( ) ;
}
2014-12-13 22:32:08 +00:00
}
else
{
unavailable . Add ( tool ) ;
2014-12-31 17:18:51 +00:00
ServiceInjector . ClearServices ( tool ) ; // the services of the old emulator core are no longer valid on the tool
2014-12-13 22:32:08 +00:00
}
}
foreach ( var tool in unavailable )
{
tool . Close ( ) ;
_tools . Remove ( tool ) ;
}
2013-11-02 19:28:45 +00:00
}
/// <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>
2013-11-02 19:28:45 +00:00
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 ( ) ;
2013-11-02 19:28:45 +00:00
}
/// <summary>
/// Runs AskSave on every tool dialog, false is returned if any tool returns false
/// </summary>
public bool AskSave ( )
{
2019-12-08 19:41:52 +00:00
if ( Global . Config . SuppressAskSave ) // User has elected to not be nagged
2014-01-01 02:09:03 +00:00
{
return true ;
}
2014-01-01 02:16:47 +00:00
return _tools
2014-08-19 19:24:17 +00:00
. Select ( tool = > tool . AskSaveChanges ( ) )
2014-01-01 02:16:47 +00:00
. All ( result = > result ) ;
2013-11-02 19:28:45 +00:00
}
/// <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>
2013-11-02 19:28:45 +00:00
public bool AskSave < T > ( ) where T : IToolForm
{
2019-12-08 19:41:52 +00:00
if ( Global . Config . SuppressAskSave ) // User has elected to not be nagged
2014-01-01 02:09:03 +00:00
{
return true ;
}
2017-05-22 17:51:34 +00:00
var tool = _tools . FirstOrDefault ( t = > t is T ) ;
2019-12-21 23:45:11 +00:00
return tool ! = null & & tool . AskSaveChanges ( ) ;
2013-11-02 19:28:45 +00:00
}
/// <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>
2013-11-02 19:28:45 +00:00
public void Close < T > ( ) where T : IToolForm
{
2017-05-22 17:51:34 +00:00
var tool = _tools . FirstOrDefault ( t = > t is T ) ;
2013-11-02 19:28:45 +00:00
if ( tool ! = null )
{
tool . Close ( ) ;
_tools . Remove ( tool ) ;
}
}
2014-12-14 02:15:19 +00:00
public void Close ( Type toolType )
{
2017-05-22 17:51:34 +00:00
var tool = _tools . FirstOrDefault ( toolType . IsInstanceOfType ) ;
2014-12-14 02:15:19 +00:00
if ( tool ! = null )
{
tool . Close ( ) ;
_tools . Remove ( tool ) ;
}
}
2013-11-02 19:28:45 +00:00
public void Close ( )
{
2017-05-22 17:51:34 +00:00
_tools . ForEach ( t = > t . Close ( ) ) ;
2013-11-02 19:28:45 +00:00
_tools . Clear ( ) ;
2015-11-22 16:56:08 +00:00
}
2013-11-02 19:28:45 +00:00
2015-11-22 16:37:00 +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>
2015-11-22 16:37:00 +00:00
/// <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
}
2015-11-22 16:37:00 +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 ;
2015-10-07 21:54:57 +00:00
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.
2015-11-22 16:37:00 +00:00
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." ,
2019-10-29 17:50:18 +00:00
"Confirm loading" , MessageBoxButtons . YesNo , MessageBoxIcon . Question ) = = DialogResult . Yes )
2015-11-22 16:37:00 +00:00
{
try
2015-11-22 16:56:08 +00:00
{
2015-11-22 16:37:00 +00:00
tool = Activator . CreateInstanceFrom ( dllPath , "BizHawk.Client.EmuHawk.CustomMainForm" ) . Unwrap ( ) as IExternalToolForm ;
2015-12-04 13:10:04 +00:00
if ( tool = = null )
2015-11-22 16:37:00 +00:00
{
2019-03-28 03:17:14 +00:00
MessageBox . Show ( $"It seems that the object CustomMainForm does not implement {nameof(IExternalToolForm)}. Please review the code." , "No, no, no. Wrong Way !" , MessageBoxButtons . OK , MessageBoxIcon . Exclamation ) ;
2015-11-22 16:37:00 +00:00
return null ;
2015-11-22 16:56:08 +00:00
}
2015-11-22 16:37:00 +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 ;
}
2015-11-28 21:47:16 +00:00
catch ( TypeLoadException )
2015-11-22 16:37:00 +00:00
{
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 )
2013-11-27 23:35:32 +00:00
{
if ( Has < LuaConsole > ( ) )
{
if ( ! fromLua )
{
2017-05-23 17:40:40 +00:00
LuaConsole . LuaImp . StartLuaDrawing ( ) ;
2013-11-27 23:35:32 +00:00
}
}
2014-01-01 02:16:47 +00:00
2013-11-27 23:35:32 +00:00
UpdateBefore ( ) ;
}
public void UpdateToolsAfter ( bool fromLua = false )
{
if ( ! fromLua & & Has < LuaConsole > ( ) )
{
LuaConsole . ResumeScripts ( true ) ;
}
2019-12-17 19:45:55 +00:00
UpdateAfter ( ) ;
2013-11-27 23:35:32 +00:00
if ( Has < LuaConsole > ( ) )
{
if ( ! fromLua )
{
2017-05-23 17:40:40 +00:00
LuaConsole . LuaImp . EndLuaDrawing ( ) ;
2013-11-27 23:35:32 +00:00
}
}
}
2014-07-25 01:55:21 +00:00
public void FastUpdateBefore ( )
{
2017-05-22 17:51:34 +00:00
var beforeList = _tools . Where ( t = > t . UpdateBefore ) ;
2014-07-25 01:55:21 +00:00
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
2014-07-25 01:55:21 +00:00
{
tool . FastUpdate ( ) ;
}
}
}
2018-03-09 10:35:10 +00:00
public void FastUpdateAfter ( bool fromLua = false )
2014-07-25 01:55:21 +00:00
{
2018-03-09 10:35:10 +00:00
if ( ! fromLua & & Global . Config . RunLuaDuringTurbo & & Has < LuaConsole > ( ) )
2015-07-10 00:14:52 +00:00
{
LuaConsole . ResumeScripts ( true ) ;
}
2017-05-22 17:51:34 +00:00
var afterList = _tools . Where ( t = > ! t . UpdateBefore ) ;
2014-07-25 01:55:21 +00:00
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
2014-07-25 01:55:21 +00:00
{
tool . FastUpdate ( ) ;
}
}
2015-10-22 16:33:04 +00:00
if ( Global . Config . RunLuaDuringTurbo & & Has < LuaConsole > ( ) )
{
2017-05-23 17:40:40 +00:00
LuaConsole . LuaImp . EndLuaDrawing ( ) ;
2015-10-22 16:33:04 +00:00
}
2014-07-25 01:55:21 +00:00
}
2019-12-21 23:45:11 +00:00
private static readonly Lazy < List < string > > LazyAsmTypes = new Lazy < List < string > > ( ( ) = >
2019-11-01 02:03:19 +00:00
Assembly . GetAssembly ( typeof ( ToolManager ) ) // Confining the search to only EmuHawk, for now at least, we may want to broaden for ApiHawk one day
2019-11-01 01:52:50 +00:00
. GetTypes ( )
. Select ( t = > t . AssemblyQualifiedName )
2019-12-21 23:45:11 +00:00
. ToList ( ) ) ;
2015-04-12 17:37:06 +00:00
2019-08-19 14:36:22 +00:00
public bool IsAvailable ( Type tool )
2015-04-12 17:37:06 +00:00
{
2019-08-19 14:36:22 +00:00
if ( ! ServiceInjector . IsAvailable ( Global . Emulator . ServiceProvider , tool )
2019-12-21 23:45:11 +00:00
| | ! LazyAsmTypes . Value . Contains ( tool . AssemblyQualifiedName ) ) // not a tool
2015-11-14 21:11:13 +00:00
{
return false ;
}
2019-10-13 18:21:15 +00:00
ToolAttribute attr = tool . GetCustomAttributes ( false ) . OfType < ToolAttribute > ( ) . SingleOrDefault ( ) ;
if ( attr = = null )
2015-11-14 21:11:13 +00:00
{
2019-08-19 14:36:22 +00:00
return true ; // no ToolAttribute on given type -> assumed all supported
2015-11-14 21:11:13 +00:00
}
2019-10-20 14:17:13 +00:00
var displayName = Global . Emulator . DisplayName ( ) ;
var systemId = Global . Emulator . SystemId ;
return ! attr . UnsupportedCores . Contains ( displayName ) // not unsupported
& & ( ! attr . SupportedSystems . Any ( ) | | attr . SupportedSystems . Contains ( systemId ) ) ; // supported (no supported list -> assumed all supported)
2015-01-01 00:06:00 +00:00
}
2019-08-19 14:36:22 +00:00
public bool IsAvailable < T > ( ) = > IsAvailable ( typeof ( T ) ) ;
2013-12-23 03:07:06 +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
2013-11-02 19:28:45 +00:00
#region Tools
2019-10-20 14:47:23 +00:00
private T GetTool < T > ( ) where T : class , IToolForm , new ( )
2013-11-02 19:28:45 +00:00
{
2019-10-20 14:47:23 +00:00
T tool = _tools . OfType < T > ( ) . FirstOrDefault ( ) ;
if ( tool ! = null )
2013-11-02 19:28:45 +00:00
{
2019-10-20 14:47:23 +00:00
if ( ! tool . IsDisposed )
2013-11-02 19:28:45 +00:00
{
2019-10-20 14:47:23 +00:00
return tool ;
2013-11-02 19:28:45 +00:00
}
2019-10-20 14:47:23 +00:00
_tools . Remove ( tool ) ;
2013-11-02 20:13:53 +00:00
}
2019-10-20 14:47:23 +00:00
tool = new T ( ) ;
_tools . Add ( tool ) ;
return tool ;
2013-11-02 20:13:53 +00:00
}
2019-10-20 14:47:23 +00:00
public RamWatch RamWatch = > GetTool < RamWatch > ( ) ;
2013-11-02 20:13:53 +00:00
2019-10-20 14:47:23 +00:00
public RamSearch RamSearch = > GetTool < RamSearch > ( ) ;
2013-11-02 19:28:45 +00:00
2019-10-20 14:47:23 +00:00
public Cheats Cheats = > GetTool < Cheats > ( ) ;
2013-11-07 20:33:29 +00:00
2019-10-20 14:47:23 +00:00
public HexEditor HexEditor = > GetTool < HexEditor > ( ) ;
2013-11-07 20:33:29 +00:00
2019-10-20 14:47:23 +00:00
public VirtualpadTool VirtualPad = > GetTool < VirtualpadTool > ( ) ;
2013-11-02 20:25:53 +00:00
2019-10-20 14:47:23 +00:00
public SNESGraphicsDebugger SNESGraphicsDebugger = > GetTool < SNESGraphicsDebugger > ( ) ;
2013-11-02 20:25:53 +00:00
2019-10-20 14:47:23 +00:00
public LuaConsole LuaConsole = > GetTool < LuaConsole > ( ) ;
2013-11-03 01:02:17 +00:00
2019-12-21 23:45:11 +00:00
public TAStudio TAStudio = > GetTool < TAStudio > ( ) ;
2015-12-05 09:09:39 +00:00
2013-11-02 19:28:45 +00:00
#endregion
2013-11-28 20:02:32 +00:00
2013-12-22 23:34:22 +00:00
#region Specialized Tool Loading Logic
2013-11-28 20:02:32 +00:00
public void LoadRamWatch ( bool loadDialog )
{
2019-12-02 23:19:58 +00:00
if ( IsLoaded < RamWatch > ( ) )
2015-06-21 00:21:36 +00:00
{
2019-12-02 23:19:58 +00:00
return ;
2015-06-21 00:21:36 +00:00
}
2015-01-02 15:19:18 +00:00
2019-12-02 23:19:58 +00:00
Load < RamWatch > ( ) ;
2015-02-25 22:32:50 +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
2013-11-28 20:02:32 +00:00
{
2015-02-25 22:32:50 +00:00
if ( Global . Config . RecentWatches . AutoLoad & & ! Global . Config . RecentWatches . Empty )
{
RamWatch . LoadFileFromRecent ( Global . Config . RecentWatches . MostRecent ) ;
}
2013-12-22 23:34:22 +00:00
2015-02-25 22:32:50 +00:00
if ( ! loadDialog )
{
Get < RamWatch > ( ) . Close ( ) ;
}
2013-11-28 20:02:32 +00:00
}
}
2013-12-22 23:34:22 +00:00
public void LoadGameGenieEc ( )
{
2019-12-17 19:45:55 +00:00
if ( IsAvailable < GameShark > ( ) )
2018-11-12 23:12:00 +00:00
{
2019-12-17 19:45:55 +00:00
Load < GameShark > ( ) ;
2013-12-22 23:34:22 +00:00
}
}
#endregion
2013-12-23 03:07:06 +00:00
public static string GenerateDefaultCheatFilename ( )
{
2014-01-01 02:16:47 +00:00
var pathEntry = Global . Config . PathEntries [ Global . Game . System , "Cheats" ]
2015-11-22 16:37:00 +00:00
? ? Global . Config . PathEntries [ Global . Game . System , "Base" ] ;
2013-12-23 03:07:06 +00:00
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" ) ;
2013-12-23 03:07:06 +00:00
}
2013-11-02 19:28:45 +00:00
}
}