diff --git a/Core/Console.cpp b/Core/Console.cpp index 34e60547..3c41e2d8 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -360,9 +360,10 @@ void Console::ResetComponents(bool softReset) _controlManager->UpdateInputState(); } -void Console::Stop() +void Console::Stop(int stopCode) { _stop = true; + _stopCode = stopCode; shared_ptr debugger = _debugger; if(debugger) { @@ -510,6 +511,7 @@ void Console::Run() } } catch(const std::runtime_error &ex) { crashed = true; + _stopCode = -1; MessageManager::DisplayMessage("Error", "GameCrash", ex.what()); } @@ -726,6 +728,11 @@ void Console::SetNextFrameOverclockStatus(bool disabled) Instance->_disableOcNextFrame = disabled; } +int32_t Console::GetStopCode() +{ + return _stopCode; +} + HdPackData* Console::GetHdData() { return Instance->_hdData.get(); diff --git a/Core/Console.h b/Core/Console.h index d6b9f868..9f589c34 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -58,6 +58,7 @@ class Console bool _stop = false; bool _running = false; + int32_t _stopCode = 0; bool _disableOcNextFrame = false; @@ -77,7 +78,9 @@ class Console void SaveBatteries(); void Run(); - void Stop(); + void Stop(int stopCode = 0); + + int32_t GetStopCode(); void RunSingleFrame(); bool UpdateHdPackMode(); diff --git a/Core/LuaApi.cpp b/Core/LuaApi.cpp index 76593370..d0d4cfb6 100644 --- a/Core/LuaApi.cpp +++ b/Core/LuaApi.cpp @@ -81,6 +81,7 @@ int LuaApi::GetLibrary(lua_State *lua) { "log", LuaApi::Log }, { "displayMessage", LuaApi::DisplayMessage }, { "reset", LuaApi::Reset }, + { "stop", LuaApi::Stop }, { "breakExecution", LuaApi::Break }, { "resume", LuaApi::Resume }, { "execute", LuaApi::Execute }, @@ -464,6 +465,15 @@ int LuaApi::Reset(lua_State *lua) return l.ReturnCount(); } +int LuaApi::Stop(lua_State *lua) +{ + LuaCallHelper l(lua); + int32_t stopCode = l.ReadInteger(0); + checkminparams(0); + Console::GetInstance()->Stop(stopCode); + return l.ReturnCount(); +} + int LuaApi::Break(lua_State *lua) { LuaCallHelper l(lua); diff --git a/Core/LuaApi.h b/Core/LuaApi.h index c0af9f2d..c161283c 100644 --- a/Core/LuaApi.h +++ b/Core/LuaApi.h @@ -41,6 +41,7 @@ public: static int DisplayMessage(lua_State *lua); static int Reset(lua_State *lua); + static int Stop(lua_State *lua); static int Break(lua_State *lua); static int Resume(lua_State *lua); static int Execute(lua_State *lua); diff --git a/GUI.NET/CommandLineHelper.cs b/GUI.NET/CommandLineHelper.cs new file mode 100644 index 00000000..ed294c87 --- /dev/null +++ b/GUI.NET/CommandLineHelper.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI +{ + class CommandLineHelper + { + public static List PreprocessCommandLineArguments(string[] args, bool toLower) + { + var switches = new List(); + for(int i = 0; i < args.Length; i++) { + if(args[i] != null) { + string arg = args[i].Trim(); + if(arg.StartsWith("--")) { + arg = "/" + arg.Substring(2); + } else if(arg.StartsWith("-")) { + arg = "/" + arg.Substring(1); + } + + if(toLower) { + arg = arg.ToLowerInvariant(); + } + switches.Add(arg); + } + } + return switches; + } + + public static void GetRomPathFromCommandLine(List switches, out string romPath, out List luaScriptsToLoad) + { + Func getValidPath = (string path, bool forLua) => { + path = path.Trim(); + if(path.ToLower().EndsWith(".lua") == forLua) { + try { + if(!File.Exists(path)) { + //Try loading file as a relative path to the folder Mesen was started from + path = Path.Combine(Program.OriginalFolder, path); + } + if(File.Exists(path)) { + return path; + } + } catch { } + } + return null; + }; + + //Check if any Lua scripts were specified + luaScriptsToLoad = new List(); + foreach(string arg in switches) { + string path = getValidPath(arg, true); + if(path != null) { + luaScriptsToLoad.Add(path); + } + } + + romPath = null; + foreach(string arg in switches) { + string path = getValidPath(arg, false); + if(path != null) { + romPath = path; + break; + } + } + } + } +} diff --git a/GUI.NET/Debugger/DebugWindowManager.cs b/GUI.NET/Debugger/DebugWindowManager.cs index e457d36d..3befc132 100644 --- a/GUI.NET/Debugger/DebugWindowManager.cs +++ b/GUI.NET/Debugger/DebugWindowManager.cs @@ -101,7 +101,11 @@ namespace Mesen.GUI.Debugger if(_openedWindows.Count == 0) { //All windows have been closed, disable debugger DebugWorkspaceManager.Clear(); - InteropEmu.DebugRelease(); + + Task.Run(() => { + //Run this in another thread to avoid deadlocks when this is called within a notification handler + InteropEmu.DebugRelease(); + }); } } diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index b97c8074..fd24fae4 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -82,28 +82,7 @@ namespace Mesen.GUI.Forms this.Resize += ResizeRecentGames; this.FormClosed += (s, e) => Application.RemoveMessageFilter(this); } - - public List PreprocessCommandLineArguments(string[] args, bool toLower) - { - var switches = new List(); - for(int i = 0; i < args.Length; i++) { - if(args[i] != null) { - string arg = args[i].Trim(); - if(arg.StartsWith("--")) { - arg = "/" + arg.Substring(2); - } else if(arg.StartsWith("-")) { - arg = "/" + arg.Substring(1); - } - - if(toLower) { - arg = arg.ToLowerInvariant(); - } - switches.Add(arg); - } - } - return switches; - } - + public void ProcessCommandLineArguments(List switches, bool forStartup) { if(forStartup) { @@ -121,42 +100,13 @@ namespace Mesen.GUI.Forms public void LoadGameFromCommandLine(List switches) { - Func getValidPath = (string path, bool forLua) => { - path = path.Trim(); - if(path.ToLower().EndsWith(".lua") == forLua) { - try { - if(!File.Exists(path)) { - //Try loading file as a relative path to the folder Mesen was started from - path = Path.Combine(Program.OriginalFolder, path); - } - if(File.Exists(path)) { - return path; - } - } catch { } - } - return null; - }; + string romPath; + CommandLineHelper.GetRomPathFromCommandLine(switches, out romPath, out _luaScriptsToLoad); - //Check if any Lua scripts were specified - _luaScriptsToLoad = new List(); - foreach(string arg in switches) { - string path = getValidPath(arg, true); - if(path != null) { - _luaScriptsToLoad.Add(path); - } - } + if(romPath != null) { + this.LoadFile(romPath); + } else { - bool fileLoaded = false; - foreach(string arg in switches) { - string path = getValidPath(arg, false); - if(path != null) { - this.LoadFile(path); - fileLoaded = true; - break; - } - } - - if(!fileLoaded) { if(_emuThread == null) { //When no ROM is loaded, only process Lua scripts if a ROM was specified as a command line param _luaScriptsToLoad.Clear(); @@ -196,7 +146,7 @@ namespace Mesen.GUI.Forms InteropEmu.ScreenSize originalSize = InteropEmu.GetScreenSize(false); VideoInfo.ApplyConfig(); - this.ProcessCommandLineArguments(this.PreprocessCommandLineArguments(_commandLineArgs, true), true); + this.ProcessCommandLineArguments(CommandLineHelper.PreprocessCommandLineArguments(_commandLineArgs, true), true); VideoInfo.ApplyConfig(); InteropEmu.ScreenSize newSize = InteropEmu.GetScreenSize(false); if(originalSize.Width != newSize.Width || originalSize.Height != newSize.Height) { @@ -267,7 +217,7 @@ namespace Mesen.GUI.Forms this.Size = ConfigManager.Config.WindowSize.Value; } - this.LoadGameFromCommandLine(this.PreprocessCommandLineArguments(_commandLineArgs, false)); + this.LoadGameFromCommandLine(CommandLineHelper.PreprocessCommandLineArguments(_commandLineArgs, false)); this.menuStrip.VisibleChanged += new System.EventHandler(this.menuStrip_VisibleChanged); this.UpdateRendererLocation(); @@ -281,7 +231,7 @@ namespace Mesen.GUI.Forms //This is needed when DPI display settings is not set to 100% _enableResize = true; - ProcessFullscreenSwitch(this.PreprocessCommandLineArguments(_commandLineArgs, true)); + ProcessFullscreenSwitch(CommandLineHelper.PreprocessCommandLineArguments(_commandLineArgs, true)); } protected override void OnClosing(CancelEventArgs e) diff --git a/GUI.NET/GUI.NET.csproj b/GUI.NET/GUI.NET.csproj index c6d1dd3c..4ac5cbc7 100644 --- a/GUI.NET/GUI.NET.csproj +++ b/GUI.NET/GUI.NET.csproj @@ -238,6 +238,7 @@ + @@ -1166,6 +1167,7 @@ + diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 84789a46..8ac49267 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -27,6 +27,7 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void SetFullscreenMode([MarshalAs(UnmanagedType.I1)]bool fullscreen, IntPtr windowHandle, UInt32 monitorWidth, UInt32 monitorHeight); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsRunning(); + [DllImport(DLLPath)] public static extern Int32 GetStopCode(); [DllImport(DLLPath)] public static extern void LoadROM([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filename, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string patchFile); [DllImport(DLLPath)] public static extern void AddKnownGameFolder([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string folder); diff --git a/GUI.NET/Program.cs b/GUI.NET/Program.cs index d3d7f7f3..a42c53a0 100644 --- a/GUI.NET/Program.cs +++ b/GUI.NET/Program.cs @@ -129,6 +129,11 @@ namespace Mesen.GUI return; } + if(CommandLineHelper.PreprocessCommandLineArguments(args, true).Contains("/testrunner")) { + Environment.ExitCode = TestRunner.Run(args); + return; + } + using(SingleInstance singleInstance = new SingleInstance()) { if(singleInstance.FirstInstance || !ConfigManager.Config.PreferenceInfo.SingleInstance) { frmMain frmMain = new frmMain(args); @@ -137,8 +142,8 @@ namespace Mesen.GUI singleInstance.ArgumentsReceived += (object sender, ArgumentsReceivedEventArgs e) => { if(frmMain.IsHandleCreated) { frmMain.BeginInvoke((MethodInvoker)(() => { - frmMain.ProcessCommandLineArguments(frmMain.PreprocessCommandLineArguments(e.Args, true), false); - frmMain.LoadGameFromCommandLine(frmMain.PreprocessCommandLineArguments(e.Args, false)); + frmMain.ProcessCommandLineArguments(CommandLineHelper.PreprocessCommandLineArguments(e.Args, true), false); + frmMain.LoadGameFromCommandLine(CommandLineHelper.PreprocessCommandLineArguments(e.Args, false)); })); } }; diff --git a/GUI.NET/TestRunner.cs b/GUI.NET/TestRunner.cs new file mode 100644 index 00000000..4b9b0d23 --- /dev/null +++ b/GUI.NET/TestRunner.cs @@ -0,0 +1,72 @@ +using Mesen.GUI.Config; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Mesen.GUI +{ + internal class TestRunner + { + internal static int Run(string[] args) + { + ConfigManager.DoNotSaveSettings = true; + string romPath; + List luaScriptsToLoad; + CommandLineHelper.GetRomPathFromCommandLine(CommandLineHelper.PreprocessCommandLineArguments(args, false), out romPath, out luaScriptsToLoad); + if(romPath == null) { + //No rom specified + return -1; + } + + List lcArgs = CommandLineHelper.PreprocessCommandLineArguments(args, true); + + int timeout = 100; //100 seconds + string timeoutArg = lcArgs.Find(arg => arg.StartsWith("/timeout=")); + if(timeoutArg != null) { + int timeoutValue; + if(Int32.TryParse(timeoutArg.Substring(timeoutArg.IndexOf("=") + 1), out timeoutValue)) { + timeout = timeoutValue; + } + } + + ConfigManager.ProcessSwitches(lcArgs); + ConfigManager.Config.ApplyConfig(); + InteropEmu.SetFlag(EmulationFlags.ConsoleMode, true); + + InteropEmu.InitializeEmu(ConfigManager.HomeFolder, IntPtr.Zero, IntPtr.Zero, true, true, true); + + InteropEmu.LoadROM(romPath, string.Empty); + + foreach(string luaScript in luaScriptsToLoad) { + try { + string script = File.ReadAllText(luaScript); + InteropEmu.DebugLoadScript(luaScript, script); + } catch { } + } + + System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); + Task.Run(() => { + InteropEmu.Run(); + }); + + InteropEmu.SetFlag(EmulationFlags.ForceMaxSpeed, true); + + sw.Start(); + int result = -1; + while(sw.ElapsedMilliseconds < timeout * 1000) { + System.Threading.Thread.Sleep(100); + + if(!InteropEmu.IsRunning()) { + result = InteropEmu.GetStopCode(); + break; + } + } + + InteropEmu.Stop(); + InteropEmu.Release(); + return result; + } + } +} \ No newline at end of file diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index b4316ab4..20ad1d9b 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -132,6 +132,7 @@ namespace InteropEmu { DllExport bool __stdcall IsRunning() { return Console::IsRunning(); } + DllExport int32_t __stdcall GetStopCode() { return Console::GetInstance()->GetStopCode(); } DllExport void __stdcall LoadROM(char* filename, char* patchFile) { Console::LoadROM((string)filename, (string)patchFile); } DllExport void __stdcall AddKnownGameFolder(char* folder) { FolderUtilities::AddKnownGameFolder(folder); }