diff --git a/src/BizHawk.Emulation.Common/Database/Database.cs b/src/BizHawk.Emulation.Common/Database/Database.cs index a4ef8a09ec..11e6f686a1 100644 --- a/src/BizHawk.Emulation.Common/Database/Database.cs +++ b/src/BizHawk.Emulation.Common/Database/Database.cs @@ -94,6 +94,54 @@ namespace BizHawk.Emulation.Common private static bool initialized = false; + public static CompactGameInfo ParseCGIRecord(string line) + { + const char FIELD_SEPARATOR = '\t'; + var items = line.Split(FIELD_SEPARATOR); + var field = 0; + var hashDigest = FormatHash(items[field++]); + var dumpStatus = items[field++].Trim() switch + { + "B" => RomStatus.BadDump, // see /Assets/gamedb/gamedb.txt + "V" => RomStatus.BadDump, // see /Assets/gamedb/gamedb.txt + "T" => RomStatus.TranslatedRom, + "O" => RomStatus.Overdump, + "I" => RomStatus.Bios, + "D" => RomStatus.Homebrew, + "H" => RomStatus.Hack, + "U" => RomStatus.Unknown, + _ => RomStatus.GoodDump + }; + var knownName = items[field++]; + var sysID = items[field++]; + string/*?*/ metadata = null; + string region = string.Empty; + string forcedCore = string.Empty; + if (field < items.Length) + { + _ = items[field++]; // rarely populated; possibly genre or just a remark + if (field < items.Length) + { + metadata = items[field++]; + if (field < items.Length) + { + region = items[field++]; + if (field < items.Length) forcedCore = items[field++]; + } + } + } + return new() + { + Hash = hashDigest, + Status = dumpStatus, + Name = knownName, + System = sysID, + MetaData = metadata, + Region = region, + ForcedCore = forcedCore, + }; + } + private static void InitializeWork(string path, bool inUser, bool silent) { if (!inUser) _expected.Remove(Path.GetFileName(path)); @@ -115,31 +163,7 @@ namespace BizHawk.Emulation.Common { continue; } - - var items = line.Split('\t'); - - var game = new CompactGameInfo - { - Hash = FormatHash(items[0]), - Status = items[1].Trim() - switch - { - "B" => RomStatus.BadDump, // see /Assets/gamedb/gamedb.txt - "V" => RomStatus.BadDump, // see /Assets/gamedb/gamedb.txt - "T" => RomStatus.TranslatedRom, - "O" => RomStatus.Overdump, - "I" => RomStatus.Bios, - "D" => RomStatus.Homebrew, - "H" => RomStatus.Hack, - "U" => RomStatus.Unknown, - _ => RomStatus.GoodDump - }, - Name = items[2], - System = items[3], - MetaData = items.Length >= 6 ? items[5] : null, - Region = items.Length >= 7 ? items[6] : "", - ForcedCore = items.Length >= 8 ? items[7].ToLowerInvariant() : "" - }; + var game = ParseCGIRecord(line); if (game.Hash is SHA1Checksum.EmptyFile or MD5Checksum.EmptyFile) { Console.WriteLine($"WARNING: gamedb {path} contains entry for empty rom as \"{game.Name}\"!"); diff --git a/src/BizHawk.Tests/Emulation.Common/Database/GameDBTSVParserTests.cs b/src/BizHawk.Tests/Emulation.Common/Database/GameDBTSVParserTests.cs new file mode 100644 index 0000000000..3e4caea4ff --- /dev/null +++ b/src/BizHawk.Tests/Emulation.Common/Database/GameDBTSVParserTests.cs @@ -0,0 +1,47 @@ +using BizHawk.Emulation.Common; + +namespace BizHawk.Tests.Emulation.Common +{ + [TestClass] + public sealed class GameDBTSVParserTests + { + [DataRow( + "sha1:f4885610503bff2c4ca816f4f28d1fe517b92f35\t\t2 Pak Special Yellow - Star Warrior,Frogger (1990) (HES) (PAL) [!]\tA26\t\tm=F6;PAL=true", + "F4885610503BFF2C4CA816F4F28D1FE517B92F35", + RomStatus.GoodDump, + "2 Pak Special Yellow - Star Warrior,Frogger (1990) (HES) (PAL) [!]", + "A26", + "m=F6;PAL=true", + ""/*empty string*/, + ""/*empty string*/)] + [DataRow( + "3064E664D34859649B67559F0ED0C2FFD6948031\tB\tActRaiser 2 (U) [b1]\tSNES", + "3064E664D34859649B67559F0ED0C2FFD6948031", + RomStatus.BadDump, + "ActRaiser 2 (U) [b1]", + "SNES", + null, + ""/*empty string*/, + ""/*empty string*/)] + [TestMethod] + public void Check( + string line, + string hashDigest, + RomStatus dumpStatus, + string knownName, + string sysID, + string? metadata, + string region, + string forcedCore) + { + var gi = Database.ParseCGIRecord(line); + Assert.AreEqual(expected: hashDigest, actual: gi.Hash, message: nameof(CompactGameInfo.Hash)); + Assert.AreEqual(expected: dumpStatus, actual: gi.Status, message: nameof(CompactGameInfo.Status)); + Assert.AreEqual(expected: knownName, actual: gi.Name, message: nameof(CompactGameInfo.Name)); + Assert.AreEqual(expected: sysID, actual: gi.System, message: nameof(CompactGameInfo.System)); + Assert.AreEqual(expected: metadata, actual: gi.MetaData, message: nameof(CompactGameInfo.MetaData)); + Assert.AreEqual(expected: region, actual: gi.Region, message: nameof(CompactGameInfo.Region)); + Assert.AreEqual(expected: forcedCore, actual: gi.ForcedCore, message: nameof(CompactGameInfo.ForcedCore)); + } + } +}