PKHeX/PKHeX.Core/Legality/Bulk/BulkAnalysis.cs
Kurt 907ed0b32f struct CheckResult
tiny object, eliminate allocation for them since they're 10-16 bytes at most.
2023-04-23 16:04:04 -07:00

107 lines
3.7 KiB
C#

using System;
using System.Collections.Generic;
namespace PKHeX.Core.Bulk;
/// <summary>
/// Analyzes content within a <see cref="SaveFile"/> for overall <see cref="PKM"/> legality analysis.
/// </summary>
public sealed class BulkAnalysis
{
public readonly IReadOnlyList<SlotCache> AllData;
public readonly IReadOnlyList<LegalityAnalysis> AllAnalysis;
public readonly ITrainerInfo Trainer;
public readonly List<CheckResult> Parse = new();
public readonly Dictionary<ulong, SlotCache> Trackers = new();
public readonly bool Valid;
public readonly IBulkAnalysisSettings Settings;
private readonly bool[] CloneFlags;
/// <summary>
/// Checks if the <see cref="AllData"/> entity at <see cref="entryIndex"/> was previously marked as a clone of another index.
/// </summary>
public bool GetIsClone(int entryIndex) => CloneFlags[entryIndex];
/// <summary>
/// Marks the <see cref="AllData"/> entity at <see cref="entryIndex"/> as a clone of another index.
/// </summary>
public bool SetIsClone(int entryIndex, bool value = true) => CloneFlags[entryIndex] = value;
public BulkAnalysis(SaveFile sav, IBulkAnalysisSettings settings)
{
Trainer = sav;
Settings = settings;
var list = new List<SlotCache>(sav.BoxSlotCount + (sav.HasParty ? 6 : 0) + 5);
SlotInfoLoader.AddFromSaveFile(sav, list);
list.RemoveAll(IsEmptyData);
AllData = list;
AllAnalysis = GetIndividualAnalysis(list);
CloneFlags = new bool[AllData.Count];
ScanAll();
Valid = Parse.Count == 0 || Parse.TrueForAll(static z => z.Valid);
}
// Remove things that aren't actual stored data, or already flagged by legality checks.
private static bool IsEmptyData(SlotCache obj)
{
var pk = obj.Entity;
if ((uint)(pk.Species - 1) >= pk.MaxSpeciesID)
return true;
if (!pk.ChecksumValid)
return true;
return false;
}
/// <summary>
/// Supported <see cref="IBulkAnalyzer"/> checkers that will be iterated through to check all entities.
/// </summary>
public static readonly List<IBulkAnalyzer> Analyzers = new()
{
new StandardCloneChecker(),
new DuplicateTrainerChecker(),
new DuplicatePIDChecker(),
new DuplicateEncryptionChecker(),
new HandlerChecker(),
new DuplicateGiftChecker(),
};
private void ScanAll()
{
foreach (var analyzer in Analyzers)
analyzer.Analyze(this);
}
private static string GetSummary(SlotCache entry) => $"[{entry.Identify()}]";
/// <summary>
/// Adds a new entry to the <see cref="Parse"/> list.
/// </summary>
public void AddLine(SlotCache first, SlotCache second, string msg, CheckIdentifier i, Severity s = Severity.Invalid)
{
var c = $"{msg}{Environment.NewLine}{GetSummary(first)}{Environment.NewLine}{GetSummary(second)}{Environment.NewLine}";
var chk = new CheckResult(s, i, c);
Parse.Add(chk);
}
/// <summary>
/// Adds a new entry to the <see cref="Parse"/> list.
/// </summary>
public void AddLine(SlotCache first, string msg, CheckIdentifier i, Severity s = Severity.Invalid)
{
var c = $"{msg}{Environment.NewLine}{GetSummary(first)}{Environment.NewLine}";
var chk = new CheckResult(s, i, c);
Parse.Add(chk);
}
private static IReadOnlyList<LegalityAnalysis> GetIndividualAnalysis(IReadOnlyList<SlotCache> pkms)
{
var results = new LegalityAnalysis[pkms.Count];
for (int i = 0; i < pkms.Count; i++)
results[i] = Get(pkms[i]);
return results;
}
private static LegalityAnalysis Get(SlotCache cache) => new(cache.Entity, cache.SAV.Personal, cache.Source.Origin);
}