2023-08-14 13:17:51 -04:00
|
|
|
using System;
|
2025-04-14 14:06:00 -04:00
|
|
|
using System.Collections.Generic;
|
2023-08-14 13:17:51 -04:00
|
|
|
using System.Linq;
|
2024-03-06 11:23:22 -05:00
|
|
|
using SabreTools.Hashing;
|
2023-09-04 23:51:37 -04:00
|
|
|
using SabreTools.Models.Metadata;
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
namespace SabreTools.Core
|
|
|
|
|
{
|
|
|
|
|
public static class DictionaryBaseExtensions
|
|
|
|
|
{
|
2025-05-02 15:47:26 -04:00
|
|
|
#region Accessors
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the name to use for a DictionaryBase or null
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static string? GetName(this DictionaryBase self)
|
|
|
|
|
{
|
|
|
|
|
if (self == 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,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the name to use for a DictionaryBase or null
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static void SetName(this DictionaryBase self, string? name)
|
|
|
|
|
{
|
|
|
|
|
if (self == 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2023-08-14 13:17:51 -04:00
|
|
|
#region Cloning
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Deep clone a DictionaryBase object
|
|
|
|
|
/// </summary>
|
2025-05-02 15:47:26 -04:00
|
|
|
public static DictionaryBase? Clone(this DictionaryBase self)
|
2023-08-14 13:17:51 -04:00
|
|
|
{
|
|
|
|
|
// If construction failed, we can't do anything
|
2025-05-02 15:47:26 -04:00
|
|
|
if (Activator.CreateInstance(self.GetType()) is not DictionaryBase clone)
|
2023-08-14 13:17:51 -04:00
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
// Loop through and clone per type
|
2025-05-02 15:47:26 -04:00
|
|
|
foreach (string key in self.Keys)
|
2023-08-14 13:17:51 -04:00
|
|
|
{
|
2025-05-02 15:47:26 -04:00
|
|
|
object? value = self[key];
|
2024-02-28 19:19:50 -05:00
|
|
|
clone[key] = value switch
|
2023-08-14 13:17:51 -04:00
|
|
|
{
|
|
|
|
|
// Primative types
|
2024-02-28 19:19:50 -05:00
|
|
|
bool or long or double or string => value,
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
// DictionaryBase types
|
2024-02-28 19:19:50 -05:00
|
|
|
DictionaryBase db => db.Clone(),
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
// Enumerable types
|
2024-02-28 19:19:50 -05:00
|
|
|
byte[] bytArr => bytArr.Clone(),
|
|
|
|
|
string[] strArr => strArr.Clone(),
|
2024-11-12 21:12:06 -05:00
|
|
|
DictionaryBase[] dbArr => Array.ConvertAll(dbArr, Clone),
|
|
|
|
|
ICloneable[] clArr => Array.ConvertAll(clArr, cl => cl.Clone()),
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
// Everything else just copies
|
2024-02-28 19:19:50 -05:00
|
|
|
_ => value,
|
|
|
|
|
};
|
2023-08-14 13:17:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return clone;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2023-08-14 14:53:28 -04:00
|
|
|
#region Conversion
|
2024-03-04 22:18:14 -05:00
|
|
|
|
2023-08-14 22:33:05 -04:00
|
|
|
/// <summary>
|
2024-03-12 22:26:54 -04:00
|
|
|
/// Convert a DictionaryBase to a Rom
|
2023-08-14 22:33:05 -04:00
|
|
|
/// </summary>
|
2024-03-12 22:26:54 -04:00
|
|
|
public static Rom? ConvertToRom(this DictionaryBase? self)
|
2023-08-14 22:33:05 -04:00
|
|
|
{
|
|
|
|
|
// If the DatItem is missing, we can't do anything
|
|
|
|
|
if (self == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
return self switch
|
|
|
|
|
{
|
|
|
|
|
Disk diskSelf => ConvertToRom(diskSelf),
|
|
|
|
|
Media mediaSelf => ConvertToRom(mediaSelf),
|
|
|
|
|
_ => null,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-14 14:53:28 -04:00
|
|
|
/// <summary>
|
|
|
|
|
/// Convert a Disk to a Rom
|
|
|
|
|
/// </summary>
|
2023-08-14 22:33:05 -04:00
|
|
|
private static Rom? ConvertToRom(this Disk? disk)
|
2023-08-14 14:53:28 -04:00
|
|
|
{
|
|
|
|
|
// If the Disk is missing, we can't do anything
|
|
|
|
|
if (disk == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
2025-01-04 19:47:39 -05:00
|
|
|
// Append a suffix to the name
|
|
|
|
|
string? name = disk.ReadString(Disk.NameKey);
|
|
|
|
|
if (name != null)
|
|
|
|
|
name += ".chd";
|
|
|
|
|
|
2023-08-14 14:53:28 -04:00
|
|
|
return new Rom
|
|
|
|
|
{
|
2025-01-04 19:47:39 -05:00
|
|
|
[Rom.NameKey] = name,
|
2023-08-14 14:53:28 -04:00
|
|
|
[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),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Convert a Media to a Rom
|
|
|
|
|
/// </summary>
|
2023-08-14 22:33:05 -04:00
|
|
|
private static Rom? ConvertToRom(this Media? media)
|
2023-08-14 14:53:28 -04:00
|
|
|
{
|
|
|
|
|
// If the Media is missing, we can't do anything
|
|
|
|
|
if (media == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
2025-01-04 19:47:39 -05:00
|
|
|
// Append a suffix to the name
|
|
|
|
|
string? name = media.ReadString(Media.NameKey);
|
|
|
|
|
if (name != null)
|
|
|
|
|
name += ".aaruf";
|
|
|
|
|
|
2023-08-14 14:53:28 -04:00
|
|
|
return new Rom
|
|
|
|
|
{
|
2025-01-04 19:47:39 -05:00
|
|
|
[Rom.NameKey] = name,
|
2023-08-14 14:53:28 -04:00
|
|
|
[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
|
|
|
|
|
|
2023-08-14 13:17:51 -04:00
|
|
|
#region Equality Checking
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Check equality of two DictionaryBase objects
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static bool EqualTo(this DictionaryBase self, DictionaryBase other)
|
|
|
|
|
{
|
|
|
|
|
// Check types first
|
|
|
|
|
if (self.GetType() != other.GetType())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Check based on the item type
|
|
|
|
|
return (self, other) switch
|
|
|
|
|
{
|
|
|
|
|
(Disk diskSelf, Disk diskOther) => EqualsImpl(diskSelf, diskOther),
|
|
|
|
|
(Media mediaSelf, Media mediaOther) => EqualsImpl(mediaSelf, mediaOther),
|
|
|
|
|
(Rom romSelf, Rom romOther) => EqualsImpl(romSelf, romOther),
|
|
|
|
|
_ => EqualsImpl(self, other),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Check equality of two DictionaryBase objects
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static bool EqualsImpl(this DictionaryBase self, DictionaryBase other)
|
|
|
|
|
{
|
|
|
|
|
// If the number of key-value pairs doesn't match, they can't match
|
|
|
|
|
if (self.Count != other.Count)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// If any keys are missing on either side, they can't match
|
2025-04-14 14:06:00 -04:00
|
|
|
var selfKeys = new HashSet<string>(self.Keys);
|
|
|
|
|
var otherKeys = new HashSet<string>(other.Keys);
|
|
|
|
|
if (!selfKeys.SetEquals(otherKeys))
|
2023-08-14 13:17:51 -04:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Check all pairs to see if they're equal
|
|
|
|
|
foreach (var kvp in self)
|
|
|
|
|
{
|
|
|
|
|
switch (kvp.Value, other[kvp.Key])
|
|
|
|
|
{
|
|
|
|
|
case (string selfString, string otherString):
|
|
|
|
|
if (!string.Equals(selfString, otherString, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
|
2025-01-08 11:36:43 -05:00
|
|
|
case (ModelBackedItem selfMbi, ModelBackedItem otherMbi):
|
|
|
|
|
if (!selfMbi.Equals(otherMbi))
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
|
2023-08-14 13:17:51 -04:00
|
|
|
case (DictionaryBase selfDb, DictionaryBase otherDb):
|
|
|
|
|
if (!selfDb.Equals(otherDb))
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// TODO: Make this case-insensitive
|
|
|
|
|
case (string[] selfStrArr, string[] otherStrArr):
|
|
|
|
|
if (selfStrArr.Length != otherStrArr.Length)
|
|
|
|
|
return false;
|
|
|
|
|
if (selfStrArr.Except(otherStrArr).Any())
|
|
|
|
|
return false;
|
|
|
|
|
if (otherStrArr.Except(selfStrArr).Any())
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// TODO: Fix the logic here for real equality
|
|
|
|
|
case (DictionaryBase[] selfDbArr, DictionaryBase[] otherDbArr):
|
|
|
|
|
if (selfDbArr.Length != otherDbArr.Length)
|
|
|
|
|
return false;
|
|
|
|
|
if (selfDbArr.Except(otherDbArr).Any())
|
|
|
|
|
return false;
|
|
|
|
|
if (otherDbArr.Except(selfDbArr).Any())
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2025-01-04 19:47:39 -05:00
|
|
|
// Handle cases where a null is involved
|
|
|
|
|
if (kvp.Value == null && other[kvp.Key] == null)
|
|
|
|
|
return true;
|
|
|
|
|
else if (kvp.Value == null && other[kvp.Key] != null)
|
|
|
|
|
return false;
|
|
|
|
|
else if (kvp.Value != null && other[kvp.Key] == null)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Try to rely on type hashes
|
|
|
|
|
else if (kvp.Value!.GetHashCode() != other[kvp.Key]!.GetHashCode())
|
2023-08-14 13:17:51 -04:00
|
|
|
return false;
|
2025-01-04 19:47:39 -05:00
|
|
|
|
2023-08-14 13:17:51 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Check equality of two Disk objects
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static bool EqualsImpl(this Disk self, Disk other)
|
|
|
|
|
{
|
|
|
|
|
string? selfStatus = self.ReadString(Disk.StatusKey);
|
|
|
|
|
string? otherStatus = other.ReadString(Disk.StatusKey);
|
|
|
|
|
|
|
|
|
|
string? selfName = self.ReadString(Disk.NameKey);
|
|
|
|
|
string? otherName = other.ReadString(Disk.NameKey);
|
|
|
|
|
|
|
|
|
|
// If all hashes are empty but they're both nodump and the names match, then they're dupes
|
|
|
|
|
if (string.Equals(selfStatus, "nodump", StringComparison.OrdinalIgnoreCase)
|
|
|
|
|
&& string.Equals(otherStatus, "nodump", StringComparison.OrdinalIgnoreCase)
|
|
|
|
|
&& string.Equals(selfName, otherName, StringComparison.OrdinalIgnoreCase)
|
|
|
|
|
&& !self.HasHashes()
|
|
|
|
|
&& !other.HasHashes())
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we get a partial match
|
|
|
|
|
if (self.HashMatch(other))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// All other cases fail
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Check equality of two Media objects
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static bool EqualsImpl(this Media self, Media other)
|
|
|
|
|
{
|
|
|
|
|
// If we get a partial match
|
|
|
|
|
if (self.HashMatch(other))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// All other cases fail
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Check equality of two Rom objects
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static bool EqualsImpl(this Rom self, Rom other)
|
|
|
|
|
{
|
|
|
|
|
string? selfStatus = self.ReadString(Rom.StatusKey);
|
|
|
|
|
string? otherStatus = other.ReadString(Rom.StatusKey);
|
|
|
|
|
|
|
|
|
|
string? selfName = self.ReadString(Rom.NameKey);
|
|
|
|
|
string? otherName = other.ReadString(Rom.NameKey);
|
|
|
|
|
|
|
|
|
|
long? selfSize = self.ReadLong(Rom.SizeKey);
|
|
|
|
|
long? otherSize = other.ReadLong(Rom.SizeKey);
|
|
|
|
|
|
|
|
|
|
// If all hashes are empty but they're both nodump and the names match, then they're dupes
|
|
|
|
|
if (string.Equals(selfStatus, "nodump", StringComparison.OrdinalIgnoreCase)
|
|
|
|
|
&& string.Equals(otherStatus, "nodump", StringComparison.OrdinalIgnoreCase)
|
|
|
|
|
&& string.Equals(selfName, otherName, StringComparison.OrdinalIgnoreCase)
|
|
|
|
|
&& !self.HasHashes()
|
|
|
|
|
&& !other.HasHashes())
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we have a file that has no known size, rely on the hashes only
|
|
|
|
|
if (selfSize == null && self.HashMatch(other))
|
2024-03-04 22:18:14 -05:00
|
|
|
return true;
|
2025-01-04 19:47:39 -05:00
|
|
|
else if (otherSize == null && self.HashMatch(other))
|
|
|
|
|
return true;
|
2023-08-14 13:17:51 -04:00
|
|
|
|
2024-03-04 22:18:14 -05:00
|
|
|
// If we get a partial match
|
|
|
|
|
if (selfSize == otherSize && self.HashMatch(other))
|
|
|
|
|
return true;
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
// All other cases fail
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Hash Checking
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if any hashes are common
|
|
|
|
|
/// </summary>
|
|
|
|
|
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
|
2024-10-20 11:37:07 -04:00
|
|
|
string? selfMd5 = self.ReadString(Disk.MD5Key);
|
|
|
|
|
string? otherMd5 = other.ReadString(Disk.MD5Key);
|
|
|
|
|
bool conditionalMd5 = Tools.Utilities.ConditionalHashEquals(selfMd5, otherMd5);
|
|
|
|
|
|
|
|
|
|
string? selfSha1 = self.ReadString(Disk.SHA1Key);
|
|
|
|
|
string? otherSha1 = other.ReadString(Disk.SHA1Key);
|
|
|
|
|
bool conditionalSha1 = Tools.Utilities.ConditionalHashEquals(selfSha1, otherSha1);
|
|
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
return conditionalMd5
|
|
|
|
|
&& conditionalSha1;
|
2023-08-14 13:17:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if any hashes are common
|
|
|
|
|
/// </summary>
|
|
|
|
|
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
|
2024-10-20 11:37:07 -04:00
|
|
|
string? selfMd5 = self.ReadString(Media.MD5Key);
|
|
|
|
|
string? otherMd5 = other.ReadString(Media.MD5Key);
|
|
|
|
|
bool conditionalMd5 = Tools.Utilities.ConditionalHashEquals(selfMd5, otherMd5);
|
|
|
|
|
|
|
|
|
|
string? selfSha1 = self.ReadString(Media.SHA1Key);
|
|
|
|
|
string? otherSha1 = other.ReadString(Media.SHA1Key);
|
|
|
|
|
bool conditionalSha1 = Tools.Utilities.ConditionalHashEquals(selfSha1, otherSha1);
|
|
|
|
|
|
|
|
|
|
string? selfSha256 = self.ReadString(Media.SHA256Key);
|
|
|
|
|
string? otherSha256 = other.ReadString(Media.SHA256Key);
|
|
|
|
|
bool conditionalSha256 = Tools.Utilities.ConditionalHashEquals(selfSha256, otherSha256);
|
|
|
|
|
|
|
|
|
|
string? selfSpamSum = self.ReadString(Media.SpamSumKey);
|
|
|
|
|
string? otherSpamSum = other.ReadString(Media.SpamSumKey);
|
|
|
|
|
bool conditionalSpamSum = Tools.Utilities.ConditionalHashEquals(selfSpamSum, otherSpamSum);
|
|
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
return conditionalMd5
|
|
|
|
|
&& conditionalSha1
|
|
|
|
|
&& conditionalSha256
|
|
|
|
|
&& conditionalSpamSum;
|
2023-08-14 13:17:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if any hashes are common
|
|
|
|
|
/// </summary>
|
|
|
|
|
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
|
2024-10-20 11:37:07 -04:00
|
|
|
string? selfCrc = self.ReadString(Rom.CRCKey);
|
|
|
|
|
string? otherCrc = other.ReadString(Rom.CRCKey);
|
|
|
|
|
bool conditionalCrc = Tools.Utilities.ConditionalHashEquals(selfCrc, otherCrc);
|
|
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
string? selfMd2 = self.ReadString(Rom.MD2Key);
|
|
|
|
|
string? otherMd2 = other.ReadString(Rom.MD2Key);
|
|
|
|
|
bool conditionalMd2 = Tools.Utilities.ConditionalHashEquals(selfMd2, otherMd2);
|
|
|
|
|
|
|
|
|
|
string? selfMd4 = self.ReadString(Rom.MD4Key);
|
|
|
|
|
string? otherMd4 = other.ReadString(Rom.MD4Key);
|
|
|
|
|
bool conditionalMd4 = Tools.Utilities.ConditionalHashEquals(selfMd4, otherMd4);
|
|
|
|
|
|
2024-10-20 11:37:07 -04:00
|
|
|
string? selfMd5 = self.ReadString(Rom.MD5Key);
|
|
|
|
|
string? otherMd5 = other.ReadString(Rom.MD5Key);
|
|
|
|
|
bool conditionalMd5 = Tools.Utilities.ConditionalHashEquals(selfMd5, otherMd5);
|
|
|
|
|
|
|
|
|
|
string? selfSha1 = self.ReadString(Rom.SHA1Key);
|
|
|
|
|
string? otherSha1 = other.ReadString(Rom.SHA1Key);
|
|
|
|
|
bool conditionalSha1 = Tools.Utilities.ConditionalHashEquals(selfSha1, otherSha1);
|
|
|
|
|
|
|
|
|
|
string? selfSha256 = self.ReadString(Rom.SHA256Key);
|
|
|
|
|
string? otherSha256 = other.ReadString(Rom.SHA256Key);
|
|
|
|
|
bool conditionalSha256 = Tools.Utilities.ConditionalHashEquals(selfSha256, otherSha256);
|
|
|
|
|
|
|
|
|
|
string? selfSha384 = self.ReadString(Rom.SHA384Key);
|
|
|
|
|
string? otherSha384 = other.ReadString(Rom.SHA384Key);
|
|
|
|
|
bool conditionalSha384 = Tools.Utilities.ConditionalHashEquals(selfSha384, otherSha384);
|
|
|
|
|
|
|
|
|
|
string? selfSha512 = self.ReadString(Rom.SHA512Key);
|
|
|
|
|
string? otherSha512 = other.ReadString(Rom.SHA512Key);
|
|
|
|
|
bool conditionalSha512 = Tools.Utilities.ConditionalHashEquals(selfSha512, otherSha512);
|
|
|
|
|
|
|
|
|
|
string? selfSpamSum = self.ReadString(Rom.SpamSumKey);
|
|
|
|
|
string? otherSpamSum = other.ReadString(Rom.SpamSumKey);
|
|
|
|
|
bool conditionalSpamSum = Tools.Utilities.ConditionalHashEquals(selfSpamSum, otherSpamSum);
|
|
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
return conditionalCrc
|
|
|
|
|
&& conditionalMd2
|
|
|
|
|
&& conditionalMd4
|
|
|
|
|
&& conditionalMd5
|
|
|
|
|
&& conditionalSha1
|
|
|
|
|
&& conditionalSha256
|
|
|
|
|
&& conditionalSha384
|
|
|
|
|
&& conditionalSha512
|
|
|
|
|
&& conditionalSpamSum;
|
2023-08-14 13:17:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if any hashes exist
|
|
|
|
|
/// </summary>
|
2024-03-12 22:26:54 -04:00
|
|
|
public static bool HasHashes(this DictionaryBase self)
|
2023-08-14 22:33:05 -04:00
|
|
|
{
|
|
|
|
|
return self switch
|
|
|
|
|
{
|
|
|
|
|
Disk diskSelf => diskSelf.HasHashes(),
|
|
|
|
|
Media mediaSelf => mediaSelf.HasHashes(),
|
|
|
|
|
Rom romSelf => romSelf.HasHashes(),
|
|
|
|
|
_ => false,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if all of the hashes are set to their 0-byte values or null
|
|
|
|
|
/// </summary>
|
2024-03-12 22:26:54 -04:00
|
|
|
public static bool HasZeroHash(this DictionaryBase self)
|
2023-08-14 22:33:05 -04:00
|
|
|
{
|
|
|
|
|
return self switch
|
|
|
|
|
{
|
|
|
|
|
Disk diskSelf => diskSelf.HasZeroHash(),
|
|
|
|
|
Media mediaSelf => mediaSelf.HasZeroHash(),
|
|
|
|
|
Rom romSelf => romSelf.HasZeroHash(),
|
|
|
|
|
_ => false,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if there are no, non-empty hashes in common
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static bool HasCommonHash(this Disk self, Disk other)
|
|
|
|
|
{
|
2024-02-28 19:49:09 -05:00
|
|
|
bool md5Null = string.IsNullOrEmpty(self.ReadString(Disk.MD5Key));
|
|
|
|
|
md5Null ^= string.IsNullOrEmpty(other.ReadString(Disk.MD5Key));
|
2023-08-14 22:33:05 -04:00
|
|
|
|
2024-02-28 19:49:09 -05:00
|
|
|
bool sha1Null = string.IsNullOrEmpty(self.ReadString(Disk.SHA1Key));
|
|
|
|
|
sha1Null ^= string.IsNullOrEmpty(other.ReadString(Disk.SHA1Key));
|
2023-08-14 22:33:05 -04:00
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
return !md5Null
|
|
|
|
|
|| !sha1Null;
|
2023-08-14 22:33:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if there are no, non-empty hashes in common
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static bool HasCommonHash(this Media self, Media other)
|
|
|
|
|
{
|
2024-02-28 19:49:09 -05:00
|
|
|
bool md5Null = string.IsNullOrEmpty(self.ReadString(Media.MD5Key));
|
|
|
|
|
md5Null ^= string.IsNullOrEmpty(other.ReadString(Media.MD5Key));
|
2023-08-14 22:33:05 -04:00
|
|
|
|
2024-02-28 19:49:09 -05:00
|
|
|
bool sha1Null = string.IsNullOrEmpty(self.ReadString(Media.SHA1Key));
|
|
|
|
|
sha1Null ^= string.IsNullOrEmpty(other.ReadString(Media.SHA1Key));
|
2023-08-14 22:33:05 -04:00
|
|
|
|
2024-02-28 19:49:09 -05:00
|
|
|
bool sha256Null = string.IsNullOrEmpty(self.ReadString(Media.SHA256Key));
|
|
|
|
|
sha256Null ^= string.IsNullOrEmpty(other.ReadString(Media.SHA256Key));
|
2023-08-14 22:33:05 -04:00
|
|
|
|
2024-02-28 19:49:09 -05:00
|
|
|
bool spamsumNull = string.IsNullOrEmpty(self.ReadString(Media.SpamSumKey));
|
|
|
|
|
spamsumNull ^= string.IsNullOrEmpty(other.ReadString(Media.SpamSumKey));
|
2023-08-14 22:33:05 -04:00
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
return !md5Null
|
|
|
|
|
|| !sha1Null
|
|
|
|
|
|| !sha256Null
|
|
|
|
|
|| !spamsumNull;
|
2023-08-14 22:33:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if there are no, non-empty hashes in common
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static bool HasCommonHash(this Rom self, Rom other)
|
|
|
|
|
{
|
2024-02-28 19:49:09 -05:00
|
|
|
bool crcNull = string.IsNullOrEmpty(self.ReadString(Rom.CRCKey));
|
|
|
|
|
crcNull ^= string.IsNullOrEmpty(other.ReadString(Rom.CRCKey));
|
2023-08-14 22:33:05 -04:00
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
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));
|
|
|
|
|
|
2024-02-28 19:49:09 -05:00
|
|
|
bool md5Null = string.IsNullOrEmpty(self.ReadString(Rom.MD5Key));
|
|
|
|
|
md5Null ^= string.IsNullOrEmpty(other.ReadString(Rom.MD5Key));
|
2023-08-14 22:33:05 -04:00
|
|
|
|
2024-02-28 19:49:09 -05:00
|
|
|
bool sha1Null = string.IsNullOrEmpty(self.ReadString(Rom.SHA1Key));
|
|
|
|
|
sha1Null ^= string.IsNullOrEmpty(other.ReadString(Rom.SHA1Key));
|
2023-08-14 22:33:05 -04:00
|
|
|
|
2024-02-28 19:49:09 -05:00
|
|
|
bool sha256Null = string.IsNullOrEmpty(self.ReadString(Rom.SHA256Key));
|
|
|
|
|
sha256Null ^= string.IsNullOrEmpty(other.ReadString(Rom.SHA256Key));
|
2023-08-14 22:33:05 -04:00
|
|
|
|
2024-02-28 19:49:09 -05:00
|
|
|
bool sha384Null = string.IsNullOrEmpty(self.ReadString(Rom.SHA384Key));
|
|
|
|
|
sha384Null ^= string.IsNullOrEmpty(other.ReadString(Rom.SHA384Key));
|
2023-08-14 22:33:05 -04:00
|
|
|
|
2024-02-28 19:49:09 -05:00
|
|
|
bool sha512Null = string.IsNullOrEmpty(self.ReadString(Rom.SHA512Key));
|
|
|
|
|
sha512Null ^= string.IsNullOrEmpty(other.ReadString(Rom.SHA512Key));
|
2023-08-14 22:33:05 -04:00
|
|
|
|
2024-02-28 19:49:09 -05:00
|
|
|
bool spamsumNull = string.IsNullOrEmpty(self.ReadString(Rom.SpamSumKey));
|
|
|
|
|
spamsumNull ^= string.IsNullOrEmpty(other.ReadString(Rom.SpamSumKey));
|
2023-08-14 22:33:05 -04:00
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
return !crcNull
|
|
|
|
|
|| !md2Null
|
|
|
|
|
|| !md4Null
|
|
|
|
|
|| !md5Null
|
|
|
|
|
|| !sha1Null
|
|
|
|
|
|| !sha256Null
|
|
|
|
|
|| !sha384Null
|
|
|
|
|
|| !sha512Null
|
|
|
|
|
|| !spamsumNull;
|
2023-08-14 22:33:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if any hashes exist
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static bool HasHashes(this Disk disk)
|
2023-08-14 13:17:51 -04:00
|
|
|
{
|
2024-02-28 19:49:09 -05:00
|
|
|
bool md5Null = string.IsNullOrEmpty(disk.ReadString(Disk.MD5Key));
|
|
|
|
|
bool sha1Null = string.IsNullOrEmpty(disk.ReadString(Disk.SHA1Key));
|
2023-08-14 13:17:51 -04:00
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
return !md5Null
|
|
|
|
|
|| !sha1Null;
|
2023-08-14 13:17:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if any hashes exist
|
|
|
|
|
/// </summary>
|
2023-08-14 22:33:05 -04:00
|
|
|
private static bool HasHashes(this Media media)
|
2023-08-14 13:17:51 -04:00
|
|
|
{
|
2024-02-28 19:49:09 -05:00
|
|
|
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));
|
2023-08-14 13:17:51 -04:00
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
return !md5Null
|
|
|
|
|
|| !sha1Null
|
|
|
|
|
|| !sha256Null
|
|
|
|
|
|| !spamsumNull;
|
2023-08-14 13:17:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if any hashes exist
|
|
|
|
|
/// </summary>
|
2023-08-14 22:33:05 -04:00
|
|
|
private static bool HasHashes(this Rom rom)
|
2023-08-14 13:17:51 -04:00
|
|
|
{
|
2024-02-28 19:49:09 -05:00
|
|
|
bool crcNull = string.IsNullOrEmpty(rom.ReadString(Rom.CRCKey));
|
2025-01-09 05:26:36 -05:00
|
|
|
bool md2Null = string.IsNullOrEmpty(rom.ReadString(Rom.MD2Key));
|
|
|
|
|
bool md4Null = string.IsNullOrEmpty(rom.ReadString(Rom.MD4Key));
|
2024-02-28 19:49:09 -05:00
|
|
|
bool md5Null = string.IsNullOrEmpty(rom.ReadString(Rom.MD5Key));
|
|
|
|
|
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));
|
2023-08-14 13:17:51 -04:00
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
return !crcNull
|
|
|
|
|
|| !md2Null
|
|
|
|
|
|| !md4Null
|
|
|
|
|
|| !md5Null
|
|
|
|
|
|| !sha1Null
|
|
|
|
|
|| !sha256Null
|
|
|
|
|
|| !sha384Null
|
|
|
|
|
|| !sha512Null
|
|
|
|
|
|| !spamsumNull;
|
2023-08-14 13:17:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if all of the hashes are set to their 0-byte values or null
|
|
|
|
|
/// </summary>
|
2023-08-14 22:33:05 -04:00
|
|
|
private static bool HasZeroHash(this Disk disk)
|
2023-08-14 13:17:51 -04:00
|
|
|
{
|
|
|
|
|
string? md5 = disk.ReadString(Disk.MD5Key);
|
2024-11-13 03:55:33 -05:00
|
|
|
bool md5Null = string.IsNullOrEmpty(md5) || string.Equals(md5, ZeroHash.MD5Str, StringComparison.OrdinalIgnoreCase);
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
string? sha1 = disk.ReadString(Disk.SHA1Key);
|
2024-11-13 03:55:33 -05:00
|
|
|
bool sha1Null = string.IsNullOrEmpty(sha1) || string.Equals(sha1, ZeroHash.SHA1Str, StringComparison.OrdinalIgnoreCase);
|
2023-08-14 13:17:51 -04:00
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
return md5Null
|
|
|
|
|
&& sha1Null;
|
2023-08-14 13:17:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if all of the hashes are set to their 0-byte values or null
|
|
|
|
|
/// </summary>
|
2023-08-14 22:33:05 -04:00
|
|
|
private static bool HasZeroHash(this Media media)
|
2023-08-14 13:17:51 -04:00
|
|
|
{
|
|
|
|
|
string? md5 = media.ReadString(Media.MD5Key);
|
2024-11-13 03:55:33 -05:00
|
|
|
bool md5Null = string.IsNullOrEmpty(md5) || string.Equals(md5, ZeroHash.MD5Str, StringComparison.OrdinalIgnoreCase);
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
string? sha1 = media.ReadString(Media.SHA1Key);
|
2024-11-13 03:55:33 -05:00
|
|
|
bool sha1Null = string.IsNullOrEmpty(sha1) || string.Equals(sha1, ZeroHash.SHA1Str, StringComparison.OrdinalIgnoreCase);
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
string? sha256 = media.ReadString(Media.SHA256Key);
|
2024-11-13 03:55:33 -05:00
|
|
|
bool sha256Null = string.IsNullOrEmpty(sha256) || string.Equals(sha256, ZeroHash.SHA256Str, StringComparison.OrdinalIgnoreCase);
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
string? spamsum = media.ReadString(Media.SpamSumKey);
|
2024-11-13 03:55:33 -05:00
|
|
|
bool spamsumNull = string.IsNullOrEmpty(spamsum) || string.Equals(spamsum, ZeroHash.SpamSumStr, StringComparison.OrdinalIgnoreCase);
|
2023-08-14 13:17:51 -04:00
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
return md5Null
|
|
|
|
|
&& sha1Null
|
|
|
|
|
&& sha256Null
|
|
|
|
|
&& spamsumNull;
|
2023-08-14 13:17:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if all of the hashes are set to their 0-byte values or null
|
|
|
|
|
/// </summary>
|
2023-08-14 22:33:05 -04:00
|
|
|
private static bool HasZeroHash(this Rom rom)
|
2023-08-14 13:17:51 -04:00
|
|
|
{
|
|
|
|
|
string? crc = rom.ReadString(Rom.CRCKey);
|
2024-11-13 03:55:33 -05:00
|
|
|
bool crcNull = string.IsNullOrEmpty(crc) || string.Equals(crc, ZeroHash.CRC32Str, StringComparison.OrdinalIgnoreCase);
|
2023-08-14 13:17:51 -04:00
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
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);
|
|
|
|
|
|
2023-08-14 13:17:51 -04:00
|
|
|
string? md5 = rom.ReadString(Rom.MD5Key);
|
2024-11-13 03:55:33 -05:00
|
|
|
bool md5Null = string.IsNullOrEmpty(md5) || string.Equals(md5, ZeroHash.MD5Str, StringComparison.OrdinalIgnoreCase);
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
string? sha1 = rom.ReadString(Rom.SHA1Key);
|
2024-11-13 03:55:33 -05:00
|
|
|
bool sha1Null = string.IsNullOrEmpty(sha1) || string.Equals(sha1, ZeroHash.SHA1Str, StringComparison.OrdinalIgnoreCase);
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
string? sha256 = rom.ReadString(Rom.SHA256Key);
|
2024-11-13 03:55:33 -05:00
|
|
|
bool sha256Null = string.IsNullOrEmpty(sha256) || string.Equals(sha256, ZeroHash.SHA256Str, StringComparison.OrdinalIgnoreCase);
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
string? sha384 = rom.ReadString(Rom.SHA384Key);
|
2024-11-13 03:55:33 -05:00
|
|
|
bool sha384Null = string.IsNullOrEmpty(sha384) || string.Equals(sha384, ZeroHash.SHA384Str, StringComparison.OrdinalIgnoreCase);
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
string? sha512 = rom.ReadString(Rom.SHA512Key);
|
2024-11-13 03:55:33 -05:00
|
|
|
bool sha512Null = string.IsNullOrEmpty(sha512) || string.Equals(sha512, ZeroHash.SHA512Str, StringComparison.OrdinalIgnoreCase);
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
string? spamsum = rom.ReadString(Rom.SpamSumKey);
|
2024-11-13 03:55:33 -05:00
|
|
|
bool spamsumNull = string.IsNullOrEmpty(spamsum) || string.Equals(spamsum, ZeroHash.SpamSumStr, StringComparison.OrdinalIgnoreCase);
|
2023-08-14 13:17:51 -04:00
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
return crcNull
|
|
|
|
|
&& md2Null
|
|
|
|
|
&& md4Null
|
|
|
|
|
&& md5Null
|
|
|
|
|
&& sha1Null
|
|
|
|
|
&& sha256Null
|
|
|
|
|
&& sha384Null
|
|
|
|
|
&& sha512Null
|
|
|
|
|
&& spamsumNull;
|
2023-08-14 13:17:51 -04:00
|
|
|
}
|
|
|
|
|
|
2023-08-14 22:33:05 -04:00
|
|
|
#endregion
|
2024-03-04 22:18:14 -05:00
|
|
|
|
2023-08-14 22:33:05 -04:00
|
|
|
#region Information Filling
|
2023-08-14 13:17:51 -04:00
|
|
|
|
|
|
|
|
/// <summary>
|
2024-03-12 22:26:54 -04:00
|
|
|
/// Fill any missing size and hash information from another DictionaryBase
|
2023-08-14 13:17:51 -04:00
|
|
|
/// </summary>
|
2024-03-12 22:26:54 -04:00
|
|
|
public static void FillMissingHashes(this DictionaryBase? self, DictionaryBase? other)
|
2023-08-14 13:17:51 -04:00
|
|
|
{
|
2023-08-14 22:33:05 -04:00
|
|
|
if (self == null || other == null)
|
|
|
|
|
return;
|
2023-08-14 13:17:51 -04:00
|
|
|
|
2023-08-14 22:33:05 -04:00
|
|
|
switch (self, other)
|
|
|
|
|
{
|
|
|
|
|
case (Disk diskSelf, Disk diskOther):
|
2024-12-06 23:21:03 -05:00
|
|
|
diskSelf.FillMissingHashes(diskOther);
|
2023-08-14 22:33:05 -04:00
|
|
|
break;
|
|
|
|
|
case (Media mediaSelf, Media mediaOther):
|
2024-12-06 23:21:03 -05:00
|
|
|
mediaSelf.FillMissingHashes(mediaOther);
|
2023-08-14 22:33:05 -04:00
|
|
|
break;
|
|
|
|
|
case (Rom romSelf, Rom romOther):
|
2024-12-06 23:21:03 -05:00
|
|
|
romSelf.FillMissingHashes(romOther);
|
2023-08-14 22:33:05 -04:00
|
|
|
break;
|
2025-04-14 14:06:00 -04:00
|
|
|
}
|
2023-08-14 13:17:51 -04:00
|
|
|
}
|
|
|
|
|
|
2023-08-14 19:06:08 -04:00
|
|
|
/// <summary>
|
|
|
|
|
/// Fill any missing size and hash information from another Disk
|
|
|
|
|
/// </summary>
|
2023-08-14 22:33:05 -04:00
|
|
|
private static void FillMissingHashes(this Disk? self, Disk? other)
|
2023-08-14 19:06:08 -04:00
|
|
|
{
|
|
|
|
|
if (self == null || other == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
string? selfMd5 = self.ReadString(Disk.MD5Key);
|
|
|
|
|
string? otherMd5 = other.ReadString(Disk.MD5Key);
|
2024-02-28 19:49:09 -05:00
|
|
|
if (string.IsNullOrEmpty(selfMd5) && !string.IsNullOrEmpty(otherMd5))
|
2023-08-14 19:06:08 -04:00
|
|
|
self[Disk.MD5Key] = otherMd5;
|
|
|
|
|
|
|
|
|
|
string? selfSha1 = self.ReadString(Disk.SHA1Key);
|
|
|
|
|
string? otherSha1 = other.ReadString(Disk.SHA1Key);
|
2024-02-28 19:49:09 -05:00
|
|
|
if (string.IsNullOrEmpty(selfSha1) && !string.IsNullOrEmpty(otherSha1))
|
2023-08-14 19:06:08 -04:00
|
|
|
self[Disk.SHA1Key] = otherSha1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Fill any missing size and hash information from another Media
|
|
|
|
|
/// </summary>
|
2023-08-14 22:33:05 -04:00
|
|
|
private static void FillMissingHashes(this Media? self, Media? other)
|
2023-08-14 19:06:08 -04:00
|
|
|
{
|
|
|
|
|
if (self == null || other == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
string? selfMd5 = self.ReadString(Media.MD5Key);
|
|
|
|
|
string? otherMd5 = other.ReadString(Media.MD5Key);
|
2024-02-28 19:49:09 -05:00
|
|
|
if (string.IsNullOrEmpty(selfMd5) && !string.IsNullOrEmpty(otherMd5))
|
2023-08-14 19:06:08 -04:00
|
|
|
self[Media.MD5Key] = otherMd5;
|
|
|
|
|
|
|
|
|
|
string? selfSha1 = self.ReadString(Media.SHA1Key);
|
|
|
|
|
string? otherSha1 = other.ReadString(Media.SHA1Key);
|
2024-02-28 19:49:09 -05:00
|
|
|
if (string.IsNullOrEmpty(selfSha1) && !string.IsNullOrEmpty(otherSha1))
|
2023-08-14 19:06:08 -04:00
|
|
|
self[Media.SHA1Key] = otherSha1;
|
|
|
|
|
|
|
|
|
|
string? selfSha256 = self.ReadString(Media.SHA256Key);
|
|
|
|
|
string? otherSha256 = other.ReadString(Media.SHA256Key);
|
2024-02-28 19:49:09 -05:00
|
|
|
if (string.IsNullOrEmpty(selfSha256) && !string.IsNullOrEmpty(otherSha256))
|
2023-08-14 19:06:08 -04:00
|
|
|
self[Media.SHA256Key] = otherSha256;
|
|
|
|
|
|
|
|
|
|
string? selfSpamSum = self.ReadString(Media.SpamSumKey);
|
|
|
|
|
string? otherSpamSum = other.ReadString(Media.SpamSumKey);
|
2024-02-28 19:49:09 -05:00
|
|
|
if (string.IsNullOrEmpty(selfSpamSum) && !string.IsNullOrEmpty(otherSpamSum))
|
2023-08-14 19:06:08 -04:00
|
|
|
self[Media.SpamSumKey] = otherSpamSum;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Fill any missing size and hash information from another Rom
|
|
|
|
|
/// </summary>
|
2023-08-14 22:33:05 -04:00
|
|
|
private static void FillMissingHashes(this Rom? self, Rom? other)
|
2023-08-14 19:06:08 -04:00
|
|
|
{
|
|
|
|
|
if (self == null || other == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
long? selfSize = self.ReadLong(Rom.SizeKey);
|
|
|
|
|
long? otherSize = other.ReadLong(Rom.SizeKey);
|
|
|
|
|
if (selfSize == null && otherSize != null)
|
|
|
|
|
self[Rom.SizeKey] = otherSize;
|
|
|
|
|
|
|
|
|
|
string? selfCrc = self.ReadString(Rom.CRCKey);
|
|
|
|
|
string? otherCrc = other.ReadString(Rom.CRCKey);
|
2024-02-28 19:49:09 -05:00
|
|
|
if (string.IsNullOrEmpty(selfCrc) && !string.IsNullOrEmpty(otherCrc))
|
2023-08-14 19:06:08 -04:00
|
|
|
self[Rom.CRCKey] = otherCrc;
|
|
|
|
|
|
2025-01-09 05:26:36 -05:00
|
|
|
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;
|
|
|
|
|
|
2023-08-14 19:06:08 -04:00
|
|
|
string? selfMd5 = self.ReadString(Rom.MD5Key);
|
|
|
|
|
string? otherMd5 = other.ReadString(Rom.MD5Key);
|
2024-02-28 19:49:09 -05:00
|
|
|
if (string.IsNullOrEmpty(selfMd5) && !string.IsNullOrEmpty(otherMd5))
|
2023-08-14 19:06:08 -04:00
|
|
|
self[Rom.MD5Key] = otherMd5;
|
|
|
|
|
|
|
|
|
|
string? selfSha1 = self.ReadString(Rom.SHA1Key);
|
|
|
|
|
string? otherSha1 = other.ReadString(Rom.SHA1Key);
|
2024-02-28 19:49:09 -05:00
|
|
|
if (string.IsNullOrEmpty(selfSha1) && !string.IsNullOrEmpty(otherSha1))
|
2023-08-14 19:06:08 -04:00
|
|
|
self[Rom.SHA1Key] = otherSha1;
|
|
|
|
|
|
|
|
|
|
string? selfSha256 = self.ReadString(Rom.SHA256Key);
|
|
|
|
|
string? otherSha256 = other.ReadString(Rom.SHA256Key);
|
2024-02-28 19:49:09 -05:00
|
|
|
if (string.IsNullOrEmpty(selfSha256) && !string.IsNullOrEmpty(otherSha256))
|
2023-08-14 19:06:08 -04:00
|
|
|
self[Rom.SHA256Key] = otherSha256;
|
|
|
|
|
|
|
|
|
|
string? selfSha384 = self.ReadString(Rom.SHA384Key);
|
|
|
|
|
string? otherSha384 = other.ReadString(Rom.SHA384Key);
|
2024-02-28 19:49:09 -05:00
|
|
|
if (string.IsNullOrEmpty(selfSha384) && !string.IsNullOrEmpty(otherSha384))
|
2023-08-14 19:06:08 -04:00
|
|
|
self[Rom.SHA384Key] = otherSha384;
|
|
|
|
|
|
|
|
|
|
string? selfSha512 = self.ReadString(Rom.SHA512Key);
|
|
|
|
|
string? otherSha512 = other.ReadString(Rom.SHA512Key);
|
2024-02-28 19:49:09 -05:00
|
|
|
if (string.IsNullOrEmpty(selfSha512) && !string.IsNullOrEmpty(otherSha512))
|
2023-08-14 19:06:08 -04:00
|
|
|
self[Rom.SHA512Key] = otherSha512;
|
|
|
|
|
|
|
|
|
|
string? selfSpamSum = self.ReadString(Rom.SpamSumKey);
|
|
|
|
|
string? otherSpamSum = other.ReadString(Rom.SpamSumKey);
|
2024-02-28 19:49:09 -05:00
|
|
|
if (string.IsNullOrEmpty(selfSpamSum) && !string.IsNullOrEmpty(otherSpamSum))
|
2023-08-14 19:06:08 -04:00
|
|
|
self[Rom.SpamSumKey] = otherSpamSum;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
2024-03-04 22:18:14 -05:00
|
|
|
|
2025-01-11 22:00:26 -05:00
|
|
|
#region Reading
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Read an item array from a given key, if possible
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static T[]? ReadItemArray<T>(this DictionaryBase self, string key) where T : DictionaryBase
|
|
|
|
|
{
|
|
|
|
|
var items = self.Read<T[]>(key);
|
|
|
|
|
if (items == default)
|
|
|
|
|
{
|
|
|
|
|
var single = self.Read<T>(key);
|
|
|
|
|
if (single != default)
|
|
|
|
|
items = [single];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return items;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
2023-08-14 13:17:51 -04:00
|
|
|
}
|
|
|
|
|
}
|