using System; using System.Collections.Generic; using System.IO; using System.Linq; using BizHawk.Client.Common; namespace BizHawk.Client.EmuHawk { 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 readonly 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) { if (existingTool.IsDisposed) { _tools.Remove(existingTool); } else { existingTool.Show(); existingTool.Focus(); return existingTool; } } var result = Get(); result.Show(); return result; } /// /// Determines whether a given IToolForm is already loaded /// public bool IsLoaded() where T : IToolForm { var existingTool = _tools.FirstOrDefault(x => x is T); if (existingTool != null) { return !existingTool.IsDisposed; } return false; } /// /// 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) { if (existingTool.IsDisposed) { Close(); return CreateInstance(); } else { return existingTool; } } else { return CreateInstance(); } } public void UpdateBefore() { 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.UpdateValues(); } } } public void UpdateAfter() { 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.UpdateValues(); } } } /// /// Calls UpdateValues() on an instance of T, if it exists /// public void UpdateValues() where T : IToolForm { CloseIfDisposed(); var tool = _tools.FirstOrDefault(x => x is T); if (tool != null) { tool.UpdateValues(); } } public void Restart() { // If Cheat tool is loaded, restarting will restart the list too anyway if (!GlobalWin.Tools.Has()) { Global.CheatList.NewList(GenerateDefaultCheatFilename(), autosave: true); } _tools.ForEach(x => x.Restart()); } /// /// Calls Restart() on an instance of T, if it exists /// public void Restart() where T : IToolForm { CloseIfDisposed(); 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() { if (Global.Config.SupressAskSave) // User has elected to not be nagged { return true; } return _tools .Select(tool => tool.AskSave()) .All(result => result); } /// /// 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 { if (Global.Config.SupressAskSave) // User has elected to not be nagged { return true; } 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(); } private IToolForm CreateInstance() { 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); } private void CloseIfDisposed() where T : IToolForm { var existingTool = _tools.FirstOrDefault(x => x is T); if (existingTool != null && existingTool.IsDisposed) { Close(); } } public void UpdateToolsBefore(bool fromLua = false) { if (Has()) { if (!fromLua) { LuaConsole.StartLuaDrawing(); } } UpdateBefore(); } public void UpdateToolsAfter(bool fromLua = false) { if (!fromLua && Has()) { LuaConsole.ResumeScripts(true); } GlobalWin.Tools.UpdateAfter(); if (Has()) { if (!fromLua) { LuaConsole.EndLuaDrawing(); } } } 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(); } } } // 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) { if (tool.IsDisposed) { _tools.Remove(tool); } else { return tool as RamWatch; } } var newTool = new RamWatch(); _tools.Add(newTool); return newTool; } } public RamSearch RamSearch { get { var tool = _tools.FirstOrDefault(x => x is RamSearch); if (tool != null) { if (tool.IsDisposed) { _tools.Remove(tool); } else { return tool as RamSearch; } } var newTool = new RamSearch(); _tools.Add(newTool); return newTool; } } 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; } } 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; } } var newTool = new HexEditor(); _tools.Add(newTool); return newTool; } } public VirtualpadTool VirtualPad { get { var tool = _tools.FirstOrDefault(x => x is VirtualpadTool); if (tool != null) { if (tool.IsDisposed) { _tools.Remove(tool); } else { return tool as VirtualpadTool; } } var newTool = new VirtualpadTool(); _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; } } 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; } } #endregion #region Specialized Tool Loading Logic public void LoadRamWatch(bool loadDialog) { if (!IsLoaded() && Global.Config.RecentWatches.AutoLoad && !Global.Config.RecentWatches.Empty) { GlobalWin.Tools.RamWatch.LoadFileFromRecent(Global.Config.RecentWatches.MostRecent); } if (loadDialog) { GlobalWin.Tools.Load(); } } public void LoadTraceLogger() { if (Global.Emulator.CoreComm.CpuTraceAvailable) { Load(); } } public void LoadGameGenieEc() { if (Global.Emulator.SystemId == "NES") { Load(); } else if (Global.Emulator.SystemId == "SNES") { Load(); } else if ((Global.Emulator.SystemId == "GB") || (Global.Game.System == "GG")) { Load(); } else if (Global.Emulator.SystemId == "GEN" && VersionInfo.DeveloperBuild) { Load(); } } #endregion public static string GenerateDefaultCheatFilename() { var pathEntry = Global.Config.PathEntries[Global.Game.System, "Cheats"] ?? Global.Config.PathEntries[Global.Game.System, "Base"]; 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"); } } }