214 lines
5.4 KiB
C#
214 lines
5.4 KiB
C#
using BizHawk.Common;
|
|
using BizHawk.Common.BizInvoke;
|
|
using BizHawk.Common.BufferExtensions;
|
|
using BizHawk.Emulation.Common;
|
|
using BizHawk.Emulation.Cores.Waterbox;
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace BizHawk.Emulation.Cores.Consoles.SNK
|
|
{
|
|
[Core("NeoPop", "Thomas Klausner, Mednafen Team", true, true, "0.9.44.1",
|
|
"https://mednafen.github.io/releases/", false)]
|
|
public class NeoGeoPort : WaterboxCore,
|
|
ISaveRam, // NGP provides its own saveram interface
|
|
ISettable<object, NeoGeoPort.SyncSettings>
|
|
{
|
|
internal LibNeoGeoPort _neopop;
|
|
|
|
[CoreConstructor("NGP")]
|
|
public NeoGeoPort(CoreComm comm, byte[] rom, SyncSettings syncSettings, bool deterministic)
|
|
: this(comm, rom, syncSettings, deterministic, PeRunner.CanonicalStart)
|
|
{
|
|
}
|
|
|
|
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"
|
|
})
|
|
{
|
|
if (rom.Length > 4 * 1024 * 1024)
|
|
throw new InvalidOperationException("ROM too big!");
|
|
|
|
_syncSettings = syncSettings ?? new SyncSettings();
|
|
|
|
_neopop = PreInit<LibNeoGeoPort>(new PeRunnerOptions
|
|
{
|
|
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
|
|
});
|
|
|
|
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,
|
|
};
|
|
}
|
|
|
|
#region Controller
|
|
|
|
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;
|
|
|
|
#endregion
|
|
|
|
#region ISettable
|
|
|
|
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 bool PutSettings(object o)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public bool PutSyncSettings(SyncSettings o)
|
|
{
|
|
var ret = SyncSettings.NeedsReboot(_syncSettings, o);
|
|
_syncSettings = o;
|
|
return ret;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ISaveram
|
|
|
|
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;
|
|
}
|
|
|
|
public new void StoreSaveRam(byte[] data)
|
|
{
|
|
if (!_neopop.PutSaveRam(data, data.Length))
|
|
throw new InvalidOperationException("Core rejected the saveram");
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|