public SAV3(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data) { LoadBlocks(out BlockOrder, out BlockOfs); Version = versionOverride != GameVersion.Any ? versionOverride : GetVersion(Data, BlockOfs[0]); _personal = SaveUtil.GetG3Personal(Version) ?? PersonalTable.RS; // Japanese games are limited to 5 character OT names; any unused characters are 0xFF. // 5 for JP, 7 for INT. There's always 1 terminator, thus we can check 0x6-0x7 being 0xFFFF = INT // OT name is stored at the top of the first block. Japanese = BitConverter.ToInt16(Data, BlockOfs[0] + 0x6) == 0; LegalKeyItems = Version switch { GameVersion.RS => Legal.Pouch_Key_RS, GameVersion.E => Legal.Pouch_Key_E, _ => Legal.Pouch_Key_FRLG }; PokeDex = BlockOfs[0] + 0x18; SeenFlagOffsets = Version switch { GameVersion.RS => new[] { PokeDex + 0x44, BlockOfs[1] + 0x938, BlockOfs[4] + 0xC0C }, GameVersion.E => new[] { PokeDex + 0x44, BlockOfs[1] + 0x988, BlockOfs[4] + 0xCA4 }, _ => new[] { PokeDex + 0x44, BlockOfs[1] + 0x5F8, BlockOfs[4] + 0xB98 } }; Initialize(); }
/// <summary> /// Checks the input <see cref="PKM"/> data for legality. /// </summary> /// <param name="pk">Input data to check</param> /// <param name="table"><see cref="SaveFile"/> specific personal data</param> public LegalityAnalysis(PKM pk, PersonalTable table = null) { pkm = pk; #if SUPPRESS try #endif { PersonalInfo = table?.GetFormeEntry(pkm.Species, pkm.AltForm) ?? pkm.PersonalInfo; ParseLegality(); if (Parse.Count <= 0) { return; } Valid = Parse.All(chk => chk.Valid) && Info.Moves.All(m => m.Valid) && Info.Relearn.All(m => m.Valid); if (pkm.FatefulEncounter && Info.Relearn.Any(chk => !chk.Valid) && EncounterMatch is EncounterInvalid) { AddLine(Severity.Indeterminate, LFatefulGiftMissing, CheckIdentifier.Fateful); } } #if SUPPRESS catch (Exception e) { System.Diagnostics.Debug.WriteLine(e.Message); Valid = false; AddLine(Severity.Invalid, L_AError, CheckIdentifier.Misc); Error = true; } #endif Parsed = true; }
public SAV3(GameVersion version = GameVersion.FRLG, bool japanese = false) { Version = version switch { GameVersion.FR or GameVersion.LG => GameVersion.FRLG, GameVersion.R or GameVersion.S => GameVersion.RS, _ => version }; _personal = SaveUtil.GetG3Personal(Version); Japanese = japanese; BlockOrder = Array.Empty <short>(); LegalKeyItems = Version switch { GameVersion.RS => Legal.Pouch_Key_RS, GameVersion.E => Legal.Pouch_Key_E, _ => Legal.Pouch_Key_FRLG }; PokeDex = 0x18; SeenFlagOffsets = Array.Empty <int>(); Initialize(); ClearBoxes(); }
public SAV3(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data) { LoadBlocks(out BlockOrder); // Copy chunk to the allocated location LoadBlocks(Small, 0, 1); LoadBlocks(Large, 1, 5); LoadBlocks(Storage, 5, BLOCK_COUNT); Version = versionOverride != GameVersion.Any ? versionOverride : GetVersion(Small); _personal = SaveUtil.GetG3Personal(Version); // Japanese games are limited to 5 character OT names; any unused characters are 0xFF. // 5 for JP, 7 for INT. There's always 1 terminator, thus we can check 0x6-0x7 being 0xFFFF = INT // OT name is stored at the top of the first block. Japanese = BitConverter.ToInt16(Small, 0x6) == 0; LegalKeyItems = Version switch { GameVersion.RS => Legal.Pouch_Key_RS, GameVersion.E => Legal.Pouch_Key_E, _ => Legal.Pouch_Key_FRLG }; PokeDex = 0x18; SeenFlagOffsets = Version switch { GameVersion.RS => new[] { 0x938, 0x3A8C }, GameVersion.E => new[] { 0x988, 0x3B24 }, _ => new[] { 0x5F8, 0x3A18 } }; Initialize(); }
public SAV3(GameVersion version = GameVersion.FRLG, bool japanese = false) : base(SaveUtil.SIZE_G3RAW) { if (version == GameVersion.FR || version == GameVersion.LG) { Version = GameVersion.FRLG; } else if (version == GameVersion.R || version == GameVersion.S) { Version = GameVersion.RS; } else { Version = version; } _personal = SaveUtil.GetG3Personal(Version) ?? PersonalTable.RS; Japanese = japanese; LoadBlocks(out BlockOrder, out BlockOfs); // spoof block offsets BlockOfs = Enumerable.Range(0, BLOCK_COUNT).ToArray(); LegalKeyItems = Version switch { GameVersion.RS => Legal.Pouch_Key_RS, GameVersion.E => Legal.Pouch_Key_E, _ => Legal.Pouch_Key_FRLG }; PokeDex = BlockOfs[0] + 0x18; SeenFlagOffsets = Array.Empty <int>(); Initialize(); ClearBoxes(); }
public List<Zukan8EntryInfo> GetRawIndexes(PersonalTable pt, int dexRevision) { var result = new List<Zukan8EntryInfo>(); for (int i = 1; i <= pt.MaxSpeciesID; i++) { var p = (PersonalInfoSWSH)pt[i]; var index = p.PokeDexIndex; if (index != 0) result.Add(new Zukan8EntryInfo(i, new Zukan8Index(Zukan8Type.Galar, index))); } if (dexRevision == 0) return result; for (int i = 1; i <= pt.MaxSpeciesID; i++) { var p = (PersonalInfoSWSH)pt[i]; var index = p.ArmorDexIndex; if (index != 0) result.Add(new Zukan8EntryInfo(i, new Zukan8Index(Zukan8Type.Armor, index))); } if (dexRevision == 1) return result; for (int i = 1; i <= pt.MaxSpeciesID; i++) { var p = (PersonalInfoSWSH)pt[i]; var index = p.CrownDexIndex; if (index != 0) result.Add(new Zukan8EntryInfo(i, new Zukan8Index(Zukan8Type.Crown, index))); } return result; }
private static Dictionary<int, Zukan8Index> GetDexLookup(PersonalTable pt, int dexRevision) { var lookup = new Dictionary<int, Zukan8Index>(); for (int i = 1; i <= pt.MaxSpeciesID; i++) { var p = (PersonalInfoSWSH) pt[i]; var index = p.PokeDexIndex; if (index != 0) { lookup.Add(i, new Zukan8Index(Zukan8Type.Galar, index)); continue; } if (dexRevision == 0) continue; var armor = p.ArmorDexIndex; if (armor != 0) { lookup.Add(i, new Zukan8Index(Zukan8Type.Armor, armor)); continue; } if (dexRevision == 1) continue; var crown = p.CrownDexIndex; if (crown != 0) { lookup.Add(i, new Zukan8Index(Zukan8Type.Crown, armor)); // continue; } } return lookup; }
private EvolutionTree(IReadOnlyList <byte[]> data, GameVersion game, PersonalTable personal, int maxSpeciesTree) { Game = game; Personal = personal; MaxSpeciesTree = maxSpeciesTree; Entries = GetEntries(data); Lineage = CreateTree(); }
public bool ResetPersonal(GameVersion g) { if (g is not(GameVersion.FR or GameVersion.LG)) { return(false); } _personal = g == GameVersion.FR ? PersonalTable.FR : PersonalTable.LG; return(true); }
public EvolutionTree(byte[][] data, GameVersion game, PersonalTable personal, int maxSpecies) { Game = game; Personal = personal; MaxSpecies = maxSpecies; switch (game) { case GameVersion.SM: Entries.AddRange(data.Select(d => new EvolutionSet7(d))); break; case GameVersion.ORAS: Entries.AddRange(data.Select(d => new EvolutionSet6(d))); break; } // Create Lineages Lineage = new EvolutionLineage[Entries.Count]; for (int i = 0; i < Entries.Count; i++) Lineage[i] = new EvolutionLineage(); if (Game == GameVersion.ORAS) Array.Resize(ref Lineage, maxSpecies + 1); // Populate Lineages for (int i = 1; i < Lineage.Length; i++) { // Iterate over all possible evolutions var s = Entries[i]; foreach (EvolutionMethod evo in s.PossibleEvolutions) { int index = getIndex(evo); if (index < 0) continue; var sourceEvo = evo.Copy(i); Lineage[index].Insert(sourceEvo); // If current entries has a pre-evolution, propagate to evolution as well if (Lineage[i].Chain.Count > 0) Lineage[index].Insert(Lineage[i].Chain[0]); if (index >= i) continue; // If destination species evolves into something (ie a 'baby' Pokemon like Cleffa) // Add it to the corresponding parent chains foreach (EvolutionMethod mid in Entries[index].PossibleEvolutions) { int newIndex = getIndex(mid); if (newIndex < 0) continue; Lineage[newIndex].Insert(sourceEvo); } } } fixEvoTreeManually(); }
// Check if any pre-evolution could have it flipped. static bool getWasDual(IEnumerable <EvoCriteria> evos, PersonalTable pt, PKM pk) { foreach (var evo in evos) { if (evo.Species == pk.Species) { continue; } var pe = pt.GetFormEntry(evo.Species, evo.Form); var abils = pe.Abilities; if (abils[0] != abils[1]) { return(true); } } return(false); }
private static bool GetWasDual(IReadOnlyList <EvoCriteria> evos, PersonalTable pt, ISpeciesForm pk) { foreach (var evo in evos) { if (evo.Species == pk.Species) { continue; } var pe = pt.GetFormEntry(evo.Species, evo.Form); var abils = pe.Abilities; if (CanAbilityCapsule(6, abils)) { return(true); } } return(false); }
private static void GetStaticMagnet(PersonalTable t, IEnumerable <EncounterSlot> grp, out List <EncounterSlot> s, out List <EncounterSlot> m) { const int steel = (int)MoveType.Steel; const int electric = (int)MoveType.Electric + 1; // offset by 1 in gen3/4 for the ??? type s = new List <EncounterSlot>(); m = new List <EncounterSlot>(); foreach (EncounterSlot Slot in grp) { var p = t[Slot.Species]; if (p.IsType(steel)) { m.Add(Slot); } if (p.IsType(electric)) { s.Add(Slot); } } }
/// <summary> /// 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. /// </summary> /// <param name="pk">Input data to check</param> /// <param name="table"><see cref="SaveFile"/> specific personal data</param> public LegalityAnalysis(PKM pk, PersonalTable table) : this(pk, table.GetFormeEntry(pk.Species, pk.AltForm)) { }
public EvolutionTree(byte[][] data, GameVersion game, PersonalTable personal, int maxSpeciesTree) { Game = game; Personal = personal; MaxSpeciesTree = maxSpeciesTree; switch (game) { case GameVersion.RBY: Entries = EvolutionSet1.GetArray(data[0], maxSpeciesTree); break; case GameVersion.GSC: Entries = EvolutionSet2.GetArray(data[0], maxSpeciesTree); break; case GameVersion.RS: Entries = EvolutionSet3.GetArray(data[0]); break; case GameVersion.DP: Entries = EvolutionSet4.GetArray(data[0]); break; case GameVersion.BW: Entries = EvolutionSet5.GetArray(data[0]); break; case GameVersion.ORAS: Entries.AddRange(data.Select(d => new EvolutionSet6(d))); break; case GameVersion.SM: Entries.AddRange(data.Select(d => new EvolutionSet7(d))); break; } // Create Lineages Lineage = new EvolutionLineage[Entries.Count]; for (int i = 0; i < Entries.Count; i++) { Lineage[i] = new EvolutionLineage(); } if (Game == GameVersion.ORAS) { Array.Resize(ref Lineage, MaxSpeciesTree + 1); } // Populate Lineages for (int i = 1; i < Lineage.Length; i++) { // Iterate over all possible evolutions var s = Entries[i]; foreach (EvolutionMethod evo in s.PossibleEvolutions) { int index = GetIndex(evo); if (index < 0) { continue; } var sourceEvo = evo.Copy(i); Lineage[index].Insert(sourceEvo); // If current entries has a pre-evolution, propagate to evolution as well if (Lineage[i].Chain.Count > 0) { Lineage[index].Insert(Lineage[i].Chain[0]); } if (index >= i) { continue; } // If destination species evolves into something (ie a 'baby' Pokemon like Cleffa) // Add it to the corresponding parent chains foreach (EvolutionMethod mid in Entries[index].PossibleEvolutions) { int newIndex = GetIndex(mid); if (newIndex < 0) { continue; } Lineage[newIndex].Insert(sourceEvo); } } } FixEvoTreeManually(); }
/// <summary> /// Marks Encounter Slots for party lead's ability slot influencing. /// </summary> /// <remarks>Magnet Pull attracts Steel type slots, and Static attracts Electric</remarks> /// <param name="Areas">Encounter Area array for game</param> /// <param name="t">Personal data for use with a given species' type</param> internal static void MarkEncountersStaticMagnetPull(IEnumerable <EncounterArea> Areas, PersonalTable t) { foreach (EncounterArea Area in Areas) { foreach (var grp in Area.Slots.GroupBy(z => z.Type)) { MarkEncountersStaticMagnetPull(grp, t); } } }
internal static void MarkEncountersStaticMagnetPullPermutation(IEnumerable <EncounterSlot> grp, PersonalTable t, List <EncounterSlot> permuted) { GetStaticMagnet(t, grp, out List <EncounterSlot> s, out List <EncounterSlot> m); // Apply static/magnet values; if any permutation has a unique slot combination, add it to the slot list. for (int i = 0; i < s.Count; i++) { var slot = s[i]; if (slot.Permissions.StaticIndex >= 0) // already has unique data { if (slot.IsMatchStatic(i, s.Count)) { continue; // same values, no permutation } if (permuted.Any(z => z.SlotNumber == slot.SlotNumber && z.IsMatchStatic(i, s.Count) && z.Species == slot.Species)) { continue; // same values, previously permuted } s[i] = slot = slot.Clone(); permuted.Add(slot); } slot.Permissions.StaticIndex = i; slot.Permissions.StaticCount = s.Count; } for (int i = 0; i < m.Count; i++) { var slot = m[i]; if (slot.Permissions.MagnetPullIndex >= 0) // already has unique data { if (slot.IsMatchStatic(i, m.Count)) { continue; // same values, no permutation } if (permuted.Any(z => z.SlotNumber == slot.SlotNumber && z.IsMatchMagnet(i, m.Count) && z.Species == slot.Species)) { continue; // same values, previously permuted } m[i] = slot = slot.Clone(); permuted.Add(slot); } slot.Permissions.MagnetPullIndex = i; slot.Permissions.MagnetPullCount = m.Count; } }
internal static void MarkEncountersStaticMagnetPull(IEnumerable <EncounterSlot> grp, PersonalTable t) { GetStaticMagnet(t, grp, out List <EncounterSlot> s, out List <EncounterSlot> m); for (var i = 0; i < s.Count; i++) { var slot = s[i]; slot.Permissions.StaticIndex = i; slot.Permissions.StaticCount = s.Count; } for (var i = 0; i < m.Count; i++) { var slot = m[i]; slot.Permissions.MagnetPullIndex = i; slot.Permissions.MagnetPullCount = m.Count; } }
/// <summary> /// Checks the input <see cref="PKM"/> data for legality. /// </summary> /// <param name="pk">Input data to check</param> /// <param name="table"><see cref="SaveFile"/> specific personal data</param> public LegalityAnalysis(PKM pk, PersonalTable table = null) { #if SUPPRESS try #endif { PersonalInfo = table?.GetFormeEntry(pk.Species, pk.AltForm) ?? pk.PersonalInfo; switch (pk.Format) // prior to storing GameVersion { case 1: ParsePK1(pk); break; case 2: ParsePK1(pk); break; } if (!Parse.Any()) { switch (pk.GenNumber) { case 3: ParsePK3(pk); break; case 4: ParsePK4(pk); break; case 5: ParsePK5(pk); break; case 6: ParsePK6(pk); break; case 1: case 2: case 7: ParsePK7(pk); break; } } if (Parse.Count > 0) { if (Parse.Any(chk => !chk.Valid)) { Valid = false; } else if (Info.Moves.Any(m => m.Valid != true)) { Valid = false; } else if (Info.Relearn.Any(m => m.Valid != true)) { Valid = false; } else { Valid = true; } if (pkm.FatefulEncounter && Info.Relearn.Any(chk => !chk.Valid) && EncounterMatch == null) { AddLine(Severity.Indeterminate, V188, CheckIdentifier.Fateful); } } } #if SUPPRESS catch (Exception e) { System.Diagnostics.Debug.WriteLine(e.Message); Valid = false; AddLine(Severity.Invalid, V190, CheckIdentifier.Misc); pkm = pk; Error = true; } #endif Parsed = true; }
/// <summary> /// Marks Encounter Slots for party lead's ability slot influencing. /// </summary> /// <remarks>Magnet Pull attracts Steel type slots, and Static attracts Electric</remarks> /// <param name="Areas">Encounter Area array for game</param> /// <param name="t">Personal data for use with a given species' type</param> internal static void MarkEncountersStaticMagnetPull(ref EncounterArea[] Areas, PersonalTable t) { const int steel = 8; const int electric = 12; foreach (EncounterArea Area in Areas) { var s = new List <EncounterSlot>(); // Static var m = new List <EncounterSlot>(); // Magnet Pull foreach (EncounterSlot Slot in Area.Slots) { var types = t[Slot.Species].Types; if (types[0] == steel || types[1] == steel) { m.Add(Slot); } if (types[0] == electric || types[1] == electric) { s.Add(Slot); } } foreach (var slot in s) { slot.Permissions.Static = true; slot.Permissions.StaticCount = s.Count; } foreach (var slot in m) { slot.Permissions.MagnetPull = true; slot.Permissions.MagnetPullCount = s.Count; } } }
public LearnLookup(PersonalTable table, Learnset[] learn, GameVersion version) { Version = version; Table = table; Learn = learn; }