mame: set up the hawk side of syncsettings (dip switches only)

This commit is contained in:
feos 2021-05-22 19:02:34 +03:00
parent d71eca3b2d
commit 512ca30d7e
5 changed files with 263 additions and 38 deletions

View File

@ -464,7 +464,7 @@ namespace BizHawk.Client.Common
nextEmulator = new MAME(
file.Directory,
file.CanonicalName,
GetCoreSyncSettings<MAME, MAME.SyncSettings>(),
GetCoreSyncSettings<MAME, MAME.MAMESyncSettings>(),
out var gameName
);
rom.GameInfo.Name = gameName;

View File

@ -92,6 +92,19 @@ namespace BizHawk.Client.EmuHawk
TypeDescriptor.RemoveProvider(desc, typeof(Emulation.Cores.Waterbox.NymaCore.NymaSyncSettings));
}
}
else if(owner.Emulator is Emulation.Cores.Arcades.MAME.MAME mame)
{
var desc = new Emulation.Cores.Arcades.MAME.MAMETypeDescriptorProvider(mame.CurrentDriverSettings);
try
{
TypeDescriptor.AddProvider(desc, typeof(Emulation.Cores.Arcades.MAME.MAME.MAMESyncSettings));
DoDialog(owner, "MAME", true, false);
}
finally
{
TypeDescriptor.RemoveProvider(desc, typeof(Emulation.Cores.Arcades.MAME.MAME.MAMESyncSettings));
}
}
else
{
using var dlg = new GenericCoreConfig(owner) { Text = title };

View File

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using static BizHawk.Emulation.Cores.Arcades.MAME.MAME;
using static BizHawk.Emulation.Cores.Arcades.MAME.MAME.DriverSetting;
namespace BizHawk.Emulation.Cores.Arcades.MAME
{
public class MAMETypeDescriptorProvider : TypeDescriptionProvider
{
public MAMETypeDescriptorProvider(List<DriverSetting> settings)
{
Settings = settings;
}
public List<DriverSetting> Settings { get; }
public override bool IsSupportedType(Type type) => type == typeof(List<DriverSetting>);
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
=> new SyncSettingsCustomTypeDescriptor(Settings);
}
public class SyncSettingsCustomTypeDescriptor : CustomTypeDescriptor
{
public SyncSettingsCustomTypeDescriptor(List<DriverSetting> settings)
{
Settings = settings;
}
public List<DriverSetting> Settings { get; }
public override string GetClassName() => nameof(List<DriverSetting>);
public override string GetComponentName() => nameof(List<DriverSetting>);
public override PropertyDescriptor GetDefaultProperty() => GetProperties()[0]; // "default" ??
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) => GetProperties();
public override PropertyDescriptorCollection GetProperties()
{
var s = Settings.Select(m => new MAMEPropertyDescriptor(m));
return new PropertyDescriptorCollection(s.ToArray());
}
}
public class MAMEPropertyDescriptor : PropertyDescriptor
{
public MAMEPropertyDescriptor(DriverSetting setting) : base(setting.LookupKey, new Attribute[0])
{
Setting = setting;
}
public DriverSetting Setting { get; private set; }
protected object ConvertFromString(string s) => s;
protected string ConvertToString(object o) => (string)o;
public override bool CanResetValue(object component) => true;
public override bool ShouldSerializeValue(object component)
=> ((MAMESyncSettings)component).DriverSettings.ContainsKey(Setting.LookupKey);
public override Type PropertyType => typeof(string);
public override TypeConverter Converter => new MyTypeConverter { Setting = Setting };
public override Type ComponentType => typeof(List<DriverSetting>);
public override bool IsReadOnly => false;
public override string Name => Setting.LookupKey;
public override string DisplayName => Setting.Name;
public override string Description => Setting.LookupKey;
public override object GetValue(object component)
{
var ss = (MAMESyncSettings)component;
if (!ss.DriverSettings.TryGetValue(Setting.LookupKey, out var val))
val = Setting.DefaultValue;
return ConvertFromString(val);
}
public override void ResetValue(object component)
{
((MAMESyncSettings)component).DriverSettings.Remove(Setting.LookupKey);
}
public override void SetValue(object component, object value)
{
var s = ConvertToString(value);
if (s == null || s == Setting.DefaultValue)
{
ResetValue(component);
return;
}
((MAMESyncSettings)component).DriverSettings[Setting.LookupKey] = s;
}
private class MyTypeConverter : TypeConverter
{
public DriverSetting Setting { get; set; }
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => true;
public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true;
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) => sourceType == typeof(string);
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => destinationType == typeof(string);
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
=> new StandardValuesCollection(Setting.Options.Select(e => e.Key).ToList());
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
=> Setting.Options.SingleOrDefault(d => d.Value == (string)value).Key ?? Setting.DefaultValue;
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
=> Setting.Options[(string)value] ?? Setting.Options[Setting.DefaultValue];
}
}
}

View File

@ -1,41 +1,116 @@
using System.Dynamic;
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Arcades.MAME
{
public partial class MAME : ISettable<object, MAME.SyncSettings>
public partial class MAME : ISettable<object, MAME.MAMESyncSettings>
{
public object GetSettings() => null;
public PutSettingsDirtyBits PutSettings(object o) => PutSettingsDirtyBits.None;
public List<DriverSetting> CurrentDriverSettings = new List<DriverSetting>();
private MAMESyncSettings _syncSettings;
private SyncSettings _syncSettings;
public SyncSettings GetSyncSettings()
public MAMESyncSettings GetSyncSettings()
{
return _syncSettings.Clone();
}
public PutSettingsDirtyBits PutSyncSettings(SyncSettings o)
public PutSettingsDirtyBits PutSyncSettings(MAMESyncSettings o)
{
bool ret = SyncSettings.NeedsReboot(o, _syncSettings);
bool ret = MAMESyncSettings.NeedsReboot(o, _syncSettings);
_syncSettings = o;
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
}
public class SyncSettings
public class MAMESyncSettings
{
public static bool NeedsReboot(SyncSettings x, SyncSettings y)
public Dictionary<string, string> DriverSettings { get; set; } = new Dictionary<string, string>();
public static bool NeedsReboot(MAMESyncSettings x, MAMESyncSettings y)
{
return !DeepEquality.DeepEquals(x, y);
}
public SyncSettings Clone()
public MAMESyncSettings Clone()
{
return (SyncSettings)MemberwiseClone();
return (MAMESyncSettings)MemberwiseClone();
}
}
public ExpandoObject ExpandoSettings { get; set; }
public void FetchDefaultGameSettings()
{
string DIPSwitchTags = MameGetString(MAMELuaCommand.GetDIPSwitchTags);
string[] tags = DIPSwitchTags.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string tag in tags)
{
string DIPSwitchFields = MameGetString(MAMELuaCommand.GetDIPSwitchFields(tag));
string[] fieldNames = DIPSwitchFields.Split(new char[] { '^' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string fieldName in fieldNames)
{
DriverSetting setting = new DriverSetting()
{
Name = fieldName,
GameName = _gameShortName,
LuaCode = MAMELuaCommand.InputField(tag, fieldName),
Type = SettingType.DIPSWITCH,
DefaultValue = LibMAME.mame_lua_get_int(
$"return { MAMELuaCommand.InputField(tag, fieldName) }.defvalue").ToString()
};
string DIPSwitchOptions = MameGetString(MAMELuaCommand.GetDIPSwitchOptions(tag, fieldName));
string[] options = DIPSwitchOptions.Split(new char[] { '@' }, StringSplitOptions.RemoveEmptyEntries);
foreach(string option in options)
{
string[] opt = option.Split(new char[] { '~' }, StringSplitOptions.RemoveEmptyEntries);
setting.Options.Add(opt[0], opt[1]);
}
CurrentDriverSettings.Add(setting);
}
}
}
public void OverrideGameSettings()
{
foreach (KeyValuePair<string, string> setting in _syncSettings.DriverSettings)
{
DriverSetting s = CurrentDriverSettings.SingleOrDefault(s => s.LookupKey == setting.Key);
if (s != null)
{
LibMAME.mame_lua_execute($"{ s.LuaCode }:set_value({ setting.Value })");
}
}
}
public class DriverSetting
{
public string Name { get; set; }
public string GameName { get; set; }
public string LuaCode { get; set; }
public string DefaultValue { get; set; }
public SettingType Type { get; set; }
public Dictionary<string, string> Options { get; set; }
public string LookupKey => $"[{ GameName }] { LuaCode }";
public DriverSetting()
{
Name = null;
GameName = null;
DefaultValue = null;
Options = new Dictionary<string, string>();
}
}
public enum SettingType
{
DIPSWITCH, BIOS
}
}
}

View File

@ -84,26 +84,24 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Arcades.MAME
{
[PortedCore(CoreNames.MAME, "MAMEDev", "0.231", "https://github.com/mamedev/mame.git", isReleased: false)]
public partial class MAME : IEmulator, IVideoProvider, ISoundProvider, ISettable<object, MAME.SyncSettings>, IStatable, IInputPollable
public partial class MAME : IEmulator, IVideoProvider, ISoundProvider, ISettable<object, MAME.MAMESyncSettings>, IStatable, IInputPollable
{
public MAME(string dir, string file, MAME.SyncSettings syncSettings, out string gamename)
public MAME(string dir, string file, MAME.MAMESyncSettings syncSettings, out string gamename)
{
OSTailoredCode.LinkedLibManager.FreeByPtr(OSTailoredCode.LinkedLibManager.LoadOrThrow(LibMAME.dll)); // don't bother if the library is missing
_gameDirectory = dir;
_gameFileName = file;
ServiceProvider = new BasicServiceProvider(this);
_gameDirectory = dir;
_gameFilename = file;
_syncSettings = syncSettings ?? new MAMESyncSettings();
_mameThread = new Thread(ExecuteMAMEThread);
_mameThread.Start();
_mameStartupComplete.WaitOne();
_syncSettings = (SyncSettings)syncSettings ?? new SyncSettings();
_syncSettings.ExpandoSettings = new ExpandoObject();
var dynamicObject = (IDictionary<string, object>)_syncSettings.ExpandoSettings;
dynamicObject.Add("OKAY", 1);
gamename = _gameName;
gamename = _gameFullName;
if (_loadFailure != "")
{
@ -112,9 +110,10 @@ namespace BizHawk.Emulation.Cores.Arcades.MAME
}
}
private string _gameName = "Arcade";
private string _gameFullName = "Arcade";
private string _gameShortName = "arcade";
private readonly string _gameDirectory;
private readonly string _gameFilename;
private readonly string _gameFileName;
private string _loadFailure = "";
private readonly Thread _mameThread;
private readonly ManualResetEvent _mameStartupComplete = new ManualResetEvent(false);
@ -128,7 +127,6 @@ namespace BizHawk.Emulation.Cores.Arcades.MAME
private void ExecuteMAMEThread()
{
// dodge GC
_periodicCallback = MAMEPeriodicCallback;
_soundCallback = MAMESoundCallback;
_bootCallback = MAMEBootCallback;
@ -143,7 +141,7 @@ namespace BizHawk.Emulation.Cores.Arcades.MAME
string[] args =
{
"mame" // dummy, internally discarded by index, so has to go first
, _gameFilename // no dash for rom names
, _gameFileName // no dash for rom names
, "-noreadconfig" // forbid reading ini files
, "-nowriteconfig" // forbid writing ini files
, "-norewind" // forbid rewind savestates (captured upon frame advance)
@ -192,7 +190,8 @@ namespace BizHawk.Emulation.Cores.Arcades.MAME
private void UpdateGameName()
{
_gameName = MameGetString(MAMELuaCommand.GetGameName);
_gameFullName = MameGetString(MAMELuaCommand.GetGameFullName);
_gameShortName = MameGetString(MAMELuaCommand.GetGameShortName);
}
private void CheckVersions()
@ -282,6 +281,8 @@ namespace BizHawk.Emulation.Cores.Arcades.MAME
UpdateFramerate();
UpdateGameName();
InitMemoryDomains();
FetchDefaultGameSettings();
OverrideGameSettings();
int length = LibMAME.mame_lua_get_int("return string.len(manager.machine:buffer_save())");
_mameSaveBuffer = new byte[length];
@ -341,21 +342,22 @@ namespace BizHawk.Emulation.Cores.Arcades.MAME
// getters
public const string GetVersion = "return emu.app_version()";
public const string GetGameName = "return manager.machine.system.description";
public const string GetPixels = "return manager.machine.video:snapshot_pixels()";
public const string GetSamples = "return manager.machine.sound:get_samples()";
public const string GetGameShortName = "return manager.machine.system.name";
public const string GetGameFullName = "return manager.machine.system.description";
public const string GetWidth = "return (select(1, manager.machine.video:snapshot_size()))";
public const string GetHeight = "return (select(2, manager.machine.video:snapshot_size()))";
public const string GetPixels = "return manager.machine.video:snapshot_pixels()";
public const string GetSamples = "return manager.machine.sound:get_samples()";
public const string GetMainCPUName = "return manager.machine.devices[\":maincpu\"].shortname";
// memory space
public const string GetSpace = "return manager.machine.devices[\":maincpu\"].spaces[\"program\"]";
public const string GetSpaceMapCount = "return #manager.machine.devices[\":maincpu\"].spaces[\"program\"].map.entries";
public const string SpaceMap = "manager.machine.devices[\":maincpu\"].spaces[\"program\"].map.entries";
public const string GetSpaceAddressMask = "return manager.machine.devices[\":maincpu\"].spaces[\"program\"].address_mask";
public const string GetSpaceAddressShift = "return manager.machine.devices[\":maincpu\"].spaces[\"program\"].shift";
public const string GetSpaceDataWidth = "return manager.machine.devices[\":maincpu\"].spaces[\"program\"].data_width";
public const string GetSpaceEndianness = "return manager.machine.devices[\":maincpu\"].spaces[\"program\"].endianness";
public const string GetSpaceMapCount = "return #manager.machine.devices[\":maincpu\"].spaces[\"program\"].map.entries";
public const string SpaceMap = "manager.machine.devices[\":maincpu\"].spaces[\"program\"].map.entries";
// complex stuff
public const string GetFrameNumber =
@ -372,8 +374,17 @@ namespace BizHawk.Emulation.Cores.Arcades.MAME
public const string GetBoundY =
"local b = manager.machine.render.ui_target.current_view.bounds " +
"return b.y1-b.y0";
public const string GetROMsInfo =
"local final = {} " +
"for __, r in pairs(manager.machine.devices[\":\"].roms) do " +
"if (r:hashdata() ~= \"\") then " +
"table.insert(final, string.format(\"%s,%s,%s;\", r:name(), r:hashdata(), r:flags())) " +
"end " +
"end " +
"table.sort(final) " +
"return table.concat(final)";
public const string GetInputFields =
"final = {} " +
"local final = {} " +
"for tag, _ in pairs(manager.machine.ioport.ports) do " +
"for name, field in pairs(manager.machine.ioport.ports[tag].fields) do " +
"if field.type_class ~= \"dipswitch\" then " +
@ -383,15 +394,37 @@ namespace BizHawk.Emulation.Cores.Arcades.MAME
"end " +
"table.sort(final) " +
"return table.concat(final)";
public const string GetROMsInfo =
"final = {} " +
"for __, r in pairs(manager.machine.devices[\":\"].roms) do " +
"if (r:hashdata() ~= \"\") then " +
"table.insert(final, string.format(\"%s,%s,%s;\", r:name(), r:hashdata(), r:flags())) " +
public const string GetDIPSwitchTags =
"local final = {} " +
"for tag, _ in pairs(manager.machine.ioport.ports) do " +
"for name, field in pairs(manager.machine.ioport.ports[tag].fields) do " +
"if field.type_class == \"dipswitch\" then " +
"table.insert(final, tag..\";\") " +
"break " +
"end " +
"end " +
"end " +
"table.sort(final) " +
"return table.concat(final)";
public static string InputField(string tag, string fieldName) =>
$"manager.machine.ioport.ports[\"{ tag }\"].fields[\"{ fieldName }\"]";
public static string GetDIPSwitchFields(string tag) =>
"local final = { } " +
$"for name, field in pairs(manager.machine.ioport.ports[\"{ tag }\"].fields) do " +
"if field.type_class == \"dipswitch\" then " +
"table.insert(final, field.name..\"^\") " +
"end " +
"end " +
"table.sort(final) " +
"return table.concat(final)";
public static string GetDIPSwitchOptions(string tag, string fieldName) =>
"local final = { } " +
$"for value, description in pairs(manager.machine.ioport.ports[\"{ tag }\"].fields[\"{ fieldName }\"].settings) do " +
"table.insert(final, string.format(\"%d~%s@\", value, description)) " +
"end " +
"table.sort(final) " +
"return table.concat(final)";
}
}
}