diff --git a/PKHeX.Core/Editing/ShowdownParsing.cs b/PKHeX.Core/Editing/ShowdownParsing.cs
new file mode 100644
index 000000000..26b063359
--- /dev/null
+++ b/PKHeX.Core/Editing/ShowdownParsing.cs
@@ -0,0 +1,183 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using static PKHeX.Core.Species;
+
+namespace PKHeX.Core
+{
+ ///
+ /// Logic for parsing details for objects.
+ ///
+ public static class ShowdownParsing
+ {
+ private static readonly string[] genderForms = { "", "F", "" };
+
+ ///
+ /// Gets the Form ID from the input .
+ ///
+ ///
+ ///
+ /// Species ID the form belongs to
+ /// Format the form name should appear in
+ /// Zero (base form) if no form matches the input string.
+ public static int GetFormFromString(string name, GameStrings strings, int species, int format)
+ {
+ if (name.Length == 0)
+ return 0;
+
+ string[] formStrings = FormConverter.GetFormList(species, strings.Types, strings.forms, genderForms, format);
+ return Math.Max(0, Array.FindIndex(formStrings, z => z.Contains(name)));
+ }
+
+ ///
+ /// Converts a Form ID to string.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static string GetStringFromForm(int form, GameStrings strings, int species, int format)
+ {
+ if (form <= 0)
+ return string.Empty;
+
+ var forms = FormConverter.GetFormList(species, strings.Types, strings.forms, genderForms, format);
+ return form >= forms.Length ? string.Empty : forms[form];
+ }
+
+ private const string MiniorFormName = "Meteor";
+
+ ///
+ /// Converts the PKHeX standard form name to Showdown's form name.
+ ///
+ /// Species ID
+ /// PKHeX form name
+ public static string GetShowdownFormName(int species, string form)
+ {
+ if (form.Length == 0)
+ {
+ return species switch
+ {
+ (int)Minior => MiniorFormName,
+ _ => form
+ };
+ }
+
+ return species switch
+ {
+ (int)Basculin when form is "Blue" => "Blue-Striped",
+ (int)Vivillon when form is "Poké Ball" => "Pokeball",
+ (int)Zygarde => form.Replace("-C", string.Empty).Replace("50%", string.Empty),
+ (int)Minior when form.StartsWith("M-") => MiniorFormName,
+ (int)Minior => form.Replace("C-", string.Empty),
+ (int)Necrozma when form is "Dusk" => $"{form}-Mane",
+ (int)Necrozma when form is "Dawn" => $"{form}-Wings",
+ (int)Polteageist or (int)Sinistea => form == "Antique" ? form : string.Empty,
+
+ (int)Furfrou or (int)Greninja or (int)Rockruff => string.Empty,
+
+ _ => Legal.Totem_USUM.Contains(species) && form == "Large"
+ ? Legal.Totem_Alolan.Contains(species) && species != (int)Mimikyu ? "Alola-Totem" : "Totem"
+ : form.Replace(' ', '-')
+ };
+ }
+
+ ///
+ /// Converts the Showdown form name to PKHeX's form name.
+ ///
+ /// Species ID
+ /// Showdown form name
+ /// Showdown ability ID
+ public static string SetShowdownFormName(int species, string form, int ability)
+ {
+ if (form.Length != 0)
+ form = form.Replace(' ', '-'); // inconsistencies are great
+
+ return species switch
+ {
+ (int)Basculin when form == "Blue-Striped" => "Blue",
+ (int)Vivillon when form == "Pokeball" => "Poké Ball",
+ (int)Necrozma when form == "Dusk-Mane" => "Dusk",
+ (int)Necrozma when form == "Dawn-Wings" => "Dawn",
+ (int)Toxtricity when form == "Low-Key" => "Low Key",
+ (int)Darmanitan when form == "Galar-Zen" => "Galar Zen",
+ (int)Minior when form != MiniorFormName => $"C-{form}",
+ (int)Zygarde when form == "Complete" => form,
+ (int)Zygarde when ability == 211 => $"{(string.IsNullOrWhiteSpace(form) ? "50%" : "10%")}-C",
+ (int)Greninja when ability == 210 => "Ash", // Battle Bond
+ (int)Rockruff when ability == 020 => "Dusk", // Rockruff-1
+ (int)Urshifu => form.Replace('-', ' '),
+
+ _ => Legal.Totem_USUM.Contains(species) && form.EndsWith("Totem") ? "Large" : form,
+ };
+ }
+
+ ///
+ /// Fetches data from the input .
+ ///
+ /// Raw lines containing numerous multi-line set data.
+ /// objects until is consumed.
+ public static IEnumerable GetShowdownSets(IEnumerable lines)
+ {
+ // exported sets always have >4 moves; new List will always require 1 resizing, allocate 2x to save 1 reallocation.
+ // intro, nature, ability, (ivs, evs, shiny, level) 4*moves
+ var setLines = new List(8);
+ foreach (var line in lines)
+ {
+ if (!string.IsNullOrWhiteSpace(line))
+ {
+ setLines.Add(line);
+ continue;
+ }
+ if (setLines.Count == 0)
+ continue;
+ yield return new ShowdownSet(setLines);
+ setLines.Clear();
+ }
+ if (setLines.Count != 0)
+ yield return new ShowdownSet(setLines);
+ }
+
+ ///
+ /// Converts the data into an importable set format for Pokémon Showdown.
+ ///
+ /// PKM to convert to string
+ /// Multi line set data
+ public static string GetShowdownText(PKM pkm)
+ {
+ if (pkm.Species == 0)
+ return string.Empty;
+ return new ShowdownSet(pkm).Text;
+ }
+
+ ///
+ /// Fetches ShowdownSet lines from the input data.
+ ///
+ /// Pokémon data to summarize.
+ /// Consumable list of lines.
+ public static IEnumerable GetShowdownSets(IEnumerable data) => data.Where(p => p.Species != 0).Select(GetShowdownText);
+
+ ///
+ /// Fetches ShowdownSet lines from the input data, and combines it into one string.
+ ///
+ /// Pokémon data to summarize.
+ /// Splitter between each set.
+ /// Single string containing all lines.
+ public static string GetShowdownSets(IEnumerable data, string separator) => string.Join(separator, GetShowdownSets(data));
+
+ ///
+ /// Gets a localized string preview of the provided .
+ ///
+ /// Pokémon data
+ /// Language code
+ /// Multi-line string
+ public static string GetLocalizedPreviewText(PKM pk, string language)
+ {
+ var set = new ShowdownSet(pk);
+ if (pk.Format <= 2) // Nature preview from IVs
+ set.Nature = Experience.GetNatureVC(pk.EXP);
+ return set.LocalizedText(language);
+ }
+ }
+}
diff --git a/PKHeX.Core/Editing/ShowdownSet.cs b/PKHeX.Core/Editing/ShowdownSet.cs
index 04497c3e4..ec45c7d78 100644
--- a/PKHeX.Core/Editing/ShowdownSet.cs
+++ b/PKHeX.Core/Editing/ShowdownSet.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using static PKHeX.Core.Species;
namespace PKHeX.Core
{
@@ -10,7 +11,6 @@ namespace PKHeX.Core
public sealed class ShowdownSet : IBattleTemplate
{
private static readonly string[] genders = {"M", "F", ""};
- private static readonly string[] genderForms = {"", "F", ""};
private static readonly string[] StatNames = { "HP", "Atk", "Def", "SpA", "SpD", "Spe" };
private static readonly string[] Splitters = {"\r\n", "\n"};
private static readonly string[] StatSplitters = { " / ", " " };
@@ -18,7 +18,7 @@ public sealed class ShowdownSet : IBattleTemplate
private static readonly string[] ItemSplit = {" @ "};
private static readonly char[] ParenJunk = { '[', ']', '(', ')' };
private static readonly ushort[] DashedSpecies = {782, 783, 784, 250, 032, 029}; // Kommo-o, Ho-Oh, Nidoran-M, Nidoran-F
- private const int MAX_SPECIES = (int)Core.Species.MAX_COUNT - 1;
+ private const int MAX_SPECIES = (int)MAX_COUNT - 1;
private static readonly GameStrings DefaultStrings = GameInfo.GetStrings(GameLanguage.DefaultLanguage);
///
@@ -103,15 +103,8 @@ private void LoadLines(IEnumerable lines)
ParseLines(lines);
- FormName = ConvertFormFromShowdown(FormName, Species, Ability);
- // Set Form
- if (FormName.Length == 0)
- {
- Form = 0;
- return;
- }
- string[] formStrings = FormConverter.GetFormList(Species, Strings.Types, Strings.forms, genderForms, Format);
- Form = Math.Max(0, Array.FindIndex(formStrings, z => z.Contains(FormName)));
+ FormName = ShowdownParsing.SetShowdownFormName(Species, FormName, Ability);
+ Form = ShowdownParsing.GetFormFromString(FormName, Strings, Species, Format);
}
private const int MaxMoveCount = 4;
@@ -233,7 +226,7 @@ public List GetSetLines()
var result = new List();
// First Line: Name, Nickname, Gender, Item
- var form = ConvertFormToShowdown(FormName, Species);
+ var form = ShowdownParsing.GetShowdownFormName(Species, FormName);
result.Add(GetStringFirstLine(form));
// IVs
@@ -269,9 +262,9 @@ private string GetStringFirstLine(string form)
string specForm = Strings.Species[Species];
if (form.Length != 0)
specForm += $"-{form.Replace("Mega ", "Mega-")}";
- else if (Species == (int)Core.Species.NidoranM)
+ else if (Species == (int)NidoranM)
specForm = specForm.Replace("♂", "-M");
- else if (Species == (int)Core.Species.NidoranF)
+ else if (Species == (int)NidoranF)
specForm = specForm.Replace("♀", "-F");
string result = GetSpeciesNickname(specForm);
@@ -322,18 +315,6 @@ private IEnumerable GetStringMoves()
}
}
- ///
- /// Converts the data into an importable set format for Pokémon Showdown.
- ///
- /// PKM to convert to string
- /// Multi line set data
- public static string GetShowdownText(PKM pkm)
- {
- if (pkm.Species == 0)
- return string.Empty;
- return new ShowdownSet(pkm).Text;
- }
-
///
/// Converts the data into an importable set format for Pokémon Showdown.
///
@@ -372,19 +353,7 @@ public ShowdownSet(PKM pkm)
}
}
- SetFormString(pkm.Form);
- }
-
- private void SetFormString(int index)
- {
- Form = index;
- if (index <= 0)
- {
- FormName = string.Empty;
- return;
- }
- var forms = FormConverter.GetFormList(Species, Strings.Types, Strings.forms, genderForms, Format);
- FormName = Form >= forms.Length ? string.Empty : forms[index];
+ FormName = ShowdownParsing.GetStringFromForm(Form = pkm.Form, Strings, Species, Format);
}
private void ParseFirstLine(string first)
@@ -428,15 +397,16 @@ bool TrySetItem(int format)
private void ParseFirstLineNoItem(string line)
{
// Gender Detection
- string last3 = line.Substring(line.Length - 3);
- if (last3 == "(M)" || last3 == "(F)")
+ if (line.EndsWith("(M)") || line.EndsWith("(F)"))
{
- Gender = last3.Substring(1, 1);
+ Gender = line[line.Length - 2].ToString();
line = line.Substring(0, line.Length - 3);
}
- else if (line.Contains(Strings.Species[(int)Core.Species.Meowstic]) || line.Contains(Strings.Species[(int)Core.Species.Indeedee])) // Meowstic Edge Case with no gender provided
+ else // Meowstic Edge Case with no gender provided
{
- Gender = "M";
+ var s = Strings.Species;
+ if (line.Contains(s[(int)Meowstic]) || line.Contains(s[(int)Indeedee]))
+ Gender = "M";
}
// Nickname Detection
@@ -446,25 +416,27 @@ private void ParseFirstLineNoItem(string line)
ParseSpeciesForm(line);
}
- private bool ParseSpeciesForm(string spec)
+ private const string Gmax = "-Gmax";
+
+ private bool ParseSpeciesForm(string speciesLine)
{
- spec = spec.Trim();
- if (spec.EndsWith(Gmax))
+ speciesLine = speciesLine.Trim();
+ if (speciesLine.EndsWith(Gmax))
{
CanGigantamax = true;
- spec = spec.Substring(0, spec.Length - Gmax.Length);
+ speciesLine = speciesLine.Substring(0, speciesLine.Length - Gmax.Length);
}
- if ((Species = StringUtil.FindIndexIgnoreCase(Strings.specieslist, spec)) >= 0) // success, nothing else!
+ if ((Species = StringUtil.FindIndexIgnoreCase(Strings.specieslist, speciesLine)) >= 0) // success, nothing else!
return true;
// Form string present.
- int end = spec.LastIndexOf('-');
+ int end = speciesLine.LastIndexOf('-');
if (end < 0)
return false;
- Species = StringUtil.FindIndexIgnoreCase(Strings.specieslist, spec.Substring(0, end));
- FormName = spec.Substring(end + 1);
+ Species = StringUtil.FindIndexIgnoreCase(Strings.specieslist, speciesLine.Substring(0, end));
+ FormName = speciesLine.Substring(end + 1);
if (Species >= 0)
return true;
@@ -473,19 +445,19 @@ private bool ParseSpeciesForm(string spec)
foreach (var e in DashedSpecies)
{
var sn = Strings.Species[e];
- if (!spec.StartsWith(sn.Replace("♂", "-M").Replace("♀", "-F")))
+ if (!speciesLine.StartsWith(sn.Replace("♂", "-M").Replace("♀", "-F")))
continue;
Species = e;
- FormName = spec.Substring(sn.Length);
+ FormName = speciesLine.Substring(sn.Length);
return true;
}
// Version Megas
- end = spec.LastIndexOf('-', Math.Max(0, end - 1));
+ end = speciesLine.LastIndexOf('-', Math.Max(0, end - 1));
if (end < 0)
return false;
- Species = StringUtil.FindIndexIgnoreCase(Strings.specieslist, spec.Substring(0, end));
- FormName = spec.Substring(end + 1);
+ Species = StringUtil.FindIndexIgnoreCase(Strings.specieslist, speciesLine.Substring(0, end));
+ FormName = speciesLine.Substring(end + 1);
return Species >= 0;
}
@@ -584,110 +556,6 @@ private void ParseLineIVs(string line)
IVs = IVsSpeedFirst;
}
- private const string Minior = "Meteor";
- private const string Gmax = "-Gmax";
-
- private static string ConvertFormToShowdown(string form, int spec)
- {
- if (form.Length == 0)
- {
- return spec switch
- {
- (int)Core.Species.Minior => Minior,
- _ => form
- };
- }
-
- switch (spec)
- {
- case (int)Core.Species.Basculin when form == "Blue":
- return "Blue-Striped";
- case (int)Core.Species.Vivillon when form == "Poké Ball":
- return "Pokeball";
- case (int)Core.Species.Zygarde:
- form = form.Replace("-C", string.Empty);
- return form.Replace("50%", string.Empty);
- case (int)Core.Species.Minior:
- if (form.StartsWith("M-"))
- return Minior;
- return form.Replace("C-", string.Empty);
- case (int)Core.Species.Necrozma when form == "Dusk":
- return $"{form}-Mane";
- case (int)Core.Species.Necrozma when form == "Dawn":
- return $"{form}-Wings";
-
- case (int)Core.Species.Furfrou:
- case (int)Core.Species.Greninja:
- case (int)Core.Species.Rockruff:
- return string.Empty;
-
- case (int)Core.Species.Polteageist:
- case (int)Core.Species.Sinistea:
- return form == "Antique" ? form : string.Empty;
-
- default:
- if (Legal.Totem_USUM.Contains(spec) && form == "Large")
- return Legal.Totem_Alolan.Contains(spec) && spec != (int)Core.Species.Mimikyu ? "Alola-Totem" : "Totem";
- return form.Replace(' ', '-');
- }
- }
-
- private static string ConvertFormFromShowdown(string form, int spec, int ability)
- {
- if (form.Length != 0)
- form = form.Replace(' ', '-'); // inconsistencies are great
-
- switch (spec)
- {
- case (int)Core.Species.Basculin when form is "Blue-Striped":
- return "Blue";
- case (int)Core.Species.Greninja when ability == 210:
- return "Ash"; // Battle Bond
- case (int)Core.Species.Vivillon when form is "Pokeball":
- return "Poké Ball";
-
- // Zygarde
- case (int)Core.Species.Zygarde when form.Length == 0:
- return ability == 211 ? "50%-C" : "50%";
- case (int)Core.Species.Zygarde when form is "Complete":
- return form;
- case (int)Core.Species.Zygarde when ability == 211:
- return "-C"; // Power Construct
-
- case (int)Core.Species.Rockruff when ability == 020: // Rockruff-1
- return "Dusk";
-
- // Minior
- case (int)Core.Species.Minior when form.Length != 0 && form != Minior:
- return $"C-{form}";
-
- // Necrozma
- case (int)Core.Species.Necrozma when form is "Dusk-Mane" or "Dusk Mane":
- return "Dusk";
- case (int)Core.Species.Necrozma when form is "Dawn-Wings" or "Dawn Wings":
- return "Dawn";
-
- // Toxtricity
- case (int)Core.Species.Toxtricity when form is "Low-Key":
- return "Low Key";
-
- // Darmanitan
- case (int)Core.Species.Darmanitan:
- if (form is "Galar-Zen")
- return "Galar Zen";
- return form;
-
- // Urshifu
- case (int)Core.Species.Urshifu:
- return form.Replace('-', ' ');
-
- default:
- if (Legal.Totem_USUM.Contains(spec) && form.EndsWith("Totem"))
- return "Large";
- return form;
- }
- }
-
private static string RemoveAll(string original, char[] remove) => string.Concat(original.Where(z => !remove.Contains(z)));
private static string[] SplitLineStats(string line)
@@ -698,60 +566,5 @@ private static string[] SplitLineStats(string line)
.Replace("SDef", "SpD").Replace("Sp Def", "SpD")
.Replace("Spd", "Spe").Replace("Speed", "Spe").Split(StatSplitters, StringSplitOptions.None);
}
-
- ///
- /// Fetches data from the input .
- ///
- /// Raw lines containing numerous multi-line set data.
- /// objects until is consumed.
- public static IEnumerable GetShowdownSets(IEnumerable lines)
- {
- // exported sets always have >4 moves; new List will always require 1 resizing, allocate 2x to save 1 reallocation.
- // intro, nature, ability, (ivs, evs, shiny, level) 4*moves
- var setLines = new List(8);
- foreach (var line in lines)
- {
- if (!string.IsNullOrWhiteSpace(line))
- {
- setLines.Add(line);
- continue;
- }
- if (setLines.Count == 0)
- continue;
- yield return new ShowdownSet(setLines);
- setLines.Clear();
- }
- if (setLines.Count != 0)
- yield return new ShowdownSet(setLines);
- }
-
- ///
- /// Fetches ShowdownSet lines from the input data.
- ///
- /// Pokémon data to summarize.
- /// Consumable list of lines.
- public static IEnumerable GetShowdownSets(IEnumerable data) => data.Where(p => p.Species != 0).Select(GetShowdownText);
-
- ///
- /// Fetches ShowdownSet lines from the input data, and combines it into one string.
- ///
- /// Pokémon data to summarize.
- /// Splitter between each set.
- /// Single string containing all lines.
- public static string GetShowdownSets(IEnumerable data, string separator) => string.Join(separator, GetShowdownSets(data));
-
- ///
- /// Gets a localized string preview of the provided .
- ///
- /// Pokémon data
- /// Language code
- /// Multi-line string
- public static string GetLocalizedPreviewText(PKM pk, string language)
- {
- var set = new ShowdownSet(pk);
- if (pk.Format <= 2) // Nature preview from IVs
- set.Nature = Experience.GetNatureVC(pk.EXP);
- return set.LocalizedText(language);
- }
}
}
diff --git a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs
index 6542e170f..2de1f0277 100644
--- a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs
+++ b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs
@@ -1165,7 +1165,7 @@ public void ClickShowdownExportCurrentBox(object sender, EventArgs e)
private static void ExportShowdownText(SaveFile sav, string success, Func> fetch)
{
var list = fetch(sav);
- var result = ShowdownSet.GetShowdownSets(list, Environment.NewLine + Environment.NewLine);
+ var result = ShowdownParsing.GetShowdownSets(list, Environment.NewLine + Environment.NewLine);
if (string.IsNullOrWhiteSpace(result))
return;
if (WinFormsUtil.SetClipboardText(result))
diff --git a/PKHeX.WinForms/Controls/Slots/SummaryPreviewer.cs b/PKHeX.WinForms/Controls/Slots/SummaryPreviewer.cs
index c953fb743..098b1ac6b 100644
--- a/PKHeX.WinForms/Controls/Slots/SummaryPreviewer.cs
+++ b/PKHeX.WinForms/Controls/Slots/SummaryPreviewer.cs
@@ -15,7 +15,7 @@ public void Show(Control pb, PKM pk)
Clear();
return;
}
- var text = ShowdownSet.GetLocalizedPreviewText(pk, Settings.Default.Language);
+ var text = ShowdownParsing.GetLocalizedPreviewText(pk, Settings.Default.Language);
ShowSet.SetToolTip(pb, text);
}
diff --git a/PKHeX.WinForms/MainWindow/Main.cs b/PKHeX.WinForms/MainWindow/Main.cs
index 4363dc199..4327f947f 100644
--- a/PKHeX.WinForms/MainWindow/Main.cs
+++ b/PKHeX.WinForms/MainWindow/Main.cs
@@ -524,7 +524,7 @@ private void ClickShowdownExportPKM(object sender, EventArgs e)
}
var pk = PreparePKM();
- var text = ShowdownSet.GetShowdownText(pk);
+ var text = ShowdownParsing.GetShowdownText(pk);
bool success = WinFormsUtil.SetClipboardText(text);
if (!success || !Clipboard.GetText().Equals(text))
WinFormsUtil.Alert(MsgClipboardFailWrite, MsgSimulatorExportFail);
diff --git a/Tests/PKHeX.Core.Tests/Simulator/ShowdownSetTests.cs b/Tests/PKHeX.Core.Tests/Simulator/ShowdownSetTests.cs
index ca2865b8c..13c609744 100644
--- a/Tests/PKHeX.Core.Tests/Simulator/ShowdownSetTests.cs
+++ b/Tests/PKHeX.Core.Tests/Simulator/ShowdownSetTests.cs
@@ -149,13 +149,13 @@ public void SimulatorParseMultiple()
{
var text = string.Join("\r\n\r\n", Sets);
var lines = text.Split(new[] {"\r\n", "\n"}, StringSplitOptions.None);
- var sets = ShowdownSet.GetShowdownSets(lines);
+ var sets = ShowdownParsing.GetShowdownSets(lines);
Assert.True(sets.Count() == Sets.Length);
- sets = ShowdownSet.GetShowdownSets(Enumerable.Empty());
+ sets = ShowdownParsing.GetShowdownSets(Enumerable.Empty());
Assert.True(!sets.Any());
- sets = ShowdownSet.GetShowdownSets(new [] {"", " ", " "});
+ sets = ShowdownParsing.GetShowdownSets(new [] {"", " ", " "});
Assert.True(!sets.Any());
}