2013-11-02 19:28:45 +00:00
using System ;
using System.Collections.Generic ;
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-11-30 18:35:25 +00:00
using BizHawk.Emulation.Common.IEmulatorExtensions ;
2013-11-24 16:00:10 +00:00
using BizHawk.Client.Common ;
2014-12-17 18:17:16 +00:00
using BizHawk.Emulation.Common ;
2014-12-19 23:33:05 +00:00
using System.Windows.Forms ;
2013-11-24 16:00:10 +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
{
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>
/// Loads the tool dialog T, if it does not exist it will be created, if it is already open, it will be focused
/// </summary>
2014-12-19 22:21:25 +00:00
public T Load < T > ( bool focus = true ) where T : IToolForm
2013-11-02 19:28:45 +00:00
{
2014-12-19 22:21:25 +00:00
return ( T ) Load ( typeof ( T ) , focus ) ;
2014-12-14 02:15:19 +00:00
}
/// <summary>
/// Loads a tool dialog of type toolType if it does not exist it will be
/// created, if it is already open, it will be focused.
/// </summary>
2014-12-19 22:21:25 +00:00
public IToolForm Load ( Type toolType , bool focus = true )
2014-12-14 02:15:19 +00:00
{
if ( ! typeof ( IToolForm ) . IsAssignableFrom ( toolType ) )
throw new ArgumentException ( String . Format ( "Type {0} does not implement IToolForm." , toolType . Name ) ) ;
2014-12-17 18:17:16 +00:00
if ( ! ServiceInjector . IsAvailable ( Global . Emulator . ServiceProvider , toolType ) )
2014-12-14 02:15:19 +00:00
return null ;
var existingTool = _tools . FirstOrDefault ( x = > toolType . IsAssignableFrom ( x . GetType ( ) ) ) ;
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 ( ) ;
}
2014-12-14 02:15:19 +00:00
return existingTool ;
}
}
2013-11-02 20:13:53 +00:00
2014-12-14 02:15:19 +00:00
var newTool = CreateInstance ( toolType ) ;
2014-12-14 01:25:28 +00:00
2014-12-17 18:17:16 +00:00
ServiceInjector . UpdateServices ( Global . Emulator . ServiceProvider , newTool ) ;
2014-12-13 22:32:08 +00:00
2014-12-19 23:33:05 +00:00
ToolDialogSettings settings ;
if ( ! Global . Config . CommonToolSettings . TryGetValue ( toolType . ToString ( ) , out settings ) )
{
settings = new ToolDialogSettings ( ) ;
Global . Config . CommonToolSettings [ toolType . ToString ( ) ] = settings ;
}
2014-12-20 17:05:13 +00:00
if ( newTool is IToolFormAutoConfig )
AttachSettingHooks ( newTool as IToolFormAutoConfig , settings ) ;
2014-12-19 23:33:05 +00:00
newTool . Restart ( ) ;
2014-12-14 02:15:19 +00:00
newTool . Show ( ) ;
return newTool ;
2013-11-02 19:28:45 +00:00
}
2014-12-19 23:33:05 +00:00
public void AutoLoad ( )
{
foreach ( var typename in Global . Config . CommonToolSettings . Where ( kvp = > kvp . Value . AutoLoad ) . Select ( kvp = > kvp . Key ) )
{
// 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 ) ;
}
}
}
2014-12-20 17:05:13 +00:00
private static 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 ;
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 ;
break ;
}
}
if ( dest = = null )
{
var submenu = new ToolStripMenuItem ( "&Settings" ) ;
ms . Items . Add ( submenu ) ;
dest = submenu . DropDownItems ;
}
break ;
}
}
if ( dest = = null )
throw new InvalidOperationException ( "IToolFormAutoConfig must have menu to bind to!" ) ;
2014-12-19 23:33:05 +00:00
2014-12-20 17:05:13 +00:00
dest . Add ( "Save Window Position" ) ;
dest . Add ( "Stay on Top" ) ;
dest . Add ( "Float from Parent" ) ;
dest . Add ( "Auto Load" ) ;
2014-12-19 23:33:05 +00:00
2014-12-20 17:05:13 +00:00
( dest [ 0 ] as ToolStripMenuItem ) . Checked = settings . SaveWindowPosition ;
( dest [ 1 ] as ToolStripMenuItem ) . Checked = settings . TopMost ;
( dest [ 2 ] as ToolStripMenuItem ) . Checked = settings . FloatingWindow ;
( dest [ 3 ] as ToolStripMenuItem ) . Checked = settings . AutoLoad ;
2014-12-19 23:33:05 +00:00
form . TopMost = settings . TopMost ;
// do we need to do this OnShown() as well?
form . Owner = settings . FloatingWindow ? null : GlobalWin . MainForm ;
if ( settings . UseWindowPosition )
{
form . Location = settings . WindowPosition ;
}
if ( settings . UseWindowSize )
{
form . Size = settings . WindowSize ;
}
form . FormClosing + = ( o , e ) = >
{
settings . Wndx = form . Location . X ;
settings . Wndy = form . Location . Y ;
settings . Width = form . Right - form . Left ; // why not form.Size.Width?
settings . Height = form . Bottom - form . Top ;
} ;
2014-12-20 17:05:13 +00:00
dest [ 0 ] . Click + = ( o , e ) = >
2014-12-19 23:33:05 +00:00
{
bool val = ! ( o as ToolStripMenuItem ) . Checked ;
settings . SaveWindowPosition = val ;
( o as ToolStripMenuItem ) . Checked = val ;
} ;
2014-12-20 17:05:13 +00:00
dest [ 1 ] . Click + = ( o , e ) = >
2014-12-19 23:33:05 +00:00
{
bool val = ! ( o as ToolStripMenuItem ) . Checked ;
settings . TopMost = val ;
( o as ToolStripMenuItem ) . Checked = val ;
form . TopMost = val ;
} ;
2014-12-20 17:05:13 +00:00
dest [ 2 ] . Click + = ( o , e ) = >
2014-12-19 23:33:05 +00:00
{
bool val = ! ( o as ToolStripMenuItem ) . Checked ;
settings . FloatingWindow = val ;
( o as ToolStripMenuItem ) . Checked = val ;
form . Owner = val ? null : GlobalWin . MainForm ;
} ;
2014-12-20 17:05:13 +00:00
dest [ 3 ] . Click + = ( o , e ) = >
2014-12-19 23:33:05 +00:00
{
bool val = ! ( o as ToolStripMenuItem ) . Checked ;
settings . AutoLoad = val ;
( o as ToolStripMenuItem ) . Checked = val ;
} ;
}
2014-03-01 19:16:17 +00:00
/// <summary>
/// Determines whether a given IToolForm is already loaded
/// </summary>
public bool IsLoaded < T > ( ) where T : IToolForm
{
var existingTool = _tools . FirstOrDefault ( x = > x is T ) ;
if ( existingTool ! = null )
{
return ! existingTool . IsDisposed ;
}
return false ;
}
2013-11-02 19:28:45 +00:00
/// <summary>
/// Returns true if an instance of T exists
/// </summary>
public bool Has < T > ( ) where T : IToolForm
{
2014-09-21 14:58:03 +00:00
return _tools . Any ( x = > x is T & & ! x . IsDisposed ) ;
2013-11-02 19:28:45 +00:00
}
/// <summary>
/// Gets the instance of T, or creates and returns a new instance
/// </summary>
public IToolForm Get < T > ( ) where T : IToolForm
{
2014-12-19 22:21:25 +00:00
return Load < T > ( false ) ;
2013-11-02 19:28:45 +00:00
}
public void UpdateBefore ( )
{
var beforeList = _tools . Where ( x = > x . UpdateBefore ) ;
foreach ( var tool in beforeList )
{
2013-11-24 16:00:10 +00:00
if ( ! tool . IsDisposed | |
2014-01-01 02:16:47 +00:00
( 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
}
}
public void UpdateAfter ( )
{
var afterList = _tools . Where ( x = > ! x . UpdateBefore ) ;
2014-05-04 14:22:11 +00:00
foreach ( var tool in afterList )
2013-11-02 19:28:45 +00:00
{
2014-04-26 14:51:33 +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 ( ) ;
}
2013-11-02 19:28:45 +00:00
}
}
/// <summary>
/// Calls UpdateValues() on an instance of T, if it exists
/// </summary>
public void UpdateValues < T > ( ) where T : IToolForm
{
2013-11-07 20:33:29 +00:00
CloseIfDisposed < T > ( ) ;
2013-11-02 19:28:45 +00:00
var tool = _tools . FirstOrDefault ( x = > x is T ) ;
if ( tool ! = null )
{
tool . UpdateValues ( ) ;
}
}
public void Restart ( )
{
2013-12-23 03:07:06 +00:00
// If Cheat tool is loaded, restarting will restart the list too anyway
if ( ! GlobalWin . Tools . Has < Cheats > ( ) )
{
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 ) ;
2014-12-17 03:21:32 +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
{
tool . Restart ( ) ;
}
2014-12-13 22:32:08 +00:00
}
else
{
unavailable . Add ( tool ) ;
}
}
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>
public void Restart < T > ( ) where T : IToolForm
{
2013-11-07 20:33:29 +00:00
CloseIfDisposed < T > ( ) ;
2013-11-02 19:28:45 +00:00
var tool = _tools . FirstOrDefault ( x = > x is T ) ;
if ( tool ! = null )
{
tool . Restart ( ) ;
}
}
/// <summary>
/// Runs AskSave on every tool dialog, false is returned if any tool returns false
/// </summary>
public bool AskSave ( )
{
2014-01-01 02:09:03 +00:00
if ( Global . Config . SupressAskSave ) // User has elected to not be nagged
{
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>
public bool AskSave < T > ( ) where T : IToolForm
{
2014-01-01 02:09:03 +00:00
if ( Global . Config . SupressAskSave ) // User has elected to not be nagged
{
return true ;
}
2013-11-02 19:28:45 +00:00
var tool = _tools . FirstOrDefault ( x = > x is T ) ;
if ( tool ! = null )
{
2014-08-19 19:24:17 +00:00
return tool . AskSaveChanges ( ) ;
2013-11-02 19:28:45 +00:00
}
else
{
return false ;
}
}
/// <summary>
/// If T exists, this call will close the tool, and remove it from memory
/// </summary>
public void Close < T > ( ) where T : IToolForm
{
var tool = _tools . FirstOrDefault ( x = > x is T ) ;
if ( tool ! = null )
{
tool . Close ( ) ;
_tools . Remove ( tool ) ;
}
}
2014-12-14 02:15:19 +00:00
public void Close ( Type toolType )
{
var tool = _tools . FirstOrDefault ( x = > toolType . IsAssignableFrom ( x . GetType ( ) ) ) ;
if ( tool ! = null )
{
tool . Close ( ) ;
_tools . Remove ( tool ) ;
}
}
2013-11-02 19:28:45 +00:00
public void Close ( )
{
_tools . ForEach ( x = > x . Close ( ) ) ;
_tools . Clear ( ) ;
}
2013-11-07 20:33:29 +00:00
private IToolForm CreateInstance < T > ( )
2014-12-15 03:19:23 +00:00
where T : IToolForm
2013-11-07 20:33:29 +00:00
{
2014-12-15 03:19:23 +00:00
return CreateInstance ( typeof ( T ) ) ;
2013-11-07 20:33:29 +00:00
}
2014-12-14 02:15:19 +00:00
private IToolForm CreateInstance ( Type toolType )
{
var tool = ( IToolForm ) Activator . CreateInstance ( toolType ) ;
// Add to our list of tools
_tools . Add ( tool ) ;
return tool ;
}
2013-11-07 20:33:29 +00:00
private void CloseIfDisposed < T > ( ) where T : IToolForm
{
var existingTool = _tools . FirstOrDefault ( x = > x is T ) ;
if ( existingTool ! = null & & existingTool . IsDisposed )
{
Close < T > ( ) ;
}
}
2013-11-27 23:35:32 +00:00
public void UpdateToolsBefore ( bool fromLua = false )
{
if ( Has < LuaConsole > ( ) )
{
if ( ! fromLua )
{
LuaConsole . StartLuaDrawing ( ) ;
}
}
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 ) ;
}
GlobalWin . Tools . UpdateAfter ( ) ;
if ( Has < LuaConsole > ( ) )
{
if ( ! fromLua )
{
LuaConsole . EndLuaDrawing ( ) ;
}
}
}
2014-07-25 01:55:21 +00:00
public void FastUpdateBefore ( )
{
var beforeList = _tools . Where ( x = > x . UpdateBefore ) ;
foreach ( var tool in beforeList )
{
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 ( ) ;
}
}
}
public void FastUpdateAfter ( )
{
var afterList = _tools . Where ( x = > ! x . UpdateBefore ) ;
foreach ( var tool in afterList )
{
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 ( ) ;
}
}
}
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
public RamWatch RamWatch
{
get
{
var tool = _tools . FirstOrDefault ( x = > x is RamWatch ) ;
if ( tool ! = null )
{
2013-11-02 20:13:53 +00:00
if ( tool . IsDisposed )
{
_tools . Remove ( tool ) ;
}
else
{
return tool as RamWatch ;
}
2013-11-02 19:28:45 +00:00
}
2013-11-02 20:13:53 +00:00
2013-11-02 22:13:37 +00:00
var newTool = new RamWatch ( ) ;
_tools . Add ( newTool ) ;
return newTool ;
2013-11-02 20:13:53 +00:00
}
}
public RamSearch RamSearch
{
get
{
var tool = _tools . FirstOrDefault ( x = > x is RamSearch ) ;
if ( tool ! = null )
2013-11-02 19:28:45 +00:00
{
2013-11-02 20:13:53 +00:00
if ( tool . IsDisposed )
{
_tools . Remove ( tool ) ;
}
else
{
return tool as RamSearch ;
}
2013-11-02 19:28:45 +00:00
}
2013-11-02 20:13:53 +00:00
2013-11-02 22:13:37 +00:00
var newTool = new RamSearch ( ) ;
_tools . Add ( newTool ) ;
return newTool ;
2013-11-02 19:28:45 +00:00
}
}
2013-11-07 20:33:29 +00:00
public Cheats Cheats
{
get
{
var tool = _tools . FirstOrDefault ( x = > x 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
{
var tool = _tools . FirstOrDefault ( x = > x is HexEditor ) ;
if ( tool ! = null )
{
if ( tool . IsDisposed )
{
_tools . Remove ( tool ) ;
}
else
{
return tool as HexEditor ;
}
}
2013-11-02 22:13:37 +00:00
var newTool = new HexEditor ( ) ;
_tools . Add ( newTool ) ;
return newTool ;
2013-11-02 20:25:53 +00:00
}
}
2014-06-22 13:57:23 +00:00
public VirtualpadTool VirtualPad
2013-11-02 21:31:04 +00:00
{
get
{
2014-06-22 13:57:23 +00:00
var tool = _tools . FirstOrDefault ( x = > x is VirtualpadTool ) ;
2013-11-02 21:31:04 +00:00
if ( tool ! = null )
{
if ( tool . IsDisposed )
{
_tools . Remove ( tool ) ;
}
else
{
2014-06-22 13:57:23 +00:00
return tool as VirtualpadTool ;
2013-11-02 21:31:04 +00:00
}
}
2014-06-22 13:57:23 +00:00
var newTool = new VirtualpadTool ( ) ;
2013-11-02 22:13:37 +00:00
_tools . Add ( newTool ) ;
return newTool ;
}
}
public SNESGraphicsDebugger SNESGraphicsDebugger
{
get
{
var tool = _tools . FirstOrDefault ( x = > x 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
}
}
2013-11-03 01:02:17 +00:00
public LuaConsole LuaConsole
{
get
{
var tool = _tools . FirstOrDefault ( x = > x 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 ;
}
}
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 )
{
2014-11-30 18:35:25 +00:00
if ( Global . Emulator . HasMemoryDomains ( ) )
2014-03-01 19:16:17 +00:00
if ( ! IsLoaded < RamWatch > ( ) & & Global . Config . RecentWatches . AutoLoad & & ! Global . Config . RecentWatches . Empty )
2013-11-28 20:02:32 +00:00
{
2014-04-06 20:46:23 +00:00
GlobalWin . Tools . RamWatch . LoadFileFromRecent ( Global . Config . RecentWatches . MostRecent ) ;
2013-11-28 20:02:32 +00:00
}
2013-12-22 23:34:22 +00:00
2013-11-28 20:02:32 +00:00
if ( loadDialog )
{
GlobalWin . Tools . Load < RamWatch > ( ) ;
}
}
2013-12-22 23:34:22 +00:00
2014-12-14 02:15:19 +00:00
public void LoadTraceLogger ( )
{
if ( Global . Emulator . CpuTraceAvailable ( ) )
{
Load < TraceLogger > ( ) ;
}
}
2013-12-22 23:34:22 +00:00
public void LoadGameGenieEc ( )
{
if ( Global . Emulator . SystemId = = "NES" )
{
Load < NESGameGenie > ( ) ;
}
else if ( Global . Emulator . SystemId = = "SNES" )
{
Load < SNESGameGenie > ( ) ;
}
else if ( ( Global . Emulator . SystemId = = "GB" ) | | ( Global . Game . System = = "GG" ) )
{
Load < GBGameGenie > ( ) ;
}
2014-06-04 17:02:54 +00:00
else if ( Global . Emulator . SystemId = = "GEN" & & VersionInfo . DeveloperBuild )
2013-12-22 23:34:22 +00:00
{
Load < GenGameGenie > ( ) ;
}
}
#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" ]
? ? 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 ( ) ;
}
return Path . Combine ( path , PathManager . FilesystemSafeName ( Global . Game ) + ".cht" ) ;
}
2013-11-02 19:28:45 +00:00
}
}