Add settings UI for nyma

This commit is contained in:
nattthebear 2020-05-26 13:17:22 -04:00
parent b0b33ec2da
commit 3f43df4b1a
6 changed files with 474 additions and 17 deletions

View File

@ -184,8 +184,9 @@ namespace BizHawk.Client.EmuHawk
VideoProvider = emu.AsVideoProviderOrDefault();
RomLoaded?.Invoke(null, EventArgs.Empty);
_allJoyPads = new List<Joypad>(RunningSystem.MaxControllers);
for (var i = 1; i <= RunningSystem.MaxControllers; i++) _allJoyPads.Add(new Joypad(RunningSystem, i));
// TODO: Don't crash
// _allJoyPads = new List<Joypad>(RunningSystem.MaxControllers);
// for (var i = 1; i <= RunningSystem.MaxControllers; i++) _allJoyPads.Add(new Joypad(RunningSystem, i));
}
/// <summary>

View File

@ -17,8 +17,8 @@ namespace BizHawk.Client.EmuHawk
private GenericCoreConfig(MainForm mainForm, bool ignoreSettings = false, bool ignoreSyncSettings = false)
{
_mainForm = mainForm;
InitializeComponent();
_mainForm = mainForm;
var settable = new SettingsAdapter(GlobalWin.Emulator);
@ -108,8 +108,27 @@ namespace BizHawk.Client.EmuHawk
public static void DoDialog(MainForm owner, string title)
{
using var dlg = new GenericCoreConfig(owner) { Text = title };
dlg.ShowDialog(owner);
if (GlobalWin.Emulator is Emulation.Cores.Waterbox.NymaCore core)
{
var desc = new Emulation.Cores.Waterbox.NymaTypeDescriptorProvider(core.SettingsInfo);
try
{
// OH GOD THE HACKS WHY
TypeDescriptor.AddProvider(desc, typeof(Emulation.Cores.Waterbox.NymaCore.NymaSettings));
TypeDescriptor.AddProvider(desc, typeof(Emulation.Cores.Waterbox.NymaCore.NymaSyncSettings));
DoDialog(owner, "Nyma Core", !core.HasSettings, !core.HasSyncSettings);
}
finally
{
TypeDescriptor.RemoveProvider(desc, typeof(Emulation.Cores.Waterbox.NymaCore.NymaSettings));
TypeDescriptor.RemoveProvider(desc, typeof(Emulation.Cores.Waterbox.NymaCore.NymaSyncSettings));
}
}
else
{
using var dlg = new GenericCoreConfig(owner) { Text = title };
dlg.ShowDialog(owner);
}
}
public static void DoDialog(MainForm owner, string title, bool hideSettings, bool hideSyncSettings)
@ -117,7 +136,6 @@ namespace BizHawk.Client.EmuHawk
using var dlg = new GenericCoreConfig(owner, hideSettings, hideSyncSettings) { Text = title };
dlg.ShowDialog(owner);
}
private void PropertyGrid2_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
{
_syncSettingsChanged = true;

View File

@ -30,5 +30,15 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
// pce always has two layers, sgx always has 4, and mednafen knows this
public override string SystemId => SettingsInfo.LayerNames.Count == 4 ? "SGX" : "PCE";
protected override ICollection<string> HiddenSettings { get; } = new[]
{
// handled by hawk
"pce.cdbios",
"pce.gecdbios",
// so fringe i don't want people bothering me about it
"pce.resamp_rate_error",
"pce.vramsize",
};
}
}

View File

@ -35,7 +35,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// </summary>
public string[] Devices { get; }
public ControllerDefinition Definition { get; }
public ControllerAdapter(LibNymaCore core, IList<string> config)
public ControllerAdapter(LibNymaCore core, IDictionary<int, string> config)
{
var ret = new ControllerDefinition
{
@ -50,7 +50,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
for (uint port = 0, devByteStart = 0; port < numPorts; port++, devByteStart += MAX_PORT_DATA)
{
var portInfo = *core.GetPort(port);
var deviceName = port < config.Count ? config[(int)port] : portInfo.DefaultDeviceShortName;
var deviceName = config.ContainsKey((int)port) ? config[(int)port] : portInfo.DefaultDeviceShortName;
finalDevices.Add(deviceName);
var devices = Enumerable.Range(0, (int)portInfo.NumDevices)

View File

@ -0,0 +1,405 @@
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using static BizHawk.Emulation.Cores.Waterbox.NymaCore;
using static BizHawk.Emulation.Cores.Waterbox.NymaCore.NymaSettingsInfo;
using static BizHawk.Emulation.Cores.Waterbox.NymaCore.NymaSettingsInfo.MednaSetting;
namespace BizHawk.Emulation.Cores.Waterbox
{
public class NymaTypeDescriptorProvider : TypeDescriptionProvider
{
public NymaSettingsInfo SettingsInfo { get; }
public NymaTypeDescriptorProvider(NymaSettingsInfo info)
{
SettingsInfo = info;
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
if (objectType == typeof(NymaSyncSettings))
return new SyncSettingsCustomTypeDescriptor(SettingsInfo);
else if (objectType == typeof(NymaSettings))
return new SettingsCustomTypeDescriptor(SettingsInfo);
else
return null; //?
}
public override bool IsSupportedType(Type type)
{
return type == typeof(NymaSyncSettings) || type == typeof(NymaSettings);
}
}
public class SyncSettingsCustomTypeDescriptor : CustomTypeDescriptor
{
public NymaSettingsInfo SettingsInfo { get; }
public SyncSettingsCustomTypeDescriptor(NymaSettingsInfo info)
{
SettingsInfo = info;
}
public override string GetClassName() => nameof(NymaSyncSettings);
public override string GetComponentName() => nameof(NymaSyncSettings);
public override PropertyDescriptor GetDefaultProperty() => GetProperties()[0]; // "default" ??
public override PropertyDescriptorCollection GetProperties()
{
var s1 = SettingsInfo.Ports
.Select((p, i) => new PortPropertyDescriptor(p, i))
.Cast<PropertyDescriptor>();
var s2 = SettingsInfo.Settings
.Where(s => !SettingsInfo.HiddenSettings.Contains(s.SettingsKey))
.Select(m => MednaPropertyDescriptor.Create(m));
return new PropertyDescriptorCollection(s1.Concat(s2).ToArray());
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) => GetProperties();
}
public class SettingsCustomTypeDescriptor : CustomTypeDescriptor
{
public NymaSettingsInfo SettingsInfo { get; }
public SettingsCustomTypeDescriptor(NymaSettingsInfo info)
{
SettingsInfo = info;
}
public override string GetClassName() => nameof(NymaSettings);
public override string GetComponentName() => nameof(NymaSettings);
public override PropertyDescriptor GetDefaultProperty() => GetProperties()[0]; // "default" ??
public override PropertyDescriptorCollection GetProperties()
{
return new PropertyDescriptorCollection(
SettingsInfo.LayerNames.Select(l => new LayerPropertyDescriptor(l)).ToArray());
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) => GetProperties();
}
public abstract class MednaPropertyDescriptor : PropertyDescriptor
{
public MednaSetting Setting { get; private set; }
public MednaPropertyDescriptor(MednaSetting setting)
: base(setting.SettingsKey, new Attribute[0])
{
Setting = setting;
}
public override Type ComponentType => typeof(NymaSyncSettings);
public override bool IsReadOnly => false;
// public override Type PropertyType => typeof(string);
public override bool CanResetValue(object component) => true;
public override string Name => Setting.SettingsKey;
public override string DisplayName => Setting.Name;
public override string Description => $"{Setting.Description}\n[{Setting.SettingsKey}]";
public override string Category => "Settings";
protected abstract object ConvertFromString(string s);
protected abstract string ConvertToString(object o);
public override object GetValue(object component)
{
var ss = (NymaSyncSettings)component;
if (!ss.MednafenValues.TryGetValue(Setting.SettingsKey, out var val))
val = Setting.DefaultValue;
var ret = ConvertFromString(val);
return ret;
}
public override void ResetValue(object component)
{
((NymaSyncSettings)component).MednafenValues.Remove(Setting.SettingsKey);
}
public override void SetValue(object component, object value)
{
var s = ConvertToString(value);
if (s == null || s == Setting.DefaultValue)
{
ResetValue(component);
return;
}
((NymaSyncSettings)component).MednafenValues[Setting.SettingsKey] = s;
}
public override bool ShouldSerializeValue(object component)
{
return ((NymaSyncSettings)component).MednafenValues.ContainsKey(Setting.SettingsKey);
}
public static MednaPropertyDescriptor Create(MednaSetting s)
{
switch (s.Type)
{
case SettingType.INT:
return new MednaLongDescriptor(s);
case SettingType.UINT:
return new MednaUlongDescriptor(s);
case SettingType.BOOL:
return new MednaBoolDescriptor(s);
case SettingType.FLOAT:
return new MednaDoubleDescriptor(s);
case SettingType.STRING:
return new MednaStringDescriptor(s);
case SettingType.ENUM:
return new MednaEnumDescriptor(s);
default:
throw new NotImplementedException($"Unexpected SettingType {s.Type}");
}
}
}
public class MednaEnumDescriptor : MednaPropertyDescriptor
{
public MednaEnumDescriptor(MednaSetting s) : base(s) {}
public override Type PropertyType => typeof(string);
protected override object ConvertFromString(string s)
{
return s;
}
protected override string ConvertToString(object o)
{
return (string)o;
}
public override TypeConverter Converter => new MyTypeConverter { Setting = Setting };
private class MyTypeConverter : TypeConverter
{
public MednaSetting Setting { get; set; }
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) => sourceType == typeof(string);
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => destinationType == typeof(string);
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return Setting.SettingEnums
.SingleOrDefault(d => d.Name == (string)value)
?.Value
?? Setting.DefaultValue;
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
return Setting.SettingEnums
.SingleOrDefault(d => d.Value == (string)value)
?.Name
?? Setting.SettingEnums
.Single(d => d.Value == Setting.DefaultValue)
.Name;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) => new StandardValuesCollection(
Setting.SettingEnums.Select(e => e.Value).ToList()
);
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => true;
public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true;
}
}
public class MednaStringDescriptor : MednaPropertyDescriptor
{
public MednaStringDescriptor(MednaSetting s) : base(s) {}
public override Type PropertyType => typeof(string);
protected override object ConvertFromString(string s)
{
return s;
}
protected override string ConvertToString(object o)
{
return (string)o;
}
}
public class MednaBoolDescriptor : MednaPropertyDescriptor
{
public MednaBoolDescriptor(MednaSetting s) : base(s) {}
public override Type PropertyType => typeof(bool);
protected override object ConvertFromString(string s)
{
return int.Parse(s) != 0;
}
protected override string ConvertToString(object o)
{
return (bool)o ? "1" : "0";
}
}
public class MednaLongDescriptor : MednaPropertyDescriptor
{
public MednaLongDescriptor(MednaSetting s) : base(s) {}
public override Type PropertyType => typeof(long);
protected override object ConvertFromString(string s)
{
var ret = long.Parse(s);
if (Setting.Min != null && ret < long.Parse(Setting.Min) || Setting.Max != null && ret > long.Parse(Setting.Max))
ret = long.Parse(Setting.DefaultValue);
return ret;
}
protected override string ConvertToString(object o)
{
return o.ToString();
}
}
public class MednaUlongDescriptor : MednaPropertyDescriptor
{
public MednaUlongDescriptor(MednaSetting s) : base(s) {}
public override Type PropertyType => typeof(ulong);
protected override object ConvertFromString(string s)
{
var ret = ulong.Parse(s);
if (Setting.Min != null && ret < ulong.Parse(Setting.Min) || Setting.Max != null && ret > ulong.Parse(Setting.Max))
ret = ulong.Parse(Setting.DefaultValue);
return ret;
}
protected override string ConvertToString(object o)
{
return o.ToString();
}
}
public class MednaDoubleDescriptor : MednaPropertyDescriptor
{
public MednaDoubleDescriptor(MednaSetting s) : base(s) {}
public override Type PropertyType => typeof(double);
protected override object ConvertFromString(string s)
{
var ret = double.Parse(s);
if (Setting.Min != null && ret < double.Parse(Setting.Min) || Setting.Max != null && ret > double.Parse(Setting.Max))
ret = double.Parse(Setting.DefaultValue);
return ret;
}
protected override string ConvertToString(object o)
{
return o.ToString();
}
}
public class PortPropertyDescriptor : PropertyDescriptor
{
public Port Port { get; private set; }
public int PortIndex { get; private set; }
public PortPropertyDescriptor(Port port, int index)
: base(port.Name, new Attribute[0])
{
Port = port;
PortIndex = index;
}
public override string Name => Port.Name;
public override string DisplayName => Port.Name;
public override string Description => $"Change the device plugged into {Port.Name}";
public override string Category => "Ports";
public override Type ComponentType => typeof(NymaSyncSettings);
public override bool IsReadOnly => false;
public override Type PropertyType => typeof(string);
public override bool CanResetValue(object component) => true;
public override object GetValue(object component)
{
var ss = (NymaSyncSettings)component;
if (!ss.PortDevices.TryGetValue(PortIndex, out var val))
val = Port.DefaultSettingsValue;
return val;
}
public override void ResetValue(object component)
{
((NymaSyncSettings)component).PortDevices.Remove(PortIndex);
}
public override void SetValue(object component, object value)
{
if ((string)value == Port.DefaultSettingsValue)
{
ResetValue(component);
}
else if (!Port.AllowedDevices.Any(d => d.SettingValue == (string)value))
{
// does not validate
}
else
{
((NymaSyncSettings)component).PortDevices[PortIndex] = (string)value;
}
}
public override bool ShouldSerializeValue(object component)
{
return ((NymaSyncSettings)component).PortDevices.ContainsKey(PortIndex);
}
public override TypeConverter Converter => new MyTypeConverter { Port = Port };
private class MyTypeConverter : TypeConverter
{
public Port Port { get; set; }
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) => sourceType == typeof(string);
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => destinationType == typeof(string);
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return Port.AllowedDevices
.SingleOrDefault(d => d.Name == (string)value)
?.SettingValue
?? Port.DefaultSettingsValue;
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
return Port.AllowedDevices
.SingleOrDefault(d => d.SettingValue == (string)value)
?.Name
?? Port.AllowedDevices
.Single(d => d.SettingValue == Port.DefaultSettingsValue)
.Name;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) => new StandardValuesCollection(
Port.AllowedDevices.Select(d => d.SettingValue).ToList()
);
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => true;
public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true;
}
}
public class LayerPropertyDescriptor : PropertyDescriptor
{
public string LayerName { get; private set; }
public LayerPropertyDescriptor(string layerName)
: base(layerName, new Attribute[0])
{
LayerName = layerName;
}
public override string Name => LayerName;
public override string DisplayName => $"Show {LayerName}";
public override string Description => null;
public override string Category => "Layers";
public override Type ComponentType => typeof(NymaSettings);
public override bool IsReadOnly => false;
public override Type PropertyType => typeof(bool);
public override bool CanResetValue(object component) => true;
public override object GetValue(object component)
{
return !((NymaSettings)component).DisabledLayers.Contains(LayerName);
}
public override void ResetValue(object component)
{
((NymaSettings)component).DisabledLayers.Remove(LayerName);
}
public override void SetValue(object component, object value)
{
if ((bool)value)
((NymaSettings)component).DisabledLayers.Remove(LayerName);
else
((NymaSettings)component).DisabledLayers.Add(LayerName);
}
public override bool ShouldSerializeValue(object component)
{
return ((NymaSettings)component).DisabledLayers.Contains(LayerName);
}
}
}

View File

@ -6,11 +6,16 @@ using System.Text;
using BizHawk.BizInvoke;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using Newtonsoft.Json;
namespace BizHawk.Emulation.Cores.Waterbox
{
unsafe partial class NymaCore : ISettable<NymaCore.NymaSettings, NymaCore.NymaSyncSettings>
{
/// <summary>
/// Settings that we shouldn't show the user
/// </summary>
protected virtual ICollection<string> HiddenSettings { get; } = new string[0];
public NymaSettingsInfo SettingsInfo { get; private set; }
private NymaSettings _settings;
private NymaSyncSettings _syncSettings;
@ -18,8 +23,16 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// What this core was actually started with
/// </summary>
private NymaSyncSettings _syncSettingsActual;
public NymaSettings GetSettings() => _settings.Clone();
public NymaSyncSettings GetSyncSettings() => _syncSettings.Clone();
public NymaSettings GetSettings()
{
var ret = _settings.Clone();
return ret;
}
public NymaSyncSettings GetSyncSettings()
{
var ret = _syncSettings.Clone();
return ret;
}
public PutSettingsDirtyBits PutSettings(NymaSettings o)
{
@ -57,13 +70,13 @@ namespace BizHawk.Emulation.Cores.Waterbox
public class NymaSyncSettings
{
public Dictionary<string, string> MednafenValues { get; set; } = new Dictionary<string, string>();
public List<string> PortDevices { get; set; } = new List<string>();
public Dictionary<int, string> PortDevices { get; set; } = new Dictionary<int, string>();
public NymaSyncSettings Clone()
{
return new NymaSyncSettings
{
MednafenValues = new Dictionary<string, string>(MednafenValues),
PortDevices = new List<string>(PortDevices)
PortDevices = new Dictionary<int, string>(PortDevices)
};
}
@ -71,7 +84,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
{
if (!(obj is NymaSyncSettings x))
return false;
return PortDevices.SequenceEqual(x.PortDevices)
return new HashSet<KeyValuePair<int, string>>(PortDevices).SetEquals(x.PortDevices)
&& new HashSet<KeyValuePair<string, string>>(MednafenValues).SetEquals(x.MednafenValues);
}
@ -83,7 +96,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
private void SettingsQuery(string name, IntPtr dest)
{
if (!_syncSettingsActual.MednafenValues.TryGetValue(name, out var val))
if (!_syncSettingsActual.MednafenValues.TryGetValue(name, out var val) || HiddenSettings.Contains(name))
{
if (SettingsInfo.SettingsByKey.TryGetValue(name, out var info))
{
@ -103,6 +116,15 @@ namespace BizHawk.Emulation.Cores.Waterbox
private LibNymaCore.FrontendSettingQuery _settingsQueryDelegate;
/// <summary>
/// If true, the settings object has at least one settable value in it
/// </summary>
public bool HasSettings => SettingsInfo.LayerNames.Any();
/// <summary>
/// If true, the syncSettings object has at least one settable value in it
/// </summary>
public bool HasSyncSettings => SettingsInfo.Ports.Any() || SettingsInfo.Settings.Any();
public class NymaSettingsInfo
{
/// <summary>
@ -213,6 +235,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
Flags = (SettingFlags)s.Flags;
Type = (SettingType)s.Type;
}
public List<MednaSetting.EnumValue> SettingEnums { get; set; } = new List<MednaSetting.EnumValue>();
}
[StructLayout(LayoutKind.Sequential)]
public class MednaSettingS
@ -234,8 +257,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
}
}
public List<MednaSetting> Settings { get; set; } = new List<MednaSetting>();
public Dictionary<string, List<MednaSetting.EnumValue>> SettingEnums { get; set; } = new Dictionary<string, List<MednaSetting.EnumValue>>();
public Dictionary<string, MednaSetting> SettingsByKey { get; set; } = new Dictionary<string, MednaSetting>();
public HashSet<string> HiddenSettings { get; set; } = new HashSet<string>();
}
private void InitSyncSettingsInfo()
{
@ -277,7 +300,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
s.SettingsByKey.Add(ss.SettingsKey, ss);
if (ss.Type == NymaSettingsInfo.MednaSetting.SettingType.ENUM)
{
var l = new List<NymaSettingsInfo.MednaSetting.EnumValue>();
var l = ss.SettingEnums;
for (var j = 0;; j++)
{
var ff = new NymaSettingsInfo.MednaSettingS.EnumValueS();
@ -287,10 +310,10 @@ namespace BizHawk.Emulation.Cores.Waterbox
var ee = new NymaSettingsInfo.MednaSetting.EnumValue(ff);
l.Add(ee);
}
s.SettingEnums.Add(ss.SettingsKey, l);
}
}
s.HiddenSettings = new HashSet<string>(HiddenSettings);
SettingsInfo = s;
}
}