From fc4c17c0ee500ed0ef3962a58845775e362d139f Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 4 Sep 2023 16:30:28 -0400 Subject: [PATCH] Add code from MPF --- Attributes/AttributeHelper.cs | 48 + Attributes/HumanReadableAttribute.cs | 25 + Attributes/LanguageAttribute.cs | 26 + Attributes/SystemAttribute.cs | 55 + Converters/DiscCategoryConverter.cs | 26 + Converters/DiscTypeConverter.cs | 26 + Converters/LanguageConverter.cs | 32 + Converters/LanguageSelectionConverter.cs | 32 + Converters/RegionConverter.cs | 26 + Converters/SystemConverter.cs | 26 + Converters/YesNoConverter.cs | 26 + Data/Constants.cs | 306 ++ Data/Enumerations.cs | 3751 ++++++++++++++++++++++ Data/Extensions.cs | 2176 +++++++++++++ Data/SubmissionInfo.cs | 570 ++++ Web/RedumpHttpClient.cs | 808 +++++ Web/RedumpWebClient.cs | 837 +++++ 17 files changed, 8796 insertions(+) create mode 100644 Attributes/AttributeHelper.cs create mode 100644 Attributes/HumanReadableAttribute.cs create mode 100644 Attributes/LanguageAttribute.cs create mode 100644 Attributes/SystemAttribute.cs create mode 100644 Converters/DiscCategoryConverter.cs create mode 100644 Converters/DiscTypeConverter.cs create mode 100644 Converters/LanguageConverter.cs create mode 100644 Converters/LanguageSelectionConverter.cs create mode 100644 Converters/RegionConverter.cs create mode 100644 Converters/SystemConverter.cs create mode 100644 Converters/YesNoConverter.cs create mode 100644 Data/Constants.cs create mode 100644 Data/Enumerations.cs create mode 100644 Data/Extensions.cs create mode 100644 Data/SubmissionInfo.cs create mode 100644 Web/RedumpHttpClient.cs create mode 100644 Web/RedumpWebClient.cs diff --git a/Attributes/AttributeHelper.cs b/Attributes/AttributeHelper.cs new file mode 100644 index 0000000..0cc457b --- /dev/null +++ b/Attributes/AttributeHelper.cs @@ -0,0 +1,48 @@ +using System; +using System.Linq; + +namespace SabreTools.RedumpLib.Attributes +{ + public static class AttributeHelper + { + /// + /// Get the HumanReadableAttribute from a supported value + /// + /// Value to use + /// HumanReadableAttribute attached to the value + public static HumanReadableAttribute GetAttribute(T value) + { + // Null value in, null value out + if (value == null) + return null; + + // Current enumeration type + var enumType = typeof(T); + if (Nullable.GetUnderlyingType(enumType) != null) + enumType = Nullable.GetUnderlyingType(enumType); + + // If the value returns a null on ToString, just return null + string valueStr = value.ToString(); + if (string.IsNullOrWhiteSpace(valueStr)) + return null; + + // Get the member info array + var memberInfos = enumType?.GetMember(valueStr); + if (memberInfos == null) + return null; + + // Get the enum value info from the array, if possible + var enumValueMemberInfo = memberInfos.FirstOrDefault(m => m.DeclaringType == enumType); + if (enumValueMemberInfo == null) + return null; + + // Try to get the relevant attribute + var attributes = enumValueMemberInfo.GetCustomAttributes(typeof(HumanReadableAttribute), true); + if (attributes == null) + return null; + + // Return the first attribute, if possible + return (HumanReadableAttribute)attributes.FirstOrDefault(); + } + } +} \ No newline at end of file diff --git a/Attributes/HumanReadableAttribute.cs b/Attributes/HumanReadableAttribute.cs new file mode 100644 index 0000000..2f15778 --- /dev/null +++ b/Attributes/HumanReadableAttribute.cs @@ -0,0 +1,25 @@ +using System; + +namespace SabreTools.RedumpLib.Attributes +{ + /// + /// Generic attribute for human readable values + /// + public class HumanReadableAttribute : Attribute + { + /// + /// Item is marked as obsolete or unusable + /// + public bool Available { get; set; } = true; + + /// + /// Human-readable name of the item + /// + public string LongName { get; set; } + + /// + /// Internally used name of the item + /// + public string ShortName { get; set; } + } +} \ No newline at end of file diff --git a/Attributes/LanguageAttribute.cs b/Attributes/LanguageAttribute.cs new file mode 100644 index 0000000..768cb40 --- /dev/null +++ b/Attributes/LanguageAttribute.cs @@ -0,0 +1,26 @@ +namespace SabreTools.RedumpLib.Attributes +{ + /// + /// Attribute specifc to Language values + /// + /// + /// Some languages have multiple proper names. Should all be supported? + /// + public class LanguageAttribute : HumanReadableAttribute + { + /// + /// ISO 639-1 Code + /// + public string TwoLetterCode { get; set; } = null; + + /// + /// ISO 639-2 Code (Standard or Bibliographic) + /// + public string ThreeLetterCode { get; set; } = null; + + /// + /// ISO 639-2 Code (Terminology) + /// + public string ThreeLetterCodeAlt { get; set; } = null; + } +} \ No newline at end of file diff --git a/Attributes/SystemAttribute.cs b/Attributes/SystemAttribute.cs new file mode 100644 index 0000000..60671d8 --- /dev/null +++ b/Attributes/SystemAttribute.cs @@ -0,0 +1,55 @@ +using RedumpLib.Data; + +namespace SabreTools.RedumpLib.Attributes +{ + /// + /// Attribute specifc to Redump System values + /// + public class SystemAttribute : HumanReadableAttribute + { + /// + /// Category for the system + /// + public SystemCategory Category { get; set; } + + /// + /// System is restricted to dumpers + /// + public bool IsBanned { get; set; } = false; + + /// + /// System has a CUE pack + /// + public bool HasCues { get; set; } = false; + + /// + /// System has a DAT + /// + public bool HasDat { get; set; } = false; + + /// + /// System has a decrypted keys pack + /// + public bool HasDkeys { get; set; } = false; + + /// + /// System has a GDI pack + /// + public bool HasGdi { get; set; } = false; + + /// + /// System has a keys pack + /// + public bool HasKeys { get; set; } = false; + + /// + /// System has an LSD pack + /// + public bool HasLsd { get; set; } = false; + + /// + /// System has an SBI pack + /// + public bool HasSbi { get; set; } = false; + } +} \ No newline at end of file diff --git a/Converters/DiscCategoryConverter.cs b/Converters/DiscCategoryConverter.cs new file mode 100644 index 0000000..fd3fa6e --- /dev/null +++ b/Converters/DiscCategoryConverter.cs @@ -0,0 +1,26 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using RedumpLib.Data; + +namespace SabreTools.RedumpLib.Converters +{ + /// + /// Serialize DiscCategory enum values + /// + public class DiscCategoryConverter : JsonConverter + { + public override bool CanRead { get { return false; } } + + public override DiscCategory? ReadJson(JsonReader reader, Type objectType, DiscCategory? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, DiscCategory? value, JsonSerializer serializer) + { + JToken t = JToken.FromObject(value.LongName() ?? string.Empty); + t.WriteTo(writer); + } + } +} \ No newline at end of file diff --git a/Converters/DiscTypeConverter.cs b/Converters/DiscTypeConverter.cs new file mode 100644 index 0000000..fdbe4ee --- /dev/null +++ b/Converters/DiscTypeConverter.cs @@ -0,0 +1,26 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using RedumpLib.Data; + +namespace SabreTools.RedumpLib.Converters +{ + /// + /// Serialize DiscType enum values + /// + public class DiscTypeConverter : JsonConverter + { + public override bool CanRead { get { return false; } } + + public override DiscType? ReadJson(JsonReader reader, Type objectType, DiscType? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, DiscType? value, JsonSerializer serializer) + { + JToken t = JToken.FromObject(value.LongName() ?? string.Empty); + t.WriteTo(writer); + } + } +} \ No newline at end of file diff --git a/Converters/LanguageConverter.cs b/Converters/LanguageConverter.cs new file mode 100644 index 0000000..2848f68 --- /dev/null +++ b/Converters/LanguageConverter.cs @@ -0,0 +1,32 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using RedumpLib.Data; + +namespace SabreTools.RedumpLib.Converters +{ + /// + /// Serialize Language enum values + /// + public class LanguageConverter : JsonConverter + { + public override bool CanRead { get { return false; } } + + public override Language?[] ReadJson(JsonReader reader, Type objectType, Language?[] existingValue, bool hasExistingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, Language?[] value, JsonSerializer serializer) + { + JArray array = new JArray(); + foreach (var val in value) + { + JToken t = JToken.FromObject(val.ShortName() ?? string.Empty); + array.Add(t); + } + + array.WriteTo(writer); + } + } +} \ No newline at end of file diff --git a/Converters/LanguageSelectionConverter.cs b/Converters/LanguageSelectionConverter.cs new file mode 100644 index 0000000..ea0762a --- /dev/null +++ b/Converters/LanguageSelectionConverter.cs @@ -0,0 +1,32 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using RedumpLib.Data; + +namespace SabreTools.RedumpLib.Converters +{ + /// + /// Serialize LanguageSelection enum values + /// + public class LanguageSelectionConverter : JsonConverter + { + public override bool CanRead { get { return false; } } + + public override LanguageSelection?[] ReadJson(JsonReader reader, Type objectType, LanguageSelection?[] existingValue, bool hasExistingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, LanguageSelection?[] value, JsonSerializer serializer) + { + JArray array = new JArray(); + foreach (var val in value) + { + JToken t = JToken.FromObject(val.LongName() ?? string.Empty); + array.Add(t); + } + + array.WriteTo(writer); + } + } +} \ No newline at end of file diff --git a/Converters/RegionConverter.cs b/Converters/RegionConverter.cs new file mode 100644 index 0000000..48c79af --- /dev/null +++ b/Converters/RegionConverter.cs @@ -0,0 +1,26 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using RedumpLib.Data; + +namespace SabreTools.RedumpLib.Converters +{ + /// + /// Serialize Region enum values + /// + public class RegionConverter : JsonConverter + { + public override bool CanRead { get { return false; } } + + public override Region? ReadJson(JsonReader reader, Type objectType, Region? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, Region? value, JsonSerializer serializer) + { + JToken t = JToken.FromObject(value.ShortName() ?? string.Empty); + t.WriteTo(writer); + } + } +} \ No newline at end of file diff --git a/Converters/SystemConverter.cs b/Converters/SystemConverter.cs new file mode 100644 index 0000000..aa1dd94 --- /dev/null +++ b/Converters/SystemConverter.cs @@ -0,0 +1,26 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using RedumpLib.Data; + +namespace SabreTools.RedumpLib.Converters +{ + /// + /// Serialize RedumpSystem enum values + /// + public class SystemConverter : JsonConverter + { + public override bool CanRead { get { return false; } } + + public override RedumpSystem? ReadJson(JsonReader reader, Type objectType, RedumpSystem? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, RedumpSystem? value, JsonSerializer serializer) + { + JToken t = JToken.FromObject(value.ShortName() ?? string.Empty); + t.WriteTo(writer); + } + } +} \ No newline at end of file diff --git a/Converters/YesNoConverter.cs b/Converters/YesNoConverter.cs new file mode 100644 index 0000000..ffe0534 --- /dev/null +++ b/Converters/YesNoConverter.cs @@ -0,0 +1,26 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using RedumpLib.Data; + +namespace SabreTools.RedumpLib.Converters +{ + /// + /// Serialize YesNo enum values + /// + public class YesNoConverter : JsonConverter + { + public override bool CanRead { get { return false; } } + + public override YesNo? ReadJson(JsonReader reader, Type objectType, YesNo? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, YesNo? value, JsonSerializer serializer) + { + JToken t = JToken.FromObject(value.LongName() ?? string.Empty); + t.WriteTo(writer); + } + } +} \ No newline at end of file diff --git a/Data/Constants.cs b/Data/Constants.cs new file mode 100644 index 0000000..bad33b2 --- /dev/null +++ b/Data/Constants.cs @@ -0,0 +1,306 @@ +using System.Text.RegularExpressions; + +namespace SabreTools.RedumpLib.Data +{ + public static class Constants + { + // TODO: Add RegexOptions.Compiled + #region Regular Expressions + + /// + /// Regex matching the added field on a disc page + /// + public static Regex AddedRegex = new Regex(@"Added(.*?)"); + + /// + /// Regex matching the barcode field on a disc page + /// + public static Regex BarcodeRegex = new Regex(@"Barcode(.*?)"); + + /// + /// Regex matching the BCA field on a disc page + /// + public static Regex BcaRegex = new Regex(@"

BCA .*?/>

" + + "RowContentsASCII" + + "(?.*?)(?.*?)(?.*?)" + + "(?.*?)(?.*?)(?.*?)" + + "(?.*?)(?.*?)(?.*?)" + + "(?.*?)(?.*?)(?.*?)", RegexOptions.Singleline); + + /// + /// Regex matching the category field on a disc page + /// + public static Regex CategoryRegex = new Regex(@"Category(.*?)"); + + /// + /// Regex matching the comments field on a disc page + /// + public static Regex CommentsRegex = new Regex(@"Comments(.*?)", RegexOptions.Singleline); + + /// + /// Regex matching the contents field on a disc page + /// + public static Regex ContentsRegex = new Regex(@"Contents(.*?)", RegexOptions.Singleline); + + /// + /// Regex matching individual disc links on a results page + /// + public static Regex DiscRegex = new Regex(@""); + + /// + /// Regex matching the disc number or letter field on a disc page + /// + public static Regex DiscNumberLetterRegex = new Regex(@"\((.*?)\)"); + + /// + /// Regex matching the dumpers on a disc page + /// + public static Regex DumpersRegex = new Regex(@""); + + /// + /// Regex matching the edition field on a disc page + /// + public static Regex EditionRegex = new Regex(@"Edition(.*?)"); + + /// + /// Regex matching the error count field on a disc page + /// + public static Regex ErrorCountRegex = new Regex(@"Errors count(.*?)"); + + /// + /// Regex matching the foreign title field on a disc page + /// + public static Regex ForeignTitleRegex = new Regex(@"

(.*?)

"); + + /// + /// Regex matching the "full match" ID list from a WIP disc page + /// + public static Regex FullMatchRegex = new Regex(@"full match ids: (.*?)"); + + /// + /// Regex matching the languages field on a disc page + /// + public static Regex LanguagesRegex = new Regex(@"\s*"); + + /// + /// Regex matching the last modified field on a disc page + /// + public static Regex LastModifiedRegex = new Regex(@"Last modified(.*?)"); + + /// + /// Regex matching the media field on a disc page + /// + public static Regex MediaRegex = new Regex(@"Media(.*?)"); + + /// + /// Regex matching individual WIP disc links on a results page + /// + public static Regex NewDiscRegex = new Regex(@"
"); + + /// + /// Regex matching the "partial match" ID list from a WIP disc page + /// + public static Regex PartialMatchRegex = new Regex(@"partial match ids: (.*?)"); + + /// + /// Regex matching the PVD field on a disc page + /// + public static Regex PvdRegex = new Regex(@"

Primary Volume Descriptor (PVD)

" + + @"Record / EntryContentsDateTimeGMT" + + @"Creation(?.*?)(?.*?)(?.*?)(?.*?)" + + @"Modification(?.*?)(?.*?)(?.*?)(?.*?)" + + @"Expiration(?.*?)(?.*?)(?.*?)(?.*?)" + + @"Effective(?.*?)(?.*?)(?.*?)(?.*?)", RegexOptions.Singleline); + + /// + /// Regex matching the region field on a disc page + /// + public static Regex RegionRegex = new Regex(@"Region
"); + + /// + /// Regex matching a double-layer disc ringcode information + /// + public static Regex RingCodeDoubleRegex = new Regex(@"", RegexOptions.Singleline); // Varies based on available fields, like Addtional Mould + + /// + /// Regex matching a single-layer disc ringcode information + /// + public static Regex RingCodeSingleRegex = new Regex(@"", RegexOptions.Singleline); // Varies based on available fields, like Addtional Mould + + /// + /// Regex matching the serial field on a disc page + /// + public static Regex SerialRegex = new Regex(@"Serial(.*?)"); + + /// + /// Regex matching the system field on a disc page + /// + public static Regex SystemRegex = new Regex(@"System"); + + /// + /// Regex matching the title field on a disc page + /// + public static Regex TitleRegex = new Regex(@"

(.*?)

"); + + /// + /// Regex matching the current nonce token for login + /// + public static Regex TokenRegex = new Regex(@""); + + /// + /// Regex matching a single track on a disc page + /// + public static Regex TrackRegex = new Regex(@"(?.*?)(?.*?)(?.*?)(?.*?)(?.*?)(?.*?)(?.*?)(?.*?)(?.*?)", RegexOptions.Singleline); + + /// + /// Regex matching the track count on a disc page + /// + public static Regex TrackCountRegex = new Regex(@"Number of tracks(.*?)"); + + /// + /// Regex matching the version field on a disc page + /// + public static Regex VersionRegex = new Regex(@"Version(.*?)"); + + /// + /// Regex matching the write offset field on a disc page + /// + public static Regex WriteOffsetRegex = new Regex(@"Write offset(.*?)"); + + #endregion + + #region URLs + + /// + /// Redump disc page URL template + /// + public const string DiscPageUrl = @"http://redump.org/disc/{0}/"; + + /// + /// Redump last modified search URL template + /// + public const string LastModifiedUrl = @"http://redump.org/discs/sort/modified/dir/desc?page={0}"; + + /// + /// Redump login page URL template + /// + public const string LoginUrl = "http://forum.redump.org/login/"; + + /// + /// Redump CUE pack URL template + /// + public const string PackCuesUrl = @"http://redump.org/cues/{0}/"; + + /// + /// Redump DAT pack URL template + /// + public const string PackDatfileUrl = @"http://redump.org/datfile/{0}/"; + + /// + /// Redump DKEYS pack URL template + /// + public const string PackDkeysUrl = @"http://redump.org/dkeys/{0}/"; + + /// + /// Redump GDI pack URL template + /// + public const string PackGdiUrl = @"http://redump.org/gdi/{0}/"; + + /// + /// Redump KEYS pack URL template + /// + public const string PackKeysUrl = @"http://redump.org/keys/{0}/"; + + /// + /// Redump LSD pack URL template + /// + public const string PackLsdUrl = @"http://redump.org/lsd/{0}/"; + + /// + /// Redump SBI pack URL template + /// + public const string PackSbiUrl = @"http://redump.org/sbi/{0}/"; + + /// + /// Redump quicksearch URL template + /// + public const string QuickSearchUrl = @"http://redump.org/discs/quicksearch/{0}/?page={1}"; + + /// + /// Redump user dumps URL template + /// + public const string UserDumpsUrl = @"http://redump.org/discs/dumper/{0}/?page={1}"; + + /// + /// Redump last modified user dumps URL template + /// + public const string UserDumpsLastModifiedUrl = @"http://redump.org/discs/sort/modified/dir/desc/dumper/{0}?page={1}"; + + /// + /// Redump WIP disc page URL template + /// + public const string WipDiscPageUrl = @"http://redump.org/newdisc/{0}/"; + + /// + /// Redump WIP dumps queue URL + /// + public const string WipDumpsUrl = @"http://redump.org/discs-wip/"; + + #endregion + + #region URL Extensions + + /// + /// Changes page subpath + /// + public const string ChangesExt = "changes/"; + + /// + /// Cuesheet download subpath + /// + public const string CueExt = "cue/"; + + /// + /// Edit page subpath + /// + public const string EditExt = "edit/"; + + /// + /// GDI download subpath + /// + public const string GdiExt = "gdi/"; + + /// + /// Key download subpath + /// + public const string KeyExt = "key/"; + + /// + /// LSD download subpath + /// + public const string LsdExt = "lsd/"; + + /// + /// MD5 download subpath + /// + public const string Md5Ext = "md5/"; + + /// + /// SBI download subpath + /// + public const string SbiExt = "sbi/"; + + /// + /// SFV download subpath + /// + public const string SfvExt = "sfv/"; + + /// + /// SHA1 download subpath + /// + public const string Sha1Ext = "sha1/"; + + #endregion + + } +} \ No newline at end of file diff --git a/Data/Enumerations.cs b/Data/Enumerations.cs new file mode 100644 index 0000000..3e63f51 --- /dev/null +++ b/Data/Enumerations.cs @@ -0,0 +1,3751 @@ +using RedumpLib.Attributes; + +namespace SabreTools.RedumpLib.Data +{ + /// + /// List of all disc categories + /// + public enum DiscCategory + { + [HumanReadable(LongName = "Games")] + Games = 1, + + [HumanReadable(LongName = "Demos")] + Demos = 2, + + [HumanReadable(LongName = "Video")] + Video = 3, + + [HumanReadable(LongName = "Audio")] + Audio = 4, + + [HumanReadable(LongName = "Multimedia")] + Multimedia = 5, + + [HumanReadable(LongName = "Applications")] + Applications = 6, + + [HumanReadable(LongName = "Coverdiscs")] + Coverdiscs = 7, + + [HumanReadable(LongName = "Educational")] + Educational = 8, + + [HumanReadable(LongName = "Bonus Discs")] + BonusDiscs = 9, + + [HumanReadable(LongName = "Preproduction")] + Preproduction = 10, + + [HumanReadable(LongName = "Add-Ons")] + AddOns = 11, + } + + /// + /// List of all disc types + /// + /// + /// All names here match Redump names for the types, not official + /// naming. Some names had to be extrapolated due to no current support + /// in the Redump site. + /// + public enum DiscType + { + NONE = 0, + + [HumanReadable(LongName = "BD-25")] + BD25, + + [HumanReadable(LongName = "BD-33")] + BD33, + + [HumanReadable(LongName = "BD-50")] + BD50, + + [HumanReadable(LongName = "BD-66")] + BD66, + + [HumanReadable(LongName = "BD-100")] + BD100, + + [HumanReadable(LongName = "BD-128")] + BD128, + + [HumanReadable(LongName = "CD")] + CD, + + [HumanReadable(LongName = "DVD-5")] + DVD5, + + [HumanReadable(LongName = "DVD-9")] + DVD9, + + [HumanReadable(LongName = "GD-ROM")] + GDROM, + + [HumanReadable(LongName = "HD-DVD SL")] + HDDVDSL, + + [HumanReadable(LongName = "HD-DVD DL")] + HDDVDDL, + + [HumanReadable(LongName = "MIL-CD")] + MILCD, + + [HumanReadable(LongName = "Nintendo GameCube Game Disc")] + NintendoGameCubeGameDisc, + + [HumanReadable(LongName = "Nintendo Wii Optical Disc SL")] + NintendoWiiOpticalDiscSL, + + [HumanReadable(LongName = "Nintendo Wii Optical Disc DL")] + NintendoWiiOpticalDiscDL, + + [HumanReadable(LongName = "Nintendo Wii U Optical Disc SL")] + NintendoWiiUOpticalDiscSL, + + [HumanReadable(LongName = "UMD SL")] + UMDSL, + + [HumanReadable(LongName = "UMD DL")] + UMDDL, + } + + /// + /// Dump status + /// + public enum DumpStatus + { + BadDumpRed = 2, + PossibleBadDumpYellow = 3, + OriginalMediaBlue = 4, + TwoOrMoreGreen = 5, + } + + /// + /// Determines what download type to initate + /// + public enum Feature + { + NONE, + Site, + WIP, + Packs, + User, + Quicksearch, + } + + /// + /// List of all disc langauges + /// + /// https://www.loc.gov/standards/iso639-2/php/code_list.php + public enum Language + { + #region A + + [Language(LongName = "Abkhazian", TwoLetterCode = "ab", ThreeLetterCode = "abk")] + Abkhazian, + + [Language(LongName = "Achinese", ThreeLetterCode = "ace")] + Achinese, + + [Language(LongName = "Acoli", ThreeLetterCode = "ach")] + Acoli, + + [Language(LongName = "Adangme", ThreeLetterCode = "ada")] + Adangme, + + // Adyghe; Adygei + [Language(LongName = "Adyghe", ThreeLetterCode = "ady")] + Adyghe, + + [Language(LongName = "Afar", TwoLetterCode = "aa", ThreeLetterCode = "aar")] + Afar, + + [Language(LongName = "Afrihili", ThreeLetterCode = "afh")] + Afrihili, + + [Language(LongName = "Afrikaans", TwoLetterCode = "af", ThreeLetterCode = "afr")] + Afrikaans, + + [Language(LongName = "Ainu", ThreeLetterCode = "ain")] + Ainu, + + [Language(LongName = "Akan", TwoLetterCode = "ak", ThreeLetterCode = "aka")] + Akan, + + [Language(LongName = "Akkadian", ThreeLetterCode = "akk")] + Akkadian, + + [Language(LongName = "Albanian", TwoLetterCode = "sq", ThreeLetterCode = "alb", ThreeLetterCodeAlt = "sqi")] + Albanian, + + [Language(LongName = "Aleut", ThreeLetterCode = "ale")] + Aleut, + + [Language(LongName = "Amharic", TwoLetterCode = "am", ThreeLetterCode = "amh")] + Amharic, + + [Language(LongName = "Angika", ThreeLetterCode = "anp")] + Angika, + + [Language(LongName = "Arabic", TwoLetterCode = "ar", ThreeLetterCode = "ara")] + Arabic, + + [Language(LongName = "Aragonese", TwoLetterCode = "an", ThreeLetterCode = "arg")] + Aragonese, + + // Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE) + [Language(LongName = "Aramaic", ThreeLetterCode = "arc")] + Aramaic, + + [Language(LongName = "Armenian", TwoLetterCode = "hy", ThreeLetterCode = "arm", ThreeLetterCodeAlt = "hye")] + Armenian, + + [Language(LongName = "Arapaho", ThreeLetterCode = "arp")] + Arapaho, + + // Aromanian; Arumanian; Macedo-Romanian + [Language(LongName = "Aromanian", ThreeLetterCode = "rup")] + Aromanian, + + [Language(LongName = "Arawak", ThreeLetterCode = "arw")] + Arawak, + + [Language(LongName = "Assamese", TwoLetterCode = "as", ThreeLetterCode = "asm")] + Assamese, + + // Asturian; Bable; Leonese; Asturleonese + [Language(LongName = "Asturian", ThreeLetterCode = "ast")] + Asturian, + + [Language(LongName = "Athapascan", ThreeLetterCode = "den")] + Athapascan, + + [Language(LongName = "Avaric", TwoLetterCode = "av", ThreeLetterCode = "ava")] + Avaric, + + [Language(LongName = "Avestan", TwoLetterCode = "ae", ThreeLetterCode = "ave")] + Avestan, + + [Language(LongName = "Awadhi", ThreeLetterCode = "awa")] + Awadhi, + + [Language(LongName = "Aymara", TwoLetterCode = "ay", ThreeLetterCode = "aym")] + Aymara, + + [Language(LongName = "Azerbaijani", TwoLetterCode = "az", ThreeLetterCode = "aze")] + Azerbaijani, + + #endregion + + #region B + + [Language(LongName = "Balinese", ThreeLetterCode = "ban")] + Balinese, + + [Language(LongName = "Baluchi", ThreeLetterCode = "bal")] + Baluchi, + + [Language(LongName = "Bambara", TwoLetterCode = "bm", ThreeLetterCode = "bam")] + Bambara, + + [Language(LongName = "Basa", ThreeLetterCode = "bas")] + Basa, + + [Language(LongName = "Bashkir", TwoLetterCode = "ba", ThreeLetterCode = "bak")] + Bashkir, + + [Language(LongName = "Basque", TwoLetterCode = "eu", ThreeLetterCode = "baq", ThreeLetterCodeAlt = "eus")] + Basque, + + // Beja; Bedawiyet + [Language(LongName = "Beja", ThreeLetterCode = "bej")] + Beja, + + [Language(LongName = "Belarusian", TwoLetterCode = "be", ThreeLetterCode = "bel")] + Belarusian, + + [Language(LongName = "Bemba", ThreeLetterCode = "bem")] + Bemba, + + [Language(LongName = "Bengali", TwoLetterCode = "bn", ThreeLetterCode = "ben")] + Bengali, + + [Language(LongName = "Bhojpuri", ThreeLetterCode = "bho")] + Bhojpuri, + + [Language(LongName = "Bikol", ThreeLetterCode = "bik")] + Bikol, + + [Language(LongName = "Bini; Edo", ThreeLetterCode = "bin")] + Bini, + + [Language(LongName = "Bislama", TwoLetterCode = "bla", ThreeLetterCode = "bis")] + Bislama, + + // Blin; Bilin + [Language(LongName = "Blin", ThreeLetterCode = "byn")] + Blin, + + // Blissymbols; Blissymbolics; Bliss + [Language(LongName = "Blissymbols", ThreeLetterCode = "zbl")] + Blissymbols, + + [Language(LongName = "Bosnian", TwoLetterCode = "bs", ThreeLetterCode = "bos")] + Bosnian, + + [Language(LongName = "Braj", ThreeLetterCode = "bra")] + Braj, + + [Language(LongName = "Breton", TwoLetterCode = "br", ThreeLetterCode = "bre")] + Breton, + + [Language(LongName = "Buginese", ThreeLetterCode = "bug")] + Buginese, + + [Language(LongName = "Bulgarian", TwoLetterCode = "bg", ThreeLetterCode = "bul")] + Bulgarian, + + [Language(LongName = "Buriat", ThreeLetterCode = "bua")] + Buriat, + + [Language(LongName = "Burmese", TwoLetterCode = "my", ThreeLetterCode = "bur", ThreeLetterCodeAlt = "mya")] + Burmese, + + #endregion + + #region C + + [Language(LongName = "Caddo", ThreeLetterCode = "cad")] + Caddo, + + // Catalan; Valencian + [Language(LongName = "Catalan", TwoLetterCode = "ca", ThreeLetterCode = "cat")] + Catalan, + + [Language(LongName = "Cebuano", ThreeLetterCode = "ceb")] + Cebuano, + + [Language(LongName = "Central Khmer", TwoLetterCode = "km", ThreeLetterCode = "khm")] + CentralKhmer, + + [Language(LongName = "Chagatai", ThreeLetterCode = "chg")] + Chagatai, + + [Language(LongName = "Chamorro", TwoLetterCode = "ch", ThreeLetterCode = "cha")] + Chamorro, + + [Language(LongName = "Chechen", TwoLetterCode = "ce", ThreeLetterCode = "che")] + Chechen, + + [Language(LongName = "Cherokee", ThreeLetterCode = "chr")] + Cherokee, + + [Language(LongName = "Cheyenne", ThreeLetterCode = "chy")] + Cheyenne, + + [Language(LongName = "Chibcha", ThreeLetterCode = "chb")] + Chibcha, + + // Chichewa; Chewa; Nyanja + [Language(LongName = "Chichewa", TwoLetterCode = "ny", ThreeLetterCode = "nya")] + Chichewa, + + [Language(LongName = "Chinese", TwoLetterCode = "zh", ThreeLetterCode = "chi", ThreeLetterCodeAlt = "zho")] + Chinese, + + [Language(LongName = "Chinook jargon", ThreeLetterCode = "chn")] + ChinookJargon, + + // Chipewyan; Dene Suline + [Language(LongName = "Chipewyan", ThreeLetterCode = "chp")] + Chipewyan, + + [Language(LongName = "Choctaw", ThreeLetterCode = "cho")] + Choctaw, + + // Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic + [Language(LongName = "Church Slavic", TwoLetterCode = "cu", ThreeLetterCode = "chu")] + ChurchSlavic, + + [Language(LongName = "Chuukese", ThreeLetterCode = "chk")] + Chuukese, + + [Language(LongName = "Chuvash", TwoLetterCode = "cv", ThreeLetterCode = "chv")] + Chuvash, + + // Classical Newari; Old Newari; Classical Nepal Bhasa + [Language(LongName = "Classical Newari", ThreeLetterCode = "nwc")] + ClassicalNewari, + + [Language(LongName = "Coptic", ThreeLetterCode = "cop")] + Coptic, + + [Language(LongName = "Cornish", TwoLetterCode = "kw", ThreeLetterCode = "cor")] + Cornish, + + [Language(LongName = "Corsican", TwoLetterCode = "co", ThreeLetterCode = "cos")] + Corsican, + + [Language(LongName = "Cree", TwoLetterCode = "cr", ThreeLetterCode = "cre")] + Cree, + + [Language(LongName = "Creek", ThreeLetterCode = "mus")] + Creek, + + [Language(LongName = "Creoles and pidgins", ThreeLetterCode = "crp")] + CreolesAndPidgins, + + [Language(LongName = "Creoles and pidgins (English-based)", ThreeLetterCode = "cpe")] + EnglishCreole, + + [Language(LongName = "Creoles and pidgins (French-based)", ThreeLetterCode = "cpf")] + FrenchCreole, + + [Language(LongName = "Creoles and pidgins (Portuguese-based)", ThreeLetterCode = "cpp")] + PortugueseCreole, + + // Crimean Tatar; Crimean Turkish + [Language(LongName = "Crimean Tatar", ThreeLetterCode = "crh")] + CrimeanTatar, + + [Language(LongName = "Croatian", TwoLetterCode = "hr", ThreeLetterCode = "hrv")] + Croatian, + + [Language(LongName = "Czech", TwoLetterCode = "cs", ThreeLetterCode = "cze", ThreeLetterCodeAlt = "ces")] + Czech, + + #endregion + + #region D + + [Language(LongName = "Dakota", ThreeLetterCode = "dak")] + Dakota, + + [Language(LongName = "Danish", TwoLetterCode = "da", ThreeLetterCode = "dan")] + Danish, + + [Language(LongName = "Dargwa", ThreeLetterCode = "dar")] + Dargwa, + + [Language(LongName = "Delaware", ThreeLetterCode = "del")] + Delaware, + + [Language(LongName = "Dinka", ThreeLetterCode = "din")] + Dinka, + + // Divehi; Dhivehi; Maldivian + [Language(LongName = "Divehi", TwoLetterCode = "dv", ThreeLetterCode = "div")] + Divehi, + + [Language(LongName = "Dogrib", ThreeLetterCode = "dgr")] + Dogrib, + + [Language(LongName = "Dogri", ThreeLetterCode = "doi")] + Dogri, + + [Language(LongName = "Duala", ThreeLetterCode = "dua")] + Duala, + + // Dutch; Flemish + [Language(LongName = "Dutch", TwoLetterCode = "nl", ThreeLetterCode = "dut", ThreeLetterCodeAlt = "nld")] + Dutch, + + [Language(LongName = "Dutch, Middle (ca.1050-1350)", ThreeLetterCode = "dum")] + MiddleDutch, + + [Language(LongName = "Dyula", ThreeLetterCode = "dyu")] + Dyula, + + [Language(LongName = "Dzongkha", TwoLetterCode = "dz", ThreeLetterCode = "dzo")] + Dzongkha, + + #endregion + + #region E + + [Language(LongName = "Eastern Frisian", ThreeLetterCode = "frs")] + EasternFrisian, + + [Language(LongName = "Efik", ThreeLetterCode = "efi")] + Efik, + + [Language(LongName = "Egyptian (Ancient)", ThreeLetterCode = "egy")] + AncientEgyptian, + + [Language(LongName = "Ekajuk", ThreeLetterCode = "eka")] + Ekajuk, + + [Language(LongName = "Elamite", ThreeLetterCode = "elx")] + Elamite, + + [Language(LongName = "English", TwoLetterCode = "en", ThreeLetterCode = "eng")] + English, + + [Language(LongName = "English, Old (ca.450-1100)", ThreeLetterCode = "ang")] + OldEnglish, + + [Language(LongName = "English, Middle (1100-1500)", ThreeLetterCode = "enm")] + MiddleEnglish, + + [Language(LongName = "Erzya", ThreeLetterCode = "myv")] + Erzya, + + [Language(LongName = "Esperanto", TwoLetterCode = "eo", ThreeLetterCode = "epo")] + Esperanto, + + [Language(LongName = "Estonian", TwoLetterCode = "et", ThreeLetterCode = "est")] + Estonian, + + [Language(LongName = "Ewe", TwoLetterCode = "ee", ThreeLetterCode = "ewe")] + Ewe, + + [Language(LongName = "Ewondo", ThreeLetterCode = "ewo")] + Ewondo, + + #endregion + + #region F + + [Language(LongName = "Fang", ThreeLetterCode = "fan")] + Fang, + + [Language(LongName = "Faroese", TwoLetterCode = "fo", ThreeLetterCode = "fao")] + Faroese, + + [Language(LongName = "Fanti", ThreeLetterCode = "fat")] + Fanti, + + [Language(LongName = "Fijian", TwoLetterCode = "fj", ThreeLetterCode = "fij")] + Fijian, + + // Filipino; Pilipino + [Language(LongName = "Filipino", ThreeLetterCode = "fil")] + Filipino, + + [Language(LongName = "Finnish", TwoLetterCode = "fi", ThreeLetterCode = "fin")] + Finnish, + + [Language(LongName = "Fon", ThreeLetterCode = "fon")] + Fon, + + [Language(LongName = "French", TwoLetterCode = "fr", ThreeLetterCode = "fre", ThreeLetterCodeAlt = "fra")] + French, + + [Language(LongName = "French, Middle (ca.1400-1600)", ThreeLetterCode = "frm")] + MiddleFrench, + + [Language(LongName = "French, Old (842-ca.1400)", ThreeLetterCode = "fro")] + OldFrench, + + [Language(LongName = "Friulian", ThreeLetterCode = "fur")] + Friulian, + + [Language(LongName = "Fulah", TwoLetterCode = "ff", ThreeLetterCode = "ful")] + Fulah, + + #endregion + + #region G + + [Language(LongName = "Ga", ThreeLetterCode = "gaa")] + Ga, + + // Gaelic; Scottish Gaelic + [Language(LongName = "Gaelic", TwoLetterCode = "gd", ThreeLetterCode = "gla")] + Gaelic, + + [Language(LongName = "Galibi Carib", ThreeLetterCode = "car")] + GalibiCarib, + + [Language(LongName = "Galician", TwoLetterCode = "gl", ThreeLetterCode = "glg")] + Galician, + + [Language(LongName = "Ganda", TwoLetterCode = "lg", ThreeLetterCode = "lug")] + Ganda, + + [Language(LongName = "Gayo", ThreeLetterCode = "gay")] + Gayo, + + [Language(LongName = "Gbaya", ThreeLetterCode = "gba")] + Gbaya, + + [Language(LongName = "Geez", ThreeLetterCode = "gez")] + Geez, + + [Language(LongName = "Georgian", TwoLetterCode = "ka", ThreeLetterCode = "geo", ThreeLetterCodeAlt = "kat")] + Georgian, + + [Language(LongName = "German", TwoLetterCode = "de", ThreeLetterCode = "ger", ThreeLetterCodeAlt = "deu")] + German, + + [Language(LongName = "German, Middle High (ca.1050-1500)", ThreeLetterCode = "gmh")] + MiddleHighGerman, + + [Language(LongName = "German, Old High (ca.750-1050)", ThreeLetterCode = "goh")] + OldHighGerman, + + [Language(LongName = "Gilbertese", ThreeLetterCode = "gil")] + Gilbertese, + + [Language(LongName = "Gondi", ThreeLetterCode = "gon")] + Gondi, + + [Language(LongName = "Gorontalo", ThreeLetterCode = "gor")] + Gorontalo, + + [Language(LongName = "Gothic", ThreeLetterCode = "got")] + Gothic, + + [Language(LongName = "Grebo", ThreeLetterCode = "grb")] + Grebo, + + [Language(LongName = "Greek", TwoLetterCode = "el", ThreeLetterCode = "gre", ThreeLetterCodeAlt = "eli")] + Greek, + + [Language(LongName = "Greek, Ancient (to 1453)", ThreeLetterCode = "grc")] + AncientGreek, + + [Language(LongName = "Guarani", TwoLetterCode = "gn", ThreeLetterCode = "grn")] + Guarani, + + [Language(LongName = "Gujarati", TwoLetterCode = "gu", ThreeLetterCode = "guj")] + Gujarati, + + [Language(LongName = "Gwich'in", ThreeLetterCode = "gwi")] + Gwichin, + + #endregion + + #region H + + [Language(LongName = "Haida", ThreeLetterCode = "hai")] + Haida, + + // Haitian; Haitian Creole + [Language(LongName = "Haitian", TwoLetterCode = "ht", ThreeLetterCode = "hat")] + Haitian, + + [Language(LongName = "Hausa", TwoLetterCode = "ha", ThreeLetterCode = "gau")] + Hausa, + + [Language(LongName = "Hawaiian", ThreeLetterCode = "haw")] + Hawaiian, + + [Language(LongName = "Hebrew", TwoLetterCode = "he", ThreeLetterCode = "heb")] + Hebrew, + + [Language(LongName = "Herero", TwoLetterCode = "hz", ThreeLetterCode = "her")] + Herero, + + [Language(LongName = "Hiligaynon", ThreeLetterCode = "hil")] + Hiligaynon, + + [Language(LongName = "Hindi", TwoLetterCode = "hi", ThreeLetterCode = "hin")] + Hindi, + + [Language(LongName = "Hiri Motu", ThreeLetterCode = "hmo")] + HiriMotu, + + [Language(LongName = "Hittite", ThreeLetterCode = "hit")] + Hittite, + + // Hmong; Mong + [Language(LongName = "Hmong", ThreeLetterCode = "hmn")] + Hmong, + + [Language(LongName = "Hungarian", TwoLetterCode = "hu", ThreeLetterCode = "hun")] + Hungarian, + + [Language(LongName = "Hupa", ThreeLetterCode = "hup")] + Hupa, + + #endregion + + #region I + + [Language(LongName = "Iban", ThreeLetterCode = "iba")] + Iban, + + [Language(LongName = "Icelandic", TwoLetterCode = "is", ThreeLetterCode = "ice", ThreeLetterCodeAlt = "isl")] + Icelandic, + + [Language(LongName = "Ido", TwoLetterCode = "io", ThreeLetterCode = "ido")] + Ido, + + [Language(LongName = "Igbo", TwoLetterCode = "ig", ThreeLetterCode = "ibo")] + Igbo, + + [Language(LongName = "Iloko", ThreeLetterCode = "ilo")] + Iloko, + + [Language(LongName = "Inari Sami", ThreeLetterCode = "smn")] + InariSami, + + [Language(LongName = "Indonesian", TwoLetterCode = "id", ThreeLetterCode = "ind")] + Indonesian, + + [Language(LongName = "Ingush", ThreeLetterCode = "inh")] + Ingush, + + // Interlingua (International Auxiliary Language Association) + [Language(LongName = "Interlingua", TwoLetterCode = "ia", ThreeLetterCode = "ina")] + Interlingua, + + // Interlingue; Occidental + [Language(LongName = "Interlingue", TwoLetterCode = "ie", ThreeLetterCode = "ile")] + Interlingue, + + [Language(LongName = "Inuktitut", TwoLetterCode = "iu", ThreeLetterCode = "iku")] + Inuktitut, + + [Language(LongName = "Inupiaq", TwoLetterCode = "ik", ThreeLetterCode = "ipk")] + Inupiaq, + + [Language(LongName = "Irish", TwoLetterCode = "ga", ThreeLetterCode = "gle")] + Irish, + + [Language(LongName = "Irish, Middle (900-1200)", ThreeLetterCode = "mga")] + MiddleIrish, + + [Language(LongName = "Irish, Old (to 900)", ThreeLetterCode = "sga")] + OldIrish, + + [Language(LongName = "Italian", TwoLetterCode = "it", ThreeLetterCode = "ita")] + Italian, + + #endregion + + #region J + + [Language(LongName = "Japanese", TwoLetterCode = "ja", ThreeLetterCode = "jap")] + Japanese, + + [Language(LongName = "Javanese", TwoLetterCode = "jv", ThreeLetterCode = "jav")] + Javanese, + + [Language(LongName = "Judeo-Arabic", ThreeLetterCode = "jrb")] + JudeoArabic, + + [Language(LongName = "Judeo-Persian", ThreeLetterCode = "jpr")] + JudeoPersian, + + #endregion + + #region K + + [Language(LongName = "Kabardian", ThreeLetterCode = "kbd")] + Kabardian, + + [Language(LongName = "Kabyle", ThreeLetterCode = "kab")] + Kabyle, + + // Kachin; Jingpho + [Language(LongName = "Kachin", ThreeLetterCode = "kac")] + Kachin, + + // Kalaallisut; Greenlandic + [Language(LongName = "Kalaallisut", TwoLetterCode = "kl", ThreeLetterCode = "kal")] + Kalaallisut, + + // Kalmyk; Oirat + [Language(LongName = "Kalmyk", ThreeLetterCode = "xal")] + Kalmyk, + + [Language(LongName = "Kamba", ThreeLetterCode = "kam")] + Kamba, + + [Language(LongName = "Kannada", TwoLetterCode = "kn", ThreeLetterCode = "kan")] + Kannada, + + [Language(LongName = "Kanuri", TwoLetterCode = "kr", ThreeLetterCode = "kau")] + Kanuri, + + [Language(LongName = "Karachay-Balkar", ThreeLetterCode = "krc")] + KarachayBalkar, + + [Language(LongName = "Kara-Kalpak", ThreeLetterCode = "kaa")] + KaraKalpak, + + [Language(LongName = "Karelian", ThreeLetterCode = "krl")] + Karelian, + + [Language(LongName = "Kashmiri", TwoLetterCode = "ks", ThreeLetterCode = "kas")] + Kashmiri, + + [Language(LongName = "Kashubian", ThreeLetterCode = "csb")] + Kashubian, + + [Language(LongName = "Kawi", ThreeLetterCode = "kaw")] + Kawi, + + [Language(LongName = "Kazakh", TwoLetterCode = "kk", ThreeLetterCode = "kaz")] + Kazakh, + + [Language(LongName = "Khasi", ThreeLetterCode = "kha")] + Khasi, + + // Khotanese; Sakan + [Language(LongName = "Khotanese", ThreeLetterCode = "kho")] + Khotanese, + + // Kikuyu; Gikuyu + [Language(LongName = "Kikuyu", TwoLetterCode = "ki", ThreeLetterCode = "kik")] + Kikuyu, + + [Language(LongName = "Kimbundu", ThreeLetterCode = "kmb")] + Kimbundu, + + [Language(LongName = "Kinyarwanda", TwoLetterCode = "rw", ThreeLetterCode = "kin")] + Kinyarwanda, + + // Kirghiz; Kyrgyz + [Language(LongName = "Kirghiz", TwoLetterCode = "ky", ThreeLetterCode = "kir")] + Kirghiz, + + // Klingon; tlhIngan-Hol + [Language(LongName = "Klingon", ThreeLetterCode = "tlh")] + Klingon, + + [Language(LongName = "Komi", TwoLetterCode = "kv", ThreeLetterCode = "kom")] + Komi, + + [Language(LongName = "Kongo", TwoLetterCode = "kg", ThreeLetterCode = "kon")] + Kongo, + + [Language(LongName = "Konkani", ThreeLetterCode = "kok")] + Konkani, + + [Language(LongName = "Korean", TwoLetterCode = "ko", ThreeLetterCode = "kor")] + Korean, + + [Language(LongName = "Kosraean", ThreeLetterCode = "kos")] + Kosraean, + + [Language(LongName = "Kpelle", ThreeLetterCode = "kpe")] + Kpelle, + + // Kuanyama; Kwanyama + [Language(LongName = "Kuanyama", TwoLetterCode = "kj", ThreeLetterCode = "kua")] + Kuanyama, + + [Language(LongName = "Kumyk", ThreeLetterCode = "kum")] + Kumyk, + + [Language(LongName = "Kurdish", TwoLetterCode = "ku", ThreeLetterCode = "kur")] + Kurdish, + + [Language(LongName = "Kurukh", ThreeLetterCode = "kru")] + Kurukh, + + [Language(LongName = "Kutenai", ThreeLetterCode = "kut")] + Kutenai, + + #endregion + + #region L + + [Language(LongName = "Ladino", ThreeLetterCode = "lad")] + Ladino, + + [Language(LongName = "Lahnda", ThreeLetterCode = "lah")] + Lahnda, + + [Language(LongName = "Lamba", ThreeLetterCode = "lam")] + Lamba, + + [Language(LongName = "Lao", TwoLetterCode = "lo", ThreeLetterCode = "lao")] + Lao, + + [Language(LongName = "Latin", TwoLetterCode = "la", ThreeLetterCode = "lat")] + Latin, + + [Language(LongName = "Latvian", TwoLetterCode = "lv", ThreeLetterCode = "lav")] + Latvian, + + [Language(LongName = "Lezghian", ThreeLetterCode = "lez")] + Lezghian, + + // Limburgan; Limburger; Limburgish + [Language(LongName = "Limburgan", TwoLetterCode = "li", ThreeLetterCode = "lim")] + Limburgan, + + [Language(LongName = "Lingala", TwoLetterCode = "ln", ThreeLetterCode = "lin")] + Lingala, + + [Language(LongName = "Lithuanian", TwoLetterCode = "lt", ThreeLetterCode = "lit")] + Lithuanian, + + [Language(LongName = "Lojban", ThreeLetterCode = "jbo")] + Lojban, + + // Low German; Low Saxon + [Language(LongName = "Low German", ThreeLetterCode = "nds")] + LowGerman, + + [Language(LongName = "Lower Sorbian", ThreeLetterCode = "dsb")] + LowerSorbian, + + [Language(LongName = "Lozi", ThreeLetterCode = "loz")] + Lozi, + + [Language(LongName = "Luba-Katanga", TwoLetterCode = "lu", ThreeLetterCode = "lub")] + LubaKatanga, + + [Language(LongName = "Luba-Lulua", ThreeLetterCode = "lua")] + LubaLulua, + + [Language(LongName = "Luiseno", ThreeLetterCode = "lui")] + Luiseno, + + [Language(LongName = "Lule Sami", ThreeLetterCode = "smj")] + LuleSami, + + [Language(LongName = "Lunda", ThreeLetterCode = "lun")] + Lunda, + + [Language(LongName = "Luo (Kenya and Tanzania)", ThreeLetterCode = "luo")] + Luo, + + [Language(LongName = "Lushai", ThreeLetterCode = "lus")] + Lushai, + + // Luxembourgish; Letzeburgesch + [Language(LongName = "Luxembourgish", TwoLetterCode = "lb", ThreeLetterCode = "ltz")] + Luxembourgish, + + #endregion + + #region M + + [Language(LongName = "Macedonian", TwoLetterCode = "mk", ThreeLetterCode = "mac", ThreeLetterCodeAlt = "mkd")] + Macedonian, + + [Language(LongName = "Madurese", ThreeLetterCode = "mad")] + Madurese, + + [Language(LongName = "Magahi", ThreeLetterCode = "mag")] + Magahi, + + [Language(LongName = "Maithili", ThreeLetterCode = "mai")] + Maithili, + + [Language(LongName = "Makasar", ThreeLetterCode = "mak")] + Makasar, + + [Language(LongName = "Malagasy", TwoLetterCode = "mg", ThreeLetterCode = "mlg")] + Malagasy, + + [Language(LongName = "Malay", TwoLetterCode = "ms", ThreeLetterCode = "may", ThreeLetterCodeAlt = "msa")] + Malay, + + [Language(LongName = "Malayalam", TwoLetterCode = "ml", ThreeLetterCode = "mal")] + Malayalam, + + [Language(LongName = "Maltese", TwoLetterCode = "mt", ThreeLetterCode = "mlt")] + Maltese, + + [Language(LongName = "Manchu", ThreeLetterCode = "mnc")] + Manchu, + + [Language(LongName = "Mandar", ThreeLetterCode = "mdr")] + Mandar, + + [Language(LongName = "Mandingo", ThreeLetterCode = "man")] + Mandingo, + + [Language(LongName = "Manipuri", ThreeLetterCode = "mni")] + Manipuri, + + [Language(LongName = "Manx", TwoLetterCode = "gv", ThreeLetterCode = "glv")] + Manx, + + [Language(LongName = "Maori", TwoLetterCode = "mi", ThreeLetterCode = "mao", ThreeLetterCodeAlt = "mri")] + Maori, + + // Mapudungun; Mapuche + [Language(LongName = "Mapudungun", ThreeLetterCode = "arn")] + Mapudungun, + + [Language(LongName = "Marathi", TwoLetterCode = "mr", ThreeLetterCode = "mar")] + Marathi, + + [Language(LongName = "Mari", ThreeLetterCode = "chm")] + Mari, + + [Language(LongName = "Marshallese", TwoLetterCode = "mh", ThreeLetterCode = "mah")] + Marshallese, + + [Language(LongName = "Marwari", ThreeLetterCode = "mwr")] + Marwari, + + [Language(LongName = "Masai", ThreeLetterCode = "mas")] + Masai, + + [Language(LongName = "Mende", ThreeLetterCode = "men")] + Mende, + + // Mi'kmaq; Micmac + [Language(LongName = "Mi'kmaq", ThreeLetterCode = "mic")] + Mikmaq, + + [Language(LongName = "Minangkabau", ThreeLetterCode = "min")] + Minangkabau, + + [Language(LongName = "Mirandese", ThreeLetterCode = "mwl")] + Mirandese, + + [Language(LongName = "Mohawk", ThreeLetterCode = "moh")] + Mohawk, + + [Language(LongName = "Moksha", ThreeLetterCode = "mdf")] + Moksha, + + [Language(LongName = "Mongo", ThreeLetterCode = "lol")] + Mongo, + + [Language(LongName = "Mongolian", TwoLetterCode = "mn", ThreeLetterCode = "mon")] + Mongolian, + + [Language(LongName = "Montenegrin", ThreeLetterCode = "cnr")] + Montenegrin, + + [Language(LongName = "Mossi", ThreeLetterCode = "mos")] + Mossi, + + #endregion + + #region N + + [Language(LongName = "N'Ko", ThreeLetterCode = "nqo")] + NKo, + + [Language(LongName = "Nauru", TwoLetterCode = "na", ThreeLetterCode = "nau")] + Nauru, + + // Navajo; Navaho + [Language(LongName = "Navajo", TwoLetterCode = "nv", ThreeLetterCode = "nav")] + Navajo, + + [Language(LongName = "Ndonga", TwoLetterCode = "ng", ThreeLetterCode = "ndo")] + Ndonga, + + [Language(LongName = "Neapolitan", ThreeLetterCode = "nap")] + Neapolitan, + + // Nepal Bhasa; Newari + [Language(LongName = "Nepal Bhasa", ThreeLetterCode = "new")] + NepalBhasa, + + [Language(LongName = "Nepali", TwoLetterCode = "ne", ThreeLetterCode = "nep")] + Nepali, + + [Language(LongName = "Nias", ThreeLetterCode = "nia")] + Nias, + + [Language(LongName = "Niuean", ThreeLetterCode = "niu")] + Niuean, + + // Commented out to avoid confusion + //[Language(LongName = "No linguistic content; Not applicable", ThreeLetterCode = "zxx")] + //NoLinguisticContent, + + [Language(LongName = "Nogai", ThreeLetterCode = "nog")] + Nogai, + + [Language(LongName = "Norse, Old", ThreeLetterCode = "non")] + OldNorse, + + [Language(LongName = "North Ndebele", TwoLetterCode = "nd", ThreeLetterCode = "nde")] + NorthNdebele, + + [Language(LongName = "Northern Frisian", ThreeLetterCode = "frr")] + NorthernFrisian, + + [Language(LongName = "Northern Sami", TwoLetterCode = "se", ThreeLetterCode = "sme")] + NorthernSami, + + [Language(LongName = "Norwegian", TwoLetterCode = "no", ThreeLetterCode = "nor")] + Norwegian, + + [Language(LongName = "Norwegian Bokmål", TwoLetterCode = "nb", ThreeLetterCode = "nob")] + NorwegianBokmal, + + [Language(LongName = "Norwegian Nynorsk", TwoLetterCode = "nn", ThreeLetterCode = "nno")] + NorwegianNynorsk, + + [Language(LongName = "Nyamwezi", ThreeLetterCode = "nym")] + Nyamwezi, + + [Language(LongName = "Nyankole", ThreeLetterCode = "nyn")] + Nyankole, + + [Language(LongName = "Nyoro", ThreeLetterCode = "nyo")] + Nyoro, + + [Language(LongName = "Nzima", ThreeLetterCode = "nzi")] + Nzima, + + #endregion + + #region O + + [Language(LongName = "Occitan (post 1500)", TwoLetterCode = "oc", ThreeLetterCode = "oci")] + Occitan, + + // Occitan, Old (to 1500); Provençal, Old (to 1500) + [Language(LongName = "Occitan, Old (to 1500)", ThreeLetterCode = "pro")] + OldOccitan, + + [Language(LongName = "Ojibwa", TwoLetterCode = "oj", ThreeLetterCode = "oji")] + Ojibwa, + + [Language(LongName = "Oriya", TwoLetterCode = "or", ThreeLetterCode = "ori")] + Oriya, + + [Language(LongName = "Oromo", TwoLetterCode = "om", ThreeLetterCode = "orm")] + Oromo, + + [Language(LongName = "Osage", ThreeLetterCode = "osa")] + Osage, + + // Ossetian; Ossetic + [Language(LongName = "Ossetian", TwoLetterCode = "os", ThreeLetterCode = "oss")] + Ossetian, + + #endregion + + #region P + + [Language(LongName = "Pahlavi", ThreeLetterCode = "pal")] + Pahlavi, + + [Language(LongName = "Palauan", ThreeLetterCode = "pau")] + Palauan, + + [Language(LongName = "Pali", TwoLetterCode = "pi", ThreeLetterCode = "pli")] + Pali, + + // Pampanga; Kapampangan + [Language(LongName = "Pampanga", ThreeLetterCode = "pam")] + Pampanga, + + [Language(LongName = "Pangasinan", ThreeLetterCode = "pag")] + Pangasinan, + + // Panjabi; Punjabi + [Language(LongName = "Panjabi", TwoLetterCode = "pa", ThreeLetterCode = "pan")] + Panjabi, + + [Language(LongName = "Papiamento", ThreeLetterCode = "pap")] + Papiamento, + + // Pedi; Sepedi; Northern Sotho + [Language(LongName = "Pedi", ThreeLetterCode = "nso")] + Pedi, + + [Language(LongName = "Persian", TwoLetterCode = "fa", ThreeLetterCode = "per", ThreeLetterCodeAlt = "fas")] + Persian, + + [Language(LongName = "Persian, Old (ca.600-400 B.C.)", ThreeLetterCode = "peo")] + OldPersian, + + [Language(LongName = "Phoenician", ThreeLetterCode = "phn")] + Phoenician, + + [Language(LongName = "Polish", TwoLetterCode = "pl", ThreeLetterCode = "pol")] + Polish, + + [Language(LongName = "Portuguese", TwoLetterCode = "pt", ThreeLetterCode = "por")] + Portuguese, + + // Pushto; Pashto + [Language(LongName = "Pushto", TwoLetterCode = "ps", ThreeLetterCode = "pus")] + Pushto, + + #endregion + + #region Q + + // qaa-qtz: Reserved for local use + + [Language(LongName = "Quechua", TwoLetterCode = "qu", ThreeLetterCode = "que")] + Quechua, + + #endregion + + #region R + + [Language(LongName = "Rajasthani", ThreeLetterCode = "raj")] + Rajasthani, + + [Language(LongName = "Rapanui", ThreeLetterCode = "rap")] + Rapanui, + + // Rarotongan; Cook Islands Maori + [Language(LongName = "Rarotongan", ThreeLetterCode = "rar")] + Rarotongan, + + // Romanian; Moldavian; Moldovan + [Language(LongName = "Romanian", TwoLetterCode = "ro", ThreeLetterCode = "rum", ThreeLetterCodeAlt = "ron")] + Romanian, + + [Language(LongName = "Romansh", TwoLetterCode = "rm", ThreeLetterCode = "roh")] + Romansh, + + [Language(LongName = "Romany", ThreeLetterCode = "rom")] + Romany, + + [Language(LongName = "Rundi", TwoLetterCode = "rn", ThreeLetterCode = "run")] + Rundi, + + [Language(LongName = "Russian", TwoLetterCode = "ru", ThreeLetterCode = "rus")] + Russian, + + #endregion + + #region S + + [Language(LongName = "Samaritan Aramaic", ThreeLetterCode = "sam")] + SamaritanAramaic, + + [Language(LongName = "Samoan", TwoLetterCode = "sm", ThreeLetterCode = "smo")] + Samoan, + + [Language(LongName = "Sandawe", ThreeLetterCode = "sad")] + Sandawe, + + [Language(LongName = "Sango", TwoLetterCode = "sg", ThreeLetterCode = "sag")] + Sango, + + [Language(LongName = "Sanskrit", TwoLetterCode = "sa", ThreeLetterCode = "san")] + Sanskrit, + + [Language(LongName = "Santali", ThreeLetterCode = "sat")] + Santali, + + [Language(LongName = "Sardinian", TwoLetterCode = "sc", ThreeLetterCode = "srd")] + Sardinian, + + [Language(LongName = "Sasak", ThreeLetterCode = "sas")] + Sasak, + + [Language(LongName = "Scots", ThreeLetterCode = "sco")] + Scots, + + [Language(LongName = "Selkup", ThreeLetterCode = "sel")] + Selkup, + + [Language(LongName = "Serbian", TwoLetterCode = "sr", ThreeLetterCode = "srp")] + Serbian, + + [Language(LongName = "Serer", ThreeLetterCode = "srr")] + Serer, + + [Language(LongName = "Shan", ThreeLetterCode = "shn")] + Shan, + + [Language(LongName = "Shona", TwoLetterCode = "sn", ThreeLetterCode = "sna")] + Shona, + + // Sichuan Yi; Nuosu + [Language(LongName = "Sichuan Yi", TwoLetterCode = "ii", ThreeLetterCode = "iii")] + SichuanYi, + + [Language(LongName = "Sicilian", ThreeLetterCode = "scn")] + Sicilian, + + [Language(LongName = "Sidamo", ThreeLetterCode = "sid")] + Sidamo, + + [Language(LongName = "Sign Languages", ThreeLetterCode = "sgn")] + SignLanguages, + + [Language(LongName = "Siksika", ThreeLetterCode = "bla")] + Siksika, + + [Language(LongName = "Sindhi", TwoLetterCode = "sd", ThreeLetterCode = "snd")] + Sindhi, + + // Sinhala; Sinhalese + [Language(LongName = "Sinhala", TwoLetterCode = "si", ThreeLetterCode = "sin")] + Sinhala, + + [Language(LongName = "Skolt Sami", ThreeLetterCode = "sms")] + SkoltSami, + + [Language(LongName = "Slovak", TwoLetterCode = "sk", ThreeLetterCode = "slo", ThreeLetterCodeAlt = "slk")] + Slovak, + + [Language(LongName = "Slovenian", TwoLetterCode = "sl", ThreeLetterCode = "slv")] + Slovenian, + + [Language(LongName = "Sogdian", ThreeLetterCode = "sog")] + Sogdian, + + [Language(LongName = "Somali", TwoLetterCode = "so", ThreeLetterCode = "som")] + Somali, + + [Language(LongName = "Soninke", ThreeLetterCode = "snk")] + Soninke, + + [Language(LongName = "Sotho, Southern", TwoLetterCode = "st", ThreeLetterCode = "sot")] + Sotho, + + [Language(LongName = "South Ndebele", TwoLetterCode = "nr", ThreeLetterCode = "nbl")] + SouthNdebele, + + [Language(LongName = "Southern Altai", ThreeLetterCode = "alt")] + SouthernAltai, + + [Language(LongName = "Southern Sami", ThreeLetterCode = "sma")] + SouthernSami, + + // Spanish; Castilian + [Language(LongName = "Spanish", TwoLetterCode = "es", ThreeLetterCode = "spa")] + Spanish, + + [Language(LongName = "Sranan Tongo", ThreeLetterCode = "srn")] + SrananTongo, + + [Language(LongName = "Standard Moroccan Tamazight", ThreeLetterCode = "zgh")] + StandardMoroccanTamazight, + + [Language(LongName = "Sukuma", ThreeLetterCode = "suk")] + Sukuma, + + [Language(LongName = "Sumerian", ThreeLetterCode = "sux")] + Sumerian, + + [Language(LongName = "Sundanese", TwoLetterCode = "su", ThreeLetterCode = "sun")] + Sundanese, + + [Language(LongName = "Susu", ThreeLetterCode = "sus")] + Susu, + + [Language(LongName = "Susu", TwoLetterCode = "sw", ThreeLetterCode = "swa")] + Swahili, + + [Language(LongName = "Swatio", TwoLetterCode = "ss", ThreeLetterCode = "ssw")] + Swati, + + [Language(LongName = "Swedish", TwoLetterCode = "sv", ThreeLetterCode = "swe")] + Swedish, + + // Swiss German; Alemannic; Alsatian + [Language(LongName = "Swiss German", ThreeLetterCode = "gsw")] + SwissGerman, + + [Language(LongName = "Syriac", ThreeLetterCode = "syr")] + Syriac, + + [Language(LongName = "Syriac, Classical", ThreeLetterCode = "syc")] + ClassicalSyriac, + + #endregion + + #region T + + [Language(LongName = "Tagalog", TwoLetterCode = "tl", ThreeLetterCode = "tgl")] + Tagalog, + + [Language(LongName = "Tahitian", TwoLetterCode = "ty", ThreeLetterCode = "tah")] + Tahitian, + + [Language(LongName = "Tajik", TwoLetterCode = "tg", ThreeLetterCode = "tgk")] + Tajik, + + [Language(LongName = "Tamashek", ThreeLetterCode = "tmh")] + Tamashek, + + [Language(LongName = "Tamil", TwoLetterCode = "ta", ThreeLetterCode = "tam")] + Tamil, + + [Language(LongName = "Tatar", TwoLetterCode = "tt", ThreeLetterCode = "tat")] + Tatar, + + [Language(LongName = "Telugu", TwoLetterCode = "te", ThreeLetterCode = "tel")] + Telugu, + + [Language(LongName = "Tereno", ThreeLetterCode = "ter")] + Tereno, + + [Language(LongName = "Tetum", ThreeLetterCode = "tet")] + Tetum, + + [Language(LongName = "Thai", TwoLetterCode = "th", ThreeLetterCode = "tha")] + Thai, + + [Language(LongName = "Tibetan", TwoLetterCode = "bo", ThreeLetterCode = "tib", ThreeLetterCodeAlt = "bod")] + Tibetan, + + [Language(LongName = "Tigre", ThreeLetterCode = "tig")] + Tigre, + + [Language(LongName = "Tigrinya", TwoLetterCode = "ti", ThreeLetterCode = "tir")] + Tigrinya, + + [Language(LongName = "Timne", ThreeLetterCode = "tem")] + Timne, + + [Language(LongName = "Tiv", ThreeLetterCode = "tiv")] + Tiv, + + [Language(LongName = "Tlingit", ThreeLetterCode = "tli")] + Tlingit, + + [Language(LongName = "Tok Pisin", ThreeLetterCode = "tpi")] + TokPisin, + + [Language(LongName = "Tokelau", ThreeLetterCode = "tkl")] + Tokelau, + + [Language(LongName = "Tonga (Nyasa)", ThreeLetterCode = "tog")] + TongaNyasa, + + [Language(LongName = "Tonga (Tonga Islands)", TwoLetterCode = "to", ThreeLetterCode = "ton")] + TongaIslands, + + [Language(LongName = "Tsimshian", ThreeLetterCode = "tsi")] + Tsimshian, + + [Language(LongName = "Tsonga", TwoLetterCode = "ts", ThreeLetterCode = "tso")] + Tsonga, + + [Language(LongName = "Tswana", TwoLetterCode = "tn", ThreeLetterCode = "tsn")] + Tswana, + + [Language(LongName = "Tumbuka", ThreeLetterCode = "tum")] + Tumbuka, + + [Language(LongName = "Turkish", TwoLetterCode = "tr", ThreeLetterCode = "tur")] + Turkish, + + [Language(LongName = "Turkish, Ottoman (1500-1928)", ThreeLetterCode = "ota")] + OttomanTurkish, + + [Language(LongName = "Turkmen", TwoLetterCode = "tk", ThreeLetterCode = "tuk")] + Turkmen, + + [Language(LongName = "Tuvalu", ThreeLetterCode = "tvl")] + Tuvalu, + + [Language(LongName = "Tuvinian", ThreeLetterCode = "tyv")] + Tuvinian, + + [Language(LongName = "Twi", TwoLetterCode = "tw", ThreeLetterCode = "twi")] + Twi, + + #endregion + + #region U + + [Language(LongName = "Udmurt", ThreeLetterCode = "udm")] + Udmurt, + + [Language(LongName = "Ugaritic", ThreeLetterCode = "uga")] + Ugaritic, + + // Uighur; Uyghur + [Language(LongName = "Uighur", TwoLetterCode = "ug", ThreeLetterCode = "uig")] + Uighur, + + [Language(LongName = "Ukrainian", TwoLetterCode = "uk", ThreeLetterCode = "ukr")] + Ukrainian, + + [Language(LongName = "Umbundu", ThreeLetterCode = "umb")] + Umbundu, + + // Commented out to avoid confusion + //[Language(LongName = "Undetermined", ThreeLetterCode = "und")] + //Undetermined, + + [Language(LongName = "Upper Sorbian", ThreeLetterCode = "hsb")] + UpperSorbian, + + [Language(LongName = "Urdu", TwoLetterCode = "ur", ThreeLetterCode = "urd")] + Urdu, + + [Language(LongName = "Uzbek", TwoLetterCode = "uz", ThreeLetterCode = "uzb")] + Uzbek, + + #endregion + + #region V + + [Language(LongName = "Vai", ThreeLetterCode = "vai")] + Vai, + + [Language(LongName = "Venda", TwoLetterCode = "ve", ThreeLetterCode = "ven")] + Venda, + + [Language(LongName = "Vietnamese", TwoLetterCode = "vi", ThreeLetterCode = "vie")] + Vietnamese, + + [Language(LongName = "Volapük", TwoLetterCode = "vo", ThreeLetterCode = "vol")] + Volapuk, + + [Language(LongName = "Votic", ThreeLetterCode = "vot")] + Votic, + + #endregion + + #region W + + [Language(LongName = "Walloon", TwoLetterCode = "wa", ThreeLetterCode = "wln")] + Walloon, + + [Language(LongName = "Waray", ThreeLetterCode = "war")] + Waray, + + [Language(LongName = "Washo", ThreeLetterCode = "was")] + Washo, + + [Language(LongName = "Welsh", TwoLetterCode = "cy", ThreeLetterCode = "wel", ThreeLetterCodeAlt = "cym")] + Welsh, + + [Language(LongName = "Western Frisian", TwoLetterCode = "fy", ThreeLetterCode = "fry")] + WesternFrisian, + + // Wolaitta; Wolaytta + [Language(LongName = "Wolaitta", ThreeLetterCode = "wal")] + Wolaitta, + + [Language(LongName = "Wolof", TwoLetterCode = "wo", ThreeLetterCode = "wol")] + Wolof, + + #endregion + + #region X + + [Language(LongName = "Xhosa", TwoLetterCode = "xh", ThreeLetterCode = "xho")] + Xhosa, + + #endregion + + #region Y + + [Language(LongName = "Yakut", ThreeLetterCode = "sah")] + Yakut, + + [Language(LongName = "Yao", ThreeLetterCode = "yao")] + Yao, + + [Language(LongName = "Yapese", ThreeLetterCode = "yap")] + Yapese, + + [Language(LongName = "Yiddish", TwoLetterCode = "yi", ThreeLetterCode = "yid")] + Yiddish, + + [Language(LongName = "Yoruba", TwoLetterCode = "yo", ThreeLetterCode = "yor")] + Yoruba, + + #endregion + + #region Z + + [Language(LongName = "Zapotec", ThreeLetterCode = "zap")] + Zapotec, + + // Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki + [Language(LongName = "Zaza", ThreeLetterCode = "zza")] + Zaza, + + [Language(LongName = "Zenaga", ThreeLetterCode = "zen")] + Zenaga, + + // Zhuang; Chuang + [Language(LongName = "Zhuang", TwoLetterCode = "za", ThreeLetterCode = "zha")] + Zhuang, + + [Language(LongName = "Zulu", TwoLetterCode = "zu", ThreeLetterCode = "zul")] + Zulu, + + [Language(LongName = "Zuni", ThreeLetterCode = "zun")] + Zuni, + + #endregion + + #region Language Families + + /* + [Language(LongName = "Afro-Asiatic languages", ThreeLetterCode = "afa")] + AfroAsiaticLanguages, + + [Language(LongName = "Algonquian languages", ThreeLetterCode = "alg")] + AlgonquianLanguages, + + [Language(LongName = "Altaic languages", ThreeLetterCode = "tut")] + AltaicLanguages, + + [Language(LongName = "Apache languages", ThreeLetterCode = "apa")] + ApacheLanguages, + + [Language(LongName = "Artificial languages", ThreeLetterCode = "art")] + ArtificialLanguages, + + [Language(LongName = "Athapascan languages", ThreeLetterCode = "ath")] + AthapascanLanguages, + + [Language(LongName = "Australian languages", ThreeLetterCode = "aus")] + AustralianLanguages, + + [Language(LongName = "Austronesian languages", ThreeLetterCode = "map")] + AustronesianLanguages, + + [Language(LongName = "Baltic languages", ThreeLetterCode = "bat")] + BalticLanguages, + + [Language(LongName = "Bamileke languages", ThreeLetterCode = "bai")] + BamilekeLanguages, + + [Language(LongName = "Banda languages", ThreeLetterCode = "bad")] + BandaLanguages, + + [Language(LongName = "Bantu languages", ThreeLetterCode = "bnt")] + BantuLanguages, + + [Language(LongName = "Batak languages", ThreeLetterCode = "btk")] + BatakLanguages, + + [Language(LongName = "Berber languages", ThreeLetterCode = "ber")] + BerberLanguages, + + [Language(LongName = "Bihari languages", TwoLetterCode = "bh", ThreeLetterCode = "bih")] + BihariLanguages, + + [Language(LongName = "Caucasian languages", ThreeLetterCode = "cau")] + CaucasianLanguages, + + [Language(LongName = "Celtic languages", ThreeLetterCode = "cel")] + CelticLanguages, + + [Language(LongName = "Central American Indian languages", ThreeLetterCode = "cai")] + CentralAmericanIndianLanguages, + + [Language(LongName = "Chamic languages", ThreeLetterCode = "cmc")] + ChamicLanguages, + + [Language(LongName = "Cushitic languages", ThreeLetterCode = "cus")] + CushiticLanguages, + + [Language(LongName = "Dravidian languages", ThreeLetterCode = "dra")] + DravidianLanguages, + + [Language(LongName = "Finno-Ugrian languages", ThreeLetterCode = "fiu")] + FinnoUgrianLanguages, + + [Language(LongName = "Germanic languages", ThreeLetterCode = "gem")] + GermanicLanguages, + + [Language(LongName = "Himachali languages; Western Pahari languages", ThreeLetterCode = "him")] + HimachaliLanguages, + + [Language(LongName = "Ijo languages", ThreeLetterCode = "ijo")] + IjoLanguages, + + [Language(LongName = "Indic languages", ThreeLetterCode = "inc")] + IndicLanguages, + + [Language(LongName = "Indo-European languages", ThreeLetterCode = "ine")] + IndoEuropeanLanguages, + + [Language(LongName = "Iranian languages", ThreeLetterCode = "ira")] + IranianLanguages, + + [Language(LongName = "Iroquoian languages", ThreeLetterCode = "iro")] + IroquoianLanguages, + + [Language(LongName = "Karen languages", ThreeLetterCode = "kar")] + KarenLanguages, + + [Language(LongName = "Khoisan languages", ThreeLetterCode = "khi")] + KhoisanLanguages, + + [Language(LongName = "Kru languages", ThreeLetterCode = "kro")] + KruLanguages, + + [Language(LongName = "Land Dayak languages", ThreeLetterCode = "day")] + LandDayakLanguages, + + [Language(LongName = "Manobo languages", ThreeLetterCode = "mno")] + ManoboLanguages, + + [Language(LongName = "Mayan languages", ThreeLetterCode = "myn")] + MayanLanguages, + + [Language(LongName = "Mon-Khmer languages", ThreeLetterCode = "mkh")] + MonKhmerLanguages, + + // Commented out to avoid confusion + //[Language(LongName = "Multiple languages", ThreeLetterCode = "mul")] + //MultipleLanguages, + + [Language(LongName = "Munda languages", ThreeLetterCode = "mun")] + MundaLanguages, + + [Language(LongName = "Nahuatl languages", ThreeLetterCode = "nah")] + NahuatlLanguages, + + [Language(LongName = "Niger-Kordofanian languages", ThreeLetterCode = "nic")] + NigerKordofanianLanguages, + + [Language(LongName = "Nilo-Saharan languages", ThreeLetterCode = "ssa")] + NiloSaharanLanguages, + + [Language(LongName = "North American Indian languages", ThreeLetterCode = "nai")] + NorthAmericanIndianLanguages, + + [Language(LongName = "Nubian languages", ThreeLetterCode = "nub")] + NubianLanguages, + + [Language(LongName = "Otomian languages", ThreeLetterCode = "oto")] + OtomianLanguages, + + [Language(LongName = "Papuan languages", ThreeLetterCode = "paa")] + PapuanLanguages, + + [Language(LongName = "Philippine languages", ThreeLetterCode = "phi")] + PhilippineLanguages, + + [Language(LongName = "Prakrit languages", ThreeLetterCode = "pra")] + PrakritLanguages, + + [Language(LongName = "Romance languages", ThreeLetterCode = "roa")] + RomanceLanguages, + + [Language(LongName = "Salishan languages", ThreeLetterCode = "sal")] + SalishanLanguages, + + [Language(LongName = "Sami languages", ThreeLetterCode = "smi")] + SamiLanguages, + + [Language(LongName = "Semitic languages", ThreeLetterCode = "sem")] + SemiticLanguages, + + [Language(LongName = "Sino-Tibetan languages", ThreeLetterCode = "sit")] + SinoTibetanLanguages, + + [Language(LongName = "Siouan languages", ThreeLetterCode = "sio")] + SiouanLanguages, + + [Language(LongName = "Slavic languages", ThreeLetterCode = "sla")] + SlavicLanguages, + + [Language(LongName = "Songhai languages", ThreeLetterCode = "son")] + SonghaiLanguages, + + [Language(LongName = "Sorbian languages", ThreeLetterCode = "wen")] + SorbianLanguages, + + [Language(LongName = "South American Indian languages", ThreeLetterCode = "sai")] + SouthAmericanIndianLanguages, + + [Language(LongName = "Tai languages", ThreeLetterCode = "tai")] + TaiLanguages, + + [Language(LongName = "Tupi languages", ThreeLetterCode = "tup")] + TupiLanguages, + + [Language(LongName = "Uncoded languages", ThreeLetterCode = "mis")] + UncodedLanguages, + + [Language(LongName = "Wakashan languages", ThreeLetterCode = "wak")] + WakashanLanguages, + + [Language(LongName = "Yupik languages", ThreeLetterCode = "ypk")] + YupikLanguages, + + [Language(LongName = "Zande languages", ThreeLetterCode = "znd")] + ZandeLanguages, + */ + + #endregion + } + + /// + /// All possible language selections + /// + public enum LanguageSelection + { + [HumanReadable(LongName = "Bios settings")] + BiosSettings, + + [HumanReadable(LongName = "Language selector")] + LanguageSelector, + + [HumanReadable(LongName = "Options menu")] + OptionsMenu, + } + + /// + /// All possible media types + /// + public enum MediaType + { + [HumanReadable(Available = false, LongName = "Unknown", ShortName = "unknown")] + NONE = 0, + + #region Punched Media + + [HumanReadable(Available = false, LongName = "Aperture card", ShortName = "aperture")] + ApertureCard, + + [HumanReadable(Available = false, LongName = "Jacquard Loom card", ShortName = "jacquard loom card")] + JacquardLoomCard, + + [HumanReadable(Available = false, LongName = "Magnetic stripe card", ShortName = "magnetic stripe")] + MagneticStripeCard, + + [HumanReadable(Available = false, LongName = "Optical phonecard", ShortName = "optical phonecard")] + OpticalPhonecard, + + [HumanReadable(Available = false, LongName = "Punched card", ShortName = "punchcard")] + PunchedCard, + + [HumanReadable(Available = false, LongName = "Punched tape", ShortName = "punchtape")] + PunchedTape, + + #endregion + + #region Tape + + [HumanReadable(Available = false, LongName = "Cassette Tape", ShortName = "cassette")] + Cassette, + + [HumanReadable(Available = false, LongName = "Data Tape Cartridge", ShortName = "data cartridge")] + DataCartridge, + + [HumanReadable(Available = false, LongName = "Open Reel Tape", ShortName = "open reel")] + OpenReel, + + #endregion + + #region Disc / Disc + + [HumanReadable(LongName = "BD-ROM", ShortName = "bdrom")] + BluRay, + + [HumanReadable(LongName = "CD-ROM", ShortName = "cdrom")] + CDROM, + + [HumanReadable(LongName = "DVD-ROM", ShortName = "dvd")] + DVD, + + [HumanReadable(LongName = "Floppy Disk", ShortName = "fd")] + FloppyDisk, + + [HumanReadable(Available = false, LongName = "Floptical", ShortName = "floptical")] + Floptical, + + [HumanReadable(LongName = "GD-ROM", ShortName = "gdrom")] + GDROM, + + [HumanReadable(LongName = "HD-DVD-ROM", ShortName = "hddvd")] + HDDVD, + + [HumanReadable(LongName = "Hard Disk", ShortName = "hdd")] + HardDisk, + + [HumanReadable(Available = false, LongName = "Iomega Bernoulli Disk", ShortName = "bernoulli")] + IomegaBernoulliDisk, + + [HumanReadable(Available = false, LongName = "Iomega Jaz", ShortName = "jaz")] + IomegaJaz, + + [HumanReadable(Available = false, LongName = "Iomega Zip", ShortName = "zip")] + IomegaZip, + + [HumanReadable(LongName = "LD-ROM / LV-ROM", ShortName = "ldrom")] + LaserDisc, // LD-ROM and LV-ROM variants + + [HumanReadable(Available = false, LongName = "64DD Disk", ShortName = "64dd")] + Nintendo64DD, + + [HumanReadable(Available = false, LongName = "Famicom Disk System Disk", ShortName = "fds")] + NintendoFamicomDiskSystem, + + [HumanReadable(LongName = "GameCube Game Disc", ShortName = "gc")] + NintendoGameCubeGameDisc, + + [HumanReadable(LongName = "Wii Optical Disc", ShortName = "wii")] + NintendoWiiOpticalDisc, + + [HumanReadable(LongName = "Wii U Optical Disc", ShortName = "wiiu")] + NintendoWiiUOpticalDisc, + + [HumanReadable(LongName = "UMD", ShortName = "umd")] + UMD, + + #endregion + + #region Unsorted Formats + + [HumanReadable(Available = false, LongName = "Cartridge", ShortName = "cart")] + Cartridge, + + [HumanReadable(Available = false, LongName = "CED", ShortName = "ced")] + CED, + + [HumanReadable(Available = false, LongName = "Compact Flash", ShortName = "cf")] + CompactFlash, + + [HumanReadable(Available = false, LongName = "MMC", ShortName = "mmc")] + MMC, + + [HumanReadable(Available = false, LongName = "SD Card", ShortName = "sd")] + SDCard, + + [HumanReadable(Available = false, LongName = "Flash Drive", ShortName = "fkd")] + FlashDrive, + + #endregion + } + + /// + /// List of all known systems + /// + /// TODO: Remove marker items + public enum RedumpSystem + { + #region BIOS Sets + + [System(LongName = "Microsoft Xbox (BIOS)", ShortName = "xbox-bios", HasDat = true)] + MicrosoftXboxBIOS, + + [System(LongName = "Nintendo GameCube (BIOS)", ShortName = "gc-bios", HasDat = true)] + NintendoGameCubeBIOS, + + [System(LongName = "Sony PlayStation (BIOS)", ShortName = "psx-bios", HasDat = true)] + SonyPlayStationBIOS, + + [System(LongName = "Sony PlayStation 2 (BIOS)", ShortName = "ps2-bios", HasDat = true)] + SonyPlayStation2BIOS, + + #endregion + + #region Disc-Based Consoles + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Atari Jaguar CD Interactive Multimedia System", ShortName = "ajcd", HasCues = true, HasDat = true)] + AtariJaguarCDInteractiveMultimediaSystem, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Bandai Playdia Quick Interactive System", ShortName = "qis", HasCues = true, HasDat = true)] + BandaiPlaydiaQuickInteractiveSystem, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Bandai Pippin", ShortName = "pippin", HasCues = true, HasDat = true)] + BandaiPippin, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Commodore Amiga CD32", ShortName = "cd32", HasCues = true, HasDat = true)] + CommodoreAmigaCD32, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Commodore Amiga CDTV", ShortName = "cdtv", HasCues = true, HasDat = true)] + CommodoreAmigaCDTV, + + [System(Category = SystemCategory.DiscBasedConsole, Available = false, LongName = "Envizions EVO Smart Console")] + EnvizionsEVOSmartConsole, + + [System(Category = SystemCategory.DiscBasedConsole, Available = false, LongName = "Fujitsu FM Towns Marty")] + FujitsuFMTownsMarty, + + [System(Category = SystemCategory.DiscBasedConsole, Available = false, LongName = "Hasbro iON Educational Gaming System")] + HasbroiONEducationalGamingSystem, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Hasbro VideoNow", ShortName = "hvn", IsBanned = true, HasCues = true, HasDat = true)] + HasbroVideoNow, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Hasbro VideoNow Color", ShortName = "hvnc", IsBanned = true, HasCues = true, HasDat = true)] + HasbroVideoNowColor, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Hasbro VideoNow Jr.", ShortName = "hvnjr", IsBanned = true, HasCues = true, HasDat = true)] + HasbroVideoNowJr, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Hasbro VideoNow XP", ShortName = "hvnxp", IsBanned = true, HasCues = true, HasDat = true)] + HasbroVideoNowXP, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Mattel Fisher-Price iXL", ShortName = "ixl", HasCues = true, HasDat = true)] + MattelFisherPriceiXL, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Mattel HyperScan", ShortName = "hs", HasCues = true, HasDat = true)] + MattelHyperScan, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Memorex Visual Information System", ShortName = "vis", HasCues = true, HasDat = true)] + MemorexVisualInformationSystem, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Microsoft Xbox", ShortName = "xbox", HasCues = true, HasDat = true)] + MicrosoftXbox, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Microsoft Xbox 360", ShortName = "xbox360", HasCues = true, HasDat = true)] + MicrosoftXbox360, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Microsoft Xbox One", ShortName = "xboxone", IsBanned = true, HasDat = true)] + MicrosoftXboxOne, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Microsoft Xbox Series X", ShortName = "xboxsx", IsBanned = true, HasDat = true)] + MicrosoftXboxSeriesXS, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "NEC PC Engine CD & TurboGrafx CD", ShortName = "pce", HasCues = true, HasDat = true)] + NECPCEngineCDTurboGrafxCD, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "NEC PC-FX & PC-FXGA", ShortName = "pc-fx", HasCues = true, HasDat = true)] + NECPCFXPCFXGA, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Nintendo GameCube", ShortName = "gc", HasDat = true)] + NintendoGameCube, + + [System(Category = SystemCategory.DiscBasedConsole, Available = false, LongName = "Nintendo-Sony Super NES CD-ROM System")] + NintendoSonySuperNESCDROMSystem, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Nintendo Wii", ShortName = "wii", HasDat = true)] + NintendoWii, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Nintendo Wii U", ShortName = "wiiu", IsBanned = true, HasDat = true, HasKeys = true)] + NintendoWiiU, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Panasonic 3DO Interactive Multiplayer", ShortName = "3do", HasCues = true, HasDat = true)] + Panasonic3DOInteractiveMultiplayer, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Philips CD-i", ShortName = "cdi", HasCues = true, HasDat = true)] + PhilipsCDi, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Philips CD-i Digital Video", ShortName = "cdi-video", IsBanned = true)] + PhilipsCDiDigitalVideo, + + [System(Category = SystemCategory.DiscBasedConsole, Available = false, LongName = "Pioneer LaserActive")] + PioneerLaserActive, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Sega Dreamcast", ShortName = "dc", HasCues = true, HasDat = true, HasGdi = true)] + SegaDreamcast, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Sega Mega CD & Sega CD", ShortName = "mcd", HasCues = true, HasDat = true)] + SegaMegaCDSegaCD, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Sega Saturn", ShortName = "ss", HasCues = true, HasDat = true)] + SegaSaturn, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Neo Geo CD", ShortName = "ngcd", HasCues = true, HasDat = true)] + SNKNeoGeoCD, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Sony PlayStation", ShortName = "psx", HasCues = true, HasDat = true, HasLsd = true, HasSbi = true)] + SonyPlayStation, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Sony PlayStation 2", ShortName = "ps2", HasCues = true, HasDat = true)] + SonyPlayStation2, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Sony PlayStation 3", ShortName = "ps3", HasCues = true, HasDat = true, HasDkeys = true, HasKeys = true)] + SonyPlayStation3, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Sony PlayStation 4", ShortName = "ps4", IsBanned = true, HasDat = true)] + SonyPlayStation4, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Sony PlayStation 5", ShortName = "ps5", IsBanned = true, HasDat = true)] + SonyPlayStation5, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "Sony PlayStation Portable", ShortName = "psp", HasDat = true)] + SonyPlayStationPortable, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "VM Labs NUON", ShortName = "nuon", HasDat = true)] + VMLabsNUON, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "VTech V.Flash & V.Smile Pro", ShortName = "vflash", HasCues = true, HasDat = true)] + VTechVFlashVSmilePro, + + [System(Category = SystemCategory.DiscBasedConsole, LongName = "ZAPiT Games Game Wave Family Entertainment System", ShortName = "gamewave", HasDat = true)] + ZAPiTGamesGameWaveFamilyEntertainmentSystem, + + // End of console section delimiter + MarkerDiscBasedConsoleEnd, + + #endregion + + #region Cartridge-Based and Other Consoles + + /* + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Amstrad GX-4000")] + AmstradGX4000, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "APF Microcomputer System")] + APFMicrocomputerSystem, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Atari 2600 & VCS")] + Atari2600VCS, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Atari 5200")] + Atari5200, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Atari 7800")] + Atari7800, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Atari Jaguar")] + AtariJaguar, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Atari XEGS")] + AtariXEGS, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Audiosonic 1292 Advanced Programmable Video System")] + Audiosonic1292AdvancedProgrammableVideoSystem, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Bally Astrocade")] + BallyAstrocade, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Bit Corporation Dina")] + BitCorporationDina, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Casio Loopy")] + CasioLoopy, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Casio PV-1000")] + CasioPV1000, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Commodore 64 Games System")] + Commodore64GamesSystem, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Daewoo Electronics Zemmix")] + DaewooElectronicsZemmix, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Emerson Arcadia 2001")] + EmersonArcadia2001, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Epoch Cassette Vision")] + EpochCassetteVision, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Epoch Super Cassette Vision")] + EpochSuperCassetteVision, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Fairchild Channel F")] + FairchildChannelF, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Funtech Super A'Can")] + FuntechSuperACan, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "GCE Vectrex")] + GCEVectrex, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Heber BBC Bridge Companion")] + HeberBBCBridgeCompanion, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Interton VC-4000")] + IntertonVC4000, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "JungleTac Vii")] + JungleTacVii, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "LeapFrog ClickStart")] + LeapFrogClickStart, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "LJN VideoArt")] + LJNVideoArt, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Magnavox Odyssey 2")] + MagnavoxOdyssey2, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Mattel Intellivision")] + MattelIntellivision, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "NEC PC Engine & TurboGrafx-16")] + NECPCEngineTurboGrafx16, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Nichibutsu MyVision")] + NichibutsuMyVision, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Nintendo 64")] + Nintendo64, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Nintendo 64DD")] + Nintendo64DD, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Nintendo Famicom & Nintendo Entertainment System")] + NintendoFamicomNintendoEntertainmentSystem, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Nintendo Famicom Disk System")] + NintendoFamicomDiskSystem, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Nintendo Super Famicom & Super Nintendo Entertainment System")] + NintendoSuperFamicomSuperNintendoEntertainmentSystem, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Nintendo Switch")] + NintendoSwitch, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Philips Videopac+ & G7400")] + PhilipsVideopacPlusG7400, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "RCA Studio-II")] + RCAStudioII, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Sega 32X")] + Sega32X, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Sega Mark III & Master System")] + SegaMarkIIIMasterSystem, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Sega MegaDrive & Genesis")] + SegaMegaDriveGenesis, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Sega SG-1000")] + SegaSG1000, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "SNK NeoGeo")] + SNKNeoGeo, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "SSD COMPANY LIMITED XaviXPORT")] + SSDCOMPANYLIMITEDXaviXPORT, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "ViewMaster Interactive Vision")] + ViewMasterInteractiveVision, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "V.Tech CreatiVision")] + VTechCreatiVision, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "V.Tech V.Smile")] + VTechVSmile, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "V.Tech Socrates")] + VTechSocrates, + + [System(Category = SystemCategory.OtherConsole, Available = false, LongName = "Worlds of Wonder ActionMax")] + WorldsOfWonderActionMax, + + // End of other console delimiter + MarkerOtherConsoleEnd, + */ + + #endregion + + #region Computers + + [System(Category = SystemCategory.Computer, LongName = "Acorn Archimedes", ShortName = "arch", HasCues = true, HasDat = true)] + AcornArchimedes, + + [System(Category = SystemCategory.Computer, LongName = "Apple Macintosh", ShortName = "mac", HasCues = true, HasDat = true, HasLsd = true, HasSbi = true)] + AppleMacintosh, + + [System(Category = SystemCategory.Computer, LongName = "Commodore Amiga CD", ShortName = "acd", HasCues = true, HasDat = true)] + CommodoreAmigaCD, + + [System(Category = SystemCategory.Computer, LongName = "Fujitsu FM Towns series", ShortName = "fmt", HasCues = true, HasDat = true)] + FujitsuFMTownsseries, + + [System(Category = SystemCategory.Computer, LongName = "IBM PC compatible", ShortName = "pc", HasCues = true, HasDat = true, HasLsd = true, HasSbi = true)] + IBMPCcompatible, + + [System(Category = SystemCategory.Computer, LongName = "NEC PC-88 series", ShortName = "pc-88", HasCues = true, HasDat = true)] + NECPC88series, + + [System(Category = SystemCategory.Computer, LongName = "NEC PC-98 series", ShortName = "pc-98", HasCues = true, HasDat = true)] + NECPC98series, + + [System(Category = SystemCategory.Computer, LongName = "Sharp X68000", ShortName = "x86kcd", HasCues = true, HasDat = true)] + SharpX68000, + + // End of computer section delimiter + MarkerComputerEnd, + + #endregion + + #region Arcade + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Amiga CUBO CD32")] + AmigaCUBOCD32, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "American Laser Games 3DO")] + AmericanLaserGames3DO, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Atari 3DO")] + Atari3DO, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Atronic")] + Atronic, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "AUSCOM System 1")] + AUSCOMSystem1, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Bally Game Magic")] + BallyGameMagic, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Capcom CP System III")] + CapcomCPSystemIII, + + [System(Category = SystemCategory.Arcade, LongName = "funworld Photo Play", ShortName = "fpp", HasCues = true, HasDat = true)] + funworldPhotoPlay, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Global VR PC-based Systems")] + GlobalVRVarious, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Global VR Vortek")] + GlobalVRVortek, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Global VR Vortek V3")] + GlobalVRVortekV3, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "ICE PC-based Hardware")] + ICEPCHardware, + + [System(Category = SystemCategory.Arcade, LongName = "Incredible Technologies Eagle", ShortName = "ite", HasCues = true, HasDat = true)] + IncredibleTechnologiesEagle, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Incredible Technologies PC-based Systems")] + IncredibleTechnologiesVarious, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "JVL iTouch")] + JVLiTouch, + + [System(Category = SystemCategory.Arcade, LongName = "Konami e-Amusement", ShortName = "kea", HasCues = true, HasDat = true)] + KonamieAmusement, + + [System(Category = SystemCategory.Arcade, LongName = "Konami FireBeat", ShortName = "kfb", HasCues = true, HasDat = true)] + KonamiFireBeat, + + [System(Category = SystemCategory.Arcade, LongName = "Konami M2", ShortName = "km2", IsBanned = true, HasCues = true, HasDat = true)] + KonamiM2, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Konami Python")] + KonamiPython, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Konami Python 2")] + KonamiPython2, + + [System(Category = SystemCategory.Arcade, LongName = "Konami System 573", ShortName = "ks573", HasCues = true, HasDat = true)] + KonamiSystem573, + + [System(Category = SystemCategory.Arcade, LongName = "Konami System GV", ShortName = "ksgv", HasCues = true, HasDat = true)] + KonamiSystemGV, + + [System(Category = SystemCategory.Arcade, LongName = "Konami Twinkle", ShortName = "kt")] + KonamiTwinkle, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Konami PC-based Systems")] + KonamiVarious, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Merit Industries Boardwalk")] + MeritIndustriesBoardwalk, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Merit Industries MegaTouch Force")] + MeritIndustriesMegaTouchForce, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Merit Industries MegaTouch ION")] + MeritIndustriesMegaTouchION, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Merit Industries MegaTouch Maxx")] + MeritIndustriesMegaTouchMaxx, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Merit Industries MegaTouch XL")] + MeritIndustriesMegaTouchXL, + + [System(Category = SystemCategory.Arcade, LongName = "Namco · Sega · Nintendo Triforce", ShortName = "triforce", HasCues = true, HasDat = true, HasGdi = true)] + NamcoSegaNintendoTriforce, + + [System(Category = SystemCategory.Arcade, LongName = "Namco System 12", ShortName = "ns12")] + NamcoSystem12, + + [System(Category = SystemCategory.Arcade, LongName = "Namco System 246 / System 256", ShortName = "ns246", HasCues = true, HasDat = true)] + NamcoSystem246256, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "New Jatre CD-i")] + NewJatreCDi, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Nichibutsu High Rate System")] + NichibutsuHighRateSystem, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Nichibutsu Super CD")] + NichibutsuSuperCD, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Nichibutsu X-Rate System")] + NichibutsuXRateSystem, + + [System(Category = SystemCategory.Arcade, LongName = "Panasonic M2", ShortName = "m2", IsBanned = true, HasCues = true, HasDat = true)] + PanasonicM2, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "PhotoPlay PC-based Systems")] + PhotoPlayVarious, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Raw Thrills PC-based Systems")] + RawThrillsVarious, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Sega ALLS")] + SegaALLS, + + [System(Category = SystemCategory.Arcade, LongName = "Sega Chihiro", ShortName = "chihiro", HasCues = true, HasDat = true, HasGdi = true)] + SegaChihiro, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Sega Europa-R")] + SegaEuropaR, + + [System(Category = SystemCategory.Arcade, LongName = "Sega Lindbergh", ShortName = "lindbergh", HasDat = true)] + SegaLindbergh, + + [System(Category = SystemCategory.Arcade, LongName = "Sega Naomi", ShortName = "naomi", HasCues = true, HasDat = true, HasGdi = true)] + SegaNaomi, + + [System(Category = SystemCategory.Arcade, LongName = "Sega Naomi 2", ShortName = "naomi2", HasCues = true, HasDat = true, HasGdi = true)] + SegaNaomi2, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Sega Nu")] + SegaNu, + + [System(Category = SystemCategory.Arcade, LongName = "Sega RingEdge", ShortName = "sre", HasDat = true)] + SegaRingEdge, + + [System(Category = SystemCategory.Arcade, LongName = "Sega RingEdge 2", ShortName = "sre2", HasDat = true)] + SegaRingEdge2, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Sega RingWide")] + SegaRingWide, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Sega System 32")] + SegaSystem32, + + [System(Category = SystemCategory.Arcade, LongName = "Sega Titan Video", ShortName = "stv")] + SegaTitanVideo, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Seibu CATS System")] + SeibuCATSSystem, + + [System(Category = SystemCategory.Arcade, LongName = "TAB-Austria Quizard", ShortName = "quizard", HasCues = true, HasDat = true)] + TABAustriaQuizard, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "Tsunami TsuMo Multi-Game Motion System")] + TsunamiTsuMoMultiGameMotionSystem, + + [System(Category = SystemCategory.Arcade, Available = false, LongName = "UltraCade PC-based Systems")] + UltraCade, + + // End of arcade section delimiter + MarkerArcadeEnd, + + #endregion + + #region Other + + [System(Category = SystemCategory.Other, LongName = "Audio CD", ShortName = "audio-cd", IsBanned = true, HasCues = true, HasDat = true)] + AudioCD, + + [System(Category = SystemCategory.Other, LongName = "BD-Video", ShortName = "bd-video", IsBanned = true, HasDat = true)] + BDVideo, + + [System(Category = SystemCategory.Other, Available = false, LongName = "DVD-Audio")] + DVDAudio, + + [System(Category = SystemCategory.Other, LongName = "DVD-Video", ShortName = "dvd-video", IsBanned = true, HasDat = true)] + DVDVideo, + + [System(Category = SystemCategory.Other, LongName = "Enhanced CD", ShortName = "enhanced-cd", IsBanned = true)] + EnhancedCD, + + [System(Category = SystemCategory.Other, LongName = "HD DVD-Video", ShortName = "hddvd-video", IsBanned = true, HasDat = true)] + HDDVDVideo, + + [System(Category = SystemCategory.Other, LongName = "Navisoft Naviken 2.1", ShortName = "navi21", IsBanned = true, HasCues = true, HasDat = true)] + NavisoftNaviken21, + + [System(Category = SystemCategory.Other, LongName = "Palm OS", ShortName = "palm", HasCues = true, HasDat = true)] + PalmOS, + + [System(Category = SystemCategory.Other, LongName = "Photo CD", ShortName = "photo-cd", HasCues = true, HasDat = true)] + PhotoCD, + + [System(Category = SystemCategory.Other, LongName = "PlayStation GameShark Updates", ShortName = "psxgs", HasCues = true, HasDat = true)] + PlayStationGameSharkUpdates, + + [System(Category = SystemCategory.Other, LongName = "Pocket PC", ShortName = "ppc", HasCues = true, HasDat = true)] + PocketPC, + + [System(Category = SystemCategory.Other, Available = false, LongName = "Rainbow Disc")] + RainbowDisc, + + [System(Category = SystemCategory.Other, LongName = "Sega Prologue 21 Multimedia Karaoke System", ShortName = "sp21", HasCues = true, HasDat = true)] + SegaPrologue21MultimediaKaraokeSystem, + + [System(Category = SystemCategory.Other, Available = false, LongName = "Sony Electronic Book")] + SonyElectronicBook, + + [System(Category = SystemCategory.Other, Available = false, LongName = "Super Audio CD")] + SuperAudioCD, + + [System(Category = SystemCategory.Other, LongName = "Tao iKTV", ShortName = "iktv")] + TaoiKTV, + + [System(Category = SystemCategory.Other, LongName = "Tomy Kiss-Site", ShortName = "ksite", HasCues = true, HasDat = true)] + TomyKissSite, + + [System(Category = SystemCategory.Other, LongName = "Video CD", ShortName = "vcd", IsBanned = true, HasCues = true, HasDat = true)] + VideoCD, + + // End of other section delimiter + MarkerOtherEnd, + + #endregion + } + + /// + /// List of all known regions + /// + /// + /// https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 + /// + /// Because of how Redump stores region IDs, the second letter of each + /// code is lower-cased. In any other system, both letters would be + /// capitalized properly. + /// + public enum Region + { + // TODO: Should "regions" and multi-country sets be phased out? + // TODO: Should "regions" be moved to the end? + + #region A + + [HumanReadable(LongName = "Afghanistan", ShortName = "Af")] + Afghanistan, + + [HumanReadable(LongName = "Åland Islands", ShortName = "Ax")] + AlandIslands, + + [HumanReadable(LongName = "Albania", ShortName = "Al")] + Albania, + + [HumanReadable(LongName = "Algeria", ShortName = "Dz")] + Algeria, + + [HumanReadable(LongName = "American Samoa", ShortName = "As")] + AmericanSamoa, + + [HumanReadable(LongName = "Andorra", ShortName = "Ad")] + Andorra, + + [HumanReadable(LongName = "Angola", ShortName = "Ao")] + Angola, + + [HumanReadable(LongName = "Anguilla", ShortName = "Ai")] + Anguilla, + + [HumanReadable(LongName = "Antarctica", ShortName = "Aq")] + Antarctica, + + [HumanReadable(LongName = "Antigua and Barbuda", ShortName = "Ag")] + AntiguaAndBarbuda, + + [HumanReadable(LongName = "Argentina", ShortName = "Ar")] + Argentina, + + [HumanReadable(LongName = "Armenia", ShortName = "Am")] + Armenia, + + [HumanReadable(LongName = "Aruba", ShortName = "Aw")] + Aruba, + + [HumanReadable(LongName = "Ascension Island", ShortName = "Ac")] + AscensionIsland, + + [HumanReadable(LongName = "Asia", ShortName = "A")] + Asia, + + [HumanReadable(LongName = "Asia, Europe", ShortName = "A,E")] + AsiaEurope, + + [HumanReadable(LongName = "Asia, USA", ShortName = "A,U")] + AsiaUSA, + + [HumanReadable(LongName = "Australia", ShortName = "Au")] + Australia, + + [HumanReadable(LongName = "Australia, Germany", ShortName = "Au,G")] + AustraliaGermany, + + [HumanReadable(LongName = "Australia, New Zealand", ShortName = "Au,Nz")] + AustraliaNewZealand, + + [HumanReadable(LongName = "Austria", ShortName = "At")] + Austria, + + [HumanReadable(LongName = "Austria, Switzerland", ShortName = "At,Ch")] + AustriaSwitzerland, + + [HumanReadable(LongName = "Azerbaijan", ShortName = "Az")] + Azerbaijan, + + #endregion + + #region B + + [HumanReadable(LongName = "Bahamas", ShortName = "Bs")] + Bahamas, + + [HumanReadable(LongName = "Bahrain", ShortName = "Bh")] + Bahrain, + + [HumanReadable(LongName = "Bangladesh", ShortName = "Bd")] + Bangladesh, + + [HumanReadable(LongName = "Barbados", ShortName = "Bb")] + Barbados, + + [HumanReadable(LongName = "Belarus", ShortName = "By")] + Belarus, + + [HumanReadable(LongName = "Belgium", ShortName = "Be")] + Belgium, + + [HumanReadable(LongName = "Belgium, Netherlands", ShortName = "Be,N")] + BelgiumNetherlands, + + [HumanReadable(LongName = "Belize", ShortName = "Bz")] + Belize, + + [HumanReadable(LongName = "Benin", ShortName = "Bj")] + Benin, + + [HumanReadable(LongName = "Bermuda", ShortName = "Bm")] + Bermuda, + + [HumanReadable(LongName = "Bhutan", ShortName = "Bt")] + Bhutan, + + [HumanReadable(LongName = "Bolivia", ShortName = "Bo")] + Bolivia, + + [HumanReadable(LongName = "Bonaire, Sint Eustatius and Saba", ShortName = "Bq")] + Bonaire, + + [HumanReadable(LongName = "Bosnia and Herzegovina", ShortName = "Ba")] + BosniaAndHerzegovina, + + [HumanReadable(LongName = "Botswana", ShortName = "Bw")] + Botswana, + + [HumanReadable(LongName = "Bouvet Island", ShortName = "Bv")] + BouvetIsland, + + // Should be "Br" + [HumanReadable(LongName = "Brazil", ShortName = "B")] + Brazil, + + [HumanReadable(LongName = "British Indian Ocean Territory", ShortName = "Io")] + BritishIndianOceanTerritory, + + [HumanReadable(LongName = "Brunei Darussalam", ShortName = "Bn")] + BruneiDarussalam, + + [HumanReadable(LongName = "Bulgaria", ShortName = "Bg")] + Bulgaria, + + [HumanReadable(LongName = "Burkina Faso", ShortName = "Bf")] + BurkinaFaso, + + [HumanReadable(LongName = "Burundi", ShortName = "Bi")] + Burundi, + + #endregion + + #region C + + [HumanReadable(LongName = "Cabo Verde", ShortName = "Cv")] + CaboVerde, + + [HumanReadable(LongName = "Cambodia", ShortName = "Kh")] + Cambodia, + + [HumanReadable(LongName = "Cameroon", ShortName = "Cm")] + Cameroon, + + [HumanReadable(LongName = "Canada", ShortName = "Ca")] + Canada, + + [HumanReadable(LongName = "Canary Islands", ShortName = "Ic")] + CanaryIslands, + + [HumanReadable(LongName = "Cayman Islands", ShortName = "Ky")] + CaymanIslands, + + [HumanReadable(LongName = "Central African Republic", ShortName = "Cf")] + CentralAfricanRepublic, + + [HumanReadable(LongName = "Ceuta, Melilla", ShortName = "Ea")] + CeutaMelilla, + + [HumanReadable(LongName = "Chad", ShortName = "Td")] + Chad, + + [HumanReadable(LongName = "Chile", ShortName = "Cl")] + Chile, + + // Should be "Cn" + [HumanReadable(LongName = "China", ShortName = "C")] + China, + + [HumanReadable(LongName = "Christmas Island", ShortName = "Cx")] + ChristmasIsland, + + [HumanReadable(LongName = "Clipperton Island", ShortName = "Cp")] + ClippertonIsland, + + [HumanReadable(LongName = "Cocos (Keeling) Islands", ShortName = "Cc")] + CocosIslands, + + [HumanReadable(LongName = "Colombia", ShortName = "Co")] + Colombia, + + [HumanReadable(LongName = "Comoros", ShortName = "Km")] + Comoros, + + [HumanReadable(LongName = "Congo", ShortName = "Cg")] + Congo, + + [HumanReadable(LongName = "Cook Islands", ShortName = "Ck")] + CookIslands, + + [HumanReadable(LongName = "Costa Rica", ShortName = "Cr")] + CostaRica, + + [HumanReadable(LongName = "Côte d'Ivoire", ShortName = "Ci")] + CoteDIvoire, + + [HumanReadable(LongName = "Croatia", ShortName = "Hr")] + Croatia, + + [HumanReadable(LongName = "Cuba", ShortName = "Cu")] + Cuba, + + [HumanReadable(LongName = "Curaçao", ShortName = "Cw")] + Curacao, + + [HumanReadable(LongName = "Cyprus", ShortName = "Cy")] + Cyprus, + + [HumanReadable(LongName = "Czechia", ShortName = "Cz")] + Czechia, + + [HumanReadable(LongName = "Czechoslovakia", ShortName = "Cs")] + Czechoslovakia, + + #endregion + + #region D + + // Zaire was "Zr" + [HumanReadable(LongName = "Democratic Republic of the Congo (Zaire)", ShortName = "Cd")] + DemocraticRepublicOfTheCongo, + + [HumanReadable(LongName = "Denmark", ShortName = "Dk")] + Denmark, + + [HumanReadable(LongName = "Diego Garcia", ShortName = "Dg")] + DiegoGarcia, + + [HumanReadable(LongName = "Djibouti", ShortName = "Dj")] + Djibouti, + + [HumanReadable(LongName = "Dominica", ShortName = "Dm")] + Dominica, + + [HumanReadable(LongName = "Dominican Republic", ShortName = "Do")] + DominicanRepublic, + + #endregion + + #region E + + [HumanReadable(LongName = "Ecuador", ShortName = "Ec")] + Ecuador, + + [HumanReadable(LongName = "Egypt", ShortName = "Eg")] + Egypt, + + [HumanReadable(LongName = "El Salvador", ShortName = "Sv")] + ElSalvador, + + [HumanReadable(LongName = "Equatorial Guinea", ShortName = "Gq")] + EquatorialGuinea, + + [HumanReadable(LongName = "Eritrea", ShortName = "Er")] + Eritrea, + + [HumanReadable(LongName = "Estonia", ShortName = "Ee")] + Estonia, + + [HumanReadable(LongName = "Eswatini", ShortName = "Sz")] + Eswatini, + + [HumanReadable(LongName = "Ethiopia", ShortName = "Et")] + Ethiopia, + + [HumanReadable(LongName = "Europe", ShortName = "E")] + Europe, + + [HumanReadable(LongName = "Europe, Asia", ShortName = "E,A")] + EuropeAsia, + + [HumanReadable(LongName = "Europe, Australia", ShortName = "E,Au")] + EuropeAustralia, + + [HumanReadable(LongName = "Europe, Canada", ShortName = "E,Ca")] + EuropeCanada, + + [HumanReadable(LongName = "Europe, Germany", ShortName = "E,G")] + EuropeGermany, + + // Commented out to avoid confusion + //[HumanReadable(LongName = "European Union", ShortName = "Eu")] + //EuropeanUnion, + + // Commented out to avoid confusion + //[HumanReadable(LongName = "Eurozone", ShortName = "Ez")] + //Eurozone, + + [HumanReadable(LongName = "Export", ShortName = "Ex")] + Export, + + #endregion + + #region F + + [HumanReadable(LongName = "Falkland Islands (Malvinas)", ShortName = "Fk")] + FalklandIslands, + + [HumanReadable(LongName = "Faroe Islands", ShortName = "Fo")] + FaroeIslands, + + [HumanReadable(LongName = "Federated States of Micronesia", ShortName = "Fm")] + FederatedStatesOfMicronesia, + + [HumanReadable(LongName = "Fiji", ShortName = "Fj")] + Fiji, + + // Formerly "Sf" + [HumanReadable(LongName = "Finland", ShortName = "Fi")] + Finland, + + // Should be "Fr" + [HumanReadable(LongName = "France", ShortName = "F")] + France, + + // Commented out to avoid confusion + //[HumanReadable(LongName = "France, Metropolitan", ShortName = "Fx")] + //FranceMetropolitan, + + [HumanReadable(LongName = "France, Spain", ShortName = "F,S")] + FranceSpain, + + [HumanReadable(LongName = "French Guiana", ShortName = "Gf")] + FrenchGuiana, + + [HumanReadable(LongName = "French Polynesia", ShortName = "Pf")] + FrenchPolynesia, + + [HumanReadable(LongName = "French Southern Territories", ShortName = "Tf")] + FrenchSouthernTerritories, + + #endregion + + #region G + + [HumanReadable(LongName = "Gabon", ShortName = "Ga")] + Gabon, + + [HumanReadable(LongName = "Gambia", ShortName = "Gm")] + Gambia, + + [HumanReadable(LongName = "Georgia", ShortName = "Ge")] + Georgia, + + // Should be "De" + [HumanReadable(LongName = "Germany", ShortName = "G")] + Germany, + + [HumanReadable(LongName = "Ghana", ShortName = "Gh")] + Ghana, + + [HumanReadable(LongName = "Gibraltar", ShortName = "Gi")] + Gibraltar, + + [HumanReadable(LongName = "Greater China", ShortName = "GC")] + GreaterChina, + + [HumanReadable(LongName = "Greece", ShortName = "Gr")] + Greece, + + [HumanReadable(LongName = "Greenland", ShortName = "Gl")] + Greenland, + + [HumanReadable(LongName = "Grenada", ShortName = "Gd")] + Grenada, + + [HumanReadable(LongName = "Guadeloupe", ShortName = "Gp")] + Guadeloupe, + + [HumanReadable(LongName = "Guam", ShortName = "Gu")] + Guam, + + [HumanReadable(LongName = "Guatemala", ShortName = "Gt")] + Guatemala, + + [HumanReadable(LongName = "Guernsey", ShortName = "Gg")] + Guernsey, + + [HumanReadable(LongName = "Guinea", ShortName = "Gn")] + Guinea, + + [HumanReadable(LongName = "Guinea-Bissau", ShortName = "Gw")] + GuineaBissau, + + [HumanReadable(LongName = "Guyana", ShortName = "Gy")] + Guyana, + + #endregion + + #region H + + [HumanReadable(LongName = "Haiti", ShortName = "Ht")] + Haiti, + + [HumanReadable(LongName = "Heard Island and McDonald Islands", ShortName = "Hm")] + HeardIslandAndMcDonaldIslands, + + [HumanReadable(LongName = "Holy See (Vatican City)", ShortName = "Va")] + HolySee, + + [HumanReadable(LongName = "Honduras", ShortName = "Hn")] + Honduras, + + [HumanReadable(LongName = "Hong Kong", ShortName = "Hk")] + HongKong, + + // Should be "Hu" + [HumanReadable(LongName = "Hungary", ShortName = "H")] + Hungary, + + #endregion + + #region I + + [HumanReadable(LongName = "Iceland", ShortName = "Is")] + Iceland, + + [HumanReadable(LongName = "India", ShortName = "In")] + India, + + [HumanReadable(LongName = "Indonesia", ShortName = "Id")] + Indonesia, + + [HumanReadable(LongName = "Iran", ShortName = "Ir")] + Iran, + + [HumanReadable(LongName = "Iraq", ShortName = "Iq")] + Iraq, + + [HumanReadable(LongName = "Ireland", ShortName = "Ie")] + Ireland, + + [HumanReadable(LongName = "Island of Sark", ShortName = "Cq")] + IslandOfSark, + + [HumanReadable(LongName = "Isle of Man", ShortName = "Im")] + IsleOfMan, + + [HumanReadable(LongName = "Israel", ShortName = "Il")] + Israel, + + // Should be "It" + [HumanReadable(LongName = "Italy", ShortName = "I")] + Italy, + + #endregion + + #region J + + [HumanReadable(LongName = "Jamaica", ShortName = "Jm")] + Jamaica, + + // Should be "Jp" + [HumanReadable(LongName = "Japan", ShortName = "J")] + Japan, + + [HumanReadable(LongName = "Japan, Asia", ShortName = "J,A")] + JapanAsia, + + [HumanReadable(LongName = "Japan, Europe", ShortName = "J,E")] + JapanEurope, + + [HumanReadable(LongName = "Japan, Korea", ShortName = "J,K")] + JapanKorea, + + [HumanReadable(LongName = "Japan, USA", ShortName = "J,U")] + JapanUSA, + + [HumanReadable(LongName = "Jersey", ShortName = "Je")] + Jersey, + + [HumanReadable(LongName = "Jordan", ShortName = "Jo")] + Jordan, + + #endregion + + #region K + + [HumanReadable(LongName = "Kazakhstan", ShortName = "Kz")] + Kazakhstan, + + [HumanReadable(LongName = "Kenya", ShortName = "Ke")] + Kenya, + + [HumanReadable(LongName = "Kiribati", ShortName = "Ki")] + Kiribati, + + [HumanReadable(LongName = "Korea (Democratic People's Republic of Korea)", ShortName = "Kp")] + NorthKorea, + + // Should be "Kr" + [HumanReadable(LongName = "Korea (Republic of Korea)", ShortName = "K")] + SouthKorea, + + [HumanReadable(LongName = "Kuwait", ShortName = "Kw")] + Kuwait, + + [HumanReadable(LongName = "Kyrgyzstan", ShortName = "Kg")] + Kyrgyzstan, + + #endregion + + #region L + + [HumanReadable(LongName = "(Laos) Lao People's Democratic Republic", ShortName = "La")] + Laos, + + [HumanReadable(LongName = "Latin America", ShortName = "LAm")] + LatinAmerica, + + [HumanReadable(LongName = "Latvia", ShortName = "Lv")] + Latvia, + + [HumanReadable(LongName = "Lebanon", ShortName = "Lb")] + Lebanon, + + [HumanReadable(LongName = "Lesotho", ShortName = "Ls")] + Lesotho, + + [HumanReadable(LongName = "Liberia", ShortName = "Lr")] + Liberia, + + [HumanReadable(LongName = "Libya", ShortName = "Ly")] + Libya, + + [HumanReadable(LongName = "Liechtenstein", ShortName = "Li")] + Liechtenstein, + + [HumanReadable(LongName = "Lithuania", ShortName = "Lt")] + Lithuania, + + [HumanReadable(LongName = "Luxembourg", ShortName = "Lu")] + Luxembourg, + + #endregion + + #region M + + [HumanReadable(LongName = "Macao", ShortName = "Mo")] + Macao, + + [HumanReadable(LongName = "Madagascar", ShortName = "Mg")] + Madagascar, + + [HumanReadable(LongName = "Malawi", ShortName = "Mw")] + Malawi, + + [HumanReadable(LongName = "Malaysia", ShortName = "My")] + Malaysia, + + [HumanReadable(LongName = "Maldives", ShortName = "Mv")] + Maldives, + + [HumanReadable(LongName = "Mali", ShortName = "Ml")] + Mali, + + [HumanReadable(LongName = "Malta", ShortName = "Mt")] + Malta, + + [HumanReadable(LongName = "Marshall Islands", ShortName = "Mh")] + MarshallIslands, + + [HumanReadable(LongName = "Martinique", ShortName = "Mq")] + Martinique, + + [HumanReadable(LongName = "Mauritania", ShortName = "Mr")] + Mauritania, + + [HumanReadable(LongName = "Mauritius", ShortName = "Mu")] + Mauritius, + + [HumanReadable(LongName = "Mayotte", ShortName = "Yt")] + Mayotte, + + [HumanReadable(LongName = "Mexico", ShortName = "Mx")] + Mexico, + + [HumanReadable(LongName = "Monaco", ShortName = "Mc")] + Monaco, + + [HumanReadable(LongName = "Mongolia", ShortName = "Mn")] + Mongolia, + + [HumanReadable(LongName = "Montenegro", ShortName = "Me")] + Montenegro, + + [HumanReadable(LongName = "Montserrat", ShortName = "Ms")] + Montserrat, + + [HumanReadable(LongName = "Morocco", ShortName = "Ma")] + Morocco, + + [HumanReadable(LongName = "Mozambique", ShortName = "Mz")] + Mozambique, + + // Burma was "Bu" + [HumanReadable(LongName = "Myanmar (Burma)", ShortName = "Mm")] + Myanmar, + + #endregion + + #region N + + [HumanReadable(LongName = "Namibia", ShortName = "Na")] + Namibia, + + [HumanReadable(LongName = "Nauru", ShortName = "Nr")] + Nauru, + + [HumanReadable(LongName = "Nepal", ShortName = "Np")] + Nepal, + + // Should be "Nl" + [HumanReadable(LongName = "Netherlands", ShortName = "N")] + Netherlands, + + [HumanReadable(LongName = "Netherlands Antilles", ShortName = "An")] + NetherlandsAntilles, + + // Commented out to avoid confusion + //[HumanReadable(LongName = "Neutral Zone", ShortName = "Nt")] + //NeutralZone, + + [HumanReadable(LongName = "New Caledonia", ShortName = "Nc")] + NewCaledonia, + + [HumanReadable(LongName = "New Zealand", ShortName = "Nz")] + NewZealand, + + [HumanReadable(LongName = "Nicaragua", ShortName = "Ni")] + Nicaragua, + + [HumanReadable(LongName = "Niger", ShortName = "Ne")] + Niger, + + [HumanReadable(LongName = "Nigeria", ShortName = "Ng")] + Nigeria, + + [HumanReadable(LongName = "Niue", ShortName = "Nu")] + Niue, + + [HumanReadable(LongName = "Norfolk Island", ShortName = "Nf")] + NorfolkIsland, + + [HumanReadable(LongName = "North Macedonia", ShortName = "Mk")] + NorthMacedonia, + + [HumanReadable(LongName = "Northern Mariana Islands", ShortName = "Mp")] + NorthernMarianaIslands, + + [HumanReadable(LongName = "Norway", ShortName = "No")] + Norway, + + #endregion + + #region O + + [HumanReadable(LongName = "Oman", ShortName = "Om")] + Oman, + + #endregion + + #region P + + [HumanReadable(LongName = "Pakistan", ShortName = "Pk")] + Pakistan, + + [HumanReadable(LongName = "Palau", ShortName = "Pw")] + Palau, + + [HumanReadable(LongName = "Panama", ShortName = "Pa")] + Panama, + + [HumanReadable(LongName = "Papua New Guinea", ShortName = "Pg")] + PapuaNewGuinea, + + [HumanReadable(LongName = "Paraguay", ShortName = "Py")] + Paraguay, + + [HumanReadable(LongName = "Peru", ShortName = "Pe")] + Peru, + + [HumanReadable(LongName = "Philippines", ShortName = "Ph")] + Philippines, + + [HumanReadable(LongName = "Pitcairn", ShortName = "Pn")] + Pitcairn, + + // Should be "Pl" + [HumanReadable(LongName = "Poland", ShortName = "P")] + Poland, + + [HumanReadable(LongName = "Portugal", ShortName = "Pt")] + Portugal, + + [HumanReadable(LongName = "Puerto Rico", ShortName = "Pr")] + PuertoRico, + + #endregion + + #region Q + + [HumanReadable(LongName = "Qatar", ShortName = "Qa")] + Qatar, + + #endregion + + #region R + + [HumanReadable(LongName = "Republic of Moldova", ShortName = "Md")] + RepublicOfMoldova, + + [HumanReadable(LongName = "Réunion", ShortName = "Re")] + Reunion, + + [HumanReadable(LongName = "Romania", ShortName = "Ro")] + Romania, + + // Should be "Ru" + [HumanReadable(LongName = "Russian Federation", ShortName = "R")] + RussianFederation, + + [HumanReadable(LongName = "Rwanda", ShortName = "Rw")] + Rwanda, + + #endregion + + #region S + + [HumanReadable(LongName = "Saint Barthélemy", ShortName = "Bl")] + SaintBarthelemy, + + [HumanReadable(LongName = "Saint Helena, Ascension and Tristan da Cunha", ShortName = "Sh")] + SaintHelena, + + [HumanReadable(LongName = "Saint Kitts and Nevis", ShortName = "Kn")] + SaintKittsAndNevis, + + [HumanReadable(LongName = "Saint Lucia", ShortName = "Lc")] + SaintLucia, + + [HumanReadable(LongName = "Saint Martin", ShortName = "Mf")] + SaintMartin, + + [HumanReadable(LongName = "Saint Pierre and Miquelon", ShortName = "Pm")] + SaintPierreAndMiquelon, + + [HumanReadable(LongName = "Saint Vincent and the Grenadines", ShortName = "Vc")] + SaintVincentAndTheGrenadines, + + [HumanReadable(LongName = "Samoa", ShortName = "Ws")] + Samoa, + + [HumanReadable(LongName = "San Marino", ShortName = "Sm")] + SanMarino, + + [HumanReadable(LongName = "Sao Tome and Principe", ShortName = "St")] + SaoTomeAndPrincipe, + + [HumanReadable(LongName = "Saudi Arabia", ShortName = "Sa")] + SaudiArabia, + + [HumanReadable(LongName = "Scandinavia", ShortName = "Sca")] + Scandinavia, + + [HumanReadable(LongName = "Senegal", ShortName = "Sn")] + Senegal, + + [HumanReadable(LongName = "Serbia", ShortName = "Rs")] + Serbia, + + [HumanReadable(LongName = "Seychelles", ShortName = "Sc")] + Seychelles, + + [HumanReadable(LongName = "Sierra Leone", ShortName = "Sl")] + SierraLeone, + + [HumanReadable(LongName = "Singapore", ShortName = "Sg")] + Singapore, + + [HumanReadable(LongName = "Sint Maarten", ShortName = "Sx")] + SintMaarten, + + [HumanReadable(LongName = "Slovakia", ShortName = "Sk")] + Slovakia, + + [HumanReadable(LongName = "Slovenia", ShortName = "Si")] + Slovenia, + + [HumanReadable(LongName = "Solomon Islands", ShortName = "Sb")] + SolomonIslands, + + [HumanReadable(LongName = "Somalia", ShortName = "So")] + Somalia, + + [HumanReadable(LongName = "South Africa", ShortName = "Za")] + SouthAfrica, + + [HumanReadable(LongName = "South Georgia and the South Sandwich Islands", ShortName = "Gs")] + SouthGeorgia, + + [HumanReadable(LongName = "South Sudan", ShortName = "Ss")] + SouthSudan, + + // Should be "Es" + [HumanReadable(LongName = "Spain", ShortName = "S")] + Spain, + + [HumanReadable(LongName = "Spain, Portugal", ShortName = "S,Pt")] + SpainPortugal, + + [HumanReadable(LongName = "Sri Lanka", ShortName = "Lk")] + SriLanka, + + [HumanReadable(LongName = "State of Palestine", ShortName = "Ps")] + StateOfPalestine, + + [HumanReadable(LongName = "Sudan", ShortName = "Sd")] + Sudan, + + [HumanReadable(LongName = "Suriname", ShortName = "Sr")] + Suriname, + + [HumanReadable(LongName = "Svalbard and Jan Mayen", ShortName = "Sj")] + SvalbardAndJanMayen, + + // Should be "Se" + [HumanReadable(LongName = "Sweden", ShortName = "Sw")] + Sweden, + + [HumanReadable(LongName = "Switzerland", ShortName = "Ch")] + Switzerland, + + [HumanReadable(LongName = "Syrian Arab Republic", ShortName = "Sy")] + SyrianArabRepublic, + + #endregion + + #region T + + [HumanReadable(LongName = "Taiwan", ShortName = "Tw")] + Taiwan, + + [HumanReadable(LongName = "Tajikistan", ShortName = "Tj")] + Tajikistan, + + [HumanReadable(LongName = "Thailand", ShortName = "Th")] + Thailand, + + // East Timor was "Tp" + [HumanReadable(LongName = "Timor-Leste (East Timor)", ShortName = "Tl")] + TimorLeste, + + [HumanReadable(LongName = "Togo", ShortName = "Tg")] + Togo, + + [HumanReadable(LongName = "Tokelau", ShortName = "Tk")] + Tokelau, + + [HumanReadable(LongName = "Tonga", ShortName = "To")] + Tonga, + + [HumanReadable(LongName = "Trinidad and Tobago", ShortName = "Tt")] + TrinidadAndTobago, + + [HumanReadable(LongName = "Tristan da Cunha", ShortName = "Ta")] + TristanDaCunha, + + [HumanReadable(LongName = "Tunisia", ShortName = "Tn")] + Tunisia, + + [HumanReadable(LongName = "Turkey", ShortName = "Tr")] + Turkey, + + [HumanReadable(LongName = "Turkmenistan", ShortName = "Tm")] + Turkmenistan, + + [HumanReadable(LongName = "Turks and Caicos Islands", ShortName = "Tc")] + TurksAndCaicosIslands, + + [HumanReadable(LongName = "Tuvalu", ShortName = "Tv")] + Tuvalu, + + #endregion + + #region U + + [HumanReadable(LongName = "Uganda", ShortName = "Ug")] + Uganda, + + // Should be both "Gb" and "Uk" + // United Kingdom of Great Britain and Northern Ireland + [HumanReadable(LongName = "UK", ShortName = "Uk")] + UnitedKingdom, + + [HumanReadable(LongName = "UK, Australia", ShortName = "Uk,Au")] + UKAustralia, + + [HumanReadable(LongName = "Ukraine", ShortName = "Ue")] + Ukraine, + + [HumanReadable(LongName = "United Arab Emirates", ShortName = "Ae")] + UnitedArabEmirates, + + // Commented out to avoid confusion + //[HumanReadable(LongName = "United Nations", ShortName = "Un")] + //UnitedNations, + + [HumanReadable(LongName = "United Republic of Tanzania", ShortName = "Tz")] + UnitedRepublicOfTanzania, + + [HumanReadable(LongName = "United States Minor Outlying Islands", ShortName = "Um")] + UnitedStatesMinorOutlyingIslands, + + [HumanReadable(LongName = "Uruguay", ShortName = "Uy")] + Uruguay, + + // Should be "Us" + // United States of America + [HumanReadable(LongName = "USA", ShortName = "U")] + UnitedStatesOfAmerica, + + [HumanReadable(LongName = "USA, Asia", ShortName = "U,A")] + USAAsia, + + [HumanReadable(LongName = "USA, Australia", ShortName = "U,Au")] + USAAustralia, + + [HumanReadable(LongName = "USA, Brazil", ShortName = "U,B")] + USABrazil, + + [HumanReadable(LongName = "USA, Canada", ShortName = "U,Ca")] + USACanada, + + [HumanReadable(LongName = "USA, Europe", ShortName = "U,E")] + USAEurope, + + [HumanReadable(LongName = "USA, Germany", ShortName = "U,G")] + USAGermany, + + [HumanReadable(LongName = "USA, Japan", ShortName = "U,J")] + USAJapan, + + [HumanReadable(LongName = "USA, Korea", ShortName = "U,K")] + USAKorea, + + [HumanReadable(LongName = "USSR", ShortName = "Su")] + USSR, + + [HumanReadable(LongName = "Uzbekistan", ShortName = "Uz")] + Uzbekistan, + + #endregion + + #region V + + [HumanReadable(LongName = "Vanuatu", ShortName = "Vu")] + Vanuatu, + + [HumanReadable(LongName = "Venezuela", ShortName = "Ve")] + Venezuela, + + [HumanReadable(LongName = "Viet Nam", ShortName = "Vn")] + VietNam, + + [HumanReadable(LongName = "Virgin Islands (British)", ShortName = "Vg")] + BritishVirginIslands, + + [HumanReadable(LongName = "Virgin Islands (US)", ShortName = "Vi")] + USVirginIslands, + + #endregion + + #region W + + [HumanReadable(LongName = "Wallis and Futuna", ShortName = "Wf")] + WallisAndFutuna, + + [HumanReadable(LongName = "Western Sahara", ShortName = "Eh")] + WesternSahara, + + [HumanReadable(LongName = "World", ShortName = "W")] + World, + + #endregion + + #region Y + + [HumanReadable(LongName = "Yemen", ShortName = "Ye")] + Yemen, + + [HumanReadable(LongName = "Yugoslavia", ShortName = "Yu")] + Yugoslavia, + + #endregion + + #region Z + + [HumanReadable(LongName = "Zambia", ShortName = "Zm")] + Zambia, + + [HumanReadable(LongName = "Zimbabwe", ShortName = "Zw")] + Zimbabwe, + + #endregion + } + + /// + /// List of all Redump site codes + /// + public enum SiteCode + { + [HumanReadable(ShortName = "[T:ACC]", LongName = "Acclaim ID:")] + AcclaimID, + + [HumanReadable(ShortName = "[T:ACT]", LongName = "Activision ID:")] + ActivisionID, + + [HumanReadable(ShortName = "[T:ALT]", LongName = "Alternative Title:")] + AlternativeTitle, + + [HumanReadable(ShortName = "[T:ALTF]", LongName = "Alternative Foreign Title:")] + AlternativeForeignTitle, + + [HumanReadable(ShortName = "[T:BID]", LongName = "Bandai ID:")] + BandaiID, + + [HumanReadable(ShortName = "[T:BBFC]", LongName = "BBFC Reg. No.:")] + BBFCRegistrationNumber, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "CD Projekt ID:", LongName = "CD Projekt ID:")] + CDProjektID, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "Disc Hologram ID:", LongName = "Disc Hologram ID:")] + DiscHologramID, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "DMI:", LongName = "DMI:")] + DMIHash, + + [HumanReadable(ShortName = "[T:DNAS]", LongName = "DNAS Disc ID:")] + DNASDiscID, + + [HumanReadable(ShortName = "[T:EAID]", LongName = "Electronic Arts ID:")] + ElectronicArtsID, + + [HumanReadable(ShortName = "[T:X]", LongName = "Extras:")] + Extras, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "Filename:", LongName = "Filename:")] + Filename, + + [HumanReadable(ShortName = "[T:FIID]", LongName = "Fox Interactive ID:")] + FoxInteractiveID, + + [HumanReadable(ShortName = "[T:GF]", LongName = "Game Footage:")] + GameFootage, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "Games:", LongName = "Games:")] + Games, + + [HumanReadable(ShortName = "[T:G]", LongName = "Genre:")] + Genre, + + [HumanReadable(ShortName = "[T:GTID]", LongName = "GT Interactive ID:")] + GTInteractiveID, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "Internal Name:", LongName = "Internal Name:")] + InternalName, + + [HumanReadable(ShortName = "[T:ISN]", LongName = "Internal Serial:")] + InternalSerialName, + + [HumanReadable(ShortName = "[T:ISBN]", LongName = "ISBN:")] + ISBN, + + [HumanReadable(ShortName = "[T:ISSN]", LongName = "ISSN:")] + ISSN, + + [HumanReadable(ShortName = "[T:JID]", LongName = "JASRAC ID:")] + JASRACID, + + [HumanReadable(ShortName = "[T:KIRZ]", LongName = "King Records ID:")] + KingRecordsID, + + [HumanReadable(ShortName = "[T:KOEI]", LongName = "Koei ID:")] + KoeiID, + + [HumanReadable(ShortName = "[T:KID]", LongName = "Konami ID:")] + KonamiID, + + [HumanReadable(ShortName = "[T:LAID]", LongName = "Lucas Arts ID:")] + LucasArtsID, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "Microsoft ID:", LongName = "Microsoft ID:")] + MicrosoftID, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "Multisession:", LongName = "Multisession:")] + Multisession, + + [HumanReadable(ShortName = "[T:NGID]", LongName = "Nagano ID:")] + NaganoID, + + [HumanReadable(ShortName = "[T:NID]", LongName = "Namco ID:")] + NamcoID, + + [HumanReadable(ShortName = "[T:NYG]", LongName = "Net Yaroze Games:")] + NetYarozeGames, + + [HumanReadable(ShortName = "[T:NPS]", LongName = "Nippon Ichi Software ID:")] + NipponIchiSoftwareID, + + [HumanReadable(ShortName = "[T:OID]", LongName = "Origin ID:")] + OriginID, + + [HumanReadable(ShortName = "[T:P]", LongName = "Patches:")] + Patches, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "PFI:", LongName = "PFI:")] + PFIHash, + + [HumanReadable(ShortName = "[T:PD]", LongName = "Playable Demos:")] + PlayableDemos, + + [HumanReadable(ShortName = "[T:PCID]", LongName = "Pony Canyon ID:")] + PonyCanyonID, + + [HumanReadable(ShortName = "[T:PT2]", LongName = "Postgap type: Form 2")] + PostgapType, + + [HumanReadable(ShortName = "[T:PPN]", LongName = "PPN:")] + PPN, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "Ring non-zero data start:", LongName = "Ring non-zero data start:")] + RingNonZeroDataStart, + + [HumanReadable(ShortName = "[T:RD]", LongName = "Rolling Demos:")] + RollingDemos, + + [HumanReadable(ShortName = "[T:SG]", LongName = "Savegames:")] + Savegames, + + [HumanReadable(ShortName = "[T:SID]", LongName = "Sega ID:")] + SegaID, + + [HumanReadable(ShortName = "[T:SNID]", LongName = "Selen ID:")] + SelenID, + + [HumanReadable(ShortName = "[T:S]", LongName = "Series:")] + Series, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "Sierra ID:", LongName = "Sierra ID:")] + SierraID, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "SS:", LongName = "SS:")] + SSHash, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "SS version:", LongName = "SS version:")] + SSVersion, + + [HumanReadable(ShortName = "[T:TID]", LongName = "Taito ID:")] + TaitoID, + + [HumanReadable(ShortName = "[T:TD]", LongName = "Tech Demos:")] + TechDemos, + + [HumanReadable(ShortName = "[T:UID]", LongName = "Ubisoft ID:")] + UbisoftID, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "Universal Hash (SHA-1):", LongName = "Universal Hash (SHA-1):")] + UniversalHash, + + [HumanReadable(ShortName = "[T:VID]", LongName = "Valve ID:")] + ValveID, + + [HumanReadable(ShortName = "[T:VFC]", LongName = "VFC code:")] + VFCCode, + + [HumanReadable(ShortName = "[T:V]", LongName = "Videos:")] + Videos, + + [HumanReadable(ShortName = "[T:VOL]", LongName = "Volume Label:")] + VolumeLabel, + + [HumanReadable(ShortName = "[T:VCD]", LongName = "V-CD")] + VCD, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "XeMID:", LongName = "XeMID:")] + XeMID, + + // TODO: This doesn't have a site tag yet + [HumanReadable(ShortName = "XMID:", LongName = "XMID:")] + XMID, + } + + /// + /// List of system categories + /// + public enum SystemCategory + { + NONE = 0, + + [HumanReadable(LongName = "Disc-Based Consoles")] + DiscBasedConsole, + + [HumanReadable(LongName = "Other Consoles")] + OtherConsole, + + [HumanReadable(LongName = "Computers")] + Computer, + + [HumanReadable(LongName = "Arcade")] + Arcade, + + [HumanReadable(LongName = "Other")] + Other, + }; + + /// + /// Generic yes/no values + /// + public enum YesNo + { + [HumanReadable(LongName = "Yes/No")] + NULL = 0, + + [HumanReadable(LongName = "No")] + No = 1, + + [HumanReadable(LongName = "Yes")] + Yes = 2, + } +} diff --git a/Data/Extensions.cs b/Data/Extensions.cs new file mode 100644 index 0000000..9ba7956 --- /dev/null +++ b/Data/Extensions.cs @@ -0,0 +1,2176 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using RedumpLib.Attributes; + +namespace SabreTools.RedumpLib.Data +{ + /// + /// Information pertaining to Redump systems + /// + public static class Extensions + { + #region Cross-Enumeration + + /// + /// Get a list of valid MediaTypes for a given RedumpSystem + /// + /// RedumpSystem value to check + /// MediaTypes, if possible + public static List MediaTypes(this RedumpSystem? system) + { + var types = new List(); + + switch (system) + { + #region Consoles + + // https://en.wikipedia.org/wiki/Atari_Jaguar_CD + case RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Playdia + case RedumpSystem.BandaiPlaydiaQuickInteractiveSystem: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Apple_Bandai_Pippin + case RedumpSystem.BandaiPippin: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Amiga_CD32 + case RedumpSystem.CommodoreAmigaCD32: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Commodore_CDTV + case RedumpSystem.CommodoreAmigaCDTV: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/EVO_Smart_Console + case RedumpSystem.EnvizionsEVOSmartConsole: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // https://en.wikipedia.org/wiki/FM_Towns_Marty + case RedumpSystem.FujitsuFMTownsMarty: + types.Add(MediaType.CDROM); + types.Add(MediaType.FloppyDisk); + break; + + // http://videogamekraken.com/ion-educational-gaming-system-by-hasbro + case RedumpSystem.HasbroiONEducationalGamingSystem: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/VideoNow + case RedumpSystem.HasbroVideoNow: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/VideoNow + case RedumpSystem.HasbroVideoNowColor: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/VideoNow + case RedumpSystem.HasbroVideoNowJr: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/VideoNow + case RedumpSystem.HasbroVideoNowXP: + types.Add(MediaType.CDROM); + break; + + case RedumpSystem.MattelFisherPriceiXL: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/HyperScan + case RedumpSystem.MattelHyperScan: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Xbox_(console) + case RedumpSystem.MicrosoftXbox: + types.Add(MediaType.DVD); + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Xbox_360 + case RedumpSystem.MicrosoftXbox360: + types.Add(MediaType.DVD); + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Xbox_One + case RedumpSystem.MicrosoftXboxOne: + types.Add(MediaType.BluRay); + break; + + // https://en.wikipedia.org/wiki/Xbox_Series_X_and_Series_S + case RedumpSystem.MicrosoftXboxSeriesXS: + types.Add(MediaType.BluRay); + break; + + // https://en.wikipedia.org/wiki/TurboGrafx-16 + case RedumpSystem.NECPCEngineCDTurboGrafxCD: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/PC-FX + case RedumpSystem.NECPCFXPCFXGA: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/GameCube + case RedumpSystem.NintendoGameCube: + types.Add(MediaType.DVD); // Only added here to help users; not strictly correct + types.Add(MediaType.NintendoGameCubeGameDisc); + break; + + // https://en.wikipedia.org/wiki/Super_NES_CD-ROM + case RedumpSystem.NintendoSonySuperNESCDROMSystem: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Wii + case RedumpSystem.NintendoWii: + types.Add(MediaType.DVD); // Only added here to help users; not strictly correct + types.Add(MediaType.NintendoWiiOpticalDisc); + break; + + // https://en.wikipedia.org/wiki/Wii_U + case RedumpSystem.NintendoWiiU: + types.Add(MediaType.NintendoWiiUOpticalDisc); + break; + + // https://en.wikipedia.org/wiki/3DO_Interactive_Multiplayer + case RedumpSystem.Panasonic3DOInteractiveMultiplayer: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Philips_CD-i + case RedumpSystem.PhilipsCDi: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/LaserActive + case RedumpSystem.PioneerLaserActive: + types.Add(MediaType.CDROM); + types.Add(MediaType.LaserDisc); + break; + + // https://en.wikipedia.org/wiki/Sega_CD + case RedumpSystem.SegaMegaCDSegaCD: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Dreamcast + case RedumpSystem.SegaDreamcast: + types.Add(MediaType.CDROM); // Low density partition, MIL-CD + types.Add(MediaType.GDROM); // High density partition + break; + + // https://en.wikipedia.org/wiki/Sega_Saturn + case RedumpSystem.SegaSaturn: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Neo_Geo_CD + case RedumpSystem.SNKNeoGeoCD: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/PlayStation_(console) + case RedumpSystem.SonyPlayStation: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/PlayStation_2 + case RedumpSystem.SonyPlayStation2: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // https://en.wikipedia.org/wiki/PlayStation_3 + case RedumpSystem.SonyPlayStation3: + types.Add(MediaType.BluRay); + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // https://en.wikipedia.org/wiki/PlayStation_4 + case RedumpSystem.SonyPlayStation4: + types.Add(MediaType.BluRay); + break; + + // https://en.wikipedia.org/wiki/PlayStation_5 + case RedumpSystem.SonyPlayStation5: + types.Add(MediaType.BluRay); + break; + + // https://en.wikipedia.org/wiki/PlayStation_Portable + case RedumpSystem.SonyPlayStationPortable: + types.Add(MediaType.UMD); + types.Add(MediaType.CDROM); // Development discs only + types.Add(MediaType.DVD); // Development discs only + break; + + // https://en.wikipedia.org/wiki/Tandy_Video_Information_System + case RedumpSystem.MemorexVisualInformationSystem: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Nuon_(DVD_technology) + case RedumpSystem.VMLabsNUON: + types.Add(MediaType.DVD); + break; + + // https://en.wikipedia.org/wiki/V.Flash + case RedumpSystem.VTechVFlashVSmilePro: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Game_Wave_Family_Entertainment_System + case RedumpSystem.ZAPiTGamesGameWaveFamilyEntertainmentSystem: + types.Add(MediaType.DVD); + break; + + #endregion + + #region Computers + + // https://en.wikipedia.org/wiki/Acorn_Archimedes + case RedumpSystem.AcornArchimedes: + types.Add(MediaType.CDROM); + types.Add(MediaType.FloppyDisk); + break; + + // https://en.wikipedia.org/wiki/Macintosh + case RedumpSystem.AppleMacintosh: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + types.Add(MediaType.FloppyDisk); + types.Add(MediaType.HardDisk); + break; + + // https://en.wikipedia.org/wiki/Amiga + case RedumpSystem.CommodoreAmigaCD: + types.Add(MediaType.CDROM); + types.Add(MediaType.FloppyDisk); + break; + + // https://en.wikipedia.org/wiki/FM_Towns + case RedumpSystem.FujitsuFMTownsseries: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/IBM_PC_compatible + case RedumpSystem.IBMPCcompatible: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + types.Add(MediaType.BluRay); + types.Add(MediaType.FloppyDisk); + types.Add(MediaType.HardDisk); + types.Add(MediaType.DataCartridge); + break; + + // https://en.wikipedia.org/wiki/PC-8800_series + case RedumpSystem.NECPC88series: + types.Add(MediaType.CDROM); + types.Add(MediaType.FloppyDisk); + break; + + // https://en.wikipedia.org/wiki/PC-9800_series + case RedumpSystem.NECPC98series: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + types.Add(MediaType.FloppyDisk); + break; + + // https://en.wikipedia.org/wiki/X68000 + case RedumpSystem.SharpX68000: + types.Add(MediaType.CDROM); + types.Add(MediaType.FloppyDisk); + break; + + #endregion + + #region Arcade + + // https://www.bigbookofamigahardware.com/bboah/product.aspx?id=36 + case RedumpSystem.AmigaCUBOCD32: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Orbatak + case RedumpSystem.AmericanLaserGames3DO: + types.Add(MediaType.CDROM); + break; + + // http://system16.com/hardware.php?id=779 + case RedumpSystem.Atari3DO: + types.Add(MediaType.CDROM); + break; + + // http://newlifegames.net/nlg/index.php?topic=22003.0 + // http://newlifegames.net/nlg/index.php?topic=5486.msg119440 + case RedumpSystem.Atronic: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // https://www.arcade-museum.com/members/member_detail.php?member_id=406530 + case RedumpSystem.AUSCOMSystem1: + types.Add(MediaType.CDROM); + break; + + // http://newlifegames.net/nlg/index.php?topic=285.0 + case RedumpSystem.BallyGameMagic: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/CP_System_III + case RedumpSystem.CapcomCPSystemIII: + types.Add(MediaType.CDROM); + break; + + // UNKNOWN + case RedumpSystem.funworldPhotoPlay: + types.Add(MediaType.CDROM); + break; + + // UNKNOWN + case RedumpSystem.GlobalVRVarious: + types.Add(MediaType.CDROM); + break; + + // https://service.globalvr.com/troubleshooting/vortek.html + case RedumpSystem.GlobalVRVortek: + types.Add(MediaType.CDROM); + break; + + // https://service.globalvr.com/downloads/v3/040-1001-01c-V3-System-Manual.pdf + case RedumpSystem.GlobalVRVortekV3: + types.Add(MediaType.CDROM); + break; + + // https://www.icegame.com/games + case RedumpSystem.ICEPCHardware: + types.Add(MediaType.DVD); + break; + + // https://github.com/mamedev/mame/blob/master/src/mame/drivers/iteagle.cpp + case RedumpSystem.IncredibleTechnologiesEagle: + types.Add(MediaType.CDROM); + break; + + // UNKNOWN + case RedumpSystem.IncredibleTechnologiesVarious: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // UNKNOWN + case RedumpSystem.JVLiTouch: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // https://en.wikipedia.org/wiki/E-Amusement + case RedumpSystem.KonamieAmusement: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // http://system16.com/hardware.php?id=828 + case RedumpSystem.KonamiFireBeat: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // http://system16.com/hardware.php?id=577 + case RedumpSystem.KonamiSystemGV: + types.Add(MediaType.CDROM); + break; + + // http://system16.com/hardware.php?id=575 + case RedumpSystem.KonamiM2: + types.Add(MediaType.CDROM); + break; + + // http://system16.com/hardware.php?id=586 + // http://system16.com/hardware.php?id=977 + case RedumpSystem.KonamiPython: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // http://system16.com/hardware.php?id=976 + // http://system16.com/hardware.php?id=831 + case RedumpSystem.KonamiPython2: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // http://system16.com/hardware.php?id=582 + // http://system16.com/hardware.php?id=822 + // http://system16.com/hardware.php?id=823 + case RedumpSystem.KonamiSystem573: + types.Add(MediaType.CDROM); + break; + + // http://system16.com/hardware.php?id=827 + case RedumpSystem.KonamiTwinkle: + types.Add(MediaType.CDROM); + break; + + // UNKNOWN + case RedumpSystem.KonamiVarious: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // http://www.meritgames.com/Support_Center/manuals/PM0591-01.pdf + case RedumpSystem.MeritIndustriesBoardwalk: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // http://www.meritgames.com/Support_Center/Force%20Elite/PM0380-09.pdf + // http://www.meritgames.com/Support_Center/Force%20Upright/PM0382-07%20FORCE%20Upright%20manual.pdf + // http://www.meritgames.com/Support_Center/Force%20Upright/PM0383-07%20FORCE%20Upright%20manual.pdf + case RedumpSystem.MeritIndustriesMegaTouchForce: + types.Add(MediaType.CDROM); + break; + + // http://www.meritgames.com/Service%20Center/Ion%20Troubleshooting.pdf + case RedumpSystem.MeritIndustriesMegaTouchION: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // http://www.meritgames.com/Support_Center/EZ%20Maxx/Manuals/MAXX%20Elite%20with%20coin.pdf + // http://www.meritgames.com/Support_Center/EZ%20Maxx/Manuals/MAXX%20Elite.pdf + // http://www.meritgames.com/Support_Center/manuals/90003010%20Maxx%20TSM_Rev%20C.pdf + case RedumpSystem.MeritIndustriesMegaTouchMaxx: + types.Add(MediaType.CDROM); + break; + + // http://www.meritgames.com/Support_Center/manuals/pm0076_OA_Megatouch%20XL%20Trouble%20Shooting%20Manual.pdf + // http://www.meritgames.com/Support_Center/MEGA%20XL/manuals/Megatouch_XL_pm0109-0D.pdf + // http://www.meritgames.com/Support_Center/MEGA%20XL/manuals/Megatouch_XL_Super_5000_manual.pdf + case RedumpSystem.MeritIndustriesMegaTouchXL: + types.Add(MediaType.CDROM); + break; + + // http://system16.com/hardware.php?id=543 + // http://system16.com/hardware.php?id=546 + // http://system16.com/hardware.php?id=872 + case RedumpSystem.NamcoSystem246256: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // http://system16.com/hardware.php?id=545 + case RedumpSystem.NamcoSegaNintendoTriforce: + types.Add(MediaType.CDROM); // Low density partition + types.Add(MediaType.GDROM); // High density partition + break; + + // http://system16.com/hardware.php?id=535 + case RedumpSystem.NamcoSystem12: + types.Add(MediaType.CDROM); + break; + + // https://www.arcade-history.com/?n=the-yakyuuken-part-1&page=detail&id=33049 + case RedumpSystem.NewJatreCDi: + types.Add(MediaType.CDROM); + break; + + // http://blog.system11.org/?p=2499 + case RedumpSystem.NichibutsuHighRateSystem: + types.Add(MediaType.DVD); + break; + + // http://blog.system11.org/?p=2514 + case RedumpSystem.NichibutsuSuperCD: + types.Add(MediaType.CDROM); + break; + + // http://collectedit.com/collectors/shou-time-213/arcade-pcbs-281/x-rate-dvd-series-17-newlywed-life-japan-by-nichibutsu-32245 + case RedumpSystem.NichibutsuXRateSystem: + types.Add(MediaType.DVD); + break; + + // https://en.wikipedia.org/wiki/Panasonic_M2 + case RedumpSystem.PanasonicM2: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + // https://github.com/mamedev/mame/blob/master/src/mame/drivers/photoply.cpp + case RedumpSystem.PhotoPlayVarious: + types.Add(MediaType.CDROM); + break; + + // UNKNOWN + case RedumpSystem.RawThrillsVarious: + types.Add(MediaType.DVD); + break; + + // UNKNOWN + case RedumpSystem.SegaALLS: + types.Add(MediaType.DVD); + break; + + // http://system16.com/hardware.php?id=729 + case RedumpSystem.SegaChihiro: + types.Add(MediaType.CDROM); // Low density partition + types.Add(MediaType.GDROM); // High density partition + break; + + // http://system16.com/hardware.php?id=907 + case RedumpSystem.SegaEuropaR: + types.Add(MediaType.DVD); + break; + + // http://system16.com/hardware.php?id=985 + // http://system16.com/hardware.php?id=731 + // http://system16.com/hardware.php?id=984 + // http://system16.com/hardware.php?id=986 + case RedumpSystem.SegaLindbergh: + types.Add(MediaType.DVD); + break; + + // http://system16.com/hardware.php?id=721 + // http://system16.com/hardware.php?id=723 + // http://system16.com/hardware.php?id=906 + // http://system16.com/hardware.php?id=722 + case RedumpSystem.SegaNaomi: + types.Add(MediaType.CDROM); // Low density partition + types.Add(MediaType.GDROM); // High density partition + break; + + // http://system16.com/hardware.php?id=725 + // http://system16.com/hardware.php?id=726 + // http://system16.com/hardware.php?id=727 + case RedumpSystem.SegaNaomi2: + types.Add(MediaType.CDROM); // Low density partition + types.Add(MediaType.GDROM); // High density partition + break; + + // https://en.wikipedia.org/wiki/List_of_Sega_arcade_system_boards#Sega_Nu + case RedumpSystem.SegaNu: + types.Add(MediaType.DVD); + break; + + // http://system16.com/hardware.php?id=910 + // https://en.wikipedia.org/wiki/List_of_Sega_arcade_system_boards#Sega_Ring_series + case RedumpSystem.SegaRingEdge: + types.Add(MediaType.DVD); + break; + + // http://system16.com/hardware.php?id=982 + // https://en.wikipedia.org/wiki/List_of_Sega_arcade_system_boards#Sega_Ring_series + case RedumpSystem.SegaRingEdge2: + types.Add(MediaType.DVD); + break; + + // http://system16.com/hardware.php?id=911 + // https://en.wikipedia.org/wiki/List_of_Sega_arcade_system_boards#Sega_Ring_series + case RedumpSystem.SegaRingWide: + types.Add(MediaType.DVD); + break; + + // http://system16.com/hardware.php?id=711 + case RedumpSystem.SegaTitanVideo: + types.Add(MediaType.CDROM); + break; + + // http://system16.com/hardware.php?id=709 + // http://system16.com/hardware.php?id=710 + case RedumpSystem.SegaSystem32: + types.Add(MediaType.CDROM); + break; + + // https://github.com/mamedev/mame/blob/master/src/mame/drivers/seibucats.cpp + case RedumpSystem.SeibuCATSSystem: + types.Add(MediaType.DVD); + break; + + // https://www.tab.at/en/support/support/downloads + case RedumpSystem.TABAustriaQuizard: + types.Add(MediaType.CDROM); + break; + + // https://primetimeamusements.com/product/tsumo-multi-game-motion-system/ + // https://www.highwaygames.com/arcade-machines/tsumo-tsunami-motion-8117/ + case RedumpSystem.TsunamiTsuMoMultiGameMotionSystem: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/UltraCade_Technologies + case RedumpSystem.UltraCade: + types.Add(MediaType.CDROM); + types.Add(MediaType.DVD); + break; + + #endregion + + #region Others + + // https://en.wikipedia.org/wiki/Audio_CD + case RedumpSystem.AudioCD: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Blu-ray#Player_profiles + case RedumpSystem.BDVideo: + types.Add(MediaType.BluRay); + break; + + // https://en.wikipedia.org/wiki/DVD-Audio + case RedumpSystem.DVDAudio: + types.Add(MediaType.DVD); + break; + + // https://en.wikipedia.org/wiki/DVD-Video + case RedumpSystem.DVDVideo: + types.Add(MediaType.DVD); + break; + + // https://en.wikipedia.org/wiki/Blue_Book_(CD_standard) + case RedumpSystem.EnhancedCD: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/HD_DVD + case RedumpSystem.HDDVDVideo: + types.Add(MediaType.HDDVD); + break; + + // UNKNOWN + case RedumpSystem.NavisoftNaviken21: + types.Add(MediaType.CDROM); + break; + + // UNKNOWN + case RedumpSystem.PalmOS: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Photo_CD + case RedumpSystem.PhotoCD: + types.Add(MediaType.CDROM); + break; + + // UNKNOWN + case RedumpSystem.PlayStationGameSharkUpdates: + types.Add(MediaType.CDROM); + break; + + // UNKNOWN + case RedumpSystem.PocketPC: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Doors_and_Windows_(EP) + case RedumpSystem.RainbowDisc: + types.Add(MediaType.CDROM); + break; + + // https://segaretro.org/Prologue_21 + case RedumpSystem.SegaPrologue21MultimediaKaraokeSystem: + types.Add(MediaType.CDROM); + break; + + // UNKNOWN + case RedumpSystem.SonyElectronicBook: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Super_Audio_CD + case RedumpSystem.SuperAudioCD: + types.Add(MediaType.CDROM); + break; + + // https://www.cnet.com/products/tao-music-iktv-karaoke-station-karaoke-system-series/ + case RedumpSystem.TaoiKTV: + types.Add(MediaType.CDROM); + break; + + // http://ultimateconsoledatabase.com/golden/kiss_site.htm + case RedumpSystem.TomyKissSite: + types.Add(MediaType.CDROM); + break; + + // https://en.wikipedia.org/wiki/Video_CD + case RedumpSystem.VideoCD: + types.Add(MediaType.CDROM); + break; + + #endregion + + default: + types.Add(MediaType.NONE); + break; + } + + return types; + } + + /// + /// Convert master list of all media types to currently known Redump disc types + /// + /// MediaType value to check + /// DiscType if possible, null on error + public static DiscType? ToDiscType(this MediaType? mediaType) + { + switch (mediaType) + { + case MediaType.BluRay: + return DiscType.BD50; + case MediaType.CDROM: + return DiscType.CD; + case MediaType.DVD: + return DiscType.DVD9; + case MediaType.GDROM: + return DiscType.GDROM; + case MediaType.HDDVD: + return DiscType.HDDVDSL; + // case MediaType.MILCD: // TODO: Support this? + // return DiscType.MILCD; + case MediaType.NintendoGameCubeGameDisc: + return DiscType.NintendoGameCubeGameDisc; + case MediaType.NintendoWiiOpticalDisc: + return DiscType.NintendoWiiOpticalDiscDL; + case MediaType.NintendoWiiUOpticalDisc: + return DiscType.NintendoWiiUOpticalDiscSL; + case MediaType.UMD: + return DiscType.UMDDL; + default: + return null; + } + } + + /// + /// Convert currently known Redump disc types to master list of all media types + /// + /// DiscType value to check + /// MediaType if possible, null on error + public static MediaType? ToMediaType(this DiscType? discType) + { + switch (discType) + { + case DiscType.BD25: + case DiscType.BD33: + case DiscType.BD50: + case DiscType.BD66: + case DiscType.BD100: + case DiscType.BD128: + return MediaType.BluRay; + case DiscType.CD: + return MediaType.CDROM; + case DiscType.DVD5: + case DiscType.DVD9: + return MediaType.DVD; + case DiscType.GDROM: + return MediaType.GDROM; + case DiscType.HDDVDSL: + case DiscType.HDDVDDL: + return MediaType.HDDVD; + // case DiscType.MILCD: // TODO: Support this? + // return MediaType.MILCD; + case DiscType.NintendoGameCubeGameDisc: + return MediaType.NintendoGameCubeGameDisc; + case DiscType.NintendoWiiOpticalDiscSL: + case DiscType.NintendoWiiOpticalDiscDL: + return MediaType.NintendoWiiOpticalDisc; + case DiscType.NintendoWiiUOpticalDiscSL: + return MediaType.NintendoWiiUOpticalDisc; + case DiscType.UMDSL: + case DiscType.UMDDL: + return MediaType.UMD; + default: + return null; + } + } + + #endregion + + #region Disc Category + + /// + /// Get the Redump longnames for each known category + /// + /// + /// + public static string LongName(this DiscCategory? category) => AttributeHelper.GetAttribute(category)?.LongName; + + /// + /// Get the Category enum value for a given string + /// + /// String value to convert + /// Category represented by the string, if possible + public static DiscCategory? ToDiscCategory(string category) + { + switch (category?.ToLowerInvariant()) + { + case "games": + return DiscCategory.Games; + case "demos": + return DiscCategory.Demos; + case "video": + return DiscCategory.Video; + case "audio": + return DiscCategory.Audio; + case "multimedia": + return DiscCategory.Multimedia; + case "applications": + return DiscCategory.Applications; + case "coverdiscs": + return DiscCategory.Coverdiscs; + case "educational": + return DiscCategory.Educational; + case "bonusdiscs": + case "bonus discs": + return DiscCategory.BonusDiscs; + case "preproduction": + return DiscCategory.Preproduction; + case "addons": + case "add-ons": + return DiscCategory.AddOns; + default: + return DiscCategory.Games; + } + } + + #endregion + + #region Disc Type + + /// + /// Get the Redump longnames for each known disc type + /// + /// + /// + public static string LongName(this DiscType? discType) => AttributeHelper.GetAttribute(discType)?.LongName; + + /// + /// Get the DiscType enum value for a given string + /// + /// String value to convert + /// DiscType represented by the string, if possible + public static DiscType? ToDiscType(string discType) + { + switch (discType?.ToLowerInvariant()) + { + case "bd25": + case "bd-25": + return DiscType.BD25; + case "bd33": + case "bd-33": + return DiscType.BD33; + case "bd50": + case "bd-50": + return DiscType.BD50; + case "bd66": + case "bd-66": + return DiscType.BD66; + case "bd100": + case "bd-100": + return DiscType.BD100; + case "bd128": + case "bd-128": + return DiscType.BD128; + case "cd": + case "cdrom": + case "cd-rom": + return DiscType.CD; + case "dvd5": + case "dvd-5": + return DiscType.DVD5; + case "dvd9": + case "dvd-9": + return DiscType.DVD9; + case "gd": + case "gdrom": + case "gd-rom": + return DiscType.GDROM; + case "hddvd": + case "hddvdsl": + case "hd-dvd sl": + return DiscType.HDDVDSL; + case "hddvddl": + case "hd-dvd dl": + return DiscType.HDDVDDL; + case "milcd": + case "mil-cd": + return DiscType.MILCD; + case "nintendogamecubegamedisc": + case "nintendo game cube game disc": + return DiscType.NintendoGameCubeGameDisc; + case "nintendowiiopticaldiscsl": + case "nintendo wii optical disc sl": + return DiscType.NintendoWiiOpticalDiscSL; + case "nintendowiiopticaldiscdl": + case "nintendo wii optical disc dl": + return DiscType.NintendoWiiOpticalDiscDL; + case "nintendowiiuopticaldiscsl": + case "nintendo wii u optical disc sl": + return DiscType.NintendoWiiUOpticalDiscSL; + case "umd": + case "umdsl": + case "umd sl": + return DiscType.UMDSL; + case "umddl": + case "umd dl": + return DiscType.UMDDL; + default: + return null; + } + } + + #endregion + + #region Language + + /// + /// Get the Redump longnames for each known language + /// + /// + /// + public static string LongName(this Language? language) => AttributeHelper.GetAttribute(language)?.LongName; + + /// + /// Get the Redump shortnames for each known language + /// + /// + /// + public static string ShortName(this Language? language) + { + // Some languages need to use the alternate code instead + switch (language) + { + case Language.Albanian: + case Language.Armenian: + case Language.Icelandic: + case Language.Macedonian: + case Language.Romanian: + case Language.Slovak: + return language.ThreeLetterCodeAlt(); + + default: + return language.ThreeLetterCode(); + } + } + + /// + /// Get the Language enum value for a given string + /// + /// String value to convert + /// Language represented by the string, if possible + public static Language? ToLanguage(string lang) + { + var languages = Enum.GetValues(typeof(Language)).Cast().ToList(); + + // Check ISO 639-1 codes + Dictionary languageMapping = languages + .Where(l => l.TwoLetterCode() != null) + .ToDictionary(l => l.TwoLetterCode(), l => l); + + if (languageMapping.ContainsKey(lang)) + return languageMapping[lang]; + + // Check standard ISO 639-2 codes + languageMapping = languages + .Where(l => l.ThreeLetterCode() != null) + .ToDictionary(l => l.ThreeLetterCode(), l => l); + + if (languageMapping.ContainsKey(lang)) + return languageMapping[lang]; + + // Check alternate ISO 639-2 codes + languageMapping = languages + .Where(l => l.ThreeLetterCodeAlt() != null) + .ToDictionary(l => l.ThreeLetterCodeAlt(), l => l); + + if (languageMapping.ContainsKey(lang)) + return languageMapping[lang]; + + return null; + } + + /// + /// Get the ISO 639-2 code for each known language + /// + /// + /// + public static string ThreeLetterCode(this Language? language) => ((LanguageAttribute)AttributeHelper.GetAttribute(language))?.ThreeLetterCode; + + /// + /// Get the ISO 639-2 alternate code for each known language + /// + /// + /// + public static string ThreeLetterCodeAlt(this Language? language) => ((LanguageAttribute)AttributeHelper.GetAttribute(language))?.ThreeLetterCodeAlt; + + /// + /// Get the ISO 639-1 code for each known language + /// + /// + /// + public static string TwoLetterCode(this Language? language) => ((LanguageAttribute)AttributeHelper.GetAttribute(language))?.TwoLetterCode; + + #endregion + + #region Language Selection + + /// + /// Get the string representation of the LanguageSelection enum values + /// + /// LanguageSelection value to convert + /// String representing the value, if possible + public static string LongName(this LanguageSelection? langSelect) => AttributeHelper.GetAttribute(langSelect)?.LongName; + + #endregion + + #region Media Type + + /// + /// List all media types with their short usable names + /// + public static List ListMediaTypes() + { + var mediaTypes = new List(); + + foreach (var val in Enum.GetValues(typeof(MediaType))) + { + if (((MediaType)val) == MediaType.NONE) + continue; + + mediaTypes.Add($"{((MediaType?)val).ShortName()} - {((MediaType?)val).LongName()}"); + } + + return mediaTypes; + } + + /// + /// Get the Redump longnames for each known media type + /// + /// + /// + public static string LongName(this MediaType? mediaType) => AttributeHelper.GetAttribute(mediaType)?.LongName; + + /// + /// Get the Redump shortnames for each known media type + /// + /// + /// + public static string ShortName(this MediaType? mediaType) => AttributeHelper.GetAttribute(mediaType)?.ShortName; + + #endregion + + #region Region + + /// + /// Get the Redump longnames for each known region + /// + /// + /// + public static string LongName(this Region? region) => AttributeHelper.GetAttribute(region)?.LongName; + + /// + /// Get the Redump shortnames for each known region + /// + /// + /// + public static string ShortName(this Region? region) => AttributeHelper.GetAttribute(region)?.ShortName; + + /// + /// Get the Region enum value for a given string + /// + /// String value to convert + /// Region represented by the string, if possible + public static Region? ToRegion(string region) + { + region = region.ToLowerInvariant(); + var regions = Enum.GetValues(typeof(Region)).Cast().ToList(); + + // Check ISO 3166-1 alpha-2 codes + Dictionary regionMapping = regions + .Where(r => r.ShortName() != null) + .ToDictionary(r => r.ShortName().ToLowerInvariant(), r => r); + + if (regionMapping.ContainsKey(region)) + return regionMapping[region]; + + return null; + } + + #endregion + + #region Site Code + + /// + /// Get the HTML version for each known site code + /// + /// + /// + public static string LongName(this SiteCode? siteCode) => AttributeHelper.GetAttribute(siteCode)?.LongName; + + /// + /// Get the short tag for each known site code + /// + /// + /// + public static string ShortName(this SiteCode? siteCode) => AttributeHelper.GetAttribute(siteCode)?.ShortName; + + #endregion + + #region System + + /// + /// Determine if a system is a marker value + /// + /// RedumpSystem value to check + /// True if the system is a marker value, false otherwise + public static bool IsMarker(this RedumpSystem? system) + { + switch (system) + { + case RedumpSystem.MarkerArcadeEnd: + case RedumpSystem.MarkerComputerEnd: + case RedumpSystem.MarkerDiscBasedConsoleEnd: + // case RedumpSystem.MarkerOtherConsoleEnd: + case RedumpSystem.MarkerOtherEnd: + return true; + default: + return false; + } + } + + /// + /// List all systems with their short usable names + /// + public static List ListSystems() + { + var systems = new List(); + + var knownSystems = Enum.GetValues(typeof(RedumpSystem)) + .OfType() + .Where(s => s != null && !s.IsMarker() && s.GetCategory() != SystemCategory.NONE) + .OrderBy(s => s.LongName() ?? string.Empty); + + foreach (var val in knownSystems) + { + systems.Add($"{val.ShortName()} - {val.LongName()}"); + } + + return systems; + } + + /// + /// Get the Redump longnames for each known system + /// + /// + /// + public static string LongName(this RedumpSystem? system) => AttributeHelper.GetAttribute(system)?.LongName; + + /// + /// Get the Redump shortnames for each known system + /// + /// + /// + public static string ShortName(this RedumpSystem? system) => AttributeHelper.GetAttribute(system)?.ShortName; + + /// + /// Determine the category of a system + /// + public static SystemCategory GetCategory(this RedumpSystem? system) => ((SystemAttribute)AttributeHelper.GetAttribute(system))?.Category ?? SystemCategory.NONE; + + /// + /// Determine if a system is available in Redump yet + /// + public static bool IsAvailable(this RedumpSystem? system) => ((SystemAttribute)AttributeHelper.GetAttribute(system))?.Available ?? false; + + /// + /// Determine if a system is restricted to dumpers + /// + public static bool IsBanned(this RedumpSystem? system) => ((SystemAttribute)AttributeHelper.GetAttribute(system))?.IsBanned ?? false; + + /// + /// Determine if a system has a CUE pack + /// + public static bool HasCues(this RedumpSystem? system) => ((SystemAttribute)AttributeHelper.GetAttribute(system))?.HasCues ?? false; + + /// + /// Determine if a system has a DAT + /// + public static bool HasDat(this RedumpSystem? system) => ((SystemAttribute)AttributeHelper.GetAttribute(system))?.HasDat ?? false; + + /// + /// Determine if a system has a decrypted keys pack + /// + public static bool HasDkeys(this RedumpSystem? system) => ((SystemAttribute)AttributeHelper.GetAttribute(system))?.HasDkeys ?? false; + + /// + /// Determine if a system has a GDI pack + /// + public static bool HasGdi(this RedumpSystem? system) => ((SystemAttribute)AttributeHelper.GetAttribute(system))?.HasGdi ?? false; + + /// + /// Determine if a system has a keys pack + /// + public static bool HasKeys(this RedumpSystem? system) => ((SystemAttribute)AttributeHelper.GetAttribute(system))?.HasKeys ?? false; + + /// + /// Determine if a system has an LSD pack + /// + public static bool HasLsd(this RedumpSystem? system) => ((SystemAttribute)AttributeHelper.GetAttribute(system))?.HasLsd ?? false; + + /// + /// Determine if a system has an SBI pack + /// + public static bool HasSbi(this RedumpSystem? system) => ((SystemAttribute)AttributeHelper.GetAttribute(system))?.HasSbi ?? false; + + /// + /// Get the RedumpSystem enum value for a given string + /// + /// String value to convert + /// RedumpSystem represented by the string, if possible + public static RedumpSystem? ToRedumpSystem(string system) + { + switch (system?.ToLowerInvariant()) + { + #region BIOS Sets + + case "xboxbios": + case "xbox bios": + case "xbox-bios": + case "microsoftxboxbios": + case "microsoftxbox bios": + case "microsoft xbox bios": + return RedumpSystem.MicrosoftXboxBIOS; + case "gcbios": + case "gc bios": + case "gc-bios": + case "gamecubebios": + case "ngcbios": + case "ngc bios": + case "nintendogamecubebios": + case "nintendo gamecube bios": + return RedumpSystem.NintendoGameCubeBIOS; + case "ps1bios": + case "ps1 bios": + case "psxbios": + case "psx bios": + case "psx-bios": + case "playstationbios": + case "playstation bios": + case "sonyps1bios": + case "sonyps1 bios": + case "sony ps1 bios": + case "sonypsxbios": + case "sonypsx bios": + case "sony psx bios": + case "sonyplaystationbios": + case "sonyplaystation bios": + case "sony playstation bios": + return RedumpSystem.SonyPlayStationBIOS; + case "ps2bios": + case "ps2 bios": + case "ps2-bios": + case "playstation2bios": + case "playstation2 bios": + case "playstation 2 bios": + case "sonyps2bios": + case "sonyps2 bios": + case "sony ps2 bios": + case "sonyplaystation2bios": + case "sonyplaystation2 bios": + case "sony playstation 2 bios": + return RedumpSystem.SonyPlayStation2BIOS; + + #endregion + + #region Consoles + + case "ajcd": + case "jaguar": + case "jagcd": + case "jaguarcd": + case "jaguar cd": + case "atarijaguar": + case "atarijagcd": + case "atarijaguarcd": + case "atari jaguar cd": + return RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem; + case "qis": + case "playdia": + case "playdiaqis": + case "playdiaquickinteractivesystem": + case "bandaiplaydia": + case "bandaiplaydiaquickinteractivesystem": + case "bandai playdia quick interactive system": + return RedumpSystem.BandaiPlaydiaQuickInteractiveSystem; + case "pippin": + case "bandaipippin": + case "bandai pippin": + case "applepippin": + case "apple pippin": + case "bandaiapplepippin": + case "bandai apple pippin": + case "bandai / apple pippin": + return RedumpSystem.BandaiPippin; + case "cd32": + case "amigacd32": + case "amiga cd32": + case "commodoreamigacd32": + case "commodore amiga cd32": + return RedumpSystem.CommodoreAmigaCD32; + case "cdtv": + case "amigacdtv": + case "amiga cdtv": + case "commodoreamigacdtv": + case "commodore amiga cdtv": + return RedumpSystem.CommodoreAmigaCDTV; + case "evosc": + case "evo sc": + case "evosmartconsole": + case "evo smart console": + case "envizionsevosc": + case "envizion evo sc": + case "envizionevosmartconsole": + case "envizion evo smart console": + return RedumpSystem.EnvizionsEVOSmartConsole; + case "fmtm": + case "fmtownsmarty": + case "fm towns marty": + case "fujitsufmtownsmarty": + case "fujitsu fm towns marty": + return RedumpSystem.FujitsuFMTownsMarty; + case "ion": + case "ionegs": + case "hasbroion": + case "hasbroioneducationalgamingsystem": + case "hasbro ion educational gaming system": + return RedumpSystem.HasbroiONEducationalGamingSystem; + case "hvn": + case "videonow": + case "hasbrovideonow": + case "hasbro videonow": + return RedumpSystem.HasbroVideoNow; + case "hvnc": + case "videonowcolor": + case "videonow color": + case "hasbrovideonowcolor": + case "hasbro videonow color": + return RedumpSystem.HasbroVideoNowColor; + case "hvnjr": + case "videonowjr": + case "videonow jr": + case "hasbrovideonowjr": + case "hasbro videonow jr": + return RedumpSystem.HasbroVideoNowJr; + case "xvnxp": + case "videonowxp": + case "videonow xp": + case "hasbrovideonowxp": + case "hasbro videonow xp": + return RedumpSystem.HasbroVideoNowXP; + case "ixl": + case "mattelixl": + case "mattel ixl": + case "fisherpriceixl": + case "fisher price ixl": + case "fisher-price ixl": + case "fisherprice ixl": + case "mattelfisherpriceixl": + case "mattel fisher price ixl": + case "mattelfisherprice ixl": + case "mattel fisherprice ixl": + case "mattel fisher-price ixl": + return RedumpSystem.MattelFisherPriceiXL; + case "hs": + case "hyperscan": + case "mattelhyperscan": + case "mattel hyperscan": + return RedumpSystem.MattelHyperScan; + case "xbox": + case "microsoftxbox": + case "microsoft xbox": + return RedumpSystem.MicrosoftXbox; + case "x360": + case "xbox360": + case "microsoftx360": + case "microsoftxbox360": + case "microsoft x360": + case "microsoft xbox 360": + return RedumpSystem.MicrosoftXbox360; + case "xb1": + case "xbone": + case "xboxone": + case "microsoftxbone": + case "microsoftxboxone": + case "microsoft xbone": + case "microsoft xbox one": + return RedumpSystem.MicrosoftXboxOne; + case "xbs": + case "xboxsx": + case "xbseries": + case "xbseriess": + case "xbseriesx": + case "xbseriessx": + case "xboxseries": + case "xboxseriess": + case "xboxseriesx": + case "xboxseriesxs": + case "microsoftxboxseries": + case "microsoftxboxseriess": + case "microsoftxboxseriesx": + case "microsoftxboxseriesxs": + case "microsoft xbox series": + case "microsoft xbox series s": + case "microsoft xbox series x": + case "microsoft xbox series x and s": + return RedumpSystem.MicrosoftXboxSeriesXS; + case "pce": + case "pcecd": + case "pce-cd": + case "tgcd": + case "tg-cd": + case "necpcecd": + case "nectgcd": + case "nec pc-engine cd": + case "nec turbografx cd": + case "nec pc-engine / turbografx cd": + return RedumpSystem.NECPCEngineCDTurboGrafxCD; + case "pcfx": + case "pc-fx": + case "pcfxga": + case "pc-fxga": + case "necpcfx": + case "necpcfxga": + case "nec pc-fx": + case "nec pc-fxga": + case "nec pc-fx / pc-fxga": + return RedumpSystem.NECPCFXPCFXGA; + case "gc": + case "gamecube": + case "ngc": + case "nintendogamecube": + case "nintendo gamecube": + return RedumpSystem.NintendoGameCube; + case "snescd": + case "snes cd": + case "snes-cd": + case "supernescd": + case "super nes cd": + case "super nes-cd": + case "supernintendocd": + case "super nintendo cd": + case "super nintendo-cd": + case "nintendosnescd": + case "nintendo snes cd": + case "nintendosnes-cd": + case "nintendosupernescd": + case "nintendo super nes cd": + case "nintendo super nes-cd": + case "nintendosupernintendocd": + case "nintendo super nintendo cd": + case "nintendo super nintendo-cd": + case "sonysnescd": + case "sony snes cd": + case "sonysnes-cd": + case "sonysupernescd": + case "sony super nes cd": + case "sony super nes-cd": + case "sonysupernintendocd": + case "sony super nintendo cd": + case "sony super nintendo-cd": + return RedumpSystem.NintendoSonySuperNESCDROMSystem; + case "wii": + case "nintendowii": + case "nintendo wii": + return RedumpSystem.NintendoWii; + case "wiiu": + case "wii u": + case "nintendowiiu": + case "nintendo wii u": + return RedumpSystem.NintendoWiiU; + case "3do": + case "3do interactive multiplayer": + case "panasonic3do": + case "panasonic 3do": + case "panasonic 3do interactive multiplayer": + return RedumpSystem.Panasonic3DOInteractiveMultiplayer; + case "cdi": + case "cd-i": + case "philipscdi": + case "philips cdi": + case "philips cd-i": + return RedumpSystem.PhilipsCDi; + case "laseractive": + case "pioneerlaseractive": + case "pioneer laseractive": + return RedumpSystem.PioneerLaserActive; + case "scd": + case "mcd": + case "smcd": + case "segacd": + case "megacd": + case "segamegacd": + case "sega cd": + case "mega cd": + case "sega cd / mega cd": + return RedumpSystem.SegaMegaCDSegaCD; + case "dc": + case "sdc": + case "dreamcast": + case "segadreamcast": + case "sega dreamcast": + return RedumpSystem.SegaDreamcast; + case "ss": + case "saturn": + case "segasaturn": + case "sega saturn": + return RedumpSystem.SegaSaturn; + case "ngcd": + case "neogeocd": + case "neogeo cd": + case "neo geo cd": + case "snk ngcd": + case "snk neogeo cd": + case "snk neo geo cd": + return RedumpSystem.SNKNeoGeoCD; + case "ps1": + case "psx": + case "playstation": + case "sonyps1": + case "sony ps1": + case "sonypsx": + case "sony psx": + case "sonyplaystation": + case "sony playstation": + return RedumpSystem.SonyPlayStation; + case "ps2": + case "playstation2": + case "playstation 2": + case "sonyps2": + case "sony ps2": + case "sonyplaystation2": + case "sony playstation 2": + return RedumpSystem.SonyPlayStation2; + case "ps3": + case "playstation3": + case "playstation 3": + case "sonyps3": + case "sony ps3": + case "sonyplaystation3": + case "sony playstation 3": + return RedumpSystem.SonyPlayStation3; + case "ps4": + case "playstation4": + case "playstation 4": + case "sonyps4": + case "sony ps4": + case "sonyplaystation4": + case "sony playstation 4": + return RedumpSystem.SonyPlayStation4; + case "ps5": + case "playstation5": + case "playstation 5": + case "sonyps5": + case "sony ps5": + case "sonyplaystation5": + case "sony playstation 5": + return RedumpSystem.SonyPlayStation5; + case "psp": + case "playstationportable": + case "playstation portable": + case "sonypsp": + case "sony psp": + case "sonyplaystationportable": + case "sony playstation portable": + return RedumpSystem.SonyPlayStationPortable; + case "vis": + case "tandyvis": + case "tandy vis": + case "tandyvisualinformationsystem": + case "tandy visual information system": + case "memorexvis": + case "memorex vis": + case "memorexvisualinformationsystem": + case "memorex visual information sytem": + case "tandy / memorex visual information system": + return RedumpSystem.MemorexVisualInformationSystem; + case "nuon": + case "vmlabsnuon": + case "vm labs nuon": + return RedumpSystem.VMLabsNUON; + case "vflash": + case "vsmile": + case "vsmilepro": + case "vsmile pro": + case "v.flash": + case "v.smile": + case "v.smilepro": + case "v.smile pro": + case "vtechvflash": + case "vtech vflash": + case "vtech v.flash": + case "vtechvsmile": + case "vtech vsmile": + case "vtech v.smile": + case "vtechvsmilepro": + case "vtech vsmile pro": + case "vtech v.smile pro": + case "vtech v.flash - v.smile pro": + return RedumpSystem.VTechVFlashVSmilePro; + case "gamewave": + case "game wave": + case "zapit": + case "zapitgamewave": + case "zapit game wave": + case "zapit games game wave family entertainment system": + return RedumpSystem.ZAPiTGamesGameWaveFamilyEntertainmentSystem; + + #endregion + + #region Computers + + case "acorn": + case "archcd": + case "archimedes": + case "acornarchimedes": + case "acorn archimedes": + return RedumpSystem.AcornArchimedes; + case "apple": + case "mac": + case "applemac": + case "macintosh": + case "applemacintosh": + case "apple mac": + case "apple macintosh": + return RedumpSystem.AppleMacintosh; + case "acd": + case "amiga": + case "commodoreamiga": + case "commodore amiga": + return RedumpSystem.CommodoreAmigaCD; + case "fmtowns": + case "fmt": + case "fm towns": + case "fujitsufmtowns": + case "fujitsu fm towns": + case "fujitsu fm towns series": + return RedumpSystem.FujitsuFMTownsseries; + case "ibm": + case "ibmpc": + case "pc": + case "ibm pc": + case "ibm pc compatible": + return RedumpSystem.IBMPCcompatible; + case "pc88": + case "pc-88": + case "necpc88": + case "nec pc88": + case "nec pc-88": + return RedumpSystem.NECPC88series; + case "pc98": + case "pc-98": + case "necpc98": + case "nec pc98": + case "nec pc-98": + return RedumpSystem.NECPC98series; + case "x68k": + case "x68kcd": + case "x68000": + case "sharpx68k": + case "sharp x68k": + case "sharpx68000": + case "sharp x68000": + return RedumpSystem.SharpX68000; + + #endregion + + #region Arcade + + case "cubo": + case "cubocd32": + case "cubo cd32": + case "amigacubocd32": + case "amiga cubo cd32": + return RedumpSystem.AmigaCUBOCD32; + case "alg3do": + case "alg 3do": + case "americanlasergames3do": + case "american laser games 3do": + return RedumpSystem.AmericanLaserGames3DO; + case "atari3do": + case "atari 3do": + return RedumpSystem.Atari3DO; + case "atronic": + return RedumpSystem.Atronic; + case "auscom": + case "auscomsystem1": + case "auscom system 1": + return RedumpSystem.AUSCOMSystem1; + case "gamemagic": + case "game magic": + case "ballygamemagic": + case "bally game magic": + return RedumpSystem.BallyGameMagic; + case "cps3": + case "cpsiii": + case "cps 3": + case "cp system 3": + case "cp system iii": + case "capcomcps3": + case "capcomcpsiii": + case "capcom cps 3": + case "capcom cps iii": + case "capcom cp system 3": + case "capcom cp system iii": + return RedumpSystem.CapcomCPSystemIII; + case "fpp": + case "funworldphotoplay": + case "funworld photoplay": + case "funworld photo play": + return RedumpSystem.funworldPhotoPlay; + case "globalvr": + case "global vr": + case "global vr pc-based systems": + return RedumpSystem.GlobalVRVarious; + case "vortek": + case "globalvrvortek": + case "global vr vortek": + return RedumpSystem.GlobalVRVortek; + case "vortekv3": + case "vortek v3": + case "globalvrvortekv3": + case "global vr vortek v3": + return RedumpSystem.GlobalVRVortekV3; + case "ice": + case "icepc": + case "ice pc": + case "ice pc-based hardware": + return RedumpSystem.ICEPCHardware; + case "ite": + case "iteagle": + case "eagle": + case "incredible technologies eagle": + return RedumpSystem.IncredibleTechnologiesEagle; + case "itpc": + case "incredible technologies pc-based systems": + return RedumpSystem.IncredibleTechnologiesVarious; + case "jvlitouch": + case "jvl itouch": + return RedumpSystem.JVLiTouch; + case "kea": + case "eamusement": + case "e-amusement": + case "konamieamusement": + case "konami eamusement": + case "konamie-amusement": + case "konami e-amusement": + return RedumpSystem.KonamieAmusement; + case "kfb": + case "firebeat": + case "konamifirebeat": + case "konami firebeat": + return RedumpSystem.KonamiFireBeat; + case "ksgv": + case "gvsystem": + case "gv system": + case "konamigvsystem": + case "konami gv system": + case "systemgv": + case "system gv": + case "konamisystemgv": + case "konami system gv": + return RedumpSystem.KonamiSystemGV; + case "km2": + case "konamim2": + case "konami m2": + return RedumpSystem.KonamiM2; + case "python": + case "konamipython": + case "konami python": + return RedumpSystem.KonamiPython; + case "python2": + case "python 2": + case "konamipython2": + case "konami python 2": + return RedumpSystem.KonamiPython2; + case "ks573": + case "system573": + case "system 573": + case "konamisystem573": + case "konami system 573": + return RedumpSystem.KonamiSystem573; + case "kt": + case "twinkle": + case "konamitwinkle": + case "konami twinkle": + return RedumpSystem.KonamiTwinkle; + case "konamipc": + case "konami pc": + case "konami pc-based systems": + return RedumpSystem.KonamiVarious; + case "boardwalk": + case "meritindustriesboardwalk": + case "merit industries boardwalk": + return RedumpSystem.MeritIndustriesBoardwalk; + case "megatouchforce": + case "megatouch force": + case "meritindustriesmegatouchforce": + case "merit industries megatouch force": + return RedumpSystem.MeritIndustriesMegaTouchForce; + case "megatouchion": + case "megatouch ion": + case "meritindustriesmegatouchion": + case "merit industries megatouch ion": + return RedumpSystem.MeritIndustriesMegaTouchION; + case "megatouchmaxx": + case "megatouch maxx": + case "meritindustriesmegatouchmaxx": + case "merit industries megatouch maxx": + return RedumpSystem.MeritIndustriesMegaTouchMaxx; + case "megatouchxl": + case "megatouch xl": + case "meritindustriesmegatouchxl": + case "merit industries megatouch xl": + return RedumpSystem.MeritIndustriesMegaTouchXL; + case "ns246": + case "system246": + case "system 246": + case "namcosystem246": + case "namco system 246": + case "capcomsystem246": + case "capcom system 246": + case "taitosystem246": + case "taito system 246": + case "namco / capcom / taito system 246": + case "system256": + case "system 256": + case "supersystem256": + case "super system 256": + case "namcosystem256": + case "namco system 256": + case "namcosupersystem256": + case "namco super system 256": + case "capcomsystem256": + case "capcom system 256": + case "capcomsupersystem256": + case "capcom super system 256": + case "namco / capcom system 256/super system 256": + return RedumpSystem.NamcoSystem246256; + case "triforce": + case "namcotriforce": + case "namco triforce": + case "segatriforce": + case "sega triforce": + case "nintendotriforce": + case "nintendo triforce": + case "namco / sega / nintendo triforce": + return RedumpSystem.NamcoSegaNintendoTriforce; + case "ns12": + case "system12": + case "system 12": + case "namcosystem12": + case "namco system 12": + return RedumpSystem.NamcoSystem12; + case "newjatrecdi": + case "new jatre cdi": + case "new jatre cd-i": + return RedumpSystem.NewJatreCDi; + case "hrs": + case "highratesytem": + case "high rate system": + case "nichibutsuhrs": + case "nichibutsu hrs": + case "nichibutsu high rate system": + return RedumpSystem.NichibutsuHighRateSystem; + case "supercd": + case "super cd": + case "nichibutsuscd": + case "nichibutsu scd": + case "nichibutsusupercd": + case "nichibutsu supercd": + case "nichibutsu super cd": + return RedumpSystem.NichibutsuSuperCD; + case "xrs": + case "xratesystem": + case "x-rate system": + case "nichibutsuxrs": + case "nichibutsu xrs": + case "nichibutsu x-rate system": + return RedumpSystem.NichibutsuXRateSystem; + case "m2": + case "panasonicm2": + case "panasonic m2": + return RedumpSystem.PanasonicM2; + case "photoplay": + case "photoplaypc": + case "photoplay pc": + case "photoplay pc-based systems": + return RedumpSystem.PhotoPlayVarious; + case "rawthrills": + case "raw thrills": + case "raw thrills pc-based systems": + return RedumpSystem.RawThrillsVarious; + case "alls": + case "segaalls": + case "sega alls": + return RedumpSystem.SegaALLS; + case "chihiro": + case "segachihiro": + case "sega chihiro": + return RedumpSystem.SegaChihiro; + case "europar": + case "europa-r": + case "segaeuropar": + case "sega europar": + case "sega europa-r": + return RedumpSystem.SegaEuropaR; + case "lindbergh": + case "segalindbergh": + case "sega lindbergh": + return RedumpSystem.SegaLindbergh; + case "naomi": + case "seganaomi": + case "sega naomi": + return RedumpSystem.SegaNaomi; + case "naomi2": + case "naomi 2": + case "seganaomi2": + case "sega naomi 2": + return RedumpSystem.SegaNaomi2; + case "nu": + case "seganu": + case "sega nu": + return RedumpSystem.SegaNu; + case "sre": + case "ringedge": + case "segaringedge": + case "sega ringedge": + return RedumpSystem.SegaRingEdge; + case "sre2": + case "ringedge2": + case "ringedge 2": + case "segaringedge2": + case "sega ringedge 2": + return RedumpSystem.SegaRingEdge2; + case "ringwide": + case "segaringwide": + case "sega ringwide": + return RedumpSystem.SegaRingWide; + case "stv": + case "titanvideo": + case "titan video": + case "segatitanvideo": + case "sega titan video": + return RedumpSystem.SegaTitanVideo; + case "system32": + case "system 32": + case "segasystem32": + case "sega system 32": + return RedumpSystem.SegaSystem32; + case "cats": + case "seibucats": + case "seibu cats": + case "seibu cats system": + return RedumpSystem.SeibuCATSSystem; + case "quizard": + case "tabaustriaquizard": + case "tab-austria quizard": + return RedumpSystem.TABAustriaQuizard; + case "tsumo": + case "tsunamitsumo": + case "tsunami tsumo": + case "tsunami tsumo multi-game motion system": + return RedumpSystem.TsunamiTsuMoMultiGameMotionSystem; + case "ultracade": + case "ultracadepc": + case "ultracade pc": + case "ultracade pc-based systems": + return RedumpSystem.UltraCade; + + #endregion + + #region Others + + case "audio": + case "audiocd": + case "audio cd": + case "audio-cd": + return RedumpSystem.AudioCD; + case "bdvideo": + case "bd-video": + case "blurayvideo": + case "bluray video": + return RedumpSystem.BDVideo; + case "dvda": + case "dvdaudio": + case "dvd-audio": + return RedumpSystem.DVDAudio; + case "dvd": + case "dvdv": + case "dvdvideo": + case "dvd-video": + return RedumpSystem.DVDVideo; + case "enhancedcd": + case "enhanced cd": + case "enhanced-cd": + case "enhancedcdrom": + case "enhanced cdrom": + case "enhanced cd-rom": + return RedumpSystem.EnhancedCD; + case "hddvd": + case "hddvdv": + case "hddvdvideo": + case "hddvd-video": + case "hd-dvd-video": + return RedumpSystem.HDDVDVideo; + case "navi21": + case "naviken": + case "naviken21": + case "naviken 2.1": + case "navisoftnaviken": + case "navisoft naviken": + case "navisoftnaviken21": + case "navisoft naviken 2.1": + return RedumpSystem.NavisoftNaviken21; + case "palm": + case "palmos": + return RedumpSystem.PalmOS; + case "photo": + case "photocd": + case "photo cd": + case "photo-cd": + return RedumpSystem.PhotoCD; + case "psxgs": + case "gameshark": + case "psgameshark": + case "ps gameshark": + case "playstationgameshark": + case "playstation gameshark": + case "playstation gameshark updates": + return RedumpSystem.PlayStationGameSharkUpdates; + case "pocketpc": + case "pocket pc": + case "ppc": + return RedumpSystem.PocketPC; + case "rainbow": + case "rainbowdisc": + case "rainbow disc": + return RedumpSystem.RainbowDisc; + case "sp21": + case "pl21": + case "prologue21": + case "prologue 21": + case "segaprologue21": + case "sega prologue21": + case "sega prologue 21": + return RedumpSystem.SegaPrologue21MultimediaKaraokeSystem; + case "electronicbook": + case "sonyelectronicbook": + case "sony electronic book": + return RedumpSystem.SonyElectronicBook; + case "sacd": + case "superaudiocd": + case "super audio cd": + return RedumpSystem.SuperAudioCD; + case "iktv": + case "taoiktv": + case "tao iktv": + return RedumpSystem.TaoiKTV; + case "ksite": + case "kisssite": + case "kiss-site": + case "tomykisssite": + case "tomy kisssite": + case "tomy kiss-site": + return RedumpSystem.TomyKissSite; + case "vcd": + case "videocd": + case "video cd": + return RedumpSystem.VideoCD; + + #endregion + + default: + return null; + } + } + + #endregion + + #region System Category + + /// + /// Get the string representation of the system category + /// + /// + /// + public static string LongName(this SystemCategory? category) => AttributeHelper.GetAttribute(category)?.LongName; + + #endregion + + #region Yes/No + + /// + /// Get the string representation of the YesNo value + /// + /// + /// + public static string LongName(this YesNo? yesno) => AttributeHelper.GetAttribute(yesno)?.LongName ?? "Yes/No"; + + /// + /// Get the YesNo enum value for a given nullable boolean + /// + /// Nullable boolean value to convert + /// YesNo represented by the nullable boolean, if possible + public static YesNo? ToYesNo(this bool? yesno) + { + switch (yesno) + { + case false: + return YesNo.No; + case true: + return YesNo.Yes; + default: + return YesNo.NULL; + } + } + + /// + /// Get the YesNo enum value for a given string + /// + /// String value to convert + /// YesNo represented by the string, if possible + public static YesNo? ToYesNo(string yesno) + { + switch (yesno?.ToLowerInvariant()) + { + case "no": + return YesNo.No; + case "yes": + return YesNo.Yes; + default: + return YesNo.NULL; + } + } + + #endregion + } +} diff --git a/Data/SubmissionInfo.cs b/Data/SubmissionInfo.cs new file mode 100644 index 0000000..5cf03f4 --- /dev/null +++ b/Data/SubmissionInfo.cs @@ -0,0 +1,570 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using RedumpLib.Converters; + +namespace SabreTools.RedumpLib.Data +{ + public class SubmissionInfo : ICloneable + { + /// + /// Version of the current schema + /// + [JsonProperty(PropertyName = "schema_version", DefaultValueHandling = DefaultValueHandling.Ignore)] + public int SchemaVersion { get; set; } = 3; + + /// + /// Fully matched Redump ID + /// + [JsonIgnore] + public int? FullyMatchedID { get; set; } + + /// + /// List of partially matched Redump IDs + /// + [JsonIgnore] + public List PartiallyMatchedIDs { get; set; } + + /// + /// DateTime of when the disc was added + /// + [JsonIgnore] + public DateTime? Added { get; set; } + + /// + /// DateTime of when the disc was last modified + /// + [JsonIgnore] + public DateTime? LastModified { get; set; } + + [JsonProperty(PropertyName = "common_disc_info", DefaultValueHandling = DefaultValueHandling.Ignore)] + public CommonDiscInfoSection CommonDiscInfo { get; set; } = new CommonDiscInfoSection(); + + [JsonProperty(PropertyName = "versions_and_editions", DefaultValueHandling = DefaultValueHandling.Ignore)] + public VersionAndEditionsSection VersionAndEditions { get; set; } = new VersionAndEditionsSection(); + + [JsonProperty(PropertyName = "edc", DefaultValueHandling = DefaultValueHandling.Ignore)] + public EDCSection EDC { get; set; } = new EDCSection(); + + [JsonProperty(PropertyName = "parent_clone_relationship", DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParentCloneRelationshipSection ParentCloneRelationship { get; set; } = new ParentCloneRelationshipSection(); + + [JsonProperty(PropertyName = "extras", DefaultValueHandling = DefaultValueHandling.Ignore)] + public ExtrasSection Extras { get; set; } = new ExtrasSection(); + + [JsonProperty(PropertyName = "copy_protection", DefaultValueHandling = DefaultValueHandling.Ignore)] + public CopyProtectionSection CopyProtection { get; set; } = new CopyProtectionSection(); + + [JsonProperty(PropertyName = "dumpers_and_status", DefaultValueHandling = DefaultValueHandling.Ignore)] + public DumpersAndStatusSection DumpersAndStatus { get; set; } = new DumpersAndStatusSection(); + + [JsonProperty(PropertyName = "tracks_and_write_offsets", DefaultValueHandling = DefaultValueHandling.Ignore)] + public TracksAndWriteOffsetsSection TracksAndWriteOffsets { get; set; } = new TracksAndWriteOffsetsSection(); + + [JsonProperty(PropertyName = "size_and_checksums", DefaultValueHandling = DefaultValueHandling.Ignore)] + public SizeAndChecksumsSection SizeAndChecksums { get; set; } = new SizeAndChecksumsSection(); + + [JsonProperty(PropertyName = "dumping_info", DefaultValueHandling = DefaultValueHandling.Ignore)] + public DumpingInfoSection DumpingInfo { get; set; } = new DumpingInfoSection(); + + [JsonProperty(PropertyName = "artifacts", DefaultValueHandling = DefaultValueHandling.Ignore)] + public Dictionary Artifacts { get; set; } = new Dictionary(); + + public object Clone() + { + return new SubmissionInfo + { + SchemaVersion = this.SchemaVersion, + FullyMatchedID = this.FullyMatchedID, + PartiallyMatchedIDs = this.PartiallyMatchedIDs, + Added = this.Added, + LastModified = this.LastModified, + CommonDiscInfo = this.CommonDiscInfo?.Clone() as CommonDiscInfoSection, + VersionAndEditions = this.VersionAndEditions?.Clone() as VersionAndEditionsSection, + EDC = this.EDC?.Clone() as EDCSection, + ParentCloneRelationship = this.ParentCloneRelationship?.Clone() as ParentCloneRelationshipSection, + Extras = this.Extras?.Clone() as ExtrasSection, + CopyProtection = this.CopyProtection?.Clone() as CopyProtectionSection, + DumpersAndStatus = this.DumpersAndStatus?.Clone() as DumpersAndStatusSection, + TracksAndWriteOffsets = this.TracksAndWriteOffsets?.Clone() as TracksAndWriteOffsetsSection, + SizeAndChecksums = this.SizeAndChecksums?.Clone() as SizeAndChecksumsSection, + DumpingInfo = this.DumpingInfo?.Clone() as DumpingInfoSection, + Artifacts = this.Artifacts?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), + }; + } + } + + /// + /// Common disc info section of New Disc Form + /// + public class CommonDiscInfoSection : ICloneable + { + // Name not defined by Redump + [JsonProperty(PropertyName = "d_system", Required = Required.AllowNull)] + [JsonConverter(typeof(SystemConverter))] + public RedumpSystem? System { get; set; } + + // Name not defined by Redump + [JsonProperty(PropertyName = "d_media", Required = Required.AllowNull)] + [JsonConverter(typeof(DiscTypeConverter))] + public DiscType? Media { get; set; } + + [JsonProperty(PropertyName = "d_title", Required = Required.AllowNull)] + public string Title { get; set; } + + [JsonProperty(PropertyName = "d_title_foreign", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string ForeignTitleNonLatin { get; set; } + + [JsonProperty(PropertyName = "d_number", NullValueHandling = NullValueHandling.Ignore)] + public string DiscNumberLetter { get; set; } + + [JsonProperty(PropertyName = "d_label", NullValueHandling = NullValueHandling.Ignore)] + public string DiscTitle { get; set; } + + [JsonProperty(PropertyName = "d_category", Required = Required.AllowNull)] + [JsonConverter(typeof(DiscCategoryConverter))] + public DiscCategory? Category { get; set; } + + [JsonProperty(PropertyName = "d_region", Required = Required.AllowNull)] + [JsonConverter(typeof(RegionConverter))] + public Region? Region { get; set; } + + [JsonProperty(PropertyName = "d_languages", Required = Required.AllowNull)] + [JsonConverter(typeof(LanguageConverter))] + public Language?[] Languages { get; set; } + + [JsonProperty(PropertyName = "d_languages_selection", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonConverter(typeof(LanguageSelectionConverter))] + public LanguageSelection?[] LanguageSelection { get; set; } + + [JsonProperty(PropertyName = "d_serial", NullValueHandling = NullValueHandling.Ignore)] + public string Serial { get; set; } + + [JsonProperty(PropertyName = "d_ring", NullValueHandling = NullValueHandling.Ignore)] + public string Ring { get; private set; } + + [JsonProperty(PropertyName = "d_ring_0_id", NullValueHandling = NullValueHandling.Ignore)] + public string RingId { get; private set; } + + [JsonProperty(PropertyName = "d_ring_0_ma1", Required = Required.AllowNull)] + public string Layer0MasteringRing { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_ma1_sid", NullValueHandling = NullValueHandling.Ignore)] + public string Layer0MasteringSID { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_ts1", NullValueHandling = NullValueHandling.Ignore)] + public string Layer0ToolstampMasteringCode { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_mo1_sid", NullValueHandling = NullValueHandling.Ignore)] + public string Layer0MouldSID { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_mo1", NullValueHandling = NullValueHandling.Ignore)] + public string Layer0AdditionalMould { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_ma2", Required = Required.AllowNull)] + public string Layer1MasteringRing { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_ma2_sid", NullValueHandling = NullValueHandling.Ignore)] + public string Layer1MasteringSID { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_ts2", NullValueHandling = NullValueHandling.Ignore)] + public string Layer1ToolstampMasteringCode { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_mo2_sid", NullValueHandling = NullValueHandling.Ignore)] + public string Layer1MouldSID { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_mo2", NullValueHandling = NullValueHandling.Ignore)] + public string Layer1AdditionalMould { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_ma3", Required = Required.AllowNull)] + public string Layer2MasteringRing { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_ma3_sid", NullValueHandling = NullValueHandling.Ignore)] + public string Layer2MasteringSID { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_ts3", NullValueHandling = NullValueHandling.Ignore)] + public string Layer2ToolstampMasteringCode { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_ma4", Required = Required.AllowNull)] + public string Layer3MasteringRing { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_ma4_sid", NullValueHandling = NullValueHandling.Ignore)] + public string Layer3MasteringSID { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_ts4", NullValueHandling = NullValueHandling.Ignore)] + public string Layer3ToolstampMasteringCode { get; set; } + + [JsonProperty(PropertyName = "d_ring_0_offsets", NullValueHandling = NullValueHandling.Ignore)] + public string RingOffsetsHidden { get { return "1"; } } + + [JsonProperty(PropertyName = "d_ring_0_0_id", NullValueHandling = NullValueHandling.Ignore)] + public string RingZeroId { get; private set; } + + [JsonProperty(PropertyName = "d_ring_0_0_density", NullValueHandling = NullValueHandling.Ignore)] + public string RingZeroDensity { get; private set; } + + [JsonProperty(PropertyName = "d_ring_0_0_value", NullValueHandling = NullValueHandling.Ignore)] + public string RingWriteOffset { get; set; } + + [JsonProperty(PropertyName = "d_ring_count", NullValueHandling = NullValueHandling.Ignore)] + public string RingCount { get { return "1"; } } + + [JsonProperty(PropertyName = "d_barcode", NullValueHandling = NullValueHandling.Ignore)] + public string Barcode { get; set; } + + [JsonProperty(PropertyName = "d_date", NullValueHandling = NullValueHandling.Ignore)] + public string EXEDateBuildDate { get; set; } + + [JsonProperty(PropertyName = "d_errors", NullValueHandling = NullValueHandling.Ignore)] + public string ErrorsCount { get; set; } + + [JsonProperty(PropertyName = "d_comments", NullValueHandling = NullValueHandling.Ignore)] + public string Comments { get; set; } + + [JsonIgnore] + public Dictionary CommentsSpecialFields { get; set; } + + [JsonProperty(PropertyName = "d_contents", NullValueHandling = NullValueHandling.Ignore)] + public string Contents { get; set; } + + [JsonIgnore] + public Dictionary ContentsSpecialFields { get; set; } + + public object Clone() + { + return new CommonDiscInfoSection + { + System = this.System, + Media = this.Media, + Title = this.Title, + ForeignTitleNonLatin = this.ForeignTitleNonLatin, + DiscNumberLetter = this.DiscNumberLetter, + DiscTitle = this.DiscTitle, + Category = this.Category, + Region = this.Region, + Languages = this.Languages?.Clone() as Language?[], + LanguageSelection = this.LanguageSelection?.Clone() as LanguageSelection?[], + Serial = this.Serial, + Ring = this.Ring, + RingId = this.RingId, + Layer0MasteringRing = this.Layer0MasteringRing, + Layer0MasteringSID = this.Layer0MasteringSID, + Layer0ToolstampMasteringCode = this.Layer0ToolstampMasteringCode, + Layer0MouldSID = this.Layer0MouldSID, + Layer0AdditionalMould = this.Layer0AdditionalMould, + Layer1MasteringRing = this.Layer1MasteringRing, + Layer1MasteringSID = this.Layer1MasteringSID, + Layer1ToolstampMasteringCode = this.Layer1ToolstampMasteringCode, + Layer1MouldSID = this.Layer1MouldSID, + Layer1AdditionalMould = this.Layer1AdditionalMould, + Layer2MasteringRing = this.Layer2MasteringRing, + Layer2MasteringSID = this.Layer2MasteringSID, + Layer2ToolstampMasteringCode = this.Layer2ToolstampMasteringCode, + Layer3MasteringRing = this.Layer3MasteringRing, + Layer3MasteringSID = this.Layer3MasteringSID, + Layer3ToolstampMasteringCode = this.Layer3ToolstampMasteringCode, + RingZeroId = this.RingZeroId, + RingZeroDensity = this.RingZeroDensity, + RingWriteOffset = this.RingWriteOffset, + Barcode = this.Barcode, + EXEDateBuildDate = this.EXEDateBuildDate, + ErrorsCount = this.ErrorsCount, + Comments = this.Comments, + CommentsSpecialFields = this.CommentsSpecialFields?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), + Contents = this.Contents, + ContentsSpecialFields = this.ContentsSpecialFields?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), + }; + } + } + + /// + /// Version and editions section of New Disc form + /// + public class VersionAndEditionsSection : ICloneable + { + [JsonProperty(PropertyName = "d_version", NullValueHandling = NullValueHandling.Ignore)] + public string Version { get; set; } + + [JsonProperty(PropertyName = "d_version_datfile", NullValueHandling = NullValueHandling.Ignore)] + public string VersionDatfile { get; set; } + + [JsonProperty(PropertyName = "d_editions", NullValueHandling = NullValueHandling.Ignore)] + public string[] CommonEditions { get; set; } + + [JsonProperty(PropertyName = "d_editions_text", NullValueHandling = NullValueHandling.Ignore)] + public string OtherEditions { get; set; } + + public object Clone() + { + return new VersionAndEditionsSection + { + Version = this.Version, + VersionDatfile = this.VersionDatfile, + CommonEditions = this.CommonEditions, + OtherEditions = this.OtherEditions, + }; + } + } + + /// + /// EDC section of New Disc form (PSX only) + /// + public class EDCSection : ICloneable + { + [JsonProperty(PropertyName = "d_edc", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(YesNoConverter))] + public YesNo? EDC { get; set; } + + public object Clone() + { + return new EDCSection + { + EDC = this.EDC, + }; + } + } + + /// + /// Parent/Clone relationship section of New Disc form + /// + public class ParentCloneRelationshipSection : ICloneable + { + [JsonProperty(PropertyName = "d_parent_id", NullValueHandling = NullValueHandling.Ignore)] + public string ParentID { get; set; } + + [JsonProperty(PropertyName = "d_is_regional_parent", NullValueHandling = NullValueHandling.Ignore)] + public bool RegionalParent { get; set; } + + public object Clone() + { + return new ParentCloneRelationshipSection + { + ParentID = this.ParentID, + RegionalParent = this.RegionalParent, + }; + } + } + + /// + /// Extras section of New Disc form + /// + public class ExtrasSection : ICloneable + { + [JsonProperty(PropertyName = "d_pvd", NullValueHandling = NullValueHandling.Ignore)] + public string PVD { get; set; } + + [JsonProperty(PropertyName = "d_d1_key", NullValueHandling = NullValueHandling.Ignore)] + public string DiscKey { get; set; } + + [JsonProperty(PropertyName = "d_d2_key", NullValueHandling = NullValueHandling.Ignore)] + public string DiscID { get; set; } + + [JsonProperty(PropertyName = "d_pic_data", NullValueHandling = NullValueHandling.Ignore)] + public string PIC { get; set; } + + [JsonProperty(PropertyName = "d_header", NullValueHandling = NullValueHandling.Ignore)] + public string Header { get; set; } + + [JsonProperty(PropertyName = "d_bca", NullValueHandling = NullValueHandling.Ignore)] + public string BCA { get; set; } + + [JsonProperty(PropertyName = "d_ssranges", NullValueHandling = NullValueHandling.Ignore)] + public string SecuritySectorRanges { get; set; } + + public object Clone() + { + return new ExtrasSection + { + PVD = this.PVD, + DiscKey = this.DiscKey, + DiscID = this.DiscID, + PIC = this.PIC, + Header = this.Header, + BCA = this.BCA, + SecuritySectorRanges = this.SecuritySectorRanges, + }; + } + } + + /// + /// Copy protection section of New Disc form + /// + public class CopyProtectionSection : ICloneable + { + [JsonProperty(PropertyName = "d_protection_a", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(YesNoConverter))] + public YesNo? AntiModchip { get; set; } + + [JsonProperty(PropertyName = "d_protection_1", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(YesNoConverter))] + public YesNo? LibCrypt { get; set; } + + [JsonProperty(PropertyName = "d_libcrypt", NullValueHandling = NullValueHandling.Ignore)] + public string LibCryptData { get; set; } + + [JsonProperty(PropertyName = "d_protection", NullValueHandling = NullValueHandling.Ignore)] + public string Protection { get; set; } + + [JsonIgnore] + public Dictionary> FullProtections { get; set; } + + [JsonProperty(PropertyName = "d_securom", NullValueHandling = NullValueHandling.Ignore)] + public string SecuROMData { get; set; } + + public object Clone() + { + return new CopyProtectionSection + { + AntiModchip = this.AntiModchip, + LibCrypt = this.LibCrypt, + LibCryptData = this.LibCryptData, + Protection = this.Protection, + FullProtections = this.FullProtections?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), + SecuROMData = this.SecuROMData, + }; + } + } + + /// + /// Dumpers and status section of New Disc form (Moderator only) + /// + public class DumpersAndStatusSection : ICloneable + { + [JsonProperty(PropertyName = "d_status", NullValueHandling = NullValueHandling.Ignore)] + public DumpStatus Status { get; set; } + + [JsonProperty(PropertyName = "d_dumpers", NullValueHandling = NullValueHandling.Ignore)] + public string[] Dumpers { get; set; } + + [JsonProperty(PropertyName = "d_dumpers_text", NullValueHandling = NullValueHandling.Ignore)] + public string OtherDumpers { get; set; } + + public object Clone() + { + return new DumpersAndStatusSection + { + Status = this.Status, + Dumpers = this.Dumpers?.Clone() as string[], + OtherDumpers = this.OtherDumpers, + }; + } + } + + /// + /// Tracks and write offsets section of New Disc form (CD/GD-based) + /// + public class TracksAndWriteOffsetsSection : ICloneable + { + [JsonProperty(PropertyName = "d_tracks", NullValueHandling = NullValueHandling.Ignore)] + public string ClrMameProData { get; set; } + + [JsonProperty(PropertyName = "d_cue", NullValueHandling = NullValueHandling.Ignore)] + public string Cuesheet { get; set; } + + [JsonProperty(PropertyName = "d_offset", NullValueHandling = NullValueHandling.Ignore)] + public int[] CommonWriteOffsets { get; set; } + + [JsonProperty(PropertyName = "d_offset_text", NullValueHandling = NullValueHandling.Ignore)] + public string OtherWriteOffsets { get; set; } + + public object Clone() + { + return new TracksAndWriteOffsetsSection + { + ClrMameProData = this.ClrMameProData, + Cuesheet = this.Cuesheet, + CommonWriteOffsets = this.CommonWriteOffsets?.Clone() as int[], + OtherWriteOffsets = this.OtherWriteOffsets, + }; + } + } + + /// + /// Size & checksums section of New Disc form (DVD/BD/UMD-based) + /// + public class SizeAndChecksumsSection : ICloneable + { + [JsonProperty(PropertyName = "d_layerbreak", NullValueHandling = NullValueHandling.Ignore)] + public long Layerbreak { get; set; } + + [JsonProperty(PropertyName = "d_layerbreak_2", NullValueHandling = NullValueHandling.Ignore)] + public long Layerbreak2 { get; set; } + + [JsonProperty(PropertyName = "d_layerbreak_3", NullValueHandling = NullValueHandling.Ignore)] + public long Layerbreak3 { get; set; } + + [JsonProperty(PropertyName = "d_pic_identifier", NullValueHandling = NullValueHandling.Ignore)] + public string PICIdentifier { get; set; } + + [JsonProperty(PropertyName = "d_size", NullValueHandling = NullValueHandling.Ignore)] + public long Size { get; set; } + + [JsonProperty(PropertyName = "d_crc32", NullValueHandling = NullValueHandling.Ignore)] + public string CRC32 { get; set; } + + [JsonProperty(PropertyName = "d_md5", NullValueHandling = NullValueHandling.Ignore)] + public string MD5 { get; set; } + + [JsonProperty(PropertyName = "d_sha1", NullValueHandling = NullValueHandling.Ignore)] + public string SHA1 { get; set; } + + public object Clone() + { + return new SizeAndChecksumsSection + { + Layerbreak = this.Layerbreak, + Layerbreak2 = this.Layerbreak2, + Layerbreak3 = this.Layerbreak3, + PICIdentifier = this.PICIdentifier, + Size = this.Size, + CRC32 = this.CRC32, + MD5 = this.MD5, + SHA1 = this.SHA1, + }; + } + } + + /// + /// Dumping info section for moderation + /// + public class DumpingInfoSection : ICloneable + { + // Name not defined by Redump + [JsonProperty(PropertyName = "d_dumping_program", Required = Required.AllowNull)] + public string DumpingProgram { get; set; } + + // Name not defined by Redump + [JsonProperty(PropertyName = "d_dumping_date", Required = Required.AllowNull)] + public string DumpingDate { get; set; } + + // Name not defined by Redump + [JsonProperty(PropertyName = "d_drive_manufacturer", Required = Required.AllowNull)] + public string Manufacturer { get; set; } + + // Name not defined by Redump + [JsonProperty(PropertyName = "d_drive_model", Required = Required.AllowNull)] + public string Model { get; set; } + + // Name not defined by Redump + [JsonProperty(PropertyName = "d_drive_firmware", Required = Required.AllowNull)] + public string Firmware { get; set; } + + // Name not defined by Redump + [JsonProperty(PropertyName = "d_reported_disc_type", Required = Required.AllowNull)] + public string ReportedDiscType { get; set; } + + public object Clone() + { + return new DumpingInfoSection + { + DumpingProgram = this.DumpingProgram, + DumpingDate = this.DumpingDate, + Manufacturer = this.Manufacturer, + Model = this.Model, + Firmware = this.Firmware, + ReportedDiscType = this.ReportedDiscType, + }; + } + } +} diff --git a/Web/RedumpHttpClient.cs b/Web/RedumpHttpClient.cs new file mode 100644 index 0000000..ae2648f --- /dev/null +++ b/Web/RedumpHttpClient.cs @@ -0,0 +1,808 @@ +#if NET6_0 + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using RedumpLib.Data; + +namespace SabreTools.RedumpLib.Web +{ + public class RedumpHttpClient : HttpClient + { + #region Properties + + /// + /// Determines if user is logged into Redump + /// + public bool LoggedIn { get; private set; } = false; + + /// + /// Determines if the user is a staff member + /// + public bool IsStaff { get; private set; } = false; + + #endregion + + /// + /// Constructor + /// + public RedumpHttpClient() + : base(new HttpClientHandler { UseCookies = true }) + { + } + + #region Credentials + + /// + /// Validate supplied credentials + /// + public async static Task<(bool?, string)> ValidateCredentials(string username, string password) + { + // If options are invalid or we're missing something key, just return + if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) + return (false, null); + + // Try logging in with the supplied credentials otherwise + using RedumpHttpClient httpClient = new(); + + bool? loggedIn = await httpClient.Login(username, password); + if (loggedIn == true) + return (true, "Redump username and password accepted!"); + else if (loggedIn == false) + return (false, "Redump username and password denied!"); + else + return (null, "An error occurred validating your credentials!"); + } + + /// + /// Login to Redump, if possible + /// + /// Redump username + /// Redump password + /// True if the user could be logged in, false otherwise, null on error + public async Task Login(string username, string password) + { + // Credentials verification + if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password)) + { + Console.WriteLine("Credentials entered, will attempt Redump login..."); + } + else if (!string.IsNullOrWhiteSpace(username) && string.IsNullOrWhiteSpace(password)) + { + Console.WriteLine("Only a username was specified, will not attempt Redump login..."); + return false; + } + else if (string.IsNullOrWhiteSpace(username)) + { + Console.WriteLine("No credentials entered, will not attempt Redump login..."); + return false; + } + + // HTTP encode the password + password = WebUtility.UrlEncode(password); + + // Attempt to login up to 3 times + for (int i = 0; i < 3; i++) + { + try + { + // Get the current token from the login page + var loginPage = await GetStringAsync(Constants.LoginUrl); + string token = Constants.TokenRegex.Match(loginPage).Groups[1].Value; + + // Construct the login request + var postContent = new StringContent($"form_sent=1&redirect_url=&csrf_token={token}&req_username={username}&req_password={password}&save_pass=0", Encoding.UTF8); + postContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded"); + + // Send the login request and get the result + var response = await PostAsync(Constants.LoginUrl, postContent); + string responseContent = await response?.Content?.ReadAsStringAsync(); + + if (string.IsNullOrWhiteSpace(responseContent)) + { + Console.WriteLine($"An error occurred while trying to log in on attempt {i}: No response"); + continue; + } + + if (responseContent.Contains("Incorrect username and/or password.")) + { + Console.WriteLine("Invalid credentials entered, continuing without logging in..."); + return false; + } + + // The user was able to be logged in + Console.WriteLine("Credentials accepted! Logged into Redump..."); + LoggedIn = true; + + // If the user is a moderator or staff, set accordingly + if (responseContent.Contains("http://forum.redump.org/forum/9/staff/")) + IsStaff = true; + + return true; + } + catch (Exception ex) + { + Console.WriteLine($"An exception occurred while trying to log in on attempt {i}: {ex}"); + } + } + + Console.WriteLine("Could not login to Redump in 3 attempts, continuing without logging in..."); + return false; + } + + #endregion + + #region Single Page Helpers + + /// + /// Process a Redump site page as a list of possible IDs or disc page + /// + /// Base URL to download using + /// List of IDs from the page, empty on error + public async Task> CheckSingleSitePage(string url) + { + List ids = new(); + + // Try up to 3 times to retrieve the data + string dumpsPage = await DownloadString(url, retries: 3); + + // If we have no dumps left + if (dumpsPage == null || dumpsPage.Contains("No discs found.")) + return ids; + + // If we have a single disc page already + if (dumpsPage.Contains("Download:")) + { + var value = Regex.Match(dumpsPage, @"/disc/(\d+)/sfv/").Groups[1].Value; + if (int.TryParse(value, out int id)) + ids.Add(id); + + return ids; + } + + // Otherwise, traverse each dump on the page + var matches = Constants.DiscRegex.Matches(dumpsPage); + foreach (Match match in matches) + { + try + { + if (int.TryParse(match.Groups[1].Value, out int value)) + ids.Add(value); + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + continue; + } + } + + return ids; + } + + /// + /// Process a Redump site page as a list of possible IDs or disc page + /// + /// Base URL to download using + /// Output directory to save data to + /// True to return on first error, false otherwise + /// True if the page could be downloaded, false otherwise + public async Task CheckSingleSitePage(string url, string outDir, bool failOnSingle) + { + // Try up to 3 times to retrieve the data + string dumpsPage = await DownloadString(url, retries: 3); + + // If we have no dumps left + if (dumpsPage == null || dumpsPage.Contains("No discs found.")) + return false; + + // If we have a single disc page already + if (dumpsPage.Contains("Download:")) + { + var value = Regex.Match(dumpsPage, @"/disc/(\d+)/sfv/").Groups[1].Value; + if (int.TryParse(value, out int id)) + { + bool downloaded = await DownloadSingleSiteID(id, outDir, false); + if (!downloaded && failOnSingle) + return false; + } + + return false; + } + + // Otherwise, traverse each dump on the page + var matches = Constants.DiscRegex.Matches(dumpsPage); + foreach (Match match in matches) + { + try + { + if (int.TryParse(match.Groups[1].Value, out int value)) + { + bool downloaded = await DownloadSingleSiteID(value, outDir, false); + if (!downloaded && failOnSingle) + return false; + } + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + continue; + } + } + + return true; + } + + /// + /// Process a Redump WIP page as a list of possible IDs or disc page + /// + /// RedumpWebClient to access the packs + /// List of IDs from the page, empty on error + public async Task> CheckSingleWIPPage(string url) + { + List ids = new(); + + // Try up to 3 times to retrieve the data + string dumpsPage = await DownloadString(url, retries: 3); + + // If we have no dumps left + if (dumpsPage == null || dumpsPage.Contains("No discs found.")) + return ids; + + // Otherwise, traverse each dump on the page + var matches = Constants.NewDiscRegex.Matches(dumpsPage); + foreach (Match match in matches) + { + try + { + if (int.TryParse(match.Groups[2].Value, out int value)) + ids.Add(value); + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + continue; + } + } + + return ids; + } + + /// + /// Process a Redump WIP page as a list of possible IDs or disc page + /// + /// RedumpWebClient to access the packs + /// Output directory to save data to + /// True to return on first error, false otherwise + /// True if the page could be downloaded, false otherwise + public async Task CheckSingleWIPPage(string url, string outDir, bool failOnSingle) + { + // Try up to 3 times to retrieve the data + string dumpsPage = await DownloadString(url, retries: 3); + + // If we have no dumps left + if (dumpsPage == null || dumpsPage.Contains("No discs found.")) + return false; + + // Otherwise, traverse each dump on the page + var matches = Constants.NewDiscRegex.Matches(dumpsPage); + foreach (Match match in matches) + { + try + { + if (int.TryParse(match.Groups[2].Value, out int value)) + { + bool downloaded = await DownloadSingleWIPID(value, outDir, false); + if (!downloaded && failOnSingle) + return false; + } + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + continue; + } + } + + return true; + } + + #endregion + + #region Download Helpers + + /// + /// Download a single pack + /// + /// Base URL to download using + /// System to download packs for + /// Byte array containing the downloaded pack, null on error + public async Task DownloadSinglePack(string url, RedumpSystem? system) + { + try + { + return await GetByteArrayAsync(string.Format(url, system.ShortName())); + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + return null; + } + } + + /// + /// Download a single pack + /// + /// Base URL to download using + /// System to download packs for + /// Output directory to save data to + /// Named subfolder for the pack, used optionally + public async Task DownloadSinglePack(string url, RedumpSystem? system, string outDir, string subfolder) + { + try + { + // If no output directory is defined, use the current directory instead + if (string.IsNullOrWhiteSpace(outDir)) + outDir = Environment.CurrentDirectory; + + string tempfile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString()); + string packUri = string.Format(url, system.ShortName()); + + // Make the call to get the pack + string remoteFileName = await DownloadFile(packUri, tempfile); + MoveOrDelete(tempfile, remoteFileName, outDir, subfolder); + return true; + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + return false; + } + } + + /// + /// Download an individual site ID data, if possible + /// + /// Redump disc ID to retrieve + /// String containing the page contents if successful, null on error + public async Task DownloadSingleSiteID(int id) + { + string paddedId = id.ToString().PadLeft(5, '0'); + Console.WriteLine($"Processing ID: {paddedId}"); + try + { + // Try up to 3 times to retrieve the data + string discPageUri = string.Format(Constants.DiscPageUrl, +id); + string discPage = await DownloadString(discPageUri, retries: 3); + + if (discPage == null || discPage.Contains($"Disc with ID \"{id}\" doesn't exist")) + { + Console.WriteLine($"ID {paddedId} could not be found!"); + return null; + } + + Console.WriteLine($"ID {paddedId} has been successfully downloaded"); + return discPage; + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + return null; + } + } + + /// + /// Download an individual site ID data, if possible + /// + /// Redump disc ID to retrieve + /// Output directory to save data to + /// True to rename deleted entries, false otherwise + /// True if all data was downloaded, false otherwise + public async Task DownloadSingleSiteID(int id, string outDir, bool rename) + { + // If no output directory is defined, use the current directory instead + if (string.IsNullOrWhiteSpace(outDir)) + outDir = Environment.CurrentDirectory; + + string paddedId = id.ToString().PadLeft(5, '0'); + string paddedIdDir = Path.Combine(outDir, paddedId); + Console.WriteLine($"Processing ID: {paddedId}"); + try + { + // Try up to 3 times to retrieve the data + string discPageUri = string.Format(Constants.DiscPageUrl, +id); + string discPage = await DownloadString(discPageUri, retries: 3); + + if (discPage == null || discPage.Contains($"Disc with ID \"{id}\" doesn't exist")) + { + try + { + if (rename) + { + if (Directory.Exists(paddedIdDir) && rename) + Directory.Move(paddedIdDir, paddedIdDir + "-deleted"); + else + Directory.CreateDirectory(paddedIdDir + "-deleted"); + } + } + catch { } + + Console.WriteLine($"ID {paddedId} could not be found!"); + return false; + } + + // Check if the page has been updated since the last time it was downloaded, if possible + if (File.Exists(Path.Combine(paddedIdDir, "disc.html"))) + { + // Read in the cached file + var oldDiscPage = File.ReadAllText(Path.Combine(paddedIdDir, "disc.html")); + + // Check for the last modified date in both pages + var oldResult = Constants.LastModifiedRegex.Match(oldDiscPage); + var newResult = Constants.LastModifiedRegex.Match(discPage); + + // If both pages contain the same modified date, skip it + if (oldResult.Success && newResult.Success && oldResult.Groups[1].Value == newResult.Groups[1].Value) + { + Console.WriteLine($"ID {paddedId} has not been changed since last download"); + return false; + } + + // If neither page contains a modified date, skip it + else if (!oldResult.Success && !newResult.Success) + { + Console.WriteLine($"ID {paddedId} has not been changed since last download"); + return false; + } + } + + // Create ID subdirectory + Directory.CreateDirectory(paddedIdDir); + + // View Edit History + if (discPage.Contains($"
+ /// Download an individual WIP ID data, if possible + /// + /// Redump WIP disc ID to retrieve + /// String containing the page contents if successful, null on error + public async Task DownloadSingleWIPID(int id) + { + string paddedId = id.ToString().PadLeft(5, '0'); + Console.WriteLine($"Processing ID: {paddedId}"); + try + { + // Try up to 3 times to retrieve the data + string discPageUri = string.Format(Constants.WipDiscPageUrl, +id); + string discPage = await DownloadString(discPageUri, retries: 3); + + if (discPage == null || discPage.Contains($"WIP disc with ID \"{id}\" doesn't exist")) + { + Console.WriteLine($"ID {paddedId} could not be found!"); + return null; + } + + Console.WriteLine($"ID {paddedId} has been successfully downloaded"); + return discPage; + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + return null; + } + } + + /// + /// Download an individual WIP ID data, if possible + /// + /// Redump WIP disc ID to retrieve + /// Output directory to save data to + /// True to rename deleted entries, false otherwise + /// True if all data was downloaded, false otherwise + public async Task DownloadSingleWIPID(int id, string outDir, bool rename) + { + // If no output directory is defined, use the current directory instead + if (string.IsNullOrWhiteSpace(outDir)) + outDir = Environment.CurrentDirectory; + + string paddedId = id.ToString().PadLeft(5, '0'); + string paddedIdDir = Path.Combine(outDir, paddedId); + Console.WriteLine($"Processing ID: {paddedId}"); + try + { + // Try up to 3 times to retrieve the data + string discPageUri = string.Format(Constants.WipDiscPageUrl, +id); + string discPage = await DownloadString(discPageUri, retries: 3); + + if (discPage == null || discPage.Contains($"WIP disc with ID \"{id}\" doesn't exist")) + { + try + { + if (rename) + { + if (Directory.Exists(paddedIdDir) && rename) + Directory.Move(paddedIdDir, paddedIdDir + "-deleted"); + else + Directory.CreateDirectory(paddedIdDir + "-deleted"); + } + } + catch { } + + Console.WriteLine($"ID {paddedId} could not be found!"); + return false; + } + + // Check if the page has been updated since the last time it was downloaded, if possible + if (File.Exists(Path.Combine(paddedIdDir, "disc.html"))) + { + // Read in the cached file + var oldDiscPage = File.ReadAllText(Path.Combine(paddedIdDir, "disc.html")); + + // Check for the full match ID in both pages + var oldResult = Constants.FullMatchRegex.Match(oldDiscPage); + var newResult = Constants.FullMatchRegex.Match(discPage); + + // If both pages contain the same ID, skip it + if (oldResult.Success && newResult.Success && oldResult.Groups[1].Value == newResult.Groups[1].Value) + { + Console.WriteLine($"ID {paddedId} has not been changed since last download"); + return false; + } + + // If neither page contains an ID, skip it + else if (!oldResult.Success && !newResult.Success) + { + Console.WriteLine($"ID {paddedId} has not been changed since last download"); + return false; + } + } + + // Create ID subdirectory + Directory.CreateDirectory(paddedIdDir); + + // HTML + using (var discStreamWriter = File.CreateText(Path.Combine(paddedIdDir, "disc.html"))) + { + discStreamWriter.Write(discPage); + } + + Console.WriteLine($"ID {paddedId} has been successfully downloaded"); + return true; + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + return false; + } + } + + #endregion + + #region Helpers + + /// + /// Download a set of packs + /// + /// Base URL to download using + /// Systems to download packs for + /// Name of the pack that is downloading + public async Task> DownloadPacks(string url, RedumpSystem?[] systems, string title) + { + var packsDictionary = new Dictionary(); + + Console.WriteLine($"Downloading {title}"); + foreach (var system in systems) + { + // If the system is invalid, we can't do anything + if (system == null || !system.IsAvailable()) + continue; + + // If we didn't have credentials + if (!LoggedIn && system.IsBanned()) + continue; + + // If the system is unknown, we can't do anything + string longName = system.LongName(); + if (string.IsNullOrWhiteSpace(longName)) + continue; + + Console.Write($"\r{longName}{new string(' ', Console.BufferWidth - longName.Length - 1)}"); + byte[] pack = await DownloadSinglePack(url, system); + if (pack != null) + packsDictionary.Add(system.Value, pack); + } + + Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}"); + Console.WriteLine(); + + return packsDictionary; + } + + /// + /// Download a set of packs + /// + /// Base URL to download using + /// Systems to download packs for + /// Name of the pack that is downloading + /// Output directory to save data to + /// Named subfolder for the pack, used optionally + public async Task DownloadPacks(string url, RedumpSystem?[] systems, string title, string outDir, string subfolder) + { + Console.WriteLine($"Downloading {title}"); + foreach (var system in systems) + { + // If the system is invalid, we can't do anything + if (system == null || !system.IsAvailable()) + continue; + + // If we didn't have credentials + if (!LoggedIn && system.IsBanned()) + continue; + + // If the system is unknown, we can't do anything + string longName = system.LongName(); + if (string.IsNullOrWhiteSpace(longName)) + continue; + + Console.Write($"\r{longName}{new string(' ', Console.BufferWidth - longName.Length - 1)}"); + await DownloadSinglePack(url, system, outDir, subfolder); + } + + Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}"); + Console.WriteLine(); + return true; + } + + /// + /// Download from a URI to a local file + /// + /// Remote URI to retrieve + /// Filename to write to + /// The remote filename from the URI, null on error + private async Task DownloadFile(string uri, string fileName) + { + // Make the call to get the file + var response = await GetAsync(uri); + if (response?.Content?.Headers == null || !response.IsSuccessStatusCode) + { + Console.WriteLine($"Could not download {uri}"); + return null; + } + + // Copy the data to a local temp file + using (var responseStream = await response.Content.ReadAsStreamAsync()) + using (var tempFileStream = File.OpenWrite(fileName)) + { + responseStream.CopyTo(tempFileStream); + } + + return response.Content.Headers.ContentDisposition?.FileName?.Replace("\"", ""); + } + + /// + /// Download from a URI to a string + /// + /// Remote URI to retrieve + /// Number of times to retry on error + /// String from the URI, null on error + private async Task DownloadString(string uri, int retries = 3) + { + // Only retry a positive number of times + if (retries <= 0) + return null; + + for (int i = 0; i < retries; i++) + { + try + { + return await GetStringAsync(uri); + } + catch { } + } + + return null; + } + + /// + /// Move a tempfile to a new name unless it aleady exists, in which case, delete the tempfile + /// + /// Path to existing temporary file + /// Path to new output file + /// Output directory to save data to + /// Optional subfolder to append to the path + private static void MoveOrDelete(string tempfile, string newfile, string outDir, string subfolder) + { + // If we don't have a file to move to, just delete the temp file + if (string.IsNullOrWhiteSpace(newfile)) + { + File.Delete(tempfile); + return; + } + + // If we have a subfolder, create it and update the newfile name + if (!string.IsNullOrWhiteSpace(subfolder)) + { + if (!Directory.Exists(Path.Combine(outDir, subfolder))) + Directory.CreateDirectory(Path.Combine(outDir, subfolder)); + + newfile = Path.Combine(subfolder, newfile); + } + + // If the file already exists, don't overwrite it + if (File.Exists(Path.Combine(outDir, newfile))) + File.Delete(tempfile); + else + File.Move(tempfile, Path.Combine(outDir, newfile)); + } + + #endregion + } +} + +#endif \ No newline at end of file diff --git a/Web/RedumpWebClient.cs b/Web/RedumpWebClient.cs new file mode 100644 index 0000000..ac648d5 --- /dev/null +++ b/Web/RedumpWebClient.cs @@ -0,0 +1,837 @@ +#if NET48 || NETSTANDARD2_1 + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using RedumpLib.Data; + +namespace SabreTools.RedumpLib.Web +{ + // https://stackoverflow.com/questions/1777221/using-cookiecontainer-with-webclient-class + public class RedumpWebClient : WebClient + { + private readonly CookieContainer m_container = new CookieContainer(); + + /// + /// Determines if user is logged into Redump + /// + public bool LoggedIn { get; private set; } = false; + + /// + /// Determines if the user is a staff member + /// + public bool IsStaff { get; private set; } = false; + + /// + /// Get the last downloaded filename, if possible + /// + /// + public string GetLastFilename() + { + // If the response headers are null or empty + if (ResponseHeaders == null || ResponseHeaders.Count == 0) + return null; + + // If we don't have the response header we care about + string headerValue = ResponseHeaders.Get("Content-Disposition"); + if (string.IsNullOrWhiteSpace(headerValue)) + return null; + + // Extract the filename from the value +#if NETSTANDARD2_1 + return headerValue[(headerValue.IndexOf("filename=") + 9)..].Replace("\"", ""); +#else + return headerValue.Substring(headerValue.IndexOf("filename=") + 9).Replace("\"", ""); +#endif + } + + /// + protected override WebRequest GetWebRequest(Uri address) + { + WebRequest request = base.GetWebRequest(address); + if (request is HttpWebRequest webRequest) + webRequest.CookieContainer = m_container; + + return request; + } + + /// + /// Validate supplied credentials + /// + public static (bool?, string) ValidateCredentials(string username, string password) + { + // If options are invalid or we're missing something key, just return + if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) + return (false, null); + + // Try logging in with the supplied credentials otherwise +#if NETSTANDARD2_1 + using RedumpWebClient wc = new RedumpWebClient(); +#else + using (RedumpWebClient wc = new RedumpWebClient()) + { +#endif + bool? loggedIn = wc.Login(username, password); + if (loggedIn == true) + return (true, "Redump username and password accepted!"); + else if (loggedIn == false) + return (false, "Redump username and password denied!"); + else + return (null, "An error occurred validating your credentials!"); +#if NET48 + } +#endif + } + + /// + /// Login to Redump, if possible + /// + /// Redump username + /// Redump password + /// True if the user could be logged in, false otherwise, null on error + public bool? Login(string username, string password) + { + // Credentials verification + if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password)) + { + Console.WriteLine("Credentials entered, will attempt Redump login..."); + } + else if (!string.IsNullOrWhiteSpace(username) && string.IsNullOrWhiteSpace(password)) + { + Console.WriteLine("Only a username was specified, will not attempt Redump login..."); + return false; + } + else if (string.IsNullOrWhiteSpace(username)) + { + Console.WriteLine("No credentials entered, will not attempt Redump login..."); + return false; + } + + // HTTP encode the password + password = WebUtility.UrlEncode(password); + + // Attempt to login up to 3 times + for (int i = 0; i < 3; i++) + { + try + { + // Get the current token from the login page + var loginPage = DownloadString(Constants.LoginUrl); + string token = Constants.TokenRegex.Match(loginPage).Groups[1].Value; + + // Construct the login request + Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; + Encoding = Encoding.UTF8; + var response = UploadString(Constants.LoginUrl, $"form_sent=1&redirect_url=&csrf_token={token}&req_username={username}&req_password={password}&save_pass=0"); + + if (response.Contains("Incorrect username and/or password.")) + { + Console.WriteLine("Invalid credentials entered, continuing without logging in..."); + return false; + } + + // The user was able to be logged in + Console.WriteLine("Credentials accepted! Logged into Redump..."); + LoggedIn = true; + + // If the user is a moderator or staff, set accordingly + if (response.Contains("http://forum.redump.org/forum/9/staff/")) + IsStaff = true; + + return true; + } + catch (Exception ex) + { + Console.WriteLine($"An exception occurred while trying to log in on attempt {i}: {ex}"); + } + } + + Console.WriteLine("Could not login to Redump in 3 attempts, continuing without logging in..."); + return false; + } + + #region Single Page Helpers + + /// + /// Process a Redump site page as a list of possible IDs or disc page + /// + /// Base URL to download using + /// List of IDs from the page, empty on error + public List CheckSingleSitePage(string url) + { + List ids = new List(); + string dumpsPage = string.Empty; + + // Try up to 3 times to retrieve the data + for (int i = 0; i < 3; i++) + { + try + { + dumpsPage = DownloadString(url); + break; + } + catch { } + } + + // If we have no dumps left + if (dumpsPage.Contains("No discs found.")) + return ids; + + // If we have a single disc page already + if (dumpsPage.Contains("Download:")) + { + var value = Regex.Match(dumpsPage, @"/disc/(\d+)/sfv/").Groups[1].Value; + if (int.TryParse(value, out int id)) + ids.Add(id); + + return ids; + } + + // Otherwise, traverse each dump on the page + var matches = Constants.DiscRegex.Matches(dumpsPage); + foreach (Match match in matches) + { + try + { + if (int.TryParse(match.Groups[1].Value, out int value)) + ids.Add(value); + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + continue; + } + } + + return ids; + } + + /// + /// Process a Redump site page as a list of possible IDs or disc page + /// + /// Base URL to download using + /// Output directory to save data to + /// True to return on first error, false otherwise + /// True if the page could be downloaded, false otherwise + public bool CheckSingleSitePage(string url, string outDir, bool failOnSingle) + { + string dumpsPage = string.Empty; + + // Try up to 3 times to retrieve the data + for (int i = 0; i < 3; i++) + { + try + { + dumpsPage = DownloadString(url); + break; + } + catch { } + } + + // If we have no dumps left + if (dumpsPage.Contains("No discs found.")) + return false; + + // If we have a single disc page already + if (dumpsPage.Contains("Download:")) + { + var value = Regex.Match(dumpsPage, @"/disc/(\d+)/sfv/").Groups[1].Value; + if (int.TryParse(value, out int id)) + { + bool downloaded = DownloadSingleSiteID(id, outDir, false); + if (!downloaded && failOnSingle) + return false; + } + + return false; + } + + // Otherwise, traverse each dump on the page + var matches = Constants.DiscRegex.Matches(dumpsPage); + foreach (Match match in matches) + { + try + { + if (int.TryParse(match.Groups[1].Value, out int value)) + { + bool downloaded = DownloadSingleSiteID(value, outDir, false); + if (!downloaded && failOnSingle) + return false; + } + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + continue; + } + } + + return true; + } + + /// + /// Process a Redump WIP page as a list of possible IDs or disc page + /// + /// RedumpWebClient to access the packs + /// List of IDs from the page, empty on error + public List CheckSingleWIPPage(string url) + { + List ids = new List(); + string dumpsPage = string.Empty; + + // Try up to 3 times to retrieve the data + for (int i = 0; i < 3; i++) + { + try + { + dumpsPage = DownloadString(url); + break; + } + catch { } + } + + // If we have no dumps left + if (dumpsPage.Contains("No discs found.")) + return ids; + + // Otherwise, traverse each dump on the page + var matches = Constants.NewDiscRegex.Matches(dumpsPage); + foreach (Match match in matches) + { + try + { + if (int.TryParse(match.Groups[2].Value, out int value)) + ids.Add(value); + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + continue; + } + } + + return ids; + } + + /// + /// Process a Redump WIP page as a list of possible IDs or disc page + /// + /// RedumpWebClient to access the packs + /// Output directory to save data to + /// True to return on first error, false otherwise + /// True if the page could be downloaded, false otherwise + public bool CheckSingleWIPPage(string url, string outDir, bool failOnSingle) + { + string dumpsPage = string.Empty; + + // Try up to 3 times to retrieve the data + for (int i = 0; i < 3; i++) + { + try + { + dumpsPage = DownloadString(url); + break; + } + catch { } + } + + // If we have no dumps left + if (dumpsPage.Contains("No discs found.")) + return false; + + // Otherwise, traverse each dump on the page + var matches = Constants.NewDiscRegex.Matches(dumpsPage); + foreach (Match match in matches) + { + try + { + if (int.TryParse(match.Groups[2].Value, out int value)) + { + bool downloaded = DownloadSingleWIPID(value, outDir, false); + if (!downloaded && failOnSingle) + return false; + } + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + continue; + } + } + + return true; + } + + #endregion + + #region Download Helpers + + /// + /// Download a single pack + /// + /// Base URL to download using + /// System to download packs for + /// Byte array containing the downloaded pack, null on error + public byte[] DownloadSinglePack(string url, RedumpSystem? system) + { + try + { + return DownloadData(string.Format(url, system.ShortName())); + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + return null; + } + } + + /// + /// Download a single pack + /// + /// Base URL to download using + /// System to download packs for + /// Output directory to save data to + /// Named subfolder for the pack, used optionally + public void DownloadSinglePack(string url, RedumpSystem? system, string outDir, string subfolder) + { + try + { + // If no output directory is defined, use the current directory instead + if (string.IsNullOrWhiteSpace(outDir)) + outDir = Environment.CurrentDirectory; + + string tempfile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString()); + DownloadFile(string.Format(url, system.ShortName()), tempfile); + MoveOrDelete(tempfile, GetLastFilename(), outDir, subfolder); + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + } + } + + /// + /// Download an individual site ID data, if possible + /// + /// Redump disc ID to retrieve + /// String containing the page contents if successful, null on error + public string DownloadSingleSiteID(int id) + { + string paddedId = id.ToString().PadLeft(5, '0'); + Console.WriteLine($"Processing ID: {paddedId}"); + try + { + string discPage = string.Empty; + + // Try up to 3 times to retrieve the data + for (int i = 0; i < 3; i++) + { + try + { + discPage = DownloadString(string.Format(Constants.DiscPageUrl, +id)); + break; + } + catch { } + } + + if (discPage.Contains($"Disc with ID \"{id}\" doesn't exist")) + { + Console.WriteLine($"ID {paddedId} could not be found!"); + return null; + } + + Console.WriteLine($"ID {paddedId} has been successfully downloaded"); + return discPage; + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + return null; + } + } + + /// + /// Download an individual site ID data, if possible + /// + /// Redump disc ID to retrieve + /// Output directory to save data to + /// True to rename deleted entries, false otherwise + /// True if all data was downloaded, false otherwise + public bool DownloadSingleSiteID(int id, string outDir, bool rename) + { + // If no output directory is defined, use the current directory instead + if (string.IsNullOrWhiteSpace(outDir)) + outDir = Environment.CurrentDirectory; + + string paddedId = id.ToString().PadLeft(5, '0'); + string paddedIdDir = Path.Combine(outDir, paddedId); + Console.WriteLine($"Processing ID: {paddedId}"); + try + { + string discPage = string.Empty; + + // Try up to 3 times to retrieve the data + for (int i = 0; i < 3; i++) + { + try + { + discPage = DownloadString(string.Format(Constants.DiscPageUrl, +id)); + break; + } + catch { } + } + + if (discPage.Contains($"Disc with ID \"{id}\" doesn't exist")) + { + try + { + if (rename) + { + if (Directory.Exists(paddedIdDir) && rename) + Directory.Move(paddedIdDir, paddedIdDir + "-deleted"); + else + Directory.CreateDirectory(paddedIdDir + "-deleted"); + } + } + catch { } + + Console.WriteLine($"ID {paddedId} could not be found!"); + return false; + } + + // Check if the page has been updated since the last time it was downloaded, if possible + if (File.Exists(Path.Combine(paddedIdDir, "disc.html"))) + { + // Read in the cached file + var oldDiscPage = File.ReadAllText(Path.Combine(paddedIdDir, "disc.html")); + + // Check for the last modified date in both pages + var oldResult = Constants.LastModifiedRegex.Match(oldDiscPage); + var newResult = Constants.LastModifiedRegex.Match(discPage); + + // If both pages contain the same modified date, skip it + if (oldResult.Success && newResult.Success && oldResult.Groups[1].Value == newResult.Groups[1].Value) + { + Console.WriteLine($"ID {paddedId} has not been changed since last download"); + return false; + } + + // If neither page contains a modified date, skip it + else if (!oldResult.Success && !newResult.Success) + { + Console.WriteLine($"ID {paddedId} has not been changed since last download"); + return false; + } + } + + // Create ID subdirectory + Directory.CreateDirectory(paddedIdDir); + + // View Edit History + if (discPage.Contains($" + /// Download an individual WIP ID data, if possible + /// + /// Redump WIP disc ID to retrieve + /// String containing the page contents if successful, null on error + public string DownloadSingleWIPID(int id) + { + string paddedId = id.ToString().PadLeft(5, '0'); + Console.WriteLine($"Processing ID: {paddedId}"); + try + { + string discPage = string.Empty; + + // Try up to 3 times to retrieve the data + for (int i = 0; i < 3; i++) + { + try + { + discPage = DownloadString(string.Format(Constants.WipDiscPageUrl, +id)); + break; + } + catch { } + } + + if (discPage.Contains($"WIP disc with ID \"{id}\" doesn't exist")) + { + Console.WriteLine($"ID {paddedId} could not be found!"); + return null; + } + + Console.WriteLine($"ID {paddedId} has been successfully downloaded"); + return discPage; + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + return null; + } + } + + /// + /// Download an individual WIP ID data, if possible + /// + /// Redump WIP disc ID to retrieve + /// Output directory to save data to + /// True to rename deleted entries, false otherwise + /// True if all data was downloaded, false otherwise + public bool DownloadSingleWIPID(int id, string outDir, bool rename) + { + // If no output directory is defined, use the current directory instead + if (string.IsNullOrWhiteSpace(outDir)) + outDir = Environment.CurrentDirectory; + + string paddedId = id.ToString().PadLeft(5, '0'); + string paddedIdDir = Path.Combine(outDir, paddedId); + Console.WriteLine($"Processing ID: {paddedId}"); + try + { + string discPage = string.Empty; + + // Try up to 3 times to retrieve the data + for (int i = 0; i < 3; i++) + { + try + { + discPage = DownloadString(string.Format(Constants.WipDiscPageUrl, +id)); + break; + } + catch { } + } + + if (discPage.Contains($"WIP disc with ID \"{id}\" doesn't exist")) + { + try + { + if (rename) + { + if (Directory.Exists(paddedIdDir) && rename) + Directory.Move(paddedIdDir, paddedIdDir + "-deleted"); + else + Directory.CreateDirectory(paddedIdDir + "-deleted"); + } + } + catch { } + + Console.WriteLine($"ID {paddedId} could not be found!"); + return false; + } + + // Check if the page has been updated since the last time it was downloaded, if possible + if (File.Exists(Path.Combine(paddedIdDir, "disc.html"))) + { + // Read in the cached file + var oldDiscPage = File.ReadAllText(Path.Combine(paddedIdDir, "disc.html")); + + // Check for the full match ID in both pages + var oldResult = Constants.FullMatchRegex.Match(oldDiscPage); + var newResult = Constants.FullMatchRegex.Match(discPage); + + // If both pages contain the same ID, skip it + if (oldResult.Success && newResult.Success && oldResult.Groups[1].Value == newResult.Groups[1].Value) + { + Console.WriteLine($"ID {paddedId} has not been changed since last download"); + return false; + } + + // If neither page contains an ID, skip it + else if (!oldResult.Success && !newResult.Success) + { + Console.WriteLine($"ID {paddedId} has not been changed since last download"); + return false; + } + } + + // Create ID subdirectory + Directory.CreateDirectory(paddedIdDir); + + // HTML + using (var discStreamWriter = File.CreateText(Path.Combine(paddedIdDir, "disc.html"))) + { + discStreamWriter.Write(discPage); + } + + Console.WriteLine($"ID {paddedId} has been successfully downloaded"); + return true; + } + catch (Exception ex) + { + Console.WriteLine($"An exception has occurred: {ex}"); + return false; + } + } + + #endregion + + #region Helpers + + /// + /// Download a set of packs + /// + /// Base URL to download using + /// Systems to download packs for + /// Name of the pack that is downloading + public Dictionary DownloadPacks(string url, RedumpSystem?[] systems, string title) + { + var packsDictionary = new Dictionary(); + + Console.WriteLine($"Downloading {title}"); + foreach (var system in systems) + { + // If the system is invalid, we can't do anything + if (system == null || !system.IsAvailable()) + continue; + + // If we didn't have credentials + if (!LoggedIn && system.IsBanned()) + continue; + + // If the system is unknown, we can't do anything + string longName = system.LongName(); + if (string.IsNullOrWhiteSpace(longName)) + continue; + + Console.Write($"\r{longName}{new string(' ', Console.BufferWidth - longName.Length - 1)}"); + byte[] pack = DownloadSinglePack(url, system); + if (pack != null) + packsDictionary.Add(system.Value, pack); + } + + Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}"); + Console.WriteLine(); + + return packsDictionary; + } + + /// + /// Download a set of packs + /// + /// Base URL to download using + /// Systems to download packs for + /// Name of the pack that is downloading + /// Output directory to save data to + /// Named subfolder for the pack, used optionally + public void DownloadPacks(string url, RedumpSystem?[] systems, string title, string outDir, string subfolder) + { + Console.WriteLine($"Downloading {title}"); + foreach (var system in systems) + { + // If the system is invalid, we can't do anything + if (system == null || !system.IsAvailable()) + continue; + + // If we didn't have credentials + if (!LoggedIn && system.IsBanned()) + continue; + + // If the system is unknown, we can't do anything + string longName = system.LongName(); + if (string.IsNullOrWhiteSpace(longName)) + continue; + + Console.Write($"\r{longName}{new string(' ', Console.BufferWidth - longName.Length - 1)}"); + DownloadSinglePack(url, system, outDir, subfolder); + } + + Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}"); + Console.WriteLine(); + } + + /// + /// Move a tempfile to a new name unless it aleady exists, in which case, delete the tempfile + /// + /// Path to existing temporary file + /// Path to new output file + /// Output directory to save data to + /// Optional subfolder to append to the path + private static void MoveOrDelete(string tempfile, string newfile, string outDir, string subfolder) + { + if (!string.IsNullOrWhiteSpace(newfile)) + { + if (!string.IsNullOrWhiteSpace(subfolder)) + { + if (!Directory.Exists(Path.Combine(outDir, subfolder))) + Directory.CreateDirectory(Path.Combine(outDir, subfolder)); + + newfile = Path.Combine(subfolder, newfile); + } + + if (File.Exists(Path.Combine(outDir, newfile))) + File.Delete(tempfile); + else + File.Move(tempfile, Path.Combine(outDir, newfile)); + } + else + File.Delete(tempfile); + } + + #endregion + } +} + +#endif \ No newline at end of file