From 8c9f4e24d828760e31c8a418311ef1f8ac548bae Mon Sep 17 00:00:00 2001 From: nattthebear Date: Sun, 24 May 2020 17:56:43 -0400 Subject: [PATCH] settings infra --- src/BizHawk.Client.Common/RomLoader.cs | 3 +- src/BizHawk.Common/PtrToStringUtf8.cs | 2 + .../Consoles/NEC/PCE/TerboGrafix.cs | 11 +- .../Waterbox/LibNymaCore.cs | 20 ++ .../Waterbox/NymaCore.Controller.cs | 27 +- .../Waterbox/NymaCore.Settings.cs | 296 ++++++++++++++++++ .../Waterbox/NymaCore.cs | 70 +++-- waterbox/nyma/.vscode/settings.json | 6 + waterbox/nyma/Interfaces.cpp | 60 ++-- waterbox/nyma/NymaCore.cpp | 52 ++- 10 files changed, 477 insertions(+), 70 deletions(-) create mode 100644 src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Settings.cs create mode 100644 waterbox/nyma/.vscode/settings.json diff --git a/src/BizHawk.Client.Common/RomLoader.cs b/src/BizHawk.Client.Common/RomLoader.cs index 9ab08e8475..177e409e87 100644 --- a/src/BizHawk.Client.Common/RomLoader.cs +++ b/src/BizHawk.Client.Common/RomLoader.cs @@ -526,7 +526,8 @@ namespace BizHawk.Client.Common else { // TODO: pass disc in - nextEmulator = new TerboGrafix(game, null, nextComm, "dunno what to put here"); + throw new NotImplementedException(); + // nextEmulator = new TerboGrafix(game, null, nextComm, "dunno what to put here"); } break; diff --git a/src/BizHawk.Common/PtrToStringUtf8.cs b/src/BizHawk.Common/PtrToStringUtf8.cs index 1a234ab210..aaace19d45 100644 --- a/src/BizHawk.Common/PtrToStringUtf8.cs +++ b/src/BizHawk.Common/PtrToStringUtf8.cs @@ -10,6 +10,8 @@ namespace BizHawk.Common /// public static unsafe string PtrToStringUtf8(IntPtr p) { + if (p == IntPtr.Zero) + return null; byte* b = (byte*)p; int len = 0; while (*b++ != 0) diff --git a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TerboGrafix.cs b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TerboGrafix.cs index 8282a0a768..ae8cd5466a 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TerboGrafix.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TerboGrafix.cs @@ -3,16 +3,13 @@ using BizHawk.Emulation.Cores.Waterbox; namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE { - [Core(CoreNames.TurboNyma, "Mednafen Team", true, false, "", "", false)] + [Core(CoreNames.TurboNyma, "Mednafen Team", true, false, "1.24.3", "", false)] public class TerboGrafix : NymaCore, IRegionable { [CoreConstructor("PCE")] - public TerboGrafix(GameInfo game, byte[] rom, CoreComm comm, string extension) - : base(game, rom, comm, new Configuration - { - SystemId = "PCE" // whatever - // TODO: This stuff isn't used so much - }) + public TerboGrafix(GameInfo game, byte[] rom, CoreComm comm, string extension, + NymaSettings settings, NymaSyncSettings syncSettings) + : base(game, rom, comm, "PCE", "PC Engine Controller", settings, syncSettings) { DoInit(game, rom, "pce.wbx", extension); } diff --git a/src/BizHawk.Emulation.Cores/Waterbox/LibNymaCore.cs b/src/BizHawk.Emulation.Cores/Waterbox/LibNymaCore.cs index 84d81fb6f0..d9aa833e40 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/LibNymaCore.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/LibNymaCore.cs @@ -25,6 +25,15 @@ namespace BizHawk.Emulation.Cores.Waterbox public string FileNameFull; } + /// + /// Do this before calling anything, even settings queries + /// + [BizImport(CC, Compatibility = true)] + public abstract void PreInit(); + + /// + /// Load a ROM + /// [BizImport(CC, Compatibility = true)] public abstract bool Init([In]InitData data); @@ -270,5 +279,16 @@ namespace BizHawk.Emulation.Cores.Waterbox [BizImport(CC, Compatibility = true)] public abstract SystemInfo* GetSystemInfo(); + + [BizImport(CC, Compatibility = true)] + public abstract void IterateSettings(int index, [In, Out]NymaCore.NymaSettingsInfo.MednaSettingS s); + + [BizImport(CC, Compatibility = true)] + public abstract void IterateSettingEnums(int index, int enumIndex,[In, Out]NymaCore.NymaSettingsInfo.MednaSettingS.EnumValueS e); + + public delegate void FrontendSettingQuery(string setting, IntPtr dest); + [BizImport(CC)] + public abstract void SetFrontendSettingQuery(FrontendSettingQuery q); + } } diff --git a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Controller.cs b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Controller.cs index d99d6fc6c0..bccbf3fafa 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Controller.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Controller.cs @@ -9,10 +9,11 @@ namespace BizHawk.Emulation.Cores.Waterbox { private ControllerAdapter _controllerAdapter; private readonly byte[] _inputPortData = new byte[16 * 16]; + private readonly string _controllerDeckName; private void InitControls() { - _controllerAdapter = new ControllerAdapter(_nyma, new string[0]); + _controllerAdapter = new ControllerAdapter(_nyma, _syncSettingsActual.PortDevices); _nyma.SetInputDevices(_controllerAdapter.Devices); ControllerDefinition = _controllerAdapter.Definition; } @@ -34,7 +35,7 @@ namespace BizHawk.Emulation.Cores.Waterbox /// public string[] Devices { get; } public ControllerDefinition Definition { get; } - public ControllerAdapter(LibNymaCore core, string[] config) + public ControllerAdapter(LibNymaCore core, IList config) { var ret = new ControllerDefinition { @@ -49,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.Length ? config[port] : portInfo.DefaultDeviceShortName; + var deviceName = port < config.Count ? config[(int)port] : portInfo.DefaultDeviceShortName; finalDevices.Add(deviceName); var devices = Enumerable.Range(0, (int)portInfo.NumDevices) @@ -91,7 +92,7 @@ namespace BizHawk.Emulation.Cores.Waterbox case LibNymaCore.InputType.BUTTON: case LibNymaCore.InputType.BUTTON_CAN_RAPID: { - var data = *core.GetButton(port, device.Index, (uint)input.Index); + // var data = *core.GetButton(port, device.Index, (uint)input.Index); // TODO: Wire up data.ExcludeName ret.BoolButtons.Add(name); _thunks.Add((c, b) => @@ -104,21 +105,23 @@ namespace BizHawk.Emulation.Cores.Waterbox case LibNymaCore.InputType.SWITCH: { var data = *core.GetSwitch(port, device.Index, (uint)input.Index); + var zzhacky = (int)data.DefaultPosition; // TODO: Possibly bulebutton for 2 states? ret.AxisControls.Add(name); ret.AxisRanges.Add(new ControllerDefinition.AxisRange( 0, (int)data.DefaultPosition, (int)data.NumPositions - 1)); - // HACK: Silently discard this until bizhawk fixes its shit - // _thunks.Add((c, b) => - // { - // var val = (int)Math.Round(c.AxisValue(name)); - // b[byteStart] |= (byte)(1 << bitOffset); - // }); + _thunks.Add((c, b) => + { + // HACK: Silently discard this until bizhawk fixes its shit + // var val = (int)Math.Round(c.AxisValue(name)); + var val = zzhacky; + b[byteStart] |= (byte)(val << bitOffset); + }); break; } case LibNymaCore.InputType.AXIS: { - var data = core.GetAxis(port, device.Index, (uint)input.Index); + // var data = core.GetAxis(port, device.Index, (uint)input.Index); ret.AxisControls.Add(name); ret.AxisRanges.Add(new ControllerDefinition.AxisRange( 0, 0x8000, 0xffff, (inputInfo.Flags & LibNymaCore.AxisFlags.INVERT_CO) != 0 @@ -133,7 +136,7 @@ namespace BizHawk.Emulation.Cores.Waterbox } case LibNymaCore.InputType.AXIS_REL: { - var data = core.GetAxis(port, device.Index, (uint)input.Index); + // var data = core.GetAxis(port, device.Index, (uint)input.Index); ret.AxisControls.Add(name); ret.AxisRanges.Add(new ControllerDefinition.AxisRange( -0x8000, 0, 0x7fff, (inputInfo.Flags & LibNymaCore.AxisFlags.INVERT_CO) != 0 diff --git a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Settings.cs b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Settings.cs new file mode 100644 index 0000000000..b9daa28237 --- /dev/null +++ b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Settings.cs @@ -0,0 +1,296 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Waterbox +{ + unsafe partial class NymaCore : ISettable + { + public NymaSettingsInfo SettingsInfo { get; private set; } + private NymaSettings _settings; + private NymaSyncSettings _syncSettings; + /// + /// What this core was actually started with + /// + private NymaSyncSettings _syncSettingsActual; + public NymaSettings GetSettings() => _settings.Clone(); + public NymaSyncSettings GetSyncSettings() => _syncSettings.Clone(); + + public PutSettingsDirtyBits PutSettings(NymaSettings o) + { + _settings = o.Clone(); + if (SettingsInfo.LayerNames.Count > 0) + { + ulong layers = ~0ul; + for (int i = 0; i < 64 && i < SettingsInfo.LayerNames.Count; i++) + { + if (_settings.DisabledLayers.Contains(SettingsInfo.LayerNames[i])) + layers &= ~(1ul << i); + } + _nyma.SetLayers(layers); + } + return PutSettingsDirtyBits.None; + } + + public PutSettingsDirtyBits PutSyncSettings(NymaSyncSettings o) + { + _syncSettings = o.Clone(); + return _syncSettings.Equals(_syncSettingsActual) + ? PutSettingsDirtyBits.None + : PutSettingsDirtyBits.RebootCore; + } + + public class NymaSettings + { + public HashSet DisabledLayers { get; set; } = new HashSet(); + public NymaSettings Clone() + { + return new NymaSettings { DisabledLayers = new HashSet(DisabledLayers) }; + } + } + + public class NymaSyncSettings + { + public Dictionary MednafenValues { get; set; } = new Dictionary(); + public List PortDevices { get; set; } = new List(); + public NymaSyncSettings Clone() + { + return new NymaSyncSettings + { + MednafenValues = new Dictionary(MednafenValues), + PortDevices = new List(PortDevices) + }; + } + + public override bool Equals(object obj) + { + if (!(obj is NymaSyncSettings x)) + return false; + return PortDevices.SequenceEqual(x.PortDevices) + && new HashSet>(MednafenValues).SetEquals(x.MednafenValues); + } + + public override int GetHashCode() + { + return 0; // some other time, maybe + } + } + + private void SettingsQuery(string name, IntPtr dest) + { + if (!_syncSettingsActual.MednafenValues.TryGetValue(name, out var val)) + { + if (SettingsInfo.SettingsByKey.TryGetValue(name, out var info)) + { + val = info.DefaultValue; + } + else + { + throw new InvalidOperationException($"Core asked for setting {name} which was not found in the defaults"); + } + } + var bytes = Encoding.UTF8.GetBytes(val); + if (bytes.Length > 255) + throw new InvalidOperationException($"Value {val} for setting {name} was too long"); + WaterboxUtils.ZeroMemory(dest, 256); + Marshal.Copy(bytes, 0, dest, bytes.Length); + } + + private LibNymaCore.FrontendSettingQuery _settingsQueryDelegate; + + public class NymaSettingsInfo + { + /// + /// What layers are available to toggle. If empty, layers cannot be set on this core. + /// + public List LayerNames { get; set; } + public class Device + { + public string Name { get; set; } + public string Description { get; set; } + public string SettingdValue { get; set; } + } + public class Port + { + public string Name { get; set; } + public List AllowedDevices { get; set; } = new List(); + public string DefaultSettingsValue { get; set; } + } + /// + /// What devices can be plugged into each port + /// + public List Ports { get; set; } = new List(); + public class MednaSetting + { + public string Name; + public string Description; + public string SettingsKey; + public string DefaultValue; + public string Min; + public string Max; + [Flags] + public enum SettingFlags : uint + { + NOFLAGS = 0U, // Always 0, makes setting definitions prettier...maybe. + + // TODO(cats) + CAT_INPUT = (1U << 8), + CAT_SOUND = (1U << 9), + CAT_VIDEO = (1U << 10), + CAT_INPUT_MAPPING = (1U << 11), // User-configurable physical->virtual button/axes and hotkey mappings(driver-side code category mainly). + + // Setting is used as a path or filename(mostly intended for automatic charset conversion of 0.9.x settings on MS Windows). + CAT_PATH = (1U << 12), + + EMU_STATE = (1U << 17), // If the setting affects emulation from the point of view of the emulated program + UNTRUSTED_SAFE = (1U << 18), // If it's safe for an untrusted source to modify it, probably only used in conjunction with + // MDFNST_EX_EMU_STATE and network play + + SUPPRESS_DOC = (1U << 19), // Suppress documentation generation for this setting. + COMMON_TEMPLATE = (1U << 20), // Auto-generated common template setting(like nes.xscale, pce.xscale, vb.xscale, nes.enable, pce.enable, vb.enable) + NONPERSISTENT = (1U << 21), // Don't save setting in settings file. + + // TODO: + // WILL_BREAK_GAMES (1U << ) // If changing the value of the setting from the default value will break games/programs that would otherwise work. + + // TODO(in progress): + REQUIRES_RELOAD = (1U << 24), // If a game reload is required for the setting to take effect. + REQUIRES_RESTART = (1U << 25), // If Mednafen restart is required for the setting to take effect. + } + public SettingFlags Flags; + public enum SettingType : int + { + INT = 0, // (signed), int8, int16, int32, int64(saved as) + UINT, // uint8, uint16, uint32, uint64(saved as) + /// + /// 0 or 1 + /// + BOOL, + /// + /// float64 + /// + FLOAT, + STRING, + /// + /// string value from a list of potential strings + /// + ENUM, + /// + /// TODO: How do these work + /// + MULTI_ENUM, + /// + /// Shouldn't see any of these + /// + ALIAS + } + public SettingType Type; + public class EnumValue + { + public string Name; + public string Description; + public string Value; + public EnumValue(MednaSettingS.EnumValueS s) + { + Name = Mershul.PtrToStringUtf8(s.Name); + Description = Mershul.PtrToStringUtf8(s.Description); + Value = Mershul.PtrToStringUtf8(s.Value); + } + } + public MednaSetting(MednaSettingS s) + { + Name = Mershul.PtrToStringUtf8(s.Name); + Description = Mershul.PtrToStringUtf8(s.Description); + SettingsKey = Mershul.PtrToStringUtf8(s.SettingsKey); + DefaultValue = Mershul.PtrToStringUtf8(s.DefaultValue); + Min = Mershul.PtrToStringUtf8(s.Min); + Max = Mershul.PtrToStringUtf8(s.Max); + Flags = (SettingFlags)s.Flags; + Type = (SettingType)s.Type; + } + } + [StructLayout(LayoutKind.Sequential)] + public class MednaSettingS + { + public IntPtr Name; + public IntPtr Description; + public IntPtr SettingsKey; + public IntPtr DefaultValue; + public IntPtr Min; + public IntPtr Max; + public uint Flags; + public int Type; + [StructLayout(LayoutKind.Sequential)] + public class EnumValueS + { + public IntPtr Name; + public IntPtr Description; + public IntPtr Value; + } + } + public List Settings { get; set; } = new List(); + public Dictionary> SettingEnums { get; set; } = new Dictionary>(); + public Dictionary SettingsByKey { get; set; } = new Dictionary(); + } + private void InitSyncSettingsInfo() + { + // TODO: Some shared logic in ControllerAdapter. Avoidable? + var s = new NymaSettingsInfo(); + + var numPorts = _nyma.GetNumPorts(); + for (uint port = 0; port < numPorts; port++) + { + var portInfo = *_nyma.GetPort(port); + + s.Ports.Add(new NymaSettingsInfo.Port + { + Name = portInfo.FullName, + DefaultSettingsValue = portInfo.DefaultDeviceShortName, + AllowedDevices = Enumerable.Range(0, (int)portInfo.NumDevices) + .Select(i => + { + var dev = *_nyma.GetDevice(port, (uint)i); + return new NymaSettingsInfo.Device + { + Name = dev.FullName, + Description = dev.Description, + SettingdValue = dev.ShortName + }; + }) + .ToList() + }); + } + + for (var i = 0;; i++) + { + var tt = new NymaSettingsInfo.MednaSettingS(); + _nyma.IterateSettings(i, tt); + if (tt.SettingsKey == IntPtr.Zero) + break; + var ss = new NymaSettingsInfo.MednaSetting(tt); + s.Settings.Add(ss); + s.SettingsByKey.Add(ss.SettingsKey, ss); + if (ss.Type == NymaSettingsInfo.MednaSetting.SettingType.ENUM) + { + var l = new List(); + for (var j = 0;; j++) + { + var ff = new NymaSettingsInfo.MednaSettingS.EnumValueS(); + _nyma.IterateSettingEnums(i, j, ff); + if (ff.Value == IntPtr.Zero) + break; + var ee = new NymaSettingsInfo.MednaSetting.EnumValue(ff); + l.Add(ee); + } + s.SettingEnums.Add(ss.SettingsKey, l); + } + } + + SettingsInfo = s; + } + } +} diff --git a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs index 7d7be2f53e..7db453f85e 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Runtime.InteropServices; using BizHawk.Common; @@ -9,35 +10,42 @@ namespace BizHawk.Emulation.Cores.Waterbox { public unsafe abstract partial class NymaCore : WaterboxCore { - protected NymaCore(GameInfo game, byte[] rom, CoreComm comm, Configuration c) - : base(comm, c) + protected NymaCore(GameInfo game, byte[] rom, CoreComm comm, string systemId, string controllerDeckName, + NymaSettings settings, NymaSyncSettings syncSettings) + : base(comm, new Configuration { SystemId = systemId }) { + _settings = settings ?? new NymaSettings(); + _syncSettings = syncSettings ?? new NymaSyncSettings(); + _syncSettingsActual = _syncSettings; + _controllerDeckName = controllerDeckName; } private LibNymaCore _nyma; - protected T DoInit(GameInfo game, byte[] rom, string filename, string extension) + protected T DoInit(GameInfo game, byte[] rom, string wbxFilename, string extension) where T : LibNymaCore { var t = PreInit(new WaterboxOptions { - // TODO cfg and stuff - Filename = filename, + // TODO fix these up + Filename = wbxFilename, SbrkHeapSizeKB = 1024 * 16, SealedHeapSizeKB = 1024 * 16, InvisibleHeapSizeKB = 1024 * 16, PlainHeapSizeKB = 1024 * 16, MmapHeapSizeKB = 1024 * 16, - StartAddress = WaterboxHost.CanonicalStart, SkipCoreConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck), SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck), }); _nyma = t; + _settingsQueryDelegate = new LibNymaCore.FrontendSettingQuery(SettingsQuery); + var fn = game.FilesystemSafeName(); using (_exe.EnterExit()) { - var fn = game.FilesystemSafeName(); - + _nyma.PreInit(); + InitSyncSettingsInfo(); _exe.AddReadonlyFile(rom, fn); + _nyma.SetFrontendSettingQuery(_settingsQueryDelegate); var didInit = _nyma.Init(new LibNymaCore.InitData { @@ -72,17 +80,24 @@ namespace BizHawk.Emulation.Cores.Waterbox } VsyncNumerator = info.FpsFixed; VsyncDenominator = 1 << 24; - _soundBuffer = new short[22050 * 2]; InitControls(); - + _nyma.SetFrontendSettingQuery(null); PostInit(); + SettingsInfo.LayerNames = GetLayerData(); + _nyma.SetFrontendSettingQuery(_settingsQueryDelegate); + PutSettings(_settings); } return t; } + protected override void LoadStateBinaryInternal(BinaryReader reader) + { + _nyma.SetFrontendSettingQuery(_settingsQueryDelegate); + } + // todo: bleh private GCHandle _frameAdvanceInputLock; @@ -107,31 +122,28 @@ namespace BizHawk.Emulation.Cores.Waterbox public DisplayType Region { get; protected set; } /// - /// Gets a string array of valid layers to pass to SetLayers, or null if that method should not be called + /// Gets a string array of valid layers to pass to SetLayers, or an empty list if that method should not be called /// - private string[] GetLayerData() + private List GetLayerData() { - using (_exe.EnterExit()) + var ret = new List(); + var p = _nyma.GetLayerData(); + if (p == null) + return ret; + var q = p; + while (true) { - var p = _nyma.GetLayerData(); - if (p == null) - return null; - var ret = new List(); - var q = p; - while (true) + if (*q == 0) { - if (*q == 0) - { - if (q > p) - ret.Add(Mershul.PtrToStringUtf8((IntPtr)p)); - else - break; - p = ++q; - } - q++; + if (q > p) + ret.Add(Mershul.PtrToStringUtf8((IntPtr)p)); + else + break; + p = q + 1; } - return ret.ToArray(); + q++; } + return ret; } } } diff --git a/waterbox/nyma/.vscode/settings.json b/waterbox/nyma/.vscode/settings.json new file mode 100644 index 0000000000..80af7469b9 --- /dev/null +++ b/waterbox/nyma/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "*.mak": "makefile", + "typeinfo": "cpp" + } +} \ No newline at end of file diff --git a/waterbox/nyma/Interfaces.cpp b/waterbox/nyma/Interfaces.cpp index f3e7970369..21e40e9d73 100644 --- a/waterbox/nyma/Interfaces.cpp +++ b/waterbox/nyma/Interfaces.cpp @@ -8,6 +8,15 @@ #include #include +#include + +enum { SETTING_VALUE_MAX_LENGTH = 256 }; + +static void (*FrontendSettingQuery)(const char* setting, char* dest); +ECL_EXPORT void SetFrontendSettingQuery(void (*q)(const char* setting, char* dest)) +{ + FrontendSettingQuery = q; +} namespace Mednafen { @@ -76,37 +85,50 @@ namespace Mednafen uint64 MDFN_GetSettingUI(const char *name) { auto s = GetSetting(name); - if (s) - return strtoul(s->default_value, nullptr, 10); - return 0; + char tmp[SETTING_VALUE_MAX_LENGTH]; + FrontendSettingQuery(name, tmp); + if (s && s->type == MDFNST_ENUM) + { + for (int i = 0; s->enum_list[i].string; i++) + { + if (strcmp(s->enum_list[i].string, tmp) == 0) + return s->enum_list[i].number; + } + for (int i = 0; s->enum_list[i].string; i++) + { + if (strcmp(s->enum_list[i].string, s->default_value) == 0) + return s->enum_list[i].number; + } + return 0; + } + else + { + return strtoul(tmp, nullptr, 10); + } } int64 MDFN_GetSettingI(const char *name) { - auto s = GetSetting(name); - if (s) - return strtol(s->default_value, nullptr, 10); - return 0; + char tmp[SETTING_VALUE_MAX_LENGTH]; + FrontendSettingQuery(name, tmp); + return strtol(tmp, nullptr, 10); } double MDFN_GetSettingF(const char *name) { - auto s = GetSetting(name); - if (s) - return strtod(s->default_value, nullptr); - return 0; + char tmp[SETTING_VALUE_MAX_LENGTH]; + FrontendSettingQuery(name, tmp); + return strtod(tmp, nullptr); } bool MDFN_GetSettingB(const char *name) { - auto s = GetSetting(name); - if (s) - return strtol(s->default_value, nullptr, 10) != 0; - return 0; + char tmp[SETTING_VALUE_MAX_LENGTH]; + FrontendSettingQuery(name, tmp); + return strtol(tmp, nullptr, 10) != 0; } std::string MDFN_GetSettingS(const char *name) { - auto s = GetSetting(name); - if (s) - return s->default_value; - return ""; + char tmp[SETTING_VALUE_MAX_LENGTH]; + FrontendSettingQuery(name, tmp); + return std::string(tmp); } void MDFNMP_Init(uint32 ps, uint32 numpages) diff --git a/waterbox/nyma/NymaCore.cpp b/waterbox/nyma/NymaCore.cpp index c13af185dc..293acd79c0 100644 --- a/waterbox/nyma/NymaCore.cpp +++ b/waterbox/nyma/NymaCore.cpp @@ -26,12 +26,15 @@ enum { MAX_PORTS = 16 }; enum { MAX_PORT_DATA = 16 }; static uint8_t InputPortData[MAX_PORTS * MAX_PORT_DATA]; +ECL_EXPORT void PreInit() +{ + SetupMDFNGameInfo(); +} + ECL_EXPORT bool Init(const InitData& data) { try { - SetupMDFNGameInfo(); - pixels = new uint32_t[Game->fb_width * Game->fb_height]; samples = new int16_t[22050 * 2]; Surf = new MDFN_Surface( @@ -335,3 +338,48 @@ ECL_EXPORT void SetInputDevices(const char** devices) Game->SetInput(port, dev.c_str(), &InputPortData[port * MAX_PORT_DATA]); } } + +struct NSetting +{ + const char* Name; + const char* Description; + const char* SettingsKey; + const char* DefaultValue; + const char* Min; + const char* Max; + uint32_t Flags; + uint32_t Type; +}; +struct NEnumValue +{ + const char* Name; + const char* Description; + const char* Value; +}; + +ECL_EXPORT void IterateSettings(int index, NSetting& s) +{ + auto& a = Game->Settings[index]; + if (a.name) + { + s.Name = a.description; + s.Description = a.description_extra; + s.SettingsKey = a.name; + s.DefaultValue = a.default_value; + s.Min = a.minimum; + s.Max = a.maximum; + s.Flags = a.flags; + s.Type = a.type; + } +} + +ECL_EXPORT void IterateSettingEnums(int index, int enumIndex, NEnumValue& e) +{ + auto& a = Game->Settings[index].enum_list[enumIndex]; + if (a.string) + { + e.Name = a.description; + e.Description = a.description_extra; + e.Value = a.string; + } +}