diff --git a/output/dll/ss.wbx.gz b/output/dll/ss.wbx.gz index e58f76c53a..aa1233dd6d 100644 Binary files a/output/dll/ss.wbx.gz and b/output/dll/ss.wbx.gz differ diff --git a/src/BizHawk.Client.Common/RomLoader.cs b/src/BizHawk.Client.Common/RomLoader.cs index 2a391bb023..5bf931a4ff 100644 --- a/src/BizHawk.Client.Common/RomLoader.cs +++ b/src/BizHawk.Client.Common/RomLoader.cs @@ -315,11 +315,11 @@ namespace BizHawk.Client.Common break; case "SAT": nextEmulator = new Saturnus( - nextComm, + nextComm, game, new[] { disc }, - Deterministic, - (Saturnus.Settings) GetCoreSettings(), - (Saturnus.SyncSettings) GetCoreSyncSettings() + (NymaCore.NymaSettings)GetCoreSettings(), + (NymaCore.NymaSyncSettings)GetCoreSyncSettings(), + 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.SyncSettings) GetCoreSyncSettings() + (NymaCore.NymaSettings)GetCoreSettings(), + (NymaCore.NymaSyncSettings)GetCoreSyncSettings(), + Deterministic ); return true; case "PCFX": diff --git a/src/BizHawk.Client.Common/movie/import/YmvImport.cs b/src/BizHawk.Client.Common/movie/import/YmvImport.cs index f859b536c0..88ae90f1e0 100644 --- a/src/BizHawk.Client.Common/movie/import/YmvImport.cs +++ b/src/BizHawk.Client.Common/movie/import/YmvImport.cs @@ -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(); diff --git a/src/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/SaturnSchema.cs b/src/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/SaturnSchema.cs index bb1915e5ca..ae021a366f 100644 --- a/src/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/SaturnSchema.cs +++ b/src/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/SaturnSchema.cs @@ -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 GetPadSchemas(IEmulator core) => throw new NotImplementedException(); + /* public IEnumerable GetPadSchemas(IEmulator core) { var ss = ((Saturnus)core).GetSyncSettings(); @@ -282,5 +285,6 @@ namespace BizHawk.Client.EmuHawk } }; } + */ } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/HyperNyma.cs b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/HyperNyma.cs index 0d3d56dfca..9fa1c86737 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/HyperNyma.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/HyperNyma.cs @@ -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 + var firmwares = new Dictionary> { - { "FIRMWARE:syscard3.pce", comm.CoreFileProvider.GetFirmware("PCECD", "Bios", true) }, + { "FIRMWARE:syscard3.pce", ("PCECD", "Bios") }, + // { "FIRMWARE:gecard.pce", ("PCECD", "GE-Bios") }, }; _terboGrafix = DoInit(game, null, discs, "pce-fast.wbx", null, deterministic, firmwares); } diff --git a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TurboNyma.cs b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TurboNyma.cs index 92e55df5ae..bf274b2348 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TurboNyma.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TurboNyma.cs @@ -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(); - 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> + { + { "FIRMWARE:syscard3.pce", ("PCECD", "Bios") }, + { "FIRMWARE:gecard.pce", ("PCECD", "GE-Bios") }, + }; _terboGrafix = DoInit(game, null, discs, "pce.wbx", null, deterministic, firmwares); } diff --git a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/Tst.cs b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/Tst.cs index a4d52107c2..43582d4b30 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/Tst.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/Tst.cs @@ -28,13 +28,9 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCFX IEnumerable 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 + var firmwares = new Dictionary> { - { "FIRMWARE:pcfx.rom", bios }, + { "FIRMWARE:pcfx.rom", ("PCFX", "BIOS") }, }; DoInit(game, null, disks.ToArray(), "pcfx.wbx", null, deterministic, firmwares); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/FilePiping.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/FilePiping.cs deleted file mode 100644 index d1072d9e35..0000000000 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/FilePiping.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.IO; -using System.IO.Pipes; -using System.Threading; - -namespace BizHawk.Emulation.Cores.Sega.Saturn -{ - /// - /// helpers for moving files across named pipes - /// - 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; - } - } -} diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/LibSaturnus.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/LibSaturnus.cs deleted file mode 100644 index 6cc62dafd9..0000000000 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/LibSaturnus.cs +++ /dev/null @@ -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); - } -} diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs index 441bbfd99d..27e35dd942 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs @@ -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 + 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 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 disks, bool deterministic, Settings settings, - SyncSettings syncSettings) - :base(comm, new Configuration + public Saturnus(CoreComm comm, GameInfo game, + IEnumerable disks, NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic) + : base(comm, "SAT", "Saturn Controller Deck", settings, syncSettings) + { + var firmwares = new Dictionary> { - 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(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(game, null, disks.ToArray(), "ss.wbx", null, deterministic, firmwares); } - protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound) + protected override IDictionary SettingsOverrides { get; } = new Dictionary { - 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 NonSyncSettingNames { get; } = new HashSet { - 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; - } - - } + }; } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/SaturnusControllerDeck.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/SaturnusControllerDeck.cs deleted file mode 100644 index fb82e4b710..0000000000 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/SaturnusControllerDeck.cs +++ /dev/null @@ -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() - .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; - } - - /// TODO verify direction against hardware - private static readonly List AnalogStickRanges = CreateAxisRangePair(0, 128, 255, AxisPairOrientation.RightAndDown); - - private static readonly AxisRange MiscAxisRange = new AxisRange(0, 128, 255); - - public static readonly List MissionAxisRanges = AnalogStickRanges.Concat(new List { MiscAxisRange }).ToList(); - - public static readonly List DualMissionAxisRanges = MissionAxisRanges.Concat(MissionAxisRanges).ToList(); - - public static readonly List ThreeDeeAxisRanges = AnalogStickRanges.Concat(new List { 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; - } - } - } - } -} diff --git a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Controller.cs b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Controller.cs index b2e26cb35d..2197ecca14 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Controller.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Controller.cs @@ -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)) diff --git a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs index e37ac531f9..5d117e89d9 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs @@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Waterbox private LibNymaCore _nyma; protected T DoInit(GameInfo game, byte[] rom, Disc[] discs, string wbxFilename, string extension, bool deterministic, - ICollection> firmwares = null) + IDictionary> firmwares = null) where T : LibNymaCore { var t = PreInit(new WaterboxOptions @@ -53,12 +53,24 @@ namespace BizHawk.Emulation.Cores.Waterbox var portData = GetInputPortsData(); InitSyncSettingsInfo(portData); _nyma.SetFrontendSettingQuery(_settingsQueryDelegate); + var filesToRemove = new List(); 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(); diff --git a/src/BizHawk.Emulation.Cores/Waterbox/Syscalls/MMan.cs b/src/BizHawk.Emulation.Cores/Waterbox/Syscalls/MMan.cs index 368f9a9119..433734528b 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/Syscalls/MMan.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/Syscalls/MMan.cs @@ -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; + } } } diff --git a/src/BizHawk.Emulation.Cores/Waterbox/Syscalls/Syscalls.cs b/src/BizHawk.Emulation.Cores/Waterbox/Syscalls/Syscalls.cs index cf3b9e51c3..3c8d174c37 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/Syscalls/Syscalls.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/Syscalls/Syscalls.cs @@ -14,6 +14,15 @@ namespace BizHawk.Emulation.Cores.Waterbox /// internal partial class Syscalls : IBinaryStateable { + /// + /// 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. + /// + public Func 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; diff --git a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxHost.cs b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxHost.cs index 1d08038656..50eb12060d 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxHost.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxHost.cs @@ -359,6 +359,20 @@ namespace BizHawk.Emulation.Cores.Waterbox return _syscalls.RemoveTransientFile(name); } + /// + /// 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. + /// + public Func MissingFileCallback + { + get => _syscalls.MissingFileCallback; + set => _syscalls.MissingFileCallback = value; + } + private const ulong MAGIC = 0x736b776162727477; private const ulong WATERBOXSTATEVERSION = 2; diff --git a/waterbox/nyma/Interfaces.cpp b/waterbox/nyma/Interfaces.cpp index 22be56d7fe..11c13af65a 100644 --- a/waterbox/nyma/Interfaces.cpp +++ b/waterbox/nyma/Interfaces.cpp @@ -178,7 +178,11 @@ namespace Mednafen FrontendSettingQuery(name, tmp); return std::string(tmp); } - + std::vector MDFN_GetSettingMultiUI(const char *name) + { + // only used in some saturn debug code, not needed + return std::vector(); + } void MDFNMP_Init(uint32 ps, uint32 numpages) {} void MDFNMP_AddRAM(uint32 size, uint32 address, uint8 *RAM, bool use_in_search) // Deprecated diff --git a/waterbox/nyma/common/config.h b/waterbox/nyma/common/config.h index b07b2f7af6..dda29401c7 100644 --- a/waterbox/nyma/common/config.h +++ b/waterbox/nyma/common/config.h @@ -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 diff --git a/waterbox/nyma/mednafen b/waterbox/nyma/mednafen index e89d39cc0c..a8a377c836 160000 --- a/waterbox/nyma/mednafen +++ b/waterbox/nyma/mednafen @@ -1 +1 @@ -Subproject commit e89d39cc0c94ffaf80674e37457987fabf811bf7 +Subproject commit a8a377c836f591c9624c07ed2a26b43e49965921 diff --git a/waterbox/nyma/ss.cpp b/waterbox/nyma/ss.cpp new file mode 100644 index 0000000000..619677802f --- /dev/null +++ b/waterbox/nyma/ss.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include "nyma.h" +#include +#include +#include +namespace MDFN_IEN_SS +{ + #include +} + +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); +} diff --git a/waterbox/nyma/ss.mak b/waterbox/nyma/ss.mak new file mode 100644 index 0000000000..ce521466ba --- /dev/null +++ b/waterbox/nyma/ss.mak @@ -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