diff --git a/src/BizHawk.Client.Common/RomLoader.cs b/src/BizHawk.Client.Common/RomLoader.cs index 7e9719f7cb..ebf6e2ae47 100644 --- a/src/BizHawk.Client.Common/RomLoader.cs +++ b/src/BizHawk.Client.Common/RomLoader.cs @@ -39,6 +39,17 @@ namespace BizHawk.Client.Common { public class RomLoader { + private class DiscGame : IDiscGame + { + public Disc DiscData { get; set; } + public DiscType DiscType { get; set; } + } + private class RomGameFake : IRomGame + { + public byte[] RomData { get; set; } + public byte[] FileData { get; set; } + public string Extension { get; set; } + } private readonly Config _config; private readonly FirmwareManager _firmwareManager; @@ -303,26 +314,34 @@ namespace BizHawk.Client.Common } } + CoreLoadParameters MakeCLP(GameInfo g) + where TEmulator : IEmulator + { + return new CoreLoadParameters + { + Comm = nextComm, + Game = g, + Settings = GetCoreSettings(), + SyncSettings = GetCoreSyncSettings(), + Discs = + { + new DiscGame + { + DiscData = disc, + DiscType = new DiscIdentifier(disc).DetectDiscType() + } + }, + DeterministicEmulationRequested = Deterministic + }; + } + switch (game.System) { case "GEN": - nextEmulator = new GPGX( - nextComm, - game, - null, - new[] { disc }, - GetCoreSettings(), - GetCoreSyncSettings() - ); + nextEmulator = new GPGX(MakeCLP(game)); break; case "SAT": - nextEmulator = new Saturnus( - nextComm, game, - new[] { disc }, - GetCoreSettings(), - GetCoreSyncSettings(), - Deterministic - ); + nextEmulator = new Saturnus(MakeCLP(game)); break; case "PSX": nextEmulator = new Octoshock( @@ -336,10 +355,7 @@ namespace BizHawk.Client.Common ); break; case "PCFX": - nextEmulator = new Tst(nextComm, game, new[] { disc }, - GetCoreSettings(), - GetCoreSyncSettings(), - Deterministic); + nextEmulator = new Tst(MakeCLP(game)); break; case "PCE": // TODO: this is clearly not used, its set to PCE by code above case "PCECD": @@ -353,22 +369,8 @@ namespace BizHawk.Client.Common GetCoreSettings(), GetCoreSyncSettings() ), - CoreNames.HyperNyma => new HyperNyma( - game, - new[] { disc }, - nextComm, - GetCoreSettings(), - GetCoreSyncSettings(), - Deterministic - ), - _ => new TurboNyma( - game, - new[] { disc }, - nextComm, - GetCoreSettings(), - GetCoreSyncSettings(), - Deterministic - ) + CoreNames.HyperNyma => new HyperNyma(MakeCLP(game)), + _ => new TurboNyma(MakeCLP(game)), }; break; default: @@ -671,6 +673,27 @@ namespace BizHawk.Client.Common var xmlGame = XmlGame.Create(file); // if load fails, are we supposed to retry as a bsnes XML???????? game = xmlGame.GI; + CoreLoadParameters MakeCLP(GameInfo g, IEnumerable disques) + where TEmulator : IEmulator + { + return new CoreLoadParameters + { + Comm = nextComm, + Game = g, + Settings = GetCoreSettings(), + SyncSettings = GetCoreSyncSettings(), + Discs = disques + .Select(d => + (IDiscGame)new DiscGame + { + DiscData = d, + DiscType = new DiscIdentifier(d).DetectDiscType() + }) + .ToList(), + DeterministicEmulationRequested = Deterministic + }; + } + switch (game.System) { case "GB": @@ -820,35 +843,28 @@ namespace BizHawk.Client.Common case "SAT": var saturnDiscs = DiscsFromXml(xmlGame, "SAT", DiscType.SegaSaturn); if (saturnDiscs.Count == 0) return false; - nextEmulator = new Saturnus( - nextComm, game, - saturnDiscs, - GetCoreSettings(), - GetCoreSyncSettings(), - Deterministic - ); + nextEmulator = new Saturnus(MakeCLP(game, saturnDiscs)); return true; case "PCFX": var pcfxDiscs = DiscsFromXml(xmlGame, "PCFX", DiscType.PCFX); if (pcfxDiscs.Count == 0) return false; - nextEmulator = new Tst(nextComm, game, pcfxDiscs, - GetCoreSettings(), - GetCoreSyncSettings(), - Deterministic); + nextEmulator = new Tst(MakeCLP(game, pcfxDiscs)); return true; case "GEN": + { var genDiscs = DiscsFromXml(xmlGame, "GEN", DiscType.MegaCD); - var romBytes = xmlGame.Assets.FirstOrDefault(kvp => !Disc.IsValidExtension(kvp.Key)).Value; - if (genDiscs.Count == 0 && romBytes == null) return false; - nextEmulator = new GPGX( - nextComm, - game, - romBytes, - genDiscs, - GetCoreSettings(), - GetCoreSyncSettings() - ); + var genRoms = xmlGame.Assets.Where(kvp => !Disc.IsValidExtension(kvp.Key)).ToList(); + if (genDiscs.Count == 0 && genRoms.Count == 0) return false; + var clp = MakeCLP(game, genDiscs); + clp.Roms.AddRange(genRoms.Select(kvp => new RomGameFake + { + RomData = kvp.Value, + FileData = kvp.Value, // TODO: Hope no one needed anything special here + Extension = Path.GetExtension(kvp.Key) + })); + nextEmulator = new GPGX(clp); return true; + } case "Game Gear": var leftBytesGG = xmlGame.Assets[0].Value; var rightBytesGG = xmlGame.Assets[1].Value; diff --git a/src/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/src/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 38d9a30907..9da7006f0a 100644 --- a/src/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/src/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -5,6 +5,7 @@ netstandard2.0 + diff --git a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/HyperNyma.cs b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/HyperNyma.cs index 33fec8e2be..f65b7f0037 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/HyperNyma.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/HyperNyma.cs @@ -23,16 +23,15 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE { _hyperNyma = DoInit(game, rom, null, "hyper.wbx", extension, deterministic); } - public HyperNyma(GameInfo game, Disc[] discs, CoreComm comm, - NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic) - : base(comm, "PCE", "PC Engine Controller", settings, syncSettings) + public HyperNyma(CoreLoadParameters lp) + : base(lp.Comm, "PCE", "PC Engine Controller", lp.Settings, lp.SyncSettings) { var firmwares = new Dictionary { { "FIRMWARE:syscard3.pce", ("PCECD", "Bios") }, // { "FIRMWARE:gecard.pce", ("PCECD", "GE-Bios") }, }; - _hyperNyma = DoInit(game, null, discs, "hyper.wbx", null, deterministic, firmwares); + _hyperNyma = DoInit(lp, "hyper.wbx", firmwares); } public override string SystemId => IsSgx ? "SGX" : "PCE"; diff --git a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TurboNyma.cs b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TurboNyma.cs index 846dd165b0..f8fa795684 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TurboNyma.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TurboNyma.cs @@ -25,18 +25,16 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE SettingOverrides["pce.disable_bram_hucard"].Default = "0"; _turboNyma = DoInit(game, rom, null, "turbo.wbx", extension, deterministic); } - public TurboNyma(GameInfo game, Disc[] discs, CoreComm comm, - NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic) - : base(comm, "PCE", "PC Engine Controller", settings, syncSettings) + public TurboNyma(CoreLoadParameters lp) + : base(lp.Comm, "PCE", "PC Engine Controller", lp.Settings, lp.SyncSettings) { - var ids = discs.Select(d => new DiscIdentifier(d).DetectDiscType()) - .ToList(); + var ids = lp.Discs.Select(dg => dg.DiscType).ToList(); var firmwares = new Dictionary(); if (ids.Contains(DiscType.TurboCD)) firmwares.Add("FIRMWARE:syscard3.pce", ("PCECD", "Bios")); if (ids.Contains(DiscType.TurboGECD)) firmwares.Add("FIRMWARE:gecard.pce", ("PCECD", "GE-Bios")); - _turboNyma = DoInit(game, null, discs, "turbo.wbx", null, deterministic, firmwares); + _turboNyma = DoInit(lp, "turbo.wbx", firmwares); } public override string SystemId => IsSgx ? "SGX" : "PCE"; diff --git a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/Tst.cs b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/Tst.cs index 5ee71a7eab..1a5fe45124 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/Tst.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/Tst.cs @@ -17,23 +17,15 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCFX "https://mednafen.github.io/releases/", false, "PC-FX")] public class Tst : NymaCore { - [CoreConstructor("PCFX")] - public Tst(CoreComm comm, NymaSettings settings, NymaSyncSettings syncSettings) - : base(comm, "PCFX", "PC-FX Controller", settings, syncSettings) - { - throw new InvalidOperationException("To load a PC-FX game, please load the CUE file and not the BIN file."); - } - - public Tst(CoreComm comm, GameInfo game, - IEnumerable disks, NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic) - : base(comm, "PCFX", "PC-FX Controller", settings, syncSettings) + public Tst(CoreLoadParameters lp) + : base(lp.Comm, "PCFX", "PC-FX Controller", lp.Settings, lp.SyncSettings) { var firmwares = new Dictionary { { "FIRMWARE:pcfx.rom", ("PCFX", "BIOS") }, }; - DoInit(game, null, disks.ToArray(), "pcfx.wbx", null, deterministic, firmwares); + DoInit(lp, "pcfx.wbx", firmwares); } protected override IDictionary SettingOverrides { get; } = new Dictionary diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs index f09191c7c3..7b7b81ec0f 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs @@ -26,9 +26,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.Saturn throw new InvalidOperationException("To load a Saturn game, please load the CUE file and not the BIN file."); } - public Saturnus(CoreComm comm, GameInfo game, - IEnumerable disks, NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic) - : base(comm, "SAT", "Saturn Controller", settings, syncSettings) + public Saturnus(CoreLoadParameters lp) + : base(lp.Comm, "SAT", "Saturn Controller", lp.Settings, lp.SyncSettings) { var firmwares = new Dictionary { @@ -38,8 +37,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.Saturn { "FIRMWARE:$ULTRA", ("SAT", "ULTRAMAN") }, // { "FIRMWARE:$SATAR", ("SAT", "AR") }, // action replay garbage }; - - DoInit(game, null, disks.ToArray(), "ss.wbx", null, deterministic, firmwares); + DoInit(lp, "ss.wbx", firmwares); } protected override IDictionary SettingOverrides { get; } = new Dictionary diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs index a671ec7438..73251be83b 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs @@ -24,12 +24,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx IInputPollable, IDebuggable, IDriveLight, ICodeDataLogger, IDisassemblable { [CoreConstructor("GEN")] - public GPGX(CoreComm comm, GameInfo game, byte[] file, GPGXSettings settings, GPGXSyncSettings syncSettings) - : this(comm, game, file, null, settings, syncSettings) - { - } - - public GPGX(CoreComm comm, GameInfo game, byte[] rom, IEnumerable cds, GPGXSettings settings, GPGXSyncSettings syncSettings) + public GPGX(CoreLoadParameters lp) { LoadCallback = new LibGPGX.load_archive_cb(load_archive); _inputCallback = new LibGPGX.input_cb(input_callback); @@ -45,22 +40,22 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx // http://www.sega-16.com/forum/showthread.php?4398-Forgotten-Worlds-giving-you-GAME-OVER-immediately-Fix-inside&highlight=forgotten%20worlds //hack, don't use - if (rom != null && rom.Length > 32 * 1024 * 1024) + if (lp.Roms.FirstOrDefault()?.RomData.Length > 32 * 1024 * 1024) { throw new InvalidOperationException("ROM too big! Did you try to load a CD as a ROM?"); } _elf = new WaterboxHost(new WaterboxOptions { - Path = comm.CoreFileProvider.DllPath(), + Path = lp.Comm.CoreFileProvider.DllPath(), Filename = "gpgx.wbx", SbrkHeapSizeKB = 512, SealedHeapSizeKB = 36 * 1024, InvisibleHeapSizeKB = 4 * 1024, PlainHeapSizeKB = 64 + 1024, MmapHeapSizeKB = 1 * 1024, - SkipCoreConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck), - SkipMemoryConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck), + SkipCoreConsistencyCheck = lp.Comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck), + SkipMemoryConsistencyCheck = lp.Comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck), }); var callingConventionAdapter = CallingConventionAdapters.MakeWaterbox(new Delegate[] @@ -72,17 +67,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx using (_elf.EnterExit()) { Core = BizInvoker.GetInvoker(_elf, _elf, callingConventionAdapter); - _syncSettings = (GPGXSyncSettings)syncSettings ?? new GPGXSyncSettings(); - _settings = (GPGXSettings)settings ?? new GPGXSettings(); + _syncSettings = lp.SyncSettings ?? new GPGXSyncSettings(); + _settings = lp.Settings ?? new GPGXSettings(); - CoreComm = comm; + CoreComm = lp.Comm; - _romfile = rom; + _romfile = lp.Roms.FirstOrDefault().RomData; - if (cds != null) + if (lp.Discs.Count > 0) { - _cds = cds.ToArray(); - _cdReaders = cds.Select(c => new DiscSectorReader(c)).ToArray(); + _cds = lp.Discs.Select(d => d.DiscData).ToArray(); + _cdReaders = _cds.Select(c => new DiscSectorReader(c)).ToArray(); Core.gpgx_set_cdd_callback(cd_callback_handle); DriveLightEnabled = true; } @@ -90,7 +85,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx LibGPGX.INPUT_SYSTEM system_a = SystemForSystem(_syncSettings.ControlTypeLeft); LibGPGX.INPUT_SYSTEM system_b = SystemForSystem(_syncSettings.ControlTypeRight); - var initResult = Core.gpgx_init(romextension, LoadCallback, _syncSettings.GetNativeSettings(game)); + var initResult = Core.gpgx_init(romextension, LoadCallback, _syncSettings.GetNativeSettings(lp.Game)); if (!initResult) throw new Exception($"{nameof(Core.gpgx_init)}() failed"); diff --git a/src/BizHawk.Emulation.Cores/CoreInventory.cs b/src/BizHawk.Emulation.Cores/CoreInventory.cs index 26dbd7c42c..a4f1d41638 100644 --- a/src/BizHawk.Emulation.Cores/CoreInventory.cs +++ b/src/BizHawk.Emulation.Cores/CoreInventory.cs @@ -15,12 +15,20 @@ namespace BizHawk.Emulation.Cores public class Core { + private class RomGameFake : IRomGame + { + public byte[] RomData { get; set; } + public byte[] FileData { get; set; } + public string Extension { get; set; } + } // expected names and types of the parameters private static readonly Dictionary ParamTypes = new Dictionary(); // map parameter names to locations in the constructor private readonly Dictionary _paramMap = new Dictionary(); + private readonly bool _useCoreLoadParameters; + static Core() { var pp = typeof(Core).GetMethod("Create")?.GetParameters(); @@ -40,6 +48,16 @@ namespace BizHawk.Emulation.Cores CTor = ctor; var pp = CTor.GetParameters(); + if (pp.Length == 1 + && pp[0].ParameterType.IsGenericType + && pp[0].ParameterType.GetGenericTypeDefinition() == typeof(CoreLoadParameters<,>) + ) + { + _useCoreLoadParameters = true; + SettingsType = pp[0].ParameterType.GetGenericArguments()[0]; + SyncSettingsType = pp[0].ParameterType.GetGenericArguments()[1]; + return; + } for (int i = 0; i < pp.Length ; i++) { var p = pp[i]; @@ -93,6 +111,24 @@ namespace BizHawk.Emulation.Cores string extension ) { + if (_useCoreLoadParameters) + { + var paramType = typeof(CoreLoadParameters<,>).MakeGenericType(new[] { SettingsType, SyncSettingsType }); + // TODO: clean this up + dynamic param = Activator.CreateInstance(paramType); + param.Comm = comm; + param.Game = game; + param.Settings = settings; + param.SyncSettings = syncSettings; + param.Roms.Add(new RomGameFake + { + RomData = rom, + FileData = file, + Extension = extension + }); + param.DeterministicEmulationRequested = deterministic; + return (IEmulator)CTor.Invoke(new object[] { param }); + } object[] o = new object[_paramMap.Count]; Bp(o, "comm", comm); Bp(o, "game", game); diff --git a/src/BizHawk.Emulation.Cores/CoreLoadParameters.cs b/src/BizHawk.Emulation.Cores/CoreLoadParameters.cs new file mode 100644 index 0000000000..82e3da06f9 --- /dev/null +++ b/src/BizHawk.Emulation.Cores/CoreLoadParameters.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Linq; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.DiscSystem; + +namespace BizHawk.Emulation.Cores +{ + public interface IRomGame + { + byte[] RomData { get; } + byte[] FileData { get; } + string Extension { get; } + } + public interface IDiscGame + { + Disc DiscData { get; } + DiscType DiscType { get; } + } + public class CoreLoadParameters + { + public CoreComm Comm { get; set; } + public GameInfo Game { get; set; } + /// + /// Settings previously returned from the core. May be null. + /// + public TSettiing Settings { get; set; } + /// + /// Sync Settings previously returned from the core. May be null. + /// + public TSync SyncSettings { get; set; } + /// + /// All roms that should be loaded as part of this core load. + /// Order may be significant. Does not include firmwares or other general resources. + /// + public List Roms { get; set; } = new List(); + /// + /// All discs that should be loaded as part of this core load. + /// Order may be significant. + /// + /// + public List Discs { get; set; } = new List(); + public bool DeterministicEmulationRequested { get; set; } + } +} diff --git a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs index 6c89ffa0c1..9a3df175cc 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs @@ -28,6 +28,23 @@ namespace BizHawk.Emulation.Cores.Waterbox } private LibNymaCore _nyma; + protected T DoInit( + CoreLoadParameters lp, + string wbxFilename, + IDictionary firmwares = null + ) + where T : LibNymaCore + { + return DoInit( + lp.Game, + lp.Roms.FirstOrDefault()?.RomData, + lp.Discs.Select(d => d.DiscData).ToArray(), + wbxFilename, + lp.Roms.FirstOrDefault()?.Extension, + lp.DeterministicEmulationRequested, + firmwares + ); + } protected T DoInit(GameInfo game, byte[] rom, Disc[] discs, string wbxFilename, string extension, bool deterministic, IDictionary firmwares = null) where T : LibNymaCore