using System; using SabreTools.Data.Models.Metadata; using SabreTools.Hashing; using SabreTools.IO.Extensions; using SabreTools.Matching; namespace SabreTools.Data.Extensions { // TODO: Add proper enum for YesNo // TODO: Combine yes/partial/no enums public static class MetadataExtensions { #region Conversion /// /// Convert a Disk to a Rom /// public static Rom? ConvertToRom(this Disk? disk) { // If the Disk is missing, we can't do anything if (disk is null) return null; // Append a suffix to the name string? name = disk.Name; if (name is not null) name += ".chd"; return new Rom { Name = name, Merge = disk.Merge, Region = disk.Region, Status = disk.Status, Optional = disk.Optional, MD5 = disk.MD5, SHA1 = disk.SHA1, }; } /// /// Convert a Media to a Rom /// public static Rom? ConvertToRom(this Media? media) { // If the Media is missing, we can't do anything if (media is null) return null; // Append a suffix to the name string? name = media.Name; if (name is not null) name += ".aaruf"; return new Rom { Name = name, MD5 = media.MD5, SHA1 = media.SHA1, SHA256 = media.SHA256, SpamSum = media.SpamSum, }; } #endregion #region Equality Checking /// /// Check equality of two Disk objects /// public static bool PartialEquals(this Disk self, Disk other) { ItemStatus? selfStatus = self.Status; ItemStatus? otherStatus = other.Status; string? selfName = self.Name; string? otherName = other.Name; // If all hashes are empty but they're both nodump and the names match, then they're dupes if (selfStatus == ItemStatus.Nodump && otherStatus == ItemStatus.Nodump && 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; } /// /// Check equality of two Media objects /// public static bool PartialEquals(this Media self, Media other) { // If we get a partial match if (self.HashMatch(other)) return true; // All other cases fail return false; } /// /// Check equality of two Rom objects /// public static bool PartialEquals(this Rom self, Rom other) { ItemStatus? selfStatus = self.Status; ItemStatus? otherStatus = other.Status; string? selfName = self.Name; string? otherName = other.Name; long? selfSize = self.Size; long? otherSize = other.Size; // If all hashes are empty but they're both nodump and the names match, then they're dupes if (selfStatus == ItemStatus.Nodump && otherStatus == ItemStatus.Nodump && 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 is null && self.HashMatch(other)) return true; else if (otherSize is null && self.HashMatch(other)) return true; // If we get a partial match if (selfSize == otherSize && self.HashMatch(other)) return true; // All other cases fail return false; } #endregion #region Hash Checking /// /// Determine if two hashes are equal for the purposes of merging /// public static bool ConditionalHashEquals(byte[]? firstHash, byte[]? secondHash) { // If either hash is empty, we say they're equal for merging if (firstHash.IsNullOrEmpty() || secondHash.IsNullOrEmpty()) return true; // If they're different sizes, they can't match if (firstHash!.Length != secondHash!.Length) return false; // Otherwise, they need to match exactly return firstHash.EqualsExactly(secondHash); } /// /// Determine if two hashes are equal for the purposes of merging /// public static bool ConditionalHashEquals(string? firstHash, string? secondHash) { // If either hash is empty, we say they're equal for merging if (string.IsNullOrEmpty(firstHash) || string.IsNullOrEmpty(secondHash)) return true; // If they're different sizes, they can't match if (firstHash!.Length != secondHash!.Length) return false; // Otherwise, they need to match exactly return string.Equals(firstHash, secondHash, StringComparison.OrdinalIgnoreCase); } /// /// Returns if any hashes are common /// public static bool HashMatch(this Disk self, Disk other) { // If either have no hashes, we return false, otherwise this would be a false positive if (!self.HasHashes() || !other.HasHashes()) return false; // If neither have hashes in common, we return false, otherwise this would be a false positive if (!self.HasCommonHash(other)) return false; // Return if all hashes match according to merge rules bool conditionalMd5 = ConditionalHashEquals(self.MD5, other.MD5); bool conditionalSha1 = ConditionalHashEquals(self.SHA1, other.SHA1); return conditionalMd5 && conditionalSha1; } /// /// Returns if any hashes are common /// public static bool HashMatch(this Media self, Media other) { // If either have no hashes, we return false, otherwise this would be a false positive if (!self.HasHashes() || !other.HasHashes()) return false; // If neither have hashes in common, we return false, otherwise this would be a false positive if (!self.HasCommonHash(other)) return false; // Return if all hashes match according to merge rules bool conditionalMd5 = ConditionalHashEquals(self.MD5, other.MD5); bool conditionalSha1 = ConditionalHashEquals(self.SHA1, other.SHA1); bool conditionalSha256 = ConditionalHashEquals(self.SHA256, other.SHA256); bool conditionalSpamSum = ConditionalHashEquals(self.SpamSum, other.SpamSum); return conditionalMd5 && conditionalSha1 && conditionalSha256 && conditionalSpamSum; } /// /// Returns if any hashes are common /// public static bool HashMatch(this Rom self, Rom other) { // If either have no hashes, we return false, otherwise this would be a false positive if (!self.HasHashes() || !other.HasHashes()) return false; // If neither have hashes in common, we return false, otherwise this would be a false positive if (!self.HasCommonHash(other)) return false; // Return if all hashes match according to merge rules string? selfCrc16 = self.CRC16; string? otherCrc16 = other.CRC16; bool conditionalCrc16 = ConditionalHashEquals(selfCrc16, otherCrc16); string? selfCrc = self.CRC32; string? otherCrc = other.CRC32; bool conditionalCrc = ConditionalHashEquals(selfCrc, otherCrc); string? selfCrc64 = self.CRC64; string? otherCrc64 = other.CRC64; bool conditionalCrc64 = ConditionalHashEquals(selfCrc64, otherCrc64); string? selfMd2 = self.MD2; string? otherMd2 = other.MD2; bool conditionalMd2 = ConditionalHashEquals(selfMd2, otherMd2); string? selfMd4 = self.MD4; string? otherMd4 = other.MD4; bool conditionalMd4 = ConditionalHashEquals(selfMd4, otherMd4); string? selfMd5 = self.MD5; string? otherMd5 = other.MD5; bool conditionalMd5 = ConditionalHashEquals(selfMd5, otherMd5); string? selfRipeMD128 = self.RIPEMD128; string? otherRipeMD128 = other.RIPEMD128; bool conditionaRipeMD128 = ConditionalHashEquals(selfRipeMD128, otherRipeMD128); string? selfRipeMD160 = self.RIPEMD160; string? otherRipeMD160 = other.RIPEMD160; bool conditionaRipeMD160 = ConditionalHashEquals(selfRipeMD160, otherRipeMD160); string? selfSha1 = self.SHA1; string? otherSha1 = other.SHA1; bool conditionalSha1 = ConditionalHashEquals(selfSha1, otherSha1); string? selfSha256 = self.SHA256; string? otherSha256 = other.SHA256; bool conditionalSha256 = ConditionalHashEquals(selfSha256, otherSha256); string? selfSha384 = self.SHA384; string? otherSha384 = other.SHA384; bool conditionalSha384 = ConditionalHashEquals(selfSha384, otherSha384); string? selfSha512 = self.SHA512; string? otherSha512 = other.SHA512; bool conditionalSha512 = ConditionalHashEquals(selfSha512, otherSha512); string? selfSpamSum = self.SpamSum; string? otherSpamSum = other.SpamSum; bool conditionalSpamSum = ConditionalHashEquals(selfSpamSum, otherSpamSum); return conditionalCrc16 && conditionalCrc && conditionalCrc64 && conditionalMd2 && conditionalMd4 && conditionalMd5 && conditionaRipeMD128 && conditionaRipeMD160 && conditionalSha1 && conditionalSha256 && conditionalSha384 && conditionalSha512 && conditionalSpamSum; } /// /// Returns if any hashes exist /// public static bool HasHashes(this Disk disk) { bool md5Null = string.IsNullOrEmpty(disk.MD5); bool sha1Null = string.IsNullOrEmpty(disk.SHA1); return !md5Null || !sha1Null; } /// /// Returns if any hashes exist /// public static bool HasHashes(this Media media) { bool md5Null = string.IsNullOrEmpty(media.MD5); bool sha1Null = string.IsNullOrEmpty(media.SHA1); bool sha256Null = string.IsNullOrEmpty(media.SHA256); bool spamsumNull = string.IsNullOrEmpty(media.SpamSum); return !md5Null || !sha1Null || !sha256Null || !spamsumNull; } /// /// Returns if any hashes exist /// public static bool HasHashes(this Rom rom) { bool crc16Null = string.IsNullOrEmpty(rom.CRC16); bool crc32Null = string.IsNullOrEmpty(rom.CRC32); bool crc64Null = string.IsNullOrEmpty(rom.CRC64); bool md2Null = string.IsNullOrEmpty(rom.MD2); bool md4Null = string.IsNullOrEmpty(rom.MD4); bool md5Null = string.IsNullOrEmpty(rom.MD5); bool ripeMD128Null = string.IsNullOrEmpty(rom.RIPEMD128); bool ripeMD160Null = string.IsNullOrEmpty(rom.RIPEMD160); bool sha1Null = string.IsNullOrEmpty(rom.SHA1); bool sha256Null = string.IsNullOrEmpty(rom.SHA256); bool sha384Null = string.IsNullOrEmpty(rom.SHA384); bool sha512Null = string.IsNullOrEmpty(rom.SHA512); bool spamsumNull = string.IsNullOrEmpty(rom.SpamSum); return !crc16Null || !crc32Null || !crc64Null || !md2Null || !md4Null || !md5Null || !ripeMD128Null || !ripeMD160Null || !sha1Null || !sha256Null || !sha384Null || !sha512Null || !spamsumNull; } /// /// Returns if all of the hashes are set to their 0-byte values or null /// public static bool HasZeroHash(this Disk disk) { string? md5 = disk.MD5; bool md5Null = string.IsNullOrEmpty(md5) || string.Equals(md5, HashType.MD5.ZeroString, StringComparison.OrdinalIgnoreCase); string? sha1 = disk.SHA1; bool sha1Null = string.IsNullOrEmpty(sha1) || string.Equals(sha1, HashType.SHA1.ZeroString, StringComparison.OrdinalIgnoreCase); return md5Null && sha1Null; } /// /// Returns if all of the hashes are set to their 0-byte values or null /// public static bool HasZeroHash(this Media media) { string? md5 = media.MD5; bool md5Null = string.IsNullOrEmpty(md5) || string.Equals(md5, HashType.MD5.ZeroString, StringComparison.OrdinalIgnoreCase); string? sha1 = media.SHA1; bool sha1Null = string.IsNullOrEmpty(sha1) || string.Equals(sha1, HashType.SHA1.ZeroString, StringComparison.OrdinalIgnoreCase); string? sha256 = media.SHA256; bool sha256Null = string.IsNullOrEmpty(sha256) || string.Equals(sha256, HashType.SHA256.ZeroString, StringComparison.OrdinalIgnoreCase); string? spamsum = media.SpamSum; bool spamsumNull = string.IsNullOrEmpty(spamsum) || string.Equals(spamsum, HashType.SpamSum.ZeroString, StringComparison.OrdinalIgnoreCase); return md5Null && sha1Null && sha256Null && spamsumNull; } /// /// Returns if all of the hashes are set to their 0-byte values or null /// public static bool HasZeroHash(this Rom rom) { string? crc16 = rom.CRC16; bool crc16Null = string.IsNullOrEmpty(crc16) || string.Equals(crc16, HashType.CRC16.ZeroString, StringComparison.OrdinalIgnoreCase); string? crc32 = rom.CRC32; bool crc32Null = string.IsNullOrEmpty(crc32) || string.Equals(crc32, HashType.CRC32.ZeroString, StringComparison.OrdinalIgnoreCase); string? crc64 = rom.CRC64; bool crc64Null = string.IsNullOrEmpty(crc64) || string.Equals(crc64, HashType.CRC64.ZeroString, StringComparison.OrdinalIgnoreCase); string? md2 = rom.MD2; bool md2Null = string.IsNullOrEmpty(md2) || string.Equals(md2, HashType.MD2.ZeroString, StringComparison.OrdinalIgnoreCase); string? md4 = rom.MD4; bool md4Null = string.IsNullOrEmpty(md4) || string.Equals(md4, HashType.MD4.ZeroString, StringComparison.OrdinalIgnoreCase); string? md5 = rom.MD5; bool md5Null = string.IsNullOrEmpty(md5) || string.Equals(md5, HashType.MD5.ZeroString, StringComparison.OrdinalIgnoreCase); string? ripeMD128 = rom.RIPEMD128; bool ripeMD128Null = string.IsNullOrEmpty(value: ripeMD128) || string.Equals(ripeMD128, HashType.RIPEMD128.ZeroString, StringComparison.OrdinalIgnoreCase); string? ripeMD160 = rom.RIPEMD160; bool ripeMD160Null = string.IsNullOrEmpty(ripeMD160) || string.Equals(ripeMD160, HashType.RIPEMD160.ZeroString, StringComparison.OrdinalIgnoreCase); string? sha1 = rom.SHA1; bool sha1Null = string.IsNullOrEmpty(sha1) || string.Equals(sha1, HashType.SHA1.ZeroString, StringComparison.OrdinalIgnoreCase); string? sha256 = rom.SHA256; bool sha256Null = string.IsNullOrEmpty(sha256) || string.Equals(sha256, HashType.SHA256.ZeroString, StringComparison.OrdinalIgnoreCase); string? sha384 = rom.SHA384; bool sha384Null = string.IsNullOrEmpty(sha384) || string.Equals(sha384, HashType.SHA384.ZeroString, StringComparison.OrdinalIgnoreCase); string? sha512 = rom.SHA512; bool sha512Null = string.IsNullOrEmpty(sha512) || string.Equals(sha512, HashType.SHA512.ZeroString, StringComparison.OrdinalIgnoreCase); string? spamsum = rom.SpamSum; bool spamsumNull = string.IsNullOrEmpty(spamsum) || string.Equals(spamsum, HashType.SpamSum.ZeroString, StringComparison.OrdinalIgnoreCase); return crc16Null && crc32Null && crc64Null && md2Null && md4Null && md5Null && ripeMD128Null && ripeMD160Null && sha1Null && sha256Null && sha384Null && sha512Null && spamsumNull; } /// /// Returns if there are no, non-empty hashes in common /// private static bool HasCommonHash(this Disk self, Disk other) { bool md5Null = string.IsNullOrEmpty(self.MD5); md5Null ^= string.IsNullOrEmpty(other.MD5); bool sha1Null = string.IsNullOrEmpty(self.SHA1); sha1Null ^= string.IsNullOrEmpty(other.SHA1); return !md5Null || !sha1Null; } /// /// Returns if there are no, non-empty hashes in common /// private static bool HasCommonHash(this Media self, Media other) { bool md5Null = string.IsNullOrEmpty(self.MD5); md5Null ^= string.IsNullOrEmpty(other.MD5); bool sha1Null = string.IsNullOrEmpty(self.SHA1); sha1Null ^= string.IsNullOrEmpty(other.SHA1); bool sha256Null = string.IsNullOrEmpty(self.SHA256); sha256Null ^= string.IsNullOrEmpty(other.SHA256); bool spamsumNull = string.IsNullOrEmpty(self.SpamSum); spamsumNull ^= string.IsNullOrEmpty(other.SpamSum); return !md5Null || !sha1Null || !sha256Null || !spamsumNull; } /// /// Returns if there are no, non-empty hashes in common /// private static bool HasCommonHash(this Rom self, Rom other) { bool crc16Null = string.IsNullOrEmpty(self.CRC16); crc16Null ^= string.IsNullOrEmpty(other.CRC16); bool crc32Null = string.IsNullOrEmpty(self.CRC32); crc32Null ^= string.IsNullOrEmpty(other.CRC32); bool crc64Null = string.IsNullOrEmpty(self.CRC64); crc64Null ^= string.IsNullOrEmpty(other.CRC64); bool md2Null = string.IsNullOrEmpty(self.MD2); md2Null ^= string.IsNullOrEmpty(other.MD2); bool md4Null = string.IsNullOrEmpty(self.MD4); md4Null ^= string.IsNullOrEmpty(other.MD4); bool md5Null = string.IsNullOrEmpty(self.MD5); md5Null ^= string.IsNullOrEmpty(other.MD5); bool ripeMD128Null = string.IsNullOrEmpty(self.RIPEMD128); ripeMD128Null ^= string.IsNullOrEmpty(other.RIPEMD128); bool ripeMD160Null = string.IsNullOrEmpty(self.RIPEMD160); ripeMD160Null ^= string.IsNullOrEmpty(other.RIPEMD160); bool sha1Null = string.IsNullOrEmpty(self.SHA1); sha1Null ^= string.IsNullOrEmpty(other.SHA1); bool sha256Null = string.IsNullOrEmpty(self.SHA256); sha256Null ^= string.IsNullOrEmpty(other.SHA256); bool sha384Null = string.IsNullOrEmpty(self.SHA384); sha384Null ^= string.IsNullOrEmpty(other.SHA384); bool sha512Null = string.IsNullOrEmpty(self.SHA512); sha512Null ^= string.IsNullOrEmpty(other.SHA512); bool spamsumNull = string.IsNullOrEmpty(self.SpamSum); spamsumNull ^= string.IsNullOrEmpty(other.SpamSum); return !crc16Null || !crc32Null || !crc64Null || !md2Null || !md4Null || !md5Null || !ripeMD128Null || !ripeMD160Null || !sha1Null || !sha256Null || !sha384Null || !sha512Null || !spamsumNull; } #endregion #region Information Filling /// /// Fill any missing size and hash information from another Disk /// public static void FillMissingHashes(this Disk? self, Disk? other) { if (self is null || other is null) return; string? selfMd5 = self.MD5; string? otherMd5 = other.MD5; if (string.IsNullOrEmpty(selfMd5) && !string.IsNullOrEmpty(otherMd5)) self.MD5 = otherMd5; string? selfSha1 = self.SHA1; string? otherSha1 = other.SHA1; if (string.IsNullOrEmpty(selfSha1) && !string.IsNullOrEmpty(otherSha1)) self.SHA1 = otherSha1; } /// /// Fill any missing size and hash information from another Media /// public static void FillMissingHashes(this Media? self, Media? other) { if (self is null || other is null) return; string? selfMd5 = self.MD5; string? otherMd5 = other.MD5; if (string.IsNullOrEmpty(selfMd5) && !string.IsNullOrEmpty(otherMd5)) self.MD5 = otherMd5; string? selfSha1 = self.SHA1; string? otherSha1 = other.SHA1; if (string.IsNullOrEmpty(selfSha1) && !string.IsNullOrEmpty(otherSha1)) self.SHA1 = otherSha1; string? selfSha256 = self.SHA256; string? otherSha256 = other.SHA256; if (string.IsNullOrEmpty(selfSha256) && !string.IsNullOrEmpty(otherSha256)) self.SHA256 = otherSha256; string? selfSpamSum = self.SpamSum; string? otherSpamSum = other.SpamSum; if (string.IsNullOrEmpty(selfSpamSum) && !string.IsNullOrEmpty(otherSpamSum)) self.SpamSum = otherSpamSum; } /// /// Fill any missing size and hash information from another Rom /// public static void FillMissingHashes(this Rom? self, Rom? other) { if (self is null || other is null) return; long? selfSize = self.Size; long? otherSize = other.Size; if (selfSize is null && otherSize is not null) self.Size = otherSize; string? selfCrc16 = self.CRC16; string? otherCrc16 = other.CRC16; if (string.IsNullOrEmpty(selfCrc16) && !string.IsNullOrEmpty(otherCrc16)) self.CRC16 = otherCrc16; string? selfCrc = self.CRC32; string? otherCrc = other.CRC32; if (string.IsNullOrEmpty(selfCrc) && !string.IsNullOrEmpty(otherCrc)) self.CRC32 = otherCrc; string? selfCrc64 = self.CRC64; string? otherCrc64 = other.CRC64; if (string.IsNullOrEmpty(selfCrc64) && !string.IsNullOrEmpty(otherCrc64)) self.CRC64 = otherCrc64; string? selfMd2 = self.MD2; string? otherMd2 = other.MD2; if (string.IsNullOrEmpty(selfMd2) && !string.IsNullOrEmpty(otherMd2)) self.MD2 = otherMd2; string? selfMd4 = self.MD4; string? otherMd4 = other.MD4; if (string.IsNullOrEmpty(selfMd4) && !string.IsNullOrEmpty(otherMd4)) self.MD4 = otherMd4; string? selfMd5 = self.MD5; string? otherMd5 = other.MD5; if (string.IsNullOrEmpty(selfMd5) && !string.IsNullOrEmpty(otherMd5)) self.MD5 = otherMd5; string? selfRipeMD128 = self.RIPEMD128; string? otherRipeMD128 = other.RIPEMD128; if (string.IsNullOrEmpty(selfRipeMD128) && !string.IsNullOrEmpty(otherRipeMD128)) self.RIPEMD128 = otherRipeMD128; string? selfRipeMD160 = self.RIPEMD160; string? otherRipeMD160 = other.RIPEMD160; if (string.IsNullOrEmpty(selfRipeMD160) && !string.IsNullOrEmpty(otherRipeMD160)) self.RIPEMD160 = otherRipeMD160; string? selfSha1 = self.SHA1; string? otherSha1 = other.SHA1; if (string.IsNullOrEmpty(selfSha1) && !string.IsNullOrEmpty(otherSha1)) self.SHA1 = otherSha1; string? selfSha256 = self.SHA256; string? otherSha256 = other.SHA256; if (string.IsNullOrEmpty(selfSha256) && !string.IsNullOrEmpty(otherSha256)) self.SHA256 = otherSha256; string? selfSha384 = self.SHA384; string? otherSha384 = other.SHA384; if (string.IsNullOrEmpty(selfSha384) && !string.IsNullOrEmpty(otherSha384)) self.SHA384 = otherSha384; string? selfSha512 = self.SHA512; string? otherSha512 = other.SHA512; if (string.IsNullOrEmpty(selfSha512) && !string.IsNullOrEmpty(otherSha512)) self.SHA512 = otherSha512; string? selfSpamSum = self.SpamSum; string? otherSpamSum = other.SpamSum; if (string.IsNullOrEmpty(selfSpamSum) && !string.IsNullOrEmpty(otherSpamSum)) self.SpamSum = otherSpamSum; } #endregion #region String to Enum /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static Blit? AsBlit(this string? value) { return value?.ToLowerInvariant() switch { "plain" => Blit.Plain, "dirty" => Blit.Dirty, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static ChipType? AsChipType(this string? value) { return value?.ToLowerInvariant() switch { "cpu" => ChipType.CPU, "audio" => ChipType.Audio, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static ControlType? AsControlType(this string? value) { return value?.ToLowerInvariant() switch { "joy" => ControlType.Joy, "stick" => ControlType.Stick, "paddle" => ControlType.Paddle, "pedal" => ControlType.Pedal, "lightgun" => ControlType.Lightgun, "positional" => ControlType.Positional, "dial" => ControlType.Dial, "trackball" => ControlType.Trackball, "mouse" => ControlType.Mouse, "only_buttons" => ControlType.OnlyButtons, "keypad" => ControlType.Keypad, "keyboard" => ControlType.Keyboard, "mahjong" => ControlType.Mahjong, "hanafuda" => ControlType.Hanafuda, "gambling" => ControlType.Gambling, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static DeviceType? AsDeviceType(this string? value) { return value?.ToLowerInvariant() switch { "unknown" => DeviceType.Unknown, "cartridge" => DeviceType.Cartridge, "floppydisk" => DeviceType.FloppyDisk, "harddisk" => DeviceType.HardDisk, "cylinder" => DeviceType.Cylinder, "cassette" => DeviceType.Cassette, "punchcard" => DeviceType.PunchCard, "punchtape" => DeviceType.PunchTape, "printout" => DeviceType.Printout, "serial" => DeviceType.Serial, "parallel" => DeviceType.Parallel, "snapshot" => DeviceType.Snapshot, "quickload" => DeviceType.QuickLoad, "memcard" => DeviceType.MemCard, "cdrom" => DeviceType.CDROM, "magtape" => DeviceType.MagTape, "romimage" => DeviceType.ROMImage, "midiin" => DeviceType.MIDIIn, "midiout" => DeviceType.MIDIOut, "picture" => DeviceType.Picture, "vidfile" => DeviceType.VidFile, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static DisplayType? AsDisplayType(this string? value) { return value?.ToLowerInvariant() switch { "raster" => DisplayType.Raster, "vector" => DisplayType.Vector, "lcd" => DisplayType.LCD, "svg" => DisplayType.SVG, "unknown" => DisplayType.Unknown, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static Endianness? AsEndianness(this string? value) { return value?.ToLowerInvariant() switch { "big" => Endianness.Big, "little" => Endianness.Little, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static FeatureStatus? AsFeatureStatus(this string? value) { return value?.ToLowerInvariant() switch { "unemulated" => FeatureStatus.Unemulated, "imperfect" => FeatureStatus.Imperfect, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static FeatureType? AsFeatureType(this string? value) { return value?.ToLowerInvariant() switch { "protection" => FeatureType.Protection, "palette" => FeatureType.Palette, "graphics" => FeatureType.Graphics, "sound" => FeatureType.Sound, "controls" => FeatureType.Controls, "keyboard" => FeatureType.Keyboard, "mouse" => FeatureType.Mouse, "microphone" => FeatureType.Microphone, "camera" => FeatureType.Camera, "disk" => FeatureType.Disk, "printer" => FeatureType.Printer, "lan" => FeatureType.Lan, "wan" => FeatureType.Wan, "timing" => FeatureType.Timing, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static ItemStatus? AsItemStatus(this string? value) { return value?.ToLowerInvariant() switch { "none" or "no" => ItemStatus.None, "good" => ItemStatus.Good, "baddump" => ItemStatus.BadDump, "nodump" or "yes" => ItemStatus.Nodump, "verified" => ItemStatus.Verified, "deduped" => ItemStatus.Deduped, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, default on error public static ItemType AsItemType(this string? value) { return value?.ToLowerInvariant() switch { // "Actionable" item types "rom" => ItemType.Rom, "disk" => ItemType.Disk, "file" => ItemType.File, "media" => ItemType.Media, // "Auxiliary" item types "adjuster" => ItemType.Adjuster, "archive" => ItemType.Archive, "biosset" => ItemType.BiosSet, "chip" => ItemType.Chip, "configuration" => ItemType.Configuration, "conflocation" => ItemType.ConfLocation, "confsetting" => ItemType.ConfSetting, "control" => ItemType.Control, "dataarea" => ItemType.DataArea, "device" => ItemType.Device, "device_ref" or "deviceref" => ItemType.DeviceRef, "diplocation" => ItemType.DipLocation, "dipswitch" => ItemType.DipSwitch, "dipvalue" => ItemType.DipValue, "diskarea" => ItemType.DiskArea, "display" => ItemType.Display, "driver" => ItemType.Driver, "dump" => ItemType.Dump, "feature" => ItemType.Feature, "info" => ItemType.Info, "input" => ItemType.Input, "original" => ItemType.Original, "part" => ItemType.Part, "part_feature" or "partfeature" => ItemType.PartFeature, "port" => ItemType.Port, "ramoption" or "ram_option" => ItemType.RamOption, "release" => ItemType.Release, "release_details" or "releasedetails" => ItemType.ReleaseDetails, "sample" => ItemType.Sample, "serials" => ItemType.Serials, "sharedfeat" or "shared_feat" or "sharedfeature" or "shared_feature" => ItemType.SharedFeat, "slot" => ItemType.Slot, "slotoption" or "slot_option" => ItemType.SlotOption, "softwarelist" or "software_list" => ItemType.SoftwareList, "sound" => ItemType.Sound, "source_details" or "sourcedetails" => ItemType.SourceDetails, "video" => ItemType.Video, "blank" => ItemType.Blank, _ => ItemType.NULL, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static LoadFlag? AsLoadFlag(this string? value) { return value?.ToLowerInvariant() switch { "load16_byte" => LoadFlag.Load16Byte, "load16_word" => LoadFlag.Load16Word, "load16_word_swap" => LoadFlag.Load16WordSwap, "load32_byte" => LoadFlag.Load32Byte, "load32_word" => LoadFlag.Load32Word, "load32_word_swap" => LoadFlag.Load32WordSwap, "load32_dword" => LoadFlag.Load32DWord, "load64_word" => LoadFlag.Load64Word, "load64_word_swap" => LoadFlag.Load64WordSwap, "reload" => LoadFlag.Reload, "fill" => LoadFlag.Fill, "continue" => LoadFlag.Continue, "reload_plain" => LoadFlag.ReloadPlain, "ignore" => LoadFlag.Ignore, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, default on error public static MergingFlag AsMergingFlag(this string? value) { return value?.ToLowerInvariant() switch { "none" => MergingFlag.None, "split" => MergingFlag.Split, "merged" => MergingFlag.Merged, "nonmerged" or "unmerged" => MergingFlag.NonMerged, "fullmerged" => MergingFlag.FullMerged, "device" or "deviceunmerged" or "devicenonmerged" => MergingFlag.DeviceNonMerged, "full" or "fullunmerged" or "fullnonmerged" => MergingFlag.FullNonMerged, _ => MergingFlag.None, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, default on error public static NodumpFlag AsNodumpFlag(this string? value) { return value?.ToLowerInvariant() switch { "none" => NodumpFlag.None, "obsolete" => NodumpFlag.Obsolete, "required" => NodumpFlag.Required, "ignore" => NodumpFlag.Ignore, _ => NodumpFlag.None, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static OpenMSXSubType? AsOpenMSXSubType(this string? value) { return value?.ToLowerInvariant() switch { "rom" => OpenMSXSubType.Rom, "megarom" => OpenMSXSubType.MegaRom, "sccpluscart" => OpenMSXSubType.SCCPlusCart, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, default on error public static PackingFlag AsPackingFlag(this string? value) { return value?.ToLowerInvariant() switch { "none" => PackingFlag.None, "zip" or "yes" => PackingFlag.Zip, "unzip" or "no" => PackingFlag.Unzip, "partial" => PackingFlag.Partial, "flat" => PackingFlag.Flat, "fileonly" => PackingFlag.FileOnly, _ => PackingFlag.None, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static Relation? AsRelation(this string? value) { return value?.ToLowerInvariant() switch { "eq" => Relation.Equal, "ne" => Relation.NotEqual, "gt" => Relation.GreaterThan, "le" => Relation.LessThanOrEqual, "lt" => Relation.LessThan, "ge" => Relation.GreaterThanOrEqual, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static Rotation? AsRotation(this string? value) { return value?.ToLowerInvariant() switch { "0" or "north" or "vertical" => Rotation.North, "90" or "east" or "horizontal" => Rotation.East, "180" or "south" => Rotation.South, "270" or "west" => Rotation.West, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static Runnable? AsRunnable(this string? value) { return value?.ToLowerInvariant() switch { "no" => Runnable.No, "partial" => Runnable.Partial, "yes" => Runnable.Yes, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static SoftwareListStatus? AsSoftwareListStatus(this string? value) { return value?.ToLowerInvariant() switch { "none" => SoftwareListStatus.None, "original" => SoftwareListStatus.Original, "compatible" => SoftwareListStatus.Compatible, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static Supported? AsSupported(this string? value) { return value?.ToLowerInvariant() switch { "no" or "unsupported" => Supported.No, "partial" => Supported.Partial, "yes" or "supported" => Supported.Yes, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static SupportStatus? AsSupportStatus(this string? value) { return value?.ToLowerInvariant() switch { "good" => SupportStatus.Good, "imperfect" => SupportStatus.Imperfect, "preliminary" => SupportStatus.Preliminary, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum value representing the input, null on error public static Width? AsWidth(this string? value) { return value?.ToLowerInvariant() switch { "8" or "byte" => Width.Byte, "16" or "short" => Width.Short, "32" or "int" => Width.Int, "64" or "long" => Width.Long, _ => null, }; } /// /// Get bool? value from input string /// /// String to get value from /// bool? corresponding to the string public static bool? AsYesNo(this string? yesno) { return yesno?.ToLowerInvariant() switch { "1" or "yes" or "true" => true, "0" or "no" or "false" => false, _ => null, }; } #endregion #region Enum to String /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this Blit value) { return value switch { Blit.Plain => "plain", Blit.Dirty => "dirty", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this ChipType value) { return value switch { ChipType.CPU => "cpu", ChipType.Audio => "audio", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this ControlType value) { return value switch { ControlType.Joy => "joy", ControlType.Stick => "stick", ControlType.Paddle => "paddle", ControlType.Pedal => "pedal", ControlType.Lightgun => "lightgun", ControlType.Positional => "positional", ControlType.Dial => "dial", ControlType.Trackball => "trackball", ControlType.Mouse => "mouse", ControlType.OnlyButtons => "only_buttons", ControlType.Keypad => "keypad", ControlType.Keyboard => "keyboard", ControlType.Mahjong => "mahjong", ControlType.Hanafuda => "hanafuda", ControlType.Gambling => "gambling", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this DeviceType value) { return value switch { DeviceType.Unknown => "unknown", DeviceType.Cartridge => "cartridge", DeviceType.FloppyDisk => "floppydisk", DeviceType.HardDisk => "harddisk", DeviceType.Cylinder => "cylinder", DeviceType.Cassette => "cassette", DeviceType.PunchCard => "punchcard", DeviceType.PunchTape => "punchtape", DeviceType.Printout => "printout", DeviceType.Serial => "serial", DeviceType.Parallel => "parallel", DeviceType.Snapshot => "snapshot", DeviceType.QuickLoad => "quickload", DeviceType.MemCard => "memcard", DeviceType.CDROM => "cdrom", DeviceType.MagTape => "magtape", DeviceType.ROMImage => "romimage", DeviceType.MIDIIn => "midiin", DeviceType.MIDIOut => "midiout", DeviceType.Picture => "picture", DeviceType.VidFile => "vidfile", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this DisplayType value) { return value switch { DisplayType.Raster => "raster", DisplayType.Vector => "vector", DisplayType.LCD => "lcd", DisplayType.SVG => "svg", DisplayType.Unknown => "unknown", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this Endianness value) { return value switch { Endianness.Big => "big", Endianness.Little => "little", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this FeatureStatus value) { return value switch { FeatureStatus.Unemulated => "unemulated", FeatureStatus.Imperfect => "imperfect", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this FeatureType value) { return value switch { FeatureType.Protection => "protection", FeatureType.Palette => "palette", FeatureType.Graphics => "graphics", FeatureType.Sound => "sound", FeatureType.Controls => "controls", FeatureType.Keyboard => "keyboard", FeatureType.Mouse => "mouse", FeatureType.Microphone => "microphone", FeatureType.Camera => "camera", FeatureType.Disk => "disk", FeatureType.Printer => "printer", FeatureType.Lan => "lan", FeatureType.Wan => "wan", FeatureType.Timing => "timing", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// True to use the second mapping option, if it exists /// String value representing the input, null on error public static string? AsStringValue(this ItemStatus value, bool useSecond = false) { return value switch { ItemStatus.None => useSecond ? "no" : "none", ItemStatus.Good => "good", ItemStatus.BadDump => "baddump", ItemStatus.Nodump => useSecond ? "yes" : "nodump", ItemStatus.Verified => "verified", ItemStatus.Deduped => "deduped", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, default on error public static string? AsStringValue(this ItemType value) { return value switch { // "Actionable" item types ItemType.Rom => "rom", ItemType.Disk => "disk", ItemType.File => "file", ItemType.Media => "media", // "Auxiliary" item types ItemType.Adjuster => "adjuster", ItemType.Archive => "archive", ItemType.BiosSet => "biosset", ItemType.Chip => "chip", ItemType.Configuration => "configuration", ItemType.ConfLocation => "conflocation", ItemType.ConfSetting => "confsetting", ItemType.Control => "control", ItemType.DataArea => "dataarea", ItemType.Device => "device", ItemType.DeviceRef => "device_ref", ItemType.DipLocation => "diplocation", ItemType.DipSwitch => "dipswitch", ItemType.DipValue => "dipvalue", ItemType.DiskArea => "diskarea", ItemType.Display => "display", ItemType.Driver => "driver", ItemType.Dump => "dump", ItemType.Feature => "feature", ItemType.Info => "info", ItemType.Input => "input", ItemType.Original => "original", ItemType.Part => "part", ItemType.PartFeature => "part_feature", ItemType.Port => "port", ItemType.RamOption => "ramoption", ItemType.Release => "release", ItemType.ReleaseDetails => "release_details", ItemType.Sample => "sample", ItemType.Serials => "serials", ItemType.SharedFeat => "sharedfeat", ItemType.Slot => "slot", ItemType.SlotOption => "slotoption", ItemType.SoftwareList => "softwarelist", ItemType.Sound => "sound", ItemType.SourceDetails => "source_details", ItemType.Video => "video", ItemType.Blank => "blank", ItemType.NULL => null, _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this LoadFlag value) { return value switch { LoadFlag.Load16Byte => "load16_byte", LoadFlag.Load16Word => "load16_word", LoadFlag.Load16WordSwap => "load16_word_swap", LoadFlag.Load32Byte => "load32_byte", LoadFlag.Load32Word => "load32_word", LoadFlag.Load32WordSwap => "load32_word_swap", LoadFlag.Load32DWord => "load32_dword", LoadFlag.Load64Word => "load64_word", LoadFlag.Load64WordSwap => "load64_word_swap", LoadFlag.Reload => "reload", LoadFlag.Fill => "fill", LoadFlag.Continue => "continue", LoadFlag.ReloadPlain => "reload_plain", LoadFlag.Ignore => "ignore", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// True to use the second mapping option, if it exists /// String value representing the input, default on error public static string? AsStringValue(this MergingFlag value, bool useSecond = false) { return value switch { MergingFlag.None => "none", MergingFlag.Split => "split", MergingFlag.Merged => "merged", MergingFlag.NonMerged => useSecond ? "unmerged" : "nonmerged", MergingFlag.FullMerged => "fullmerged", MergingFlag.DeviceNonMerged => useSecond ? "devicenonmerged" : "device", MergingFlag.FullNonMerged => useSecond ? "fullnonmerged" : "full", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, default on error public static string? AsStringValue(this NodumpFlag value) { return value switch { NodumpFlag.None => "none", NodumpFlag.Obsolete => "obsolete", NodumpFlag.Required => "required", NodumpFlag.Ignore => "ignore", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this OpenMSXSubType value) { return value switch { OpenMSXSubType.Rom => "rom", OpenMSXSubType.MegaRom => "megarom", OpenMSXSubType.SCCPlusCart => "sccpluscart", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// True to use the second mapping option, if it exists /// String value representing the input, default on error public static string? AsStringValue(this PackingFlag value, bool useSecond = false) { return value switch { PackingFlag.None => "none", PackingFlag.Zip => useSecond ? "yes" : "zip", PackingFlag.Unzip => useSecond ? "no" : "unzip", PackingFlag.Partial => "partial", PackingFlag.Flat => "flat", PackingFlag.FileOnly => "fileonly", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this Relation value) { return value switch { Relation.Equal => "eq", Relation.NotEqual => "ne", Relation.GreaterThan => "gt", Relation.LessThanOrEqual => "le", Relation.LessThan => "lt", Relation.GreaterThanOrEqual => "ge", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this Rotation value, bool useSecond = false) { return value switch { Rotation.North => useSecond ? "vertical" : "0", Rotation.East => useSecond ? "horizontal" : "90", Rotation.South => useSecond ? "vertical" : "180", Rotation.West => useSecond ? "horizontal" : "270", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this Runnable value) { return value switch { Runnable.No => "no", Runnable.Partial => "partial", Runnable.Yes => "yes", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this SoftwareListStatus value) { return value switch { SoftwareListStatus.None => "none", SoftwareListStatus.Original => "original", SoftwareListStatus.Compatible => "compatible", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// True to use the second mapping option, if it exists /// String value representing the input, null on error public static string? AsStringValue(this Supported value, bool useSecond = false) { return value switch { Supported.No => useSecond ? "unsupported" : "no", Supported.Partial => "partial", Supported.Yes => useSecond ? "supported" : "yes", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this Width value) { return value switch { Width.Byte => "8", Width.Short => "16", Width.Int => "32", Width.Long => "64", _ => null, }; } /// /// Get the string value for an input enum, if possible /// /// Enum value to parse/param> /// String value representing the input, null on error public static string? AsStringValue(this SupportStatus value) { return value switch { SupportStatus.Good => "good", SupportStatus.Imperfect => "imperfect", SupportStatus.Preliminary => "preliminary", SupportStatus.Test => "test", _ => null, }; } /// /// Get string value from input bool? /// /// bool? to get value from /// String corresponding to the bool? public static string? FromYesNo(this bool? yesno) { return yesno switch { true => "yes", false => "no", _ => null, }; } #endregion } }