Next piece of the puzzle

This probably breaks something.  I am sorry, grab me and I'll fix it
This commit is contained in:
nattthebear 2020-07-12 12:52:27 -04:00
parent 33a0954087
commit 5c5ffed5ff
14 changed files with 131 additions and 155 deletions

View File

@ -423,7 +423,50 @@ namespace BizHawk.Client.Common
throw new NotImplementedException("M3U not supported!");
}
private void LoadOther(string path, CoreComm nextComm, bool forceAccurateCore, HawkFile file, out IEmulator nextEmulator, out RomGame rom, out GameInfo game, out bool cancel)
private static string ForcedCoreToCoreName(string forcedCore)
{
// TODO: Is this yet another list of core names really needed? Let's just make the gamedb less dumb
return forcedCore switch
{
"snes9x" => CoreNames.Snes9X,
"bsnes" => CoreNames.Bsnes,
"quicknes" => CoreNames.QuickNes,
"pico" => CoreNames.PicoDrive,
_ => null
};
}
private IEmulator MakeCoreFromCoreInventory(string systemId, CoreInventoryParameters cip, string forcedCoreGameDb)
{
_config.PreferredCores.TryGetValue(systemId, out var preferredCore);
var forcedCore = ForcedCoreToCoreName(forcedCoreGameDb);
var cores = CoreInventory.Instance.GetCores(systemId)
.OrderBy(c =>
{
if (c.Name == preferredCore)
return (int)CorePriority.UserPreference;
else if (c.Name == forcedCore)
return (int)CorePriority.GameDbPreference;
else
return (int)c.Priority;
})
.ToList();
var exceptions = new List<Exception>();
foreach (var core in cores)
{
try
{
return core.Create(cip);
}
catch (Exception e)
{
exceptions.Add(e);
}
}
throw new AggregateException("No core could load the ROM", exceptions);
}
private void LoadOther(string path, CoreComm nextComm, HawkFile file, out IEmulator nextEmulator, out RomGame rom, out GameInfo game, out bool cancel)
{
cancel = false;
rom = new RomGame(file);
@ -463,92 +506,17 @@ namespace BizHawk.Client.Common
game = rom.GameInfo;
var isXml = false;
if (file.Extension.ToLowerInvariant() == ".xml")
{
game.System = "SNES"; // other xml has already been handled
isXml = true;
}
nextEmulator = null;
if (game.System == null)
return; // The user picked nothing in the Core picker
CoreInventory.Core core;
switch (game.System)
{
case "SNES":
var name = game.ForcedCore?.ToLower() switch
{
"snes9x" => CoreNames.Snes9X,
"bsnes" => CoreNames.Bsnes,
_ => _config.PreferredCores["SNES"]
};
try
{
core = CoreInventory.Instance["SNES", name];
}
catch // TODO: CoreInventory should support some sort of trygetvalue
{
// need to get rid of this hack at some point
nextEmulator = new LibsnesCore(
game,
isXml ? null : rom.FileData,
isXml ? rom.FileData : null,
Path.GetDirectoryName(path.SubstringBefore('|')),
nextComm,
GetCoreSettings<LibsnesCore, LibsnesCore.SnesSettings>(),
GetCoreSyncSettings<LibsnesCore, LibsnesCore.SnesSyncSettings>()
);
return;
}
break;
case "NES":
// apply main spur-of-the-moment switcheroo as lowest priority
var preference = _config.PreferredCores["NES"];
// if user has saw fit to override in gamedb, apply that
if (!string.IsNullOrEmpty(game.ForcedCore))
{
preference = game.ForcedCore.ToLower() switch
{
"quicknes" => CoreNames.QuickNes,
_ => CoreNames.NesHawk
};
}
// but only neshawk is accurate
if (forceAccurateCore)
{
preference = CoreNames.NesHawk;
}
core = CoreInventory.Instance["NES", preference];
break;
case "GB":
case "GBC":
if (_config.GbAsSgb)
{
if (_config.SgbUseBsnes)
{
game.System = "SNES";
game.AddOption("SGB", "");
nextEmulator = new LibsnesCore(
game,
rom.FileData,
null,
null,
nextComm,
GetCoreSettings<LibsnesCore, LibsnesCore.SnesSettings>(),
GetCoreSyncSettings<LibsnesCore, LibsnesCore.SnesSyncSettings>()
);
return;
}
core = CoreInventory.Instance["SGB", CoreNames.SameBoy];
}
else
{
core = CoreInventory.Instance["GB", _config.PreferredCores["GB"]];
game.System = "SGB";
}
break;
case "ChannelF":
@ -567,17 +535,10 @@ namespace BizHawk.Client.Common
);
rom.GameInfo.Name = gameName;
return;
case "GEN":
core = CoreInventory.Instance["GEN", game.ForcedCore?.ToLower() == "pico" ? CoreNames.PicoDrive : CoreNames.Gpgx];
break;
default:
core = _config.PreferredCores.TryGetValue(game.System, out var coreName)
? CoreInventory.Instance[game.System, coreName]
: CoreInventory.Instance[game.System];
break;
}
nextEmulator = core.Create(new CoreInventoryParameters(this)
var cip = new CoreInventoryParameters(this)
{
Comm = nextComm,
Game = game,
@ -593,7 +554,8 @@ namespace BizHawk.Client.Common
},
DeterministicEmulationRequested = Deterministic
});
};
nextEmulator = MakeCoreFromCoreInventory(game.System, cip, game.ForcedCore);
}
private void LoadPSF(string path, CoreComm nextComm, HawkFile file, out IEmulator nextEmulator, out RomGame rom, out GameInfo game)
@ -747,7 +709,7 @@ namespace BizHawk.Client.Common
}
}
public bool LoadRom(string path, CoreComm nextComm, string launchLibretroCore, bool forceAccurateCore = false, int recursiveCount = 0)
public bool LoadRom(string path, CoreComm nextComm, string launchLibretroCore, int recursiveCount = 0)
{
if (path == null) return false;
@ -862,7 +824,7 @@ namespace BizHawk.Client.Common
}
else
{
LoadOther(path, nextComm, forceAccurateCore, file, out nextEmulator, out rom, out game, out cancel); // must be called after LoadXML because of SNES hacks
LoadOther(path, nextComm, file, out nextEmulator, out rom, out game, out cancel); // must be called after LoadXML because of SNES hacks
}
break;
}
@ -894,7 +856,7 @@ namespace BizHawk.Client.Common
DoMessageCallback("Unable to use quicknes, using NESHawk instead");
}
return LoadRom(path, nextComm, launchLibretroCore, true, recursiveCount + 1);
return LoadRom(path, nextComm, launchLibretroCore, recursiveCount + 1);
}
else if (ex is MissingFirmwareException)
{
@ -906,7 +868,7 @@ namespace BizHawk.Client.Common
// To avoid catch-22, disable SGB mode
_config.GbAsSgb = false;
DoMessageCallback("Failed to load a GB rom in SGB mode. Disabling SGB Mode.");
return LoadRom(path, nextComm, launchLibretroCore, false, recursiveCount + 1);
return LoadRom(path, nextComm, launchLibretroCore, recursiveCount + 1);
}
else if (ex is NoAvailableCoreException)
{

View File

@ -16,7 +16,8 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
{
private readonly LibHyperNyma _hyperNyma;
[CoreConstructor(new[] { "PCE", "SGX" })]
[CoreConstructor("PCE", Priority = CorePriority.Low)]
[CoreConstructor("SGX", Priority = CorePriority.Low)]
public HyperNyma(GameInfo game, byte[] rom, CoreComm comm, string extension,
NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
: base(comm, "PCE", "PC Engine Controller", settings, syncSettings)

View File

@ -16,7 +16,8 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
{
private readonly LibTurboNyma _turboNyma;
[CoreConstructor(new[] { "PCE", "SGX" })]
[CoreConstructor("PCE")]
[CoreConstructor("SGX")]
public TurboNyma(GameInfo game, byte[] rom, CoreComm comm, string extension,
NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
: base(comm, "PCE", "PC Engine Controller", settings, syncSettings)

View File

@ -93,7 +93,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
private static byte[] GBA_override = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0xAA, 0x31, 0x90, 0x94, 0x00, 0x00, 0x00, 0x00 };
[CoreConstructor(new[] { "GB", "GBC" })]
[CoreConstructor("GB")]
[CoreConstructor("GBC")]
public GBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ GBSettings settings, GBSyncSettings syncSettings)
{
var ser = new BasicServiceProvider(this);

View File

@ -23,7 +23,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
IBoardInfo, IRomInfo, IDebuggable, ISettable<Gameboy.GambatteSettings, Gameboy.GambatteSyncSettings>,
IGameboyCommon, ICycleTiming, ILinkable
{
[CoreConstructor(new[] { "GB", "GBC" })]
[CoreConstructor("GB")]
[CoreConstructor("GBC")]
public Gameboy(CoreComm comm, GameInfo game, byte[] file, Gameboy.GambatteSettings settings, Gameboy.GambatteSyncSettings syncSettings, bool deterministic)
{
var ser = new BasicServiceProvider(this);

View File

@ -41,6 +41,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
{ }
[CoreConstructor("GB")]
[CoreConstructor("GBC")]
public Sameboy(CoreComm comm, byte[] rom, Settings settings, SyncSettings syncSettings, bool deterministic)
: this(rom, comm, false, settings, syncSettings, deterministic)
{ }

View File

@ -33,7 +33,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
QN.qn_setup_mappers();
}
[CoreConstructor("NES")]
[CoreConstructor("NES", Priority = CorePriority.Low)]
public QuickNES(byte[] file, QuickNESSettings settings, QuickNESSyncSettings syncSettings)
{
FP = OSTailoredCode.IsUnixHost

View File

@ -29,6 +29,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
public unsafe partial class LibsnesCore : IEmulator, IVideoProvider, ISaveRam, IStatable, IInputPollable, IRegionable, ICodeDataLogger,
IDebuggable, ISettable<LibsnesCore.SnesSettings, LibsnesCore.SnesSyncSettings>
{
[CoreConstructor("SGB")]
[CoreConstructor("SNES")]
public LibsnesCore(GameInfo game, byte[] rom, CoreComm comm,
LibsnesCore.SnesSettings settings, LibsnesCore.SnesSyncSettings syncSettings)
:this(game, rom, null, null, comm, settings, syncSettings)
{}
public LibsnesCore(GameInfo game, byte[] romData, byte[] xmlData, string baseRomPath, CoreComm comm,
LibsnesCore.SnesSettings settings, LibsnesCore.SnesSyncSettings syncSettings)
{
@ -47,7 +54,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
CoreComm = comm;
byte[] sgbRomData = null;
if (game["SGB"])
if (game.System == "SGB")
{
if ((romData[0x143] & 0xc0) == 0xc0)
{
@ -112,7 +119,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
romData = newData;
}
if (game["SGB"])
if (game.System == "SGB")
{
IsSGB = true;
SystemId = "SNES";

View File

@ -14,7 +14,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SubGBHawk
public partial class SubGBHawk : IEmulator, IStatable, IInputPollable,
ISettable<GBHawk.GBHawk.GBSettings, GBHawk.GBHawk.GBSyncSettings>, IDebuggable
{
[CoreConstructor(new[] { "GB", "GBC" })]
[CoreConstructor("GB", Priority = CorePriority.SuperLow)]
[CoreConstructor("GBC", Priority = CorePriority.SuperLow)]
public SubGBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ GBHawk.GBHawk.GBSettings settings, GBHawk.GBHawk.GBSyncSettings syncSettings)
{

View File

@ -12,7 +12,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SubNESHawk
public partial class SubNESHawk : IEmulator, IStatable, IInputPollable,
ISettable<NES.NES.NESSettings, NES.NES.NESSyncSettings>
{
[CoreConstructor("NES")]
[CoreConstructor("NES", Priority = CorePriority.SuperLow)]
public SubNESHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ NES.NES.NESSettings settings, NES.NES.NESSyncSettings syncSettings)
{
var subNesSettings = (NES.NES.NESSettings)settings ?? new NES.NES.NESSettings();

View File

@ -21,7 +21,8 @@ namespace BizHawk.Emulation.Cores.PCEngine
IDebuggable, ISettable<PCEngine.PCESettings, PCEngine.PCESyncSettings>, IDriveLight, ICodeDataLogger,
IPceGpuView
{
[CoreConstructor(new[] { "PCE", "SGX" })]
[CoreConstructor("PCE", Priority = CorePriority.Low)]
[CoreConstructor("SGX", Priority = CorePriority.Low)]
public PCEngine(GameInfo game, byte[] rom, PCEngine.PCESettings settings, PCEngine.PCESyncSettings syncSettings)
{
switch (game.System)

View File

@ -19,7 +19,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.PicoDrive
private DiscSectorReader _cdReader;
private bool _isPal;
[CoreConstructor(new[] { "GEN", "32X" })]
[CoreConstructor("GEN", Priority = CorePriority.Low)]
[CoreConstructor("32X")]
public PicoDrive(CoreComm comm, GameInfo game, byte[] rom, bool deterministic, SyncSettings syncSettings)
: this(comm, game, rom, null, deterministic, syncSettings)
{ }

View File

@ -23,7 +23,9 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
public partial class SMS : IEmulator, ISoundProvider, ISaveRam, IInputPollable, IRegionable,
IDebuggable, ISettable<SMS.SmsSettings, SMS.SmsSyncSettings>, ICodeDataLogger
{
[CoreConstructor(new[] { "SMS", "SG", "GG" })]
[CoreConstructor("SMS")]
[CoreConstructor("SG")]
[CoreConstructor("GG")]
public SMS(CoreComm comm, GameInfo game, byte[] rom, SmsSettings settings, SmsSyncSettings syncSettings)
{
var ser = new BasicServiceProvider(this);

View File

@ -28,11 +28,12 @@ namespace BizHawk.Emulation.Cores
// If true, this is a new style constructor that takes a CoreLoadParameters object
private readonly bool _useCoreLoadParameters;
public Core(string name, Type type, ConstructorInfo ctor)
public Core(string name, Type type, ConstructorInfo ctor, CorePriority priority)
{
Name = name;
Type = type;
CTor = ctor;
Priority = priority;
var pp = CTor.GetParameters();
if (pp.Length == 1
@ -65,9 +66,14 @@ namespace BizHawk.Emulation.Cores
}
}
/// <summary>
/// (hopefully) a CoreNames value
/// </summary>
/// <value></value>
public string Name { get; }
public Type Type { get; }
public ConstructorInfo CTor { get; }
public CorePriority Priority { get; }
public Type SettingsType { get; } = typeof(object);
public Type SyncSettingsType { get; } = typeof(object);
@ -116,53 +122,22 @@ namespace BizHawk.Emulation.Cores
}
}
private void ProcessConstructor(Type type, string system, CoreAttribute coreAttr, ConstructorInfo cons)
private void ProcessConstructor(Type type, CoreConstructorAttribute consAttr, CoreAttribute coreAttr, ConstructorInfo cons)
{
Core core = new Core(coreAttr.CoreName, type, cons);
if (!_systems.TryGetValue(system, out var ss))
Core core = new Core(coreAttr.CoreName, type, cons, consAttr.Priority);
if (!_systems.TryGetValue(consAttr.System, out var ss))
{
ss = new List<Core>();
_systems.Add(system, ss);
_systems.Add(consAttr.System, ss);
}
ss.Add(core);
}
/// <summary>
/// find a core matching a particular game.system
/// </summary>
public Core this[string system]
public IEnumerable<Core> GetCores(string system)
{
get
{
List<Core> ss = _systems[system];
if (ss.Count != 1)
{
throw new InvalidOperationException("Ambiguous core selection!");
}
return ss[0];
}
}
/// <summary>
/// find a core matching a particular game.system with a particular CoreAttributes.Name
/// </summary>
public Core this[string system, string core]
{
get
{
List<Core> ss = _systems[system];
foreach (Core c in ss)
{
if (c.Name == core)
{
return c;
}
}
throw new InvalidOperationException("No such core!");
}
_systems.TryGetValue(system, out var cores);
return cores ?? Enumerable.Empty<Core>();
}
/// <summary>
@ -183,9 +158,9 @@ namespace BizHawk.Emulation.Cores
.Where(c => c.GetCustomAttributes(typeof(CoreConstructorAttribute), false).Length > 0);
foreach(var con in cons)
{
foreach (string system in ((CoreConstructorAttribute)con.GetCustomAttributes(typeof(CoreConstructorAttribute), false)[0]).Systems)
foreach (var consAttr in con.GetCustomAttributes(typeof(CoreConstructorAttribute), false).Cast<CoreConstructorAttribute>())
{
ProcessConstructor(typ, system, (CoreAttribute)coreAttr[0], con);
ProcessConstructor(typ, consAttr, (CoreAttribute)coreAttr[0], con);
}
}
}
@ -196,23 +171,45 @@ namespace BizHawk.Emulation.Cores
public static readonly CoreInventory Instance = new CoreInventory(new[] { typeof(CoreInventory).Assembly });
}
[AttributeUsage(AttributeTargets.Constructor)]
public enum CorePriority
{
/// <summary>
/// The gamedb has requested this core for this game
/// </summary>
GameDbPreference = -300,
/// <summary>
/// The user has indicated in preferences that this is their favourite core
/// </summary>
UserPreference = -200,
/// <summary>
/// A very good core that should be prefered over normal cores. Don't use this?
/// </summary>
High = -100,
/// <summary>
/// Most cores should use this
/// </summary>
Normal = 0,
/// <summary>
/// Experimental, special use, or garbage core
/// </summary>
Low = 100,
/// <summary>
/// TODO: Do we need this? Does it need a better name?
/// </summary>
SuperLow = 200,
}
[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = true)]
public sealed class CoreConstructorAttribute : Attribute
{
private readonly List<string> _systems = new List<string>();
/// <remarks>TODO neither array nor <see cref="IEnumerable{T}"/> is the correct collection to be using here, try <see cref="IReadOnlyList{T}"/>/<see cref="IReadOnlyCollection{T}"/> instead</remarks>
public CoreConstructorAttribute(string[] systems)
{
_systems.AddRange(systems);
}
public string System { get; }
public CoreConstructorAttribute(string system)
{
_systems.Add(system);
System = system;
}
public IEnumerable<string> Systems => _systems;
public CorePriority Priority { get; set; }
}
/// <summary>