From d772a829533e4168d3c2a960d9d84c67b57960ba Mon Sep 17 00:00:00 2001 From: Kurt Date: Thu, 17 May 2018 22:43:07 -0700 Subject: [PATCH] Move batch editor logic to core previous hurdle a year ago was propertyinfo fetching not looking at the base class's properties; dig deeper for all properties to mimic existing code for netframework end result is batch editing now possible without gui --- PKHeX.Core/Editing/Bulk/BatchEditing.cs | 308 ++++++++++++ PKHeX.Core/Editing/Bulk/BatchEditor.cs | 51 ++ PKHeX.Core/Editing/Bulk/ModifyResult.cs | 11 + PKHeX.Core/Editing/Bulk/PKMInfo.cs | 16 + PKHeX.Core/Editing/Bulk/StringInstruction.cs | 61 +++ .../Editing/Bulk/StringInstructionSet.cs | 34 ++ PKHeX.Core/PKM/PKM.cs | 7 +- PKHeX.Core/Saves/SaveFile.cs | 2 + PKHeX.Core/Util/ReflectUtil.cs | 82 +-- .../Subforms/PKM Editors/BatchEditor.cs | 472 +----------------- .../Subforms/PKM Editors/RibbonEditor.cs | 2 +- .../PKM Editors/SuperTrainingEditor.cs | 2 +- PKHeX.WinForms/Subforms/SAV_Database.cs | 11 +- PKHeX.WinForms/Subforms/SAV_MysteryGiftDB.cs | 40 +- 14 files changed, 580 insertions(+), 519 deletions(-) create mode 100644 PKHeX.Core/Editing/Bulk/BatchEditing.cs create mode 100644 PKHeX.Core/Editing/Bulk/BatchEditor.cs create mode 100644 PKHeX.Core/Editing/Bulk/ModifyResult.cs create mode 100644 PKHeX.Core/Editing/Bulk/PKMInfo.cs create mode 100644 PKHeX.Core/Editing/Bulk/StringInstruction.cs create mode 100644 PKHeX.Core/Editing/Bulk/StringInstructionSet.cs diff --git a/PKHeX.Core/Editing/Bulk/BatchEditing.cs b/PKHeX.Core/Editing/Bulk/BatchEditing.cs new file mode 100644 index 000000000..345c8787f --- /dev/null +++ b/PKHeX.Core/Editing/Bulk/BatchEditing.cs @@ -0,0 +1,308 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Reflection; + +using static PKHeX.Core.MessageStrings; + +namespace PKHeX.Core +{ + public static class BatchEditing + { + public static readonly Type[] Types = + { + typeof (PK7), typeof (PK6), typeof (PK5), typeof (PK4), typeof(BK4), + typeof (PK3), typeof (XK3), typeof (CK3), + typeof (PK2), typeof (PK1), + }; + public static readonly string[] CustomProperties = { PROP_LEGAL }; + public static readonly string[][] Properties = GetPropArray(); + + private static readonly Dictionary[] Props = Types.Select(z => ReflectUtil.GetAllPropertyInfoCanWritePublic(z) + .GroupBy(p => p.Name).Select(g => g.First()).ToDictionary(p => p.Name)) + .ToArray(); + + private const string CONST_RAND = "$rand"; + private const string CONST_SHINY = "$shiny"; + private const string CONST_SUGGEST = "$suggest"; + private const string CONST_BYTES = "$[]"; + + private const string PROP_LEGAL = "Legal"; + + private static string[][] GetPropArray() + { + var p = new string[Types.Length][]; + for (int i = 0; i < p.Length; i++) + { + var pz = ReflectUtil.GetPropertiesCanWritePublic(Types[i]).Distinct(); + p[i] = pz.Concat(CustomProperties).OrderBy(a => a).ToArray(); + } + + // Properties for any PKM + var any = ReflectUtil.GetPropertiesCanWritePublic(typeof(PK1)).Distinct().Union(p.SelectMany(a => a)).OrderBy(a => a).ToArray(); + // Properties shared by all PKM + var all = p.Aggregate(new HashSet(p[0]), (h, e) => { h.IntersectWith(e); return h; }).OrderBy(a => a).ToArray(); + + var p1 = new string[Types.Length + 2][]; + Array.Copy(p, 0, p1, 1, p.Length); + p1[0] = any; + p1[p1.Length - 1] = all; + + return p1; + } + + public static bool TryGetHasProperty(PKM pk, string name, out PropertyInfo pi) + { + var props = Props[Array.IndexOf(Types, pk.GetType())]; + return props.TryGetValue(name, out pi); + } + public static bool TryGetPropertyValue(PKM pk, string name, out object value) + { + if (TryGetHasProperty(pk, name, out var pi)) + { + value = pi.GetValue(pk); + return true; + } + value = null; + return false; + } + + public static string GetPropertyType(string propertyName, int typeIndex = 0) + { + if (CustomProperties.Contains(propertyName)) + return "Custom"; + + if (typeIndex == 0) // Any + { + foreach (var p in Props) + if (p.TryGetValue(propertyName, out var pi)) + return pi.PropertyType.Name; + return null; + } + + int index = typeIndex == Props.Length - 1 ? 0 : typeIndex - 1; // All vs Specific + var pr = Props[typeIndex - 1]; + if (!pr.TryGetValue(propertyName, out var info)) + return null; + return info.PropertyType.Name; + } + + public static void ScreenStrings(IEnumerable il) + { + foreach (var i in il.Where(i => !i.PropertyValue.All(char.IsDigit))) + { + string pv = i.PropertyValue; + if (pv.StartsWith("$") && !pv.StartsWith(CONST_BYTES) && pv.Contains(',')) + i.SetRandRange(pv); + + SetInstructionScreenedValue(i); + } + } + + private static void SetInstructionScreenedValue(StringInstruction i) + { + switch (i.PropertyName) + { + case nameof(PKM.Species): i.SetScreenedValue(GameInfo.Strings.specieslist); return; + case nameof(PKM.HeldItem): i.SetScreenedValue(GameInfo.Strings.itemlist); return; + case nameof(PKM.Ability): i.SetScreenedValue(GameInfo.Strings.abilitylist); return; + case nameof(PKM.Nature): i.SetScreenedValue(GameInfo.Strings.natures); return; + case nameof(PKM.Ball): i.SetScreenedValue(GameInfo.Strings.balllist); return; + case nameof(PKM.Move1): + case nameof(PKM.Move2): + case nameof(PKM.Move3): + case nameof(PKM.Move4): + case nameof(PKM.RelearnMove1): + case nameof(PKM.RelearnMove2): + case nameof(PKM.RelearnMove3): + case nameof(PKM.RelearnMove4): + i.SetScreenedValue(GameInfo.Strings.movelist); return; + } + } + + internal static ModifyResult TryModifyPKM(PKM PKM, IEnumerable Filters, IEnumerable Instructions) + { + if (!PKM.ChecksumValid || PKM.Species == 0) + return ModifyResult.Invalid; + + PKMInfo info = new PKMInfo(PKM); + var pi = Props[Array.IndexOf(Types, PKM.GetType())]; + foreach (var cmd in Filters) + { + try + { + var filter = IsPKMFiltered(cmd, info, pi); + if (filter != ModifyResult.None) + return filter; // why it was filtered out + } + catch (Exception ex) { Debug.WriteLine(MsgBEModifyFailCompare + " " + ex.Message, cmd.PropertyName, cmd.PropertyValue); } + } + + ModifyResult result = ModifyResult.Modified; + foreach (var cmd in Instructions) + { + try + { + var tmp = SetPKMProperty(PKM, info, cmd, pi); + if (result != ModifyResult.Modified) + result = tmp; + } + catch (Exception ex) { Debug.WriteLine(MsgBEModifyFail + " " + ex.Message, cmd.PropertyName, cmd.PropertyValue); } + } + return result; + } + private static ModifyResult SetPKMProperty(PKM PKM, PKMInfo info, StringInstruction cmd, Dictionary props) + { + if (cmd.PropertyValue.StartsWith(CONST_BYTES)) + return SetByteArrayProperty(PKM, cmd) + ? ModifyResult.Modified + : ModifyResult.Error; + + if (cmd.PropertyValue == CONST_SUGGEST) + return SetSuggestedPKMProperty(cmd, info) + ? ModifyResult.Modified + : ModifyResult.Error; + + if (SetComplexProperty(PKM, cmd)) + return ModifyResult.Modified; + + object val = cmd.Random ? (object)cmd.RandomValue : cmd.PropertyValue; + if (TryGetHasProperty(PKM, cmd.PropertyName, out var pi)) + { + ReflectUtil.SetValue(pi, PKM, val); + return ModifyResult.Modified; + } + return ModifyResult.Error; + } + private static ModifyResult IsPKMFiltered(StringInstruction cmd, PKMInfo info, Dictionary props) + { + if (cmd.PropertyName == PROP_LEGAL) + { + if (!bool.TryParse(cmd.PropertyValue, out bool legal)) + return ModifyResult.Error; + if (legal == info.Legal == cmd.Evaluator) + return ModifyResult.None; + return ModifyResult.Filtered; + } + + if (!props.TryGetValue(cmd.PropertyName, out var pi)) + return ModifyResult.Filtered; + if (pi.IsValueEqual(info.pkm, cmd.PropertyValue) != cmd.Evaluator) + return ModifyResult.Filtered; + return ModifyResult.None; + } + private static bool SetSuggestedPKMProperty(StringInstruction cmd, PKMInfo info) + { + var PKM = info.pkm; + switch (cmd.PropertyName) + { + case nameof(PKM.HyperTrainFlags): + PKM.HyperTrainFlags = GetSuggestedHyperTrainingStatus(PKM); + return true; + case nameof(PKM.RelearnMoves): + PKM.RelearnMoves = info.SuggestedRelearn; + return true; + case nameof(PKM.Met_Location): + var encounter = info.SuggestedEncounter; + if (encounter == null) + return false; + + int level = encounter.Level; + int location = encounter.Location; + int minlvl = Legal.GetLowestLevel(PKM, encounter.LevelMin); + + PKM.Met_Level = level; + PKM.Met_Location = location; + PKM.CurrentLevel = Math.Max(minlvl, level); + + return true; + + case nameof(PKM.Moves): + var moves = info.SuggestedMoves; + Util.Shuffle(moves); + PKM.SetMoves(moves); + return true; + + default: + return false; + } + } + + private static int GetSuggestedHyperTrainingStatus(PKM pkm) + { + if (pkm.Format < 7 || pkm.CurrentLevel != 100) + return 0; + + int val = 0; + if (pkm.IV_HP != 31) + val |= 1 << 0; + if (pkm.IV_ATK < 31 && pkm.IV_ATK > 1) + val |= 1 << 1; + if (pkm.IV_DEF != 31) + val |= 1 << 2; + if (pkm.IV_SPE < 31 && pkm.IV_SPE > 1) + val |= 1 << 3; + if (pkm.IV_SPA != 31) + val |= 1 << 4; + if (pkm.IV_SPD != 31) + val |= 1 << 5; + return val; + } + + private static bool SetByteArrayProperty(PKM PKM, StringInstruction cmd) + { + switch (cmd.PropertyName) + { + case nameof(PKM.Nickname_Trash): + PKM.Nickname_Trash = string2arr(cmd.PropertyValue); + return true; + case nameof(PKM.OT_Trash): + PKM.OT_Trash = string2arr(cmd.PropertyValue); + return true; + default: + return false; + } + byte[] string2arr(string str) => str.Substring(CONST_BYTES.Length).Split(',').Select(z => Convert.ToByte(z.Trim(), 16)).ToArray(); + } + private static bool SetComplexProperty(PKM PKM, StringInstruction cmd) + { + if (cmd.PropertyName == nameof(PKM.MetDate)) + PKM.MetDate = DateTime.ParseExact(cmd.PropertyValue, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None); + else if (cmd.PropertyName == nameof(PKM.EggMetDate)) + PKM.EggMetDate = DateTime.ParseExact(cmd.PropertyValue, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None); + else if (cmd.PropertyName == nameof(PKM.EncryptionConstant) && cmd.PropertyValue == CONST_RAND) + PKM.EncryptionConstant = Util.Rand32(); + else if ((cmd.PropertyName == nameof(PKM.Ability) || cmd.PropertyName == nameof(PKM.AbilityNumber)) && cmd.PropertyValue.StartsWith("$")) + PKM.RefreshAbility(Convert.ToInt16(cmd.PropertyValue[1]) - 0x30); + else if (cmd.PropertyName == nameof(PKM.PID) && cmd.PropertyValue == CONST_RAND) + PKM.SetPIDGender(PKM.Gender); + else if (cmd.PropertyName == nameof(PKM.EncryptionConstant) && cmd.PropertyValue == nameof(PKM.PID)) + PKM.EncryptionConstant = PKM.PID; + else if (cmd.PropertyName == nameof(PKM.PID) && cmd.PropertyValue == CONST_SHINY) + PKM.SetShinyPID(); + else if (cmd.PropertyName == nameof(PKM.Species) && cmd.PropertyValue == "0") + PKM.Data = new byte[PKM.Data.Length]; + else if (cmd.PropertyName.StartsWith("IV") && cmd.PropertyValue == CONST_RAND) + SetRandomIVs(PKM, cmd); + else if (cmd.PropertyName == nameof(PKM.IsNicknamed) && string.Equals(cmd.PropertyValue, "false", StringComparison.OrdinalIgnoreCase)) + { PKM.IsNicknamed = false; PKM.Nickname = PKX.GetSpeciesNameGeneration(PKM.Species, PKM.Language, PKM.Format); } + else + return false; + + return true; + } + private static void SetRandomIVs(PKM PKM, StringInstruction cmd) + { + if (cmd.PropertyName == nameof(PKM.IVs)) + { + PKM.SetRandomIVs(); + return; + } + + if (TryGetHasProperty(PKM, cmd.PropertyName, out var pi)) + ReflectUtil.SetValue(pi, PKM, Util.Rand32() & PKM.MaxIV); + } + } +} diff --git a/PKHeX.Core/Editing/Bulk/BatchEditor.cs b/PKHeX.Core/Editing/Bulk/BatchEditor.cs new file mode 100644 index 000000000..49a579892 --- /dev/null +++ b/PKHeX.Core/Editing/Bulk/BatchEditor.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using static PKHeX.Core.MessageStrings; + +namespace PKHeX.Core +{ + public class BatchEditor + { + private int Modified { get; set; } + private int Iterated { get; set; } + private int Errored { get; set; } + + public bool ProcessPKM(PKM pkm, IEnumerable Filters, IEnumerable Instructions) + { + if (pkm.Species <= 0) + return false; + if (!pkm.Valid || pkm.Locked) + { + Iterated++; + var reason = pkm.Locked ? "Locked." : "Not Valid."; + Debug.WriteLine($"{MsgBEModifyFailBlocked} {reason}"); + return false; + } + + var r = BatchEditing.TryModifyPKM(pkm, Filters, Instructions); + if (r != ModifyResult.Invalid) + Iterated++; + if (r == ModifyResult.Error) + Errored++; + if (r != ModifyResult.Modified) + return false; + + pkm.RefreshChecksum(); + Modified++; + return true; + } + + public string GetEditorResults(ICollection sets) + { + int ctr = Modified / sets.Count; + int len = Iterated / sets.Count; + string maybe = sets.Count == 1 ? string.Empty : "~"; + string result = string.Format(MsgBEModifySuccess, maybe, ctr, len); + if (Errored > 0) + result += $"{Environment.NewLine}{maybe}" + string.Format(MsgBEModifyFailError, Errored); + return result; + } + } +} diff --git a/PKHeX.Core/Editing/Bulk/ModifyResult.cs b/PKHeX.Core/Editing/Bulk/ModifyResult.cs new file mode 100644 index 000000000..59cf92af0 --- /dev/null +++ b/PKHeX.Core/Editing/Bulk/ModifyResult.cs @@ -0,0 +1,11 @@ +namespace PKHeX.Core +{ + internal enum ModifyResult + { + None, + Invalid, + Error, + Filtered, + Modified, + } +} diff --git a/PKHeX.Core/Editing/Bulk/PKMInfo.cs b/PKHeX.Core/Editing/Bulk/PKMInfo.cs new file mode 100644 index 000000000..cc878a242 --- /dev/null +++ b/PKHeX.Core/Editing/Bulk/PKMInfo.cs @@ -0,0 +1,16 @@ +namespace PKHeX.Core +{ + internal sealed class PKMInfo + { + internal PKM pkm { get; } + internal PKMInfo(PKM pk) { pkm = pk; } + + private LegalityAnalysis la; + private LegalityAnalysis Legality => la ?? (la = new LegalityAnalysis(pkm)); + + internal bool Legal => Legality.Valid; + internal int[] SuggestedRelearn => Legality.GetSuggestedRelearn(); + internal int[] SuggestedMoves => Legality.GetSuggestedMoves(tm: true, tutor: true, reminder: false); + internal EncounterStatic SuggestedEncounter => Legality.GetSuggestedMetInfo(); + } +} \ No newline at end of file diff --git a/PKHeX.Core/Editing/Bulk/StringInstruction.cs b/PKHeX.Core/Editing/Bulk/StringInstruction.cs new file mode 100644 index 000000000..b7b19fee9 --- /dev/null +++ b/PKHeX.Core/Editing/Bulk/StringInstruction.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace PKHeX.Core +{ + public class StringInstruction + { + public string PropertyName { get; set; } + public string PropertyValue { get; set; } + public bool Evaluator { get; set; } + public void SetScreenedValue(string[] arr) + { + int index = Array.IndexOf(arr, PropertyValue); + PropertyValue = index > -1 ? index.ToString() : PropertyValue; + } + + // Extra Functionality + private int Min, Max; + public bool Random { get; private set; } + public int RandomValue => Util.Rand.Next(Min, Max + 1); + public void SetRandRange(string pv) + { + string str = pv.Substring(1); + var split = str.Split(','); + int.TryParse(split[0], out Min); + int.TryParse(split[1], out Max); + + if (Min == Max) + { + PropertyValue = Min.ToString(); + Debug.WriteLine(PropertyName + " randomization range Min/Max same?"); + } + else + Random = true; + } + + public static IEnumerable GetFilters(IEnumerable lines) + { + var raw = GetRelevantStrings(lines, '!', '='); + return from line in raw + let eval = line[0] == '=' + let split = line.Substring(1).Split('=') + where split.Length == 2 && !string.IsNullOrWhiteSpace(split[0]) + select new StringInstruction { PropertyName = split[0], PropertyValue = split[1], Evaluator = eval }; + } + public static IEnumerable GetInstructions(IEnumerable lines) + { + var raw = GetRelevantStrings(lines, '.').Select(line => line.Substring(1)); + return from line in raw + select line.Split('=') into split + where split.Length == 2 + select new StringInstruction { PropertyName = split[0], PropertyValue = split[1] }; + } + private static IEnumerable GetRelevantStrings(IEnumerable lines, params char[] pieces) + { + return lines.Where(line => !string.IsNullOrEmpty(line) && pieces.Any(z => z == line[0])); + } + } +} diff --git a/PKHeX.Core/Editing/Bulk/StringInstructionSet.cs b/PKHeX.Core/Editing/Bulk/StringInstructionSet.cs new file mode 100644 index 000000000..ea6790167 --- /dev/null +++ b/PKHeX.Core/Editing/Bulk/StringInstructionSet.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; + +namespace PKHeX.Core +{ + + // Utility Methods + + public class StringInstructionSet + { + public IList Filters { get; private set; } + public IList Instructions { get; private set; } + + private const string SetSeparator = ";"; + public static IEnumerable GetBatchSets(string[] lines) + { + int start = 0; + while (start < lines.Length) + { + var list = lines.Skip(start).TakeWhile(_ => !lines[start++].StartsWith(SetSeparator)).ToList(); + yield return GetBatchSet(list); + } + } + + private static StringInstructionSet GetBatchSet(IList set) + { + return new StringInstructionSet + { + Filters = StringInstruction.GetFilters(set).ToList(), + Instructions = StringInstruction.GetInstructions(set).ToList(), + }; + } + } +} diff --git a/PKHeX.Core/PKM/PKM.cs b/PKHeX.Core/PKM/PKM.cs index 31056184f..0e93465f8 100644 --- a/PKHeX.Core/PKM/PKM.cs +++ b/PKHeX.Core/PKM/PKM.cs @@ -995,9 +995,10 @@ public void TransferPropertiesWithReflection(PKM Destination) var shared = DestinationProperties.Intersect(SourceProperties); foreach (string property in shared) { - var prop = ReflectUtil.GetValue(this, property); - if (prop != null && !(prop is byte[])) - ReflectUtil.SetValue(Destination, property, prop); + BatchEditing.TryGetHasProperty(Destination, property, out var src); + var prop = src.GetValue(this); + if (prop != null && !(prop is byte[]) && BatchEditing.TryGetHasProperty(Destination, property, out var pi)) + ReflectUtil.SetValue(pi, Destination, prop); } } diff --git a/PKHeX.Core/Saves/SaveFile.cs b/PKHeX.Core/Saves/SaveFile.cs index efd2e1ec7..06f586e1e 100644 --- a/PKHeX.Core/Saves/SaveFile.cs +++ b/PKHeX.Core/Saves/SaveFile.cs @@ -617,6 +617,8 @@ public void ClearBoxes(int BoxStart = 0, int BoxEnd = -1, Func delete int offset = GetBoxOffset(i); for (int p = 0; p < BoxSlotCount; p++) { + if (IsSlotLocked(i, p)) + continue; var ofs = offset + SIZE_STORED * p; if (deleteCriteria != null) { diff --git a/PKHeX.Core/Util/ReflectUtil.cs b/PKHeX.Core/Util/ReflectUtil.cs index 0c83a6d72..34b844a78 100644 --- a/PKHeX.Core/Util/ReflectUtil.cs +++ b/PKHeX.Core/Util/ReflectUtil.cs @@ -8,53 +8,44 @@ namespace PKHeX.Core { public static class ReflectUtil { - public static bool IsValueEqual(object obj, string propertyName, object value) + public static bool IsValueEqual(this PropertyInfo pi, object obj, object value) { - PropertyInfo pi = obj.GetType().GetTypeInfo().GetDeclaredProperty(propertyName); - if (pi == null) - return false; var v = pi.GetValue(obj, null); var c = ConvertValue(value, pi.PropertyType); return v.Equals(c); } - public static void SetValue(object obj, string propertyName, object value) + public static void SetValue(PropertyInfo pi, object obj, object value) { - PropertyInfo pi = obj.GetType().GetTypeInfo().GetDeclaredProperty(propertyName); - pi.SetValue(obj, ConvertValue(value, pi.PropertyType), null); - } - - public static object GetValue(object obj, string propertyName) - { - PropertyInfo pi = obj.GetType().GetTypeInfo().GetDeclaredProperty(propertyName); - return pi.GetValue(obj, null); + var c = ConvertValue(value, pi.PropertyType); + pi.SetValue(obj, c, null); } + public static object GetValue(object obj, string name) => GetPropertyInfo(obj?.GetType().GetTypeInfo(), name)?.GetValue(obj); + public static void SetValue(object obj, string name, object value) => GetPropertyInfo(obj?.GetType().GetTypeInfo(), name)?.SetValue(obj, value, null); public static object GetValue(Type t, string propertyName) => t.GetTypeInfo().GetDeclaredProperty(propertyName).GetValue(null); public static void SetValue(Type t, string propertyName, object value) => t.GetTypeInfo().GetDeclaredProperty(propertyName).SetValue(null, value); public static IEnumerable GetPropertiesStartWithPrefix(Type type, string prefix) { - return type.GetTypeInfo().DeclaredProperties + return type.GetTypeInfo().GetAllTypeInfo().SelectMany(GetAllProperties) .Where(p => p.Name.StartsWith(prefix, StringComparison.Ordinal)) .Select(p => p.Name); } public static IEnumerable GetPropertiesCanWritePublic(Type type) { - return type.GetTypeInfo().DeclaredProperties - .Where(p => p.CanWrite && p.SetMethod.IsPublic) - .Select(p => p.Name); + return GetAllPropertyInfoCanWritePublic(type).Select(p => p.Name); } + public static IEnumerable GetAllPropertyInfoCanWritePublic(Type type) + { + return type.GetTypeInfo().GetAllTypeInfo().SelectMany(GetAllProperties) + .Where(p => p.CanWrite && p.SetMethod.IsPublic); + } + public static IEnumerable GetPropertiesCanWritePublicDeclared(Type type) { - return GetPropertiesCanWritePublic(type); - } - public static bool HasProperty(this Type type, string name) - { - return type.GetTypeInfo().GetDeclaredProperty(name) != null; - } - public static bool HasPropertyAll(this Type type, Type Base, string name) - { - return HasProperty(type, name) || HasProperty(Base, name); + return type.GetTypeInfo().GetAllProperties() + .Where(p => p.CanWrite && p.SetMethod.IsPublic) + .Select(p => p.Name); } private static object ConvertValue(object value, Type type) @@ -69,9 +60,44 @@ private static object ConvertValue(object value, Type type) // Convert.ChangeType is suitable for most things return Convert.ChangeType(value, type); } - public static bool? GetBooleanState(object obj, string prop) + + public static IEnumerable GetAllConstructors(this TypeInfo typeInfo) + => GetAll(typeInfo, ti => ti.DeclaredConstructors); + + public static IEnumerable GetAllEvents(this TypeInfo typeInfo) + => GetAll(typeInfo, ti => ti.DeclaredEvents); + + public static IEnumerable GetAllFields(this TypeInfo typeInfo) + => GetAll(typeInfo, ti => ti.DeclaredFields); + + public static IEnumerable GetAllMembers(this TypeInfo typeInfo) + => GetAll(typeInfo, ti => ti.DeclaredMembers); + + public static IEnumerable GetAllMethods(this TypeInfo typeInfo) + => GetAll(typeInfo, ti => ti.DeclaredMethods); + + public static IEnumerable GetAllNestedTypes(this TypeInfo typeInfo) + => GetAll(typeInfo, ti => ti.DeclaredNestedTypes); + + public static IEnumerable GetAllProperties(this TypeInfo typeInfo) + => GetAll(typeInfo, ti => ti.DeclaredProperties); + + public static IEnumerable GetAllTypeInfo(this TypeInfo typeInfo) { - return obj.GetType().HasProperty(prop) ? GetValue(obj, prop) as bool? : null; + while (typeInfo != null) + { + yield return typeInfo; + typeInfo = typeInfo.BaseType?.GetTypeInfo(); + } + } + public static bool HasProperty(object obj, string name, out PropertyInfo pi) => (pi = GetPropertyInfo(obj?.GetType().GetTypeInfo(), name)) != null; + public static PropertyInfo GetPropertyInfo(this TypeInfo typeInfo, string name) + { + return typeInfo.GetAllTypeInfo().Select(t => t.GetDeclaredProperty(name)).FirstOrDefault(pi => pi != null); + } + private static IEnumerable GetAll(this TypeInfo typeInfo, Func> accessor) + { + return GetAllTypeInfo(typeInfo).SelectMany(y => accessor(typeInfo)); } } } diff --git a/PKHeX.WinForms/Subforms/PKM Editors/BatchEditor.cs b/PKHeX.WinForms/Subforms/PKM Editors/BatchEditor.cs index 196524549..09d5186be 100644 --- a/PKHeX.WinForms/Subforms/PKM Editors/BatchEditor.cs +++ b/PKHeX.WinForms/Subforms/PKM Editors/BatchEditor.cs @@ -1,12 +1,9 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics; using System.Drawing; -using System.Globalization; using System.IO; using System.Linq; -using System.Reflection; using System.Windows.Forms; using PKHeX.Core; using static PKHeX.Core.MessageStrings; @@ -27,7 +24,8 @@ public BatchEditor(PKM pk, SaveFile sav) CB_Format.Items.Clear(); CB_Format.Items.Add(MsgAny); - foreach (Type t in types) CB_Format.Items.Add(t.Name.ToLower()); + foreach (Type t in BatchEditing.Types) + CB_Format.Items.Add(t.Name.ToLower()); CB_Format.Items.Add(MsgAll); CB_Format.SelectedIndex = CB_Require.SelectedIndex = 0; @@ -35,42 +33,10 @@ public BatchEditor(PKM pk, SaveFile sav) new ToolTip().SetToolTip(L_PropType, MsgBEToolTipPropType); new ToolTip().SetToolTip(L_PropValue, MsgBEToolTipPropValue); } - private static string[][] GetPropArray() - { - var p = new string[types.Length][]; - for (int i = 0; i < p.Length; i++) - p[i] = ReflectFrameworkUtil.GetPropertiesCanWritePublicDeclared(types[i]).Concat(CustomProperties).OrderBy(a => a).ToArray(); - - // Properties for any PKM - var any = ReflectFrameworkUtil.GetPropertiesCanWritePublic(typeof(PK1)).Union(p.SelectMany(a => a)).OrderBy(a => a).ToArray(); - // Properties shared by all PKM - var all = p.Aggregate(new HashSet(p[0]), (h, e) => { h.IntersectWith(e); return h; }).OrderBy(a => a).ToArray(); - - var p1 = new string[types.Length + 2][]; - Array.Copy(p, 0, p1, 1, p.Length); - p1[0] = any; - p1[p1.Length - 1] = all; - - return p1; - } private readonly PKM pkmref; - private const string CONST_RAND = "$rand"; - private const string CONST_SHINY = "$shiny"; - private const string CONST_SUGGEST = "$suggest"; - private const string CONST_BYTES = "$[]"; - - private const string PROP_LEGAL = "Legal"; - private static readonly string[] CustomProperties = {PROP_LEGAL}; private int currentFormat = -1; - private static readonly Type[] types = - { - typeof (PK7), typeof (PK6), typeof (PK5), typeof (PK4), typeof(BK4), - typeof (PK3), typeof (XK3), typeof (CK3), - typeof (PK2), typeof (PK1), - }; - private static readonly string[][] properties = GetPropArray(); // GUI Methods private void B_Open_Click(object sender, EventArgs e) @@ -111,16 +77,16 @@ private void CB_Format_SelectedIndexChanged(object sender, EventArgs e) int format = CB_Format.SelectedIndex; CB_Property.Items.Clear(); - CB_Property.Items.AddRange(properties[format]); + CB_Property.Items.AddRange(BatchEditing.Properties[format]); CB_Property.SelectedIndex = 0; currentFormat = format; } private void CB_Property_SelectedIndexChanged(object sender, EventArgs e) { - L_PropType.Text = GetPropertyType(CB_Property.Text); - if (pkmref.GetType().HasProperty(CB_Property.Text)) + L_PropType.Text = BatchEditing.GetPropertyType(CB_Property.Text, CB_Format.SelectedIndex); + if (BatchEditing.TryGetHasProperty(pkmref, CB_Property.Text, out var pi)) { - L_PropValue.Text = ReflectFrameworkUtil.GetValue(pkmref, CB_Property.Text).ToString(); + L_PropValue.Text = pi.GetValue(pkmref).ToString(); L_PropType.ForeColor = L_PropValue.ForeColor; // reset color } else // no property, flag @@ -184,14 +150,14 @@ private void RunBackgroundWorker() foreach (var set in sets) { - ScreenStrings(set.Filters); - ScreenStrings(set.Instructions); + BatchEditing.ScreenStrings(set.Filters); + BatchEditing.ScreenStrings(set.Instructions); } RunBatchEdit(sets, TB_Folder.Text, destPath); } private void RunBatchEdit(StringInstructionSet[] sets, string source, string destination) { - len = err = ctr = 0; + editor = new Core.BatchEditor(); b = new BackgroundWorker { WorkerReportsProgress = true }; b.DoWork += (sender, e) => { @@ -205,18 +171,14 @@ private void RunBatchEdit(StringInstructionSet[] sets, string source, string des b.ProgressChanged += (sender, e) => SetProgressBar(e.ProgressPercentage); b.RunWorkerCompleted += (sender, e) => { - ctr /= sets.Length; - len /= sets.Length; - string maybe = sets.Length == 1 ? string.Empty : "~"; - string result = string.Format(MsgBEModifySuccess, maybe, ctr, len); - if (err > 0) - result += $"{Environment.NewLine}{maybe}" + string.Format(MsgBEModifyFailError, err); + string result = editor.GetEditorResults(sets); WinFormsUtil.Alert(result); FLP_RB.Enabled = RTB_Instructions.Enabled = B_Go.Enabled = true; SetupProgressBar(0); }; b.RunWorkerAsync(); } + private void RunBatchEditFolder(IList sets, string source, string destination) { var files = Directory.GetFiles(source, "*", SearchOption.AllDirectories); @@ -257,12 +219,12 @@ private void SetProgressBar(int i) } // Mass Editing - private int ctr, len, err; + private Core.BatchEditor editor = new Core.BatchEditor(); private void ProcessSAV(IList data, IList Filters, IList Instructions) { for (int i = 0; i < data.Count; i++) { - ProcessPKM(data[i], Filters, Instructions); + editor.ProcessPKM(data[i], Filters, Instructions); b.ReportProgress(i); } } @@ -281,417 +243,11 @@ private void ProcessFolder(IReadOnlyList files, IList int format = PKX.GetPKMFormatFromExtension(fi.Extension, SAV.Generation); byte[] data = File.ReadAllBytes(file); var pkm = PKMConverter.GetPKMfromBytes(data, prefer: format); - if (ProcessPKM(pkm, Filters, Instructions)) + if (editor.ProcessPKM(pkm, Filters, Instructions)) File.WriteAllBytes(Path.Combine(destPath, Path.GetFileName(file)), pkm.DecryptedBoxData); b.ReportProgress(i); } } - private bool ProcessPKM(PKM pkm, IEnumerable Filters, IEnumerable Instructions) - { - if (!pkm.Valid || pkm.Locked) - { - len++; - var reason = pkm.Locked ? "Locked." : "Not Valid."; - Debug.WriteLine($"{MsgBEModifyFailBlocked} {reason}"); - return false; - } - - ModifyResult r = TryModifyPKM(pkm, Filters, Instructions); - if (r != ModifyResult.Invalid) - len++; - if (r == ModifyResult.Error) - err++; - if (r != ModifyResult.Modified) - return false; - if (pkm.Species <= 0) - return false; - - pkm.RefreshChecksum(); - ctr++; - return true; - } - - private string GetPropertyType(string propertyName) - { - if (CustomProperties.Contains(propertyName)) - return "Custom"; - - int typeIndex = CB_Format.SelectedIndex; - - if (typeIndex == properties.Length - 1) // All - return types[0].GetProperty(propertyName).PropertyType.Name; - - if (typeIndex == 0) // Any - foreach (var p in types.Select(t => t.GetProperty(propertyName)).Where(p => p != null)) - return p.PropertyType.Name; - - return types[typeIndex - 1].GetProperty(propertyName).PropertyType.Name; - } - - // Utility Methods - private enum ModifyResult - { - Invalid, - Error, - Filtered, - Modified, - } - - public class StringInstructionSet - { - public IList Filters { get; private set; } - public IList Instructions { get; private set; } - - private const string SetSeparator = ";"; - public static IEnumerable GetBatchSets(string[] lines) - { - int start = 0; - while (start < lines.Length) - { - var list = lines.Skip(start).TakeWhile(_ => !lines[start++].StartsWith(SetSeparator)).ToList(); - yield return GetBatchSet(list); - } - } - - private static StringInstructionSet GetBatchSet(IList set) - { - return new StringInstructionSet - { - Filters = StringInstruction.GetFilters(set).ToList(), - Instructions = StringInstruction.GetInstructions(set).ToList(), - }; - } - } - public class StringInstruction - { - public string PropertyName { get; set; } - public string PropertyValue { get; set; } - public bool Evaluator { get; set; } - public void SetScreenedValue(string[] arr) - { - int index = Array.IndexOf(arr, PropertyValue); - PropertyValue = index > -1 ? index.ToString() : PropertyValue; - } - - // Extra Functionality - private int Min, Max; - public bool Random { get; private set; } - public int RandomValue => Util.Rand.Next(Min, Max + 1); - public void SetRandRange(string pv) - { - string str = pv.Substring(1); - var split = str.Split(','); - int.TryParse(split[0], out Min); - int.TryParse(split[1], out Max); - - if (Min == Max) - { - PropertyValue = Min.ToString(); - Debug.WriteLine(PropertyName + " randomization range Min/Max same?"); - } - else - Random = true; - } - - public static IEnumerable GetFilters(IEnumerable lines) - { - var raw = GetRelevantStrings(lines, '!', '='); - return from line in raw - let eval = line[0] == '=' - let split = line.Substring(1).Split('=') - where split.Length == 2 && !string.IsNullOrWhiteSpace(split[0]) - select new StringInstruction { PropertyName = split[0], PropertyValue = split[1], Evaluator = eval }; - } - public static IEnumerable GetInstructions(IEnumerable lines) - { - var raw = GetRelevantStrings(lines, '.').Select(line => line.Substring(1)); - return from line in raw - select line.Split('=') into split - where split.Length == 2 - select new StringInstruction { PropertyName = split[0], PropertyValue = split[1] }; - } - private static IEnumerable GetRelevantStrings(IEnumerable lines, params char[] pieces) - { - return lines.Where(line => !string.IsNullOrEmpty(line) && pieces.Any(z => z == line[0])); - } - } - private sealed class PKMInfo - { - internal PKM pkm { get; } - internal PKMInfo(PKM pk) { pkm = pk; } - - private LegalityAnalysis la; - private LegalityAnalysis Legality => la ?? (la = new LegalityAnalysis(pkm)); - - internal bool Legal => Legality.Valid; - internal int[] SuggestedRelearn => Legality.GetSuggestedRelearn(); - internal int[] SuggestedMoves => Legality.GetSuggestedMoves(tm: true, tutor: true, reminder: false); - internal EncounterStatic SuggestedEncounter => Legality.GetSuggestedMetInfo(); - } - - public static void ScreenStrings(IEnumerable il) - { - foreach (var i in il.Where(i => !i.PropertyValue.All(char.IsDigit))) - { - string pv = i.PropertyValue; - if (pv.StartsWith("$") && !pv.StartsWith(CONST_BYTES) && pv.Contains(',')) - i.SetRandRange(pv); - - SetInstructionScreenedValue(i); - } - } - private static void SetInstructionScreenedValue(StringInstruction i) - { - switch (i.PropertyName) - { - case nameof(PKM.Species): i.SetScreenedValue(GameInfo.Strings.specieslist); return; - case nameof(PKM.HeldItem): i.SetScreenedValue(GameInfo.Strings.itemlist); return; - case nameof(PKM.Ability): i.SetScreenedValue(GameInfo.Strings.abilitylist); return; - case nameof(PKM.Nature): i.SetScreenedValue(GameInfo.Strings.natures); return; - case nameof(PKM.Ball): i.SetScreenedValue(GameInfo.Strings.balllist); return; - case nameof(PKM.Move1): - case nameof(PKM.Move2): - case nameof(PKM.Move3): - case nameof(PKM.Move4): - case nameof(PKM.RelearnMove1): - case nameof(PKM.RelearnMove2): - case nameof(PKM.RelearnMove3): - case nameof(PKM.RelearnMove4): - i.SetScreenedValue(GameInfo.Strings.movelist); return; - } - } - private static ModifyResult TryModifyPKM(PKM PKM, IEnumerable Filters, IEnumerable Instructions) - { - if (!PKM.ChecksumValid || PKM.Species == 0) - return ModifyResult.Invalid; - - Type pkm = PKM.GetType(); - PKMInfo info = new PKMInfo(PKM); - - ModifyResult result = ModifyResult.Error; - foreach (var cmd in Filters) - { - try - { - if (IsPKMFiltered(pkm, cmd, info, out result)) - return result; // why it was filtered out - } - catch { Debug.WriteLine(MsgBEModifyFailCompare, cmd.PropertyName, cmd.PropertyValue); } - } - - foreach (var cmd in Instructions) - { - try - { - result = SetPKMProperty(PKM, info, cmd); - } - catch { Debug.WriteLine(MsgBEModifyFail, cmd.PropertyName, cmd.PropertyValue); } - } - return result; - } - private static ModifyResult SetPKMProperty(PKM PKM, PKMInfo info, StringInstruction cmd) - { - if (cmd.PropertyValue.StartsWith(CONST_BYTES)) - return SetByteArrayProperty(PKM, cmd) - ? ModifyResult.Modified - : ModifyResult.Error; - - if (cmd.PropertyValue == CONST_SUGGEST) - return SetSuggestedPKMProperty(PKM, cmd, info) - ? ModifyResult.Modified - : ModifyResult.Error; - - SetProperty(PKM, cmd); - return ModifyResult.Modified; - } - private static bool IsPKMFiltered(Type pkm, StringInstruction cmd, PKMInfo info, out ModifyResult result) - { - result = ModifyResult.Error; - if (cmd.PropertyName == PROP_LEGAL) - { - if (!bool.TryParse(cmd.PropertyValue, out bool legal)) - return true; - if (legal == info.Legal == cmd.Evaluator) - return false; - result = ModifyResult.Filtered; - return true; - } - if (!pkm.HasPropertyAll(cmd.PropertyName) - || pkm.IsValueEqual(info.pkm, cmd.PropertyName, cmd.PropertyValue) != cmd.Evaluator) - { - result = ModifyResult.Filtered; - return true; - } - return false; - } - private static bool SetSuggestedPKMProperty(PKM PKM, StringInstruction cmd, PKMInfo info) - { - switch (cmd.PropertyName) - { - case nameof(PKM.HyperTrainFlags): - PKM.HyperTrainFlags = GetSuggestedHyperTrainingStatus(PKM); - return true; - case nameof(PKM.RelearnMoves): - PKM.RelearnMoves = info.SuggestedRelearn; - return true; - case nameof(PKM.Met_Location): - var encounter = info.SuggestedEncounter; - if (encounter == null) - return false; - - int level = encounter.Level; - int location = encounter.Location; - int minlvl = Legal.GetLowestLevel(PKM, encounter.LevelMin); - - PKM.Met_Level = level; - PKM.Met_Location = location; - PKM.CurrentLevel = Math.Max(minlvl, level); - - return true; - - case nameof(PKM.Moves): - var moves = info.SuggestedMoves; - Util.Shuffle(moves); - PKM.SetMoves(moves); - return true; - - default: - return false; - } - } - - private static int GetSuggestedHyperTrainingStatus(PKM pkm) - { - if (pkm.Format < 7 || pkm.CurrentLevel != 100) - return 0; - - int val = 0; - if (pkm.IV_HP != 31) - val |= 1 << 0; - if (pkm.IV_ATK < 31 && pkm.IV_ATK > 1) - val |= 1 << 1; - if (pkm.IV_DEF != 31) - val |= 1 << 2; - if (pkm.IV_SPE < 31 && pkm.IV_SPE > 1) - val |= 1 << 3; - if (pkm.IV_SPA != 31) - val |= 1 << 4; - if (pkm.IV_SPD != 31) - val |= 1 << 5; - return val; - } - - private static bool SetByteArrayProperty(PKM PKM, StringInstruction cmd) - { - switch (cmd.PropertyName) - { - case nameof(PKM.Nickname_Trash): - PKM.Nickname_Trash = string2arr(cmd.PropertyValue); - return true; - case nameof(PKM.OT_Trash): - PKM.OT_Trash = string2arr(cmd.PropertyValue); - return true; - default: - return false; - } - byte[] string2arr(string str) => str.Substring(CONST_BYTES.Length).Split(',').Select(z => Convert.ToByte(z.Trim(), 16)).ToArray(); - } - private static void SetProperty(PKM PKM, StringInstruction cmd) - { - if (cmd.PropertyName == nameof(PKM.MetDate)) - PKM.MetDate = DateTime.ParseExact(cmd.PropertyValue, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None); - else if (cmd.PropertyName == nameof(PKM.EggMetDate)) - PKM.EggMetDate = DateTime.ParseExact(cmd.PropertyValue, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None); - else if (cmd.PropertyName == nameof(PKM.EncryptionConstant) && cmd.PropertyValue == CONST_RAND) - ReflectFrameworkUtil.SetValue(PKM, cmd.PropertyName, Util.Rand32().ToString()); - else if ((cmd.PropertyName == nameof(PKM.Ability) || cmd.PropertyName == nameof(PKM.AbilityNumber)) && cmd.PropertyValue.StartsWith("$")) - PKM.RefreshAbility(Convert.ToInt16(cmd.PropertyValue[1]) - 0x30); - else if (cmd.PropertyName == nameof(PKM.PID) && cmd.PropertyValue == CONST_RAND) - PKM.SetPIDGender(PKM.Gender); - else if (cmd.PropertyName == nameof(PKM.EncryptionConstant) && cmd.PropertyValue == nameof(PKM.PID)) - PKM.EncryptionConstant = PKM.PID; - else if (cmd.PropertyName == nameof(PKM.PID) && cmd.PropertyValue == CONST_SHINY) - PKM.SetShinyPID(); - else if (cmd.PropertyName == nameof(PKM.Species) && cmd.PropertyValue == "0") - PKM.Data = new byte[PKM.Data.Length]; - else if (cmd.PropertyName.StartsWith("IV") && cmd.PropertyValue == CONST_RAND) - SetRandomIVs(PKM, cmd); - else if (cmd.Random) - ReflectFrameworkUtil.SetValue(PKM, cmd.PropertyName, cmd.RandomValue); - else if (cmd.PropertyName == nameof(PKM.IsNicknamed) && string.Equals(cmd.PropertyValue, "false", StringComparison.OrdinalIgnoreCase)) - { PKM.IsNicknamed = false; PKM.Nickname = PKX.GetSpeciesNameGeneration(PKM.Species, PKM.Language, PKM.Format); } - else - ReflectFrameworkUtil.SetValue(PKM, cmd.PropertyName, cmd.PropertyValue); - } - private static void SetRandomIVs(PKM PKM, StringInstruction cmd) - { - if (cmd.PropertyName == nameof(PKM.IVs)) - { - PKM.SetRandomIVs(); - return; - } - ReflectFrameworkUtil.SetValue(PKM, cmd.PropertyName, Util.Rand32() & PKM.MaxIV); - } - } - - public static class ReflectFrameworkUtil - { - public static bool IsValueEqual(this Type t, object obj, string propertyName, object value) - { - PropertyInfo pi = t.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - var v = pi.GetValue(obj, null); - var c = ConvertValue(value, pi.PropertyType); - return v.Equals(c); - } - public static void SetValue(object obj, string propertyName, object value) - { - PropertyInfo pi = obj.GetType().GetProperty(propertyName); - pi.SetValue(obj, ConvertValue(value, pi.PropertyType), null); - } - - public static object GetValue(object obj, string propertyName) - { - PropertyInfo pi = obj.GetType().GetProperty(propertyName); - return pi.GetValue(obj, null); - } - - public static IEnumerable GetPropertiesStartWithPrefix(Type type, string prefix, BindingFlags flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public) - { - return type.GetProperties(flags) - .Where(p => p.Name.StartsWith(prefix, StringComparison.Ordinal)) - .Select(p => p.Name); - } - public static IEnumerable GetPropertiesCanWritePublic(Type type, BindingFlags flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public) - { - return type.GetProperties(flags) - .Where(p => p.CanWrite && p.GetSetMethod(nonPublic: true).IsPublic) - .Select(p => p.Name); - } - public static IEnumerable GetPropertiesCanWritePublicDeclared(Type type) - { - return GetPropertiesCanWritePublic(type, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); - } - public static bool HasProperty(this Type type, string name) - { - return type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance) != null; - } - public static bool HasPropertyAll(this Type type, string name) - { - return type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) != null; - } - - private static object ConvertValue(object value, Type type) - { - if (type == typeof(DateTime?)) // Used for PKM.MetDate and other similar properties - { - if (DateTime.TryParseExact(value.ToString(), "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateValue)) - return dateValue; - return null; - } - - // Convert.ChangeType is suitable for most things - return Convert.ChangeType(value, type); - } } } diff --git a/PKHeX.WinForms/Subforms/PKM Editors/RibbonEditor.cs b/PKHeX.WinForms/Subforms/PKM Editors/RibbonEditor.cs index f6d1c548b..986c795d4 100644 --- a/PKHeX.WinForms/Subforms/PKM Editors/RibbonEditor.cs +++ b/PKHeX.WinForms/Subforms/PKM Editors/RibbonEditor.cs @@ -46,7 +46,7 @@ private void B_Save_Click(object sender, EventArgs e) private void PopulateRibbons() { // Get a list of all Ribbon Attributes in the PKM - var RibbonNames = ReflectFrameworkUtil.GetPropertiesStartWithPrefix(pkm.GetType(), "Ribbon"); + var RibbonNames = ReflectUtil.GetPropertiesStartWithPrefix(pkm.GetType(), "Ribbon").Distinct(); foreach (var RibbonName in RibbonNames) { object RibbonValue = ReflectUtil.GetValue(pkm, RibbonName); diff --git a/PKHeX.WinForms/Subforms/PKM Editors/SuperTrainingEditor.cs b/PKHeX.WinForms/Subforms/PKM Editors/SuperTrainingEditor.cs index fbee6f801..a0c346182 100644 --- a/PKHeX.WinForms/Subforms/PKM Editors/SuperTrainingEditor.cs +++ b/PKHeX.WinForms/Subforms/PKM Editors/SuperTrainingEditor.cs @@ -68,7 +68,7 @@ private void B_Save_Click(object sender, EventArgs e) private void PopulateRegimens(string Type, TableLayoutPanel TLP, List list) { // Get a list of all Regimen Attregutes in the PKM - var RegimenNames = ReflectFrameworkUtil.GetPropertiesStartWithPrefix(pkm.GetType(), Type); + var RegimenNames = ReflectUtil.GetPropertiesStartWithPrefix(pkm.GetType(), Type); list.AddRange(from RegimenName in RegimenNames let RegimenValue = ReflectUtil.GetValue(pkm, RegimenName) where RegimenValue is bool diff --git a/PKHeX.WinForms/Subforms/SAV_Database.cs b/PKHeX.WinForms/Subforms/SAV_Database.cs index 0e633c502..b265d087d 100644 --- a/PKHeX.WinForms/Subforms/SAV_Database.cs +++ b/PKHeX.WinForms/Subforms/SAV_Database.cs @@ -509,8 +509,8 @@ private IEnumerable SearchDatabase() if (RTB_Instructions.Lines.Any(line => line.Length > 0)) { - var filters = BatchEditor.StringInstruction.GetFilters(RTB_Instructions.Lines).ToArray(); - BatchEditor.ScreenStrings(filters); + var filters = StringInstruction.GetFilters(RTB_Instructions.Lines).ToArray(); + BatchEditing.ScreenStrings(filters); res = res.Where(pkm => IsPKMFiltered(pkm, filters)); // Compare across all filters } @@ -520,7 +520,7 @@ private IEnumerable SearchDatabase() return res; } - private static bool IsPKMFiltered(PKM pkm, IEnumerable filters) + private static bool IsPKMFiltered(PKM pkm, IEnumerable filters) { foreach (var cmd in filters) { @@ -531,9 +531,10 @@ private static bool IsPKMFiltered(PKM pkm, IEnumerable line.Length > 0)) { - var raw = RTB_Instructions.Lines.Where(line => !string.IsNullOrWhiteSpace(line) && new[] { '!', '=' }.Contains(line[0])); - - var filters = (from line in raw - let eval = line[0] == '=' - let split = line.Substring(1).Split('=') - where split.Length == 2 && !string.IsNullOrWhiteSpace(split[0]) - select new BatchEditor.StringInstruction { PropertyName = split[0], PropertyValue = split[1], Evaluator = eval }).ToArray(); - - if (filters.Any(z => string.IsNullOrWhiteSpace(z.PropertyValue))) - { WinFormsUtil.Error(MsgBEFilterEmpty); return; } - - res = res.Where(gift => // Compare across all filters - { - foreach (var cmd in filters) - { - if (!gift.GetType().HasPropertyAll(cmd.PropertyName)) - return false; - try { if (gift.GetType().IsValueEqual(gift, cmd.PropertyName, cmd.PropertyValue) == cmd.Evaluator) continue; } - catch { Debug.WriteLine($"Unable to compare {cmd.PropertyName} to {cmd.PropertyValue}."); } - return false; - } - return true; - }); + var filters = StringInstruction.GetFilters(RTB_Instructions.Lines).ToArray(); + BatchEditing.ScreenStrings(filters); + res = res.Where(pkm => IsPKMFiltered(pkm, filters)); // Compare across all filters } var results = res.ToArray(); @@ -313,6 +293,20 @@ private void B_Search_Click(object sender, EventArgs e) SetResults(new List(results)); // updates Count Label as well. System.Media.SystemSounds.Asterisk.Play(); } + + private static bool IsPKMFiltered(MysteryGift gift, StringInstruction[] filters) + { + foreach (var cmd in filters) + { + if (!ReflectUtil.HasProperty(gift, cmd.PropertyName, out var pi)) + return false; + try { if (pi.IsValueEqual(gift, cmd.PropertyValue) == cmd.Evaluator) continue; } + catch { Debug.WriteLine($"Unable to compare {cmd.PropertyName} to {cmd.PropertyValue}."); } + return false; + } + return true; + } + private void UpdateScroll(object sender, ScrollEventArgs e) { if (e.OldValue != e.NewValue)