diff --git a/BizHawk.MultiClient/BizHawk.MultiClient.csproj b/BizHawk.MultiClient/BizHawk.MultiClient.csproj
index 882f27e5dc..47428f4f55 100644
--- a/BizHawk.MultiClient/BizHawk.MultiClient.csproj
+++ b/BizHawk.MultiClient/BizHawk.MultiClient.csproj
@@ -443,6 +443,7 @@
Component
+
@@ -605,6 +606,7 @@
TI83KeyPad.cs
+
Form
diff --git a/BizHawk.MultiClient/GlobalWinF.cs b/BizHawk.MultiClient/GlobalWinF.cs
index cb75010815..9d584d17f3 100644
--- a/BizHawk.MultiClient/GlobalWinF.cs
+++ b/BizHawk.MultiClient/GlobalWinF.cs
@@ -8,6 +8,7 @@ namespace BizHawk.MultiClient
public static class GlobalWinF
{
public static MainForm MainForm;
+ public static ToolManager Tools;
#if WINDOWS
public static DirectSound DSound;
public static Direct3D Direct3D;
diff --git a/BizHawk.MultiClient/MainForm.Events.cs b/BizHawk.MultiClient/MainForm.Events.cs
index 67731ae57d..5fec45ec28 100644
--- a/BizHawk.MultiClient/MainForm.Events.cs
+++ b/BizHawk.MultiClient/MainForm.Events.cs
@@ -504,7 +504,7 @@ namespace BizHawk.MultiClient
private void ExitMenuItem_Click(object sender, EventArgs e)
{
- if (RamWatch1.AskSave())
+ if (GlobalWinF.Tools.AskSave())
{
Close();
}
@@ -2179,7 +2179,7 @@ namespace BizHawk.MultiClient
else if (ext.ToUpper() == ".WCH")
{
LoadRamWatch(true);
- RamWatch1.LoadWatchFile(new FileInfo(filePaths[0]), false);
+ (GlobalWinF.Tools.Get() as RamWatch).LoadWatchFile(new FileInfo(filePaths[0]), false);
}
else if (MovieImport.IsValidMovieExtension(Path.GetExtension(filePaths[0])))
diff --git a/BizHawk.MultiClient/MainForm.cs b/BizHawk.MultiClient/MainForm.cs
index ed8df9da47..c5beeae170 100644
--- a/BizHawk.MultiClient/MainForm.cs
+++ b/BizHawk.MultiClient/MainForm.cs
@@ -86,36 +86,35 @@ namespace BizHawk.MultiClient
//tool dialogs
- private RamSearch _ramsearch = null;
+ private RamSearch _ramsearch;
- private HexEditor _hexeditor = null;
- private TraceLogger _tracelogger = null;
- private SNESGraphicsDebugger _snesgraphicsdebugger = null;
- private NESNameTableViewer _nesnametableview = null;
- private NESPPU _nesppu = null;
- private NESDebugger _nesdebugger = null;
- private GBtools.GBGPUView _gbgpuview = null;
- private GBAtools.GBAGPUView _gbagpuview = null;
- private PCEBGViewer _pcebgviewer = null;
- private Cheats _cheats = null;
- private ToolBox _toolbox = null;
- private TI83KeyPad _ti83pad = null;
- private TAStudio _tastudio = null;
- private VirtualPadForm _vpad = null;
- private NESGameGenie _ngg = null;
- private SNESGameGenie _sgg = null;
- private GBGameGenie _gbgg = null;
- private GenGameGenie _gengg = null;
- private NESSoundConfig _nessound = null;
- private RamWatch _ramwatch = null;
+ private HexEditor _hexeditor;
+ private TraceLogger _tracelogger;
+ private SNESGraphicsDebugger _snesgraphicsdebugger;
+ private NESNameTableViewer _nesnametableview;
+ private NESPPU _nesppu;
+ private NESDebugger _nesdebugger;
+ private GBtools.GBGPUView _gbgpuview;
+ private GBAtools.GBAGPUView _gbagpuview;
+ private PCEBGViewer _pcebgviewer;
+ private Cheats _cheats;
+ private ToolBox _toolbox;
+ private TI83KeyPad _ti83pad;
+ private TAStudio _tastudio;
+ private VirtualPadForm _vpad;
+ private NESGameGenie _ngg;
+ private SNESGameGenie _sgg;
+ private GBGameGenie _gbgg;
+ private GenGameGenie _gengg;
+ private NESSoundConfig _nessound;
//TODO: this is a lazy way to refactor things, but works for now. The point is to not have these objects created until needed, without refactoring a lot of code
public RamSearch RamSearch1 { get { if (_ramsearch == null) _ramsearch = new RamSearch(); return _ramsearch; } set { _ramsearch = value; } }
public HexEditor HexEditor1 { get { if (_hexeditor == null) _hexeditor = new HexEditor(); return _hexeditor; } set { _hexeditor = value; } }
public TraceLogger TraceLogger1 { get { if (_tracelogger == null) _tracelogger = new TraceLogger(); return _tracelogger; } set { _tracelogger = value; } }
public SNESGraphicsDebugger SNESGraphicsDebugger1 { get { if (_snesgraphicsdebugger == null) _snesgraphicsdebugger = new SNESGraphicsDebugger(); return _snesgraphicsdebugger; } set { _snesgraphicsdebugger = value; } }
- public NESNameTableViewer NESNameTableViewer1 { get { if (_nesnametableview == null) _nesnametableview = new NESNameTableViewer(); return _nesnametableview; } set { _nesnametableview = value; } }
- public NESPPU NESPPU1 { get { if (_nesppu == null) _nesppu = new NESPPU(); return _nesppu; } set { _nesppu = value; } }
+ public NESNameTableViewer NESNameTableViewer1 { get { return _nesnametableview ?? (_nesnametableview = new NESNameTableViewer()); } set { _nesnametableview = value; } }
+ public NESPPU NESPPU1 { get { return _nesppu ?? (_nesppu = new NESPPU()); } set { _nesppu = value; } }
public NESDebugger NESDebug1 { get { if (_nesdebugger == null) _nesdebugger = new NESDebugger(); return _nesdebugger; } set { _nesdebugger = value; } }
public GBtools.GBGPUView GBGPUView1 { get { if (_gbgpuview == null) _gbgpuview = new GBtools.GBGPUView(); return _gbgpuview; } set { _gbgpuview = value; } }
public GBAtools.GBAGPUView GBAGPUView1 { get { if (_gbagpuview == null) _gbagpuview = new GBAtools.GBAGPUView(); return _gbagpuview; } set { _gbagpuview = value; } }
@@ -130,8 +129,6 @@ namespace BizHawk.MultiClient
public GenGameGenie Gengg { get { if (_gengg == null) _gengg = new GenGameGenie(); return _gengg; } set { _gengg = value; } }
public NESSoundConfig NesSound { get { if (_nessound == null) _nessound = new NESSoundConfig(); return _nessound; } set { _nessound = value; } }
- public RamWatch RamWatch1 { get { if (_ramwatch == null) _ramwatch = new RamWatch(); return _ramwatch; } set { _ramwatch = value; } }
-
//TODO: eventually start doing this, rather than tools attempting to talk to tools
public void Cheats_UpdateValues() { if (_cheats != null) { _cheats.UpdateValues(); } }
public void Cheats_Restart()
@@ -229,7 +226,7 @@ namespace BizHawk.MultiClient
Global.CheatList.SaveOnClose();
CloseGame();
Global.MovieSession.Movie.Stop();
- CloseTools();
+ GlobalWinF.Tools.Close();
SaveConfig();
};
@@ -259,6 +256,7 @@ namespace BizHawk.MultiClient
#endif
GlobalWinF.Sound.StartSound();
RewireInputChain();
+ GlobalWinF.Tools = new ToolManager();
//TODO - replace this with some kind of standard dictionary-yielding parser in a separate component
string cmdRom = null;
string cmdLoadState = null;
@@ -351,14 +349,7 @@ namespace BizHawk.MultiClient
if (Global.Config.RecentWatches.AutoLoad)
{
- if (Global.Config.DisplayRamWatch)
- {
- LoadRamWatch(false);
- }
- else
- {
- LoadRamWatch(true);
- }
+ LoadRamWatch(!Global.Config.DisplayRamWatch);
}
if (Global.Config.RecentSearches.AutoLoad)
{
@@ -779,8 +770,8 @@ namespace BizHawk.MultiClient
private void InitControls()
{
var controls = new Controller(
- new ControllerDefinition()
- {
+ new ControllerDefinition
+ {
Name = "Emulator Frontend Controls",
BoolButtons = Global.Config.HotkeyBindings.Select(x => x.DisplayName).ToList()
});
@@ -984,7 +975,7 @@ namespace BizHawk.MultiClient
}
}
- static Controller BindToDefinition(ControllerDefinition def, Dictionary> allbinds, Dictionary> analogbinds)
+ static Controller BindToDefinition(ControllerDefinition def, Dictionary> allbinds, Dictionary> analogbinds)
{
var ret = new Controller(def);
Dictionary binds;
@@ -1389,7 +1380,7 @@ namespace BizHawk.MultiClient
else
{
string sgbromPath = Global.FirmwareManager.Request("SNES", "Rom_SGB");
- byte[] sgbrom = null;
+ byte[] sgbrom;
try
{
if (File.Exists(sgbromPath))
@@ -1514,7 +1505,7 @@ namespace BizHawk.MultiClient
if (INTERIM)
{
string gbabiospath = Global.FirmwareManager.Request("GBA", "Bios");
- byte[] gbabios = null;
+ byte[] gbabios;
if (File.Exists(gbabiospath))
{
@@ -1616,8 +1607,11 @@ namespace BizHawk.MultiClient
// throttle.SetCoreFps(Global.Emulator.CoreComm.VsyncRate);
// SyncThrottle();
//}
+
+ GlobalWinF.Tools.Restart();
+
if (_ramsearch != null) RamSearch1.Restart();
- if (_ramwatch != null) RamWatch1.Restart();
+
if (_hexeditor != null) HexEditor1.Restart();
if (_nesppu != null) NESPPU1.Restart();
if (_nesnametableview != null) NESNameTableViewer1.Restart();
@@ -2350,6 +2344,9 @@ namespace BizHawk.MultiClient
LuaConsole1.LuaImp.CallFrameBeforeEvent();
}
#endif
+
+ GlobalWinF.Tools.UpdateBefore();
+
if (_nesnametableview != null) NESNameTableViewer1.UpdateValues();
if (_nesppu != null) NESPPU1.UpdateValues();
if (_pcebgviewer != null) PCEBGViewer1.UpdateValues();
@@ -2374,7 +2371,7 @@ namespace BizHawk.MultiClient
}
#endif
- if (_ramwatch != null) RamWatch1.UpdateValues();
+ GlobalWinF.Tools.UpdateAfter();
if (_ramsearch != null) RamSearch1.UpdateValues();
if (_hexeditor != null) HexEditor1.UpdateValues();
//The other tool updates are earlier, TAStudio needs to be later so it can display the latest
@@ -3208,11 +3205,12 @@ namespace BizHawk.MultiClient
CoreFileProvider.SyncCoreCommInputSignals();
Global.Emulator = new NullEmulator(Global.CoreComm);
Global.Game = GameInfo.GetNullGame();
-
+
+ GlobalWinF.Tools.Restart();
+
RewireSound();
ResetRewindBuffer();
RamSearch1.Restart();
- RamWatch1.Restart();
HexEditor1.Restart();
NESPPU1.Restart();
NESNameTableViewer1.Restart();
@@ -3253,7 +3251,6 @@ namespace BizHawk.MultiClient
public void CloseTools()
{
- CloseForm(RamWatch1);
CloseForm(RamSearch1);
CloseForm(HexEditor1);
CloseForm(NESNameTableViewer1);
@@ -4146,21 +4143,13 @@ namespace BizHawk.MultiClient
public void LoadRamWatch(bool load_dialog)
{
- if (!RamWatch1.IsHandleCreated || RamWatch1.IsDisposed)
+ if (Global.Config.RecentWatches.AutoLoad && !Global.Config.RecentWatches.Empty)
{
- RamWatch1 = new RamWatch();
- if (Global.Config.RecentWatches.AutoLoad && !Global.Config.RecentWatches.Empty)
- {
- RamWatch1.LoadFileFromRecent(Global.Config.RecentWatches[0]);
- }
- if (load_dialog)
- {
- RamWatch1.Show();
- }
+ GlobalWinF.Tools.RamWatch.LoadFileFromRecent(Global.Config.RecentWatches[0]);
}
- else
+ if (load_dialog)
{
- RamWatch1.Focus();
+ GlobalWinF.Tools.Load();
}
}
diff --git a/BizHawk.MultiClient/tools/HexEditor/HexEditor.cs b/BizHawk.MultiClient/tools/HexEditor/HexEditor.cs
index 491ea8d133..62fdbc771e 100644
--- a/BizHawk.MultiClient/tools/HexEditor/HexEditor.cs
+++ b/BizHawk.MultiClient/tools/HexEditor/HexEditor.cs
@@ -712,11 +712,11 @@ namespace BizHawk.MultiClient
if (HighlightedAddress.HasValue)
{
- GlobalWinF.MainForm.RamWatch1.AddWatch(MakeWatch(HighlightedAddress.Value));
+ GlobalWinF.Tools.RamWatch.AddWatch(MakeWatch(HighlightedAddress.Value));
}
foreach (int i in SecondaryHighlightedAddresses)
{
- GlobalWinF.MainForm.RamWatch1.AddWatch(MakeWatch(i));
+ GlobalWinF.Tools.RamWatch.AddWatch(MakeWatch(i));
}
}
@@ -897,7 +897,7 @@ namespace BizHawk.MultiClient
{
GlobalWinF.MainForm.UpdateCheatStatus();
GlobalWinF.MainForm.RamSearch1.UpdateValues();
- GlobalWinF.MainForm.RamWatch1.UpdateValues();
+ GlobalWinF.Tools.UpdateValues();
GlobalWinF.MainForm.Cheats_UpdateValues();
UpdateValues();
}
diff --git a/BizHawk.MultiClient/tools/IToolForm.cs b/BizHawk.MultiClient/tools/IToolForm.cs
new file mode 100644
index 0000000000..e8af01f8e9
--- /dev/null
+++ b/BizHawk.MultiClient/tools/IToolForm.cs
@@ -0,0 +1,37 @@
+namespace BizHawk.MultiClient
+{
+ public interface IToolForm
+ {
+ ///
+ /// Will be called by the client anytime an Update needs to occur, such as after an emulated frame, a loadstate, or a related dialog has made a relevant change
+ ///
+ void UpdateValues();
+
+ ///
+ /// Will be called anytime the dialog needs to be restarted, such as when a new ROM is loaded
+ /// The tool implementing this needs to account for a Game and Core change
+ ///
+ void Restart();
+
+ ///
+ /// This gives the opportunity for the tool dialog to ask the user to save changes (such is necessary when
+ /// This tool dialog edits a file. Returning false will tell the client the user wants to cancel the given action,
+ /// Return false to tell the client to back out of an action (such as closing the emulator)
+ ///
+ ///
+ bool AskSave();
+
+
+ ///
+ /// Indicates whether the tool should be updated before a frame loop or after.
+ /// In general, tools that draw graphics from the core should update before the loop,
+ /// Information tools such as those that display core ram values should be after.
+ ///
+ bool UpdateBefore { get; }
+
+ //Necessary winform calls
+ bool Focus();
+ void Show();
+ void Close();
+ }
+}
diff --git a/BizHawk.MultiClient/tools/ToolHelpers.cs b/BizHawk.MultiClient/tools/ToolHelpers.cs
index 903c8ab1c7..c3df6e2d91 100644
--- a/BizHawk.MultiClient/tools/ToolHelpers.cs
+++ b/BizHawk.MultiClient/tools/ToolHelpers.cs
@@ -206,7 +206,7 @@ namespace BizHawk.MultiClient
public static void UpdateCheatRelatedTools()
{
- GlobalWinF.MainForm.RamWatch1.UpdateValues();
+ GlobalWinF.Tools.UpdateValues();
GlobalWinF.MainForm.HexEditor1.UpdateValues();
GlobalWinF.MainForm.Cheats_UpdateValues();
GlobalWinF.MainForm.RamSearch1.UpdateValues();
diff --git a/BizHawk.MultiClient/tools/ToolManager.cs b/BizHawk.MultiClient/tools/ToolManager.cs
new file mode 100644
index 0000000000..70db529b4e
--- /dev/null
+++ b/BizHawk.MultiClient/tools/ToolManager.cs
@@ -0,0 +1,200 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BizHawk.MultiClient
+{
+ public class ToolManager
+ {
+ //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 List _tools = new List();
+
+ ///
+ /// Loads the tool dialog T, if it does not exist it will be created, if it is already open, it will be focused
+ ///
+ ///
+ ///
+ public IToolForm Load() where T : IToolForm
+ {
+ var existingTool = _tools.FirstOrDefault(x => x is T);
+ if (existingTool != null)
+ {
+ existingTool.Show();
+ existingTool.Focus();
+ return existingTool;
+ }
+ else
+ {
+ var result = Get();
+ result.Show();
+ return result;
+ }
+ }
+
+ ///
+ /// Returns true if an instance of T exists
+ ///
+ ///
+ ///
+ public bool Has() where T : IToolForm
+ {
+ return _tools.Any(x => x is T);
+ }
+
+ ///
+ /// Gets the instance of T, or creates and returns a new instance
+ ///
+ ///
+ ///
+ public IToolForm Get() where T : IToolForm
+ {
+ var existingTool = _tools.FirstOrDefault(x => x is T);
+ if (existingTool != null)
+ {
+ return existingTool;
+ }
+ else
+ {
+ var tool = Activator.CreateInstance(typeof(T));
+
+ //Add to the list and extract it, so it will be strongly typed as T
+ _tools.Add(tool as IToolForm);
+ return _tools.FirstOrDefault(x => x is T);
+ }
+
+ }
+
+ public void UpdateBefore()
+ {
+ var beforeList = _tools.Where(x => x.UpdateBefore);
+ foreach (var tool in beforeList)
+ {
+ tool.UpdateValues();
+ }
+ }
+
+ public void UpdateAfter()
+ {
+ var afterList = _tools.Where(x => !x.UpdateBefore);
+ foreach (var tool in afterList)
+ {
+ tool.UpdateValues();
+ }
+ }
+
+ ///
+ /// Calls UpdateValues() on an instance of T, if it exists
+ ///
+ public void UpdateValues() where T : IToolForm
+ {
+ var tool = _tools.FirstOrDefault(x => x is T);
+ if (tool != null)
+ {
+ tool.UpdateValues();
+ }
+ }
+
+ public void Restart()
+ {
+ _tools.ForEach(x => x.Restart());
+ }
+
+ ///
+ /// Calls Restart() on an instance of T, if it exists
+ ///
+ ///
+ public void Restart() where T : IToolForm
+ {
+ var tool = _tools.FirstOrDefault(x => x is T);
+ if (tool != null)
+ {
+ tool.Restart();
+ }
+ }
+
+ ///
+ /// Runs AskSave on every tool dialog, false is returned if any tool returns false
+ ///
+ ///
+ public bool AskSave()
+ {
+ foreach (var tool in _tools)
+ {
+ var result = tool.AskSave();
+ if (!result)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// 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
+ ///
+ ///
+ ///
+ public bool AskSave() where T : IToolForm
+ {
+ var tool = _tools.FirstOrDefault(x => x is T);
+ if (tool != null)
+ {
+ return tool.AskSave();
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// If T exists, this call will close the tool, and remove it from memory
+ ///
+ ///
+ public void Close() where T : IToolForm
+ {
+ var tool = _tools.FirstOrDefault(x => x is T);
+ if (tool != null)
+ {
+ tool.Close();
+ _tools.Remove(tool);
+ }
+ }
+
+ public void Close()
+ {
+ _tools.ForEach(x => x.Close());
+ _tools.Clear();
+ }
+
+ //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
+ {
+ var tool = _tools.FirstOrDefault(x => x is RamWatch);
+ if (tool != null)
+ {
+ return tool as RamWatch;
+ }
+ else
+ {
+ var ramWatch = new RamWatch();
+ _tools.Add(ramWatch);
+ return ramWatch;
+ }
+ }
+ }
+
+
+ #endregion
+ }
+}
diff --git a/BizHawk.MultiClient/tools/Watch/RamSearch.cs b/BizHawk.MultiClient/tools/Watch/RamSearch.cs
index 335e5c5ae0..b4c9cddf8a 100644
--- a/BizHawk.MultiClient/tools/Watch/RamSearch.cs
+++ b/BizHawk.MultiClient/tools/Watch/RamSearch.cs
@@ -816,7 +816,7 @@ namespace BizHawk.MultiClient
GlobalWinF.MainForm.LoadRamWatch(true);
for (int x = 0; x < SelectedIndices.Count; x++)
{
- GlobalWinF.MainForm.RamWatch1.AddWatch(Searches[SelectedIndices[x]]);
+ GlobalWinF.Tools.RamWatch.AddWatch(Searches[SelectedIndices[x]]);
}
if (Global.Config.RamSearchAlwaysExcludeRamWatch)
@@ -854,9 +854,12 @@ namespace BizHawk.MultiClient
private void RemoveRamWatchesFromList()
{
- Searches.RemoveRange(GlobalWinF.MainForm.RamWatch1.AddressList);
- WatchListView.ItemCount = Searches.Count;
- SetTotal();
+ if (GlobalWinF.Tools.Has())
+ {
+ Searches.RemoveRange(GlobalWinF.Tools.RamWatch.AddressList);
+ WatchListView.ItemCount = Searches.Count;
+ SetTotal();
+ }
}
private void UpdateUndoToolBarButtons()
diff --git a/BizHawk.MultiClient/tools/Watch/RamWatch.cs b/BizHawk.MultiClient/tools/Watch/RamWatch.cs
index 2cab1aa448..8240d8995b 100644
--- a/BizHawk.MultiClient/tools/Watch/RamWatch.cs
+++ b/BizHawk.MultiClient/tools/Watch/RamWatch.cs
@@ -11,7 +11,7 @@ using BizHawk.Client.Common;
namespace BizHawk.MultiClient
{
- public partial class RamWatch : Form
+ public partial class RamWatch : Form, IToolForm
{
private readonly Dictionary DefaultColumnWidths = new Dictionary
{
@@ -30,6 +30,8 @@ namespace BizHawk.MultiClient
private string _sortedColumn = "";
private bool _sortReverse = false;
+ public bool UpdateBefore { get { return true; } }
+
public RamWatch()
{
InitializeComponent();