using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; namespace SabreTools.Core.Tools { public static class Converters { #region Enum to Enum /// /// Get the DatItemFields associated with each hash type /// public static List AsDatItemFields(this Hash hash) { List fields = new(); if (hash.HasFlag(Hash.CRC)) fields.Add(DatItemField.CRC); if (hash.HasFlag(Hash.MD5)) fields.Add(DatItemField.MD5); if (hash.HasFlag(Hash.SHA1)) fields.Add(DatItemField.SHA1); if (hash.HasFlag(Hash.SHA256)) fields.Add(DatItemField.SHA256); if (hash.HasFlag(Hash.SHA384)) fields.Add(DatItemField.SHA384); if (hash.HasFlag(Hash.SHA512)) fields.Add(DatItemField.SHA512); if (hash.HasFlag(Hash.SpamSum)) fields.Add(DatItemField.SpamSum); return fields; } #endregion #region String to Enum /// /// Get ChipType value from input string /// /// String to get value from /// ChipType value corresponding to the string public static ChipType AsChipType(this string chipType) => AsEnumValue(chipType); /// /// Get ControlType value from input string /// /// String to get value from /// ControlType value corresponding to the string public static ControlType AsControlType(this string controlType) => AsEnumValue(controlType); /// /// Get DatHeaderField value from input string /// /// String to get value from /// DatHeaderField value corresponding to the string public static DatHeaderField AsDatHeaderField(this string input) { // If the input is empty, we return null if (string.IsNullOrEmpty(input)) return DatHeaderField.NULL; // Normalize the input input = input.ToLowerInvariant(); // Create regex string headerRegex = @"^(dat|header|datheader)[.\-_\s]"; // If we don't have a header field, skip if (!Regex.IsMatch(input, headerRegex)) return DatHeaderField.NULL; // Replace the match and re-normalize string headerInput = Regex.Replace(input, headerRegex, string.Empty) .Replace(' ', '_') .Replace('-', '_') .Replace('.', '_'); return AsEnumValue(headerInput); } /// /// Get DatItemField value from input string /// /// String to get value from /// DatItemField value corresponding to the string public static DatItemField AsDatItemField(this string input) { // If the input is empty, we return null if (string.IsNullOrEmpty(input)) return DatItemField.NULL; // Normalize the input input = input.ToLowerInvariant(); // Create regex string datItemRegex = @"^(item|datitem)[.\-_\s]"; // If we don't have an item field, skip if (!Regex.IsMatch(input, datItemRegex)) return DatItemField.NULL; // Replace the match and re-normalize string itemInput = Regex.Replace(input, datItemRegex, string.Empty) .Replace(' ', '_') .Replace('-', '_') .Replace('.', '_'); return AsEnumValue(itemInput); } /// /// Get DeviceType value from input string /// /// String to get value from /// DeviceType value corresponding to the string public static DeviceType AsDeviceType(this string deviceType) => AsEnumValue(deviceType); /// /// Get DisplayType value from input string /// /// String to get value from /// DisplayType value corresponding to the string public static DisplayType AsDisplayType(this string displayType) => AsEnumValue(displayType); /// /// Get Endianness value from input string /// /// String to get value from /// Endianness value corresponding to the string public static Endianness AsEndianness(this string endianness) => AsEnumValue(endianness); /// /// Get FeatureStatus value from input string /// /// String to get value from /// FeatureStatus value corresponding to the string public static FeatureStatus AsFeatureStatus(this string featureStatus) => AsEnumValue(featureStatus); /// /// Get FeatureType value from input string /// /// String to get value from /// FeatureType value corresponding to the string public static FeatureType AsFeatureType(this string featureType) => AsEnumValue(featureType); /// /// Get ItemStatus value from input string /// /// String to get value from /// ItemStatus value corresponding to the string public static ItemStatus AsItemStatus(this string status) => AsEnumValue(status); /// /// Get ItemType? value from input string /// /// String to get value from /// ItemType? value corresponding to the string public static ItemType AsItemType(this string itemType) => AsEnumValue(itemType); /// /// Get LoadFlag value from input string /// /// String to get value from /// LoadFlag value corresponding to the string public static LoadFlag AsLoadFlag(this string loadFlag) => AsEnumValue(loadFlag); /// /// Get LogLevel value from input string /// /// String to get value from /// LogLevel value corresponding to the string public static LogLevel AsLogLevel(this string logLevel) => AsEnumValue(logLevel); /// /// Get MachineField value from input string /// /// String to get value from /// MachineField value corresponding to the string public static MachineField AsMachineField(this string input) { // If the input is empty, we return null if (string.IsNullOrEmpty(input)) return MachineField.NULL; // Normalize the input input = input.ToLowerInvariant(); // Create regex string machineRegex = @"^(game|machine)[.\-_\s]"; // If we don't have a machine field, skip if (!Regex.IsMatch(input, machineRegex)) return MachineField.NULL; // Replace the match and re-normalize string machineInput = Regex.Replace(input, machineRegex, string.Empty) .Replace(' ', '_') .Replace('-', '_') .Replace('.', '_'); return AsEnumValue(machineInput); } /// /// Get MachineType value from input string /// /// String to get value from /// MachineType value corresponding to the string public static MachineType AsMachineType(this string gametype) => AsEnumValue(gametype); /// /// Get MergingFlag value from input string /// /// String to get value from /// MergingFlag value corresponding to the string public static MergingFlag AsMergingFlag(this string merging) => AsEnumValue(merging); /// /// Get NodumpFlag value from input string /// /// String to get value from /// NodumpFlag value corresponding to the string public static NodumpFlag AsNodumpFlag(this string nodump) => AsEnumValue(nodump); /// /// Get OpenMSXSubType value from input string /// /// String to get value from /// OpenMSXSubType value corresponding to the string public static OpenMSXSubType AsOpenMSXSubType(this string subType) => AsEnumValue(subType); /// /// Get PackingFlag value from input string /// /// String to get value from /// PackingFlag value corresponding to the string public static PackingFlag AsPackingFlag(this string packing) => AsEnumValue(packing); /// /// Get Relation value from input string /// /// String to get value from /// Relation value corresponding to the string public static Relation AsRelation(this string relation) => AsEnumValue(relation); /// /// Get Runnable value from input string /// /// String to get value from /// Runnable value corresponding to the string public static Runnable AsRunnable(this string runnable) => AsEnumValue(runnable); /// /// Get SoftwareListStatus value from input string /// /// String to get value from /// SoftwareListStatus value corresponding to the string public static SoftwareListStatus AsSoftwareListStatus(this string status) => AsEnumValue(status); /// /// Get Supported value from input string /// /// String to get value from /// Supported value corresponding to the string public static Supported AsSupported(this string supported) => AsEnumValue(supported); /// /// Get SupportStatus value from input string /// /// String to get value from /// SupportStatus value corresponding to the string public static SupportStatus AsSupportStatus(this string supportStatus) => AsEnumValue(supportStatus); /// /// 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 { "yes" or "true" => true, "no" or "false" => false, _ => null, }; } /// /// Get the enum value for an input string, if possible /// /// String value to parse/param> /// Enum type that is expected /// Enum value representing the input, default on error internal static T AsEnumValue(string value) { // Get the mapping dictionary var mappings = GenerateToEnum(); // Normalize the input value value = value?.ToLowerInvariant(); if (value == null) return default; // Try to get the value from the mappings if (mappings.ContainsKey(value)) return mappings[value]; // Otherwise, return the default value for the enum return default; } /// /// Get a set of mappings from strings to enum values /// /// Enum type that is expected /// Dictionary of string to enum values internal static Dictionary GenerateToEnum() { try { // Get all of the values for the enum type var values = Enum.GetValues(typeof(T)); // Build the output dictionary Dictionary mappings = new(); foreach (T value in values) { // Try to get the mapping attribute MappingAttribute attr = AttributeHelper.GetAttribute(value); if (attr?.Mappings == null || !attr.Mappings.Any()) continue; // Loop through the mappings and add each foreach (string mapString in attr.Mappings) { if (mapString != null) mappings[mapString] = value; } } // Return the output dictionary return mappings; } catch { // This should not happen, only if the type was not an enum return new Dictionary(); } } #endregion #region Enum to String /// /// Get string value from input ChipType /// /// ChipType to get value from /// String value corresponding to the ChipType public static string FromChipType(this ChipType chipType) => AsStringValue(chipType); /// /// Get string value from input ControlType /// /// ControlType to get value from /// String value corresponding to the ControlType public static string FromControlType(this ControlType controlType) => AsStringValue(controlType); /// /// Get string value from input DeviceType /// /// vDeviceType to get value from /// String value corresponding to the DeviceType public static string FromDeviceType(this DeviceType deviceType) => AsStringValue(deviceType); /// /// Get string value from input DisplayType /// /// DisplayType to get value from /// String value corresponding to the DisplayType public static string FromDisplayType(this DisplayType displayType) => AsStringValue(displayType); /// /// Get string value from input Endianness /// /// Endianness to get value from /// String value corresponding to the Endianness public static string FromEndianness(this Endianness endianness) => AsStringValue(endianness); /// /// Get string value from input FeatureStatus /// /// FeatureStatus to get value from /// String value corresponding to the FeatureStatus public static string FromFeatureStatus(this FeatureStatus featureStatus) => AsStringValue(featureStatus); /// /// Get string value from input FeatureType /// /// FeatureType to get value from /// String value corresponding to the FeatureType public static string FromFeatureType(this FeatureType featureType) => AsStringValue(featureType); /// /// Get string value from input ItemStatus /// /// ItemStatus to get value from /// True to use Yes/No format instead /// String value corresponding to the ItemStatus public static string FromItemStatus(this ItemStatus status, bool yesno) => AsStringValue(status, yesno); /// /// Get string value from input ItemType? /// /// ItemType? to get value from /// String value corresponding to the ItemType? public static string FromItemType(this ItemType itemType) => AsStringValue(itemType); /// /// Get string value from input LoadFlag /// /// LoadFlag to get value from /// String value corresponding to the LoadFlag public static string FromLoadFlag(this LoadFlag loadFlag) => AsStringValue(loadFlag); /// /// Get string value from input MachineType /// /// MachineType to get value from /// True to use old naming instead /// String value corresponding to the MachineType public static string FromMachineType(this MachineType gametype, bool old) => AsStringValue(gametype, old); /// /// Get string value from input MergingFlag /// /// MergingFlag to get value from /// True to use RomCenter naming instead /// String value corresponding to the MergingFlag public static string FromMergingFlag(this MergingFlag merging, bool romCenter) => AsStringValue(merging, romCenter); /// /// Get string value from input NodumpFlag /// /// NodumpFlag to get value from /// String value corresponding to the NodumpFlag public static string FromNodumpFlag(this NodumpFlag nodump) => AsStringValue(nodump); /// /// Get string value from input OpenMSXSubType /// /// OpenMSXSubType to get value from /// String value corresponding to the OpenMSXSubType public static string FromOpenMSXSubType(this OpenMSXSubType subType) => AsStringValue(subType); /// /// Get string value from input PackingFlag /// /// PackingFlag to get value from /// True to use Yes/No format instead /// String value corresponding to the PackingFlag public static string FromPackingFlag(this PackingFlag packing, bool yesno) => AsStringValue(packing, yesno); /// /// Get string value from input Relation /// /// Relation to get value from /// String value corresponding to the Relation public static string FromRelation(this Relation relation) => AsStringValue(relation); /// /// Get string value from input Runnable /// /// Runnable to get value from /// String value corresponding to the Runnable public static string FromRunnable(this Runnable runnable) => AsStringValue(runnable); /// /// Get string value from input SoftwareListStatus /// /// SoftwareListStatus to get value from /// String value corresponding to the SoftwareListStatus public static string FromSoftwareListStatus(this SoftwareListStatus status) => AsStringValue(status); /// /// Get string value from input Supported /// /// Supported to get value from /// True to use verbose output, false otherwise /// String value corresponding to the Supported public static string FromSupported(this Supported supported, bool verbose) => AsStringValue(supported, verbose); /// /// Get string value from input SupportStatus /// /// SupportStatus to get value from /// String value corresponding to the SupportStatus public static string FromSupportStatus(this SupportStatus supportStatus) => AsStringValue(supportStatus); /// /// 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, }; } /// /// 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 /// Enum type that is expected /// String value representing the input, default on error internal static string? AsStringValue(T value, bool useSecond = false) { // Get the mapping dictionary var mappings = GenerateToString(useSecond); // Try to get the value from the mappings if (mappings.ContainsKey(value)) return mappings[value]; // Otherwise, return null return null; } /// /// Get a set of mappings from enum values to string /// /// True to use the second mapping option, if it exists /// Enum type that is expected /// Dictionary of enum to string values internal static Dictionary GenerateToString(bool useSecond) { try { // Get all of the values for the enum type var values = Enum.GetValues(typeof(T)); // Build the output dictionary Dictionary mappings = new(); foreach (T value in values) { // Try to get the mapping attribute MappingAttribute attr = AttributeHelper.GetAttribute(value); if (attr?.Mappings == null || !attr.Mappings.Any()) continue; // Use either the first or second item in the list if (attr.Mappings.Length > 1 && useSecond) mappings[value] = attr.Mappings[1]; else mappings[value] = attr.Mappings[0]; } // Return the output dictionary return mappings; } catch { // This should not happen, only if the type was not an enum return new Dictionary(); } } #endregion } }