diff --git a/src/BizHawk.Client.Common/config/Config.cs b/src/BizHawk.Client.Common/config/Config.cs
index 364dfdd603f..886cb32d599 100644
--- a/src/BizHawk.Client.Common/config/Config.cs
+++ b/src/BizHawk.Client.Common/config/Config.cs
@@ -18,50 +18,15 @@ public class Config
{
public static string ControlDefaultPath => Path.Combine(PathUtils.ExeDirectoryPath, "defctrl.json");
- ///
- /// CoreNames[0] is the default (out-of-the-box) core.
- /// AppliesTo are concatenated to make the submenu's label, and
- /// Config.PreferredCores[AppliesTo[0]] (lookup on global instance) determines which option is shown as checked.
- /// The order within submenus and the order of the submenus themselves are determined by the declaration order here.
- ///
- public static readonly IReadOnlyList<(string[] AppliesTo, string[] CoreNames)> CorePickerUIData = new List<(string[], string[])>
- {
- ([ VSystemID.Raw.A26 ],
- [ CoreNames.Atari2600Hawk, CoreNames.Stella ]),
- ([ VSystemID.Raw.Satellaview ],
- [ CoreNames.Bsnes115, CoreNames.SubBsnes115 ]),
- ([ VSystemID.Raw.GB, VSystemID.Raw.GBC ],
- [ CoreNames.Gambatte, CoreNames.Sameboy, CoreNames.GbHawk, CoreNames.SubGbHawk ]),
- ([ VSystemID.Raw.GBL ],
- [ CoreNames.GambatteLink, CoreNames.GBHawkLink, CoreNames.GBHawkLink3x, CoreNames.GBHawkLink4x ]),
- ([ VSystemID.Raw.SGB ],
- [ CoreNames.Gambatte, CoreNames.Bsnes115, CoreNames.SubBsnes115, CoreNames.Bsnes ]),
- ([ VSystemID.Raw.GEN ],
- [ CoreNames.Gpgx, CoreNames.PicoDrive ]),
- ([ VSystemID.Raw.N64 ],
- [ CoreNames.Mupen64Plus, CoreNames.Ares64 ]),
- ([ VSystemID.Raw.NES ],
- [ CoreNames.QuickNes, CoreNames.NesHawk, CoreNames.SubNesHawk ]),
- ([ VSystemID.Raw.PCE, VSystemID.Raw.PCECD, VSystemID.Raw.SGX, VSystemID.Raw.SGXCD ],
- [ CoreNames.TurboNyma, CoreNames.HyperNyma, CoreNames.PceHawk ]),
- ([ VSystemID.Raw.PSX ],
- [ CoreNames.Nymashock, CoreNames.Octoshock ]),
- ([ VSystemID.Raw.SMS, VSystemID.Raw.GG, VSystemID.Raw.SG ],
- [ CoreNames.Gpgx, CoreNames.SMSHawk ]),
- ([ VSystemID.Raw.SNES ],
- [ CoreNames.Snes9X, CoreNames.Bsnes115, CoreNames.SubBsnes115, CoreNames.Faust, CoreNames.Bsnes ]),
- ([ VSystemID.Raw.TI83 ],
- [ CoreNames.Emu83, CoreNames.TI83Hawk ]),
- };
-
public static Dictionary GenDefaultCorePreferences()
{
Dictionary dict = new();
- foreach (var (appliesTo, coreNames) in CorePickerUIData)
+ foreach(var (systemId, cores) in CoreInventory.Instance.AllCores)
{
- var defaultCore = coreNames[0];
- foreach (var sysID in appliesTo) dict[sysID] = defaultCore;
+ if (cores.Count > 1)
+ dict[systemId] = cores.Find(core => core.Priority == CorePriority.DefaultPreference)?.Name;
}
+
return dict;
}
diff --git a/src/BizHawk.Client.EmuHawk/MainForm.cs b/src/BizHawk.Client.EmuHawk/MainForm.cs
index 2d9ccd8ec28..f251d438664 100644
--- a/src/BizHawk.Client.EmuHawk/MainForm.cs
+++ b/src/BizHawk.Client.EmuHawk/MainForm.cs
@@ -75,30 +75,32 @@ private void MainForm_Load(object sender, EventArgs e)
}
}
- foreach (var (appliesTo, coreNames) in Config.CorePickerUIData)
+ foreach (var (systemIds, coreNames) in CoreInventory.Instance.SystemGroups
+ .Where(tuple => tuple.CoreNames.Count >= 2)
+ .OrderBy(tuple => tuple.SystemIds[0]))
{
- var submenu = new ToolStripMenuItem { Text = string.Join(" | ", appliesTo) };
+ var submenu = new ToolStripMenuItem { Text = string.Join(" | ", systemIds) };
submenu.DropDownItems.AddRange(coreNames.Select(coreName => {
var entry = new ToolStripMenuItem { Text = coreName };
entry.Click += (_, _) =>
{
string currentCoreName = Emulator.Attributes().CoreName;
if (coreName != currentCoreName && coreNames.Contains(currentCoreName)) FlagNeedsReboot();
- foreach (string system in appliesTo)
- Config.PreferredCores[system] = coreName;
+ foreach (string systemId in systemIds)
+ Config.PreferredCores[systemId] = coreName;
};
return (ToolStripItem) entry;
}).ToArray());
submenu.DropDownOpened += (openedSender, _1) =>
{
- _ = Config.PreferredCores.TryGetValue(appliesTo[0], out var preferred);
+ _ = Config.PreferredCores.TryGetValue(systemIds[0], out var preferred);
if (!coreNames.Contains(preferred))
{
// invalid --> default (doing this here rather than when reading config file to allow for hacked-in values, though I'm not sure if that could do anything at the moment --yoshi)
var defaultCore = coreNames[0];
Console.WriteLine($"setting preferred core for {submenu.Text} to {defaultCore} (was {preferred ?? "null"})");
preferred = defaultCore;
- foreach (var sysID in appliesTo) Config.PreferredCores[sysID] = preferred;
+ foreach (var sysID in systemIds) Config.PreferredCores[sysID] = preferred;
}
foreach (ToolStripMenuItem entry in ((ToolStripMenuItem) openedSender).DropDownItems) entry.Checked = entry.Text == preferred;
};
diff --git a/src/BizHawk.Emulation.Cores/Calculators/Emu83/Emu83.cs b/src/BizHawk.Emulation.Cores/Calculators/Emu83/Emu83.cs
index b1c8153b489..4fd11bd03ce 100644
--- a/src/BizHawk.Emulation.Cores/Calculators/Emu83/Emu83.cs
+++ b/src/BizHawk.Emulation.Cores/Calculators/Emu83/Emu83.cs
@@ -26,7 +26,7 @@ static Emu83()
private readonly TI83Disassembler _disassembler = new();
- [CoreConstructor(VSystemID.Raw.TI83)]
+ [CoreConstructor(VSystemID.Raw.TI83, Priority = CorePriority.DefaultPreference)]
public Emu83(CoreLoadParameters lp)
{
try
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.cs
index 4704d1e42f0..61d8a866a23 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.cs
@@ -18,7 +18,7 @@ internal static class RomChecksums
public const string Tapper = "SHA1:E986E1818E747BEB9B33CE4DFF1CDC6B55BDB620";
}
- [CoreConstructor(VSystemID.Raw.A26)]
+ [CoreConstructor(VSystemID.Raw.A26, Priority = CorePriority.DefaultPreference)]
public Atari2600(GameInfo game, byte[] rom, Atari2600.A2600Settings settings, Atari2600.A2600SyncSettings syncSettings)
{
var ser = new BasicServiceProvider(this);
diff --git a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/HyperNyma.cs b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/HyperNyma.cs
index 3ba961c73b9..0ae18007d9c 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/HyperNyma.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/HyperNyma.cs
@@ -34,10 +34,10 @@ public static NymaSettingsInfo CachedSettingsInfo(CoreComm comm)
private readonly LibHyperNyma _hyperNyma;
private readonly bool _hasCds;
- [CoreConstructor(VSystemID.Raw.PCE, Priority = CorePriority.Low)]
- [CoreConstructor(VSystemID.Raw.SGX, Priority = CorePriority.Low)]
- [CoreConstructor(VSystemID.Raw.PCECD, Priority = CorePriority.Low)]
- [CoreConstructor(VSystemID.Raw.SGXCD, Priority = CorePriority.Low)]
+ [CoreConstructor(VSystemID.Raw.PCE)]
+ [CoreConstructor(VSystemID.Raw.SGX)]
+ [CoreConstructor(VSystemID.Raw.PCECD)]
+ [CoreConstructor(VSystemID.Raw.SGXCD)]
public HyperNyma(CoreLoadParameters lp)
: base(lp.Comm, VSystemID.Raw.PCE, "PC Engine Controller", lp.Settings, lp.SyncSettings)
{
diff --git a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TurboNyma.cs b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TurboNyma.cs
index ca7cb4b3fac..56b6d28f851 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TurboNyma.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TurboNyma.cs
@@ -36,10 +36,10 @@ public static NymaSettingsInfo CachedSettingsInfo(CoreComm comm)
private readonly LibTurboNyma _turboNyma;
private readonly bool _hasCds;
- [CoreConstructor(VSystemID.Raw.PCE)]
- [CoreConstructor(VSystemID.Raw.SGX)]
- [CoreConstructor(VSystemID.Raw.PCECD)]
- [CoreConstructor(VSystemID.Raw.SGXCD)]
+ [CoreConstructor(VSystemID.Raw.PCE, Priority = CorePriority.DefaultPreference)]
+ [CoreConstructor(VSystemID.Raw.SGX, Priority = CorePriority.DefaultPreference)]
+ [CoreConstructor(VSystemID.Raw.PCECD, Priority = CorePriority.DefaultPreference)]
+ [CoreConstructor(VSystemID.Raw.SGXCD, Priority = CorePriority.DefaultPreference)]
public TurboNyma(CoreLoadParameters lp)
: base(lp.Comm, VSystemID.Raw.PCE, "PC Engine Controller", lp.Settings, lp.SyncSettings)
{
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.cs
index 0109ea1adb8..cd18bf497dc 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.cs
@@ -17,8 +17,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
[ServiceNotApplicable(new[] { typeof(IDriveLight) })]
public partial class BsnesCore : IEmulator, IDebuggable, IVideoProvider, ISaveRam, IStatable, IInputPollable, IRegionable, ISettable, IBSNESForGfxDebugger, IBoardInfo
{
- [CoreConstructor(VSystemID.Raw.Satellaview)]
- [CoreConstructor(VSystemID.Raw.SGB)]
+ [CoreConstructor(VSystemID.Raw.Satellaview, Priority = CorePriority.DefaultPreference)]
+ [CoreConstructor(VSystemID.Raw.SGB, Priority = CorePriority.DefaultPreference)]
[CoreConstructor(VSystemID.Raw.SNES)]
public BsnesCore(CoreLoadParameters loadParameters) : this(loadParameters, false) { }
public BsnesCore(CoreLoadParameters loadParameters, bool subframe = false)
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs
index 2c841e8c224..8b2ffad6c6f 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs
@@ -19,8 +19,8 @@ public partial class Gameboy : IInputPollable, IRomInfo, IGameboyCommon, ICycleT
/// HACK disables BIOS requirement if the environment looks like a test runner...
private static readonly bool TestromsBIOSDisableHack = Type.GetType("Microsoft.VisualStudio.TestTools.UnitTesting.Assert, Microsoft.VisualStudio.TestPlatform.TestFramework") is not null;
- [CoreConstructor(VSystemID.Raw.GB)]
- [CoreConstructor(VSystemID.Raw.GBC)]
+ [CoreConstructor(VSystemID.Raw.GB, Priority = CorePriority.DefaultPreference)]
+ [CoreConstructor(VSystemID.Raw.GBC, Priority = CorePriority.DefaultPreference)]
[CoreConstructor(VSystemID.Raw.SGB)]
public Gameboy(CoreComm comm, IGameInfo game, byte[] file, GambatteSettings settings, GambatteSyncSettings syncSettings, bool deterministic)
{
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.cs
index e66dc3b3c89..ef72bdabd93 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.cs
@@ -10,7 +10,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
[ServiceNotApplicable(new[] { typeof(IDriveLight) })]
public partial class GambatteLink : ILinkable, ILinkedGameBoyCommon, IRomInfo
{
- [CoreConstructor(VSystemID.Raw.GBL)]
+ [CoreConstructor(VSystemID.Raw.GBL, Priority = CorePriority.DefaultPreference)]
public GambatteLink(CoreLoadParameters lp)
{
if (lp.Roms.Count < MIN_PLAYERS || lp.Roms.Count > MAX_PLAYERS)
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.cs
index db5a6dc4f2b..bcd4cffb8d9 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.cs
@@ -18,7 +18,7 @@ public partial class N64 : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPo
/// Rom that should be loaded
/// rom data with consistent endianness/order
/// N64SyncSettings object
- [CoreConstructor(VSystemID.Raw.N64)]
+ [CoreConstructor(VSystemID.Raw.N64, Priority = CorePriority.DefaultPreference)]
public N64(GameInfo game, byte[] file, byte[] rom, N64Settings settings, N64SyncSettings syncSettings)
{
if (OSTailoredCode.IsUnixHost) throw new NotImplementedException();
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs
index 9acad9ba4df..760fc154a0b 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs
@@ -28,7 +28,7 @@ static QuickNES()
QN = BizInvoker.GetInvoker(resolver, CallingConventionAdapters.Native);
}
- [CoreConstructor(VSystemID.Raw.NES)]
+ [CoreConstructor(VSystemID.Raw.NES, Priority = CorePriority.DefaultPreference)]
public QuickNES(byte[] file, QuickNESSettings settings, QuickNESSyncSettings syncSettings)
{
ServiceProvider = new BasicServiceProvider(this);
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9x.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9x.cs
index 6d637228cdd..4d26afc33f0 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9x.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9x.cs
@@ -14,7 +14,7 @@ public class Snes9x : WaterboxCore,
{
private readonly LibSnes9x _core;
- [CoreConstructor(VSystemID.Raw.SNES)]
+ [CoreConstructor(VSystemID.Raw.SNES, Priority = CorePriority.DefaultPreference)]
public Snes9x(CoreLoadParameters loadParameters)
:base(loadParameters.Comm, new Configuration
{
diff --git a/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs b/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs
index 8cc473e6c5a..dfaa3c81a86 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs
@@ -19,10 +19,10 @@ public sealed partial class PCEngine : IEmulator, ISaveRam, IInputPollable, IVid
int IVideoLogicalOffsets.ScreenY => Settings.TopLine;
- [CoreConstructor(VSystemID.Raw.PCE, Priority = CorePriority.Low)]
- [CoreConstructor(VSystemID.Raw.SGX, Priority = CorePriority.Low)]
- [CoreConstructor(VSystemID.Raw.PCECD, Priority = CorePriority.Low)]
- [CoreConstructor(VSystemID.Raw.SGXCD, Priority = CorePriority.Low)]
+ [CoreConstructor(VSystemID.Raw.PCE)]
+ [CoreConstructor(VSystemID.Raw.SGX)]
+ [CoreConstructor(VSystemID.Raw.PCECD)]
+ [CoreConstructor(VSystemID.Raw.SGXCD)]
public PCEngine(CoreLoadParameters lp)
{
if (lp.Discs.Count == 1 && lp.Roms.Count == 0)
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/PicoDrive/PicoDrive.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/PicoDrive/PicoDrive.cs
index 081e6ce71fa..ab1699902ac 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Sega/PicoDrive/PicoDrive.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/PicoDrive/PicoDrive.cs
@@ -18,7 +18,7 @@ public class PicoDrive : WaterboxCore, IDriveLight, IRegionable, ISettable lp)
{
LoadCallback = LoadArchive;
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Nymashock.cs b/src/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Nymashock.cs
index 5442f218ee1..90fb0758159 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Nymashock.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Nymashock.cs
@@ -57,7 +57,7 @@ public static NymaSettingsInfo CachedSettingsInfo(CoreComm comm)
return _cachedSettingsInfo;
}
- [CoreConstructor(VSystemID.Raw.PSX)]
+ [CoreConstructor(VSystemID.Raw.PSX, Priority = CorePriority.DefaultPreference)]
public Nymashock(CoreLoadParameters lp)
: base(lp.Comm, VSystemID.Raw.PSX, "PSX Front Panel", lp.Settings, lp.SyncSettings)
{
diff --git a/src/BizHawk.Emulation.Cores/CoreInventory.cs b/src/BizHawk.Emulation.Cores/CoreInventory.cs
index a6515a34184..65310ce974f 100644
--- a/src/BizHawk.Emulation.Cores/CoreInventory.cs
+++ b/src/BizHawk.Emulation.Cores/CoreInventory.cs
@@ -3,6 +3,7 @@
using System.Reflection;
using System.Runtime.ExceptionServices;
+using BizHawk.Common;
using BizHawk.Common.CollectionExtensions;
using BizHawk.Emulation.Common;
@@ -14,10 +15,14 @@ namespace BizHawk.Emulation.Cores
public class CoreInventory
{
private readonly Dictionary> _systems = new Dictionary>();
+ private readonly List<(List, List)> _systemGroups = [ ];
/// keys are system IDs; values are core/ctor info for all that system's cores
public IReadOnlyDictionary> AllCores => _systems;
+ // list of system ids groups; each system id in the group shares the same core choices
+ public IReadOnlyList<(List SystemIds, List CoreNames)> SystemGroups => _systemGroups;
+
public readonly IReadOnlyCollection SystemsFlat;
public class Core
@@ -157,7 +162,7 @@ public IEnumerable GetCores(string system)
///
/// create a core inventory, collecting all IEmulators from some assemblies
///
- public CoreInventory(IEnumerable> assys)
+ public CoreInventory(IEnumerable> assemblies)
{
var systemsFlat = new Dictionary();
void ProcessConstructor(Type type, CoreConstructorAttribute consAttr, CoreAttribute coreAttr, ConstructorInfo cons)
@@ -166,27 +171,41 @@ void ProcessConstructor(Type type, CoreConstructorAttribute consAttr, CoreAttrib
_systems.GetValueOrPutNew(consAttr.System).Add(core);
systemsFlat[type] = core;
}
- foreach (var assy in assys)
+ foreach (var type in assemblies.SelectMany(assembly => assembly).OrderBy(type => type.AssemblyQualifiedName))
{
- foreach (var typ in assy)
+ if (!type.IsAbstract && type.GetInterfaces().Contains(typeof(IEmulator)))
{
- if (!typ.IsAbstract && typ.GetInterfaces().Contains(typeof(IEmulator)))
+ var coreAttr = type.GetCustomAttributes(typeof(CoreAttribute), false);
+ if (coreAttr.Length != 1)
+ throw new InvalidOperationException($"{nameof(IEmulator)} {type} without {nameof(CoreAttribute)}s!");
+ var cons = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance)
+ .Where(c => c.GetCustomAttributes(typeof(CoreConstructorAttribute), false).Length > 0);
+ foreach(var con in cons)
{
- var coreAttr = typ.GetCustomAttributes(typeof(CoreAttribute), false);
- if (coreAttr.Length != 1)
- throw new InvalidOperationException($"{nameof(IEmulator)} {typ} without {nameof(CoreAttribute)}s!");
- var cons = typ.GetConstructors(BindingFlags.Public | BindingFlags.Instance)
- .Where(c => c.GetCustomAttributes(typeof(CoreConstructorAttribute), false).Length > 0);
- foreach(var con in cons)
+ foreach (var consAttr in con.GetCustomAttributes(typeof(CoreConstructorAttribute), false).Cast())
{
- foreach (var consAttr in con.GetCustomAttributes(typeof(CoreConstructorAttribute), false).Cast())
- {
- ProcessConstructor(typ, consAttr, (CoreAttribute)coreAttr[0], con);
- }
+ ProcessConstructor(type, consAttr, (CoreAttribute)coreAttr[0], con);
}
}
}
}
+ foreach (var (systemId, cores) in _systems)
+ {
+ var coreNames = cores.Select(core => core.Name).ToList();
+ bool found = false;
+ foreach (var (systemIds, existingCores) in _systemGroups)
+ {
+ if (existingCores.SequenceEqual(coreNames))
+ {
+ systemIds.Add(systemId);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ _systemGroups.Add(([ systemId ], coreNames));
+ }
SystemsFlat = systemsFlat.Values;
}
@@ -205,9 +224,9 @@ public enum CorePriority
UserPreference = -200,
///
- /// A very good core that should be preferred over normal cores. Don't use this?
+ /// The default core for a system when no other preferences exist. Must be set once per system
///
- High = -100,
+ DefaultPreference = -100,
///
/// Most cores should use this
diff --git a/src/BizHawk.Tests/Client.Common/config/CorePickerStabilityTests.cs b/src/BizHawk.Tests/Client.Common/config/CorePickerStabilityTests.cs
index a5f4903ebd0..a3b51032be5 100644
--- a/src/BizHawk.Tests/Client.Common/config/CorePickerStabilityTests.cs
+++ b/src/BizHawk.Tests/Client.Common/config/CorePickerStabilityTests.cs
@@ -13,22 +13,24 @@ public sealed class CorePickerStabilityTests
private static readonly IReadOnlyDictionary DefaultCorePrefDict = new Config().PreferredCores;
[TestMethod]
- public void AssertAllChoicesInMenu()
+ public void AssertAllPrefencesExist()
{
var multiCoreSystems = CoreInventory.Instance.AllCores.Where(kvp => kvp.Value.Count != 1)
- .Select(kvp => kvp.Key)
- .ToHashSet();
- foreach (var sysID in DefaultCorePrefDict.Keys)
+ .Select(kvp => kvp.Key);
+ foreach (var sysID in multiCoreSystems)
{
- Assert.IsTrue(multiCoreSystems.Contains(sysID), $"a default core preference exists for {sysID} but that system doesn't have alternate cores");
+ Assert.IsTrue(DefaultCorePrefDict.ContainsKey(sysID), $"{sysID} has multiple cores, but no default core preference exists for it!");
}
- foreach (var (appliesTo, _) in Config.CorePickerUIData)
+ }
+
+ [TestMethod]
+ public void AssertNoExtraPreferences()
+ {
+ var singleCoreSystems = CoreInventory.Instance.AllCores.Where(kvp => kvp.Value.Count == 1)
+ .Select(kvp => kvp.Key);
+ foreach (var sysID in singleCoreSystems)
{
- Assert.IsTrue(
- appliesTo.Any(multiCoreSystems.Contains),
- appliesTo.Length is 1
- ? $"core picker has submenu for {appliesTo[0]}, but that system doesn't have alternate cores"
- : $"core picker has submenu for {appliesTo[0]} ({string.Join("/", appliesTo)}), but none of those systems have alternate cores");
+ Assert.IsFalse(DefaultCorePrefDict.ContainsKey(sysID), $"{sysID} only has one core implementing it, but an unnecessary preference choice exists for it!");
}
}
@@ -40,26 +42,33 @@ public void AssertNoMissingCores()
{
Assert.IsTrue(allCoreNames.Contains(coreName), $"default core preference for {sysID} is \"{coreName}\", which doesn't exist");
}
- foreach (var (appliesTo, coreNames) in Config.CorePickerUIData) foreach (var coreName in coreNames)
- {
- Assert.IsTrue(allCoreNames.Contains(coreName), $"core picker includes nonexistant core \"{coreName}\" under {appliesTo[0]} group");
- }
}
- /// this really shouldn't be necessary
[TestMethod]
- public void AssertNoMissingSystems()
+ public void AssertExactlyOnePreferredCore()
{
- var allSysIDs = CoreInventory.Instance.AllCores.Keys.ToHashSet();
-#if false // already covered by AssertAllChoicesInMenu
- foreach (var sysID in DefaultCorePrefDict.Keys)
+ foreach(var (systemId, cores) in CoreInventory.Instance.AllCores)
{
- Assert.IsTrue(allSysIDs.Contains(sysID), $"a default core preference exists for {sysID}, which isn't emulated by any core");
+ if (cores.Count >= 2)
+ {
+ int preferredCoresCount = cores.Count(core => core.Priority == CorePriority.DefaultPreference);
+ Assert.IsTrue(preferredCoresCount == 1, $"{systemId} has {preferredCoresCount} preferred cores, expected exactly 1.");
+ }
}
-#endif
- foreach (var (appliesTo, _) in Config.CorePickerUIData) foreach (var sysID in appliesTo)
+ }
+
+ [TestMethod]
+ public void AssertNoConflictingPreferenceInGroup()
+ {
+ foreach(var (systemIds, cores) in CoreInventory.Instance.SystemGroups.Where(tuple => tuple.CoreNames.Count > 1))
{
- Assert.IsTrue(allSysIDs.Contains(sysID), $"core picker has choices for {sysID}, which isn't emulated by any core");
+ var preferredCoreForGroup = DefaultCorePrefDict[systemIds[0]];
+ foreach (var systemId in systemIds)
+ {
+ var preferredCore = DefaultCorePrefDict[systemId];
+
+ Assert.AreEqual(preferredCoreForGroup, preferredCore, $"Default core preference for {systemId} does not match the preferred core for the whole group ({string.Join(" | ", systemIds)})");
+ }
}
}
}