Move ngp over to nyma core and update to latest mednafen

It's seen some upstream updates recently, so that's cool
This commit is contained in:
nattthebear 2020-05-27 18:42:51 -04:00
parent bf544e02fa
commit 2afe356fc9
18 changed files with 195 additions and 552 deletions

Binary file not shown.

View File

@ -529,7 +529,7 @@ namespace BizHawk.Client.Common
{ {
nextEmulator = new TerboGrafix(game, new[] { disc }, nextComm, nextEmulator = new TerboGrafix(game, new[] { disc }, nextComm,
(Emulation.Cores.Waterbox.NymaCore.NymaSettings)GetCoreSettings<TerboGrafix>(), (Emulation.Cores.Waterbox.NymaCore.NymaSettings)GetCoreSettings<TerboGrafix>(),
(Emulation.Cores.Waterbox.NymaCore.NymaSyncSettings)GetCoreSyncSettings<TerboGrafix>()); (Emulation.Cores.Waterbox.NymaCore.NymaSyncSettings)GetCoreSyncSettings<TerboGrafix>(), Deterministic);
} }
break; break;

View File

@ -11,22 +11,22 @@ using BizHawk.Emulation.DiscSystem;
namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE 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 public class TerboGrafix : NymaCore, IRegionable, IPceGpuView
{ {
private readonly LibTerboGrafix _terboGrafix; private readonly LibTerboGrafix _terboGrafix;
[CoreConstructor(new[] { "PCE", "SGX" })] [CoreConstructor(new[] { "PCE", "SGX" })]
public TerboGrafix(GameInfo game, byte[] rom, CoreComm comm, string extension, 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) : base(comm, "PCE", "PC Engine Controller", settings, syncSettings)
{ {
if (game["BRAM"]) if (game["BRAM"])
SettingsOverrides["pce.disable_bram_hucard"] = "0"; SettingsOverrides["pce.disable_bram_hucard"] = "0";
_terboGrafix = DoInit<LibTerboGrafix>(game, rom, null, "pce.wbx", extension); _terboGrafix = DoInit<LibTerboGrafix>(game, rom, null, "pce.wbx", extension, deterministic);
} }
public TerboGrafix(GameInfo game, Disc[] discs, CoreComm comm, 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) : base(comm, "PCE", "PC Engine Controller", settings, syncSettings)
{ {
var firmwares = new Dictionary<string, byte[]>(); var firmwares = new Dictionary<string, byte[]>();
@ -36,7 +36,7 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
firmwares.Add("FIRMWARE:syscard3.pce", comm.CoreFileProvider.GetFirmware("PCECD", "Bios", true)); firmwares.Add("FIRMWARE:syscard3.pce", comm.CoreFileProvider.GetFirmware("PCECD", "Bios", true));
if (types.Contains(DiscType.TurboGECD)) if (types.Contains(DiscType.TurboGECD))
firmwares.Add("FIRMWARE:gecard.pce", comm.CoreFileProvider.GetFirmware("PCECD", "GE-Bios", true)); firmwares.Add("FIRMWARE:gecard.pce", comm.CoreFileProvider.GetFirmware("PCECD", "GE-Bios", true));
_terboGrafix = DoInit<LibTerboGrafix>(game, null, discs, "pce.wbx", null, firmwares); _terboGrafix = DoInit<LibTerboGrafix>(game, null, discs, "pce.wbx", null, deterministic, firmwares);
} }
public override string SystemId => IsSgx ? "SGX" : "PCE"; public override string SystemId => IsSgx ? "SGX" : "PCE";

View File

@ -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<ISoundProvider>(_soundProvider);
// _videoProvider = new SideBySideVideo(_left, _right);
// _serviceProvider.Register<IVideoProvider>(_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<LinkRequest> LeftIn = new BlockingCollection<LinkRequest>();
public readonly BlockingCollection<LinkResult> LeftOut = new BlockingCollection<LinkResult>();
public readonly BlockingCollection<LinkRequest> RightIn = new BlockingCollection<LinkRequest>();
public readonly BlockingCollection<LinkResult> RightOut = new BlockingCollection<LinkResult>();
private readonly Queue<byte> _leftData = new Queue<byte>();
private readonly Queue<byte> _rightData = new Queue<byte>();
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<LinkRequest> _push;
private readonly BlockingCollection<LinkResult> _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<LinkRequest> push, BlockingCollection<LinkResult> 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;
}
}
}
}

View File

@ -5,35 +5,11 @@ using System.Runtime.InteropServices;
namespace BizHawk.Emulation.Cores.Consoles.SNK 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)] [BizImport(CC)]
public abstract bool LoadSystem(byte[] rom, int romlength, Language language); public abstract bool GetSaveRam();
[BizImport(CC)] [BizImport(CC)]
public abstract void SetLayers(int enable); // 1, 2, 4 bg,fg,sprites public abstract bool PutSaveRam();
[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);
} }
} }

View File

@ -9,188 +9,47 @@ using System.Runtime.InteropServices;
namespace BizHawk.Emulation.Cores.Consoles.SNK 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)] "https://mednafen.github.io/releases/", false)]
public class NeoGeoPort : WaterboxCore, public class NeoGeoPort : NymaCore,
ISaveRam, // NGP provides its own saveram interface ISaveRam // NGP provides its own saveram interface
ISettable<object, NeoGeoPort.SyncSettings>
{ {
internal LibNeoGeoPort _neopop; private readonly LibNeoGeoPort _neopop;
[CoreConstructor("NGP")] [CoreConstructor("NGP")]
public NeoGeoPort(CoreComm comm, byte[] rom, SyncSettings syncSettings, bool deterministic) public NeoGeoPort(CoreComm comm, byte[] rom, GameInfo game,
: this(comm, rom, syncSettings, deterministic, WaterboxHost.CanonicalStart) NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic, string extension)
: base(comm, "NGP", "NeoGeo Portable Controller", settings, syncSettings)
{ {
_neopop = DoInit<LibNeoGeoPort>(game, rom, null, "ngp.wbx", extension, deterministic);
} }
internal NeoGeoPort(CoreComm comm, byte[] rom, SyncSettings syncSettings, bool deterministic, ulong startAddress) public new bool SaveRamModified
:base(comm, new Configuration
{
DefaultFpsNumerator = 6144000,
DefaultFpsDenominator = 515 * 198,
DefaultWidth = 160,
DefaultHeight = 152,
MaxWidth = 160,
MaxHeight = 152,
MaxSamples = 8192,
SystemId = "NGP"
})
{ {
if (rom.Length > 4 * 1024 * 1024) get
throw new InvalidOperationException("ROM too big!");
_syncSettings = syncSettings ?? new SyncSettings();
_neopop = PreInit<LibNeoGeoPort>(new WaterboxOptions
{ {
Filename = "ngp.wbx", _exe.AddTransientFile(new byte[0], "SAV:flash");
SbrkHeapSizeKB = 256, if (!_neopop.GetSaveRam())
SealedHeapSizeKB = 5 * 1024, // must be a bit larger than the ROM size throw new InvalidOperationException("Error divining saveram");
InvisibleHeapSizeKB = 4, return _exe.RemoveTransientFile("SAV:flash").Length > 0;
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<string, int> ButtonOrdinals = new Dictionary<string, int>
{
["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);
} }
} }
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() public new byte[] CloneSaveRam()
{ {
byte[] ret = null; _exe.AddTransientFile(new byte[0], "SAV:flash");
_neopop.GetSaveRam((data, size) =>
{ if (!_neopop.GetSaveRam())
ret = new byte[size]; throw new InvalidOperationException("Error returning saveram");
Marshal.Copy(data, ret, 0, size); return _exe.RemoveTransientFile("SAV:flash");
});
return ret;
} }
public new void StoreSaveRam(byte[] data) 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"); throw new InvalidOperationException("Core rejected the saveram");
_exe.RemoveTransientFile("SAV:flash");
} }
} }
} }

View File

@ -88,6 +88,10 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// raw data for each input port, assumed to be MAX_PORTS * MAX_PORT_DATA long /// raw data for each input port, assumed to be MAX_PORTS * MAX_PORT_DATA long
/// </summary> /// </summary>
public byte* InputPortData; public byte* InputPortData;
/// <summary>
/// If the core calls time functions, this is the value that will be used
/// </summary>
public long FrontendTime;
} }
/// <summary> /// <summary>

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -77,7 +78,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
return new NymaSyncSettings return new NymaSyncSettings
{ {
MednafenValues = new Dictionary<string, string>(MednafenValues), MednafenValues = new Dictionary<string, string>(MednafenValues),
PortDevices = new Dictionary<int, string>(PortDevices) PortDevices = new Dictionary<int, string>(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); SettingsOverrides.TryGetValue(name, out var val);
if (val == null) 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); var bytes = Encoding.UTF8.GetBytes(val);
if (bytes.Length > 255) if (bytes.Length > 255)
throw new InvalidOperationException($"Value {val} for setting {name} was too long"); 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); Value = Mershul.PtrToStringUtf8(s.Value);
} }
} }
public MednaSetting()
{}
public MednaSetting(MednaSettingS s) public MednaSetting(MednaSettingS s)
{ {
Name = Mershul.PtrToStringUtf8(s.Name); Name = Mershul.PtrToStringUtf8(s.Name);
@ -320,7 +328,34 @@ namespace BizHawk.Emulation.Cores.Waterbox
} }
s.HiddenSettings = new HashSet<string>(SettingsOverrides.Keys); s.HiddenSettings = new HashSet<string>(SettingsOverrides.Keys);
foreach (var ss in ExtraSettings)
{
s.Settings.Add(ss);
s.SettingsByKey.Add(ss.SettingsKey, ss);
}
SettingsInfo = s; SettingsInfo = s;
} }
private static IReadOnlyCollection<NymaSettingsInfo.MednaSetting> ExtraSettings = new List<NymaSettingsInfo.MednaSetting>
{
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
},
};
} }
} }

View File

@ -23,7 +23,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
} }
private LibNymaCore _nyma; private LibNymaCore _nyma;
protected T DoInit<T>(GameInfo game, byte[] rom, Disc[] discs, string wbxFilename, string extension, protected T DoInit<T>(GameInfo game, byte[] rom, Disc[] discs, string wbxFilename, string extension, bool deterministic,
ICollection<KeyValuePair<string, byte[]>> firmwares = null) ICollection<KeyValuePair<string, byte[]>> firmwares = null)
where T : LibNymaCore where T : LibNymaCore
{ {
@ -115,12 +115,25 @@ namespace BizHawk.Emulation.Cores.Waterbox
InitControls(); InitControls();
_nyma.SetFrontendSettingQuery(null); _nyma.SetFrontendSettingQuery(null);
_nyma.SetCDCallbacks(null, null); if (_disks != null)
_nyma.SetCDCallbacks(null, null);
PostInit(); PostInit();
SettingsInfo.LayerNames = GetLayerData(); SettingsInfo.LayerNames = GetLayerData();
_nyma.SetFrontendSettingQuery(_settingsQueryDelegate); _nyma.SetFrontendSettingQuery(_settingsQueryDelegate);
_nyma.SetCDCallbacks(_cdTocCallback, _cdSectorCallback); if (_disks != null)
_nyma.SetCDCallbacks(_cdTocCallback, _cdSectorCallback);
PutSettings(_settings); 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; return t;
@ -129,7 +142,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
protected override void LoadStateBinaryInternal(BinaryReader reader) protected override void LoadStateBinaryInternal(BinaryReader reader)
{ {
_nyma.SetFrontendSettingQuery(_settingsQueryDelegate); _nyma.SetFrontendSettingQuery(_settingsQueryDelegate);
_nyma.SetCDCallbacks(_cdTocCallback, _cdSectorCallback); if (_disks != null)
_nyma.SetCDCallbacks(_cdTocCallback, _cdSectorCallback);
} }
// todo: bleh // todo: bleh
@ -149,7 +163,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
: controller.IsPressed("Reset") : controller.IsPressed("Reset")
? LibNymaCore.CommandType.RESET ? LibNymaCore.CommandType.RESET
: LibNymaCore.CommandType.NONE, : LibNymaCore.CommandType.NONE,
InputPortData = (byte*)_frameAdvanceInputLock.AddrOfPinnedObject() InputPortData = (byte*)_frameAdvanceInputLock.AddrOfPinnedObject(),
FrontendTime = GetRtcTime(SettingsQuery("nyma.rtcrealtime") != "0"),
}; };
return ret; return ret;
} }

3
waterbox/nyma/.gitignore vendored Normal file
View File

@ -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

View File

@ -5,6 +5,7 @@
#include "mednafen/src/mempatcher.h" #include "mednafen/src/mempatcher.h"
#include "mednafen/src/mednafen-driver.h" #include "mednafen/src/mednafen-driver.h"
#include "mednafen/src/player.h" #include "mednafen/src/player.h"
#include "mednafen/src/Time.h"
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
@ -193,4 +194,30 @@ namespace Mednafen
{} {}
void Player_Draw(MDFN_Surface *surface, MDFN_Rect *dr, int CurrentSong, int16 *samples, int32 sampcount) 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;
}
}
} }

View File

@ -28,6 +28,7 @@ static uint8_t InputPortData[(MAX_PORTS + 1) * MAX_PORT_DATA];
bool LagFlag; bool LagFlag;
void (*InputCallback)(); void (*InputCallback)();
int64_t FrontendTime = 1555555555555;
ECL_EXPORT void PreInit() ECL_EXPORT void PreInit()
{ {
@ -36,8 +37,8 @@ ECL_EXPORT void PreInit()
static void Setup() static void Setup()
{ {
pixels = new uint32_t[Game->fb_width * Game->fb_height]; pixels = (uint32_t*)alloc_invisible(Game->fb_width * Game->fb_height * sizeof(*pixels));
samples = new int16_t[22050 * 2]; samples = (int16_t*)alloc_invisible(22050 * 2 * sizeof(*samples));
Surf = new MDFN_Surface( Surf = new MDFN_Surface(
pixels, Game->fb_width, Game->fb_height, Game->fb_width, pixels, Game->fb_width, Game->fb_height, Game->fb_width,
MDFN_PixelFormat(MDFN_COLORSPACE_RGB, 16, 8, 0, 24) MDFN_PixelFormat(MDFN_COLORSPACE_RGB, 16, 8, 0, 24)
@ -105,10 +106,12 @@ struct MyFrameInfo: public FrameInfo
int32_t Command; int32_t Command;
// raw data for each input port, assumed to be MAX_PORTS * MAX_PORT_DATA long // raw data for each input port, assumed to be MAX_PORTS * MAX_PORT_DATA long
uint8_t* InputPortData; uint8_t* InputPortData;
int64_t FrontendTime;
}; };
ECL_EXPORT void FrameAdvance(MyFrameInfo& frame) ECL_EXPORT void FrameAdvance(MyFrameInfo& frame)
{ {
FrontendTime = frame.FrontendTime;
LagFlag = true; LagFlag = true;
EES->skip = frame.SkipRendering; EES->skip = frame.SkipRendering;
@ -117,7 +120,8 @@ ECL_EXPORT void FrameAdvance(MyFrameInfo& frame)
memcpy(InputPortData, frame.InputPortData, sizeof(InputPortData)); memcpy(InputPortData, frame.InputPortData, sizeof(InputPortData));
Game->TransformInput(); if (Game->TransformInput)
Game->TransformInput();
Game->Emulate(EES); Game->Emulate(EES);
EES->VideoFormatChanged = false; EES->VideoFormatChanged = false;

View File

@ -0,0 +1,13 @@
#include <stdlib.h>
#include <emulibc.h>
// 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();
}

View File

@ -39,7 +39,6 @@ SRCS := \
mednafen/src/file.cpp \ mednafen/src/file.cpp \
mednafen/src/NativeVFS.cpp \ mednafen/src/NativeVFS.cpp \
mednafen/src/IPSPatcher.cpp \ mednafen/src/IPSPatcher.cpp \
mednafen/src/Time.cpp \
mednafen/src/git.cpp \ mednafen/src/git.cpp \
mednafen/src/endian.cpp \ mednafen/src/endian.cpp \
$(call cppdir,string) \ $(call cppdir,string) \

View File

@ -16,3 +16,4 @@ CheatArea* FindCheatArea(uint32_t address);
extern bool LagFlag; extern bool LagFlag;
extern void (*InputCallback)(); extern void (*InputCallback)();
extern int64_t FrontendTime;

@ -1 +1 @@
Subproject commit fe36c52dffe228848e41680d7d4008e76816c381 Subproject commit 593da4feb3846c476973d93f44b20f493a44738f

View File

@ -2,6 +2,8 @@
#include "nyma.h" #include "nyma.h"
#include <emulibc.h> #include <emulibc.h>
#include "mednafen/src/ngp/neopop.h" #include "mednafen/src/ngp/neopop.h"
#include <src/ngp/flash.h>
#include <waterboxcore.h>
using namespace MDFN_IEN_NGP; using namespace MDFN_IEN_NGP;
@ -11,3 +13,51 @@ void SetupMDFNGameInfo()
{ {
Mednafen::MDFNGameInfo = &EmulatedNGP; 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;
}

View File

@ -3,6 +3,7 @@ include common.mak
SRCS += \ SRCS += \
$(call cppdir,ngp) \ $(call cppdir,ngp) \
$(call cppdir,hw_cpu/z80-fuse) \ $(call cppdir,hw_cpu/z80-fuse) \
cdrom_dummy.cpp \
ngp.cpp ngp.cpp
include ../common.mak include ../common.mak