diff --git a/PKHeX.Core/Editing/Saves/Slots/Extensions.cs b/PKHeX.Core/Editing/Saves/Slots/Extensions.cs index e33e609c2..53c646bda 100644 --- a/PKHeX.Core/Editing/Saves/Slots/Extensions.cs +++ b/PKHeX.Core/Editing/Saves/Slots/Extensions.cs @@ -103,7 +103,7 @@ private static List GetExtraSlots6XY(SAV6XY sav) [ new(sav.GTS.Upload, 0) {Type = StorageSlotType.GTS}, new(sav.Fused[0], 0) {Type = StorageSlotType.FusedKyurem}, - new(sav.SUBE.GiveSlot, 0) {Type = StorageSlotType.Misc}, // Old Man + new(sav.SUBE.GiveSlot, 0, Mutable: true) {Type = StorageSlotType.Misc}, // Old Man new(sav.BattleBox[0], 0) {Type = StorageSlotType.BattleBox}, new(sav.BattleBox[1], 1) {Type = StorageSlotType.BattleBox}, @@ -265,7 +265,7 @@ private static List GetExtraSlots9a(SAV9ZA sav) var ofs = (i * size) + 8; var entry = giveAway.Raw.Slice(ofs, PokeCrypto.SIZE_9PARTY); if (EntityDetection.IsPresent(entry.Span)) - list.Add(new(entry, i, true) { Type = StorageSlotType.Misc }); + list.Add(new(entry, i, true, Mutable: true) { Type = StorageSlotType.Misc }); else break; } diff --git a/PKHeX.Core/Legality/Verifiers/Ability/AbilityVerifier.cs b/PKHeX.Core/Legality/Verifiers/Ability/AbilityVerifier.cs index 8b55cfdc9..d5cbd31e3 100644 --- a/PKHeX.Core/Legality/Verifiers/Ability/AbilityVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/Ability/AbilityVerifier.cs @@ -465,7 +465,11 @@ private CheckResult VerifyBirthAbility(LegalityAnalysis data, PA9 pa9) { var enc = data.EncounterMatch; var pi = PersonalTable.ZA[enc.Species, enc.Form]; - var index = pa9.AbilityNumber >> 1; + var bitNum = pa9.AbilityNumber; + if (!IsValidAbilityBits(bitNum)) + return INVALID; + + var index = bitNum >> 1; var expect = pi.GetAbilityAtIndex(index); if (pa9.Ability != expect) return GetInvalid(AbilityMismatch); diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor9ZA.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor9ZA.cs index 3beaf50a4..f44556396 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor9ZA.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor9ZA.cs @@ -34,6 +34,8 @@ public sealed class SaveBlockAccessor9ZA(SAV9ZA sav) : SCBlockAccessor public EventWorkValueStorage WorkSpawn { get; } = new(sav, Block(sav, KEventWorkSpawn)); public EventWorkFlagStorage Flags { get; } = new(sav, Block(sav, KEventFlagsOther)); + public MableStatus9a Mable { get; } = new(sav, Block(sav, KStatusMable)); + private const uint KBox = 0x0d66012c; // Box Data private const uint KParty = 0x3AA1A9AD; // Party Data private const uint KItem = 0x21C9BD44; // Items @@ -60,7 +62,7 @@ public sealed class SaveBlockAccessor9ZA(SAV9ZA sav) : SCBlockAccessor private const uint KEventWorkMable = 0x03913534; // momiji_work (u64,u64)[1024] - Mable Tasks Status private const uint KEventCountMable = 0x8D80EC0F; // momiji_count (u64,u64)[64] - Mable Tasks Counts - private const uint KEventCountTitle = 0x2C2C6964; // TITLE_COUNT_XXXX (u64,u64)[64], such as total Money earned/spent (TITLE=TOTAL/placeholder title?) + private const uint KEventCountTitle = 0x2C2C6964; // title_count (u64,u64)[64] - Player earned display titles private const uint KEventWorkSpawn = 0x53FD0223; // Overworld Spawner 0x46500 small values (u64,u64)[18000] // 7C896A83 0x2000 unused // B25E7EE5 0x400 unused @@ -132,4 +134,6 @@ public sealed class SaveBlockAccessor9ZA(SAV9ZA sav) : SCBlockAccessor private const uint KNightRoyalePostBattleRewards = 0x356087AD; // object private const uint KNightRoyaleTrainerStatus = 0x718B8CB1; // object private const uint KNightRoyaleBonusCards = 0x2A07F494; // object + + private const uint KStatusMable = 0x85DBDCE9; // Mable Overall Status } diff --git a/PKHeX.Core/Saves/Substructures/Gen9/ZA/MableStatus9a.cs b/PKHeX.Core/Saves/Substructures/Gen9/ZA/MableStatus9a.cs new file mode 100644 index 000000000..3ceb25d70 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen9/ZA/MableStatus9a.cs @@ -0,0 +1,20 @@ +using PKHeX.Core; +using static System.Buffers.Binary.BinaryPrimitives; + +public sealed class MableStatus9a(SAV9ZA sav, SCBlock block) : SaveBlock(sav, block.Raw) +{ + /// + /// Current level achieved for Mable's Research. + /// + public uint LevelCurrent { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); } + + /// + /// Last viewed/claimed level for Mable's Research. + /// + public uint LevelViewed { get => ReadUInt32LittleEndian(Data[0x04..]); set => WriteUInt32LittleEndian(Data[0x04..], value); } + + /// + /// Research Points accumulated for Mable's Research. + /// + public uint Points { get => ReadUInt32LittleEndian(Data[0x08..]); set => WriteUInt32LittleEndian(Data[0x08..], value); } +}