Flatten merge and make consistent across implementations

This commit is contained in:
Matt Nadareski
2025-01-07 14:55:56 -05:00
parent 9cdd108f74
commit 15ac732877
7 changed files with 109 additions and 202 deletions

View File

@@ -736,7 +736,7 @@ namespace SabreTools.DatFiles
/// Merge an arbitrary set of item pairs based on the supplied information /// Merge an arbitrary set of item pairs based on the supplied information
/// </summary> /// </summary>
/// <param name="itemMappings">List of pairs representing the items to be merged</param> /// <param name="itemMappings">List of pairs representing the items to be merged</param>
private List<KeyValuePair<long, DatItem>> Deduplicate(List<KeyValuePair<long, DatItem>> itemMappings) private List<KeyValuePair<long, DatItem>> Merge(List<KeyValuePair<long, DatItem>> itemMappings)
{ {
// Check for null or blank roms first // Check for null or blank roms first
if (itemMappings == null || itemMappings.Count == 0) if (itemMappings == null || itemMappings.Count == 0)
@@ -769,49 +769,43 @@ namespace SabreTools.DatFiles
nodumpCount++; nodumpCount++;
continue; continue;
} }
// If it's the first non-nodump rom in the list, don't touch it // If it's the first non-nodump rom in the list, don't touch it
else if (output.Count == 0 || output.Count == nodumpCount) if (output.Count == nodumpCount)
{ {
output.Add(new KeyValuePair<long, DatItem>(itemIndex, datItem)); output.Add(new KeyValuePair<long, DatItem>(itemIndex, datItem));
continue; continue;
} }
// Check if the rom is a duplicate // Find the index of the first duplicate, if one exists
DupeType dupetype = 0x00; int pos = output.FindIndex(lastItem => datItem.GetDuplicateStatus(lastItem.Value) != 0x00);
long savedIndex = -1; if (pos < 0)
DatItem saveditem = new Blank();
int pos = -1;
for (int i = 0; i < output.Count; i++)
{ {
long lastIndex = output[i].Key; output.Add(new KeyValuePair<long, DatItem>(itemIndex, datItem));
DatItem lastrom = output[i].Value; continue;
}
// Get the duplicate item
long savedIndex = output[pos].Key;
DatItem savedItem = output[pos].Value;
DupeType dupetype = datItem.GetDuplicateStatus(savedItem);
// Disks, Media, and Roms have more information to fill
if (datItem is Disk diskItem && savedItem is Disk savedDisk)
savedDisk.FillMissingInformation(diskItem);
else if (datItem is DatItems.Formats.File fileItem && savedItem is DatItems.Formats.File savedFile)
savedFile.FillMissingInformation(fileItem);
else if (datItem is Media mediaItem && savedItem is Media savedMedia)
savedMedia.FillMissingInformation(mediaItem);
else if (datItem is Rom romItem && savedItem is Rom savedRom)
savedRom.FillMissingInformation(romItem);
savedItem.SetFieldValue<DupeType>(DatItem.DupeTypeKey, dupetype);
// Get the sources associated with the items // Get the sources associated with the items
var savedSource = _sources[_itemToSourceMapping[savedIndex]]; var savedSource = _sources[_itemToSourceMapping[savedIndex]];
var itemSource = _sources[_itemToSourceMapping[itemIndex]]; var itemSource = _sources[_itemToSourceMapping[itemIndex]];
// Get the duplicate status
dupetype = datItem.GetDuplicateStatus(itemSource, lastrom, savedSource);
// If it's a duplicate, skip adding it to the output but add any missing information
if (dupetype != 0x00)
{
savedIndex = lastIndex;
saveditem = lastrom;
pos = i;
// Disks, Media, and Roms have more information to fill
if (datItem is Disk disk && saveditem is Disk savedDisk)
savedDisk.FillMissingInformation(disk);
else if (datItem is DatItems.Formats.File fileItem && saveditem is DatItems.Formats.File savedFile)
savedFile.FillMissingInformation(fileItem);
else if (datItem is Media media && saveditem is Media savedMedia)
savedMedia.FillMissingInformation(media);
else if (datItem is Rom romItem && saveditem is Rom savedRom)
savedRom.FillMissingInformation(romItem);
saveditem.SetFieldValue<DupeType>(DatItem.DupeTypeKey, dupetype);
// Get the machines associated with the items // Get the machines associated with the items
var savedMachine = _machines[_itemToMachineMapping[savedIndex]]; var savedMachine = _machines[_itemToMachineMapping[savedIndex]];
var itemMachine = _machines[_itemToMachineMapping[itemIndex]]; var itemMachine = _machines[_itemToMachineMapping[itemIndex]];
@@ -821,7 +815,7 @@ namespace SabreTools.DatFiles
{ {
_itemToSourceMapping[itemIndex] = _itemToSourceMapping[savedIndex]; _itemToSourceMapping[itemIndex] = _itemToSourceMapping[savedIndex];
_machines[_itemToMachineMapping[savedIndex]] = (itemMachine.Clone() as Machine)!; _machines[_itemToMachineMapping[savedIndex]] = (itemMachine.Clone() as Machine)!;
saveditem.SetName(datItem.GetName()); savedItem.SetName(datItem.GetName());
} }
// If the current machine is a child of the new machine, use the new machine instead // If the current machine is a child of the new machine, use the new machine instead
@@ -829,23 +823,12 @@ namespace SabreTools.DatFiles
|| savedMachine.GetStringFieldValue(Models.Metadata.Machine.RomOfKey) == itemMachine.GetStringFieldValue(Models.Metadata.Machine.NameKey)) || savedMachine.GetStringFieldValue(Models.Metadata.Machine.RomOfKey) == itemMachine.GetStringFieldValue(Models.Metadata.Machine.NameKey))
{ {
_machines[_itemToMachineMapping[savedIndex]] = (itemMachine.Clone() as Machine)!; _machines[_itemToMachineMapping[savedIndex]] = (itemMachine.Clone() as Machine)!;
saveditem.SetName(datItem.GetName()); savedItem.SetName(datItem.GetName());
} }
break; // Replace the original item in the list
}
}
// If no duplicate is found, add it to the list
if (dupetype == 0x00)
{
output.Add(new KeyValuePair<long, DatItem>(itemIndex, datItem));
}
// Otherwise, if a new rom information is found, add that
{
output.RemoveAt(pos); output.RemoveAt(pos);
output.Insert(pos, new KeyValuePair<long, DatItem>(savedIndex, saveditem)); output.Insert(pos, new KeyValuePair<long, DatItem>(savedIndex, savedItem));
}
} }
return output; return output;
@@ -1072,7 +1055,7 @@ namespace SabreTools.DatFiles
// If we're merging the roms, do so // If we're merging the roms, do so
if (dedupeType == DedupeType.Full || (dedupeType == DedupeType.Game && bucketBy == ItemKey.Machine)) if (dedupeType == DedupeType.Full || (dedupeType == DedupeType.Game && bucketBy == ItemKey.Machine))
datItems = Deduplicate(datItems); datItems = Merge(datItems);
_buckets[bucketKeys[i]] = [.. datItems.Select(kvp => kvp.Key)]; _buckets[bucketKeys[i]] = [.. datItems.Select(kvp => kvp.Key)];
#if NET40_OR_GREATER || NETCOREAPP #if NET40_OR_GREATER || NETCOREAPP

View File

@@ -123,75 +123,6 @@ namespace SabreTools.DatItems
/// <returns>Clone of the DatItem</returns> /// <returns>Clone of the DatItem</returns>
public abstract object Clone(); public abstract object Clone();
/// <summary>
/// Conditionally copy all machine information from another item
/// </summary>
/// <param name="item">Existing item to copy information from</param>
/// <remarks>
/// The cases when Machine data is updated:
/// - Current machine is a clone of the other machine
/// - Current machine is a rom of the other machine
/// </remarks>
public void ConditionalUpdateMachine(DatItem item)
{
// Get the machines for the two items
Machine? selfMachine = GetFieldValue<Machine>(DatItem.MachineKey);
Machine? itemMachine = item.GetFieldValue<Machine>(DatItem.MachineKey);
// If either machine is missing
if (selfMachine == null || itemMachine == null)
return;
// Get the required strings
string? selfCloneOf = selfMachine.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey);
string? selfRomOf = selfMachine.GetStringFieldValue(Models.Metadata.Machine.RomOfKey);
string? otherMachineName = itemMachine.GetStringFieldValue(Models.Metadata.Machine.NameKey);
// If the other machine has no name
if (otherMachineName == null)
return;
// If the current machine is a child of the new machine, use the new machine instead
if (selfCloneOf == otherMachineName)
{
CopyMachineInformation(item);
SetName(item.GetName());
}
else if (selfRomOf == otherMachineName)
{
CopyMachineInformation(item);
SetName(item.GetName());
}
}
/// <summary>
/// Conditionally copy all source information from another item
/// </summary>
/// <param name="item">Existing item to copy information from</param>
/// <remarks>
/// The cases when Source data is updated:
/// - Current source data has an index higher than the other item
/// </remarks>
public void ConditionalUpdateSource(DatItem item)
{
// Get the sources for comparison
Source? selfSource = GetFieldValue<Source?>(DatItem.SourceKey);
Source? itemSource = item.GetFieldValue<Source?>(DatItem.SourceKey);
// If either source is missing
if (selfSource == null || itemSource == null)
return;
// Use the new source if less than
if (selfSource.Index > itemSource.Index)
{
SetFieldValue<Source?>(DatItem.SourceKey, itemSource.Clone() as Source);
CopyMachineInformation(item);
SetName(item.GetName());
return;
}
}
/// <summary> /// <summary>
/// Copy all machine information over in one shot /// Copy all machine information over in one shot
/// </summary> /// </summary>

View File

@@ -281,85 +281,93 @@ namespace SabreTools.DatItems
return []; return [];
// Create output list // Create output list
List<DatItem> outfiles = []; List<DatItem> output = [];
// Then deduplicate them by checking to see if data matches previous saved roms // Then deduplicate them by checking to see if data matches previous saved roms
int nodumpCount = 0; int nodumpCount = 0;
foreach (DatItem item in items) foreach (DatItem datItem in items)
{ {
// If we don't have a Disk, File, Media, or Rom, we skip checking for duplicates // If we don't have a Disk, File, Media, or Rom, we skip checking for duplicates
if (item is not Disk && item is not Formats.File && item is not Media && item is not Rom) if (datItem is not Disk && datItem is not Formats.File && datItem is not Media && datItem is not Rom)
continue; continue;
// If it's a nodump, add and skip // If it's a nodump, add and skip
if (item is Rom rom && rom.GetStringFieldValue(Models.Metadata.Rom.StatusKey).AsEnumValue<ItemStatus>() == ItemStatus.Nodump) if (datItem is Rom rom && rom.GetStringFieldValue(Models.Metadata.Rom.StatusKey).AsEnumValue<ItemStatus>() == ItemStatus.Nodump)
{ {
outfiles.Add(item); output.Add(datItem);
nodumpCount++; nodumpCount++;
continue; continue;
} }
else if (item is Disk disk && disk.GetStringFieldValue(Models.Metadata.Disk.StatusKey).AsEnumValue<ItemStatus>() == ItemStatus.Nodump) else if (datItem is Disk disk && disk.GetStringFieldValue(Models.Metadata.Disk.StatusKey).AsEnumValue<ItemStatus>() == ItemStatus.Nodump)
{ {
outfiles.Add(item); output.Add(datItem);
nodumpCount++; nodumpCount++;
continue; continue;
} }
// If it's the first non-nodump item in the list, don't touch it // If it's the first non-nodump item in the list, don't touch it
if (outfiles.Count == nodumpCount) if (output.Count == nodumpCount)
{ {
outfiles.Add(item); output.Add(datItem);
continue; continue;
} }
// Check if the item is a duplicate // Find the index of the first duplicate, if one exists
DupeType dupetype = 0x00; int pos = output.FindIndex(lastItem => datItem.GetDuplicateStatus(lastItem) != 0x00);
DatItem savedItem = new Blank(); if (pos < 0)
int pos = -1;
for (int i = 0; i < outfiles.Count; i++)
{ {
// Get the next item output.Add(datItem);
DatItem lastItem = outfiles[i];
// Get the duplicate status
dupetype = item.GetDuplicateStatus(lastItem);
if (dupetype == 0x00)
continue; continue;
}
// If it's a duplicate, skip adding it to the output but add any missing information // Get the duplicate item
savedItem = lastItem; DatItem savedItem = output[pos];
pos = i; DupeType dupetype = datItem.GetDuplicateStatus(savedItem);
// Disks, File, Media, and Roms have more information to fill // Disks, File, Media, and Roms have more information to fill
if (item is Disk disk && savedItem is Disk savedDisk) if (datItem is Disk diskItem && savedItem is Disk savedDisk)
savedDisk.FillMissingInformation(disk); savedDisk.FillMissingInformation(diskItem);
else if (item is Formats.File fileItem && savedItem is Formats.File savedFile) else if (datItem is Formats.File fileItem && savedItem is Formats.File savedFile)
savedFile.FillMissingInformation(fileItem); savedFile.FillMissingInformation(fileItem);
else if (item is Media media && savedItem is Media savedMedia) else if (datItem is Media mediaItem && savedItem is Media savedMedia)
savedMedia.FillMissingInformation(media); savedMedia.FillMissingInformation(mediaItem);
else if (item is Rom romItem && savedItem is Rom savedRom) else if (datItem is Rom romItem && savedItem is Rom savedRom)
savedRom.FillMissingInformation(romItem); savedRom.FillMissingInformation(romItem);
// Set the duplicate type on the saved item // Set the duplicate type on the saved item
savedItem.SetFieldValue<DupeType>(DatItem.DupeTypeKey, dupetype); savedItem.SetFieldValue<DupeType>(DatItem.DupeTypeKey, dupetype);
break;
// Get the sources associated with the items
var savedSource = savedItem.GetFieldValue<Source?>(DatItem.SourceKey);
var itemSource = datItem.GetFieldValue<Source?>(DatItem.SourceKey);
// Get the machines associated with the items
var savedMachine = savedItem.GetFieldValue<Machine>(DatItem.MachineKey);
var itemMachine = datItem.GetFieldValue<Machine>(DatItem.MachineKey);
// If the current system has a lower ID than the previous, set the system accordingly
if (itemSource?.Index < savedSource?.Index)
{
datItem.SetFieldValue<Source?>(DatItem.SourceKey, savedSource.Clone() as Source);
savedItem.CopyMachineInformation(datItem);
savedItem.SetName(datItem.GetName());
} }
// If no duplicate is found, add it to the list // If the current machine is a child of the new machine, use the new machine instead
if (dupetype == 0x00 || pos < 0) if (savedMachine?.GetStringFieldValue(Models.Metadata.Machine.CloneOfKey) == itemMachine?.GetStringFieldValue(Models.Metadata.Machine.NameKey)
|| savedMachine?.GetStringFieldValue(Models.Metadata.Machine.RomOfKey) == itemMachine?.GetStringFieldValue(Models.Metadata.Machine.NameKey))
{ {
outfiles.Add(item); savedItem.CopyMachineInformation(datItem);
} savedItem.SetName(datItem.GetName());
// Otherwise, if a new rom information is found, add that
else
{
outfiles.RemoveAt(pos);
outfiles.Insert(pos, savedItem);
} }
// Replace the original item in the list
output.RemoveAt(pos);
output.Insert(pos, savedItem);
} }
// Then return the result // Then return the result
return outfiles; return output;
} }
/// <summary> /// <summary>

View File

@@ -99,11 +99,7 @@ namespace SabreTools.DatItems.Formats
/// </summary> /// </summary>
/// <param name="other">Disk to fill information from</param> /// <param name="other">Disk to fill information from</param>
public void FillMissingInformation(Disk other) public void FillMissingInformation(Disk other)
{ => _internal.FillMissingHashes(other._internal);
_internal.FillMissingHashes(other._internal);
ConditionalUpdateSource(other);
ConditionalUpdateMachine(other);
}
/// <summary> /// <summary>
/// Get unique duplicate suffix on name collision /// Get unique duplicate suffix on name collision

View File

@@ -207,9 +207,6 @@ namespace SabreTools.DatItems.Formats
if (_sha256.IsNullOrEmpty() && !other._sha256.IsNullOrEmpty()) if (_sha256.IsNullOrEmpty() && !other._sha256.IsNullOrEmpty())
_sha256 = other._sha256; _sha256 = other._sha256;
ConditionalUpdateSource(other);
ConditionalUpdateMachine(other);
} }
/// <summary> /// <summary>

View File

@@ -60,11 +60,7 @@ namespace SabreTools.DatItems.Formats
/// </summary> /// </summary>
/// <param name="other">Media to fill information from</param> /// <param name="other">Media to fill information from</param>
public void FillMissingInformation(Media other) public void FillMissingInformation(Media other)
{ => _internal.FillMissingHashes(other._internal);
_internal.FillMissingHashes(other._internal);
ConditionalUpdateSource(other);
ConditionalUpdateMachine(other);
}
/// <summary> /// <summary>
/// Get unique duplicate suffix on name collision /// Get unique duplicate suffix on name collision

View File

@@ -103,11 +103,7 @@ namespace SabreTools.DatItems.Formats
/// </summary> /// </summary>
/// <param name="other">Rom to fill information from</param> /// <param name="other">Rom to fill information from</param>
public void FillMissingInformation(Rom other) public void FillMissingInformation(Rom other)
{ => _internal.FillMissingHashes(other._internal);
_internal.FillMissingHashes(other._internal);
ConditionalUpdateSource(other);
ConditionalUpdateMachine(other);
}
/// <summary> /// <summary>
/// Get unique duplicate suffix on name collision /// Get unique duplicate suffix on name collision