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
private 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
private 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)
{
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)
{
return chipType switch
{
ChipType.CPU => "cpu",
ChipType.Audio => "audio",
_ => null,
};
}
///
/// Get string value from input ControlType
///
/// ControlType to get value from
/// String value corresponding to the ControlType
public static string FromControlType(this ControlType controlType)
{
return controlType 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 string value from input DeviceType
///
/// vDeviceType to get value from
/// String value corresponding to the DeviceType
public static string FromDeviceType(this DeviceType deviceType)
{
return deviceType 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 string value from input DisplayType
///
/// DisplayType to get value from
/// String value corresponding to the DisplayType
public static string FromDisplayType(this DisplayType displayType)
{
return displayType switch
{
DisplayType.Raster => "raster",
DisplayType.Vector => "vector",
DisplayType.LCD => "lcd",
DisplayType.SVG => "svg",
DisplayType.Unknown => "unknown",
_ => null,
};
}
///
/// Get string value from input Endianness
///
/// Endianness to get value from
/// String value corresponding to the Endianness
public static string FromEndianness(this Endianness endianness)
{
return endianness switch
{
Endianness.Big => "big",
Endianness.Little => "little",
_ => null,
};
}
///
/// Get string value from input FeatureStatus
///
/// FeatureStatus to get value from
/// String value corresponding to the FeatureStatus
public static string FromFeatureStatus(this FeatureStatus featureStatus)
{
return featureStatus switch
{
FeatureStatus.Unemulated => "unemulated",
FeatureStatus.Imperfect => "imperfect",
_ => null,
};
}
///
/// Get string value from input FeatureType
///
/// FeatureType to get value from
/// String value corresponding to the FeatureType
public static string FromFeatureType(this FeatureType featureType)
{
return featureType 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 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)
{
return status switch
{
ItemStatus.Good => "good",
ItemStatus.BadDump => "baddump",
ItemStatus.Nodump => yesno ? "yes" : "nodump",
ItemStatus.Verified => "verified",
_ => null,
};
}
///
/// Get string value from input LoadFlag
///
/// LoadFlag to get value from
/// String value corresponding to the LoadFlag
public static string FromLoadFlag(this LoadFlag loadFlag)
{
return loadFlag 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 string value from input ItemType?
///
/// ItemType? to get value from
/// String value corresponding to the ItemType?
public static string FromItemType(this ItemType? itemType)
{
return itemType switch
{
ItemType.Adjuster => "adjuster",
ItemType.Analog => "analog",
ItemType.Archive => "archive",
ItemType.BiosSet => "biosset",
ItemType.Blank => "blank",
ItemType.Chip => "chip",
ItemType.Condition => "condition",
ItemType.Configuration => "configuration",
ItemType.Control => "control",
ItemType.DataArea => "dataarea",
ItemType.Device => "device",
ItemType.DeviceReference => "device_ref",
ItemType.DipSwitch => "dipswitch",
ItemType.Disk => "disk",
ItemType.DiskArea => "diskarea",
ItemType.Display => "display",
ItemType.Driver => "driver",
ItemType.Extension => "extension",
ItemType.Feature => "feature",
ItemType.Info => "info",
ItemType.Input => "input",
ItemType.Instance => "instance",
ItemType.Location => "location",
ItemType.Media => "media",
ItemType.Part => "part",
ItemType.PartFeature => "part_feature",
ItemType.Port => "port",
ItemType.RamOption => "ramoption",
ItemType.Release => "release",
ItemType.ReleaseDetails => "release_details",
ItemType.Rom => "rom",
ItemType.Sample => "sample",
ItemType.Serials => "serials",
ItemType.Setting => "setting",
ItemType.SharedFeature => "sharedfeat",
ItemType.Slot => "slot",
ItemType.SlotOption => "slotoption",
ItemType.SoftwareList => "softwarelist",
ItemType.Sound => "sound",
ItemType.SourceDetails => "source_details",
_ => null,
};
}
///
/// 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)
{
return gametype switch
{
MachineType.Bios => "bios",
MachineType.Device => old ? "dev" : "device",
MachineType.Mechanical => old ? "mech" : "mechanical",
_ => null,
};
}
///
/// 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)
{
return merging switch
{
MergingFlag.Split => "split",
MergingFlag.Merged => "merged",
MergingFlag.FullMerged => "fullmerged",
MergingFlag.NonMerged => romCenter ? "unmerged" : "nonmerged",
MergingFlag.FullNonMerged => "full",
MergingFlag.DeviceNonMerged => "device",
_ => null,
};
}
///
/// Get string value from input NodumpFlag
///
/// NodumpFlag to get value from
/// String value corresponding to the NodumpFlag
public static string FromNodumpFlag(this NodumpFlag nodump)
{
return nodump switch
{
NodumpFlag.Obsolete => "obsolete",
NodumpFlag.Required => "required",
NodumpFlag.Ignore => "ignore",
_ => null,
};
}
///
/// Get string value from input OpenMSXSubType
///
/// OpenMSXSubType to get value from
/// String value corresponding to the OpenMSXSubType
public static string FromOpenMSXSubType(this OpenMSXSubType itemType)
{
return itemType switch
{
OpenMSXSubType.Rom => "rom",
OpenMSXSubType.MegaRom => "megarom",
OpenMSXSubType.SCCPlusCart => "sccpluscart",
_ => null,
};
}
///
/// 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)
{
return packing switch
{
PackingFlag.Zip => yesno ? "yes" : "zip",
PackingFlag.Unzip => yesno ? "no" : "unzip",
PackingFlag.Partial => "partial",
PackingFlag.Flat => "flat",
_ => null,
};
}
///
/// Get string value from input Relation
///
/// Relation to get value from
/// String value corresponding to the Relation
public static string FromRelation(this Relation relation)
{
return relation switch
{
Relation.Equal => "eq",
Relation.NotEqual => "ne",
Relation.GreaterThan => "gt",
Relation.LessThanOrEqual => "le",
Relation.LessThan => "lt",
Relation.GreaterThanOrEqual => "ge",
_ => null,
};
}
///
/// Get string value from input Runnable
///
/// Runnable to get value from
/// String value corresponding to the Runnable
public static string FromRunnable(this Runnable runnable)
{
return runnable switch
{
Runnable.No => "no",
Runnable.Partial => "partial",
Runnable.Yes => "yes",
_ => null,
};
}
///
/// Get string value from input SoftwareListStatus
///
/// SoftwareListStatus to get value from
/// String value corresponding to the SoftwareListStatus
public static string FromSoftwareListStatus(this SoftwareListStatus status)
{
return status switch
{
SoftwareListStatus.Original => "original",
SoftwareListStatus.Compatible => "compatible",
_ => null,
};
}
///
/// 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)
{
return supported switch
{
Supported.No => verbose ? "unsupported" : "no",
Supported.Partial => "partial",
Supported.Yes => verbose ? "supported" : "yes",
_ => null,
};
}
///
/// Get string value from input SupportStatus
///
/// SupportStatus to get value from
/// String value corresponding to the SupportStatus
public static string FromSupportStatus(this SupportStatus supportStatus)
{
return supportStatus switch
{
SupportStatus.Good => "good",
SupportStatus.Imperfect => "imperfect",
SupportStatus.Preliminary => "preliminary",
_ => 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,
};
}
///
/// Get the string value for an input enum, if possible
///
/// Enum value to parse/param>
/// Enum type that is expected
/// String value representing the input, default on error
private static string? AsStringValue(T value)
{
// Get the mapping dictionary
var mappings = GenerateToString();
// 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
///
/// Enum type to generate from
/// Dictionary of enum to string values
private static Dictionary GenerateToString()
{
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;
// Always use the first value in the list
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
}
}