Move saturn over to nyma, get latest version.

This commit is contained in:
nattthebear 2020-06-09 19:39:59 -04:00
parent 73e2922a0d
commit f14dace4a0
21 changed files with 243 additions and 1668 deletions

Binary file not shown.

View File

@ -315,11 +315,11 @@ namespace BizHawk.Client.Common
break;
case "SAT":
nextEmulator = new Saturnus(
nextComm,
nextComm, game,
new[] { disc },
Deterministic,
(Saturnus.Settings) GetCoreSettings<Saturnus>(),
(Saturnus.SyncSettings) GetCoreSyncSettings<Saturnus>()
(NymaCore.NymaSettings)GetCoreSettings<Saturnus>(),
(NymaCore.NymaSyncSettings)GetCoreSyncSettings<Saturnus>(),
Deterministic
);
break;
case "PSX":
@ -821,11 +821,11 @@ namespace BizHawk.Client.Common
var saturnDiscs = DiscsFromXml(xmlGame, "SAT", DiscType.SegaSaturn);
if (saturnDiscs.Count == 0) return false;
nextEmulator = new Saturnus(
nextComm,
nextComm, game,
saturnDiscs,
Deterministic,
(Saturnus.Settings) GetCoreSettings<Saturnus>(),
(Saturnus.SyncSettings) GetCoreSyncSettings<Saturnus>()
(NymaCore.NymaSettings)GetCoreSettings<Saturnus>(),
(NymaCore.NymaSyncSettings)GetCoreSyncSettings<Saturnus>(),
Deterministic
);
return true;
case "PCFX":

View File

@ -14,10 +14,23 @@ namespace BizHawk.Client.Common.movie.import
protected override void RunImport()
{
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "SAT";
var ss = new Saturnus.SyncSettings
var ss = new Emulation.Cores.Waterbox.NymaCore.NymaSyncSettings
{
Port1 = SaturnusControllerDeck.Device.Gamepad,
Port2 = SaturnusControllerDeck.Device.None
PortDevices =
{
{ 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();

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
@ -12,6 +13,8 @@ namespace BizHawk.Client.EmuHawk
// ReSharper disable once UnusedMember.Global
public class SaturnSchema : IVirtualPadSchema
{
public IEnumerable<PadSchema> GetPadSchemas(IEmulator core) => throw new NotImplementedException();
/*
public IEnumerable<PadSchema> GetPadSchemas(IEmulator core)
{
var ss = ((Saturnus)core).GetSyncSettings();
@ -282,5 +285,6 @@ namespace BizHawk.Client.EmuHawk
}
};
}
*/
}
}

View File

@ -27,9 +27,10 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
: 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);
}

View File

@ -29,13 +29,11 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
: base(comm, "PCE", "PC Engine Controller", settings, syncSettings)
{
var firmwares = new Dictionary<string, byte[]>();
var types = discs.Select(d => new DiscIdentifier(d).DetectDiscType())
.ToList();
if (types.Contains(DiscType.TurboCD))
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));
var firmwares = new Dictionary<string, ValueTuple<string, string>>
{
{ "FIRMWARE:syscard3.pce", ("PCECD", "Bios") },
{ "FIRMWARE:gecard.pce", ("PCECD", "GE-Bios") },
};
_terboGrafix = DoInit<LibTurboNyma>(game, null, discs, "pce.wbx", null, deterministic, firmwares);
}

View File

@ -28,13 +28,9 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCFX
IEnumerable<Disc> disks, NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
: base(comm, "PCFX", "PCFX Controller Deck", settings, syncSettings)
{
var bios = comm.CoreFileProvider.GetFirmware("PCFX", "BIOS", true);
if (bios.Length != 1024 * 1024)
throw new InvalidOperationException("Wrong size BIOS file!");
var firmwares = new Dictionary<string, byte[]>
var firmwares = new Dictionary<string, ValueTuple<string, string>>
{
{ "FIRMWARE:pcfx.rom", bios },
{ "FIRMWARE:pcfx.rom", ("PCFX", "BIOS") },
};
DoInit<LibNymaCore>(game, null, disks.ToArray(), "pcfx.wbx", null, deterministic, firmwares);

View File

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

View File

@ -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);
}
}

View File

@ -17,766 +17,45 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.Saturn
{
[Core("Saturnus", "Mednafen Team", true, true, "0.9.44.1",
"https://mednafen.github.io/releases/", false)]
public class Saturnus : WaterboxCore,
IDriveLight, IRegionable,
ISettable<Saturnus.Settings, Saturnus.SyncSettings>
public class Saturnus : NymaCore, IRegionable
{
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")]
public Saturnus(CoreComm comm, byte[] rom)
:base(comm, new Configuration())
public Saturnus(CoreComm comm, NymaSettings settings, NymaSyncSettings syncSettings)
: 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.");
}
public Saturnus(CoreComm comm, IEnumerable<Disc> disks, bool deterministic, Settings settings,
SyncSettings syncSettings)
:base(comm, new Configuration
public Saturnus(CoreComm comm, GameInfo game,
IEnumerable<Disc> disks, NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
: base(comm, "SAT", "Saturn Controller Deck", settings, syncSettings)
{
var firmwares = new Dictionary<string, ValueTuple<string, string>>
{
MaxSamples = 8192,
DefaultWidth = 320,
DefaultHeight = 240,
MaxWidth = 1024,
MaxHeight = 1024,
SystemId = "SAT"
})
{
settings = settings ?? new Settings();
syncSettings = syncSettings ?? new SyncSettings();
{ "FIRMWARE:$J", ("SAT", "J") },
{ "FIRMWARE:$U", ("SAT", "U") },
{ "FIRMWARE:$KOF", ("SAT", "KOF95") },
{ "FIRMWARE:$ULTRA", ("SAT", "ULTRAMAN") },
// { "FIRMWARE:$SATAR", ("SAT", "AR") }, // action replay garbage
};
_disks = disks.ToArray();
_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;
DoInit<LibNymaCore>(game, null, disks.ToArray(), "ss.wbx", null, deterministic, firmwares);
}
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");
var nextDiskSignal = controller.IsPressed("Next Disk");
var newDisk = _activeDisk;
if (prevDiskSignal && !_prevDiskSignal)
newDisk--;
if (nextDiskSignal && !_nextDiskSignal)
newDisk++;
_prevDiskSignal = prevDiskSignal;
_nextDiskSignal = nextDiskSignal;
if (newDisk < -1)
newDisk = -1;
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
{ "ss.bios_jp", "$J" }, // FIRMWARE:
{ "ss.bios_na_eu", "$U" }, // FIRMWARE:
{ "ss.cart.kof95_path", "$KOF" }, // FIRMWARE:
{ "ss.cart.ultraman_path", "$ULTRA" }, // FIRMWARE:
{ "ss.cart.satar4mp_path", "$SATAR" }, // FIRMWARE:
{ "ss.affinity.vdp2", null },
{ "ss.dbg_exe_cdpath", null },
{ "ss.dbg_exe_cem", null },
{ "ss.dbg_exe_hh", null },
};
protected override ISet<string> NonSyncSettingNames { get; } = new HashSet<string>
{
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;
}
}
};
}
}

View File

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

View File

@ -83,6 +83,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
foreach (var input in inputs)
{
if (input.Type == InputType.ResetButton)
System.Diagnostics.Debugger.Break();
if (input.Type == InputType.Padding)
continue;
@ -91,19 +93,23 @@ namespace BizHawk.Emulation.Cores.Waterbox
var byteStart = devByteStart + bitOffset / 8;
bitOffset %= 8;
var baseName = input.Name;
if (overrides.ContainsKey(baseName))
if (baseName != null && overrides.ContainsKey(baseName))
baseName = overrides[baseName];
var name = $"P{port + 1} {baseName}";
var name = input.Type == InputType.ResetButton ? "Reset" : $"P{port + 1} {baseName}";
switch (input.Type)
{
case InputType.ResetButton:
case InputType.Button:
case InputType.ButtonCanRapid:
{
// var data = inputInfo.Extra.AsButton();
// TODO: Wire up data.ExcludeName
ret.BoolButtons.Add(name);
ret.CategoryLabels[name] = category;
if (input.Type != InputType.ResetButton)
{
ret.BoolButtons.Add(name);
ret.CategoryLabels[name] = category;
}
_thunks.Add((c, b) =>
{
if (c.IsPressed(name))

View File

@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
private LibNymaCore _nyma;
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
{
var t = PreInit<T>(new WaterboxOptions
@ -53,12 +53,24 @@ namespace BizHawk.Emulation.Cores.Waterbox
var portData = GetInputPortsData();
InitSyncSettingsInfo(portData);
_nyma.SetFrontendSettingQuery(_settingsQueryDelegate);
var filesToRemove = new List<string>();
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)
{
@ -91,10 +103,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
}
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();

View File

@ -190,5 +190,13 @@ namespace BizHawk.Emulation.Cores.Waterbox
}
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;
}
}
}

View File

@ -14,6 +14,15 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// </summary>
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
{
bool Open(FileAccess access);
@ -289,7 +298,18 @@ namespace BizHawk.Emulation.Cores.Waterbox
public long Open(string path, int flags, int mode)
{
if (!_availableFiles.TryGetValue(path, out var o))
return -ENOENT;
{
var retry = MissingFileCallback?.Invoke(path) == true;
if (retry)
{
if (!_availableFiles.TryGetValue(path, out o))
return -ENOENT;
}
else
{
return -ENOENT;
}
}
if (_openFiles.Contains(o))
return -EACCES;
FileAccess access;

View File

@ -359,6 +359,20 @@ namespace BizHawk.Emulation.Cores.Waterbox
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 WATERBOXSTATEVERSION = 2;

View File

@ -178,7 +178,11 @@ namespace Mednafen
FrontendSettingQuery(name, 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_AddRAM(uint32 size, uint32 address, uint8 *RAM, bool use_in_search) // Deprecated

View File

@ -601,31 +601,31 @@
#undef PTHREAD_IN_USE_DETECTION_HARD
/* The size of `char', as computed by sizeof. */
#undef SIZEOF_CHAR
#define SIZEOF_CHAR 1
/* The size of `int', as computed by sizeof. */
#undef SIZEOF_INT
#define SIZEOF_INT 4
/* The size of `long', as computed by sizeof. */
#undef SIZEOF_LONG
#define SIZEOF_LONG 8
/* 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. */
#undef SIZEOF_OFF_T
#define SIZEOF_OFF_T 8
/* 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. */
#undef SIZEOF_SHORT
#define SIZEOF_SHORT 2
/* 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. */
#undef SIZEOF_VOID_P
#define SIZEOF_VOID_P 8
/* The size of `__int64', as computed by sizeof. */
#undef SIZEOF___INT64

@ -1 +1 @@
Subproject commit e89d39cc0c94ffaf80674e37457987fabf811bf7
Subproject commit a8a377c836f591c9624c07ed2a26b43e49965921

78
waterbox/nyma/ss.cpp Normal file
View File

@ -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);
}

11
waterbox/nyma/ss.mak Normal file
View File

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