settings infra
This commit is contained in:
parent
daaa28f451
commit
8c9f4e24d8
|
@ -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;
|
||||
|
|
|
@ -10,6 +10,8 @@ namespace BizHawk.Common
|
|||
/// </summary>
|
||||
public static unsafe string PtrToStringUtf8(IntPtr p)
|
||||
{
|
||||
if (p == IntPtr.Zero)
|
||||
return null;
|
||||
byte* b = (byte*)p;
|
||||
int len = 0;
|
||||
while (*b++ != 0)
|
||||
|
|
|
@ -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<LibNymaCore>(game, rom, "pce.wbx", extension);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,15 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
public string FileNameFull;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do this before calling anything, even settings queries
|
||||
/// </summary>
|
||||
[BizImport(CC, Compatibility = true)]
|
||||
public abstract void PreInit();
|
||||
|
||||
/// <summary>
|
||||
/// Load a ROM
|
||||
/// </summary>
|
||||
[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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|||
/// </summary>
|
||||
public string[] Devices { get; }
|
||||
public ControllerDefinition Definition { get; }
|
||||
public ControllerAdapter(LibNymaCore core, string[] config)
|
||||
public ControllerAdapter(LibNymaCore core, IList<string> 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
|
||||
|
|
|
@ -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<NymaCore.NymaSettings, NymaCore.NymaSyncSettings>
|
||||
{
|
||||
public NymaSettingsInfo SettingsInfo { get; private set; }
|
||||
private NymaSettings _settings;
|
||||
private NymaSyncSettings _syncSettings;
|
||||
/// <summary>
|
||||
/// What this core was actually started with
|
||||
/// </summary>
|
||||
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<string> DisabledLayers { get; set; } = new HashSet<string>();
|
||||
public NymaSettings Clone()
|
||||
{
|
||||
return new NymaSettings { DisabledLayers = new HashSet<string>(DisabledLayers) };
|
||||
}
|
||||
}
|
||||
|
||||
public class NymaSyncSettings
|
||||
{
|
||||
public Dictionary<string, string> MednafenValues { get; set; } = new Dictionary<string, string>();
|
||||
public List<string> PortDevices { get; set; } = new List<string>();
|
||||
public NymaSyncSettings Clone()
|
||||
{
|
||||
return new NymaSyncSettings
|
||||
{
|
||||
MednafenValues = new Dictionary<string, string>(MednafenValues),
|
||||
PortDevices = new List<string>(PortDevices)
|
||||
};
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is NymaSyncSettings x))
|
||||
return false;
|
||||
return PortDevices.SequenceEqual(x.PortDevices)
|
||||
&& new HashSet<KeyValuePair<string, string>>(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
|
||||
{
|
||||
/// <summary>
|
||||
/// What layers are available to toggle. If empty, layers cannot be set on this core.
|
||||
/// </summary>
|
||||
public List<string> 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<Device> AllowedDevices { get; set; } = new List<Device>();
|
||||
public string DefaultSettingsValue { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// What devices can be plugged into each port
|
||||
/// </summary>
|
||||
public List<Port> Ports { get; set; } = new List<Port>();
|
||||
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)
|
||||
/// <summary>
|
||||
/// 0 or 1
|
||||
/// </summary>
|
||||
BOOL,
|
||||
/// <summary>
|
||||
/// float64
|
||||
/// </summary>
|
||||
FLOAT,
|
||||
STRING,
|
||||
/// <summary>
|
||||
/// string value from a list of potential strings
|
||||
/// </summary>
|
||||
ENUM,
|
||||
/// <summary>
|
||||
/// TODO: How do these work
|
||||
/// </summary>
|
||||
MULTI_ENUM,
|
||||
/// <summary>
|
||||
/// Shouldn't see any of these
|
||||
/// </summary>
|
||||
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<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>();
|
||||
}
|
||||
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<NymaSettingsInfo.MednaSetting.EnumValue>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<T>(GameInfo game, byte[] rom, string filename, string extension)
|
||||
protected T DoInit<T>(GameInfo game, byte[] rom, string wbxFilename, string extension)
|
||||
where T : LibNymaCore
|
||||
{
|
||||
var t = PreInit<T>(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; }
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
private string[] GetLayerData()
|
||||
private List<string> GetLayerData()
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
var ret = new List<string>();
|
||||
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<string>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"*.mak": "makefile",
|
||||
"typeinfo": "cpp"
|
||||
}
|
||||
}
|
|
@ -8,6 +8,15 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <emulibc.h>
|
||||
|
||||
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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue