2017-11-09 06:59:18 +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 ;
2016-03-05 04:43:00 +00:00
using System.Linq ;
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
2017-01-08 07:54:09 +00:00
namespace PKHeX.Core
2016-02-23 06:52:48 +00:00
{
2017-10-24 06:12:58 +00:00
/// <summary>
/// Legality Check object containing the <see cref="CheckResult"/> data and overview values from the parse.
/// </summary>
2020-11-27 20:00:49 +00:00
public sealed class LegalityAnalysis
2016-02-23 06:52:48 +00:00
{
2021-03-14 18:28:46 +00:00
/// <summary> The entity we are checking. </summary>
2018-06-24 05:00:01 +00:00
internal readonly PKM pkm ;
2021-03-14 18:28:46 +00:00
/// <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>
2018-07-21 03:22:46 +00:00
internal readonly PersonalInfo PersonalInfo ;
2021-03-14 18:28:46 +00:00
2021-08-06 00:16:13 +00:00
private readonly List < CheckResult > Parse = new ( 8 ) ;
2016-10-23 19:48:49 +00:00
2019-02-15 04:14:46 +00:00
/// <summary>
/// Parse result list allowing view of the legality parse.
/// </summary>
public IReadOnlyList < CheckResult > Results = > Parse ;
2019-11-16 01:34:18 +00:00
/// <summary>
/// Only use this when trying to mutate the legality. Not for use when checking legality.
/// </summary>
public void ResetParse ( ) = > Parse . Clear ( ) ;
2018-07-21 03:22:46 +00:00
/// <summary>
/// Matched encounter data for the <see cref="pkm"/>.
/// </summary>
2018-06-24 05:00:01 +00:00
public IEncounterable EncounterMatch = > Info . EncounterMatch ;
2018-07-21 03:22:46 +00:00
/// <summary>
/// Original encounter data for the <see cref="pkm"/>.
/// </summary>
/// <remarks>
/// Generation 1/2 <see cref="pkm"/> 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>
2019-12-09 23:48:54 +00:00
public IEncounterable EncounterOriginal = > Info . EncounterOriginal ;
2017-11-07 02:06:23 +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
public readonly SlotOrigin SlotOrigin ;
2018-07-21 03:22:46 +00:00
/// <summary>
/// Indicates if all checks ran to completion.
/// </summary>
/// <remarks>This value is false if any checks encountered an error.</remarks>
2017-08-16 04:16:47 +00:00
public readonly bool Parsed ;
2018-07-21 03:22:46 +00:00
/// <summary>
/// Indicates if all checks returned a <see cref="Severity.Valid"/> result.
/// </summary>
2017-08-16 04:16:47 +00:00
public readonly bool Valid ;
2018-07-21 03:22:46 +00:00
/// <summary>
/// Contains various data reused for multiple checks.
/// </summary>
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 readonly LegalInfo Info ;
2018-07-21 03:22:46 +00:00
2017-10-24 06:12:58 +00:00
/// <summary>
2020-01-17 07:22:54 +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>
/// <param name="pk">Input data to check</param>
2017-11-07 03:31:24 +00:00
/// <param name="table"><see cref="SaveFile"/> specific personal data</param>
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
/// <param name="source">Details about where the <see cref="pk"/> originated from.</param>
public LegalityAnalysis ( PKM pk , PersonalTable table , SlotOrigin source = SlotOrigin . Party ) : this ( pk , table . GetFormEntry ( pk . Species , pk . Form ) , source ) { }
2020-01-17 07:22:54 +00:00
/// <summary>
/// Checks the input <see cref="PKM"/> data for legality.
/// </summary>
/// <param name="pk">Input data to check</param>
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
/// <param name="source">Details about where the <see cref="pk"/> originated from.</param>
public LegalityAnalysis ( PKM pk , SlotOrigin source = SlotOrigin . Party ) : this ( pk , pk . PersonalInfo , source ) { }
2020-01-17 07:22:54 +00:00
/// <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>
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
/// <param name="source">Details about where the <see cref="pk"/> originated from.</param>
public LegalityAnalysis ( PKM pk , PersonalInfo pi , SlotOrigin source = SlotOrigin . Party )
2016-02-23 06:52:48 +00:00
{
2018-05-28 00:57:45 +00:00
pkm = pk ;
2020-01-17 07:22:54 +00:00
PersonalInfo = pi ;
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
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
if ( pkm . Format < = 2 ) // prior to storing GameVersion
pkm . TradebackStatus = GBRestrictions . GetTradebackStatusInitial ( pkm ) ;
2021-08-06 00:16:13 +00:00
Info = new LegalInfo ( pkm , Parse ) ;
2017-07-18 23:21:31 +00:00
#if SUPPRESS
2016-06-30 05:59:20 +00:00
try
2017-07-18 23:21:31 +00:00
#endif
2016-06-30 05:59:20 +00:00
{
2021-02-14 18:23:53 +00:00
EncounterFinder . FindVerifiedEncounter ( pkm , Info ) ;
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
if ( ! pkm . IsOriginValid )
AddLine ( Severity . Invalid , LEncConditionBadSpecies , CheckIdentifier . GameOrigin ) ;
GetParseMethod ( ) ( ) ;
2017-02-14 06:49:32 +00:00
2018-05-12 15:13:39 +00:00
Valid = Parse . All ( chk = > chk . Valid )
& & Info . Moves . All ( m = > m . Valid )
2017-12-05 04:16:54 +00:00
& & Info . Relearn . All ( m = > m . Valid ) ;
2016-11-15 00:27:15 +00:00
2021-08-06 00:16:13 +00:00
if ( ! Valid & & IsPotentiallyMysteryGift ( Info , pkm ) )
2018-09-01 21:11:12 +00:00
AddLine ( Severity . Indeterminate , LFatefulGiftMissing , CheckIdentifier . Fateful ) ;
2019-11-26 02:32:50 +00:00
Parsed = true ;
2016-06-30 05:59:20 +00:00
}
2017-07-18 23:21:31 +00:00
#if SUPPRESS
2020-09-05 19:11:43 +00:00
// We want to swallow any error from malformed input data from the user. The Valid state is all that we really need.
#pragma warning disable CA1031 // Do not catch general exception types
2017-03-24 06:15:49 +00:00
catch ( Exception e )
2020-09-05 19:11:43 +00:00
#pragma warning restore CA1031 // Do not catch general exception types
2017-03-24 06:15:49 +00:00
{
2017-07-18 23:21:31 +00:00
System . Diagnostics . Debug . WriteLine ( e . Message ) ;
2017-03-24 06:15:49 +00:00
Valid = false ;
2021-06-08 02:59:08 +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 moves = Info . Moves ;
2021-06-08 02:59:08 +00:00
// Moves and Relearn arrays can potentially be empty on error.
// ReSharper disable once ConstantNullCoalescingCondition
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
for ( int i = 0 ; i < moves . Length ; i + + )
moves [ i ] ? ? = new CheckMoveResult ( MoveSource . None , pkm . Format , Severity . Indeterminate , L_AError , CheckIdentifier . CurrentMove ) ;
2021-06-08 02:59:08 +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 relearn = Info . Relearn ;
2021-06-08 02:59:08 +00:00
// ReSharper disable once ConstantNullCoalescingCondition
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
for ( int i = 0 ; i < relearn . Length ; i + + )
relearn [ i ] ? ? = new CheckResult ( Severity . Indeterminate , L_AError , CheckIdentifier . RelearnMove ) ;
2021-06-08 02:59:08 +00:00
2018-09-01 21:11:12 +00:00
AddLine ( Severity . Invalid , L_AError , CheckIdentifier . Misc ) ;
2017-03-24 06:15:49 +00:00
}
2017-07-18 23:21:31 +00:00
#endif
2016-10-23 19:48:49 +00:00
}
2018-07-27 02:34:27 +00:00
2021-08-06 00:16:13 +00:00
private static bool IsPotentiallyMysteryGift ( LegalInfo info , PKM pk )
{
if ( info . EncounterOriginal is not EncounterInvalid enc )
return false ;
if ( enc . Generation < = 3 )
return true ;
if ( ! pk . FatefulEncounter )
return false ;
if ( enc . Generation < 6 )
return true ;
if ( info . Relearn . Any ( chk = > ! chk . Valid ) )
return true ;
return false ;
}
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
private Action GetParseMethod ( )
2017-12-05 04:16:54 +00:00
{
2018-10-28 04:59:31 +00:00
if ( pkm . Format < = 2 ) // prior to storing GameVersion
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
return ParsePK1 ;
2020-12-11 04:42:30 +00:00
int gen = pkm . Generation ;
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
if ( gen < = 0 )
gen = pkm . Format ;
return gen switch
2017-12-05 04:16:54 +00:00
{
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
3 = > ParsePK3 ,
4 = > ParsePK4 ,
5 = > ParsePK5 ,
6 = > ParsePK6 ,
2017-12-05 04:16:54 +00:00
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
1 = > ParsePK7 ,
2 = > ParsePK7 ,
7 = > ParsePK7 ,
2019-09-23 23:56:47 +00:00
2020-01-07 01:50:18 +00:00
8 = > ParsePK8 ,
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
_ = > throw new Exception ( )
} ;
2017-12-05 04:16:54 +00:00
}
2018-07-27 02:34:27 +00:00
2018-05-28 00:57:45 +00:00
private void ParsePK1 ( )
2017-02-13 01:00:03 +00:00
{
2018-07-25 02:33:42 +00:00
if ( pkm . TradebackStatus = = TradebackType . Any & & Info . Generation ! = pkm . Format )
pkm . TradebackStatus = TradebackType . WasTradeback ; // Example: GSC Pokemon with only possible encounters in RBY, like the legendary birds
2018-07-02 00:07:29 +00:00
Nickname . Verify ( this ) ;
Level . Verify ( this ) ;
Level . VerifyG1 ( this ) ;
2018-06-24 05:00:01 +00:00
Trainer . VerifyOTG1 ( this ) ;
2020-11-27 20:00:49 +00:00
MiscValues . VerifyMiscG1 ( this ) ;
2018-06-11 15:05:15 +00:00
if ( pkm . Format = = 2 )
2018-07-02 00:07:29 +00:00
Item . Verify ( this ) ;
2017-02-13 01:00:03 +00:00
}
2018-07-27 02:34:27 +00:00
2018-05-28 00:57:45 +00:00
private void ParsePK3 ( )
2017-03-18 23:50:34 +00:00
{
2017-06-18 01:37:19 +00:00
UpdateChecks ( ) ;
2017-05-28 04:17:53 +00:00
if ( pkm . Format > 3 )
2018-06-24 05:00:01 +00:00
Transfer . VerifyTransferLegalityG3 ( this ) ;
2017-04-30 19:17:27 +00:00
2018-05-28 00:57:45 +00:00
if ( pkm . Version = = ( int ) GameVersion . CXD )
2018-07-02 00:07:29 +00:00
CXD . Verify ( this ) ;
2017-06-09 03:57:30 +00:00
2020-12-22 06:33:48 +00:00
if ( Info . EncounterMatch is WC3 { NotDistributed : true } )
2018-09-01 21:11:12 +00:00
AddLine ( Severity . Invalid , LEncUnreleased , CheckIdentifier . Encounter ) ;
2019-11-17 00:05:45 +00:00
if ( pkm . Format > = 8 )
Transfer . VerifyTransferLegalityG8 ( this ) ;
2017-03-18 23:50:34 +00:00
}
2018-07-27 02:34:27 +00:00
2018-05-28 00:57:45 +00:00
private void ParsePK4 ( )
2017-03-18 23:50:34 +00:00
{
2017-06-18 01:37:19 +00:00
UpdateChecks ( ) ;
2017-05-28 04:17:53 +00:00
if ( pkm . Format > 4 )
2018-06-24 05:00:01 +00:00
Transfer . VerifyTransferLegalityG4 ( this ) ;
2019-11-17 00:05:45 +00:00
if ( pkm . Format > = 8 )
Transfer . VerifyTransferLegalityG8 ( this ) ;
2017-03-18 23:50:34 +00:00
}
2018-07-27 02:34:27 +00:00
2018-05-28 00:57:45 +00:00
private void ParsePK5 ( )
2017-03-18 23:50:34 +00:00
{
2017-06-18 01:37:19 +00:00
UpdateChecks ( ) ;
2018-07-02 00:07:29 +00:00
NHarmonia . Verify ( this ) ;
2019-11-17 00:05:45 +00:00
if ( pkm . Format > = 8 )
Transfer . VerifyTransferLegalityG8 ( this ) ;
2017-03-18 23:50:34 +00:00
}
2018-07-27 02:34:27 +00:00
2018-05-28 00:57:45 +00:00
private void ParsePK6 ( )
2016-10-23 19:48:49 +00:00
{
2017-06-18 01:37:19 +00:00
UpdateChecks ( ) ;
2019-11-17 00:05:45 +00:00
if ( pkm . Format > = 8 )
Transfer . VerifyTransferLegalityG8 ( this ) ;
2016-10-23 19:48:49 +00:00
}
2018-07-27 02:34:27 +00:00
2018-05-28 00:57:45 +00:00
private void ParsePK7 ( )
2016-10-23 19:48:49 +00:00
{
2017-07-09 15:47:17 +00:00
if ( pkm . VC )
UpdateVCTransferInfo ( ) ;
2017-06-18 01:37:19 +00:00
UpdateChecks ( ) ;
2019-11-17 00:05:45 +00:00
if ( pkm . Format > = 8 )
Transfer . VerifyTransferLegalityG8 ( this ) ;
2016-03-12 04:56:40 +00:00
}
2016-03-14 03:19:04 +00:00
2019-09-23 23:56:47 +00:00
private void ParsePK8 ( )
{
UpdateChecks ( ) ;
2019-11-28 19:51:23 +00:00
Transfer . VerifyTransferLegalityG8 ( this ) ;
2019-09-23 23:56:47 +00:00
}
2018-07-21 03:22:46 +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 ) ) ;
/// <summary>
/// Adds a new Check parse value.
/// </summary>
2018-07-27 02:34:27 +00:00
/// <param name="chk">Check result to add.</param>
2018-07-21 03:22:46 +00:00
internal void AddLine ( CheckResult chk ) = > Parse . Add ( chk ) ;
2017-12-12 00:01:24 +00:00
2017-07-09 15:47:17 +00:00
private void UpdateVCTransferInfo ( )
{
2019-12-09 23:48:54 +00:00
var enc = ( Info . EncounterOriginalGB = EncounterMatch ) ;
if ( enc is EncounterInvalid )
2018-06-10 07:26:33 +00:00
return ;
2021-02-01 17:52:25 +00:00
var vc = EncounterStaticGenerator . GetVCStaticTransferEncounter ( pkm , enc , Info . EvoChainsAllGens [ 7 ] ) ;
2020-09-05 19:11:43 +00:00
Info . EncounterMatch = vc ;
2017-10-21 04:07:15 +00:00
2020-09-05 19:11:43 +00:00
foreach ( var z in Transfer . VerifyVCEncounter ( pkm , enc , vc , Info . Moves ) )
2017-07-09 15:47:17 +00:00
AddLine ( z ) ;
2019-04-04 03:13:30 +00:00
Transfer . VerifyTransferLegalityG12 ( this ) ;
2017-07-09 15:47:17 +00:00
}
2018-07-27 02:34:27 +00:00
2017-06-18 01:37:19 +00:00
private void UpdateChecks ( )
2017-02-15 01:33:57 +00:00
{
2018-07-02 00:07:29 +00:00
PIDEC . Verify ( this ) ;
Nickname . Verify ( this ) ;
2020-11-27 20:00:49 +00:00
LanguageIndex . Verify ( this ) ;
2018-07-02 00:07:29 +00:00
Trainer . Verify ( this ) ;
IndividualValues . Verify ( this ) ;
EffortValues . Verify ( this ) ;
Level . Verify ( this ) ;
Ribbon . Verify ( this ) ;
2020-11-27 20:00:49 +00:00
AbilityValues . Verify ( this ) ;
BallIndex . Verify ( this ) ;
FormValues . Verify ( this ) ;
MiscValues . Verify ( this ) ;
GenderValues . Verify ( this ) ;
2018-07-02 00:07:29 +00:00
Item . Verify ( this ) ;
2020-05-10 03:47:32 +00:00
Contest . Verify ( this ) ;
2021-01-11 02:15:33 +00:00
var format = pkm . Format ;
2021-08-06 00:16:13 +00:00
if ( format is 4 or 5 or 6 ) // Gen 6->7 transfer removes this property.
Gen4GroundTile . Verify ( this ) ;
2018-07-02 00:07:29 +00:00
2021-01-11 02:15:33 +00:00
if ( format < 6 )
return ;
2020-07-31 18:17:31 +00:00
2019-12-08 02:12:31 +00:00
History . Verify ( this ) ;
2021-08-06 00:16:13 +00:00
if ( format < 8 ) // Gen 7->8 transfer removes these properties.
ConsoleRegion . Verify ( this ) ;
2020-08-05 05:56:55 +00:00
if ( pkm is ITrainerMemories )
Memory . Verify ( this ) ;
2019-11-26 19:01:09 +00:00
if ( pkm is ISuperTrain )
2018-11-14 03:10:31 +00:00
Medal . Verify ( this ) ;
2018-07-02 00:07:29 +00:00
2021-01-11 02:15:33 +00:00
if ( format < 7 )
2019-11-26 19:01:09 +00:00
return ;
HyperTraining . Verify ( this ) ;
2020-11-27 20:00:49 +00:00
MiscValues . VerifyVersionEvolution ( this ) ;
2020-04-06 23:32:23 +00:00
2021-01-11 02:15:33 +00:00
if ( format < 8 )
2020-04-06 23:32:23 +00:00
return ;
Mark . Verify ( this ) ;
2016-02-23 06:52:48 +00:00
}
}
}