Add and use FirmwareID struct

decided not to propogate to cores yet, left TODOs in Emulation.Common.Extensions
This commit is contained in:
YoshiRulz 2021-02-12 16:10:39 +10:00
parent 35f317a887
commit 10ed0872a4
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
13 changed files with 93 additions and 72 deletions

View File

@ -36,33 +36,29 @@ namespace BizHawk.Client.Common
public string GetRetroSystemPath(IGameInfo game)
=> _pathEntries.RetroSystemAbsolutePath(game);
private void FirmwareWarn(string sysID, string firmwareID, bool required, string msg = null)
private void FirmwareWarn(FirmwareID id, bool required, string msg = null)
{
if (required)
{
var fullMsg = $"Couldn't find required firmware \"{sysID}:{firmwareID}\". This is fatal{(msg != null ? $": {msg}" : ".")}";
var fullMsg = $"Couldn't find required firmware \"{id.System}:{id.Firmware}\". This is fatal{(msg != null ? $": {msg}" : ".")}";
throw new MissingFirmwareException(fullMsg);
}
if (msg != null)
{
var fullMsg = $"Couldn't find firmware \"{sysID}:{firmwareID}\". Will attempt to continue: {msg}";
var fullMsg = $"Couldn't find firmware \"{id.System}:{id.Firmware}\". Will attempt to continue: {msg}";
_showWarning(fullMsg);
}
}
private byte[] GetFirmwareWithPath(string sysId, string firmwareId, bool required, string msg, out string path)
private byte[] GetFirmwareWithPath(FirmwareID id, bool required, string msg, out string path)
{
var firmwarePath = _firmwareManager.Request(
_pathEntries,
_firmwareUserSpecifications,
sysId,
firmwareId);
var firmwarePath = _firmwareManager.Request(_pathEntries, _firmwareUserSpecifications, id);
if (firmwarePath == null || !File.Exists(firmwarePath))
{
path = null;
FirmwareWarn(sysId, firmwareId, required, msg);
FirmwareWarn(id, required, msg);
return null;
}
@ -75,19 +71,19 @@ namespace BizHawk.Client.Common
catch (IOException)
{
path = null;
FirmwareWarn(sysId, firmwareId, required, msg);
FirmwareWarn(id, required, msg);
return null;
}
}
/// <exception cref="MissingFirmwareException">not found and <paramref name="required"/> is true</exception>
public byte[] GetFirmware(string sysId, string firmwareId, bool required, string msg = null)
=> GetFirmwareWithPath(sysId, firmwareId, required, msg, out _);
public byte[] GetFirmware(FirmwareID id, bool required, string msg = null)
=> GetFirmwareWithPath(id, required, msg, out _);
/// <exception cref="MissingFirmwareException">not found and <paramref name="required"/> is true</exception>
public byte[] GetFirmwareWithGameInfo(string sysId, string firmwareId, bool required, out GameInfo gi, string msg = null)
public byte[] GetFirmwareWithGameInfo(FirmwareID id, bool required, out GameInfo gi, string msg = null)
{
byte[] ret = GetFirmwareWithPath(sysId, firmwareId, required, msg, out var path);
var ret = GetFirmwareWithPath(id, required, msg, out var path);
gi = ret != null && path != null
? Database.GetGameInfo(ret, path)
: null;

View File

@ -1,13 +1,13 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public sealed class FirmwareEventArgs
{
public string FirmwareId { get; set; }
public string Hash { get; set; }
public long Size { get; set; }
public FirmwareID ID { get; set; }
public string SystemId { get; set; }
public long Size { get; set; }
}
}

View File

@ -11,6 +11,8 @@ namespace BizHawk.Client.Common
{
public class FirmwareManager
{
private static readonly FirmwareID NDS_FIRMWARE = new("NDS", "firmware");
public List<FirmwareEventArgs> RecentlyServed { get; } = new List<FirmwareEventArgs>();
private readonly Dictionary<FirmwareRecord, ResolutionInfo> _resolutionDictionary = new();
@ -30,12 +32,12 @@ namespace BizHawk.Client.Common
}
// Requests the specified firmware. tries really hard to scan and resolve as necessary
public string Request(PathEntryCollection pathEntries, IDictionary<string, string> userSpecifications, string sysId, string firmwareId)
public string Request(PathEntryCollection pathEntries, IDictionary<string, string> userSpecifications, FirmwareID id)
{
var resolved = Resolve(
pathEntries,
userSpecifications,
FirmwareDatabase.FirmwareRecords.FirstOrDefault(fr => fr.FirmwareId == firmwareId && fr.SystemId == sysId));
FirmwareDatabase.FirmwareRecords.FirstOrDefault(fr => fr.ID == id));
if (resolved == null)
{
return null;
@ -43,8 +45,7 @@ namespace BizHawk.Client.Common
RecentlyServed.Add(new FirmwareEventArgs
{
SystemId = sysId,
FirmwareId = firmwareId,
ID = id,
Hash = resolved.Hash,
Size = resolved.Size
});
@ -150,11 +151,8 @@ namespace BizHawk.Client.Common
_resolutionDictionary.Remove(fr);
// get all options for this firmware (in order)
var fr1 = fr;
var options = FirmwareDatabase.FirmwareOptions
.Where(fo => fo.SystemId == fr1.SystemId
&& fo.FirmwareId == fr1.FirmwareId
&& fo.IsAcceptableOrIdeal);
var id = fr.ID;
var options = FirmwareDatabase.FirmwareOptions.Where(fo => fo.ID == id && fo.IsAcceptableOrIdeal);
// try each option
foreach (var fo in options)
@ -182,7 +180,7 @@ namespace BizHawk.Client.Common
foreach (var fr in FirmwareDatabase.FirmwareRecords)
{
// do we have a user specification for this firmware record?
if (userSpecifications.TryGetValue(fr.ConfigKey, out var userSpec))
if (userSpecifications.TryGetValue(fr.ID.ConfigKey, out var userSpec))
{
// flag it as user specified
if (!_resolutionDictionary.TryGetValue(fr, out ResolutionInfo ri))
@ -205,12 +203,10 @@ namespace BizHawk.Client.Common
}
// compute its hash
RealFirmwareFile rff;
// NDS's firmware file contains user settings; these are over-written by sync settings, so we shouldn't allow them to impact the hash
if (fr.SystemId == "NDS" && fr.FirmwareId == "firmware")
rff = reader.Read(new FileInfo(Emulation.Cores.Consoles.Nintendo.NDS.MelonDS.CreateModifiedFirmware(userSpec)));
else
rff = reader.Read(fi);
var rff = reader.Read(fr.ID == NDS_FIRMWARE
? new FileInfo(Emulation.Cores.Consoles.Nintendo.NDS.MelonDS.CreateModifiedFirmware(userSpec))
: fi);
ri.Size = fi.Length;
ri.Hash = rff.Hash;
@ -220,7 +216,7 @@ namespace BizHawk.Client.Common
ri.KnownFirmwareFile = ff;
// if the known firmware file is for a different firmware, flag it so we can show a warning
if (FirmwareDatabase.FirmwareOptions.Any(fo => fo.Hash == rff.Hash && fo.ConfigKey != fr.ConfigKey))
if (FirmwareDatabase.FirmwareOptions.Any(fo => fo.Hash == rff.Hash && fo.ID != fr.ID))
{
ri.KnownMismatching = true;
}

View File

@ -239,7 +239,7 @@ namespace BizHawk.Client.Common
{
foreach (var firmware in firmwareManager.RecentlyServed)
{
var key = $"{firmware.SystemId}_Firmware_{firmware.FirmwareId}";
var key = $"{firmware.ID.System}_Firmware_{firmware.ID.Firmware}";
if (!movie.HeaderEntries.ContainsKey(key))
{

View File

@ -3854,7 +3854,7 @@ namespace BizHawk.Client.EmuHawk
Console.WriteLine("Active Firmwares:");
foreach (var f in FirmwareManager.RecentlyServed)
{
Console.WriteLine(" {0} : {1}", f.FirmwareId, f.Hash);
Console.WriteLine($" {f.ID} : {f.Hash}");
}
}

View File

@ -177,8 +177,8 @@ namespace BizHawk.Client.EmuHawk
ImageIndex = IdUnsure,
ToolTipText = null
};
lvi.SubItems.Add(fr.SystemId);
lvi.SubItems.Add(fr.FirmwareId);
lvi.SubItems.Add(fr.ID.System);
lvi.SubItems.Add(fr.ID.Firmware);
lvi.SubItems.Add(fr.Descr);
lvi.SubItems.Add(""); // resolved with
lvi.SubItems.Add(""); // location
@ -189,15 +189,15 @@ namespace BizHawk.Client.EmuHawk
lvFirmwares.Items.Add(lvi);
// build the groups in the ListView as we go:
if (!groups.ContainsKey(fr.SystemId))
if (!groups.ContainsKey(fr.ID.System))
{
if (!SystemGroupNames.TryGetValue(fr.SystemId, out var name))
if (!SystemGroupNames.TryGetValue(fr.ID.System, out var name))
name = "FIX ME (FirmwaresConfig.cs)";
lvFirmwares.Groups.Add(fr.SystemId, name);
lvFirmwares.Groups.Add(fr.ID.System, name);
var lvg = lvFirmwares.Groups[lvFirmwares.Groups.Count - 1];
groups[fr.SystemId] = lvg;
groups[fr.ID.System] = lvg;
}
lvi.Group = groups[fr.SystemId];
lvi.Group = groups[fr.ID.System];
}
// now that we have some items in the ListView, we can size some columns to sensible widths
@ -496,7 +496,7 @@ namespace BizHawk.Client.EmuHawk
}
}
_firmwareUserSpecifications[fr.ConfigKey] = filePath;
_firmwareUserSpecifications[fr.ID.ConfigKey] = filePath;
}
}
catch (Exception ex)
@ -515,7 +515,7 @@ namespace BizHawk.Client.EmuHawk
foreach (ListViewItem lvi in lvFirmwares.SelectedItems)
{
var fr = (FirmwareRecord) lvi.Tag;
_firmwareUserSpecifications.Remove(fr.ConfigKey);
_firmwareUserSpecifications.Remove(fr.ID.ConfigKey);
}
DoScan();
@ -527,16 +527,13 @@ namespace BizHawk.Client.EmuHawk
var fr = (FirmwareRecord) lvi.Tag;
// get all options for this firmware (in order)
var options =
from fo in FirmwareDatabase.FirmwareOptions
where fo.SystemId == fr.SystemId && fo.FirmwareId == fr.FirmwareId
select fo;
var options = FirmwareDatabase.FirmwareOptions.Where(fo => fo.ID == fr.ID);
var fciDialog = new FirmwaresConfigInfo
{
lblFirmware =
{
Text = $"{fr.SystemId} : {fr.FirmwareId} ({fr.Descr})"
Text = $"{fr.ID.System} : {fr.ID.Firmware} ({fr.Descr})"
}
};

View File

@ -41,8 +41,7 @@ namespace BizHawk.Emulation.Common
void Option(string systemId, string id, FirmwareFile ff, FirmwareOptionStatus status = FirmwareOptionStatus.Acceptable)
=> options.Add(new FirmwareOption
{
SystemId = systemId,
FirmwareId = id,
ID = new(systemId, id),
Hash = ff.Hash,
Status = ff.Bad ? FirmwareOptionStatus.Bad : status,
Size = ff.Size
@ -51,8 +50,7 @@ namespace BizHawk.Emulation.Common
void Firmware(string systemId, string id, string desc)
=> records.Add(new FirmwareRecord
{
SystemId = systemId,
FirmwareId = id,
ID = new(systemId, id),
Descr = desc
});

View File

@ -0,0 +1,30 @@
#nullable enable
namespace BizHawk.Emulation.Common
{
public readonly struct FirmwareID
{
public static bool operator ==(FirmwareID a, FirmwareID b) => a.Firmware == b.Firmware && a.System == b.System;
public static bool operator !=(FirmwareID a, FirmwareID b) => a.Firmware != b.Firmware || a.System != b.System;
public string ConfigKey => $"{System}+{Firmware}";
public readonly string Firmware;
public readonly string System;
public FirmwareID(string system, string firmware)
{
System = system;
Firmware = firmware;
}
public override bool Equals(object obj) => obj is FirmwareID other
&& other.Firmware == Firmware && other.System == System;
public override int GetHashCode() => (System, Firmware).GetHashCode();
public override string ToString() => ConfigKey;
}
}

View File

@ -2,18 +2,14 @@ namespace BizHawk.Emulation.Common
{
public sealed class FirmwareOption
{
public string ConfigKey => $"{SystemId}+{FirmwareId}";
public string FirmwareId { get; set; }
public string Hash { get; set; }
public FirmwareID ID { get; set; }
public bool IsAcceptableOrIdeal => Status == FirmwareOptionStatus.Ideal || Status == FirmwareOptionStatus.Acceptable;
public long Size { get; set; }
public FirmwareOptionStatus Status { get; set; }
public string SystemId { get; set; }
}
}

View File

@ -2,12 +2,8 @@ namespace BizHawk.Emulation.Common
{
public sealed class FirmwareRecord
{
public string ConfigKey => $"{SystemId}+{FirmwareId}";
public string Descr { get; set; }
public string FirmwareId { get; set; }
public string SystemId { get; set; }
public FirmwareID ID { get; set; }
}
}

View File

@ -446,5 +446,18 @@ namespace BizHawk.Emulation.Common
.AddAxis(string.Format(nameFormat, "Z"), rangeAll, neutralAll);
public static AxisSpec With(this in AxisSpec spec, Range<int> range, int neutral) => new AxisSpec(range, neutral, spec.IsReversed, spec.Constraint);
/// <summary>Get a firmware as a byte array</summary>
/// <param name="sysId">the core systemID</param>
/// <param name="firmwareId">the firmware id</param>
/// <param name="required">if true, result is guaranteed to be valid; else null is possible if not found</param>
/// <param name="msg">message to show if fail to get</param>
/// <remarks>TODO inline (only change is wrapping strings in <see cref="FirmwareID"/> ctor, these IDs should probably be consts in each core's class)</remarks>
public static byte[] GetFirmware(this ICoreFileProvider cfp, string sysId, string firmwareId, bool required, string msg = null)
=> cfp.GetFirmware(new(system: sysId, firmware: firmwareId), required: required, msg: msg);
/// <remarks>TODO inline (only change is wrapping strings in <see cref="FirmwareID"/> ctor, these IDs should probably be consts in each core's class)</remarks>
public static byte[] GetFirmwareWithGameInfo(this ICoreFileProvider cfp, string sysId, string firmwareId, bool required, out GameInfo gi, string msg = null)
=> cfp.GetFirmwareWithGameInfo(new(system: sysId, firmware: firmwareId), required: required, out gi, msg: msg);
}
}
}

View File

@ -23,12 +23,11 @@
/// <summary>
/// Get a firmware as a byte array
/// </summary>
/// <param name="sysId">the core systemID</param>
/// <param name="firmwareId">the firmware id</param>
/// <param name="id">the firmware id</param>
/// <param name="required">if true, result is guaranteed to be valid; else null is possible if not found</param>
/// <param name="msg">message to show if fail to get</param>
byte[] GetFirmware(string sysId, string firmwareId, bool required, string msg = null);
byte[] GetFirmware(FirmwareID id, bool required, string msg = null);
byte[] GetFirmwareWithGameInfo(string sysId, string firmwareId, bool required, out GameInfo gi, string msg = null);
byte[] GetFirmwareWithGameInfo(FirmwareID id, bool required, out GameInfo gi, string msg = null);
}
}

View File

@ -13,7 +13,7 @@ namespace BizHawk.Tests.Emulation.Common
[TestMethod]
public void CheckFilesInOptions()
{
foreach (var fo in FirmwareDatabase.FirmwareOptions) Assert.IsTrue(FirmwareDatabase.FirmwareFilesByHash.ContainsKey(fo.Hash), $"option {fo.ConfigKey} references unknown file {fo.Hash}");
foreach (var fo in FirmwareDatabase.FirmwareOptions) Assert.IsTrue(FirmwareDatabase.FirmwareFilesByHash.ContainsKey(fo.Hash), $"option {fo.ID} references unknown file {fo.Hash}");
}
[TestMethod]