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.

This commit is contained in:
adelikat 2013-11-02 19:28:45 +00:00
parent 1525f4a4bd
commit 024e005884
10 changed files with 301 additions and 67 deletions

View File

@ -443,6 +443,7 @@
<Compile Include="tools\HexEditor\MemoryViewer.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="tools\IToolForm.cs" />
<Compile Include="tools\Lua\Libraries\EmuLuaLibrary.Client.cs" />
<Compile Include="tools\Lua\Libraries\EmuLuaLibrary.Console.cs" />
<Compile Include="tools\Lua\Libraries\EmuLuaLibrary.cs" />
@ -605,6 +606,7 @@
<DependentUpon>TI83KeyPad.cs</DependentUpon>
</Compile>
<Compile Include="tools\ToolHelpers.cs" />
<Compile Include="tools\ToolManager.cs" />
<Compile Include="tools\TraceLogger.cs">
<SubType>Form</SubType>
</Compile>

View File

@ -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;

View File

@ -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<RamWatch>() as RamWatch).LoadWatchFile(new FileInfo(filePaths[0]), false);
}
else if (MovieImport.IsValidMovieExtension(Path.GetExtension(filePaths[0])))

View File

@ -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<string, Dictionary<string, string>> allbinds, Dictionary<string, Dictionary<string, BizHawk.Client.Common.Config.AnalogBind>> analogbinds)
static Controller BindToDefinition(ControllerDefinition def, Dictionary<string, Dictionary<string, string>> allbinds, Dictionary<string, Dictionary<string, Config.AnalogBind>> analogbinds)
{
var ret = new Controller(def);
Dictionary<string, string> 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<RamWatch>();
}
}

View File

@ -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<RamWatch>();
GlobalWinF.MainForm.Cheats_UpdateValues();
UpdateValues();
}

View File

@ -0,0 +1,37 @@
namespace BizHawk.MultiClient
{
public interface IToolForm
{
/// <summary>
/// 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
/// </summary>
void UpdateValues();
/// <summary>
/// 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
/// </summary>
void Restart();
/// <summary>
/// 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)
/// </summary>
/// <returns></returns>
bool AskSave();
/// <summary>
/// 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.
/// </summary>
bool UpdateBefore { get; }
//Necessary winform calls
bool Focus();
void Show();
void Close();
}
}

View File

@ -206,7 +206,7 @@ namespace BizHawk.MultiClient
public static void UpdateCheatRelatedTools()
{
GlobalWinF.MainForm.RamWatch1.UpdateValues();
GlobalWinF.Tools.UpdateValues<RamWatch>();
GlobalWinF.MainForm.HexEditor1.UpdateValues();
GlobalWinF.MainForm.Cheats_UpdateValues();
GlobalWinF.MainForm.RamSearch1.UpdateValues();

View File

@ -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<IToolForm> _tools = new List<IToolForm>();
/// <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>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IToolForm Load<T>() where T : IToolForm
{
var existingTool = _tools.FirstOrDefault(x => x is T);
if (existingTool != null)
{
existingTool.Show();
existingTool.Focus();
return existingTool;
}
else
{
var result = Get<T>();
result.Show();
return result;
}
}
/// <summary>
/// Returns true if an instance of T exists
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public bool Has<T>() where T : IToolForm
{
return _tools.Any(x => x is T);
}
/// <summary>
/// Gets the instance of T, or creates and returns a new instance
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IToolForm Get<T>() 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();
}
}
/// <summary>
/// Calls UpdateValues() on an instance of T, if it exists
/// </summary>
public void UpdateValues<T>() where T : IToolForm
{
var tool = _tools.FirstOrDefault(x => x is T);
if (tool != null)
{
tool.UpdateValues();
}
}
public void Restart()
{
_tools.ForEach(x => x.Restart());
}
/// <summary>
/// Calls Restart() on an instance of T, if it exists
/// </summary>
/// <typeparam name="T"></typeparam>
public void Restart<T>() where T : IToolForm
{
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>
/// <returns></returns>
public bool AskSave()
{
foreach (var tool in _tools)
{
var result = tool.AskSave();
if (!result)
{
return false;
}
}
return true;
}
/// <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>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public bool AskSave<T>() where T : IToolForm
{
var tool = _tools.FirstOrDefault(x => x is T);
if (tool != null)
{
return tool.AskSave();
}
else
{
return false;
}
}
/// <summary>
/// If T exists, this call will close the tool, and remove it from memory
/// </summary>
/// <typeparam name="T"></typeparam>
public void Close<T>() 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
}
}

View File

@ -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<RamWatch>())
{
Searches.RemoveRange(GlobalWinF.Tools.RamWatch.AddressList);
WatchListView.ItemCount = Searches.Count;
SetTotal();
}
}
private void UpdateUndoToolBarButtons()

View File

@ -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<string, int> DefaultColumnWidths = new Dictionary<string, int>
{
@ -30,6 +30,8 @@ namespace BizHawk.MultiClient
private string _sortedColumn = "";
private bool _sortReverse = false;
public bool UpdateBefore { get { return true; } }
public RamWatch()
{
InitializeComponent();