mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 19:14:27 +00:00
Add nullable context to SabreTools.DatItems
This change also starts migrating the internals of the DatItem formats to the new internal models. Right now, it's basically just acting like a wrapper around those models.
This commit is contained in:
15
SabreTools.Core/ArrayExtensions.cs
Normal file
15
SabreTools.Core/ArrayExtensions.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Core
|
||||
{
|
||||
public static class ArrayExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whether the specified array is null or has a length of zero
|
||||
/// </summary>
|
||||
public static bool IsNullOrEmpty(this Array? array)
|
||||
{
|
||||
return array == null || array.Length == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
479
SabreTools.Core/DictionaryBaseExtensions.cs
Normal file
479
SabreTools.Core/DictionaryBaseExtensions.cs
Normal file
@@ -0,0 +1,479 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using SabreTools.Models.Internal;
|
||||
|
||||
namespace SabreTools.Core
|
||||
{
|
||||
public static class DictionaryBaseExtensions
|
||||
{
|
||||
#region Cloning
|
||||
|
||||
/// <summary>
|
||||
/// Deep clone a DictionaryBase object
|
||||
/// </summary>
|
||||
public static DictionaryBase? Clone(this DictionaryBase dictionaryBase)
|
||||
{
|
||||
// Create a new object of the same type
|
||||
var clone = dictionaryBase
|
||||
.GetType()
|
||||
.GetConstructor(System.Reflection.BindingFlags.Public, Array.Empty<Type>())?
|
||||
.Invoke(null) as DictionaryBase;
|
||||
|
||||
// If construction failed, we can't do anything
|
||||
if (clone == null)
|
||||
return null;
|
||||
|
||||
// Loop through and clone per type
|
||||
foreach (string key in dictionaryBase.Keys)
|
||||
{
|
||||
object? value = dictionaryBase[key];
|
||||
switch (value)
|
||||
{
|
||||
// Primative types
|
||||
case bool:
|
||||
case long:
|
||||
case double:
|
||||
case string:
|
||||
clone[key] = value;
|
||||
break;
|
||||
|
||||
// DictionaryBase types
|
||||
case DictionaryBase db:
|
||||
clone[key] = db.Clone();
|
||||
break;
|
||||
|
||||
// Enumerable types
|
||||
case byte[] bytArr:
|
||||
clone[key] = bytArr.Clone();
|
||||
break;
|
||||
case string[] strArr:
|
||||
clone[key] = strArr.Clone();
|
||||
break;
|
||||
case DictionaryBase[] enDb:
|
||||
clone[key] = enDb.Select(Clone).ToArray();
|
||||
break;
|
||||
|
||||
// Everything else just copies
|
||||
default:
|
||||
clone[key] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#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
|
||||
if (self.Keys.Except(other.Keys).Any())
|
||||
return false;
|
||||
if (other.Keys.Except(self.Keys).Any())
|
||||
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;
|
||||
|
||||
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:
|
||||
if (kvp.Value != other[kvp.Key])
|
||||
return false;
|
||||
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))
|
||||
|
||||
// If we get a partial match
|
||||
if (selfSize == otherSize && self.HashMatch(other))
|
||||
return true;
|
||||
|
||||
// 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
|
||||
return ConditionalHashEquals(self.ReadString(Disk.MD5Key), other.ReadString(Disk.MD5Key))
|
||||
&& ConditionalHashEquals(self.ReadString(Disk.SHA1Key), other.ReadString(Disk.SHA1Key));
|
||||
}
|
||||
|
||||
/// <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
|
||||
return ConditionalHashEquals(self.ReadString(Media.MD5Key), other.ReadString(Media.MD5Key))
|
||||
&& ConditionalHashEquals(self.ReadString(Media.SHA1Key), other.ReadString(Media.SHA1Key))
|
||||
&& ConditionalHashEquals(self.ReadString(Media.SHA256Key), other.ReadString(Media.SHA256Key))
|
||||
&& ConditionalHashEquals(self.ReadString(Media.SpamSumKey), other.ReadString(Media.SpamSumKey));
|
||||
}
|
||||
|
||||
/// <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
|
||||
return ConditionalHashEquals(self.ReadString(Rom.CRCKey), other.ReadString(Rom.CRCKey))
|
||||
&& ConditionalHashEquals(self.ReadString(Rom.MD5Key), other.ReadString(Rom.MD5Key))
|
||||
&& ConditionalHashEquals(self.ReadString(Rom.SHA1Key), other.ReadString(Rom.SHA1Key))
|
||||
&& ConditionalHashEquals(self.ReadString(Rom.SHA256Key), other.ReadString(Rom.SHA256Key))
|
||||
&& ConditionalHashEquals(self.ReadString(Rom.SHA384Key), other.ReadString(Rom.SHA384Key))
|
||||
&& ConditionalHashEquals(self.ReadString(Rom.SHA512Key), other.ReadString(Rom.SHA512Key))
|
||||
&& ConditionalHashEquals(self.ReadString(Rom.SpamSumKey), other.ReadString(Rom.SpamSumKey));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if any hashes exist
|
||||
/// </summary>
|
||||
public static bool HasHashes(this Disk disk)
|
||||
{
|
||||
bool md5Null = string.IsNullOrWhiteSpace(disk.ReadString(Disk.MD5Key));
|
||||
bool sha1Null = string.IsNullOrWhiteSpace(disk.ReadString(Disk.SHA1Key));
|
||||
|
||||
return !md5Null || !sha1Null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if any hashes exist
|
||||
/// </summary>
|
||||
public static bool HasHashes(this Media media)
|
||||
{
|
||||
bool md5Null = string.IsNullOrWhiteSpace(media.ReadString(Media.MD5Key));
|
||||
bool sha1Null = string.IsNullOrWhiteSpace(media.ReadString(Media.SHA1Key));
|
||||
bool sha256Null = string.IsNullOrWhiteSpace(media.ReadString(Media.SHA256Key));
|
||||
bool spamsumNull = string.IsNullOrWhiteSpace(media.ReadString(Media.SpamSumKey));
|
||||
|
||||
return !md5Null || !sha1Null || !sha256Null || !spamsumNull;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if any hashes exist
|
||||
/// </summary>
|
||||
public static bool HasHashes(this Rom rom)
|
||||
{
|
||||
bool crcNull = string.IsNullOrWhiteSpace(rom.ReadString(Rom.CRCKey));
|
||||
bool md5Null = string.IsNullOrWhiteSpace(rom.ReadString(Rom.MD5Key));
|
||||
bool sha1Null = string.IsNullOrWhiteSpace(rom.ReadString(Rom.SHA1Key));
|
||||
bool sha256Null = string.IsNullOrWhiteSpace(rom.ReadString(Rom.SHA256Key));
|
||||
bool sha384Null = string.IsNullOrWhiteSpace(rom.ReadString(Rom.SHA384Key));
|
||||
bool sha512Null = string.IsNullOrWhiteSpace(rom.ReadString(Rom.SHA512Key));
|
||||
bool spamsumNull = string.IsNullOrWhiteSpace(rom.ReadString(Rom.SpamSumKey));
|
||||
|
||||
return !crcNull || !md5Null || !sha1Null || !sha256Null || !sha384Null || !sha512Null || !spamsumNull;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if all of the hashes are set to their 0-byte values or null
|
||||
/// </summary>
|
||||
public static bool HasZeroHash(this Disk disk)
|
||||
{
|
||||
string? md5 = disk.ReadString(Disk.MD5Key);
|
||||
bool md5Null = string.IsNullOrWhiteSpace(md5) || string.Equals(md5, Constants.MD5Zero, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
string? sha1 = disk.ReadString(Disk.SHA1Key);
|
||||
bool sha1Null = string.IsNullOrWhiteSpace(sha1) || string.Equals(sha1, Constants.SHA1Zero, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
return md5Null && sha1Null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if all of the hashes are set to their 0-byte values or null
|
||||
/// </summary>
|
||||
public static bool HasZeroHash(this Media media)
|
||||
{
|
||||
string? md5 = media.ReadString(Media.MD5Key);
|
||||
bool md5Null = string.IsNullOrWhiteSpace(md5) || string.Equals(md5, Constants.MD5Zero, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
string? sha1 = media.ReadString(Media.SHA1Key);
|
||||
bool sha1Null = string.IsNullOrWhiteSpace(sha1) || string.Equals(sha1, Constants.SHA1Zero, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
string? sha256 = media.ReadString(Media.SHA256Key);
|
||||
bool sha256Null = string.IsNullOrWhiteSpace(sha256) || string.Equals(sha256, Constants.SHA256Zero, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
string? spamsum = media.ReadString(Media.SpamSumKey);
|
||||
bool spamsumNull = string.IsNullOrWhiteSpace(spamsum) || string.Equals(spamsum, Constants.SpamSumZero, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
return md5Null && sha1Null && sha256Null && spamsumNull;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if all of the hashes are set to their 0-byte values or null
|
||||
/// </summary>
|
||||
public static bool HasZeroHash(this Rom rom)
|
||||
{
|
||||
string? crc = rom.ReadString(Rom.CRCKey);
|
||||
bool crcNull = string.IsNullOrWhiteSpace(crc) || string.Equals(crc, Constants.CRCZero, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
string? md5 = rom.ReadString(Rom.MD5Key);
|
||||
bool md5Null = string.IsNullOrWhiteSpace(md5) || string.Equals(md5, Constants.MD5Zero, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
string? sha1 = rom.ReadString(Rom.SHA1Key);
|
||||
bool sha1Null = string.IsNullOrWhiteSpace(sha1) || string.Equals(sha1, Constants.SHA1Zero, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
string? sha256 = rom.ReadString(Rom.SHA256Key);
|
||||
bool sha256Null = string.IsNullOrWhiteSpace(sha256) || string.Equals(sha256, Constants.SHA256Zero, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
string? sha384 = rom.ReadString(Rom.SHA384Key);
|
||||
bool sha384Null = string.IsNullOrWhiteSpace(sha384) || string.Equals(sha384, Constants.SHA384Zero, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
string? sha512 = rom.ReadString(Rom.SHA512Key);
|
||||
bool sha512Null = string.IsNullOrWhiteSpace(sha512) || string.Equals(sha512, Constants.SHA512Zero, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
string? spamsum = rom.ReadString(Rom.SpamSumKey);
|
||||
bool spamsumNull = string.IsNullOrWhiteSpace(spamsum) || string.Equals(spamsum, Constants.SpamSumZero, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
return crcNull && md5Null && sha1Null && sha256Null && sha384Null && sha512Null && spamsumNull;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if two hashes are equal for the purposes of merging
|
||||
/// </summary>
|
||||
private static bool ConditionalHashEquals(string? firstHash, string? secondHash)
|
||||
{
|
||||
// If either hash is empty, we say they're equal for merging
|
||||
if (string.IsNullOrWhiteSpace(firstHash) || string.IsNullOrWhiteSpace(secondHash))
|
||||
return true;
|
||||
|
||||
// If they're different sizes, they can't match
|
||||
if (firstHash!.Length != secondHash!.Length)
|
||||
return false;
|
||||
|
||||
// Otherwise, they need to match exactly
|
||||
return string.Equals(firstHash, secondHash, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if there are no, non-empty hashes in common
|
||||
/// </summary>
|
||||
private static bool HasCommonHash(this Disk self, Disk other)
|
||||
{
|
||||
bool md5Null = string.IsNullOrWhiteSpace(self.ReadString(Disk.MD5Key));
|
||||
md5Null ^= string.IsNullOrWhiteSpace(other.ReadString(Disk.MD5Key));
|
||||
|
||||
bool sha1Null = string.IsNullOrWhiteSpace(self.ReadString(Disk.SHA1Key));
|
||||
sha1Null ^= string.IsNullOrWhiteSpace(other.ReadString(Disk.SHA1Key));
|
||||
|
||||
return !md5Null || !sha1Null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if there are no, non-empty hashes in common
|
||||
/// </summary>
|
||||
private static bool HasCommonHash(this Media self, Media other)
|
||||
{
|
||||
bool md5Null = string.IsNullOrWhiteSpace(self.ReadString(Media.MD5Key));
|
||||
md5Null ^= string.IsNullOrWhiteSpace(other.ReadString(Media.MD5Key));
|
||||
|
||||
bool sha1Null = string.IsNullOrWhiteSpace(self.ReadString(Media.SHA1Key));
|
||||
sha1Null ^= string.IsNullOrWhiteSpace(other.ReadString(Media.SHA1Key));
|
||||
|
||||
bool sha256Null = string.IsNullOrWhiteSpace(self.ReadString(Media.SHA256Key));
|
||||
sha256Null ^= string.IsNullOrWhiteSpace(other.ReadString(Media.SHA256Key));
|
||||
|
||||
bool spamsumNull = string.IsNullOrWhiteSpace(self.ReadString(Media.SpamSumKey));
|
||||
spamsumNull ^= string.IsNullOrWhiteSpace(other.ReadString(Media.SpamSumKey));
|
||||
|
||||
return !md5Null || !sha1Null || !sha256Null || !spamsumNull;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if there are no, non-empty hashes in common
|
||||
/// </summary>
|
||||
private static bool HasCommonHash(this Rom self, Rom other)
|
||||
{
|
||||
bool crcNull = string.IsNullOrWhiteSpace(self.ReadString(Rom.CRCKey));
|
||||
crcNull ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.CRCKey));
|
||||
|
||||
bool md5Null = string.IsNullOrWhiteSpace(self.ReadString(Rom.MD5Key));
|
||||
md5Null ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.MD5Key));
|
||||
|
||||
bool sha1Null = string.IsNullOrWhiteSpace(self.ReadString(Rom.SHA1Key));
|
||||
sha1Null ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.SHA1Key));
|
||||
|
||||
bool sha256Null = string.IsNullOrWhiteSpace(self.ReadString(Rom.SHA256Key));
|
||||
sha256Null ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.SHA256Key));
|
||||
|
||||
bool sha384Null = string.IsNullOrWhiteSpace(self.ReadString(Rom.SHA384Key));
|
||||
sha384Null ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.SHA384Key));
|
||||
|
||||
bool sha512Null = string.IsNullOrWhiteSpace(self.ReadString(Rom.SHA512Key));
|
||||
sha512Null ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.SHA512Key));
|
||||
|
||||
bool spamsumNull = string.IsNullOrWhiteSpace(self.ReadString(Rom.SpamSumKey));
|
||||
spamsumNull ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.SpamSumKey));
|
||||
|
||||
return !crcNull || !md5Null || !sha1Null || !sha256Null || !sha384Null || !sha512Null || !spamsumNull;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Add DatItem conversion extensions here
|
||||
// TODO: Once done with the above, replace innards on each current model
|
||||
}
|
||||
}
|
||||
@@ -452,6 +452,12 @@ namespace SabreTools.Core
|
||||
[Mapping("configuration")]
|
||||
Configuration,
|
||||
|
||||
[Mapping("conflocation")]
|
||||
ConfLocation,
|
||||
|
||||
[Mapping("confsetting")]
|
||||
ConfSetting,
|
||||
|
||||
[Mapping("control")]
|
||||
Control,
|
||||
|
||||
@@ -464,9 +470,15 @@ namespace SabreTools.Core
|
||||
[Mapping("device_ref", "deviceref")]
|
||||
DeviceReference,
|
||||
|
||||
[Mapping("diplocation")]
|
||||
DipLocation,
|
||||
|
||||
[Mapping("dipswitch")]
|
||||
DipSwitch,
|
||||
|
||||
[Mapping("dipvalue")]
|
||||
DipValue,
|
||||
|
||||
[Mapping("diskarea")]
|
||||
DiskArea,
|
||||
|
||||
@@ -491,9 +503,6 @@ namespace SabreTools.Core
|
||||
[Mapping("instance")]
|
||||
Instance,
|
||||
|
||||
[Mapping("location")]
|
||||
Location,
|
||||
|
||||
[Mapping("original")]
|
||||
Original,
|
||||
|
||||
@@ -521,9 +530,6 @@ namespace SabreTools.Core
|
||||
[Mapping("serials")]
|
||||
Serials,
|
||||
|
||||
[Mapping("setting")]
|
||||
Setting,
|
||||
|
||||
[Mapping("sharedfeat", "shared_feat", "sharedfeature", "shared_feature")]
|
||||
SharedFeature,
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
<InternalsVisibleTo Include="SabreTools.Test" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Models\SabreTools.Models.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -25,7 +25,43 @@ namespace SabreTools.Core.Tools
|
||||
return input;
|
||||
}
|
||||
|
||||
// <summary>
|
||||
/// <summary>
|
||||
/// Normalize a CRC32 string and pad to the correct size
|
||||
/// </summary>
|
||||
public static string NormalizeCRC32(string? hash)
|
||||
=> NormalizeHashData(hash, Constants.CRCLength);
|
||||
|
||||
/// <summary>
|
||||
/// Normalize a MD5 string and pad to the correct size
|
||||
/// </summary>
|
||||
public static string NormalizeMD5(string? hash)
|
||||
=> NormalizeHashData(hash, Constants.MD5Length);
|
||||
|
||||
/// <summary>
|
||||
/// Normalize a SHA1 string and pad to the correct size
|
||||
/// </summary>
|
||||
public static string NormalizeSHA1(string? hash)
|
||||
=> NormalizeHashData(hash, Constants.SHA1Length);
|
||||
|
||||
/// <summary>
|
||||
/// Normalize a SHA256 string and pad to the correct size
|
||||
/// </summary>
|
||||
public static string NormalizeSHA256(string? hash)
|
||||
=> NormalizeHashData(hash, Constants.SHA256Length);
|
||||
|
||||
/// <summary>
|
||||
/// Normalize a SHA384 string and pad to the correct size
|
||||
/// </summary>
|
||||
public static string NormalizeSHA384(string? hash)
|
||||
=> NormalizeHashData(hash, Constants.SHA384Length);
|
||||
|
||||
/// <summary>
|
||||
/// Normalize a SHA512 string and pad to the correct size
|
||||
/// </summary>
|
||||
public static string NormalizeSHA512(string? hash)
|
||||
=> NormalizeHashData(hash, Constants.SHA512Length);
|
||||
|
||||
/// <summary>
|
||||
/// Remove all chars that are considered path unsafe
|
||||
/// </summary>
|
||||
public static string? RemovePathUnsafeCharacters(string? input)
|
||||
@@ -33,6 +69,8 @@ namespace SabreTools.Core.Tools
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
return input;
|
||||
|
||||
input = input.ToLowerInvariant();
|
||||
|
||||
List<char> invalidPath = Path.GetInvalidPathChars().ToList();
|
||||
return new string(input.Where(c => !invalidPath.Contains(c)).ToArray());
|
||||
}
|
||||
@@ -90,6 +128,40 @@ namespace SabreTools.Core.Tools
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalize a hash string and pad to the correct size
|
||||
/// </summary>
|
||||
private static string NormalizeHashData(string? hash, int expectedLength)
|
||||
{
|
||||
// If we have a known blank hash, return blank
|
||||
if (string.IsNullOrWhiteSpace(hash) || hash == "-" || hash == "_")
|
||||
return string.Empty;
|
||||
|
||||
// Check to see if it's a "hex" hash
|
||||
hash = hash.Trim().Replace("0x", string.Empty);
|
||||
|
||||
// If we have a blank hash now, return blank
|
||||
if (string.IsNullOrWhiteSpace(hash))
|
||||
return string.Empty;
|
||||
|
||||
// If the hash shorter than the required length, pad it
|
||||
if (hash.Length < expectedLength)
|
||||
hash = hash.PadLeft(expectedLength, '0');
|
||||
|
||||
// If the hash is longer than the required length, it's invalid
|
||||
else if (hash.Length > expectedLength)
|
||||
return string.Empty;
|
||||
|
||||
// Now normalize the hash
|
||||
hash = hash.ToLowerInvariant();
|
||||
|
||||
// Otherwise, make sure that every character is a proper match
|
||||
if (hash.Any(c => (c < '0' || c > '9') && (c < 'a' || c > 'f')))
|
||||
hash = string.Empty;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert Cyrillic lettering to Latin lettering
|
||||
/// </summary>
|
||||
|
||||
@@ -163,16 +163,6 @@ namespace SabreTools.Core.Tools
|
||||
};
|
||||
}
|
||||
|
||||
/// Indicates whether the specified array is null or has a length of zero
|
||||
/// </summary>
|
||||
/// <param name="array">The array to test</param>
|
||||
/// <returns>true if the array parameter is null or has a length of zero; otherwise, false.</returns>
|
||||
/// <link>https://stackoverflow.com/questions/8560106/isnullorempty-equivalent-for-array-c-sharp</link>
|
||||
public static bool IsNullOrEmpty(this Array array)
|
||||
{
|
||||
return array == null || array.Length == 0;
|
||||
}
|
||||
|
||||
//// <summary>
|
||||
/// Returns if the first byte array starts with the second array
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user