Skip to content

Commit

Permalink
UI: Display error messages when command line arguments/files are inva…
Browse files Browse the repository at this point in the history
…lid/not found
  • Loading branch information
SourMesen committed Dec 17, 2023
1 parent f66ebfa commit 4b81e8b
Show file tree
Hide file tree
Showing 13 changed files with 120 additions and 35 deletions.
6 changes: 3 additions & 3 deletions UI/Config/ConfigAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ public MinMaxAttribute(object min, object max)

public class ValidValuesAttribute : Attribute
{
public uint[] ValidValues { get; set; }
public Enum[] ValidValues { get; set; }

public ValidValuesAttribute(params uint[] validValues)
public ValidValuesAttribute(params object[] validValues)
{
this.ValidValues = validValues;
this.ValidValues = validValues.Cast<Enum>().ToArray();
}
}
}
47 changes: 30 additions & 17 deletions UI/Config/ConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,12 @@ public static void LoadConfig()

public static MesenTheme ActiveTheme { get; private set; }

private static void ApplySetting(object instance, PropertyInfo property, string value)
private static bool ApplySetting(object instance, PropertyInfo property, string value)
{
Type t = property.PropertyType;
try {
if(!property.CanWrite) {
return;
return false;
}

if(t == typeof(int) || t == typeof(uint) || t == typeof(double)) {
Expand All @@ -88,45 +88,56 @@ private static void ApplySetting(object instance, PropertyInfo property, string
if(int.TryParse(value, out int result)) {
if(result >= (int)minMaxAttribute.Min && result <= (int)minMaxAttribute.Max) {
property.SetValue(instance, result);
} else {
return false;
}
}
} else if(t == typeof(uint)) {
if(uint.TryParse(value, out uint result)) {
if(result >= (uint)(int)minMaxAttribute.Min && result <= (uint)(int)minMaxAttribute.Max) {
property.SetValue(instance, result);
} else {
return false;
}
}
} else if(t == typeof(double)) {
if(double.TryParse(value, out double result)) {
if(result >= (double)minMaxAttribute.Min && result <= (double)minMaxAttribute.Max) {
property.SetValue(instance, result);
}
}
}
} else {
if(property.GetCustomAttribute<ValidValuesAttribute>() is ValidValuesAttribute validValuesAttribute) {
if(uint.TryParse(value, out uint result)) {
if(validValuesAttribute.ValidValues.Contains(result)) {
property.SetValue(instance, result);
} else {
return false;
}
}
}
}
} else if(t == typeof(bool)) {
if(bool.TryParse(value, out bool boolValue)) {
property.SetValue(instance, boolValue);
} else {
return false;
}
} else if(t.IsEnum) {
int indexOf = Enum.GetNames(t).Select((enumValue) => enumValue.ToLower()).ToList().IndexOf(value.ToLower());
if(indexOf >= 0) {
property.SetValue(instance, indexOf);
if(Enum.TryParse(t, value, true, out object? enumValue)) {
if(property.GetCustomAttribute<ValidValuesAttribute>() is ValidValuesAttribute validValuesAttribute) {
if(validValuesAttribute.ValidValues.Contains(enumValue)) {
property.SetValue(instance, enumValue);
} else {
return false;
}
} else {
property.SetValue(instance, enumValue);
}
} else {
return false;
}
}
} catch {
return false;
}
return true;
}

public static void ProcessSwitch(string switchArg)
public static bool ProcessSwitch(string switchArg)
{
Regex regex = new Regex("([a-z0-9_A-Z.]+)=([a-z0-9_A-Z.\\-]+)");
Match match = regex.Match(switchArg);
Expand All @@ -140,20 +151,22 @@ public static void ProcessSwitch(string switchArg)
property = cfg.GetType().GetProperty(switchPath[i], BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if(property == null) {
//Invalid switch name
return;
return false;
}

if(i < switchPath.Length - 1) {
cfg = property.GetValue(cfg);
if(cfg == null) {
//Invalid
return;
return false;
}
} else {
ApplySetting(cfg, property, switchValue);
return ApplySetting(cfg, property, switchValue);
}
}
}

return false;
}

public static void ResetHomeFolder()
Expand Down
2 changes: 2 additions & 0 deletions UI/Config/NesConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ public class NesConfig : BaseConfig<NesConfig>
[Reactive] public bool AutoConfigureInput { get; set; } = true;

//General
[ValidValues(ConsoleRegion.Auto, ConsoleRegion.Ntsc, ConsoleRegion.Pal, ConsoleRegion.Dendy)]
[Reactive] public ConsoleRegion Region { get; set; } = ConsoleRegion.Auto;

[Reactive] public bool EnableHdPacks { get; set; } = true;
[Reactive] public bool DisableGameDatabase { get; set; } = false;
[Reactive] public bool FdsAutoLoadDisk { get; set; } = true;
Expand Down
4 changes: 4 additions & 0 deletions UI/Config/SmsConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ public class SmsConfig : BaseConfig<SmsConfig>
[Reactive] public SmsControllerConfig Port1 { get; set; } = new();
[Reactive] public SmsControllerConfig Port2 { get; set; } = new();

[ValidValues(ConsoleRegion.Auto, ConsoleRegion.Ntsc, ConsoleRegion.Pal)]
[Reactive] public ConsoleRegion Region { get; set; } = ConsoleRegion.Auto;

[ValidValues(ConsoleRegion.Auto, ConsoleRegion.Ntsc, ConsoleRegion.NtscJapan, ConsoleRegion.Pal)]
[Reactive] public ConsoleRegion GameGearRegion { get; set; } = ConsoleRegion.Auto;

[Reactive] public RamState RamPowerOnState { get; set; } = RamState.Random;

[Reactive] public SmsRevision Revision { get; set; } = SmsRevision.Compatibility;
Expand Down
1 change: 1 addition & 0 deletions UI/Config/SnesConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class SnesConfig : BaseConfig<SnesConfig>
[Reactive] public SnesControllerConfig Port2C { get; set; } = new SnesControllerConfig();
[Reactive] public SnesControllerConfig Port2D { get; set; } = new SnesControllerConfig();

[ValidValues(ConsoleRegion.Auto, ConsoleRegion.Ntsc, ConsoleRegion.Pal)]
[Reactive] public ConsoleRegion Region { get; set; } = ConsoleRegion.Auto;

//Video
Expand Down
1 change: 1 addition & 0 deletions UI/Localization/resources.en.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1586,6 +1586,7 @@ E
<Message ID="ErrorWhileCheckingUpdates">An error has occurred while trying to check for updates. Check your internet connection and try again.&#xA;&#xA;Error details:&#xA;{0}</Message>
<Message ID="AutoUpdateDisabledMessage">Automatic updates are not enabled on this build - please download the latest version of the code and recompile Mesen to get the latest updates.</Message>
<Message ID="FileNotFound">File not found: {0}</Message>
<Message ID="InvalidArgument">Invalid argument: {0}</Message>

<Message ID="MesenUpToDate">You are running the latest version of Mesen.</Message>
<Message ID="PatchAndReset">Patch and reset the current game?</Message>
Expand Down
28 changes: 20 additions & 8 deletions UI/Utilities/CommandLineHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Mesen.Debugger.ViewModels;
using Mesen.Debugger.Windows;
using Mesen.Interop;
using Mesen.Localization;
using Mesen.Utilities;
using Mesen.ViewModels;
using Mesen.Windows;
Expand Down Expand Up @@ -31,6 +32,8 @@ public class CommandLineHelper
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);
Expand All @@ -51,7 +54,7 @@ private void ProcessCommandLineArgs(string[] args, bool forStartup)
case ".lua": LuaScriptsToLoad.Add(absPath); break;
default: FilesToLoad.Add(absPath); break;
}
} else {
} else if(arg.StartsWith("-") || arg.StartsWith("/")) {
string switchArg = ConvertArg(arg).ToLowerInvariant();
switch(switchArg) {
case "novideo": NoVideo = true; break;
Expand Down Expand Up @@ -88,10 +91,14 @@ private void ProcessCommandLineArgs(string[] args, bool forStartup)
TestRunnerTimeout = timeout;
}
} else {
ConfigManager.ProcessSwitch(switchArg);
if(!ConfigManager.ProcessSwitch(switchArg)) {
_errorMessages.Add(ResourceHelper.GetMessage("InvalidArgument", arg));
}
}
break;
}
} else {
_errorMessages.Add(ResourceHelper.GetMessage("FileNotFound", arg));
}
}
}
Expand Down Expand Up @@ -154,6 +161,10 @@ 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()
Expand All @@ -175,6 +186,7 @@ public static Dictionary<string, string> GetAvailableSwitches()
result["Snes"] = GetSwichesForObject("snes.", typeof(SnesConfig));
result["Game Boy"] = GetSwichesForObject("gameBoy.", typeof(GameboyConfig));
result["PC Engine"] = GetSwichesForObject("pcEngine.", typeof(PcEngineConfig));
result["SMS"] = GetSwichesForObject("sms.", typeof(SmsConfig));

return result;
}
Expand All @@ -193,17 +205,17 @@ private static string GetSwichesForObject(string prefix, Type type)
MinMaxAttribute? minMaxAttribute = info.GetCustomAttribute(typeof(MinMaxAttribute)) as MinMaxAttribute;
if(minMaxAttribute != null) {
sb.AppendLine("--" + prefix + name + "=[" + minMaxAttribute.Min.ToString() + " - " + minMaxAttribute.Max.ToString() + "]");
} else {
ValidValuesAttribute? validValuesAttribute = info.GetCustomAttribute(typeof(ValidValuesAttribute)) as ValidValuesAttribute;
if(validValuesAttribute != null) {
sb.AppendLine("--" + prefix + name + "=[" + string.Join(" | ", validValuesAttribute.ValidValues) + "]");
}
}
} else if(info.PropertyType == typeof(bool)) {
sb.AppendLine("--" + prefix + name + "=[true | false]");
} else if(info.PropertyType.IsEnum) {
if(info.PropertyType != typeof(ControllerType)) {
sb.AppendLine("--" + prefix + name + "=[" + string.Join(" | ", Enum.GetNames(info.PropertyType)) + "]");
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);
Expand Down
44 changes: 44 additions & 0 deletions UI/Utilities/DisplayMessageHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Avalonia.Threading;
using Mesen.Config;
using Mesen.Interop;
using Mesen.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Mesen.Utilities;

public class DisplayMessageHelper
{
private static int _taskId = 0;

public static void DisplayMessage(string title, string message, string? param1 = null)
{
if(EmuApi.IsRunning() || ConfigManager.Config.Preferences.GameSelectionScreenMode == GameSelectionMode.Disabled) {
EmuApi.DisplayMessage(title, message, param1);
} else {
//Temporarily hide selection screen to allow displaying messages
MainWindowViewModel.Instance.RecentGames.Visible = false;

EmuApi.DisplayMessage(title, message, param1);

//Prevent multiple calls from causing the game selection screen from appearing too quickly
int counter = Interlocked.Increment(ref _taskId);

Task.Run(async () => {
await Task.Delay(3100);

//Show game selection screen after ~3 seconds
//This allows the message to be visible to the user
Dispatcher.UIThread.Post(() => {
if(_taskId == counter && !EmuApi.IsRunning()) {
MainWindowViewModel.Instance.RecentGames.Visible = true;
}
});
});
}
}
}
3 changes: 3 additions & 0 deletions UI/Utilities/LoadRomHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Avalonia.Threading;
using Mesen.Config;
using Mesen.Interop;
using Mesen.Localization;
using Mesen.ViewModels;
using Mesen.Windows;
using System;
Expand Down Expand Up @@ -145,6 +146,8 @@ public static void LoadFile(string filename)
} else {
LoadRom(filename);
}
} else {
DisplayMessageHelper.DisplayMessage("Error", ResourceHelper.GetMessage("FileNotFound", filename));
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion UI/Utilities/MouseManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ private CursorIcon MouseIcon
private void CaptureMouse()
{
if(!_mouseCaptured && AllowMouseCapture) {
EmuApi.DisplayMessage("Input", ResourceHelper.GetMessage("MouseModeEnabled"));
DisplayMessageHelper.DisplayMessage("Input", ResourceHelper.GetMessage("MouseModeEnabled"));
_mouseCaptured = true;

PixelPoint topLeft = _renderer.PointToScreen(new Point());
Expand Down
12 changes: 8 additions & 4 deletions UI/Utilities/ShortcutHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -301,10 +301,14 @@ enum VideoLayer

private void ToggleVideoLayer(VideoLayer layer)
{
if(!EmuApi.IsRunning()) {
return;
}

(Func<bool>? get, Action<bool>? set) = GetFlagSetterGetter(layer);
if(get != null && set != null) {
set(!get());
EmuApi.DisplayMessage("Debug", ResourceHelper.GetMessage(get() ? "VideoLayerDisabled" : "VideoLayerEnabled", ResourceHelper.GetEnumText(layer)));
DisplayMessageHelper.DisplayMessage("Debug", ResourceHelper.GetMessage(get() ? "VideoLayerDisabled" : "VideoLayerEnabled", ResourceHelper.GetEnumText(layer)));
ConfigManager.Config.Snes.ApplyConfig();
ConfigManager.Config.Nes.ApplyConfig();
ConfigManager.Config.Gameboy.ApplyConfig();
Expand Down Expand Up @@ -336,7 +340,7 @@ private void EnableAllLayers()
ConfigManager.Config.PcEngine.ApplyConfig();
ConfigManager.Config.Sms.ApplyConfig();

EmuApi.DisplayMessage("Debug", ResourceHelper.GetMessage("AllLayersEnabled"));
DisplayMessageHelper.DisplayMessage("Debug", ResourceHelper.GetMessage("AllLayersEnabled"));
}

private void SetEmulationSpeed(uint emulationSpeed)
Expand All @@ -345,9 +349,9 @@ private void SetEmulationSpeed(uint emulationSpeed)
ConfigManager.Config.Emulation.ApplyConfig();

if(emulationSpeed == 0) {
EmuApi.DisplayMessage("EmulationSpeed", "EmulationMaximumSpeed");
DisplayMessageHelper.DisplayMessage("EmulationSpeed", "EmulationMaximumSpeed");
} else {
EmuApi.DisplayMessage("EmulationSpeed", "EmulationSpeedPercent", emulationSpeed.ToString());
DisplayMessageHelper.DisplayMessage("EmulationSpeed", "EmulationSpeedPercent", emulationSpeed.ToString());
}
}

Expand Down
2 changes: 1 addition & 1 deletion UI/Windows/CommandLineHelpWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="Mesen.Windows.CommandLineHelpWindow"
Width="650" Height="400"
Width="700" Height="400"
Title="{l:Translate wndTitle}"
CanResize="False"
Name="root"
Expand Down
3 changes: 2 additions & 1 deletion UI/Windows/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using Avalonia.Input.Platform;
using System.Collections.Generic;
using Mesen.Controls;
using Mesen.Localization;

namespace Mesen.Windows
{
Expand Down Expand Up @@ -176,7 +177,7 @@ private void OnDrop(object? sender, DragEventArgs e)
LoadRomHelper.LoadFile(filename);
Activate();
} else {
EmuApi.DisplayMessage("Error", "File not found: " + filename);
DisplayMessageHelper.DisplayMessage("Error", ResourceHelper.GetMessage("FileNotFound", filename));
}
}
}
Expand Down

0 comments on commit 4b81e8b

Please sign in to comment.