2018-05-18 05:43:07 +00:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
2021-01-17 08:05:07 +00:00
using System.Diagnostics.CodeAnalysis ;
2018-05-18 05:43:07 +00:00
using System.Globalization ;
using System.Linq ;
using System.Reflection ;
using static PKHeX . Core . MessageStrings ;
2021-05-27 19:20:00 +00:00
using static PKHeX . Core . BatchModifications ;
2018-05-18 05:43:07 +00:00
namespace PKHeX.Core
{
2019-07-14 22:06:45 +00:00
/// <summary>
/// Logic for editing many <see cref="PKM"/> with user provided <see cref="StringInstruction"/> list.
/// </summary>
2018-05-18 05:43:07 +00:00
public static class BatchEditing
{
public static readonly Type [ ] Types =
{
2022-02-05 01:35:15 +00:00
typeof ( PK8 ) , typeof ( PA8 ) , typeof ( PB8 ) ,
2018-11-14 03:10:43 +00:00
typeof ( PB7 ) ,
2018-05-18 05:43:07 +00:00
typeof ( PK7 ) , typeof ( PK6 ) , typeof ( PK5 ) , typeof ( PK4 ) , typeof ( BK4 ) ,
typeof ( PK3 ) , typeof ( XK3 ) , typeof ( CK3 ) ,
2020-10-09 21:44:12 +00:00
typeof ( PK2 ) , typeof ( SK2 ) , typeof ( PK1 ) ,
2018-05-18 05:43:07 +00:00
} ;
2018-05-19 02:19:15 +00:00
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
/// <summary>
/// Extra properties to show in the list of selectable properties (GUI)
/// </summary>
public static readonly List < string > CustomProperties = new ( )
{
2022-02-05 04:04:57 +00:00
PROP_LEGAL , PROP_TYPENAME , PROP_RIBBONS , PROP_CONTESTSTATS , PROP_MOVEMASTERY ,
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
IdentifierContains , nameof ( ISlotInfo . Slot ) , nameof ( SlotInfoBox . Box ) ,
} ;
/// <summary>
/// Property names, indexed by <see cref="Types"/>.
/// </summary>
2022-03-08 03:42:08 +00:00
public static string [ ] [ ] Properties = > GetProperties . Value ;
private static readonly Lazy < string [ ] [ ] > GetProperties = new ( ( ) = > GetPropArray ( Types , CustomProperties ) ) ;
2018-05-18 05:43:07 +00:00
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
private static readonly Dictionary < string , PropertyInfo > [ ] Props = GetPropertyDictionaries ( Types ) ;
private static Dictionary < string , PropertyInfo > [ ] GetPropertyDictionaries ( IReadOnlyList < Type > types )
{
var result = new Dictionary < string , PropertyInfo > [ types . Count ] ;
for ( int i = 0 ; i < types . Count ; i + + )
result [ i ] = GetPropertyDictionary ( types [ i ] , ReflectUtil . GetAllPropertyInfoPublic ) ;
return result ;
}
2018-05-18 05:43:07 +00:00
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
private static Dictionary < string , PropertyInfo > GetPropertyDictionary ( Type type , Func < Type , IEnumerable < PropertyInfo > > selector )
{
var dict = new Dictionary < string , PropertyInfo > ( ) ;
var props = selector ( type ) ;
foreach ( var p in props )
{
if ( ! dict . ContainsKey ( p . Name ) )
dict . Add ( p . Name , p ) ;
}
return dict ;
}
internal const string CONST_RAND = "$rand" ;
internal const string CONST_SHINY = "$shiny" ;
2021-12-21 03:16:23 +00:00
internal const string CONST_SUGGEST = "$suggest" ;
2018-05-18 05:43:07 +00:00
private const string CONST_BYTES = "$[]" ;
2022-03-05 06:35:04 +00:00
private const string CONST_POINTER = "*" ;
2018-05-18 05:43:07 +00:00
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
internal const string PROP_LEGAL = "Legal" ;
internal const string PROP_TYPENAME = "ObjectType" ;
internal const string PROP_RIBBONS = "Ribbons" ;
2022-02-05 04:04:57 +00:00
internal const string PROP_CONTESTSTATS = "ContestStats" ;
internal const string PROP_MOVEMASTERY = "MoveMastery" ;
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
internal const string IdentifierContains = nameof ( IdentifierContains ) ;
2018-05-18 05:43:07 +00:00
2022-03-08 03:42:08 +00:00
private static string [ ] [ ] GetPropArray ( IReadOnlyList < Type > types , IReadOnlyList < string > extra )
2018-05-18 05:43:07 +00:00
{
2022-03-08 03:42:08 +00:00
var result = new string [ types . Count + 2 ] [ ] ;
var p = result . AsSpan ( 1 , types . Count ) ;
2018-05-18 05:43:07 +00:00
for ( int i = 0 ; i < p . Length ; i + + )
{
2022-03-08 03:42:08 +00:00
var props = ReflectUtil . GetPropertiesPublic ( types [ i ] ) ;
p [ i ] = props . Concat ( extra ) . OrderBy ( a = > a ) . ToArray ( ) ;
2018-05-18 05:43:07 +00:00
}
// Properties for any PKM
// Properties shared by all PKM
2022-03-08 03:42:08 +00:00
var first = p [ 0 ] ;
var any = new HashSet < string > ( first ) ;
var all = new HashSet < string > ( first ) ;
for ( int i = 1 ; i < p . Length ; i + + )
{
any . UnionWith ( p [ i ] ) ;
all . IntersectWith ( p [ i ] ) ;
}
2018-05-18 05:43:07 +00:00
2022-03-08 03:42:08 +00:00
result [ 0 ] = any . OrderBy ( z = > z ) . ToArray ( ) ;
result [ ^ 1 ] = all . OrderBy ( z = > z ) . ToArray ( ) ;
return result ;
2018-05-18 05:43:07 +00:00
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Tries to fetch the <see cref="PKM"/> property from the cache of available properties.
/// </summary>
2019-02-21 06:23:54 +00:00
/// <param name="pk">Pokémon to check</param>
2018-05-19 02:19:15 +00:00
/// <param name="name">Property Name to check</param>
/// <param name="pi">Property Info retrieved (if any).</param>
/// <returns>True if has property, false if does not.</returns>
2021-01-17 08:05:07 +00:00
public static bool TryGetHasProperty ( PKM pk , string name , [ NotNullWhen ( true ) ] out PropertyInfo ? pi )
2018-05-18 05:43:07 +00:00
{
2021-01-17 08:05:07 +00:00
var type = pk . GetType ( ) ;
return TryGetHasProperty ( type , name , out pi ) ;
}
/// <summary>
/// Tries to fetch the <see cref="PKM"/> property from the cache of available properties.
/// </summary>
/// <param name="type">Type to check</param>
/// <param name="name">Property Name to check</param>
/// <param name="pi">Property Info retrieved (if any).</param>
/// <returns>True if has property, false if does not.</returns>
public static bool TryGetHasProperty ( Type type , string name , [ NotNullWhen ( true ) ] out PropertyInfo ? pi )
{
var index = Array . IndexOf ( Types , type ) ;
if ( index < 0 )
{
pi = null ;
return false ;
}
var props = Props [ index ] ;
2018-05-18 05:43:07 +00:00
return props . TryGetValue ( name , out pi ) ;
}
2021-01-17 08:05:07 +00:00
/// <summary>
/// Gets a list of <see cref="PKM"/> types that implement the requested <see cref="property"/>.
/// </summary>
public static IEnumerable < string > GetTypesImplementing ( string property )
{
for ( int i = 0 ; i < Types . Length ; i + + )
{
var type = Types [ i ] ;
var props = Props [ i ] ;
if ( ! props . TryGetValue ( property , out var pi ) )
continue ;
yield return $"{type.Name}: {pi.PropertyType.Name}" ;
}
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Gets the type of the <see cref="PKM"/> property using the saved cache of properties.
/// </summary>
/// <param name="propertyName">Property Name to fetch the type for</param>
/// <param name="typeIndex">Type index (within <see cref="Types"/>. Leave empty (0) for a nonspecific format.</param>
/// <returns>Short name of the property's type.</returns>
PKHeX.Core Nullable cleanup (#2401)
* Handle some nullable cases
Refactor MysteryGift into a second abstract class (backed by a byte array, or fake data)
Make some classes have explicit constructors instead of { } initialization
* Handle bits more obviously without null
* Make SaveFile.BAK explicitly readonly again
* merge constructor methods to have readonly fields
* Inline some properties
* More nullable handling
* Rearrange box actions
define straightforward classes to not have any null properties
* Make extrabyte reference array immutable
* Move tooltip creation to designer
* Rearrange some logic to reduce nesting
* Cache generated fonts
* Split mystery gift album purpose
* Handle more tooltips
* Disallow null setters
* Don't capture RNG object, only type enum
* Unify learnset objects
Now have readonly properties which are never null
don't new() empty learnsets (>800 Learnset objects no longer created,
total of 2400 objects since we also new() a move & level array)
optimize g1/2 reader for early abort case
* Access rewrite
Initialize blocks in a separate object, and get via that object
removes a couple hundred "might be null" warnings since blocks are now readonly getters
some block references have been relocated, but interfaces should expose all that's needed
put HoF6 controls in a groupbox, and disable
* Readonly personal data
* IVs non nullable for mystery gift
* Explicitly initialize forced encounter moves
* Make shadow objects readonly & non-null
Put murkrow fix in binary data resource, instead of on startup
* Assign dex form fetch on constructor
Fixes legality parsing edge cases
also handle cxd parse for valid; exit before exception is thrown in FrameGenerator
* Remove unnecessary null checks
* Keep empty value until init
SetPouch sets the value to an actual one during load, but whatever
* Readonly team lock data
* Readonly locks
Put locked encounters at bottom (favor unlocked)
* Mail readonly data / offset
Rearrange some call flow and pass defaults
Add fake classes for SaveDataEditor mocking
Always party size, no need to check twice in stat editor
use a fake save file as initial data for savedata editor, and for
gamedata (wow i found a usage)
constrain eventwork editor to struct variable types (uint, int, etc),
thus preventing null assignment errors
2019-10-17 01:47:31 +00:00
public static string? GetPropertyType ( string propertyName , int typeIndex = 0 )
2018-05-18 05:43:07 +00:00
{
if ( CustomProperties . Contains ( propertyName ) )
return "Custom" ;
if ( typeIndex = = 0 ) // Any
{
foreach ( var p in Props )
2018-07-29 20:27:48 +00:00
{
2018-05-18 05:43:07 +00:00
if ( p . TryGetValue ( propertyName , out var pi ) )
return pi . PropertyType . Name ;
2018-07-29 20:27:48 +00:00
}
2018-05-18 05:43:07 +00:00
return null ;
}
2019-09-14 18:48:07 +00:00
int index = typeIndex - 1 > = Props . Length ? 0 : typeIndex - 1 ; // All vs Specific
2018-05-19 02:19:15 +00:00
var pr = Props [ index ] ;
2018-05-18 05:43:07 +00:00
if ( ! pr . TryGetValue ( propertyName , out var info ) )
return null ;
return info . PropertyType . Name ;
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Initializes the <see cref="StringInstruction"/> list with a context-sensitive value. If the provided value is a string, it will attempt to convert that string to its corresponding index.
/// </summary>
/// <param name="il">Instructions to initialize.</param>
2018-05-18 05:43:07 +00:00
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 ) ;
}
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Initializes the <see cref="StringInstruction"/> with a context-sensitive value. If the provided value is a string, it will attempt to convert that string to its corresponding index.
/// </summary>
/// <param name="i">Instruction to initialize.</param>
2018-05-18 05:43:07 +00:00
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 ;
2020-12-25 18:58:33 +00:00
case nameof ( PKM . Move1 ) or nameof ( PKM . Move2 ) or nameof ( PKM . Move3 ) or nameof ( PKM . Move4 ) :
case nameof ( PKM . RelearnMove1 ) or nameof ( PKM . RelearnMove2 ) or nameof ( PKM . RelearnMove3 ) or nameof ( PKM . RelearnMove4 ) :
2018-05-18 05:43:07 +00:00
i . SetScreenedValue ( GameInfo . Strings . movelist ) ; return ;
}
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Checks if the object is filtered by the provided <see cref="filters"/>.
/// </summary>
/// <param name="filters">Filters which must be satisfied.</param>
2019-02-21 06:23:54 +00:00
/// <param name="pk">Object to check.</param>
/// <returns>True if <see cref="pk"/> matches all filters.</returns>
public static bool IsFilterMatch ( IEnumerable < StringInstruction > filters , PKM pk ) = > filters . All ( z = > IsFilterMatch ( z , pk , Props [ Array . IndexOf ( Types , pk . GetType ( ) ) ] ) ) ;
2018-05-19 02:19:15 +00:00
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
/// <summary>
/// Checks if the object is filtered by the provided <see cref="filters"/>.
/// </summary>
/// <param name="filters">Filters which must be satisfied.</param>
/// <param name="pk">Object to check.</param>
/// <returns>True if <see cref="pk"/> matches all filters.</returns>
public static bool IsFilterMatchMeta ( IEnumerable < StringInstruction > filters , SlotCache pk )
{
foreach ( var i in filters )
{
foreach ( var filter in BatchFilters . FilterMeta )
{
if ( ! filter . IsMatch ( i . PropertyName ) )
continue ;
if ( ! filter . IsFiltered ( pk , i ) )
return false ;
break ;
}
}
return true ;
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Checks if the object is filtered by the provided <see cref="filters"/>.
/// </summary>
/// <param name="filters">Filters which must be satisfied.</param>
/// <param name="obj">Object to check.</param>
/// <returns>True if <see cref="obj"/> matches all filters.</returns>
2018-06-01 02:59:05 +00:00
public static bool IsFilterMatch ( IEnumerable < StringInstruction > filters , object obj )
2018-05-19 02:19:15 +00:00
{
foreach ( var cmd in filters )
{
2021-06-09 03:28:13 +00:00
if ( cmd . PropertyName is PROP_TYPENAME )
{
if ( ( obj . GetType ( ) . Name = = cmd . PropertyValue ) ! = cmd . Evaluator )
return false ;
2021-06-10 01:40:17 +00:00
continue ;
2021-06-09 03:28:13 +00:00
}
2018-05-19 02:19:15 +00:00
if ( ! ReflectUtil . HasProperty ( obj , cmd . PropertyName , out var pi ) )
return false ;
2019-02-21 06:23:54 +00:00
try
{
if ( pi . IsValueEqual ( obj , cmd . PropertyValue ) = = cmd . Evaluator )
continue ;
}
2020-09-19 05:11:13 +00:00
// User provided inputs can mismatch the type's required value format, and fail to be compared.
2019-02-21 06:23:54 +00:00
catch ( Exception e )
{
Debug . WriteLine ( $"Unable to compare {cmd.PropertyName} to {cmd.PropertyValue}." ) ;
Debug . WriteLine ( e . Message ) ;
}
2018-05-19 02:19:15 +00:00
return false ;
}
return true ;
}
/// <summary>
/// Tries to modify the <see cref="PKM"/>.
/// </summary>
2019-02-21 06:23:54 +00:00
/// <param name="pk">Object to modify.</param>
2018-05-19 02:19:15 +00:00
/// <param name="filters">Filters which must be satisfied prior to any modifications being made.</param>
2019-02-21 06:23:54 +00:00
/// <param name="modifications">Modifications to perform on the <see cref="pk"/>.</param>
2018-05-19 02:19:15 +00:00
/// <returns>Result of the attempted modification.</returns>
2019-02-21 06:23:54 +00:00
public static bool TryModify ( PKM pk , IEnumerable < StringInstruction > filters , IEnumerable < StringInstruction > modifications )
2018-05-18 05:43:07 +00:00
{
2019-02-21 06:23:54 +00:00
var result = TryModifyPKM ( pk , filters , modifications ) ;
2018-05-19 02:19:15 +00:00
return result = = ModifyResult . Modified ;
}
/// <summary>
2021-04-20 08:50:47 +00:00
/// Tries to modify the <see cref="BatchInfo"/>.
2018-05-19 02:19:15 +00:00
/// </summary>
2019-02-21 06:23:54 +00:00
/// <param name="pk">Command Filter</param>
2018-05-19 02:19:15 +00:00
/// <param name="filters">Filters which must be satisfied prior to any modifications being made.</param>
2019-02-21 06:23:54 +00:00
/// <param name="modifications">Modifications to perform on the <see cref="pk"/>.</param>
2018-05-19 02:19:15 +00:00
/// <returns>Result of the attempted modification.</returns>
2019-02-21 06:23:54 +00:00
internal static ModifyResult TryModifyPKM ( PKM pk , IEnumerable < StringInstruction > filters , IEnumerable < StringInstruction > modifications )
2018-05-19 02:19:15 +00:00
{
2019-02-21 06:23:54 +00:00
if ( ! pk . ChecksumValid | | pk . Species = = 0 )
2018-05-18 05:43:07 +00:00
return ModifyResult . Invalid ;
2021-04-20 08:50:47 +00:00
var info = new BatchInfo ( pk ) ;
2019-02-21 06:23:54 +00:00
var pi = Props [ Array . IndexOf ( Types , pk . GetType ( ) ) ] ;
2018-05-19 02:19:15 +00:00
foreach ( var cmd in filters )
2018-05-18 05:43:07 +00:00
{
try
{
2018-06-01 02:59:05 +00:00
if ( ! IsFilterMatch ( cmd , info , pi ) )
2018-05-19 02:19:15 +00:00
return ModifyResult . Filtered ;
}
2020-09-19 05:11:13 +00:00
// Swallow any error because this can be malformed user input.
2018-05-19 02:19:15 +00:00
catch ( Exception ex )
{
Debug . WriteLine ( MsgBEModifyFailCompare + " " + ex . Message , cmd . PropertyName , cmd . PropertyValue ) ;
return ModifyResult . Error ;
2018-05-18 05:43:07 +00:00
}
}
ModifyResult result = ModifyResult . Modified ;
2018-05-19 02:19:15 +00:00
foreach ( var cmd in modifications )
2018-05-18 05:43:07 +00:00
{
try
{
2018-05-19 02:19:15 +00:00
var tmp = SetPKMProperty ( cmd , info , pi ) ;
2018-06-14 01:52:09 +00:00
if ( tmp ! = ModifyResult . Modified )
2018-05-18 05:43:07 +00:00
result = tmp ;
}
2020-09-06 17:53:13 +00:00
// Swallow any error because this can be malformed user input.
catch ( Exception ex )
{
Debug . WriteLine ( MsgBEModifyFail + " " + ex . Message , cmd . PropertyName , cmd . PropertyValue ) ;
}
2018-05-18 05:43:07 +00:00
}
return result ;
}
2018-05-19 02:19:15 +00:00
/// <summary>
2021-04-20 08:50:47 +00:00
/// Sets the if the <see cref="BatchInfo"/> should be filtered due to the <see cref="StringInstruction"/> provided.
2018-05-19 02:19:15 +00:00
/// </summary>
/// <param name="cmd">Command Filter</param>
/// <param name="info">Pokémon to check.</param>
/// <param name="props">PropertyInfo cache (optional)</param>
/// <returns>True if filtered, else false.</returns>
2021-04-20 08:50:47 +00:00
private static ModifyResult SetPKMProperty ( StringInstruction cmd , BatchInfo info , IReadOnlyDictionary < string , PropertyInfo > props )
2018-05-18 05:43:07 +00:00
{
2019-10-27 19:57:04 +00:00
var pk = info . Entity ;
2018-05-18 05:43:07 +00:00
if ( cmd . PropertyValue . StartsWith ( CONST_BYTES ) )
2019-02-21 06:23:54 +00:00
return SetByteArrayProperty ( pk , cmd ) ;
2018-05-18 05:43:07 +00:00
2020-04-15 23:23:20 +00:00
if ( cmd . PropertyValue . StartsWith ( CONST_SUGGEST , true , CultureInfo . CurrentCulture ) )
2019-12-09 01:52:17 +00:00
return SetSuggestedPKMProperty ( cmd . PropertyName , info , cmd . PropertyValue ) ;
2018-05-20 21:29:13 +00:00
if ( cmd . PropertyValue = = CONST_RAND & & cmd . PropertyName = = nameof ( PKM . Moves ) )
2020-04-12 23:07:59 +00:00
return SetMoves ( pk , info . Legality . GetMoveSet ( true ) ) ;
2018-05-18 05:43:07 +00:00
2019-02-21 06:23:54 +00:00
if ( SetComplexProperty ( pk , cmd ) )
2018-05-18 05:43:07 +00:00
return ModifyResult . Modified ;
2018-05-19 02:19:15 +00:00
if ( ! props . TryGetValue ( cmd . PropertyName , out var pi ) )
return ModifyResult . Error ;
2018-06-14 01:52:09 +00:00
if ( ! pi . CanWrite )
return ModifyResult . Error ;
2022-03-05 06:35:04 +00:00
object val ;
if ( cmd . Random )
val = cmd . RandomValue ;
else if ( cmd . PropertyValue . StartsWith ( CONST_POINTER ) & & props . TryGetValue ( cmd . PropertyValue [ 1. . ] , out var opi ) )
val = opi . GetValue ( pk ) ;
else
val = cmd . PropertyValue ;
2019-02-21 06:23:54 +00:00
ReflectUtil . SetValue ( pi , pk , val ) ;
2018-05-19 02:19:15 +00:00
return ModifyResult . Modified ;
2018-05-18 05:43:07 +00:00
}
2018-05-19 02:19:15 +00:00
/// <summary>
2021-04-20 08:50:47 +00:00
/// Checks if the <see cref="BatchInfo"/> should be filtered due to the <see cref="StringInstruction"/> provided.
2018-05-19 02:19:15 +00:00
/// </summary>
/// <param name="cmd">Command Filter</param>
/// <param name="info">Pokémon to check.</param>
/// <param name="props">PropertyInfo cache (optional)</param>
2018-06-01 02:59:05 +00:00
/// <returns>True if filter matches, else false.</returns>
2021-04-20 08:50:47 +00:00
private static bool IsFilterMatch ( StringInstruction cmd , BatchInfo info , IReadOnlyDictionary < string , PropertyInfo > props )
2018-05-18 05:43:07 +00:00
{
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
var match = BatchFilters . FilterMods . Find ( z = > z . IsMatch ( cmd . PropertyName ) ) ;
2021-05-27 21:43:32 +00:00
if ( match ! = null )
return match . IsFiltered ( info , cmd ) ;
2019-10-27 19:57:04 +00:00
return IsPropertyFiltered ( cmd , info . Entity , props ) ;
2018-05-19 02:19:15 +00:00
}
2018-05-18 05:43:07 +00:00
2018-05-19 02:19:15 +00:00
/// <summary>
/// Checks if the <see cref="PKM"/> should be filtered due to the <see cref="StringInstruction"/> provided.
/// </summary>
/// <param name="cmd">Command Filter</param>
2019-02-21 06:23:54 +00:00
/// <param name="pk">Pokémon to check.</param>
2018-05-19 02:19:15 +00:00
/// <param name="props">PropertyInfo cache (optional)</param>
2018-06-01 02:59:05 +00:00
/// <returns>True if filter matches, else false.</returns>
2019-02-21 06:23:54 +00:00
private static bool IsFilterMatch ( StringInstruction cmd , PKM pk , IReadOnlyDictionary < string , PropertyInfo > props )
2018-05-19 02:19:15 +00:00
{
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
var match = BatchFilters . FilterMods . Find ( z = > z . IsMatch ( cmd . PropertyName ) ) ;
2021-05-27 21:43:32 +00:00
if ( match ! = null )
return match . IsFiltered ( pk , cmd ) ;
2019-02-21 06:23:54 +00:00
return IsPropertyFiltered ( cmd , pk , props ) ;
2018-05-19 02:19:15 +00:00
}
2018-05-21 02:26:36 +00:00
/// <summary>
/// Checks if the <see cref="PKM"/> should be filtered due to the <see cref="StringInstruction"/> provided.
/// </summary>
/// <param name="cmd">Command Filter</param>
2019-02-21 06:23:54 +00:00
/// <param name="pk">Pokémon to check.</param>
2018-05-21 02:26:36 +00:00
/// <param name="props">PropertyInfo cache</param>
/// <returns>True if filtered, else false.</returns>
2019-02-21 06:23:54 +00:00
private static bool IsPropertyFiltered ( StringInstruction cmd , PKM pk , IReadOnlyDictionary < string , PropertyInfo > props )
2018-05-21 02:26:36 +00:00
{
if ( ! props . TryGetValue ( cmd . PropertyName , out var pi ) )
return false ;
2018-06-14 01:52:09 +00:00
if ( ! pi . CanRead )
return false ;
2019-02-21 06:23:54 +00:00
return pi . IsValueEqual ( pk , cmd . PropertyValue ) = = cmd . Evaluator ;
2018-05-19 02:19:15 +00:00
}
/// <summary>
/// Sets the <see cref="PKM"/> data with a suggested value based on its <see cref="LegalityAnalysis"/>.
/// </summary>
/// <param name="name">Property to modify.</param>
/// <param name="info">Cached info storing Legal data.</param>
2019-12-09 01:52:17 +00:00
/// <param name="propValue">Suggestion string which starts with <see cref="CONST_SUGGEST"/></param>
2021-04-20 08:50:47 +00:00
private static ModifyResult SetSuggestedPKMProperty ( string name , BatchInfo info , string propValue )
2018-05-18 05:43:07 +00:00
{
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
var first = BatchMods . SuggestionMods . Find ( z = > z . IsMatch ( name , propValue , info ) ) ;
2021-05-27 19:20:00 +00:00
if ( first ! = null )
return first . Modify ( name , propValue , info ) ;
return ModifyResult . Error ;
2018-05-20 21:29:13 +00:00
}
2018-05-19 02:19:15 +00:00
/// <summary>
2020-06-17 02:46:22 +00:00
/// Sets the <see cref="PKM"/> byte array property to a specified value.
2018-05-19 02:19:15 +00:00
/// </summary>
2019-02-21 06:23:54 +00:00
/// <param name="pk">Pokémon to modify.</param>
2018-05-19 02:19:15 +00:00
/// <param name="cmd">Modification</param>
2019-02-21 06:23:54 +00:00
private static ModifyResult SetByteArrayProperty ( PKM pk , StringInstruction cmd )
2018-05-18 05:43:07 +00:00
{
switch ( cmd . PropertyName )
{
2022-01-03 05:35:59 +00:00
case nameof ( PKM . Nickname_Trash ) : ConvertToBytes ( cmd . PropertyValue ) . CopyTo ( pk . Nickname_Trash ) ; return ModifyResult . Modified ;
case nameof ( PKM . OT_Trash ) : ConvertToBytes ( cmd . PropertyValue ) . CopyTo ( pk . OT_Trash ) ; return ModifyResult . Modified ;
case nameof ( PKM . HT_Trash ) : ConvertToBytes ( cmd . PropertyValue ) . CopyTo ( pk . HT_Trash ) ; return ModifyResult . Modified ;
2018-05-18 05:43:07 +00:00
default :
2018-05-19 02:19:15 +00:00
return ModifyResult . Error ;
2018-05-18 05:43:07 +00:00
}
2022-03-06 04:03:52 +00:00
static byte [ ] ConvertToBytes ( string str )
{
var arr = str [ CONST_BYTES . Length . . ] . Split ( ',' ) ;
return Array . ConvertAll ( arr , z = > Convert . ToByte ( z . Trim ( ) , 16 ) ) ;
}
2018-05-18 05:43:07 +00:00
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Sets the <see cref="PKM"/> property to a non-specific smart value.
/// </summary>
2019-02-21 06:23:54 +00:00
/// <param name="pk">Pokémon to modify.</param>
2018-05-19 02:19:15 +00:00
/// <param name="cmd">Modification</param>
2019-07-14 22:06:45 +00:00
/// <returns>True if modified, false if no modifications done.</returns>
2019-02-21 06:23:54 +00:00
private static bool SetComplexProperty ( PKM pk , StringInstruction cmd )
2018-05-18 05:43:07 +00:00
{
2021-05-27 19:20:00 +00:00
if ( cmd . PropertyName . StartsWith ( "IV" ) & & cmd . PropertyValue = = CONST_RAND )
{
2019-02-21 06:23:54 +00:00
SetRandomIVs ( pk , cmd ) ;
2021-05-27 19:20:00 +00:00
return true ;
}
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
var match = BatchMods . ComplexMods . Find ( z = > z . IsMatch ( cmd . PropertyName , cmd . PropertyValue ) ) ;
2021-05-27 19:20:00 +00:00
if ( match = = null )
2018-05-18 05:43:07 +00:00
return false ;
2021-05-27 19:20:00 +00:00
match . Modify ( pk , cmd ) ;
2018-05-18 05:43:07 +00:00
return true ;
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Sets the <see cref="PKM"/> IV(s) to a random value.
/// </summary>
2019-02-21 06:23:54 +00:00
/// <param name="pk">Pokémon to modify.</param>
2018-05-19 02:19:15 +00:00
/// <param name="cmd">Modification</param>
2019-02-21 06:23:54 +00:00
private static void SetRandomIVs ( PKM pk , StringInstruction cmd )
2018-05-18 05:43:07 +00:00
{
2019-02-21 06:23:54 +00:00
if ( cmd . PropertyName = = nameof ( PKM . IVs ) )
2018-05-18 05:43:07 +00:00
{
2019-02-21 06:23:54 +00:00
pk . SetRandomIVs ( ) ;
2018-05-18 05:43:07 +00:00
return ;
}
2019-02-21 06:23:54 +00:00
if ( TryGetHasProperty ( pk , cmd . PropertyName , out var pi ) )
ReflectUtil . SetValue ( pi , pk , Util . Rand . Next ( pk . MaxIV + 1 ) ) ;
2018-05-18 05:43:07 +00:00
}
}
}