diff --git a/SabreTools.Data.Extensions.Test/MetadataExtensions.cs b/SabreTools.Data.Extensions.Test/MetadataExtensions.cs
new file mode 100644
index 00000000..f4e0483c
--- /dev/null
+++ b/SabreTools.Data.Extensions.Test/MetadataExtensions.cs
@@ -0,0 +1,1337 @@
+using SabreTools.Data.Models.Metadata;
+using SabreTools.Hashing;
+using Xunit;
+
+namespace SabreTools.Data.Extensions.Test
+{
+ public class MetadataExtensionsTests
+ {
+ #region ConvertToRom
+
+ [Fact]
+ public void ConvertToRom_Null_Null()
+ {
+ DictionaryBase? self = null;
+ Rom? actual = self.ConvertToRom();
+ Assert.Null(actual);
+ }
+
+ [Fact]
+ public void ConvertToRom_EmptyDisk_EmptyRom()
+ {
+ DictionaryBase? self = new Disk();
+ Rom? actual = self.ConvertToRom();
+
+ Assert.NotNull(actual);
+ Assert.Equal(8, actual.Count);
+ Assert.Equal(ItemType.Rom, actual["_type"]);
+ Assert.Null(actual[Rom.NameKey]);
+ Assert.Null(actual[Rom.MergeKey]);
+ Assert.Null(actual[Rom.RegionKey]);
+ Assert.Null(actual[Rom.StatusKey]);
+ Assert.Null(actual[Rom.OptionalKey]);
+ Assert.Null(actual[Rom.MD5Key]);
+ Assert.Null(actual[Rom.SHA1Key]);
+ }
+
+ [Fact]
+ public void ConvertToRom_FilledDisk_FilledRom()
+ {
+ DictionaryBase? self = new Disk
+ {
+ [Disk.NameKey] = "XXXXXX",
+ [Disk.MergeKey] = "XXXXXX",
+ [Disk.RegionKey] = "XXXXXX",
+ [Disk.StatusKey] = "XXXXXX",
+ [Disk.OptionalKey] = "XXXXXX",
+ [Disk.MD5Key] = "XXXXXX",
+ [Disk.SHA1Key] = "XXXXXX",
+ };
+
+ Rom? actual = self.ConvertToRom();
+
+ Assert.NotNull(actual);
+ Assert.Equal(8, actual.Count);
+ Assert.Equal(ItemType.Rom, actual["_type"]);
+ Assert.Equal("XXXXXX.chd", actual[Rom.NameKey]);
+ Assert.Equal("XXXXXX", actual[Rom.MergeKey]);
+ Assert.Equal("XXXXXX", actual[Rom.RegionKey]);
+ Assert.Equal("XXXXXX", actual[Rom.StatusKey]);
+ Assert.Equal("XXXXXX", actual[Rom.OptionalKey]);
+ Assert.Equal("XXXXXX", actual[Rom.MD5Key]);
+ Assert.Equal("XXXXXX", actual[Rom.SHA1Key]);
+ }
+
+ [Fact]
+ public void ConvertToRom_EmptyMedia_EmptyRom()
+ {
+ DictionaryBase? self = new Media();
+ Rom? actual = self.ConvertToRom();
+
+ Assert.NotNull(actual);
+ Assert.Equal(6, actual.Count);
+ Assert.Equal(ItemType.Rom, actual["_type"]);
+ Assert.Null(actual[Rom.NameKey]);
+ Assert.Null(actual[Rom.MD5Key]);
+ Assert.Null(actual[Rom.SHA1Key]);
+ Assert.Null(actual[Rom.SHA256Key]);
+ Assert.Null(actual[Rom.SpamSumKey]);
+ }
+
+ [Fact]
+ public void ConvertToRom_FilledMedia_FilledRom()
+ {
+ DictionaryBase? self = new Media
+ {
+ [Media.NameKey] = "XXXXXX",
+ [Media.MD5Key] = "XXXXXX",
+ [Media.SHA1Key] = "XXXXXX",
+ [Media.SHA256Key] = "XXXXXX",
+ [Media.SpamSumKey] = "XXXXXX",
+ };
+
+ Rom? actual = self.ConvertToRom();
+
+ Assert.NotNull(actual);
+ Assert.Equal(6, actual.Count);
+ Assert.Equal(ItemType.Rom, actual["_type"]);
+ Assert.Equal("XXXXXX.aaruf", actual[Rom.NameKey]);
+ Assert.Equal("XXXXXX", actual[Rom.MD5Key]);
+ Assert.Equal("XXXXXX", actual[Rom.SHA1Key]);
+ Assert.Equal("XXXXXX", actual[Rom.SHA256Key]);
+ Assert.Equal("XXXXXX", actual[Rom.SpamSumKey]);
+ }
+
+ [Fact]
+ public void ConvertToRom_Other_Null()
+ {
+ DictionaryBase? self = new Sample();
+ Rom? actual = self.ConvertToRom();
+ Assert.Null(actual);
+ }
+
+ #endregion
+
+ #region HashMatch
+
+ [Fact]
+ public void HashMatch_Disk_Mismatch_False()
+ {
+ Disk self = new Disk
+ {
+ [Disk.MD5Key] = "XXXXXX",
+ [Disk.SHA1Key] = string.Empty,
+ };
+ Disk other = new Disk
+ {
+ [Disk.MD5Key] = string.Empty,
+ [Disk.SHA1Key] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.False(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Disk_PartialMD5_True()
+ {
+ Disk self = new Disk
+ {
+ [Disk.MD5Key] = "XXXXXX",
+ [Disk.SHA1Key] = string.Empty,
+ };
+ Disk other = new Disk
+ {
+ [Disk.MD5Key] = "XXXXXX",
+ [Disk.SHA1Key] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Disk_PartialSHA1_True()
+ {
+ Disk self = new Disk
+ {
+ [Disk.MD5Key] = string.Empty,
+ [Disk.SHA1Key] = "XXXXXX",
+ };
+ Disk other = new Disk
+ {
+ [Disk.MD5Key] = "XXXXXX",
+ [Disk.SHA1Key] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Disk_FullMatch_True()
+ {
+ Disk self = new Disk
+ {
+ [Disk.MD5Key] = "XXXXXX",
+ [Disk.SHA1Key] = "XXXXXX",
+ };
+ Disk other = new Disk
+ {
+ [Disk.MD5Key] = "XXXXXX",
+ [Disk.SHA1Key] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Media_Mismatch_False()
+ {
+ Media self = new Media
+ {
+ [Media.MD5Key] = "XXXXXX",
+ [Media.SHA1Key] = string.Empty,
+ [Media.SHA256Key] = "XXXXXX",
+ [Media.SpamSumKey] = string.Empty,
+ };
+ Media other = new Media
+ {
+ [Media.MD5Key] = string.Empty,
+ [Media.SHA1Key] = "XXXXXX",
+ [Media.SHA256Key] = string.Empty,
+ [Media.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.False(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Media_PartialMD5_True()
+ {
+ Media self = new Media
+ {
+ [Media.MD5Key] = "XXXXXX",
+ [Media.SHA1Key] = string.Empty,
+ [Media.SHA256Key] = string.Empty,
+ [Media.SpamSumKey] = string.Empty,
+ };
+ Media other = new Media
+ {
+ [Media.MD5Key] = "XXXXXX",
+ [Media.SHA1Key] = "XXXXXX",
+ [Media.SHA256Key] = "XXXXXX",
+ [Media.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Media_PartialSHA1_True()
+ {
+ Media self = new Media
+ {
+ [Media.MD5Key] = string.Empty,
+ [Media.SHA1Key] = "XXXXXX",
+ [Media.SHA256Key] = string.Empty,
+ [Media.SpamSumKey] = string.Empty,
+ };
+ Media other = new Media
+ {
+ [Media.MD5Key] = "XXXXXX",
+ [Media.SHA1Key] = "XXXXXX",
+ [Media.SHA256Key] = "XXXXXX",
+ [Media.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Media_PartialSHA256_True()
+ {
+ Media self = new Media
+ {
+ [Media.MD5Key] = string.Empty,
+ [Media.SHA1Key] = string.Empty,
+ [Media.SHA256Key] = "XXXXXX",
+ [Media.SpamSumKey] = string.Empty,
+ };
+ Media other = new Media
+ {
+ [Media.MD5Key] = "XXXXXX",
+ [Media.SHA1Key] = "XXXXXX",
+ [Media.SHA256Key] = "XXXXXX",
+ [Media.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Media_PartialSpamSum_True()
+ {
+ Media self = new Media
+ {
+ [Media.MD5Key] = string.Empty,
+ [Media.SHA1Key] = string.Empty,
+ [Media.SHA256Key] = string.Empty,
+ [Media.SpamSumKey] = "XXXXXX",
+ };
+ Media other = new Media
+ {
+ [Media.MD5Key] = "XXXXXX",
+ [Media.SHA1Key] = "XXXXXX",
+ [Media.SHA256Key] = "XXXXXX",
+ [Media.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Media_FullMatch_True()
+ {
+ Media self = new Media
+ {
+ [Media.MD5Key] = "XXXXXX",
+ [Media.SHA1Key] = "XXXXXX",
+ [Media.SHA256Key] = "XXXXXX",
+ [Media.SpamSumKey] = "XXXXXX",
+ };
+ Media other = new Media
+ {
+ [Media.MD5Key] = "XXXXXX",
+ [Media.SHA1Key] = "XXXXXX",
+ [Media.SHA256Key] = "XXXXXX",
+ [Media.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Rom_Mismatch_False()
+ {
+ Rom self = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+ Rom other = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.False(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Rom_PartialCRC_True()
+ {
+ Rom self = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+ Rom other = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Rom_PartialMD2_True()
+ {
+ Rom self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+ Rom other = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Rom_PartialMD4_True()
+ {
+ Rom self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+ Rom other = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Rom_PartialMD5_True()
+ {
+ Rom self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+ Rom other = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Rom_PartialRIPEMD128_True()
+ {
+ Rom self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+ Rom other = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Rom_PartialRIPEMD160_True()
+ {
+ Rom self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+ Rom other = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Rom_PartialSHA1_True()
+ {
+ Rom self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+ Rom other = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Rom_PartialSHA256_True()
+ {
+ Rom self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+ Rom other = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Rom_PartialSHA384_True()
+ {
+ Rom self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+ Rom other = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Rom_PartialSHA512_True()
+ {
+ Rom self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = string.Empty,
+ };
+ Rom other = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Rom_PartialSpamSum_True()
+ {
+ Rom self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+ Rom other = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HashMatch_Rom_FullMatch_True()
+ {
+ Rom self = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+ Rom other = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HashMatch(other);
+ Assert.True(actual);
+ }
+
+ #endregion
+
+ #region HasZeroHash
+
+ [Fact]
+ public void HasZeroHash_Disk_NoHash_True()
+ {
+ DictionaryBase self = new Disk();
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Disk_NonZeroHash_False()
+ {
+ DictionaryBase self = new Disk
+ {
+ [Disk.MD5Key] = "XXXXXX",
+ [Disk.SHA1Key] = "XXXXXX",
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.False(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Disk_ZeroMD5_True()
+ {
+ DictionaryBase self = new Disk
+ {
+ [Disk.MD5Key] = ZeroHash.MD5Str,
+ [Disk.SHA1Key] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Disk_ZeroSHA1_True()
+ {
+ DictionaryBase self = new Disk
+ {
+ [Disk.MD5Key] = string.Empty,
+ [Disk.SHA1Key] = ZeroHash.SHA1Str,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Disk_ZeroAll_True()
+ {
+ DictionaryBase self = new Disk
+ {
+ [Disk.MD5Key] = ZeroHash.MD5Str,
+ [Disk.SHA1Key] = ZeroHash.SHA1Str,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Media_NoHash_True()
+ {
+ DictionaryBase self = new Media();
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Media_NonZeroHash_False()
+ {
+ DictionaryBase self = new Media
+ {
+ [Media.MD5Key] = "XXXXXX",
+ [Media.SHA1Key] = "XXXXXX",
+ [Media.SHA256Key] = "XXXXXX",
+ [Media.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.False(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Media_ZeroMD5_True()
+ {
+ DictionaryBase self = new Media
+ {
+ [Media.MD5Key] = ZeroHash.MD5Str,
+ [Media.SHA1Key] = string.Empty,
+ [Media.SHA256Key] = string.Empty,
+ [Media.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Media_ZeroSHA1_True()
+ {
+ DictionaryBase self = new Media
+ {
+ [Media.MD5Key] = string.Empty,
+ [Media.SHA1Key] = ZeroHash.SHA1Str,
+ [Media.SHA256Key] = string.Empty,
+ [Media.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Media_ZeroSHA256_True()
+ {
+ DictionaryBase self = new Media
+ {
+ [Media.MD5Key] = string.Empty,
+ [Media.SHA1Key] = string.Empty,
+ [Media.SHA256Key] = ZeroHash.SHA256Str,
+ [Media.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Media_ZeroSpamSum_True()
+ {
+ DictionaryBase self = new Media
+ {
+ [Media.MD5Key] = string.Empty,
+ [Media.SHA1Key] = string.Empty,
+ [Media.SHA256Key] = string.Empty,
+ [Media.SpamSumKey] = ZeroHash.SpamSumStr,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Media_ZeroAll_True()
+ {
+ DictionaryBase self = new Media
+ {
+ [Media.MD5Key] = ZeroHash.MD5Str,
+ [Media.SHA1Key] = ZeroHash.SHA1Str,
+ [Media.SHA256Key] = ZeroHash.SHA256Str,
+ [Media.SpamSumKey] = ZeroHash.SpamSumStr,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_NoHash_True()
+ {
+ DictionaryBase self = new Rom();
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_NonZeroHash_False()
+ {
+ DictionaryBase self = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.False(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_ZeroCRC_True()
+ {
+ DictionaryBase self = new Rom
+ {
+ [Rom.CRCKey] = ZeroHash.CRC32Str,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_ZeroMD2_True()
+ {
+ DictionaryBase self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = ZeroHash.GetString(HashType.MD2),
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_ZeroMD4_True()
+ {
+ DictionaryBase self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = ZeroHash.GetString(HashType.MD4),
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_ZeroMD5_True()
+ {
+ DictionaryBase self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = ZeroHash.MD5Str,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_ZeroRIPEMD128_True()
+ {
+ DictionaryBase self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = ZeroHash.GetString(HashType.RIPEMD128),
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_ZeroRIPEMD160_True()
+ {
+ DictionaryBase self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = ZeroHash.GetString(HashType.RIPEMD160),
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_ZeroSHA1_True()
+ {
+ DictionaryBase self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = ZeroHash.SHA1Str,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_ZeroSHA256_True()
+ {
+ DictionaryBase self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = ZeroHash.SHA256Str,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_ZeroSHA384_True()
+ {
+ DictionaryBase self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = ZeroHash.SHA384Str,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_ZeroSHA512_True()
+ {
+ DictionaryBase self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = ZeroHash.SHA512Str,
+ [Rom.SpamSumKey] = string.Empty,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_ZeroSpamSum_True()
+ {
+ DictionaryBase self = new Rom
+ {
+ [Rom.CRCKey] = string.Empty,
+ [Rom.MD2Key] = string.Empty,
+ [Rom.MD4Key] = string.Empty,
+ [Rom.MD5Key] = string.Empty,
+ [Rom.RIPEMD128Key] = string.Empty,
+ [Rom.RIPEMD160Key] = string.Empty,
+ [Rom.SHA1Key] = string.Empty,
+ [Rom.SHA256Key] = string.Empty,
+ [Rom.SHA384Key] = string.Empty,
+ [Rom.SHA512Key] = string.Empty,
+ [Rom.SpamSumKey] = ZeroHash.SpamSumStr,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Rom_ZeroAll_True()
+ {
+ DictionaryBase self = new Rom
+ {
+ [Rom.CRCKey] = ZeroHash.CRC32Str,
+ [Rom.MD2Key] = ZeroHash.GetString(HashType.MD2),
+ [Rom.MD4Key] = ZeroHash.GetString(HashType.MD4),
+ [Rom.MD5Key] = ZeroHash.MD5Str,
+ [Rom.RIPEMD128Key] = ZeroHash.GetString(HashType.RIPEMD128),
+ [Rom.RIPEMD160Key] = ZeroHash.GetString(HashType.RIPEMD160),
+ [Rom.SHA1Key] = ZeroHash.SHA1Str,
+ [Rom.SHA256Key] = ZeroHash.SHA256Str,
+ [Rom.SHA384Key] = ZeroHash.SHA384Str,
+ [Rom.SHA512Key] = ZeroHash.SHA512Str,
+ [Rom.SpamSumKey] = ZeroHash.SpamSumStr,
+ };
+
+ bool actual = self.HasZeroHash();
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void HasZeroHash_Other_False()
+ {
+ DictionaryBase self = new Sample();
+ bool actual = self.HasZeroHash();
+ Assert.False(actual);
+ }
+
+ #endregion
+
+ #region FillMissingHashes
+
+ [Fact]
+ public void FillMissingHashes_Disk_BothEmpty()
+ {
+ DictionaryBase self = new Disk();
+ DictionaryBase other = new Disk();
+
+ self.FillMissingHashes(other);
+ Assert.Single(self);
+ }
+
+ [Fact]
+ public void FillMissingHashes_Disk_AllMissing()
+ {
+ DictionaryBase self = new Disk();
+ DictionaryBase other = new Disk
+ {
+ [Disk.MD5Key] = "XXXXXX",
+ [Disk.SHA1Key] = "XXXXXX",
+ };
+
+ self.FillMissingHashes(other);
+ }
+
+ [Fact]
+ public void FillMissingHashes_Media_BothEmpty()
+ {
+ DictionaryBase self = new Media();
+ DictionaryBase other = new Media();
+ self.FillMissingHashes(other);
+ Assert.Single(self);
+ }
+
+ [Fact]
+ public void FillMissingHashes_Media_AllMissing()
+ {
+ DictionaryBase self = new Media();
+ DictionaryBase other = new Media
+ {
+ [Media.MD5Key] = "XXXXXX",
+ [Media.SHA1Key] = "XXXXXX",
+ [Media.SHA256Key] = "XXXXXX",
+ [Media.SpamSumKey] = "XXXXXX",
+ };
+
+ self.FillMissingHashes(other);
+ }
+
+ [Fact]
+ public void FillMissingHashes_Rom_BothEmpty()
+ {
+ DictionaryBase self = new Rom();
+ DictionaryBase other = new Rom();
+ self.FillMissingHashes(other);
+ Assert.Single(self);
+ }
+
+ [Fact]
+ public void FillMissingHashes_Rom_AllMissing()
+ {
+ DictionaryBase self = new Rom();
+ DictionaryBase other = new Rom
+ {
+ [Rom.CRCKey] = "XXXXXX",
+ [Rom.MD2Key] = "XXXXXX",
+ [Rom.MD4Key] = "XXXXXX",
+ [Rom.MD5Key] = "XXXXXX",
+ [Rom.RIPEMD128Key] = "XXXXXX",
+ [Rom.RIPEMD160Key] = "XXXXXX",
+ [Rom.SHA1Key] = "XXXXXX",
+ [Rom.SHA256Key] = "XXXXXX",
+ [Rom.SHA384Key] = "XXXXXX",
+ [Rom.SHA512Key] = "XXXXXX",
+ [Rom.SpamSumKey] = "XXXXXX",
+ };
+
+ self.FillMissingHashes(other);
+ }
+
+ #endregion
+ }
+}
diff --git a/SabreTools.Data.Extensions/MetadataExtensions.cs b/SabreTools.Data.Extensions/MetadataExtensions.cs
new file mode 100644
index 00000000..10f30948
--- /dev/null
+++ b/SabreTools.Data.Extensions/MetadataExtensions.cs
@@ -0,0 +1,842 @@
+using System;
+using SabreTools.Data.Models.Metadata;
+using SabreTools.Hashing;
+using SabreTools.IO.Extensions;
+
+namespace SabreTools.Data.Extensions
+{
+ public static class MetadataExtensions
+ {
+ #region Accessors
+
+ ///
+ /// Gets the name to use for a DictionaryBase or null
+ ///
+ public static string? GetName(this DictionaryBase self)
+ {
+ if (self is null)
+ return null;
+
+ return self switch
+ {
+ Machine => self.ReadString(Machine.NameKey),
+
+ Adjuster => self.ReadString(Adjuster.NameKey),
+ Analog => null,
+ Archive => self.ReadString(Archive.NameKey),
+ BiosSet => self.ReadString(BiosSet.NameKey),
+ Chip => self.ReadString(Chip.NameKey),
+ Condition => null,
+ ConfLocation => self.ReadString(ConfLocation.NameKey),
+ ConfSetting => self.ReadString(ConfSetting.NameKey),
+ Configuration => self.ReadString(Configuration.NameKey),
+ Control => null,
+ DataArea => self.ReadString(DataArea.NameKey),
+ Device => null,
+ DeviceRef => self.ReadString(DeviceRef.NameKey),
+ DipLocation => self.ReadString(DipLocation.NameKey),
+ DipSwitch => self.ReadString(DipSwitch.NameKey),
+ DipValue => self.ReadString(DipValue.NameKey),
+ Disk => self.ReadString(Disk.NameKey),
+ DiskArea => self.ReadString(DiskArea.NameKey),
+ Display => null,
+ Driver => null,
+ Extension => self.ReadString(Extension.NameKey),
+ Feature => self.ReadString(Feature.NameKey),
+ Info => self.ReadString(Info.NameKey),
+ Input => null,
+ Instance => self.ReadString(Instance.NameKey),
+ Media => self.ReadString(Media.NameKey),
+ Part => self.ReadString(Part.NameKey),
+ Port => null,
+ RamOption => self.ReadString(RamOption.NameKey),
+ Release => self.ReadString(Release.NameKey),
+ Rom => self.ReadString(Rom.NameKey),
+ Sample => self.ReadString(Sample.NameKey),
+ SharedFeat => self.ReadString(SharedFeat.NameKey),
+ Slot => self.ReadString(Slot.NameKey),
+ SlotOption => self.ReadString(SlotOption.NameKey),
+ SoftwareList => self.ReadString(SoftwareList.NameKey),
+ Sound => null,
+
+ _ => null,
+ };
+ }
+
+ ///
+ /// Gets the name to use for a DictionaryBase or null
+ ///
+ public static void SetName(this DictionaryBase self, string? name)
+ {
+ if (self is null || string.IsNullOrEmpty(name))
+ return;
+
+ switch (self)
+ {
+ case Machine: self[Machine.NameKey] = name; break;
+
+ case Adjuster: self[Adjuster.NameKey] = name; break;
+ case Analog: break;
+ case Archive: self[Archive.NameKey] = name; break;
+ case BiosSet: self[BiosSet.NameKey] = name; break;
+ case Chip: self[Chip.NameKey] = name; break;
+ case Condition: break;
+ case ConfLocation: self[ConfLocation.NameKey] = name; break;
+ case ConfSetting: self[ConfSetting.NameKey] = name; break;
+ case Configuration: self[Configuration.NameKey] = name; break;
+ case Control: break;
+ case DataArea: self[DataArea.NameKey] = name; break;
+ case Device: break;
+ case DeviceRef: self[DeviceRef.NameKey] = name; break;
+ case DipLocation: self[DipLocation.NameKey] = name; break;
+ case DipSwitch: self[DipSwitch.NameKey] = name; break;
+ case DipValue: self[DipValue.NameKey] = name; break;
+ case Disk: self[Disk.NameKey] = name; break;
+ case DiskArea: self[DiskArea.NameKey] = name; break;
+ case Display: break;
+ case Driver: break;
+ case Extension: self[Extension.NameKey] = name; break;
+ case Feature: self[Feature.NameKey] = name; break;
+ case Info: self[Info.NameKey] = name; break;
+ case Input: break;
+ case Instance: self[Instance.NameKey] = name; break;
+ case Media: self[Media.NameKey] = name; break;
+ case Part: self[Part.NameKey] = name; break;
+ case Port: break;
+ case RamOption: self[RamOption.NameKey] = name; break;
+ case Release: self[Release.NameKey] = name; break;
+ case Rom: self[Rom.NameKey] = name; break;
+ case Sample: self[Sample.NameKey] = name; break;
+ case SharedFeat: self[SharedFeat.NameKey] = name; break;
+ case Slot: self[Slot.NameKey] = name; break;
+ case SlotOption: self[SlotOption.NameKey] = name; break;
+ case SoftwareList: self[SoftwareList.NameKey] = name; break;
+ case Sound: break;
+
+ default: break;
+ }
+ }
+
+ #endregion
+
+ #region Cloning
+
+ ///
+ /// Deep clone a DictionaryBase object
+ ///
+ public static DictionaryBase? Clone(this DictionaryBase self)
+ {
+ // If construction failed, we can't do anything
+ if (Activator.CreateInstance(self.GetType()) is not DictionaryBase clone)
+ return null;
+
+ // Loop through and clone per type
+ foreach (string key in self.Keys)
+ {
+ object? value = self[key];
+ clone[key] = value switch
+ {
+ // Primative types
+ bool or long or double or string => value,
+
+ // DictionaryBase types
+ DictionaryBase db => db.Clone(),
+
+ // Enumerable types
+ byte[] bytArr => bytArr.Clone(),
+ string[] strArr => strArr.Clone(),
+ DictionaryBase[] dbArr => Array.ConvertAll(dbArr, Clone),
+ ICloneable[] clArr => Array.ConvertAll(clArr, cl => cl.Clone()),
+
+ // Everything else just copies
+ _ => value,
+ };
+ }
+
+ return clone;
+ }
+
+ #endregion
+
+ #region Conversion
+
+ ///
+ /// Convert a DictionaryBase to a Rom
+ ///
+ public static Rom? ConvertToRom(this DictionaryBase? self)
+ {
+ // If the DatItem is missing, we can't do anything
+ if (self is null)
+ return null;
+
+ return self switch
+ {
+ Disk diskSelf => ConvertToRom(diskSelf),
+ Media mediaSelf => ConvertToRom(mediaSelf),
+ _ => null,
+ };
+ }
+
+ ///
+ /// Convert a Disk to a Rom
+ ///
+ private static Rom? ConvertToRom(this Disk? disk)
+ {
+ // If the Disk is missing, we can't do anything
+ if (disk is null)
+ return null;
+
+ // Append a suffix to the name
+ string? name = disk.ReadString(Disk.NameKey);
+ if (name is not null)
+ name += ".chd";
+
+ return new Rom
+ {
+ [Rom.NameKey] = name,
+ [Rom.MergeKey] = disk.ReadString(Disk.MergeKey),
+ [Rom.RegionKey] = disk.ReadString(Disk.RegionKey),
+ [Rom.StatusKey] = disk.ReadString(Disk.StatusKey),
+ [Rom.OptionalKey] = disk.ReadString(Disk.OptionalKey),
+ [Rom.MD5Key] = disk.ReadString(Disk.MD5Key),
+ [Rom.SHA1Key] = disk.ReadString(Disk.SHA1Key),
+ };
+ }
+
+ ///
+ /// Convert a Media to a Rom
+ ///
+ private static Rom? ConvertToRom(this Media? media)
+ {
+ // If the Media is missing, we can't do anything
+ if (media is null)
+ return null;
+
+ // Append a suffix to the name
+ string? name = media.ReadString(Media.NameKey);
+ if (name is not null)
+ name += ".aaruf";
+
+ return new Rom
+ {
+ [Rom.NameKey] = name,
+ [Rom.MD5Key] = media.ReadString(Media.MD5Key),
+ [Rom.SHA1Key] = media.ReadString(Media.SHA1Key),
+ [Rom.SHA256Key] = media.ReadString(Media.SHA256Key),
+ [Rom.SpamSumKey] = media.ReadString(Media.SpamSumKey),
+ };
+ }
+
+ #endregion
+
+ #region Hash Checking
+
+ ///
+ /// Determine if two hashes are equal for the purposes of merging
+ ///
+ public static bool ConditionalHashEquals(byte[]? firstHash, byte[]? secondHash)
+ {
+ // If either hash is empty, we say they're equal for merging
+ if (firstHash.IsNullOrEmpty() || secondHash.IsNullOrEmpty())
+ return true;
+
+ // If they're different sizes, they can't match
+ if (firstHash!.Length != secondHash!.Length)
+ return false;
+
+ // Otherwise, they need to match exactly
+ return firstHash.EqualsExactly(secondHash);
+ }
+
+ ///
+ /// Determine if two hashes are equal for the purposes of merging
+ ///
+ public static bool ConditionalHashEquals(string? firstHash, string? secondHash)
+ {
+ // If either hash is empty, we say they're equal for merging
+ if (string.IsNullOrEmpty(firstHash) || string.IsNullOrEmpty(secondHash))
+ return true;
+
+ // If they're different sizes, they can't match
+ if (firstHash!.Length != secondHash!.Length)
+ return false;
+
+ // Otherwise, they need to match exactly
+ return string.Equals(firstHash, secondHash, StringComparison.OrdinalIgnoreCase);
+ }
+
+ ///
+ /// Returns if any hashes are common
+ ///
+ public static bool HashMatch(this Disk self, Disk other)
+ {
+ // If either have no hashes, we return false, otherwise this would be a false positive
+ if (!self.HasHashes() || !other.HasHashes())
+ return false;
+
+ // If neither have hashes in common, we return false, otherwise this would be a false positive
+ if (!self.HasCommonHash(other))
+ return false;
+
+ // Return if all hashes match according to merge rules
+ string? selfMd5 = self.ReadString(Disk.MD5Key);
+ string? otherMd5 = other.ReadString(Disk.MD5Key);
+ bool conditionalMd5 = ConditionalHashEquals(selfMd5, otherMd5);
+
+ string? selfSha1 = self.ReadString(Disk.SHA1Key);
+ string? otherSha1 = other.ReadString(Disk.SHA1Key);
+ bool conditionalSha1 = ConditionalHashEquals(selfSha1, otherSha1);
+
+ return conditionalMd5
+ && conditionalSha1;
+ }
+
+ ///
+ /// Returns if any hashes are common
+ ///
+ public static bool HashMatch(this Media self, Media other)
+ {
+ // If either have no hashes, we return false, otherwise this would be a false positive
+ if (!self.HasHashes() || !other.HasHashes())
+ return false;
+
+ // If neither have hashes in common, we return false, otherwise this would be a false positive
+ if (!self.HasCommonHash(other))
+ return false;
+
+ // Return if all hashes match according to merge rules
+ string? selfMd5 = self.ReadString(Media.MD5Key);
+ string? otherMd5 = other.ReadString(Media.MD5Key);
+ bool conditionalMd5 = ConditionalHashEquals(selfMd5, otherMd5);
+
+ string? selfSha1 = self.ReadString(Media.SHA1Key);
+ string? otherSha1 = other.ReadString(Media.SHA1Key);
+ bool conditionalSha1 = ConditionalHashEquals(selfSha1, otherSha1);
+
+ string? selfSha256 = self.ReadString(Media.SHA256Key);
+ string? otherSha256 = other.ReadString(Media.SHA256Key);
+ bool conditionalSha256 = ConditionalHashEquals(selfSha256, otherSha256);
+
+ string? selfSpamSum = self.ReadString(Media.SpamSumKey);
+ string? otherSpamSum = other.ReadString(Media.SpamSumKey);
+ bool conditionalSpamSum = ConditionalHashEquals(selfSpamSum, otherSpamSum);
+
+ return conditionalMd5
+ && conditionalSha1
+ && conditionalSha256
+ && conditionalSpamSum;
+ }
+
+ ///
+ /// Returns if any hashes are common
+ ///
+ public static bool HashMatch(this Rom self, Rom other)
+ {
+ // If either have no hashes, we return false, otherwise this would be a false positive
+ if (!self.HasHashes() || !other.HasHashes())
+ return false;
+
+ // If neither have hashes in common, we return false, otherwise this would be a false positive
+ if (!self.HasCommonHash(other))
+ return false;
+
+ // Return if all hashes match according to merge rules
+ string? selfCrc = self.ReadString(Rom.CRCKey);
+ string? otherCrc = other.ReadString(Rom.CRCKey);
+ bool conditionalCrc = ConditionalHashEquals(selfCrc, otherCrc);
+
+ string? selfMd2 = self.ReadString(Rom.MD2Key);
+ string? otherMd2 = other.ReadString(Rom.MD2Key);
+ bool conditionalMd2 = ConditionalHashEquals(selfMd2, otherMd2);
+
+ string? selfMd4 = self.ReadString(Rom.MD4Key);
+ string? otherMd4 = other.ReadString(Rom.MD4Key);
+ bool conditionalMd4 = ConditionalHashEquals(selfMd4, otherMd4);
+
+ string? selfMd5 = self.ReadString(Rom.MD5Key);
+ string? otherMd5 = other.ReadString(Rom.MD5Key);
+ bool conditionalMd5 = ConditionalHashEquals(selfMd5, otherMd5);
+
+ string? selfRipeMD128 = self.ReadString(Rom.RIPEMD128Key);
+ string? otherRipeMD128 = other.ReadString(Rom.RIPEMD128Key);
+ bool conditionaRipeMD128 = ConditionalHashEquals(selfRipeMD128, otherRipeMD128);
+
+ string? selfRipeMD160 = self.ReadString(Rom.RIPEMD160Key);
+ string? otherRipeMD160 = other.ReadString(Rom.RIPEMD160Key);
+ bool conditionaRipeMD160 = ConditionalHashEquals(selfRipeMD160, otherRipeMD160);
+
+ string? selfSha1 = self.ReadString(Rom.SHA1Key);
+ string? otherSha1 = other.ReadString(Rom.SHA1Key);
+ bool conditionalSha1 = ConditionalHashEquals(selfSha1, otherSha1);
+
+ string? selfSha256 = self.ReadString(Rom.SHA256Key);
+ string? otherSha256 = other.ReadString(Rom.SHA256Key);
+ bool conditionalSha256 = ConditionalHashEquals(selfSha256, otherSha256);
+
+ string? selfSha384 = self.ReadString(Rom.SHA384Key);
+ string? otherSha384 = other.ReadString(Rom.SHA384Key);
+ bool conditionalSha384 = ConditionalHashEquals(selfSha384, otherSha384);
+
+ string? selfSha512 = self.ReadString(Rom.SHA512Key);
+ string? otherSha512 = other.ReadString(Rom.SHA512Key);
+ bool conditionalSha512 = ConditionalHashEquals(selfSha512, otherSha512);
+
+ string? selfSpamSum = self.ReadString(Rom.SpamSumKey);
+ string? otherSpamSum = other.ReadString(Rom.SpamSumKey);
+ bool conditionalSpamSum = ConditionalHashEquals(selfSpamSum, otherSpamSum);
+
+ return conditionalCrc
+ && conditionalMd2
+ && conditionalMd4
+ && conditionalMd5
+ && conditionaRipeMD128
+ && conditionaRipeMD160
+ && conditionalSha1
+ && conditionalSha256
+ && conditionalSha384
+ && conditionalSha512
+ && conditionalSpamSum;
+ }
+
+ ///
+ /// Returns if any hashes exist
+ ///
+ public static bool HasHashes(this DictionaryBase self)
+ {
+ return self switch
+ {
+ Disk diskSelf => diskSelf.HasHashes(),
+ Media mediaSelf => mediaSelf.HasHashes(),
+ Rom romSelf => romSelf.HasHashes(),
+ _ => false,
+ };
+ }
+
+ ///
+ /// Returns if all of the hashes are set to their 0-byte values or null
+ ///
+ public static bool HasZeroHash(this DictionaryBase self)
+ {
+ return self switch
+ {
+ Disk diskSelf => diskSelf.HasZeroHash(),
+ Media mediaSelf => mediaSelf.HasZeroHash(),
+ Rom romSelf => romSelf.HasZeroHash(),
+ _ => false,
+ };
+ }
+
+ ///
+ /// Returns if there are no, non-empty hashes in common
+ ///
+ private static bool HasCommonHash(this Disk self, Disk other)
+ {
+ bool md5Null = string.IsNullOrEmpty(self.ReadString(Disk.MD5Key));
+ md5Null ^= string.IsNullOrEmpty(other.ReadString(Disk.MD5Key));
+
+ bool sha1Null = string.IsNullOrEmpty(self.ReadString(Disk.SHA1Key));
+ sha1Null ^= string.IsNullOrEmpty(other.ReadString(Disk.SHA1Key));
+
+ return !md5Null
+ || !sha1Null;
+ }
+
+ ///
+ /// Returns if there are no, non-empty hashes in common
+ ///
+ private static bool HasCommonHash(this Media self, Media other)
+ {
+ bool md5Null = string.IsNullOrEmpty(self.ReadString(Media.MD5Key));
+ md5Null ^= string.IsNullOrEmpty(other.ReadString(Media.MD5Key));
+
+ bool sha1Null = string.IsNullOrEmpty(self.ReadString(Media.SHA1Key));
+ sha1Null ^= string.IsNullOrEmpty(other.ReadString(Media.SHA1Key));
+
+ bool sha256Null = string.IsNullOrEmpty(self.ReadString(Media.SHA256Key));
+ sha256Null ^= string.IsNullOrEmpty(other.ReadString(Media.SHA256Key));
+
+ bool spamsumNull = string.IsNullOrEmpty(self.ReadString(Media.SpamSumKey));
+ spamsumNull ^= string.IsNullOrEmpty(other.ReadString(Media.SpamSumKey));
+
+ return !md5Null
+ || !sha1Null
+ || !sha256Null
+ || !spamsumNull;
+ }
+
+ ///
+ /// Returns if there are no, non-empty hashes in common
+ ///
+ private static bool HasCommonHash(this Rom self, Rom other)
+ {
+ bool crcNull = string.IsNullOrEmpty(self.ReadString(Rom.CRCKey));
+ crcNull ^= string.IsNullOrEmpty(other.ReadString(Rom.CRCKey));
+
+ bool md2Null = string.IsNullOrEmpty(self.ReadString(Rom.MD2Key));
+ md2Null ^= string.IsNullOrEmpty(other.ReadString(Rom.MD2Key));
+
+ bool md4Null = string.IsNullOrEmpty(self.ReadString(Rom.MD4Key));
+ md4Null ^= string.IsNullOrEmpty(other.ReadString(Rom.MD4Key));
+
+ bool md5Null = string.IsNullOrEmpty(self.ReadString(Rom.MD5Key));
+ md5Null ^= string.IsNullOrEmpty(other.ReadString(Rom.MD5Key));
+
+ bool ripeMD128Null = string.IsNullOrEmpty(self.ReadString(Rom.RIPEMD128Key));
+ ripeMD128Null ^= string.IsNullOrEmpty(other.ReadString(Rom.RIPEMD128Key));
+
+ bool ripeMD160Null = string.IsNullOrEmpty(self.ReadString(Rom.RIPEMD160Key));
+ ripeMD160Null ^= string.IsNullOrEmpty(other.ReadString(Rom.RIPEMD160Key));
+
+ bool sha1Null = string.IsNullOrEmpty(self.ReadString(Rom.SHA1Key));
+ sha1Null ^= string.IsNullOrEmpty(other.ReadString(Rom.SHA1Key));
+
+ bool sha256Null = string.IsNullOrEmpty(self.ReadString(Rom.SHA256Key));
+ sha256Null ^= string.IsNullOrEmpty(other.ReadString(Rom.SHA256Key));
+
+ bool sha384Null = string.IsNullOrEmpty(self.ReadString(Rom.SHA384Key));
+ sha384Null ^= string.IsNullOrEmpty(other.ReadString(Rom.SHA384Key));
+
+ bool sha512Null = string.IsNullOrEmpty(self.ReadString(Rom.SHA512Key));
+ sha512Null ^= string.IsNullOrEmpty(other.ReadString(Rom.SHA512Key));
+
+ bool spamsumNull = string.IsNullOrEmpty(self.ReadString(Rom.SpamSumKey));
+ spamsumNull ^= string.IsNullOrEmpty(other.ReadString(Rom.SpamSumKey));
+
+ return !crcNull
+ || !md2Null
+ || !md4Null
+ || !md5Null
+ || !ripeMD128Null
+ || !ripeMD160Null
+ || !sha1Null
+ || !sha256Null
+ || !sha384Null
+ || !sha512Null
+ || !spamsumNull;
+ }
+
+ ///
+ /// Returns if any hashes exist
+ ///
+ private static bool HasHashes(this Disk disk)
+ {
+ bool md5Null = string.IsNullOrEmpty(disk.ReadString(Disk.MD5Key));
+ bool sha1Null = string.IsNullOrEmpty(disk.ReadString(Disk.SHA1Key));
+
+ return !md5Null
+ || !sha1Null;
+ }
+
+ ///
+ /// Returns if any hashes exist
+ ///
+ private static bool HasHashes(this Media media)
+ {
+ bool md5Null = string.IsNullOrEmpty(media.ReadString(Media.MD5Key));
+ bool sha1Null = string.IsNullOrEmpty(media.ReadString(Media.SHA1Key));
+ bool sha256Null = string.IsNullOrEmpty(media.ReadString(Media.SHA256Key));
+ bool spamsumNull = string.IsNullOrEmpty(media.ReadString(Media.SpamSumKey));
+
+ return !md5Null
+ || !sha1Null
+ || !sha256Null
+ || !spamsumNull;
+ }
+
+ ///
+ /// Returns if any hashes exist
+ ///
+ private static bool HasHashes(this Rom rom)
+ {
+ bool crcNull = string.IsNullOrEmpty(rom.ReadString(Rom.CRCKey));
+ bool md2Null = string.IsNullOrEmpty(rom.ReadString(Rom.MD2Key));
+ bool md4Null = string.IsNullOrEmpty(rom.ReadString(Rom.MD4Key));
+ bool md5Null = string.IsNullOrEmpty(rom.ReadString(Rom.MD5Key));
+ bool ripeMD128Null = string.IsNullOrEmpty(rom.ReadString(Rom.RIPEMD128Key));
+ bool ripeMD160Null = string.IsNullOrEmpty(rom.ReadString(Rom.RIPEMD160Key));
+ bool sha1Null = string.IsNullOrEmpty(rom.ReadString(Rom.SHA1Key));
+ bool sha256Null = string.IsNullOrEmpty(rom.ReadString(Rom.SHA256Key));
+ bool sha384Null = string.IsNullOrEmpty(rom.ReadString(Rom.SHA384Key));
+ bool sha512Null = string.IsNullOrEmpty(rom.ReadString(Rom.SHA512Key));
+ bool spamsumNull = string.IsNullOrEmpty(rom.ReadString(Rom.SpamSumKey));
+
+ return !crcNull
+ || !md2Null
+ || !md4Null
+ || !md5Null
+ || !ripeMD128Null
+ || !ripeMD160Null
+ || !sha1Null
+ || !sha256Null
+ || !sha384Null
+ || !sha512Null
+ || !spamsumNull;
+ }
+
+ ///
+ /// Returns if all of the hashes are set to their 0-byte values or null
+ ///
+ private static bool HasZeroHash(this Disk disk)
+ {
+ string? md5 = disk.ReadString(Disk.MD5Key);
+ bool md5Null = string.IsNullOrEmpty(md5) || string.Equals(md5, ZeroHash.MD5Str, StringComparison.OrdinalIgnoreCase);
+
+ string? sha1 = disk.ReadString(Disk.SHA1Key);
+ bool sha1Null = string.IsNullOrEmpty(sha1) || string.Equals(sha1, ZeroHash.SHA1Str, StringComparison.OrdinalIgnoreCase);
+
+ return md5Null
+ && sha1Null;
+ }
+
+ ///
+ /// Returns if all of the hashes are set to their 0-byte values or null
+ ///
+ private static bool HasZeroHash(this Media media)
+ {
+ string? md5 = media.ReadString(Media.MD5Key);
+ bool md5Null = string.IsNullOrEmpty(md5) || string.Equals(md5, ZeroHash.MD5Str, StringComparison.OrdinalIgnoreCase);
+
+ string? sha1 = media.ReadString(Media.SHA1Key);
+ bool sha1Null = string.IsNullOrEmpty(sha1) || string.Equals(sha1, ZeroHash.SHA1Str, StringComparison.OrdinalIgnoreCase);
+
+ string? sha256 = media.ReadString(Media.SHA256Key);
+ bool sha256Null = string.IsNullOrEmpty(sha256) || string.Equals(sha256, ZeroHash.SHA256Str, StringComparison.OrdinalIgnoreCase);
+
+ string? spamsum = media.ReadString(Media.SpamSumKey);
+ bool spamsumNull = string.IsNullOrEmpty(spamsum) || string.Equals(spamsum, ZeroHash.SpamSumStr, StringComparison.OrdinalIgnoreCase);
+
+ return md5Null
+ && sha1Null
+ && sha256Null
+ && spamsumNull;
+ }
+
+ ///
+ /// Returns if all of the hashes are set to their 0-byte values or null
+ ///
+ private static bool HasZeroHash(this Rom rom)
+ {
+ string? crc = rom.ReadString(Rom.CRCKey);
+ bool crcNull = string.IsNullOrEmpty(crc) || string.Equals(crc, ZeroHash.CRC32Str, StringComparison.OrdinalIgnoreCase);
+
+ string? md2 = rom.ReadString(Rom.MD2Key);
+ bool md2Null = string.IsNullOrEmpty(md2) || string.Equals(md2, ZeroHash.GetString(HashType.MD2), StringComparison.OrdinalIgnoreCase);
+
+ string? md4 = rom.ReadString(Rom.MD4Key);
+ bool md4Null = string.IsNullOrEmpty(md4) || string.Equals(md4, ZeroHash.GetString(HashType.MD4), StringComparison.OrdinalIgnoreCase);
+
+ string? md5 = rom.ReadString(Rom.MD5Key);
+ bool md5Null = string.IsNullOrEmpty(md5) || string.Equals(md5, ZeroHash.MD5Str, StringComparison.OrdinalIgnoreCase);
+
+ string? ripeMD128 = rom.ReadString(Rom.RIPEMD128Key);
+ bool ripeMD128Null = string.IsNullOrEmpty(value: ripeMD128) || string.Equals(ripeMD128, ZeroHash.GetString(HashType.RIPEMD128), StringComparison.OrdinalIgnoreCase);
+
+ string? ripeMD160 = rom.ReadString(Rom.RIPEMD160Key);
+ bool ripeMD160Null = string.IsNullOrEmpty(ripeMD160) || string.Equals(ripeMD160, ZeroHash.GetString(HashType.RIPEMD160), StringComparison.OrdinalIgnoreCase);
+
+ string? sha1 = rom.ReadString(Rom.SHA1Key);
+ bool sha1Null = string.IsNullOrEmpty(sha1) || string.Equals(sha1, ZeroHash.SHA1Str, StringComparison.OrdinalIgnoreCase);
+
+ string? sha256 = rom.ReadString(Rom.SHA256Key);
+ bool sha256Null = string.IsNullOrEmpty(sha256) || string.Equals(sha256, ZeroHash.SHA256Str, StringComparison.OrdinalIgnoreCase);
+
+ string? sha384 = rom.ReadString(Rom.SHA384Key);
+ bool sha384Null = string.IsNullOrEmpty(sha384) || string.Equals(sha384, ZeroHash.SHA384Str, StringComparison.OrdinalIgnoreCase);
+
+ string? sha512 = rom.ReadString(Rom.SHA512Key);
+ bool sha512Null = string.IsNullOrEmpty(sha512) || string.Equals(sha512, ZeroHash.SHA512Str, StringComparison.OrdinalIgnoreCase);
+
+ string? spamsum = rom.ReadString(Rom.SpamSumKey);
+ bool spamsumNull = string.IsNullOrEmpty(spamsum) || string.Equals(spamsum, ZeroHash.SpamSumStr, StringComparison.OrdinalIgnoreCase);
+
+ return crcNull
+ && md2Null
+ && md4Null
+ && md5Null
+ && ripeMD128Null
+ && ripeMD160Null
+ && sha1Null
+ && sha256Null
+ && sha384Null
+ && sha512Null
+ && spamsumNull;
+ }
+
+ #endregion
+
+ #region Information Filling
+
+ ///
+ /// Fill any missing size and hash information from another DictionaryBase
+ ///
+ public static void FillMissingHashes(this DictionaryBase? self, DictionaryBase? other)
+ {
+ if (self is null || other is null)
+ return;
+
+#if NETCOREAPP || NETSTANDARD2_0_OR_GREATER
+ switch (self, other)
+ {
+ case (Disk diskSelf, Disk diskOther):
+ diskSelf.FillMissingHashes(diskOther);
+ break;
+ case (Media mediaSelf, Media mediaOther):
+ mediaSelf.FillMissingHashes(mediaOther);
+ break;
+ case (Rom romSelf, Rom romOther):
+ romSelf.FillMissingHashes(romOther);
+ break;
+
+ default:
+ break;
+ }
+#else
+ if (self is Disk diskSelf && other is Disk diskOther)
+ diskSelf.FillMissingHashes(diskOther);
+ else if (self is Media mediaSelf && other is Media mediaOther)
+ mediaSelf.FillMissingHashes(mediaOther);
+ else if (self is Rom romSelf && other is Rom romOther)
+ romSelf.FillMissingHashes(romOther);
+#endif
+ }
+
+ ///
+ /// Fill any missing size and hash information from another Disk
+ ///
+ private static void FillMissingHashes(this Disk? self, Disk? other)
+ {
+ if (self is null || other is null)
+ return;
+
+ string? selfMd5 = self.ReadString(Disk.MD5Key);
+ string? otherMd5 = other.ReadString(Disk.MD5Key);
+ if (string.IsNullOrEmpty(selfMd5) && !string.IsNullOrEmpty(otherMd5))
+ self[Disk.MD5Key] = otherMd5;
+
+ string? selfSha1 = self.ReadString(Disk.SHA1Key);
+ string? otherSha1 = other.ReadString(Disk.SHA1Key);
+ if (string.IsNullOrEmpty(selfSha1) && !string.IsNullOrEmpty(otherSha1))
+ self[Disk.SHA1Key] = otherSha1;
+ }
+
+ ///
+ /// Fill any missing size and hash information from another Media
+ ///
+ private static void FillMissingHashes(this Media? self, Media? other)
+ {
+ if (self is null || other is null)
+ return;
+
+ string? selfMd5 = self.ReadString(Media.MD5Key);
+ string? otherMd5 = other.ReadString(Media.MD5Key);
+ if (string.IsNullOrEmpty(selfMd5) && !string.IsNullOrEmpty(otherMd5))
+ self[Media.MD5Key] = otherMd5;
+
+ string? selfSha1 = self.ReadString(Media.SHA1Key);
+ string? otherSha1 = other.ReadString(Media.SHA1Key);
+ if (string.IsNullOrEmpty(selfSha1) && !string.IsNullOrEmpty(otherSha1))
+ self[Media.SHA1Key] = otherSha1;
+
+ string? selfSha256 = self.ReadString(Media.SHA256Key);
+ string? otherSha256 = other.ReadString(Media.SHA256Key);
+ if (string.IsNullOrEmpty(selfSha256) && !string.IsNullOrEmpty(otherSha256))
+ self[Media.SHA256Key] = otherSha256;
+
+ string? selfSpamSum = self.ReadString(Media.SpamSumKey);
+ string? otherSpamSum = other.ReadString(Media.SpamSumKey);
+ if (string.IsNullOrEmpty(selfSpamSum) && !string.IsNullOrEmpty(otherSpamSum))
+ self[Media.SpamSumKey] = otherSpamSum;
+ }
+
+ ///
+ /// Fill any missing size and hash information from another Rom
+ ///
+ private static void FillMissingHashes(this Rom? self, Rom? other)
+ {
+ if (self is null || other is null)
+ return;
+
+ long? selfSize = self.ReadLong(Rom.SizeKey);
+ long? otherSize = other.ReadLong(Rom.SizeKey);
+ if (selfSize is null && otherSize is not null)
+ self[Rom.SizeKey] = otherSize;
+
+ string? selfCrc = self.ReadString(Rom.CRCKey);
+ string? otherCrc = other.ReadString(Rom.CRCKey);
+ if (string.IsNullOrEmpty(selfCrc) && !string.IsNullOrEmpty(otherCrc))
+ self[Rom.CRCKey] = otherCrc;
+
+ string? selfMd2 = self.ReadString(Rom.MD2Key);
+ string? otherMd2 = other.ReadString(Rom.MD2Key);
+ if (string.IsNullOrEmpty(selfMd2) && !string.IsNullOrEmpty(otherMd2))
+ self[Rom.MD2Key] = otherMd2;
+
+ string? selfMd4 = self.ReadString(Rom.MD4Key);
+ string? otherMd4 = other.ReadString(Rom.MD4Key);
+ if (string.IsNullOrEmpty(selfMd4) && !string.IsNullOrEmpty(otherMd4))
+ self[Rom.MD4Key] = otherMd4;
+
+ string? selfMd5 = self.ReadString(Rom.MD5Key);
+ string? otherMd5 = other.ReadString(Rom.MD5Key);
+ if (string.IsNullOrEmpty(selfMd5) && !string.IsNullOrEmpty(otherMd5))
+ self[Rom.MD5Key] = otherMd5;
+
+ string? selfRipeMD128 = self.ReadString(Rom.RIPEMD128Key);
+ string? otherRipeMD128 = other.ReadString(Rom.RIPEMD128Key);
+ if (string.IsNullOrEmpty(selfRipeMD128) && !string.IsNullOrEmpty(otherRipeMD128))
+ self[Rom.RIPEMD128Key] = otherRipeMD128;
+
+ string? selfRipeMD160 = self.ReadString(Rom.RIPEMD160Key);
+ string? otherRipeMD160 = other.ReadString(Rom.RIPEMD160Key);
+ if (string.IsNullOrEmpty(selfRipeMD160) && !string.IsNullOrEmpty(otherRipeMD160))
+ self[Rom.RIPEMD160Key] = otherRipeMD160;
+
+ string? selfSha1 = self.ReadString(Rom.SHA1Key);
+ string? otherSha1 = other.ReadString(Rom.SHA1Key);
+ if (string.IsNullOrEmpty(selfSha1) && !string.IsNullOrEmpty(otherSha1))
+ self[Rom.SHA1Key] = otherSha1;
+
+ string? selfSha256 = self.ReadString(Rom.SHA256Key);
+ string? otherSha256 = other.ReadString(Rom.SHA256Key);
+ if (string.IsNullOrEmpty(selfSha256) && !string.IsNullOrEmpty(otherSha256))
+ self[Rom.SHA256Key] = otherSha256;
+
+ string? selfSha384 = self.ReadString(Rom.SHA384Key);
+ string? otherSha384 = other.ReadString(Rom.SHA384Key);
+ if (string.IsNullOrEmpty(selfSha384) && !string.IsNullOrEmpty(otherSha384))
+ self[Rom.SHA384Key] = otherSha384;
+
+ string? selfSha512 = self.ReadString(Rom.SHA512Key);
+ string? otherSha512 = other.ReadString(Rom.SHA512Key);
+ if (string.IsNullOrEmpty(selfSha512) && !string.IsNullOrEmpty(otherSha512))
+ self[Rom.SHA512Key] = otherSha512;
+
+ string? selfSpamSum = self.ReadString(Rom.SpamSumKey);
+ string? otherSpamSum = other.ReadString(Rom.SpamSumKey);
+ if (string.IsNullOrEmpty(selfSpamSum) && !string.IsNullOrEmpty(otherSpamSum))
+ self[Rom.SpamSumKey] = otherSpamSum;
+ }
+
+ #endregion
+
+ #region Reading
+
+ ///
+ /// Read an item array from a given key, if possible
+ ///
+ public static T[]? ReadItemArray(this DictionaryBase self, string key) where T : DictionaryBase
+ {
+ var items = self.Read(key);
+ if (items == default)
+ {
+ var single = self.Read(key);
+ if (single != default)
+ items = [single];
+ }
+
+ return items;
+ }
+
+ #endregion
+ }
+}