Move saturn over to nyma, get latest version.
This commit is contained in:
parent
73e2922a0d
commit
f14dace4a0
Binary file not shown.
|
@ -315,11 +315,11 @@ namespace BizHawk.Client.Common
|
||||||
break;
|
break;
|
||||||
case "SAT":
|
case "SAT":
|
||||||
nextEmulator = new Saturnus(
|
nextEmulator = new Saturnus(
|
||||||
nextComm,
|
nextComm, game,
|
||||||
new[] { disc },
|
new[] { disc },
|
||||||
Deterministic,
|
(NymaCore.NymaSettings)GetCoreSettings<Saturnus>(),
|
||||||
(Saturnus.Settings) GetCoreSettings<Saturnus>(),
|
(NymaCore.NymaSyncSettings)GetCoreSyncSettings<Saturnus>(),
|
||||||
(Saturnus.SyncSettings) GetCoreSyncSettings<Saturnus>()
|
Deterministic
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "PSX":
|
case "PSX":
|
||||||
|
@ -821,11 +821,11 @@ namespace BizHawk.Client.Common
|
||||||
var saturnDiscs = DiscsFromXml(xmlGame, "SAT", DiscType.SegaSaturn);
|
var saturnDiscs = DiscsFromXml(xmlGame, "SAT", DiscType.SegaSaturn);
|
||||||
if (saturnDiscs.Count == 0) return false;
|
if (saturnDiscs.Count == 0) return false;
|
||||||
nextEmulator = new Saturnus(
|
nextEmulator = new Saturnus(
|
||||||
nextComm,
|
nextComm, game,
|
||||||
saturnDiscs,
|
saturnDiscs,
|
||||||
Deterministic,
|
(NymaCore.NymaSettings)GetCoreSettings<Saturnus>(),
|
||||||
(Saturnus.Settings) GetCoreSettings<Saturnus>(),
|
(NymaCore.NymaSyncSettings)GetCoreSyncSettings<Saturnus>(),
|
||||||
(Saturnus.SyncSettings) GetCoreSyncSettings<Saturnus>()
|
Deterministic
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
case "PCFX":
|
case "PCFX":
|
||||||
|
|
|
@ -14,10 +14,23 @@ namespace BizHawk.Client.Common.movie.import
|
||||||
protected override void RunImport()
|
protected override void RunImport()
|
||||||
{
|
{
|
||||||
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "SAT";
|
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "SAT";
|
||||||
var ss = new Saturnus.SyncSettings
|
var ss = new Emulation.Cores.Waterbox.NymaCore.NymaSyncSettings
|
||||||
{
|
{
|
||||||
Port1 = SaturnusControllerDeck.Device.Gamepad,
|
PortDevices =
|
||||||
Port2 = SaturnusControllerDeck.Device.None
|
{
|
||||||
|
{ 0, "gamepad" },
|
||||||
|
{ 1, "none" },
|
||||||
|
{ 2, "none" },
|
||||||
|
{ 3, "none" },
|
||||||
|
{ 4, "none" },
|
||||||
|
{ 5, "none" },
|
||||||
|
{ 6, "none" },
|
||||||
|
{ 7, "none" },
|
||||||
|
{ 8, "none" },
|
||||||
|
{ 9, "none" },
|
||||||
|
{ 10, "none" },
|
||||||
|
{ 11, "none" },
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using var sr = SourceFile.OpenText();
|
using var sr = SourceFile.OpenText();
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
@ -12,6 +13,8 @@ namespace BizHawk.Client.EmuHawk
|
||||||
// ReSharper disable once UnusedMember.Global
|
// ReSharper disable once UnusedMember.Global
|
||||||
public class SaturnSchema : IVirtualPadSchema
|
public class SaturnSchema : IVirtualPadSchema
|
||||||
{
|
{
|
||||||
|
public IEnumerable<PadSchema> GetPadSchemas(IEmulator core) => throw new NotImplementedException();
|
||||||
|
/*
|
||||||
public IEnumerable<PadSchema> GetPadSchemas(IEmulator core)
|
public IEnumerable<PadSchema> GetPadSchemas(IEmulator core)
|
||||||
{
|
{
|
||||||
var ss = ((Saturnus)core).GetSyncSettings();
|
var ss = ((Saturnus)core).GetSyncSettings();
|
||||||
|
@ -282,5 +285,6 @@ namespace BizHawk.Client.EmuHawk
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,10 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
|
||||||
NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
|
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, ValueTuple<string, string>>
|
||||||
{
|
{
|
||||||
{ "FIRMWARE:syscard3.pce", comm.CoreFileProvider.GetFirmware("PCECD", "Bios", true) },
|
{ "FIRMWARE:syscard3.pce", ("PCECD", "Bios") },
|
||||||
|
// { "FIRMWARE:gecard.pce", ("PCECD", "GE-Bios") },
|
||||||
};
|
};
|
||||||
_terboGrafix = DoInit<LibHyperNyma>(game, null, discs, "pce-fast.wbx", null, deterministic, firmwares);
|
_terboGrafix = DoInit<LibHyperNyma>(game, null, discs, "pce-fast.wbx", null, deterministic, firmwares);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,13 +29,11 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
|
||||||
NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
|
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, ValueTuple<string, string>>
|
||||||
var types = discs.Select(d => new DiscIdentifier(d).DetectDiscType())
|
{
|
||||||
.ToList();
|
{ "FIRMWARE:syscard3.pce", ("PCECD", "Bios") },
|
||||||
if (types.Contains(DiscType.TurboCD))
|
{ "FIRMWARE:gecard.pce", ("PCECD", "GE-Bios") },
|
||||||
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<LibTurboNyma>(game, null, discs, "pce.wbx", null, deterministic, firmwares);
|
_terboGrafix = DoInit<LibTurboNyma>(game, null, discs, "pce.wbx", null, deterministic, firmwares);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,13 +28,9 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCFX
|
||||||
IEnumerable<Disc> disks, NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
|
IEnumerable<Disc> disks, NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
|
||||||
: base(comm, "PCFX", "PCFX Controller Deck", settings, syncSettings)
|
: base(comm, "PCFX", "PCFX Controller Deck", settings, syncSettings)
|
||||||
{
|
{
|
||||||
var bios = comm.CoreFileProvider.GetFirmware("PCFX", "BIOS", true);
|
var firmwares = new Dictionary<string, ValueTuple<string, string>>
|
||||||
if (bios.Length != 1024 * 1024)
|
|
||||||
throw new InvalidOperationException("Wrong size BIOS file!");
|
|
||||||
|
|
||||||
var firmwares = new Dictionary<string, byte[]>
|
|
||||||
{
|
{
|
||||||
{ "FIRMWARE:pcfx.rom", bios },
|
{ "FIRMWARE:pcfx.rom", ("PCFX", "BIOS") },
|
||||||
};
|
};
|
||||||
|
|
||||||
DoInit<LibNymaCore>(game, null, disks.ToArray(), "pcfx.wbx", null, deterministic, firmwares);
|
DoInit<LibNymaCore>(game, null, disks.ToArray(), "pcfx.wbx", null, deterministic, firmwares);
|
||||||
|
|
|
@ -1,108 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Pipes;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Sega.Saturn
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// helpers for moving files across named pipes
|
|
||||||
/// </summary>
|
|
||||||
public class FilePiping
|
|
||||||
{
|
|
||||||
public void Offer(byte[] data)
|
|
||||||
{
|
|
||||||
MemoryStream ms = new MemoryStream(data, false);
|
|
||||||
Offer(ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
string PipeName;
|
|
||||||
Thread thr;
|
|
||||||
Exception e;
|
|
||||||
|
|
||||||
public string GetPipeName()
|
|
||||||
{
|
|
||||||
return PipeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetPipeNameNative()
|
|
||||||
{
|
|
||||||
return @"\\.\pipe\" + PipeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FilePiping()
|
|
||||||
{
|
|
||||||
PipeName = "BizHawk-" + Guid.NewGuid();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Get(Stream s)
|
|
||||||
{
|
|
||||||
if (thr != null)
|
|
||||||
throw new InvalidOperationException("Can only serve one thing at a time!");
|
|
||||||
if (e != null)
|
|
||||||
throw new InvalidOperationException("Previous attempt failed!", e);
|
|
||||||
if (!s.CanWrite)
|
|
||||||
throw new ArgumentException($"{nameof(Stream)} must be readable!");
|
|
||||||
|
|
||||||
using var evt = new ManualResetEventSlim();
|
|
||||||
thr = new Thread(delegate()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var srv = new NamedPipeServerStream(PipeName, PipeDirection.In);
|
|
||||||
evt.Set();
|
|
||||||
srv.WaitForConnection();
|
|
||||||
srv.CopyTo(s);
|
|
||||||
//srv.Flush();
|
|
||||||
}
|
|
||||||
catch (Exception ee)
|
|
||||||
{
|
|
||||||
e = ee;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
thr.Start();
|
|
||||||
evt.Wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Offer(Stream s)
|
|
||||||
{
|
|
||||||
if (thr != null)
|
|
||||||
throw new InvalidOperationException("Can only serve one thing at a time!");
|
|
||||||
if (e != null)
|
|
||||||
throw new InvalidOperationException("Previous attempt failed!", e);
|
|
||||||
if (!s.CanRead)
|
|
||||||
throw new ArgumentException($"{nameof(Stream)} must be readable!");
|
|
||||||
|
|
||||||
using var evt = new ManualResetEventSlim();
|
|
||||||
thr = new Thread(delegate()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var srv = new NamedPipeServerStream(PipeName, PipeDirection.Out);
|
|
||||||
evt.Set();
|
|
||||||
srv.WaitForConnection();
|
|
||||||
s.CopyTo(srv);
|
|
||||||
srv.WaitForPipeDrain();
|
|
||||||
}
|
|
||||||
catch (Exception ee)
|
|
||||||
{
|
|
||||||
e = ee;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
thr.Start();
|
|
||||||
evt.Wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Finish()
|
|
||||||
{
|
|
||||||
if (thr == null)
|
|
||||||
throw new InvalidOperationException("No pending!");
|
|
||||||
thr.Join();
|
|
||||||
thr = null;
|
|
||||||
Exception ret = e;
|
|
||||||
e = null;
|
|
||||||
if (ret != null)
|
|
||||||
throw ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
using BizHawk.BizInvoke;
|
|
||||||
using BizHawk.Emulation.Cores.Waterbox;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.Saturn
|
|
||||||
{
|
|
||||||
public abstract class LibSaturnus : LibWaterboxCore
|
|
||||||
{
|
|
||||||
// some of the internal code uses wizardry by which certain pointers in ss.wbx[.text]
|
|
||||||
// must be greater than or equal to this address, but less than 4GB bigger than it
|
|
||||||
public const ulong StartAddress = WaterboxHost.CanonicalStart;
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public class TOC
|
|
||||||
{
|
|
||||||
public int FirstTrack;
|
|
||||||
public int LastTrack;
|
|
||||||
public int DiskType;
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct Track
|
|
||||||
{
|
|
||||||
public int Adr;
|
|
||||||
public int Control;
|
|
||||||
public int Lba;
|
|
||||||
public int Valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 101)]
|
|
||||||
public Track[] Tracks;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public new class FrameInfo : LibWaterboxCore.FrameInfo
|
|
||||||
{
|
|
||||||
public int ResetPushed;
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CC)]
|
|
||||||
public delegate int FirmwareSizeCallback(string filename);
|
|
||||||
[UnmanagedFunctionPointer(CC)]
|
|
||||||
public delegate void FirmwareDataCallback(string filename, IntPtr dest);
|
|
||||||
[UnmanagedFunctionPointer(CC)]
|
|
||||||
public delegate void CDTOCCallback(int disk, [In, Out]TOC toc);
|
|
||||||
[UnmanagedFunctionPointer(CC)]
|
|
||||||
public delegate void CDSectorCallback(int disk, int lba, IntPtr dest);
|
|
||||||
|
|
||||||
[BizImport(CC)]
|
|
||||||
public abstract void SetFirmwareCallbacks(FirmwareSizeCallback sizecallback, FirmwareDataCallback datacallback);
|
|
||||||
[BizImport(CC)]
|
|
||||||
public abstract void SetCDCallbacks(CDTOCCallback toccallback, CDSectorCallback sectorcallback);
|
|
||||||
[BizImport(CC)]
|
|
||||||
public abstract bool Init(
|
|
||||||
int numDisks,
|
|
||||||
Saturnus.SyncSettings.CartType cartType,
|
|
||||||
Saturnus.SyncSettings.RegionType regionDefault,
|
|
||||||
bool regionAutodetect);
|
|
||||||
[BizImport(CC)]
|
|
||||||
public abstract void HardReset();
|
|
||||||
[BizImport(CC)]
|
|
||||||
public abstract void SetDisk(int disk, bool open);
|
|
||||||
[BizImport(CC)]
|
|
||||||
public abstract void SetControllerData(byte[] controllerData);
|
|
||||||
[BizImport(CC)]
|
|
||||||
public abstract void SetupInput(int[] portdevices, int[] multitaps);
|
|
||||||
[BizImport(CC)]
|
|
||||||
public abstract void SetRtc(long ticks, Saturnus.SyncSettings.LanguageType language);
|
|
||||||
[BizImport(CC)]
|
|
||||||
public abstract void SetVideoParameters(bool correctAspect, bool hBlend, bool hOverscan, int sls, int sle);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,766 +17,45 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.Saturn
|
||||||
{
|
{
|
||||||
[Core("Saturnus", "Mednafen Team", true, true, "0.9.44.1",
|
[Core("Saturnus", "Mednafen Team", true, true, "0.9.44.1",
|
||||||
"https://mednafen.github.io/releases/", false)]
|
"https://mednafen.github.io/releases/", false)]
|
||||||
public class Saturnus : WaterboxCore,
|
public class Saturnus : NymaCore, IRegionable
|
||||||
IDriveLight, IRegionable,
|
|
||||||
ISettable<Saturnus.Settings, Saturnus.SyncSettings>
|
|
||||||
{
|
{
|
||||||
private static readonly DiscSectorReaderPolicy _diskPolicy = new DiscSectorReaderPolicy
|
|
||||||
{
|
|
||||||
DeinterleavedSubcode = false
|
|
||||||
};
|
|
||||||
|
|
||||||
private LibSaturnus _core;
|
|
||||||
private Disc[] _disks;
|
|
||||||
private DiscSectorReader[] _diskReaders;
|
|
||||||
private bool _isPal;
|
|
||||||
private SaturnusControllerDeck _controllerDeck;
|
|
||||||
private int _activeDisk;
|
|
||||||
private bool _prevDiskSignal;
|
|
||||||
private bool _nextDiskSignal;
|
|
||||||
|
|
||||||
private static bool CheckDisks(IEnumerable<DiscSectorReader> readers)
|
|
||||||
{
|
|
||||||
var buff = new byte[2048 * 16];
|
|
||||||
foreach (var r in readers)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
if (r.ReadLBA_2048(i, buff, 2048 * i) != 2048)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Encoding.ASCII.GetString(buff, 0, 16) != "SEGA SEGASATURN ")
|
|
||||||
return false;
|
|
||||||
|
|
||||||
using var sha256 = SHA256.Create();
|
|
||||||
sha256.ComputeHash(buff, 0x100, 0xd00);
|
|
||||||
if (sha256.Hash.BytesToHexString() != "96B8EA48819CFA589F24C40AA149C224C420DCCF38B730F00156EFE25C9BBC8F")
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CoreConstructor("SAT")]
|
[CoreConstructor("SAT")]
|
||||||
public Saturnus(CoreComm comm, byte[] rom)
|
public Saturnus(CoreComm comm, NymaSettings settings, NymaSyncSettings syncSettings)
|
||||||
:base(comm, new Configuration())
|
: base(comm, "SAT", "Saturn Controller Deck", settings, syncSettings)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("To load a Saturn game, please load the CUE file and not the BIN file.");
|
throw new InvalidOperationException("To load a Saturn game, please load the CUE file and not the BIN file.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Saturnus(CoreComm comm, IEnumerable<Disc> disks, bool deterministic, Settings settings,
|
public Saturnus(CoreComm comm, GameInfo game,
|
||||||
SyncSettings syncSettings)
|
IEnumerable<Disc> disks, NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
|
||||||
:base(comm, new Configuration
|
: base(comm, "SAT", "Saturn Controller Deck", settings, syncSettings)
|
||||||
{
|
{
|
||||||
MaxSamples = 8192,
|
var firmwares = new Dictionary<string, ValueTuple<string, string>>
|
||||||
DefaultWidth = 320,
|
|
||||||
DefaultHeight = 240,
|
|
||||||
MaxWidth = 1024,
|
|
||||||
MaxHeight = 1024,
|
|
||||||
SystemId = "SAT"
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
settings = settings ?? new Settings();
|
{ "FIRMWARE:$J", ("SAT", "J") },
|
||||||
syncSettings = syncSettings ?? new SyncSettings();
|
{ "FIRMWARE:$U", ("SAT", "U") },
|
||||||
|
{ "FIRMWARE:$KOF", ("SAT", "KOF95") },
|
||||||
|
{ "FIRMWARE:$ULTRA", ("SAT", "ULTRAMAN") },
|
||||||
|
// { "FIRMWARE:$SATAR", ("SAT", "AR") }, // action replay garbage
|
||||||
|
};
|
||||||
|
|
||||||
_disks = disks.ToArray();
|
DoInit<LibNymaCore>(game, null, disks.ToArray(), "ss.wbx", null, deterministic, firmwares);
|
||||||
_diskReaders = disks.Select(d => new DiscSectorReader(d) { Policy = _diskPolicy }).ToArray();
|
|
||||||
if (!CheckDisks(_diskReaders))
|
|
||||||
throw new InvalidOperationException("Some disks are not valid");
|
|
||||||
InitCallbacks();
|
|
||||||
|
|
||||||
_core = PreInit<LibSaturnus>(new WaterboxOptions
|
|
||||||
{
|
|
||||||
Filename = "ss.wbx",
|
|
||||||
SbrkHeapSizeKB = 128,
|
|
||||||
SealedHeapSizeKB = 4096, // 512KB of bios, 2MB of kof95/ultraman carts
|
|
||||||
InvisibleHeapSizeKB = 8 * 1024, // 4MB of framebuffer
|
|
||||||
MmapHeapSizeKB = 0, // not used?
|
|
||||||
PlainHeapSizeKB = 24 * 1024, // up to 16MB of cart ram
|
|
||||||
StartAddress = LibSaturnus.StartAddress,
|
|
||||||
SkipCoreConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck),
|
|
||||||
SkipMemoryConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
|
|
||||||
});
|
|
||||||
|
|
||||||
SetFirmwareCallbacks();
|
|
||||||
SetCdCallbacks();
|
|
||||||
|
|
||||||
if (!_core.Init(_disks.Length, syncSettings.ExpansionCart, syncSettings.Region, syncSettings.RegionAutodetect))
|
|
||||||
throw new InvalidOperationException("Core rejected the disks!");
|
|
||||||
ClearAllCallbacks();
|
|
||||||
|
|
||||||
_controllerDeck = new SaturnusControllerDeck(new[]
|
|
||||||
{
|
|
||||||
syncSettings.Port1Multitap,
|
|
||||||
syncSettings.Port2Multitap
|
|
||||||
}, new[]
|
|
||||||
{
|
|
||||||
syncSettings.Port1,
|
|
||||||
syncSettings.Port2,
|
|
||||||
syncSettings.Port3,
|
|
||||||
syncSettings.Port4,
|
|
||||||
syncSettings.Port5,
|
|
||||||
syncSettings.Port6,
|
|
||||||
syncSettings.Port7,
|
|
||||||
syncSettings.Port8,
|
|
||||||
syncSettings.Port9,
|
|
||||||
syncSettings.Port10,
|
|
||||||
syncSettings.Port11,
|
|
||||||
syncSettings.Port12
|
|
||||||
}, _core);
|
|
||||||
ControllerDefinition = _controllerDeck.Definition;
|
|
||||||
ControllerDefinition.Name = "Saturn Controller";
|
|
||||||
ControllerDefinition.BoolButtons.AddRange(new[]
|
|
||||||
{
|
|
||||||
"Power", "Reset", "Previous Disk", "Next Disk"
|
|
||||||
});
|
|
||||||
|
|
||||||
_core.SetRtc((long)syncSettings.InitialTime.Subtract(new DateTime(1970, 1, 1)).TotalSeconds,
|
|
||||||
syncSettings.Language);
|
|
||||||
|
|
||||||
PostInit();
|
|
||||||
SetCdCallbacks();
|
|
||||||
_core.SetDisk(0, false);
|
|
||||||
PutSettings(settings);
|
|
||||||
_syncSettings = syncSettings;
|
|
||||||
DeterministicEmulation = deterministic || !_syncSettings.UseRealTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound)
|
protected override IDictionary<string, string> SettingsOverrides { get; } = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
var prevDiskSignal = controller.IsPressed("Previous Disk");
|
{ "ss.bios_jp", "$J" }, // FIRMWARE:
|
||||||
var nextDiskSignal = controller.IsPressed("Next Disk");
|
{ "ss.bios_na_eu", "$U" }, // FIRMWARE:
|
||||||
var newDisk = _activeDisk;
|
{ "ss.cart.kof95_path", "$KOF" }, // FIRMWARE:
|
||||||
if (prevDiskSignal && !_prevDiskSignal)
|
{ "ss.cart.ultraman_path", "$ULTRA" }, // FIRMWARE:
|
||||||
newDisk--;
|
{ "ss.cart.satar4mp_path", "$SATAR" }, // FIRMWARE:
|
||||||
if (nextDiskSignal && !_nextDiskSignal)
|
{ "ss.affinity.vdp2", null },
|
||||||
newDisk++;
|
{ "ss.dbg_exe_cdpath", null },
|
||||||
_prevDiskSignal = prevDiskSignal;
|
{ "ss.dbg_exe_cem", null },
|
||||||
_nextDiskSignal = nextDiskSignal;
|
{ "ss.dbg_exe_hh", null },
|
||||||
if (newDisk < -1)
|
};
|
||||||
newDisk = -1;
|
protected override ISet<string> NonSyncSettingNames { get; } = new HashSet<string>
|
||||||
if (newDisk >= _disks.Length)
|
|
||||||
newDisk = _disks.Length - 1;
|
|
||||||
if (newDisk != _activeDisk)
|
|
||||||
{
|
{
|
||||||
_core.SetDisk(newDisk == -1 ? 0 : newDisk, newDisk == -1);
|
};
|
||||||
_activeDisk = newDisk;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if not reset, the core will maintain its own deterministic increasing time each frame
|
|
||||||
if (!DeterministicEmulation)
|
|
||||||
{
|
|
||||||
_core.SetRtc((long)DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds,
|
|
||||||
_syncSettings.Language);
|
|
||||||
}
|
|
||||||
|
|
||||||
DriveLightOn = false;
|
|
||||||
if (controller.IsPressed("Power"))
|
|
||||||
_core.HardReset();
|
|
||||||
|
|
||||||
_core.SetControllerData(_controllerDeck.Poll(controller));
|
|
||||||
|
|
||||||
SetVideoParameters();
|
|
||||||
|
|
||||||
return new LibSaturnus.FrameInfo { ResetPushed = controller.IsPressed("Reset") ? 1 : 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
public DisplayType Region => _isPal ? DisplayType.PAL : DisplayType.NTSC;
|
|
||||||
|
|
||||||
public class Settings
|
|
||||||
{
|
|
||||||
public enum ResolutionModeTypes
|
|
||||||
{
|
|
||||||
[Display(Name = "Pixel Pro")]
|
|
||||||
PixelPro,
|
|
||||||
[Display(Name = "Hardcode Debug")]
|
|
||||||
HardcoreDebug,
|
|
||||||
[Display(Name = "Mednafen (5:4 AR)")]
|
|
||||||
Mednafen,
|
|
||||||
[Display(Name = "Tweaked Mednafen (5:4 AR)")]
|
|
||||||
TweakedMednafen,
|
|
||||||
}
|
|
||||||
|
|
||||||
[DisplayName("Resolution Mode")]
|
|
||||||
[DefaultValue(ResolutionModeTypes.PixelPro)]
|
|
||||||
[Description("Method for managing varying resolutions")]
|
|
||||||
public ResolutionModeTypes ResolutionMode { get; set; }
|
|
||||||
|
|
||||||
// extern bool setting_ss_h_blend;
|
|
||||||
[DisplayName("Horizontal Blend")]
|
|
||||||
[DefaultValue(false)]
|
|
||||||
[Description("Use horizontal blend filter")]
|
|
||||||
public bool HBlend { get; set; }
|
|
||||||
|
|
||||||
// extern bool setting_ss_h_overscan;
|
|
||||||
[DisplayName("Horizontal Overscan")]
|
|
||||||
[DefaultValue(true)]
|
|
||||||
[Description("Show horiziontal overscan area")]
|
|
||||||
public bool HOverscan { get; set; }
|
|
||||||
|
|
||||||
// extern int setting_ss_slstart;
|
|
||||||
[DefaultValue(0)]
|
|
||||||
[Description("First scanline to display in NTSC mode")]
|
|
||||||
[Range(0, 239)]
|
|
||||||
public int ScanlineStartNtsc { get; set; }
|
|
||||||
// extern int setting_ss_slend;
|
|
||||||
[DefaultValue(239)]
|
|
||||||
[Description("Last scanline to display in NTSC mode")]
|
|
||||||
[Range(0, 239)]
|
|
||||||
public int ScanlineEndNtsc { get; set; }
|
|
||||||
// extern int setting_ss_slstartp;
|
|
||||||
[DefaultValue(0)]
|
|
||||||
[Description("First scanline to display in PAL mode")]
|
|
||||||
[Range(-16, 271)]
|
|
||||||
public int ScanlineStartPal { get; set; }
|
|
||||||
// extern int setting_ss_slendp;
|
|
||||||
[DefaultValue(255)]
|
|
||||||
[Description("Last scanline to display in PAL mode")]
|
|
||||||
[Range(-16, 271)]
|
|
||||||
public int ScanlineEndPal { get; set; }
|
|
||||||
|
|
||||||
public Settings()
|
|
||||||
{
|
|
||||||
SettingsUtil.SetDefaultValues(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Settings Clone()
|
|
||||||
{
|
|
||||||
return (Settings)MemberwiseClone();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool NeedsReboot(Settings x, Settings y)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SyncSettings
|
|
||||||
{
|
|
||||||
public enum CartType
|
|
||||||
{
|
|
||||||
[Display(Name = "Autodetect. Will use Backup Ram Cart if no others are appropriate")]
|
|
||||||
Autodetect = -1,
|
|
||||||
[Display(Name = "None")]
|
|
||||||
None,
|
|
||||||
[Display(Name = "Backup Ram Cart")]
|
|
||||||
Backup,
|
|
||||||
[Display(Name = "1 Meg Ram Cart")]
|
|
||||||
Ram1Meg,
|
|
||||||
[Display(Name = "4 Meg Ram Cart")]
|
|
||||||
Ram4Meg,
|
|
||||||
[Display(Name = "King of Fighters 95 Rom Cart")]
|
|
||||||
Kof95,
|
|
||||||
[Display(Name = "Ultraman Rom Cart")]
|
|
||||||
Ultraman,
|
|
||||||
[Display(Name = "CS1 16 Meg Ram Cart")]
|
|
||||||
Ram16Meg
|
|
||||||
}
|
|
||||||
|
|
||||||
// extern int setting_ss_cart;
|
|
||||||
[DefaultValue(CartType.Autodetect)]
|
|
||||||
[Description("What to plug into the Saturn expansion slot")]
|
|
||||||
public CartType ExpansionCart { get; set; }
|
|
||||||
|
|
||||||
[DisplayName("Port 1 Device")]
|
|
||||||
[DefaultValue(SaturnusControllerDeck.Device.Gamepad)]
|
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
|
||||||
public SaturnusControllerDeck.Device Port1 { get; set; }
|
|
||||||
|
|
||||||
[DisplayName("Port 2 Device")]
|
|
||||||
[DefaultValue(SaturnusControllerDeck.Device.Gamepad)]
|
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
|
||||||
public SaturnusControllerDeck.Device Port2 { get; set; }
|
|
||||||
|
|
||||||
[DisplayName("Port 3 Device")]
|
|
||||||
[DefaultValue(SaturnusControllerDeck.Device.None)]
|
|
||||||
[Description("Only used if one or both multitaps are set")]
|
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
|
||||||
public SaturnusControllerDeck.Device Port3 { get; set; }
|
|
||||||
|
|
||||||
[DisplayName("Port 4 Device")]
|
|
||||||
[DefaultValue(SaturnusControllerDeck.Device.None)]
|
|
||||||
[Description("Only used if one or both multitaps are set")]
|
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
|
||||||
public SaturnusControllerDeck.Device Port4 { get; set; }
|
|
||||||
|
|
||||||
[DisplayName("Port 5 Device")]
|
|
||||||
[DefaultValue(SaturnusControllerDeck.Device.None)]
|
|
||||||
[Description("Only used if one or both multitaps are set")]
|
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
|
||||||
public SaturnusControllerDeck.Device Port5 { get; set; }
|
|
||||||
|
|
||||||
[DisplayName("Port 6 Device")]
|
|
||||||
[DefaultValue(SaturnusControllerDeck.Device.None)]
|
|
||||||
[Description("Only used if one or both multitaps are set")]
|
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
|
||||||
public SaturnusControllerDeck.Device Port6 { get; set; }
|
|
||||||
|
|
||||||
[DisplayName("Port 7 Device")]
|
|
||||||
[DefaultValue(SaturnusControllerDeck.Device.None)]
|
|
||||||
[Description("Only used if one or both multitaps are set")]
|
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
|
||||||
public SaturnusControllerDeck.Device Port7 { get; set; }
|
|
||||||
|
|
||||||
[DisplayName("Port 8 Device")]
|
|
||||||
[DefaultValue(SaturnusControllerDeck.Device.None)]
|
|
||||||
[Description("Only used if both multitaps are set")]
|
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
|
||||||
public SaturnusControllerDeck.Device Port8 { get; set; }
|
|
||||||
|
|
||||||
[DisplayName("Port 9 Device")]
|
|
||||||
[DefaultValue(SaturnusControllerDeck.Device.None)]
|
|
||||||
[Description("Only used if both multitaps are set")]
|
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
|
||||||
public SaturnusControllerDeck.Device Port9 { get; set; }
|
|
||||||
|
|
||||||
[DisplayName("Port 10 Device")]
|
|
||||||
[DefaultValue(SaturnusControllerDeck.Device.None)]
|
|
||||||
[Description("Only used if both multitaps are set")]
|
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
|
||||||
public SaturnusControllerDeck.Device Port10 { get; set; }
|
|
||||||
|
|
||||||
[DisplayName("Port 11 Device")]
|
|
||||||
[DefaultValue(SaturnusControllerDeck.Device.None)]
|
|
||||||
[Description("Only used if both multitaps are set")]
|
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
|
||||||
public SaturnusControllerDeck.Device Port11 { get; set; }
|
|
||||||
|
|
||||||
[DisplayName("Port 12 Device")]
|
|
||||||
[DefaultValue(SaturnusControllerDeck.Device.None)]
|
|
||||||
[Description("Only used if both multitaps are set")]
|
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
|
||||||
public SaturnusControllerDeck.Device Port12 { get; set; }
|
|
||||||
|
|
||||||
[DisplayName("Port 1 Multitap")]
|
|
||||||
[Description("If true, Ports 1-6 will be used for multitap")]
|
|
||||||
[DefaultValue(false)]
|
|
||||||
public bool Port1Multitap { get; set; }
|
|
||||||
[DisplayName("Port 2 Multitap")]
|
|
||||||
[Description("If true, Ports 2-7 (or 7-12, depending on Port 1 Multitap) will be used for multitap")]
|
|
||||||
[DefaultValue(false)]
|
|
||||||
public bool Port2Multitap { get; set; }
|
|
||||||
|
|
||||||
// extern bool setting_ss_region_autodetect;
|
|
||||||
[DisplayName("Region Autodetect")]
|
|
||||||
[Description("Attempt to guess the game region")]
|
|
||||||
[DefaultValue(true)]
|
|
||||||
public bool RegionAutodetect { get; set; }
|
|
||||||
|
|
||||||
public enum RegionType
|
|
||||||
{
|
|
||||||
[Display(Name = "Japan")]
|
|
||||||
Japan = 1,
|
|
||||||
[Display(Name = "Asia NTSC (Taiwan, Phillipines)")]
|
|
||||||
Taiwan = 2,
|
|
||||||
[Display(Name = "North America")]
|
|
||||||
Murka = 4,
|
|
||||||
[Display(Name = "Latin America NTSC (Brazil)")]
|
|
||||||
Brazil = 5,
|
|
||||||
[Display(Name = "South Korea")]
|
|
||||||
Korea = 6,
|
|
||||||
[Display(Name = "Asia PAL (China, Middle East)")]
|
|
||||||
China = 10,
|
|
||||||
[Display(Name = "Europe")]
|
|
||||||
Yurop = 12,
|
|
||||||
[Display(Name = "Latin America PAL")]
|
|
||||||
SouthAmerica = 13
|
|
||||||
}
|
|
||||||
|
|
||||||
// extern int setting_ss_region_default;
|
|
||||||
[DisplayName("Default Region")]
|
|
||||||
[Description("Used when Region Autodetect is disabled or fails")]
|
|
||||||
[DefaultValue(RegionType.Japan)]
|
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
|
||||||
public RegionType Region { get; set; }
|
|
||||||
|
|
||||||
public enum LanguageType
|
|
||||||
{
|
|
||||||
English = 0,
|
|
||||||
German = 1,
|
|
||||||
French = 2,
|
|
||||||
Spanish = 3,
|
|
||||||
Italian = 4,
|
|
||||||
Japanese = 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
[DisplayName("Language")]
|
|
||||||
[Description("Language of the system. Only affects some games in some regions.")]
|
|
||||||
[DefaultValue(LanguageType.Japanese)]
|
|
||||||
public LanguageType 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()
|
|
||||||
{
|
|
||||||
SettingsUtil.SetDefaultValues(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SyncSettings Clone()
|
|
||||||
{
|
|
||||||
return (SyncSettings)MemberwiseClone();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool NeedsReboot(SyncSettings x, SyncSettings y)
|
|
||||||
{
|
|
||||||
return !DeepEquality.DeepEquals(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Settings _settings;
|
|
||||||
private SyncSettings _syncSettings;
|
|
||||||
|
|
||||||
public Settings GetSettings()
|
|
||||||
{
|
|
||||||
return _settings.Clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PutSettingsDirtyBits PutSettings(Settings s)
|
|
||||||
{
|
|
||||||
var ret = Settings.NeedsReboot(_settings, s);
|
|
||||||
_settings = s;
|
|
||||||
|
|
||||||
|
|
||||||
//todo natt - is this safe? this is now called before every frameadvance
|
|
||||||
//(the correct aspect ratio is no longer an option for other reasons)
|
|
||||||
//_core.SetVideoParameters(s.CorrectAspectRatio, s.HBlend, s.HOverscan, sls, sle);
|
|
||||||
|
|
||||||
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SyncSettings GetSyncSettings()
|
|
||||||
{
|
|
||||||
return _syncSettings.Clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PutSettingsDirtyBits PutSyncSettings(SyncSettings s)
|
|
||||||
{
|
|
||||||
var ret = SyncSettings.NeedsReboot(_syncSettings, s);
|
|
||||||
_syncSettings = s;
|
|
||||||
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetVideoParameters()
|
|
||||||
{
|
|
||||||
var s = _settings;
|
|
||||||
var sls = _isPal ? s.ScanlineStartPal + 16 : s.ScanlineStartNtsc;
|
|
||||||
var sle = _isPal ? s.ScanlineEndPal + 16 : s.ScanlineEndNtsc;
|
|
||||||
|
|
||||||
bool correctAspect = true;
|
|
||||||
if (_settings.ResolutionMode == Settings.ResolutionModeTypes.PixelPro)
|
|
||||||
correctAspect = false;
|
|
||||||
|
|
||||||
_core.SetVideoParameters(correctAspect, _settings.HBlend, _settings.HOverscan, sls, sle);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void SaveStateBinaryInternal(BinaryWriter writer)
|
|
||||||
{
|
|
||||||
writer.Write(_activeDisk);
|
|
||||||
writer.Write(_prevDiskSignal);
|
|
||||||
writer.Write(_nextDiskSignal);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadStateBinaryInternal(BinaryReader reader)
|
|
||||||
{
|
|
||||||
_activeDisk = reader.ReadInt32();
|
|
||||||
_prevDiskSignal = reader.ReadBoolean();
|
|
||||||
_nextDiskSignal = reader.ReadBoolean();
|
|
||||||
// any managed pointers that we sent to the core need to be resent now!
|
|
||||||
SetCdCallbacks();
|
|
||||||
|
|
||||||
//todo natt: evaluate the philosophy of this.
|
|
||||||
//i think it's sound: loadstate will replace the last values set by frontend; so this should re-assert them.
|
|
||||||
//or we could make sure values from the frontend are stored in a segment designed with the appropriate waterboxing rules, but that seems tricky...
|
|
||||||
//anyway, in this case, I did it before frameadvance instead, so that's just as good (probably?)
|
|
||||||
//PutSettings(_settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
private LibSaturnus.FirmwareSizeCallback _firmwareSizeCallback;
|
|
||||||
private LibSaturnus.FirmwareDataCallback _firmwareDataCallback;
|
|
||||||
private LibSaturnus.CDTOCCallback _cdTocCallback;
|
|
||||||
private LibSaturnus.CDSectorCallback _cdSectorCallback;
|
|
||||||
|
|
||||||
private void InitCallbacks()
|
|
||||||
{
|
|
||||||
_firmwareSizeCallback = FirmwareSize;
|
|
||||||
_firmwareDataCallback = FirmwareData;
|
|
||||||
_cdTocCallback = CDTOCCallback;
|
|
||||||
_cdSectorCallback = CDSectorCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetFirmwareCallbacks()
|
|
||||||
{
|
|
||||||
_core.SetFirmwareCallbacks(_firmwareSizeCallback, _firmwareDataCallback);
|
|
||||||
}
|
|
||||||
private void SetCdCallbacks()
|
|
||||||
{
|
|
||||||
_core.SetCDCallbacks(_cdTocCallback, _cdSectorCallback);
|
|
||||||
}
|
|
||||||
private void ClearAllCallbacks()
|
|
||||||
{
|
|
||||||
_core.SetFirmwareCallbacks(null, null);
|
|
||||||
_core.SetCDCallbacks(null, null);
|
|
||||||
_core.SetInputCallback(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string TranslateFirmwareName(string filename)
|
|
||||||
{
|
|
||||||
switch (filename)
|
|
||||||
{
|
|
||||||
case "ss.cart.kof95_path":
|
|
||||||
return "KOF95";
|
|
||||||
case "ss.cart.ultraman_path":
|
|
||||||
return "ULTRAMAN";
|
|
||||||
case "BIOS_J":
|
|
||||||
case "BIOS_A":
|
|
||||||
return "J";
|
|
||||||
case "BIOS_E":
|
|
||||||
_isPal = true;
|
|
||||||
return "E";
|
|
||||||
case "BIOS_U":
|
|
||||||
return "U";
|
|
||||||
default:
|
|
||||||
throw new InvalidOperationException("Unknown BIOS file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private byte[] GetFirmware(string filename)
|
|
||||||
{
|
|
||||||
return CoreComm.CoreFileProvider.GetFirmware("SAT", TranslateFirmwareName(filename), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int FirmwareSize(string filename)
|
|
||||||
{
|
|
||||||
return GetFirmware(filename).Length;
|
|
||||||
}
|
|
||||||
private void FirmwareData(string filename, IntPtr dest)
|
|
||||||
{
|
|
||||||
var data = GetFirmware(filename);
|
|
||||||
Marshal.Copy(data, 0, dest, data.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetupTOC(LibSaturnus.TOC t, DiscTOC tin)
|
|
||||||
{
|
|
||||||
// everything that's not commented, we're sure about
|
|
||||||
t.FirstTrack = tin.FirstRecordedTrackNumber;
|
|
||||||
t.LastTrack = tin.LastRecordedTrackNumber;
|
|
||||||
t.DiskType = (int)tin.Session1Format;
|
|
||||||
for (int i = 0; i < 101; i++)
|
|
||||||
{
|
|
||||||
t.Tracks[i].Adr = tin.TOCItems[i].Exists ? 1 : 0; // ????
|
|
||||||
t.Tracks[i].Lba = tin.TOCItems[i].LBA;
|
|
||||||
t.Tracks[i].Control = (int)tin.TOCItems[i].Control;
|
|
||||||
t.Tracks[i].Valid = tin.TOCItems[i].Exists ? 1 : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CDTOCCallback(int disk, [In, Out]LibSaturnus.TOC t)
|
|
||||||
{
|
|
||||||
SetupTOC(t, _disks[disk].TOC);
|
|
||||||
}
|
|
||||||
private void CDSectorCallback(int disk, int lba, IntPtr dest)
|
|
||||||
{
|
|
||||||
var buff = new byte[2448];
|
|
||||||
_diskReaders[disk].ReadLBA_2448(lba, buff, 0);
|
|
||||||
Marshal.Copy(buff, 0, dest, 2448);
|
|
||||||
DriveLightOn = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DriveLightEnabled => true;
|
|
||||||
public bool DriveLightOn { get; private set; }
|
|
||||||
|
|
||||||
private const int PalFpsNum = 1734687500;
|
|
||||||
private const int PalFpsDen = 61 * 455 * 1251;
|
|
||||||
private const int NtscFpsNum = 1746818182; // 1746818181.8
|
|
||||||
private const int NtscFpsDen = 61 * 455 * 1051;
|
|
||||||
public override int VsyncNumerator => _isPal ? PalFpsNum : NtscFpsNum;
|
|
||||||
public override int VsyncDenominator => _isPal ? PalFpsDen : NtscFpsDen;
|
|
||||||
|
|
||||||
private int _virtualWidth;
|
|
||||||
private int _virtualHeight;
|
|
||||||
public override int VirtualWidth => _virtualWidth;
|
|
||||||
public override int VirtualHeight => _virtualHeight;
|
|
||||||
|
|
||||||
bool _useResizedBuffer;
|
|
||||||
int[] _resizedBuffer;
|
|
||||||
|
|
||||||
public override int[] GetVideoBuffer()
|
|
||||||
{
|
|
||||||
if (_useResizedBuffer) return _resizedBuffer;
|
|
||||||
else return _videoBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AllocResizedBuffer(int width, int height)
|
|
||||||
{
|
|
||||||
_useResizedBuffer = true;
|
|
||||||
if (_resizedBuffer == null || width * height != _resizedBuffer.Length)
|
|
||||||
_resizedBuffer = new int[width * height];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override unsafe void FrameAdvancePost()
|
|
||||||
{
|
|
||||||
//TODO: can we force the videoprovider to add a prescale instead of having to do it in software here?
|
|
||||||
//TODO: if not, actually do it in software, instead of relying on the virtual sizes
|
|
||||||
//TODO: find a reason why relying on the virtual sizes is actually not good enough?
|
|
||||||
|
|
||||||
//TODO: export VDP2 display area width from emulator and add option to aggressively crop overscan - OR - add that logic to core
|
|
||||||
|
|
||||||
//mednafen, for reference:
|
|
||||||
//if (PAL)
|
|
||||||
//{
|
|
||||||
// gi->nominal_width = (ShowHOverscan ? 365 : 354);
|
|
||||||
// gi->fb_height = 576;
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// gi->nominal_width = (ShowHOverscan ? 302 : 292);
|
|
||||||
// gi->fb_height = 480;
|
|
||||||
//}
|
|
||||||
//gi->nominal_height = LineVisLast + 1 - LineVisFirst;
|
|
||||||
//gi->lcm_width = (ShowHOverscan ? 10560 : 10240);
|
|
||||||
//gi->lcm_height = (LineVisLast + 1 - LineVisFirst) * 2;
|
|
||||||
//if (!CorrectAspect)
|
|
||||||
//{
|
|
||||||
// gi->nominal_width = (ShowHOverscan ? 352 : 341);
|
|
||||||
// gi->lcm_width = gi->nominal_width * 2;
|
|
||||||
//}
|
|
||||||
|
|
||||||
_useResizedBuffer = false;
|
|
||||||
|
|
||||||
bool isHorz2x = false, isVert2x = false;
|
|
||||||
|
|
||||||
//note: these work with PAL also
|
|
||||||
//these are all with CorrectAR.
|
|
||||||
//with IncorrectAR, only 352 and 341 will show up
|
|
||||||
//that is, 330 is forced to 352 so mednafen's presentation can display it pristine no matter what mode it is
|
|
||||||
//(it's mednafen's equivalent of a more debuggish mode, I guess)
|
|
||||||
if (BufferWidth == 352) { } //a large basic framebuffer size
|
|
||||||
else if (BufferWidth == 341) { } //a large basic framebuffer size with overscan cropped
|
|
||||||
else if (BufferWidth == 330) { } //a small basic framebuffer
|
|
||||||
else if (BufferWidth == 320) { } //a small basic framebuffer with overscan cropped
|
|
||||||
else isHorz2x = true;
|
|
||||||
|
|
||||||
int slStart = _isPal ? _settings.ScanlineStartPal : _settings.ScanlineStartNtsc;
|
|
||||||
int slEnd = _isPal ? _settings.ScanlineEndPal : _settings.ScanlineEndNtsc;
|
|
||||||
int slHeight = (slEnd - slStart) + 1;
|
|
||||||
|
|
||||||
if (BufferHeight == slHeight) { }
|
|
||||||
else isVert2x = true;
|
|
||||||
|
|
||||||
if (_settings.ResolutionMode == Settings.ResolutionModeTypes.PixelPro)
|
|
||||||
{
|
|
||||||
//this is the tricky one: need to adapt the framebuffer size
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (_settings.ResolutionMode)
|
|
||||||
{
|
|
||||||
case Settings.ResolutionModeTypes.HardcoreDebug:
|
|
||||||
_virtualWidth = BufferWidth;
|
|
||||||
_virtualHeight = BufferHeight;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Settings.ResolutionModeTypes.Mednafen:
|
|
||||||
//this is mednafen's "correct AR" case.
|
|
||||||
//note that this will shrink a width from 330 to 302 (that's the nature of mednafen)
|
|
||||||
//that makes the bios saturn orb get stretched vertically
|
|
||||||
//note: these are never high resolution (use a 2x window size if you want to see high resolution content)
|
|
||||||
if (_isPal)
|
|
||||||
{
|
|
||||||
_virtualWidth = (_settings.HOverscan ? 365 : 354);
|
|
||||||
_virtualHeight = slHeight;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_virtualWidth = (_settings.HOverscan ? 302 : 292);
|
|
||||||
_virtualHeight = slHeight;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Settings.ResolutionModeTypes.TweakedMednafen:
|
|
||||||
//same as mednafen but stretch up
|
|
||||||
//base case is 330x254
|
|
||||||
if (_isPal)
|
|
||||||
{
|
|
||||||
//this can be the same as Mednafen mode? we don't shrink down in any case, so it must be OK
|
|
||||||
_virtualWidth = (_settings.HOverscan ? 365 : 354);
|
|
||||||
_virtualHeight = slHeight;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//ideally we want a height of 254, but we may have changed the overscan settings
|
|
||||||
_virtualWidth = (_settings.HOverscan ? 330 : 320);
|
|
||||||
_virtualHeight = BufferHeight * 254 / 240;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Settings.ResolutionModeTypes.PixelPro:
|
|
||||||
//mednafen makes sure we always get 352 or 341 (if overscan cropped)
|
|
||||||
//really the only thing we do
|
|
||||||
//(that's not the best solution for us [not what psx does], but it will do for now)
|
|
||||||
_virtualWidth = BufferWidth * (isHorz2x ? 1 : 2); //not a mistake, we scale to make it bigger if it isn't already
|
|
||||||
_virtualHeight = BufferHeight * (isVert2x ? 1 : 2); //not a mistake
|
|
||||||
|
|
||||||
if (isHorz2x && isVert2x) { } //nothing to do
|
|
||||||
else if (isHorz2x && !isVert2x)
|
|
||||||
{
|
|
||||||
//needs double sizing vertically
|
|
||||||
AllocResizedBuffer(_virtualWidth, _virtualHeight);
|
|
||||||
for (int y = 0; y < BufferHeight; y++)
|
|
||||||
{
|
|
||||||
Buffer.BlockCopy(_videoBuffer, BufferWidth * y * 4, _resizedBuffer, BufferWidth * (y * 2 + 0) * 4, BufferWidth * 4);
|
|
||||||
Buffer.BlockCopy(_videoBuffer, BufferWidth * y * 4, _resizedBuffer, BufferWidth * (y * 2 + 1) * 4, BufferWidth * 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!isHorz2x && isVert2x)
|
|
||||||
{
|
|
||||||
//needs double sizing horizontally
|
|
||||||
AllocResizedBuffer(_virtualWidth, _virtualHeight);
|
|
||||||
fixed (int* _pdst = &_resizedBuffer[0])
|
|
||||||
{
|
|
||||||
fixed (int* _psrc = &_videoBuffer[0])
|
|
||||||
{
|
|
||||||
int* psrc = _psrc;
|
|
||||||
int* pdst = _pdst;
|
|
||||||
for (int y = 0; y < BufferHeight; y++)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < BufferWidth; x++) { *pdst++ = *psrc; *pdst++ = *psrc++; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//needs double sizing horizontally and vertically
|
|
||||||
AllocResizedBuffer(_virtualWidth, _virtualHeight);
|
|
||||||
fixed (int* _pdst = &_resizedBuffer[0])
|
|
||||||
{
|
|
||||||
fixed (int* _psrc = &_videoBuffer[0])
|
|
||||||
{
|
|
||||||
int* psrc = _psrc;
|
|
||||||
int* pdst = _pdst;
|
|
||||||
for (int y = 0; y < BufferHeight; y++)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < BufferWidth; x++) { *pdst++ = psrc[x]; *pdst++ = psrc[x]; }
|
|
||||||
for (int x = 0; x < BufferWidth; x++) { *pdst++ = psrc[x]; *pdst++ = psrc[x]; }
|
|
||||||
psrc += BufferWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferWidth = _virtualWidth;
|
|
||||||
BufferHeight = _virtualHeight;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,690 +0,0 @@
|
||||||
using BizHawk.Emulation.Common;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.Linq;
|
|
||||||
using static BizHawk.Emulation.Common.ControllerDefinition;
|
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.Saturn
|
|
||||||
{
|
|
||||||
public class SaturnusControllerDeck
|
|
||||||
{
|
|
||||||
private const int DataSize = 32;
|
|
||||||
|
|
||||||
private static readonly Type[] Implementors =
|
|
||||||
{
|
|
||||||
typeof(None),
|
|
||||||
typeof(Gamepad),
|
|
||||||
typeof(ThreeDeeGamepad),
|
|
||||||
typeof(Mouse),
|
|
||||||
typeof(Wheel),
|
|
||||||
typeof(Mission),
|
|
||||||
typeof(DualMission),
|
|
||||||
typeof(Keyboard)
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly IDevice[] _devices;
|
|
||||||
private readonly ControlDefUnMerger[] _unmerger;
|
|
||||||
private readonly byte[] _data;
|
|
||||||
|
|
||||||
public ControllerDefinition Definition { get; }
|
|
||||||
|
|
||||||
public SaturnusControllerDeck(bool[] multitap, Device[] devices, LibSaturnus core)
|
|
||||||
{
|
|
||||||
int count = 2 + multitap.Count(b => b) * 5;
|
|
||||||
|
|
||||||
int[] dev = new int[12];
|
|
||||||
int[] mt = new int[2];
|
|
||||||
|
|
||||||
for (int i = 0; i < 12; i++)
|
|
||||||
dev[i] = (int)(i < count ? devices[i] : Device.None);
|
|
||||||
for (int i = 0; i < 2; i++)
|
|
||||||
mt[i] = multitap[i] ? 1 : 0;
|
|
||||||
|
|
||||||
core.SetupInput(dev, mt);
|
|
||||||
|
|
||||||
_devices = dev.Take(count)
|
|
||||||
.Select(i => Activator.CreateInstance(Implementors[i]))
|
|
||||||
.Cast<IDevice>()
|
|
||||||
.ToArray();
|
|
||||||
_data = new byte[count * DataSize];
|
|
||||||
|
|
||||||
Definition = ControllerDefinitionMerger.GetMerged(_devices.Select(d => d.Definition),
|
|
||||||
out var cdum);
|
|
||||||
_unmerger = cdum.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] Poll(IController controller)
|
|
||||||
{
|
|
||||||
for (int i = 0, offset = 0; i < _devices.Length; i++, offset += DataSize)
|
|
||||||
_devices[i].Update(_unmerger[i].UnMerge(controller), _data, offset);
|
|
||||||
return _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <remarks>TODO verify direction against hardware</remarks>
|
|
||||||
private static readonly List<AxisRange> AnalogStickRanges = CreateAxisRangePair(0, 128, 255, AxisPairOrientation.RightAndDown);
|
|
||||||
|
|
||||||
private static readonly AxisRange MiscAxisRange = new AxisRange(0, 128, 255);
|
|
||||||
|
|
||||||
public static readonly List<AxisRange> MissionAxisRanges = AnalogStickRanges.Concat(new List<AxisRange> { MiscAxisRange }).ToList();
|
|
||||||
|
|
||||||
public static readonly List<AxisRange> DualMissionAxisRanges = MissionAxisRanges.Concat(MissionAxisRanges).ToList();
|
|
||||||
|
|
||||||
public static readonly List<AxisRange> ThreeDeeAxisRanges = AnalogStickRanges.Concat(new List<AxisRange> { new AxisRange(0, 0, 255), new AxisRange(0, 0, 255) }).ToList();
|
|
||||||
|
|
||||||
public enum Device
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Gamepad,
|
|
||||||
[Display(Name = "3D Pad")]
|
|
||||||
ThreeDeePad,
|
|
||||||
Mouse,
|
|
||||||
[Display(Name = "Racing Controller")]
|
|
||||||
Wheel,
|
|
||||||
[Display(Name = "Mission Stick")]
|
|
||||||
Mission,
|
|
||||||
[Display(Name = "Two Mission Sticks")]
|
|
||||||
DualMission,
|
|
||||||
Keyboard
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface IDevice
|
|
||||||
{
|
|
||||||
void Update(IController controller, byte[] dest, int offset);
|
|
||||||
ControllerDefinition Definition { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private class None : IDevice
|
|
||||||
{
|
|
||||||
private static readonly ControllerDefinition NoneDefition = new ControllerDefinition();
|
|
||||||
public ControllerDefinition Definition => NoneDefition;
|
|
||||||
public void Update(IController controller, byte[] dest, int offset)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private abstract class ButtonedDevice : IDevice
|
|
||||||
{
|
|
||||||
protected ButtonedDevice()
|
|
||||||
{
|
|
||||||
_bakedButtonNames = ButtonNames.Select(s => s != null ? "0" + s : null).ToArray();
|
|
||||||
_bakedAnalogNames = AnalogNames.Select(s => "0" + s).ToArray();
|
|
||||||
|
|
||||||
Definition = new ControllerDefinition
|
|
||||||
{
|
|
||||||
BoolButtons = _bakedButtonNames
|
|
||||||
.Select((s, i) => new { s, i })
|
|
||||||
.Where(a => a.s != null)
|
|
||||||
.OrderBy(a => ButtonOrdinal(ButtonNames[a.i]))
|
|
||||||
.Select(a => a.s)
|
|
||||||
.ToList(),
|
|
||||||
};
|
|
||||||
Definition.AxisControls.AddRange(_bakedAnalogNames
|
|
||||||
.Select((s, i) => new { s, i })
|
|
||||||
.OrderBy(a => AnalogOrdinal(AnalogNames[a.i]))
|
|
||||||
.Select(a => a.s));
|
|
||||||
Definition.AxisRanges.AddRange(_bakedAnalogNames.Select(s => MiscAxisRange));
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly string[] _bakedButtonNames;
|
|
||||||
private readonly string[] _bakedAnalogNames;
|
|
||||||
|
|
||||||
protected virtual string[] ButtonNames { get; } = new string[0];
|
|
||||||
protected virtual int ButtonByteOffset { get; } = 0;
|
|
||||||
protected virtual string[] AnalogNames { get; } = new string[0];
|
|
||||||
protected virtual int AnalogByteOffset => (ButtonNames.Length + 7) / 8;
|
|
||||||
public ControllerDefinition Definition { get; }
|
|
||||||
|
|
||||||
protected virtual int ButtonOrdinal(string name)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
protected virtual int AnalogOrdinal(string name)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateButtons(IController controller, byte[] dest, int offset)
|
|
||||||
{
|
|
||||||
int pos = offset + ButtonByteOffset;
|
|
||||||
byte data = 0;
|
|
||||||
int bit = 0;
|
|
||||||
for (int i = 0; i < _bakedButtonNames.Length; i++)
|
|
||||||
{
|
|
||||||
if (_bakedButtonNames[i] != null && controller.IsPressed(_bakedButtonNames[i]))
|
|
||||||
data |= (byte)(1 << bit);
|
|
||||||
if (++bit == 8)
|
|
||||||
{
|
|
||||||
bit = 0;
|
|
||||||
dest[pos++] = data;
|
|
||||||
data = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bit != 0)
|
|
||||||
dest[pos] = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAnalogs(IController controller, byte[] dest, int offset)
|
|
||||||
{
|
|
||||||
int pos = offset + AnalogByteOffset;
|
|
||||||
for (int i = 0; i < _bakedAnalogNames.Length; i++)
|
|
||||||
{
|
|
||||||
var data = (byte)(int)controller.AxisValue(_bakedAnalogNames[i]);
|
|
||||||
dest[pos++] = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public virtual void Update(IController controller, byte[] dest, int offset)
|
|
||||||
{
|
|
||||||
UpdateButtons(controller, dest, offset);
|
|
||||||
UpdateAnalogs(controller, dest, offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Gamepad : ButtonedDevice
|
|
||||||
{
|
|
||||||
private static readonly string[] _buttonNames =
|
|
||||||
{
|
|
||||||
"Z", "Y", "X", "R",
|
|
||||||
"Up", "Down", "Left", "Right",
|
|
||||||
"B", "C", "A", "Start",
|
|
||||||
null, null, null, "L"
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override string[] ButtonNames => _buttonNames;
|
|
||||||
|
|
||||||
protected override int ButtonOrdinal(string name)
|
|
||||||
{
|
|
||||||
switch (name)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
case "A":
|
|
||||||
return 1;
|
|
||||||
case "B":
|
|
||||||
case "C":
|
|
||||||
return 2;
|
|
||||||
case "X":
|
|
||||||
return 3;
|
|
||||||
case "Y":
|
|
||||||
return 4;
|
|
||||||
case "Z":
|
|
||||||
case "L":
|
|
||||||
return 5;
|
|
||||||
case "R":
|
|
||||||
return 6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ThreeDeeGamepad : ButtonedDevice
|
|
||||||
{
|
|
||||||
private static readonly string[] _buttonNames =
|
|
||||||
{
|
|
||||||
"Up", "Down", "Left", "Right",
|
|
||||||
"B", "C", "A", "Start",
|
|
||||||
"Z", "Y", "X"
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override string[] ButtonNames => _buttonNames;
|
|
||||||
|
|
||||||
private static readonly string[] _analogNames =
|
|
||||||
{
|
|
||||||
"Stick Horizontal",
|
|
||||||
"Stick Vertical",
|
|
||||||
"Left Shoulder",
|
|
||||||
"Right Shoulder"
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override string[] AnalogNames => _analogNames;
|
|
||||||
|
|
||||||
public ThreeDeeGamepad()
|
|
||||||
{
|
|
||||||
Definition.AxisRanges = ThreeDeeAxisRanges;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update(IController controller, byte[] dest, int offset)
|
|
||||||
{
|
|
||||||
base.Update(controller, dest, offset);
|
|
||||||
// set the "Mode" button to analog at all times
|
|
||||||
dest[offset + 1] |= 0x10;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override int ButtonOrdinal(string name)
|
|
||||||
{
|
|
||||||
switch (name)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
case "A":
|
|
||||||
return 1;
|
|
||||||
case "B":
|
|
||||||
case "C":
|
|
||||||
return 2;
|
|
||||||
case "X":
|
|
||||||
return 3;
|
|
||||||
case "Y":
|
|
||||||
return 4;
|
|
||||||
case "Z":
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Mouse : ButtonedDevice
|
|
||||||
{
|
|
||||||
private static readonly string[] _buttonNames =
|
|
||||||
{
|
|
||||||
"Mouse Left", "Mouse Right", "Mouse Center", "Start"
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override string[] ButtonNames => _buttonNames;
|
|
||||||
|
|
||||||
private static readonly string[] _analogNames =
|
|
||||||
{
|
|
||||||
"X", "Y"
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override string[] AnalogNames => _analogNames;
|
|
||||||
|
|
||||||
protected override int ButtonOrdinal(string name)
|
|
||||||
{
|
|
||||||
switch (name)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
case "Mouse Left":
|
|
||||||
return 0;
|
|
||||||
case "Mouse Center":
|
|
||||||
return 1;
|
|
||||||
case "Mouse Right":
|
|
||||||
return 2;
|
|
||||||
case "Start":
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Wheel : ButtonedDevice
|
|
||||||
{
|
|
||||||
private static readonly string[] _buttonNames =
|
|
||||||
{
|
|
||||||
"Up", "Down", null, null,
|
|
||||||
"B", "C", "A", "Start",
|
|
||||||
"Z", "Y", "X"
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override string[] ButtonNames => _buttonNames;
|
|
||||||
|
|
||||||
private static readonly string[] _analogNames =
|
|
||||||
{
|
|
||||||
"Wheel"
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override string[] AnalogNames => _analogNames;
|
|
||||||
|
|
||||||
protected override int ButtonOrdinal(string name)
|
|
||||||
{
|
|
||||||
switch (name)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
case "A":
|
|
||||||
return 1;
|
|
||||||
case "B":
|
|
||||||
case "C":
|
|
||||||
return 2;
|
|
||||||
case "X":
|
|
||||||
return 3;
|
|
||||||
case "Y":
|
|
||||||
return 4;
|
|
||||||
case "Z":
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Mission : ButtonedDevice
|
|
||||||
{
|
|
||||||
private static readonly string[] _buttonNames =
|
|
||||||
{
|
|
||||||
"B", "C", "A", "Start",
|
|
||||||
"Z", "Y", "X", "R",
|
|
||||||
null, null, null, "L"
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override string[] ButtonNames => _buttonNames;
|
|
||||||
|
|
||||||
private static readonly string[] _analogNames =
|
|
||||||
{
|
|
||||||
"Stick Horizontal",
|
|
||||||
"Stick Vertical",
|
|
||||||
"Throttle"
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override string[] AnalogNames => _analogNames;
|
|
||||||
protected override int AnalogByteOffset => 4;
|
|
||||||
|
|
||||||
public Mission()
|
|
||||||
{
|
|
||||||
Definition.AxisRanges = MissionAxisRanges;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override int ButtonOrdinal(string name)
|
|
||||||
{
|
|
||||||
switch (name)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
case "Start":
|
|
||||||
return 1;
|
|
||||||
case "A":
|
|
||||||
return 2;
|
|
||||||
case "B":
|
|
||||||
case "C":
|
|
||||||
return 3;
|
|
||||||
case "X":
|
|
||||||
return 4;
|
|
||||||
case "Y":
|
|
||||||
return 5;
|
|
||||||
case "Z":
|
|
||||||
case "L":
|
|
||||||
return 6;
|
|
||||||
case "R":
|
|
||||||
return 7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DualMission : Mission
|
|
||||||
{
|
|
||||||
private static readonly string[] _analogNames =
|
|
||||||
{
|
|
||||||
"Right Stick Horizontal",
|
|
||||||
"Right Stick Vertical",
|
|
||||||
"Right Throttle",
|
|
||||||
"Left Stick Horizontal",
|
|
||||||
"Left Stick Vertical",
|
|
||||||
"Left Throttle"
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override string[] AnalogNames => _analogNames;
|
|
||||||
|
|
||||||
public DualMission()
|
|
||||||
{
|
|
||||||
Definition.AxisRanges = DualMissionAxisRanges;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Keyboard : ButtonedDevice
|
|
||||||
{
|
|
||||||
// TODO: LEDs, which are actually data sent back by the core
|
|
||||||
private static readonly string[] _buttonNames =
|
|
||||||
{
|
|
||||||
null,
|
|
||||||
"F9",
|
|
||||||
null,
|
|
||||||
"F5",
|
|
||||||
"F3",
|
|
||||||
"F1",
|
|
||||||
"F2",
|
|
||||||
"F12",
|
|
||||||
null,
|
|
||||||
"F10",
|
|
||||||
"F8",
|
|
||||||
"F6",
|
|
||||||
"F4",
|
|
||||||
"Tab",
|
|
||||||
"Grave`",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"LeftAlt",
|
|
||||||
"LeftShift",
|
|
||||||
null,
|
|
||||||
"LeftCtrl",
|
|
||||||
"Q",
|
|
||||||
"1(One)",
|
|
||||||
"RightAlt",
|
|
||||||
"RightCtrl",
|
|
||||||
"KeypadEnter",
|
|
||||||
"Z(Key)",
|
|
||||||
"S",
|
|
||||||
"A(Key)",
|
|
||||||
"W",
|
|
||||||
"2",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"C(Key)",
|
|
||||||
"X(Key)",
|
|
||||||
"D",
|
|
||||||
"E",
|
|
||||||
"4",
|
|
||||||
"3",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"Space",
|
|
||||||
"V",
|
|
||||||
"F",
|
|
||||||
"T",
|
|
||||||
"R(Key)",
|
|
||||||
"5",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"N",
|
|
||||||
"B(Key)",
|
|
||||||
"H",
|
|
||||||
"G",
|
|
||||||
"Y(Key)",
|
|
||||||
"6",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"M",
|
|
||||||
"J",
|
|
||||||
"U",
|
|
||||||
"7",
|
|
||||||
"8",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"Comma,",
|
|
||||||
"K",
|
|
||||||
"I",
|
|
||||||
"O",
|
|
||||||
"0(Zero)",
|
|
||||||
"9",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"Period.",
|
|
||||||
"Slash/",
|
|
||||||
"L(Key)",
|
|
||||||
"Semicolon;",
|
|
||||||
"P",
|
|
||||||
"Minus-",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"Quote'",
|
|
||||||
null,
|
|
||||||
"LeftBracket[",
|
|
||||||
"Equals=",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"CapsLock",
|
|
||||||
"RightShift",
|
|
||||||
"Enter",
|
|
||||||
"RightBracket]",
|
|
||||||
null,
|
|
||||||
"Backslash\\",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"Backspace",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"KeypadEnd/1",
|
|
||||||
null,
|
|
||||||
"KeypadLeft/4",
|
|
||||||
"KeypadHome/7",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"KeypadInsert/0",
|
|
||||||
"KeypadDelete",
|
|
||||||
"KeypadDown/2",
|
|
||||||
"KeypadCenter/5",
|
|
||||||
"KeypadRight/6",
|
|
||||||
"KeypadUp/8",
|
|
||||||
"Escape",
|
|
||||||
"NumLock",
|
|
||||||
"F11",
|
|
||||||
"KeypadPlus",
|
|
||||||
"KeypadPagedown/3",
|
|
||||||
"KeypadMinus",
|
|
||||||
"KeypadAsterisk(Multiply)",
|
|
||||||
"KeypadPageup/9",
|
|
||||||
"ScrollLock",
|
|
||||||
null,
|
|
||||||
"KeypadSlash(Divide)",
|
|
||||||
"Insert",
|
|
||||||
"Pause",
|
|
||||||
"F7",
|
|
||||||
"PrintScreen",
|
|
||||||
"Delete",
|
|
||||||
"CursorLeft",
|
|
||||||
"Home",
|
|
||||||
"End",
|
|
||||||
"Up",
|
|
||||||
"Down",
|
|
||||||
"PageUp",
|
|
||||||
"PageDown",
|
|
||||||
"Right",
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override string[] ButtonNames => _buttonNames;
|
|
||||||
|
|
||||||
protected override int ButtonOrdinal(string name)
|
|
||||||
{
|
|
||||||
switch (name)
|
|
||||||
{
|
|
||||||
default: return 0;
|
|
||||||
case "Escape": return 1;
|
|
||||||
case "F1": return 2;
|
|
||||||
case "F2": return 3;
|
|
||||||
case "F3": return 4;
|
|
||||||
case "F4": return 5;
|
|
||||||
case "F5": return 6;
|
|
||||||
case "F6": return 7;
|
|
||||||
case "F7": return 8;
|
|
||||||
case "F8": return 9;
|
|
||||||
case "F9": return 10;
|
|
||||||
case "F10": return 11;
|
|
||||||
case "F11": return 12;
|
|
||||||
case "F12": return 13;
|
|
||||||
|
|
||||||
case "Grave`": return 100;
|
|
||||||
case "1(One)": return 101;
|
|
||||||
case "2": return 102;
|
|
||||||
case "3": return 103;
|
|
||||||
case "4": return 104;
|
|
||||||
case "5": return 105;
|
|
||||||
case "6": return 106;
|
|
||||||
case "7": return 107;
|
|
||||||
case "8": return 108;
|
|
||||||
case "9": return 109;
|
|
||||||
case "0(Zero)": return 110;
|
|
||||||
case "Minus-": return 111;
|
|
||||||
case "Equals=": return 112;
|
|
||||||
case "Backslash\\": return 113;
|
|
||||||
case "Backspace": return 114;
|
|
||||||
|
|
||||||
case "Tab": return 200;
|
|
||||||
case "Q": return 201;
|
|
||||||
case "W": return 202;
|
|
||||||
case "E": return 203;
|
|
||||||
case "R(Key)": return 204;
|
|
||||||
case "T": return 205;
|
|
||||||
case "Y(Key)": return 206;
|
|
||||||
case "U": return 207;
|
|
||||||
case "I": return 208;
|
|
||||||
case "O": return 209;
|
|
||||||
case "P": return 210;
|
|
||||||
case "LeftBracket[": return 211;
|
|
||||||
case "RightBracket]": return 212;
|
|
||||||
case "Enter": return 213;
|
|
||||||
|
|
||||||
case "CapsLock": return 300;
|
|
||||||
case "A(Key)": return 301;
|
|
||||||
case "S": return 302;
|
|
||||||
case "D": return 303;
|
|
||||||
case "F": return 304;
|
|
||||||
case "G": return 305;
|
|
||||||
case "H": return 306;
|
|
||||||
case "J": return 307;
|
|
||||||
case "K": return 308;
|
|
||||||
case "L(Key)": return 309;
|
|
||||||
case "Semicolon;": return 310;
|
|
||||||
case "Quote'": return 311;
|
|
||||||
|
|
||||||
case "LeftShift": return 400;
|
|
||||||
case "Z(Key)": return 401;
|
|
||||||
case "X(Key)": return 402;
|
|
||||||
case "C(Key)": return 403;
|
|
||||||
case "V": return 404;
|
|
||||||
case "B(Key)": return 405;
|
|
||||||
case "N": return 406;
|
|
||||||
case "M": return 407;
|
|
||||||
case "Comma,": return 408;
|
|
||||||
case "Period.": return 409;
|
|
||||||
case "Slash/": return 410;
|
|
||||||
case "RightShift": return 411;
|
|
||||||
|
|
||||||
case "LeftCtrl": return 500;
|
|
||||||
case "LeftAlt": return 501;
|
|
||||||
case "Space": return 502;
|
|
||||||
case "RightAlt": return 503;
|
|
||||||
case "RightCtrl": return 504;
|
|
||||||
|
|
||||||
case "PrintScreen": return 1000;
|
|
||||||
case "ScrollLock": return 1001;
|
|
||||||
case "Pause": return 1002;
|
|
||||||
|
|
||||||
case "Insert": return 1100;
|
|
||||||
case "Delete": return 1101;
|
|
||||||
case "Home": return 1102;
|
|
||||||
case "End": return 1103;
|
|
||||||
case "PageUp": return 1104;
|
|
||||||
case "PageDown": return 1105;
|
|
||||||
|
|
||||||
case "Up": return 1200;
|
|
||||||
case "Down": return 1201;
|
|
||||||
case "CursorLeft": return 1202;
|
|
||||||
case "Right": return 1203;
|
|
||||||
|
|
||||||
case "NumLock": return 1300;
|
|
||||||
case "KeypadSlash(Divide)": return 1301;
|
|
||||||
case "KeypadAsterisk(Multiply)": return 1302;
|
|
||||||
case "KeypadMinus": return 1303;
|
|
||||||
case "KeypadHome/7": return 1304;
|
|
||||||
case "KeypadUp/8": return 1305;
|
|
||||||
case "KeypadPageup/9": return 1306;
|
|
||||||
case "KeypadPlus": return 1307;
|
|
||||||
case "KeypadLeft/4": return 1308;
|
|
||||||
case "KeypadCenter/5": return 1309;
|
|
||||||
case "KeypadRight/6": return 1310;
|
|
||||||
case "KeypadEnd/1": return 1311;
|
|
||||||
case "KeypadDown/2": return 1312;
|
|
||||||
case "KeypadPagedown/3": return 1313;
|
|
||||||
case "KeypadEnter": return 1314;
|
|
||||||
case "KeypadInsert/0": return 1315;
|
|
||||||
case "KeypadDelete": return 1316;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -83,6 +83,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
|
|
||||||
foreach (var input in inputs)
|
foreach (var input in inputs)
|
||||||
{
|
{
|
||||||
|
if (input.Type == InputType.ResetButton)
|
||||||
|
System.Diagnostics.Debugger.Break();
|
||||||
if (input.Type == InputType.Padding)
|
if (input.Type == InputType.Padding)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -91,19 +93,23 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
var byteStart = devByteStart + bitOffset / 8;
|
var byteStart = devByteStart + bitOffset / 8;
|
||||||
bitOffset %= 8;
|
bitOffset %= 8;
|
||||||
var baseName = input.Name;
|
var baseName = input.Name;
|
||||||
if (overrides.ContainsKey(baseName))
|
if (baseName != null && overrides.ContainsKey(baseName))
|
||||||
baseName = overrides[baseName];
|
baseName = overrides[baseName];
|
||||||
var name = $"P{port + 1} {baseName}";
|
var name = input.Type == InputType.ResetButton ? "Reset" : $"P{port + 1} {baseName}";
|
||||||
|
|
||||||
switch (input.Type)
|
switch (input.Type)
|
||||||
{
|
{
|
||||||
|
case InputType.ResetButton:
|
||||||
case InputType.Button:
|
case InputType.Button:
|
||||||
case InputType.ButtonCanRapid:
|
case InputType.ButtonCanRapid:
|
||||||
{
|
{
|
||||||
// var data = inputInfo.Extra.AsButton();
|
// var data = inputInfo.Extra.AsButton();
|
||||||
// TODO: Wire up data.ExcludeName
|
// TODO: Wire up data.ExcludeName
|
||||||
|
if (input.Type != InputType.ResetButton)
|
||||||
|
{
|
||||||
ret.BoolButtons.Add(name);
|
ret.BoolButtons.Add(name);
|
||||||
ret.CategoryLabels[name] = category;
|
ret.CategoryLabels[name] = category;
|
||||||
|
}
|
||||||
_thunks.Add((c, b) =>
|
_thunks.Add((c, b) =>
|
||||||
{
|
{
|
||||||
if (c.IsPressed(name))
|
if (c.IsPressed(name))
|
||||||
|
|
|
@ -29,7 +29,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, bool deterministic,
|
protected T DoInit<T>(GameInfo game, byte[] rom, Disc[] discs, string wbxFilename, string extension, bool deterministic,
|
||||||
ICollection<KeyValuePair<string, byte[]>> firmwares = null)
|
IDictionary<string, ValueTuple<string, string>> firmwares = null)
|
||||||
where T : LibNymaCore
|
where T : LibNymaCore
|
||||||
{
|
{
|
||||||
var t = PreInit<T>(new WaterboxOptions
|
var t = PreInit<T>(new WaterboxOptions
|
||||||
|
@ -53,13 +53,25 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
var portData = GetInputPortsData();
|
var portData = GetInputPortsData();
|
||||||
InitSyncSettingsInfo(portData);
|
InitSyncSettingsInfo(portData);
|
||||||
_nyma.SetFrontendSettingQuery(_settingsQueryDelegate);
|
_nyma.SetFrontendSettingQuery(_settingsQueryDelegate);
|
||||||
|
var filesToRemove = new List<string>();
|
||||||
if (firmwares != null)
|
if (firmwares != null)
|
||||||
{
|
{
|
||||||
foreach (var kvp in firmwares)
|
_exe.MissingFileCallback = s =>
|
||||||
{
|
{
|
||||||
_exe.AddReadonlyFile(kvp.Value, kvp.Key);
|
if (firmwares.TryGetValue(s, out var tt))
|
||||||
|
{
|
||||||
|
var data = CoreComm.CoreFileProvider.GetFirmware(tt.Item1, tt.Item2, false,
|
||||||
|
"Firmware files are usually required and may stop your game from loading");
|
||||||
|
if (data != null)
|
||||||
|
{
|
||||||
|
_exe.AddReadonlyFile(data, s);
|
||||||
|
filesToRemove.Add(s);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
if (discs?.Length > 0)
|
if (discs?.Length > 0)
|
||||||
{
|
{
|
||||||
_disks = discs;
|
_disks = discs;
|
||||||
|
@ -91,10 +103,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
}
|
}
|
||||||
if (firmwares != null)
|
if (firmwares != null)
|
||||||
{
|
{
|
||||||
foreach (var kvp in firmwares)
|
foreach (var s in filesToRemove)
|
||||||
{
|
{
|
||||||
_exe.RemoveReadonlyFile(kvp.Key);
|
_exe.RemoveReadonlyFile(s);
|
||||||
}
|
}
|
||||||
|
_exe.MissingFileCallback = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var info = *_nyma.GetSystemInfo();
|
var info = *_nyma.GetSystemInfo();
|
||||||
|
|
|
@ -190,5 +190,13 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
}
|
}
|
||||||
return _parent._mmapheap.Protect((ulong)address, (ulong)size, mprot) ? 0 : MAP_FAILED;
|
return _parent._mmapheap.Protect((ulong)address, (ulong)size, mprot) ? 0 : MAP_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "__wsyscalltab[28]")]
|
||||||
|
public long MAdvise(UIntPtr address, UIntPtr size, ulong advice)
|
||||||
|
{
|
||||||
|
// malloc will call this with DONTNEED in a few situations. Ignore for now.
|
||||||
|
// TODO: This could be implemented along with munmap returning pages to non-dirty
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,15 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal partial class Syscalls : IBinaryStateable
|
internal partial class Syscalls : IBinaryStateable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Can be set by the frontend and will be called if the core attempts to open a missing file.
|
||||||
|
/// The callee may add additional files to the waterbox during the callback and return `true` to indicate
|
||||||
|
/// that the right file was added and the scan should be rerun. The callee may return `false` to indicate
|
||||||
|
/// that the file should be reported as missing. Do not call other things during this callback.
|
||||||
|
/// Can be called at any time by the core, so you may want to remove your callback entirely after init
|
||||||
|
/// if it was for firmware only.
|
||||||
|
/// </summary>
|
||||||
|
public Func<string, bool> MissingFileCallback { get; set; }
|
||||||
public interface IFileObject : IBinaryStateable
|
public interface IFileObject : IBinaryStateable
|
||||||
{
|
{
|
||||||
bool Open(FileAccess access);
|
bool Open(FileAccess access);
|
||||||
|
@ -289,7 +298,18 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
public long Open(string path, int flags, int mode)
|
public long Open(string path, int flags, int mode)
|
||||||
{
|
{
|
||||||
if (!_availableFiles.TryGetValue(path, out var o))
|
if (!_availableFiles.TryGetValue(path, out var o))
|
||||||
|
{
|
||||||
|
var retry = MissingFileCallback?.Invoke(path) == true;
|
||||||
|
if (retry)
|
||||||
|
{
|
||||||
|
if (!_availableFiles.TryGetValue(path, out o))
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (_openFiles.Contains(o))
|
if (_openFiles.Contains(o))
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
FileAccess access;
|
FileAccess access;
|
||||||
|
|
|
@ -359,6 +359,20 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
return _syscalls.RemoveTransientFile(name);
|
return _syscalls.RemoveTransientFile(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Can be set by the frontend and will be called if the core attempts to open a missing file.
|
||||||
|
/// The callee may add additional files to the waterbox during the callback and return `true` to indicate
|
||||||
|
/// that the right file was added and the scan should be rerun. The callee may return `false` to indicate
|
||||||
|
/// that the file should be reported as missing. Do not call other things during this callback.
|
||||||
|
/// Can be called at any time by the core, so you may want to remove your callback entirely after init
|
||||||
|
/// if it was for firmware only.
|
||||||
|
/// </summary>
|
||||||
|
public Func<string, bool> MissingFileCallback
|
||||||
|
{
|
||||||
|
get => _syscalls.MissingFileCallback;
|
||||||
|
set => _syscalls.MissingFileCallback = value;
|
||||||
|
}
|
||||||
|
|
||||||
private const ulong MAGIC = 0x736b776162727477;
|
private const ulong MAGIC = 0x736b776162727477;
|
||||||
private const ulong WATERBOXSTATEVERSION = 2;
|
private const ulong WATERBOXSTATEVERSION = 2;
|
||||||
|
|
||||||
|
|
|
@ -178,7 +178,11 @@ namespace Mednafen
|
||||||
FrontendSettingQuery(name, tmp);
|
FrontendSettingQuery(name, tmp);
|
||||||
return std::string(tmp);
|
return std::string(tmp);
|
||||||
}
|
}
|
||||||
|
std::vector<uint64> MDFN_GetSettingMultiUI(const char *name)
|
||||||
|
{
|
||||||
|
// only used in some saturn debug code, not needed
|
||||||
|
return std::vector<uint64>();
|
||||||
|
}
|
||||||
void MDFNMP_Init(uint32 ps, uint32 numpages)
|
void MDFNMP_Init(uint32 ps, uint32 numpages)
|
||||||
{}
|
{}
|
||||||
void MDFNMP_AddRAM(uint32 size, uint32 address, uint8 *RAM, bool use_in_search) // Deprecated
|
void MDFNMP_AddRAM(uint32 size, uint32 address, uint8 *RAM, bool use_in_search) // Deprecated
|
||||||
|
|
|
@ -601,31 +601,31 @@
|
||||||
#undef PTHREAD_IN_USE_DETECTION_HARD
|
#undef PTHREAD_IN_USE_DETECTION_HARD
|
||||||
|
|
||||||
/* The size of `char', as computed by sizeof. */
|
/* The size of `char', as computed by sizeof. */
|
||||||
#undef SIZEOF_CHAR
|
#define SIZEOF_CHAR 1
|
||||||
|
|
||||||
/* The size of `int', as computed by sizeof. */
|
/* The size of `int', as computed by sizeof. */
|
||||||
#undef SIZEOF_INT
|
#define SIZEOF_INT 4
|
||||||
|
|
||||||
/* The size of `long', as computed by sizeof. */
|
/* The size of `long', as computed by sizeof. */
|
||||||
#undef SIZEOF_LONG
|
#define SIZEOF_LONG 8
|
||||||
|
|
||||||
/* The size of `long long', as computed by sizeof. */
|
/* The size of `long long', as computed by sizeof. */
|
||||||
#undef SIZEOF_LONG_LONG
|
#define SIZEOF_LONG_LONG 8
|
||||||
|
|
||||||
/* The size of `off_t', as computed by sizeof. */
|
/* The size of `off_t', as computed by sizeof. */
|
||||||
#undef SIZEOF_OFF_T
|
#define SIZEOF_OFF_T 8
|
||||||
|
|
||||||
/* The size of `ptrdiff_t', as computed by sizeof. */
|
/* The size of `ptrdiff_t', as computed by sizeof. */
|
||||||
#undef SIZEOF_PTRDIFF_T
|
#define SIZEOF_PTRDIFF_T 8
|
||||||
|
|
||||||
/* The size of `short', as computed by sizeof. */
|
/* The size of `short', as computed by sizeof. */
|
||||||
#undef SIZEOF_SHORT
|
#define SIZEOF_SHORT 2
|
||||||
|
|
||||||
/* The size of `size_t', as computed by sizeof. */
|
/* The size of `size_t', as computed by sizeof. */
|
||||||
#undef SIZEOF_SIZE_T
|
#define SIZEOF_SIZE_T 8
|
||||||
|
|
||||||
/* The size of `void *', as computed by sizeof. */
|
/* The size of `void *', as computed by sizeof. */
|
||||||
#undef SIZEOF_VOID_P
|
#define SIZEOF_VOID_P 8
|
||||||
|
|
||||||
/* The size of `__int64', as computed by sizeof. */
|
/* The size of `__int64', as computed by sizeof. */
|
||||||
#undef SIZEOF___INT64
|
#undef SIZEOF___INT64
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit e89d39cc0c94ffaf80674e37457987fabf811bf7
|
Subproject commit a8a377c836f591c9624c07ed2a26b43e49965921
|
|
@ -0,0 +1,78 @@
|
||||||
|
#include <src/types.h>
|
||||||
|
#include <src/mednafen.h>
|
||||||
|
#include <src/ss/ss.h>
|
||||||
|
#include "nyma.h"
|
||||||
|
#include <emulibc.h>
|
||||||
|
#include <waterboxcore.h>
|
||||||
|
#include <src/ss/cart.h>
|
||||||
|
namespace MDFN_IEN_SS
|
||||||
|
{
|
||||||
|
#include <src/ss/scsp.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace MDFN_IEN_SS;
|
||||||
|
|
||||||
|
extern Mednafen::MDFNGI EmulatedSS;
|
||||||
|
|
||||||
|
void SetupMDFNGameInfo()
|
||||||
|
{
|
||||||
|
Mednafen::MDFNGameInfo = &EmulatedSS;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MDFN_IEN_SS
|
||||||
|
{
|
||||||
|
extern SS_SCSP SCSP;
|
||||||
|
extern uint16 BIOSROM[524288 / sizeof(uint16)];
|
||||||
|
extern uint16 WorkRAML[1024 * 1024 / sizeof(uint16)];
|
||||||
|
extern uint16 WorkRAMH[1024 * 1024 / sizeof(uint16)]; // Effectively 32-bit in reality, but 16-bit here because of CPU interpreter design(regarding fastmap).
|
||||||
|
extern uint8 BackupRAM[32768];
|
||||||
|
namespace VDP1
|
||||||
|
{
|
||||||
|
extern uint16 VRAM[0x40000];
|
||||||
|
extern uint16 FB[2][0x20000];
|
||||||
|
}
|
||||||
|
namespace VDP2
|
||||||
|
{
|
||||||
|
extern uint16 VRAM[262144];
|
||||||
|
extern uint16 CRAM[2048];
|
||||||
|
}
|
||||||
|
extern uint8 ExtBackupRAM[0x80000];
|
||||||
|
extern uint16* CS1RAM;
|
||||||
|
extern uint16 ExtRAM[0x200000];
|
||||||
|
extern uint16 ROM[0x100000];
|
||||||
|
|
||||||
|
extern int ActiveCartType;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECL_EXPORT void GetMemoryAreas(MemoryArea* m)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
#define AddMemoryDomain(name,data,size,flags) do\
|
||||||
|
{\
|
||||||
|
m[i].Data = data;\
|
||||||
|
m[i].Name = name;\
|
||||||
|
m[i].Size = size;\
|
||||||
|
m[i].Flags = flags;\
|
||||||
|
i++;\
|
||||||
|
}\
|
||||||
|
while (0)
|
||||||
|
AddMemoryDomain("Sound Ram", SCSP.GetRAMPtr(), 0x100000, MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
|
||||||
|
AddMemoryDomain("Backup Ram", BackupRAM, sizeof(BackupRAM), MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_SAVERAMMABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
|
||||||
|
AddMemoryDomain("Boot Rom", BIOSROM, 524288, MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
|
||||||
|
AddMemoryDomain("Work Ram Low", WorkRAML, sizeof(WorkRAML), MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
|
||||||
|
AddMemoryDomain("Work Ram High", WorkRAMH, sizeof(WorkRAMH), MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2 | MEMORYAREA_FLAGS_PRIMARY);
|
||||||
|
AddMemoryDomain("VDP1 Ram", VDP1::VRAM, sizeof(VDP1::VRAM), MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
|
||||||
|
AddMemoryDomain("VDP1 Framebuffer", VDP1::FB, sizeof(VDP1::FB), MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
|
||||||
|
AddMemoryDomain("VDP2 Ram", VDP2::VRAM, sizeof(VDP2::VRAM), MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
|
||||||
|
AddMemoryDomain("VDP2 CRam", VDP2::CRAM, sizeof(VDP2::CRAM), MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
|
||||||
|
if (ActiveCartType == CART_BACKUP_MEM)
|
||||||
|
AddMemoryDomain("Backup Cart", ExtBackupRAM, sizeof(ExtBackupRAM), MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_SAVERAMMABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
|
||||||
|
if (ActiveCartType == CART_CS1RAM_16M)
|
||||||
|
AddMemoryDomain("CS1 Cart", CS1RAM, 0x1000000, MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
|
||||||
|
if (ActiveCartType == CART_EXTRAM_4M)
|
||||||
|
AddMemoryDomain("Ram Cart", ExtRAM, sizeof(ExtRAM), MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
|
||||||
|
if (ActiveCartType == CART_EXTRAM_1M)
|
||||||
|
AddMemoryDomain("Ram Cart", ExtRAM, sizeof(ExtRAM) / 4, MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
|
||||||
|
if (ActiveCartType == CART_KOF95 || ActiveCartType == CART_ULTRAMAN)
|
||||||
|
AddMemoryDomain("Rom Cart", ROM, sizeof(ROM), MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
include common.mak
|
||||||
|
|
||||||
|
SRCS += \
|
||||||
|
$(filter-out %gen_dsp.cpp,$(call cppdir,ss)) \
|
||||||
|
$(filter-out %gen.cpp,$(call cppdir,hw_cpu/m68k)) \
|
||||||
|
mednafen/src/resampler/resample.c \
|
||||||
|
$(CD_SRCS) \
|
||||||
|
mednafen/src/PSFLoader.cpp mednafen/src/SSFLoader.cpp \
|
||||||
|
ss.cpp
|
||||||
|
|
||||||
|
include ../common.mak
|
Loading…
Reference in New Issue