From fa80dac2ac7a433231c752cd7e7b1aaf396a956d Mon Sep 17 00:00:00 2001 From: Kurt Date: Sun, 3 Mar 2024 23:13:16 -0600 Subject: [PATCH] Refactoring: Rework saveblock to be Memory based (#4200) --- .../Editing/Applicators/BallApplicator.cs | 4 +- .../Editors/EventUnlock/EventUnlocker.cs | 8 +- .../Editors/EventWork/Diff/EventWorkDiff.cs | 12 +- .../Editing/Saves/Editors/FakeSaveFile.cs | 2 - PKHeX.Core/Editing/Saves/Slots/BoxEdit.cs | 13 +- .../Saves/Slots/Exporting/BoxExport.cs | 3 +- PKHeX.Core/Editing/Saves/Slots/Extensions.cs | 125 ++- .../Editing/Saves/Slots/Info/SlotCache.cs | 4 +- .../Editing/Saves/Slots/Info/SlotInfoMisc.cs | 10 - PKHeX.Core/Game/GameUtil.cs | 2 +- .../Moveset/EncounterMovesetGenerator.cs | 2 +- .../PKM/Util/Conversion/FormConverter.cs | 2 +- PKHeX.Core/Saves/Access/ISCBlockArray.cs | 2 +- PKHeX.Core/Saves/Access/ISaveBlock5BW.cs | 5 + PKHeX.Core/Saves/Access/ISaveBlock6AO.cs | 3 +- PKHeX.Core/Saves/Access/ISaveBlock6Main.cs | 4 +- PKHeX.Core/Saves/Access/ISaveBlock7Main.cs | 6 +- PKHeX.Core/Saves/Access/ISaveBlock7b.cs | 3 +- PKHeX.Core/Saves/Access/SCBlockAccessor.cs | 8 + .../Saves/Access/SaveBlockAccessor5B2W2.cs | 51 +- .../Saves/Access/SaveBlockAccessor5BW.cs | 45 +- .../Saves/Access/SaveBlockAccessor6AO.cs | 82 +- .../Saves/Access/SaveBlockAccessor6AODemo.cs | 28 +- .../Saves/Access/SaveBlockAccessor6XY.cs | 58 +- .../Saves/Access/SaveBlockAccessor7SM.cs | 82 +- .../Saves/Access/SaveBlockAccessor7USUM.cs | 84 +- .../Saves/Access/SaveBlockAccessor7b.cs | 59 +- .../Saves/Access/SaveBlockAccessor8LA.cs | 42 +- .../Saves/Access/SaveBlockAccessor8SWSH.cs | 71 +- .../Saves/Access/SaveBlockAccessor9SV.cs | 107 +-- PKHeX.Core/Saves/Blocks/BlockInfo4.cs | 4 +- PKHeX.Core/Saves/Blocks/RecordBlock.cs | 13 +- PKHeX.Core/Saves/Blocks/SaveBlock.cs | 15 +- PKHeX.Core/Saves/Encryption/ColoCrypto.cs | 211 +++++ .../Saves/Encryption/MemeCrypto/MemeCrypto.cs | 2 +- .../Encryption/SwishCrypto/SCBlockCompare.cs | 2 +- .../Encryption/SwishCrypto/SCBlockMetadata.cs | 2 +- PKHeX.Core/Saves/SAV1.cs | 64 +- PKHeX.Core/Saves/SAV2.cs | 25 +- PKHeX.Core/Saves/SAV2Stadium.cs | 15 +- PKHeX.Core/Saves/SAV3.cs | 62 +- PKHeX.Core/Saves/SAV3Colosseum.cs | 212 +---- PKHeX.Core/Saves/SAV3E.cs | 30 +- PKHeX.Core/Saves/SAV3FRLG.cs | 27 +- PKHeX.Core/Saves/SAV3GCMemoryCard.cs | 13 +- PKHeX.Core/Saves/SAV3RS.cs | 26 +- PKHeX.Core/Saves/SAV3RSBox.cs | 17 +- PKHeX.Core/Saves/SAV3XD.cs | 20 +- PKHeX.Core/Saves/SAV4.cs | 407 +++++---- PKHeX.Core/Saves/SAV4BR.cs | 11 +- PKHeX.Core/Saves/SAV4DP.cs | 56 +- PKHeX.Core/Saves/SAV4HGSS.cs | 26 +- PKHeX.Core/Saves/SAV4Pt.cs | 21 +- PKHeX.Core/Saves/SAV4Sinnoh.cs | 10 +- PKHeX.Core/Saves/SAV5.cs | 92 +- PKHeX.Core/Saves/SAV5B2W2.cs | 23 +- PKHeX.Core/Saves/SAV5BW.cs | 27 +- PKHeX.Core/Saves/SAV6.cs | 67 +- PKHeX.Core/Saves/SAV6AO.cs | 114 +-- PKHeX.Core/Saves/SAV6AODemo.cs | 10 +- PKHeX.Core/Saves/SAV6XY.cs | 69 +- PKHeX.Core/Saves/SAV7.cs | 57 +- PKHeX.Core/Saves/SAV7SM.cs | 17 +- PKHeX.Core/Saves/SAV7USUM.cs | 15 +- PKHeX.Core/Saves/SAV7b.cs | 39 +- PKHeX.Core/Saves/SAV8BS.cs | 117 ++- PKHeX.Core/Saves/SAV8LA.cs | 14 +- PKHeX.Core/Saves/SAV8SWSH.cs | 17 +- PKHeX.Core/Saves/SAV9SV.cs | 17 +- PKHeX.Core/Saves/SAV_STADIUM.cs | 2 - PKHeX.Core/Saves/SaveFile.cs | 103 +-- PKHeX.Core/Saves/Storage/Bank3.cs | 4 +- PKHeX.Core/Saves/Storage/Bank4.cs | 4 +- PKHeX.Core/Saves/Storage/Bank7.cs | 4 +- PKHeX.Core/Saves/Storage/BulkStorage.cs | 2 - .../Substructures/Daycare/IDaycareEggState.cs | 6 + .../Daycare/IDaycareExperience.cs | 7 + .../Substructures/Daycare/IDaycareMulti.cs | 8 + .../Daycare/IDaycareRandomState.cs | 6 + .../Substructures/Daycare/IDaycareStorage.cs | 16 + .../Saves/Substructures/Gen3/Roamer3.cs | 52 +- .../Saves/Substructures/Gen3/SecretBase3.cs | 44 +- .../Saves/Substructures/Gen4/Chatter4.cs | 8 +- .../Saves/Substructures/Gen4/Dendou4.cs | 7 +- .../Saves/Substructures/Gen5/BattleBox5.cs | 23 + .../Saves/Substructures/Gen5/BattleSubway5.cs | 40 +- .../Saves/Substructures/Gen5/BoxLayout5.cs | 14 +- .../Saves/Substructures/Gen5/Chatter5.cs | 8 +- .../Saves/Substructures/Gen5/Daycare5.cs | 53 +- .../Saves/Substructures/Gen5/Encount5.cs | 41 +- .../Saves/Substructures/Gen5/Entralink5.cs | 30 +- .../Substructures/Gen5/Entree/EntreeForest.cs | 39 +- .../Saves/Substructures/Gen5/EventWork5.cs | 52 ++ .../Saves/Substructures/Gen5/FestaBlock5.cs | 42 +- .../Saves/Substructures/Gen5/MedalList5.cs | 24 +- PKHeX.Core/Saves/Substructures/Gen5/Misc5.cs | 24 +- .../Saves/Substructures/Gen5/Musical5.cs | 15 +- .../Saves/Substructures/Gen5/MyItem5B2W2.cs | 13 +- .../Saves/Substructures/Gen5/MyItem5BW.cs | 13 +- .../Saves/Substructures/Gen5/MysteryBlock5.cs | 134 +-- .../Saves/Substructures/Gen5/PWTBlock5.cs | 19 +- .../Saves/Substructures/Gen5/PlayerData5.cs | 64 +- .../Saves/Substructures/Gen5/Roamer5.cs | 5 +- .../Saves/Substructures/Gen5/UnityTower5.cs | 10 +- .../Saves/Substructures/Gen5/WhiteBlack5.cs | 20 + .../Saves/Substructures/Gen6/BattleBox6.cs | 22 +- .../Saves/Substructures/Gen6/BerryField6AO.cs | 56 ++ .../Saves/Substructures/Gen6/BoxLayout6.cs | 26 +- .../Saves/Substructures/Gen6/ConfigSave6.cs | 8 +- .../Saves/Substructures/Gen6/Daycare6AO.cs | 72 ++ .../Saves/Substructures/Gen6/Encount6.cs | 16 +- .../Saves/Substructures/Gen6/EventWork6.cs | 23 + .../Saves/Substructures/Gen6/Fashion6XY.cs | 4 +- PKHeX.Core/Saves/Substructures/Gen6/GTS6.cs | 10 + .../Saves/Substructures/Gen6/GameTime6.cs | 18 +- .../Saves/Substructures/Gen6/ItemInfo6.cs | 10 +- .../Saves/Substructures/Gen6/LinkBlock6.cs | 21 +- .../Saves/Substructures/Gen6/MaisonBlock.cs | 8 +- .../Saves/Substructures/Gen6/Misc6AO.cs | 20 +- .../Saves/Substructures/Gen6/Misc6XY.cs | 18 +- .../Saves/Substructures/Gen6/MyItem6AO.cs | 15 +- .../Saves/Substructures/Gen6/MyItem6XY.cs | 13 +- .../Saves/Substructures/Gen6/MyStatus6.cs | 72 +- .../Saves/Substructures/Gen6/MyStatus6XY.cs | 14 +- .../Saves/Substructures/Gen6/MysteryBlock6.cs | 67 +- .../Substructures/Gen6/OPower/OPower6.cs | 107 +-- .../Gen6/OPower/OPower6BattleType.cs | 13 + .../Gen6/OPower/OPower6FieldType.cs | 16 + .../Substructures/Gen6/OPower/OPower6Type.cs | 133 ++- .../Gen6/OPower/OPowerFlagSet.cs | 53 -- .../Gen6/OPower/OPowerFlagState.cs | 4 +- PKHeX.Core/Saves/Substructures/Gen6/PSS6.cs | 2 +- .../Saves/Substructures/Gen6/PlayTime6.cs | 18 +- PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs | 10 +- .../Saves/Substructures/Gen6/RecordBlock6.cs | 24 +- .../Substructures/Gen6/SangoInfoBlock.cs | 14 +- .../Gen6/SecretBase/SecretBase6.cs | 65 +- .../SecretBase/SecretBase6GoodPlacement.cs | 7 +- .../Gen6/SecretBase/SecretBase6GoodStock.cs | 10 +- .../Substructures/Gen6/SecretBase6Block.cs | 32 +- .../Saves/Substructures/Gen6/Situation6.cs | 26 +- .../Saves/Substructures/Gen6/SubEventLog6.cs | 34 +- .../Substructures/Gen6/SuperTrainBlock.cs | 79 +- .../Substructures/Gen6/TrainerFashion6.cs | 41 +- .../Substructures/Gen6/TrainerSprite6.cs | 4 +- .../Saves/Substructures/Gen6/UnionPokemon6.cs | 10 + .../Saves/Substructures/Gen7/BattleAgency7.cs | 7 +- .../Saves/Substructures/Gen7/BattleTree7.cs | 30 +- .../Saves/Substructures/Gen7/BoxLayout7.cs | 27 +- .../Saves/Substructures/Gen7/ConfigSave7.cs | 6 +- .../Saves/Substructures/Gen7/Daycare7.cs | 46 +- .../Saves/Substructures/Gen7/EventWork7.cs | 119 +++ .../Saves/Substructures/Gen7/FashionBlock7.cs | 12 +- .../Saves/Substructures/Gen7/FestaFacility.cs | 35 +- .../Saves/Substructures/Gen7/FieldMenu7.cs | 12 +- .../Substructures/Gen7/FieldMoveModelSave7.cs | 18 +- PKHeX.Core/Saves/Substructures/Gen7/GTS7.cs | 11 + .../Saves/Substructures/Gen7/GameTime7.cs | 20 +- .../Saves/Substructures/Gen7/HallOfFame7.cs | 37 - .../Saves/Substructures/Gen7/JoinFesta7.cs | 52 +- .../Substructures/Gen7/LGPE/CaptureRecords.cs | 22 +- .../Substructures/Gen7/LGPE/ConfigSave7b.cs | 6 +- .../Substructures/Gen7/LGPE/Coordinates7b.cs | 24 +- .../Substructures/Gen7/LGPE/Daycare7b.cs | 9 + .../Substructures/Gen7/LGPE/EventWork7b.cs | 14 +- .../Substructures/Gen7/LGPE/Fashion7b.cs | 6 +- .../Saves/Substructures/Gen7/LGPE/GP1.cs | 10 +- .../Substructures/Gen7/LGPE/GoParkStorage.cs | 7 +- .../Saves/Substructures/Gen7/LGPE/Misc7b.cs | 8 +- .../Saves/Substructures/Gen7/LGPE/MyItem7b.cs | 3 +- .../Substructures/Gen7/LGPE/MyStatus7b.cs | 80 +- .../Substructures/Gen7/LGPE/PlayTime7b.cs | 16 +- .../Gen7/LGPE/PlayerGeoLocation7b.cs | 4 +- .../Substructures/Gen7/LGPE/PokeListHeader.cs | 24 +- .../Substructures/Gen7/LGPE/WB7Records.cs | 93 +- PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs | 36 +- .../Saves/Substructures/Gen7/MyItem7SM.cs | 15 +- .../Saves/Substructures/Gen7/MyItem7USUM.cs | 3 +- .../Saves/Substructures/Gen7/MyStatus7.cs | 74 +- .../Saves/Substructures/Gen7/MysteryBlock7.cs | 70 +- .../Saves/Substructures/Gen7/PokeFinder7.cs | 30 +- .../Saves/Substructures/Gen7/ResortSave7.cs | 53 +- .../Saves/Substructures/Gen7/Situation7.cs | 46 +- .../Saves/Substructures/Gen7/UnionPokemon7.cs | 18 + .../Substructures/Gen7/WormholeInfoReader.cs | 8 +- .../Gen8/BS/BattleTowerWork8b.cs | 57 +- .../Gen8/BS/BattleTrainerStatus8b.cs | 16 +- .../Gen8/BS/BerryTreeGrowSave8b.cs | 6 +- .../Substructures/Gen8/BS/BoxLayout8b.cs | 40 +- .../Substructures/Gen8/BS/ConfigSave8b.cs | 60 +- .../Saves/Substructures/Gen8/BS/Contest8b.cs | 6 +- .../Gen8/BS/ContestPhotoLanguage8b.cs | 10 +- .../Saves/Substructures/Gen8/BS/Daycare8b.cs | 43 +- .../Substructures/Gen8/BS/EncounterSave8b.cs | 123 ++- .../Gen8/BS/FieldGimmickSave8b.cs | 12 +- .../Gen8/BS/FieldObjectSave8b.cs | 11 +- .../Saves/Substructures/Gen8/BS/FlagWork8b.cs | 35 +- .../Substructures/Gen8/BS/Gem8Version.cs | 8 +- .../Substructures/Gen8/BS/MenuSelect8b.cs | 15 +- .../Saves/Substructures/Gen8/BS/MyItem8b.cs | 23 +- .../Saves/Substructures/Gen8/BS/MyStatus8b.cs | 54 +- .../Substructures/Gen8/BS/MysteryBlock8b.cs | 141 +-- .../Saves/Substructures/Gen8/BS/Party8b.cs | 19 +- .../Saves/Substructures/Gen8/BS/PlayTime8b.cs | 14 +- .../Substructures/Gen8/BS/PlayerData8b.cs | 86 +- .../Substructures/Gen8/BS/PoffinSaveData8b.cs | 22 +- .../Saves/Substructures/Gen8/BS/Poketch8b.cs | 55 +- .../Substructures/Gen8/BS/RandomGroup8b.cs | 40 +- .../Saves/Substructures/Gen8/BS/Record8b.cs | 12 +- .../Substructures/Gen8/BS/RecordAddData8b.cs | 35 +- .../Gen8/BS/SaveItemShortcut8b.cs | 14 +- .../Saves/Substructures/Gen8/BS/SealDeco8b.cs | 36 +- .../Saves/Substructures/Gen8/BS/SealList8b.cs | 8 +- .../Substructures/Gen8/BS/SealSticker8b.cs | 22 +- .../Substructures/Gen8/BS/SystemData8b.cs | 24 +- .../Substructures/Gen8/BS/UgCountRecord8b.cs | 48 +- .../Substructures/Gen8/BS/UgSaveData8b.cs | 44 +- .../Gen8/BS/UndergroundItem8b.cs | 20 +- .../Gen8/BS/UndergroundItemList8b.cs | 12 +- .../Substructures/Gen8/BS/UnionSaveData8b.cs | 12 +- .../Saves/Substructures/Gen8/BS/Zukan8b.cs | 30 +- .../Substructures/Gen8/BS/ZukanSpinda8b.cs | 14 +- .../Substructures/Gen8/LA/BoxLayout8a.cs | 2 +- .../Substructures/Gen8/LA/Coordinates8a.cs | 16 +- .../Substructures/Gen8/LA/LastSaved8a.cs | 2 +- .../Saves/Substructures/Gen8/LA/MyStatus8a.cs | 22 +- .../Saves/Substructures/Gen8/LA/PlayTime8a.cs | 8 +- .../Substructures/Gen8/LA/PlayerFashion8a.cs | 24 +- .../Gen8/LA/Pokedex/PokedexSaveData.cs | 10 +- .../Gen8/LA/Pokedex/PokedexSaveGlobalData.cs | 10 +- .../Gen8/LA/Pokedex/PokedexSaveLocalData.cs | 11 +- .../LA/Pokedex/PokedexSaveResearchEntry.cs | 11 +- .../LA/Pokedex/PokedexSaveStatisticsEntry.cs | 10 +- .../Saves/Substructures/Gen8/SWSH/Box8.cs | 8 +- .../Substructures/Gen8/SWSH/BoxLayout8.cs | 2 +- .../Substructures/Gen8/SWSH/Coordinates8.cs | 22 +- .../Saves/Substructures/Gen8/SWSH/Daycare8.cs | 6 +- .../Substructures/Gen8/SWSH/FashionUnlock8.cs | 4 +- .../Gen8/SWSH/FieldMoveModelSave8.cs | 11 +- .../Saves/Substructures/Gen8/SWSH/Fused8.cs | 3 +- .../Gen8/SWSH/HallOfFameTime8.cs | 6 +- .../Saves/Substructures/Gen8/SWSH/Misc8.cs | 13 +- .../Substructures/Gen8/SWSH/MyStatus8.cs | 88 +- .../Substructures/Gen8/SWSH/PlayTime8.cs | 14 +- .../Substructures/Gen8/SWSH/RaidSpawnList8.cs | 30 +- .../Saves/Substructures/Gen8/SWSH/Record8.cs | 6 +- .../Substructures/Gen8/SWSH/TitleScreen8.cs | 55 +- .../Substructures/Gen8/SWSH/TrainerCard8.cs | 179 ++-- .../Substructures/Gen9/BlueberryClubRoom9.cs | 4 +- .../Gen9/BlueberryQuestRecord9.cs | 7 +- .../Saves/Substructures/Gen9/BoxLayout9.cs | 4 +- .../Saves/Substructures/Gen9/ConfigCamera9.cs | 7 +- .../Saves/Substructures/Gen9/ConfigSave9.cs | 7 +- .../Substructures/Gen9/FixedSpawnList9.cs | 24 +- .../Saves/Substructures/Gen9/MyItem9.cs | 5 +- .../Saves/Substructures/Gen9/MyStatus9.cs | 14 +- .../Saves/Substructures/Gen9/PlayTime9.cs | 12 +- .../Substructures/Gen9/PlayerAppearance9.cs | 28 +- .../Substructures/Gen9/PlayerFashion9.cs | 22 +- .../Substructures/Gen9/RaidSevenStar9.cs | 35 +- .../Saves/Substructures/IEventFlagArray.cs | 5 + .../Saves/Substructures/Inventory/MyItem.cs | 6 +- .../Inventory/Pouch/InventoryPouch8b.cs | 14 +- .../Substructures/Misc/IBoxDetailName.cs | 44 +- .../Substructures/Misc/IBoxDetailWallpaper.cs | 34 +- .../MysteryGift/IMysteryGiftFlags.cs | 9 + .../MysteryGift/IMysteryGiftStorage.cs | 11 + .../IMysteryGiftStorageProvider.cs | 12 + .../Saves/Substructures/MysteryGiftAlbum.cs | 29 - .../Substructures/PokeDex/Gen9/Zukan9.cs | 17 +- .../PokeDex/Gen9/Zukan9Kitakami.cs | 2 +- .../PokeDex/Gen9/Zukan9Paldea.cs | 2 +- .../Saves/Substructures/PokeDex/Zukan.cs | 27 +- .../Saves/Substructures/PokeDex/Zukan4.cs | 5 +- .../Saves/Substructures/PokeDex/Zukan5.cs | 4 +- .../Saves/Substructures/PokeDex/Zukan6.cs | 16 +- .../Saves/Substructures/PokeDex/Zukan7.cs | 30 +- .../Saves/Substructures/PokeDex/Zukan7b.cs | 2 +- .../Saves/Substructures/PokeDex/Zukan8.cs | 2 +- PKHeX.Core/Saves/Substructures/Records.cs | 6 +- PKHeX.Core/Saves/Util/BoxUtil.cs | 11 +- PKHeX.Core/Util/FlagUtil.cs | 4 + PKHeX.Drawing.Misc/QR/QRDecode.cs | 2 +- PKHeX.Drawing.Misc/Util/WallpaperUtil.cs | 10 +- PKHeX.WinForms/Controls/PKM Editor/SizeCP.cs | 4 +- .../Controls/SAV Editor/SAVEditor.Designer.cs | 2 +- .../Controls/SAV Editor/SAVEditor.cs | 161 +++- PKHeX.WinForms/Controls/Slots/SlotList.cs | 2 +- PKHeX.WinForms/MainWindow/Main.cs | 2 +- PKHeX.WinForms/Subforms/SAV_Encounters.cs | 2 +- .../Subforms/Save Editors/Gen3/SAV_Misc3.cs | 4 +- .../Subforms/Save Editors/Gen3/SAV_Roamer3.cs | 6 +- .../Subforms/Save Editors/Gen5/SAV_Misc5.cs | 58 +- .../Save Editors/Gen6/SAV_BerryFieldXY.cs | 4 +- .../Save Editors/Gen6/SAV_BoxLayout.cs | 58 +- .../Save Editors/Gen6/SAV_HallOfFame.cs | 31 +- .../Save Editors/Gen6/SAV_OPower.Designer.cs | 843 ++++++++++++++++-- .../Subforms/Save Editors/Gen6/SAV_OPower.cs | 98 +- .../Save Editors/Gen6/SAV_PokeBlockORAS.cs | 16 +- .../Subforms/Save Editors/Gen6/SAV_Roamer6.cs | 2 +- .../Subforms/Save Editors/Gen6/SAV_Trainer.cs | 15 +- .../Save Editors/Gen7/SAV_FestivalPlaza.cs | 15 +- .../Save Editors/Gen7/SAV_HallOfFame7.cs | 8 +- .../Save Editors/Gen7/SAV_Trainer7.cs | 44 +- .../Gen7/SAV_ZygardeCell.Designer.cs | 48 +- .../Save Editors/Gen7/SAV_ZygardeCell.cs | 56 +- .../Subforms/Save Editors/SAV_Inventory.cs | 2 +- .../Subforms/Save Editors/SAV_Wondercard.cs | 150 ++-- PKHeX.WinForms/Util/DevUtil.cs | 3 +- PKHeX.WinForms/Util/WinFormsTranslator.cs | 71 +- Tests/PKHeX.Core.Tests/Saves/PokeDex.cs | 21 +- 311 files changed, 5669 insertions(+), 4525 deletions(-) create mode 100644 PKHeX.Core/Saves/Encryption/ColoCrypto.cs create mode 100644 PKHeX.Core/Saves/Substructures/Daycare/IDaycareEggState.cs create mode 100644 PKHeX.Core/Saves/Substructures/Daycare/IDaycareExperience.cs create mode 100644 PKHeX.Core/Saves/Substructures/Daycare/IDaycareMulti.cs create mode 100644 PKHeX.Core/Saves/Substructures/Daycare/IDaycareRandomState.cs create mode 100644 PKHeX.Core/Saves/Substructures/Daycare/IDaycareStorage.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen5/BattleBox5.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen5/EventWork5.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen5/WhiteBlack5.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen6/BerryField6AO.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen6/Daycare6AO.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen6/EventWork6.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen6/GTS6.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6BattleType.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6FieldType.cs delete mode 100644 PKHeX.Core/Saves/Substructures/Gen6/OPower/OPowerFlagSet.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen6/UnionPokemon6.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen7/EventWork7.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen7/GTS7.cs delete mode 100644 PKHeX.Core/Saves/Substructures/Gen7/HallOfFame7.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen7/LGPE/Daycare7b.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen7/UnionPokemon7.cs create mode 100644 PKHeX.Core/Saves/Substructures/MysteryGift/IMysteryGiftFlags.cs create mode 100644 PKHeX.Core/Saves/Substructures/MysteryGift/IMysteryGiftStorage.cs create mode 100644 PKHeX.Core/Saves/Substructures/MysteryGift/IMysteryGiftStorageProvider.cs delete mode 100644 PKHeX.Core/Saves/Substructures/MysteryGiftAlbum.cs diff --git a/PKHeX.Core/Editing/Applicators/BallApplicator.cs b/PKHeX.Core/Editing/Applicators/BallApplicator.cs index 2b4a97e65..d5962c444 100644 --- a/PKHeX.Core/Editing/Applicators/BallApplicator.cs +++ b/PKHeX.Core/Editing/Applicators/BallApplicator.cs @@ -119,7 +119,7 @@ public static class BallApplicator return items.Length; } - private static readonly Ball[] BallList = (Ball[])Enum.GetValues(typeof(Ball)); + private static readonly Ball[] BallList = Enum.GetValues(); private static int MaxBallSpanAlloc => BallList.Length; static BallApplicator() @@ -129,7 +129,7 @@ public static class BallApplicator Span all = stackalloc Ball[BallList.Length - exclude.Length]; all = all[..FillExcept(all, exclude, BallList)]; - var colors = (PersonalColor[])Enum.GetValues(typeof(PersonalColor)); + var colors = Enum.GetValues(); foreach (var color in colors) { int c = (int)color; diff --git a/PKHeX.Core/Editing/Saves/Editors/EventUnlock/EventUnlocker.cs b/PKHeX.Core/Editing/Saves/Editors/EventUnlock/EventUnlocker.cs index 407b6616c..abdbdc920 100644 --- a/PKHeX.Core/Editing/Saves/Editors/EventUnlock/EventUnlocker.cs +++ b/PKHeX.Core/Editing/Saves/Editors/EventUnlock/EventUnlocker.cs @@ -1,7 +1,7 @@ -namespace PKHeX.Core; +namespace PKHeX.Core; -public abstract class EventUnlocker where T : SaveFile +public abstract class EventUnlocker(T sav) + where T : SaveFile { - protected T SAV { get; } - protected EventUnlocker(T sav) => SAV = sav; + protected T SAV { get; } = sav; } diff --git a/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiff.cs b/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiff.cs index 22938a01a..0fca50707 100644 --- a/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiff.cs +++ b/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiff.cs @@ -28,14 +28,24 @@ public sealed class EventBlockDiff : IEventWorkDiff where T : class, IEve return; var s1 = SaveUtil.GetVariantSAV(f1); var s2 = SaveUtil.GetVariantSAV(f2); - if (s1 == null || s2 == null || s1.GetType() != s2.GetType() || s1 is not T t1 || s2 is not T t2) + if (s1 == null || s2 == null || s1.GetType() != s2.GetType() || GetBlock(s1) is not { } t1 || GetBlock(s2) is not { } t2) { Message = DifferentGameGroup; return; } + Diff(t1, t2); } + private static T? GetBlock(SaveFile s1) + { + if (s1 is T t1) + return t1; + if (s1 is IEventFlagProvider37 p1) + return p1.EventWork as T; + return null; + } + private static EventWorkDiffCompatibility SanityCheckSaveInfo(T s1, T s2) { if (s1.GetType() != s2.GetType()) diff --git a/PKHeX.Core/Editing/Saves/Editors/FakeSaveFile.cs b/PKHeX.Core/Editing/Saves/Editors/FakeSaveFile.cs index c8e667bb5..cd1a6ce1a 100644 --- a/PKHeX.Core/Editing/Saves/Editors/FakeSaveFile.cs +++ b/PKHeX.Core/Editing/Saves/Editors/FakeSaveFile.cs @@ -17,8 +17,6 @@ public sealed class FakeSaveFile : SaveFile public override int MaxEV => 0; public override ReadOnlySpan HeldItems => Legal.HeldItems_RS; public override int GetBoxOffset(int box) => -1; - public override string GetBoxName(int box) => $"Box {box:00}"; - public override void SetBoxName(int box, ReadOnlySpan value) { } public override int MaxStringLengthOT => 5; public override int MaxStringLengthNickname => 5; public override ushort MaxMoveID => 5; diff --git a/PKHeX.Core/Editing/Saves/Slots/BoxEdit.cs b/PKHeX.Core/Editing/Saves/Slots/BoxEdit.cs index 67cdf39dc..0ac80da0b 100644 --- a/PKHeX.Core/Editing/Saves/Slots/BoxEdit.cs +++ b/PKHeX.Core/Editing/Saves/Slots/BoxEdit.cs @@ -30,8 +30,17 @@ public sealed class BoxEdit(SaveFile SAV) } public int CurrentBox { get; private set; } - public int BoxWallpaper { get => SAV.GetBoxWallpaper(CurrentBox); set => SAV.SetBoxWallpaper(CurrentBox, value); } - public string BoxName { get => SAV.GetBoxName(CurrentBox); set => SAV.SetBoxName(CurrentBox, value); } + public int BoxWallpaper + { + get => (SAV as IBoxDetailWallpaper)?.GetBoxWallpaper(CurrentBox) ?? 0; + set => (SAV as IBoxDetailWallpaper)?.SetBoxWallpaper(CurrentBox, value); + } + + public string BoxName + { + get => (SAV as IBoxDetailNameRead)?.GetBoxName(CurrentBox) ?? BoxDetailNameExtensions.GetDefaultBoxName(CurrentBox); + set => (SAV as IBoxDetailName)?.SetBoxName(CurrentBox, value); + } public int MoveLeft(bool max = false) { diff --git a/PKHeX.Core/Editing/Saves/Slots/Exporting/BoxExport.cs b/PKHeX.Core/Editing/Saves/Slots/Exporting/BoxExport.cs index eee1f65dd..2a5ca4d3c 100644 --- a/PKHeX.Core/Editing/Saves/Slots/Exporting/BoxExport.cs +++ b/PKHeX.Core/Editing/Saves/Slots/Exporting/BoxExport.cs @@ -115,7 +115,8 @@ public static class BoxExport private static string GetFolderName(SaveFile sav, int box, BoxExportFolderNaming mode) { - var boxName = Util.CleanFileName(sav.GetBoxName(box)); + var boxName = sav is IBoxDetailNameRead r ? r.GetBoxName(box) : BoxDetailNameExtensions.GetDefaultBoxName(box); + boxName = Util.CleanFileName(boxName); return mode switch { BoxExportFolderNaming.BoxName => boxName, diff --git a/PKHeX.Core/Editing/Saves/Slots/Extensions.cs b/PKHeX.Core/Editing/Saves/Slots/Extensions.cs index 716c62e30..f2a243c9b 100644 --- a/PKHeX.Core/Editing/Saves/Slots/Extensions.cs +++ b/PKHeX.Core/Editing/Saves/Slots/Extensions.cs @@ -52,7 +52,7 @@ public static partial class Extensions { return [ - new(sav.Data.AsMemory(sav.GetDaycareSlotOffset(0, 2)), 0) {Type = StorageSlotType.Daycare }, // egg + new(sav.GetDaycareEgg(), 0) {Type = StorageSlotType.Daycare }, // egg ]; } @@ -71,8 +71,8 @@ public static partial class Extensions var list = new List(); if (sav.GTS > 0) list.Add(new SlotInfoMisc(sav.GeneralBuffer[sav.GTS..], 0) { Type = StorageSlotType.GTS }); - if (sav is SAV4HGSS) - list.Add(new SlotInfoMisc(sav.GeneralBuffer[SAV4HGSS.WalkerPair..], 1) {Type = StorageSlotType.Misc}); + if (sav is SAV4HGSS hgss) + list.Add(new SlotInfoMisc(hgss.GeneralBuffer[SAV4HGSS.WalkerPair..], 1) {Type = StorageSlotType.Misc}); return list; } @@ -80,19 +80,19 @@ public static partial class Extensions { var list = new List { - new(sav.Data, 0, sav.GTS) {Type = StorageSlotType.GTS}, - new(sav.Data, 0, sav.PGL) { Type = StorageSlotType.Misc }, + new(sav.GTS.Upload, 0) {Type = StorageSlotType.GTS}, + new(sav.GlobalLink.Upload, 0) { Type = StorageSlotType.Misc }, - new(sav.Data, 0, sav.GetBattleBoxSlot(0)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 1, sav.GetBattleBoxSlot(1)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 2, sav.GetBattleBoxSlot(2)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 3, sav.GetBattleBoxSlot(3)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 4, sav.GetBattleBoxSlot(4)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 5, sav.GetBattleBoxSlot(5)) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[0], 0) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[1], 1) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[2], 2) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[3], 3) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[4], 4) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[5], 5) {Type = StorageSlotType.BattleBox}, }; if (sav is SAV5B2W2 b2w2) - list.Insert(1, new(b2w2.Data, 0, b2w2.Fused) { Type = StorageSlotType.Fused }); + list.Insert(1, new(b2w2.Forest.Fused, 0) { Type = StorageSlotType.Fused }); return list; } @@ -101,16 +101,16 @@ public static partial class Extensions { return [ - new(sav.Data, 0, sav.GTS) {Type = StorageSlotType.GTS}, - new(sav.Data, 0, sav.Fused) {Type = StorageSlotType.Fused}, - new(sav.Data, 0, sav.SUBE.Give) {Type = StorageSlotType.Misc}, // Old Man + new(sav.GTS.Upload, 0) {Type = StorageSlotType.GTS}, + new(sav.Fused[0], 0) {Type = StorageSlotType.Fused}, + new(sav.SUBE.GiveSlot, 0) {Type = StorageSlotType.Misc}, // Old Man - new(sav.Data, 0, sav.GetBattleBoxSlot(0)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 1, sav.GetBattleBoxSlot(1)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 2, sav.GetBattleBoxSlot(2)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 3, sav.GetBattleBoxSlot(3)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 4, sav.GetBattleBoxSlot(4)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 5, sav.GetBattleBoxSlot(5)) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[0], 0) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[1], 1) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[2], 2) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[3], 3) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[4], 4) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[5], 5) {Type = StorageSlotType.BattleBox}, ]; } @@ -118,16 +118,16 @@ public static partial class Extensions { return [ - new(sav.Data, 0, SAV6AO.GTS) {Type = StorageSlotType.GTS}, - new(sav.Data, 0, SAV6AO.Fused) {Type = StorageSlotType.Fused}, - new(sav.Data, 0, sav.SUBE.Give) {Type = StorageSlotType.Misc}, + new(sav.GTS.Upload, 0) { Type = StorageSlotType.GTS }, + new(sav.Fused[0], 0) { Type = StorageSlotType.Fused }, + new(sav.SUBE.GiveSlot, 0) {Type = StorageSlotType.Misc}, - new(sav.Data, 0, sav.GetBattleBoxSlot(0)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 1, sav.GetBattleBoxSlot(1)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 2, sav.GetBattleBoxSlot(2)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 3, sav.GetBattleBoxSlot(3)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 4, sav.GetBattleBoxSlot(4)) {Type = StorageSlotType.BattleBox}, - new(sav.Data, 5, sav.GetBattleBoxSlot(5)) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[0], 0) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[1], 1) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[2], 2) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[3], 3) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[4], 4) {Type = StorageSlotType.BattleBox}, + new(sav.BattleBox[5], 5) {Type = StorageSlotType.BattleBox}, ]; } @@ -135,22 +135,21 @@ public static partial class Extensions { var list = new List { - new(sav.Data, 0, sav.AllBlocks[07].Offset) {Type = StorageSlotType.GTS}, - new(sav.Data, 0, sav.GetFusedSlotOffset(0)) {Type = StorageSlotType.Fused}, + new(sav.GTS.Upload, 0) {Type = StorageSlotType.GTS}, + new(sav.Fused[0], 0, PartyFormat: true) {Type = StorageSlotType.Fused}, }; if (sav is SAV7USUM uu) { list.AddRange( [ - new SlotInfoMisc(uu.Data, 1, uu.GetFusedSlotOffset(1)) {Type = StorageSlotType.Fused}, - new SlotInfoMisc(uu.Data, 2, uu.GetFusedSlotOffset(2)) {Type = StorageSlotType.Fused}, + new SlotInfoMisc(uu.Fused[1], 1, PartyFormat: true) {Type = StorageSlotType.Fused}, + new SlotInfoMisc(uu.Fused[2], 2, PartyFormat: true) {Type = StorageSlotType.Fused}, ]); - var ba = uu.BattleAgency; list.AddRange( [ - new SlotInfoMisc(uu.Data, 0, ba.GetSlotOffset(0)) {Type = StorageSlotType.Misc}, - new SlotInfoMisc(uu.Data, 1, ba.GetSlotOffset(1)) {Type = StorageSlotType.Misc}, - new SlotInfoMisc(uu.Data, 2, ba.GetSlotOffset(2)) {Type = StorageSlotType.Misc}, + new SlotInfoMisc(uu.BattleAgency[0], 0) {Type = StorageSlotType.Misc}, + new SlotInfoMisc(uu.BattleAgency[1], 1) {Type = StorageSlotType.Misc}, + new SlotInfoMisc(uu.BattleAgency[2], 2) {Type = StorageSlotType.Misc}, ]); } @@ -158,15 +157,15 @@ public static partial class Extensions return list; for (int i = 0; i < ResortSave7.ResortCount; i++) - list.Add(new SlotInfoMisc(sav.Data, i, sav.ResortSave.GetResortSlotOffset(i)) { Type = StorageSlotType.Resort }); + list.Add(new SlotInfoMisc(sav.ResortSave[i], i) { Type = StorageSlotType.Resort }); return list; } - private static List GetExtraSlots7b(SAV7b sav) + private static List GetExtraSlots7b(ISaveBlock7b sav) { return [ - new(sav.Data, 0, sav.Blocks.GetBlockOffset(BelugaBlockIndex.Daycare) + 8, true) {Type = StorageSlotType.Daycare}, + new(sav.Daycare.Stored, 0) {Type = StorageSlotType.Daycare}, ]; } @@ -176,20 +175,20 @@ public static partial class Extensions var dc = sav.Daycare; var list = new List { - new(fused.Data, 0, Fused8.GetFusedSlotOffset(0), true) {Type = StorageSlotType.Fused}, - new(fused.Data, 1, Fused8.GetFusedSlotOffset(1), true) {Type = StorageSlotType.Fused}, - new(fused.Data, 2, Fused8.GetFusedSlotOffset(2), true) {Type = StorageSlotType.Fused}, + new(fused[0], 0, true) {Type = StorageSlotType.Fused}, + new(fused[1], 1, true) {Type = StorageSlotType.Fused}, + new(fused[2], 2, true) {Type = StorageSlotType.Fused}, - new(dc.Data, 0, Daycare8.GetDaycareSlotOffset(0, 0)) {Type = StorageSlotType.Daycare}, - new(dc.Data, 0, Daycare8.GetDaycareSlotOffset(0, 1)) {Type = StorageSlotType.Daycare}, - new(dc.Data, 0, Daycare8.GetDaycareSlotOffset(1, 0)) {Type = StorageSlotType.Daycare}, - new(dc.Data, 0, Daycare8.GetDaycareSlotOffset(1, 1)) {Type = StorageSlotType.Daycare}, + new(dc[0], 0) {Type = StorageSlotType.Daycare}, + new(dc[1], 1) {Type = StorageSlotType.Daycare}, + new(dc[2], 2) {Type = StorageSlotType.Daycare}, + new(dc[3], 3) {Type = StorageSlotType.Daycare}, }; if (sav is SAV8SWSH {SaveRevision: >= 2} s8) { var block = s8.Blocks.GetBlockSafe(SaveBlockAccessor8SWSH.KFusedCalyrex); - var c = new SlotInfoMisc(block.Data, 3, 0, true) {Type = StorageSlotType.Fused}; + var c = new SlotInfoMisc(block.Data, 3, true) {Type = StorageSlotType.Fused}; list.Insert(3, c); } @@ -200,15 +199,15 @@ public static partial class Extensions { return [ - new(sav.Data, 0, sav.UgSaveData.GetSlotOffset(0), true) { Type = StorageSlotType.Misc }, - new(sav.Data, 1, sav.UgSaveData.GetSlotOffset(1), true) { Type = StorageSlotType.Misc }, - new(sav.Data, 2, sav.UgSaveData.GetSlotOffset(2), true) { Type = StorageSlotType.Misc }, - new(sav.Data, 3, sav.UgSaveData.GetSlotOffset(3), true) { Type = StorageSlotType.Misc }, - new(sav.Data, 4, sav.UgSaveData.GetSlotOffset(4), true) { Type = StorageSlotType.Misc }, - new(sav.Data, 5, sav.UgSaveData.GetSlotOffset(5), true) { Type = StorageSlotType.Misc }, - new(sav.Data, 6, sav.UgSaveData.GetSlotOffset(6), true) { Type = StorageSlotType.Misc }, - new(sav.Data, 7, sav.UgSaveData.GetSlotOffset(7), true) { Type = StorageSlotType.Misc }, - new(sav.Data, 8, sav.UgSaveData.GetSlotOffset(8), true) { Type = StorageSlotType.Misc }, + new(sav.UgSaveData[0], 0, true) { Type = StorageSlotType.Misc }, + new(sav.UgSaveData[1], 1, true) { Type = StorageSlotType.Misc }, + new(sav.UgSaveData[2], 2, true) { Type = StorageSlotType.Misc }, + new(sav.UgSaveData[3], 3, true) { Type = StorageSlotType.Misc }, + new(sav.UgSaveData[4], 4, true) { Type = StorageSlotType.Misc }, + new(sav.UgSaveData[5], 5, true) { Type = StorageSlotType.Misc }, + new(sav.UgSaveData[6], 6, true) { Type = StorageSlotType.Misc }, + new(sav.UgSaveData[7], 7, true) { Type = StorageSlotType.Misc }, + new(sav.UgSaveData[8], 8, true) { Type = StorageSlotType.Misc }, ]; } @@ -223,18 +222,18 @@ public static partial class Extensions var list = new List { // Ride Legend - new(sav.BoxInfo.Data.AsMemory(afterBox), 0, true, Mutable: true) { Type = StorageSlotType.Party }, + new(sav.BoxInfo.Raw.Slice(afterBox, PokeCrypto.SIZE_9PARTY), 0, true, Mutable: true) { Type = StorageSlotType.Party }, }; var block = sav.Blocks.GetBlock(SaveBlockAccessor9SV.KFusedCalyrex); - list.Add(new(block.Data, 0, 0, true) { Type = StorageSlotType.Fused }); + list.Add(new(block.Data, 0, true) { Type = StorageSlotType.Fused }); if (sav.Blocks.TryGetBlock(SaveBlockAccessor9SV.KFusedKyurem, out var kyurem)) - list.Add(new(kyurem.Data, 1, 0, true) { Type = StorageSlotType.Fused }); + list.Add(new(kyurem.Data, 1, true) { Type = StorageSlotType.Fused }); if (sav.Blocks.TryGetBlock(SaveBlockAccessor9SV.KFusedNecrozmaS, out var solgaleo)) - list.Add(new(solgaleo.Data, 2, 0, true) { Type = StorageSlotType.Fused }); + list.Add(new(solgaleo.Data, 2, true) { Type = StorageSlotType.Fused }); if (sav.Blocks.TryGetBlock(SaveBlockAccessor9SV.KFusedNecrozmaM, out var lunala)) - list.Add(new(lunala.Data, 3, 0, true) { Type = StorageSlotType.Fused }); + list.Add(new(lunala.Data, 3, true) { Type = StorageSlotType.Fused }); return list; } } diff --git a/PKHeX.Core/Editing/Saves/Slots/Info/SlotCache.cs b/PKHeX.Core/Editing/Saves/Slots/Info/SlotCache.cs index 777c35ad4..c74929154 100644 --- a/PKHeX.Core/Editing/Saves/Slots/Info/SlotCache.cs +++ b/PKHeX.Core/Editing/Saves/Slots/Info/SlotCache.cs @@ -38,9 +38,11 @@ public sealed class SlotCache : IComparable SAV = sav; } + private string GetBoxName(int box) => SAV is IBoxDetailNameRead r ? r.GetBoxName(box) : BoxDetailNameExtensions.GetDefaultBoxName(box); + public string Identify() => GetFileName() + Source switch { - SlotInfoBox box => $"[{box.Box + 1:00}] ({SAV.GetBoxName(box.Box)})-{box.Slot + 1:00}: {Entity.FileName}", + SlotInfoBox box => $"[{box.Box + 1:00}] ({GetBoxName(box.Box)})-{box.Slot + 1:00}: {Entity.FileName}", SlotInfoFile file => $"File: {file.Path}", SlotInfoMisc misc => $"{misc.Type}-{misc.Slot}: {Entity.FileName}", SlotInfoParty party => $"Party: {party.Slot}: {Entity.FileName}", diff --git a/PKHeX.Core/Editing/Saves/Slots/Info/SlotInfoMisc.cs b/PKHeX.Core/Editing/Saves/Slots/Info/SlotInfoMisc.cs index 7ad0380e5..dd0fe0160 100644 --- a/PKHeX.Core/Editing/Saves/Slots/Info/SlotInfoMisc.cs +++ b/PKHeX.Core/Editing/Saves/Slots/Info/SlotInfoMisc.cs @@ -7,21 +7,11 @@ namespace PKHeX.Core; /// public sealed record SlotInfoMisc(Memory Data, int Slot, bool PartyFormat = false, bool Mutable = false) : ISlotInfo { - public SlotInfoMisc(SaveFile sav, int slot, int offset, bool party = false) : this(GetBuffer(sav)[offset..], slot, party) { } - public SlotInfoMisc(byte[] data, int slot, int offset, bool party = false) : this(data.AsMemory(offset), slot, party) { } - public SlotOrigin Origin => PartyFormat ? SlotOrigin.Party : SlotOrigin.Box; public bool CanWriteTo(SaveFile sav) => Mutable; public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pk) => Mutable ? WriteBlockedMessage.None : WriteBlockedMessage.InvalidDestination; public StorageSlotType Type { get; init; } - private static Memory GetBuffer(SaveFile sav) => sav switch - { - SAV4 s => s.GeneralBuffer, - SAV3 s3 => s3.Large, - _ => sav.Data, - }; - public bool WriteTo(SaveFile sav, PKM pk, PKMImportSetting setting = PKMImportSetting.UseDefault) { var span = Data.Span; diff --git a/PKHeX.Core/Game/GameUtil.cs b/PKHeX.Core/Game/GameUtil.cs index 5de6b8670..bcd3b8c48 100644 --- a/PKHeX.Core/Game/GameUtil.cs +++ b/PKHeX.Core/Game/GameUtil.cs @@ -18,7 +18,7 @@ public static class GameUtil private static GameVersion[] GetValidGameVersions() { - var all = (GameVersion[])Enum.GetValues(typeof(GameVersion)); + var all = Enum.GetValues(); var valid = Array.FindAll(all, IsValidSavedVersion); Array.Reverse(valid); return valid; diff --git a/PKHeX.Core/Legality/Encounters/Generator/Moveset/EncounterMovesetGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/Moveset/EncounterMovesetGenerator.cs index 93459e7ed..7becc0b28 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/Moveset/EncounterMovesetGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/Moveset/EncounterMovesetGenerator.cs @@ -21,7 +21,7 @@ public static class EncounterMovesetGenerator /// public static void ResetFilters() => PriorityList = GetAllGroups(); - private static EncounterTypeGroup[] GetAllGroups() => (EncounterTypeGroup[])Enum.GetValues(typeof(EncounterTypeGroup)); + private static EncounterTypeGroup[] GetAllGroups() => Enum.GetValues(); /// /// Gets possible objects that allow all moves requested to be learned. diff --git a/PKHeX.Core/PKM/Util/Conversion/FormConverter.cs b/PKHeX.Core/PKM/Util/Conversion/FormConverter.cs index af7026cec..5e9fff3a0 100644 --- a/PKHeX.Core/PKM/Util/Conversion/FormConverter.cs +++ b/PKHeX.Core/PKM/Util/Conversion/FormConverter.cs @@ -966,7 +966,7 @@ public static class FormConverter public static string[] GetFormArgumentStrings(ushort species) => species switch { - (int)Alcremie => Enum.GetNames(typeof(AlcremieDecoration)), + (int)Alcremie => Enum.GetNames(), _ => EMPTY, }; } diff --git a/PKHeX.Core/Saves/Access/ISCBlockArray.cs b/PKHeX.Core/Saves/Access/ISCBlockArray.cs index 65d4f7b7f..ff5ab34f6 100644 --- a/PKHeX.Core/Saves/Access/ISCBlockArray.cs +++ b/PKHeX.Core/Saves/Access/ISCBlockArray.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace PKHeX.Core; diff --git a/PKHeX.Core/Saves/Access/ISaveBlock5BW.cs b/PKHeX.Core/Saves/Access/ISaveBlock5BW.cs index f728ab9c4..422140beb 100644 --- a/PKHeX.Core/Saves/Access/ISaveBlock5BW.cs +++ b/PKHeX.Core/Saves/Access/ISaveBlock5BW.cs @@ -17,4 +17,9 @@ public interface ISaveBlock5BW Entralink5 Entralink { get; } Musical5 Musical { get; } Encount5 Encount { get; } + EventWork5 EventWork { get; } + BattleBox5 BattleBox { get; } + GlobalLink5 GlobalLink { get; } + GTS5 GTS { get; } + AdventureInfo5 AdventureInfo { get; } } diff --git a/PKHeX.Core/Saves/Access/ISaveBlock6AO.cs b/PKHeX.Core/Saves/Access/ISaveBlock6AO.cs index ef7264337..27603630b 100644 --- a/PKHeX.Core/Saves/Access/ISaveBlock6AO.cs +++ b/PKHeX.Core/Saves/Access/ISaveBlock6AO.cs @@ -1,4 +1,4 @@ -namespace PKHeX.Core; +namespace PKHeX.Core; /// /// Interface for Accessing named blocks within a Generation 6 save file. @@ -8,4 +8,5 @@ public interface ISaveBlock6AO : ISaveBlock6Main Misc6AO Misc { get; } Zukan6AO Zukan { get; } SecretBase6Block SecretBase { get; } + BerryField6AO BerryField { get; } } diff --git a/PKHeX.Core/Saves/Access/ISaveBlock6Main.cs b/PKHeX.Core/Saves/Access/ISaveBlock6Main.cs index b425c06fd..2f2507547 100644 --- a/PKHeX.Core/Saves/Access/ISaveBlock6Main.cs +++ b/PKHeX.Core/Saves/Access/ISaveBlock6Main.cs @@ -1,4 +1,4 @@ -namespace PKHeX.Core; +namespace PKHeX.Core; /// /// Interface for Accessing named blocks within a Generation 6 save file. @@ -8,6 +8,8 @@ public interface ISaveBlock6Main : ISaveBlock6Core { Puff6 Puff { get; } OPower6 OPower { get; } + GTS6 GTS { get; } + UnionPokemon6 Fused { get; } LinkBlock6 Link { get; } BoxLayout6 BoxLayout { get; } BattleBox6 BattleBox { get; } diff --git a/PKHeX.Core/Saves/Access/ISaveBlock7Main.cs b/PKHeX.Core/Saves/Access/ISaveBlock7Main.cs index b8f01d076..ec09be417 100644 --- a/PKHeX.Core/Saves/Access/ISaveBlock7Main.cs +++ b/PKHeX.Core/Saves/Access/ISaveBlock7Main.cs @@ -1,4 +1,4 @@ -namespace PKHeX.Core; +namespace PKHeX.Core; /// /// Interface for Accessing named blocks within a Generation 7 save file. @@ -25,5 +25,7 @@ public interface ISaveBlock7Main ResortSave7 ResortSave { get; } FieldMenu7 FieldMenu { get; } FashionBlock7 Fashion { get; } - HallOfFame7 Fame { get; } + EventWork7 EventWork { get; } + UnionPokemon7 Fused { get; } + GTS7 GTS { get; } } diff --git a/PKHeX.Core/Saves/Access/ISaveBlock7b.cs b/PKHeX.Core/Saves/Access/ISaveBlock7b.cs index 1142e7afd..c34b0dc76 100644 --- a/PKHeX.Core/Saves/Access/ISaveBlock7b.cs +++ b/PKHeX.Core/Saves/Access/ISaveBlock7b.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// Blocks common for public interface ISaveBlock7b { - MyItem Items { get; } + MyItem7b Items { get; } Misc7b Misc { get; } Zukan7b Zukan { get; } MyStatus7b Status { get; } @@ -15,6 +15,7 @@ public interface ISaveBlock7b EventWork7b EventWork { get; } PokeListHeader Storage { get; } WB7Records GiftRecords { get; } + Daycare7b Daycare { get; } CaptureRecords Captured { get; } GoParkStorage Park { get; } PlayerGeoLocation7b PlayerGeoLocation { get; } diff --git a/PKHeX.Core/Saves/Access/SCBlockAccessor.cs b/PKHeX.Core/Saves/Access/SCBlockAccessor.cs index 10ea8a473..66f706baa 100644 --- a/PKHeX.Core/Saves/Access/SCBlockAccessor.cs +++ b/PKHeX.Core/Saves/Access/SCBlockAccessor.cs @@ -9,6 +9,14 @@ namespace PKHeX.Core; /// public abstract class SCBlockAccessor : ISaveBlockAccessor { + public static SCBlock GetBlock(IReadOnlyList blocks, uint key) => Find(blocks, key); + public static SCBlock GetBlockSafe(IReadOnlyList blocks, uint key) => FindOrDefault(blocks, key); + public static bool TryGetBlock(IReadOnlyList blocks, uint key, [NotNullWhen(true)] out SCBlock? block) => TryFind(blocks, key, out block); + + protected static SCBlock Block(T sav, uint key) where T : ISCBlockArray => GetBlock(sav.AllBlocks, key); + protected static SCBlock BlockSafe(T sav, uint key) where T : ISCBlockArray + => GetBlockSafe(sav.AllBlocks, key); + public abstract IReadOnlyList BlockInfo { get; } /// Checks if there is any with the requested . diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor5B2W2.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor5B2W2.cs index aaa5f22d2..4410f47f3 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor5B2W2.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor5B2W2.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; @@ -87,20 +88,38 @@ public sealed class SaveBlockAccessor5B2W2(SAV5B2W2 sav) ]; public IReadOnlyList BlockInfo => BlocksB2W2; - public BoxLayout5 BoxLayout { get; } = new(sav, 0x00000); - public PlayerData5 PlayerData { get; } = new(sav, 0x19400); - public MyItem Items { get; } = new MyItem5B2W2(sav, 0x18400); - public UnityTower5 UnityTower { get; } = new(sav, 0x19600); - public MysteryBlock5 Mystery { get; } = new(sav, 0x1C800); - public Chatter5 Chatter { get; } = new(sav, 0x1D500); - public Musical5 Musical { get; } = new(sav, 0x1F700); - public Daycare5 Daycare { get; } = new(sav, 0x20D00); - public Misc5 Misc { get; } = new Misc5B2W2(sav, 0x21100); - public Zukan5 Zukan { get; } = new(sav, 0x21400, 0x328); // form flags size is + 8 from B/W with new forms (Therians) - public Entralink5 Entralink { get; } = new Entralink5B2W2(sav, 0x21200); - public Encount5 Encount { get; } = new Encount5B2W2(sav, 0x21900); - public BattleSubway5 BattleSubway { get; } = new(sav, 0x21B00); - public PWTBlock5 PWT { get; } = new(sav, 0x23700); - public MedalList5 Medals { get; } = new(sav, 0x25300); - public FestaBlock5 Festa { get; } = new(sav, 0x25900); + public BoxLayout5 BoxLayout { get; } = new(sav, Block(sav, 0)); + public MyItem5B2W2 Items { get; } = new(sav, Block(sav, 25)); + public PlayerData5 PlayerData { get; } = new(sav, Block(sav, 27)); + public UnityTower5 UnityTower { get; } = new(sav, Block(sav, 29)); + public MysteryBlock5 Mystery { get; } = new(sav, Block(sav, 34)); + public GlobalLink5 GlobalLink { get; } = new(sav, Block(sav, 35)); + public Chatter5 Chatter { get; } = new(sav, Block(sav, 36)); + public AdventureInfo5 AdventureInfo { get; } = new(sav, Block(sav, 37)); + public Musical5 Musical { get; } = new(sav, Block(sav, 42)); + public WhiteBlack5B2W2 Forest { get; } = new(sav, Block(sav, 43)); + public EventWork5B2W2 EventWork { get; } = new(sav, Block(sav, 45)); + public GTS5 GTS { get; } = new(sav, Block(sav, 46)); + public BattleBox5 BattleBox { get; } = new(sav, Block(sav, 49)); + public Daycare5 Daycare { get; } = new(sav, Block(sav, 50)); + public Misc5B2W2 Misc { get; } = new(sav, Block(sav, 52)); + public Entralink5B2W2 Entralink { get; } = new(sav, Block(sav, 53)); + public Zukan5 Zukan { get; } = new(sav, Block(sav, 54), 0x328); // form flags size is + 8 from B/W with new forms (Therians) + public Encount5B2W2 Encount { get; } = new(sav, Block(sav, 55)); + public BattleSubway5 BattleSubway { get; } = new(sav, Block(sav, 57)); + public EntreeForest EntreeForest { get; } = new(sav, Block(sav, 60)); + public PWTBlock5 PWT { get; } = new(sav, Block(sav, 63)); + public MedalList5 Medals { get; } = new(sav, Block(sav, 68)); + public FestaBlock5 Festa { get; } = new(sav, Block(sav, 70)); + EventWork5 ISaveBlock5BW.EventWork => EventWork; + Encount5 ISaveBlock5BW.Encount => Encount; + MyItem ISaveBlock5BW.Items => Items; + Entralink5 ISaveBlock5BW.Entralink => Entralink; + Misc5 ISaveBlock5BW.Misc => Misc; + + public static Memory Block(SAV5B2W2 sav, int index) + { + var block = BlocksB2W2[index]; + return sav.Data.AsMemory(block.Offset, block.Length); + } } diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor5BW.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor5BW.cs index 50c7432c6..49a30fc18 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor5BW.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor5BW.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; @@ -82,17 +83,35 @@ public sealed class SaveBlockAccessor5BW(SAV5BW sav) : ISaveBlockAccessor BlockInfo => BlocksBW; - public BoxLayout5 BoxLayout { get; } = new(sav, 0x00000); - public MyItem Items { get; } = new MyItem5BW(sav, 0x18400); - public PlayerData5 PlayerData { get; } = new(sav, 0x19400); - public UnityTower5 UnityTower { get; } = new(sav, 0x19600); - public MysteryBlock5 Mystery { get; } = new(sav, 0x1C800); - public Chatter5 Chatter { get; } = new(sav, 0x1D500); - public Musical5 Musical { get; } = new(sav, 0x1F700); - public Daycare5 Daycare { get; } = new(sav, 0x20E00); - public Misc5 Misc { get; } = new Misc5BW(sav, 0x21200); - public Entralink5 Entralink { get; } = new Entralink5BW(sav, 0x21300); - public Zukan5 Zukan { get; } = new(sav, 0x21600, 0x320); - public Encount5 Encount { get; } = new Encount5BW(sav, 0x21B00); - public BattleSubway5 BattleSubway { get; } = new(sav, 0x21D00); + public BoxLayout5 BoxLayout { get; } = new(sav, Block(sav, 0)); + public MyItem5BW Items { get; } = new(sav, Block(sav, 25)); + public PlayerData5 PlayerData { get; } = new(sav, Block(sav, 27)); + public UnityTower5 UnityTower { get; } = new(sav, Block(sav, 29)); + public MysteryBlock5 Mystery { get; } = new(sav, Block(sav, 34)); + public GlobalLink5 GlobalLink { get; } = new(sav, Block(sav, 35)); + public Chatter5 Chatter { get; } = new(sav, Block(sav, 36)); + public AdventureInfo5 AdventureInfo { get; } = new(sav, Block(sav, 37)); + public Musical5 Musical { get; } = new(sav, Block(sav, 42)); + public WhiteBlack5BW Forest { get; } = new(sav, Block(sav, 43)); + public EventWork5BW EventWork { get; } = new(sav, Block(sav, 45)); + public GTS5 GTS { get; } = new(sav, Block(sav, 46)); + public BattleBox5 BattleBox { get; } = new(sav, Block(sav, 49)); + public Daycare5 Daycare { get; } = new(sav, Block(sav, 50)); + public Misc5BW Misc { get; } = new(sav, Block(sav, 52)); + public Entralink5BW Entralink { get; } = new(sav, Block(sav, 53)); + public Zukan5 Zukan { get; } = new(sav, Block(sav, 55), 0x320); + public Encount5BW Encount { get; } = new(sav, Block(sav, 56)); + public BattleSubway5 BattleSubway { get; } = new(sav, Block(sav, 58)); + public EntreeForest EntreeForest { get; } = new(sav, Block(sav, 61)); + EventWork5 ISaveBlock5BW.EventWork => EventWork; + Encount5 ISaveBlock5BW.Encount => Encount; + MyItem ISaveBlock5BW.Items => Items; + Entralink5 ISaveBlock5BW.Entralink => Entralink; + Misc5 ISaveBlock5BW.Misc => Misc; + + public static Memory Block(SAV5BW sav, int index) + { + var block = BlocksBW[index]; + return sav.Data.AsMemory(block.Offset, block.Length); + } } diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor6AO.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor6AO.cs index 12c6191b9..469c79181 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor6AO.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor6AO.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; @@ -5,7 +6,7 @@ namespace PKHeX.Core; /// /// Information for Accessing individual blocks within a . /// -public sealed class SaveBlockAccessor6AO : ISaveBlockAccessor, ISaveBlock6Main +public sealed class SaveBlockAccessor6AO(SAV6AO sav) : ISaveBlockAccessor, ISaveBlock6Main { public const int BlockMetadataOffset = SaveUtil.SIZE_G6ORAS - 0x200; private const int boAO = BlockMetadataOffset; @@ -73,53 +74,42 @@ public sealed class SaveBlockAccessor6AO : ISaveBlockAccessor, ISave ]; public IReadOnlyList BlockInfo => BlocksAO; - public MyItem Items { get; } - public ItemInfo6 ItemInfo { get; } - public GameTime6 GameTime { get; } - public Situation6 Situation { get; } - public PlayTime6 Played { get; } - public MyStatus6 Status { get; } - public RecordBlock6 Records { get; } + public Puff6 Puff { get; } = new(sav, Block(sav, 0)); + public MyItem6AO Items { get; } = new(sav, Block(sav, 1)); + public ItemInfo6 ItemInfo { get; } = new(sav, Block(sav, 2)); + public GameTime6 GameTime { get; } = new(sav, Block(sav, 3)); + public Situation6 Situation { get; } = new(sav, Block(sav, 4)); + public PlayTime6 Played { get; } = new(sav, Block(sav, 6)); + public Misc6AO Misc { get; } = new(sav, Block(sav, 11)); + public BoxLayout6 BoxLayout { get; } = new(sav, Block(sav, 12)); + public BattleBox6 BattleBox { get; } = new(sav, Block(sav, 13)); + public MyStatus6 Status { get; } = new(sav, Block(sav, 17)); + public EventWork6 EventWork { get; } = new(sav, Block(sav, 19)); + public Zukan6AO Zukan { get; } = new(sav, Block(sav, 20), 0x400); + public UnionPokemon6 Fused { get; } = new(sav, Block(sav, 22)); + public ConfigSave6 Config { get; } = new(sav, Block(sav, 23)); + public OPower6 OPower { get; } = new(sav, Block(sav, 25)); + public GTS6 GTS { get; } = new(sav, Block(sav, 28)); + public Encount6 Encount { get; } = new(sav, Block(sav, 31)); + public MaisonBlock Maison { get; } = new(sav, Block(sav, 37)); + public Daycare6AO Daycare { get; } = new(sav, Block(sav, 38)); + public BerryField6AO BerryField { get; } = new(sav, Block(sav, 40)); + public MysteryBlock6 MysteryGift { get; } = new(sav, Block(sav, 41)); + public SubEventLog6AO SUBE { get; } = new(sav, Block(sav, 42)); + public RecordBlock6AO Records { get; } = new(sav, Block(sav, 44)); + public SuperTrainBlock SuperTrain { get; } = new(sav, Block(sav, 46)); + public LinkBlock6 Link { get; } = new(sav, Block(sav, 48)); + public SecretBase6Block SecretBase { get; } = new(sav, Block(sav, 54)); + public SangoInfoBlock Sango { get; } = new(sav, Block(sav, 55)); - public Zukan6AO Zukan { get; } - public Puff6 Puff { get; } - public BoxLayout6 BoxLayout { get; } - public BattleBox6 BattleBox { get; } - public OPower6 OPower { get; } - public MysteryBlock6 MysteryGift { get; } - public SangoInfoBlock Sango { get; } - public LinkBlock6 Link { get; } - public Misc6AO Misc { get; } - public SuperTrainBlock SuperTrain { get; } - public MaisonBlock Maison { get; } - public SubEventLog6 SUBE { get; } - public ConfigSave6 Config { get; } - public Encount6 Encount { get; } - public SecretBase6Block SecretBase { get; } + MyItem ISaveBlock6Core.Items => Items; + SubEventLog6 ISaveBlock6Main.SUBE => SUBE; + RecordBlock6 ISaveBlock6Core.Records=> Records; - public SaveBlockAccessor6AO(SAV6AO sav) + private static Memory Block(SAV6AO sav, int index) { - Puff = new Puff6(sav, 0x0000); - Items = new MyItem6AO(sav, 0x00400); - ItemInfo = new ItemInfo6(sav, 0x1000); - GameTime = new GameTime6(sav, 0x01200); - Situation = new Situation6(sav, 0x01400); - Played = new PlayTime6(sav, 0x01800); - Misc = new Misc6AO(sav, 0x04200); - BoxLayout = new BoxLayout6(sav, 0x04400); - BattleBox = new BattleBox6(sav, 0x04A00); - Status = new MyStatus6(sav, 0x14000); - Zukan = new Zukan6AO(sav, 0x15000, 0x400); - Config = new ConfigSave6(sav, 0x16C00); - OPower = new OPower6(sav, 0x17400); - Encount = new Encount6(sav, 0x18800); - Maison = new MaisonBlock(sav, 0x1BA00); - MysteryGift = new MysteryBlock6(sav, 0x1CC00); - SUBE = new SubEventLog6AO(sav, 0x1E800); - Records = new RecordBlock6AO(sav, 0x1F400); - SuperTrain = new SuperTrainBlock(sav, 0x20200); - Link = new LinkBlock6(sav, 0x20E00); - SecretBase = new SecretBase6Block(sav, 0x23A00); - Sango = new SangoInfoBlock(sav, 0x2B600); + var data = sav.Data; + var block = BlocksAO[index]; + return data.AsMemory(block.Offset, block.Length); } } diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor6AODemo.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor6AODemo.cs index b17fa2b01..c05b8fc6d 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor6AODemo.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor6AODemo.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; @@ -31,12 +32,23 @@ public sealed class SaveBlockAccessor6AODemo(SAV6AODemo sav) : ISaveBlockAccesso ]; public IReadOnlyList BlockInfo => BlocksAODemo; - public MyItem Items { get; } = new MyItem6AO(sav, 0x00000); - public ItemInfo6 ItemInfo { get; } = new(sav, 0x00C00); - public GameTime6 GameTime { get; } = new(sav, 0x00E00); - public Situation6 Situation { get; } = new(sav, 0x01000); - public PlayTime6 Played { get; } = new(sav, 0x01400); - public MyStatus6 Status { get; } = new(sav, 0x03C00); - public RecordBlock6 Records { get; } = new RecordBlock6AO(sav, 0x05400); - public Misc6AO Misc { get; } = new(sav, 0x03A00); + public MyItem6AO Items { get; } = new(sav, Block(sav, 0)); + public ItemInfo6 ItemInfo { get; } = new(sav, Block(sav, 1)); + public GameTime6 GameTime { get; } = new(sav, Block(sav, 2)); + public Situation6 Situation { get; } = new(sav, Block(sav, 3)); + public PlayTime6 Played { get; } = new(sav, Block(sav, 5)); + public Misc6AO Misc { get; } = new(sav, Block(sav, 8)); + public MyStatus6 Status { get; } = new(sav, Block(sav, 9)); + public EventWork6 EventWork { get; } = new(sav, Block(sav, 11)); + public RecordBlock6AO Records { get; } = new(sav, Block(sav, 15)); + + MyItem ISaveBlock6Core.Items => Items; + RecordBlock6 ISaveBlock6Core.Records => Records; + + private static Memory Block(SAV6AODemo sav, int index) + { + var data = sav.Data; + var block = BlocksAODemo[index]; + return data.AsMemory(block.Offset, block.Length); + } } diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor6XY.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor6XY.cs index 3e7e73ef6..0dd1eb76b 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor6XY.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor6XY.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; @@ -70,26 +71,41 @@ public sealed class SaveBlockAccessor6XY(SAV6XY sav) : ISaveBlockAccessor BlockInfo => BlocksXY; + public Puff6 Puff { get; } = new(sav, Block(sav, 0)); + public MyItem6XY Items { get; } = new(sav, Block(sav, 1)); + public ItemInfo6 ItemInfo { get; } = new(sav, Block(sav, 2)); + public GameTime6 GameTime { get; } = new(sav, Block(sav, 3)); + public Situation6 Situation { get; } = new(sav, Block(sav, 4)); + public PlayTime6 Played { get; } = new(sav, Block(sav, 6)); + public Fashion6XY Fashion { get; } = new(sav, Block(sav, 7)); + public Misc6XY Misc { get; } = new(sav, Block(sav, 11)); + public BoxLayout6 BoxLayout { get; } = new(sav, Block(sav, 12)); + public BattleBox6 BattleBox { get; } = new(sav, Block(sav, 13)); + public MyStatus6XY Status { get; } = new(sav, Block(sav, 17)); + public EventWork6 EventWork { get; } = new(sav, Block(sav, 19)); + public Zukan6XY Zukan { get; } = new(sav, Block(sav, 20), 0x3C8); + public UnionPokemon6 Fused { get; } = new(sav, Block(sav, 22)); + public ConfigSave6 Config { get; } = new(sav, Block(sav, 23)); + public OPower6 OPower { get; } = new(sav, Block(sav, 25)); + public GTS6 GTS { get; } = new(sav, Block(sav, 28)); + public Encount6 Encount { get; } = new(sav, Block(sav, 31)); + public MaisonBlock Maison { get; } = new(sav, Block(sav, 37)); + public Daycare6XY Daycare { get; } = new(sav, Block(sav, 38)); + public MysteryBlock6 MysteryGift { get; } = new(sav, Block(sav, 41)); + public SubEventLog6XY SUBE { get; } = new(sav, Block(sav, 42)); + public RecordBlock6XY Records { get; } = new(sav, Block(sav, 44)); + public SuperTrainBlock SuperTrain { get; } = new(sav, Block(sav, 46)); + public LinkBlock6 Link { get; } = new(sav, Block(sav, 48)); - public Puff6 Puff { get; } = new(sav, 0x00000); - public MyItem Items { get; } = new MyItem6XY(sav, 0x00400); - public ItemInfo6 ItemInfo { get; } = new(sav, 0x01000); - public GameTime6 GameTime { get; } = new(sav, 0x01200); - public Situation6 Situation { get; } = new(sav, 0x01400); - public PlayTime6 Played { get; } = new(sav, 0x01800); - public Fashion6XY Fashion { get; } = new(sav, 0x1A00); - public Misc6XY Misc { get; } = new(sav, 0x4200); - public BoxLayout6 BoxLayout { get; } = new(sav, 0x4400); - public BattleBox6 BattleBox { get; } = new(sav, 0x04A00); - public MyStatus6 Status { get; } = new MyStatus6XY(sav, 0x14000); - public Zukan6XY Zukan { get; } = new(sav, 0x15000, 0x3C8); - public ConfigSave6 Config { get; } = new(sav, 0x16200); - public OPower6 OPower { get; } = new(sav, 0x16A00); - public Encount6 Encount { get; } = new(sav, 0x17E00); - public MysteryBlock6 MysteryGift { get; } = new(sav, 0x1BC00); - public SubEventLog6 SUBE { get; } = new SubEventLog6XY(sav, 0x1D800); - public RecordBlock6 Records { get; } = new RecordBlock6XY(sav, 0x1E400); - public SuperTrainBlock SuperTrain { get; } = new(sav, 0x1F200); - public LinkBlock6 Link { get; } = new(sav, 0x1FE00); - public MaisonBlock Maison { get; } = new(sav, 0x1B000); + MyItem ISaveBlock6Core.Items => Items; + SubEventLog6 ISaveBlock6Main.SUBE => SUBE; + RecordBlock6 ISaveBlock6Core.Records => Records; + MyStatus6 ISaveBlock6Core.Status => Status; + + private static Memory Block(SAV6XY sav, int i) + { + var data = sav.Data; + var block = BlocksXY[i]; + return data.AsMemory(block.Offset, block.Length); + } } diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor7SM.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor7SM.cs index 0ac0b0b9a..fe0ca0b2f 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor7SM.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor7SM.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; @@ -5,7 +6,7 @@ namespace PKHeX.Core; /// /// Information for Accessing individual blocks within a . /// -public sealed class SaveBlockAccessor7SM : ISaveBlockAccessor, ISaveBlock7SM +public sealed class SaveBlockAccessor7SM(SAV7SM sav) : ISaveBlockAccessor, ISaveBlock7SM { public const int BlockMetadataOffset = SaveUtil.SIZE_G7SM - 0x200; private const int boSM = BlockMetadataOffset; @@ -51,52 +52,39 @@ public sealed class SaveBlockAccessor7SM : ISaveBlockAccessor, ISave new(boSM, 36, 0x6BA00, 0x00200), // 36 TurtleSalmonSave ]; - public SaveBlockAccessor7SM(SAV7SM sav) - { - var bi = BlockInfo; - - Items = new MyItem7SM(sav, 0); - Situation = new Situation7(sav, bi[01].Offset); - MyStatus = new MyStatus7(sav, bi[03].Offset); - Fame = new HallOfFame7(sav, bi[05].Offset + 0x9C4); - Zukan = new Zukan7(sav, bi[06].Offset, 0x550); - Misc = new Misc7(sav, bi[09].Offset); - FieldMenu = new FieldMenu7(sav, bi[10].Offset); - Config = new ConfigSave7(sav, bi[11].Offset); - GameTime = new GameTime7(sav, bi[12].Offset); - BoxLayout = new BoxLayout7(sav, bi[13].Offset); - ResortSave = new ResortSave7(sav, bi[15].Offset); - Played = new PlayTime6(sav, bi[16].Offset); - Overworld = new FieldMoveModelSave7(sav, bi[17].Offset); - Fashion = new FashionBlock7(sav, bi[18].Offset); - Festa = new JoinFesta7(sav, bi[21].Offset); - PokeFinder = new PokeFinder7(sav, bi[26].Offset); - MysteryGift = new MysteryBlock7(sav, bi[27].Offset); - Records = new RecordBlock7SM(sav, bi[28].Offset); - BattleTree = new BattleTree7(sav, bi[32].Offset); - Daycare = new Daycare7(sav, bi[33].Offset); - } - public IReadOnlyList BlockInfo => BlockInfoSM; - public MyItem Items { get; } - public MysteryBlock7 MysteryGift { get; } - public PokeFinder7 PokeFinder { get; } - public JoinFesta7 Festa { get; } - public Daycare7 Daycare { get; } - public RecordBlock6 Records { get; } - public PlayTime6 Played { get; } - public MyStatus7 MyStatus { get; } - public FieldMoveModelSave7 Overworld { get; } - public Situation7 Situation { get; } - public ConfigSave7 Config { get; } - public GameTime7 GameTime { get; } - public Misc7 Misc { get; } - public Zukan7 Zukan { get; } - public BoxLayout7 BoxLayout { get; } - public BattleTree7 BattleTree { get; } - public ResortSave7 ResortSave { get; } - public FieldMenu7 FieldMenu { get; } - public FashionBlock7 Fashion { get; } - public HallOfFame7 Fame { get; } + public MyItem7SM Items { get; } = new(sav, Block(sav, 0)); + public Situation7 Situation { get; } = new(sav, Block(sav, 1)); + public MyStatus7 MyStatus { get; } = new(sav, Block(sav, 3)); + public EventWork7SM EventWork { get; } = new(sav, Block(sav, 5)); + public Zukan7 Zukan { get; } = new(sav, Block(sav, 6), 0x550); + public GTS7 GTS { get; } = new(sav, Block(sav, 07)); + public UnionPokemon7 Fused { get; } = new(sav, Block(sav, 8)); + public Misc7 Misc { get; } = new(sav, Block(sav, 9)); + public FieldMenu7 FieldMenu { get; } = new(sav, Block(sav, 10)); + public ConfigSave7 Config { get; } = new(sav, Block(sav, 11)); + public GameTime7 GameTime { get; } = new(sav, Block(sav, 12)); + public BoxLayout7 BoxLayout { get; } = new(sav, Block(sav, 13)); + public ResortSave7 ResortSave { get; } = new(sav, Block(sav, 15)); + public PlayTime6 Played { get; } = new(sav, Block(sav, 16)); + public FieldMoveModelSave7 Overworld { get; } = new(sav, Block(sav, 17)); + public FashionBlock7 Fashion { get; } = new(sav, Block(sav, 18)); + public JoinFesta7 Festa { get; } = new(sav, Block(sav, 21)); + public PokeFinder7 PokeFinder { get; } = new(sav, Block(sav, 26)); + public MysteryBlock7 MysteryGift { get; } = new(sav, Block(sav, 27)); + public RecordBlock7SM Records { get; } = new(sav, Block(sav, 28)); + public BattleTree7 BattleTree { get; } = new(sav, Block(sav, 32)); + public Daycare7 Daycare { get; } = new(sav, Block(sav, 33)); + + MyItem ISaveBlock7Main.Items => Items; + EventWork7 ISaveBlock7Main.EventWork => EventWork; + RecordBlock6 ISaveBlock7Main.Records => Records; + + private static Memory Block(SAV7SM sav, int index) + { + var data = sav.Data; + var block = BlockInfoSM[index]; + return data.AsMemory(block.Offset, block.Length); + } } diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor7USUM.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor7USUM.cs index 17d5969fe..91785ebd4 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor7USUM.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor7USUM.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; @@ -5,7 +6,7 @@ namespace PKHeX.Core; /// /// Information for Accessing individual blocks within a . /// -public sealed class SaveBlockAccessor7USUM : ISaveBlockAccessor, ISaveBlock7USUM +public sealed class SaveBlockAccessor7USUM(SAV7USUM sav) : ISaveBlockAccessor, ISaveBlock7USUM { public const int BlockMetadataOffset = SaveUtil.SIZE_G7USUM - 0x200; private const int boUU = BlockMetadataOffset; @@ -53,54 +54,39 @@ public sealed class SaveBlockAccessor7USUM : ISaveBlockAccessor, ISa new(boUU, 38, 0x6C600, 0x00400), // 38 FinderStudioSave ]; - public SaveBlockAccessor7USUM(SAV7USUM sav) - { - var bi = BlockInfo; - - Items = new MyItem7USUM(sav, 0); - Situation = new Situation7(sav, bi[01].Offset); - MyStatus = new MyStatus7(sav, bi[03].Offset); - Fame = new HallOfFame7(sav, bi[05].Offset + 0xA3C); - Zukan = new Zukan7(sav, bi[06].Offset, 0x550); - Misc = new Misc7(sav, bi[09].Offset); - FieldMenu = new FieldMenu7(sav, bi[10].Offset); - Config = new ConfigSave7(sav, bi[11].Offset); - GameTime = new GameTime7(sav, bi[12].Offset); - BoxLayout = new BoxLayout7(sav, bi[13].Offset); - ResortSave = new ResortSave7(sav, bi[15].Offset); - Played = new PlayTime6(sav, bi[16].Offset); - Overworld = new FieldMoveModelSave7(sav, bi[17].Offset); - Fashion = new FashionBlock7(sav, bi[18].Offset); - Festa = new JoinFesta7(sav, bi[21].Offset); - PokeFinder = new PokeFinder7(sav, bi[26].Offset); - MysteryGift = new MysteryBlock7(sav, bi[27].Offset); - Records = new RecordBlock7USUM(sav, bi[28].Offset); - BattleTree = new BattleTree7(sav, bi[32].Offset); - Daycare = new Daycare7(sav, bi[33].Offset); - BattleAgency = new BattleAgency7(sav, bi[37].Offset); - } - public IReadOnlyList BlockInfo => BlockInfoUSUM; + public MyItem7USUM Items { get; } = new(sav, Block(sav, 00)); + public Situation7 Situation { get; } = new(sav, Block(sav, 01)); + public MyStatus7 MyStatus { get; } = new(sav, Block(sav, 03)); + public EventWork7USUM EventWork { get; } = new(sav, Block(sav, 5)); + public Zukan7 Zukan { get; } = new(sav, Block(sav, 06), 0x550); + public GTS7 GTS { get; } = new(sav, Block(sav, 07)); + public UnionPokemon7 Fused { get; } = new(sav, Block(sav, 08)); + public Misc7 Misc { get; } = new(sav, Block(sav, 09)); + public FieldMenu7 FieldMenu { get; } = new(sav, Block(sav, 10)); + public ConfigSave7 Config { get; } = new(sav, Block(sav, 11)); + public GameTime7 GameTime { get; } = new(sav, Block(sav, 12)); + public BoxLayout7 BoxLayout { get; } = new(sav, Block(sav, 13)); + public ResortSave7 ResortSave { get; } = new(sav, Block(sav, 15)); + public PlayTime6 Played { get; } = new(sav, Block(sav, 16)); + public FieldMoveModelSave7 Overworld { get; } = new(sav, Block(sav, 17)); + public FashionBlock7 Fashion { get; } = new(sav, Block(sav, 18)); + public JoinFesta7 Festa { get; } = new(sav, Block(sav, 21)); + public PokeFinder7 PokeFinder { get; } = new(sav, Block(sav, 26)); + public MysteryBlock7 MysteryGift { get; } = new(sav, Block(sav, 27)); + public RecordBlock7USUM Records { get; } = new(sav, Block(sav, 28)); + public BattleTree7 BattleTree { get; } = new(sav, Block(sav, 32)); + public Daycare7 Daycare { get; } = new(sav, Block(sav, 33)); + public BattleAgency7 BattleAgency { get; } = new(sav, Block(sav, 37)); - public MyItem Items { get; } - public MysteryBlock7 MysteryGift { get; } - public PokeFinder7 PokeFinder { get; } - public JoinFesta7 Festa { get; } - public Daycare7 Daycare { get; } - public RecordBlock6 Records { get; } - public PlayTime6 Played { get; } - public MyStatus7 MyStatus { get; } - public FieldMoveModelSave7 Overworld { get; } - public Situation7 Situation { get; } - public ConfigSave7 Config { get; } - public GameTime7 GameTime { get; } - public Misc7 Misc { get; } - public Zukan7 Zukan { get; } - public BoxLayout7 BoxLayout { get; } - public BattleTree7 BattleTree { get; } - public ResortSave7 ResortSave { get; } - public FieldMenu7 FieldMenu { get; } - public FashionBlock7 Fashion { get; } - public HallOfFame7 Fame { get; } - public BattleAgency7 BattleAgency { get; } + MyItem ISaveBlock7Main.Items => Items; + EventWork7 ISaveBlock7Main.EventWork => EventWork; + RecordBlock6 ISaveBlock7Main.Records => Records; + + private static Memory Block(SAV7USUM sav, int index) + { + var data = sav.Data; + var block = BlockInfoUSUM[index]; + return data.AsMemory(block.Offset, block.Length); + } } diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor7b.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor7b.cs index 460c5cab6..f6ccd97a2 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor7b.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor7b.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; @@ -5,7 +6,7 @@ namespace PKHeX.Core; /// /// Information for Accessing individual blocks within a . /// -public sealed class SaveBlockAccessor7b : ISaveBlockAccessor, ISaveBlock7b +public sealed class SaveBlockAccessor7b(SAV7b sav) : ISaveBlockAccessor, ISaveBlock7b { private const int boGG = 0xB8800 - 0x200; // nowhere near 1MB (savedata.bin size) @@ -36,40 +37,26 @@ public sealed class SaveBlockAccessor7b : ISaveBlockAccessor, ISave public IReadOnlyList BlockInfo => BlockInfoGG; - public SaveBlockAccessor7b(SAV7b sav) - { - Zukan = new Zukan7b(sav, GetBlockOffset(BelugaBlockIndex.Zukan), 0x550); - Config = new ConfigSave7b(sav, GetBlockOffset(BelugaBlockIndex.ConfigSave)); - Items = new MyItem7b(sav, GetBlockOffset(BelugaBlockIndex.MyItem)); - Coordinates = new Coordinates7b(sav, GetBlockOffset(BelugaBlockIndex.Coordinates)); - Storage = new PokeListHeader(sav, GetBlockOffset(BelugaBlockIndex.PokeListHeader)); - Status = new MyStatus7b(sav, GetBlockOffset(BelugaBlockIndex.MyStatus)); - Played = new PlayTime7b(sav, GetBlockOffset(BelugaBlockIndex.PlayTime)); - Misc = new Misc7b(sav, GetBlockOffset(BelugaBlockIndex.Misc)); - EventWork = new EventWork7b(sav, GetBlockOffset(BelugaBlockIndex.EventWork)); - GiftRecords = new WB7Records(sav, GetBlockOffset(BelugaBlockIndex.WB7Record)); - Captured = new CaptureRecords(sav, GetBlockOffset(BelugaBlockIndex.CaptureRecord)); - FashionPlayer = new Fashion7b(sav, GetBlockOffset(BelugaBlockIndex.FashionPlayer)); - FashionStarter = new Fashion7b(sav, GetBlockOffset(BelugaBlockIndex.FashionStarter)); - Park = new GoParkStorage(sav, GetBlockOffset(BelugaBlockIndex.GoParkEntities)); - PlayerGeoLocation = new PlayerGeoLocation7b(sav, GetBlockOffset(BelugaBlockIndex.PlayerGeoLocation)); - } + public MyItem7b Items { get; } = new(sav, Block(sav, BelugaBlockIndex.MyItem)); + public Coordinates7b Coordinates { get; } = new(sav, Block(sav, BelugaBlockIndex.Coordinates)); + public Misc7b Misc { get; } = new(sav, Block(sav, BelugaBlockIndex.Misc)); + public Zukan7b Zukan { get; } = new(sav, Block(sav, BelugaBlockIndex.Zukan), 0x550); + public MyStatus7b Status { get; } = new(sav, Block(sav, BelugaBlockIndex.MyStatus)); + public PlayTime7b Played { get; } = new(sav, Block(sav, BelugaBlockIndex.PlayTime)); + public ConfigSave7b Config { get; } = new(sav, Block(sav, BelugaBlockIndex.ConfigSave)); + public EventWork7b EventWork { get; } = new(sav, Block(sav, BelugaBlockIndex.EventWork)); + public PokeListHeader Storage { get; } = new(sav, Block(sav, BelugaBlockIndex.PokeListHeader), sav.State.Exportable); + public WB7Records GiftRecords { get; } = new(sav, Block(sav, BelugaBlockIndex.WB7Record)); + public CaptureRecords Captured { get; } = new(sav, Block(sav, BelugaBlockIndex.CaptureRecord)); + public Daycare7b Daycare { get; } = new(sav, Block(sav, BelugaBlockIndex.Daycare)); + public Fashion7b FashionPlayer { get; } = new(sav, Block(sav, BelugaBlockIndex.FashionPlayer)); + public Fashion7b FashionStarter { get; } = new(sav, Block(sav, BelugaBlockIndex.FashionStarter)); + public GoParkStorage Park { get; } = new(sav, Block(sav, BelugaBlockIndex.GoParkEntities)); + public PlayerGeoLocation7b PlayerGeoLocation { get; } = new(sav, Block(sav, BelugaBlockIndex.PlayerGeoLocation)); - public MyItem Items { get; } - public Coordinates7b Coordinates { get; } - public Misc7b Misc { get; } - public Zukan7b Zukan { get; } - public MyStatus7b Status { get; } - public PlayTime7b Played { get; } - public ConfigSave7b Config { get; } - public EventWork7b EventWork { get; } - public PokeListHeader Storage { get; } - public WB7Records GiftRecords { get; } - public CaptureRecords Captured { get; } - public Fashion7b FashionPlayer { get; } - public Fashion7b FashionStarter { get; } - public GoParkStorage Park { get; } - public PlayerGeoLocation7b PlayerGeoLocation { get; } - public BlockInfo GetBlock(BelugaBlockIndex index) => BlockInfo[(int)index]; - public int GetBlockOffset(BelugaBlockIndex index) => GetBlock(index).Offset; + public static Memory Block(SAV7b sav, BelugaBlockIndex index) + { + var block = BlockInfoGG[(int)index]; + return sav.Data.AsMemory(block.Offset, block.Length); + } } diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor8LA.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor8LA.cs index 7f19174f4..94e3666bf 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor8LA.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor8LA.cs @@ -4,36 +4,20 @@ namespace PKHeX.Core; // ReSharper disable UnusedMember.Local #pragma warning disable IDE0051, RCS1213 // Remove unused private members -public sealed class SaveBlockAccessor8LA : SCBlockAccessor, ISaveBlock8LA +public sealed class SaveBlockAccessor8LA(SAV8LA sav) : SCBlockAccessor, ISaveBlock8LA { - public override IReadOnlyList BlockInfo { get; } - public Party8a PartyInfo { get; } - public Box8 BoxInfo { get; } - public MyStatus8a MyStatus { get; } - public PokedexSave8a PokedexSave { get; } - public BoxLayout8a BoxLayout { get; } - public MyItem8a Items { get; } - public Epoch1970Value AdventureStart { get; } - public Coordinates8a Coordinates { get; } - public LastSaved8a LastSaved { get; } - public PlayerFashion8a FashionPlayer { get; } - public PlayTime8a Played { get; } - - public SaveBlockAccessor8LA(SAV8LA sav) - { - BlockInfo = sav.AllBlocks; - BoxInfo = new Box8(sav, GetBlock(KBox)); - PokedexSave = new PokedexSave8a(sav, GetBlock(KZukan)); - BoxLayout = new BoxLayout8a(sav, GetBlock(KBoxLayout)); - PartyInfo = new Party8a(sav, GetBlock(KParty)); - MyStatus = new MyStatus8a(sav, GetBlock(KMyStatus)); - Items = new MyItem8a(sav, GetBlock(KItemRegular)); - AdventureStart = new Epoch1970Value(GetBlock(KAdventureStart)); - LastSaved = new LastSaved8a(sav, GetBlock(KLastSaved)); - Played = new PlayTime8a(sav, GetBlock(KPlayTime)); - Coordinates = new Coordinates8a(sav, GetBlock(KCoordinates)); - FashionPlayer = new PlayerFashion8a(sav, GetBlock(KFashionPlayer)); - } + public override IReadOnlyList BlockInfo { get; } = sav.AllBlocks; + public Party8a PartyInfo { get; } = new(sav, Block(sav, KParty)); + public Box8 BoxInfo { get; } = new(sav, Block(sav, KBox)); + public MyStatus8a MyStatus { get; } = new(sav, Block(sav, KMyStatus)); + public PokedexSave8a PokedexSave { get; } = new(sav, Block(sav, KZukan)); + public BoxLayout8a BoxLayout { get; } = new(sav, Block(sav, KBoxLayout)); + public MyItem8a Items { get; } = new(sav, Block(sav, KItemRegular)); + public Epoch1970Value AdventureStart { get; } = new(Block(sav, KAdventureStart)); + public Coordinates8a Coordinates { get; } = new(sav, Block(sav, KCoordinates)); + public LastSaved8a LastSaved { get; } = new(sav, Block(sav, KLastSaved)); + public PlayerFashion8a FashionPlayer { get; } = new(sav, Block(sav, KFashionPlayer)); + public PlayTime8a Played { get; } = new(sav, Block(sav, KPlayTime)); public int DetectRevision() => HasBlock(0x8184EFB4) ? 1 : 0; diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor8SWSH.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor8SWSH.cs index 82165dafe..2349f46f3 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor8SWSH.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor8SWSH.cs @@ -7,54 +7,29 @@ namespace PKHeX.Core; /// /// Information for Accessing individual blocks within a . /// -public sealed class SaveBlockAccessor8SWSH : SCBlockAccessor, ISaveBlock8Main +public sealed class SaveBlockAccessor8SWSH(SAV8SWSH sav) : SCBlockAccessor, ISaveBlock8Main { - public override IReadOnlyList BlockInfo { get; } - public Box8 BoxInfo { get; } - public Party8 PartyInfo { get; } - public MyItem8 Items { get; } - public Coordinates8 Coordinates { get; } - public MyStatus8 MyStatus { get; } - public Misc8 Misc { get; } - public Zukan8 Zukan { get; } - public BoxLayout8 BoxLayout { get; } - public PlayTime8 Played { get; } - public Fused8 Fused { get; } - public Daycare8 Daycare { get; } - public Record8 Records { get; } - public TrainerCard8 TrainerCard{ get; } - public FashionUnlock8 Fashion { get; } - public RaidSpawnList8 RaidGalar { get; } - public RaidSpawnList8 RaidArmor { get; } - public RaidSpawnList8 RaidCrown { get; } - public TitleScreen8 TitleScreen { get; } - public TeamIndexes8 TeamIndexes { get; } - public HallOfFameTime8 FameTime { get; } - - public SaveBlockAccessor8SWSH(SAV8SWSH sav) - { - BlockInfo = sav.AllBlocks; - BoxInfo = new Box8(sav, GetBlock(KBox)); - PartyInfo = new Party8(sav, GetBlock(KParty)); - Items = new MyItem8(sav, GetBlock(KItem)); - Coordinates = new Coordinates8(sav, GetBlock(KCoordinates)); - Zukan = new Zukan8(sav, GetBlock(KZukan), GetBlockSafe(KZukanR1), GetBlockSafe(KZukanR2)); - MyStatus = new MyStatus8(sav, GetBlock(KMyStatus)); - Misc = new Misc8(sav, GetBlock(KMisc)); - BoxLayout = new BoxLayout8(sav, GetBlock(KBoxLayout)); - TrainerCard = new TrainerCard8(sav, GetBlock(KTrainerCard)); - Played = new PlayTime8(sav, GetBlock(KPlayTime)); - Fused = new Fused8(sav, GetBlock(KFused)); - Daycare = new Daycare8(sav, GetBlock(KDaycare)); - Records = new Record8(sav, GetBlock(KRecord)); - Fashion = new FashionUnlock8(sav, GetBlock(KFashionUnlock)); - RaidGalar = new RaidSpawnList8(sav, GetBlock(KRaidSpawnList), RaidSpawnList8.RaidCountLegal_O0); - RaidArmor = new RaidSpawnList8(sav, GetBlockSafe(KRaidSpawnListR1), RaidSpawnList8.RaidCountLegal_R1); - RaidCrown = new RaidSpawnList8(sav, GetBlockSafe(KRaidSpawnListR2), RaidSpawnList8.RaidCountLegal_R2); - TitleScreen = new TitleScreen8(sav, GetBlock(KTitleScreenTeam)); - TeamIndexes = new TeamIndexes8(sav, GetBlock(KTeamIndexes), GetBlock(KTeamLocks)); - FameTime = new HallOfFameTime8(sav, GetBlock(KEnteredHallOfFame)); - } + public override IReadOnlyList BlockInfo { get; } = sav.AllBlocks; + public Box8 BoxInfo { get; } = new(sav, Block(sav, KBox)); + public Party8 PartyInfo { get; } = new(sav, Block(sav, KParty)); + public MyItem8 Items { get; } = new(sav, Block(sav, KItem)); + public Coordinates8 Coordinates { get; } = new(sav, Block(sav, KCoordinates)); + public MyStatus8 MyStatus { get; } = new(sav, Block(sav, KMyStatus)); + public Misc8 Misc { get; } = new(sav, Block(sav, KMisc)); + public Zukan8 Zukan { get; } = new(sav, Block(sav, KZukan), BlockSafe(sav, KZukanR1), BlockSafe(sav, KZukanR2)); + public BoxLayout8 BoxLayout { get; } = new(sav, Block(sav, KBoxLayout)); + public PlayTime8 Played { get; } = new(sav, Block(sav, KPlayTime)); + public Fused8 Fused { get; } = new(sav, Block(sav, KFused)); + public Daycare8 Daycare { get; } = new(sav, Block(sav, KDaycare)); + public Record8 Records { get; } = new(sav, Block(sav, KRecord)); + public TrainerCard8 TrainerCard{ get; } = new(sav, Block(sav, KTrainerCard)); + public FashionUnlock8 Fashion { get; } = new(sav, Block(sav, KFashionUnlock)); + public RaidSpawnList8 RaidGalar { get; } = new(sav, Block(sav, KRaidSpawnList), RaidSpawnList8.RaidCountLegal_O0); + public RaidSpawnList8 RaidArmor { get; } = new(sav, BlockSafe(sav, KRaidSpawnListR1), RaidSpawnList8.RaidCountLegal_R1); + public RaidSpawnList8 RaidCrown { get; } = new(sav, BlockSafe(sav, KRaidSpawnListR2), RaidSpawnList8.RaidCountLegal_R2); + public TitleScreen8 TitleScreen { get; } = new(sav, Block(sav, KTitleScreenTeam)); + public TeamIndexes8 TeamIndexes { get; } = new(sav, Block(sav, KTeamIndexes), Block(sav, KTeamLocks)); + public HallOfFameTime8 FameTime { get; } = new(sav, Block(sav, KEnteredHallOfFame)); // Arrays (Blocks) private const uint KTeamNames = 0x1920C1E4; // Team 1, 2...6 ((10 + terminator)*6 char16 strings) @@ -97,7 +72,7 @@ public sealed class SaveBlockAccessor8SWSH : SCBlockAccessor, ISaveBlock8Main private const uint KTrainer3EndlessRecordData = 0x7BD78AF1; // Trainer 3's Data of Best Endless Dynamax Adventure Record private const uint KTrainer4EndlessRecordData = 0x7AD7895E; // Trainer 4's Data of Best Endless Dynamax Adventure Record private const uint KPokeJobStorage = 0xB25C772B; // Pokémon storage while they are doing Jobs - private const uint KTeamLocks = 0x605EBC30; + private const uint KTeamLocks = 0x605EBC30; // Rental Teams - Objects (Blocks) private const uint KRentalTeam1 = 0x149A1DD0; diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor9SV.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor9SV.cs index a68e047fb..c177dea1c 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor9SV.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor9SV.cs @@ -9,71 +9,60 @@ namespace PKHeX.Core; /// /// Information for Accessing individual blocks within a . /// -public sealed class SaveBlockAccessor9SV : SCBlockAccessor, ISaveBlock9Main +public sealed class SaveBlockAccessor9SV(SAV9SV sav) : SCBlockAccessor, ISaveBlock9Main { - public override IReadOnlyList BlockInfo { get; } - public Box8 BoxInfo { get; } - public Party9 PartyInfo { get; } - public MyItem9 Items { get; } - public MyStatus9 MyStatus { get; } - public BoxLayout9 BoxLayout { get; } - public PlayTime9 Played { get; } - public Zukan9 Zukan { get; } - public ConfigSave9 Config { get; } - public ConfigCamera9 ConfigCamera { get; } - public TeamIndexes8 TeamIndexes { get; } - public Epoch1900DateTimeValue LastSaved { get; } - public Epoch1970Value LastDateCycle { get; } - public PlayerFashion9 PlayerFashion { get; } - public PlayerAppearance9 PlayerAppearance { get; } - public RaidSpawnList9 RaidPaldea { get; } - public RaidSpawnList9 RaidKitakami { get; } - public RaidSpawnList9 RaidBlueberry { get; } - public RaidSevenStar9 RaidSevenStar { get; } - public Epoch1900DateValue EnrollmentDate { get; } - public BlueberryQuestRecord9 BlueberryQuestRecord { get; } - public BlueberryClubRoom9 BlueberryClubRoom { get; } + public override IReadOnlyList BlockInfo { get; } = sav.AllBlocks; + public Box8 BoxInfo { get; } = new(sav, Block(sav, KBox)); + public Party9 PartyInfo { get; } = new(sav, Block(sav, KParty)); + public MyItem9 Items { get; } = new(sav, Block(sav, KItem)); + public MyStatus9 MyStatus { get; } = new(sav, Block(sav, KMyStatus)); + public BoxLayout9 BoxLayout { get; } = new(sav, Block(sav, KBoxLayout)); + public PlayTime9 Played { get; } = new(sav, Block(sav, KPlayTime)); + public Zukan9 Zukan { get; } = new(sav, Block(sav, KZukan), BlockSafe(sav, KZukanT1)); + public ConfigSave9 Config { get; } = new(sav, Block(sav, KConfig)); + public ConfigCamera9 ConfigCamera { get; } = new(sav, BlockSafe(sav, KConfigCamera)); + public TeamIndexes8 TeamIndexes { get; } = new(sav, Block(sav, KTeamIndexes), Block(sav, KTeamLocks)); + public Epoch1900DateTimeValue LastSaved { get; } = new(Block(sav, KLastSaved)); + public Epoch1970Value LastDateCycle { get; } = new(Block(sav, KLastDateCycle)); + public PlayerFashion9 PlayerFashion { get; } = new(sav, Block(sav, KCurrentClothing)); + public PlayerAppearance9 PlayerAppearance { get; } = new(sav, Block(sav, KCurrentAppearance)); + public RaidSpawnList9 RaidPaldea => Raid.Paldea; + public RaidSpawnList9 RaidKitakami => Raid.Kitakami; + public RaidSpawnList9 RaidBlueberry => Raid.Blueberry; + public RaidSevenStar9 RaidSevenStar { get; } = new(sav, Block(sav, KSevenStarRaidsCapture), BlockSafe(sav, KSevenStarRaidsDefeat)); + public Epoch1900DateValue EnrollmentDate { get; } = new(Block(sav, KEnrollmentDate)); + public BlueberryQuestRecord9 BlueberryQuestRecord { get; } = new(sav, BlockSafe(sav, KBlueberryQuestRecords)); + public BlueberryClubRoom9 BlueberryClubRoom { get; } = new(sav, BlockSafe(sav, KBlueberryClubRoom)); - public SaveBlockAccessor9SV(SAV9SV sav) + private Raid9 Raid { get; } = new(sav); + + private class Raid9 { - BlockInfo = sav.AllBlocks; - BoxInfo = new Box8(sav, GetBlock(KBox)); - PartyInfo = new Party9(sav, GetBlock(KParty)); - Items = new MyItem9(sav, GetBlock(KItem)); - BoxLayout = new BoxLayout9(sav, GetBlock(KBoxLayout)); - MyStatus = new MyStatus9(sav, GetBlock(KMyStatus)); - Played = new PlayTime9(sav, GetBlock(KPlayTime)); - Zukan = new Zukan9(sav, GetBlock(KZukan), GetBlockSafe(KZukanT1)); - Config = new ConfigSave9(sav, GetBlock(KConfig)); - ConfigCamera = new ConfigCamera9(sav, GetBlockSafe(KConfigCamera)); - TeamIndexes = new TeamIndexes8(sav, GetBlock(KTeamIndexes), GetBlock(KTeamLocks)); - LastSaved = new Epoch1900DateTimeValue(GetBlock(KLastSaved)); - LastDateCycle = new Epoch1970Value(GetBlock(KLastDateCycle)); - PlayerFashion = new PlayerFashion9(sav, GetBlock(KCurrentClothing)); - PlayerAppearance = new PlayerAppearance9(sav, GetBlock(KCurrentAppearance)); + public RaidSpawnList9 Paldea { get; } + public RaidSpawnList9 Kitakami { get; } + public RaidSpawnList9 Blueberry { get; } - var raidPaldea = GetBlock(KTeraRaidPaldea); - RaidPaldea = new RaidSpawnList9(sav, raidPaldea, raidPaldea.Data, RaidSpawnList9.RaidCountLegal_T0, true); - if (TryGetBlock(KTeraRaidDLC, out var raidDLC)) + public Raid9(SAV9SV sav) { - var buffer = raidDLC.Data; - const int size = 0xC80; - var memKita = buffer.AsMemory(0, size); - var memBlue = buffer.AsMemory(size, size); - RaidKitakami = new RaidSpawnList9(sav, raidDLC, memKita, RaidSpawnList9.RaidCountLegal_T1, false); - RaidBlueberry = new RaidSpawnList9(sav, raidDLC, memBlue, RaidSpawnList9.RaidCountLegal_T2, false); - } - else - { - var fake = GetFakeBlock(); - RaidKitakami = new RaidSpawnList9(sav, fake, default, RaidSpawnList9.RaidCountLegal_T1, false); - RaidBlueberry = new RaidSpawnList9(sav, fake, default, RaidSpawnList9.RaidCountLegal_T2, false); - } + var paldea = GetBlock(sav.AllBlocks, KTeraRaidPaldea); + Paldea = new RaidSpawnList9(sav, paldea, paldea.Data, RaidSpawnList9.RaidCountLegal_T0, true); - RaidSevenStar = new RaidSevenStar9(sav, GetBlock(KSevenStarRaidsCapture), GetBlockSafe(KSevenStarRaidsDefeat)); - EnrollmentDate = new Epoch1900DateValue(GetBlock(KEnrollmentDate)); - BlueberryQuestRecord = new BlueberryQuestRecord9(sav, GetBlockSafe(KBlueberryQuestRecords)); - BlueberryClubRoom = new BlueberryClubRoom9(sav, GetBlockSafe(KBlueberryClubRoom)); + if (TryGetBlock(sav.AllBlocks, KTeraRaidDLC, out var raidDLC)) + { + var buffer = raidDLC.Data; + const int size = 0xC80; + var memKita = buffer.AsMemory(0, size); + var memBlue = buffer.AsMemory(size, size); + Kitakami = new RaidSpawnList9(sav, raidDLC, memKita, RaidSpawnList9.RaidCountLegal_T1, false); + Blueberry = new RaidSpawnList9(sav, raidDLC, memBlue, RaidSpawnList9.RaidCountLegal_T2, false); + } + else + { + var fake = GetFakeBlock(); + Kitakami = new RaidSpawnList9(sav, fake, default, RaidSpawnList9.RaidCountLegal_T1, false); + Blueberry = new RaidSpawnList9(sav, fake, default, RaidSpawnList9.RaidCountLegal_T2, false); + } + } } // Arrays (Blocks) diff --git a/PKHeX.Core/Saves/Blocks/BlockInfo4.cs b/PKHeX.Core/Saves/Blocks/BlockInfo4.cs index dc00bdaf8..15c11cdf8 100644 --- a/PKHeX.Core/Saves/Blocks/BlockInfo4.cs +++ b/PKHeX.Core/Saves/Blocks/BlockInfo4.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// /// Gen4 Extra Block Info /// -public class BlockInfo4 : BlockInfo +public sealed class BlockInfo4 : BlockInfo { private const int SIZE_FOOTER = 0x10; private readonly int FooterOffset; @@ -61,7 +61,7 @@ public class BlockInfo4 : BlockInfo WriteUInt16LittleEndian(data[(FooterOffset + 14)..], chk); } - protected void SetMagic(Span data, uint magic) + private void SetMagic(Span data, uint magic) { if (!IsInitialized(data)) return; diff --git a/PKHeX.Core/Saves/Blocks/RecordBlock.cs b/PKHeX.Core/Saves/Blocks/RecordBlock.cs index c50e03920..c8a1019e2 100644 --- a/PKHeX.Core/Saves/Blocks/RecordBlock.cs +++ b/PKHeX.Core/Saves/Blocks/RecordBlock.cs @@ -2,21 +2,14 @@ using System; namespace PKHeX.Core; -public abstract class RecordBlock : SaveBlock, IRecordStatStorage where T : SaveFile +public abstract class RecordBlock(T sav, Memory raw) : SaveBlock(sav, raw), IRecordStatStorage + where T : SaveFile { protected abstract ReadOnlySpan RecordMax { get; } public abstract int GetRecord(int recordID); public abstract void SetRecord(int recordID, int value); public int GetRecordMax(int recordID) => Records.GetMax(recordID, RecordMax); - public int GetRecordOffset(int recordID) => Records.GetOffset(Offset, recordID); + public int GetRecordOffset(int recordID) => Records.GetOffset(recordID); public void AddRecord(int recordID, int count = 1) => SetRecord(recordID, GetRecord(recordID) + count); - - protected RecordBlock(T sav) : base(sav) - { - } - - protected RecordBlock(T sav, byte[] data) : base(sav, data) - { - } } diff --git a/PKHeX.Core/Saves/Blocks/SaveBlock.cs b/PKHeX.Core/Saves/Blocks/SaveBlock.cs index 3ecbf63ae..4658f9815 100644 --- a/PKHeX.Core/Saves/Blocks/SaveBlock.cs +++ b/PKHeX.Core/Saves/Blocks/SaveBlock.cs @@ -1,3 +1,4 @@ +using System; using System.ComponentModel; namespace PKHeX.Core; @@ -5,20 +6,18 @@ namespace PKHeX.Core; /// /// Base class for a savegame data reader. /// -public abstract class SaveBlock(T sav, byte[] data, int offset = 0) : IDataIndirect where T : SaveFile +public abstract class SaveBlock(T sav, Memory raw) : IDataIndirect where T : SaveFile { protected readonly T SAV = sav; - [Browsable(false)] public byte[] Data { get; } = data; - [Browsable(false)] public int Offset { get; protected init; } = offset; + [Browsable(false)] protected Memory Raw => raw; + [Browsable(false)] public Span Data => raw.Span; - protected SaveBlock(T sav) : this(sav, sav.Data) { } - - protected SaveBlock(T sav, int offset) : this(sav, sav.Data, offset) { } + public bool Equals(ReadOnlyMemory other) => other.Equals(raw); } public interface IDataIndirect { - int Offset { get; } - byte[] Data { get; } + Span Data { get; } + bool Equals(ReadOnlyMemory other); } diff --git a/PKHeX.Core/Saves/Encryption/ColoCrypto.cs b/PKHeX.Core/Saves/Encryption/ColoCrypto.cs new file mode 100644 index 000000000..08c486ef1 --- /dev/null +++ b/PKHeX.Core/Saves/Encryption/ColoCrypto.cs @@ -0,0 +1,211 @@ +using System; +using System.Security.Cryptography; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core; + +/// +/// Encryption logic used by Pokémon Colosseum +/// +public static class ColoCrypto +{ + private const int sha1HashSize = 20; + public const int SLOT_SIZE = 0x1E000; + public const int SLOT_START = 0x6000; + public const int SLOT_COUNT = 3; + public const int SAVE_SIZE = SLOT_START + (SLOT_SIZE * SLOT_COUNT); + + public static byte[] GetSlot(ReadOnlySpan fullEncrypted, int slotIndex) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(fullEncrypted.Length, SAVE_SIZE); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)slotIndex, SLOT_COUNT); + + var result = fullEncrypted.Slice(SLOT_START + (slotIndex * SLOT_SIZE), SLOT_SIZE).ToArray(); + DecryptInPlace(result); + return result; + } + + public static Memory GetSlot(Memory fullEncrypted, int slotIndex) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(fullEncrypted.Length, SAVE_SIZE); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)slotIndex, SLOT_COUNT); + + var result = fullEncrypted.Slice(SLOT_START + (slotIndex * SLOT_SIZE), SLOT_SIZE); + DecryptInPlace(result.Span); + return result; + } + + public static void GetSlot(ReadOnlySpan fullEncrypted, int slotIndex, Span slotData) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(slotData.Length, SLOT_SIZE); + ArgumentOutOfRangeException.ThrowIfNotEqual(fullEncrypted.Length, SAVE_SIZE); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)slotIndex, SLOT_COUNT); + + var data = fullEncrypted.Slice(SLOT_START + (slotIndex * SLOT_SIZE), SLOT_SIZE); + data.CopyTo(slotData); + DecryptInPlace(slotData); + } + + public static void SetSlot(Span fullEncrypted, int slotIndex, ReadOnlySpan slotData) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(slotData.Length, SLOT_SIZE); + ArgumentOutOfRangeException.ThrowIfNotEqual(fullEncrypted.Length, SAVE_SIZE); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)slotIndex, SLOT_COUNT); + + var data = fullEncrypted.Slice(SLOT_START + (slotIndex * SLOT_SIZE), SLOT_SIZE); + slotData.CopyTo(data); + EncryptInPlace(data); + } + + public static void EncryptInPlace(Span slot) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(slot.Length, SLOT_SIZE); + + // Get updated save slot data + Span digest = stackalloc byte[sha1HashSize]; + slot[^sha1HashSize..].CopyTo(digest); + + EncryptColosseum(slot, digest); + } + + public static void DecryptInPlace(Span slot) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(slot.Length, SLOT_SIZE); + + // Get updated save slot data + Span digest = stackalloc byte[sha1HashSize]; + slot[^sha1HashSize..].CopyTo(digest); + + DecryptColosseum(slot, digest); + } + + // Encryption Algorithm: + // Use a 20-byte hash as the seed for an iterative xor & hash-decrypted routine. + // Decrypt: Hash encrypted bytes for next loop, then un-xor to the decrypted state. + // Encrypt: xor to the encrypted state, then hash for the next loop. + + private static void EncryptColosseum(Span data, Span digest) + { + // At-rest key is NOT the initial key :) + for (int i = 0; i < digest.Length; i++) + digest[i] = (byte)~digest[i]; + + var span = data[0x18..^sha1HashSize]; + while (span.Length >= sha1HashSize) + { + // Could cast to u32 but juggling that is not worth it. + for (int j = 0; j < digest.Length; j++) + span[j] ^= digest[j]; + + // Hash the encrypted bytes for the next loop + SHA1.HashData(span[..sha1HashSize], digest); + + span = span[sha1HashSize..]; + } + } + + // The second to last 20 bytes of the slot doesn't appear to be used for anything. + // When we do our hash over 0x18..0x1DFD7, we spill over 0x10 bytes due to (length % 20) not being 0. + // Wonder what the correct behavior is here? :( + + private static void DecryptColosseum(Span data, Span digest) + { + // At-rest key is NOT the initial key :) + for (int i = 0; i < digest.Length; i++) + digest[i] = (byte)~digest[i]; + + Span hash = stackalloc byte[sha1HashSize]; + var span = data[0x18..^sha1HashSize]; + while (span.Length >= sha1HashSize) + { + // Hash the encrypted bytes for the next loop + SHA1.HashData(span[..sha1HashSize], hash); + + // Could cast to u32 but juggling that is not worth it. + for (int j = 0; j < digest.Length; j++) + span[j] ^= digest[j]; + + // for use in next loop + hash.CopyTo(digest); + + span = span[sha1HashSize..]; + } + } + + private static int ComputeHeaderChecksum(Span header, Span hash) + { + int result = 0; + for (int i = 0; i < 0x18; i += 4) + result -= ReadInt32BigEndian(header[i..]); + result -= ReadInt32BigEndian(header[0x18..]) ^ ~ReadInt32BigEndian(hash); + result -= ReadInt32BigEndian(header[0x1C..]) ^ ~ReadInt32BigEndian(hash[4..]); + return result; + } + + public static void SetChecksums(Span data) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(data.Length, SLOT_SIZE); + + var header = data[..0x20]; + var payload = data[..^(2 * sha1HashSize)]; + var hash = data[^sha1HashSize..]; + var headerCHK = data[0x0C..]; + + // Clear Header Checksum + WriteInt32BigEndian(headerCHK, 0); + // Compute checksum of data + SHA1.HashData(payload, hash); + + // Compute new header checksum + int newHC = ComputeHeaderChecksum(header, hash); + + // Set Header Checksum + WriteInt32BigEndian(headerCHK, newHC); + } + + /// + /// Checks if the checksums are valid. Needs to mutate the header, only temporarily (no changes when returned). + /// + public static (bool IsHeaderValid, bool IsBodyValid) IsChecksumValid(Span data) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(data.Length, SLOT_SIZE); + + var header = data[..0x20]; + var payload = data[..^(2 * sha1HashSize)]; + var storedHash = data[^sha1HashSize..]; + var hc = header[0x0C..]; + + int oldHC = ReadInt32BigEndian(hc); + // Clear Header Checksum + WriteInt32BigEndian(hc, 0); + Span currentHash = stackalloc byte[sha1HashSize]; + SHA1.HashData(payload, currentHash); + + // Compute new header checksum + int newHC = ComputeHeaderChecksum(header, currentHash); + + // Restore old header checksum + WriteInt32BigEndian(hc, oldHC); + bool isHeaderValid = newHC == oldHC; + bool isBodyValid = storedHash.SequenceEqual(currentHash); + return (isHeaderValid, isBodyValid); + } + + public static (int Index, int Count) DetectLatest(ReadOnlySpan data) + { + // Scan all 3 save slots for the highest counter + ArgumentOutOfRangeException.ThrowIfNotEqual(data.Length, SaveUtil.SIZE_G3COLO); + int counter = -1, index = -1; + for (int i = 0; i < SLOT_COUNT; i++) + { + int slotOffset = SLOT_START + (i * SLOT_SIZE); + int SaveCounter = ReadInt32BigEndian(data[(slotOffset + 4)..]); + if (SaveCounter <= counter) + continue; + + counter = SaveCounter; + index = i; + } + return (index, counter); + } +} diff --git a/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeCrypto.cs b/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeCrypto.cs index dd1955434..60f19f9b3 100644 --- a/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeCrypto.cs +++ b/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeCrypto.cs @@ -52,7 +52,7 @@ public static class MemeCrypto public static bool VerifyMemeData(ReadOnlySpan input, out byte[] output) { - foreach (MemeKeyIndex keyIndex in Enum.GetValues(typeof(MemeKeyIndex))) + foreach (MemeKeyIndex keyIndex in Enum.GetValues()) { if (VerifyMemeData(input, out output, keyIndex)) return true; diff --git a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlockCompare.cs b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlockCompare.cs index 3c1ac7811..93b4408a5 100644 --- a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlockCompare.cs +++ b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlockCompare.cs @@ -105,7 +105,7 @@ public sealed class SCBlockCompare { foreach (var b in blocks) { - var match = list.FirstOrDefault(z => ReferenceEquals(z.Key.Data, b.Data)); + var match = list.FirstOrDefault(z => z.Key.Equals(b.Data)); if (match.Value is not { } x) continue; ref var exist = ref CollectionsMarshal.GetValueRefOrNullRef(names, b.Key); diff --git a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlockMetadata.cs b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlockMetadata.cs index 063b53b9a..39feba4d6 100644 --- a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlockMetadata.cs +++ b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlockMetadata.cs @@ -95,7 +95,7 @@ public sealed class SCBlockMetadata // See if we have a Block object for this block if (block.Data.Length != 0) { - var obj = BlockList.FirstOrDefault(z => ReferenceEquals(z.Key.Data, block.Data)); + var obj = BlockList.FirstOrDefault(z => z.Key.Equals(block.Data)); if (obj is not (null, null)) { saveBlock = obj.Key; diff --git a/PKHeX.Core/Saves/SAV1.cs b/PKHeX.Core/Saves/SAV1.cs index c99b2caa7..481a97ebc 100644 --- a/PKHeX.Core/Saves/SAV1.cs +++ b/PKHeX.Core/Saves/SAV1.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// /// Generation 1 object. /// -public sealed class SAV1 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWorkArray +public sealed class SAV1 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWorkArray, IBoxDetailName, IDaycareStorage { protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public override string Extension => ".sav"; @@ -108,9 +108,11 @@ public sealed class SAV1 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo DaycareOffset = GetPartyOffset(7); // Enable Pokedex editing - PokeDex = 0; } + private int DaycareOffset = -1; + public override bool HasPokeDex => true; + private void UnpackBox(int srcOfs, int destOfs, int boxSize, int boxIndex, PokeListType boxCapacity) { var boxData = Data.AsSpan(srcOfs, boxSize).ToArray(); @@ -196,9 +198,9 @@ public sealed class SAV1 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo partyPL.Write().CopyTo(Data, Offsets.Party); // Daycare is read-only, but in case it ever becomes editable, copy it back in. - Span rawDC = Data.AsSpan(GetDaycareSlotOffset(loc: 0, slot: 0), SIZE_STORED); + Span rawDC = Data.AsSpan(GetDaycareSlotOffset(index: 0), SIZE_STORED); Span dc = stackalloc byte[1 + (2 * StringLength) + PokeCrypto.SIZE_1STORED]; - dc[0] = IsDaycareOccupied(0, 0) == true ? (byte)1 : (byte)0; + dc[0] = IsDaycareOccupied(0) ? (byte)1 : (byte)0; rawDC.Slice(2 + 1 + PokeCrypto.SIZE_1PARTY + StringLength, StringLength).CopyTo(dc[1..]); rawDC.Slice(2 + 1 + PokeCrypto.SIZE_1PARTY, StringLength).CopyTo(dc[(1 + StringLength)..]); rawDC.Slice(2 + 1, PokeCrypto.SIZE_1STORED).CopyTo(dc[(1 + (2 * StringLength))..]); @@ -241,7 +243,6 @@ public sealed class SAV1 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo public override int MaxIV => 15; public override byte Generation => 1; public override EntityContext Context => EntityContext.Gen1; - protected override int GiftCountMax => 0; public override int MaxStringLengthOT => Japanese ? 5 : 7; public override int MaxStringLengthNickname => Japanese ? 5 : 10; public override int BoxSlotCount => Japanese ? 30 : 20; @@ -308,7 +309,6 @@ public sealed class SAV1 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo public bool Yellow => Starter == 0x54; // Pikachu public byte Starter { get => Data[Offsets.Starter]; set => Data[Offsets.Starter] = value; } - public ref byte WramD72E => ref Data[Offsets.Starter + 0x17]; // offset relative to player starter // bit0 of d72e @@ -440,31 +440,30 @@ public sealed class SAV1 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo set => value.SaveAll(Data); } - public override int GetDaycareSlotOffset(int loc, int slot) + public int DaycareSlotCount => 1; + + public Memory GetDaycareSlot(int index) { + ArgumentOutOfRangeException.ThrowIfNotEqual(index, 0, nameof(index)); + return Data.AsMemory(DaycareOffset, SIZE_STORED); + } + + private int GetDaycareSlotOffset(int index) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(index, 0, nameof(index)); return DaycareOffset; } - public override uint? GetDaycareEXP(int loc, int slot) + public bool IsDaycareOccupied(int index) { - return null; + ArgumentOutOfRangeException.ThrowIfNotEqual(index, 0, nameof(index)); + return Data[Offsets.Daycare] == 0x01; } - public override bool? IsDaycareOccupied(int loc, int slot) + public void SetDaycareOccupied(int index, bool occupied) { - if (loc == 0 && slot == 0) - return Data[Offsets.Daycare] == 0x01; - return null; - } - - public override void SetDaycareEXP(int loc, int slot, uint EXP) - { - // todo - } - - public override void SetDaycareOccupied(int loc, int slot, bool occupied) - { - // todo + ArgumentOutOfRangeException.ThrowIfNotEqual(index, 0, nameof(index)); + Data[Offsets.Daycare] = (byte)(occupied ? 0x01 : 0x00); } // Storage @@ -474,15 +473,8 @@ public sealed class SAV1 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo protected set => Data[Offsets.Party] = (byte)value; } - public override int GetBoxOffset(int box) - { - return Data.Length - SIZE_RESERVED + (box * SIZE_BOX); - } - - public override int GetPartyOffset(int slot) - { - return Data.Length - SIZE_RESERVED + (BoxCount * SIZE_BOX) + (slot * SIZE_STORED); - } + public override int GetBoxOffset(int box) => Data.Length - SIZE_RESERVED + (box * SIZE_BOX); + public override int GetPartyOffset(int slot) => Data.Length - SIZE_RESERVED + (BoxCount * SIZE_BOX) + (slot * SIZE_STORED); public override int CurrentBox { @@ -496,14 +488,14 @@ public sealed class SAV1 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo set => Data[Offsets.CurrentBoxIndex] = (byte)((Data[Offsets.CurrentBoxIndex] & 0x7F) | (byte)(value ? 0x80 : 0)); } - public override string GetBoxName(int box) + public string GetBoxName(int box) { if (Japanese) - return $"ボックス{box + 1}"; - return $"BOX {box + 1}"; + return BoxDetailNameExtensions.GetDefaultBoxNameJapanese(box); + return BoxDetailNameExtensions.GetDefaultBoxName(box); } - public override void SetBoxName(int box, ReadOnlySpan value) + public void SetBoxName(int box, ReadOnlySpan value) { // Don't allow for custom box names } diff --git a/PKHeX.Core/Saves/SAV2.cs b/PKHeX.Core/Saves/SAV2.cs index 0ab6826dc..eba57024e 100644 --- a/PKHeX.Core/Saves/SAV2.cs +++ b/PKHeX.Core/Saves/SAV2.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// /// Generation 2 object. /// -public sealed class SAV2 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWorkArray +public sealed class SAV2 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWorkArray, IBoxDetailName, IDaycareStorage, IDaycareEggState { protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public override string Extension => ".sav"; @@ -134,13 +134,11 @@ public sealed class SAV2 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo daycare1.Write().CopyTo(Data, GetPartyOffset(7 + (0 * 2))); daycare2.Write().CopyTo(Data, GetPartyOffset(7 + (1 * 2))); daycare3.Write().CopyTo(Data, GetPartyOffset(7 + (2 * 2))); - DaycareOffset = Offsets.Daycare; } - - // Enable Pokedex editing - PokeDex = 0; } + public override bool HasPokeDex => true; + private int EventFlag => Offsets.EventFlag; private int EventWork => Offsets.EventWork; @@ -282,13 +280,11 @@ public sealed class SAV2 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo public override int MaxIV => 15; public override byte Generation => 2; public override EntityContext Context => EntityContext.Gen2; - protected override int GiftCountMax => 0; public override int MaxStringLengthOT => Japanese || Korean ? 5 : 7; public override int MaxStringLengthNickname => Japanese || Korean ? 5 : 10; public override int BoxSlotCount => Japanese ? 30 : 20; public override bool HasParty => true; - public override bool HasNamableBoxes => true; private int StringLength => Japanese ? GBPKML.StringLengthJapanese : GBPKML.StringLengthNotJapan; // Checksums @@ -579,12 +575,13 @@ public sealed class SAV2 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo return offset; } - public override int GetDaycareSlotOffset(int loc, int slot) => GetPartyOffset(7 + (slot * 2)); - public override uint? GetDaycareEXP(int loc, int slot) => null; - public override bool? IsDaycareOccupied(int loc, int slot) => (DaycareFlagByte(slot) & 1) != 0; - public override void SetDaycareEXP(int loc, int slot, uint EXP) { } + public int DaycareSlotCount => 2; + private int GetDaycareSlotOffset(int slot) => GetPartyOffset(7 + (slot * 2)); + public Memory GetDaycareSlot(int slot) => Data.AsMemory(GetDaycareSlotOffset(slot), SIZE_STORED); + public Memory GetDaycareEgg() => Data.AsMemory(GetDaycareSlotOffset(2), SIZE_STORED); + public bool IsDaycareOccupied(int slot) => (DaycareFlagByte(slot) & 1) != 0; - public override void SetDaycareOccupied(int loc, int slot, bool occupied) + public void SetDaycareOccupied(int slot, bool occupied) { if (occupied) DaycareFlagByte(slot) |= 1; @@ -623,7 +620,7 @@ public sealed class SAV2 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo set => Data[Offsets.CurrentBoxIndex] = (byte)((Data[Offsets.CurrentBoxIndex] & 0x7F) | (byte)(value ? 0x80 : 0)); } - public override string GetBoxName(int box) => GetString(GetBoxNameSpan(box)); + public string GetBoxName(int box) => GetString(GetBoxNameSpan(box)); private Span GetBoxNameSpan(int box) { @@ -631,7 +628,7 @@ public sealed class SAV2 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo return Data.AsSpan(Offsets.BoxNames + (box * len), len); } - public override void SetBoxName(int box, ReadOnlySpan value) + public void SetBoxName(int box, ReadOnlySpan value) { var span = GetBoxNameSpan(box); SetString(span, value, 8, StringConverterOption.Clear50); diff --git a/PKHeX.Core/Saves/SAV2Stadium.cs b/PKHeX.Core/Saves/SAV2Stadium.cs index 35dfc360c..00d3d1aa0 100644 --- a/PKHeX.Core/Saves/SAV2Stadium.cs +++ b/PKHeX.Core/Saves/SAV2Stadium.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// /// Pokémon Stadium 2 (Pokémon Stadium GS in Japan) /// -public sealed class SAV2Stadium : SAV_STADIUM +public sealed class SAV2Stadium : SAV_STADIUM, IBoxDetailName { public override int SaveRevision => Japanese ? 0 : 1; public override string SaveRevisionString => Japanese ? "J" : "U"; @@ -143,16 +143,25 @@ public sealed class SAV2Stadium : SAV_STADIUM return $"{name} [{id:D5}:{str}]"; } - public override string GetBoxName(int box) + public string GetBoxName(int box) { var ofs = GetBoxOffset(box) - 0x10; var boxNameSpan = Data.AsSpan(ofs, 0x10); var str = GetString(boxNameSpan); if (string.IsNullOrWhiteSpace(str)) - return $"Box {box + 1}"; + return BoxDetailNameExtensions.GetDefaultBoxName(box); return str; } + public void SetBoxName(int box, ReadOnlySpan name) + { + if (name.Length > StringLength) + throw new ArgumentOutOfRangeException(nameof(name), "Box name is too long."); + var ofs = GetBoxOffset(box) - 0x10; + var boxNameSpan = Data.AsSpan(ofs, 0x10); + SetString(boxNameSpan, name, StringLength, StringConverterOption.None); + } + public override SlotGroup GetTeam(int team) { if ((uint)team >= TeamCount) diff --git a/PKHeX.Core/Saves/SAV3.cs b/PKHeX.Core/Saves/SAV3.cs index fe4dd7764..ee401866b 100644 --- a/PKHeX.Core/Saves/SAV3.cs +++ b/PKHeX.Core/Saves/SAV3.cs @@ -8,7 +8,7 @@ namespace PKHeX.Core; /// /// Generation 3 object. /// -public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37 +public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37, IBoxDetailName, IBoxDetailWallpaper, IDaycareStorage, IDaycareEggState, IDaycareExperience { protected internal sealed override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public sealed override string Extension => ".sav"; @@ -35,8 +35,8 @@ public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37 // There's no harm having buffers larger than their actual size (per format). // A checksum consuming extra zeroes does not change the prior checksum result. - public readonly byte[] Small = new byte[1 * SIZE_SECTOR_USED]; // [0x890 RS, 0xf24 FR/LG, 0xf2c E] - public readonly byte[] Large = new byte[4 * SIZE_SECTOR_USED]; //3+[0xc40 RS, 0xee8 FR/LG, 0xf08 E] + public readonly byte[] Small = new byte[1 * SIZE_SECTOR_USED]; // [0x890 RS, 0xf24 FR/LG, 0xf2c E] + public readonly byte[] Large = new byte[4 * SIZE_SECTOR_USED]; //3+[0xc40 RS, 0xee8 FR/LG, 0xf08 E] public readonly byte[] Storage = new byte[9 * SIZE_SECTOR_USED]; // [0x83D0] private readonly int ActiveSlot; @@ -64,9 +64,9 @@ public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37 var id = ReadInt16LittleEndian(data[(ofs + 0xFF4)..]); switch (id) { - case >=5: data.Slice(ofs, SIZE_SECTOR_USED).CopyTo(Storage.AsSpan((id - 5) * SIZE_SECTOR_USED)); break; - case >=1: data.Slice(ofs, SIZE_SECTOR_USED).CopyTo(Large .AsSpan((id - 1) * SIZE_SECTOR_USED)); break; - default: data.Slice(ofs, SIZE_SECTOR_USED).CopyTo(Small .AsSpan(0 )); break; + case >= 5: data.Slice(ofs, SIZE_SECTOR_USED).CopyTo(Storage.AsSpan((id - 5) * SIZE_SECTOR_USED)); break; + case >= 1: data.Slice(ofs, SIZE_SECTOR_USED).CopyTo(Large.AsSpan((id - 1) * SIZE_SECTOR_USED)); break; + default: data.Slice(ofs, SIZE_SECTOR_USED).CopyTo(Small.AsSpan(0)); break; } } } @@ -81,9 +81,9 @@ public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37 var id = ReadInt16LittleEndian(data[(ofs + 0xFF4)..]); switch (id) { - case >=5: Storage.AsSpan((id - 5) * SIZE_SECTOR_USED, SIZE_SECTOR_USED).CopyTo(data[ofs..]); break; - case >=1: Large .AsSpan((id - 1) * SIZE_SECTOR_USED, SIZE_SECTOR_USED).CopyTo(data[ofs..]); break; - default: Small .AsSpan(0 , SIZE_SECTOR_USED).CopyTo(data[ofs..]); break; + case >= 5: Storage.AsSpan((id - 5) * SIZE_SECTOR_USED, SIZE_SECTOR_USED).CopyTo(data[ofs..]); break; + case >= 1: Large.AsSpan((id - 1) * SIZE_SECTOR_USED, SIZE_SECTOR_USED).CopyTo(data[ofs..]); break; + default: Small.AsSpan(0, SIZE_SECTOR_USED).CopyTo(data[ofs..]); break; } } } @@ -176,8 +176,8 @@ public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37 /// New object. public SAV3 ForceLoad(GameVersion version) => version switch { - GameVersion.R or GameVersion.S or GameVersion.RS => new SAV3RS(Data), - GameVersion.E => new SAV3E(Data), + GameVersion.R or GameVersion.S or GameVersion.RS => new SAV3RS(Data), + GameVersion.E => new SAV3E(Data), GameVersion.FR or GameVersion.LG or GameVersion.FRLG => new SAV3FRLG(Data), _ => throw new ArgumentOutOfRangeException(nameof(version)), }; @@ -188,7 +188,6 @@ public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37 public sealed override int MaxEV => EffortValues.Max255; public sealed override byte Generation => 3; public sealed override EntityContext Context => EntityContext.Gen3; - protected sealed override int GiftCountMax => 1; public sealed override int MaxStringLengthOT => 7; public sealed override int MaxStringLengthNickname => 10; public sealed override int MaxMoney => 999999; @@ -284,7 +283,7 @@ public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37 for (int i = 0; i < COUNT_MAIN; i++) { if (!IsSectorValid(i)) - list.Add($"Sector {i} @ {i*SIZE_SECTOR:X5} invalid."); + list.Add($"Sector {i} @ {i * SIZE_SECTOR:X5} invalid."); } if (Data.Length > SaveUtil.SIZE_G3RAW) // don't check HoF for half-sizes @@ -392,8 +391,8 @@ public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37 public void SetWork(int index, ushort value) => WriteUInt16LittleEndian(Large.AsSpan(EventWork)[(index * 2)..], value); #endregion - public sealed override bool GetFlag(int offset, int bitIndex) => FlagUtil.GetFlag(Large, offset, bitIndex); - public sealed override void SetFlag(int offset, int bitIndex, bool value) => FlagUtil.SetFlag(Large, offset, bitIndex, value); + public sealed override bool GetFlag(int offset, int bitIndex) => GetFlag(Large, offset, bitIndex); + public sealed override void SetFlag(int offset, int bitIndex, bool value) => SetFlag(Large, offset, bitIndex, value); protected abstract int BadgeFlagStart { get; } public abstract uint Coin { get; set; } @@ -436,20 +435,21 @@ public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37 } protected abstract InventoryPouch3[] GetItems(); + protected abstract int PokeDex { get; } + public override bool HasPokeDex => true; + public int DaycareSlotCount => 2; protected abstract int DaycareSlotSize { get; } - - public sealed override uint? GetDaycareEXP(int loc, int slot) => ReadUInt32LittleEndian(Large.AsSpan(GetDaycareEXPOffset(slot))); - public sealed override void SetDaycareEXP(int loc, int slot, uint EXP) => WriteUInt32LittleEndian(Large.AsSpan(GetDaycareEXPOffset(slot)), EXP); - public sealed override bool? IsDaycareOccupied(int loc, int slot) => IsPKMPresent(Large.AsSpan(GetDaycareSlotOffset(loc, slot))); - public sealed override void SetDaycareOccupied(int loc, int slot, bool occupied) { /* todo */ } - public sealed override int GetDaycareSlotOffset(int loc, int slot) => DaycareOffset + (slot * DaycareSlotSize); - - protected abstract int EggEventFlag { get; } - public sealed override bool? IsDaycareHasEgg(int loc) => GetEventFlag(EggEventFlag); - public sealed override void SetDaycareHasEgg(int loc, bool hasEgg) => SetEventFlag(EggEventFlag, hasEgg); - + protected abstract int DaycareOffset { get; } protected abstract int GetDaycareEXPOffset(int slot); + public Memory GetDaycareSlot(int slot) => Large.AsMemory(GetDaycareSlotOffset(slot), DaycareSlotSize); + public uint GetDaycareEXP(int index) => ReadUInt32LittleEndian(Large.AsSpan(GetDaycareEXPOffset(index))); + public void SetDaycareEXP(int index, uint value) => WriteUInt32LittleEndian(Large.AsSpan(GetDaycareEXPOffset(index)), value); + public bool IsDaycareOccupied(int slot) => IsPKMPresent(Large.AsSpan(GetDaycareSlotOffset(slot))); + public void SetDaycareOccupied(int slot, bool occupied) { /* todo */ } + public int GetDaycareSlotOffset(int slot) => DaycareOffset + (slot * DaycareSlotSize); + protected abstract int EggEventFlag { get; } + public bool IsEggAvailable { get => GetEventFlag(EggEventFlag); set => SetEventFlag(EggEventFlag, value); } #region Storage public sealed override int GetBoxOffset(int box) => Box + 4 + (SIZE_STORED * box * COUNT_SLOTSPERBOX); @@ -460,7 +460,7 @@ public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37 set => Storage[0] = (byte)value; } - public sealed override int GetBoxWallpaper(int box) + public int GetBoxWallpaper(int box) { if (box > COUNT_BOX) return box; @@ -470,7 +470,7 @@ public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37 private const int COUNT_BOXNAME = 8 + 1; - public sealed override void SetBoxWallpaper(int box, int value) + public void SetBoxWallpaper(int box, int value) { if (box > COUNT_BOX) return; @@ -478,20 +478,20 @@ public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37 Storage[offset] = (byte)value; } - protected sealed override int GetBoxWallpaperOffset(int box) + protected int GetBoxWallpaperOffset(int box) { int offset = GetBoxOffset(COUNT_BOX); offset += (COUNT_BOX * COUNT_BOXNAME) + box; return offset; } - public sealed override string GetBoxName(int box) + public string GetBoxName(int box) { int offset = GetBoxOffset(COUNT_BOX); return StringConverter3.GetString(Storage.AsSpan(offset + (box * COUNT_BOXNAME), COUNT_BOXNAME), Japanese); } - public sealed override void SetBoxName(int box, ReadOnlySpan value) + public void SetBoxName(int box, ReadOnlySpan value) { int offset = GetBoxOffset(COUNT_BOX); var dest = Storage.AsSpan(offset + (box * COUNT_BOXNAME), COUNT_BOXNAME); diff --git a/PKHeX.Core/Saves/SAV3Colosseum.cs b/PKHeX.Core/Saves/SAV3Colosseum.cs index b7d7b88b2..015dccb2d 100644 --- a/PKHeX.Core/Saves/SAV3Colosseum.cs +++ b/PKHeX.Core/Saves/SAV3Colosseum.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Security.Cryptography; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -8,7 +7,7 @@ namespace PKHeX.Core; /// /// Generation 3 object for Pokémon Colosseum saves. /// -public sealed class SAV3Colosseum : SaveFile, IGCSaveFile +public sealed class SAV3Colosseum : SaveFile, IGCSaveFile, IBoxDetailName, IDaycareStorage, IDaycareExperience { protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public override string Extension => this.GCExtension(); @@ -16,6 +15,11 @@ public sealed class SAV3Colosseum : SaveFile, IGCSaveFile public override ReadOnlySpan HeldItems => Legal.HeldItems_RS; public SAV3GCMemoryCard? MemoryCard { get; init; } + private readonly Memory Raw; + private new Span Data => Raw.Span; + protected override Span BoxBuffer => Data; + protected override Span PartyBuffer => Data; + // 3 Save files are stored // 0x0000-0x6000 contains memory card data // 0x6000-0x60000 contains the 3 save slots @@ -24,25 +28,20 @@ public sealed class SAV3Colosseum : SaveFile, IGCSaveFile // Another SHA1 hash is 0x1DFD8, for 20 bytes. Unknown purpose. // Checksum is used as the crypto key. - private const int SLOT_SIZE = 0x1E000; - private const int SLOT_START = 0x6000; - private const int SLOT_COUNT = 3; - - private const int sha1HashSize = 20; - - private int SaveCount = -1; - private int SaveIndex = -1; + private readonly int SaveCount = -1; + private readonly int SaveIndex = -1; private readonly StrategyMemo StrategyMemo; public const int MaxShadowID = 0x80; // 128 private int Memo; + private int DaycareOffset; - private readonly byte[] BAK; private readonly bool Japanese; - public SAV3Colosseum(bool japanese = false) : base(SaveUtil.SIZE_G3COLO) + public SAV3Colosseum(bool japanese = false) { Japanese = japanese; - BAK = []; + + Raw = new byte[ColoCrypto.SLOT_SIZE]; StrategyMemo = Initialize(); ClearBoxes(); } @@ -50,8 +49,10 @@ public sealed class SAV3Colosseum : SaveFile, IGCSaveFile public SAV3Colosseum(byte[] data) : base(data) { Japanese = data[0] == 0x83; // Japanese game name first character - BAK = data; - InitializeData(); + + // Decrypt most recent save slot + (SaveIndex, SaveCount) = ColoCrypto.DetectLatest(data); + Raw = ColoCrypto.GetSlot(data.AsMemory(), SaveIndex); StrategyMemo = Initialize(); } @@ -69,41 +70,15 @@ public sealed class SAV3Colosseum : SaveFile, IGCSaveFile for (int i = 0; i < 6; i++) { var ofs = GetPartyOffset(i); - var span = Data.AsSpan(ofs); + var span = Data[ofs..]; if (ReadUInt16BigEndian(span) != 0) // species is at offset 0x00 PartyCount++; } - var memo = new StrategyMemo(Data.AsSpan(Memo), xd: false); + var memo = new StrategyMemo(Data[Memo..], xd: false); return memo; } - private void InitializeData() - { - // Scan all 3 save slots for the highest counter - for (int i = 0; i < SLOT_COUNT; i++) - { - int slotOffset = SLOT_START + (i * SLOT_SIZE); - int SaveCounter = ReadInt32BigEndian(Data.AsSpan(slotOffset + 4)); - if (SaveCounter <= SaveCount) - continue; - - SaveCount = SaveCounter; - SaveIndex = i; - } - - // Decrypt most recent save slot - { - int slotOffset = SLOT_START + (SaveIndex * SLOT_SIZE); - ReadOnlySpan slot = Data.AsSpan(slotOffset, SLOT_SIZE); - Span digest = stackalloc byte[sha1HashSize]; - slot[^sha1HashSize..].CopyTo(digest); - - // Decrypt Slot - Data = DecryptColosseum(slot, digest); - } - } - protected override byte[] GetFinalData() { var newFile = GetInnerData(); @@ -118,18 +93,13 @@ public sealed class SAV3Colosseum : SaveFile, IGCSaveFile private byte[] GetInnerData() { - StrategyMemo.Write().CopyTo(Data, Memo); + StrategyMemo.Write().CopyTo(Data[Memo..]); SetChecksums(); - // Get updated save slot data - ReadOnlySpan slot = Data; - Span digest = stackalloc byte[sha1HashSize]; - slot[^sha1HashSize..].CopyTo(digest); - byte[] newSAV = EncryptColosseum(slot, digest); - // Put save slot back in original save data - byte[] newFile = MemoryCard != null ? MemoryCard.ReadSaveGameData().ToArray() : (byte[])BAK.Clone(); - Array.Copy(newSAV, 0, newFile, SLOT_START + (SaveIndex * SLOT_SIZE), newSAV.Length); + byte[] newFile = (byte[])base.Data.Clone(); + ColoCrypto.SetSlot(newFile, SaveIndex, Data); + return newFile; } @@ -151,7 +121,6 @@ public sealed class SAV3Colosseum : SaveFile, IGCSaveFile public override int MaxEV => EffortValues.Max255; public override byte Generation => 3; public override EntityContext Context => EntityContext.Gen3; - protected override int GiftCountMax => 1; public override int MaxStringLengthOT => 10; // as evident by Mattle Ho-Oh public override int MaxStringLengthNickname => 10; public override int MaxMoney => 9999999; @@ -159,109 +128,14 @@ public sealed class SAV3Colosseum : SaveFile, IGCSaveFile public override int BoxCount => 3; public override bool IsPKMPresent(ReadOnlySpan data) => EntityDetection.IsPresentGC(data); - private static byte[] EncryptColosseum(ReadOnlySpan input, Span digest) - { - ArgumentOutOfRangeException.ThrowIfNotEqual(input.Length, SLOT_SIZE); - - byte[] output = input.ToArray(); - - // NOT key - for (int i = 0; i < digest.Length; i++) - digest[i] = (byte)~digest[i]; - - const int start = 0x18; - const int end = (SLOT_SIZE - (2 * sha1HashSize)); - var crypt = output.AsSpan(); - for (int i = start; i < end; i += sha1HashSize) - { - var slice = crypt.Slice(i, digest.Length); - for (int j = 0; j < digest.Length; j++) - slice[j] ^= digest[j]; - SHA1.HashData(slice, digest); // update digest - } - return output; - } - - private static byte[] DecryptColosseum(ReadOnlySpan input, Span digest) - { - ArgumentOutOfRangeException.ThrowIfNotEqual(input.Length, SLOT_SIZE); - - byte[] output = input.ToArray(); - - // NOT key - for (int i = 0; i < digest.Length; i++) - digest[i] = (byte)~digest[i]; - - Span hash = stackalloc byte[sha1HashSize]; - const int start = 0x18; - const int end = (SLOT_SIZE - (2 * sha1HashSize)); - var crypt = output.AsSpan(); - for (int i = start; i < end; i += sha1HashSize) - { - var slice = crypt.Slice(i, Math.Min(crypt.Length - i, sha1HashSize)); - SHA1.HashData(slice, hash); // update digest - for (int j = 0; j < slice.Length; j++) - slice[j] ^= digest[j]; - hash.CopyTo(digest); // for use in next loop - } - return output; - } - - protected override void SetChecksums() - { - var data = Data.AsSpan(); - var header = data[..0x20]; - var payload = data[..(SLOT_SIZE - (2 * sha1HashSize))]; - var hash = data[^sha1HashSize..]; - var headerCHK = data[0x0C..]; - - // Clear Header Checksum - WriteInt32BigEndian(headerCHK, 0); - // Compute checksum of data - SHA1.HashData(payload, hash); - - // Compute new header checksum - int newHC = ComputeHeaderChecksum(header, hash); - - // Set Header Checksum - WriteInt32BigEndian(headerCHK, newHC); - } - - private static int ComputeHeaderChecksum(Span header, Span hash) - { - int result = 0; - for (int i = 0; i < 0x18; i += 4) - result -= ReadInt32BigEndian(header[i..]); - result -= ReadInt32BigEndian(header[0x18..]) ^ ~ReadInt32BigEndian(hash); - result -= ReadInt32BigEndian(header[0x1C..]) ^ ~ReadInt32BigEndian(hash[4..]); - return result; - } - + protected override void SetChecksums() => ColoCrypto.SetChecksums(Data); public override bool ChecksumsValid => !ChecksumInfo.Contains("Invalid"); public override string ChecksumInfo { get { - var data = Data.AsSpan(); - var header = data[..0x20]; - var payload = data[..(SLOT_SIZE - (2 * sha1HashSize))]; - var storedHash = data[^sha1HashSize..]; - var hc = header[0x0C..]; - - int oldHC = ReadInt32BigEndian(hc); - // Clear Header Checksum - WriteInt32BigEndian(hc, 0); - Span currentHash = stackalloc byte[sha1HashSize]; - SHA1.HashData(payload, currentHash); - - // Compute new header checksum - int newHC = ComputeHeaderChecksum(header, currentHash); - - // Restore old header checksum - WriteInt32BigEndian(hc, oldHC); - bool isHeaderValid = newHC == oldHC; - bool isBodyValid = storedHash.SequenceEqual(currentHash); + (bool isHeaderValid, bool isBodyValid) = ColoCrypto.IsChecksumValid(Data); static string valid(bool s) => s ? "Valid" : "Invalid"; return $"Header Checksum {valid(isHeaderValid)}, Body Checksum {valid(isBodyValid)}."; } @@ -281,14 +155,14 @@ public sealed class SAV3Colosseum : SaveFile, IGCSaveFile return Box + (((30 * SIZE_STORED) + 0x14)*box) + 0x14; } - private Span GetBoxNameSpan(int box) => Data.AsSpan(Box + (0x24A4 * box), 16); + private Span GetBoxNameSpan(int box) => Data.Slice(Box + (0x24A4 * box), 16); - public override string GetBoxName(int box) + public string GetBoxName(int box) { return GetString(GetBoxNameSpan(box)); } - public override void SetBoxName(int box, ReadOnlySpan value) + public void SetBoxName(int box, ReadOnlySpan value) { SetString(GetBoxNameSpan(box), value, 8, StringConverterOption.ClearZero); } @@ -347,8 +221,8 @@ public sealed class SAV3Colosseum : SaveFile, IGCSaveFile private TimeSpan PlayedSpan { - get => TimeSpan.FromSeconds(ReadSingleBigEndian(Data.AsSpan(Config + 0x20))); - set => WriteSingleBigEndian(Data.AsSpan(Config + 0x20), (float)value.TotalSeconds); + get => TimeSpan.FromSeconds(ReadSingleBigEndian(Data[(Config + 0x20)..])); + set => WriteSingleBigEndian(Data[(Config + 0x20)..], (float)value.TotalSeconds); } public override int PlayedHours @@ -370,17 +244,17 @@ public sealed class SAV3Colosseum : SaveFile, IGCSaveFile } // Trainer Info (offset 0x78, length 0xB18, end @ 0xB90) - public override string OT { get => GetString(Data.AsSpan(0x78, 20)); set { SetString(Data.AsSpan(0x78, 20), value, 10, StringConverterOption.ClearZero); OT2 = value; } } - public string OT2 { get => GetString(Data.AsSpan(0x8C, 20)); set => SetString(Data.AsSpan(0x8C, 20), value, 10, StringConverterOption.ClearZero); } + public override string OT { get => GetString(Data.Slice(0x78, 20)); set { SetString(Data.Slice(0x78, 20), value, 10, StringConverterOption.ClearZero); OT2 = value; } } + public string OT2 { get => GetString(Data.Slice(0x8C, 20)); set => SetString(Data.Slice(0x8C, 20), value, 10, StringConverterOption.ClearZero); } - public override uint ID32 { get => ReadUInt32BigEndian(Data.AsSpan(0xA4)); set => WriteUInt32BigEndian(Data.AsSpan(0xA4), value); } - public override ushort SID16 { get => ReadUInt16BigEndian(Data.AsSpan(0xA4)); set => WriteUInt16BigEndian(Data.AsSpan(0xA4), value); } - public override ushort TID16 { get => ReadUInt16BigEndian(Data.AsSpan(0xA6)); set => WriteUInt16BigEndian(Data.AsSpan(0xA6), value); } + public override uint ID32 { get => ReadUInt32BigEndian(Data[0xA4..]); set => WriteUInt32BigEndian(Data[0xA4..], value); } + public override ushort SID16 { get => ReadUInt16BigEndian(Data[0xA4..]); set => WriteUInt16BigEndian(Data[0xA4..], value); } + public override ushort TID16 { get => ReadUInt16BigEndian(Data[0xA6..]); set => WriteUInt16BigEndian(Data[0xA6..], value); } public override byte Gender { get => Data[0xAF8]; set => Data[0xAF8] = value; } - public override uint Money { get => ReadUInt32BigEndian(Data.AsSpan(0xAFC)); set => WriteUInt32BigEndian(Data.AsSpan(0xAFC), value); } - public uint Coupons { get => ReadUInt32BigEndian(Data.AsSpan(0xB00)); set => WriteUInt32BigEndian(Data.AsSpan(0xB00), value); } - public string RUI_Name { get => GetString(Data.AsSpan(0xB3A, 20)); set => SetString(Data.AsSpan(0xB3A, 20), value, 10, StringConverterOption.ClearZero); } + public override uint Money { get => ReadUInt32BigEndian(Data[0xAFC..]); set => WriteUInt32BigEndian(Data[0xAFC..], value); } + public uint Coupons { get => ReadUInt32BigEndian(Data[0xB00..]); set => WriteUInt32BigEndian(Data[0xB00..], value); } + public string RUI_Name { get => GetString(Data.Slice(0xB3A, 20)); set => SetString(Data.Slice(0xB3A, 20), value, 10, StringConverterOption.ClearZero); } public override IReadOnlyList Inventory { @@ -406,11 +280,13 @@ public sealed class SAV3Colosseum : SaveFile, IGCSaveFile // 0x01 -- Deposited Level // 0x02-0x03 -- unused? // 0x04-0x07 -- Initial EXP - public override int GetDaycareSlotOffset(int loc, int slot) { return DaycareOffset + 8; } - public override uint? GetDaycareEXP(int loc, int slot) { return null; } - public override bool? IsDaycareOccupied(int loc, int slot) { return null; } - public override void SetDaycareEXP(int loc, int slot, uint EXP) { } - public override void SetDaycareOccupied(int loc, int slot, bool occupied) { } + public int DaycareSlotCount => 1; + public bool IsDaycareOccupied(int slot) => Data[DaycareOffset] != 0; + public void SetDaycareOccupied(int slot, bool occupied) => Data[DaycareOffset] = (byte)(occupied ? 1 : 0); + public byte DaycareDepositLevel { get => Data[DaycareOffset + 1]; set => Data[DaycareOffset + 1] = value; } + public uint GetDaycareEXP(int index) => ReadUInt32BigEndian(Data[(DaycareOffset + 4)..]); + public void SetDaycareEXP(int index, uint value) => WriteUInt32BigEndian(Data[(DaycareOffset + 4)..], value); + public Memory GetDaycareSlot(int slot) => Raw.Slice(DaycareOffset + 8, PokeCrypto.SIZE_3CSTORED); public override string GetString(ReadOnlySpan data) => StringConverter3GC.GetString(data); diff --git a/PKHeX.Core/Saves/SAV3E.cs b/PKHeX.Core/Saves/SAV3E.cs index 8ac898c17..26e7729e3 100644 --- a/PKHeX.Core/Saves/SAV3E.cs +++ b/PKHeX.Core/Saves/SAV3E.cs @@ -8,7 +8,7 @@ namespace PKHeX.Core; /// Generation 3 object for . /// /// -public sealed class SAV3E : SAV3, IGen3Hoenn, IGen3Joyful, IGen3Wonder +public sealed class SAV3E : SAV3, IGen3Hoenn, IGen3Joyful, IGen3Wonder, IDaycareRandomState { // Configuration protected override SAV3E CloneInternal() => new(Write()); @@ -18,7 +18,6 @@ public sealed class SAV3E : SAV3, IGen3Hoenn, IGen3Joyful, IGen3Wonder public override int EventFlagCount => 8 * 300; public override int EventWorkCount => 0x100; protected override int DaycareSlotSize => SIZE_STORED + 0x3C; // 0x38 mail + 4 exp - public override int DaycareSeedSize => 8; // 32bit protected override int EggEventFlag => 0x86; protected override int BadgeFlagStart => 0x867; @@ -29,17 +28,11 @@ public sealed class SAV3E : SAV3, IGen3Hoenn, IGen3Joyful, IGen3Wonder protected override int EventWork => 0x139C; public override int MaxItemID => Legal.MaxItemID_3_E; - private void Initialize() - { - // small - PokeDex = 0x18; + protected override int PokeDex => 0x18; // small + protected override int DaycareOffset => 0x3030; // large - // large - DaycareOffset = 0x3030; - - // storage - Box = 0; - } + // storage + private void Initialize() => Box = 0; #region Small public override bool NationalDex @@ -183,9 +176,12 @@ public sealed class SAV3E : SAV3, IGen3Hoenn, IGen3Joyful, IGen3Wonder protected override int MailOffset => 0x2BE0; - protected override int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(0, slot + 1) - 4; // @ end of each pk slot - public override string GetDaycareRNGSeed(int loc) => ReadUInt32LittleEndian(Large.AsSpan(GetDaycareSlotOffset(0, 2))).ToString("X8"); // after the 2 slots, before the step counter - public override void SetDaycareRNGSeed(int loc, string seed) => WriteUInt32LittleEndian(Large.AsSpan(GetDaycareEXPOffset(2)), Util.GetHexValue(seed)); + protected override int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(slot + 1) - 4; // @ end of each pk slot + uint IDaycareRandomState.Seed // after the 2 slots, before the step counter + { + get => ReadUInt32LittleEndian(Large.AsSpan(GetDaycareEXPOffset(2))); + set => WriteUInt32LittleEndian(Large.AsSpan(GetDaycareEXPOffset(2)), value); + } protected override int ExternalEventData => 0x31B3; @@ -224,12 +220,12 @@ public sealed class SAV3E : SAV3, IGen3Hoenn, IGen3Joyful, IGen3Wonder /** Each value unit represents 1/60th of a second. Value 0 if no record. */ public uint GetTrainerHillRecord(TrainerHillMode3E mode) { - return ReadUInt32LittleEndian(Large.AsSpan(OFS_TrainerHillRecord + (byte)mode * 4)); + return ReadUInt32LittleEndian(Large.AsSpan(OFS_TrainerHillRecord + ((byte)mode * 4))); } public void SetTrainerHillRecord(TrainerHillMode3E mode, uint value) { - WriteUInt32LittleEndian(Large.AsSpan(OFS_TrainerHillRecord + (byte)mode * 4), value); + WriteUInt32LittleEndian(Large.AsSpan(OFS_TrainerHillRecord + ((byte)mode * 4)), value); State.Edited = true; } diff --git a/PKHeX.Core/Saves/SAV3FRLG.cs b/PKHeX.Core/Saves/SAV3FRLG.cs index 20f3c82d8..5d208cc4c 100644 --- a/PKHeX.Core/Saves/SAV3FRLG.cs +++ b/PKHeX.Core/Saves/SAV3FRLG.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// Generation 5 object for . /// /// -public sealed class SAV3FRLG : SAV3, IGen3Joyful, IGen3Wonder +public sealed class SAV3FRLG : SAV3, IGen3Joyful, IGen3Wonder, IDaycareRandomState { // Configuration protected override SAV3FRLG CloneInternal() => new(Write()); @@ -18,7 +18,6 @@ public sealed class SAV3FRLG : SAV3, IGen3Joyful, IGen3Wonder public override int EventFlagCount => 8 * 288; public override int EventWorkCount => 0x100; protected override int DaycareSlotSize => SIZE_STORED + 0x3C; // 0x38 mail + 4 exp - public override int DaycareSeedSize => 4; // 16bit protected override int EggEventFlag => 0x266; protected override int BadgeFlagStart => 0x820; @@ -36,18 +35,11 @@ public sealed class SAV3FRLG : SAV3, IGen3Joyful, IGen3Wonder protected override int EventFlag => 0xEE0; protected override int EventWork => 0x1000; + protected override int PokeDex => 0x18; // small + protected override int DaycareOffset => 0x2F80; // large - private void Initialize() - { - // small - PokeDex = 0x18; - - // large - DaycareOffset = 0x2F80; - - // storage - Box = 0; - } + // storage + private void Initialize() => Box = 0; public bool ResetPersonal(GameVersion g) { @@ -135,9 +127,12 @@ public sealed class SAV3FRLG : SAV3, IGen3Joyful, IGen3Wonder protected override int SeenOffset2 => 0x5F8; protected override int MailOffset => 0x2CD0; - protected override int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(0, slot + 1) - 4; // @ end of each pk slot - public override string GetDaycareRNGSeed(int loc) => ReadUInt16LittleEndian(Large.AsSpan(GetDaycareEXPOffset(2))).ToString("X4"); // after the 2nd slot EXP, before the step counter - public override void SetDaycareRNGSeed(int loc, string seed) => WriteUInt16LittleEndian(Large.AsSpan(GetDaycareEXPOffset(2)), (ushort)Util.GetHexValue(seed)); + protected override int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(slot + 1) - 4; // @ end of each pk slot + ushort IDaycareRandomState.Seed + { + get => ReadUInt16LittleEndian(Large.AsSpan(GetDaycareEXPOffset(2))); + set => WriteUInt16LittleEndian(Large.AsSpan(GetDaycareEXPOffset(2)), value); + } protected override int ExternalEventData => 0x30A7; diff --git a/PKHeX.Core/Saves/SAV3GCMemoryCard.cs b/PKHeX.Core/Saves/SAV3GCMemoryCard.cs index 0ec05fb00..867f71ac2 100644 --- a/PKHeX.Core/Saves/SAV3GCMemoryCard.cs +++ b/PKHeX.Core/Saves/SAV3GCMemoryCard.cs @@ -201,11 +201,12 @@ public sealed class SAV3GCMemoryCard(byte[] Data) public GCMemoryCardState GetMemoryCardState() { - if (!IsMemoryCardSize(Data)) + ReadOnlySpan data = Data; + if (!IsMemoryCardSize(data)) return GCMemoryCardState.Invalid; // Invalid size // Size in megabits, not megabytes - int m_sizeMb = Data.Length / BLOCK_SIZE / MBIT_TO_BLOCKS; + int m_sizeMb = data.Length / BLOCK_SIZE / MBIT_TO_BLOCKS; if (m_sizeMb != Header_Size) // Memory card file size does not match the header size return GCMemoryCardState.Invalid; @@ -222,18 +223,18 @@ public sealed class SAV3GCMemoryCard(byte[] Data) for (int i = 0; i < NumEntries_Directory; i++) { int offset = (DirectoryBlock_Used * BLOCK_SIZE) + (i * DENTRY_SIZE); - if (ReadUInt32BigEndian(Data.AsSpan(offset)) == uint.MaxValue) // empty entry + if (ReadUInt32BigEndian(data[offset..]) == uint.MaxValue) // empty entry continue; - int FirstBlock = ReadUInt16BigEndian(Data.AsSpan(offset + 0x36)); + int FirstBlock = ReadUInt16BigEndian(data[0x36..]); if (FirstBlock < 5) continue; - int BlockCount = ReadUInt16BigEndian(Data.AsSpan(offset + 0x38)); + int BlockCount = ReadUInt16BigEndian(data[0x38..]); var dataEnd = (FirstBlock + BlockCount) * BLOCK_SIZE; // Memory card directory contains info for deleted files with boundaries beyond memory card size, ignore - if (dataEnd > Data.Length) + if (dataEnd > data.Length) continue; var header = Data.AsSpan(offset, 4); diff --git a/PKHeX.Core/Saves/SAV3RS.cs b/PKHeX.Core/Saves/SAV3RS.cs index 7956cc545..f1f5965cd 100644 --- a/PKHeX.Core/Saves/SAV3RS.cs +++ b/PKHeX.Core/Saves/SAV3RS.cs @@ -8,7 +8,7 @@ namespace PKHeX.Core; /// Generation 3 object for . /// /// -public sealed class SAV3RS : SAV3, IGen3Hoenn +public sealed class SAV3RS : SAV3, IGen3Hoenn, IDaycareRandomState { // Configuration protected override SAV3RS CloneInternal() => new(Write()); @@ -18,7 +18,6 @@ public sealed class SAV3RS : SAV3, IGen3Hoenn public override int EventFlagCount => 8 * 288; public override int EventWorkCount => 0x100; protected override int DaycareSlotSize => SIZE_STORED; - public override int DaycareSeedSize => 4; // 16bit protected override int EggEventFlag => 0x86; protected override int BadgeFlagStart => 0x807; @@ -28,17 +27,11 @@ public sealed class SAV3RS : SAV3, IGen3Hoenn protected override int EventFlag => 0x1220; protected override int EventWork => 0x1340; - private void Initialize() - { - // small - PokeDex = 0x18; + protected override int PokeDex => 0x18; // small + protected override int DaycareOffset => 0x2F9C; // large - // large - DaycareOffset = 0x2F9C; - - // storage - Box = 0; - } + // storage + private void Initialize() => Box = 0; #region Small public override bool NationalDex @@ -142,9 +135,12 @@ public sealed class SAV3RS : SAV3, IGen3Hoenn protected override int MailOffset => 0x2B4C; - protected override int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(0, 2) + (2 * 0x38) + (4 * slot); // consecutive vals, after both consecutive slots & 2 mail - public override string GetDaycareRNGSeed(int loc) => ReadUInt16LittleEndian(Large.AsSpan(GetDaycareEXPOffset(2))).ToString("X4"); - public override void SetDaycareRNGSeed(int loc, string seed) => WriteUInt16LittleEndian(Large.AsSpan(GetDaycareEXPOffset(2)), (ushort)Util.GetHexValue(seed)); + protected override int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(2) + (2 * 0x38) + (4 * slot); // consecutive vals, after both consecutive slots & 2 mail + ushort IDaycareRandomState.Seed + { + get => ReadUInt16LittleEndian(Large.AsSpan(GetDaycareEXPOffset(2))); + set => WriteUInt16LittleEndian(Large.AsSpan(GetDaycareEXPOffset(2)), value); + } protected override int ExternalEventData => 0x311B; diff --git a/PKHeX.Core/Saves/SAV3RSBox.cs b/PKHeX.Core/Saves/SAV3RSBox.cs index 1c590a894..17074bfe6 100644 --- a/PKHeX.Core/Saves/SAV3RSBox.cs +++ b/PKHeX.Core/Saves/SAV3RSBox.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// /// Generation 3 object for Pokémon Ruby Sapphire Box saves. /// -public sealed class SAV3RSBox : SaveFile, IGCSaveFile +public sealed class SAV3RSBox : SaveFile, IGCSaveFile, IBoxDetailName, IBoxDetailWallpaper { protected internal override string ShortSummary => $"{Version} #{SaveCount:0000}"; public override string Extension => this.GCExtension(); @@ -115,11 +115,9 @@ public sealed class SAV3RSBox : SaveFile, IGCSaveFile public override int MaxEV => EffortValues.Max255; public override byte Generation => 3; public override EntityContext Context => EntityContext.Gen3; - protected override int GiftCountMax => 1; public override int MaxStringLengthOT => 7; public override int MaxStringLengthNickname => 10; public override int MaxMoney => 999999; - public override bool HasBoxWallpapers => false; public override int BoxCount => 50; public override bool HasParty => false; @@ -149,13 +147,16 @@ public sealed class SAV3RSBox : SaveFile, IGCSaveFile return Data.AsSpan(offset, 9); } - protected override int GetBoxWallpaperOffset(int box) + private int GetBoxWallpaperOffset(int box) { // Box Wallpaper is directly after the Box Names return Box + 0x1ED19 + (box / 2); } - public override string GetBoxName(int box) + public int GetBoxWallpaper(int box) => Data[GetBoxWallpaperOffset(box)]; + public void SetBoxWallpaper(int box, int value) => Data[GetBoxWallpaperOffset(box)] = (byte)value; + + public string GetBoxName(int box) { // Tweaked for the 1-30/31-60 box showing int lo = (30 *(box%2)) + 1; @@ -165,17 +166,17 @@ public sealed class SAV3RSBox : SaveFile, IGCSaveFile var span = GetBoxNameSpan(box); if (span[0] is 0 or 0xFF) - boxName += $"BOX {box + 1}"; + boxName += BoxDetailNameExtensions.GetDefaultBoxNameCaps(box); else boxName += GetString(span); return boxName; } - public override void SetBoxName(int box, ReadOnlySpan value) + public void SetBoxName(int box, ReadOnlySpan value) { var span = GetBoxNameSpan(box); - if (value == $"BOX {box + 1}") + if (value == BoxDetailNameExtensions.GetDefaultBoxNameCaps(box)) { span.Clear(); return; diff --git a/PKHeX.Core/Saves/SAV3XD.cs b/PKHeX.Core/Saves/SAV3XD.cs index 194622709..7f6a35213 100644 --- a/PKHeX.Core/Saves/SAV3XD.cs +++ b/PKHeX.Core/Saves/SAV3XD.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// /// Generation 3 object for Pokémon XD saves. /// -public sealed class SAV3XD : SaveFile, IGCSaveFile +public sealed class SAV3XD : SaveFile, IGCSaveFile, IBoxDetailName, IDaycareStorage, IDaycareExperience { protected internal override string ShortSummary => $"{OT} ({Version}) {PlayTimeString}"; public override string Extension => this.GCExtension(); @@ -29,6 +29,7 @@ public sealed class SAV3XD : SaveFile, IGCSaveFile private int OFS_PouchHeldItem, OFS_PouchKeyItem, OFS_PouchBalls, OFS_PouchTMHM, OFS_PouchBerry, OFS_PouchCologne, OFS_PouchDisc; private readonly int[] subOffsets = new int[16]; private readonly byte[] BAK; + private int DaycareOffset; public SAV3XD() : base(SaveUtil.SIZE_G3XD) { @@ -191,7 +192,6 @@ public sealed class SAV3XD : SaveFile, IGCSaveFile public override int MaxEV => EffortValues.Max255; public override byte Generation => 3; public override EntityContext Context => EntityContext.Gen3; - protected override int GiftCountMax => 1; public override int MaxStringLengthOT => 7; public override int MaxStringLengthNickname => 10; public override int MaxMoney => 9999999; @@ -334,9 +334,9 @@ public sealed class SAV3XD : SaveFile, IGCSaveFile public override int GetPartyOffset(int slot) => Party + (SIZE_STORED * slot); private int GetBoxInfoOffset(int box) => Box + (((30 * SIZE_STORED) + 0x14) * box); public override int GetBoxOffset(int box) => GetBoxInfoOffset(box) + 20; - public override string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxInfoOffset(box), 16)); + public string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxInfoOffset(box), 16)); - public override void SetBoxName(int box, ReadOnlySpan value) + public void SetBoxName(int box, ReadOnlySpan value) { SetString(Data.AsSpan(GetBoxInfoOffset(box), 20), value, 8, StringConverterOption.ClearZero); } @@ -444,11 +444,13 @@ public sealed class SAV3XD : SaveFile, IGCSaveFile // 0x01 -- Deposited Level // 0x02-0x03 -- unused? // 0x04-0x07 -- Initial EXP - public override int GetDaycareSlotOffset(int loc, int slot) { return DaycareOffset + 8; } - public override uint? GetDaycareEXP(int loc, int slot) { return null; } - public override bool? IsDaycareOccupied(int loc, int slot) { return null; } - public override void SetDaycareEXP(int loc, int slot, uint EXP) { /* todo */ } - public override void SetDaycareOccupied(int loc, int slot, bool occupied) { /* todo */ } + public int DaycareSlotCount => 1; + public bool IsDaycareOccupied(int slot) => Data[DaycareOffset] != 0; + public void SetDaycareOccupied(int slot, bool occupied) => Data[DaycareOffset] = (byte)(occupied ? 1 : 0); + public byte DaycareDepositLevel { get => Data[DaycareOffset + 1]; set => Data[DaycareOffset + 1] = value; } + public uint GetDaycareEXP(int index) => ReadUInt32BigEndian(Data.AsSpan(DaycareOffset + 4)); + public void SetDaycareEXP(int index, uint value) => WriteUInt32BigEndian(Data.AsSpan(DaycareOffset + 4), value); + public Memory GetDaycareSlot(int slot) => Data.AsMemory(DaycareOffset + 8, PokeCrypto.SIZE_3XSTORED); public override string GetString(ReadOnlySpan data) => StringConverter3GC.GetString(data); diff --git a/PKHeX.Core/Saves/SAV4.cs b/PKHeX.Core/Saves/SAV4.cs index b5ac05ceb..240972885 100644 --- a/PKHeX.Core/Saves/SAV4.cs +++ b/PKHeX.Core/Saves/SAV4.cs @@ -11,7 +11,7 @@ namespace PKHeX.Core; /// /// Storage data is stored in one contiguous block, and the remaining data is stored in another block. /// -public abstract class SAV4 : SaveFile, IEventFlag37 +public abstract class SAV4 : SaveFile, IEventFlag37, IDaycareStorage, IDaycareRandomState, IDaycareExperience, IDaycareEggState, IMysteryGiftStorageProvider { protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public sealed override string Extension => ".sav"; @@ -37,8 +37,8 @@ public abstract class SAV4 : SaveFile, IEventFlag37 protected abstract int EventFlag { get; } protected abstract int EventWork { get; } - public sealed override bool GetFlag(int offset, int bitIndex) => FlagUtil.GetFlag(General, offset, bitIndex); - public sealed override void SetFlag(int offset, int bitIndex, bool value) => FlagUtil.SetFlag(General, offset, bitIndex, value); + public sealed override bool GetFlag(int offset, int bitIndex) => GetFlag(General, offset, bitIndex); + public sealed override void SetFlag(int offset, int bitIndex, bool value) => SetFlag(General, offset, bitIndex, value); protected SAV4([ConstantExpected] int gSize, [ConstantExpected] int sSize) { @@ -95,7 +95,6 @@ public abstract class SAV4 : SaveFile, IEventFlag37 public override EntityContext Context => EntityContext.Gen4; public int EventFlagCount => 0xB60; // 2912 public int EventWorkCount => (EventFlag - EventWork) >> 1; - protected sealed override int GiftCountMax => 11; public sealed override int MaxStringLengthOT => 7; public sealed override int MaxStringLengthNickname => 10; public sealed override int MaxMoney => 999999; @@ -213,7 +212,7 @@ public abstract class SAV4 : SaveFile, IEventFlag37 public int GTS { get; protected set; } = int.MinValue; public int ChatterOffset { get; protected set; } = int.MinValue; - public Chatter4 Chatter => new(this, ChatterOffset); + public Chatter4 Chatter => new(this, Data.AsMemory(ChatterOffset)); // Storage public override int PartyCount @@ -352,172 +351,57 @@ public abstract class SAV4 : SaveFile, IEventFlag37 } // Daycare - public override int GetDaycareSlotOffset(int loc, int slot) => DaycareOffset + (slot * SIZE_PARTY); + public int DaycareSlotCount => 2; + private const int DaycareSlotSize = PokeCrypto.SIZE_4PARTY; + protected abstract int DaycareOffset { get; } + public Memory GetDaycareSlot(int slot) => GeneralBuffer.Slice(DaycareOffset + (slot * DaycareSlotSize), DaycareSlotSize); - public override uint? GetDaycareEXP(int loc, int slot) + // EXP: Last 4 bytes of each slot + public uint GetDaycareEXP(int index) { - int ofs = DaycareOffset + ((slot+1)*SIZE_PARTY) - 4; + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, 2u); + int ofs = DaycareOffset + DaycareSlotSize - 4; return ReadUInt32LittleEndian(General[ofs..]); } - public override bool? IsDaycareOccupied(int loc, int slot) => null; // todo - - public override void SetDaycareEXP(int loc, int slot, uint EXP) + public void SetDaycareEXP(int index, uint value) { - int ofs = DaycareOffset + ((slot+1)*SIZE_PARTY) - 4; - WriteUInt32LittleEndian(General[ofs..], EXP); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, 2u); + int ofs = DaycareOffset + DaycareSlotSize - 4; + WriteUInt32LittleEndian(General[ofs..], value); } - public override void SetDaycareOccupied(int loc, int slot, bool occupied) + public bool IsDaycareOccupied(int index) => GetStoredSlot(GetDaycareSlot(index).Span).Species != 0; + + public void SetDaycareOccupied(int index, bool occupied) { - // todo + if (occupied) + return; // how would we even set this? just ignore and assume they'll set the slot data via other means. + GetDaycareSlot(index).Span.Clear(); + } + + uint IDaycareRandomState.Seed + { + get => ReadUInt32LittleEndian(General[(DaycareOffset + (2 * DaycareSlotSize))..]); + set => WriteUInt32LittleEndian(General[DaycareOffset..], value); + } + + public byte DaycareStepCounter { get => General[DaycareOffset + (2 * DaycareSlotSize) + 4]; set => General[DaycareOffset + (2 * DaycareSlotSize) + 4] = value; } + + public bool IsEggAvailable + { + get => ((IDaycareRandomState)this).Seed != 0; + set + { + if (!value) + ((IDaycareRandomState)this).Seed = 0; + else if (((IDaycareRandomState)this).Seed == 0) + ((IDaycareRandomState)this).Seed = (uint)Util.Rand.Next(1, int.MaxValue); + } } // Mystery Gift - private bool MysteryGiftActive { get => (General[72] & 1) == 1; set => General[72] = (byte)((General[72] & 0xFE) | (value ? 1 : 0)); } - - private static bool IsMysteryGiftAvailable(DataMysteryGift[] value) - { - for (int i = 0; i < 8; i++) // 8 PGT - { - if (value[i] is PGT {CardType: not 0}) - return true; - } - for (int i = 8; i < 11; i++) // 3 PCD - { - if (value[i] is PCD {Gift.CardType: not 0 }) - return true; - } - return false; - } - - private bool MatchMysteryGifts(DataMysteryGift[] value, Span indexes) - { - for (int i = 0; i < 8; i++) - { - if (value[i] is not PGT pgt) - continue; - - if (pgt.CardType == 0) // empty - { - indexes[i] = pgt.Slot = 0; - continue; - } - - indexes[i] = pgt.Slot = 3; - for (byte j = 0; j < 3; j++) - { - if (value[8 + j] is not PCD pcd) - continue; - - // Check if data matches (except Slot @ 0x02) - if (!pcd.GiftEquals(pgt)) - continue; - - if (this is not SAV4HGSS) - j++; // HG/SS 0,1,2; D/P/Pt 1,2,3 - indexes[i] = pgt.Slot = j; - break; - } - } - return true; - } - - public override MysteryGiftAlbum GiftAlbum - { - get => new(MysteryGiftCards, MysteryGiftReceivedFlags) {Flags = {[2047] = false}}; - set - { - bool available = IsMysteryGiftAvailable(value.Gifts); - if (available && !MysteryGiftActive) - MysteryGiftActive = true; - value.Flags[2047] = available; - - // Check encryption for each gift (decrypted wc4 sneaking in) - foreach (var g in value.Gifts) - { - if (g is PGT pgt) - { - pgt.VerifyPKEncryption(); - } - else if (g is PCD pcd) - { - var dg = pcd.Gift; - if (dg.VerifyPKEncryption()) - pcd.Gift = dg; // set encrypted gift back to PCD. - } - } - - MysteryGiftReceivedFlags = value.Flags; - MysteryGiftCards = value.Gifts; - } - } - - protected sealed override bool[] MysteryGiftReceivedFlags - { - get - { - bool[] result = new bool[GiftFlagMax]; - for (int i = 0; i < result.Length; i++) - result[i] = ((General[WondercardFlags + (i >> 3)] >> (i & 7)) & 0x1) == 1; - return result; - } - set - { - if (GiftFlagMax != value.Length) - return; - - Span data = General.Slice(WondercardFlags, value.Length / 8); - data.Clear(); - for (int i = 0; i < value.Length; i++) - { - if (value[i]) - data[i >> 3] |= (byte)(1 << (i & 7)); - } - } - } - - protected sealed override DataMysteryGift[] MysteryGiftCards - { - get - { - int pcd = this is SAV4HGSS ? 4 : 3; - DataMysteryGift[] cards = new DataMysteryGift[8 + pcd]; - for (int i = 0; i < 8; i++) // 8 PGT - cards[i] = new PGT(General.Slice(WondercardData + (i * PGT.Size), PGT.Size).ToArray()); - for (int i = 8; i < 11; i++) // 3 PCD - cards[i] = new PCD(General.Slice(WondercardData + (8 * PGT.Size) + ((i-8) * PCD.Size), PCD.Size).ToArray()); - if (this is SAV4HGSS hgss) - cards[^1] = hgss.LockCapsuleSlot; - return cards; - } - set - { - Span indexes = stackalloc byte[8]; - bool matchAny = MatchMysteryGifts(value, indexes); // automatically applied - if (!matchAny) - return; - - for (int i = 0; i < 8; i++) // 8 PGT - { - if (value[i] is PGT) - { - var ofs = (WondercardData + (i * PGT.Size)); - SetData(General[ofs..], value[i].Data); - } - } - for (int i = 8; i < 11; i++) // 3 PCD - { - if (value[i] is PCD) - { - var ofs = (WondercardData + (8 * PGT.Size) + ((i - 8) * PCD.Size)); - SetData(General[ofs..], value[i].Data); - } - } - if (this is SAV4HGSS hgss && value.Length >= 11 && value[^1] is PCD capsule) - hgss.LockCapsuleSlot = capsule; - } - } + public bool IsMysteryGiftUnlocked { get => (General[72] & 1) == 1; set => General[72] = (byte)((General[72] & 0xFE) | (value ? 1 : 0)); } protected sealed override void SetDex(PKM pk) => Dex.SetDex(pk); public sealed override bool GetCaught(ushort species) => Dex.GetCaught(species); @@ -654,4 +538,209 @@ public abstract class SAV4 : SaveFile, IEventFlag37 public abstract int BP { get; set; } public abstract BattleFrontierFacility4 MaxFacility { get; } + + public abstract MysteryBlock4 Mystery { get; } + IMysteryGiftStorage IMysteryGiftStorageProvider.MysteryGiftStorage => Mystery; +} + +public sealed class MysteryBlock4DP(SAV4DP sav, Memory raw) : MysteryBlock4(sav, raw) +{ + // 0x100 Flags + // 11 u32 IsActive sentinels + // 8 PGT + // 3 PCD + public const int Size = (MaxReceivedFlag / 8) + (SentinelCount * sizeof(uint)) + (MaxCountPGT * PGT.Size) + (MaxCountPCD * PCD.Size); + protected override int CardStart => FlagStart + FlagRegionSize + (11 * sizeof(uint)); + private const int SentinelCount = 11; + + // reverse crc32 polynomial, nice! + private const uint MysteryGiftDPSlotActive = 0xEDB88320; + + private Span SentinelSpan => Data.Slice(FlagStart + FlagRegionSize, 11 * sizeof(uint)); + + public uint GetMysteryGiftReceivedSentinel(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, SentinelCount); + return ReadUInt32LittleEndian(SentinelSpan[(index * sizeof(uint))..]); + } + + public void SetMysteryGiftReceivedSentinel(int index, uint value) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, SentinelCount); + WriteUInt32LittleEndian(SentinelSpan[(index * sizeof(uint))..], value); + } + + public override void SetMysteryGift(int index, PGT pgt) + { + base.SetMysteryGift(index, pgt); + SetMysteryGiftReceivedSentinel(index, pgt.Empty ? 0 : MysteryGiftDPSlotActive); + } + + public override void SetMysteryGift(int index, PCD pcd) + { + base.SetMysteryGift(index, pcd); + SetMysteryGiftReceivedSentinel(index, pcd.Empty ? 0 : MysteryGiftDPSlotActive); + } +} + +public sealed class MysteryBlock4Pt(SAV4Pt sav, Memory raw) : MysteryBlock4(sav, raw) +{ + // 0x100 Flags + // 8 PGT + // 3 PCD + public const int Size = (MaxReceivedFlag / 8) + (MaxCountPGT * PGT.Size) + (MaxCountPCD * PCD.Size); + protected override int CardStart => FlagStart + FlagRegionSize; +} + +public sealed class MysteryBlock4HGSS(SAV4HGSS sav, Memory raw) : MysteryBlock4(sav, raw) +{ + // 0x100 Flags + // 8 PGT + // 3 PCD0x100 + public const int Size = (MaxReceivedFlag / 8) + (MaxCountPGT * PGT.Size) + (MaxCountPCD * PCD.Size); + protected override int CardStart => FlagStart + FlagRegionSize; +} + +public abstract class MysteryBlock4(SAV4 sav, Memory raw) : SaveBlock(sav, raw), IMysteryGiftStorage, IMysteryGiftFlags +{ + protected const int FlagStart = 0; + protected const int MaxReceivedFlag = 2048; + protected const int MaxCountPGT = 8; + protected const int MaxCountPCD = 3; + protected const int MaxCardsPresent = MaxCountPGT + MaxCountPCD; + protected const int FlagRegionSize = (MaxReceivedFlag / 8); // 0x100 + protected abstract int CardStart { get; } + private const int FlagDeliveryManActive = 2047; + + private int CardRegionPGTStart => CardStart; + private int CardRegionPCDStart => CardRegionPGTStart + (MaxCountPGT * PGT.Size); + + public bool IsDeliveryManActive + { + get => GetMysteryGiftReceivedFlag(FlagDeliveryManActive); + set + { + if (value && !SAV.IsMysteryGiftUnlocked) + SAV.IsMysteryGiftUnlocked = true; // be nice to the user and unlock the Mystery Gift menu feature. + SetMysteryGiftReceivedFlag(FlagDeliveryManActive, value); + } + } + + public int GiftCountMax => MaxCardsPresent; + DataMysteryGift IMysteryGiftStorage.GetMysteryGift(int index) + { + if ((uint)index < MaxCountPGT) + return GetMysteryGiftPGT(index); + if ((uint)index < MaxCardsPresent) + return GetMysteryGiftPCD(index - MaxCountPGT); + throw new ArgumentOutOfRangeException(nameof(index)); + } + + void IMysteryGiftStorage.SetMysteryGift(int index, DataMysteryGift gift) + { + if ((uint) index < MaxCountPGT) + SetMysteryGift(index, (PGT)gift); + else if ((uint)index < MaxCardsPresent) + SetMysteryGift(index - MaxCountPGT, (PCD)gift); + else throw new ArgumentOutOfRangeException(nameof(index)); + } + + public int MysteryGiftReceivedFlagMax => FlagDeliveryManActive; // ignore the delivery man flag when populating flags + private Span FlagRegion => Data[..CardStart]; // 0x100 + + public void ClearReceivedFlags() => FlagRegion.Clear(); + + private int GetGiftOffsetPGT(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxCountPGT); + return CardRegionPGTStart + (index * PGT.Size); + } + + private int GetGiftOffsetPCD(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxCountPCD); + return CardRegionPCDStart + (index * PCD.Size); + } + + private Span GetCardSpanPGT(int index) => Data.Slice(GetGiftOffsetPGT(index), PGT.Size); + private Span GetCardSpanPCD(int index) => Data.Slice(GetGiftOffsetPCD(index), PGT.Size); + public PGT GetMysteryGiftPGT(int index) => new(GetCardSpanPGT(index).ToArray()); + public PCD GetMysteryGiftPCD(int index) => new(GetCardSpanPCD(index).ToArray()); + + public virtual void SetMysteryGift(int index, PGT pgt) + { + if ((uint)index > MaxCardsPresent) + throw new ArgumentOutOfRangeException(nameof(index)); + if (pgt.Data.Length != PGT.Size) + throw new InvalidCastException(nameof(pgt)); + pgt.VerifyPKEncryption(); + SAV.SetData(GetCardSpanPGT(index), pgt.Data); + } + + public virtual void SetMysteryGift(int index, PCD pcd) + { + if ((uint)index > MaxCardsPresent) + throw new ArgumentOutOfRangeException(nameof(index)); + if (pcd.Data.Length != PCD.Size) + throw new InvalidCastException(nameof(pcd)); + var gift = pcd.Gift; + if (gift.VerifyPKEncryption()) + pcd.Gift = gift; // ensure data is encrypted in the object + SAV.SetData(GetCardSpanPCD(index), pcd.Data); + } + + public bool GetMysteryGiftReceivedFlag(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxReceivedFlag); + return FlagUtil.GetFlag(Data, index); // offset 0 + } + + public void SetMysteryGiftReceivedFlag(int index, bool value) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxReceivedFlag); + FlagUtil.SetFlag(Data, index, value); // offset 0 + } + + // HG/SS 0,1,2,[3]; D/P/Pt 1,2,3,[4] + private const byte NoPCDforPGT = 3; // 4 for DPPt; handle this manually. + + /// + /// Each PGT points to a PCD slot if it is correlated. + /// + public static void UpdateSlotPGT(ReadOnlySpan value, bool hgss) + { + var arrPGT = value[..MaxCountPGT]; + var arrPCD = value.Slice(MaxCountPGT, MaxCountPCD); + UpdateSlotPGT(hgss, arrPGT, arrPCD); + } + + public static void UpdateSlotPGT(bool hgss, ReadOnlySpan arrPGT, ReadOnlySpan arrPCD) + { + foreach (var gift in arrPGT) + { + var pgt = (PGT)gift; + if (pgt.CardType == 0) // empty + { + pgt.Slot = 0; + continue; + } + + var index = FindIndexPCD(pgt, arrPCD); + if (!hgss) + index++; + pgt.Slot = index; + } + } + + private static byte FindIndexPCD(PGT pgt, ReadOnlySpan arrPCD) + { + for (byte i = 0; i < arrPCD.Length; i++) + { + var pcd = (PCD)arrPCD[i]; + // Check if data matches (except Slot @ 0x02) + if (pcd.GiftEquals(pgt)) + return i; + } + return NoPCDforPGT; + } } diff --git a/PKHeX.Core/Saves/SAV4BR.cs b/PKHeX.Core/Saves/SAV4BR.cs index 74fd6369f..48143659c 100644 --- a/PKHeX.Core/Saves/SAV4BR.cs +++ b/PKHeX.Core/Saves/SAV4BR.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// /// Generation 4 object for Pokémon Battle Revolution saves. /// -public sealed class SAV4BR : SaveFile +public sealed class SAV4BR : SaveFile, IBoxDetailName { protected internal override string ShortSummary => $"{Version} #{SaveCount:0000}"; public override string Extension => string.Empty; @@ -107,7 +107,6 @@ public sealed class SAV4BR : SaveFile public override int MaxEV => EffortValues.Max255; public override byte Generation => 4; public override EntityContext Context => EntityContext.Gen4; - protected override int GiftCountMax => 1; public override int MaxStringLengthOT => 7; public override int MaxStringLengthNickname => 10; public override int MaxMoney => 999999; @@ -206,18 +205,18 @@ public sealed class SAV4BR : SaveFile return Data.AsSpan(ofs, BoxNameLength); } - public override string GetBoxName(int box) + public string GetBoxName(int box) { if (BoxName < 0) - return $"BOX {box + 1}"; + return BoxDetailNameExtensions.GetDefaultBoxNameCaps(box); var span = GetBoxNameSpan(box); if (ReadUInt16BigEndian(span) == 0) - return $"BOX {box + 1}"; + return BoxDetailNameExtensions.GetDefaultBoxNameCaps(box); return GetString(span); } - public override void SetBoxName(int box, ReadOnlySpan value) + public void SetBoxName(int box, ReadOnlySpan value) { if (BoxName < 0) return; diff --git a/PKHeX.Core/Saves/SAV4DP.cs b/PKHeX.Core/Saves/SAV4DP.cs index 4a6164157..a8e842e4f 100644 --- a/PKHeX.Core/Saves/SAV4DP.cs +++ b/PKHeX.Core/Saves/SAV4DP.cs @@ -12,16 +12,21 @@ public sealed class SAV4DP : SAV4Sinnoh public SAV4DP() : base(GeneralSize, StorageSize) { Initialize(); - Dex = new Zukan4(this, PokeDex); + Mystery = new MysteryBlock4DP(this, GeneralBuffer.Slice(OffsetMystery, MysteryBlock4DP.Size)); + Dex = new Zukan4(this, GeneralBuffer[PokeDex..]); } public SAV4DP(byte[] data) : base(data, GeneralSize, StorageSize, GeneralSize) { Initialize(); - Dex = new Zukan4(this, PokeDex); + Mystery = new MysteryBlock4DP(this, GeneralBuffer.Slice(OffsetMystery, MysteryBlock4DP.Size)); + Dex = new Zukan4(this, GeneralBuffer[PokeDex..]); } + private const int OffsetMystery = 0xA7FC; + private const int PokeDex = 0x12DC; public override Zukan4 Dex { get; } + public override MysteryBlock4DP Mystery { get; } protected override SAV4 CloneInternal4() => State.Exportable ? new SAV4DP((byte[])Data.Clone()) : new SAV4DP(); public override GameVersion Version { get => GameVersion.DP; set { } } @@ -45,6 +50,7 @@ public sealed class SAV4DP : SAV4Sinnoh protected override int EventWork => 0xD9C; protected override int EventFlag => 0xFDC; + protected override int DaycareOffset => 0x141C; public override BattleFrontierFacility4 MaxFacility => BattleFrontierFacility4.Tower; private void GetSAVOffsets() @@ -52,15 +58,10 @@ public sealed class SAV4DP : SAV4Sinnoh AdventureInfo = 0; Trainer1 = 0x64; Party = 0x98; - PokeDex = 0x12DC; ChatterOffset = 0x61CC; Geonet = 0x96D8; WondercardFlags = 0xA6D0; - WondercardData = 0xA7fC; - - DaycareOffset = 0x141C; OFS_HONEY = 0x72E4; - OFS_UG_Stats = 0x3A2C; OFS_UG_Items = 0x42B0; @@ -109,47 +110,6 @@ public sealed class SAV4DP : SAV4Sinnoh set => value.SaveAll(General); } - // reverse crc32 polynomial, nice! - private const uint MysteryGiftDPSlotActive = 0xEDB88320; - - public bool[] GetMysteryGiftDPSlotActiveFlags() - { - var span = General[(WondercardFlags + 0x100)..]; // skip over flags - bool[] active = new bool[GiftCountMax]; // 8 PGT, 3 PCD - for (int i = 0; i < active.Length; i++) - active[i] = ReadUInt32LittleEndian(span[(4*i)..]) == MysteryGiftDPSlotActive; - - return active; - } - - public void SetMysteryGiftDPSlotActiveFlags(ReadOnlySpan value) - { - if (value.Length != GiftCountMax) - return; - - var span = General[(WondercardFlags + 0x100)..]; // skip over flags - for (int i = 0; i < value.Length; i++) - WriteUInt32LittleEndian(span[(4 * i)..], value[i] ? MysteryGiftDPSlotActive : 0); - } - - public override MysteryGiftAlbum GiftAlbum - { - get => base.GiftAlbum; - set - { - base.GiftAlbum = value; - SetActiveGiftFlags(value.Gifts); - } - } - - private void SetActiveGiftFlags(IReadOnlyList gifts) - { - var arr = new bool[gifts.Count]; - for (int i = 0; i < arr.Length; i++) - arr[i] = !gifts[i].Empty; - SetMysteryGiftDPSlotActiveFlags(arr); - } - public override int M { get => ReadUInt16LittleEndian(General[0x1238..]); set => WriteUInt16LittleEndian(General[0x1238..], (ushort)value); } public override int X { get => ReadUInt16LittleEndian(General[0x1240..]); set => WriteUInt16LittleEndian(General[0x1240..], (ushort)(X2 = value)); } public override int Y { get => ReadUInt16LittleEndian(General[0x1244..]); set => WriteUInt16LittleEndian(General[0x1244..], (ushort)(Y2 = value)); } diff --git a/PKHeX.Core/Saves/SAV4HGSS.cs b/PKHeX.Core/Saves/SAV4HGSS.cs index 23d8450c1..9023497bb 100644 --- a/PKHeX.Core/Saves/SAV4HGSS.cs +++ b/PKHeX.Core/Saves/SAV4HGSS.cs @@ -8,18 +8,20 @@ namespace PKHeX.Core; /// /// format for /// -public sealed class SAV4HGSS : SAV4 +public sealed class SAV4HGSS : SAV4, IBoxDetailName, IBoxDetailWallpaper { public SAV4HGSS() : base(GeneralSize, StorageSize) { Initialize(); - Dex = new Zukan4(this, PokeDex); + Mystery = new MysteryBlock4HGSS(this, GeneralBuffer.Slice(OffsetMystery, MysteryBlock4HGSS.Size)); + Dex = new Zukan4(this, GeneralBuffer[PokeDex..]); } public SAV4HGSS(byte[] data) : base(data, GeneralSize, StorageSize, GeneralSize + GeneralGap) { Initialize(); - Dex = new Zukan4(this, PokeDex); + Mystery = new MysteryBlock4HGSS(this, GeneralBuffer.Slice(OffsetMystery, MysteryBlock4HGSS.Size)); + Dex = new Zukan4(this, GeneralBuffer[PokeDex..]); } public override Zukan4 Dex { get; } @@ -32,6 +34,7 @@ public sealed class SAV4HGSS : SAV4 public const int GeneralSize = 0xF628; private const int StorageSize = 0x12310; // Start 0xF700, +0 starts box data private const int GeneralGap = 0xD8; + private const int PokeDex = 0x12B8; protected override int FooterSize => 0x10; protected override BlockInfo4[] ExtraBlocks => @@ -50,8 +53,11 @@ public sealed class SAV4HGSS : SAV4 GetSAVOffsets(); } + private const int OffsetMystery = 0x9E3C; protected override int EventWork => 0xDE4; protected override int EventFlag => 0x10C4; + protected override int DaycareOffset => 0x15FC; + public override MysteryBlock4HGSS Mystery { get; } public override BattleFrontierFacility4 MaxFacility => BattleFrontierFacility4.Arcade; private void GetSAVOffsets() @@ -59,14 +65,10 @@ public sealed class SAV4HGSS : SAV4 AdventureInfo = 0; Trainer1 = 0x64; Party = 0x98; - PokeDex = 0x12B8; Extra = 0x230C; ChatterOffset = 0x4E74; Geonet = 0x8D44; WondercardFlags = 0x9D3C; - WondercardData = 0x9E3C; - - DaycareOffset = 0x15FC; Seal = 0x4E20; Box = 0; @@ -106,7 +108,7 @@ public sealed class SAV4HGSS : SAV4 public override int GetBoxOffset(int box) => box * 0x1000; private static int GetBoxNameOffset(int box) => BOX_NAME + (box * BOX_NAME_LEN); - protected override int GetBoxWallpaperOffset(int box) => BOX_WP + box; + private static int GetBoxWallpaperOffset(int box) => BOX_WP + box; // 8 bytes current box (align 32) & (stored count?) public override int CurrentBox @@ -128,9 +130,9 @@ public sealed class SAV4HGSS : SAV4 } private Span GetBoxNameSpan(int box) => Storage.Slice(GetBoxNameOffset(box), BOX_NAME_LEN); - public override string GetBoxName(int box) => GetString(GetBoxNameSpan(box)); + public string GetBoxName(int box) => GetString(GetBoxNameSpan(box)); - public override void SetBoxName(int box, ReadOnlySpan value) + public void SetBoxName(int box, ReadOnlySpan value) { const int maxlen = 8; var span = GetBoxNameSpan(box); @@ -146,14 +148,14 @@ public sealed class SAV4HGSS : SAV4 return value; } - public override int GetBoxWallpaper(int box) + public int GetBoxWallpaper(int box) { int offset = GetBoxWallpaperOffset(box); int value = Storage[offset]; return AdjustWallpaper(value, -0x10); } - public override void SetBoxWallpaper(int box, int value) + public void SetBoxWallpaper(int box, int value) { value = AdjustWallpaper(value, 0x10); Storage[GetBoxWallpaperOffset(box)] = (byte)value; diff --git a/PKHeX.Core/Saves/SAV4Pt.cs b/PKHeX.Core/Saves/SAV4Pt.cs index 02ea01367..235e414fa 100644 --- a/PKHeX.Core/Saves/SAV4Pt.cs +++ b/PKHeX.Core/Saves/SAV4Pt.cs @@ -12,16 +12,19 @@ public sealed class SAV4Pt : SAV4Sinnoh public SAV4Pt() : base(GeneralSize, StorageSize) { Initialize(); - Dex = new Zukan4(this, PokeDex); + Mystery = new MysteryBlock4Pt(this, GeneralBuffer.Slice(OffsetMystery, MysteryBlock4Pt.Size)); + Dex = new Zukan4(this, GeneralBuffer[PokeDex..]); } public SAV4Pt(byte[] data) : base(data, GeneralSize, StorageSize, GeneralSize) { Initialize(); - Dex = new Zukan4(this, PokeDex); + Mystery = new MysteryBlock4Pt(this, GeneralBuffer.Slice(OffsetMystery, MysteryBlock4Pt.Size)); + Dex = new Zukan4(this, GeneralBuffer[PokeDex..]); } public override Zukan4 Dex { get; } + public override MysteryBlock4Pt Mystery { get; } protected override SAV4 CloneInternal4() => State.Exportable ? new SAV4Pt((byte[])Data.Clone()) : new SAV4Pt(); public override GameVersion Version { get => GameVersion.Pt; set { } } public override PersonalTable4 Personal => PersonalTable.Pt; @@ -43,14 +46,12 @@ public sealed class SAV4Pt : SAV4Sinnoh new BlockInfo4(5, 0x2A000, 0x1D60), // Battle Video (Other Videos 3) ]; - private void Initialize() - { - Version = GameVersion.Pt; - GetSAVOffsets(); - } + private void Initialize() => GetSAVOffsets(); protected override int EventWork => 0xDAC; protected override int EventFlag => 0xFEC; + private const int OffsetMystery = 0xB5C0; + protected override int DaycareOffset => 0x1654; public override BattleFrontierFacility4 MaxFacility => BattleFrontierFacility4.Arcade; private const int OFS_AccessoryMultiCount = 0x4E38; // 4 bits each @@ -59,19 +60,19 @@ public sealed class SAV4Pt : SAV4Sinnoh private const int OFS_ToughWord = 0xCEB4; private const int OFS_VillaFurniture = 0x111F; + private const int PokeDex = 0x1328; + public override bool HasPokeDex => true; + private void GetSAVOffsets() { AdventureInfo = 0; Trainer1 = 0x68; Party = 0xA0; - PokeDex = 0x1328; Extra = 0x2820; ChatterOffset = 0x64EC; Geonet = 0xA4C4; WondercardFlags = 0xB4C0; - WondercardData = 0xB5C0; - DaycareOffset = 0x1654; OFS_HONEY = 0x7F38; OFS_UG_Stats = 0x3CB4; diff --git a/PKHeX.Core/Saves/SAV4Sinnoh.cs b/PKHeX.Core/Saves/SAV4Sinnoh.cs index a422a6d62..2ba9306a7 100644 --- a/PKHeX.Core/Saves/SAV4Sinnoh.cs +++ b/PKHeX.Core/Saves/SAV4Sinnoh.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// /// Abstract format for and /// -public abstract class SAV4Sinnoh : SAV4 +public abstract class SAV4Sinnoh : SAV4, IBoxDetailName, IBoxDetailWallpaper { protected override int FooterSize => 0x14; protected SAV4Sinnoh([ConstantExpected] int gSize, [ConstantExpected] int sSize) : base(gSize, sSize) { } @@ -30,7 +30,7 @@ public abstract class SAV4Sinnoh : SAV4 public override int GetBoxOffset(int box) => 4 + (box * BOX_DATA_LEN); private static int GetBoxNameOffset(int box) => BOX_NAME + (box * BOX_NAME_LEN); - protected override int GetBoxWallpaperOffset(int box) => BOX_WP + box; + protected static int GetBoxWallpaperOffset(int box) => BOX_WP + box; public override int CurrentBox // (align 32) { @@ -45,14 +45,16 @@ public abstract class SAV4Sinnoh : SAV4 } private Span GetBoxNameSpan(int box) => Storage.Slice(GetBoxNameOffset(box), BOX_NAME_LEN); - public override string GetBoxName(int box) => GetString(GetBoxNameSpan(box)); + public string GetBoxName(int box) => GetString(GetBoxNameSpan(box)); - public override void SetBoxName(int box, ReadOnlySpan value) + public void SetBoxName(int box, ReadOnlySpan value) { const int maxlen = 8; var span = GetBoxNameSpan(box); SetString(span, value, maxlen, StringConverterOption.ClearZero); } + public abstract int GetBoxWallpaper(int box); + public abstract void SetBoxWallpaper(int box, int value); #endregion #region Poketch diff --git a/PKHeX.Core/Saves/SAV5.cs b/PKHeX.Core/Saves/SAV5.cs index db11c3d48..7efe91d7a 100644 --- a/PKHeX.Core/Saves/SAV5.cs +++ b/PKHeX.Core/Saves/SAV5.cs @@ -8,7 +8,7 @@ namespace PKHeX.Core; /// /// Generation 5 object. /// -public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37 +public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlagProvider37, IBoxDetailName, IBoxDetailWallpaper, IDaycareRandomState, IDaycareStorage, IDaycareExperience, IDaycareEggState, IMysteryGiftStorageProvider { protected override PK5 GetPKM(byte[] data) => new(data); protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray45(data); @@ -28,11 +28,6 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37 public override EntityContext Context => EntityContext.Gen5; public override int MaxStringLengthOT => 7; public override int MaxStringLengthNickname => 10; - protected override int GiftCountMax => 12; - public abstract int EventFlagCount { get; } - public abstract int EventWorkCount { get; } - protected abstract int EventFlagOffset { get; } - protected abstract int EventWorkOffset { get; } public override ushort MaxMoveID => Legal.MaxMoveID_5; public override ushort MaxSpeciesID => Legal.MaxSpeciesID_5; @@ -55,7 +50,6 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37 { Box = 0x400; Party = 0x18E00; - AdventureInfo = 0x1D900; } // Blocks & Offsets @@ -65,19 +59,16 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37 protected int CGearInfoOffset; protected int CGearDataOffset; - protected int EntreeForestOffset; - private int AdventureInfo; - public abstract int GTS { get; } - public int PGL => AllBlocks[35].Offset + 8; // Dream World Upload // Daycare - public override int DaycareSeedSize => Daycare5.DaycareSeedSize; - public override bool? IsDaycareOccupied(int loc, int slot) => Daycare.IsOccupied(slot); - public override int GetDaycareSlotOffset(int loc, int slot) => Daycare.GetPKMOffset(slot); - public override uint? GetDaycareEXP(int loc, int slot) => Daycare.GetEXP(slot); - public override void SetDaycareEXP(int loc, int slot, uint EXP) => Daycare.SetEXP(slot, EXP); - public override void SetDaycareOccupied(int loc, int slot, bool occupied) => Daycare.SetOccupied(slot, occupied); - public override void SetDaycareRNGSeed(int loc, string seed) => Daycare.SetSeed(seed); + public int DaycareSlotCount => 2; + public Memory GetDaycareSlot(int slot) => Daycare.GetDaycareSlot(slot); + public bool IsDaycareOccupied(int slot) => Daycare.IsDaycareOccupied(slot); + public uint GetDaycareEXP(int slot) => Daycare.GetDaycareEXP(slot); + public void SetDaycareEXP(int slot, uint value) => Daycare.SetDaycareEXP(slot, value); + public void SetDaycareOccupied(int slot, bool occupied) => Daycare.SetDaycareOccupied(slot, occupied); + public bool IsEggAvailable { get => Daycare.IsEggAvailable; set => Daycare.IsEggAvailable = value; } + ulong IDaycareRandomState.Seed { get => Daycare.Seed; set => Daycare.Seed = value; } // Storage public override int PartyCount @@ -89,22 +80,13 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37 public override int GetBoxOffset(int box) => Box + (SIZE_STORED * box * 30) + (box * 0x10); public override int GetPartyOffset(int slot) => Party + 8 + (SIZE_PARTY * slot); - protected override int GetBoxWallpaperOffset(int box) => BoxLayout.GetBoxWallpaperOffset(box); public override int BoxesUnlocked { get => BoxLayout.BoxesUnlocked; set => BoxLayout.BoxesUnlocked = (byte)value; } - public override int GetBoxWallpaper(int box) => BoxLayout.GetBoxWallpaper(box); - public override void SetBoxWallpaper(int box, int value) => BoxLayout.SetBoxWallpaper(box, value); - public override string GetBoxName(int box) => BoxLayout[box]; - public override void SetBoxName(int box, ReadOnlySpan value) => BoxLayout.SetBoxName(box, value); + public int GetBoxWallpaper(int box) => BoxLayout.GetBoxWallpaper(box); + public void SetBoxWallpaper(int box, int value) => BoxLayout.SetBoxWallpaper(box, value); + public string GetBoxName(int box) => BoxLayout[box]; + public void SetBoxName(int box, ReadOnlySpan value) => BoxLayout.SetBoxName(box, value); public override int CurrentBox { get => BoxLayout.CurrentBox; set => BoxLayout.CurrentBox = value; } - protected int BattleBoxOffset; - - public bool BattleBoxLocked - { - get => Data[BattleBoxOffset + 0x358] != 0; // Wi-Fi/Live Tournament Active - set => Data[BattleBoxOffset + 0x358] = value ? (byte)1 : (byte)0; - } - protected override void SetPKM(PKM pk, bool isParty = false) { var pk5 = (PK5)pk; @@ -127,9 +109,8 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37 public override int PlayedMinutes { get => PlayerData.PlayedMinutes; set => PlayerData.PlayedMinutes = value; } public override int PlayedSeconds { get => PlayerData.PlayedSeconds; set => PlayerData.PlayedSeconds = value; } public override uint Money { get => Misc.Money; set => Misc.Money = value; } - public override uint SecondsToStart { get => ReadUInt32LittleEndian(Data.AsSpan(AdventureInfo + 0x34)); set => WriteUInt32LittleEndian(Data.AsSpan(AdventureInfo + 0x34), value); } - public override uint SecondsToFame { get => ReadUInt32LittleEndian(Data.AsSpan(AdventureInfo + 0x3C)); set => WriteUInt32LittleEndian(Data.AsSpan(AdventureInfo + 0x3C), value); } - public override MysteryGiftAlbum GiftAlbum { get => Mystery.GiftAlbum; set => Mystery.GiftAlbum = (EncryptedMysteryGiftAlbum)value; } + public override uint SecondsToStart { get => AdventureInfo.SecondsToStart; set => AdventureInfo.SecondsToStart = value; } + public override uint SecondsToFame { get => AdventureInfo.SecondsToFame ; set => AdventureInfo.SecondsToFame = value; } public override IReadOnlyList Inventory { get => Items.Inventory; set => Items.Inventory = value; } protected override void SetDex(PKM pk) => Zukan.SetDex(pk); @@ -143,30 +124,13 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37 return StringConverter5.SetString(destBuffer, value, maxLength, option); } - public bool GetEventFlag(int flagNumber) - { - if ((uint)flagNumber >= EventFlagCount) - throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagCount})."); - return GetFlag(EventFlagOffset + (flagNumber >> 3), flagNumber & 7); - } - - public void SetEventFlag(int flagNumber, bool value) - { - if ((uint)flagNumber >= EventFlagCount) - throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagCount})."); - SetFlag(EventFlagOffset + (flagNumber >> 3), flagNumber & 7, value); - } - - public ushort GetWork(int index) => ReadUInt16LittleEndian(Data.AsSpan(EventWorkOffset + (index * 2))); - public void SetWork(int index, ushort value) => WriteUInt16LittleEndian(Data.AsSpan(EventWorkOffset)[(index * 2)..], value); - // DLC private int CGearSkinInfoOffset => CGearInfoOffset + (this is SAV5B2W2 ? 0x10 : 0) + 0x24; private bool CGearSkinPresent { get => Data[CGearSkinInfoOffset + 2] == 1; - set => Data[CGearSkinInfoOffset + 2] = Data[PlayerData.Offset + (this is SAV5B2W2 ? 0x6C : 0x54)] = value ? (byte)1 : (byte)0; + set => Data[CGearSkinInfoOffset + 2] = PlayerData.Data[this is SAV5B2W2 ? 0x6C : 0x54] = value ? (byte)1 : (byte)0; } private static ReadOnlySpan DLCFooter => [ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x27, 0x00, 0x00, 0x27, 0x35, 0x05, 0x31, 0x00, 0x00 ]; @@ -205,12 +169,6 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37 } } - public EntreeForest EntreeData - { - get => new(Data.AsSpan(EntreeForestOffset, EntreeForest.SIZE).ToArray()); - set => SetData(value.Write(), EntreeForestOffset); - } - public abstract IReadOnlyList AllBlocks { get; } public abstract MyItem Items { get; } public abstract Zukan5 Zukan { get; } @@ -225,10 +183,24 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37 public abstract Musical5 Musical { get; } public abstract Encount5 Encount { get; } public abstract UnityTower5 UnityTower { get; } + public abstract EventWork5 EventWork { get; } + public abstract BattleBox5 BattleBox { get; } + public abstract EntreeForest EntreeForest { get; } + public abstract GlobalLink5 GlobalLink { get; } + public abstract WhiteBlack5 Forest { get; } + public abstract GTS5 GTS { get; } + public abstract AdventureInfo5 AdventureInfo { get; } + IEventFlag37 IEventFlagProvider37.EventWork => EventWork; + + protected override byte[] GetFinalData() + { + EntreeForest.EndAccess(); + Mystery.EndAccess(); + return base.GetFinalData(); + } public static int GetMailOffset(int index) => (index * Mail5.SIZE) + 0x1DD00; public byte[] GetMailData(int offset) => Data.AsSpan(offset, Mail5.SIZE).ToArray(); - public int GetBattleBoxSlot(int slot) => BattleBoxOffset + (slot * SIZE_STORED); public MailDetail GetMail(int mailIndex) { @@ -236,4 +208,6 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37 var data = GetMailData(ofs); return new Mail5(data, ofs); } + + IMysteryGiftStorage IMysteryGiftStorageProvider.MysteryGiftStorage => Mystery; } diff --git a/PKHeX.Core/Saves/SAV5B2W2.cs b/PKHeX.Core/Saves/SAV5B2W2.cs index d257f2901..9ec81715c 100644 --- a/PKHeX.Core/Saves/SAV5B2W2.cs +++ b/PKHeX.Core/Saves/SAV5B2W2.cs @@ -24,23 +24,16 @@ public sealed class SAV5B2W2 : SAV5, ISaveBlock5B2W2 public override PersonalTable5B2W2 Personal => PersonalTable.B2W2; public SaveBlockAccessor5B2W2 Blocks { get; } protected override SAV5B2W2 CloneInternal() => new((byte[]) Data.Clone()); - public override int EventWorkCount => 0x1AF; // this doesn't seem right? - public override int EventFlagCount => 0xBF8; - protected override int EventWorkOffset => 0x1FF00; - protected override int EventFlagOffset => EventWorkOffset + 0x35E; public override int MaxItemID => Legal.MaxItemID_5_B2W2; private void Initialize() { - BattleBoxOffset = 0x20900; CGearInfoOffset = 0x1C000; CGearDataOffset = 0x52800; - EntreeForestOffset = 0x22A00; - PokeDex = Blocks.Zukan.PokeDex; - WondercardData = Blocks.Mystery.Offset; - DaycareOffset = Blocks.Daycare.Offset; } + public override bool HasPokeDex => true; + public override IReadOnlyList AllBlocks => Blocks.BlockInfo; public override MyItem Items => Blocks.Items; public override Zukan5 Zukan => Blocks.Zukan; @@ -55,11 +48,17 @@ public sealed class SAV5B2W2 : SAV5, ISaveBlock5B2W2 public override Musical5 Musical => Blocks.Musical; public override Encount5 Encount => Blocks.Encount; public override UnityTower5 UnityTower => Blocks.UnityTower; + public override EventWork5B2W2 EventWork => Blocks.EventWork; + public override BattleBox5 BattleBox => Blocks.BattleBox; + public override EntreeForest EntreeForest => Blocks.EntreeForest; + public override GlobalLink5 GlobalLink => Blocks.GlobalLink; + public override GTS5 GTS => Blocks.GTS; + public override WhiteBlack5B2W2 Forest => Blocks.Forest; + public override AdventureInfo5 AdventureInfo => Blocks.AdventureInfo; + public FestaBlock5 Festa => Blocks.Festa; public PWTBlock5 PWT => Blocks.PWT; public MedalList5 Medals => Blocks.Medals; - public int Fused => 0x1FA00 + sizeof(uint); - public override int GTS => 0x20400; public string Rival { @@ -72,6 +71,4 @@ public sealed class SAV5B2W2 : SAV5, ISaveBlock5B2W2 get => Data.AsSpan(0x23BA4, MaxStringLengthOT * 2); set { if (value.Length == MaxStringLengthOT * 2) value.CopyTo(Data.AsSpan(0x23BA4)); } } - - public override string GetDaycareRNGSeed(int loc) => $"{Daycare.GetSeed()!:X16}"; } diff --git a/PKHeX.Core/Saves/SAV5BW.cs b/PKHeX.Core/Saves/SAV5BW.cs index 06fc6a11d..b4fcd1f49 100644 --- a/PKHeX.Core/Saves/SAV5BW.cs +++ b/PKHeX.Core/Saves/SAV5BW.cs @@ -23,36 +23,35 @@ public sealed class SAV5BW : SAV5 public override PersonalTable5BW Personal => PersonalTable.BW; public SaveBlockAccessor5BW Blocks { get; } protected override SAV5BW CloneInternal() => new((byte[])Data.Clone()); - public override int EventWorkCount => 0x13E; - public override int EventFlagCount => 0xB60; - protected override int EventWorkOffset => 0x20100; - protected override int EventFlagOffset => EventWorkOffset + 0x27C; public override int MaxItemID => Legal.MaxItemID_5_BW; + public override bool HasPokeDex => true; + private void Initialize() { - BattleBoxOffset = 0x20A00; CGearInfoOffset = 0x1C000; CGearDataOffset = 0x52000; - EntreeForestOffset = 0x22C00; - PokeDex = Blocks.Zukan.PokeDex; - WondercardData = Blocks.Mystery.Offset; - DaycareOffset = Blocks.Daycare.Offset; } public override IReadOnlyList AllBlocks => Blocks.BlockInfo; - public override MyItem Items => Blocks.Items; + public override MyItem5BW Items => Blocks.Items; public override Zukan5 Zukan => Blocks.Zukan; - public override Misc5 Misc => Blocks.Misc; + public override Misc5BW Misc => Blocks.Misc; public override MysteryBlock5 Mystery => Blocks.Mystery; public override Chatter5 Chatter => Blocks.Chatter; public override Daycare5 Daycare => Blocks.Daycare; public override BoxLayout5 BoxLayout => Blocks.BoxLayout; public override PlayerData5 PlayerData => Blocks.PlayerData; public override BattleSubway5 BattleSubway => Blocks.BattleSubway; - public override Entralink5 Entralink => Blocks.Entralink; + public override Entralink5BW Entralink => Blocks.Entralink; public override Musical5 Musical => Blocks.Musical; - public override Encount5 Encount => Blocks.Encount; + public override Encount5BW Encount => Blocks.Encount; public override UnityTower5 UnityTower => Blocks.UnityTower; - public override int GTS => 0x20500; + public override EventWork5BW EventWork => Blocks.EventWork; + public override BattleBox5 BattleBox => Blocks.BattleBox; + public override EntreeForest EntreeForest => Blocks.EntreeForest; + public override GlobalLink5 GlobalLink => Blocks.GlobalLink; + public override GTS5 GTS => Blocks.GTS; + public override WhiteBlack5BW Forest => Blocks.Forest; + public override AdventureInfo5 AdventureInfo => Blocks.AdventureInfo; } diff --git a/PKHeX.Core/Saves/SAV6.cs b/PKHeX.Core/Saves/SAV6.cs index b5fee4815..bce6f046f 100644 --- a/PKHeX.Core/Saves/SAV6.cs +++ b/PKHeX.Core/Saves/SAV6.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; /// /// Generation 6 object. /// -public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IRegionOrigin, IGameSync, IEventFlag37 +public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IRegionOrigin, IGameSync, IEventFlagProvider37 { // Save Data Attributes protected internal override string ShortSummary => $"{OT} ({Version}) - {Played.LastSavedTime}"; @@ -18,21 +17,15 @@ public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IReg protected SAV6([ConstantExpected] int size, [ConstantExpected] int biOffset) : base(size, biOffset) { } // Configuration - protected override int SIZE_STORED => PokeCrypto.SIZE_6STORED; - protected override int SIZE_PARTY => PokeCrypto.SIZE_6PARTY; - public override PK6 BlankPKM => new(); - public override Type PKMType => typeof(PK6); + protected sealed override int SIZE_STORED => PokeCrypto.SIZE_6STORED; + protected sealed override int SIZE_PARTY => PokeCrypto.SIZE_6PARTY; + public sealed override PK6 BlankPKM => new(); + public sealed override Type PKMType => typeof(PK6); - public override int BoxCount => 31; - public override int MaxEV => EffortValues.Max252; - public override byte Generation => 6; - public override EntityContext Context => EntityContext.Gen6; - protected override int GiftCountMax => 24; - protected override int GiftFlagMax => 0x100 * 8; - public int EventFlagCount => 8 * 0x1A0; - public int EventWorkCount => (EventFlag - EventWork) / sizeof(ushort); - protected abstract int EventFlag { get; } - protected abstract int EventWork { get; } + public sealed override int BoxCount => 31; + public sealed override int MaxEV => EffortValues.Max252; + public sealed override byte Generation => 6; + public sealed override EntityContext Context => EntityContext.Gen6; public override int MaxStringLengthOT => 12; public override int MaxStringLengthNickname => 12; @@ -43,14 +36,9 @@ public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IReg protected override PK6 GetPKM(byte[] data) => new(data); protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray6(data); - protected int WondercardFlags { get; set; } = int.MinValue; protected int JPEG { get; set; } = int.MinValue; public int PSS { get; protected set; } = int.MinValue; - public int BerryField { get; protected set; } = int.MinValue; public int HoF { get; protected set; } = int.MinValue; - protected int PCLayout { private get; set; } = int.MinValue; - protected int BattleBoxOffset { get; set; } = int.MinValue; - public int GetBattleBoxSlot(int slot) => BattleBoxOffset + (slot * SIZE_STORED); public virtual string JPEGTitle => string.Empty; public virtual byte[] GetJPEGData() => []; @@ -84,29 +72,11 @@ public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IReg public override uint SecondsToFame { get => GameTime.SecondsToFame; set => GameTime.SecondsToFame = value; } public override IReadOnlyList Inventory { get => Items.Inventory; set => Items.Inventory = value; } - // Daycare - public override int DaycareSeedSize => 16; - // Storage public override int GetPartyOffset(int slot) => Party + (SIZE_PARTY * slot); public override int GetBoxOffset(int box) => Box + (SIZE_STORED * box * 30); - private int GetBoxNameOffset(int box) => PCLayout + (LongStringLength * box); - - public override string GetBoxName(int box) - { - if (PCLayout < 0) - return $"B{box + 1}"; - return GetString(Data.AsSpan(GetBoxNameOffset(box), LongStringLength)); - } - - public override void SetBoxName(int box, ReadOnlySpan value) - { - var span = Data.AsSpan(GetBoxNameOffset(box), LongStringLength); - SetString(span, value, LongStringLength / 2, StringConverterOption.ClearZero); - } - protected override void SetPKM(PKM pk, bool isParty = false) { PK6 pk6 = (PK6)pk; @@ -187,21 +157,6 @@ public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IReg public abstract PlayTime6 Played { get; } public abstract MyStatus6 Status { get; } public abstract RecordBlock6 Records { get; } - - public bool GetEventFlag(int flagNumber) - { - if ((uint)flagNumber >= EventFlagCount) - throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagCount})."); - return GetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7); - } - - public void SetEventFlag(int flagNumber, bool value) - { - if ((uint)flagNumber >= EventFlagCount) - throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagCount})."); - SetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7, value); - } - - public ushort GetWork(int index) => ReadUInt16LittleEndian(Data.AsSpan(EventWork + (index * 2))); - public void SetWork(int index, ushort value) => WriteUInt16LittleEndian(Data.AsSpan(EventWork)[(index * 2)..], value); + public abstract EventWork6 EventWork { get; } + IEventFlag37 IEventFlagProvider37.EventWork => EventWork; } diff --git a/PKHeX.Core/Saves/SAV6AO.cs b/PKHeX.Core/Saves/SAV6AO.cs index b19cec6d0..5773461bc 100644 --- a/PKHeX.Core/Saves/SAV6AO.cs +++ b/PKHeX.Core/Saves/SAV6AO.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -8,7 +7,7 @@ namespace PKHeX.Core; /// Generation 6 object for . /// /// -public sealed class SAV6AO : SAV6, ISaveBlock6AO, IMultiplayerSprite +public sealed class SAV6AO : SAV6, ISaveBlock6AO, IMultiplayerSprite, IBoxDetailName, IBoxDetailWallpaper, IDaycareMulti { public SAV6AO(byte[] data) : base(data, SaveBlockAccessor6AO.BlockMetadataOffset) { @@ -31,44 +30,32 @@ public sealed class SAV6AO : SAV6, ISaveBlock6AO, IMultiplayerSprite public override int MaxItemID => Legal.MaxItemID_6_AO; public override int MaxAbilityID => Legal.MaxAbilityID_6_AO; - protected override int EventWork => 0x14A00; - protected override int EventFlag => EventWork + 0x2F0; + public override bool HasPokeDex => true; private void Initialize() { - PCLayout = 0x04400; - BattleBoxOffset = 0x04A00; PSS = 0x05000; Party = 0x14200; - PokeDex = 0x15000; HoF = 0x19E00; - DaycareOffset = 0x1BC00; - BerryField = 0x1C400; - WondercardFlags = 0x1CC00; Box = 0x33000; JPEG = 0x67C00; - - WondercardData = WondercardFlags + 0x100; } - /// Offset of the UnionPokemon block. - public const int Fused = 0x16A00; - /// Offset of the GtsData block. - public const int GTS = 0x18200; - /// Offset of the second daycare structure within the Daycare block. - private const int Daycare2 = 0x1BC00 + 0x1F0; /// Offset of the Contest data block. public const int Contest = 0x23600; #region Blocks public override IReadOnlyList AllBlocks => Blocks.BlockInfo; - public override MyItem Items => Blocks.Items; + public override MyItem6AO Items => Blocks.Items; public override ItemInfo6 ItemInfo => Blocks.ItemInfo; public override GameTime6 GameTime => Blocks.GameTime; public override Situation6 Situation => Blocks.Situation; public override PlayTime6 Played => Blocks.Played; public override MyStatus6 Status => Blocks.Status; - public override RecordBlock6 Records => Blocks.Records; + public override RecordBlock6AO Records => Blocks.Records; + public override EventWork6 EventWork => Blocks.EventWork; + public UnionPokemon6 Fused => Blocks.Fused; + public GTS6 GTS => Blocks.GTS; public Puff6 Puff => Blocks.Puff; public OPower6 OPower => Blocks.OPower; public LinkBlock6 Link => Blocks.Link; @@ -77,13 +64,17 @@ public sealed class SAV6AO : SAV6, ISaveBlock6AO, IMultiplayerSprite public MysteryBlock6 MysteryGift => Blocks.MysteryGift; public SuperTrainBlock SuperTrain => Blocks.SuperTrain; public MaisonBlock Maison => Blocks.Maison; - public SubEventLog6 SUBE => Blocks.SUBE; + public SubEventLog6AO SUBE => Blocks.SUBE; public ConfigSave6 Config => Blocks.Config; public Encount6 Encount => Blocks.Encount; - public Misc6AO Misc => Blocks.Misc; public Zukan6AO Zukan => Blocks.Zukan; public SecretBase6Block SecretBase => Blocks.SecretBase; + public BerryField6AO BerryField => Blocks.BerryField; + + MyItem ISaveBlock6Core.Items => Items; + SubEventLog6 ISaveBlock6Main.SUBE => SUBE; + RecordBlock6 ISaveBlock6Core.Records => Records; #endregion public override bool IsVersionValid() => Version is GameVersion.AS or GameVersion.OR; @@ -106,84 +97,25 @@ public sealed class SAV6AO : SAV6, ISaveBlock6AO, IMultiplayerSprite } // Daycare - public override int DaycareSeedSize => 16; - public override bool HasTwoDaycares => true; - public override int GetDaycareSlotOffset(int loc, int slot) - { - int ofs = loc == 0 ? DaycareOffset : Daycare2; - return ofs + 8 + (slot * (SIZE_STORED + 8)); - } - - public override uint? GetDaycareEXP(int loc, int slot) - { - int ofs = loc == 0 ? DaycareOffset : Daycare2; - return ReadUInt32LittleEndian(Data.AsSpan(ofs + ((SIZE_STORED + 8) * slot) + 4)); - } - - public override bool? IsDaycareOccupied(int loc, int slot) - { - int ofs = loc == 0 ? DaycareOffset : Daycare2; - return Data[ofs + ((SIZE_STORED + 8) * slot)] == 1; - } - - public override string GetDaycareRNGSeed(int loc) - { - int ofs = loc == 0 ? DaycareOffset : Daycare2; - return Util.GetHexStringFromBytes(Data.AsSpan(ofs + 0x1E8, DaycareSeedSize / 2)); - } - - public override bool? IsDaycareHasEgg(int loc) - { - int ofs = loc == 0 ? DaycareOffset : Daycare2; - return Data[ofs + 0x1E0] == 1; - } - - public override void SetDaycareEXP(int loc, int slot, uint EXP) - { - int ofs = loc == 0 ? DaycareOffset : Daycare2; - WriteUInt32LittleEndian(Data.AsSpan(ofs + ((SIZE_STORED + 8) * slot) + 4), EXP); - } - - public override void SetDaycareOccupied(int loc, int slot, bool occupied) - { - int ofs = loc == 0 ? DaycareOffset : Daycare2; - Data[ofs + ((SIZE_STORED + 8) * slot)] = occupied ? (byte)1 : (byte)0; - } - - public override void SetDaycareRNGSeed(int loc, string seed) - { - if (loc != 0) - return; - if (DaycareOffset < 0) - return; - if (seed.Length > DaycareSeedSize) - return; - - Util.GetBytesFromHexString(seed).CopyTo(Data, DaycareOffset + 0x1E8); - } - - public override void SetDaycareHasEgg(int loc, bool hasEgg) - { - int ofs = loc == 0 ? DaycareOffset : Daycare2; - Data[ofs + 0x1E0] = hasEgg ? (byte)1 : (byte)0; - } - - public override string JPEGTitle => !HasJPPEGData ? string.Empty : StringConverter6.GetString(Data.AsSpan(JPEG, 0x1A)); - public override byte[] GetJPEGData() => !HasJPPEGData ? [] : Data.AsSpan(JPEG + 0x54, 0xE004).ToArray(); - private bool HasJPPEGData => Data[JPEG + 0x54] == 0xFF; - - protected override bool[] MysteryGiftReceivedFlags { get => Blocks.MysteryGift.GetReceivedFlags(); set => Blocks.MysteryGift.SetReceivedFlags(value); } - protected override DataMysteryGift[] MysteryGiftCards { get => Blocks.MysteryGift.GetGifts(); set => Blocks.MysteryGift.SetGifts(value); } + public override string JPEGTitle => !HasJPEGData ? string.Empty : StringConverter6.GetString(Data.AsSpan(JPEG, 0x1A)); + public override byte[] GetJPEGData() => !HasJPEGData ? [] : Data.AsSpan(JPEG + 0x54, 0xE004).ToArray(); + private bool HasJPEGData => Data[JPEG + 0x54] == 0xFF; public override int CurrentBox { get => Blocks.BoxLayout.CurrentBox; set => Blocks.BoxLayout.CurrentBox = value; } - protected override int GetBoxWallpaperOffset(int box) => Blocks.BoxLayout.GetBoxWallpaperOffset(box); public override int BoxesUnlocked { get => Blocks.BoxLayout.BoxesUnlocked; set => Blocks.BoxLayout.BoxesUnlocked = value; } public override byte[] BoxFlags { get => Blocks.BoxLayout.BoxFlags; set => Blocks.BoxLayout.BoxFlags = value; } + public int GetBoxWallpaper(int box) => BoxLayout.GetBoxWallpaper(box); + public void SetBoxWallpaper(int box, int wallpaper) => BoxLayout.SetBoxWallpaper(box, wallpaper); + public string GetBoxName(int box) => BoxLayout.GetBoxName(box); + public void SetBoxName(int box, ReadOnlySpan name) => BoxLayout.SetBoxName(box, name); public bool BattleBoxLocked { get => Blocks.BattleBox.Locked; set => Blocks.BattleBox.Locked = value; } + + public int DaycareCount => 2; + public IDaycareStorage this[int index] => Blocks.Daycare[index]; } diff --git a/PKHeX.Core/Saves/SAV6AODemo.cs b/PKHeX.Core/Saves/SAV6AODemo.cs index 3a82e72eb..0c91b6f4c 100644 --- a/PKHeX.Core/Saves/SAV6AODemo.cs +++ b/PKHeX.Core/Saves/SAV6AODemo.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// Generation 6 object for . /// /// -public sealed class SAV6AODemo : SAV6 +public sealed class SAV6AODemo : SAV6, ISaveBlock6Core { public SAV6AODemo(byte[] data) : base(data, SaveBlockAccessor6AODemo.BlockMetadataOffset) { @@ -27,8 +27,6 @@ public sealed class SAV6AODemo : SAV6 public override ushort MaxMoveID => Legal.MaxMoveID_6_AO; public override int MaxItemID => Legal.MaxItemID_6_AO; public override int MaxAbilityID => Legal.MaxAbilityID_6_AO; - protected override int EventWork => 0x04600; - protected override int EventFlag => EventWork + 0x2F0; public SaveBlockAccessor6AODemo Blocks { get; } private void Initialize() @@ -42,12 +40,16 @@ public sealed class SAV6AODemo : SAV6 public override int Vivillon { get => Blocks.Misc.Vivillon; set => Blocks.Misc.Vivillon = value; } // unused public override int Badges { get => Blocks.Misc.Badges; set => Blocks.Misc.Badges = value; } // unused public override int BP { get => Blocks.Misc.BP; set => Blocks.Misc.BP = value; } // unused - public override MyItem Items => Blocks.Items; + public override MyItem6AO Items => Blocks.Items; public override ItemInfo6 ItemInfo => Blocks.ItemInfo; public override GameTime6 GameTime => Blocks.GameTime; public override Situation6 Situation => Blocks.Situation; public override PlayTime6 Played => Blocks.Played; public override MyStatus6 Status => Blocks.Status; public override RecordBlock6 Records => Blocks.Records; + public override EventWork6 EventWork => Blocks.EventWork; public override IReadOnlyList AllBlocks => Blocks.BlockInfo; + + MyItem ISaveBlock6Core.Items => Items; + RecordBlock6 ISaveBlock6Core.Records => Records; } diff --git a/PKHeX.Core/Saves/SAV6XY.cs b/PKHeX.Core/Saves/SAV6XY.cs index dc66888ff..29e330542 100644 --- a/PKHeX.Core/Saves/SAV6XY.cs +++ b/PKHeX.Core/Saves/SAV6XY.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -8,7 +7,7 @@ namespace PKHeX.Core; /// Generation 6 object for . /// /// -public sealed class SAV6XY : SAV6, ISaveBlock6XY, IMultiplayerSprite +public sealed class SAV6XY : SAV6, ISaveBlock6XY, IMultiplayerSprite, IBoxDetailName, IBoxDetailWallpaper, IDaycareStorage, IDaycareEggState, IDaycareExperience, IDaycareRandomState { public SAV6XY(byte[] data) : base(data, SaveBlockAccessor6XY.BlockMetadataOffset) { @@ -31,33 +30,20 @@ public sealed class SAV6XY : SAV6, ISaveBlock6XY, IMultiplayerSprite public override int MaxItemID => Legal.MaxItemID_6_XY; public override int MaxAbilityID => Legal.MaxAbilityID_6_XY; - protected override int EventWork => 0x14A00; - protected override int EventFlag => EventWork + 0x2F0; + public override bool HasPokeDex => true; private void Initialize() { // Enable Features Party = 0x14200; - PCLayout = 0x4400; - BattleBoxOffset = 0x04A00; PSS = 0x05000; - PokeDex = 0x15000; HoF = 0x19400; - DaycareOffset = 0x1B200; - BerryField = 0x1B800; - WondercardFlags = 0x1BC00; + Box = 0x22600; JPEG = 0x57200; - - WondercardData = WondercardFlags + 0x100; - - // Extra Viewable Slots - Fused = 0x16000; - GTS = 0x17800; } - public int GTS { get; private set; } = int.MinValue; - public int Fused { get; private set; } = int.MinValue; + public const int BerryField = 0x1B800; #region Blocks public override IReadOnlyList AllBlocks => Blocks.BlockInfo; @@ -68,6 +54,9 @@ public sealed class SAV6XY : SAV6, ISaveBlock6XY, IMultiplayerSprite public override PlayTime6 Played => Blocks.Played; public override MyStatus6 Status => Blocks.Status; public override RecordBlock6 Records => Blocks.Records; + public override EventWork6 EventWork => Blocks.EventWork; + public UnionPokemon6 Fused => Blocks.Fused; + public GTS6 GTS => Blocks.GTS; public Puff6 Puff => Blocks.Puff; public OPower6 OPower => Blocks.OPower; public LinkBlock6 Link => Blocks.Link; @@ -86,29 +75,24 @@ public sealed class SAV6XY : SAV6, ISaveBlock6XY, IMultiplayerSprite protected override void SetDex(PKM pk) => Blocks.Zukan.SetDex(pk); - // Daycare - public override int DaycareSeedSize => 16; - public override bool HasTwoDaycares => false; - public override bool? IsDaycareOccupied(int loc, int slot) => Data[DaycareOffset + 0 + ((SIZE_STORED + 8) * slot)] == 1; - public override uint? GetDaycareEXP(int loc, int slot) => ReadUInt32LittleEndian(Data.AsSpan(DaycareOffset + 4 + ((SIZE_STORED + 8) * slot))); + // Daycare - delegate from block + public int DaycareSlotCount => Blocks.Daycare.DaycareSlotCount; + public Memory GetDaycareSlot(int index) => Blocks.Daycare.GetDaycareSlot(index); + public bool IsDaycareOccupied(int index) => Blocks.Daycare.IsDaycareOccupied(index); + public void SetDaycareOccupied(int index, bool occupied) => Blocks.Daycare.SetDaycareOccupied(index, occupied); + public uint GetDaycareEXP(int index) => Blocks.Daycare.GetDaycareEXP(index); + public void SetDaycareEXP(int index, uint exp) => Blocks.Daycare.SetDaycareEXP(index, exp); - public override int GetDaycareSlotOffset(int loc, int slot) => DaycareOffset + 8 + (slot * (SIZE_STORED + 8)); - public override bool? IsDaycareHasEgg(int loc) => Data[DaycareOffset + 0x1E0] == 1; - public override void SetDaycareHasEgg(int loc, bool hasEgg) => Data[DaycareOffset + 0x1E0] = hasEgg ? (byte)1 : (byte)0; - public override void SetDaycareOccupied(int loc, int slot, bool occupied) => Data[DaycareOffset + ((SIZE_STORED + 8) * slot)] = occupied ? (byte)1 : (byte)0; - public override void SetDaycareEXP(int loc, int slot, uint EXP) => WriteUInt32LittleEndian(Data.AsSpan(DaycareOffset + 4 + ((SIZE_STORED + 8) * slot)), EXP); - public override string GetDaycareRNGSeed(int loc) => Util.GetHexStringFromBytes(Data.AsSpan(DaycareOffset + 0x1E8, DaycareSeedSize / 2)); - - public override void SetDaycareRNGSeed(int loc, string seed) + public bool IsEggAvailable { - if (loc != 0) - return; - if (DaycareOffset < 0) - return; - if (seed.Length > DaycareSeedSize) - return; + get => Blocks.Daycare.IsEggAvailable; + set => Blocks.Daycare.IsEggAvailable = value; + } - Util.GetBytesFromHexString(seed).CopyTo(Data, DaycareOffset + 0x1E8); + ulong IDaycareRandomState.Seed + { + get => Blocks.Daycare.Seed; + set => Blocks.Daycare.Seed = value; } public override string JPEGTitle => !HasJPPEGData ? string.Empty : StringConverter6.GetString(Data.AsSpan(JPEG, 0x1A)); @@ -131,19 +115,18 @@ public sealed class SAV6XY : SAV6, ISaveBlock6XY, IMultiplayerSprite } public override bool IsVersionValid() => Version is GameVersion.X or GameVersion.Y; - - protected override bool[] MysteryGiftReceivedFlags { get => Blocks.MysteryGift.GetReceivedFlags(); set => Blocks.MysteryGift.SetReceivedFlags(value); } - protected override DataMysteryGift[] MysteryGiftCards { get => Blocks.MysteryGift.GetGifts(); set => Blocks.MysteryGift.SetGifts(value); } - public override bool GetCaught(ushort species) => Blocks.Zukan.GetCaught(species); public override bool GetSeen(ushort species) => Blocks.Zukan.GetSeen(species); public override void SetSeen(ushort species, bool seen) => Blocks.Zukan.SetSeen(species, seen); public override void SetCaught(ushort species, bool caught) => Blocks.Zukan.SetCaught(species, caught); public override int CurrentBox { get => Blocks.BoxLayout.CurrentBox; set => Blocks.BoxLayout.CurrentBox = value; } - protected override int GetBoxWallpaperOffset(int box) => Blocks.BoxLayout.GetBoxWallpaperOffset(box); public override int BoxesUnlocked { get => Blocks.BoxLayout.BoxesUnlocked; set => Blocks.BoxLayout.BoxesUnlocked = value; } public override byte[] BoxFlags { get => Blocks.BoxLayout.BoxFlags; set => Blocks.BoxLayout.BoxFlags = value; } + public int GetBoxWallpaper(int box) => BoxLayout.GetBoxWallpaper(box); + public void SetBoxWallpaper(int box, int wallpaper) => BoxLayout.SetBoxWallpaper(box, wallpaper); + public string GetBoxName(int box) => BoxLayout.GetBoxName(box); + public void SetBoxName(int box, ReadOnlySpan name) => BoxLayout.SetBoxName(box, name); public bool BattleBoxLocked { diff --git a/PKHeX.Core/Saves/SAV7.cs b/PKHeX.Core/Saves/SAV7.cs index 2a46c5dbc..b9f75ac36 100644 --- a/PKHeX.Core/Saves/SAV7.cs +++ b/PKHeX.Core/Saves/SAV7.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; /// /// Generation 7 object. /// -public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IRegionOrigin, IGameSync, IEventFlag37 +public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IRegionOrigin, IGameSync, IEventFlagProvider37, IBoxDetailName, IBoxDetailWallpaper, IDaycareStorage, IDaycareEggState, IDaycareRandomState, IMysteryGiftStorageProvider { // Save Data Attributes protected internal override string ShortSummary => $"{OT} ({Version}) - {Played.LastSavedTime}"; @@ -30,7 +29,7 @@ public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IReg protected void ReloadBattleTeams() { - var demo = this is SAV7SM && !Data.AsSpan(BoxLayout.Offset, 0x4C4).ContainsAnyExcept(0); // up to Battle Box values + var demo = this is SAV7SM && !BoxLayout.Data[..0x4C4].ContainsAnyExcept(0); // up to Battle Box values if (demo || !State.Exportable) { BoxLayout.ClearBattleTeams(); @@ -61,7 +60,9 @@ public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IReg public abstract ResortSave7 ResortSave { get; } public abstract FieldMenu7 FieldMenu { get; } public abstract FashionBlock7 Fashion { get; } - public abstract HallOfFame7 Fame { get; } + public abstract EventWork7 EventWork { get; } + public abstract UnionPokemon7 Fused { get; } + public abstract GTS7 GTS { get; } #endregion // Configuration @@ -74,12 +75,6 @@ public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IReg public override int MaxEV => EffortValues.Max252; public override byte Generation => 7; public override EntityContext Context => EntityContext.Gen7; - protected override int GiftCountMax => 48; - protected override int GiftFlagMax => 0x100 * 8; - public abstract int EventFlagCount { get; } - public int EventWorkCount => 1000; - private int EventWork => AllBlocks[05].Offset; - private int EventFlag => EventWork + (EventWorkCount * 2); // After Event Const (u16)*n public override int MaxStringLengthOT => 12; public override int MaxStringLengthNickname => 12; @@ -156,11 +151,10 @@ public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IReg // Storage public override int GetPartyOffset(int slot) => Party + (SIZE_PARTY * slot); public override int GetBoxOffset(int box) => Box + (SIZE_STORED * box * 30); - protected override int GetBoxWallpaperOffset(int box) => BoxLayout.GetBoxWallpaperOffset(box); - public override int GetBoxWallpaper(int box) => BoxLayout.GetBoxWallpaper(box); - public override void SetBoxWallpaper(int box, int value) => BoxLayout.SetBoxWallpaper(box, value); - public override string GetBoxName(int box) => BoxLayout[box]; - public override void SetBoxName(int box, ReadOnlySpan value) => BoxLayout.SetBoxName(box, value); + public int GetBoxWallpaper(int box) => BoxLayout.GetBoxWallpaper(box); + public void SetBoxWallpaper(int box, int value) => BoxLayout.SetBoxWallpaper(box, value); + public string GetBoxName(int box) => BoxLayout[box]; + public void SetBoxName(int box, ReadOnlySpan value) => BoxLayout.SetBoxName(box, value); public override int CurrentBox { get => BoxLayout.CurrentBox; set => BoxLayout.CurrentBox = value; } public override int BoxesUnlocked { get => BoxLayout.BoxesUnlocked; set => BoxLayout.BoxesUnlocked = value; } public override byte[] BoxFlags { get => BoxLayout.BoxFlags; set => BoxLayout.BoxFlags = value; } @@ -237,31 +231,24 @@ public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IReg return AllBlocks[08].Offset + (PokeCrypto.SIZE_6PARTY * slot); // 0x104*slot } - public override int DaycareSeedSize => Daycare7.DaycareSeedSize; // 128 bits - public override int GetDaycareSlotOffset(int loc, int slot) => Daycare.GetDaycareSlotOffset(slot); - public override bool? IsDaycareOccupied(int loc, int slot) => Daycare.GetIsOccupied(slot); - public override string GetDaycareRNGSeed(int loc) => Daycare.RNGSeed; - public override bool? IsDaycareHasEgg(int loc) => Daycare.HasEgg; - public override void SetDaycareOccupied(int loc, int slot, bool occupied) => Daycare.SetOccupied(slot, occupied); - public override void SetDaycareRNGSeed(int loc, string seed) => Daycare.RNGSeed = seed; - public override void SetDaycareHasEgg(int loc, bool hasEgg) => Daycare.HasEgg = hasEgg; + // Daycare - delegate from block + public int DaycareSlotCount => Daycare.DaycareSlotCount; + public Memory GetDaycareSlot(int index) => Daycare.GetDaycareSlot(index); + public bool IsDaycareOccupied(int index) => Daycare.IsDaycareOccupied(index); + public void SetDaycareOccupied(int index, bool occupied) => Daycare.SetDaycareOccupied(index, occupied); - protected override bool[] MysteryGiftReceivedFlags { get => MysteryGift.MysteryGiftReceivedFlags; set => MysteryGift.MysteryGiftReceivedFlags = value; } - protected override DataMysteryGift[] MysteryGiftCards { get => MysteryGift.MysteryGiftCards; set => MysteryGift.MysteryGiftCards = value; } - public bool GetEventFlag(int flagNumber) + public bool IsEggAvailable { - if ((uint)flagNumber >= EventFlagCount) - throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagCount})."); - return GetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7); + get => Daycare.IsEggAvailable; + set => Daycare.IsEggAvailable = value; } - public void SetEventFlag(int flagNumber, bool value) + UInt128 IDaycareRandomState.Seed { - if ((uint)flagNumber >= EventFlagCount) - throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagCount})."); - SetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7, value); + get => Daycare.Seed; + set => Daycare.Seed = value; } - public ushort GetWork(int index) => ReadUInt16LittleEndian(Data.AsSpan(EventWork + (index * 2))); - public void SetWork(int index, ushort value) => WriteUInt16LittleEndian(Data.AsSpan(EventWork)[(index * 2)..], value); + IEventFlag37 IEventFlagProvider37.EventWork => EventWork; + IMysteryGiftStorage IMysteryGiftStorageProvider.MysteryGiftStorage => MysteryGift; } diff --git a/PKHeX.Core/Saves/SAV7SM.cs b/PKHeX.Core/Saves/SAV7SM.cs index a049e8597..963bc2433 100644 --- a/PKHeX.Core/Saves/SAV7SM.cs +++ b/PKHeX.Core/Saves/SAV7SM.cs @@ -20,15 +20,13 @@ public sealed class SAV7SM : SAV7, ISaveBlock7SM ClearBoxes(); } + public override bool HasPokeDex => true; + private void Initialize() { Party = Blocks.BlockInfo[04].Offset; - PokeDex = Blocks.BlockInfo[06].Offset; - TeamSlots = Blocks.BoxLayout.TeamSlots; Box = Blocks.BlockInfo[14].Offset; - WondercardData = Blocks.MysteryGift.Offset; - DaycareOffset = Blocks.Daycare.Offset; ReloadBattleTeams(); } @@ -40,12 +38,12 @@ public sealed class SAV7SM : SAV7, ISaveBlock7SM #region Blocks public SaveBlockAccessor7SM Blocks { get; } public override IReadOnlyList AllBlocks => Blocks.BlockInfo; - public override MyItem Items => Blocks.Items; + public override MyItem7SM Items => Blocks.Items; public override MysteryBlock7 MysteryGift => Blocks.MysteryGift; public override PokeFinder7 PokeFinder => Blocks.PokeFinder; public override JoinFesta7 Festa => Blocks.Festa; public override Daycare7 Daycare => Blocks.Daycare; - public override RecordBlock6 Records => Blocks.Records; + public override RecordBlock7SM Records => Blocks.Records; public override PlayTime6 Played => Blocks.Played; public override MyStatus7 MyStatus => Blocks.MyStatus; public override FieldMoveModelSave7 Overworld => Blocks.Overworld; @@ -59,10 +57,11 @@ public sealed class SAV7SM : SAV7, ISaveBlock7SM public override ResortSave7 ResortSave => Blocks.ResortSave; public override FieldMenu7 FieldMenu => Blocks.FieldMenu; public override FashionBlock7 Fashion => Blocks.Fashion; - public override HallOfFame7 Fame => Blocks.Fame; + public override EventWork7SM EventWork => Blocks.EventWork; + public override UnionPokemon7 Fused => Blocks.Fused; + public override GTS7 GTS => Blocks.GTS; #endregion - public override int EventFlagCount => 4000; public override ushort MaxMoveID => Legal.MaxMoveID_7; public override ushort MaxSpeciesID => Legal.MaxSpeciesID_7; public override int MaxItemID => Legal.MaxItemID_7; @@ -72,7 +71,7 @@ public sealed class SAV7SM : SAV7, ISaveBlock7SM public void UpdateMagearnaConstant() { - var flag = GetEventFlag(3100); + var flag = EventWork.GetEventFlag(EventWork7SM.MagearnaEventFlag); // 3100 ulong value = flag ? MagearnaConst : 0ul; WriteUInt64LittleEndian(Data.AsSpan(Blocks.BlockInfo[35].Offset + 0x168), value); } diff --git a/PKHeX.Core/Saves/SAV7USUM.cs b/PKHeX.Core/Saves/SAV7USUM.cs index b77302703..d65b2728e 100644 --- a/PKHeX.Core/Saves/SAV7USUM.cs +++ b/PKHeX.Core/Saves/SAV7USUM.cs @@ -18,15 +18,13 @@ public sealed class SAV7USUM : SAV7, ISaveBlock7USUM Initialize(); } + public override bool HasPokeDex => true; + private void Initialize() { Party = Blocks.BlockInfo[04].Offset; - PokeDex = Blocks.BlockInfo[06].Offset; - TeamSlots = Blocks.BoxLayout.TeamSlots; Box = Blocks.BlockInfo[14].Offset; - WondercardData = Blocks.MysteryGift.Offset; - DaycareOffset = Blocks.Daycare.Offset; ReloadBattleTeams(); } @@ -34,7 +32,6 @@ public sealed class SAV7USUM : SAV7, ISaveBlock7USUM public override PersonalTable7 Personal => PersonalTable.USUM; public override ReadOnlySpan HeldItems => Legal.HeldItems_USUM; protected override SAV7USUM CloneInternal() => new((byte[])Data.Clone()); - public override int EventFlagCount => 4960; public override ushort MaxMoveID => Legal.MaxMoveID_7_USUM; public override ushort MaxSpeciesID => Legal.MaxSpeciesID_7_USUM; public override int MaxItemID => Legal.MaxItemID_7_USUM; @@ -45,12 +42,12 @@ public sealed class SAV7USUM : SAV7, ISaveBlock7USUM #region Blocks public SaveBlockAccessor7USUM Blocks { get; } public override IReadOnlyList AllBlocks => Blocks.BlockInfo; - public override MyItem Items => Blocks.Items; + public override MyItem7USUM Items => Blocks.Items; public override MysteryBlock7 MysteryGift => Blocks.MysteryGift; public override PokeFinder7 PokeFinder => Blocks.PokeFinder; public override JoinFesta7 Festa => Blocks.Festa; public override Daycare7 Daycare => Blocks.Daycare; - public override RecordBlock6 Records => Blocks.Records; + public override RecordBlock7USUM Records => Blocks.Records; public override PlayTime6 Played => Blocks.Played; public override MyStatus7 MyStatus => Blocks.MyStatus; public override FieldMoveModelSave7 Overworld => Blocks.Overworld; @@ -64,7 +61,9 @@ public sealed class SAV7USUM : SAV7, ISaveBlock7USUM public override ResortSave7 ResortSave => Blocks.ResortSave; public override FieldMenu7 FieldMenu => Blocks.FieldMenu; public override FashionBlock7 Fashion => Blocks.Fashion; - public override HallOfFame7 Fame => Blocks.Fame; + public override EventWork7USUM EventWork => Blocks.EventWork; + public override UnionPokemon7 Fused => Blocks.Fused; + public override GTS7 GTS => Blocks.GTS; public BattleAgency7 BattleAgency => Blocks.BattleAgency; #endregion } diff --git a/PKHeX.Core/Saves/SAV7b.cs b/PKHeX.Core/Saves/SAV7b.cs index 2382430c5..ab5cba001 100644 --- a/PKHeX.Core/Saves/SAV7b.cs +++ b/PKHeX.Core/Saves/SAV7b.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// /// Generation 7 object for games. /// -public sealed class SAV7b : SAV_BEEF, ISaveBlock7b, IGameSync, IEventFlagArray +public sealed class SAV7b : SAV_BEEF, ISaveBlock7b, IGameSync, IMysteryGiftStorageProvider { protected internal override string ShortSummary => $"{OT} ({Version}) - {Blocks.Played.LastSavedTime}"; public override string Extension => ".bin"; @@ -40,17 +40,16 @@ public sealed class SAV7b : SAV_BEEF, ISaveBlock7b, IGameSync, IEventFlagArray Initialize(); } + public override bool HasPokeDex => true; + private void Initialize() { - Box = Blocks.GetBlockOffset(BelugaBlockIndex.PokeListPokemon); - Party = Blocks.GetBlockOffset(BelugaBlockIndex.PokeListPokemon); - PokeDex = Blocks.GetBlockOffset(BelugaBlockIndex.Zukan); - - WondercardData = Blocks.GiftRecords.Offset; + Box = Blocks.BlockInfo[(int)BelugaBlockIndex.PokeListPokemon].Offset; + Party = Blocks.BlockInfo[(int)BelugaBlockIndex.PokeListPokemon].Offset; } // Save Block accessors - public MyItem Items => Blocks.Items; + public MyItem7b Items => Blocks.Items; public Coordinates7b Coordinates => Blocks.Coordinates; public Misc7b Misc => Blocks.Misc; public Zukan7b Zukan => Blocks.Zukan; @@ -60,6 +59,7 @@ public sealed class SAV7b : SAV_BEEF, ISaveBlock7b, IGameSync, IEventFlagArray public EventWork7b EventWork => Blocks.EventWork; public PokeListHeader Storage => Blocks.Storage; public WB7Records GiftRecords => Blocks.GiftRecords; + public Daycare7b Daycare => Blocks.Daycare; public CaptureRecords Captured => Blocks.Captured; public GoParkStorage Park => Blocks.Park; public PlayerGeoLocation7b PlayerGeoLocation => Blocks.PlayerGeoLocation; @@ -80,9 +80,6 @@ public sealed class SAV7b : SAV_BEEF, ISaveBlock7b, IGameSync, IEventFlagArray public override int MaxEV => EffortValues.Max252; public override int MaxStringLengthOT => 12; public override int MaxStringLengthNickname => 12; - protected override int GiftCountMax => 48; - protected override int GiftFlagMax => 0x100 * 8; - public int EventFlagCount => 4160; // 0xDC0 (true max may be up to 0x7F less. 23A8 starts u64 hashvals) public override bool HasParty => false; // handled via team slots @@ -125,9 +122,6 @@ public sealed class SAV7b : SAV_BEEF, ISaveBlock7b, IGameSync, IEventFlagArray return result; } - public override string GetBoxName(int box) => $"Box {box + 1}"; - public override void SetBoxName(int box, ReadOnlySpan value) { } - public override string GetString(ReadOnlySpan data) => StringConverter8.GetString(data); public override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) @@ -151,24 +145,7 @@ public sealed class SAV7b : SAV_BEEF, ISaveBlock7b, IGameSync, IEventFlagArray public override int PlayedMinutes { get => Blocks.Played.PlayedMinutes; set => Blocks.Played.PlayedMinutes = value; } public override int PlayedSeconds { get => Blocks.Played.PlayedSeconds; set => Blocks.Played.PlayedSeconds = value; } - /// - /// Gets the status of a desired Event Flag - /// - /// Event Flag to check - /// Flag is Set (true) or not Set (false) - public bool GetEventFlag(int flagNumber) => Blocks.EventWork.GetFlag(flagNumber); - - /// - /// Sets the status of a desired Event Flag - /// - /// Event Flag to check - /// Event Flag status to set - /// Flag is Set (true) or not Set (false) - public void SetEventFlag(int flagNumber, bool value) => Blocks.EventWork.SetFlag(flagNumber, value); - - protected override bool[] MysteryGiftReceivedFlags { get => Blocks.GiftRecords.GetFlags(); set => Blocks.GiftRecords.SetFlags(value); } - protected override DataMysteryGift[] MysteryGiftCards { get => Blocks.GiftRecords.GetRecords(); set => Blocks.GiftRecords.SetRecords((WR7[])value); } - public int GameSyncIDSize => MyStatus7b.GameSyncIDSize; // 64 bits public string GameSyncID { get => Blocks.Status.GameSyncID; set => Blocks.Status.GameSyncID = value; } + IMysteryGiftStorage IMysteryGiftStorageProvider.MysteryGiftStorage => Blocks.GiftRecords; } diff --git a/PKHeX.Core/Saves/SAV8BS.cs b/PKHeX.Core/Saves/SAV8BS.cs index 1b833aff1..f5ed326b7 100644 --- a/PKHeX.Core/Saves/SAV8BS.cs +++ b/PKHeX.Core/Saves/SAV8BS.cs @@ -8,7 +8,7 @@ namespace PKHeX.Core; /// /// Generation 8 object for games. /// -public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IEventFlagArray, IEventWorkArray +public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IEventWorkArray, IBoxDetailName, IBoxDetailWallpaper, IDaycareStorage, IDaycareEggState, IDaycareRandomState { // Save Data Attributes protected internal override string ShortSummary => $"{OT} ({Version}) - {System.LastSavedTime}"; @@ -20,58 +20,61 @@ public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IE return gen <= 8; }); + public SAV8BS() : this(new byte[SaveUtil.SIZE_G8BDSP_3], false) => SaveRevision = (int)Gem8Version.V1_3; + public SAV8BS(byte[] data, bool exportable = true) : base(data, exportable) { - FlagWork = new FlagWork8b(this, 0x00004); - Items = new MyItem8b(this, 0x0563C); - Underground = new UndergroundItemList8b(this, 0x111BC); - SelectBoundItems = new SaveItemShortcut8b(this, 0x14090); // size: 0x8 - PartyInfo = new Party8b(this, 0x14098); - BoxLayout = new BoxLayout8b(this, 0x148AA); // size: 0x64A + var Raw = Data.AsMemory(); + FlagWork = new FlagWork8b(this, Raw.Slice(0x00004, FlagWork8b.SIZE)); + Items = new MyItem8b(this, Raw.Slice(0x0563C, MyItem8b.SIZE)); + Underground = new UndergroundItemList8b(this, Raw.Slice(0x111BC, UndergroundItemList8b.SIZE)); + SelectBoundItems = new SaveItemShortcut8b(this, Raw.Slice(0x14090, 8)); + PartyInfo = new Party8b(this, Raw.Slice(0x14098, Party8b.SIZE)); + BoxLayout = new BoxLayout8b(this, Raw.Slice(0x148AA, 0x64A)); // 0x14EF4 - Box[40] // PLAYER_DATA: - Config = new ConfigSave8b(this, 0x79B74); // size: 0x40 - MyStatus = new MyStatus8b(this, 0x79BB4); // size: 0x50 - Played = new PlayTime8b(this, 0x79C04); // size: 0x04 - Contest = new Contest8b(this, 0x79C08); // size: 0x720 + Config = new ConfigSave8b(this, Raw.Slice(0x79B74, 0x40)); + MyStatus = new MyStatus8b(this, Raw.Slice(0x79BB4, 0x50)); + Played = new PlayTime8b(this, Raw.Slice(0x79C04, 0x04)); + Contest = new Contest8b(this, Raw.Slice(0x79C08, 0x720)); - Zukan = new Zukan8b(this, 0x7A328); // size: 0x30B8 - BattleTrainer = new BattleTrainerStatus8b(this, 0x7D3E0); // size: 0x1618 - MenuSelection = new MenuSelect8b(this, 0x7E9F8); // size: 0x44 - FieldObjects = new FieldObjectSave8b(this, 0x7EA3C); // size: 0x109A0 (1000 * 0x44) - Records = new Record8b(this, 0x8F3DC); // size: 0x78 * 12 - Encounter = new EncounterSave8b(this, 0x8F97C); // size: 0x188 - Player = new PlayerData8b(this, 0x8FB04); // 0x80 - SealDeco = new SealBallDecoData8b(this, 0x8FB84); // size: 0x4288 - SealList = new SealList8b(this, 0x93E0C); // size: 0x960 SaveSealData[200] - Random = new RandomGroup8b(this, 0x9476C); // size: 0x630 - FieldGimmick = new FieldGimmickSave8b(this, 0x94D9C); // FieldGimmickSaveData; int[3] gearRotate - BerryTrees = new BerryTreeGrowSave8b(this, 0x94DA8); // size: 0x808 - Poffins = new PoffinSaveData8b(this, 0x955B0); // size: 0x644 - BattleTower = new BattleTowerWork8b(this, 0x95BF4); // size: 0x1B8 - System = new SystemData8b(this, 0x95DAC); // size: 0x138 - Poketch = new Poketch8b(this, 0x95EE4); // todo - Daycare = new Daycare8b(this, 0x96080); // 0x2C0 + Zukan = new Zukan8b(this, Raw.Slice(0x7A328, 0x30B8)); + BattleTrainer = new BattleTrainerStatus8b(this, Raw.Slice(0x7D3E0, 0x1618)); + MenuSelection = new MenuSelect8b(this, Raw.Slice(0x7E9F8, 0x44)); + FieldObjects = new FieldObjectSave8b(this, Raw.Slice(0x7EA3C, 0x109A0)); + Records = new Record8b(this, Raw.Slice(0x8F3DC, 0x78 * 12)); + Encounter = new EncounterSave8b(this, Raw.Slice(0x8F97C, 0x188)); + Player = new PlayerData8b(this, Raw.Slice(0x8FB04, PlayerData8b.SIZE)); + SealDeco = new SealBallDecoData8b(this, Raw.Slice(0x8FB84, SealBallDecoData8b.SIZE)); + SealList = new SealList8b(this, Raw.Slice(0x93E0C, 0x960)); // size: 0x960 SaveSealData[200] + Random = new RandomGroup8b(this, Raw.Slice(0x9476C, 0x630)); // size: 0x630 + FieldGimmick = new FieldGimmickSave8b(this, Raw.Slice(0x94D9C, 0xC)); // FieldGimmickSaveData; int[3] gearRotate + BerryTrees = new BerryTreeGrowSave8b(this, Raw.Slice(0x94DA8, 0x808)); // size: 0x808 + Poffins = new PoffinSaveData8b(this, Raw.Slice(0x955B0, 0x644)); // size: 0x644 + BattleTower = new BattleTowerWork8b(this, Raw.Slice(0x95BF4, 0x1B8)); // size: 0x1B8 + System = new SystemData8b(this, Raw.Slice(0x95DAC, SystemData8b.SIZE)); + Poketch = new Poketch8b(this, Raw.Slice(0x95EE4, Poketch8b.SIZE)); + Daycare = new Daycare8b(this, Raw.Slice(0x96080, Daycare8b.SIZE)); // 0x96340 - _DENDOU_SAVEDATA; DENDOU_RECORD[30], POKEMON_DATA_INSIDE[6], ushort[4] ? // BadgeSaveData; byte[8] // BoukenNote; byte[24] // TV_DATA (int[48], TV_STR_DATA[42]), (int[37], bool[37])*2, (int[8], int[8]), TV_STR_DATA[10]; 144 128bit zeroed (900 bytes?)? - UgSaveData = new UgSaveData8b(this, 0x9A89C); // size: 0x27A0 + UgSaveData = new UgSaveData8b(this, Raw.Slice(0x9A89C, 0x27A0)); // 0x9D03C - GMS_DATA // size: 0x31304, (GMS_POINT_DATA[650], ushort, ushort, byte)?; substructure GMS_POINT_HISTORY_DATA[5] // 0xCE340 - PLAYER_NETWORK_DATA; bcatFlagArray byte[1300] - UnionSave = new UnionSaveData8b(this, 0xCEA10); // size: 0xC - ContestPhotoLanguage = new ContestPhotoLanguage8b(this, 0xCEA1C); // size: 0x18 - ZukanExtra = new ZukanSpinda8b(this, 0xCEA34); // size: 0x64 (100) + UnionSave = new UnionSaveData8b(this, Raw.Slice(0xCEA10, 0xC)); + ContestPhotoLanguage = new ContestPhotoLanguage8b(this, Raw.Slice(0xCEA1C, 0x18)); + ZukanExtra = new ZukanSpinda8b(this, Raw.Slice(0xCEA34, 0x64)); // CON_PHOTO_EXT_DATA[5] // GMS_POINT_HISTORY_EXT_DATA[3250] - UgCount = new UgCountRecord8b(this, 0xE8178); // size: 0x20 + UgCount = new UgCountRecord8b(this, Raw.Slice(0xE8178, 0x20)); // size: 0x20 // 0xE8198 - ReBuffnameData; RE_DENDOU_RECORD[30], RE_DENDOU_POKEMON_DATA_INSIDE[6] (0x20) = 0x1680 // 0xE9818 -- 0x10 byte[] MD5 hash of all savedata; // v1.1 additions - RecordAdd = new RecordAddData8b(this, 0xE9828); // size: 0x3C0 - MysteryRecords = new MysteryBlock8b(this, 0xE9BE8); // size: ??? + RecordAdd = new RecordAddData8b(this, GetSafe(Raw, 0xE9828, 0x3C0)); + MysteryRecords = new MysteryBlock8b(this, GetSafe(Raw, 0xE9BE8, MysteryBlock8b.MinSize)); // size: ??? // POKETCH_POKETORE_COUNT_ARRAY -- (u16 species, u16 unused, i32 count, i32 reserved, i32 reserved)[3] = 0x10bytes // PLAYREPORT_DATA -- reporting player progress online? 248 bytes? // MT_DATA mtData; -- 0x400 bytes @@ -85,14 +88,19 @@ public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IE Initialize(); } - public SAV8BS() : this(new byte[SaveUtil.SIZE_G8BDSP_3], false) => SaveRevision = (int)Gem8Version.V1_3; + private static Memory GetSafe(Memory src, int ofs, int len) + { + if (ofs + len > src.Length) + return new byte[len]; + return src.Slice(ofs, len); + } + + public override bool HasPokeDex => true; private void Initialize() { Box = 0x14EF4; - Party = PartyInfo.Offset; - PokeDex = Zukan.PokeDex; - DaycareOffset = Daycare.Offset; + Party = 1; ReloadBattleTeams(); TeamSlots = BoxLayout.TeamSlots; @@ -243,10 +251,6 @@ public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IE return StringConverter8.SetString(destBuffer, value, maxLength, option); } - public int EventFlagCount => FlagWork8b.COUNT_FLAG; - public bool GetEventFlag(int flagNumber) => FlagWork.GetFlag(flagNumber); - public void SetEventFlag(int flagNumber, bool value) => FlagWork.SetFlag(flagNumber, value); - // Player Information public override uint ID32 { get => MyStatus.ID32; set => MyStatus.ID32 = value; } public override ushort TID16 { get => MyStatus.TID16; set => MyStatus.TID16 = value; } @@ -267,11 +271,10 @@ public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IE // Storage public override int GetPartyOffset(int slot) => Party + (SIZE_PARTY * slot); public override int GetBoxOffset(int box) => Box + (SIZE_PARTY * box * 30); - protected override int GetBoxWallpaperOffset(int box) => BoxLayout.GetBoxWallpaperOffset(box); - public override int GetBoxWallpaper(int box) => BoxLayout.GetBoxWallpaper(box); - public override void SetBoxWallpaper(int box, int value) => BoxLayout.SetBoxWallpaper(box, value); - public override string GetBoxName(int box) => BoxLayout[box]; - public override void SetBoxName(int box, ReadOnlySpan value) => BoxLayout.SetBoxName(box, value); + public int GetBoxWallpaper(int box) => BoxLayout.GetBoxWallpaper(box); + public void SetBoxWallpaper(int box, int value) => BoxLayout.SetBoxWallpaper(box, value); + public string GetBoxName(int box) => BoxLayout[box]; + public void SetBoxName(int box, ReadOnlySpan value) => BoxLayout.SetBoxName(box, value); public override byte[] GetDataForBox(PKM pk) => pk.EncryptedPartyData; public override int CurrentBox { get => BoxLayout.CurrentBox; set => BoxLayout.CurrentBox = (byte)value; } public override int BoxesUnlocked { get => BoxLayout.BoxesUnlocked; set => BoxLayout.BoxesUnlocked = (byte)value; } @@ -344,21 +347,17 @@ public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IE public int RecordCount => Record8b.RecordCount; public int GetRecord(int recordID) => Records.GetRecord(recordID); - public int GetRecordOffset(int recordID) => Records.GetRecordOffset(recordID); + public int GetRecordOffset(int recordID) => Record8b.GetRecordOffset(recordID); public int GetRecordMax(int recordID) => Record8b.GetMax(recordID); public void SetRecord(int recordID, int value) => Records.SetRecord(recordID, value); #region Daycare - public override int DaycareSeedSize => 16; // 8byte - public override int GetDaycareSlotOffset(int loc, int slot) => Daycare.GetParentSlotOffset(slot); - public override uint? GetDaycareEXP(int loc, int slot) => 0; - public override bool? IsDaycareOccupied(int loc, int slot) => Daycare.GetDaycareSlotOccupied(slot); - public override bool? IsDaycareHasEgg(int loc) => Daycare.IsEggAvailable; - public override void SetDaycareEXP(int loc, int slot, uint EXP) { } - public override void SetDaycareOccupied(int loc, int slot, bool occupied) { } - public override void SetDaycareHasEgg(int loc, bool hasEgg) => Daycare.IsEggAvailable = hasEgg; - public override string GetDaycareRNGSeed(int loc) => $"{Daycare.DaycareSeed:X16}"; - public override void SetDaycareRNGSeed(int loc, string seed) => Daycare.DaycareSeed = Util.GetHexValue64(seed); + public int DaycareSlotCount => Daycare.DaycareSlotCount; + public bool IsDaycareOccupied(int slot) => Daycare.IsDaycareOccupied(slot); + public bool IsEggAvailable { get => Daycare.IsEggAvailable; set => Daycare.IsEggAvailable = value; } + public void SetDaycareOccupied(int slot, bool occupied) { } + public Memory GetDaycareSlot(int index) => Daycare.GetDaycareSlot(index); + ulong IDaycareRandomState.Seed { get => Daycare.Seed; set => Daycare.Seed = value; } #endregion public int EventWorkCount => FlagWork8b.COUNT_WORK; diff --git a/PKHeX.Core/Saves/SAV8LA.cs b/PKHeX.Core/Saves/SAV8LA.cs index 8115ccaf4..e963be5fa 100644 --- a/PKHeX.Core/Saves/SAV8LA.cs +++ b/PKHeX.Core/Saves/SAV8LA.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// /// Generation 8 object for games. /// -public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray, ISaveFileRevision +public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray, ISaveFileRevision, IBoxDetailName { protected internal override string ShortSummary => $"{OT} ({Version}) - {LastSaved.LastSavedTime}"; public override string Extension => string.Empty; @@ -130,11 +130,11 @@ public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray, ISaveFileRe protected override Span BoxBuffer => BoxInfo.Data; protected override Span PartyBuffer => PartyInfo.Data; + public override bool HasPokeDex => true; private void Initialize() { Box = 0; Party = 0; - PokeDex = 0; } public override int GetPartyOffset(int slot) => Party + (SIZE_PARTY * slot); @@ -184,8 +184,6 @@ public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray, ISaveFileRe public override IReadOnlyList Inventory { get => Items.Inventory; set => Items.Inventory = value; } #region Boxes - public override bool HasBoxWallpapers => false; - public override bool HasNamableBoxes => true; public override int CurrentBox { get => BoxLayout.CurrentBox; set => BoxLayout.CurrentBox = value; } public override int BoxesUnlocked { get => (byte)Blocks.GetBlockValue(SaveBlockAccessor8LA.KBoxesUnlocked); set => Blocks.SetBlockValue(SaveBlockAccessor8LA.KBoxesUnlocked, (byte)value); } @@ -209,10 +207,10 @@ public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray, ISaveFileRe } public override int GetBoxOffset(int box) => Box + (SIZE_BOXSLOT * box * 30); - public override string GetBoxName(int box) => BoxLayout.GetBoxName(box); - public override void SetBoxName(int box, ReadOnlySpan value) => BoxLayout.SetBoxName(box, value); + public string GetBoxName(int box) => BoxLayout[box]; + public void SetBoxName(int box, ReadOnlySpan value) => BoxLayout.SetBoxName(box, value); - public override int GetBoxWallpaper(int box) + public int GetBoxWallpaper(int box) { if ((uint)box >= BoxCount) return box; @@ -220,7 +218,7 @@ public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray, ISaveFileRe return b.Data[box]; } - public override void SetBoxWallpaper(int box, int value) + public void SetBoxWallpaper(int box, int value) { if ((uint)box >= BoxCount) return; diff --git a/PKHeX.Core/Saves/SAV8SWSH.cs b/PKHeX.Core/Saves/SAV8SWSH.cs index 4752ce9a1..912094627 100644 --- a/PKHeX.Core/Saves/SAV8SWSH.cs +++ b/PKHeX.Core/Saves/SAV8SWSH.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// /// Generation 8 object for games. /// -public sealed class SAV8SWSH : SaveFile, ISaveBlock8SWSH, ITrainerStatRecord, ISaveFileRevision, ISCBlockArray +public sealed class SAV8SWSH : SaveFile, ISaveBlock8SWSH, ITrainerStatRecord, ISaveFileRevision, ISCBlockArray, IBoxDetailName, IBoxDetailWallpaper { public SAV8SWSH(byte[] data) : this(SwishCrypto.Decrypt(data)) { } @@ -99,11 +99,11 @@ public sealed class SAV8SWSH : SaveFile, ISaveBlock8SWSH, ITrainerStatRecord, IS public override GameVersion MaxGameID => Legal.MaxGameID_8; public override int MaxAbilityID => m_abil; + public override bool HasPokeDex => true; private void Initialize() { Box = 0; Party = 0; - PokeDex = 0; TeamIndexes.LoadBattleTeams(); int rev = SaveRevision; @@ -183,8 +183,8 @@ public sealed class SAV8SWSH : SaveFile, ISaveBlock8SWSH, ITrainerStatRecord, IS // Storage public override int GetPartyOffset(int slot) => Party + (SIZE_PARTY * slot); public override int GetBoxOffset(int box) => Box + (SIZE_PARTY * box * 30); - public override string GetBoxName(int box) => BoxLayout[box]; - public override void SetBoxName(int box, ReadOnlySpan value) => BoxLayout.SetBoxName(box, value); + public string GetBoxName(int box) => BoxLayout[box]; + public void SetBoxName(int box, ReadOnlySpan value) => BoxLayout.SetBoxName(box, value); public override byte[] GetDataForBox(PKM pk) => pk.EncryptedPartyData; protected override void SetPKM(PKM pk, bool isParty = false) @@ -246,7 +246,7 @@ public sealed class SAV8SWSH : SaveFile, ISaveBlock8SWSH, ITrainerStatRecord, IS protected override Span BoxBuffer => BoxInfo.Data; protected override Span PartyBuffer => PartyInfo.Data; public override PK8 GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data)); - public override PK8 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.AsSpan(offset, SIZE_PARTY).ToArray()); // party format in boxes! + public override PK8 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.Slice(offset, SIZE_PARTY).ToArray()); // party format in boxes! public int GetRecord(int recordID) => Records.GetRecord(recordID); public void SetRecord(int recordID, int value) => Records.SetRecord(recordID, value); @@ -285,10 +285,7 @@ public sealed class SAV8SWSH : SaveFile, ISaveBlock8SWSH, ITrainerStatRecord, IS } } - public override bool HasBoxWallpapers => true; - public override bool HasNamableBoxes => true; - - public override int GetBoxWallpaper(int box) + public int GetBoxWallpaper(int box) { if ((uint)box >= BoxCount) return box; @@ -296,7 +293,7 @@ public sealed class SAV8SWSH : SaveFile, ISaveBlock8SWSH, ITrainerStatRecord, IS return b.Data[box]; } - public override void SetBoxWallpaper(int box, int value) + public void SetBoxWallpaper(int box, int value) { if ((uint)box >= BoxCount) return; diff --git a/PKHeX.Core/Saves/SAV9SV.cs b/PKHeX.Core/Saves/SAV9SV.cs index 2b0380e8a..e9e4ca27f 100644 --- a/PKHeX.Core/Saves/SAV9SV.cs +++ b/PKHeX.Core/Saves/SAV9SV.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// /// Generation 9 object for games. /// -public sealed class SAV9SV : SaveFile, ISaveBlock9Main, ISCBlockArray, ISaveFileRevision +public sealed class SAV9SV : SaveFile, ISaveBlock9Main, ISCBlockArray, ISaveFileRevision, IBoxDetailName, IBoxDetailWallpaper { protected internal override string ShortSummary => $"{OT} ({Version}) - {LastSaved.DisplayValue}"; public override string Extension => string.Empty; @@ -104,11 +104,12 @@ public sealed class SAV9SV : SaveFile, ISaveBlock9Main, ISCBlockArray, ISaveFile public override int MaxItemID => m_item; public override int MaxAbilityID => m_abil; + public override bool HasPokeDex => true; + private void Initialize() { Box = 0; Party = 0; - PokeDex = 0; TeamIndexes.LoadBattleTeams(); int rev = SaveRevision; @@ -189,8 +190,8 @@ public sealed class SAV9SV : SaveFile, ISaveBlock9Main, ISCBlockArray, ISaveFile // Storage public override int GetPartyOffset(int slot) => Party + (SIZE_PARTY * slot); public override int GetBoxOffset(int box) => Box + (SIZE_PARTY * box * 30); - public override string GetBoxName(int box) => BoxLayout[box]; - public override void SetBoxName(int box, ReadOnlySpan value) => BoxLayout.SetBoxName(box, value); + public string GetBoxName(int box) => BoxLayout[box]; + public void SetBoxName(int box, ReadOnlySpan value) => BoxLayout.SetBoxName(box, value); public override byte[] GetDataForBox(PKM pk) => pk.EncryptedPartyData; protected override void SetPKM(PKM pk, bool isParty = false) @@ -254,7 +255,7 @@ public sealed class SAV9SV : SaveFile, ISaveBlock9Main, ISCBlockArray, ISaveFile protected override Span BoxBuffer => BoxInfo.Data; protected override Span PartyBuffer => PartyInfo.Data; public override PK9 GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data)); - public override PK9 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.AsSpan(offset, SIZE_PARTY).ToArray()); // party format in boxes! + public override PK9 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.Slice(offset, SIZE_PARTY).ToArray()); // party format in boxes! //public int GetRecord(int recordID) => Records.GetRecord(recordID); //public void SetRecord(int recordID, int value) => Records.SetRecord(recordID, value); @@ -277,8 +278,6 @@ public sealed class SAV9SV : SaveFile, ISaveBlock9Main, ISCBlockArray, ISaveFile public override int CurrentBox { get => BoxLayout.CurrentBox; set => BoxLayout.CurrentBox = value; } public override int BoxesUnlocked { get => (byte)Blocks.GetBlockValue(SaveBlockAccessor9SV.KBoxesUnlocked); set => Blocks.SetBlockValue(SaveBlockAccessor9SV.KBoxesUnlocked, (byte)value); } - public override bool HasBoxWallpapers => true; - public override bool HasNamableBoxes => true; public Span Coordinates => Blocks.GetBlock(SaveBlockAccessor9SV.KCoordinates).Data; public float X { get => ReadSingleLittleEndian(Coordinates); set => WriteSingleLittleEndian(Coordinates, value); } public float Y { get => ReadSingleLittleEndian(Coordinates[4..]); set => WriteSingleLittleEndian(Coordinates[4..], value); } @@ -312,7 +311,7 @@ public sealed class SAV9SV : SaveFile, ISaveBlock9Main, ISCBlockArray, ISaveFile RW = rw; } - public override int GetBoxWallpaper(int box) + public int GetBoxWallpaper(int box) { if ((uint)box >= BoxCount) return box; @@ -320,7 +319,7 @@ public sealed class SAV9SV : SaveFile, ISaveBlock9Main, ISCBlockArray, ISaveFile return b.Data[box]; } - public override void SetBoxWallpaper(int box, int value) + public void SetBoxWallpaper(int box, int value) { if ((uint)box >= BoxCount) return; diff --git a/PKHeX.Core/Saves/SAV_STADIUM.cs b/PKHeX.Core/Saves/SAV_STADIUM.cs index 7beabb192..92d4e391b 100644 --- a/PKHeX.Core/Saves/SAV_STADIUM.cs +++ b/PKHeX.Core/Saves/SAV_STADIUM.cs @@ -49,8 +49,6 @@ public abstract class SAV_STADIUM : SaveFile, ILangDeviantSave protected sealed override byte[] DecryptPKM(byte[] data) => data; public sealed override int GetPartyOffset(int slot) => -1; - public override string GetBoxName(int box) => $"Box {box + 1}"; - public sealed override void SetBoxName(int box, ReadOnlySpan value) { } public sealed override bool ChecksumsValid => GetBoxChecksumsValid(); public sealed override string ChecksumInfo => ChecksumsValid ? "Checksum valid." : "Checksum invalid"; protected abstract void SetBoxChecksum(int box); diff --git a/PKHeX.Core/Saves/SaveFile.cs b/PKHeX.Core/Saves/SaveFile.cs index d76451dac..c2373c6d5 100644 --- a/PKHeX.Core/Saves/SaveFile.cs +++ b/PKHeX.Core/Saves/SaveFile.cs @@ -8,7 +8,7 @@ namespace PKHeX.Core; /// /// Base Class for Save Files /// -public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpaper, IBoxDetailName, IGeneration, IVersion +public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IGeneration, IVersion { // General Object Properties public byte[] Data; @@ -104,7 +104,7 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpa /// Offset to read from /// Bit index to read /// Flag is Set (true) or not Set (false) - public virtual bool GetFlag(int offset, int bitIndex) => FlagUtil.GetFlag(Data, offset, bitIndex); + public virtual bool GetFlag(int offset, int bitIndex) => GetFlag(Data, offset, bitIndex); /// /// Sets the status of the Flag at the specified offset and index. @@ -113,29 +113,17 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpa /// Bit index to read /// Flag status to set /// Flag is Set (true) or not Set (false) - public virtual void SetFlag(int offset, int bitIndex, bool value) => FlagUtil.SetFlag(Data, offset, bitIndex, value); + public virtual void SetFlag(int offset, int bitIndex, bool value) => SetFlag(Data, offset, bitIndex, value); + + public bool GetFlag(Span data, int offset, int bitIndex) => FlagUtil.GetFlag(data, offset, bitIndex); + public void SetFlag(Span data, int offset, int bitIndex, bool value) + { + FlagUtil.SetFlag(data, offset, bitIndex, value); + State.Edited = true; + } public virtual IReadOnlyList Inventory { get => []; set { } } - #region Mystery Gift - protected virtual int GiftCountMax => int.MinValue; - protected virtual int GiftFlagMax => 0x800; - protected int WondercardData { get; set; } = int.MinValue; - public bool HasWondercards => WondercardData > -1; - protected virtual bool[] MysteryGiftReceivedFlags { get => []; set { } } - protected virtual DataMysteryGift[] MysteryGiftCards { get => []; set { } } - - public virtual MysteryGiftAlbum GiftAlbum - { - get => new(MysteryGiftCards, MysteryGiftReceivedFlags); - set - { - MysteryGiftReceivedFlags = value.Flags; - MysteryGiftCards = value.Gifts; - } - } - #endregion - #region Player Info public virtual byte Gender { get; set; } public virtual int Language { get => -1; set { } } @@ -227,24 +215,6 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpa // Varied Methods protected abstract void SetChecksums(); - #region Daycare - public bool HasDaycare => DaycareOffset > -1; - protected int DaycareOffset { get; set; } = int.MinValue; - public virtual int DaycareSeedSize => 0; - public int DaycareIndex; - public virtual bool HasTwoDaycares => false; - public virtual int GetDaycareSlotOffset(int loc, int slot) => -1; - public virtual uint? GetDaycareEXP(int loc, int slot) => null; - public virtual string GetDaycareRNGSeed(int loc) => string.Empty; - public virtual bool? IsDaycareHasEgg(int loc) => null; - public virtual bool? IsDaycareOccupied(int loc, int slot) => null; - - public virtual void SetDaycareEXP(int loc, int slot, uint EXP) { } - public virtual void SetDaycareRNGSeed(int loc, string seed) { } - public virtual void SetDaycareHasEgg(int loc, bool hasEgg) { } - public virtual void SetDaycareOccupied(int loc, int slot, bool occupied) { } - #endregion - private Span GetPartySpan(int index) => PartyBuffer[GetPartyOffset(index)..]; public PKM GetPartySlotAtIndex(int index) => GetPartySlot(GetPartySpan(index)); @@ -395,8 +365,7 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpa #endregion #region Pokédex - public int PokeDex { get; protected set; } = int.MinValue; - public bool HasPokeDex => PokeDex > -1; + public virtual bool HasPokeDex => false; public virtual bool GetSeen(ushort species) => false; public virtual void SetSeen(ushort species, bool seen) { } public virtual bool GetCaught(ushort species) => false; @@ -612,16 +581,11 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpa int len = BoxSlotCount * SIZE_BOXSLOT; byte[] boxdata = storage.Slice(GetBoxOffset(0), len * BoxCount).ToArray(); // get all boxes - string[] boxNames = Get(GetBoxName, BoxCount); - int[] boxWallpapers = Get(GetBoxWallpaper, BoxCount); - static T[] Get(Func act, int count) - { - T[] result = new T[count]; - for (int i = 0; i < result.Length; i++) - result[i] = act(i); - return result; - } + if (this is IBoxDetailWallpaper w) + w.MoveWallpaper(box, insertBeforeBox); + if (this is IBoxDetailName n) + n.MoveBoxName(box, insertBeforeBox); min /= BoxSlotCount; max /= BoxSlotCount; @@ -638,8 +602,6 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpa } boxdata.AsSpan(len * i, len).CopyTo(storage[GetBoxOffset(b)..]); - SetBoxName(b, boxNames[i]); - SetBoxWallpaper(b, boxWallpapers[i]); } SlotPointerUtil.UpdateMove(box, insertBeforeBox, BoxSlotCount, SlotPointers); @@ -670,14 +632,12 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpa b1.CopyTo(boxData[b2o..]); // Name - string b1n = GetBoxName(box1); - SetBoxName(box1, GetBoxName(box2)); - SetBoxName(box2, b1n); + if (this is IBoxDetailName n) + n.SwapBoxName(box1, box2); // Wallpaper - int b1w = GetBoxWallpaper(box1); - SetBoxWallpaper(box1, GetBoxWallpaper(box2)); - SetBoxWallpaper(box2, b1w); + if (this is IBoxDetailWallpaper w) + w.SwapWallpaper(box1, box2); // Pointers SlotPointerUtil.UpdateSwap(box1, box2, BoxSlotCount, SlotPointers); @@ -809,31 +769,6 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpa } #endregion - #region Storage Name & Decoration - public virtual bool HasBoxWallpapers => GetBoxWallpaperOffset(0) > -1; - public virtual bool HasNamableBoxes => HasBoxWallpapers; - - public abstract string GetBoxName(int box); - public abstract void SetBoxName(int box, ReadOnlySpan value); - protected virtual int GetBoxWallpaperOffset(int box) => -1; - - public virtual int GetBoxWallpaper(int box) - { - int offset = GetBoxWallpaperOffset(box); - if (offset < 0 || (uint)box > BoxCount) - return box; - return Data[offset]; - } - - public virtual void SetBoxWallpaper(int box, int value) - { - int offset = GetBoxWallpaperOffset(box); - if (offset < 0 || (uint)box > BoxCount) - return; - Data[offset] = (byte)value; - } - #endregion - #region Box Binaries public byte[] GetPCBinary() => BoxData.SelectMany(GetDataForBox).ToArray(); public byte[] GetBoxBinary(int box) => GetBoxData(box).SelectMany(GetDataForBox).ToArray(); diff --git a/PKHeX.Core/Saves/Storage/Bank3.cs b/PKHeX.Core/Saves/Storage/Bank3.cs index da8bf35f2..782e1531c 100644 --- a/PKHeX.Core/Saves/Storage/Bank3.cs +++ b/PKHeX.Core/Saves/Storage/Bank3.cs @@ -5,7 +5,7 @@ namespace PKHeX.Core; /// /// Generation 3 object that reads exported data for Generation 3 PokeStock .gst dumps. /// -public sealed class Bank3 : BulkStorage +public sealed class Bank3 : BulkStorage, IBoxDetailNameRead { public Bank3(byte[] data) : base(data, typeof(PK3), 0) => Version = GameVersion.RS; @@ -22,6 +22,6 @@ public sealed class Bank3 : BulkStorage private int BoxDataSize => SlotsPerBox * SIZE_STORED; public override int GetBoxOffset(int box) => Box + (BoxDataSize * box); - public override string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxNameOffset(box), BoxNameSize)); + public string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxNameOffset(box), BoxNameSize)); private static int GetBoxNameOffset(int box) => 0x25800 + (9 * box); } diff --git a/PKHeX.Core/Saves/Storage/Bank4.cs b/PKHeX.Core/Saves/Storage/Bank4.cs index 8b1e10ae9..cc5d06f86 100644 --- a/PKHeX.Core/Saves/Storage/Bank4.cs +++ b/PKHeX.Core/Saves/Storage/Bank4.cs @@ -5,7 +5,7 @@ namespace PKHeX.Core; /// /// Generation 4 object that reads Generation 4 PokeStock .stk dumps. /// -public sealed class Bank4 : BulkStorage +public sealed class Bank4 : BulkStorage, IBoxDetailNameRead { public Bank4(byte[] data) : base(data, typeof(PK4), 0) => Version = GameVersion.HGSS; @@ -22,6 +22,6 @@ public sealed class Bank4 : BulkStorage private int BoxDataSize => SlotsPerBox * SIZE_STORED; public override int GetBoxOffset(int box) => Box + (BoxDataSize * box); - public override string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxNameOffset(box), BoxNameSize / 2)); + public string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxNameOffset(box), BoxNameSize / 2)); private static int GetBoxNameOffset(int box) => 0x3FC00 + (0x19 * box); } diff --git a/PKHeX.Core/Saves/Storage/Bank7.cs b/PKHeX.Core/Saves/Storage/Bank7.cs index 8eac84a85..09ead8e2a 100644 --- a/PKHeX.Core/Saves/Storage/Bank7.cs +++ b/PKHeX.Core/Saves/Storage/Bank7.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// /// Generation 7 object that reads from Pokémon Bank savedata (stored on AWS). /// -public sealed class Bank7 : BulkStorage +public sealed class Bank7 : BulkStorage, IBoxDetailNameRead { public Bank7(byte[] data, Type t, [ConstantExpected] int start, int slotsPerBox = 30) : base(data, t, start, slotsPerBox) => Version = GameVersion.USUM; @@ -47,7 +47,7 @@ public sealed class Bank7 : BulkStorage private int BoxDataSize => (SlotsPerBox * SIZE_STORED) + BankNameSpacing; public override int GetBoxOffset(int box) => Box + (BoxDataSize * box); - public override string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxNameOffset(box), BankNameSize / 2)); + public string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxNameOffset(box), BankNameSize / 2)); public int GetBoxNameOffset(int box) => GetBoxOffset(box) + (SlotsPerBox * SIZE_STORED); public int GetBoxIndex(int box) => ReadUInt16LittleEndian(Data.AsSpan(GetBoxNameOffset(box) + BankNameSize)); diff --git a/PKHeX.Core/Saves/Storage/BulkStorage.cs b/PKHeX.Core/Saves/Storage/BulkStorage.cs index 47912b2b7..22ed5d709 100644 --- a/PKHeX.Core/Saves/Storage/BulkStorage.cs +++ b/PKHeX.Core/Saves/Storage/BulkStorage.cs @@ -50,8 +50,6 @@ public abstract class BulkStorage : SaveFile protected override void SetChecksums() { } public override int GetBoxOffset(int box) => Box + (box * (SlotsPerBox * SIZE_STORED)); - public override string GetBoxName(int box) => $"Box {box + 1:d2}"; - public sealed override void SetBoxName(int box, ReadOnlySpan value) { } public sealed override int GetPartyOffset(int slot) => int.MinValue; public override string GetString(ReadOnlySpan data) diff --git a/PKHeX.Core/Saves/Substructures/Daycare/IDaycareEggState.cs b/PKHeX.Core/Saves/Substructures/Daycare/IDaycareEggState.cs new file mode 100644 index 000000000..c751ac586 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Daycare/IDaycareEggState.cs @@ -0,0 +1,6 @@ +namespace PKHeX.Core; + +public interface IDaycareEggState +{ + bool IsEggAvailable { get; set; } +} diff --git a/PKHeX.Core/Saves/Substructures/Daycare/IDaycareExperience.cs b/PKHeX.Core/Saves/Substructures/Daycare/IDaycareExperience.cs new file mode 100644 index 000000000..b54015d02 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Daycare/IDaycareExperience.cs @@ -0,0 +1,7 @@ +namespace PKHeX.Core; + +public interface IDaycareExperience +{ + uint GetDaycareEXP(int index); + void SetDaycareEXP(int index, uint value); +} diff --git a/PKHeX.Core/Saves/Substructures/Daycare/IDaycareMulti.cs b/PKHeX.Core/Saves/Substructures/Daycare/IDaycareMulti.cs new file mode 100644 index 000000000..61c4e5473 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Daycare/IDaycareMulti.cs @@ -0,0 +1,8 @@ +namespace PKHeX.Core; + +public interface IDaycareMulti +{ + int DaycareCount { get; } + + IDaycareStorage this[int index] { get; } +} diff --git a/PKHeX.Core/Saves/Substructures/Daycare/IDaycareRandomState.cs b/PKHeX.Core/Saves/Substructures/Daycare/IDaycareRandomState.cs new file mode 100644 index 000000000..693228757 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Daycare/IDaycareRandomState.cs @@ -0,0 +1,6 @@ +namespace PKHeX.Core; + +public interface IDaycareRandomState +{ + T Seed { get; set; } +} diff --git a/PKHeX.Core/Saves/Substructures/Daycare/IDaycareStorage.cs b/PKHeX.Core/Saves/Substructures/Daycare/IDaycareStorage.cs new file mode 100644 index 000000000..d1057111f --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Daycare/IDaycareStorage.cs @@ -0,0 +1,16 @@ +using System; + +namespace PKHeX.Core; + +public interface IDaycareStorage +{ + int DaycareSlotCount { get; } + + /// + /// Gets the segment of memory that holds a stored entity. + /// + Memory GetDaycareSlot(int index); + + bool IsDaycareOccupied(int index); + void SetDaycareOccupied(int index, bool occupied); +} diff --git a/PKHeX.Core/Saves/Substructures/Gen3/Roamer3.cs b/PKHeX.Core/Saves/Substructures/Gen3/Roamer3.cs index 94caa4eaa..3f4034fc6 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/Roamer3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/Roamer3.cs @@ -5,62 +5,63 @@ namespace PKHeX.Core; public sealed class Roamer3 : IContestStats { - private readonly int Offset; + public const int SIZE = 0x14; public bool IsGlitched { get; } - private readonly byte[] Data; - private readonly SAV3 SAV; + private readonly Memory Raw; + private Span Data => Raw.Span; public Roamer3(SAV3 sav) { - Data = (SAV = sav).Large; - Offset = sav.Version switch + var buffer = sav.Large; + var offset = sav.Version switch { GameVersion.RS => 0x3144, GameVersion.E => 0x31DC, _ => 0x30D0, // FRLG }; + Raw = buffer.AsMemory(offset, SIZE); IsGlitched = sav.Version != GameVersion.E; } public uint IV32 { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset), value); + get => ReadUInt32LittleEndian(Data); + set => WriteUInt32LittleEndian(Data, value); } public uint PID { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 4)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 4), value); + get => ReadUInt32LittleEndian(Data[4..]); + set => WriteUInt32LittleEndian(Data[4..], value); } public ushort Species { - get => SpeciesConverter.GetNational3(ReadUInt16LittleEndian(Data.AsSpan(Offset + 8))); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 8), SpeciesConverter.GetInternal3(value)); + get => SpeciesConverter.GetNational3(ReadUInt16LittleEndian(Data[8..])); + set => WriteUInt16LittleEndian(Data[8..], SpeciesConverter.GetInternal3(value)); } public int HP_Current { - get => ReadInt16LittleEndian(Data.AsSpan(Offset + 10)); - set => WriteInt16LittleEndian(Data.AsSpan(Offset + 10), (short)value); + get => ReadInt16LittleEndian(Data[10..]); + set => WriteInt16LittleEndian(Data[10..], (short)value); } public byte CurrentLevel { - get => Data[Offset + 12]; - set => Data[Offset + 12] = value; + get => Data[12]; + set => Data[12] = value; } - public int Status { get => Data[Offset + 0x0D]; set => Data[Offset + 0x0D] = (byte)value; } + public int Status { get => Data[0x0D]; set => Data[0x0D] = (byte)value; } - public byte ContestCool { get => Data[Offset + 0x0E]; set => Data[Offset + 0x0E] = value; } - public byte ContestBeauty { get => Data[Offset + 0x0F]; set => Data[Offset + 0x0F] = value; } - public byte ContestCute { get => Data[Offset + 0x10]; set => Data[Offset + 0x10] = value; } - public byte ContestSmart { get => Data[Offset + 0x11]; set => Data[Offset + 0x11] = value; } - public byte ContestTough { get => Data[Offset + 0x12]; set => Data[Offset + 0x12] = value; } + public byte ContestCool { get => Data[0x0E]; set => Data[0x0E] = value; } + public byte ContestBeauty { get => Data[0x0F]; set => Data[0x0F] = value; } + public byte ContestCute { get => Data[0x10]; set => Data[0x10] = value; } + public byte ContestSmart { get => Data[0x11]; set => Data[0x11] = value; } + public byte ContestTough { get => Data[0x12]; set => Data[0x12] = value; } public byte ContestSheen { get => 0; set { } } - public bool Active { get => Data[Offset + 0x13] == 1; set => Data[Offset + 0x13] = value ? (byte)1 : (byte)0; } + public bool Active { get => Data[0x13] == 1; set => Data[0x13] = value ? (byte)1 : (byte)0; } // Derived Properties private int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | (uint)((value > 31 ? 31 : value) << 00); } @@ -92,13 +93,14 @@ public sealed class Roamer3 : IContestStats } /// - /// Indicates if the Roamer is shiny with the 's Trainer Details. + /// Indicates if the Roamer is shiny with the 's Trainer Details. /// /// PID to check for + /// Trainer to check for /// Indication if the PID is shiny for the trainer. - public bool IsShiny(uint pid) + public static bool IsShiny(uint pid, ITrainerID32 tr) { - var tmp = SAV.ID32 ^ pid; + var tmp = tr.ID32 ^ pid; var xor = (tmp >> 16) ^ (tmp & 0xFFFF); return xor < 8; } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/SecretBase3.cs b/PKHeX.Core/Saves/Substructures/Gen3/SecretBase3.cs index e3c25676a..e0bea6a1c 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/SecretBase3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/SecretBase3.cs @@ -3,61 +3,63 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class SecretBase3(byte[] Data, int Offset) +public sealed class SecretBase3(Memory raw) { + private Span Data => raw.Span; + private bool Japanese => Language == (int) LanguageID.Japanese; - public int SecretBaseLocation { get => Data[Offset + 0]; set => Data[Offset + 0] = (byte) value; } + public int SecretBaseLocation { get => Data[0]; set => Data[0] = (byte) value; } public byte OriginalTrainerGender { - get => (byte)((Data[Offset + 1] >> 4) & 1); - set => Data[Offset + 1] = (byte) ((Data[Offset + 1] & 0xEF) | ((value & 1) << 4)); + get => (byte)((Data[1] >> 4) & 1); + set => Data[1] = (byte) ((Data[1] & 0xEF) | ((value & 1) << 4)); } public bool BattledToday { - get => ((Data[Offset + 1] >> 5) & 1) == 1; - set => Data[Offset + 1] = (byte)((Data[Offset + 1] & 0xDF) | ((value ? 1 : 0) << 5)); + get => ((Data[1] >> 5) & 1) == 1; + set => Data[1] = (byte)((Data[1] & 0xDF) | ((value ? 1 : 0) << 5)); } public int RegistryStatus { - get => (Data[Offset + 1] >> 6) & 3; - set => Data[Offset + 1] = (byte)((Data[Offset + 1] & 0x3F) | ((value & 3) << 6)); + get => (Data[1] >> 6) & 3; + set => Data[1] = (byte)((Data[1] & 0x3F) | ((value & 3) << 6)); } public string OriginalTrainerName { - get => StringConverter3.GetString(Data.AsSpan(Offset + 2, 7), Japanese); - set => StringConverter3.SetString(Data.AsSpan(Offset + 2, 7), value, 7, Japanese, StringConverterOption.ClearFF); + get => StringConverter3.GetString(Data.Slice(2, 7), Japanese); + set => StringConverter3.SetString(Data.Slice(2, 7), value, 7, Japanese, StringConverterOption.ClearFF); } public uint OT_ID { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 9)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 9), value); + get => ReadUInt32LittleEndian(Data[9..]); + set => WriteUInt32LittleEndian(Data[9..], value); } - public int OT_Class => Data[Offset + 9] % 5; - public int Language { get => Data[Offset + 0x0D]; set => Data[Offset + 0x0D] = (byte)value; } + public int OT_Class => Data[9] % 5; + public int Language { get => Data[0x0D]; set => Data[0x0D] = (byte)value; } public ushort SecretBasesReceived { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x0E)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x0E), value); + get => ReadUInt16LittleEndian(Data[0x0E..]); + set => WriteUInt16LittleEndian(Data[0x0E..], value); } - public byte TimesEntered { get => Data[Offset + 0x10]; set => Data[Offset + 0x10] = value; } - public int Unused11 { get => Data[Offset + 0x11]; set => Data[Offset + 0x11] = (byte)value; } // alignment padding + public byte TimesEntered { get => Data[0x10]; set => Data[0x10] = value; } + public int Unused11 { get => Data[0x11]; set => Data[0x11] = (byte)value; } // alignment padding - public Span GetDecorations() => Data.AsSpan(Offset + 0x12, 0x10); + public Span GetDecorations() => Data.Slice(0x12, 0x10); public void SetDecorations(Span value) => value.CopyTo(GetDecorations()); - public Span GetDecorationCoordinates() => Data.AsSpan(Offset + 0x22, 0x10); + public Span GetDecorationCoordinates() => Data.Slice(0x22, 0x10); public void SetDecorationCoordinates(Span value) => value.CopyTo(GetDecorationCoordinates()); - private Span TeamData => Data.AsSpan(Offset + 50, 72); + private Span TeamData => Data.Slice(50, 72); public SecretBase3Team Team { get => new(TeamData.ToArray()); diff --git a/PKHeX.Core/Saves/Substructures/Gen4/Chatter4.cs b/PKHeX.Core/Saves/Substructures/Gen4/Chatter4.cs index b26cdff57..b12ff1bd4 100644 --- a/PKHeX.Core/Saves/Substructures/Gen4/Chatter4.cs +++ b/PKHeX.Core/Saves/Substructures/Gen4/Chatter4.cs @@ -6,15 +6,15 @@ namespace PKHeX.Core; /// /// Generation 4 Chatter Recording /// -public sealed class Chatter4(SAV4 SAV, int offset) : SaveBlock(SAV, offset), IChatter +public sealed class Chatter4(SAV4 SAV, Memory raw) : SaveBlock(SAV, raw), IChatter { public bool Initialized { - get => ReadUInt32LittleEndian(SAV.General[Offset..]) == 1u; - set => WriteUInt32LittleEndian(SAV.General[Offset..], value ? 1u : 0u); + get => ReadUInt32LittleEndian(Data) == 1u; + set => WriteUInt32LittleEndian(Data, value ? 1u : 0u); } - public Span Recording => SAV.General.Slice(Offset + sizeof(uint), IChatter.SIZE_PCM); + public Span Recording => Data.Slice(sizeof(uint), IChatter.SIZE_PCM); public int ConfusionChance => !Initialized ? 1 : (sbyte)Recording[15] switch { diff --git a/PKHeX.Core/Saves/Substructures/Gen4/Dendou4.cs b/PKHeX.Core/Saves/Substructures/Gen4/Dendou4.cs index 44df49979..00458a37f 100644 --- a/PKHeX.Core/Saves/Substructures/Gen4/Dendou4.cs +++ b/PKHeX.Core/Saves/Substructures/Gen4/Dendou4.cs @@ -3,7 +3,7 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class Dendou4 +public sealed class Dendou4(Memory raw) { private const int SIZE = 0x2AB0; private const int SIZE_FOOTER = 0x10; @@ -12,10 +12,7 @@ public sealed class Dendou4 public const int MaxClears = 9999; public const int MaxRecords = 30; - private readonly byte[] Raw; - private readonly int Offset; - private Span Data => Raw.AsSpan(Offset, SIZE_BLOCK); - public Dendou4(byte[] data, int offset) => (Raw, Offset) = (data, offset); + private Span Data => raw.Span; // Structure: // record[30] records diff --git a/PKHeX.Core/Saves/Substructures/Gen5/BattleBox5.cs b/PKHeX.Core/Saves/Substructures/Gen5/BattleBox5.cs new file mode 100644 index 000000000..6b72b48a0 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen5/BattleBox5.cs @@ -0,0 +1,23 @@ +using System; + +namespace PKHeX.Core; + +public sealed class BattleBox5(SAV5 sav, Memory raw) : SaveBlock(sav, raw) +{ + private const int SizeStored = PokeCrypto.SIZE_5STORED; + public const int Count = 6; + + public Memory GetSlot(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, Count); + return Raw.Slice(index * SizeStored, SizeStored); + } + + public Memory this [int index] => GetSlot(index); + + public bool BattleBoxLocked + { + get => Data[0x358] != 0; // Wi-Fi/Live Tournament Active + set => Data[0x358] = value ? (byte)1 : (byte)0; + } +} diff --git a/PKHeX.Core/Saves/Substructures/Gen5/BattleSubway5.cs b/PKHeX.Core/Saves/Substructures/Gen5/BattleSubway5.cs index abfb46bae..72d3e0ae3 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/BattleSubway5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/BattleSubway5.cs @@ -3,12 +3,10 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class BattleSubway5(SAV5 sav, int offset) : SaveBlock(sav, offset) +public sealed class BattleSubway5(SAV5 sav, Memory raw) : SaveBlock(sav, raw) { - private Span Raw => Data.AsSpan(Offset); - - public int BP { get => ReadUInt16LittleEndian(Raw); set => WriteUInt16LittleEndian(Raw, (ushort)value); } - public int Flags { get => Raw[0x04]; set => Raw[0x04] = (byte)value; } + public int BP { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, (ushort)value); } + public int Flags { get => Data[0x04]; set => Data[0x04] = (byte)value; } public bool Flag0 { get => ((Flags >> 0) & 1) != 0; set => Flags = (Flags & ~(1 << 0)) | ((value ? 1 : 0) << 0); } public bool Flag1 { get => ((Flags >> 1) & 1) != 0; set => Flags = (Flags & ~(1 << 1)) | ((value ? 1 : 0) << 1); } public bool Flag2 { get => ((Flags >> 2) & 1) != 0; set => Flags = (Flags & ~(1 << 2)) | ((value ? 1 : 0) << 2); } @@ -18,20 +16,20 @@ public sealed class BattleSubway5(SAV5 sav, int offset) : SaveBlock(sav, o public bool SuperMulti { get => ((Flags >> 6) & 1) != 0; set => Flags = (Flags & ~(1 << 6)) | ((value ? 1 : 0) << 6); } public bool Flag7 { get => ((Flags >> 7) & 1) != 0; set => Flags = (Flags & ~(1 << 7)) | ((value ? 1 : 0) << 7); } - public int SinglePast { get => ReadUInt16LittleEndian(Raw[0x08..]); set => WriteUInt16LittleEndian(Raw[0x08..], (ushort)value); } - public int DoublePast { get => ReadUInt16LittleEndian(Raw[0x0A..]); set => WriteUInt16LittleEndian(Raw[0x0A..], (ushort)value); } - public int MultiNPCPast { get => ReadUInt16LittleEndian(Raw[0x0C..]); set => WriteUInt16LittleEndian(Raw[0x0C..], (ushort)value); } - public int MultiFriendsPast { get => ReadUInt16LittleEndian(Raw[0x0E..]); set => WriteUInt16LittleEndian(Raw[0x0E..], (ushort)value); } - public int SuperSinglePast { get => ReadUInt16LittleEndian(Raw[0x12..]); set => WriteUInt16LittleEndian(Raw[0x12..], (ushort)value); } - public int SuperDoublePast { get => ReadUInt16LittleEndian(Raw[0x14..]); set => WriteUInt16LittleEndian(Raw[0x14..], (ushort)value); } - public int SuperMultiNPCPast { get => ReadUInt16LittleEndian(Raw[0x16..]); set => WriteUInt16LittleEndian(Raw[0x16..], (ushort)value); } - public int SuperMultiFriendsPast { get => ReadUInt16LittleEndian(Raw[0x18..]); set => WriteUInt16LittleEndian(Raw[0x18..], (ushort)value); } - public int SingleRecord { get => ReadUInt16LittleEndian(Raw[0x1A..]); set => WriteUInt16LittleEndian(Raw[0x1A..], (ushort)value); } - public int DoubleRecord { get => ReadUInt16LittleEndian(Raw[0x1C..]); set => WriteUInt16LittleEndian(Raw[0x1C..], (ushort)value); } - public int MultiNPCRecord { get => ReadUInt16LittleEndian(Raw[0x1E..]); set => WriteUInt16LittleEndian(Raw[0x1E..], (ushort)value); } - public int MultiFriendsRecord { get => ReadUInt16LittleEndian(Raw[0x20..]); set => WriteUInt16LittleEndian(Raw[0x20..], (ushort)value); } - public int SuperSingleRecord { get => ReadUInt16LittleEndian(Raw[0x24..]); set => WriteUInt16LittleEndian(Raw[0x24..], (ushort)value); } - public int SuperDoubleRecord { get => ReadUInt16LittleEndian(Raw[0x26..]); set => WriteUInt16LittleEndian(Raw[0x26..], (ushort)value); } - public int SuperMultiNPCRecord { get => ReadUInt16LittleEndian(Raw[0x28..]); set => WriteUInt16LittleEndian(Raw[0x28..], (ushort)value); } - public int SuperMultiFriendsRecord { get => ReadUInt16LittleEndian(Raw[0x2A..]); set => WriteUInt16LittleEndian(Raw[0x2A..], (ushort)value); } + public int SinglePast { get => ReadUInt16LittleEndian(Data[0x08..]); set => WriteUInt16LittleEndian(Data[0x08..], (ushort)value); } + public int DoublePast { get => ReadUInt16LittleEndian(Data[0x0A..]); set => WriteUInt16LittleEndian(Data[0x0A..], (ushort)value); } + public int MultiNPCPast { get => ReadUInt16LittleEndian(Data[0x0C..]); set => WriteUInt16LittleEndian(Data[0x0C..], (ushort)value); } + public int MultiFriendsPast { get => ReadUInt16LittleEndian(Data[0x0E..]); set => WriteUInt16LittleEndian(Data[0x0E..], (ushort)value); } + public int SuperSinglePast { get => ReadUInt16LittleEndian(Data[0x12..]); set => WriteUInt16LittleEndian(Data[0x12..], (ushort)value); } + public int SuperDoublePast { get => ReadUInt16LittleEndian(Data[0x14..]); set => WriteUInt16LittleEndian(Data[0x14..], (ushort)value); } + public int SuperMultiNPCPast { get => ReadUInt16LittleEndian(Data[0x16..]); set => WriteUInt16LittleEndian(Data[0x16..], (ushort)value); } + public int SuperMultiFriendsPast { get => ReadUInt16LittleEndian(Data[0x18..]); set => WriteUInt16LittleEndian(Data[0x18..], (ushort)value); } + public int SingleRecord { get => ReadUInt16LittleEndian(Data[0x1A..]); set => WriteUInt16LittleEndian(Data[0x1A..], (ushort)value); } + public int DoubleRecord { get => ReadUInt16LittleEndian(Data[0x1C..]); set => WriteUInt16LittleEndian(Data[0x1C..], (ushort)value); } + public int MultiNPCRecord { get => ReadUInt16LittleEndian(Data[0x1E..]); set => WriteUInt16LittleEndian(Data[0x1E..], (ushort)value); } + public int MultiFriendsRecord { get => ReadUInt16LittleEndian(Data[0x20..]); set => WriteUInt16LittleEndian(Data[0x20..], (ushort)value); } + public int SuperSingleRecord { get => ReadUInt16LittleEndian(Data[0x24..]); set => WriteUInt16LittleEndian(Data[0x24..], (ushort)value); } + public int SuperDoubleRecord { get => ReadUInt16LittleEndian(Data[0x26..]); set => WriteUInt16LittleEndian(Data[0x26..], (ushort)value); } + public int SuperMultiNPCRecord { get => ReadUInt16LittleEndian(Data[0x28..]); set => WriteUInt16LittleEndian(Data[0x28..], (ushort)value); } + public int SuperMultiFriendsRecord { get => ReadUInt16LittleEndian(Data[0x2A..]); set => WriteUInt16LittleEndian(Data[0x2A..], (ushort)value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/BoxLayout5.cs b/PKHeX.Core/Saves/Substructures/Gen5/BoxLayout5.cs index 9f4d86117..0e2d12250 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/BoxLayout5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/BoxLayout5.cs @@ -2,11 +2,11 @@ using System; namespace PKHeX.Core; -public sealed class BoxLayout5(SAV5 sav, int offset) : SaveBlock(sav, offset) +public sealed class BoxLayout5(SAV5 sav, Memory raw) : SaveBlock(sav, raw) { - public int CurrentBox { get => Data[Offset]; set => Data[Offset] = (byte)value; } - public int GetBoxNameOffset(int box) => Offset + (0x28 * box) + 4; - public int GetBoxWallpaperOffset(int box) => Offset + 0x3C4 + box; + public int CurrentBox { get => Data[0]; set => Data[0] = (byte)value; } + public int GetBoxNameOffset(int box) => (0x28 * box) + 4; + public int GetBoxWallpaperOffset(int box) => 0x3C4 + box; public int GetBoxWallpaper(int box) { @@ -22,7 +22,7 @@ public sealed class BoxLayout5(SAV5 sav, int offset) : SaveBlock(sav, offs Data[GetBoxWallpaperOffset(box)] = (byte)value; } - private Span GetBoxNameSpan(int box) => Data.AsSpan(GetBoxNameOffset(box), 0x14); + private Span GetBoxNameSpan(int box) => Data.Slice(GetBoxNameOffset(box), 0x14); public string GetBoxName(int box) { @@ -38,12 +38,12 @@ public sealed class BoxLayout5(SAV5 sav, int offset) : SaveBlock(sav, offs public byte BoxesUnlocked { - get => Data[Offset + 0x3DD]; + get => Data[0x3DD]; set { if (value > SAV.BoxCount) value = (byte)SAV.BoxCount; - Data[Offset + 0x3DD] = value; + Data[0x3DD] = value; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Chatter5.cs b/PKHeX.Core/Saves/Substructures/Gen5/Chatter5.cs index 07b4c8fc6..54b1e387b 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/Chatter5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/Chatter5.cs @@ -6,15 +6,15 @@ namespace PKHeX.Core; /// /// Generation 5 Chatter Recording /// -public sealed class Chatter5(SAV5 SAV, int offset) : SaveBlock(SAV, offset), IChatter +public sealed class Chatter5(SAV5 SAV, Memory raw) : SaveBlock(SAV, raw), IChatter { public bool Initialized { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset)) == 1u; - set => WriteUInt32LittleEndian(Data.AsSpan(Offset), value ? 1u : 0u); + get => ReadUInt32LittleEndian(Data) == 1u; + set => WriteUInt32LittleEndian(Data, value ? 1u : 0u); } - public Span Recording => Data.AsSpan(Offset + sizeof(uint), IChatter.SIZE_PCM); + public Span Recording => Data.Slice(sizeof(uint), IChatter.SIZE_PCM); public int ConfusionChance => !Initialized ? 0 : (Recording[99] ^ Recording[499] ^ Recording[699]) switch { diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Daycare5.cs b/PKHeX.Core/Saves/Substructures/Gen5/Daycare5.cs index 50911afcc..f8bcc90b3 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/Daycare5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/Daycare5.cs @@ -3,7 +3,7 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class Daycare5(SAV5 sav, int offset) : SaveBlock(sav, offset) +public sealed class Daycare5(SAV5 sav, Memory raw) : SaveBlock(sav, raw), IDaycareStorage, IDaycareRandomState, IDaycareExperience { // struct daycareSlot // bool32 occupied @@ -15,30 +15,41 @@ public sealed class Daycare5(SAV5 sav, int offset) : SaveBlock(sav, offset // daycareSlot[2] // ???->end ??? - public const int DaycareSeedSize = 16; // 8 bytes, B2/W2 only + public int DaycareSlotCount => 2; - public ulong? GetSeed() + private static int GetDaycareSlotOffset(int index) => SlotSize * index; + public bool IsDaycareOccupied(int index) => ReadUInt32LittleEndian(Data[GetDaycareSlotOffset(index)..]) == 1; + public void SetDaycareOccupied(int index, bool occupied) => WriteUInt32LittleEndian(Data[GetDaycareSlotOffset(index)..], occupied ? 1u : 0); + + private static int GetPKMOffset(int index) => GetDaycareSlotOffset(index) + 4; + public Memory GetDaycareSlot(int index) => Raw.Slice(GetPKMOffset(index), PokeCrypto.SIZE_5PARTY); + + private static int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(slot) + 4 + PokeCrypto.SIZE_5PARTY; + public uint GetDaycareEXP(int index) => ReadUInt32LittleEndian(Data[GetDaycareEXPOffset(index)..]); + public void SetDaycareEXP(int index, uint value) => WriteUInt32LittleEndian(Data[GetDaycareEXPOffset(index)..], value); + + // 0x1C8 + + public bool IsEggAvailable { - if (SAV is not SAV5B2W2) - return null; - return ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x1CC)); + get => (Data[0x1C8] & 1) != 0; + set => Data[0x1C8] = (byte)(value ? (Data[0x1C8] | 1) : (Data[0x1C8] & ~1)); } - public void SetSeed(ReadOnlySpan value) + // 8 bytes, B2/W2 only + public ulong Seed { - if (SAV is not SAV5B2W2) - return; - var data = Util.GetBytesFromHexString(value); - SAV.SetData(data, Offset + 0x1CC); + get + { + if (SAV is not SAV5B2W2) + return 0; + return ReadUInt64LittleEndian(Data[0x1CC..]); + } + set + { + if (SAV is not SAV5B2W2) + return; + WriteUInt64LittleEndian(Data[0x1CC..], value); + } } - - private int GetDaycareSlotOffset(int slot) => Offset + (SlotSize * slot); - public int GetPKMOffset(int slot) => GetDaycareSlotOffset(slot) + 4; - private int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(slot) + 4 + PokeCrypto.SIZE_5PARTY; - - public bool? IsOccupied(int slot) => ReadUInt32LittleEndian(Data.AsSpan(GetDaycareSlotOffset(slot))) == 1; - public void SetOccupied(int slot, bool occupied) => WriteUInt32LittleEndian(Data.AsSpan(GetDaycareSlotOffset(slot)), occupied ? 1u : 0); - - public uint? GetEXP(int slot) => ReadUInt32LittleEndian(Data.AsSpan(GetDaycareEXPOffset(slot))); - public void SetEXP(int slot, uint EXP) => WriteUInt32LittleEndian(Data.AsSpan(GetDaycareEXPOffset(slot)), EXP); } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Encount5.cs b/PKHeX.Core/Saves/Substructures/Gen5/Encount5.cs index 41417017d..c36a81028 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/Encount5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/Encount5.cs @@ -3,13 +3,13 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public abstract class Encount5(SAV5 SAV, int offset) : SaveBlock(SAV, offset) +public abstract class Encount5(SAV5 SAV, Memory raw) : SaveBlock(SAV, raw) { public ushort LastLocation { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, value); } - public ushort CaptureUnknown { get => ReadUInt16LittleEndian(Data.AsSpan()[0x02..]); set => WriteUInt16LittleEndian(Data.AsSpan()[0x02..], value); } + public ushort CaptureUnknown { get => ReadUInt16LittleEndian(Data[0x02..]); set => WriteUInt16LittleEndian(Data[0x02..], value); } - public Roamer5 Roamer1 => new(Data, Offset + 4); - public Roamer5 Roamer2 => new(Data, Offset + 4 + Roamer5.SIZE); + public Roamer5 Roamer1 => new(Raw.Slice(4, Roamer5.SIZE)); + public Roamer5 Roamer2 => new(Raw.Slice(4 + Roamer5.SIZE, Roamer5.SIZE)); public abstract byte SwarmSeed { get; set; } public abstract uint SwarmMaxCountModulo { get; } @@ -21,14 +21,39 @@ public abstract class Encount5(SAV5 SAV, int offset) : SaveBlock(SAV, offs } } -public sealed class Encount5BW(SAV5BW SAV, int offset) : Encount5(SAV, offset) +public sealed class Encount5BW(SAV5BW sav, Memory raw) : Encount5(sav, raw) { - public override byte SwarmSeed { get => Data[Offset + 0x30]; set => Data[Offset + 0x30] = value; } + // 642, 641 + public byte GetRoamerState(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, 2); + return Data[0x2E + index]; + } + + public byte GetRoamerState2C(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, 2); + return Data[0x2C + index]; + } + + public override byte SwarmSeed { get => Data[0x30]; set => Data[0x30] = value; } public override uint SwarmMaxCountModulo => 17; + + public void SetRoamerState(int index, byte value) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, 2); + Data[0x2E + index] = value; + } + + public void SetRoamerState2C(int index, byte value) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, 2); + Data[0x2C + index] = value; + } } -public sealed class Encount5B2W2(SAV5B2W2 SAV, int offset) : Encount5(SAV, offset) +public sealed class Encount5B2W2(SAV5B2W2 sav, Memory raw) : Encount5(sav, raw) { - public override byte SwarmSeed { get => Data[Offset + 0x2C]; set => Data[Offset + 0x2C] = value; } + public override byte SwarmSeed { get => Data[0x2C]; set => Data[0x2C] = value; } public override uint SwarmMaxCountModulo => 19; } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Entralink5.cs b/PKHeX.Core/Saves/Substructures/Gen5/Entralink5.cs index d85a348c0..e8b09e33f 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/Entralink5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/Entralink5.cs @@ -3,47 +3,47 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public abstract class Entralink5(SAV5 SAV, int offset) : SaveBlock(SAV, offset) +public abstract class Entralink5(SAV5 SAV, Memory raw) : SaveBlock(SAV, raw) { public ushort WhiteForestLevel { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x0C)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x0C), value); + get => ReadUInt16LittleEndian(Data[0x0C..]); + set => WriteUInt16LittleEndian(Data[0x0C..], value); } public ushort BlackCityLevel { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x0E)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x0E), value); + get => ReadUInt16LittleEndian(Data[0x0E..]); + set => WriteUInt16LittleEndian(Data[0x0E..], value); } } -public sealed class Entralink5BW(SAV5BW SAV, int offset) : Entralink5(SAV, offset) +public sealed class Entralink5BW(SAV5BW SAV, Memory raw) : Entralink5(SAV, raw) { public byte MissionsComplete { - get => Data[Offset + 0x1A4]; - set => Data[Offset + 0x1A4] = value; + get => Data[0x1A4]; + set => Data[0x1A4] = value; } } -public sealed class Entralink5B2W2(SAV5B2W2 SAV, int offset) : Entralink5(SAV, offset) +public sealed class Entralink5B2W2(SAV5B2W2 SAV, Memory raw) : Entralink5(SAV, raw) { public byte PassPower1 { - get => Data[Offset + 0x1A0]; - set => Data[Offset + 0x1A0] = value; + get => Data[0x1A0]; + set => Data[0x1A0] = value; } public byte PassPower2 { - get => Data[Offset + 0x1A1]; - set => Data[Offset + 0x1A1] = value; + get => Data[0x1A1]; + set => Data[0x1A1] = value; } public byte PassPower3 { - get => Data[Offset + 0x1A2]; - set => Data[Offset + 0x1A2] = value; + get => Data[0x1A2]; + set => Data[0x1A2] = value; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Entree/EntreeForest.cs b/PKHeX.Core/Saves/Substructures/Gen5/Entree/EntreeForest.cs index 6da725037..84d090c87 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/Entree/EntreeForest.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/Entree/EntreeForest.cs @@ -6,41 +6,35 @@ namespace PKHeX.Core; /// /// Generation 5 Entrée Forest /// -public sealed class EntreeForest +public sealed class EntreeForest(SAV5 sav, Memory raw) : SaveBlock(sav, raw) { - /// - /// Areas 1 through 8 have 20 slots. - /// + /// Areas 1 through 8 have 20 slots. private const byte Count18 = 20; - /// - /// 9th Area has only 10 slots. - /// + /// 9th Area has only 10 slots. private const byte Count9 = 10; private const int TotalSlots = Count18 + (3 * 8 * Count18) + (3 * Count9); // 530 - /// - /// Areas 3 through 8 can be unlocked (set a value 0 to 6). - /// + /// Areas 3 through 8 can be unlocked (set a value 0 to 6). private const byte MaxUnlock38Areas = 6; private const int EncryptionSeedOffset = SIZE - 4; // 0x84C public const int SIZE = 0x850; - private readonly byte[] Data; + private Span DataRegion => Data[..EncryptionSeedOffset]; // 0x84C - public EntreeForest(byte[] data) => CryptRegion(Data = data); - - public byte[] Write() + private bool IsDecrypted; + public void EndAccess() => EnsureDecrypted(false); + public void StartAccess() => EnsureDecrypted(); + public void EnsureDecrypted(bool state = true) { - byte[] data = (byte[])Data.Clone(); - CryptRegion(data); - return data; + if (IsDecrypted == state) + return; + PokeCrypto.CryptArray(DataRegion, EncryptionSeed); + IsDecrypted = state; } - private void CryptRegion(Span data) => PokeCrypto.CryptArray(data[..EncryptionSeedOffset], EncryptionSeed); - /// /// Gets all Entree Slot data. /// @@ -48,9 +42,10 @@ public sealed class EntreeForest { get { + EnsureDecrypted(); var slots = new EntreeSlot[TotalSlots]; for (int i = 0; i < slots.Length; i++) - slots[i] = new EntreeSlot(Data.AsMemory(i * EntreeSlot.SIZE, EntreeSlot.SIZE)) { Area = GetSlotArea(i) }; + slots[i] = new EntreeSlot(Raw.Slice(i * EntreeSlot.SIZE, EntreeSlot.SIZE)) { Area = GetSlotArea(i) }; return slots; } } @@ -75,8 +70,8 @@ public sealed class EntreeForest public uint EncryptionSeed { - get => ReadUInt32LittleEndian(Data.AsSpan(EncryptionSeedOffset)); - private set => WriteUInt32LittleEndian(Data.AsSpan(EncryptionSeedOffset), value); + get => ReadUInt32LittleEndian(Data[EncryptionSeedOffset..]); + private set => WriteUInt32LittleEndian(Data[EncryptionSeedOffset..], value); } public void UnlockAllAreas() diff --git a/PKHeX.Core/Saves/Substructures/Gen5/EventWork5.cs b/PKHeX.Core/Saves/Substructures/Gen5/EventWork5.cs new file mode 100644 index 000000000..6d697c152 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen5/EventWork5.cs @@ -0,0 +1,52 @@ +using System; +using System.Buffers.Binary; + +namespace PKHeX.Core; + +public abstract class EventWork5(SAV5 sav, Memory raw) : SaveBlock(sav, raw), IEventFlag37 +{ + protected abstract Span WorkSpan { get; } + protected abstract Span FlagSpan { get; } + + public bool GetEventFlag(int flagNumber) => FlagUtil.GetFlag(FlagSpan, flagNumber); + public void SetEventFlag(int flagNumber, bool value) => FlagUtil.SetFlag(FlagSpan, flagNumber, value); + public ushort GetWork(int index) => BinaryPrimitives.ReadUInt16LittleEndian(WorkSpan[(index * sizeof(ushort))..]); + public void SetWork(int index, ushort value) => BinaryPrimitives.WriteUInt16LittleEndian(WorkSpan[(index * sizeof(ushort))..], value); + + public abstract int EventWorkCount { get; } + public abstract int EventFlagCount { get; } +} + +public sealed class EventWork5BW(SAV5BW sav, Memory raw) : EventWork5(sav, raw) +{ + public const int OffsetEventWork = 0; + public const int OffsetEventFlag = OffsetEventWork + (CountEventFlag * sizeof(ushort)); + + public const int CountEventWork = 0x13E; + public const int CountEventFlag = 0xB60; + + protected override Span WorkSpan => Data[..(CountEventWork * sizeof(ushort))]; + protected override Span FlagSpan => Data.Slice(OffsetEventFlag, CountEventFlag / 8); + + public override int EventWorkCount => CountEventWork; + public override int EventFlagCount => CountEventFlag; + + private const int WorkRoamer = 192; + public ushort GetWorkRoamer() => GetWork(WorkRoamer); + public void SetWorkRoamer(ushort value) => SetWork(WorkRoamer, value); +} + +public sealed class EventWork5B2W2(SAV5B2W2 sav, Memory raw) : EventWork5(sav, raw) +{ + public const int OffsetEventWork = 0; + public const int OffsetEventFlag = OffsetEventWork + (CountEventFlag * sizeof(ushort)); + + public const int CountEventWork = 0x1AF; + public const int CountEventFlag = 0xBF8; + + protected override Span WorkSpan => Data[..(CountEventWork * sizeof(ushort))]; + protected override Span FlagSpan => Data.Slice(OffsetEventFlag, CountEventFlag / 8); + + public override int EventWorkCount => CountEventWork; + public override int EventFlagCount => CountEventFlag; +} diff --git a/PKHeX.Core/Saves/Substructures/Gen5/FestaBlock5.cs b/PKHeX.Core/Saves/Substructures/Gen5/FestaBlock5.cs index 1d1d55b0c..d6f5de8ee 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/FestaBlock5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/FestaBlock5.cs @@ -3,7 +3,7 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class FestaBlock5(SAV5B2W2 SAV, int offset) : SaveBlock(SAV, offset) +public sealed class FestaBlock5(SAV5B2W2 SAV, Memory raw) : SaveBlock(SAV, raw) { public const ushort MaxScore = 9999; public const int FunfestFlag = 2438; @@ -12,44 +12,44 @@ public sealed class FestaBlock5(SAV5B2W2 SAV, int offset) : SaveBlock( public ushort Hosted { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0xF0)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0xF0), Math.Min(MaxScore, value)); + get => ReadUInt16LittleEndian(Data[0xF0..]); + set => WriteUInt16LittleEndian(Data[0xF0..], Math.Min(MaxScore, value)); } public ushort Participated { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0xF2)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0xF2), Math.Min(MaxScore, value)); + get => ReadUInt16LittleEndian(Data[0xF2..]); + set => WriteUInt16LittleEndian(Data[0xF2..], Math.Min(MaxScore, value)); } public ushort Completed { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0xF4)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0xF4), Math.Min(MaxScore, value)); + get => ReadUInt16LittleEndian(Data[0xF4..]); + set => WriteUInt16LittleEndian(Data[0xF4..], Math.Min(MaxScore, value)); } public ushort TopScores { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0xF6)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0xF6), Math.Min(MaxScore, value)); + get => ReadUInt16LittleEndian(Data[0xF6..]); + set => WriteUInt16LittleEndian(Data[0xF6..], Math.Min(MaxScore, value)); } public byte WhiteEXP { - get => Data[Offset + 0xF8]; - set => Data[Offset + 0xF8] = value; + get => Data[0xF8]; + set => Data[0xF8] = value; } public byte BlackEXP { - get => Data[Offset + 0xF9]; - set => Data[Offset + 0xF9] = value; + get => Data[0xF9]; + set => Data[0xF9] = value; } public byte Participants { - get => Data[Offset + 0xFA]; - set => Data[Offset + 0xFA] = value; + get => Data[0xFA]; + set => Data[0xFA] = value; } private static int GetMissionRecordOffset(int mission) @@ -61,20 +61,20 @@ public sealed class FestaBlock5(SAV5B2W2 SAV, int offset) : SaveBlock( public Funfest5Score GetMissionRecord(int mission) { - var raw = ReadUInt32LittleEndian(Data.AsSpan(Offset + GetMissionRecordOffset(mission))); + var raw = ReadUInt32LittleEndian(Data[GetMissionRecordOffset(mission)..]); return new Funfest5Score(raw); } public void SetMissionRecord(int mission, Funfest5Score score) { var value = score.RawValue; - WriteUInt32LittleEndian(Data.AsSpan(Offset + GetMissionRecordOffset(mission)), value); + WriteUInt32LittleEndian(Data[GetMissionRecordOffset(mission)..], value); } public bool IsFunfestMissionsUnlocked { - get => SAV.GetEventFlag(FunfestFlag); - set => SAV.SetEventFlag(FunfestFlag, value); + get => SAV.EventWork.GetEventFlag(FunfestFlag); + set => SAV.EventWork.SetEventFlag(FunfestFlag, value); } public bool IsFunfestMissionUnlocked(int mission) @@ -88,7 +88,7 @@ public sealed class FestaBlock5(SAV5B2W2 SAV, int offset) : SaveBlock( var req = FunfestMissionUnlockFlagRequired[mission]; foreach (var f in req) { - if (!SAV.GetEventFlag(f)) + if (!SAV.EventWork.GetEventFlag(f)) return false; } return true; @@ -102,7 +102,7 @@ public sealed class FestaBlock5(SAV5B2W2 SAV, int offset) : SaveBlock( IsFunfestMissionsUnlocked = true; var req = FunfestMissionUnlockFlagRequired[mission]; foreach (var f in req) - SAV.SetEventFlag(f, true); + SAV.EventWork.SetEventFlag(f, true); } public void UnlockAllFunfestMissions() diff --git a/PKHeX.Core/Saves/Substructures/Gen5/MedalList5.cs b/PKHeX.Core/Saves/Substructures/Gen5/MedalList5.cs index 4f50271c5..3fc01af3a 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/MedalList5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/MedalList5.cs @@ -2,18 +2,11 @@ using System; namespace PKHeX.Core; -public sealed class MedalList5 : SaveBlock +public sealed class MedalList5(SAV5B2W2 SAV, Memory raw) : SaveBlock(SAV, raw) { private const int MAX_MEDALS = 255; - private readonly Medal5[] Medals; - public MedalList5(SAV5B2W2 SAV, int offset) : base(SAV, offset) - { - var memory = Data.AsMemory(Offset, MAX_MEDALS * Medal5.SIZE); - Medals = GetMedals(memory); - } - - private static Medal5[] GetMedals(Memory memory) + public static Medal5[] GetMedals(Memory memory) { var count = memory.Length / Medal5.SIZE; var result = new Medal5[count]; @@ -29,18 +22,11 @@ public sealed class MedalList5 : SaveBlock return new Medal5(memory.Slice(index * Medal5.SIZE, Medal5.SIZE)); } - public Medal5 this[int index] - { - get => Medals[index]; - set => Medals[index] = value; - } + public Medal5 this[int index] => GetMedal(Raw, index); public void ObtainAll(DateOnly date, bool unread = true) { - foreach (var medal in Medals) - { - if (!medal.IsObtained) - medal.Obtain(date, unread); - } + for (int i = 0; i < MAX_MEDALS; i++) + this[i].Obtain(date, unread); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Misc5.cs b/PKHeX.Core/Saves/Substructures/Gen5/Misc5.cs index 73756d4c9..121470fce 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/Misc5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/Misc5.cs @@ -3,24 +3,24 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public abstract class Misc5(SAV5 sav, int offset) : SaveBlock(sav, offset), IGymTeamInfo +public abstract class Misc5(SAV5 sav, Memory raw) : SaveBlock(sav, raw), IGymTeamInfo { public uint Money { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset), value); + get => ReadUInt32LittleEndian(Data); + set => WriteUInt32LittleEndian(Data, value); } public int Badges { - get => Data[Offset + 0x4]; - set => Data[Offset + 0x4] = (byte)value; + get => Data[0x4]; + set => Data[0x4] = (byte)value; } public ushort PokeTransferMinigameScore { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + TransferMinigameScoreOffset)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + TransferMinigameScoreOffset), value); + get => ReadUInt16LittleEndian(Data[TransferMinigameScoreOffset..]); + set => WriteUInt16LittleEndian(Data[TransferMinigameScoreOffset..], value); } protected abstract int BadgeVictoryOffset { get; } @@ -31,29 +31,29 @@ public abstract class Misc5(SAV5 sav, int offset) : SaveBlock(sav, offset) ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(badge, 8); ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(slot, 6); - return Offset + BadgeVictoryOffset + (int)(((6 * badge) + slot) * sizeof(ushort)); + return BadgeVictoryOffset + (int)(((6 * badge) + slot) * sizeof(ushort)); } public ushort GetBadgeVictorySpecies(uint badge, uint slot) { var ofs = GetBadgeVictorySpeciesOffset(badge, slot); - return ReadUInt16LittleEndian(Data.AsSpan(ofs)); + return ReadUInt16LittleEndian(Data[ofs..]); } public void SetBadgeVictorySpecies(uint badge, uint slot, ushort species) { var ofs = GetBadgeVictorySpeciesOffset(badge, slot); - WriteUInt16LittleEndian(SAV.Data.AsSpan(ofs), species); + WriteUInt16LittleEndian(Data[ofs..], species); } } -public sealed class Misc5BW(SAV5BW sav, int offset) : Misc5(sav, offset) +public sealed class Misc5BW(SAV5BW sav, Memory raw) : Misc5(sav, raw) { protected override int TransferMinigameScoreOffset => 0x14; protected override int BadgeVictoryOffset => 0x58; // thru 0xB7 } -public sealed class Misc5B2W2(SAV5B2W2 sav, int offset) : Misc5(sav, offset) +public sealed class Misc5B2W2(SAV5B2W2 sav, Memory raw) : Misc5(sav, raw) { protected override int TransferMinigameScoreOffset => 0x18; protected override int BadgeVictoryOffset => 0x5C; // thru 0xBB diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Musical5.cs b/PKHeX.Core/Saves/Substructures/Gen5/Musical5.cs index 517fe88a7..1d1a9765e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/Musical5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/Musical5.cs @@ -1,29 +1,28 @@ +using System; + namespace PKHeX.Core; -public sealed class Musical5(SAV5 SAV, int offset) : SaveBlock(SAV, offset) +public sealed class Musical5(SAV5 SAV, Memory raw) : SaveBlock(SAV, raw) { private const int PropOffset = 0x258; public void UnlockAllMusicalProps() { // 100 props, which is 12.5 bytes of bitflags. - var bitFieldOffset = Offset + PropOffset; for (int i = 0; i < 0xC; i++) - Data[bitFieldOffset + i] = 0xFF; - Data[bitFieldOffset + 0xC] = 0x0F; // top 4 bits unset, to complete multiple of 8 (100=>104 bits). + Data[PropOffset + i] = 0xFF; + Data[PropOffset + 0xC] = 0x0F; // top 4 bits unset, to complete multiple of 8 (100=>104 bits). } public bool GetHasProp(int prop) { - var bitFieldOffset = Offset + PropOffset; var bitOffset = prop >> 3; - return SAV.GetFlag(bitFieldOffset + bitOffset, prop & 7); + return SAV.GetFlag(Data, PropOffset + bitOffset, prop & 7); } public void SetHasProp(int prop, bool value = true) { - var bitFieldOffset = Offset + PropOffset; var bitOffset = prop >> 3; - SAV.SetFlag(bitFieldOffset + bitOffset, prop & 7, value); + SAV.SetFlag(Data, PropOffset + bitOffset, prop & 7, value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/MyItem5B2W2.cs b/PKHeX.Core/Saves/Substructures/Gen5/MyItem5B2W2.cs index 4b60a3d75..8fd81eb14 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/MyItem5B2W2.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/MyItem5B2W2.cs @@ -1,8 +1,9 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; -public sealed class MyItem5B2W2(SAV5B2W2 SAV, int offset) : MyItem(SAV, offset) +public sealed class MyItem5B2W2(SAV5B2W2 SAV, Memory raw) : MyItem(SAV, raw) { // offsets/pouch sizes are the same for both B/W and B2/W2, but Key Item permissions are different private const int HeldItem = 0x000; // 0 @@ -18,11 +19,11 @@ public sealed class MyItem5B2W2(SAV5B2W2 SAV, int offset) : MyItem(SAV, offset) var info = ItemStorage5B2W2.Instance; InventoryPouch4[] pouch = [ - new(InventoryType.Items, info, 999, Offset + HeldItem), - new(InventoryType.KeyItems, info, 1, Offset + KeyItem), - new(InventoryType.TMHMs, info, 1, Offset + TMHM), - new(InventoryType.Medicine, info, 999, Offset + Medicine), - new(InventoryType.Berries, info, 999, Offset + Berry), + new(InventoryType.Items, info, 999, HeldItem), + new(InventoryType.KeyItems, info, 1, KeyItem), + new(InventoryType.TMHMs, info, 1, TMHM), + new(InventoryType.Medicine, info, 999, Medicine), + new(InventoryType.Berries, info, 999, Berry), ]; return pouch.LoadAll(Data); } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/MyItem5BW.cs b/PKHeX.Core/Saves/Substructures/Gen5/MyItem5BW.cs index e0656cf62..05bc50f07 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/MyItem5BW.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/MyItem5BW.cs @@ -1,8 +1,9 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; -public sealed class MyItem5BW(SAV5BW SAV, int offset) : MyItem(SAV, offset) +public sealed class MyItem5BW(SAV5BW SAV, Memory raw) : MyItem(SAV, raw) { // offsets/pouch sizes are the same for both B/W and B2/W2, but Key Item permissions are different private const int HeldItem = 0x000; // 0 @@ -18,11 +19,11 @@ public sealed class MyItem5BW(SAV5BW SAV, int offset) : MyItem(SAV, offset) var info = ItemStorage5BW.Instance; InventoryPouch4[] pouch = [ - new(InventoryType.Items, info, 999, Offset + HeldItem), - new(InventoryType.KeyItems, info, 1, Offset + KeyItem), - new(InventoryType.TMHMs, info, 1, Offset + TMHM), - new(InventoryType.Medicine, info, 999, Offset + Medicine), - new(InventoryType.Berries, info, 999, Offset + Berry), + new(InventoryType.Items, info, 999, HeldItem), + new(InventoryType.KeyItems, info, 1, KeyItem), + new(InventoryType.TMHMs, info, 1, TMHM), + new(InventoryType.Medicine, info, 999, Medicine), + new(InventoryType.Berries, info, 999, Berry), ]; return pouch.LoadAll(Data); } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/MysteryBlock5.cs b/PKHeX.Core/Saves/Substructures/Gen5/MysteryBlock5.cs index 011fe0023..bf52d9026 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/MysteryBlock5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/MysteryBlock5.cs @@ -3,7 +3,7 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class MysteryBlock5(SAV5 sav, int offset) : SaveBlock(sav, offset) +public sealed class MysteryBlock5(SAV5 sav, Memory raw) : SaveBlock(sav, raw), IMysteryGiftStorage, IMysteryGiftFlags { private const int FlagStart = 0; private const int MaxReceivedFlag = 2048; @@ -12,65 +12,95 @@ public sealed class MysteryBlock5(SAV5 sav, int offset) : SaveBlock(sav, o private const int CardStart = FlagStart + FlagRegionSize; private const int DataSize = 0xA90; - private int SeedOffset => Offset + DataSize; // Everything is stored encrypted, and only decrypted on demand. Only crypt on object fetch... - - public EncryptedMysteryGiftAlbum GiftAlbum + private uint AlbumSeed { - get - { - uint seed = ReadUInt32LittleEndian(Data.AsSpan(SeedOffset)); - byte[] wcData = SAV.Data.AsSpan(Offset + FlagStart, 0xA90).ToArray(); // Encrypted, Decrypt - return GetAlbum(seed, wcData); - } - set - { - var wcData = SetAlbum(value); - // Write Back - wcData.CopyTo(Data, Offset + FlagStart); - WriteUInt32LittleEndian(Data.AsSpan(SeedOffset), value.Seed); - } + get => ReadUInt32LittleEndian(Data[DataSize..]); + set => WriteUInt32LittleEndian(Data[DataSize..], value); } - private static EncryptedMysteryGiftAlbum GetAlbum(uint seed, byte[] wcData) + private Span DataRegion => Data[..^4]; // 0xA90 + private Span FlagRegion => Data[..CardStart]; // 0x100 + + private bool IsDecrypted; + public void EndAccess() => EnsureDecrypted(false); + private void EnsureDecrypted(bool state = true) { - PokeCrypto.CryptArray(wcData, seed); - - var flags = new bool[MaxReceivedFlag]; - var gifts = new DataMysteryGift[MaxCardsPresent]; - var Info = new EncryptedMysteryGiftAlbum(gifts, flags, seed); - - // 0x100 Bytes for Used Flags - for (int i = 0; i < Info.Flags.Length; i++) - Info.Flags[i] = ((wcData[i / 8] >> (i % 8)) & 0x1) == 1; - // 12 PGFs - for (int i = 0; i < Info.Gifts.Length; i++) - { - var data = wcData.AsSpan(CardStart + (i * PGF.Size), PGF.Size).ToArray(); - Info.Gifts[i] = new PGF(data); - } - - return Info; + if (IsDecrypted == state) + return; + PokeCrypto.CryptArray(DataRegion, AlbumSeed); + IsDecrypted = state; } - private static byte[] SetAlbum(EncryptedMysteryGiftAlbum value) + public void ClearReceivedFlags() { - byte[] wcData = new byte[0xA90]; - - // Toss back into byte[] - for (int i = 0; i < value.Flags.Length; i++) - { - if (value.Flags[i]) - wcData[i / 8] |= (byte) (1 << (i & 7)); - } - - var span = wcData.AsSpan(CardStart); - for (int i = 0; i < value.Gifts.Length; i++) - value.Gifts[i].Data.CopyTo(span[(i * PGF.Size)..]); - - // Decrypted, Encrypt - PokeCrypto.CryptArray(wcData, value.Seed); - return wcData; + EnsureDecrypted(); + FlagRegion.Clear(); } + + private static int GetGiftOffset(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxCardsPresent); + return CardStart + (index * PGF.Size); + } + + private Span GetCardSpan(int index) + { + var offset = GetGiftOffset(index); + EnsureDecrypted(); + return Data.Slice(offset, PGF.Size); + } + + public PGF GetMysteryGift(int index) => new(GetCardSpan(index).ToArray()); + + public void SetMysteryGift(int index, PGF pgf) + { + if ((uint)index > MaxCardsPresent) + throw new ArgumentOutOfRangeException(nameof(index)); + if (pgf.Data.Length != PGF.Size) + throw new InvalidCastException(nameof(pgf)); + SAV.SetData(GetCardSpan(index), pgf.Data); + } + + public int MysteryGiftReceivedFlagMax => MaxReceivedFlag; + public bool GetMysteryGiftReceivedFlag(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxReceivedFlag); + EnsureDecrypted(); + return FlagUtil.GetFlag(Data, index); // offset 0 + } + + public void SetMysteryGiftReceivedFlag(int index, bool value) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxReceivedFlag); + EnsureDecrypted(); + FlagUtil.SetFlag(Data, index, value); // offset 0 + } + + public int GiftCountMax => MaxCardsPresent; + DataMysteryGift IMysteryGiftStorage.GetMysteryGift(int index) => GetMysteryGift(index); + void IMysteryGiftStorage.SetMysteryGift(int index, DataMysteryGift gift) => SetMysteryGift(index, (PGF)gift); +} + +public sealed class GTS5(SAV5 sav, Memory raw) : SaveBlock(sav, raw) +{ + // 0x08: Stored Upload + private const int SizeStored = PokeCrypto.SIZE_5STORED; + + public Memory Upload => Raw[..SizeStored]; +} + +public sealed class GlobalLink5(SAV5 sav, Memory raw) : SaveBlock(sav, raw) +{ + // 0x08: Stored Upload + private const int SizeStored = PokeCrypto.SIZE_5STORED; + + public Memory Upload => Raw.Slice(8, SizeStored); +} + +public sealed class AdventureInfo5(SAV5 sav, Memory raw) : SaveBlock(sav, raw) +{ + public uint SecondsToStart { get => ReadUInt32LittleEndian(Data[0x34..]); set => WriteUInt32LittleEndian(Data[0x34..], value); } + public uint SecondsToFame { get => ReadUInt32LittleEndian(Data[0x3C..]); set => WriteUInt32LittleEndian(Data[0x3C..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/PWTBlock5.cs b/PKHeX.Core/Saves/Substructures/Gen5/PWTBlock5.cs index 4862b0110..920f2037d 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/PWTBlock5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/PWTBlock5.cs @@ -3,25 +3,18 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class PWTBlock5(SAV5B2W2 sav, int offset) : SaveBlock(sav, offset) +public sealed class PWTBlock5(SAV5B2W2 sav, Memory raw) : SaveBlock(sav, raw) { public ushort GetPWTRecord(int id) => GetPWTRecord((PWTRecordID)id); - - public ushort GetPWTRecord(PWTRecordID id) - { - if (id is < PWTRecordID.Normal or > PWTRecordID.MixMaster) - throw new ArgumentOutOfRangeException(nameof(id)); - int ofs = Offset + 0x5C + ((int)id * 2); - return ReadUInt16LittleEndian(Data.AsSpan(ofs)); - } - public void SetPWTRecord(int id, ushort value) => SetPWTRecord((PWTRecordID)id, value); - public void SetPWTRecord(PWTRecordID id, ushort value) + public ushort GetPWTRecord(PWTRecordID id) => ReadUInt16LittleEndian(Data[GetRecordOffset(id)..]); + public void SetPWTRecord(PWTRecordID id, ushort value) => WriteUInt16LittleEndian(Data[GetRecordOffset(id)..], value); + + private static int GetRecordOffset(PWTRecordID id) { if (id is < PWTRecordID.Normal or > PWTRecordID.MixMaster) throw new ArgumentOutOfRangeException(nameof(id)); - int ofs = Offset + 0x5C + ((int)id * 2); - WriteUInt16LittleEndian(Data.AsSpan(ofs), value); + return 0x5C + ((int)id * 2); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/PlayerData5.cs b/PKHeX.Core/Saves/Substructures/Gen5/PlayerData5.cs index 43cd6e4d8..820d9ffa6 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/PlayerData5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/PlayerData5.cs @@ -6,9 +6,9 @@ namespace PKHeX.Core; /// /// Combined save block; 0x100 for first, 0x100 for second. /// -public sealed class PlayerData5(SAV5 sav, int offset) : SaveBlock(sav, offset) +public sealed class PlayerData5(SAV5 sav, Memory raw) : SaveBlock(sav, raw) { - private Span OriginalTrainerTrash => Data.AsSpan(Offset + 4, 0x10); + private Span OriginalTrainerTrash => Data.Slice(4, 0x10); public string OT { @@ -18,93 +18,93 @@ public sealed class PlayerData5(SAV5 sav, int offset) : SaveBlock(sav, off public uint ID32 { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x14)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x14), value); + get => ReadUInt32LittleEndian(Data[0x14..]); + set => WriteUInt32LittleEndian(Data[0x14..], value); } public ushort TID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x14 + 0)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x14 + 0), value); + get => ReadUInt16LittleEndian(Data[(0x14 + 0)..]); + set => WriteUInt16LittleEndian(Data[(0x14 + 0)..], value); } public ushort SID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x14 + 2)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x14 + 2), value); + get => ReadUInt16LittleEndian(Data[(0x14 + 2)..]); + set => WriteUInt16LittleEndian(Data[(0x14 + 2)..], value); } public int Country { - get => Data[Offset + 0x1C]; - set => Data[Offset + 0x1C] = (byte)value; + get => Data[0x1C]; + set => Data[0x1C] = (byte)value; } public int Region { - get => Data[Offset + 0x1D]; - set => Data[Offset + 0x1D] = (byte)value; + get => Data[0x1D]; + set => Data[0x1D] = (byte)value; } public int Language { - get => Data[Offset + 0x1E]; - set => Data[Offset + 0x1E] = (byte)value; + get => Data[0x1E]; + set => Data[0x1E] = (byte)value; } public byte Game { - get => Data[Offset + 0x1F]; - set => Data[Offset + 0x1F] = value; + get => Data[0x1F]; + set => Data[0x1F] = value; } public byte Gender { - get => Data[Offset + 0x21]; - set => Data[Offset + 0x21] = value; + get => Data[0x21]; + set => Data[0x21] = value; } // 22,23 ?? public int PlayedHours { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x24)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x24), (ushort)value); + get => ReadUInt16LittleEndian(Data[0x24..]); + set => WriteUInt16LittleEndian(Data[0x24..], (ushort)value); } public int PlayedMinutes { - get => Data[Offset + 0x24 + 2]; - set => Data[Offset + 0x24 + 2] = (byte)value; + get => Data[0x24 + 2]; + set => Data[0x24 + 2] = (byte)value; } public int PlayedSeconds { - get => Data[Offset + 0x24 + 3]; - set => Data[Offset + 0x24 + 3] = (byte)value; + get => Data[0x24 + 3]; + set => Data[0x24 + 3] = (byte)value; } public int M { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x180)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x180), (ushort)value); + get => ReadInt32LittleEndian(Data[0x180..]); + set => WriteUInt16LittleEndian(Data[0x180..], (ushort)value); } public int X { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x186)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x186), (ushort)value); + get => ReadUInt16LittleEndian(Data[0x186..]); + set => WriteUInt16LittleEndian(Data[0x186..], (ushort)value); } public int Z { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x18A)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x18A), (ushort)value); + get => ReadUInt16LittleEndian(Data[0x18A..]); + set => WriteUInt16LittleEndian(Data[0x18A..], (ushort)value); } public int Y { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x18E)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x18E), (ushort)value); + get => ReadUInt16LittleEndian(Data[0x18E..]); + set => WriteUInt16LittleEndian(Data[0x18E..], (ushort)value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Roamer5.cs b/PKHeX.Core/Saves/Substructures/Gen5/Roamer5.cs index f2e9ae0b2..8a622d179 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/Roamer5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/Roamer5.cs @@ -9,12 +9,11 @@ namespace PKHeX.Core; /// /// size 0x18 [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class Roamer5(byte[] Data, int offset) +public sealed class Roamer5(Memory raw) { public const int SIZE = 0x14; - private readonly Memory Raw = Data.AsMemory(offset, SIZE); - private Span Data => Raw.Span; + private Span Data => raw.Span; public ushort Location { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data , value); } public ushort Nature { get => ReadUInt16LittleEndian(Data[0x02..]); set => WriteUInt16LittleEndian(Data[0x02..], value); } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/UnityTower5.cs b/PKHeX.Core/Saves/Substructures/Gen5/UnityTower5.cs index 7ae036de1..cff456341 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/UnityTower5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/UnityTower5.cs @@ -2,7 +2,7 @@ using System; namespace PKHeX.Core; -public sealed class UnityTower5(SAV5 SAV, int offset) : SaveBlock(SAV, offset) +public sealed class UnityTower5(SAV5 SAV, Memory raw) : SaveBlock(SAV, raw) { private const int CountryCount = 232; @@ -55,19 +55,19 @@ public sealed class UnityTower5(SAV5 SAV, int offset) : SaveBlock(SAV, off private const int UnityTowerFlagOffset = 0x345; private const int GeonetOffset = 0x348; - public bool GlobalFlag { get => Data[Offset + GeonetGlobalFlagOffset] != 0; set => Data[Offset + GeonetGlobalFlagOffset] = (byte)(value ? 1 : 0); } - public bool UnityTowerFlag { get => Data[Offset + UnityTowerFlagOffset] != 0; set => Data[Offset + UnityTowerFlagOffset] = (byte)(value ? 1 : 0); } + public bool GlobalFlag { get => Data[GeonetGlobalFlagOffset] != 0; set => Data[GeonetGlobalFlagOffset] = (byte)(value ? 1 : 0); } + public bool UnityTowerFlag { get => Data[UnityTowerFlagOffset] != 0; set => Data[UnityTowerFlagOffset] = (byte)(value ? 1 : 0); } public void SetCountrySubregion(byte country, byte subregion, Point point) { - int index = Offset + GeonetOffset + ((country - 1) * 16) + (subregion / 4); + int index = GeonetOffset + ((country - 1) * 16) + (subregion / 4); int shift = 2 * (subregion % 4); Data[index] = (byte)((Data[index] & ~(0b11 << shift)) | ((int)point << shift)); } public void SetUnityTowerFloor(byte country, bool unlocked) { - int index = Offset + UnityTowerOffset + (country / 8); + int index = UnityTowerOffset + (country / 8); int shift = country % 8; Data[index] = (byte)((Data[index] & ~(0b1 << shift)) | (unlocked ? 0b1 : 0b0) << shift); } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/WhiteBlack5.cs b/PKHeX.Core/Saves/Substructures/Gen5/WhiteBlack5.cs new file mode 100644 index 000000000..f8797c3e4 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen5/WhiteBlack5.cs @@ -0,0 +1,20 @@ +using System; +using System.Buffers.Binary; + +namespace PKHeX.Core; + +public abstract class WhiteBlack5(SAV5 sav, Memory raw) : SaveBlock(sav, raw) +{ + public uint Unknown { get => BinaryPrimitives.ReadUInt32LittleEndian(Data); set => BinaryPrimitives.WriteUInt32LittleEndian(Data, value); } +} + +public sealed class WhiteBlack5BW(SAV5BW sav, Memory raw) : WhiteBlack5(sav, raw) +{ + public const int ForestCitySize = 0x1E8; + public Memory ForestCity => Raw[..ForestCitySize]; +} + +public sealed class WhiteBlack5B2W2(SAV5B2W2 sav, Memory raw) : WhiteBlack5(sav, raw) +{ + public Memory Fused => Raw.Slice(4, PokeCrypto.SIZE_5PARTY); +} diff --git a/PKHeX.Core/Saves/Substructures/Gen6/BattleBox6.cs b/PKHeX.Core/Saves/Substructures/Gen6/BattleBox6.cs index c9d540256..ed689a0eb 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/BattleBox6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/BattleBox6.cs @@ -1,11 +1,23 @@ +using System; + namespace PKHeX.Core; public sealed class BattleBox6 : SaveBlock { - public BattleBox6(SAV6XY SAV, int offset) : base(SAV, offset) { } - public BattleBox6(SAV6AO SAV, int offset) : base(SAV, offset) { } + public BattleBox6(SAV6XY SAV, Memory raw) : base(SAV, raw) { } + public BattleBox6(SAV6AO SAV, Memory raw) : base(SAV, raw) { } - private int LockedFlagOffset => Offset + (6 * PokeCrypto.SIZE_6STORED); + public const int Count = 6; + private const int SizeStored = PokeCrypto.SIZE_6STORED; + private const int LockedFlagOffset = (Count * SizeStored); + + public Memory GetSlot(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, Count); + return Raw.Slice(index * SizeStored, SizeStored); + } + + public Memory this[int index] => GetSlot(index); public bool Locked { @@ -16,12 +28,12 @@ public sealed class BattleBox6 : SaveBlock public bool LockedWiFiTournament { get => (Data[LockedFlagOffset] & 1) != 0; - set => Data[LockedFlagOffset] = (byte)((Data[Offset + LockedFlagOffset] & ~1) | (value ? 1 : 0)); + set => Data[LockedFlagOffset] = (byte)((Data[LockedFlagOffset] & ~1) | (value ? 1 : 0)); } public bool LockedLiveTournament { get => (Data[LockedFlagOffset] & 2) != 0; - set => Data[LockedFlagOffset] = (byte)((Data[Offset + LockedFlagOffset] & ~2) | (value ? 2 : 0)); + set => Data[LockedFlagOffset] = (byte)((Data[LockedFlagOffset] & ~2) | (value ? 2 : 0)); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/BerryField6AO.cs b/PKHeX.Core/Saves/Substructures/Gen6/BerryField6AO.cs new file mode 100644 index 000000000..71ad59e48 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen6/BerryField6AO.cs @@ -0,0 +1,56 @@ +using System; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core; + +public sealed class BerryField6AO(SAV6AO sav, Memory raw) : SaveBlock(sav, raw) +{ + public const int Size = 16; // bytes per entry + public const int Count = 90; + public const int CountAllocated = 100; // 10 unused slots + + public void ResetAndRandomize(Random rnd, ReadOnlySpan choices) + { + for (int i = 0; i < Count; i++) + { + var plot = GetPlot(i); + plot.SetAsDefault(); + plot.Berry = choices[rnd.Next(choices.Length)]; + } + } + + public BerryPlot6AO GetPlot(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)Count); + return new BerryPlot6AO(Raw.Slice(index * Size, Size)); + } +} + +public sealed class BerryPlot6AO(Memory raw) +{ + private Span Data => raw.Span; + + public void SetAsDefault() + { + Data.Clear(); + Time = 0; + Water = 0; + GrowthStage = 5; + Count = 4; + IsDefault = true; + } + + public bool HasBerry => Berry is not (0 or NoBerry); + + public const ushort NoBerry = ushort.MaxValue; + + // Structure: + public byte GrowthStage { get => Data[0]; set => Data[0] = value; } + // alignment + public ushort Time { get => ReadUInt16LittleEndian(Data[2..]); set => WriteUInt16LittleEndian(Data[2..], value); } + public ushort Water { get => ReadUInt16LittleEndian(Data[4..]); set => WriteUInt16LittleEndian(Data[4..], value); } + public ushort Berry { get => ReadUInt16LittleEndian(Data[6..]); set => WriteUInt16LittleEndian(Data[6..], value); } + public float Count { get => ReadSingleLittleEndian(Data[8..]); set => WriteSingleLittleEndian(Data[8..], value); } + public bool IsDefault { get => Data[12] == 1; set => Data[12] = value ? (byte)1 : (byte)0; } + // .. 3 bytes alignment +} diff --git a/PKHeX.Core/Saves/Substructures/Gen6/BoxLayout6.cs b/PKHeX.Core/Saves/Substructures/Gen6/BoxLayout6.cs index e8d96f8cf..2f4f02d71 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/BoxLayout6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/BoxLayout6.cs @@ -19,10 +19,10 @@ public sealed class BoxLayout6 : SaveBlock, IBoxDetailName, IBoxDetailWall private const int Unlocked = PCFlags + 1; // 0x43E; private const int LastViewedBoxOffset = Unlocked + 1; // 0x43F; - public BoxLayout6(SAV6XY sav, int offset) : base(sav, offset) { } - public BoxLayout6(SAV6AO sav, int offset) : base(sav, offset) { } + public BoxLayout6(SAV6XY sav, Memory raw) : base(sav, raw) { } + public BoxLayout6(SAV6AO sav, Memory raw) : base(sav, raw) { } - public int GetBoxWallpaperOffset(int box) => Offset + PCBackgrounds + box; + public int GetBoxWallpaperOffset(int box) => PCBackgrounds + box; public int GetBoxWallpaper(int box) { @@ -38,41 +38,41 @@ public sealed class BoxLayout6 : SaveBlock, IBoxDetailName, IBoxDetailWall Data[GetBoxWallpaperOffset(box)] = (byte)value; } - private int GetBoxNameOffset(int box) => Offset + (StringMaxByteCount * box); + private static int GetBoxNameOffset(int box) => (StringMaxByteCount * box); - public string GetBoxName(int box) => SAV.GetString(Data.AsSpan(GetBoxNameOffset(box), StringMaxByteCount)); + public string GetBoxName(int box) => SAV.GetString(Data.Slice(GetBoxNameOffset(box), StringMaxByteCount)); public void SetBoxName(int box, ReadOnlySpan value) { - var span = Data.AsSpan(GetBoxNameOffset(box) + (StringMaxByteCount * box), StringMaxByteCount); + var span = Data.Slice(GetBoxNameOffset(box) + (StringMaxByteCount * box), StringMaxByteCount); SAV.SetString(span, value, StringMaxLength, StringConverterOption.ClearZero); } public byte[] BoxFlags { - get => [ Data[Offset + PCFlags] ]; // 7 bits for wallpaper unlocks, top bit to unlock final box (delta episode) + get => [ Data[PCFlags] ]; // 7 bits for wallpaper unlocks, top bit to unlock final box (delta episode) set { if (value.Length != 1) return; - Data[Offset + PCFlags] = value[0]; + Data[PCFlags] = value[0]; } } public int BoxesUnlocked { - get => Data[Offset + Unlocked]; + get => Data[Unlocked]; set { if (value > BoxCount) value = BoxCount; if (value == BoxCount) - Data[Offset + PCFlags] |= 0x80; // set final box unlocked flag + Data[PCFlags] |= 0x80; // set final box unlocked flag else - Data[Offset + PCFlags] &= 0x7F; // clear final box unlocked flag - Data[Offset + Unlocked] = (byte)value; + Data[PCFlags] &= 0x7F; // clear final box unlocked flag + Data[Unlocked] = (byte)value; } } - public int CurrentBox { get => Data[Offset + LastViewedBoxOffset]; set => Data[Offset + LastViewedBoxOffset] = (byte)value; } + public int CurrentBox { get => Data[LastViewedBoxOffset]; set => Data[LastViewedBoxOffset] = (byte)value; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/ConfigSave6.cs b/PKHeX.Core/Saves/Substructures/Gen6/ConfigSave6.cs index 5adefa21f..16f7fa847 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/ConfigSave6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/ConfigSave6.cs @@ -19,13 +19,13 @@ public sealed class ConfigSave6 : SaveBlock * unknown:14 19..31 */ - public ConfigSave6(SAV6XY sav, int offset) : base(sav, offset) { } - public ConfigSave6(SAV6AO sav, int offset) : base(sav, offset) { } + public ConfigSave6(SAV6XY sav, Memory raw) : base(sav, raw) { } + public ConfigSave6(SAV6AO sav, Memory raw) : base(sav, raw) { } public int ConfigValue { - get => ReadInt32LittleEndian(Data.AsSpan(Offset)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset), value); + get => ReadInt32LittleEndian(Data); + set => WriteInt32LittleEndian(Data, value); } public int TalkingSpeed diff --git a/PKHeX.Core/Saves/Substructures/Gen6/Daycare6AO.cs b/PKHeX.Core/Saves/Substructures/Gen6/Daycare6AO.cs new file mode 100644 index 000000000..2e09622bd --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen6/Daycare6AO.cs @@ -0,0 +1,72 @@ +using System; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core; + +public sealed class Daycare6XY(SAV6XY sav, Memory raw) : SaveBlock(sav, raw), IDaycareStorage, IDaycareRandomState, IDaycareEggState, IDaycareExperience +{ + private const int SIZE_STORED = PokeCrypto.SIZE_6STORED; + private const int SlotSize = 4 + SIZE_STORED + 4; // occupied, exp, stored + + public int DaycareSlotCount => 2; + + public bool IsDaycareOccupied(int slot) => Data[SlotSize * slot] == 1; + public void SetDaycareOccupied(int slot, bool occupied) => Data[SlotSize * slot] = occupied ? (byte)1 : (byte)0; + + public uint GetDaycareEXP(int slot) => ReadUInt32LittleEndian(Data[(4 + (SlotSize * slot))..]); + public void SetDaycareEXP(int slot, uint EXP) => WriteUInt32LittleEndian(Data[(4 + (SlotSize * slot))..], EXP); + + public Memory GetDaycareSlot(int slot) => Raw.Slice((SlotSize * slot) + 8, SIZE_STORED); + + public bool IsEggAvailable + { + get => Data[0x1E0] == 1; + set => Data[0x1E0] = value ? (byte)1 : (byte)0; + } + + public ulong Seed + { + get => ReadUInt64LittleEndian(Data[0x1E8..]); + set => WriteUInt64LittleEndian(Data[0x1E8..], value); + } +} + +public sealed class Daycare6AO(SAV6AO sav, Memory raw) : SaveBlock(sav, raw), IDaycareMulti +{ + public readonly Daycare6Couple Primary = new(raw[..Daycare6Couple.SIZE]); + public readonly Daycare6Couple Secondary = new(raw.Slice(Daycare6Couple.SIZE, Daycare6Couple.SIZE)); + + public int DaycareCount => 2; + public IDaycareStorage this[int index] => index == 0 ? Primary : Secondary; +} + +public sealed class Daycare6Couple(Memory Raw) : IDaycareStorage, IDaycareRandomState, IDaycareEggState, IDaycareExperience +{ + public const int SIZE = 0x1F0; + private const int SIZE_STORED = PokeCrypto.SIZE_6STORED; + private const int SlotSize = 4 + SIZE_STORED + 4; // occupied, exp, stored + + private Span Data => Raw.Span; + + public int DaycareSlotCount => 2; + + public bool IsDaycareOccupied(int slot) => Data[SlotSize * slot] == 1; + public void SetDaycareOccupied(int slot, bool occupied) => Data[SlotSize * slot] = occupied ? (byte)1 : (byte)0; + + public uint GetDaycareEXP(int slot) => ReadUInt32LittleEndian(Data[(4 + (SlotSize * slot))..]); + public void SetDaycareEXP(int slot, uint EXP) => WriteUInt32LittleEndian(Data[(4 + (SlotSize * slot))..], EXP); + + public Memory GetDaycareSlot(int slot) => Raw.Slice((SlotSize * slot) + 8, SIZE_STORED); + + public bool IsEggAvailable + { + get => Data[0x1E0] == 1; + set => Data[0x1E0] = value ? (byte)1 : (byte)0; + } + + public ulong Seed + { + get => ReadUInt64LittleEndian(Data[0x1E8..]); + set => WriteUInt64LittleEndian(Data[0x1E8..], value); + } +} diff --git a/PKHeX.Core/Saves/Substructures/Gen6/Encount6.cs b/PKHeX.Core/Saves/Substructures/Gen6/Encount6.cs index 27022b2ea..ae84a11aa 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/Encount6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/Encount6.cs @@ -9,26 +9,26 @@ namespace PKHeX.Core; /// public sealed class Encount6 : SaveBlock { - public Encount6(SAV6XY SAV, int offset) : base(SAV, offset) { } - public Encount6(SAV6AO SAV, int offset) : base(SAV, offset) { } + public Encount6(SAV6XY SAV, Memory raw) : base(SAV, raw) { } + public Encount6(SAV6AO SAV, Memory raw) : base(SAV, raw) { } - public ushort RepelItemUsed { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x00)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x00), value); } - public byte RepelSteps { get => Data[Offset + 0x02]; set => Data[Offset + 0x02] = value; } + public ushort RepelItemUsed { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, value); } + public byte RepelSteps { get => Data[0x02]; set => Data[0x02] = value; } // 0x04 public PokeRadar6 Radar { - get => new(Data.AsMemory(Offset + 0x04, PokeRadar6.SIZE)); - set => value.Data.CopyTo(Data.AsMemory(Offset + 0x04)); + get => new(Raw.Slice(0x04, PokeRadar6.SIZE)); + set => value.Data.Span.CopyTo(Data[0x04..]); } // 0x1C public Roamer6 Roamer { - get => new(Data.AsMemory(Offset + 0x1C, Roamer6.SIZE)); - set => value.Data.CopyTo(Data.AsMemory(Offset + 0x1C, Roamer6.SIZE)); + get => new(Raw.Slice(0x1C, Roamer6.SIZE)); + set => value.Data.Span.CopyTo(Data.Slice(0x1C, Roamer6.SIZE)); } // 0x44 diff --git a/PKHeX.Core/Saves/Substructures/Gen6/EventWork6.cs b/PKHeX.Core/Saves/Substructures/Gen6/EventWork6.cs new file mode 100644 index 000000000..e610541ad --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen6/EventWork6.cs @@ -0,0 +1,23 @@ +using System; +using System.Buffers.Binary; + +namespace PKHeX.Core; + +public sealed class EventWork6(SAV6 sav, Memory raw) : SaveBlock(sav, raw), IEventFlag37 +{ + public const int OffsetEventWork = 0; + public const int OffsetEventFlag = OffsetEventWork + (CountEventFlag * sizeof(ushort)); + + public const int CountEventWork = 0x178; + public const int CountEventFlag = 0xD00; // 3328 + + private Span WorkSpan => Data[..(CountEventWork * sizeof(ushort))]; + private Span FlagSpan => Data.Slice(OffsetEventFlag, CountEventFlag / 8); + + public int EventWorkCount => CountEventWork; + public int EventFlagCount => CountEventFlag; + public bool GetEventFlag(int flagNumber) => FlagUtil.GetFlag(FlagSpan, flagNumber); + public void SetEventFlag(int flagNumber, bool value) => FlagUtil.SetFlag(FlagSpan, flagNumber, value); + public ushort GetWork(int index) => BinaryPrimitives.ReadUInt16LittleEndian(WorkSpan[(index * sizeof(ushort))..]); + public void SetWork(int index, ushort value) => BinaryPrimitives.WriteUInt16LittleEndian(WorkSpan[(index * sizeof(ushort))..], value); +} diff --git a/PKHeX.Core/Saves/Substructures/Gen6/Fashion6XY.cs b/PKHeX.Core/Saves/Substructures/Gen6/Fashion6XY.cs index ed4dfae44..77e8e38fa 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/Fashion6XY.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/Fashion6XY.cs @@ -2,11 +2,11 @@ using System; namespace PKHeX.Core; -public sealed class Fashion6XY(SAV6XY sav, int offset) : SaveBlock(sav, offset) +public sealed class Fashion6XY(SAV6XY sav, Memory raw) : SaveBlock(sav, raw) { public void UnlockAllAccessories() { - SAV.SetData(AllAccessories, Offset); + SAV.SetData(Data, AllAccessories); } private static ReadOnlySpan AllAccessories => diff --git a/PKHeX.Core/Saves/Substructures/Gen6/GTS6.cs b/PKHeX.Core/Saves/Substructures/Gen6/GTS6.cs new file mode 100644 index 000000000..9041f6062 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen6/GTS6.cs @@ -0,0 +1,10 @@ +using System; + +namespace PKHeX.Core; + +public sealed class GTS6(SAV6 sav, Memory raw) : SaveBlock(sav, raw) +{ + private const int SizeStored = PokeCrypto.SIZE_6STORED; + + public Memory Upload => Raw[..SizeStored]; +} diff --git a/PKHeX.Core/Saves/Substructures/Gen6/GameTime6.cs b/PKHeX.Core/Saves/Substructures/Gen6/GameTime6.cs index af0ed4ace..fcba5a8c9 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/GameTime6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/GameTime6.cs @@ -3,14 +3,14 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class GameTime6(SAV6 sav, int offset) : SaveBlock(sav, offset) +public sealed class GameTime6(SAV6 sav, Memory raw) : SaveBlock(sav, raw) { - public int ResumeYear { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x4)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x4), value); } - public int ResumeMonth { get => Data[Offset + 0x8]; set => Data[Offset + 0x8] = (byte)value; } - public int ResumeDay { get => Data[Offset + 0x9]; set => Data[Offset + 0x9] = (byte)value; } - public int ResumeHour { get => Data[Offset + 0xB]; set => Data[Offset + 0xB] = (byte)value; } - public int ResumeMinute { get => Data[Offset + 0xC]; set => Data[Offset + 0xC] = (byte)value; } - public int ResumeSeconds { get => Data[Offset + 0xD]; set => Data[Offset + 0xD] = (byte)value; } - public uint SecondsToStart { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x18)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x18), value); } - public uint SecondsToFame { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x20)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x20), value); } + public int ResumeYear { get => ReadInt32LittleEndian(Data[0x4..]); set => WriteInt32LittleEndian(Data[0x4..], value); } + public int ResumeMonth { get => Data[0x8]; set => Data[0x8] = (byte)value; } + public int ResumeDay { get => Data[0x9]; set => Data[0x9] = (byte)value; } + public int ResumeHour { get => Data[0xB]; set => Data[0xB] = (byte)value; } + public int ResumeMinute { get => Data[0xC]; set => Data[0xC] = (byte)value; } + public int ResumeSeconds { get => Data[0xD]; set => Data[0xD] = (byte)value; } + public uint SecondsToStart { get => ReadUInt32LittleEndian(Data[0x18..]); set => WriteUInt32LittleEndian(Data[0x18..], value); } + public uint SecondsToFame { get => ReadUInt32LittleEndian(Data[0x20..]); set => WriteUInt32LittleEndian(Data[0x20..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/ItemInfo6.cs b/PKHeX.Core/Saves/Substructures/Gen6/ItemInfo6.cs index 2d397c801..8bade7953 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/ItemInfo6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/ItemInfo6.cs @@ -3,7 +3,7 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class ItemInfo6(SAV6 sav, int offset) : SaveBlock(sav, offset) +public sealed class ItemInfo6(SAV6 sav, Memory raw) : SaveBlock(sav, raw) { private const int BoundItemCount = 4; private const int RecentItemCount = 12; @@ -13,7 +13,7 @@ public sealed class ItemInfo6(SAV6 sav, int offset) : SaveBlock(sav, offse // UP,RIGHT,DOWN,LEFT get { - var span = Data.AsSpan(Offset + 10); + var span = Data[10..]; var result = new ushort[BoundItemCount]; for (int i = 0; i < result.Length; i++) result[i] = ReadUInt16LittleEndian(span[(2 * i)..]); @@ -22,7 +22,7 @@ public sealed class ItemInfo6(SAV6 sav, int offset) : SaveBlock(sav, offse set { ArgumentOutOfRangeException.ThrowIfNotEqual(value.Length, BoundItemCount); - var span = Data.AsSpan(Offset + 10); + var span = Data[10..]; for (int i = 0; i < value.Length; i++) WriteUInt16LittleEndian(span[(2 * i)..], value[i]); } @@ -33,7 +33,7 @@ public sealed class ItemInfo6(SAV6 sav, int offset) : SaveBlock(sav, offse // Items recently interacted with (Give, Use) get { - var span = Data.AsSpan(Offset + 20); + var span = Data[20..]; var result = new ushort[RecentItemCount]; for (int i = 0; i < result.Length; i++) result[i] = ReadUInt16LittleEndian(span[(2 * i)..]); @@ -42,7 +42,7 @@ public sealed class ItemInfo6(SAV6 sav, int offset) : SaveBlock(sav, offse set { ArgumentOutOfRangeException.ThrowIfNotEqual(value.Length, RecentItemCount); - var span = Data.AsSpan(Offset + 20); + var span = Data[20..]; for (int i = 0; i < value.Length; i++) WriteUInt16LittleEndian(span[(2 * i)..], value[i]); } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/LinkBlock6.cs b/PKHeX.Core/Saves/Substructures/Gen6/LinkBlock6.cs index 8a7057eb6..88f482791 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/LinkBlock6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/LinkBlock6.cs @@ -5,31 +5,30 @@ namespace PKHeX.Core; public sealed class LinkBlock6 : SaveBlock { - public LinkBlock6(SAV6XY sav, int offset) : base(sav, offset) { } - public LinkBlock6(SAV6AO sav, int offset) : base(sav, offset) { } + public LinkBlock6(SAV6XY sav, Memory raw) : base(sav, raw) { } + public LinkBlock6(SAV6AO sav, Memory raw) : base(sav, raw) { } - public byte[] GetLinkInfoData() => Data.AsSpan(Offset + 0x1FF, PL6.Size).ToArray(); - public PL6 GetLinkInfo() => new(GetLinkInfoData()); + public Span InfoSpan() => Data.Slice(0x1FF, PL6.Size); + + public PL6 GetLinkInfo() => new(InfoSpan().ToArray()); public void SetLinkInfoData(ReadOnlySpan data) { - data.CopyTo(Data.AsSpan(Offset)); + data.CopyTo(Data); Checksum = GetCalculatedChecksum(); // [app,chk) } public void SetLinkInfo(PL6 pl6) { - pl6.Data.CopyTo(Data, Offset + 0x1FF); + pl6.Data.CopyTo(InfoSpan()); Checksum = GetCalculatedChecksum(); // [app,chk) } - private ushort GetCalculatedChecksum() => Checksums.CRC16_CCITT(new ReadOnlySpan(Data, Offset + 0x200, 0xC48 - 4 - 0x200)); // [app,chk) - - private int GetChecksumOffset() => Offset + 0xC48 - 4; + private ushort GetCalculatedChecksum() => Checksums.CRC16_CCITT(Data[0x200..^4]); // [app,chk) public ushort Checksum { - get => ReadUInt16LittleEndian(Data.AsSpan(GetChecksumOffset())); - set => WriteUInt16LittleEndian(Data.AsSpan(GetChecksumOffset()), value); + get => ReadUInt16LittleEndian(Data[^4..]); + set => WriteUInt16LittleEndian(Data[^4..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/MaisonBlock.cs b/PKHeX.Core/Saves/Substructures/Gen6/MaisonBlock.cs index ff0e05589..57a66abe1 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/MaisonBlock.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/MaisonBlock.cs @@ -5,14 +5,14 @@ namespace PKHeX.Core; public sealed class MaisonBlock : SaveBlock { - public MaisonBlock(SAV6XY sav, int offset) : base(sav, offset) { } - public MaisonBlock(SAV6AO sav, int offset) : base(sav, offset) { } + public MaisonBlock(SAV6XY sav, Memory raw) : base(sav, raw) { } + public MaisonBlock(SAV6AO sav, Memory raw) : base(sav, raw) { } // 5 * [u16*4: normal,super,normalStreak,superStreak] public const int MaisonStatCount = 20; - public ushort GetMaisonStat(int index) => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x1C0 + (2 * index))); - public void SetMaisonStat(int index, ushort value) => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x1C0 + (2 * index)), value); + public ushort GetMaisonStat(int index) => ReadUInt16LittleEndian(Data[(0x1C0 + (2 * index))..]); + public void SetMaisonStat(int index, ushort value) => WriteUInt16LittleEndian(Data[(0x1C0 + (2 * index))..], value); private static int GetMaisonStatIndex(BattleStyle6 type, bool streak, bool super) => ((int)type << 2) | (streak ? 2 : 0) | (super ? 1 : 0); public ushort GetMaisonStat(BattleStyle6 type, bool streak, bool super) => GetMaisonStat(GetMaisonStatIndex(type, streak, super)); diff --git a/PKHeX.Core/Saves/Substructures/Gen6/Misc6AO.cs b/PKHeX.Core/Saves/Substructures/Gen6/Misc6AO.cs index 7ed8d77eb..99c9dfb62 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/Misc6AO.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/Misc6AO.cs @@ -10,30 +10,30 @@ public interface IMisc6 public sealed class Misc6AO : SaveBlock, IMisc6 { - public Misc6AO(SAV6AO sav, int offset) : base(sav, offset) { } - public Misc6AO(SAV6AODemo sav, int offset) : base(sav, offset) { } + public Misc6AO(SAV6AO sav, Memory raw) : base(sav, raw) { } + public Misc6AO(SAV6AODemo sav, Memory raw) : base(sav, raw) { } public uint Money { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x8)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x8), value); + get => ReadUInt32LittleEndian(Data[0x8..]); + set => WriteUInt32LittleEndian(Data[0x8..], value); } public int Badges { - get => Data[Offset + 0xC]; - set => Data[Offset + 0xC] = (byte)value; + get => Data[0xC]; + set => Data[0xC] = (byte)value; } public int BP { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x30)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x30), (ushort)value); + get => ReadUInt16LittleEndian(Data[0x30..]); + set => WriteUInt16LittleEndian(Data[0x30..], (ushort)value); } public int Vivillon { - get => Data[Offset + 0x44]; - set => Data[Offset + 0x44] = (byte)value; + get => Data[0x44]; + set => Data[0x44] = (byte)value; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/Misc6XY.cs b/PKHeX.Core/Saves/Substructures/Gen6/Misc6XY.cs index 8dfcc57d2..d729f766e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/Misc6XY.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/Misc6XY.cs @@ -3,29 +3,29 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class Misc6XY(SAV6XY sav, int offset) : SaveBlock(sav, offset), IMisc6 +public sealed class Misc6XY(SAV6XY sav, Memory raw) : SaveBlock(sav, raw), IMisc6 { public uint Money { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x8)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x8), value); + get => ReadUInt32LittleEndian(Data[0x8..]); + set => WriteUInt32LittleEndian(Data[0x8..], value); } public int Badges { - get => Data[Offset + 0xC]; - set => Data[Offset + 0xC] = (byte)value; + get => Data[0xC]; + set => Data[0xC] = (byte)value; } public int BP { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x3C)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x3C), (ushort)value); + get => ReadUInt16LittleEndian(Data[0x3C..]); + set => WriteUInt16LittleEndian(Data[0x3C..], (ushort)value); } public int Vivillon { - get => Data[Offset + 0x50]; - set => Data[Offset + 0x50] = (byte)value; + get => Data[0x50]; + set => Data[0x50] = (byte)value; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/MyItem6AO.cs b/PKHeX.Core/Saves/Substructures/Gen6/MyItem6AO.cs index e0846f586..e4b22ecb7 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/MyItem6AO.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/MyItem6AO.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; @@ -10,8 +11,8 @@ public sealed class MyItem6AO : MyItem private const int Medicine = 0x970; // 3, +2 items shift because 2 HMs added private const int Berry = 0xA70; // 4 - public MyItem6AO(SAV6AO SAV, int offset) : base(SAV, offset) { } - public MyItem6AO(SAV6AODemo SAV, int offset) : base(SAV, offset) { } + public MyItem6AO(SAV6AO SAV, Memory raw) : base(SAV, raw) { } + public MyItem6AO(SAV6AODemo SAV, Memory raw) : base(SAV, raw) { } public override IReadOnlyList Inventory { @@ -20,11 +21,11 @@ public sealed class MyItem6AO : MyItem var info = ItemStorage6AO.Instance; InventoryPouch4[] pouch = [ - new(InventoryType.Items, info, 999, Offset + HeldItem), - new(InventoryType.KeyItems, info, 1, Offset + KeyItem), - new(InventoryType.TMHMs, info, 1, Offset + TMHM), - new(InventoryType.Medicine, info, 999, Offset + Medicine), - new(InventoryType.Berries, info, 999, Offset + Berry), + new(InventoryType.Items, info, 999, HeldItem), + new(InventoryType.KeyItems, info, 1, KeyItem), + new(InventoryType.TMHMs, info, 1, TMHM), + new(InventoryType.Medicine, info, 999, Medicine), + new(InventoryType.Berries, info, 999, Berry), ]; return pouch.LoadAll(Data); } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/MyItem6XY.cs b/PKHeX.Core/Saves/Substructures/Gen6/MyItem6XY.cs index 27c588cec..8398fbf4f 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/MyItem6XY.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/MyItem6XY.cs @@ -1,8 +1,9 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; -public sealed class MyItem6XY(SAV6XY SAV, int offset) : MyItem(SAV, offset) +public sealed class MyItem6XY(SAV6XY SAV, Memory raw) : MyItem(SAV, raw) { private const int HeldItem = 0; // 0 private const int KeyItem = 0x640; // 1 @@ -17,11 +18,11 @@ public sealed class MyItem6XY(SAV6XY SAV, int offset) : MyItem(SAV, offset) var info = ItemStorage6XY.Instance; InventoryPouch4[] pouch = [ - new(InventoryType.Items, info, 999, Offset + HeldItem), - new(InventoryType.KeyItems, info, 1, Offset + KeyItem), - new(InventoryType.TMHMs, info, 1, Offset + TMHM), - new(InventoryType.Medicine, info, 999, Offset + Medicine), - new(InventoryType.Berries, info, 999, Offset + Berry), + new(InventoryType.Items, info, 999, HeldItem), + new(InventoryType.KeyItems, info, 1, KeyItem), + new(InventoryType.TMHMs, info, 1, TMHM), + new(InventoryType.Medicine, info, 999, Medicine), + new(InventoryType.Berries, info, 999, Berry), ]; return pouch.LoadAll(Data); } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6.cs b/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6.cs index c0cafb000..d6f1ebc7c 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6.cs @@ -6,102 +6,102 @@ namespace PKHeX.Core; /// /// Generation 6 savedata object that stores the player's trainer data. /// -public class MyStatus6(SAV6 sav, int offset) : SaveBlock(sav, offset), IRegionOrigin +public class MyStatus6(SAV6 sav, Memory raw) : SaveBlock(sav, raw), IRegionOrigin { public uint ID32 { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0), value); + get => ReadUInt32LittleEndian(Data); + set => WriteUInt32LittleEndian(Data, value); } public ushort TID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0), value); + get => ReadUInt16LittleEndian(Data); + set => WriteUInt16LittleEndian(Data, value); } public ushort SID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 2)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 2), value); + get => ReadUInt16LittleEndian(Data[2..]); + set => WriteUInt16LittleEndian(Data[2..], value); } public byte Game { - get => Data[Offset + 4]; - set => Data[Offset + 4] = value; + get => Data[4]; + set => Data[4] = value; } public byte Gender { - get => Data[Offset + 5]; - set => Data[Offset + 5] = value; + get => Data[5]; + set => Data[5] = value; } public int MultiplayerSpriteID_1 { - get => Data[Offset + 6]; - set => Data[Offset + 6] = (byte)value; + get => Data[6]; + set => Data[6] = (byte)value; } public int MultiplayerSpriteID_2 { - get => Data[Offset + 7]; - set => Data[Offset + 7] = (byte)value; + get => Data[7]; + set => Data[7] = (byte)value; } public const int GameSyncIDSize = 16; // 64 bits public string GameSyncID { - get => Util.GetHexStringFromBytes(Data.AsSpan(Offset + 0x08, GameSyncIDSize / 2)); + get => Util.GetHexStringFromBytes(Data.Slice(0x08, GameSyncIDSize / 2)); set { if (value.Length != GameSyncIDSize) throw new ArgumentOutOfRangeException(nameof(value)); var data = Util.GetBytesFromHexString(value); - SAV.SetData(data, Offset + 0x08); + SAV.SetData(Data[0x8..], data); } } public byte Region { - get => Data[Offset + 0x26]; - set => Data[Offset + 0x26] = value; + get => Data[0x26]; + set => Data[0x26] = value; } public byte Country { - get => Data[Offset + 0x27]; - set => Data[Offset + 0x27] = value; + get => Data[0x27]; + set => Data[0x27] = value; } public decimal Latitude // don't use the setters { - get => (ReadInt16LittleEndian(Data.AsSpan(Offset + 0x28)) * 180m) / 0x8000; - set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0x28), (short)((value * 0x8000) / 180m)); + get => (ReadInt16LittleEndian(Data[0x28..]) * 180m) / 0x8000; + set => WriteInt16LittleEndian(Data[0x28..], (short)((value * 0x8000) / 180m)); } public decimal Longitude // don't use the setters { - get => (ReadInt16LittleEndian(Data.AsSpan(Offset + 0x2A)) * 180m) / 0x8000; - set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0x2A), (short)((value * 0x8000) / 180m)); + get => (ReadInt16LittleEndian(Data[0x2A..]) * 180m) / 0x8000; + set => WriteInt16LittleEndian(Data[0x2A..], (short)((value * 0x8000) / 180m)); } public byte ConsoleRegion { - get => Data[Offset + 0x2C]; - set => Data[Offset + 0x2C] = value; + get => Data[0x2C]; + set => Data[0x2C] = value; } public int Language { - get => Data[Offset + 0x2D]; - set => Data[Offset + 0x2D] = (byte)value; + get => Data[0x2D]; + set => Data[0x2D] = (byte)value; } - private Span OriginalTrainerTrash => Data.AsSpan(Offset + 0x48, 0x1A); + private Span OriginalTrainerTrash => Data.Slice(0x48, 0x1A); public string OT { @@ -109,8 +109,8 @@ public class MyStatus6(SAV6 sav, int offset) : SaveBlock(sav, offset), IRe set => SAV.SetString(OriginalTrainerTrash, value, SAV.MaxStringLengthOT, StringConverterOption.ClearZero); } - private Span GetSayingSpan(int say) => Data.AsSpan(GetSayingOffset(say), SAV6.LongStringLength); - private int GetSayingOffset(int say) => Offset + 0x7C + (SAV6.LongStringLength * say); + private Span GetSayingSpan(int say) => Data.Slice(GetSayingOffset(say), SAV6.LongStringLength); + private static int GetSayingOffset(int say) => 0x7C + (SAV6.LongStringLength * say); private string GetSaying(int say) => SAV.GetString(GetSayingSpan(say)); private void SetSaying(int say, ReadOnlySpan value) => SAV.SetString(GetSayingSpan(say), value, SAV6.LongStringLength / 2, StringConverterOption.ClearZero); @@ -122,13 +122,13 @@ public class MyStatus6(SAV6 sav, int offset) : SaveBlock(sav, offset), IRe public bool IsMegaEvolutionUnlocked { - get => (Data[Offset + 0x14A] & 0x01) != 0; - set => Data[Offset + 0x14A] = (byte)((Data[Offset + 0x14A] & 0xFE) | (value ? 1 : 0)); // in battle + get => (Data[0x14A] & 0x01) != 0; + set => Data[0x14A] = (byte)((Data[0x14A] & 0xFE) | (value ? 1 : 0)); // in battle } public bool IsMegaRayquazaUnlocked { - get => (Data[Offset + 0x14A] & 0x02) != 0; - set => Data[Offset + 0x14A] = (byte)((Data[Offset + 0x14A] & ~2) | (value ? 2 : 0)); // in battle + get => (Data[0x14A] & 0x02) != 0; + set => Data[0x14A] = (byte)((Data[0x14A] & ~2) | (value ? 2 : 0)); // in battle } } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6XY.cs b/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6XY.cs index 496cab982..11e7adf9e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6XY.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6XY.cs @@ -7,15 +7,17 @@ namespace PKHeX.Core; /// XY specific features for /// /// These properties are technically included in OR/AS, but they are unused; assumed backwards compatibility for communications with XY -public sealed class MyStatus6XY(SAV6XY sav, int offset) : MyStatus6(sav, offset) +public sealed class MyStatus6XY(SAV6XY sav, Memory raw) : MyStatus6(sav, raw) { public TrainerFashion6 Fashion { - get => TrainerFashion6.GetFashion(SAV.Data, Offset + 0x30, SAV.Gender); - set => value.Write(Data, Offset + 0x30); + get => TrainerFashion6.GetFashion(FashionSpan, SAV.Gender); + set => value.Write(FashionSpan); } - private Span NicknameTrash => Data.AsSpan(Offset + 0x62, SAV6.ShortStringLength); + private Span FashionSpan => Data.Slice(0x30, TrainerFashion6.SIZE); + + private Span NicknameTrash => Data.Slice(0x62, SAV6.ShortStringLength); public string Nickname { @@ -25,7 +27,7 @@ public sealed class MyStatus6XY(SAV6XY sav, int offset) : MyStatus6(sav, offset) public short EyeColor { - get => ReadInt16LittleEndian(Data.AsSpan(Offset + 0x148)); - set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0x148), value); + get => ReadInt16LittleEndian(Data[0x148..]); + set => WriteInt16LittleEndian(Data[0x148..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/MysteryBlock6.cs b/PKHeX.Core/Saves/Substructures/Gen6/MysteryBlock6.cs index cec4186c8..011adf4f7 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/MysteryBlock6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/MysteryBlock6.cs @@ -2,57 +2,29 @@ using System; namespace PKHeX.Core; -public sealed class MysteryBlock6 : SaveBlock +public sealed class MysteryBlock6 : SaveBlock, IMysteryGiftFlags, IMysteryGiftStorage { private const int FlagStart = 0; private const int MaxReceivedFlag = 2048; private const int MaxCardsPresent = 24; // private const int FlagRegionSize = (MaxReceivedFlag / 8); // 0x100 private const int CardStart = FlagStart + (MaxReceivedFlag / 8); + public void ClearReceivedFlags() => Data[..(MaxReceivedFlag / 8)].Clear(); - public MysteryBlock6(SAV6XY sav, int offset) : base(sav, offset) { } - public MysteryBlock6(SAV6AO sav, int offset) : base(sav, offset) { } + public MysteryBlock6(SAV6XY sav, Memory raw) : base(sav, raw) { } + public MysteryBlock6(SAV6AO sav, Memory raw) : base(sav, raw) { } - public bool[] GetReceivedFlags() => FlagUtil.GetBitFlagArray(Data.AsSpan(Offset + FlagStart), MaxReceivedFlag); - - public void SetReceivedFlags(ReadOnlySpan value) + private static int GetGiftOffset(int index) { - if (value.Length != MaxReceivedFlag) - return; - FlagUtil.SetBitFlagArray(Data.AsSpan(Offset + FlagStart), value); - SAV.State.Edited = true; + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxCardsPresent); + return CardStart + (index * WC6.Size); } - public DataMysteryGift[] GetGifts() - { - var cards = new DataMysteryGift[MaxCardsPresent]; - for (int i = 0; i < cards.Length; i++) - cards[i] = GetGift(i); - return cards; - } + private Span GetCardSpan(int index) => Data.Slice(GetGiftOffset(index), WC6.Size); - public void SetGifts(DataMysteryGift[] value) - { - int count = Math.Min(MaxCardsPresent, value.Length); - for (int i = 0; i < count; i++) - SetGift(value[i], i); - for (int i = value.Length; i < MaxCardsPresent; i++) - SetGift(new WC6(), i); - } + public WC6 GetMysteryGift(int index) => new(GetCardSpan(index).ToArray()); - public DataMysteryGift GetGift(int index) - { - if ((uint)index > MaxCardsPresent) - throw new ArgumentOutOfRangeException(nameof(index)); - - var offset = GetGiftOffset(index); - var data = SAV.Data.AsSpan(offset, WC6.Size).ToArray(); - return new WC6(data); - } - - private int GetGiftOffset(int index) => Offset + CardStart + (index * WC6.Size); - - public void SetGift(DataMysteryGift wc6, int index) + public void SetMysteryGift(int index, WC6 wc6) { if ((uint)index > MaxCardsPresent) throw new ArgumentOutOfRangeException(nameof(index)); @@ -69,6 +41,23 @@ public sealed class MysteryBlock6 : SaveBlock info.EnableSendEon(); } - SAV.SetData(wc6.Data, GetGiftOffset(index)); + SAV.SetData(GetCardSpan(index), wc6.Data); } + + public int MysteryGiftReceivedFlagMax => MaxReceivedFlag; + public bool GetMysteryGiftReceivedFlag(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxReceivedFlag); + return FlagUtil.GetFlag(Data, index); // offset 0 + } + + public void SetMysteryGiftReceivedFlag(int index, bool value) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxReceivedFlag); + FlagUtil.SetFlag(Data, index, value); // offset 0 + } + + public int GiftCountMax => MaxCardsPresent; + DataMysteryGift IMysteryGiftStorage.GetMysteryGift(int index) => GetMysteryGift(index); + void IMysteryGiftStorage.SetMysteryGift(int index, DataMysteryGift gift) => SetMysteryGift(index, (WC6)gift); } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6.cs b/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6.cs index 0f54e1fb4..1dbee241f 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6.cs @@ -1,85 +1,56 @@ using System; -using static PKHeX.Core.OPower6Type; namespace PKHeX.Core; public sealed class OPower6 : SaveBlock { - private static readonly OPowerFlagSet[] Mapping = - [ - // Skip unused byte - new(5, Hatching) {Offset = 1}, - new(5, Bargain) {Offset = 6}, - new(5, Prize_Money) {Offset = 11}, - new(5, Exp_Point) {Offset = 16}, - new(5, Capture) {Offset = 21}, + public OPower6(SAV6XY sav, Memory raw) : base(sav, raw) => ArgumentOutOfRangeException.ThrowIfNotEqual(raw.Length, Size); + public OPower6(SAV6AO sav, Memory raw) : base(sav, raw) => ArgumentOutOfRangeException.ThrowIfNotEqual(raw.Length, Size); - new(3, Encounter) {Offset = 26}, - new(3, Stealth) {Offset = 29}, - new(3, HP_Restoring) {Offset = 32}, - new(3, PP_Restoring) {Offset = 35}, + // Structure: - new(1, Full_Recovery) {Offset = 38}, + // u8[65] OPowerTypeFlags + // u8 Points + // u8[10] Field Level 1 + // u8[10] Field Level 2 + // u8[7] Battle Level 1 + // u8[7] Battle Level 2 + public const int OffsetFlags = 0; + public const int OffsetPoints = 65; + public const int OffsetFieldLevel1 = 66; + public const int OffsetFieldLevel2 = OffsetFieldLevel1 + (int)OPower6FieldType.Count; + public const int OffsetBattleLevel1 = OffsetFieldLevel2 + (int)OPower6FieldType.Count; + public const int OffsetBattleLevel2 = OffsetBattleLevel1 + (int)OPower6BattleType.Count; + public const int Size = OffsetBattleLevel2 + (int)OPower6BattleType.Count; // 100 - new(5, Befriending) {Offset = 39}, + private Span IndexFlags => Data[OffsetFlags..OffsetPoints]; + private Span FieldLevels1 => Data[OffsetFieldLevel1..OffsetFieldLevel2]; + private Span FieldLevels2 => Data[OffsetFieldLevel2..OffsetBattleLevel1]; + private Span BattleLevels1 => Data[OffsetBattleLevel1..OffsetBattleLevel2]; + private Span BattleLevels2 => Data[OffsetBattleLevel2..Size]; - new(3, Attack) {Offset = 44}, - new(3, Defense) {Offset = 47}, - new(3, Sp_Attack) {Offset = 50}, - new(3, Sp_Defense) {Offset = 53}, - new(3, Speed) {Offset = 56}, - new(3, Critical) {Offset = 59}, - new(3, Accuracy) {Offset = 62}, - ]; + public byte Points { get => Data[OffsetPoints]; set => Data[OffsetPoints] = value; } - public OPower6(SAV6XY sav, int offset) : base(sav, offset) { } - public OPower6(SAV6AO sav, int offset) : base(sav, offset) { } + public OPowerFlagState GetState(OPower6Index index) => (OPowerFlagState)IndexFlags[(int)index]; + public byte GetLevel1(OPower6FieldType type) => FieldLevels1[(int)type]; + public byte GetLevel2(OPower6FieldType type) => FieldLevels2[(int)type]; + public byte GetLevel1(OPower6BattleType type) => BattleLevels1[(int)type]; + public byte GetLevel2(OPower6BattleType type) => BattleLevels2[(int)type]; - private static OPowerFlagSet Get(OPower6Type type) => Array.Find(Mapping, t => t.Identifier == type) ?? throw new ArgumentOutOfRangeException(nameof(type)); - public static int GetOPowerCount(OPower6Type type) => Get(type).BaseCount; - public int GetOPowerLevel(OPower6Type type) => Get(type).GetOPowerLevel(Data.AsSpan(Offset)); + public void SetState(OPower6Index index, OPowerFlagState state) => IndexFlags[(int)index] = (byte)state; + public void SetLevel1(OPower6FieldType type, byte value) => FieldLevels1[(int)type] = value; + public void SetLevel2(OPower6FieldType type, byte value) => FieldLevels2[(int)type] = value; + public void SetLevel1(OPower6BattleType type, byte value) => BattleLevels1[(int)type] = value; + public void SetLevel2(OPower6BattleType type, byte value) => BattleLevels2[(int)type] = value; - public static bool GetHasOPowerS(OPower6Type type) => Get(type).HasOPowerS; - public static bool GetHasOPowerMAX(OPower6Type type) => Get(type).HasOPowerMAX; - public bool GetOPowerS(OPower6Type type) => Get(type).GetOPowerS(Data.AsSpan(Offset)); - public bool GetOPowerMAX(OPower6Type type) => Get(type).GetOPowerMAX(Data.AsSpan(Offset)); - - public void SetOPowerLevel(OPower6Type type, int lvl) => Get(type).SetOPowerLevel(Data.AsSpan(Offset), lvl); - public void SetOPowerS(OPower6Type type, bool value) => Get(type).SetOPowerS(Data.AsSpan(Offset), value); - public void SetOPowerMAX(OPower6Type type, bool value) => Get(type).SetOPowerMAX(Data.AsSpan(Offset), value); - - public bool MasterFlag + public void UnlockAll() { - get => Data[Offset] == 1; - set => Data[Offset] = (byte) (value ? OPowerFlagState.Unlocked : OPowerFlagState.Locked); + IndexFlags.Fill(1); + FieldLevels1.Fill(3); + FieldLevels2.Fill(3); + BattleLevels1.Fill(3); + BattleLevels2.Fill(3); } - public void UnlockAll() => ToggleFlags(allEvents: true); - public void UnlockRegular() => ToggleFlags(); - public void ClearAll() => ToggleFlags(clearOnly: true); - - private void ToggleFlags(bool allEvents = false, bool clearOnly = false) - { - var span = Data.AsSpan(Offset); - foreach (var m in Mapping) - { - // Clear before applying new value - m.SetOPowerLevel(span, 0); - m.SetOPowerS(span, false); - m.SetOPowerMAX(span, false); - - if (clearOnly) - continue; - - int lvl = allEvents ? m.BaseCount : (m.BaseCount != 1 ? 3 : 0); // Full_Recovery is OR/AS event only @ 1 level - m.SetOPowerLevel(span, lvl); - if (!allEvents) - continue; - - m.SetOPowerS(span, true); - m.SetOPowerMAX(span, true); - } - } - - public byte[] Write() => Data; + public void ClearAll() => Data[1..].Clear(); // skip the unlock flag. } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6BattleType.cs b/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6BattleType.cs new file mode 100644 index 000000000..7053c6186 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6BattleType.cs @@ -0,0 +1,13 @@ +namespace PKHeX.Core; + +public enum OPower6BattleType : byte +{ + Attack = 0, + Defense = 1, + Sp_Attack = 2, + Sp_Defense = 3, + Speed = 4, + Critical = 5, + Accuracy = 6, + Count = 7, +} diff --git a/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6FieldType.cs b/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6FieldType.cs new file mode 100644 index 000000000..781096228 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6FieldType.cs @@ -0,0 +1,16 @@ +namespace PKHeX.Core; + +public enum OPower6FieldType : byte +{ + Hatching = 0, + Bargain = 1, + PrizeMoney = 2, + Experience = 3, + Capture = 4, + Encounter = 5, + Stealth = 6, + HPRestoring = 7, + PPRestoring = 8, + Befriending = 9, + Count = 10, +} diff --git a/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6Type.cs b/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6Type.cs index e5fa40a62..a4237306d 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6Type.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPower6Type.cs @@ -1,23 +1,116 @@ -namespace PKHeX.Core; +namespace PKHeX.Core; -public enum OPower6Type +public enum OPower6Index : byte { - Hatching, - Bargain, - Prize_Money, - Exp_Point, - Capture, - Encounter, - Stealth, - HP_Restoring, - PP_Restoring, - Full_Recovery, - Befriending, - Attack, - Defense, - Sp_Attack, - Sp_Defense, - Speed, - Critical, - Accuracy, + Enable = 0, + + Hatching1 = 1, + Hatching2 = 2, + Hatching3 = 3, + HatchingS = 4, + HatchingMAX = 5, + + Bargain1 = 6, + Bargain2 = 7, + Bargain3 = 8, + BargainS = 9, + BargainMAX = 10, + + PrizeMoney1 = 11, + PrizeMoney2 = 12, + PrizeMoney3 = 13, + PrizeMoneyS = 14, + PrizeMoneyMAX = 15, + + Experience1 = 16, + Experience2 = 17, + Experience3 = 18, + ExperienceS = 19, + ExperienceMAX = 20, + + Capture1 = 21, + Capture2 = 22, + Capture3 = 23, + CaptureS = 24, + CaptureMAX = 25, + + Encounter1 = 26, + Encounter2 = 27, + Encounter3 = 28, + + Stealth1 = 29, + Stealth2 = 30, + Stealth3 = 31, + + HPRestoring1 = 32, + HPRestoring2 = 33, + HPRestoring3 = 34, + + PPRestoring1 = 35, + PPRestoring2 = 36, + PPRestoring3 = 37, + + FullRecovery = 38, + + Befriending1 = 39, + Befriending2 = 40, + Befriending3 = 41, + BefriendingS = 42, + BefriendingMAX = 43, + + Attack1 = 44, + Attack2 = 45, + Attack3 = 46, + + Defense1 = 47, + Defense2 = 48, + Defense3 = 49, + + SpecialAttack1 = 50, + SpecialAttack2 = 51, + SpecialAttack3 = 52, + + SpecialDefense1 = 53, + SpecialDefense2 = 54, + SpecialDefense3 = 55, + + Speed1 = 56, + Speed2 = 57, + Speed3 = 58, + + Critical1 = 59, + Critical2 = 60, + Critical3 = 61, + + Accuracy1 = 62, + Accuracy2 = 63, + Accuracy3 = 64, + + Count = 65, +} + +public static class OPowerTypeExtensions +{ + public static OPower6FieldType GetFieldType(this OPower6Index index) => index switch + { + 0 => OPower6FieldType.Count, // Invalid + <= OPower6Index.HatchingMAX => OPower6FieldType.Hatching, + <= OPower6Index.BargainMAX => OPower6FieldType.Bargain, + <= OPower6Index.PrizeMoneyMAX => OPower6FieldType.PrizeMoney, + <= OPower6Index.ExperienceMAX => OPower6FieldType.Experience, + <= OPower6Index.CaptureMAX => OPower6FieldType.Capture, + <= OPower6Index.Encounter3 => OPower6FieldType.Encounter, + <= OPower6Index.Stealth3 => OPower6FieldType.Stealth, + <= OPower6Index.HPRestoring3 => OPower6FieldType.HPRestoring, + <= OPower6Index.PPRestoring3 => OPower6FieldType.PPRestoring, + OPower6Index.FullRecovery => OPower6FieldType.Count, // Invalid + <= OPower6Index.BefriendingMAX => OPower6FieldType.Befriending, + _ => OPower6FieldType.Count, // Invalid + }; + + public static OPower6BattleType GetBattleType(this OPower6Index index) => index switch + { + >= OPower6Index.Attack1 and <= OPower6Index.Accuracy3 => (OPower6BattleType)((index - OPower6Index.Attack1) / 3), + _ => OPower6BattleType.Count, // Invalid + }; } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPowerFlagSet.cs b/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPowerFlagSet.cs deleted file mode 100644 index f7c4793f8..000000000 --- a/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPowerFlagSet.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Diagnostics; - -namespace PKHeX.Core; - -internal sealed class OPowerFlagSet(int Count, OPower6Type Identifier) -{ - public readonly OPower6Type Identifier = Identifier; - public readonly int Count = Count; - - public int BaseCount => Math.Min(3, Count); - public bool HasOPowerS => Count > 3; - public bool HasOPowerMAX => Count == 5; - public int Offset { get; init; } - - public int GetOPowerLevel(ReadOnlySpan data) - { - for (int i = 0; i < BaseCount; i++) - { - if (GetFlag(data, i)) - continue; - Debug.WriteLine($"Fetched {Identifier}: {i}"); - return i; - } - - Debug.WriteLine($"Fetched {Identifier}: {BaseCount}"); - return BaseCount; - } - - public void SetOPowerLevel(Span data, int value) - { - Debug.WriteLine($"Setting {Identifier}: {value}"); - for (int i = 0; i < BaseCount; i++) - SetFlag(data, i, i + 1 <= value); - Debug.Assert(value == GetOPowerLevel(data)); - } - - public bool GetOPowerS(ReadOnlySpan data) => HasOPowerS && GetFlag(data, 3); - public bool GetOPowerMAX(ReadOnlySpan data) => HasOPowerMAX && GetFlag(data, 4); - public void SetOPowerS(Span data, bool value) => SetFlag(data, 3, value); - public void SetOPowerMAX(Span data, bool value) => SetFlag(data, 4, value); - - private bool GetFlag(ReadOnlySpan data, int index) - { - return data[Offset + index] == (byte)OPowerFlagState.Unlocked; - } - - private void SetFlag(Span data, int index, bool value) - { - if (index < Count) - data[Offset + index] = (byte)(value ? OPowerFlagState.Unlocked : OPowerFlagState.Locked); - } -} diff --git a/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPowerFlagState.cs b/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPowerFlagState.cs index 7cbfd5124..a793b4f2e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPowerFlagState.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/OPower/OPowerFlagState.cs @@ -1,6 +1,6 @@ -namespace PKHeX.Core; +namespace PKHeX.Core; -internal enum OPowerFlagState : byte +public enum OPowerFlagState : byte { Locked = 0, Unlocked = 1, diff --git a/PKHeX.Core/Saves/Substructures/Gen6/PSS6.cs b/PKHeX.Core/Saves/Substructures/Gen6/PSS6.cs index 03b8e4aa7..54b6ce2e7 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/PSS6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/PSS6.cs @@ -19,7 +19,7 @@ public static class PSS6 result.Add("----"); result.Add(headers[g]); result.Add("----"); - // uint count = ReadUInt32LittleEndian(data.AsSpan(offset + 0x4E20)); + // uint count = ReadUInt32LittleEndian(Data.Slice(0x4E20)); ReadTrainers(result, data, offset, 100); offset += 0x5000; // Advance to next block } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/PlayTime6.cs b/PKHeX.Core/Saves/Substructures/Gen6/PlayTime6.cs index a9b22c543..3e40d584c 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/PlayTime6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/PlayTime6.cs @@ -5,28 +5,28 @@ namespace PKHeX.Core; public sealed class PlayTime6 : SaveBlock { - public PlayTime6(SAV6 sav, int offset) : base(sav, offset) { } - public PlayTime6(SAV7 sav, int offset) : base(sav, offset) { } + public PlayTime6(SAV6 sav, Memory raw) : base(sav, raw) { } + public PlayTime6(SAV7 sav, Memory raw) : base(sav, raw) { } public int PlayedHours { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset), (ushort)value); + get => ReadUInt16LittleEndian(Data); + set => WriteUInt16LittleEndian(Data, (ushort)value); } public int PlayedMinutes { - get => Data[Offset + 2]; - set => Data[Offset + 2] = (byte)value; + get => Data[2]; + set => Data[2] = (byte)value; } public int PlayedSeconds { - get => Data[Offset + 3]; - set => Data[Offset + 3] = (byte)value; + get => Data[3]; + set => Data[3] = (byte)value; } - private uint LastSaved { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x4)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x4), value); } + private uint LastSaved { get => ReadUInt32LittleEndian(Data[0x4..]); set => WriteUInt32LittleEndian(Data[0x4..], value); } private int LastSavedYear { get => (int)(LastSaved & 0xFFF); set => LastSaved = (LastSaved & 0xFFFFF000) | (uint)value; } private int LastSavedMonth { get => (int)((LastSaved >> 12) & 0xF); set => LastSaved = (LastSaved & 0xFFFF0FFF) | (((uint)value & 0xF) << 12); } private int LastSavedDay { get => (int)((LastSaved >> 16) & 0x1F); set => LastSaved = (LastSaved & 0xFFE0FFFF) | (((uint)value & 0x1F) << 16); } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs b/PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs index 1386cfd1a..011e5f7e8 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs @@ -3,18 +3,18 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class Puff6(SAV6 SAV, int offset) : SaveBlock(SAV, offset) +public sealed class Puff6(SAV6 SAV, Memory raw) : SaveBlock(SAV, raw) { private const byte MaxPuffID = 26; // Supreme Winter Poké Puff private const int PuffSlots = 100; - public Span GetPuffs() => SAV.Data.AsSpan(Offset, PuffSlots); - public void SetPuffs(ReadOnlySpan value) => SAV.SetData(value, Offset); + public Span GetPuffs() => Data[..PuffSlots]; + public void SetPuffs(ReadOnlySpan value) => SAV.SetData(GetPuffs(), value); public int PuffCount { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + PuffSlots)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + PuffSlots), value); + get => ReadInt32LittleEndian(Data[PuffSlots..]); + set => WriteInt32LittleEndian(Data[PuffSlots..], value); } public void Reset() diff --git a/PKHeX.Core/Saves/Substructures/Gen6/RecordBlock6.cs b/PKHeX.Core/Saves/Substructures/Gen6/RecordBlock6.cs index cdb8d6291..e06a7d581 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/RecordBlock6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/RecordBlock6.cs @@ -4,25 +4,23 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public abstract class RecordBlock6 : RecordBlock // 6 or 7 +public abstract class RecordBlock6(SaveFile sav, Memory raw) : RecordBlock(sav, raw) // 6 or 7 { public const int RecordCount = 200; - protected RecordBlock6(SaveFile sav, int offset) : base(sav) => Offset = offset; - // Structure: // uint[100]; // ushort[100]; public override int GetRecord(int recordID) { - int ofs = Records.GetOffset(Offset, recordID); + int ofs = Records.GetOffset(recordID); switch (recordID) { case < 100: - return ReadInt32LittleEndian(Data.AsSpan(ofs)); + return ReadInt32LittleEndian(Data[ofs..]); case < 200: - return ReadInt16LittleEndian(Data.AsSpan(ofs)); + return ReadInt16LittleEndian(Data[ofs..]); default: Trace.Fail(nameof(recordID)); return 0; @@ -40,10 +38,10 @@ public abstract class RecordBlock6 : RecordBlock // 6 or 7 switch (recordID) { case < 100: - WriteInt32LittleEndian(Data.AsSpan(ofs), value); + WriteInt32LittleEndian(Data[ofs..], value); break; case < 200: - WriteUInt16LittleEndian(Data.AsSpan(ofs), (ushort)value); + WriteUInt16LittleEndian(Data[ofs..], (ushort)value); break; default: Trace.Fail(nameof(recordID)); @@ -52,7 +50,7 @@ public abstract class RecordBlock6 : RecordBlock // 6 or 7 } } -public sealed class RecordBlock6XY(SAV6XY sav, int offset) : RecordBlock6(sav, offset) +public sealed class RecordBlock6XY(SAV6XY sav, Memory raw) : RecordBlock6(sav, raw) { protected override ReadOnlySpan RecordMax => MaxType_XY; @@ -84,8 +82,8 @@ public sealed class RecordBlock6XY(SAV6XY sav, int offset) : RecordBlock6(sav, o public sealed class RecordBlock6AO : RecordBlock6 { - public RecordBlock6AO(SAV6AO sav, int offset) : base(sav, offset) { } - public RecordBlock6AO(SAV6AODemo sav, int offset) : base(sav, offset) { } + public RecordBlock6AO(SAV6AO sav, Memory raw) : base(sav, raw) { } + public RecordBlock6AO(SAV6AODemo sav, Memory raw) : base(sav, raw) { } protected override ReadOnlySpan RecordMax => MaxType_AO; private static ReadOnlySpan MaxType_AO => @@ -114,7 +112,7 @@ public sealed class RecordBlock6AO : RecordBlock6 ]; } -public sealed class RecordBlock7SM(SAV7SM sav, int offset) : RecordBlock6(sav, offset) +public sealed class RecordBlock7SM(SAV7SM sav, Memory raw) : RecordBlock6(sav, raw) { protected override ReadOnlySpan RecordMax => MaxType_SM; @@ -144,7 +142,7 @@ public sealed class RecordBlock7SM(SAV7SM sav, int offset) : RecordBlock6(sav, o ]; } -public sealed class RecordBlock7USUM(SAV7USUM sav, int offset) : RecordBlock6(sav, offset) +public sealed class RecordBlock7USUM(SAV7USUM sav, Memory raw) : RecordBlock6(sav, raw) { protected override ReadOnlySpan RecordMax => MaxType_USUM; diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SangoInfoBlock.cs b/PKHeX.Core/Saves/Substructures/Gen6/SangoInfoBlock.cs index dcc0b3b7c..bbf82f2b3 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SangoInfoBlock.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SangoInfoBlock.cs @@ -3,26 +3,26 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class SangoInfoBlock(SAV6AO SAV, int offset) : SaveBlock(SAV, offset) +public sealed class SangoInfoBlock(SAV6AO SAV, Memory raw) : SaveBlock(SAV, raw) { private const uint EON_MAGIC = WC6.EonTicketConst; public uint EonTicketReceivedMagic // 0x319B8 { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x63B8)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x63B8), value); + get => ReadUInt32LittleEndian(Data[0x63B8..]); + set => WriteUInt32LittleEndian(Data[0x63B8..], value); } public string SecretBaseQRText // 0x319BC -- 17*u16 { - get => SAV.GetString(Data.AsSpan(Offset + 0x63BC, 36)); - set => SAV.SetString(Data.AsSpan(Offset + 0x63BC, 36), value, 0x10, StringConverterOption.ClearZero); + get => SAV.GetString(Data.Slice(0x63BC, 36)); + set => SAV.SetString(Data.Slice(0x63BC, 36), value, 0x10, StringConverterOption.ClearZero); } public uint EonTicketSendMagic // 0x319DE { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x63DE)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x63DE), value); + get => ReadUInt32LittleEndian(Data[0x63DE..]); + set => WriteUInt32LittleEndian(Data[0x63DE..], value); } public void ReceiveEon() => EonTicketReceivedMagic = EON_MAGIC; diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6.cs b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6.cs index 7a8b338ee..65c51a127 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6.cs @@ -6,15 +6,14 @@ namespace PKHeX.Core; /// /// Secret base format for /// -public class SecretBase6(byte[] Data, int Offset = 0) +public class SecretBase6(Memory raw) { public const int SIZE = 0x310; public const int COUNT_GOODS = 28; public const int MinLocationID = -1; public const int MaxLocationID = 85; - protected readonly byte[] Data = Data; - protected readonly int Offset = Offset; + protected Span Data => raw.Span; // structure: (first at 23D24 in sav) // [000-001] u8 IsNew @@ -39,14 +38,14 @@ public class SecretBase6(byte[] Data, int Offset = 0) public bool IsNew { - get => Data[Offset] == 1; - set => WriteUInt16LittleEndian(Data.AsSpan(Offset), (ushort)(value ? 1 : 0)); + get => Data[0] == 1; + set => WriteUInt16LittleEndian(Data, (ushort)(value ? 1 : 0)); } private int RawLocation { - get => ReadInt16LittleEndian(Data.AsSpan(Offset + 2)); - set => WriteInt16LittleEndian(Data.AsSpan(Offset + 2), (short)value); + get => ReadInt16LittleEndian(Data[2..]); + set => WriteInt16LittleEndian(Data[2..], (short)value); } public int BaseLocation @@ -61,7 +60,7 @@ public class SecretBase6(byte[] Data, int Offset = 0) }; } - public SecretBase6GoodPlacement GetPlacement(int index) => new(Data, Offset + GetPlacementOffset(index)); + public SecretBase6GoodPlacement GetPlacement(int index) => new(raw.Slice(GetPlacementOffset(index), SecretBase6GoodPlacement.SIZE)); private static int GetPlacementOffset(int index) { @@ -72,8 +71,8 @@ public class SecretBase6(byte[] Data, int Offset = 0) public byte BoppoyamaScore { - get => Data[Offset + 0x174]; - set => Data[Offset + 0x174] = value; + get => Data[0x174]; + set => Data[0x174] = value; } private const int NameLengthBytes = 0x1A; @@ -83,11 +82,11 @@ public class SecretBase6(byte[] Data, int Offset = 0) public string TrainerName { - get => StringConverter6.GetString(Data.AsSpan(Offset + 0x21A, NameLengthBytes)); - set => StringConverter6.SetString(Data.AsSpan(Offset + 0x21A, NameLengthBytes), value, NameLength, StringConverterOption.ClearZero); + get => StringConverter6.GetString(Data.Slice(0x21A, NameLengthBytes)); + set => StringConverter6.SetString(Data.Slice(0x21A, NameLengthBytes), value, NameLength, StringConverterOption.ClearZero); } - private Span GetMessageSpan(int index) => Data.AsSpan(Offset + 0x234 + (MessageLengthBytes * index), MessageLengthBytes); + private Span GetMessageSpan(int index) => Data.Slice(0x234 + (MessageLengthBytes * index), MessageLengthBytes); private string GetMessage(int index) => StringConverter6.GetString(GetMessageSpan(index)); private void SetMessage(int index, ReadOnlySpan value) => StringConverter6.SetString(GetMessageSpan(index), value, MessageLength, StringConverterOption.ClearZero); @@ -100,24 +99,24 @@ public class SecretBase6(byte[] Data, int Offset = 0) public SecretBase6Rank Rank { - get => (SecretBase6Rank) ReadInt32LittleEndian(Data.AsSpan(Offset + 0x300)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x300), (int)value); + get => (SecretBase6Rank) ReadInt32LittleEndian(Data[0x300..]); + set => WriteInt32LittleEndian(Data[0x300..], (int)value); } public uint TotalFlagsFromFriends { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x304)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x304), value); + get => ReadUInt32LittleEndian(Data[0x304..]); + set => WriteUInt32LittleEndian(Data[0x304..], value); } public uint TotalFlagsFromOther { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x308)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x308), value); + get => ReadUInt32LittleEndian(Data[0x308..]); + set => WriteUInt32LittleEndian(Data[0x308..], value); } - public byte CollectedFlagsToday { get => Data[Offset + 0x30C]; set => Data[Offset + 0x30C] = value; } - public byte CollectedFlagsYesterday { get => Data[Offset + 0x30D]; set => Data[Offset + 0x30D] = value; } + public byte CollectedFlagsToday { get => Data[0x30C]; set => Data[0x30C] = value; } + public byte CollectedFlagsYesterday { get => Data[0x30D]; set => Data[0x30D] = value; } // Derived Values @@ -125,7 +124,7 @@ public class SecretBase6(byte[] Data, int Offset = 0) public bool IsEmpty => BaseLocation <= 0; protected virtual void LoadOther(SecretBase6Other other) => LoadSelf(other); - private void LoadSelf(SecretBase6 other) => other.Data.AsSpan(other.Offset, SIZE).CopyTo(Data.AsSpan(Offset)); + private void LoadSelf(SecretBase6 other) => other.Data[..SIZE].CopyTo(Data); // ensure input is truncated to correct size (other has extra fields) public void Load(SecretBase6 other) { @@ -135,7 +134,7 @@ public class SecretBase6(byte[] Data, int Offset = 0) LoadSelf(other); } - public virtual byte[] Write() => Data.AsSpan(Offset, SIZE).ToArray(); + public virtual byte[] Write() => Data.ToArray(); public static SecretBase6? Read(byte[] data) { @@ -151,7 +150,7 @@ public class SecretBase6(byte[] Data, int Offset = 0) /// /// An expanded structure of containing extra fields to describe another trainer's base. /// -public sealed class SecretBase6Other(byte[] Data, int Offset = 0) : SecretBase6(Data, Offset) +public sealed class SecretBase6Other(Memory raw) : SecretBase6(raw) { public new const int SIZE = 0x3E0; @@ -177,27 +176,27 @@ public sealed class SecretBase6Other(byte[] Data, int Offset = 0) : SecretBase6( public byte Language { - get => Data[Offset + 0x320]; - set => Data[Offset + 0x320] = value; + get => Data[0x320]; + set => Data[0x320] = value; } public byte Gender { - get => Data[Offset + 0x321]; - set => Data[Offset + 0x321] = value; + get => Data[0x321]; + set => Data[0x321] = value; } public const int COUNT_TEAM = 3; - private Span GetParticipantData(int index) => Data.AsSpan(GetParticipantOffset(index), SecretBase6PKM.SIZE); + private Span GetParticipantData(int index) => Data.Slice(GetParticipantOffset(index), SecretBase6PKM.SIZE); public SecretBase6PKM GetParticipant(int index) => new(GetParticipantData(index).ToArray()); public void SetParticipant(int index, SecretBase6PKM pk) => pk.Data.CopyTo(GetParticipantData(index)); - public int GetParticipantOffset(int index) + public static int GetParticipantOffset(int index) { if ((uint) index >= COUNT_TEAM) throw new ArgumentOutOfRangeException(nameof(index)); - return Offset + 0x330 + (index * SecretBase6PKM.SIZE); + return 0x330 + (index * SecretBase6PKM.SIZE); } public SecretBase6PKM[] GetTeam() @@ -222,7 +221,7 @@ public sealed class SecretBase6Other(byte[] Data, int Offset = 0) : SecretBase6( SetParticipant(i, arr[i]); } - protected override void LoadOther(SecretBase6Other other) => other.Data.AsSpan(other.Offset, SIZE).CopyTo(Data.AsSpan(Offset)); + protected override void LoadOther(SecretBase6Other other) => other.Data.CopyTo(Data); - public override byte[] Write() => Data.AsSpan(Offset, SIZE).ToArray(); + public override byte[] Write() => Data.ToArray(); } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodPlacement.cs b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodPlacement.cs index 40298141b..b1be3f93c 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodPlacement.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodPlacement.cs @@ -3,14 +3,11 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class SecretBase6GoodPlacement(byte[] Data, int Offset) +public sealed class SecretBase6GoodPlacement(Memory raw) { public const int SIZE = 12; - private byte[] Raw { get; } = Data; - private int Offset { get; } = Offset; - - private Span Data => Raw.AsSpan(Offset); + private Span Data => raw.Span; public ushort Good { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, value); } public ushort X { get => ReadUInt16LittleEndian(Data[2..]); set => WriteUInt16LittleEndian(Data[2..], value); } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodStock.cs b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodStock.cs index cc9059c27..e566d1cef 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodStock.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodStock.cs @@ -7,14 +7,14 @@ namespace PKHeX.Core; /// /// Generation 6 Secret Base Decoration Good Inventory stock for a given good-index. /// -public sealed class SecretBase6GoodStock(byte[] Data, int Offset) +public sealed class SecretBase6GoodStock(Memory raw) { public const int SIZE = 4; - private Span Span => Data.AsSpan(Offset); + private Span Data => raw.Span; - public ushort Count { get => ReadUInt16LittleEndian(Span); set => WriteUInt16LittleEndian(Span, value); } - public bool IsNew { get => Span[2] != 0; set => Span[2] = (byte)(value ? 1 : 0); } + public ushort Count { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, value); } + public bool IsNew { get => Data[2] != 0; set => Data[2] = (byte)(value ? 1 : 0); } - public void Clear() => MemoryMarshal.Write(Span, 0); + public void Clear() => MemoryMarshal.Write(Data, 0); } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase6Block.cs b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase6Block.cs index 3b445f3c3..0a0ece254 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase6Block.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase6Block.cs @@ -3,7 +3,7 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class SecretBase6Block(SAV6AO sav, int offset) : SaveBlock(sav, offset) +public sealed class SecretBase6Block(SAV6AO sav, Memory raw) : SaveBlock(sav, raw) { // structure: 0x7AD0 bytes total // [0000-031F] SecretBaseGoodStock[200] (800 bytes) @@ -19,7 +19,7 @@ public sealed class SecretBase6Block(SAV6AO sav, int offset) : SaveBlock public const int Count_Goods = 200; public const int Count_Goods_Used = 173; - public SecretBase6GoodStock GetGood(int index) => new(Data, Offset + GetGoodOffset(index)); + public SecretBase6GoodStock GetGood(int index) => new(Raw.Slice(GetGoodOffset(index), SecretBase6GoodStock.SIZE)); private static int GetGoodOffset(int index) { @@ -32,19 +32,19 @@ public sealed class SecretBase6Block(SAV6AO sav, int offset) : SaveBlock { const uint value = 25u | (1 << 16); // count: 25, new flag. for (int i = 0; i < Count_Goods_Used; i++) - WriteUInt32LittleEndian(Data.AsSpan(Offset + GetGoodOffset(i)), value); + WriteUInt32LittleEndian(Data[GetGoodOffset(i)..], value); } public ushort SecretBaseSelfLocation { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 800)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 800), value); + get => ReadUInt16LittleEndian(Data[800..]); + set => WriteUInt16LittleEndian(Data[800..], value); } public const int OtherSecretBaseCount = 30; private const int OtherSecretStart = 0x638; - public SecretBase6 GetSecretBaseSelf() => new(Data, Offset + 0x324); - public SecretBase6Other GetSecretBaseOther(int index) => new(Data, Offset + OtherSecretStart + GetSecretBaseOtherOffset(index)); + public SecretBase6 GetSecretBaseSelf() => new(Raw.Slice(0x324, SecretBase6.SIZE)); + public SecretBase6Other GetSecretBaseOther(int index) => new(Raw.Slice(OtherSecretStart + GetSecretBaseOtherOffset(index), SecretBase6Other.SIZE)); private static int GetSecretBaseOtherOffset(int index) { @@ -55,26 +55,28 @@ public sealed class SecretBase6Block(SAV6AO sav, int offset) : SaveBlock public bool SecretBaseHasFlag { - get => Data[Offset + 0x7AC8] == 1; - set => Data[Offset + 0x7AC8] = (byte) (value ? 1 : 0); + get => Data[0x7AC8] == 1; + set => Data[0x7AC8] = (byte) (value ? 1 : 0); } public void DeleteOther(int index) { - int baseOffset = Offset + OtherSecretStart; const int maxBaseIndex = OtherSecretBaseCount - 1; const int size = SecretBase6Other.SIZE; - int offset = baseOffset + GetSecretBaseOtherOffset(index); - var arr = SAV.Data; + int offset = OtherSecretStart + GetSecretBaseOtherOffset(index); + var arr = Data; if ((uint)index < OtherSecretBaseCount) { int shiftDownCount = maxBaseIndex - index; int shiftDownLength = size * shiftDownCount; - Array.Copy(arr, offset + size, arr, offset, shiftDownLength); + + var src = arr.Slice(offset + size, shiftDownLength); + var dst = arr.Slice(offset, shiftDownLength); + src.CopyTo(dst); } // Ensure Last Entry is Cleared - int lastBaseOffset = baseOffset + (size * maxBaseIndex); - arr.AsSpan(lastBaseOffset, size).Clear(); + const int lastBaseOffset = OtherSecretStart + (size * maxBaseIndex); + arr.Slice(lastBaseOffset, size).Clear(); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/Situation6.cs b/PKHeX.Core/Saves/Substructures/Gen6/Situation6.cs index 276db5633..8708d9102 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/Situation6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/Situation6.cs @@ -3,14 +3,14 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class Situation6(SAV6 SAV, int offset) : SaveBlock(SAV, offset) +public sealed class Situation6(SAV6 SAV, Memory raw) : SaveBlock(SAV, raw) { public int M { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x02)); + get => ReadUInt16LittleEndian(Data[0x02..]); set { - var span = Data.AsSpan(Offset + 0x02); + var span = Data[0x02..]; WriteUInt16LittleEndian(span, (ushort)value); WriteUInt16LittleEndian(span[0xF2..], (ushort)value); } @@ -18,10 +18,10 @@ public sealed class Situation6(SAV6 SAV, int offset) : SaveBlock(SAV, offs public int R { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x06)); + get => ReadUInt16LittleEndian(Data[0x06..]); set { - var span = Data.AsSpan(Offset + 0x06); + var span = Data[0x06..]; WriteUInt16LittleEndian(span, (ushort)value); WriteUInt16LittleEndian(span[0xF0..], (ushort)value); } @@ -29,10 +29,10 @@ public sealed class Situation6(SAV6 SAV, int offset) : SaveBlock(SAV, offs public float X { - get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x10)); + get => ReadSingleLittleEndian(Data[0x10..]); set { - var span = Data.AsSpan(Offset + 0x10); + var span = Data[0x10..]; WriteSingleLittleEndian(span, value); WriteSingleLittleEndian(span[0xF4..], value); } @@ -40,10 +40,10 @@ public sealed class Situation6(SAV6 SAV, int offset) : SaveBlock(SAV, offs public float Z { - get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x14)); + get => ReadSingleLittleEndian(Data[0x14..]); set { - var span = Data.AsSpan(Offset + 0x14); + var span = Data[0x14..]; WriteSingleLittleEndian(span, value); WriteSingleLittleEndian(span[0xF4..], value); } @@ -51,10 +51,10 @@ public sealed class Situation6(SAV6 SAV, int offset) : SaveBlock(SAV, offs public float Y { - get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x18)); + get => ReadSingleLittleEndian(Data[0x18..]); set { - var span = Data.AsSpan(Offset + 0x18); + var span = Data[0x18..]; WriteSingleLittleEndian(span, value); WriteSingleLittleEndian(span[0xF4..], value); } @@ -63,7 +63,7 @@ public sealed class Situation6(SAV6 SAV, int offset) : SaveBlock(SAV, offs // xy only public int Style { - get => Data[Offset + 0x14D]; - set => Data[Offset + 0x14D] = (byte)value; + get => Data[0x14D]; + set => Data[0x14D] = (byte)value; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SubEventLog6.cs b/PKHeX.Core/Saves/Substructures/Gen6/SubEventLog6.cs index ca0db778e..5eaf63699 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SubEventLog6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SubEventLog6.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// /// SUBE block that stores in-game event results. /// -public abstract class SubEventLog6(SAV6 sav, int offset) : SaveBlock(sav, offset), IGymTeamInfo +public abstract class SubEventLog6(SAV6 sav, Memory raw) : SaveBlock(sav, raw), IGymTeamInfo { protected abstract int BadgeVictoryOffset { get; } @@ -15,6 +15,8 @@ public abstract class SubEventLog6(SAV6 sav, int offset) : SaveBlock(sav, /// public abstract int Give { get; } + public Memory GiveSlot => Raw.Slice(Give, PokeCrypto.SIZE_6STORED); + /// /// Absolute offset of the that is unreferenced? /// @@ -24,23 +26,23 @@ public abstract class SubEventLog6(SAV6 sav, int offset) : SaveBlock(sav, { ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(badge, 8); ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(slot, 6); - return Offset + BadgeVictoryOffset + (int)(((6 * badge) + slot) * sizeof(ushort)); + return BadgeVictoryOffset + (int)(((6 * badge) + slot) * sizeof(ushort)); } public ushort GetBadgeVictorySpecies(uint badge, uint slot) { var ofs = GetBadgeVictorySpeciesOffset(badge, slot); - return ReadUInt16LittleEndian(Data.AsSpan(ofs)); + return ReadUInt16LittleEndian(Data[ofs..]); } public void SetBadgeVictorySpecies(uint badge, uint slot, ushort species) { var ofs = GetBadgeVictorySpeciesOffset(badge, slot); - WriteUInt16LittleEndian(Data.AsSpan(ofs), species); + WriteUInt16LittleEndian(Data[ofs..], species); } } -public sealed class SubEventLog6XY(SAV6XY sav, int offset) : SubEventLog6(sav, offset) +public sealed class SubEventLog6XY(SAV6XY sav, Memory raw) : SubEventLog6(sav, raw) { // Structure: @@ -48,8 +50,8 @@ public sealed class SubEventLog6XY(SAV6XY sav, int offset) : SubEventLog6(sav, o // u8[0x28] chateau data private ushort ChateauValue { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset), value); + get => ReadUInt16LittleEndian(Data); + set => WriteUInt16LittleEndian(Data, value); } public ushort ChateauRank @@ -73,19 +75,19 @@ public sealed class SubEventLog6XY(SAV6XY sav, int offset) : SubEventLog6(sav, o // 0x90 // u8[0xE8] pk? - public override int Give => 0x90 + Offset; + public override int Give => 0x90; // u32 SUBE @ 0x178 // 0x17C // u8[0xE8] pk? - public override int UnusedPKM => 0x17C + Offset; + public override int UnusedPKM => 0x17C; // u32 SUBE @ 0x264 // 0x268 // u8[0xA0] unused? } -public sealed class SubEventLog6AO(SAV6AO sav, int offset) : SubEventLog6(sav, offset) +public sealed class SubEventLog6AO(SAV6AO sav, Memory raw) : SubEventLog6(sav, raw) { // Structure: @@ -101,26 +103,26 @@ public sealed class SubEventLog6AO(SAV6AO sav, int offset) : SubEventLog6(sav, o // 0xC4 // u8[0xE8] pk? - public override int Give => 0xC4 + Offset; + public override int Give => 0xC4; // u32 SUBE @ 0x1AC // 0x1B0 // u8[0xE8] pk? - public override int UnusedPKM => 0x1B0 + Offset; + public override int UnusedPKM => 0x1B0; // u32 SUBE @ 0x298 // 0x29C // u16 SeasideCyclingRoadTimeMilliseconds 29C public ushort SeasideCyclingRoadTimeMilliseconds { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x29C)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x29C), value); + get => ReadUInt16LittleEndian(Data[0x29C..]); + set => WriteUInt16LittleEndian(Data[0x29C..], value); } // u16 SeasideCyclingRoadCollisions 29E public ushort SeasideCyclingRoadCollisions { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x29E)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x29E), value); + get => ReadUInt16LittleEndian(Data[0x29E..]); + set => WriteUInt16LittleEndian(Data[0x29E..], value); } // u16[7] 2A0 // u16[7] 2AE diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SuperTrainBlock.cs b/PKHeX.Core/Saves/Substructures/Gen6/SuperTrainBlock.cs index 525dff425..4dbd38f6b 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SuperTrainBlock.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SuperTrainBlock.cs @@ -5,8 +5,8 @@ namespace PKHeX.Core; public sealed class SuperTrainBlock : SaveBlock { - public SuperTrainBlock(SAV6XY sav, int offset) : base(sav, offset) { } - public SuperTrainBlock(SAV6AO sav, int offset) : base(sav, offset) { } + public SuperTrainBlock(SAV6XY sav, Memory raw) : base(sav, raw) { } + public SuperTrainBlock(SAV6AO sav, Memory raw) : base(sav, raw) { } // Structure: // 6 bytes stage unlock flags @@ -37,7 +37,7 @@ public sealed class SuperTrainBlock : SaveBlock { if ((uint)index >= MAX) throw new ArgumentOutOfRangeException(nameof(index)); - return SAV.GetFlag(Offset, index); + return SAV.GetFlag(Data, 0, index); } /// @@ -49,7 +49,7 @@ public sealed class SuperTrainBlock : SaveBlock { if ((uint)index >= MAX) throw new ArgumentOutOfRangeException(nameof(index)); - SAV.SetFlag(Offset, index, value); + SAV.SetFlag(Data, 0, index, value); } /// @@ -61,7 +61,7 @@ public sealed class SuperTrainBlock : SaveBlock { if ((uint)index >= MAX_DIST) throw new ArgumentOutOfRangeException(nameof(index)); - return SAV.GetFlag(Offset + 6, index); + return SAV.GetFlag(Data, 6, index); } /// @@ -73,7 +73,7 @@ public sealed class SuperTrainBlock : SaveBlock { if ((uint)index >= MAX_DIST) throw new ArgumentOutOfRangeException(nameof(index)); - SAV.SetFlag(Offset + 6, index, value); + SAV.SetFlag(Data, 6, index, value); } /// @@ -81,8 +81,8 @@ public sealed class SuperTrainBlock : SaveBlock /// public byte Counter { - get => Data[Offset + 7]; - set => Data[Offset + 7] = Math.Min((byte)10, value); + get => Data[7]; + set => Data[7] = Math.Min((byte)10, value); } /// @@ -94,7 +94,7 @@ public sealed class SuperTrainBlock : SaveBlock if ((uint) index >= MAX) throw new ArgumentOutOfRangeException(nameof(index)); - return ReadSingleLittleEndian(Data.AsSpan(Offset + 0x08 + (4 * index))); + return ReadSingleLittleEndian(Data[(0x08 + (4 * index))..]); } /// @@ -107,7 +107,7 @@ public sealed class SuperTrainBlock : SaveBlock if ((uint)index >= MAX) throw new ArgumentOutOfRangeException(nameof(index)); - WriteSingleLittleEndian(Data.AsSpan(Offset + 0x08 + (4 * index)), value); + WriteSingleLittleEndian(Data[(0x08 + (4 * index))..], value); } /// @@ -119,7 +119,7 @@ public sealed class SuperTrainBlock : SaveBlock if ((uint)index >= MAX) throw new ArgumentOutOfRangeException(nameof(index)); - return ReadSingleLittleEndian(Data.AsSpan(Offset + 0xC8 + (4 * index))); + return ReadSingleLittleEndian(Data[(0xC8 + (4 * index))..]); } /// @@ -132,7 +132,7 @@ public sealed class SuperTrainBlock : SaveBlock if ((uint)index >= MAX) throw new ArgumentOutOfRangeException(nameof(index)); - WriteSingleLittleEndian(Data.AsSpan(Offset + 0xC8 + (4 * index)), value); + WriteSingleLittleEndian(Data[(0xC8 + (4 * index))..], value); } /// @@ -142,10 +142,9 @@ public sealed class SuperTrainBlock : SaveBlock /// Object that will edit the record data if modified. public SuperTrainingSpeciesRecord GetHolder1(int index) { - if ((uint)index >= MAX) - throw new ArgumentOutOfRangeException(nameof(index)); - - return new SuperTrainingSpeciesRecord(Data, Offset + 0x188 + (4 * index)); + var ofs = GetOffsetHolder1(index); + var raw = Raw.Slice(ofs, SuperTrainingSpeciesRecord.SIZE); + return new SuperTrainingSpeciesRecord(raw); } /// @@ -154,11 +153,24 @@ public sealed class SuperTrainBlock : SaveBlock /// Index of the record. /// Object that will edit the record data if modified. public SuperTrainingSpeciesRecord GetHolder2(int index) + { + var ofs = GetOffsetHolder2(index); + var raw = Raw.Slice(ofs, SuperTrainingSpeciesRecord.SIZE); + return new SuperTrainingSpeciesRecord(raw); + } + + private static int GetOffsetHolder1(int index) { if ((uint)index >= MAX) throw new ArgumentOutOfRangeException(nameof(index)); + return 0x188 + (SuperTrainingSpeciesRecord.SIZE * index); + } - return new SuperTrainingSpeciesRecord(Data, Offset + 0x248 + (4 * index)); + private static int GetOffsetHolder2(int index) + { + if ((uint)index >= MAX) + throw new ArgumentOutOfRangeException(nameof(index)); + return 0x248 + (SuperTrainingSpeciesRecord.SIZE * index); } /// @@ -169,7 +181,7 @@ public sealed class SuperTrainBlock : SaveBlock { if ((uint)index >= MAX_BAG) throw new ArgumentOutOfRangeException(nameof(index)); - return Data[Offset + 0x308 + index]; + return Data[0x308 + index]; } /// @@ -181,7 +193,7 @@ public sealed class SuperTrainBlock : SaveBlock { if ((uint)index >= MAX_BAG) throw new ArgumentOutOfRangeException(nameof(index)); - Data[Offset + 0x308 + index] = value; + Data[0x308 + index] = value; } /// @@ -231,8 +243,8 @@ public sealed class SuperTrainBlock : SaveBlock public uint TutorialIndex { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x314)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x314), value); + get => ReadUInt32LittleEndian(Data[0x314..]); + set => WriteUInt32LittleEndian(Data[0x314..], value); } /// @@ -280,18 +292,22 @@ public sealed class SuperTrainBlock : SaveBlock /// /// Clears all data in the block. /// - public void ClearBlock() => Array.Clear(Data, Offset, 0x318); + public void ClearBlock() => Data.Clear(); } -public sealed class SuperTrainingSpeciesRecord(byte[] Data, int Offset) : ISpeciesForm +public sealed class SuperTrainingSpeciesRecord(Memory raw) : ISpeciesForm { + public const int SIZE = 4; + + private Span Data => raw.Span; + /// /// of the Record Holder. /// public ushort Species { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0), value); + get => ReadUInt16LittleEndian(Data); + set => WriteUInt16LittleEndian(Data, value); } /// @@ -299,8 +315,8 @@ public sealed class SuperTrainingSpeciesRecord(byte[] Data, int Offset) : ISpeci /// public byte Form { - get => Data[Offset + 2]; - set => Data[Offset + 2] = value; + get => Data[2]; + set => Data[2] = value; } /// @@ -309,17 +325,14 @@ public sealed class SuperTrainingSpeciesRecord(byte[] Data, int Offset) : ISpeci /// public byte Gender { - get => Data[Offset + 3]; - set => Data[Offset + 3] = value; + get => Data[3]; + set => Data[3] = value; } /// /// Wipes the record holder's pk-related data. /// - public void Clear() - { - Species = 0; Form = Gender = 0; - } + public void Clear() => Data.Clear(); /// /// Sets the data to match what is in the provided reference. diff --git a/PKHeX.Core/Saves/Substructures/Gen6/TrainerFashion6.cs b/PKHeX.Core/Saves/Substructures/Gen6/TrainerFashion6.cs index ecba7f411..b5ca0c8df 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/TrainerFashion6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/TrainerFashion6.cs @@ -3,37 +3,28 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public abstract class TrainerFashion6 +public abstract class TrainerFashion6(ReadOnlySpan span) { - protected uint data0; - protected uint data1; - protected uint data2; - protected uint data3; + public const int SIZE = 16; - protected TrainerFashion6(ReadOnlySpan data, int offset) : this(data[offset..]) { } + protected uint data0 = ReadUInt32LittleEndian(span); + protected uint data1 = ReadUInt32LittleEndian(span[04..]); + protected uint data2 = ReadUInt32LittleEndian(span[08..]); + protected uint data3 = ReadUInt32LittleEndian(span[12..]); - private TrainerFashion6(ReadOnlySpan span) - { - data0 = ReadUInt32LittleEndian(span); - data1 = ReadUInt32LittleEndian(span[04..]); - data2 = ReadUInt32LittleEndian(span[08..]); - data3 = ReadUInt32LittleEndian(span[12..]); - } - - public static TrainerFashion6 GetFashion(byte[] data, int offset, byte gender) + public static TrainerFashion6 GetFashion(ReadOnlySpan data, byte gender) { if (gender == 0) // m - return new Fashion6Male(data, offset); - return new Fashion6Female(data, offset); + return new Fashion6Male(data); + return new Fashion6Female(data); } - public void Write(byte[] data, int offset) + public void Write(Span data) { - var span = data.AsSpan(offset); - WriteUInt32LittleEndian(span, data0); - WriteUInt32LittleEndian(span[04..], data1); - WriteUInt32LittleEndian(span[08..], data2); - WriteUInt32LittleEndian(span[12..], data3); + WriteUInt32LittleEndian(data, data0); + WriteUInt32LittleEndian(data[04..], data1); + WriteUInt32LittleEndian(data[08..], data2); + WriteUInt32LittleEndian(data[12..], data3); } protected static uint GetBits(uint value, int startPos, int bits) @@ -76,7 +67,7 @@ public abstract class TrainerFashion6 } } -public sealed class Fashion6Male(byte[] data, int offset) : TrainerFashion6(data, offset) +public sealed class Fashion6Male(ReadOnlySpan data) : TrainerFashion6(data) { public uint Version { get => GetBits(data0, 0, 3); set => data0 = SetBits(data0, 0, 3, value); } public uint Model { get => GetBits(data0, 3, 3); set => data0 = SetBits(data0, 3, 3, value); } @@ -273,7 +264,7 @@ public sealed class Fashion6Male(byte[] data, int offset) : TrainerFashion6(data } } -public sealed class Fashion6Female(byte[] data, int offset) : TrainerFashion6(data, offset) +public sealed class Fashion6Female(ReadOnlySpan data) : TrainerFashion6(data) { public uint Version { get => GetBits(data0, 0, 3); set => data0 = SetBits(data0, 0, 3, value); } public uint Model { get => GetBits(data0, 3, 3); set => data0 = SetBits(data0, 3, 3, value); } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/TrainerSprite6.cs b/PKHeX.Core/Saves/Substructures/Gen6/TrainerSprite6.cs index a28cc6bb2..a069562fe 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/TrainerSprite6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/TrainerSprite6.cs @@ -1,6 +1,6 @@ -namespace PKHeX.Core; +namespace PKHeX.Core; -public enum TrainerSprite6 +public enum TrainerSprite6 : byte { Serena = 00, Calem = 01, diff --git a/PKHeX.Core/Saves/Substructures/Gen6/UnionPokemon6.cs b/PKHeX.Core/Saves/Substructures/Gen6/UnionPokemon6.cs new file mode 100644 index 000000000..48650a4f8 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen6/UnionPokemon6.cs @@ -0,0 +1,10 @@ +using System; + +namespace PKHeX.Core; + +public sealed class UnionPokemon6(SAV6 sav, Memory raw) : SaveBlock(sav, raw) +{ + private const int SizeStored = PokeCrypto.SIZE_6STORED; + + public Memory this[int index] => Raw[..SizeStored]; +} diff --git a/PKHeX.Core/Saves/Substructures/Gen7/BattleAgency7.cs b/PKHeX.Core/Saves/Substructures/Gen7/BattleAgency7.cs index d2e399a72..43e280b22 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/BattleAgency7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/BattleAgency7.cs @@ -2,13 +2,16 @@ using System; namespace PKHeX.Core; -public sealed class BattleAgency7(SAV7USUM sav, int offset) : SaveBlock(sav, offset) +public sealed class BattleAgency7(SAV7USUM sav, Memory raw) : SaveBlock(sav, raw) { - public int GetSlotOffset(int slot) => Offset + slot switch + public static int GetSlotOffset(int slot) => slot switch { 0 => 0, 1 => PokeCrypto.SIZE_6STORED, + // 0x30 bytes in between 2 => 0x220, _ => throw new ArgumentOutOfRangeException(nameof(slot)), }; + + public Memory this[int i] => Raw.Slice(GetSlotOffset(i), PokeCrypto.SIZE_6STORED); } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/BattleTree7.cs b/PKHeX.Core/Saves/Substructures/Gen7/BattleTree7.cs index fd5f50492..0519d4764 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/BattleTree7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/BattleTree7.cs @@ -4,7 +4,7 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class BattleTree7(SAV7 sav, int offset) : SaveBlock(sav, offset) +public sealed class BattleTree7(SAV7 sav, Memory raw) : SaveBlock(sav, raw) { public const int BattleTypeMax = 4; @@ -13,7 +13,7 @@ public sealed class BattleTree7(SAV7 sav, int offset) : SaveBlock(sav, off ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(battletype, BattleTypeMax); var offset = GetStreakOffset(battletype, super, max); - return ReadUInt16LittleEndian(Data.AsSpan(Offset + offset)); + return ReadUInt16LittleEndian(Data[offset..]); } public void SetTreeStreak(int value, int battletype, bool super, bool max) @@ -24,7 +24,7 @@ public sealed class BattleTree7(SAV7 sav, int offset) : SaveBlock(sav, off value = ushort.MaxValue; var offset = GetStreakOffset(battletype, super, max); - WriteUInt16LittleEndian(Data.AsSpan(Offset + offset), (ushort)value); + WriteUInt16LittleEndian(Data[offset..], (ushort)value); } private static int GetStreakOffset(int battletype, bool super, bool max) @@ -43,12 +43,12 @@ public sealed class BattleTree7(SAV7 sav, int offset) : SaveBlock(sav, off { ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, ScoutCount); - var id = ReadInt16LittleEndian(Data.AsSpan(Offset + 0x24 + (index * 2))); - var p1 = ReadInt16LittleEndian(Data.AsSpan(Offset + 0x88 + (index * 2))); - var p2 = ReadInt16LittleEndian(Data.AsSpan(Offset + 0xEC + (index * 2))); + var id = ReadInt16LittleEndian(Data[(0x24 + (index * 2))..]); + var p1 = ReadInt16LittleEndian(Data[(0x88 + (index * 2))..]); + var p2 = ReadInt16LittleEndian(Data[(0xEC + (index * 2))..]); - var a1 = (sbyte)Data[Offset + 0x154 + index]; - var a2 = (sbyte)Data[Offset + 0x186 + index]; + var a1 = (sbyte)Data[0x154 + index]; + var a2 = (sbyte)Data[0x186 + index]; var poke1 = new BattleTreePokemon(p1, a1); var poke2 = new BattleTreePokemon(p2, a2); @@ -60,18 +60,18 @@ public sealed class BattleTree7(SAV7 sav, int offset) : SaveBlock(sav, off if ((uint)index >= ScoutCount) throw new ArgumentOutOfRangeException(nameof(index)); - WriteInt16LittleEndian(Data.AsSpan(Offset + 0x24 + (index * 2)), tr.ID ); - WriteInt16LittleEndian(Data.AsSpan(Offset + 0x88 + (index * 2)), tr.Poke1.ID); - WriteInt16LittleEndian(Data.AsSpan(Offset + 0xEC + (index * 2)), tr.Poke2.ID); + WriteInt16LittleEndian(Data[(0x24 + (index * 2))..], tr.ID ); + WriteInt16LittleEndian(Data[(0x88 + (index * 2))..], tr.Poke1.ID); + WriteInt16LittleEndian(Data[(0xEC + (index * 2))..], tr.Poke2.ID); - Data[Offset + 0x154 + index] = (byte)tr.Poke1.AbilityIndex; - Data[Offset + 0x186 + index] = (byte)tr.Poke2.AbilityIndex; + Data[0x154 + index] = (byte)tr.Poke1.AbilityIndex; + Data[0x186 + index] = (byte)tr.Poke2.AbilityIndex; } public int Music { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x18)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x18), value); + get => ReadInt32LittleEndian(Data[0x18..]); + set => WriteInt32LittleEndian(Data[0x18..], value); } public BattleTreeTrainer[] ScoutedTrainers diff --git a/PKHeX.Core/Saves/Substructures/Gen7/BoxLayout7.cs b/PKHeX.Core/Saves/Substructures/Gen7/BoxLayout7.cs index b9d33af8f..ab041144a 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/BoxLayout7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/BoxLayout7.cs @@ -3,7 +3,8 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class BoxLayout7(SAV7 sav, int offset) : SaveBlock(sav, offset), IBoxDetailName, IBoxDetailWallpaper, ITeamIndexSet +public sealed class BoxLayout7(SAV7 sav, Memory raw) : SaveBlock(sav, raw), + IBoxDetailName, IBoxDetailWallpaper, ITeamIndexSet { private const int BoxCount = 32; @@ -19,7 +20,7 @@ public sealed class BoxLayout7(SAV7 sav, int offset) : SaveBlock(sav, offs private const int NONE_SELECTED = -1; public readonly int[] TeamSlots = new int[TeamCount * 6]; - public int GetBoxWallpaperOffset(int box) => Offset + PCBackgrounds + box; + public static int GetBoxWallpaperOffset(int box) => PCBackgrounds + box; public int GetBoxWallpaper(int box) { @@ -35,40 +36,40 @@ public sealed class BoxLayout7(SAV7 sav, int offset) : SaveBlock(sav, offs Data[GetBoxWallpaperOffset(box)] = (byte)value; } - private Span GetBoxNameSpan(int box) => Data.AsSpan(GetBoxNameOffset(box), SAV6.LongStringLength); - private int GetBoxNameOffset(int box) => Offset + (SAV6.LongStringLength * box); + private Span GetBoxNameSpan(int box) => Data.Slice(GetBoxNameOffset(box), SAV6.LongStringLength); + private static int GetBoxNameOffset(int box) => (SAV6.LongStringLength * box); public string GetBoxName(int box) => SAV.GetString(GetBoxNameSpan(box)); public void SetBoxName(int box, ReadOnlySpan value) => SAV.SetString(GetBoxNameSpan(box), value, StringMaxLength, StringConverterOption.ClearZero); public byte[] BoxFlags { - get => [ Data[Offset + PCFlags] ]; // bits for wallpaper unlocks + get => [ Data[PCFlags] ]; // bits for wallpaper unlocks set { if (value.Length != 1) return; - Data[Offset + PCFlags] = value[0]; + Data[PCFlags] = value[0]; } } public int BoxesUnlocked { - get => Data[Offset + Unlocked]; + get => Data[Unlocked]; set { if (value > BoxCount) value = BoxCount; - Data[Offset + Unlocked] = (byte)value; + Data[Unlocked] = (byte)value; } } - public int CurrentBox { get => Data[Offset + LastViewedBoxOffset]; set => Data[Offset + LastViewedBoxOffset] = (byte)value; } + public int CurrentBox { get => Data[LastViewedBoxOffset]; set => Data[LastViewedBoxOffset] = (byte)value; } public void LoadBattleTeams() { for (int i = 0; i < TeamCount * 6; i++) { - short val = ReadInt16LittleEndian(Data.AsSpan(Offset + BattleBoxFlags + (i * 2))); + short val = ReadInt16LittleEndian(Data[(BattleBoxFlags + (i * 2))..]); if (val < 0) { TeamSlots[i] = NONE_SELECTED; @@ -92,7 +93,7 @@ public sealed class BoxLayout7(SAV7 sav, int offset) : SaveBlock(sav, offs public void SaveBattleTeams() { - var span = Data.AsSpan(Offset + BattleBoxFlags); + var span = Data[BattleBoxFlags..]; for (int i = 0; i < TeamCount * 6; i++) { int index = TeamSlots[i]; @@ -114,8 +115,8 @@ public sealed class BoxLayout7(SAV7 sav, int offset) : SaveBlock(sav, offs SetIsTeamLocked(i, false); } - public bool GetIsTeamLocked(int team) => Data[Offset + PCBackgrounds - TeamCount - team] == 1; - public void SetIsTeamLocked(int team, bool value) => Data[Offset + PCBackgrounds - TeamCount - team] = value ? (byte)1 : (byte)0; + public bool GetIsTeamLocked(int team) => Data[PCBackgrounds - TeamCount - team] == 1; + public void SetIsTeamLocked(int team, bool value) => Data[PCBackgrounds - TeamCount - team] = value ? (byte)1 : (byte)0; public string this[int i] { diff --git a/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7.cs b/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7.cs index 2ab2d4924..bd2cd4b28 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7.cs @@ -3,7 +3,7 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class ConfigSave7(SAV7 sav, int offset) : SaveBlock(sav, offset) +public sealed class ConfigSave7(SAV7 sav, Memory raw) : SaveBlock(sav, raw) { /* ===32 bits=== * talkSpeed:2 0,1 @@ -17,8 +17,8 @@ public sealed class ConfigSave7(SAV7 sav, int offset) : SaveBlock(sav, off public int ConfigValue { - get => ReadInt32LittleEndian(Data.AsSpan(Offset)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset), value); + get => ReadInt32LittleEndian(Data); + set => WriteInt32LittleEndian(Data, value); } public int TalkingSpeed diff --git a/PKHeX.Core/Saves/Substructures/Gen7/Daycare7.cs b/PKHeX.Core/Saves/Substructures/Gen7/Daycare7.cs index b57889be8..d6bcc8b2c 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/Daycare7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/Daycare7.cs @@ -1,42 +1,28 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class Daycare7(SAV7 sav, int offset) : SaveBlock(sav, offset) +public sealed class Daycare7(SAV7 sav, Memory raw) : SaveBlock(sav, raw), IDaycareStorage, IDaycareEggState, IDaycareRandomState { - public const int DaycareSeedSize = 32; // 128 bits + private const int SlotSize = PokeCrypto.SIZE_6STORED + 1; - public bool GetIsOccupied(int slot) + public int DaycareSlotCount => 2; + + public bool IsDaycareOccupied(int slot) => Data[SlotSize * slot] != 0; + public void SetDaycareOccupied(int slot, bool occupied) => Data[SlotSize * slot] = occupied ? (byte)1 : (byte)0; + + public Memory GetDaycareSlot(int slot) => Raw.Slice(1 + (slot * SlotSize), PokeCrypto.SIZE_6STORED); + + public bool IsEggAvailable { - return Data[Offset + ((PokeCrypto.SIZE_6STORED + 1) * slot)] != 0; + get => Data[0x1D8] == 1; + set => Data[0x1D8] = value ? (byte)1 : (byte)0; } - public void SetOccupied(int slot, bool occupied) + public UInt128 Seed { - Data[Offset + ((PokeCrypto.SIZE_6STORED + 1) * slot)] = occupied ? (byte)1 : (byte)0; - } - - public int GetDaycareSlotOffset(int slot) - { - return Offset + 1 + (slot * (PokeCrypto.SIZE_6STORED + 1)); - } - - public bool HasEgg - { - get => Data[Offset + 0x1D8] == 1; - set => Data[Offset + 0x1D8] = value ? (byte)1 : (byte)0; - } - - public string RNGSeed - { - get => Util.GetHexStringFromBytes(Data.AsSpan(Offset + 0x1DC, DaycareSeedSize / 2)); - set - { - if (value.Length != DaycareSeedSize) - return; - - var data = Util.GetBytesFromHexString(value); - SAV.SetData(data, Offset + 0x1DC); - } + get => ReadUInt128LittleEndian(Data[0x1DC..]); + set => WriteUInt128LittleEndian(Data[0x1DC..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/EventWork7.cs b/PKHeX.Core/Saves/Substructures/Gen7/EventWork7.cs new file mode 100644 index 000000000..e8365eb63 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen7/EventWork7.cs @@ -0,0 +1,119 @@ +using System; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core; + +public abstract class EventWork7(SAV7 sav, Memory raw) : SaveBlock(sav, raw), IEventFlag37 +{ + protected abstract Span WorkSpan { get; } + protected abstract Span FlagSpan { get; } + protected abstract Memory FameSpan { get; } + public HallOfFame7 Fame => new(FameSpan); + public abstract int EventFlagCount { get; } + public abstract int EventWorkCount { get; } + public abstract int TotalZygardeCellCount { get; } + + public bool GetEventFlag(int flagNumber) => FlagUtil.GetFlag(FlagSpan, flagNumber); + public void SetEventFlag(int flagNumber, bool value) => FlagUtil.SetFlag(FlagSpan, flagNumber, value); + + public ushort GetWork(int index) => ReadUInt16LittleEndian(WorkSpan[(index * sizeof(ushort))..]); + public void SetWork(int index, ushort value) => WriteUInt16LittleEndian(WorkSpan[(index * sizeof(ushort))..], value); + + private const int cellstotal = 161; + private const int cellscollected = 169; + private const int celloffset = 198; + + public ushort ZygardeCellCount { get => GetWork(cellscollected); set => SetWork(cellscollected, value); } + public ushort ZygardeCellTotal { get => GetWork(cellstotal); set => SetWork(cellstotal, value); } + + public ushort GetZygardeCell(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)TotalZygardeCellCount); + return GetWork(celloffset + index); + } + + public void SetZygardeCell(int index, ushort val) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)TotalZygardeCellCount); + SetWork(celloffset + index, val); + } +} + +public sealed class EventWork7SM(SAV7SM sav, Memory raw) : EventWork7(sav, raw) +{ + public const int WorkCount = 1000; // u16 + public const int FlagCount = 4000; // bits + + private const int OffsetWork = 0x0; + private const int OffsetFlag = OffsetWork + (WorkCount * sizeof(ushort)); // 0x7D0 + + // Hallf of Fame + private const int OffsetPostData = OffsetFlag + (FlagCount / 8); // 0x9C4 + + public override int EventFlagCount => FlagCount; + public override int EventWorkCount => WorkCount; + public override int TotalZygardeCellCount => 95; + protected override Span WorkSpan => Raw.Span[..(WorkCount * sizeof(ushort))]; + protected override Span FlagSpan => Raw.Span.Slice(OffsetFlag, FlagCount / 8); + protected override Memory FameSpan => Raw.Slice(OffsetPostData, HallOfFame7.SIZE); + + public const int MagearnaEventFlag = 3100; +} + +public sealed class EventWork7USUM(SAV7USUM sav, Memory raw) : EventWork7(sav, raw) +{ + public const int WorkCount = 1000; // u16 + public const int FlagCount = 4960; + + private const int OffsetWork = 0x0; + private const int OffsetFlag = OffsetWork + (WorkCount * sizeof(ushort)); // 0x7D0 + + // Hallf of Fame + private const int OffsetPostData = OffsetFlag + (FlagCount / 8); // 0xA3C + + public override int EventFlagCount => FlagCount; + public override int EventWorkCount => WorkCount; + public override int TotalZygardeCellCount => 100; + protected override Span WorkSpan => Raw.Span[..(EventWorkCount * sizeof(ushort))]; + protected override Span FlagSpan => Raw.Span.Slice(OffsetFlag, FlagCount / 8); + protected override Memory FameSpan => Raw.Slice(OffsetPostData, HallOfFame7.SIZE); +} + +public sealed class HallOfFame7(Memory raw) +{ + // this HoF region is immediately after the Event Flags + private const uint MaxCount = 12; + + public const int SIZE = 2 * (6 * sizeof(ushort)); // 24 bytes + + private Span Data => raw.Span; + + public ushort First1 { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, value); } + public ushort First2 { get => ReadUInt16LittleEndian(Data[0x02..]); set => WriteUInt16LittleEndian(Data[0x02..], value); } + public ushort First3 { get => ReadUInt16LittleEndian(Data[0x04..]); set => WriteUInt16LittleEndian(Data[0x04..], value); } + public ushort First4 { get => ReadUInt16LittleEndian(Data[0x04..]); set => WriteUInt16LittleEndian(Data[0x04..], value); } + public ushort First5 { get => ReadUInt16LittleEndian(Data[0x06..]); set => WriteUInt16LittleEndian(Data[0x06..], value); } + public ushort First6 { get => ReadUInt16LittleEndian(Data[0x08..]); set => WriteUInt16LittleEndian(Data[0x08..], value); } + + public ushort Current1 { get => ReadUInt16LittleEndian(Data[0x0A..]); set => WriteUInt16LittleEndian(Data[0x0A..], value); } + public ushort Current2 { get => ReadUInt16LittleEndian(Data[0x0C..]); set => WriteUInt16LittleEndian(Data[0x0C..], value); } + public ushort Current3 { get => ReadUInt16LittleEndian(Data[0x0E..]); set => WriteUInt16LittleEndian(Data[0x0E..], value); } + public ushort Current4 { get => ReadUInt16LittleEndian(Data[0x10..]); set => WriteUInt16LittleEndian(Data[0x10..], value); } + public ushort Current5 { get => ReadUInt16LittleEndian(Data[0x12..]); set => WriteUInt16LittleEndian(Data[0x12..], value); } + public ushort Current6 { get => ReadUInt16LittleEndian(Data[0x14..]); set => WriteUInt16LittleEndian(Data[0x14..], value); } + + public ushort GetEntry(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, MaxCount); + if ((uint)index >= MaxCount) + throw new ArgumentOutOfRangeException(nameof(index)); + return ReadUInt16LittleEndian(Data[(index * 2)..]); + } + + public void SetEntry(int index, ushort value) + { + if ((uint)index >= MaxCount) + throw new ArgumentOutOfRangeException(nameof(index)); + WriteUInt16LittleEndian(Data[(index * 2)..], value); + } +} diff --git a/PKHeX.Core/Saves/Substructures/Gen7/FashionBlock7.cs b/PKHeX.Core/Saves/Substructures/Gen7/FashionBlock7.cs index 9b07f47e1..736d76bd1 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/FashionBlock7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/FashionBlock7.cs @@ -2,7 +2,7 @@ using System; namespace PKHeX.Core; -public sealed class FashionBlock7(SAV7 sav, int offset) : SaveBlock(sav, offset) +public sealed class FashionBlock7(SAV7 sav, Memory raw) : SaveBlock(sav, raw) { private const int FashionLength = 0x1A08; @@ -10,7 +10,7 @@ public sealed class FashionBlock7(SAV7 sav, int offset) : SaveBlock(sav, o { get { - var data = SAV.Data.AsSpan(Offset, 0x5A8).ToArray(); + var data = Data[..0x5A8].ToArray(); return Array.ConvertAll(data, b => new FashionItem7(b)); } set @@ -18,11 +18,11 @@ public sealed class FashionBlock7(SAV7 sav, int offset) : SaveBlock(sav, o if (value.Length != 0x5A8) throw new ArgumentOutOfRangeException($"Unexpected size: 0x{value.Length:X}"); var arr = Array.ConvertAll(value, z => z.Value); - SAV.SetData(arr, Offset); + SAV.SetData(Data[..0x5A8], arr); } } - public void Clear() => Array.Clear(Data, Offset, FashionLength); + public void Clear() => Data[..FashionLength].Clear(); /// /// Resets the fashion unlocks to default values. @@ -31,7 +31,7 @@ public sealed class FashionBlock7(SAV7 sav, int offset) : SaveBlock(sav, o { var offsetList = GetDefaultFashionOffsets(SAV); foreach (var ofs in offsetList) - SAV.Data[Offset + ofs] = 3; // owned | new + Data[ofs] = 3; // owned | new } private static ReadOnlySpan GetDefaultFashionOffsets(SAV7 sav) => sav switch @@ -48,6 +48,8 @@ public sealed class FashionBlock7(SAV7 sav, int offset) : SaveBlock(sav, o private static ReadOnlySpan DefaultFashionOffsetSM_F => [ 0x000, 0x100, 0x223, 0x288, 0x3B4, 0x452, 0x517 ]; private static ReadOnlySpan DefaultFashionOffsetUU_M => [ 0x03A, 0x109, 0x1DA, 0x305, 0x3D9, 0x4B1, 0x584 ]; private static ReadOnlySpan DefaultFashionOffsetUU_F => [ 0x05E, 0x208, 0x264, 0x395, 0x3B4, 0x4F9, 0x5A8 ]; + + public void ImportPayload(ReadOnlySpan data) => SAV.SetData(Data[..FashionLength], data); } // Every fashion item is 2 bits, New Flag (high) & Owned Flag (low) diff --git a/PKHeX.Core/Saves/Substructures/Gen7/FestaFacility.cs b/PKHeX.Core/Saves/Substructures/Gen7/FestaFacility.cs index 034526ea0..8c030d94a 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/FestaFacility.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/FestaFacility.cs @@ -3,39 +3,28 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class FestaFacility +public sealed class FestaFacility(Memory raw, int language) { - private const int SIZE = 0x48; - private readonly byte[] Data; - private readonly int Language; - private readonly int ofs; - - public FestaFacility(SAV7 sav, int index) - { - ofs = (index * SIZE) + sav.Festa.Offset + 0x310; - Data = sav.Data.AsSpan(ofs, SIZE).ToArray(); - Language = sav.Language; - } - - public void CopyTo(SAV7 sav) => sav.SetData(Data, ofs); + public const int SIZE = 0x48; + private Span Data => raw.Span; public int Type { get => Data[0x00]; set => Data[0x00] = (byte)value; } public int Color { get => Data[0x01]; set => Data[0x01] = (byte)value; } public bool IsIntroduced { get => Data[0x02] != 0; set => Data[0x02] = value ? (byte)1 : (byte)0; } public byte Gender { get => Data[0x03]; set => Data[0x03] = value; } - public string OriginalTrainerName { get => StringConverter7.GetString(Data.AsSpan(0x04, 0x1A)); set => StringConverter7.SetString(Data.AsSpan(0x04, 0x1A), value, 12, Language, StringConverterOption.ClearZero); } + public string OriginalTrainerName { get => StringConverter7.GetString(Data.Slice(0x04, 0x1A)); set => StringConverter7.SetString(Data.Slice(0x04, 0x1A), value, 12, language, StringConverterOption.ClearZero); } - private int MessageMeet { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); } - private int MessagePart { get => ReadUInt16LittleEndian(Data.AsSpan(0x20)); set => WriteUInt16LittleEndian(Data.AsSpan(0x20), (ushort)value); } - private int MessageMoved { get => ReadUInt16LittleEndian(Data.AsSpan(0x22)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22), (ushort)value); } - private int MessageDisappointed { get => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); } + private int MessageMeet { get => ReadUInt16LittleEndian(Data[0x1E..]); set => WriteUInt16LittleEndian(Data[0x1E..], (ushort)value); } + private int MessagePart { get => ReadUInt16LittleEndian(Data[0x20..]); set => WriteUInt16LittleEndian(Data[0x20..], (ushort)value); } + private int MessageMoved { get => ReadUInt16LittleEndian(Data[0x22..]); set => WriteUInt16LittleEndian(Data[0x22..], (ushort)value); } + private int MessageDisappointed { get => ReadUInt16LittleEndian(Data[0x24..]); set => WriteUInt16LittleEndian(Data[0x24..], (ushort)value); } public int UsedLuckyRank { get => Data[0x26]; set => Data[0x26] = (byte)value; } // 1:a bit, 2:a whole lot, 3:a whole ton public int UsedLuckyPlace { get => Data[0x27]; set => Data[0x27] = (byte)value; } // 1:GTS, ... 7:haunted house - public uint UsedFlags { get => ReadUInt32LittleEndian(Data.AsSpan(0x28)); set => WriteUInt32LittleEndian(Data.AsSpan(0x28), value); } - public uint UsedRandStat { get => ReadUInt32LittleEndian(Data.AsSpan(0x2C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x2C), value); } + public uint UsedFlags { get => ReadUInt32LittleEndian(Data[0x28..]); set => WriteUInt32LittleEndian(Data[0x28..], value); } + public uint UsedRandStat { get => ReadUInt32LittleEndian(Data[0x2C..]); set => WriteUInt32LittleEndian(Data[0x2C..], value); } - public int NPC { get => Math.Max(0, ReadInt32LittleEndian(Data.AsSpan(0x30))); set => WriteInt32LittleEndian(Data.AsSpan(0x30), Math.Max(0, value)); } - public Span TrainerFesID => Data.AsSpan(0x34, 0xC); + public int NPC { get => Math.Max(0, ReadInt32LittleEndian(Data[0x30..])); set => WriteInt32LittleEndian(Data[0x30..], Math.Max(0, value)); } + public Span TrainerFesID => Data.Slice(0x34, 0xC); public void ClearTrainerFesID() => TrainerFesID.Clear(); public int ExchangeLeftCount { get => Data[0x40]; set => Data[0x40] = (byte)value; } // used when Type=Exchange diff --git a/PKHeX.Core/Saves/Substructures/Gen7/FieldMenu7.cs b/PKHeX.Core/Saves/Substructures/Gen7/FieldMenu7.cs index ef385f85e..9473299ca 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/FieldMenu7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/FieldMenu7.cs @@ -3,17 +3,17 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class FieldMenu7(SAV7 sav, int offset) : SaveBlock(sav, offset) +public sealed class FieldMenu7(SAV7 sav, Memory raw) : SaveBlock(sav, raw) { // US/UM ONLY public ushort RotomAffection { - get => ReadUInt16LittleEndian(SAV.Data.AsSpan(Offset + 0x1A)); - set => WriteUInt16LittleEndian(SAV.Data.AsSpan(Offset + 0x1A), Math.Min((ushort)1000, value)); + get => ReadUInt16LittleEndian(Data[0x1A..]); + set => WriteUInt16LittleEndian(Data[0x1A..], Math.Min((ushort)1000, value)); } - public bool RotomLoto1 { get => (SAV.Data[Offset + 0x2A] & 1) == 1; set => SAV.Data[Offset + 0x2A] = (byte)((SAV.Data[Offset + 0x2A] & ~1) | (value ? 1 : 0)); } - public bool RotomLoto2 { get => (SAV.Data[Offset + 0x2A] & 2) == 2; set => SAV.Data[Offset + 0x2A] = (byte)((SAV.Data[Offset + 0x2A] & ~2) | (value ? 2 : 0)); } + public bool RotomLoto1 { get => (Data[0x2A] & 1) == 1; set => Data[0x2A] = (byte)((Data[0x2A] & ~1) | (value ? 1 : 0)); } + public bool RotomLoto2 { get => (Data[0x2A] & 2) == 2; set => Data[0x2A] = (byte)((Data[0x2A] & ~2) | (value ? 2 : 0)); } public string RotomOT { @@ -21,5 +21,5 @@ public sealed class FieldMenu7(SAV7 sav, int offset) : SaveBlock(sav, offs set => SAV.SetString(RotomNameSpan, value, SAV.MaxStringLengthOT, StringConverterOption.ClearZero); } - private Span RotomNameSpan => Data.AsSpan(Offset + 0x30, 0x1A); + private Span RotomNameSpan => Data.Slice(0x30, 0x1A); } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/FieldMoveModelSave7.cs b/PKHeX.Core/Saves/Substructures/Gen7/FieldMoveModelSave7.cs index be7798e42..4e0cb57e2 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/FieldMoveModelSave7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/FieldMoveModelSave7.cs @@ -3,14 +3,14 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class FieldMoveModelSave7(SAV7 sav, int offset) : SaveBlock(sav, offset) +public sealed class FieldMoveModelSave7(SAV7 sav, Memory raw) : SaveBlock(sav, raw) { - //public int Unknown { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x00)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x00), (ushort)value); } // related to Ride Pokémon - public float X { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x08)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x08), value); } - public float Z { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x0C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x0C), value); } - public float Y { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x10)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x10), value); } - public float RX { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x14)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x14), value); } - public float RZ { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x18)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x18), value); } - public float RY { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x1C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x1C), value); } - public float RW { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x20)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x20), value); } + //public int Unknown { get => ReadUInt16LittleEndian(Data.Slice(0x00)); set => WriteUInt16LittleEndian(Data.Slice(0x00), (ushort)value); } // related to Ride Pokémon + public float X { get => ReadSingleLittleEndian(Data[0x08..]); set => WriteSingleLittleEndian(Data[0x08..], value); } + public float Z { get => ReadSingleLittleEndian(Data[0x0C..]); set => WriteSingleLittleEndian(Data[0x0C..], value); } + public float Y { get => ReadSingleLittleEndian(Data[0x10..]); set => WriteSingleLittleEndian(Data[0x10..], value); } + public float RX { get => ReadSingleLittleEndian(Data[0x14..]); set => WriteSingleLittleEndian(Data[0x14..], value); } + public float RZ { get => ReadSingleLittleEndian(Data[0x18..]); set => WriteSingleLittleEndian(Data[0x18..], value); } + public float RY { get => ReadSingleLittleEndian(Data[0x1C..]); set => WriteSingleLittleEndian(Data[0x1C..], value); } + public float RW { get => ReadSingleLittleEndian(Data[0x20..]); set => WriteSingleLittleEndian(Data[0x20..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/GTS7.cs b/PKHeX.Core/Saves/Substructures/Gen7/GTS7.cs new file mode 100644 index 000000000..7efeef616 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen7/GTS7.cs @@ -0,0 +1,11 @@ +using System; + +namespace PKHeX.Core; + +public sealed class GTS7(SAV7 sav, Memory raw) : SaveBlock(sav, raw) +{ + // 0x00: Stored Upload + private const int SizeStored = PokeCrypto.SIZE_6STORED; + + public Memory Upload => Raw[..SizeStored]; +} diff --git a/PKHeX.Core/Saves/Substructures/Gen7/GameTime7.cs b/PKHeX.Core/Saves/Substructures/Gen7/GameTime7.cs index 2960e65a0..3674fbb79 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/GameTime7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/GameTime7.cs @@ -3,16 +3,16 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class GameTime7(SAV7 sav, int offset) : SaveBlock(sav, offset) +public sealed class GameTime7(SAV7 sav, Memory raw) : SaveBlock(sav, raw) { - public int ResumeYear { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x4)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x4), value); } - public int ResumeMonth { get => Data[Offset + 0x8]; set => Data[Offset + 0x8] = (byte)value; } - public int ResumeDay { get => Data[Offset + 0x9]; set => Data[Offset + 0x9] = (byte)value; } - public int ResumeHour { get => Data[Offset + 0xB]; set => Data[Offset + 0xB] = (byte)value; } - public int ResumeMinute { get => Data[Offset + 0xC]; set => Data[Offset + 0xC] = (byte)value; } - public int ResumeSeconds { get => Data[Offset + 0xD]; set => Data[Offset + 0xD] = (byte)value; } - public uint SecondsToStart { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x28)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x28), value); } - public uint SecondsToFame { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x30)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x30), value); } + public int ResumeYear { get => ReadInt32LittleEndian(Data[0x4..]); set => WriteInt32LittleEndian(Data[0x4..], value); } + public int ResumeMonth { get => Data[0x8]; set => Data[0x8] = (byte)value; } + public int ResumeDay { get => Data[0x9]; set => Data[0x9] = (byte)value; } + public int ResumeHour { get => Data[0xB]; set => Data[0xB] = (byte)value; } + public int ResumeMinute { get => Data[0xC]; set => Data[0xC] = (byte)value; } + public int ResumeSeconds { get => Data[0xD]; set => Data[0xD] = (byte)value; } + public uint SecondsToStart { get => ReadUInt32LittleEndian(Data[0x28..]); set => WriteUInt32LittleEndian(Data[0x28..], value); } + public uint SecondsToFame { get => ReadUInt32LittleEndian(Data[0x30..]); set => WriteUInt32LittleEndian(Data[0x30..], value); } - public ulong AlolaTime { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x48)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x48), value); } + public ulong AlolaTime { get => ReadUInt64LittleEndian(Data[0x48..]); set => WriteUInt64LittleEndian(Data[0x48..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/HallOfFame7.cs b/PKHeX.Core/Saves/Substructures/Gen7/HallOfFame7.cs deleted file mode 100644 index 0ccaf1414..000000000 --- a/PKHeX.Core/Saves/Substructures/Gen7/HallOfFame7.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using static System.Buffers.Binary.BinaryPrimitives; - -namespace PKHeX.Core; - -public sealed class HallOfFame7(SAV7 sav, int offset) : SaveBlock(sav, offset) -{ - // this HoF region is immediately after the Event Flags - private const int MaxCount = 12; - public int First1 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x00)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x00), (ushort)value); } - public int First2 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x02)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x02), (ushort)value); } - public int First3 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x04)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x04), (ushort)value); } - public int First4 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x04)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x04), (ushort)value); } - public int First5 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x06)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x06), (ushort)value); } - public int First6 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x08)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x08), (ushort)value); } - - public int Current1 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x0A), (ushort)value); } - public int Current2 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x0C)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x0C), (ushort)value); } - public int Current3 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x0E)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x0E), (ushort)value); } - public int Current4 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x10)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x10), (ushort)value); } - public int Current5 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x12)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x12), (ushort)value); } - public int Current6 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x14)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x14), (ushort)value); } - - public int GetEntry(int index) - { - if ((uint)index >= MaxCount) - throw new ArgumentOutOfRangeException(nameof(index)); - return ReadUInt16LittleEndian(SAV.Data.AsSpan(Offset + (index * 2))); - } - - public void SetEntry(int index, ushort value) - { - if ((uint)index >= MaxCount) - throw new ArgumentOutOfRangeException(nameof(index)); - WriteUInt16LittleEndian(SAV.Data.AsSpan(Offset + (index * 2)), value); - } -} diff --git a/PKHeX.Core/Saves/Substructures/Gen7/JoinFesta7.cs b/PKHeX.Core/Saves/Substructures/Gen7/JoinFesta7.cs index 30fefd281..c8a2f6528 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/JoinFesta7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/JoinFesta7.cs @@ -3,16 +3,30 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class JoinFesta7(SAV7 sav, int offset) : SaveBlock(sav, offset) +public sealed class JoinFesta7(SAV7 sav, Memory raw) : SaveBlock(sav, raw) { + public const uint FestaFacilityCount = 7; + + public FestaFacility GetFestaFacility(int index) + { + var chunk = Raw.Slice(GetFestaFacilityOffset(index), FestaFacility.SIZE); + return new FestaFacility(chunk, SAV.Language); + } + + private static int GetFestaFacilityOffset(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)index, FestaFacilityCount); + return 0x310 + (index * FestaFacility.SIZE); + } + public int FestaCoins { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x508)); + get => ReadInt32LittleEndian(Data[0x508..]); set { if (value > 9999999) value = 9999999; - WriteInt32LittleEndian(Data.AsSpan(Offset + 0x508), value); + WriteInt32LittleEndian(Data[0x508..], value); TotalFestaCoins = SAV.GetRecord(038) + value; // UsedFestaCoins } @@ -20,16 +34,16 @@ public sealed class JoinFesta7(SAV7 sav, int offset) : SaveBlock(sav, offs public int TotalFestaCoins { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x50C)); + get => ReadInt32LittleEndian(Data[0x50C..]); set { if (value > 9999999) value = 9999999; - WriteInt32LittleEndian(Data.AsSpan(Offset + 0x50C), value); + WriteInt32LittleEndian(Data[0x50C..], value); } } - private Span FestivalPlazaNameSpan => Data.AsSpan(Offset + 0x510, 0x2A); + private Span FestivalPlazaNameSpan => Data.Slice(0x510, 0x2A); public string FestivalPlazaName { @@ -37,25 +51,25 @@ public sealed class JoinFesta7(SAV7 sav, int offset) : SaveBlock(sav, offs set => StringConverter7.SetString(FestivalPlazaNameSpan, value, 20, 0, StringConverterOption.ClearZero); } - public ushort FestaRank { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x53A)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x53A), value); } - public ushort GetFestaMessage(int index) => ReadUInt16LittleEndian(Data.AsSpan(Offset + (index * 2))); - public void SetFestaMessage(int index, ushort value) => WriteUInt16LittleEndian(Data.AsSpan(Offset + (index * 2)), value); - public bool GetFestaPhraseUnlocked(int index) => Data[Offset + 0x2A50 + index] != 0; //index: 0 to 105:commonPhrases, 106:Lv100! + public ushort FestaRank { get => ReadUInt16LittleEndian(Data[0x53A..]); set => WriteUInt16LittleEndian(Data[0x53A..], value); } + public ushort GetFestaMessage(int index) => ReadUInt16LittleEndian(Data[(index * 2)..]); + public void SetFestaMessage(int index, ushort value) => WriteUInt16LittleEndian(Data[(index * 2)..], value); + public bool GetFestaPhraseUnlocked(int index) => Data[0x2A50 + index] != 0; //index: 0 to 105:commonPhrases, 106:Lv100! public void SetFestaPhraseUnlocked(int index, bool value) { if (GetFestaPhraseUnlocked(index) != value) - Data[Offset + 0x2A50 + index] = value ? (byte)1 : (byte)0; + Data[0x2A50 + index] = value ? (byte)1 : (byte)0; } - public byte GetFestPrizeReceived(int index) => Data[Offset + 0x53C + index]; - public void SetFestaPrizeReceived(int index, byte value) => Data[Offset + 0x53C + index] = value; - private int FestaYear { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x2F0)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x2F0), value); } - private int FestaMonth { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x2F4)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x2F4), value); } - private int FestaDay { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x2F8)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x2F8), value); } - private int FestaHour { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x300)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x300), value); } - private int FestaMinute { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x304)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x304), value); } - private int FestaSecond { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x308)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x308), value); } + public byte GetFestPrizeReceived(int index) => Data[0x53C + index]; + public void SetFestaPrizeReceived(int index, byte value) => Data[0x53C + index] = value; + private int FestaYear { get => ReadInt32LittleEndian(Data[0x2F0..]); set => WriteInt32LittleEndian(Data[0x2F0..], value); } + private int FestaMonth { get => ReadInt32LittleEndian(Data[0x2F4..]); set => WriteInt32LittleEndian(Data[0x2F4..], value); } + private int FestaDay { get => ReadInt32LittleEndian(Data[0x2F8..]); set => WriteInt32LittleEndian(Data[0x2F8..], value); } + private int FestaHour { get => ReadInt32LittleEndian(Data[0x300..]); set => WriteInt32LittleEndian(Data[0x300..], value); } + private int FestaMinute { get => ReadInt32LittleEndian(Data[0x304..]); set => WriteInt32LittleEndian(Data[0x304..], value); } + private int FestaSecond { get => ReadInt32LittleEndian(Data[0x308..]); set => WriteInt32LittleEndian(Data[0x308..], value); } public DateTime? FestaDate { diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/CaptureRecords.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/CaptureRecords.cs index ce5ae5be6..577271540 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/CaptureRecords.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/CaptureRecords.cs @@ -3,7 +3,7 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class CaptureRecords(SAV7b sav, int offset) : SaveBlock(sav, offset) +public sealed class CaptureRecords(SAV7b sav, Memory raw) : SaveBlock(sav, raw) { private const int ENTRY_COUNT = 153; private const int MAX_COUNT_ENTRY_CAPTURE = 9_999; @@ -25,12 +25,12 @@ public sealed class CaptureRecords(SAV7b sav, int offset) : SaveBlock(sav private const int TotalTransferredOffset = 0x7A8; // Calling into these directly, you should be sure that you're less than ENTRY_COUNT. - private int GetCapturedOffset(int index) => Offset + CapturedOffset + (index * 4); - private int GetTransferredOffset(int index) => Offset + TransferredOffset + (index * 4); - public uint GetCapturedCountIndex(int index) => ReadUInt32LittleEndian(Data.AsSpan(GetCapturedOffset(index))); - public uint GetTransferredCountIndex(int index) => ReadUInt32LittleEndian(Data.AsSpan(GetTransferredOffset(index))); - public void SetCapturedCountIndex(int index, uint value) => WriteUInt32LittleEndian(Data.AsSpan(GetCapturedOffset(index)), Math.Min(MAX_COUNT_ENTRY_CAPTURE, value)); - public void SetTransferredCountIndex(int index, uint value) => WriteUInt32LittleEndian(Data.AsSpan(GetTransferredOffset(index)), Math.Min(MAX_COUNT_ENTRY_TRANSFER, value)); + private int GetCapturedOffset(int index) => CapturedOffset + (index * 4); + private int GetTransferredOffset(int index) => TransferredOffset + (index * 4); + public uint GetCapturedCountIndex(int index) => ReadUInt32LittleEndian(Data[GetCapturedOffset(index)..]); + public uint GetTransferredCountIndex(int index) => ReadUInt32LittleEndian(Data[GetTransferredOffset(index)..]); + public void SetCapturedCountIndex(int index, uint value) => WriteUInt32LittleEndian(Data[GetCapturedOffset(index)..], Math.Min(MAX_COUNT_ENTRY_CAPTURE, value)); + public void SetTransferredCountIndex(int index, uint value) => WriteUInt32LittleEndian(Data[GetTransferredOffset(index)..], Math.Min(MAX_COUNT_ENTRY_TRANSFER, value)); public const ushort MaxIndex = 152; @@ -82,14 +82,14 @@ public sealed class CaptureRecords(SAV7b sav, int offset) : SaveBlock(sav public uint TotalCaptured { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + TotalCapturedOffset)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + TotalCapturedOffset), Math.Min(MAX_COUNT_TOTAL, value)); + get => ReadUInt32LittleEndian(Data[TotalCapturedOffset..]); + set => WriteUInt32LittleEndian(Data[TotalCapturedOffset..], Math.Min(MAX_COUNT_TOTAL, value)); } public uint TotalTransferred { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + TotalTransferredOffset)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + TotalTransferredOffset), Math.Min(MAX_COUNT_TOTAL, value)); + get => ReadUInt32LittleEndian(Data[TotalTransferredOffset..]); + set => WriteUInt32LittleEndian(Data[TotalTransferredOffset..], Math.Min(MAX_COUNT_TOTAL, value)); } public uint CalculateTotalCaptured() diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/ConfigSave7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/ConfigSave7b.cs index e17088c3c..389942a50 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/ConfigSave7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/ConfigSave7b.cs @@ -3,7 +3,7 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class ConfigSave7b(SAV7b sav, int offset) : SaveBlock(sav, offset) +public sealed class ConfigSave7b(SAV7b sav, Memory raw) : SaveBlock(sav, raw) { /* ===First 8 bits=== * talkSpeed:2 @@ -16,8 +16,8 @@ public sealed class ConfigSave7b(SAV7b sav, int offset) : SaveBlock(sav, public int ConfigValue { - get => ReadInt32LittleEndian(Data.AsSpan(Offset)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset), value); + get => ReadInt32LittleEndian(Data); + set => WriteInt32LittleEndian(Data, value); } public int TalkingSpeed diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Coordinates7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Coordinates7b.cs index 5f226a784..802325ed1 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Coordinates7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Coordinates7b.cs @@ -6,24 +6,24 @@ namespace PKHeX.Core; /// /// Stores the position of the player. /// -public sealed class Coordinates7b(SAV7b sav, int offset) : SaveBlock(sav, offset) +public sealed class Coordinates7b(SAV7b sav, Memory raw) : SaveBlock(sav, raw) { // Position - public float X { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x10)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x10), value); } - public float Z { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x14)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x14), value); } - public float Y { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x18)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x18), value); } + public float X { get => ReadSingleLittleEndian(Data[0x10..]); set => WriteSingleLittleEndian(Data[0x10..], value); } + public float Z { get => ReadSingleLittleEndian(Data[0x14..]); set => WriteSingleLittleEndian(Data[0x14..], value); } + public float Y { get => ReadSingleLittleEndian(Data[0x18..]); set => WriteSingleLittleEndian(Data[0x18..], value); } // Scale - public float SX { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x20)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x20), value); } - public float SZ { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x24)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x24), value); } - public float SY { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x28)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x28), value); } + public float SX { get => ReadSingleLittleEndian(Data[0x20..]); set => WriteSingleLittleEndian(Data[0x20..], value); } + public float SZ { get => ReadSingleLittleEndian(Data[0x24..]); set => WriteSingleLittleEndian(Data[0x24..], value); } + public float SY { get => ReadSingleLittleEndian(Data[0x28..]); set => WriteSingleLittleEndian(Data[0x28..], value); } // Rotation - public float RX { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x30)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x30), value); } - public float RZ { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x34)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x34), value); } - public float RY { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x38)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x38), value); } - public float RW { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x3C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x3C), value); } + public float RX { get => ReadSingleLittleEndian(Data[0x30..]); set => WriteSingleLittleEndian(Data[0x30..], value); } + public float RZ { get => ReadSingleLittleEndian(Data[0x34..]); set => WriteSingleLittleEndian(Data[0x34..], value); } + public float RY { get => ReadSingleLittleEndian(Data[0x38..]); set => WriteSingleLittleEndian(Data[0x38..], value); } + public float RW { get => ReadSingleLittleEndian(Data[0x3C..]); set => WriteSingleLittleEndian(Data[0x3C..], value); } // Map - public ulong M { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x6000)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x6000), value); } + public ulong M { get => ReadUInt64LittleEndian(Data[0x6000..]); set => WriteUInt64LittleEndian(Data[0x6000..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Daycare7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Daycare7b.cs new file mode 100644 index 000000000..29c00dc9a --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Daycare7b.cs @@ -0,0 +1,9 @@ +using System; + +namespace PKHeX.Core; + +public sealed class Daycare7b(SAV7b sav, Memory raw) : SaveBlock(sav, raw) +{ + private const int StoredSize = PokeCrypto.SIZE_6STORED; + public Memory Stored => Raw.Slice(8, StoredSize); +} diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/EventWork7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/EventWork7b.cs index a5fc8c0a1..cdb06e364 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/EventWork7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/EventWork7b.cs @@ -3,7 +3,7 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class EventWork7b(SAV7b sav, int offset) : SaveBlock(sav, offset), IEventVar +public sealed class EventWork7b(SAV7b sav, Memory raw) : SaveBlock(sav, raw), IEventVar { // Zone @ 0x21A0 - 0x21AF (128 flags) // System @ 0x21B0 - 0x21EF (512 flags) -- is this really 256 instead, with another 256 region after for the small vanish? @@ -51,8 +51,8 @@ public sealed class EventWork7b(SAV7b sav, int offset) : SaveBlock(sav, o public int CountFlag => FlagCount; public int CountWork => WorkCount; - public int GetWork(int index) => ReadInt32LittleEndian(Data.AsSpan(Offset + (index * WorkSize))); - public void SetWork(int index, int value) => WriteInt32LittleEndian(Data.AsSpan(Offset + (index * WorkSize)), value); + public int GetWork(int index) => ReadInt32LittleEndian(Data[(index * WorkSize)..]); + public void SetWork(int index, int value) => WriteInt32LittleEndian(Data[(index * WorkSize)..], value); public int GetWork(EventVarType type, int index) => GetWork(GetWorkRawIndex(type, index)); public void SetWork(EventVarType type, int index, int value) => SetWork(GetWorkRawIndex(type, index), value); public bool GetFlag(EventVarType type, int index) => GetFlag(GetFlagRawIndex(type, index)); @@ -78,13 +78,13 @@ public sealed class EventWork7b(SAV7b sav, int offset) : SaveBlock(sav, o public bool GetFlag(int index) { - var offset = Offset + FlagStart + (index >> 3); + var offset = FlagStart + (index >> 3); return FlagUtil.GetFlag(Data, offset, index); } public void SetFlag(int index, bool value = true) { - var offset = Offset + FlagStart + (index >> 3); + var offset = FlagStart + (index >> 3); FlagUtil.SetFlag(Data, offset, index, value); } @@ -170,14 +170,14 @@ public sealed class EventWork7b(SAV7b sav, int offset) : SaveBlock(sav, o { if ((uint)index >= MaxTitleFlag) throw new ArgumentOutOfRangeException(nameof(index)); - return FlagUtil.GetFlag(Data, Offset + TitleFlagStart + (index >> 3), index); + return FlagUtil.GetFlag(Data, TitleFlagStart + (index >> 3), index); } public void SetTitleFlag(int index, bool value = true) { if ((uint)index >= MaxTitleFlag) throw new ArgumentOutOfRangeException(nameof(index)); - FlagUtil.SetFlag(Data, Offset + TitleFlagStart + (index >> 3), index, value); + FlagUtil.SetFlag(Data, TitleFlagStart + (index >> 3), index, value); } public void UnlockAllTitleFlags() diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Fashion7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Fashion7b.cs index e8bd0bf75..e744befda 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Fashion7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Fashion7b.cs @@ -2,16 +2,16 @@ using System; namespace PKHeX.Core; -public sealed class Fashion7b(SAV7b sav, int offset) : SaveBlock(sav, offset) +public sealed class Fashion7b(SAV7b sav, Memory raw) : SaveBlock(sav, raw) { public void UnlockAllAccessoriesPlayer() { - SAV.SetData(AllAccessoriesPlayer, Offset); + SAV.SetData(Data, AllAccessoriesPlayer); } public void UnlockAllAccessoriesStarter() { - SAV.SetData(AllAccessoriesStarter, Offset); + SAV.SetData(Data, AllAccessoriesStarter); } private static ReadOnlySpan AllAccessoriesPlayer => diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/GP1.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/GP1.cs index bcf1752e3..a40dd2458 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/GP1.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/GP1.cs @@ -23,15 +23,9 @@ public sealed class GP1(byte[] Data) public AbilityPermission Ability => AbilityPermission.Any12; public GP1() : this(new byte[SIZE]) => InitializeBlank(Data); - public void WriteTo(byte[] data, int offset) => Data.CopyTo(data, offset); + public void WriteTo(Span data) => Data.CopyTo(data); - public static GP1 FromData(byte[] data, int offset) - { - var span = data.AsSpan(offset); - return FromData(span); - } - - private static GP1 FromData(ReadOnlySpan span) + public static GP1 FromData(ReadOnlySpan span) { var result = new GP1(); span[..SIZE].CopyTo(result.Data); diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/GoParkStorage.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/GoParkStorage.cs index 06cdad223..27a2de78f 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/GoParkStorage.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/GoParkStorage.cs @@ -1,10 +1,11 @@ +using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; namespace PKHeX.Core; -public sealed class GoParkStorage(SAV7b sav, int offset) : SaveBlock(sav, offset), IEnumerable +public sealed class GoParkStorage(SAV7b sav, Memory raw) : SaveBlock(sav, raw), IEnumerable { public const int SlotsPerArea = 50; public const int Areas = 20; @@ -15,12 +16,12 @@ public sealed class GoParkStorage(SAV7b sav, int offset) : SaveBlock(sav, get { Debug.Assert(index < Count); - return GP1.FromData(Data, Offset + (GP1.SIZE * index)); + return GP1.FromData(Data[(GP1.SIZE * index)..]); } set { Debug.Assert(index < Count); - value.WriteTo(Data, Offset + (GP1.SIZE * index)); + value.WriteTo(Data[(GP1.SIZE * index)..]); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Misc7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Misc7b.cs index e4f4a62fd..472a88063 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Misc7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/Misc7b.cs @@ -3,15 +3,15 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class Misc7b(SAV7b sav, int offset) : SaveBlock(sav, offset) +public sealed class Misc7b(SAV7b sav, Memory raw) : SaveBlock(sav, raw) { public uint Money { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 4)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 4), value); + get => ReadUInt32LittleEndian(Data[4..]); + set => WriteUInt32LittleEndian(Data[4..], value); } - private Span Rival_Trash => Data.AsSpan(Offset + 0x200, 0x1A); + private Span Rival_Trash => Data.Slice(0x200, 0x1A); public string Rival { diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/MyItem7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/MyItem7b.cs index 207c8c849..7e224d749 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/MyItem7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/MyItem7b.cs @@ -1,8 +1,9 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; -public sealed class MyItem7b(SAV7b sav, int offset) : MyItem(sav, offset) +public sealed class MyItem7b(SAV7b sav, Memory raw) : MyItem(sav, raw) { private const int Medicine = 0x0000; // 0 private const int TM = 0x00F0; // 1 diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/MyStatus7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/MyStatus7b.cs index b46453a72..f1025377a 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/MyStatus7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/MyStatus7b.cs @@ -3,7 +3,7 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class MyStatus7b(SAV7b sav, int offset) : SaveBlock(sav, offset) +public sealed class MyStatus7b(SAV7b sav, Memory raw) : SaveBlock(sav, raw) { // Player Information @@ -11,55 +11,55 @@ public sealed class MyStatus7b(SAV7b sav, int offset) : SaveBlock(sav, of public uint ID32 { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0), value); + get => ReadUInt32LittleEndian(Data); + set => WriteUInt32LittleEndian(Data, value); } public ushort TID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0), value); + get => ReadUInt16LittleEndian(Data); + set => WriteUInt16LittleEndian(Data, value); } public ushort SID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 2)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 2), value); + get => ReadUInt16LittleEndian(Data[2..]); + set => WriteUInt16LittleEndian(Data[2..], value); } public byte Game { - get => Data[Offset + 4]; - set => Data[Offset + 4] = value; + get => Data[4]; + set => Data[4] = value; } public byte Gender { - get => Data[Offset + 5]; - set => Data[Offset + 5] = OverworldGender = value; + get => Data[5]; + set => Data[5] = OverworldGender = value; } public const int GameSyncIDSize = 16; // 8 bytes public string GameSyncID { - get => Util.GetHexStringFromBytes(Data.AsSpan(Offset + 0x10, GameSyncIDSize / 2)); + get => Util.GetHexStringFromBytes(Data.Slice(0x10, GameSyncIDSize / 2)); set { ArgumentOutOfRangeException.ThrowIfGreaterThan(value.Length, 16); var data = Util.GetBytesFromHexString(value); - SAV.SetData(data, Offset + 0x10); + SAV.SetData(Data[0x10..], data); } } public int Language { - get => Data[Offset + 0x35]; - set => Data[Offset + 0x35] = (byte)value; + get => Data[0x35]; + set => Data[0x35] = (byte)value; } - private Span OriginalTrainerTrash => Data.AsSpan(Offset + 0x38, 0x1A); + private Span OriginalTrainerTrash => Data.Slice(0x38, 0x1A); public string OT { @@ -70,26 +70,26 @@ public sealed class MyStatus7b(SAV7b sav, int offset) : SaveBlock(sav, of // The value here corresponds to a Trainer Class string (ranging from 000 to 383, use pkNX to get a full list). public byte TrainerClassIndex { - get => Data[Offset + 0x076]; - set => Data[Offset + 0x076] = value; + get => Data[0x076]; + set => Data[0x076] = value; } public StarterChoice7b StarterChoice { - get => (StarterChoice7b)Data[Offset + 0x0B8]; - set => Data[Offset + 0x0B8] = (byte)value; + get => (StarterChoice7b)Data[0x0B8]; + set => Data[0x0B8] = (byte)value; } public byte StarterGender { - get => Data[Offset + 0x0B9]; - set => Data[Offset + 0x0B9] = value; + get => Data[0x0B9]; + set => Data[0x0B9] = value; } public byte OverworldGender // Model { - get => Data[Offset + 0x108]; - set => Data[Offset + 0x108] = value; + get => Data[0x108]; + set => Data[0x108] = value; } public enum StarterChoice7b : byte @@ -100,20 +100,20 @@ public sealed class MyStatus7b(SAV7b sav, int offset) : SaveBlock(sav, of } // Fashion and Appearance - public ulong StarterHairstyle { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x0C0)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x0C0), value); } - public ulong StarterHat { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x0C8)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x0C8), value); } - public ulong StarterGlasses { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x0D0)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x0D0), value); } - public ulong StarterAccessoryEar { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x0D8)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x0D8), value); } - public ulong StarterAccessoryChest { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x0E0)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x0E0), value); } - public ulong StarterAccessoryBack { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x0E8)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x0E8), value); } - public ulong StarterAccessoryTail { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x0F0)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x0F0), value); } - public ulong StarterClothes { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x0F8)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x0F8), value); } - public ulong PlayerSkinColor { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x110)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x110), value); } - public ulong PlayerEyeColor { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x118)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x118), value); } - public ulong PlayerHairColor { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x120)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x120), value); } - public ulong PlayerOuterwear { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x128)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x128), value); } - public ulong PlayerPants { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x130)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x130), value); } - public ulong PlayerShoes { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x138)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x138), value); } - public ulong PlayerHat { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x140)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x140), value); } - public ulong PlayerBag { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x148)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x148), value); } + public ulong StarterHairstyle { get => ReadUInt64LittleEndian(Data[0x0C0..]); set => WriteUInt64LittleEndian(Data[0x0C0..], value); } + public ulong StarterHat { get => ReadUInt64LittleEndian(Data[0x0C8..]); set => WriteUInt64LittleEndian(Data[0x0C8..], value); } + public ulong StarterGlasses { get => ReadUInt64LittleEndian(Data[0x0D0..]); set => WriteUInt64LittleEndian(Data[0x0D0..], value); } + public ulong StarterAccessoryEar { get => ReadUInt64LittleEndian(Data[0x0D8..]); set => WriteUInt64LittleEndian(Data[0x0D8..], value); } + public ulong StarterAccessoryChest { get => ReadUInt64LittleEndian(Data[0x0E0..]); set => WriteUInt64LittleEndian(Data[0x0E0..], value); } + public ulong StarterAccessoryBack { get => ReadUInt64LittleEndian(Data[0x0E8..]); set => WriteUInt64LittleEndian(Data[0x0E8..], value); } + public ulong StarterAccessoryTail { get => ReadUInt64LittleEndian(Data[0x0F0..]); set => WriteUInt64LittleEndian(Data[0x0F0..], value); } + public ulong StarterClothes { get => ReadUInt64LittleEndian(Data[0x0F8..]); set => WriteUInt64LittleEndian(Data[0x0F8..], value); } + public ulong PlayerSkinColor { get => ReadUInt64LittleEndian(Data[0x110..]); set => WriteUInt64LittleEndian(Data[0x110..], value); } + public ulong PlayerEyeColor { get => ReadUInt64LittleEndian(Data[0x118..]); set => WriteUInt64LittleEndian(Data[0x118..], value); } + public ulong PlayerHairColor { get => ReadUInt64LittleEndian(Data[0x120..]); set => WriteUInt64LittleEndian(Data[0x120..], value); } + public ulong PlayerOuterwear { get => ReadUInt64LittleEndian(Data[0x128..]); set => WriteUInt64LittleEndian(Data[0x128..], value); } + public ulong PlayerPants { get => ReadUInt64LittleEndian(Data[0x130..]); set => WriteUInt64LittleEndian(Data[0x130..], value); } + public ulong PlayerShoes { get => ReadUInt64LittleEndian(Data[0x138..]); set => WriteUInt64LittleEndian(Data[0x138..], value); } + public ulong PlayerHat { get => ReadUInt64LittleEndian(Data[0x140..]); set => WriteUInt64LittleEndian(Data[0x140..], value); } + public ulong PlayerBag { get => ReadUInt64LittleEndian(Data[0x148..]); set => WriteUInt64LittleEndian(Data[0x148..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PlayTime7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PlayTime7b.cs index bd4659cae..0b606f388 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PlayTime7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PlayTime7b.cs @@ -3,27 +3,27 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class PlayTime7b(SAV7b sav, int offset) : SaveBlock(sav, offset) +public sealed class PlayTime7b(SAV7b sav, Memory raw) : SaveBlock(sav, raw) { public int PlayedHours { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset), (ushort)value); + get => ReadUInt16LittleEndian(Data); + set => WriteUInt16LittleEndian(Data, (ushort)value); } public int PlayedMinutes { - get => Data[Offset + 2]; - set => Data[Offset + 2] = (byte)value; + get => Data[2]; + set => Data[2] = (byte)value; } public int PlayedSeconds { - get => Data[Offset + 3]; - set => Data[Offset + 3] = (byte)value; + get => Data[3]; + set => Data[3] = (byte)value; } - private Epoch1900DateTimeValue LastSaved => new(Data.AsMemory(Offset + 0x4)); + private Epoch1900DateTimeValue LastSaved => new(Raw.Slice(0x4, 4)); public string LastSavedTime => $"{LastSaved.Year:0000}-{LastSaved.Month:00}-{LastSaved.Day:00} {LastSaved.Hour:00}ː{LastSaved.Minute:00}"; // not : public DateTime? LastSavedDate diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PlayerGeoLocation7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PlayerGeoLocation7b.cs index a1533e666..77f7b2e0e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PlayerGeoLocation7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PlayerGeoLocation7b.cs @@ -2,7 +2,7 @@ using System; namespace PKHeX.Core; -public sealed class PlayerGeoLocation7b(SAV7b sav, int offset) : SaveBlock(sav, offset) +public sealed class PlayerGeoLocation7b(SAV7b sav, Memory raw) : SaveBlock(sav, raw) { - public Epoch1970Value AdventureBegin => new(Data.AsMemory(Offset + 0x48)); + public Epoch1970Value AdventureBegin => new(Raw.Slice(0x48, 8)); } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PokeListHeader.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PokeListHeader.cs index a61b4ab74..bf73a337a 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PokeListHeader.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PokeListHeader.cs @@ -15,6 +15,9 @@ namespace PKHeX.Core; /// public sealed class PokeListHeader : SaveBlock { + public PokeListHeader(SAV7b sav, Memory raw, bool exportable) : base(sav, raw) + => PokeListInfo = Initialize(exportable); + /// /// Raw representation of data, cast to ushort[]. /// @@ -25,22 +28,23 @@ public sealed class PokeListHeader : SaveBlock private const int MAX_SLOTS = 1000; private const int SLOT_EMPTY = 1001; - public PokeListHeader(SAV7b sav, int offset) : base(sav, offset) + private int[] Initialize(bool exportable) { - var info = PokeListInfo = LoadPointerData(); - if (!sav.State.Exportable) + var info = LoadPointerData(); + if (!exportable) { - for (int i = 0; i < COUNT; i++) - info[i] = SLOT_EMPTY; + info.AsSpan().Fill(SLOT_EMPTY); } else { + // Count the party slots that are valid for (int i = 0; i < 6; i++) { if (info[i] < MAX_SLOTS) ++_partyCount; } } + return info; } private int _partyCount; @@ -94,14 +98,14 @@ public sealed class PokeListHeader : SaveBlock public int Count { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + (COUNT * 2))); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + (COUNT * 2)), (ushort)value); + get => ReadUInt16LittleEndian(Data[(COUNT * 2)..]); + set => WriteUInt16LittleEndian(Data[(COUNT * 2)..], (ushort)value); } private int[] LoadPointerData() { - var span = Data.AsSpan(Offset); - var list = new int[7]; + var span = Data; + var list = new int[COUNT]; for (int i = 0; i < list.Length; i++) list[i] = ReadUInt16LittleEndian(span[(i * 2)..]); return list; @@ -109,7 +113,7 @@ public sealed class PokeListHeader : SaveBlock private void SetPointerData(ReadOnlySpan vals) { - var span = Data.AsSpan(Offset); + var span = Data; for (int i = 0; i < vals.Length; i++) WriteUInt16LittleEndian(span[(i*2)..], (ushort)vals[i]); vals.CopyTo(PokeListInfo); diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/WB7Records.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/WB7Records.cs index 54e8ddfa9..717d2ac13 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/WB7Records.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/WB7Records.cs @@ -2,81 +2,50 @@ using System; namespace PKHeX.Core; -public sealed class WB7Records(SAV7b sav, int offset) : SaveBlock(sav, offset) +public sealed class WB7Records(SAV7b sav, Memory raw) : SaveBlock(sav, raw), IMysteryGiftStorage, IMysteryGiftFlags { - private const int RecordMax = 10; // 0xE90 > (0x140 * 0xA = 0xC80), not sure what final 0x210 bytes are used for - private const int FlagCountMax = 0x1C00; // (7168) end of the block? + private const int CardStart = 0; + private const int FlagStart = (MaxCardsPresent * WR7.Size); - private int FlagStart => Offset + (RecordMax * WR7.Size); + private const int MaxCardsPresent = 10; // 0xE90 > (0x140 * 0xA = 0xC80), not sure what final 0x210 bytes are used for + private const int MaxReceivedFlag = 0x1C00; // (7168) end of the block? - private int GetRecordOffset(int index) + public void ClearReceivedFlags() => Data[..(MaxReceivedFlag / 8)].Clear(); + + private static int GetGiftOffset(int index) { - ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, RecordMax); - - return Offset + (index * WR7.Size); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxCardsPresent); + return CardStart + (index * WR7.Size); } - private int GetFlagOffset(int flag) + private Span GetCardSpan(int index) => Data.Slice(GetGiftOffset(index), WR7.Size); + + public WR7 GetMysteryGift(int index) => new(GetCardSpan(index).ToArray()); + + public void SetMysteryGift(int index, WR7 wr7) { - ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(flag, FlagCountMax); - return FlagStart + (flag / 8); + if ((uint)index > MaxCardsPresent) + throw new ArgumentOutOfRangeException(nameof(index)); + if (wr7.Data.Length != WR7.Size) + throw new InvalidCastException(nameof(wr7)); + + SAV.SetData(Data[GetGiftOffset(index)..], wr7.Data); } - public WR7 GetRecord(int index) + public int MysteryGiftReceivedFlagMax => MaxReceivedFlag; + public bool GetMysteryGiftReceivedFlag(int index) { - int ofs = GetRecordOffset(index); - byte[] data = Data.AsSpan(ofs, WR7.Size).ToArray(); - return new WR7(data); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxReceivedFlag); + return FlagUtil.GetFlag(Data[FlagStart..], index); // offset 0 } - public void SetRecord(WR7 record, int index) + public void SetMysteryGiftReceivedFlag(int index, bool value) { - int ofs = GetRecordOffset(index); - record.Data.CopyTo(Data, ofs); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxReceivedFlag); + FlagUtil.SetFlag(Data[FlagStart..], index, value); // offset 0 } - public WR7[] GetRecords() - { - var arr = new WR7[RecordMax]; - for (int i = 0; i < arr.Length; i++) - arr[i] = GetRecord(i); - return arr; - } - - public void SetRecords(WR7[] value) - { - for (int i = 0; i < value.Length; i++) - SetRecord(value[i], i); - } - - public bool GetFlag(int flag) - { - int ofs = GetFlagOffset(flag); - var mask = 1 << (flag & 7); - return (Data[ofs] & mask) != 0; - } - - public void SetFlag(int flag, bool value) - { - int ofs = GetFlagOffset(flag); - var mask = 1 << (flag & 7); - if (value) - Data[ofs] |= (byte)mask; - else - Data[ofs] &= (byte)~mask; - } - - public bool[] GetFlags() - { - var value = new bool[FlagCountMax]; - for (int i = 0; i < value.Length; i++) - value[i] = GetFlag(i); - return value; - } - - public void SetFlags(ReadOnlySpan value) - { - for (int i = 0; i < value.Length; i++) - SetFlag(i, value[i]); - } + public int GiftCountMax => MaxCardsPresent; + DataMysteryGift IMysteryGiftStorage.GetMysteryGift(int index) => GetMysteryGift(index); + void IMysteryGiftStorage.SetMysteryGift(int index, DataMysteryGift gift) => SetMysteryGift(index, (WR7)gift); } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs b/PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs index f32a33ab6..b11e18701 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs @@ -1,74 +1,72 @@ -using System; +using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class Misc7 : SaveBlock +public sealed class Misc7(SAV7 sav, Memory raw) : SaveBlock(sav, raw) { - public Misc7(SAV7 sav, int offset) : base(sav) => Offset = offset; - public uint Money { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x4)); + get => ReadUInt32LittleEndian(Data[0x4..]); set { if (value > 9_999_999) value = 9_999_999; - WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x4), value); + WriteUInt32LittleEndian(Data[0x4..], value); } } public uint Stamps { - get => (ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x08)) << 13) >> 17; // 15 stamps; discard top13, lowest4 + get => (ReadUInt32LittleEndian(Data[0x08..]) << 13) >> 17; // 15 stamps; discard top13, lowest4 set { - uint flags = ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x08)) & 0xFFF8000F; + uint flags = ReadUInt32LittleEndian(Data[0x08..]) & 0xFFF8000F; flags |= (value & 0x7FFF) << 4; - WriteUInt32LittleEndian(SAV.Data.AsSpan(Offset + 0x08), flags); + WriteUInt32LittleEndian(Data[0x08..], flags); } } public uint BP { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x11C)); + get => ReadUInt32LittleEndian(Data[0x11C..]); set { if (value > 9999) value = 9999; - WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x11C), value); + WriteUInt32LittleEndian(Data[0x11C..], value); } } public int Vivillon { - get => Data[Offset + 0x130] & 0x1F; - set => Data[Offset + 0x130] = (byte)((Data[Offset + 0x130] & ~0x1F) | (value & 0x1F)); + get => Data[0x130] & 0x1F; + set => Data[0x130] = (byte)((Data[0x130] & ~0x1F) | (value & 0x1F)); } public uint StarterEncryptionConstant { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x148)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x148), value); + get => ReadUInt32LittleEndian(Data[0x148..]); + set => WriteUInt32LittleEndian(Data[0x148..], value); } public int DaysFromRefreshed { - get => Data[Offset + 0x123]; - set => Data[Offset + 0x123] = (byte)value; + get => Data[0x123]; + set => Data[0x123] = (byte)value; } public int GetSurfScore(int recordID) { if ((uint)recordID >= 4) recordID = 0; - return ReadInt32LittleEndian(Data.AsSpan(Offset + 0x138 + (4 * recordID))); + return ReadInt32LittleEndian(Data[(0x138 + (4 * recordID))..]); } public void SetSurfScore(int recordID, int score) { if ((uint)recordID >= 4) recordID = 0; - WriteInt32LittleEndian(Data.AsSpan(Offset + 0x138 + (4 * recordID)), score); + WriteInt32LittleEndian(Data[(0x138 + (4 * recordID))..], score); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/MyItem7SM.cs b/PKHeX.Core/Saves/Substructures/Gen7/MyItem7SM.cs index 6abdbc57a..f7d961328 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/MyItem7SM.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/MyItem7SM.cs @@ -1,8 +1,9 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; -public sealed class MyItem7SM(SAV7SM SAV, int offset) : MyItem(SAV, offset) +public sealed class MyItem7SM(SAV7SM SAV, Memory raw) : MyItem(SAV, raw) { private const int HeldItem = 0; // 430 (Case 0) private const int KeyItem = HeldItem + (4 * 430); // 184 (Case 4) @@ -18,12 +19,12 @@ public sealed class MyItem7SM(SAV7SM SAV, int offset) : MyItem(SAV, offset) var info = ItemStorage7SM.Instance; InventoryPouch7[] pouch = [ - new(InventoryType.Medicine, info, 999, Offset + Medicine), - new(InventoryType.Items, info, 999, Offset + HeldItem), - new(InventoryType.TMHMs, info, 1, Offset + TMHM), - new(InventoryType.Berries, info, 999, Offset + Berry), - new(InventoryType.KeyItems, info, 1, Offset + KeyItem), - new(InventoryType.ZCrystals, info, 1, Offset + ZCrystals), + new(InventoryType.Medicine, info, 999, Medicine), + new(InventoryType.Items, info, 999, HeldItem), + new(InventoryType.TMHMs, info, 1, TMHM), + new(InventoryType.Berries, info, 999, Berry), + new(InventoryType.KeyItems, info, 1, KeyItem), + new(InventoryType.ZCrystals, info, 1, ZCrystals), ]; return pouch.LoadAll(Data); } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/MyItem7USUM.cs b/PKHeX.Core/Saves/Substructures/Gen7/MyItem7USUM.cs index 132eeae48..bd26547dd 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/MyItem7USUM.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/MyItem7USUM.cs @@ -1,8 +1,9 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; -public sealed class MyItem7USUM(SAV7USUM SAV, int offset) : MyItem(SAV, offset) +public sealed class MyItem7USUM(SAV7USUM SAV, Memory raw) : MyItem(SAV, raw) { private const int HeldItem = 0; // 427 (Case 0) private const int KeyItem = HeldItem + (4 * 427); // 198 (Case 4) diff --git a/PKHeX.Core/Saves/Substructures/Gen7/MyStatus7.cs b/PKHeX.Core/Saves/Substructures/Gen7/MyStatus7.cs index 913a58b10..99c4cb446 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/MyStatus7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/MyStatus7.cs @@ -3,98 +3,96 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class MyStatus7 : SaveBlock, IRegionOrigin +public sealed class MyStatus7(SAV7 sav, Memory raw) : SaveBlock(sav, raw), IRegionOrigin { public const int GameSyncIDSize = 16; // 64 bits public const int NexUniqueIDSize = 32; // 128 bits - public MyStatus7(SAV7 sav, int offset) : base(sav) => Offset = offset; - public uint ID32 { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0), value); + get => ReadUInt32LittleEndian(Data); + set => WriteUInt32LittleEndian(Data, value); } public ushort TID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0), value); + get => ReadUInt16LittleEndian(Data); + set => WriteUInt16LittleEndian(Data, value); } public ushort SID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 2)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 2), value); + get => ReadUInt16LittleEndian(Data[2..]); + set => WriteUInt16LittleEndian(Data[2..], value); } public byte Game { - get => Data[Offset + 4]; - set => Data[Offset + 4] = value; + get => Data[4]; + set => Data[4] = value; } public byte Gender { - get => Data[Offset + 5]; - set => Data[Offset + 5] = value; + get => Data[5]; + set => Data[5] = value; } public string GameSyncID { - get => Util.GetHexStringFromBytes(Data.AsSpan(Offset + 0x10, GameSyncIDSize / 2)); + get => Util.GetHexStringFromBytes(Data.Slice(0x10, GameSyncIDSize / 2)); set { ArgumentOutOfRangeException.ThrowIfNotEqual(value.Length, GameSyncIDSize); var data = Util.GetBytesFromHexString(value); - SAV.SetData(data, Offset + 0x10); + SAV.SetData(Data[0x10..], data); } } public string NexUniqueID { - get => Util.GetHexStringFromBytes(Data.AsSpan(Offset + 0x18, NexUniqueIDSize / 2)); + get => Util.GetHexStringFromBytes(Data.Slice(0x18, NexUniqueIDSize / 2)); set { ArgumentOutOfRangeException.ThrowIfNotEqual(value.Length, NexUniqueIDSize); var data = Util.GetBytesFromHexString(value); - SAV.SetData(data, Offset + 0x18); + SAV.SetData(Data[0x18..], data); } } public uint FestaID { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x28)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x28), value); + get => ReadUInt32LittleEndian(Data[0x28..]); + set => WriteUInt32LittleEndian(Data[0x28..], value); } public byte Region { - get => Data[Offset + 0x2E]; - set => Data[Offset + 0x2E] = value; + get => Data[0x2E]; + set => Data[0x2E] = value; } public byte Country { - get => Data[Offset + 0x2F]; - set => Data[Offset + 0x2F] = value; + get => Data[0x2F]; + set => Data[0x2F] = value; } public byte ConsoleRegion { - get => Data[Offset + 0x34]; - set => Data[Offset + 0x34] = value; + get => Data[0x34]; + set => Data[0x34] = value; } public int Language { - get => Data[Offset + 0x35]; - set => Data[Offset + 0x35] = (byte)value; + get => Data[0x35]; + set => Data[0x35] = (byte)value; } - private Span OriginalTrainerTrash => Data.AsSpan(Offset + 0x38, 0x1A); + private Span OriginalTrainerTrash => Data.Slice(0x38, 0x1A); public string OT { @@ -104,31 +102,31 @@ public sealed class MyStatus7 : SaveBlock, IRegionOrigin public int DressUpSkinColor { - get => (Data[Offset + 0x54] >> 2) & 7; - set => Data[Offset + 0x54] = (byte)((Data[Offset + 0x54] & ~(7 << 2)) | (value << 2)); + get => (Data[0x54] >> 2) & 7; + set => Data[0x54] = (byte)((Data[0x54] & ~(7 << 2)) | (value << 2)); } public int MultiplayerSpriteID // holdover from Gen6 { - get => Data[Offset + 0x58]; - set => Data[Offset + 0x58] = (byte)value; + get => Data[0x58]; + set => Data[0x58] = (byte)value; } public bool MegaUnlocked { - get => (Data[Offset + 0x78] & 0x01) != 0; - set => Data[Offset + 0x78] = (byte)((Data[Offset + 0x78] & 0xFE) | (value ? 1 : 0)); // in battle + get => (Data[0x78] & 0x01) != 0; + set => Data[0x78] = (byte)((Data[0x78] & 0xFE) | (value ? 1 : 0)); // in battle } public bool ZMoveUnlocked { - get => (Data[Offset + 0x78] & 2) != 0; - set => Data[Offset + 0x78] = (byte)((Data[Offset + 0x78] & ~2) | (value ? 2 : 0)); // in battle + get => (Data[0x78] & 2) != 0; + set => Data[0x78] = (byte)((Data[0x78] & ~2) | (value ? 2 : 0)); // in battle } public byte BallThrowType { - get => Data[Offset + 0x7A]; - set => Data[Offset + 0x7A] = (byte)(value > 8 ? 0 : value); + get => Data[0x7A]; + set => Data[0x7A] = (byte)(value > 8 ? 0 : value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs b/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs index 9709216c6..4b0636444 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs @@ -2,67 +2,49 @@ using System; namespace PKHeX.Core; -public sealed class MysteryBlock7 : SaveBlock +public sealed class MysteryBlock7(SAV7 sav, Memory raw) : SaveBlock(sav, raw), IMysteryGiftStorage, IMysteryGiftFlags { private const int FlagStart = 0; private const int MaxReceivedFlag = 2048; private const int MaxCardsPresent = 48; // private const int FlagRegionSize = (MaxReceivedFlag / 8); // 0x100 private const int CardStart = FlagStart + (MaxReceivedFlag / 8); + public void ClearReceivedFlags() => Data[..(MaxReceivedFlag / 8)].Clear(); - public MysteryBlock7(SAV7 sav, int offset) : base(sav) => Offset = offset; - - // Mystery Gift - public bool[] MysteryGiftReceivedFlags + private static int GetGiftOffset(int index) { - get => FlagUtil.GetBitFlagArray(Data.AsSpan(Offset + FlagStart), MaxReceivedFlag); - set - { - if (value.Length != MaxReceivedFlag) - return; - FlagUtil.SetBitFlagArray(Data.AsSpan(Offset + FlagStart), value); - SAV.State.Edited = true; - } + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxCardsPresent); + return CardStart + (index * WC7.Size); } - public DataMysteryGift[] MysteryGiftCards - { - get - { - var cards = new DataMysteryGift[MaxCardsPresent]; - for (int i = 0; i < cards.Length; i++) - cards[i] = GetGift(i); - return cards; - } - set - { - int count = Math.Min(MaxCardsPresent, value.Length); - for (int i = 0; i < count; i++) - SetGift((WC7)value[i], i); - for (int i = value.Length; i < MaxCardsPresent; i++) - SetGift(new WC7(), i); - } - } + private Span GetCardSpan(int index) => Data.Slice(GetGiftOffset(index), WC7.Size); - private WC7 GetGift(int index) - { - if ((uint)index > MaxCardsPresent) - throw new ArgumentOutOfRangeException(nameof(index)); + public WC7 GetMysteryGift(int index) => new(GetCardSpan(index).ToArray()); - var offset = GetGiftOffset(index); - var data = SAV.Data.AsSpan(offset, WC7.Size).ToArray(); - return new WC7(data); - } - - private int GetGiftOffset(int index) => Offset + CardStart + (index * WC7.Size); - - private void SetGift(WC7 wc7, int index) + public void SetMysteryGift(int index, WC7 wc7) { if ((uint)index > MaxCardsPresent) throw new ArgumentOutOfRangeException(nameof(index)); if (wc7.Data.Length != WC7.Size) throw new InvalidCastException(nameof(wc7)); - SAV.SetData(wc7.Data, GetGiftOffset(index)); + SAV.SetData(Data[GetGiftOffset(index)..], wc7.Data); } + + public int MysteryGiftReceivedFlagMax => MaxReceivedFlag; + public bool GetMysteryGiftReceivedFlag(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxReceivedFlag); + return FlagUtil.GetFlag(Data, index); // offset 0 + } + + public void SetMysteryGiftReceivedFlag(int index, bool value) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxReceivedFlag); + FlagUtil.SetFlag(Data, index, value); // offset 0 + } + + public int GiftCountMax => MaxCardsPresent; + DataMysteryGift IMysteryGiftStorage.GetMysteryGift(int index) => GetMysteryGift(index); + void IMysteryGiftStorage.SetMysteryGift(int index, DataMysteryGift gift) => SetMysteryGift(index, (WC7)gift); } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/PokeFinder7.cs b/PKHeX.Core/Saves/Substructures/Gen7/PokeFinder7.cs index 16765b8f5..e50f37e50 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/PokeFinder7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/PokeFinder7.cs @@ -1,49 +1,47 @@ -using System; +using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class PokeFinder7 : SaveBlock +public sealed class PokeFinder7(SAV7 sav, Memory raw) : SaveBlock(sav, raw) { - public PokeFinder7(SAV7 sav, int offset) : base(sav) => Offset = offset; - public ushort CameraVersion { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x00)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x00), value); + get => ReadUInt16LittleEndian(Data); + set => WriteUInt16LittleEndian(Data, value); } public bool GyroFlag { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x02)) == 1; - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x02), (ushort)(value ? 1 : 0)); + get => ReadUInt16LittleEndian(Data[0x02..]) == 1; + set => WriteUInt16LittleEndian(Data[0x02..], (ushort)(value ? 1 : 0)); } public uint SnapCount { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x04)); + get => ReadUInt32LittleEndian(Data[0x04..]); set { if (value > 9999999) // Top bound is unchecked, check anyway value = 9999999; - WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); + WriteUInt32LittleEndian(Data[0x04..], value); } } public uint ThumbsTotalValue { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x0C)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x0C), value); + get => ReadUInt32LittleEndian(Data[0x0C..]); + set => WriteUInt32LittleEndian(Data[0x0C..], value); } public uint ThumbsHighValue { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x10)); + get => ReadUInt32LittleEndian(Data[0x10..]); set { if (value > 9_999_999) value = 9_999_999; - WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x10), value); + WriteUInt32LittleEndian(Data[0x10..], value); if (value > ThumbsTotalValue) ThumbsTotalValue = value; @@ -52,7 +50,7 @@ public sealed class PokeFinder7 : SaveBlock public ushort TutorialFlags { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x14)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x14), value); + get => ReadUInt16LittleEndian(Data[0x14..]); + set => WriteUInt16LittleEndian(Data[0x14..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/ResortSave7.cs b/PKHeX.Core/Saves/Substructures/Gen7/ResortSave7.cs index 49731bb9f..2696e3a51 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/ResortSave7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/ResortSave7.cs @@ -1,44 +1,37 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class ResortSave7 : SaveBlock +public sealed class ResortSave7(SAV7 sav, Memory raw) : SaveBlock(sav, raw) { - public ResortSave7(SAV7 sav, int offset) : base(sav) => Offset = offset; + private const int SIZE_7STORED = PokeCrypto.SIZE_6STORED; + private const int SIZE_7STORED_R = SIZE_7STORED + 4; // 3 bytes of extra metadata - private const int SIZE_7STORED_R = PokeCrypto.SIZE_6STORED + 4; + // tinymt64 + public UInt128 State { get => ReadUInt128LittleEndian(Data); set => WriteUInt128LittleEndian(Data, value); } - public const int ResortCount = 93; - public int GetResortSlotOffset(int slot) => Offset + 0x16 + (slot * SIZE_7STORED_R); + // 6 bytes ??? - public PK7[] ResortPKM + public const int ResortCount1 = 18; + public const int ResortCount2 = 3; + public const int ResortCount3 = ResortCount1; // 18 + public const int ResortCount4 = ResortCount1; // 18 + public const int ResortCount5 = ResortCount1; // 18 + public const int ResortCount6 = ResortCount1; // 18 + public const int ResortCount = ResortCount1 + ResortCount2 + ResortCount3 + ResortCount4 + ResortCount5 + ResortCount6; // 93 + // End of Resort Slots: 0x55D2 + + public static int GetResortSlotOffset(int index) { - get - { - PK7[] result = new PK7[ResortCount]; - for (int i = 0; i < result.Length; i++) - { - var ofs = GetResortSlotOffset(i); - var data = SAV.Data.AsSpan(ofs, PokeCrypto.SIZE_6STORED).ToArray(); - result[i] = new PK7(data); - } - return result; - } - set - { - ArgumentOutOfRangeException.ThrowIfNotEqual(value.Length, ResortCount); - - for (int i = 0; i < value.Length; i++) - { - var ofs = GetResortSlotOffset(i); - var dest = Data.AsSpan(ofs, PokeCrypto.SIZE_6STORED); - SAV.SetSlotFormatStored(value[i], dest); - } - } + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)ResortCount); + return 0x16 + (index * SIZE_7STORED_R); } + public Memory this[int index] => Raw.Slice(GetResortSlotOffset(index), SIZE_7STORED); + public const int BEANS_MAX = 15; - public Span GetBeans() => Data.AsSpan(Offset + 0x564C, BEANS_MAX); + public Span GetBeans() => Data.Slice(0x564C, BEANS_MAX); public void ClearBeans() => GetBeans().Clear(); public void FillBeans(byte value = 255) => GetBeans().Fill(value); @@ -58,7 +51,7 @@ public sealed class ResortSave7 : SaveBlock /// public static string[] GetBeanIndexNames() { - var colors = Enum.GetNames(typeof(BeanColor7)); + var colors = Enum.GetNames(); return GetBeanIndexNames(colors); } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/Situation7.cs b/PKHeX.Core/Saves/Substructures/Gen7/Situation7.cs index 490a354ee..24a894b1f 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/Situation7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/Situation7.cs @@ -1,21 +1,19 @@ -using System; +using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class Situation7 : SaveBlock +public sealed class Situation7(SAV7 sav, Memory raw) : SaveBlock(sav, raw) { - public Situation7(SAV7 sav, int offset) : base(sav) => Offset = offset; - // "StartLocation" - public int M { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x00)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x00), (ushort)value); } - public float X { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x08)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x08), value); } - public float Z { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x0C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x0C), value); } - public float Y { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x10)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x10), value); } - public float RX { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x14)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x14), value); } - public float RZ { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x18)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x18), value); } - public float RY { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x1C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x1C), value); } - public float RW { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x20)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x20), value); } + public int M { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, (ushort)value); } + public float X { get => ReadSingleLittleEndian(Data[0x08..]); set => WriteSingleLittleEndian(Data[0x08..], value); } + public float Z { get => ReadSingleLittleEndian(Data[0x0C..]); set => WriteSingleLittleEndian(Data[0x0C..], value); } + public float Y { get => ReadSingleLittleEndian(Data[0x10..]); set => WriteSingleLittleEndian(Data[0x10..], value); } + public float RX { get => ReadSingleLittleEndian(Data[0x14..]); set => WriteSingleLittleEndian(Data[0x14..], value); } + public float RZ { get => ReadSingleLittleEndian(Data[0x18..]); set => WriteSingleLittleEndian(Data[0x18..], value); } + public float RY { get => ReadSingleLittleEndian(Data[0x1C..]); set => WriteSingleLittleEndian(Data[0x1C..], value); } + public float RW { get => ReadSingleLittleEndian(Data[0x20..]); set => WriteSingleLittleEndian(Data[0x20..], value); } public void UpdateOverworldCoordinates() { @@ -31,37 +29,37 @@ public sealed class Situation7 : SaveBlock public int SpecialLocation { - get => Data[Offset + 0x24]; - set => Data[Offset + 0x24] = (byte)value; + get => Data[0x24]; + set => Data[0x24] = (byte)value; } public int WarpContinueRequest { - get => Data[Offset + 0x6E]; - set => Data[Offset + 0x6E] = (byte)value; + get => Data[0x6E]; + set => Data[0x6E] = (byte)value; } public int StepCountEgg { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x70)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x70), value); + get => ReadInt32LittleEndian(Data[0x70..]); + set => WriteInt32LittleEndian(Data[0x70..], value); } public int LastZoneID { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x74)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x74), (ushort)value); + get => ReadUInt16LittleEndian(Data[0x74..]); + set => WriteUInt16LittleEndian(Data[0x74..], (ushort)value); } public int StepCountFriendship { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x76)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x76), (ushort)value); + get => ReadUInt16LittleEndian(Data[0x76..]); + set => WriteUInt16LittleEndian(Data[0x76..], (ushort)value); } public int StepCountAffection // Kawaigari { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x78)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x78), (ushort)value); + get => ReadUInt16LittleEndian(Data[0x78..]); + set => WriteUInt16LittleEndian(Data[0x78..], (ushort)value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/UnionPokemon7.cs b/PKHeX.Core/Saves/Substructures/Gen7/UnionPokemon7.cs new file mode 100644 index 000000000..e8f0a2f9a --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen7/UnionPokemon7.cs @@ -0,0 +1,18 @@ +using System; + +namespace PKHeX.Core; + +public sealed class UnionPokemon7(SAV7 sav, Memory raw) : SaveBlock(sav, raw) +{ + private const int EntitySize = PokeCrypto.SIZE_6PARTY; + + // 0 - Kyurem (Zekron/Reshiram) + // 1 - Necrozma (Solgaleo) + // 2 - Necrozma (Lunala) + + // 1 for S/M, 3 for US/UM. + // Bounds checks will throw exceptions based on the size of the Raw data this block object is provided. + public Memory GetSlot(int index) => Raw.Slice(index * EntitySize, EntitySize); + + public Memory this[int index] => GetSlot(index); +} diff --git a/PKHeX.Core/Saves/Substructures/Gen7/WormholeInfoReader.cs b/PKHeX.Core/Saves/Substructures/Gen7/WormholeInfoReader.cs index 7e0e9ba46..67c4e15d5 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/WormholeInfoReader.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/WormholeInfoReader.cs @@ -10,8 +10,8 @@ public sealed class WormholeInfoReader(SAV7 SAV) // https://projectpokemon.org/home/forums/topic/39433-gen-7-save-research-thread/?page=3&tab=comments#comment-239090 public bool WormholeShininess // 0x4535 = Misc (0x4400 in US/UM) + 0x0135 { - get => SAV.Data[SAV.Misc.Offset + 0x0135] == 1; - set => SAV.Data[SAV.Misc.Offset + 0x0135] = value ? (byte)1 : (byte)0; + get => SAV.Misc.Data[0x0135] == 1; + set => SAV.Misc.Data[0x0135] = value ? (byte)1 : (byte)0; } public const int WormholeSlotMax = 15; @@ -24,7 +24,7 @@ public sealed class WormholeInfoReader(SAV7 SAV) { for (int i = 1; i <= WormholeSlotMax; i++) { - if (!SAV.GetEventFlag(i)) + if (!SAV.EventWork.GetEventFlag(i)) return i; } return -1; @@ -34,7 +34,7 @@ public sealed class WormholeInfoReader(SAV7 SAV) if (value is < 1 or > WormholeSlotMax) return; for (int i = 1; i <= WormholeSlotMax; i++) - SAV.SetEventFlag(i, value != i); + SAV.EventWork.SetEventFlag(i, value != i); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTowerWork8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTowerWork8b.cs index 7bcdb78b3..3c262dbb7 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTowerWork8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTowerWork8b.cs @@ -10,13 +10,11 @@ namespace PKHeX.Core; /// /// size: 0x1B8 [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class BattleTowerWork8b : SaveBlock +public sealed class BattleTowerWork8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { private const int OFS_ClassData = 20; private const int COUNT_CLASSDATA = 4; - public BattleTowerWork8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - // Structure: // uint max_master_rank; // int play_mode; @@ -25,12 +23,12 @@ public sealed class BattleTowerWork8b : SaveBlock // uint day_challeng_cnt; // BTLTOWER_CLASSDATA[4] class_data; // uint challenge_cnt; - public int MasterRankMax { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x0)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x0), value); } // max_master_rank - public int PlayMode { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x4)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x4), value); }// play_mode - public int PlayModeOld { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x8)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x8), value); } // old_playmode - public uint BP { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0xC)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0xC), value); } // btl_point + public int MasterRankMax { get => ReadInt32LittleEndian(Data); set => WriteInt32LittleEndian(Data, value); } // max_master_rank + public int PlayMode { get => ReadInt32LittleEndian(Data[0x4..]); set => WriteInt32LittleEndian(Data[0x4..], value); }// play_mode + public int PlayModeOld { get => ReadInt32LittleEndian(Data[0x8..]); set => WriteInt32LittleEndian(Data[0x8..], value); } // old_playmode + public uint BP { get => ReadUInt32LittleEndian(Data[0xC..]); set => WriteUInt32LittleEndian(Data[0xC..], value); } // btl_point - public uint ChallengeCount { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x1B4)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x1B4), value); } // challenge_cnt + public uint ChallengeCount { get => ReadUInt32LittleEndian(Data[0x1B4..]); set => WriteUInt32LittleEndian(Data[0x1B4..], value); } // challenge_cnt public BattleTowerClassData8b[] Records { @@ -42,7 +40,7 @@ public sealed class BattleTowerWork8b : SaveBlock { var result = new BattleTowerClassData8b[COUNT_CLASSDATA]; for (int i = 0; i < result.Length; i++) - result[i] = new BattleTowerClassData8b(Data, Offset + OFS_ClassData + (i * BattleTowerClassData8b.SIZE)); + result[i] = new BattleTowerClassData8b(Raw.Slice(OFS_ClassData + (i * BattleTowerClassData8b.SIZE), BattleTowerClassData8b.SIZE)); return result; } @@ -54,37 +52,38 @@ public sealed class BattleTowerWork8b : SaveBlock } [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class BattleTowerClassData8b(byte[] Data, int Offset) +public sealed class BattleTowerClassData8b(Memory raw) { public const int SIZE = 0x68; + private Span Data => raw.Span; public override string ToString() => $"Rank: {Rank}, Streak: {RenshouCount} (Max {RenshouCountOld}), Wins: {TotalWins}|{TotalWinsLoop}|{TotalWinsLose}"; public byte Cleared { - get => Data[Offset + 0x00]; - set => Data[Offset]= value; + get => Data[0]; + set => Data[0] = value; } public bool Suspended { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x04)) == 1; - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x04), value ? 1u : 0u); + get => ReadInt32LittleEndian(Data[0x04..]) == 1; + set => WriteUInt32LittleEndian(Data[0x04..], value ? 1u : 0u); } - public ulong BattlePlaySeed { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x08)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x08), value); } - public uint Rank { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x10)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x10), value); } - public uint RankDownLose { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x14)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x14), value); } + public ulong BattlePlaySeed { get => ReadUInt64LittleEndian(Data[0x08..]); set => WriteUInt64LittleEndian(Data[0x08..], value); } + public uint Rank { get => ReadUInt32LittleEndian(Data[0x10..]); set => WriteUInt32LittleEndian(Data[0x10..], value); } + public uint RankDownLose { get => ReadUInt32LittleEndian(Data[0x14..]); set => WriteUInt32LittleEndian(Data[0x14..], value); } - public ulong TrainerSeed1 { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x1C)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x1C), value); } - public ulong TrainerSeed2 { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x24)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x24), value); } - public ulong TrainerSeed3 { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x2C)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x2C), value); } - public ulong TrainerSeed4 { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x34)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x34), value); } - public ulong TrainerSeed5 { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x3C)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x3C), value); } - public ulong TrainerSeed6 { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x44)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x44), value); } - public ulong TrainerSeed7 { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x4C)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x4C), value); } + public ulong TrainerSeed1 { get => ReadUInt64LittleEndian(Data[0x1C..]); set => WriteUInt64LittleEndian(Data[0x1C..], value); } + public ulong TrainerSeed2 { get => ReadUInt64LittleEndian(Data[0x24..]); set => WriteUInt64LittleEndian(Data[0x24..], value); } + public ulong TrainerSeed3 { get => ReadUInt64LittleEndian(Data[0x2C..]); set => WriteUInt64LittleEndian(Data[0x2C..], value); } + public ulong TrainerSeed4 { get => ReadUInt64LittleEndian(Data[0x34..]); set => WriteUInt64LittleEndian(Data[0x34..], value); } + public ulong TrainerSeed5 { get => ReadUInt64LittleEndian(Data[0x3C..]); set => WriteUInt64LittleEndian(Data[0x3C..], value); } + public ulong TrainerSeed6 { get => ReadUInt64LittleEndian(Data[0x44..]); set => WriteUInt64LittleEndian(Data[0x44..], value); } + public ulong TrainerSeed7 { get => ReadUInt64LittleEndian(Data[0x4C..]); set => WriteUInt64LittleEndian(Data[0x4C..], value); } - public uint TotalWins { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x54)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x54), value); } - public uint TotalWinsLoop { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x58)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x58), value); } - public uint TotalWinsLose { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x5C)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x5C), value); } - public uint RenshouCountOld { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x60)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x60), value); } - public uint RenshouCount { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x64)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x64), value); } + public uint TotalWins { get => ReadUInt32LittleEndian(Data[0x54..]); set => WriteUInt32LittleEndian(Data[0x54..], value); } + public uint TotalWinsLoop { get => ReadUInt32LittleEndian(Data[0x58..]); set => WriteUInt32LittleEndian(Data[0x58..], value); } + public uint TotalWinsLose { get => ReadUInt32LittleEndian(Data[0x5C..]); set => WriteUInt32LittleEndian(Data[0x5C..], value); } + public uint RenshouCountOld { get => ReadUInt32LittleEndian(Data[0x60..]); set => WriteUInt32LittleEndian(Data[0x60..], value); } + public uint RenshouCount { get => ReadUInt32LittleEndian(Data[0x64..]); set => WriteUInt32LittleEndian(Data[0x64..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTrainerStatus8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTrainerStatus8b.cs index c3ffec035..3adb2aabb 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTrainerStatus8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTrainerStatus8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using static System.Buffers.Binary.BinaryPrimitives; @@ -8,10 +8,8 @@ namespace PKHeX.Core; /// Defeated Status for all trainers (Dpr.Trainer.TrainerID) /// /// size: 0x1618 -public sealed class BattleTrainerStatus8b : SaveBlock +public sealed class BattleTrainerStatus8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { - public BattleTrainerStatus8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - // Structure: // (bool IsWin, bool IsBattleSearcher)[707]; private const int COUNT_TRAINER = 707; @@ -48,11 +46,11 @@ public sealed class BattleTrainerStatus8b : SaveBlock { if ((uint)trainer >= COUNT_TRAINER) throw new ArgumentOutOfRangeException(nameof(trainer)); - return Offset + (trainer * SIZE_TRAINER); + return (trainer * SIZE_TRAINER); } - public bool GetIsWin(int trainer) => ReadUInt32LittleEndian(Data.AsSpan(GetTrainerOffset(trainer))) == 1; - public bool GetIsBattleSearcher(int trainer) => ReadUInt32LittleEndian(Data.AsSpan(GetTrainerOffset(trainer) + 4)) == 1; - public void SetIsWin(int trainer, bool value) => WriteUInt32LittleEndian(Data.AsSpan(GetTrainerOffset(trainer)), value ? 1u : 0u); - public void SetIsBattleSearcher(int trainer, bool value) => WriteUInt32LittleEndian(Data.AsSpan(GetTrainerOffset(trainer) + 4), value ? 1u : 0u); + public bool GetIsWin(int trainer) => ReadUInt32LittleEndian(Data[GetTrainerOffset(trainer)..]) == 1; + public bool GetIsBattleSearcher(int trainer) => ReadUInt32LittleEndian(Data[(GetTrainerOffset(trainer) + 4)..]) == 1; + public void SetIsWin(int trainer, bool value) => WriteUInt32LittleEndian(Data[GetTrainerOffset(trainer)..], value ? 1u : 0u); + public void SetIsBattleSearcher(int trainer, bool value) => WriteUInt32LittleEndian(Data[(GetTrainerOffset(trainer) + 4)..], value ? 1u : 0u); } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/BerryTreeGrowSave8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/BerryTreeGrowSave8b.cs index 2cecdbf8b..7c71d4336 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/BerryTreeGrowSave8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/BerryTreeGrowSave8b.cs @@ -9,10 +9,8 @@ namespace PKHeX.Core; /// /// size: 0x808 [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class BerryTreeGrowSave8b : SaveBlock +public sealed class BerryTreeGrowSave8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { - public BerryTreeGrowSave8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public const int KinomiGrowsCount = 128; public const int KinomiSize = 0x10; @@ -20,5 +18,5 @@ public sealed class BerryTreeGrowSave8b : SaveBlock // KinomiGrow[] kinomiGrows; // 0x0 // long LastUpdateMinutes; // 0x8 - public long LastUpdateMinutes { get => ReadInt64LittleEndian(Data.AsSpan(Offset + 0x800)); set => WriteInt64LittleEndian(Data.AsSpan(Offset + 0x800), value); } + public long LastUpdateMinutes { get => ReadInt64LittleEndian(Data[0x800..]); set => WriteInt64LittleEndian(Data[0x800..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/BoxLayout8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/BoxLayout8b.cs index 3d155ea37..44a211cad 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/BoxLayout8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/BoxLayout8b.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// Information about the storage boxes. /// /// Structure name: SaveBoxData, size: 0x64A -public sealed class BoxLayout8b : SaveBlock, IBoxDetailName +public sealed class BoxLayout8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw), IBoxDetailName { public const int BoxCount = 40; @@ -15,29 +15,27 @@ public sealed class BoxLayout8b : SaveBlock, IBoxDetailName public readonly int[] TeamSlots = new int[TeamCount * TeamSlotCount]; private const int TeamNameLength = 0x16; - public BoxLayout8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - private static int GetBoxNameOffset(int box) => SAV6.LongStringLength * box; private static int GetTeamNameOffset(int box) => GetBoxNameOffset(BoxCount) + (TeamNameLength * box); public string GetBoxName(int box) { - var span = Data.AsSpan(Offset + GetBoxNameOffset(box), SAV6.LongStringLength); + var span = Data.Slice(GetBoxNameOffset(box), SAV6.LongStringLength); if (ReadUInt16LittleEndian(span) == 0) - return $"Box {box + 1}"; + return BoxDetailNameExtensions.GetDefaultBoxName(box); return SAV.GetString(span); } public void SetBoxName(int box, ReadOnlySpan value) { - var span = Data.AsSpan(Offset + GetBoxNameOffset(box), SAV6.LongStringLength); + var span = Data.Slice(GetBoxNameOffset(box), SAV6.LongStringLength); SAV.SetString(span, value, StringMaxLength, StringConverterOption.ClearZero); } public string GetTeamName(int team) { - var offset = Offset + GetTeamNameOffset(team); - var span = Data.AsSpan(offset, TeamNameLength); + var offset = GetTeamNameOffset(team); + var span = Data.Slice(offset, TeamNameLength); if (ReadUInt16LittleEndian(span) == 0) return $"Team {team + 1}"; return SAV.GetString(span); @@ -45,8 +43,8 @@ public sealed class BoxLayout8b : SaveBlock, IBoxDetailName public void SetTeamName(int team, ReadOnlySpan value) { - var offset = Offset + GetTeamNameOffset(team); - var span = Data.AsSpan(offset, TeamNameLength); + var offset = GetTeamNameOffset(team); + var span = Data.Slice(offset, TeamNameLength); SAV.SetString(span, value, TeamNameLength/2, StringConverterOption.ClearZero); } @@ -65,7 +63,7 @@ public sealed class BoxLayout8b : SaveBlock, IBoxDetailName { for (int i = 0; i < TeamCount * TeamSlotCount; i++) { - short val = ReadInt16LittleEndian(Data.AsSpan(Offset + TeamPositionOffset + (i * 2))); + short val = ReadInt16LittleEndian(Data[(TeamPositionOffset + (i * 2))..]); if (val < 0) { TeamSlots[i] = NONE_SELECTED; @@ -88,7 +86,7 @@ public sealed class BoxLayout8b : SaveBlock, IBoxDetailName public void SaveBattleTeams() { - var span = Data.AsSpan(Offset + TeamPositionOffset); + var span = Data[TeamPositionOffset..]; for (int i = 0; i < TeamCount * 6; i++) { int index = TeamSlots[i]; @@ -107,35 +105,35 @@ public sealed class BoxLayout8b : SaveBlock, IBoxDetailName // bitflags public byte LockedTeam { - get => Data[Offset + 0x61C]; + get => Data[0x61C]; set { if (value > BoxCount) value = BoxCount; - Data[Offset + 0x61C] = value; + Data[0x61C] = value; } } public byte BoxesUnlocked { - get => Data[Offset + 0x61D]; + get => Data[0x61D]; set { if (value > BoxCount) value = BoxCount; - Data[Offset + 0x61D] = value; + Data[0x61D] = value; } } public byte CurrentBox { - get => Data[Offset + 0x61E]; - set => Data[Offset + 0x61E] = value; + get => Data[0x61E]; + set => Data[0x61E] = value; } public bool GetIsTeamLocked(int team) => (LockedTeam & (1 << team)) != 0; - public int GetBoxWallpaperOffset(int box) => Offset + 0x620 + box; + public static int GetBoxWallpaperOffset(int box) => 0x620 + box; public int GetBoxWallpaper(int box) { @@ -153,7 +151,7 @@ public sealed class BoxLayout8b : SaveBlock, IBoxDetailName public ushort StatusPut { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x648)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x648), value); + get => ReadUInt16LittleEndian(Data[0x648..]); + set => WriteUInt16LittleEndian(Data[0x648..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/ConfigSave8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/ConfigSave8b.cs index e77d256e9..ae3ebb1f8 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/ConfigSave8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/ConfigSave8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using static System.Buffers.Binary.BinaryPrimitives; @@ -9,77 +9,75 @@ namespace PKHeX.Core; /// /// size 0x40, struct_name CONFIG [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class ConfigSave8b : SaveBlock +public sealed class ConfigSave8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { - public ConfigSave8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public TextSpeedOption TextSpeed { - get => (TextSpeedOption)ReadInt32LittleEndian(Data.AsSpan(Offset + 0)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0), (int)value); + get => (TextSpeedOption)ReadInt32LittleEndian(Data); + set => WriteInt32LittleEndian(Data, (int)value); } public int Language { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 4)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 4), value); + get => ReadInt32LittleEndian(Data[4..]); + set => WriteInt32LittleEndian(Data[4..], value); } - public bool IsKanji { get => Data[Offset + 8] == 1; set => Data[Offset + 8] = (byte)(value ? 1 : 0); } + public bool IsKanji { get => Data[8] == 1; set => Data[8] = (byte)(value ? 1 : 0); } public int WindowType { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x0C)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x0C), value); + get => ReadInt32LittleEndian(Data[0x0C..]); + set => WriteInt32LittleEndian(Data[0x0C..], value); } public BattleAnimationSetting MoveAnimations { - get => (BattleAnimationSetting)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x10)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x10), (int)value); + get => (BattleAnimationSetting)ReadInt32LittleEndian(Data[0x10..]); + set => WriteInt32LittleEndian(Data[0x10..], (int)value); } public BattleStyleSetting BattleStyle { - get => (BattleStyleSetting)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x14)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x14), (int)value); + get => (BattleStyleSetting)ReadInt32LittleEndian(Data[0x14..]); + set => WriteInt32LittleEndian(Data[0x14..], (int)value); } public PartyBoxSetting PartyBox { - get => (PartyBoxSetting)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x18)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x18), (int)value); + get => (PartyBoxSetting)ReadInt32LittleEndian(Data[0x18..]); + set => WriteInt32LittleEndian(Data[0x18..], (int)value); } // 4 byte bool, nice - public bool RegistNickname { get => Data[Offset + 0x1C] == 1; set => Data[Offset + 0x1C] = (byte)(value ? 1 : 0); } - public bool GyroSensor { get => Data[Offset + 0x20] == 1; set => Data[Offset + 0x20] = (byte)(value ? 1 : 0); } - public bool CameraShakeOfFossil { get => Data[Offset + 0x24] == 1; set => Data[Offset + 0x24] = (byte)(value ? 1 : 0); } + public bool RegistNickname { get => Data[0x1C] == 1; set => Data[0x1C] = (byte)(value ? 1 : 0); } + public bool GyroSensor { get => Data[0x20] == 1; set => Data[0x20] = (byte)(value ? 1 : 0); } + public bool CameraShakeOfFossil { get => Data[0x24] == 1; set => Data[0x24] = (byte)(value ? 1 : 0); } public CameraInputMode CameraUpDown { - get => (CameraInputMode)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x28)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x28), (int)value); + get => (CameraInputMode)ReadInt32LittleEndian(Data[0x28..]); + set => WriteInt32LittleEndian(Data[0x28..], (int)value); } public CameraInputMode CamerLeftRight { - get => (CameraInputMode)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x2C)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x2C), (int)value); + get => (CameraInputMode)ReadInt32LittleEndian(Data[0x2C..]); + set => WriteInt32LittleEndian(Data[0x2C..], (int)value); } - public bool AutoReport { get => Data[Offset + 0x30] == 1; set => Data[Offset + 0x30] = (byte)(value ? 1 : 0); } + public bool AutoReport { get => Data[0x30] == 1; set => Data[0x30] = (byte)(value ? 1 : 0); } public InputMode Input { - get => (InputMode)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x34)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x34), (int)value); + get => (InputMode)ReadInt32LittleEndian(Data[0x34..]); + set => WriteInt32LittleEndian(Data[0x34..], (int)value); } - public bool ShowNicknames { get => Data[Offset + 0x38] == 1; set => Data[Offset + 0x38] = (byte)(value ? 1 : 0); } - public byte VolumeBGM { get => Data[Offset + 0x3C]; set => Data[Offset + 0x3C] = value; } - public byte VolumeSoundEffects { get => Data[Offset + 0x3D]; set => Data[Offset + 0x3D] = value; } - public byte VolumeVoice { get => Data[Offset + 0x3E]; set => Data[Offset + 0x3E] = value; } + public bool ShowNicknames { get => Data[0x38] == 1; set => Data[0x38] = (byte)(value ? 1 : 0); } + public byte VolumeBGM { get => Data[0x3C]; set => Data[0x3C] = value; } + public byte VolumeSoundEffects { get => Data[0x3D]; set => Data[0x3D] = value; } + public byte VolumeVoice { get => Data[0x3E]; set => Data[0x3E] = value; } public enum CameraInputMode { diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/Contest8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/Contest8b.cs index e2274d773..635750bea 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/Contest8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/Contest8b.cs @@ -9,14 +9,12 @@ namespace PKHeX.Core; /// /// size 0x720, struct_name CONTEST_DATA [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class Contest8b : SaveBlock +public sealed class Contest8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { public const int SIZE_CONTEST_PHOTO = 0x16C; public const int PHOTO_MAX = 5; - public Contest8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - private const int OFS_RANK = SIZE_CONTEST_PHOTO * PHOTO_MAX; // 0x71C; - public uint ContestRankPoint { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_RANK)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_RANK), value); } + public uint ContestRankPoint { get => ReadUInt32LittleEndian(Data[OFS_RANK..]); set => WriteUInt32LittleEndian(Data[OFS_RANK..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/ContestPhotoLanguage8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/ContestPhotoLanguage8b.cs index 0cb24d31c..256339484 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/ContestPhotoLanguage8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/ContestPhotoLanguage8b.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace PKHeX.Core; @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// Contest photo language data /// /// CON_PHOTO_LANG_DATA size: 0x18 -public sealed class ContestPhotoLanguage8b : SaveBlock +public sealed class ContestPhotoLanguage8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { // structure: // 5 (Style, Beautiful, Cute, Clever, Strong) bytes of language IDs @@ -14,9 +14,7 @@ public sealed class ContestPhotoLanguage8b : SaveBlock // 2 s64 reserved private const int COUNT_CONTEST = 5; - public ContestPhotoLanguage8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - - public byte GetLanguage(int contest) => Data[Offset + GetContestOffset(contest)]; + public byte GetLanguage(int contest) => Data[GetContestOffset(contest)]; private static int GetContestOffset(int contest) { @@ -25,5 +23,5 @@ public sealed class ContestPhotoLanguage8b : SaveBlock return contest; } - public void SetLanguage(int contest, int language) => Data[Offset + GetContestOffset(contest)] = (byte)language; + public void SetLanguage(int contest, int language) => Data[GetContestOffset(contest)] = (byte)language; } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/Daycare8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/Daycare8b.cs index d1ed05337..2d35bfb44 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/Daycare8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/Daycare8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using static System.Buffers.Binary.BinaryPrimitives; @@ -9,10 +9,8 @@ namespace PKHeX.Core; /// /// size: 0x2C0 [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class Daycare8b : SaveBlock +public sealed class Daycare8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw), IDaycareStorage, IDaycareEggState, IDaycareRandomState { - public Daycare8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - // BLOCK STRUCTURE // PB8[2] Parents; // bool32 eggExist; @@ -21,39 +19,52 @@ public sealed class Daycare8b : SaveBlock private const int SlotCount = 2; private const int ExtraDataOffset = PokeCrypto.SIZE_8PARTY * SlotCount; + public const int SIZE = ExtraDataOffset + 16; // 0x2C0 - public bool GetDaycareSlotOccupied(int slot) => GetSlot(slot).Species != 0; + public int DaycareSlotCount => 2; - public int GetParentSlotOffset(int slot) + public bool IsDaycareOccupied(int slot) => GetSlot(slot).Species != 0; + + public void SetDaycareOccupied(int slot, bool occupied) + { + if (occupied) + return; + GetDaycareSlot(slot).Span.Clear(); + } + + public static int GetParentSlotOffset(int slot) { if ((uint)slot >= SlotCount) throw new ArgumentOutOfRangeException(nameof(slot)); - - return Offset + (slot * PokeCrypto.SIZE_8PARTY); + return slot * PokeCrypto.SIZE_8PARTY; } + public Memory GetDaycareSlot(int index) => Raw.Slice(GetParentSlotOffset(index), PokeCrypto.SIZE_8PARTY); + public PB8 GetSlot(int slot) { var offset = GetParentSlotOffset(slot); - var data = Data.AsSpan(offset, PokeCrypto.SIZE_8PARTY).ToArray(); + var data = Data.Slice(offset, PokeCrypto.SIZE_8PARTY).ToArray(); return new PB8(data); } + private Span ExtraData => Data[ExtraDataOffset..]; + public bool IsEggAvailable { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + ExtraDataOffset)) == 1; - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + ExtraDataOffset), value ? 1u : 0u); + get => ReadUInt32LittleEndian(ExtraData) == 1; + set => WriteUInt32LittleEndian(ExtraData, value ? 1u : 0u); } - public ulong DaycareSeed + public ulong Seed { - get => ReadUInt64LittleEndian(Data.AsSpan(Offset + ExtraDataOffset + 4)); - set => WriteUInt64LittleEndian(Data.AsSpan(Offset + ExtraDataOffset + 4), value); + get => ReadUInt64LittleEndian(ExtraData[4..]); + set => WriteUInt64LittleEndian(ExtraData[4..], value); } public int EggStepCount { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + ExtraDataOffset + 4 + 8)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + ExtraDataOffset + 4 + 8), value); + get => ReadInt32LittleEndian(ExtraData[8..]); + set => WriteInt32LittleEndian(ExtraData[8..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/EncounterSave8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/EncounterSave8b.cs index c563b1311..cb703c3f6 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/EncounterSave8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/EncounterSave8b.cs @@ -10,7 +10,7 @@ namespace PKHeX.Core; /// /// size 0x188, struct_name ENC_SV_DATA [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class EncounterSave8b : SaveBlock +public sealed class EncounterSave8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { public const int COUNT_HONEYTREE = 21; private const int SIZE_HillBackData = 0x8; @@ -27,55 +27,53 @@ public sealed class EncounterSave8b : SaveBlock private const int OFS_CLOSING = OFS_ROAM2 + SIZE_Roamer; // 0x17C private const int SIZE = OFS_CLOSING + SIZE_Closing; // 0x188 - public EncounterSave8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - - public void Clear() => Data.AsSpan(Offset, SIZE).Clear(); + public void Clear() => Data[..SIZE].Clear(); public int EncounterWalkCount { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x00)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x00), value); + get => ReadInt32LittleEndian(Data); + set => WriteInt32LittleEndian(Data, value); } public uint SafariRandSeed { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x04)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); + get => ReadUInt32LittleEndian(Data[0x04..]); + set => WriteUInt32LittleEndian(Data[0x04..], value); } public uint GenerateRandSeed { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x08)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x08), value); + get => ReadUInt32LittleEndian(Data[0x08..]); + set => WriteUInt32LittleEndian(Data[0x08..], value); } // HILL_BACK_DATA public bool HillTalkFlag { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_HillBackData + 0x00)) == 1; - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_HillBackData + 0x00), value ? 1u : 0u); + get => ReadUInt32LittleEndian(Data[(OFS_HillBackData + 0x00)..]) == 1; + set => WriteUInt32LittleEndian(Data[(OFS_HillBackData + 0x00)..], value ? 1u : 0u); } public ushort HillEncTblIdx1 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + OFS_HillBackData + 0x04)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + OFS_HillBackData + 0x04), value); + get => ReadUInt16LittleEndian(Data[(OFS_HillBackData + 0x04)..]); + set => WriteUInt16LittleEndian(Data[(OFS_HillBackData + 0x04)..], value); } public ushort HillEncTblIdx2 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + OFS_HillBackData + 0x06)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + OFS_HillBackData + 0x06), value); + get => ReadUInt16LittleEndian(Data[(OFS_HillBackData + 0x06)..]); + set => WriteUInt16LittleEndian(Data[(OFS_HillBackData + 0x06)..], value); } // HONEY_TREE public long HoneyLastUpdateMinutes { - get => ReadInt64LittleEndian(Data.AsSpan(Offset + OFS_HoneyTree + 0x00)); - set => WriteInt64LittleEndian(Data.AsSpan(Offset + OFS_HoneyTree + 0x00), value); + get => ReadInt64LittleEndian(Data[(OFS_HoneyTree + 0x00)..]); + set => WriteInt64LittleEndian(Data[(OFS_HoneyTree + 0x00)..], value); } public byte HoneyTreeNo { - get => Data[Offset + OFS_HoneyTree + 0x08]; - set => Data[Offset + OFS_HoneyTree + 0x08] = value; + get => Data[OFS_HoneyTree + 0x08]; + set => Data[OFS_HoneyTree + 0x08] = value; } public HoneyTree8b[] HoneyTrees @@ -88,7 +86,7 @@ public sealed class EncounterSave8b : SaveBlock { var result = new HoneyTree8b[COUNT_HONEYTREE]; for (int i = 0; i < result.Length; i++) - result[i] = new HoneyTree8b(Data, Offset + OFS_HoneyTree + 0xC + (i * HoneyTree8b.SIZE)); + result[i] = new HoneyTree8b(Raw.Slice(OFS_HoneyTree + 0xC + (i * HoneyTree8b.SIZE), HoneyTree8b.SIZE)); return result; } @@ -98,83 +96,84 @@ public sealed class EncounterSave8b : SaveBlock // data is already hard-referencing the original byte array. This is mostly a hack for Property Grid displays. } - public uint Radar1Species { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_SWAY + 0x00)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_SWAY + 0x00), value); } - public uint Radar1Chain { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_SWAY + 0x04)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_SWAY + 0x04), value); } - public uint Radar2Species { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_SWAY + 0x08)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_SWAY + 0x08), value); } - public uint Radar2Chain { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_SWAY + 0x0C)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_SWAY + 0x0C), value); } - public uint Radar3Species { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_SWAY + 0x10)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_SWAY + 0x10), value); } - public uint Radar3Chain { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_SWAY + 0x14)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_SWAY + 0x14), value); } + public uint Radar1Species { get => ReadUInt32LittleEndian(Data[(OFS_SWAY + 0x00)..]); set => WriteUInt32LittleEndian(Data[(OFS_SWAY + 0x00)..], value); } + public uint Radar1Chain { get => ReadUInt32LittleEndian(Data[(OFS_SWAY + 0x04)..]); set => WriteUInt32LittleEndian(Data[(OFS_SWAY + 0x04)..], value); } + public uint Radar2Species { get => ReadUInt32LittleEndian(Data[(OFS_SWAY + 0x08)..]); set => WriteUInt32LittleEndian(Data[(OFS_SWAY + 0x08)..], value); } + public uint Radar2Chain { get => ReadUInt32LittleEndian(Data[(OFS_SWAY + 0x0C)..]); set => WriteUInt32LittleEndian(Data[(OFS_SWAY + 0x0C)..], value); } + public uint Radar3Species { get => ReadUInt32LittleEndian(Data[(OFS_SWAY + 0x10)..]); set => WriteUInt32LittleEndian(Data[(OFS_SWAY + 0x10)..], value); } + public uint Radar3Chain { get => ReadUInt32LittleEndian(Data[(OFS_SWAY + 0x14)..]); set => WriteUInt32LittleEndian(Data[(OFS_SWAY + 0x14)..], value); } - public int BeforeZone { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_ZONEHISTORY + 0x00)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_ZONEHISTORY + 0x00), value); } - public int OldZone { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_ZONEHISTORY + 0x04)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_ZONEHISTORY + 0x04), value); } + public int BeforeZone { get => ReadInt32LittleEndian(Data[(OFS_ZONEHISTORY + 0x00)..]); set => WriteInt32LittleEndian(Data[(OFS_ZONEHISTORY + 0x00)..], value); } + public int OldZone { get => ReadInt32LittleEndian(Data[(OFS_ZONEHISTORY + 0x04)..]); set => WriteInt32LittleEndian(Data[(OFS_ZONEHISTORY + 0x04)..], value); } // Mesprit - public int Roamer1ZoneID { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM1 + 0x00)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM1 + 0x00), value); } - public ulong Roamer1Seed { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + OFS_ROAM1 + 0x04)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + OFS_ROAM1 + 0x04), value); } - public uint Roamer1Species { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM1 + 0x0C)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM1 + 0x0C), value); } - public uint Roamer1HP { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM1 + 0x10)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM1 + 0x10), value); } - public byte Roamer1Level { get => Data[Offset + OFS_ROAM1 + 0x14]; set => Data[Offset + OFS_ROAM1 + 0x14] = value; } - public uint Roamer1Status { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM1 + 0x18)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM1 + 0x18), value); } - public byte Roamer1Encount { get => Data[Offset + OFS_ROAM1 + 0x1C]; set => Data[Offset + OFS_ROAM1 + 0x1C] = value; } + public int Roamer1ZoneID { get => ReadInt32LittleEndian(Data[(OFS_ROAM1 + 0x00)..]); set => WriteInt32LittleEndian(Data[(OFS_ROAM1 + 0x00)..], value); } + public ulong Roamer1Seed { get => ReadUInt64LittleEndian(Data[(OFS_ROAM1 + 0x04)..]); set => WriteUInt64LittleEndian(Data[(OFS_ROAM1 + 0x04)..], value); } + public uint Roamer1Species { get => ReadUInt32LittleEndian(Data[(OFS_ROAM1 + 0x0C)..]); set => WriteUInt32LittleEndian(Data[(OFS_ROAM1 + 0x0C)..], value); } + public uint Roamer1HP { get => ReadUInt32LittleEndian(Data[(OFS_ROAM1 + 0x10)..]); set => WriteUInt32LittleEndian(Data[(OFS_ROAM1 + 0x10)..], value); } + public byte Roamer1Level { get => Data[OFS_ROAM1 + 0x14]; set => Data[OFS_ROAM1 + 0x14] = value; } + public uint Roamer1Status { get => ReadUInt32LittleEndian(Data[(OFS_ROAM1 + 0x18)..]); set => WriteUInt32LittleEndian(Data[(OFS_ROAM1 + 0x18)..], value); } + public byte Roamer1Encount { get => Data[OFS_ROAM1 + 0x1C]; set => Data[OFS_ROAM1 + 0x1C] = value; } // Cresselia - public int Roamer2ZoneID { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM2 + 0x00)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM2 + 0x00), value); } - public ulong Roamer2Seed { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + OFS_ROAM2 + 0x04)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + OFS_ROAM2 + 0x04), value); } - public uint Roamer2Species { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM2 + 0x0C)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM2 + 0x0C), value); } - public uint Roamer2HP { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM2 + 0x10)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM2 + 0x10), value); } - public byte Roamer2Level { get => Data[Offset + OFS_ROAM2 + 0x14]; set => Data[Offset + OFS_ROAM2 + 0x14] = value; } - public uint Roamer2Status { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM2 + 0x18)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_ROAM2 + 0x18), value); } - public byte Roamer2Encount { get => Data[Offset + OFS_ROAM2 + 0x1C]; set => Data[Offset + OFS_ROAM2 + 0x1C] = value; } + public int Roamer2ZoneID { get => ReadInt32LittleEndian(Data[(OFS_ROAM2 + 0x00)..]); set => WriteInt32LittleEndian(Data[(OFS_ROAM2 + 0x00)..], value); } + public ulong Roamer2Seed { get => ReadUInt64LittleEndian(Data[(OFS_ROAM2 + 0x04)..]); set => WriteUInt64LittleEndian(Data[(OFS_ROAM2 + 0x04)..], value); } + public uint Roamer2Species { get => ReadUInt32LittleEndian(Data[(OFS_ROAM2 + 0x0C)..]); set => WriteUInt32LittleEndian(Data[(OFS_ROAM2 + 0x0C)..], value); } + public uint Roamer2HP { get => ReadUInt32LittleEndian(Data[(OFS_ROAM2 + 0x10)..]); set => WriteUInt32LittleEndian(Data[(OFS_ROAM2 + 0x10)..], value); } + public byte Roamer2Level { get => Data[OFS_ROAM2 + 0x14]; set => Data[OFS_ROAM2 + 0x14] = value; } + public uint Roamer2Status { get => ReadUInt32LittleEndian(Data[(OFS_ROAM2 + 0x18)..]); set => WriteUInt32LittleEndian(Data[(OFS_ROAM2 + 0x18)..], value); } + public byte Roamer2Encount { get => Data[OFS_ROAM2 + 0x1C]; set => Data[OFS_ROAM2 + 0x1C] = value; } public bool GenerateValid { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_CLOSING + 0)) == 1; - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_CLOSING + 0), value ? 1u : 0u); + get => ReadUInt32LittleEndian(Data[(OFS_CLOSING + 0)..]) == 1; + set => WriteUInt32LittleEndian(Data[(OFS_CLOSING + 0)..], value ? 1u : 0u); } public short SprayCount { - get => ReadInt16LittleEndian(Data.AsSpan(Offset + OFS_CLOSING + 4)); - set => WriteInt16LittleEndian(Data.AsSpan(Offset + OFS_CLOSING + 4), value); + get => ReadInt16LittleEndian(Data[(OFS_CLOSING + 4)..]); + set => WriteInt16LittleEndian(Data[(OFS_CLOSING + 4)..], value); } - public byte SprayType { get => Data[Offset + OFS_CLOSING + 6]; set => Data[Offset + OFS_CLOSING + 6] = value; } - public byte VsSeekerCharge { get => Data[Offset + OFS_CLOSING + 7]; set => Data[Offset + OFS_CLOSING + 7] = value; } // max 100 - public byte PokeRadarCharge { get => Data[Offset + OFS_CLOSING + 8]; set => Data[Offset + OFS_CLOSING + 8] = value; } // max 50 - public byte FluteType { get => Data[Offset + OFS_CLOSING + 9]; set => Data[Offset + OFS_CLOSING + 9] = value; } // vidro + public byte SprayType { get => Data[OFS_CLOSING + 6]; set => Data[OFS_CLOSING + 6] = value; } + public byte VsSeekerCharge { get => Data[OFS_CLOSING + 7]; set => Data[OFS_CLOSING + 7] = value; } // max 100 + public byte PokeRadarCharge { get => Data[OFS_CLOSING + 8]; set => Data[OFS_CLOSING + 8] = value; } // max 50 + public byte FluteType { get => Data[OFS_CLOSING + 9]; set => Data[OFS_CLOSING + 9] = value; } // vidro } [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class HoneyTree8b(byte[] Data, int Offset) +public sealed class HoneyTree8b(Memory raw) { public const int SIZE = 0xC; + private Span Data => raw.Span; public bool Spreaded { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x00)) == 1; - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x00), value ? 1u : 0u); + get => ReadUInt32LittleEndian(Data) == 1; + set => WriteUInt32LittleEndian(Data, value ? 1u : 0u); } public int Minutes { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x04)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); + get => ReadInt32LittleEndian(Data[0x04..]); + set => WriteInt32LittleEndian(Data[0x04..], value); } public byte TblMonsNo { - get => Data[Offset + 0x08]; - set => Data[Offset + 0x08] = value; + get => Data[0x08]; + set => Data[0x08] = value; } public byte RareLv { - get => Data[Offset + 0x09]; - set => Data[Offset + 0x09] = value; + get => Data[0x09]; + set => Data[0x09] = value; } public byte SwayLv { - get => Data[Offset + 0x0A]; - set => Data[Offset + 0x0A] = value; + get => Data[0x0A]; + set => Data[0x0A] = value; } // 0xB alignment diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldGimmickSave8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldGimmickSave8b.cs index fa537a205..a890c4f61 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldGimmickSave8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldGimmickSave8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using static System.Buffers.Binary.BinaryPrimitives; @@ -8,11 +8,9 @@ namespace PKHeX.Core; /// size: 0xC /// [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class FieldGimmickSave8b : SaveBlock +public sealed class FieldGimmickSave8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { - public FieldGimmickSave8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - - public int Value0 { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x00)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x00), value); } - public int Value1 { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x04)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); } - public int Value2 { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x08)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x08), value); } + public int Value0 { get => ReadInt32LittleEndian(Data); set => WriteInt32LittleEndian(Data, value); } + public int Value1 { get => ReadInt32LittleEndian(Data[0x04..]); set => WriteInt32LittleEndian(Data[0x04..], value); } + public int Value2 { get => ReadInt32LittleEndian(Data[0x08..]); set => WriteInt32LittleEndian(Data[0x08..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldObjectSave8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldObjectSave8b.cs index 4165d21dd..de88f9d37 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldObjectSave8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldObjectSave8b.cs @@ -10,12 +10,10 @@ namespace PKHeX.Core; /// /// size: 0x109A0 (1000 * 4*17) [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class FieldObjectSave8b : SaveBlock +public sealed class FieldObjectSave8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { private const int COUNT_OBJECTS = 1_000; - public FieldObjectSave8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public FieldObject8b[] AllObjects { get => GetObjects(); @@ -26,7 +24,7 @@ public sealed class FieldObjectSave8b : SaveBlock { var result = new FieldObject8b[COUNT_OBJECTS]; for (int i = 0; i < result.Length; i++) - result[i] = new FieldObject8b(Data, Offset + (i * FieldObject8b.SIZE)); + result[i] = new FieldObject8b(Data.Slice((i * FieldObject8b.SIZE), FieldObject8b.SIZE)); return result; } @@ -46,10 +44,7 @@ public sealed class FieldObject8b public override string ToString() => $"{NameHash:X8} @ ({GridX:000},{GridY:000}) - {(Active ? "✓" : "✕")}"; - public FieldObject8b(byte[] data, int offset) - { - data.AsSpan(offset, SIZE).CopyTo(Data); - } + public FieldObject8b(ReadOnlySpan data) => data[..SIZE].CopyTo(Data); public byte Count // cnt { diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/FlagWork8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/FlagWork8b.cs index 4e6c9ea63..98fa9de0f 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/FlagWork8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/FlagWork8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -7,7 +7,8 @@ namespace PKHeX.Core; /// Structure that manages the 3 event variable storage arrays. /// /// Comprised of 3 fixed-sized arrays, we do our read/write in-place. Each element in an array is 4 bytes. Total size: 0x55F0 -public sealed class FlagWork8b : SaveBlock, IEventFlag, ISystemFlag, IEventWork +public sealed class FlagWork8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw), + IEventFlag, ISystemFlag, IEventWork { public const int COUNT_WORK = 500; public const int COUNT_FLAG = 4000; @@ -16,6 +17,7 @@ public sealed class FlagWork8b : SaveBlock, IEventFlag, ISystemFlag, IEv public const int OFS_WORK = 0; public const int OFS_FLAG = OFS_WORK + (COUNT_WORK * 4); public const int OFS_SYSTEM = OFS_FLAG + (COUNT_FLAG * 4); + public const int SIZE = OFS_SYSTEM + (COUNT_SYSTEM * 4); // 0x55F0 public const int FH_START = 0; public const int FH_END = 63; @@ -33,40 +35,39 @@ public sealed class FlagWork8b : SaveBlock, IEventFlag, ISystemFlag, IEv private const int END_VANISH = FV_END - 1; private const int COUNT_VANISH = END_VANISH - BASE_VANISH; // 0x120 - public FlagWork8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; public int CountFlag => COUNT_FLAG; public int CountSystem => COUNT_SYSTEM; public int CountWork => COUNT_WORK; - private int GetOffsetFlag(int index) + private static int GetOffsetFlag(int index) { if ((uint)index >= COUNT_FLAG) throw new ArgumentOutOfRangeException(nameof(index), $"Expected a number below {COUNT_FLAG}, not {index}."); - return Offset + OFS_FLAG + (4 * index); + return OFS_FLAG + (4 * index); } - private int GetOffsetSystem(int index) + private static int GetOffsetSystem(int index) { if ((uint)index >= COUNT_SYSTEM) throw new ArgumentOutOfRangeException(nameof(index), $"Expected a number below {COUNT_SYSTEM}, not {index}."); - return Offset + OFS_SYSTEM + (4 * index); + return OFS_SYSTEM + (4 * index); } - private int GetOffsetWork(int index) + private static int GetOffsetWork(int index) { if ((uint)index >= COUNT_WORK) throw new ArgumentOutOfRangeException(nameof(index), $"Expected a number below {COUNT_WORK}, not {index}."); - return Offset + OFS_WORK + (4 * index); + return OFS_WORK + (4 * index); } - public bool GetFlag (int index) => ReadInt32LittleEndian(Data.AsSpan(GetOffsetFlag(index))) == 1; - public bool GetSystemFlag(int index) => ReadInt32LittleEndian(Data.AsSpan(GetOffsetSystem(index))) == 1; - public int GetWork (int index) => ReadInt32LittleEndian(Data.AsSpan(GetOffsetWork(index))); - public float GetFloatWork(int index) => ReadSingleLittleEndian(Data.AsSpan(GetOffsetWork(index))); + public bool GetFlag (int index) => ReadInt32LittleEndian(Data[GetOffsetFlag(index)..]) == 1; + public bool GetSystemFlag(int index) => ReadInt32LittleEndian(Data[GetOffsetSystem(index)..]) == 1; + public int GetWork (int index) => ReadInt32LittleEndian(Data[GetOffsetWork(index)..]); + public float GetFloatWork(int index) => ReadSingleLittleEndian(Data[GetOffsetWork(index)..]); - public void SetFlag (int index, bool value) => WriteUInt32LittleEndian(Data.AsSpan(GetOffsetFlag(index)), value ? 1u : 0u); - public void SetSystemFlag(int index, bool value) => WriteUInt32LittleEndian(Data.AsSpan(GetOffsetSystem(index)), value ? 1u : 0u); - public void SetWork (int index, int value) => WriteInt32LittleEndian(Data.AsSpan(GetOffsetWork(index)), value); - public void SetFloatWork (int index, float value) => WriteSingleLittleEndian(Data.AsSpan(GetOffsetWork(index)), value); + public void SetFlag (int index, bool value) => WriteUInt32LittleEndian(Data[GetOffsetFlag(index)..], value ? 1u : 0u); + public void SetSystemFlag(int index, bool value) => WriteUInt32LittleEndian(Data[GetOffsetSystem(index)..], value ? 1u : 0u); + public void SetWork (int index, int value) => WriteInt32LittleEndian(Data[GetOffsetWork(index)..], value); + public void SetFloatWork (int index, float value) => WriteSingleLittleEndian(Data[GetOffsetWork(index)..], value); public void ResetFlag (int index) => SetFlag(index, false); public void ResetVanishFlag(int index) => SetVanishFlag(index, false); diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/Gem8Version.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/Gem8Version.cs index 2e268bd3a..27f661e4b 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/Gem8Version.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/Gem8Version.cs @@ -1,4 +1,4 @@ -using System; +using System; using static PKHeX.Core.Gem8Version; namespace PKHeX.Core; @@ -17,19 +17,19 @@ public enum Gem8Version V1_0 = 0x25, // 37 /// - /// Pre-release patch. + /// November 2021 pre-release patch. /// /// V1_1 = 0x2C, // 44 /// - /// February patch. + /// February 2022 patch. /// /// V1_2 = 0x32, // 50 /// - /// March patch. + /// March 2022 patch. /// /// V1_3 = 0x34, // 52 diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/MenuSelect8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/MenuSelect8b.cs index 4341c421a..1737920ca 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/MenuSelect8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/MenuSelect8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using static System.Buffers.Binary.BinaryPrimitives; @@ -8,35 +8,34 @@ namespace PKHeX.Core; /// Tracks the main menu items. Size: 0x44 /// [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class MenuSelect8b : SaveBlock +public sealed class MenuSelect8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { // (TopMenuItemTypeInt32, bool IsNew)[8], TopMenuItemTypeInt32 LastSelected private const int COUNT_ITEMS = 8; private const int SIZE_TUPLE = 4 + 4; // int,bool32 - public MenuSelect8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; public int GetMenuItem(int index) { int ofs = GetOffset(index); - return ReadInt32LittleEndian(Data.AsSpan(Offset + ofs)); + return ReadInt32LittleEndian(Data[ofs..]); } public void SetMenuItem(int index, int value) { int ofs = GetOffset(index); - WriteInt32LittleEndian(Data.AsSpan(Offset + ofs), value); + WriteInt32LittleEndian(Data[ofs..], value); } public bool GetMenuItemIsNew(int index) { int ofs = GetOffset(index); - return ReadInt32LittleEndian(Data.AsSpan(Offset + ofs + 4)) == 1; + return ReadInt32LittleEndian(Data[(ofs + 4)..]) == 1; } public void SetMenuItemIsNew(int index, bool value) { int ofs = GetOffset(index); - WriteInt32LittleEndian(Data.AsSpan(Offset + ofs + 4), value ? 1 : 0); + WriteInt32LittleEndian(Data[(ofs + 4)..], value ? 1 : 0); } private static int GetOffset(int index) @@ -46,5 +45,5 @@ public sealed class MenuSelect8b : SaveBlock return index * SIZE_TUPLE; } - public int LastSelectedMenu { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x40)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x40), value); } + public int LastSelectedMenu { get => ReadInt32LittleEndian(Data[0x40..]); set => WriteInt32LittleEndian(Data[0x40..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/MyItem8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/MyItem8b.cs index cd373b6c9..841aa0b88 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/MyItem8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/MyItem8b.cs @@ -7,24 +7,23 @@ namespace PKHeX.Core; /// Player item pouches storage /// /// size=0xBB80 ( items) -public sealed class MyItem8b : MyItem +public sealed class MyItem8b(SAV8BS sav, Memory raw) : MyItem(sav, raw) { public const int ItemSaveSize = 3000; - - public MyItem8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; + public const int SIZE = ItemSaveSize * InventoryItem8b.SIZE; public int GetItemQuantity(ushort itemIndex) { - var ofs = InventoryPouch8b.GetItemOffset(itemIndex, Offset); - var span = Data.AsSpan(ofs, InventoryItem8b.SIZE); + var ofs = InventoryPouch8b.GetItemOffset(itemIndex); + var span = Data.Slice(ofs, InventoryItem8b.SIZE); var item = InventoryItem8b.Read(itemIndex, span); return item.Count; } public void SetItemQuantity(ushort itemIndex, int quantity) { - var ofs = InventoryPouch8b.GetItemOffset(itemIndex, Offset); - var span = Data.AsSpan(ofs, InventoryItem8b.SIZE); + var ofs = InventoryPouch8b.GetItemOffset(itemIndex); + var span = Data.Slice(ofs, InventoryItem8b.SIZE); var item = InventoryItem8b.Read(itemIndex, span); item.Count = quantity; if (!item.IsValidSaveSortNumberCount) // not yet obtained @@ -43,8 +42,8 @@ public sealed class MyItem8b : MyItem ushort max = 0; foreach (var itemID in legal) { - var ofs = InventoryPouch8b.GetItemOffset(itemID, Offset); - var span = Data.AsSpan(ofs, InventoryItem8b.SIZE); + var ofs = InventoryPouch8b.GetItemOffset(itemID); + var span = Data.Slice(ofs, InventoryItem8b.SIZE); var item = InventoryItem8b.Read(itemID, span); if (item.SortOrder > max) max = item.SortOrder; @@ -90,14 +89,14 @@ public sealed class MyItem8b : MyItem for (ushort i = 0; i < (ushort)SAV.MaxItemID; i++) // even though there are 3000, just overwrite the ones that people will mess up. { if (!hashSet.Contains(i)) - InventoryItem8b.Clear(Data, InventoryPouch8b.GetItemOffset(i, Offset)); + InventoryItem8b.Clear(Data, InventoryPouch8b.GetItemOffset(i)); } } - private InventoryPouch8b MakePouch(InventoryType type) + private static InventoryPouch8b MakePouch(InventoryType type) { var info = ItemStorage8BDSP.Instance; var max = info.GetMax(type); - return new InventoryPouch8b(type, info, max, Offset); + return new InventoryPouch8b(type, info, max); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/MyStatus8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/MyStatus8b.cs index 979c59197..fbbf485a9 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/MyStatus8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/MyStatus8b.cs @@ -8,15 +8,13 @@ namespace PKHeX.Core; /// Player status and info about the trainer /// [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class MyStatus8b : SaveBlock +public sealed class MyStatus8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { public const int MAX_MONEY = 999_999; public const byte MAX_BADGE = 8; // public const byte MAX_RANK = 5; // unused? - public MyStatus8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - - private Span OriginalTrainerTrash => Data.AsSpan(Offset + 0, 0x1A); + private Span OriginalTrainerTrash => Data[..0x1A]; public string OT { @@ -26,55 +24,55 @@ public sealed class MyStatus8b : SaveBlock public uint ID32 { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x1C)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x1C), value); + get => ReadUInt32LittleEndian(Data[0x1C..]); + set => WriteUInt32LittleEndian(Data[0x1C..], value); } public ushort TID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x1C)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x1C), value); + get => ReadUInt16LittleEndian(Data[0x1C..]); + set => WriteUInt16LittleEndian(Data[0x1C..], value); } public ushort SID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x1E)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x1E), value); + get => ReadUInt16LittleEndian(Data[0x1E..]); + set => WriteUInt16LittleEndian(Data[0x1E..], value); } public uint Money { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x20)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x20), Math.Min(MAX_MONEY, value)); + get => ReadUInt32LittleEndian(Data[0x20..]); + set => WriteUInt32LittleEndian(Data[0x20..], Math.Min(MAX_MONEY, value)); } - public bool Male { get => Data[Offset + 0x24] == 1; set => Data[Offset + 0x24] = (byte)(value ? 1 : 0); } + public bool Male { get => Data[0x24] == 1; set => Data[0x24] = (byte)(value ? 1 : 0); } // 3byte align - public byte RegionCode { get => Data[Offset + 0x28]; set => Data[Offset + 0x28] = value; } - public byte BadgeCount { get => Data[Offset + 0x29]; set => Data[Offset + 0x29] = Math.Min(MAX_BADGE, value); } - public byte TrainerView { get => Data[Offset + 0x2A]; set => Data[Offset + 0x2A] = value; } - public byte ROMCode { get => Data[Offset + 0x2B]; set => Data[Offset + 0x2B] = value; } - public bool GameClear { get => Data[Offset + 0x2C] == 1; set => Data[Offset + 0x2C] = (byte)(value ? 1 : 0); } + public byte RegionCode { get => Data[0x28]; set => Data[0x28] = value; } + public byte BadgeCount { get => Data[0x29]; set => Data[0x29] = Math.Min(MAX_BADGE, value); } + public byte TrainerView { get => Data[0x2A]; set => Data[0x2A] = value; } + public byte ROMCode { get => Data[0x2B]; set => Data[0x2B] = value; } + public bool GameClear { get => Data[0x2C] == 1; set => Data[0x2C] = (byte)(value ? 1 : 0); } // 3byte align - public byte BodyType { get => Data[Offset + 0x30]; set => Data[Offset + 0x30] = value; } - public Fashion FashionID { get => (Fashion)Data[Offset + 0x31]; set => Data[Offset + 0x31] = (byte)value; } + public byte BodyType { get => Data[0x30]; set => Data[0x30] = value; } + public Fashion FashionID { get => (Fashion)Data[0x31]; set => Data[0x31] = (byte)value; } // 2byte align public MoveType StarterType { - get => (MoveType)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x34)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x34), (int)value); + get => (MoveType)ReadInt32LittleEndian(Data[0x34..]); + set => WriteInt32LittleEndian(Data[0x34..], (int)value); } - public bool DSPlayer { get => Data[Offset + 0x38] == 1; set => Data[Offset + 0x38] = (byte)(value ? 1 : 0); } + public bool DSPlayer { get => Data[0x38] == 1; set => Data[0x38] = (byte)(value ? 1 : 0); } // 3byte align /// turewalkMemberIndex - public int FollowIndex { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x3C)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x3C), value); } - public int X { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x40)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x40), value); } - public int Y { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x44)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x44), value); } - public float Height { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x48)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x48), value); } - public float Rotation { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x4C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x4C), value); } + public int FollowIndex { get => ReadInt32LittleEndian(Data[0x3C..]); set => WriteInt32LittleEndian(Data[0x3C..], value); } + public int X { get => ReadInt32LittleEndian(Data[0x40..]); set => WriteInt32LittleEndian(Data[0x40..], value); } + public int Y { get => ReadInt32LittleEndian(Data[0x44..]); set => WriteInt32LittleEndian(Data[0x44..], value); } + public float Height { get => ReadSingleLittleEndian(Data[0x48..]); set => WriteSingleLittleEndian(Data[0x48..], value); } + public float Rotation { get => ReadSingleLittleEndian(Data[0x4C..]); set => WriteSingleLittleEndian(Data[0x4C..], value); } // end structure! diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/MysteryBlock8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/MysteryBlock8b.cs index 362be6243..39b5a8d40 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/MysteryBlock8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/MysteryBlock8b.cs @@ -10,10 +10,8 @@ namespace PKHeX.Core; /// /// size ???, struct_name CONTEST_DATA [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class MysteryBlock8b : SaveBlock +public sealed class MysteryBlock8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { - public MysteryBlock8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public const int RecvDataMax = 50; public const int OneDayMax = 10; public const int SerialDataNoMax = 895; @@ -25,6 +23,7 @@ public sealed class MysteryBlock8b : SaveBlock public const int OFS_RECVFLAG = OFS_RECV + (RecvDataMax * RecvData8b.SIZE); // 0x2BC0 public const int OFS_ONEDAY = OFS_RECVFLAG + FlagSize; // 0x2CC0 public const int OFS_SERIALLOCK = OFS_ONEDAY + (OneDayMax * OneDay8b.SIZE); // 0x2D60 + public const int MinSize = OFS_SERIALLOCK + 8; // 0x2D68 // Structure: // RecvData[50] recvDatas; @@ -36,33 +35,33 @@ public sealed class MysteryBlock8b : SaveBlock // ushort[66] reserved_ushorts; // uint[66] reserve; - private int GetRecvDataOffset(int index) + private static int GetRecvDataOffset(int index) { if ((uint)index >= RecvDataMax) throw new ArgumentOutOfRangeException(nameof(index)); - return Offset + OFS_RECV + (index * RecvData8b.SIZE); + return OFS_RECV + (index * RecvData8b.SIZE); } - private int GetFlagOffset(int flag) + private static int GetFlagOffset(int flag) { if ((uint)flag >= FlagMax) throw new ArgumentOutOfRangeException(nameof(flag)); - return Offset + OFS_RECVFLAG + (flag >> 8); + return OFS_RECVFLAG + (flag >> 8); } - private int GetOneDayOffset(int index) + private static int GetOneDayOffset(int index) { if ((uint)index >= OneDayMax) throw new ArgumentOutOfRangeException(nameof(index)); - return Offset + OFS_ONEDAY + (index * OneDay8b.SIZE); + return OFS_ONEDAY + (index * OneDay8b.SIZE); } - public RecvData8b GetReceived(int index) => new(Data, GetRecvDataOffset(index)); - public OneDay8b GetOneDay(int index) => new(Data, GetOneDayOffset(index)); + public RecvData8b GetReceived(int index) => new(Raw.Slice(GetRecvDataOffset(index), RecvData8b.SIZE)); + public OneDay8b GetOneDay(int index) => new(Raw.Slice(GetOneDayOffset(index), OneDay8b.SIZE)); public bool GetFlag(int flag) => FlagUtil.GetFlag(Data, GetFlagOffset(flag), flag & 7); public void SetFlag(int flag, bool value) => FlagUtil.SetFlag(Data, GetFlagOffset(flag), flag & 7, value); public void SetReceived(int index, RecvData8b data) => data.CopyTo(Data, GetRecvDataOffset(index)); public void SetOneDay(int index, OneDay8b data) => data.CopyTo(Data, GetOneDayOffset(index)); - public long TicksSerialLock { get => ReadInt64LittleEndian(Data.AsSpan(Offset + OFS_SERIALLOCK)); set => WriteInt64LittleEndian(Data.AsSpan(Offset + OFS_SERIALLOCK), value); } + public long TicksSerialLock { get => ReadInt64LittleEndian(Data[OFS_SERIALLOCK..]); set => WriteInt64LittleEndian(Data[OFS_SERIALLOCK..], value); } public DateTime TimestampSerialLock { get => DateTime.FromFileTimeUtc(TicksSerialLock); set => TicksSerialLock = value.ToFileTimeUtc(); } public DateTime LocalTimestampSerialLock { get => TimestampSerialLock.ToLocalTime(); set => TimestampSerialLock = value.ToUniversalTime(); } public void ResetLock() => TicksSerialLock = 0; @@ -147,96 +146,98 @@ public sealed class MysteryBlock8b : SaveBlock } [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class RecvData8b(byte[] Data, int Offset = 0) +public sealed class RecvData8b(Memory raw) { public const int SIZE = 0xE0; // private const int ItemCount = 7; // private const int DressIDCount = 7; - public override string ToString() => $"{DeliveryID:0000} @ {LocalTimestamp:F}"; - public void CopyTo(Span destination, int offset1) => Data.AsSpan(Offset, SIZE).CopyTo(destination[offset1..]); - public void Clear() => Data.AsSpan(Offset, SIZE).Clear(); + private Span Data => raw.Span; - public long Ticks { get => ReadInt64LittleEndian(Data.AsSpan(Offset)); set => WriteInt64LittleEndian(Data.AsSpan(Offset), value); } + public override string ToString() => $"{DeliveryID:0000} @ {LocalTimestamp:F}"; + public void CopyTo(Span destination, int offset1) => Data.CopyTo(destination[offset1..]); + + public long Ticks { get => ReadInt64LittleEndian(Data); set => WriteInt64LittleEndian(Data, value); } public DateTime Timestamp { get => DateTime.FromFileTimeUtc(Ticks); set => Ticks = value.ToFileTimeUtc(); } public DateTime LocalTimestamp { get => Timestamp.ToLocalTime(); set => Timestamp = value.ToUniversalTime(); } - public ushort DeliveryID { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x8)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x8), value); } - public ushort TextID { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0xA)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0xA), value); } - public byte DataType { get => Data[Offset + 0xC]; set => Data[Offset + 0xC] = value; } - public byte ReservedByte1 { get => Data[Offset + 0xD]; set => Data[Offset + 0xD] = value; } - public short ReservedShort1 { get => ReadInt16LittleEndian(Data.AsSpan(Offset + 0xE)); set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0xE), value); } + public ushort DeliveryID { get => ReadUInt16LittleEndian(Data[0x8..]); set => WriteUInt16LittleEndian(Data[0x8..], value); } + public ushort TextID { get => ReadUInt16LittleEndian(Data[0xA..]); set => WriteUInt16LittleEndian(Data[0xA..], value); } + public byte DataType { get => Data[0xC]; set => Data[0xC] = value; } + public byte ReservedByte1 { get => Data[0xD]; set => Data[0xD] = value; } + public short ReservedShort1 { get => ReadInt16LittleEndian(Data[0xE..]); set => WriteInt16LittleEndian(Data[0xE..], value); } #region MonsData: 0x30 Bytes - public ushort Species { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x10)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x10), value); } - public ushort Form { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x12)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x12), value); } - public ushort HeldItem{ get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x14)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x14), value); } - public ushort Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x16)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x16), value); } - public ushort Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x18)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x18), value); } - public ushort Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x1A)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x1A), value); } - public ushort Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x1C), value); } + public ushort Species { get => ReadUInt16LittleEndian(Data[0x10..]); set => WriteUInt16LittleEndian(Data[0x10..], value); } + public ushort Form { get => ReadUInt16LittleEndian(Data[0x12..]); set => WriteUInt16LittleEndian(Data[0x12..], value); } + public ushort HeldItem{ get => ReadUInt16LittleEndian(Data[0x14..]); set => WriteUInt16LittleEndian(Data[0x14..], value); } + public ushort Move1 { get => ReadUInt16LittleEndian(Data[0x16..]); set => WriteUInt16LittleEndian(Data[0x16..], value); } + public ushort Move2 { get => ReadUInt16LittleEndian(Data[0x18..]); set => WriteUInt16LittleEndian(Data[0x18..], value); } + public ushort Move3 { get => ReadUInt16LittleEndian(Data[0x1A..]); set => WriteUInt16LittleEndian(Data[0x1A..], value); } + public ushort Move4 { get => ReadUInt16LittleEndian(Data[0x1C..]); set => WriteUInt16LittleEndian(Data[0x1C..], value); } public string OT { - get => StringConverter8.GetString(Data.AsSpan(Offset + 0x1E, 0x1A)); - set => StringConverter8.SetString(Data.AsSpan(Offset + 0x1E, 0x1A), value, 12, StringConverterOption.ClearZero); + get => StringConverter8.GetString(Data.Slice(0x1E, 0x1A)); + set => StringConverter8.SetString(Data.Slice(0x1E, 0x1A), value, 12, StringConverterOption.ClearZero); } - public byte OriginalTrainerGender { get => Data[Offset + 0x38]; set => Data[Offset + 0x38] = value; } - public byte IsEgg { get => Data[Offset + 0x39]; set => Data[Offset + 0x39] = value; } - public byte TwoRibbon { get => Data[Offset + 0x3A]; set => Data[Offset + 0x3A] = value; } - public byte Gender { get => Data[Offset + 0x3B]; set => Data[Offset + 0x3B] = value; } - public byte Language { get => Data[Offset + 0x3C]; set => Data[Offset + 0x3C] = value; } + public byte OriginalTrainerGender { get => Data[0x38]; set => Data[0x38] = value; } + public byte IsEgg { get => Data[0x39]; set => Data[0x39] = value; } + public byte TwoRibbon { get => Data[0x3A]; set => Data[0x3A] = value; } + public byte Gender { get => Data[0x3B]; set => Data[0x3B] = value; } + public byte Language { get => Data[0x3C]; set => Data[0x3C] = value; } // 0x3D 0x3E 0x3F reserved #endregion - public ushort Item1 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x40)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x40), value); } - public ushort Item2 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x50)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x50), value); } - public ushort Item3 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x60)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x60), value); } - public ushort Item4 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x70)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x70), value); } - public ushort Item5 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x80)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x80), value); } - public ushort Item6 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x90)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x90), value); } - public ushort Item7 { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0xA0)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0xA0), value); } - public ushort Item1Count { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x42)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x42), value); } - public ushort Item2Count { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x52)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x52), value); } - public ushort Item3Count { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x62)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x62), value); } - public ushort Item4Count { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x72)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x72), value); } - public ushort Item5Count { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x82)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x82), value); } - public ushort Item6Count { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x92)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x92), value); } - public ushort Item7Count { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0xA2)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0xA2), value); } + public ushort Item1 { get => ReadUInt16LittleEndian(Data[0x40..]); set => WriteUInt16LittleEndian(Data[0x40..], value); } + public ushort Item2 { get => ReadUInt16LittleEndian(Data[0x50..]); set => WriteUInt16LittleEndian(Data[0x50..], value); } + public ushort Item3 { get => ReadUInt16LittleEndian(Data[0x60..]); set => WriteUInt16LittleEndian(Data[0x60..], value); } + public ushort Item4 { get => ReadUInt16LittleEndian(Data[0x70..]); set => WriteUInt16LittleEndian(Data[0x70..], value); } + public ushort Item5 { get => ReadUInt16LittleEndian(Data[0x80..]); set => WriteUInt16LittleEndian(Data[0x80..], value); } + public ushort Item6 { get => ReadUInt16LittleEndian(Data[0x90..]); set => WriteUInt16LittleEndian(Data[0x90..], value); } + public ushort Item7 { get => ReadUInt16LittleEndian(Data[0xA0..]); set => WriteUInt16LittleEndian(Data[0xA0..], value); } + public ushort Item1Count { get => ReadUInt16LittleEndian(Data[0x42..]); set => WriteUInt16LittleEndian(Data[0x42..], value); } + public ushort Item2Count { get => ReadUInt16LittleEndian(Data[0x52..]); set => WriteUInt16LittleEndian(Data[0x52..], value); } + public ushort Item3Count { get => ReadUInt16LittleEndian(Data[0x62..]); set => WriteUInt16LittleEndian(Data[0x62..], value); } + public ushort Item4Count { get => ReadUInt16LittleEndian(Data[0x72..]); set => WriteUInt16LittleEndian(Data[0x72..], value); } + public ushort Item5Count { get => ReadUInt16LittleEndian(Data[0x82..]); set => WriteUInt16LittleEndian(Data[0x82..], value); } + public ushort Item6Count { get => ReadUInt16LittleEndian(Data[0x92..]); set => WriteUInt16LittleEndian(Data[0x92..], value); } + public ushort Item7Count { get => ReadUInt16LittleEndian(Data[0xA2..]); set => WriteUInt16LittleEndian(Data[0xA2..], value); } // 0xC reserved for each item index - public uint DressID1 { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0xB0)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0xB0), value); } - public uint DressID2 { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0xB4)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0xB4), value); } - public uint DressID3 { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0xB8)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0xB8), value); } - public uint DressID4 { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0xBC)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0xBC), value); } - public uint DressID5 { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0xC0)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0xC0), value); } - public uint DressID6 { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0xC4)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0xC4), value); } - public uint DressID7 { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0xC8)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0xC8), value); } + public uint DressID1 { get => ReadUInt32LittleEndian(Data[0xB0..]); set => WriteUInt32LittleEndian(Data[0xB0..], value); } + public uint DressID2 { get => ReadUInt32LittleEndian(Data[0xB4..]); set => WriteUInt32LittleEndian(Data[0xB4..], value); } + public uint DressID3 { get => ReadUInt32LittleEndian(Data[0xB8..]); set => WriteUInt32LittleEndian(Data[0xB8..], value); } + public uint DressID4 { get => ReadUInt32LittleEndian(Data[0xBC..]); set => WriteUInt32LittleEndian(Data[0xBC..], value); } + public uint DressID5 { get => ReadUInt32LittleEndian(Data[0xC0..]); set => WriteUInt32LittleEndian(Data[0xC0..], value); } + public uint DressID6 { get => ReadUInt32LittleEndian(Data[0xC4..]); set => WriteUInt32LittleEndian(Data[0xC4..], value); } + public uint DressID7 { get => ReadUInt32LittleEndian(Data[0xC8..]); set => WriteUInt32LittleEndian(Data[0xC8..], value); } - public uint MoneyData { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0xCC)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0xCC), value); } - public int Reserved1 { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0xD0)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0xD0), value); } - public int Reserved2 { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0xD4)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0xD4), value); } - public int Reserved3 { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0xD8)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0xD8), value); } - public int Reserved4 { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0xDC)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0xDC), value); } + public uint MoneyData { get => ReadUInt32LittleEndian(Data[0xCC..]); set => WriteUInt32LittleEndian(Data[0xCC..], value); } + public int Reserved1 { get => ReadInt32LittleEndian(Data[0xD0..]); set => WriteInt32LittleEndian(Data[0xD0..], value); } + public int Reserved2 { get => ReadInt32LittleEndian(Data[0xD4..]); set => WriteInt32LittleEndian(Data[0xD4..], value); } + public int Reserved3 { get => ReadInt32LittleEndian(Data[0xD8..]); set => WriteInt32LittleEndian(Data[0xD8..], value); } + public int Reserved4 { get => ReadInt32LittleEndian(Data[0xDC..]); set => WriteInt32LittleEndian(Data[0xDC..], value); } } [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class OneDay8b(byte[] Data, int Offset = 0) +public sealed class OneDay8b(Memory raw) { public const int SIZE = 0x10; + private Span Data => raw.Span; + public override string ToString() => $"{DeliveryID:0000} @ {LocalTimestamp:F}"; - public void CopyTo(Span destination, int offset1) => Data.AsSpan(Offset, SIZE).CopyTo(destination[offset1..]); - public void Clear() => Data.AsSpan(Offset, SIZE).Clear(); + public void CopyTo(Span destination, int offset1) => Data.CopyTo(destination[offset1..]); - public long Ticks { get => ReadInt64LittleEndian(Data.AsSpan(Offset)); set => WriteInt64LittleEndian(Data.AsSpan(Offset), value); } + public long Ticks { get => ReadInt64LittleEndian(Data); set => WriteInt64LittleEndian(Data, value); } public DateTime Timestamp { get => DateTime.FromFileTimeUtc(Ticks); set => Ticks = value.ToFileTimeUtc(); } public DateTime LocalTimestamp { get => Timestamp.ToLocalTime(); set => Timestamp = value.ToUniversalTime(); } - public ushort DeliveryID { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x8)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x8), value); } - public short Reserved1 { get => ReadInt16LittleEndian(Data.AsSpan(Offset + 0xA)); set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0xA), value); } - public short Reserved2 { get => ReadInt16LittleEndian(Data.AsSpan(Offset + 0xC)); set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0xC), value); } - public short Reserved3 { get => ReadInt16LittleEndian(Data.AsSpan(Offset + 0xE)); set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0xE), value); } + public ushort DeliveryID { get => ReadUInt16LittleEndian(Data[0x8..]); set => WriteUInt16LittleEndian(Data[0x8..], value); } + public short Reserved1 { get => ReadInt16LittleEndian(Data[0xA..]); set => WriteInt16LittleEndian(Data[0xA..], value); } + public short Reserved2 { get => ReadInt16LittleEndian(Data[0xC..]); set => WriteInt16LittleEndian(Data[0xC..], value); } + public short Reserved3 { get => ReadInt16LittleEndian(Data[0xE..]); set => WriteInt16LittleEndian(Data[0xE..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/Party8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/Party8b.cs index 7b81d95b8..f0232b79e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/Party8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/Party8b.cs @@ -1,21 +1,26 @@ -namespace PKHeX.Core; +using System; + +namespace PKHeX.Core; /// /// Party data storage and metadata /// -public sealed class Party8b : SaveBlock +public sealed class Party8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { - public Party8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; + private const int SizeSingle = PokeCrypto.SIZE_8PARTY; + private const int TeamSize = 6 * SizeSingle; + + public const int SIZE = TeamSize + 2; // 0x812 public int PartyCount { - get => Data[Offset + (6 * PokeCrypto.SIZE_8PARTY)]; - set => Data[Offset + (6 * PokeCrypto.SIZE_8PARTY)] = (byte)value; + get => Data[TeamSize]; + set => Data[TeamSize] = (byte)value; } public int MarkingIndex { - get => Data[Offset + (6 * PokeCrypto.SIZE_8PARTY) + 1]; - set => Data[Offset + (6 * PokeCrypto.SIZE_8PARTY) + 1] = (byte)value; + get => Data[TeamSize + 1]; + set => Data[TeamSize + 1] = (byte)value; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayTime8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayTime8b.cs index 4943216a0..154cca3a7 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayTime8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayTime8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using static System.Buffers.Binary.BinaryPrimitives; @@ -8,17 +8,15 @@ namespace PKHeX.Core; /// Playtime storage /// [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class PlayTime8b : SaveBlock +public sealed class PlayTime8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { - public PlayTime8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public ushort PlayedHours { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset), value); + get => ReadUInt16LittleEndian(Data); + set => WriteUInt16LittleEndian(Data, value); } - public byte PlayedMinutes { get => Data[Offset + 2]; set => Data[Offset + 2] = value; } - public byte PlayedSeconds { get => Data[Offset + 3]; set => Data[Offset + 3] = value; } + public byte PlayedMinutes { get => Data[2]; set => Data[2] = value; } + public byte PlayedSeconds { get => Data[3]; set => Data[3] = value; } public string LastSavedTime => $"{PlayedHours:0000}ː{PlayedMinutes:00}ː{PlayedSeconds:00}"; // not : } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayerData8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayerData8b.cs index 552850925..7d3410476 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayerData8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayerData8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using static System.Buffers.Binary.BinaryPrimitives; @@ -9,7 +9,7 @@ namespace PKHeX.Core; /// /// size 0x80, struct_name PLAYER_SAVE_DATA [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class PlayerData8b : SaveBlock +public sealed class PlayerData8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { private const int SIZE_LOCATION = 4 + (4 * 3) + 4; // 20 (0x14) @@ -20,76 +20,72 @@ public sealed class PlayerData8b : SaveBlock private const int OFS_MAP = OFS_PART2 + 4 + 4; private const int OFS_TOKUSHU_BOOL = OFS_MAP + SIZE_LOCATION; private const int OFS_TOKUSHU = OFS_TOKUSHU_BOOL + 4; - private const int SIZE = OFS_TOKUSHU + SIZE_LOCATION; // 0x80 - - public PlayerData8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - - public void Clear() => Data.AsSpan(Offset, SIZE).Clear(); + public const int SIZE = OFS_TOKUSHU + SIZE_LOCATION; // 0x80 public bool GearType { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x00)) == 1; - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x00), value ? 1u : 0); + get => ReadUInt32LittleEndian(Data) == 1; + set => WriteUInt32LittleEndian(Data, value ? 1u : 0); } public bool ShoesFlag { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x04)) == 1; - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x04), value ? 1u : 0); + get => ReadUInt32LittleEndian(Data[0x04..]) == 1; + set => WriteUInt32LittleEndian(Data[0x04..], value ? 1u : 0); } public uint Form { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x08)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x08), value); + get => ReadUInt32LittleEndian(Data[0x08..]); + set => WriteUInt32LittleEndian(Data[0x08..], value); } - public byte BikeColor { get => Data[Offset + 0x0C]; set => Data[Offset + 0x0C] = value; } + public byte BikeColor { get => Data[0x0C]; set => Data[0x0C] = value; } // 0x10: WorpPoint - Teleport // 0x10: WorpPoint - Zenmetu // 0x10: WorpPoint - Ananuke - public int WarpTeleportZone { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_LOC1 + 0x00)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_LOC1 + 0x00), value); } - public float WarpTeleportX { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC1 + 0x04)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC1 + 0x04), value); } - public float WarpTeleportY { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC1 + 0x08)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC1 + 0x08), value); } - public float WarpTeleportZ { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC1 + 0x0C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC1 + 0x0C), value); } - public int WarpTeleportDir { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_LOC1 + 0x10)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_LOC1 + 0x10), value); } + public int WarpTeleportZone { get => ReadInt32LittleEndian(Data[(OFS_LOC1 + 0x00)..]); set => WriteInt32LittleEndian(Data[(OFS_LOC1 + 0x00)..], value); } + public float WarpTeleportX { get => ReadSingleLittleEndian(Data[(OFS_LOC1 + 0x04)..]); set => WriteSingleLittleEndian(Data[(OFS_LOC1 + 0x04)..], value); } + public float WarpTeleportY { get => ReadSingleLittleEndian(Data[(OFS_LOC1 + 0x08)..]); set => WriteSingleLittleEndian(Data[(OFS_LOC1 + 0x08)..], value); } + public float WarpTeleportZ { get => ReadSingleLittleEndian(Data[(OFS_LOC1 + 0x0C)..]); set => WriteSingleLittleEndian(Data[(OFS_LOC1 + 0x0C)..], value); } + public int WarpTeleportDir { get => ReadInt32LittleEndian(Data[(OFS_LOC1 + 0x10)..]); set => WriteInt32LittleEndian(Data[(OFS_LOC1 + 0x10)..], value); } - public int WarpZenmetuZone { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_LOC2 + 0x00)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_LOC2 + 0x00), value); } - public float WarpZenmetuX { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC2 + 0x04)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC2 + 0x04), value); } - public float WarpZenmetuY { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC2 + 0x08)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC2 + 0x08), value); } - public float WarpZenmetuZ { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC2 + 0x0C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC2 + 0x0C), value); } - public int WarpZenmetuDir { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_LOC2 + 0x10)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_LOC2 + 0x10), value); } + public int WarpZenmetuZone { get => ReadInt32LittleEndian(Data[(OFS_LOC2 + 0x00)..]); set => WriteInt32LittleEndian(Data[(OFS_LOC2 + 0x00)..], value); } + public float WarpZenmetuX { get => ReadSingleLittleEndian(Data[(OFS_LOC2 + 0x04)..]); set => WriteSingleLittleEndian(Data[(OFS_LOC2 + 0x04)..], value); } + public float WarpZenmetuY { get => ReadSingleLittleEndian(Data[(OFS_LOC2 + 0x08)..]); set => WriteSingleLittleEndian(Data[(OFS_LOC2 + 0x08)..], value); } + public float WarpZenmetuZ { get => ReadSingleLittleEndian(Data[(OFS_LOC2 + 0x0C)..]); set => WriteSingleLittleEndian(Data[(OFS_LOC2 + 0x0C)..], value); } + public int WarpZenmetuDir { get => ReadInt32LittleEndian(Data[(OFS_LOC2 + 0x10)..]); set => WriteInt32LittleEndian(Data[(OFS_LOC2 + 0x10)..], value); } - public int WarpAnanukeZone { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_LOC3 + 0x00)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_LOC3 + 0x00), value); } - public float WarpAnanukeX { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC3 + 0x04)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC3 + 0x04), value); } - public float WarpAnanukeY { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC3 + 0x08)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC3 + 0x08), value); } - public float WarpAnanukeZ { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC3 + 0x0C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_LOC3 + 0x0C), value); } - public int WarpAnanukeDir { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_LOC3 + 0x10)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_LOC3 + 0x10), value); } + public int WarpAnanukeZone { get => ReadInt32LittleEndian(Data[(OFS_LOC3 + 0x00)..]); set => WriteInt32LittleEndian(Data[(OFS_LOC3 + 0x00)..], value); } + public float WarpAnanukeX { get => ReadSingleLittleEndian(Data[(OFS_LOC3 + 0x04)..]); set => WriteSingleLittleEndian(Data[(OFS_LOC3 + 0x04)..], value); } + public float WarpAnanukeY { get => ReadSingleLittleEndian(Data[(OFS_LOC3 + 0x08)..]); set => WriteSingleLittleEndian(Data[(OFS_LOC3 + 0x08)..], value); } + public float WarpAnanukeZ { get => ReadSingleLittleEndian(Data[(OFS_LOC3 + 0x0C)..]); set => WriteSingleLittleEndian(Data[(OFS_LOC3 + 0x0C)..], value); } + public int WarpAnanukeDir { get => ReadInt32LittleEndian(Data[(OFS_LOC3 + 0x10)..]); set => WriteInt32LittleEndian(Data[(OFS_LOC3 + 0x10)..], value); } public float WalkCount { - get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_PART2 + 0x00)); - set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_PART2 + 0x04), value); + get => ReadSingleLittleEndian(Data[(OFS_PART2 + 0x00)..]); + set => WriteSingleLittleEndian(Data[(OFS_PART2 + 0x04)..], value); } public int NatukiWalkCount { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_PART2 + 0x04)); - set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_PART2 + 0x04), value); + get => ReadInt32LittleEndian(Data[(OFS_PART2 + 0x04)..]); + set => WriteSingleLittleEndian(Data[(OFS_PART2 + 0x04)..], value); } - public int TownMapZone { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_MAP + 0x00)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_MAP + 0x00), value); } - public float TownMapX { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_MAP + 0x04)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_MAP + 0x04), value); } - public float TownMapY { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_MAP + 0x08)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_MAP + 0x08), value); } - public float TownMapZ { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_MAP + 0x0C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_MAP + 0x0C), value); } - public int TownMapDir { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_MAP + 0x10)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_MAP + 0x10), value); } + public int TownMapZone { get => ReadInt32LittleEndian(Data[(OFS_MAP + 0x00)..]); set => WriteInt32LittleEndian(Data[(OFS_MAP + 0x00)..], value); } + public float TownMapX { get => ReadSingleLittleEndian(Data[(OFS_MAP + 0x04)..]); set => WriteSingleLittleEndian(Data[(OFS_MAP + 0x04)..], value); } + public float TownMapY { get => ReadSingleLittleEndian(Data[(OFS_MAP + 0x08)..]); set => WriteSingleLittleEndian(Data[(OFS_MAP + 0x08)..], value); } + public float TownMapZ { get => ReadSingleLittleEndian(Data[(OFS_MAP + 0x0C)..]); set => WriteSingleLittleEndian(Data[(OFS_MAP + 0x0C)..], value); } + public int TownMapDir { get => ReadInt32LittleEndian(Data[(OFS_MAP + 0x10)..]); set => WriteInt32LittleEndian(Data[(OFS_MAP + 0x10)..], value); } public bool IsTokushuLocation { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU_BOOL + 0x00)) == 1; - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU_BOOL + 0x00), value ? 1u : 0u); + get => ReadUInt32LittleEndian(Data[(OFS_TOKUSHU_BOOL + 0x00)..]) == 1; + set => WriteUInt32LittleEndian(Data[(OFS_TOKUSHU_BOOL + 0x00)..], value ? 1u : 0u); } - public int TokushuZone { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU + 0x00)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU + 0x00), value); } - public float TokushuX { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU + 0x04)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU + 0x04), value); } - public float TokushuY { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU + 0x08)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU + 0x08), value); } - public float TokushuZ { get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU + 0x0C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU + 0x0C), value); } - public int TokushuDir { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU + 0x10)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU + 0x10), value); } + public int TokushuZone { get => ReadInt32LittleEndian(Data[(OFS_TOKUSHU + 0x00)..]); set => WriteInt32LittleEndian(Data[(OFS_TOKUSHU + 0x00)..], value); } + public float TokushuX { get => ReadSingleLittleEndian(Data[(OFS_TOKUSHU + 0x04)..]); set => WriteSingleLittleEndian(Data[(OFS_TOKUSHU + 0x04)..], value); } + public float TokushuY { get => ReadSingleLittleEndian(Data[(OFS_TOKUSHU + 0x08)..]); set => WriteSingleLittleEndian(Data[(OFS_TOKUSHU + 0x08)..], value); } + public float TokushuZ { get => ReadSingleLittleEndian(Data[(OFS_TOKUSHU + 0x0C)..]); set => WriteSingleLittleEndian(Data[(OFS_TOKUSHU + 0x0C)..], value); } + public int TokushuDir { get => ReadInt32LittleEndian(Data[(OFS_TOKUSHU + 0x10)..]); set => WriteInt32LittleEndian(Data[(OFS_TOKUSHU + 0x10)..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/PoffinSaveData8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/PoffinSaveData8b.cs index 7929ae75f..aefb9f154 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/PoffinSaveData8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/PoffinSaveData8b.cs @@ -11,10 +11,8 @@ namespace PKHeX.Core; /// /// size: 0x644 [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class PoffinSaveData8b : SaveBlock +public sealed class PoffinSaveData8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { - public PoffinSaveData8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - // structure: // PoffinData[] Poffins; // int CookingCount; @@ -22,16 +20,17 @@ public sealed class PoffinSaveData8b : SaveBlock // 0x640 bytes of data is for poffins public const int COUNT_POFFIN = 100; - private int GetPoffinOffset(int index) + private static int GetPoffinOffset(int index) { if ((uint)index >= COUNT_POFFIN) throw new ArgumentOutOfRangeException(nameof(index)); - return Offset + (index * Poffin8b.SIZE); + return (index * Poffin8b.SIZE); } - public Poffin8b GetPoffin(int index) => new(Data, GetPoffinOffset(index)); - public void SetPoffin(int index, Poffin8b poffin) => poffin.CopyTo(Data, GetPoffinOffset(index)); - public int CookingCount { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x640)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x640), value); } + private Memory GetPoffinSpan(int index) => Raw.Slice(GetPoffinOffset(index), Poffin8b.SIZE); + public Poffin8b GetPoffin(int index) => new(GetPoffinSpan(index)); + public void SetPoffin(int index, Poffin8b poffin) => poffin.CopyTo(GetPoffinSpan(index).Span); + public int CookingCount { get => ReadInt32LittleEndian(Data[0x640..]); set => WriteInt32LittleEndian(Data[0x640..], value); } public Poffin8b[] GetPoffins() { @@ -60,13 +59,10 @@ public sealed class Poffin8b private readonly byte[] Data = new byte[SIZE]; - public Poffin8b(byte[] data, int offset) - { - data.AsSpan(offset, SIZE).CopyTo(Data); - } + public Poffin8b(Memory raw) => raw.Span.CopyTo(Data); public void Clear() => Data.AsSpan().Clear(); - public void CopyTo(byte[] data, int offset) => Data.CopyTo(data, offset); + public void CopyTo(Span dest) => Data.CopyTo(dest); public void ToNull() => MstID = 0xFF; public bool IsNull => MstID == 0xFF; diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/Poketch8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/Poketch8b.cs index 9b65bc3b1..2746f7251 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/Poketch8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/Poketch8b.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; namespace PKHeX.Core; @@ -7,7 +8,7 @@ namespace PKHeX.Core; /// /// size: 0x19C [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class Poketch8b : SaveBlock +public sealed class Poketch8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { public const int APP_REGIST_MAX = 20; // bool array unlock flags public const int POKETCH_MAP_MARK_MAX = 6; // mark_map_pos[6] @@ -16,29 +17,29 @@ public sealed class Poketch8b : SaveBlock public const int POKETCH_PEDOMETER_MAX = 99999; public const int POKETCH_CALENDER_MONTH_MAX = 12; // calendar markbit uint[12] - public Poketch8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - - public enum PoketchApp8b - { - DWATCH = 0, - CALC = 1, - MEMO = 2, - PEDOMETER = 3, - POKELIST = 4, - NATSUKI_CHECK = 5, - DOWSING = 6, - SODATEYA_CAMERA = 7, - POKEMON_HISTORY = 8, - COUNTER = 9, - AWATCH = 10, - MAP_MARKING = 11, - COINTOSS = 12, - CALENDER = 13, - DOTART = 14, - ROULETTE = 15, - POKEMON_COUNTER = 16, - KITCHEN_TIMER = 17, - COLOR_CHANGER = 18, - HIDEN_WAZA = 19, - } + public const int SIZE = 0x19C; +} + +public enum PoketchApp8b +{ + DWATCH = 0, + CALC = 1, + MEMO = 2, + PEDOMETER = 3, + POKELIST = 4, + NATSUKI_CHECK = 5, + DOWSING = 6, + SODATEYA_CAMERA = 7, + POKEMON_HISTORY = 8, + COUNTER = 9, + AWATCH = 10, + MAP_MARKING = 11, + COINTOSS = 12, + CALENDER = 13, + DOTART = 14, + ROULETTE = 15, + POKEMON_COUNTER = 16, + KITCHEN_TIMER = 17, + COLOR_CHANGER = 18, + HIDEN_WAZA = 19, } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/RandomGroup8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/RandomGroup8b.cs index a5099f6eb..e46fb50d5 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/RandomGroup8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/RandomGroup8b.cs @@ -10,12 +10,10 @@ namespace PKHeX.Core; /// /// size: 0x630 [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class RandomGroup8b : SaveBlock +public sealed class RandomGroup8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { public const int COUNT_GROUP = 12; - public RandomGroup8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public RandomSeed8b[] Seeds { get => GetSeeds(); @@ -26,7 +24,7 @@ public sealed class RandomGroup8b : SaveBlock { var result = new RandomSeed8b[COUNT_GROUP]; for (int i = 0; i < result.Length; i++) - result[i] = new RandomSeed8b(Data, Offset + (i * RandomSeed8b.SIZE)); + result[i] = new RandomSeed8b(Raw.Slice(i * RandomSeed8b.SIZE, RandomSeed8b.SIZE)); return result; } @@ -42,8 +40,10 @@ public sealed class RandomGroup8b : SaveBlock /// Random Seed data. /// [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class RandomSeed8b(byte[] Data, int Offset) +public sealed class RandomSeed8b(Memory raw) { + private Span Data => raw.Span; + public const int GROUP_NAME_SIZE = 16; // chars public const int PERSON_NAME_SIZE = 32; // chars @@ -59,46 +59,46 @@ public sealed class RandomSeed8b(byte[] Data, int Offset) public string GroupName { - get => StringConverter8.GetString(Data.AsSpan(Offset + OFS_GROUPNAME, GROUP_NAME_SIZE * 2)); - set => StringConverter8.SetString(Data.AsSpan(Offset + OFS_GROUPNAME, GROUP_NAME_SIZE * 2), value, GROUP_NAME_SIZE, StringConverterOption.ClearZero); + get => StringConverter8.GetString(Data[..(GROUP_NAME_SIZE * 2)]); + set => StringConverter8.SetString(Data[..(GROUP_NAME_SIZE * 2)], value, GROUP_NAME_SIZE, StringConverterOption.ClearZero); } public string PlayerName { - get => StringConverter8.GetString(Data.AsSpan(Offset + OFS_PLAYERNAME, PERSON_NAME_SIZE * 2)); - set => StringConverter8.SetString(Data.AsSpan(Offset + OFS_PLAYERNAME, PERSON_NAME_SIZE * 2), value, PERSON_NAME_SIZE, StringConverterOption.ClearZero); + get => StringConverter8.GetString(Data.Slice(OFS_PLAYERNAME, PERSON_NAME_SIZE * 2)); + set => StringConverter8.SetString(Data.Slice(OFS_PLAYERNAME, PERSON_NAME_SIZE * 2), value, PERSON_NAME_SIZE, StringConverterOption.ClearZero); } - public bool Male { get => Data[Offset + OFS_GENDER] == 1; set => Data[Offset + OFS_GENDER] = (byte)(value ? 1 : 0); } + public bool Male { get => Data[OFS_GENDER] == 1; set => Data[+ OFS_GENDER] = (byte)(value ? 1 : 0); } public int RegionCode { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_REGION)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_REGION), value); + get => ReadInt32LittleEndian(Data[OFS_REGION..]); + set => WriteInt32LittleEndian(Data[OFS_REGION..], value); } public ulong Seed { - get => ReadUInt64LittleEndian(Data.AsSpan(Offset + OFS_SEED)); - set => WriteUInt64LittleEndian(Data.AsSpan(Offset + OFS_SEED), value); + get => ReadUInt64LittleEndian(Data[OFS_SEED..]); + set => WriteUInt64LittleEndian(Data[OFS_SEED..], value); } public ulong Random { - get => ReadUInt64LittleEndian(Data.AsSpan(Offset + OFS_RAND)); - set => WriteUInt64LittleEndian(Data.AsSpan(Offset + OFS_RAND), value); + get => ReadUInt64LittleEndian(Data[OFS_RAND..]); + set => WriteUInt64LittleEndian(Data[OFS_RAND..], value); } public long Ticks { - get => ReadInt64LittleEndian(Data.AsSpan(Offset + OFS_TICK)); - set => WriteInt64LittleEndian(Data.AsSpan(Offset + OFS_TICK), value); + get => ReadInt64LittleEndian(Data[OFS_TICK..]); + set => WriteInt64LittleEndian(Data[OFS_TICK..], value); } public int UserID { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_UID)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_UID), value); + get => ReadInt32LittleEndian(Data[OFS_UID..]); + set => WriteInt32LittleEndian(Data[OFS_UID..], value); } public DateTime Timestamp { get => DateTime.FromFileTimeUtc(Ticks); set => Ticks = value.ToFileTimeUtc(); } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/Record8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/Record8b.cs index 8e48cf321..076358048 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/Record8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/Record8b.cs @@ -8,14 +8,12 @@ namespace PKHeX.Core; /// Stores 12 different sets of record data, with the earliest entry being called the "head" record index. /// /// size: 0x5A0 (12 * 4*30) -public sealed class Record8b : SaveBlock, IRecordStatStorage +public sealed class Record8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw), IRecordStatStorage { public const int RecordIndexCount = 12; // There's a total of 12 uint[30] record entries. The head one is used, not sure about the others. public const int RecordCount = 30; public const int RecordMaxValue = 999_999; - public Record8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public static int GetMax(int recordID) => MaxValue_BDSP[recordID]; private static int ClampRecord(int recordID, int value) @@ -24,16 +22,16 @@ public sealed class Record8b : SaveBlock, IRecordStatStorage return Math.Min(max, value); } - public int GetRecordOffset(int recordID) + public static int GetRecordOffset(int recordID) { if ((uint)recordID >= RecordCount) throw new ArgumentOutOfRangeException(nameof(recordID)); - return Offset + (sizeof(int) * recordID); + return (sizeof(int) * recordID); } public int GetRecord(int recordID) { - var value = ReadInt32LittleEndian(Data.AsSpan(GetRecordOffset(recordID))); + var value = ReadInt32LittleEndian(Data[GetRecordOffset(recordID)..]); if (recordID != 0) value = ClampRecord(recordID, value); return value; @@ -43,7 +41,7 @@ public sealed class Record8b : SaveBlock, IRecordStatStorage { if (recordID != 0) value = Math.Min(RecordMaxValue, value); - WriteInt32LittleEndian(Data.AsSpan(GetRecordOffset(recordID)), value); + WriteInt32LittleEndian(Data[GetRecordOffset(recordID)..], value); } public void AddRecord(int recordID, int count = 1) => SetRecord(recordID, GetRecord(recordID) + count); diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/RecordAddData8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/RecordAddData8b.cs index b067c9976..28e6d322d 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/RecordAddData8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/RecordAddData8b.cs @@ -7,10 +7,9 @@ namespace PKHeX.Core; /// Stores additional record data. /// /// size: 0x3C0 -public sealed class RecordAddData8b : SaveBlock +public sealed class RecordAddData8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { // RECORD_ADD_DATA: 0x30-sized[12] (0x240 bytes), and 12*byte[32] (0x180), total 0x3C0 - public RecordAddData8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; private const int COUNT_RECORD_ADD = 12; private const int COUNT_RECORD_RANKING = 12; @@ -20,7 +19,7 @@ public sealed class RecordAddData8b : SaveBlock { if ((uint)index >= COUNT_RECORD_ADD) throw new ArgumentOutOfRangeException(nameof(index)); - return new RecordAdd8b(Data, Offset + (index * RecordAdd8b.SIZE)); + return new RecordAdd8b(Raw.Slice(index * RecordAdd8b.SIZE, RecordAdd8b.SIZE)); } public RecordAdd8b[] GetRecords() @@ -47,14 +46,16 @@ public sealed class RecordAddData8b : SaveBlock } } -public sealed class RecordAdd8b(byte[] Data, int Offset) +public sealed class RecordAdd8b(Memory Raw) { + private Span Data => Raw.Span; + public const int SIZE = 0x30; public string OT { - get => StringConverter8.GetString(Data.AsSpan(Offset + 0, 0x1A)); - set => StringConverter8.SetString(Data.AsSpan(Offset + 0, 0x1A), value, 12, StringConverterOption.ClearZero); + get => StringConverter8.GetString(Data[..0x1A]); + set => StringConverter8.SetString(Data[..0x1A], value, 12, StringConverterOption.ClearZero); } // 1A reserved byte @@ -62,37 +63,37 @@ public sealed class RecordAdd8b(byte[] Data, int Offset) public int Language { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x1C)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x1C), value); + get => ReadInt32LittleEndian(Data[0x1C..]); + set => WriteInt32LittleEndian(Data[0x1C..], value); } - public byte Gender { get => Data[Offset + 0x20]; set => Data[Offset + 0x20] = value; } + public byte Gender { get => Data[0x20]; set => Data[0x20] = value; } // 21 // 22 // 23 public int BodyType { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x24)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x24), value); + get => ReadInt32LittleEndian(Data[0x24..]); + set => WriteInt32LittleEndian(Data[0x24..], value); } public uint ID32 { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x28)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x28), value); + get => ReadUInt32LittleEndian(Data[0x28..]); + set => WriteUInt32LittleEndian(Data[0x28..], value); } public ushort TID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x28)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x28), value); + get => ReadUInt16LittleEndian(Data[0x28..]); + set => WriteUInt16LittleEndian(Data[0x28..], value); } public ushort SID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x2A)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x2A), value); + get => ReadUInt16LittleEndian(Data[0x2A..]); + set => WriteUInt16LittleEndian(Data[0x2A..], value); } // 0x2C int32 reserved diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/SaveItemShortcut8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/SaveItemShortcut8b.cs index 540813612..621733f8d 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/SaveItemShortcut8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/SaveItemShortcut8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using static System.Buffers.Binary.BinaryPrimitives; @@ -8,12 +8,10 @@ namespace PKHeX.Core; /// Tracks the 4 select bound item slots. Size: 0x8 (4 * u16) /// [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class SaveItemShortcut8b : SaveBlock +public sealed class SaveItemShortcut8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { - public SaveItemShortcut8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - - public int Item0 { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x00)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x00), value); } - public int Item1 { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x02)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x02), value); } - public int Item2 { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x04)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); } - public int Item3 { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x06)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x06), value); } + public int Item0 { get => ReadInt32LittleEndian(Data); set => WriteInt32LittleEndian(Data, value); } + public int Item1 { get => ReadInt32LittleEndian(Data[0x02..]); set => WriteInt32LittleEndian(Data[0x02..], value); } + public int Item2 { get => ReadInt32LittleEndian(Data[0x04..]); set => WriteInt32LittleEndian(Data[0x04..], value); } + public int Item3 { get => ReadInt32LittleEndian(Data[0x06..]); set => WriteInt32LittleEndian(Data[0x06..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/SealDeco8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/SealDeco8b.cs index 19faea721..fb2888eef 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/SealDeco8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/SealDeco8b.cs @@ -10,17 +10,15 @@ namespace PKHeX.Core; /// /// size 0x4288 [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class SealBallDecoData8b : SaveBlock +public sealed class SealBallDecoData8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { public const int COUNT_CAPSULE = 99; // CapsuleData[99] - private const int SIZE = 4 + (COUNT_CAPSULE * SealCapsule8b.SIZE); // 0x4288 + public const int SIZE = 4 + (COUNT_CAPSULE * SealCapsule8b.SIZE); // 0x4288 - public SealBallDecoData8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; + public void Clear() => Data.Clear(); - public void Clear() => Data.AsSpan(Offset, SIZE).Clear(); - - public byte CapsuleCount { get => Data[Offset]; set => Data[Offset] = value; } + public byte CapsuleCount { get => Data[0]; set => Data[0] = value; } public SealCapsule8b[] Capsules { @@ -32,7 +30,7 @@ public sealed class SealBallDecoData8b : SaveBlock { var result = new SealCapsule8b[COUNT_CAPSULE]; for (int i = 0; i < result.Length; i++) - result[i] = new SealCapsule8b(Data, Offset + 4 + (i * SealCapsule8b.SIZE)); + result[i] = new SealCapsule8b(Raw.Slice(4 + (i * SealCapsule8b.SIZE), SealCapsule8b.SIZE)); return result; } @@ -44,16 +42,18 @@ public sealed class SealBallDecoData8b : SaveBlock } [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class SealCapsule8b(byte[] Data, int Offset) +public sealed class SealCapsule8b(Memory raw) { public const int COUNT_SEAL = 20; // AffixSealData[20] public const int SIZE = 12 + (COUNT_SEAL * AffixSealData8b.SIZE); // 0xAC + private Span Data => raw.Span; + public override string ToString() => $"{(Species)Species}-{EncryptionConstant:X8}-{Unknown}"; - public uint Species { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0), value); } - public uint EncryptionConstant { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 4)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 4), value); } - public uint Unknown { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 8)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 8), value); } + public uint Species { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); } + public uint EncryptionConstant { get => ReadUInt32LittleEndian(Data[4..]); set => WriteUInt32LittleEndian(Data[4..], value); } + public uint Unknown { get => ReadUInt32LittleEndian(Data[8..]); set => WriteUInt32LittleEndian(Data[8..], value); } public AffixSealData8b[] Seals { @@ -65,7 +65,7 @@ public sealed class SealCapsule8b(byte[] Data, int Offset) { var result = new AffixSealData8b[COUNT_SEAL]; for (int i = 0; i < result.Length; i++) - result[i] = new AffixSealData8b(Data, Offset + 0xC + (i * AffixSealData8b.SIZE)); + result[i] = new AffixSealData8b(raw.Slice(0xC + (i * AffixSealData8b.SIZE), AffixSealData8b.SIZE)); return result; } @@ -77,14 +77,16 @@ public sealed class SealCapsule8b(byte[] Data, int Offset) } [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class AffixSealData8b(byte[] Data, int Offset) +public sealed class AffixSealData8b(Memory raw) { public const int SIZE = 8; // u16 id, s16 x,y,z + private Span Data => raw.Span; + public override string ToString() => $"{(Seal8b)SealID}-({X},{Y},{Z})"; - public ushort SealID { get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0)); set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0), value); } - public short X { get => ReadInt16LittleEndian(Data.AsSpan(Offset + 2)); set => WriteInt16LittleEndian(Data.AsSpan(Offset + 2), value); } - public short Y { get => ReadInt16LittleEndian(Data.AsSpan(Offset + 4)); set => WriteInt16LittleEndian(Data.AsSpan(Offset + 4), value); } - public short Z { get => ReadInt16LittleEndian(Data.AsSpan(Offset + 6)); set => WriteInt16LittleEndian(Data.AsSpan(Offset + 6), value); } + public ushort SealID { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, value); } + public short X { get => ReadInt16LittleEndian(Data[2..]); set => WriteInt16LittleEndian(Data[2..], value); } + public short Y { get => ReadInt16LittleEndian(Data[4..]); set => WriteInt16LittleEndian(Data[4..], value); } + public short Z { get => ReadInt16LittleEndian(Data[6..]); set => WriteInt16LittleEndian(Data[6..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/SealList8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/SealList8b.cs index 118303bb6..3a3308f46 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/SealList8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/SealList8b.cs @@ -7,18 +7,16 @@ namespace PKHeX.Core; /// Array storing all the seal sticker counts the player has collected. /// /// size: 0x960 -public sealed class SealList8b : SaveBlock +public sealed class SealList8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { public const int SealSaveSize = 200; public const int SealMaxCount = 99; - public SealList8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public IReadOnlyList ReadItems() { var result = new SealSticker8b[SealSaveSize]; for (int i = 0; i < result.Length; i++) - result[i] = new SealSticker8b(Data, Offset, i); + result[i] = new SealSticker8b(Data, i); return result; } @@ -26,7 +24,7 @@ public sealed class SealList8b : SaveBlock { ArgumentOutOfRangeException.ThrowIfNotEqual(items.Count, SealSaveSize); foreach (var item in items) - item.Write(Data, Offset); + item.Write(Data); SAV.State.Edited = true; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/SealSticker8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/SealSticker8b.cs index 9d30e138c..7b2bfa077 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/SealSticker8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/SealSticker8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -13,29 +13,29 @@ public sealed class SealSticker8b public int Count { get; set; } public int TotalCount { get; set; } - public SealSticker8b(byte[] data, int baseOffset, int index) + public SealSticker8b(Span raw, int index) { Index = index; - var offset = baseOffset + (SIZE * index); - var span = data.AsSpan(offset, SIZE); - Read(span); + var offset = (SIZE * index); + var span = raw.Slice(offset, SIZE); + ReadAbsolute(span); } - private void Read(ReadOnlySpan span) + private void ReadAbsolute(ReadOnlySpan span) { IsGet = ReadUInt32LittleEndian(span) == 1; Count = ReadInt32LittleEndian(span[4..]); TotalCount = ReadInt32LittleEndian(span[8..]); } - public void Write(byte[] data, int baseOffset) + public void Write(Span data) { - var offset = baseOffset + (SIZE * Index); - var span = data.AsSpan(offset, SIZE); - Write(span); + var offset = (SIZE * Index); + var span = data.Slice(offset, SIZE); + WriteAbsolute(span); } - private void Write(Span span) + private void WriteAbsolute(Span span) { WriteUInt32LittleEndian(span, IsGet ? 1u : 0u); WriteInt32LittleEndian(span[4..], Count); diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/SystemData8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/SystemData8b.cs index 964204e90..f8c808ad2 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/SystemData8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/SystemData8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using static System.Buffers.Binary.BinaryPrimitives; @@ -9,7 +9,7 @@ namespace PKHeX.Core; /// /// size: 0x138 [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class SystemData8b : SaveBlock +public sealed class SystemData8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { // Structure: // (u32 count, u64 FILETIME) Start Time @@ -25,18 +25,16 @@ public sealed class SystemData8b : SaveBlock private const int OFS_SNAPSHOT = 4 + (3 * SIZE_GMTIME) + SIZE_GMTIME; // 0x34 private const int OFS_FDBGM = OFS_SNAPSHOT + SIZE_SNAPSHOT; private const int OFS_RESERVED = OFS_FDBGM + 4; - private const int SIZE_TOTAL = OFS_RESERVED + (6 * 8); // 0x138 + internal const int SIZE = OFS_RESERVED + (6 * 8); // 0x138 - public SystemData8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - - public uint CountStart { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0 + (0 * SIZE_GMTIME))); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0 + (0 * SIZE_GMTIME)), value); } - public long TicksStart { get => ReadInt64LittleEndian(Data.AsSpan(Offset + 4 + (0 * SIZE_GMTIME))); set => WriteInt64LittleEndian(Data.AsSpan(Offset + 4 + (0 * SIZE_GMTIME)), value); } - public uint CountLatest { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0 + (1 * SIZE_GMTIME))); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0 + (1 * SIZE_GMTIME)), value); } - public long TicksLatest { get => ReadInt64LittleEndian(Data.AsSpan(Offset + 4 + (1 * SIZE_GMTIME))); set => WriteInt64LittleEndian(Data.AsSpan(Offset + 4 + (1 * SIZE_GMTIME)), value); } - public uint CountPenalty { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0 + (2 * SIZE_GMTIME))); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0 + (2 * SIZE_GMTIME)), value); } - public long TicksPenalty { get => ReadInt64LittleEndian(Data.AsSpan(Offset + 4 + (2 * SIZE_GMTIME))); set => WriteInt64LittleEndian(Data.AsSpan(Offset + 4 + (2 * SIZE_GMTIME)), value); } - public uint CountDaily { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0 + (3 * SIZE_GMTIME))); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0 + (3 * SIZE_GMTIME)), value); } - public long TicksDaily { get => ReadInt64LittleEndian(Data.AsSpan(Offset + 4 + (3 * SIZE_GMTIME))); set => WriteInt64LittleEndian(Data.AsSpan(Offset + 4 + (3 * SIZE_GMTIME)), value); } + public uint CountStart { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); } + public long TicksStart { get => ReadInt64LittleEndian(Data[(4 + (0 * SIZE_GMTIME))..]); set => WriteInt64LittleEndian(Data[(4 + (0 * SIZE_GMTIME))..], value); } + public uint CountLatest { get => ReadUInt32LittleEndian(Data[(0 + (1 * SIZE_GMTIME))..]); set => WriteUInt32LittleEndian(Data[(0 + (1 * SIZE_GMTIME))..], value); } + public long TicksLatest { get => ReadInt64LittleEndian(Data[(4 + (1 * SIZE_GMTIME))..]); set => WriteInt64LittleEndian(Data[(4 + (1 * SIZE_GMTIME))..], value); } + public uint CountPenalty { get => ReadUInt32LittleEndian(Data[(0 + (2 * SIZE_GMTIME))..]); set => WriteUInt32LittleEndian(Data[(0 + (2 * SIZE_GMTIME))..], value); } + public long TicksPenalty { get => ReadInt64LittleEndian(Data[(4 + (2 * SIZE_GMTIME))..]); set => WriteInt64LittleEndian(Data[(4 + (2 * SIZE_GMTIME))..], value); } + public uint CountDaily { get => ReadUInt32LittleEndian(Data[(0 + (3 * SIZE_GMTIME))..]); set => WriteUInt32LittleEndian(Data[(0 + (3 * SIZE_GMTIME))..], value); } + public long TicksDaily { get => ReadInt64LittleEndian(Data[(4 + (3 * SIZE_GMTIME))..]); set => WriteInt64LittleEndian(Data[(4 + (3 * SIZE_GMTIME))..], value); } // byte[] nxSnapshot // u32 fd_bgmEvnet diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/UgCountRecord8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/UgCountRecord8b.cs index 8d77b6f3c..916d3cc5a 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/UgCountRecord8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/UgCountRecord8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using static System.Buffers.Binary.BinaryPrimitives; @@ -9,43 +9,13 @@ namespace PKHeX.Core; /// /// size 0x20, struct_name UgCountRecord [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class UgCountRecord8b : SaveBlock +public sealed class UgCountRecord8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { - public UgCountRecord8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - - public short DigFossilPlayCount - { - get => ReadInt16LittleEndian(Data.AsSpan(Offset + 0x00)); - set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0x00), value); - } - public short NumStatueBroadcastOnTV - { - get => ReadInt16LittleEndian(Data.AsSpan(Offset + 0x02)); - set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0x02), value); - } - public int NumTimesSecretBaseBroadcastOnTVWereLiked - { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x04)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); - } - public int SomeoneSecretBaseLikeCount - { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x08)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x08), value); - } - public int NumSuccessfulLightStoneSearches - { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x0C)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x0C), value); - } - public long Reserved1 - { - get => ReadInt64LittleEndian(Data.AsSpan(Offset + 0x10)); - set => WriteInt64LittleEndian(Data.AsSpan(Offset + 0x10), value); - } - public long Reserved2 - { - get => ReadInt64LittleEndian(Data.AsSpan(Offset + 0x18)); - set => WriteInt64LittleEndian(Data.AsSpan(Offset + 0x18), value); - } + public short DigFossilPlayCount { get => ReadInt16LittleEndian(Data); set => WriteInt16LittleEndian(Data, value); } + public short NumStatueBroadcastOnTV { get => ReadInt16LittleEndian(Data[0x02..]); set => WriteInt16LittleEndian(Data[0x02..], value); } + public int NumTimesSecretBaseBroadcastOnTVWereLiked { get => ReadInt32LittleEndian(Data[0x04..]); set => WriteInt32LittleEndian(Data[0x04..], value); } + public int SomeoneSecretBaseLikeCount { get => ReadInt32LittleEndian(Data[0x08..]); set => WriteInt32LittleEndian(Data[0x08..], value); } + public int NumSuccessfulLightStoneSearches { get => ReadInt32LittleEndian(Data[0x0C..]); set => WriteInt32LittleEndian(Data[0x0C..], value); } + public long Reserved1 { get => ReadInt64LittleEndian(Data[0x10..]); set => WriteInt64LittleEndian(Data[0x10..], value); } + public long Reserved2 { get => ReadInt64LittleEndian(Data[0x18..]); set => WriteInt64LittleEndian(Data[0x18..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/UgSaveData8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/UgSaveData8b.cs index e86fbd9df..519d00260 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/UgSaveData8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/UgSaveData8b.cs @@ -9,7 +9,7 @@ namespace PKHeX.Core; /// /// size: 0x27A0 [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class UgSaveData8b : SaveBlock +public sealed class UgSaveData8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { public const int COUNT_DIGPOINTS = 10; public const int COUNT_ENCOUNTERS = 15; @@ -64,36 +64,34 @@ public sealed class UgSaveData8b : SaveBlock // int StatueID, int PedestalID, int X, int Y, int Direction (size: 20 bytes) // bool isEnable; // 0x26C - public UgSaveData8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; + public int ReturnZoneID { get => ReadInt32LittleEndian(Data); set => WriteInt32LittleEndian(Data, value); } + public int ReturnGridPositionX { get => ReadInt32LittleEndian(Data[0x04..]); set => WriteInt32LittleEndian(Data[0x4..], value); } + public int ReturnGridPositionY { get => ReadInt32LittleEndian(Data[0x08..]); set => WriteInt32LittleEndian(Data[0x8..], value); } + public int ReturnGridPositionZ { get => ReadInt32LittleEndian(Data[0x0C..]); set => WriteInt32LittleEndian(Data[0xC..], value); } - public int ReturnZoneID { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x00)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x0), value); } - public int ReturnGridPositionX { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x04)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x4), value); } - public int ReturnGridPositionY { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x08)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x8), value); } - public int ReturnGridPositionZ { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x0C)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0xC), value); } + public int ZenmetsuZoneID { get => ReadInt32LittleEndian(Data[0x10..]); set => WriteInt32LittleEndian(Data[0x10..], value); } + public float ZenmetsuPositionX { get => ReadSingleLittleEndian(Data[0x14..]); set => WriteSingleLittleEndian(Data[0x14..], value); } + public float ZenmetsuPositionY { get => ReadSingleLittleEndian(Data[0x18..]); set => WriteSingleLittleEndian(Data[0x18..], value); } + public float ZenmetsuPositionZ { get => ReadSingleLittleEndian(Data[0x1C..]); set => WriteSingleLittleEndian(Data[0x1C..], value); } + public int ZenmetsuDirection { get => ReadInt32LittleEndian(Data[0x20..]); set => WriteInt32LittleEndian(Data[0x20..], value); } - public int ZenmetsuZoneID { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x10)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x10), value); } - public float ZenmetsuPositionX { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x14)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x14), value); } - public float ZenmetsuPositionY { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x18)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x18), value); } - public float ZenmetsuPositionZ { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x1C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x1C), value); } - public int ZenmetsuDirection { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x20)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x20), value); } - - private Span GetDigPoints() => Data.AsSpan(Offset + 0x24, COUNT_DIGPOINTS); + private Span GetDigPoints() => Data.Slice(0x24, COUNT_DIGPOINTS); public void ClearDigPoints() => GetDigPoints().Fill(0xFF); - public int GetSlotOffset(int slot) + public static int GetSlotOffset(int slot) { if ((uint)slot >= COUNT_ENCOUNTERS) throw new ArgumentOutOfRangeException(nameof(slot)); - return Offset + OFS_ENCOUNTPOKE + (slot * PokeCrypto.SIZE_8PARTY); + return OFS_ENCOUNTPOKE + (slot * PokeCrypto.SIZE_8PARTY); } - public int ReturnUgZoneID { get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_ReturnUgZoneID)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_ReturnUgZoneID), value); } - public uint TalkPlayerDataID { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_UgRecord + 0x0)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_UgRecord + 0x0), value); } - public uint TalkPlayerCount { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_UgRecord + 0x4)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_UgRecord + 0x4), value); } + public int ReturnUgZoneID { get => ReadInt32LittleEndian(Data[OFS_ReturnUgZoneID..]); set => WriteInt32LittleEndian(Data[OFS_ReturnUgZoneID..], value); } + public uint TalkPlayerDataID { get => ReadUInt32LittleEndian(Data[(OFS_UgRecord + 0x0)..]); set => WriteUInt32LittleEndian(Data[(OFS_UgRecord + 0x0)..], value); } + public uint TalkPlayerCount { get => ReadUInt32LittleEndian(Data[(OFS_UgRecord + 0x4)..]); set => WriteUInt32LittleEndian(Data[(OFS_UgRecord + 0x4)..], value); } #region Seen NPCs - public Span GetTrainers() => Data.AsSpan(Offset + OFS_NPC, COUNT_TRAINERS); + public Span GetTrainers() => Data.Slice(OFS_NPC, COUNT_TRAINERS); public void SetTrainers(ReadOnlySpan data) { @@ -101,8 +99,8 @@ public sealed class UgSaveData8b : SaveBlock data.CopyTo(GetTrainers()); } - public byte GetNPCSeen(int index) => Data[Offset + OFS_NPC + index]; - public void SetNPCSeen(int index, byte value) => Data[Offset + OFS_NPC + index] = value; + public byte GetNPCSeen(int index) => Data[OFS_NPC + index]; + public void SetNPCSeen(int index, byte value) => Data[OFS_NPC + index] = value; public void ClearNPC() => GetTrainers().Clear(); public void ClearNPC(int start, int count = COUNT_TRAINERS) => FillNPC(0, start, count); @@ -129,10 +127,12 @@ public sealed class UgSaveData8b : SaveBlock if ((uint)start > COUNT_TRAINERS) throw new ArgumentOutOfRangeException(nameof(start)); - var ofs = Offset + OFS_NPC + start; + var ofs = OFS_NPC + start; for (int i = 0; i < count; i++) Data[ofs + i] = value; } #endregion + + public Memory this[int i] => Raw.Slice(GetSlotOffset(i), PokeCrypto.SIZE_8PARTY); } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/UndergroundItem8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/UndergroundItem8b.cs index ec435cd6d..1f23c32c3 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/UndergroundItem8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/UndergroundItem8b.cs @@ -1,11 +1,11 @@ -using System; +using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; public sealed class UndergroundItem8b { - private const int SIZE = 0xC; + public const int SIZE = 0xC; public readonly int Index; // not serialized public UgItemType Type => UgItemUtil.GetType(Index); public int MaxValue => UgItemUtil.GetMax(Index); @@ -14,29 +14,29 @@ public sealed class UndergroundItem8b public bool HideNewFlag { get; set; } public bool IsFavoriteFlag { get; set; } - public UndergroundItem8b(ReadOnlySpan data, int baseOffset, int index) + public UndergroundItem8b(ReadOnlySpan data, int index) { Index = index; - var offset = baseOffset + (SIZE * index); + var offset = (SIZE * index); var span = data.Slice(offset, SIZE); - Read(span); + ReadAbsolute(span); } - public void Write(Span data, int baseOffset) + public void Write(Span data) { - var offset = baseOffset + (SIZE * Index); + var offset = (SIZE * Index); var span = data.Slice(offset, SIZE); - Write(span); + WriteAbsolute(span); } - private void Read(ReadOnlySpan span) + private void ReadAbsolute(ReadOnlySpan span) { Count = ReadInt32LittleEndian(span); HideNewFlag = ReadUInt32LittleEndian(span[4..]) == 1; IsFavoriteFlag = ReadUInt32LittleEndian(span[8..]) == 1; } - private void Write(Span span) + private void WriteAbsolute(Span span) { WriteInt32LittleEndian(span, Count); WriteUInt32LittleEndian(span[4..], HideNewFlag ? 1u : 0u); diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/UndergroundItemList8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/UndergroundItemList8b.cs index 39a4d42fa..4f726c0e5 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/UndergroundItemList8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/UndergroundItemList8b.cs @@ -3,19 +3,21 @@ using System.Collections.Generic; namespace PKHeX.Core; -public sealed class UndergroundItemList8b : SaveBlock +/// +/// 2ED4 +/// +public sealed class UndergroundItemList8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { public const int ItemSaveSize = 999; public const int ItemMaxCount = 999; public const int StatueMaxCount = 99; - - public UndergroundItemList8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; + public const int SIZE = ItemSaveSize * UndergroundItem8b.SIZE; public IReadOnlyList ReadItems() { var result = new UndergroundItem8b[ItemSaveSize]; for (int i = 0; i < result.Length; i++) - result[i] = new UndergroundItem8b(Data, Offset, i); + result[i] = new UndergroundItem8b(Data, i); return result; } @@ -23,7 +25,7 @@ public sealed class UndergroundItemList8b : SaveBlock { ArgumentOutOfRangeException.ThrowIfNotEqual(items.Count, ItemSaveSize); foreach (var item in items) - item.Write(Data, Offset); + item.Write(Data); SAV.State.Edited = true; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/UnionSaveData8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/UnionSaveData8b.cs index 01d68846f..d8ac5c433 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/UnionSaveData8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/UnionSaveData8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using static System.Buffers.Binary.BinaryPrimitives; @@ -9,11 +9,9 @@ namespace PKHeX.Core; /// /// UnionSaveData size: 0xC [TypeConverter(typeof(ExpandableObjectConverter))] -public sealed class UnionSaveData8b : SaveBlock +public sealed class UnionSaveData8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { - public UnionSaveData8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - - public bool IsInitTalk { get => ReadUInt32LittleEndian(Data.AsSpan(Offset)) == 1; set => WriteUInt32LittleEndian(Data.AsSpan(Offset), value ? 1u : 0u); } - public int PenaltyCount { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x4)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x4), value); } - public float PenaltyTime { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x8)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x8), value); } + public bool IsInitTalk { get => ReadUInt32LittleEndian(Data) == 1; set => WriteUInt32LittleEndian(Data, value ? 1u : 0u); } + public int PenaltyCount { get => ReadInt32LittleEndian(Data[0x4..]); set => WriteInt32LittleEndian(Data[0x4..], value); } + public float PenaltyTime { get => ReadSingleLittleEndian(Data[0x8..]); set => WriteSingleLittleEndian(Data[0x8..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/Zukan8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/Zukan8b.cs index 6aa43144e..743f3cc90 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/Zukan8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/Zukan8b.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// Pokédex structure used for Brilliant Diamond & Shining Pearl. /// /// size 0x30B8, struct_name ZUKAN_WORK -public sealed class Zukan8b(SAV8BS sav, int dex) : ZukanBase(sav, dex) +public sealed class Zukan8b(SAV8BS sav, Memory dex) : ZukanBase(sav, dex) { /* Structure Notes: u32 [493] state: None/HeardOf/Seen/Captured @@ -140,7 +140,7 @@ public sealed class Zukan8b(SAV8BS sav, int dex) : ZukanBase(sav, dex) var index = species - 1; var offset = OFS_STATE + (sizeof(int) * index); - return (ZukanState8b)ReadInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset)); + return (ZukanState8b)ReadInt32LittleEndian(Data[offset..]); } public void SetState(ushort species, ZukanState8b state) @@ -150,7 +150,7 @@ public sealed class Zukan8b(SAV8BS sav, int dex) : ZukanBase(sav, dex) var index = species - 1; var offset = OFS_STATE + (sizeof(int) * index); - WriteInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset), (int)state); + WriteInt32LittleEndian(Data[offset..], (int)state); } private bool GetBoolean(int index, int baseOffset) @@ -159,7 +159,7 @@ public sealed class Zukan8b(SAV8BS sav, int dex) : ZukanBase(sav, dex) throw new ArgumentOutOfRangeException(nameof(index)); var offset = baseOffset + (ALIGN_BOOLARRAY * index); - return ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset)) == 1; + return ReadUInt32LittleEndian(Data[offset..]) == 1; } private void SetBoolean(int index, int baseOffset, bool value) @@ -168,7 +168,7 @@ public sealed class Zukan8b(SAV8BS sav, int dex) : ZukanBase(sav, dex) throw new ArgumentOutOfRangeException(nameof(index)); var offset = baseOffset + (ALIGN_BOOLARRAY * index); - WriteUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset), value ? 1u : 0u); + WriteUInt32LittleEndian(Data[offset..], value ? 1u : 0u); } public void GetGenderFlags(ushort species, out bool m, out bool f, out bool ms, out bool fs) @@ -197,7 +197,7 @@ public sealed class Zukan8b(SAV8BS sav, int dex) : ZukanBase(sav, dex) var index = species - 1; var offset = OFS_LANGUAGE + (sizeof(int) * index); - var current = ReadInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset)); + var current = ReadInt32LittleEndian(Data[offset..]); return (current & (1 << languageBit)) != 0; } @@ -211,10 +211,10 @@ public sealed class Zukan8b(SAV8BS sav, int dex) : ZukanBase(sav, dex) var index = species - 1; var offset = OFS_LANGUAGE + (sizeof(int) * index); - var current = ReadInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset)); + var current = ReadInt32LittleEndian(Data[offset..]); var mask = (1 << languageBit); var update = value ? current | mask : current & ~(mask); - WriteInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset), update); + WriteInt32LittleEndian(Data[offset..], update); } public void SetLanguageFlags(ushort species, int value) @@ -224,7 +224,7 @@ public sealed class Zukan8b(SAV8BS sav, int dex) : ZukanBase(sav, dex) var index = species - 1; var offset = OFS_LANGUAGE + (sizeof(int) * index); - WriteInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset), value); + WriteInt32LittleEndian(Data[offset..], value); } private static int GetLanguageBit(int language) @@ -238,14 +238,14 @@ public sealed class Zukan8b(SAV8BS sav, int dex) : ZukanBase(sav, dex) public bool HasRegionalDex { - get => ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + OFS_FLAG_REGIONAL)) == 1; - set => WriteUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + OFS_FLAG_REGIONAL), value ? 1u : 0u); + get => ReadUInt32LittleEndian(Data[OFS_FLAG_REGIONAL..]) == 1; + set => WriteUInt32LittleEndian(Data[OFS_FLAG_REGIONAL..], value ? 1u : 0u); } public bool HasNationalDex { - get => ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + OFS_FLAG_NATIONAL)) == 1; - set => WriteUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + OFS_FLAG_NATIONAL), value ? 1u : 0u); + get => ReadUInt32LittleEndian(Data[OFS_FLAG_NATIONAL..]) == 1; + set => WriteUInt32LittleEndian(Data[OFS_FLAG_NATIONAL..], value ? 1u : 0u); } public bool GetHasFormFlag(ushort species, byte form, bool shiny) @@ -257,7 +257,7 @@ public sealed class Zukan8b(SAV8BS sav, int dex) : ZukanBase(sav, dex) var baseOffset = GetFormOffset(species); var sizeShift = shiny ? GetFormSize(species) : 0; var offset = baseOffset + sizeShift + (ALIGN_BOOLARRAY * form); - return ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset)) == 1; + return ReadUInt32LittleEndian(Data[offset..]) == 1; } public void SetHasFormFlag(ushort species, byte form, bool shiny, bool value) @@ -269,7 +269,7 @@ public sealed class Zukan8b(SAV8BS sav, int dex) : ZukanBase(sav, dex) var baseOffset = GetFormOffset(species); var sizeShift = shiny ? GetFormSize(species) : 0; var offset = baseOffset + sizeShift + (ALIGN_BOOLARRAY * form); - WriteUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset), value ? 1u : 0u); + WriteUInt32LittleEndian(Data[offset..], value ? 1u : 0u); } public static int GetFormCount(ushort species) => species switch diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/ZukanSpinda8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/ZukanSpinda8b.cs index abae26828..c169f9e47 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/ZukanSpinda8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/ZukanSpinda8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -7,32 +7,30 @@ namespace PKHeX.Core; /// Tracks data for the game. /// /// ZUKAN_PERSONAL_RND_DATA size: 0x64 (100) -public sealed class ZukanSpinda8b : SaveBlock +public sealed class ZukanSpinda8b(SAV8BS sav, Memory raw) : SaveBlock(sav, raw) { - public ZukanSpinda8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public uint GetSeen(byte gender, bool shiny) { var ofs = GetOffset(gender, shiny); - return ReadUInt32LittleEndian(Data.AsSpan(Offset + ofs)); + return ReadUInt32LittleEndian(Data[ofs..]); } public uint GetCaught(byte gender, bool shiny) { var ofs = GetOffset(gender, shiny); - return ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x10 + ofs)); + return ReadUInt32LittleEndian(Data[(0x10 + ofs)..]); } public void SetSeen(byte gender, bool shiny, uint value) { var ofs = GetOffset(gender, shiny); - WriteUInt32LittleEndian(Data.AsSpan(Offset + ofs), value); + WriteUInt32LittleEndian(Data[ofs..], value); } public void SetCaught(byte gender, bool shiny, uint value) { var ofs = GetOffset(gender, shiny); - WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x10 + ofs), value); + WriteUInt32LittleEndian(Data[(0x10 + ofs)..], value); } private static int GetOffset(byte gender, bool shiny) => 4 * ((gender & 1) + (shiny ? 2 : 0)); diff --git a/PKHeX.Core/Saves/Substructures/Gen8/LA/BoxLayout8a.cs b/PKHeX.Core/Saves/Substructures/Gen8/LA/BoxLayout8a.cs index b6a31be7e..8e5e7c5d0 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/LA/BoxLayout8a.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/LA/BoxLayout8a.cs @@ -14,7 +14,7 @@ public sealed class BoxLayout8a(SAV8LA sav, SCBlock block) : SaveBlock(s private const int StringMaxLength = SAV6.LongStringLength / 2; // 0x22 bytes private static int GetBoxNameOffset(int box) => SAV6.LongStringLength * box; - private Span GetBoxNameSpan(int box) => Data.AsSpan(GetBoxNameOffset(box), SAV6.LongStringLength); + private Span GetBoxNameSpan(int box) => Data.Slice(GetBoxNameOffset(box), SAV6.LongStringLength); public string GetBoxName(int box) => SAV.GetString(GetBoxNameSpan(box)); public void SetBoxName(int box, ReadOnlySpan value) => SAV.SetString(GetBoxNameSpan(box), value, StringMaxLength, StringConverterOption.ClearZero); diff --git a/PKHeX.Core/Saves/Substructures/Gen8/LA/Coordinates8a.cs b/PKHeX.Core/Saves/Substructures/Gen8/LA/Coordinates8a.cs index 5ca6853e1..acdb0ce4b 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/LA/Coordinates8a.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/LA/Coordinates8a.cs @@ -12,7 +12,7 @@ namespace PKHeX.Core; public sealed class Coordinates8a(SAV8LA sav, SCBlock block) : SaveBlock(sav, block.Data) { // Map - private Span MapName() => Data.AsSpan(0x08, 0x48); + private Span MapName() => Data.Slice(0x08, 0x48); public string M { @@ -33,13 +33,13 @@ public sealed class Coordinates8a(SAV8LA sav, SCBlock block) : SaveBlock } // Position - public float X { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x50)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x50), value); } - public float Z { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x54)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x54), value); } - public float Y { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x58)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x58), value); } + public float X { get => ReadSingleLittleEndian(Data[0x50..]); set => WriteSingleLittleEndian(Data[0x50..], value); } + public float Z { get => ReadSingleLittleEndian(Data[0x54..]); set => WriteSingleLittleEndian(Data[0x54..], value); } + public float Y { get => ReadSingleLittleEndian(Data[0x58..]); set => WriteSingleLittleEndian(Data[0x58..], value); } // Rotation - public float RX { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x60)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x60), value); } - public float RZ { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x64)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x64), value); } - public float RY { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x68)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x68), value); } - public float RW { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x6C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x6C), value); } + public float RX { get => ReadSingleLittleEndian(Data[0x60..]); set => WriteSingleLittleEndian(Data[0x60..], value); } + public float RZ { get => ReadSingleLittleEndian(Data[0x64..]); set => WriteSingleLittleEndian(Data[0x64..], value); } + public float RY { get => ReadSingleLittleEndian(Data[0x68..]); set => WriteSingleLittleEndian(Data[0x68..], value); } + public float RW { get => ReadSingleLittleEndian(Data[0x6C..]); set => WriteSingleLittleEndian(Data[0x6C..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/LA/LastSaved8a.cs b/PKHeX.Core/Saves/Substructures/Gen8/LA/LastSaved8a.cs index ad8691183..df89b790a 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/LA/LastSaved8a.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/LA/LastSaved8a.cs @@ -14,7 +14,7 @@ namespace PKHeX.Core; [TypeConverter(typeof(ExpandableObjectConverter))] public sealed class LastSaved8a(SAV8LA sav, SCBlock block) : SaveBlock(sav, block.Data) { - private uint LastSaved { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x0)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x0), value); } + private uint LastSaved { get => ReadUInt32LittleEndian(Data.Slice(0x0)); set => WriteUInt32LittleEndian(Data.Slice(0x0), value); } private int LastSavedYear { get => (int)(LastSaved & 0xFFF); set => LastSaved = (LastSaved & 0xFFFFF000) | (uint)value; } private int LastSavedMonth { get => (int)((LastSaved >> 12) & 0xF); set => LastSaved = (LastSaved & 0xFFFF0FFF) | (((uint)value & 0xF) << 12); } private int LastSavedDay { get => (int)((LastSaved >> 16) & 0x1F); set => LastSaved = (LastSaved & 0xFFE0FFFF) | (((uint)value & 0x1F) << 16); } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/LA/MyStatus8a.cs b/PKHeX.Core/Saves/Substructures/Gen8/LA/MyStatus8a.cs index 16187b057..9e684dfe4 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/LA/MyStatus8a.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/LA/MyStatus8a.cs @@ -12,20 +12,20 @@ public sealed class MyStatus8a(SAV8LA sav, SCBlock block) : SaveBlock(sa { public uint ID32 { - get => ReadUInt32LittleEndian(Data.AsSpan(0x10)); - set => WriteUInt32LittleEndian(Data.AsSpan(0x10), value); + get => ReadUInt32LittleEndian(Data[0x10..]); + set => WriteUInt32LittleEndian(Data[0x10..], value); } public ushort TID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(0x10)); - set => WriteUInt16LittleEndian(Data.AsSpan(0x10), value); + get => ReadUInt16LittleEndian(Data[0x10..]); + set => WriteUInt16LittleEndian(Data[0x10..], value); } public ushort SID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(0x12)); - set => WriteUInt16LittleEndian(Data.AsSpan(0x12), value); + get => ReadUInt16LittleEndian(Data[0x12..]); + set => WriteUInt16LittleEndian(Data[0x12..], value); } public byte Game @@ -43,12 +43,12 @@ public sealed class MyStatus8a(SAV8LA sav, SCBlock block) : SaveBlock(sa // A6 public int Language { - get => Data[Offset + 0x17]; + get => Data[0x17]; set { if (value == Language) return; - Data[Offset + 0x17] = (byte)value; + Data[0x17] = (byte)value; // For runtime language, the game shifts all languages above Language 6 (unused) down one. if (value >= 6) @@ -57,7 +57,7 @@ public sealed class MyStatus8a(SAV8LA sav, SCBlock block) : SaveBlock(sa } } - private Span OriginalTrainerTrash => Data.AsSpan(0x20, 0x1A); + private Span OriginalTrainerTrash => Data.Slice(0x20, 0x1A); public string OT { @@ -67,7 +67,7 @@ public sealed class MyStatus8a(SAV8LA sav, SCBlock block) : SaveBlock(sa public byte Unk_0x50 { - get => Data[Offset + 0x50]; - set => Data[Offset + 0x50] = value; + get => Data[0x50]; + set => Data[0x50] = value; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/LA/PlayTime8a.cs b/PKHeX.Core/Saves/Substructures/Gen8/LA/PlayTime8a.cs index b29cd8e47..543d228c9 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/LA/PlayTime8a.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/LA/PlayTime8a.cs @@ -12,11 +12,11 @@ public sealed class PlayTime8a(SAV8LA sav, SCBlock block) : SaveBlock(sa { public ushort PlayedHours { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset), value); + get => ReadUInt16LittleEndian(Data); + set => WriteUInt16LittleEndian(Data, value); } - public byte PlayedMinutes { get => Data[Offset + 2]; set => Data[Offset + 2] = value; } - public byte PlayedSeconds { get => Data[Offset + 3]; set => Data[Offset + 3] = value; } + public byte PlayedMinutes { get => Data[2]; set => Data[2] = value; } + public byte PlayedSeconds { get => Data[3]; set => Data[3] = value; } public string LastSavedTime => $"{PlayedHours:0000}ː{PlayedMinutes:00}ː{PlayedSeconds:00}"; // not : } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/LA/PlayerFashion8a.cs b/PKHeX.Core/Saves/Substructures/Gen8/LA/PlayerFashion8a.cs index 517b872fa..884d3b931 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/LA/PlayerFashion8a.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/LA/PlayerFashion8a.cs @@ -10,16 +10,16 @@ namespace PKHeX.Core; [TypeConverter(typeof(ExpandableObjectConverter))] public sealed class PlayerFashion8a(SAV8LA sav, SCBlock block) : SaveBlock(sav, block.Data) { - public ulong Hair { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x00)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x00), value); } - public ulong Contacts { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x08)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x08), value); } - public ulong Eyebrows { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x10)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x10), value); } - public ulong Glasses { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x18)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x18), value); } - public ulong Hat { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x20)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x20), value); } - public ulong Top { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x28)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x28), value); } - public ulong Bottoms { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x30)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x30), value); } - public ulong _38 { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x38)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x38), value); } - public ulong Shoes { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x40)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x40), value); } - public ulong _48 { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x48)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x48), value); } - public ulong _50 { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x50)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x50), value); } - public ulong Skin { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x58)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x58), value); } + public ulong Hair { get => ReadUInt64LittleEndian(Data.Slice(0x00)); set => WriteUInt64LittleEndian(Data.Slice(0x00), value); } + public ulong Contacts { get => ReadUInt64LittleEndian(Data.Slice(0x08)); set => WriteUInt64LittleEndian(Data.Slice(0x08), value); } + public ulong Eyebrows { get => ReadUInt64LittleEndian(Data.Slice(0x10)); set => WriteUInt64LittleEndian(Data.Slice(0x10), value); } + public ulong Glasses { get => ReadUInt64LittleEndian(Data.Slice(0x18)); set => WriteUInt64LittleEndian(Data.Slice(0x18), value); } + public ulong Hat { get => ReadUInt64LittleEndian(Data.Slice(0x20)); set => WriteUInt64LittleEndian(Data.Slice(0x20), value); } + public ulong Top { get => ReadUInt64LittleEndian(Data.Slice(0x28)); set => WriteUInt64LittleEndian(Data.Slice(0x28), value); } + public ulong Bottoms { get => ReadUInt64LittleEndian(Data.Slice(0x30)); set => WriteUInt64LittleEndian(Data.Slice(0x30), value); } + public ulong _38 { get => ReadUInt64LittleEndian(Data.Slice(0x38)); set => WriteUInt64LittleEndian(Data.Slice(0x38), value); } + public ulong Shoes { get => ReadUInt64LittleEndian(Data.Slice(0x40)); set => WriteUInt64LittleEndian(Data.Slice(0x40), value); } + public ulong _48 { get => ReadUInt64LittleEndian(Data.Slice(0x48)); set => WriteUInt64LittleEndian(Data.Slice(0x48), value); } + public ulong _50 { get => ReadUInt64LittleEndian(Data.Slice(0x50)); set => WriteUInt64LittleEndian(Data.Slice(0x50), value); } + public ulong Skin { get => ReadUInt64LittleEndian(Data.Slice(0x58)); set => WriteUInt64LittleEndian(Data.Slice(0x58), value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveData.cs b/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveData.cs index 430d5aa25..c1ac2089e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveData.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveData.cs @@ -17,23 +17,23 @@ public sealed class PokedexSaveData public const int STATISTICS_ENTRIES_MAX = 1480; - public PokedexSaveData(byte[] data) + public PokedexSaveData(Memory data) { ArgumentOutOfRangeException.ThrowIfNotEqual(data.Length, POKEDEX_SAVE_DATA_SIZE); - GlobalData = new PokedexSaveGlobalData(data, 0); + GlobalData = new PokedexSaveGlobalData(data.Slice(0, PokedexSaveGlobalData.SIZE)); LocalData = new PokedexSaveLocalData[5]; for (var i = 0; i < LocalData.Length; i++) - LocalData[i] = new PokedexSaveLocalData(data, 0x10 + (0x10 * i)); + LocalData[i] = new PokedexSaveLocalData(data.Slice(0x10 + (PokedexSaveLocalData.SIZE * i), PokedexSaveLocalData.SIZE)); ResearchEntries = new PokedexSaveResearchEntry[PokedexSave8a.MAX_SPECIES]; for (var i = 0; i < ResearchEntries.Length; i++) - ResearchEntries[i] = new PokedexSaveResearchEntry(data, 0x70 + (0x58 * i)); + ResearchEntries[i] = new PokedexSaveResearchEntry(data.Slice(0x70 + (0x58 * i), PokedexSaveResearchEntry.SIZE)); StatisticsEntries = new PokedexSaveStatisticsEntry[STATISTICS_ENTRIES_MAX]; for (var i = 0; i < StatisticsEntries.Length; i++) - StatisticsEntries[i] = new PokedexSaveStatisticsEntry(data, 0x151A8 + (0x18 * i)); + StatisticsEntries[i] = new PokedexSaveStatisticsEntry(data.Slice(0x151A8 + (PokedexSaveStatisticsEntry.SIZE * i), PokedexSaveStatisticsEntry.SIZE)); } public bool IsPokedexCompleted(PokedexType8a which) => (GlobalData.Flags & (which < PokedexType8a.Count ? (1 << (int)which) : 1)) != 0; diff --git a/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveGlobalData.cs b/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveGlobalData.cs index 9a9fa3c1a..9f979529a 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveGlobalData.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveGlobalData.cs @@ -1,4 +1,4 @@ -using System; +using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -6,13 +6,11 @@ namespace PKHeX.Core; /// /// Revision details for the Pokédex. /// -public sealed class PokedexSaveGlobalData +public sealed class PokedexSaveGlobalData(Memory raw) { - private readonly byte[] _data; - private readonly int Offset; + public const int SIZE = 0x10; - public PokedexSaveGlobalData(byte[] data, int offset) => (_data, Offset) = (data, offset); - private Span Data => _data.AsSpan(Offset); + private Span Data => raw.Span; public uint Flags { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); } public byte Field_04 { get => Data[4]; set => Data[4] = value; } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveLocalData.cs b/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveLocalData.cs index 9389b78ea..21a29c409 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveLocalData.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveLocalData.cs @@ -1,4 +1,4 @@ -using System; +using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -6,13 +6,10 @@ namespace PKHeX.Core; /// /// Local region (within Hisui) Pokédex structure. /// -public sealed class PokedexSaveLocalData +public sealed class PokedexSaveLocalData(Memory raw) { - private readonly byte[] _data; - private readonly int Offset; - - public PokedexSaveLocalData(byte[] data, int offset) => (_data, Offset) = (data, offset); - private Span Data => _data.AsSpan(Offset); + public const int SIZE = 0x10; + private Span Data => raw.Span; public ushort Field_00 { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, value); } public byte Field_02 { get => Data[2]; set => Data[2] = value; } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveResearchEntry.cs b/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveResearchEntry.cs index e6d3df248..a96e1da17 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveResearchEntry.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveResearchEntry.cs @@ -1,4 +1,4 @@ -using System; +using System; using static System.Buffers.Binary.BinaryPrimitives; using static PKHeX.Core.PokedexResearchTaskType8a; @@ -7,13 +7,10 @@ namespace PKHeX.Core; /// /// Per-species research logs used for Pokédex entries. /// -public sealed class PokedexSaveResearchEntry +public sealed class PokedexSaveResearchEntry(Memory raw) { - private readonly byte[] _data; - private readonly int Offset; - - public PokedexSaveResearchEntry(byte[] data, int offset) => (_data, Offset) = (data, offset); - private Span Data => _data.AsSpan(Offset); + private Span Data => raw.Span; + public const int SIZE = 0x58; public uint Flags { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); } public bool HasEverBeenUpdated { get => (Flags & (1u << 0)) != 0; set => Flags = (Flags & ~(1u << 0)) | ((value ? 1u : 0u) << 0); } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveStatisticsEntry.cs b/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveStatisticsEntry.cs index 30c97ce99..4532e623c 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveStatisticsEntry.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSaveStatisticsEntry.cs @@ -1,4 +1,4 @@ -using System; +using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -6,13 +6,11 @@ namespace PKHeX.Core; /// /// Per-species/form research logs used for Pokédex entries. /// -public sealed class PokedexSaveStatisticsEntry +public sealed class PokedexSaveStatisticsEntry(Memory raw) { - private readonly byte[] _data; - private readonly int Offset; + private Span Data => raw.Span; - public PokedexSaveStatisticsEntry(byte[] data, int offset) => (_data, Offset) = (data, offset); - private Span Data => _data.AsSpan(Offset); + public const int SIZE = 0x18; public uint Flags { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); } public bool HasMaximumStatistics { get => (Flags & (1u << 0)) != 0; set => Flags = (Flags & ~(1u << 0)) | ((value ? 1u : 0u) << 0); } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Box8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Box8.cs index c0c513f7b..44083542e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Box8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Box8.cs @@ -1,3 +1,9 @@ +using System; +using System.ComponentModel; + namespace PKHeX.Core; -public sealed class Box8(SaveFile sav, SCBlock block) : SaveBlock(sav, block.Data); +public sealed class Box8(SaveFile sav, SCBlock block) : SaveBlock(sav, block.Data) +{ + [Browsable(false)] public new Memory Raw => base.Raw; +} diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/BoxLayout8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/BoxLayout8.cs index 40077a155..ffe13b9e0 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/BoxLayout8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/BoxLayout8.cs @@ -9,7 +9,7 @@ public sealed class BoxLayout8(SAV8SWSH sav, SCBlock block) : SaveBlock SAV6.LongStringLength * box; - private Span GetBoxNameSpan(int box) => Data.AsSpan(GetBoxNameOffset(box), SAV6.LongStringLength); + private Span GetBoxNameSpan(int box) => Data.Slice(GetBoxNameOffset(box), SAV6.LongStringLength); public string GetBoxName(int box) => SAV.GetString(GetBoxNameSpan(box)); public void SetBoxName(int box, ReadOnlySpan value) => SAV.SetString(GetBoxNameSpan(box), value, StringMaxLength, StringConverterOption.ClearZero); diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Coordinates8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Coordinates8.cs index 4cdc9d575..951625a17 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Coordinates8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Coordinates8.cs @@ -9,21 +9,21 @@ namespace PKHeX.Core; public sealed class Coordinates8(SAV8SWSH sav, SCBlock block) : SaveBlock(sav, block.Data) { // Position - public float X { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x10)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x10), value); } - public float Z { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x14)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x14), value); } - public float Y { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x18)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x18), value); } + public float X { get => ReadSingleLittleEndian(Data.Slice(0x10)); set => WriteSingleLittleEndian(Data.Slice(0x10), value); } + public float Z { get => ReadSingleLittleEndian(Data.Slice(0x14)); set => WriteSingleLittleEndian(Data.Slice(0x14), value); } + public float Y { get => ReadSingleLittleEndian(Data.Slice(0x18)); set => WriteSingleLittleEndian(Data.Slice(0x18), value); } // Scale - public float SX { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x20)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x20), value); } - public float SZ { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x24)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x24), value); } - public float SY { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x28)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x28), value); } + public float SX { get => ReadSingleLittleEndian(Data.Slice(0x20)); set => WriteSingleLittleEndian(Data.Slice(0x20), value); } + public float SZ { get => ReadSingleLittleEndian(Data.Slice(0x24)); set => WriteSingleLittleEndian(Data.Slice(0x24), value); } + public float SY { get => ReadSingleLittleEndian(Data.Slice(0x28)); set => WriteSingleLittleEndian(Data.Slice(0x28), value); } // Rotation - public float RX { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x30)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x30), value); } - public float RZ { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x34)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x34), value); } - public float RY { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x38)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x38), value); } - public float RW { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x3C)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x3C), value); } + public float RX { get => ReadSingleLittleEndian(Data.Slice(0x30)); set => WriteSingleLittleEndian(Data.Slice(0x30), value); } + public float RZ { get => ReadSingleLittleEndian(Data.Slice(0x34)); set => WriteSingleLittleEndian(Data.Slice(0x34), value); } + public float RY { get => ReadSingleLittleEndian(Data.Slice(0x38)); set => WriteSingleLittleEndian(Data.Slice(0x38), value); } + public float RW { get => ReadSingleLittleEndian(Data.Slice(0x3C)); set => WriteSingleLittleEndian(Data.Slice(0x3C), value); } // Map - public ulong M { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x6000)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x6000), value); } + public ulong M { get => ReadUInt64LittleEndian(Data.Slice(0x6000)); set => WriteUInt64LittleEndian(Data.Slice(0x6000), value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Daycare8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Daycare8.cs index e6434fdf5..d6ee62307 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Daycare8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Daycare8.cs @@ -85,6 +85,8 @@ public sealed class Daycare8(SAV8SWSH sav, SCBlock block) : SaveBlock( _ => throw new ArgumentOutOfRangeException(nameof(daycare), daycare, "Expected 0/1"), }; - public ulong GetDaycareSeed(int daycare) => ReadUInt64LittleEndian(Data.AsSpan(GetDaycareMetadataOffset(daycare) + 6)); - public void SetDaycareSeed(int daycare, ulong value) => WriteUInt64LittleEndian(Data.AsSpan(GetDaycareMetadataOffset(daycare) + 6), value); + public ulong GetDaycareSeed(int daycare) => ReadUInt64LittleEndian(Data[(GetDaycareMetadataOffset(daycare) + 6)..]); + public void SetDaycareSeed(int daycare, ulong value) => WriteUInt64LittleEndian(Data[(GetDaycareMetadataOffset(daycare) + 6)..], value); + + public Memory this[int i] => Raw[GetDaycareSlotOffset(i >> 1, i & 1)..]; } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/FashionUnlock8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/FashionUnlock8.cs index e5b863726..83e150ce1 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/FashionUnlock8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/FashionUnlock8.cs @@ -18,8 +18,8 @@ public sealed class FashionUnlock8(SAV8SWSH sav, SCBlock block) : SaveBlock GetOwnedRegion(int region) => Data.AsSpan(region * SIZE_ENTRY, SIZE_ENTRY); - private Span GetNewRegion(int region) => Data.AsSpan((region + REGIONS) * SIZE_ENTRY, SIZE_ENTRY); + private Span GetOwnedRegion(int region) => Data.Slice(region * SIZE_ENTRY, SIZE_ENTRY); + private Span GetNewRegion(int region) => Data.Slice((region + REGIONS) * SIZE_ENTRY, SIZE_ENTRY); public bool[] GetArrayOwnedFlag(int region) => FlagUtil.GetBitFlagArray(GetOwnedRegion(region), SIZE_ENTRY * 8); public bool[] GetArrayNewFlag(int region) => FlagUtil.GetBitFlagArray(GetNewRegion(region), SIZE_ENTRY * 8); diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/FieldMoveModelSave8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/FieldMoveModelSave8.cs index 131187f7e..1d4ee5216 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/FieldMoveModelSave8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/FieldMoveModelSave8.cs @@ -1,13 +1,12 @@ -using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; public sealed class FieldMoveModelSave8(SAV8SWSH sav, SCBlock block) : SaveBlock(sav, block.Data) { - public int M { get => ReadUInt16LittleEndian(Data.AsSpan(0x00)); set => WriteUInt16LittleEndian(Data.AsSpan(0x00), (ushort)value); } - public float X { get => ReadSingleLittleEndian(Data.AsSpan(0x08)); set => WriteSingleLittleEndian(Data.AsSpan(0x08), value); } - public float Z { get => ReadSingleLittleEndian(Data.AsSpan(0x10)); set => WriteSingleLittleEndian(Data.AsSpan(0x10), value); } - public float Y { get => (int)ReadSingleLittleEndian(Data.AsSpan(0x18)); set => WriteSingleLittleEndian(Data.AsSpan(0x18), value); } - public float R { get => (int)ReadSingleLittleEndian(Data.AsSpan(0x20)); set => WriteSingleLittleEndian(Data.AsSpan(0x20), value); } + public int M { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, (ushort)value); } + public float X { get => ReadSingleLittleEndian(Data[0x08..]); set => WriteSingleLittleEndian(Data[0x08..], value); } + public float Z { get => ReadSingleLittleEndian(Data[0x10..]); set => WriteSingleLittleEndian(Data[0x10..], value); } + public float Y { get => (int)ReadSingleLittleEndian(Data[0x18..]); set => WriteSingleLittleEndian(Data[0x18..], value); } + public float R { get => (int)ReadSingleLittleEndian(Data[0x20..]); set => WriteSingleLittleEndian(Data[0x20..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Fused8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Fused8.cs index 121e189d9..2aea938cc 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Fused8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Fused8.cs @@ -14,7 +14,8 @@ public sealed class Fused8(SAV8SWSH sav, SCBlock block) : SaveBlock(sa return PokeCrypto.SIZE_8PARTY * slot; } - private Span GetSlotSpan(int index) => Data.AsSpan(GetFusedSlotOffset(index), PokeCrypto.SIZE_8STORED); + public Memory this[int i] => Raw.Slice(GetFusedSlotOffset(i), PokeCrypto.SIZE_8STORED); + private Span GetSlotSpan(int index) => Data.Slice(GetFusedSlotOffset(index), PokeCrypto.SIZE_8STORED); private PK8 GetStoredSlot(int index) => (PK8)SAV.GetStoredSlot(GetSlotSpan(index)); private void SetStoredSlot(PK8 pk, int index) => pk.EncryptedBoxData.CopyTo(GetSlotSpan(index)); diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/HallOfFameTime8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/HallOfFameTime8.cs index d2ac328b2..d035b50f2 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/HallOfFameTime8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/HallOfFameTime8.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.ComponentModel; using static System.Buffers.Binary.BinaryPrimitives; @@ -17,8 +17,8 @@ public sealed class HallOfFameTime8 : SaveBlock [Category(General), Description("Raw amount of seconds since 1970 (Unix Timestamp)")] public long TimeStamp { - get => ReadInt64LittleEndian(Data.AsSpan(Offset + 0)); - set => WriteInt64LittleEndian(Data.AsSpan(Offset), value); + get => ReadInt64LittleEndian(Data); + set => WriteInt64LittleEndian(Data, value); } [Category(General), Description("Date and Time (UTC) the player entered the Hall Of Fame")] diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Misc8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Misc8.cs index 1ead1012b..2abf847fb 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Misc8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Misc8.cs @@ -1,4 +1,3 @@ -using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -7,24 +6,24 @@ public sealed class Misc8(SAV8SWSH sav, SCBlock block) : SaveBlock(sav { public int Badges { - get => Data[Offset + 0x00]; - set => Data[Offset + 0x00] = (byte)value; + get => Data[0x00]; + set => Data[0x00] = (byte)value; } public uint Money { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x04)); + get => ReadUInt32LittleEndian(Data[0x04..]); set { if (value > 9999999) value = 9999999; - WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); + WriteUInt32LittleEndian(Data[0x04..], value); } } public int BP { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x11C)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x11C), (ushort)value); + get => ReadUInt16LittleEndian(Data[0x11C..]); + set => WriteUInt16LittleEndian(Data[0x11C..], (ushort)value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/MyStatus8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/MyStatus8.cs index a4097a883..99da4a64c 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/MyStatus8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/MyStatus8.cs @@ -10,7 +10,7 @@ public sealed class MyStatus8(SAV8SWSH sav, SCBlock block) : SaveBlock public string Number { - get => Encoding.ASCII.GetString(Data, 0x01, 3); + get => Encoding.ASCII.GetString(Data.Slice(1, 3)); set { for (int i = 0; i < 3; i++) @@ -21,119 +21,119 @@ public sealed class MyStatus8(SAV8SWSH sav, SCBlock block) : SaveBlock public ulong Skin // aka the base model { - get => ReadUInt64LittleEndian(Data.AsSpan(0x08)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x08), value); + get => ReadUInt64LittleEndian(Data[0x08..]); + set => WriteUInt64LittleEndian(Data[0x08..], value); } public ulong Hair { - get => ReadUInt64LittleEndian(Data.AsSpan(0x10)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x10), value); + get => ReadUInt64LittleEndian(Data[0x10..]); + set => WriteUInt64LittleEndian(Data[0x10..], value); } public ulong Brow { - get => ReadUInt64LittleEndian(Data.AsSpan(0x18)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x18), value); + get => ReadUInt64LittleEndian(Data[0x18..]); + set => WriteUInt64LittleEndian(Data[0x18..], value); } public ulong Lashes { - get => ReadUInt64LittleEndian(Data.AsSpan(0x20)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x20), value); + get => ReadUInt64LittleEndian(Data[0x20..]); + set => WriteUInt64LittleEndian(Data[0x20..], value); } public ulong Contacts { - get => ReadUInt64LittleEndian(Data.AsSpan(0x28)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x28), value); + get => ReadUInt64LittleEndian(Data[0x28..]); + set => WriteUInt64LittleEndian(Data[0x28..], value); } public ulong Lips { - get => ReadUInt64LittleEndian(Data.AsSpan(0x30)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x30), value); + get => ReadUInt64LittleEndian(Data[0x30..]); + set => WriteUInt64LittleEndian(Data[0x30..], value); } public ulong Glasses { - get => ReadUInt64LittleEndian(Data.AsSpan(0x38)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x38), value); + get => ReadUInt64LittleEndian(Data[0x38..]); + set => WriteUInt64LittleEndian(Data[0x38..], value); } public ulong Hat { - get => ReadUInt64LittleEndian(Data.AsSpan(0x40)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x40), value); + get => ReadUInt64LittleEndian(Data[0x40..]); + set => WriteUInt64LittleEndian(Data[0x40..], value); } public ulong Jacket { - get => ReadUInt64LittleEndian(Data.AsSpan(0x48)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x48), value); + get => ReadUInt64LittleEndian(Data[0x48..]); + set => WriteUInt64LittleEndian(Data[0x48..], value); } public ulong Top { - get => ReadUInt64LittleEndian(Data.AsSpan(0x50)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x50), value); + get => ReadUInt64LittleEndian(Data[0x50..]); + set => WriteUInt64LittleEndian(Data[0x50..], value); } public ulong Bag { - get => ReadUInt64LittleEndian(Data.AsSpan(0x58)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x58), value); + get => ReadUInt64LittleEndian(Data[0x58..]); + set => WriteUInt64LittleEndian(Data[0x58..], value); } public ulong Gloves { - get => ReadUInt64LittleEndian(Data.AsSpan(0x60)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x60), value); + get => ReadUInt64LittleEndian(Data[0x60..]); + set => WriteUInt64LittleEndian(Data[0x60..], value); } public ulong BottomOrDress { - get => ReadUInt64LittleEndian(Data.AsSpan(0x68)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x68), value); + get => ReadUInt64LittleEndian(Data[0x68..]); + set => WriteUInt64LittleEndian(Data[0x68..], value); } public ulong Sock { - get => ReadUInt64LittleEndian(Data.AsSpan(0x70)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x70), value); + get => ReadUInt64LittleEndian(Data[0x70..]); + set => WriteUInt64LittleEndian(Data[0x70..], value); } public ulong Shoe { - get => ReadUInt64LittleEndian(Data.AsSpan(0x78)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x78), value); + get => ReadUInt64LittleEndian(Data[0x78..]); + set => WriteUInt64LittleEndian(Data[0x78..], value); } // 80 - 87 public ulong MomSkin // aka the base model { - get => ReadUInt64LittleEndian(Data.AsSpan(0x88)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x88), value); + get => ReadUInt64LittleEndian(Data[0x88..]); + set => WriteUInt64LittleEndian(Data[0x88..], value); } // 8C - 9F public uint ID32 { - get => ReadUInt32LittleEndian(Data.AsSpan(0xA0)); - set => WriteUInt32LittleEndian(Data.AsSpan(0xA0), value); + get => ReadUInt32LittleEndian(Data[0xA0..]); + set => WriteUInt32LittleEndian(Data[0xA0..], value); } public ushort TID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(0xA0)); - set => WriteUInt16LittleEndian(Data.AsSpan(0xA0), value); + get => ReadUInt16LittleEndian(Data[0xA0..]); + set => WriteUInt16LittleEndian(Data[0xA0..], value); } public ushort SID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(0xA2)); - set => WriteUInt16LittleEndian(Data.AsSpan(0xA2), value); + get => ReadUInt16LittleEndian(Data[0xA2..]); + set => WriteUInt16LittleEndian(Data[0xA2..], value); } public byte Game @@ -151,12 +151,12 @@ public sealed class MyStatus8(SAV8SWSH sav, SCBlock block) : SaveBlock // A6 public int Language { - get => Data[Offset + 0xA7]; + get => Data[0xA7]; set { if (value == Language) return; - Data[Offset + 0xA7] = (byte) value; + Data[0xA7] = (byte) value; // For runtime language, the game shifts all languages above Language 6 (unused) down one. if (value >= 6) @@ -165,7 +165,7 @@ public sealed class MyStatus8(SAV8SWSH sav, SCBlock block) : SaveBlock } } - private Span OriginalTrainerTrash => Data.AsSpan(0xB0, 0x1A); + private Span OriginalTrainerTrash => Data.Slice(0xB0, 0x1A); public string OT { @@ -176,12 +176,12 @@ public sealed class MyStatus8(SAV8SWSH sav, SCBlock block) : SaveBlock // D0 public uint Watt { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0xD0)); + get => ReadUInt32LittleEndian(Data[0xD0..]); set { if (value > MaxWatt) value = MaxWatt; - WriteUInt32LittleEndian(Data.AsSpan(Offset + 0xD0), value); + WriteUInt32LittleEndian(Data[0xD0..], value); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/PlayTime8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/PlayTime8.cs index 2e3b0947a..1b372614f 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/PlayTime8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/PlayTime8.cs @@ -7,23 +7,23 @@ public sealed class PlayTime8(SAV8SWSH sav, SCBlock block) : SaveBlock { public int PlayedHours { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset), (ushort)value); + get => ReadUInt16LittleEndian(Data); + set => WriteUInt16LittleEndian(Data, (ushort)value); } public int PlayedMinutes { - get => Data[Offset + 2]; - set => Data[Offset + 2] = (byte)value; + get => Data[2]; + set => Data[2] = (byte)value; } public int PlayedSeconds { - get => Data[Offset + 3]; - set => Data[Offset + 3] = (byte)value; + get => Data[3]; + set => Data[3] = (byte)value; } - private uint LastSaved { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x4)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x4), value); } + private uint LastSaved { get => ReadUInt32LittleEndian(Data[0x4..]); set => WriteUInt32LittleEndian(Data[0x4..], value); } private int LastSavedYear { get => (int)(LastSaved & 0xFFF) + 1900; set => LastSaved = (LastSaved & 0xFFFFF000) | (uint)(value - 1900); } private int LastSavedMonth { get => (int)((LastSaved >> 12) & 0xF) + 1; set => LastSaved = (LastSaved & 0xFFFF0FFF) | (((uint)(value - 1) & 0xF) << 12); } private int LastSavedDay { get => (int)((LastSaved >> 16) & 0x1F); set => LastSaved = (LastSaved & 0xFFE0FFFF) | (((uint)value & 0x1F) << 16); } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/RaidSpawnList8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/RaidSpawnList8.cs index 191f75da0..337324804 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/RaidSpawnList8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/RaidSpawnList8.cs @@ -13,7 +13,7 @@ public sealed class RaidSpawnList8(SAV8SWSH sav, SCBlock block, int legal) : Sav public const int RaidCountLegal_R1 = 90; public const int RaidCountLegal_R2 = 86; - public RaidSpawnDetail GetRaid(int entry) => new(Data, entry * RaidSpawnDetail.SIZE); + public RaidSpawnDetail GetRaid(int entry) => new(Raw.Slice(entry * RaidSpawnDetail.SIZE, RaidSpawnDetail.SIZE)); public RaidSpawnDetail[] GetAllRaids() { @@ -56,48 +56,50 @@ public sealed class RaidSpawnList8(SAV8SWSH sav, SCBlock block, int legal) : Sav } } -public sealed class RaidSpawnDetail(byte[] Data, int Offset) +public sealed class RaidSpawnDetail(Memory raw) { public const int SIZE = 0x18; + private Span Data => raw.Span; + private const string General = nameof(General); private const string Derived = nameof(Derived); [Category(General), Description("FNV Hash for fetching the Raid data table (64bit)."), TypeConverter(typeof(TypeConverterU64))] public ulong Hash { - get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0)); - set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0), value); + get => ReadUInt64LittleEndian(Data); + set => WriteUInt64LittleEndian(Data, value); } [Category(General), Description("RNG Seed for generating the Raid's content (64bit)."), TypeConverter(typeof(TypeConverterU64))] public ulong Seed { - get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 8)); - set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 8), value); + get => ReadUInt64LittleEndian(Data[8..]); + set => WriteUInt64LittleEndian(Data[8..], value); } [Category(General), Description("Star Count for the Raid's content (0-4).")] public byte Stars { - get => Data[Offset + 0x10]; - set => Data[Offset + 0x10] = value; + get => Data[0x10]; + set => Data[0x10] = value; } [Category(General), Description("Random value which picks out the encounter from the Raid data table (1-100).")] public byte RandRoll { - get => Data[Offset + 0x11]; - set => Data[Offset + 0x11] = value; + get => Data[0x11]; + set => Data[0x11] = value; } [Category(General), Description("First set of Den Flags.")] public RaidType DenType { - get => (RaidType)Data[Offset + 0x12]; + get => (RaidType)Data[0x12]; set { - Data[Offset + 0x12] = (byte)value; + Data[0x12] = (byte)value; if (value == RaidType.Event) { IsEvent = true; @@ -112,8 +114,8 @@ public sealed class RaidSpawnDetail(byte[] Data, int Offset) [Category(General), Description("Second set of Den Flags.")] public byte Flags { - get => Data[Offset + 0x13]; - set => Data[Offset + 0x13] = value; + get => Data[0x13]; + set => Data[0x13] = value; } [Category(Derived), Description("Active Nest")] diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Record8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Record8.cs index 9ad2ebfe7..ff37a3509 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Record8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Record8.cs @@ -12,9 +12,9 @@ public sealed class Record8(SAV8SWSH sav, SCBlock block) : RecordBlock public override int GetRecord(int recordID) { - int ofs = Records.GetOffset(Offset, recordID); + int ofs = Records.GetOffset(recordID); if (recordID < RecordCount) - return ReadInt32LittleEndian(Data.AsSpan(ofs)); + return ReadInt32LittleEndian(Data[ofs..]); Trace.Fail(nameof(recordID)); return 0; } @@ -28,7 +28,7 @@ public sealed class Record8(SAV8SWSH sav, SCBlock block) : RecordBlock if (value > max) value = max; if (recordID < RecordCount) - WriteInt32LittleEndian(Data.AsSpan(ofs), value); + WriteInt32LittleEndian(Data[ofs..], value); else Trace.Fail(nameof(recordID)); } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/TitleScreen8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/TitleScreen8.cs index c496eb2de..448a44eef 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/TitleScreen8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/TitleScreen8.cs @@ -13,10 +13,17 @@ public sealed class TitleScreen8(SAV8SWSH sav, SCBlock block) : SaveBlock. /// public TitleScreen8Poke ViewPoke(int index) + { + int ofs = GetPokeOffset(index); + var raw = block.Data.AsMemory(ofs, TitleScreen8Poke.SIZE); + return new TitleScreen8Poke(raw); + } + + private static int GetPokeOffset(int index) { if ((uint)index >= 6) throw new ArgumentOutOfRangeException(nameof(index)); - return new TitleScreen8Poke(Data, Offset + 0x00 + (index * TitleScreen8Poke.SIZE)); + return index * TitleScreen8Poke.SIZE; } /// @@ -33,71 +40,73 @@ public sealed class TitleScreen8(SAV8SWSH sav, SCBlock block) : SaveBlock raw) : ISpeciesForm { public const int SIZE = 0x28; + private Span Data => raw.Span; + public ushort Species { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x00)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x00), value); + get => ReadUInt16LittleEndian(Data); + set => WriteUInt32LittleEndian(Data, value); } public byte Form { - get => Data[Offset + 0x04]; - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); + get => Data[0x04]; + set => WriteInt32LittleEndian(Data[0x04..], value); } public byte Gender { - get => (byte)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x08)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x08), value); + get => (byte)ReadInt32LittleEndian(Data[0x08..]); + set => WriteInt32LittleEndian(Data[0x08..], value); } public bool IsShiny { - get => Data[Offset + 0xC] != 0; - set => Data[Offset + 0xC] = value ? (byte)1 : (byte)0; + get => Data[0xC] != 0; + set => Data[0xC] = value ? (byte)1 : (byte)0; } public uint EncryptionConstant { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x10)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x10), value); + get => ReadUInt32LittleEndian(Data[0x10..]); + set => WriteUInt32LittleEndian(Data[0x10..], value); } public int FormArgument { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x14)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x14), value); + get => ReadInt32LittleEndian(Data[0x14..]); + set => WriteInt32LittleEndian(Data[0x14..], value); } public uint Unknown18 { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x18)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x18), value); + get => ReadUInt32LittleEndian(Data[0x18..]); + set => WriteUInt32LittleEndian(Data[0x18..], value); } public uint Unknown1C { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x1C)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x1C), value); + get => ReadUInt32LittleEndian(Data[0x1C..]); + set => WriteUInt32LittleEndian(Data[0x1C..], value); } public uint Unknown20 { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x20)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x20), value); + get => ReadUInt32LittleEndian(Data[0x20..]); + set => WriteUInt32LittleEndian(Data[0x20..], value); } public uint Unknown24 { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x24)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x24), value); + get => ReadUInt32LittleEndian(Data[0x24..]); + set => WriteUInt32LittleEndian(Data[0x24..], value); } - public void Clear() => Array.Clear(Data, Offset, SIZE); + public void Clear() => Data.Clear(); public void LoadFrom(PKM pk) { diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/TrainerCard8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/TrainerCard8.cs index 56c760a06..5d26aaf78 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/TrainerCard8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/TrainerCard8.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; public sealed class TrainerCard8(SAV8SWSH sav, SCBlock block) : SaveBlock(sav, block.Data) { - private Span OriginalTrainerTrash => Data.AsSpan(0x00, 0x1A); + private Span OriginalTrainerTrash => Data[..0x1A]; public string OT { @@ -23,20 +23,20 @@ public sealed class TrainerCard8(SAV8SWSH sav, SCBlock block) : SaveBlock ReadInt32LittleEndian(Data.AsSpan(0x1C)); - set => WriteInt32LittleEndian(Data.AsSpan(0x1C), value); + get => ReadInt32LittleEndian(Data[0x1C..]); + set => WriteInt32LittleEndian(Data[0x1C..], value); } public ushort PokeDexOwned { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x20)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x20), value); + get => ReadUInt16LittleEndian(Data[0x20..]); + set => WriteUInt16LittleEndian(Data[0x20..], value); } public ushort ShinyPokemonFound { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x22)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x22), value); + get => ReadUInt16LittleEndian(Data[0x22..]); + set => WriteUInt16LittleEndian(Data[0x22..], value); } public byte Game @@ -53,20 +53,20 @@ public sealed class TrainerCard8(SAV8SWSH sav, SCBlock block) : SaveBlock ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x26)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x26), value); + get => ReadUInt16LittleEndian(Data[0x26..]); + set => WriteUInt16LittleEndian(Data[0x26..], value); } public const int RotoRallyScoreMax = 99_999; public int RotoRallyScore { - get => ReadInt32LittleEndian(Data.AsSpan(0x28)); + get => ReadInt32LittleEndian(Data[0x28..]); set { if (value > RotoRallyScoreMax) value = RotoRallyScoreMax; - WriteInt32LittleEndian(Data.AsSpan(0x28), value); + WriteInt32LittleEndian(Data[0x28..], value); // set to the other block since it doesn't have an accessor SAV.SetValue(SaveBlockAccessor8SWSH.KRotoRally, (uint)value); } @@ -76,31 +76,31 @@ public sealed class TrainerCard8(SAV8SWSH sav, SCBlock block) : SaveBlock ReadInt32LittleEndian(Data.AsSpan(0x2C)); + get => ReadInt32LittleEndian(Data[0x2C..]); set { if (value > MaxPokemonCaught) value = MaxPokemonCaught; - WriteInt32LittleEndian(Data.AsSpan(0x2C), value); + WriteInt32LittleEndian(Data[0x2C..], value); } } public bool PokeDexComplete { - get => Data[Offset + 0x30] == 1; - set => Data[Offset + 0x30] = value ? (byte)1 : (byte)0; + get => Data[0x30] == 1; + set => Data[0x30] = value ? (byte)1 : (byte)0; } public bool ArmorDexComplete { - get => Data[Offset + 0x1B4] == 1; - set => Data[Offset + 0x1B4] = value ? (byte)1 : (byte)0; + get => Data[0x1B4] == 1; + set => Data[0x1B4] = value ? (byte)1 : (byte)0; } public bool CrownDexComplete { - get => Data[Offset + 0x1B5] == 1; - set => Data[Offset + 0x1B5] = value ? (byte)1 : (byte)0; + get => Data[0x1B5] == 1; + set => Data[0x1B5] = value ? (byte)1 : (byte)0; } public byte Gender @@ -111,7 +111,7 @@ public sealed class TrainerCard8(SAV8SWSH sav, SCBlock block) : SaveBlock Encoding.ASCII.GetString(Data, 0x39, 3); + get => Encoding.ASCII.GetString(Data.Slice(0x39, 3)); set { for (int i = 0; i < 3; i++) @@ -122,98 +122,98 @@ public sealed class TrainerCard8(SAV8SWSH sav, SCBlock block) : SaveBlock ReadUInt64LittleEndian(Data.AsSpan(0x40)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x40), value); + get => ReadUInt64LittleEndian(Data[0x40..]); + set => WriteUInt64LittleEndian(Data[0x40..], value); } public ulong Hair { - get => ReadUInt64LittleEndian(Data.AsSpan(0x48)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x48), value); + get => ReadUInt64LittleEndian(Data[0x48..]); + set => WriteUInt64LittleEndian(Data[0x48..], value); } public ulong Brow { - get => ReadUInt64LittleEndian(Data.AsSpan(0x50)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x50), value); + get => ReadUInt64LittleEndian(Data[0x50..]); + set => WriteUInt64LittleEndian(Data[0x50..], value); } public ulong Lashes { - get => ReadUInt64LittleEndian(Data.AsSpan(0x58)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x58), value); + get => ReadUInt64LittleEndian(Data[0x58..]); + set => WriteUInt64LittleEndian(Data[0x58..], value); } public ulong Contacts { - get => ReadUInt64LittleEndian(Data.AsSpan(0x60)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x60), value); + get => ReadUInt64LittleEndian(Data[0x60..]); + set => WriteUInt64LittleEndian(Data[0x60..], value); } public ulong Lips { - get => ReadUInt64LittleEndian(Data.AsSpan(0x68)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x68), value); + get => ReadUInt64LittleEndian(Data[0x68..]); + set => WriteUInt64LittleEndian(Data[0x68..], value); } public ulong Glasses { - get => ReadUInt64LittleEndian(Data.AsSpan(0x70)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x70), value); + get => ReadUInt64LittleEndian(Data[0x70..]); + set => WriteUInt64LittleEndian(Data[0x70..], value); } public ulong Hat { - get => ReadUInt64LittleEndian(Data.AsSpan(0x78)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x78), value); + get => ReadUInt64LittleEndian(Data[0x78..]); + set => WriteUInt64LittleEndian(Data[0x78..], value); } public ulong Jacket { - get => ReadUInt64LittleEndian(Data.AsSpan(0x80)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x80), value); + get => ReadUInt64LittleEndian(Data[0x80..]); + set => WriteUInt64LittleEndian(Data[0x80..], value); } public ulong Top { - get => ReadUInt64LittleEndian(Data.AsSpan(0x88)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x88), value); + get => ReadUInt64LittleEndian(Data[0x88..]); + set => WriteUInt64LittleEndian(Data[0x88..], value); } public ulong Bag { - get => ReadUInt64LittleEndian(Data.AsSpan(0x90)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x90), value); + get => ReadUInt64LittleEndian(Data[0x90..]); + set => WriteUInt64LittleEndian(Data[0x90..], value); } public ulong Gloves { - get => ReadUInt64LittleEndian(Data.AsSpan(0x98)); - set => WriteUInt64LittleEndian(Data.AsSpan(0x98), value); + get => ReadUInt64LittleEndian(Data[0x98..]); + set => WriteUInt64LittleEndian(Data[0x98..], value); } public ulong BottomOrDress { - get => ReadUInt64LittleEndian(Data.AsSpan(0xA0)); - set => WriteUInt64LittleEndian(Data.AsSpan(0xA0), value); + get => ReadUInt64LittleEndian(Data[0xA0..]); + set => WriteUInt64LittleEndian(Data[0xA0..], value); } public ulong Sock { - get => ReadUInt64LittleEndian(Data.AsSpan(0xA8)); - set => WriteUInt64LittleEndian(Data.AsSpan(0xA8), value); + get => ReadUInt64LittleEndian(Data[0xA8..]); + set => WriteUInt64LittleEndian(Data[0xA8..], value); } public ulong Shoe { - get => ReadUInt64LittleEndian(Data.AsSpan(0xB0)); - set => WriteUInt64LittleEndian(Data.AsSpan(0xB0), value); + get => ReadUInt64LittleEndian(Data[0xB0..]); + set => WriteUInt64LittleEndian(Data[0xB0..], value); } public ulong MomSkin // aka the base model { - get => ReadUInt64LittleEndian(Data.AsSpan(0xC0)); - set => WriteUInt64LittleEndian(Data.AsSpan(0xC0), value); + get => ReadUInt64LittleEndian(Data[0xC0..]); + set => WriteUInt64LittleEndian(Data[0xC0..], value); } // Trainer Card Pokemon @@ -229,9 +229,16 @@ public sealed class TrainerCard8(SAV8SWSH sav, SCBlock block) : SaveBlock public TrainerCard8Poke ViewPoke(int index) { - if ((uint) index >= 6) + var ofs = GetPokeOffset(index); + var raw = block.Data.AsMemory(ofs, TrainerCard8Poke.SIZE); + return new TrainerCard8Poke(raw); + } + + public static int GetPokeOffset(int index) + { + if ((uint)index >= 6) throw new ArgumentOutOfRangeException(nameof(index)); - return new TrainerCard8Poke(Data, Offset + 0xC8 + (index * TrainerCard8Poke.SIZE)); + return 0xC8 + (index * TrainerCard8Poke.SIZE); } /// @@ -249,77 +256,51 @@ public sealed class TrainerCard8(SAV8SWSH sav, SCBlock block) : SaveBlock ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x170)); - set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x170), value); + get => ReadUInt16LittleEndian(Data[0x170..]); + set => WriteUInt16LittleEndian(Data[0x170..], value); } public byte StartedMonth { - get => Data[Offset + 0x172]; - set => Data[Offset + 0x172] = value; + get => Data[0x172]; + set => Data[0x172] = value; } public byte StartedDay { - get => Data[Offset + 0x173]; - set => Data[Offset + 0x173] = value; + get => Data[0x173]; + set => Data[0x173] = value; } public uint TimestampPrinted { // should this be an unsigned long? - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x1A8)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x1A8), value); + get => ReadUInt32LittleEndian(Data[0x1A8..]); + set => WriteUInt32LittleEndian(Data[0x1A8..], value); } } -public sealed class TrainerCard8Poke(byte[] Data, int Offset) : ISpeciesForm +public sealed class TrainerCard8Poke(Memory raw) : ISpeciesForm { public const int SIZE = 0x1C; - public ushort Species - { - get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x00)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x00), value); - } + private Span Data => raw.Span; - public byte Form - { - get => Data[Offset + 0x04]; - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); - } - - public byte Gender - { - get => (byte)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x08)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x08), value); - } + public ushort Species { get => ReadUInt16LittleEndian(Data); set => WriteInt32LittleEndian(Data, value); } + public byte Form { get => Data[0x04]; set => WriteInt32LittleEndian(Data[0x04..], value); } + public byte Gender { get => Data[0x08]; set => WriteInt32LittleEndian(Data[0x08..], value); } public bool IsShiny { - get => Data[Offset + 0xC] != 0; - set => Data[Offset + 0xC] = value ? (byte)1 : (byte)0; + get => Data[0xC] != 0; + set => Data[0xC] = value ? (byte)1 : (byte)0; } - public uint EncryptionConstant - { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x10)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x10), value); - } + public uint EncryptionConstant { get => ReadUInt32LittleEndian(Data[0x10..]); set => WriteUInt32LittleEndian(Data[0x10..], value); } + public uint Unknown { get => ReadUInt32LittleEndian(Data[0x14..]); set => WriteUInt32LittleEndian(Data[0x14..], value); } + public int FormArgument { get => ReadInt32LittleEndian(Data[0x18..]); set => WriteInt32LittleEndian(Data[0x18..], value); } - public uint Unknown - { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x14)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x14), value); - } - - public int FormArgument - { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x18)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x18), value); - } - - public void Clear() => Array.Clear(Data, Offset, SIZE); + public void Clear() => Data.Clear(); public void LoadFrom(PKM pk) { diff --git a/PKHeX.Core/Saves/Substructures/Gen9/BlueberryClubRoom9.cs b/PKHeX.Core/Saves/Substructures/Gen9/BlueberryClubRoom9.cs index d1cbd59c3..0f3e52fdb 100644 --- a/PKHeX.Core/Saves/Substructures/Gen9/BlueberryClubRoom9.cs +++ b/PKHeX.Core/Saves/Substructures/Gen9/BlueberryClubRoom9.cs @@ -4,11 +4,9 @@ namespace PKHeX.Core; public sealed class BlueberryClubRoom9(SAV9SV sav, SCBlock block) : SaveBlock(sav, block.Data) { - private Span Span => Data.AsSpan(); - public BlueberrySupportBoard9 SupportBoard => new(block.Data.AsMemory(0, 0x38)); - public BlueberryClubRoomStyle9 CurrentStyle { get => (BlueberryClubRoomStyle9)Span[0xE6C]; set => Span[0xE6C] = (byte)value; } + public BlueberryClubRoomStyle9 CurrentStyle { get => (BlueberryClubRoomStyle9)Data[0xE6C]; set => Data[0xE6C] = (byte)value; } } public sealed class BlueberrySupportBoard9(Memory Data) diff --git a/PKHeX.Core/Saves/Substructures/Gen9/BlueberryQuestRecord9.cs b/PKHeX.Core/Saves/Substructures/Gen9/BlueberryQuestRecord9.cs index ba6b35d9b..0fc4336f8 100644 --- a/PKHeX.Core/Saves/Substructures/Gen9/BlueberryQuestRecord9.cs +++ b/PKHeX.Core/Saves/Substructures/Gen9/BlueberryQuestRecord9.cs @@ -1,12 +1,9 @@ -using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; public sealed class BlueberryQuestRecord9(SAV9SV sav, SCBlock block) : SaveBlock(sav, block.Data) { - private Span Span => Data.AsSpan(); - - public uint QuestsDoneSolo { get => ReadUInt32LittleEndian(Span[0x188..]); set => WriteUInt32LittleEndian(Span[0x188..], value); } - public uint QuestsDoneGroup { get => ReadUInt32LittleEndian(Span[0x18C..]); set => WriteUInt32LittleEndian(Span[0x18C..], value); } + public uint QuestsDoneSolo { get => ReadUInt32LittleEndian(Data[0x188..]); set => WriteUInt32LittleEndian(Data[0x188..], value); } + public uint QuestsDoneGroup { get => ReadUInt32LittleEndian(Data[0x18C..]); set => WriteUInt32LittleEndian(Data[0x18C..], value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen9/BoxLayout9.cs b/PKHeX.Core/Saves/Substructures/Gen9/BoxLayout9.cs index a44bb5997..fb4e421ab 100644 --- a/PKHeX.Core/Saves/Substructures/Gen9/BoxLayout9.cs +++ b/PKHeX.Core/Saves/Substructures/Gen9/BoxLayout9.cs @@ -9,13 +9,13 @@ public sealed class BoxLayout9(SAV9SV sav, SCBlock block) : SaveBlock(sa private const int StringMaxLength = SAV6.LongStringLength / 2; private static int GetBoxNameOffset(int box) => SAV6.LongStringLength * box; - private Span GetBoxNameSpan(int box) => Data.AsSpan(GetBoxNameOffset(box), SAV6.LongStringLength); + private Span GetBoxNameSpan(int box) => Data.Slice(GetBoxNameOffset(box), SAV6.LongStringLength); public string GetBoxName(int box) { var span = GetBoxNameSpan(box); if (System.Buffers.Binary.BinaryPrimitives.ReadUInt16LittleEndian(span) == 0) - return $"Box {box + 1}"; + return BoxDetailNameExtensions.GetDefaultBoxName(box); return SAV.GetString(span); } diff --git a/PKHeX.Core/Saves/Substructures/Gen9/ConfigCamera9.cs b/PKHeX.Core/Saves/Substructures/Gen9/ConfigCamera9.cs index 02f65b438..cf1ac522c 100644 --- a/PKHeX.Core/Saves/Substructures/Gen9/ConfigCamera9.cs +++ b/PKHeX.Core/Saves/Substructures/Gen9/ConfigCamera9.cs @@ -1,5 +1,4 @@ -using System; -using System.Buffers.Binary; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -15,8 +14,8 @@ public sealed class ConfigCamera9(SAV9SV sav, SCBlock block) : SaveBlock public int ConfigValue { - get => BinaryPrimitives.ReadInt32LittleEndian(Data.AsSpan(Offset)); - set => BinaryPrimitives.WriteInt32LittleEndian(Data.AsSpan(Offset), value); + get => ReadInt32LittleEndian(Data); + set => WriteInt32LittleEndian(Data, value); } private const int DefaultValue = 0x00000002; diff --git a/PKHeX.Core/Saves/Substructures/Gen9/ConfigSave9.cs b/PKHeX.Core/Saves/Substructures/Gen9/ConfigSave9.cs index 2e97d3229..0d7011a09 100644 --- a/PKHeX.Core/Saves/Substructures/Gen9/ConfigSave9.cs +++ b/PKHeX.Core/Saves/Substructures/Gen9/ConfigSave9.cs @@ -1,5 +1,4 @@ -using System; -using System.Buffers.Binary; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -29,8 +28,8 @@ public sealed class ConfigSave9(SAV9SV sav, SCBlock block) : SaveBlock(s public int ConfigValue { - get => BinaryPrimitives.ReadInt32LittleEndian(Data.AsSpan(Offset)); - set => BinaryPrimitives.WriteInt32LittleEndian(Data.AsSpan(Offset), value); + get => ReadInt32LittleEndian(Data); + set => WriteInt32LittleEndian(Data, value); } private const int DefaultValue = 0x002AAA05; diff --git a/PKHeX.Core/Saves/Substructures/Gen9/FixedSpawnList9.cs b/PKHeX.Core/Saves/Substructures/Gen9/FixedSpawnList9.cs index d7698fef3..f38aadc96 100644 --- a/PKHeX.Core/Saves/Substructures/Gen9/FixedSpawnList9.cs +++ b/PKHeX.Core/Saves/Substructures/Gen9/FixedSpawnList9.cs @@ -10,7 +10,7 @@ public sealed class FixedSpawnList9(SAV9SV sav, SCBlock block) : SaveBlock new(Data, entry * FixedSpawnDetail.SIZE); + public FixedSpawnDetail GetSpawn(int entry) => new(Raw.Slice(entry * FixedSpawnDetail.SIZE, FixedSpawnDetail.SIZE)); public FixedSpawnDetail[] Spawns => GetAllSpawns(); @@ -42,46 +42,48 @@ public sealed class FixedSpawnList9(SAV9SV sav, SCBlock block) : SaveBlock raw) { public const int SIZE = 0x170; + private Span Data => raw.Span; + private const string General = nameof(General); private const string Misc = nameof(Misc); [Category(Misc), Description("Unknown Hash.")] public ulong Hash { - get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x00)); - set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x00), value); + get => ReadUInt64LittleEndian(Data); + set => WriteUInt64LittleEndian(Data, value); } [Category(General), Description("Indicates if this entry is available on the overworld.")] public bool IsEnabled { - get => Data[Offset + 0x08] == 1; - set => Data[Offset + 0x08] = value ? (byte)1 : (byte)0; + get => Data[0x08] == 1; + set => Data[0x08] = value ? (byte)1 : (byte)0; } [Category(General), Description("Encrypted Entity data.")] public PK9 Entity { - get => new(Data.AsSpan(Offset + 0x09, PokeCrypto.SIZE_9PARTY).ToArray()); - set => value.EncryptedPartyData.CopyTo(Data, Offset + 0x09); + get => new(Data.Slice(0x09, PokeCrypto.SIZE_9PARTY).ToArray()); + set => value.EncryptedPartyData.CopyTo(Data.Slice(0x9, PokeCrypto.SIZE_9PARTY)); } // 7 bytes of padding, for alignment of next field. [Category(Misc), Description("time_t (64 bit) of when the encounter was last regenerated. Usually just a date (no time component).")] public ulong Seconds { - get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x168)); - set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x168), value); + get => ReadUInt64LittleEndian(Data[0x168..]); + set => WriteUInt64LittleEndian(Data[0x168..], value); } public override string ToString() { var pk = Entity; - var timestamp = new Epoch1970Value(Data.AsMemory(Offset + 0x168)).DisplayValue; + var timestamp = new Epoch1970Value(raw[0x168..]).DisplayValue; var state = IsEnabled ? "X" : " "; return $"{timestamp} {state} {Hash:X16} {pk.Species:0000} - {pk.Nickname} ({pk.CurrentLevel})"; } diff --git a/PKHeX.Core/Saves/Substructures/Gen9/MyItem9.cs b/PKHeX.Core/Saves/Substructures/Gen9/MyItem9.cs index 8c22ee729..658545a5a 100644 --- a/PKHeX.Core/Saves/Substructures/Gen9/MyItem9.cs +++ b/PKHeX.Core/Saves/Substructures/Gen9/MyItem9.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; namespace PKHeX.Core; @@ -14,7 +13,7 @@ public sealed class MyItem9(SAV9SV sav, SCBlock block) : MyItem(sav, block.Data) public int GetItemQuantity(ushort itemIndex) { var ofs = InventoryPouch9.GetItemOffset(itemIndex); - var span = Data.AsSpan(ofs, InventoryItem9.SIZE); + var span = Data.Slice(ofs, InventoryItem9.SIZE); var item = InventoryItem9.Read(itemIndex, span); return item.Count; } @@ -22,7 +21,7 @@ public sealed class MyItem9(SAV9SV sav, SCBlock block) : MyItem(sav, block.Data) public void SetItemQuantity(ushort itemIndex, int quantity) { var ofs = InventoryPouch9.GetItemOffset(itemIndex); - var span = Data.AsSpan(ofs, InventoryItem9.SIZE); + var span = Data.Slice(ofs, InventoryItem9.SIZE); var item = InventoryItem9.Read(itemIndex, span); item.Count = quantity; item.Pouch = GetPouchIndex(GetType(itemIndex)); diff --git a/PKHeX.Core/Saves/Substructures/Gen9/MyStatus9.cs b/PKHeX.Core/Saves/Substructures/Gen9/MyStatus9.cs index 36169d57a..942becc96 100644 --- a/PKHeX.Core/Saves/Substructures/Gen9/MyStatus9.cs +++ b/PKHeX.Core/Saves/Substructures/Gen9/MyStatus9.cs @@ -13,14 +13,14 @@ public sealed class MyStatus9(SAV9SV sav, SCBlock block) : SaveBlock(sav public ushort TID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(0x00)); - set => WriteUInt16LittleEndian(Data.AsSpan(0x00), value); + get => ReadUInt16LittleEndian(Data); + set => WriteUInt16LittleEndian(Data, value); } public ushort SID16 { - get => ReadUInt16LittleEndian(Data.AsSpan(0x02)); - set => WriteUInt16LittleEndian(Data.AsSpan(0x02), value); + get => ReadUInt16LittleEndian(Data[0x02..]); + set => WriteUInt16LittleEndian(Data[0x02..], value); } public byte Game @@ -38,12 +38,12 @@ public sealed class MyStatus9(SAV9SV sav, SCBlock block) : SaveBlock(sav // A6 public int Language { - get => Data[Offset + 0x07]; + get => Data[0x07]; set { if (value == Language) return; - Data[Offset + 0x07] = (byte)value; + Data[0x07] = (byte)value; // For runtime language, the game has different indexes (not even shifted like previous games, just different) var runtimeLanguage = GetRuntimeLanguage((LanguageID)value); @@ -78,7 +78,7 @@ public sealed class MyStatus9(SAV9SV sav, SCBlock block) : SaveBlock(sav ChineseT = 8, } - private Span OriginalTrainerTrash => Data.AsSpan(0x10, 0x1A); + private Span OriginalTrainerTrash => Data.Slice(0x10, 0x1A); public string OT { diff --git a/PKHeX.Core/Saves/Substructures/Gen9/PlayTime9.cs b/PKHeX.Core/Saves/Substructures/Gen9/PlayTime9.cs index f384daf67..fe9603580 100644 --- a/PKHeX.Core/Saves/Substructures/Gen9/PlayTime9.cs +++ b/PKHeX.Core/Saves/Substructures/Gen9/PlayTime9.cs @@ -7,19 +7,19 @@ public sealed class PlayTime9(SAV9SV sav, SCBlock block) : SaveBlock(sav { public int PlayedHours { - get => ReadInt32LittleEndian(Data.AsSpan(Offset)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset), (ushort)value); + get => ReadInt32LittleEndian(Data); + set => WriteInt32LittleEndian(Data, (ushort)value); } public int PlayedMinutes { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 4)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 4), (ushort)value); + get => ReadInt32LittleEndian(Data[4..]); + set => WriteInt32LittleEndian(Data[4..], (ushort)value); } public int PlayedSeconds { - get => ReadInt32LittleEndian(Data.AsSpan(Offset + 8)); - set => WriteInt32LittleEndian(Data.AsSpan(Offset + 8), (ushort)value); + get => ReadInt32LittleEndian(Data[8..]); + set => WriteInt32LittleEndian(Data[8..], (ushort)value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen9/PlayerAppearance9.cs b/PKHeX.Core/Saves/Substructures/Gen9/PlayerAppearance9.cs index e255865e9..0cf3b8619 100644 --- a/PKHeX.Core/Saves/Substructures/Gen9/PlayerAppearance9.cs +++ b/PKHeX.Core/Saves/Substructures/Gen9/PlayerAppearance9.cs @@ -10,18 +10,18 @@ namespace PKHeX.Core; [TypeConverter(typeof(ExpandableObjectConverter))] public sealed class PlayerAppearance9(SAV9SV sav, SCBlock block) : SaveBlock(sav, block.Data) { - public ulong SkinColor { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x00)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x00), value); } - public ulong LipColor { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x08)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x08), value); } - public ulong ColorContacts { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x10)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x10), value); } - public ulong EyeShape { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x18)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x18), value); } - public ulong EyebrowColor { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x20)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x20), value); } - public ulong EyebrowShape { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x28)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x28), value); } - public ulong EyebrowVolume { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x30)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x30), value); } - public ulong EyelashColor { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x38)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x38), value); } - public ulong EyelashShape { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x40)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x40), value); } - public ulong EyelashVolume { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x48)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x48), value); } - public ulong Mouth { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x50)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x50), value); } - public ulong BeautySpot { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x58)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x58), value); } - public ulong Freckles { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x60)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x60), value); } - public ulong HairColor { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x68)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x68), value); } + public ulong SkinColor { get => ReadUInt64LittleEndian(Data.Slice(0x00)); set => WriteUInt64LittleEndian(Data.Slice(0x00), value); } + public ulong LipColor { get => ReadUInt64LittleEndian(Data.Slice(0x08)); set => WriteUInt64LittleEndian(Data.Slice(0x08), value); } + public ulong ColorContacts { get => ReadUInt64LittleEndian(Data.Slice(0x10)); set => WriteUInt64LittleEndian(Data.Slice(0x10), value); } + public ulong EyeShape { get => ReadUInt64LittleEndian(Data.Slice(0x18)); set => WriteUInt64LittleEndian(Data.Slice(0x18), value); } + public ulong EyebrowColor { get => ReadUInt64LittleEndian(Data.Slice(0x20)); set => WriteUInt64LittleEndian(Data.Slice(0x20), value); } + public ulong EyebrowShape { get => ReadUInt64LittleEndian(Data.Slice(0x28)); set => WriteUInt64LittleEndian(Data.Slice(0x28), value); } + public ulong EyebrowVolume { get => ReadUInt64LittleEndian(Data.Slice(0x30)); set => WriteUInt64LittleEndian(Data.Slice(0x30), value); } + public ulong EyelashColor { get => ReadUInt64LittleEndian(Data.Slice(0x38)); set => WriteUInt64LittleEndian(Data.Slice(0x38), value); } + public ulong EyelashShape { get => ReadUInt64LittleEndian(Data.Slice(0x40)); set => WriteUInt64LittleEndian(Data.Slice(0x40), value); } + public ulong EyelashVolume { get => ReadUInt64LittleEndian(Data.Slice(0x48)); set => WriteUInt64LittleEndian(Data.Slice(0x48), value); } + public ulong Mouth { get => ReadUInt64LittleEndian(Data.Slice(0x50)); set => WriteUInt64LittleEndian(Data.Slice(0x50), value); } + public ulong BeautySpot { get => ReadUInt64LittleEndian(Data.Slice(0x58)); set => WriteUInt64LittleEndian(Data.Slice(0x58), value); } + public ulong Freckles { get => ReadUInt64LittleEndian(Data.Slice(0x60)); set => WriteUInt64LittleEndian(Data.Slice(0x60), value); } + public ulong HairColor { get => ReadUInt64LittleEndian(Data.Slice(0x68)); set => WriteUInt64LittleEndian(Data.Slice(0x68), value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen9/PlayerFashion9.cs b/PKHeX.Core/Saves/Substructures/Gen9/PlayerFashion9.cs index 8c5c8810b..eb6fc5ea3 100644 --- a/PKHeX.Core/Saves/Substructures/Gen9/PlayerFashion9.cs +++ b/PKHeX.Core/Saves/Substructures/Gen9/PlayerFashion9.cs @@ -10,15 +10,15 @@ namespace PKHeX.Core; [TypeConverter(typeof(ExpandableObjectConverter))] public sealed class PlayerFashion9(SAV9SV sav, SCBlock block) : SaveBlock(sav, block.Data) { - public ulong Base { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x00)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x00), value); } - public ulong Accessory { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x08)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x08), value); } - public ulong Bag { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x10)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x10), value); } - public ulong Eyewear { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x18)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x18), value); } - public ulong Footwear { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x20)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x20), value); } - public ulong Gloves { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x28)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x28), value); } - public ulong Headwear { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x30)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x30), value); } - public ulong Hairstyle { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x38)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x38), value); } - public ulong Legwear { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x40)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x40), value); } - public ulong Clothing { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x48)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x48), value); } - public ulong Face { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x50)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x50), value); } + public ulong Base { get => ReadUInt64LittleEndian(Data.Slice(0x00)); set => WriteUInt64LittleEndian(Data.Slice(0x00), value); } + public ulong Accessory { get => ReadUInt64LittleEndian(Data.Slice(0x08)); set => WriteUInt64LittleEndian(Data.Slice(0x08), value); } + public ulong Bag { get => ReadUInt64LittleEndian(Data.Slice(0x10)); set => WriteUInt64LittleEndian(Data.Slice(0x10), value); } + public ulong Eyewear { get => ReadUInt64LittleEndian(Data.Slice(0x18)); set => WriteUInt64LittleEndian(Data.Slice(0x18), value); } + public ulong Footwear { get => ReadUInt64LittleEndian(Data.Slice(0x20)); set => WriteUInt64LittleEndian(Data.Slice(0x20), value); } + public ulong Gloves { get => ReadUInt64LittleEndian(Data.Slice(0x28)); set => WriteUInt64LittleEndian(Data.Slice(0x28), value); } + public ulong Headwear { get => ReadUInt64LittleEndian(Data.Slice(0x30)); set => WriteUInt64LittleEndian(Data.Slice(0x30), value); } + public ulong Hairstyle { get => ReadUInt64LittleEndian(Data.Slice(0x38)); set => WriteUInt64LittleEndian(Data.Slice(0x38), value); } + public ulong Legwear { get => ReadUInt64LittleEndian(Data.Slice(0x40)); set => WriteUInt64LittleEndian(Data.Slice(0x40), value); } + public ulong Clothing { get => ReadUInt64LittleEndian(Data.Slice(0x48)); set => WriteUInt64LittleEndian(Data.Slice(0x48), value); } + public ulong Face { get => ReadUInt64LittleEndian(Data.Slice(0x50)); set => WriteUInt64LittleEndian(Data.Slice(0x50), value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen9/RaidSevenStar9.cs b/PKHeX.Core/Saves/Substructures/Gen9/RaidSevenStar9.cs index d2c2c0cf5..e396299c7 100644 --- a/PKHeX.Core/Saves/Substructures/Gen9/RaidSevenStar9.cs +++ b/PKHeX.Core/Saves/Substructures/Gen9/RaidSevenStar9.cs @@ -64,7 +64,7 @@ public sealed class RaidSevenStarCaptured9(SAV9SV sav, SCBlock block) : SaveBloc { public readonly int CountAll = block.Data.Length / SevenStarRaidCapturedDetail.SIZE; - public SevenStarRaidCapturedDetail GetRaid(int entry) => new(Data, 0x00 + (entry * SevenStarRaidCapturedDetail.SIZE)); + public SevenStarRaidCapturedDetail GetRaid(int entry) => new(Raw.Slice((entry * SevenStarRaidCapturedDetail.SIZE), SevenStarRaidCapturedDetail.SIZE)); public SevenStarRaidCapturedDetail[] GetAllRaids() { @@ -75,26 +75,28 @@ public sealed class RaidSevenStarCaptured9(SAV9SV sav, SCBlock block) : SaveBloc } } -public sealed class SevenStarRaidCapturedDetail(byte[] Data, int Offset) +public sealed class SevenStarRaidCapturedDetail(Memory raw) { public const int SIZE = 0x08; + private Span Data => raw.Span; + public uint Identifier { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x00)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x00), value); + get => ReadUInt32LittleEndian(Data); + set => WriteUInt32LittleEndian(Data, value); } public bool Captured { - get => Data[Offset + 0x04] == 1; - set => Data[Offset + 0x04] = (byte)(value ? 1 : 0); + get => Data[0x04] == 1; + set => Data[0x04] = (byte)(value ? 1 : 0); } public bool Defeated { - get => Data[Offset + 0x05] == 1; - set => Data[Offset + 0x05] = (byte)(value ? 1 : 0); + get => Data[0x05] == 1; + set => Data[0x05] = (byte)(value ? 1 : 0); } // 0x06 - 0x07 padding @@ -104,8 +106,9 @@ public sealed class RaidSevenStarDefeated9(SAV9SV sav, SCBlock block) : SaveBloc { // Structure matches the RaidSevenStarCapture9 but there are 4 bytes at the front to indicate if the copy of previous defeated flags happened when updating save public readonly int CountAll = (block.Data.Length - 0x04) / SevenStarRaidDefeatedDetail.SIZE; - - public SevenStarRaidDefeatedDetail? GetRaid(int entry) => block.Type != SCTypeCode.None ? new(Data, 0x04 + (entry * SevenStarRaidDefeatedDetail.SIZE)) : null; + public SevenStarRaidDefeatedDetail? GetRaid(int entry) => block.Type != SCTypeCode.None + ? new(Raw[4..].Slice(entry * SevenStarRaidDefeatedDetail.SIZE, SevenStarRaidDefeatedDetail.SIZE)) + : null; public SevenStarRaidDefeatedDetail?[] GetAllRaids() { @@ -116,20 +119,22 @@ public sealed class RaidSevenStarDefeated9(SAV9SV sav, SCBlock block) : SaveBloc } } -public sealed class SevenStarRaidDefeatedDetail(byte[] Data, int Offset) +public sealed class SevenStarRaidDefeatedDetail(Memory raw) { public const int SIZE = 0x08; + private Span Data => raw.Span; + public uint Identifier { - get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x00)); - set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x00), value); + get => ReadUInt32LittleEndian(Data); + set => WriteUInt32LittleEndian(Data, value); } public bool Defeated { - get => Data[Offset + 0x04] == 1; - set => Data[Offset + 0x04] = (byte)(value ? 1 : 0); + get => Data[0x04] == 1; + set => Data[0x04] = (byte)(value ? 1 : 0); } // 0x05 - 0x07 padding diff --git a/PKHeX.Core/Saves/Substructures/IEventFlagArray.cs b/PKHeX.Core/Saves/Substructures/IEventFlagArray.cs index 3f9bce179..40dbfa574 100644 --- a/PKHeX.Core/Saves/Substructures/IEventFlagArray.cs +++ b/PKHeX.Core/Saves/Substructures/IEventFlagArray.cs @@ -11,6 +11,11 @@ public interface IEventFlagArray public interface IEventFlag37 : IEventFlagArray, IEventWorkArray; +public interface IEventFlagProvider37 +{ + IEventFlag37 EventWork { get; } +} + public static class EventFlagArrayExtensions { /// All Event Flag values for the savegame diff --git a/PKHeX.Core/Saves/Substructures/Inventory/MyItem.cs b/PKHeX.Core/Saves/Substructures/Inventory/MyItem.cs index 6bbd4d304..0e8767a40 100644 --- a/PKHeX.Core/Saves/Substructures/Inventory/MyItem.cs +++ b/PKHeX.Core/Saves/Substructures/Inventory/MyItem.cs @@ -1,11 +1,9 @@ +using System; using System.Collections.Generic; namespace PKHeX.Core; -public abstract class MyItem : SaveBlock +public abstract class MyItem(SaveFile SAV, Memory raw) : SaveBlock(SAV, raw) { public abstract IReadOnlyList Inventory { get; set; } - protected MyItem(SaveFile SAV) : base(SAV) { } - protected MyItem(SaveFile SAV, byte[] data) : base(SAV, data) { } - protected MyItem(SaveFile SAV, int offset) : base(SAV, offset) { } } diff --git a/PKHeX.Core/Saves/Substructures/Inventory/Pouch/InventoryPouch8b.cs b/PKHeX.Core/Saves/Substructures/Inventory/Pouch/InventoryPouch8b.cs index e8efbcb12..945a0bb92 100644 --- a/PKHeX.Core/Saves/Substructures/Inventory/Pouch/InventoryPouch8b.cs +++ b/PKHeX.Core/Saves/Substructures/Inventory/Pouch/InventoryPouch8b.cs @@ -4,8 +4,8 @@ using System.Diagnostics; namespace PKHeX.Core; -public sealed class InventoryPouch8b(InventoryType type, IItemStorage info, int maxCount, int offset) - : InventoryPouch(type, info, maxCount, offset) +public sealed class InventoryPouch8b(InventoryType type, IItemStorage info, int maxCount) + : InventoryPouch(type, info, maxCount, 0) { public bool SetNew { get; set; } @@ -31,9 +31,9 @@ public sealed class InventoryPouch8b(InventoryType type, IItemStorage info, int SortBy(z => !z.IsValidSaveSortNumberCount ? (ushort)0xFFFF : z.SortOrder); } - public InventoryItem8b GetItem(ReadOnlySpan data, ushort itemID) + public static InventoryItem8b GetItem(ReadOnlySpan data, ushort itemID) { - var ofs = GetItemOffset(itemID, Offset); + var ofs = GetItemOffset(itemID); return InventoryItem8b.Read(itemID, data[ofs..]); } @@ -64,7 +64,7 @@ public sealed class InventoryPouch8b(InventoryType type, IItemStorage info, int item.IsNew |= !original.IsValidSaveSortNumberCount; } - var ofs = GetItemOffset(index, Offset); + var ofs = GetItemOffset(index); item.Write(data[ofs..]); // In the event of duplicates, we just overwrite what was previously written by a prior duplicate. @@ -81,7 +81,7 @@ public sealed class InventoryPouch8b(InventoryType type, IItemStorage info, int } } - public static int GetItemOffset(ushort index, int baseOffset) => baseOffset + (InventoryItem8b.SIZE * index); + public static int GetItemOffset(ushort index) => InventoryItem8b.SIZE * index; - public void ClearItem(Span data, ushort index) => InventoryItem8b.Clear(data, GetItemOffset(index, Offset)); + public static void ClearItem(Span data, ushort index) => InventoryItem8b.Clear(data, GetItemOffset(index)); } diff --git a/PKHeX.Core/Saves/Substructures/Misc/IBoxDetailName.cs b/PKHeX.Core/Saves/Substructures/Misc/IBoxDetailName.cs index b4eecec5b..ac6b07aae 100644 --- a/PKHeX.Core/Saves/Substructures/Misc/IBoxDetailName.cs +++ b/PKHeX.Core/Saves/Substructures/Misc/IBoxDetailName.cs @@ -5,8 +5,48 @@ namespace PKHeX.Core; /// /// Provides details about box names within the save file. /// -public interface IBoxDetailName +public interface IBoxDetailName : IBoxDetailNameRead { - public string GetBoxName(int box); public void SetBoxName(int box, ReadOnlySpan value); } + +public interface IBoxDetailNameRead +{ + public string GetBoxName(int box); +} + +public static class BoxDetailNameExtensions +{ + public static string GetDefaultBoxName(int box) => $"Box {box + 1}"; // 0-indexed + public static string GetDefaultBoxNameCaps(int box) => $"BOX {box + 1}"; // 0-indexed + public static string GetDefaultBoxNameJapanese(int box) => $"ボックス{box + 1}"; + + public static void MoveBoxName(this IBoxDetailName obj, int box, int insertBeforeBox) + { + if (box == insertBeforeBox) + return; + var value = obj.GetBoxName(box); + // Shift all names between the two boxes + if (box < insertBeforeBox) + { + for (int i = box; i < insertBeforeBox; i++) + obj.SetBoxName(i, obj.GetBoxName(i + 1)); + } + else + { + for (int i = box; i > insertBeforeBox; i--) + obj.SetBoxName(i, obj.GetBoxName(i - 1)); + } + obj.SetBoxName(insertBeforeBox, value); + } + + public static void SwapBoxName(this IBoxDetailName obj, int box1, int box2) + { + if (box1 == box2) + return; + var value1 = obj.GetBoxName(box1); + var value2 = obj.GetBoxName(box2); + obj.SetBoxName(box1, value2); + obj.SetBoxName(box2, value1); + } +} diff --git a/PKHeX.Core/Saves/Substructures/Misc/IBoxDetailWallpaper.cs b/PKHeX.Core/Saves/Substructures/Misc/IBoxDetailWallpaper.cs index 0205df33d..e2e7d355d 100644 --- a/PKHeX.Core/Saves/Substructures/Misc/IBoxDetailWallpaper.cs +++ b/PKHeX.Core/Saves/Substructures/Misc/IBoxDetailWallpaper.cs @@ -1,4 +1,4 @@ -namespace PKHeX.Core; +namespace PKHeX.Core; /// /// Provides details about box wallpaper values within the save file. @@ -8,3 +8,35 @@ public interface IBoxDetailWallpaper public int GetBoxWallpaper(int box); public void SetBoxWallpaper(int box, int value); } + +public static class BoxDetailWallpaperExtension +{ + public static void MoveWallpaper(this IBoxDetailWallpaper obj, int box, int insertBeforeBox) + { + if (box == insertBeforeBox) + return; + var value = obj.GetBoxWallpaper(box); + // Shift all wallpapers between the two boxes + if (box < insertBeforeBox) + { + for (int i = box; i < insertBeforeBox; i++) + obj.SetBoxWallpaper(i, obj.GetBoxWallpaper(i + 1)); + } + else + { + for (int i = box; i > insertBeforeBox; i--) + obj.SetBoxWallpaper(i, obj.GetBoxWallpaper(i - 1)); + } + obj.SetBoxWallpaper(insertBeforeBox, value); + } + + public static void SwapWallpaper(this IBoxDetailWallpaper obj, int box1, int box2) + { + if (box1 == box2) + return; + var value1 = obj.GetBoxWallpaper(box1); + var value2 = obj.GetBoxWallpaper(box2); + obj.SetBoxWallpaper(box1, value2); + obj.SetBoxWallpaper(box2, value1); + } +} diff --git a/PKHeX.Core/Saves/Substructures/MysteryGift/IMysteryGiftFlags.cs b/PKHeX.Core/Saves/Substructures/MysteryGift/IMysteryGiftFlags.cs new file mode 100644 index 000000000..943044193 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/MysteryGift/IMysteryGiftFlags.cs @@ -0,0 +1,9 @@ +namespace PKHeX.Core; + +public interface IMysteryGiftFlags +{ + int MysteryGiftReceivedFlagMax { get; } + bool GetMysteryGiftReceivedFlag(int index); + void SetMysteryGiftReceivedFlag(int index, bool value); + void ClearReceivedFlags(); +} diff --git a/PKHeX.Core/Saves/Substructures/MysteryGift/IMysteryGiftStorage.cs b/PKHeX.Core/Saves/Substructures/MysteryGift/IMysteryGiftStorage.cs new file mode 100644 index 000000000..603d39e1c --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/MysteryGift/IMysteryGiftStorage.cs @@ -0,0 +1,11 @@ +namespace PKHeX.Core; + +/// +/// Provides interactions for Mystery Gift reading and writing. +/// +public interface IMysteryGiftStorage +{ + int GiftCountMax { get; } + DataMysteryGift GetMysteryGift(int index); + void SetMysteryGift(int index, DataMysteryGift gift); +} diff --git a/PKHeX.Core/Saves/Substructures/MysteryGift/IMysteryGiftStorageProvider.cs b/PKHeX.Core/Saves/Substructures/MysteryGift/IMysteryGiftStorageProvider.cs new file mode 100644 index 000000000..44813529e --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/MysteryGift/IMysteryGiftStorageProvider.cs @@ -0,0 +1,12 @@ +namespace PKHeX.Core; + +/// +/// Provides a instance. +/// +public interface IMysteryGiftStorageProvider +{ + /// + /// Provides a instance. + /// + IMysteryGiftStorage MysteryGiftStorage { get; } +} diff --git a/PKHeX.Core/Saves/Substructures/MysteryGiftAlbum.cs b/PKHeX.Core/Saves/Substructures/MysteryGiftAlbum.cs deleted file mode 100644 index 40d37a972..000000000 --- a/PKHeX.Core/Saves/Substructures/MysteryGiftAlbum.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace PKHeX.Core; - -/// -/// Structure containing the Mystery Gift Data -/// -public class MysteryGiftAlbum(DataMysteryGift[] Gifts, bool[] Flags) -{ - /// - /// Mystery Gift data received - /// - public readonly DataMysteryGift[] Gifts = Gifts; - - /// - /// Received Flag list - /// - /// - /// this[index] == true iff index= has been received already. - /// - public readonly bool[] Flags = Flags; -} - -public sealed class EncryptedMysteryGiftAlbum(DataMysteryGift[] Gifts, bool[] Flags, uint Seed) - : MysteryGiftAlbum(Gifts, Flags) -{ - /// - /// Encryption Seed (only used in Generation 5 to encrypt the stored data) - /// - public readonly uint Seed = Seed; -} diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Gen9/Zukan9.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Gen9/Zukan9.cs index ac96a48be..56ac08f5e 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Gen9/Zukan9.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Gen9/Zukan9.cs @@ -6,20 +6,13 @@ namespace PKHeX.Core; /// /// Pokédex structure used for . /// > -public sealed class Zukan9 : ZukanBase +public sealed class Zukan9(SAV9SV sav, SCBlock paldea, SCBlock kitakami) : ZukanBase(sav, default) { - public readonly Zukan9Paldea DexPaldea; - public readonly Zukan9Kitakami DexKitakami; - private readonly DexBlockMode9 Mode; + public readonly Zukan9Paldea DexPaldea = new(sav, paldea); + public readonly Zukan9Kitakami DexKitakami = new(sav, kitakami); + private readonly DexBlockMode9 Mode = kitakami.Data.Length != 0 ? Kitakami : Paldea; - public Zukan9(SAV9SV sav, SCBlock paldea, SCBlock kitakami) : base(sav, 0) - { - DexPaldea = new(sav, paldea); - DexKitakami = new(sav, kitakami); - - // Starting in 2.0.1, the developers have dummied out the old Paldea Pokédex block and exclusively use the new Kitakami block. - Mode = kitakami.Data.Length != 0 ? Kitakami : Paldea; - } + // Starting in 2.0.1, the developers have dummied out the old Paldea Pokédex block and exclusively use the new Kitakami block. /// /// Checks how much DLC patches have been installed by detecting if DLC blocks are present. diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Gen9/Zukan9Kitakami.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Gen9/Zukan9Kitakami.cs index 9951267f6..aa06a0e2d 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Gen9/Zukan9Kitakami.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Gen9/Zukan9Kitakami.cs @@ -5,7 +5,7 @@ namespace PKHeX.Core; /// /// Pokédex structure used for . /// > -public sealed class Zukan9Kitakami(SAV9SV sav, SCBlock Block) : ZukanBase(sav, 0) +public sealed class Zukan9Kitakami(SAV9SV sav, SCBlock Block) : ZukanBase(sav, default) { public PokeDexEntry9Kitakami Get(ushort species) { diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Gen9/Zukan9Paldea.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Gen9/Zukan9Paldea.cs index c37a1505d..9703c0278 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Gen9/Zukan9Paldea.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Gen9/Zukan9Paldea.cs @@ -5,7 +5,7 @@ namespace PKHeX.Core; /// /// Pokédex structure used for . /// > -public sealed class Zukan9Paldea(SAV9SV sav, SCBlock block) : ZukanBase(sav, 0) +public sealed class Zukan9Paldea(SAV9SV sav, SCBlock block) : ZukanBase(sav, block.Data) { public PokeDexEntry9Paldea Get(ushort species) { diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan.cs index b1e4b7036..aaf9229f7 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan.cs @@ -5,17 +5,9 @@ namespace PKHeX.Core; /// /// Base class for Pokédex logic operations. /// -public abstract class ZukanBase where T : SaveFile +public abstract class ZukanBase(T sav, Memory dex) : SaveBlock(sav, dex) + where T : SaveFile { - protected readonly T SAV; - public readonly int PokeDex; - - protected ZukanBase(T sav, int dex) - { - SAV = sav; - PokeDex = dex; - } - #region Overall Info /// Count of unique Species Seen public int SeenCount @@ -76,15 +68,10 @@ public abstract class ZukanBase where T : SaveFile /// /// Base class for Pokédex operations, exposing the shared structure features used by Generations 5, 6, and 7. /// -public abstract class Zukan : ZukanBase where T : SaveFile +public abstract class Zukan(T sav, Memory raw, int langflag) : ZukanBase(sav, raw) + where T : SaveFile { - protected readonly int PokeDexLanguageFlags; - - protected Zukan(T sav, int dex, int langflag) : base(sav, dex) - { - PokeDexLanguageFlags = langflag; - ArgumentOutOfRangeException.ThrowIfGreaterThan(langflag, dex); - } + protected readonly int PokeDexLanguageFlags = langflag; protected abstract int OFS_SEEN { get; } protected abstract int OFS_CAUGHT { get; } @@ -98,8 +85,8 @@ public abstract class Zukan : ZukanBase where T : SaveFile protected abstract void SetAllDexFlagsLanguage(int bit, int lang, bool value = true); protected abstract void SetAllDexSeenFlags(int baseBit, byte b, byte gender, bool isShiny, bool value = true); - protected bool GetFlag(int ofs, int bitIndex) => SAV.GetFlag(PokeDex + ofs + (bitIndex >> 3), bitIndex); - protected void SetFlag(int ofs, int bitIndex, bool value = true) => SAV.SetFlag(PokeDex + ofs + (bitIndex >> 3), bitIndex, value); + protected bool GetFlag(int ofs, int bitIndex) => SAV.GetFlag(Data, ofs + (bitIndex >> 3), bitIndex); + protected void SetFlag(int ofs, int bitIndex, bool value = true) => SAV.SetFlag(Data, ofs + (bitIndex >> 3), bitIndex, value); public override bool GetCaught(ushort species) => GetFlag(OFS_CAUGHT, species - 1); public virtual void SetCaught(ushort species, bool value = true) => SetFlag(OFS_CAUGHT, species - 1, value); diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan4.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan4.cs index ca964fec3..1ce245f7d 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan4.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan4.cs @@ -6,11 +6,8 @@ namespace PKHeX.Core; /// /// Pokédex structure used by games. /// -public sealed class Zukan4(SAV4 sav, int offset) : ZukanBase(sav, offset) +public sealed class Zukan4(SAV4 sav, Memory raw) : ZukanBase(sav, raw) { - private readonly Memory Buffer = sav.GeneralBuffer[offset..]; - private Span Data => Buffer.Span; - // General structure: u32 magic, 4*bitflags, u32 spinda, form flags, language flags, more form flags, upgrade flags /* 4 BitRegions with 0x40*8 bits diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan5.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan5.cs index 7d383ea3e..eeb9408ac 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan5.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan5.cs @@ -13,12 +13,12 @@ public sealed class Zukan5 : Zukan protected override int DexLangFlagByteCount => 7; protected override int DexLangIDCount => 7; - public Zukan5(SAV5B2W2 sav, int dex, int langflag) : base(sav, dex, langflag) + public Zukan5(SAV5B2W2 sav, Memory dex, int langflag) : base(sav, dex, langflag) { DexFormIndexFetcher = DexFormUtil.GetDexFormIndexB2W2; } - public Zukan5(SAV5BW sav, int dex, int langflag) : base(sav, dex, langflag) + public Zukan5(SAV5BW sav, Memory dex, int langflag) : base(sav, dex, langflag) { DexFormIndexFetcher = DexFormUtil.GetDexFormIndexBW; } diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan6.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan6.cs index a4112c547..9763be13a 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan6.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan6.cs @@ -16,14 +16,14 @@ public abstract class Zukan6 : Zukan protected override int DexLangIDCount => 7; protected int SpindaOffset { get; init; } - protected Zukan6(SAV6XY sav, int dex, int langflag) : base(sav, dex, langflag) + protected Zukan6(SAV6XY sav, Memory dex, int langflag) : base(sav, dex, langflag) { DexFormIndexFetcher = DexFormUtil.GetDexFormIndexXY; } private Func DexFormIndexFetcher { get; } - protected Zukan6(SAV6AO sav, int dex, int langflag) : base(sav, dex, langflag) + protected Zukan6(SAV6AO sav, Memory dex, int langflag) : base(sav, dex, langflag) { DexFormIndexFetcher = DexFormUtil.GetDexFormIndexORAS; } @@ -130,8 +130,8 @@ public abstract class Zukan6 : Zukan public uint SpindaPID { - get => ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + SpindaOffset)); - set => WriteUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + SpindaOffset), value); + get => ReadUInt32LittleEndian(Data[SpindaOffset..]); + set => WriteUInt32LittleEndian(Data[SpindaOffset..], value); } public bool[] GetLanguageBitflags(ushort species) @@ -183,7 +183,7 @@ public abstract class Zukan6 : Zukan /// public sealed class Zukan6AO : Zukan6 { - public Zukan6AO(SAV6AO sav, int dex, int langflag) : base(sav, dex, langflag) + public Zukan6AO(SAV6AO sav, Memory dex, int langflag) : base(sav, dex, langflag) { SpindaOffset = 0x680; } @@ -197,13 +197,13 @@ public sealed class Zukan6AO : Zukan6 public ushort GetEncounterCount(int index) { - var ofs = PokeDex + 0x686 + (index * 2); + var ofs = 0x686 + (index * 2); return ReadUInt16LittleEndian(SAV.Data.AsSpan(ofs)); } public void SetEncounterCount(int index, ushort value) { - var ofs = PokeDex + 0x686 + (index * 2); + var ofs = 0x686 + (index * 2); WriteUInt16LittleEndian(SAV.Data.AsSpan(ofs), value); } } @@ -213,7 +213,7 @@ public sealed class Zukan6AO : Zukan6 /// public sealed class Zukan6XY : Zukan6 { - public Zukan6XY(SAV6XY sav, int dex, int langflag) : base(sav, dex, langflag) + public Zukan6XY(SAV6XY sav, Memory dex, int langflag) : base(sav, dex, langflag) { SpindaOffset = 0x648; } diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs index a3e9f8f71..2df39e535 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs @@ -25,15 +25,15 @@ public class Zukan7 : Zukan private readonly IList FormBaseSpecies; - public Zukan7(SAV7SM sav, int dex, int langflag) : this(sav, dex, langflag, DexFormUtil.GetDexFormIndexSM) { } - public Zukan7(SAV7USUM sav, int dex, int langflag) : this(sav, dex, langflag, DexFormUtil.GetDexFormIndexUSUM) { } - protected Zukan7(SAV7b sav, int dex, int langflag) : this(sav, dex, langflag, DexFormUtil.GetDexFormIndexGG) { } + public Zukan7(SAV7SM sav, Memory dex, int langflag) : this(sav, dex, langflag, DexFormUtil.GetDexFormIndexSM) { } + public Zukan7(SAV7USUM sav, Memory dex, int langflag) : this(sav, dex, langflag, DexFormUtil.GetDexFormIndexUSUM) { } + protected Zukan7(SAV7b sav, Memory dex, int langflag) : this(sav, dex, langflag, DexFormUtil.GetDexFormIndexGG) { } - private Zukan7(SaveFile sav, int dex, int langflag, Func form) : base(sav, dex, langflag) + private Zukan7(SaveFile sav, Memory dex, int langflag, Func form) : base(sav, dex, langflag) { GetCountFormsPriorTo = form; FormBaseSpecies = GetFormIndexBaseSpeciesList(); - Debug.Assert(!SAV.State.Exportable || ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex)) == MAGIC); + Debug.Assert(!SAV.State.Exportable || ReadUInt32LittleEndian(Data) == MAGIC); } public Func GetCountFormsPriorTo { get; } @@ -138,32 +138,32 @@ public class Zukan7 : Zukan if (alreadySeen) // update? { var flag1 = (1 << (shift + 4)); - if ((SAV.Data[PokeDex + 0x84] & flag1) != 0) // Already showing this one + if ((Data[0x84] & flag1) != 0) // Already showing this one return; - var span = SAV.Data.AsSpan(PokeDex + 0x8E8 + (shift * 4)); + var span = Data[(0x8E8 + (shift * 4))..]; WriteUInt32LittleEndian(span, pk.EncryptionConstant); - SAV.Data[PokeDex + 0x84] |= (byte)(flag1 | (1 << shift)); + Data[0x84] |= (byte)(flag1 | (1 << shift)); } - else if ((SAV.Data[PokeDex + 0x84] & (1 << shift)) == 0) + else if ((Data[0x84] & (1 << shift)) == 0) { - var span = SAV.Data.AsSpan(PokeDex + 0x8E8 + (shift * 4)); + var span = Data[(0x8E8 + (shift * 4))..]; WriteUInt32LittleEndian(span, pk.EncryptionConstant); - SAV.Data[PokeDex + 0x84] |= (byte)(1 << shift); + Data[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)); + get => (Data[4] & 1) == 1; + set => Data[4] = (byte)((Data[4] & 0xFE) | (value ? 1 : 0)); } /// /// Gets the last viewed dex entry in the Pokédex (by National Dex ID), internally called DefaultMons /// - public uint CurrentViewedDex => (ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + 4)) >> 9) & 0x3FF; + public uint CurrentViewedDex => (ReadUInt32LittleEndian(Data[4..]) >> 9) & 0x3FF; public IEnumerable GetAllFormEntries(ushort species) { @@ -327,6 +327,6 @@ public class Zukan7 : Zukan return species - 1; // bit index valid - return (SAV.MaxSpeciesID + previous + (form - 1)); + return SAV.MaxSpeciesID + previous + (form - 1); } } diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7b.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7b.cs index 5e7b3b3c7..056c05b4c 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7b.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7b.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// /// Pokédex structure used for games, slightly modified from . /// > -public sealed class Zukan7b(SAV7b sav, int dex, int langflag) : Zukan7(sav, dex, langflag) +public sealed class Zukan7b(SAV7b sav, Memory dex, int langflag) : Zukan7(sav, dex, langflag) { private const int UNSET = 0x007F00FE; private const int BaseOffset = 0x2A00; diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan8.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan8.cs index 8bd835e4b..eaae5e101 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan8.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan8.cs @@ -20,7 +20,7 @@ public sealed class Zukan8 : ZukanBase /// public readonly IReadOnlyDictionary DexLookup; - public Zukan8(SAV8SWSH sav, SCBlock galar, SCBlock rigel1, SCBlock rigel2) : base(sav, 0) + public Zukan8(SAV8SWSH sav, SCBlock galar, SCBlock rigel1, SCBlock rigel2) : base(sav, default) { Galar = galar; Rigel1 = rigel1; diff --git a/PKHeX.Core/Saves/Substructures/Records.cs b/PKHeX.Core/Saves/Substructures/Records.cs index 3532f7763..6ae0179f6 100644 --- a/PKHeX.Core/Saves/Substructures/Records.cs +++ b/PKHeX.Core/Saves/Substructures/Records.cs @@ -25,10 +25,10 @@ public static class Records return MaxByType[maxes[recordID]]; } - public static int GetOffset(int baseOfs, int recordID) => recordID switch + public static int GetOffset(int recordID) => recordID switch { - < LargeRecordCount => baseOfs + (recordID * sizeof(int)), - < Count => baseOfs + (LargeRecordCount * sizeof(int)) + ((recordID - LargeRecordCount) * sizeof(ushort)), + < LargeRecordCount => (recordID * sizeof(int)), + < Count => (LargeRecordCount * sizeof(int)) + ((recordID - LargeRecordCount) * sizeof(ushort)), _ => -1, }; diff --git a/PKHeX.Core/Saves/Util/BoxUtil.cs b/PKHeX.Core/Saves/Util/BoxUtil.cs index c8492bac4..436ba144e 100644 --- a/PKHeX.Core/Saves/Util/BoxUtil.cs +++ b/PKHeX.Core/Saves/Util/BoxUtil.cs @@ -36,7 +36,8 @@ public static class BoxUtil var boxFolder = path; if (boxFolders) { - var boxName = Util.CleanFileName(sav.GetBoxName(box)); + string boxName = sav is IBoxDetailName bn ? bn.GetBoxName(box) : BoxDetailNameExtensions.GetDefaultBoxName(box); + boxName = Util.CleanFileName(boxName); boxFolder = Path.Combine(path, boxName); Directory.CreateDirectory(boxFolder); } @@ -225,17 +226,17 @@ public static class BoxUtil { int count = sav.BoxCount; var result = new string[count]; - if (!sav.State.Exportable) + if (!sav.State.Exportable || sav is not IBoxDetailNameRead r) { for (int i = 0; i < count; i++) - result[i] = $"Box {i + 1}"; + result[i] = BoxDetailNameExtensions.GetDefaultBoxName(i); return result; } for (int i = 0; i < count; i++) { - try { result[i] = sav.GetBoxName(i); } - catch { result[i] = $"Box {i + 1}"; } + try { result[i] = r.GetBoxName(i); } + catch { result[i] = BoxDetailNameExtensions.GetDefaultBoxName(i); } } return result; diff --git a/PKHeX.Core/Util/FlagUtil.cs b/PKHeX.Core/Util/FlagUtil.cs index 515cb18ec..e71b93d84 100644 --- a/PKHeX.Core/Util/FlagUtil.cs +++ b/PKHeX.Core/Util/FlagUtil.cs @@ -19,6 +19,8 @@ public static class FlagUtil return ((arr[offset] >> bitIndex) & 1) != 0; } + public static bool GetFlag(ReadOnlySpan arr, int index) => GetFlag(arr, index >> 3, index); + /// /// Sets the requested value to the byte at . /// @@ -34,6 +36,8 @@ public static class FlagUtil arr[offset] = (byte)newValue; } + public static void SetFlag(Span arr, int index, bool value) => SetFlag(arr, index >> 3, index, value); + public static bool[] GetBitFlagArray(ReadOnlySpan data, int count) { var result = new bool[count]; diff --git a/PKHeX.Drawing.Misc/QR/QRDecode.cs b/PKHeX.Drawing.Misc/QR/QRDecode.cs index 25c1c9435..e8699fd8c 100644 --- a/PKHeX.Drawing.Misc/QR/QRDecode.cs +++ b/PKHeX.Drawing.Misc/QR/QRDecode.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.Net; using System.Text; diff --git a/PKHeX.Drawing.Misc/Util/WallpaperUtil.cs b/PKHeX.Drawing.Misc/Util/WallpaperUtil.cs index 5c3965f29..0f614835b 100644 --- a/PKHeX.Drawing.Misc/Util/WallpaperUtil.cs +++ b/PKHeX.Drawing.Misc/Util/WallpaperUtil.cs @@ -7,12 +7,18 @@ namespace PKHeX.Drawing.Misc; public static class WallpaperUtil { + private static Bitmap DefaultWallpaper => Resources.box_wp16xy; + public static Bitmap WallpaperImage(this SaveFile sav, int box) => GetWallpaper(sav, box); private static Bitmap GetWallpaper(SaveFile sav, int box) { - string s = GetWallpaperResourceName(sav.Version, sav.GetBoxWallpaper(box)); - return (Bitmap?)Resources.ResourceManager.GetObject(s) ?? Resources.box_wp16xy; + if (sav is not IBoxDetailWallpaper wp) + return DefaultWallpaper; + + int wallpaper = wp.GetBoxWallpaper(box); + string s = GetWallpaperResourceName(sav.Version, wallpaper); + return (Bitmap?)Resources.ResourceManager.GetObject(s) ?? DefaultWallpaper; } public static string GetWallpaperResourceName(GameVersion version, int index) diff --git a/PKHeX.WinForms/Controls/PKM Editor/SizeCP.cs b/PKHeX.WinForms/Controls/PKM Editor/SizeCP.cs index e2f6a2251..c37c878d8 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/SizeCP.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/SizeCP.cs @@ -21,8 +21,8 @@ public partial class SizeCP : UserControl } private readonly bool Initialized; - private static readonly string[] SizeClass = Enum.GetNames(typeof(PokeSize)); - private static readonly string[] SizeClassDetailed = Enum.GetNames(typeof(PokeSizeDetailed)); + private static readonly string[] SizeClass = Enum.GetNames(); + private static readonly string[] SizeClassDetailed = Enum.GetNames(); public void LoadPKM(PKM entity) { diff --git a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.Designer.cs b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.Designer.cs index 601fe536f..eb77ce3da 100644 --- a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.Designer.cs +++ b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.Designer.cs @@ -564,7 +564,7 @@ namespace PKHeX.WinForms.Controls B_OpenHallofFame.TabIndex = 1; B_OpenHallofFame.Text = "Hall of Fame"; B_OpenHallofFame.UseVisualStyleBackColor = true; - B_OpenHallofFame.Click += B_OUTHallofFame_Click; + B_OpenHallofFame.Click += B_HallofFame_Click; // // B_OUTPasserby // diff --git a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs index 0d7ddea29..21085d9e2 100644 --- a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs +++ b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs @@ -170,7 +170,7 @@ public partial class SAVEditor : UserControl, ISlotViewer, ISaveFile public int GetViewIndex(ISlotInfo slot) { - if (!SAV.HasDaycare) + if (GetCurrentDaycare() is not { }) return -1; for (int i = 0; i < SlotPictureBoxes.Count; i++) @@ -203,8 +203,9 @@ public partial class SAVEditor : UserControl, ISlotViewer, ISaveFile private SlotInfoMisc GetSlotData(int index) { - var ofs = SAV.GetDaycareSlotOffset(SAV.DaycareIndex, index); - return new SlotInfoMisc(SAV, index, ofs); + if (GetCurrentDaycare() is not { } s) + throw new Exception(); + return new SlotInfoMisc(s.GetDaycareSlot(index), index); } public void SetPKMBoxes() @@ -245,45 +246,110 @@ public partial class SAVEditor : UserControl, ISlotViewer, ISaveFile private void ResetDaycare() { - if (!SAV.HasDaycare) + IDaycareStorage? s = GetCurrentDaycare(); + if (s is null) return; + int slotCount = s.DaycareSlotCount; for (int i = 0; i < 2; i++) { - var relIndex = i; - var pb = UpdateSlot(relIndex); + if (i >= slotCount) + { + L_SlotOccupied[i].Visible = false; + TB_SlotEXP[i].Visible = false; + continue; + } - uint? exp = SAV.GetDaycareEXP(SAV.DaycareIndex, i); - TB_SlotEXP[i].Visible = L_SlotEXP[i].Visible = exp != null; - TB_SlotEXP[i].Text = exp.ToString(); + if (s is IDaycareExperience dExp) + { + var exp = dExp.GetDaycareEXP(i); + L_SlotEXP[i].Visible = TB_SlotEXP[i].Visible = true; + TB_SlotEXP[i].Text = exp.ToString(); + } + else + { + L_SlotEXP[i].Visible = TB_SlotEXP[i].Visible = true; + } - bool? occ = SAV.IsDaycareOccupied(SAV.DaycareIndex, i); - L_SlotOccupied[i].Visible = occ != null; - if (occ == true) // If Occupied + bool occ = s.IsDaycareOccupied(i); + L_SlotOccupied[i].Visible = true; + if (occ) // If Occupied { L_SlotOccupied[i].Text = $"{i + 1}: ✓"; } else { L_SlotOccupied[i].Text = $"{i + 1}: ✘"; + var pb = UpdateSlot(i); var current = pb.Image; if (current != null) pb.Image = ImageUtil.ChangeOpacity(current, 0.6); } } - bool? egg = SAV.IsDaycareHasEgg(SAV.DaycareIndex); - DayCare_HasEgg.Visible = egg != null; - DayCare_HasEgg.Checked = egg == true; + LoadDaycareEggState(s); + LoadDaycareSeed(s); + } - var seed = SAV.GetDaycareRNGSeed(SAV.DaycareIndex); - bool hasSeed = !string.IsNullOrEmpty(seed); - if (hasSeed) + private IDaycareStorage? GetCurrentDaycare() + { + if (SAV is IDaycareMulti m) { - TB_RNGSeed.MaxLength = SAV.DaycareSeedSize; - TB_RNGSeed.Text = seed; + if (DaycareIndex < m.DaycareCount) + return m[DaycareIndex]; + return m[DaycareIndex = 0]; } - L_DaycareSeed.Visible = TB_RNGSeed.Visible = hasSeed; + if (SAV is IDaycareStorage s) + return s; + return null; + } + + private void LoadDaycareEggState(IDaycareStorage s) + { + if (s is IDaycareEggState dEgg) + { + DayCare_HasEgg.Visible = true; + DayCare_HasEgg.Checked = dEgg.IsEggAvailable; + } + else + { + DayCare_HasEgg.Visible = false; + } + } + + private void LoadDaycareSeed(IDaycareStorage s) + { + if (s is IDaycareRandomState u16) + { + TB_RNGSeed.Visible = true; + TB_RNGSeed.MaxLength = 4; + TB_RNGSeed.Text = $"{u16.Seed:X4}"; + } + else if (s is IDaycareRandomState u32) + { + TB_RNGSeed.Visible = true; + TB_RNGSeed.MaxLength = 8; + TB_RNGSeed.Text = $"{u32.Seed:X8}"; + } + else if (s is IDaycareRandomState u64) + { + TB_RNGSeed.Visible = true; + TB_RNGSeed.MaxLength = 16; + TB_RNGSeed.Text = $"{u64.Seed:X16}"; + } + else if (s is IDaycareRandomState u128) + { + TB_RNGSeed.Visible = true; + TB_RNGSeed.MaxLength = 32; + TB_RNGSeed.Text = $"{u128.Seed:X32}"; + } + else + { + L_DaycareSeed.Visible = TB_RNGSeed.Visible = false; + return; + } + + L_DaycareSeed.Visible = TB_RNGSeed.Visible = true; } private PictureBox UpdateSlot(int relIndex) @@ -454,9 +520,8 @@ public partial class SAVEditor : UserControl, ISlotViewer, ISaveFile // Write final value back to the save if (tb == TB_RNGSeed) { - var value = filterText.PadLeft(SAV.DaycareSeedSize, '0'); - SAV.SetDaycareRNGSeed(SAV.DaycareIndex, value); - SAV.State.Edited = true; + if (GetCurrentDaycare() is { } s) + SetDaycareSeed(s, filterText); } else if (tb == TB_GameSync && SAV is IGameSync sync) { @@ -475,14 +540,42 @@ public partial class SAVEditor : UserControl, ISlotViewer, ISaveFile } } + private void SetDaycareSeed(IDaycareStorage daycare, string filterText) + { + if (daycare is IDaycareRandomState u16) + { + if (ushort.TryParse(filterText, System.Globalization.NumberStyles.HexNumber, null, out var v16)) + u16.Seed = v16; + } + else if (daycare is IDaycareRandomState u32) + { + if (uint.TryParse(filterText, System.Globalization.NumberStyles.HexNumber, null, out var v32)) + u32.Seed = v32; + } + else if (daycare is IDaycareRandomState u64) + { + if (ulong.TryParse(filterText, System.Globalization.NumberStyles.HexNumber, null, out var v64)) + u64.Seed = v64; + } + else if (daycare is IDaycareRandomState u128) + { + if (UInt128.TryParse(filterText, System.Globalization.NumberStyles.HexNumber, null, out var v128)) + u128.Seed = v128; + } + SAV.State.Edited = true; + } + + private int DaycareIndex; + private void SwitchDaycare(object sender, EventArgs e) { - if (!SAV.HasTwoDaycares) + if (SAV is not IDaycareMulti m) return; - var current = string.Format(MsgSaveSwitchDaycareCurrent, SAV.DaycareIndex + 1); + var current = string.Format(MsgSaveSwitchDaycareCurrent, DaycareIndex + 1); + var next = (DaycareIndex + 1) % m.DaycareCount; if (DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MsgSaveSwitchDaycareView, current)) return; - SAV.DaycareIndex ^= 1; + DaycareIndex = next; ResetDaycare(); } @@ -535,6 +628,7 @@ public partial class SAVEditor : UserControl, ISlotViewer, ISaveFile SAV7b s => new SAV_EventWork(s), SAV8BS s => new SAV_FlagWork8b(s), IEventFlag37 g37 => new SAV_EventFlags(g37), + IEventFlagProvider37 p => new SAV_EventFlags(p.EventWork), SAV2 s => new SAV_EventFlags2(s), _ => throw new Exception(), }; @@ -713,7 +807,7 @@ public partial class SAVEditor : UserControl, ISlotViewer, ISaveFile WinFormsUtil.SetClipboardText(string.Join(Environment.NewLine, result)); } - private void B_OUTHallofFame_Click(object sender, EventArgs e) + private void B_HallofFame_Click(object sender, EventArgs e) { using var form = SAV switch { @@ -1067,7 +1161,7 @@ public partial class SAVEditor : UserControl, ISlotViewer, ISaveFile private bool ToggleViewDaycare(SaveFile sav, int BoxTab, int PartyTab) { - if ((!sav.HasDaycare && SL_Extra.SlotCount == 0) || !sav.State.Exportable) + if ((GetCurrentDaycare() is null && SL_Extra.SlotCount == 0) || !sav.State.Exportable) { if (tabBoxMulti.TabPages.Contains(Tab_Other)) tabBoxMulti.TabPages.Remove(Tab_Other); @@ -1098,20 +1192,21 @@ public partial class SAVEditor : UserControl, ISlotViewer, ISaveFile return; } - GB_Daycare.Visible = sav.HasDaycare; + DaycareIndex = 0; + GB_Daycare.Visible = sav is IDaycareStorage or IDaycareMulti; B_ConvertKorean.Visible = sav is SAV4; B_OpenPokeblocks.Visible = sav is SAV6AO; B_OpenSecretBase.Visible = sav is SAV6AO; B_OpenPokepuffs.Visible = sav is ISaveBlock6Main; B_JPEG.Visible = B_OpenLinkInfo.Visible = B_OpenSuperTraining.Visible = B_OUTPasserby.Visible = sav is ISaveBlock6Main; - B_OpenBoxLayout.Visible = sav.HasNamableBoxes; - B_OpenWondercards.Visible = sav.HasWondercards; + B_OpenBoxLayout.Visible = sav is IBoxDetailName; + B_OpenWondercards.Visible = sav is IMysteryGiftStorageProvider; B_OpenHallofFame.Visible = sav is ISaveBlock6Main or SAV7; B_OpenOPowers.Visible = sav is ISaveBlock6Main; B_OpenPokedex.Visible = sav.HasPokeDex; B_OpenBerryField.Visible = sav is SAV6XY; // OR/AS undocumented B_OpenFriendSafari.Visible = sav is SAV6XY; - B_OpenEventFlags.Visible = sav is IEventFlag37 or SAV1 or SAV2 or SAV8BS or SAV7b; + B_OpenEventFlags.Visible = sav is IEventFlag37 or IEventFlagProvider37 or SAV1 or SAV2 or SAV8BS or SAV7b; B_CGearSkin.Visible = sav.Generation == 5; B_OpenPokeBeans.Visible = B_CellsStickers.Visible = B_FestivalPlaza.Visible = sav is SAV7; diff --git a/PKHeX.WinForms/Controls/Slots/SlotList.cs b/PKHeX.WinForms/Controls/Slots/SlotList.cs index 4310b8936..dc63762e3 100644 --- a/PKHeX.WinForms/Controls/Slots/SlotList.cs +++ b/PKHeX.WinForms/Controls/Slots/SlotList.cs @@ -8,7 +8,7 @@ namespace PKHeX.WinForms.Controls; public partial class SlotList : UserControl, ISlotViewer { - private static readonly string[] names = Enum.GetNames(typeof(StorageSlotType)); + private static readonly string[] names = Enum.GetNames(); private readonly Label[] Labels = new Label[names.Length]; private readonly List slots = []; private List SlotOffsets = []; diff --git a/PKHeX.WinForms/MainWindow/Main.cs b/PKHeX.WinForms/MainWindow/Main.cs index 8cbc4146e..5edf2c51f 100644 --- a/PKHeX.WinForms/MainWindow/Main.cs +++ b/PKHeX.WinForms/MainWindow/Main.cs @@ -89,7 +89,7 @@ public partial class Main : Form public static IReadOnlyList GenderSymbols { get; private set; } = GameInfo.GenderSymbolUnicode; public static bool HaX { get; private set; } - private readonly string[] main_langlist = Enum.GetNames(typeof(ProgramLanguage)); + private readonly string[] main_langlist = Enum.GetNames(); private static readonly List Plugins = []; #endregion diff --git a/PKHeX.WinForms/Subforms/SAV_Encounters.cs b/PKHeX.WinForms/Subforms/SAV_Encounters.cs index 9d1b6b2f0..920ce387d 100644 --- a/PKHeX.WinForms/Subforms/SAV_Encounters.cs +++ b/PKHeX.WinForms/Subforms/SAV_Encounters.cs @@ -103,7 +103,7 @@ public partial class SAV_Encounters : Form private void GetTypeFilters() { - var types = (EncounterTypeGroup[])Enum.GetValues(typeof(EncounterTypeGroup)); + var types = Enum.GetValues(); var checks = types.Select(z => new CheckBox { Name = z.ToString(), diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs index 3befdcdcb..c937178f0 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs @@ -94,10 +94,10 @@ public partial class SAV_Misc3 : Form private void ReadJoyful(IGen3Joyful j) { TB_J1.Text = Math.Min((ushort)9999, j.JoyfulJumpInRow).ToString(); - TB_J2.Text = Math.Min(9999, j.JoyfulJumpScore).ToString(); + TB_J2.Text = Math.Min(99990, j.JoyfulJumpScore).ToString(); TB_J3.Text = Math.Min((ushort)9999, j.JoyfulJump5InRow).ToString(); TB_B1.Text = Math.Min((ushort)9999, j.JoyfulBerriesInRow).ToString(); - TB_B2.Text = Math.Min(9999, j.JoyfulBerriesScore).ToString(); + TB_B2.Text = Math.Min(99990, j.JoyfulBerriesScore).ToString(); TB_B3.Text = Math.Min((ushort)9999, j.JoyfulBerries5InRow).ToString(); } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Roamer3.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Roamer3.cs index c8c5b84bf..40b5aab73 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Roamer3.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Roamer3.cs @@ -7,12 +7,14 @@ namespace PKHeX.WinForms; public partial class SAV_Roamer3 : Form { private readonly Roamer3 Reader; + private readonly SAV3 SAV; public SAV_Roamer3(SAV3 sav) { InitializeComponent(); WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage); Reader = new Roamer3(sav); + SAV = sav; CB_Species.InitializeBinding(); CB_Species.DataSource = new BindingSource(GameInfo.FilteredSources.Species, null); @@ -23,7 +25,7 @@ public partial class SAV_Roamer3 : Form private void LoadData() { TB_PID.Text = Reader.PID.ToString("X8"); - CHK_Shiny.Checked = Reader.IsShiny(Reader.PID); + CHK_Shiny.Checked = Roamer3.IsShiny(Reader.PID, SAV); CB_Species.SelectedValue = (int)Reader.Species; var IVs = Reader.IVs; @@ -67,6 +69,6 @@ public partial class SAV_Roamer3 : Form private void TB_PID_TextChanged(object sender, EventArgs e) { var pid = Util.GetHexValue(TB_PID.Text); - CHK_Shiny.Checked = Reader.IsShiny(pid); + CHK_Shiny.Checked = Roamer3.IsShiny(pid, SAV); } } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs index bccea5d35..2a3a0e156 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs @@ -20,11 +20,7 @@ public partial class SAV_Misc5 : Form private ComboBox[] cbr = null!; private int ofsFly; private int[] FlyDestC = null!; - private const int WorkRoamer = 192; - private const int ofsRoamer = 0x21B00; private const int ofsLibPass = 0x212BC; - private const int ofsForestCity = 0x1FA00; - private const int ofsForestCitySize = 0x1E8; private const uint keyLibPass = 2010_04_06; // 0x132B536 private uint valLibPass; private bool bLibPass; @@ -125,7 +121,7 @@ public partial class SAV_Misc5 : Form CLB_FlyDest.SetItemChecked(i, (SAV.Data[ofsFly + (FlyDestC[i] >> 3)] & (1 << (FlyDestC[i] & 7))) != 0); } - if (SAV is SAV5BW) + if (SAV is SAV5BW bw) { TC_Misc.TabPages.Remove(TAB_Medals); GB_KeySystem.Visible = false; @@ -139,7 +135,7 @@ public partial class SAV_Misc5 : Form // Top 2 bit acts as flags of some sorts for (int i = 0; i < cbr.Length; i++) { - int c = SAV.Data[ofsRoamer + 0x2E + i]; + byte c = bw.Encount.GetRoamerState(i); var states = GetStates(); if (states.All(z => z.Value != c)) states.Add(new ComboItem($"Unknown (0x{c:X2})", c)); @@ -155,7 +151,7 @@ public partial class SAV_Misc5 : Form // to the cabin in where old grandpa and grandma live // located at route 7. { - var current = SAV.GetWork(WorkRoamer); + var current = bw.EventWork.GetWorkRoamer(); var states = GetRoamStatusStates(); if (states.All(z => z.Value != current)) states.Add(new ComboItem($"Unknown (0x{current:X2})", current)); @@ -234,29 +230,29 @@ public partial class SAV_Misc5 : Form } WriteUInt32LittleEndian(SAV.Data.AsSpan(ofsFly), valFly); - if (SAV is SAV5BW) + if (SAV is SAV5BW bw) { // Roamer + var encount = bw.Encount; for (int i = 0; i < cbr.Length; i++) { - int c = SAV.Data[ofsRoamer + 0x2E + i]; - var d = (ushort)WinFormsUtil.GetIndex(cbr[i]); + int c = bw.Encount.GetRoamerState(i); + var d = (byte)WinFormsUtil.GetIndex(cbr[i]); if (c == d) continue; - SAV.Data[ofsRoamer + 0x2E + i] = (byte)d; + encount.SetRoamerState(i, d); if (c != 1) continue; - SAV.Data.AsSpan(ofsRoamer + 4 + (i * 0x14), 14).Clear(); - SAV.Data[ofsRoamer + 0x2C + i] = 0; + var roamer = i == 0 ? encount.Roamer1 : encount.Roamer2; + roamer.Clear(); + encount.SetRoamerState2C(i, 0); } // RoamStatus { - int current = SAV.GetWork(192); var desired = (ushort)WinFormsUtil.GetIndex(CB_RoamStatus); - if (current != desired) - SAV.SetWork(WorkRoamer, desired); + bw.EventWork.SetWorkRoamer(desired); } // LibertyPass @@ -300,11 +296,12 @@ public partial class SAV_Misc5 : Form if (SAV is SAV5B2W2 b2w2) { var pass = (Entralink5B2W2)entree; - var ppv = (PassPower5[])Enum.GetValues(typeof(PassPower5)); - var ppn = Enum.GetNames(typeof(PassPower5)); - ComboItem[] PassPowerB = [.. ppn.Zip(ppv, (f, s) => new ComboItem(f, (int)s)).OrderBy(z => z.Text)]; - var cba = new[] { CB_PassPower1, CB_PassPower2, CB_PassPower3 }; - foreach (var cb in cba) + var ppv = Enum.GetValues(); + var ppn = Enum.GetNames(); + var PassPowerB = new ComboItem[ppv.Length]; + for (int i = 0; i < ppv.Length; i++) + PassPowerB[i] = new ComboItem(ppn[i], (int)ppv[i]); + foreach (var cb in (ComboBox[])[CB_PassPower1, CB_PassPower2, CB_PassPower3]) { cb.Items.Clear(); cb.InitializeBinding(); @@ -324,7 +321,7 @@ public partial class SAV_Misc5 : Form NUD_EntreeWhiteEXP.SetValueClamped(block.WhiteEXP); NUD_EntreeBlackEXP.SetValueClamped(block.BlackEXP); - string[] FMTitles = Enum.GetNames(typeof(Funfest5Mission)); + string[] FMTitles = Enum.GetNames(); LB_FunfestMissions.Items.Clear(); LB_FunfestMissions.Items.AddRange(FMTitles); @@ -498,7 +495,8 @@ public partial class SAV_Misc5 : Form private void LoadForest() { - Forest = SAV.EntreeData; + Forest = SAV.EntreeForest; + Forest.EnsureDecrypted(); AllSlots = Forest.Slots; NUD_Unlocked.SetValueClamped(Forest.Unlock38Areas + 2); CHK_Area9.Checked = Forest.Unlock9thArea; @@ -522,7 +520,6 @@ public partial class SAV_Misc5 : Form { Forest.Unlock38Areas = (int)NUD_Unlocked.Value - 2; Forest.Unlock9thArea = CHK_Area9.Checked; - SAV.EntreeData = Forest; } private IList CurrentSlots = null!; @@ -766,18 +763,23 @@ public partial class SAV_Misc5 : Form private void B_DumpFC_Click(object sender, EventArgs e) { + if (SAV is not SAV5BW bw) + return; using var sfd = new SaveFileDialog(); sfd.Filter = ForestCityBinFilter; sfd.FileName = string.Format(ForestCityBinPath, SAV.Version); if (sfd.ShowDialog() != DialogResult.OK) return; - var data = SAV.Data.AsSpan(ofsForestCity, ofsForestCitySize).ToArray(); + var data = bw.Forest.ForestCity.ToArray(); File.WriteAllBytes(sfd.FileName, data); } private void B_ImportFC_Click(object sender, EventArgs e) { + if (SAV is not SAV5BW bw) + return; + using var ofd = new OpenFileDialog(); ofd.Filter = ForestCityBinFilter; ofd.FileName = string.Format(ForestCityBinPath, SAV.Version); @@ -785,14 +787,14 @@ public partial class SAV_Misc5 : Form return; var fi = new FileInfo(ofd.FileName); - if (fi.Length != ofsForestCitySize) + if (fi.Length != WhiteBlack5BW.ForestCitySize) { - WinFormsUtil.Alert(string.Format(MessageStrings.MsgFileSizeIncorrect, fi.Length, ofsForestCitySize)); + WinFormsUtil.Alert(string.Format(MessageStrings.MsgFileSizeIncorrect, fi.Length, WhiteBlack5BW.ForestCitySize)); return; } var data = File.ReadAllBytes(ofd.FileName); - SAV.SetData(data, ofsForestCity); + bw.SetData(bw.Forest.ForestCity.Span, data); } private readonly string[] MedalNames = Util.GetStringList("medals", Main.CurrentLanguage); diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_BerryFieldXY.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_BerryFieldXY.cs index 4e9bd5811..52795d658 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_BerryFieldXY.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_BerryFieldXY.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Windows.Forms; using PKHeX.Core; using static System.Buffers.Binary.BinaryPrimitives; @@ -22,7 +22,7 @@ public partial class SAV_BerryFieldXY : Form // Change Berry Field // Gather Data - int ofs = SAV.BerryField + 0xC + (listBox1.SelectedIndex * 0x18); + int ofs = SAV6XY.BerryField + 0xC + (listBox1.SelectedIndex * 0x18); int berry = ReadUInt16LittleEndian(SAV.Data.AsSpan(ofs + (2 * 0))); int u1 = ReadUInt16LittleEndian(SAV.Data.AsSpan(ofs + (2 * 1))); int u2 = ReadUInt16LittleEndian(SAV.Data.AsSpan(ofs + (2 * 2))); diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_BoxLayout.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_BoxLayout.cs index 7943baa55..9a1543803 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_BoxLayout.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_BoxLayout.cs @@ -17,12 +17,10 @@ public partial class SAV_BoxLayout : Form SAV = (Origin = sav).Clone(); editing = true; - if (!SAV.HasBoxWallpapers) - CB_BG.Visible = PAN_BG.Visible = false; - else if (!LoadWallpaperNames()) // Repopulate Wallpaper names + bool any = LoadWallpapers(SAV); + any |= LoadBoxNames(SAV); + if (!any) WinFormsUtil.Error("Box layout is not supported for this game.", "Please close the window."); - - LoadBoxNames(); LoadFlags(); LoadUnlockedCount(); @@ -36,8 +34,15 @@ public partial class SAV_BoxLayout : Form editing = false; } - private bool LoadWallpaperNames() + private bool LoadWallpapers(SaveFile sav) { + if (sav is not IBoxDetailWallpaper) + { + Controls.Remove(CB_BG); + Controls.Remove(PAN_BG); + return false; + } + CB_BG.Items.Clear(); static void AddRange(ComboBox cb, ReadOnlySpan names) @@ -78,11 +83,19 @@ public partial class SAV_BoxLayout : Form } } - private void LoadBoxNames() + private bool LoadBoxNames(SaveFile sav) { + if (sav is not IBoxDetailNameRead r) + { + Controls.Remove(TB_BoxName); + return false; + } + if (sav is not IBoxDetailName) + TB_BoxName.Enabled = false; LB_BoxSelect.Items.Clear(); for (int i = 0; i < SAV.BoxCount; i++) - LB_BoxSelect.Items.Add(SAV.GetBoxName(i)); + LB_BoxSelect.Items.Add(r.GetBoxName(i)); + return true; } private void LoadUnlockedCount() @@ -133,8 +146,16 @@ public partial class SAV_BoxLayout : Form return; editing = true; - CB_BG.SelectedIndex = Math.Min(CB_BG.Items.Count - 1, SAV.GetBoxWallpaper(LB_BoxSelect.SelectedIndex)); - TB_BoxName.Text = SAV.GetBoxName(LB_BoxSelect.SelectedIndex); + var box = LB_BoxSelect.SelectedIndex; + if (SAV is IBoxDetailWallpaper wp) + { + var choice = wp.GetBoxWallpaper(box); + var maxWallpaper = CB_BG.Items.Count - 1; + CB_BG.SelectedIndex = Math.Clamp(choice, 0, maxWallpaper); + } + + if (SAV is IBoxDetailNameRead r) + TB_BoxName.Text = r.GetBoxName(LB_BoxSelect.SelectedIndex); editing = false; } @@ -145,15 +166,15 @@ public partial class SAV_BoxLayout : Form return; renamingBox = true; - SAV.SetBoxName(LB_BoxSelect.SelectedIndex, TB_BoxName.Text); - LB_BoxSelect.Items[LB_BoxSelect.SelectedIndex] = TB_BoxName.Text; + if (SAV is IBoxDetailName name) + { + name.SetBoxName(LB_BoxSelect.SelectedIndex, TB_BoxName.Text); + LB_BoxSelect.Items[LB_BoxSelect.SelectedIndex] = TB_BoxName.Text; + } renamingBox = false; } - private void B_Cancel_Click(object sender, EventArgs e) - { - Close(); - } + private void B_Cancel_Click(object sender, EventArgs e) => Close(); private void B_Save_Click(object sender, EventArgs e) { @@ -169,7 +190,10 @@ public partial class SAV_BoxLayout : Form private void ChangeBoxBackground(object sender, EventArgs e) { if (!editing) - SAV.SetBoxWallpaper(LB_BoxSelect.SelectedIndex, CB_BG.SelectedIndex); + { + if (SAV is IBoxDetailWallpaper wp) + wp.SetBoxWallpaper(LB_BoxSelect.SelectedIndex, CB_BG.SelectedIndex); + } PAN_BG.BackgroundImage = SAV.WallpaperImage(LB_BoxSelect.SelectedIndex); } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.cs index b7602af86..659bef3d4 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.cs @@ -16,7 +16,8 @@ public partial class SAV_HallOfFame : Form private bool editing; private readonly IReadOnlyList gendersymbols = Main.GenderSymbols; - private readonly byte[] data; + private readonly Memory Raw; + private Span Data => Raw.Span; private const int Count = 16; private const int StructureSize = 0x1B4; @@ -28,7 +29,7 @@ public partial class SAV_HallOfFame : Form WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage); SAV = (SAV6)(Origin = sav).Clone(); - data = SAV.Data.AsSpan(SAV.HoF, StructureTotal).ToArray(); // Copy HoF section of save into Data + Raw = SAV.Data.AsSpan(SAV.HoF, StructureTotal).ToArray(); // Copy HoF section of save into Data Setup(); LB_DataEntry.SelectedIndex = 0; NUP_PartyIndex_ValueChanged(this, EventArgs.Empty); @@ -68,7 +69,7 @@ public partial class SAV_HallOfFame : Form private void B_Close_Click(object sender, EventArgs e) { - Origin.SetData(data, SAV.HoF); + Origin.SetData(Data, SAV.HoF); Close(); } @@ -80,7 +81,7 @@ public partial class SAV_HallOfFame : Form int index = LB_DataEntry.SelectedIndex; int offset = index * StructureSize; - uint vnd = ReadUInt32LittleEndian(data.AsSpan(offset + 0x1B0)); + uint vnd = ReadUInt32LittleEndian(Data.Slice(offset + 0x1B0)); uint vn = vnd & 0xFF; TB_VN.Text = vn.ToString("000"); var s = new List { $"Entry #{vn}" }; @@ -125,7 +126,7 @@ public partial class SAV_HallOfFame : Form int moncount = 0; for (int i = 0; i < 6; i++) { - var entry = new HallFame6Entity(data.AsSpan(offset, HallFame6Entity.SIZE)); + var entry = new HallFame6Entity(Data.Slice(offset, HallFame6Entity.SIZE)); if (entry.Species == 0) continue; @@ -168,7 +169,7 @@ public partial class SAV_HallOfFame : Form if (offset < 0) return; - var entry = new HallFame6Entity(data.AsSpan(offset, HallFame6Entity.SIZE)); + var entry = new HallFame6Entity(Data.Slice(offset, HallFame6Entity.SIZE)); CB_Species.SelectedValue = (int)entry.Species; CB_HeldItem.SelectedValue = (int)entry.HeldItem; CB_Move1.SelectedValue = (int)entry.Move1; @@ -207,7 +208,7 @@ public partial class SAV_HallOfFame : Form int index = LB_DataEntry.SelectedIndex; int partymember = Convert.ToInt32(NUP_PartyIndex.Value) - 1; int offset = (index * StructureSize) + (partymember * HallFame6Entity.SIZE); - var span = data.AsSpan(offset, HallFame6Entity.SIZE); + var span = Data.Slice(offset, HallFame6Entity.SIZE); var entry = new HallFame6Entity(span) { Species = Convert.ToUInt16(CB_Species.SelectedValue), @@ -229,6 +230,7 @@ public partial class SAV_HallOfFame : Form OriginalTrainerGender = (uint)EntityGender.GetFromString(Label_OTGender.Text) & 1, }; + offset = index * StructureSize; uint vnd = 0; @@ -239,9 +241,9 @@ public partial class SAV_HallOfFame : Form date |= (uint)((CAL_MetDate.Value.Day & 0x1F) << 12); vnd |= (date & 0x1FFFF) << 14; //Fix for top bit - uint rawvnd = ReadUInt32LittleEndian(data.AsSpan(offset + 0x1B0)); + uint rawvnd = ReadUInt32LittleEndian(Data.Slice(offset + 0x1B0)); vnd |= rawvnd & 0x80000000; - WriteUInt32LittleEndian(data.AsSpan(offset + 0x1B0), vnd); + WriteUInt32LittleEndian(Data.Slice(offset + 0x1B0), vnd); var shiny = entry.IsShiny ? Shiny.Always : Shiny.Never; bpkx.Image = SpriteUtil.GetSprite(entry.Species, entry.Form, (byte)entry.Gender, 0, entry.HeldItem, false, shiny, EntityContext.Gen6); @@ -373,10 +375,15 @@ public partial class SAV_HallOfFame : Form int offset = index * StructureSize; if (index != 15) - Array.Copy(data, offset + StructureSize, data, offset, StructureSize * (Count - 1 - index)); + { + // Shift down + var dest = Data[offset..]; + var above = Data.Slice(offset + StructureSize, StructureSize * (Count - 1 - index)); + above.CopyTo(dest); + } // Ensure Last Entry is Cleared - data.AsSpan(StructureSize * (Count - 1), StructureSize).Clear(); + Data.Slice(StructureSize * (Count - 1), StructureSize).Clear(); DisplayEntry(LB_DataEntry, EventArgs.Empty); } @@ -390,7 +397,7 @@ public partial class SAV_HallOfFame : Form var team = LB_DataEntry.SelectedIndex; var member = (int)NUP_PartyIndex.Value - 1; int offset = (team * (4 + (6 * HallFame6Entity.SIZE))) + (member * HallFame6Entity.SIZE); - var nicktrash = data.AsSpan(offset + 0x18, 26); + var nicktrash = Data.Slice(0x18 + offset, 26); var text = tb.Text; SAV.SetString(nicktrash, text, 12, StringConverterOption.ClearZero); var d = new TrashEditor(tb, nicktrash, SAV); diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_OPower.Designer.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_OPower.Designer.cs index f613b02fb..8ad2ebc28 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_OPower.Designer.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_OPower.Designer.cs @@ -30,24 +30,110 @@ { B_Cancel = new System.Windows.Forms.Button(); B_Save = new System.Windows.Forms.Button(); - L_Type = new System.Windows.Forms.Label(); - CB_Type = new System.Windows.Forms.ComboBox(); - CB_Value = new System.Windows.Forms.ComboBox(); B_ClearAll = new System.Windows.Forms.Button(); B_GiveAll = new System.Windows.Forms.Button(); - B_GiveAllMAX = new System.Windows.Forms.Button(); - CHK_Master = new System.Windows.Forms.CheckBox(); - CHK_S = new System.Windows.Forms.CheckBox(); - CHK_MAX = new System.Windows.Forms.CheckBox(); + CLB_Unlock = new System.Windows.Forms.CheckedListBox(); + GB_Field = new System.Windows.Forms.GroupBox(); + L_F5 = new System.Windows.Forms.Label(); + L_F6 = new System.Windows.Forms.Label(); + L_F7 = new System.Windows.Forms.Label(); + L_F8 = new System.Windows.Forms.Label(); + L_F9 = new System.Windows.Forms.Label(); + L_F0 = new System.Windows.Forms.Label(); + L_F1 = new System.Windows.Forms.Label(); + L_F2 = new System.Windows.Forms.Label(); + L_F3 = new System.Windows.Forms.Label(); + L_F4 = new System.Windows.Forms.Label(); + NUD_F9B = new System.Windows.Forms.NumericUpDown(); + NUD_F9A = new System.Windows.Forms.NumericUpDown(); + NUD_F8B = new System.Windows.Forms.NumericUpDown(); + NUD_F8A = new System.Windows.Forms.NumericUpDown(); + NUD_F7B = new System.Windows.Forms.NumericUpDown(); + NUD_F7A = new System.Windows.Forms.NumericUpDown(); + NUD_F6B = new System.Windows.Forms.NumericUpDown(); + NUD_F6A = new System.Windows.Forms.NumericUpDown(); + NUD_F5B = new System.Windows.Forms.NumericUpDown(); + NUD_F5A = new System.Windows.Forms.NumericUpDown(); + NUD_F4B = new System.Windows.Forms.NumericUpDown(); + NUD_F4A = new System.Windows.Forms.NumericUpDown(); + NUD_F3B = new System.Windows.Forms.NumericUpDown(); + NUD_F3A = new System.Windows.Forms.NumericUpDown(); + NUD_F2B = new System.Windows.Forms.NumericUpDown(); + NUD_F2A = new System.Windows.Forms.NumericUpDown(); + NUD_F1B = new System.Windows.Forms.NumericUpDown(); + NUD_F1A = new System.Windows.Forms.NumericUpDown(); + NUD_F0B = new System.Windows.Forms.NumericUpDown(); + NUD_F0A = new System.Windows.Forms.NumericUpDown(); + GB_Battle = new System.Windows.Forms.GroupBox(); + L_B6 = new System.Windows.Forms.Label(); + L_B5 = new System.Windows.Forms.Label(); + L_B4 = new System.Windows.Forms.Label(); + L_B3 = new System.Windows.Forms.Label(); + L_B2 = new System.Windows.Forms.Label(); + L_B1 = new System.Windows.Forms.Label(); + L_B0 = new System.Windows.Forms.Label(); + NUD_B6B = new System.Windows.Forms.NumericUpDown(); + NUD_B6A = new System.Windows.Forms.NumericUpDown(); + NUD_B5B = new System.Windows.Forms.NumericUpDown(); + NUD_B5A = new System.Windows.Forms.NumericUpDown(); + NUD_B4B = new System.Windows.Forms.NumericUpDown(); + NUD_B4A = new System.Windows.Forms.NumericUpDown(); + NUD_B3B = new System.Windows.Forms.NumericUpDown(); + NUD_B3A = new System.Windows.Forms.NumericUpDown(); + NUD_B2B = new System.Windows.Forms.NumericUpDown(); + NUD_B2A = new System.Windows.Forms.NumericUpDown(); + NUD_B1B = new System.Windows.Forms.NumericUpDown(); + NUD_B1A = new System.Windows.Forms.NumericUpDown(); + NUD_B0B = new System.Windows.Forms.NumericUpDown(); + NUD_B0A = new System.Windows.Forms.NumericUpDown(); + NUD_Points = new System.Windows.Forms.NumericUpDown(); + L_Points = new System.Windows.Forms.Label(); + GB_Field.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)NUD_F9B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F9A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F8B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F8A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F7B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F7A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F6B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F6A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F5B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F5A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F4B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F4A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F3B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F3A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F2B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F2A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F1B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F1A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F0B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F0A).BeginInit(); + GB_Battle.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)NUD_B6B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B6A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B5B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B5A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B4B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B4A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B3B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B3A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B2B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B2A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B1B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B1A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B0B).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B0A).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_Points).BeginInit(); SuspendLayout(); // // B_Cancel // B_Cancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; - B_Cancel.Location = new System.Drawing.Point(250, 82); + B_Cancel.Location = new System.Drawing.Point(416, 289); B_Cancel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_Cancel.Name = "B_Cancel"; - B_Cancel.Size = new System.Drawing.Size(75, 27); + B_Cancel.Size = new System.Drawing.Size(88, 27); B_Cancel.TabIndex = 34; B_Cancel.Text = "Cancel"; B_Cancel.UseVisualStyleBackColor = true; @@ -56,50 +142,19 @@ // B_Save // B_Save.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; - B_Save.Location = new System.Drawing.Point(331, 82); + B_Save.Location = new System.Drawing.Point(512, 289); B_Save.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_Save.Name = "B_Save"; - B_Save.Size = new System.Drawing.Size(75, 27); + B_Save.Size = new System.Drawing.Size(88, 27); B_Save.TabIndex = 35; B_Save.Text = "Save"; B_Save.UseVisualStyleBackColor = true; B_Save.Click += B_Save_Click; // - // L_Type - // - L_Type.Location = new System.Drawing.Point(10, 15); - L_Type.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - L_Type.Name = "L_Type"; - L_Type.Size = new System.Drawing.Size(58, 23); - L_Type.TabIndex = 36; - L_Type.Text = "Type:"; - L_Type.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // CB_Type - // - CB_Type.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; - CB_Type.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - CB_Type.FormattingEnabled = true; - CB_Type.Location = new System.Drawing.Point(76, 16); - CB_Type.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - CB_Type.Name = "CB_Type"; - CB_Type.Size = new System.Drawing.Size(119, 23); - CB_Type.TabIndex = 37; - // - // CB_Value - // - CB_Value.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right; - CB_Value.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - CB_Value.FormattingEnabled = true; - CB_Value.Location = new System.Drawing.Point(203, 16); - CB_Value.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - CB_Value.Name = "CB_Value"; - CB_Value.Size = new System.Drawing.Size(74, 23); - CB_Value.TabIndex = 38; - // // B_ClearAll // - B_ClearAll.Location = new System.Drawing.Point(14, 82); + B_ClearAll.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + B_ClearAll.Location = new System.Drawing.Point(416, 256); B_ClearAll.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_ClearAll.Name = "B_ClearAll"; B_ClearAll.Size = new System.Drawing.Size(88, 27); @@ -109,7 +164,8 @@ // // B_GiveAll // - B_GiveAll.Location = new System.Drawing.Point(14, 47); + B_GiveAll.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + B_GiveAll.Location = new System.Drawing.Point(512, 256); B_GiveAll.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_GiveAll.Name = "B_GiveAll"; B_GiveAll.Size = new System.Drawing.Size(88, 27); @@ -117,63 +173,571 @@ B_GiveAll.Text = "Give All"; B_GiveAll.UseVisualStyleBackColor = true; // - // B_GiveAllMAX + // CLB_Unlock // - B_GiveAllMAX.Location = new System.Drawing.Point(108, 47); - B_GiveAllMAX.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - B_GiveAllMAX.Name = "B_GiveAllMAX"; - B_GiveAllMAX.Size = new System.Drawing.Size(88, 27); - B_GiveAllMAX.TabIndex = 41; - B_GiveAllMAX.Text = "MAX All"; - B_GiveAllMAX.UseVisualStyleBackColor = true; + CLB_Unlock.FormattingEnabled = true; + CLB_Unlock.Location = new System.Drawing.Point(14, 12); + CLB_Unlock.Name = "CLB_Unlock"; + CLB_Unlock.Size = new System.Drawing.Size(184, 310); + CLB_Unlock.TabIndex = 41; // - // CHK_Master + // GB_Field // - CHK_Master.AutoSize = true; - CHK_Master.CheckAlign = System.Drawing.ContentAlignment.MiddleRight; - CHK_Master.Location = new System.Drawing.Point(118, 87); - CHK_Master.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - CHK_Master.Name = "CHK_Master"; - CHK_Master.Size = new System.Drawing.Size(66, 19); - CHK_Master.TabIndex = 42; - CHK_Master.Text = "??? Flag"; - CHK_Master.UseVisualStyleBackColor = true; + GB_Field.Controls.Add(L_F5); + GB_Field.Controls.Add(L_F6); + GB_Field.Controls.Add(L_F7); + GB_Field.Controls.Add(L_F8); + GB_Field.Controls.Add(L_F9); + GB_Field.Controls.Add(L_F0); + GB_Field.Controls.Add(L_F1); + GB_Field.Controls.Add(L_F2); + GB_Field.Controls.Add(L_F3); + GB_Field.Controls.Add(L_F4); + GB_Field.Controls.Add(NUD_F9B); + GB_Field.Controls.Add(NUD_F9A); + GB_Field.Controls.Add(NUD_F8B); + GB_Field.Controls.Add(NUD_F8A); + GB_Field.Controls.Add(NUD_F7B); + GB_Field.Controls.Add(NUD_F7A); + GB_Field.Controls.Add(NUD_F6B); + GB_Field.Controls.Add(NUD_F6A); + GB_Field.Controls.Add(NUD_F5B); + GB_Field.Controls.Add(NUD_F5A); + GB_Field.Controls.Add(NUD_F4B); + GB_Field.Controls.Add(NUD_F4A); + GB_Field.Controls.Add(NUD_F3B); + GB_Field.Controls.Add(NUD_F3A); + GB_Field.Controls.Add(NUD_F2B); + GB_Field.Controls.Add(NUD_F2A); + GB_Field.Controls.Add(NUD_F1B); + GB_Field.Controls.Add(NUD_F1A); + GB_Field.Controls.Add(NUD_F0B); + GB_Field.Controls.Add(NUD_F0A); + GB_Field.Location = new System.Drawing.Point(204, 4); + GB_Field.Name = "GB_Field"; + GB_Field.Size = new System.Drawing.Size(200, 312); + GB_Field.TabIndex = 42; + GB_Field.TabStop = false; + GB_Field.Text = "Field"; // - // CHK_S + // L_F5 // - CHK_S.AutoSize = true; - CHK_S.Location = new System.Drawing.Point(285, 18); - CHK_S.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - CHK_S.Name = "CHK_S"; - CHK_S.Size = new System.Drawing.Size(32, 19); - CHK_S.TabIndex = 43; - CHK_S.Text = "S"; - CHK_S.UseVisualStyleBackColor = true; + L_F5.Location = new System.Drawing.Point(6, 161); + L_F5.Name = "L_F5"; + L_F5.Size = new System.Drawing.Size(80, 23); + L_F5.TabIndex = 75; + L_F5.Text = "label13"; + L_F5.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // - // CHK_MAX + // L_F6 // - CHK_MAX.AutoSize = true; - CHK_MAX.Location = new System.Drawing.Point(330, 18); - CHK_MAX.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - CHK_MAX.Name = "CHK_MAX"; - CHK_MAX.Size = new System.Drawing.Size(52, 19); - CHK_MAX.TabIndex = 44; - CHK_MAX.Text = "MAX"; - CHK_MAX.UseVisualStyleBackColor = true; + L_F6.Location = new System.Drawing.Point(6, 190); + L_F6.Name = "L_F6"; + L_F6.Size = new System.Drawing.Size(80, 23); + L_F6.TabIndex = 74; + L_F6.Text = "label14"; + L_F6.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_F7 + // + L_F7.Location = new System.Drawing.Point(6, 219); + L_F7.Name = "L_F7"; + L_F7.Size = new System.Drawing.Size(80, 23); + L_F7.TabIndex = 73; + L_F7.Text = "label15"; + L_F7.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_F8 + // + L_F8.Location = new System.Drawing.Point(6, 248); + L_F8.Name = "L_F8"; + L_F8.Size = new System.Drawing.Size(80, 23); + L_F8.TabIndex = 72; + L_F8.Text = "label16"; + L_F8.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_F9 + // + L_F9.Location = new System.Drawing.Point(6, 277); + L_F9.Name = "L_F9"; + L_F9.Size = new System.Drawing.Size(80, 23); + L_F9.TabIndex = 71; + L_F9.Text = "label17"; + L_F9.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_F0 + // + L_F0.Location = new System.Drawing.Point(6, 16); + L_F0.Name = "L_F0"; + L_F0.Size = new System.Drawing.Size(80, 23); + L_F0.TabIndex = 70; + L_F0.Text = "label8"; + L_F0.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_F1 + // + L_F1.Location = new System.Drawing.Point(6, 45); + L_F1.Name = "L_F1"; + L_F1.Size = new System.Drawing.Size(80, 23); + L_F1.TabIndex = 69; + L_F1.Text = "label9"; + L_F1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_F2 + // + L_F2.Location = new System.Drawing.Point(6, 74); + L_F2.Name = "L_F2"; + L_F2.Size = new System.Drawing.Size(80, 23); + L_F2.TabIndex = 68; + L_F2.Text = "label10"; + L_F2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_F3 + // + L_F3.Location = new System.Drawing.Point(6, 103); + L_F3.Name = "L_F3"; + L_F3.Size = new System.Drawing.Size(80, 23); + L_F3.TabIndex = 67; + L_F3.Text = "label11"; + L_F3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_F4 + // + L_F4.Location = new System.Drawing.Point(6, 132); + L_F4.Name = "L_F4"; + L_F4.Size = new System.Drawing.Size(80, 23); + L_F4.TabIndex = 66; + L_F4.Text = "label12"; + L_F4.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // NUD_F9B + // + NUD_F9B.Location = new System.Drawing.Point(136, 277); + NUD_F9B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F9B.Name = "NUD_F9B"; + NUD_F9B.Size = new System.Drawing.Size(40, 23); + NUD_F9B.TabIndex = 62; + NUD_F9B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F9A + // + NUD_F9A.Location = new System.Drawing.Point(89, 277); + NUD_F9A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F9A.Name = "NUD_F9A"; + NUD_F9A.Size = new System.Drawing.Size(40, 23); + NUD_F9A.TabIndex = 62; + NUD_F9A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F8B + // + NUD_F8B.Location = new System.Drawing.Point(136, 248); + NUD_F8B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F8B.Name = "NUD_F8B"; + NUD_F8B.Size = new System.Drawing.Size(40, 23); + NUD_F8B.TabIndex = 61; + NUD_F8B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F8A + // + NUD_F8A.Location = new System.Drawing.Point(89, 248); + NUD_F8A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F8A.Name = "NUD_F8A"; + NUD_F8A.Size = new System.Drawing.Size(40, 23); + NUD_F8A.TabIndex = 61; + NUD_F8A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F7B + // + NUD_F7B.Location = new System.Drawing.Point(136, 219); + NUD_F7B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F7B.Name = "NUD_F7B"; + NUD_F7B.Size = new System.Drawing.Size(40, 23); + NUD_F7B.TabIndex = 60; + NUD_F7B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F7A + // + NUD_F7A.Location = new System.Drawing.Point(89, 219); + NUD_F7A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F7A.Name = "NUD_F7A"; + NUD_F7A.Size = new System.Drawing.Size(40, 23); + NUD_F7A.TabIndex = 60; + NUD_F7A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F6B + // + NUD_F6B.Location = new System.Drawing.Point(136, 190); + NUD_F6B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F6B.Name = "NUD_F6B"; + NUD_F6B.Size = new System.Drawing.Size(40, 23); + NUD_F6B.TabIndex = 59; + NUD_F6B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F6A + // + NUD_F6A.Location = new System.Drawing.Point(89, 190); + NUD_F6A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F6A.Name = "NUD_F6A"; + NUD_F6A.Size = new System.Drawing.Size(40, 23); + NUD_F6A.TabIndex = 59; + NUD_F6A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F5B + // + NUD_F5B.Location = new System.Drawing.Point(136, 161); + NUD_F5B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F5B.Name = "NUD_F5B"; + NUD_F5B.Size = new System.Drawing.Size(40, 23); + NUD_F5B.TabIndex = 58; + NUD_F5B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F5A + // + NUD_F5A.Location = new System.Drawing.Point(89, 161); + NUD_F5A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F5A.Name = "NUD_F5A"; + NUD_F5A.Size = new System.Drawing.Size(40, 23); + NUD_F5A.TabIndex = 58; + NUD_F5A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F4B + // + NUD_F4B.Location = new System.Drawing.Point(136, 132); + NUD_F4B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F4B.Name = "NUD_F4B"; + NUD_F4B.Size = new System.Drawing.Size(40, 23); + NUD_F4B.TabIndex = 57; + NUD_F4B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F4A + // + NUD_F4A.Location = new System.Drawing.Point(89, 132); + NUD_F4A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F4A.Name = "NUD_F4A"; + NUD_F4A.Size = new System.Drawing.Size(40, 23); + NUD_F4A.TabIndex = 57; + NUD_F4A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F3B + // + NUD_F3B.Location = new System.Drawing.Point(136, 103); + NUD_F3B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F3B.Name = "NUD_F3B"; + NUD_F3B.Size = new System.Drawing.Size(40, 23); + NUD_F3B.TabIndex = 56; + NUD_F3B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F3A + // + NUD_F3A.Location = new System.Drawing.Point(89, 103); + NUD_F3A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F3A.Name = "NUD_F3A"; + NUD_F3A.Size = new System.Drawing.Size(40, 23); + NUD_F3A.TabIndex = 56; + NUD_F3A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F2B + // + NUD_F2B.Location = new System.Drawing.Point(136, 74); + NUD_F2B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F2B.Name = "NUD_F2B"; + NUD_F2B.Size = new System.Drawing.Size(40, 23); + NUD_F2B.TabIndex = 55; + NUD_F2B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F2A + // + NUD_F2A.Location = new System.Drawing.Point(89, 74); + NUD_F2A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F2A.Name = "NUD_F2A"; + NUD_F2A.Size = new System.Drawing.Size(40, 23); + NUD_F2A.TabIndex = 55; + NUD_F2A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F1B + // + NUD_F1B.Location = new System.Drawing.Point(136, 45); + NUD_F1B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F1B.Name = "NUD_F1B"; + NUD_F1B.Size = new System.Drawing.Size(40, 23); + NUD_F1B.TabIndex = 54; + NUD_F1B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F1A + // + NUD_F1A.Location = new System.Drawing.Point(89, 45); + NUD_F1A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F1A.Name = "NUD_F1A"; + NUD_F1A.Size = new System.Drawing.Size(40, 23); + NUD_F1A.TabIndex = 54; + NUD_F1A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F0B + // + NUD_F0B.Location = new System.Drawing.Point(136, 16); + NUD_F0B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F0B.Name = "NUD_F0B"; + NUD_F0B.Size = new System.Drawing.Size(40, 23); + NUD_F0B.TabIndex = 53; + NUD_F0B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_F0A + // + NUD_F0A.Location = new System.Drawing.Point(89, 16); + NUD_F0A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_F0A.Name = "NUD_F0A"; + NUD_F0A.Size = new System.Drawing.Size(40, 23); + NUD_F0A.TabIndex = 53; + NUD_F0A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // GB_Battle + // + GB_Battle.Controls.Add(L_B6); + GB_Battle.Controls.Add(L_B5); + GB_Battle.Controls.Add(L_B4); + GB_Battle.Controls.Add(L_B3); + GB_Battle.Controls.Add(L_B2); + GB_Battle.Controls.Add(L_B1); + GB_Battle.Controls.Add(L_B0); + GB_Battle.Controls.Add(NUD_B6B); + GB_Battle.Controls.Add(NUD_B6A); + GB_Battle.Controls.Add(NUD_B5B); + GB_Battle.Controls.Add(NUD_B5A); + GB_Battle.Controls.Add(NUD_B4B); + GB_Battle.Controls.Add(NUD_B4A); + GB_Battle.Controls.Add(NUD_B3B); + GB_Battle.Controls.Add(NUD_B3A); + GB_Battle.Controls.Add(NUD_B2B); + GB_Battle.Controls.Add(NUD_B2A); + GB_Battle.Controls.Add(NUD_B1B); + GB_Battle.Controls.Add(NUD_B1A); + GB_Battle.Controls.Add(NUD_B0B); + GB_Battle.Controls.Add(NUD_B0A); + GB_Battle.Location = new System.Drawing.Point(413, 4); + GB_Battle.Name = "GB_Battle"; + GB_Battle.Size = new System.Drawing.Size(184, 220); + GB_Battle.TabIndex = 64; + GB_Battle.TabStop = false; + GB_Battle.Text = "Battle"; + // + // L_B6 + // + L_B6.Location = new System.Drawing.Point(8, 190); + L_B6.Name = "L_B6"; + L_B6.Size = new System.Drawing.Size(80, 23); + L_B6.TabIndex = 67; + L_B6.Text = "label7"; + L_B6.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_B5 + // + L_B5.Location = new System.Drawing.Point(8, 161); + L_B5.Name = "L_B5"; + L_B5.Size = new System.Drawing.Size(80, 23); + L_B5.TabIndex = 66; + L_B5.Text = "label6"; + L_B5.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_B4 + // + L_B4.Location = new System.Drawing.Point(8, 132); + L_B4.Name = "L_B4"; + L_B4.Size = new System.Drawing.Size(80, 23); + L_B4.TabIndex = 65; + L_B4.Text = "label5"; + L_B4.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_B3 + // + L_B3.Location = new System.Drawing.Point(8, 103); + L_B3.Name = "L_B3"; + L_B3.Size = new System.Drawing.Size(80, 23); + L_B3.TabIndex = 64; + L_B3.Text = "label4"; + L_B3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_B2 + // + L_B2.Location = new System.Drawing.Point(8, 74); + L_B2.Name = "L_B2"; + L_B2.Size = new System.Drawing.Size(80, 23); + L_B2.TabIndex = 63; + L_B2.Text = "label3"; + L_B2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_B1 + // + L_B1.Location = new System.Drawing.Point(8, 45); + L_B1.Name = "L_B1"; + L_B1.Size = new System.Drawing.Size(80, 23); + L_B1.TabIndex = 62; + L_B1.Text = "label2"; + L_B1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_B0 + // + L_B0.Location = new System.Drawing.Point(8, 16); + L_B0.Name = "L_B0"; + L_B0.Size = new System.Drawing.Size(80, 23); + L_B0.TabIndex = 61; + L_B0.Text = "label1"; + L_B0.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // NUD_B6B + // + NUD_B6B.Location = new System.Drawing.Point(135, 190); + NUD_B6B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B6B.Name = "NUD_B6B"; + NUD_B6B.Size = new System.Drawing.Size(40, 23); + NUD_B6B.TabIndex = 59; + NUD_B6B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_B6A + // + NUD_B6A.Location = new System.Drawing.Point(89, 190); + NUD_B6A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B6A.Name = "NUD_B6A"; + NUD_B6A.Size = new System.Drawing.Size(40, 23); + NUD_B6A.TabIndex = 59; + NUD_B6A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_B5B + // + NUD_B5B.Location = new System.Drawing.Point(135, 161); + NUD_B5B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B5B.Name = "NUD_B5B"; + NUD_B5B.Size = new System.Drawing.Size(40, 23); + NUD_B5B.TabIndex = 58; + NUD_B5B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_B5A + // + NUD_B5A.Location = new System.Drawing.Point(89, 161); + NUD_B5A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B5A.Name = "NUD_B5A"; + NUD_B5A.Size = new System.Drawing.Size(40, 23); + NUD_B5A.TabIndex = 58; + NUD_B5A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_B4B + // + NUD_B4B.Location = new System.Drawing.Point(135, 132); + NUD_B4B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B4B.Name = "NUD_B4B"; + NUD_B4B.Size = new System.Drawing.Size(40, 23); + NUD_B4B.TabIndex = 57; + NUD_B4B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_B4A + // + NUD_B4A.Location = new System.Drawing.Point(89, 132); + NUD_B4A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B4A.Name = "NUD_B4A"; + NUD_B4A.Size = new System.Drawing.Size(40, 23); + NUD_B4A.TabIndex = 57; + NUD_B4A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_B3B + // + NUD_B3B.Location = new System.Drawing.Point(135, 103); + NUD_B3B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B3B.Name = "NUD_B3B"; + NUD_B3B.Size = new System.Drawing.Size(40, 23); + NUD_B3B.TabIndex = 56; + NUD_B3B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_B3A + // + NUD_B3A.Location = new System.Drawing.Point(89, 103); + NUD_B3A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B3A.Name = "NUD_B3A"; + NUD_B3A.Size = new System.Drawing.Size(40, 23); + NUD_B3A.TabIndex = 56; + NUD_B3A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_B2B + // + NUD_B2B.Location = new System.Drawing.Point(135, 74); + NUD_B2B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B2B.Name = "NUD_B2B"; + NUD_B2B.Size = new System.Drawing.Size(40, 23); + NUD_B2B.TabIndex = 55; + NUD_B2B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_B2A + // + NUD_B2A.Location = new System.Drawing.Point(89, 74); + NUD_B2A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B2A.Name = "NUD_B2A"; + NUD_B2A.Size = new System.Drawing.Size(40, 23); + NUD_B2A.TabIndex = 55; + NUD_B2A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_B1B + // + NUD_B1B.Location = new System.Drawing.Point(135, 45); + NUD_B1B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B1B.Name = "NUD_B1B"; + NUD_B1B.Size = new System.Drawing.Size(40, 23); + NUD_B1B.TabIndex = 54; + NUD_B1B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_B1A + // + NUD_B1A.Location = new System.Drawing.Point(89, 45); + NUD_B1A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B1A.Name = "NUD_B1A"; + NUD_B1A.Size = new System.Drawing.Size(40, 23); + NUD_B1A.TabIndex = 54; + NUD_B1A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_B0B + // + NUD_B0B.Location = new System.Drawing.Point(135, 16); + NUD_B0B.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B0B.Name = "NUD_B0B"; + NUD_B0B.Size = new System.Drawing.Size(40, 23); + NUD_B0B.TabIndex = 53; + NUD_B0B.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // NUD_B0A + // + NUD_B0A.Location = new System.Drawing.Point(89, 16); + NUD_B0A.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_B0A.Name = "NUD_B0A"; + NUD_B0A.Size = new System.Drawing.Size(40, 23); + NUD_B0A.TabIndex = 53; + NUD_B0A.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // numericUpDown1 + // + NUD_Points.Location = new System.Drawing.Point(557, 228); + NUD_Points.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_Points.Name = "NUD_Points"; + NUD_Points.Size = new System.Drawing.Size(40, 23); + NUD_Points.TabIndex = 68; + NUD_Points.Value = new decimal(new int[] { 255, 0, 0, 0 }); + // + // L_Points + // + L_Points.Location = new System.Drawing.Point(471, 228); + L_Points.Name = "L_Points"; + L_Points.Size = new System.Drawing.Size(80, 23); + L_Points.TabIndex = 69; + L_Points.Text = "Points:"; + L_Points.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // SAV_OPower // AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit; - ClientSize = new System.Drawing.Size(414, 122); - Controls.Add(CHK_MAX); - Controls.Add(CHK_S); - Controls.Add(CHK_Master); - Controls.Add(B_GiveAllMAX); + ClientSize = new System.Drawing.Size(608, 329); + Controls.Add(L_Points); + Controls.Add(NUD_Points); + Controls.Add(GB_Battle); + Controls.Add(GB_Field); + Controls.Add(CLB_Unlock); Controls.Add(B_GiveAll); Controls.Add(B_ClearAll); - Controls.Add(CB_Value); - Controls.Add(CB_Type); - Controls.Add(L_Type); Controls.Add(B_Save); Controls.Add(B_Cancel); FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; @@ -184,21 +748,106 @@ Name = "SAV_OPower"; StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; Text = "O-Power Editor"; + GB_Field.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)NUD_F9B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F9A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F8B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F8A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F7B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F7A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F6B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F6A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F5B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F5A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F4B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F4A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F3B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F3A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F2B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F2A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F1B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F1A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F0B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_F0A).EndInit(); + GB_Battle.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)NUD_B6B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B6A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B5B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B5A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B4B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B4A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B3B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B3A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B2B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B2A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B1B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B1A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B0B).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_B0A).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_Points).EndInit(); ResumeLayout(false); - PerformLayout(); } #endregion private System.Windows.Forms.Button B_Cancel; private System.Windows.Forms.Button B_Save; - private System.Windows.Forms.Label L_Type; - private System.Windows.Forms.ComboBox CB_Type; - private System.Windows.Forms.ComboBox CB_Value; private System.Windows.Forms.Button B_ClearAll; private System.Windows.Forms.Button B_GiveAll; - private System.Windows.Forms.Button B_GiveAllMAX; - private System.Windows.Forms.CheckBox CHK_Master; - private System.Windows.Forms.CheckBox CHK_S; - private System.Windows.Forms.CheckBox CHK_MAX; + private System.Windows.Forms.CheckedListBox CLB_Unlock; + private System.Windows.Forms.GroupBox GB_Field; + private System.Windows.Forms.NumericUpDown NUD_F9B; + private System.Windows.Forms.NumericUpDown NUD_F9A; + private System.Windows.Forms.NumericUpDown NUD_F8B; + private System.Windows.Forms.NumericUpDown NUD_F8A; + private System.Windows.Forms.NumericUpDown NUD_F7B; + private System.Windows.Forms.NumericUpDown NUD_F7A; + private System.Windows.Forms.NumericUpDown NUD_F6B; + private System.Windows.Forms.NumericUpDown NUD_F6A; + private System.Windows.Forms.NumericUpDown NUD_F5B; + private System.Windows.Forms.NumericUpDown NUD_F5A; + private System.Windows.Forms.NumericUpDown NUD_F4B; + private System.Windows.Forms.NumericUpDown NUD_F4A; + private System.Windows.Forms.NumericUpDown NUD_F3B; + private System.Windows.Forms.NumericUpDown NUD_F3A; + private System.Windows.Forms.NumericUpDown NUD_F2B; + private System.Windows.Forms.NumericUpDown NUD_F2A; + private System.Windows.Forms.NumericUpDown NUD_F1B; + private System.Windows.Forms.NumericUpDown NUD_F1A; + private System.Windows.Forms.NumericUpDown NUD_F0B; + private System.Windows.Forms.NumericUpDown NUD_F0A; + private System.Windows.Forms.GroupBox GB_Battle; + private System.Windows.Forms.NumericUpDown NUD_B6B; + private System.Windows.Forms.NumericUpDown NUD_B6A; + private System.Windows.Forms.NumericUpDown NUD_B5B; + private System.Windows.Forms.NumericUpDown NUD_B5A; + private System.Windows.Forms.NumericUpDown NUD_B4B; + private System.Windows.Forms.NumericUpDown NUD_B4A; + private System.Windows.Forms.NumericUpDown NUD_B3B; + private System.Windows.Forms.NumericUpDown NUD_B3A; + private System.Windows.Forms.NumericUpDown NUD_B2B; + private System.Windows.Forms.NumericUpDown NUD_B2A; + private System.Windows.Forms.NumericUpDown NUD_B1B; + private System.Windows.Forms.NumericUpDown NUD_B1A; + private System.Windows.Forms.NumericUpDown NUD_B0B; + private System.Windows.Forms.NumericUpDown NUD_B0A; + private System.Windows.Forms.Label L_B6; + private System.Windows.Forms.Label L_B5; + private System.Windows.Forms.Label L_B4; + private System.Windows.Forms.Label L_B3; + private System.Windows.Forms.Label L_B2; + private System.Windows.Forms.Label L_B1; + private System.Windows.Forms.Label L_B0; + private System.Windows.Forms.Label L_F5; + private System.Windows.Forms.Label L_F6; + private System.Windows.Forms.Label L_F7; + private System.Windows.Forms.Label L_F8; + private System.Windows.Forms.Label L_F9; + private System.Windows.Forms.Label L_F0; + private System.Windows.Forms.Label L_F1; + private System.Windows.Forms.Label L_F2; + private System.Windows.Forms.Label L_F3; + private System.Windows.Forms.Label L_F4; + private System.Windows.Forms.NumericUpDown NUD_Points; + private System.Windows.Forms.Label L_Points; } } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_OPower.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_OPower.cs index 203641f4f..395f08a9b 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_OPower.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_OPower.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Windows.Forms; using PKHeX.Core; @@ -8,7 +8,12 @@ public partial class SAV_OPower : Form { private readonly SaveFile Origin; private readonly SaveFile SAV; - private readonly OPower6 Data; + private readonly OPower6 Block; + + private readonly NumericUpDown[] NUDField_A; + private readonly NumericUpDown[] NUDField_B; + private readonly NumericUpDown[] NUDBattle_A; + private readonly NumericUpDown[] NUDBattle_B; public SAV_OPower(ISaveBlock6Main sav) { @@ -16,63 +21,72 @@ public partial class SAV_OPower : Form WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage); Origin = (SaveFile)sav; SAV = Origin.Clone(); - Data = ((ISaveBlock6Main)SAV).OPower; + Block = ((ISaveBlock6Main)SAV).OPower; + + Label[] LabelField = [L_F0, L_F1, L_F2, L_F3, L_F4, L_F5, L_F6, L_F7, L_F8, L_F9]; + Label[] LabelBattle = [L_B0, L_B1, L_B2, L_B3, L_B4, L_B5, L_B6]; + NUDField_A = [NUD_F0A, NUD_F1A, NUD_F2A, NUD_F3A, NUD_F4A, NUD_F5A, NUD_F6A, NUD_F7A, NUD_F8A, NUD_F9A]; + NUDField_B = [NUD_F0B, NUD_F1B, NUD_F2B, NUD_F3B, NUD_F4B, NUD_F5B, NUD_F6B, NUD_F7B, NUD_F8B, NUD_F9B]; + NUDBattle_A = [NUD_B0A, NUD_B1A, NUD_B2A, NUD_B3A, NUD_B4A, NUD_B5A, NUD_B6A]; + NUDBattle_B = [NUD_B0B, NUD_B1B, NUD_B2B, NUD_B3B, NUD_B4B, NUD_B5B, NUD_B6B]; + + // get names, without the "Count" enum value at the end. + var nameIndex = Enum.GetNames().AsSpan()[..^1]; + var nameField = Enum.GetNames().AsSpan()[..^1]; + var nameBattle = Enum.GetNames().AsSpan()[..^1]; + foreach (string index in nameIndex) + CLB_Unlock.Items.Add(index); + for (int i = 0; i < nameField.Length; i++) + LabelField[i].Text = nameField[i]; + for (int i = 0; i < nameBattle.Length; i++) + LabelBattle[i].Text = nameBattle[i]; - Current = Types[0]; - foreach (var z in Types) - CB_Type.Items.Add(z.ToString()); - CB_Type.SelectedIndex = 0; - CHK_Master.Checked = Data.MasterFlag; LoadCurrent(); - CB_Type.SelectedIndexChanged += (s, e) => { SaveCurrent(); LoadCurrent(); }; - B_ClearAll.Click += (s, e) => { Data.ClearAll(); LoadCurrent(); }; - B_GiveAll.Click += (s, e) => { Data.UnlockRegular(); LoadCurrent(); }; - B_GiveAllMAX.Click += (s, e) => { Data.UnlockAll(); LoadCurrent(); }; + B_ClearAll.Click += (_, _) => { Block.ClearAll(); LoadCurrent(); }; + B_GiveAll.Click += (_, _) => { Block.UnlockAll(); LoadCurrent(); }; } private void B_Cancel_Click(object sender, EventArgs e) => Close(); private void B_Save_Click(object sender, EventArgs e) { - SaveData(); - Close(); - } - - private static readonly OPower6Type[] Types = (OPower6Type[])Enum.GetValues(typeof(OPower6Type)); - private static readonly string[] Values = Enum.GetNames(typeof(OPower6Value)); - - private OPower6Type Current; - - private void SaveData() - { - Data.MasterFlag = CHK_Master.Checked; SaveCurrent(); - Origin.Data = SAV.Data; - Origin.State.Edited = true; + Origin.CopyChangesFrom(SAV); + Close(); } private void LoadCurrent() { - Current = Types[CB_Type.SelectedIndex]; - - CB_Value.Items.Clear(); - int count = OPower6.GetOPowerCount(Current); - for (int i = 0; i <= count; i++) - CB_Value.Items.Add(Values[i]); - - CB_Value.SelectedIndex = Data.GetOPowerLevel(Current); - - CHK_S.Enabled = OPower6.GetHasOPowerS(Current); - CHK_S.Checked = Data.GetOPowerS(Current); - CHK_MAX.Enabled = OPower6.GetHasOPowerMAX(Current); - CHK_MAX.Checked = Data.GetOPowerMAX(Current); + for (int i = 0; i < CLB_Unlock.Items.Count; i++) + CLB_Unlock.SetItemChecked(i, Block.GetState((OPower6Index)i) == OPowerFlagState.Unlocked); + for (int i = 0; i < NUDField_A.Length; i++) + { + NUDField_A[i].Value = Block.GetLevel1((OPower6FieldType)i); + NUDField_B[i].Value = Block.GetLevel2((OPower6FieldType)i); + } + for (int i = 0; i < NUDBattle_A.Length; i++) + { + NUDBattle_A[i].Value = Block.GetLevel1((OPower6BattleType)i); + NUDBattle_B[i].Value = Block.GetLevel2((OPower6BattleType)i); + } + NUD_Points.Value = Block.Points; } private void SaveCurrent() { - Data.SetOPowerLevel(Current, CB_Value.SelectedIndex); - Data.SetOPowerS(Current, CHK_S.Checked); - Data.SetOPowerMAX(Current, CHK_MAX.Checked); + for (int i = 0; i < CLB_Unlock.Items.Count; i++) + Block.SetState((OPower6Index)i, CLB_Unlock.GetItemChecked(i) ? OPowerFlagState.Unlocked : OPowerFlagState.Locked); + for (int i = 0; i < NUDField_A.Length; i++) + { + Block.SetLevel1((OPower6FieldType)i, (byte)NUDField_A[i].Value); + Block.SetLevel2((OPower6FieldType)i, (byte)NUDField_B[i].Value); + } + for (int i = 0; i < NUDBattle_A.Length; i++) + { + Block.SetLevel1((OPower6BattleType)i, (byte)NUDBattle_A[i].Value); + Block.SetLevel2((OPower6BattleType)i, (byte)NUDBattle_B[i].Value); + } + Block.Points = (byte)NUD_Points.Value; } } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_PokeBlockORAS.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_PokeBlockORAS.cs index f0f85decd..33c6e85fc 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_PokeBlockORAS.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_PokeBlockORAS.cs @@ -42,27 +42,13 @@ public partial class SAV_PokeBlockORAS : Form Close(); } - private static ReadOnlySpan DefaultBerryTree => [ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x40, 0x01, 0x00, 0x00, 0x00 ]; - private void B_RandomizeBerries_Click(object sender, EventArgs e) { if (DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, "Repopulate all berry plots with random berries?")) return; // Randomize the trees. - ReadOnlySpan tree = DefaultBerryTree; - var plantable = ItemStorage6XY.Pouch_Berry_XY; // 0 index is None, skip with rand - var rnd = Util.Rand; - - var plots = SAV.Data.AsSpan(SAV.BerryField); - for (int i = 0; i < 90; i++) // amount of plots in the game - { - var plot = plots[(i * 0x10)..]; - tree.CopyTo(plot); // put tree into plot - - ushort berry = plantable[rnd.Next(1, plantable.Length)]; // get random berry item ID from list - WriteUInt16LittleEndian(plot[6..], berry); // put berry into tree. - } + SAV.BerryField.ResetAndRandomize(Util.Rand, ItemStorage6XY.Pouch_Berry_XY); } private void B_GiveAllBlocks_Click(object sender, EventArgs e) diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_Roamer6.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_Roamer6.cs index f145f3fe1..dfd1a89fd 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_Roamer6.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_Roamer6.cs @@ -33,7 +33,7 @@ public partial class SAV_Roamer6 : Form if (roamer.Species != 0) return roamer.Species - SpeciesOffset; // Roamer Species is not set if the player hasn't beaten the league so derive the species from the starter choice - return sav.GetWork(StarterChoiceIndex); + return sav.EventWork.GetWork(StarterChoiceIndex); } private void CB_Species_SelectedIndexChanged(object sender, EventArgs e) => roamer.Species = (ushort)(SpeciesOffset + CB_Species.SelectedIndex); diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_Trainer.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_Trainer.cs index 746054f76..066739b27 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_Trainer.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_Trainer.cs @@ -81,13 +81,12 @@ public partial class SAV_Trainer : Form CB_Region.InitializeBinding(); Main.SetCountrySubRegion(CB_Country, "countries"); - var names = Enum.GetNames(typeof(TrainerSprite6)); - var values = (int[])Enum.GetValues(typeof(TrainerSprite6)); - var data = names.Zip(values, (a, b) => new ComboItem(a, b)) - .ToList(); - if (SAV is not SAV6AO) - data.RemoveAll(z => z.Value > 36); - + var names = Enum.GetNames(); + var values = Enum.GetValues(); + var max = SAV is not SAV6AO ? (int)TrainerSprite6.Trevor : names.Length; + var data = new ComboItem[max]; + for (int i = 0; i < max; i++) + data[i] = new ComboItem(names[i], (int)values[i]); CB_MultiplayerSprite.InitializeBinding(); CB_MultiplayerSprite.DataSource = data; @@ -249,7 +248,7 @@ public partial class SAV_Trainer : Form // Sprite if (SAV is IMultiplayerSprite ms) - ms.MultiplayerSpriteID = Convert.ToByte(CB_MultiplayerSprite.SelectedValue); + ms.MultiplayerSpriteID = (byte)WinFormsUtil.GetIndex(CB_MultiplayerSprite); // Appearance if (SAV is SAV6XY xy) diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_FestivalPlaza.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_FestivalPlaza.cs index cf8017a1d..8200a8e48 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_FestivalPlaza.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_FestivalPlaza.cs @@ -89,8 +89,8 @@ public partial class SAV_FestivalPlaza : Form for (int i = 0; i < res2.Length - 1; i++) CLB_Reward.Items.Add(res2[i], (CheckState)RewardState[SAV.Festa.GetFestPrizeReceived(i)]); - for (int i = 0; i < 7; i++) - f[i] = new FestaFacility(SAV, i); + for (int i = 0; i < JoinFesta7.FestaFacilityCount; i++) + f[i] = SAV.Festa.GetFestaFacility(i); string[] res3 = ["Meet", "Part", "Moved", "Disappointed"]; CB_FacilityMessage.Items.Clear(); @@ -176,8 +176,8 @@ public partial class SAV_FestivalPlaza : Form private bool editing; private static ReadOnlySpan RewardState => [ 0, 2, 1 ]; // CheckState.Indeterminate <-> CheckState.Checked private readonly int typeMAX; - private readonly FestaFacility[] f = new FestaFacility[7]; - private readonly string[] RES_Color = Enum.GetNames(typeof(FestivalPlazaFacilityColor)); + private readonly FestaFacility[] f = new FestaFacility[JoinFesta7.FestaFacilityCount]; + private readonly string[] RES_Color = Enum.GetNames(); public enum FestivalPlazaFacilityColor : byte { @@ -291,9 +291,6 @@ public partial class SAV_FestivalPlaza : Form SAV.Festa.SetFestaPrizeReceived(i - 1, RewardState[(int)CLB_Reward.GetItemCheckState(i)]); SaveFacility(); - foreach (FestaFacility facility in f) - facility.CopyTo(SAV); - if (SAV is SAV7USUM) SaveBattleAgency(); } @@ -321,7 +318,7 @@ public partial class SAV_FestivalPlaza : Form var m = (int)NUD_Trainers[i].Maximum; NUD_Trainers[i].Value = (uint)j > m ? m : j; } - B_AgentGlass.Enabled = (SAV.Data[SAV.Fashion.Offset + 0xD0] & 1) == 0; + B_AgentGlass.Enabled = (SAV.Fashion.Data[0xD0] & 1) == 0; } private void LoadPictureBox() @@ -759,7 +756,7 @@ public partial class SAV_FestivalPlaza : Form { if (NUD_Grade.Value < 30 && DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, "Agent Sunglasses is reward of Grade 30.", "Continue?")) return; - SAV.Data[SAV.Fashion.Offset + 0xD0] = 3; + SAV.Fashion.Data[0xD0] = 3; B_AgentGlass.Enabled = false; System.Media.SystemSounds.Asterisk.Play(); } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_HallOfFame7.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_HallOfFame7.cs index 8e434f478..7342dc694 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_HallOfFame7.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_HallOfFame7.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Windows.Forms; using PKHeX.Core; @@ -20,7 +20,7 @@ public partial class SAV_HallOfFame7 : Form CB_C1, CB_C2, CB_C3, CB_C4, CB_C5, CB_C6, ]; - var block = SAV.Fame; + var block = SAV.EventWork.Fame; var specList = GameInfo.FilteredSources.Species; for (int i = 0; i < entries.Length; i++) { @@ -28,7 +28,7 @@ public partial class SAV_HallOfFame7 : Form cb.Items.Clear(); cb.InitializeBinding(); cb.DataSource = new BindingSource(specList, null); - cb.SelectedValue = block.GetEntry(i); + cb.SelectedValue = (int)block.GetEntry(i); } if (SAV is SAV7USUM uu) @@ -41,7 +41,7 @@ public partial class SAV_HallOfFame7 : Form private void B_Close_Click(object sender, EventArgs e) { - var block = SAV.Fame; + var block = SAV.EventWork.Fame; for (int i = 0; i < entries.Length; i++) block.SetEntry(i, (ushort)WinFormsUtil.GetIndex(entries[i])); diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_Trainer7.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_Trainer7.cs index 982131a98..5b7e97c9e 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_Trainer7.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_Trainer7.cs @@ -45,7 +45,7 @@ public partial class SAV_Trainer7 : Form private readonly bool Loading; private bool MapUpdated; - private static readonly string[] AllStyles = Enum.GetNames(typeof(PlayerBattleStyle7)); + private static readonly string[] AllStyles = Enum.GetNames(); private readonly List BattleStyles = [..AllStyles]; private int[] FlyDestFlagOfs = null!, MapUnmaskFlagOfs = null!; @@ -65,7 +65,7 @@ public partial class SAV_Trainer7 : Form Main.SetCountrySubRegion(CB_Country, "countries"); CB_SkinColor.Items.Clear(); - CB_SkinColor.Items.AddRange(Enum.GetNames(typeof(PlayerSkinColor7))); + CB_SkinColor.Items.AddRange(Enum.GetNames()); L_Vivillon.Text = GameInfo.Strings.Species[(int)Species.Vivillon] + ":"; CB_Vivillon.InitializeBinding(); @@ -80,7 +80,7 @@ public partial class SAV_Trainer7 : Form LB_BallThrowTypeLearned.Items.Add(t); } - var stamps = Enum.GetNames(typeof(Stamp7)).Select(z => z.Replace("_", " ")); + var stamps = Enum.GetNames().Select(z => z.Replace("_", " ")); foreach (string t in stamps) LB_Stamps.Items.Add(t); } @@ -189,7 +189,7 @@ public partial class SAV_Trainer7 : Form CB_Vivillon.SelectedIndex = (SAV.Misc.Vivillon < CB_Vivillon.Items.Count) ? SAV.Misc.Vivillon : -1; NUD_DaysFromRefreshed.Value = Math.Min(NUD_DaysFromRefreshed.Maximum, SAV.Misc.DaysFromRefreshed); - if (SAV.MyStatus.BallThrowType >= 0 && SAV.MyStatus.BallThrowType < CB_BallThrowType.Items.Count) + if ((sbyte)SAV.MyStatus.BallThrowType >= 0 && SAV.MyStatus.BallThrowType < CB_BallThrowType.Items.Count) CB_BallThrowType.SelectedIndex = SAV.MyStatus.BallThrowType; if (SAV is SAV7SM) @@ -201,9 +201,9 @@ public partial class SAV_Trainer7 : Form for (int i = 0; i < LB_Stamps.Items.Count; i++) LB_Stamps.SetSelected(i, (stampBits & (1 << i)) != 0); - CHK_UnlockSuperSingles.Checked = SAV.GetEventFlag(333); - CHK_UnlockSuperDoubles.Checked = SAV.GetEventFlag(334); - CHK_UnlockSuperMulti.Checked = SAV.GetEventFlag(335); + CHK_UnlockSuperSingles.Checked = SAV.EventWork.GetEventFlag(333); + CHK_UnlockSuperDoubles.Checked = SAV.EventWork.GetEventFlag(334); + CHK_UnlockSuperMulti.Checked = SAV.EventWork.GetEventFlag(335); CHK_UnlockMega.Checked = SAV.MyStatus.MegaUnlocked; CHK_UnlockZMove.Checked = SAV.MyStatus.ZMoveUnlocked; @@ -218,11 +218,11 @@ public partial class SAV_Trainer7 : Form LB_BallThrowTypeUnlocked.SetSelected(0, true); LB_BallThrowTypeUnlocked.SetSelected(1, true); for (int i = 2; i < BattleStyles.Count; i++) - LB_BallThrowTypeUnlocked.SetSelected(i, SAV.GetEventFlag(unlockStart + i)); + LB_BallThrowTypeUnlocked.SetSelected(i, SAV.EventWork.GetEventFlag(unlockStart + i)); LB_BallThrowTypeLearned.SetSelected(0, true); for (int i = 1; i < BattleStyles.Count; i++) - LB_BallThrowTypeLearned.SetSelected(i, SAV.GetEventFlag(learnedStart + i)); + LB_BallThrowTypeLearned.SetSelected(i, SAV.EventWork.GetEventFlag(learnedStart + i)); CB_BallThrowTypeListMode.SelectedIndex = 0; } @@ -257,7 +257,7 @@ public partial class SAV_Trainer7 : Form { var dest = FlyDestNameIndex[i]; var name = dest < 0 ? FlyDestAltName[u++] : metLocationList.First(v => v.Value == dest).Text; - var state = SAV.GetEventFlag(SkipFlag + FlyDestFlagOfs[i]); + var state = SAV.EventWork.GetEventFlag(SkipFlag + FlyDestFlagOfs[i]); CLB_FlyDest.Items.Add(name, state); } int[] MapUnmaskNameIndex = [ @@ -282,7 +282,7 @@ public partial class SAV_Trainer7 : Form { var dest = MapUnmaskNameIndex[i]; var name = dest < 0 ? MapUnmaskAltName[u++] : metLocationList.First(v => v.Value == dest).Text; - var state = SAV.GetEventFlag(SkipFlag + MapUnmaskFlagOfs[i]); + var state = SAV.EventWork.GetEventFlag(SkipFlag + MapUnmaskFlagOfs[i]); CLB_MapUnmask.Items.Add(name, state); } } @@ -419,26 +419,26 @@ public partial class SAV_Trainer7 : Form const int unlockStart = 292; const int learnedStart = 3479; for (int i = 2; i < BattleStyles.Count; i++) - SAV.SetEventFlag(unlockStart + i, LB_BallThrowTypeUnlocked.GetSelected(i)); + SAV.EventWork.SetEventFlag(unlockStart + i, LB_BallThrowTypeUnlocked.GetSelected(i)); for (int i = 1; i < BattleStyles.Count; i++) - SAV.SetEventFlag(learnedStart + i, LB_BallThrowTypeLearned.GetSelected(i)); + SAV.EventWork.SetEventFlag(learnedStart + i, LB_BallThrowTypeLearned.GetSelected(i)); } private void SaveFlags() { SAV.Misc.Stamps = GetBits(LB_Stamps); - SAV.SetEventFlag(333, CHK_UnlockSuperSingles.Checked); - SAV.SetEventFlag(334, CHK_UnlockSuperDoubles.Checked); - SAV.SetEventFlag(335, CHK_UnlockSuperMulti.Checked); + SAV.EventWork.SetEventFlag(333, CHK_UnlockSuperSingles.Checked); + SAV.EventWork.SetEventFlag(334, CHK_UnlockSuperDoubles.Checked); + SAV.EventWork.SetEventFlag(335, CHK_UnlockSuperMulti.Checked); SAV.MyStatus.MegaUnlocked = CHK_UnlockMega.Checked; SAV.MyStatus.ZMoveUnlocked = CHK_UnlockZMove.Checked; for (int i = 0; i < CLB_FlyDest.Items.Count; i++) - SAV.SetEventFlag(SkipFlag + FlyDestFlagOfs[i], CLB_FlyDest.GetItemChecked(i)); + SAV.EventWork.SetEventFlag(SkipFlag + FlyDestFlagOfs[i], CLB_FlyDest.GetItemChecked(i)); for (int i = 0; i < CLB_MapUnmask.Items.Count; i++) - SAV.SetEventFlag(SkipFlag + MapUnmaskFlagOfs[i], CLB_MapUnmask.GetItemChecked(i)); + SAV.EventWork.SetEventFlag(SkipFlag + MapUnmaskFlagOfs[i], CLB_MapUnmask.GetItemChecked(i)); } private void SaveUltraData() @@ -539,16 +539,16 @@ public partial class SAV_Trainer7 : Form break; } case 1: // Full Legal - byte[] data1 = SAV is SAV7USUM + ReadOnlySpan data1 = SAV is SAV7USUM ? SAV.Gender == 0 ? Properties.Resources.fashion_m_uu : Properties.Resources.fashion_f_uu : SAV.Gender == 0 ? Properties.Resources.fashion_m_sm : Properties.Resources.fashion_f_sm; - SAV.SetData(data1, SAV.Fashion.Offset); + SAV.Fashion.ImportPayload(data1); break; case 2: // Everything - byte[] data2 = SAV is SAV7USUM + ReadOnlySpan data2 = SAV is SAV7USUM ? SAV.Gender == 0 ? Properties.Resources.fashion_m_uu_illegal : Properties.Resources.fashion_f_uu_illegal : SAV.Gender == 0 ? Properties.Resources.fashion_m_sm_illegal : Properties.Resources.fashion_f_sm_illegal; - SAV.SetData(data2, SAV.Fashion.Offset); + SAV.Fashion.ImportPayload(data2); break; default: return; diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_ZygardeCell.Designer.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_ZygardeCell.Designer.cs index 5d14c8741..908f82f33 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_ZygardeCell.Designer.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_ZygardeCell.Designer.cs @@ -34,14 +34,14 @@ dgv_val = new System.Windows.Forms.DataGridViewComboBoxColumn(); B_Cancel = new System.Windows.Forms.Button(); B_Save = new System.Windows.Forms.Button(); - NUD_Cells = new System.Windows.Forms.NumericUpDown(); + NUD_CellsTotal = new System.Windows.Forms.NumericUpDown(); L_Cells = new System.Windows.Forms.Label(); B_GiveAll = new System.Windows.Forms.Button(); L_Collected = new System.Windows.Forms.Label(); - NUD_Collected = new System.Windows.Forms.NumericUpDown(); + NUD_CellsCollected = new System.Windows.Forms.NumericUpDown(); ((System.ComponentModel.ISupportInitialize)dgv).BeginInit(); - ((System.ComponentModel.ISupportInitialize)NUD_Cells).BeginInit(); - ((System.ComponentModel.ISupportInitialize)NUD_Collected).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_CellsTotal).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_CellsCollected).BeginInit(); SuspendLayout(); // // dgv @@ -113,13 +113,13 @@ // // NUD_Cells // - NUD_Cells.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; - NUD_Cells.Location = new System.Drawing.Point(105, 357); - NUD_Cells.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - NUD_Cells.Maximum = new decimal(new int[] { 65535, 0, 0, 0 }); - NUD_Cells.Name = "NUD_Cells"; - NUD_Cells.Size = new System.Drawing.Size(77, 23); - NUD_Cells.TabIndex = 3; + NUD_CellsTotal.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; + NUD_CellsTotal.Location = new System.Drawing.Point(105, 357); + NUD_CellsTotal.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + NUD_CellsTotal.Maximum = new decimal(new int[] { 65535, 0, 0, 0 }); + NUD_CellsTotal.Name = "NUD_CellsTotal"; + NUD_CellsTotal.Size = new System.Drawing.Size(77, 23); + NUD_CellsTotal.TabIndex = 3; // // L_Cells // @@ -157,23 +157,23 @@ // // NUD_Collected // - NUD_Collected.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; - NUD_Collected.Location = new System.Drawing.Point(105, 328); - NUD_Collected.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - NUD_Collected.Maximum = new decimal(new int[] { 65535, 0, 0, 0 }); - NUD_Collected.Name = "NUD_Collected"; - NUD_Collected.Size = new System.Drawing.Size(77, 23); - NUD_Collected.TabIndex = 6; + NUD_CellsCollected.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; + NUD_CellsCollected.Location = new System.Drawing.Point(105, 328); + NUD_CellsCollected.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + NUD_CellsCollected.Maximum = new decimal(new int[] { 65535, 0, 0, 0 }); + NUD_CellsCollected.Name = "NUD_CellsCollected"; + NUD_CellsCollected.Size = new System.Drawing.Size(77, 23); + NUD_CellsCollected.TabIndex = 6; // // SAV_ZygardeCell // AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit; ClientSize = new System.Drawing.Size(588, 393); Controls.Add(L_Collected); - Controls.Add(NUD_Collected); + Controls.Add(NUD_CellsCollected); Controls.Add(B_GiveAll); Controls.Add(L_Cells); - Controls.Add(NUD_Cells); + Controls.Add(NUD_CellsTotal); Controls.Add(B_Save); Controls.Add(B_Cancel); Controls.Add(dgv); @@ -185,8 +185,8 @@ StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; Text = "Cells/Sticker Editor"; ((System.ComponentModel.ISupportInitialize)dgv).EndInit(); - ((System.ComponentModel.ISupportInitialize)NUD_Cells).EndInit(); - ((System.ComponentModel.ISupportInitialize)NUD_Collected).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_CellsTotal).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_CellsCollected).EndInit(); ResumeLayout(false); } @@ -195,13 +195,13 @@ private System.Windows.Forms.DataGridView dgv; private System.Windows.Forms.Button B_Cancel; private System.Windows.Forms.Button B_Save; - private System.Windows.Forms.NumericUpDown NUD_Cells; + private System.Windows.Forms.NumericUpDown NUD_CellsTotal; private System.Windows.Forms.Label L_Cells; private System.Windows.Forms.Button B_GiveAll; private System.Windows.Forms.DataGridViewComboBoxColumn dgv_val; private System.Windows.Forms.DataGridViewTextBoxColumn dgv_location; private System.Windows.Forms.DataGridViewTextBoxColumn dgv_ref; private System.Windows.Forms.Label L_Collected; - private System.Windows.Forms.NumericUpDown NUD_Collected; + private System.Windows.Forms.NumericUpDown NUD_CellsCollected; } } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_ZygardeCell.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_ZygardeCell.cs index 87cc9a83c..45aff7d55 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_ZygardeCell.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_ZygardeCell.cs @@ -14,64 +14,51 @@ public partial class SAV_ZygardeCell : Form InitializeComponent(); WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage); SAV = (SAV7)(Origin = sav).Clone(); - - // Constants @ 0x1C00 - // Cell Data @ 0x1D8C - // Use constants 0x18C/2 = 198 thru +95 - ushort[] constants = SAV.GetAllEventWork(); - var cells = constants.AsSpan(celloffset, CellCount); - - int cellCount = constants[cellstotal]; - int cellCollected = constants[cellscollected]; - - NUD_Cells.Value = cellCount; - NUD_Collected.Value = cellCollected; + var ew = sav.EventWork; + NUD_CellsTotal.Value = ew.ZygardeCellTotal; + NUD_CellsCollected.Value = ew.ZygardeCellCount; var combo = (DataGridViewComboBoxColumn)dgv.Columns[2]; combo.Items.AddRange(states); // add only the Names dgv.Columns[0].ValueType = typeof(int); // Populate Grid - dgv.Rows.Add(CellCount); + dgv.Rows.Add(ew.TotalZygardeCellCount); var locations = SAV is SAV7SM ? locationsSM : locationsUSUM; - for (int i = 0; i < CellCount; i++) + for (int i = 0; i < dgv.RowCount; i++) { - if (cells[i] > 2) + var cell = ew.GetZygardeCell(i); + if (cell > 2) throw new IndexOutOfRangeException("Unable to find cell index."); - dgv.Rows[i].Cells[0].Value = (i + 1); - dgv.Rows[i].Cells[1].Value = locations[i]; - dgv.Rows[i].Cells[2].Value = states[cells[i]]; + var c = dgv.Rows[i].Cells; + c[0].Value = (i + 1); + c[1].Value = locations[i]; + c[2].Value = states[cell]; } } - private const int cellstotal = 161; - private const int cellscollected = 169; - private const int celloffset = 0xC6; - private int CellCount => SAV is SAV7USUM ? 100 : 95; private static readonly string[] states = ["None", "Available", "Received"]; private void B_Save_Click(object sender, EventArgs e) { - ushort[] constants = SAV.GetAllEventWork(); - for (int i = 0; i < CellCount; i++) + var ew = SAV.EventWork; + for (int i = 0; i < dgv.RowCount; i++) { string str = (string)dgv.Rows[i].Cells[2].Value; int val = Array.IndexOf(states, str); if (val < 0) throw new IndexOutOfRangeException("Unable to find cell index."); - constants[celloffset + i] = (ushort)val; + ew.SetZygardeCell(i, (ushort)val); } - constants[cellstotal] = (ushort)NUD_Cells.Value; - constants[cellscollected] = (ushort)NUD_Collected.Value; + ew.ZygardeCellTotal = (ushort)NUD_CellsTotal.Value; + ew.ZygardeCellCount = (ushort)NUD_CellsCollected.Value; if (SAV is SAV7USUM) - SAV.SetRecord(72, (int)NUD_Collected.Value); + SAV.SetRecord(72, (int)NUD_CellsCollected.Value); - SAV.SetAllEventWork(constants); Origin.CopyChangesFrom(SAV); - Close(); } @@ -85,14 +72,15 @@ public partial class SAV_ZygardeCell : Form int added = 0; for (int i = 0; i < dgv.RowCount; i++) { - if (Array.IndexOf(states, (string)dgv.Rows[i].Cells[2].Value) != 2) // Not Collected + var state = dgv.Rows[i].Cells[2]; + if (Array.IndexOf(states, (string)state.Value) != 2) // Not Collected added++; - dgv.Rows[i].Cells[2].Value = states[2]; + state.Value = states[2]; } - NUD_Collected.Value += added; + NUD_CellsCollected.Value += added; if (SAV is not SAV7USUM) - NUD_Cells.Value += added; + NUD_CellsTotal.Value += added; System.Media.SystemSounds.Asterisk.Play(); } diff --git a/PKHeX.WinForms/Subforms/Save Editors/SAV_Inventory.cs b/PKHeX.WinForms/Subforms/Save Editors/SAV_Inventory.cs index cbfee2bcb..e86705e94 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/SAV_Inventory.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/SAV_Inventory.cs @@ -398,7 +398,7 @@ file static class InventoryTypeImageUtil ImageSize = Properties.Resources.bag_items.Size, // Match the size of the resources. }; var images = result.Images; - var types = (InventoryType[])Enum.GetValues(typeof(InventoryType)); + var types = Enum.GetValues(); foreach (var type in types) { if (type is InventoryType.None) diff --git a/PKHeX.WinForms/Subforms/Save Editors/SAV_Wondercard.cs b/PKHeX.WinForms/Subforms/Save Editors/SAV_Wondercard.cs index 8a9ebe093..3ab52a64f 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/SAV_Wondercard.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/SAV_Wondercard.cs @@ -19,13 +19,20 @@ public partial class SAV_Wondercard : Form private readonly SaveFile Origin; private readonly SaveFile SAV; private readonly SummaryPreviewer Summary = new(); + private readonly IMysteryGiftStorage Cards; + private readonly IMysteryGiftFlags? Flags; + private readonly DataMysteryGift[] Album; public SAV_Wondercard(SaveFile sav, DataMysteryGift? g = null) { InitializeComponent(); WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage); + Cards = GetMysteryGiftProvider(sav); SAV = (Origin = sav).Clone(); - mga = SAV.GiftAlbum; + + Album = LoadMysteryGifts(); + Flags = Cards as IMysteryGiftFlags; + pba = GetGiftPictureBoxes(SAV.Generation); foreach (var pb in pba) { @@ -34,14 +41,14 @@ public partial class SAV_Wondercard : Form pb.DragEnter += BoxSlot_DragEnter; pb.MouseDown += BoxSlot_MouseDown; pb.ContextMenuStrip = mnuVSD; - pb.MouseHover += (_, _) => Summary.Show(pb, mga.Gifts[pba.IndexOf(pb)]); + pb.MouseHover += (_, _) => Summary.Show(pb, Album[pba.IndexOf(pb)]); pb.Enter += (sender, e) => { var index = pba.IndexOf(pb); if (index < 0) return; - var enc = mga.Gifts[index]; + var enc = Album[index]; pb.AccessibleDescription = string.Join(Environment.NewLine, enc.GetTextLines()); }; } @@ -52,7 +59,7 @@ public partial class SAV_Wondercard : Form if (LB_Received.Items.Count > 0) LB_Received.SelectedIndex = 0; - if (mga.Gifts[0] is WR7) // giftused is not a valid prop + if (Album[0] is WR7) // giftused is not a valid prop B_UnusedAll.Visible = B_UsedAll.Visible = L_QR.Visible = false; DragEnter += Main_DragEnter; @@ -64,6 +71,25 @@ public partial class SAV_Wondercard : Form ViewGiftData(g); } + private DataMysteryGift[] LoadMysteryGifts() + { + var count = Cards.GiftCountMax; + var size = SAV is SAV4HGSS ? count + 1 : count; + var result = new DataMysteryGift[size]; + for (int i = 0; i < count; i++) + result[i] = Cards.GetMysteryGift(i); + if (SAV is SAV4HGSS s4) + result[^1] = s4.LockCapsuleSlot; + return result; + } + + private static IMysteryGiftStorage GetMysteryGiftProvider(SaveFile sav) + { + if (sav is IMysteryGiftStorageProvider provider) + return provider.MysteryGiftStorage; + throw new ArgumentException("Save file does not support Mystery Gifts.", nameof(sav)); + } + private List GetGiftPictureBoxes(byte generation) => generation switch { 4 => PopulateViewGiftsG4(), @@ -71,24 +97,20 @@ public partial class SAV_Wondercard : Form _ => throw new ArgumentOutOfRangeException(nameof(generation), generation, "Game not supported."), }; - private readonly MysteryGiftAlbum mga; private DataMysteryGift? mg; private readonly List pba; // don't mutate this list // Re-population Functions private void SetBackground(int index, Image bg) { - for (int i = 0; i < mga.Gifts.Length; i++) + for (int i = 0; i < Album.Length; i++) pba[i].BackgroundImage = index == i ? bg : null; } private void SetGiftBoxes() { - for (int i = 0; i < mga.Gifts.Length; i++) - { - MysteryGift m = mga.Gifts[i]; - pba[i].Image = m.Sprite(); - } + for (int i = 0; i < Album.Length; i++) + pba[i].Image = Album[i].Sprite(); } private void ViewGiftData(DataMysteryGift g) @@ -118,9 +140,12 @@ public partial class SAV_Wondercard : Form private void GetReceivedFlags() { LB_Received.Items.Clear(); - for (int i = 1; i < mga.Flags.Length; i++) + if (Flags is not { } f) + return; + var count = f.MysteryGiftReceivedFlagMax; + for (int i = 1; i < count; i++) { - if (mga.Flags[i]) + if (f.GetMysteryGiftReceivedFlag(i)) LB_Received.Items.Add(i.ToString("0000")); } @@ -130,7 +155,7 @@ public partial class SAV_Wondercard : Form private void SetCardID(int cardID) { - if (cardID is <= 0 or >= 0x100 * 8) + if (Flags is null || (uint)cardID >= Flags.MysteryGiftReceivedFlagMax) return; string card = cardID.ToString("0000"); @@ -167,12 +192,11 @@ public partial class SAV_Wondercard : Form WinFormsUtil.ExportMGDialog(mg); } - private static int GetLastUnfilledByType(MysteryGift gift, MysteryGiftAlbum album) + private static int GetLastUnfilledByType(DataMysteryGift gift, ReadOnlySpan album) { - var gifts = album.Gifts; - for (int i = 0; i < gifts.Length; i++) + for (int i = 0; i < album.Length; i++) { - var exist = gifts[i]; + var exist = album[i]; if (!exist.Empty) continue; if (exist.Type != gift.Type) @@ -191,7 +215,7 @@ public partial class SAV_Wondercard : Form int index = pba.IndexOf(pb); SetBackground(index, Drawing.PokeSprite.Properties.Resources.slotView); - ViewGiftData(mga.Gifts[index]); + ViewGiftData(Album[index]); } private void ClickSet(object sender, EventArgs e) @@ -211,13 +235,13 @@ public partial class SAV_Wondercard : Form int index = pba.IndexOf(pb); // Hijack to the latest unfilled slot if index creates interstitial empty slots. - int lastUnfilled = GetLastUnfilledByType(gift, mga); + int lastUnfilled = GetLastUnfilledByType(gift, Album); if (lastUnfilled > -1 && lastUnfilled < index) index = lastUnfilled; if (gift is PCD { IsLockCapsule: true }) index = 11; - var gifts = mga.Gifts; + var gifts = Album; var other = gifts[index]; if (gift is PCD { CanConvertToPGT: true } pcd && other is PGT) { @@ -246,25 +270,25 @@ public partial class SAV_Wondercard : Form return; int index = pba.IndexOf(pb); - var arr = mga.Gifts[index].Data; + var arr = Album[index].Data; Array.Clear(arr, 0, arr.Length); // Shuffle blank card down int i = index; - while (i < mga.Gifts.Length - 1) + while (i < Album.Length - 1) { - if (mga.Gifts[i + 1].Empty) + if (Album[i + 1].Empty) break; - if (mga.Gifts[i + 1].Type != mga.Gifts[i].Type) + if (Album[i + 1].Type != Album[i].Type) break; i++; - var mg1 = mga.Gifts[i]; - var mg2 = mga.Gifts[i - 1]; + var mg1 = Album[i]; + var mg2 = Album[i - 1]; - mga.Gifts[i - 1] = mg1; - mga.Gifts[i] = mg2; + Album[i - 1] = mg1; + Album[i] = mg2; } SetBackground(i, Drawing.PokeSprite.Properties.Resources.slotDel); SetGiftBoxes(); @@ -278,24 +302,44 @@ public partial class SAV_Wondercard : Form private void B_Save_Click(object sender, EventArgs e) { - // Store the list of set flag indexes back to the bitflag array. - bool[] flags = new bool[mga.Flags.Length]; - foreach (var o in LB_Received.Items) - { - var value = o?.ToString(); - if (value == null) - continue; - var flag = Util.ToUInt32(value); - flags[flag] = true; - } - - flags.CopyTo(mga.Flags, 0); - SAV.GiftAlbum = mga; + SaveReceivedFlags(); + SaveReceivedCards(); Origin.CopyChangesFrom(SAV); Close(); } + private void SaveReceivedCards() + { + if (Cards is MysteryBlock4 s4) + { + s4.IsDeliveryManActive = Album.Any(g => !g.Empty); + MysteryBlock4.UpdateSlotPGT(Album, SAV is SAV4HGSS); + if (SAV is SAV4HGSS hgss) + hgss.LockCapsuleSlot = (PCD)Album[^1]; + } + int count = Cards.GiftCountMax; + for (int i = 0; i < count; i++) + Cards.SetMysteryGift(i, Album[i]); + if (Cards is MysteryBlock5 s5) + s5.EndAccess(); // need to encrypt the at-rest data with the seed. + } + + private void SaveReceivedFlags() + { + if (Flags is null) + return; // nothing to save + + // Store the list of set flag indexes back to the bitflag array. + Flags.ClearReceivedFlags(); + foreach (var o in LB_Received.Items) + { + if (o?.ToString() is not { } x || !int.TryParse(x, out var index)) + continue; + Flags.SetMysteryGiftReceivedFlag(index, true); + } + } + // Delete Received Flag private void ClearReceivedFlag(object sender, EventArgs e) { @@ -407,14 +451,14 @@ public partial class SAV_Wondercard : Form if (data.Length == 0) return; - string[] types = mga.Gifts.Select(g => g.Type).Distinct().ToArray(); + string[] types = Album.Select(g => g.Type).Distinct().ToArray(); var gift = MysteryGift.GetMysteryGift(data); if (gift == null) return; string giftType = gift.Type; - if (mga.Gifts.All(card => card.Data.Length != data.Length)) + if (Album.All(card => card.Data.Length != data.Length)) WinFormsUtil.Alert(MsgMysteryGiftQRTypeLength, string.Format(MsgQRDecodeSize, $"0x{data.Length:X}")); else if (types.All(type => type != giftType)) WinFormsUtil.Alert(MsgMysteryGiftTypeIncompatible, $"{MsgMysteryGiftQRReceived} {gift.Type}{Environment.NewLine}{MsgMysteryGiftTypeUnexpected} {string.Join(", ", types)}"); @@ -442,7 +486,7 @@ public partial class SAV_Wondercard : Form return; int index = pba.IndexOf(pb); - var gift = mga.Gifts[index]; + var gift = Album[index]; if (gift.Empty) return; @@ -480,8 +524,8 @@ public partial class SAV_Wondercard : Form int index = pba.IndexOf(pb); // Hijack to the latest unfilled slot if index creates interstitial empty slots. - int lastUnfilled = GetLastUnfilledByType(mg, mga); - if (lastUnfilled > -1 && lastUnfilled < index && mga.Gifts[lastUnfilled].Type == mga.Gifts[index].Type) + int lastUnfilled = GetLastUnfilledByType(mg, Album); + if (lastUnfilled > -1 && lastUnfilled < index && Album[lastUnfilled].Type == Album[index].Type) index = lastUnfilled; if (mg is PCD { IsLockCapsule: true }) index = 11; @@ -501,7 +545,7 @@ public partial class SAV_Wondercard : Form if (gift == null) { WinFormsUtil.Alert(MsgFileUnsupported, first); return; } - ref var dest = ref mga.Gifts[index]; + ref var dest = ref Album[index]; if (gift is PCD { CanConvertToPGT: true } pcd && dest is PGT) { gift = pcd.Gift; @@ -529,7 +573,7 @@ public partial class SAV_Wondercard : Form private int SwapSlots(int dest, int src) { - var gifts = mga.Gifts; + var gifts = Album; var s1 = gifts[dest]; var s2 = gifts[src]; @@ -629,7 +673,7 @@ public partial class SAV_Wondercard : Form FLP_Gifts.Controls.Add(f2); FLP_Gifts.Controls.Add(f3); - if (mga.Gifts.Length == 12) // lock capsule + if (Album.Length == 12) // lock capsule { // Row 4 var f4 = GetFlowLayoutPanel(); @@ -649,8 +693,8 @@ public partial class SAV_Wondercard : Form List pb = []; const int cellsPerRow = 6; - int rows = (int)Math.Ceiling(mga.Gifts.Length / (decimal)cellsPerRow); - int countRemaining = mga.Gifts.Length; + int rows = (int)Math.Ceiling(Album.Length / (decimal)cellsPerRow); + int countRemaining = Album.Length; var spriter = SpriteUtil.Spriter; for (int i = 0; i < rows; i++) @@ -704,7 +748,7 @@ public partial class SAV_Wondercard : Form private void B_ModifyAll_Click(object sender, EventArgs e) { - foreach (var g in mga.Gifts) + foreach (var g in Album) g.GiftUsed = sender == B_UsedAll; SetGiftBoxes(); System.Media.SystemSounds.Asterisk.Play(); diff --git a/PKHeX.WinForms/Util/DevUtil.cs b/PKHeX.WinForms/Util/DevUtil.cs index c71a47d4c..8cabd98b2 100644 --- a/PKHeX.WinForms/Util/DevUtil.cs +++ b/PKHeX.WinForms/Util/DevUtil.cs @@ -87,7 +87,7 @@ namespace PKHeX.WinForms private static IEnumerable GetExtraControls() { - var slotGroupLabels = Enum.GetNames(typeof(StorageSlotType)); + var slotGroupLabels = Enum.GetNames(); foreach (var name in slotGroupLabels) yield return new Label { Name = $"{nameof(Main)}.L_{name}", Text = name }; } @@ -116,6 +116,7 @@ namespace PKHeX.WinForms $"{nameof(SAV_GameSelect)}.L_Prompt", // prompt text (dynamic) $"{nameof(SAV_BlockDump8)}.L_BlockName", // Block name (dynamic) $"{nameof(SAV_PokedexResearchEditorLA)}.L_", // Dynamic label + $"{nameof(SAV_OPower)}.L_", // Dynamic label ]; private static readonly string[] PurgeBanlist = diff --git a/PKHeX.WinForms/Util/WinFormsTranslator.cs b/PKHeX.WinForms/Util/WinFormsTranslator.cs index cdf7f0bf2..59e6bff53 100644 --- a/PKHeX.WinForms/Util/WinFormsTranslator.cs +++ b/PKHeX.WinForms/Util/WinFormsTranslator.cs @@ -13,8 +13,8 @@ public static class WinFormsTranslator private static readonly Dictionary Context = []; internal static void TranslateInterface(this Control form, string lang) => TranslateForm(form, GetContext(lang)); - private static string GetTranslationFileNameInternal(string lang) => $"lang_{lang}"; - private static string GetTranslationFileNameExternal(string lang) => $"lang_{lang}.txt"; + private static string GetTranslationFileNameInternal(ReadOnlySpan lang) => $"lang_{lang}"; + private static string GetTranslationFileNameExternal(ReadOnlySpan lang) => $"lang_{lang}.txt"; public static IReadOnlyDictionary GetDictionary(string lang) => GetContext(lang).Lookup; @@ -68,7 +68,7 @@ public static class WinFormsTranslator }; } - private static void TranslateControl(object c, TranslationContext context, string formname) + private static void TranslateControl(object c, TranslationContext context, ReadOnlySpan formname) { if (c is Control r) { @@ -86,7 +86,7 @@ public static class WinFormsTranslator } } - private static ReadOnlySpan GetTranslationFile(string lang) + private static ReadOnlySpan GetTranslationFile(ReadOnlySpan lang) { var file = GetTranslationFileNameInternal(lang); // Check to see if the desired translation file exists in the same folder as the executable @@ -181,22 +181,52 @@ public static class WinFormsTranslator } } - public static void DumpAll(params string[] banlist) + public static void DumpAll(ReadOnlySpan banlist) { foreach (var (lang, value) in Context) { var fn = GetTranslationFileNameExternal(lang); var lines = value.Write(); - var result = lines.Where(z => !banlist.Any(z.Contains)); - File.WriteAllLines(fn, result); + + // Write a new file. + using var fs = new StreamWriter(fn); + foreach (var line in lines) + { + // Ensure line isn't banned. + if (IsBannedContains(line, banlist)) + continue; + fs.WriteLine(line); + } } } - public static void LoadAllForms(IEnumerable types, params string[] banlist) + private static bool IsBannedContains(ReadOnlySpan line, ReadOnlySpan banlist) + { + foreach (var banned in banlist) + { + if (banned.AsSpan().Contains(line, StringComparison.Ordinal)) + return true; + } + return false; + } + + private static bool IsBannedStartsWith(ReadOnlySpan line, ReadOnlySpan banlist) + { + foreach (var banned in banlist) + { + if (line.StartsWith(banned, StringComparison.Ordinal)) + return true; + } + return false; + } + + public static void LoadAllForms(IEnumerable types, ReadOnlySpan banlist) { - types = types.Where(t => t.BaseType == typeof(Form) && !banlist.Contains(t.Name)); foreach (var t in types) { + if (t.BaseType == typeof(Form) && IsBannedStartsWith(t.Name, banlist)) + continue; + var constructors = t.GetConstructors(); if (constructors.Length == 0) { System.Diagnostics.Debug.WriteLine($"No constructors: {t.Name}"); continue; } @@ -222,11 +252,10 @@ public static class WinFormsTranslator } } - public static void RemoveAll(string defaultLanguage, params string[] banlist) + public static void RemoveAll(string defaultLanguage, ReadOnlySpan banlist) { var badKeys = Context[defaultLanguage]; - var split = badKeys.Write().Select(z => z.Split(TranslationContext.Separator)[0]) - .Where(l => !banlist.Any(l.StartsWith)).ToArray(); + var split = GetSkips(banlist, badKeys); foreach (var c in Context) { var lang = c.Key; @@ -237,6 +266,24 @@ public static class WinFormsTranslator } } + private static string[] GetSkips(ReadOnlySpan banlist, TranslationContext badKeys) + { + List split = []; + foreach (var line in badKeys.Write()) + { + var index = line.IndexOf(TranslationContext.Separator); + if (index < 0) + continue; + var key = line.AsSpan(0, index); + if (IsBannedStartsWith(key, banlist)) + split.Add(key.ToString()); + } + + if (split.Count == 0) + return []; + return [..split]; + } + public static void LoadSettings(string defaultLanguage, bool add = true) { var context = (Dictionary)Context[defaultLanguage].Lookup; diff --git a/Tests/PKHeX.Core.Tests/Saves/PokeDex.cs b/Tests/PKHeX.Core.Tests/Saves/PokeDex.cs index c18d3318e..df9075cab 100644 --- a/Tests/PKHeX.Core.Tests/Saves/PokeDex.cs +++ b/Tests/PKHeX.Core.Tests/Saves/PokeDex.cs @@ -1,4 +1,3 @@ -using System; using Xunit; using FluentAssertions; @@ -25,7 +24,7 @@ public static class PokeDex CheckDexFlags5(bw, (ushort)species, 0, 0x54, 0xB); } - private static void SetDexSpecies(SaveFile sav, ushort species, int regionSize) + private static void SetDexSpecies(SAV5B2W2 sav, ushort species, int regionSize) { var pk5 = new PK5 {Species = species, TID16 = 1337}; // non-shiny pk5.Gender = pk5.GetSaneGender(); @@ -35,29 +34,29 @@ public static class PokeDex CheckFlags(sav, species, regionSize); } - private static void CheckFlags(SaveFile sav, ushort species, int regionSize) + private static void CheckFlags(SAV5B2W2 sav, ushort species, int regionSize) { - var dex = sav.PokeDex; - var data = sav.Data; + var dex = sav.Blocks.Zukan; + var data = dex.Data; var bit = species - 1; var value = (byte) (1 << (bit & 7)); var offset = bit >> 3; // Check the regular flag regions. - var span = data.AsSpan(dex + 0x08); + var span = data[0x08..]; span[offset].Should().Be(value, "caught flag"); span[offset + regionSize].Should().Be(value, "seen flag"); span[offset + regionSize + (regionSize * 4)].Should().Be(value, "displayed flag"); } - private static void CheckDexFlags5(SaveFile sav, ushort species, byte form, int regionSize, int formRegionSize) + private static void CheckDexFlags5(SAV5B2W2 sav, ushort species, byte form, int regionSize, int formRegionSize) { - var dex = sav.PokeDex; - var data = sav.Data; + var dex = sav.Blocks.Zukan; + var data = dex.Data; var fc = sav.Personal[species].FormCount; - var bit = ((SAV5)sav).Zukan.DexFormIndexFetcher(species, fc); + var bit = sav.Zukan.DexFormIndexFetcher(species, fc); if (bit < 0) return; bit += form; @@ -65,7 +64,7 @@ public static class PokeDex var offset = bit >> 3; // Check the form flag regions. - var span = data.AsSpan(dex + 0x08 + (regionSize * 9)); + var span = data[(0x08 + (regionSize * 9))..]; span[offset].Should().Be(value, "seen flag"); span[offset + (formRegionSize * 2)].Should().Be(value, "displayed flag"); }