From 05c86e4c24e81d63bf2929b6369e0f952edb4593 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Thu, 23 Jul 2026 11:36:54 -0400 Subject: [PATCH] Add support for BLAKE3 --- .../MetadataExtensionsTests.cs | 195 ++++++++++++++++++ .../MetadataExtensions.cs | 32 ++- SabreTools.Data.Models/ClrMamePro/Rom.cs | 3 + SabreTools.Data.Models/Hashfile/BLAKE3.cs | 15 ++ SabreTools.Data.Models/Hashfile/Hashfile.cs | 2 + SabreTools.Data.Models/Logiqx/Rom.cs | 4 + SabreTools.Data.Models/Metadata/Rom.cs | 3 + .../DatFileTests.FromMetadata.cs | 2 + .../DatFileTests.ToMetadata.cs | 1 + .../DatFileTests.cs | 28 ++- .../DatStatisticsTests.cs | 8 + .../FormatsTests.cs | 25 +++ SabreTools.Metadata.DatFiles/DatFile.cs | 16 +- SabreTools.Metadata.DatFiles/DatStatistics.cs | 12 ++ SabreTools.Metadata.DatFiles/Enums.cs | 5 + .../Formats/Blake3File.cs | 56 +++++ .../Formats/ClrMamePro.cs | 3 +- .../Formats/Logiqx.cs | 3 +- SabreTools.Metadata.DatFiles/ItemDatabase.cs | 1 + .../ItemDictionary.cs | 1 + .../DatItemTests.cs | 11 +- .../Formats/DiskTests.cs | 4 + .../Formats/FileTests.cs | 4 + .../Formats/MediaTests.cs | 4 + .../Formats/RomTests.cs | 149 ++++++++++--- SabreTools.Metadata.DatItems/DatItem.cs | 8 + SabreTools.Metadata.DatItems/Enums.cs | 1 + SabreTools.Metadata.DatItems/Formats/Rom.cs | 10 + .../FilterObjectTests.cs | 2 + SabreTools.Metadata.Filter/Constants.cs | 1 + SabreTools.Metadata.Filter/FilterObject.cs | 3 + .../ClrMameProTests.cs | 2 + .../HashfileTests.cs | 35 ++++ .../LogiqxTests.cs | 2 + .../ClrMamePro.Deserializer.cs | 1 + .../ClrMamePro.Serializer.cs | 1 + .../Hashfile.Deserializer.cs | 26 +++ .../Hashfile.Serializer.cs | 17 +- .../Logiqx.Deserializer.cs | 1 + .../Logiqx.Serializer.cs | 1 + .../ClrMameProTests.cs | 2 + .../HashfileTests.cs | 36 ++++ .../LogiqxTests.cs | 2 + .../ClrMamePro.cs | 3 + SabreTools.Serialization.Readers/Hashfile.cs | 51 +++++ SabreTools.Serialization.Readers/Logiqx.cs | 1 + .../ClrMamePro.cs | 1 + SabreTools.Serialization.Writers/Hashfile.cs | 29 +++ SabreTools.Serialization.Writers/Logiqx.cs | 1 + 49 files changed, 779 insertions(+), 45 deletions(-) create mode 100644 SabreTools.Data.Models/Hashfile/BLAKE3.cs create mode 100644 SabreTools.Metadata.DatFiles/Formats/Blake3File.cs diff --git a/SabreTools.Data.Extensions.Test/MetadataExtensionsTests.cs b/SabreTools.Data.Extensions.Test/MetadataExtensionsTests.cs index 37aeb821..1c0eae18 100644 --- a/SabreTools.Data.Extensions.Test/MetadataExtensionsTests.cs +++ b/SabreTools.Data.Extensions.Test/MetadataExtensionsTests.cs @@ -361,6 +361,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; var other = new Rom { @@ -380,6 +381,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.PartialEquals(other); @@ -406,6 +408,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = string.Empty, SpamSum = "spamsum", + BLAKE3 = string.Empty, }; var other = new Rom { @@ -424,6 +427,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = "sha512", SpamSum = string.Empty, + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -449,6 +453,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; var other = new Rom { @@ -467,6 +472,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -493,6 +499,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; var other = new Rom { @@ -510,6 +517,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -536,6 +544,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; var other = new Rom { @@ -554,6 +563,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -580,6 +590,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; var other = new Rom { @@ -598,6 +609,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -624,6 +636,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; var other = new Rom { @@ -642,6 +655,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -668,6 +682,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; var other = new Rom { @@ -686,6 +701,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -712,6 +728,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; var other = new Rom { @@ -730,6 +747,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -756,6 +774,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; var other = new Rom { @@ -774,6 +793,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -800,6 +820,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; var other = new Rom { @@ -818,6 +839,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -844,6 +866,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; var other = new Rom { @@ -862,6 +885,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -888,6 +912,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; var other = new Rom { @@ -906,6 +931,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -932,6 +958,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; var other = new Rom { @@ -950,6 +977,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -976,6 +1004,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; var other = new Rom { @@ -994,6 +1023,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -1020,6 +1050,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = "sha512", SpamSum = string.Empty, + BLAKE3 = string.Empty, }; var other = new Rom { @@ -1038,6 +1069,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -1064,6 +1096,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = "spamsum", + BLAKE3 = string.Empty, }; var other = new Rom { @@ -1082,6 +1115,53 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", + }; + + bool actual = self.PartialEquals(other); + Assert.True(actual); + } + + [Fact] + public void PartialEquals_Rom_PartialBLAKE3_True() + { + var self = new Rom + { + Name = "name1", + Size = 12345, + CRC16 = string.Empty, + CRC32 = string.Empty, + CRC64 = string.Empty, + MD2 = string.Empty, + MD4 = string.Empty, + MD5 = string.Empty, + RIPEMD128 = string.Empty, + RIPEMD160 = string.Empty, + SHA1 = string.Empty, + SHA256 = string.Empty, + SHA384 = string.Empty, + SHA512 = string.Empty, + SpamSum = string.Empty, + BLAKE3 = "blake3", + }; + var other = new Rom + { + Name = "name2", + Size = 12345, + CRC16 = "crc16", + CRC32 = "crc32", + CRC64 = "crc64", + MD2 = "md2", + MD4 = "md4", + MD5 = "md5", + RIPEMD128 = "ripemd128", + RIPEMD160 = "ripemd160", + SHA1 = "sha1", + SHA256 = "sha256", + SHA384 = "sha384", + SHA512 = "sha512", + SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -1108,6 +1188,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; var other = new Rom { @@ -1126,6 +1207,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.PartialEquals(other); @@ -1392,6 +1474,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = string.Empty, SpamSum = "spamsum", + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1408,6 +1491,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = "sha512", SpamSum = string.Empty, + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1432,6 +1516,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1448,6 +1533,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1472,6 +1558,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1488,6 +1575,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1512,6 +1600,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1528,6 +1617,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1552,6 +1642,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1568,6 +1659,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1592,6 +1684,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1608,6 +1701,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1632,6 +1726,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1648,6 +1743,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1672,6 +1768,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1688,6 +1785,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1712,6 +1810,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1728,6 +1827,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1752,6 +1852,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1768,6 +1869,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1792,6 +1894,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1808,6 +1911,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1832,6 +1936,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1848,6 +1953,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1872,6 +1978,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = "sha512", SpamSum = string.Empty, + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1888,6 +1995,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1912,6 +2020,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = "spamsum", + BLAKE3 = string.Empty, }; Rom other = new Rom { @@ -1928,6 +2037,49 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", + }; + + bool actual = self.HashMatch(other); + Assert.True(actual); + } + + [Fact] + public void HashMatch_Rom_PartialBLAKE3_True() + { + Rom self = new Rom + { + CRC16 = string.Empty, + CRC32 = string.Empty, + CRC64 = string.Empty, + MD2 = string.Empty, + MD4 = string.Empty, + MD5 = string.Empty, + RIPEMD128 = string.Empty, + RIPEMD160 = string.Empty, + SHA1 = string.Empty, + SHA256 = string.Empty, + SHA384 = string.Empty, + SHA512 = string.Empty, + SpamSum = string.Empty, + BLAKE3 = "blake3", + }; + Rom other = new Rom + { + CRC16 = "crc16", + CRC32 = "crc32", + CRC64 = "crc64", + MD2 = "md2", + MD4 = "md4", + MD5 = "md5", + RIPEMD128 = "ripemd128", + RIPEMD160 = "ripemd160", + SHA1 = "sha1", + SHA256 = "sha256", + SHA384 = "sha384", + SHA512 = "sha512", + SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -1952,6 +2104,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; Rom other = new Rom { @@ -1968,6 +2121,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HashMatch(other); @@ -2162,6 +2316,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HasZeroHash(); @@ -2186,6 +2341,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -2210,6 +2366,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -2234,6 +2391,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -2258,6 +2416,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -2282,6 +2441,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -2306,6 +2466,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -2330,6 +2491,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -2354,6 +2516,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -2378,6 +2541,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -2402,6 +2566,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -2426,6 +2591,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = HashType.SHA384.ZeroString, SHA512 = string.Empty, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -2450,6 +2616,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = HashType.SHA512.ZeroString, SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -2474,6 +2641,32 @@ namespace SabreTools.Data.Extensions.Test SHA384 = string.Empty, SHA512 = string.Empty, SpamSum = HashType.SpamSum.ZeroString, + BLAKE3 = string.Empty, + }; + + bool actual = self.HasZeroHash(); + Assert.True(actual); + } + + [Fact] + public void HasZeroHash_Rom_ZeroBLAKE3_True() + { + var self = new Rom + { + CRC16 = string.Empty, + CRC32 = string.Empty, + CRC64 = string.Empty, + MD2 = string.Empty, + MD4 = string.Empty, + MD5 = string.Empty, + RIPEMD128 = string.Empty, + RIPEMD160 = string.Empty, + SHA1 = string.Empty, + SHA256 = string.Empty, + SHA384 = string.Empty, + SHA512 = string.Empty, + SpamSum = string.Empty, + BLAKE3 = HashType.BLAKE3.ZeroString, }; bool actual = self.HasZeroHash(); @@ -2498,6 +2691,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = HashType.SHA384.ZeroString, SHA512 = HashType.SHA512.ZeroString, SpamSum = HashType.SpamSum.ZeroString, + BLAKE3 = HashType.BLAKE3.ZeroString, }; bool actual = self.HasZeroHash(); @@ -2583,6 +2777,7 @@ namespace SabreTools.Data.Extensions.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", }; self.FillMissingHashes(other); diff --git a/SabreTools.Data.Extensions/MetadataExtensions.cs b/SabreTools.Data.Extensions/MetadataExtensions.cs index d0d83788..23019145 100644 --- a/SabreTools.Data.Extensions/MetadataExtensions.cs +++ b/SabreTools.Data.Extensions/MetadataExtensions.cs @@ -296,6 +296,10 @@ namespace SabreTools.Data.Extensions string? otherSpamSum = other.SpamSum; bool conditionalSpamSum = ConditionalHashEquals(selfSpamSum, otherSpamSum); + string? selfBlake3 = self.BLAKE3; + string? otherBlake3 = other.BLAKE3; + bool conditionalBlake3 = ConditionalHashEquals(selfBlake3, otherBlake3); + return conditionalCrc16 && conditionalCrc && conditionalCrc64 @@ -308,7 +312,8 @@ namespace SabreTools.Data.Extensions && conditionalSha256 && conditionalSha384 && conditionalSha512 - && conditionalSpamSum; + && conditionalSpamSum + && conditionalBlake3; } /// @@ -357,6 +362,7 @@ namespace SabreTools.Data.Extensions bool sha384Null = string.IsNullOrEmpty(rom.SHA384); bool sha512Null = string.IsNullOrEmpty(rom.SHA512); bool spamsumNull = string.IsNullOrEmpty(rom.SpamSum); + bool blake3Null = string.IsNullOrEmpty(rom.BLAKE3); return !crc16Null || !crc32Null @@ -370,7 +376,8 @@ namespace SabreTools.Data.Extensions || !sha256Null || !sha384Null || !sha512Null - || !spamsumNull; + || !spamsumNull + || !blake3Null; } /// @@ -455,6 +462,13 @@ namespace SabreTools.Data.Extensions string? spamsum = rom.SpamSum; bool spamsumNull = string.IsNullOrEmpty(spamsum) || string.Equals(spamsum, HashType.SpamSum.ZeroString, StringComparison.OrdinalIgnoreCase); + string? blake3 = rom.BLAKE3; +#if NET7_0_OR_GREATER + bool blake3Null = string.IsNullOrEmpty(blake3) || string.Equals(blake3, HashType.BLAKE3.ZeroString, StringComparison.OrdinalIgnoreCase); +#else + bool blake3Null = string.IsNullOrEmpty(blake3) || string.Equals(blake3, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262", StringComparison.OrdinalIgnoreCase); +#endif + return crc16Null && crc32Null && crc64Null @@ -467,7 +481,8 @@ namespace SabreTools.Data.Extensions && sha256Null && sha384Null && sha512Null - && spamsumNull; + && spamsumNull + && blake3Null; } /// @@ -552,6 +567,9 @@ namespace SabreTools.Data.Extensions bool spamsumNull = string.IsNullOrEmpty(self.SpamSum); spamsumNull ^= string.IsNullOrEmpty(other.SpamSum); + bool blake3Null = string.IsNullOrEmpty(self.BLAKE3); + blake3Null ^= string.IsNullOrEmpty(other.BLAKE3); + return !crc16Null || !crc32Null || !crc64Null @@ -564,7 +582,8 @@ namespace SabreTools.Data.Extensions || !sha256Null || !sha384Null || !sha512Null - || !spamsumNull; + || !spamsumNull + || !blake3Null; } #endregion @@ -696,6 +715,11 @@ namespace SabreTools.Data.Extensions string? otherSpamSum = other.SpamSum; if (string.IsNullOrEmpty(selfSpamSum) && !string.IsNullOrEmpty(otherSpamSum)) self.SpamSum = otherSpamSum; + + string? selfBlake3 = self.BLAKE3; + string? otherBlake3 = other.BLAKE3; + if (string.IsNullOrEmpty(selfBlake3) && !string.IsNullOrEmpty(otherBlake3)) + self.BLAKE3 = otherBlake3; } #endregion diff --git a/SabreTools.Data.Models/ClrMamePro/Rom.cs b/SabreTools.Data.Models/ClrMamePro/Rom.cs index 329fa18e..0e369317 100644 --- a/SabreTools.Data.Models/ClrMamePro/Rom.cs +++ b/SabreTools.Data.Models/ClrMamePro/Rom.cs @@ -68,6 +68,9 @@ namespace SabreTools.Data.Models.ClrMamePro /// spamsum; Appears after SHA512 public string? SpamSum { get; set; } + /// blake3; Appears after SHA512 + public string? BLAKE3 { get; set; } + #endregion #region DiscImgeCreator Extensions diff --git a/SabreTools.Data.Models/Hashfile/BLAKE3.cs b/SabreTools.Data.Models/Hashfile/BLAKE3.cs new file mode 100644 index 00000000..e42ecd7f --- /dev/null +++ b/SabreTools.Data.Models/Hashfile/BLAKE3.cs @@ -0,0 +1,15 @@ + +namespace SabreTools.Data.Models.Hashfile +{ + /// + /// BLAKE3 File + /// + public class BLAKE3 + { + [Required] + public string? Hash { get; set; } + + [Required] + public string? File { get; set; } + } +} diff --git a/SabreTools.Data.Models/Hashfile/Hashfile.cs b/SabreTools.Data.Models/Hashfile/Hashfile.cs index 4c4a77f3..c7257748 100644 --- a/SabreTools.Data.Models/Hashfile/Hashfile.cs +++ b/SabreTools.Data.Models/Hashfile/Hashfile.cs @@ -24,5 +24,7 @@ namespace SabreTools.Data.Models.Hashfile public SHA512[]? SHA512 { get; set; } public SpamSum[]? SpamSum { get; set; } + + public BLAKE3[]? BLAKE3 { get; set; } } } diff --git a/SabreTools.Data.Models/Logiqx/Rom.cs b/SabreTools.Data.Models/Logiqx/Rom.cs index bef09c1c..df379a35 100644 --- a/SabreTools.Data.Models/Logiqx/Rom.cs +++ b/SabreTools.Data.Models/Logiqx/Rom.cs @@ -65,6 +65,10 @@ namespace SabreTools.Data.Models.Logiqx [XmlAttribute("spamsum")] public string? SpamSum { get; set; } + /// Hash extension + [XmlAttribute("blake3")] + public string? BLAKE3 { get; set; } + /// DiscImgeCreator extension [XmlAttribute("xxh3_64")] public string? xxHash364 { get; set; } diff --git a/SabreTools.Data.Models/Metadata/Rom.cs b/SabreTools.Data.Models/Metadata/Rom.cs index 9bbbbe04..d1993916 100644 --- a/SabreTools.Data.Models/Metadata/Rom.cs +++ b/SabreTools.Data.Models/Metadata/Rom.cs @@ -39,6 +39,8 @@ namespace SabreTools.Data.Models.Metadata /// ArchiveDotOrg.File public string? BitTorrentMagnetHash { get; set; } + public string? BLAKE3 { get; set; } + /// ArchiveDotOrg.File public string? ClothCoverDetectionModuleVersion { get; set; } @@ -293,6 +295,7 @@ namespace SabreTools.Data.Models.Metadata obj.Bios = Bios; obj.Bitrate = Bitrate; obj.BitTorrentMagnetHash = BitTorrentMagnetHash; + obj.BLAKE3 = BLAKE3; obj.ClothCoverDetectionModuleVersion = ClothCoverDetectionModuleVersion; obj.CollectionCatalogNumber = CollectionCatalogNumber; obj.Comment = Comment; diff --git a/SabreTools.Metadata.DatFiles.Test/DatFileTests.FromMetadata.cs b/SabreTools.Metadata.DatFiles.Test/DatFileTests.FromMetadata.cs index c31826e7..7ff69af3 100644 --- a/SabreTools.Metadata.DatFiles.Test/DatFileTests.FromMetadata.cs +++ b/SabreTools.Metadata.DatFiles.Test/DatFileTests.FromMetadata.cs @@ -778,6 +778,7 @@ namespace SabreTools.Metadata.DatFiles.Test Bios = "bios", Bitrate = "bitrate", BitTorrentMagnetHash = "btih", + BLAKE3 = HashType.BLAKE3.ZeroString, ClothCoverDetectionModuleVersion = "cloth_cover_detection_module_version", CollectionCatalogNumber = "collection-catalog-number", Comment = "comment", @@ -1450,6 +1451,7 @@ namespace SabreTools.Metadata.DatFiles.Test Assert.Equal("bios", rom.Bios); Assert.Equal("bitrate", rom.Bitrate); Assert.Equal("btih", rom.BitTorrentMagnetHash); + Assert.Equal(HashType.BLAKE3.ZeroString, rom.BLAKE3); Assert.Equal("cloth_cover_detection_module_version", rom.ClothCoverDetectionModuleVersion); Assert.Equal("collection-catalog-number", rom.CollectionCatalogNumber); Assert.Equal("comment", rom.Comment); diff --git a/SabreTools.Metadata.DatFiles.Test/DatFileTests.ToMetadata.cs b/SabreTools.Metadata.DatFiles.Test/DatFileTests.ToMetadata.cs index 6e9385af..c6e4bdd0 100644 --- a/SabreTools.Metadata.DatFiles.Test/DatFileTests.ToMetadata.cs +++ b/SabreTools.Metadata.DatFiles.Test/DatFileTests.ToMetadata.cs @@ -1005,6 +1005,7 @@ namespace SabreTools.Metadata.DatFiles.Test Assert.Equal("bios", rom.Bios); Assert.Equal("bitrate", rom.Bitrate); Assert.Equal("btih", rom.BitTorrentMagnetHash); + Assert.Equal(HashType.BLAKE3.ZeroString, rom.BLAKE3); Assert.Equal("cloth_cover_detection_module_version", rom.ClothCoverDetectionModuleVersion); Assert.Equal("collection-catalog-number", rom.CollectionCatalogNumber); Assert.Equal("comment", rom.Comment); diff --git a/SabreTools.Metadata.DatFiles.Test/DatFileTests.cs b/SabreTools.Metadata.DatFiles.Test/DatFileTests.cs index e9bedc5e..ee93d33d 100644 --- a/SabreTools.Metadata.DatFiles.Test/DatFileTests.cs +++ b/SabreTools.Metadata.DatFiles.Test/DatFileTests.cs @@ -1396,7 +1396,8 @@ namespace SabreTools.Metadata.DatFiles.Test SHA256 = "sha256", SHA384 = "sha384", SHA512 = "sha512", - SpamSum = "spamsum" + SpamSum = "spamsum", + BLAKE3 = "blake3", }; string actual = DatFile.FormatPrefixPostfix(item, machine, fix); @@ -1514,7 +1515,8 @@ namespace SabreTools.Metadata.DatFiles.Test SHA256 = "sha256", SHA384 = "sha384", SHA512 = "sha512", - SpamSum = "spamsum" + SpamSum = "spamsum", + BLAKE3 = "blake3", }; string actual = DatFile.FormatPrefixPostfix(item, machine, fix); @@ -1550,6 +1552,7 @@ namespace SabreTools.Metadata.DatFiles.Test Assert.Null(rom.SHA384); Assert.Null(rom.SHA512); Assert.Null(rom.SpamSum); + Assert.Null(rom.BLAKE3); } [Fact] @@ -1575,6 +1578,7 @@ namespace SabreTools.Metadata.DatFiles.Test Assert.Null(rom.SHA384); Assert.Null(rom.SHA512); Assert.Null(rom.SpamSum); + Assert.Null(rom.BLAKE3); } [Fact] @@ -1594,7 +1598,8 @@ namespace SabreTools.Metadata.DatFiles.Test SHA256 = "null", SHA384 = "null", SHA512 = "null", - SpamSum = "null" + SpamSum = "null", + BLAKE3 = "null", }; DatItem actual = DatFile.ProcessNullifiedItem(item); @@ -1614,6 +1619,7 @@ namespace SabreTools.Metadata.DatFiles.Test Assert.Equal(HashType.SHA384.ZeroString, rom.SHA384); Assert.Equal(HashType.SHA512.ZeroString, rom.SHA512); Assert.Equal(HashType.SpamSum.ZeroString, rom.SpamSum); + Assert.Equal(HashType.BLAKE3.ZeroString, rom.BLAKE3); } #endregion @@ -1955,6 +1961,19 @@ namespace SabreTools.Metadata.DatFiles.Test Assert.Equal($"_{hash}", actual); } + [Fact] + public void GetDuplicateSuffix_Rom_BLAKE3() + { + string hash = "hash"; + DatItem datItem = new Rom + { + BLAKE3 = hash + }; + + string actual = DatFile.GetDuplicateSuffix(datItem); + Assert.Equal($"_{hash}", actual); + } + [Fact] public void GetDuplicateSuffix_Other_Generic() { @@ -2434,7 +2453,8 @@ namespace SabreTools.Metadata.DatFiles.Test SHA256 = "crc", SHA384 = "crc", SHA512 = "crc", - SpamSum = "crc" + SpamSum = "crc", + BLAKE3 = "crc", }; DatFile datFile = new Logiqx(null, useGame: false); diff --git a/SabreTools.Metadata.DatFiles.Test/DatStatisticsTests.cs b/SabreTools.Metadata.DatFiles.Test/DatStatisticsTests.cs index 4af801e8..da0f138f 100644 --- a/SabreTools.Metadata.DatFiles.Test/DatStatisticsTests.cs +++ b/SabreTools.Metadata.DatFiles.Test/DatStatisticsTests.cs @@ -60,6 +60,7 @@ namespace SabreTools.Metadata.DatFiles.Test Assert.Equal(0, stats.GetHashCount(HashType.SHA384)); Assert.Equal(0, stats.GetHashCount(HashType.SHA512)); Assert.Equal(0, stats.GetHashCount(HashType.SpamSum)); + Assert.Equal(0, stats.GetHashCount(HashType.BLAKE3)); Assert.Equal(0, stats.GetItemCount(Data.Models.Metadata.ItemType.Disk)); Assert.Equal(0, stats.GetItemCount(Data.Models.Metadata.ItemType.File)); Assert.Equal(0, stats.GetItemCount(Data.Models.Metadata.ItemType.Media)); @@ -88,6 +89,7 @@ namespace SabreTools.Metadata.DatFiles.Test Assert.Equal(1, stats.GetHashCount(HashType.SHA384)); Assert.Equal(1, stats.GetHashCount(HashType.SHA512)); Assert.Equal(2, stats.GetHashCount(HashType.SpamSum)); + Assert.Equal(1, stats.GetHashCount(HashType.BLAKE3)); Assert.Equal(1, stats.GetItemCount(Data.Models.Metadata.ItemType.Disk)); Assert.Equal(1, stats.GetItemCount(Data.Models.Metadata.ItemType.File)); Assert.Equal(1, stats.GetItemCount(Data.Models.Metadata.ItemType.Media)); @@ -116,6 +118,7 @@ namespace SabreTools.Metadata.DatFiles.Test Assert.Equal(0, stats.GetHashCount(HashType.SHA384)); Assert.Equal(0, stats.GetHashCount(HashType.SHA512)); Assert.Equal(0, stats.GetHashCount(HashType.SpamSum)); + Assert.Equal(0, stats.GetHashCount(HashType.BLAKE3)); Assert.Equal(0, stats.GetItemCount(Data.Models.Metadata.ItemType.Disk)); Assert.Equal(0, stats.GetItemCount(Data.Models.Metadata.ItemType.File)); Assert.Equal(0, stats.GetItemCount(Data.Models.Metadata.ItemType.Media)); @@ -151,6 +154,7 @@ namespace SabreTools.Metadata.DatFiles.Test Assert.Equal(0, stats.GetHashCount(HashType.SHA384)); Assert.Equal(0, stats.GetHashCount(HashType.SHA512)); Assert.Equal(0, stats.GetHashCount(HashType.SpamSum)); + Assert.Equal(0, stats.GetHashCount(HashType.BLAKE3)); Assert.Equal(0, stats.GetItemCount(Data.Models.Metadata.ItemType.Disk)); Assert.Equal(0, stats.GetItemCount(Data.Models.Metadata.ItemType.File)); Assert.Equal(0, stats.GetItemCount(Data.Models.Metadata.ItemType.Media)); @@ -179,6 +183,7 @@ namespace SabreTools.Metadata.DatFiles.Test Assert.Equal(1, stats.GetHashCount(HashType.SHA384)); Assert.Equal(1, stats.GetHashCount(HashType.SHA512)); Assert.Equal(2, stats.GetHashCount(HashType.SpamSum)); + Assert.Equal(1, stats.GetHashCount(HashType.BLAKE3)); Assert.Equal(1, stats.GetItemCount(Data.Models.Metadata.ItemType.Disk)); Assert.Equal(1, stats.GetItemCount(Data.Models.Metadata.ItemType.File)); Assert.Equal(1, stats.GetItemCount(Data.Models.Metadata.ItemType.Media)); @@ -203,6 +208,7 @@ namespace SabreTools.Metadata.DatFiles.Test Assert.Equal(0, stats.GetHashCount(HashType.SHA384)); Assert.Equal(0, stats.GetHashCount(HashType.SHA512)); Assert.Equal(0, stats.GetHashCount(HashType.SpamSum)); + Assert.Equal(0, stats.GetHashCount(HashType.BLAKE3)); Assert.Equal(0, stats.GetItemCount(Data.Models.Metadata.ItemType.Disk)); Assert.Equal(0, stats.GetItemCount(Data.Models.Metadata.ItemType.File)); Assert.Equal(0, stats.GetItemCount(Data.Models.Metadata.ItemType.Media)); @@ -238,6 +244,7 @@ namespace SabreTools.Metadata.DatFiles.Test Assert.Equal(1, newStats.GetHashCount(HashType.SHA384)); Assert.Equal(1, newStats.GetHashCount(HashType.SHA512)); Assert.Equal(1, newStats.GetHashCount(HashType.SpamSum)); + Assert.Equal(1, newStats.GetHashCount(HashType.BLAKE3)); Assert.Equal(1, newStats.GetItemCount(Data.Models.Metadata.ItemType.Rom)); Assert.Equal(1, newStats.GetStatusCount(ItemStatus.Good)); } @@ -317,6 +324,7 @@ namespace SabreTools.Metadata.DatFiles.Test rom.SHA384 = HashType.SHA384.ZeroString; rom.SHA512 = HashType.SHA512.ZeroString; rom.SpamSum = HashType.SpamSum.ZeroString; + rom.BLAKE3 = HashType.BLAKE3.ZeroString; return rom; } diff --git a/SabreTools.Metadata.DatFiles.Test/FormatsTests.cs b/SabreTools.Metadata.DatFiles.Test/FormatsTests.cs index ff4a96fd..0a7d2b27 100644 --- a/SabreTools.Metadata.DatFiles.Test/FormatsTests.cs +++ b/SabreTools.Metadata.DatFiles.Test/FormatsTests.cs @@ -700,6 +700,31 @@ namespace SabreTools.Metadata.DatFiles.Test ])); } + [Fact] + public void Blake3File_SupportedTypes() + { + var datFile = new Blake3File(null); + var actual = datFile.SupportedTypes; + Assert.True(actual.SequenceEqual([ + Data.Models.Metadata.ItemType.Rom, + ])); + } + + [Fact] + public void Blake3File_GetMissingRequiredFields_Rom() + { + var datItem = new Rom(); + var datFile = new Blake3File(null); + + var actual = datFile.GetMissingRequiredFields(datItem); + + Assert.NotNull(actual); + Assert.True(actual.SequenceEqual([ + nameof(Data.Models.Metadata.Rom.Name), + nameof(Data.Models.Metadata.Rom.BLAKE3), + ])); + } + #endregion #region Listrom diff --git a/SabreTools.Metadata.DatFiles/DatFile.cs b/SabreTools.Metadata.DatFiles/DatFile.cs index 7885d33a..829573d5 100644 --- a/SabreTools.Metadata.DatFiles/DatFile.cs +++ b/SabreTools.Metadata.DatFiles/DatFile.cs @@ -593,7 +593,8 @@ namespace SabreTools.Metadata.DatFiles sha384 = string.Empty, sha512 = string.Empty, size = string.Empty, - spamsum = string.Empty; + spamsum = string.Empty, + blake3 = string.Empty; // Ensure we have the proper values for replacement if (item is Disk disk) @@ -633,6 +634,7 @@ namespace SabreTools.Metadata.DatFiles sha512 = rom.SHA512 ?? string.Empty; size = rom.Size.ToString() ?? string.Empty; spamsum = rom.SpamSum ?? string.Empty; + blake3 = rom.BLAKE3 ?? string.Empty; } // Now do bulk replacement where possible @@ -657,7 +659,8 @@ namespace SabreTools.Metadata.DatFiles .Replace("%sha384%", sha384) .Replace("%sha512%", sha512) .Replace("%size%", size) - .Replace("%spamsum%", spamsum); + .Replace("%spamsum%", spamsum) + .Replace("%blake3%", blake3); return fix; } @@ -697,6 +700,11 @@ namespace SabreTools.Metadata.DatFiles rom.SHA384 = rom.SHA384 == "null" ? HashType.SHA384.ZeroString : null; rom.SHA512 = rom.SHA512 == "null" ? HashType.SHA512.ZeroString : null; rom.SpamSum = rom.SpamSum == "null" ? HashType.SpamSum.ZeroString : null; +#if NET7_0_OR_GREATER + rom.BLAKE3 = rom.BLAKE3 == "null" ? HashType.BLAKE3.ZeroString : null; +#else + rom.BLAKE3 = rom.BLAKE3 == "null" ? "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262" : null; +#endif return rom; } @@ -1127,6 +1135,10 @@ namespace SabreTools.Metadata.DatFiles if (!string.IsNullOrEmpty(spamSum)) return $"_{spamSum}"; + string? blake3 = datItem.BLAKE3; + if (!string.IsNullOrEmpty(blake3)) + return $"_{blake3}"; + return "_1"; } diff --git a/SabreTools.Metadata.DatFiles/DatStatistics.cs b/SabreTools.Metadata.DatFiles/DatStatistics.cs index e07673c4..7c38d194 100644 --- a/SabreTools.Metadata.DatFiles/DatStatistics.cs +++ b/SabreTools.Metadata.DatFiles/DatStatistics.cs @@ -353,6 +353,12 @@ namespace SabreTools.Metadata.DatFiles ModifyHashCount(HashType.SHA384, string.IsNullOrEmpty(rom.SHA384) ? 0 : 1); ModifyHashCount(HashType.SHA512, string.IsNullOrEmpty(rom.SHA512) ? 0 : 1); ModifyHashCount(HashType.SpamSum, string.IsNullOrEmpty(rom.SpamSum) ? 0 : 1); +#if NET7_0_OR_GREATER + ModifyHashCount(HashType.BLAKE3, string.IsNullOrEmpty(rom.BLAKE3) ? 0 : 1); +#else + // HACK because BLAKE3 is not supported below .NET 7 + ModifyHashCount(HashType.CRC1_ZERO, string.IsNullOrEmpty(rom.BLAKE3) ? 0 : 1); +#endif } ModifyStatusCount(ItemStatus.BadDump, status == ItemStatus.BadDump ? 1 : 0); @@ -430,6 +436,12 @@ namespace SabreTools.Metadata.DatFiles ModifyHashCount(HashType.SHA384, string.IsNullOrEmpty(rom.SHA384) ? 0 : -1); ModifyHashCount(HashType.SHA512, string.IsNullOrEmpty(rom.SHA512) ? 0 : -1); ModifyHashCount(HashType.SpamSum, string.IsNullOrEmpty(rom.SpamSum) ? 0 : -1); +#if NET7_0_OR_GREATER + ModifyHashCount(HashType.BLAKE3, string.IsNullOrEmpty(rom.BLAKE3) ? 0 : -1); +#else + // HACK because BLAKE3 is not supported below .NET 7 + ModifyHashCount(HashType.CRC1_ZERO, string.IsNullOrEmpty(rom.BLAKE3) ? 0 : -1); +#endif } ModifyStatusCount(ItemStatus.BadDump, status == ItemStatus.BadDump ? -1 : 0); diff --git a/SabreTools.Metadata.DatFiles/Enums.cs b/SabreTools.Metadata.DatFiles/Enums.cs index 2cf3667b..9a68c70a 100644 --- a/SabreTools.Metadata.DatFiles/Enums.cs +++ b/SabreTools.Metadata.DatFiles/Enums.cs @@ -169,6 +169,11 @@ namespace SabreTools.Metadata.DatFiles /// RedumpSpamSum, + /// + /// BLAKE3 hash list + /// + RedumpBLAKE3, + #endregion } } diff --git a/SabreTools.Metadata.DatFiles/Formats/Blake3File.cs b/SabreTools.Metadata.DatFiles/Formats/Blake3File.cs new file mode 100644 index 00000000..8a6894af --- /dev/null +++ b/SabreTools.Metadata.DatFiles/Formats/Blake3File.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using SabreTools.Hashing; +using SabreTools.Metadata.DatItems; +using SabreTools.Metadata.DatItems.Formats; + +namespace SabreTools.Metadata.DatFiles.Formats +{ + /// + /// Represents an BLAKE3 hashfile + /// + public sealed class Blake3File : Hashfile + { + /// + public override Data.Models.Metadata.ItemType[] SupportedTypes + => [ + Data.Models.Metadata.ItemType.Rom, + ]; + + /// + /// Constructor designed for casting a base DatFile + /// + /// Parent DatFile to copy from + public Blake3File(DatFile? datFile) : base(datFile) + { +#if NET7_0_OR_GREATER + _hash = HashType.BLAKE3; +#else + // HACK because BLAKE3 is not supported below .NET 7 + _hash = HashType.CRC1_ZERO; +#endif + Header.DatFormat = DatFormat.RedumpBLAKE3; + } + + /// + protected internal override List? GetMissingRequiredFields(DatItem datItem) + { + List missingFields = []; + + switch (datItem) + { + case Rom rom: + if (string.IsNullOrEmpty(rom.Name)) + missingFields.Add(nameof(Data.Models.Metadata.Rom.Name)); + if (string.IsNullOrEmpty(rom.BLAKE3)) + missingFields.Add(nameof(Data.Models.Metadata.Rom.BLAKE3)); + break; + + default: + // Item type is not supported + break; + } + + return missingFields; + } + } +} diff --git a/SabreTools.Metadata.DatFiles/Formats/ClrMamePro.cs b/SabreTools.Metadata.DatFiles/Formats/ClrMamePro.cs index 4d97268b..6c1f85f5 100644 --- a/SabreTools.Metadata.DatFiles/Formats/ClrMamePro.cs +++ b/SabreTools.Metadata.DatFiles/Formats/ClrMamePro.cs @@ -90,7 +90,8 @@ namespace SabreTools.Metadata.DatFiles.Formats missingFields.Add(nameof(Data.Models.Metadata.Rom.Name)); if (rom.Size is null || rom.Size < 0) missingFields.Add(nameof(Data.Models.Metadata.Rom.Size)); - if (string.IsNullOrEmpty(rom.CRC16) + if (string.IsNullOrEmpty(rom.BLAKE3) + && string.IsNullOrEmpty(rom.CRC16) && string.IsNullOrEmpty(rom.CRC32) && string.IsNullOrEmpty(rom.CRC64) && string.IsNullOrEmpty(rom.MD2) diff --git a/SabreTools.Metadata.DatFiles/Formats/Logiqx.cs b/SabreTools.Metadata.DatFiles/Formats/Logiqx.cs index 82d809e6..291cfd37 100644 --- a/SabreTools.Metadata.DatFiles/Formats/Logiqx.cs +++ b/SabreTools.Metadata.DatFiles/Formats/Logiqx.cs @@ -284,7 +284,8 @@ namespace SabreTools.Metadata.DatFiles.Formats missingFields.Add(nameof(Data.Models.Metadata.Rom.Name)); if (rom.Size is null || rom.Size < 0) missingFields.Add(nameof(Data.Models.Metadata.Rom.Size)); - if (string.IsNullOrEmpty(rom.CRC16) + if (string.IsNullOrEmpty(rom.BLAKE3) + && string.IsNullOrEmpty(rom.CRC16) && string.IsNullOrEmpty(rom.CRC32) && string.IsNullOrEmpty(rom.CRC64) && string.IsNullOrEmpty(rom.MD2) diff --git a/SabreTools.Metadata.DatFiles/ItemDatabase.cs b/SabreTools.Metadata.DatFiles/ItemDatabase.cs index 240c430f..141f48fb 100644 --- a/SabreTools.Metadata.DatFiles/ItemDatabase.cs +++ b/SabreTools.Metadata.DatFiles/ItemDatabase.cs @@ -482,6 +482,7 @@ namespace SabreTools.Metadata.DatFiles rom.SHA384 = null; // HashType.SHA384.ZeroString; rom.SHA512 = null; // HashType.SHA512.ZeroString; rom.SpamSum = null; // HashType.SpamSum.ZeroString; + rom.BLAKE3 = null; // HashType.BLAKE3.ZeroString; } // If the file has no size and it's not the above case, skip and log diff --git a/SabreTools.Metadata.DatFiles/ItemDictionary.cs b/SabreTools.Metadata.DatFiles/ItemDictionary.cs index 825b196d..29973ee0 100644 --- a/SabreTools.Metadata.DatFiles/ItemDictionary.cs +++ b/SabreTools.Metadata.DatFiles/ItemDictionary.cs @@ -163,6 +163,7 @@ namespace SabreTools.Metadata.DatFiles rom.SHA384 = null; // HashType.SHA384.ZeroString; rom.SHA512 = null; // HashType.SHA512.ZeroString; rom.SpamSum = null; // HashType.SpamSum.ZeroString; + rom.BLAKE3 = null; // HashType.BLAKE3.ZeroString; } // If the file has no size and it's not the above case, skip and log diff --git a/SabreTools.Metadata.DatItems.Test/DatItemTests.cs b/SabreTools.Metadata.DatItems.Test/DatItemTests.cs index 1dceb862..97c562ee 100644 --- a/SabreTools.Metadata.DatItems.Test/DatItemTests.cs +++ b/SabreTools.Metadata.DatItems.Test/DatItemTests.cs @@ -290,6 +290,10 @@ namespace SabreTools.Metadata.DatItems.Test [InlineData(ItemKey.SpamSum, false, true, "3::")] [InlineData(ItemKey.SpamSum, true, false, "3::")] [InlineData(ItemKey.SpamSum, true, true, "3::")] + [InlineData(ItemKey.BLAKE3, false, false, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] + [InlineData(ItemKey.BLAKE3, false, true, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] + [InlineData(ItemKey.BLAKE3, true, false, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] + [InlineData(ItemKey.BLAKE3, true, true, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] public void GetKeyDB_DefaultImplementation(ItemKey bucketedBy, bool lower, bool norename, string expected) { Source source = new Source(0); @@ -363,6 +367,10 @@ namespace SabreTools.Metadata.DatItems.Test [InlineData(ItemKey.SpamSum, false, true, "BASE64")] [InlineData(ItemKey.SpamSum, true, false, "base64")] [InlineData(ItemKey.SpamSum, true, true, "base64")] + [InlineData(ItemKey.BLAKE3, false, false, "DEADBEEF")] + [InlineData(ItemKey.BLAKE3, false, true, "DEADBEEF")] + [InlineData(ItemKey.BLAKE3, true, false, "deadbeef")] + [InlineData(ItemKey.BLAKE3, true, true, "deadbeef")] public void GetKeyDB_CustomImplementation(ItemKey bucketedBy, bool lower, bool norename, string expected) { Source source = new Source(0); @@ -383,7 +391,8 @@ namespace SabreTools.Metadata.DatItems.Test SHA256 = "DEADBEEF", SHA384 = "DEADBEEF", SHA512 = "DEADBEEF", - SpamSum = "BASE64" + SpamSum = "BASE64", + BLAKE3 = "DEADBEEF", }; string actual = datItem.GetKey(bucketedBy, machine, source, lower, norename); diff --git a/SabreTools.Metadata.DatItems.Test/Formats/DiskTests.cs b/SabreTools.Metadata.DatItems.Test/Formats/DiskTests.cs index 6ba7f164..19e0236f 100644 --- a/SabreTools.Metadata.DatItems.Test/Formats/DiskTests.cs +++ b/SabreTools.Metadata.DatItems.Test/Formats/DiskTests.cs @@ -260,6 +260,10 @@ namespace SabreTools.Metadata.DatItems.Formats.Test [InlineData(ItemKey.SpamSum, false, true, "3::")] [InlineData(ItemKey.SpamSum, true, false, "3::")] [InlineData(ItemKey.SpamSum, true, true, "3::")] + [InlineData(ItemKey.BLAKE3, false, false, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] + [InlineData(ItemKey.BLAKE3, false, true, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] + [InlineData(ItemKey.BLAKE3, true, false, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] + [InlineData(ItemKey.BLAKE3, true, true, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] public void GetKeyDBTest(ItemKey bucketedBy, bool lower, bool norename, string expected) { Source source = new Source(0); diff --git a/SabreTools.Metadata.DatItems.Test/Formats/FileTests.cs b/SabreTools.Metadata.DatItems.Test/Formats/FileTests.cs index ee87dd62..00305ac6 100644 --- a/SabreTools.Metadata.DatItems.Test/Formats/FileTests.cs +++ b/SabreTools.Metadata.DatItems.Test/Formats/FileTests.cs @@ -337,6 +337,10 @@ namespace SabreTools.Metadata.DatItems.Formats.Test [InlineData(ItemKey.SpamSum, false, true, "3::")] [InlineData(ItemKey.SpamSum, true, false, "3::")] [InlineData(ItemKey.SpamSum, true, true, "3::")] + [InlineData(ItemKey.BLAKE3, false, false, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] + [InlineData(ItemKey.BLAKE3, false, true, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] + [InlineData(ItemKey.BLAKE3, true, false, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] + [InlineData(ItemKey.BLAKE3, true, true, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] public void GetKeyDBTest(ItemKey bucketedBy, bool lower, bool norename, string expected) { Source source = new Source(0); diff --git a/SabreTools.Metadata.DatItems.Test/Formats/MediaTests.cs b/SabreTools.Metadata.DatItems.Test/Formats/MediaTests.cs index eb27e090..ce08be42 100644 --- a/SabreTools.Metadata.DatItems.Test/Formats/MediaTests.cs +++ b/SabreTools.Metadata.DatItems.Test/Formats/MediaTests.cs @@ -330,6 +330,10 @@ namespace SabreTools.Metadata.DatItems.Formats.Test [InlineData(ItemKey.SpamSum, false, true, "DEADBEEF")] [InlineData(ItemKey.SpamSum, true, false, "deadbeef")] [InlineData(ItemKey.SpamSum, true, true, "deadbeef")] + [InlineData(ItemKey.BLAKE3, false, false, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] + [InlineData(ItemKey.BLAKE3, false, true, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] + [InlineData(ItemKey.BLAKE3, true, false, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] + [InlineData(ItemKey.BLAKE3, true, true, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] public void GetKeyDBTest(ItemKey bucketedBy, bool lower, bool norename, string expected) { Source source = new Source(0); diff --git a/SabreTools.Metadata.DatItems.Test/Formats/RomTests.cs b/SabreTools.Metadata.DatItems.Test/Formats/RomTests.cs index ff8604f7..d891cfe0 100644 --- a/SabreTools.Metadata.DatItems.Test/Formats/RomTests.cs +++ b/SabreTools.Metadata.DatItems.Test/Formats/RomTests.cs @@ -28,6 +28,7 @@ namespace SabreTools.Metadata.DatItems.Formats.Test Assert.Null(self.SHA384); Assert.Null(self.SHA512); Assert.Null(self.SpamSum); + Assert.Null(self.BLAKE3); } [Fact] @@ -49,7 +50,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = "sha256", SHA384 = "sha384", SHA512 = "sha512", - SpamSum = "spamsum" + SpamSum = "spamsum", + BLAKE3 = "blake3", }; self.FillMissingInformation(other); @@ -67,6 +69,7 @@ namespace SabreTools.Metadata.DatItems.Formats.Test Assert.Equal("sha384", self.SHA384); Assert.Equal("sha512", self.SHA512); Assert.Equal("spamsum", self.SpamSum); + Assert.Equal("blake3", self.BLAKE3); } #endregion @@ -98,7 +101,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasHashes(); @@ -122,7 +126,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasHashes(); @@ -146,7 +151,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasHashes(); @@ -170,7 +176,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasHashes(); @@ -194,7 +201,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasHashes(); @@ -218,7 +226,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasHashes(); @@ -242,7 +251,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasHashes(); @@ -266,7 +276,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasHashes(); @@ -290,7 +301,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasHashes(); @@ -314,7 +326,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = "sha256", SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasHashes(); @@ -338,7 +351,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = "sha384", SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasHashes(); @@ -362,7 +376,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = "sha512", - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasHashes(); @@ -386,7 +401,33 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = "spamsum" + SpamSum = "spamsum", + BLAKE3 = string.Empty, + }; + + bool actual = self.HasHashes(); + Assert.True(actual); + } + + [Fact] + public void HasHashes_BLAKE3_True() + { + Rom self = new Rom + { + CRC16 = string.Empty, + CRC32 = string.Empty, + CRC64 = string.Empty, + MD2 = string.Empty, + MD4 = string.Empty, + MD5 = string.Empty, + RIPEMD128 = string.Empty, + RIPEMD160 = string.Empty, + SHA1 = string.Empty, + SHA256 = string.Empty, + SHA384 = string.Empty, + SHA512 = string.Empty, + SpamSum = string.Empty, + BLAKE3 = "blake3", }; bool actual = self.HasHashes(); @@ -410,7 +451,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = "sha256", SHA384 = "sha384", SHA512 = "sha512", - SpamSum = "spamsum" + SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HasHashes(); @@ -446,7 +488,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = "sha256", SHA384 = "sha384", SHA512 = "sha512", - SpamSum = "spamsum" + SpamSum = "spamsum", + BLAKE3 = "blake3", }; bool actual = self.HasZeroHash(); @@ -470,7 +513,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -494,7 +538,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -518,7 +563,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -542,7 +588,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -566,7 +613,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -590,7 +638,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -614,7 +663,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -638,7 +688,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -662,7 +713,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -686,7 +738,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = HashType.SHA256.ZeroString, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -710,7 +763,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = HashType.SHA384.ZeroString, SHA512 = string.Empty, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -734,7 +788,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = HashType.SHA512.ZeroString, - SpamSum = string.Empty + SpamSum = string.Empty, + BLAKE3 = string.Empty, }; bool actual = self.HasZeroHash(); @@ -758,7 +813,33 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = string.Empty, SHA384 = string.Empty, SHA512 = string.Empty, - SpamSum = HashType.SpamSum.ZeroString + SpamSum = HashType.SpamSum.ZeroString, + BLAKE3 = string.Empty, + }; + + bool actual = self.HasZeroHash(); + Assert.True(actual); + } + + [Fact] + public void HasZeroHash_ZeroBLAKE3_True() + { + Rom self = new Rom + { + CRC16 = string.Empty, + CRC32 = string.Empty, + CRC64 = string.Empty, + MD2 = string.Empty, + MD4 = string.Empty, + MD5 = string.Empty, + RIPEMD128 = string.Empty, + RIPEMD160 = string.Empty, + SHA1 = string.Empty, + SHA256 = string.Empty, + SHA384 = string.Empty, + SHA512 = string.Empty, + SpamSum = string.Empty, + BLAKE3 = HashType.BLAKE3.ZeroString, }; bool actual = self.HasZeroHash(); @@ -782,7 +863,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = HashType.SHA256.ZeroString, SHA384 = HashType.SHA384.ZeroString, SHA512 = HashType.SHA512.ZeroString, - SpamSum = HashType.SpamSum.ZeroString + SpamSum = HashType.SpamSum.ZeroString, + BLAKE3 = HashType.BLAKE3.ZeroString, }; bool actual = self.HasZeroHash(); @@ -855,6 +937,10 @@ namespace SabreTools.Metadata.DatItems.Formats.Test [InlineData(ItemKey.SpamSum, false, true, "DEADBEEF")] [InlineData(ItemKey.SpamSum, true, false, "deadbeef")] [InlineData(ItemKey.SpamSum, true, true, "deadbeef")] + [InlineData(ItemKey.BLAKE3, false, false, "DEADBEEF")] + [InlineData(ItemKey.BLAKE3, false, true, "DEADBEEF")] + [InlineData(ItemKey.BLAKE3, true, false, "deadbeef")] + [InlineData(ItemKey.BLAKE3, true, true, "deadbeef")] public void GetKeyDBTest(ItemKey bucketedBy, bool lower, bool norename, string expected) { Source source = new Source(0); @@ -875,7 +961,8 @@ namespace SabreTools.Metadata.DatItems.Formats.Test SHA256 = "DEADBEEF", SHA384 = "DEADBEEF", SHA512 = "DEADBEEF", - SpamSum = "DEADBEEF" + SpamSum = "DEADBEEF", + BLAKE3 = "DEADBEEF", }; string actual = datItem.GetKey(bucketedBy, machine, source, lower, norename); diff --git a/SabreTools.Metadata.DatItems/DatItem.cs b/SabreTools.Metadata.DatItems/DatItem.cs index 88092f15..fc096929 100644 --- a/SabreTools.Metadata.DatItems/DatItem.cs +++ b/SabreTools.Metadata.DatItems/DatItem.cs @@ -191,6 +191,14 @@ namespace SabreTools.Metadata.DatItems // Now determine what the key should be based on the bucketedBy value switch (bucketedBy) { + case ItemKey.BLAKE3: +#if NET7_0_OR_GREATER + key = HashType.BLAKE3.ZeroString; +#else + key = "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"; +#endif + break; + case ItemKey.CRC16: key = HashType.CRC16.ZeroString; break; diff --git a/SabreTools.Metadata.DatItems/Enums.cs b/SabreTools.Metadata.DatItems/Enums.cs index e5e58a37..a598d76a 100644 --- a/SabreTools.Metadata.DatItems/Enums.cs +++ b/SabreTools.Metadata.DatItems/Enums.cs @@ -26,6 +26,7 @@ namespace SabreTools.Metadata.DatItems Machine, + BLAKE3, CRC16, CRC32, CRC64, diff --git a/SabreTools.Metadata.DatItems/Formats/Rom.cs b/SabreTools.Metadata.DatItems/Formats/Rom.cs index 8f6d38fa..be603944 100644 --- a/SabreTools.Metadata.DatItems/Formats/Rom.cs +++ b/SabreTools.Metadata.DatItems/Formats/Rom.cs @@ -74,6 +74,12 @@ namespace SabreTools.Metadata.DatItems.Formats set => _internal.BitTorrentMagnetHash = value; } + public string? BLAKE3 + { + get => _internal.BLAKE3; + set => _internal.BLAKE3 = value; + } + public string? ClothCoverDetectionModuleVersion { get => _internal.ClothCoverDetectionModuleVersion; @@ -858,6 +864,10 @@ namespace SabreTools.Metadata.DatItems.Formats // Now determine what the key should be based on the bucketedBy value switch (bucketedBy) { + case ItemKey.BLAKE3: + key = BLAKE3; + break; + case ItemKey.CRC16: key = CRC16; break; diff --git a/SabreTools.Metadata.Filter.Test/FilterObjectTests.cs b/SabreTools.Metadata.Filter.Test/FilterObjectTests.cs index 20b07857..6d3dc128 100644 --- a/SabreTools.Metadata.Filter.Test/FilterObjectTests.cs +++ b/SabreTools.Metadata.Filter.Test/FilterObjectTests.cs @@ -1582,6 +1582,7 @@ namespace SabreTools.Metadata.Filter.Test [InlineData("rom.bitrate", "bitrate")] [InlineData("rom.bittorrentmagnethash", "btih")] [InlineData("rom.btih", "btih")] + [InlineData("rom.blake3", "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")] [InlineData("rom.cloth_cover_detection_module_version", "cloth_cover_detection_module_version")] [InlineData("rom.clothcoverdetectionmoduleversion", "cloth_cover_detection_module_version")] [InlineData("rom.collection-catalog-number", "collection-catalog-number")] @@ -1719,6 +1720,7 @@ namespace SabreTools.Metadata.Filter.Test Bios = "bios", Bitrate = "bitrate", BitTorrentMagnetHash = "btih", + BLAKE3 = HashType.BLAKE3.ZeroString, ClothCoverDetectionModuleVersion = "cloth_cover_detection_module_version", CollectionCatalogNumber = "collection-catalog-number", Comment = "comment", diff --git a/SabreTools.Metadata.Filter/Constants.cs b/SabreTools.Metadata.Filter/Constants.cs index 5f5c5c85..12b61cee 100644 --- a/SabreTools.Metadata.Filter/Constants.cs +++ b/SabreTools.Metadata.Filter/Constants.cs @@ -540,6 +540,7 @@ namespace SabreTools.Metadata.Filter "bitrate", "bittorrentmagnethash", "btih", + "blake3", "cloth_cover_detection_module_version", "clothcoverdetectionmoduleversion", "collection-catalog-number", diff --git a/SabreTools.Metadata.Filter/FilterObject.cs b/SabreTools.Metadata.Filter/FilterObject.cs index a06d2688..60f1ebd5 100644 --- a/SabreTools.Metadata.Filter/FilterObject.cs +++ b/SabreTools.Metadata.Filter/FilterObject.cs @@ -1901,6 +1901,9 @@ namespace SabreTools.Metadata.Filter case "bittorrentmagnethash": checkValue = obj.BitTorrentMagnetHash; return true; + case "blake3": + checkValue = obj.BLAKE3; + return true; case "cloth_cover_detection_module_version": case "clothcoverdetectionmoduleversion": checkValue = obj.ClothCoverDetectionModuleVersion; diff --git a/SabreTools.Serialization.CrossModel.Test/ClrMameProTests.cs b/SabreTools.Serialization.CrossModel.Test/ClrMameProTests.cs index 473b7d20..4d0708a3 100644 --- a/SabreTools.Serialization.CrossModel.Test/ClrMameProTests.cs +++ b/SabreTools.Serialization.CrossModel.Test/ClrMameProTests.cs @@ -115,6 +115,7 @@ namespace SabreTools.Serialization.CrossModel.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", xxHash364 = "xxhash364", xxHash3128 = "xxhash3128", Region = "region", @@ -378,6 +379,7 @@ namespace SabreTools.Serialization.CrossModel.Test Assert.Equal("sha384", rom.SHA384); Assert.Equal("sha512", rom.SHA512); Assert.Equal("spamsum", rom.SpamSum); + Assert.Equal("blake3", rom.BLAKE3); Assert.Equal("xxhash364", rom.xxHash364); Assert.Equal("xxhash3128", rom.xxHash3128); Assert.Equal("region", rom.Region); diff --git a/SabreTools.Serialization.CrossModel.Test/HashfileTests.cs b/SabreTools.Serialization.CrossModel.Test/HashfileTests.cs index c1311acf..2ad163aa 100644 --- a/SabreTools.Serialization.CrossModel.Test/HashfileTests.cs +++ b/SabreTools.Serialization.CrossModel.Test/HashfileTests.cs @@ -213,6 +213,29 @@ namespace SabreTools.Serialization.CrossModel.Test Validate(newSpamsum); } + [Fact] + public void RoundTripBLAKE3Test() + { + // Get the cross-model serializer + var serializer = new Hashfile(); + + // Build the data + Data.Models.Hashfile.Hashfile hf = Build(HashType.BLAKE3); + + // Serialize to generic model + Data.Models.Metadata.MetadataFile? metadata = serializer.Serialize(hf); + Assert.NotNull(metadata); + + // Serialize back to original model + Data.Models.Hashfile.Hashfile? newHf = serializer.Deserialize(metadata, HashType.BLAKE3); + + // Validate the data + Assert.NotNull(newHf); + Assert.NotNull(newHf.BLAKE3); + var newBlake3 = Assert.Single(newHf.BLAKE3); + Validate(newBlake3); + } + /// /// Build model for serialization and deserialization /// @@ -236,6 +259,8 @@ namespace SabreTools.Serialization.CrossModel.Test return new Data.Models.Hashfile.Hashfile { SHA512 = [new Data.Models.Hashfile.SHA512 { Hash = "hash", File = "file" }] }; else if (hashType == HashType.SpamSum) return new Data.Models.Hashfile.Hashfile { SpamSum = [new Data.Models.Hashfile.SpamSum { Hash = "hash", File = "file" }] }; + else if (hashType == HashType.BLAKE3) + return new Data.Models.Hashfile.Hashfile { BLAKE3 = [new Data.Models.Hashfile.BLAKE3 { Hash = "hash", File = "file" }] }; else throw new ArgumentOutOfRangeException(nameof(hashType)); } @@ -329,5 +354,15 @@ namespace SabreTools.Serialization.CrossModel.Test Assert.Equal("hash", spamsum.Hash); Assert.Equal("file", spamsum.File); } + + /// + /// Validate a BLAKE3 + /// + private static void Validate(Data.Models.Hashfile.BLAKE3? blake3) + { + Assert.NotNull(blake3); + Assert.Equal("hash", blake3.Hash); + Assert.Equal("file", blake3.File); + } } } diff --git a/SabreTools.Serialization.CrossModel.Test/LogiqxTests.cs b/SabreTools.Serialization.CrossModel.Test/LogiqxTests.cs index 1418e8b8..31f6f149 100644 --- a/SabreTools.Serialization.CrossModel.Test/LogiqxTests.cs +++ b/SabreTools.Serialization.CrossModel.Test/LogiqxTests.cs @@ -171,6 +171,7 @@ namespace SabreTools.Serialization.CrossModel.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", xxHash364 = "xxhash364", xxHash3128 = "xxhash3128", Merge = "merge", @@ -504,6 +505,7 @@ namespace SabreTools.Serialization.CrossModel.Test Assert.Equal("sha384", rom.SHA384); Assert.Equal("sha512", rom.SHA512); Assert.Equal("spamsum", rom.SpamSum); + Assert.Equal("blake3", rom.BLAKE3); Assert.Equal("xxhash364", rom.xxHash364); Assert.Equal("xxhash3128", rom.xxHash3128); Assert.Equal("merge", rom.Merge); diff --git a/SabreTools.Serialization.CrossModel/ClrMamePro.Deserializer.cs b/SabreTools.Serialization.CrossModel/ClrMamePro.Deserializer.cs index e6ee071e..4e6aa577 100644 --- a/SabreTools.Serialization.CrossModel/ClrMamePro.Deserializer.cs +++ b/SabreTools.Serialization.CrossModel/ClrMamePro.Deserializer.cs @@ -290,6 +290,7 @@ namespace SabreTools.Serialization.CrossModel SHA384 = item.SHA384, SHA512 = item.SHA512, SpamSum = item.SpamSum, + BLAKE3 = item.BLAKE3, xxHash364 = item.xxHash364, xxHash3128 = item.xxHash3128, Merge = item.Merge, diff --git a/SabreTools.Serialization.CrossModel/ClrMamePro.Serializer.cs b/SabreTools.Serialization.CrossModel/ClrMamePro.Serializer.cs index 21277c45..bb597297 100644 --- a/SabreTools.Serialization.CrossModel/ClrMamePro.Serializer.cs +++ b/SabreTools.Serialization.CrossModel/ClrMamePro.Serializer.cs @@ -270,6 +270,7 @@ namespace SabreTools.Serialization.CrossModel SHA384 = item.SHA384, SHA512 = item.SHA512, SpamSum = item.SpamSum, + BLAKE3 = item.BLAKE3, xxHash364 = item.xxHash364, xxHash3128 = item.xxHash3128, Merge = item.Merge, diff --git a/SabreTools.Serialization.CrossModel/Hashfile.Deserializer.cs b/SabreTools.Serialization.CrossModel/Hashfile.Deserializer.cs index d2ddd494..ca88d903 100644 --- a/SabreTools.Serialization.CrossModel/Hashfile.Deserializer.cs +++ b/SabreTools.Serialization.CrossModel/Hashfile.Deserializer.cs @@ -36,6 +36,7 @@ namespace SabreTools.Serialization.CrossModel var sha384s = new List(); var sha512s = new List(); var spamsums = new List(); + var blake3s = new List(); foreach (var hashfile in hashfiles) { @@ -61,6 +62,8 @@ namespace SabreTools.Serialization.CrossModel sha512s.AddRange(hashfile.SHA512); if (hashfile.SpamSum is not null && hashfile.SpamSum.Length > 0) spamsums.AddRange(hashfile.SpamSum); + if (hashfile.BLAKE3 is not null && hashfile.BLAKE3.Length > 0) + blake3s.AddRange(hashfile.BLAKE3); } var hashfileItem = new Data.Models.Hashfile.Hashfile(); @@ -87,6 +90,8 @@ namespace SabreTools.Serialization.CrossModel hashfileItem.SHA512 = [.. sha512s]; if (spamsums.Count > 0) hashfileItem.SpamSum = [.. spamsums]; + if (blake3s.Count > 0) + hashfileItem.BLAKE3 = [.. blake3s]; return hashfileItem; } @@ -135,6 +140,14 @@ namespace SabreTools.Serialization.CrossModel SpamSum = hash == HashType.SpamSum ? Array.ConvertAll(roms, ConvertToSpamSum) : null, +#if NET7_0_OR_GREATER + BLAKE3 = hash == HashType.BLAKE3 +#else + // HACK because BLAKE3 is not supported below .NET 7 + BLAKE3 = hash == HashType.CRC1_ZERO +#endif + ? Array.ConvertAll(roms, ConvertToBLAKE3) + : null, }; } @@ -280,5 +293,18 @@ namespace SabreTools.Serialization.CrossModel }; return spamsum; } + + /// + /// Convert from to + /// + private static BLAKE3 ConvertToBLAKE3(Data.Models.Metadata.Rom item) + { + var spamsum = new BLAKE3 + { + Hash = item.BLAKE3, + File = item.Name, + }; + return spamsum; + } } } diff --git a/SabreTools.Serialization.CrossModel/Hashfile.Serializer.cs b/SabreTools.Serialization.CrossModel/Hashfile.Serializer.cs index 0601c170..92973c1e 100644 --- a/SabreTools.Serialization.CrossModel/Hashfile.Serializer.cs +++ b/SabreTools.Serialization.CrossModel/Hashfile.Serializer.cs @@ -29,7 +29,7 @@ namespace SabreTools.Serialization.CrossModel { var header = new Data.Models.Metadata.Header { - Name = "Hashfile", + Name = "Hashfile", }; return header; } @@ -63,6 +63,8 @@ namespace SabreTools.Serialization.CrossModel machine.Rom = Array.ConvertAll(item.SHA512, ConvertToInternalModel); else if (item.SpamSum is not null && item.SpamSum.Length > 0) machine.Rom = Array.ConvertAll(item.SpamSum, ConvertToInternalModel); + else if (item.BLAKE3 is not null && item.BLAKE3.Length > 0) + machine.Rom = Array.ConvertAll(item.BLAKE3, ConvertToInternalModel); return machine; } @@ -209,5 +211,18 @@ namespace SabreTools.Serialization.CrossModel }; return rom; } + + /// + /// Convert from to + /// + private static Data.Models.Metadata.Rom ConvertToInternalModel(BLAKE3 item) + { + var rom = new Data.Models.Metadata.Rom + { + BLAKE3 = item.Hash, + Name = item.File, + }; + return rom; + } } } diff --git a/SabreTools.Serialization.CrossModel/Logiqx.Deserializer.cs b/SabreTools.Serialization.CrossModel/Logiqx.Deserializer.cs index bbc3d7a0..d519e429 100644 --- a/SabreTools.Serialization.CrossModel/Logiqx.Deserializer.cs +++ b/SabreTools.Serialization.CrossModel/Logiqx.Deserializer.cs @@ -352,6 +352,7 @@ namespace SabreTools.Serialization.CrossModel SHA384 = item.SHA384, SHA512 = item.SHA512, SpamSum = item.SpamSum, + BLAKE3 = item.BLAKE3, xxHash364 = item.xxHash364, xxHash3128 = item.xxHash3128, Merge = item.Merge, diff --git a/SabreTools.Serialization.CrossModel/Logiqx.Serializer.cs b/SabreTools.Serialization.CrossModel/Logiqx.Serializer.cs index 669aab66..5fcad906 100644 --- a/SabreTools.Serialization.CrossModel/Logiqx.Serializer.cs +++ b/SabreTools.Serialization.CrossModel/Logiqx.Serializer.cs @@ -321,6 +321,7 @@ namespace SabreTools.Serialization.CrossModel SHA384 = item.SHA384, SHA512 = item.SHA512, SpamSum = item.SpamSum, + BLAKE3 = item.BLAKE3, xxHash364 = item.xxHash364, xxHash3128 = item.xxHash3128, Merge = item.Merge, diff --git a/SabreTools.Serialization.Readers.Test/ClrMameProTests.cs b/SabreTools.Serialization.Readers.Test/ClrMameProTests.cs index 3ac6ea0a..dda42561 100644 --- a/SabreTools.Serialization.Readers.Test/ClrMameProTests.cs +++ b/SabreTools.Serialization.Readers.Test/ClrMameProTests.cs @@ -232,6 +232,7 @@ namespace SabreTools.Serialization.Readers.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", xxHash364 = "xxhash364", xxHash3128 = "xxhash3128", Region = "region", @@ -495,6 +496,7 @@ namespace SabreTools.Serialization.Readers.Test Assert.Equal("sha384", rom.SHA384); Assert.Equal("sha512", rom.SHA512); Assert.Equal("spamsum", rom.SpamSum); + Assert.Equal("blake3", rom.BLAKE3); Assert.Equal("xxhash364", rom.xxHash364); Assert.Equal("xxhash3128", rom.xxHash3128); Assert.Equal("region", rom.Region); diff --git a/SabreTools.Serialization.Readers.Test/HashfileTests.cs b/SabreTools.Serialization.Readers.Test/HashfileTests.cs index 2e188feb..ba2b0857 100644 --- a/SabreTools.Serialization.Readers.Test/HashfileTests.cs +++ b/SabreTools.Serialization.Readers.Test/HashfileTests.cs @@ -287,6 +287,30 @@ namespace SabreTools.Serialization.Readers.Test Validate(newSpamsum); } + [Fact] + public void RoundTripBLAKE3Test() + { + // Get the serializer and deserializer + var deserializer = new Hashfile(); + var serializer = new Writers.Hashfile(); + + // Build the data + Data.Models.Hashfile.Hashfile hf = Build(HashType.BLAKE3); + + // Serialize to stream + Stream? actual = serializer.SerializeStream(hf, HashType.BLAKE3); + Assert.NotNull(actual); + + // Serialize back to original model + Data.Models.Hashfile.Hashfile? newHf = deserializer.Deserialize(actual, HashType.BLAKE3); + + // Validate the data + Assert.NotNull(newHf); + Assert.NotNull(newHf.BLAKE3); + var newBlake3 = Assert.Single(newHf.BLAKE3); + Validate(newBlake3); + } + /// /// Build model for serialization and deserialization /// @@ -310,6 +334,8 @@ namespace SabreTools.Serialization.Readers.Test return new Data.Models.Hashfile.Hashfile { SHA512 = [new Data.Models.Hashfile.SHA512 { Hash = "hash", File = "file" }] }; else if (hashType == HashType.SpamSum) return new Data.Models.Hashfile.Hashfile { SpamSum = [new Data.Models.Hashfile.SpamSum { Hash = "hash", File = "file" }] }; + else if (hashType == HashType.BLAKE3) + return new Data.Models.Hashfile.Hashfile { BLAKE3 = [new Data.Models.Hashfile.BLAKE3 { Hash = "hash", File = "file" }] }; else throw new ArgumentOutOfRangeException(nameof(hashType)); } @@ -403,5 +429,15 @@ namespace SabreTools.Serialization.Readers.Test Assert.Equal("hash", spamsum.Hash); Assert.Equal("file", spamsum.File); } + + /// + /// Validate a BLAKE3 + /// + private static void Validate(Data.Models.Hashfile.BLAKE3? blake3) + { + Assert.NotNull(blake3); + Assert.Equal("hash", blake3.Hash); + Assert.Equal("file", blake3.File); + } } } diff --git a/SabreTools.Serialization.Readers.Test/LogiqxTests.cs b/SabreTools.Serialization.Readers.Test/LogiqxTests.cs index d26b439f..bcce142b 100644 --- a/SabreTools.Serialization.Readers.Test/LogiqxTests.cs +++ b/SabreTools.Serialization.Readers.Test/LogiqxTests.cs @@ -236,6 +236,7 @@ namespace SabreTools.Serialization.Readers.Test SHA384 = "sha384", SHA512 = "sha512", SpamSum = "spamsum", + BLAKE3 = "blake3", xxHash364 = "xxhash364", xxHash3128 = "xxhash3128", Merge = "merge", @@ -552,6 +553,7 @@ namespace SabreTools.Serialization.Readers.Test Assert.Equal("sha384", rom.SHA384); Assert.Equal("sha512", rom.SHA512); Assert.Equal("spamsum", rom.SpamSum); + Assert.Equal("blake3", rom.BLAKE3); Assert.Equal("xxhash364", rom.xxHash364); Assert.Equal("xxhash3128", rom.xxHash3128); Assert.Equal("merge", rom.Merge); diff --git a/SabreTools.Serialization.Readers/ClrMamePro.cs b/SabreTools.Serialization.Readers/ClrMamePro.cs index ce60b091..11a01b52 100644 --- a/SabreTools.Serialization.Readers/ClrMamePro.cs +++ b/SabreTools.Serialization.Readers/ClrMamePro.cs @@ -568,6 +568,9 @@ namespace SabreTools.Serialization.Readers case "spamsum": rom.SpamSum = kvp.Value; break; + case "blake3": + rom.BLAKE3 = kvp.Value; + break; case "xxh3_64": rom.xxHash364 = kvp.Value; break; diff --git a/SabreTools.Serialization.Readers/Hashfile.cs b/SabreTools.Serialization.Readers/Hashfile.cs index fcc6f0b2..118a9822 100644 --- a/SabreTools.Serialization.Readers/Hashfile.cs +++ b/SabreTools.Serialization.Readers/Hashfile.cs @@ -91,6 +91,13 @@ namespace SabreTools.Serialization.Readers return DeserializeSHA512(data); else if (hash == HashType.SpamSum) return DeserializeSpamSum(data); +#if NET7_0_OR_GREATER + else if (hash == HashType.BLAKE3) +#else + // HACK because BLAKE3 is not supported below .NET 7 + else if (hash == HashType.CRC1_ZERO) +#endif + return DeserializeBLAKE3(data); else return null; } @@ -558,6 +565,50 @@ namespace SabreTools.Serialization.Readers } } + /// + public Data.Models.Hashfile.Hashfile? DeserializeBLAKE3(Stream? data) + { + // If the data is invalid + if (data is null || !data.CanRead) + return default; + + try + { + // Setup the reader and output + var reader = new StreamReader(data); + var blake3List = new List(); + + // Loop through the rows and parse out values + while (!reader.EndOfStream) + { + // Read and split the line + string? line = reader.ReadLine(); + string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries); + if (lineParts is null || lineParts.Length < 2) + continue; + + // Parse the line into a hash + var blake3 = new BLAKE3 + { + Hash = lineParts[0], + File = string.Join(" ", lineParts, 1, lineParts.Length - 1), + }; + blake3List.Add(blake3); + } + + // Assign the hashes to the hashfile and return + if (blake3List.Count > 0) + return new Data.Models.Hashfile.Hashfile { BLAKE3 = [.. blake3List] }; + + return null; + } + catch + { + // Ignore the actual error + return null; + } + } + #endregion } } diff --git a/SabreTools.Serialization.Readers/Logiqx.cs b/SabreTools.Serialization.Readers/Logiqx.cs index 8c50d42e..f1924f8d 100644 --- a/SabreTools.Serialization.Readers/Logiqx.cs +++ b/SabreTools.Serialization.Readers/Logiqx.cs @@ -788,6 +788,7 @@ namespace SabreTools.Serialization.Readers obj.SHA384 = reader.GetAttribute("sha384"); obj.SHA512 = reader.GetAttribute("sha512"); obj.SpamSum = reader.GetAttribute("spamsum"); + obj.BLAKE3 = reader.GetAttribute("blake3"); obj.xxHash364 = reader.GetAttribute("xxh3_64"); obj.xxHash3128 = reader.GetAttribute("xxh3_128"); obj.Merge = reader.GetAttribute("merge"); diff --git a/SabreTools.Serialization.Writers/ClrMamePro.cs b/SabreTools.Serialization.Writers/ClrMamePro.cs index 7bdce944..3f181ce9 100644 --- a/SabreTools.Serialization.Writers/ClrMamePro.cs +++ b/SabreTools.Serialization.Writers/ClrMamePro.cs @@ -275,6 +275,7 @@ namespace SabreTools.Serialization.Writers writer.WriteOptionalAttributeString("sha384", rom.SHA384); writer.WriteOptionalAttributeString("sha512", rom.SHA512); writer.WriteOptionalAttributeString("spamsum", rom.SpamSum); + writer.WriteOptionalAttributeString("blake3", rom.BLAKE3); writer.WriteOptionalAttributeString("xxh3_64", rom.xxHash364); writer.WriteOptionalAttributeString("xxh3_128", rom.xxHash3128); writer.WriteOptionalAttributeString("merge", rom.Merge); diff --git a/SabreTools.Serialization.Writers/Hashfile.cs b/SabreTools.Serialization.Writers/Hashfile.cs index e1b83070..67021209 100644 --- a/SabreTools.Serialization.Writers/Hashfile.cs +++ b/SabreTools.Serialization.Writers/Hashfile.cs @@ -101,6 +101,13 @@ namespace SabreTools.Serialization.Writers WriteSHA512(obj.SHA512, writer); else if (hash == HashType.SpamSum) WriteSpamSum(obj.SpamSum, writer); +#if NET7_0_OR_GREATER + else if (hash == HashType.BLAKE3) +#else + // HACK because BLAKE3 is not supported below .NET 7 + else if (hash == HashType.CRC1_ZERO) +#endif + WriteBLAKE3(obj.BLAKE3, writer); else throw new ArgumentOutOfRangeException(nameof(hash)); @@ -351,6 +358,28 @@ namespace SabreTools.Serialization.Writers } } + /// + /// Write BLAKE3 information to the current writer + /// + /// Array of BLAKE3 objects representing the files + /// Writer representing the output + private static void WriteBLAKE3(BLAKE3[]? blake3s, Writer writer) + { + // If the item information is missing, we can't do anything + if (blake3s is null || blake3s.Length == 0) + return; + + // Loop through and write out the items + foreach (var blake3 in blake3s) + { + if (string.IsNullOrEmpty(blake3.Hash) || string.IsNullOrEmpty(blake3.File)) + continue; + + writer.WriteValues([blake3.Hash!, blake3.File!]); + writer.Flush(); + } + } + #endregion } } diff --git a/SabreTools.Serialization.Writers/Logiqx.cs b/SabreTools.Serialization.Writers/Logiqx.cs index db0dfdf1..fcd9d6cc 100644 --- a/SabreTools.Serialization.Writers/Logiqx.cs +++ b/SabreTools.Serialization.Writers/Logiqx.cs @@ -528,6 +528,7 @@ namespace SabreTools.Serialization.Writers writer.WriteOptionalAttributeString("sha384", obj.SHA384); writer.WriteOptionalAttributeString("sha512", obj.SHA512); writer.WriteOptionalAttributeString("spamsum", obj.SpamSum); + writer.WriteOptionalAttributeString("blake3", obj.BLAKE3); writer.WriteOptionalAttributeString("xxh3_64", obj.xxHash364); writer.WriteOptionalAttributeString("xxh3_128", obj.xxHash3128); writer.WriteOptionalAttributeString("merge", obj.Merge);