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.
This commit is contained in:
nattthebear 2020-07-11 16:01:12 -04:00
parent 3a820302e3
commit cc9d7df9f7
10 changed files with 196 additions and 100 deletions

View File

@ -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<TSetting, TSync> MakeCLP<TEmulator, TSetting, TSync>(GameInfo g)
where TEmulator : IEmulator
{
return new CoreLoadParameters<TSetting, TSync>
{
Comm = nextComm,
Game = g,
Settings = GetCoreSettings<TEmulator, TSetting>(),
SyncSettings = GetCoreSyncSettings<TEmulator, TSync>(),
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<GPGX, GPGX.GPGXSettings>(),
GetCoreSyncSettings<GPGX, GPGX.GPGXSyncSettings>()
);
nextEmulator = new GPGX(MakeCLP<GPGX, GPGX.GPGXSettings, GPGX.GPGXSyncSettings>(game));
break;
case "SAT":
nextEmulator = new Saturnus(
nextComm, game,
new[] { disc },
GetCoreSettings<Saturnus, NymaCore.NymaSettings>(),
GetCoreSyncSettings<Saturnus, NymaCore.NymaSyncSettings>(),
Deterministic
);
nextEmulator = new Saturnus(MakeCLP<Saturnus, NymaCore.NymaSettings, NymaCore.NymaSyncSettings>(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<Tst, NymaCore.NymaSettings>(),
GetCoreSyncSettings<Tst, NymaCore.NymaSyncSettings>(),
Deterministic);
nextEmulator = new Tst(MakeCLP<Tst, NymaCore.NymaSettings, NymaCore.NymaSyncSettings>(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<PCEngine, PCEngine.PCESettings>(),
GetCoreSyncSettings<PCEngine, PCEngine.PCESyncSettings>()
),
CoreNames.HyperNyma => new HyperNyma(
game,
new[] { disc },
nextComm,
GetCoreSettings<HyperNyma, NymaCore.NymaSettings>(),
GetCoreSyncSettings<HyperNyma, NymaCore.NymaSyncSettings>(),
Deterministic
),
_ => new TurboNyma(
game,
new[] { disc },
nextComm,
GetCoreSettings<TurboNyma, NymaCore.NymaSettings>(),
GetCoreSyncSettings<TurboNyma, NymaCore.NymaSyncSettings>(),
Deterministic
)
CoreNames.HyperNyma => new HyperNyma(MakeCLP<HyperNyma, NymaCore.NymaSettings, NymaCore.NymaSyncSettings>(game)),
_ => new TurboNyma(MakeCLP<TurboNyma, NymaCore.NymaSettings, NymaCore.NymaSyncSettings>(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<TSetting, TSync> MakeCLP<TEmulator, TSetting, TSync>(GameInfo g, IEnumerable<Disc> disques)
where TEmulator : IEmulator
{
return new CoreLoadParameters<TSetting, TSync>
{
Comm = nextComm,
Game = g,
Settings = GetCoreSettings<TEmulator, TSetting>(),
SyncSettings = GetCoreSyncSettings<TEmulator, TSync>(),
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<Saturnus, NymaCore.NymaSettings>(),
GetCoreSyncSettings<Saturnus, NymaCore.NymaSyncSettings>(),
Deterministic
);
nextEmulator = new Saturnus(MakeCLP<Saturnus, NymaCore.NymaSettings, NymaCore.NymaSyncSettings>(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<Tst, NymaCore.NymaSettings>(),
GetCoreSyncSettings<Tst, NymaCore.NymaSyncSettings>(),
Deterministic);
nextEmulator = new Tst(MakeCLP<Tst, NymaCore.NymaSettings, NymaCore.NymaSyncSettings>(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<GPGX, GPGX.GPGXSettings>(),
GetCoreSyncSettings<GPGX, GPGX.GPGXSyncSettings>()
);
var genRoms = xmlGame.Assets.Where(kvp => !Disc.IsValidExtension(kvp.Key)).ToList();
if (genDiscs.Count == 0 && genRoms.Count == 0) return false;
var clp = MakeCLP<GPGX, GPGX.GPGXSettings, GPGX.GPGXSyncSettings>(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;

View File

@ -5,6 +5,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="System.Memory" Version="4.5.4" />
<Reference Include="FlatBuffers.Core" HintPath="$(ProjectDir)../../References/FlatBuffers.Core.dll" Private="true" />
<Reference Include="Virtu" HintPath="$(ProjectDir)../../References/Virtu.dll" Private="true" />

View File

@ -23,16 +23,15 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
{
_hyperNyma = DoInit<LibHyperNyma>(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<NymaSettings, NymaSyncSettings> lp)
: base(lp.Comm, "PCE", "PC Engine Controller", lp.Settings, lp.SyncSettings)
{
var firmwares = new Dictionary<string, (string, string)>
{
{ "FIRMWARE:syscard3.pce", ("PCECD", "Bios") },
// { "FIRMWARE:gecard.pce", ("PCECD", "GE-Bios") },
};
_hyperNyma = DoInit<LibHyperNyma>(game, null, discs, "hyper.wbx", null, deterministic, firmwares);
_hyperNyma = DoInit<LibHyperNyma>(lp, "hyper.wbx", firmwares);
}
public override string SystemId => IsSgx ? "SGX" : "PCE";

View File

@ -25,18 +25,16 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
SettingOverrides["pce.disable_bram_hucard"].Default = "0";
_turboNyma = DoInit<LibTurboNyma>(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<NymaSettings, NymaSyncSettings> 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<string, (string, string)>();
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<LibTurboNyma>(game, null, discs, "turbo.wbx", null, deterministic, firmwares);
_turboNyma = DoInit<LibTurboNyma>(lp, "turbo.wbx", firmwares);
}
public override string SystemId => IsSgx ? "SGX" : "PCE";

View File

@ -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<Disc> disks, NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
: base(comm, "PCFX", "PC-FX Controller", settings, syncSettings)
public Tst(CoreLoadParameters<NymaSettings, NymaSyncSettings> lp)
: base(lp.Comm, "PCFX", "PC-FX Controller", lp.Settings, lp.SyncSettings)
{
var firmwares = new Dictionary<string, (string, string)>
{
{ "FIRMWARE:pcfx.rom", ("PCFX", "BIOS") },
};
DoInit<LibNymaCore>(game, null, disks.ToArray(), "pcfx.wbx", null, deterministic, firmwares);
DoInit<LibNymaCore>(lp, "pcfx.wbx", firmwares);
}
protected override IDictionary<string, SettingOverride> SettingOverrides { get; } = new Dictionary<string, SettingOverride>

View File

@ -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<Disc> disks, NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
: base(comm, "SAT", "Saturn Controller", settings, syncSettings)
public Saturnus(CoreLoadParameters<NymaSettings, NymaSyncSettings> lp)
: base(lp.Comm, "SAT", "Saturn Controller", lp.Settings, lp.SyncSettings)
{
var firmwares = new Dictionary<string, (string, string)>
{
@ -38,8 +37,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.Saturn
{ "FIRMWARE:$ULTRA", ("SAT", "ULTRAMAN") },
// { "FIRMWARE:$SATAR", ("SAT", "AR") }, // action replay garbage
};
DoInit<LibNymaCore>(game, null, disks.ToArray(), "ss.wbx", null, deterministic, firmwares);
DoInit<LibNymaCore>(lp, "ss.wbx", firmwares);
}
protected override IDictionary<string, SettingOverride> SettingOverrides { get; } = new Dictionary<string, SettingOverride>

View File

@ -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<Disc> cds, GPGXSettings settings, GPGXSyncSettings syncSettings)
public GPGX(CoreLoadParameters<GPGXSettings, GPGXSyncSettings> 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<LibGPGX>(_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");

View File

@ -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<string, Type> ParamTypes = new Dictionary<string, Type>();
// map parameter names to locations in the constructor
private readonly Dictionary<string, int> _paramMap = new Dictionary<string, int>();
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);

View File

@ -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<TSettiing, TSync>
{
public CoreComm Comm { get; set; }
public GameInfo Game { get; set; }
/// <summary>
/// Settings previously returned from the core. May be null.
/// </summary>
public TSettiing Settings { get; set; }
/// <summary>
/// Sync Settings previously returned from the core. May be null.
/// </summary>
public TSync SyncSettings { get; set; }
/// <summary>
/// All roms that should be loaded as part of this core load.
/// Order may be significant. Does not include firmwares or other general resources.
/// </summary>
public List<IRomGame> Roms { get; set; } = new List<IRomGame>();
/// <summary>
/// All discs that should be loaded as part of this core load.
/// Order may be significant.
/// </summary>
/// <value></value>
public List<IDiscGame> Discs { get; set; } = new List<IDiscGame>();
public bool DeterministicEmulationRequested { get; set; }
}
}

View File

@ -28,6 +28,23 @@ namespace BizHawk.Emulation.Cores.Waterbox
}
private LibNymaCore _nyma;
protected T DoInit<T>(
CoreLoadParameters<NymaSettings, NymaSyncSettings> lp,
string wbxFilename,
IDictionary<string, (string SystemID, string FirmwareID)> firmwares = null
)
where T : LibNymaCore
{
return DoInit<T>(
lp.Game,
lp.Roms.FirstOrDefault()?.RomData,
lp.Discs.Select(d => d.DiscData).ToArray(),
wbxFilename,
lp.Roms.FirstOrDefault()?.Extension,
lp.DeterministicEmulationRequested,
firmwares
);
}
protected T DoInit<T>(GameInfo game, byte[] rom, Disc[] discs, string wbxFilename, string extension, bool deterministic,
IDictionary<string, (string SystemID, string FirmwareID)> firmwares = null)
where T : LibNymaCore