diff --git a/PKHeX.Core/Game/GameVersion.cs b/PKHeX.Core/Game/GameVersion.cs index 99fcac002..4c4eeb7e3 100644 --- a/PKHeX.Core/Game/GameVersion.cs +++ b/PKHeX.Core/Game/GameVersion.cs @@ -354,7 +354,7 @@ USUM, /// - /// Pokémon Let's Go (TBD) + /// Pokémon Let's Go Pikachu & Eevee /// /// Used to lump data from the associated games as data assets are shared. /// diff --git a/PKHeX.Core/Legality/Encounters/Data/Encounters3Shadow.cs b/PKHeX.Core/Legality/Encounters/Data/Encounters3Shadow.cs index f166eaa7e..f618421f5 100644 --- a/PKHeX.Core/Legality/Encounters/Data/Encounters3Shadow.cs +++ b/PKHeX.Core/Legality/Encounters/Data/Encounters3Shadow.cs @@ -479,6 +479,7 @@ new NPCLock(085), // Shadow Dodrio new NPCLock(340, 18, 0, 127), // Whiscash (M) (Bashful) }}; + public static readonly TeamLock XRaticateDodrioSeen = new TeamLock { Species = 020, // Raticate Comment = "Dodrio Seen", @@ -678,8 +679,8 @@ Species = 125, // Electabuzz Locks = new[] { new NPCLock(277), // Shadow Swellow - new NPCLock(065, 24, 0, 063), // Alakazam (M) (Serious) - new NPCLock(230, 6, 1, 127), // Kingdra (F) (Bashful) + new NPCLock(065, 24, 0, 063), // Alakazam (M) (Quirky) + new NPCLock(230, 6, 1, 127), // Kingdra (F) (Docile) new NPCLock(214, 18, 1, 127), // Heracross (F) (Bashful) }}; @@ -688,8 +689,8 @@ Comment = "Swellow Seen", Locks = new[] { new NPCLock(277, true), // Shadow Swellow (Seen) - new NPCLock(065, 24, 0, 063), // Alakazam (M) (Serious) - new NPCLock(230, 6, 1, 127), // Kingdra (F) (Bashful) + new NPCLock(065, 24, 0, 063), // Alakazam (M) (Quirky) + new NPCLock(230, 6, 1, 127), // Kingdra (F) (Docile) new NPCLock(214, 18, 1, 127), // Heracross (F) (Bashful) }}; diff --git a/PKHeX.Core/Legality/Tables.cs b/PKHeX.Core/Legality/Tables.cs index 8ef7271be..037c42e14 100644 --- a/PKHeX.Core/Legality/Tables.cs +++ b/PKHeX.Core/Legality/Tables.cs @@ -450,6 +450,7 @@ namespace PKHeX.Core #region Games + public static readonly int[] Games_7gg = { 42, 43 }; public static readonly int[] Games_7vc2 = { 39, 40, 41 }; // Gold, Silver, Crystal public static readonly int[] Games_7vc1 = { 35, 36, 37, 38 }; // Red, Green, Blue, Yellow public static readonly int[] Games_7go = { 34 }; diff --git a/PKHeX.Core/Legality/Tables7.cs b/PKHeX.Core/Legality/Tables7.cs index b7edd8e96..93298e770 100644 --- a/PKHeX.Core/Legality/Tables7.cs +++ b/PKHeX.Core/Legality/Tables7.cs @@ -17,11 +17,6 @@ namespace PKHeX.Core internal const int MaxItemID_7_USUM = 959; internal const int MaxAbilityID_7_USUM = 233; - internal const int MaxSpeciesID_7_GG = 809; - internal const int MaxMoveID_7_GG = 728; // todo - internal const int MaxItemID_7_GG = 959; // todo - internal const int MaxAbilityID_7_GG = MaxAbilityID_7_USUM; - #region Met Locations internal static readonly int[] Met_SMc = { 0, 60002, 30002, }; diff --git a/PKHeX.Core/PKM/Util/FormConverter.cs b/PKHeX.Core/PKM/Util/FormConverter.cs index ce8989abc..c57c4024a 100644 --- a/PKHeX.Core/PKM/Util/FormConverter.cs +++ b/PKHeX.Core/PKM/Util/FormConverter.cs @@ -44,6 +44,8 @@ namespace PKHeX.Core return GetFormsGen7(species, types, forms); } + private static bool IsGG() => false; + public static bool IsTotemForm(int species, int form, int generation = 7) { if (generation != 7) @@ -83,6 +85,7 @@ namespace PKHeX.Core } private static readonly string[] EMPTY = {""}; + private const string Partner = "Starter"; private static string[] GetFormsGen1(int species, IReadOnlyList types, IReadOnlyList forms, int generation) { @@ -96,6 +99,14 @@ namespace PKHeX.Core forms[805], // Mega X forms[806], // Mega Y }; + + case 133 when IsGG(): + return new[] + { + types[000], // Normal + Partner, + }; + case 025: return GetFormsPikachu(generation, types, forms); @@ -547,7 +558,7 @@ namespace PKHeX.Core forms[734], // Cosplay }; case 7: - return new[] + var arr = new[] { types[000], // Normal forms[813], // Original @@ -558,6 +569,11 @@ namespace PKHeX.Core forms[818], // Alola forms[1063] // Partner }; + if (!IsGG()) + return arr; + System.Array.Resize(ref arr, arr.Length + 1); + arr[arr.Length - 1] = Partner; + return arr; } } diff --git a/PKHeX.Core/Saves/SaveFile.cs b/PKHeX.Core/Saves/SaveFile.cs index 494ac0e64..00d172d40 100644 --- a/PKHeX.Core/Saves/SaveFile.cs +++ b/PKHeX.Core/Saves/SaveFile.cs @@ -825,5 +825,62 @@ namespace PKHeX.Core public virtual string EBerryName => string.Empty; public virtual bool IsEBerryIsEnigma => true; + + /// + /// Compresses the by pulling out the empty storage slots and putting them at the end, retaining all existing data. + /// + /// Count of actual stored. + /// Important slot pointers that need to be repointed if a slot moves. + /// True if was updated, false if no update done. + public bool CompressStorage(out int storedCount, params ushort[][] slotPointers) + { + // keep track of empty slots, and only write them at the end if slots were shifted (no need otherwise). + var empty = new List(); + bool shiftedSlots = false; + + ushort ctr = 0; + int size = SIZE_STORED; + int count = BoxSlotCount; + for (int i = 0; i < count; i++) + { + int offset = Box + (i * size); + if (IsPKMPresent(offset)) + { + if (ctr != i) // copy required + { + shiftedSlots = true; // appending empty slots afterwards is now required since a rewrite was done + Buffer.BlockCopy(Data, offset, Data, Box + (ctr * size), size); + foreach (var ptrSet in slotPointers) + { + for (int j = 0; j < ptrSet.Length; j++) // update ptr + { + if (ptrSet[j] == i) + ptrSet[j] = ctr; + } + } + } + ctr++; + continue; + } + + // pop out an empty slot; save all unused data & preserve order + byte[] data = new byte[size]; + Buffer.BlockCopy(Data, offset, data, 0, size); + empty.Add(data); + } + + storedCount = ctr; + + if (!shiftedSlots) + return false; + + for (int i = ctr; i < count; i++) + { + var data = empty[i - ctr]; + int offset = Box + (i * size); + Buffer.BlockCopy(Data, offset, data, 0, size); + } + return true; + } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/EntreeForest.cs b/PKHeX.Core/Saves/Substructures/Gen5/EntreeForest.cs index 8bce55aff..ac483cca7 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/EntreeForest.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/EntreeForest.cs @@ -23,7 +23,6 @@ namespace PKHeX.Core private const int EncryptionSeedOffset = 0x84C; - private readonly byte[] Data; public EntreeForest(byte[] data) diff --git a/PKHeX.Core/Saves/Substructures/Gen7/SaveBlock.cs b/PKHeX.Core/Saves/Substructures/Gen7/SaveBlock.cs new file mode 100644 index 000000000..f41a269b8 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen7/SaveBlock.cs @@ -0,0 +1,8 @@ +namespace PKHeX.Core +{ + public abstract class SaveBlock + { + protected readonly byte[] Data; + protected SaveBlock(SaveFile SAV) => Data = SAV.Data; + } +} \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan.cs new file mode 100644 index 000000000..c7e9dab18 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan.cs @@ -0,0 +1,163 @@ +using System; +using System.Linq; + +namespace PKHeX.Core +{ + public abstract class Zukan + { + protected SaveFile SAV { get; set; } + protected int PokeDex { get; set; } + protected int PokeDexLanguageFlags { get; set; } + + protected abstract int OFS_SEEN { get; } + protected abstract int OFS_CAUGHT { get; } + protected abstract int BitSeenSize { get; } + protected abstract int DexLangFlagByteCount { get; } + protected abstract int DexLangIDCount { get; } + protected abstract int GetDexLangFlag(int lang); + + protected Func DexFormIndexFetcher { get; set; } + + protected abstract bool GetSaneFormsToIterate(int species, out int formStart, out int formEnd, int formIn); + protected abstract void SetSpindaDexData(PKM pkm, bool alreadySeen); + protected abstract void SetAllDexFlagsLanguage(int bit, int lang, bool value = true); + protected abstract void SetAllDexSeenFlags(int baseBit, int altform, int gender, bool isShiny, bool value = true); + + public bool GetFlag(int ofs, int bitIndex) => SAV.GetFlag(PokeDex + ofs, bitIndex); + public void SetFlag(int ofs, int bitIndex, bool value = true) => SAV.SetFlag(PokeDex + ofs, bitIndex, value); + + public bool GetCaught(int species) => GetFlag(OFS_CAUGHT, species - 1); + public void SetCaught(int species, bool value = true) => SetFlag(OFS_CAUGHT, species - 1, value); + + public int SeenCount => Enumerable.Range(1, SAV.MaxSpeciesID).Count(GetSeen); + public int CaughtCount => Enumerable.Range(1, SAV.MaxSpeciesID).Count(GetCaught); + public decimal PercentSeen => (decimal)SeenCount / SAV.MaxSpeciesID; + public decimal PercentCaught => (decimal)CaughtCount / SAV.MaxSpeciesID; + + public bool GetSeen(int species) + { + // check all 4 seen flags (gender/shiny) + for (int i = 0; i < 4; i++) + { + if (GetFlag(OFS_SEEN + (i * BitSeenSize), species - 1)) + return true; + } + return false; + } + + public void SetSeen(int species, bool value = true) + { + if (!value) + { + ClearSeen(species); + return; + } + + // check all 4 seen flags (gender/shiny) + for (int i = 0; i < 4; i++) + { + if (GetFlag(OFS_SEEN + (i * BitSeenSize), species - 1)) + return; + } + var gender = SAV.Personal[species].RandomGender & 1; + SetAllDexSeenFlags(species - 1, 0, gender, false); + } + + private void ClearSeen(int species) + { + SetCaught(species, false); + for (int i = 0; i < 4; i++) + SetFlag(OFS_SEEN + (i * BitSeenSize), species - 1, false); + } + + public virtual void SetDex(PKM pkm) + { + if (PokeDex < 0 || SAV.Version == GameVersion.Invalid) // sanity + return; + if (pkm.Species == 0 || pkm.Species > SAV.MaxSpeciesID) // out of range + return; + if (pkm.IsEgg) // do not add + return; + + int bit = pkm.Species - 1; + SetCaught(pkm.Species); // Set the Owned Flag + if (pkm.Species == 327) // Spinda + SetSpindaDexData(pkm, GetSeen(pkm.Species)); + SetAllDexSeenFlags(bit, pkm.AltForm, pkm.Gender & 1, pkm.IsShiny); // genderless -> male + SetAllDexFlagsLanguage(bit, pkm.Language); + } + + protected void SetDexFlags(int baseBit, int formBit, int gender, int shiny, bool value = true) + { + int shift = gender | (shiny << 1); + + // Set the [Species/Gender/Shiny] Seen Flag + SetFlag(OFS_SEEN + (shift * BitSeenSize), baseBit, value); + + // Set the Display flag if none are set + bool displayed = GetIsSpeciesFormAnyDisplayed(baseBit, formBit); + if (!displayed || !value) + SetFlag(OFS_SEEN + ((4 + shift) * BitSeenSize), formBit, value); + } + + private bool GetIsSpeciesFormAnyDisplayed(int baseBit, int formBit) + { + // Check Displayed Status for base form + for (int i = 4; i < 8; i++) + { + if (GetFlag(OFS_SEEN + (i * BitSeenSize), baseBit)) + return true; + } + if (baseBit == formBit) + return false; + + // If form is not base form, check form too + for (int i = 4; i < 8; i++) + { + if (GetFlag(OFS_SEEN + (i * BitSeenSize), formBit)) + return true; + } + return false; + } + + // Bulk Manipulation + + public void SetDexEntriesAll(bool value = true, int max = -1) + { + if (max <= 0) + max = SAV.MaxSpeciesID; + for (int i = 1; i <= max; i++) + SetDexEntriesSingle(i, value); + } + + public void SetDexEntriesSingle(int species, bool value = true) + { + SetCaught(species, value); + SetSeen(species, value); + + var entry = SAV.Personal[species]; + int baseBit = species - 1; + int fc = entry.FormeCount; + for (int f = 0; f < fc; f++) + { + if (!entry.OnlyFemale) + { + SetAllDexSeenFlags(baseBit, f, 0, false, value); + SetAllDexSeenFlags(baseBit, f, 0, true, value); + } + if (!entry.OnlyMale && !entry.Genderless) + { + SetAllDexSeenFlags(baseBit, f, 1, false, value); + SetAllDexSeenFlags(baseBit, f, 1, true, value); + } + } + SetAllDexFlagsLanguage(baseBit, value); + } + + protected void SetAllDexFlagsLanguage(int bit, bool value = true) + { + for (int i = 1; i <= DexLangIDCount + 1; i++) + SetAllDexFlagsLanguage(bit, i, value); + } + } +} \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan6.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan6.cs new file mode 100644 index 000000000..408eb1c56 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan6.cs @@ -0,0 +1,176 @@ +namespace PKHeX.Core +{ + public class Zukan6 : Zukan + { + protected override int OFS_SEEN => OFS_CAUGHT + BitSeenSize; + protected override int OFS_CAUGHT => 0x8; + protected override int BitSeenSize => 0x60; + protected override int DexLangFlagByteCount => 7; + protected override int DexLangIDCount => 7; + + protected override int GetDexLangFlag(int lang) + { + lang--; + if (lang > 5) + lang--; // 0-6 language vals + if ((uint)lang > 5) + return -1; + return lang; + } + + protected override bool GetSaneFormsToIterate(int species, out int formStart, out int formEnd, int formIn) + { + formStart = 0; + formEnd = 0; + return false; + } + + protected override void SetSpindaDexData(PKM pkm, bool alreadySeen) + { + + } + + protected override void SetAllDexFlagsLanguage(int bit, int lang, bool value = true) + { + lang = GetDexLangFlag(lang); + if (lang < 0) + return; + + int lbit = (bit * DexLangIDCount) + lang; + if (lbit < DexLangFlagByteCount << 3) // Sanity check for max length of region + SetFlag(PokeDexLanguageFlags, lbit, value); + } + + protected override void SetAllDexSeenFlags(int baseBit, int altform, int gender, bool isShiny, bool value = true) + { + var shiny = isShiny ? 1 : 0; + SetDexFlags(baseBit, 0, gender, shiny); + SetFormFlags(baseBit + 1, altform, shiny, value); + } + + public override void SetDex(PKM pkm) + { + if (PokeDex < 0) + return; + if (pkm.Species == 0) + return; + if (pkm.Species > SAV.MaxSpeciesID) + return; + if (SAV.Version == GameVersion.Invalid) + return; + + int bit = pkm.Species - 1; + SetCaughtFlag(bit, pkm.Version); + + // Set the [Species/Gender/Shiny] Seen Flag + SetAllDexSeenFlags(pkm.Species - 1, pkm.AltForm, pkm.Gender, pkm.IsShiny); + SetAllDexFlagsLanguage(bit, pkm.Language); + SetFormFlags(pkm); + } + + private void SetCaughtFlag(int bit, int origin) + { + // Owned quality flag + if (SAV.ORAS) + { + SetFlag(OFS_CAUGHT, bit); + // Set DexNav count (only if not encountered previously) + var count = ((SAV6)SAV).GetEncounterCount(bit); + if (count == 0) + ((SAV6)SAV).SetEncounterCount(bit, 1); + } + else + { + // Species: 1-649 for X/Y, and not for ORAS; Set the Foreign Owned Flag + if (origin < 24 && bit < 649) + SetFlag(OFS_CAUGHT + 0x644, bit); + else + SetFlag(OFS_CAUGHT, bit); + } + } + + private int FormLen => SAV.ORAS ? 0x26 : 0x18; + private int FormDex => PokeDex + 0x8 + (BitSeenSize * 9); + + private void SetFormFlags(PKM pkm) + { + int species = pkm.Species; + int form = pkm.AltForm; + var shiny = pkm.IsShiny ? 1 : 0; + SetFormFlags(species, form, shiny); + } + + private void SetFormFlags(int species, int form, int shiny, bool value = true) + { + int fc = SAV.Personal[species].FormeCount; + int f = SAV.ORAS ? SaveUtil.GetDexFormIndexORAS(species, fc) : SaveUtil.GetDexFormIndexXY(species, fc); + if (f < 0) + return; + + var bit = f + form; + + // Set Form Seen Flag + SetFlag(FormDex + (FormLen * shiny), bit, value); + + // Set Displayed Flag if necessary, check all flags + if (!value || !GetIsFormDisplayed(f, fc)) + SetFlag(FormDex + (FormLen * (2 + shiny)), bit, value); + } + + private bool GetIsFormDisplayed(int f, int fc) + { + for (int i = 0; i < fc; i++) + { + var bit2 = f + i; + if (GetFlag(FormDex + (FormLen * 2), bit2)) // Nonshiny + return true; // already set + if (GetFlag(FormDex + (FormLen * 3), bit2)) // Shiny + return true; // already set + } + return false; + } + + public bool[] GetLanguageBitflags(int species) + { + var result = new bool[DexLangIDCount]; + int bit = species - 1; + for (int i = 0; i < DexLangIDCount; i++) + { + int lbit = (bit * DexLangIDCount) + i; + result[i] = GetFlag(PokeDexLanguageFlags, lbit); + } + return result; + } + + public void SetLanguageBitflags(int species, bool[] value) + { + int bit = species - 1; + for (int i = 0; i < DexLangIDCount; i++) + { + int lbit = (bit * DexLangIDCount) + i; + SetFlag(PokeDexLanguageFlags, lbit, value[i]); + } + } + + public void ToggleLanguageFlagsAll(bool value) + { + var arr = GetBlankLanguageBits(value); + for (int i = 1; i <= SAV.MaxSpeciesID; i++) + SetLanguageBitflags(i, arr); + } + + public void ToggleLanguageFlagsSingle(int species, bool value) + { + var arr = GetBlankLanguageBits(value); + SetLanguageBitflags(species, arr); + } + + private bool[] GetBlankLanguageBits(bool value) + { + var result = new bool[DexLangIDCount]; + for (int i = 0; i < DexLangIDCount; i++) + result[i] = value; + return result; + } + } +} \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs new file mode 100644 index 000000000..33809c604 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs @@ -0,0 +1,312 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace PKHeX.Core +{ + public class Zukan7 : Zukan + { + private const int MAGIC = 0x2F120F17; + private const int SIZE_MAGIC = 4; + private const int SIZE_FLAGS = 4; + private const int SIZE_MISC = 0x80; // Misc Data (1024 bits) + private const int SIZE_CAUGHT = 0x68; // 832 bits + + protected override int OFS_CAUGHT => SIZE_MAGIC + SIZE_FLAGS + SIZE_MISC; + protected override int OFS_SEEN => OFS_CAUGHT + SIZE_CAUGHT; + + protected override int BitSeenSize => 0x8C; // 1120 bits + protected override int DexLangFlagByteCount => 920; // 0x398 = 817*9, top off the savedata block. + protected override int DexLangIDCount => 9; // CHT, skipping langID 6 (unused) + + private readonly IList FormBaseSpecies; + + protected Zukan7() { } + + public Zukan7(SaveFile sav, int dex, int langflag) + { + SAV = sav; + PokeDex = dex; + PokeDexLanguageFlags = langflag; + DexFormIndexFetcher = SAV.USUM ? (Func) SaveUtil.GetDexFormIndexSM : SaveUtil.GetDexFormIndexSM; + FormBaseSpecies = GetFormIndexBaseSpeciesList(); + Debug.Assert(BitConverter.ToUInt32(SAV.Data, PokeDex) == MAGIC); + } + + protected override void SetAllDexSeenFlags(int baseBit, int altform, int gender, bool isShiny, bool value = true) + { + int species = baseBit + 1; + + if (species == 351) // castform + isShiny = false; + + // Starting with Gen7, form bits are stored in the same region as the species flags. + int formstart = altform; + int formend = altform; + bool reset = GetSaneFormsToIterate(species, out int fs, out int fe, formstart); + if (reset) + { + formstart = fs; + formend = fe; + } + + int shiny = isShiny ? 1 : 0; + for (int form = formstart; form <= formend; form++) + { + int formBit = baseBit; + if (form > 0) // Override the bit to overwrite + { + int fc = SAV.Personal[species].FormeCount; + if (fc > 1) // actually has forms + { + int f = DexFormIndexFetcher(species, fc, SAV.MaxSpeciesID - 1); + if (f >= 0) // bit index valid + formBit = f + form; + } + } + SetDexFlags(baseBit, formBit, gender, shiny, value); + } + } + + protected override bool GetSaneFormsToIterate(int species, out int formStart, out int formEnd, int formIn) + { + // 004AA370 in Moon + // Simplified in terms of usage -- only overrides to give all the battle forms for a pkm + switch (species) + { + case 351: // Castform + formStart = 0; + formEnd = 3; + return true; + + case 020: // Raticate + case 105: // Marowak + formStart = 0; + formEnd = 1; + return true; + + case 735: // Gumshoos + case 758: // Salazzle + case 754: // Lurantis + case 738: // Vikavolt + case 784: // Kommo-o + case 752: // Araquanid + case 777: // Togedemaru + case 743: // Ribombee + case 744: // Rockruff + break; + + case 421: // Cherrim + case 555: // Darmanitan + case 648: // Meloetta + case 746: // Wishiwashi + case 778: // Mimikyu + formStart = 0; + formEnd = 1; + return true; + + case 774 when formIn <= 6: // Minior + break; // don't give meteor forms except the first + + case 718 when formIn > 1: + break; + default: + int count = SAV.USUM ? SaveUtil.GetDexFormCountUSUM(species) : SaveUtil.GetDexFormCountSM(species); + formStart = formEnd = 0; + return count < formIn; + } + formStart = 0; + formEnd = 0; + return true; + } + + protected override int GetDexLangFlag(int lang) + { + if (lang > 10 || lang == 6 || lang <= 0) + return -1; // invalid language + + if (lang >= 7) // skip over langID 6 (unused) + lang--; + lang--; // skip over langID 0 (unused) => [0-8] + return lang; + } + + protected override void SetSpindaDexData(PKM pkm, bool alreadySeen) + { + int shift = (pkm.Gender & 1) | (pkm.IsShiny ? 2 : 0); + if (alreadySeen) // update? + { + var flag1 = (1 << (shift + 4)); + if ((SAV.Data[PokeDex + 0x84] & flag1) != 0) // Already showing this one + return; + BitConverter.GetBytes(pkm.EncryptionConstant).CopyTo(SAV.Data, PokeDex + 0x8E8 + (shift * 4)); + SAV.Data[PokeDex + 0x84] |= (byte)(flag1 | (1 << shift)); + } + else if ((SAV.Data[PokeDex + 0x84] & (1 << shift)) == 0) + { + BitConverter.GetBytes(pkm.EncryptionConstant).CopyTo(SAV.Data, PokeDex + 0x8E8 + (shift * 4)); + SAV.Data[PokeDex + 0x84] |= (byte)(1 << shift); + } + } + + // Dex Flags + public bool NationalDex + { + get => (SAV.Data[PokeDex + 4] & 1) == 1; + set => SAV.Data[PokeDex + 4] = (byte)((SAV.Data[PokeDex + 4] & 0xFE) | (value ? 1 : 0)); + } + + /// + /// Gets the last viewed dex entry in the Pokedex (by National Dex ID), internally called DefaultMons + /// + public uint CurrentViewedDex => BitConverter.ToUInt32(SAV.Data, PokeDex + 4) >> 9 & 0x3FF; + + public IEnumerable GetAllFormEntries(int spec) + { + var fc = SAV.Personal[spec].FormeCount; + for (int j = 1; j < fc; j++) + { + int start = j; + int end = j; + if (GetSaneFormsToIterate(spec, out int s, out int n, j)) + { + start = s; + end = n; + } + start = Math.Max(1, start); + for (int f = start; f <= end; f++) + { + int x = GetDexFormIndex(spec, fc, f); + if (x >= 0) + yield return x; + } + } + } + + public int GetDexFormIndex(int spec, int fc, int f) + { + var index = DexFormIndexFetcher(spec, fc, f); + if (index < 0) + return index; + return index + SAV.MaxSpeciesID - 1; + } + + public IEnumerable GetEntryNames(IReadOnlyList Species) + { + var names = new List(); + for (int i = 1; i <= SAV.MaxSpeciesID; i++) + names.Add($"{i:000} - {Species[i]}"); + + // Add Formes + int ctr = SAV.MaxSpeciesID; + for (int spec = 1; spec <= SAV.MaxSpeciesID; spec++) + { + int c = SAV.Personal[spec].FormeCount; + for (int f = 1; f < c; f++) + { + int x = GetDexFormIndex(spec, c, f); + if (x >= 0) + names.Add($"{ctr++:000} - {Species[spec]}-{f}"); + } + } + return names; + } + + /// + /// Gets a list of Species IDs that a given dex-forme index corresponds to. + /// + /// + private List GetFormIndexBaseSpeciesList() + { + var baseSpecies = new List(); + for (int spec = 1; spec <= SAV.MaxSpeciesID; spec++) + { + int c = SAV.Personal[spec].FormeCount; + for (int f = 1; f < c; f++) + { + int x = GetDexFormIndex(spec, c, f); + if (x >= 0) + baseSpecies.Add(spec); + } + } + return baseSpecies; + } + + public int GetBaseSpeciesGenderValue(int index) + { + // meowstic special handling + const int meow = 678; + if (index == meow - 1 || (index >= SAV.MaxSpeciesID && FormBaseSpecies[index - SAV.MaxSpeciesID] == meow)) + return index < SAV.MaxSpeciesID ? 0 : 254; // M : F + + if (index < SAV.MaxSpeciesID) + return SAV.Personal[index + 1].Gender; + + index -= SAV.MaxSpeciesID; + int spec = FormBaseSpecies[index]; + return SAV.Personal[spec].Gender; + } + + public int GetBaseSpecies(int index) + { + if (index <= SAV.MaxSpeciesID) + return index; + + return FormBaseSpecies[index - SAV.MaxSpeciesID - 1]; + } + + protected override void SetAllDexFlagsLanguage(int bit, int lang, bool value = true) + { + lang = GetDexLangFlag(lang); + if (lang < 0) + return; + + int lbit = (bit * DexLangIDCount) + lang; + if (lbit < DexLangFlagByteCount << 3) // Sanity check for max length of region + SetFlag(PokeDexLanguageFlags, lbit, value); + } + + public bool[] GetLanguageBitflags(int species) + { + var result = new bool[DexLangIDCount]; + int bit = species - 1; + for (int i = 0; i < DexLangIDCount; i++) + { + int lbit = (bit * DexLangIDCount) + i; + result[i] = GetFlag(PokeDexLanguageFlags, lbit); + } + return result; + } + + public void SetLanguageBitflags(int species, bool[] value) + { + int bit = species - 1; + for (int i = 0; i < DexLangIDCount; i++) + { + int lbit = (bit * DexLangIDCount) + i; + SetFlag(PokeDexLanguageFlags, lbit, value[i]); + } + } + + public void ToggleLanguageFlagsAll(bool value) + { + var arr = GetBlankLanguageBits(value); + for (int i = 1; i <= SAV.MaxSpeciesID; i++) + SetLanguageBitflags(i, arr); + } + + public void ToggleLanguageFlagsSingle(int species, bool value) + { + var arr = GetBlankLanguageBits(value); + SetLanguageBitflags(species, arr); + } + + private bool[] GetBlankLanguageBits(bool value) + { + var result = new bool[DexLangIDCount]; + for (int i = 0; i < DexLangIDCount; i++) + result[i] = value; + return result; + } + } +} \ No newline at end of file diff --git a/Tests/PKHeX.Tests/Legality/ShadowTests.cs b/Tests/PKHeX.Tests/Legality/ShadowTests.cs index 9a3015273..d8e6db1fe 100644 --- a/Tests/PKHeX.Tests/Legality/ShadowTests.cs +++ b/Tests/PKHeX.Tests/Legality/ShadowTests.cs @@ -122,7 +122,6 @@ namespace PKHeX.Tests.Legality Assert.IsTrue(match, "Unable to verify lock conditions: " + teams[0].Species); } - [TestMethod] [TestCategory(VerifyPIDCategory)] public void VerifyPIDResultsDelcatty()