2022-02-05 17:31:36 +00:00
#define SUPPRESS
2017-07-18 23:21:31 +00:00
using System ;
2016-03-25 07:10:11 +00:00
using System.Collections.Generic ;
2020-11-27 20:00:49 +00:00
using static PKHeX . Core . LegalityAnalyzers ;
2020-11-28 02:45:06 +00:00
using static PKHeX . Core . LegalityCheckStrings ;
2016-02-23 06:52:48 +00:00
2022-06-18 18:04:24 +00:00
namespace PKHeX.Core ;
/// <summary>
/// Legality Check object containing the <see cref="CheckResult"/> data and overview values from the parse.
/// </summary>
public sealed class LegalityAnalysis
2016-02-23 06:52:48 +00:00
{
2022-06-18 18:04:24 +00:00
/// <summary> The entity we are checking. </summary>
internal readonly PKM Entity ;
/// <summary> The entity's <see cref="PersonalInfo"/>, which may have been sourced from the Save File it resides on. </summary>
/// <remarks>We store this rather than re-fetching, as some games that use the same <see cref="PKM"/> format have different values.</remarks>
internal readonly PersonalInfo PersonalInfo ;
private readonly List < CheckResult > Parse = new ( 8 ) ;
/// <summary>
/// Parse result list allowing view of the legality parse.
/// </summary>
public IReadOnlyList < CheckResult > Results = > Parse ;
/// <summary>
/// Only use this when trying to mutate the legality. Not for use when checking legality.
/// </summary>
public void ResetParse ( ) = > Parse . Clear ( ) ;
/// <summary>
/// Matched encounter data for the <see cref="Entity"/>.
/// </summary>
public IEncounterable EncounterMatch = > Info . EncounterMatch ;
/// <summary>
/// Original encounter data for the <see cref="Entity"/>.
/// </summary>
/// <remarks>
/// Generation 1/2 <see cref="Entity"/> that are transferred forward to Generation 7 are restricted to new encounter details.
/// By retaining their original match, more information can be provided by the parse.
/// </remarks>
public IEncounterable EncounterOriginal = > Info . EncounterOriginal ;
public readonly SlotOrigin SlotOrigin ;
/// <summary>
/// Indicates if all checks ran to completion.
/// </summary>
/// <remarks>This value is false if any checks encountered an error.</remarks>
public readonly bool Parsed ;
/// <summary>
/// Indicates if all checks returned a <see cref="Severity.Valid"/> result.
/// </summary>
public readonly bool Valid ;
/// <summary>
/// Contains various data reused for multiple checks.
/// </summary>
public readonly LegalInfo Info ;
2017-10-24 06:12:58 +00:00
/// <summary>
2022-06-18 18:04:24 +00:00
/// Checks the input <see cref="PKM"/> data for legality. This is the best method for checking with context, as some games do not have all Alternate Form data available.
2017-10-24 06:12:58 +00:00
/// </summary>
2022-06-18 18:04:24 +00:00
/// <param name="pk">Input data to check</param>
/// <param name="table"><see cref="SaveFile"/> specific personal data</param>
/// <param name="source">Details about where the <see cref="Entity"/> originated from.</param>
public LegalityAnalysis ( PKM pk , PersonalTable table , SlotOrigin source = SlotOrigin . Party ) : this ( pk , table . GetFormEntry ( pk . Species , pk . Form ) , source ) { }
/// <summary>
/// Checks the input <see cref="PKM"/> data for legality.
/// </summary>
/// <param name="pk">Input data to check</param>
/// <param name="source">Details about where the <see cref="Entity"/> originated from.</param>
public LegalityAnalysis ( PKM pk , SlotOrigin source = SlotOrigin . Party ) : this ( pk , pk . PersonalInfo , source ) { }
/// <summary>
/// Checks the input <see cref="PKM"/> data for legality.
/// </summary>
/// <param name="pk">Input data to check</param>
/// <param name="pi">Personal info to parse with</param>
/// <param name="source">Details about where the <see cref="Entity"/> originated from.</param>
public LegalityAnalysis ( PKM pk , PersonalInfo pi , SlotOrigin source = SlotOrigin . Party )
2016-02-23 06:52:48 +00:00
{
2022-06-18 18:04:24 +00:00
Entity = pk ;
PersonalInfo = pi ;
SlotOrigin = source ;
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
2022-06-18 18:04:24 +00:00
Info = new LegalInfo ( pk , Parse ) ;
2017-07-18 23:21:31 +00:00
#if SUPPRESS
2022-06-18 18:04:24 +00:00
try
2017-07-18 23:21:31 +00:00
#endif
2022-06-18 18:04:24 +00:00
{
EncounterFinder . FindVerifiedEncounter ( pk , Info ) ;
if ( ! pk . IsOriginValid )
AddLine ( Severity . Invalid , LEncConditionBadSpecies , CheckIdentifier . GameOrigin ) ;
GetParseMethod ( ) ( ) ;
2017-02-14 06:49:32 +00:00
2022-06-18 18:04:24 +00:00
Valid = Parse . TrueForAll ( chk = > chk . Valid )
2022-03-13 01:06:03 +00:00
& & Array . TrueForAll ( Info . Moves , m = > m . Valid )
& & Array . TrueForAll ( Info . Relearn , m = > m . Valid ) ;
2016-11-15 00:27:15 +00:00
2022-06-18 18:04:24 +00:00
if ( ! Valid & & IsPotentiallyMysteryGift ( Info , pk ) )
AddLine ( Severity . Indeterminate , LFatefulGiftMissing , CheckIdentifier . Fateful ) ;
Parsed = true ;
}
2017-07-18 23:21:31 +00:00
#if SUPPRESS
2022-06-18 18:04:24 +00:00
// We want to swallow any error from malformed input data from the user. The Valid state is all that we really need.
catch ( Exception e )
{
System . Diagnostics . Debug . WriteLine ( e . Message ) ;
Valid = false ;
// Moves and Relearn arrays can potentially be empty on error.
foreach ( var p in Info . Moves )
2017-03-24 06:15:49 +00:00
{
2022-06-18 18:04:24 +00:00
if ( ! p . IsParsed )
p . Set ( MoveSource . Unknown , pk . Format , Severity . Indeterminate , L_AError , CheckIdentifier . CurrentMove ) ;
2017-03-24 06:15:49 +00:00
}
2022-06-18 18:04:24 +00:00
foreach ( var p in Info . Relearn )
{
if ( ! p . IsParsed )
p . Set ( MoveSource . Unknown , 0 , Severity . Indeterminate , L_AError , CheckIdentifier . RelearnMove ) ;
}
AddLine ( Severity . Invalid , L_AError , CheckIdentifier . Misc ) ;
2016-10-23 19:48:49 +00:00
}
2022-06-18 18:04:24 +00:00
#endif
}
2018-07-27 02:34:27 +00:00
2022-06-18 18:04:24 +00:00
private static bool IsPotentiallyMysteryGift ( LegalInfo info , PKM pk )
{
if ( info . EncounterOriginal is not EncounterInvalid enc )
2021-08-06 00:16:13 +00:00
return false ;
2022-06-18 18:04:24 +00:00
if ( enc . Generation < = 3 )
return true ;
if ( ! pk . FatefulEncounter )
return false ;
if ( enc . Generation < 6 )
return true ;
if ( Array . TrueForAll ( info . Relearn , chk = > ! chk . Valid ) )
return true ;
return false ;
}
2018-07-27 02:34:27 +00:00
2022-06-18 18:04:24 +00:00
private Action GetParseMethod ( )
{
if ( Entity . Format < = 2 ) // prior to storing GameVersion
return ParsePK1 ;
2018-07-27 02:34:27 +00:00
2022-06-18 18:04:24 +00:00
int gen = Entity . Generation ;
if ( gen < = 0 )
gen = Entity . Format ;
return gen switch
2017-03-18 23:50:34 +00:00
{
2022-06-18 18:04:24 +00:00
3 = > ParsePK3 ,
4 = > ParsePK4 ,
5 = > ParsePK5 ,
6 = > ParsePK6 ,
2017-04-30 19:17:27 +00:00
2022-06-18 18:04:24 +00:00
1 = > ParsePK7 ,
2 = > ParsePK7 ,
7 = > ParsePK7 ,
2017-06-09 03:57:30 +00:00
2022-06-18 18:04:24 +00:00
8 = > ParsePK8 ,
2019-11-17 00:05:45 +00:00
2022-06-18 18:04:24 +00:00
_ = > throw new ArgumentOutOfRangeException ( nameof ( gen ) ) ,
} ;
}
2018-07-27 02:34:27 +00:00
2022-06-18 18:04:24 +00:00
private void ParsePK1 ( )
{
Nickname . Verify ( this ) ;
Level . Verify ( this ) ;
Level . VerifyG1 ( this ) ;
Trainer . VerifyOTG1 ( this ) ;
MiscValues . VerifyMiscG1 ( this ) ;
if ( Entity . Format = = 2 )
Item . Verify ( this ) ;
}
2018-07-27 02:34:27 +00:00
2022-06-18 18:04:24 +00:00
private void ParsePK3 ( )
{
UpdateChecks ( ) ;
if ( Entity . Format > 3 )
Transfer . VerifyTransferLegalityG3 ( this ) ;
2018-07-27 02:34:27 +00:00
2022-06-18 18:04:24 +00:00
if ( Entity . Version = = ( int ) GameVersion . CXD )
CXD . Verify ( this ) ;
2018-07-27 02:34:27 +00:00
2022-06-18 18:04:24 +00:00
if ( Info . EncounterMatch is WC3 { NotDistributed : true } )
AddLine ( Severity . Invalid , LEncUnreleased , CheckIdentifier . Encounter ) ;
2016-03-14 03:19:04 +00:00
2022-06-18 18:04:24 +00:00
if ( Entity . Format > = 8 )
2019-11-28 19:51:23 +00:00
Transfer . VerifyTransferLegalityG8 ( this ) ;
2022-06-18 18:04:24 +00:00
}
2017-10-21 04:07:15 +00:00
2022-06-18 18:04:24 +00:00
private void ParsePK4 ( )
{
UpdateChecks ( ) ;
if ( Entity . Format > 4 )
Transfer . VerifyTransferLegalityG4 ( this ) ;
if ( Entity . Format > = 8 )
Transfer . VerifyTransferLegalityG8 ( this ) ;
}
2019-04-04 03:13:30 +00:00
2022-06-18 18:04:24 +00:00
private void ParsePK5 ( )
{
UpdateChecks ( ) ;
NHarmonia . Verify ( this ) ;
if ( Entity . Format > = 8 )
Transfer . VerifyTransferLegalityG8 ( this ) ;
}
2018-07-27 02:34:27 +00:00
2022-06-18 18:04:24 +00:00
private void ParsePK6 ( )
{
UpdateChecks ( ) ;
if ( Entity . Format > = 8 )
Transfer . VerifyTransferLegalityG8 ( this ) ;
}
2020-05-10 03:47:32 +00:00
2022-06-18 18:04:24 +00:00
private void ParsePK7 ( )
{
if ( Entity . VC )
UpdateVCTransferInfo ( ) ;
UpdateChecks ( ) ;
if ( Entity . Format > = 8 )
Transfer . VerifyTransferLegalityG8 ( this ) ;
}
2018-07-02 00:07:29 +00:00
2022-06-18 18:04:24 +00:00
private void ParsePK8 ( )
{
UpdateChecks ( ) ;
Transfer . VerifyTransferLegalityG8 ( this ) ;
}
2020-07-31 18:17:31 +00:00
2022-06-18 18:04:24 +00:00
/// <summary>
/// Adds a new Check parse value.
/// </summary>
/// <param name="s">Check severity</param>
/// <param name="c">Check comment</param>
/// <param name="i">Check type</param>
internal void AddLine ( Severity s , string c , CheckIdentifier i ) = > AddLine ( new CheckResult ( s , c , i ) ) ;
2020-08-05 05:56:55 +00:00
2022-06-18 18:04:24 +00:00
/// <summary>
/// Adds a new Check parse value.
/// </summary>
/// <param name="chk">Check result to add.</param>
internal void AddLine ( CheckResult chk ) = > Parse . Add ( chk ) ;
2018-07-02 00:07:29 +00:00
2022-06-18 18:04:24 +00:00
private void UpdateVCTransferInfo ( )
{
var enc = ( Info . EncounterOriginalGB = EncounterMatch ) ;
if ( enc is EncounterInvalid )
return ;
var vc = EncounterStaticGenerator . GetVCStaticTransferEncounter ( Entity , enc , Info . EvoChainsAllGens . Gen7 ) ;
Info . EncounterMatch = vc ;
2019-11-26 19:01:09 +00:00
2022-06-18 18:04:24 +00:00
foreach ( var z in Transfer . VerifyVCEncounter ( Entity , enc , vc , Info . Moves ) )
AddLine ( z ) ;
2020-04-06 23:32:23 +00:00
2022-06-18 18:04:24 +00:00
Transfer . VerifyTransferLegalityG12 ( this ) ;
}
2020-04-06 23:32:23 +00:00
2022-06-18 18:04:24 +00:00
private void UpdateChecks ( )
{
PIDEC . Verify ( this ) ;
Nickname . Verify ( this ) ;
LanguageIndex . Verify ( this ) ;
Trainer . Verify ( this ) ;
TrainerID . Verify ( this ) ;
IVs . Verify ( this ) ;
EVs . Verify ( this ) ;
Level . Verify ( this ) ;
Ribbon . Verify ( this ) ;
AbilityValues . Verify ( this ) ;
BallIndex . Verify ( this ) ;
FormValues . Verify ( this ) ;
MiscValues . Verify ( this ) ;
GenderValues . Verify ( this ) ;
Item . Verify ( this ) ;
Contest . Verify ( this ) ;
Marking . Verify ( this ) ;
var format = Entity . Format ;
if ( format is 4 or 5 or 6 ) // Gen 6->7 transfer removes this property.
Gen4GroundTile . Verify ( this ) ;
if ( format < 6 )
return ;
History . Verify ( this ) ;
if ( format < 8 ) // Gen 7->8 transfer removes these properties.
ConsoleRegion . Verify ( this ) ;
if ( Entity is ITrainerMemories )
Memory . Verify ( this ) ;
if ( Entity is ISuperTrain )
Medal . Verify ( this ) ;
if ( format < 7 )
return ;
HyperTraining . Verify ( this ) ;
MiscValues . VerifyVersionEvolution ( this ) ;
if ( format < 8 )
return ;
Mark . Verify ( this ) ;
Arceus . Verify ( this ) ;
2016-02-23 06:52:48 +00:00
}
}