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
This commit is contained in:
Kurt
2018-05-17 22:43:07 -07:00
parent 51a4d8095d
commit d772a82953
14 changed files with 580 additions and 519 deletions

View File

@@ -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<string, PropertyInfo>[] 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<string>(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<StringInstruction> 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<StringInstruction> Filters, IEnumerable<StringInstruction> 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<string, PropertyInfo> 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<string, PropertyInfo> 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);
}
}
}

View File

@@ -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<StringInstruction> Filters, IEnumerable<StringInstruction> 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<StringInstructionSet> 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;
}
}
}

View File

@@ -0,0 +1,11 @@
namespace PKHeX.Core
{
internal enum ModifyResult
{
None,
Invalid,
Error,
Filtered,
Modified,
}
}

View File

@@ -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();
}
}

View File

@@ -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<StringInstruction> GetFilters(IEnumerable<string> 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<StringInstruction> GetInstructions(IEnumerable<string> 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<string> GetRelevantStrings(IEnumerable<string> lines, params char[] pieces)
{
return lines.Where(line => !string.IsNullOrEmpty(line) && pieces.Any(z => z == line[0]));
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
// Utility Methods
public class StringInstructionSet
{
public IList<StringInstruction> Filters { get; private set; }
public IList<StringInstruction> Instructions { get; private set; }
private const string SetSeparator = ";";
public static IEnumerable<StringInstructionSet> 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<string> set)
{
return new StringInstructionSet
{
Filters = StringInstruction.GetFilters(set).ToList(),
Instructions = StringInstruction.GetInstructions(set).ToList(),
};
}
}
}

View File

@@ -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);
}
}

View File

@@ -617,6 +617,8 @@ public void ClearBoxes(int BoxStart = 0, int BoxEnd = -1, Func<PKM, bool> 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)
{

View File

@@ -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<string> 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<string> 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<PropertyInfo> GetAllPropertyInfoCanWritePublic(Type type)
{
return type.GetTypeInfo().GetAllTypeInfo().SelectMany(GetAllProperties)
.Where(p => p.CanWrite && p.SetMethod.IsPublic);
}
public static IEnumerable<string> 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<ConstructorInfo> GetAllConstructors(this TypeInfo typeInfo)
=> GetAll(typeInfo, ti => ti.DeclaredConstructors);
public static IEnumerable<EventInfo> GetAllEvents(this TypeInfo typeInfo)
=> GetAll(typeInfo, ti => ti.DeclaredEvents);
public static IEnumerable<FieldInfo> GetAllFields(this TypeInfo typeInfo)
=> GetAll(typeInfo, ti => ti.DeclaredFields);
public static IEnumerable<MemberInfo> GetAllMembers(this TypeInfo typeInfo)
=> GetAll(typeInfo, ti => ti.DeclaredMembers);
public static IEnumerable<MethodInfo> GetAllMethods(this TypeInfo typeInfo)
=> GetAll(typeInfo, ti => ti.DeclaredMethods);
public static IEnumerable<TypeInfo> GetAllNestedTypes(this TypeInfo typeInfo)
=> GetAll(typeInfo, ti => ti.DeclaredNestedTypes);
public static IEnumerable<PropertyInfo> GetAllProperties(this TypeInfo typeInfo)
=> GetAll(typeInfo, ti => ti.DeclaredProperties);
public static IEnumerable<TypeInfo> 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<T> GetAll<T>(this TypeInfo typeInfo, Func<TypeInfo, IEnumerable<T>> accessor)
{
return GetAllTypeInfo(typeInfo).SelectMany(y => accessor(typeInfo));
}
}
}

View File

@@ -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<string>(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<StringInstructionSet> 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<PKM> data, IList<StringInstruction> Filters, IList<StringInstruction> 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<string> files, IList<StringInstruction>
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<StringInstruction> Filters, IEnumerable<StringInstruction> 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<StringInstruction> Filters { get; private set; }
public IList<StringInstruction> Instructions { get; private set; }
private const string SetSeparator = ";";
public static IEnumerable<StringInstructionSet> 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<string> 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<StringInstruction> GetFilters(IEnumerable<string> 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<StringInstruction> GetInstructions(IEnumerable<string> 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<string> GetRelevantStrings(IEnumerable<string> 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<StringInstruction> 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<StringInstruction> Filters, IEnumerable<StringInstruction> 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<string> 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<string> 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<string> 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);
}
}
}

View File

@@ -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);

View File

@@ -68,7 +68,7 @@ private void B_Save_Click(object sender, EventArgs e)
private void PopulateRegimens(string Type, TableLayoutPanel TLP, List<RegimenInfo> 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

View File

@@ -509,8 +509,8 @@ private IEnumerable<PKM> 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<PKM> SearchDatabase()
return res;
}
private static bool IsPKMFiltered(PKM pkm, IEnumerable<BatchEditor.StringInstruction> filters)
private static bool IsPKMFiltered(PKM pkm, IEnumerable<StringInstruction> filters)
{
foreach (var cmd in filters)
{
@@ -531,9 +531,10 @@ private static bool IsPKMFiltered(PKM pkm, IEnumerable<BatchEditor.StringInstruc
return false;
continue;
}
if (!pkm.GetType().HasPropertyAll(cmd.PropertyName))
if (!ReflectUtil.HasProperty(pkm, cmd.PropertyName, out var pi))
return false;
try { if (pkm.GetType().IsValueEqual(pkm, cmd.PropertyName, cmd.PropertyValue) == cmd.Evaluator) continue; }
try { if (pi.IsValueEqual(pkm, cmd.PropertyValue) == cmd.Evaluator) continue; }
catch { Debug.WriteLine($"Unable to compare {cmd.PropertyName} to {cmd.PropertyValue}."); }
return false;
}

View File

@@ -281,29 +281,9 @@ private void B_Search_Click(object sender, EventArgs e)
if (RTB_Instructions.Lines.Any(line => 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<MysteryGift>(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)