From 024e005884383c95cc7653726fd1d2f6d4fe5bbe Mon Sep 17 00:00:00 2001 From: adelikat Date: Sat, 2 Nov 2013 19:28:45 +0000 Subject: [PATCH] Multiclient - Created a ToolManager class to manage winform tools and simplify and unify basic functions such as UpdateValues and Restart. Also smartly creates and disposes the tools intelligently, which minimizes ram usage on start up. For instance, calls to UpdateValues() don't create an instance of the object. Also, the instance of ToolManager is a Global instance called Tools, as opposed to all the tools being public fields on Mainform. Also, created IToolForm interface that all tool dialogs must implement. Refactored Ram Watch to use this new system. --- .../BizHawk.MultiClient.csproj | 2 + BizHawk.MultiClient/GlobalWinF.cs | 1 + BizHawk.MultiClient/MainForm.Events.cs | 4 +- BizHawk.MultiClient/MainForm.cs | 101 ++++----- .../tools/HexEditor/HexEditor.cs | 6 +- BizHawk.MultiClient/tools/IToolForm.cs | 37 ++++ BizHawk.MultiClient/tools/ToolHelpers.cs | 2 +- BizHawk.MultiClient/tools/ToolManager.cs | 200 ++++++++++++++++++ BizHawk.MultiClient/tools/Watch/RamSearch.cs | 11 +- BizHawk.MultiClient/tools/Watch/RamWatch.cs | 4 +- 10 files changed, 301 insertions(+), 67 deletions(-) create mode 100644 BizHawk.MultiClient/tools/IToolForm.cs create mode 100644 BizHawk.MultiClient/tools/ToolManager.cs 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();