From cc9d7df9f7b07a1405af2e0a96bf35e8f805eeca Mon Sep 17 00:00:00 2001 From: nattthebear Date: Sat, 11 Jul 2020 16:01:12 -0400 Subject: [PATCH] WIP of some romloader cleanups This one commit doesn't make things cleaner by itself, no. The rough idea is that eventually (R) we'll pass all cores that currently use mangled arse-custom constructors with custom code all o'er the place a single, awesome, CoreLoadParameters object. Then the romloader can be changed to just synthesize this object on one common codepath, some bs for core preferences, and boom everything is perfect. Sort of. --- src/BizHawk.Client.Common/RomLoader.cs | 128 ++++++++++-------- .../BizHawk.Emulation.Cores.csproj | 1 + .../Consoles/NEC/PCE/HyperNyma.cs | 7 +- .../Consoles/NEC/PCE/TurboNyma.cs | 10 +- .../Consoles/NEC/PCFX/Tst.cs | 14 +- .../Consoles/Sega/Saturn/Saturnus.cs | 8 +- .../Consoles/Sega/gpgx64/GPGX.cs | 31 ++--- src/BizHawk.Emulation.Cores/CoreInventory.cs | 36 +++++ .../CoreLoadParameters.cs | 44 ++++++ .../Waterbox/NymaCore.cs | 17 +++ 10 files changed, 196 insertions(+), 100 deletions(-) create mode 100644 src/BizHawk.Emulation.Cores/CoreLoadParameters.cs 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