diff --git a/SabreTools.DatFiles.Test/DatFileTests.cs b/SabreTools.DatFiles.Test/DatFileTests.cs index d6980843..61e1e675 100644 --- a/SabreTools.DatFiles.Test/DatFileTests.cs +++ b/SabreTools.DatFiles.Test/DatFileTests.cs @@ -1869,31 +1869,32 @@ namespace SabreTools.DatFiles.Test [Fact] public void ResolveNamesDB_AllDuplicate_Single() { + DatFile datFile = new Formats.Logiqx(null, useGame: false); + Machine machine = new Machine(); machine.SetFieldValue(Models.Metadata.Machine.NameKey, "machine"); + long machineIndex = datFile.AddMachineDB(machine); Source source = new Source(0); + long sourceIndex = datFile.AddSourceDB(source); Rom romA = new Rom(); romA.SetName("rom"); romA.SetFieldValue(Models.Metadata.Rom.SizeKey, 12345); romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "crc"); - romA.SetFieldValue(DatItem.MachineKey, (Machine)machine.Clone()); - romA.SetFieldValue(DatItem.SourceKey, (Source)source.Clone()); + long romAIndex = datFile.AddItemDB(romA, machineIndex, sourceIndex, statsOnly: false); Rom romB = new Rom(); romB.SetName("rom"); romB.SetFieldValue(Models.Metadata.Rom.SizeKey, 12345); romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "crc"); - romB.SetFieldValue(DatItem.MachineKey, (Machine)machine.Clone()); - romB.SetFieldValue(DatItem.SourceKey, (Source)source.Clone()); + long romBIndex = datFile.AddItemDB(romB, machineIndex, sourceIndex, statsOnly: false); List> mappings = [ - new KeyValuePair(0, romA), - new KeyValuePair(1, romB), + new KeyValuePair(romAIndex, romA), + new KeyValuePair(romBIndex, romB), ]; - DatFile datFile = new Formats.Logiqx(null, useGame: false); List> actual = datFile.ResolveNamesDB(mappings); KeyValuePair actualItemA = Assert.Single(actual); diff --git a/SabreTools.DatFiles.Test/ItemDictionaryDBTests.cs b/SabreTools.DatFiles.Test/ItemDictionaryDBTests.cs index 134cc595..68ff5252 100644 --- a/SabreTools.DatFiles.Test/ItemDictionaryDBTests.cs +++ b/SabreTools.DatFiles.Test/ItemDictionaryDBTests.cs @@ -696,6 +696,209 @@ namespace SabreTools.DatFiles.Test #endregion + #region GetDuplicateStatus + + [Fact] + public void GetDuplicateStatus_NullOther_NoDupe() + { + var dict = new ItemDictionaryDB(); + + Source? selfSource = null; + Source? lastSource = null; + + KeyValuePair? item = new KeyValuePair(0, new Rom()); + KeyValuePair? lastItem = null; + + var actual = dict.GetDuplicateStatus(item, selfSource, lastItem, lastSource); + Assert.Equal((DupeType)0x00, actual); + } + + [Fact] + public void GetDuplicateStatus_DifferentTypes_NoDupe() + { + var dict = new ItemDictionaryDB(); + + Source? selfSource = null; + Source? lastSource = null; + + KeyValuePair? rom = new KeyValuePair(0, new Rom()); + KeyValuePair? lastItem = new KeyValuePair(1, new Disk()); + var actual = dict.GetDuplicateStatus(rom, selfSource, lastItem, lastSource); + Assert.Equal((DupeType)0x00, actual); + } + + [Fact] + public void GetDuplicateStatus_MismatchedHashes_NoDupe() + { + var dict = new ItemDictionaryDB(); + + Source? sourceA = new Source(0); + long sourceAIndex = dict.AddSource(sourceA); + Source? sourceB = new Source(1); + long sourceBIndex = dict.AddSource(sourceB); + + Machine? machineA = new Machine(); + machineA.SetName("name-same"); + long machineAIndex = dict.AddMachine(machineA); + + Machine? machineB = new Machine(); + machineB.SetName("name-same"); + long machineBIndex = dict.AddMachine(machineB); + + var romA = new Rom(); + romA.SetName("same-name"); + romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "BEEFDEAD"); + long romAIndex = dict.AddItem(romA, machineAIndex, sourceAIndex); + KeyValuePair? romAPair = new KeyValuePair(romAIndex, romA); + + var romB = new Rom(); + romB.SetName("same-name"); + romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + long romBIndex = dict.AddItem(romB, machineBIndex, sourceBIndex); + KeyValuePair? romBPair = new KeyValuePair(romBIndex, romB); + + var actual = dict.GetDuplicateStatus(romAPair, sourceA, romBPair, sourceB); + Assert.Equal((DupeType)0x00, actual); + } + + [Fact] + public void GetDuplicateStatus_DifferentSource_NameMatch_ExternalAll() + { + var dict = new ItemDictionaryDB(); + + Source? sourceA = new Source(0); + long sourceAIndex = dict.AddSource(sourceA); + Source? sourceB = new Source(1); + long sourceBIndex = dict.AddSource(sourceB); + + Machine? machineA = new Machine(); + machineA.SetName("name-same"); + long machineAIndex = dict.AddMachine(machineA); + + Machine? machineB = new Machine(); + machineB.SetName("name-same"); + long machineBIndex = dict.AddMachine(machineB); + + var romA = new Rom(); + romA.SetName("same-name"); + romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + long romAIndex = dict.AddItem(romA, machineAIndex, sourceAIndex); + KeyValuePair? romAPair = new KeyValuePair(romAIndex, romA); + + var romB = new Rom(); + romB.SetName("same-name"); + romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + long romBIndex = dict.AddItem(romB, machineBIndex, sourceBIndex); + KeyValuePair? romBPair = new KeyValuePair(romBIndex, romB); + + var actual = dict.GetDuplicateStatus(romAPair, sourceA, romBPair, sourceB); + Assert.Equal(DupeType.External | DupeType.All, actual); + } + + [Fact] + public void GetDuplicateStatus_DifferentSource_NoNameMatch_ExternalHash() + { + var dict = new ItemDictionaryDB(); + + Source? sourceA = new Source(0); + long sourceAIndex = dict.AddSource(sourceA); + Source? sourceB = new Source(1); + long sourceBIndex = dict.AddSource(sourceB); + + Machine? machineA = new Machine(); + machineA.SetName("name-same"); + long machineAIndex = dict.AddMachine(machineA); + + Machine? machineB = new Machine(); + machineB.SetName("not-name-same"); + long machineBIndex = dict.AddMachine(machineB); + + var romA = new Rom(); + romA.SetName("same-name"); + romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + long romAIndex = dict.AddItem(romA, machineAIndex, sourceAIndex); + KeyValuePair? romAPair = new KeyValuePair(romAIndex, romA); + + var romB = new Rom(); + romB.SetName("same-name"); + romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + long romBIndex = dict.AddItem(romB, machineBIndex, sourceBIndex); + KeyValuePair? romBPair = new KeyValuePair(romBIndex, romB); + + var actual = dict.GetDuplicateStatus(romAPair, sourceA, romBPair, sourceB); + Assert.Equal(DupeType.External | DupeType.Hash, actual); + } + + [Fact] + public void GetDuplicateStatus_SameSource_NameMatch_InternalAll() + { + var dict = new ItemDictionaryDB(); + + Source? sourceA = new Source(0); + long sourceAIndex = dict.AddSource(sourceA); + Source? sourceB = new Source(0); + long sourceBIndex = dict.AddSource(sourceB); + + Machine? machineA = new Machine(); + machineA.SetName("name-same"); + long machineAIndex = dict.AddMachine(machineA); + + Machine? machineB = new Machine(); + machineB.SetName("name-same"); + long machineBIndex = dict.AddMachine(machineB); + + var romA = new Rom(); + romA.SetName("same-name"); + romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + long romAIndex = dict.AddItem(romA, machineAIndex, sourceAIndex); + KeyValuePair? romAPair = new KeyValuePair(romAIndex, romA); + + var romB = new Rom(); + romB.SetName("same-name"); + romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + long romBIndex = dict.AddItem(romB, machineBIndex, sourceBIndex); + KeyValuePair? romBPair = new KeyValuePair(romBIndex, romB); + + var actual = dict.GetDuplicateStatus(romAPair, sourceA, romBPair, sourceB); + Assert.Equal(DupeType.Internal | DupeType.All, actual); + } + + [Fact] + public void GetDuplicateStatus_SameSource_NoNameMatch_InternalHash() + { + var dict = new ItemDictionaryDB(); + + Source? sourceA = new Source(0); + long sourceAIndex = dict.AddSource(sourceA); + Source? sourceB = new Source(0); + long sourceBIndex = dict.AddSource(sourceB); + + Machine? machineA = new Machine(); + machineA.SetName("name-same"); + long machineAIndex = dict.AddMachine(machineA); + + Machine? machineB = new Machine(); + machineB.SetName("not-name-same"); + long machineBIndex = dict.AddMachine(machineB); + + var romA = new Rom(); + romA.SetName("same-name"); + romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + long romAIndex = dict.AddItem(romA, machineAIndex, sourceAIndex); + KeyValuePair? romAPair = new KeyValuePair(romAIndex, romA); + + var romB = new Rom(); + romB.SetName("same-name"); + romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + long romBIndex = dict.AddItem(romB, machineBIndex, sourceBIndex); + KeyValuePair? romBPair = new KeyValuePair(romBIndex, romB); + + var actual = dict.GetDuplicateStatus(romAPair, sourceA, romBPair, sourceB); + Assert.Equal(DupeType.Internal | DupeType.Hash, actual); + } + + #endregion + #region GetDuplicates [Theory] diff --git a/SabreTools.DatFiles.Test/ItemDictionaryTests.cs b/SabreTools.DatFiles.Test/ItemDictionaryTests.cs index e2f4aac1..e0a87afc 100644 --- a/SabreTools.DatFiles.Test/ItemDictionaryTests.cs +++ b/SabreTools.DatFiles.Test/ItemDictionaryTests.cs @@ -535,6 +535,165 @@ namespace SabreTools.DatFiles.Test #endregion + #region GetDuplicateStatus + + [Fact] + public void GetDuplicateStatus_NullOther_NoDupe() + { + var dict = new ItemDictionary(); + DatItem item = new Rom(); + DatItem? lastItem = null; + var actual = dict.GetDuplicateStatus(item, lastItem); + Assert.Equal((DupeType)0x00, actual); + } + + [Fact] + public void GetDuplicateStatus_DifferentTypes_NoDupe() + { + var dict = new ItemDictionary(); + var rom = new Rom(); + DatItem? lastItem = new Disk(); + var actual = dict.GetDuplicateStatus(rom, lastItem); + Assert.Equal((DupeType)0x00, actual); + } + + [Fact] + public void GetDuplicateStatus_MismatchedHashes_NoDupe() + { + var dict = new ItemDictionary(); + + Machine? machineA = new Machine(); + machineA.SetName("name-same"); + + Machine? machineB = new Machine(); + machineB.SetName("name-same"); + + var romA = new Rom(); + romA.SetName("same-name"); + romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "BEEFDEAD"); + romA.SetFieldValue(DatItem.SourceKey, new Source(0)); + romA.CopyMachineInformation(machineA); + + var romB = new Rom(); + romB.SetName("same-name"); + romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + romB.SetFieldValue(DatItem.SourceKey, new Source(1)); + romB.CopyMachineInformation(machineB); + + var actual = dict.GetDuplicateStatus(romA, romB); + Assert.Equal((DupeType)0x00, actual); + } + + [Fact] + public void GetDuplicateStatus_DifferentSource_NameMatch_ExternalAll() + { + var dict = new ItemDictionary(); + + Machine? machineA = new Machine(); + machineA.SetName("name-same"); + + Machine? machineB = new Machine(); + machineB.SetName("name-same"); + + var romA = new Rom(); + romA.SetName("same-name"); + romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + romA.SetFieldValue(DatItem.SourceKey, new Source(0)); + romA.CopyMachineInformation(machineA); + + var romB = new Rom(); + romB.SetName("same-name"); + romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + romB.SetFieldValue(DatItem.SourceKey, new Source(1)); + romB.CopyMachineInformation(machineB); + + var actual = dict.GetDuplicateStatus(romA, romB); + Assert.Equal(DupeType.External | DupeType.All, actual); + } + + [Fact] + public void GetDuplicateStatus_DifferentSource_NoNameMatch_ExternalHash() + { + var dict = new ItemDictionary(); + + Machine? machineA = new Machine(); + machineA.SetName("name-same"); + + Machine? machineB = new Machine(); + machineB.SetName("not-name-same"); + + var romA = new Rom(); + romA.SetName("same-name"); + romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + romA.SetFieldValue(DatItem.SourceKey, new Source(0)); + romA.CopyMachineInformation(machineA); + + var romB = new Rom(); + romB.SetName("same-name"); + romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + romB.SetFieldValue(DatItem.SourceKey, new Source(1)); + romB.CopyMachineInformation(machineB); + + var actual = dict.GetDuplicateStatus(romA, romB); + Assert.Equal(DupeType.External | DupeType.Hash, actual); + } + + [Fact] + public void GetDuplicateStatus_SameSource_NameMatch_InternalAll() + { + var dict = new ItemDictionary(); + + Machine? machineA = new Machine(); + machineA.SetName("name-same"); + + Machine? machineB = new Machine(); + machineB.SetName("name-same"); + + var romA = new Rom(); + romA.SetName("same-name"); + romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + romA.SetFieldValue(DatItem.SourceKey, new Source(0)); + romA.CopyMachineInformation(machineA); + + var romB = new Rom(); + romB.SetName("same-name"); + romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + romB.SetFieldValue(DatItem.SourceKey, new Source(0)); + romB.CopyMachineInformation(machineB); + + var actual = dict.GetDuplicateStatus(romA, romB); + Assert.Equal(DupeType.Internal | DupeType.All, actual); + } + + [Fact] + public void GetDuplicateStatus_SameSource_NoNameMatch_InternalHash() + { + var dict = new ItemDictionary(); + + Machine? machineA = new Machine(); + machineA.SetName("name-same"); + + Machine? machineB = new Machine(); + machineB.SetName("not-name-same"); + + var romA = new Rom(); + romA.SetName("same-name"); + romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + romA.SetFieldValue(DatItem.SourceKey, new Source(0)); + romA.CopyMachineInformation(machineA); + + var romB = new Rom(); + romB.SetName("same-name"); + romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); + romB.SetFieldValue(DatItem.SourceKey, new Source(0)); + romB.CopyMachineInformation(machineB); + + var actual = dict.GetDuplicateStatus(romA, romB); + Assert.Equal(DupeType.Internal | DupeType.Hash, actual); + } + + #endregion + #region GetDuplicates [Theory] diff --git a/SabreTools.DatFiles/DatFile.cs b/SabreTools.DatFiles/DatFile.cs index 6131df8f..7625de92 100644 --- a/SabreTools.DatFiles/DatFile.cs +++ b/SabreTools.DatFiles/DatFile.cs @@ -766,9 +766,9 @@ namespace SabreTools.DatFiles // If the current item exactly matches the last item, then we don't add it #if NET20 || NET35 - if ((datItem.GetDuplicateStatus(lastItem) & DupeType.All) != 0) + if ((Items.GetDuplicateStatus(datItem, lastItem) & DupeType.All) != 0) #else - if (datItem.GetDuplicateStatus(lastItem).HasFlag(DupeType.All)) + if (Items.GetDuplicateStatus(datItem, lastItem).HasFlag(DupeType.All)) #endif { _logger.Verbose($"Exact duplicate found for '{datItemName}'"); @@ -838,7 +838,7 @@ namespace SabreTools.DatFiles SortDB(ref mappings, true); // Now we want to loop through and check names - DatItem? lastItem = null; + KeyValuePair? lastItem = null; string? lastrenamed = null; int lastid = 0; foreach (var datItem in mappings) @@ -847,13 +847,13 @@ namespace SabreTools.DatFiles if (lastItem == null) { output.Add(datItem); - lastItem = datItem.Value; + lastItem = datItem; continue; } // Get the last item name, if applicable - string lastItemName = lastItem.GetName() - ?? lastItem.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue().AsStringValue() + string lastItemName = lastItem.Value.Value.GetName() + ?? lastItem.Value.Value.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue().AsStringValue() ?? string.Empty; // Get the current item name, if applicable @@ -861,11 +861,15 @@ namespace SabreTools.DatFiles ?? datItem.Value.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue().AsStringValue() ?? string.Empty; + // Get sources for both items + var datItemSource = ItemsDB.GetSourceForItem(datItem.Key); + var lastItemSource = ItemsDB.GetSourceForItem(lastItem.Value.Key); + // If the current item exactly matches the last item, then we don't add it #if NET20 || NET35 - if ((datItem.Value.GetDuplicateStatus(lastItem) & DupeType.All) != 0) + if ((ItemsDB.GetDuplicateStatus(datItem, datItemSource.Value, lastItem, lastItemSource.Value) & DupeType.All) != 0) #else - if (datItem.Value.GetDuplicateStatus(lastItem).HasFlag(DupeType.All)) + if (ItemsDB.GetDuplicateStatus(datItem, datItemSource.Value, lastItem, lastItemSource.Value).HasFlag(DupeType.All)) #endif { _logger.Verbose($"Exact duplicate found for '{datItemName}'"); @@ -904,7 +908,7 @@ namespace SabreTools.DatFiles else { output.Add(datItem); - lastItem = datItem.Value; + lastItem = datItem; lastrenamed = null; lastid = 0; } diff --git a/SabreTools.DatFiles/ItemDictionary.cs b/SabreTools.DatFiles/ItemDictionary.cs index 22d16344..4cd32114 100644 --- a/SabreTools.DatFiles/ItemDictionary.cs +++ b/SabreTools.DatFiles/ItemDictionary.cs @@ -447,6 +447,61 @@ namespace SabreTools.DatFiles #endif } + /// + /// Return the duplicate status of two items + /// + /// Current DatItem + /// DatItem to check against + /// The DupeType corresponding to the relationship between the two + public DupeType GetDuplicateStatus(DatItem? self, DatItem? last) + { + DupeType output = 0x00; + + // If either item is null + if (self == null || last == null) + return output; + + // If we don't have a duplicate at all, return none + if (!self.Equals(last)) + return output; + + // Get the sources for comparison + var selfSource = self.GetFieldValue(DatItem.SourceKey); + var lastSource = last.GetFieldValue(DatItem.SourceKey); + + // Get the machines for comparison + var selfMachine = self.GetMachine(); + string? selfMachineName = selfMachine?.GetName(); + var lastMachine = last.GetMachine(); + string? lastMachineName = lastMachine?.GetName(); + + // If the duplicate is external already +#if NET20 || NET35 + if ((last.GetFieldValue(DatItem.DupeTypeKey) & DupeType.External) != 0) +#else + if (last.GetFieldValue(DatItem.DupeTypeKey).HasFlag(DupeType.External)) +#endif + output |= DupeType.External; + + // If the duplicate should be external + else if (lastSource?.Index != selfSource?.Index) + output |= DupeType.External; + + // Otherwise, it's considered an internal dupe + else + output |= DupeType.Internal; + + // If the item and machine names match + if (lastMachineName == selfMachineName && last.GetName() == self.GetName()) + output |= DupeType.All; + + // Otherwise, hash match is assumed + else + output |= DupeType.Hash; + + return output; + } + /// /// Merge an arbitrary set of DatItems based on the supplied information /// @@ -459,6 +514,9 @@ namespace SabreTools.DatFiles if (items == null || items.Count == 0) return []; + // Create placeholder object for checking duplicates + var dupDict = new ItemDictionary(); + // Create output list List output = []; @@ -492,7 +550,7 @@ namespace SabreTools.DatFiles } // Find the index of the first duplicate, if one exists - int pos = output.FindIndex(lastItem => datItem.GetDuplicateStatus(lastItem) != 0x00); + int pos = output.FindIndex(lastItem => dupDict.GetDuplicateStatus(datItem, lastItem) != 0x00); if (pos < 0) { output.Add(datItem); @@ -501,7 +559,7 @@ namespace SabreTools.DatFiles // Get the duplicate item DatItem savedItem = output[pos]; - DupeType dupetype = datItem.GetDuplicateStatus(savedItem); + DupeType dupetype = dupDict.GetDuplicateStatus(datItem, savedItem); // Disks, File, Media, and Roms have more information to fill if (datItem is Disk diskItem && savedItem is Disk savedDisk) diff --git a/SabreTools.DatFiles/ItemDictionaryDB.cs b/SabreTools.DatFiles/ItemDictionaryDB.cs index f7e8c6e8..ced648ef 100644 --- a/SabreTools.DatFiles/ItemDictionaryDB.cs +++ b/SabreTools.DatFiles/ItemDictionaryDB.cs @@ -719,6 +719,59 @@ namespace SabreTools.DatFiles #endif } + /// + /// Return the duplicate status of two items + /// + /// Current DatItem + /// Source associated with this item + /// DatItem to check against + /// Source associated with the last item + /// The DupeType corresponding to the relationship between the two + public DupeType GetDuplicateStatus(KeyValuePair? selfItem, Source? selfSource, KeyValuePair? lastItem, Source? lastSource) + { + DupeType output = 0x00; + + // If either item is null + if (selfItem == null || lastItem == null) + return output; + + // If we don't have a duplicate at all, return none + if (!selfItem.Value.Value.Equals(lastItem.Value.Value)) + return output; + + // Get the machines for comparison + var selfMachine = GetMachineForItem(selfItem.Value.Key).Value; + string? selfMachineName = selfMachine?.GetName(); + var lastMachine = GetMachineForItem(lastItem.Value.Key).Value; + string? lastMachineName = lastMachine?.GetName(); + + // If the duplicate is external already +#if NET20 || NET35 + if ((lastItem.Value.Value.GetFieldValue(DatItem.DupeTypeKey) & DupeType.External) != 0) +#else + if (lastItem.Value.Value.GetFieldValue(DatItem.DupeTypeKey).HasFlag(DupeType.External)) +#endif + output |= DupeType.External; + + // If the duplicate should be external + else if (lastSource?.Index != selfSource?.Index) + output |= DupeType.External; + + // Otherwise, it's considered an internal dupe + else + output |= DupeType.Internal; + + // If the item and machine names match + if (lastMachineName == selfMachineName && lastItem.Value.Value.GetName() == selfItem.Value.Value.GetName()) + output |= DupeType.All; + + // Otherwise, hash match is assumed + else + output |= DupeType.Hash; + + return output; + } + /// /// List all duplicates found in a DAT based on a DatItem /// @@ -837,7 +890,12 @@ namespace SabreTools.DatFiles } // Find the index of the first duplicate, if one exists - int pos = output.FindIndex(lastItem => datItem.GetDuplicateStatus(lastItem.Value) != 0x00); + var datItemSource = GetSourceForItem(itemIndex); + int pos = output.FindIndex(lastItem => + { + var lastItemSource = GetSourceForItem(lastItem.Key); + return GetDuplicateStatus(kvp, datItemSource.Value, lastItem, lastItemSource.Value) != 0x00; + }); if (pos < 0) { output.Add(new KeyValuePair(itemIndex, datItem)); @@ -847,7 +905,8 @@ namespace SabreTools.DatFiles // Get the duplicate item long savedIndex = output[pos].Key; DatItem savedItem = output[pos].Value; - DupeType dupetype = datItem.GetDuplicateStatus(savedItem); + var savedItemSource = GetSourceForItem(savedIndex); + DupeType dupetype = GetDuplicateStatus(kvp, datItemSource.Value, output[pos], savedItemSource.Value); // Disks, Media, and Roms have more information to fill if (datItem is Disk diskItem && savedItem is Disk savedDisk) diff --git a/SabreTools.DatItems.Test/DatItemTests.cs b/SabreTools.DatItems.Test/DatItemTests.cs index 376df493..e893f1f9 100644 --- a/SabreTools.DatItems.Test/DatItemTests.cs +++ b/SabreTools.DatItems.Test/DatItemTests.cs @@ -280,312 +280,6 @@ namespace SabreTools.DatItems.Test #endregion - #region GetDuplicateStatus - - [Fact] - public void GetDuplicateStatus_NullOther_NoDupe() - { - DatItem item = new Rom(); - DatItem? lastItem = null; - var actual = item.GetDuplicateStatus(lastItem); - Assert.Equal((DupeType)0x00, actual); - } - - [Fact] - public void GetDuplicateStatus_DifferentTypes_NoDupe() - { - var rom = new Rom(); - DatItem? lastItem = new Disk(); - var actual = rom.GetDuplicateStatus(lastItem); - Assert.Equal((DupeType)0x00, actual); - } - - [Fact] - public void GetDuplicateStatus_MismatchedHashes_NoDupe() - { - Machine? machineA = new Machine(); - machineA.SetName("name-same"); - - Machine? machineB = new Machine(); - machineB.SetName("name-same"); - - var romA = new Rom(); - romA.SetName("same-name"); - romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "BEEFDEAD"); - romA.SetFieldValue(DatItem.SourceKey, new Source(0)); - romA.CopyMachineInformation(machineA); - - var romB = new Rom(); - romB.SetName("same-name"); - romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romB.SetFieldValue(DatItem.SourceKey, new Source(1)); - romB.CopyMachineInformation(machineB); - - var actual = romA.GetDuplicateStatus(romB); - Assert.Equal((DupeType)0x00, actual); - } - - [Fact] - public void GetDuplicateStatus_DifferentSource_NameMatch_ExternalAll() - { - Machine? machineA = new Machine(); - machineA.SetName("name-same"); - - Machine? machineB = new Machine(); - machineB.SetName("name-same"); - - var romA = new Rom(); - romA.SetName("same-name"); - romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romA.SetFieldValue(DatItem.SourceKey, new Source(0)); - romA.CopyMachineInformation(machineA); - - var romB = new Rom(); - romB.SetName("same-name"); - romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romB.SetFieldValue(DatItem.SourceKey, new Source(1)); - romB.CopyMachineInformation(machineB); - - var actual = romA.GetDuplicateStatus(romB); - Assert.Equal(DupeType.External | DupeType.All, actual); - } - - [Fact] - public void GetDuplicateStatus_DifferentSource_NoNameMatch_ExternalHash() - { - Machine? machineA = new Machine(); - machineA.SetName("name-same"); - - Machine? machineB = new Machine(); - machineB.SetName("not-name-same"); - - var romA = new Rom(); - romA.SetName("same-name"); - romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romA.SetFieldValue(DatItem.SourceKey, new Source(0)); - romA.CopyMachineInformation(machineA); - - var romB = new Rom(); - romB.SetName("same-name"); - romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romB.SetFieldValue(DatItem.SourceKey, new Source(1)); - romB.CopyMachineInformation(machineB); - - var actual = romA.GetDuplicateStatus(romB); - Assert.Equal(DupeType.External | DupeType.Hash, actual); - } - - [Fact] - public void GetDuplicateStatus_SameSource_NameMatch_InternalAll() - { - Machine? machineA = new Machine(); - machineA.SetName("name-same"); - - Machine? machineB = new Machine(); - machineB.SetName("name-same"); - - var romA = new Rom(); - romA.SetName("same-name"); - romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romA.SetFieldValue(DatItem.SourceKey, new Source(0)); - romA.CopyMachineInformation(machineA); - - var romB = new Rom(); - romB.SetName("same-name"); - romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romB.SetFieldValue(DatItem.SourceKey, new Source(0)); - romB.CopyMachineInformation(machineB); - - var actual = romA.GetDuplicateStatus(romB); - Assert.Equal(DupeType.Internal | DupeType.All, actual); - } - - [Fact] - public void GetDuplicateStatus_SameSource_NoNameMatch_InternalHash() - { - Machine? machineA = new Machine(); - machineA.SetName("name-same"); - - Machine? machineB = new Machine(); - machineB.SetName("not-name-same"); - - var romA = new Rom(); - romA.SetName("same-name"); - romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romA.SetFieldValue(DatItem.SourceKey, new Source(0)); - romA.CopyMachineInformation(machineA); - - var romB = new Rom(); - romB.SetName("same-name"); - romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romB.SetFieldValue(DatItem.SourceKey, new Source(0)); - romB.CopyMachineInformation(machineB); - - var actual = romA.GetDuplicateStatus(romB); - Assert.Equal(DupeType.Internal | DupeType.Hash, actual); - } - - #endregion - - // TODO: Change when Machine retrieval gets fixed - #region GetDuplicateStatusDB - - [Fact] - public void GetDuplicateStatusDB_NullOther_NoDupe() - { - Source? selfSource = null; - Source? lastSource = null; - - DatItem item = new Rom(); - DatItem? lastItem = null; - var actual = item.GetDuplicateStatusDB(selfSource, lastItem, lastSource); - Assert.Equal((DupeType)0x00, actual); - } - - [Fact] - public void GetDuplicateStatusDB_DifferentTypes_NoDupe() - { - Source? selfSource = null; - Source? lastSource = null; - - var rom = new Rom(); - DatItem? lastItem = new Disk(); - var actual = rom.GetDuplicateStatusDB(selfSource, lastItem, lastSource); - Assert.Equal((DupeType)0x00, actual); - } - - [Fact] - public void GetDuplicateStatusDB_MismatchedHashes_NoDupe() - { - Source? sourceA = new Source(0); - Source? sourceB = new Source(1); - - Machine? machineA = new Machine(); - machineA.SetName("name-same"); - - Machine? machineB = new Machine(); - machineB.SetName("name-same"); - - var romA = new Rom(); - romA.SetName("same-name"); - romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "BEEFDEAD"); - romA.CopyMachineInformation(machineA); - - var romB = new Rom(); - romB.SetName("same-name"); - romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romB.CopyMachineInformation(machineB); - - var actual = romA.GetDuplicateStatusDB(sourceA, romB, sourceB); - Assert.Equal((DupeType)0x00, actual); - } - - [Fact] - public void GetDuplicateStatusDB_DifferentSource_NameMatch_ExternalAll() - { - Source? sourceA = new Source(0); - Source? sourceB = new Source(1); - - Machine? machineA = new Machine(); - machineA.SetName("name-same"); - - Machine? machineB = new Machine(); - machineB.SetName("name-same"); - - var romA = new Rom(); - romA.SetName("same-name"); - romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romA.CopyMachineInformation(machineA); - - var romB = new Rom(); - romB.SetName("same-name"); - romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romB.CopyMachineInformation(machineB); - - var actual = romA.GetDuplicateStatusDB(sourceA, romB, sourceB); - Assert.Equal(DupeType.External | DupeType.All, actual); - } - - [Fact] - public void GetDuplicateStatusDB_DifferentSource_NoNameMatch_ExternalHash() - { - Source? sourceA = new Source(0); - Source? sourceB = new Source(1); - - Machine? machineA = new Machine(); - machineA.SetName("name-same"); - - Machine? machineB = new Machine(); - machineB.SetName("not-name-same"); - - var romA = new Rom(); - romA.SetName("same-name"); - romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romA.CopyMachineInformation(machineA); - - var romB = new Rom(); - romB.SetName("same-name"); - romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romB.CopyMachineInformation(machineB); - - var actual = romA.GetDuplicateStatusDB(sourceA, romB, sourceB); - Assert.Equal(DupeType.External | DupeType.Hash, actual); - } - - [Fact] - public void GetDuplicateStatusDB_SameSource_NameMatch_InternalAll() - { - Source? sourceA = new Source(0); - Source? sourceB = new Source(0); - - Machine? machineA = new Machine(); - machineA.SetName("name-same"); - - Machine? machineB = new Machine(); - machineB.SetName("name-same"); - - var romA = new Rom(); - romA.SetName("same-name"); - romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romA.CopyMachineInformation(machineA); - - var romB = new Rom(); - romB.SetName("same-name"); - romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romB.CopyMachineInformation(machineB); - - var actual = romA.GetDuplicateStatusDB(sourceA, romB, sourceB); - Assert.Equal(DupeType.Internal | DupeType.All, actual); - } - - [Fact] - public void GetDuplicateStatusDB_SameSource_NoNameMatch_InternalHash() - { - Source? sourceA = new Source(0); - Source? sourceB = new Source(0); - - Machine? machineA = new Machine(); - machineA.SetName("name-same"); - - Machine? machineB = new Machine(); - machineB.SetName("not-name-same"); - - var romA = new Rom(); - romA.SetName("same-name"); - romA.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romA.CopyMachineInformation(machineA); - - var romB = new Rom(); - romB.SetName("same-name"); - romB.SetFieldValue(Models.Metadata.Rom.CRCKey, "DEADBEEF"); - romB.CopyMachineInformation(machineB); - - var actual = romA.GetDuplicateStatusDB(sourceA, romB, sourceB); - Assert.Equal(DupeType.Internal | DupeType.Hash, actual); - } - - #endregion - // TODO: Change when Machine retrieval gets fixed #region GetKey diff --git a/SabreTools.DatItems/DatItem.cs b/SabreTools.DatItems/DatItem.cs index f869c1e6..bd74a053 100644 --- a/SabreTools.DatItems/DatItem.cs +++ b/SabreTools.DatItems/DatItem.cs @@ -234,105 +234,6 @@ namespace SabreTools.DatItems return _internal.EqualTo(other._internal); } - /// - /// Return the duplicate status of two items - /// - /// DatItem to check against - /// The DupeType corresponding to the relationship between the two - public DupeType GetDuplicateStatus(DatItem? lastItem) - { - DupeType output = 0x00; - - // If we don't have a duplicate at all, return none - if (!Equals(lastItem)) - return output; - - // Get the sources for comparison - var selfSource = GetFieldValue(SourceKey); - var lastSource = lastItem.GetFieldValue(SourceKey); - - // Get the machines for comparison - var selfMachine = GetMachine(); - string? selfMachineName = selfMachine?.GetName(); - var lastMachine = lastItem.GetMachine(); - string? lastMachineName = lastMachine?.GetName(); - - // If the duplicate is external already -#if NET20 || NET35 - if ((lastItem.GetFieldValue(DupeTypeKey) & DupeType.External) != 0) -#else - if (lastItem.GetFieldValue(DupeTypeKey).HasFlag(DupeType.External)) -#endif - output |= DupeType.External; - - // If the duplicate should be external - else if (lastSource?.Index != selfSource?.Index) - output |= DupeType.External; - - // Otherwise, it's considered an internal dupe - else - output |= DupeType.Internal; - - // If the item and machine names match - if (lastMachineName == selfMachineName && lastItem.GetName() == GetName()) - output |= DupeType.All; - - // Otherwise, hash match is assumed - else - output |= DupeType.Hash; - - return output; - } - - /// - /// Return the duplicate status of two items - /// - /// Source associated with this item - /// DatItem to check against - /// Source associated with the last item - /// The DupeType corresponding to the relationship between the two - public DupeType GetDuplicateStatusDB(Source? selfSource, DatItem? lastItem, Source? lastSource) - { - DupeType output = 0x00; - - // If we don't have a duplicate at all, return none - if (!Equals(lastItem)) - return output; - - // TODO: Fix this since machines are determined in a different way - // Get the machines for comparison - var selfMachine = GetMachine(); - string? selfMachineName = selfMachine?.GetName(); - var lastMachine = lastItem.GetMachine(); - string? lastMachineName = lastMachine?.GetName(); - - // If the duplicate is external already -#if NET20 || NET35 - if ((lastItem.GetFieldValue(DupeTypeKey) & DupeType.External) != 0) -#else - if (lastItem.GetFieldValue(DupeTypeKey).HasFlag(DupeType.External)) -#endif - output |= DupeType.External; - - // If the duplicate should be external - else if (lastSource?.Index != selfSource?.Index) - output |= DupeType.External; - - // Otherwise, it's considered an internal dupe - else - output |= DupeType.Internal; - - // If the item and machine names match - if (lastMachineName == selfMachineName && lastItem.GetName() == GetName()) - output |= DupeType.All; - - // Otherwise, hash match is assumed - else - output |= DupeType.Hash; - - return output; - } - #endregion #region Manipulation