mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
244 lines
8.6 KiB
C#
244 lines
8.6 KiB
C#
using Mesen.Config;
|
|
using Mesen.Debugger.Utilities;
|
|
using Mesen.Debugger.ViewModels;
|
|
using Mesen.Debugger.Windows;
|
|
using Mesen.Interop;
|
|
using Mesen.Localization;
|
|
using Mesen.Utilities;
|
|
using Mesen.ViewModels;
|
|
using Mesen.Windows;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using System.Xml.Linq;
|
|
|
|
namespace Mesen.Utilities;
|
|
|
|
public class CommandLineHelper
|
|
{
|
|
public bool NoVideo { get; private set; }
|
|
public bool NoAudio { get; private set; }
|
|
public bool NoInput { get; private set; }
|
|
public bool Fullscreen { get; private set; }
|
|
public bool LoadLastSessionRequested { get; private set; }
|
|
public string? MovieToRecord { get; private set; } = null;
|
|
public int TestRunnerTimeout { get; private set; } = 100;
|
|
public List<string> LuaScriptsToLoad { get; private set; } = new();
|
|
public List<string> FilesToLoad { get; private set; } = new();
|
|
|
|
private List<string> _errorMessages = new();
|
|
|
|
public CommandLineHelper(string[] args, bool forStartup)
|
|
{
|
|
ProcessCommandLineArgs(args, forStartup);
|
|
}
|
|
|
|
private void ProcessCommandLineArgs(string[] args, bool forStartup)
|
|
{
|
|
foreach(string arg in args) {
|
|
string absPath;
|
|
if(Path.IsPathRooted(arg)) {
|
|
absPath = arg;
|
|
} else {
|
|
absPath = Path.GetFullPath(arg, Program.OriginalFolder);
|
|
}
|
|
|
|
if(File.Exists(absPath)) {
|
|
switch(Path.GetExtension(absPath).ToLowerInvariant()) {
|
|
case ".lua": LuaScriptsToLoad.Add(absPath); break;
|
|
default: FilesToLoad.Add(absPath); break;
|
|
}
|
|
} else if(arg.StartsWith("-") || arg.StartsWith("/")) {
|
|
string switchArg = ConvertArg(arg).ToLowerInvariant();
|
|
switch(switchArg) {
|
|
case "novideo": NoVideo = true; break;
|
|
case "noaudio": NoAudio = true; break;
|
|
case "noinput": NoInput = true; break;
|
|
case "fullscreen": Fullscreen = true; break;
|
|
case "donotsavesettings": ConfigManager.DisableSaveSettings = true; break;
|
|
case "loadlastsession": LoadLastSessionRequested = true; break;
|
|
default:
|
|
if(switchArg.StartsWith("recordmovie=")) {
|
|
string[] values = switchArg.Split('=');
|
|
if(values.Length <= 1) {
|
|
//invalid
|
|
continue;
|
|
}
|
|
string moviePath = values[1];
|
|
string? folder = Path.GetDirectoryName(moviePath);
|
|
if(string.IsNullOrWhiteSpace(folder)) {
|
|
moviePath = Path.Combine(ConfigManager.MovieFolder, moviePath);
|
|
} else if(!Path.IsPathRooted(moviePath)) {
|
|
moviePath = Path.Combine(Program.OriginalFolder, moviePath);
|
|
}
|
|
if(!moviePath.ToLower().EndsWith("." + FileDialogHelper.MesenMovieExt)) {
|
|
moviePath += "." + FileDialogHelper.MesenMovieExt;
|
|
}
|
|
MovieToRecord = moviePath;
|
|
} else if(switchArg.StartsWith("timeout=")) {
|
|
string[] values = switchArg.Split('=');
|
|
if(values.Length <= 1) {
|
|
//invalid
|
|
continue;
|
|
}
|
|
if(int.TryParse(values[1], out int timeout)) {
|
|
TestRunnerTimeout = timeout;
|
|
}
|
|
} else {
|
|
if(!ConfigManager.ProcessSwitch(switchArg)) {
|
|
_errorMessages.Add(ResourceHelper.GetMessage("InvalidArgument", arg));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
_errorMessages.Add(ResourceHelper.GetMessage("FileNotFound", arg));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnAfterInit(MainWindow wnd)
|
|
{
|
|
if(Fullscreen && FilesToLoad.Count == 0) {
|
|
wnd.ToggleFullscreen();
|
|
Fullscreen = false;
|
|
}
|
|
}
|
|
|
|
private static string ConvertArg(string arg)
|
|
{
|
|
arg = arg.Trim();
|
|
if(arg.StartsWith("--")) {
|
|
arg = arg.Substring(2);
|
|
} else if(arg.StartsWith("-") || arg.StartsWith("/")) {
|
|
arg = arg.Substring(1);
|
|
}
|
|
return arg;
|
|
}
|
|
|
|
public static bool IsTestRunner(string[] args)
|
|
{
|
|
return args.Any(arg => CommandLineHelper.ConvertArg(arg).ToLowerInvariant() == "testrunner");
|
|
}
|
|
|
|
public void ProcessPostLoadCommandSwitches(MainWindow wnd)
|
|
{
|
|
if(LuaScriptsToLoad.Count > 0) {
|
|
foreach(string luaScript in LuaScriptsToLoad) {
|
|
ScriptWindow? existingWnd = DebugWindowManager.GetDebugWindow<ScriptWindow>(wnd => !string.IsNullOrWhiteSpace(wnd.Model.FilePath) && Path.GetFullPath(wnd.Model.FilePath) == Path.GetFullPath(luaScript));
|
|
if(existingWnd != null) {
|
|
//Script is already opened, skip it
|
|
continue;
|
|
}
|
|
|
|
ScriptWindowViewModel model = new(null);
|
|
model.LoadScript(luaScript);
|
|
DebugWindowManager.OpenDebugWindow(() => new ScriptWindow(model));
|
|
}
|
|
|
|
//Shift focus back to main window after opening the script window(s)
|
|
wnd.BringToFront();
|
|
}
|
|
|
|
if(MovieToRecord != null) {
|
|
if(RecordApi.MovieRecording()) {
|
|
RecordApi.MovieStop();
|
|
}
|
|
RecordMovieOptions options = new RecordMovieOptions(MovieToRecord, "", "", RecordMovieFrom.StartWithSaveData);
|
|
RecordApi.MovieRecord(options);
|
|
}
|
|
|
|
if(Fullscreen) {
|
|
wnd.ToggleFullscreen();
|
|
}
|
|
|
|
if(LoadLastSessionRequested) {
|
|
Task.Run(() => {
|
|
EmuApi.ExecuteShortcut(new ExecuteShortcutParams() { Shortcut = Config.Shortcuts.EmulatorShortcut.LoadLastSession });
|
|
});
|
|
}
|
|
}
|
|
|
|
public void LoadFiles()
|
|
{
|
|
foreach(string file in FilesToLoad) {
|
|
LoadRomHelper.LoadFile(file);
|
|
}
|
|
|
|
foreach(string msg in _errorMessages) {
|
|
DisplayMessageHelper.DisplayMessage("Error", msg);
|
|
}
|
|
}
|
|
|
|
public static Dictionary<string, string> GetAvailableSwitches()
|
|
{
|
|
Dictionary<string, string> result = new();
|
|
|
|
string general = @"--fullscreen - Start in fullscreen mode
|
|
--doNotSaveSettings - Prevent settings from being saved to the disk (useful to prevent command line options from becoming the default settings)
|
|
--recordMovie=""filename.mmo"" - Start recording a movie after the specified game is loaded.
|
|
--loadLastSession - Resumes the game in the state it was left in when it was last played.";
|
|
|
|
result["General"] = general;
|
|
result["Audio"] = GetSwichesForObject("audio.", typeof(AudioConfig));
|
|
result["Emulation"] = GetSwichesForObject("emulation.", typeof(EmulationConfig));
|
|
result["Input"] = GetSwichesForObject("input.", typeof(InputConfig));
|
|
result["Video"] = GetSwichesForObject("video.", typeof(VideoConfig));
|
|
result["Preferences"] = GetSwichesForObject("preferences.", typeof(PreferencesConfig));
|
|
result["NES"] = GetSwichesForObject("nes.", typeof(NesConfig));
|
|
result["SNES"] = GetSwichesForObject("snes.", typeof(SnesConfig));
|
|
result["Game Boy"] = GetSwichesForObject("gameBoy.", typeof(GameboyConfig));
|
|
result["GBA"] = GetSwichesForObject("gba.", typeof(GbaConfig));
|
|
result["PC Engine"] = GetSwichesForObject("pcEngine.", typeof(PcEngineConfig));
|
|
result["SMS"] = GetSwichesForObject("sms.", typeof(SmsConfig));
|
|
result["WS"] = GetSwichesForObject("ws.", typeof(WsConfig));
|
|
result["CV"] = GetSwichesForObject("cv.", typeof(CvConfig));
|
|
|
|
return result;
|
|
}
|
|
|
|
private static string GetSwichesForObject(string prefix, Type type)
|
|
{
|
|
StringBuilder sb = new();
|
|
|
|
#pragma warning disable IL2070 // 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The parameter of method does not have matching annotations.
|
|
foreach(PropertyInfo info in type.GetProperties()) {
|
|
if(!info.CanWrite) {
|
|
continue;
|
|
}
|
|
|
|
string name = char.ToLowerInvariant(info.Name[0]) + info.Name.Substring(1);
|
|
if(info.PropertyType == typeof(int) || info.PropertyType == typeof(uint) || info.PropertyType == typeof(double)) {
|
|
MinMaxAttribute? minMaxAttribute = info.GetCustomAttribute(typeof(MinMaxAttribute)) as MinMaxAttribute;
|
|
if(minMaxAttribute != null) {
|
|
sb.AppendLine("--" + prefix + name + "=[" + minMaxAttribute.Min.ToString() + " - " + minMaxAttribute.Max.ToString() + "]");
|
|
}
|
|
} else if(info.PropertyType == typeof(bool)) {
|
|
sb.AppendLine("--" + prefix + name + "=[true | false]");
|
|
} else if(info.PropertyType.IsEnum) {
|
|
if(info.PropertyType != typeof(ControllerType)) {
|
|
ValidValuesAttribute? validValuesAttribute = info.GetCustomAttribute(typeof(ValidValuesAttribute)) as ValidValuesAttribute;
|
|
if(validValuesAttribute != null) {
|
|
sb.AppendLine("--" + prefix + name + "=[" + string.Join(" | ", validValuesAttribute.ValidValues.Select(v => Enum.GetName(info.PropertyType, v))) + "]");
|
|
} else {
|
|
sb.AppendLine("--" + prefix + name + "=[" + string.Join(" | ", Enum.GetNames(info.PropertyType)) + "]");
|
|
}
|
|
}
|
|
} else if(info.PropertyType.IsClass && !info.PropertyType.IsGenericType) {
|
|
string content = GetSwichesForObject(prefix + name + ".", info.PropertyType);
|
|
if(content.Length > 0) {
|
|
sb.Append(content);
|
|
}
|
|
}
|
|
}
|
|
#pragma warning restore IL2070 // 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The parameter of method does not have matching annotations.
|
|
|
|
return sb.ToString();
|
|
}
|
|
}
|