using System; using System.Collections.Generic; namespace PKHeX.Core.Bulk; /// /// Analyzes content within a for overall legality analysis. /// public sealed class BulkAnalysis { public readonly IReadOnlyList AllData; public readonly IReadOnlyList AllAnalysis; public readonly ITrainerInfo Trainer; public readonly List Parse = new(); public readonly Dictionary Trackers = new(); public readonly bool Valid; public readonly IBulkAnalysisSettings Settings; private readonly bool[] CloneFlags; /// /// Checks if the entity at was previously marked as a clone of another index. /// public bool GetIsClone(int entryIndex) => CloneFlags[entryIndex]; /// /// Marks the entity at as a clone of another index. /// 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(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; } /// /// Supported checkers that will be iterated through to check all entities. /// public static readonly List 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()}]"; /// /// Adds a new entry to the list. /// 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); } /// /// Adds a new entry to the list. /// 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 GetIndividualAnalysis(IReadOnlyList 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); }