diff --git a/output/dll/ngp.wbx.gz b/output/dll/ngp.wbx.gz index 8682534364..803a48c03c 100644 Binary files a/output/dll/ngp.wbx.gz and b/output/dll/ngp.wbx.gz differ diff --git a/src/BizHawk.Client.Common/RomLoader.cs b/src/BizHawk.Client.Common/RomLoader.cs index b9be9e9399..0f5c50a2e7 100644 --- a/src/BizHawk.Client.Common/RomLoader.cs +++ b/src/BizHawk.Client.Common/RomLoader.cs @@ -529,7 +529,7 @@ namespace BizHawk.Client.Common { nextEmulator = new TerboGrafix(game, new[] { disc }, nextComm, (Emulation.Cores.Waterbox.NymaCore.NymaSettings)GetCoreSettings(), - (Emulation.Cores.Waterbox.NymaCore.NymaSyncSettings)GetCoreSyncSettings()); + (Emulation.Cores.Waterbox.NymaCore.NymaSyncSettings)GetCoreSyncSettings(), Deterministic); } break; diff --git a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TerboGrafix.cs b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TerboGrafix.cs index d702a07bd5..ed62da62e8 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TerboGrafix.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TerboGrafix.cs @@ -11,22 +11,22 @@ using BizHawk.Emulation.DiscSystem; namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE { - [Core(CoreNames.TurboNyma, "Mednafen Team", true, false, "1.24.3", "", false)] + [Core(CoreNames.TurboNyma, "Mednafen Team", true, false, "1.24.3", "https://mednafen.github.io/releases/", false)] public class TerboGrafix : NymaCore, IRegionable, IPceGpuView { private readonly LibTerboGrafix _terboGrafix; [CoreConstructor(new[] { "PCE", "SGX" })] public TerboGrafix(GameInfo game, byte[] rom, CoreComm comm, string extension, - NymaSettings settings, NymaSyncSettings syncSettings) + NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic) : base(comm, "PCE", "PC Engine Controller", settings, syncSettings) { if (game["BRAM"]) SettingsOverrides["pce.disable_bram_hucard"] = "0"; - _terboGrafix = DoInit(game, rom, null, "pce.wbx", extension); + _terboGrafix = DoInit(game, rom, null, "pce.wbx", extension, deterministic); } public TerboGrafix(GameInfo game, Disc[] discs, CoreComm comm, - NymaSettings settings, NymaSyncSettings syncSettings) + NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic) : base(comm, "PCE", "PC Engine Controller", settings, syncSettings) { var firmwares = new Dictionary(); @@ -36,7 +36,7 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE firmwares.Add("FIRMWARE:syscard3.pce", comm.CoreFileProvider.GetFirmware("PCECD", "Bios", true)); if (types.Contains(DiscType.TurboGECD)) firmwares.Add("FIRMWARE:gecard.pce", comm.CoreFileProvider.GetFirmware("PCECD", "GE-Bios", true)); - _terboGrafix = DoInit(game, null, discs, "pce.wbx", null, firmwares); + _terboGrafix = DoInit(game, null, discs, "pce.wbx", null, deterministic, firmwares); } public override string SystemId => IsSgx ? "SGX" : "PCE"; diff --git a/src/BizHawk.Emulation.Cores/Consoles/SNK/DualNeoGeoPort.cs b/src/BizHawk.Emulation.Cores/Consoles/SNK/DualNeoGeoPort.cs deleted file mode 100644 index 3950dde0dd..0000000000 --- a/src/BizHawk.Emulation.Cores/Consoles/SNK/DualNeoGeoPort.cs +++ /dev/null @@ -1,344 +0,0 @@ -using BizHawk.Common; -using BizHawk.BizInvoke; -using BizHawk.Emulation.Common; -using BizHawk.Emulation.Cores.Sound; -using BizHawk.Emulation.Cores.Waterbox; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Consoles.SNK -{ - [Core("Dual NeoPop", "Thomas Klausner, Mednafen Team, natt", true, false, "0.9.44.1", - "https://mednafen.github.io/releases/", false)] - public class DualNeoGeoPort : IEmulator - { - private NeoGeoPort _left; - private NeoGeoPort _right; - private readonly BasicServiceProvider _serviceProvider; - private bool _disposed = false; - private readonly DualSyncSound _soundProvider; - private readonly SideBySideVideo _videoProvider; - private readonly LinkInterop _leftEnd; - private readonly LinkInterop _rightEnd; - private readonly LinkCable _linkCable; - - [CoreConstructor("DNGP")] - public DualNeoGeoPort(CoreComm comm, byte[] rom, bool deterministic) - { - throw new NotImplementedException(); - // // this will throw on construct: we need to compile two different copies of ngp at different starts - // CoreComm = comm; - // _left = new NeoGeoPort(comm, rom, new NeoGeoPort.SyncSettings { Language = LibNeoGeoPort.Language.English }, deterministic, WaterboxHost.CanonicalStart); - // _right = new NeoGeoPort(comm, rom, new NeoGeoPort.SyncSettings { Language = LibNeoGeoPort.Language.English }, deterministic, WaterboxHost.CanonicalStart); - // _linkCable = new LinkCable(); - // _leftEnd = new LinkInterop(_left, _linkCable.LeftIn, _linkCable.LeftOut); - // _rightEnd = new LinkInterop(_right, _linkCable.RightIn, _linkCable.RightOut); - - - // _serviceProvider = new BasicServiceProvider(this); - // _soundProvider = new DualSyncSound(_left, _right); - // _serviceProvider.Register(_soundProvider); - // _videoProvider = new SideBySideVideo(_left, _right); - // _serviceProvider.Register(_videoProvider); - } - - public bool FrameAdvance(IController controller, bool render, bool rendersound = true) - { - var t1 = Task.Run(() => - { - _left.FrameAdvance(new PrefixController(controller, "P1 "), render, rendersound); - _leftEnd.SignalEndOfFrame(); - }); - var t2 = Task.Run(() => - { - _right.FrameAdvance(new PrefixController(controller, "P2 "), render, rendersound); - _rightEnd.SignalEndOfFrame(); - }); - var t3 = Task.Run(() => - { - _linkCable.RunFrame(); - }); - Task.WaitAll(t1, t2, t3); - Frame++; - _soundProvider.Fetch(); - _videoProvider.Fetch(); - - return true; - } - - private class LinkCable - { - public readonly BlockingCollection LeftIn = new BlockingCollection(); - public readonly BlockingCollection LeftOut = new BlockingCollection(); - public readonly BlockingCollection RightIn = new BlockingCollection(); - public readonly BlockingCollection RightOut = new BlockingCollection(); - - private readonly Queue _leftData = new Queue(); - private readonly Queue _rightData = new Queue(); - - public void RunFrame() - { - LinkRequest l = LeftIn.Take(); - LinkRequest r = RightIn.Take(); - while (true) - { - switch (l.RequestType) - { - case LinkRequest.RequestTypes.EndOfFrame: - if (r.RequestType == LinkRequest.RequestTypes.EndOfFrame) - { - Console.WriteLine("\nEnd of Frame {0} {1}", _leftData.Count, _rightData.Count); - return; - } - break; - case LinkRequest.RequestTypes.Write: - Console.Write("LW "); - _leftData.Enqueue(l.Data); - l = LeftIn.Take(); - continue; - case LinkRequest.RequestTypes.Read: - case LinkRequest.RequestTypes.Poll: - if (_rightData.Count > 0) - { - if (l.RequestType == LinkRequest.RequestTypes.Read) - Console.Write("LR "); - LeftOut.Add(new LinkResult - { - Data = l.RequestType == LinkRequest.RequestTypes.Read ? _rightData.Dequeue() : _rightData.Peek(), - Return = true - }); - l = LeftIn.Take(); - continue; - } - else if (r.RequestType != LinkRequest.RequestTypes.Write) - { - if (l.RequestType == LinkRequest.RequestTypes.Read) - Console.Write("L! "); - LeftOut.Add(new LinkResult - { - Data = l.Data, - Return = false - }); - l = LeftIn.Take(); - continue; - } - else - { - break; - } - } - switch (r.RequestType) - { - case LinkRequest.RequestTypes.Write: - Console.Write("RW "); - _rightData.Enqueue(r.Data); - r = RightIn.Take(); - continue; - case LinkRequest.RequestTypes.Read: - case LinkRequest.RequestTypes.Poll: - if (_leftData.Count > 0) - { - if (r.RequestType == LinkRequest.RequestTypes.Read) - Console.Write("RR "); - RightOut.Add(new LinkResult - { - Data = r.RequestType == LinkRequest.RequestTypes.Read ? _leftData.Dequeue() : _leftData.Peek(), - Return = true - }); - r = RightIn.Take(); - continue; - } - else if (l.RequestType != LinkRequest.RequestTypes.Write) - { - if (r.RequestType == LinkRequest.RequestTypes.Read) - Console.Write("R! "); - RightOut.Add(new LinkResult - { - Data = r.Data, - Return = false - }); - r = RightIn.Take(); - continue; - } - else - { - break; - } - } - } - } - } - - public struct LinkRequest - { - public enum RequestTypes : byte - { - Read, - Poll, - Write, - EndOfFrame - } - public RequestTypes RequestType; - public byte Data; - } - public struct LinkResult - { - public byte Data; - public bool Return; - } - - private unsafe class LinkInterop - { - private readonly BlockingCollection _push; - private readonly BlockingCollection _pull; - private NeoGeoPort _core; - private readonly IntPtr _readcb; - private readonly IntPtr _pollcb; - private readonly IntPtr _writecb; - private readonly IImportResolver _exporter; - - public LinkInterop(NeoGeoPort core, BlockingCollection push, BlockingCollection pull) - { - _core = core; - _push = push; - _pull = pull; - _exporter = BizExvoker.GetExvoker(this, CallingConventionAdapters.Waterbox); - _readcb = _exporter.GetProcAddrOrThrow("CommsReadCallback"); - _pollcb = _exporter.GetProcAddrOrThrow("CommsPollCallback"); - _writecb = _exporter.GetProcAddrOrThrow("CommsWriteCallback"); - ConnectPointers(); - } - - private void ConnectPointers() - { - _core._neopop.SetCommsCallbacks(_readcb, _pollcb, _writecb); - } - - private bool CommsPollNoBuffer() - { - _push.Add(new LinkRequest - { - RequestType = LinkRequest.RequestTypes.Poll - }); - return _pull.Take().Return; - } - - [BizExport(CallingConvention.Cdecl)] - public bool CommsReadCallback(byte* buffer) - { - if (buffer == null) - return CommsPollNoBuffer(); - _push.Add(new LinkRequest - { - RequestType = LinkRequest.RequestTypes.Read, - Data = *buffer - }); - var r = _pull.Take(); - *buffer = r.Data; - return r.Return; - } - [BizExport(CallingConvention.Cdecl)] - public bool CommsPollCallback(byte* buffer) - { - if (buffer == null) - return CommsPollNoBuffer(); - _push.Add(new LinkRequest - { - RequestType = LinkRequest.RequestTypes.Poll, - Data = *buffer - }); - var r = _pull.Take(); - *buffer = r.Data; - return r.Return; - } - [BizExport(CallingConvention.Cdecl)] - public void CommsWriteCallback(byte data) - { - _push.Add(new LinkRequest - { - RequestType = LinkRequest.RequestTypes.Write, - Data = data - }); - } - - public void SignalEndOfFrame() - { - _push.Add(new LinkRequest - { - RequestType = LinkRequest.RequestTypes.EndOfFrame - }); - } - - public void PostLoadState() - { - ConnectPointers(); - } - } - - private class PrefixController : IController - { - public PrefixController(IController controller, string prefix) - { - _controller = controller; - _prefix = prefix; - } - - private readonly IController _controller; - private readonly string _prefix; - - public ControllerDefinition Definition => null; - - public int AxisValue(string name) - { - return _controller.AxisValue(_prefix + name); - } - - public bool IsPressed(string button) - { - return _controller.IsPressed(_prefix + button); - } - } - - public ControllerDefinition ControllerDefinition => DualNeoGeoPortController; - - private static readonly ControllerDefinition DualNeoGeoPortController = new ControllerDefinition - { - BoolButtons = - { - "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 A", "P1 B", "P1 Option", "P1 Power", - "P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 A", "P2 B", "P2 Option", "P2 Power" - }, - Name = "Dual NeoGeo Portable Controller" - }; - - public void ResetCounters() - { - Frame = 0; - } - - public int Frame { get; private set; } - - public IEmulatorServiceProvider ServiceProvider => _serviceProvider; - - public CoreComm CoreComm { get; } - - public bool DeterministicEmulation => _left.DeterministicEmulation && _right.DeterministicEmulation; - - public string SystemId => "DNGP"; - - public void Dispose() - { - if (!_disposed) - { - _left.Dispose(); - _right.Dispose(); - _left = null; - _right = null; - _disposed = true; - } - } - } -} diff --git a/src/BizHawk.Emulation.Cores/Consoles/SNK/LibNeoGeoPort.cs b/src/BizHawk.Emulation.Cores/Consoles/SNK/LibNeoGeoPort.cs index 3f6540253f..76ef267de3 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/SNK/LibNeoGeoPort.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/SNK/LibNeoGeoPort.cs @@ -5,35 +5,11 @@ using System.Runtime.InteropServices; namespace BizHawk.Emulation.Cores.Consoles.SNK { - public abstract class LibNeoGeoPort : LibWaterboxCore + public abstract class LibNeoGeoPort : LibNymaCore { - [StructLayout(LayoutKind.Sequential)] - public new class FrameInfo : LibWaterboxCore.FrameInfo - { - public long FrontendTime; - public int SkipRendering; - public int Buttons; - } - public enum Language - { - Japanese, English - } - [UnmanagedFunctionPointer(CC)] - public delegate void SaveRamCallback(IntPtr data, int length); - [BizImport(CC)] - public abstract bool LoadSystem(byte[] rom, int romlength, Language language); + public abstract bool GetSaveRam(); [BizImport(CC)] - public abstract void SetLayers(int enable); // 1, 2, 4 bg,fg,sprites - [BizImport(CC)] - public abstract void HardReset(); - [BizImport(CC)] - public abstract void SetCommsCallbacks(IntPtr readcb, IntPtr pollcb, IntPtr writecb); - [BizImport(CC)] - public abstract bool HasSaveRam(); - [BizImport(CC)] - public abstract bool PutSaveRam(byte[] data, int length); - [BizImport(CC)] - public abstract void GetSaveRam(SaveRamCallback callback); + public abstract bool PutSaveRam(); } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeoPort.cs b/src/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeoPort.cs index 6f48682424..cbcb82a7dd 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeoPort.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeoPort.cs @@ -9,188 +9,47 @@ using System.Runtime.InteropServices; namespace BizHawk.Emulation.Cores.Consoles.SNK { - [Core("NeoPop", "Thomas Klausner, Mednafen Team", true, true, "0.9.44.1", + [Core("NeoPop", "Thomas Klausner, Mednafen Team", true, true, "1.24.3", "https://mednafen.github.io/releases/", false)] - public class NeoGeoPort : WaterboxCore, - ISaveRam, // NGP provides its own saveram interface - ISettable + public class NeoGeoPort : NymaCore, + ISaveRam // NGP provides its own saveram interface { - internal LibNeoGeoPort _neopop; + private readonly LibNeoGeoPort _neopop; [CoreConstructor("NGP")] - public NeoGeoPort(CoreComm comm, byte[] rom, SyncSettings syncSettings, bool deterministic) - : this(comm, rom, syncSettings, deterministic, WaterboxHost.CanonicalStart) + public NeoGeoPort(CoreComm comm, byte[] rom, GameInfo game, + NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic, string extension) + : base(comm, "NGP", "NeoGeo Portable Controller", settings, syncSettings) { + _neopop = DoInit(game, rom, null, "ngp.wbx", extension, deterministic); } - internal NeoGeoPort(CoreComm comm, byte[] rom, SyncSettings syncSettings, bool deterministic, ulong startAddress) - :base(comm, new Configuration - { - DefaultFpsNumerator = 6144000, - DefaultFpsDenominator = 515 * 198, - DefaultWidth = 160, - DefaultHeight = 152, - MaxWidth = 160, - MaxHeight = 152, - MaxSamples = 8192, - SystemId = "NGP" - }) + public new bool SaveRamModified { - if (rom.Length > 4 * 1024 * 1024) - throw new InvalidOperationException("ROM too big!"); - - _syncSettings = syncSettings ?? new SyncSettings(); - - _neopop = PreInit(new WaterboxOptions + get { - Filename = "ngp.wbx", - SbrkHeapSizeKB = 256, - SealedHeapSizeKB = 5 * 1024, // must be a bit larger than the ROM size - InvisibleHeapSizeKB = 4, - PlainHeapSizeKB = 5 * 1024, // must be a bit larger than the ROM size - StartAddress = startAddress, - SkipCoreConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck), - SkipMemoryConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck), - }); - - if (!_neopop.LoadSystem(rom, rom.Length, _syncSettings.Language)) - throw new InvalidOperationException("Core rejected the rom"); - - PostInit(); - - DeterministicEmulation = deterministic || !_syncSettings.UseRealTime; - InitializeRtc(_syncSettings.InitialTime); - } - - protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound) - { - if (controller.IsPressed("Power")) - _neopop.HardReset(); - - return new LibNeoGeoPort.FrameInfo - { - FrontendTime = GetRtcTime(!DeterministicEmulation), - Buttons = GetButtons(controller), - SkipRendering = render ? 0 : 1, - }; - } - - private static int GetButtons(IController c) - { - var ret = 0; - var val = 1; - foreach (var s in CoreButtons) - { - if (c.IsPressed(s)) - ret |= val; - val <<= 1; - } - return ret; - } - - private static readonly string[] CoreButtons = - { - "Up", "Down", "Left", "Right", "A", "B", "Option" - }; - - private static readonly Dictionary ButtonOrdinals = new Dictionary - { - ["Up"] = 1, - ["Down"] = 2, - ["Left"] = 3, - ["Right"] = 4, - ["B"] = 9, - ["A"] = 10, - ["R"] = 11, - ["L"] = 12, - ["Option"] = 13 - }; - - private static readonly ControllerDefinition NeoGeoPortableController = new ControllerDefinition - { - Name = "NeoGeo Portable Controller", - BoolButtons = CoreButtons - .OrderBy(b => ButtonOrdinals[b]) - .Concat(new[] { "Power" }) - .ToList() - }; - - public override ControllerDefinition ControllerDefinition => NeoGeoPortableController; - - private SyncSettings _syncSettings; - - public class SyncSettings - { - [DisplayName("Language")] - [Description("Language of the system. Only affects some games.")] - [DefaultValue(LibNeoGeoPort.Language.Japanese)] - public LibNeoGeoPort.Language Language { get; set; } - - [DisplayName("Initial Time")] - [Description("Initial time of emulation. Only relevant when UseRealTime is false.")] - [DefaultValue(typeof(DateTime), "2010-01-01")] - public DateTime InitialTime { get; set; } - - [DisplayName("Use RealTime")] - [Description("If true, RTC clock will be based off of real time instead of emulated time. Ignored (set to false) when recording a movie.")] - [DefaultValue(false)] - public bool UseRealTime { get; set; } - - public SyncSettings Clone() - { - return (SyncSettings)MemberwiseClone(); - } - - public static bool NeedsReboot(SyncSettings x, SyncSettings y) - { - return !DeepEquality.DeepEquals(x, y); - } - - public SyncSettings() - { - SettingsUtil.SetDefaultValues(this); + _exe.AddTransientFile(new byte[0], "SAV:flash"); + if (!_neopop.GetSaveRam()) + throw new InvalidOperationException("Error divining saveram"); + return _exe.RemoveTransientFile("SAV:flash").Length > 0; } } - public object GetSettings() - { - return null; - } - - public SyncSettings GetSyncSettings() - { - return _syncSettings.Clone(); - } - - public PutSettingsDirtyBits PutSettings(object o) - { - return PutSettingsDirtyBits.None; - } - - public PutSettingsDirtyBits PutSyncSettings(SyncSettings o) - { - var ret = SyncSettings.NeedsReboot(_syncSettings, o); - _syncSettings = o; - return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None; - } - - public new bool SaveRamModified => _neopop.HasSaveRam(); - public new byte[] CloneSaveRam() { - byte[] ret = null; - _neopop.GetSaveRam((data, size) => - { - ret = new byte[size]; - Marshal.Copy(data, ret, 0, size); - }); - return ret; + _exe.AddTransientFile(new byte[0], "SAV:flash"); + + if (!_neopop.GetSaveRam()) + throw new InvalidOperationException("Error returning saveram"); + return _exe.RemoveTransientFile("SAV:flash"); } public new void StoreSaveRam(byte[] data) { - if (!_neopop.PutSaveRam(data, data.Length)) + _exe.AddTransientFile(data, "SAV:flash"); + if (!_neopop.PutSaveRam()) throw new InvalidOperationException("Core rejected the saveram"); + _exe.RemoveTransientFile("SAV:flash"); } } } diff --git a/src/BizHawk.Emulation.Cores/Waterbox/LibNymaCore.cs b/src/BizHawk.Emulation.Cores/Waterbox/LibNymaCore.cs index 5abaa2f0b6..1f14e8624a 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/LibNymaCore.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/LibNymaCore.cs @@ -88,6 +88,10 @@ namespace BizHawk.Emulation.Cores.Waterbox /// raw data for each input port, assumed to be MAX_PORTS * MAX_PORT_DATA long /// public byte* InputPortData; + /// + /// If the core calls time functions, this is the value that will be used + /// + public long FrontendTime; } /// diff --git a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Settings.cs b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Settings.cs index ecae0e2df1..173ede44b2 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Settings.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Settings.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -77,7 +78,7 @@ namespace BizHawk.Emulation.Cores.Waterbox return new NymaSyncSettings { MednafenValues = new Dictionary(MednafenValues), - PortDevices = new Dictionary(PortDevices) + PortDevices = new Dictionary(PortDevices), }; } @@ -95,7 +96,7 @@ namespace BizHawk.Emulation.Cores.Waterbox } } - private void SettingsQuery(string name, IntPtr dest) + protected string SettingsQuery(string name) { SettingsOverrides.TryGetValue(name, out var val); if (val == null) @@ -112,7 +113,12 @@ namespace BizHawk.Emulation.Cores.Waterbox } } } + return val; + } + private void SettingsQuery(string name, IntPtr dest) + { + var val = SettingsQuery(name); var bytes = Encoding.UTF8.GetBytes(val); if (bytes.Length > 255) throw new InvalidOperationException($"Value {val} for setting {name} was too long"); @@ -230,6 +236,8 @@ namespace BizHawk.Emulation.Cores.Waterbox Value = Mershul.PtrToStringUtf8(s.Value); } } + public MednaSetting() + {} public MednaSetting(MednaSettingS s) { Name = Mershul.PtrToStringUtf8(s.Name); @@ -320,7 +328,34 @@ namespace BizHawk.Emulation.Cores.Waterbox } s.HiddenSettings = new HashSet(SettingsOverrides.Keys); + foreach (var ss in ExtraSettings) + { + s.Settings.Add(ss); + s.SettingsByKey.Add(ss.SettingsKey, ss); + } SettingsInfo = s; } + + private static IReadOnlyCollection ExtraSettings = new List + { + new NymaSettingsInfo.MednaSetting + { + Name = "Initial Time", + Description = "Initial time of emulation. Only relevant when UseRealTime is false.\nEnter as IS0-8601.", + SettingsKey = "nyma.rtcinitialtime", + DefaultValue = "2010-01-01", + Flags = NymaSettingsInfo.MednaSetting.SettingFlags.EMU_STATE, + Type = NymaSettingsInfo.MednaSetting.SettingType.STRING + }, + new NymaSettingsInfo.MednaSetting + { + Name = "Use RealTime", + Description = "If true, RTC clock will be based off of real time instead of emulated time. Ignored (set to false) when recording a movie.", + SettingsKey = "nyma.rtcrealtime", + DefaultValue = "0", + Flags = NymaSettingsInfo.MednaSetting.SettingFlags.EMU_STATE, + Type = NymaSettingsInfo.MednaSetting.SettingType.BOOL + }, + }; } } diff --git a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs index 7d764e0d93..c3672edfe1 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs @@ -23,7 +23,7 @@ namespace BizHawk.Emulation.Cores.Waterbox } private LibNymaCore _nyma; - protected T DoInit(GameInfo game, byte[] rom, Disc[] discs, string wbxFilename, string extension, + protected T DoInit(GameInfo game, byte[] rom, Disc[] discs, string wbxFilename, string extension, bool deterministic, ICollection> firmwares = null) where T : LibNymaCore { @@ -115,12 +115,25 @@ namespace BizHawk.Emulation.Cores.Waterbox InitControls(); _nyma.SetFrontendSettingQuery(null); - _nyma.SetCDCallbacks(null, null); + if (_disks != null) + _nyma.SetCDCallbacks(null, null); PostInit(); SettingsInfo.LayerNames = GetLayerData(); _nyma.SetFrontendSettingQuery(_settingsQueryDelegate); - _nyma.SetCDCallbacks(_cdTocCallback, _cdSectorCallback); + if (_disks != null) + _nyma.SetCDCallbacks(_cdTocCallback, _cdSectorCallback); PutSettings(_settings); + DateTime RtcStart = DateTime.Parse("2010-01-01"); + try + { + RtcStart = DateTime.Parse(SettingsQuery("nyma.rtcinitialtime")); + } + catch + { + Console.Error.WriteLine($"Couldn't parse DateTime \"{SettingsQuery("nyma.rtcinitialtime")}\""); + } + DeterministicEmulation = deterministic || SettingsQuery("nyma.rtcrealtime") == "0"; + InitializeRtc(RtcStart); } return t; @@ -129,7 +142,8 @@ namespace BizHawk.Emulation.Cores.Waterbox protected override void LoadStateBinaryInternal(BinaryReader reader) { _nyma.SetFrontendSettingQuery(_settingsQueryDelegate); - _nyma.SetCDCallbacks(_cdTocCallback, _cdSectorCallback); + if (_disks != null) + _nyma.SetCDCallbacks(_cdTocCallback, _cdSectorCallback); } // todo: bleh @@ -149,7 +163,8 @@ namespace BizHawk.Emulation.Cores.Waterbox : controller.IsPressed("Reset") ? LibNymaCore.CommandType.RESET : LibNymaCore.CommandType.NONE, - InputPortData = (byte*)_frameAdvanceInputLock.AddrOfPinnedObject() + InputPortData = (byte*)_frameAdvanceInputLock.AddrOfPinnedObject(), + FrontendTime = GetRtcTime(SettingsQuery("nyma.rtcrealtime") != "0"), }; return ret; } diff --git a/waterbox/nyma/.gitignore b/waterbox/nyma/.gitignore new file mode 100644 index 0000000000..677f90725b --- /dev/null +++ b/waterbox/nyma/.gitignore @@ -0,0 +1,3 @@ +# These are already ignored, but for some reason vscode won't pick it up +# and stop showing me things I don't want to see without this +/obj diff --git a/waterbox/nyma/Interfaces.cpp b/waterbox/nyma/Interfaces.cpp index 39161e98f2..fecfe98504 100644 --- a/waterbox/nyma/Interfaces.cpp +++ b/waterbox/nyma/Interfaces.cpp @@ -5,6 +5,7 @@ #include "mednafen/src/mempatcher.h" #include "mednafen/src/mednafen-driver.h" #include "mednafen/src/player.h" +#include "mednafen/src/Time.h" #include #include @@ -193,4 +194,30 @@ namespace Mednafen {} void Player_Draw(MDFN_Surface *surface, MDFN_Rect *dr, int CurrentSong, int16 *samples, int32 sampcount) {} + + namespace Time + { + void Time_Init(void) + {} + int64 EpochTime(void) + { + return FrontendTime; + } + struct tm LocalTime(const int64 ept) + { + // musl's localtime_r gets into a lot of unfun syscalls, and we wouldn't allow changable timezone anyway + return UTCTime(ept); + } + struct tm UTCTime(const int64 ept) + { + struct tm tout; + time_t tt = (time_t)ept; + if(!gmtime_r(&tt, &tout)) + { + ErrnoHolder ene(errno); + throw MDFN_Error(ene.Errno(), _("%s failed: %s"), "gmtime_r()", ene.StrError()); + } + return tout; + } + } } diff --git a/waterbox/nyma/NymaCore.cpp b/waterbox/nyma/NymaCore.cpp index a838eef6f9..f977c1eca7 100644 --- a/waterbox/nyma/NymaCore.cpp +++ b/waterbox/nyma/NymaCore.cpp @@ -28,6 +28,7 @@ static uint8_t InputPortData[(MAX_PORTS + 1) * MAX_PORT_DATA]; bool LagFlag; void (*InputCallback)(); +int64_t FrontendTime = 1555555555555; ECL_EXPORT void PreInit() { @@ -36,8 +37,8 @@ ECL_EXPORT void PreInit() static void Setup() { - pixels = new uint32_t[Game->fb_width * Game->fb_height]; - samples = new int16_t[22050 * 2]; + pixels = (uint32_t*)alloc_invisible(Game->fb_width * Game->fb_height * sizeof(*pixels)); + samples = (int16_t*)alloc_invisible(22050 * 2 * sizeof(*samples)); Surf = new MDFN_Surface( pixels, Game->fb_width, Game->fb_height, Game->fb_width, MDFN_PixelFormat(MDFN_COLORSPACE_RGB, 16, 8, 0, 24) @@ -105,10 +106,12 @@ struct MyFrameInfo: public FrameInfo int32_t Command; // raw data for each input port, assumed to be MAX_PORTS * MAX_PORT_DATA long uint8_t* InputPortData; + int64_t FrontendTime; }; ECL_EXPORT void FrameAdvance(MyFrameInfo& frame) { + FrontendTime = frame.FrontendTime; LagFlag = true; EES->skip = frame.SkipRendering; @@ -117,7 +120,8 @@ ECL_EXPORT void FrameAdvance(MyFrameInfo& frame) memcpy(InputPortData, frame.InputPortData, sizeof(InputPortData)); - Game->TransformInput(); + if (Game->TransformInput) + Game->TransformInput(); Game->Emulate(EES); EES->VideoFormatChanged = false; diff --git a/waterbox/nyma/cdrom_dummy.cpp b/waterbox/nyma/cdrom_dummy.cpp new file mode 100644 index 0000000000..e3b36e150b --- /dev/null +++ b/waterbox/nyma/cdrom_dummy.cpp @@ -0,0 +1,13 @@ +#include +#include + +// for systems that do not include cds, we need just a bit of stub code + +void StartGameWithCds(int numdisks) +{ + abort(); +} +ECL_EXPORT void SetCDCallbacks() +{ + abort(); +} diff --git a/waterbox/nyma/common.mak b/waterbox/nyma/common.mak index 67ac906b74..719c3a629a 100644 --- a/waterbox/nyma/common.mak +++ b/waterbox/nyma/common.mak @@ -39,7 +39,6 @@ SRCS := \ mednafen/src/file.cpp \ mednafen/src/NativeVFS.cpp \ mednafen/src/IPSPatcher.cpp \ - mednafen/src/Time.cpp \ mednafen/src/git.cpp \ mednafen/src/endian.cpp \ $(call cppdir,string) \ diff --git a/waterbox/nyma/common/nyma.h b/waterbox/nyma/common/nyma.h index c16f96a3ab..66766ac166 100644 --- a/waterbox/nyma/common/nyma.h +++ b/waterbox/nyma/common/nyma.h @@ -16,3 +16,4 @@ CheatArea* FindCheatArea(uint32_t address); extern bool LagFlag; extern void (*InputCallback)(); +extern int64_t FrontendTime; diff --git a/waterbox/nyma/mednafen b/waterbox/nyma/mednafen index fe36c52dff..593da4feb3 160000 --- a/waterbox/nyma/mednafen +++ b/waterbox/nyma/mednafen @@ -1 +1 @@ -Subproject commit fe36c52dffe228848e41680d7d4008e76816c381 +Subproject commit 593da4feb3846c476973d93f44b20f493a44738f diff --git a/waterbox/nyma/ngp.cpp b/waterbox/nyma/ngp.cpp index 48acb40608..25f6d991c2 100644 --- a/waterbox/nyma/ngp.cpp +++ b/waterbox/nyma/ngp.cpp @@ -2,6 +2,8 @@ #include "nyma.h" #include #include "mednafen/src/ngp/neopop.h" +#include +#include using namespace MDFN_IEN_NGP; @@ -11,3 +13,51 @@ void SetupMDFNGameInfo() { Mednafen::MDFNGameInfo = &EmulatedNGP; } + +ECL_EXPORT bool GetSaveRam() +{ + try + { + FLASH_SaveNV(); + return true; + } + catch(...) + { + return false; + } +} +ECL_EXPORT bool PutSaveRam() +{ + try + { + FLASH_LoadNV(); + return true; + } + catch(...) + { + return false; + } +} + +namespace MDFN_IEN_NGP +{ + extern uint8 CPUExRAM[16384]; +} + +ECL_EXPORT void GetMemoryAreas(MemoryArea* m) +{ + m[0].Data = CPUExRAM; + m[0].Name = "RAM"; + m[0].Size = 16384; + m[0].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_PRIMARY | MEMORYAREA_FLAGS_WORDSIZE4; + + m[1].Data = ngpc_rom.data; + m[1].Name = "ROM"; + m[1].Size = ngpc_rom.length; + m[1].Flags = MEMORYAREA_FLAGS_WORDSIZE4; + + m[2].Data = ngpc_rom.orig_data; + m[2].Name = "ORIGINAL ROM"; + m[2].Size = ngpc_rom.length; + m[2].Flags = MEMORYAREA_FLAGS_WORDSIZE4; +} diff --git a/waterbox/nyma/ngp.mak b/waterbox/nyma/ngp.mak index 82aeceeeb6..ad25d4c671 100644 --- a/waterbox/nyma/ngp.mak +++ b/waterbox/nyma/ngp.mak @@ -3,6 +3,7 @@ include common.mak SRCS += \ $(call cppdir,ngp) \ $(call cppdir,hw_cpu/z80-fuse) \ + cdrom_dummy.cpp \ ngp.cpp include ../common.mak