From 47071b41f37fba59f4553b2e2176ed4681c3b4fe Mon Sep 17 00:00:00 2001 From: Kurt Date: Sun, 2 Jan 2022 21:35:59 -0800 Subject: [PATCH] Refactoring: Span-based value writes and method signatures (#3361) Existing `get`/`set` logic is flawed in that it doesn't work on Big Endian operating systems, and it allocates heap objects when it doesn't need to. `System.Buffers.Binary.BinaryPrimitives` in the `System.Memory` NuGet package provides both Little Endian and Big Endian methods to read and write data; all the `get`/`set` operations have been reworked to use this new API. This removes the need for PKHeX's manual `BigEndian` class, as all functions are already covered by the BinaryPrimitives API. The `StringConverter` has now been rewritten to accept a Span to read from & write to, no longer requiring a temporary StringBuilder. Other Fixes included: - The Super Training UI for Gen6 has been reworked according to the latest block structure additions. - Cloning a Stadium2 Save File now works correctly (opening from the Folder browser list). - Checksum & Sanity properties removed from parent PKM class, and is now implemented via interface. --- .../Editing/Applicators/BallApplicator.cs | 4 +- .../Editing/Applicators/MoveSetApplicator.cs | 2 +- PKHeX.Core/Editing/Bulk/BatchEditing.cs | 9 +- PKHeX.Core/Editing/PKM/EntitySummary.cs | 2 +- PKHeX.Core/Editing/PKM/EntityTemplates.cs | 4 +- .../Saves/Editors/EventWork/EventWork.cs | 4 +- .../Editors/EventWork/SplitEventEditor.cs | 8 +- .../Editing/Saves/Editors/SaveDataEditor.cs | 4 +- .../Editing/Saves/Slots/Info/SlotCache.cs | 6 +- PKHeX.Core/Editing/Showdown/ShowdownSet.cs | 2 +- PKHeX.Core/Game/GameStrings/GameDataSource.cs | 22 +- PKHeX.Core/Legality/Areas/EncounterArea1.cs | 21 +- PKHeX.Core/Legality/Areas/EncounterArea2.cs | 20 +- PKHeX.Core/Legality/Areas/EncounterArea3.cs | 75 +- PKHeX.Core/Legality/Areas/EncounterArea4.cs | 37 +- PKHeX.Core/Legality/Areas/EncounterArea5.cs | 27 +- PKHeX.Core/Legality/Areas/EncounterArea6AO.cs | 25 +- PKHeX.Core/Legality/Areas/EncounterArea6XY.cs | 14 +- PKHeX.Core/Legality/Areas/EncounterArea7.cs | 23 +- PKHeX.Core/Legality/Areas/EncounterArea7b.cs | 23 +- PKHeX.Core/Legality/Areas/EncounterArea7g.cs | 18 +- PKHeX.Core/Legality/Areas/EncounterArea8.cs | 20 +- PKHeX.Core/Legality/Areas/EncounterArea8b.cs | 23 +- PKHeX.Core/Legality/Areas/EncounterArea8g.cs | 18 +- .../Encounters/Data/EncounterEvent.cs | 18 +- .../Encounters/EncounterSlot/EncounterSlot.cs | 4 +- .../Encounters/Generator/EncounterCriteria.cs | 5 +- .../Verifiers/MysteryGiftVerifier.cs | 16 +- .../Verifiers/VerifyRelearnMoves.cs | 5 +- .../Evolutions/EvolutionSets/EvolutionSet1.cs | 15 +- .../Evolutions/EvolutionSets/EvolutionSet3.cs | 15 +- .../Evolutions/EvolutionSets/EvolutionSet4.cs | 15 +- .../Evolutions/EvolutionSets/EvolutionSet5.cs | 45 +- .../Evolutions/EvolutionSets/EvolutionSet6.cs | 27 +- .../Evolutions/EvolutionSets/EvolutionSet7.cs | 23 +- .../Legality/Learnset/LearnsetReader.cs | 11 +- PKHeX.Core/Legality/RNG/Algorithms/RNGUtil.cs | 22 +- PKHeX.Core/Legality/RNG/MethodFinder.cs | 36 +- PKHeX.Core/Legality/RNG/Overworld8RNG.cs | 44 +- PKHeX.Core/Legality/RNG/PIDGenerator.cs | 4 +- PKHeX.Core/Legality/RNG/Roaming8bRNG.cs | 24 +- .../Restrictions/Memories/MemoryContext8.cs | 4 +- PKHeX.Core/Legality/Structures/EggMoves.cs | 26 +- .../Legality/Verifiers/MedalVerifier.cs | 2 +- PKHeX.Core/Legality/Verifiers/MiscVerifier.cs | 9 +- .../Legality/Verifiers/NicknameVerifier.cs | 2 +- PKHeX.Core/MysteryGifts/MysteryGift.cs | 3 +- PKHeX.Core/MysteryGifts/PCD.cs | 23 +- PKHeX.Core/MysteryGifts/PGF.cs | 47 +- PKHeX.Core/MysteryGifts/PGT.cs | 12 +- PKHeX.Core/MysteryGifts/PL6.cs | 85 +- PKHeX.Core/MysteryGifts/WB7.cs | 88 +- PKHeX.Core/MysteryGifts/WB8.cs | 92 +- PKHeX.Core/MysteryGifts/WC6.cs | 70 +- PKHeX.Core/MysteryGifts/WC6Full.cs | 11 +- PKHeX.Core/MysteryGifts/WC7.cs | 86 +- PKHeX.Core/MysteryGifts/WC7Full.cs | 10 +- PKHeX.Core/MysteryGifts/WC8.cs | 94 +- PKHeX.Core/MysteryGifts/WR7.cs | 49 +- PKHeX.Core/PKM/BK4.cs | 127 +- PKHeX.Core/PKM/CK3.cs | 113 +- PKHeX.Core/PKM/G8PKM.cs | 122 +- PKHeX.Core/PKM/PB7.cs | 124 +- PKHeX.Core/PKM/PK1.cs | 51 +- PKHeX.Core/PKM/PK2.cs | 60 +- PKHeX.Core/PKM/PK3.cs | 73 +- PKHeX.Core/PKM/PK4.cs | 143 +- PKHeX.Core/PKM/PK5.cs | 68 +- PKHeX.Core/PKM/PK6.cs | 122 +- PKHeX.Core/PKM/PK7.cs | 126 +- PKHeX.Core/PKM/PK8.cs | 3 +- PKHeX.Core/PKM/PKM.cs | 54 +- PKHeX.Core/PKM/SK2.cs | 49 +- PKHeX.Core/PKM/Shared/G3PKM.cs | 4 + PKHeX.Core/PKM/Shared/G4PKM.cs | 76 +- PKHeX.Core/PKM/Shared/G6PKM.cs | 19 +- PKHeX.Core/PKM/Shared/GBPKM.cs | 5 +- PKHeX.Core/PKM/Shared/GBPKML.cs | 40 +- PKHeX.Core/PKM/Shared/ISanityChecksum.cs | 8 + PKHeX.Core/PKM/Shared/PokeList1.cs | 11 +- PKHeX.Core/PKM/Shared/PokeList2.cs | 11 +- PKHeX.Core/PKM/Shared/QRPK7.cs | 19 +- PKHeX.Core/PKM/Strings/StringConverter.cs | 383 ++--- PKHeX.Core/PKM/Strings/StringConverter12.cs | 1523 +++++++++-------- .../Strings/StringConverter12Transporter.cs | 255 +-- PKHeX.Core/PKM/Strings/StringConverter2KOR.cs | 1087 ++++++------ PKHeX.Core/PKM/Strings/StringConverter3.cs | 320 ++-- PKHeX.Core/PKM/Strings/StringConverter345.cs | 111 +- PKHeX.Core/PKM/Strings/StringConverter3GC.cs | 59 + PKHeX.Core/PKM/Strings/StringConverter4.cs | 657 +------ PKHeX.Core/PKM/Strings/StringConverter4GC.cs | 130 ++ .../PKM/Strings/StringConverter4Util.cs | 439 +++++ PKHeX.Core/PKM/Strings/StringConverter5.cs | 63 + PKHeX.Core/PKM/Strings/StringConverter6.cs | 63 + PKHeX.Core/PKM/Strings/StringConverter7.cs | 71 + PKHeX.Core/PKM/Strings/StringConverter7ZH.cs | 165 +- PKHeX.Core/PKM/Strings/StringConverter8.cs | 63 + .../PKM/Strings/StringConverterOption.cs | 10 + PKHeX.Core/PKM/Util/EntityFileNamer.cs | 3 +- PKHeX.Core/PKM/Util/PKMConverter.cs | 15 +- PKHeX.Core/PKM/Util/PKX.cs | 43 +- PKHeX.Core/PKM/Util/PokeCrypto.cs | 136 +- PKHeX.Core/PKM/Util/SpeciesName.cs | 29 +- PKHeX.Core/PKM/XK3.cs | 87 +- PKHeX.Core/PersonalInfo/PersonalInfoBDSP.cs | 41 +- PKHeX.Core/PersonalInfo/PersonalInfoBW.cs | 19 +- PKHeX.Core/PersonalInfo/PersonalInfoG3.cs | 7 +- PKHeX.Core/PersonalInfo/PersonalInfoG4.cs | 3 +- PKHeX.Core/PersonalInfo/PersonalInfoGG.cs | 3 +- PKHeX.Core/PersonalInfo/PersonalInfoSM.cs | 7 +- PKHeX.Core/PersonalInfo/PersonalInfoSWSH.cs | 43 +- PKHeX.Core/PersonalInfo/PersonalTable.cs | 2 +- PKHeX.Core/Saves/Blocks/BlockInfo.cs | 18 +- PKHeX.Core/Saves/Blocks/BlockInfo3DS.cs | 17 +- PKHeX.Core/Saves/Blocks/BlockInfoNDS.cs | 18 +- PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs | 26 +- PKHeX.Core/Saves/Encryption/GeniusCrypto.cs | 29 +- .../Saves/Encryption/MemeCrypto/MemeCrypto.cs | 77 +- .../Saves/Encryption/MemeCrypto/MemeKey.cs | 83 +- .../Saves/Encryption/SwishCrypto/SCBlock.cs | 29 +- .../Encryption/SwishCrypto/SCBlockCompare.cs | 2 +- .../Encryption/SwishCrypto/SCTypeCode.cs | 37 +- .../Encryption/SwishCrypto/SCXorShift32.cs | 4 +- .../Encryption/SwishCrypto/SwishCrypto.cs | 18 +- PKHeX.Core/Saves/SAV1.cs | 57 +- PKHeX.Core/Saves/SAV1Stadium.cs | 26 +- PKHeX.Core/Saves/SAV1StadiumJ.cs | 22 +- PKHeX.Core/Saves/SAV2.cs | 102 +- PKHeX.Core/Saves/SAV2Stadium.cs | 15 +- PKHeX.Core/Saves/SAV3.cs | 132 +- PKHeX.Core/Saves/SAV3Colosseum.cs | 124 +- PKHeX.Core/Saves/SAV3E.cs | 49 +- PKHeX.Core/Saves/SAV3FRLG.cs | 43 +- PKHeX.Core/Saves/SAV3GCMemoryCard.cs | 95 +- PKHeX.Core/Saves/SAV3RS.cs | 17 +- PKHeX.Core/Saves/SAV3RSBox.cs | 26 +- PKHeX.Core/Saves/SAV3XD.cs | 66 +- PKHeX.Core/Saves/SAV4.cs | 76 +- PKHeX.Core/Saves/SAV4BR.cs | 106 +- PKHeX.Core/Saves/SAV4DP.cs | 34 +- PKHeX.Core/Saves/SAV4HGSS.cs | 46 +- PKHeX.Core/Saves/SAV4Pt.cs | 23 +- PKHeX.Core/Saves/SAV4Sinnoh.cs | 40 +- PKHeX.Core/Saves/SAV5.cs | 45 +- PKHeX.Core/Saves/SAV5B2W2.cs | 4 +- PKHeX.Core/Saves/SAV6.cs | 14 +- PKHeX.Core/Saves/SAV6AO.cs | 11 +- PKHeX.Core/Saves/SAV6XY.cs | 16 +- PKHeX.Core/Saves/SAV7.cs | 14 +- PKHeX.Core/Saves/SAV7SM.cs | 3 +- PKHeX.Core/Saves/SAV7b.cs | 16 +- PKHeX.Core/Saves/SAV8.cs | 8 +- PKHeX.Core/Saves/SAV8BS.cs | 43 +- PKHeX.Core/Saves/SAV8SWSH.cs | 6 +- PKHeX.Core/Saves/SAV_BEEF.cs | 9 +- PKHeX.Core/Saves/SAV_STADIUM.cs | 30 +- PKHeX.Core/Saves/SaveFile.cs | 65 +- PKHeX.Core/Saves/Storage/Bank7.cs | 7 +- PKHeX.Core/Saves/Storage/BulkStorage.cs | 13 +- PKHeX.Core/Saves/Storage/SAV4Ranch.cs | 26 +- .../Saves/Substructures/Battle Videos/BV3.cs | 19 +- .../Saves/Substructures/Battle Videos/BV6.cs | 39 +- .../Saves/Substructures/Battle Videos/BV7.cs | 11 +- .../Battle Videos/BVRequestUtil.cs | 10 +- .../Battle Videos/BattleVideo.cs | 5 +- .../Gen3/CXD/ShadowInfoEntryColo.cs | 11 +- .../Gen3/CXD/ShadowInfoEntryXD.cs | 18 +- .../Gen3/CXD/ShadowInfoTableXD.cs | 9 +- .../Substructures/Gen3/Events/WonderCard3.cs | 7 +- .../Gen3/Events/WonderCard3Extra.cs | 9 +- .../Substructures/Gen3/Events/WonderNews3.cs | 3 +- .../Substructures/Gen3/Gen3MysteryData.cs | 7 +- .../Saves/Substructures/Gen3/HallFame3.cs | 16 +- .../Saves/Substructures/Gen3/PokeBlock3.cs | 5 +- .../Substructures/Gen3/PokeBlock3Case.cs | 4 +- PKHeX.Core/Saves/Substructures/Gen3/RTC3.cs | 5 +- .../Saves/Substructures/Gen3/Record3.cs | 5 +- .../Saves/Substructures/Gen3/Roamer3.cs | 21 +- .../Saves/Substructures/Gen3/SecretBase3.cs | 13 +- .../Substructures/Gen3/SecretBase3Team.cs | 33 +- .../Saves/Substructures/Gen3/StrategyMemo.cs | 25 +- .../Substructures/Gen4/HoneyTreeValue.cs | 3 +- .../Saves/Substructures/Gen4/Poffin4.cs | 7 +- .../Substructures/Gen4/SAV4BlockDetection.cs | 11 +- .../Saves/Substructures/Gen5/BattleSubway5.cs | 69 +- .../Saves/Substructures/Gen5/BoxLayout5.cs | 15 +- .../Substructures/Gen5/CGearBackground.cs | 50 +- .../Saves/Substructures/Gen5/Daycare5.cs | 11 +- .../Saves/Substructures/Gen5/Entralink5.cs | 9 +- .../Saves/Substructures/Gen5/EntreeForest.cs | 5 +- .../Saves/Substructures/Gen5/EntreeSlot.cs | 5 +- .../Saves/Substructures/Gen5/FestaBlock5.cs | 21 +- PKHeX.Core/Saves/Substructures/Gen5/Misc5.cs | 13 +- .../Saves/Substructures/Gen5/MysteryBlock5.cs | 11 +- .../Saves/Substructures/Gen5/PWTBlock5.cs | 5 +- .../Saves/Substructures/Gen5/PlayerData5.cs | 35 +- .../Saves/Substructures/Gen6/BoxLayout6.cs | 11 +- .../Saves/Substructures/Gen6/Encount6.cs | 27 +- .../Saves/Substructures/Gen6/GameTime6.cs | 7 +- .../Saves/Substructures/Gen6/ItemInfo6.cs | 15 +- .../Saves/Substructures/Gen6/LinkBlock6.cs | 9 +- .../Saves/Substructures/Gen6/MaisonBlock.cs | 5 +- .../Saves/Substructures/Gen6/Misc6AO.cs | 9 +- .../Saves/Substructures/Gen6/Misc6XY.cs | 9 +- .../Saves/Substructures/Gen6/MyStatus6.cs | 28 +- .../Saves/Substructures/Gen6/MyStatus6XY.cs | 13 +- .../Saves/Substructures/Gen6/MysteryBlock6.cs | 4 +- PKHeX.Core/Saves/Substructures/Gen6/PSS6.cs | 34 +- .../Saves/Substructures/Gen6/PlayTime6.cs | 7 +- PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs | 7 +- .../Saves/Substructures/Gen6/RecordBlock6.cs | 9 +- .../Substructures/Gen6/SangoInfoBlock.cs | 15 +- .../Gen6/SecretBase/SecretBase6.cs | 32 +- .../SecretBase/SecretBase6GoodPlacement.cs | 29 +- .../Gen6/SecretBase/SecretBase6GoodStock.cs | 15 +- .../Gen6/SecretBase/SecretBase6PKM.cs | 43 +- .../Substructures/Gen6/SecretBase6Block.cs | 13 +- .../Saves/Substructures/Gen6/Situation6.cs | 35 +- .../Saves/Substructures/Gen6/SubEventLog6.cs | 17 +- .../Substructures/Gen6/SuperTrainBlock.cs | 17 +- .../Substructures/Gen6/TrainerFashion6.cs | 22 +- .../Saves/Substructures/Gen7/BattleTree7.cs | 21 +- .../Saves/Substructures/Gen7/BoxLayout7.cs | 26 +- .../Substructures/Gen7/CaptureRecords.cs | 17 +- .../Saves/Substructures/Gen7/ConfigSave6.cs | 5 +- .../Saves/Substructures/Gen7/ConfigSave7.cs | 5 +- .../Saves/Substructures/Gen7/ConfigSave7b.cs | 5 +- .../Saves/Substructures/Gen7/EventWork7b.cs | 5 +- .../Saves/Substructures/Gen7/FestaFacility.cs | 18 +- .../Saves/Substructures/Gen7/FieldMenu7.cs | 7 +- .../Substructures/Gen7/FieldMoveModelSave7.cs | 11 +- PKHeX.Core/Saves/Substructures/Gen7/GP1.cs | 33 +- .../Saves/Substructures/Gen7/GameTime7.cs | 9 +- .../Saves/Substructures/Gen7/HallOfFame7.cs | 29 +- .../Saves/Substructures/Gen7/JoinFesta7.cs | 33 +- PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs | 25 +- PKHeX.Core/Saves/Substructures/Gen7/Misc7b.cs | 13 +- .../Saves/Substructures/Gen7/MyStatus7.cs | 19 +- .../Saves/Substructures/Gen7/MyStatus7b.cs | 15 +- .../Saves/Substructures/Gen7/MysteryBlock7.cs | 4 +- .../Saves/Substructures/Gen7/PlayTime7b.cs | 7 +- .../Saves/Substructures/Gen7/PokeFinder7.cs | 25 +- .../Substructures/Gen7/PokeListHeader.cs | 11 +- PKHeX.Core/Saves/Substructures/Gen7/QR7.cs | 21 +- .../Saves/Substructures/Gen7/ResortSave7.cs | 4 + .../Saves/Substructures/Gen7/Situation7.cs | 27 +- .../Saves/Substructures/Gen7/WB7Records.cs | 3 +- .../Gen8/BS/BattleTowerWork8b.cs | 45 +- .../Gen8/BS/BattleTrainerStatus8b.cs | 9 +- .../Gen8/BS/BerryTreeGrowSave8b.cs | 3 +- .../Substructures/Gen8/BS/BoxLayout8b.cs | 40 +- .../Substructures/Gen8/BS/ConfigSave8b.cs | 53 +- .../Saves/Substructures/Gen8/BS/Contest8b.cs | 3 +- .../Saves/Substructures/Gen8/BS/Daycare8b.cs | 13 +- .../Substructures/Gen8/BS/EncounterSave8b.cs | 81 +- .../Gen8/BS/FieldGimmickSave8b.cs | 7 +- .../Gen8/BS/FieldObjectSave8b.cs | 33 +- .../Saves/Substructures/Gen8/BS/FlagWork8b.cs | 17 +- .../Substructures/Gen8/BS/MenuSelect8b.cs | 11 +- .../Saves/Substructures/Gen8/BS/MyItem8b.cs | 11 +- .../Saves/Substructures/Gen8/BS/MyStatus8b.cs | 33 +- .../Substructures/Gen8/BS/MysteryBlock8b.cs | 95 +- .../Saves/Substructures/Gen8/BS/PlayTime8b.cs | 5 +- .../Substructures/Gen8/BS/PlayerData8b.cs | 75 +- .../Substructures/Gen8/BS/PoffinSaveData8b.cs | 5 +- .../Substructures/Gen8/BS/RandomGroup8b.cs | 29 +- .../Saves/Substructures/Gen8/BS/Record8b.cs | 5 +- .../Substructures/Gen8/BS/RecordAddData8b.cs | 21 +- .../Gen8/BS/SaveItemShortcut8b.cs | 9 +- .../Saves/Substructures/Gen8/BS/SealDeco8b.cs | 15 +- .../Substructures/Gen8/BS/SealSticker8b.cs | 25 +- .../Substructures/Gen8/BS/SystemData8b.cs | 17 +- .../Substructures/Gen8/BS/UgCountRecord8b.cs | 29 +- .../Substructures/Gen8/BS/UgSaveData8b.cs | 25 +- .../Gen8/BS/UndergroundItem8b.cs | 29 +- .../Substructures/Gen8/BS/UnionSaveData8b.cs | 7 +- .../Saves/Substructures/Gen8/BS/Zukan8b.cs | 29 +- .../Substructures/Gen8/BS/ZukanSpinda8b.cs | 9 +- .../Saves/Substructures/Gen8/BoxLayout8.cs | 19 +- .../Saves/Substructures/Gen8/Daycare8.cs | 5 +- .../Substructures/Gen8/FashionUnlock8.cs | 11 +- .../Substructures/Gen8/FieldMoveModelSave8.cs | 11 +- .../Substructures/Gen8/HallOfFameTime8.cs | 7 +- PKHeX.Core/Saves/Substructures/Gen8/Misc8.cs | 9 +- .../Saves/Substructures/Gen8/MyStatus8.cs | 83 +- .../Saves/Substructures/Gen8/PlayTime8.cs | 7 +- .../Substructures/Gen8/RaidSpawnList8.cs | 9 +- .../Saves/Substructures/Gen8/Record8.cs | 5 +- .../Saves/Substructures/Gen8/TeamIndexes8.cs | 10 +- .../Saves/Substructures/Gen8/TitleScreen8.cs | 37 +- .../Saves/Substructures/Gen8/TrainerCard8.cs | 129 +- .../Substructures/Inventory/BeanPouch.cs | 29 +- .../Inventory/InventoryItem8b.cs | 25 +- .../Substructures/Inventory/InventoryPouch.cs | 4 +- .../Inventory/InventoryPouch3.cs | 20 +- .../Inventory/InventoryPouch3GC.cs | 17 +- .../Inventory/InventoryPouch4.cs | 18 +- .../Inventory/InventoryPouch7.cs | 16 +- .../Inventory/InventoryPouch7b.cs | 13 +- .../Inventory/InventoryPouch8.cs | 13 +- .../Inventory/InventoryPouch8b.cs | 12 +- .../Inventory/InventoryPouchGB.cs | 4 +- PKHeX.Core/Saves/Substructures/Mail/Mail2.cs | 14 +- PKHeX.Core/Saves/Substructures/Mail/Mail3.cs | 27 +- PKHeX.Core/Saves/Substructures/Mail/Mail4.cs | 15 +- PKHeX.Core/Saves/Substructures/Mail/Mail5.cs | 17 +- .../Saves/Substructures/OPower/OPower6.cs | 25 +- .../Substructures/OPower/OPowerFlagSet.cs | 28 +- .../Saves/Substructures/PokeDex/Zukan4.cs | 41 +- .../Saves/Substructures/PokeDex/Zukan6.cs | 10 +- .../Saves/Substructures/PokeDex/Zukan7.cs | 12 +- .../Saves/Substructures/PokeDex/Zukan7b.cs | 37 +- .../Saves/Substructures/PokeDex/Zukan8.cs | 29 +- PKHeX.Core/Saves/Util/Checksums.cs | 26 +- PKHeX.Core/Saves/Util/SaveUtil.cs | 98 +- PKHeX.Core/Saves/Util/StadiumUtil.cs | 22 +- PKHeX.Core/Util/ArrayUtil.cs | 36 +- PKHeX.Core/Util/BigEndian.cs | 63 - PKHeX.Core/Util/BinaryCodedDecimal.cs | 39 +- PKHeX.Core/Util/ComboItemUtil.cs | 20 +- PKHeX.Core/Util/FileUtil.cs | 3 +- PKHeX.Core/Util/FlagUtil.cs | 8 +- PKHeX.Core/Util/FrameworkUtil.cs | 16 +- PKHeX.Core/Util/RandUtil.cs | 5 +- PKHeX.Core/Util/StringUtil.cs | 8 +- PKHeX.Core/Util/Util.cs | 43 +- PKHeX.Drawing/ImageUtil.cs | 47 +- .../Controls/PKM Editor/PKMEditor.cs | 23 +- .../Controls/PKM Editor/StatEditor.cs | 4 +- .../Controls/PKM Editor/TrainerID.cs | 20 +- PKHeX.WinForms/Controls/Slots/SlotList.cs | 4 +- .../Subforms/Misc/SortableBindingList.cs | 4 +- PKHeX.WinForms/Subforms/PKM Editors/Text.cs | 10 +- .../Subforms/Save Editors/Gen3/SAV_Misc3.cs | 21 +- .../Subforms/Save Editors/Gen4/SAV_Misc4.cs | 111 +- .../Save Editors/Gen4/SAV_Underground.cs | 8 +- .../Subforms/Save Editors/Gen5/SAV_Misc5.cs | 26 +- .../Save Editors/Gen6/SAV_BerryFieldXY.cs | 17 +- .../Save Editors/Gen6/SAV_HallOfFame.cs | 106 +- .../Save Editors/Gen6/SAV_PokeBlockORAS.cs | 17 +- .../Gen6/SAV_SuperTrain.Designer.cs | 229 +-- .../Save Editors/Gen6/SAV_SuperTrain.cs | 93 +- .../Save Editors/Gen7/SAV_FestivalPlaza.cs | 44 +- .../Save Editors/Gen7/SAV_Pokebean.cs | 3 +- .../Save Editors/Gen8/SAV_PokedexSWSH.cs | 8 +- .../Subforms/Save Editors/SAV_EventWork.cs | 14 +- .../Subforms/Save Editors/SAV_Inventory.cs | 2 +- .../PKHeX.Core.Tests/Legality/ShadowTests.cs | 11 +- Tests/PKHeX.Core.Tests/PKM/StringTests.cs | 8 +- Tests/PKHeX.Core.Tests/Saves/PokeDex.cs | 29 +- .../PKHeX.Core.Tests/Util/ConvertUtilTests.cs | 60 +- Tests/PKHeX.Core.Tests/Util/FlagUtilTests.cs | 46 + .../Util/StringQualityTests.cs | 6 +- 353 files changed, 7728 insertions(+), 7066 deletions(-) create mode 100644 PKHeX.Core/PKM/Shared/ISanityChecksum.cs create mode 100644 PKHeX.Core/PKM/Strings/StringConverter3GC.cs create mode 100644 PKHeX.Core/PKM/Strings/StringConverter4GC.cs create mode 100644 PKHeX.Core/PKM/Strings/StringConverter4Util.cs create mode 100644 PKHeX.Core/PKM/Strings/StringConverter5.cs create mode 100644 PKHeX.Core/PKM/Strings/StringConverter6.cs create mode 100644 PKHeX.Core/PKM/Strings/StringConverter7.cs create mode 100644 PKHeX.Core/PKM/Strings/StringConverter8.cs create mode 100644 PKHeX.Core/PKM/Strings/StringConverterOption.cs delete mode 100644 PKHeX.Core/Util/BigEndian.cs create mode 100644 Tests/PKHeX.Core.Tests/Util/FlagUtilTests.cs diff --git a/PKHeX.Core/Editing/Applicators/BallApplicator.cs b/PKHeX.Core/Editing/Applicators/BallApplicator.cs index 1ecac547c..9a7d75f7e 100644 --- a/PKHeX.Core/Editing/Applicators/BallApplicator.cs +++ b/PKHeX.Core/Editing/Applicators/BallApplicator.cs @@ -39,7 +39,7 @@ public static IEnumerable GetLegalBalls(PKM pkm) public static int ApplyBallLegalRandom(PKM pkm) { var balls = GetBallListFromColor(pkm).ToArray(); - Util.Shuffle(balls); + Util.Shuffle(balls.AsSpan()); return ApplyFirstLegalBall(pkm, balls); } @@ -118,7 +118,7 @@ static BallApplicator() { var matchingColors = BallColors[c]; var extra = allBalls.Except(matchingColors).ToArray(); - Util.Shuffle(extra); + Util.Shuffle(extra.AsSpan()); BallColors[c] = ArrayUtil.ConcatAll(matchingColors, extra, end); } } diff --git a/PKHeX.Core/Editing/Applicators/MoveSetApplicator.cs b/PKHeX.Core/Editing/Applicators/MoveSetApplicator.cs index 2efa62a45..c4556c506 100644 --- a/PKHeX.Core/Editing/Applicators/MoveSetApplicator.cs +++ b/PKHeX.Core/Editing/Applicators/MoveSetApplicator.cs @@ -47,7 +47,7 @@ public static int[] GetMoveSet(this LegalityAnalysis la, bool random = false) m = m.Intersect(learn).ToArray(); if (random && !la.pkm.IsEgg) - Util.Shuffle(m); + Util.Shuffle(m.AsSpan()); const int count = 4; if (m.Length > count) diff --git a/PKHeX.Core/Editing/Bulk/BatchEditing.cs b/PKHeX.Core/Editing/Bulk/BatchEditing.cs index 339233d55..fea55b69e 100644 --- a/PKHeX.Core/Editing/Bulk/BatchEditing.cs +++ b/PKHeX.Core/Editing/Bulk/BatchEditing.cs @@ -432,12 +432,9 @@ private static ModifyResult SetByteArrayProperty(PKM pk, StringInstruction cmd) { switch (cmd.PropertyName) { - case nameof(PKM.Nickname_Trash): - pk.Nickname_Trash = ConvertToBytes(cmd.PropertyValue); - return ModifyResult.Modified; - case nameof(PKM.OT_Trash): - pk.OT_Trash = ConvertToBytes(cmd.PropertyValue); - return ModifyResult.Modified; + case nameof(PKM.Nickname_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.Nickname_Trash); return ModifyResult.Modified; + case nameof(PKM.OT_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.OT_Trash); return ModifyResult.Modified; + case nameof(PKM.HT_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.HT_Trash); return ModifyResult.Modified; default: return ModifyResult.Error; } diff --git a/PKHeX.Core/Editing/PKM/EntitySummary.cs b/PKHeX.Core/Editing/PKM/EntitySummary.cs index de062a7fd..dc1011fbe 100644 --- a/PKHeX.Core/Editing/PKM/EntitySummary.cs +++ b/PKHeX.Core/Editing/PKM/EntitySummary.cs @@ -96,7 +96,7 @@ public class EntitySummary // do NOT seal, allow inheritance public string Relearn2 => Get(Strings.movelist, pkm.RelearnMove2); public string Relearn3 => Get(Strings.movelist, pkm.RelearnMove3); public string Relearn4 => Get(Strings.movelist, pkm.RelearnMove4); - public ushort Checksum => pkm.Checksum; + public ushort Checksum => pkm is ISanityChecksum s ? s.Checksum : PokeCrypto.GetCHK(pkm.Data, pkm.SIZE_STORED); public int Friendship => pkm.OT_Friendship; public int Egg_Year => pkm.EggMetDate.GetValueOrDefault().Year; public int Egg_Month => pkm.EggMetDate.GetValueOrDefault().Month; diff --git a/PKHeX.Core/Editing/PKM/EntityTemplates.cs b/PKHeX.Core/Editing/PKM/EntityTemplates.cs index 524128cc6..09abc70ab 100644 --- a/PKHeX.Core/Editing/PKM/EntityTemplates.cs +++ b/PKHeX.Core/Editing/PKM/EntityTemplates.cs @@ -51,10 +51,10 @@ private static void ApplyTrashBytes(PKM pk, ITrainerInfo tr) switch (tr) { case SAV1 s1: - pk12.OT_Trash = s1.OT_Trash; + s1.OT_Trash.CopyTo(pk12.OT_Trash); break; case SAV2 s2: - pk12.OT_Trash = s2.OT_Trash; + s2.OT_Trash.CopyTo(pk12.OT_Trash); break; } } diff --git a/PKHeX.Core/Editing/Saves/Editors/EventWork/EventWork.cs b/PKHeX.Core/Editing/Saves/Editors/EventWork/EventWork.cs index f7e652775..430613924 100644 --- a/PKHeX.Core/Editing/Saves/Editors/EventWork/EventWork.cs +++ b/PKHeX.Core/Editing/Saves/Editors/EventWork/EventWork.cs @@ -24,8 +24,8 @@ public EventWork(int index, EventVarType t, IReadOnlyList pieces) : base foreach (var s in items) { - if (int.TryParse(s[0], out var val)) - Options.Add(new EventWorkVal(s[1], val)); + if (int.TryParse(s[0], out var value)) + Options.Add(new EventWorkVal(s[1], value)); } } } diff --git a/PKHeX.Core/Editing/Saves/Editors/EventWork/SplitEventEditor.cs b/PKHeX.Core/Editing/Saves/Editors/EventWork/SplitEventEditor.cs index 8a9c528f6..ca0259cb5 100644 --- a/PKHeX.Core/Editing/Saves/Editors/EventWork/SplitEventEditor.cs +++ b/PKHeX.Core/Editing/Saves/Editors/EventWork/SplitEventEditor.cs @@ -50,16 +50,16 @@ public void Save() { foreach (var item in g.Vars) { - var val = ((EventWork)item).Value; - Block.SetWork(item.RawIndex, val); + var value = ((EventWork)item).Value; + Block.SetWork(item.RawIndex, value); } } foreach (var g in Flag) { foreach (var item in g.Vars) { - var val = ((EventFlag)item).Flag; - Block.SetFlag(item.RawIndex, val); + var value = ((EventFlag)item).Flag; + Block.SetFlag(item.RawIndex, value); } } } diff --git a/PKHeX.Core/Editing/Saves/Editors/SaveDataEditor.cs b/PKHeX.Core/Editing/Saves/Editors/SaveDataEditor.cs index 6f7dd24e0..40b44b0bb 100644 --- a/PKHeX.Core/Editing/Saves/Editors/SaveDataEditor.cs +++ b/PKHeX.Core/Editing/Saves/Editors/SaveDataEditor.cs @@ -56,8 +56,8 @@ public sealed class FakeSaveFile : SaveFile public override bool ChecksumsValid => true; public override string ChecksumInfo => string.Empty; public override int Generation => 3; - public override string GetString(byte[] data, int offset, int length) => string.Empty; - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) => Array.Empty(); + public override string GetString(ReadOnlySpan data) => string.Empty; + public override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) => 0; public override PersonalTable Personal => PersonalTable.RS; public override int MaxEV => 0; public override IReadOnlyList HeldItems => Legal.HeldItems_RS; diff --git a/PKHeX.Core/Editing/Saves/Slots/Info/SlotCache.cs b/PKHeX.Core/Editing/Saves/Slots/Info/SlotCache.cs index 91816f0e3..faaabeb17 100644 --- a/PKHeX.Core/Editing/Saves/Slots/Info/SlotCache.cs +++ b/PKHeX.Core/Editing/Saves/Slots/Info/SlotCache.cs @@ -55,11 +55,7 @@ private string GetFileName() return $"{fn} @ "; } - public bool IsDataValid() - { - var e = Entity; - return e.Species != 0 && e.ChecksumValid && (e.Sanity == 0 || e is BK4); - } + public bool IsDataValid() => Entity.Species != 0 && Entity.Valid; public int CompareTo(SlotCache? other) { diff --git a/PKHeX.Core/Editing/Showdown/ShowdownSet.cs b/PKHeX.Core/Editing/Showdown/ShowdownSet.cs index d23e48ca5..d9f4e98da 100644 --- a/PKHeX.Core/Editing/Showdown/ShowdownSet.cs +++ b/PKHeX.Core/Editing/Showdown/ShowdownSet.cs @@ -311,7 +311,7 @@ private string GetSpeciesNickname(string specForm) return $"{Nickname} ({specForm})"; } - private static IList GetStringStats(int[] stats, int ignore) + private static IList GetStringStats(ReadOnlySpan stats, int ignore) { var result = new List(); for (int i = 0; i < stats.Length; i++) diff --git a/PKHeX.Core/Game/GameStrings/GameDataSource.cs b/PKHeX.Core/Game/GameStrings/GameDataSource.cs index 1906bfe98..7259748a2 100644 --- a/PKHeX.Core/Game/GameStrings/GameDataSource.cs +++ b/PKHeX.Core/Game/GameStrings/GameDataSource.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using System.Linq; +using System; +using System.Collections.Generic; namespace PKHeX.Core { @@ -31,14 +31,10 @@ public sealed class GameDataSource new ComboItem("CHT (繁體中文)", (int)LanguageID.ChineseT), }; - // ignores Poke/Great/Ultra - private static readonly ushort[] ball_nums = { 007, 576, 013, 492, 497, 014, 495, 493, 496, 494, 011, 498, 008, 006, 012, 015, 009, 005, 499, 010, 001, 016, 851 }; - private static readonly byte[] ball_vals = { 007, 025, 013, 017, 022, 014, 020, 018, 021, 019, 011, 023, 008, 006, 012, 015, 009, 005, 024, 010, 001, 016, 026 }; - public GameDataSource(GameStrings s) { Strings = s; - BallDataSource = Util.GetVariedCBListBall(s.itemlist, ball_nums, ball_vals); + BallDataSource = GetBalls(s.itemlist); SpeciesDataSource = Util.GetCBList(s.specieslist); NatureDataSource = Util.GetCBList(s.natures); AbilityDataSource = Util.GetCBList(s.abilitylist); @@ -75,10 +71,18 @@ public GameDataSource(GameStrings s) public readonly IReadOnlyList HaXMoveDataSource; public readonly IReadOnlyList GroundTileDataSource; + private static IReadOnlyList GetBalls(string[] itemList) + { + // ignores Poke/Great/Ultra + ReadOnlySpan ball_nums = stackalloc ushort[] { 007, 576, 013, 492, 497, 014, 495, 493, 496, 494, 011, 498, 008, 006, 012, 015, 009, 005, 499, 010, 001, 016, 851 }; + ReadOnlySpan ball_vals = stackalloc byte[] { 007, 025, 013, 017, 022, 014, 020, 018, 021, 019, 011, 023, 008, 006, 012, 015, 009, 005, 024, 010, 001, 016, 026 }; + return Util.GetVariedCBListBall(itemList, ball_nums, ball_vals); + } + private static IReadOnlyList GetVersionList(GameStrings s) { var list = s.gamelist; - var games = new byte[] + ReadOnlySpan games = stackalloc byte[] { 48, 49, // 8 bdsp 44, 45, // 8 swsh @@ -111,7 +115,7 @@ public List GetItemDataSource(GameVersion game, int generation, IRead public static IReadOnlyList LanguageDataSource(int gen) { - var languages = LanguageList.ToList(); + var languages = new List(LanguageList); if (gen == 3) languages.RemoveAll(l => l.Value >= (int)LanguageID.Korean); else if (gen < 7) diff --git a/PKHeX.Core/Legality/Areas/EncounterArea1.cs b/PKHeX.Core/Legality/Areas/EncounterArea1.cs index 54ee9da2d..235779811 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea1.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea1.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace PKHeX.Core { @@ -21,7 +22,7 @@ public static EncounterArea1[] GetAreas(byte[][] input, GameVersion game) return result; } - private EncounterArea1(byte[] data, GameVersion game) : base(game) + private EncounterArea1(ReadOnlySpan data, GameVersion game) : base(game) { Location = data[0]; // 1 byte unused @@ -33,15 +34,21 @@ private EncounterArea1(byte[] data, GameVersion game) : base(game) for (int i = 0; i < slots.Length; i++) { int offset = 4 + (4 * i); - int species = data[offset + 0]; - int slotNum = data[offset + 1]; - int min = data[offset + 2]; - int max = data[offset + 3]; - slots[i] = new EncounterSlot1(this, species, min, max, slotNum); + var entry = data.Slice(offset, 4); + slots[i] = ReadSlot(entry); } Slots = slots; } + private EncounterSlot1 ReadSlot(ReadOnlySpan entry) + { + int species = entry[0]; + int slotNum = entry[1]; + int min = entry[2]; + int max = entry[3]; + return new EncounterSlot1(this, species, min, max, slotNum); + } + public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyList chain) { int rate = pkm is PK1 pk1 ? pk1.Catch_Rate : -1; diff --git a/PKHeX.Core/Legality/Areas/EncounterArea2.cs b/PKHeX.Core/Legality/Areas/EncounterArea2.cs index 2a5594786..1244cb000 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea2.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea2.cs @@ -28,7 +28,7 @@ public static EncounterArea2[] GetAreas(byte[][] input, GameVersion game) return result; } - private EncounterArea2(byte[] data, GameVersion game) : base(game) + private EncounterArea2(ReadOnlySpan data, GameVersion game) : base(game) { Location = data[0]; Time = (EncounterTime)data[1]; @@ -63,22 +63,28 @@ private EncounterArea2(byte[] data, GameVersion game) : base(game) } } - private EncounterSlot2[] ReadSlots(byte[] data, int count, int start) + private EncounterSlot2[] ReadSlots(ReadOnlySpan data, int count, int start) { var slots = new EncounterSlot2[count]; for (int i = 0; i < slots.Length; i++) { int offset = start + (4 * i); - int species = data[offset + 0]; - int slotNum = data[offset + 1]; - int min = data[offset + 2]; - int max = data[offset + 3]; - slots[i] = new EncounterSlot2(this, species, min, max, slotNum); + var entry = data.Slice(offset, 4); + slots[i] = ReadSlot(entry); } return slots; } + private EncounterSlot2 ReadSlot(ReadOnlySpan entry) + { + int species = entry[0]; + int slotNum = entry[1]; + int min = entry[2]; + int max = entry[3]; + return new EncounterSlot2(this, species, min, max, slotNum); + } + public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyList chain) { if (pkm is not ICaughtData2 pk2 || pk2.CaughtData == 0) diff --git a/PKHeX.Core/Legality/Areas/EncounterArea3.cs b/PKHeX.Core/Legality/Areas/EncounterArea3.cs index fcafbe735..0420f48a5 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea3.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea3.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -30,7 +31,7 @@ public static EncounterArea3[] GetAreasSwarm(byte[][] input, GameVersion game) return result; } - private EncounterArea3(byte[] data, GameVersion game) : base(game) + private EncounterArea3(ReadOnlySpan data, GameVersion game) : base(game) { Location = data[0] | (data[1] << 8); Type = (SlotType)data[2]; @@ -39,7 +40,7 @@ private EncounterArea3(byte[] data, GameVersion game) : base(game) Slots = ReadRegularSlots(data); } - private EncounterArea3(byte[] data, GameVersion game, SlotType type) : base(game) + private EncounterArea3(ReadOnlySpan data, GameVersion game, SlotType type) : base(game) { Location = data[0] | (data[1] << 8); Type = type; @@ -48,7 +49,7 @@ private EncounterArea3(byte[] data, GameVersion game, SlotType type) : base(game Slots = ReadSwarmSlots(data); } - private EncounterSlot3[] ReadRegularSlots(byte[] data) + private EncounterSlot3[] ReadRegularSlots(ReadOnlySpan data) { const int size = 10; int count = (data.Length - 4) / size; @@ -56,24 +57,29 @@ private EncounterSlot3[] ReadRegularSlots(byte[] data) for (int i = 0; i < slots.Length; i++) { int offset = 4 + (size * i); - - int species = BitConverter.ToUInt16(data, offset + 0); - int form = data[offset + 2]; - int slotNum = data[offset + 3]; - int min = data[offset + 4]; - int max = data[offset + 5]; - - int mpi = data[offset + 6]; - int mpc = data[offset + 7]; - int sti = data[offset + 8]; - int stc = data[offset + 9]; - slots[i] = new EncounterSlot3(this, species, form, min, max, slotNum, mpi, mpc, sti, stc); + var entry = data.Slice(offset, size); + slots[i] = ReadRegularSlot(entry); } return slots; } - private EncounterSlot3[] ReadSwarmSlots(byte[] data) + private EncounterSlot3 ReadRegularSlot(ReadOnlySpan entry) + { + int species = ReadUInt16LittleEndian(entry); + int form = entry[2]; + int slotNum = entry[3]; + int min = entry[4]; + int max = entry[5]; + + int mpi = entry[6]; + int mpc = entry[7]; + int sti = entry[8]; + int stc = entry[9]; + return new EncounterSlot3(this, species, form, min, max, slotNum, mpi, mpc, sti, stc); + } + + private EncounterSlot3[] ReadSwarmSlots(ReadOnlySpan data) { const int size = 14; int count = (data.Length - 4) / size; @@ -81,27 +87,32 @@ private EncounterSlot3[] ReadSwarmSlots(byte[] data) for (int i = 0; i < slots.Length; i++) { int offset = 4 + (size * i); - - int species = BitConverter.ToUInt16(data, offset + 0); - // form always 0 - int slotNum = data[offset + 3]; - int min = data[offset + 4]; - int max = data[offset + 5]; - - int[] moves = - { - BitConverter.ToUInt16(data, offset + 6), - BitConverter.ToUInt16(data, offset + 8), - BitConverter.ToUInt16(data, offset + 10), - BitConverter.ToUInt16(data, offset + 12), - }; - - slots[i] = new EncounterSlot3Swarm(this, species, min, max, slotNum, moves); + var entry = data.Slice(offset, size); + slots[i] = ReadSwarmSlot(entry); } return slots; } + private EncounterSlot3Swarm ReadSwarmSlot(ReadOnlySpan entry) + { + int species = ReadUInt16LittleEndian(entry); + // form always 0 + int slotNum = entry[3]; + int min = entry[4]; + int max = entry[5]; + + int[] moves = + { + ReadUInt16LittleEndian(entry[06..]), + ReadUInt16LittleEndian(entry[08..]), + ReadUInt16LittleEndian(entry[10..]), + ReadUInt16LittleEndian(entry[12..]), + }; + + return new EncounterSlot3Swarm(this, species, min, max, slotNum, moves); + } + public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyList chain) { if (pkm.Format != 3) // Met Location and Met Level are changed on PK3->PK4 diff --git a/PKHeX.Core/Legality/Areas/EncounterArea4.cs b/PKHeX.Core/Legality/Areas/EncounterArea4.cs index 58b22694a..654327871 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea4.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea4.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -23,18 +24,18 @@ public static EncounterArea4[] GetAreas(byte[][] input, GameVersion game) return result; } - private EncounterArea4(byte[] data, GameVersion game) : base(game) + private EncounterArea4(ReadOnlySpan data, GameVersion game) : base(game) { - Location = data[0] | (data[1] << 8); + Location = ReadUInt16LittleEndian(data); Type = (SlotType)data[2]; Rate = data[3]; // although GroundTilePermission flags are 32bit, none have values > 16bit. - GroundTile = (GroundTilePermission) BitConverter.ToUInt16(data, 4); + GroundTile = (GroundTilePermission)ReadUInt16LittleEndian(data[4..]); Slots = ReadRegularSlots(data); } - private EncounterSlot4[] ReadRegularSlots(byte[] data) + private EncounterSlot4[] ReadRegularSlots(ReadOnlySpan data) { const int size = 10; int count = (data.Length - 6) / size; @@ -42,23 +43,27 @@ private EncounterSlot4[] ReadRegularSlots(byte[] data) for (int i = 0; i < slots.Length; i++) { int offset = 6 + (size * i); - - int species = BitConverter.ToUInt16(data, offset + 0); - int form = data[offset + 2]; - int slotNum = data[offset + 3]; - int min = data[offset + 4]; - int max = data[offset + 5]; - - int mpi = data[offset + 6]; - int mpc = data[offset + 7]; - int sti = data[offset + 8]; - int stc = data[offset + 9]; - slots[i] = new EncounterSlot4(this, species, form, min, max, slotNum, mpi, mpc, sti, stc); + var entry = data.Slice(offset, size); + slots[i] = ReadRegularSlot(entry); } return slots; } + private EncounterSlot4 ReadRegularSlot(ReadOnlySpan entry) + { + int species = ReadUInt16LittleEndian(entry); + int form = entry[2]; + int slotNum = entry[3]; + int min = entry[4]; + int max = entry[5]; + int mpi = entry[6]; + int mpc = entry[7]; + int sti = entry[8]; + int stc = entry[9]; + return new EncounterSlot4(this, species, form, min, max, slotNum, mpi, mpc, sti, stc); + } + public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyList chain) { if (pkm.Format != 4) // Met Location and Met Level are changed on PK4->PK5 diff --git a/PKHeX.Core/Legality/Areas/EncounterArea5.cs b/PKHeX.Core/Legality/Areas/EncounterArea5.cs index 9112a1889..8fa4dc1ea 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea5.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea5.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -21,15 +22,15 @@ public static EncounterArea5[] GetAreas(byte[][] input, GameVersion game) return result; } - private EncounterArea5(byte[] data, GameVersion game) : base(game) + private EncounterArea5(ReadOnlySpan data, GameVersion game) : base(game) { - Location = data[0] | (data[1] << 8); + Location = ReadUInt16LittleEndian(data); Type = (SlotType)data[2]; Slots = ReadSlots(data); } - private EncounterSlot5[] ReadSlots(byte[] data) + private EncounterSlot5[] ReadSlots(ReadOnlySpan data) { const int size = 4; int count = (data.Length - 4) / size; @@ -37,17 +38,23 @@ private EncounterSlot5[] ReadSlots(byte[] data) for (int i = 0; i < slots.Length; i++) { int offset = 4 + (size * i); - ushort SpecForm = BitConverter.ToUInt16(data, offset); - int species = SpecForm & 0x3FF; - int form = SpecForm >> 11; - int min = data[offset + 2]; - int max = data[offset + 3]; - slots[i] = new EncounterSlot5(this, species, form, min, max); + var entry = data.Slice(offset, size); + slots[i] = ReadSlot(entry); } return slots; } + private EncounterSlot5 ReadSlot(ReadOnlySpan entry) + { + ushort SpecForm = ReadUInt16LittleEndian(entry); + int species = SpecForm & 0x3FF; + int form = SpecForm >> 11; + int min = entry[2]; + int max = entry[3]; + return new EncounterSlot5(this, species, form, min, max); + } + public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyList chain) { foreach (var slot in Slots) @@ -71,4 +78,4 @@ public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyLi } } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Legality/Areas/EncounterArea6AO.cs b/PKHeX.Core/Legality/Areas/EncounterArea6AO.cs index bc0cb3a3b..0a01b78fc 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea6AO.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea6AO.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -21,15 +22,15 @@ public static EncounterArea6AO[] GetAreas(byte[][] input, GameVersion game) return result; } - private EncounterArea6AO(byte[] data, GameVersion game) : base(game) + private EncounterArea6AO(ReadOnlySpan data, GameVersion game) : base(game) { - Location = data[0] | (data[1] << 8); + Location = ReadInt16LittleEndian(data); Type = (SlotType)data[2]; Slots = ReadSlots(data); } - private EncounterSlot6AO[] ReadSlots(byte[] data) + private EncounterSlot6AO[] ReadSlots(ReadOnlySpan data) { const int size = 4; int count = (data.Length - 4) / size; @@ -37,17 +38,23 @@ private EncounterSlot6AO[] ReadSlots(byte[] data) for (int i = 0; i < slots.Length; i++) { int offset = 4 + (size * i); - ushort SpecForm = BitConverter.ToUInt16(data, offset); - int species = SpecForm & 0x3FF; - int form = SpecForm >> 11; - int min = data[offset + 2]; - int max = data[offset + 3]; - slots[i] = new EncounterSlot6AO(this, species, form, min, max); + var entry = data.Slice(offset, size); + slots[i] = ReadSlot(entry); } return slots; } + private EncounterSlot6AO ReadSlot(ReadOnlySpan entry) + { + ushort SpecForm = ReadUInt16LittleEndian(entry); + int species = SpecForm & 0x3FF; + int form = SpecForm >> 11; + int min = entry[2]; + int max = entry[3]; + return new EncounterSlot6AO(this, species, form, min, max); + } + private const int FluteBoostMin = 4; // White Flute decreases levels. private const int FluteBoostMax = 4; // Black Flute increases levels. private const int DexNavBoost = 30; // Maximum DexNav chain diff --git a/PKHeX.Core/Legality/Areas/EncounterArea6XY.cs b/PKHeX.Core/Legality/Areas/EncounterArea6XY.cs index aaf20bf4b..07f1f1124 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea6XY.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea6XY.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -30,7 +31,7 @@ public EncounterArea6XY() : base(GameVersion.XY) Slots = LoadSafariSlots(); } - private EncounterArea6XY(byte[] data, GameVersion game) : base(game) + private EncounterArea6XY(ReadOnlySpan data, GameVersion game) : base(game) { Location = data[0] | (data[1] << 8); Type = (SlotType)data[2]; @@ -78,11 +79,11 @@ private EncounterSlot6XY[] LoadSafariSlots() slots[i++] = new EncounterSlot6XY(this, (int)Species.Floette, 3, 30, 30); // Region Random Vivillon - slots[i] = new EncounterSlot6XY(this, (int)Species.Vivillon, 30, 30, 30); + slots[i] = new EncounterSlot6XY(this, (int)Species.Vivillon, EncounterSlot.FormVivillon, 30, 30); return slots; } - private EncounterSlot6XY[] ReadSlots(byte[] data) + private EncounterSlot6XY[] ReadSlots(ReadOnlySpan data) { const int size = 4; int count = (data.Length - 4) / size; @@ -90,11 +91,12 @@ private EncounterSlot6XY[] ReadSlots(byte[] data) for (int i = 0; i < slots.Length; i++) { int offset = 4 + (size * i); - ushort SpecForm = BitConverter.ToUInt16(data, offset); + var entry = data.Slice(offset, size); + ushort SpecForm = ReadUInt16LittleEndian(entry); int species = SpecForm & 0x3FF; int form = SpecForm >> 11; - int min = data[offset + 2]; - int max = data[offset + 3]; + int min = entry[2]; + int max = entry[3]; slots[i] = new EncounterSlot6XY(this, species, form, min, max); } diff --git a/PKHeX.Core/Legality/Areas/EncounterArea7.cs b/PKHeX.Core/Legality/Areas/EncounterArea7.cs index 04374d9ee..6c10e7cd3 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea7.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea7.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -21,7 +22,7 @@ public static EncounterArea7[] GetAreas(byte[][] input, GameVersion game) return result; } - private EncounterArea7(byte[] data, GameVersion game) : base(game) + private EncounterArea7(ReadOnlySpan data, GameVersion game) : base(game) { Location = data[0] | (data[1] << 8); Type = (SlotType)data[2]; @@ -29,7 +30,7 @@ private EncounterArea7(byte[] data, GameVersion game) : base(game) Slots = ReadSlots(data); } - private EncounterSlot7[] ReadSlots(byte[] data) + private EncounterSlot7[] ReadSlots(ReadOnlySpan data) { const int size = 4; int count = (data.Length - 4) / size; @@ -37,17 +38,23 @@ private EncounterSlot7[] ReadSlots(byte[] data) for (int i = 0; i < slots.Length; i++) { int offset = 4 + (size * i); - ushort SpecForm = BitConverter.ToUInt16(data, offset); - int species = SpecForm & 0x3FF; - int form = SpecForm >> 11; - int min = data[offset + 2]; - int max = data[offset + 3]; - slots[i] = new EncounterSlot7(this, species, form, min, max); + var entry = data.Slice(offset, size); + slots[i] = ReadSlot(entry); } return slots; } + private EncounterSlot7 ReadSlot(ReadOnlySpan entry) + { + ushort SpecForm = ReadUInt16LittleEndian(entry); + int species = SpecForm & 0x3FF; + int form = SpecForm >> 11; + int min = entry[2]; + int max = entry[3]; + return new EncounterSlot7(this, species, form, min, max); + } + public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyList chain) { foreach (var slot in Slots) diff --git a/PKHeX.Core/Legality/Areas/EncounterArea7b.cs b/PKHeX.Core/Legality/Areas/EncounterArea7b.cs index e580553e1..0372fffc8 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea7b.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea7b.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace PKHeX.Core { @@ -20,13 +21,13 @@ public static EncounterArea7b[] GetAreas(byte[][] input, GameVersion game) return result; } - private EncounterArea7b(byte[] data, GameVersion game) : base(game) + private EncounterArea7b(ReadOnlySpan data, GameVersion game) : base(game) { Location = data[0] | (data[1] << 8); Slots = ReadSlots(data); } - private EncounterSlot7b[] ReadSlots(byte[] data) + private EncounterSlot7b[] ReadSlots(ReadOnlySpan data) { const int size = 4; int count = (data.Length - 2) / size; @@ -34,16 +35,22 @@ private EncounterSlot7b[] ReadSlots(byte[] data) for (int i = 0; i < slots.Length; i++) { int offset = 2 + (size * i); - int species = data[offset]; // always < 255; only original 151 - // form is always 0 - int min = data[offset + 2]; - int max = data[offset + 3]; - slots[i] = new EncounterSlot7b(this, species, min, max); + var entry = data.Slice(offset, size); + slots[i] = ReadSlot(entry); } return slots; } + private EncounterSlot7b ReadSlot(ReadOnlySpan entry) + { + int species = entry[0]; // always < 255; only original 151 + // form is always 0 + int min = entry[2]; + int max = entry[3]; + return new EncounterSlot7b(this, species, min, max); + } + private const int CatchComboBonus = 1; public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyList chain) diff --git a/PKHeX.Core/Legality/Areas/EncounterArea7g.cs b/PKHeX.Core/Legality/Areas/EncounterArea7g.cs index 6e7f0f80e..690b7e3d3 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea7g.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea7g.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -37,9 +38,9 @@ internal static EncounterArea7g[] GetArea(byte[][] data) private const int entrySize = (2 * sizeof(int)) + 2; - private static EncounterArea7g GetArea(byte[] data) + private static EncounterArea7g GetArea(ReadOnlySpan data) { - var sf = BitConverter.ToUInt16(data, 0); + var sf = ReadUInt16LittleEndian(data); int species = sf & 0x7FF; int form = sf >> 11; @@ -48,20 +49,21 @@ private static EncounterArea7g GetArea(byte[] data) for (int i = 0; i < result.Length; i++) { var offset = (i * entrySize) + 2; - result[i] = ReadSlot(data, offset, area, species, form); + var entry = data.Slice(offset, entrySize); + result[i] = ReadSlot(entry, area, species, form); } return area; } - private static EncounterSlot7GO ReadSlot(byte[] data, int offset, EncounterArea7g area, int species, int form) + private static EncounterSlot7GO ReadSlot(ReadOnlySpan entry, EncounterArea7g area, int species, int form) { - int start = BitConverter.ToInt32(data, offset); - int end = BitConverter.ToInt32(data, offset + 4); - var sg = data[offset + 8]; + int start = ReadInt32LittleEndian(entry); + int end = ReadInt32LittleEndian(entry[4..]); + var sg = entry[8]; var shiny = (Shiny)(sg & 0x3F); var gender = (Gender)(sg >> 6); - var type = (PogoType)data[offset + 9]; + var type = (PogoType)entry[9]; return new EncounterSlot7GO(area, species, form, start, end, shiny, gender, type); } diff --git a/PKHeX.Core/Legality/Areas/EncounterArea8.cs b/PKHeX.Core/Legality/Areas/EncounterArea8.cs index cf67096cb..2556e12b0 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea8.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea8.cs @@ -3,6 +3,7 @@ using System.Linq; using static PKHeX.Core.AreaWeather8; using static PKHeX.Core.AreaSlotType8; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -369,7 +370,7 @@ private EncounterArea8(byte[] areaData, bool symbol, GameVersion game) : base(ga Slots = ReadSlots(areaData, areaData[1]); } - private EncounterSlot8[] ReadSlots(byte[] areaData, byte slotCount) + private EncounterSlot8[] ReadSlots(ReadOnlySpan areaData, byte slotCount) { var slots = new EncounterSlot8[slotCount]; @@ -378,17 +379,20 @@ private EncounterSlot8[] ReadSlots(byte[] areaData, byte slotCount) do { // Read area metadata - var flags = (AreaWeather8) BitConverter.ToUInt16(areaData, ofs); - var min = areaData[ofs + 2]; - var max = areaData[ofs + 3]; - var count = areaData[ofs + 4]; - var slotType = (AreaSlotType8) areaData[ofs + 5]; + var meta = areaData.Slice(ofs, 6); + var flags = (AreaWeather8) ReadUInt16LittleEndian(meta); + var min = meta[2]; + var max = meta[3]; + var count = meta[4]; + var slotType = (AreaSlotType8)meta[5]; ofs += 6; // Read slots - for (int i = 0; i < count; i++, ctr++, ofs += 2) + const int bpe = 2; + for (int i = 0; i < count; i++, ctr++, ofs += bpe) { - var specForm = BitConverter.ToUInt16(areaData, ofs); + var entry = areaData.Slice(ofs, bpe); + var specForm = ReadUInt16LittleEndian(entry); var species = specForm & 0x7FF; var form = specForm >> 11; slots[ctr] = new EncounterSlot8(this, species, form, min, max, flags, slotType); diff --git a/PKHeX.Core/Legality/Areas/EncounterArea8b.cs b/PKHeX.Core/Legality/Areas/EncounterArea8b.cs index 56c2e228b..028c15ac3 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea8b.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea8b.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -21,7 +22,7 @@ public static EncounterArea8b[] GetAreas(byte[][] input, GameVersion game) return result; } - private EncounterArea8b(byte[] data, GameVersion game) : base(game) + private EncounterArea8b(ReadOnlySpan data, GameVersion game) : base(game) { Location = data[0] | (data[1] << 8); Type = (SlotType)data[2]; @@ -29,7 +30,7 @@ private EncounterArea8b(byte[] data, GameVersion game) : base(game) Slots = ReadSlots(data); } - private EncounterSlot8b[] ReadSlots(byte[] data) + private EncounterSlot8b[] ReadSlots(ReadOnlySpan data) { const int size = 4; int count = (data.Length - 4) / size; @@ -37,16 +38,22 @@ private EncounterSlot8b[] ReadSlots(byte[] data) for (int i = 0; i < slots.Length; i++) { int offset = 4 + (size * i); - ushort SpecForm = BitConverter.ToUInt16(data, offset); - int species = SpecForm & 0x3FF; - int form = SpecForm >> 11; - int min = data[offset + 2]; - int max = data[offset + 3]; - slots[i] = new EncounterSlot8b(this, species, form, min, max); + var entry = data.Slice(offset, size); + slots[i] = ReadSlot(entry); } return slots; } + private EncounterSlot8b ReadSlot(ReadOnlySpan data) + { + ushort SpecForm = ReadUInt16LittleEndian(data); + int species = SpecForm & 0x3FF; + int form = SpecForm >> 11; + int min = data[2]; + int max = data[3]; + return new EncounterSlot8b(this, species, form, min, max); + } + public override bool IsMatchLocation(int location) { if (base.IsMatchLocation(location)) diff --git a/PKHeX.Core/Legality/Areas/EncounterArea8g.cs b/PKHeX.Core/Legality/Areas/EncounterArea8g.cs index e938a2622..480916ab2 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea8g.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea8g.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -37,9 +38,9 @@ internal static EncounterArea8g[] GetArea(byte[][] data) private const int entrySize = (2 * sizeof(int)) + 2; - private static EncounterArea8g GetArea(byte[] data) + private static EncounterArea8g GetArea(ReadOnlySpan data) { - var sf = BitConverter.ToUInt16(data, 0); + var sf = ReadUInt16LittleEndian(data); int species = sf & 0x7FF; int form = sf >> 11; @@ -50,20 +51,21 @@ private static EncounterArea8g GetArea(byte[] data) for (int i = 0; i < result.Length; i++) { var offset = (i * entrySize) + 2; - result[i] = ReadSlot(data, offset, area, species, form, group); + var entry = data.Slice(offset, entrySize); + result[i] = ReadSlot(entry, area, species, form, group); } return area; } - private static EncounterSlot8GO ReadSlot(byte[] data, int offset, EncounterArea8g area, int species, int form, GameVersion group) + private static EncounterSlot8GO ReadSlot(ReadOnlySpan entry, EncounterArea8g area, int species, int form, GameVersion group) { - int start = BitConverter.ToInt32(data, offset); - int end = BitConverter.ToInt32(data, offset + 4); - var sg = data[offset + 8]; + int start = ReadInt32LittleEndian(entry); + int end = ReadInt32LittleEndian(entry[4..]); + var sg = entry[8]; var shiny = (Shiny)(sg & 0x3F); var gender = (Gender)(sg >> 6); - var type = (PogoType)data[offset + 9]; + var type = (PogoType)entry[9]; return new EncounterSlot8GO(area, species, form, start, end, shiny, gender, type, group); } diff --git a/PKHeX.Core/Legality/Encounters/Data/EncounterEvent.cs b/PKHeX.Core/Legality/Encounters/Data/EncounterEvent.cs index f69d3c6b0..9e55e0bfe 100644 --- a/PKHeX.Core/Legality/Encounters/Data/EncounterEvent.cs +++ b/PKHeX.Core/Legality/Encounters/Data/EncounterEvent.cs @@ -36,24 +36,24 @@ public static class EncounterEvent /// Indicates if the databases are initialized. public static bool Initialized => MGDB_G3.Count != 0; - private static PCD[] GetPCDDB(byte[] bin) => Get(bin, PCD.Size, d => new PCD(d)); - private static PGF[] GetPGFDB(byte[] bin) => Get(bin, PGF.Size, d => new PGF(d)); + private static PCD[] GetPCDDB(ReadOnlySpan bin) => Get(bin, PCD.Size, d => new PCD(d)); + private static PGF[] GetPGFDB(ReadOnlySpan bin) => Get(bin, PGF.Size, d => new PGF(d)); - private static WC6[] GetWC6DB(byte[] wc6bin, byte[] wc6full) => WC6Full.GetArray(wc6full, wc6bin); - private static WC7[] GetWC7DB(byte[] wc7bin, byte[] wc7full) => WC7Full.GetArray(wc7full, wc7bin); + private static WC6[] GetWC6DB(ReadOnlySpan wc6bin, ReadOnlySpan wc6full) => WC6Full.GetArray(wc6full, wc6bin); + private static WC7[] GetWC7DB(ReadOnlySpan wc7bin, ReadOnlySpan wc7full) => WC7Full.GetArray(wc7full, wc7bin); - private static WB7[] GetWB7DB(byte[] bin) => Get(bin, WB7.SizeFull, d => new WB7(d)); - private static WC8[] GetWC8DB(byte[] bin) => Get(bin, WC8.Size, d => new WC8(d)); - private static WB8[] GetWB8DB(byte[] bin) => Get(bin, WB8.Size, d => new WB8(d)); + private static WB7[] GetWB7DB(ReadOnlySpan bin) => Get(bin, WB7.SizeFull, d => new WB7(d)); + private static WC8[] GetWC8DB(ReadOnlySpan bin) => Get(bin, WC8.Size, d => new WC8(d)); + private static WB8[] GetWB8DB(ReadOnlySpan bin) => Get(bin, WB8.Size, d => new WB8(d)); - private static T[] Get(byte[] bin, int size, Func ctor) + private static T[] Get(ReadOnlySpan bin, int size, Func ctor) { var result = new T[bin.Length / size]; System.Diagnostics.Debug.Assert(result.Length * size == bin.Length); for (int i = 0; i < result.Length; i++) { var offset = i * size; - var slice = bin.Slice(offset, size); + var slice = bin.Slice(offset, size).ToArray(); result[i] = ctor(slice); } return result; diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot.cs index 1baed8a28..e12f9758d 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot.cs @@ -165,8 +165,8 @@ private void SetMetData(PKM pk, int level, int location) public bool IsRandomUnspecificForm => Form >= FormDynamic; private const int FormDynamic = FormVivillon; - private const int FormVivillon = 30; - protected const int FormRandom = 31; + protected internal const int FormVivillon = 30; + protected internal const int FormRandom = 31; private static int GetWildForm(PKM pk, int form, ITrainerInfo sav) { diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterCriteria.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterCriteria.cs index 4ec0f1389..e30fc50b4 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/EncounterCriteria.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterCriteria.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using static PKHeX.Core.AbilityPermission; namespace PKHeX.Core @@ -47,7 +48,7 @@ public sealed record EncounterCriteria /// Encounter template's IV restrictions. Speed is not last. /// Destination generation /// True if compatible, false if incompatible. - public bool IsIVsCompatible(int[] encounterIVs, int generation) + public bool IsIVsCompatible(Span encounterIVs, int generation) { var IVs = encounterIVs; if (!ivCanMatch(IV_HP , IVs[0])) return false; diff --git a/PKHeX.Core/Legality/Encounters/Verifiers/MysteryGiftVerifier.cs b/PKHeX.Core/Legality/Encounters/Verifiers/MysteryGiftVerifier.cs index d251086bf..cb5fe5c5f 100644 --- a/PKHeX.Core/Legality/Encounters/Verifiers/MysteryGiftVerifier.cs +++ b/PKHeX.Core/Legality/Encounters/Verifiers/MysteryGiftVerifier.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using static PKHeX.Core.LegalityCheckStrings; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -21,12 +22,13 @@ public static class MysteryGiftVerifier private static Dictionary GetRestriction(int generation) { var resource = RestrictionSetName(generation); - var data = Util.GetBinaryResource(resource); + var data = Util.GetBinaryResource(resource).AsSpan(); var dict = new Dictionary(); for (int i = 0; i < data.Length; i += 8) { - int hash = BitConverter.ToInt32(data, i + 0); - var restrict = BitConverter.ToInt32(data, i + 4); + var entry = data[i..]; + int hash = ReadInt32LittleEndian(entry); + var restrict = ReadInt32LittleEndian(entry[4..]); dict.Add(hash, (MysteryGiftRestriction)restrict); } return dict; @@ -34,21 +36,21 @@ public static class MysteryGiftVerifier public static CheckResult VerifyGift(PKM pk, MysteryGift g) { - bool restricted = TryGetRestriction(g, out var val); + bool restricted = TryGetRestriction(g, out var value); if (!restricted) return new CheckResult(CheckIdentifier.GameOrigin); - var ver = (int)val >> 16; + var ver = (int)value >> 16; if (ver != 0 && !CanVersionReceiveGift(g.Generation, ver, pk.Version)) return new CheckResult(Severity.Invalid, LEncGiftVersionNotDistributed, CheckIdentifier.GameOrigin); - var lang = val & MysteryGiftRestriction.LangRestrict; + var lang = value & MysteryGiftRestriction.LangRestrict; if (lang != 0 && !lang.HasFlagFast((MysteryGiftRestriction) (1 << pk.Language))) return new CheckResult(Severity.Invalid, string.Format(LOTLanguage, lang.GetSuggestedLanguage(), pk.Language), CheckIdentifier.GameOrigin); if (pk is IRegionOrigin tr) { - var region = val & MysteryGiftRestriction.RegionRestrict; + var region = value & MysteryGiftRestriction.RegionRestrict; if (region != 0 && !region.HasFlagFast((MysteryGiftRestriction)((int)MysteryGiftRestriction.RegionBase << tr.ConsoleRegion))) return new CheckResult(Severity.Invalid, LGeoHardwareRange, CheckIdentifier.GameOrigin); } diff --git a/PKHeX.Core/Legality/Encounters/Verifiers/VerifyRelearnMoves.cs b/PKHeX.Core/Legality/Encounters/Verifiers/VerifyRelearnMoves.cs index d389f0894..069c137a9 100644 --- a/PKHeX.Core/Legality/Encounters/Verifiers/VerifyRelearnMoves.cs +++ b/PKHeX.Core/Legality/Encounters/Verifiers/VerifyRelearnMoves.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using static PKHeX.Core.LegalityCheckStrings; using static PKHeX.Core.ParseSettings; @@ -127,7 +128,7 @@ internal static CheckResult[] VerifyEggMoveset(EncounterEgg e, CheckResult[] res private const int NO_DUPE = -1; - private static int IsAnyMoveDuplicate(int[] move) + private static int IsAnyMoveDuplicate(ReadOnlySpan move) { int m1 = move[0]; int m2 = move[1]; diff --git a/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet1.cs b/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet1.cs index 7f326508b..621b9d722 100644 --- a/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet1.cs +++ b/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet1.cs @@ -8,17 +8,18 @@ namespace PKHeX.Core /// public static class EvolutionSet1 { - private static EvolutionMethod GetMethod(byte[] data, int offset) + private static EvolutionMethod GetMethod(ReadOnlySpan data) { - int method = data[offset]; - int species = data[offset + 1]; - int arg = data[offset + 2]; + int method = data[0]; + int species = data[1]; + int arg = data[2]; + return (method == 1) ? new EvolutionMethod(method, species, level: arg) : new EvolutionMethod(method, species, argument: arg); } - public static IReadOnlyList GetArray(byte[] data, int maxSpecies) + public static IReadOnlyList GetArray(ReadOnlySpan data, int maxSpecies) { var evos = new EvolutionMethod[maxSpecies + 1][]; int ofs = 0; @@ -35,7 +36,7 @@ public static IReadOnlyList GetArray(byte[] data, int maxSpec var m = new EvolutionMethod[count]; for (int j = 0; j < m.Length; j++) { - m[j] = GetMethod(data, ofs); + m[j] = GetMethod(data.Slice(ofs, bpe)); ofs += bpe; } evos[i] = m; @@ -43,4 +44,4 @@ public static IReadOnlyList GetArray(byte[] data, int maxSpec return evos; } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet3.cs b/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet3.cs index 2a82454a6..edfc5887e 100644 --- a/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet3.cs +++ b/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet3.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,11 +9,11 @@ namespace PKHeX.Core /// public static class EvolutionSet3 { - private static EvolutionMethod GetMethod(byte[] data, int offset) + private static EvolutionMethod GetMethod(ReadOnlySpan data) { - int method = BitConverter.ToUInt16(data, offset + 0); - int arg = BitConverter.ToUInt16(data, offset + 2); - int species = SpeciesConverter.GetG4Species(BitConverter.ToUInt16(data, offset + 4)); + int method = ReadUInt16LittleEndian(data); + int arg = ReadUInt16LittleEndian(data[2..]); + int species = SpeciesConverter.GetG4Species(ReadUInt16LittleEndian(data[4..])); //2 bytes padding switch (method) @@ -42,7 +43,7 @@ private static EvolutionMethod GetMethod(byte[] data, int offset) } } - public static IReadOnlyList GetArray(byte[] data) + public static IReadOnlyList GetArray(ReadOnlySpan data) { var evos = new EvolutionMethod[Legal.MaxSpeciesID_3 + 1][]; evos[0] = Array.Empty(); @@ -70,10 +71,10 @@ public static IReadOnlyList GetArray(byte[] data) var set = new EvolutionMethod[count]; for (int j = 0; j < set.Length; j++) - set[j] = GetMethod(data, offset + (j * size)); + set[j] = GetMethod(data.Slice(offset + (j * size), size)); evos[g4species] = set; } return evos; } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet4.cs b/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet4.cs index 445499f4b..a5ec29501 100644 --- a/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet4.cs +++ b/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet4.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,11 +9,11 @@ namespace PKHeX.Core /// public static class EvolutionSet4 { - private static EvolutionMethod GetMethod(byte[] data, int offset) + private static EvolutionMethod GetMethod(ReadOnlySpan data) { - var method = data[offset]; // other byte unnecessary - int arg = BitConverter.ToUInt16(data, offset + 2); - int species = BitConverter.ToUInt16(data, offset + 4); + var method = data[0]; // other byte unnecessary + int arg = ReadUInt16LittleEndian(data[2..]); + int species = ReadUInt16LittleEndian(data[4..]); if (method == 0) throw new ArgumentOutOfRangeException(nameof(method)); @@ -26,7 +27,7 @@ private static EvolutionMethod GetMethod(byte[] data, int offset) return new EvolutionMethod(method, species, argument: arg, level: lvl); } - public static IReadOnlyList GetArray(byte[] data) + public static IReadOnlyList GetArray(ReadOnlySpan data) { const int bpe = 6; // bytes per evolution entry const int entries = 7; // amount of entries per species @@ -52,10 +53,10 @@ public static IReadOnlyList GetArray(byte[] data) var set = new EvolutionMethod[count]; for (int j = 0; j < set.Length; j++) - set[j] = GetMethod(data, offset + (j * bpe)); + set[j] = GetMethod(data.Slice(offset + (j * bpe), bpe)); evos[i] = set; } return evos; } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet5.cs b/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet5.cs index 519ba9203..979aa0232 100644 --- a/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet5.cs +++ b/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet5.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,11 +9,11 @@ namespace PKHeX.Core /// public static class EvolutionSet5 { - private static EvolutionMethod GetMethod(byte[] data, int offset) + private static EvolutionMethod GetMethod(ReadOnlySpan data) { - var method = data[offset]; // other byte unnecessary - int arg = BitConverter.ToUInt16(data, offset + 2); - int species = BitConverter.ToUInt16(data, offset + 4); + var method = data[0]; // other byte unnecessary + int arg = ReadUInt16LittleEndian(data[2..]); + int species = ReadUInt16LittleEndian(data[4..]); if (method == 0) throw new ArgumentOutOfRangeException(nameof(method)); @@ -21,24 +22,18 @@ private static EvolutionMethod GetMethod(byte[] data, int offset) return new EvolutionMethod(method, species, argument: arg, level: lvl); } - public static IReadOnlyList GetArray(byte[] data) - { - const int bpe = 6; // bytes per evolution entry - const int entries = 7; // amount of entries per species - const int size = entries * bpe; // bytes per species entry + private const int bpe = 6; // bytes per evolution entry + private const int entries = 7; // amount of entries per species + private const int size = entries * bpe; // bytes per species entry + public static IReadOnlyList GetArray(ReadOnlySpan data) + { var evos = new EvolutionMethod[data.Length / size][]; for (int i = 0; i < evos.Length; i++) { int offset = i * size; - int count = 0; - for (; count < entries; count++) - { - var methodOffset = offset + (count * bpe); - var method = data[methodOffset]; - if (method == 0) - break; - } + var rawEntries = data.Slice(offset, size); + var count = ScanCountEvolutions(rawEntries); if (count == 0) { evos[i] = Array.Empty(); @@ -47,10 +42,22 @@ public static IReadOnlyList GetArray(byte[] data) var set = new EvolutionMethod[count]; for (int j = 0; j < set.Length; j++) - set[j] = GetMethod(data, offset + (j * bpe)); + set[j] = GetMethod(rawEntries.Slice((j * bpe), bpe)); evos[i] = set; } return evos; } + + private static int ScanCountEvolutions(ReadOnlySpan data) + { + for (int count = 0; count < entries; count++) + { + var methodOffset = count * bpe; + var method = data[methodOffset]; + if (method == 0) + return count; + } + return entries; + } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet6.cs b/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet6.cs index 65861b3bb..2a3812e16 100644 --- a/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet6.cs +++ b/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet6.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -11,23 +12,29 @@ public static class EvolutionSet6 internal static readonly HashSet EvosWithArg = new() {6, 8, 16, 17, 18, 19, 20, 21, 22, 29}; private const int SIZE = 6; - private static EvolutionMethod[] GetMethods(byte[] data) + private static EvolutionMethod[] GetMethods(ReadOnlySpan data) { var evos = new EvolutionMethod[data.Length / SIZE]; for (int i = 0; i < data.Length; i += SIZE) { - var method = BitConverter.ToUInt16(data, i + 0); - var arg = BitConverter.ToUInt16(data, i + 2); - var species = BitConverter.ToUInt16(data, i + 4); - - // Argument is used by both Level argument and Item/Move/etc. Clear if appropriate. - var lvl = EvosWithArg.Contains(method) ? 0 : arg; - - evos[i/SIZE] = new EvolutionMethod(method, species, argument: arg, level: lvl); + var entry = data.Slice(i, SIZE); + evos[i/SIZE] = GetMethod(entry); } return evos; } + private static EvolutionMethod GetMethod(ReadOnlySpan entry) + { + var method = ReadUInt16LittleEndian(entry); + var arg = ReadUInt16LittleEndian(entry[2..]); + var species = ReadUInt16LittleEndian(entry[4..]); + + // Argument is used by both Level argument and Item/Move/etc. Clear if appropriate. + var lvl = EvosWithArg.Contains(method) ? 0 : arg; + + return new EvolutionMethod(method, species, argument: arg, level: lvl); + } + public static IReadOnlyList GetArray(IReadOnlyList data) { var evos = new EvolutionMethod[data.Count][]; @@ -36,4 +43,4 @@ public static IReadOnlyList GetArray(IReadOnlyList da return evos; } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet7.cs b/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet7.cs index bf125c68b..520195529 100644 --- a/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet7.cs +++ b/PKHeX.Core/Legality/Evolutions/EvolutionSets/EvolutionSet7.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -10,21 +11,27 @@ public static class EvolutionSet7 { private const int SIZE = 8; - private static EvolutionMethod[] GetMethods(byte[] data) + private static EvolutionMethod[] GetMethods(ReadOnlySpan data) { var evos = new EvolutionMethod[data.Length / SIZE]; for (int i = 0; i < data.Length; i += SIZE) { - var method = BitConverter.ToUInt16(data, i + 0); - var arg = BitConverter.ToUInt16(data, i + 2); - var species = BitConverter.ToUInt16(data, i + 4); - var form = (sbyte) data[i + 6]; - var level = data[i + 7]; - evos[i / SIZE] = new EvolutionMethod(method, species, argument: arg, level: level, form: form); + var entry = data.Slice(i, SIZE); + evos[i / SIZE] = ReadEvolution(entry); } return evos; } + private static EvolutionMethod ReadEvolution(ReadOnlySpan entry) + { + var method = ReadUInt16LittleEndian(entry); + var arg = ReadUInt16LittleEndian(entry[2..]); + var species = ReadUInt16LittleEndian(entry[4..]); + var form = (sbyte)entry[6]; + var level = entry[7]; + return new EvolutionMethod(method, species, argument: arg, level: level, form: form); + } + public static IReadOnlyList GetArray(IReadOnlyList data) { var evos = new EvolutionMethod[data.Count][]; @@ -33,4 +40,4 @@ public static IReadOnlyList GetArray(IReadOnlyList da return evos; } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Legality/Learnset/LearnsetReader.cs b/PKHeX.Core/Legality/Learnset/LearnsetReader.cs index 814555872..24cb47af6 100644 --- a/PKHeX.Core/Legality/Learnset/LearnsetReader.cs +++ b/PKHeX.Core/Legality/Learnset/LearnsetReader.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -41,7 +42,7 @@ public static Learnset[] GetArray(byte[][] entries) /// Reads a Level up move pool definition from a contiguous chunk of GB era ROM data. /// /// Moves and Levels are 8-bit - private static Learnset ReadLearnset8(byte[] data, ref int offset) + private static Learnset ReadLearnset8(ReadOnlySpan data, ref int offset) { int end = offset; // scan for count if (data[end] == 0) @@ -67,7 +68,7 @@ private static Learnset ReadLearnset8(byte[] data, ref int offset) /// Reads a Level up move pool definition from a single move pool definition. /// /// Count of moves, followed by Moves and Levels which are 16-bit - private static Learnset ReadLearnset16(byte[] data) + private static Learnset ReadLearnset16(ReadOnlySpan data) { if (data.Length == 0) return EMPTY; @@ -76,9 +77,9 @@ private static Learnset ReadLearnset16(byte[] data) var Levels = new int[Count]; for (int i = 0; i < Count; i++) { - int ofs = i * 4; - Moves[i] = BitConverter.ToInt16(data, ofs); - Levels[i] = BitConverter.ToInt16(data, ofs + 2); + var move = data.Slice(i * 4, 4); + Moves[i] = ReadInt16LittleEndian(move); + Levels[i] = ReadInt16LittleEndian(move[2..]); } return new Learnset(Moves, Levels); } diff --git a/PKHeX.Core/Legality/RNG/Algorithms/RNGUtil.cs b/PKHeX.Core/Legality/RNG/Algorithms/RNGUtil.cs index 115a015c4..04a97d783 100644 --- a/PKHeX.Core/Legality/RNG/Algorithms/RNGUtil.cs +++ b/PKHeX.Core/Legality/RNG/Algorithms/RNGUtil.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; namespace PKHeX.Core { @@ -9,17 +10,19 @@ public static class RNGUtil /// /// RNG to use /// RNG seed - /// Array of 6 IVs as . + /// Expected IVs + /// True if all match. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static uint[] GetSequentialIVsUInt32(this LCRNG rng, uint seed) + internal static bool GetSequentialIVsUInt32(this LCRNG rng, uint seed, ReadOnlySpan IVs) { - uint[] ivs = new uint[6]; - for (int i = 0; i < 6; i++) + foreach (var iv in IVs) { seed = rng.Next(seed); - ivs[i] = seed >> 27; + var IV = seed >> 27; + if (IV != iv) + return false; } - return ivs; + return true; } /// @@ -27,17 +30,16 @@ internal static uint[] GetSequentialIVsUInt32(this LCRNG rng, uint seed) /// /// RNG to use /// RNG seed + /// Buffer to store generated values /// Array of 6 IVs as . [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static int[] GetSequentialIVsInt32(this LCRNG rng, uint seed) + internal static void GetSequentialIVsInt32(this LCRNG rng, uint seed, Span ivs) { - int[] ivs = new int[6]; for (int i = 0; i < 6; i++) { seed = rng.Next(seed); ivs[i] = (int)(seed >> 27); } - return ivs; } } } diff --git a/PKHeX.Core/Legality/RNG/MethodFinder.cs b/PKHeX.Core/Legality/RNG/MethodFinder.cs index 99cea27c1..b5377322a 100644 --- a/PKHeX.Core/Legality/RNG/MethodFinder.cs +++ b/PKHeX.Core/Legality/RNG/MethodFinder.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; @@ -25,9 +26,10 @@ public static PIDIV Analyze(PKM pk) var top = pid >> 16; var bot = pid & 0xFFFF; - var IVs = new uint[6]; + Span temp = stackalloc uint[6]; for (int i = 0; i < 6; i++) - IVs[i] = (uint)pk.GetIV(i); + temp[i] = (uint)pk.GetIV(i); + ReadOnlySpan IVs = temp; if (GetLCRNGMatch(top, bot, IVs, out PIDIV pidiv)) return pidiv; @@ -54,7 +56,7 @@ public static PIDIV Analyze(PKM pk) return PIDIV.None; // no match } - private static bool GetModifiedPIDMatch(PKM pk, uint pid, uint[] IVs, out PIDIV pidiv) + private static bool GetModifiedPIDMatch(PKM pk, uint pid, ReadOnlySpan IVs, out PIDIV pidiv) { if (pk.IsShiny) { @@ -79,7 +81,7 @@ private static bool GetModified8BitMatch(PKM pk, uint pid, out PIDIV pidiv) : GetG5MGShinyMatch(pk, pid, out pidiv) || (pid <= 0xFF && GetCuteCharmMatch(pk, pid, out pidiv)); } - private static bool GetLCRNGMatch(uint top, uint bot, uint[] IVs, out PIDIV pidiv) + private static bool GetLCRNGMatch(uint top, uint bot, ReadOnlySpan IVs, out PIDIV pidiv) { var reg = GetSeedsFromPID(RNG.LCRNG, top, bot); var iv1 = GetIVChunk(IVs, 0); @@ -147,7 +149,7 @@ private static bool GetLCRNGMatch(uint top, uint bot, uint[] IVs, out PIDIV pidi return GetNonMatch(out pidiv); } - private static bool GetLCRNGUnownMatch(uint top, uint bot, uint[] IVs, out PIDIV pidiv) + private static bool GetLCRNGUnownMatch(uint top, uint bot, ReadOnlySpan IVs, out PIDIV pidiv) { // this is an exact copy of LCRNG 1,2,4 matching, except the PID has its halves switched (BACD, BADE, BACE) var reg = GetSeedsFromPID(RNG.LCRNG, bot, top); // reversed! @@ -216,7 +218,7 @@ private static bool GetLCRNGUnownMatch(uint top, uint bot, uint[] IVs, out PIDIV return GetNonMatch(out pidiv); } - private static bool GetLCRNGRoamerMatch(uint top, uint bot, uint[] IVs, out PIDIV pidiv) + private static bool GetLCRNGRoamerMatch(uint top, uint bot, ReadOnlySpan IVs, out PIDIV pidiv) { if (IVs[2] != 0 || IVs[3] != 0 || IVs[4] != 0 || IVs[5] != 0 || IVs[1] > 7) return GetNonMatch(out pidiv); @@ -235,7 +237,7 @@ private static bool GetLCRNGRoamerMatch(uint top, uint bot, uint[] IVs, out PIDI return GetNonMatch(out pidiv); } - private static bool GetXDRNGMatch(PKM pk, uint top, uint bot, uint[] IVs, out PIDIV pidiv) + private static bool GetXDRNGMatch(PKM pk, uint top, uint bot, ReadOnlySpan IVs, out PIDIV pidiv) { var xdc = GetSeedsFromPIDEuclid(RNG.XDRNG, top, bot); foreach (var seed in xdc) @@ -284,7 +286,7 @@ private static bool GetXDRNGMatch(PKM pk, uint top, uint bot, uint[] IVs, out PI return GetNonMatch(out pidiv); } - private static bool GetChannelMatch(uint top, uint bot, uint[] IVs, out PIDIV pidiv, PKM pk) + private static bool GetChannelMatch(uint top, uint bot, ReadOnlySpan IVs, out PIDIV pidiv, PKM pk) { var ver = pk.Version; if (ver is not ((int)GameVersion.R or (int)GameVersion.S)) @@ -307,7 +309,7 @@ private static bool GetChannelMatch(uint top, uint bot, uint[] IVs, out PIDIV pi if (E >> 31 != pk.OT_Gender) continue; - if (!RNG.XDRNG.GetSequentialIVsUInt32(E).SequenceEqual(IVs)) + if (!RNG.XDRNG.GetSequentialIVsUInt32(E, IVs)) continue; if (seed >> 16 != pk.SID) @@ -319,7 +321,7 @@ private static bool GetChannelMatch(uint top, uint bot, uint[] IVs, out PIDIV pi return GetNonMatch(out pidiv); } - private static bool GetMG4Match(uint pid, uint[] IVs, out PIDIV pidiv) + private static bool GetMG4Match(uint pid, ReadOnlySpan IVs, out PIDIV pidiv) { uint mg4Rev = RNG.ARNG.Prev(pid); var mg4 = GetSeedsFromPID(RNG.LCRNG, mg4Rev >> 16, mg4Rev & 0xFFFF); @@ -387,7 +389,7 @@ private static bool GetCuteCharmMatch(PKM pk, uint pid, out PIDIV pidiv) return GetNonMatch(out pidiv); } - private static bool GetChainShinyMatch(PKM pk, uint pid, uint[] IVs, out PIDIV pidiv) + private static bool GetChainShinyMatch(PKM pk, uint pid, ReadOnlySpan IVs, out PIDIV pidiv) { // 13 shiny bits // PIDH & 7 @@ -432,7 +434,7 @@ private static bool GetChainShinyMatch(PKM pk, uint pid, uint[] IVs, out PIDIV p public static IEnumerable GetCuteCharmSeeds(PKM pk) { - var IVs = new uint[6]; + Span IVs = stackalloc uint[6]; for (int i = 0; i < 6; i++) IVs[i] = (uint)pk.GetIV(i); var bot = GetIVChunk(IVs, 0); @@ -441,7 +443,7 @@ public static IEnumerable GetCuteCharmSeeds(PKM pk) return GetSeedsFromIVs(RNG.LCRNG, top, bot); } - private static bool GetBACDMatch(PKM pk, uint pid, uint[] IVs, out PIDIV pidiv) + private static bool GetBACDMatch(PKM pk, uint pid, ReadOnlySpan IVs, out PIDIV pidiv) { var bot = GetIVChunk(IVs, 0); var top = GetIVChunk(IVs, 3); @@ -536,7 +538,7 @@ private static bool IsAzurillEdgeCaseM(PKM pk, uint nature, uint oldpid) return pid == oldpid; } - private static bool GetColoStarterMatch(PKM pk, uint top, uint bot, uint[] IVs, out PIDIV pidiv) + private static bool GetColoStarterMatch(PKM pk, uint top, uint bot, ReadOnlySpan IVs, out PIDIV pidiv) { if (pk.Version != (int)GameVersion.CXD || pk.Species is not ((int)Species.Espeon or (int)Species.Umbreon)) return GetNonMatch(out pidiv); @@ -700,7 +702,7 @@ public static IEnumerable GetSeedsFromIVsEuclid(RNG method, uint rand1, ui /// IVs that should be the result /// IVs match random number IVs [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IVsMatch(uint r1, uint r2, IReadOnlyList IVs) + private static bool IVsMatch(uint r1, uint r2, ReadOnlySpan IVs) { if (IVs[0] != (r1 & 31)) return false; @@ -749,7 +751,7 @@ internal static int[] GetIVsInt32(uint r1, uint r2) }; } - private static uint GetIVChunk(uint[] IVs, int start) + private static uint GetIVChunk(ReadOnlySpan IVs, int start) { uint val = 0; for (int i = 0; i < 3; i++) diff --git a/PKHeX.Core/Legality/RNG/Overworld8RNG.cs b/PKHeX.Core/Legality/RNG/Overworld8RNG.cs index ba2fd8e65..b0d91716f 100644 --- a/PKHeX.Core/Legality/RNG/Overworld8RNG.cs +++ b/PKHeX.Core/Legality/RNG/Overworld8RNG.cs @@ -1,4 +1,6 @@ -namespace PKHeX.Core +using System; + +namespace PKHeX.Core { /// /// Contains logic for the Generation 8 (SW/SH) overworld spawns that walk around the overworld. @@ -55,7 +57,7 @@ private static bool TryApplyFromSeed(PKM pk, EncounterCriteria criteria, Shiny s pk.PID = pid; // IVs - var ivs = new[] {UNSET, UNSET, UNSET, UNSET, UNSET, UNSET}; + Span ivs = stackalloc[] {UNSET, UNSET, UNSET, UNSET, UNSET, UNSET}; const int MAX = 31; for (int i = 0; i < flawless; i++) { @@ -153,7 +155,7 @@ private static int GetIsMatchEnd(PKM pk, Xoroshiro128Plus xoro, int start = 0, i continue; var copy = xoro; - int[] ivs = { UNSET, UNSET, UNSET, UNSET, UNSET, UNSET }; + Span ivs = stackalloc [] { UNSET, UNSET, UNSET, UNSET, UNSET, UNSET }; const int MAX = 31; for (int i = 0; i < iv_count; i++) { @@ -162,8 +164,18 @@ private static int GetIsMatchEnd(PKM pk, Xoroshiro128Plus xoro, int start = 0, i ivs[index] = MAX; } - if (!IsValidSequence(pk, ivs, ref copy)) - continue; + for (var i = 0; i < ivs.Length; i++) + { + if (ivs[i] == UNSET) + ivs[i] = (int)copy.NextInt(31 + 1); + } + + if (ivs[0] != pk.IV_HP) continue; + if (ivs[1] != pk.IV_ATK) continue; + if (ivs[2] != pk.IV_DEF) continue; + if (ivs[3] != pk.IV_SPA) continue; + if (ivs[4] != pk.IV_SPD) continue; + if (ivs[5] != pk.IV_SPE) continue; if (pk is not IScaledSize s) continue; @@ -179,28 +191,6 @@ private static int GetIsMatchEnd(PKM pk, Xoroshiro128Plus xoro, int start = 0, i return NoMatchIVs; } - private static bool IsValidSequence(PKM pk, int[] template, ref Xoroshiro128Plus rng) - { - for (int i = 0; i < 6; i++) - { - var temp = template[i]; - var expect = temp == UNSET ? (int)rng.NextInt(32) : temp; - var actual = i switch - { - 0 => pk.IV_HP, - 1 => pk.IV_ATK, - 2 => pk.IV_DEF, - 3 => pk.IV_SPA, - 4 => pk.IV_SPD, - _ => pk.IV_SPE, - }; - if (expect != actual) - return false; - } - - return true; - } - private static uint GetShinyPID(int tid, int sid, uint pid, int type) { return (uint)(((tid ^ sid ^ (pid & 0xFFFF) ^ type) << 16) | (pid & 0xFFFF)); diff --git a/PKHeX.Core/Legality/RNG/PIDGenerator.cs b/PKHeX.Core/Legality/RNG/PIDGenerator.cs index 21280f0f5..82d52d0a3 100644 --- a/PKHeX.Core/Legality/RNG/PIDGenerator.cs +++ b/PKHeX.Core/Legality/RNG/PIDGenerator.cs @@ -127,7 +127,9 @@ private static void SetValuesFromSeedChannel(PKM pk, uint seed) pk.HeldItem = (int)(C >> 31) + 169; // 0-Ganlon, 1-Salac pk.Version = (int)(D >> 31) + 1; // 0-Sapphire, 1-Ruby pk.OT_Gender = (int)(E >> 31); - pk.IVs = rng.GetSequentialIVsInt32(E); + Span ivs = stackalloc int[6]; + rng.GetSequentialIVsInt32(E, ivs); + pk.SetIVs(ivs); } public static void SetValuesFromSeed(PKM pk, PIDType type, uint seed) diff --git a/PKHeX.Core/Legality/RNG/Roaming8bRNG.cs b/PKHeX.Core/Legality/RNG/Roaming8bRNG.cs index 93bd223b1..1e1449307 100644 --- a/PKHeX.Core/Legality/RNG/Roaming8bRNG.cs +++ b/PKHeX.Core/Legality/RNG/Roaming8bRNG.cs @@ -1,4 +1,6 @@ -namespace PKHeX.Core +using System; + +namespace PKHeX.Core { /// /// Contains logic for the Generation 8b (BD/SP) roaming spawns. @@ -61,12 +63,12 @@ private static bool TryApplyFromSeed(PKM pk, EncounterCriteria criteria, Shiny s pk.PID = pid; // Check IVs: Create flawless IVs at random indexes, then the random IVs for not flawless. - int[] ivs = { UNSET, UNSET, UNSET, UNSET, UNSET, UNSET }; + Span ivs = stackalloc [] { UNSET, UNSET, UNSET, UNSET, UNSET, UNSET }; const int MAX = 31; var determined = 0; while (determined < flawless) { - var idx = xoro.NextUInt(6); + var idx = (int)xoro.NextUInt(6); if (ivs[idx] != UNSET) continue; ivs[idx] = 31; @@ -115,12 +117,12 @@ public static bool ValidateRoamingEncounter(PKM pk, Shiny shiny = Shiny.Random, return false; // Check IVs: Create flawless IVs at random indexes, then the random IVs for not flawless. - int[] ivs = { UNSET, UNSET, UNSET, UNSET, UNSET, UNSET }; + Span ivs = stackalloc [] { UNSET, UNSET, UNSET, UNSET, UNSET, UNSET }; var determined = 0; while (determined < flawless) { - var idx = xoro.NextUInt(6); + var idx = (int)xoro.NextUInt(6); if (ivs[idx] != UNSET) continue; ivs[idx] = 31; @@ -133,12 +135,12 @@ public static bool ValidateRoamingEncounter(PKM pk, Shiny shiny = Shiny.Random, ivs[i] = (int)xoro.NextUInt(31 + 1); } - if (ivs[0] != pk.GetIV(0)) return false; - if (ivs[1] != pk.GetIV(1)) return false; - if (ivs[2] != pk.GetIV(2)) return false; - if (ivs[3] != pk.GetIV(4)) return false; - if (ivs[4] != pk.GetIV(5)) return false; - if (ivs[5] != pk.GetIV(3)) return false; + if (ivs[0] != pk.IV_HP ) return false; + if (ivs[1] != pk.IV_ATK) return false; + if (ivs[2] != pk.IV_DEF) return false; + if (ivs[3] != pk.IV_SPA) return false; + if (ivs[4] != pk.IV_SPD) return false; + if (ivs[5] != pk.IV_SPE) return false; // Don't check Hidden ability, as roaming encounters are 1/2 only. if (pk.AbilityNumber != (1 << (int)xoro.NextUInt(2))) diff --git a/PKHeX.Core/Legality/Restrictions/Memories/MemoryContext8.cs b/PKHeX.Core/Legality/Restrictions/Memories/MemoryContext8.cs index 5b03d8038..5d1104755 100644 --- a/PKHeX.Core/Legality/Restrictions/Memories/MemoryContext8.cs +++ b/PKHeX.Core/Legality/Restrictions/Memories/MemoryContext8.cs @@ -84,8 +84,8 @@ private static bool IsInvalidGenLoc8(int memory, int loc, int egg, int variable, return true; if (loc is Encounters8Nest.SharedNest) return !PossibleGeneralLocations8.Contains(arg) || arg is 79; // dangerous place - all locations are Y-Comm locked - if (SingleGenLocAreas.TryGetValue((byte)loc, out var val)) - return arg != val; + if (SingleGenLocAreas.TryGetValue((byte)loc, out var value)) + return arg != value; if (MultiGenLocAreas.TryGetValue((byte)loc, out var arr)) return !arr.Contains(arg); return false; diff --git a/PKHeX.Core/Legality/Structures/EggMoves.cs b/PKHeX.Core/Legality/Structures/EggMoves.cs index 8a7ede83a..02f8286ec 100644 --- a/PKHeX.Core/Legality/Structures/EggMoves.cs +++ b/PKHeX.Core/Legality/Structures/EggMoves.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -14,16 +15,16 @@ public sealed class EggMoves2 : EggMoves { private EggMoves2(int[] moves) : base(moves) { } - public static EggMoves2[] GetArray(byte[] data, int count) + public static EggMoves2[] GetArray(ReadOnlySpan data, int count) { var entries = new EggMoves2[count + 1]; var empty = entries[0] = new EggMoves2(Array.Empty()); - int baseOffset = BitConverter.ToInt16(data, 0) - (count * 2); + int baseOffset = ReadInt16LittleEndian(data) - (count * 2); for (int i = 1; i < entries.Length; i++) { - int start = BitConverter.ToInt16(data, (i - 1) * 2) - baseOffset; - int end = Array.IndexOf(data, (byte)0xFF, start); + int start = ReadInt16LittleEndian(data[((i - 1) * 2)..]) - baseOffset; + int end = data[start..].IndexOf((byte)0xFF) + start; if (start == end) { entries[i] = empty; @@ -47,15 +48,16 @@ public sealed class EggMoves6 : EggMoves private EggMoves6(int[] moves) : base(moves) { } - private static EggMoves6 Get(byte[] data) + private static EggMoves6 Get(ReadOnlySpan data) { if (data.Length == 0) return None; - int count = BitConverter.ToInt16(data, 0); + int count = ReadInt16LittleEndian(data); var moves = new int[count]; + var span = data[2..]; for (int i = 0; i < moves.Length; i++) - moves[i] = BitConverter.ToInt16(data, 2 + (i * 2)); + moves[i] = ReadInt16LittleEndian(span[(i * 2)..]); return new EggMoves6(moves); } @@ -75,16 +77,18 @@ public sealed class EggMoves7 : EggMoves private EggMoves7(int[] moves, int formIndex = 0) : base(moves) => FormTableIndex = formIndex; - private static EggMoves7 Get(byte[] data) + private static EggMoves7 Get(ReadOnlySpan data) { if (data.Length == 0) return None; - int formIndex = BitConverter.ToInt16(data, 0); - int count = BitConverter.ToInt16(data, 2); + int formIndex = ReadInt16LittleEndian(data); + int count = ReadInt16LittleEndian(data[2..]); var moves = new int[count]; + + var moveSpan = data[4..]; for (int i = 0; i < moves.Length; i++) - moves[i] = BitConverter.ToInt16(data, 4 + (i * 2)); + moves[i] = ReadInt16LittleEndian(moveSpan[(i * 2)..]); return new EggMoves7(moves, formIndex); } diff --git a/PKHeX.Core/Legality/Verifiers/MedalVerifier.cs b/PKHeX.Core/Legality/Verifiers/MedalVerifier.cs index 79dff3858..b7fa6d78d 100644 --- a/PKHeX.Core/Legality/Verifiers/MedalVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/MedalVerifier.cs @@ -21,7 +21,7 @@ private void VerifyMedalsRegular(LegalityAnalysis data) var pkm = data.pkm; var train = (ISuperTrain)pkm; var Info = data.Info; - uint value = BitConverter.ToUInt32(data.pkm.Data, 0x2C); + uint value = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(data.pkm.Data.AsSpan(0x2C)); if ((value & 3) != 0) // 2 unused flags data.AddLine(GetInvalid(LSuperUnused)); int TrainCount = train.SuperTrainingMedalCount(); diff --git a/PKHeX.Core/Legality/Verifiers/MiscVerifier.cs b/PKHeX.Core/Legality/Verifiers/MiscVerifier.cs index 1001cadee..80ae07840 100644 --- a/PKHeX.Core/Legality/Verifiers/MiscVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/MiscVerifier.cs @@ -3,6 +3,7 @@ using System.Linq; using static PKHeX.Core.LegalityCheckStrings; using static PKHeX.Core.CheckIdentifier; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -435,8 +436,12 @@ private static void VerifyBelugaStats(LegalityAnalysis data, PB7 pb7) private static bool IsCloseEnough(float a, float b) { - var ia = BitConverter.ToInt32(BitConverter.GetBytes(a), 0); - var ib = BitConverter.ToInt32(BitConverter.GetBytes(b), 0); + // since we don't have access to SingleToInt32Bits on net46, just do a temp write-read. + Span ta = stackalloc byte[4]; + WriteSingleLittleEndian(ta, a); + var ia = ReadInt32LittleEndian(ta); + WriteSingleLittleEndian(ta, b); + var ib = ReadInt32LittleEndian(ta); return Math.Abs(ia - ib) <= 7; } diff --git a/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs b/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs index 85698b8e7..fe14c1f05 100644 --- a/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs @@ -164,7 +164,7 @@ private bool VerifyUnNicknamedEncounter(LegalityAnalysis data, PKM pkm, string n data.AddLine(Get(msg, ParseSettings.NicknamedAnotherSpecies)); return true; } - if (pkm.Format <= 7 && StringConverter.HasEastAsianScriptCharacters(nickname) && pkm is not PB7) // East Asian Scripts + if (pkm.Format <= 7 && StringConverter.HasEastAsianScriptCharacters(nickname.AsSpan()) && pkm is not PB7) // East Asian Scripts { data.AddLine(GetInvalid(LNickInvalidChar)); return true; diff --git a/PKHeX.Core/MysteryGifts/MysteryGift.cs b/PKHeX.Core/MysteryGifts/MysteryGift.cs index db037297b..bb226c762 100644 --- a/PKHeX.Core/MysteryGifts/MysteryGift.cs +++ b/PKHeX.Core/MysteryGifts/MysteryGift.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -58,7 +59,7 @@ public abstract class MysteryGift : IEncounterable, IMoveset, IRelearn, ILocatio WB8.Size => new WB8(data), // WC6/WC7: Check year - WC6.Size => BitConverter.ToUInt32(data, 0x4C) / 10000 < 2000 ? new WC7(data) : new WC6(data), + WC6.Size => ReadUInt32LittleEndian(data.AsSpan(0x4C)) / 10000 < 2000 ? new WC7(data) : new WC6(data), // WC6Full/WC7Full: 0x205 has 3 * 0x46 for gen6, now only 2. WC6Full.Size => data[0x205] == 0 ? new WC7Full(data).Gift : new WC6Full(data).Gift, _ => null, diff --git a/PKHeX.Core/MysteryGifts/PCD.cs b/PKHeX.Core/MysteryGifts/PCD.cs index 9c9265067..9ec37d8ab 100644 --- a/PKHeX.Core/MysteryGifts/PCD.cs +++ b/PKHeX.Core/MysteryGifts/PCD.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -50,7 +51,7 @@ public PGT Gift private PGT? _gift; public Span GetMetadata() => Data.AsSpan(PGT.Size); - public void SetMetadata(byte[] data) => data.CopyTo(Data, Data.Length - PGT.Size); + public void SetMetadata(ReadOnlySpan data) => data.CopyTo(Data.AsSpan(PGT.Size)); public override bool GiftUsed { get => Gift.GiftUsed; set => Gift.GiftUsed = value; } public override bool IsPokémon { get => Gift.IsPokémon; set => Gift.IsPokémon = value; } @@ -59,27 +60,21 @@ public PGT Gift public override int CardID { - get => BitConverter.ToUInt16(Data, 0x150); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x150); + get => ReadUInt16LittleEndian(Data.AsSpan(0x150)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x150), (ushort)value); } private const int TitleLength = 0x48; + private Span CardTitleSpan => Data.AsSpan(0x104, TitleLength); + public override string CardTitle { - get => StringConverter4.GetString4(Data, 0x104, TitleLength); - set - { - byte[] data = StringConverter4.SetString4(value, (TitleLength / 2) - 1, TitleLength / 2, 0xFFFF); - int len = data.Length; - Array.Resize(ref data, 0x48); - for (int i = 0; i < len; i++) - data[i] = 0xFF; - data.CopyTo(Data, 0x104); - } + get => StringConverter4.GetString(CardTitleSpan); + set => StringConverter4.SetString(CardTitleSpan, value.AsSpan(), TitleLength / 2, StringConverterOption.ClearFF); } - public ushort CardCompatibility => BitConverter.ToUInt16(Data, 0x14C); // rest of bytes we don't really care about + public ushort CardCompatibility => ReadUInt16LittleEndian(Data.AsSpan(0x14C)); // rest of bytes we don't really care about public override int Species { get => Gift.IsManaphyEgg ? 490 : Gift.Species; set => Gift.Species = value; } public override IReadOnlyList Moves { get => Gift.Moves; set => Gift.Moves = value; } diff --git a/PKHeX.Core/MysteryGifts/PGF.cs b/PKHeX.Core/MysteryGifts/PGF.cs index 243b92b48..3fb113996 100644 --- a/PKHeX.Core/MysteryGifts/PGF.cs +++ b/PKHeX.Core/MysteryGifts/PGF.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -14,11 +15,11 @@ public sealed class PGF : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4, I public PGF() : this(new byte[Size]) { } public PGF(byte[] data) : base(data) { } - public override int TID { get => BitConverter.ToUInt16(Data, 0x00); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x00); } - public override int SID { get => BitConverter.ToUInt16(Data, 0x02); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x02); } + public override int TID { get => ReadUInt16LittleEndian(Data.AsSpan(0x00)); set => WriteUInt16LittleEndian(Data.AsSpan(0x00), (ushort)value); } + public override int SID { get => ReadUInt16LittleEndian(Data.AsSpan(0x02)); set => WriteUInt16LittleEndian(Data.AsSpan(0x02), (ushort)value); } public int OriginGame { get => Data[0x04]; set => Data[0x04] = (byte)value; } // Unused 0x05 0x06, 0x07 - public uint PID { get => BitConverter.ToUInt32(Data, 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, 0x08); } + public uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0x08)); set => WriteUInt32LittleEndian(Data.AsSpan(0x08), value); } private byte RIB0 { get => Data[0x0C]; set => Data[0x0C] = value; } private byte RIB1 { get => Data[0x0D]; set => Data[0x0D] = value; } @@ -40,27 +41,27 @@ public sealed class PGF : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4, I public bool RIB1_7 { get => (RIB1 & (1 << 7)) == 1 << 7; set => RIB1 = (byte)((RIB1 & ~(1 << 7)) | (value ? 1 << 7 : 0)); } public override int Ball { get => Data[0x0E]; set => Data[0x0E] = (byte)value; } - public override int HeldItem { get => BitConverter.ToUInt16(Data, 0x10); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x10); } - public int Move1 { get => BitConverter.ToUInt16(Data, 0x12); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x12); } - public int Move2 { get => BitConverter.ToUInt16(Data, 0x14); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14); } - public int Move3 { get => BitConverter.ToUInt16(Data, 0x16); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x16); } - public int Move4 { get => BitConverter.ToUInt16(Data, 0x18); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x18); } - public override int Species { get => BitConverter.ToUInt16(Data, 0x1A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1A); } + public override int HeldItem { get => ReadUInt16LittleEndian(Data.AsSpan(0x10)); set => WriteUInt16LittleEndian(Data.AsSpan(0x10), (ushort)value); } + public int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x12)); set => WriteUInt16LittleEndian(Data.AsSpan(0x12), (ushort)value); } + public int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x14)); set => WriteUInt16LittleEndian(Data.AsSpan(0x14), (ushort)value); } + public int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x16)); set => WriteUInt16LittleEndian(Data.AsSpan(0x16), (ushort)value); } + public int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x18)); set => WriteUInt16LittleEndian(Data.AsSpan(0x18), (ushort)value); } + public override int Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x1A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1A), (ushort)value); } public override int Form { get => Data[0x1C]; set => Data[0x1C] = (byte)value; } public int Language { get => Data[0x1D]; set => Data[0x1D] = (byte)value; } public string Nickname { - get => StringConverter.GetString5(Data, 0x1E, 11 * 2); - set => StringConverter.SetString5(value, 11, 11, '\uFFFF').CopyTo(Data, 0x1E); + get => StringConverter5.GetString(Data.AsSpan( 0x1E, 11 * 2)); + set => StringConverter5.SetString(Data.AsSpan(0x1E, 11 * 2), value.AsSpan(), 11, StringConverterOption.ClearFF); } public int Nature { get => (sbyte)Data[0x34]; set => Data[0x34] = (byte)value; } public override int Gender { get => Data[0x35]; set => Data[0x35] = (byte)value; } public override int AbilityType { get => Data[0x36]; set => Data[0x36] = (byte)value; } public int PIDType { get => Data[0x37]; set => Data[0x37] = (byte)value; } - public override int EggLocation { get => BitConverter.ToUInt16(Data, 0x38); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x38); } - public ushort MetLocation { get => BitConverter.ToUInt16(Data, 0x3A); set => BitConverter.GetBytes(value).CopyTo(Data, 0x3A); } + public override int EggLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0x38)); set => WriteUInt16LittleEndian(Data.AsSpan(0x38), (ushort)value); } + public ushort MetLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0x3A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x3A), value); } public int MetLevel { get => Data[0x3C]; set => Data[0x3C] = (byte)value; } public byte CNT_Cool { get => Data[0x3D]; set => Data[0x3D] = value; } public byte CNT_Beauty { get => Data[0x3E]; set => Data[0x3E] = value; } @@ -77,8 +78,8 @@ public string Nickname // Unused 0x49 public override string OT_Name { - get => StringConverter.GetString5(Data, 0x4A, 8 * 2); - set => StringConverter.SetString5(value, 8, 8, '\uFFFF').CopyTo(Data, 0x4A); + get => StringConverter5.GetString(Data.AsSpan(0x4A, 8 * 2)); + set => StringConverter5.SetString(Data.AsSpan(0x4A, 8 * 2), value.AsSpan(), 8, StringConverterOption.ClearFF); } public int OTGender { get => Data[0x5A]; set => Data[0x5A] = (byte)value; } @@ -87,14 +88,14 @@ public override string OT_Name // Unused 0x5D 0x5E 0x5F public override string CardTitle { - get => StringConverter.GetString5(Data, 0x60, 37 * 2); - set => StringConverter.SetString5(value, 37, 37, 0).CopyTo(Data, 0x60); + get => StringConverter5.GetString(Data.AsSpan(0x60, 37 * 2)); + set => StringConverter5.SetString(Data.AsSpan(0x60, 37 * 2), value.AsSpan(), 36, StringConverterOption.ClearZero); } // Card Attributes - public override int ItemID { get => BitConverter.ToUInt16(Data, 0x00); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x00); } + public override int ItemID { get => ReadUInt16LittleEndian(Data.AsSpan(0x00)); set => WriteUInt16LittleEndian(Data.AsSpan(0x00), (ushort)value); } - private ushort Year { get => BitConverter.ToUInt16(Data, 0xAE); set => BitConverter.GetBytes(value).CopyTo(Data, 0xAE); } + private ushort Year { get => ReadUInt16LittleEndian(Data.AsSpan(0xAE)); set => WriteUInt16LittleEndian(Data.AsSpan(0xAE), value); } private byte Month { get => Data[0xAD]; set => Data[0xAD] = value; } private byte Day { get => Data[0xAC]; set => Data[0xAC] = value; } @@ -133,8 +134,8 @@ public override string CardTitle public override int CardID { - get => BitConverter.ToUInt16(Data, 0xB0); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xB0); + get => ReadUInt16LittleEndian(Data.AsSpan(0xB0)); + set => WriteUInt16LittleEndian(Data.AsSpan(0xB0), (ushort)value); } public int CardLocation { get => Data[0xB2]; set => Data[0xB2] = (byte)value; } @@ -330,11 +331,11 @@ private void SetPID(PKM pk, int av) private void SetIVs(PKM pk) { - int[] finalIVs = new int[6]; + Span finalIVs = stackalloc int[6]; var rnd = Util.Rand; for (int i = 0; i < IVs.Length; i++) finalIVs[i] = IVs[i] == 0xFF ? rnd.Next(32) : IVs[i]; - pk.IVs = finalIVs; + pk.SetIVs(finalIVs); } public override bool IsMatchExact(PKM pkm, DexLevel evo) diff --git a/PKHeX.Core/MysteryGifts/PGT.cs b/PKHeX.Core/MysteryGifts/PGT.cs index 24aabc13b..888afbe00 100644 --- a/PKHeX.Core/MysteryGifts/PGT.cs +++ b/PKHeX.Core/MysteryGifts/PGT.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -54,7 +55,7 @@ private enum GiftType // Unused 0x01 public byte Slot { get => Data[2]; set => Data[2] = value; } public byte Detail { get => Data[3]; set => Data[3] = value; } - public override int ItemID { get => BitConverter.ToUInt16(Data, 0x4); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x4); } + public override int ItemID { get => ReadUInt16LittleEndian(Data.AsSpan(0x4)); set => WriteUInt16LittleEndian(Data.AsSpan(0x4), (ushort)value); } public PK4 PK { @@ -85,7 +86,7 @@ public override byte[] Write() /// True if data was encrypted, false if the data was not modified. public bool VerifyPKEncryption() { - if (!IsPokémon || BitConverter.ToUInt32(Data, 0x64 + 8) != 0) + if (!IsPokémon || ReadUInt32LittleEndian(Data.AsSpan(0x64 + 8)) != 0) return false; EncryptPK(); return true; @@ -93,10 +94,9 @@ public bool VerifyPKEncryption() private void EncryptPK() { - byte[] ekdata = new byte[PokeCrypto.SIZE_4PARTY]; - Array.Copy(Data, 8, ekdata, 0, ekdata.Length); - ekdata = PokeCrypto.EncryptArray45(ekdata); - ekdata.CopyTo(Data, 8); + var span = Data.AsSpan(8, PokeCrypto.SIZE_4PARTY); + var ekdata = PokeCrypto.EncryptArray45(span); + ekdata.CopyTo(span); } private GiftType PGTGiftType { get => (GiftType)Data[0]; set => Data[0] = (byte)value; } diff --git a/PKHeX.Core/MysteryGifts/PL6.cs b/PKHeX.Core/MysteryGifts/PL6.cs index 6cda0ed7d..e667cdedc 100644 --- a/PKHeX.Core/MysteryGifts/PL6.cs +++ b/PKHeX.Core/MysteryGifts/PL6.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -34,15 +35,15 @@ public byte Flags /// /// Name of data source /// - public string Origin { get => StringConverter.GetString6(Data, 0x01, 110); set => StringConverter.SetString6(value, 54, 55).CopyTo(Data, 0x01); } + public string Origin { get => StringConverter6.GetString(Data.AsSpan(0x01, 110)); set => StringConverter6.SetString(Data.AsSpan(0x01, 110), value.AsSpan(), 54, StringConverterOption.ClearZero); } // Pokemon transfer flags? - public uint Flags_1 { get => BitConverter.ToUInt32(Data, 0x099); set => BitConverter.GetBytes(value).CopyTo(Data, 0x099); } - public uint Flags_2 { get => BitConverter.ToUInt32(Data, 0x141); set => BitConverter.GetBytes(value).CopyTo(Data, 0x141); } - public uint Flags_3 { get => BitConverter.ToUInt32(Data, 0x1E9); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1E9); } - public uint Flags_4 { get => BitConverter.ToUInt32(Data, 0x291); set => BitConverter.GetBytes(value).CopyTo(Data, 0x291); } - public uint Flags_5 { get => BitConverter.ToUInt32(Data, 0x339); set => BitConverter.GetBytes(value).CopyTo(Data, 0x339); } - public uint Flags_6 { get => BitConverter.ToUInt32(Data, 0x3E1); set => BitConverter.GetBytes(value).CopyTo(Data, 0x3E1); } + public uint Flags_1 { get => ReadUInt32LittleEndian(Data.AsSpan(0x099)); set => WriteUInt32LittleEndian(Data.AsSpan(0x099), value); } + public uint Flags_2 { get => ReadUInt32LittleEndian(Data.AsSpan(0x141)); set => WriteUInt32LittleEndian(Data.AsSpan(0x141), value); } + public uint Flags_3 { get => ReadUInt32LittleEndian(Data.AsSpan(0x1E9)); set => WriteUInt32LittleEndian(Data.AsSpan(0x1E9), value); } + public uint Flags_4 { get => ReadUInt32LittleEndian(Data.AsSpan(0x291)); set => WriteUInt32LittleEndian(Data.AsSpan(0x291), value); } + public uint Flags_5 { get => ReadUInt32LittleEndian(Data.AsSpan(0x339)); set => WriteUInt32LittleEndian(Data.AsSpan(0x339), value); } + public uint Flags_6 { get => ReadUInt32LittleEndian(Data.AsSpan(0x3E1)); set => WriteUInt32LittleEndian(Data.AsSpan(0x3E1), value); } // Pokémon public PL6_PKM Poke_1 { get => new(Data.Slice(0x09D, PL6_PKM.Size)); set => value.Data.CopyTo(Data, 0x09D); } @@ -53,21 +54,21 @@ public byte Flags public PL6_PKM Poke_6 { get => new(Data.Slice(0x3E5, PL6_PKM.Size)); set => value.Data.CopyTo(Data, 0x3E5); } // Item Properties - public int Item_1 { get => BitConverter.ToUInt16(Data, 0x489); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x489); } - public int Quantity_1 { get => BitConverter.ToUInt16(Data, 0x48B); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x48B); } - public int Item_2 { get => BitConverter.ToUInt16(Data, 0x48D); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x48D); } - public int Quantity_2 { get => BitConverter.ToUInt16(Data, 0x48F); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x48F); } - public int Item_3 { get => BitConverter.ToUInt16(Data, 0x491); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x491); } - public int Quantity_3 { get => BitConverter.ToUInt16(Data, 0x493); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x493); } - public int Item_4 { get => BitConverter.ToUInt16(Data, 0x495); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x495); } - public int Quantity_4 { get => BitConverter.ToUInt16(Data, 0x497); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x497); } - public int Item_5 { get => BitConverter.ToUInt16(Data, 0x499); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x499); } - public int Quantity_5 { get => BitConverter.ToUInt16(Data, 0x49B); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x49B); } - public int Item_6 { get => BitConverter.ToUInt16(Data, 0x49D); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x49D); } - public int Quantity_6 { get => BitConverter.ToUInt16(Data, 0x49F); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x49F); } + public int Item_1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x489)); set => WriteUInt16LittleEndian(Data.AsSpan(0x489), (ushort)value); } + public int Quantity_1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x48B)); set => WriteUInt16LittleEndian(Data.AsSpan(0x48B), (ushort)value); } + public int Item_2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x48D)); set => WriteUInt16LittleEndian(Data.AsSpan(0x48D), (ushort)value); } + public int Quantity_2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x48F)); set => WriteUInt16LittleEndian(Data.AsSpan(0x48F), (ushort)value); } + public int Item_3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x491)); set => WriteUInt16LittleEndian(Data.AsSpan(0x491), (ushort)value); } + public int Quantity_3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x493)); set => WriteUInt16LittleEndian(Data.AsSpan(0x493), (ushort)value); } + public int Item_4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x495)); set => WriteUInt16LittleEndian(Data.AsSpan(0x495), (ushort)value); } + public int Quantity_4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x497)); set => WriteUInt16LittleEndian(Data.AsSpan(0x497), (ushort)value); } + public int Item_5 { get => ReadUInt16LittleEndian(Data.AsSpan(0x499)); set => WriteUInt16LittleEndian(Data.AsSpan(0x499), (ushort)value); } + public int Quantity_5 { get => ReadUInt16LittleEndian(Data.AsSpan(0x49B)); set => WriteUInt16LittleEndian(Data.AsSpan(0x49B), (ushort)value); } + public int Item_6 { get => ReadUInt16LittleEndian(Data.AsSpan(0x49D)); set => WriteUInt16LittleEndian(Data.AsSpan(0x49D), (ushort)value); } + public int Quantity_6 { get => ReadUInt16LittleEndian(Data.AsSpan(0x49F)); set => WriteUInt16LittleEndian(Data.AsSpan(0x49F), (ushort)value); } - public int BattlePoints { get => BitConverter.ToUInt16(Data, 0x4A1); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x4A1); } - public int Pokemiles { get => BitConverter.ToUInt16(Data, 0x4A3); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x4A3); } + public int BattlePoints { get => ReadUInt16LittleEndian(Data.AsSpan(0x4A1)); set => WriteUInt16LittleEndian(Data.AsSpan(0x4A1), (ushort)value); } + public int Pokemiles { get => ReadUInt16LittleEndian(Data.AsSpan(0x4A3)); set => WriteUInt16LittleEndian(Data.AsSpan(0x4A3), (ushort)value); } } /// @@ -86,32 +87,32 @@ public sealed class PL6_PKM : IRibbonSetEvent3, IRibbonSetEvent4, IEncounterInfo public PL6_PKM() : this(new byte[Size]) { } public PL6_PKM(byte[] data) => Data = data; - public int TID { get => BitConverter.ToUInt16(Data, 0x00); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x00); } - public int SID { get => BitConverter.ToUInt16(Data, 0x02); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x02); } + public int TID { get => ReadUInt16LittleEndian(Data.AsSpan(0x00)); set => WriteUInt16LittleEndian(Data.AsSpan(0x00), (ushort)value); } + public int SID { get => ReadUInt16LittleEndian(Data.AsSpan(0x02)); set => WriteUInt16LittleEndian(Data.AsSpan(0x02), (ushort)value); } public int OriginGame { get => Data[0x04]; set => Data[0x04] = (byte)value; } - public uint EncryptionConstant { get => BitConverter.ToUInt32(Data, 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, 0x08); } + public uint EncryptionConstant { get => ReadUInt32LittleEndian(Data.AsSpan(0x08)); set => WriteUInt32LittleEndian(Data.AsSpan(0x08), value); } public int Pokéball { get => Data[0xE]; set => Data[0xE] = (byte)value; } - public int HeldItem { get => BitConverter.ToUInt16(Data, 0x10); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x10); } - public int Move1 { get => BitConverter.ToUInt16(Data, 0x12); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x12); } - public int Move2 { get => BitConverter.ToUInt16(Data, 0x14); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14); } - public int Move3 { get => BitConverter.ToUInt16(Data, 0x16); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x16); } - public int Move4 { get => BitConverter.ToUInt16(Data, 0x18); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x18); } - public int Species { get => BitConverter.ToUInt16(Data, 0x1A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1A); } + public int HeldItem { get => ReadUInt16LittleEndian(Data.AsSpan(0x10)); set => WriteUInt16LittleEndian(Data.AsSpan(0x10), (ushort)value); } + public int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x12)); set => WriteUInt16LittleEndian(Data.AsSpan(0x12), (ushort)value); } + public int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x14)); set => WriteUInt16LittleEndian(Data.AsSpan(0x14), (ushort)value); } + public int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x16)); set => WriteUInt16LittleEndian(Data.AsSpan(0x16), (ushort)value); } + public int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x18)); set => WriteUInt16LittleEndian(Data.AsSpan(0x18), (ushort)value); } + public int Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x1A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1A), (ushort)value); } public int Form { get => Data[0x1C]; set => Data[0x1C] = (byte)value; } public int Language { get => Data[0x1D]; set => Data[0x1D] = (byte)value; } public string Nickname { - get => StringConverter.GetString6(Data, 0x1E, 0x1A); - set => StringConverter.SetString6(value, 12, 13).CopyTo(Data, 0x1E); + get => StringConverter6.GetString(Data.AsSpan(0x1E, 0x1A)); + set => StringConverter6.SetString(Data.AsSpan(0x1E, 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); } public int Nature { get => Data[0x38]; set => Data[0x38] = (byte)value; } public int Gender { get => Data[0x39]; set => Data[0x39] = (byte)value; } public int AbilityType { get => Data[0x3A]; set => Data[0x3A] = (byte)value; } public int PIDType { get => Data[0x3B]; set => Data[0x3B] = (byte)value; } - public int EggLocation { get => BitConverter.ToUInt16(Data, 0x3C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x3C); } - public int MetLocation { get => BitConverter.ToUInt16(Data, 0x3E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x3F); } + public int EggLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0x3C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x3C), (ushort)value); } + public int MetLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0x3E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x3E), (ushort)value); } public int MetLevel { get => Data[0x40]; set => Data[0x40] = (byte)value; } public int CNT_Cool { get => Data[0x41]; set => Data[0x41] = (byte)value; } @@ -132,20 +133,20 @@ public string Nickname public string OT { - get => StringConverter.GetString6(Data, 0x4E, 0x1A); - set => StringConverter.SetString6(value, 12, 13).CopyTo(Data, 0x4E); + get => StringConverter6.GetString(Data.AsSpan(0x4E, 0x1A)); + set => StringConverter6.SetString(Data.AsSpan(0x4E, 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); } public int Level { get => Data[0x68]; set => Data[0x68] = (byte)value; } public bool IsEgg { get => Data[0x69] == 1; set => Data[0x69] = value ? (byte)1 : (byte)0; } - public uint PID { get => BitConverter.ToUInt32(Data, 0x6C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x6C); } - public int RelearnMove1 { get => BitConverter.ToUInt16(Data, 0x70); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x70); } - public int RelearnMove2 { get => BitConverter.ToUInt16(Data, 0x72); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x72); } - public int RelearnMove3 { get => BitConverter.ToUInt16(Data, 0x74); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x74); } - public int RelearnMove4 { get => BitConverter.ToUInt16(Data, 0x76); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x76); } + public uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0x6C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x6C), value); } + public int RelearnMove1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x70)); set => WriteUInt16LittleEndian(Data.AsSpan(0x70), (ushort)value); } + public int RelearnMove2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x72)); set => WriteUInt16LittleEndian(Data.AsSpan(0x72), (ushort)value); } + public int RelearnMove3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x74)); set => WriteUInt16LittleEndian(Data.AsSpan(0x74), (ushort)value); } + public int RelearnMove4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x76)); set => WriteUInt16LittleEndian(Data.AsSpan(0x76), (ushort)value); } public int OT_Intensity { get => Data[0x78]; set => Data[0x78] = (byte)value; } public int OT_Memory { get => Data[0x79]; set => Data[0x79] = (byte)value; } - public int OT_TextVar { get => BitConverter.ToUInt16(Data, 0x7A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x7A); } + public int OT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0x7A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x7A), (ushort)value); } public int OT_Feeling { get => Data[0x7C]; set => Data[0x7C] = (byte)value; } private byte RIB0 { get => Data[0x0C]; set => Data[0x0C] = value; } diff --git a/PKHeX.Core/MysteryGifts/WB7.cs b/PKHeX.Core/MysteryGifts/WB7.cs index 19428307e..6f0a35315 100644 --- a/PKHeX.Core/MysteryGifts/WB7.cs +++ b/PKHeX.Core/MysteryGifts/WB7.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; +using static System.Buffers.Binary. BinaryPrimitives; namespace PKHeX.Core { @@ -36,21 +36,21 @@ public bool CanBeReceivedByVersion(int v) // General Card Properties public override int CardID { - get => BitConverter.ToUInt16(Data, CardStart + 0); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0), (ushort)value); } public override string CardTitle { // Max len 36 char, followed by null terminator - get => StringConverter.GetString7b(Data, CardStart + 2, 0x4A); - set => StringConverter.SetString7b(value, 36, 37).CopyTo(Data, CardStart + 2); + get => StringConverter8.GetString(Data.AsSpan(CardStart + 2, 0x4A)); + set => StringConverter8.SetString(Data.AsSpan(CardStart + 2, 0x4A), value.AsSpan(), 36, StringConverterOption.ClearZero); } private uint RawDate { - get => BitConverter.ToUInt32(Data, CardStart + 0x4C); - set => BitConverter.GetBytes(value).CopyTo(Data, CardStart + 0x4C); + get => ReadUInt32LittleEndian(Data.AsSpan(CardStart + 0x4C)); + set => WriteUInt32LittleEndian(Data.AsSpan(CardStart + 0x4C), value); } private uint Year @@ -119,16 +119,16 @@ private uint Day // Item Properties public override bool IsItem { get => CardType == 1; set { if (value) CardType = 1; } } - public override int ItemID { get => BitConverter.ToUInt16(Data, CardStart + 0x68); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x68); } - public int GetItem(int index) => BitConverter.ToUInt16(Data, CardStart + 0x68 + (0x4 * index)); - public void SetItem(int index, ushort item) => BitConverter.GetBytes(item).CopyTo(Data, CardStart + 0x68 + (4 * index)); - public int GetQuantity(int index) => BitConverter.ToUInt16(Data, CardStart + 0x6A + (0x4 * index)); - public void SetQuantity(int index, ushort quantity) => BitConverter.GetBytes(quantity).CopyTo(Data, CardStart + 0x6A + (4 * index)); + public override int ItemID { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x68)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x68), (ushort)value); } + public int GetItem(int index) => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x68 + (0x4 * index))); + public void SetItem(int index, ushort item) => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x68 + (4 * index)), item); + public int GetQuantity(int index) => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x6A + (0x4 * index))); + public void SetQuantity(int index, ushort quantity) => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x6A + (4 * index)), quantity); public override int Quantity { - get => BitConverter.ToUInt16(Data, CardStart + 0x6A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x6A); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x6A)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x6A), (ushort)value); } // Pokémon Properties @@ -137,24 +137,24 @@ public override int Quantity public override int TID { - get => BitConverter.ToUInt16(Data, CardStart + 0x68); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x68); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x68)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x68), (ushort)value); } public override int SID { - get => BitConverter.ToUInt16(Data, CardStart + 0x6A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x6A); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x6A)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x6A), (ushort)value); } public int OriginGame { - get => BitConverter.ToInt32(Data, CardStart + 0x6C); - set => BitConverter.GetBytes(value).CopyTo(Data, CardStart + 0x6C); + get => ReadInt32LittleEndian(Data.AsSpan(CardStart + 0x6C)); + set => WriteInt32LittleEndian(Data.AsSpan(CardStart + 0x6C), value); } public uint EncryptionConstant { - get => BitConverter.ToUInt32(Data, CardStart + 0x70); - set => BitConverter.GetBytes(value).CopyTo(Data, CardStart + 0x70); + get => ReadUInt32LittleEndian(Data.AsSpan(CardStart + 0x70)); + set => WriteUInt32LittleEndian(Data.AsSpan(CardStart + 0x70), value); } public override int Ball @@ -164,15 +164,15 @@ public override int Ball public override int HeldItem // no references { - get => BitConverter.ToUInt16(Data, CardStart + 0x78); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x78); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x78)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x78), (ushort)value); } - public int Move1 { get => BitConverter.ToUInt16(Data, CardStart + 0x7A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x7A); } - public int Move2 { get => BitConverter.ToUInt16(Data, CardStart + 0x7C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x7C); } - public int Move3 { get => BitConverter.ToUInt16(Data, CardStart + 0x7E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x7E); } - public int Move4 { get => BitConverter.ToUInt16(Data, CardStart + 0x80); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x80); } - public override int Species { get => BitConverter.ToUInt16(Data, CardStart + 0x82); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x82); } + public int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x7A)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x7A), (ushort)value); } + public int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x7C)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x7C), (ushort)value); } + public int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x7E)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x7E), (ushort)value); } + public int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x80)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x80), (ushort)value); } + public override int Species { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x82)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x82), (ushort)value); } public override int Form { get => Data[CardStart + 0x84]; set => Data[CardStart + 0x84] = (byte)value; } // public int Language { get => Data[CardStart + 0x85]; set => Data[CardStart + 0x85] = (byte)value; } @@ -187,8 +187,8 @@ public override int Ball public override int Gender { get => Data[CardStart + 0xA1]; set => Data[CardStart + 0xA1] = (byte)value; } public override int AbilityType { get => 3; set => Data[CardStart + 0xA2] = (byte)value; } // no references, always ability 0/1 public Shiny PIDType { get => (Shiny)Data[CardStart + 0xA3]; set => Data[CardStart + 0xA3] = (byte)value; } - public override int EggLocation { get => BitConverter.ToUInt16(Data, CardStart + 0xA4); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0xA4); } - public int MetLocation { get => BitConverter.ToUInt16(Data, CardStart + 0xA6); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0xA6); } + public override int EggLocation { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0xA4)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0xA4), (ushort)value); } + public int MetLocation { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0xA6)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0xA6), (ushort)value); } public int MetLevel { get => Data[CardStart + 0xA8]; set => Data[CardStart + 0xA8] = (byte)value; } public int IV_HP { get => Data[CardStart + 0xAF]; set => Data[CardStart + 0xAF] = (byte)value; } @@ -208,13 +208,13 @@ public override int Ball public override int Level { get => Data[CardStart + 0xD0]; set => Data[CardStart + 0xD0] = (byte)value; } public override bool IsEgg { get => Data[CardStart + 0xD1] == 1; set => Data[CardStart + 0xD1] = value ? (byte)1 : (byte)0; } - public ushort AdditionalItem { get => BitConverter.ToUInt16(Data, CardStart + 0xD2); set => BitConverter.GetBytes(value).CopyTo(Data, CardStart + 0xD2); } + public ushort AdditionalItem { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0xD2)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0xD2), value); } - public uint PID { get => BitConverter.ToUInt32(Data, 0xD4); set => BitConverter.GetBytes(value).CopyTo(Data, 0xD4); } - public int RelearnMove1 { get => BitConverter.ToUInt16(Data, CardStart + 0xD8); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0xD8); } - public int RelearnMove2 { get => BitConverter.ToUInt16(Data, CardStart + 0xDA); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0xDA); } - public int RelearnMove3 { get => BitConverter.ToUInt16(Data, CardStart + 0xDC); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0xDC); } - public int RelearnMove4 { get => BitConverter.ToUInt16(Data, CardStart + 0xDE); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0xDE); } + public uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0xD4)); set => WriteUInt32LittleEndian(Data.AsSpan(0xD4), value); } + public int RelearnMove1 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0xD8)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0xD8), (ushort)value); } + public int RelearnMove2 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0xDA)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0xDA), (ushort)value); } + public int RelearnMove3 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0xDC)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0xDC), (ushort)value); } + public int RelearnMove4 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0xDE)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0xDE), (ushort)value); } public int AV_HP { get => Data[CardStart + 0xE5]; set => Data[CardStart + 0xE5] = (byte)value; } public int AV_ATK { get => Data[CardStart + 0xE6]; set => Data[CardStart + 0xE6] = (byte)value; } @@ -289,11 +289,11 @@ public int GetLanguage(int redeemLanguage) return redeemLanguage; } - public string GetNickname(int language) => StringConverter.GetString7b(Data, GetNicknameOffset(language), 0x1A); - public void SetNickname(int language, string value) => StringConverter.SetString7b(value, 12, 13).CopyTo(Data, GetNicknameOffset(language)); + public string GetNickname(int language) => StringConverter8.GetString(Data.AsSpan(GetNicknameOffset(language), 0x1A)); + public void SetNickname(int language, string value) => StringConverter8.SetString(Data.AsSpan(GetNicknameOffset(language), 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); - public string GetOT(int language) => StringConverter.GetString7b(Data, GetOTOffset(language), 0x1A); - public void SetOT(int language, string value) => StringConverter.SetString7b(value, 12, 13).CopyTo(Data, GetOTOffset(language)); + public string GetOT(int language) => StringConverter8.GetString(Data.AsSpan(GetOTOffset(language), 0x1A)); + public void SetOT(int language, string value) => StringConverter8.SetString(Data.AsSpan(GetOTOffset(language), 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); private static int GetNicknameOffset(int language) { @@ -459,7 +459,7 @@ private void SetPID(PKM pk) private void SetIVs(PKM pk) { - int[] finalIVs = new int[6]; + Span finalIVs = stackalloc int[6]; var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3); var rng = Util.Rand; if (ivflag == 0) // Random IVs @@ -471,11 +471,11 @@ private void SetIVs(PKM pk) { int IVCount = ivflag - 0xFB; do { finalIVs[rng.Next(6)] = 31; } - while (finalIVs.Count(iv => iv == 31) < IVCount); + while (finalIVs.Count(31) < IVCount); for (int i = 0; i < 6; i++) finalIVs[i] = finalIVs[i] == 31 ? 31 : rng.Next(32); } - pk.IVs = finalIVs; + pk.SetIVs(finalIVs); } public bool CanHaveLanguage(int language) diff --git a/PKHeX.Core/MysteryGifts/WB8.cs b/PKHeX.Core/MysteryGifts/WB8.cs index 95b5bc59c..f1948eaf1 100644 --- a/PKHeX.Core/MysteryGifts/WB8.cs +++ b/PKHeX.Core/MysteryGifts/WB8.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; using static PKHeX.Core.RibbonIndex; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -40,8 +40,8 @@ public enum GiftType : byte public override int CardID { - get => BitConverter.ToUInt16(Data, CardStart + 0x8); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x8); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x8)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x8), (ushort)value); } public byte CardFlags { get => Data[CardStart + 0x10]; set => Data[CardStart + 0x10] = value; } @@ -77,10 +77,10 @@ public override int Quantity set => SetQuantity(0, (ushort)value); } - public int GetItem(int index) => BitConverter.ToUInt16(Data, CardStart + 0x20 + (0x10 * index)); - public void SetItem(int index, ushort item) => BitConverter.GetBytes(item).CopyTo(Data, CardStart + 0x20 + (0x10 * index)); - public int GetQuantity(int index) => BitConverter.ToUInt16(Data, CardStart + 0x22 + (0x10 * index)); - public void SetQuantity(int index, ushort quantity) => BitConverter.GetBytes(quantity).CopyTo(Data, CardStart + 0x22 + (0x10 * index)); + public int GetItem(int index) => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x20 + (0x10 * index))); + public void SetItem(int index, ushort item) => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x20 + (0x10 * index)), item); + public int GetQuantity(int index) => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x22 + (0x10 * index))); + public void SetQuantity(int index, ushort quantity) => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x22 + (0x10 * index)), quantity); // Pokémon Properties public override bool IsPokémon { get => CardType == GiftType.Pokemon; set { if (value) CardType = GiftType.Pokemon; } } @@ -108,59 +108,59 @@ public override bool IsShiny public override int TID { - get => BitConverter.ToUInt16(Data, CardStart + 0x20); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x20); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x20)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x20), (ushort)value); } public override int SID { - get => BitConverter.ToUInt16(Data, CardStart + 0x22); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x22); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x22)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x22), (ushort)value); } public int OriginGame { - get => BitConverter.ToInt32(Data, CardStart + 0x24); - set => BitConverter.GetBytes(value).CopyTo(Data, CardStart + 0x24); + get => ReadInt32LittleEndian(Data.AsSpan(CardStart + 0x24)); + set => WriteInt32LittleEndian(Data.AsSpan(CardStart + 0x24), value); } public uint EncryptionConstant { - get => BitConverter.ToUInt32(Data, CardStart + 0x28); - set => BitConverter.GetBytes(value).CopyTo(Data, CardStart + 0x28); + get => ReadUInt32LittleEndian(Data.AsSpan(CardStart + 0x28)); + set => WriteUInt32LittleEndian(Data.AsSpan(CardStart + 0x28), value); } public uint PID { - get => BitConverter.ToUInt32(Data, CardStart + 0x2C); - set => BitConverter.GetBytes(value).CopyTo(Data, CardStart + 0x2C); + get => ReadUInt32LittleEndian(Data.AsSpan(CardStart + 0x2C)); + set => WriteUInt32LittleEndian(Data.AsSpan(CardStart + 0x2C), value); } // Nicknames, OT Names 0x30 - 0x270 - public override int EggLocation { get => BitConverter.ToUInt16(Data, CardStart + 0x270); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x270); } - public int MetLocation { get => BitConverter.ToUInt16(Data, CardStart + 0x272); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x272); } + public override int EggLocation { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x270)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x270), (ushort)value); } + public int MetLocation { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x272)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x272), (ushort)value); } public override int Ball { - get => BitConverter.ToUInt16(Data, CardStart + 0x274); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x274); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x274)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x274), (ushort)value); } public override int HeldItem { - get => BitConverter.ToUInt16(Data, CardStart + 0x276); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x276); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x276)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x276), (ushort)value); } - public int Move1 { get => BitConverter.ToUInt16(Data, CardStart + 0x278); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x278); } - public int Move2 { get => BitConverter.ToUInt16(Data, CardStart + 0x27A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x27A); } - public int Move3 { get => BitConverter.ToUInt16(Data, CardStart + 0x27C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x27C); } - public int Move4 { get => BitConverter.ToUInt16(Data, CardStart + 0x27E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x27E); } - public int RelearnMove1 { get => BitConverter.ToUInt16(Data, CardStart + 0x280); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x280); } - public int RelearnMove2 { get => BitConverter.ToUInt16(Data, CardStart + 0x282); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x282); } - public int RelearnMove3 { get => BitConverter.ToUInt16(Data, CardStart + 0x284); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x284); } - public int RelearnMove4 { get => BitConverter.ToUInt16(Data, CardStart + 0x286); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x286); } + public int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x278)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x278), (ushort)value); } + public int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x27A)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x27A), (ushort)value); } + public int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x27C)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x27C), (ushort)value); } + public int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x27E)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x27E), (ushort)value); } + public int RelearnMove1 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x280)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x280), (ushort)value); } + public int RelearnMove2 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x282)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x282), (ushort)value); } + public int RelearnMove3 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x284)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x284), (ushort)value); } + public int RelearnMove4 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x286)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x286), (ushort)value); } - public override int Species { get => BitConverter.ToUInt16(Data, CardStart + 0x288); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x288); } + public override int Species { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x288)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x288), (ushort)value); } public override int Form { get => Data[CardStart + 0x28A]; set => Data[CardStart + 0x28A] = (byte)value; } public override int Gender { get => Data[CardStart + 0x28B]; set => Data[CardStart + 0x28B] = (byte)value; } public override int Level { get => Data[CardStart + 0x28C]; set => Data[CardStart + 0x28C] = (byte)value; } @@ -191,10 +191,10 @@ public bool HasMark() { for (int i = 0; i < RibbonBytesCount; i++) { - var val = Data[RibbonBytesOffset + i]; - if (val == RibbonByteNone) + var value = Data[RibbonBytesOffset + i]; + if (value == RibbonByteNone) return false; - if ((RibbonIndex)val is >= MarkLunchtime and <= MarkSlump) + if ((RibbonIndex)value is >= MarkLunchtime and <= MarkSlump) return true; } return false; @@ -260,14 +260,14 @@ public int[] EVs } } - public bool GetIsNicknamed(int language) => BitConverter.ToUInt16(Data, GetNicknameOffset(language)) != 0; + public bool GetIsNicknamed(int language) => ReadUInt16LittleEndian(Data.AsSpan(GetNicknameOffset(language))) != 0; public bool CanBeAnyLanguage() { for (int i = 0; i < 9; i++) { var ofs = GetLanguageOffset(i); - var lang = BitConverter.ToInt16(Data, ofs); + var lang = ReadInt16LittleEndian(Data.AsSpan(ofs)); if (lang != 0) return false; } @@ -285,7 +285,7 @@ public bool CanHaveLanguage(int language) for (int i = 0; i < 9; i++) { var ofs = GetLanguageOffset(i); - var lang = BitConverter.ToInt16(Data, ofs); + var lang = ReadInt16LittleEndian(Data.AsSpan(ofs)); if (lang == language) return true; } @@ -295,7 +295,7 @@ public bool CanHaveLanguage(int language) public int GetLanguage(int redeemLanguage) => Data[GetLanguageOffset(GetLanguageIndex(redeemLanguage))]; private static int GetLanguageOffset(int index) => 0x30 + (index * 0x20) + 0x1A; - public bool GetHasOT(int language) => BitConverter.ToUInt16(Data, GetOTOffset(language)) != 0; + public bool GetHasOT(int language) => ReadUInt16LittleEndian(Data.AsSpan(GetOTOffset(language))) != 0; private static int GetLanguageIndex(int language) { @@ -336,11 +336,11 @@ public override IReadOnlyList Relearn public bool IsNicknamed => false; public int Language => 2; - public string GetNickname(int language) => StringConverter.GetString7b(Data, GetNicknameOffset(language), 0x1A); - public void SetNickname(int language, string value) => StringConverter.SetString7b(value, 12, 13).CopyTo(Data, GetNicknameOffset(language)); + public string GetNickname(int language) => StringConverter8.GetString(Data.AsSpan(GetNicknameOffset(language), 0x1A)); + public void SetNickname(int language, string value) => StringConverter8.SetString(Data.AsSpan(GetNicknameOffset(language), 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); - public string GetOT(int language) => StringConverter.GetString7b(Data, GetOTOffset(language), 0x1A); - public void SetOT(int language, string value) => StringConverter.SetString7b(value, 12, 13).CopyTo(Data, GetOTOffset(language)); + public string GetOT(int language) => StringConverter8.GetString(Data.AsSpan(GetOTOffset(language), 0x1A)); + public void SetOT(int language, string value) => StringConverter8.SetString(Data.AsSpan(GetOTOffset(language), 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); private static int GetNicknameOffset(int language) { @@ -536,7 +536,7 @@ private void SetPID(PKM pk) private void SetIVs(PKM pk) { - int[] finalIVs = new int[6]; + Span finalIVs = stackalloc int[6]; var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3); var rng = Util.Rand; if (ivflag == 0) // Random IVs @@ -548,11 +548,11 @@ private void SetIVs(PKM pk) { int IVCount = ivflag - 0xFB; do { finalIVs[rng.Next(6)] = 31; } - while (finalIVs.Count(iv => iv == 31) < IVCount); + while (finalIVs.Count(31) < IVCount); for (int i = 0; i < 6; i++) finalIVs[i] = finalIVs[i] == 31 ? 31 : rng.Next(32); } - pk.IVs = finalIVs; + pk.SetIVs(finalIVs); } public override bool IsMatchExact(PKM pkm, DexLevel evo) diff --git a/PKHeX.Core/MysteryGifts/WC6.cs b/PKHeX.Core/MysteryGifts/WC6.cs index 763e129ed..de27f714e 100644 --- a/PKHeX.Core/MysteryGifts/WC6.cs +++ b/PKHeX.Core/MysteryGifts/WC6.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -33,21 +33,21 @@ public bool CanBeReceivedByVersion(int v) // General Card Properties public override int CardID { - get => BitConverter.ToUInt16(Data, 0); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0); + get => ReadUInt16LittleEndian(Data.AsSpan(0)); + set => WriteUInt16LittleEndian(Data.AsSpan(0), (ushort)value); } public override string CardTitle { // Max len 36 char, followed by null terminator - get => StringConverter.GetString6(Data, 2, 0x4A); - set => StringConverter.SetString6(value, 36, 37).CopyTo(Data, 2); + get => StringConverter6.GetString(Data.AsSpan(2, 0x4A)); + set => StringConverter6.SetString(Data.AsSpan(2, 0x4A), value.AsSpan(), 36, StringConverterOption.ClearZero); } internal uint RawDate { - get => BitConverter.ToUInt32(Data, 0x4C); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x4C); + get => ReadUInt32LittleEndian(Data.AsSpan(0x4C)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x4C), value); } private uint Year @@ -113,44 +113,44 @@ private uint Day public override bool IsItem { get => CardType == 1; set { if (value) CardType = 1; } } public override int ItemID { - get => BitConverter.ToUInt16(Data, 0x68); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x68); } + get => ReadUInt16LittleEndian(Data.AsSpan(0x68)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x68), (ushort)value); } public override int Quantity { - get => BitConverter.ToUInt16(Data, 0x70); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x70); } + get => ReadUInt16LittleEndian(Data.AsSpan(0x70)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x70), (ushort)value); } // Pokémon Properties public override bool IsPokémon { get => CardType == 0; set { if (value) CardType = 0; } } public override bool IsShiny => PIDType == Shiny.Always; - public override int TID { get => BitConverter.ToUInt16(Data, 0x68); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x68); } - public override int SID { get => BitConverter.ToUInt16(Data, 0x6A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6A); } + public override int TID { get => ReadUInt16LittleEndian(Data.AsSpan(0x68)); set => WriteUInt16LittleEndian(Data.AsSpan(0x68), (ushort)value); } + public override int SID { get => ReadUInt16LittleEndian(Data.AsSpan(0x6A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x6A), (ushort)value); } public int OriginGame { get => Data[0x6C]; set => Data[0x6C] = (byte)value; } - public uint EncryptionConstant { get => BitConverter.ToUInt32(Data, 0x70); set => BitConverter.GetBytes(value).CopyTo(Data, 0x70); } + public uint EncryptionConstant { get => ReadUInt32LittleEndian(Data.AsSpan(0x70)); set => WriteUInt32LittleEndian(Data.AsSpan(0x70), value); } public override int Ball { get => Data[0x76]; set => Data[0x76] = (byte)value; } - public override int HeldItem { get => BitConverter.ToUInt16(Data, 0x78); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x78); } + public override int HeldItem { get => ReadUInt16LittleEndian(Data.AsSpan(0x78)); set => WriteUInt16LittleEndian(Data.AsSpan(0x78), (ushort)value); } - public int Move1 { get => BitConverter.ToUInt16(Data, 0x7A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x7A); } - public int Move2 { get => BitConverter.ToUInt16(Data, 0x7C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x7C); } - public int Move3 { get => BitConverter.ToUInt16(Data, 0x7E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x7E); } - public int Move4 { get => BitConverter.ToUInt16(Data, 0x80); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x80); } + public int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x7A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x7A), (ushort)value); } + public int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x7C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x7C), (ushort)value); } + public int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x7E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x7E), (ushort)value); } + public int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x80)); set => WriteUInt16LittleEndian(Data.AsSpan(0x80), (ushort)value); } - public override int Species { get => BitConverter.ToUInt16(Data, 0x82); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x82); } + public override int Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x82)); set => WriteUInt16LittleEndian(Data.AsSpan(0x82), (ushort)value); } public override int Form { get => Data[0x84]; set => Data[0x84] = (byte)value; } public int Language { get => Data[0x85]; set => Data[0x85] = (byte)value; } public string Nickname { - get => StringConverter.GetString6(Data, 0x86, 0x1A); - set => StringConverter.SetString6(value, 12, 13).CopyTo(Data, 0x86); + get => StringConverter6.GetString(Data.AsSpan(0x86, 0x1A)); + set => StringConverter6.SetString(Data.AsSpan(0x86, 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); } public int Nature { get => (sbyte)Data[0xA0]; set => Data[0xA0] = (byte)value; } public override int Gender { get => Data[0xA1]; set => Data[0xA1] = (byte)value; } public override int AbilityType { get => Data[0xA2]; set => Data[0xA2] = (byte)value; } public Shiny PIDType { get => (Shiny)Data[0xA3]; set => Data[0xA3] = (byte)value; } - public override int EggLocation { get => BitConverter.ToUInt16(Data, 0xA4); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xA4); } - public int MetLocation { get => BitConverter.ToUInt16(Data, 0xA6); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xA6); } + public override int EggLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0xA4)); set => WriteUInt16LittleEndian(Data.AsSpan(0xA4), (ushort)value); } + public int MetLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0xA6)); set => WriteUInt16LittleEndian(Data.AsSpan(0xA6), (ushort)value); } public byte CNT_Cool { get => Data[0xA9]; set => Data[0xA9] = value; } public byte CNT_Beauty { get => Data[0xAA]; set => Data[0xAA] = value; } @@ -170,22 +170,22 @@ public string Nickname public override string OT_Name { - get => StringConverter.GetString6(Data, 0xB6, 0x1A); - set => StringConverter.SetString6(value, 12, 13).CopyTo(Data, 0xB6); + get => StringConverter6.GetString(Data.AsSpan(0xB6, 0x1A)); + set => StringConverter6.SetString(Data.AsSpan(0xB6, 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); } public override int Level { get => Data[0xD0]; set => Data[0xD0] = (byte)value; } public override bool IsEgg { get => Data[0xD1] == 1; set => Data[0xD1] = value ? (byte)1 : (byte)0; } - public uint PID { get => BitConverter.ToUInt32(Data, 0xD4); set => BitConverter.GetBytes(value).CopyTo(Data, 0xD4); } + public uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0xD4)); set => WriteUInt32LittleEndian(Data.AsSpan(0xD4), value); } - public int RelearnMove1 { get => BitConverter.ToUInt16(Data, 0xD8); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xD8); } - public int RelearnMove2 { get => BitConverter.ToUInt16(Data, 0xDA); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xDA); } - public int RelearnMove3 { get => BitConverter.ToUInt16(Data, 0xDC); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xDC); } - public int RelearnMove4 { get => BitConverter.ToUInt16(Data, 0xDE); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xDE); } + public int RelearnMove1 { get => ReadUInt16LittleEndian(Data.AsSpan(0xD8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xD8), (ushort)value); } + public int RelearnMove2 { get => ReadUInt16LittleEndian(Data.AsSpan(0xDA)); set => WriteUInt16LittleEndian(Data.AsSpan(0xDA), (ushort)value); } + public int RelearnMove3 { get => ReadUInt16LittleEndian(Data.AsSpan(0xDC)); set => WriteUInt16LittleEndian(Data.AsSpan(0xDC), (ushort)value); } + public int RelearnMove4 { get => ReadUInt16LittleEndian(Data.AsSpan(0xDE)); set => WriteUInt16LittleEndian(Data.AsSpan(0xDE), (ushort)value); } public int OT_Intensity { get => Data[0xE0]; set => Data[0xE0] = (byte)value; } public int OT_Memory { get => Data[0xE1]; set => Data[0xE1] = (byte)value; } - public int OT_TextVar { get => BitConverter.ToUInt16(Data, 0xE2); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xE2); } + public int OT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0xE2)); set => WriteUInt16LittleEndian(Data.AsSpan(0xE2), (ushort)value); } public int OT_Feeling { get => Data[0xE4]; set => Data[0xE4] = (byte)value; } public int EV_HP { get => Data[0xE5]; set => Data[0xE5] = (byte)value; } @@ -448,7 +448,7 @@ private void SetPID(PKM pk) private void SetIVs(PKM pk) { - int[] finalIVs = new int[6]; + Span finalIVs = stackalloc int[6]; var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3); var rnd = Util.Rand; if (ivflag == 0) // Random IVs @@ -460,11 +460,11 @@ private void SetIVs(PKM pk) { int IVCount = ivflag - 0xFB; do { finalIVs[rnd.Next(6)] = 31; } - while (finalIVs.Count(iv => iv == 31) < IVCount); + while (finalIVs.Count(31) < IVCount); for (int i = 0; i < 6; i++) finalIVs[i] = finalIVs[i] == 31 ? 31 : rnd.Next(32); } - pk.IVs = finalIVs; + pk.SetIVs(finalIVs); } public override bool IsMatchExact(PKM pkm, DexLevel evo) diff --git a/PKHeX.Core/MysteryGifts/WC6Full.cs b/PKHeX.Core/MysteryGifts/WC6Full.cs index feb722f58..60bfe0050 100644 --- a/PKHeX.Core/MysteryGifts/WC6Full.cs +++ b/PKHeX.Core/MysteryGifts/WC6Full.cs @@ -23,7 +23,8 @@ public WC6Full(byte[] data) Gift.RestrictVersion = RestrictVersion; Gift.RestrictLanguage = RestrictLanguage; } - public static WC6[] GetArray(byte[] WC6Full, byte[] data) + + public static WC6[] GetArray(ReadOnlySpan WC6Full, ReadOnlySpan data) { var countfull = WC6Full.Length / Size; var countgift = data.Length / WC6.Size; @@ -38,9 +39,9 @@ public static WC6[] GetArray(byte[] WC6Full, byte[] data) return result; } - private static WC6 ReadWC6(byte[] data, int ofs, DateTime date) + private static WC6 ReadWC6(ReadOnlySpan data, int ofs, DateTime date) { - var slice = data.Slice(ofs + GiftStart, WC6.Size); + var slice = data.Slice(ofs + GiftStart, WC6.Size).ToArray(); return new WC6(slice) { RestrictVersion = data[ofs], @@ -49,9 +50,9 @@ private static WC6 ReadWC6(byte[] data, int ofs, DateTime date) }; } - private static WC6 ReadWC6Only(byte[] data, int ofs) + private static WC6 ReadWC6Only(ReadOnlySpan data, int ofs) { - var slice = data.Slice(ofs, WC6.Size); + var slice = data.Slice(ofs, WC6.Size).ToArray(); return new WC6(slice); } } diff --git a/PKHeX.Core/MysteryGifts/WC7.cs b/PKHeX.Core/MysteryGifts/WC7.cs index 490461f9b..a40b0cdcb 100644 --- a/PKHeX.Core/MysteryGifts/WC7.cs +++ b/PKHeX.Core/MysteryGifts/WC7.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -32,21 +32,21 @@ public bool CanBeReceivedByVersion(int v) // General Card Properties public override int CardID { - get => BitConverter.ToUInt16(Data, 0); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0); + get => ReadUInt16LittleEndian(Data.AsSpan(0)); + set => WriteUInt16LittleEndian(Data.AsSpan(0), (ushort)value); } public override string CardTitle { // Max len 36 char, followed by null terminator - get => StringConverter.GetString7(Data, 2, 0x4A); - set => StringConverter.SetString7(value, 36, 37).CopyTo(Data, 2); + get => StringConverter7.GetString(Data.AsSpan(2, 0x4A)); + set => StringConverter7.SetString(Data.AsSpan(2, 0x4A), value.AsSpan(), 36, Language, StringConverterOption.ClearZero); } internal uint RawDate { - get => BitConverter.ToUInt32(Data, 0x4C); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x4C); + get => ReadUInt32LittleEndian(Data.AsSpan(0x4C)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x4C), value); } private uint Year @@ -123,16 +123,16 @@ private uint Day // Item Properties public override bool IsItem { get => CardType == 1; set { if (value) CardType = 1; } } - public override int ItemID { get => BitConverter.ToUInt16(Data, 0x68); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x68); } - public int GetItem(int index) => BitConverter.ToUInt16(Data, 0x68 + (0x4 * index)); - public void SetItem(int index, ushort item) => BitConverter.GetBytes(item).CopyTo(Data, 0x68 + (4 * index)); - public int GetQuantity(int index) => BitConverter.ToUInt16(Data, 0x6A + (0x4 * index)); - public void SetQuantity(int index, ushort quantity) => BitConverter.GetBytes(quantity).CopyTo(Data, 0x6A + (4 * index)); + public override int ItemID { get => ReadUInt16LittleEndian(Data.AsSpan(0x68)); set => WriteUInt16LittleEndian(Data.AsSpan(0x68), (ushort)value); } + public int GetItem(int index) => ReadUInt16LittleEndian(Data.AsSpan(0x68 + (0x4 * index))); + public void SetItem(int index, ushort item) => WriteUInt16LittleEndian(Data.AsSpan(0x68 + (4 * index)), item); + public int GetQuantity(int index) => ReadUInt16LittleEndian(Data.AsSpan(0x6A + (0x4 * index))); + public void SetQuantity(int index, ushort quantity) => WriteUInt16LittleEndian(Data.AsSpan(0x6A + (4 * index)), quantity); public override int Quantity { - get => BitConverter.ToUInt16(Data, 0x6A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6A); + get => ReadUInt16LittleEndian(Data.AsSpan(0x6A)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x6A), (ushort)value); } // Pokémon Properties @@ -141,13 +141,13 @@ public override int Quantity public override int TID { - get => BitConverter.ToUInt16(Data, 0x68); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x68); + get => ReadUInt16LittleEndian(Data.AsSpan(0x68)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x68), (ushort)value); } public override int SID { - get => BitConverter.ToUInt16(Data, 0x6A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6A); + get => ReadUInt16LittleEndian(Data.AsSpan(0x6A)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x6A), (ushort)value); } public int OriginGame @@ -157,8 +157,8 @@ public int OriginGame } public uint EncryptionConstant { - get => BitConverter.ToUInt32(Data, 0x70); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x70); + get => ReadUInt32LittleEndian(Data.AsSpan(0x70)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x70), value); } public override int Ball @@ -168,30 +168,30 @@ public override int Ball public override int HeldItem { - get => BitConverter.ToUInt16(Data, 0x78); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x78); + get => ReadUInt16LittleEndian(Data.AsSpan(0x78)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x78), (ushort)value); } - public int Move1 { get => BitConverter.ToUInt16(Data, 0x7A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x7A); } - public int Move2 { get => BitConverter.ToUInt16(Data, 0x7C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x7C); } - public int Move3 { get => BitConverter.ToUInt16(Data, 0x7E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x7E); } - public int Move4 { get => BitConverter.ToUInt16(Data, 0x80); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x80); } - public override int Species { get => BitConverter.ToUInt16(Data, 0x82); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x82); } + public int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x7A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x7A), (ushort)value); } + public int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x7C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x7C), (ushort)value); } + public int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x7E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x7E), (ushort)value); } + public int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x80)); set => WriteUInt16LittleEndian(Data.AsSpan(0x80), (ushort)value); } + public override int Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x82)); set => WriteUInt16LittleEndian(Data.AsSpan(0x82), (ushort)value); } public override int Form { get => Data[0x84]; set => Data[0x84] = (byte)value; } public int Language { get => Data[0x85]; set => Data[0x85] = (byte)value; } public string Nickname { - get => StringConverter.GetString7(Data, 0x86, 0x1A); - set => StringConverter.SetString7(value, 12, 13).CopyTo(Data, 0x86); + get => StringConverter7.GetString(Data.AsSpan(0x86, 0x1A)); + set => StringConverter7.SetString(Data.AsSpan(0x86, 0x1A), value.AsSpan(), 12, Language, StringConverterOption.ClearZero); } public int Nature { get => (sbyte)Data[0xA0]; set => Data[0xA0] = (byte)value; } public override int Gender { get => Data[0xA1]; set => Data[0xA1] = (byte)value; } public override int AbilityType { get => Data[0xA2]; set => Data[0xA2] = (byte)value; } public Shiny PIDType { get => (Shiny)Data[0xA3]; set => Data[0xA3] = (byte)value; } - public override int EggLocation { get => BitConverter.ToUInt16(Data, 0xA4); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xA4); } - public int MetLocation { get => BitConverter.ToUInt16(Data, 0xA6); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xA6); } + public override int EggLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0xA4)); set => WriteUInt16LittleEndian(Data.AsSpan(0xA4), (ushort)value); } + public int MetLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0xA6)); set => WriteUInt16LittleEndian(Data.AsSpan(0xA6), (ushort)value); } public int MetLevel { get => Data[0xA8]; set => Data[0xA8] = (byte)value; } public byte CNT_Cool { get => Data[0xA9]; set => Data[0xA9] = value; } @@ -212,23 +212,23 @@ public string Nickname public override string OT_Name { - get => StringConverter.GetString7(Data, 0xB6, 0x1A); - set => StringConverter.SetString7(value, 12, 13).CopyTo(Data, 0xB6); + get => StringConverter7.GetString(Data.AsSpan(0xB6, 0x1A)); + set => StringConverter7.SetString(Data.AsSpan(0xB6, 0x1A), value.AsSpan(), 12, Language, StringConverterOption.ClearZero); } public override int Level { get => Data[0xD0]; set => Data[0xD0] = (byte)value; } public override bool IsEgg { get => Data[0xD1] == 1; set => Data[0xD1] = value ? (byte)1 : (byte)0; } - public ushort AdditionalItem { get => BitConverter.ToUInt16(Data, 0xD2); set => BitConverter.GetBytes(value).CopyTo(Data, 0xD2); } + public ushort AdditionalItem { get => ReadUInt16LittleEndian(Data.AsSpan(0xD2)); set => WriteUInt16LittleEndian(Data.AsSpan(0xD2), value); } - public uint PID { get => BitConverter.ToUInt32(Data, 0xD4); set => BitConverter.GetBytes(value).CopyTo(Data, 0xD4); } - public int RelearnMove1 { get => BitConverter.ToUInt16(Data, 0xD8); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xD8); } - public int RelearnMove2 { get => BitConverter.ToUInt16(Data, 0xDA); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xDA); } - public int RelearnMove3 { get => BitConverter.ToUInt16(Data, 0xDC); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xDC); } - public int RelearnMove4 { get => BitConverter.ToUInt16(Data, 0xDE); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xDE); } + public uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0xD4)); set => WriteUInt32LittleEndian(Data.AsSpan(0xD4), value); } + public int RelearnMove1 { get => ReadUInt16LittleEndian(Data.AsSpan(0xD8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xD8), (ushort)value); } + public int RelearnMove2 { get => ReadUInt16LittleEndian(Data.AsSpan(0xDA)); set => WriteUInt16LittleEndian(Data.AsSpan(0xDA), (ushort)value); } + public int RelearnMove3 { get => ReadUInt16LittleEndian(Data.AsSpan(0xDC)); set => WriteUInt16LittleEndian(Data.AsSpan(0xDC), (ushort)value); } + public int RelearnMove4 { get => ReadUInt16LittleEndian(Data.AsSpan(0xDE)); set => WriteUInt16LittleEndian(Data.AsSpan(0xDE), (ushort)value); } public int OT_Intensity { get => Data[0xE0]; set => Data[0xE0] = (byte)value; } public int OT_Memory { get => Data[0xE1]; set => Data[0xE1] = (byte)value; } - public int OT_TextVar { get => BitConverter.ToUInt16(Data, 0xE2); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xE2); } + public int OT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0xE2)); set => WriteUInt16LittleEndian(Data.AsSpan(0xE2), (ushort)value); } public int OT_Feeling { get => Data[0xE4]; set => Data[0xE4] = (byte)value; } public int EV_HP { get => Data[0xE5]; set => Data[0xE5] = (byte)value; } @@ -480,7 +480,7 @@ private void SetPID(PKM pk) private void SetIVs(PKM pk) { - int[] finalIVs = new int[6]; + Span finalIVs = stackalloc int[6]; var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3); var rng = Util.Rand; if (ivflag == 0) // Random IVs @@ -492,11 +492,11 @@ private void SetIVs(PKM pk) { int IVCount = ivflag - 0xFB; do { finalIVs[rng.Next(6)] = 31; } - while (finalIVs.Count(iv => iv == 31) < IVCount); + while (finalIVs.Count(31) < IVCount); for (int i = 0; i < 6; i++) finalIVs[i] = finalIVs[i] == 31 ? 31 : rng.Next(32); } - pk.IVs = finalIVs; + pk.SetIVs(finalIVs); } public bool IsAshGreninjaWC7(PKM pkm) diff --git a/PKHeX.Core/MysteryGifts/WC7Full.cs b/PKHeX.Core/MysteryGifts/WC7Full.cs index fd2ec2790..6af1225ae 100644 --- a/PKHeX.Core/MysteryGifts/WC7Full.cs +++ b/PKHeX.Core/MysteryGifts/WC7Full.cs @@ -24,7 +24,7 @@ public WC7Full(byte[] data) Gift.RestrictLanguage = RestrictLanguage; } - public static WC7[] GetArray(byte[] wc7Full, byte[] data) + public static WC7[] GetArray(ReadOnlySpan wc7Full, ReadOnlySpan data) { var countfull = wc7Full.Length / Size; var countgift = data.Length / WC7.Size; @@ -39,9 +39,9 @@ public static WC7[] GetArray(byte[] wc7Full, byte[] data) return result; } - private static WC7 ReadWC7(byte[] data, int ofs, DateTime date) + private static WC7 ReadWC7(ReadOnlySpan data, int ofs, DateTime date) { - var slice = data.Slice(ofs + GiftStart, WC7.Size); + var slice = data.Slice(ofs + GiftStart, WC7.Size).ToArray(); return new WC7(slice) { RestrictVersion = data[ofs], @@ -50,9 +50,9 @@ private static WC7 ReadWC7(byte[] data, int ofs, DateTime date) }; } - private static WC7 ReadWC7Only(byte[] data, int ofs) + private static WC7 ReadWC7Only(ReadOnlySpan data, int ofs) { - var slice = data.Slice(ofs, WC7.Size); + var slice = data.Slice(ofs, WC7.Size).ToArray(); return new WC7(slice); } } diff --git a/PKHeX.Core/MysteryGifts/WC8.cs b/PKHeX.Core/MysteryGifts/WC8.cs index fef48713e..57fd57d34 100644 --- a/PKHeX.Core/MysteryGifts/WC8.cs +++ b/PKHeX.Core/MysteryGifts/WC8.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; using static PKHeX.Core.RibbonIndex; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -35,8 +35,8 @@ public enum GiftType : byte // General Card Properties public override int CardID { - get => BitConverter.ToUInt16(Data, CardStart + 0x8); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x8); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x8)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x8), (ushort)value); } public byte CardFlags { get => Data[CardStart + 0x10]; set => Data[CardStart + 0x10] = value; } @@ -71,10 +71,10 @@ public override int Quantity set => SetQuantity(0, (ushort)value); } - public int GetItem(int index) => BitConverter.ToUInt16(Data, CardStart + 0x20 + (0x4 * index)); - public void SetItem(int index, ushort item) => BitConverter.GetBytes(item).CopyTo(Data, CardStart + 0x20 + (4 * index)); - public int GetQuantity(int index) => BitConverter.ToUInt16(Data, CardStart + 0x22 + (0x4 * index)); - public void SetQuantity(int index, ushort quantity) => BitConverter.GetBytes(quantity).CopyTo(Data, CardStart + 0x22 + (4 * index)); + public int GetItem(int index) => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x20 + (0x4 * index))); + public void SetItem(int index, ushort item) => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x20 + (4 * index)), item); + public int GetQuantity(int index) => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x22 + (0x4 * index))); + public void SetQuantity(int index, ushort quantity) => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x22 + (4 * index)), quantity); // Pokémon Properties public override bool IsPokémon { get => CardType == GiftType.Pokemon; set { if (value) CardType = GiftType.Pokemon; } } @@ -102,59 +102,59 @@ public override bool IsShiny public override int TID { - get => BitConverter.ToUInt16(Data, CardStart + 0x20); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x20); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x20)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x20), (ushort)value); } public override int SID { - get => BitConverter.ToUInt16(Data, CardStart + 0x22); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x22); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x22)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x22), (ushort)value); } public int OriginGame { - get => BitConverter.ToInt32(Data, CardStart + 0x24); - set => BitConverter.GetBytes(value).CopyTo(Data, CardStart + 0x24); + get => ReadInt32LittleEndian(Data.AsSpan(CardStart + 0x24)); + set => WriteInt32LittleEndian(Data.AsSpan(CardStart + 0x24), value); } public uint EncryptionConstant { - get => BitConverter.ToUInt32(Data, CardStart + 0x28); - set => BitConverter.GetBytes(value).CopyTo(Data, CardStart + 0x28); + get => ReadUInt32LittleEndian(Data.AsSpan(CardStart + 0x28)); + set => WriteUInt32LittleEndian(Data.AsSpan(CardStart + 0x28), value); } public uint PID { - get => BitConverter.ToUInt32(Data, CardStart + 0x2C); - set => BitConverter.GetBytes(value).CopyTo(Data, CardStart + 0x2C); + get => ReadUInt32LittleEndian(Data.AsSpan(CardStart + 0x2C)); + set => WriteUInt32LittleEndian(Data.AsSpan(CardStart + 0x2C), value); } // Nicknames, OT Names 0x30 - 0x228 - public override int EggLocation { get => BitConverter.ToUInt16(Data, CardStart + 0x228); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x228); } - public int MetLocation { get => BitConverter.ToUInt16(Data, CardStart + 0x22A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x22A); } + public override int EggLocation { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x228)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x228), (ushort)value); } + public int MetLocation { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x22A)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x22A), (ushort)value); } public override int Ball { - get => BitConverter.ToUInt16(Data, CardStart + 0x22C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x22C); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x22C)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x22C), (ushort)value); } public override int HeldItem { - get => BitConverter.ToUInt16(Data, CardStart + 0x22E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x22E); + get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x22E)); + set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x22E), (ushort)value); } - public int Move1 { get => BitConverter.ToUInt16(Data, CardStart + 0x230); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x230); } - public int Move2 { get => BitConverter.ToUInt16(Data, CardStart + 0x232); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x232); } - public int Move3 { get => BitConverter.ToUInt16(Data, CardStart + 0x234); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x234); } - public int Move4 { get => BitConverter.ToUInt16(Data, CardStart + 0x236); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x236); } - public int RelearnMove1 { get => BitConverter.ToUInt16(Data, CardStart + 0x238); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x238); } - public int RelearnMove2 { get => BitConverter.ToUInt16(Data, CardStart + 0x23A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x23A); } - public int RelearnMove3 { get => BitConverter.ToUInt16(Data, CardStart + 0x23C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x23C); } - public int RelearnMove4 { get => BitConverter.ToUInt16(Data, CardStart + 0x23E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x23E); } + public int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x230)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x230), (ushort)value); } + public int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x232)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x232), (ushort)value); } + public int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x234)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x234), (ushort)value); } + public int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x236)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x236), (ushort)value); } + public int RelearnMove1 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x238)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x238), (ushort)value); } + public int RelearnMove2 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x23A)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x23A), (ushort)value); } + public int RelearnMove3 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x23C)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x23C), (ushort)value); } + public int RelearnMove4 { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x23E)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x23E), (ushort)value); } - public override int Species { get => BitConverter.ToUInt16(Data, CardStart + 0x240); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x240); } + public override int Species { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x240)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x240), (ushort)value); } public override int Form { get => Data[CardStart + 0x242]; set => Data[CardStart + 0x242] = (byte)value; } public override int Gender { get => Data[CardStart + 0x243]; set => Data[CardStart + 0x243] = (byte)value; } public override int Level { get => Data[CardStart + 0x244]; set => Data[CardStart + 0x244] = (byte)value; } @@ -187,10 +187,10 @@ public bool HasMark() { for (int i = 0; i < RibbonBytesCount; i++) { - var val = Data[RibbonBytesOffset + i]; - if (val == RibbonByteNone) + var value = Data[RibbonBytesOffset + i]; + if (value == RibbonByteNone) return false; - if ((RibbonIndex)val is >= MarkLunchtime and <= MarkSlump) + if ((RibbonIndex)value is >= MarkLunchtime and <= MarkSlump) return true; } return false; @@ -229,7 +229,7 @@ public void SetRibbonAtIndex(int byteIndex, byte ribbonIndex) public int OT_Intensity { get => Data[CardStart + 0x279]; set => Data[CardStart + 0x279] = (byte)value; } public int OT_Memory { get => Data[CardStart + 0x27A]; set => Data[CardStart + 0x27A] = (byte)value; } public int OT_Feeling { get => Data[CardStart + 0x27B]; set => Data[CardStart + 0x27B] = (byte)value; } - public int OT_TextVar { get => BitConverter.ToUInt16(Data, CardStart + 0x27C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x27C); } + public int OT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x27C)); set => WriteUInt16LittleEndian(Data.AsSpan(CardStart + 0x27C), (ushort)value); } // Meta Accessible Properties public override int[] IVs @@ -254,14 +254,14 @@ public int[] EVs } } - public bool GetIsNicknamed(int language) => BitConverter.ToUInt16(Data, GetNicknameOffset(language)) != 0; + public bool GetIsNicknamed(int language) => ReadUInt16LittleEndian(Data.AsSpan(GetNicknameOffset(language))) != 0; public bool CanBeAnyLanguage() { for (int i = 0; i < 9; i++) { var ofs = GetLanguageOffset(i); - var lang = BitConverter.ToInt16(Data, ofs); + var lang = ReadInt16LittleEndian(Data.AsSpan(ofs)); if (lang != 0) return false; } @@ -279,7 +279,7 @@ public bool CanHaveLanguage(int language) for (int i = 0; i < 9; i++) { var ofs = GetLanguageOffset(i); - var lang = BitConverter.ToInt16(Data, ofs); + var lang = ReadInt16LittleEndian(Data.AsSpan(ofs)); if (lang == language) return true; } @@ -289,7 +289,7 @@ public bool CanHaveLanguage(int language) public int GetLanguage(int redeemLanguage) => Data[GetLanguageOffset(GetLanguageIndex(redeemLanguage))]; private static int GetLanguageOffset(int index) => 0x30 + (index * 0x1C) + 0x1A; - public bool GetHasOT(int language) => BitConverter.ToUInt16(Data, GetOTOffset(language)) != 0; + public bool GetHasOT(int language) => ReadUInt16LittleEndian(Data.AsSpan(GetOTOffset(language))) != 0; private static int GetLanguageIndex(int language) { @@ -330,11 +330,11 @@ public override IReadOnlyList Relearn public bool IsNicknamed => false; public int Language => 2; - public string GetNickname(int language) => StringConverter.GetString7b(Data, GetNicknameOffset(language), 0x1A); - public void SetNickname(int language, string value) => StringConverter.SetString7b(value, 12, 13).CopyTo(Data, GetNicknameOffset(language)); + public string GetNickname(int language) => StringConverter8.GetString(Data.AsSpan(GetNicknameOffset(language), 0x1A)); + public void SetNickname(int language, string value) => StringConverter8.SetString(Data.AsSpan(GetNicknameOffset(language), 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); - public string GetOT(int language) => StringConverter.GetString7b(Data, GetOTOffset(language), 0x1A); - public void SetOT(int language, string value) => StringConverter.SetString7b(value, 12, 13).CopyTo(Data, GetOTOffset(language)); + public string GetOT(int language) => StringConverter8.GetString(Data.AsSpan(GetOTOffset(language), 0x1A)); + public void SetOT(int language, string value) => StringConverter8.SetString(Data.AsSpan(GetOTOffset(language), 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); private static int GetNicknameOffset(int language) { @@ -541,7 +541,7 @@ private void SetPID(PKM pk) private void SetIVs(PKM pk) { - int[] finalIVs = new int[6]; + Span finalIVs = stackalloc int[6]; var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3); var rng = Util.Rand; if (ivflag == 0) // Random IVs @@ -553,11 +553,11 @@ private void SetIVs(PKM pk) { int IVCount = ivflag - 0xFB; do { finalIVs[rng.Next(6)] = 31; } - while (finalIVs.Count(iv => iv == 31) < IVCount); + while (finalIVs.Count(31) < IVCount); for (int i = 0; i < 6; i++) finalIVs[i] = finalIVs[i] == 31 ? 31 : rng.Next(32); } - pk.IVs = finalIVs; + pk.SetIVs(finalIVs); } public override bool IsMatchExact(PKM pkm, DexLevel evo) diff --git a/PKHeX.Core/MysteryGifts/WR7.cs b/PKHeX.Core/MysteryGifts/WR7.cs index c1ccd696a..542993cb6 100644 --- a/PKHeX.Core/MysteryGifts/WR7.cs +++ b/PKHeX.Core/MysteryGifts/WR7.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -23,22 +24,22 @@ public sealed class WR7 : DataMysteryGift public uint Epoch { - get => BitConverter.ToUInt32(Data, 0x00); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); + get => ReadUInt32LittleEndian(Data.AsSpan(0x00)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value); } public DateTime Date => new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(Epoch); public override int CardID { - get => BitConverter.ToUInt16(Data, 0x08); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08); + get => ReadUInt16LittleEndian(Data.AsSpan(0x08)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x08), (ushort)value); } public ushort CardType { - get => BitConverter.ToUInt16(Data, 0x0A); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x0A); + get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), value); } public WR7GiftType GiftType @@ -57,30 +58,30 @@ public int ItemCount public override int Species { - get => BitConverter.ToUInt16(Data, 0x10C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x10C); + get => ReadUInt16LittleEndian(Data.AsSpan(0x10C)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x10C), (ushort)value); } public override bool GiftUsed { get; set; } public override int Level // are moves stored? mew has '1' but this could be move { - get => BitConverter.ToUInt16(Data, 0x10E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x10E); + get => ReadUInt16LittleEndian(Data.AsSpan(0x10E)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x10E), (ushort)value); } - public override int ItemID { get => BitConverter.ToUInt16(Data, 0x110); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x110); } - public ushort ItemIDCount { get => BitConverter.ToUInt16(Data, 0x112); set => BitConverter.GetBytes(value).CopyTo(Data, 0x112); } - public ushort ItemSet2Item { get => BitConverter.ToUInt16(Data, 0x114); set => BitConverter.GetBytes(value).CopyTo(Data, 0x114); } - public ushort ItemSet2Count { get => BitConverter.ToUInt16(Data, 0x116); set => BitConverter.GetBytes(value).CopyTo(Data, 0x116); } - public ushort ItemSet3Item { get => BitConverter.ToUInt16(Data, 0x118); set => BitConverter.GetBytes(value).CopyTo(Data, 0x118); } - public ushort ItemSet3Count { get => BitConverter.ToUInt16(Data, 0x11A); set => BitConverter.GetBytes(value).CopyTo(Data, 0x11A); } - public ushort ItemSet4Item { get => BitConverter.ToUInt16(Data, 0x11C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x11C); } - public ushort ItemSet4Count { get => BitConverter.ToUInt16(Data, 0x11E); set => BitConverter.GetBytes(value).CopyTo(Data, 0x11E); } - public ushort ItemSet5Item { get => BitConverter.ToUInt16(Data, 0x120); set => BitConverter.GetBytes(value).CopyTo(Data, 0x120); } // struct union overlaps OT Name data, beware! - public ushort ItemSet5Count { get => BitConverter.ToUInt16(Data, 0x122); set => BitConverter.GetBytes(value).CopyTo(Data, 0x122); } - public ushort ItemSet6Item { get => BitConverter.ToUInt16(Data, 0x124); set => BitConverter.GetBytes(value).CopyTo(Data, 0x124); } - public ushort ItemSet6Count { get => BitConverter.ToUInt16(Data, 0x126); set => BitConverter.GetBytes(value).CopyTo(Data, 0x126); } + public override int ItemID { get => ReadUInt16LittleEndian(Data.AsSpan(0x110)); set => WriteUInt16LittleEndian(Data.AsSpan(0x110), (ushort)value); } + public ushort ItemIDCount { get => ReadUInt16LittleEndian(Data.AsSpan(0x112)); set => WriteUInt16LittleEndian(Data.AsSpan(0x112), value); } + public ushort ItemSet2Item { get => ReadUInt16LittleEndian(Data.AsSpan(0x114)); set => WriteUInt16LittleEndian(Data.AsSpan(0x114), value); } + public ushort ItemSet2Count { get => ReadUInt16LittleEndian(Data.AsSpan(0x116)); set => WriteUInt16LittleEndian(Data.AsSpan(0x116), value); } + public ushort ItemSet3Item { get => ReadUInt16LittleEndian(Data.AsSpan(0x118)); set => WriteUInt16LittleEndian(Data.AsSpan(0x118), value); } + public ushort ItemSet3Count { get => ReadUInt16LittleEndian(Data.AsSpan(0x11A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x11A), value); } + public ushort ItemSet4Item { get => ReadUInt16LittleEndian(Data.AsSpan(0x11C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x11C), value); } + public ushort ItemSet4Count { get => ReadUInt16LittleEndian(Data.AsSpan(0x11E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x11E), value); } + public ushort ItemSet5Item { get => ReadUInt16LittleEndian(Data.AsSpan(0x120)); set => WriteUInt16LittleEndian(Data.AsSpan(0x120), value); } // struct union overlaps OT Name data, beware! + public ushort ItemSet5Count { get => ReadUInt16LittleEndian(Data.AsSpan(0x122)); set => WriteUInt16LittleEndian(Data.AsSpan(0x122), value); } + public ushort ItemSet6Item { get => ReadUInt16LittleEndian(Data.AsSpan(0x124)); set => WriteUInt16LittleEndian(Data.AsSpan(0x124), value); } + public ushort ItemSet6Count { get => ReadUInt16LittleEndian(Data.AsSpan(0x126)); set => WriteUInt16LittleEndian(Data.AsSpan(0x126), value); } public override int Gender { get; set; } public override int Form { get; set; } @@ -89,8 +90,8 @@ public override int Species public override string OT_Name { - get => StringConverter.GetString7b(Data, 0x120, 0x1A); - set => StringConverter.SetString7b(value, 12, 13).CopyTo(Data, 0x120 + 0xB6); // careful with length + get => StringConverter8.GetString(Data.AsSpan(0x120, 0x1A)); + set => StringConverter8.SetString(Data.AsSpan(0x120, 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); } public LanguageID LanguageReceived diff --git a/PKHeX.Core/PKM/BK4.cs b/PKHeX.Core/PKM/BK4.cs index 9862d0058..ae2ecf701 100644 --- a/PKHeX.Core/PKM/BK4.cs +++ b/PKHeX.Core/PKM/BK4.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -25,12 +27,12 @@ public sealed class BK4 : G4PKM public override bool Valid => ChecksumValid || (Sanity == 0 && Species <= MaxSpeciesID); - public static BK4 ReadUnshuffle(byte[] data) + public static BK4 ReadUnshuffle(ReadOnlySpan data) { - var PID = BigEndian.ToUInt32(data, 0); + var PID = ReadUInt32BigEndian(data); uint sv = ((PID & 0x3E000) >> 0xD) % 24; - var Data = PokeCrypto.ShuffleArray(data, sv, PokeCrypto.SIZE_4BLOCK); - var result = new BK4(Data); + var unshuffled = PokeCrypto.ShuffleArray(data, sv, PokeCrypto.SIZE_4BLOCK); + var result = new BK4(unshuffled); result.RefreshChecksum(); return result; } @@ -45,24 +47,21 @@ public BK4(byte[] data) : base(data) public override PKM Clone() => new BK4((byte[])Data.Clone()); - public string GetString(int offset, int count) => StringConverter4.GetBEString4(Data, offset, count); - private static byte[] SetString(string value, int maxLength) => StringConverter4.SetBEString4(value, maxLength); - // Structure - public override uint PID { get => BigEndian.ToUInt32(Data, 0x00); set => BigEndian.GetBytes(value).CopyTo(Data, 0x00); } - public override ushort Sanity { get => BigEndian.ToUInt16(Data, 0x04); set => BigEndian.GetBytes(value).CopyTo(Data, 0x04); } - public override ushort Checksum { get => BigEndian.ToUInt16(Data, 0x06); set => BigEndian.GetBytes(value).CopyTo(Data, 0x06); } + public override uint PID { get => ReadUInt16BigEndian(Data.AsSpan(0x00)); set => WriteUInt32BigEndian(Data.AsSpan(0x00), value); } + public override ushort Sanity { get => ReadUInt16BigEndian(Data.AsSpan(0x04)); set => WriteUInt32BigEndian(Data.AsSpan(0x04), value); } + public override ushort Checksum { get => ReadUInt16BigEndian(Data.AsSpan(0x06)); set => WriteUInt32BigEndian(Data.AsSpan(0x06), value); } #region Block A - public override int Species { get => BigEndian.ToUInt16(Data, 0x08); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x08); } - public override int HeldItem { get => BigEndian.ToUInt16(Data, 0x0A); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x0A); } - public override int SID { get => BigEndian.ToUInt16(Data, 0x0C); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x0C); } - public override int TID { get => BigEndian.ToUInt16(Data, 0x0E); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x0E); } + public override int Species { get => ReadUInt16BigEndian(Data.AsSpan(0x08)); set => WriteUInt16BigEndian(Data.AsSpan(0x08), (ushort)value); } + public override int HeldItem { get => ReadUInt16BigEndian(Data.AsSpan(0x0A)); set => WriteUInt16BigEndian(Data.AsSpan(0x0A), (ushort)value); } + public override int SID { get => ReadUInt16BigEndian(Data.AsSpan(0x0C)); set => WriteUInt16BigEndian(Data.AsSpan(0x0C), (ushort)value); } + public override int TID { get => ReadUInt16BigEndian(Data.AsSpan(0x0E)); set => WriteUInt16BigEndian(Data.AsSpan(0x0E), (ushort)value); } public override uint EXP { - get => BigEndian.ToUInt32(Data, 0x10); - set => BigEndian.GetBytes(value).CopyTo(Data, 0x10); + get => ReadUInt32BigEndian(Data.AsSpan(0x10)); + set => WriteUInt32BigEndian(Data.AsSpan(0x10), value); } public override int OT_Friendship { get => Data[0x14]; set => Data[0x14] = (byte)value; } @@ -121,10 +120,10 @@ public override uint EXP #endregion #region Block B - public override int Move1 { get => BigEndian.ToUInt16(Data, 0x28); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x28); } - public override int Move2 { get => BigEndian.ToUInt16(Data, 0x2A); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x2A); } - public override int Move3 { get => BigEndian.ToUInt16(Data, 0x2C); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x2C); } - public override int Move4 { get => BigEndian.ToUInt16(Data, 0x2E); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x2E); } + public override int Move1 { get => ReadUInt16BigEndian(Data.AsSpan(0x28)); set => WriteUInt16BigEndian(Data.AsSpan(0x28), (ushort)value); } + public override int Move2 { get => ReadUInt16BigEndian(Data.AsSpan(0x2A)); set => WriteUInt16BigEndian(Data.AsSpan(0x2A), (ushort)value); } + public override int Move3 { get => ReadUInt16BigEndian(Data.AsSpan(0x2C)); set => WriteUInt16BigEndian(Data.AsSpan(0x2C), (ushort)value); } + public override int Move4 { get => ReadUInt16BigEndian(Data.AsSpan(0x2E)); set => WriteUInt16BigEndian(Data.AsSpan(0x2E), (ushort)value); } public override int Move1_PP { get => Data[0x30]; set => Data[0x30] = (byte)value; } public override int Move2_PP { get => Data[0x31]; set => Data[0x31] = (byte)value; } public override int Move3_PP { get => Data[0x32]; set => Data[0x32] = (byte)value; } @@ -133,7 +132,7 @@ public override uint EXP public override int Move2_PPUps { get => Data[0x35]; set => Data[0x35] = (byte)value; } public override int Move3_PPUps { get => Data[0x36]; set => Data[0x36] = (byte)value; } public override int Move4_PPUps { get => Data[0x37]; set => Data[0x37] = (byte)value; } - private uint IV32 { get => BigEndian.ToUInt32(Data, 0x38); set => BigEndian.GetBytes(value).CopyTo(Data, 0x38); } + private uint IV32 { get => ReadUInt32BigEndian(Data.AsSpan(0x38)); set => WriteUInt32BigEndian(Data.AsSpan(0x38), value); } public override int IV_SPD { get => (int)(IV32 >> 02) & 0x1F; set => IV32 = ((IV32 & ~(0x1Fu << 02)) | ((value > 31 ? 31u : (uint)value) << 02)); } public override int IV_SPA { get => (int)(IV32 >> 07) & 0x1F; set => IV32 = ((IV32 & ~(0x1Fu << 07)) | ((value > 31 ? 31u : (uint)value) << 07)); } public override int IV_SPE { get => (int)(IV32 >> 12) & 0x1F; set => IV32 = ((IV32 & ~(0x1Fu << 12)) | ((value > 31 ? 31u : (uint)value) << 12)); } @@ -184,11 +183,23 @@ public override uint EXP public override int Gender { get => (Data[0x40] >> 5) & 0x3; set => Data[0x40] = (byte)((Data[0x40] & ~0x60) | ((value & 3) << 5)); } public override int Form { get => Data[0x40] & 0x1F; set => Data[0x40] = (byte)((Data[0x40] & ~0x1F) | (value & 0x1F)); } public override int ShinyLeaf { get => Data[0x41]; set => Data[0x41] = (byte)value; } - // 0x43-0x47 Unused + + // 0x42-0x43 Unused + public override ushort Egg_LocationExtended + { + get => ReadUInt16BigEndian(Data.AsSpan(0x44)); + set => WriteUInt16BigEndian(Data.AsSpan(0x44), value); + } + + public override ushort Met_LocationExtended + { + get => ReadUInt16BigEndian(Data.AsSpan(0x46)); + set => WriteUInt16BigEndian(Data.AsSpan(0x46), value); + } #endregion #region Block C - public override string Nickname { get => GetString(0x48, 20); set => SetString(value, 10).CopyTo(Data, 0x48); } + public override string Nickname { get => StringConverter4GC.GetString(Nickname_Trash); set => StringConverter4GC.SetString(Nickname_Trash, value.AsSpan(), 10, StringConverterOption.None); } // 0x5E unused public override int Version { get => Data[0x5F]; set => Data[0x5F] = (byte)value; } private byte RIB8 { get => Data[0x60]; set => Data[0x60] = value; } // Sinnoh 3 @@ -231,7 +242,7 @@ public override uint EXP #endregion #region Block D - public override string OT_Name { get => GetString(0x68, 14); set => SetString(value, 7).CopyTo(Data, 0x68); } + public override string OT_Name { get => StringConverter4GC.GetString(OT_Trash); set => StringConverter4GC.SetString(OT_Trash, value.AsSpan(), 7, StringConverterOption.None); } public override int Egg_Year { get => Data[0x78]; set => Data[0x78] = (byte)value; } public override int Egg_Month { get => Data[0x79]; set => Data[0x79] = (byte)value; } public override int Egg_Day { get => Data[0x7A]; set => Data[0x7A] = (byte)value; } @@ -239,66 +250,15 @@ public override uint EXP public override int Met_Month { get => Data[0x7C]; set => Data[0x7C] = (byte)value; } public override int Met_Day { get => Data[0x7D]; set => Data[0x7D] = (byte)value; } - public override int Egg_Location + public override ushort Egg_LocationDP { - get - { - ushort hgssloc = BigEndian.ToUInt16(Data, 0x44); - if (hgssloc != 0) - return hgssloc; - return BigEndian.ToUInt16(Data, 0x7E); - } - set - { - if (value == 0) - { - BigEndian.GetBytes((ushort)0).CopyTo(Data, 0x44); - BigEndian.GetBytes((ushort)0).CopyTo(Data, 0x7E); - } - else if (Locations.IsPtHGSSLocation(value) || Locations.IsPtHGSSLocationEgg(value)) - { - // Met location not in DP, set to Faraway Place - BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x44); - BigEndian.GetBytes((ushort)0xBBA).CopyTo(Data, 0x7E); - } - else - { - int pthgss = PtHGSS ? value : 0; // only set to PtHGSS loc if encountered in game - BigEndian.GetBytes((ushort)pthgss).CopyTo(Data, 0x44); - BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x7E); - } - } + get => ReadUInt16BigEndian(Data.AsSpan(0x7E)); + set => WriteUInt16BigEndian(Data.AsSpan(0x7E), value); } - - public override int Met_Location + public override ushort Met_LocationDP { - get - { - ushort hgssloc = BigEndian.ToUInt16(Data, 0x46); - if (hgssloc != 0) - return hgssloc; - return BigEndian.ToUInt16(Data, 0x80); - } - set - { - if (value == 0) - { - BigEndian.GetBytes((ushort)0).CopyTo(Data, 0x46); - BigEndian.GetBytes((ushort)0).CopyTo(Data, 0x80); - } - else if (Locations.IsPtHGSSLocation(value) || Locations.IsPtHGSSLocationEgg(value)) - { - // Met location not in DP, set to Faraway Place - BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x46); - BigEndian.GetBytes((ushort)0xBBA).CopyTo(Data, 0x80); - } - else - { - int pthgss = PtHGSS ? value : 0; // only set to PtHGSS loc if encountered in game - BigEndian.GetBytes((ushort)pthgss).CopyTo(Data, 0x46); - BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x80); - } - } + get => ReadUInt16BigEndian(Data.AsSpan(0x80)); + set => WriteUInt16BigEndian(Data.AsSpan(0x80), value); } private byte PKRS { get => Data[0x82]; set => Data[0x82] = value; } @@ -326,9 +286,10 @@ public override int Met_Location // Methods protected override ushort CalculateChecksum() { + ReadOnlySpan arr = Data.AsSpan(); ushort chk = 0; for (int i = 8; i < PokeCrypto.SIZE_4STORED; i += 2) - chk += BigEndian.ToUInt16(Data, i); + chk += ReadUInt16BigEndian(arr[i..]); return chk; } diff --git a/PKHeX.Core/PKM/CK3.cs b/PKHeX.Core/PKM/CK3.cs index 08e425cbe..36b4e690f 100644 --- a/PKHeX.Core/PKM/CK3.cs +++ b/PKHeX.Core/PKM/CK3.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -27,38 +28,30 @@ public sealed class CK3 : G3PKM, IShadowPKM public CK3() : this(new byte[PokeCrypto.SIZE_3CSTORED]) { } public override PKM Clone() => new CK3((byte[])Data.Clone()); - private string GetString(int offset, int count) => StringConverter3.GetBEString3(Data, offset, count); - private static byte[] SetString(string value, int maxLength) => StringConverter3.SetBEString3(value, maxLength); - // Trash Bytes - public override Span Nickname_Trash { get => Data.AsSpan(0x2E, 20); set { if (value.Length == 20) value.CopyTo(Data.AsSpan(0x2E)); } } - public override Span OT_Trash { get => Data.AsSpan(0x18, 20); set { if (value.Length == 20) value.CopyTo(Data.AsSpan(0x18)); } } + public override Span OT_Trash => Data.AsSpan(0x18, 22); + public override Span Nickname_Trash => Data.AsSpan(0x2E, 22); + public Span NicknameCopy_Trash => Data.AsSpan(0x44, 22); // Future Attributes - // Silly Attributes - public override ushort Sanity { get => 0; set { } } // valid flag set in pkm structure. - public override ushort Checksum { get => Checksums.CRC16_CCITT(Data); set { } } // totally false, just a way to get a 'random' ident for the pkm. - public override bool ChecksumValid => true; - public override bool Valid { get => !Invalid; set => Invalid = !value; } - - public override int Species { get => SpeciesConverter.GetG4Species(BigEndian.ToUInt16(Data, 0x00)); set => BigEndian.GetBytes((ushort)SpeciesConverter.GetG3Species(value)).CopyTo(Data, 0x00); } + public override int Species { get => SpeciesConverter.GetG4Species(ReadUInt16BigEndian(Data.AsSpan(0x00))); set => WriteUInt16BigEndian(Data, (ushort)SpeciesConverter.GetG3Species(value)); } // 02-04 unused - public override uint PID { get => BigEndian.ToUInt32(Data, 0x04); set => BigEndian.GetBytes(value).CopyTo(Data, 0x04); } + public override uint PID { get => ReadUInt32BigEndian(Data.AsSpan(0x04)); set => WriteUInt32BigEndian(Data.AsSpan(0x04), value); } public override int Version { get => GetGBAVersionID(Data[0x08]); set => Data[0x08] = GetGCVersionID(value); } public int CurrentRegion { get => Data[0x09]; set => Data[0x09] = (byte)value; } public int OriginalRegion { get => Data[0x0A]; set => Data[0x0A] = (byte)value; } public override int Language { get => Core.Language.GetMainLangIDfromGC(Data[0x0B]); set => Data[0x0B] = Core.Language.GetGCLangIDfromMain((byte)value); } - public override int Met_Location { get => BigEndian.ToUInt16(Data, 0x0C); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x0C); } + public override int Met_Location { get => ReadUInt16BigEndian(Data.AsSpan(0x0C)); set => WriteUInt16BigEndian(Data.AsSpan(0x0C), (ushort)value); } public override int Met_Level { get => Data[0x0E]; set => Data[0x0E] = (byte)value; } public override int Ball { get => Data[0x0F]; set => Data[0x0F] = (byte)value; } public override int OT_Gender { get => Data[0x10]; set => Data[0x10] = (byte)value; } - public override int SID { get => BigEndian.ToUInt16(Data, 0x14); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x14); } - public override int TID { get => BigEndian.ToUInt16(Data, 0x16); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x16); } - public override string OT_Name { get => GetString(0x18, 20); set => SetString(value, 10).CopyTo(Data, 0x18); } // +2 terminator - public override string Nickname { get => GetString(0x2E, 20); set { SetString(value, 10).CopyTo(Data, 0x2E); NicknameCopy = value; } } // +2 terminator - public string NicknameCopy { get => GetString(0x44, 20); set => SetString(value, 10).CopyTo(Data, 0x44); } // +2 terminator - public override uint EXP { get => BigEndian.ToUInt32(Data, 0x5C); set => BigEndian.GetBytes(value).CopyTo(Data, 0x5C); } + public override int SID { get => ReadUInt16BigEndian(Data.AsSpan(0x14)); set => WriteUInt16BigEndian(Data.AsSpan(0x14), (ushort)value); } + public override int TID { get => ReadUInt16BigEndian(Data.AsSpan(0x16)); set => WriteUInt16BigEndian(Data.AsSpan(0x16), (ushort)value); } + public override string OT_Name { get => StringConverter3GC.GetString(OT_Trash); set => StringConverter3GC.SetString(OT_Trash, value.AsSpan(), 10, StringConverterOption.None); } + public override string Nickname { get => StringConverter3GC.GetString(Nickname_Trash); set { StringConverter3GC.SetString(Nickname_Trash, value.AsSpan(), 10, StringConverterOption.None); NicknameCopy = value; } } + public string NicknameCopy { get => StringConverter3GC.GetString(NicknameCopy_Trash); set => StringConverter3GC.SetString(NicknameCopy_Trash, value.AsSpan(), 10, StringConverterOption.None); } + public override uint EXP { get => ReadUInt32BigEndian(Data.AsSpan(0x5C)); set => WriteUInt32BigEndian(Data.AsSpan(0x5C), value); } public override int Stat_Level { get => Data[0x60]; set => Data[0x60] = (byte)value; } // 0x64-0x77 are battle/status related @@ -66,80 +59,80 @@ public sealed class CK3 : G3PKM, IShadowPKM // Not that the program cares // Moves - public override int Move1 { get => BigEndian.ToUInt16(Data, 0x78); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x78); } + public override int Move1 { get => ReadUInt16BigEndian(Data.AsSpan(0x78)); set => WriteUInt16BigEndian(Data.AsSpan(0x78), (ushort)value); } public override int Move1_PP { get => Data[0x7A]; set => Data[0x7A] = (byte)value; } public override int Move1_PPUps { get => Data[0x7B]; set => Data[0x7B] = (byte)value; } - public override int Move2 { get => BigEndian.ToUInt16(Data, 0x7C); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x7C); } + public override int Move2 { get => ReadUInt16BigEndian(Data.AsSpan(0x7C)); set => WriteUInt16BigEndian(Data.AsSpan(0x7C), (ushort)value); } public override int Move2_PP { get => Data[0x7E]; set => Data[0x7E] = (byte)value; } public override int Move2_PPUps { get => Data[0x7F]; set => Data[0x7F] = (byte)value; } - public override int Move3 { get => BigEndian.ToUInt16(Data, 0x80); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x80); } + public override int Move3 { get => ReadUInt16BigEndian(Data.AsSpan(0x80)); set => WriteUInt16BigEndian(Data.AsSpan(0x80), (ushort)value); } public override int Move3_PP { get => Data[0x82]; set => Data[0x82] = (byte)value; } public override int Move3_PPUps { get => Data[0x83]; set => Data[0x83] = (byte)value; } - public override int Move4 { get => BigEndian.ToUInt16(Data, 0x84); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x84); } + public override int Move4 { get => ReadUInt16BigEndian(Data.AsSpan(0x84)); set => WriteUInt16BigEndian(Data.AsSpan(0x84), (ushort)value); } public override int Move4_PP { get => Data[0x86]; set => Data[0x86] = (byte)value; } public override int Move4_PPUps { get => Data[0x87]; set => Data[0x87] = (byte)value; } public override int SpriteItem => ItemConverter.GetItemFuture3((ushort)HeldItem); - public override int HeldItem { get => BigEndian.ToUInt16(Data, 0x88); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x88); } + public override int HeldItem { get => ReadUInt16BigEndian(Data.AsSpan(0x88)); set => WriteUInt16BigEndian(Data.AsSpan(0x88), (ushort)value); } // More party stats - public override int Stat_HPCurrent { get => BigEndian.ToUInt16(Data, 0x8A); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x8A); } - public override int Stat_HPMax { get => BigEndian.ToUInt16(Data, 0x8C); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x8C); } - public override int Stat_ATK { get => BigEndian.ToUInt16(Data, 0x8E); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x8E); } - public override int Stat_DEF { get => BigEndian.ToUInt16(Data, 0x90); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x90); } - public override int Stat_SPA { get => BigEndian.ToUInt16(Data, 0x92); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x92); } - public override int Stat_SPD { get => BigEndian.ToUInt16(Data, 0x94); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x94); } - public override int Stat_SPE { get => BigEndian.ToUInt16(Data, 0x96); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x96); } + public override int Stat_HPCurrent { get => ReadUInt16BigEndian(Data.AsSpan(0x8A)); set => WriteUInt16BigEndian(Data.AsSpan(0x8A), (ushort)value); } + public override int Stat_HPMax { get => ReadUInt16BigEndian(Data.AsSpan(0x8C)); set => WriteUInt16BigEndian(Data.AsSpan(0x8C), (ushort)value); } + public override int Stat_ATK { get => ReadUInt16BigEndian(Data.AsSpan(0x8E)); set => WriteUInt16BigEndian(Data.AsSpan(0x8E), (ushort)value); } + public override int Stat_DEF { get => ReadUInt16BigEndian(Data.AsSpan(0x90)); set => WriteUInt16BigEndian(Data.AsSpan(0x90), (ushort)value); } + public override int Stat_SPA { get => ReadUInt16BigEndian(Data.AsSpan(0x92)); set => WriteUInt16BigEndian(Data.AsSpan(0x92), (ushort)value); } + public override int Stat_SPD { get => ReadUInt16BigEndian(Data.AsSpan(0x94)); set => WriteUInt16BigEndian(Data.AsSpan(0x94), (ushort)value); } + public override int Stat_SPE { get => ReadUInt16BigEndian(Data.AsSpan(0x96)); set => WriteUInt16BigEndian(Data.AsSpan(0x96), (ushort)value); } // EVs public override int EV_HP { - get => Math.Min(byte.MaxValue, BigEndian.ToUInt16(Data, 0x98)); - set => BigEndian.GetBytes((ushort)(value & 0xFF)).CopyTo(Data, 0x98); } + get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0x98))); + set => WriteUInt16BigEndian(Data.AsSpan(0x98), (ushort)(value & 0xFF)); } public override int EV_ATK { - get => Math.Min(byte.MaxValue, BigEndian.ToUInt16(Data, 0x9A)); - set => BigEndian.GetBytes((ushort)(value & 0xFF)).CopyTo(Data, 0x9A); } + get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0x9A))); + set => WriteUInt16BigEndian(Data.AsSpan(0x9A), (ushort)(value & 0xFF)); } public override int EV_DEF { - get => Math.Min(byte.MaxValue, BigEndian.ToUInt16(Data, 0x9C)); - set => BigEndian.GetBytes((ushort)(value & 0xFF)).CopyTo(Data, 0x9C); } + get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0x9C))); + set => WriteUInt16BigEndian(Data.AsSpan(0x9C), (ushort)(value & 0xFF)); } public override int EV_SPA { - get => Math.Min(byte.MaxValue, BigEndian.ToUInt16(Data, 0x9E)); - set => BigEndian.GetBytes((ushort)(value & 0xFF)).CopyTo(Data, 0x9E); } + get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0x9E))); + set => WriteUInt16BigEndian(Data.AsSpan(0x9E), (ushort)(value & 0xFF)); } public override int EV_SPD { - get => Math.Min(byte.MaxValue, BigEndian.ToUInt16(Data, 0xA0)); - set => BigEndian.GetBytes((ushort)(value & 0xFF)).CopyTo(Data, 0xA0); } + get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0xA0))); + set => WriteUInt16BigEndian(Data.AsSpan(0xA0), (ushort)(value & 0xFF)); } public override int EV_SPE { - get => Math.Min(byte.MaxValue, BigEndian.ToUInt16(Data, 0xA2)); - set => BigEndian.GetBytes((ushort)(value & 0xFF)).CopyTo(Data, 0xA2); } + get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0xA2))); + set => WriteUInt16BigEndian(Data.AsSpan(0xA2), (ushort)(value & 0xFF)); } // IVs public override int IV_HP { - get => Math.Min((ushort)31, BigEndian.ToUInt16(Data, 0xA4)); - set => BigEndian.GetBytes((ushort)(value & 0x1F)).CopyTo(Data, 0xA4); } + get => Math.Min((ushort)31, ReadUInt16BigEndian(Data.AsSpan(0xA4))); + set => WriteUInt16BigEndian(Data.AsSpan(0xA4), (ushort)(value & 0x1F)); } public override int IV_ATK { - get => Math.Min((ushort)31, BigEndian.ToUInt16(Data, 0xA6)); - set => BigEndian.GetBytes((ushort)(value & 0x1F)).CopyTo(Data, 0xA6); } + get => Math.Min((ushort)31, ReadUInt16BigEndian(Data.AsSpan(0xA6))); + set => WriteUInt16BigEndian(Data.AsSpan(0xA6), (ushort)(value & 0x1F)); } public override int IV_DEF { - get => Math.Min((ushort)31, BigEndian.ToUInt16(Data, 0xA8)); - set => BigEndian.GetBytes((ushort)(value & 0x1F)).CopyTo(Data, 0xA8); } + get => Math.Min((ushort)31, ReadUInt16BigEndian(Data.AsSpan(0xA8))); + set => WriteUInt16BigEndian(Data.AsSpan(0xA8), (ushort)(value & 0x1F)); } public override int IV_SPA { - get => Math.Min((ushort)31, BigEndian.ToUInt16(Data, 0xAA)); - set => BigEndian.GetBytes((ushort)(value & 0x1F)).CopyTo(Data, 0xAA); } + get => Math.Min((ushort)31, ReadUInt16BigEndian(Data.AsSpan(0xAA))); + set => WriteUInt16BigEndian(Data.AsSpan(0xAA), (ushort)(value & 0x1F)); } public override int IV_SPD { - get => Math.Min((ushort)31, BigEndian.ToUInt16(Data, 0xAC)); - set => BigEndian.GetBytes((ushort)(value & 0x1F)).CopyTo(Data, 0xAC); } + get => Math.Min((ushort)31, ReadUInt16BigEndian(Data.AsSpan(0xAC))); + set => WriteUInt16BigEndian(Data.AsSpan(0xAC), (ushort)(value & 0x1F)); } public override int IV_SPE { - get => Math.Min((ushort)31, BigEndian.ToUInt16(Data, 0xAE)); - set => BigEndian.GetBytes((ushort)(value & 0x1F)).CopyTo(Data, 0xAE); } + get => Math.Min((ushort)31, ReadUInt16BigEndian(Data.AsSpan(0xAE))); + set => WriteUInt16BigEndian(Data.AsSpan(0xAE), (ushort)(value & 0x1F)); } public override int OT_Friendship { get => Data[0xB0]; set => Data[0xB0] = (byte)value; } @@ -178,15 +171,15 @@ public sealed class CK3 : G3PKM, IShadowPKM public override int PKRS_Strain { get => Data[0xCA] & 0xF; set => Data[0xCA] = (byte)(value & 0xF); } public override bool IsEgg { get => Data[0xCB] == 1; set => Data[0xCB] = value ? (byte)1 : (byte)0; } public override bool AbilityBit { get => Data[0xCC] == 1; set => Data[0xCC] = value ? (byte)1 : (byte)0; } - private bool Invalid { get => Data[0xCD] != 0; set => Data[0xCD] = value ? (byte)1 : (byte)0; } + public override bool Valid { get => Data[0xCD] == 0; set => Data[0xCD] = !value ? (byte)1 : (byte)0; } public override int MarkValue { get => SwapBits(Data[0xCF], 1, 2); protected set => Data[0xCF] = (byte)SwapBits(value, 1, 2); } public override int PKRS_Days { get => Math.Max((sbyte)Data[0xD0], (sbyte)0); set => Data[0xD0] = (byte)(value == 0 ? 0xFF : value & 0xF); } public int PartySlot { get => Data[0xD7]; set => Data[0xD7] = (byte)value; } // or not; only really used while in party? - public int ShadowID { get => BigEndian.ToUInt16(Data, 0xD8); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0xD8); } - public int Purification { get => BigEndian.ToInt32(Data, 0xDC); set => BigEndian.GetBytes(value).CopyTo(Data, 0xDC); } - public uint EXP_Shadow { get => BigEndian.ToUInt32(Data, 0xC0); set => BigEndian.GetBytes(value).CopyTo(Data, 0xC0); } + public int ShadowID { get => ReadUInt16BigEndian(Data.AsSpan(0xD8)); set => WriteUInt16BigEndian(Data.AsSpan(0xD8), (ushort)value); } + public int Purification { get => ReadInt32BigEndian(Data.AsSpan(0xDC)); set => WriteInt32BigEndian(Data.AsSpan(0xDC), value); } + public uint EXP_Shadow { get => ReadUInt32BigEndian(Data.AsSpan(0xC0)); set => WriteUInt32BigEndian(Data.AsSpan(0xC0), value); } public const int Purified = -100; public bool IsShadow => ShadowID != 0 && Purification != Purified; diff --git a/PKHeX.Core/PKM/G8PKM.cs b/PKHeX.Core/PKM/G8PKM.cs index a7b62867e..7b1f49667 100644 --- a/PKHeX.Core/PKM/G8PKM.cs +++ b/PKHeX.Core/PKM/G8PKM.cs @@ -1,9 +1,10 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { /// Generation 8 format. - public abstract class G8PKM : PKM, + public abstract class G8PKM : PKM, ISanityChecksum, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMark8, IRibbonSetAffixed, IContestStats, IContestStatsMutable, IHyperTrain, IScaledSize, IGigantamax, IFavorite, IDynamaxLevel, IRibbonIndex, IHandlerLanguage, IFormArgument, IHomeTrack, IBattleVersion, ITrainerMemories { @@ -18,11 +19,11 @@ private static byte[] DecryptParty(byte[] data) return data; } - protected override ushort CalculateChecksum() + private ushort CalculateChecksum() { ushort chk = 0; for (int i = 8; i < PokeCrypto.SIZE_8STORED; i += 2) - chk += BitConverter.ToUInt16(Data, i); + chk += ReadUInt16LittleEndian(Data.AsSpan(i)); return chk; } @@ -33,16 +34,17 @@ public override int CurrentFriendship set { if (CurrentHandler == 0) OT_Friendship = value; else HT_Friendship = value; } } - private string GetString(int offset, int count) => StringConverter.GetString7b(Data, offset, count); - private static byte[] SetString(string value, int maxLength) => StringConverter.SetString7b(value, maxLength); - public override int SIZE_PARTY => PokeCrypto.SIZE_8PARTY; public override int SIZE_STORED => PokeCrypto.SIZE_8STORED; + public sealed override bool ChecksumValid => CalculateChecksum() == Checksum; + public sealed override void RefreshChecksum() => Checksum = CalculateChecksum(); + public sealed override bool Valid { get => Sanity == 0 && ChecksumValid; set { if (!value) return; Sanity = 0; RefreshChecksum(); } } + // Trash Bytes - public override Span Nickname_Trash { get => Data.AsSpan(0x58, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0x58)); } } - public override Span HT_Trash { get => Data.AsSpan(0xA8, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0xA8)); } } - public override Span OT_Trash { get => Data.AsSpan(0xF8, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0xF8)); } } + public override Span Nickname_Trash => Data.AsSpan(0x58, 26); + public override Span HT_Trash => Data.AsSpan(0xA8, 26); + public override Span OT_Trash => Data.AsSpan(0xF8, 26); // Maximums public override int MaxIV => 31; @@ -104,26 +106,26 @@ public void FixRelearn() } } - public override uint EncryptionConstant { get => BitConverter.ToUInt32(Data, 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); } - public override ushort Sanity { get => BitConverter.ToUInt16(Data, 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); } - public override ushort Checksum { get => BitConverter.ToUInt16(Data, 0x06); set => BitConverter.GetBytes(value).CopyTo(Data, 0x06); } + public override uint EncryptionConstant { get => ReadUInt32LittleEndian(Data.AsSpan(0x00)); set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value); } + public ushort Sanity { get => ReadUInt16LittleEndian(Data.AsSpan(0x04)); set => WriteUInt16LittleEndian(Data.AsSpan(0x04), value); } + public ushort Checksum { get => ReadUInt16LittleEndian(Data.AsSpan(0x06)); set => WriteUInt16LittleEndian(Data.AsSpan(0x06), value); } // Structure #region Block A - public override int Species { get => BitConverter.ToUInt16(Data, 0x08); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08); } - public override int HeldItem { get => BitConverter.ToUInt16(Data, 0x0A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); } - public override int TID { get => BitConverter.ToUInt16(Data, 0x0C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0C); } - public override int SID { get => BitConverter.ToUInt16(Data, 0x0E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0E); } - public override uint EXP { get => BitConverter.ToUInt32(Data, 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, 0x10); } - public override int Ability { get => BitConverter.ToUInt16(Data, 0x14); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14); } + public override int Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x08)); set => WriteUInt16LittleEndian(Data.AsSpan(0x08), (ushort)value); } + public override int HeldItem { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); } + public override int TID { get => ReadUInt16LittleEndian(Data.AsSpan(0x0C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0C), (ushort)value); } + public override int SID { get => ReadUInt16LittleEndian(Data.AsSpan(0x0E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0E), (ushort)value); } + public override uint EXP { get => ReadUInt32LittleEndian(Data.AsSpan(0x10)); set => WriteUInt32LittleEndian(Data.AsSpan(0x10), value); } + public override int Ability { get => ReadUInt16LittleEndian(Data.AsSpan(0x14)); set => WriteUInt16LittleEndian(Data.AsSpan(0x14), (ushort)value); } public override int AbilityNumber { get => Data[0x16] & 7; set => Data[0x16] = (byte)((Data[0x16] & ~7) | (value & 7)); } public bool Favorite { get => (Data[0x16] & 8) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~8) | ((value ? 1 : 0) << 3)); } // unused, was in LGPE but not in SWSH public bool CanGigantamax { get => (Data[0x16] & 16) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~16) | (value ? 16 : 0)); } // 0x17 alignment unused - public override int MarkValue { get => BitConverter.ToUInt16(Data, 0x18); protected set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x18); } + public override int MarkValue { get => ReadUInt16LittleEndian(Data.AsSpan(0x18)); protected set => WriteUInt16LittleEndian(Data.AsSpan(0x18), (ushort)value); } // 0x1A alignment unused // 0x1B alignment unused - public override uint PID { get => BitConverter.ToUInt32(Data, 0x1C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1C); } + public override uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x1C), value); } public override int Nature { get => Data[0x20]; set => Data[0x20] = (byte)value; } public override int StatNature { get => Data[0x21]; set => Data[0x21] = (byte)value; } public override bool FatefulEncounter { get => (Data[0x22] & 1) == 1; set => Data[0x22] = (byte)((Data[0x22] & ~0x01) | (value ? 1 : 0)); } @@ -131,7 +133,7 @@ public void FixRelearn() public override int Gender { get => (Data[0x22] >> 2) & 0x3; set => Data[0x22] = (byte)((Data[0x22] & 0xF3) | (value << 2)); } // 0x23 alignment unused - public override int Form { get => BitConverter.ToUInt16(Data, 0x24); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x24); } + public override int Form { get => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); } public override int EV_HP { get => Data[0x26]; set => Data[0x26] = (byte)value; } public override int EV_ATK { get => Data[0x27]; set => Data[0x27] = (byte)value; } public override int EV_DEF { get => Data[0x28]; set => Data[0x28] = (byte)value; } @@ -303,15 +305,15 @@ public void FixRelearn() public bool HasMark() { - var d = Data; - if ((BitConverter.ToUInt16(d, 0x3A) & 0xFFE0) != 0) + var d = Data.AsSpan(); + if ((ReadUInt16LittleEndian(d[0x3A..]) & 0xFFE0) != 0) return true; - if (BitConverter.ToUInt32(d, 0x40) != 0) + if (ReadUInt32LittleEndian(d[0x40..]) != 0) return true; return (d[0x44] & 3) != 0; } - public uint Sociability { get => BitConverter.ToUInt32(Data, 0x48); set => BitConverter.GetBytes(value).CopyTo(Data, 0x48); } + public uint Sociability { get => ReadUInt32LittleEndian(Data.AsSpan(0x48)); set => WriteUInt32LittleEndian(Data.AsSpan(0x48), value); } // 0x4C-0x4F unused @@ -324,16 +326,16 @@ public bool HasMark() #region Block B public override string Nickname { - get => GetString(0x58, 24); - set => SetString(value, 12).CopyTo(Data, 0x58); + get => StringConverter8.GetString(Nickname_Trash); + set => StringConverter8.SetString(Nickname_Trash, value.AsSpan(), 12, StringConverterOption.None); } // 2 bytes for \0, automatically handled above - public override int Move1 { get => BitConverter.ToUInt16(Data, 0x72); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x72); } - public override int Move2 { get => BitConverter.ToUInt16(Data, 0x74); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x74); } - public override int Move3 { get => BitConverter.ToUInt16(Data, 0x76); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x76); } - public override int Move4 { get => BitConverter.ToUInt16(Data, 0x78); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x78); } + public override int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x72)); set => WriteUInt16LittleEndian(Data.AsSpan(0x72), (ushort)value); } + public override int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x74)); set => WriteUInt16LittleEndian(Data.AsSpan(0x74), (ushort)value); } + public override int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x76)); set => WriteUInt16LittleEndian(Data.AsSpan(0x76), (ushort)value); } + public override int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x78)); set => WriteUInt16LittleEndian(Data.AsSpan(0x78), (ushort)value); } public override int Move1_PP { get => Data[0x7A]; set => Data[0x7A] = (byte)value; } public override int Move2_PP { get => Data[0x7B]; set => Data[0x7B] = (byte)value; } @@ -344,14 +346,14 @@ public override string Nickname public override int Move3_PPUps { get => Data[0x80]; set => Data[0x80] = (byte)value; } public override int Move4_PPUps { get => Data[0x81]; set => Data[0x81] = (byte)value; } - public override int RelearnMove1 { get => BitConverter.ToUInt16(Data, 0x82); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x82); } - public override int RelearnMove2 { get => BitConverter.ToUInt16(Data, 0x84); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x84); } - public override int RelearnMove3 { get => BitConverter.ToUInt16(Data, 0x86); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x86); } - public override int RelearnMove4 { get => BitConverter.ToUInt16(Data, 0x88); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x88); } + public override int RelearnMove1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x82)); set => WriteUInt16LittleEndian(Data.AsSpan(0x82), (ushort)value); } + public override int RelearnMove2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x84)); set => WriteUInt16LittleEndian(Data.AsSpan(0x84), (ushort)value); } + public override int RelearnMove3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x86)); set => WriteUInt16LittleEndian(Data.AsSpan(0x86), (ushort)value); } + public override int RelearnMove4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x88)); set => WriteUInt16LittleEndian(Data.AsSpan(0x88), (ushort)value); } - public override int Stat_HPCurrent { get => BitConverter.ToUInt16(Data, 0x8A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x8A); } + public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data.AsSpan(0x8A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x8A), (ushort)value); } - private uint IV32 { get => BitConverter.ToUInt32(Data, 0x8C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x8C); } + private uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x8C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x8C), value); } public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); } public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); } public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); } @@ -365,24 +367,29 @@ public override string Nickname // 0x90-0x93 unused - public override int Status_Condition { get => BitConverter.ToInt32(Data, 0x94); set => BitConverter.GetBytes(value).CopyTo(Data, 0x94); } - public int Unk98 { get => BitConverter.ToInt32(Data, 0x98); set => BitConverter.GetBytes(value).CopyTo(Data, 0x98); } + public override int Status_Condition { get => ReadInt32LittleEndian(Data.AsSpan(0x94)); set => WriteInt32LittleEndian(Data.AsSpan(0x94), value); } + public int Unk98 { get => ReadInt32LittleEndian(Data.AsSpan(0x98)); set => WriteInt32LittleEndian(Data.AsSpan(0x98), value); } // 0x9C-0xA7 unused #endregion #region Block C - public override string HT_Name { get => GetString(0xA8, 24); set => SetString(value, 12).CopyTo(Data, 0xA8); } + public override string HT_Name + { + get => StringConverter8.GetString(HT_Trash); + set => StringConverter8.SetString(HT_Trash, value.AsSpan(), 12, StringConverterOption.None); + } + public override int HT_Gender { get => Data[0xC2]; set => Data[0xC2] = (byte)value; } public int HT_Language { get => Data[0xC3]; set => Data[0xC3] = (byte)value; } public override int CurrentHandler { get => Data[0xC4]; set => Data[0xC4] = (byte)value; } // 0xC5 unused (alignment) - public int HT_TrainerID { get => BitConverter.ToUInt16(Data, 0xC6); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xC6); } // unused? + public int HT_TrainerID { get => ReadUInt16LittleEndian(Data.AsSpan(0xC6)); set => WriteUInt16LittleEndian(Data.AsSpan(0xC6), (ushort)value); } // unused? public override int HT_Friendship { get => Data[0xC8]; set => Data[0xC8] = (byte)value; } public int HT_Intensity { get => Data[0xC9]; set => Data[0xC9] = (byte)value; } public int HT_Memory { get => Data[0xCA]; set => Data[0xCA] = (byte)value; } public int HT_Feeling { get => Data[0xCB]; set => Data[0xCB] = (byte)value; } - public int HT_TextVar { get => BitConverter.ToUInt16(Data, 0xCC); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xCC); } + public int HT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0xCC)); set => WriteUInt16LittleEndian(Data.AsSpan(0xCC), (ushort)value); } // 0xCE-0xDB unused @@ -394,7 +401,7 @@ public override string Nickname // public override int ConsoleRegion { get => Data[0xE1]; set => Data[0xE1] = (byte)value; } public override int Language { get => Data[0xE2]; set => Data[0xE2] = (byte)value; } public int UnkE3 { get => Data[0xE3]; set => Data[0xE3] = (byte)value; } - public uint FormArgument { get => BitConverter.ToUInt32(Data, 0xE4); set => BitConverter.GetBytes(value).CopyTo(Data, 0xE4); } + public uint FormArgument { get => ReadUInt32LittleEndian(Data.AsSpan(0xE4)); set => WriteUInt32LittleEndian(Data.AsSpan(0xE4), value); } public byte FormArgumentRemain { get => (byte)FormArgument; set => FormArgument = (FormArgument & ~0xFFu) | value; } public byte FormArgumentElapsed { get => (byte)(FormArgument >> 8); set => FormArgument = (FormArgument & ~0xFF00u) | (uint)(value << 8); } public byte FormArgumentMaximum { get => (byte)(FormArgument >> 16); set => FormArgument = (FormArgument & ~0xFF0000u) | (uint)(value << 16); } @@ -403,12 +410,17 @@ public override string Nickname #endregion #region Block D - public override string OT_Name { get => GetString(0xF8, 24); set => SetString(value, 12).CopyTo(Data, 0xF8); } + public override string OT_Name + { + get => StringConverter8.GetString(OT_Trash); + set => StringConverter8.SetString(OT_Trash, value.AsSpan(), 12, StringConverterOption.None); + } + public override int OT_Friendship { get => Data[0x112]; set => Data[0x112] = (byte)value; } public int OT_Intensity { get => Data[0x113]; set => Data[0x113] = (byte)value; } public int OT_Memory { get => Data[0x114]; set => Data[0x114] = (byte)value; } // 0x115 unused align - public int OT_TextVar { get => BitConverter.ToUInt16(Data, 0x116); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x116); } + public int OT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0x116)); set => WriteUInt16LittleEndian(Data.AsSpan(0x116), (ushort)value); } public int OT_Feeling { get => Data[0x118]; set => Data[0x118] = (byte)value; } public override int Egg_Year { get => Data[0x119]; set => Data[0x119] = (byte)value; } public override int Egg_Month { get => Data[0x11A]; set => Data[0x11A] = (byte)value; } @@ -417,8 +429,8 @@ public override string Nickname public override int Met_Month { get => Data[0x11D]; set => Data[0x11D] = (byte)value; } public override int Met_Day { get => Data[0x11E]; set => Data[0x11E] = (byte)value; } // 0x11F unused align - public override int Egg_Location { get => BitConverter.ToUInt16(Data, 0x120); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x120); } - public override int Met_Location { get => BitConverter.ToUInt16(Data, 0x122); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x122); } + public override int Egg_Location { get => ReadUInt16LittleEndian(Data.AsSpan(0x120)); set => WriteUInt16LittleEndian(Data.AsSpan(0x120), (ushort)value); } + public override int Met_Location { get => ReadUInt16LittleEndian(Data.AsSpan(0x122)); set => WriteUInt16LittleEndian(Data.AsSpan(0x122), (ushort)value); } public override int Ball { get => Data[0x124]; set => Data[0x124] = (byte)value; } public override int Met_Level { get => Data[0x125] & ~0x80; set => Data[0x125] = (byte)((Data[0x125] & 0x80) | value); } public override int OT_Gender { get => Data[0x125] >> 7; set => Data[0x125] = (byte)((Data[0x125] & ~0x80) | (value << 7)); } @@ -451,20 +463,20 @@ public void SetMoveRecordFlag(int index, bool value) // Why did you mis-align this field, GameFreak? public ulong Tracker { - get => BitConverter.ToUInt64(Data, 0x135); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x135); + get => ReadUInt64LittleEndian(Data.AsSpan(0x135)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x135), value); } #endregion #region Battle Stats public override int Stat_Level { get => Data[0x148]; set => Data[0x148] = (byte)value; } // 0x149 unused alignment - public override int Stat_HPMax { get => BitConverter.ToUInt16(Data, 0x14A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14A); } - public override int Stat_ATK { get => BitConverter.ToUInt16(Data, 0x14C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14C); } - public override int Stat_DEF { get => BitConverter.ToUInt16(Data, 0x14E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14E); } - public override int Stat_SPE { get => BitConverter.ToUInt16(Data, 0x150); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x150); } - public override int Stat_SPA { get => BitConverter.ToUInt16(Data, 0x152); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x152); } - public override int Stat_SPD { get => BitConverter.ToUInt16(Data, 0x154); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x154); } + public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data.AsSpan(0x14A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x14A), (ushort)value); } + public override int Stat_ATK { get => ReadUInt16LittleEndian(Data.AsSpan(0x14C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x14C), (ushort)value); } + public override int Stat_DEF { get => ReadUInt16LittleEndian(Data.AsSpan(0x14E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x14E), (ushort)value); } + public override int Stat_SPE { get => ReadUInt16LittleEndian(Data.AsSpan(0x150)); set => WriteUInt16LittleEndian(Data.AsSpan(0x150), (ushort)value); } + public override int Stat_SPA { get => ReadUInt16LittleEndian(Data.AsSpan(0x152)); set => WriteUInt16LittleEndian(Data.AsSpan(0x152), (ushort)value); } + public override int Stat_SPD { get => ReadUInt16LittleEndian(Data.AsSpan(0x154)); set => WriteUInt16LittleEndian(Data.AsSpan(0x154), (ushort)value); } #endregion public override int[] Markings diff --git a/PKHeX.Core/PKM/PB7.cs b/PKHeX.Core/PKM/PB7.cs index 7667b9c7b..88ca60d31 100644 --- a/PKHeX.Core/PKM/PB7.cs +++ b/PKHeX.Core/PKM/PB7.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -41,68 +42,65 @@ private static byte[] DecryptParty(byte[] data) public override PKM Clone() => new PB7((byte[])Data.Clone()); - private string GetString(int offset, int count) => StringConverter.GetString7b(Data, offset, count); - private static byte[] SetString(string value, int maxLength) => StringConverter.SetString7b(value, maxLength); - // Structure #region Block A public override uint EncryptionConstant { - get => BitConverter.ToUInt32(Data, 0x00); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); + get => ReadUInt32LittleEndian(Data.AsSpan(0x00)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value); } public override ushort Sanity { - get => BitConverter.ToUInt16(Data, 0x04); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); + get => ReadUInt16LittleEndian(Data.AsSpan(0x04)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x04), value); } public override ushort Checksum { - get => BitConverter.ToUInt16(Data, 0x06); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x06); + get => ReadUInt16LittleEndian(Data.AsSpan(0x06)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x06), value); } public override int Species { - get => BitConverter.ToUInt16(Data, 0x08); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08); + get => ReadUInt16LittleEndian(Data.AsSpan(0x08)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x08), (ushort)value); } public override int HeldItem { - get => BitConverter.ToUInt16(Data, 0x0A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); + get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); } public override int TID { - get => BitConverter.ToUInt16(Data, 0x0C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0C); + get => ReadUInt16LittleEndian(Data.AsSpan(0x0C)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x0C), (ushort)value); } public override int SID { - get => BitConverter.ToUInt16(Data, 0x0E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0E); + get => ReadUInt16LittleEndian(Data.AsSpan(0x0E)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x0E), (ushort)value); } public override uint EXP { - get => BitConverter.ToUInt32(Data, 0x10); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x10); + get => ReadUInt32LittleEndian(Data.AsSpan(0x10)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x10), value); } public override int Ability { get => Data[0x14]; set => Data[0x14] = (byte)value; } public override int AbilityNumber { get => Data[0x15] & 7; set => Data[0x15] = (byte)((Data[0x15] & ~7) | (value & 7)); } public bool Favorite { get => (Data[0x15] & 8) != 0; set => Data[0x15] = (byte)((Data[0x15] & ~8) | ((value ? 1 : 0) << 3)); } - public override int MarkValue { get => BitConverter.ToUInt16(Data, 0x16); protected set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x16); } + public override int MarkValue { get => ReadUInt16LittleEndian(Data.AsSpan(0x16)); protected set => WriteUInt16LittleEndian(Data.AsSpan(0x16), (ushort)value); } public override uint PID { - get => BitConverter.ToUInt32(Data, 0x18); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x18); + get => ReadUInt32LittleEndian(Data.AsSpan(0x18)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x18), value); } public override int Nature { get => Data[0x1C]; set => Data[0x1C] = (byte)value; } @@ -125,12 +123,12 @@ public override uint PID private byte PKRS { get => Data[0x2B]; set => Data[0x2B] = value; } public override int PKRS_Days { get => PKRS & 0xF; set => PKRS = (byte)((PKRS & ~0xF) | value); } public override int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)((PKRS & 0xF) | value << 4); } - public float HeightAbsolute { get => BitConverter.ToSingle(Data, 0x2C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x2C); } + public float HeightAbsolute { get => ReadSingleLittleEndian(Data.AsSpan(0x2C)); set => WriteSingleLittleEndian(Data.AsSpan(0x2C), value); } // 0x38 Unused // 0x39 Unused public int HeightScalar { get => Data[0x3A]; set => Data[0x3A] = (byte)value; } public int WeightScalar { get => Data[0x3B]; set => Data[0x3B] = (byte)value; } - public uint FormArgument { get => BitConverter.ToUInt32(Data, 0x3C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x3C); } + public uint FormArgument { get => ReadUInt32LittleEndian(Data.AsSpan(0x3C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x3C), value); } public byte FormArgumentRemain { get => (byte)FormArgument; set => FormArgument = (FormArgument & ~0xFFu) | value; } public byte FormArgumentElapsed { get => (byte)(FormArgument >> 8); set => FormArgument = (FormArgument & ~0xFF00u) | (uint)(value << 8); } public byte FormArgumentMaximum { get => (byte)(FormArgument >> 16); set => FormArgument = (FormArgument & ~0xFF0000u) | (uint)(value << 16); } @@ -139,32 +137,32 @@ public override uint PID #region Block B public override string Nickname { - get => GetString(0x40, 24); - set => SetString(value, 12).CopyTo(Data, 0x40); + get => StringConverter8.GetString(Nickname_Trash); + set => StringConverter8.SetString(Nickname_Trash, value.AsSpan(), 12, StringConverterOption.None); } public override int Move1 { - get => BitConverter.ToUInt16(Data, 0x5A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5A); + get => ReadUInt16LittleEndian(Data.AsSpan(0x5A)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x5A), (ushort)value); } public override int Move2 { - get => BitConverter.ToUInt16(Data, 0x5C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5C); + get => ReadUInt16LittleEndian(Data.AsSpan(0x5C)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x5C), (ushort)value); } public override int Move3 { - get => BitConverter.ToUInt16(Data, 0x5E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5E); + get => ReadUInt16LittleEndian(Data.AsSpan(0x5E)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x5E), (ushort)value); } public override int Move4 { - get => BitConverter.ToUInt16(Data, 0x60); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x60); + get => ReadUInt16LittleEndian(Data.AsSpan(0x60)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x60), (ushort)value); } public override int Move1_PP { get => Data[0x62]; set => Data[0x62] = (byte)value; } @@ -178,31 +176,31 @@ public override int Move4 public override int RelearnMove1 { - get => BitConverter.ToUInt16(Data, 0x6A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6A); + get => ReadUInt16LittleEndian(Data.AsSpan(0x6A)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x6A), (ushort)value); } public override int RelearnMove2 { - get => BitConverter.ToUInt16(Data, 0x6C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6C); + get => ReadUInt16LittleEndian(Data.AsSpan(0x6C)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x6C), (ushort)value); } public override int RelearnMove3 { - get => BitConverter.ToUInt16(Data, 0x6E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6E); + get => ReadUInt16LittleEndian(Data.AsSpan(0x6E)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x6E), (ushort)value); } public override int RelearnMove4 { - get => BitConverter.ToUInt16(Data, 0x70); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x70); + get => ReadUInt16LittleEndian(Data.AsSpan(0x70)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x70), (ushort)value); } // 0x72 Unused // 0x73 Unused - private uint IV32 { get => BitConverter.ToUInt32(Data, 0x74); set => BitConverter.GetBytes(value).CopyTo(Data, 0x74); } + private uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x74)); set => WriteUInt32LittleEndian(Data.AsSpan(0x74), value); } public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); } public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); } public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); } @@ -213,7 +211,12 @@ public override int RelearnMove4 public override bool IsNicknamed { get => ((IV32 >> 31) & 1) == 1; set => IV32 = (IV32 & 0x7FFFFFFFu) | (value ? 0x80000000u : 0u); } #endregion #region Block C - public override string HT_Name { get => GetString(0x78, 24); set => SetString(value, 12).CopyTo(Data, 0x78); } + public override string HT_Name + { + get => StringConverter8.GetString(HT_Trash); + set => StringConverter8.SetString(HT_Trash, value.AsSpan(), 12, StringConverterOption.None); + } + public override int HT_Gender { get => Data[0x92]; set => Data[0x92] = (byte)value; } public override int CurrentHandler { get => Data[0x93]; set => Data[0x93] = (byte)value; } // 0x94 Unused @@ -236,7 +239,7 @@ public override int RelearnMove4 public int HT_Memory { get => Data[0xA5]; set => Data[0xA5] = (byte)value; } public int HT_Feeling { get => Data[0xA6]; set => Data[0xA6] = (byte)value; } // 0xA7 Unused - public int HT_TextVar { get => BitConverter.ToUInt16(Data, 0xA8); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xA8); } + public int HT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0xA8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xA8), (ushort)value); } // 0xAA Unused // 0xAB Unused public byte FieldEventFatigue1 { get => Data[0xAC]; set => Data[0xAC] = value; } @@ -245,7 +248,12 @@ public override int RelearnMove4 public override byte Enjoyment { get => Data[0xAF]; set => Data[0xAF] = value; } #endregion #region Block D - public override string OT_Name { get => GetString(0xB0, 24); set => SetString(value, 12).CopyTo(Data, 0xB0); } + public override string OT_Name + { + get => StringConverter8.GetString(OT_Trash); + set => StringConverter8.SetString(OT_Trash, value.AsSpan(), 12, StringConverterOption.None); + } + public override int OT_Friendship { get => Data[0xCA]; set => Data[0xCA] = (byte)value; } // 0xCB Unused // 0xCC Unused @@ -260,8 +268,8 @@ public override int RelearnMove4 public override int Met_Month { get => Data[0xD5]; set => Data[0xD5] = (byte)value; } public override int Met_Day { get => Data[0xD6]; set => Data[0xD6] = (byte)value; } public int Rank { get => Data[0xD7]; set => Data[0xD7] = (byte)value; } // unused but fetched for stat calcs, and set for trpoke data? - public override int Egg_Location { get => BitConverter.ToUInt16(Data, 0xD8); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xD8); } - public override int Met_Location { get => BitConverter.ToUInt16(Data, 0xDA); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xDA); } + public override int Egg_Location { get => ReadUInt16LittleEndian(Data.AsSpan(0xD8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xD8), (ushort)value); } + public override int Met_Location { get => ReadUInt16LittleEndian(Data.AsSpan(0xDA)); set => WriteUInt16LittleEndian(Data.AsSpan(0xDA), (ushort)value); } public override int Ball { get => Data[0xDC]; set => Data[0xDC] = (byte)value; } public override int Met_Level { get => Data[0xDD] & ~0x80; set => Data[0xDD] = (byte)((Data[0xDD] & 0x80) | value); } public override int OT_Gender { get => Data[0xDD] >> 7; set => Data[0xDD] = (byte)((Data[0xDD] & ~0x80) | (value << 7)); } @@ -277,22 +285,22 @@ public override int RelearnMove4 // 0xE1 Unused // 0xE2 Unused public override int Language { get => Data[0xE3]; set => Data[0xE3] = (byte)value; } - public float WeightAbsolute { get => BitConverter.ToSingle(Data, 0xE4); set => BitConverter.GetBytes(value).CopyTo(Data, 0xE4); } + public float WeightAbsolute { get => ReadSingleLittleEndian(Data.AsSpan(0xE4)); set => WriteSingleLittleEndian(Data.AsSpan(0xE4), value); } #endregion #region Battle Stats - public override int Status_Condition { get => BitConverter.ToInt32(Data, 0xE8); set => BitConverter.GetBytes(value).CopyTo(Data, 0xE8); } + public override int Status_Condition { get => ReadInt32LittleEndian(Data.AsSpan(0xE8)); set => WriteInt32LittleEndian(Data.AsSpan(0xE8), value); } public override int Stat_Level { get => Data[0xEC]; set => Data[0xEC] = (byte)value; } public byte DirtType { get => Data[0xED]; set => Data[0xED] = value; } public byte DirtLocation { get => Data[0xEE]; set => Data[0xEE] = value; } // 0xEF unused - public override int Stat_HPCurrent { get => BitConverter.ToUInt16(Data, 0xF0); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF0); } - public override int Stat_HPMax { get => BitConverter.ToUInt16(Data, 0xF2); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF2); } - public override int Stat_ATK { get => BitConverter.ToUInt16(Data, 0xF4); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF4); } - public override int Stat_DEF { get => BitConverter.ToUInt16(Data, 0xF6); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF6); } - public override int Stat_SPE { get => BitConverter.ToUInt16(Data, 0xF8); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF8); } - public override int Stat_SPA { get => BitConverter.ToUInt16(Data, 0xFA); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xFA); } - public override int Stat_SPD { get => BitConverter.ToUInt16(Data, 0xFC); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xFC); } - public int Stat_CP { get => BitConverter.ToUInt16(Data, 0xFE); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xFE); } + public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data.AsSpan(0xF0)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF0), (ushort)value); } + public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data.AsSpan(0xF2)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF2), (ushort)value); } + public override int Stat_ATK { get => ReadUInt16LittleEndian(Data.AsSpan(0xF4)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF4), (ushort)value); } + public override int Stat_DEF { get => ReadUInt16LittleEndian(Data.AsSpan(0xF6)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF6), (ushort)value); } + public override int Stat_SPE { get => ReadUInt16LittleEndian(Data.AsSpan(0xF8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF8), (ushort)value); } + public override int Stat_SPA { get => ReadUInt16LittleEndian(Data.AsSpan(0xFA)); set => WriteUInt16LittleEndian(Data.AsSpan(0xFA), (ushort)value); } + public override int Stat_SPD { get => ReadUInt16LittleEndian(Data.AsSpan(0xFC)); set => WriteUInt16LittleEndian(Data.AsSpan(0xFC), (ushort)value); } + public int Stat_CP { get => ReadUInt16LittleEndian(Data.AsSpan(0xFE)); set => WriteUInt16LittleEndian(Data.AsSpan(0xFE), (ushort)value); } public bool Stat_Mega { get => Data[0x100] != 0; set => Data[0x100] = value ? (byte)1 : (byte)0; } public int Stat_MegaForm { get => Data[0x101]; set => Data[0x101] = (byte)value; } // 102/103 unused diff --git a/PKHeX.Core/PKM/PK1.cs b/PKHeX.Core/PKM/PK1.cs index ce3605181..6bd8ba09d 100644 --- a/PKHeX.Core/PKM/PK1.cs +++ b/PKHeX.Core/PKM/PK1.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -26,18 +27,20 @@ private static byte[] EnsurePartySize(byte[] data) return data; } - public override PKM Clone() => new PK1((byte[])Data.Clone(), Japanese) + public override PKM Clone() { - OT_Trash = RawOT, - Nickname_Trash = RawNickname, - }; + var clone = new PK1((byte[])Data.Clone(), Japanese); + OT_Trash.CopyTo(clone.OT_Trash); + Nickname_Trash.CopyTo(clone.Nickname_Trash); + return clone; + } protected override byte[] Encrypt() => new PokeList1(this).Write(); #region Stored Attributes public byte SpeciesID1 { get => Data[0]; set => Data[0] = value; } // raw access public override int Species { get => SpeciesConverter.GetG1Species(SpeciesID1); set => SetSpeciesValues(value); } - public override int Stat_HPCurrent { get => BigEndian.ToUInt16(Data, 0x1); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x1); } + public override int Stat_HPCurrent { get => ReadUInt16BigEndian(Data.AsSpan(0x1)); set => WriteUInt16BigEndian(Data.AsSpan(0x1), (ushort)value); } public int Stat_LevelBox { get => Data[3]; set => Data[3] = (byte)value; } public override int Status_Condition { get => Data[4]; set => Data[4] = (byte)value; } public int Type_A { get => Data[5]; set => Data[5] = (byte)value; } @@ -47,14 +50,14 @@ public override PKM Clone() => new PK1((byte[])Data.Clone(), Japanese) public override int Move2 { get => Data[9]; set => Data[9] = (byte)value; } public override int Move3 { get => Data[10]; set => Data[10] = (byte)value; } public override int Move4 { get => Data[11]; set => Data[11] = (byte)value; } - public override int TID { get => BigEndian.ToUInt16(Data, 0xC); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0xC); } - public override uint EXP { get => BigEndian.ToUInt32(Data, 0xE) >> 8; set => Array.Copy(BigEndian.GetBytes(value << 8), 0, Data, 0xE, 3); } - public override int EV_HP { get => BigEndian.ToUInt16(Data, 0x11); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x11); } - public override int EV_ATK { get => BigEndian.ToUInt16(Data, 0x13); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x13); } - public override int EV_DEF { get => BigEndian.ToUInt16(Data, 0x15); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x15); } - public override int EV_SPE { get => BigEndian.ToUInt16(Data, 0x17); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x17); } - public override int EV_SPC { get => BigEndian.ToUInt16(Data, 0x19); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x19); } - public override ushort DV16 { get => BigEndian.ToUInt16(Data, 0x1B); set => BigEndian.GetBytes(value).CopyTo(Data, 0x1B); } + public override int TID { get => ReadUInt16BigEndian(Data.AsSpan(0xC)); set => WriteUInt16BigEndian(Data.AsSpan(0xC), (ushort)value); } + public override uint EXP { get => ReadUInt32BigEndian(Data.AsSpan(0xE)) >> 8; set => WriteUInt32BigEndian(Data.AsSpan(0xE), (value << 8) | Data[0x11]); } + public override int EV_HP { get => ReadUInt16BigEndian(Data.AsSpan(0x11)); set => WriteUInt16BigEndian(Data.AsSpan(0x11), (ushort)value); } + public override int EV_ATK { get => ReadUInt16BigEndian(Data.AsSpan(0x13)); set => WriteUInt16BigEndian(Data.AsSpan(0x13), (ushort)value); } + public override int EV_DEF { get => ReadUInt16BigEndian(Data.AsSpan(0x15)); set => WriteUInt16BigEndian(Data.AsSpan(0x15), (ushort)value); } + public override int EV_SPE { get => ReadUInt16BigEndian(Data.AsSpan(0x17)); set => WriteUInt16BigEndian(Data.AsSpan(0x17), (ushort)value); } + public override int EV_SPC { get => ReadUInt16BigEndian(Data.AsSpan(0x19)); set => WriteUInt16BigEndian(Data.AsSpan(0x19), (ushort)value); } + public override ushort DV16 { get => ReadUInt16BigEndian(Data.AsSpan(0x1B)); set => WriteUInt16BigEndian(Data.AsSpan(0x1B), value); } public override int Move1_PP { get => Data[0x1D] & 0x3F; set => Data[0x1D] = (byte)((Data[0x1D] & 0xC0) | Math.Min(63, value)); } public override int Move2_PP { get => Data[0x1E] & 0x3F; set => Data[0x1E] = (byte)((Data[0x1E] & 0xC0) | Math.Min(63, value)); } public override int Move3_PP { get => Data[0x1F] & 0x3F; set => Data[0x1F] = (byte)((Data[0x1F] & 0xC0) | Math.Min(63, value)); } @@ -67,11 +70,11 @@ public override PKM Clone() => new PK1((byte[])Data.Clone(), Japanese) #region Party Attributes public override int Stat_Level { get => Data[0x21]; set => Stat_LevelBox = Data[0x21] = (byte)value; } - public override int Stat_HPMax { get => BigEndian.ToUInt16(Data, 0x22); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x22); } - public override int Stat_ATK { get => BigEndian.ToUInt16(Data, 0x24); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x24); } - public override int Stat_DEF { get => BigEndian.ToUInt16(Data, 0x26); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x26); } - public override int Stat_SPE { get => BigEndian.ToUInt16(Data, 0x28); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x28); } - public int Stat_SPC { get => BigEndian.ToUInt16(Data, 0x2A); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x2A); } + public override int Stat_HPMax { get => ReadUInt16BigEndian(Data.AsSpan(0x22)); set => WriteUInt16BigEndian(Data.AsSpan(0x22), (ushort)value); } + public override int Stat_ATK { get => ReadUInt16BigEndian(Data.AsSpan(0x24)); set => WriteUInt16BigEndian(Data.AsSpan(0x24), (ushort)value); } + public override int Stat_DEF { get => ReadUInt16BigEndian(Data.AsSpan(0x26)); set => WriteUInt16BigEndian(Data.AsSpan(0x26), (ushort)value); } + public override int Stat_SPE { get => ReadUInt16BigEndian(Data.AsSpan(0x28)); set => WriteUInt16BigEndian(Data.AsSpan(0x28), (ushort)value); } + public int Stat_SPC { get => ReadUInt16BigEndian(Data.AsSpan(0x2A)); set => WriteUInt16BigEndian(Data.AsSpan(0x2A), (ushort)value); } // Leave SPA and SPD as alias for SPC public override int Stat_SPA { get => Stat_SPC; set => Stat_SPC = value; } public override int Stat_SPD { get => Stat_SPC; set { } } @@ -195,15 +198,15 @@ public PK7 ConvertToPK7() pk7.OT_Friendship = pk7.HT_Friendship = PersonalTable.SM[Species].BaseFriendship; // IVs - var new_ivs = new int[6]; + Span finalIVs = stackalloc int[6]; int flawless = Species == (int)Core.Species.Mew ? 5 : 3; var rnd = Util.Rand; - for (var i = 0; i < new_ivs.Length; i++) - new_ivs[i] = rnd.Next(32); + for (var i = 0; i < finalIVs.Length; i++) + finalIVs[i] = rnd.Next(32); for (var i = 0; i < flawless; i++) - new_ivs[i] = 31; - Util.Shuffle(new_ivs); - pk7.IVs = new_ivs; + finalIVs[i] = 31; + Util.Shuffle(finalIVs); + pk7.SetIVs(finalIVs); switch (IsShiny ? Shiny.Always : Shiny.Never) { diff --git a/PKHeX.Core/PKM/PK2.cs b/PKHeX.Core/PKM/PK2.cs index 98ef77bb6..3646b0d96 100644 --- a/PKHeX.Core/PKM/PK2.cs +++ b/PKHeX.Core/PKM/PK2.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -26,12 +27,13 @@ private static byte[] EnsurePartySize(byte[] data) return data; } - public override PKM Clone() => new PK2((byte[])Data.Clone(), Japanese) + public override PKM Clone() { - OT_Trash = RawOT, - Nickname_Trash = RawNickname, - IsEgg = IsEgg, - }; + var clone = new PK2((byte[])Data.Clone(), Japanese) { IsEgg = IsEgg }; + OT_Trash.CopyTo(clone.OT_Trash); + Nickname_Trash.CopyTo(clone.Nickname_Trash); + return clone; + } protected override byte[] Encrypt() => new PokeList2(this).Write(); @@ -43,14 +45,14 @@ public override PKM Clone() => new PK2((byte[])Data.Clone(), Japanese) public override int Move2 { get => Data[3]; set => Data[3] = (byte)value; } public override int Move3 { get => Data[4]; set => Data[4] = (byte)value; } public override int Move4 { get => Data[5]; set => Data[5] = (byte)value; } - public override int TID { get => BigEndian.ToUInt16(Data, 6); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 6); } - public override uint EXP { get => BigEndian.ToUInt32(Data, 8) >> 8; set => Array.Copy(BigEndian.GetBytes(value << 8), 0, Data, 8, 3); } - public override int EV_HP { get => BigEndian.ToUInt16(Data, 0xB); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0xB); } - public override int EV_ATK { get => BigEndian.ToUInt16(Data, 0xD); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0xD); } - public override int EV_DEF { get => BigEndian.ToUInt16(Data, 0xF); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0xF); } - public override int EV_SPE { get => BigEndian.ToUInt16(Data, 0x11); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x11); } - public override int EV_SPC { get => BigEndian.ToUInt16(Data, 0x13); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x13); } - public override ushort DV16 { get => BigEndian.ToUInt16(Data, 0x15); set => BigEndian.GetBytes(value).CopyTo(Data, 0x15); } + public override int TID { get => ReadUInt16BigEndian(Data.AsSpan(6)); set => WriteUInt16BigEndian(Data.AsSpan(6), (ushort)value); } + public override uint EXP { get => ReadUInt32BigEndian(Data.AsSpan(0x08)) >> 8; set => WriteUInt32BigEndian(Data.AsSpan(8), (value << 8) | Data[0xB]); } + public override int EV_HP { get => ReadUInt16BigEndian(Data.AsSpan(0x0B)); set => WriteUInt16BigEndian(Data.AsSpan(0xB), (ushort)value); } + public override int EV_ATK { get => ReadUInt16BigEndian(Data.AsSpan(0x0D)); set => WriteUInt16BigEndian(Data.AsSpan(0xD), (ushort)value); } + public override int EV_DEF { get => ReadUInt16BigEndian(Data.AsSpan(0x0F)); set => WriteUInt16BigEndian(Data.AsSpan(0xF), (ushort)value); } + public override int EV_SPE { get => ReadUInt16BigEndian(Data.AsSpan(0x11)); set => WriteUInt16BigEndian(Data.AsSpan(0x11), (ushort)value); } + public override int EV_SPC { get => ReadUInt16BigEndian(Data.AsSpan(0x13)); set => WriteUInt16BigEndian(Data.AsSpan(0x13), (ushort)value); } + public override ushort DV16 { get => ReadUInt16BigEndian(Data.AsSpan(0x15)); set => WriteUInt16BigEndian(Data.AsSpan(0x15), value); } public override int Move1_PP { get => Data[0x17] & 0x3F; set => Data[0x17] = (byte)((Data[0x17] & 0xC0) | Math.Min(63, value)); } public override int Move2_PP { get => Data[0x18] & 0x3F; set => Data[0x18] = (byte)((Data[0x18] & 0xC0) | Math.Min(63, value)); } public override int Move3_PP { get => Data[0x19] & 0x3F; set => Data[0x19] = (byte)((Data[0x19] & 0xC0) | Math.Min(63, value)); } @@ -64,7 +66,7 @@ public override PKM Clone() => new PK2((byte[])Data.Clone(), Japanese) public override int PKRS_Days { get => PKRS & 0xF; set => PKRS = (byte)((PKRS & ~0xF) | value); } public override int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)((PKRS & 0xF) | value << 4); } // Crystal only Caught Data - public int CaughtData { get => BigEndian.ToUInt16(Data, 0x1D); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x1D); } + public int CaughtData { get => ReadUInt16BigEndian(Data.AsSpan(0x1D)); set => WriteUInt16BigEndian(Data.AsSpan(0x1D), (ushort)value); } public int Met_TimeOfDay { get => (CaughtData >> 14) & 0x3; set => CaughtData = (CaughtData & 0x3FFF) | ((value & 0x3) << 14); } public override int Met_Level { get => (CaughtData >> 8) & 0x3F; set => CaughtData = (CaughtData & 0xC0FF) | ((value & 0x3F) << 8); } public override int OT_Gender { get => (CaughtData >> 7) & 1; set => CaughtData = (CaughtData & 0xFF7F) | ((value & 1) << 7); } @@ -81,13 +83,13 @@ public override int Stat_Level #region Party Attributes public override int Status_Condition { get => Data[0x20]; set => Data[0x20] = (byte)value; } - public override int Stat_HPCurrent { get => BigEndian.ToUInt16(Data, 0x22); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x22); } - public override int Stat_HPMax { get => BigEndian.ToUInt16(Data, 0x24); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x24); } - public override int Stat_ATK { get => BigEndian.ToUInt16(Data, 0x26); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x26); } - public override int Stat_DEF { get => BigEndian.ToUInt16(Data, 0x28); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x28); } - public override int Stat_SPE { get => BigEndian.ToUInt16(Data, 0x2A); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x2A); } - public override int Stat_SPA { get => BigEndian.ToUInt16(Data, 0x2C); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x2C); } - public override int Stat_SPD { get => BigEndian.ToUInt16(Data, 0x2E); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x2E); } + public override int Stat_HPCurrent { get => ReadUInt16BigEndian(Data.AsSpan(0x22)); set => WriteUInt16BigEndian(Data.AsSpan(0x22), (ushort)value); } + public override int Stat_HPMax { get => ReadUInt16BigEndian(Data.AsSpan(0x24)); set => WriteUInt16BigEndian(Data.AsSpan(0x24), (ushort)value); } + public override int Stat_ATK { get => ReadUInt16BigEndian(Data.AsSpan(0x26)); set => WriteUInt16BigEndian(Data.AsSpan(0x26), (ushort)value); } + public override int Stat_DEF { get => ReadUInt16BigEndian(Data.AsSpan(0x28)); set => WriteUInt16BigEndian(Data.AsSpan(0x28), (ushort)value); } + public override int Stat_SPE { get => ReadUInt16BigEndian(Data.AsSpan(0x2A)); set => WriteUInt16BigEndian(Data.AsSpan(0x2A), (ushort)value); } + public override int Stat_SPA { get => ReadUInt16BigEndian(Data.AsSpan(0x2C)); set => WriteUInt16BigEndian(Data.AsSpan(0x2C), (ushort)value); } + public override int Stat_SPD { get => ReadUInt16BigEndian(Data.AsSpan(0x2E)); set => WriteUInt16BigEndian(Data.AsSpan(0x2E), (ushort)value); } #endregion public override bool IsEgg { get; set; } @@ -119,8 +121,8 @@ public PK1 ConvertToPK1() pk1.Stat_Level = Stat_Level; } // Status = 0 - pk1.OT_Trash = RawOT; - pk1.Nickname_Trash = RawNickname; + OT_Trash.CopyTo(pk1.OT_Trash); + Nickname_Trash.CopyTo(pk1.OT_Trash); pk1.ClearInvalidMoves(); @@ -168,15 +170,15 @@ public PK7 ConvertToPK7() // IVs var special = Species is 151 or 251; - var new_ivs = new int[6]; + Span finalIVs = stackalloc int[6]; int flawless = special ? 5 : 3; var rnd = Util.Rand; - for (var i = 0; i < new_ivs.Length; i++) - new_ivs[i] = rnd.Next(32); + for (var i = 0; i < finalIVs.Length; i++) + finalIVs[i] = rnd.Next(32); for (var i = 0; i < flawless; i++) - new_ivs[i] = 31; - Util.Shuffle(new_ivs); - pk7.IVs = new_ivs; + finalIVs[i] = 31; + Util.Shuffle(finalIVs); + pk7.SetIVs(finalIVs); switch (IsShiny ? Shiny.Always : Shiny.Never) { diff --git a/PKHeX.Core/PKM/PK3.cs b/PKHeX.Core/PKM/PK3.cs index 356c8f07f..836a3ca3c 100644 --- a/PKHeX.Core/PKM/PK3.cs +++ b/PKHeX.Core/PKM/PK3.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { /// Generation 3 format. - public sealed class PK3 : G3PKM + public sealed class PK3 : G3PKM, ISanityChecksum { private static readonly ushort[] Unused = { @@ -37,33 +38,38 @@ public override PKM Clone() return pk; } - private string GetString(int offset, int count) => StringConverter3.GetString3(Data, offset, count, Japanese); - private byte[] SetString(string value, int maxLength) => StringConverter3.SetString3(value, maxLength, Japanese); - private const string EggNameJapanese = "タマゴ"; // Trash Bytes - public override Span Nickname_Trash { get => Data.AsSpan(0x08, 10); set { if (value.Length == 10) value.CopyTo(Data.AsSpan(0x08)); } } - public override Span OT_Trash { get => Data.AsSpan(0x14, 7); set { if (value.Length == 7) value.CopyTo(Data.AsSpan(0x14)); } } + public override Span Nickname_Trash => Data.AsSpan(0x08, 10); // no inaccessible terminator + public override Span OT_Trash => Data.AsSpan(0x14, 7); // no inaccessible terminator // At top for System.Reflection execution order hack // 0x20 Intro - public override uint PID { get => BitConverter.ToUInt32(Data, 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); } - public override int TID { get => BitConverter.ToUInt16(Data, 0x04); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x04); } - public override int SID { get => BitConverter.ToUInt16(Data, 0x06); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x06); } - public override string Nickname { get => GetString(0x08, 10); set => SetString(IsEgg ? EggNameJapanese : value, 10).CopyTo(Data, 0x08); } + public override uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0x00)); set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value); } + public override int TID { get => ReadUInt16LittleEndian(Data.AsSpan(0x04)); set => WriteUInt16LittleEndian(Data.AsSpan(0x04), (ushort)value); } + public override int SID { get => ReadUInt16LittleEndian(Data.AsSpan(0x06)); set => WriteUInt16LittleEndian(Data.AsSpan(0x06), (ushort)value); } + public override string Nickname + { + get => StringConverter3.GetString(Nickname_Trash, Japanese); + set => StringConverter3.SetString(Nickname_Trash, (IsEgg ? EggNameJapanese : value).AsSpan(), 10, Japanese, StringConverterOption.None); + } public override int Language { get => Data[0x12]; set => Data[0x12] = (byte)value; } public bool FlagIsBadEgg { get => (Data[0x13] & 1) != 0; set => Data[0x13] = (byte)((Data[0x13] & ~1) | (value ? 1 : 0)); } public bool FlagHasSpecies { get => (Data[0x13] & 2) != 0; set => Data[0x13] = (byte)((Data[0x13] & ~2) | (value ? 2 : 0)); } public bool FlagIsEgg { get => (Data[0x13] & 4) != 0; set => Data[0x13] = (byte)((Data[0x13] & ~4) | (value ? 4 : 0)); } - public override string OT_Name { get => GetString(0x14, 7); set => SetString(value, 7).CopyTo(Data, 0x14); } + public override string OT_Name + { + get => StringConverter3.GetString(OT_Trash, Japanese); + set => StringConverter3.SetString(OT_Trash, value.AsSpan(), 7, Japanese, StringConverterOption.None); + } public override int MarkValue { get => SwapBits(Data[0x1B], 1, 2); protected set => Data[0x1B] = (byte)SwapBits(value, 1, 2); } - public override ushort Checksum { get => BitConverter.ToUInt16(Data, 0x1C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1C); } - public override ushort Sanity { get => BitConverter.ToUInt16(Data, 0x1E); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1E); } + public ushort Checksum { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), value); } + public ushort Sanity { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), value); } #region Block A - public int SpeciesID3 { get => BitConverter.ToUInt16(Data, 0x20); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x20); } // raw access + public int SpeciesID3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x20)); set => WriteUInt16LittleEndian(Data.AsSpan(0x20), (ushort)value); } // raw access public override int Species { @@ -76,9 +82,9 @@ public override int Species } public override int SpriteItem => ItemConverter.GetItemFuture3((ushort)HeldItem); - public override int HeldItem { get => BitConverter.ToUInt16(Data, 0x22); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x22); } + public override int HeldItem { get => ReadUInt16LittleEndian(Data.AsSpan(0x22)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22), (ushort)value); } - public override uint EXP { get => BitConverter.ToUInt32(Data, 0x24); set => BitConverter.GetBytes(value).CopyTo(Data, 0x24); } + public override uint EXP { get => ReadUInt32LittleEndian(Data.AsSpan(0x24)); set => WriteUInt32LittleEndian(Data.AsSpan(0x24), value); } private byte PPUps { get => Data[0x28]; set => Data[0x28] = value; } public override int Move1_PPUps { get => (PPUps >> 0) & 3; set => PPUps = (byte)((PPUps & ~(3 << 0)) | value << 0); } public override int Move2_PPUps { get => (PPUps >> 2) & 3; set => PPUps = (byte)((PPUps & ~(3 << 2)) | value << 2); } @@ -89,10 +95,10 @@ public override int Species #endregion #region Block B - public override int Move1 { get => BitConverter.ToUInt16(Data, 0x2C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x2C); } - public override int Move2 { get => BitConverter.ToUInt16(Data, 0x2E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x2E); } - public override int Move3 { get => BitConverter.ToUInt16(Data, 0x30); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x30); } - public override int Move4 { get => BitConverter.ToUInt16(Data, 0x32); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x32); } + public override int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x2C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2C), (ushort)value); } + public override int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x2E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2E), (ushort)value); } + public override int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x30)); set => WriteUInt16LittleEndian(Data.AsSpan(0x30), (ushort)value); } + public override int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x32)); set => WriteUInt16LittleEndian(Data.AsSpan(0x32), (ushort)value); } public override int Move1_PP { get => Data[0x34]; set => Data[0x34] = (byte)value; } public override int Move2_PP { get => Data[0x35]; set => Data[0x35] = (byte)value; } public override int Move3_PP { get => Data[0x36]; set => Data[0x36] = (byte)value; } @@ -120,13 +126,13 @@ public override int Species public override int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)((PKRS & 0xF) | value << 4); } public override int Met_Location { get => Data[0x45]; set => Data[0x45] = (byte)value; } // Origins - private ushort Origins { get => BitConverter.ToUInt16(Data, 0x46); set => BitConverter.GetBytes(value).CopyTo(Data, 0x46); } + private ushort Origins { get => ReadUInt16LittleEndian(Data.AsSpan(0x46)); set => WriteUInt16LittleEndian(Data.AsSpan(0x46), value); } public override int Met_Level { get => Origins & 0x7F; set => Origins = (ushort)((Origins & ~0x7F) | value); } public override int Version { get => (Origins >> 7) & 0xF; set => Origins = (ushort)((Origins & ~0x780) | ((value & 0xF) << 7)); } public override int Ball { get => (Origins >> 11) & 0xF; set => Origins = (ushort)((Origins & ~0x7800) | ((value & 0xF) << 11)); } public override int OT_Gender { get => (Origins >> 15) & 1; set => Origins = (ushort)((Origins & ~(1 << 15)) | ((value & 1) << 15)); } - public uint IV32 { get => BitConverter.ToUInt32(Data, 0x48); set => BitConverter.GetBytes(value).CopyTo(Data, 0x48); } + public uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x48)); set => WriteUInt32LittleEndian(Data.AsSpan(0x48), value); } public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); } public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); } public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); } @@ -151,7 +157,7 @@ public override bool IsEgg public override bool AbilityBit { get => IV32 >> 31 == 1; set => IV32 = (IV32 & 0x7FFFFFFF) | (value ? 1u << 31 : 0u); } - private uint RIB0 { get => BitConverter.ToUInt32(Data, 0x4C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x4C); } + private uint RIB0 { get => ReadUInt32LittleEndian(Data.AsSpan(0x4C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x4C), value); } public override int RibbonCountG3Cool { get => (int)(RIB0 >> 00) & 7; set => RIB0 = ((RIB0 & ~(7u << 00)) | (uint)(value & 7) << 00); } public override int RibbonCountG3Beauty { get => (int)(RIB0 >> 03) & 7; set => RIB0 = ((RIB0 & ~(7u << 03)) | (uint)(value & 7) << 03); } public override int RibbonCountG3Cute { get => (int)(RIB0 >> 06) & 7; set => RIB0 = ((RIB0 & ~(7u << 06)) | (uint)(value & 7) << 06); } @@ -177,16 +183,16 @@ public override bool IsEgg #endregion #region Battle Stats - public override int Status_Condition { get => BitConverter.ToInt32(Data, 0x50); set => BitConverter.GetBytes(value).CopyTo(Data, 0x50); } + public override int Status_Condition { get => ReadInt32LittleEndian(Data.AsSpan(0x50)); set => WriteInt32LittleEndian(Data.AsSpan(0x50), value); } public override int Stat_Level { get => Data[0x54]; set => Data[0x54] = (byte)value; } public sbyte HeldMailID { get => (sbyte)Data[0x55]; set => Data[0x55] = (byte)value; } - public override int Stat_HPCurrent { get => BitConverter.ToUInt16(Data, 0x56); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x56); } - public override int Stat_HPMax { get => BitConverter.ToUInt16(Data, 0x58); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x58); } - public override int Stat_ATK { get => BitConverter.ToUInt16(Data, 0x5A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5A); } - public override int Stat_DEF { get => BitConverter.ToUInt16(Data, 0x5C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5C); } - public override int Stat_SPE { get => BitConverter.ToUInt16(Data, 0x5E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5E); } - public override int Stat_SPA { get => BitConverter.ToUInt16(Data, 0x60); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x60); } - public override int Stat_SPD { get => BitConverter.ToUInt16(Data, 0x62); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x62); } + public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data.AsSpan(0x56)); set => WriteUInt16LittleEndian(Data.AsSpan(0x56), (ushort)value); } + public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data.AsSpan(0x58)); set => WriteUInt16LittleEndian(Data.AsSpan(0x58), (ushort)value); } + public override int Stat_ATK { get => ReadUInt16LittleEndian(Data.AsSpan(0x5A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5A), (ushort)value); } + public override int Stat_DEF { get => ReadUInt16LittleEndian(Data.AsSpan(0x5C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5C), (ushort)value); } + public override int Stat_SPE { get => ReadUInt16LittleEndian(Data.AsSpan(0x5E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5E), (ushort)value); } + public override int Stat_SPA { get => ReadUInt16LittleEndian(Data.AsSpan(0x60)); set => WriteUInt16LittleEndian(Data.AsSpan(0x60), (ushort)value); } + public override int Stat_SPD { get => ReadUInt16LittleEndian(Data.AsSpan(0x62)); set => WriteUInt16LittleEndian(Data.AsSpan(0x62), (ushort)value); } #endregion protected override byte[] Encrypt() @@ -198,10 +204,11 @@ protected override byte[] Encrypt() public override void RefreshChecksum() { FlagIsBadEgg = false; - base.RefreshChecksum(); + Checksum = PokeCrypto.GetCHK3(Data); } - protected override ushort CalculateChecksum() => PokeCrypto.GetCHK3(Data); + public override bool ChecksumValid => CalculateChecksum() == Checksum; + private ushort CalculateChecksum() => PokeCrypto.GetCHK3(Data); public PK4 ConvertToPK4() { diff --git a/PKHeX.Core/PKM/PK4.cs b/PKHeX.Core/PKM/PK4.cs index 66fbf642e..5bb9d7f66 100644 --- a/PKHeX.Core/PKM/PK4.cs +++ b/PKHeX.Core/PKM/PK4.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -30,20 +31,17 @@ private static byte[] DecryptParty(byte[] data) public override PKM Clone() => new PK4((byte[])Data.Clone()); - private string GetString(int offset, int count) => StringConverter4.GetString4(Data, offset, count); - private static byte[] SetString(string value, int maxLength) => StringConverter4.SetString4(value, maxLength); - // Structure - public override uint PID { get => BitConverter.ToUInt32(Data, 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); } - public override ushort Sanity { get => BitConverter.ToUInt16(Data, 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); } - public override ushort Checksum { get => BitConverter.ToUInt16(Data, 0x06); set => BitConverter.GetBytes(value).CopyTo(Data, 0x06); } + public override uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0x00)); set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value); } + public override ushort Sanity { get => ReadUInt16LittleEndian(Data.AsSpan(0x04)); set => WriteUInt16LittleEndian(Data.AsSpan(0x04), value); } + public override ushort Checksum { get => ReadUInt16LittleEndian(Data.AsSpan(0x06)); set => WriteUInt16LittleEndian(Data.AsSpan(0x06), value); } #region Block A - public override int Species { get => BitConverter.ToUInt16(Data, 0x08); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08); } - public override int HeldItem { get => BitConverter.ToUInt16(Data, 0x0A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); } - public override int TID { get => BitConverter.ToUInt16(Data, 0x0C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0C); } - public override int SID { get => BitConverter.ToUInt16(Data, 0x0E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0E); } - public override uint EXP { get => BitConverter.ToUInt32(Data, 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, 0x10); } + public override int Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x08)); set => WriteUInt16LittleEndian(Data.AsSpan(0x08), (ushort)value); } + public override int HeldItem { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); } + public override int TID { get => ReadUInt16LittleEndian(Data.AsSpan(0x0C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0C), (ushort)value); } + public override int SID { get => ReadUInt16LittleEndian(Data.AsSpan(0x0E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0E), (ushort)value); } + public override uint EXP { get => ReadUInt32LittleEndian(Data.AsSpan(0x10)); set => WriteUInt32LittleEndian(Data.AsSpan(0x10), value); } public override int OT_Friendship { get => Data[0x14]; set => Data[0x14] = (byte)value; } public override int Ability { get => Data[0x15]; set => Data[0x15] = (byte)value; } public override int MarkValue { get => Data[0x16]; protected set => Data[0x16] = (byte)value; } @@ -100,10 +98,10 @@ private static byte[] DecryptParty(byte[] data) #endregion #region Block B - public override int Move1 { get => BitConverter.ToUInt16(Data, 0x28); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x28); } - public override int Move2 { get => BitConverter.ToUInt16(Data, 0x2A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x2A); } - public override int Move3 { get => BitConverter.ToUInt16(Data, 0x2C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x2C); } - public override int Move4 { get => BitConverter.ToUInt16(Data, 0x2E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x2E); } + public override int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x28)); set => WriteUInt16LittleEndian(Data.AsSpan(0x28), (ushort)value); } + public override int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x2A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2A), (ushort)value); } + public override int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x2C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2C), (ushort)value); } + public override int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x2E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2E), (ushort)value); } public override int Move1_PP { get => Data[0x30]; set => Data[0x30] = (byte)value; } public override int Move2_PP { get => Data[0x31]; set => Data[0x31] = (byte)value; } public override int Move3_PP { get => Data[0x32]; set => Data[0x32] = (byte)value; } @@ -112,7 +110,7 @@ private static byte[] DecryptParty(byte[] data) public override int Move2_PPUps { get => Data[0x35]; set => Data[0x35] = (byte)value; } public override int Move3_PPUps { get => Data[0x36]; set => Data[0x36] = (byte)value; } public override int Move4_PPUps { get => Data[0x37]; set => Data[0x37] = (byte)value; } - public uint IV32 { get => BitConverter.ToUInt32(Data, 0x38); set => BitConverter.GetBytes(value).CopyTo(Data, 0x38); } + public uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x38)); set => WriteUInt32LittleEndian(Data.AsSpan(0x38), value); } public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); } public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); } public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); } @@ -163,11 +161,28 @@ private static byte[] DecryptParty(byte[] data) public override int Gender { get => (Data[0x40] >> 1) & 0x3; set => Data[0x40] = (byte)((Data[0x40] & ~0x06) | (value << 1)); } public override int Form { get => Data[0x40] >> 3; set => Data[0x40] = (byte)((Data[0x40] & 0x07) | (value << 3)); } public override int ShinyLeaf { get => Data[0x41]; set => Data[0x41] = (byte) value; } - // 0x43-0x47 Unused + // 0x42-0x43 Unused + public override ushort Egg_LocationExtended + { + get => ReadUInt16LittleEndian(Data.AsSpan(0x44)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x44), value); + } + + public override ushort Met_LocationExtended + { + get => ReadUInt16LittleEndian(Data.AsSpan(0x46)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x46), value); + } + #endregion #region Block C - public override string Nickname { get => GetString(0x48, 20); set => SetString(value, 10).CopyTo(Data, 0x48); } + public override string Nickname + { + get => StringConverter4.GetString(Nickname_Trash); + set => StringConverter4.SetString(Nickname_Trash, value.AsSpan(), 10, StringConverterOption.None); + } + // 0x5E unused public override int Version { get => Data[0x5F]; set => Data[0x5F] = (byte)value; } private byte RIB8 { get => Data[0x60]; set => Data[0x60] = value; } // Sinnoh 3 @@ -210,7 +225,12 @@ private static byte[] DecryptParty(byte[] data) #endregion #region Block D - public override string OT_Name { get => GetString(0x68, 14); set => SetString(value, 7).CopyTo(Data, 0x68); } + public override string OT_Name + { + get => StringConverter4.GetString(OT_Trash); + set => StringConverter4.SetString(OT_Trash, value.AsSpan(), 7, StringConverterOption.None); + } + public override int Egg_Year { get => Data[0x78]; set => Data[0x78] = (byte)value; } public override int Egg_Month { get => Data[0x79]; set => Data[0x79] = (byte)value; } public override int Egg_Day { get => Data[0x7A]; set => Data[0x7A] = (byte)value; } @@ -218,66 +238,15 @@ private static byte[] DecryptParty(byte[] data) public override int Met_Month { get => Data[0x7C]; set => Data[0x7C] = (byte)value; } public override int Met_Day { get => Data[0x7D]; set => Data[0x7D] = (byte)value; } - public override int Egg_Location + public override ushort Egg_LocationDP { - get - { - ushort hgssloc = BitConverter.ToUInt16(Data, 0x44); - if (hgssloc != 0) - return hgssloc; - return BitConverter.ToUInt16(Data, 0x7E); - } - set - { - if (value == 0) - { - BitConverter.GetBytes((ushort)0).CopyTo(Data, 0x44); - BitConverter.GetBytes((ushort)0).CopyTo(Data, 0x7E); - } - else if (Locations.IsPtHGSSLocation(value) || Locations.IsPtHGSSLocationEgg(value)) - { - // Met location not in DP, set to Faraway Place - BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x44); - BitConverter.GetBytes((ushort)0xBBA).CopyTo(Data, 0x7E); - } - else - { - int pthgss = PtHGSS ? value : 0; // only set to PtHGSS loc if encountered in game - BitConverter.GetBytes((ushort)pthgss).CopyTo(Data, 0x44); - BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x7E); - } - } + get => ReadUInt16LittleEndian(Data.AsSpan(0x7E)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x7E), value); } - - public override int Met_Location + public override ushort Met_LocationDP { - get - { - ushort hgssloc = BitConverter.ToUInt16(Data, 0x46); - if (hgssloc != 0) - return hgssloc; - return BitConverter.ToUInt16(Data, 0x80); - } - set - { - if (value == 0) - { - BitConverter.GetBytes((ushort)0).CopyTo(Data, 0x46); - BitConverter.GetBytes((ushort)0).CopyTo(Data, 0x80); - } - else if (Locations.IsPtHGSSLocation(value) || Locations.IsPtHGSSLocationEgg(value)) - { - // Met location not in DP, set to Faraway Place - BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x46); - BitConverter.GetBytes((ushort)0xBBA).CopyTo(Data, 0x80); - } - else - { - int pthgss = PtHGSS ? value : 0; // only set to PtHGSS loc if encountered in game - BitConverter.GetBytes((ushort)pthgss).CopyTo(Data, 0x46); - BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x80); - } - } + get => ReadUInt16LittleEndian(Data.AsSpan(0x80)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x80), value); } private byte PKRS { get => Data[0x82]; set => Data[0x82] = value; } @@ -292,15 +261,15 @@ public override int Met_Location #endregion #region Battle Stats - public override int Status_Condition { get => BitConverter.ToInt32(Data, 0x88); set => BitConverter.GetBytes(value).CopyTo(Data, 0x88); } + public override int Status_Condition { get => ReadInt32LittleEndian(Data.AsSpan(0x88)); set => WriteInt32LittleEndian(Data.AsSpan(0x88), value); } public override int Stat_Level { get => Data[0x8C]; set => Data[0x8C] = (byte)value; } - public override int Stat_HPCurrent { get => BitConverter.ToUInt16(Data, 0x8E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x8E); } - public override int Stat_HPMax { get => BitConverter.ToUInt16(Data, 0x90); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x90); } - public override int Stat_ATK { get => BitConverter.ToUInt16(Data, 0x92); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x92); } - public override int Stat_DEF { get => BitConverter.ToUInt16(Data, 0x94); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x94); } - public override int Stat_SPE { get => BitConverter.ToUInt16(Data, 0x96); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x96); } - public override int Stat_SPA { get => BitConverter.ToUInt16(Data, 0x98); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x98); } - public override int Stat_SPD { get => BitConverter.ToUInt16(Data, 0x9A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x9A); } + public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data.AsSpan(0x8E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x8E), (ushort)value); } + public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data.AsSpan(0x90)); set => WriteUInt16LittleEndian(Data.AsSpan(0x90), (ushort)value); } + public override int Stat_ATK { get => ReadUInt16LittleEndian(Data.AsSpan(0x92)); set => WriteUInt16LittleEndian(Data.AsSpan(0x92), (ushort)value); } + public override int Stat_DEF { get => ReadUInt16LittleEndian(Data.AsSpan(0x94)); set => WriteUInt16LittleEndian(Data.AsSpan(0x94), (ushort)value); } + public override int Stat_SPE { get => ReadUInt16LittleEndian(Data.AsSpan(0x96)); set => WriteUInt16LittleEndian(Data.AsSpan(0x96), (ushort)value); } + public override int Stat_SPA { get => ReadUInt16LittleEndian(Data.AsSpan(0x98)); set => WriteUInt16LittleEndian(Data.AsSpan(0x98), (ushort)value); } + public override int Stat_SPD { get => ReadUInt16LittleEndian(Data.AsSpan(0x9A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x9A), (ushort)value); } public byte[] GetHeldMailData() => Data.Slice(0x9C, 0x38); public void SetHeldMailData(byte[] value) => value.CopyTo(Data, 0x9C); @@ -330,7 +299,7 @@ public BK4 ConvertToBK4() public PK5 ConvertToPK5() { // Double Check Location Data to see if we're already a PK5 - if (Data[0x5F] < 0x10 && BitConverter.ToUInt16(Data, 0x80) > 0x4000) + if (Data[0x5F] < 0x10 && ReadUInt16LittleEndian(Data.AsSpan(0x80)) > 0x4000) return new PK5(Data); DateTime moment = DateTime.Now; @@ -360,7 +329,7 @@ public PK5 ConvertToPK5() pk5.Nature = Nature; // Delete Platinum/HGSS Met Location Data - BitConverter.GetBytes((uint)0).CopyTo(pk5.Data, 0x44); + WriteUInt32LittleEndian(pk5.Data.AsSpan(0x44), 0); // Met / Crown Data Detection pk5.Met_Location = Legal.GetTransfer45MetLocation(pk5); @@ -369,7 +338,7 @@ public PK5 ConvertToPK5() // pk5.Egg_Location = Egg_Location; // Delete HGSS Data - BitConverter.GetBytes((ushort)0).CopyTo(pk5.Data, 0x86); + WriteUInt16LittleEndian(pk5.Data.AsSpan(0x86), 0); pk5.Ball = Ball; // Transfer Nickname and OT Name, update encoding diff --git a/PKHeX.Core/PKM/PK5.cs b/PKHeX.Core/PKM/PK5.cs index af5eba1f2..7655dbb11 100644 --- a/PKHeX.Core/PKM/PK5.cs +++ b/PKHeX.Core/PKM/PK5.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { /// Generation 5 format. - public sealed class PK5 : PKM, + public sealed class PK5 : PKM, ISanityChecksum, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetUnique3, IRibbonSetUnique4, IRibbonSetCommon3, IRibbonSetCommon4, IContestStats, IContestStatsMutable, IGroundTile { @@ -36,13 +37,14 @@ private static byte[] DecryptParty(byte[] data) } public override PKM Clone() => new PK5((byte[])Data.Clone()); - - private string GetString(int offset, int count) => StringConverter.GetString5(Data, offset, count); - private static byte[] SetString(string value, int maxLength) => StringConverter.SetString5(value, maxLength); + public override void RefreshChecksum() => Checksum = CalculateChecksum(); + public override bool ChecksumValid => CalculateChecksum() == Checksum; + public override bool Valid { get => Sanity == 0 && ChecksumValid; set { if (!value) return; Sanity = 0; RefreshChecksum(); } } + private ushort CalculateChecksum() => PokeCrypto.GetCHK(Data, PokeCrypto.SIZE_4STORED); // Trash Bytes - public override Span Nickname_Trash { get => Data.AsSpan(0x48, 22); set { if (value.Length == 22) value.CopyTo(Data.AsSpan(0x48)); } } - public override Span OT_Trash { get => Data.AsSpan(0x68, 16); set { if (value.Length == 16) value.CopyTo(Data.AsSpan(0x68)); } } + public override Span Nickname_Trash => Data.AsSpan(0x48, 22); + public override Span OT_Trash => Data.AsSpan(0x68, 16); // Future Attributes public override uint EncryptionConstant { get => PID; set { } } @@ -51,16 +53,16 @@ private static byte[] DecryptParty(byte[] data) public override int AbilityNumber { get => HiddenAbility ? 4 : 1 << PIDAbility; set { } } // Structure - public override uint PID { get => BitConverter.ToUInt32(Data, 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); } - public override ushort Sanity { get => BitConverter.ToUInt16(Data, 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); } - public override ushort Checksum { get => BitConverter.ToUInt16(Data, 0x06); set => BitConverter.GetBytes(value).CopyTo(Data, 0x06); } + public override uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0x00)); set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value); } + public ushort Sanity { get => ReadUInt16LittleEndian(Data.AsSpan(0x04)); set => WriteUInt16LittleEndian(Data.AsSpan(0x04), value); } + public ushort Checksum { get => ReadUInt16LittleEndian(Data.AsSpan(0x06)); set => WriteUInt16LittleEndian(Data.AsSpan(0x06), value); } #region Block A - public override int Species { get => BitConverter.ToUInt16(Data, 0x08); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08); } - public override int HeldItem { get => BitConverter.ToUInt16(Data, 0x0A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); } - public override int TID { get => BitConverter.ToUInt16(Data, 0x0C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0C); } - public override int SID { get => BitConverter.ToUInt16(Data, 0x0E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0E); } - public override uint EXP { get => BitConverter.ToUInt32(Data, 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, 0x10); } + public override int Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x08)); set => WriteUInt16LittleEndian(Data.AsSpan(0x08), (ushort)value); } + public override int HeldItem { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); } + public override int TID { get => ReadUInt16LittleEndian(Data.AsSpan(0x0C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0C), (ushort)value); } + public override int SID { get => ReadUInt16LittleEndian(Data.AsSpan(0x0E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0E), (ushort)value); } + public override uint EXP { get => ReadUInt32LittleEndian(Data.AsSpan(0x10)); set => WriteUInt32LittleEndian(Data.AsSpan(0x10), value); } public override int OT_Friendship { get => Data[0x14]; set => Data[0x14] = (byte)value; } public override int Ability { get => Data[0x15]; set => Data[0x15] = (byte)value; } public override int MarkValue { get => Data[0x16]; protected set => Data[0x16] = (byte)value; } @@ -117,10 +119,10 @@ private static byte[] DecryptParty(byte[] data) #endregion #region Block B - public override int Move1 { get => BitConverter.ToUInt16(Data, 0x28); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x28); } - public override int Move2 { get => BitConverter.ToUInt16(Data, 0x2A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x2A); } - public override int Move3 { get => BitConverter.ToUInt16(Data, 0x2C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x2C); } - public override int Move4 { get => BitConverter.ToUInt16(Data, 0x2E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x2E); } + public override int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x28)); set => WriteUInt16LittleEndian(Data.AsSpan(0x28), (ushort)value); } + public override int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x2A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2A), (ushort)value); } + public override int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x2C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2C), (ushort)value); } + public override int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x2E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2E), (ushort)value); } public override int Move1_PP { get => Data[0x30]; set => Data[0x30] = (byte)value; } public override int Move2_PP { get => Data[0x31]; set => Data[0x31] = (byte)value; } public override int Move3_PP { get => Data[0x32]; set => Data[0x32] = (byte)value; } @@ -129,7 +131,7 @@ private static byte[] DecryptParty(byte[] data) public override int Move2_PPUps { get => Data[0x35]; set => Data[0x35] = (byte)value; } public override int Move3_PPUps { get => Data[0x36]; set => Data[0x36] = (byte)value; } public override int Move4_PPUps { get => Data[0x37]; set => Data[0x37] = (byte)value; } - private uint IV32 { get => BitConverter.ToUInt32(Data, 0x38); set => BitConverter.GetBytes(value).CopyTo(Data, 0x38); } + private uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x38)); set => WriteUInt32LittleEndian(Data.AsSpan(0x38), value); } public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); } public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); } public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); } @@ -186,7 +188,7 @@ private static byte[] DecryptParty(byte[] data) #endregion #region Block C - public override string Nickname { get => GetString(0x48, 20); set => SetString(value, 10).CopyTo(Data, 0x48); } + public override string Nickname { get => StringConverter5.GetString(Nickname_Trash); set => StringConverter5.SetString(Nickname_Trash, value.AsSpan(), 10, StringConverterOption.None); } // 0x5E unused public override int Version { get => Data[0x5F]; set => Data[0x5F] = (byte)value; } private byte RIB8 { get => Data[0x60]; set => Data[0x60] = value; } // Sinnoh 3 @@ -229,15 +231,15 @@ private static byte[] DecryptParty(byte[] data) #endregion #region Block D - public override string OT_Name { get => GetString(0x68, 14); set => SetString(value, 7).CopyTo(Data, 0x68); } + public override string OT_Name { get => StringConverter5.GetString(OT_Trash); set => StringConverter5.SetString(OT_Trash, value.AsSpan(), 7, StringConverterOption.None); } public override int Egg_Year { get => Data[0x78]; set => Data[0x78] = (byte)value; } public override int Egg_Month { get => Data[0x79]; set => Data[0x79] = (byte)value; } public override int Egg_Day { get => Data[0x7A]; set => Data[0x7A] = (byte)value; } public override int Met_Year { get => Data[0x7B]; set => Data[0x7B] = (byte)value; } public override int Met_Month { get => Data[0x7C]; set => Data[0x7C] = (byte)value; } public override int Met_Day { get => Data[0x7D]; set => Data[0x7D] = (byte)value; } - public override int Egg_Location { get => BitConverter.ToUInt16(Data, 0x7E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x7E); } - public override int Met_Location { get => BitConverter.ToUInt16(Data, 0x80); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x80); } + public override int Egg_Location { get => ReadUInt16LittleEndian(Data.AsSpan(0x7E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x7E), (ushort)value); } + public override int Met_Location { get => ReadUInt16LittleEndian(Data.AsSpan(0x80)); set => WriteUInt16LittleEndian(Data.AsSpan(0x80), (ushort)value); } private byte PKRS { get => Data[0x82]; set => Data[0x82] = value; } public override int PKRS_Days { get => PKRS & 0xF; set => PKRS = (byte)((PKRS & ~0xF) | value); } public override int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)((PKRS & 0xF) | (value << 4)); } @@ -251,15 +253,15 @@ private static byte[] DecryptParty(byte[] data) #endregion #region Battle Stats - public override int Status_Condition { get => BitConverter.ToInt32(Data, 0x88); set => BitConverter.GetBytes(value).CopyTo(Data, 0x88); } + public override int Status_Condition { get => ReadInt32LittleEndian(Data.AsSpan(0x88)); set => WriteInt32LittleEndian(Data.AsSpan(0x88), value); } public override int Stat_Level { get => Data[0x8C]; set => Data[0x8C] = (byte)value; } - public override int Stat_HPCurrent { get => BitConverter.ToUInt16(Data, 0x8E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x8E); } - public override int Stat_HPMax { get => BitConverter.ToUInt16(Data, 0x90); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x90); } - public override int Stat_ATK { get => BitConverter.ToUInt16(Data, 0x92); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x92); } - public override int Stat_DEF { get => BitConverter.ToUInt16(Data, 0x94); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x94); } - public override int Stat_SPE { get => BitConverter.ToUInt16(Data, 0x96); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x96); } - public override int Stat_SPA { get => BitConverter.ToUInt16(Data, 0x98); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x98); } - public override int Stat_SPD { get => BitConverter.ToUInt16(Data, 0x9A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x9A); } + public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data.AsSpan(0x8E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x8E), (ushort)value); } + public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data.AsSpan(0x90)); set => WriteUInt16LittleEndian(Data.AsSpan(0x90), (ushort)value); } + public override int Stat_ATK { get => ReadUInt16LittleEndian(Data.AsSpan(0x92)); set => WriteUInt16LittleEndian(Data.AsSpan(0x92), (ushort)value); } + public override int Stat_DEF { get => ReadUInt16LittleEndian(Data.AsSpan(0x94)); set => WriteUInt16LittleEndian(Data.AsSpan(0x94), (ushort)value); } + public override int Stat_SPE { get => ReadUInt16LittleEndian(Data.AsSpan(0x96)); set => WriteUInt16LittleEndian(Data.AsSpan(0x96), (ushort)value); } + public override int Stat_SPA { get => ReadUInt16LittleEndian(Data.AsSpan(0x98)); set => WriteUInt16LittleEndian(Data.AsSpan(0x98), (ushort)value); } + public override int Stat_SPD { get => ReadUInt16LittleEndian(Data.AsSpan(0x9A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x9A), (ushort)value); } public byte[] GetHeldMailData() => Data.Slice(0x9C, 0x38); public void SetHeldMailData(byte[] value) => value.CopyTo(Data, 0x9C); @@ -476,8 +478,8 @@ public PK6 ConvertToPK6() pk6.HealPP(); // Fix Name Strings - pk6.Nickname = StringConverter345.TransferGlyphs56(pk6.Nickname); - pk6.OT_Name = StringConverter345.TransferGlyphs56(pk6.OT_Name); + StringConverter345.TransferGlyphs56(pk6.Nickname_Trash); + StringConverter345.TransferGlyphs56(pk6.OT_Trash); // Fix Checksum pk6.RefreshChecksum(); diff --git a/PKHeX.Core/PKM/PK6.cs b/PKHeX.Core/PKM/PK6.cs index a675ff057..957fd8879 100644 --- a/PKHeX.Core/PKM/PK6.cs +++ b/PKHeX.Core/PKM/PK6.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -29,57 +30,54 @@ private static byte[] DecryptParty(byte[] data) public override PKM Clone() => new PK6((byte[])Data.Clone()); - private string GetString(int offset, int count) => StringConverter.GetString6(Data, offset, count); - private static byte[] SetString(string value, int maxLength) => StringConverter.SetString6(value, maxLength); - // Structure #region Block A public override uint EncryptionConstant { - get => BitConverter.ToUInt32(Data, 0x00); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); + get => ReadUInt32LittleEndian(Data.AsSpan(0x00)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value); } public override ushort Sanity { - get => BitConverter.ToUInt16(Data, 0x04); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); + get => ReadUInt16LittleEndian(Data.AsSpan(0x04)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x04), value); } public override ushort Checksum { - get => BitConverter.ToUInt16(Data, 0x06); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x06); + get => ReadUInt16LittleEndian(Data.AsSpan(0x06)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x06), value); } public override int Species { - get => BitConverter.ToUInt16(Data, 0x08); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08); + get => ReadUInt16LittleEndian(Data.AsSpan(0x08)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x08), (ushort)value); } public override int HeldItem { - get => BitConverter.ToUInt16(Data, 0x0A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); + get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); } public override int TID { - get => BitConverter.ToUInt16(Data, 0x0C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0C); + get => ReadUInt16LittleEndian(Data.AsSpan(0x0C)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x0C), (ushort)value); } public override int SID { - get => BitConverter.ToUInt16(Data, 0x0E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0E); + get => ReadUInt16LittleEndian(Data.AsSpan(0x0E)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x0E), (ushort)value); } public override uint EXP { - get => BitConverter.ToUInt32(Data, 0x10); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x10); + get => ReadUInt32LittleEndian(Data.AsSpan(0x10)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x10), value); } public override int Ability { get => Data[0x14]; set => Data[0x14] = (byte)value; } @@ -89,8 +87,8 @@ public override uint EXP public override uint PID { - get => BitConverter.ToUInt32(Data, 0x18); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x18); + get => ReadUInt32LittleEndian(Data.AsSpan(0x18)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x18), value); } public override int Nature { get => Data[0x1C]; set => Data[0x1C] = (byte)value; } @@ -149,7 +147,7 @@ public override uint PID public bool SuperTrain7_2 { get => (ST4 & (1 << 5)) == 1 << 5; set => ST4 = (byte)((ST4 & ~(1 << 5)) | (value ? 1 << 5 : 0)); } public bool SuperTrain7_3 { get => (ST4 & (1 << 6)) == 1 << 6; set => ST4 = (byte)((ST4 & ~(1 << 6)) | (value ? 1 << 6 : 0)); } public bool SuperTrain8_1 { get => (ST4 & (1 << 7)) == 1 << 7; set => ST4 = (byte)((ST4 & ~(1 << 7)) | (value ? 1 << 7 : 0)); } - public uint SuperTrainBitFlags { get => BitConverter.ToUInt32(Data, 0x2C); set => BitConverter.GetBytes(value).CopyTo(Data); } + public uint SuperTrainBitFlags { get => ReadUInt32LittleEndian(Data.AsSpan(0x2C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x2C), value); } private byte RIB0 { get => Data[0x30]; set => Data[0x30] = value; } // Ribbons are read as uints, but let's keep them per byte. private byte RIB1 { get => Data[0x31]; set => Data[0x31] = value; } private byte RIB2 { get => Data[0x32]; set => Data[0x32] = value; } @@ -206,7 +204,7 @@ public override uint PID public bool RIB5_7 { get => (RIB5 & (1 << 7)) == 1 << 7; set => RIB5 = (byte)((RIB5 & ~(1 << 7)) | (value ? 1 << 7 : 0)); } // Unused public int RibbonCountMemoryContest { get => Data[0x38]; set => HasContestMemoryRibbon = (Data[0x38] = (byte)value) != 0; } public int RibbonCountMemoryBattle { get => Data[0x39]; set => HasBattleMemoryRibbon = (Data[0x39] = (byte)value) != 0; } - private ushort DistByte { get => BitConverter.ToUInt16(Data, 0x3A); set => BitConverter.GetBytes(value).CopyTo(Data, 0x3A); } + private ushort DistByte { get => ReadUInt16LittleEndian(Data.AsSpan(0x3A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x3A), value); } public bool DistSuperTrain1 { get => (DistByte & (1 << 0)) == 1 << 0; set => DistByte = (byte)((DistByte & ~(1 << 0)) | (value ? 1 << 0 : 0)); } public bool DistSuperTrain2 { get => (DistByte & (1 << 1)) == 1 << 1; set => DistByte = (byte)((DistByte & ~(1 << 1)) | (value ? 1 << 1 : 0)); } public bool DistSuperTrain3 { get => (DistByte & (1 << 2)) == 1 << 2; set => DistByte = (byte)((DistByte & ~(1 << 2)) | (value ? 1 << 2 : 0)); } @@ -215,34 +213,38 @@ public override uint PID public bool DistSuperTrain6 { get => (DistByte & (1 << 5)) == 1 << 5; set => DistByte = (byte)((DistByte & ~(1 << 5)) | (value ? 1 << 5 : 0)); } public bool Dist7 { get => (DistByte & (1 << 6)) == 1 << 6; set => DistByte = (byte)((DistByte & ~(1 << 6)) | (value ? 1 << 6 : 0)); } public bool Dist8 { get => (DistByte & (1 << 7)) == 1 << 7; set => DistByte = (byte)((DistByte & ~(1 << 7)) | (value ? 1 << 7 : 0)); } - public uint FormArgument { get => BitConverter.ToUInt32(Data, 0x3C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x3C); } + public uint FormArgument { get => ReadUInt32LittleEndian(Data.AsSpan(0x3C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x3C), value); } public byte FormArgumentMaximum { get => (byte)FormArgument; set => FormArgument = value & 0xFFu; } #endregion #region Block B - public override string Nickname { get => GetString(0x40, 24); set => SetString(value, 12).CopyTo(Data, 0x40); } + public override string Nickname + { + get => StringConverter6.GetString(Nickname_Trash); + set => StringConverter6.SetString(Nickname_Trash, value.AsSpan(), 12, StringConverterOption.None); + } public override int Move1 { - get => BitConverter.ToUInt16(Data, 0x5A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5A); + get => ReadUInt16LittleEndian(Data.AsSpan(0x5A)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x5A), (ushort)value); } public override int Move2 { - get => BitConverter.ToUInt16(Data, 0x5C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5C); + get => ReadUInt16LittleEndian(Data.AsSpan(0x5C)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x5C), (ushort)value); } public override int Move3 { - get => BitConverter.ToUInt16(Data, 0x5E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5E); + get => ReadUInt16LittleEndian(Data.AsSpan(0x5E)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x5E), (ushort)value); } public override int Move4 { - get => BitConverter.ToUInt16(Data, 0x60); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x60); + get => ReadUInt16LittleEndian(Data.AsSpan(0x60)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x60), (ushort)value); } public override int Move1_PP { get => Data[0x62]; set => Data[0x62] = (byte)value; } @@ -256,32 +258,32 @@ public override int Move4 public override int RelearnMove1 { - get => BitConverter.ToUInt16(Data, 0x6A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6A); + get => ReadUInt16LittleEndian(Data.AsSpan(0x6A)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x6A), (ushort)value); } public override int RelearnMove2 { - get => BitConverter.ToUInt16(Data, 0x6C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6C); + get => ReadUInt16LittleEndian(Data.AsSpan(0x6C)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x6C), (ushort)value); } public override int RelearnMove3 { - get => BitConverter.ToUInt16(Data, 0x6E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6E); + get => ReadUInt16LittleEndian(Data.AsSpan(0x6E)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x6E), (ushort)value); } public override int RelearnMove4 { - get => BitConverter.ToUInt16(Data, 0x70); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x70); + get => ReadUInt16LittleEndian(Data.AsSpan(0x70)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x70), (ushort)value); } public bool SecretSuperTrainingUnlocked { get => (Data[0x72] & 1) == 1; set => Data[0x72] = (byte)((Data[0x72] & ~1) | (value ? 1 : 0)); } public bool SecretSuperTrainingComplete { get => (Data[0x72] & 2) == 2; set => Data[0x72] = (byte)((Data[0x72] & ~2) | (value ? 2 : 0)); } // 0x73 Unused - private uint IV32 { get => BitConverter.ToUInt32(Data, 0x74); set => BitConverter.GetBytes(value).CopyTo(Data, 0x74); } + private uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x74)); set => WriteUInt32LittleEndian(Data.AsSpan(0x74), value); } public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); } public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); } public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); } @@ -292,7 +294,11 @@ public override int RelearnMove4 public override bool IsNicknamed { get => ((IV32 >> 31) & 1) == 1; set => IV32 = (IV32 & 0x7FFFFFFFu) | (value ? 0x80000000u : 0u); } #endregion #region Block C - public override string HT_Name { get => GetString(0x78, 24); set => SetString(value, 12).CopyTo(Data, 0x78); } + public override string HT_Name + { + get => StringConverter6.GetString(HT_Trash); + set => StringConverter6.SetString(HT_Trash, value.AsSpan(), 12, StringConverterOption.None); + } public override int HT_Gender { get => Data[0x92]; set => Data[0x92] = (byte)value; } public override int CurrentHandler { get => Data[0x93]; set => Data[0x93] = (byte)value; } public byte Geo1_Region { get => Data[0x94]; set => Data[0x94] = value; } @@ -315,7 +321,7 @@ public override int RelearnMove4 public int HT_Memory { get => Data[0xA5]; set => Data[0xA5] = (byte)value; } public int HT_Feeling { get => Data[0xA6]; set => Data[0xA6] = (byte)value; } // 0xA7 Unused - public int HT_TextVar { get => BitConverter.ToUInt16(Data, 0xA8); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xA8); } + public int HT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0xA8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xA8), (ushort)value); } // 0xAA Unused // 0xAB Unused // 0xAC Unused @@ -324,12 +330,16 @@ public override int RelearnMove4 public override byte Enjoyment { get => Data[0xAF]; set => Data[0xAF] = value; } #endregion #region Block D - public override string OT_Name { get => GetString(0xB0, 24); set => SetString(value, 12).CopyTo(Data, 0xB0); } + public override string OT_Name + { + get => StringConverter6.GetString(OT_Trash); + set => StringConverter6.SetString(OT_Trash, value.AsSpan(), 12, StringConverterOption.None); + } public override int OT_Friendship { get => Data[0xCA]; set => Data[0xCA] = (byte)value; } public int OT_Affection { get => Data[0xCB]; set => Data[0xCB] = (byte)value; } public int OT_Intensity { get => Data[0xCC]; set => Data[0xCC] = (byte)value; } public int OT_Memory { get => Data[0xCD]; set => Data[0xCD] = (byte)value; } - public int OT_TextVar { get => BitConverter.ToUInt16(Data, 0xCE); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xCE); } + public int OT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0xCE)); set => WriteUInt16LittleEndian(Data.AsSpan(0xCE), (ushort)value); } public int OT_Feeling { get => Data[0xD0]; set => Data[0xD0] = (byte)value; } public override int Egg_Year { get => Data[0xD1]; set => Data[0xD1] = (byte)value; } public override int Egg_Month { get => Data[0xD2]; set => Data[0xD2] = (byte)value; } @@ -338,8 +348,8 @@ public override int RelearnMove4 public override int Met_Month { get => Data[0xD5]; set => Data[0xD5] = (byte)value; } public override int Met_Day { get => Data[0xD6]; set => Data[0xD6] = (byte)value; } // 0xD7 Unused - public override int Egg_Location { get => BitConverter.ToUInt16(Data, 0xD8); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xD8); } - public override int Met_Location { get => BitConverter.ToUInt16(Data, 0xDA); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xDA); } + public override int Egg_Location { get => ReadUInt16LittleEndian(Data.AsSpan(0xD8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xD8), (ushort)value); } + public override int Met_Location { get => ReadUInt16LittleEndian(Data.AsSpan(0xDA)); set => WriteUInt16LittleEndian(Data.AsSpan(0xDA), (ushort)value); } public override int Ball { get => Data[0xDC]; set => Data[0xDC] = (byte)value; } public override int Met_Level { get => Data[0xDD] & ~0x80; set => Data[0xDD] = (byte)((Data[0xDD] & 0x80) | value); } public override int OT_Gender { get => Data[0xDD] >> 7; set => Data[0xDD] = (byte)((Data[0xDD] & ~0x80) | (value << 7)); } @@ -351,17 +361,17 @@ public override int RelearnMove4 public override int Language { get => Data[0xE3]; set => Data[0xE3] = (byte)value; } #endregion #region Battle Stats - public override int Status_Condition { get => BitConverter.ToInt32(Data, 0xE8); set => BitConverter.GetBytes(value).CopyTo(Data, 0xE8); } + public override int Status_Condition { get => ReadInt32LittleEndian(Data.AsSpan(0xE8)); set => WriteInt32LittleEndian(Data.AsSpan(0xE8), value); } public override int Stat_Level { get => Data[0xEC]; set => Data[0xEC] = (byte)value; } public byte FormArgumentRemain { get => Data[0xED]; set => Data[0xED] = value; } public byte FormArgumentElapsed { get => Data[0xEE]; set => Data[0xEE] = value; } - public override int Stat_HPCurrent { get => BitConverter.ToUInt16(Data, 0xF0); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF0); } - public override int Stat_HPMax { get => BitConverter.ToUInt16(Data, 0xF2); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF2); } - public override int Stat_ATK { get => BitConverter.ToUInt16(Data, 0xF4); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF4); } - public override int Stat_DEF { get => BitConverter.ToUInt16(Data, 0xF6); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF6); } - public override int Stat_SPE { get => BitConverter.ToUInt16(Data, 0xF8); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF8); } - public override int Stat_SPA { get => BitConverter.ToUInt16(Data, 0xFA); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xFA); } - public override int Stat_SPD { get => BitConverter.ToUInt16(Data, 0xFC); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xFC); } + public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data.AsSpan(0xF0)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF0), (ushort)value); } + public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data.AsSpan(0xF2)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF2), (ushort)value); } + public override int Stat_ATK { get => ReadUInt16LittleEndian(Data.AsSpan(0xF4)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF4), (ushort)value); } + public override int Stat_DEF { get => ReadUInt16LittleEndian(Data.AsSpan(0xF6)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF6), (ushort)value); } + public override int Stat_SPE { get => ReadUInt16LittleEndian(Data.AsSpan(0xF8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF8), (ushort)value); } + public override int Stat_SPA { get => ReadUInt16LittleEndian(Data.AsSpan(0xFA)); set => WriteUInt16LittleEndian(Data.AsSpan(0xFA), (ushort)value); } + public override int Stat_SPD { get => ReadUInt16LittleEndian(Data.AsSpan(0xFC)); set => WriteUInt16LittleEndian(Data.AsSpan(0xFC), (ushort)value); } #endregion public int SuperTrainingMedalCount(int maxCount = 30) diff --git a/PKHeX.Core/PKM/PK7.cs b/PKHeX.Core/PKM/PK7.cs index baf57a623..0e057ea26 100644 --- a/PKHeX.Core/PKM/PK7.cs +++ b/PKHeX.Core/PKM/PK7.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -30,67 +31,64 @@ private static byte[] DecryptParty(byte[] data) public override PKM Clone() => new PK7((byte[])Data.Clone()); - private string GetString(int offset, int count) => StringConverter.GetString7(Data, offset, count); - private byte[] SetString(string value, int maxLength, bool chinese = false) => StringConverter.SetString7(value, maxLength, Language, chinese: chinese); - // Structure #region Block A public override uint EncryptionConstant { - get => BitConverter.ToUInt32(Data, 0x00); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); + get => ReadUInt32LittleEndian(Data.AsSpan(0x00)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value); } public override ushort Sanity { - get => BitConverter.ToUInt16(Data, 0x04); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); + get => ReadUInt16LittleEndian(Data.AsSpan(0x04)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x04), value); } public override ushort Checksum { - get => BitConverter.ToUInt16(Data, 0x06); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x06); + get => ReadUInt16LittleEndian(Data.AsSpan(0x06)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x06), value); } public override int Species { - get => BitConverter.ToUInt16(Data, 0x08); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08); + get => ReadUInt16LittleEndian(Data.AsSpan(0x08)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x08), (ushort)value); } public override int HeldItem { - get => BitConverter.ToUInt16(Data, 0x0A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); + get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); } public override int TID { - get => BitConverter.ToUInt16(Data, 0x0C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0C); + get => ReadUInt16LittleEndian(Data.AsSpan(0x0C)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x0C), (ushort)value); } public override int SID { - get => BitConverter.ToUInt16(Data, 0x0E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0E); + get => ReadUInt16LittleEndian(Data.AsSpan(0x0E)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x0E), (ushort)value); } public override uint EXP { - get => BitConverter.ToUInt32(Data, 0x10); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x10); + get => ReadUInt32LittleEndian(Data.AsSpan(0x10)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x10), value); } public override int Ability { get => Data[0x14]; set => Data[0x14] = (byte)value; } public override int AbilityNumber { get => Data[0x15] & 7; set => Data[0x15] = (byte)((Data[0x15] & ~7) | (value & 7)); } - public override int MarkValue { get => BitConverter.ToUInt16(Data, 0x16); protected set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x16); } + public override int MarkValue { get => ReadUInt16LittleEndian(Data.AsSpan(0x16)); protected set => WriteUInt16LittleEndian(Data.AsSpan(0x16), (ushort)value); } public override uint PID { - get => BitConverter.ToUInt32(Data, 0x18); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x18); + get => ReadUInt32LittleEndian(Data.AsSpan(0x18)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x18), value); } public override int Nature { get => Data[0x1C]; set => Data[0x1C] = (byte)value; } @@ -149,7 +147,7 @@ public override uint PID public bool SuperTrain7_2 { get => (ST4 & (1 << 5)) == 1 << 5; set => ST4 = (byte)((ST4 & ~(1 << 5)) | (value ? 1 << 5 : 0)); } public bool SuperTrain7_3 { get => (ST4 & (1 << 6)) == 1 << 6; set => ST4 = (byte)((ST4 & ~(1 << 6)) | (value ? 1 << 6 : 0)); } public bool SuperTrain8_1 { get => (ST4 & (1 << 7)) == 1 << 7; set => ST4 = (byte)((ST4 & ~(1 << 7)) | (value ? 1 << 7 : 0)); } - public uint SuperTrainBitFlags { get => BitConverter.ToUInt32(Data, 0x2C); set => BitConverter.GetBytes(value).CopyTo(Data); } + public uint SuperTrainBitFlags { get => ReadUInt32LittleEndian(Data.AsSpan(0x2C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x2C), value); } private byte RIB0 { get => Data[0x30]; set => Data[0x30] = value; } // Ribbons are read as uints, but let's keep them per byte. private byte RIB1 { get => Data[0x31]; set => Data[0x31] = value; } private byte RIB2 { get => Data[0x32]; set => Data[0x32] = value; } @@ -216,7 +214,7 @@ public override uint PID public bool RIB6_7 { get => (RIB6 & (1 << 7)) == 1 << 7; set => RIB6 = (byte)((RIB6 & ~(1 << 7)) | (value ? 1 << 7 : 0)); } // Unused public int RibbonCountMemoryContest { get => Data[0x38]; set => HasContestMemoryRibbon = (Data[0x38] = (byte)value) != 0; } public int RibbonCountMemoryBattle { get => Data[0x39]; set => HasBattleMemoryRibbon = (Data[0x39] = (byte)value) != 0; } - private ushort DistByte { get => BitConverter.ToUInt16(Data, 0x3A); set => BitConverter.GetBytes(value).CopyTo(Data, 0x3A); } + private ushort DistByte { get => ReadUInt16LittleEndian(Data.AsSpan(0x3A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x3A), value); } public bool DistSuperTrain1 { get => (DistByte & (1 << 0)) == 1 << 0; set => DistByte = (byte)((DistByte & ~(1 << 0)) | (value ? 1 << 0 : 0)); } public bool DistSuperTrain2 { get => (DistByte & (1 << 1)) == 1 << 1; set => DistByte = (byte)((DistByte & ~(1 << 1)) | (value ? 1 << 1 : 0)); } public bool DistSuperTrain3 { get => (DistByte & (1 << 2)) == 1 << 2; set => DistByte = (byte)((DistByte & ~(1 << 2)) | (value ? 1 << 2 : 0)); } @@ -225,7 +223,7 @@ public override uint PID public bool DistSuperTrain6 { get => (DistByte & (1 << 5)) == 1 << 5; set => DistByte = (byte)((DistByte & ~(1 << 5)) | (value ? 1 << 5 : 0)); } public bool Dist7 { get => (DistByte & (1 << 6)) == 1 << 6; set => DistByte = (byte)((DistByte & ~(1 << 6)) | (value ? 1 << 6 : 0)); } public bool Dist8 { get => (DistByte & (1 << 7)) == 1 << 7; set => DistByte = (byte)((DistByte & ~(1 << 7)) | (value ? 1 << 7 : 0)); } - public uint FormArgument { get => BitConverter.ToUInt32(Data, 0x3C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x3C); } + public uint FormArgument { get => ReadUInt32LittleEndian(Data.AsSpan(0x3C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x3C), value); } public byte FormArgumentRemain { get => (byte)FormArgument; set => FormArgument = (FormArgument & ~0xFFu) | value; } public byte FormArgumentElapsed { get => (byte)(FormArgument >> 8); set => FormArgument = (FormArgument & ~0xFF00u) | (uint)(value << 8); } public byte FormArgumentMaximum { get => (byte)(FormArgument >> 16); set => FormArgument = (FormArgument & ~0xFF0000u) | (uint)(value << 16); } @@ -233,7 +231,7 @@ public override uint PID #region Block B public override string Nickname { - get => GetString(0x40, 24); + get => StringConverter7.GetString(Nickname_Trash); set { if (!IsNicknamed) @@ -241,36 +239,36 @@ public override string Nickname int lang = SpeciesName.GetSpeciesNameLanguage(Species, Language, value, 7); if (lang is (int)LanguageID.ChineseS or (int)LanguageID.ChineseT) { - StringConverter.SetString7(value, 12, lang, chinese: true).CopyTo(Data, 0x40); + StringConverter7.SetString(Nickname_Trash, value.AsSpan(), 12, lang, StringConverterOption.None, chinese: true); return; } } - SetString(value, 12).CopyTo(Data, 0x40); + StringConverter7.SetString(Nickname_Trash, value.AsSpan(), 12, 0, StringConverterOption.None); } } public override int Move1 { - get => BitConverter.ToUInt16(Data, 0x5A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5A); + get => ReadUInt16LittleEndian(Data.AsSpan(0x5A)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x5A), (ushort)value); } public override int Move2 { - get => BitConverter.ToUInt16(Data, 0x5C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5C); + get => ReadUInt16LittleEndian(Data.AsSpan(0x5C)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x5C), (ushort)value); } public override int Move3 { - get => BitConverter.ToUInt16(Data, 0x5E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5E); + get => ReadUInt16LittleEndian(Data.AsSpan(0x5E)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x5E), (ushort)value); } public override int Move4 { - get => BitConverter.ToUInt16(Data, 0x60); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x60); + get => ReadUInt16LittleEndian(Data.AsSpan(0x60)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x60), (ushort)value); } public override int Move1_PP { get => Data[0x62]; set => Data[0x62] = (byte)value; } @@ -284,32 +282,32 @@ public override int Move4 public override int RelearnMove1 { - get => BitConverter.ToUInt16(Data, 0x6A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6A); + get => ReadUInt16LittleEndian(Data.AsSpan(0x6A)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x6A), (ushort)value); } public override int RelearnMove2 { - get => BitConverter.ToUInt16(Data, 0x6C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6C); + get => ReadUInt16LittleEndian(Data.AsSpan(0x6C)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x6C), (ushort)value); } public override int RelearnMove3 { - get => BitConverter.ToUInt16(Data, 0x6E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6E); + get => ReadUInt16LittleEndian(Data.AsSpan(0x6E)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x6E), (ushort)value); } public override int RelearnMove4 { - get => BitConverter.ToUInt16(Data, 0x70); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x70); + get => ReadUInt16LittleEndian(Data.AsSpan(0x70)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x70), (ushort)value); } public bool SecretSuperTrainingUnlocked { get => (Data[0x72] & 1) == 1; set => Data[0x72] = (byte)((Data[0x72] & ~1) | (value ? 1 : 0)); } public bool SecretSuperTrainingComplete { get => (Data[0x72] & 2) == 2; set => Data[0x72] = (byte)((Data[0x72] & ~2) | (value ? 2 : 0)); } // 0x73 Unused - private uint IV32 { get => BitConverter.ToUInt32(Data, 0x74); set => BitConverter.GetBytes(value).CopyTo(Data, 0x74); } + private uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x74)); set => WriteUInt32LittleEndian(Data.AsSpan(0x74), value); } public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); } public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); } public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); } @@ -320,7 +318,12 @@ public override int RelearnMove4 public override bool IsNicknamed { get => ((IV32 >> 31) & 1) == 1; set => IV32 = (IV32 & 0x7FFFFFFFu) | (value ? 0x80000000u : 0u); } #endregion #region Block C - public override string HT_Name { get => GetString(0x78, 24); set => SetString(value, 12).CopyTo(Data, 0x78); } + public override string HT_Name + { + get => StringConverter7.GetString(HT_Trash); + set => StringConverter7.SetString(HT_Trash, value.AsSpan(), 12, 0, StringConverterOption.None); + } + public override int HT_Gender { get => Data[0x92]; set => Data[0x92] = (byte)value; } public override int CurrentHandler { get => Data[0x93]; set => Data[0x93] = (byte)value; } public byte Geo1_Region { get => Data[0x94]; set => Data[0x94] = value; } @@ -343,7 +346,7 @@ public override int RelearnMove4 public int HT_Memory { get => Data[0xA5]; set => Data[0xA5] = (byte)value; } public int HT_Feeling { get => Data[0xA6]; set => Data[0xA6] = (byte)value; } // 0xA7 Unused - public int HT_TextVar { get => BitConverter.ToUInt16(Data, 0xA8); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xA8); } + public int HT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0xA8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xA8), (ushort)value); } // 0xAA Unused // 0xAB Unused // 0xAC Unused @@ -352,12 +355,17 @@ public override int RelearnMove4 public override byte Enjoyment { get => Data[0xAF]; set => Data[0xAF] = value; } #endregion #region Block D - public override string OT_Name { get => GetString(0xB0, 24); set => SetString(value, 12).CopyTo(Data, 0xB0); } + public override string OT_Name + { + get => StringConverter7.GetString(OT_Trash); + set => StringConverter7.SetString(OT_Trash, value.AsSpan(), 12, 0, StringConverterOption.None); + } + public override int OT_Friendship { get => Data[0xCA]; set => Data[0xCA] = (byte)value; } public int OT_Affection { get => Data[0xCB]; set => Data[0xCB] = (byte)value; } public int OT_Intensity { get => Data[0xCC]; set => Data[0xCC] = (byte)value; } public int OT_Memory { get => Data[0xCD]; set => Data[0xCD] = (byte)value; } - public int OT_TextVar { get => BitConverter.ToUInt16(Data, 0xCE); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xCE); } + public int OT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0xCE)); set => WriteUInt16LittleEndian(Data.AsSpan(0xCE), (ushort)value); } public int OT_Feeling { get => Data[0xD0]; set => Data[0xD0] = (byte)value; } public override int Egg_Year { get => Data[0xD1]; set => Data[0xD1] = (byte)value; } public override int Egg_Month { get => Data[0xD2]; set => Data[0xD2] = (byte)value; } @@ -366,8 +374,8 @@ public override int RelearnMove4 public override int Met_Month { get => Data[0xD5]; set => Data[0xD5] = (byte)value; } public override int Met_Day { get => Data[0xD6]; set => Data[0xD6] = (byte)value; } // Unused 0xD7 - public override int Egg_Location { get => BitConverter.ToUInt16(Data, 0xD8); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xD8); } - public override int Met_Location { get => BitConverter.ToUInt16(Data, 0xDA); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xDA); } + public override int Egg_Location { get => ReadUInt16LittleEndian(Data.AsSpan(0xD8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xD8), (ushort)value); } + public override int Met_Location { get => ReadUInt16LittleEndian(Data.AsSpan(0xDA)); set => WriteUInt16LittleEndian(Data.AsSpan(0xDA), (ushort)value); } public override int Ball { get => Data[0xDC]; set => Data[0xDC] = (byte)value; } public override int Met_Level { get => Data[0xDD] & ~0x80; set => Data[0xDD] = (byte)((Data[0xDD] & 0x80) | value); } public override int OT_Gender { get => Data[0xDD] >> 7; set => Data[0xDD] = (byte)((Data[0xDD] & ~0x80) | (value << 7)); } @@ -385,18 +393,18 @@ public override int RelearnMove4 public override int Language { get => Data[0xE3]; set => Data[0xE3] = (byte)value; } #endregion #region Battle Stats - public override int Status_Condition { get => BitConverter.ToInt32(Data, 0xE8); set => BitConverter.GetBytes(value).CopyTo(Data, 0xE8); } + public override int Status_Condition { get => ReadInt32LittleEndian(Data.AsSpan(0xE8)); set => WriteInt32LittleEndian(Data.AsSpan(0xE8), value); } public override int Stat_Level { get => Data[0xEC]; set => Data[0xEC] = (byte)value; } public byte DirtType { get => Data[0xED]; set => Data[0xED] = value; } public byte DirtLocation { get => Data[0xEE]; set => Data[0xEE] = value; } // 0xEF unused - public override int Stat_HPCurrent { get => BitConverter.ToUInt16(Data, 0xF0); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF0); } - public override int Stat_HPMax { get => BitConverter.ToUInt16(Data, 0xF2); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF2); } - public override int Stat_ATK { get => BitConverter.ToUInt16(Data, 0xF4); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF4); } - public override int Stat_DEF { get => BitConverter.ToUInt16(Data, 0xF6); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF6); } - public override int Stat_SPE { get => BitConverter.ToUInt16(Data, 0xF8); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xF8); } - public override int Stat_SPA { get => BitConverter.ToUInt16(Data, 0xFA); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xFA); } - public override int Stat_SPD { get => BitConverter.ToUInt16(Data, 0xFC); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xFC); } + public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data.AsSpan(0xF0)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF0), (ushort)value); } + public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data.AsSpan(0xF2)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF2), (ushort)value); } + public override int Stat_ATK { get => ReadUInt16LittleEndian(Data.AsSpan(0xF4)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF4), (ushort)value); } + public override int Stat_DEF { get => ReadUInt16LittleEndian(Data.AsSpan(0xF6)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF6), (ushort)value); } + public override int Stat_SPE { get => ReadUInt16LittleEndian(Data.AsSpan(0xF8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF8), (ushort)value); } + public override int Stat_SPA { get => ReadUInt16LittleEndian(Data.AsSpan(0xFA)); set => WriteUInt16LittleEndian(Data.AsSpan(0xFA), (ushort)value); } + public override int Stat_SPD { get => ReadUInt16LittleEndian(Data.AsSpan(0xFC)); set => WriteUInt16LittleEndian(Data.AsSpan(0xFC), (ushort)value); } #endregion public int SuperTrainingMedalCount(int maxCount = 30) diff --git a/PKHeX.Core/PKM/PK8.cs b/PKHeX.Core/PKM/PK8.cs index cef23a08f..e12f6829f 100644 --- a/PKHeX.Core/PKM/PK8.cs +++ b/PKHeX.Core/PKM/PK8.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -49,7 +50,7 @@ public void Trade(ITrainerInfo tr, int Day = 1, int Month = 1, int Year = 2015) TradeHT(tr); } - public int DynamaxType { get => BitConverter.ToUInt16(Data, 0x156); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x156); } + public int DynamaxType { get => ReadUInt16LittleEndian(Data.AsSpan(0x156)); set => WriteUInt16LittleEndian(Data.AsSpan(0x156), (ushort)value); } public void FixMemories() { diff --git a/PKHeX.Core/PKM/PKM.cs b/PKHeX.Core/PKM/PKM.cs index a8b168ad7..b261a986c 100644 --- a/PKHeX.Core/PKM/PKM.cs +++ b/PKHeX.Core/PKM/PKM.cs @@ -23,19 +23,20 @@ public abstract class PKM : ISpeciesForm, ITrainerID, IGeneration, IShiny, ILang protected PKM(byte[] data) => Data = data; protected PKM(int size) => Data = new byte[size]; - public virtual byte[] EncryptedPartyData => ArrayUtil.Truncate(Encrypt(), SIZE_PARTY); - public virtual byte[] EncryptedBoxData => ArrayUtil.Truncate(Encrypt(), SIZE_STORED); - public virtual byte[] DecryptedPartyData => ArrayUtil.Truncate(Write(), SIZE_PARTY); - public virtual byte[] DecryptedBoxData => ArrayUtil.Truncate(Write(), SIZE_STORED); + public virtual byte[] EncryptedPartyData => Encrypt().AsSpan()[..SIZE_PARTY].ToArray(); + public virtual byte[] EncryptedBoxData => Encrypt().AsSpan()[..SIZE_STORED].ToArray(); + public virtual byte[] DecryptedPartyData => Write().AsSpan()[..SIZE_PARTY].ToArray(); + public virtual byte[] DecryptedBoxData => Write().AsSpan()[..SIZE_STORED].ToArray(); - public virtual bool Valid { get => ChecksumValid && Sanity == 0; set { if (!value) return; Sanity = 0; RefreshChecksum(); } } + /// + /// Rough indication if the data is junk or not. + /// + public abstract bool Valid { get; set; } // Trash Bytes - public abstract Span Nickname_Trash { get; set; } - public abstract Span OT_Trash { get; set; } - public virtual Span HT_Trash { get => Span.Empty; set { } } - - protected virtual ushort CalculateChecksum() => PokeCrypto.GetCHK(Data, SIZE_STORED); + public abstract Span Nickname_Trash { get; } + public abstract Span OT_Trash { get; } + public virtual Span HT_Trash => Span.Empty; protected abstract byte[] Encrypt(); public abstract int Format { get; } @@ -108,8 +109,6 @@ private byte[] Write() public abstract uint EncryptionConstant { get; set; } public abstract uint PID { get; set; } - public abstract ushort Sanity { get; set; } - public abstract ushort Checksum { get; set; } // Misc Properties public abstract int Language { get; set; } @@ -336,7 +335,6 @@ public bool PKRS_Cured } } - public virtual bool ChecksumValid => Checksum == CalculateChecksum(); public int CurrentLevel { get => Experience.GetLevel(EXP, PersonalInfo.EXPGrowth); set => EXP = Experience.GetEXP(Stat_Level = value, PersonalInfo.EXPGrowth); } public int MarkCircle { get => Markings[0]; set { var marks = Markings; marks[0] = value; Markings = marks; } } public int MarkTriangle { get => Markings[1]; set { var marks = Markings; marks[1] = value; Markings = marks; } } @@ -371,13 +369,19 @@ public int FlawlessIVCount public int[] IVs { get => new[] { IV_HP, IV_ATK, IV_DEF, IV_SPE, IV_SPA, IV_SPD }; - set - { - if (value.Length != 6) - return; - IV_HP = value[0]; IV_ATK = value[1]; IV_DEF = value[2]; - IV_SPE = value[3]; IV_SPA = value[4]; IV_SPD = value[5]; - } + set => SetIVs(value); + } + + public void SetIVs(ReadOnlySpan value) + { + if (value.Length != 6) + return; + IV_HP = value[0]; + IV_ATK = value[1]; + IV_DEF = value[2]; + IV_SPE = value[3]; + IV_SPA = value[4]; + IV_SPD = value[5]; } public int[] EVs @@ -575,7 +579,13 @@ public virtual bool IsGenderValid() /// /// Updates the checksum of the . /// - public virtual void RefreshChecksum() => Checksum = CalculateChecksum(); + public abstract void RefreshChecksum(); + + /// + /// Indicates if the data has a proper checksum. + /// + /// Returns true for structures that do not compute or contain a checksum in the structure. + public abstract bool ChecksumValid { get; } /// /// Reorders moves and fixes PP if necessary. @@ -891,7 +901,7 @@ public int[] SetRandomIVs(int? flawless = null) { for (int i = 0; i < count; i++) ivs[i] = MaxIV; - Util.Shuffle(ivs, 0, ivs.Length, rnd); // Randomize IV order + Util.Shuffle(ivs.AsSpan(), 0, ivs.Length, rnd); // Randomize IV order } return IVs = ivs; } diff --git a/PKHeX.Core/PKM/SK2.cs b/PKHeX.Core/PKM/SK2.cs index 27b4810e8..f63beb233 100644 --- a/PKHeX.Core/PKM/SK2.cs +++ b/PKHeX.Core/PKM/SK2.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -39,14 +40,14 @@ public override PKM Clone() => new SK2((byte[])Data.Clone(), Japanese) public override int Move2 { get => Data[3]; set => Data[3] = (byte)value; } public override int Move3 { get => Data[4]; set => Data[4] = (byte)value; } public override int Move4 { get => Data[5]; set => Data[5] = (byte)value; } - public override int TID { get => BigEndian.ToUInt16(Data, 6); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 6); } - public override uint EXP { get => BigEndian.ToUInt32(Data, 8); set => BigEndian.GetBytes(value).CopyTo(Data, 8); } // not 3 bytes like in PK2 - public override int EV_HP { get => BigEndian.ToUInt16(Data, 0x0C); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x0C); } - public override int EV_ATK { get => BigEndian.ToUInt16(Data, 0x0E); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x0E); } - public override int EV_DEF { get => BigEndian.ToUInt16(Data, 0x10); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x10); } - public override int EV_SPE { get => BigEndian.ToUInt16(Data, 0x12); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x12); } - public override int EV_SPC { get => BigEndian.ToUInt16(Data, 0x14); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x14); } - public override ushort DV16 { get => BigEndian.ToUInt16(Data, 0x16); set => BigEndian.GetBytes(value).CopyTo(Data, 0x16); } + public override int TID { get => ReadUInt16BigEndian(Data.AsSpan(6)); set => WriteUInt16BigEndian(Data.AsSpan(6), (ushort)value); } + public override uint EXP { get => ReadUInt32BigEndian(Data.AsSpan(8)); set => WriteUInt32BigEndian(Data.AsSpan(8), value); } // not 3 bytes like in PK2 + public override int EV_HP { get => ReadUInt16BigEndian(Data.AsSpan(0x0C)); set => WriteUInt16BigEndian(Data.AsSpan(0x0C), (ushort)value); } + public override int EV_ATK { get => ReadUInt16BigEndian(Data.AsSpan(0x0E)); set => WriteUInt16BigEndian(Data.AsSpan(0x0E), (ushort)value); } + public override int EV_DEF { get => ReadUInt16BigEndian(Data.AsSpan(0x10)); set => WriteUInt16BigEndian(Data.AsSpan(0x10), (ushort)value); } + public override int EV_SPE { get => ReadUInt16BigEndian(Data.AsSpan(0x12)); set => WriteUInt16BigEndian(Data.AsSpan(0x12), (ushort)value); } + public override int EV_SPC { get => ReadUInt16BigEndian(Data.AsSpan(0x14)); set => WriteUInt16BigEndian(Data.AsSpan(0x14), (ushort)value); } + public override ushort DV16 { get => ReadUInt16BigEndian(Data.AsSpan(0x16)); set => WriteUInt16BigEndian(Data.AsSpan(0x16), value); } public override int Move1_PP { get => Data[0x18] & 0x3F; set => Data[0x18] = (byte)((Data[0x18] & 0xC0) | Math.Min(63, value)); } public override int Move2_PP { get => Data[0x19] & 0x3F; set => Data[0x19] = (byte)((Data[0x19] & 0xC0) | Math.Min(63, value)); } public override int Move3_PP { get => Data[0x1A] & 0x3F; set => Data[0x1A] = (byte)((Data[0x1A] & 0xC0) | Math.Min(63, value)); } @@ -83,31 +84,36 @@ public bool IsRental public override int PKRS_Days { get => PKRS & 0xF; set => PKRS = (byte)((PKRS & ~0xF) | value); } public override int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)((PKRS & 0xF) | value << 4); } - public int CaughtData { get => BigEndian.ToUInt16(Data, 0x21); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x21); } + public int CaughtData { get => ReadUInt16BigEndian(Data.AsSpan(0x21)); set => WriteUInt16BigEndian(Data.AsSpan(0x21), (ushort)value); } public int Met_TimeOfDay { get => (CaughtData >> 14) & 0x3; set => CaughtData = (CaughtData & 0x3FFF) | ((value & 0x3) << 14); } public override int Met_Level { get => (CaughtData >> 8) & 0x3F; set => CaughtData = (CaughtData & 0xC0FF) | ((value & 0x3F) << 8); } public override int OT_Gender { get => (CaughtData >> 7) & 1; set => CaughtData = (CaughtData & 0xFF7F) | ((value & 1) << 7); } public override int Met_Location { get => CaughtData & 0x7F; set => CaughtData = (CaughtData & 0xFF80) | (value & 0x7F); } - public override string Nickname { get => GetString(0x24, StringLength); set => StringConverter12.SetString1(value, 12, Japanese).CopyTo(Data, 0x24); } + public override string Nickname + { + get => StringConverter12.GetString(Nickname_Trash, Japanese); + set => StringConverter12.SetString(Nickname_Trash, value.AsSpan(), 12, Japanese, StringConverterOption.None); + } public override string OT_Name { - get => GetString(0x30, StringLength); + get => StringConverter12.GetString(OT_Trash, Japanese); set { if (IsRental) { - Array.Clear(Data, 0x30, StringLength); + OT_Trash.Clear(); return; } - SetString(value, StringLength).CopyTo(Data, 0x30); + StringConverter12.SetString(OT_Trash, value.AsSpan(), StringLength, Japanese, StringConverterOption.None); } } - public override Span Nickname_Trash { get => Data.AsSpan(0x24, 12); set { if (value.Length == 12) value.CopyTo(Data.AsSpan(0x24)); } } - public override Span OT_Trash { get => Data.AsSpan(0x30, 12); set { if (value.Length == 12) value.CopyTo(Data.AsSpan(0x30)); } } + public override Span Nickname_Trash => Data.AsSpan(0x24, 12); + public override Span OT_Trash => Data.AsSpan(0x30, 12); + #endregion #region Party Attributes @@ -125,19 +131,18 @@ public override string OT_Name public override bool HasOriginalMetLocation => CaughtData != 0; public override int Version { get => (int)GameVersion.GSC; set { } } - private string GetString(int offset, int length) => StringConverter12.GetString1(Data, offset, length - 1, Japanese); - private byte[] SetString(string value, int length) => StringConverter12.SetString1(value, length - 1, Japanese); - protected override byte[] GetNonNickname(int language) { - var name = SpeciesName.GetSpeciesNameGeneration(Species, language, Format); - return SetString(name, StringLength); + var name = SpeciesName.GetSpeciesNameGeneration(Species, language, 2); + byte[] data = new byte[name.Length]; + StringConverter12.SetString(data, name.AsSpan(), data.Length, Japanese, StringConverterOption.Clear50); + return data; } public override void SetNotNicknamed(int language) { - var name = SpeciesName.GetSpeciesNameGeneration(Species, language, Format); - Array.Clear(Data, 0x24, 0xC); + var name = SpeciesName.GetSpeciesNameGeneration(Species, language, 2); + Nickname_Trash.Clear(); Nickname = name; } diff --git a/PKHeX.Core/PKM/Shared/G3PKM.cs b/PKHeX.Core/PKM/Shared/G3PKM.cs index 85f50e78a..a0f508a6f 100644 --- a/PKHeX.Core/PKM/Shared/G3PKM.cs +++ b/PKHeX.Core/PKM/Shared/G3PKM.cs @@ -46,6 +46,10 @@ public sealed override void RefreshAbility(int n) AbilityBit = n == 1 && ((PersonalInfoG3)PersonalInfo).HasSecondAbility; } + public override bool Valid { get => true; set { } } + public override void RefreshChecksum() { } + public override bool ChecksumValid => Valid; + public abstract bool RibbonEarth { get; set; } public abstract bool RibbonNational { get; set; } public abstract bool RibbonCountry { get; set; } diff --git a/PKHeX.Core/PKM/Shared/G4PKM.cs b/PKHeX.Core/PKM/Shared/G4PKM.cs index ff0883308..cfb486619 100644 --- a/PKHeX.Core/PKM/Shared/G4PKM.cs +++ b/PKHeX.Core/PKM/Shared/G4PKM.cs @@ -43,9 +43,16 @@ public sealed override int Characteristic } } + public abstract ushort Sanity { get; set; } + public abstract ushort Checksum { get; set; } + public sealed override void RefreshChecksum() => Checksum = CalculateChecksum(); + public sealed override bool ChecksumValid => CalculateChecksum() == Checksum; + public override bool Valid { get => Sanity == 0 && ChecksumValid; set { if (!value) return; Sanity = 0; RefreshChecksum(); } } + protected virtual ushort CalculateChecksum() => PokeCrypto.GetCHK(Data, PokeCrypto.SIZE_4STORED); + // Trash Bytes - public sealed override Span Nickname_Trash { get => Data.AsSpan(0x48, 22); set { if (value.Length == 22) value.CopyTo(Data.AsSpan(0x48)); } } - public sealed override Span OT_Trash { get => Data.AsSpan(0x68, 16); set { if (value.Length == 16) value.CopyTo(Data.AsSpan(0x68)); } } + public sealed override Span Nickname_Trash => Data.AsSpan(0x48, 22); + public sealed override Span OT_Trash => Data.AsSpan(0x68, 16); // Future Attributes public sealed override uint EncryptionConstant { get => PID; set { } } @@ -169,6 +176,71 @@ public sealed override int Characteristic public abstract byte BallHGSS { get; set; } public abstract byte PokéathlonStat { get; set; } + public abstract ushort Egg_LocationDP { get; set; } + public abstract ushort Egg_LocationExtended { get; set; } + public abstract ushort Met_LocationDP { get; set; } + public abstract ushort Met_LocationExtended { get; set; } + + public sealed override int Egg_Location + { + get + { + ushort hgssloc = Egg_LocationExtended; + if (hgssloc != 0) + return hgssloc; + return Egg_LocationDP; + } + set + { + if (value == 0) + { + Egg_LocationDP = Egg_LocationExtended = 0; + } + else if (Locations.IsPtHGSSLocation(value) || Locations.IsPtHGSSLocationEgg(value)) + { + // Met location not in DP, set to Faraway Place + Egg_LocationDP = Locations.Faraway4; + Egg_LocationExtended = (ushort)value; + } + else + { + int pthgss = PtHGSS ? value : 0; // only set to PtHGSS loc if encountered in game + Egg_LocationDP = (ushort)pthgss; + Egg_LocationExtended = (ushort)value; + } + } + } + + public sealed override int Met_Location + { + get + { + ushort hgssloc = Met_LocationExtended; + if (hgssloc != 0) + return hgssloc; + return Met_LocationDP; + } + set + { + if (value == 0) + { + Met_LocationDP = Met_LocationExtended = 0; + } + else if (Locations.IsPtHGSSLocation(value) || Locations.IsPtHGSSLocationEgg(value)) + { + // Met location not in DP, set to Faraway Place + Met_LocationDP = Locations.Faraway4; + Met_LocationExtended = (ushort)value; + } + else + { + int pthgss = PtHGSS ? value : 0; // only set to PtHGSS loc if encountered in game + Met_LocationDP = (ushort)pthgss; + Met_LocationExtended = (ushort)value; + } + } + } + public sealed override int Ball { // HG/SS added new ball IDs mid-generation, and the previous Gen4 games couldn't handle invalid ball values. diff --git a/PKHeX.Core/PKM/Shared/G6PKM.cs b/PKHeX.Core/PKM/Shared/G6PKM.cs index 9f2022c1a..37c28cb43 100644 --- a/PKHeX.Core/PKM/Shared/G6PKM.cs +++ b/PKHeX.Core/PKM/Shared/G6PKM.cs @@ -1,9 +1,10 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { /// Generation 6 format. - public abstract class G6PKM : PKM + public abstract class G6PKM : PKM, ISanityChecksum { public override int SIZE_PARTY => PokeCrypto.SIZE_6PARTY; public override int SIZE_STORED => PokeCrypto.SIZE_6STORED; @@ -11,15 +12,21 @@ public abstract class G6PKM : PKM protected G6PKM(int size) : base(size) { } // Trash Bytes - public sealed override Span Nickname_Trash { get => Data.AsSpan(0x40, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0x40)); } } - public sealed override Span HT_Trash { get => Data.AsSpan(0x78, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0x78)); } } - public sealed override Span OT_Trash { get => Data.AsSpan(0xB0, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0xB0)); } } + public sealed override Span Nickname_Trash => Data.AsSpan(0x40, 26); + public sealed override Span HT_Trash => Data.AsSpan(0x78, 26); + public sealed override Span OT_Trash => Data.AsSpan(0xB0, 26); - protected sealed override ushort CalculateChecksum() + public abstract ushort Sanity { get; set; } + public abstract ushort Checksum { get; set; } + public sealed override void RefreshChecksum() => Checksum = CalculateChecksum(); + public sealed override bool ChecksumValid => CalculateChecksum() == Checksum; + public sealed override bool Valid { get => Sanity == 0 && ChecksumValid; set { if (!value) return; Sanity = 0; RefreshChecksum(); } } + + private ushort CalculateChecksum() { ushort chk = 0; for (int i = 8; i < PokeCrypto.SIZE_6STORED; i += 2) // don't use SIZE_STORED property; pb7 overrides stored size - chk += BitConverter.ToUInt16(Data, i); + chk += ReadUInt16LittleEndian(Data.AsSpan(i)); return chk; } diff --git a/PKHeX.Core/PKM/Shared/GBPKM.cs b/PKHeX.Core/PKM/Shared/GBPKM.cs index a0f295d15..6eb55aedd 100644 --- a/PKHeX.Core/PKM/Shared/GBPKM.cs +++ b/PKHeX.Core/PKM/Shared/GBPKM.cs @@ -28,6 +28,9 @@ public abstract class GBPKM : PKM public sealed override byte[] DecryptedBoxData => Encrypt(); public sealed override byte[] DecryptedPartyData => Encrypt(); + public override bool Valid { get => true; set { } } + public sealed override void RefreshChecksum() { } + protected abstract byte[] GetNonNickname(int language); private bool? _isnicknamed; @@ -101,9 +104,7 @@ public sealed override int Gender public sealed override uint EncryptionConstant { get => 0; set { } } public sealed override uint PID { get => 0; set { } } public sealed override int Nature { get => 0; set { } } - public sealed override ushort Sanity { get => 0; set { } } public sealed override bool ChecksumValid => true; - public sealed override ushort Checksum { get => 0; set { } } public sealed override bool FatefulEncounter { get => false; set { } } public sealed override int TSV => 0x0000; public sealed override int PSV => 0xFFFF; diff --git a/PKHeX.Core/PKM/Shared/GBPKML.cs b/PKHeX.Core/PKM/Shared/GBPKML.cs index 13b649b4c..5b70220dc 100644 --- a/PKHeX.Core/PKM/Shared/GBPKML.cs +++ b/PKHeX.Core/PKM/Shared/GBPKML.cs @@ -18,8 +18,8 @@ public abstract class GBPKML : GBPKM internal readonly byte[] RawNickname; // Trash Bytes - public sealed override Span Nickname_Trash { get => RawNickname; set { if (value.Length == RawNickname.Length) value.CopyTo(RawNickname); } } - public sealed override Span OT_Trash { get => RawOT; set { if (value.Length == RawOT.Length) value.CopyTo(RawOT); } } + public sealed override Span Nickname_Trash => RawNickname; + public sealed override Span OT_Trash => RawOT; protected GBPKML(int size, bool jp = false) : base(size) { @@ -28,8 +28,8 @@ protected GBPKML(int size, bool jp = false) : base(size) // initialize string buffers RawOT = new byte[strLen]; RawNickname = new byte[strLen]; - for (int i = 0; i < RawOT.Length; i++) - RawOT[i] = RawNickname[i] = StringConverter12.G1TerminatorCode; + RawOT.AsSpan().Fill(StringConverter12.G1TerminatorCode); + RawNickname.AsSpan().Fill(StringConverter12.G1TerminatorCode); } protected GBPKML(byte[] data, bool jp = false) : base(data) @@ -39,8 +39,8 @@ protected GBPKML(byte[] data, bool jp = false) : base(data) // initialize string buffers RawOT = new byte[strLen]; RawNickname = new byte[strLen]; - for (int i = 0; i < RawOT.Length; i++) - RawOT[i] = RawNickname[i] = StringConverter12.G1TerminatorCode; + RawOT.AsSpan().Fill(StringConverter12.G1TerminatorCode); + RawNickname.AsSpan().Fill(StringConverter12.G1TerminatorCode); } public override void SetNotNicknamed(int language) => GetNonNickname(language).CopyTo(RawNickname); @@ -49,7 +49,8 @@ protected override byte[] GetNonNickname(int language) { var name = SpeciesName.GetSpeciesNameGeneration(Species, language, Format); var len = Nickname_Trash.Length; - var data = SetString(name, len, len, 0x50); + byte[] data = new byte[len]; + SetString(name.AsSpan(), data, len, StringConverterOption.Clear50); if (!Korean) { // Decimal point<->period fix @@ -62,11 +63,11 @@ protected override byte[] GetNonNickname(int language) return data; } - private byte[] SetString(string value, int maxLength, int padTo = 0, byte padWith = 0) + private void SetString(ReadOnlySpan value, Span destBuffer, int maxLength, StringConverterOption option = StringConverterOption.None) { if (Korean) - return StringConverter2KOR.SetString2KOR(value, maxLength - 1, padTo, padWith); - return StringConverter12.SetString1(value, maxLength - 1, Japanese, padTo, padWith); + StringConverter2KOR.SetString(value, destBuffer, maxLength, option); + StringConverter12.SetString(destBuffer, value, maxLength, Japanese, option); } public sealed override string Nickname @@ -74,15 +75,15 @@ public sealed override string Nickname get { if (Korean) - return StringConverter2KOR.GetString2KOR(RawNickname, 0, RawNickname.Length); - return StringConverter12.GetString1(RawNickname, 0, RawNickname.Length, Japanese); + return StringConverter2KOR.GetString(RawNickname); + return StringConverter12.GetString(RawNickname, Japanese); } set { if (!IsNicknamed && Nickname == value) return; - SetStringKeepTerminatorStyle(value, RawNickname); + SetStringKeepTerminatorStyle(value.AsSpan(), RawNickname); } } @@ -91,28 +92,27 @@ public sealed override string OT_Name get { if (Korean) - return StringConverter2KOR.GetString2KOR(RawOT, 0, RawOT.Length); - return StringConverter12.GetString1(RawOT, 0, RawOT.Length, Japanese); + return StringConverter2KOR.GetString(RawOT.AsSpan()); + return StringConverter12.GetString(RawOT.AsSpan(), Japanese); } set { if (value == OT_Name) return; - SetStringKeepTerminatorStyle(value, RawOT); + SetStringKeepTerminatorStyle(value.AsSpan(), RawOT); } } - private void SetStringKeepTerminatorStyle(string value, byte[] exist) + private void SetStringKeepTerminatorStyle(ReadOnlySpan value, Span exist) { // Reset the destination buffer based on the termination style of the existing string. - bool zeroed = Array.IndexOf(exist, (byte)0) != -1; + bool zeroed = exist.IndexOf((byte)0) != -1; byte fill = zeroed ? (byte)0 : StringConverter12.G1TerminatorCode; for (int i = 0; i < exist.Length; i++) exist[i] = fill; int finalLength = Math.Min(value.Length + 1, exist.Length); - byte[] strdata = SetString(value, finalLength); - strdata.CopyTo(exist, 0); + SetString(value, exist, finalLength); } } } diff --git a/PKHeX.Core/PKM/Shared/ISanityChecksum.cs b/PKHeX.Core/PKM/Shared/ISanityChecksum.cs new file mode 100644 index 000000000..9f130f5c1 --- /dev/null +++ b/PKHeX.Core/PKM/Shared/ISanityChecksum.cs @@ -0,0 +1,8 @@ +namespace PKHeX.Core +{ + public interface ISanityChecksum + { + ushort Sanity { get; set; } + ushort Checksum { get; set; } + } +} diff --git a/PKHeX.Core/PKM/Shared/PokeList1.cs b/PKHeX.Core/PKM/Shared/PokeList1.cs index 62b85598a..fb9e717c4 100644 --- a/PKHeX.Core/PKM/Shared/PokeList1.cs +++ b/PKHeX.Core/PKM/Shared/PokeList1.cs @@ -1,3 +1,5 @@ +using System; + namespace PKHeX.Core { /// @@ -6,7 +8,14 @@ namespace PKHeX.Core public sealed class PokeList1 : PokeListGB { protected override byte GetSpeciesBoxIdentifier(PK1 pk) => SpeciesConverter.SetG1Species(pk.Species); - protected override PK1 GetEntry(byte[] dat, byte[] otname, byte[] nick, bool egg) => new(dat, Japanese) { OT_Trash = otname, Nickname_Trash = nick }; + protected override PK1 GetEntry(byte[] dat, byte[] otname, byte[] nick, bool egg) + { + var result = new PK1(dat, Japanese); // no eggs + otname.AsSpan().CopyTo(result.OT_Trash); + nick.AsSpan().CopyTo(result.Nickname_Trash); + return result; + } + protected override int GetEntrySize() => GetEntrySize(IsFormatParty); public PokeList1(byte[] d, PokeListType c = PokeListType.Single, bool jp = false) : base(d, c, jp) { } diff --git a/PKHeX.Core/PKM/Shared/PokeList2.cs b/PKHeX.Core/PKM/Shared/PokeList2.cs index 537188064..1a4514bc6 100644 --- a/PKHeX.Core/PKM/Shared/PokeList2.cs +++ b/PKHeX.Core/PKM/Shared/PokeList2.cs @@ -1,3 +1,5 @@ +using System; + namespace PKHeX.Core { /// @@ -6,7 +8,14 @@ namespace PKHeX.Core public sealed class PokeList2 : PokeListGB { protected override byte GetSpeciesBoxIdentifier(PK2 pk) => pk.IsEgg ? PK2.EggSpeciesValue : (byte)pk.Species; - protected override PK2 GetEntry(byte[] dat, byte[] otname, byte[] nick, bool egg) => new(dat, Japanese) { OT_Trash = otname, Nickname_Trash = nick, IsEgg = egg }; + protected override PK2 GetEntry(byte[] dat, byte[] otname, byte[] nick, bool egg) + { + var result = new PK2(dat, Japanese) { IsEgg = egg }; + otname.AsSpan().CopyTo(result.OT_Trash); + nick.AsSpan().CopyTo(result.Nickname_Trash); + return result; + } + protected override int GetEntrySize() => GetEntrySize(IsFormatParty); public PokeList2(byte[] d, PokeListType c = PokeListType.Single, bool jp = false) : base(d, c, jp) { } diff --git a/PKHeX.Core/PKM/Shared/QRPK7.cs b/PKHeX.Core/PKM/Shared/QRPK7.cs index 6cb49b4a6..2690ac0a5 100644 --- a/PKHeX.Core/PKM/Shared/QRPK7.cs +++ b/PKHeX.Core/PKM/Shared/QRPK7.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -15,7 +16,7 @@ public sealed class QRPK7 : IEncounterInfo public const int SIZE = 0x30; public QRPK7(byte[] d) => Data = (byte[])d.Clone(); - public uint EncryptionConstant => BitConverter.ToUInt32(Data, 0); + public uint EncryptionConstant => ReadUInt32LittleEndian(Data.AsSpan(0)); public int HT_Flags => Data[4]; public int Unk_5 => Data[5]; public int Unk_6 => Data[6]; @@ -24,20 +25,20 @@ public sealed class QRPK7 : IEncounterInfo public int Move2_PPUps => Data[9]; public int Move3_PPUps => Data[0xA]; public int Move4_PPUps => Data[0xB]; - public uint IV32 { get => BitConverter.ToUInt32(Data, 0xC); set => BitConverter.GetBytes(value).CopyTo(Data, 0xC); } + public uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0xC)); set => WriteUInt32LittleEndian(Data.AsSpan(0xC), value); } public int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 00)) | (uint)((value > 31 ? 31 : value) << 00)); } public int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 05)) | (uint)((value > 31 ? 31 : value) << 05)); } public int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 10)) | (uint)((value > 31 ? 31 : value) << 10)); } public int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 15)) | (uint)((value > 31 ? 31 : value) << 15)); } public int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 20)) | (uint)((value > 31 ? 31 : value) << 20)); } public int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 25)) | (uint)((value > 31 ? 31 : value) << 25)); } - public uint PID => BitConverter.ToUInt32(Data, 0x10); - public int Species => BitConverter.ToUInt16(Data, 0x14); - public ushort HeldItem => BitConverter.ToUInt16(Data, 0x16); - public ushort Move1 => BitConverter.ToUInt16(Data, 0x18); - public ushort Move2 => BitConverter.ToUInt16(Data, 0x1A); - public ushort Move3 => BitConverter.ToUInt16(Data, 0x1C); - public ushort Move4 => BitConverter.ToUInt16(Data, 0x1E); + public uint PID => ReadUInt32LittleEndian(Data.AsSpan(0x10)); + public int Species => ReadUInt16LittleEndian(Data.AsSpan(0x14)); + public ushort HeldItem => ReadUInt16LittleEndian(Data.AsSpan(0x16)); + public ushort Move1 => ReadUInt16LittleEndian(Data.AsSpan(0x18)); + public ushort Move2 => ReadUInt16LittleEndian(Data.AsSpan(0x1A)); + public ushort Move3 => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); + public ushort Move4 => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); public int Unk_20 => Data[0x20]; public int AbilityIndex => Data[0x21]; public int Nature => Data[0x22]; diff --git a/PKHeX.Core/PKM/Strings/StringConverter.cs b/PKHeX.Core/PKM/Strings/StringConverter.cs index d488b5c8b..5d81c4e6c 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter.cs @@ -1,290 +1,125 @@ using System; -using System.Text; -using static PKHeX.Core.StringConverter7ZH; -namespace PKHeX.Core +namespace PKHeX.Core; + +/// +/// Logic for converting a between the various generation specific encoding formats. +/// +public static class StringConverter { /// - /// Logic for converting a between the various generation specific encoding formats. + /// Converts bytes to a string according to the input parameters. /// - public static class StringConverter + /// Encoded data + /// Generation string format + /// Encoding is Japanese + /// Encoding is Big Endian + /// Decoded string. + public static string GetString(ReadOnlySpan data, int generation, bool jp, bool isBigEndian = false) => generation switch { - private const char TerminatorNull = (char)0; - private const char TerminatorFFFF = (char)0xFFFF; + 3 when isBigEndian => StringConverter3GC.GetString(data), + 4 when isBigEndian => StringConverter4GC.GetString(data), - /// - /// Converts bytes to a string according to the input parameters. - /// - /// Encoded data - /// Generation string format - /// Encoding is Japanese - /// Encoding is Big Endian - /// Length of data to read. - /// Offset to read from - /// Decoded string. - public static string GetString(byte[] data, int generation, bool jp, bool isBigEndian, int count, int offset = 0) + 1 or 2 => StringConverter12.GetString(data, jp), + 3 => StringConverter3.GetString(data, jp), + 4 => StringConverter4.GetString(data), + 5 => StringConverter5.GetString(data), + 6 => StringConverter6.GetString(data), + 7 => StringConverter7.GetString(data), + 8 => StringConverter8.GetString(data), + _ => throw new ArgumentOutOfRangeException(nameof(generation)), + }; + + /// + /// Gets the bytes for a Generation specific string according to the input parameters. + /// + /// + /// Decoded string. + /// Maximum length of the input + /// + /// Generation string format + /// Encoding is Japanese + /// Encoding is Big Endian + /// Language specific conversion (Chinese) + /// Encoded data. + public static int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option, + int generation, bool jp, bool isBigEndian, int language = 0) => generation switch + { + 3 when isBigEndian => StringConverter3GC.SetString(destBuffer, value, maxLength, option), + 4 when isBigEndian => StringConverter4GC.SetString(destBuffer, value, maxLength, option), + + 1 or 2 => StringConverter12.SetString(destBuffer, value, maxLength, jp, option), + 3 => StringConverter3.SetString(destBuffer, value, maxLength, jp, option), + 4 => StringConverter4.SetString(destBuffer, value, maxLength, option), + 5 => StringConverter5.SetString(destBuffer, value, maxLength, option), + 6 => StringConverter6.SetString(destBuffer, value, maxLength, option), + 7 => StringConverter7.SetString(destBuffer, value, maxLength, language, option), + 8 => StringConverter8.SetString(destBuffer, value, maxLength, option), + _ => throw new ArgumentOutOfRangeException(nameof(generation)), + }; + + /// + /// Converts full width to single width + /// + /// Input character to sanitize. + internal static char SanitizeChar(char chr) => chr switch + { + '\uE08F' => '♀', + '\uE08E' => '♂', + '\u246E' => '♀', + '\u246D' => '♂', + _ => chr, + }; + + /// + /// Converts full width to half width when appropriate + /// + /// Input character to set back to data + /// Checks if the overall string is full-width + internal static char UnSanitizeChar(char chr, bool fullWidth = false) + { + if (fullWidth) // jp/ko/zh strings + return chr; // keep as full width + + return chr switch { - if (isBigEndian) - return generation == 3 ? StringConverter3.GetBEString3(data, offset, count) : StringConverter4.GetBEString4(data, offset, count); + '\u2640' => '\uE08F', + '\u2642' => '\uE08E', + _ => chr, + }; + } - return generation switch - { - 1 or 2 => StringConverter12.GetString1(data, offset, count, jp), - 3 => StringConverter3.GetString3(data, offset, count, jp), - 4 => StringConverter4.GetString4(data, offset, count), - 5 => GetString5(data, offset, count), - 6 => GetString6(data, offset, count), - 7 => GetString7(data, offset, count), - _ => GetString7b(data, offset, count), - }; + /// + /// Converts full width to half width when appropriate, for Gen5 and prior. + /// + /// Input character to set back to data + internal static char UnSanitizeChar5(char chr) => chr switch + { + '\u2640' => '\u246E', + '\u2642' => '\u246D', + _ => chr, + }; + + internal static bool GetIsFullWidthString(ReadOnlySpan str) + { + foreach (var c in str) + { + if (c >> 12 is (0 or 0xE)) + continue; + if (c is '\u2640' or '\u2642') // ♀♂ + continue; + return true; } + return false; + } - /// - /// Gets the bytes for a Generation specific string according to the input parameters. - /// - /// Decoded string. - /// Generation string format - /// Encoding is Japanese - /// Encoding is Big Endian - /// Maximum length of the input - /// Language specific conversion (Chinese) - /// Pad the input to given length - /// Pad the input with this character value - /// Encoded data. - public static byte[] SetString(string value, int generation, bool jp, bool isBigEndian, int maxLength, int language = 0, int padTo = 0, ushort padWith = 0) + public static bool HasEastAsianScriptCharacters(ReadOnlySpan str) + { + foreach (var c in str) { - if (isBigEndian) - return generation == 3 ? StringConverter3.SetBEString3(value, maxLength, padTo, padWith) : StringConverter4.SetBEString4(value, maxLength, padTo, padWith); - - return generation switch - { - 1 or 2 => StringConverter12.SetString1(value, maxLength, jp, padTo, (byte)padWith), - 3 => StringConverter3.SetString3(value, maxLength, jp, padTo, padWith), - 4 => StringConverter4.SetString4(value, maxLength, padTo, padWith), - 5 => SetString5(value, maxLength, padTo, padWith), - 6 => SetString6(value, maxLength, padTo, padWith), - 7 => SetString7(value, maxLength, language, padTo, padWith), - _ => SetString7b(value, maxLength, padTo, padWith), - }; - } - - /// Converts Generation 5 encoded data to decoded string. - /// Encoded data - /// Offset to read from - /// Length of data to read. - /// Decoded string. - public static string GetString5(byte[] data, int offset, int count) - { - var raw = Encoding.Unicode.GetString(data, offset, count); - var sb = new StringBuilder(raw); - Util.TrimFromFirst(sb, TerminatorFFFF); - SanitizeString(sb); - return sb.ToString(); - } - - /// Gets the bytes for a Generation 5 string. - /// Decoded string. - /// Maximum length of the input - /// Pad the input to given length - /// Pad the input with this character value - /// Encoded data. - public static byte[] SetString5(string value, int maxLength, int padTo = 0, ushort padWith = 0) - { - var sb = new StringBuilder(value, Math.Max(maxLength, padTo)); - var delta = sb.Length - maxLength; - if (delta > 0) - sb.Remove(maxLength, delta); - - // Replace Special Characters and add Terminator - UnSanitizeString(sb, 5); - sb.Append(TerminatorFFFF); - var d2 = padTo - sb.Length; - if (d2 > 0) - sb.Append((char)padWith, d2); - - return Encoding.Unicode.GetBytes(sb.ToString()); - } - - /// Converts Generation 6 encoded data to decoded string. - /// Encoded data - /// Offset to read from - /// Length of data to read. - /// Decoded string. - public static string GetString6(byte[] data, int offset, int count) - { - var raw = Encoding.Unicode.GetString(data, offset, count); - var sb = new StringBuilder(raw); - Util.TrimFromFirst(sb, TerminatorNull); - SanitizeString(sb); - return sb.ToString(); - } - - /// Gets the bytes for a Generation 6 string. - /// Decoded string. - /// Maximum length of the input - /// Pad the input to given length - /// Pad the input with this character value - /// Encoded data. - public static byte[] SetString6(string value, int maxLength, int padTo = 0, ushort padWith = 0) - { - var sb = new StringBuilder(value); - var delta = sb.Length - maxLength; - if (delta > 0) - sb.Remove(maxLength, delta); - - // Replace Special Characters and add Terminator - UnSanitizeString(sb, 6); - sb.Append(TerminatorNull); - var d2 = padTo - sb.Length; - if (d2 > 0) - sb.Append((char)padWith, d2); - - return Encoding.Unicode.GetBytes(sb.ToString()); - } - - /// Converts Generation 7 encoded data to decoded string. - /// Encoded data - /// Offset to read from - /// Length of data to read. - /// Decoded string. - public static string GetString7(byte[] data, int offset, int count) - { - var raw = Encoding.Unicode.GetString(data, offset, count); - var sb = new StringBuilder(raw); - Util.TrimFromFirst(sb, TerminatorNull); - SanitizeString(sb); - RemapChineseGlyphsBin2String(sb); - return sb.ToString(); - } - - /// Converts Generation 7-Beluga encoded data to decoded string. - /// Encoded data - /// Offset to read from - /// Length of data to read. - /// Decoded string. - public static string GetString7b(byte[] data, int offset, int count) - { - var raw = Encoding.Unicode.GetString(data, offset, count); - var sb = new StringBuilder(raw); - Util.TrimFromFirst(sb, TerminatorNull); - SanitizeString(sb); - //RemapChineseGlyphsBin2String(sb); - return sb.ToString(); - } - - /// Gets the bytes for a Generation 7 string. - /// Decoded string. - /// Maximum length of the input - /// Language specific conversion (Chinese) - /// Pad the input to given length - /// Pad the input with this character value - /// Chinese string remapping should be attempted - /// Encoded data. - public static byte[] SetString7(string value, int maxLength, int language, int padTo = 0, ushort padWith = 0, bool chinese = false) - { - var sb = new StringBuilder(value); - var delta = sb.Length - maxLength; - if (delta > 0) - sb.Remove(maxLength, delta); - if (chinese) - ConvertString2BinG7_zh(sb, language); - - // Replace Special Characters and add Terminator - UnSanitizeString(sb, 7); - sb.Append(TerminatorNull); - var d2 = padTo - sb.Length; - if (d2 > 0) - sb.Append((char)padWith, d2); - - return Encoding.Unicode.GetBytes(sb.ToString()); - } - - /// Gets the bytes for a Generation 7-Beluga string. - /// Decoded string. - /// Maximum length of the input - /// Pad the input to given length - /// Pad the input with this character value - /// Encoded data. - public static byte[] SetString7b(string value, int maxLength, int padTo = 0, ushort padWith = 0) - { - var sb = new StringBuilder(value); - var delta = sb.Length - maxLength; - if (delta > 0) - sb.Remove(maxLength, delta); - - // Replace Special Characters and add Terminator - // No special characters! - sb.Append(TerminatorNull); - var d2 = padTo - sb.Length; - if (d2 > 0) - sb.Append((char)padWith, d2); - - return Encoding.Unicode.GetBytes(sb.ToString()); - } - - /// - /// Converts full width to single width - /// - /// Input string to sanitize. - /// - internal static void SanitizeString(StringBuilder s) - { - if (s.Length == 0) - return; - - // remap custom glyphs to unicode - s.Replace('\uE08F', '♀'); // ♀ (gen6+) - s.Replace('\uE08E', '♂'); // ♂ (gen6+) - s.Replace('\u246E', '♀'); // ♀ (gen5) - s.Replace('\u246D', '♂'); // ♂ (gen5) - } - - /// - /// Converts full width to half width when appropriate - /// - /// Input string to set. - /// Generation specific context - /// - internal static void UnSanitizeString(StringBuilder str, int generation) - { - if (generation <= 5) - { - str.Replace('\u2640', '\u246E'); // ♀ - str.Replace('\u2642', '\u246D'); // ♂ - return; - } - - bool fullwidth = GetIsFullWidthString(str); - if (fullwidth) // jp/ko/zh strings - return; // keep as full width - - // Convert back to half width glyphs - str.Replace('\u2640', '\uE08F'); // ♀ - str.Replace('\u2642', '\uE08E'); // ♂ - } - - private static bool GetIsFullWidthString(StringBuilder str) - { - for (int i = 0; i < str.Length; i++) - { - var c = str[i]; - if (c >> 12 is (0 or 0xE)) - continue; - if (c is '\u2640' or '\u2642') // ♀♂ - continue; + if (c is >= '\u4E00' and <= '\u9FFF') return true; - } - return false; - } - - public static bool HasEastAsianScriptCharacters(string str) - { - foreach (var c in str) - { - if (c is >= '\u4E00' and <= '\u9FFF') - return true; - } - return false; } + return false; } } diff --git a/PKHeX.Core/PKM/Strings/StringConverter12.cs b/PKHeX.Core/PKM/Strings/StringConverter12.cs index a290c3318..e5edc9385 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter12.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter12.cs @@ -1,757 +1,784 @@ using System; using System.Collections.Generic; -using System.Text; -namespace PKHeX.Core +namespace PKHeX.Core; + +/// +/// Logic for converting a for Generation 1 & 2 games. +/// +public static class StringConverter12 { - /// - /// Logic for converting a for Generation 1 & 2 games. - /// - public static class StringConverter12 + public static bool GetIsG1Japanese(ReadOnlySpan str) => AllCharsInDictionary(str, U2RBY_J); + public static bool GetIsG1English(ReadOnlySpan str) => AllCharsInDictionary(str, U2RBY_U); + public static bool GetIsG1Japanese(ReadOnlySpan raw) => AllCharsInDictionary(raw, RBY2U_J); + public static bool GetIsG1English(ReadOnlySpan raw) => AllCharsInDictionary(raw, RBY2U_U); + + private static bool AllCharsInDictionary(ReadOnlySpan c, IReadOnlyDictionary d) { - public static bool GetIsG1Japanese(ReadOnlySpan str) => AllCharsInDictionary(str, U2RBY_J); - public static bool GetIsG1English(ReadOnlySpan str) => AllCharsInDictionary(str, U2RBY_U); - public static bool GetIsG1Japanese(ReadOnlySpan raw) => AllCharsInDictionary(raw, RBY2U_J); - public static bool GetIsG1English(ReadOnlySpan raw) => AllCharsInDictionary(raw, RBY2U_U); - - private static bool AllCharsInDictionary(ReadOnlySpan c, IReadOnlyDictionary d) + foreach (var x in c) { - foreach (var x in c) - { - if (!d.ContainsKey(x)) - return false; - } - return true; + if (!d.ContainsKey(x)) + return false; } - - private static bool AllCharsInDictionary(ReadOnlySpan data, IReadOnlyDictionary d) - { - foreach (var c in data) - { - if (c == 0) - break; - if (!d.ContainsKey(c)) - return false; - } - return true; - } - - public const byte G1TerminatorCode = 0x50; - public const char G1Terminator = '\0'; - public const byte G1TradeOTCode = 0x5D; - public const char G1TradeOT = '*'; - public const string G1TradeOTStr = "*"; - - public static readonly IReadOnlyList G1TradeOTName = new [] - { - string.Empty, - "トレーナー", - "Trainer", - "Dresseur", - "Allenatore", - "Trainer", - string.Empty, - "Entrenador", - "트레이너", - }; - - /// - /// Checks if the input byte array is definitely of German origin (any ÄÖÜäöü) - /// - /// Raw string bytes - /// Indication if the data is from a definitely-german string - public static bool IsG12German(ReadOnlySpan data) - { - foreach (var b in data) - { - if (b is >= 0xC0 and <= 0xC6) - return true; - } - return false; - } - - /// - /// Checks if the input byte array is definitely of German origin (any ÄÖÜäöü) - /// - /// Input string - /// Indication if the data is from a definitely-german string - public static bool IsG12German(string value) => IsG12German(SetString1(value, value.Length, false)); - - /// - /// Converts Generation 1 encoded data into a string. - /// - /// Encoded data. - /// Offset to read from - /// - /// Data source is Japanese. - /// Decoded string. - public static string GetString1(byte[] data, int offset, int count, bool jp) => GetString1(data.AsSpan(offset, count), jp); - - /// - /// Converts Generation 1 encoded data into a string. - /// - /// Encoded data. - /// Data source is Japanese. - /// Decoded string. - public static string GetString1(ReadOnlySpan data, bool jp) - { - var dict = jp ? RBY2U_J : RBY2U_U; - - var s = new StringBuilder(data.Length); - foreach (var val in data) - { - if (!dict.TryGetValue(val, out var c)) // Take valid values - break; - if (c == G1Terminator) // Stop if Terminator - break; - s.Append(c); - } - StringConverter.SanitizeString(s); - return s.ToString(); - } - - /// - /// Converts a string to Generation 1 encoded data. - /// - /// Decoded string. - /// Maximum length of the input - /// Data destination is Japanese. - /// Pad the input to given length - /// Pad the input with this character value - /// Encoded data. - public static byte[] SetString1(string value, int maxLength, bool jp, int padTo = 0, byte padWith = 0) - { - if (value.StartsWith(G1TradeOTStr)) // Handle "[TRAINER]" - return new[] { G1TradeOTCode, G1TerminatorCode }; - - if (value.Length > maxLength) - value = value[..maxLength]; // Hard cap - - var capacity = Math.Max(value.Length, padTo); - var arr = new List(capacity); - var dict = jp ? U2RBY_J : U2RBY_U; - foreach (char c in value) - { - if (!dict.TryGetValue(c, out byte val)) - break; - arr.Add(val); - } - arr.Add(G1TerminatorCode); - while (arr.Count < padTo) - arr.Add(padWith); - return arr.ToArray(); - } - - /// - /// Converts Generation 1 encoded character to string. - /// - /// Encoded character. - /// Data source is Japanese. - /// Decoded string. - public static char GetG1Char(byte key, bool jp) - { - var dict = jp ? RBY2U_J : RBY2U_U; - if (dict.TryGetValue(key, out var val)) - return val; - return G1Terminator; - } - - #region Gen 1/2 Character Tables - - internal static readonly Dictionary RBY2U_U = new() - { - {0x50, G1Terminator}, - {0x5D, G1TradeOT}, - {0x7F, ' '}, - {0x80, 'A'}, - {0x81, 'B'}, - {0x82, 'C'}, - {0x83, 'D'}, - {0x84, 'E'}, - {0x85, 'F'}, - {0x86, 'G'}, - {0x87, 'H'}, - {0x88, 'I'}, - {0x89, 'J'}, - {0x8A, 'K'}, - {0x8B, 'L'}, - {0x8C, 'M'}, - {0x8D, 'N'}, - {0x8E, 'O'}, - {0x8F, 'P'}, - {0x90, 'Q'}, - {0x91, 'R'}, - {0x92, 'S'}, - {0x93, 'T'}, - {0x94, 'U'}, - {0x95, 'V'}, - {0x96, 'W'}, - {0x97, 'X'}, - {0x98, 'Y'}, - {0x99, 'Z'}, - {0x9A, '('}, - {0x9B, ')'}, - {0x9C, ':'}, - {0x9D, ';'}, - {0x9E, '['}, - {0x9F, ']'}, - {0xA0, 'a'}, - {0xA1, 'b'}, - {0xA2, 'c'}, - {0xA3, 'd'}, - {0xA4, 'e'}, - {0xA5, 'f'}, - {0xA6, 'g'}, - {0xA7, 'h'}, - {0xA8, 'i'}, - {0xA9, 'j'}, - {0xAA, 'k'}, - {0xAB, 'l'}, - {0xAC, 'm'}, - {0xAD, 'n'}, - {0xAE, 'o'}, - {0xAF, 'p'}, - {0xB0, 'q'}, - {0xB1, 'r'}, - {0xB2, 's'}, - {0xB3, 't'}, - {0xB4, 'u'}, - {0xB5, 'v'}, - {0xB6, 'w'}, - {0xB7, 'x'}, - {0xB8, 'y'}, - {0xB9, 'z'}, - - // unused characters - {0xBA, 'à'}, - {0xBB, 'è'}, - {0xBC, 'é'}, - {0xBD, 'ù'}, - {0xBE, 'À'}, - {0xBF, 'Á'}, // Used in Spanish FALCÁN in-game trade, inaccessible from keyboard - - {0xC0, 'Ä'}, - {0xC1, 'Ö'}, - {0xC2, 'Ü'}, - {0xC3, 'ä'}, - {0xC4, 'ö'}, - {0xC5, 'ü'}, - - // unused characters - {0xC6, 'È'}, - {0xC7, 'É'}, - {0xC8, 'Ì'}, - {0xC9, 'Í'}, // Used in Spanish MANÍA in-game trade, inaccessible from keyboard - {0xCA, 'Ñ'}, - {0xCB, 'Ò'}, - {0xCC, 'Ó'}, - {0xCD, 'Ù'}, - {0xCE, 'Ú'}, - {0xCF, 'á'}, - {0xD0, 'ì'}, - {0xD1, 'í'}, - {0xD2, 'ñ'}, - {0xD3, 'ò'}, - {0xD4, 'ó'}, - {0xD5, 'ú'}, - - {0xE0, '’'}, - {0xE1, '{'}, /* Pk */ - {0xE2, '}'}, /* Mn */ - {0xE3, '-'}, - {0xE6, '?'}, - {0xE7, '!'}, - {0xE8, '.'}, // Alias decimal point to . - {0xEF, '♂'}, - {0xF1, '×'}, - {0xF2, '.'}, - {0xF3, '/'}, - {0xF4, ','}, - {0xF5, '♀'}, - {0xF6, '0'}, - {0xF7, '1'}, - {0xF8, '2'}, - {0xF9, '3'}, - {0xFA, '4'}, - {0xFB, '5'}, - {0xFC, '6'}, - {0xFD, '7'}, - {0xFE, '8'}, - {0xFF, '9'}, - }; - - private static readonly Dictionary RBY2U_J = new() - { - {0x05, 'ガ'}, - {0x06, 'ギ'}, - {0x07, 'グ'}, - {0x08, 'ゲ'}, - {0x09, 'ゴ'}, - {0x0A, 'ザ'}, - {0x0B, 'ジ'}, - {0x0C, 'ズ'}, - {0x0D, 'ゼ'}, - {0x0E, 'ゾ'}, - {0x0F, 'ダ'}, - {0x10, 'ヂ'}, - {0x11, 'ヅ'}, - {0x12, 'デ'}, - {0x13, 'ド'}, - {0x19, 'バ'}, - {0x1A, 'ビ'}, - {0x1B, 'ブ'}, - {0x1C, 'ボ'}, - {0x26, 'が'}, - {0x27, 'ぎ'}, - {0x28, 'ぐ'}, - {0x29, 'げ'}, - {0x2A, 'ご'}, - {0x2B, 'ざ'}, - {0x2C, 'じ'}, - {0x2D, 'ず'}, - {0x2E, 'ぜ'}, - {0x2F, 'ぞ'}, - {0x30, 'だ'}, - {0x31, 'ぢ'}, - {0x32, 'づ'}, - {0x33, 'で'}, - {0x34, 'ど'}, - {0x3A, 'ば'}, - {0x3B, 'び'}, - {0x3C, 'ぶ'}, - {0x3D, 'ベ'}, - {0x3E, 'ぼ'}, - {0x40, 'パ'}, - {0x41, 'ピ'}, - {0x42, 'プ'}, - {0x43, 'ポ'}, - {0x44, 'ぱ'}, - {0x45, 'ぴ'}, - {0x46, 'ぷ'}, - {0x47, 'ペ'}, - {0x48, 'ぽ'}, - {0x50, G1Terminator}, - {0x5D, G1TradeOT}, - {0x7F, ' '}, - {0x80, 'ア'}, - {0x81, 'イ'}, - {0x82, 'ウ'}, - {0x83, 'エ'}, - {0x84, 'オ'}, - {0x85, 'カ'}, - {0x86, 'キ'}, - {0x87, 'ク'}, - {0x88, 'ケ'}, - {0x89, 'コ'}, - {0x8A, 'サ'}, - {0x8B, 'シ'}, - {0x8C, 'ス'}, - {0x8D, 'セ'}, - {0x8E, 'ソ'}, - {0x8F, 'タ'}, - {0x90, 'チ'}, - {0x91, 'ツ'}, - {0x92, 'テ'}, - {0x93, 'ト'}, - {0x94, 'ナ'}, - {0x95, 'ニ'}, - {0x96, 'ヌ'}, - {0x97, 'ネ'}, - {0x98, 'ノ'}, - {0x99, 'ハ'}, - {0x9A, 'ヒ'}, - {0x9B, 'フ'}, - {0x9C, 'ホ'}, - {0x9D, 'マ'}, - {0x9E, 'ミ'}, - {0x9F, 'ム'}, - {0xA0, 'メ'}, - {0xA1, 'モ'}, - {0xA2, 'ヤ'}, - {0xA3, 'ユ'}, - {0xA4, 'ヨ'}, - {0xA5, 'ラ'}, - {0xA6, 'ル'}, - {0xA7, 'レ'}, - {0xA8, 'ロ'}, - {0xA9, 'ワ'}, - {0xAA, 'ヲ'}, - {0xAB, 'ン'}, - {0xAC, 'ッ'}, - {0xAD, 'ャ'}, - {0xAE, 'ュ'}, - {0xAF, 'ョ'}, - {0xB0, 'ィ'}, - {0xB1, 'あ'}, - {0xB2, 'い'}, - {0xB3, 'う'}, - {0xB4, 'え'}, - {0xB5, 'お'}, - {0xB6, 'か'}, - {0xB7, 'き'}, - {0xB8, 'く'}, - {0xB9, 'け'}, - {0xBA, 'こ'}, - {0xBB, 'さ'}, - {0xBC, 'し'}, - {0xBD, 'す'}, - {0xBE, 'せ'}, - {0xBF, 'そ'}, - {0xC0, 'た'}, - {0xC1, 'ち'}, - {0xC2, 'つ'}, - {0xC3, 'て'}, - {0xC4, 'と'}, - {0xC5, 'な'}, - {0xC6, 'に'}, - {0xC7, 'ぬ'}, - {0xC8, 'ね'}, - {0xC9, 'の'}, - {0xCA, 'は'}, - {0xCB, 'ひ'}, - {0xCC, 'ふ'}, - {0xCD, 'へ'}, - {0xCE, 'ほ'}, - {0xCF, 'ま'}, - {0xD0, 'み'}, - {0xD1, 'む'}, - {0xD2, 'め'}, - {0xD3, 'も'}, - {0xD4, 'や'}, - {0xD5, 'ゆ'}, - {0xD6, 'よ'}, - {0xD7, 'ら'}, - {0xD8, 'リ'}, - {0xD9, 'る'}, - {0xDA, 'れ'}, - {0xDB, 'ろ'}, - {0xDC, 'わ'}, - {0xDD, 'を'}, - {0xDE, 'ん'}, - {0xDF, 'っ'}, - {0xE0, 'ゃ'}, - {0xE1, 'ゅ'}, - {0xE2, 'ょ'}, - {0xE3, 'ー'}, - {0xE6, '?'}, - {0xE7, '!'}, - {0xE9, 'ァ'}, - {0xEA, 'ゥ'}, - {0xEB, 'ェ'}, - {0xEF, '♂'}, - {0xF4, 'ォ'}, - {0xF5, '♀'}, - {0xF6, '0'}, - {0xF7, '1'}, - {0xF8, '2'}, - {0xF9, '3'}, - {0xFA, '4'}, - {0xFB, '5'}, - {0xFC, '6'}, - {0xFD, '7'}, - {0xFE, '8'}, - {0xFF, '9'}, - }; - - internal static readonly Dictionary U2RBY_U = new() - { - {G1Terminator, 0x50}, - {G1TradeOT, 0x5D}, // TRAINER (Localized per ROM) - {' ', 0x7F}, - {'A', 0x80}, - {'B', 0x81}, - {'C', 0x82}, - {'D', 0x83}, - {'E', 0x84}, - {'F', 0x85}, - {'G', 0x86}, - {'H', 0x87}, - {'I', 0x88}, - {'J', 0x89}, - {'K', 0x8A}, - {'L', 0x8B}, - {'M', 0x8C}, - {'N', 0x8D}, - {'O', 0x8E}, - {'P', 0x8F}, - {'Q', 0x90}, - {'R', 0x91}, - {'S', 0x92}, - {'T', 0x93}, - {'U', 0x94}, - {'V', 0x95}, - {'W', 0x96}, - {'X', 0x97}, - {'Y', 0x98}, - {'Z', 0x99}, - {'(', 0x9A}, - {')', 0x9B}, - {':', 0x9C}, - {';', 0x9D}, - {'[', 0x9E}, - {']', 0x9F}, - {'a', 0xA0}, - {'b', 0xA1}, - {'c', 0xA2}, - {'d', 0xA3}, - {'e', 0xA4}, - {'f', 0xA5}, - {'g', 0xA6}, - {'h', 0xA7}, - {'i', 0xA8}, - {'j', 0xA9}, - {'k', 0xAA}, - {'l', 0xAB}, - {'m', 0xAC}, - {'n', 0xAD}, - {'o', 0xAE}, - {'p', 0xAF}, - {'q', 0xB0}, - {'r', 0xB1}, - {'s', 0xB2}, - {'t', 0xB3}, - {'u', 0xB4}, - {'v', 0xB5}, - {'w', 0xB6}, - {'x', 0xB7}, - {'y', 0xB8}, - {'z', 0xB9}, - - // unused characters - {'à', 0xBA}, - {'è', 0xBB}, - {'é', 0xBC}, - {'ù', 0xBD}, - {'À', 0xBE}, - {'Á', 0xBF}, // Used in Spanish FALCÁN in-game trade, inaccessible from keyboard - - {'Ä', 0xC0}, - {'Ö', 0xC1}, - {'Ü', 0xC2}, - {'ä', 0xC3}, - {'ö', 0xC4}, - {'ü', 0xC5}, - - // unused characters - {'È', 0xC6}, - {'É', 0xC7}, - {'Ì', 0xC8}, - {'Í', 0xC9}, // Used in Spanish MANÍA in-game trade, inaccessible from keyboard - {'Ñ', 0xCA}, - {'Ò', 0xCB}, - {'Ó', 0xCC}, - {'Ù', 0xCD}, - {'Ú', 0xCE}, - {'á', 0xCF}, - {'ì', 0xD0}, - {'í', 0xD1}, - {'ñ', 0xD2}, - {'ò', 0xD3}, - {'ó', 0xD4}, - {'ú', 0xD5}, - - {'\'', 0xE0}, // Alias ' to ’ for Farfetch’d - {'’', 0xE0}, - {'{', 0xE1}, /* Pk */ - {'}', 0xE2}, /* Mn */ - {'-', 0xE3}, - {'?', 0xE6}, - {'!', 0xE7}, - {'♂', 0xEF}, - {'×', 0xF1}, - {'.', 0xF2}, - {'/', 0xF3}, - {',', 0xF4}, - {'♀', 0xF5}, - {'0', 0xF6}, - {'1', 0xF7}, - {'2', 0xF8}, - {'3', 0xF9}, - {'4', 0xFA}, - {'5', 0xFB}, - {'6', 0xFC}, - {'7', 0xFD}, - {'8', 0xFE}, - {'9', 0xFF}, - }; - - private static readonly Dictionary U2RBY_J = new() - { - {'ガ', 0x05}, - {'ギ', 0x06}, - {'グ', 0x07}, - {'ゲ', 0x08}, - {'ゴ', 0x09}, - {'ザ', 0x0A}, - {'ジ', 0x0B}, - {'ズ', 0x0C}, - {'ゼ', 0x0D}, - {'ゾ', 0x0E}, - {'ダ', 0x0F}, - {'ヂ', 0x10}, - {'ヅ', 0x11}, - {'デ', 0x12}, - {'ド', 0x13}, - {'バ', 0x19}, - {'ビ', 0x1A}, - {'ブ', 0x1B}, - {'ボ', 0x1C}, - {'が', 0x26}, - {'ぎ', 0x27}, - {'ぐ', 0x28}, - {'げ', 0x29}, - {'ご', 0x2A}, - {'ざ', 0x2B}, - {'じ', 0x2C}, - {'ず', 0x2D}, - {'ぜ', 0x2E}, - {'ぞ', 0x2F}, - {'だ', 0x30}, - {'ぢ', 0x31}, - {'づ', 0x32}, - {'で', 0x33}, - {'ど', 0x34}, - {'ば', 0x3A}, - {'び', 0x3B}, - {'ぶ', 0x3C}, - {'ベ', 0x3D}, - {'べ', 0x3D}, - {'ぼ', 0x3E}, - {'パ', 0x40}, - {'ピ', 0x41}, - {'プ', 0x42}, - {'ポ', 0x43}, - {'ぱ', 0x44}, - {'ぴ', 0x45}, - {'ぷ', 0x46}, - {'ぺ', 0x47}, - {'ペ', 0x47}, - {'ぽ', 0x48}, - {G1Terminator, 0x50}, - {G1TradeOT, 0x5D}, // トレーナー, (Localized per ROM) - {' ', 0x7F}, - {'ア', 0x80}, - {'イ', 0x81}, - {'ウ', 0x82}, - {'エ', 0x83}, - {'オ', 0x84}, - {'カ', 0x85}, - {'キ', 0x86}, - {'ク', 0x87}, - {'ケ', 0x88}, - {'コ', 0x89}, - {'サ', 0x8A}, - {'シ', 0x8B}, - {'ス', 0x8C}, - {'セ', 0x8D}, - {'ソ', 0x8E}, - {'タ', 0x8F}, - {'チ', 0x90}, - {'ツ', 0x91}, - {'テ', 0x92}, - {'ト', 0x93}, - {'ナ', 0x94}, - {'ニ', 0x95}, - {'ヌ', 0x96}, - {'ネ', 0x97}, - {'ノ', 0x98}, - {'ハ', 0x99}, - {'ヒ', 0x9A}, - {'フ', 0x9B}, - {'ホ', 0x9C}, - {'マ', 0x9D}, - {'ミ', 0x9E}, - {'ム', 0x9F}, - {'メ', 0xA0}, - {'モ', 0xA1}, - {'ヤ', 0xA2}, - {'ユ', 0xA3}, - {'ヨ', 0xA4}, - {'ラ', 0xA5}, - {'ル', 0xA6}, - {'レ', 0xA7}, - {'ロ', 0xA8}, - {'ワ', 0xA9}, - {'ヲ', 0xAA}, - {'ン', 0xAB}, - {'ッ', 0xAC}, - {'ャ', 0xAD}, - {'ュ', 0xAE}, - {'ョ', 0xAF}, - {'ィ', 0xB0}, - {'あ', 0xB1}, - {'い', 0xB2}, - {'う', 0xB3}, - {'え', 0xB4}, - {'お', 0xB5}, - {'か', 0xB6}, - {'き', 0xB7}, - {'く', 0xB8}, - {'け', 0xB9}, - {'こ', 0xBA}, - {'さ', 0xBB}, - {'し', 0xBC}, - {'す', 0xBD}, - {'せ', 0xBE}, - {'そ', 0xBF}, - {'た', 0xC0}, - {'ち', 0xC1}, - {'つ', 0xC2}, - {'て', 0xC3}, - {'と', 0xC4}, - {'な', 0xC5}, - {'に', 0xC6}, - {'ぬ', 0xC7}, - {'ね', 0xC8}, - {'の', 0xC9}, - {'は', 0xCA}, - {'ひ', 0xCB}, - {'ふ', 0xCC}, - {'へ', 0xCD}, - {'ヘ', 0xCD}, // Katakana ヘ => Hiragana へ - {'ほ', 0xCE}, - {'ま', 0xCF}, - {'み', 0xD0}, - {'む', 0xD1}, - {'め', 0xD2}, - {'も', 0xD3}, - {'や', 0xD4}, - {'ゆ', 0xD5}, - {'よ', 0xD6}, - {'ら', 0xD7}, - {'リ', 0xD8}, - {'り', 0xD8}, - {'る', 0xD9}, - {'れ', 0xDA}, - {'ろ', 0xDB}, - {'わ', 0xDC}, - {'を', 0xDD}, - {'ん', 0xDE}, - {'っ', 0xDF}, - {'ゃ', 0xE0}, - {'ゅ', 0xE1}, - {'ょ', 0xE2}, - {'ー', 0xE3}, - {'?', 0xE6}, - {'!', 0xE7}, - {'ァ', 0xE9}, - {'ゥ', 0xEA}, - {'ェ', 0xEB}, - {'♂', 0xEF}, - {'ォ', 0xF4}, - {'♀', 0xF5}, - {'0', 0xF6}, - {'1', 0xF7}, - {'2', 0xF8}, - {'3', 0xF9}, - {'4', 0xFA}, - {'5', 0xFB}, - {'6', 0xFC}, - {'7', 0xFD}, - {'8', 0xFE}, - {'9', 0xFF}, - }; - - #endregion + return true; } + + private static bool AllCharsInDictionary(ReadOnlySpan data, IReadOnlyDictionary d) + { + foreach (var c in data) + { + if (c == 0) + break; + if (!d.ContainsKey(c)) + return false; + } + return true; + } + + public const byte G1TerminatorCode = 0x50; + public const char G1Terminator = '\0'; + public const byte G1TradeOTCode = 0x5D; + public const char G1TradeOT = '*'; + public const string G1TradeOTStr = "*"; + public const byte G1SpaceCode = 0x7F; + + public static readonly IReadOnlyList G1TradeOTName = new [] + { + string.Empty, + "トレーナー", + "Trainer", + "Dresseur", + "Allenatore", + "Trainer", + string.Empty, + "Entrenador", + "트레이너", + }; + + /// + /// Checks if the input byte array is definitely of German origin (any ÄÖÜäöü) + /// + /// Raw string bytes + /// Indication if the data is from a definitely-german string + public static bool IsG12German(ReadOnlySpan data) + { + foreach (var b in data) + { + if (b is >= 0xC0 and <= 0xC6) + return true; + } + return false; + } + + /// + /// Checks if the input byte array is definitely of German origin (any ÄÖÜäöü) + /// + /// Input string + /// Indication if the data is from a definitely-german string + public static bool IsG12German(ReadOnlySpan value) + { + var table = U2RBY_U; + foreach (var c in value) + { + if (!table.TryGetValue(c, out var b)) + continue; + + if (b is >= 0xC0 and <= 0xC6) + return true; + } + return false; + } + + /// + /// Converts Generation 1 encoded data into a string. + /// + /// Encoded data. + /// Data source is Japanese. + /// Decoded string. + public static string GetString(ReadOnlySpan data, bool jp) + { + Span result = stackalloc char[data.Length]; + int length = LoadString(data, result, jp); + return new string(result[..length].ToArray()); + } + + private static int LoadString(ReadOnlySpan data, Span result, bool jp) + { + if (data[0] == G1TradeOTCode) + { + result[0] = G1Terminator; + return 1; + } + + var dict = jp ? RBY2U_J : RBY2U_U; + int i = 0; + for (; i < data.Length; i++) + { + var value = data[i]; + if (!dict.TryGetValue(value, out var c)) // Take valid values + break; + if (c == G1Terminator) // Stop if Terminator + break; + result[i] = c; + } + return i; + } + + /// + /// Converts a string to Generation 1 encoded data. + /// + /// + /// Decoded string. + /// Maximum length of the input + /// Data destination is Japanese. + /// + /// Encoded data. + public static int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, bool jp, + StringConverterOption option = StringConverterOption.Clear50) + { + if (option is StringConverterOption.ClearZero) + destBuffer.Clear(); + else if (option is StringConverterOption.Clear50) + destBuffer.Fill(G1TerminatorCode); + else if (option is StringConverterOption.Clear7F) + destBuffer.Fill(G1SpaceCode); + + if (value.Length != 0 && value[0] == G1TradeOT) // Handle "[TRAINER]" + { + destBuffer[0] = G1TradeOTCode; + destBuffer[1] = G1TerminatorCode; + return 2; + } + + if (value.Length > maxLength) + value = value[..maxLength]; // Hard cap + + var dict = jp ? U2RBY_J : U2RBY_U; + int i = 0; + for (; i < value.Length; i++) + { + char c = value[i]; + if (!dict.TryGetValue(c, out byte val)) + break; + destBuffer[i] = val; + } + + int count = i; + if (count == destBuffer.Length) + return count; + destBuffer[value.Length] = G1TerminatorCode; + return count + 1; + } + + /// + /// Converts Generation 1 encoded character to string. + /// + /// Encoded character. + /// Data source is Japanese. + /// Decoded string. + public static char GetG1Char(byte key, bool jp) + { + var dict = jp ? RBY2U_J : RBY2U_U; + if (dict.TryGetValue(key, out var value)) + return value; + return G1Terminator; + } + + #region Gen 1/2 Character Tables + + internal static readonly Dictionary RBY2U_U = new() + { + {0x50, G1Terminator}, + {0x5D, G1TradeOT}, + {0x7F, ' '}, + {0x80, 'A'}, + {0x81, 'B'}, + {0x82, 'C'}, + {0x83, 'D'}, + {0x84, 'E'}, + {0x85, 'F'}, + {0x86, 'G'}, + {0x87, 'H'}, + {0x88, 'I'}, + {0x89, 'J'}, + {0x8A, 'K'}, + {0x8B, 'L'}, + {0x8C, 'M'}, + {0x8D, 'N'}, + {0x8E, 'O'}, + {0x8F, 'P'}, + {0x90, 'Q'}, + {0x91, 'R'}, + {0x92, 'S'}, + {0x93, 'T'}, + {0x94, 'U'}, + {0x95, 'V'}, + {0x96, 'W'}, + {0x97, 'X'}, + {0x98, 'Y'}, + {0x99, 'Z'}, + {0x9A, '('}, + {0x9B, ')'}, + {0x9C, ':'}, + {0x9D, ';'}, + {0x9E, '['}, + {0x9F, ']'}, + {0xA0, 'a'}, + {0xA1, 'b'}, + {0xA2, 'c'}, + {0xA3, 'd'}, + {0xA4, 'e'}, + {0xA5, 'f'}, + {0xA6, 'g'}, + {0xA7, 'h'}, + {0xA8, 'i'}, + {0xA9, 'j'}, + {0xAA, 'k'}, + {0xAB, 'l'}, + {0xAC, 'm'}, + {0xAD, 'n'}, + {0xAE, 'o'}, + {0xAF, 'p'}, + {0xB0, 'q'}, + {0xB1, 'r'}, + {0xB2, 's'}, + {0xB3, 't'}, + {0xB4, 'u'}, + {0xB5, 'v'}, + {0xB6, 'w'}, + {0xB7, 'x'}, + {0xB8, 'y'}, + {0xB9, 'z'}, + + // unused characters + {0xBA, 'à'}, + {0xBB, 'è'}, + {0xBC, 'é'}, + {0xBD, 'ù'}, + {0xBE, 'À'}, + {0xBF, 'Á'}, // Used in Spanish FALCÁN in-game trade, inaccessible from keyboard + + {0xC0, 'Ä'}, + {0xC1, 'Ö'}, + {0xC2, 'Ü'}, + {0xC3, 'ä'}, + {0xC4, 'ö'}, + {0xC5, 'ü'}, + + // unused characters + {0xC6, 'È'}, + {0xC7, 'É'}, + {0xC8, 'Ì'}, + {0xC9, 'Í'}, // Used in Spanish MANÍA in-game trade, inaccessible from keyboard + {0xCA, 'Ñ'}, + {0xCB, 'Ò'}, + {0xCC, 'Ó'}, + {0xCD, 'Ù'}, + {0xCE, 'Ú'}, + {0xCF, 'á'}, + {0xD0, 'ì'}, + {0xD1, 'í'}, + {0xD2, 'ñ'}, + {0xD3, 'ò'}, + {0xD4, 'ó'}, + {0xD5, 'ú'}, + + {0xE0, '’'}, + {0xE1, '{'}, /* Pk */ + {0xE2, '}'}, /* Mn */ + {0xE3, '-'}, + {0xE6, '?'}, + {0xE7, '!'}, + {0xE8, '.'}, // Alias decimal point to . + {0xEF, '♂'}, + {0xF1, '×'}, + {0xF2, '.'}, + {0xF3, '/'}, + {0xF4, ','}, + {0xF5, '♀'}, + {0xF6, '0'}, + {0xF7, '1'}, + {0xF8, '2'}, + {0xF9, '3'}, + {0xFA, '4'}, + {0xFB, '5'}, + {0xFC, '6'}, + {0xFD, '7'}, + {0xFE, '8'}, + {0xFF, '9'}, + }; + + private static readonly Dictionary RBY2U_J = new() + { + {0x05, 'ガ'}, + {0x06, 'ギ'}, + {0x07, 'グ'}, + {0x08, 'ゲ'}, + {0x09, 'ゴ'}, + {0x0A, 'ザ'}, + {0x0B, 'ジ'}, + {0x0C, 'ズ'}, + {0x0D, 'ゼ'}, + {0x0E, 'ゾ'}, + {0x0F, 'ダ'}, + {0x10, 'ヂ'}, + {0x11, 'ヅ'}, + {0x12, 'デ'}, + {0x13, 'ド'}, + {0x19, 'バ'}, + {0x1A, 'ビ'}, + {0x1B, 'ブ'}, + {0x1C, 'ボ'}, + {0x26, 'が'}, + {0x27, 'ぎ'}, + {0x28, 'ぐ'}, + {0x29, 'げ'}, + {0x2A, 'ご'}, + {0x2B, 'ざ'}, + {0x2C, 'じ'}, + {0x2D, 'ず'}, + {0x2E, 'ぜ'}, + {0x2F, 'ぞ'}, + {0x30, 'だ'}, + {0x31, 'ぢ'}, + {0x32, 'づ'}, + {0x33, 'で'}, + {0x34, 'ど'}, + {0x3A, 'ば'}, + {0x3B, 'び'}, + {0x3C, 'ぶ'}, + {0x3D, 'ベ'}, + {0x3E, 'ぼ'}, + {0x40, 'パ'}, + {0x41, 'ピ'}, + {0x42, 'プ'}, + {0x43, 'ポ'}, + {0x44, 'ぱ'}, + {0x45, 'ぴ'}, + {0x46, 'ぷ'}, + {0x47, 'ペ'}, + {0x48, 'ぽ'}, + {0x50, G1Terminator}, + {0x5D, G1TradeOT}, + {0x7F, ' '}, + {0x80, 'ア'}, + {0x81, 'イ'}, + {0x82, 'ウ'}, + {0x83, 'エ'}, + {0x84, 'オ'}, + {0x85, 'カ'}, + {0x86, 'キ'}, + {0x87, 'ク'}, + {0x88, 'ケ'}, + {0x89, 'コ'}, + {0x8A, 'サ'}, + {0x8B, 'シ'}, + {0x8C, 'ス'}, + {0x8D, 'セ'}, + {0x8E, 'ソ'}, + {0x8F, 'タ'}, + {0x90, 'チ'}, + {0x91, 'ツ'}, + {0x92, 'テ'}, + {0x93, 'ト'}, + {0x94, 'ナ'}, + {0x95, 'ニ'}, + {0x96, 'ヌ'}, + {0x97, 'ネ'}, + {0x98, 'ノ'}, + {0x99, 'ハ'}, + {0x9A, 'ヒ'}, + {0x9B, 'フ'}, + {0x9C, 'ホ'}, + {0x9D, 'マ'}, + {0x9E, 'ミ'}, + {0x9F, 'ム'}, + {0xA0, 'メ'}, + {0xA1, 'モ'}, + {0xA2, 'ヤ'}, + {0xA3, 'ユ'}, + {0xA4, 'ヨ'}, + {0xA5, 'ラ'}, + {0xA6, 'ル'}, + {0xA7, 'レ'}, + {0xA8, 'ロ'}, + {0xA9, 'ワ'}, + {0xAA, 'ヲ'}, + {0xAB, 'ン'}, + {0xAC, 'ッ'}, + {0xAD, 'ャ'}, + {0xAE, 'ュ'}, + {0xAF, 'ョ'}, + {0xB0, 'ィ'}, + {0xB1, 'あ'}, + {0xB2, 'い'}, + {0xB3, 'う'}, + {0xB4, 'え'}, + {0xB5, 'お'}, + {0xB6, 'か'}, + {0xB7, 'き'}, + {0xB8, 'く'}, + {0xB9, 'け'}, + {0xBA, 'こ'}, + {0xBB, 'さ'}, + {0xBC, 'し'}, + {0xBD, 'す'}, + {0xBE, 'せ'}, + {0xBF, 'そ'}, + {0xC0, 'た'}, + {0xC1, 'ち'}, + {0xC2, 'つ'}, + {0xC3, 'て'}, + {0xC4, 'と'}, + {0xC5, 'な'}, + {0xC6, 'に'}, + {0xC7, 'ぬ'}, + {0xC8, 'ね'}, + {0xC9, 'の'}, + {0xCA, 'は'}, + {0xCB, 'ひ'}, + {0xCC, 'ふ'}, + {0xCD, 'へ'}, + {0xCE, 'ほ'}, + {0xCF, 'ま'}, + {0xD0, 'み'}, + {0xD1, 'む'}, + {0xD2, 'め'}, + {0xD3, 'も'}, + {0xD4, 'や'}, + {0xD5, 'ゆ'}, + {0xD6, 'よ'}, + {0xD7, 'ら'}, + {0xD8, 'リ'}, + {0xD9, 'る'}, + {0xDA, 'れ'}, + {0xDB, 'ろ'}, + {0xDC, 'わ'}, + {0xDD, 'を'}, + {0xDE, 'ん'}, + {0xDF, 'っ'}, + {0xE0, 'ゃ'}, + {0xE1, 'ゅ'}, + {0xE2, 'ょ'}, + {0xE3, 'ー'}, + {0xE6, '?'}, + {0xE7, '!'}, + {0xE9, 'ァ'}, + {0xEA, 'ゥ'}, + {0xEB, 'ェ'}, + {0xEF, '♂'}, + {0xF4, 'ォ'}, + {0xF5, '♀'}, + {0xF6, '0'}, + {0xF7, '1'}, + {0xF8, '2'}, + {0xF9, '3'}, + {0xFA, '4'}, + {0xFB, '5'}, + {0xFC, '6'}, + {0xFD, '7'}, + {0xFE, '8'}, + {0xFF, '9'}, + }; + + internal static readonly Dictionary U2RBY_U = new() + { + {G1Terminator, 0x50}, + {G1TradeOT, 0x5D}, // TRAINER (Localized per ROM) + {' ', 0x7F}, + {'A', 0x80}, + {'B', 0x81}, + {'C', 0x82}, + {'D', 0x83}, + {'E', 0x84}, + {'F', 0x85}, + {'G', 0x86}, + {'H', 0x87}, + {'I', 0x88}, + {'J', 0x89}, + {'K', 0x8A}, + {'L', 0x8B}, + {'M', 0x8C}, + {'N', 0x8D}, + {'O', 0x8E}, + {'P', 0x8F}, + {'Q', 0x90}, + {'R', 0x91}, + {'S', 0x92}, + {'T', 0x93}, + {'U', 0x94}, + {'V', 0x95}, + {'W', 0x96}, + {'X', 0x97}, + {'Y', 0x98}, + {'Z', 0x99}, + {'(', 0x9A}, + {')', 0x9B}, + {':', 0x9C}, + {';', 0x9D}, + {'[', 0x9E}, + {']', 0x9F}, + {'a', 0xA0}, + {'b', 0xA1}, + {'c', 0xA2}, + {'d', 0xA3}, + {'e', 0xA4}, + {'f', 0xA5}, + {'g', 0xA6}, + {'h', 0xA7}, + {'i', 0xA8}, + {'j', 0xA9}, + {'k', 0xAA}, + {'l', 0xAB}, + {'m', 0xAC}, + {'n', 0xAD}, + {'o', 0xAE}, + {'p', 0xAF}, + {'q', 0xB0}, + {'r', 0xB1}, + {'s', 0xB2}, + {'t', 0xB3}, + {'u', 0xB4}, + {'v', 0xB5}, + {'w', 0xB6}, + {'x', 0xB7}, + {'y', 0xB8}, + {'z', 0xB9}, + + // unused characters + {'à', 0xBA}, + {'è', 0xBB}, + {'é', 0xBC}, + {'ù', 0xBD}, + {'À', 0xBE}, + {'Á', 0xBF}, // Used in Spanish FALCÁN in-game trade, inaccessible from keyboard + + {'Ä', 0xC0}, + {'Ö', 0xC1}, + {'Ü', 0xC2}, + {'ä', 0xC3}, + {'ö', 0xC4}, + {'ü', 0xC5}, + + // unused characters + {'È', 0xC6}, + {'É', 0xC7}, + {'Ì', 0xC8}, + {'Í', 0xC9}, // Used in Spanish MANÍA in-game trade, inaccessible from keyboard + {'Ñ', 0xCA}, + {'Ò', 0xCB}, + {'Ó', 0xCC}, + {'Ù', 0xCD}, + {'Ú', 0xCE}, + {'á', 0xCF}, + {'ì', 0xD0}, + {'í', 0xD1}, + {'ñ', 0xD2}, + {'ò', 0xD3}, + {'ó', 0xD4}, + {'ú', 0xD5}, + + {'\'', 0xE0}, // Alias ' to ’ for Farfetch’d + {'’', 0xE0}, + {'{', 0xE1}, /* Pk */ + {'}', 0xE2}, /* Mn */ + {'-', 0xE3}, + {'?', 0xE6}, + {'!', 0xE7}, + {'♂', 0xEF}, + {'×', 0xF1}, + {'.', 0xF2}, + {'/', 0xF3}, + {',', 0xF4}, + {'♀', 0xF5}, + {'0', 0xF6}, + {'1', 0xF7}, + {'2', 0xF8}, + {'3', 0xF9}, + {'4', 0xFA}, + {'5', 0xFB}, + {'6', 0xFC}, + {'7', 0xFD}, + {'8', 0xFE}, + {'9', 0xFF}, + }; + + private static readonly Dictionary U2RBY_J = new() + { + {'ガ', 0x05}, + {'ギ', 0x06}, + {'グ', 0x07}, + {'ゲ', 0x08}, + {'ゴ', 0x09}, + {'ザ', 0x0A}, + {'ジ', 0x0B}, + {'ズ', 0x0C}, + {'ゼ', 0x0D}, + {'ゾ', 0x0E}, + {'ダ', 0x0F}, + {'ヂ', 0x10}, + {'ヅ', 0x11}, + {'デ', 0x12}, + {'ド', 0x13}, + {'バ', 0x19}, + {'ビ', 0x1A}, + {'ブ', 0x1B}, + {'ボ', 0x1C}, + {'が', 0x26}, + {'ぎ', 0x27}, + {'ぐ', 0x28}, + {'げ', 0x29}, + {'ご', 0x2A}, + {'ざ', 0x2B}, + {'じ', 0x2C}, + {'ず', 0x2D}, + {'ぜ', 0x2E}, + {'ぞ', 0x2F}, + {'だ', 0x30}, + {'ぢ', 0x31}, + {'づ', 0x32}, + {'で', 0x33}, + {'ど', 0x34}, + {'ば', 0x3A}, + {'び', 0x3B}, + {'ぶ', 0x3C}, + {'ベ', 0x3D}, + {'べ', 0x3D}, + {'ぼ', 0x3E}, + {'パ', 0x40}, + {'ピ', 0x41}, + {'プ', 0x42}, + {'ポ', 0x43}, + {'ぱ', 0x44}, + {'ぴ', 0x45}, + {'ぷ', 0x46}, + {'ぺ', 0x47}, + {'ペ', 0x47}, + {'ぽ', 0x48}, + {G1Terminator, 0x50}, + {G1TradeOT, 0x5D}, // トレーナー, (Localized per ROM) + {' ', 0x7F}, + {'ア', 0x80}, + {'イ', 0x81}, + {'ウ', 0x82}, + {'エ', 0x83}, + {'オ', 0x84}, + {'カ', 0x85}, + {'キ', 0x86}, + {'ク', 0x87}, + {'ケ', 0x88}, + {'コ', 0x89}, + {'サ', 0x8A}, + {'シ', 0x8B}, + {'ス', 0x8C}, + {'セ', 0x8D}, + {'ソ', 0x8E}, + {'タ', 0x8F}, + {'チ', 0x90}, + {'ツ', 0x91}, + {'テ', 0x92}, + {'ト', 0x93}, + {'ナ', 0x94}, + {'ニ', 0x95}, + {'ヌ', 0x96}, + {'ネ', 0x97}, + {'ノ', 0x98}, + {'ハ', 0x99}, + {'ヒ', 0x9A}, + {'フ', 0x9B}, + {'ホ', 0x9C}, + {'マ', 0x9D}, + {'ミ', 0x9E}, + {'ム', 0x9F}, + {'メ', 0xA0}, + {'モ', 0xA1}, + {'ヤ', 0xA2}, + {'ユ', 0xA3}, + {'ヨ', 0xA4}, + {'ラ', 0xA5}, + {'ル', 0xA6}, + {'レ', 0xA7}, + {'ロ', 0xA8}, + {'ワ', 0xA9}, + {'ヲ', 0xAA}, + {'ン', 0xAB}, + {'ッ', 0xAC}, + {'ャ', 0xAD}, + {'ュ', 0xAE}, + {'ョ', 0xAF}, + {'ィ', 0xB0}, + {'あ', 0xB1}, + {'い', 0xB2}, + {'う', 0xB3}, + {'え', 0xB4}, + {'お', 0xB5}, + {'か', 0xB6}, + {'き', 0xB7}, + {'く', 0xB8}, + {'け', 0xB9}, + {'こ', 0xBA}, + {'さ', 0xBB}, + {'し', 0xBC}, + {'す', 0xBD}, + {'せ', 0xBE}, + {'そ', 0xBF}, + {'た', 0xC0}, + {'ち', 0xC1}, + {'つ', 0xC2}, + {'て', 0xC3}, + {'と', 0xC4}, + {'な', 0xC5}, + {'に', 0xC6}, + {'ぬ', 0xC7}, + {'ね', 0xC8}, + {'の', 0xC9}, + {'は', 0xCA}, + {'ひ', 0xCB}, + {'ふ', 0xCC}, + {'へ', 0xCD}, + {'ヘ', 0xCD}, // Katakana ヘ => Hiragana へ + {'ほ', 0xCE}, + {'ま', 0xCF}, + {'み', 0xD0}, + {'む', 0xD1}, + {'め', 0xD2}, + {'も', 0xD3}, + {'や', 0xD4}, + {'ゆ', 0xD5}, + {'よ', 0xD6}, + {'ら', 0xD7}, + {'リ', 0xD8}, + {'り', 0xD8}, + {'る', 0xD9}, + {'れ', 0xDA}, + {'ろ', 0xDB}, + {'わ', 0xDC}, + {'を', 0xDD}, + {'ん', 0xDE}, + {'っ', 0xDF}, + {'ゃ', 0xE0}, + {'ゅ', 0xE1}, + {'ょ', 0xE2}, + {'ー', 0xE3}, + {'?', 0xE6}, + {'!', 0xE7}, + {'ァ', 0xE9}, + {'ゥ', 0xEA}, + {'ェ', 0xEB}, + {'♂', 0xEF}, + {'ォ', 0xF4}, + {'♀', 0xF5}, + {'0', 0xF6}, + {'1', 0xF7}, + {'2', 0xF8}, + {'3', 0xF9}, + {'4', 0xFA}, + {'5', 0xFB}, + {'6', 0xFC}, + {'7', 0xFD}, + {'8', 0xFE}, + {'9', 0xFF}, + }; + + #endregion } diff --git a/PKHeX.Core/PKM/Strings/StringConverter12Transporter.cs b/PKHeX.Core/PKM/Strings/StringConverter12Transporter.cs index 8e376f80b..d9dee8e50 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter12Transporter.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter12Transporter.cs @@ -1,130 +1,141 @@ using System; -using System.Collections.Generic; -using System.Linq; -namespace PKHeX.Core +namespace PKHeX.Core; + +/// +/// Logic for converting a from Generation 1 & 2 games to Generation 7. +/// +public static class StringConverter12Transporter { + private const ushort Terminator = 0; + /// - /// Logic for converting a from Generation 1 & 2 games to Generation 7. + /// Converts Generation 1 encoded data the same way Bank converts. /// - public static class StringConverter12Transporter + /// Generation 1 encoded data. + /// Data source is Japanese. + /// Decoded string. + public static string GetString(ReadOnlySpan data, bool jp) { - /// - /// Converts Generation 1 encoded data the same way Bank converts. - /// - /// Generation 1 encoded data. - /// Data source is Japanese. - /// Decoded string. - public static string GetString(byte[] data, bool jp) - { - var table = jp ? jp_table : us_table; - var chars = GetG1ConvertedChar(data, table); - if (jp) - chars = CheckKata(chars); - - return string.Concat(chars); - } - - private static IEnumerable GetG1ConvertedChar(byte[] data, ushort[] table) - { - foreach (var b in data) - { - if (b == 0) - yield break; - - var val = table[b]; - if (val == 0) - yield break; - - yield return (char)val; - } - } - - private static IEnumerable CheckKata(IEnumerable chars) - { - var arr = chars.ToArray(); - if (!arr.Any(Katakana.Contains)) - return arr; - - if (!IsHiragana(arr)) - return arr; - - for (int i = 0; i < arr.Length; i++) - { - int index = Array.IndexOf(Katakana, arr[i]); - if (index == -1) - continue; - arr[i] = Hiragana[index]; - } - - return arr; - } - - private static bool IsHiragana(IEnumerable chars) - { - foreach (var c in chars) - { - if ((uint)(c - 0x3041) < 0x53) - return true; - if ((uint)(c - 0x30A1) < 0x56) - return false; - } - return true; - } - - private static readonly char[] Katakana = { 'ベ', 'ペ', 'ヘ', 'リ' }; - private static readonly char[] Hiragana = { 'べ', 'ぺ', 'へ', 'り' }; - - /// - /// International 1->7 character translation table - /// - /// - /// Exported from Gen1's VC string transferring, with manual modifications for the two permitted accent marks: - ///
0xC1 at arr[0xBF] = Á (Spanish In-game Trade Voltorb, FALCÁN)
- ///
0xCD at arr[0xC9] = Í (Spanish In-game Trade Shuckle, MANÍA)
- ///
All other new language sensitive re-mapping (or lack thereof) are inaccessible via the character entry screen.
- ///
- private static readonly ushort[] us_table = - { - 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 0 - 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 1 - 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 2 - 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 3 - 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 4 - 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 5 - 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 6 - 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 7 - 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, // 8 - 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x0028, 0x0029, 0x003A, 0x003B, 0x0028, 0x0029, // 9 - 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, // A - 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x00C1, // B - 0x00C4, 0x00D6, 0x00DC, 0x00E4, 0x00F6, 0x00FC, 0x0020, 0x0020, 0x0020, 0x00CD, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // C - 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // D - 0x0020, 0x0050, 0x004D, 0x002D, 0x0020, 0x0020, 0x003F, 0x0021, 0x002D, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0xE08E, // E - 0x0020, 0x0078, 0x002E, 0x002F, 0x002C, 0xE08F, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // F - }; - - /// - /// Japanese 1->7 character translation table - /// - private static readonly ushort[] jp_table = - { - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x30AC, 0x30AE, 0x30B0, 0x30B2, 0x30B4, 0x30B6, 0x30B8, 0x30BA, 0x30BC, 0x30BE, 0x30C0, // 0 - 0x30C2, 0x30C5, 0x30C7, 0x30C9, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x30D0, 0x30D3, 0x30D6, 0x30DC, 0x3000, 0x3000, 0x3000, // 1 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x304C, 0x304E, 0x3050, 0x3052, 0x3054, 0x3056, 0x3058, 0x305A, 0x305C, 0x305E, // 2 - 0x3060, 0x3062, 0x3065, 0x3067, 0x3069, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3070, 0x3073, 0x3076, 0x30D9, 0x307C, 0x3000, // 3 - 0x30D1, 0x30D4, 0x30D7, 0x30DD, 0x3071, 0x3074, 0x3077, 0x30DA, 0x307D, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, // 4 - 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, // 5 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, // 6 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, // 7 - 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF, // 8 - 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30DB, 0x30DE, 0x30DF, 0x30E0, // 9 - 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F2, 0x30F3, 0x30C3, 0x30E3, 0x30E5, 0x30E7, // A - 0x30A3, 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F, 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, // B - 0x305F, 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F, 0x3072, 0x3075, 0x30D8, 0x307B, 0x307E, // C - 0x307F, 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x30EA, 0x308B, 0x308C, 0x308D, 0x308F, 0x3092, 0x3093, 0x3063, // D - 0x3083, 0x3085, 0x3087, 0x30FC, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x30A1, 0x30A5, 0x30A7, 0x3000, 0x3000, 0x3000, 0x2642, // E - 0x3000, 0x3000, 0x3000, 0x3000, 0x30A9, 0x2640, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, // F - }; + Span result = stackalloc char[data.Length]; + int length = LoadString1(data, result, jp); + return new string(result[..length].ToArray()); } + + private static int LoadString1(ReadOnlySpan data, Span result, bool jp) + { + var table = jp ? jp_table : us_table; + int i = 0; + for (; i < data.Length; i++) + { + var b = data[i]; + if (b == 0) + break; + + var value = table[b]; + if (value == Terminator) + break; + + result[i] = (char)value; + } + CheckKata(result[..i]); + return i; + } + + private static void CheckKata(Span chars) + { + bool isAnyKata = IsKata(chars); + if (!isAnyKata) + return; + + if (!IsHiragana(chars)) + return; + + var span = Katakana.AsSpan(); + for (int i = 0; i < chars.Length; i++) + { + int index = span.IndexOf(chars[i]); + if (index == -1) + continue; + chars[i] = Hiragana[index]; + } + } + + private static bool IsKata(ReadOnlySpan chars) + { + var span = Katakana.AsSpan(); + foreach (var c in chars) + { + if (span.IndexOf(c) != -1) + return true; + } + return false; + } + + private static bool IsHiragana(ReadOnlySpan chars) + { + foreach (var c in chars) + { + if ((uint)(c - 0x3041) < 0x53) + return true; + if ((uint)(c - 0x30A1) < 0x56) + return false; + } + return true; + } + + private static readonly char[] Katakana = { 'ベ', 'ペ', 'ヘ', 'リ' }; + private static readonly char[] Hiragana = { 'べ', 'ぺ', 'へ', 'り' }; + + /// + /// International 1->7 character translation table + /// + /// + /// Exported from Gen1's VC string transferring, with manual modifications for the two permitted accent marks: + ///
0xC1 at arr[0xBF] = Á (Spanish In-game Trade Voltorb, FALCÁN)
+ ///
0xCD at arr[0xC9] = Í (Spanish In-game Trade Shuckle, MANÍA)
+ ///
All other new language sensitive re-mapping (or lack thereof) are inaccessible via the character entry screen.
+ ///
+ private static readonly ushort[] us_table = + { + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 0 + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 1 + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 2 + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 3 + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 4 + 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 5 + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 6 + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // 7 + 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, // 8 + 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x0028, 0x0029, 0x003A, 0x003B, 0x0028, 0x0029, // 9 + 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, // A + 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x00C1, // B + 0x00C4, 0x00D6, 0x00DC, 0x00E4, 0x00F6, 0x00FC, 0x0020, 0x0020, 0x0020, 0x00CD, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // C + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // D + 0x0020, 0x0050, 0x004D, 0x002D, 0x0020, 0x0020, 0x003F, 0x0021, 0x002D, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0xE08E, // E + 0x0020, 0x0078, 0x002E, 0x002F, 0x002C, 0xE08F, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // F + }; + + /// + /// Japanese 1->7 character translation table + /// + private static readonly ushort[] jp_table = + { + 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x30AC, 0x30AE, 0x30B0, 0x30B2, 0x30B4, 0x30B6, 0x30B8, 0x30BA, 0x30BC, 0x30BE, 0x30C0, // 0 + 0x30C2, 0x30C5, 0x30C7, 0x30C9, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x30D0, 0x30D3, 0x30D6, 0x30DC, 0x3000, 0x3000, 0x3000, // 1 + 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x304C, 0x304E, 0x3050, 0x3052, 0x3054, 0x3056, 0x3058, 0x305A, 0x305C, 0x305E, // 2 + 0x3060, 0x3062, 0x3065, 0x3067, 0x3069, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3070, 0x3073, 0x3076, 0x30D9, 0x307C, 0x3000, // 3 + 0x30D1, 0x30D4, 0x30D7, 0x30DD, 0x3071, 0x3074, 0x3077, 0x30DA, 0x307D, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, // 4 + 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, // 5 + 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, // 6 + 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, // 7 + 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF, // 8 + 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30DB, 0x30DE, 0x30DF, 0x30E0, // 9 + 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F2, 0x30F3, 0x30C3, 0x30E3, 0x30E5, 0x30E7, // A + 0x30A3, 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F, 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, // B + 0x305F, 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F, 0x3072, 0x3075, 0x30D8, 0x307B, 0x307E, // C + 0x307F, 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x30EA, 0x308B, 0x308C, 0x308D, 0x308F, 0x3092, 0x3093, 0x3063, // D + 0x3083, 0x3085, 0x3087, 0x30FC, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x30A1, 0x30A5, 0x30A7, 0x3000, 0x3000, 0x3000, 0x2642, // E + 0x3000, 0x3000, 0x3000, 0x3000, 0x30A9, 0x2640, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, // F + }; } diff --git a/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs b/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs index a611011fc..b748bda6a 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs @@ -1,549 +1,566 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using static PKHeX.Core.StringConverter12; -namespace PKHeX.Core +namespace PKHeX.Core; + +/// +/// Logic for converting a for Generation 2 Korean Gold/Silver. +/// +public static class StringConverter2KOR { /// - /// Logic for converting a for Generation 2 Korean Gold/Silver. + /// Checks if any of the characters inside are from the special Korean codepoint pages. /// - public static class StringConverter2KOR + public static bool GetIsG2Korean(ReadOnlySpan str) { - /// - /// Checks if any of the characters inside are from the special Korean codepoint pages. - /// - public static bool GetIsG2Korean(ReadOnlySpan str) + var dict = U2GSC_KOR; + foreach (var c in str) { - var dict = U2GSC_KOR; - foreach (var c in str) - { - if (!dict.Any(d => d.ContainsKey(c))) - return false; - } - return true; + if (!dict.Any(d => d.ContainsKey(c))) + return false; } - - /// - /// Converts Generation 2 Korean encoded data into a string. - /// - /// Encoded data. - /// Offset to read from - /// - /// Decoded string. - public static string GetString2KOR(byte[] data, int offset, int count) - { - var s = new StringBuilder(count); - for (int i = 0; i < count; i++) - { - var val = data[offset + i]; - var dict = val <= 0xB ? GSC2U_KOR[val] : RBY2U_U; - if (val is <= 0xB and not 0) - val = data[offset + (++i)]; - if (!dict.TryGetValue(val, out var c)) // Take valid values - break; - if (c == G1Terminator) // Stop if Terminator - break; - s.Append(c); - } - StringConverter.SanitizeString(s); - return s.ToString(); - } - - /// - /// Converts a string to Generation 1 encoded data. - /// - /// Decoded string. - /// Maximum length of the input - /// Pad the input to given length - /// Pad the input with this character value - /// Encoded data. - public static byte[] SetString2KOR(string value, int maxLength, int padTo = 0, byte padWith = 0) - { - if (value.StartsWith(G1TradeOTStr)) // Handle "[TRAINER]" - return new[] { G1TradeOTCode, G1TerminatorCode }; - - if (value.Length > maxLength) - value = value[..maxLength]; // Hard cap - - var kor = U2GSC_KOR; - var dict = U2RBY_U; - var capacity = Math.Max(value.Length * 2, padTo); - var arr = new List(capacity); - foreach (char c in value) - { - var koreanChar = false; - // although the 0x00 and 0x0B dictionaries are identical, the game only uses 0x0B. - for (byte i = 1; i < kor.Length; i++) - { - var table = kor[i]; - if (!table.TryGetValue(c, out byte val)) - continue; - koreanChar = true; - if (arr.Count + 1 >= maxLength) - break; // adding 2 characters will overflow requested buffer cap - arr.Add(i); - arr.Add(val); - break; - } - if (!koreanChar) - { - if (!dict.TryGetValue(c, out byte val)) - break; - arr.Add(val); - } - } - if (arr.Count > maxLength) - arr.RemoveRange(maxLength, arr.Count - maxLength); - arr.Add(0x50); // terminator - while (arr.Count < padTo) - arr.Add(padWith); - return arr.ToArray(); - } - - /// - /// Localizes a Gen4+ Korean species name to the localization used in Generation 2 Gold/Silver - /// - /// Generation 4 Species Name - /// Localized Name for Generation 2 - public static string LocalizeKOR2(string nick) - { - if (KorG2Localized.TryGetValue(nick, out string localized)) - return localized; - return nick; - } - - private static readonly Dictionary KorG2Localized = new() - { - { "덩쿠리", "덩구리" }, // Tangela - { "슈륙챙이", "수륙챙이" }, // Poliwhirl - }; - - #region Gen 2 Korean Character Tables - private static readonly Dictionary U2GSC_KOR_0 = new() - { - {'ㄱ', 0x00}, {'ㄴ', 0x01}, {'ㄷ', 0x02}, {'ㄹ', 0x03}, {'ㅁ', 0x04}, {'ㅂ', 0x05}, {'ㅅ', 0x06}, {'ㅇ', 0x07}, {'ㅈ', 0x08}, {'ㅊ', 0x09}, {'ㅋ', 0x0A}, {'ㅌ', 0x0B}, {'ㅍ', 0x0C}, {'ㅎ', 0x0D}, {'ㄲ', 0x0E}, {'ㄸ', 0x0F}, - {'ㅃ', 0x10}, {'ㅆ', 0x11}, {'ㅉ', 0x12}, - {'ㅏ', 0x20}, {'ㅑ', 0x21}, {'ㅓ', 0x22}, {'ㅕ', 0x23}, {'ㅗ', 0x24}, {'ㅛ', 0x25}, {'ㅜ', 0x26}, {'ㅠ', 0x27}, {'ㅡ', 0x28}, {'ㅣ', 0x29}, {'ㅐ', 0x2A}, {'ㅒ', 0x2B}, {'ㅔ', 0x2C}, {'ㅖ', 0x2D}, {'ㅘ', 0x2E}, {'ㅙ', 0x2F}, - {'ㅚ', 0x30}, {'ㅝ', 0x31}, {'ㅞ', 0x32}, {'ㅟ', 0x33}, {'ㅢ', 0x34}, {'_', 0x3E}, {'—', 0x3F}, - {'「', 0x60}, {'」', 0x61}, {'『', 0x62}, {'』', 0x63}, {'(', 0x64}, {')', 0x65}, {'!', 0x66}, {'?', 0x67}, {'-', 0x68}, {'~', 0x69}, {'…', 0x6A}, {',', 0x6B}, {'.', 0x6C}, // . is 0x6D for bank - {'0', 0xF0}, {'1', 0xF1}, {'2', 0xF2}, {'3', 0xF3}, {'4', 0xF4}, {'5', 0xF5}, {'6', 0xF6}, {'7', 0xF7}, {'8', 0xF8}, {'9', 0xF9}, - {'\u3000', 0xFF}, - }; - - private static readonly Dictionary U2GSC_KOR_1 = new() - { - {'가', 0x01}, {'각', 0x02}, {'간', 0x03}, {'갇', 0x04}, {'갈', 0x05}, {'갉', 0x06}, {'갊', 0x07}, {'감', 0x08}, {'갑', 0x09}, {'값', 0x0A}, {'갓', 0x0B}, {'갔', 0x0C}, {'강', 0x0D}, {'갖', 0x0E}, {'갗', 0x0F}, - {'같', 0x10}, {'갚', 0x11}, {'갛', 0x12}, {'개', 0x13}, {'객', 0x14}, {'갠', 0x15}, {'갤', 0x16}, {'갬', 0x17}, {'갭', 0x18}, {'갯', 0x19}, {'갰', 0x1A}, {'갱', 0x1B}, {'갸', 0x1C}, {'갹', 0x1D}, {'갼', 0x1E}, {'걀', 0x1F}, - {'걋', 0x20}, {'걍', 0x21}, {'걔', 0x22}, {'걘', 0x23}, {'걜', 0x24}, {'거', 0x25}, {'걱', 0x26}, {'건', 0x27}, {'걷', 0x28}, {'걸', 0x29}, {'걺', 0x2A}, {'검', 0x2B}, {'겁', 0x2C}, {'것', 0x2D}, {'겄', 0x2E}, {'겅', 0x2F}, - {'겆', 0x30}, {'겉', 0x31}, {'겊', 0x32}, {'겋', 0x33}, {'게', 0x34}, {'겐', 0x35}, {'겔', 0x36}, {'겜', 0x37}, {'겝', 0x38}, {'겟', 0x39}, {'겠', 0x3A}, {'겡', 0x3B}, {'겨', 0x3C}, {'격', 0x3D}, {'겪', 0x3E}, {'견', 0x3F}, - {'겯', 0x40}, {'결', 0x41}, {'겹', 0x42}, {'겸', 0x43}, {'겻', 0x44}, {'겼', 0x45}, {'경', 0x46}, {'곁', 0x47}, {'계', 0x48}, {'곈', 0x49}, {'곌', 0x4A}, {'곕', 0x4B}, {'곗', 0x4C}, {'고', 0x4D}, {'곡', 0x4E}, {'곤', 0x4F}, - {'곧', 0x60}, {'골', 0x61}, {'곪', 0x62}, {'곬', 0x63}, {'곯', 0x64}, {'곰', 0x65}, {'곱', 0x66}, {'곳', 0x67}, {'공', 0x68}, {'곶', 0x69}, {'과', 0x6A}, {'곽', 0x6B}, {'관', 0x6C}, {'괄', 0x6D}, {'괆', 0x6E}, - {'괌', 0x71}, {'괍', 0x72}, {'괏', 0x73}, {'광', 0x74}, {'괘', 0x75}, {'괜', 0x76}, {'괠', 0x77}, {'괩', 0x78}, {'괬', 0x79}, {'괭', 0x7A}, {'괴', 0x7B}, {'괵', 0x7C}, {'괸', 0x7D}, {'괼', 0x7E}, {'괻', 0x7F}, - {'굅', 0x80}, {'굇', 0x81}, {'굉', 0x82}, {'교', 0x83}, {'굔', 0x84}, {'굘', 0x85}, {'굡', 0x86}, {'굣', 0x87}, {'구', 0x88}, {'국', 0x89}, {'군', 0x8A}, {'굳', 0x8B}, {'굴', 0x8C}, {'굵', 0x8D}, {'굶', 0x8E}, {'굻', 0x8F}, - {'굼', 0x90}, {'굽', 0x91}, {'굿', 0x92}, {'궁', 0x93}, {'궂', 0x94}, {'궈', 0x95}, {'궉', 0x96}, {'권', 0x97}, {'궐', 0x98}, {'궜', 0x99}, {'궝', 0x9A}, {'궤', 0x9B}, {'궷', 0x9C}, {'귀', 0x9D}, {'귁', 0x9E}, {'귄', 0x9F}, - {'귈', 0xA0}, {'귐', 0xA1}, {'귑', 0xA2}, {'귓', 0xA3}, {'규', 0xA4}, {'균', 0xA5}, {'귤', 0xA6}, {'그', 0xA7}, {'극', 0xA8}, {'근', 0xA9}, {'귿', 0xAA}, {'글', 0xAB}, {'긁', 0xAC}, {'금', 0xAD}, {'급', 0xAE}, {'긋', 0xAF}, - {'긍', 0xB0}, {'긔', 0xB1}, {'기', 0xB2}, {'긱', 0xB3}, {'긴', 0xB4}, {'긷', 0xB5}, {'길', 0xB6}, {'긺', 0xB7}, {'김', 0xB8}, {'깁', 0xB9}, {'깃', 0xBA}, {'깅', 0xBB}, {'깆', 0xBC}, {'깊', 0xBD}, {'까', 0xBE}, {'깍', 0xBF}, - {'깎', 0xC0}, {'깐', 0xC1}, {'깔', 0xC2}, {'깖', 0xC3}, {'깜', 0xC4}, {'깝', 0xC5}, {'깟', 0xC6}, {'깠', 0xC7}, {'깡', 0xC8}, {'깥', 0xC9}, {'깨', 0xCA}, {'깩', 0xCB}, {'깬', 0xCC}, {'깰', 0xCD}, {'깸', 0xCE}, - {'깹', 0xD1}, {'깻', 0xD2}, {'깼', 0xD3}, {'깽', 0xD4}, {'꺄', 0xD5}, {'꺅', 0xD6}, {'꺌', 0xD7}, {'꺼', 0xD8}, {'꺽', 0xD9}, {'꺾', 0xDA}, {'껀', 0xDB}, {'껄', 0xDC}, {'껌', 0xDD}, {'껍', 0xDE}, {'껏', 0xDF}, - {'껐', 0xE0}, {'껑', 0xE1}, {'께', 0xE2}, {'껙', 0xE3}, {'껜', 0xE4}, {'껨', 0xE5}, {'껫', 0xE6}, {'껭', 0xE7}, {'껴', 0xE8}, {'껸', 0xE9}, {'껼', 0xEA}, {'꼇', 0xEB}, {'꼈', 0xEC}, {'꼍', 0xED}, {'꼐', 0xEE}, {'꼬', 0xEF}, - {'꼭', 0xF0}, {'꼰', 0xF1}, {'꼲', 0xF2}, {'꼴', 0xF3}, {'꼼', 0xF4}, {'꼽', 0xF5}, {'꼿', 0xF6}, {'꽁', 0xF7}, {'꽂', 0xF8}, {'꽃', 0xF9}, {'꽈', 0xFA}, {'꽉', 0xFB}, {'꽐', 0xFC}, {'꽜', 0xFD}, {'꽝', 0xFE}, {'꽤', 0xFF}, - }; - - private static readonly Dictionary U2GSC_KOR_2 = new() - { - {'꽥', 0x00}, {'꽹', 0x01}, {'꾀', 0x02}, {'꾄', 0x03}, {'꾈', 0x04}, {'꾐', 0x05}, {'꾑', 0x06}, {'꾕', 0x07}, {'꾜', 0x08}, {'꾸', 0x09}, {'꾹', 0x0A}, {'꾼', 0x0B}, {'꿀', 0x0C}, {'꿇', 0x0D}, {'꿈', 0x0E}, {'꿉', 0x0F}, - {'꿋', 0x10}, {'꿍', 0x11}, {'꿎', 0x12}, {'꿔', 0x13}, {'꿜', 0x14}, {'꿨', 0x15}, {'꿩', 0x16}, {'꿰', 0x17}, {'꿱', 0x18}, {'꿴', 0x19}, {'꿸', 0x1A}, {'뀀', 0x1B}, {'뀁', 0x1C}, {'뀄', 0x1D}, {'뀌', 0x1E}, {'뀐', 0x1F}, - {'뀔', 0x20}, {'뀜', 0x21}, {'뀝', 0x22}, {'뀨', 0x23}, {'끄', 0x24}, {'끅', 0x25}, {'끈', 0x26}, {'끊', 0x27}, {'끌', 0x28}, {'끎', 0x29}, {'끓', 0x2A}, {'끔', 0x2B}, {'끕', 0x2C}, {'끗', 0x2D}, {'끙', 0x2E}, - {'끝', 0x31}, {'끼', 0x32}, {'끽', 0x33}, {'낀', 0x34}, {'낄', 0x35}, {'낌', 0x36}, {'낍', 0x37}, {'낏', 0x38}, {'낑', 0x39}, {'나', 0x3A}, {'낙', 0x3B}, {'낚', 0x3C}, {'난', 0x3D}, {'낟', 0x3E}, {'날', 0x3F}, - {'낡', 0x40}, {'낢', 0x41}, {'남', 0x42}, {'납', 0x43}, {'낫', 0x44}, {'났', 0x45}, {'낭', 0x46}, {'낮', 0x47}, {'낯', 0x48}, {'낱', 0x49}, {'낳', 0x4A}, {'내', 0x4B}, {'낵', 0x4C}, {'낸', 0x4D}, {'낼', 0x4E}, {'냄', 0x4F}, - {'냅', 0x60}, {'냇', 0x61}, {'냈', 0x62}, {'냉', 0x63}, {'냐', 0x64}, {'냑', 0x65}, {'냔', 0x66}, {'냘', 0x67}, {'냠', 0x68}, {'냥', 0x69}, {'너', 0x6A}, {'넉', 0x6B}, {'넋', 0x6C}, {'넌', 0x6D}, {'널', 0x6E}, {'넒', 0x6F}, - {'넓', 0x70}, {'넘', 0x71}, {'넙', 0x72}, {'넛', 0x73}, {'넜', 0x74}, {'넝', 0x75}, {'넣', 0x76}, {'네', 0x77}, {'넥', 0x78}, {'넨', 0x79}, {'넬', 0x7A}, {'넴', 0x7B}, {'넵', 0x7C}, {'넷', 0x7D}, {'넸', 0x7E}, {'넹', 0x7F}, - {'녀', 0x80}, {'녁', 0x81}, {'년', 0x82}, {'녈', 0x83}, {'념', 0x84}, {'녑', 0x85}, {'녔', 0x86}, {'녕', 0x87}, {'녘', 0x88}, {'녜', 0x89}, {'녠', 0x8A}, {'노', 0x8B}, {'녹', 0x8C}, {'논', 0x8D}, {'놀', 0x8E}, {'놂', 0x8F}, - {'놈', 0x90}, {'놉', 0x91}, {'놋', 0x92}, {'농', 0x93}, {'높', 0x94}, {'놓', 0x95}, {'놔', 0x96}, {'놘', 0x97}, {'놜', 0x98}, {'놨', 0x99}, {'뇌', 0x9A}, {'뇐', 0x9B}, {'뇔', 0x9C}, {'뇜', 0x9D}, {'뇝', 0x9E}, - {'뇟', 0xA1}, {'뇨', 0xA2}, {'뇩', 0xA3}, {'뇬', 0xA4}, {'뇰', 0xA5}, {'뇹', 0xA6}, {'뇻', 0xA7}, {'뇽', 0xA8}, {'누', 0xA9}, {'눅', 0xAA}, {'눈', 0xAB}, {'눋', 0xAC}, {'눌', 0xAD}, {'눔', 0xAE}, {'눕', 0xAF}, - {'눗', 0xB0}, {'눙', 0xB1}, {'눠', 0xB2}, {'눴', 0xB3}, {'눼', 0xB4}, {'뉘', 0xB5}, {'뉜', 0xB6}, {'뉠', 0xB7}, {'뉨', 0xB8}, {'뉩', 0xB9}, {'뉴', 0xBA}, {'뉵', 0xBB}, {'뉼', 0xBC}, {'늄', 0xBD}, {'늅', 0xBE}, {'늉', 0xBF}, - {'느', 0xC0}, {'늑', 0xC1}, {'는', 0xC2}, {'늘', 0xC3}, {'늙', 0xC4}, {'늚', 0xC5}, {'늠', 0xC6}, {'늡', 0xC7}, {'늣', 0xC8}, {'능', 0xC9}, {'늦', 0xCA}, {'늪', 0xCB}, {'늬', 0xCC}, {'늰', 0xCD}, {'늴', 0xCE}, {'니', 0xCF}, - {'닉', 0xD0}, {'닌', 0xD1}, {'닐', 0xD2}, {'닒', 0xD3}, {'님', 0xD4}, {'닙', 0xD5}, {'닛', 0xD6}, {'닝', 0xD7}, {'닢', 0xD8}, {'다', 0xD9}, {'닥', 0xDA}, {'닦', 0xDB}, {'단', 0xDC}, {'닫', 0xDD}, {'달', 0xDE}, {'닭', 0xDF}, - {'닮', 0xE0}, {'닯', 0xE1}, {'닳', 0xE2}, {'담', 0xE3}, {'답', 0xE4}, {'닷', 0xE5}, {'닸', 0xE6}, {'당', 0xE7}, {'닺', 0xE8}, {'닻', 0xE9}, {'닿', 0xEA}, {'대', 0xEB}, {'댁', 0xEC}, {'댄', 0xED}, {'댈', 0xEE}, {'댐', 0xEF}, - {'댑', 0xF0}, {'댓', 0xF1}, {'댔', 0xF2}, {'댕', 0xF3}, {'더', 0xF5}, {'덕', 0xF6}, {'덖', 0xF7}, {'던', 0xF8}, {'덛', 0xF9}, {'덜', 0xFA}, {'덞', 0xFB}, {'덟', 0xFC}, {'덤', 0xFD}, {'덥', 0xFE}, - }; - - private static readonly Dictionary U2GSC_KOR_3 = new() - { - {'덧', 0x01}, {'덩', 0x02}, {'덫', 0x03}, {'덮', 0x04}, {'데', 0x05}, {'덱', 0x06}, {'덴', 0x07}, {'델', 0x08}, {'뎀', 0x09}, {'뎁', 0x0A}, {'뎃', 0x0B}, {'뎄', 0x0C}, {'뎅', 0x0D}, {'뎌', 0x0E}, {'뎐', 0x0F}, - {'뎔', 0x10}, {'뎠', 0x11}, {'뎡', 0x12}, {'뎨', 0x13}, {'뎬', 0x14}, {'도', 0x15}, {'독', 0x16}, {'돈', 0x17}, {'돋', 0x18}, {'돌', 0x19}, {'돎', 0x1A}, {'돔', 0x1C}, {'돕', 0x1D}, {'돗', 0x1E}, {'동', 0x1F}, - {'돛', 0x20}, {'돝', 0x21}, {'돠', 0x22}, {'돤', 0x23}, {'돨', 0x24}, {'돼', 0x25}, {'됐', 0x26}, {'되', 0x27}, {'된', 0x28}, {'될', 0x29}, {'됨', 0x2A}, {'됩', 0x2B}, {'됫', 0x2C}, {'됴', 0x2D}, {'두', 0x2E}, {'둑', 0x2F}, - {'둔', 0x30}, {'둘', 0x31}, {'둠', 0x32}, {'둡', 0x33}, {'둣', 0x34}, {'둥', 0x35}, {'둬', 0x36}, {'뒀', 0x37}, {'뒈', 0x38}, {'뒝', 0x39}, {'뒤', 0x3A}, {'뒨', 0x3B}, {'뒬', 0x3C}, {'뒵', 0x3D}, {'뒷', 0x3E}, {'뒹', 0x3F}, - {'듀', 0x40}, {'듄', 0x41}, {'듈', 0x42}, {'듐', 0x43}, {'듕', 0x44}, {'드', 0x45}, {'득', 0x46}, {'든', 0x47}, {'듣', 0x48}, {'들', 0x49}, {'듦', 0x4A}, {'듬', 0x4B}, {'듭', 0x4C}, {'듯', 0x4D}, {'등', 0x4E}, {'듸', 0x4F}, - {'디', 0x60}, {'딕', 0x61}, {'딘', 0x62}, {'딛', 0x63}, {'딜', 0x64}, {'딤', 0x65}, {'딥', 0x66}, {'딧', 0x67}, {'딨', 0x68}, {'딩', 0x69}, {'딪', 0x6A}, {'따', 0x6B}, {'딱', 0x6C}, {'딴', 0x6D}, {'딸', 0x6E}, - {'땀', 0x71}, {'땁', 0x72}, {'땃', 0x73}, {'땄', 0x74}, {'땅', 0x75}, {'땋', 0x76}, {'때', 0x77}, {'땍', 0x78}, {'땐', 0x79}, {'땔', 0x7A}, {'땜', 0x7B}, {'땝', 0x7C}, {'땟', 0x7D}, {'땠', 0x7E}, {'땡', 0x7F}, - {'떠', 0x80}, {'떡', 0x81}, {'떤', 0x82}, {'떨', 0x83}, {'떪', 0x84}, {'떫', 0x85}, {'떰', 0x86}, {'떱', 0x87}, {'떳', 0x88}, {'떴', 0x89}, {'떵', 0x8A}, {'떻', 0x8B}, {'떼', 0x8C}, {'떽', 0x8D}, {'뗀', 0x8E}, {'뗄', 0x8F}, - {'뗌', 0x90}, {'뗍', 0x91}, {'뗏', 0x92}, {'뗐', 0x93}, {'뗑', 0x94}, {'뗘', 0x95}, {'뗬', 0x96}, {'또', 0x97}, {'똑', 0x98}, {'똔', 0x99}, {'똘', 0x9A}, {'똥', 0x9B}, {'똬', 0x9C}, {'똴', 0x9D}, {'뙈', 0x9E}, {'뙤', 0x9F}, - {'뙨', 0xA0}, {'뚜', 0xA1}, {'뚝', 0xA2}, {'뚠', 0xA3}, {'뚤', 0xA4}, {'뚫', 0xA5}, {'뚬', 0xA6}, {'뚱', 0xA7}, {'뛔', 0xA8}, {'뛰', 0xA9}, {'뛴', 0xAA}, {'뛸', 0xAB}, {'뜀', 0xAC}, {'뜁', 0xAD}, {'뜅', 0xAE}, {'뜨', 0xAF}, - {'뜩', 0xB0}, {'뜬', 0xB1}, {'뜯', 0xB2}, {'뜰', 0xB3}, {'뜸', 0xB4}, {'뜹', 0xB5}, {'뜻', 0xB6}, {'띄', 0xB7}, {'띈', 0xB8}, {'띌', 0xB9}, {'띔', 0xBA}, {'띕', 0xBB}, {'띠', 0xBC}, {'띤', 0xBD}, {'띨', 0xBE}, {'띰', 0xBF}, - {'띱', 0xC0}, {'띳', 0xC1}, {'띵', 0xC2}, {'라', 0xC3}, {'락', 0xC4}, {'란', 0xC5}, {'랄', 0xC6}, {'람', 0xC7}, {'랍', 0xC8}, {'랏', 0xC9}, {'랐', 0xCA}, {'랑', 0xCB}, {'랒', 0xCC}, {'랖', 0xCD}, {'랗', 0xCE}, - {'뢔', 0xD0}, {'래', 0xD1}, {'랙', 0xD2}, {'랜', 0xD3}, {'랠', 0xD4}, {'램', 0xD5}, {'랩', 0xD6}, {'랫', 0xD7}, {'랬', 0xD8}, {'랭', 0xD9}, {'랴', 0xDA}, {'략', 0xDB}, {'랸', 0xDC}, {'럇', 0xDD}, {'량', 0xDE}, {'러', 0xDF}, - {'럭', 0xE0}, {'런', 0xE1}, {'럴', 0xE2}, {'럼', 0xE3}, {'럽', 0xE4}, {'럿', 0xE5}, {'렀', 0xE6}, {'렁', 0xE7}, {'렇', 0xE8}, {'레', 0xE9}, {'렉', 0xEA}, {'렌', 0xEB}, {'렐', 0xEC}, {'렘', 0xED}, {'렙', 0xEE}, {'렛', 0xEF}, - {'렝', 0xF0}, {'려', 0xF1}, {'력', 0xF2}, {'련', 0xF3}, {'렬', 0xF4}, {'렴', 0xF5}, {'렵', 0xF6}, {'렷', 0xF7}, {'렸', 0xF8}, {'령', 0xF9}, {'례', 0xFA}, {'롄', 0xFB}, {'롑', 0xFC}, {'롓', 0xFD}, {'로', 0xFE}, {'록', 0xFF}, - }; - - private static readonly Dictionary U2GSC_KOR_4 = new() - { - {'론', 0x00}, {'롤', 0x01}, {'롬', 0x02}, {'롭', 0x03}, {'롯', 0x04}, {'롱', 0x05}, {'롸', 0x06}, {'롼', 0x07}, {'뢍', 0x08}, {'뢨', 0x09}, {'뢰', 0x0A}, {'뢴', 0x0B}, {'뢸', 0x0C}, {'룀', 0x0D}, {'룁', 0x0E}, {'룃', 0x0F}, - {'룅', 0x10}, {'료', 0x11}, {'룐', 0x12}, {'룔', 0x13}, {'룝', 0x14}, {'룟', 0x15}, {'룡', 0x16}, {'루', 0x17}, {'룩', 0x18}, {'룬', 0x19}, {'룰', 0x1A}, {'룸', 0x1B}, {'룹', 0x1C}, {'룻', 0x1D}, {'룽', 0x1E}, {'뤄', 0x1F}, - {'뤘', 0x20}, {'뤠', 0x21}, {'뤼', 0x22}, {'뤽', 0x23}, {'륀', 0x24}, {'륄', 0x25}, {'륌', 0x26}, {'륏', 0x27}, {'륑', 0x28}, {'류', 0x29}, {'륙', 0x2A}, {'륜', 0x2B}, {'률', 0x2C}, {'륨', 0x2D}, {'륩', 0x2E}, - {'륫', 0x31}, {'륭', 0x32}, {'르', 0x33}, {'륵', 0x34}, {'른', 0x35}, {'를', 0x36}, {'름', 0x37}, {'릅', 0x38}, {'릇', 0x39}, {'릉', 0x3A}, {'릊', 0x3B}, {'릍', 0x3C}, {'릎', 0x3D}, {'리', 0x3E}, {'릭', 0x3F}, - {'린', 0x40}, {'릴', 0x41}, {'림', 0x42}, {'립', 0x43}, {'릿', 0x44}, {'링', 0x45}, {'마', 0x46}, {'막', 0x47}, {'만', 0x48}, {'많', 0x49}, {'맏', 0x4A}, {'말', 0x4B}, {'맑', 0x4C}, {'맒', 0x4D}, {'맘', 0x4E}, {'맙', 0x4F}, - {'맛', 0x60}, {'망', 0x61}, {'맞', 0x62}, {'맡', 0x63}, {'맣', 0x64}, {'매', 0x65}, {'맥', 0x66}, {'맨', 0x67}, {'맬', 0x68}, {'맴', 0x69}, {'맵', 0x6A}, {'맷', 0x6B}, {'맸', 0x6C}, {'맹', 0x6D}, {'맺', 0x6E}, {'먀', 0x6F}, - {'먁', 0x70}, {'먈', 0x71}, {'먕', 0x72}, {'머', 0x73}, {'먹', 0x74}, {'먼', 0x75}, {'멀', 0x76}, {'멂', 0x77}, {'멈', 0x78}, {'멉', 0x79}, {'멋', 0x7A}, {'멍', 0x7B}, {'멎', 0x7C}, {'멓', 0x7D}, {'메', 0x7E}, {'멕', 0x7F}, - {'멘', 0x80}, {'멜', 0x81}, {'멤', 0x82}, {'멥', 0x83}, {'멧', 0x84}, {'멨', 0x85}, {'멩', 0x86}, {'며', 0x87}, {'멱', 0x88}, {'면', 0x89}, {'멸', 0x8A}, {'몃', 0x8B}, {'몄', 0x8C}, {'명', 0x8D}, {'몇', 0x8E}, {'몌', 0x8F}, - {'모', 0x90}, {'목', 0x91}, {'몫', 0x92}, {'몬', 0x93}, {'몰', 0x94}, {'몲', 0x95}, {'몸', 0x96}, {'몹', 0x97}, {'못', 0x98}, {'몽', 0x99}, {'뫄', 0x9A}, {'뫈', 0x9B}, {'뫘', 0x9C}, {'뫙', 0x9D}, {'뫼', 0x9E}, - {'묀', 0xA1}, {'묄', 0xA2}, {'묍', 0xA3}, {'묏', 0xA4}, {'묑', 0xA5}, {'묘', 0xA6}, {'묜', 0xA7}, {'묠', 0xA8}, {'묩', 0xA9}, {'묫', 0xAA}, {'무', 0xAB}, {'묵', 0xAC}, {'묶', 0xAD}, {'문', 0xAE}, {'묻', 0xAF}, - {'물', 0xB0}, {'묽', 0xB1}, {'묾', 0xB2}, {'뭄', 0xB3}, {'뭅', 0xB4}, {'뭇', 0xB5}, {'뭉', 0xB6}, {'뭍', 0xB7}, {'뭏', 0xB8}, {'뭐', 0xB9}, {'뭔', 0xBA}, {'뭘', 0xBB}, {'뭡', 0xBC}, {'뭣', 0xBD}, {'뭬', 0xBE}, {'뮈', 0xBF}, - {'뮌', 0xC0}, {'뮐', 0xC1}, {'뮤', 0xC2}, {'뮨', 0xC3}, {'뮬', 0xC4}, {'뮴', 0xC5}, {'뮷', 0xC6}, {'므', 0xC7}, {'믄', 0xC8}, {'믈', 0xC9}, {'믐', 0xCA}, {'믓', 0xCB}, {'미', 0xCC}, {'믹', 0xCD}, {'민', 0xCE}, {'믿', 0xCF}, - {'밀', 0xD0}, {'밂', 0xD1}, {'밈', 0xD2}, {'밉', 0xD3}, {'밋', 0xD4}, {'밌', 0xD5}, {'밍', 0xD6}, {'및', 0xD7}, {'밑', 0xD8}, {'바', 0xD9}, {'박', 0xDA}, {'밖', 0xDB}, {'밗', 0xDC}, {'반', 0xDD}, {'받', 0xDE}, {'발', 0xDF}, - {'밝', 0xE0}, {'밞', 0xE1}, {'밟', 0xE2}, {'밤', 0xE3}, {'밥', 0xE4}, {'밧', 0xE5}, {'방', 0xE6}, {'밭', 0xE7}, {'배', 0xE8}, {'백', 0xE9}, {'밴', 0xEA}, {'밸', 0xEB}, {'뱀', 0xEC}, {'뱁', 0xED}, {'뱃', 0xEE}, {'뱄', 0xEF}, - {'뱅', 0xF0}, {'뱉', 0xF1}, {'뱌', 0xF2}, {'뱍', 0xF3}, {'뱐', 0xF4}, {'뱝', 0xF5}, {'버', 0xF6}, {'벅', 0xF7}, {'번', 0xF8}, {'벋', 0xF9}, {'벌', 0xFA}, {'벎', 0xFB}, {'범', 0xFC}, {'법', 0xFD}, {'벗', 0xFE}, - }; - - private static readonly Dictionary U2GSC_KOR_5 = new() - { - {'벙', 0x01}, {'벚', 0x02}, {'베', 0x03}, {'벡', 0x04}, {'벤', 0x05}, {'벧', 0x06}, {'벨', 0x07}, {'벰', 0x08}, {'벱', 0x09}, {'벳', 0x0A}, {'벴', 0x0B}, {'벵', 0x0C}, {'벼', 0x0D}, {'벽', 0x0E}, {'변', 0x0F}, - {'별', 0x10}, {'볍', 0x11}, {'볏', 0x12}, {'볐', 0x13}, {'병', 0x14}, {'볕', 0x15}, {'볘', 0x16}, {'볜', 0x17}, {'보', 0x18}, {'복', 0x19}, {'볶', 0x1A}, {'본', 0x1B}, {'볼', 0x1C}, {'봄', 0x1D}, {'봅', 0x1E}, {'봇', 0x1F}, - {'봉', 0x20}, {'봐', 0x21}, {'봔', 0x22}, {'봤', 0x23}, {'봬', 0x24}, {'뵀', 0x25}, {'뵈', 0x26}, {'뵉', 0x27}, {'뵌', 0x28}, {'뵐', 0x29}, {'뵘', 0x2A}, {'뵙', 0x2B}, {'뵤', 0x2C}, {'뵨', 0x2D}, {'부', 0x2E}, {'북', 0x2F}, - {'분', 0x30}, {'붇', 0x31}, {'불', 0x32}, {'붉', 0x33}, {'붊', 0x34}, {'붐', 0x35}, {'붑', 0x36}, {'붓', 0x37}, {'붕', 0x38}, {'붙', 0x39}, {'붚', 0x3A}, {'붜', 0x3B}, {'붤', 0x3C}, {'붰', 0x3D}, {'붸', 0x3E}, {'뷔', 0x3F}, - {'뷕', 0x40}, {'뷘', 0x41}, {'뷜', 0x42}, {'뷩', 0x43}, {'뷰', 0x44}, {'뷴', 0x45}, {'뷸', 0x46}, {'븀', 0x47}, {'븃', 0x48}, {'븅', 0x49}, {'브', 0x4A}, {'븍', 0x4B}, {'븐', 0x4C}, {'블', 0x4D}, {'븜', 0x4E}, {'븝', 0x4F}, - {'븟', 0x60}, {'비', 0x61}, {'빅', 0x62}, {'빈', 0x63}, {'빌', 0x64}, {'빎', 0x65}, {'빔', 0x66}, {'빕', 0x67}, {'빗', 0x68}, {'빙', 0x69}, {'빚', 0x6A}, {'빛', 0x6B}, {'빠', 0x6C}, {'빡', 0x6D}, {'빤', 0x6E}, - {'빨', 0x71}, {'빪', 0x72}, {'빰', 0x73}, {'빱', 0x74}, {'빳', 0x75}, {'빴', 0x76}, {'빵', 0x77}, {'빻', 0x78}, {'빼', 0x79}, {'빽', 0x7A}, {'뺀', 0x7B}, {'뺄', 0x7C}, {'뺌', 0x7D}, {'뺍', 0x7E}, {'뺏', 0x7F}, - {'뺐', 0x80}, {'뺑', 0x81}, {'뺘', 0x82}, {'뺙', 0x83}, {'뺨', 0x84}, {'뻐', 0x85}, {'뻑', 0x86}, {'뻔', 0x87}, {'뻗', 0x88}, {'뻘', 0x89}, {'뻠', 0x8A}, {'뻣', 0x8B}, {'뻤', 0x8C}, {'뻥', 0x8D}, {'뻬', 0x8E}, {'뼁', 0x8F}, - {'뼈', 0x90}, {'뼉', 0x91}, {'뼘', 0x92}, {'뼙', 0x93}, {'뼛', 0x94}, {'뼜', 0x95}, {'뼝', 0x96}, {'뽀', 0x97}, {'뽁', 0x98}, {'뽄', 0x99}, {'뽈', 0x9A}, {'뽐', 0x9B}, {'뽑', 0x9C}, {'뽕', 0x9D}, {'뾔', 0x9E}, {'뾰', 0x9F}, - {'뿅', 0xA0}, {'뿌', 0xA1}, {'뿍', 0xA2}, {'뿐', 0xA3}, {'뿔', 0xA4}, {'뿜', 0xA5}, {'뿟', 0xA6}, {'뿡', 0xA7}, {'쀼', 0xA8}, {'쁑', 0xA9}, {'쁘', 0xAA}, {'쁜', 0xAB}, {'쁠', 0xAC}, {'쁨', 0xAD}, {'쁩', 0xAE}, {'삐', 0xAF}, - {'삑', 0xB0}, {'삔', 0xB1}, {'삘', 0xB2}, {'삠', 0xB3}, {'삡', 0xB4}, {'삣', 0xB5}, {'삥', 0xB6}, {'사', 0xB7}, {'삭', 0xB8}, {'삯', 0xB9}, {'산', 0xBA}, {'삳', 0xBB}, {'살', 0xBC}, {'삵', 0xBD}, {'삶', 0xBE}, {'삼', 0xBF}, - {'삽', 0xC0}, {'삿', 0xC1}, {'샀', 0xC2}, {'상', 0xC3}, {'샅', 0xC4}, {'새', 0xC5}, {'색', 0xC6}, {'샌', 0xC7}, {'샐', 0xC8}, {'샘', 0xC9}, {'샙', 0xCA}, {'샛', 0xCB}, {'샜', 0xCC}, {'생', 0xCD}, {'샤', 0xCE}, - {'샥', 0xD1}, {'샨', 0xD2}, {'샬', 0xD3}, {'샴', 0xD4}, {'샵', 0xD5}, {'샷', 0xD6}, {'샹', 0xD7}, {'섀', 0xD8}, {'섄', 0xD9}, {'섈', 0xDA}, {'섐', 0xDB}, {'섕', 0xDC}, {'서', 0xDD}, {'석', 0xDE}, {'섞', 0xDF}, - {'섟', 0xE0}, {'선', 0xE1}, {'섣', 0xE2}, {'설', 0xE3}, {'섦', 0xE4}, {'섧', 0xE5}, {'섬', 0xE6}, {'섭', 0xE7}, {'섯', 0xE8}, {'섰', 0xE9}, {'성', 0xEA}, {'섶', 0xEB}, {'세', 0xEC}, {'섹', 0xED}, {'센', 0xEE}, {'셀', 0xEF}, - {'셈', 0xF0}, {'셉', 0xF1}, {'셋', 0xF2}, {'셌', 0xF3}, {'셍', 0xF4}, {'셔', 0xF5}, {'셕', 0xF6}, {'션', 0xF7}, {'셜', 0xF8}, {'셤', 0xF9}, {'셥', 0xFA}, {'셧', 0xFB}, {'셨', 0xFC}, {'셩', 0xFD}, {'셰', 0xFE}, {'셴', 0xFF}, - }; - - private static readonly Dictionary U2GSC_KOR_6 = new() - { - {'셸', 0x00}, {'솅', 0x01}, {'소', 0x02}, {'속', 0x03}, {'솎', 0x04}, {'손', 0x05}, {'솔', 0x06}, {'솖', 0x07}, {'솜', 0x08}, {'솝', 0x09}, {'솟', 0x0A}, {'송', 0x0B}, {'솥', 0x0C}, {'솨', 0x0D}, {'솩', 0x0E}, {'솬', 0x0F}, - {'솰', 0x10}, {'솽', 0x11}, {'쇄', 0x12}, {'쇈', 0x13}, {'쇌', 0x14}, {'쇔', 0x15}, {'쇗', 0x16}, {'쇘', 0x17}, {'쇠', 0x18}, {'쇤', 0x19}, {'쇨', 0x1A}, {'쇰', 0x1B}, {'쇱', 0x1C}, {'쇳', 0x1D}, {'쇼', 0x1E}, {'쇽', 0x1F}, - {'숀', 0x20}, {'숄', 0x21}, {'숌', 0x22}, {'숍', 0x23}, {'숏', 0x24}, {'숑', 0x25}, {'수', 0x26}, {'숙', 0x27}, {'순', 0x28}, {'숟', 0x29}, {'술', 0x2A}, {'숨', 0x2B}, {'숩', 0x2C}, {'숫', 0x2D}, {'숭', 0x2E}, {'쌰', 0x2F}, - {'쎼', 0x30}, {'숯', 0x31}, {'숱', 0x32}, {'숲', 0x33}, {'숴', 0x34}, {'쉈', 0x35}, {'쉐', 0x36}, {'쉑', 0x37}, {'쉔', 0x38}, {'쉘', 0x39}, {'쉠', 0x3A}, {'쉥', 0x3B}, {'쉬', 0x3C}, {'쉭', 0x3D}, {'쉰', 0x3E}, {'쉴', 0x3F}, - {'쉼', 0x40}, {'쉽', 0x41}, {'쉿', 0x42}, {'슁', 0x43}, {'슈', 0x44}, {'슉', 0x45}, {'슐', 0x46}, {'슘', 0x47}, {'슛', 0x48}, {'슝', 0x49}, {'스', 0x4A}, {'슥', 0x4B}, {'슨', 0x4C}, {'슬', 0x4D}, {'슭', 0x4E}, {'슴', 0x4F}, - {'습', 0x60}, {'슷', 0x61}, {'승', 0x62}, {'시', 0x63}, {'식', 0x64}, {'신', 0x65}, {'싣', 0x66}, {'실', 0x67}, {'싫', 0x68}, {'심', 0x69}, {'십', 0x6A}, {'싯', 0x6B}, {'싱', 0x6C}, {'싶', 0x6D}, {'싸', 0x6E}, {'싹', 0x6F}, - {'싻', 0x70}, {'싼', 0x71}, {'쌀', 0x72}, {'쌈', 0x73}, {'쌉', 0x74}, {'쌌', 0x75}, {'쌍', 0x76}, {'쌓', 0x77}, {'쌔', 0x78}, {'쌕', 0x79}, {'쌘', 0x7A}, {'쌜', 0x7B}, {'쌤', 0x7C}, {'쌥', 0x7D}, {'쌨', 0x7E}, {'쌩', 0x7F}, - {'썅', 0x80}, {'써', 0x81}, {'썩', 0x82}, {'썬', 0x83}, {'썰', 0x84}, {'썲', 0x85}, {'썸', 0x86}, {'썹', 0x87}, {'썼', 0x88}, {'썽', 0x89}, {'쎄', 0x8A}, {'쎈', 0x8B}, {'쎌', 0x8C}, {'쏀', 0x8D}, {'쏘', 0x8E}, {'쏙', 0x8F}, - {'쏜', 0x90}, {'쏟', 0x91}, {'쏠', 0x92}, {'쏢', 0x93}, {'쏨', 0x94}, {'쏩', 0x95}, {'쏭', 0x96}, {'쏴', 0x97}, {'쏵', 0x98}, {'쏸', 0x99}, {'쐈', 0x9A}, {'쐐', 0x9B}, {'쐤', 0x9C}, {'쐬', 0x9D}, {'쐰', 0x9E}, - {'쓔', 0xA0}, {'쐴', 0xA1}, {'쐼', 0xA2}, {'쐽', 0xA3}, {'쑈', 0xA4}, {'쑤', 0xA5}, {'쑥', 0xA6}, {'쑨', 0xA7}, {'쑬', 0xA8}, {'쑴', 0xA9}, {'쑵', 0xAA}, {'쑹', 0xAB}, {'쒀', 0xAC}, {'쒔', 0xAD}, {'쒜', 0xAE}, {'쒸', 0xAF}, - {'쒼', 0xB0}, {'쓩', 0xB1}, {'쓰', 0xB2}, {'쓱', 0xB3}, {'쓴', 0xB4}, {'쓸', 0xB5}, {'쓺', 0xB6}, {'쓿', 0xB7}, {'씀', 0xB8}, {'씁', 0xB9}, {'씌', 0xBA}, {'씐', 0xBB}, {'씔', 0xBC}, {'씜', 0xBD}, {'씨', 0xBE}, {'씩', 0xBF}, - {'씬', 0xC0}, {'씰', 0xC1}, {'씸', 0xC2}, {'씹', 0xC3}, {'씻', 0xC4}, {'씽', 0xC5}, {'아', 0xC6}, {'악', 0xC7}, {'안', 0xC8}, {'앉', 0xC9}, {'않', 0xCA}, {'알', 0xCB}, {'앍', 0xCC}, {'앎', 0xCD}, {'앓', 0xCE}, {'암', 0xCF}, - {'압', 0xD0}, {'앗', 0xD1}, {'았', 0xD2}, {'앙', 0xD3}, {'앝', 0xD4}, {'앞', 0xD5}, {'애', 0xD6}, {'액', 0xD7}, {'앤', 0xD8}, {'앨', 0xD9}, {'앰', 0xDA}, {'앱', 0xDB}, {'앳', 0xDC}, {'앴', 0xDD}, {'앵', 0xDE}, {'야', 0xDF}, - {'약', 0xE0}, {'얀', 0xE1}, {'얄', 0xE2}, {'얇', 0xE3}, {'얌', 0xE4}, {'얍', 0xE5}, {'얏', 0xE6}, {'양', 0xE7}, {'얕', 0xE8}, {'얗', 0xE9}, {'얘', 0xEA}, {'얜', 0xEB}, {'얠', 0xEC}, {'얩', 0xED}, {'어', 0xEE}, {'억', 0xEF}, - {'언', 0xF0}, {'얹', 0xF1}, {'얻', 0xF2}, {'얼', 0xF3}, {'얽', 0xF4}, {'얾', 0xF5}, {'엄', 0xF6}, {'업', 0xF7}, {'없', 0xF8}, {'엇', 0xF9}, {'었', 0xFA}, {'엉', 0xFB}, {'엊', 0xFC}, {'엌', 0xFD}, {'엎', 0xFE}, - }; - - private static readonly Dictionary U2GSC_KOR_7 = new() - { - {'에', 0x01}, {'엑', 0x02}, {'엔', 0x03}, {'엘', 0x04}, {'엠', 0x05}, {'엡', 0x06}, {'엣', 0x07}, {'엥', 0x08}, {'여', 0x09}, {'역', 0x0A}, {'엮', 0x0B}, {'연', 0x0C}, {'열', 0x0D}, {'엶', 0x0E}, {'엷', 0x0F}, - {'염', 0x10}, {'엽', 0x11}, {'엾', 0x12}, {'엿', 0x13}, {'였', 0x14}, {'영', 0x15}, {'옅', 0x16}, {'옆', 0x17}, {'옇', 0x18}, {'예', 0x19}, {'옌', 0x1A}, {'옐', 0x1B}, {'옘', 0x1C}, {'옙', 0x1D}, {'옛', 0x1E}, {'옜', 0x1F}, - {'오', 0x20}, {'옥', 0x21}, {'온', 0x22}, {'올', 0x23}, {'옭', 0x24}, {'옮', 0x25}, {'옰', 0x26}, {'옳', 0x27}, {'옴', 0x28}, {'옵', 0x29}, {'옷', 0x2A}, {'옹', 0x2B}, {'옻', 0x2C}, {'와', 0x2D}, {'왁', 0x2E}, {'완', 0x2F}, - {'왈', 0x30}, {'왐', 0x31}, {'왑', 0x32}, {'왓', 0x33}, {'왔', 0x34}, {'왕', 0x35}, {'왜', 0x36}, {'왝', 0x37}, {'왠', 0x38}, {'왬', 0x39}, {'왯', 0x3A}, {'왱', 0x3B}, {'외', 0x3C}, {'왹', 0x3D}, {'왼', 0x3E}, {'욀', 0x3F}, - {'욈', 0x40}, {'욉', 0x41}, {'욋', 0x42}, {'욍', 0x43}, {'요', 0x44}, {'욕', 0x45}, {'욘', 0x46}, {'욜', 0x47}, {'욤', 0x48}, {'욥', 0x49}, {'욧', 0x4A}, {'용', 0x4B}, {'우', 0x4C}, {'욱', 0x4D}, {'운', 0x4E}, {'울', 0x4F}, - {'욹', 0x60}, {'욺', 0x61}, {'움', 0x62}, {'웁', 0x63}, {'웃', 0x64}, {'웅', 0x65}, {'워', 0x66}, {'웍', 0x67}, {'원', 0x68}, {'월', 0x69}, {'웜', 0x6A}, {'웝', 0x6B}, {'웠', 0x6C}, {'웡', 0x6D}, {'웨', 0x6E}, - {'웩', 0x71}, {'웬', 0x72}, {'웰', 0x73}, {'웸', 0x74}, {'웹', 0x75}, {'웽', 0x76}, {'위', 0x77}, {'윅', 0x78}, {'윈', 0x79}, {'윌', 0x7A}, {'윔', 0x7B}, {'윕', 0x7C}, {'윗', 0x7D}, {'윙', 0x7E}, {'유', 0x7F}, - {'육', 0x80}, {'윤', 0x81}, {'율', 0x82}, {'윰', 0x83}, {'윱', 0x84}, {'윳', 0x85}, {'융', 0x86}, {'윷', 0x87}, {'으', 0x88}, {'윽', 0x89}, {'은', 0x8A}, {'을', 0x8B}, {'읆', 0x8C}, {'음', 0x8D}, {'읍', 0x8E}, {'읏', 0x8F}, - {'응', 0x90}, {'읒', 0x91}, {'읓', 0x92}, {'읔', 0x93}, {'읕', 0x94}, {'읖', 0x95}, {'읗', 0x96}, {'의', 0x97}, {'읜', 0x98}, {'읠', 0x99}, {'읨', 0x9A}, {'읫', 0x9B}, {'이', 0x9C}, {'익', 0x9D}, {'인', 0x9E}, {'일', 0x9F}, - {'읽', 0xA0}, {'읾', 0xA1}, {'잃', 0xA2}, {'임', 0xA3}, {'입', 0xA4}, {'잇', 0xA5}, {'있', 0xA6}, {'잉', 0xA7}, {'잊', 0xA8}, {'잎', 0xA9}, {'자', 0xAA}, {'작', 0xAB}, {'잔', 0xAC}, {'잖', 0xAD}, {'잗', 0xAE}, {'잘', 0xAF}, - {'잚', 0xB0}, {'잠', 0xB1}, {'잡', 0xB2}, {'잣', 0xB3}, {'잤', 0xB4}, {'장', 0xB5}, {'잦', 0xB6}, {'재', 0xB7}, {'잭', 0xB8}, {'잰', 0xB9}, {'잴', 0xBA}, {'잼', 0xBB}, {'잽', 0xBC}, {'잿', 0xBD}, {'쟀', 0xBE}, {'쟁', 0xBF}, - {'쟈', 0xC0}, {'쟉', 0xC1}, {'쟌', 0xC2}, {'쟎', 0xC3}, {'쟐', 0xC4}, {'쟘', 0xC5}, {'쟝', 0xC6}, {'쟤', 0xC7}, {'쟨', 0xC8}, {'쟬', 0xC9}, {'저', 0xCA}, {'적', 0xCB}, {'전', 0xCC}, {'절', 0xCD}, {'젊', 0xCE}, - {'점', 0xD1}, {'접', 0xD2}, {'젓', 0xD3}, {'정', 0xD4}, {'젖', 0xD5}, {'제', 0xD6}, {'젝', 0xD7}, {'젠', 0xD8}, {'젤', 0xD9}, {'젬', 0xDA}, {'젭', 0xDB}, {'젯', 0xDC}, {'젱', 0xDD}, {'져', 0xDE}, {'젼', 0xDF}, - {'졀', 0xE0}, {'졈', 0xE1}, {'졉', 0xE2}, {'졌', 0xE3}, {'졍', 0xE4}, {'졔', 0xE5}, {'조', 0xE6}, {'족', 0xE7}, {'존', 0xE8}, {'졸', 0xE9}, {'졺', 0xEA}, {'좀', 0xEB}, {'좁', 0xEC}, {'좃', 0xED}, {'종', 0xEE}, {'좆', 0xEF}, - {'좇', 0xF0}, {'좋', 0xF1}, {'좌', 0xF2}, {'좍', 0xF3}, {'좔', 0xF4}, {'좝', 0xF5}, {'좟', 0xF6}, {'좡', 0xF7}, {'좨', 0xF8}, {'좼', 0xF9}, {'좽', 0xFA}, {'죄', 0xFB}, {'죈', 0xFC}, {'죌', 0xFD}, {'죔', 0xFE}, {'죕', 0xFF}, - }; - - private static readonly Dictionary U2GSC_KOR_8 = new() - { - {'죗', 0x00}, {'죙', 0x01}, {'죠', 0x02}, {'죡', 0x03}, {'죤', 0x04}, {'죵', 0x05}, {'주', 0x06}, {'죽', 0x07}, {'준', 0x08}, {'줄', 0x09}, {'줅', 0x0A}, {'줆', 0x0B}, {'줌', 0x0C}, {'줍', 0x0D}, {'줏', 0x0E}, {'중', 0x0F}, - {'줘', 0x10}, {'줬', 0x11}, {'줴', 0x12}, {'쥐', 0x13}, {'쥑', 0x14}, {'쥔', 0x15}, {'쥘', 0x16}, {'쥠', 0x17}, {'쥡', 0x18}, {'쥣', 0x19}, {'쥬', 0x1A}, {'쥰', 0x1B}, {'쥴', 0x1C}, {'쥼', 0x1D}, {'즈', 0x1E}, {'즉', 0x1F}, - {'즌', 0x20}, {'즐', 0x21}, {'즘', 0x22}, {'즙', 0x23}, {'즛', 0x24}, {'증', 0x25}, {'지', 0x26}, {'직', 0x27}, {'진', 0x28}, {'짇', 0x29}, {'질', 0x2A}, {'짊', 0x2B}, {'짐', 0x2C}, {'집', 0x2D}, {'짓', 0x2E}, - {'쬬', 0x30}, {'징', 0x31}, {'짖', 0x32}, {'짙', 0x33}, {'짚', 0x34}, {'짜', 0x35}, {'짝', 0x36}, {'짠', 0x37}, {'짢', 0x38}, {'짤', 0x39}, {'짧', 0x3A}, {'짬', 0x3B}, {'짭', 0x3C}, {'짯', 0x3D}, {'짰', 0x3E}, {'짱', 0x3F}, - {'째', 0x40}, {'짹', 0x41}, {'짼', 0x42}, {'쨀', 0x43}, {'쨈', 0x44}, {'쨉', 0x45}, {'쨋', 0x46}, {'쨌', 0x47}, {'쨍', 0x48}, {'쨔', 0x49}, {'쨘', 0x4A}, {'쨩', 0x4B}, {'쩌', 0x4C}, {'쩍', 0x4D}, {'쩐', 0x4E}, {'쩔', 0x4F}, - {'쩜', 0x60}, {'쩝', 0x61}, {'쩟', 0x62}, {'쩠', 0x63}, {'쩡', 0x64}, {'쩨', 0x65}, {'쩽', 0x66}, {'쪄', 0x67}, {'쪘', 0x68}, {'쪼', 0x69}, {'쪽', 0x6A}, {'쫀', 0x6B}, {'쫄', 0x6C}, {'쫌', 0x6D}, {'쫍', 0x6E}, {'쫏', 0x6F}, - {'쫑', 0x70}, {'쫓', 0x71}, {'쫘', 0x72}, {'쫙', 0x73}, {'쫠', 0x74}, {'쫬', 0x75}, {'쫴', 0x76}, {'쬈', 0x77}, {'쬐', 0x78}, {'쬔', 0x79}, {'쬘', 0x7A}, {'쬠', 0x7B}, {'쬡', 0x7C}, {'쭁', 0x7D}, {'쭈', 0x7E}, {'쭉', 0x7F}, - {'쭌', 0x80}, {'쭐', 0x81}, {'쭘', 0x82}, {'쭙', 0x83}, {'쭝', 0x84}, {'쭤', 0x85}, {'쭸', 0x86}, {'쭹', 0x87}, {'쮜', 0x88}, {'쮸', 0x89}, {'쯔', 0x8A}, {'쯤', 0x8B}, {'쯧', 0x8C}, {'쯩', 0x8D}, {'찌', 0x8E}, {'찍', 0x8F}, - {'찐', 0x90}, {'찔', 0x91}, {'찜', 0x92}, {'찝', 0x93}, {'찡', 0x94}, {'찢', 0x95}, {'찧', 0x96}, {'차', 0x97}, {'착', 0x98}, {'찬', 0x99}, {'찮', 0x9A}, {'찰', 0x9B}, {'참', 0x9C}, {'찹', 0x9D}, {'찻', 0x9E}, - {'찼', 0xA1}, {'창', 0xA2}, {'찾', 0xA3}, {'채', 0xA4}, {'책', 0xA5}, {'챈', 0xA6}, {'챌', 0xA7}, {'챔', 0xA8}, {'챕', 0xA9}, {'챗', 0xAA}, {'챘', 0xAB}, {'챙', 0xAC}, {'챠', 0xAD}, {'챤', 0xAE}, {'챦', 0xAF}, - {'챨', 0xB0}, {'챰', 0xB1}, {'챵', 0xB2}, {'처', 0xB3}, {'척', 0xB4}, {'천', 0xB5}, {'철', 0xB6}, {'첨', 0xB7}, {'첩', 0xB8}, {'첫', 0xB9}, {'첬', 0xBA}, {'청', 0xBB}, {'체', 0xBC}, {'첵', 0xBD}, {'첸', 0xBE}, {'첼', 0xBF}, - {'쳄', 0xC0}, {'쳅', 0xC1}, {'쳇', 0xC2}, {'쳉', 0xC3}, {'쳐', 0xC4}, {'쳔', 0xC5}, {'쳤', 0xC6}, {'쳬', 0xC7}, {'쳰', 0xC8}, {'촁', 0xC9}, {'초', 0xCA}, {'촉', 0xCB}, {'촌', 0xCC}, {'촐', 0xCD}, {'촘', 0xCE}, {'촙', 0xCF}, - {'촛', 0xD0}, {'총', 0xD1}, {'촤', 0xD2}, {'촨', 0xD3}, {'촬', 0xD4}, {'촹', 0xD5}, {'최', 0xD6}, {'쵠', 0xD7}, {'쵤', 0xD8}, {'쵬', 0xD9}, {'쵭', 0xDA}, {'쵯', 0xDB}, {'쵱', 0xDC}, {'쵸', 0xDD}, {'춈', 0xDE}, {'추', 0xDF}, - {'축', 0xE0}, {'춘', 0xE1}, {'출', 0xE2}, {'춤', 0xE3}, {'춥', 0xE4}, {'춧', 0xE5}, {'충', 0xE6}, {'춰', 0xE7}, {'췄', 0xE8}, {'췌', 0xE9}, {'췐', 0xEA}, {'취', 0xEB}, {'췬', 0xEC}, {'췰', 0xED}, {'췸', 0xEE}, {'췹', 0xEF}, - {'췻', 0xF0}, {'췽', 0xF1}, {'츄', 0xF2}, {'츈', 0xF3}, {'츌', 0xF4}, {'츔', 0xF5}, {'츙', 0xF6}, {'츠', 0xF7}, {'측', 0xF8}, {'츤', 0xF9}, {'츨', 0xFA}, {'츰', 0xFB}, {'츱', 0xFC}, {'츳', 0xFD}, {'층', 0xFE}, - }; - - private static readonly Dictionary U2GSC_KOR_9 = new() - { - {'치', 0x01}, {'칙', 0x02}, {'친', 0x03}, {'칟', 0x04}, {'칠', 0x05}, {'칡', 0x06}, {'침', 0x07}, {'칩', 0x08}, {'칫', 0x09}, {'칭', 0x0A}, {'카', 0x0B}, {'칵', 0x0C}, {'칸', 0x0D}, {'칼', 0x0E}, {'캄', 0x0F}, - {'캅', 0x10}, {'캇', 0x11}, {'캉', 0x12}, {'캐', 0x13}, {'캑', 0x14}, {'캔', 0x15}, {'캘', 0x16}, {'캠', 0x17}, {'캡', 0x18}, {'캣', 0x19}, {'캤', 0x1A}, {'캥', 0x1B}, {'캬', 0x1C}, {'캭', 0x1D}, {'컁', 0x1E}, {'커', 0x1F}, - {'컥', 0x20}, {'컨', 0x21}, {'컫', 0x22}, {'컬', 0x23}, {'컴', 0x24}, {'컵', 0x25}, {'컷', 0x26}, {'컸', 0x27}, {'컹', 0x28}, {'케', 0x29}, {'켁', 0x2A}, {'켄', 0x2B}, {'켈', 0x2C}, {'켐', 0x2D}, {'켑', 0x2E}, {'켓', 0x2F}, - {'켕', 0x30}, {'켜', 0x31}, {'켠', 0x32}, {'켤', 0x33}, {'켬', 0x34}, {'켭', 0x35}, {'켯', 0x36}, {'켰', 0x37}, {'켱', 0x38}, {'켸', 0x39}, {'코', 0x3A}, {'콕', 0x3B}, {'콘', 0x3C}, {'콜', 0x3D}, {'콤', 0x3E}, {'콥', 0x3F}, - {'콧', 0x40}, {'콩', 0x41}, {'콰', 0x42}, {'콱', 0x43}, {'콴', 0x44}, {'콸', 0x45}, {'쾀', 0x46}, {'쾅', 0x47}, {'쾌', 0x48}, {'쾡', 0x49}, {'쾨', 0x4A}, {'쾰', 0x4B}, {'쿄', 0x4C}, {'쿠', 0x4D}, {'쿡', 0x4E}, {'쿤', 0x4F}, - {'쿨', 0x60}, {'쿰', 0x61}, {'쿱', 0x62}, {'쿳', 0x63}, {'쿵', 0x64}, {'쿼', 0x65}, {'퀀', 0x66}, {'퀄', 0x67}, {'퀑', 0x68}, {'퀘', 0x69}, {'퀭', 0x6A}, {'퀴', 0x6B}, {'퀵', 0x6C}, {'퀸', 0x6D}, {'퀼', 0x6E}, - {'큄', 0x71}, {'큅', 0x72}, {'큇', 0x73}, {'큉', 0x74}, {'큐', 0x75}, {'큔', 0x76}, {'큘', 0x77}, {'큠', 0x78}, {'크', 0x79}, {'큭', 0x7A}, {'큰', 0x7B}, {'클', 0x7C}, {'큼', 0x7D}, {'큽', 0x7E}, {'킁', 0x7F}, - {'키', 0x80}, {'킥', 0x81}, {'킨', 0x82}, {'킬', 0x83}, {'킴', 0x84}, {'킵', 0x85}, {'킷', 0x86}, {'킹', 0x87}, {'타', 0x88}, {'탁', 0x89}, {'탄', 0x8A}, {'탈', 0x8B}, {'탉', 0x8C}, {'탐', 0x8D}, {'탑', 0x8E}, {'탓', 0x8F}, - {'탔', 0x90}, {'탕', 0x91}, {'태', 0x92}, {'택', 0x93}, {'탠', 0x94}, {'탤', 0x95}, {'탬', 0x96}, {'탭', 0x97}, {'탯', 0x98}, {'탰', 0x99}, {'탱', 0x9A}, {'탸', 0x9B}, {'턍', 0x9C}, {'터', 0x9D}, {'턱', 0x9E}, {'턴', 0x9F}, - {'털', 0xA0}, {'턺', 0xA1}, {'텀', 0xA2}, {'텁', 0xA3}, {'텃', 0xA4}, {'텄', 0xA5}, {'텅', 0xA6}, {'테', 0xA7}, {'텍', 0xA8}, {'텐', 0xA9}, {'텔', 0xAA}, {'템', 0xAB}, {'텝', 0xAC}, {'텟', 0xAD}, {'텡', 0xAE}, {'텨', 0xAF}, - {'텬', 0xB0}, {'텼', 0xB1}, {'톄', 0xB2}, {'톈', 0xB3}, {'토', 0xB4}, {'톡', 0xB5}, {'톤', 0xB6}, {'톨', 0xB7}, {'톰', 0xB8}, {'톱', 0xB9}, {'톳', 0xBA}, {'통', 0xBB}, {'톺', 0xBC}, {'톼', 0xBD}, {'퇀', 0xBE}, {'퇘', 0xBF}, - {'퇴', 0xC0}, {'퇸', 0xC1}, {'툇', 0xC2}, {'툉', 0xC3}, {'툐', 0xC4}, {'투', 0xC5}, {'툭', 0xC6}, {'툰', 0xC7}, {'툴', 0xC8}, {'툼', 0xC9}, {'툽', 0xCA}, {'툿', 0xCB}, {'퉁', 0xCC}, {'퉈', 0xCD}, {'퉜', 0xCE}, - {'퉤', 0xD1}, {'튀', 0xD2}, {'튁', 0xD3}, {'튄', 0xD4}, {'튈', 0xD5}, {'튐', 0xD6}, {'튑', 0xD7}, {'튕', 0xD8}, {'튜', 0xD9}, {'튠', 0xDA}, {'튤', 0xDB}, {'튬', 0xDC}, {'튱', 0xDD}, {'트', 0xDE}, {'특', 0xDF}, - {'튼', 0xE0}, {'튿', 0xE1}, {'틀', 0xE2}, {'틂', 0xE3}, {'틈', 0xE4}, {'틉', 0xE5}, {'틋', 0xE6}, {'틔', 0xE7}, {'틘', 0xE8}, {'틜', 0xE9}, {'틤', 0xEA}, {'틥', 0xEB}, {'티', 0xEC}, {'틱', 0xED}, {'틴', 0xEE}, {'틸', 0xEF}, - {'팀', 0xF0}, {'팁', 0xF1}, {'팃', 0xF2}, {'팅', 0xF3}, {'파', 0xF4}, {'팍', 0xF5}, {'팎', 0xF6}, {'판', 0xF7}, {'팔', 0xF8}, {'팖', 0xF9}, {'팜', 0xFA}, {'팝', 0xFB}, {'팟', 0xFC}, {'팠', 0xFD}, {'팡', 0xFE}, {'팥', 0xFF}, - }; - - private static readonly Dictionary U2GSC_KOR_A = new() - { - {'패', 0x00}, {'팩', 0x01}, {'팬', 0x02}, {'팰', 0x03}, {'팸', 0x04}, {'팹', 0x05}, {'팻', 0x06}, {'팼', 0x07}, {'팽', 0x08}, {'퍄', 0x09}, {'퍅', 0x0A}, {'퍼', 0x0B}, {'퍽', 0x0C}, {'펀', 0x0D}, {'펄', 0x0E}, {'펌', 0x0F}, - {'펍', 0x10}, {'펏', 0x11}, {'펐', 0x12}, {'펑', 0x13}, {'페', 0x14}, {'펙', 0x15}, {'펜', 0x16}, {'펠', 0x17}, {'펨', 0x18}, {'펩', 0x19}, {'펫', 0x1A}, {'펭', 0x1B}, {'펴', 0x1C}, {'편', 0x1D}, {'펼', 0x1E}, {'폄', 0x1F}, - {'폅', 0x20}, {'폈', 0x21}, {'평', 0x22}, {'폐', 0x23}, {'폘', 0x24}, {'폡', 0x25}, {'폣', 0x26}, {'포', 0x27}, {'폭', 0x28}, {'폰', 0x29}, {'폴', 0x2A}, {'폼', 0x2B}, {'폽', 0x2C}, {'폿', 0x2D}, {'퐁', 0x2E}, - {'퐈', 0x31}, {'퐝', 0x32}, {'푀', 0x33}, {'푄', 0x34}, {'표', 0x35}, {'푠', 0x36}, {'푤', 0x37}, {'푭', 0x38}, {'푯', 0x39}, {'푸', 0x3A}, {'푹', 0x3B}, {'푼', 0x3C}, {'푿', 0x3D}, {'풀', 0x3E}, {'풂', 0x3F}, - {'품', 0x40}, {'풉', 0x41}, {'풋', 0x42}, {'풍', 0x43}, {'풔', 0x44}, {'풩', 0x45}, {'퓌', 0x46}, {'퓐', 0x47}, {'퓔', 0x48}, {'퓜', 0x49}, {'퓟', 0x4A}, {'퓨', 0x4B}, {'퓬', 0x4C}, {'퓰', 0x4D}, {'퓸', 0x4E}, {'퓻', 0x4F}, - {'퓽', 0x60}, {'프', 0x61}, {'픈', 0x62}, {'플', 0x63}, {'픔', 0x64}, {'픕', 0x65}, {'픗', 0x66}, {'피', 0x67}, {'픽', 0x68}, {'핀', 0x69}, {'필', 0x6A}, {'핌', 0x6B}, {'핍', 0x6C}, {'핏', 0x6D}, {'핑', 0x6E}, {'하', 0x6F}, - {'학', 0x70}, {'한', 0x71}, {'할', 0x72}, {'핥', 0x73}, {'함', 0x74}, {'합', 0x75}, {'핫', 0x76}, {'항', 0x77}, {'해', 0x78}, {'핵', 0x79}, {'핸', 0x7A}, {'핼', 0x7B}, {'햄', 0x7C}, {'햅', 0x7D}, {'햇', 0x7E}, {'했', 0x7F}, - {'행', 0x80}, {'햐', 0x81}, {'향', 0x82}, {'허', 0x83}, {'헉', 0x84}, {'헌', 0x85}, {'헐', 0x86}, {'헒', 0x87}, {'험', 0x88}, {'헙', 0x89}, {'헛', 0x8A}, {'헝', 0x8B}, {'헤', 0x8C}, {'헥', 0x8D}, {'헨', 0x8E}, {'헬', 0x8F}, - {'헴', 0x90}, {'헵', 0x91}, {'헷', 0x92}, {'헹', 0x93}, {'혀', 0x94}, {'혁', 0x95}, {'현', 0x96}, {'혈', 0x97}, {'혐', 0x98}, {'협', 0x99}, {'혓', 0x9A}, {'혔', 0x9B}, {'형', 0x9C}, {'혜', 0x9D}, {'혠', 0x9E}, - {'혤', 0xA1}, {'혭', 0xA2}, {'호', 0xA3}, {'혹', 0xA4}, {'혼', 0xA5}, {'홀', 0xA6}, {'홅', 0xA7}, {'홈', 0xA8}, {'홉', 0xA9}, {'홋', 0xAA}, {'홍', 0xAB}, {'홑', 0xAC}, {'화', 0xAD}, {'확', 0xAE}, {'환', 0xAF}, - {'활', 0xB0}, {'홧', 0xB1}, {'황', 0xB2}, {'홰', 0xB3}, {'홱', 0xB4}, {'홴', 0xB5}, {'횃', 0xB6}, {'횅', 0xB7}, {'회', 0xB8}, {'획', 0xB9}, {'횐', 0xBA}, {'횔', 0xBB}, {'횝', 0xBC}, {'횟', 0xBD}, {'횡', 0xBE}, {'효', 0xBF}, - {'횬', 0xC0}, {'횰', 0xC1}, {'횹', 0xC2}, {'횻', 0xC3}, {'후', 0xC4}, {'훅', 0xC5}, {'훈', 0xC6}, {'훌', 0xC7}, {'훑', 0xC8}, {'훔', 0xC9}, {'훗', 0xCA}, {'훙', 0xCB}, {'훠', 0xCC}, {'훤', 0xCD}, {'훨', 0xCE}, {'훰', 0xCF}, - {'훵', 0xD0}, {'훼', 0xD1}, {'훽', 0xD2}, {'휀', 0xD3}, {'휄', 0xD4}, {'휑', 0xD5}, {'휘', 0xD6}, {'휙', 0xD7}, {'휜', 0xD8}, {'휠', 0xD9}, {'휨', 0xDA}, {'휩', 0xDB}, {'휫', 0xDC}, {'휭', 0xDD}, {'휴', 0xDE}, {'휵', 0xDF}, - {'휸', 0xE0}, {'휼', 0xE1}, {'흄', 0xE2}, {'흇', 0xE3}, {'흉', 0xE4}, {'흐', 0xE5}, {'흑', 0xE6}, {'흔', 0xE7}, {'흖', 0xE8}, {'흗', 0xE9}, {'흘', 0xEA}, {'흙', 0xEB}, {'흠', 0xEC}, {'흡', 0xED}, {'흣', 0xEE}, {'흥', 0xEF}, - {'흩', 0xF0}, {'희', 0xF1}, {'흰', 0xF2}, {'흴', 0xF3}, {'흼', 0xF4}, {'흽', 0xF5}, {'힁', 0xF6}, {'히', 0xF7}, {'힉', 0xF8}, {'힌', 0xF9}, {'힐', 0xFA}, {'힘', 0xFB}, {'힙', 0xFC}, {'힛', 0xFD}, {'힝', 0xFE}, - }; - - private static readonly Dictionary U2GSC_KOR_B = U2GSC_KOR_0; - - private static readonly Dictionary GSC2U_KOR_0 = new() - { - {0x00, 'ㄱ'}, {0x01, 'ㄴ'}, {0x02, 'ㄷ'}, {0x03, 'ㄹ'}, {0x04, 'ㅁ'}, {0x05, 'ㅂ'}, {0x06, 'ㅅ'}, {0x07, 'ㅇ'}, {0x08, 'ㅈ'}, {0x09, 'ㅊ'}, {0x0A, 'ㅋ'}, {0x0B, 'ㅌ'}, {0x0C, 'ㅍ'}, {0x0D, 'ㅎ'}, {0x0E, 'ㄲ'}, {0x0F, 'ㄸ'}, - {0x10, 'ㅃ'}, {0x11, 'ㅆ'}, {0x12, 'ㅉ'}, - {0x20, 'ㅏ'}, {0x21, 'ㅑ'}, {0x22, 'ㅓ'}, {0x23, 'ㅕ'}, {0x24, 'ㅗ'}, {0x25, 'ㅛ'}, {0x26, 'ㅜ'}, {0x27, 'ㅠ'}, {0x28, 'ㅡ'}, {0x29, 'ㅣ'}, {0x2A, 'ㅐ'}, {0x2B, 'ㅒ'}, {0x2C, 'ㅔ'}, {0x2D, 'ㅖ'}, {0x2E, 'ㅘ'}, {0x2F, 'ㅙ'}, - {0x30, 'ㅚ'}, {0x31, 'ㅝ'}, {0x32, 'ㅞ'}, {0x33, 'ㅟ'}, {0x34, 'ㅢ'}, {0x3E, '_'}, {0x3F, '—'}, - {0x60, '「'}, {0x61, '」'}, {0x62, '『'}, {0x63, '』'}, {0x64, '('}, {0x65, ')'}, {0x66, '!'}, {0x67, '?'}, {0x68, '-'}, {0x69, '~'}, {0x6A, '…'}, {0x6B, ','}, {0x6C, '.'}, // . is 0x6D for bank - {0xF0, '0'}, {0xF1, '1'}, {0xF2, '2'}, {0xF3, '3'}, {0xF4, '4'}, {0xF5, '5'}, {0xF6, '6'}, {0xF7, '7'}, {0xF8, '8'}, {0xF9, '9'}, - {0xFF, '\u3000'}, - }; - - private static readonly Dictionary GSC2U_KOR_1 = new() - { - {0x01, '가'}, {0x02, '각'}, {0x03, '간'}, {0x04, '갇'}, {0x05, '갈'}, {0x06, '갉'}, {0x07, '갊'}, {0x08, '감'}, {0x09, '갑'}, {0x0A, '값'}, {0x0B, '갓'}, {0x0C, '갔'}, {0x0D, '강'}, {0x0E, '갖'}, {0x0F, '갗'}, - {0x10, '같'}, {0x11, '갚'}, {0x12, '갛'}, {0x13, '개'}, {0x14, '객'}, {0x15, '갠'}, {0x16, '갤'}, {0x17, '갬'}, {0x18, '갭'}, {0x19, '갯'}, {0x1A, '갰'}, {0x1B, '갱'}, {0x1C, '갸'}, {0x1D, '갹'}, {0x1E, '갼'}, {0x1F, '걀'}, - {0x20, '걋'}, {0x21, '걍'}, {0x22, '걔'}, {0x23, '걘'}, {0x24, '걜'}, {0x25, '거'}, {0x26, '걱'}, {0x27, '건'}, {0x28, '걷'}, {0x29, '걸'}, {0x2A, '걺'}, {0x2B, '검'}, {0x2C, '겁'}, {0x2D, '것'}, {0x2E, '겄'}, {0x2F, '겅'}, - {0x30, '겆'}, {0x31, '겉'}, {0x32, '겊'}, {0x33, '겋'}, {0x34, '게'}, {0x35, '겐'}, {0x36, '겔'}, {0x37, '겜'}, {0x38, '겝'}, {0x39, '겟'}, {0x3A, '겠'}, {0x3B, '겡'}, {0x3C, '겨'}, {0x3D, '격'}, {0x3E, '겪'}, {0x3F, '견'}, - {0x40, '겯'}, {0x41, '결'}, {0x42, '겹'}, {0x43, '겸'}, {0x44, '겻'}, {0x45, '겼'}, {0x46, '경'}, {0x47, '곁'}, {0x48, '계'}, {0x49, '곈'}, {0x4A, '곌'}, {0x4B, '곕'}, {0x4C, '곗'}, {0x4D, '고'}, {0x4E, '곡'}, {0x4F, '곤'}, - {0x60, '곧'}, {0x61, '골'}, {0x62, '곪'}, {0x63, '곬'}, {0x64, '곯'}, {0x65, '곰'}, {0x66, '곱'}, {0x67, '곳'}, {0x68, '공'}, {0x69, '곶'}, {0x6A, '과'}, {0x6B, '곽'}, {0x6C, '관'}, {0x6D, '괄'}, {0x6E, '괆'}, - {0x71, '괌'}, {0x72, '괍'}, {0x73, '괏'}, {0x74, '광'}, {0x75, '괘'}, {0x76, '괜'}, {0x77, '괠'}, {0x78, '괩'}, {0x79, '괬'}, {0x7A, '괭'}, {0x7B, '괴'}, {0x7C, '괵'}, {0x7D, '괸'}, {0x7E, '괼'}, {0x7F, '괻'}, - {0x80, '굅'}, {0x81, '굇'}, {0x82, '굉'}, {0x83, '교'}, {0x84, '굔'}, {0x85, '굘'}, {0x86, '굡'}, {0x87, '굣'}, {0x88, '구'}, {0x89, '국'}, {0x8A, '군'}, {0x8B, '굳'}, {0x8C, '굴'}, {0x8D, '굵'}, {0x8E, '굶'}, {0x8F, '굻'}, - {0x90, '굼'}, {0x91, '굽'}, {0x92, '굿'}, {0x93, '궁'}, {0x94, '궂'}, {0x95, '궈'}, {0x96, '궉'}, {0x97, '권'}, {0x98, '궐'}, {0x99, '궜'}, {0x9A, '궝'}, {0x9B, '궤'}, {0x9C, '궷'}, {0x9D, '귀'}, {0x9E, '귁'}, {0x9F, '귄'}, - {0xA0, '귈'}, {0xA1, '귐'}, {0xA2, '귑'}, {0xA3, '귓'}, {0xA4, '규'}, {0xA5, '균'}, {0xA6, '귤'}, {0xA7, '그'}, {0xA8, '극'}, {0xA9, '근'}, {0xAA, '귿'}, {0xAB, '글'}, {0xAC, '긁'}, {0xAD, '금'}, {0xAE, '급'}, {0xAF, '긋'}, - {0xB0, '긍'}, {0xB1, '긔'}, {0xB2, '기'}, {0xB3, '긱'}, {0xB4, '긴'}, {0xB5, '긷'}, {0xB6, '길'}, {0xB7, '긺'}, {0xB8, '김'}, {0xB9, '깁'}, {0xBA, '깃'}, {0xBB, '깅'}, {0xBC, '깆'}, {0xBD, '깊'}, {0xBE, '까'}, {0xBF, '깍'}, - {0xC0, '깎'}, {0xC1, '깐'}, {0xC2, '깔'}, {0xC3, '깖'}, {0xC4, '깜'}, {0xC5, '깝'}, {0xC6, '깟'}, {0xC7, '깠'}, {0xC8, '깡'}, {0xC9, '깥'}, {0xCA, '깨'}, {0xCB, '깩'}, {0xCC, '깬'}, {0xCD, '깰'}, {0xCE, '깸'}, - {0xD1, '깹'}, {0xD2, '깻'}, {0xD3, '깼'}, {0xD4, '깽'}, {0xD5, '꺄'}, {0xD6, '꺅'}, {0xD7, '꺌'}, {0xD8, '꺼'}, {0xD9, '꺽'}, {0xDA, '꺾'}, {0xDB, '껀'}, {0xDC, '껄'}, {0xDD, '껌'}, {0xDE, '껍'}, {0xDF, '껏'}, - {0xE0, '껐'}, {0xE1, '껑'}, {0xE2, '께'}, {0xE3, '껙'}, {0xE4, '껜'}, {0xE5, '껨'}, {0xE6, '껫'}, {0xE7, '껭'}, {0xE8, '껴'}, {0xE9, '껸'}, {0xEA, '껼'}, {0xEB, '꼇'}, {0xEC, '꼈'}, {0xED, '꼍'}, {0xEE, '꼐'}, {0xEF, '꼬'}, - {0xF0, '꼭'}, {0xF1, '꼰'}, {0xF2, '꼲'}, {0xF3, '꼴'}, {0xF4, '꼼'}, {0xF5, '꼽'}, {0xF6, '꼿'}, {0xF7, '꽁'}, {0xF8, '꽂'}, {0xF9, '꽃'}, {0xFA, '꽈'}, {0xFB, '꽉'}, {0xFC, '꽐'}, {0xFD, '꽜'}, {0xFE, '꽝'}, {0xFF, '꽤'}, - }; - - private static readonly Dictionary GSC2U_KOR_2 = new() - { - {0x00, '꽥'}, {0x01, '꽹'}, {0x02, '꾀'}, {0x03, '꾄'}, {0x04, '꾈'}, {0x05, '꾐'}, {0x06, '꾑'}, {0x07, '꾕'}, {0x08, '꾜'}, {0x09, '꾸'}, {0x0A, '꾹'}, {0x0B, '꾼'}, {0x0C, '꿀'}, {0x0D, '꿇'}, {0x0E, '꿈'}, {0x0F, '꿉'}, - {0x10, '꿋'}, {0x11, '꿍'}, {0x12, '꿎'}, {0x13, '꿔'}, {0x14, '꿜'}, {0x15, '꿨'}, {0x16, '꿩'}, {0x17, '꿰'}, {0x18, '꿱'}, {0x19, '꿴'}, {0x1A, '꿸'}, {0x1B, '뀀'}, {0x1C, '뀁'}, {0x1D, '뀄'}, {0x1E, '뀌'}, {0x1F, '뀐'}, - {0x20, '뀔'}, {0x21, '뀜'}, {0x22, '뀝'}, {0x23, '뀨'}, {0x24, '끄'}, {0x25, '끅'}, {0x26, '끈'}, {0x27, '끊'}, {0x28, '끌'}, {0x29, '끎'}, {0x2A, '끓'}, {0x2B, '끔'}, {0x2C, '끕'}, {0x2D, '끗'}, {0x2E, '끙'}, - {0x31, '끝'}, {0x32, '끼'}, {0x33, '끽'}, {0x34, '낀'}, {0x35, '낄'}, {0x36, '낌'}, {0x37, '낍'}, {0x38, '낏'}, {0x39, '낑'}, {0x3A, '나'}, {0x3B, '낙'}, {0x3C, '낚'}, {0x3D, '난'}, {0x3E, '낟'}, {0x3F, '날'}, - {0x40, '낡'}, {0x41, '낢'}, {0x42, '남'}, {0x43, '납'}, {0x44, '낫'}, {0x45, '났'}, {0x46, '낭'}, {0x47, '낮'}, {0x48, '낯'}, {0x49, '낱'}, {0x4A, '낳'}, {0x4B, '내'}, {0x4C, '낵'}, {0x4D, '낸'}, {0x4E, '낼'}, {0x4F, '냄'}, - {0x60, '냅'}, {0x61, '냇'}, {0x62, '냈'}, {0x63, '냉'}, {0x64, '냐'}, {0x65, '냑'}, {0x66, '냔'}, {0x67, '냘'}, {0x68, '냠'}, {0x69, '냥'}, {0x6A, '너'}, {0x6B, '넉'}, {0x6C, '넋'}, {0x6D, '넌'}, {0x6E, '널'}, {0x6F, '넒'}, - {0x70, '넓'}, {0x71, '넘'}, {0x72, '넙'}, {0x73, '넛'}, {0x74, '넜'}, {0x75, '넝'}, {0x76, '넣'}, {0x77, '네'}, {0x78, '넥'}, {0x79, '넨'}, {0x7A, '넬'}, {0x7B, '넴'}, {0x7C, '넵'}, {0x7D, '넷'}, {0x7E, '넸'}, {0x7F, '넹'}, - {0x80, '녀'}, {0x81, '녁'}, {0x82, '년'}, {0x83, '녈'}, {0x84, '념'}, {0x85, '녑'}, {0x86, '녔'}, {0x87, '녕'}, {0x88, '녘'}, {0x89, '녜'}, {0x8A, '녠'}, {0x8B, '노'}, {0x8C, '녹'}, {0x8D, '논'}, {0x8E, '놀'}, {0x8F, '놂'}, - {0x90, '놈'}, {0x91, '놉'}, {0x92, '놋'}, {0x93, '농'}, {0x94, '높'}, {0x95, '놓'}, {0x96, '놔'}, {0x97, '놘'}, {0x98, '놜'}, {0x99, '놨'}, {0x9A, '뇌'}, {0x9B, '뇐'}, {0x9C, '뇔'}, {0x9D, '뇜'}, {0x9E, '뇝'}, - {0xA1, '뇟'}, {0xA2, '뇨'}, {0xA3, '뇩'}, {0xA4, '뇬'}, {0xA5, '뇰'}, {0xA6, '뇹'}, {0xA7, '뇻'}, {0xA8, '뇽'}, {0xA9, '누'}, {0xAA, '눅'}, {0xAB, '눈'}, {0xAC, '눋'}, {0xAD, '눌'}, {0xAE, '눔'}, {0xAF, '눕'}, - {0xB0, '눗'}, {0xB1, '눙'}, {0xB2, '눠'}, {0xB3, '눴'}, {0xB4, '눼'}, {0xB5, '뉘'}, {0xB6, '뉜'}, {0xB7, '뉠'}, {0xB8, '뉨'}, {0xB9, '뉩'}, {0xBA, '뉴'}, {0xBB, '뉵'}, {0xBC, '뉼'}, {0xBD, '늄'}, {0xBE, '늅'}, {0xBF, '늉'}, - {0xC0, '느'}, {0xC1, '늑'}, {0xC2, '는'}, {0xC3, '늘'}, {0xC4, '늙'}, {0xC5, '늚'}, {0xC6, '늠'}, {0xC7, '늡'}, {0xC8, '늣'}, {0xC9, '능'}, {0xCA, '늦'}, {0xCB, '늪'}, {0xCC, '늬'}, {0xCD, '늰'}, {0xCE, '늴'}, {0xCF, '니'}, - {0xD0, '닉'}, {0xD1, '닌'}, {0xD2, '닐'}, {0xD3, '닒'}, {0xD4, '님'}, {0xD5, '닙'}, {0xD6, '닛'}, {0xD7, '닝'}, {0xD8, '닢'}, {0xD9, '다'}, {0xDA, '닥'}, {0xDB, '닦'}, {0xDC, '단'}, {0xDD, '닫'}, {0xDE, '달'}, {0xDF, '닭'}, - {0xE0, '닮'}, {0xE1, '닯'}, {0xE2, '닳'}, {0xE3, '담'}, {0xE4, '답'}, {0xE5, '닷'}, {0xE6, '닸'}, {0xE7, '당'}, {0xE8, '닺'}, {0xE9, '닻'}, {0xEA, '닿'}, {0xEB, '대'}, {0xEC, '댁'}, {0xED, '댄'}, {0xEE, '댈'}, {0xEF, '댐'}, - {0xF0, '댑'}, {0xF1, '댓'}, {0xF2, '댔'}, {0xF3, '댕'}, {0xF5, '더'}, {0xF6, '덕'}, {0xF7, '덖'}, {0xF8, '던'}, {0xF9, '덛'}, {0xFA, '덜'}, {0xFB, '덞'}, {0xFC, '덟'}, {0xFD, '덤'}, {0xFE, '덥'}, - }; - - private static readonly Dictionary GSC2U_KOR_3 = new() - { - {0x01, '덧'}, {0x02, '덩'}, {0x03, '덫'}, {0x04, '덮'}, {0x05, '데'}, {0x06, '덱'}, {0x07, '덴'}, {0x08, '델'}, {0x09, '뎀'}, {0x0A, '뎁'}, {0x0B, '뎃'}, {0x0C, '뎄'}, {0x0D, '뎅'}, {0x0E, '뎌'}, {0x0F, '뎐'}, - {0x10, '뎔'}, {0x11, '뎠'}, {0x12, '뎡'}, {0x13, '뎨'}, {0x14, '뎬'}, {0x15, '도'}, {0x16, '독'}, {0x17, '돈'}, {0x18, '돋'}, {0x19, '돌'}, {0x1A, '돎'}, {0x1C, '돔'}, {0x1D, '돕'}, {0x1E, '돗'}, {0x1F, '동'}, - {0x20, '돛'}, {0x21, '돝'}, {0x22, '돠'}, {0x23, '돤'}, {0x24, '돨'}, {0x25, '돼'}, {0x26, '됐'}, {0x27, '되'}, {0x28, '된'}, {0x29, '될'}, {0x2A, '됨'}, {0x2B, '됩'}, {0x2C, '됫'}, {0x2D, '됴'}, {0x2E, '두'}, {0x2F, '둑'}, - {0x30, '둔'}, {0x31, '둘'}, {0x32, '둠'}, {0x33, '둡'}, {0x34, '둣'}, {0x35, '둥'}, {0x36, '둬'}, {0x37, '뒀'}, {0x38, '뒈'}, {0x39, '뒝'}, {0x3A, '뒤'}, {0x3B, '뒨'}, {0x3C, '뒬'}, {0x3D, '뒵'}, {0x3E, '뒷'}, {0x3F, '뒹'}, - {0x40, '듀'}, {0x41, '듄'}, {0x42, '듈'}, {0x43, '듐'}, {0x44, '듕'}, {0x45, '드'}, {0x46, '득'}, {0x47, '든'}, {0x48, '듣'}, {0x49, '들'}, {0x4A, '듦'}, {0x4B, '듬'}, {0x4C, '듭'}, {0x4D, '듯'}, {0x4E, '등'}, {0x4F, '듸'}, - {0x60, '디'}, {0x61, '딕'}, {0x62, '딘'}, {0x63, '딛'}, {0x64, '딜'}, {0x65, '딤'}, {0x66, '딥'}, {0x67, '딧'}, {0x68, '딨'}, {0x69, '딩'}, {0x6A, '딪'}, {0x6B, '따'}, {0x6C, '딱'}, {0x6D, '딴'}, {0x6E, '딸'}, - {0x71, '땀'}, {0x72, '땁'}, {0x73, '땃'}, {0x74, '땄'}, {0x75, '땅'}, {0x76, '땋'}, {0x77, '때'}, {0x78, '땍'}, {0x79, '땐'}, {0x7A, '땔'}, {0x7B, '땜'}, {0x7C, '땝'}, {0x7D, '땟'}, {0x7E, '땠'}, {0x7F, '땡'}, - {0x80, '떠'}, {0x81, '떡'}, {0x82, '떤'}, {0x83, '떨'}, {0x84, '떪'}, {0x85, '떫'}, {0x86, '떰'}, {0x87, '떱'}, {0x88, '떳'}, {0x89, '떴'}, {0x8A, '떵'}, {0x8B, '떻'}, {0x8C, '떼'}, {0x8D, '떽'}, {0x8E, '뗀'}, {0x8F, '뗄'}, - {0x90, '뗌'}, {0x91, '뗍'}, {0x92, '뗏'}, {0x93, '뗐'}, {0x94, '뗑'}, {0x95, '뗘'}, {0x96, '뗬'}, {0x97, '또'}, {0x98, '똑'}, {0x99, '똔'}, {0x9A, '똘'}, {0x9B, '똥'}, {0x9C, '똬'}, {0x9D, '똴'}, {0x9E, '뙈'}, {0x9F, '뙤'}, - {0xA0, '뙨'}, {0xA1, '뚜'}, {0xA2, '뚝'}, {0xA3, '뚠'}, {0xA4, '뚤'}, {0xA5, '뚫'}, {0xA6, '뚬'}, {0xA7, '뚱'}, {0xA8, '뛔'}, {0xA9, '뛰'}, {0xAA, '뛴'}, {0xAB, '뛸'}, {0xAC, '뜀'}, {0xAD, '뜁'}, {0xAE, '뜅'}, {0xAF, '뜨'}, - {0xB0, '뜩'}, {0xB1, '뜬'}, {0xB2, '뜯'}, {0xB3, '뜰'}, {0xB4, '뜸'}, {0xB5, '뜹'}, {0xB6, '뜻'}, {0xB7, '띄'}, {0xB8, '띈'}, {0xB9, '띌'}, {0xBA, '띔'}, {0xBB, '띕'}, {0xBC, '띠'}, {0xBD, '띤'}, {0xBE, '띨'}, {0xBF, '띰'}, - {0xC0, '띱'}, {0xC1, '띳'}, {0xC2, '띵'}, {0xC3, '라'}, {0xC4, '락'}, {0xC5, '란'}, {0xC6, '랄'}, {0xC7, '람'}, {0xC8, '랍'}, {0xC9, '랏'}, {0xCA, '랐'}, {0xCB, '랑'}, {0xCC, '랒'}, {0xCD, '랖'}, {0xCE, '랗'}, - {0xD0, '뢔'}, {0xD1, '래'}, {0xD2, '랙'}, {0xD3, '랜'}, {0xD4, '랠'}, {0xD5, '램'}, {0xD6, '랩'}, {0xD7, '랫'}, {0xD8, '랬'}, {0xD9, '랭'}, {0xDA, '랴'}, {0xDB, '략'}, {0xDC, '랸'}, {0xDD, '럇'}, {0xDE, '량'}, {0xDF, '러'}, - {0xE0, '럭'}, {0xE1, '런'}, {0xE2, '럴'}, {0xE3, '럼'}, {0xE4, '럽'}, {0xE5, '럿'}, {0xE6, '렀'}, {0xE7, '렁'}, {0xE8, '렇'}, {0xE9, '레'}, {0xEA, '렉'}, {0xEB, '렌'}, {0xEC, '렐'}, {0xED, '렘'}, {0xEE, '렙'}, {0xEF, '렛'}, - {0xF0, '렝'}, {0xF1, '려'}, {0xF2, '력'}, {0xF3, '련'}, {0xF4, '렬'}, {0xF5, '렴'}, {0xF6, '렵'}, {0xF7, '렷'}, {0xF8, '렸'}, {0xF9, '령'}, {0xFA, '례'}, {0xFB, '롄'}, {0xFC, '롑'}, {0xFD, '롓'}, {0xFE, '로'}, {0xFF, '록'}, - }; - - private static readonly Dictionary GSC2U_KOR_4 = new() - { - {0x00, '론'}, {0x01, '롤'}, {0x02, '롬'}, {0x03, '롭'}, {0x04, '롯'}, {0x05, '롱'}, {0x06, '롸'}, {0x07, '롼'}, {0x08, '뢍'}, {0x09, '뢨'}, {0x0A, '뢰'}, {0x0B, '뢴'}, {0x0C, '뢸'}, {0x0D, '룀'}, {0x0E, '룁'}, {0x0F, '룃'}, - {0x10, '룅'}, {0x11, '료'}, {0x12, '룐'}, {0x13, '룔'}, {0x14, '룝'}, {0x15, '룟'}, {0x16, '룡'}, {0x17, '루'}, {0x18, '룩'}, {0x19, '룬'}, {0x1A, '룰'}, {0x1B, '룸'}, {0x1C, '룹'}, {0x1D, '룻'}, {0x1E, '룽'}, {0x1F, '뤄'}, - {0x20, '뤘'}, {0x21, '뤠'}, {0x22, '뤼'}, {0x23, '뤽'}, {0x24, '륀'}, {0x25, '륄'}, {0x26, '륌'}, {0x27, '륏'}, {0x28, '륑'}, {0x29, '류'}, {0x2A, '륙'}, {0x2B, '륜'}, {0x2C, '률'}, {0x2D, '륨'}, {0x2E, '륩'}, - {0x31, '륫'}, {0x32, '륭'}, {0x33, '르'}, {0x34, '륵'}, {0x35, '른'}, {0x36, '를'}, {0x37, '름'}, {0x38, '릅'}, {0x39, '릇'}, {0x3A, '릉'}, {0x3B, '릊'}, {0x3C, '릍'}, {0x3D, '릎'}, {0x3E, '리'}, {0x3F, '릭'}, - {0x40, '린'}, {0x41, '릴'}, {0x42, '림'}, {0x43, '립'}, {0x44, '릿'}, {0x45, '링'}, {0x46, '마'}, {0x47, '막'}, {0x48, '만'}, {0x49, '많'}, {0x4A, '맏'}, {0x4B, '말'}, {0x4C, '맑'}, {0x4D, '맒'}, {0x4E, '맘'}, {0x4F, '맙'}, - {0x60, '맛'}, {0x61, '망'}, {0x62, '맞'}, {0x63, '맡'}, {0x64, '맣'}, {0x65, '매'}, {0x66, '맥'}, {0x67, '맨'}, {0x68, '맬'}, {0x69, '맴'}, {0x6A, '맵'}, {0x6B, '맷'}, {0x6C, '맸'}, {0x6D, '맹'}, {0x6E, '맺'}, {0x6F, '먀'}, - {0x70, '먁'}, {0x71, '먈'}, {0x72, '먕'}, {0x73, '머'}, {0x74, '먹'}, {0x75, '먼'}, {0x76, '멀'}, {0x77, '멂'}, {0x78, '멈'}, {0x79, '멉'}, {0x7A, '멋'}, {0x7B, '멍'}, {0x7C, '멎'}, {0x7D, '멓'}, {0x7E, '메'}, {0x7F, '멕'}, - {0x80, '멘'}, {0x81, '멜'}, {0x82, '멤'}, {0x83, '멥'}, {0x84, '멧'}, {0x85, '멨'}, {0x86, '멩'}, {0x87, '며'}, {0x88, '멱'}, {0x89, '면'}, {0x8A, '멸'}, {0x8B, '몃'}, {0x8C, '몄'}, {0x8D, '명'}, {0x8E, '몇'}, {0x8F, '몌'}, - {0x90, '모'}, {0x91, '목'}, {0x92, '몫'}, {0x93, '몬'}, {0x94, '몰'}, {0x95, '몲'}, {0x96, '몸'}, {0x97, '몹'}, {0x98, '못'}, {0x99, '몽'}, {0x9A, '뫄'}, {0x9B, '뫈'}, {0x9C, '뫘'}, {0x9D, '뫙'}, {0x9E, '뫼'}, - {0xA1, '묀'}, {0xA2, '묄'}, {0xA3, '묍'}, {0xA4, '묏'}, {0xA5, '묑'}, {0xA6, '묘'}, {0xA7, '묜'}, {0xA8, '묠'}, {0xA9, '묩'}, {0xAA, '묫'}, {0xAB, '무'}, {0xAC, '묵'}, {0xAD, '묶'}, {0xAE, '문'}, {0xAF, '묻'}, - {0xB0, '물'}, {0xB1, '묽'}, {0xB2, '묾'}, {0xB3, '뭄'}, {0xB4, '뭅'}, {0xB5, '뭇'}, {0xB6, '뭉'}, {0xB7, '뭍'}, {0xB8, '뭏'}, {0xB9, '뭐'}, {0xBA, '뭔'}, {0xBB, '뭘'}, {0xBC, '뭡'}, {0xBD, '뭣'}, {0xBE, '뭬'}, {0xBF, '뮈'}, - {0xC0, '뮌'}, {0xC1, '뮐'}, {0xC2, '뮤'}, {0xC3, '뮨'}, {0xC4, '뮬'}, {0xC5, '뮴'}, {0xC6, '뮷'}, {0xC7, '므'}, {0xC8, '믄'}, {0xC9, '믈'}, {0xCA, '믐'}, {0xCB, '믓'}, {0xCC, '미'}, {0xCD, '믹'}, {0xCE, '민'}, {0xCF, '믿'}, - {0xD0, '밀'}, {0xD1, '밂'}, {0xD2, '밈'}, {0xD3, '밉'}, {0xD4, '밋'}, {0xD5, '밌'}, {0xD6, '밍'}, {0xD7, '및'}, {0xD8, '밑'}, {0xD9, '바'}, {0xDA, '박'}, {0xDB, '밖'}, {0xDC, '밗'}, {0xDD, '반'}, {0xDE, '받'}, {0xDF, '발'}, - {0xE0, '밝'}, {0xE1, '밞'}, {0xE2, '밟'}, {0xE3, '밤'}, {0xE4, '밥'}, {0xE5, '밧'}, {0xE6, '방'}, {0xE7, '밭'}, {0xE8, '배'}, {0xE9, '백'}, {0xEA, '밴'}, {0xEB, '밸'}, {0xEC, '뱀'}, {0xED, '뱁'}, {0xEE, '뱃'}, {0xEF, '뱄'}, - {0xF0, '뱅'}, {0xF1, '뱉'}, {0xF2, '뱌'}, {0xF3, '뱍'}, {0xF4, '뱐'}, {0xF5, '뱝'}, {0xF6, '버'}, {0xF7, '벅'}, {0xF8, '번'}, {0xF9, '벋'}, {0xFA, '벌'}, {0xFB, '벎'}, {0xFC, '범'}, {0xFD, '법'}, {0xFE, '벗'}, - }; - - private static readonly Dictionary GSC2U_KOR_5 = new() - { - {0x01, '벙'}, {0x02, '벚'}, {0x03, '베'}, {0x04, '벡'}, {0x05, '벤'}, {0x06, '벧'}, {0x07, '벨'}, {0x08, '벰'}, {0x09, '벱'}, {0x0A, '벳'}, {0x0B, '벴'}, {0x0C, '벵'}, {0x0D, '벼'}, {0x0E, '벽'}, {0x0F, '변'}, - {0x10, '별'}, {0x11, '볍'}, {0x12, '볏'}, {0x13, '볐'}, {0x14, '병'}, {0x15, '볕'}, {0x16, '볘'}, {0x17, '볜'}, {0x18, '보'}, {0x19, '복'}, {0x1A, '볶'}, {0x1B, '본'}, {0x1C, '볼'}, {0x1D, '봄'}, {0x1E, '봅'}, {0x1F, '봇'}, - {0x20, '봉'}, {0x21, '봐'}, {0x22, '봔'}, {0x23, '봤'}, {0x24, '봬'}, {0x25, '뵀'}, {0x26, '뵈'}, {0x27, '뵉'}, {0x28, '뵌'}, {0x29, '뵐'}, {0x2A, '뵘'}, {0x2B, '뵙'}, {0x2C, '뵤'}, {0x2D, '뵨'}, {0x2E, '부'}, {0x2F, '북'}, - {0x30, '분'}, {0x31, '붇'}, {0x32, '불'}, {0x33, '붉'}, {0x34, '붊'}, {0x35, '붐'}, {0x36, '붑'}, {0x37, '붓'}, {0x38, '붕'}, {0x39, '붙'}, {0x3A, '붚'}, {0x3B, '붜'}, {0x3C, '붤'}, {0x3D, '붰'}, {0x3E, '붸'}, {0x3F, '뷔'}, - {0x40, '뷕'}, {0x41, '뷘'}, {0x42, '뷜'}, {0x43, '뷩'}, {0x44, '뷰'}, {0x45, '뷴'}, {0x46, '뷸'}, {0x47, '븀'}, {0x48, '븃'}, {0x49, '븅'}, {0x4A, '브'}, {0x4B, '븍'}, {0x4C, '븐'}, {0x4D, '블'}, {0x4E, '븜'}, {0x4F, '븝'}, - {0x60, '븟'}, {0x61, '비'}, {0x62, '빅'}, {0x63, '빈'}, {0x64, '빌'}, {0x65, '빎'}, {0x66, '빔'}, {0x67, '빕'}, {0x68, '빗'}, {0x69, '빙'}, {0x6A, '빚'}, {0x6B, '빛'}, {0x6C, '빠'}, {0x6D, '빡'}, {0x6E, '빤'}, - {0x71, '빨'}, {0x72, '빪'}, {0x73, '빰'}, {0x74, '빱'}, {0x75, '빳'}, {0x76, '빴'}, {0x77, '빵'}, {0x78, '빻'}, {0x79, '빼'}, {0x7A, '빽'}, {0x7B, '뺀'}, {0x7C, '뺄'}, {0x7D, '뺌'}, {0x7E, '뺍'}, {0x7F, '뺏'}, - {0x80, '뺐'}, {0x81, '뺑'}, {0x82, '뺘'}, {0x83, '뺙'}, {0x84, '뺨'}, {0x85, '뻐'}, {0x86, '뻑'}, {0x87, '뻔'}, {0x88, '뻗'}, {0x89, '뻘'}, {0x8A, '뻠'}, {0x8B, '뻣'}, {0x8C, '뻤'}, {0x8D, '뻥'}, {0x8E, '뻬'}, {0x8F, '뼁'}, - {0x90, '뼈'}, {0x91, '뼉'}, {0x92, '뼘'}, {0x93, '뼙'}, {0x94, '뼛'}, {0x95, '뼜'}, {0x96, '뼝'}, {0x97, '뽀'}, {0x98, '뽁'}, {0x99, '뽄'}, {0x9A, '뽈'}, {0x9B, '뽐'}, {0x9C, '뽑'}, {0x9D, '뽕'}, {0x9E, '뾔'}, {0x9F, '뾰'}, - {0xA0, '뿅'}, {0xA1, '뿌'}, {0xA2, '뿍'}, {0xA3, '뿐'}, {0xA4, '뿔'}, {0xA5, '뿜'}, {0xA6, '뿟'}, {0xA7, '뿡'}, {0xA8, '쀼'}, {0xA9, '쁑'}, {0xAA, '쁘'}, {0xAB, '쁜'}, {0xAC, '쁠'}, {0xAD, '쁨'}, {0xAE, '쁩'}, {0xAF, '삐'}, - {0xB0, '삑'}, {0xB1, '삔'}, {0xB2, '삘'}, {0xB3, '삠'}, {0xB4, '삡'}, {0xB5, '삣'}, {0xB6, '삥'}, {0xB7, '사'}, {0xB8, '삭'}, {0xB9, '삯'}, {0xBA, '산'}, {0xBB, '삳'}, {0xBC, '살'}, {0xBD, '삵'}, {0xBE, '삶'}, {0xBF, '삼'}, - {0xC0, '삽'}, {0xC1, '삿'}, {0xC2, '샀'}, {0xC3, '상'}, {0xC4, '샅'}, {0xC5, '새'}, {0xC6, '색'}, {0xC7, '샌'}, {0xC8, '샐'}, {0xC9, '샘'}, {0xCA, '샙'}, {0xCB, '샛'}, {0xCC, '샜'}, {0xCD, '생'}, {0xCE, '샤'}, - {0xD1, '샥'}, {0xD2, '샨'}, {0xD3, '샬'}, {0xD4, '샴'}, {0xD5, '샵'}, {0xD6, '샷'}, {0xD7, '샹'}, {0xD8, '섀'}, {0xD9, '섄'}, {0xDA, '섈'}, {0xDB, '섐'}, {0xDC, '섕'}, {0xDD, '서'}, {0xDE, '석'}, {0xDF, '섞'}, - {0xE0, '섟'}, {0xE1, '선'}, {0xE2, '섣'}, {0xE3, '설'}, {0xE4, '섦'}, {0xE5, '섧'}, {0xE6, '섬'}, {0xE7, '섭'}, {0xE8, '섯'}, {0xE9, '섰'}, {0xEA, '성'}, {0xEB, '섶'}, {0xEC, '세'}, {0xED, '섹'}, {0xEE, '센'}, {0xEF, '셀'}, - {0xF0, '셈'}, {0xF1, '셉'}, {0xF2, '셋'}, {0xF3, '셌'}, {0xF4, '셍'}, {0xF5, '셔'}, {0xF6, '셕'}, {0xF7, '션'}, {0xF8, '셜'}, {0xF9, '셤'}, {0xFA, '셥'}, {0xFB, '셧'}, {0xFC, '셨'}, {0xFD, '셩'}, {0xFE, '셰'}, {0xFF, '셴'}, - }; - - private static readonly Dictionary GSC2U_KOR_6 = new() - { - {0x00, '셸'}, {0x01, '솅'}, {0x02, '소'}, {0x03, '속'}, {0x04, '솎'}, {0x05, '손'}, {0x06, '솔'}, {0x07, '솖'}, {0x08, '솜'}, {0x09, '솝'}, {0x0A, '솟'}, {0x0B, '송'}, {0x0C, '솥'}, {0x0D, '솨'}, {0x0E, '솩'}, {0x0F, '솬'}, - {0x10, '솰'}, {0x11, '솽'}, {0x12, '쇄'}, {0x13, '쇈'}, {0x14, '쇌'}, {0x15, '쇔'}, {0x16, '쇗'}, {0x17, '쇘'}, {0x18, '쇠'}, {0x19, '쇤'}, {0x1A, '쇨'}, {0x1B, '쇰'}, {0x1C, '쇱'}, {0x1D, '쇳'}, {0x1E, '쇼'}, {0x1F, '쇽'}, - {0x20, '숀'}, {0x21, '숄'}, {0x22, '숌'}, {0x23, '숍'}, {0x24, '숏'}, {0x25, '숑'}, {0x26, '수'}, {0x27, '숙'}, {0x28, '순'}, {0x29, '숟'}, {0x2A, '술'}, {0x2B, '숨'}, {0x2C, '숩'}, {0x2D, '숫'}, {0x2E, '숭'}, {0x2F, '쌰'}, - {0x30, '쎼'}, {0x31, '숯'}, {0x32, '숱'}, {0x33, '숲'}, {0x34, '숴'}, {0x35, '쉈'}, {0x36, '쉐'}, {0x37, '쉑'}, {0x38, '쉔'}, {0x39, '쉘'}, {0x3A, '쉠'}, {0x3B, '쉥'}, {0x3C, '쉬'}, {0x3D, '쉭'}, {0x3E, '쉰'}, {0x3F, '쉴'}, - {0x40, '쉼'}, {0x41, '쉽'}, {0x42, '쉿'}, {0x43, '슁'}, {0x44, '슈'}, {0x45, '슉'}, {0x46, '슐'}, {0x47, '슘'}, {0x48, '슛'}, {0x49, '슝'}, {0x4A, '스'}, {0x4B, '슥'}, {0x4C, '슨'}, {0x4D, '슬'}, {0x4E, '슭'}, {0x4F, '슴'}, - {0x60, '습'}, {0x61, '슷'}, {0x62, '승'}, {0x63, '시'}, {0x64, '식'}, {0x65, '신'}, {0x66, '싣'}, {0x67, '실'}, {0x68, '싫'}, {0x69, '심'}, {0x6A, '십'}, {0x6B, '싯'}, {0x6C, '싱'}, {0x6D, '싶'}, {0x6E, '싸'}, {0x6F, '싹'}, - {0x70, '싻'}, {0x71, '싼'}, {0x72, '쌀'}, {0x73, '쌈'}, {0x74, '쌉'}, {0x75, '쌌'}, {0x76, '쌍'}, {0x77, '쌓'}, {0x78, '쌔'}, {0x79, '쌕'}, {0x7A, '쌘'}, {0x7B, '쌜'}, {0x7C, '쌤'}, {0x7D, '쌥'}, {0x7E, '쌨'}, {0x7F, '쌩'}, - {0x80, '썅'}, {0x81, '써'}, {0x82, '썩'}, {0x83, '썬'}, {0x84, '썰'}, {0x85, '썲'}, {0x86, '썸'}, {0x87, '썹'}, {0x88, '썼'}, {0x89, '썽'}, {0x8A, '쎄'}, {0x8B, '쎈'}, {0x8C, '쎌'}, {0x8D, '쏀'}, {0x8E, '쏘'}, {0x8F, '쏙'}, - {0x90, '쏜'}, {0x91, '쏟'}, {0x92, '쏠'}, {0x93, '쏢'}, {0x94, '쏨'}, {0x95, '쏩'}, {0x96, '쏭'}, {0x97, '쏴'}, {0x98, '쏵'}, {0x99, '쏸'}, {0x9A, '쐈'}, {0x9B, '쐐'}, {0x9C, '쐤'}, {0x9D, '쐬'}, {0x9E, '쐰'}, - {0xA0, '쓔'}, {0xA1, '쐴'}, {0xA2, '쐼'}, {0xA3, '쐽'}, {0xA4, '쑈'}, {0xA5, '쑤'}, {0xA6, '쑥'}, {0xA7, '쑨'}, {0xA8, '쑬'}, {0xA9, '쑴'}, {0xAA, '쑵'}, {0xAB, '쑹'}, {0xAC, '쒀'}, {0xAD, '쒔'}, {0xAE, '쒜'}, {0xAF, '쒸'}, - {0xB0, '쒼'}, {0xB1, '쓩'}, {0xB2, '쓰'}, {0xB3, '쓱'}, {0xB4, '쓴'}, {0xB5, '쓸'}, {0xB6, '쓺'}, {0xB7, '쓿'}, {0xB8, '씀'}, {0xB9, '씁'}, {0xBA, '씌'}, {0xBB, '씐'}, {0xBC, '씔'}, {0xBD, '씜'}, {0xBE, '씨'}, {0xBF, '씩'}, - {0xC0, '씬'}, {0xC1, '씰'}, {0xC2, '씸'}, {0xC3, '씹'}, {0xC4, '씻'}, {0xC5, '씽'}, {0xC6, '아'}, {0xC7, '악'}, {0xC8, '안'}, {0xC9, '앉'}, {0xCA, '않'}, {0xCB, '알'}, {0xCC, '앍'}, {0xCD, '앎'}, {0xCE, '앓'}, {0xCF, '암'}, - {0xD0, '압'}, {0xD1, '앗'}, {0xD2, '았'}, {0xD3, '앙'}, {0xD4, '앝'}, {0xD5, '앞'}, {0xD6, '애'}, {0xD7, '액'}, {0xD8, '앤'}, {0xD9, '앨'}, {0xDA, '앰'}, {0xDB, '앱'}, {0xDC, '앳'}, {0xDD, '앴'}, {0xDE, '앵'}, {0xDF, '야'}, - {0xE0, '약'}, {0xE1, '얀'}, {0xE2, '얄'}, {0xE3, '얇'}, {0xE4, '얌'}, {0xE5, '얍'}, {0xE6, '얏'}, {0xE7, '양'}, {0xE8, '얕'}, {0xE9, '얗'}, {0xEA, '얘'}, {0xEB, '얜'}, {0xEC, '얠'}, {0xED, '얩'}, {0xEE, '어'}, {0xEF, '억'}, - {0xF0, '언'}, {0xF1, '얹'}, {0xF2, '얻'}, {0xF3, '얼'}, {0xF4, '얽'}, {0xF5, '얾'}, {0xF6, '엄'}, {0xF7, '업'}, {0xF8, '없'}, {0xF9, '엇'}, {0xFA, '었'}, {0xFB, '엉'}, {0xFC, '엊'}, {0xFD, '엌'}, {0xFE, '엎'}, - }; - - private static readonly Dictionary GSC2U_KOR_7 = new() - { - {0x01, '에'}, {0x02, '엑'}, {0x03, '엔'}, {0x04, '엘'}, {0x05, '엠'}, {0x06, '엡'}, {0x07, '엣'}, {0x08, '엥'}, {0x09, '여'}, {0x0A, '역'}, {0x0B, '엮'}, {0x0C, '연'}, {0x0D, '열'}, {0x0E, '엶'}, {0x0F, '엷'}, - {0x10, '염'}, {0x11, '엽'}, {0x12, '엾'}, {0x13, '엿'}, {0x14, '였'}, {0x15, '영'}, {0x16, '옅'}, {0x17, '옆'}, {0x18, '옇'}, {0x19, '예'}, {0x1A, '옌'}, {0x1B, '옐'}, {0x1C, '옘'}, {0x1D, '옙'}, {0x1E, '옛'}, {0x1F, '옜'}, - {0x20, '오'}, {0x21, '옥'}, {0x22, '온'}, {0x23, '올'}, {0x24, '옭'}, {0x25, '옮'}, {0x26, '옰'}, {0x27, '옳'}, {0x28, '옴'}, {0x29, '옵'}, {0x2A, '옷'}, {0x2B, '옹'}, {0x2C, '옻'}, {0x2D, '와'}, {0x2E, '왁'}, {0x2F, '완'}, - {0x30, '왈'}, {0x31, '왐'}, {0x32, '왑'}, {0x33, '왓'}, {0x34, '왔'}, {0x35, '왕'}, {0x36, '왜'}, {0x37, '왝'}, {0x38, '왠'}, {0x39, '왬'}, {0x3A, '왯'}, {0x3B, '왱'}, {0x3C, '외'}, {0x3D, '왹'}, {0x3E, '왼'}, {0x3F, '욀'}, - {0x40, '욈'}, {0x41, '욉'}, {0x42, '욋'}, {0x43, '욍'}, {0x44, '요'}, {0x45, '욕'}, {0x46, '욘'}, {0x47, '욜'}, {0x48, '욤'}, {0x49, '욥'}, {0x4A, '욧'}, {0x4B, '용'}, {0x4C, '우'}, {0x4D, '욱'}, {0x4E, '운'}, {0x4F, '울'}, - {0x60, '욹'}, {0x61, '욺'}, {0x62, '움'}, {0x63, '웁'}, {0x64, '웃'}, {0x65, '웅'}, {0x66, '워'}, {0x67, '웍'}, {0x68, '원'}, {0x69, '월'}, {0x6A, '웜'}, {0x6B, '웝'}, {0x6C, '웠'}, {0x6D, '웡'}, {0x6E, '웨'}, - {0x71, '웩'}, {0x72, '웬'}, {0x73, '웰'}, {0x74, '웸'}, {0x75, '웹'}, {0x76, '웽'}, {0x77, '위'}, {0x78, '윅'}, {0x79, '윈'}, {0x7A, '윌'}, {0x7B, '윔'}, {0x7C, '윕'}, {0x7D, '윗'}, {0x7E, '윙'}, {0x7F, '유'}, - {0x80, '육'}, {0x81, '윤'}, {0x82, '율'}, {0x83, '윰'}, {0x84, '윱'}, {0x85, '윳'}, {0x86, '융'}, {0x87, '윷'}, {0x88, '으'}, {0x89, '윽'}, {0x8A, '은'}, {0x8B, '을'}, {0x8C, '읆'}, {0x8D, '음'}, {0x8E, '읍'}, {0x8F, '읏'}, - {0x90, '응'}, {0x91, '읒'}, {0x92, '읓'}, {0x93, '읔'}, {0x94, '읕'}, {0x95, '읖'}, {0x96, '읗'}, {0x97, '의'}, {0x98, '읜'}, {0x99, '읠'}, {0x9A, '읨'}, {0x9B, '읫'}, {0x9C, '이'}, {0x9D, '익'}, {0x9E, '인'}, {0x9F, '일'}, - {0xA0, '읽'}, {0xA1, '읾'}, {0xA2, '잃'}, {0xA3, '임'}, {0xA4, '입'}, {0xA5, '잇'}, {0xA6, '있'}, {0xA7, '잉'}, {0xA8, '잊'}, {0xA9, '잎'}, {0xAA, '자'}, {0xAB, '작'}, {0xAC, '잔'}, {0xAD, '잖'}, {0xAE, '잗'}, {0xAF, '잘'}, - {0xB0, '잚'}, {0xB1, '잠'}, {0xB2, '잡'}, {0xB3, '잣'}, {0xB4, '잤'}, {0xB5, '장'}, {0xB6, '잦'}, {0xB7, '재'}, {0xB8, '잭'}, {0xB9, '잰'}, {0xBA, '잴'}, {0xBB, '잼'}, {0xBC, '잽'}, {0xBD, '잿'}, {0xBE, '쟀'}, {0xBF, '쟁'}, - {0xC0, '쟈'}, {0xC1, '쟉'}, {0xC2, '쟌'}, {0xC3, '쟎'}, {0xC4, '쟐'}, {0xC5, '쟘'}, {0xC6, '쟝'}, {0xC7, '쟤'}, {0xC8, '쟨'}, {0xC9, '쟬'}, {0xCA, '저'}, {0xCB, '적'}, {0xCC, '전'}, {0xCD, '절'}, {0xCE, '젊'}, - {0xD1, '점'}, {0xD2, '접'}, {0xD3, '젓'}, {0xD4, '정'}, {0xD5, '젖'}, {0xD6, '제'}, {0xD7, '젝'}, {0xD8, '젠'}, {0xD9, '젤'}, {0xDA, '젬'}, {0xDB, '젭'}, {0xDC, '젯'}, {0xDD, '젱'}, {0xDE, '져'}, {0xDF, '젼'}, - {0xE0, '졀'}, {0xE1, '졈'}, {0xE2, '졉'}, {0xE3, '졌'}, {0xE4, '졍'}, {0xE5, '졔'}, {0xE6, '조'}, {0xE7, '족'}, {0xE8, '존'}, {0xE9, '졸'}, {0xEA, '졺'}, {0xEB, '좀'}, {0xEC, '좁'}, {0xED, '좃'}, {0xEE, '종'}, {0xEF, '좆'}, - {0xF0, '좇'}, {0xF1, '좋'}, {0xF2, '좌'}, {0xF3, '좍'}, {0xF4, '좔'}, {0xF5, '좝'}, {0xF6, '좟'}, {0xF7, '좡'}, {0xF8, '좨'}, {0xF9, '좼'}, {0xFA, '좽'}, {0xFB, '죄'}, {0xFC, '죈'}, {0xFD, '죌'}, {0xFE, '죔'}, {0xFF, '죕'}, - }; - - private static readonly Dictionary GSC2U_KOR_8 = new() - { - {0x00, '죗'}, {0x01, '죙'}, {0x02, '죠'}, {0x03, '죡'}, {0x04, '죤'}, {0x05, '죵'}, {0x06, '주'}, {0x07, '죽'}, {0x08, '준'}, {0x09, '줄'}, {0x0A, '줅'}, {0x0B, '줆'}, {0x0C, '줌'}, {0x0D, '줍'}, {0x0E, '줏'}, {0x0F, '중'}, - {0x10, '줘'}, {0x11, '줬'}, {0x12, '줴'}, {0x13, '쥐'}, {0x14, '쥑'}, {0x15, '쥔'}, {0x16, '쥘'}, {0x17, '쥠'}, {0x18, '쥡'}, {0x19, '쥣'}, {0x1A, '쥬'}, {0x1B, '쥰'}, {0x1C, '쥴'}, {0x1D, '쥼'}, {0x1E, '즈'}, {0x1F, '즉'}, - {0x20, '즌'}, {0x21, '즐'}, {0x22, '즘'}, {0x23, '즙'}, {0x24, '즛'}, {0x25, '증'}, {0x26, '지'}, {0x27, '직'}, {0x28, '진'}, {0x29, '짇'}, {0x2A, '질'}, {0x2B, '짊'}, {0x2C, '짐'}, {0x2D, '집'}, {0x2E, '짓'}, - {0x30, '쬬'}, {0x31, '징'}, {0x32, '짖'}, {0x33, '짙'}, {0x34, '짚'}, {0x35, '짜'}, {0x36, '짝'}, {0x37, '짠'}, {0x38, '짢'}, {0x39, '짤'}, {0x3A, '짧'}, {0x3B, '짬'}, {0x3C, '짭'}, {0x3D, '짯'}, {0x3E, '짰'}, {0x3F, '짱'}, - {0x40, '째'}, {0x41, '짹'}, {0x42, '짼'}, {0x43, '쨀'}, {0x44, '쨈'}, {0x45, '쨉'}, {0x46, '쨋'}, {0x47, '쨌'}, {0x48, '쨍'}, {0x49, '쨔'}, {0x4A, '쨘'}, {0x4B, '쨩'}, {0x4C, '쩌'}, {0x4D, '쩍'}, {0x4E, '쩐'}, {0x4F, '쩔'}, - {0x60, '쩜'}, {0x61, '쩝'}, {0x62, '쩟'}, {0x63, '쩠'}, {0x64, '쩡'}, {0x65, '쩨'}, {0x66, '쩽'}, {0x67, '쪄'}, {0x68, '쪘'}, {0x69, '쪼'}, {0x6A, '쪽'}, {0x6B, '쫀'}, {0x6C, '쫄'}, {0x6D, '쫌'}, {0x6E, '쫍'}, {0x6F, '쫏'}, - {0x70, '쫑'}, {0x71, '쫓'}, {0x72, '쫘'}, {0x73, '쫙'}, {0x74, '쫠'}, {0x75, '쫬'}, {0x76, '쫴'}, {0x77, '쬈'}, {0x78, '쬐'}, {0x79, '쬔'}, {0x7A, '쬘'}, {0x7B, '쬠'}, {0x7C, '쬡'}, {0x7D, '쭁'}, {0x7E, '쭈'}, {0x7F, '쭉'}, - {0x80, '쭌'}, {0x81, '쭐'}, {0x82, '쭘'}, {0x83, '쭙'}, {0x84, '쭝'}, {0x85, '쭤'}, {0x86, '쭸'}, {0x87, '쭹'}, {0x88, '쮜'}, {0x89, '쮸'}, {0x8A, '쯔'}, {0x8B, '쯤'}, {0x8C, '쯧'}, {0x8D, '쯩'}, {0x8E, '찌'}, {0x8F, '찍'}, - {0x90, '찐'}, {0x91, '찔'}, {0x92, '찜'}, {0x93, '찝'}, {0x94, '찡'}, {0x95, '찢'}, {0x96, '찧'}, {0x97, '차'}, {0x98, '착'}, {0x99, '찬'}, {0x9A, '찮'}, {0x9B, '찰'}, {0x9C, '참'}, {0x9D, '찹'}, {0x9E, '찻'}, - {0xA1, '찼'}, {0xA2, '창'}, {0xA3, '찾'}, {0xA4, '채'}, {0xA5, '책'}, {0xA6, '챈'}, {0xA7, '챌'}, {0xA8, '챔'}, {0xA9, '챕'}, {0xAA, '챗'}, {0xAB, '챘'}, {0xAC, '챙'}, {0xAD, '챠'}, {0xAE, '챤'}, {0xAF, '챦'}, - {0xB0, '챨'}, {0xB1, '챰'}, {0xB2, '챵'}, {0xB3, '처'}, {0xB4, '척'}, {0xB5, '천'}, {0xB6, '철'}, {0xB7, '첨'}, {0xB8, '첩'}, {0xB9, '첫'}, {0xBA, '첬'}, {0xBB, '청'}, {0xBC, '체'}, {0xBD, '첵'}, {0xBE, '첸'}, {0xBF, '첼'}, - {0xC0, '쳄'}, {0xC1, '쳅'}, {0xC2, '쳇'}, {0xC3, '쳉'}, {0xC4, '쳐'}, {0xC5, '쳔'}, {0xC6, '쳤'}, {0xC7, '쳬'}, {0xC8, '쳰'}, {0xC9, '촁'}, {0xCA, '초'}, {0xCB, '촉'}, {0xCC, '촌'}, {0xCD, '촐'}, {0xCE, '촘'}, {0xCF, '촙'}, - {0xD0, '촛'}, {0xD1, '총'}, {0xD2, '촤'}, {0xD3, '촨'}, {0xD4, '촬'}, {0xD5, '촹'}, {0xD6, '최'}, {0xD7, '쵠'}, {0xD8, '쵤'}, {0xD9, '쵬'}, {0xDA, '쵭'}, {0xDB, '쵯'}, {0xDC, '쵱'}, {0xDD, '쵸'}, {0xDE, '춈'}, {0xDF, '추'}, - {0xE0, '축'}, {0xE1, '춘'}, {0xE2, '출'}, {0xE3, '춤'}, {0xE4, '춥'}, {0xE5, '춧'}, {0xE6, '충'}, {0xE7, '춰'}, {0xE8, '췄'}, {0xE9, '췌'}, {0xEA, '췐'}, {0xEB, '취'}, {0xEC, '췬'}, {0xED, '췰'}, {0xEE, '췸'}, {0xEF, '췹'}, - {0xF0, '췻'}, {0xF1, '췽'}, {0xF2, '츄'}, {0xF3, '츈'}, {0xF4, '츌'}, {0xF5, '츔'}, {0xF6, '츙'}, {0xF7, '츠'}, {0xF8, '측'}, {0xF9, '츤'}, {0xFA, '츨'}, {0xFB, '츰'}, {0xFC, '츱'}, {0xFD, '츳'}, {0xFE, '층'}, - }; - - private static readonly Dictionary GSC2U_KOR_9 = new() - { - {0x01, '치'}, {0x02, '칙'}, {0x03, '친'}, {0x04, '칟'}, {0x05, '칠'}, {0x06, '칡'}, {0x07, '침'}, {0x08, '칩'}, {0x09, '칫'}, {0x0A, '칭'}, {0x0B, '카'}, {0x0C, '칵'}, {0x0D, '칸'}, {0x0E, '칼'}, {0x0F, '캄'}, - {0x10, '캅'}, {0x11, '캇'}, {0x12, '캉'}, {0x13, '캐'}, {0x14, '캑'}, {0x15, '캔'}, {0x16, '캘'}, {0x17, '캠'}, {0x18, '캡'}, {0x19, '캣'}, {0x1A, '캤'}, {0x1B, '캥'}, {0x1C, '캬'}, {0x1D, '캭'}, {0x1E, '컁'}, {0x1F, '커'}, - {0x20, '컥'}, {0x21, '컨'}, {0x22, '컫'}, {0x23, '컬'}, {0x24, '컴'}, {0x25, '컵'}, {0x26, '컷'}, {0x27, '컸'}, {0x28, '컹'}, {0x29, '케'}, {0x2A, '켁'}, {0x2B, '켄'}, {0x2C, '켈'}, {0x2D, '켐'}, {0x2E, '켑'}, {0x2F, '켓'}, - {0x30, '켕'}, {0x31, '켜'}, {0x32, '켠'}, {0x33, '켤'}, {0x34, '켬'}, {0x35, '켭'}, {0x36, '켯'}, {0x37, '켰'}, {0x38, '켱'}, {0x39, '켸'}, {0x3A, '코'}, {0x3B, '콕'}, {0x3C, '콘'}, {0x3D, '콜'}, {0x3E, '콤'}, {0x3F, '콥'}, - {0x40, '콧'}, {0x41, '콩'}, {0x42, '콰'}, {0x43, '콱'}, {0x44, '콴'}, {0x45, '콸'}, {0x46, '쾀'}, {0x47, '쾅'}, {0x48, '쾌'}, {0x49, '쾡'}, {0x4A, '쾨'}, {0x4B, '쾰'}, {0x4C, '쿄'}, {0x4D, '쿠'}, {0x4E, '쿡'}, {0x4F, '쿤'}, - {0x60, '쿨'}, {0x61, '쿰'}, {0x62, '쿱'}, {0x63, '쿳'}, {0x64, '쿵'}, {0x65, '쿼'}, {0x66, '퀀'}, {0x67, '퀄'}, {0x68, '퀑'}, {0x69, '퀘'}, {0x6A, '퀭'}, {0x6B, '퀴'}, {0x6C, '퀵'}, {0x6D, '퀸'}, {0x6E, '퀼'}, - {0x71, '큄'}, {0x72, '큅'}, {0x73, '큇'}, {0x74, '큉'}, {0x75, '큐'}, {0x76, '큔'}, {0x77, '큘'}, {0x78, '큠'}, {0x79, '크'}, {0x7A, '큭'}, {0x7B, '큰'}, {0x7C, '클'}, {0x7D, '큼'}, {0x7E, '큽'}, {0x7F, '킁'}, - {0x80, '키'}, {0x81, '킥'}, {0x82, '킨'}, {0x83, '킬'}, {0x84, '킴'}, {0x85, '킵'}, {0x86, '킷'}, {0x87, '킹'}, {0x88, '타'}, {0x89, '탁'}, {0x8A, '탄'}, {0x8B, '탈'}, {0x8C, '탉'}, {0x8D, '탐'}, {0x8E, '탑'}, {0x8F, '탓'}, - {0x90, '탔'}, {0x91, '탕'}, {0x92, '태'}, {0x93, '택'}, {0x94, '탠'}, {0x95, '탤'}, {0x96, '탬'}, {0x97, '탭'}, {0x98, '탯'}, {0x99, '탰'}, {0x9A, '탱'}, {0x9B, '탸'}, {0x9C, '턍'}, {0x9D, '터'}, {0x9E, '턱'}, {0x9F, '턴'}, - {0xA0, '털'}, {0xA1, '턺'}, {0xA2, '텀'}, {0xA3, '텁'}, {0xA4, '텃'}, {0xA5, '텄'}, {0xA6, '텅'}, {0xA7, '테'}, {0xA8, '텍'}, {0xA9, '텐'}, {0xAA, '텔'}, {0xAB, '템'}, {0xAC, '텝'}, {0xAD, '텟'}, {0xAE, '텡'}, {0xAF, '텨'}, - {0xB0, '텬'}, {0xB1, '텼'}, {0xB2, '톄'}, {0xB3, '톈'}, {0xB4, '토'}, {0xB5, '톡'}, {0xB6, '톤'}, {0xB7, '톨'}, {0xB8, '톰'}, {0xB9, '톱'}, {0xBA, '톳'}, {0xBB, '통'}, {0xBC, '톺'}, {0xBD, '톼'}, {0xBE, '퇀'}, {0xBF, '퇘'}, - {0xC0, '퇴'}, {0xC1, '퇸'}, {0xC2, '툇'}, {0xC3, '툉'}, {0xC4, '툐'}, {0xC5, '투'}, {0xC6, '툭'}, {0xC7, '툰'}, {0xC8, '툴'}, {0xC9, '툼'}, {0xCA, '툽'}, {0xCB, '툿'}, {0xCC, '퉁'}, {0xCD, '퉈'}, {0xCE, '퉜'}, - {0xD1, '퉤'}, {0xD2, '튀'}, {0xD3, '튁'}, {0xD4, '튄'}, {0xD5, '튈'}, {0xD6, '튐'}, {0xD7, '튑'}, {0xD8, '튕'}, {0xD9, '튜'}, {0xDA, '튠'}, {0xDB, '튤'}, {0xDC, '튬'}, {0xDD, '튱'}, {0xDE, '트'}, {0xDF, '특'}, - {0xE0, '튼'}, {0xE1, '튿'}, {0xE2, '틀'}, {0xE3, '틂'}, {0xE4, '틈'}, {0xE5, '틉'}, {0xE6, '틋'}, {0xE7, '틔'}, {0xE8, '틘'}, {0xE9, '틜'}, {0xEA, '틤'}, {0xEB, '틥'}, {0xEC, '티'}, {0xED, '틱'}, {0xEE, '틴'}, {0xEF, '틸'}, - {0xF0, '팀'}, {0xF1, '팁'}, {0xF2, '팃'}, {0xF3, '팅'}, {0xF4, '파'}, {0xF5, '팍'}, {0xF6, '팎'}, {0xF7, '판'}, {0xF8, '팔'}, {0xF9, '팖'}, {0xFA, '팜'}, {0xFB, '팝'}, {0xFC, '팟'}, {0xFD, '팠'}, {0xFE, '팡'}, {0xFF, '팥'}, - }; - - private static readonly Dictionary GSC2U_KOR_A = new() - { - {0x00, '패'}, {0x01, '팩'}, {0x02, '팬'}, {0x03, '팰'}, {0x04, '팸'}, {0x05, '팹'}, {0x06, '팻'}, {0x07, '팼'}, {0x08, '팽'}, {0x09, '퍄'}, {0x0A, '퍅'}, {0x0B, '퍼'}, {0x0C, '퍽'}, {0x0D, '펀'}, {0x0E, '펄'}, {0x0F, '펌'}, - {0x10, '펍'}, {0x11, '펏'}, {0x12, '펐'}, {0x13, '펑'}, {0x14, '페'}, {0x15, '펙'}, {0x16, '펜'}, {0x17, '펠'}, {0x18, '펨'}, {0x19, '펩'}, {0x1A, '펫'}, {0x1B, '펭'}, {0x1C, '펴'}, {0x1D, '편'}, {0x1E, '펼'}, {0x1F, '폄'}, - {0x20, '폅'}, {0x21, '폈'}, {0x22, '평'}, {0x23, '폐'}, {0x24, '폘'}, {0x25, '폡'}, {0x26, '폣'}, {0x27, '포'}, {0x28, '폭'}, {0x29, '폰'}, {0x2A, '폴'}, {0x2B, '폼'}, {0x2C, '폽'}, {0x2D, '폿'}, {0x2E, '퐁'}, - {0x31, '퐈'}, {0x32, '퐝'}, {0x33, '푀'}, {0x34, '푄'}, {0x35, '표'}, {0x36, '푠'}, {0x37, '푤'}, {0x38, '푭'}, {0x39, '푯'}, {0x3A, '푸'}, {0x3B, '푹'}, {0x3C, '푼'}, {0x3D, '푿'}, {0x3E, '풀'}, {0x3F, '풂'}, - {0x40, '품'}, {0x41, '풉'}, {0x42, '풋'}, {0x43, '풍'}, {0x44, '풔'}, {0x45, '풩'}, {0x46, '퓌'}, {0x47, '퓐'}, {0x48, '퓔'}, {0x49, '퓜'}, {0x4A, '퓟'}, {0x4B, '퓨'}, {0x4C, '퓬'}, {0x4D, '퓰'}, {0x4E, '퓸'}, {0x4F, '퓻'}, - {0x60, '퓽'}, {0x61, '프'}, {0x62, '픈'}, {0x63, '플'}, {0x64, '픔'}, {0x65, '픕'}, {0x66, '픗'}, {0x67, '피'}, {0x68, '픽'}, {0x69, '핀'}, {0x6A, '필'}, {0x6B, '핌'}, {0x6C, '핍'}, {0x6D, '핏'}, {0x6E, '핑'}, {0x6F, '하'}, - {0x70, '학'}, {0x71, '한'}, {0x72, '할'}, {0x73, '핥'}, {0x74, '함'}, {0x75, '합'}, {0x76, '핫'}, {0x77, '항'}, {0x78, '해'}, {0x79, '핵'}, {0x7A, '핸'}, {0x7B, '핼'}, {0x7C, '햄'}, {0x7D, '햅'}, {0x7E, '햇'}, {0x7F, '했'}, - {0x80, '행'}, {0x81, '햐'}, {0x82, '향'}, {0x83, '허'}, {0x84, '헉'}, {0x85, '헌'}, {0x86, '헐'}, {0x87, '헒'}, {0x88, '험'}, {0x89, '헙'}, {0x8A, '헛'}, {0x8B, '헝'}, {0x8C, '헤'}, {0x8D, '헥'}, {0x8E, '헨'}, {0x8F, '헬'}, - {0x90, '헴'}, {0x91, '헵'}, {0x92, '헷'}, {0x93, '헹'}, {0x94, '혀'}, {0x95, '혁'}, {0x96, '현'}, {0x97, '혈'}, {0x98, '혐'}, {0x99, '협'}, {0x9A, '혓'}, {0x9B, '혔'}, {0x9C, '형'}, {0x9D, '혜'}, {0x9E, '혠'}, - {0xA1, '혤'}, {0xA2, '혭'}, {0xA3, '호'}, {0xA4, '혹'}, {0xA5, '혼'}, {0xA6, '홀'}, {0xA7, '홅'}, {0xA8, '홈'}, {0xA9, '홉'}, {0xAA, '홋'}, {0xAB, '홍'}, {0xAC, '홑'}, {0xAD, '화'}, {0xAE, '확'}, {0xAF, '환'}, - {0xB0, '활'}, {0xB1, '홧'}, {0xB2, '황'}, {0xB3, '홰'}, {0xB4, '홱'}, {0xB5, '홴'}, {0xB6, '횃'}, {0xB7, '횅'}, {0xB8, '회'}, {0xB9, '획'}, {0xBA, '횐'}, {0xBB, '횔'}, {0xBC, '횝'}, {0xBD, '횟'}, {0xBE, '횡'}, {0xBF, '효'}, - {0xC0, '횬'}, {0xC1, '횰'}, {0xC2, '횹'}, {0xC3, '횻'}, {0xC4, '후'}, {0xC5, '훅'}, {0xC6, '훈'}, {0xC7, '훌'}, {0xC8, '훑'}, {0xC9, '훔'}, {0xCA, '훗'}, {0xCB, '훙'}, {0xCC, '훠'}, {0xCD, '훤'}, {0xCE, '훨'}, {0xCF, '훰'}, - {0xD0, '훵'}, {0xD1, '훼'}, {0xD2, '훽'}, {0xD3, '휀'}, {0xD4, '휄'}, {0xD5, '휑'}, {0xD6, '휘'}, {0xD7, '휙'}, {0xD8, '휜'}, {0xD9, '휠'}, {0xDA, '휨'}, {0xDB, '휩'}, {0xDC, '휫'}, {0xDD, '휭'}, {0xDE, '휴'}, {0xDF, '휵'}, - {0xE0, '휸'}, {0xE1, '휼'}, {0xE2, '흄'}, {0xE3, '흇'}, {0xE4, '흉'}, {0xE5, '흐'}, {0xE6, '흑'}, {0xE7, '흔'}, {0xE8, '흖'}, {0xE9, '흗'}, {0xEA, '흘'}, {0xEB, '흙'}, {0xEC, '흠'}, {0xED, '흡'}, {0xEE, '흣'}, {0xEF, '흥'}, - {0xF0, '흩'}, {0xF1, '희'}, {0xF2, '흰'}, {0xF3, '흴'}, {0xF4, '흼'}, {0xF5, '흽'}, {0xF6, '힁'}, {0xF7, '히'}, {0xF8, '힉'}, {0xF9, '힌'}, {0xFA, '힐'}, {0xFB, '힘'}, {0xFC, '힙'}, {0xFD, '힛'}, {0xFE, '힝'}, - }; - - private static readonly Dictionary GSC2U_KOR_B = GSC2U_KOR_0; - - /// Unicode codepoint => Gen2 value dictionary pages. - private static readonly Dictionary[] U2GSC_KOR = - { - U2GSC_KOR_0, U2GSC_KOR_1, U2GSC_KOR_2, U2GSC_KOR_3, - U2GSC_KOR_4, U2GSC_KOR_5, U2GSC_KOR_6, U2GSC_KOR_7, - U2GSC_KOR_8, U2GSC_KOR_9, U2GSC_KOR_A, U2GSC_KOR_B, - }; - - /// Gen2 value => Unicode codepoint dictionary pages. - private static readonly Dictionary[] GSC2U_KOR = - { - GSC2U_KOR_0, GSC2U_KOR_1, GSC2U_KOR_2, GSC2U_KOR_3, - GSC2U_KOR_4, GSC2U_KOR_5, GSC2U_KOR_6, GSC2U_KOR_7, - GSC2U_KOR_8, GSC2U_KOR_9, GSC2U_KOR_A, GSC2U_KOR_B, - }; - - #endregion + return true; } + + /// + /// Converts Generation 2 Korean encoded data into a string. + /// + /// Encoded data. + /// Decoded string. + public static string GetString(ReadOnlySpan data) + { + Span result = stackalloc char[data.Length]; + int length = LoadString(data, result); + return new string(result[..length].ToArray()); + } + + private static int LoadString(ReadOnlySpan data, Span result) + { + if (data[0] == G1TradeOTCode) + { + result[0] = G1Terminator; + return 1; + } + + int i = 0; + int ctr = 0; + for (; i < data.Length; i++) + { + var value = data[i]; + var table = value <= 0xB ? GSC2U_KOR[value] : RBY2U_U; + if (value is <= 0xB and not 0) + value = data[++i]; + if (!table.TryGetValue(value, out var c)) // Take valid values + break; + if (c == G1Terminator) // Stop if Terminator + break; + result[ctr++] = c; + } + return ctr; + } + + /// + /// Converts a string to Generation 1 encoded data. + /// + /// Decoded string. + /// + /// Maximum length of the input + /// + /// Encoded data. + public static int SetString(ReadOnlySpan value, Span destBuffer, int maxLength, StringConverterOption option = StringConverterOption.Clear50) + { + if (option is StringConverterOption.ClearZero) + destBuffer.Clear(); + else if (option is StringConverterOption.Clear50) + destBuffer.Fill(G1TerminatorCode); + + if (value.Length != 0 && value[0] == G1TradeOT) // Handle "[TRAINER]" + { + destBuffer[0] = G1TradeOTCode; + destBuffer[1] = G1TerminatorCode; + return 2; + } + + if (value.Length > maxLength) + value = value[..maxLength]; // Hard cap + + var kor = U2GSC_KOR; + var dict = U2RBY_U; + int ctr = 0; + foreach (var c in value) + { + var koreanChar = false; + // although the 0x00 and 0x0B dictionaries are identical, the game only uses 0x0B. + for (byte t = 1; t < kor.Length; t++) + { + var table = kor[t]; + if (!table.TryGetValue(c, out byte val)) + continue; + koreanChar = true; + + if (ctr + 2 > destBuffer.Length) + break; // adding 2 characters will overflow requested buffer cap + destBuffer[ctr++] = t; + destBuffer[ctr++] = val; + break; + } + + if (!koreanChar) + { + if (!dict.TryGetValue(c, out byte val)) + break; + destBuffer[ctr++] = val; + } + } + + if (destBuffer.Length != value.Length) + destBuffer[ctr++] = G1TerminatorCode; + return ctr; + } + + /// + /// Localizes a Gen4+ Korean species name to the localization used in Generation 2 Gold/Silver + /// + /// Generation 4 Species Name + /// Localized Name for Generation 2 + public static string LocalizeKOR2(string nick) + { + if (KorG2Localized.TryGetValue(nick, out string localized)) + return localized; + return nick; + } + + private static readonly Dictionary KorG2Localized = new() + { + { "덩쿠리", "덩구리" }, // Tangela + { "슈륙챙이", "수륙챙이" }, // Poliwhirl + }; + + #region Gen 2 Korean Character Tables + private static readonly Dictionary U2GSC_KOR_0 = new() + { + {'ㄱ', 0x00}, {'ㄴ', 0x01}, {'ㄷ', 0x02}, {'ㄹ', 0x03}, {'ㅁ', 0x04}, {'ㅂ', 0x05}, {'ㅅ', 0x06}, {'ㅇ', 0x07}, {'ㅈ', 0x08}, {'ㅊ', 0x09}, {'ㅋ', 0x0A}, {'ㅌ', 0x0B}, {'ㅍ', 0x0C}, {'ㅎ', 0x0D}, {'ㄲ', 0x0E}, {'ㄸ', 0x0F}, + {'ㅃ', 0x10}, {'ㅆ', 0x11}, {'ㅉ', 0x12}, + {'ㅏ', 0x20}, {'ㅑ', 0x21}, {'ㅓ', 0x22}, {'ㅕ', 0x23}, {'ㅗ', 0x24}, {'ㅛ', 0x25}, {'ㅜ', 0x26}, {'ㅠ', 0x27}, {'ㅡ', 0x28}, {'ㅣ', 0x29}, {'ㅐ', 0x2A}, {'ㅒ', 0x2B}, {'ㅔ', 0x2C}, {'ㅖ', 0x2D}, {'ㅘ', 0x2E}, {'ㅙ', 0x2F}, + {'ㅚ', 0x30}, {'ㅝ', 0x31}, {'ㅞ', 0x32}, {'ㅟ', 0x33}, {'ㅢ', 0x34}, {'_', 0x3E}, {'—', 0x3F}, + {'「', 0x60}, {'」', 0x61}, {'『', 0x62}, {'』', 0x63}, {'(', 0x64}, {')', 0x65}, {'!', 0x66}, {'?', 0x67}, {'-', 0x68}, {'~', 0x69}, {'…', 0x6A}, {',', 0x6B}, {'.', 0x6C}, // . is 0x6D for bank + {'0', 0xF0}, {'1', 0xF1}, {'2', 0xF2}, {'3', 0xF3}, {'4', 0xF4}, {'5', 0xF5}, {'6', 0xF6}, {'7', 0xF7}, {'8', 0xF8}, {'9', 0xF9}, + {'\u3000', 0xFF}, + }; + + private static readonly Dictionary U2GSC_KOR_1 = new() + { + {'가', 0x01}, {'각', 0x02}, {'간', 0x03}, {'갇', 0x04}, {'갈', 0x05}, {'갉', 0x06}, {'갊', 0x07}, {'감', 0x08}, {'갑', 0x09}, {'값', 0x0A}, {'갓', 0x0B}, {'갔', 0x0C}, {'강', 0x0D}, {'갖', 0x0E}, {'갗', 0x0F}, + {'같', 0x10}, {'갚', 0x11}, {'갛', 0x12}, {'개', 0x13}, {'객', 0x14}, {'갠', 0x15}, {'갤', 0x16}, {'갬', 0x17}, {'갭', 0x18}, {'갯', 0x19}, {'갰', 0x1A}, {'갱', 0x1B}, {'갸', 0x1C}, {'갹', 0x1D}, {'갼', 0x1E}, {'걀', 0x1F}, + {'걋', 0x20}, {'걍', 0x21}, {'걔', 0x22}, {'걘', 0x23}, {'걜', 0x24}, {'거', 0x25}, {'걱', 0x26}, {'건', 0x27}, {'걷', 0x28}, {'걸', 0x29}, {'걺', 0x2A}, {'검', 0x2B}, {'겁', 0x2C}, {'것', 0x2D}, {'겄', 0x2E}, {'겅', 0x2F}, + {'겆', 0x30}, {'겉', 0x31}, {'겊', 0x32}, {'겋', 0x33}, {'게', 0x34}, {'겐', 0x35}, {'겔', 0x36}, {'겜', 0x37}, {'겝', 0x38}, {'겟', 0x39}, {'겠', 0x3A}, {'겡', 0x3B}, {'겨', 0x3C}, {'격', 0x3D}, {'겪', 0x3E}, {'견', 0x3F}, + {'겯', 0x40}, {'결', 0x41}, {'겹', 0x42}, {'겸', 0x43}, {'겻', 0x44}, {'겼', 0x45}, {'경', 0x46}, {'곁', 0x47}, {'계', 0x48}, {'곈', 0x49}, {'곌', 0x4A}, {'곕', 0x4B}, {'곗', 0x4C}, {'고', 0x4D}, {'곡', 0x4E}, {'곤', 0x4F}, + {'곧', 0x60}, {'골', 0x61}, {'곪', 0x62}, {'곬', 0x63}, {'곯', 0x64}, {'곰', 0x65}, {'곱', 0x66}, {'곳', 0x67}, {'공', 0x68}, {'곶', 0x69}, {'과', 0x6A}, {'곽', 0x6B}, {'관', 0x6C}, {'괄', 0x6D}, {'괆', 0x6E}, + {'괌', 0x71}, {'괍', 0x72}, {'괏', 0x73}, {'광', 0x74}, {'괘', 0x75}, {'괜', 0x76}, {'괠', 0x77}, {'괩', 0x78}, {'괬', 0x79}, {'괭', 0x7A}, {'괴', 0x7B}, {'괵', 0x7C}, {'괸', 0x7D}, {'괼', 0x7E}, {'괻', 0x7F}, + {'굅', 0x80}, {'굇', 0x81}, {'굉', 0x82}, {'교', 0x83}, {'굔', 0x84}, {'굘', 0x85}, {'굡', 0x86}, {'굣', 0x87}, {'구', 0x88}, {'국', 0x89}, {'군', 0x8A}, {'굳', 0x8B}, {'굴', 0x8C}, {'굵', 0x8D}, {'굶', 0x8E}, {'굻', 0x8F}, + {'굼', 0x90}, {'굽', 0x91}, {'굿', 0x92}, {'궁', 0x93}, {'궂', 0x94}, {'궈', 0x95}, {'궉', 0x96}, {'권', 0x97}, {'궐', 0x98}, {'궜', 0x99}, {'궝', 0x9A}, {'궤', 0x9B}, {'궷', 0x9C}, {'귀', 0x9D}, {'귁', 0x9E}, {'귄', 0x9F}, + {'귈', 0xA0}, {'귐', 0xA1}, {'귑', 0xA2}, {'귓', 0xA3}, {'규', 0xA4}, {'균', 0xA5}, {'귤', 0xA6}, {'그', 0xA7}, {'극', 0xA8}, {'근', 0xA9}, {'귿', 0xAA}, {'글', 0xAB}, {'긁', 0xAC}, {'금', 0xAD}, {'급', 0xAE}, {'긋', 0xAF}, + {'긍', 0xB0}, {'긔', 0xB1}, {'기', 0xB2}, {'긱', 0xB3}, {'긴', 0xB4}, {'긷', 0xB5}, {'길', 0xB6}, {'긺', 0xB7}, {'김', 0xB8}, {'깁', 0xB9}, {'깃', 0xBA}, {'깅', 0xBB}, {'깆', 0xBC}, {'깊', 0xBD}, {'까', 0xBE}, {'깍', 0xBF}, + {'깎', 0xC0}, {'깐', 0xC1}, {'깔', 0xC2}, {'깖', 0xC3}, {'깜', 0xC4}, {'깝', 0xC5}, {'깟', 0xC6}, {'깠', 0xC7}, {'깡', 0xC8}, {'깥', 0xC9}, {'깨', 0xCA}, {'깩', 0xCB}, {'깬', 0xCC}, {'깰', 0xCD}, {'깸', 0xCE}, + {'깹', 0xD1}, {'깻', 0xD2}, {'깼', 0xD3}, {'깽', 0xD4}, {'꺄', 0xD5}, {'꺅', 0xD6}, {'꺌', 0xD7}, {'꺼', 0xD8}, {'꺽', 0xD9}, {'꺾', 0xDA}, {'껀', 0xDB}, {'껄', 0xDC}, {'껌', 0xDD}, {'껍', 0xDE}, {'껏', 0xDF}, + {'껐', 0xE0}, {'껑', 0xE1}, {'께', 0xE2}, {'껙', 0xE3}, {'껜', 0xE4}, {'껨', 0xE5}, {'껫', 0xE6}, {'껭', 0xE7}, {'껴', 0xE8}, {'껸', 0xE9}, {'껼', 0xEA}, {'꼇', 0xEB}, {'꼈', 0xEC}, {'꼍', 0xED}, {'꼐', 0xEE}, {'꼬', 0xEF}, + {'꼭', 0xF0}, {'꼰', 0xF1}, {'꼲', 0xF2}, {'꼴', 0xF3}, {'꼼', 0xF4}, {'꼽', 0xF5}, {'꼿', 0xF6}, {'꽁', 0xF7}, {'꽂', 0xF8}, {'꽃', 0xF9}, {'꽈', 0xFA}, {'꽉', 0xFB}, {'꽐', 0xFC}, {'꽜', 0xFD}, {'꽝', 0xFE}, {'꽤', 0xFF}, + }; + + private static readonly Dictionary U2GSC_KOR_2 = new() + { + {'꽥', 0x00}, {'꽹', 0x01}, {'꾀', 0x02}, {'꾄', 0x03}, {'꾈', 0x04}, {'꾐', 0x05}, {'꾑', 0x06}, {'꾕', 0x07}, {'꾜', 0x08}, {'꾸', 0x09}, {'꾹', 0x0A}, {'꾼', 0x0B}, {'꿀', 0x0C}, {'꿇', 0x0D}, {'꿈', 0x0E}, {'꿉', 0x0F}, + {'꿋', 0x10}, {'꿍', 0x11}, {'꿎', 0x12}, {'꿔', 0x13}, {'꿜', 0x14}, {'꿨', 0x15}, {'꿩', 0x16}, {'꿰', 0x17}, {'꿱', 0x18}, {'꿴', 0x19}, {'꿸', 0x1A}, {'뀀', 0x1B}, {'뀁', 0x1C}, {'뀄', 0x1D}, {'뀌', 0x1E}, {'뀐', 0x1F}, + {'뀔', 0x20}, {'뀜', 0x21}, {'뀝', 0x22}, {'뀨', 0x23}, {'끄', 0x24}, {'끅', 0x25}, {'끈', 0x26}, {'끊', 0x27}, {'끌', 0x28}, {'끎', 0x29}, {'끓', 0x2A}, {'끔', 0x2B}, {'끕', 0x2C}, {'끗', 0x2D}, {'끙', 0x2E}, + {'끝', 0x31}, {'끼', 0x32}, {'끽', 0x33}, {'낀', 0x34}, {'낄', 0x35}, {'낌', 0x36}, {'낍', 0x37}, {'낏', 0x38}, {'낑', 0x39}, {'나', 0x3A}, {'낙', 0x3B}, {'낚', 0x3C}, {'난', 0x3D}, {'낟', 0x3E}, {'날', 0x3F}, + {'낡', 0x40}, {'낢', 0x41}, {'남', 0x42}, {'납', 0x43}, {'낫', 0x44}, {'났', 0x45}, {'낭', 0x46}, {'낮', 0x47}, {'낯', 0x48}, {'낱', 0x49}, {'낳', 0x4A}, {'내', 0x4B}, {'낵', 0x4C}, {'낸', 0x4D}, {'낼', 0x4E}, {'냄', 0x4F}, + {'냅', 0x60}, {'냇', 0x61}, {'냈', 0x62}, {'냉', 0x63}, {'냐', 0x64}, {'냑', 0x65}, {'냔', 0x66}, {'냘', 0x67}, {'냠', 0x68}, {'냥', 0x69}, {'너', 0x6A}, {'넉', 0x6B}, {'넋', 0x6C}, {'넌', 0x6D}, {'널', 0x6E}, {'넒', 0x6F}, + {'넓', 0x70}, {'넘', 0x71}, {'넙', 0x72}, {'넛', 0x73}, {'넜', 0x74}, {'넝', 0x75}, {'넣', 0x76}, {'네', 0x77}, {'넥', 0x78}, {'넨', 0x79}, {'넬', 0x7A}, {'넴', 0x7B}, {'넵', 0x7C}, {'넷', 0x7D}, {'넸', 0x7E}, {'넹', 0x7F}, + {'녀', 0x80}, {'녁', 0x81}, {'년', 0x82}, {'녈', 0x83}, {'념', 0x84}, {'녑', 0x85}, {'녔', 0x86}, {'녕', 0x87}, {'녘', 0x88}, {'녜', 0x89}, {'녠', 0x8A}, {'노', 0x8B}, {'녹', 0x8C}, {'논', 0x8D}, {'놀', 0x8E}, {'놂', 0x8F}, + {'놈', 0x90}, {'놉', 0x91}, {'놋', 0x92}, {'농', 0x93}, {'높', 0x94}, {'놓', 0x95}, {'놔', 0x96}, {'놘', 0x97}, {'놜', 0x98}, {'놨', 0x99}, {'뇌', 0x9A}, {'뇐', 0x9B}, {'뇔', 0x9C}, {'뇜', 0x9D}, {'뇝', 0x9E}, + {'뇟', 0xA1}, {'뇨', 0xA2}, {'뇩', 0xA3}, {'뇬', 0xA4}, {'뇰', 0xA5}, {'뇹', 0xA6}, {'뇻', 0xA7}, {'뇽', 0xA8}, {'누', 0xA9}, {'눅', 0xAA}, {'눈', 0xAB}, {'눋', 0xAC}, {'눌', 0xAD}, {'눔', 0xAE}, {'눕', 0xAF}, + {'눗', 0xB0}, {'눙', 0xB1}, {'눠', 0xB2}, {'눴', 0xB3}, {'눼', 0xB4}, {'뉘', 0xB5}, {'뉜', 0xB6}, {'뉠', 0xB7}, {'뉨', 0xB8}, {'뉩', 0xB9}, {'뉴', 0xBA}, {'뉵', 0xBB}, {'뉼', 0xBC}, {'늄', 0xBD}, {'늅', 0xBE}, {'늉', 0xBF}, + {'느', 0xC0}, {'늑', 0xC1}, {'는', 0xC2}, {'늘', 0xC3}, {'늙', 0xC4}, {'늚', 0xC5}, {'늠', 0xC6}, {'늡', 0xC7}, {'늣', 0xC8}, {'능', 0xC9}, {'늦', 0xCA}, {'늪', 0xCB}, {'늬', 0xCC}, {'늰', 0xCD}, {'늴', 0xCE}, {'니', 0xCF}, + {'닉', 0xD0}, {'닌', 0xD1}, {'닐', 0xD2}, {'닒', 0xD3}, {'님', 0xD4}, {'닙', 0xD5}, {'닛', 0xD6}, {'닝', 0xD7}, {'닢', 0xD8}, {'다', 0xD9}, {'닥', 0xDA}, {'닦', 0xDB}, {'단', 0xDC}, {'닫', 0xDD}, {'달', 0xDE}, {'닭', 0xDF}, + {'닮', 0xE0}, {'닯', 0xE1}, {'닳', 0xE2}, {'담', 0xE3}, {'답', 0xE4}, {'닷', 0xE5}, {'닸', 0xE6}, {'당', 0xE7}, {'닺', 0xE8}, {'닻', 0xE9}, {'닿', 0xEA}, {'대', 0xEB}, {'댁', 0xEC}, {'댄', 0xED}, {'댈', 0xEE}, {'댐', 0xEF}, + {'댑', 0xF0}, {'댓', 0xF1}, {'댔', 0xF2}, {'댕', 0xF3}, {'더', 0xF5}, {'덕', 0xF6}, {'덖', 0xF7}, {'던', 0xF8}, {'덛', 0xF9}, {'덜', 0xFA}, {'덞', 0xFB}, {'덟', 0xFC}, {'덤', 0xFD}, {'덥', 0xFE}, + }; + + private static readonly Dictionary U2GSC_KOR_3 = new() + { + {'덧', 0x01}, {'덩', 0x02}, {'덫', 0x03}, {'덮', 0x04}, {'데', 0x05}, {'덱', 0x06}, {'덴', 0x07}, {'델', 0x08}, {'뎀', 0x09}, {'뎁', 0x0A}, {'뎃', 0x0B}, {'뎄', 0x0C}, {'뎅', 0x0D}, {'뎌', 0x0E}, {'뎐', 0x0F}, + {'뎔', 0x10}, {'뎠', 0x11}, {'뎡', 0x12}, {'뎨', 0x13}, {'뎬', 0x14}, {'도', 0x15}, {'독', 0x16}, {'돈', 0x17}, {'돋', 0x18}, {'돌', 0x19}, {'돎', 0x1A}, {'돔', 0x1C}, {'돕', 0x1D}, {'돗', 0x1E}, {'동', 0x1F}, + {'돛', 0x20}, {'돝', 0x21}, {'돠', 0x22}, {'돤', 0x23}, {'돨', 0x24}, {'돼', 0x25}, {'됐', 0x26}, {'되', 0x27}, {'된', 0x28}, {'될', 0x29}, {'됨', 0x2A}, {'됩', 0x2B}, {'됫', 0x2C}, {'됴', 0x2D}, {'두', 0x2E}, {'둑', 0x2F}, + {'둔', 0x30}, {'둘', 0x31}, {'둠', 0x32}, {'둡', 0x33}, {'둣', 0x34}, {'둥', 0x35}, {'둬', 0x36}, {'뒀', 0x37}, {'뒈', 0x38}, {'뒝', 0x39}, {'뒤', 0x3A}, {'뒨', 0x3B}, {'뒬', 0x3C}, {'뒵', 0x3D}, {'뒷', 0x3E}, {'뒹', 0x3F}, + {'듀', 0x40}, {'듄', 0x41}, {'듈', 0x42}, {'듐', 0x43}, {'듕', 0x44}, {'드', 0x45}, {'득', 0x46}, {'든', 0x47}, {'듣', 0x48}, {'들', 0x49}, {'듦', 0x4A}, {'듬', 0x4B}, {'듭', 0x4C}, {'듯', 0x4D}, {'등', 0x4E}, {'듸', 0x4F}, + {'디', 0x60}, {'딕', 0x61}, {'딘', 0x62}, {'딛', 0x63}, {'딜', 0x64}, {'딤', 0x65}, {'딥', 0x66}, {'딧', 0x67}, {'딨', 0x68}, {'딩', 0x69}, {'딪', 0x6A}, {'따', 0x6B}, {'딱', 0x6C}, {'딴', 0x6D}, {'딸', 0x6E}, + {'땀', 0x71}, {'땁', 0x72}, {'땃', 0x73}, {'땄', 0x74}, {'땅', 0x75}, {'땋', 0x76}, {'때', 0x77}, {'땍', 0x78}, {'땐', 0x79}, {'땔', 0x7A}, {'땜', 0x7B}, {'땝', 0x7C}, {'땟', 0x7D}, {'땠', 0x7E}, {'땡', 0x7F}, + {'떠', 0x80}, {'떡', 0x81}, {'떤', 0x82}, {'떨', 0x83}, {'떪', 0x84}, {'떫', 0x85}, {'떰', 0x86}, {'떱', 0x87}, {'떳', 0x88}, {'떴', 0x89}, {'떵', 0x8A}, {'떻', 0x8B}, {'떼', 0x8C}, {'떽', 0x8D}, {'뗀', 0x8E}, {'뗄', 0x8F}, + {'뗌', 0x90}, {'뗍', 0x91}, {'뗏', 0x92}, {'뗐', 0x93}, {'뗑', 0x94}, {'뗘', 0x95}, {'뗬', 0x96}, {'또', 0x97}, {'똑', 0x98}, {'똔', 0x99}, {'똘', 0x9A}, {'똥', 0x9B}, {'똬', 0x9C}, {'똴', 0x9D}, {'뙈', 0x9E}, {'뙤', 0x9F}, + {'뙨', 0xA0}, {'뚜', 0xA1}, {'뚝', 0xA2}, {'뚠', 0xA3}, {'뚤', 0xA4}, {'뚫', 0xA5}, {'뚬', 0xA6}, {'뚱', 0xA7}, {'뛔', 0xA8}, {'뛰', 0xA9}, {'뛴', 0xAA}, {'뛸', 0xAB}, {'뜀', 0xAC}, {'뜁', 0xAD}, {'뜅', 0xAE}, {'뜨', 0xAF}, + {'뜩', 0xB0}, {'뜬', 0xB1}, {'뜯', 0xB2}, {'뜰', 0xB3}, {'뜸', 0xB4}, {'뜹', 0xB5}, {'뜻', 0xB6}, {'띄', 0xB7}, {'띈', 0xB8}, {'띌', 0xB9}, {'띔', 0xBA}, {'띕', 0xBB}, {'띠', 0xBC}, {'띤', 0xBD}, {'띨', 0xBE}, {'띰', 0xBF}, + {'띱', 0xC0}, {'띳', 0xC1}, {'띵', 0xC2}, {'라', 0xC3}, {'락', 0xC4}, {'란', 0xC5}, {'랄', 0xC6}, {'람', 0xC7}, {'랍', 0xC8}, {'랏', 0xC9}, {'랐', 0xCA}, {'랑', 0xCB}, {'랒', 0xCC}, {'랖', 0xCD}, {'랗', 0xCE}, + {'뢔', 0xD0}, {'래', 0xD1}, {'랙', 0xD2}, {'랜', 0xD3}, {'랠', 0xD4}, {'램', 0xD5}, {'랩', 0xD6}, {'랫', 0xD7}, {'랬', 0xD8}, {'랭', 0xD9}, {'랴', 0xDA}, {'략', 0xDB}, {'랸', 0xDC}, {'럇', 0xDD}, {'량', 0xDE}, {'러', 0xDF}, + {'럭', 0xE0}, {'런', 0xE1}, {'럴', 0xE2}, {'럼', 0xE3}, {'럽', 0xE4}, {'럿', 0xE5}, {'렀', 0xE6}, {'렁', 0xE7}, {'렇', 0xE8}, {'레', 0xE9}, {'렉', 0xEA}, {'렌', 0xEB}, {'렐', 0xEC}, {'렘', 0xED}, {'렙', 0xEE}, {'렛', 0xEF}, + {'렝', 0xF0}, {'려', 0xF1}, {'력', 0xF2}, {'련', 0xF3}, {'렬', 0xF4}, {'렴', 0xF5}, {'렵', 0xF6}, {'렷', 0xF7}, {'렸', 0xF8}, {'령', 0xF9}, {'례', 0xFA}, {'롄', 0xFB}, {'롑', 0xFC}, {'롓', 0xFD}, {'로', 0xFE}, {'록', 0xFF}, + }; + + private static readonly Dictionary U2GSC_KOR_4 = new() + { + {'론', 0x00}, {'롤', 0x01}, {'롬', 0x02}, {'롭', 0x03}, {'롯', 0x04}, {'롱', 0x05}, {'롸', 0x06}, {'롼', 0x07}, {'뢍', 0x08}, {'뢨', 0x09}, {'뢰', 0x0A}, {'뢴', 0x0B}, {'뢸', 0x0C}, {'룀', 0x0D}, {'룁', 0x0E}, {'룃', 0x0F}, + {'룅', 0x10}, {'료', 0x11}, {'룐', 0x12}, {'룔', 0x13}, {'룝', 0x14}, {'룟', 0x15}, {'룡', 0x16}, {'루', 0x17}, {'룩', 0x18}, {'룬', 0x19}, {'룰', 0x1A}, {'룸', 0x1B}, {'룹', 0x1C}, {'룻', 0x1D}, {'룽', 0x1E}, {'뤄', 0x1F}, + {'뤘', 0x20}, {'뤠', 0x21}, {'뤼', 0x22}, {'뤽', 0x23}, {'륀', 0x24}, {'륄', 0x25}, {'륌', 0x26}, {'륏', 0x27}, {'륑', 0x28}, {'류', 0x29}, {'륙', 0x2A}, {'륜', 0x2B}, {'률', 0x2C}, {'륨', 0x2D}, {'륩', 0x2E}, + {'륫', 0x31}, {'륭', 0x32}, {'르', 0x33}, {'륵', 0x34}, {'른', 0x35}, {'를', 0x36}, {'름', 0x37}, {'릅', 0x38}, {'릇', 0x39}, {'릉', 0x3A}, {'릊', 0x3B}, {'릍', 0x3C}, {'릎', 0x3D}, {'리', 0x3E}, {'릭', 0x3F}, + {'린', 0x40}, {'릴', 0x41}, {'림', 0x42}, {'립', 0x43}, {'릿', 0x44}, {'링', 0x45}, {'마', 0x46}, {'막', 0x47}, {'만', 0x48}, {'많', 0x49}, {'맏', 0x4A}, {'말', 0x4B}, {'맑', 0x4C}, {'맒', 0x4D}, {'맘', 0x4E}, {'맙', 0x4F}, + {'맛', 0x60}, {'망', 0x61}, {'맞', 0x62}, {'맡', 0x63}, {'맣', 0x64}, {'매', 0x65}, {'맥', 0x66}, {'맨', 0x67}, {'맬', 0x68}, {'맴', 0x69}, {'맵', 0x6A}, {'맷', 0x6B}, {'맸', 0x6C}, {'맹', 0x6D}, {'맺', 0x6E}, {'먀', 0x6F}, + {'먁', 0x70}, {'먈', 0x71}, {'먕', 0x72}, {'머', 0x73}, {'먹', 0x74}, {'먼', 0x75}, {'멀', 0x76}, {'멂', 0x77}, {'멈', 0x78}, {'멉', 0x79}, {'멋', 0x7A}, {'멍', 0x7B}, {'멎', 0x7C}, {'멓', 0x7D}, {'메', 0x7E}, {'멕', 0x7F}, + {'멘', 0x80}, {'멜', 0x81}, {'멤', 0x82}, {'멥', 0x83}, {'멧', 0x84}, {'멨', 0x85}, {'멩', 0x86}, {'며', 0x87}, {'멱', 0x88}, {'면', 0x89}, {'멸', 0x8A}, {'몃', 0x8B}, {'몄', 0x8C}, {'명', 0x8D}, {'몇', 0x8E}, {'몌', 0x8F}, + {'모', 0x90}, {'목', 0x91}, {'몫', 0x92}, {'몬', 0x93}, {'몰', 0x94}, {'몲', 0x95}, {'몸', 0x96}, {'몹', 0x97}, {'못', 0x98}, {'몽', 0x99}, {'뫄', 0x9A}, {'뫈', 0x9B}, {'뫘', 0x9C}, {'뫙', 0x9D}, {'뫼', 0x9E}, + {'묀', 0xA1}, {'묄', 0xA2}, {'묍', 0xA3}, {'묏', 0xA4}, {'묑', 0xA5}, {'묘', 0xA6}, {'묜', 0xA7}, {'묠', 0xA8}, {'묩', 0xA9}, {'묫', 0xAA}, {'무', 0xAB}, {'묵', 0xAC}, {'묶', 0xAD}, {'문', 0xAE}, {'묻', 0xAF}, + {'물', 0xB0}, {'묽', 0xB1}, {'묾', 0xB2}, {'뭄', 0xB3}, {'뭅', 0xB4}, {'뭇', 0xB5}, {'뭉', 0xB6}, {'뭍', 0xB7}, {'뭏', 0xB8}, {'뭐', 0xB9}, {'뭔', 0xBA}, {'뭘', 0xBB}, {'뭡', 0xBC}, {'뭣', 0xBD}, {'뭬', 0xBE}, {'뮈', 0xBF}, + {'뮌', 0xC0}, {'뮐', 0xC1}, {'뮤', 0xC2}, {'뮨', 0xC3}, {'뮬', 0xC4}, {'뮴', 0xC5}, {'뮷', 0xC6}, {'므', 0xC7}, {'믄', 0xC8}, {'믈', 0xC9}, {'믐', 0xCA}, {'믓', 0xCB}, {'미', 0xCC}, {'믹', 0xCD}, {'민', 0xCE}, {'믿', 0xCF}, + {'밀', 0xD0}, {'밂', 0xD1}, {'밈', 0xD2}, {'밉', 0xD3}, {'밋', 0xD4}, {'밌', 0xD5}, {'밍', 0xD6}, {'및', 0xD7}, {'밑', 0xD8}, {'바', 0xD9}, {'박', 0xDA}, {'밖', 0xDB}, {'밗', 0xDC}, {'반', 0xDD}, {'받', 0xDE}, {'발', 0xDF}, + {'밝', 0xE0}, {'밞', 0xE1}, {'밟', 0xE2}, {'밤', 0xE3}, {'밥', 0xE4}, {'밧', 0xE5}, {'방', 0xE6}, {'밭', 0xE7}, {'배', 0xE8}, {'백', 0xE9}, {'밴', 0xEA}, {'밸', 0xEB}, {'뱀', 0xEC}, {'뱁', 0xED}, {'뱃', 0xEE}, {'뱄', 0xEF}, + {'뱅', 0xF0}, {'뱉', 0xF1}, {'뱌', 0xF2}, {'뱍', 0xF3}, {'뱐', 0xF4}, {'뱝', 0xF5}, {'버', 0xF6}, {'벅', 0xF7}, {'번', 0xF8}, {'벋', 0xF9}, {'벌', 0xFA}, {'벎', 0xFB}, {'범', 0xFC}, {'법', 0xFD}, {'벗', 0xFE}, + }; + + private static readonly Dictionary U2GSC_KOR_5 = new() + { + {'벙', 0x01}, {'벚', 0x02}, {'베', 0x03}, {'벡', 0x04}, {'벤', 0x05}, {'벧', 0x06}, {'벨', 0x07}, {'벰', 0x08}, {'벱', 0x09}, {'벳', 0x0A}, {'벴', 0x0B}, {'벵', 0x0C}, {'벼', 0x0D}, {'벽', 0x0E}, {'변', 0x0F}, + {'별', 0x10}, {'볍', 0x11}, {'볏', 0x12}, {'볐', 0x13}, {'병', 0x14}, {'볕', 0x15}, {'볘', 0x16}, {'볜', 0x17}, {'보', 0x18}, {'복', 0x19}, {'볶', 0x1A}, {'본', 0x1B}, {'볼', 0x1C}, {'봄', 0x1D}, {'봅', 0x1E}, {'봇', 0x1F}, + {'봉', 0x20}, {'봐', 0x21}, {'봔', 0x22}, {'봤', 0x23}, {'봬', 0x24}, {'뵀', 0x25}, {'뵈', 0x26}, {'뵉', 0x27}, {'뵌', 0x28}, {'뵐', 0x29}, {'뵘', 0x2A}, {'뵙', 0x2B}, {'뵤', 0x2C}, {'뵨', 0x2D}, {'부', 0x2E}, {'북', 0x2F}, + {'분', 0x30}, {'붇', 0x31}, {'불', 0x32}, {'붉', 0x33}, {'붊', 0x34}, {'붐', 0x35}, {'붑', 0x36}, {'붓', 0x37}, {'붕', 0x38}, {'붙', 0x39}, {'붚', 0x3A}, {'붜', 0x3B}, {'붤', 0x3C}, {'붰', 0x3D}, {'붸', 0x3E}, {'뷔', 0x3F}, + {'뷕', 0x40}, {'뷘', 0x41}, {'뷜', 0x42}, {'뷩', 0x43}, {'뷰', 0x44}, {'뷴', 0x45}, {'뷸', 0x46}, {'븀', 0x47}, {'븃', 0x48}, {'븅', 0x49}, {'브', 0x4A}, {'븍', 0x4B}, {'븐', 0x4C}, {'블', 0x4D}, {'븜', 0x4E}, {'븝', 0x4F}, + {'븟', 0x60}, {'비', 0x61}, {'빅', 0x62}, {'빈', 0x63}, {'빌', 0x64}, {'빎', 0x65}, {'빔', 0x66}, {'빕', 0x67}, {'빗', 0x68}, {'빙', 0x69}, {'빚', 0x6A}, {'빛', 0x6B}, {'빠', 0x6C}, {'빡', 0x6D}, {'빤', 0x6E}, + {'빨', 0x71}, {'빪', 0x72}, {'빰', 0x73}, {'빱', 0x74}, {'빳', 0x75}, {'빴', 0x76}, {'빵', 0x77}, {'빻', 0x78}, {'빼', 0x79}, {'빽', 0x7A}, {'뺀', 0x7B}, {'뺄', 0x7C}, {'뺌', 0x7D}, {'뺍', 0x7E}, {'뺏', 0x7F}, + {'뺐', 0x80}, {'뺑', 0x81}, {'뺘', 0x82}, {'뺙', 0x83}, {'뺨', 0x84}, {'뻐', 0x85}, {'뻑', 0x86}, {'뻔', 0x87}, {'뻗', 0x88}, {'뻘', 0x89}, {'뻠', 0x8A}, {'뻣', 0x8B}, {'뻤', 0x8C}, {'뻥', 0x8D}, {'뻬', 0x8E}, {'뼁', 0x8F}, + {'뼈', 0x90}, {'뼉', 0x91}, {'뼘', 0x92}, {'뼙', 0x93}, {'뼛', 0x94}, {'뼜', 0x95}, {'뼝', 0x96}, {'뽀', 0x97}, {'뽁', 0x98}, {'뽄', 0x99}, {'뽈', 0x9A}, {'뽐', 0x9B}, {'뽑', 0x9C}, {'뽕', 0x9D}, {'뾔', 0x9E}, {'뾰', 0x9F}, + {'뿅', 0xA0}, {'뿌', 0xA1}, {'뿍', 0xA2}, {'뿐', 0xA3}, {'뿔', 0xA4}, {'뿜', 0xA5}, {'뿟', 0xA6}, {'뿡', 0xA7}, {'쀼', 0xA8}, {'쁑', 0xA9}, {'쁘', 0xAA}, {'쁜', 0xAB}, {'쁠', 0xAC}, {'쁨', 0xAD}, {'쁩', 0xAE}, {'삐', 0xAF}, + {'삑', 0xB0}, {'삔', 0xB1}, {'삘', 0xB2}, {'삠', 0xB3}, {'삡', 0xB4}, {'삣', 0xB5}, {'삥', 0xB6}, {'사', 0xB7}, {'삭', 0xB8}, {'삯', 0xB9}, {'산', 0xBA}, {'삳', 0xBB}, {'살', 0xBC}, {'삵', 0xBD}, {'삶', 0xBE}, {'삼', 0xBF}, + {'삽', 0xC0}, {'삿', 0xC1}, {'샀', 0xC2}, {'상', 0xC3}, {'샅', 0xC4}, {'새', 0xC5}, {'색', 0xC6}, {'샌', 0xC7}, {'샐', 0xC8}, {'샘', 0xC9}, {'샙', 0xCA}, {'샛', 0xCB}, {'샜', 0xCC}, {'생', 0xCD}, {'샤', 0xCE}, + {'샥', 0xD1}, {'샨', 0xD2}, {'샬', 0xD3}, {'샴', 0xD4}, {'샵', 0xD5}, {'샷', 0xD6}, {'샹', 0xD7}, {'섀', 0xD8}, {'섄', 0xD9}, {'섈', 0xDA}, {'섐', 0xDB}, {'섕', 0xDC}, {'서', 0xDD}, {'석', 0xDE}, {'섞', 0xDF}, + {'섟', 0xE0}, {'선', 0xE1}, {'섣', 0xE2}, {'설', 0xE3}, {'섦', 0xE4}, {'섧', 0xE5}, {'섬', 0xE6}, {'섭', 0xE7}, {'섯', 0xE8}, {'섰', 0xE9}, {'성', 0xEA}, {'섶', 0xEB}, {'세', 0xEC}, {'섹', 0xED}, {'센', 0xEE}, {'셀', 0xEF}, + {'셈', 0xF0}, {'셉', 0xF1}, {'셋', 0xF2}, {'셌', 0xF3}, {'셍', 0xF4}, {'셔', 0xF5}, {'셕', 0xF6}, {'션', 0xF7}, {'셜', 0xF8}, {'셤', 0xF9}, {'셥', 0xFA}, {'셧', 0xFB}, {'셨', 0xFC}, {'셩', 0xFD}, {'셰', 0xFE}, {'셴', 0xFF}, + }; + + private static readonly Dictionary U2GSC_KOR_6 = new() + { + {'셸', 0x00}, {'솅', 0x01}, {'소', 0x02}, {'속', 0x03}, {'솎', 0x04}, {'손', 0x05}, {'솔', 0x06}, {'솖', 0x07}, {'솜', 0x08}, {'솝', 0x09}, {'솟', 0x0A}, {'송', 0x0B}, {'솥', 0x0C}, {'솨', 0x0D}, {'솩', 0x0E}, {'솬', 0x0F}, + {'솰', 0x10}, {'솽', 0x11}, {'쇄', 0x12}, {'쇈', 0x13}, {'쇌', 0x14}, {'쇔', 0x15}, {'쇗', 0x16}, {'쇘', 0x17}, {'쇠', 0x18}, {'쇤', 0x19}, {'쇨', 0x1A}, {'쇰', 0x1B}, {'쇱', 0x1C}, {'쇳', 0x1D}, {'쇼', 0x1E}, {'쇽', 0x1F}, + {'숀', 0x20}, {'숄', 0x21}, {'숌', 0x22}, {'숍', 0x23}, {'숏', 0x24}, {'숑', 0x25}, {'수', 0x26}, {'숙', 0x27}, {'순', 0x28}, {'숟', 0x29}, {'술', 0x2A}, {'숨', 0x2B}, {'숩', 0x2C}, {'숫', 0x2D}, {'숭', 0x2E}, {'쌰', 0x2F}, + {'쎼', 0x30}, {'숯', 0x31}, {'숱', 0x32}, {'숲', 0x33}, {'숴', 0x34}, {'쉈', 0x35}, {'쉐', 0x36}, {'쉑', 0x37}, {'쉔', 0x38}, {'쉘', 0x39}, {'쉠', 0x3A}, {'쉥', 0x3B}, {'쉬', 0x3C}, {'쉭', 0x3D}, {'쉰', 0x3E}, {'쉴', 0x3F}, + {'쉼', 0x40}, {'쉽', 0x41}, {'쉿', 0x42}, {'슁', 0x43}, {'슈', 0x44}, {'슉', 0x45}, {'슐', 0x46}, {'슘', 0x47}, {'슛', 0x48}, {'슝', 0x49}, {'스', 0x4A}, {'슥', 0x4B}, {'슨', 0x4C}, {'슬', 0x4D}, {'슭', 0x4E}, {'슴', 0x4F}, + {'습', 0x60}, {'슷', 0x61}, {'승', 0x62}, {'시', 0x63}, {'식', 0x64}, {'신', 0x65}, {'싣', 0x66}, {'실', 0x67}, {'싫', 0x68}, {'심', 0x69}, {'십', 0x6A}, {'싯', 0x6B}, {'싱', 0x6C}, {'싶', 0x6D}, {'싸', 0x6E}, {'싹', 0x6F}, + {'싻', 0x70}, {'싼', 0x71}, {'쌀', 0x72}, {'쌈', 0x73}, {'쌉', 0x74}, {'쌌', 0x75}, {'쌍', 0x76}, {'쌓', 0x77}, {'쌔', 0x78}, {'쌕', 0x79}, {'쌘', 0x7A}, {'쌜', 0x7B}, {'쌤', 0x7C}, {'쌥', 0x7D}, {'쌨', 0x7E}, {'쌩', 0x7F}, + {'썅', 0x80}, {'써', 0x81}, {'썩', 0x82}, {'썬', 0x83}, {'썰', 0x84}, {'썲', 0x85}, {'썸', 0x86}, {'썹', 0x87}, {'썼', 0x88}, {'썽', 0x89}, {'쎄', 0x8A}, {'쎈', 0x8B}, {'쎌', 0x8C}, {'쏀', 0x8D}, {'쏘', 0x8E}, {'쏙', 0x8F}, + {'쏜', 0x90}, {'쏟', 0x91}, {'쏠', 0x92}, {'쏢', 0x93}, {'쏨', 0x94}, {'쏩', 0x95}, {'쏭', 0x96}, {'쏴', 0x97}, {'쏵', 0x98}, {'쏸', 0x99}, {'쐈', 0x9A}, {'쐐', 0x9B}, {'쐤', 0x9C}, {'쐬', 0x9D}, {'쐰', 0x9E}, + {'쓔', 0xA0}, {'쐴', 0xA1}, {'쐼', 0xA2}, {'쐽', 0xA3}, {'쑈', 0xA4}, {'쑤', 0xA5}, {'쑥', 0xA6}, {'쑨', 0xA7}, {'쑬', 0xA8}, {'쑴', 0xA9}, {'쑵', 0xAA}, {'쑹', 0xAB}, {'쒀', 0xAC}, {'쒔', 0xAD}, {'쒜', 0xAE}, {'쒸', 0xAF}, + {'쒼', 0xB0}, {'쓩', 0xB1}, {'쓰', 0xB2}, {'쓱', 0xB3}, {'쓴', 0xB4}, {'쓸', 0xB5}, {'쓺', 0xB6}, {'쓿', 0xB7}, {'씀', 0xB8}, {'씁', 0xB9}, {'씌', 0xBA}, {'씐', 0xBB}, {'씔', 0xBC}, {'씜', 0xBD}, {'씨', 0xBE}, {'씩', 0xBF}, + {'씬', 0xC0}, {'씰', 0xC1}, {'씸', 0xC2}, {'씹', 0xC3}, {'씻', 0xC4}, {'씽', 0xC5}, {'아', 0xC6}, {'악', 0xC7}, {'안', 0xC8}, {'앉', 0xC9}, {'않', 0xCA}, {'알', 0xCB}, {'앍', 0xCC}, {'앎', 0xCD}, {'앓', 0xCE}, {'암', 0xCF}, + {'압', 0xD0}, {'앗', 0xD1}, {'았', 0xD2}, {'앙', 0xD3}, {'앝', 0xD4}, {'앞', 0xD5}, {'애', 0xD6}, {'액', 0xD7}, {'앤', 0xD8}, {'앨', 0xD9}, {'앰', 0xDA}, {'앱', 0xDB}, {'앳', 0xDC}, {'앴', 0xDD}, {'앵', 0xDE}, {'야', 0xDF}, + {'약', 0xE0}, {'얀', 0xE1}, {'얄', 0xE2}, {'얇', 0xE3}, {'얌', 0xE4}, {'얍', 0xE5}, {'얏', 0xE6}, {'양', 0xE7}, {'얕', 0xE8}, {'얗', 0xE9}, {'얘', 0xEA}, {'얜', 0xEB}, {'얠', 0xEC}, {'얩', 0xED}, {'어', 0xEE}, {'억', 0xEF}, + {'언', 0xF0}, {'얹', 0xF1}, {'얻', 0xF2}, {'얼', 0xF3}, {'얽', 0xF4}, {'얾', 0xF5}, {'엄', 0xF6}, {'업', 0xF7}, {'없', 0xF8}, {'엇', 0xF9}, {'었', 0xFA}, {'엉', 0xFB}, {'엊', 0xFC}, {'엌', 0xFD}, {'엎', 0xFE}, + }; + + private static readonly Dictionary U2GSC_KOR_7 = new() + { + {'에', 0x01}, {'엑', 0x02}, {'엔', 0x03}, {'엘', 0x04}, {'엠', 0x05}, {'엡', 0x06}, {'엣', 0x07}, {'엥', 0x08}, {'여', 0x09}, {'역', 0x0A}, {'엮', 0x0B}, {'연', 0x0C}, {'열', 0x0D}, {'엶', 0x0E}, {'엷', 0x0F}, + {'염', 0x10}, {'엽', 0x11}, {'엾', 0x12}, {'엿', 0x13}, {'였', 0x14}, {'영', 0x15}, {'옅', 0x16}, {'옆', 0x17}, {'옇', 0x18}, {'예', 0x19}, {'옌', 0x1A}, {'옐', 0x1B}, {'옘', 0x1C}, {'옙', 0x1D}, {'옛', 0x1E}, {'옜', 0x1F}, + {'오', 0x20}, {'옥', 0x21}, {'온', 0x22}, {'올', 0x23}, {'옭', 0x24}, {'옮', 0x25}, {'옰', 0x26}, {'옳', 0x27}, {'옴', 0x28}, {'옵', 0x29}, {'옷', 0x2A}, {'옹', 0x2B}, {'옻', 0x2C}, {'와', 0x2D}, {'왁', 0x2E}, {'완', 0x2F}, + {'왈', 0x30}, {'왐', 0x31}, {'왑', 0x32}, {'왓', 0x33}, {'왔', 0x34}, {'왕', 0x35}, {'왜', 0x36}, {'왝', 0x37}, {'왠', 0x38}, {'왬', 0x39}, {'왯', 0x3A}, {'왱', 0x3B}, {'외', 0x3C}, {'왹', 0x3D}, {'왼', 0x3E}, {'욀', 0x3F}, + {'욈', 0x40}, {'욉', 0x41}, {'욋', 0x42}, {'욍', 0x43}, {'요', 0x44}, {'욕', 0x45}, {'욘', 0x46}, {'욜', 0x47}, {'욤', 0x48}, {'욥', 0x49}, {'욧', 0x4A}, {'용', 0x4B}, {'우', 0x4C}, {'욱', 0x4D}, {'운', 0x4E}, {'울', 0x4F}, + {'욹', 0x60}, {'욺', 0x61}, {'움', 0x62}, {'웁', 0x63}, {'웃', 0x64}, {'웅', 0x65}, {'워', 0x66}, {'웍', 0x67}, {'원', 0x68}, {'월', 0x69}, {'웜', 0x6A}, {'웝', 0x6B}, {'웠', 0x6C}, {'웡', 0x6D}, {'웨', 0x6E}, + {'웩', 0x71}, {'웬', 0x72}, {'웰', 0x73}, {'웸', 0x74}, {'웹', 0x75}, {'웽', 0x76}, {'위', 0x77}, {'윅', 0x78}, {'윈', 0x79}, {'윌', 0x7A}, {'윔', 0x7B}, {'윕', 0x7C}, {'윗', 0x7D}, {'윙', 0x7E}, {'유', 0x7F}, + {'육', 0x80}, {'윤', 0x81}, {'율', 0x82}, {'윰', 0x83}, {'윱', 0x84}, {'윳', 0x85}, {'융', 0x86}, {'윷', 0x87}, {'으', 0x88}, {'윽', 0x89}, {'은', 0x8A}, {'을', 0x8B}, {'읆', 0x8C}, {'음', 0x8D}, {'읍', 0x8E}, {'읏', 0x8F}, + {'응', 0x90}, {'읒', 0x91}, {'읓', 0x92}, {'읔', 0x93}, {'읕', 0x94}, {'읖', 0x95}, {'읗', 0x96}, {'의', 0x97}, {'읜', 0x98}, {'읠', 0x99}, {'읨', 0x9A}, {'읫', 0x9B}, {'이', 0x9C}, {'익', 0x9D}, {'인', 0x9E}, {'일', 0x9F}, + {'읽', 0xA0}, {'읾', 0xA1}, {'잃', 0xA2}, {'임', 0xA3}, {'입', 0xA4}, {'잇', 0xA5}, {'있', 0xA6}, {'잉', 0xA7}, {'잊', 0xA8}, {'잎', 0xA9}, {'자', 0xAA}, {'작', 0xAB}, {'잔', 0xAC}, {'잖', 0xAD}, {'잗', 0xAE}, {'잘', 0xAF}, + {'잚', 0xB0}, {'잠', 0xB1}, {'잡', 0xB2}, {'잣', 0xB3}, {'잤', 0xB4}, {'장', 0xB5}, {'잦', 0xB6}, {'재', 0xB7}, {'잭', 0xB8}, {'잰', 0xB9}, {'잴', 0xBA}, {'잼', 0xBB}, {'잽', 0xBC}, {'잿', 0xBD}, {'쟀', 0xBE}, {'쟁', 0xBF}, + {'쟈', 0xC0}, {'쟉', 0xC1}, {'쟌', 0xC2}, {'쟎', 0xC3}, {'쟐', 0xC4}, {'쟘', 0xC5}, {'쟝', 0xC6}, {'쟤', 0xC7}, {'쟨', 0xC8}, {'쟬', 0xC9}, {'저', 0xCA}, {'적', 0xCB}, {'전', 0xCC}, {'절', 0xCD}, {'젊', 0xCE}, + {'점', 0xD1}, {'접', 0xD2}, {'젓', 0xD3}, {'정', 0xD4}, {'젖', 0xD5}, {'제', 0xD6}, {'젝', 0xD7}, {'젠', 0xD8}, {'젤', 0xD9}, {'젬', 0xDA}, {'젭', 0xDB}, {'젯', 0xDC}, {'젱', 0xDD}, {'져', 0xDE}, {'젼', 0xDF}, + {'졀', 0xE0}, {'졈', 0xE1}, {'졉', 0xE2}, {'졌', 0xE3}, {'졍', 0xE4}, {'졔', 0xE5}, {'조', 0xE6}, {'족', 0xE7}, {'존', 0xE8}, {'졸', 0xE9}, {'졺', 0xEA}, {'좀', 0xEB}, {'좁', 0xEC}, {'좃', 0xED}, {'종', 0xEE}, {'좆', 0xEF}, + {'좇', 0xF0}, {'좋', 0xF1}, {'좌', 0xF2}, {'좍', 0xF3}, {'좔', 0xF4}, {'좝', 0xF5}, {'좟', 0xF6}, {'좡', 0xF7}, {'좨', 0xF8}, {'좼', 0xF9}, {'좽', 0xFA}, {'죄', 0xFB}, {'죈', 0xFC}, {'죌', 0xFD}, {'죔', 0xFE}, {'죕', 0xFF}, + }; + + private static readonly Dictionary U2GSC_KOR_8 = new() + { + {'죗', 0x00}, {'죙', 0x01}, {'죠', 0x02}, {'죡', 0x03}, {'죤', 0x04}, {'죵', 0x05}, {'주', 0x06}, {'죽', 0x07}, {'준', 0x08}, {'줄', 0x09}, {'줅', 0x0A}, {'줆', 0x0B}, {'줌', 0x0C}, {'줍', 0x0D}, {'줏', 0x0E}, {'중', 0x0F}, + {'줘', 0x10}, {'줬', 0x11}, {'줴', 0x12}, {'쥐', 0x13}, {'쥑', 0x14}, {'쥔', 0x15}, {'쥘', 0x16}, {'쥠', 0x17}, {'쥡', 0x18}, {'쥣', 0x19}, {'쥬', 0x1A}, {'쥰', 0x1B}, {'쥴', 0x1C}, {'쥼', 0x1D}, {'즈', 0x1E}, {'즉', 0x1F}, + {'즌', 0x20}, {'즐', 0x21}, {'즘', 0x22}, {'즙', 0x23}, {'즛', 0x24}, {'증', 0x25}, {'지', 0x26}, {'직', 0x27}, {'진', 0x28}, {'짇', 0x29}, {'질', 0x2A}, {'짊', 0x2B}, {'짐', 0x2C}, {'집', 0x2D}, {'짓', 0x2E}, + {'쬬', 0x30}, {'징', 0x31}, {'짖', 0x32}, {'짙', 0x33}, {'짚', 0x34}, {'짜', 0x35}, {'짝', 0x36}, {'짠', 0x37}, {'짢', 0x38}, {'짤', 0x39}, {'짧', 0x3A}, {'짬', 0x3B}, {'짭', 0x3C}, {'짯', 0x3D}, {'짰', 0x3E}, {'짱', 0x3F}, + {'째', 0x40}, {'짹', 0x41}, {'짼', 0x42}, {'쨀', 0x43}, {'쨈', 0x44}, {'쨉', 0x45}, {'쨋', 0x46}, {'쨌', 0x47}, {'쨍', 0x48}, {'쨔', 0x49}, {'쨘', 0x4A}, {'쨩', 0x4B}, {'쩌', 0x4C}, {'쩍', 0x4D}, {'쩐', 0x4E}, {'쩔', 0x4F}, + {'쩜', 0x60}, {'쩝', 0x61}, {'쩟', 0x62}, {'쩠', 0x63}, {'쩡', 0x64}, {'쩨', 0x65}, {'쩽', 0x66}, {'쪄', 0x67}, {'쪘', 0x68}, {'쪼', 0x69}, {'쪽', 0x6A}, {'쫀', 0x6B}, {'쫄', 0x6C}, {'쫌', 0x6D}, {'쫍', 0x6E}, {'쫏', 0x6F}, + {'쫑', 0x70}, {'쫓', 0x71}, {'쫘', 0x72}, {'쫙', 0x73}, {'쫠', 0x74}, {'쫬', 0x75}, {'쫴', 0x76}, {'쬈', 0x77}, {'쬐', 0x78}, {'쬔', 0x79}, {'쬘', 0x7A}, {'쬠', 0x7B}, {'쬡', 0x7C}, {'쭁', 0x7D}, {'쭈', 0x7E}, {'쭉', 0x7F}, + {'쭌', 0x80}, {'쭐', 0x81}, {'쭘', 0x82}, {'쭙', 0x83}, {'쭝', 0x84}, {'쭤', 0x85}, {'쭸', 0x86}, {'쭹', 0x87}, {'쮜', 0x88}, {'쮸', 0x89}, {'쯔', 0x8A}, {'쯤', 0x8B}, {'쯧', 0x8C}, {'쯩', 0x8D}, {'찌', 0x8E}, {'찍', 0x8F}, + {'찐', 0x90}, {'찔', 0x91}, {'찜', 0x92}, {'찝', 0x93}, {'찡', 0x94}, {'찢', 0x95}, {'찧', 0x96}, {'차', 0x97}, {'착', 0x98}, {'찬', 0x99}, {'찮', 0x9A}, {'찰', 0x9B}, {'참', 0x9C}, {'찹', 0x9D}, {'찻', 0x9E}, + {'찼', 0xA1}, {'창', 0xA2}, {'찾', 0xA3}, {'채', 0xA4}, {'책', 0xA5}, {'챈', 0xA6}, {'챌', 0xA7}, {'챔', 0xA8}, {'챕', 0xA9}, {'챗', 0xAA}, {'챘', 0xAB}, {'챙', 0xAC}, {'챠', 0xAD}, {'챤', 0xAE}, {'챦', 0xAF}, + {'챨', 0xB0}, {'챰', 0xB1}, {'챵', 0xB2}, {'처', 0xB3}, {'척', 0xB4}, {'천', 0xB5}, {'철', 0xB6}, {'첨', 0xB7}, {'첩', 0xB8}, {'첫', 0xB9}, {'첬', 0xBA}, {'청', 0xBB}, {'체', 0xBC}, {'첵', 0xBD}, {'첸', 0xBE}, {'첼', 0xBF}, + {'쳄', 0xC0}, {'쳅', 0xC1}, {'쳇', 0xC2}, {'쳉', 0xC3}, {'쳐', 0xC4}, {'쳔', 0xC5}, {'쳤', 0xC6}, {'쳬', 0xC7}, {'쳰', 0xC8}, {'촁', 0xC9}, {'초', 0xCA}, {'촉', 0xCB}, {'촌', 0xCC}, {'촐', 0xCD}, {'촘', 0xCE}, {'촙', 0xCF}, + {'촛', 0xD0}, {'총', 0xD1}, {'촤', 0xD2}, {'촨', 0xD3}, {'촬', 0xD4}, {'촹', 0xD5}, {'최', 0xD6}, {'쵠', 0xD7}, {'쵤', 0xD8}, {'쵬', 0xD9}, {'쵭', 0xDA}, {'쵯', 0xDB}, {'쵱', 0xDC}, {'쵸', 0xDD}, {'춈', 0xDE}, {'추', 0xDF}, + {'축', 0xE0}, {'춘', 0xE1}, {'출', 0xE2}, {'춤', 0xE3}, {'춥', 0xE4}, {'춧', 0xE5}, {'충', 0xE6}, {'춰', 0xE7}, {'췄', 0xE8}, {'췌', 0xE9}, {'췐', 0xEA}, {'취', 0xEB}, {'췬', 0xEC}, {'췰', 0xED}, {'췸', 0xEE}, {'췹', 0xEF}, + {'췻', 0xF0}, {'췽', 0xF1}, {'츄', 0xF2}, {'츈', 0xF3}, {'츌', 0xF4}, {'츔', 0xF5}, {'츙', 0xF6}, {'츠', 0xF7}, {'측', 0xF8}, {'츤', 0xF9}, {'츨', 0xFA}, {'츰', 0xFB}, {'츱', 0xFC}, {'츳', 0xFD}, {'층', 0xFE}, + }; + + private static readonly Dictionary U2GSC_KOR_9 = new() + { + {'치', 0x01}, {'칙', 0x02}, {'친', 0x03}, {'칟', 0x04}, {'칠', 0x05}, {'칡', 0x06}, {'침', 0x07}, {'칩', 0x08}, {'칫', 0x09}, {'칭', 0x0A}, {'카', 0x0B}, {'칵', 0x0C}, {'칸', 0x0D}, {'칼', 0x0E}, {'캄', 0x0F}, + {'캅', 0x10}, {'캇', 0x11}, {'캉', 0x12}, {'캐', 0x13}, {'캑', 0x14}, {'캔', 0x15}, {'캘', 0x16}, {'캠', 0x17}, {'캡', 0x18}, {'캣', 0x19}, {'캤', 0x1A}, {'캥', 0x1B}, {'캬', 0x1C}, {'캭', 0x1D}, {'컁', 0x1E}, {'커', 0x1F}, + {'컥', 0x20}, {'컨', 0x21}, {'컫', 0x22}, {'컬', 0x23}, {'컴', 0x24}, {'컵', 0x25}, {'컷', 0x26}, {'컸', 0x27}, {'컹', 0x28}, {'케', 0x29}, {'켁', 0x2A}, {'켄', 0x2B}, {'켈', 0x2C}, {'켐', 0x2D}, {'켑', 0x2E}, {'켓', 0x2F}, + {'켕', 0x30}, {'켜', 0x31}, {'켠', 0x32}, {'켤', 0x33}, {'켬', 0x34}, {'켭', 0x35}, {'켯', 0x36}, {'켰', 0x37}, {'켱', 0x38}, {'켸', 0x39}, {'코', 0x3A}, {'콕', 0x3B}, {'콘', 0x3C}, {'콜', 0x3D}, {'콤', 0x3E}, {'콥', 0x3F}, + {'콧', 0x40}, {'콩', 0x41}, {'콰', 0x42}, {'콱', 0x43}, {'콴', 0x44}, {'콸', 0x45}, {'쾀', 0x46}, {'쾅', 0x47}, {'쾌', 0x48}, {'쾡', 0x49}, {'쾨', 0x4A}, {'쾰', 0x4B}, {'쿄', 0x4C}, {'쿠', 0x4D}, {'쿡', 0x4E}, {'쿤', 0x4F}, + {'쿨', 0x60}, {'쿰', 0x61}, {'쿱', 0x62}, {'쿳', 0x63}, {'쿵', 0x64}, {'쿼', 0x65}, {'퀀', 0x66}, {'퀄', 0x67}, {'퀑', 0x68}, {'퀘', 0x69}, {'퀭', 0x6A}, {'퀴', 0x6B}, {'퀵', 0x6C}, {'퀸', 0x6D}, {'퀼', 0x6E}, + {'큄', 0x71}, {'큅', 0x72}, {'큇', 0x73}, {'큉', 0x74}, {'큐', 0x75}, {'큔', 0x76}, {'큘', 0x77}, {'큠', 0x78}, {'크', 0x79}, {'큭', 0x7A}, {'큰', 0x7B}, {'클', 0x7C}, {'큼', 0x7D}, {'큽', 0x7E}, {'킁', 0x7F}, + {'키', 0x80}, {'킥', 0x81}, {'킨', 0x82}, {'킬', 0x83}, {'킴', 0x84}, {'킵', 0x85}, {'킷', 0x86}, {'킹', 0x87}, {'타', 0x88}, {'탁', 0x89}, {'탄', 0x8A}, {'탈', 0x8B}, {'탉', 0x8C}, {'탐', 0x8D}, {'탑', 0x8E}, {'탓', 0x8F}, + {'탔', 0x90}, {'탕', 0x91}, {'태', 0x92}, {'택', 0x93}, {'탠', 0x94}, {'탤', 0x95}, {'탬', 0x96}, {'탭', 0x97}, {'탯', 0x98}, {'탰', 0x99}, {'탱', 0x9A}, {'탸', 0x9B}, {'턍', 0x9C}, {'터', 0x9D}, {'턱', 0x9E}, {'턴', 0x9F}, + {'털', 0xA0}, {'턺', 0xA1}, {'텀', 0xA2}, {'텁', 0xA3}, {'텃', 0xA4}, {'텄', 0xA5}, {'텅', 0xA6}, {'테', 0xA7}, {'텍', 0xA8}, {'텐', 0xA9}, {'텔', 0xAA}, {'템', 0xAB}, {'텝', 0xAC}, {'텟', 0xAD}, {'텡', 0xAE}, {'텨', 0xAF}, + {'텬', 0xB0}, {'텼', 0xB1}, {'톄', 0xB2}, {'톈', 0xB3}, {'토', 0xB4}, {'톡', 0xB5}, {'톤', 0xB6}, {'톨', 0xB7}, {'톰', 0xB8}, {'톱', 0xB9}, {'톳', 0xBA}, {'통', 0xBB}, {'톺', 0xBC}, {'톼', 0xBD}, {'퇀', 0xBE}, {'퇘', 0xBF}, + {'퇴', 0xC0}, {'퇸', 0xC1}, {'툇', 0xC2}, {'툉', 0xC3}, {'툐', 0xC4}, {'투', 0xC5}, {'툭', 0xC6}, {'툰', 0xC7}, {'툴', 0xC8}, {'툼', 0xC9}, {'툽', 0xCA}, {'툿', 0xCB}, {'퉁', 0xCC}, {'퉈', 0xCD}, {'퉜', 0xCE}, + {'퉤', 0xD1}, {'튀', 0xD2}, {'튁', 0xD3}, {'튄', 0xD4}, {'튈', 0xD5}, {'튐', 0xD6}, {'튑', 0xD7}, {'튕', 0xD8}, {'튜', 0xD9}, {'튠', 0xDA}, {'튤', 0xDB}, {'튬', 0xDC}, {'튱', 0xDD}, {'트', 0xDE}, {'특', 0xDF}, + {'튼', 0xE0}, {'튿', 0xE1}, {'틀', 0xE2}, {'틂', 0xE3}, {'틈', 0xE4}, {'틉', 0xE5}, {'틋', 0xE6}, {'틔', 0xE7}, {'틘', 0xE8}, {'틜', 0xE9}, {'틤', 0xEA}, {'틥', 0xEB}, {'티', 0xEC}, {'틱', 0xED}, {'틴', 0xEE}, {'틸', 0xEF}, + {'팀', 0xF0}, {'팁', 0xF1}, {'팃', 0xF2}, {'팅', 0xF3}, {'파', 0xF4}, {'팍', 0xF5}, {'팎', 0xF6}, {'판', 0xF7}, {'팔', 0xF8}, {'팖', 0xF9}, {'팜', 0xFA}, {'팝', 0xFB}, {'팟', 0xFC}, {'팠', 0xFD}, {'팡', 0xFE}, {'팥', 0xFF}, + }; + + private static readonly Dictionary U2GSC_KOR_A = new() + { + {'패', 0x00}, {'팩', 0x01}, {'팬', 0x02}, {'팰', 0x03}, {'팸', 0x04}, {'팹', 0x05}, {'팻', 0x06}, {'팼', 0x07}, {'팽', 0x08}, {'퍄', 0x09}, {'퍅', 0x0A}, {'퍼', 0x0B}, {'퍽', 0x0C}, {'펀', 0x0D}, {'펄', 0x0E}, {'펌', 0x0F}, + {'펍', 0x10}, {'펏', 0x11}, {'펐', 0x12}, {'펑', 0x13}, {'페', 0x14}, {'펙', 0x15}, {'펜', 0x16}, {'펠', 0x17}, {'펨', 0x18}, {'펩', 0x19}, {'펫', 0x1A}, {'펭', 0x1B}, {'펴', 0x1C}, {'편', 0x1D}, {'펼', 0x1E}, {'폄', 0x1F}, + {'폅', 0x20}, {'폈', 0x21}, {'평', 0x22}, {'폐', 0x23}, {'폘', 0x24}, {'폡', 0x25}, {'폣', 0x26}, {'포', 0x27}, {'폭', 0x28}, {'폰', 0x29}, {'폴', 0x2A}, {'폼', 0x2B}, {'폽', 0x2C}, {'폿', 0x2D}, {'퐁', 0x2E}, + {'퐈', 0x31}, {'퐝', 0x32}, {'푀', 0x33}, {'푄', 0x34}, {'표', 0x35}, {'푠', 0x36}, {'푤', 0x37}, {'푭', 0x38}, {'푯', 0x39}, {'푸', 0x3A}, {'푹', 0x3B}, {'푼', 0x3C}, {'푿', 0x3D}, {'풀', 0x3E}, {'풂', 0x3F}, + {'품', 0x40}, {'풉', 0x41}, {'풋', 0x42}, {'풍', 0x43}, {'풔', 0x44}, {'풩', 0x45}, {'퓌', 0x46}, {'퓐', 0x47}, {'퓔', 0x48}, {'퓜', 0x49}, {'퓟', 0x4A}, {'퓨', 0x4B}, {'퓬', 0x4C}, {'퓰', 0x4D}, {'퓸', 0x4E}, {'퓻', 0x4F}, + {'퓽', 0x60}, {'프', 0x61}, {'픈', 0x62}, {'플', 0x63}, {'픔', 0x64}, {'픕', 0x65}, {'픗', 0x66}, {'피', 0x67}, {'픽', 0x68}, {'핀', 0x69}, {'필', 0x6A}, {'핌', 0x6B}, {'핍', 0x6C}, {'핏', 0x6D}, {'핑', 0x6E}, {'하', 0x6F}, + {'학', 0x70}, {'한', 0x71}, {'할', 0x72}, {'핥', 0x73}, {'함', 0x74}, {'합', 0x75}, {'핫', 0x76}, {'항', 0x77}, {'해', 0x78}, {'핵', 0x79}, {'핸', 0x7A}, {'핼', 0x7B}, {'햄', 0x7C}, {'햅', 0x7D}, {'햇', 0x7E}, {'했', 0x7F}, + {'행', 0x80}, {'햐', 0x81}, {'향', 0x82}, {'허', 0x83}, {'헉', 0x84}, {'헌', 0x85}, {'헐', 0x86}, {'헒', 0x87}, {'험', 0x88}, {'헙', 0x89}, {'헛', 0x8A}, {'헝', 0x8B}, {'헤', 0x8C}, {'헥', 0x8D}, {'헨', 0x8E}, {'헬', 0x8F}, + {'헴', 0x90}, {'헵', 0x91}, {'헷', 0x92}, {'헹', 0x93}, {'혀', 0x94}, {'혁', 0x95}, {'현', 0x96}, {'혈', 0x97}, {'혐', 0x98}, {'협', 0x99}, {'혓', 0x9A}, {'혔', 0x9B}, {'형', 0x9C}, {'혜', 0x9D}, {'혠', 0x9E}, + {'혤', 0xA1}, {'혭', 0xA2}, {'호', 0xA3}, {'혹', 0xA4}, {'혼', 0xA5}, {'홀', 0xA6}, {'홅', 0xA7}, {'홈', 0xA8}, {'홉', 0xA9}, {'홋', 0xAA}, {'홍', 0xAB}, {'홑', 0xAC}, {'화', 0xAD}, {'확', 0xAE}, {'환', 0xAF}, + {'활', 0xB0}, {'홧', 0xB1}, {'황', 0xB2}, {'홰', 0xB3}, {'홱', 0xB4}, {'홴', 0xB5}, {'횃', 0xB6}, {'횅', 0xB7}, {'회', 0xB8}, {'획', 0xB9}, {'횐', 0xBA}, {'횔', 0xBB}, {'횝', 0xBC}, {'횟', 0xBD}, {'횡', 0xBE}, {'효', 0xBF}, + {'횬', 0xC0}, {'횰', 0xC1}, {'횹', 0xC2}, {'횻', 0xC3}, {'후', 0xC4}, {'훅', 0xC5}, {'훈', 0xC6}, {'훌', 0xC7}, {'훑', 0xC8}, {'훔', 0xC9}, {'훗', 0xCA}, {'훙', 0xCB}, {'훠', 0xCC}, {'훤', 0xCD}, {'훨', 0xCE}, {'훰', 0xCF}, + {'훵', 0xD0}, {'훼', 0xD1}, {'훽', 0xD2}, {'휀', 0xD3}, {'휄', 0xD4}, {'휑', 0xD5}, {'휘', 0xD6}, {'휙', 0xD7}, {'휜', 0xD8}, {'휠', 0xD9}, {'휨', 0xDA}, {'휩', 0xDB}, {'휫', 0xDC}, {'휭', 0xDD}, {'휴', 0xDE}, {'휵', 0xDF}, + {'휸', 0xE0}, {'휼', 0xE1}, {'흄', 0xE2}, {'흇', 0xE3}, {'흉', 0xE4}, {'흐', 0xE5}, {'흑', 0xE6}, {'흔', 0xE7}, {'흖', 0xE8}, {'흗', 0xE9}, {'흘', 0xEA}, {'흙', 0xEB}, {'흠', 0xEC}, {'흡', 0xED}, {'흣', 0xEE}, {'흥', 0xEF}, + {'흩', 0xF0}, {'희', 0xF1}, {'흰', 0xF2}, {'흴', 0xF3}, {'흼', 0xF4}, {'흽', 0xF5}, {'힁', 0xF6}, {'히', 0xF7}, {'힉', 0xF8}, {'힌', 0xF9}, {'힐', 0xFA}, {'힘', 0xFB}, {'힙', 0xFC}, {'힛', 0xFD}, {'힝', 0xFE}, + }; + + private static readonly Dictionary U2GSC_KOR_B = U2GSC_KOR_0; + + private static readonly Dictionary GSC2U_KOR_0 = new() + { + {0x00, 'ㄱ'}, {0x01, 'ㄴ'}, {0x02, 'ㄷ'}, {0x03, 'ㄹ'}, {0x04, 'ㅁ'}, {0x05, 'ㅂ'}, {0x06, 'ㅅ'}, {0x07, 'ㅇ'}, {0x08, 'ㅈ'}, {0x09, 'ㅊ'}, {0x0A, 'ㅋ'}, {0x0B, 'ㅌ'}, {0x0C, 'ㅍ'}, {0x0D, 'ㅎ'}, {0x0E, 'ㄲ'}, {0x0F, 'ㄸ'}, + {0x10, 'ㅃ'}, {0x11, 'ㅆ'}, {0x12, 'ㅉ'}, + {0x20, 'ㅏ'}, {0x21, 'ㅑ'}, {0x22, 'ㅓ'}, {0x23, 'ㅕ'}, {0x24, 'ㅗ'}, {0x25, 'ㅛ'}, {0x26, 'ㅜ'}, {0x27, 'ㅠ'}, {0x28, 'ㅡ'}, {0x29, 'ㅣ'}, {0x2A, 'ㅐ'}, {0x2B, 'ㅒ'}, {0x2C, 'ㅔ'}, {0x2D, 'ㅖ'}, {0x2E, 'ㅘ'}, {0x2F, 'ㅙ'}, + {0x30, 'ㅚ'}, {0x31, 'ㅝ'}, {0x32, 'ㅞ'}, {0x33, 'ㅟ'}, {0x34, 'ㅢ'}, {0x3E, '_'}, {0x3F, '—'}, + {0x60, '「'}, {0x61, '」'}, {0x62, '『'}, {0x63, '』'}, {0x64, '('}, {0x65, ')'}, {0x66, '!'}, {0x67, '?'}, {0x68, '-'}, {0x69, '~'}, {0x6A, '…'}, {0x6B, ','}, {0x6C, '.'}, // . is 0x6D for bank + {0xF0, '0'}, {0xF1, '1'}, {0xF2, '2'}, {0xF3, '3'}, {0xF4, '4'}, {0xF5, '5'}, {0xF6, '6'}, {0xF7, '7'}, {0xF8, '8'}, {0xF9, '9'}, + {0xFF, '\u3000'}, + }; + + private static readonly Dictionary GSC2U_KOR_1 = new() + { + {0x01, '가'}, {0x02, '각'}, {0x03, '간'}, {0x04, '갇'}, {0x05, '갈'}, {0x06, '갉'}, {0x07, '갊'}, {0x08, '감'}, {0x09, '갑'}, {0x0A, '값'}, {0x0B, '갓'}, {0x0C, '갔'}, {0x0D, '강'}, {0x0E, '갖'}, {0x0F, '갗'}, + {0x10, '같'}, {0x11, '갚'}, {0x12, '갛'}, {0x13, '개'}, {0x14, '객'}, {0x15, '갠'}, {0x16, '갤'}, {0x17, '갬'}, {0x18, '갭'}, {0x19, '갯'}, {0x1A, '갰'}, {0x1B, '갱'}, {0x1C, '갸'}, {0x1D, '갹'}, {0x1E, '갼'}, {0x1F, '걀'}, + {0x20, '걋'}, {0x21, '걍'}, {0x22, '걔'}, {0x23, '걘'}, {0x24, '걜'}, {0x25, '거'}, {0x26, '걱'}, {0x27, '건'}, {0x28, '걷'}, {0x29, '걸'}, {0x2A, '걺'}, {0x2B, '검'}, {0x2C, '겁'}, {0x2D, '것'}, {0x2E, '겄'}, {0x2F, '겅'}, + {0x30, '겆'}, {0x31, '겉'}, {0x32, '겊'}, {0x33, '겋'}, {0x34, '게'}, {0x35, '겐'}, {0x36, '겔'}, {0x37, '겜'}, {0x38, '겝'}, {0x39, '겟'}, {0x3A, '겠'}, {0x3B, '겡'}, {0x3C, '겨'}, {0x3D, '격'}, {0x3E, '겪'}, {0x3F, '견'}, + {0x40, '겯'}, {0x41, '결'}, {0x42, '겹'}, {0x43, '겸'}, {0x44, '겻'}, {0x45, '겼'}, {0x46, '경'}, {0x47, '곁'}, {0x48, '계'}, {0x49, '곈'}, {0x4A, '곌'}, {0x4B, '곕'}, {0x4C, '곗'}, {0x4D, '고'}, {0x4E, '곡'}, {0x4F, '곤'}, + {0x60, '곧'}, {0x61, '골'}, {0x62, '곪'}, {0x63, '곬'}, {0x64, '곯'}, {0x65, '곰'}, {0x66, '곱'}, {0x67, '곳'}, {0x68, '공'}, {0x69, '곶'}, {0x6A, '과'}, {0x6B, '곽'}, {0x6C, '관'}, {0x6D, '괄'}, {0x6E, '괆'}, + {0x71, '괌'}, {0x72, '괍'}, {0x73, '괏'}, {0x74, '광'}, {0x75, '괘'}, {0x76, '괜'}, {0x77, '괠'}, {0x78, '괩'}, {0x79, '괬'}, {0x7A, '괭'}, {0x7B, '괴'}, {0x7C, '괵'}, {0x7D, '괸'}, {0x7E, '괼'}, {0x7F, '괻'}, + {0x80, '굅'}, {0x81, '굇'}, {0x82, '굉'}, {0x83, '교'}, {0x84, '굔'}, {0x85, '굘'}, {0x86, '굡'}, {0x87, '굣'}, {0x88, '구'}, {0x89, '국'}, {0x8A, '군'}, {0x8B, '굳'}, {0x8C, '굴'}, {0x8D, '굵'}, {0x8E, '굶'}, {0x8F, '굻'}, + {0x90, '굼'}, {0x91, '굽'}, {0x92, '굿'}, {0x93, '궁'}, {0x94, '궂'}, {0x95, '궈'}, {0x96, '궉'}, {0x97, '권'}, {0x98, '궐'}, {0x99, '궜'}, {0x9A, '궝'}, {0x9B, '궤'}, {0x9C, '궷'}, {0x9D, '귀'}, {0x9E, '귁'}, {0x9F, '귄'}, + {0xA0, '귈'}, {0xA1, '귐'}, {0xA2, '귑'}, {0xA3, '귓'}, {0xA4, '규'}, {0xA5, '균'}, {0xA6, '귤'}, {0xA7, '그'}, {0xA8, '극'}, {0xA9, '근'}, {0xAA, '귿'}, {0xAB, '글'}, {0xAC, '긁'}, {0xAD, '금'}, {0xAE, '급'}, {0xAF, '긋'}, + {0xB0, '긍'}, {0xB1, '긔'}, {0xB2, '기'}, {0xB3, '긱'}, {0xB4, '긴'}, {0xB5, '긷'}, {0xB6, '길'}, {0xB7, '긺'}, {0xB8, '김'}, {0xB9, '깁'}, {0xBA, '깃'}, {0xBB, '깅'}, {0xBC, '깆'}, {0xBD, '깊'}, {0xBE, '까'}, {0xBF, '깍'}, + {0xC0, '깎'}, {0xC1, '깐'}, {0xC2, '깔'}, {0xC3, '깖'}, {0xC4, '깜'}, {0xC5, '깝'}, {0xC6, '깟'}, {0xC7, '깠'}, {0xC8, '깡'}, {0xC9, '깥'}, {0xCA, '깨'}, {0xCB, '깩'}, {0xCC, '깬'}, {0xCD, '깰'}, {0xCE, '깸'}, + {0xD1, '깹'}, {0xD2, '깻'}, {0xD3, '깼'}, {0xD4, '깽'}, {0xD5, '꺄'}, {0xD6, '꺅'}, {0xD7, '꺌'}, {0xD8, '꺼'}, {0xD9, '꺽'}, {0xDA, '꺾'}, {0xDB, '껀'}, {0xDC, '껄'}, {0xDD, '껌'}, {0xDE, '껍'}, {0xDF, '껏'}, + {0xE0, '껐'}, {0xE1, '껑'}, {0xE2, '께'}, {0xE3, '껙'}, {0xE4, '껜'}, {0xE5, '껨'}, {0xE6, '껫'}, {0xE7, '껭'}, {0xE8, '껴'}, {0xE9, '껸'}, {0xEA, '껼'}, {0xEB, '꼇'}, {0xEC, '꼈'}, {0xED, '꼍'}, {0xEE, '꼐'}, {0xEF, '꼬'}, + {0xF0, '꼭'}, {0xF1, '꼰'}, {0xF2, '꼲'}, {0xF3, '꼴'}, {0xF4, '꼼'}, {0xF5, '꼽'}, {0xF6, '꼿'}, {0xF7, '꽁'}, {0xF8, '꽂'}, {0xF9, '꽃'}, {0xFA, '꽈'}, {0xFB, '꽉'}, {0xFC, '꽐'}, {0xFD, '꽜'}, {0xFE, '꽝'}, {0xFF, '꽤'}, + }; + + private static readonly Dictionary GSC2U_KOR_2 = new() + { + {0x00, '꽥'}, {0x01, '꽹'}, {0x02, '꾀'}, {0x03, '꾄'}, {0x04, '꾈'}, {0x05, '꾐'}, {0x06, '꾑'}, {0x07, '꾕'}, {0x08, '꾜'}, {0x09, '꾸'}, {0x0A, '꾹'}, {0x0B, '꾼'}, {0x0C, '꿀'}, {0x0D, '꿇'}, {0x0E, '꿈'}, {0x0F, '꿉'}, + {0x10, '꿋'}, {0x11, '꿍'}, {0x12, '꿎'}, {0x13, '꿔'}, {0x14, '꿜'}, {0x15, '꿨'}, {0x16, '꿩'}, {0x17, '꿰'}, {0x18, '꿱'}, {0x19, '꿴'}, {0x1A, '꿸'}, {0x1B, '뀀'}, {0x1C, '뀁'}, {0x1D, '뀄'}, {0x1E, '뀌'}, {0x1F, '뀐'}, + {0x20, '뀔'}, {0x21, '뀜'}, {0x22, '뀝'}, {0x23, '뀨'}, {0x24, '끄'}, {0x25, '끅'}, {0x26, '끈'}, {0x27, '끊'}, {0x28, '끌'}, {0x29, '끎'}, {0x2A, '끓'}, {0x2B, '끔'}, {0x2C, '끕'}, {0x2D, '끗'}, {0x2E, '끙'}, + {0x31, '끝'}, {0x32, '끼'}, {0x33, '끽'}, {0x34, '낀'}, {0x35, '낄'}, {0x36, '낌'}, {0x37, '낍'}, {0x38, '낏'}, {0x39, '낑'}, {0x3A, '나'}, {0x3B, '낙'}, {0x3C, '낚'}, {0x3D, '난'}, {0x3E, '낟'}, {0x3F, '날'}, + {0x40, '낡'}, {0x41, '낢'}, {0x42, '남'}, {0x43, '납'}, {0x44, '낫'}, {0x45, '났'}, {0x46, '낭'}, {0x47, '낮'}, {0x48, '낯'}, {0x49, '낱'}, {0x4A, '낳'}, {0x4B, '내'}, {0x4C, '낵'}, {0x4D, '낸'}, {0x4E, '낼'}, {0x4F, '냄'}, + {0x60, '냅'}, {0x61, '냇'}, {0x62, '냈'}, {0x63, '냉'}, {0x64, '냐'}, {0x65, '냑'}, {0x66, '냔'}, {0x67, '냘'}, {0x68, '냠'}, {0x69, '냥'}, {0x6A, '너'}, {0x6B, '넉'}, {0x6C, '넋'}, {0x6D, '넌'}, {0x6E, '널'}, {0x6F, '넒'}, + {0x70, '넓'}, {0x71, '넘'}, {0x72, '넙'}, {0x73, '넛'}, {0x74, '넜'}, {0x75, '넝'}, {0x76, '넣'}, {0x77, '네'}, {0x78, '넥'}, {0x79, '넨'}, {0x7A, '넬'}, {0x7B, '넴'}, {0x7C, '넵'}, {0x7D, '넷'}, {0x7E, '넸'}, {0x7F, '넹'}, + {0x80, '녀'}, {0x81, '녁'}, {0x82, '년'}, {0x83, '녈'}, {0x84, '념'}, {0x85, '녑'}, {0x86, '녔'}, {0x87, '녕'}, {0x88, '녘'}, {0x89, '녜'}, {0x8A, '녠'}, {0x8B, '노'}, {0x8C, '녹'}, {0x8D, '논'}, {0x8E, '놀'}, {0x8F, '놂'}, + {0x90, '놈'}, {0x91, '놉'}, {0x92, '놋'}, {0x93, '농'}, {0x94, '높'}, {0x95, '놓'}, {0x96, '놔'}, {0x97, '놘'}, {0x98, '놜'}, {0x99, '놨'}, {0x9A, '뇌'}, {0x9B, '뇐'}, {0x9C, '뇔'}, {0x9D, '뇜'}, {0x9E, '뇝'}, + {0xA1, '뇟'}, {0xA2, '뇨'}, {0xA3, '뇩'}, {0xA4, '뇬'}, {0xA5, '뇰'}, {0xA6, '뇹'}, {0xA7, '뇻'}, {0xA8, '뇽'}, {0xA9, '누'}, {0xAA, '눅'}, {0xAB, '눈'}, {0xAC, '눋'}, {0xAD, '눌'}, {0xAE, '눔'}, {0xAF, '눕'}, + {0xB0, '눗'}, {0xB1, '눙'}, {0xB2, '눠'}, {0xB3, '눴'}, {0xB4, '눼'}, {0xB5, '뉘'}, {0xB6, '뉜'}, {0xB7, '뉠'}, {0xB8, '뉨'}, {0xB9, '뉩'}, {0xBA, '뉴'}, {0xBB, '뉵'}, {0xBC, '뉼'}, {0xBD, '늄'}, {0xBE, '늅'}, {0xBF, '늉'}, + {0xC0, '느'}, {0xC1, '늑'}, {0xC2, '는'}, {0xC3, '늘'}, {0xC4, '늙'}, {0xC5, '늚'}, {0xC6, '늠'}, {0xC7, '늡'}, {0xC8, '늣'}, {0xC9, '능'}, {0xCA, '늦'}, {0xCB, '늪'}, {0xCC, '늬'}, {0xCD, '늰'}, {0xCE, '늴'}, {0xCF, '니'}, + {0xD0, '닉'}, {0xD1, '닌'}, {0xD2, '닐'}, {0xD3, '닒'}, {0xD4, '님'}, {0xD5, '닙'}, {0xD6, '닛'}, {0xD7, '닝'}, {0xD8, '닢'}, {0xD9, '다'}, {0xDA, '닥'}, {0xDB, '닦'}, {0xDC, '단'}, {0xDD, '닫'}, {0xDE, '달'}, {0xDF, '닭'}, + {0xE0, '닮'}, {0xE1, '닯'}, {0xE2, '닳'}, {0xE3, '담'}, {0xE4, '답'}, {0xE5, '닷'}, {0xE6, '닸'}, {0xE7, '당'}, {0xE8, '닺'}, {0xE9, '닻'}, {0xEA, '닿'}, {0xEB, '대'}, {0xEC, '댁'}, {0xED, '댄'}, {0xEE, '댈'}, {0xEF, '댐'}, + {0xF0, '댑'}, {0xF1, '댓'}, {0xF2, '댔'}, {0xF3, '댕'}, {0xF5, '더'}, {0xF6, '덕'}, {0xF7, '덖'}, {0xF8, '던'}, {0xF9, '덛'}, {0xFA, '덜'}, {0xFB, '덞'}, {0xFC, '덟'}, {0xFD, '덤'}, {0xFE, '덥'}, + }; + + private static readonly Dictionary GSC2U_KOR_3 = new() + { + {0x01, '덧'}, {0x02, '덩'}, {0x03, '덫'}, {0x04, '덮'}, {0x05, '데'}, {0x06, '덱'}, {0x07, '덴'}, {0x08, '델'}, {0x09, '뎀'}, {0x0A, '뎁'}, {0x0B, '뎃'}, {0x0C, '뎄'}, {0x0D, '뎅'}, {0x0E, '뎌'}, {0x0F, '뎐'}, + {0x10, '뎔'}, {0x11, '뎠'}, {0x12, '뎡'}, {0x13, '뎨'}, {0x14, '뎬'}, {0x15, '도'}, {0x16, '독'}, {0x17, '돈'}, {0x18, '돋'}, {0x19, '돌'}, {0x1A, '돎'}, {0x1C, '돔'}, {0x1D, '돕'}, {0x1E, '돗'}, {0x1F, '동'}, + {0x20, '돛'}, {0x21, '돝'}, {0x22, '돠'}, {0x23, '돤'}, {0x24, '돨'}, {0x25, '돼'}, {0x26, '됐'}, {0x27, '되'}, {0x28, '된'}, {0x29, '될'}, {0x2A, '됨'}, {0x2B, '됩'}, {0x2C, '됫'}, {0x2D, '됴'}, {0x2E, '두'}, {0x2F, '둑'}, + {0x30, '둔'}, {0x31, '둘'}, {0x32, '둠'}, {0x33, '둡'}, {0x34, '둣'}, {0x35, '둥'}, {0x36, '둬'}, {0x37, '뒀'}, {0x38, '뒈'}, {0x39, '뒝'}, {0x3A, '뒤'}, {0x3B, '뒨'}, {0x3C, '뒬'}, {0x3D, '뒵'}, {0x3E, '뒷'}, {0x3F, '뒹'}, + {0x40, '듀'}, {0x41, '듄'}, {0x42, '듈'}, {0x43, '듐'}, {0x44, '듕'}, {0x45, '드'}, {0x46, '득'}, {0x47, '든'}, {0x48, '듣'}, {0x49, '들'}, {0x4A, '듦'}, {0x4B, '듬'}, {0x4C, '듭'}, {0x4D, '듯'}, {0x4E, '등'}, {0x4F, '듸'}, + {0x60, '디'}, {0x61, '딕'}, {0x62, '딘'}, {0x63, '딛'}, {0x64, '딜'}, {0x65, '딤'}, {0x66, '딥'}, {0x67, '딧'}, {0x68, '딨'}, {0x69, '딩'}, {0x6A, '딪'}, {0x6B, '따'}, {0x6C, '딱'}, {0x6D, '딴'}, {0x6E, '딸'}, + {0x71, '땀'}, {0x72, '땁'}, {0x73, '땃'}, {0x74, '땄'}, {0x75, '땅'}, {0x76, '땋'}, {0x77, '때'}, {0x78, '땍'}, {0x79, '땐'}, {0x7A, '땔'}, {0x7B, '땜'}, {0x7C, '땝'}, {0x7D, '땟'}, {0x7E, '땠'}, {0x7F, '땡'}, + {0x80, '떠'}, {0x81, '떡'}, {0x82, '떤'}, {0x83, '떨'}, {0x84, '떪'}, {0x85, '떫'}, {0x86, '떰'}, {0x87, '떱'}, {0x88, '떳'}, {0x89, '떴'}, {0x8A, '떵'}, {0x8B, '떻'}, {0x8C, '떼'}, {0x8D, '떽'}, {0x8E, '뗀'}, {0x8F, '뗄'}, + {0x90, '뗌'}, {0x91, '뗍'}, {0x92, '뗏'}, {0x93, '뗐'}, {0x94, '뗑'}, {0x95, '뗘'}, {0x96, '뗬'}, {0x97, '또'}, {0x98, '똑'}, {0x99, '똔'}, {0x9A, '똘'}, {0x9B, '똥'}, {0x9C, '똬'}, {0x9D, '똴'}, {0x9E, '뙈'}, {0x9F, '뙤'}, + {0xA0, '뙨'}, {0xA1, '뚜'}, {0xA2, '뚝'}, {0xA3, '뚠'}, {0xA4, '뚤'}, {0xA5, '뚫'}, {0xA6, '뚬'}, {0xA7, '뚱'}, {0xA8, '뛔'}, {0xA9, '뛰'}, {0xAA, '뛴'}, {0xAB, '뛸'}, {0xAC, '뜀'}, {0xAD, '뜁'}, {0xAE, '뜅'}, {0xAF, '뜨'}, + {0xB0, '뜩'}, {0xB1, '뜬'}, {0xB2, '뜯'}, {0xB3, '뜰'}, {0xB4, '뜸'}, {0xB5, '뜹'}, {0xB6, '뜻'}, {0xB7, '띄'}, {0xB8, '띈'}, {0xB9, '띌'}, {0xBA, '띔'}, {0xBB, '띕'}, {0xBC, '띠'}, {0xBD, '띤'}, {0xBE, '띨'}, {0xBF, '띰'}, + {0xC0, '띱'}, {0xC1, '띳'}, {0xC2, '띵'}, {0xC3, '라'}, {0xC4, '락'}, {0xC5, '란'}, {0xC6, '랄'}, {0xC7, '람'}, {0xC8, '랍'}, {0xC9, '랏'}, {0xCA, '랐'}, {0xCB, '랑'}, {0xCC, '랒'}, {0xCD, '랖'}, {0xCE, '랗'}, + {0xD0, '뢔'}, {0xD1, '래'}, {0xD2, '랙'}, {0xD3, '랜'}, {0xD4, '랠'}, {0xD5, '램'}, {0xD6, '랩'}, {0xD7, '랫'}, {0xD8, '랬'}, {0xD9, '랭'}, {0xDA, '랴'}, {0xDB, '략'}, {0xDC, '랸'}, {0xDD, '럇'}, {0xDE, '량'}, {0xDF, '러'}, + {0xE0, '럭'}, {0xE1, '런'}, {0xE2, '럴'}, {0xE3, '럼'}, {0xE4, '럽'}, {0xE5, '럿'}, {0xE6, '렀'}, {0xE7, '렁'}, {0xE8, '렇'}, {0xE9, '레'}, {0xEA, '렉'}, {0xEB, '렌'}, {0xEC, '렐'}, {0xED, '렘'}, {0xEE, '렙'}, {0xEF, '렛'}, + {0xF0, '렝'}, {0xF1, '려'}, {0xF2, '력'}, {0xF3, '련'}, {0xF4, '렬'}, {0xF5, '렴'}, {0xF6, '렵'}, {0xF7, '렷'}, {0xF8, '렸'}, {0xF9, '령'}, {0xFA, '례'}, {0xFB, '롄'}, {0xFC, '롑'}, {0xFD, '롓'}, {0xFE, '로'}, {0xFF, '록'}, + }; + + private static readonly Dictionary GSC2U_KOR_4 = new() + { + {0x00, '론'}, {0x01, '롤'}, {0x02, '롬'}, {0x03, '롭'}, {0x04, '롯'}, {0x05, '롱'}, {0x06, '롸'}, {0x07, '롼'}, {0x08, '뢍'}, {0x09, '뢨'}, {0x0A, '뢰'}, {0x0B, '뢴'}, {0x0C, '뢸'}, {0x0D, '룀'}, {0x0E, '룁'}, {0x0F, '룃'}, + {0x10, '룅'}, {0x11, '료'}, {0x12, '룐'}, {0x13, '룔'}, {0x14, '룝'}, {0x15, '룟'}, {0x16, '룡'}, {0x17, '루'}, {0x18, '룩'}, {0x19, '룬'}, {0x1A, '룰'}, {0x1B, '룸'}, {0x1C, '룹'}, {0x1D, '룻'}, {0x1E, '룽'}, {0x1F, '뤄'}, + {0x20, '뤘'}, {0x21, '뤠'}, {0x22, '뤼'}, {0x23, '뤽'}, {0x24, '륀'}, {0x25, '륄'}, {0x26, '륌'}, {0x27, '륏'}, {0x28, '륑'}, {0x29, '류'}, {0x2A, '륙'}, {0x2B, '륜'}, {0x2C, '률'}, {0x2D, '륨'}, {0x2E, '륩'}, + {0x31, '륫'}, {0x32, '륭'}, {0x33, '르'}, {0x34, '륵'}, {0x35, '른'}, {0x36, '를'}, {0x37, '름'}, {0x38, '릅'}, {0x39, '릇'}, {0x3A, '릉'}, {0x3B, '릊'}, {0x3C, '릍'}, {0x3D, '릎'}, {0x3E, '리'}, {0x3F, '릭'}, + {0x40, '린'}, {0x41, '릴'}, {0x42, '림'}, {0x43, '립'}, {0x44, '릿'}, {0x45, '링'}, {0x46, '마'}, {0x47, '막'}, {0x48, '만'}, {0x49, '많'}, {0x4A, '맏'}, {0x4B, '말'}, {0x4C, '맑'}, {0x4D, '맒'}, {0x4E, '맘'}, {0x4F, '맙'}, + {0x60, '맛'}, {0x61, '망'}, {0x62, '맞'}, {0x63, '맡'}, {0x64, '맣'}, {0x65, '매'}, {0x66, '맥'}, {0x67, '맨'}, {0x68, '맬'}, {0x69, '맴'}, {0x6A, '맵'}, {0x6B, '맷'}, {0x6C, '맸'}, {0x6D, '맹'}, {0x6E, '맺'}, {0x6F, '먀'}, + {0x70, '먁'}, {0x71, '먈'}, {0x72, '먕'}, {0x73, '머'}, {0x74, '먹'}, {0x75, '먼'}, {0x76, '멀'}, {0x77, '멂'}, {0x78, '멈'}, {0x79, '멉'}, {0x7A, '멋'}, {0x7B, '멍'}, {0x7C, '멎'}, {0x7D, '멓'}, {0x7E, '메'}, {0x7F, '멕'}, + {0x80, '멘'}, {0x81, '멜'}, {0x82, '멤'}, {0x83, '멥'}, {0x84, '멧'}, {0x85, '멨'}, {0x86, '멩'}, {0x87, '며'}, {0x88, '멱'}, {0x89, '면'}, {0x8A, '멸'}, {0x8B, '몃'}, {0x8C, '몄'}, {0x8D, '명'}, {0x8E, '몇'}, {0x8F, '몌'}, + {0x90, '모'}, {0x91, '목'}, {0x92, '몫'}, {0x93, '몬'}, {0x94, '몰'}, {0x95, '몲'}, {0x96, '몸'}, {0x97, '몹'}, {0x98, '못'}, {0x99, '몽'}, {0x9A, '뫄'}, {0x9B, '뫈'}, {0x9C, '뫘'}, {0x9D, '뫙'}, {0x9E, '뫼'}, + {0xA1, '묀'}, {0xA2, '묄'}, {0xA3, '묍'}, {0xA4, '묏'}, {0xA5, '묑'}, {0xA6, '묘'}, {0xA7, '묜'}, {0xA8, '묠'}, {0xA9, '묩'}, {0xAA, '묫'}, {0xAB, '무'}, {0xAC, '묵'}, {0xAD, '묶'}, {0xAE, '문'}, {0xAF, '묻'}, + {0xB0, '물'}, {0xB1, '묽'}, {0xB2, '묾'}, {0xB3, '뭄'}, {0xB4, '뭅'}, {0xB5, '뭇'}, {0xB6, '뭉'}, {0xB7, '뭍'}, {0xB8, '뭏'}, {0xB9, '뭐'}, {0xBA, '뭔'}, {0xBB, '뭘'}, {0xBC, '뭡'}, {0xBD, '뭣'}, {0xBE, '뭬'}, {0xBF, '뮈'}, + {0xC0, '뮌'}, {0xC1, '뮐'}, {0xC2, '뮤'}, {0xC3, '뮨'}, {0xC4, '뮬'}, {0xC5, '뮴'}, {0xC6, '뮷'}, {0xC7, '므'}, {0xC8, '믄'}, {0xC9, '믈'}, {0xCA, '믐'}, {0xCB, '믓'}, {0xCC, '미'}, {0xCD, '믹'}, {0xCE, '민'}, {0xCF, '믿'}, + {0xD0, '밀'}, {0xD1, '밂'}, {0xD2, '밈'}, {0xD3, '밉'}, {0xD4, '밋'}, {0xD5, '밌'}, {0xD6, '밍'}, {0xD7, '및'}, {0xD8, '밑'}, {0xD9, '바'}, {0xDA, '박'}, {0xDB, '밖'}, {0xDC, '밗'}, {0xDD, '반'}, {0xDE, '받'}, {0xDF, '발'}, + {0xE0, '밝'}, {0xE1, '밞'}, {0xE2, '밟'}, {0xE3, '밤'}, {0xE4, '밥'}, {0xE5, '밧'}, {0xE6, '방'}, {0xE7, '밭'}, {0xE8, '배'}, {0xE9, '백'}, {0xEA, '밴'}, {0xEB, '밸'}, {0xEC, '뱀'}, {0xED, '뱁'}, {0xEE, '뱃'}, {0xEF, '뱄'}, + {0xF0, '뱅'}, {0xF1, '뱉'}, {0xF2, '뱌'}, {0xF3, '뱍'}, {0xF4, '뱐'}, {0xF5, '뱝'}, {0xF6, '버'}, {0xF7, '벅'}, {0xF8, '번'}, {0xF9, '벋'}, {0xFA, '벌'}, {0xFB, '벎'}, {0xFC, '범'}, {0xFD, '법'}, {0xFE, '벗'}, + }; + + private static readonly Dictionary GSC2U_KOR_5 = new() + { + {0x01, '벙'}, {0x02, '벚'}, {0x03, '베'}, {0x04, '벡'}, {0x05, '벤'}, {0x06, '벧'}, {0x07, '벨'}, {0x08, '벰'}, {0x09, '벱'}, {0x0A, '벳'}, {0x0B, '벴'}, {0x0C, '벵'}, {0x0D, '벼'}, {0x0E, '벽'}, {0x0F, '변'}, + {0x10, '별'}, {0x11, '볍'}, {0x12, '볏'}, {0x13, '볐'}, {0x14, '병'}, {0x15, '볕'}, {0x16, '볘'}, {0x17, '볜'}, {0x18, '보'}, {0x19, '복'}, {0x1A, '볶'}, {0x1B, '본'}, {0x1C, '볼'}, {0x1D, '봄'}, {0x1E, '봅'}, {0x1F, '봇'}, + {0x20, '봉'}, {0x21, '봐'}, {0x22, '봔'}, {0x23, '봤'}, {0x24, '봬'}, {0x25, '뵀'}, {0x26, '뵈'}, {0x27, '뵉'}, {0x28, '뵌'}, {0x29, '뵐'}, {0x2A, '뵘'}, {0x2B, '뵙'}, {0x2C, '뵤'}, {0x2D, '뵨'}, {0x2E, '부'}, {0x2F, '북'}, + {0x30, '분'}, {0x31, '붇'}, {0x32, '불'}, {0x33, '붉'}, {0x34, '붊'}, {0x35, '붐'}, {0x36, '붑'}, {0x37, '붓'}, {0x38, '붕'}, {0x39, '붙'}, {0x3A, '붚'}, {0x3B, '붜'}, {0x3C, '붤'}, {0x3D, '붰'}, {0x3E, '붸'}, {0x3F, '뷔'}, + {0x40, '뷕'}, {0x41, '뷘'}, {0x42, '뷜'}, {0x43, '뷩'}, {0x44, '뷰'}, {0x45, '뷴'}, {0x46, '뷸'}, {0x47, '븀'}, {0x48, '븃'}, {0x49, '븅'}, {0x4A, '브'}, {0x4B, '븍'}, {0x4C, '븐'}, {0x4D, '블'}, {0x4E, '븜'}, {0x4F, '븝'}, + {0x60, '븟'}, {0x61, '비'}, {0x62, '빅'}, {0x63, '빈'}, {0x64, '빌'}, {0x65, '빎'}, {0x66, '빔'}, {0x67, '빕'}, {0x68, '빗'}, {0x69, '빙'}, {0x6A, '빚'}, {0x6B, '빛'}, {0x6C, '빠'}, {0x6D, '빡'}, {0x6E, '빤'}, + {0x71, '빨'}, {0x72, '빪'}, {0x73, '빰'}, {0x74, '빱'}, {0x75, '빳'}, {0x76, '빴'}, {0x77, '빵'}, {0x78, '빻'}, {0x79, '빼'}, {0x7A, '빽'}, {0x7B, '뺀'}, {0x7C, '뺄'}, {0x7D, '뺌'}, {0x7E, '뺍'}, {0x7F, '뺏'}, + {0x80, '뺐'}, {0x81, '뺑'}, {0x82, '뺘'}, {0x83, '뺙'}, {0x84, '뺨'}, {0x85, '뻐'}, {0x86, '뻑'}, {0x87, '뻔'}, {0x88, '뻗'}, {0x89, '뻘'}, {0x8A, '뻠'}, {0x8B, '뻣'}, {0x8C, '뻤'}, {0x8D, '뻥'}, {0x8E, '뻬'}, {0x8F, '뼁'}, + {0x90, '뼈'}, {0x91, '뼉'}, {0x92, '뼘'}, {0x93, '뼙'}, {0x94, '뼛'}, {0x95, '뼜'}, {0x96, '뼝'}, {0x97, '뽀'}, {0x98, '뽁'}, {0x99, '뽄'}, {0x9A, '뽈'}, {0x9B, '뽐'}, {0x9C, '뽑'}, {0x9D, '뽕'}, {0x9E, '뾔'}, {0x9F, '뾰'}, + {0xA0, '뿅'}, {0xA1, '뿌'}, {0xA2, '뿍'}, {0xA3, '뿐'}, {0xA4, '뿔'}, {0xA5, '뿜'}, {0xA6, '뿟'}, {0xA7, '뿡'}, {0xA8, '쀼'}, {0xA9, '쁑'}, {0xAA, '쁘'}, {0xAB, '쁜'}, {0xAC, '쁠'}, {0xAD, '쁨'}, {0xAE, '쁩'}, {0xAF, '삐'}, + {0xB0, '삑'}, {0xB1, '삔'}, {0xB2, '삘'}, {0xB3, '삠'}, {0xB4, '삡'}, {0xB5, '삣'}, {0xB6, '삥'}, {0xB7, '사'}, {0xB8, '삭'}, {0xB9, '삯'}, {0xBA, '산'}, {0xBB, '삳'}, {0xBC, '살'}, {0xBD, '삵'}, {0xBE, '삶'}, {0xBF, '삼'}, + {0xC0, '삽'}, {0xC1, '삿'}, {0xC2, '샀'}, {0xC3, '상'}, {0xC4, '샅'}, {0xC5, '새'}, {0xC6, '색'}, {0xC7, '샌'}, {0xC8, '샐'}, {0xC9, '샘'}, {0xCA, '샙'}, {0xCB, '샛'}, {0xCC, '샜'}, {0xCD, '생'}, {0xCE, '샤'}, + {0xD1, '샥'}, {0xD2, '샨'}, {0xD3, '샬'}, {0xD4, '샴'}, {0xD5, '샵'}, {0xD6, '샷'}, {0xD7, '샹'}, {0xD8, '섀'}, {0xD9, '섄'}, {0xDA, '섈'}, {0xDB, '섐'}, {0xDC, '섕'}, {0xDD, '서'}, {0xDE, '석'}, {0xDF, '섞'}, + {0xE0, '섟'}, {0xE1, '선'}, {0xE2, '섣'}, {0xE3, '설'}, {0xE4, '섦'}, {0xE5, '섧'}, {0xE6, '섬'}, {0xE7, '섭'}, {0xE8, '섯'}, {0xE9, '섰'}, {0xEA, '성'}, {0xEB, '섶'}, {0xEC, '세'}, {0xED, '섹'}, {0xEE, '센'}, {0xEF, '셀'}, + {0xF0, '셈'}, {0xF1, '셉'}, {0xF2, '셋'}, {0xF3, '셌'}, {0xF4, '셍'}, {0xF5, '셔'}, {0xF6, '셕'}, {0xF7, '션'}, {0xF8, '셜'}, {0xF9, '셤'}, {0xFA, '셥'}, {0xFB, '셧'}, {0xFC, '셨'}, {0xFD, '셩'}, {0xFE, '셰'}, {0xFF, '셴'}, + }; + + private static readonly Dictionary GSC2U_KOR_6 = new() + { + {0x00, '셸'}, {0x01, '솅'}, {0x02, '소'}, {0x03, '속'}, {0x04, '솎'}, {0x05, '손'}, {0x06, '솔'}, {0x07, '솖'}, {0x08, '솜'}, {0x09, '솝'}, {0x0A, '솟'}, {0x0B, '송'}, {0x0C, '솥'}, {0x0D, '솨'}, {0x0E, '솩'}, {0x0F, '솬'}, + {0x10, '솰'}, {0x11, '솽'}, {0x12, '쇄'}, {0x13, '쇈'}, {0x14, '쇌'}, {0x15, '쇔'}, {0x16, '쇗'}, {0x17, '쇘'}, {0x18, '쇠'}, {0x19, '쇤'}, {0x1A, '쇨'}, {0x1B, '쇰'}, {0x1C, '쇱'}, {0x1D, '쇳'}, {0x1E, '쇼'}, {0x1F, '쇽'}, + {0x20, '숀'}, {0x21, '숄'}, {0x22, '숌'}, {0x23, '숍'}, {0x24, '숏'}, {0x25, '숑'}, {0x26, '수'}, {0x27, '숙'}, {0x28, '순'}, {0x29, '숟'}, {0x2A, '술'}, {0x2B, '숨'}, {0x2C, '숩'}, {0x2D, '숫'}, {0x2E, '숭'}, {0x2F, '쌰'}, + {0x30, '쎼'}, {0x31, '숯'}, {0x32, '숱'}, {0x33, '숲'}, {0x34, '숴'}, {0x35, '쉈'}, {0x36, '쉐'}, {0x37, '쉑'}, {0x38, '쉔'}, {0x39, '쉘'}, {0x3A, '쉠'}, {0x3B, '쉥'}, {0x3C, '쉬'}, {0x3D, '쉭'}, {0x3E, '쉰'}, {0x3F, '쉴'}, + {0x40, '쉼'}, {0x41, '쉽'}, {0x42, '쉿'}, {0x43, '슁'}, {0x44, '슈'}, {0x45, '슉'}, {0x46, '슐'}, {0x47, '슘'}, {0x48, '슛'}, {0x49, '슝'}, {0x4A, '스'}, {0x4B, '슥'}, {0x4C, '슨'}, {0x4D, '슬'}, {0x4E, '슭'}, {0x4F, '슴'}, + {0x60, '습'}, {0x61, '슷'}, {0x62, '승'}, {0x63, '시'}, {0x64, '식'}, {0x65, '신'}, {0x66, '싣'}, {0x67, '실'}, {0x68, '싫'}, {0x69, '심'}, {0x6A, '십'}, {0x6B, '싯'}, {0x6C, '싱'}, {0x6D, '싶'}, {0x6E, '싸'}, {0x6F, '싹'}, + {0x70, '싻'}, {0x71, '싼'}, {0x72, '쌀'}, {0x73, '쌈'}, {0x74, '쌉'}, {0x75, '쌌'}, {0x76, '쌍'}, {0x77, '쌓'}, {0x78, '쌔'}, {0x79, '쌕'}, {0x7A, '쌘'}, {0x7B, '쌜'}, {0x7C, '쌤'}, {0x7D, '쌥'}, {0x7E, '쌨'}, {0x7F, '쌩'}, + {0x80, '썅'}, {0x81, '써'}, {0x82, '썩'}, {0x83, '썬'}, {0x84, '썰'}, {0x85, '썲'}, {0x86, '썸'}, {0x87, '썹'}, {0x88, '썼'}, {0x89, '썽'}, {0x8A, '쎄'}, {0x8B, '쎈'}, {0x8C, '쎌'}, {0x8D, '쏀'}, {0x8E, '쏘'}, {0x8F, '쏙'}, + {0x90, '쏜'}, {0x91, '쏟'}, {0x92, '쏠'}, {0x93, '쏢'}, {0x94, '쏨'}, {0x95, '쏩'}, {0x96, '쏭'}, {0x97, '쏴'}, {0x98, '쏵'}, {0x99, '쏸'}, {0x9A, '쐈'}, {0x9B, '쐐'}, {0x9C, '쐤'}, {0x9D, '쐬'}, {0x9E, '쐰'}, + {0xA0, '쓔'}, {0xA1, '쐴'}, {0xA2, '쐼'}, {0xA3, '쐽'}, {0xA4, '쑈'}, {0xA5, '쑤'}, {0xA6, '쑥'}, {0xA7, '쑨'}, {0xA8, '쑬'}, {0xA9, '쑴'}, {0xAA, '쑵'}, {0xAB, '쑹'}, {0xAC, '쒀'}, {0xAD, '쒔'}, {0xAE, '쒜'}, {0xAF, '쒸'}, + {0xB0, '쒼'}, {0xB1, '쓩'}, {0xB2, '쓰'}, {0xB3, '쓱'}, {0xB4, '쓴'}, {0xB5, '쓸'}, {0xB6, '쓺'}, {0xB7, '쓿'}, {0xB8, '씀'}, {0xB9, '씁'}, {0xBA, '씌'}, {0xBB, '씐'}, {0xBC, '씔'}, {0xBD, '씜'}, {0xBE, '씨'}, {0xBF, '씩'}, + {0xC0, '씬'}, {0xC1, '씰'}, {0xC2, '씸'}, {0xC3, '씹'}, {0xC4, '씻'}, {0xC5, '씽'}, {0xC6, '아'}, {0xC7, '악'}, {0xC8, '안'}, {0xC9, '앉'}, {0xCA, '않'}, {0xCB, '알'}, {0xCC, '앍'}, {0xCD, '앎'}, {0xCE, '앓'}, {0xCF, '암'}, + {0xD0, '압'}, {0xD1, '앗'}, {0xD2, '았'}, {0xD3, '앙'}, {0xD4, '앝'}, {0xD5, '앞'}, {0xD6, '애'}, {0xD7, '액'}, {0xD8, '앤'}, {0xD9, '앨'}, {0xDA, '앰'}, {0xDB, '앱'}, {0xDC, '앳'}, {0xDD, '앴'}, {0xDE, '앵'}, {0xDF, '야'}, + {0xE0, '약'}, {0xE1, '얀'}, {0xE2, '얄'}, {0xE3, '얇'}, {0xE4, '얌'}, {0xE5, '얍'}, {0xE6, '얏'}, {0xE7, '양'}, {0xE8, '얕'}, {0xE9, '얗'}, {0xEA, '얘'}, {0xEB, '얜'}, {0xEC, '얠'}, {0xED, '얩'}, {0xEE, '어'}, {0xEF, '억'}, + {0xF0, '언'}, {0xF1, '얹'}, {0xF2, '얻'}, {0xF3, '얼'}, {0xF4, '얽'}, {0xF5, '얾'}, {0xF6, '엄'}, {0xF7, '업'}, {0xF8, '없'}, {0xF9, '엇'}, {0xFA, '었'}, {0xFB, '엉'}, {0xFC, '엊'}, {0xFD, '엌'}, {0xFE, '엎'}, + }; + + private static readonly Dictionary GSC2U_KOR_7 = new() + { + {0x01, '에'}, {0x02, '엑'}, {0x03, '엔'}, {0x04, '엘'}, {0x05, '엠'}, {0x06, '엡'}, {0x07, '엣'}, {0x08, '엥'}, {0x09, '여'}, {0x0A, '역'}, {0x0B, '엮'}, {0x0C, '연'}, {0x0D, '열'}, {0x0E, '엶'}, {0x0F, '엷'}, + {0x10, '염'}, {0x11, '엽'}, {0x12, '엾'}, {0x13, '엿'}, {0x14, '였'}, {0x15, '영'}, {0x16, '옅'}, {0x17, '옆'}, {0x18, '옇'}, {0x19, '예'}, {0x1A, '옌'}, {0x1B, '옐'}, {0x1C, '옘'}, {0x1D, '옙'}, {0x1E, '옛'}, {0x1F, '옜'}, + {0x20, '오'}, {0x21, '옥'}, {0x22, '온'}, {0x23, '올'}, {0x24, '옭'}, {0x25, '옮'}, {0x26, '옰'}, {0x27, '옳'}, {0x28, '옴'}, {0x29, '옵'}, {0x2A, '옷'}, {0x2B, '옹'}, {0x2C, '옻'}, {0x2D, '와'}, {0x2E, '왁'}, {0x2F, '완'}, + {0x30, '왈'}, {0x31, '왐'}, {0x32, '왑'}, {0x33, '왓'}, {0x34, '왔'}, {0x35, '왕'}, {0x36, '왜'}, {0x37, '왝'}, {0x38, '왠'}, {0x39, '왬'}, {0x3A, '왯'}, {0x3B, '왱'}, {0x3C, '외'}, {0x3D, '왹'}, {0x3E, '왼'}, {0x3F, '욀'}, + {0x40, '욈'}, {0x41, '욉'}, {0x42, '욋'}, {0x43, '욍'}, {0x44, '요'}, {0x45, '욕'}, {0x46, '욘'}, {0x47, '욜'}, {0x48, '욤'}, {0x49, '욥'}, {0x4A, '욧'}, {0x4B, '용'}, {0x4C, '우'}, {0x4D, '욱'}, {0x4E, '운'}, {0x4F, '울'}, + {0x60, '욹'}, {0x61, '욺'}, {0x62, '움'}, {0x63, '웁'}, {0x64, '웃'}, {0x65, '웅'}, {0x66, '워'}, {0x67, '웍'}, {0x68, '원'}, {0x69, '월'}, {0x6A, '웜'}, {0x6B, '웝'}, {0x6C, '웠'}, {0x6D, '웡'}, {0x6E, '웨'}, + {0x71, '웩'}, {0x72, '웬'}, {0x73, '웰'}, {0x74, '웸'}, {0x75, '웹'}, {0x76, '웽'}, {0x77, '위'}, {0x78, '윅'}, {0x79, '윈'}, {0x7A, '윌'}, {0x7B, '윔'}, {0x7C, '윕'}, {0x7D, '윗'}, {0x7E, '윙'}, {0x7F, '유'}, + {0x80, '육'}, {0x81, '윤'}, {0x82, '율'}, {0x83, '윰'}, {0x84, '윱'}, {0x85, '윳'}, {0x86, '융'}, {0x87, '윷'}, {0x88, '으'}, {0x89, '윽'}, {0x8A, '은'}, {0x8B, '을'}, {0x8C, '읆'}, {0x8D, '음'}, {0x8E, '읍'}, {0x8F, '읏'}, + {0x90, '응'}, {0x91, '읒'}, {0x92, '읓'}, {0x93, '읔'}, {0x94, '읕'}, {0x95, '읖'}, {0x96, '읗'}, {0x97, '의'}, {0x98, '읜'}, {0x99, '읠'}, {0x9A, '읨'}, {0x9B, '읫'}, {0x9C, '이'}, {0x9D, '익'}, {0x9E, '인'}, {0x9F, '일'}, + {0xA0, '읽'}, {0xA1, '읾'}, {0xA2, '잃'}, {0xA3, '임'}, {0xA4, '입'}, {0xA5, '잇'}, {0xA6, '있'}, {0xA7, '잉'}, {0xA8, '잊'}, {0xA9, '잎'}, {0xAA, '자'}, {0xAB, '작'}, {0xAC, '잔'}, {0xAD, '잖'}, {0xAE, '잗'}, {0xAF, '잘'}, + {0xB0, '잚'}, {0xB1, '잠'}, {0xB2, '잡'}, {0xB3, '잣'}, {0xB4, '잤'}, {0xB5, '장'}, {0xB6, '잦'}, {0xB7, '재'}, {0xB8, '잭'}, {0xB9, '잰'}, {0xBA, '잴'}, {0xBB, '잼'}, {0xBC, '잽'}, {0xBD, '잿'}, {0xBE, '쟀'}, {0xBF, '쟁'}, + {0xC0, '쟈'}, {0xC1, '쟉'}, {0xC2, '쟌'}, {0xC3, '쟎'}, {0xC4, '쟐'}, {0xC5, '쟘'}, {0xC6, '쟝'}, {0xC7, '쟤'}, {0xC8, '쟨'}, {0xC9, '쟬'}, {0xCA, '저'}, {0xCB, '적'}, {0xCC, '전'}, {0xCD, '절'}, {0xCE, '젊'}, + {0xD1, '점'}, {0xD2, '접'}, {0xD3, '젓'}, {0xD4, '정'}, {0xD5, '젖'}, {0xD6, '제'}, {0xD7, '젝'}, {0xD8, '젠'}, {0xD9, '젤'}, {0xDA, '젬'}, {0xDB, '젭'}, {0xDC, '젯'}, {0xDD, '젱'}, {0xDE, '져'}, {0xDF, '젼'}, + {0xE0, '졀'}, {0xE1, '졈'}, {0xE2, '졉'}, {0xE3, '졌'}, {0xE4, '졍'}, {0xE5, '졔'}, {0xE6, '조'}, {0xE7, '족'}, {0xE8, '존'}, {0xE9, '졸'}, {0xEA, '졺'}, {0xEB, '좀'}, {0xEC, '좁'}, {0xED, '좃'}, {0xEE, '종'}, {0xEF, '좆'}, + {0xF0, '좇'}, {0xF1, '좋'}, {0xF2, '좌'}, {0xF3, '좍'}, {0xF4, '좔'}, {0xF5, '좝'}, {0xF6, '좟'}, {0xF7, '좡'}, {0xF8, '좨'}, {0xF9, '좼'}, {0xFA, '좽'}, {0xFB, '죄'}, {0xFC, '죈'}, {0xFD, '죌'}, {0xFE, '죔'}, {0xFF, '죕'}, + }; + + private static readonly Dictionary GSC2U_KOR_8 = new() + { + {0x00, '죗'}, {0x01, '죙'}, {0x02, '죠'}, {0x03, '죡'}, {0x04, '죤'}, {0x05, '죵'}, {0x06, '주'}, {0x07, '죽'}, {0x08, '준'}, {0x09, '줄'}, {0x0A, '줅'}, {0x0B, '줆'}, {0x0C, '줌'}, {0x0D, '줍'}, {0x0E, '줏'}, {0x0F, '중'}, + {0x10, '줘'}, {0x11, '줬'}, {0x12, '줴'}, {0x13, '쥐'}, {0x14, '쥑'}, {0x15, '쥔'}, {0x16, '쥘'}, {0x17, '쥠'}, {0x18, '쥡'}, {0x19, '쥣'}, {0x1A, '쥬'}, {0x1B, '쥰'}, {0x1C, '쥴'}, {0x1D, '쥼'}, {0x1E, '즈'}, {0x1F, '즉'}, + {0x20, '즌'}, {0x21, '즐'}, {0x22, '즘'}, {0x23, '즙'}, {0x24, '즛'}, {0x25, '증'}, {0x26, '지'}, {0x27, '직'}, {0x28, '진'}, {0x29, '짇'}, {0x2A, '질'}, {0x2B, '짊'}, {0x2C, '짐'}, {0x2D, '집'}, {0x2E, '짓'}, + {0x30, '쬬'}, {0x31, '징'}, {0x32, '짖'}, {0x33, '짙'}, {0x34, '짚'}, {0x35, '짜'}, {0x36, '짝'}, {0x37, '짠'}, {0x38, '짢'}, {0x39, '짤'}, {0x3A, '짧'}, {0x3B, '짬'}, {0x3C, '짭'}, {0x3D, '짯'}, {0x3E, '짰'}, {0x3F, '짱'}, + {0x40, '째'}, {0x41, '짹'}, {0x42, '짼'}, {0x43, '쨀'}, {0x44, '쨈'}, {0x45, '쨉'}, {0x46, '쨋'}, {0x47, '쨌'}, {0x48, '쨍'}, {0x49, '쨔'}, {0x4A, '쨘'}, {0x4B, '쨩'}, {0x4C, '쩌'}, {0x4D, '쩍'}, {0x4E, '쩐'}, {0x4F, '쩔'}, + {0x60, '쩜'}, {0x61, '쩝'}, {0x62, '쩟'}, {0x63, '쩠'}, {0x64, '쩡'}, {0x65, '쩨'}, {0x66, '쩽'}, {0x67, '쪄'}, {0x68, '쪘'}, {0x69, '쪼'}, {0x6A, '쪽'}, {0x6B, '쫀'}, {0x6C, '쫄'}, {0x6D, '쫌'}, {0x6E, '쫍'}, {0x6F, '쫏'}, + {0x70, '쫑'}, {0x71, '쫓'}, {0x72, '쫘'}, {0x73, '쫙'}, {0x74, '쫠'}, {0x75, '쫬'}, {0x76, '쫴'}, {0x77, '쬈'}, {0x78, '쬐'}, {0x79, '쬔'}, {0x7A, '쬘'}, {0x7B, '쬠'}, {0x7C, '쬡'}, {0x7D, '쭁'}, {0x7E, '쭈'}, {0x7F, '쭉'}, + {0x80, '쭌'}, {0x81, '쭐'}, {0x82, '쭘'}, {0x83, '쭙'}, {0x84, '쭝'}, {0x85, '쭤'}, {0x86, '쭸'}, {0x87, '쭹'}, {0x88, '쮜'}, {0x89, '쮸'}, {0x8A, '쯔'}, {0x8B, '쯤'}, {0x8C, '쯧'}, {0x8D, '쯩'}, {0x8E, '찌'}, {0x8F, '찍'}, + {0x90, '찐'}, {0x91, '찔'}, {0x92, '찜'}, {0x93, '찝'}, {0x94, '찡'}, {0x95, '찢'}, {0x96, '찧'}, {0x97, '차'}, {0x98, '착'}, {0x99, '찬'}, {0x9A, '찮'}, {0x9B, '찰'}, {0x9C, '참'}, {0x9D, '찹'}, {0x9E, '찻'}, + {0xA1, '찼'}, {0xA2, '창'}, {0xA3, '찾'}, {0xA4, '채'}, {0xA5, '책'}, {0xA6, '챈'}, {0xA7, '챌'}, {0xA8, '챔'}, {0xA9, '챕'}, {0xAA, '챗'}, {0xAB, '챘'}, {0xAC, '챙'}, {0xAD, '챠'}, {0xAE, '챤'}, {0xAF, '챦'}, + {0xB0, '챨'}, {0xB1, '챰'}, {0xB2, '챵'}, {0xB3, '처'}, {0xB4, '척'}, {0xB5, '천'}, {0xB6, '철'}, {0xB7, '첨'}, {0xB8, '첩'}, {0xB9, '첫'}, {0xBA, '첬'}, {0xBB, '청'}, {0xBC, '체'}, {0xBD, '첵'}, {0xBE, '첸'}, {0xBF, '첼'}, + {0xC0, '쳄'}, {0xC1, '쳅'}, {0xC2, '쳇'}, {0xC3, '쳉'}, {0xC4, '쳐'}, {0xC5, '쳔'}, {0xC6, '쳤'}, {0xC7, '쳬'}, {0xC8, '쳰'}, {0xC9, '촁'}, {0xCA, '초'}, {0xCB, '촉'}, {0xCC, '촌'}, {0xCD, '촐'}, {0xCE, '촘'}, {0xCF, '촙'}, + {0xD0, '촛'}, {0xD1, '총'}, {0xD2, '촤'}, {0xD3, '촨'}, {0xD4, '촬'}, {0xD5, '촹'}, {0xD6, '최'}, {0xD7, '쵠'}, {0xD8, '쵤'}, {0xD9, '쵬'}, {0xDA, '쵭'}, {0xDB, '쵯'}, {0xDC, '쵱'}, {0xDD, '쵸'}, {0xDE, '춈'}, {0xDF, '추'}, + {0xE0, '축'}, {0xE1, '춘'}, {0xE2, '출'}, {0xE3, '춤'}, {0xE4, '춥'}, {0xE5, '춧'}, {0xE6, '충'}, {0xE7, '춰'}, {0xE8, '췄'}, {0xE9, '췌'}, {0xEA, '췐'}, {0xEB, '취'}, {0xEC, '췬'}, {0xED, '췰'}, {0xEE, '췸'}, {0xEF, '췹'}, + {0xF0, '췻'}, {0xF1, '췽'}, {0xF2, '츄'}, {0xF3, '츈'}, {0xF4, '츌'}, {0xF5, '츔'}, {0xF6, '츙'}, {0xF7, '츠'}, {0xF8, '측'}, {0xF9, '츤'}, {0xFA, '츨'}, {0xFB, '츰'}, {0xFC, '츱'}, {0xFD, '츳'}, {0xFE, '층'}, + }; + + private static readonly Dictionary GSC2U_KOR_9 = new() + { + {0x01, '치'}, {0x02, '칙'}, {0x03, '친'}, {0x04, '칟'}, {0x05, '칠'}, {0x06, '칡'}, {0x07, '침'}, {0x08, '칩'}, {0x09, '칫'}, {0x0A, '칭'}, {0x0B, '카'}, {0x0C, '칵'}, {0x0D, '칸'}, {0x0E, '칼'}, {0x0F, '캄'}, + {0x10, '캅'}, {0x11, '캇'}, {0x12, '캉'}, {0x13, '캐'}, {0x14, '캑'}, {0x15, '캔'}, {0x16, '캘'}, {0x17, '캠'}, {0x18, '캡'}, {0x19, '캣'}, {0x1A, '캤'}, {0x1B, '캥'}, {0x1C, '캬'}, {0x1D, '캭'}, {0x1E, '컁'}, {0x1F, '커'}, + {0x20, '컥'}, {0x21, '컨'}, {0x22, '컫'}, {0x23, '컬'}, {0x24, '컴'}, {0x25, '컵'}, {0x26, '컷'}, {0x27, '컸'}, {0x28, '컹'}, {0x29, '케'}, {0x2A, '켁'}, {0x2B, '켄'}, {0x2C, '켈'}, {0x2D, '켐'}, {0x2E, '켑'}, {0x2F, '켓'}, + {0x30, '켕'}, {0x31, '켜'}, {0x32, '켠'}, {0x33, '켤'}, {0x34, '켬'}, {0x35, '켭'}, {0x36, '켯'}, {0x37, '켰'}, {0x38, '켱'}, {0x39, '켸'}, {0x3A, '코'}, {0x3B, '콕'}, {0x3C, '콘'}, {0x3D, '콜'}, {0x3E, '콤'}, {0x3F, '콥'}, + {0x40, '콧'}, {0x41, '콩'}, {0x42, '콰'}, {0x43, '콱'}, {0x44, '콴'}, {0x45, '콸'}, {0x46, '쾀'}, {0x47, '쾅'}, {0x48, '쾌'}, {0x49, '쾡'}, {0x4A, '쾨'}, {0x4B, '쾰'}, {0x4C, '쿄'}, {0x4D, '쿠'}, {0x4E, '쿡'}, {0x4F, '쿤'}, + {0x60, '쿨'}, {0x61, '쿰'}, {0x62, '쿱'}, {0x63, '쿳'}, {0x64, '쿵'}, {0x65, '쿼'}, {0x66, '퀀'}, {0x67, '퀄'}, {0x68, '퀑'}, {0x69, '퀘'}, {0x6A, '퀭'}, {0x6B, '퀴'}, {0x6C, '퀵'}, {0x6D, '퀸'}, {0x6E, '퀼'}, + {0x71, '큄'}, {0x72, '큅'}, {0x73, '큇'}, {0x74, '큉'}, {0x75, '큐'}, {0x76, '큔'}, {0x77, '큘'}, {0x78, '큠'}, {0x79, '크'}, {0x7A, '큭'}, {0x7B, '큰'}, {0x7C, '클'}, {0x7D, '큼'}, {0x7E, '큽'}, {0x7F, '킁'}, + {0x80, '키'}, {0x81, '킥'}, {0x82, '킨'}, {0x83, '킬'}, {0x84, '킴'}, {0x85, '킵'}, {0x86, '킷'}, {0x87, '킹'}, {0x88, '타'}, {0x89, '탁'}, {0x8A, '탄'}, {0x8B, '탈'}, {0x8C, '탉'}, {0x8D, '탐'}, {0x8E, '탑'}, {0x8F, '탓'}, + {0x90, '탔'}, {0x91, '탕'}, {0x92, '태'}, {0x93, '택'}, {0x94, '탠'}, {0x95, '탤'}, {0x96, '탬'}, {0x97, '탭'}, {0x98, '탯'}, {0x99, '탰'}, {0x9A, '탱'}, {0x9B, '탸'}, {0x9C, '턍'}, {0x9D, '터'}, {0x9E, '턱'}, {0x9F, '턴'}, + {0xA0, '털'}, {0xA1, '턺'}, {0xA2, '텀'}, {0xA3, '텁'}, {0xA4, '텃'}, {0xA5, '텄'}, {0xA6, '텅'}, {0xA7, '테'}, {0xA8, '텍'}, {0xA9, '텐'}, {0xAA, '텔'}, {0xAB, '템'}, {0xAC, '텝'}, {0xAD, '텟'}, {0xAE, '텡'}, {0xAF, '텨'}, + {0xB0, '텬'}, {0xB1, '텼'}, {0xB2, '톄'}, {0xB3, '톈'}, {0xB4, '토'}, {0xB5, '톡'}, {0xB6, '톤'}, {0xB7, '톨'}, {0xB8, '톰'}, {0xB9, '톱'}, {0xBA, '톳'}, {0xBB, '통'}, {0xBC, '톺'}, {0xBD, '톼'}, {0xBE, '퇀'}, {0xBF, '퇘'}, + {0xC0, '퇴'}, {0xC1, '퇸'}, {0xC2, '툇'}, {0xC3, '툉'}, {0xC4, '툐'}, {0xC5, '투'}, {0xC6, '툭'}, {0xC7, '툰'}, {0xC8, '툴'}, {0xC9, '툼'}, {0xCA, '툽'}, {0xCB, '툿'}, {0xCC, '퉁'}, {0xCD, '퉈'}, {0xCE, '퉜'}, + {0xD1, '퉤'}, {0xD2, '튀'}, {0xD3, '튁'}, {0xD4, '튄'}, {0xD5, '튈'}, {0xD6, '튐'}, {0xD7, '튑'}, {0xD8, '튕'}, {0xD9, '튜'}, {0xDA, '튠'}, {0xDB, '튤'}, {0xDC, '튬'}, {0xDD, '튱'}, {0xDE, '트'}, {0xDF, '특'}, + {0xE0, '튼'}, {0xE1, '튿'}, {0xE2, '틀'}, {0xE3, '틂'}, {0xE4, '틈'}, {0xE5, '틉'}, {0xE6, '틋'}, {0xE7, '틔'}, {0xE8, '틘'}, {0xE9, '틜'}, {0xEA, '틤'}, {0xEB, '틥'}, {0xEC, '티'}, {0xED, '틱'}, {0xEE, '틴'}, {0xEF, '틸'}, + {0xF0, '팀'}, {0xF1, '팁'}, {0xF2, '팃'}, {0xF3, '팅'}, {0xF4, '파'}, {0xF5, '팍'}, {0xF6, '팎'}, {0xF7, '판'}, {0xF8, '팔'}, {0xF9, '팖'}, {0xFA, '팜'}, {0xFB, '팝'}, {0xFC, '팟'}, {0xFD, '팠'}, {0xFE, '팡'}, {0xFF, '팥'}, + }; + + private static readonly Dictionary GSC2U_KOR_A = new() + { + {0x00, '패'}, {0x01, '팩'}, {0x02, '팬'}, {0x03, '팰'}, {0x04, '팸'}, {0x05, '팹'}, {0x06, '팻'}, {0x07, '팼'}, {0x08, '팽'}, {0x09, '퍄'}, {0x0A, '퍅'}, {0x0B, '퍼'}, {0x0C, '퍽'}, {0x0D, '펀'}, {0x0E, '펄'}, {0x0F, '펌'}, + {0x10, '펍'}, {0x11, '펏'}, {0x12, '펐'}, {0x13, '펑'}, {0x14, '페'}, {0x15, '펙'}, {0x16, '펜'}, {0x17, '펠'}, {0x18, '펨'}, {0x19, '펩'}, {0x1A, '펫'}, {0x1B, '펭'}, {0x1C, '펴'}, {0x1D, '편'}, {0x1E, '펼'}, {0x1F, '폄'}, + {0x20, '폅'}, {0x21, '폈'}, {0x22, '평'}, {0x23, '폐'}, {0x24, '폘'}, {0x25, '폡'}, {0x26, '폣'}, {0x27, '포'}, {0x28, '폭'}, {0x29, '폰'}, {0x2A, '폴'}, {0x2B, '폼'}, {0x2C, '폽'}, {0x2D, '폿'}, {0x2E, '퐁'}, + {0x31, '퐈'}, {0x32, '퐝'}, {0x33, '푀'}, {0x34, '푄'}, {0x35, '표'}, {0x36, '푠'}, {0x37, '푤'}, {0x38, '푭'}, {0x39, '푯'}, {0x3A, '푸'}, {0x3B, '푹'}, {0x3C, '푼'}, {0x3D, '푿'}, {0x3E, '풀'}, {0x3F, '풂'}, + {0x40, '품'}, {0x41, '풉'}, {0x42, '풋'}, {0x43, '풍'}, {0x44, '풔'}, {0x45, '풩'}, {0x46, '퓌'}, {0x47, '퓐'}, {0x48, '퓔'}, {0x49, '퓜'}, {0x4A, '퓟'}, {0x4B, '퓨'}, {0x4C, '퓬'}, {0x4D, '퓰'}, {0x4E, '퓸'}, {0x4F, '퓻'}, + {0x60, '퓽'}, {0x61, '프'}, {0x62, '픈'}, {0x63, '플'}, {0x64, '픔'}, {0x65, '픕'}, {0x66, '픗'}, {0x67, '피'}, {0x68, '픽'}, {0x69, '핀'}, {0x6A, '필'}, {0x6B, '핌'}, {0x6C, '핍'}, {0x6D, '핏'}, {0x6E, '핑'}, {0x6F, '하'}, + {0x70, '학'}, {0x71, '한'}, {0x72, '할'}, {0x73, '핥'}, {0x74, '함'}, {0x75, '합'}, {0x76, '핫'}, {0x77, '항'}, {0x78, '해'}, {0x79, '핵'}, {0x7A, '핸'}, {0x7B, '핼'}, {0x7C, '햄'}, {0x7D, '햅'}, {0x7E, '햇'}, {0x7F, '했'}, + {0x80, '행'}, {0x81, '햐'}, {0x82, '향'}, {0x83, '허'}, {0x84, '헉'}, {0x85, '헌'}, {0x86, '헐'}, {0x87, '헒'}, {0x88, '험'}, {0x89, '헙'}, {0x8A, '헛'}, {0x8B, '헝'}, {0x8C, '헤'}, {0x8D, '헥'}, {0x8E, '헨'}, {0x8F, '헬'}, + {0x90, '헴'}, {0x91, '헵'}, {0x92, '헷'}, {0x93, '헹'}, {0x94, '혀'}, {0x95, '혁'}, {0x96, '현'}, {0x97, '혈'}, {0x98, '혐'}, {0x99, '협'}, {0x9A, '혓'}, {0x9B, '혔'}, {0x9C, '형'}, {0x9D, '혜'}, {0x9E, '혠'}, + {0xA1, '혤'}, {0xA2, '혭'}, {0xA3, '호'}, {0xA4, '혹'}, {0xA5, '혼'}, {0xA6, '홀'}, {0xA7, '홅'}, {0xA8, '홈'}, {0xA9, '홉'}, {0xAA, '홋'}, {0xAB, '홍'}, {0xAC, '홑'}, {0xAD, '화'}, {0xAE, '확'}, {0xAF, '환'}, + {0xB0, '활'}, {0xB1, '홧'}, {0xB2, '황'}, {0xB3, '홰'}, {0xB4, '홱'}, {0xB5, '홴'}, {0xB6, '횃'}, {0xB7, '횅'}, {0xB8, '회'}, {0xB9, '획'}, {0xBA, '횐'}, {0xBB, '횔'}, {0xBC, '횝'}, {0xBD, '횟'}, {0xBE, '횡'}, {0xBF, '효'}, + {0xC0, '횬'}, {0xC1, '횰'}, {0xC2, '횹'}, {0xC3, '횻'}, {0xC4, '후'}, {0xC5, '훅'}, {0xC6, '훈'}, {0xC7, '훌'}, {0xC8, '훑'}, {0xC9, '훔'}, {0xCA, '훗'}, {0xCB, '훙'}, {0xCC, '훠'}, {0xCD, '훤'}, {0xCE, '훨'}, {0xCF, '훰'}, + {0xD0, '훵'}, {0xD1, '훼'}, {0xD2, '훽'}, {0xD3, '휀'}, {0xD4, '휄'}, {0xD5, '휑'}, {0xD6, '휘'}, {0xD7, '휙'}, {0xD8, '휜'}, {0xD9, '휠'}, {0xDA, '휨'}, {0xDB, '휩'}, {0xDC, '휫'}, {0xDD, '휭'}, {0xDE, '휴'}, {0xDF, '휵'}, + {0xE0, '휸'}, {0xE1, '휼'}, {0xE2, '흄'}, {0xE3, '흇'}, {0xE4, '흉'}, {0xE5, '흐'}, {0xE6, '흑'}, {0xE7, '흔'}, {0xE8, '흖'}, {0xE9, '흗'}, {0xEA, '흘'}, {0xEB, '흙'}, {0xEC, '흠'}, {0xED, '흡'}, {0xEE, '흣'}, {0xEF, '흥'}, + {0xF0, '흩'}, {0xF1, '희'}, {0xF2, '흰'}, {0xF3, '흴'}, {0xF4, '흼'}, {0xF5, '흽'}, {0xF6, '힁'}, {0xF7, '히'}, {0xF8, '힉'}, {0xF9, '힌'}, {0xFA, '힐'}, {0xFB, '힘'}, {0xFC, '힙'}, {0xFD, '힛'}, {0xFE, '힝'}, + }; + + private static readonly Dictionary GSC2U_KOR_B = GSC2U_KOR_0; + + /// Unicode codepoint => Gen2 value dictionary pages. + private static readonly Dictionary[] U2GSC_KOR = + { + U2GSC_KOR_0, U2GSC_KOR_1, U2GSC_KOR_2, U2GSC_KOR_3, + U2GSC_KOR_4, U2GSC_KOR_5, U2GSC_KOR_6, U2GSC_KOR_7, + U2GSC_KOR_8, U2GSC_KOR_9, U2GSC_KOR_A, U2GSC_KOR_B, + }; + + /// Gen2 value => Unicode codepoint dictionary pages. + private static readonly Dictionary[] GSC2U_KOR = + { + GSC2U_KOR_0, GSC2U_KOR_1, GSC2U_KOR_2, GSC2U_KOR_3, + GSC2U_KOR_4, GSC2U_KOR_5, GSC2U_KOR_6, GSC2U_KOR_7, + GSC2U_KOR_8, GSC2U_KOR_9, GSC2U_KOR_A, GSC2U_KOR_B, + }; + + #endregion } diff --git a/PKHeX.Core/PKM/Strings/StringConverter3.cs b/PKHeX.Core/PKM/Strings/StringConverter3.cs index c1024842a..41cc5e83b 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter3.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter3.cs @@ -1,196 +1,144 @@ using System; -using System.Text; -namespace PKHeX.Core +namespace PKHeX.Core; + +/// +/// Logic for converting a for Generation 3. +/// +public static class StringConverter3 { + private const byte TerminatorByte = 0xFF; + private const char Terminator = (char)TerminatorByte; + /// - /// Logic for converting a for Generation 3. + /// Converts a Generation 3 encoded value array to string. /// - public static class StringConverter3 + /// Byte array containing string data. + /// Value source is Japanese font. + /// Decoded string. + public static string GetString(ReadOnlySpan data, bool jp) { - private const byte TerminatorByte = 0xFF; - private const char Terminator = (char)TerminatorByte; - private const char TerminatorBigEndian = (char)0; // GC - - /// - /// Converts a Generation 3 encoded value array to string. - /// - /// Byte array containing string data. - /// Offset to read from - /// Length of data to read. - /// Value source is Japanese font. - /// Decoded string. - public static string GetString3(byte[] data, int offset, int count, bool jp) => GetString3(data.AsSpan(offset, count), jp); - - /// - /// Converts a Generation 3 encoded value array to string. - /// - /// Byte array containing string data. - /// Value source is Japanese font. - /// Decoded string. - public static string GetString3(ReadOnlySpan data, bool jp) + Span result = stackalloc char[data.Length]; + int i = 0; + for (; i < data.Length; i++) { - var s = new StringBuilder(data.Length); - foreach (var val in data) - { - var c = GetG3Char(val, jp); // Convert to Unicode - if (c == Terminator) // Stop if Terminator/Invalid - break; - s.Append(c); - } - StringConverter.SanitizeString(s); - return s.ToString(); + var value = data[i]; + var c = GetG3Char(value, jp); // Convert to Unicode + if (c == Terminator) // Stop if Terminator/Invalid + break; + result[i] = c; } - - /// - /// Converts a string to a Generation 3 encoded value array. - /// - /// Decoded string. - /// Maximum length of the input - /// String destination is Japanese font. - /// Pad the input to given length - /// Pad the input with this character value - /// Encoded data. - public static byte[] SetString3(string value, int maxLength, bool jp, int padTo = 0, ushort padWith = 0) - { - if (value.Length > maxLength) - value = value[..maxLength]; // Hard cap - var data = new byte[value.Length + 1]; // +1 for Terminator - for (int i = 0; i < value.Length; i++) - { - var chr = value[i]; - var val = SetG3Char(chr, jp); - if (val == Terminator) // end - { - Array.Resize(ref data, i + 1); - break; - } - data[i] = val; - } - if (data.Length > 0) - data[^1] = TerminatorByte; - if (data.Length > maxLength && padTo <= maxLength) - { - // Truncate - Array.Resize(ref data, maxLength); - return data; - } - if (data.Length < padTo) - { - var start = data.Length; - Array.Resize(ref data, padTo); - for (int i = start; i < data.Length; i++) - data[i] = (byte)padWith; - } - return data; - } - - /// Converts Big Endian encoded data to decoded string. - /// Encoded data - /// Offset to read from - /// Length of data to read. - /// Decoded string. - public static string GetBEString3(byte[] data, int offset, int count) - { - var raw = Encoding.BigEndianUnicode.GetString(data, offset, count); - var sb = new StringBuilder(raw); - Util.TrimFromFirst(sb, TerminatorBigEndian); - return sb.ToString(); - } - - /// Gets the bytes for a Big Endian string. - /// Decoded string. - /// Maximum length of the input - /// Pad the input to given length - /// Pad the input with this character value - /// Encoded data. - public static byte[] SetBEString3(string value, int maxLength, int padTo = 0, ushort padWith = TerminatorBigEndian) - { - if (value.Length > maxLength) - value = value[..maxLength]; // Hard cap - var sb = new StringBuilder(value); - StringConverter.SanitizeString(sb); - sb.Append(TerminatorBigEndian); - var delta = padTo - value.Length; - if (delta > 0) - sb.Append((char)padWith, delta); - var result = sb.ToString(); - return Encoding.BigEndianUnicode.GetBytes(result); - } - - /// - /// Decodes a character from a Generation 3 encoded value. - /// - /// Generation 4 decoded character. - /// Character destination is Japanese font. - /// Generation 3 encoded value. - private static char GetG3Char(byte chr, bool jp) - { - var table = jp ? G3_JP : G3_EN; - return table[chr]; - } - - /// - /// Encodes a character to a Generation 3 encoded value. - /// - /// Generation 4 decoded character. - /// Character destination is Japanese font. - /// Generation 3 encoded value. - private static byte SetG3Char(char chr, bool jp) - { - if (chr == '\'') // ’ - return 0xB4; - var table = jp ? G3_JP : G3_EN; - var index = Array.IndexOf(table, chr); - if (index == -1) - return TerminatorByte; - return (byte)index; - } - - private static readonly char[] G3_EN = - { - ' ', 'À', 'Á', 'Â', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'こ', 'Î', 'Ï', 'Ò', 'Ó', 'Ô', // 0 - 'Œ', 'Ù', 'Ú', 'Û', 'Ñ', 'ß', 'à', 'á', 'ね', 'Ç', 'È', 'é', 'ê', 'ë', 'ì', 'í', // 1 - 'î', 'ï', 'ò', 'ó', 'ô', 'œ', 'ù', 'ú', 'û', 'ñ', 'º', 'ª', '⒅', '&', '+', 'あ', // 2 - 'ぃ', 'ぅ', 'ぇ', 'ぉ', 'ゃ', '=', 'ょ', 'が', 'ぎ', 'ぐ', 'げ', 'ご', 'ざ', 'じ', 'ず', 'ぜ', // 3 - 'ぞ', 'だ', 'ぢ', 'づ', 'で', 'ど', 'ば', 'び', 'ぶ', 'べ', 'ぼ', 'ぱ', 'ぴ', 'ぷ', 'ぺ', 'ぽ', // 4 - 'っ', '¿', '¡', '⒆', '⒇', 'オ', 'カ', 'キ', 'ク', 'ケ', 'Í', 'コ', 'サ', 'ス', 'セ', 'ソ', // 5 - 'タ', 'チ', 'ツ', 'テ', 'ト', 'ナ', 'ニ', 'ヌ', 'â', 'ノ', 'ハ', 'ヒ', 'フ', 'ヘ', 'ホ', 'í', // 6 - 'ミ', 'ム', 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', 'ワ', 'ヲ', 'ン', 'ァ', // 7 - 'ィ', 'ゥ', 'ェ', 'ォ', 'ャ', 'ュ', 'ョ', 'ガ', 'ギ', 'グ', 'ゲ', 'ゴ', 'ザ', 'ジ', 'ズ', 'ゼ', // 8 - 'ゾ', 'ダ', 'ヂ', 'ヅ', 'デ', 'ド', 'バ', 'ビ', 'ブ', 'ベ', 'ボ', 'パ', 'ピ', 'プ', 'ペ', 'ポ', // 9 - 'ッ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '?', '.', '-', '・',// A - '⑬', '“', '”', '‘', '’', '♂', '♀', '$', ',', '⑧', '/', 'A', 'B', 'C', 'D', 'E', // B - 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', // C - 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', // D - 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', // E - ':', 'Ä', 'Ö', 'Ü', 'ä', 'ö', 'ü', // F - - // Make the total length 256 so that any byte access is always within the array - Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, - }; - - private static readonly char[] G3_JP = - { - ' ', 'あ', 'い', 'う', 'え', 'お', 'か', 'き', 'く', 'け', 'こ', 'さ', 'し', 'す', 'せ', 'そ', // 0 - 'た', 'ち', 'つ', 'て', 'と', 'な', 'に', 'ぬ', 'ね', 'の', 'は', 'ひ', 'ふ', 'へ', 'ほ', 'ま', // 1 - 'み', 'む', 'め', 'も', 'や', 'ゆ', 'よ', 'ら', 'り', 'る', 'れ', 'ろ', 'わ', 'を', 'ん', 'ぁ', // 2 - 'ぃ', 'ぅ', 'ぇ', 'ぉ', 'ゃ', 'ゅ', 'ょ', 'が', 'ぎ', 'ぐ', 'げ', 'ご', 'ざ', 'じ', 'ず', 'ぜ', // 3 - 'ぞ', 'だ', 'ぢ', 'づ', 'で', 'ど', 'ば', 'び', 'ぶ', 'べ', 'ぼ', 'ぱ', 'ぴ', 'ぷ', 'ぺ', 'ぽ', // 4 - 'っ', 'ア', 'イ', 'ウ', 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ', 'サ', 'シ', 'ス', 'セ', 'ソ', // 5 - 'タ', 'チ', 'ツ', 'テ', 'ト', 'ナ', 'ニ', 'ヌ', 'ネ', 'ノ', 'ハ', 'ヒ', 'フ', 'ヘ', 'ホ', 'マ', // 6 - 'ミ', 'ム', 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', 'ワ', 'ヲ', 'ン', 'ァ', // 7 - 'ィ', 'ゥ', 'ェ', 'ォ', 'ャ', 'ュ', 'ョ', 'ガ', 'ギ', 'グ', 'ゲ', 'ゴ', 'ザ', 'ジ', 'ズ', 'ゼ', // 8 - 'ゾ', 'ダ', 'ヂ', 'ヅ', 'デ', 'ド', 'バ', 'ビ', 'ブ', 'ベ', 'ボ', 'パ', 'ピ', 'プ', 'ペ', 'ポ', // 9 - 'ッ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '?', '。', 'ー', '・', // A - '⋯', '『', '』', '「', '」', '♂', '♀', '$', '.', '⑧', '/', 'A', 'B', 'C', 'D', 'E', // B - 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', // C - 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', // D - 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', // E - ':', 'Ä', 'Ö', 'Ü', 'ä', 'ö', 'ü', // F - - // Make the total length 256 so that any byte access is always within the array - Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, - }; + return new string(result[..i].ToArray()); } + + /// + /// Converts a string to a Generation 3 encoded value array. + /// + /// + /// Decoded string. + /// Maximum length of the input + /// String destination is Japanese font. + /// + /// Encoded data. + public static int SetString(Span buffer, ReadOnlySpan value, int maxLength, bool jp, + StringConverterOption option = StringConverterOption.ClearFF) + { + if (value.Length > maxLength) + value = value[..maxLength]; // Hard cap + + if (option is StringConverterOption.ClearFF) + buffer.Fill(0xFF); + else if (option is StringConverterOption.ClearZero) + buffer.Clear(); + + int i = 0; + for (; i < value.Length; i++) + { + var chr = value[i]; + var b = SetG3Char(chr, jp); + if (b == TerminatorByte) + break; + buffer[i] = b; + } + + int count = i; + if (count < buffer.Length) + buffer[count++] = TerminatorByte; + return count; + } + + /// + /// Decodes a character from a Generation 3 encoded value. + /// + /// Generation 4 decoded character. + /// Character destination is Japanese font. + /// Generation 3 encoded value. + private static char GetG3Char(byte chr, bool jp) + { + var table = jp ? G3_JP : G3_EN; + return table[chr]; + } + + /// + /// Encodes a character to a Generation 3 encoded value. + /// + /// Generation 4 decoded character. + /// Character destination is Japanese font. + /// Generation 3 encoded value. + private static byte SetG3Char(char chr, bool jp) + { + if (chr == '\'') // ’ + return 0xB4; + var table = jp ? G3_JP : G3_EN; + var index = Array.IndexOf(table, chr); + if (index == -1) + return TerminatorByte; + return (byte)index; + } + + private static readonly char[] G3_EN = + { + ' ', 'À', 'Á', 'Â', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'こ', 'Î', 'Ï', 'Ò', 'Ó', 'Ô', // 0 + 'Œ', 'Ù', 'Ú', 'Û', 'Ñ', 'ß', 'à', 'á', 'ね', 'Ç', 'È', 'é', 'ê', 'ë', 'ì', 'í', // 1 + 'î', 'ï', 'ò', 'ó', 'ô', 'œ', 'ù', 'ú', 'û', 'ñ', 'º', 'ª', '⒅', '&', '+', 'あ', // 2 + 'ぃ', 'ぅ', 'ぇ', 'ぉ', 'ゃ', '=', 'ょ', 'が', 'ぎ', 'ぐ', 'げ', 'ご', 'ざ', 'じ', 'ず', 'ぜ', // 3 + 'ぞ', 'だ', 'ぢ', 'づ', 'で', 'ど', 'ば', 'び', 'ぶ', 'べ', 'ぼ', 'ぱ', 'ぴ', 'ぷ', 'ぺ', 'ぽ', // 4 + 'っ', '¿', '¡', '⒆', '⒇', 'オ', 'カ', 'キ', 'ク', 'ケ', 'Í', 'コ', 'サ', 'ス', 'セ', 'ソ', // 5 + 'タ', 'チ', 'ツ', 'テ', 'ト', 'ナ', 'ニ', 'ヌ', 'â', 'ノ', 'ハ', 'ヒ', 'フ', 'ヘ', 'ホ', 'í', // 6 + 'ミ', 'ム', 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', 'ワ', 'ヲ', 'ン', 'ァ', // 7 + 'ィ', 'ゥ', 'ェ', 'ォ', 'ャ', 'ュ', 'ョ', 'ガ', 'ギ', 'グ', 'ゲ', 'ゴ', 'ザ', 'ジ', 'ズ', 'ゼ', // 8 + 'ゾ', 'ダ', 'ヂ', 'ヅ', 'デ', 'ド', 'バ', 'ビ', 'ブ', 'ベ', 'ボ', 'パ', 'ピ', 'プ', 'ペ', 'ポ', // 9 + 'ッ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '?', '.', '-', '・',// A + '⑬', '“', '”', '‘', '’', '♂', '♀', '$', ',', '⑧', '/', 'A', 'B', 'C', 'D', 'E', // B + 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', // C + 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', // D + 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', // E + ':', 'Ä', 'Ö', 'Ü', 'ä', 'ö', 'ü', // F + + // Make the total length 256 so that any byte access is always within the array + Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, + }; + + private static readonly char[] G3_JP = + { + ' ', 'あ', 'い', 'う', 'え', 'お', 'か', 'き', 'く', 'け', 'こ', 'さ', 'し', 'す', 'せ', 'そ', // 0 + 'た', 'ち', 'つ', 'て', 'と', 'な', 'に', 'ぬ', 'ね', 'の', 'は', 'ひ', 'ふ', 'へ', 'ほ', 'ま', // 1 + 'み', 'む', 'め', 'も', 'や', 'ゆ', 'よ', 'ら', 'り', 'る', 'れ', 'ろ', 'わ', 'を', 'ん', 'ぁ', // 2 + 'ぃ', 'ぅ', 'ぇ', 'ぉ', 'ゃ', 'ゅ', 'ょ', 'が', 'ぎ', 'ぐ', 'げ', 'ご', 'ざ', 'じ', 'ず', 'ぜ', // 3 + 'ぞ', 'だ', 'ぢ', 'づ', 'で', 'ど', 'ば', 'び', 'ぶ', 'べ', 'ぼ', 'ぱ', 'ぴ', 'ぷ', 'ぺ', 'ぽ', // 4 + 'っ', 'ア', 'イ', 'ウ', 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ', 'サ', 'シ', 'ス', 'セ', 'ソ', // 5 + 'タ', 'チ', 'ツ', 'テ', 'ト', 'ナ', 'ニ', 'ヌ', 'ネ', 'ノ', 'ハ', 'ヒ', 'フ', 'ヘ', 'ホ', 'マ', // 6 + 'ミ', 'ム', 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', 'ワ', 'ヲ', 'ン', 'ァ', // 7 + 'ィ', 'ゥ', 'ェ', 'ォ', 'ャ', 'ュ', 'ョ', 'ガ', 'ギ', 'グ', 'ゲ', 'ゴ', 'ザ', 'ジ', 'ズ', 'ゼ', // 8 + 'ゾ', 'ダ', 'ヂ', 'ヅ', 'デ', 'ド', 'バ', 'ビ', 'ブ', 'ベ', 'ボ', 'パ', 'ピ', 'プ', 'ペ', 'ポ', // 9 + 'ッ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '?', '。', 'ー', '・', // A + '⋯', '『', '』', '「', '」', '♂', '♀', '$', '.', '⑧', '/', 'A', 'B', 'C', 'D', 'E', // B + 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', // C + 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', // D + 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', // E + ':', 'Ä', 'Ö', 'Ü', 'ä', 'ö', 'ü', // F + + // Make the total length 256 so that any byte access is always within the array + Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, Terminator, + }; } diff --git a/PKHeX.Core/PKM/Strings/StringConverter345.cs b/PKHeX.Core/PKM/Strings/StringConverter345.cs index 36937bd90..18e32d23b 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter345.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter345.cs @@ -1,67 +1,66 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; -namespace PKHeX.Core +namespace PKHeX.Core; + +/// +/// Logic for converting a for to future Generations. +/// +public static class StringConverter345 { /// - /// Logic for converting a for to future Generations. + /// Trash Bytes for Generation 3->4 /// - public static class StringConverter345 + /// String buffers are reused, data is not cleared which yields the trash bytes. + public static readonly byte[][] G4TransferTrashBytes = { + Array.Empty(), // Unused + new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + new byte[] { 0x18, 0x20, 0x0D, 0x02, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xA1, 0x0C, 0x02, 0xE0, 0xFF }, + new byte[] { 0x74, 0x20, 0x0D, 0x02, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA1, 0x0C, 0x02, 0xE0, 0xFF }, + new byte[] { 0x54, 0x20, 0x0D, 0x02, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0xA1, 0x0C, 0x02, 0xE0, 0xFF }, + new byte[] { 0x74, 0x20, 0x0D, 0x02, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA1, 0x0C, 0x02, 0xE0, 0xFF }, + Array.Empty(), // Unused + new byte[] { 0x74, 0x20, 0x0D, 0x02, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA1, 0x0C, 0x02, 0xE0, 0xFF }, + }; + + /// + /// Remaps Gen5 Glyphs to unicode codepoint. + /// + /// Input characters to transfer in place + /// Remapped string + public static void TransferGlyphs56(Span buffer) { - /// - /// Trash Bytes for Generation 3->4 - /// - /// String buffers are reused, data is not cleared which yields the trash bytes. - public static readonly byte[][] G4TransferTrashBytes = { - Array.Empty(), // Unused - new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - new byte[] { 0x18, 0x20, 0x0D, 0x02, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xA1, 0x0C, 0x02, 0xE0, 0xFF }, - new byte[] { 0x74, 0x20, 0x0D, 0x02, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA1, 0x0C, 0x02, 0xE0, 0xFF }, - new byte[] { 0x54, 0x20, 0x0D, 0x02, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0xA1, 0x0C, 0x02, 0xE0, 0xFF }, - new byte[] { 0x74, 0x20, 0x0D, 0x02, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA1, 0x0C, 0x02, 0xE0, 0xFF }, - Array.Empty(), // Unused - new byte[] { 0x74, 0x20, 0x0D, 0x02, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA1, 0x0C, 0x02, 0xE0, 0xFF }, - }; - - /// - /// Remaps Gen5 Glyphs to unicode codepoint. - /// - /// Input string - /// Remapped string - public static string TransferGlyphs56(string str) + var table = Glyph56; + for (int i = 0; i < buffer.Length; i += 2) { - var result = new char[str.Length]; - var table = Glyph56; - for (int i = 0; i < str.Length; i++) - { - var c = str[i]; - result[i] = table.TryGetValue(c, out var translated) ? translated : c; - } - return new string(result); + var c = (char)ReadUInt16LittleEndian(buffer[i..]); + if (table.TryGetValue(c, out c)) + WriteUInt16LittleEndian(buffer[i..], c); } - - private static readonly Dictionary Glyph56 = new() - { - {'\u2467', '\u00d7'}, // × - {'\u2468', '\u00f7'}, // ÷ - - {'\u246c', '\uE08D'}, // … - {'\u246d', '\uE08E'}, // ♂ - {'\u246e', '\uE08F'}, // ♀ - {'\u246f', '\uE090'}, // ♠ - {'\u2470', '\uE091'}, // ♣ - {'\u2471', '\uE092'}, // ♥ - {'\u2472', '\uE093'}, // ♦ - {'\u2473', '\uE094'}, // ★ - {'\u2474', '\uE095'}, // ◎ - {'\u2475', '\uE096'}, // ○ - {'\u2476', '\uE097'}, // □ - {'\u2477', '\uE098'}, // △ - {'\u2478', '\uE099'}, // ◇ - {'\u2479', '\uE09A'}, // ♪ - {'\u247a', '\uE09B'}, // ☀ - {'\u247b', '\uE09C'}, // ☁ - {'\u247d', '\uE09D'}, // ☂ - }; } + + private static readonly Dictionary Glyph56 = new() + { + {'\u2467', '\u00d7'}, // × + {'\u2468', '\u00f7'}, // ÷ + + {'\u246c', '\uE08D'}, // … + {'\u246d', '\uE08E'}, // ♂ + {'\u246e', '\uE08F'}, // ♀ + {'\u246f', '\uE090'}, // ♠ + {'\u2470', '\uE091'}, // ♣ + {'\u2471', '\uE092'}, // ♥ + {'\u2472', '\uE093'}, // ♦ + {'\u2473', '\uE094'}, // ★ + {'\u2474', '\uE095'}, // ◎ + {'\u2475', '\uE096'}, // ○ + {'\u2476', '\uE097'}, // □ + {'\u2477', '\uE098'}, // △ + {'\u2478', '\uE099'}, // ◇ + {'\u2479', '\uE09A'}, // ♪ + {'\u247a', '\uE09B'}, // ☀ + {'\u247b', '\uE09C'}, // ☁ + {'\u247d', '\uE09D'}, // ☂ + }; } diff --git a/PKHeX.Core/PKM/Strings/StringConverter3GC.cs b/PKHeX.Core/PKM/Strings/StringConverter3GC.cs new file mode 100644 index 000000000..c5d3b8422 --- /dev/null +++ b/PKHeX.Core/PKM/Strings/StringConverter3GC.cs @@ -0,0 +1,59 @@ +using System; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core; + +public static class StringConverter3GC +{ + private const char TerminatorBigEndian = (char)0; // GC + + /// Converts Big Endian encoded data to decoded string. + /// Encoded data + /// Decoded string. + public static string GetString(ReadOnlySpan data) + { + Span result = stackalloc char[data.Length]; + int length = LoadString(data, result); + return new string(result[..length].ToArray()); + } + + private static int LoadString(ReadOnlySpan data, Span result) + { + int i = 0; + for (; i < data.Length; i += 2) + { + var value = (char)ReadUInt16BigEndian(data[i..]); + if (value == TerminatorBigEndian) + break; + result[i/2] = value; + } + return i/2; + } + + /// Gets the bytes for a Big Endian string. + /// + /// Decoded string. + /// Maximum length of the input + /// Option to clear the buffer. Only is recognized. + /// Encoded data. + public static int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, + StringConverterOption option) + { + if (value.Length > maxLength) + value = value[..maxLength]; // Hard cap + + if (option is StringConverterOption.ClearZero) + destBuffer.Clear(); + + for (int i = 0; i < value.Length; i++) + { + var c = value[i]; + WriteUInt16BigEndian(destBuffer[(i * 2)..], c); + } + + if (destBuffer.Length == value.Length * 2) + return value.Length * 2; + WriteUInt16BigEndian(destBuffer[(value.Length * 2)..], TerminatorBigEndian); + return (value.Length * 2) + 2; + } +} diff --git a/PKHeX.Core/PKM/Strings/StringConverter4.cs b/PKHeX.Core/PKM/Strings/StringConverter4.cs index 0a54636cd..df330ebcb 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter4.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter4.cs @@ -1,608 +1,71 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using static PKHeX.Core.StringConverter4Util; +using static System.Buffers.Binary.BinaryPrimitives; -namespace PKHeX.Core +namespace PKHeX.Core; + +/// +/// Logic for converting a for Generation 4. +/// +public static class StringConverter4 { - /// - /// Logic for converting a for Generation 4. - /// - public static class StringConverter4 + private const ushort Terminator = 0xFFFF; + private const char TerminatorChar = (char)Terminator; + + /// Converts Generation 4 encoded data to decoded string. + /// Encoded data + /// Decoded string. + public static string GetString(ReadOnlySpan data) { - private const ushort Terminator = 0xFFFF; - private const char TerminatorChar = (char)Terminator; + Span result = stackalloc char[data.Length]; + var length = LoadString(data, result); + return new string(result[..length].ToArray()); + } - /// Converts Generation 4 encoded data to decoded string. - /// Encoded data - /// Offset to read from - /// Length of data to read. - /// Decoded string. - public static string GetString4(byte[] data, int offset, int count) + private static int LoadString(ReadOnlySpan data, Span result) + { + int i = 0; + for (; i < data.Length; i += 2) { - var s = new StringBuilder(count / 2); - for (int i = 0; i < count; i += 2) - { - var val = BitConverter.ToUInt16(data, offset + i); - if (val == Terminator) - break; - var chr = ConvertValue2CharG4(val); - if (chr == TerminatorChar) - break; - s.Append((char)chr); - } - StringConverter.SanitizeString(s); - return s.ToString(); - } - - /// Gets the bytes for a 4th Generation String - /// Decoded string. - /// Maximum length of the input - /// Pad the input to given length - /// Pad the input with this character value - /// Encoded data. - public static byte[] SetString4(string value, int maxLength, int padTo = 0, ushort padWith = 0) - { - var sb = new StringBuilder(value); - var delta = sb.Length - maxLength; - if (delta > 0) - sb.Remove(maxLength, delta); - - // Replace Special Characters and add Terminator - StringConverter.UnSanitizeString(sb, 4); - sb.Append(TerminatorChar); - var d2 = padTo - sb.Length; - if (d2 > 0) - sb.Append((char)padWith, d2); - - var data = new byte[sb.Length * 2]; - for (int i = 0; i < sb.Length; i++) - { - var chr = sb[i]; - var val = ConvertChar2ValueG4(chr); - BitConverter.GetBytes(val).CopyTo(data, i * 2); - } - return data; - } - - /// - /// Converts Generation 4 Big Endian encoded character data to string. - /// - /// Byte array containing encoded character data. - /// Offset to read from - /// Length of data to read. - /// Converted string. - public static string GetBEString4(byte[] data, int offset, int count) - { - var sb = new StringBuilder(count / 2); - for (int i = 0; i < count; i += 2) - { - var val = BigEndian.ToUInt16(data, offset + i); - if (val == Terminator) - break; - var chr = ConvertValue2CharG4(val); - if (chr == TerminatorChar) - break; - sb.Append((char)chr); - } - StringConverter.SanitizeString(sb); - return sb.ToString(); - } - - /// - /// Converts a string to Generation 4 Big Endian encoded character data. - /// - /// String to be converted. - /// Maximum length of string - /// Pad the input to given length - /// Pad the input with this character value - /// Byte array containing encoded character data - public static byte[] SetBEString4(string value, int maxLength, int padTo = 0, ushort padWith = 0) - { - var sb = new StringBuilder(value); - var delta = sb.Length - maxLength; - if (delta > 0) - sb.Remove(maxLength, delta); - - // Replace Special Characters and add Terminator - StringConverter.UnSanitizeString(sb, 4); - sb.Append(TerminatorChar); - var d2 = padTo - sb.Length; - if (d2 > 0) - sb.Append((char)padWith, d2); - - var data = new byte[sb.Length * 2]; - for (int i = 0; i < sb.Length; i++) - { - var chr = sb[i]; - var val = ConvertChar2ValueG4(chr); - BigEndian.GetBytes(val).CopyTo(data, i * 2); - } - return data; - } - - /// - /// Converts Generation 4 Big Endian encoded character data to string, with direct Unicode characters. - /// - /// Used by the Save File's internal strings. - /// Byte array containing encoded character data. - /// Offset to read from - /// Length of data to read. - /// Converted string. - public static string GetBEString4Unicode(byte[] data, int offset, int count) - { - // Scan for null terminator - for (int i = 0; i < count; i+=2) - { - if (BitConverter.ToInt16(data, offset + i) != 0) - continue; - count = i; + var value = ReadUInt16LittleEndian(data[i..]); + if (value == Terminator) break; - } - return Encoding.BigEndianUnicode.GetString(data, offset, count); + char chr = (char)ConvertValue2CharG4(value); + if (chr == TerminatorChar) + break; + chr = StringConverter.SanitizeChar(chr); + result[i/2] = chr; + } + return i/2; + } + + /// Gets the bytes for a 4th Generation String + /// + /// Decoded string. + /// Maximum length of the input + /// + /// Encoded data. + public static int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, + StringConverterOption option = StringConverterOption.ClearZero) + { + if (value.Length > maxLength) + value = value[..maxLength]; // Hard cap + + if (option is StringConverterOption.ClearZero) + destBuffer.Clear(); + + for (int i = 0; i < value.Length; i++) + { + var chr = value[i]; + chr = StringConverter.UnSanitizeChar5(chr); + ushort val = ConvertChar2ValueG4(chr); + WriteUInt16LittleEndian(destBuffer[(i * 2)..], val); } - /// - /// Converts a string to Generation 4 Big Endian encoded character data, with direct Unicode characters. - /// - /// Used by the Save File's internal strings. - /// String to be converted. - /// Maximum length of string - /// Pad the input to given length - /// Pad the input with this character value - /// Byte array containing encoded character data - public static byte[] SetBEString4Unicode(string value, int maxLength, int padTo = 0, ushort padWith = 0) - { - var sb = new StringBuilder(value); - var delta = sb.Length - maxLength; - if (delta > 0) - sb.Remove(maxLength, delta); - - sb.Append((char)0); - var d2 = padTo - sb.Length; - if (d2 > 0) - sb.Append((char)padWith, d2); - - var data = new byte[sb.Length * 2]; - for (int i = 0; i < sb.Length; i++) - { - var ofs = i * 2; - var c = (ushort)sb[i]; - data[ofs + 1] = (byte)c; - data[ofs] = (byte)(c >> 8); - } - return data; - } - - /// - /// Converts a Generation 4 value to Unicode character. - /// - /// Encoded value. - /// Decoded value (unicode). - public static ushort ConvertValue2CharG4(ushort val) - { - return G4ValueId.TryGetValue(val, out int index) - ? G4Chars[index] : '?'; - } - - /// - /// Converts a Unicode character to Generation 4 value. - /// - /// Decoded value (unicode). - /// Encoded value. - public static ushort ConvertChar2ValueG4(ushort chr) - { - return G4CharId.TryGetValue(chr, out int index) - ? G4Values[index] : (ushort)00428; // '?' - } - - /// - /// Strips diacritics on gen1-4 french pkm names - /// - /// String to clean - /// Cleaned string - /// Only 4 characters are accented in gen1-4 - public static void StripDiacriticsFR4(StringBuilder input) - { - for (int i = 0; i < input.Length; i++) - { - if (FrDiacritic.TryGetValue(input[i], out var value)) - input[i] = value; - } - } - - private static readonly Dictionary FrDiacritic = new(4) - { - { 'È', 'E' }, - { 'É', 'E' }, - { 'Ê', 'E' }, - { 'Ï', 'I' }, - }; - - #region Conversion Data - - /// - /// Values (stored value) - /// - private static readonly ushort[] G4Values = - { - 00001, 00002, 00003, 00004, 00005, 00006, 00007, 00008, 00009, 00010, 00011, 00012, 00013, 00014, 00015, 00016, - 00017, 00018, 00019, 00020, 00021, 00022, 00023, 00024, 00025, 00026, 00027, 00028, 00029, 00030, 00031, 00032, - 00033, 00034, 00035, 00036, 00037, 00038, 00039, 00040, 00041, 00042, 00043, 00044, 00045, 00046, 00047, 00048, - 00049, 00050, 00051, 00052, 00053, 00054, 00055, 00056, 00057, 00058, 00059, 00060, 00061, 00062, 00063, 00064, - 00065, 00066, 00067, 00068, 00069, 00070, 00071, 00072, 00073, 00074, 00075, 00076, 00077, 00078, 00079, 00080, - 00081, 00082, 00083, 00084, 00085, 00086, 00087, 00088, 00089, 00090, 00091, 00092, 00093, 00094, 00095, 00096, - 00097, 00098, 00099, 00100, 00101, 00102, 00103, 00104, 00105, 00106, 00107, 00108, 00109, 00110, 00111, 00112, - 00113, 00114, 00115, 00116, 00117, 00118, 00119, 00120, 00121, 00122, 00123, 00124, 00125, 00126, 00127, 00128, - 00129, 00130, 00131, 00132, 00133, 00134, 00135, 00136, 00137, 00138, 00139, 00140, 00141, 00142, 00143, 00144, - 00145, 00146, 00147, 00148, 00149, 00150, 00151, 00152, 00153, 00154, 00155, 00156, 00157, 00158, 00159, 00160, - 00161, 00162, 00163, 00164, 00165, 00166, 00167, 00168, 00169, 00170, 00171, 00172, 00173, 00174, 00175, 00176, - 00177, 00178, 00179, 00180, 00181, 00182, 00183, 00184, 00185, 00186, 00187, 00188, 00189, 00190, 00191, 00192, - 00193, 00194, 00195, 00196, 00197, 00198, 00199, 00200, 00201, 00202, 00203, 00204, 00205, 00206, 00207, 00208, - 00209, 00210, 00211, 00212, 00213, 00214, 00215, 00216, 00217, 00218, 00219, 00220, 00221, 00222, 00223, 00225, - 00226, 00227, 00228, 00229, 00230, 00231, 00232, 00233, 00234, 00235, 00236, 00237, 00238, 00239, 00240, 00241, - 00242, 00243, 00244, 00245, 00246, 00247, 00248, 00249, 00250, 00251, 00252, 00253, 00254, 00255, 00256, 00257, - 00258, 00259, 00260, 00261, 00262, 00263, 00264, 00265, 00266, 00267, 00268, 00269, 00270, 00271, 00272, 00273, - 00275, 00276, 00277, 00278, 00279, 00280, 00281, 00282, 00283, 00284, 00285, 00286, 00287, 00288, 00289, - 00290, 00291, 00292, 00293, 00294, 00295, 00296, 00297, 00298, 00299, 00300, 00301, 00302, 00303, 00304, 00305, - 00306, 00307, 00308, 00309, 00310, 00311, 00312, 00313, 00314, 00315, 00316, 00317, 00318, 00319, 00320, 00321, - 00322, 00323, 00324, 00325, 00326, 00327, 00328, 00329, 00330, 00331, 00332, 00333, 00334, 00335, 00336, 00337, - 00338, 00339, 00340, 00341, 00342, 00343, 00344, 00345, 00346, 00347, 00348, 00349, 00350, 00351, 00352, 00353, - 00354, 00355, 00356, 00357, 00358, 00359, 00360, 00361, 00362, 00363, 00364, 00365, 00366, 00367, 00368, 00369, - 00370, 00371, 00372, 00373, 00374, 00375, 00376, 00377, 00378, 00379, 00380, 00381, 00382, 00383, 00384, 00385, - 00386, 00387, 00388, 00389, 00390, 00391, 00392, 00393, 00394, 00395, 00396, 00397, 00398, 00399, 00400, 00401, - 00402, 00403, 00404, 00405, 00406, 00407, 00408, 00409, 00410, 00411, 00412, 00413, 00414, 00415, 00416, 00417, - 00418, 00419, 00420, 00421, 00422, 00423, 00424, 00425, 00426, 00427, 00428, 00429, 00430, 00431, 00432, 00433, - 00434, 00435, 00436, 00437, 00438, 00439, 00440, 00441, 00442, 00443, 00444, 00445, 00446, 00447, 00448, 00449, - 00450, 00451, 00452, 00453, 00454, 00455, 00456, 00457, 00458, 00459, 00460, 00461, 00462, 00463, 00464, 00465, - 00466, 00467, 00468, 00469, 00470, 00471, 00472, 00473, 00474, 00475, 00476, 00477, 00478, - 01025, 01026, 01027, 01028, 01029, 01030, 01031, 01032, 01033, 01034, 01035, 01036, 01037, - 01038, 01039, 01040, 01041, 01042, 01043, 01044, 01045, 01046, 01047, 01048, 01049, 01050, 01051, 01052, 01053, - 01054, 01055, 01056, 01057, 01058, 01059, 01060, 01061, 01062, 01063, 01064, 01065, 01066, 01067, 01068, 01069, - 01070, 01071, 01072, 01073, 01074, 01075, 01076, 01077, 01078, 01079, 01080, 01081, 01082, 01083, 01084, 01085, - 01086, 01087, 01088, 01089, 01090, 01091, 01092, 01093, 01094, 01095, 01096, 01097, 01098, 01099, 01100, 01101, - 01102, 01103, 01104, 01105, 01106, 01107, 01108, 01109, 01110, 01111, 01112, 01113, 01114, 01115, 01116, 01117, - 01118, 01119, 01120, 01121, 01122, 01123, 01124, 01125, 01126, 01127, 01128, 01129, 01130, 01131, 01132, 01133, - 01134, 01135, 01136, 01137, 01138, 01139, 01140, 01141, 01142, 01143, 01144, 01145, 01146, 01147, 01148, 01149, - 01150, 01151, 01152, 01153, 01154, 01155, 01156, 01157, 01158, 01159, 01160, 01161, 01162, 01163, 01164, 01165, - 01166, 01167, 01168, 01169, 01170, 01171, 01172, 01173, 01174, 01175, 01176, 01177, 01178, 01179, 01180, 01181, - 01182, 01183, 01184, 01185, 01186, 01187, 01188, 01189, 01190, 01191, 01192, 01193, 01194, 01195, 01196, 01197, - 01198, 01199, 01200, 01201, 01202, 01203, 01204, 01205, 01206, 01207, 01208, 01209, 01210, 01211, 01212, 01213, - 01214, 01215, 01216, 01217, 01218, 01219, 01220, 01221, 01222, 01223, 01224, 01225, 01226, 01227, 01228, 01229, - 01230, 01231, 01232, 01233, 01234, 01235, 01236, 01237, 01238, 01239, 01240, 01241, 01242, 01243, 01244, 01245, - 01246, 01247, 01248, 01249, 01250, 01251, 01252, 01253, 01254, 01255, 01256, 01257, 01258, 01259, 01260, 01261, - 01262, 01263, 01264, 01265, 01266, 01267, 01268, 01269, 01270, 01271, 01272, 01273, 01274, 01275, 01276, 01277, - 01278, 01279, 01280, 01281, 01282, 01283, 01284, 01285, 01286, 01287, 01288, 01289, 01290, 01291, 01292, 01293, - 01294, 01295, 01296, 01297, 01298, 01299, 01300, 01301, 01302, 01303, 01304, 01305, 01306, 01307, 01308, 01309, - 01310, 01311, 01312, 01313, 01314, 01315, 01316, 01317, 01318, 01319, 01320, 01321, 01322, 01323, 01324, 01325, - 01326, 01327, 01328, 01329, 01330, 01331, 01332, 01333, 01334, 01335, 01336, 01337, 01338, 01339, 01340, 01341, - 01342, 01343, 01344, 01345, 01346, 01347, 01348, 01349, 01350, 01351, 01352, 01353, 01354, 01355, 01356, 01357, - 01358, 01359, 01360, 01361, 01362, 01363, 01364, 01365, 01366, 01367, 01368, 01369, 01370, 01371, 01372, 01373, - 01374, 01375, 01376, 01377, 01378, 01379, 01380, 01381, 01382, 01383, 01384, 01385, 01386, 01387, 01388, 01389, - 01390, 01391, 01392, 01393, 01394, 01395, 01396, 01397, 01398, 01399, 01400, 01401, 01402, 01403, 01404, 01405, - 01406, 01407, 01408, 01409, 01410, 01411, 01412, 01413, 01414, 01415, 01416, 01417, 01418, 01419, 01420, 01421, - 01422, 01423, 01424, 01425, 01426, 01427, 01428, 01429, 01430, 01431, 01432, 01433, 01434, 01435, 01436, 01437, - 01438, 01439, 01440, 01441, 01442, 01443, 01444, 01445, 01446, 01447, 01448, 01449, 01450, 01451, 01452, 01453, - 01454, 01455, 01456, 01457, 01458, 01459, 01460, 01461, 01462, 01463, 01464, 01465, 01466, 01467, 01468, 01469, - 01470, 01471, 01472, 01473, 01474, 01475, 01476, 01477, 01478, 01479, 01480, 01481, 01482, 01483, 01484, 01485, - 01486, 01487, 01488, 01489, 01490, 01491, 01492, 01493, 01494, 01495, 01496, 01497, 01498, 01499, 01500, 01501, - 01502, 01503, 01504, 01505, 01506, 01507, 01508, 01509, 01510, 01511, 01512, 01513, 01514, 01515, 01516, 01517, - 01518, 01519, 01520, 01521, 01522, 01523, 01524, 01525, 01526, 01527, 01528, 01529, 01530, 01531, 01532, 01533, - 01534, 01535, 01536, 01537, 01538, 01539, 01540, 01541, 01542, 01543, 01544, 01545, 01546, 01547, 01548, 01549, - 01550, 01551, 01552, 01553, 01554, 01555, 01556, 01557, 01558, 01559, 01560, 01561, 01562, 01563, 01564, 01565, - 01566, 01567, 01568, 01569, 01570, 01571, 01572, 01573, 01574, 01575, 01576, 01577, 01578, 01579, 01580, 01581, - 01582, 01583, 01584, 01585, 01586, 01587, 01588, 01589, 01590, 01591, 01592, 01593, 01594, 01595, 01596, 01597, - 01598, 01599, 01600, 01601, 01602, 01603, 01604, 01605, 01606, 01607, 01608, 01609, 01610, 01611, 01612, 01613, - 01614, 01615, 01616, 01617, 01618, 01619, 01620, 01621, 01622, 01623, 01624, 01625, 01626, 01627, 01628, 01629, - 01630, 01631, 01632, 01633, 01634, 01635, 01636, 01637, 01638, 01639, 01640, 01641, 01642, 01643, 01644, 01645, - 01646, 01647, 01648, 01649, 01650, 01651, 01652, 01653, 01654, 01655, 01656, 01657, 01658, 01659, 01660, 01661, - 01662, 01663, 01664, 01665, 01666, 01667, 01668, 01669, 01670, 01671, 01672, 01673, 01674, 01675, 01676, 01677, - 01678, 01679, 01680, 01681, 01682, 01683, 01684, 01685, 01686, 01687, 01688, 01689, 01690, 01691, 01692, 01693, - 01694, 01695, 01696, 01697, 01698, 01699, 01700, 01701, 01702, 01703, 01704, 01705, 01706, 01707, 01708, 01709, - 01710, 01711, 01712, 01713, 01714, 01715, 01716, 01717, 01718, 01719, 01720, 01721, 01722, 01723, 01724, 01725, - 01726, 01727, 01728, 01729, 01730, 01731, 01732, 01733, 01734, 01735, 01736, 01737, 01738, 01739, 01740, 01741, - 01742, 01743, 01744, 01745, 01746, 01747, 01748, 01749, 01750, 01751, 01752, 01753, 01754, 01755, 01756, 01757, - 01758, 01759, 01760, 01761, 01762, 01763, 01764, 01765, 01766, 01767, 01768, 01769, 01770, 01771, 01772, 01773, - 01774, 01775, 01776, 01777, 01778, 01779, 01780, 01781, 01782, 01783, 01784, 01785, 01786, 01787, 01788, 01789, - 01790, 01791, 01792, 01793, 01794, 01795, 01796, 01797, 01798, 01799, 01800, 01801, 01802, 01803, 01804, 01805, - 01806, 01807, 01808, 01809, 01810, 01811, 01812, 01813, 01814, 01815, 01816, 01817, 01818, 01819, 01820, 01821, - 01822, 01823, 01824, 01825, 01826, 01827, 01828, 01829, 01830, 01831, 01832, 01833, 01834, 01835, 01836, 01837, - 01838, 01839, 01840, 01841, 01842, 01843, 01844, 01845, 01846, 01847, 01848, 01849, 01850, 01851, 01852, 01853, - 01854, 01855, 01856, 01857, 01858, 01859, 01860, 01861, 01862, 01863, 01864, 01865, 01866, 01867, 01868, 01869, - 01870, 01871, 01872, 01873, 01874, 01875, 01876, 01877, 01878, 01879, 01880, 01881, 01882, 01883, 01884, 01885, - 01886, 01887, 01888, 01889, 01890, 01891, 01892, 01893, 01894, 01895, 01896, 01897, 01898, 01899, 01900, 01901, - 01902, 01903, 01904, 01905, 01906, 01907, 01908, 01909, 01910, 01911, 01912, 01913, 01914, 01915, 01916, 01917, - 01918, 01919, 01920, 01921, 01922, 01923, 01924, 01925, 01926, 01927, 01928, 01929, 01930, 01931, 01932, 01933, - 01934, 01935, 01936, 01937, 01938, 01939, 01940, 01941, 01942, 01943, 01944, 01945, 01946, 01947, 01948, 01949, - 01950, 01951, 01952, 01953, 01954, 01955, 01956, 01957, 01958, 01959, 01960, 01961, 01962, 01963, 01964, 01965, - 01966, 01967, 01968, 01969, 01970, 01971, 01972, 01973, 01974, 01975, 01976, 01977, 01978, 01979, 01980, 01981, - 01982, 01983, 01984, 01985, 01986, 01987, 01988, 01989, 01990, 01991, 01992, 01993, 01994, 01995, 01996, 01997, - 01998, 01999, 02000, 02001, 02002, 02003, 02004, 02005, 02006, 02007, 02008, 02009, 02010, 02011, 02012, 02013, - 02014, 02015, 02016, 02017, 02018, 02019, 02020, 02021, 02022, 02023, 02024, 02025, 02026, 02027, 02028, 02029, - 02030, 02031, 02032, 02033, 02034, 02035, 02036, 02037, 02038, 02039, 02040, 02041, 02042, 02043, 02044, 02045, - 02046, 02047, 02048, 02049, 02050, 02051, 02052, 02053, 02054, 02055, 02056, 02057, 02058, 02059, 02060, 02061, - 02062, 02063, 02064, 02065, 02066, 02067, 02068, 02069, 02070, 02071, 02072, 02073, 02074, 02075, 02076, 02077, - 02078, 02079, 02080, 02081, 02082, 02083, 02084, 02085, 02086, 02087, 02088, 02089, 02090, 02091, 02092, 02093, - 02094, 02095, 02096, 02097, 02098, 02099, 02100, 02101, 02102, 02103, 02104, 02105, 02106, 02107, 02108, 02109, - 02110, 02111, 02112, 02113, 02114, 02115, 02116, 02117, 02118, 02119, 02120, 02121, 02122, 02123, 02124, 02125, - 02126, 02127, 02128, 02129, 02130, 02131, 02132, 02133, 02134, 02135, 02136, 02137, 02138, 02139, 02140, 02141, - 02142, 02143, 02144, 02145, 02146, 02147, 02148, 02149, 02150, 02151, 02152, 02153, 02154, 02155, 02156, 02157, - 02158, 02159, 02160, 02161, 02162, 02163, 02164, 02165, 02166, 02167, 02168, 02169, 02170, 02171, 02172, 02173, - 02174, 02175, 02176, 02177, 02178, 02179, 02180, 02181, 02182, 02183, 02184, 02185, 02186, 02187, 02188, 02189, - 02190, 02191, 02192, 02193, 02194, 02195, 02196, 02197, 02198, 02199, 02200, 02201, 02202, 02203, 02204, 02205, - 02206, 02207, 02208, 02209, 02210, 02211, 02212, 02213, 02214, 02215, 02216, 02217, 02218, 02219, 02220, 02221, - 02222, 02223, 02224, 02225, 02226, 02227, 02228, 02229, 02230, 02231, 02232, 02233, 02234, 02235, 02236, 02237, - 02238, 02239, 02240, 02241, 02242, 02243, 02244, 02245, 02246, 02247, 02248, 02249, 02250, 02251, 02252, 02253, - 02254, 02255, 02256, 02257, 02258, 02259, 02260, 02261, 02262, 02263, 02264, 02265, 02266, 02267, 02268, 02269, - 02270, 02271, 02272, 02273, 02274, 02275, 02276, 02277, 02278, 02279, 02280, 02281, 02282, 02283, 02284, 02285, - 02286, 02287, 02288, 02289, 02290, 02291, 02292, 02293, 02294, 02295, 02296, 02297, 02298, 02299, 02300, 02301, - 02302, 02303, 02304, 02305, 02306, 02307, 02308, 02309, 02310, 02311, 02312, 02313, 02314, 02315, 02316, 02317, - 02318, 02319, 02320, 02321, 02322, 02323, 02324, 02325, 02326, 02327, 02328, 02329, 02330, 02331, 02332, 02333, - 02334, 02335, 02336, 02337, 02338, 02339, 02340, 02341, 02342, 02343, 02344, 02345, 02346, 02347, 02348, 02349, - 02350, 02351, 02352, 02353, 02354, 02355, 02356, 02357, 02358, 02359, 02360, 02361, 02362, 02363, 02364, 02365, - 02366, 02367, 02368, 02369, 02370, 02371, 02372, 02373, 02374, 02375, 02376, 02377, 02378, 02379, 02380, 02381, - 02382, 02383, 02384, 02385, 02386, 02387, 02388, 02389, 02390, 02391, 02392, 02393, 02394, 02395, 02396, 02397, - 02398, 02399, 02400, 02401, 02402, 02403, 02404, 02405, 02406, 02407, 02408, 02409, 02410, 02411, 02412, 02413, - 02414, 02415, 02416, 02417, 02418, 02419, 02420, 02421, 02422, 02423, 02424, 02425, 02426, 02427, 02428, 02429, - 02430, 02431, 02432, 02433, 02434, 02435, 02436, 02437, 02438, 02439, 02440, 02441, 02442, 02443, 02444, 02445, - 02446, 02447, 02448, 02449, 02450, 02451, 02452, 02453, 02454, 02455, 02456, 02457, 02458, 02459, 02460, 02461, - 02462, 02463, 02464, 02465, 02466, 02467, 02468, 02469, 02470, 02471, 02472, 02473, 02474, 02475, 02476, 02477, - 02478, 02479, 02480, 02481, 02482, 02483, 02484, 02485, 02486, 02487, 02488, 02489, 02490, 02491, 02492, 02493, - 02494, 02495, 02496, 02497, 02498, 02499, 02500, 02501, 02502, 02503, 02504, 02505, 02506, 02507, 02508, 02509, - 02510, 02511, 02512, 02513, 02514, 02515, 02516, 02517, 02518, 02519, 02520, 02521, 02522, 02523, 02524, 02525, - 02526, 02527, 02528, 02529, 02530, 02531, 02532, 02533, 02534, 02535, 02536, 02537, 02538, 02539, 02540, 02541, - 02542, 02543, 02544, 02545, 02546, 02547, 02548, 02549, 02550, 02551, 02552, 02553, 02554, 02555, 02556, 02557, - 02558, 02559, 02560, 02561, 02562, 02563, 02564, 02565, 02566, 02567, 02568, 02569, 02570, 02571, 02572, 02573, - 02574, 02575, 02576, 02577, 02578, 02579, 02580, 02581, 02582, 02583, 02584, 02585, 02586, 02587, 02588, 02589, - 02590, 02591, 02592, 02593, 02594, 02595, 02596, 02597, 02598, 02599, 02600, 02601, 02602, 02603, 02604, 02605, - 02606, 02607, 02608, 02609, 02610, 02611, 02612, 02613, 02614, 02615, 02616, 02617, 02618, 02619, 02620, 02621, - 02622, 02623, 02624, 02625, 02626, 02627, 02628, 02629, 02630, 02631, 02632, 02633, 02634, 02635, 02636, 02637, - 02638, 02639, 02640, 02641, 02642, 02643, 02644, 02645, 02646, 02647, 02648, 02649, 02650, 02651, 02652, 02653, - 02654, 02655, 02656, 02657, 02658, 02659, 02660, 02661, 02662, 02663, 02664, 02665, 02666, 02667, 02668, 02669, - 02670, 02671, 02672, 02673, 02674, 02675, 02676, 02677, 02678, 02679, 02680, 02681, 02682, 02683, 02684, 02685, - 02686, 02687, 02688, 02689, 02690, 02691, 02692, 02693, 02694, 02695, 02696, 02697, 02698, 02699, 02700, 02701, - 02702, 02703, 02704, 02705, 02706, 02707, 02708, 02709, 02710, 02711, 02712, 02713, 02714, 02715, 02716, 02717, - 02718, 02719, 02720, 02721, 02722, 02723, 02724, 02725, 02726, 02727, 02728, 02729, 02730, 02731, 02732, 02733, - 02734, 02735, 02736, 02737, 02738, 02739, 02740, 02741, 02742, 02743, 02744, 02745, 02746, 02747, 02748, 02749, - 02750, 02751, 02752, 02753, 02754, 02755, 02756, 02757, 02758, 02759, 02760, 02761, 02762, 02763, 02764, 02765, - 02766, 02767, 02768, 02769, 02770, 02771, 02772, 02773, 02774, 02775, 02776, 02777, 02778, 02779, 02780, 02781, - 02782, 02783, 02784, 02785, 02786, 02787, 02788, 02789, 02790, 02791, 02792, 02793, 02794, 02795, 02796, 02797, - 02798, 02799, 02800, 02801, 02802, 02803, 02804, 02805, 02806, 02807, 02808, 02809, 02810, 02811, 02812, 02813, - 02814, 02815, 02816, 02817, 02818, 02819, 02820, 02821, 02822, 02823, 02824, 02825, 02826, 02827, 02828, 02829, - 02830, 02831, 02832, 02833, 02834, 02835, 02836, 02837, 02838, 02839, 02840, 02841, 02842, 02843, 02844, 02845, - 02846, 02847, 02848, 02849, 02850, 02851, 02852, 02853, 02854, 02855, 02856, 02857, 02858, 02859, 02860, 02861, - 02862, 02863, 02864, 02865, 02866, 02867, 02868, 02869, 02870, 02871, 02872, 02873, 02874, 02875, 02876, 02877, - 02878, 02879, 02880, 02881, 02882, 02883, 02884, 02885, 02886, 02887, 02888, 02889, 02890, 02891, 02892, 02893, - 02894, 02895, 02896, 02897, 02898, 02899, 02900, 02901, 02902, 02903, 02904, 02905, 02906, 02907, 02908, 02909, - 02910, 02911, 02912, 02913, 02914, 02915, 02916, 02917, 02918, 02919, 02920, 02921, 02922, 02923, 02924, 02925, - 02926, 02927, 02928, 02929, 02930, 02931, 02932, 02933, 02934, 02935, 02936, 02937, 02938, 02939, 02940, 02941, - 02942, 02943, 02944, 02945, 02946, 02947, 02948, 02949, 02950, 02951, 02952, 02953, 02954, 02955, 02956, 02957, - 02958, 02959, 02960, 02961, 02962, 02963, 02964, 02965, 02966, 02967, 02968, 02969, 02970, 02971, 02972, 02973, - 02974, 02975, 02976, 02977, 02978, 02979, 02980, 02981, 02982, 02983, 02984, 02985, 02986, 02987, 02988, 02989, - 02990, 02991, 02992, 02993, 02994, 02995, 02996, 02997, 02998, 02999, 03000, 03001, 03002, 03003, 03004, 03005, - 03006, 03007, 03008, 03009, 03010, 03011, 03012, 03013, 03014, 03015, 03016, 03017, 03018, 03019, 03020, 03021, - 03022, 03023, 03024, 03025, 03026, 03027, 03028, 03029, 03030, 03031, 03032, 03033, 03034, 03035, 03036, 03037, - 03038, 03039, 03040, 03041, 03042, 03043, 03044, 03045, 03046, 03047, 03048, 03049, 03050, 03051, 03052, 03053, - 03054, 03055, 03056, 03057, 03058, 03059, 03060, 03061, 03062, 03063, 03064, 03065, 03066, 03067, 03068, 03069, - 03070, 03071, 03072, 03073, 03074, 03075, 03076, 03077, 03078, 03079, 03080, 03081, 03082, 03083, 03084, 03085, - 03086, 03087, 03088, 03089, 03090, 03091, 03092, 03093, 03094, 03095, 03096, 03097, 03098, 03099, 03100, 03101, - 03102, 03103, 03104, 03105, 03106, 03107, 03108, 03109, 03110, 03111, 03112, 03113, 03114, 03115, 03116, 03117, - 03118, 03119, 03120, 03121, 03122, 03123, 03124, 03125, 03126, 03127, 03128, 03129, 03130, 03131, 03132, 03133, - 03134, 03135, 03136, 03137, 03138, 03139, 03140, 03141, 03142, 03143, 03144, 03145, 03146, 03147, 03148, 03149, - 03150, 03151, 03152, 03153, 03154, 03155, 03156, 03157, 03158, 03159, 03160, 03161, 03162, 03163, 03164, 03165, - 03166, 03167, 03168, 03169, 03170, 03171, 03172, 03173, 03174, 03175, 03176, 03177, 03178, 03179, 03180, 03181, - 03182, 03183, 03184, 03185, 03186, 03187, 03188, 03189, 03190, 03191, 03192, 03193, 03194, 03195, 03196, 03197, - 03198, 03199, 03200, 03201, 03202, 03203, 03204, 03205, 03206, 03207, 03208, 03209, 03210, 03211, 03212, 03213, - 03214, 03215, 03216, 03217, 03218, 03219, 03220, 03221, 03222, 03223, 03224, 03225, 03226, 03227, 03228, 03229, - 03230, 03231, 03232, 03233, 03234, 03235, 03236, 03237, 03238, 03239, 03240, 03241, 03242, 03243, 03244, 03245, - 03246, 03247, 03248, 03249, 03250, 03251, 03252, 03253, 03254, 03255, 03256, 03257, 03258, 03259, 03260, 03261, - 03262, 03263, 03264, 03265, 03266, 03267, 03268, 03269, 03270, 03271, 03272, 03273, 03274, 03275, 03276, 03277, - 03278, 03279, 03280, 03281, 03282, 03283, 03284, 03285, 03286, 03287, 03288, 03289, 03290, 03291, 03292, 03293, - 03294, 03295, 03296, 03297, 03298, 03299, 03300, 03301, 03302, 03303, 03304, 03305, 03306, 03307, 03308, 03309, - 03310, 03311, 03312, 03313, 03314, 03315, 03316, 03317, 03318, 03319, 03320, 03321, 03322, 03323, 03324, 03325, - 03326, 03327, 03328, 03329, 03330, 03331, 03332, 03333, 03334, 03335, 03336, 03337, 03338, 03339, 03340, 03341, - 03342, 03343, 03344, 03345, 03346, 03347, 03348, 03349, 03350, 03351, 03352, 03353, 03354, 03355, 03356, 03357, - 03358, 03359, 03360, 03361, 03362, 03363, 03364, 03365, 03366, 03367, 03368, 03369, 03370, 03371, 03372, 03373, - 03374, 03377, 03378, 03379, 03380, 03381, 03382, 03383, 03384, 03385, 03386, 03387, 03388, 03389, 03390, 03391, - 03392, 03393, 03394, 03395, 03396, 03397, 03398, 03399, 03400, 03401, 03402, 03403, 03404, 03405, 03406, 03407, - 03408, 03409, 03425, 03426, 03427, 03428, 03429, 65535, - }; - - /// - /// Characters (Unicode representation) - /// - private static readonly ushort[] G4Chars = - { - 12288, 12353, 12354, 12355, 12356, 12357, 12358, 12359, 12360, 12361, 12362, 12363, 12364, 12365, 12366, 12367, - 12368, 12369, 12370, 12371, 12372, 12373, 12374, 12375, 12376, 12377, 12378, 12379, 12380, 12381, 12382, 12383, - 12384, 12385, 12386, 12387, 12388, 12389, 12390, 12391, 12392, 12393, 12394, 12395, 12396, 12397, 12398, 12399, - 12400, 12401, 12402, 12403, 12404, 12405, 12406, 12407, 12408, 12409, 12410, 12411, 12412, 12413, 12414, 12415, - 12416, 12417, 12418, 12419, 12420, 12421, 12422, 12423, 12424, 12425, 12426, 12427, 12428, 12429, 12431, 12434, - 12435, 12449, 12450, 12451, 12452, 12453, 12454, 12455, 12456, 12457, 12458, 12459, 12460, 12461, 12462, 12463, - 12464, 12465, 12466, 12467, 12468, 12469, 12470, 12471, 12472, 12473, 12474, 12475, 12476, 12477, 12478, 12479, - 12480, 12481, 12482, 12483, 12484, 12485, 12486, 12487, 12488, 12489, 12490, 12491, 12492, 12493, 12494, 12495, - 12496, 12497, 12498, 12499, 12500, 12501, 12502, 12503, 12504, 12505, 12506, 12507, 12508, 12509, 12510, 12511, - 12512, 12513, 12514, 12515, 12516, 12517, 12518, 12519, 12520, 12521, 12522, 12523, 12524, 12525, 12527, 12530, - 12531, 65296, 65297, 65298, 65299, 65300, 65301, 65302, 65303, 65304, 65305, 65313, 65314, 65315, 65316, 65317, - 65318, 65319, 65320, 65321, 65322, 65323, 65324, 65325, 65326, 65327, 65328, 65329, 65330, 65331, 65332, 65333, - 65334, 65335, 65336, 65337, 65338, 65345, 65346, 65347, 65348, 65349, 65350, 65351, 65352, 65353, 65354, 65355, - 65356, 65357, 65358, 65359, 65360, 65361, 65362, 65363, 65364, 65365, 65366, 65367, 65368, 65369, 65370, 65281, - 65311, 12289, 12290, 08943, 12539, 65295, 12300, 12301, 12302, 12303, 65288, 65289, 09325, 09326, 65291, 12540, - 09319, 09320, 65309, 65374, 65306, 65307, 65294, 65292, 09824, 09827, 09829, 09830, 09733, 09678, 09675, 09633, - 09651, 09671, 65312, 09834, 65285, 09728, 09729, 09730, 09731, 09312, 09313, 09314, 09738, 09316, 09317, 09318, - 09800, 09801, 09802, 09803, 09804, 09805, 09806, 09807, 08592, 08593, 08595, 08594, 08227, 65286, 00048, - 00049, 00050, 00051, 00052, 00053, 00054, 00055, 00056, 00057, 00065, 00066, 00067, 00068, 00069, 00070, 00071, - 00072, 00073, 00074, 00075, 00076, 00077, 00078, 00079, 00080, 00081, 00082, 00083, 00084, 00085, 00086, 00087, - 00088, 00089, 00090, 00097, 00098, 00099, 00100, 00101, 00102, 00103, 00104, 00105, 00106, 00107, 00108, 00109, - 00110, 00111, 00112, 00113, 00114, 00115, 00116, 00117, 00118, 00119, 00120, 00121, 00122, 00192, 00193, 00194, - 00195, 00196, 00197, 00198, 00199, 00200, 00201, 00202, 00203, 00204, 00205, 00206, 00207, 00208, 00209, 00210, - 00211, 00212, 00213, 00214, 00215, 00216, 00217, 00218, 00219, 00220, 00221, 00222, 00223, 00224, 00225, 00226, - 00227, 00228, 00229, 00230, 00231, 00232, 00233, 00234, 00235, 00236, 00237, 00238, 00239, 00240, 00241, 00242, - 00243, 00244, 00245, 00246, 00247, 00248, 00249, 00250, 00251, 00252, 00253, 00254, 00255, 00338, 00339, 00350, - 00351, 00170, 00186, 00185, 00178, 00179, 00036, 00161, 00191, 00033, 00063, 00044, 00046, 09324, 65381, 00047, - 08216, 08217, 08220, 08221, 08222, 12298, 12299, 00040, 00041, 09794, 09792, 00043, 00045, 00042, 00035, 00061, - 00038, 00126, 00058, 00059, 09327, 09328, 09329, 09330, 09331, 09332, 09333, 09334, 09335, 09336, 00064, 09337, - 00037, 09338, 09339, 09340, 09341, 09342, 09343, 09344, 09345, 09346, 09347, 09348, 00032, - 44032, 44033, 44036, 44039, 44040, 44041, 44042, 44048, 44049, 44050, 44051, 44052, 44053, - 44054, 44055, 44057, 44058, 44059, 44060, 44061, 44064, 44068, 44076, 44077, 44079, 44080, 44081, 44088, 44089, - 44092, 44096, 44107, 44109, 44116, 44120, 44124, 44144, 44145, 44148, 44151, 44152, 44154, 44160, 44161, 44163, - 44164, 44165, 44166, 44169, 44170, 44171, 44172, 44176, 44180, 44188, 44189, 44191, 44192, 44193, 44200, 44201, - 44202, 44204, 44207, 44208, 44216, 44217, 44219, 44220, 44221, 44225, 44228, 44232, 44236, 44245, 44247, 44256, - 44257, 44260, 44263, 44264, 44266, 44268, 44271, 44272, 44273, 44275, 44277, 44278, 44284, 44285, 44288, 44292, - 44294, 44300, 44301, 44303, 44305, 44312, 44316, 44320, 44329, 44332, 44333, 44340, 44341, 44344, 44348, 44356, - 44357, 44359, 44361, 44368, 44372, 44376, 44385, 44387, 44396, 44397, 44400, 44403, 44404, 44405, 44406, 44411, - 44412, 44413, 44415, 44417, 44418, 44424, 44425, 44428, 44432, 44444, 44445, 44452, 44471, 44480, 44481, 44484, - 44488, 44496, 44497, 44499, 44508, 44512, 44516, 44536, 44537, 44540, 44543, 44544, 44545, 44552, 44553, 44555, - 44557, 44564, 44592, 44593, 44596, 44599, 44600, 44602, 44608, 44609, 44611, 44613, 44614, 44618, 44620, 44621, - 44622, 44624, 44628, 44630, 44636, 44637, 44639, 44640, 44641, 44645, 44648, 44649, 44652, 44656, 44664, 44665, - 44667, 44668, 44669, 44676, 44677, 44684, 44732, 44733, 44734, 44736, 44740, 44748, 44749, 44751, 44752, 44753, - 44760, 44761, 44764, 44776, 44779, 44781, 44788, 44792, 44796, 44807, 44808, 44813, 44816, 44844, 44845, 44848, - 44850, 44852, 44860, 44861, 44863, 44865, 44866, 44867, 44872, 44873, 44880, 44892, 44893, 44900, 44901, 44921, - 44928, 44932, 44936, 44944, 44945, 44949, 44956, 44984, 44985, 44988, 44992, 44999, 45000, 45001, 45003, 45005, - 45006, 45012, 45020, 45032, 45033, 45040, 45041, 45044, 45048, 45056, 45057, 45060, 45068, 45072, 45076, 45084, - 45085, 45096, 45124, 45125, 45128, 45130, 45132, 45134, 45139, 45140, 45141, 45143, 45145, 45149, 45180, 45181, - 45184, 45188, 45196, 45197, 45199, 45201, 45208, 45209, 45210, 45212, 45215, 45216, 45217, 45218, 45224, 45225, - 45227, 45228, 45229, 45230, 45231, 45233, 45235, 45236, 45237, 45240, 45244, 45252, 45253, 45255, 45256, 45257, - 45264, 45265, 45268, 45272, 45280, 45285, 45320, 45321, 45323, 45324, 45328, 45330, 45331, 45336, 45337, 45339, - 45340, 45341, 45347, 45348, 45349, 45352, 45356, 45364, 45365, 45367, 45368, 45369, 45376, 45377, 45380, 45384, - 45392, 45393, 45396, 45397, 45400, 45404, 45408, 45432, 45433, 45436, 45440, 45442, 45448, 45449, 45451, 45453, - 45458, 45459, 45460, 45464, 45468, 45480, 45516, 45520, 45524, 45532, 45533, 45535, 45544, 45545, 45548, 45552, - 45561, 45563, 45565, 45572, 45573, 45576, 45579, 45580, 45588, 45589, 45591, 45593, 45600, 45620, 45628, 45656, - 45660, 45664, 45672, 45673, 45684, 45685, 45692, 45700, 45701, 45705, 45712, 45713, 45716, 45720, 45721, 45722, - 45728, 45729, 45731, 45733, 45734, 45738, 45740, 45744, 45748, 45768, 45769, 45772, 45776, 45778, 45784, 45785, - 45787, 45789, 45794, 45796, 45797, 45798, 45800, 45803, 45804, 45805, 45806, 45807, 45811, 45812, 45813, 45815, - 45816, 45817, 45818, 45819, 45823, 45824, 45825, 45828, 45832, 45840, 45841, 45843, 45844, 45845, 45852, 45908, - 45909, 45910, 45912, 45915, 45916, 45918, 45919, 45924, 45925, 45927, 45929, 45931, 45934, 45936, 45937, 45940, - 45944, 45952, 45953, 45955, 45956, 45957, 45964, 45968, 45972, 45984, 45985, 45992, 45996, 46020, 46021, 46024, - 46027, 46028, 46030, 46032, 46036, 46037, 46039, 46041, 46043, 46045, 46048, 46052, 46056, 46076, 46096, 46104, - 46108, 46112, 46120, 46121, 46123, 46132, 46160, 46161, 46164, 46168, 46176, 46177, 46179, 46181, 46188, 46208, - 46216, 46237, 46244, 46248, 46252, 46261, 46263, 46265, 46272, 46276, 46280, 46288, 46293, 46300, 46301, 46304, - 46307, 46308, 46310, 46316, 46317, 46319, 46321, 46328, 46356, 46357, 46360, 46363, 46364, 46372, 46373, 46375, - 46376, 46377, 46378, 46384, 46385, 46388, 46392, 46400, 46401, 46403, 46404, 46405, 46411, 46412, 46413, 46416, - 46420, 46428, 46429, 46431, 46432, 46433, 46496, 46497, 46500, 46504, 46506, 46507, 46512, 46513, 46515, 46516, - 46517, 46523, 46524, 46525, 46528, 46532, 46540, 46541, 46543, 46544, 46545, 46552, 46572, 46608, 46609, 46612, - 46616, 46629, 46636, 46644, 46664, 46692, 46696, 46748, 46749, 46752, 46756, 46763, 46764, 46769, 46804, 46832, - 46836, 46840, 46848, 46849, 46853, 46888, 46889, 46892, 46895, 46896, 46904, 46905, 46907, 46916, 46920, 46924, - 46932, 46933, 46944, 46948, 46952, 46960, 46961, 46963, 46965, 46972, 46973, 46976, 46980, 46988, 46989, 46991, - 46992, 46993, 46994, 46998, 46999, 47000, 47001, 47004, 47008, 47016, 47017, 47019, 47020, 47021, 47028, 47029, - 47032, 47047, 47049, 47084, 47085, 47088, 47092, 47100, 47101, 47103, 47104, 47105, 47111, 47112, 47113, 47116, - 47120, 47128, 47129, 47131, 47133, 47140, 47141, 47144, 47148, 47156, 47157, 47159, 47160, 47161, 47168, 47172, - 47185, 47187, 47196, 47197, 47200, 47204, 47212, 47213, 47215, 47217, 47224, 47228, 47245, 47272, 47280, 47284, - 47288, 47296, 47297, 47299, 47301, 47308, 47312, 47316, 47325, 47327, 47329, 47336, 47337, 47340, 47344, 47352, - 47353, 47355, 47357, 47364, 47384, 47392, 47420, 47421, 47424, 47428, 47436, 47439, 47441, 47448, 47449, 47452, - 47456, 47464, 47465, 47467, 47469, 47476, 47477, 47480, 47484, 47492, 47493, 47495, 47497, 47498, 47501, 47502, - 47532, 47533, 47536, 47540, 47548, 47549, 47551, 47553, 47560, 47561, 47564, 47566, 47567, 47568, 47569, 47570, - 47576, 47577, 47579, 47581, 47582, 47585, 47587, 47588, 47589, 47592, 47596, 47604, 47605, 47607, 47608, 47609, - 47610, 47616, 47617, 47624, 47637, 47672, 47673, 47676, 47680, 47682, 47688, 47689, 47691, 47693, 47694, 47699, - 47700, 47701, 47704, 47708, 47716, 47717, 47719, 47720, 47721, 47728, 47729, 47732, 47736, 47747, 47748, 47749, - 47751, 47756, 47784, 47785, 47787, 47788, 47792, 47794, 47800, 47801, 47803, 47805, 47812, 47816, 47832, 47833, - 47868, 47872, 47876, 47885, 47887, 47889, 47896, 47900, 47904, 47913, 47915, 47924, 47925, 47926, 47928, 47931, - 47932, 47933, 47934, 47940, 47941, 47943, 47945, 47949, 47951, 47952, 47956, 47960, 47969, 47971, 47980, 48008, - 48012, 48016, 48036, 48040, 48044, 48052, 48055, 48064, 48068, 48072, 48080, 48083, 48120, 48121, 48124, 48127, - 48128, 48130, 48136, 48137, 48139, 48140, 48141, 48143, 48145, 48148, 48149, 48150, 48151, 48152, 48155, 48156, - 48157, 48158, 48159, 48164, 48165, 48167, 48169, 48173, 48176, 48177, 48180, 48184, 48192, 48193, 48195, 48196, - 48197, 48201, 48204, 48205, 48208, 48221, 48260, 48261, 48264, 48267, 48268, 48270, 48276, 48277, 48279, 48281, - 48282, 48288, 48289, 48292, 48295, 48296, 48304, 48305, 48307, 48308, 48309, 48316, 48317, 48320, 48324, 48333, - 48335, 48336, 48337, 48341, 48344, 48348, 48372, 48373, 48374, 48376, 48380, 48388, 48389, 48391, 48393, 48400, - 48404, 48420, 48428, 48448, 48456, 48457, 48460, 48464, 48472, 48473, 48484, 48488, 48512, 48513, 48516, 48519, - 48520, 48521, 48522, 48528, 48529, 48531, 48533, 48537, 48538, 48540, 48548, 48560, 48568, 48596, 48597, 48600, - 48604, 48617, 48624, 48628, 48632, 48640, 48643, 48645, 48652, 48653, 48656, 48660, 48668, 48669, 48671, 48708, - 48709, 48712, 48716, 48718, 48724, 48725, 48727, 48729, 48730, 48731, 48736, 48737, 48740, 48744, 48746, 48752, - 48753, 48755, 48756, 48757, 48763, 48764, 48765, 48768, 48772, 48780, 48781, 48783, 48784, 48785, 48792, 48793, - 48808, 48848, 48849, 48852, 48855, 48856, 48864, 48867, 48868, 48869, 48876, 48897, 48904, 48905, 48920, 48921, - 48923, 48924, 48925, 48960, 48961, 48964, 48968, 48976, 48977, 48981, 49044, 49072, 49093, 49100, 49101, 49104, - 49108, 49116, 49119, 49121, 49212, 49233, 49240, 49244, 49248, 49256, 49257, 49296, 49297, 49300, 49304, 49312, - 49313, 49315, 49317, 49324, 49325, 49327, 49328, 49331, 49332, 49333, 49334, 49340, 49341, 49343, 49344, 49345, - 49349, 49352, 49353, 49356, 49360, 49368, 49369, 49371, 49372, 49373, 49380, 49381, 49384, 49388, 49396, 49397, - 49399, 49401, 49408, 49412, 49416, 49424, 49429, 49436, 49437, 49438, 49439, 49440, 49443, 49444, 49446, 49447, - 49452, 49453, 49455, 49456, 49457, 49462, 49464, 49465, 49468, 49472, 49480, 49481, 49483, 49484, 49485, 49492, - 49493, 49496, 49500, 49508, 49509, 49511, 49512, 49513, 49520, 49524, 49528, 49541, 49548, 49549, 49550, 49552, - 49556, 49558, 49564, 49565, 49567, 49569, 49573, 49576, 49577, 49580, 49584, 49597, 49604, 49608, 49612, 49620, - 49623, 49624, 49632, 49636, 49640, 49648, 49649, 49651, 49660, 49661, 49664, 49668, 49676, 49677, 49679, 49681, - 49688, 49689, 49692, 49695, 49696, 49704, 49705, 49707, 49709, 49711, 49713, 49714, 49716, 49736, 49744, 49745, - 49748, 49752, 49760, 49765, 49772, 49773, 49776, 49780, 49788, 49789, 49791, 49793, 49800, 49801, 49808, 49816, - 49819, 49821, 49828, 49829, 49832, 49836, 49837, 49844, 49845, 49847, 49849, 49884, 49885, 49888, 49891, 49892, - 49899, 49900, 49901, 49903, 49905, 49910, 49912, 49913, 49915, 49916, 49920, 49928, 49929, 49932, 49933, 49939, - 49940, 49941, 49944, 49948, 49956, 49957, 49960, 49961, 49989, 50024, 50025, 50028, 50032, 50034, 50040, 50041, - 50044, 50045, 50052, 50056, 50060, 50112, 50136, 50137, 50140, 50143, 50144, 50146, 50152, 50153, 50157, 50164, - 50165, 50168, 50184, 50192, 50212, 50220, 50224, 50228, 50236, 50237, 50248, 50276, 50277, 50280, 50284, 50292, - 50293, 50297, 50304, 50324, 50332, 50360, 50364, 50409, 50416, 50417, 50420, 50424, 50426, 50431, 50432, 50433, - 50444, 50448, 50452, 50460, 50472, 50473, 50476, 50480, 50488, 50489, 50491, 50493, 50500, 50501, 50504, 50505, - 50506, 50508, 50509, 50510, 50515, 50516, 50517, 50519, 50520, 50521, 50525, 50526, 50528, 50529, 50532, 50536, - 50544, 50545, 50547, 50548, 50549, 50556, 50557, 50560, 50564, 50567, 50572, 50573, 50575, 50577, 50581, 50583, - 50584, 50588, 50592, 50601, 50612, 50613, 50616, 50617, 50619, 50620, 50621, 50622, 50628, 50629, 50630, 50631, - 50632, 50633, 50634, 50636, 50638, 50640, 50641, 50644, 50648, 50656, 50657, 50659, 50661, 50668, 50669, 50670, - 50672, 50676, 50678, 50679, 50684, 50685, 50686, 50687, 50688, 50689, 50693, 50694, 50695, 50696, 50700, 50704, - 50712, 50713, 50715, 50716, 50724, 50725, 50728, 50732, 50733, 50734, 50736, 50739, 50740, 50741, 50743, 50745, - 50747, 50752, 50753, 50756, 50760, 50768, 50769, 50771, 50772, 50773, 50780, 50781, 50784, 50796, 50799, 50801, - 50808, 50809, 50812, 50816, 50824, 50825, 50827, 50829, 50836, 50837, 50840, 50844, 50852, 50853, 50855, 50857, - 50864, 50865, 50868, 50872, 50873, 50874, 50880, 50881, 50883, 50885, 50892, 50893, 50896, 50900, 50908, 50909, - 50912, 50913, 50920, 50921, 50924, 50928, 50936, 50937, 50941, 50948, 50949, 50952, 50956, 50964, 50965, 50967, - 50969, 50976, 50977, 50980, 50984, 50992, 50993, 50995, 50997, 50999, 51004, 51005, 51008, 51012, 51018, 51020, - 51021, 51023, 51025, 51026, 51027, 51028, 51029, 51030, 51031, 51032, 51036, 51040, 51048, 51051, 51060, 51061, - 51064, 51068, 51069, 51070, 51075, 51076, 51077, 51079, 51080, 51081, 51082, 51086, 51088, 51089, 51092, 51094, - 51095, 51096, 51098, 51104, 51105, 51107, 51108, 51109, 51110, 51116, 51117, 51120, 51124, 51132, 51133, 51135, - 51136, 51137, 51144, 51145, 51148, 51150, 51152, 51160, 51165, 51172, 51176, 51180, 51200, 51201, 51204, 51208, - 51210, 51216, 51217, 51219, 51221, 51222, 51228, 51229, 51232, 51236, 51244, 51245, 51247, 51249, 51256, 51260, - 51264, 51272, 51273, 51276, 51277, 51284, 51312, 51313, 51316, 51320, 51322, 51328, 51329, 51331, 51333, 51334, - 51335, 51339, 51340, 51341, 51348, 51357, 51359, 51361, 51368, 51388, 51389, 51396, 51400, 51404, 51412, 51413, - 51415, 51417, 51424, 51425, 51428, 51445, 51452, 51453, 51456, 51460, 51461, 51462, 51468, 51469, 51471, 51473, - 51480, 51500, 51508, 51536, 51537, 51540, 51544, 51552, 51553, 51555, 51564, 51568, 51572, 51580, 51592, 51593, - 51596, 51600, 51608, 51609, 51611, 51613, 51648, 51649, 51652, 51655, 51656, 51658, 51664, 51665, 51667, 51669, - 51670, 51673, 51674, 51676, 51677, 51680, 51682, 51684, 51687, 51692, 51693, 51695, 51696, 51697, 51704, 51705, - 51708, 51712, 51720, 51721, 51723, 51724, 51725, 51732, 51736, 51753, 51788, 51789, 51792, 51796, 51804, 51805, - 51807, 51808, 51809, 51816, 51837, 51844, 51864, 51900, 51901, 51904, 51908, 51916, 51917, 51919, 51921, 51923, - 51928, 51929, 51936, 51948, 51956, 51976, 51984, 51988, 51992, 52000, 52001, 52033, 52040, 52041, 52044, 52048, - 52056, 52057, 52061, 52068, 52088, 52089, 52124, 52152, 52180, 52196, 52199, 52201, 52236, 52237, 52240, 52244, - 52252, 52253, 52257, 52258, 52263, 52264, 52265, 52268, 52270, 52272, 52280, 52281, 52283, 52284, 52285, 52286, - 52292, 52293, 52296, 52300, 52308, 52309, 52311, 52312, 52313, 52320, 52324, 52326, 52328, 52336, 52341, 52376, - 52377, 52380, 52384, 52392, 52393, 52395, 52396, 52397, 52404, 52405, 52408, 52412, 52420, 52421, 52423, 52425, - 52432, 52436, 52452, 52460, 52464, 52481, 52488, 52489, 52492, 52496, 52504, 52505, 52507, 52509, 52516, 52520, - 52524, 52537, 52572, 52576, 52580, 52588, 52589, 52591, 52593, 52600, 52616, 52628, 52629, 52632, 52636, 52644, - 52645, 52647, 52649, 52656, 52676, 52684, 52688, 52712, 52716, 52720, 52728, 52729, 52731, 52733, 52740, 52744, - 52748, 52756, 52761, 52768, 52769, 52772, 52776, 52784, 52785, 52787, 52789, 52824, 52825, 52828, 52831, 52832, - 52833, 52840, 52841, 52843, 52845, 52852, 52853, 52856, 52860, 52868, 52869, 52871, 52873, 52880, 52881, 52884, - 52888, 52896, 52897, 52899, 52900, 52901, 52908, 52909, 52929, 52964, 52965, 52968, 52971, 52972, 52980, 52981, - 52983, 52984, 52985, 52992, 52993, 52996, 53000, 53008, 53009, 53011, 53013, 53020, 53024, 53028, 53036, 53037, - 53039, 53040, 53041, 53048, 53076, 53077, 53080, 53084, 53092, 53093, 53095, 53097, 53104, 53105, 53108, 53112, - 53120, 53125, 53132, 53153, 53160, 53168, 53188, 53216, 53217, 53220, 53224, 53232, 53233, 53235, 53237, 53244, - 53248, 53252, 53265, 53272, 53293, 53300, 53301, 53304, 53308, 53316, 53317, 53319, 53321, 53328, 53332, 53336, - 53344, 53356, 53357, 53360, 53364, 53372, 53373, 53377, 53412, 53413, 53416, 53420, 53428, 53429, 53431, 53433, - 53440, 53441, 53444, 53448, 53449, 53456, 53457, 53459, 53460, 53461, 53468, 53469, 53472, 53476, 53484, 53485, - 53487, 53488, 53489, 53496, 53517, 53552, 53553, 53556, 53560, 53562, 53568, 53569, 53571, 53572, 53573, 53580, - 53581, 53584, 53588, 53596, 53597, 53599, 53601, 53608, 53612, 53628, 53636, 53640, 53664, 53665, 53668, 53672, - 53680, 53681, 53683, 53685, 53690, 53692, 53696, 53720, 53748, 53752, 53767, 53769, 53776, 53804, 53805, 53808, - 53812, 53820, 53821, 53823, 53825, 53832, 53852, 53860, 53888, 53889, 53892, 53896, 53904, 53905, 53909, 53916, - 53920, 53924, 53932, 53937, 53944, 53945, 53948, 53951, 53952, 53954, 53960, 53961, 53963, 53972, 53976, 53980, - 53988, 53989, 54000, 54001, 54004, 54008, 54016, 54017, 54019, 54021, 54028, 54029, 54030, 54032, 54036, 54038, - 54044, 54045, 54047, 54048, 54049, 54053, 54056, 54057, 54060, 54064, 54072, 54073, 54075, 54076, 54077, 54084, - 54085, 54140, 54141, 54144, 54148, 54156, 54157, 54159, 54160, 54161, 54168, 54169, 54172, 54176, 54184, 54185, - 54187, 54189, 54196, 54200, 54204, 54212, 54213, 54216, 54217, 54224, 54232, 54241, 54243, 54252, 54253, 54256, - 54260, 54268, 54269, 54271, 54273, 54280, 54301, 54336, 54340, 54364, 54368, 54372, 54381, 54383, 54392, 54393, - 54396, 54399, 54400, 54402, 54408, 54409, 54411, 54413, 54420, 54441, 54476, 54480, 54484, 54492, 54495, 54504, - 54508, 54512, 54520, 54523, 54525, 54532, 54536, 54540, 54548, 54549, 54551, 54588, 54589, 54592, 54596, 54604, - 54605, 54607, 54609, 54616, 54617, 54620, 54624, 54629, 54632, 54633, 54635, 54637, 54644, 54645, 54648, 54652, - 54660, 54661, 54663, 54664, 54665, 54672, 54693, 54728, 54729, 54732, 54736, 54738, 54744, 54745, 54747, 54749, - 54756, 54757, 54760, 54764, 54772, 54773, 54775, 54777, 54784, 54785, 54788, 54792, 54800, 54801, 54803, 54804, - 54805, 54812, 54816, 54820, 54829, 54840, 54841, 54844, 54848, 54853, 54856, 54857, 54859, 54861, 54865, 54868, - 54869, 54872, 54876, 54887, 54889, 54896, 54897, 54900, 54915, 54917, 54924, 54925, 54928, 54932, 54941, 54943, - 54945, 54952, 54956, 54960, 54969, 54971, 54980, 54981, 54984, 54988, 54993, 54996, 54999, 55001, 55008, 55012, - 55016, 55024, 55029, 55036, 55037, 55040, 55044, 55057, 55064, 55065, 55068, 55072, 55080, 55081, 55083, 55085, - 55092, 55093, 55096, 55100, 55108, 55111, 55113, 55120, 55121, 55124, 55126, 55127, 55128, 55129, 55136, 55137, - 55139, 55141, 55145, 55148, 55152, 55156, 55164, 55165, 55169, 55176, 55177, 55180, 55184, 55192, 55193, 55195, - 55197, 04352, 04353, 04354, 04355, 04356, 04357, 04358, 04359, 04360, 04361, 04362, 04363, 04364, 04365, 04366, - 04367, 04368, 04369, 04370, 04449, 04450, 04451, 04452, 04453, 04454, 04455, 04456, 04457, 04461, 04462, 04466, - 04467, 04469, 47252, 49968, 50108, 50388, 52012, 65535, - }; - - private static readonly Dictionary G4ValueId = G4Values - .Select((value, index) => new KeyValuePair(value, index)) - .ToDictionary(pair => pair.Key, pair => pair.Value); - - private static readonly Dictionary G4CharId = G4Chars - .Select((value, index) => new KeyValuePair(value, index)) - .ToDictionary(pair => pair.Key, pair => pair.Value); - - #endregion + int count = value.Length * 2; + if (count == destBuffer.Length) + return count; + WriteUInt16LittleEndian(destBuffer[count..], Terminator); + return count + 2; } } diff --git a/PKHeX.Core/PKM/Strings/StringConverter4GC.cs b/PKHeX.Core/PKM/Strings/StringConverter4GC.cs new file mode 100644 index 000000000..af5b20708 --- /dev/null +++ b/PKHeX.Core/PKM/Strings/StringConverter4GC.cs @@ -0,0 +1,130 @@ +using System; +using static PKHeX.Core.StringConverter4Util; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core; + +public static class StringConverter4GC +{ + private const ushort Terminator = 0xFFFF; + private const char TerminatorChar = (char)Terminator; + + /// + /// Converts Generation 4 Big Endian encoded character data to string. + /// + /// Byte array containing encoded character data. + /// Converted string. + public static string GetString(ReadOnlySpan data) + { + Span result = stackalloc char[data.Length]; + var length = LoadString(data, result); + return new string(result[..length].ToArray()); + } + + private static int LoadString(ReadOnlySpan data, Span result) + { + int i = 0; + for (; i < data.Length; i += 2) + { + var value = ReadUInt16BigEndian(data[i..]); + if (value == Terminator) + break; + char chr = (char)ConvertValue2CharG4(value); + if (chr == TerminatorChar) + break; + chr = StringConverter.SanitizeChar(chr); + result[i/2] = chr; + } + return i/2; + } + + /// + /// Converts a string to Generation 4 Big Endian encoded character data. + /// + /// + /// String to be converted. + /// Maximum length of string + /// + /// Byte array containing encoded character data + public static int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, + StringConverterOption option = StringConverterOption.ClearZero) + { + if (value.Length > maxLength) + value = value[..maxLength]; // Hard cap + + if (option is StringConverterOption.ClearZero) + destBuffer.Clear(); + + for (int i = 0; i < value.Length; i++) + { + var chr = value[i]; + chr = StringConverter.UnSanitizeChar5(chr); + ushort val = ConvertChar2ValueG4(chr); + WriteUInt16BigEndian(destBuffer[(i * 2)..], val); + } + + var count = value.Length * 2; + if (count == destBuffer.Length) + return count; + WriteUInt16BigEndian(destBuffer[count..], Terminator); + return count + 2; + } + + /// + /// Converts Generation 4 Big Endian encoded character data to string, with direct Unicode characters. + /// + /// Used by the Save File's internal strings. + /// Byte array containing encoded character data. + /// Converted string. + public static string GetStringUnicode(ReadOnlySpan data) + { + Span result = stackalloc char[data.Length]; + var length = LoadStringUnicode(data, result); + return new string(result[..length].ToArray()); + } + + private static int LoadStringUnicode(ReadOnlySpan data, Span result) + { + int i = 0; + for (; i < data.Length; i += 2) + { + char chr = (char)ReadUInt16BigEndian(data[i..]); + if (chr == TerminatorChar) + break; + chr = StringConverter.SanitizeChar(chr); + result[i/2] = chr; + } + return i/2; + } + + /// + /// Converts a string to Generation 4 Big Endian encoded character data, with direct Unicode characters. + /// + /// Used by the Save File's internal strings. + /// String to be converted. + /// + /// Maximum length of string + /// + /// Byte array containing encoded character data + public static int SetStringUnicode(ReadOnlySpan value, Span destBuffer, int maxLength, StringConverterOption option = StringConverterOption.ClearZero) + { + if (value.Length > maxLength) + value = value[..maxLength]; // Hard cap + + if (option is StringConverterOption.ClearZero) + destBuffer.Clear(); + + for (int i = 0; i < value.Length; i++) + { + var c = value[i]; + c = StringConverter.UnSanitizeChar5(c); + WriteUInt16BigEndian(destBuffer[(i * 2)..], c); + } + + var count = value.Length * 2; + if (count == destBuffer.Length) + return count; + WriteUInt16BigEndian(destBuffer[count..], 0); + return count + 2; + } +} diff --git a/PKHeX.Core/PKM/Strings/StringConverter4Util.cs b/PKHeX.Core/PKM/Strings/StringConverter4Util.cs new file mode 100644 index 000000000..b0db0dd80 --- /dev/null +++ b/PKHeX.Core/PKM/Strings/StringConverter4Util.cs @@ -0,0 +1,439 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PKHeX.Core; + +public static class StringConverter4Util +{ + /// + /// Converts a Generation 4 value to Unicode character. + /// + /// Encoded value. + /// Decoded value (unicode). + public static ushort ConvertValue2CharG4(ushort val) + { + return G4ValueId.TryGetValue(val, out int index) + ? G4Chars[index] : '?'; + } + + /// + /// Converts a Unicode character to Generation 4 value. + /// + /// Decoded value (unicode). + /// Encoded value. + public static ushort ConvertChar2ValueG4(ushort chr) + { + return G4CharId.TryGetValue(chr, out int index) + ? G4Values[index] : (ushort)00428; // '?' + } + + /// + /// Strips diacritics on gen1-4 french pkm names + /// + /// String to clean + /// Cleaned string + /// Only 4 characters are accented in gen1-4 + public static void StripDiacriticsFR4(Span input) + { + for (int i = 0; i < input.Length; i++) + { + if (FrDiacritic.TryGetValue(input[i], out var value)) + input[i] = value; + } + } + + private static readonly Dictionary FrDiacritic = new(4) + { + { 'È', 'E' }, + { 'É', 'E' }, + { 'Ê', 'E' }, + { 'Ï', 'I' }, + }; + + #region Conversion Data + + /// + /// Values (stored value) + /// + private static readonly ushort[] G4Values = + { + 00001, 00002, 00003, 00004, 00005, 00006, 00007, 00008, 00009, 00010, 00011, 00012, 00013, 00014, 00015, 00016, + 00017, 00018, 00019, 00020, 00021, 00022, 00023, 00024, 00025, 00026, 00027, 00028, 00029, 00030, 00031, 00032, + 00033, 00034, 00035, 00036, 00037, 00038, 00039, 00040, 00041, 00042, 00043, 00044, 00045, 00046, 00047, 00048, + 00049, 00050, 00051, 00052, 00053, 00054, 00055, 00056, 00057, 00058, 00059, 00060, 00061, 00062, 00063, 00064, + 00065, 00066, 00067, 00068, 00069, 00070, 00071, 00072, 00073, 00074, 00075, 00076, 00077, 00078, 00079, 00080, + 00081, 00082, 00083, 00084, 00085, 00086, 00087, 00088, 00089, 00090, 00091, 00092, 00093, 00094, 00095, 00096, + 00097, 00098, 00099, 00100, 00101, 00102, 00103, 00104, 00105, 00106, 00107, 00108, 00109, 00110, 00111, 00112, + 00113, 00114, 00115, 00116, 00117, 00118, 00119, 00120, 00121, 00122, 00123, 00124, 00125, 00126, 00127, 00128, + 00129, 00130, 00131, 00132, 00133, 00134, 00135, 00136, 00137, 00138, 00139, 00140, 00141, 00142, 00143, 00144, + 00145, 00146, 00147, 00148, 00149, 00150, 00151, 00152, 00153, 00154, 00155, 00156, 00157, 00158, 00159, 00160, + 00161, 00162, 00163, 00164, 00165, 00166, 00167, 00168, 00169, 00170, 00171, 00172, 00173, 00174, 00175, 00176, + 00177, 00178, 00179, 00180, 00181, 00182, 00183, 00184, 00185, 00186, 00187, 00188, 00189, 00190, 00191, 00192, + 00193, 00194, 00195, 00196, 00197, 00198, 00199, 00200, 00201, 00202, 00203, 00204, 00205, 00206, 00207, 00208, + 00209, 00210, 00211, 00212, 00213, 00214, 00215, 00216, 00217, 00218, 00219, 00220, 00221, 00222, 00223, 00225, + 00226, 00227, 00228, 00229, 00230, 00231, 00232, 00233, 00234, 00235, 00236, 00237, 00238, 00239, 00240, 00241, + 00242, 00243, 00244, 00245, 00246, 00247, 00248, 00249, 00250, 00251, 00252, 00253, 00254, 00255, 00256, 00257, + 00258, 00259, 00260, 00261, 00262, 00263, 00264, 00265, 00266, 00267, 00268, 00269, 00270, 00271, 00272, 00273, + 00275, 00276, 00277, 00278, 00279, 00280, 00281, 00282, 00283, 00284, 00285, 00286, 00287, 00288, 00289, + 00290, 00291, 00292, 00293, 00294, 00295, 00296, 00297, 00298, 00299, 00300, 00301, 00302, 00303, 00304, 00305, + 00306, 00307, 00308, 00309, 00310, 00311, 00312, 00313, 00314, 00315, 00316, 00317, 00318, 00319, 00320, 00321, + 00322, 00323, 00324, 00325, 00326, 00327, 00328, 00329, 00330, 00331, 00332, 00333, 00334, 00335, 00336, 00337, + 00338, 00339, 00340, 00341, 00342, 00343, 00344, 00345, 00346, 00347, 00348, 00349, 00350, 00351, 00352, 00353, + 00354, 00355, 00356, 00357, 00358, 00359, 00360, 00361, 00362, 00363, 00364, 00365, 00366, 00367, 00368, 00369, + 00370, 00371, 00372, 00373, 00374, 00375, 00376, 00377, 00378, 00379, 00380, 00381, 00382, 00383, 00384, 00385, + 00386, 00387, 00388, 00389, 00390, 00391, 00392, 00393, 00394, 00395, 00396, 00397, 00398, 00399, 00400, 00401, + 00402, 00403, 00404, 00405, 00406, 00407, 00408, 00409, 00410, 00411, 00412, 00413, 00414, 00415, 00416, 00417, + 00418, 00419, 00420, 00421, 00422, 00423, 00424, 00425, 00426, 00427, 00428, 00429, 00430, 00431, 00432, 00433, + 00434, 00435, 00436, 00437, 00438, 00439, 00440, 00441, 00442, 00443, 00444, 00445, 00446, 00447, 00448, 00449, + 00450, 00451, 00452, 00453, 00454, 00455, 00456, 00457, 00458, 00459, 00460, 00461, 00462, 00463, 00464, 00465, + 00466, 00467, 00468, 00469, 00470, 00471, 00472, 00473, 00474, 00475, 00476, 00477, 00478, + 01025, 01026, 01027, 01028, 01029, 01030, 01031, 01032, 01033, 01034, 01035, 01036, 01037, + 01038, 01039, 01040, 01041, 01042, 01043, 01044, 01045, 01046, 01047, 01048, 01049, 01050, 01051, 01052, 01053, + 01054, 01055, 01056, 01057, 01058, 01059, 01060, 01061, 01062, 01063, 01064, 01065, 01066, 01067, 01068, 01069, + 01070, 01071, 01072, 01073, 01074, 01075, 01076, 01077, 01078, 01079, 01080, 01081, 01082, 01083, 01084, 01085, + 01086, 01087, 01088, 01089, 01090, 01091, 01092, 01093, 01094, 01095, 01096, 01097, 01098, 01099, 01100, 01101, + 01102, 01103, 01104, 01105, 01106, 01107, 01108, 01109, 01110, 01111, 01112, 01113, 01114, 01115, 01116, 01117, + 01118, 01119, 01120, 01121, 01122, 01123, 01124, 01125, 01126, 01127, 01128, 01129, 01130, 01131, 01132, 01133, + 01134, 01135, 01136, 01137, 01138, 01139, 01140, 01141, 01142, 01143, 01144, 01145, 01146, 01147, 01148, 01149, + 01150, 01151, 01152, 01153, 01154, 01155, 01156, 01157, 01158, 01159, 01160, 01161, 01162, 01163, 01164, 01165, + 01166, 01167, 01168, 01169, 01170, 01171, 01172, 01173, 01174, 01175, 01176, 01177, 01178, 01179, 01180, 01181, + 01182, 01183, 01184, 01185, 01186, 01187, 01188, 01189, 01190, 01191, 01192, 01193, 01194, 01195, 01196, 01197, + 01198, 01199, 01200, 01201, 01202, 01203, 01204, 01205, 01206, 01207, 01208, 01209, 01210, 01211, 01212, 01213, + 01214, 01215, 01216, 01217, 01218, 01219, 01220, 01221, 01222, 01223, 01224, 01225, 01226, 01227, 01228, 01229, + 01230, 01231, 01232, 01233, 01234, 01235, 01236, 01237, 01238, 01239, 01240, 01241, 01242, 01243, 01244, 01245, + 01246, 01247, 01248, 01249, 01250, 01251, 01252, 01253, 01254, 01255, 01256, 01257, 01258, 01259, 01260, 01261, + 01262, 01263, 01264, 01265, 01266, 01267, 01268, 01269, 01270, 01271, 01272, 01273, 01274, 01275, 01276, 01277, + 01278, 01279, 01280, 01281, 01282, 01283, 01284, 01285, 01286, 01287, 01288, 01289, 01290, 01291, 01292, 01293, + 01294, 01295, 01296, 01297, 01298, 01299, 01300, 01301, 01302, 01303, 01304, 01305, 01306, 01307, 01308, 01309, + 01310, 01311, 01312, 01313, 01314, 01315, 01316, 01317, 01318, 01319, 01320, 01321, 01322, 01323, 01324, 01325, + 01326, 01327, 01328, 01329, 01330, 01331, 01332, 01333, 01334, 01335, 01336, 01337, 01338, 01339, 01340, 01341, + 01342, 01343, 01344, 01345, 01346, 01347, 01348, 01349, 01350, 01351, 01352, 01353, 01354, 01355, 01356, 01357, + 01358, 01359, 01360, 01361, 01362, 01363, 01364, 01365, 01366, 01367, 01368, 01369, 01370, 01371, 01372, 01373, + 01374, 01375, 01376, 01377, 01378, 01379, 01380, 01381, 01382, 01383, 01384, 01385, 01386, 01387, 01388, 01389, + 01390, 01391, 01392, 01393, 01394, 01395, 01396, 01397, 01398, 01399, 01400, 01401, 01402, 01403, 01404, 01405, + 01406, 01407, 01408, 01409, 01410, 01411, 01412, 01413, 01414, 01415, 01416, 01417, 01418, 01419, 01420, 01421, + 01422, 01423, 01424, 01425, 01426, 01427, 01428, 01429, 01430, 01431, 01432, 01433, 01434, 01435, 01436, 01437, + 01438, 01439, 01440, 01441, 01442, 01443, 01444, 01445, 01446, 01447, 01448, 01449, 01450, 01451, 01452, 01453, + 01454, 01455, 01456, 01457, 01458, 01459, 01460, 01461, 01462, 01463, 01464, 01465, 01466, 01467, 01468, 01469, + 01470, 01471, 01472, 01473, 01474, 01475, 01476, 01477, 01478, 01479, 01480, 01481, 01482, 01483, 01484, 01485, + 01486, 01487, 01488, 01489, 01490, 01491, 01492, 01493, 01494, 01495, 01496, 01497, 01498, 01499, 01500, 01501, + 01502, 01503, 01504, 01505, 01506, 01507, 01508, 01509, 01510, 01511, 01512, 01513, 01514, 01515, 01516, 01517, + 01518, 01519, 01520, 01521, 01522, 01523, 01524, 01525, 01526, 01527, 01528, 01529, 01530, 01531, 01532, 01533, + 01534, 01535, 01536, 01537, 01538, 01539, 01540, 01541, 01542, 01543, 01544, 01545, 01546, 01547, 01548, 01549, + 01550, 01551, 01552, 01553, 01554, 01555, 01556, 01557, 01558, 01559, 01560, 01561, 01562, 01563, 01564, 01565, + 01566, 01567, 01568, 01569, 01570, 01571, 01572, 01573, 01574, 01575, 01576, 01577, 01578, 01579, 01580, 01581, + 01582, 01583, 01584, 01585, 01586, 01587, 01588, 01589, 01590, 01591, 01592, 01593, 01594, 01595, 01596, 01597, + 01598, 01599, 01600, 01601, 01602, 01603, 01604, 01605, 01606, 01607, 01608, 01609, 01610, 01611, 01612, 01613, + 01614, 01615, 01616, 01617, 01618, 01619, 01620, 01621, 01622, 01623, 01624, 01625, 01626, 01627, 01628, 01629, + 01630, 01631, 01632, 01633, 01634, 01635, 01636, 01637, 01638, 01639, 01640, 01641, 01642, 01643, 01644, 01645, + 01646, 01647, 01648, 01649, 01650, 01651, 01652, 01653, 01654, 01655, 01656, 01657, 01658, 01659, 01660, 01661, + 01662, 01663, 01664, 01665, 01666, 01667, 01668, 01669, 01670, 01671, 01672, 01673, 01674, 01675, 01676, 01677, + 01678, 01679, 01680, 01681, 01682, 01683, 01684, 01685, 01686, 01687, 01688, 01689, 01690, 01691, 01692, 01693, + 01694, 01695, 01696, 01697, 01698, 01699, 01700, 01701, 01702, 01703, 01704, 01705, 01706, 01707, 01708, 01709, + 01710, 01711, 01712, 01713, 01714, 01715, 01716, 01717, 01718, 01719, 01720, 01721, 01722, 01723, 01724, 01725, + 01726, 01727, 01728, 01729, 01730, 01731, 01732, 01733, 01734, 01735, 01736, 01737, 01738, 01739, 01740, 01741, + 01742, 01743, 01744, 01745, 01746, 01747, 01748, 01749, 01750, 01751, 01752, 01753, 01754, 01755, 01756, 01757, + 01758, 01759, 01760, 01761, 01762, 01763, 01764, 01765, 01766, 01767, 01768, 01769, 01770, 01771, 01772, 01773, + 01774, 01775, 01776, 01777, 01778, 01779, 01780, 01781, 01782, 01783, 01784, 01785, 01786, 01787, 01788, 01789, + 01790, 01791, 01792, 01793, 01794, 01795, 01796, 01797, 01798, 01799, 01800, 01801, 01802, 01803, 01804, 01805, + 01806, 01807, 01808, 01809, 01810, 01811, 01812, 01813, 01814, 01815, 01816, 01817, 01818, 01819, 01820, 01821, + 01822, 01823, 01824, 01825, 01826, 01827, 01828, 01829, 01830, 01831, 01832, 01833, 01834, 01835, 01836, 01837, + 01838, 01839, 01840, 01841, 01842, 01843, 01844, 01845, 01846, 01847, 01848, 01849, 01850, 01851, 01852, 01853, + 01854, 01855, 01856, 01857, 01858, 01859, 01860, 01861, 01862, 01863, 01864, 01865, 01866, 01867, 01868, 01869, + 01870, 01871, 01872, 01873, 01874, 01875, 01876, 01877, 01878, 01879, 01880, 01881, 01882, 01883, 01884, 01885, + 01886, 01887, 01888, 01889, 01890, 01891, 01892, 01893, 01894, 01895, 01896, 01897, 01898, 01899, 01900, 01901, + 01902, 01903, 01904, 01905, 01906, 01907, 01908, 01909, 01910, 01911, 01912, 01913, 01914, 01915, 01916, 01917, + 01918, 01919, 01920, 01921, 01922, 01923, 01924, 01925, 01926, 01927, 01928, 01929, 01930, 01931, 01932, 01933, + 01934, 01935, 01936, 01937, 01938, 01939, 01940, 01941, 01942, 01943, 01944, 01945, 01946, 01947, 01948, 01949, + 01950, 01951, 01952, 01953, 01954, 01955, 01956, 01957, 01958, 01959, 01960, 01961, 01962, 01963, 01964, 01965, + 01966, 01967, 01968, 01969, 01970, 01971, 01972, 01973, 01974, 01975, 01976, 01977, 01978, 01979, 01980, 01981, + 01982, 01983, 01984, 01985, 01986, 01987, 01988, 01989, 01990, 01991, 01992, 01993, 01994, 01995, 01996, 01997, + 01998, 01999, 02000, 02001, 02002, 02003, 02004, 02005, 02006, 02007, 02008, 02009, 02010, 02011, 02012, 02013, + 02014, 02015, 02016, 02017, 02018, 02019, 02020, 02021, 02022, 02023, 02024, 02025, 02026, 02027, 02028, 02029, + 02030, 02031, 02032, 02033, 02034, 02035, 02036, 02037, 02038, 02039, 02040, 02041, 02042, 02043, 02044, 02045, + 02046, 02047, 02048, 02049, 02050, 02051, 02052, 02053, 02054, 02055, 02056, 02057, 02058, 02059, 02060, 02061, + 02062, 02063, 02064, 02065, 02066, 02067, 02068, 02069, 02070, 02071, 02072, 02073, 02074, 02075, 02076, 02077, + 02078, 02079, 02080, 02081, 02082, 02083, 02084, 02085, 02086, 02087, 02088, 02089, 02090, 02091, 02092, 02093, + 02094, 02095, 02096, 02097, 02098, 02099, 02100, 02101, 02102, 02103, 02104, 02105, 02106, 02107, 02108, 02109, + 02110, 02111, 02112, 02113, 02114, 02115, 02116, 02117, 02118, 02119, 02120, 02121, 02122, 02123, 02124, 02125, + 02126, 02127, 02128, 02129, 02130, 02131, 02132, 02133, 02134, 02135, 02136, 02137, 02138, 02139, 02140, 02141, + 02142, 02143, 02144, 02145, 02146, 02147, 02148, 02149, 02150, 02151, 02152, 02153, 02154, 02155, 02156, 02157, + 02158, 02159, 02160, 02161, 02162, 02163, 02164, 02165, 02166, 02167, 02168, 02169, 02170, 02171, 02172, 02173, + 02174, 02175, 02176, 02177, 02178, 02179, 02180, 02181, 02182, 02183, 02184, 02185, 02186, 02187, 02188, 02189, + 02190, 02191, 02192, 02193, 02194, 02195, 02196, 02197, 02198, 02199, 02200, 02201, 02202, 02203, 02204, 02205, + 02206, 02207, 02208, 02209, 02210, 02211, 02212, 02213, 02214, 02215, 02216, 02217, 02218, 02219, 02220, 02221, + 02222, 02223, 02224, 02225, 02226, 02227, 02228, 02229, 02230, 02231, 02232, 02233, 02234, 02235, 02236, 02237, + 02238, 02239, 02240, 02241, 02242, 02243, 02244, 02245, 02246, 02247, 02248, 02249, 02250, 02251, 02252, 02253, + 02254, 02255, 02256, 02257, 02258, 02259, 02260, 02261, 02262, 02263, 02264, 02265, 02266, 02267, 02268, 02269, + 02270, 02271, 02272, 02273, 02274, 02275, 02276, 02277, 02278, 02279, 02280, 02281, 02282, 02283, 02284, 02285, + 02286, 02287, 02288, 02289, 02290, 02291, 02292, 02293, 02294, 02295, 02296, 02297, 02298, 02299, 02300, 02301, + 02302, 02303, 02304, 02305, 02306, 02307, 02308, 02309, 02310, 02311, 02312, 02313, 02314, 02315, 02316, 02317, + 02318, 02319, 02320, 02321, 02322, 02323, 02324, 02325, 02326, 02327, 02328, 02329, 02330, 02331, 02332, 02333, + 02334, 02335, 02336, 02337, 02338, 02339, 02340, 02341, 02342, 02343, 02344, 02345, 02346, 02347, 02348, 02349, + 02350, 02351, 02352, 02353, 02354, 02355, 02356, 02357, 02358, 02359, 02360, 02361, 02362, 02363, 02364, 02365, + 02366, 02367, 02368, 02369, 02370, 02371, 02372, 02373, 02374, 02375, 02376, 02377, 02378, 02379, 02380, 02381, + 02382, 02383, 02384, 02385, 02386, 02387, 02388, 02389, 02390, 02391, 02392, 02393, 02394, 02395, 02396, 02397, + 02398, 02399, 02400, 02401, 02402, 02403, 02404, 02405, 02406, 02407, 02408, 02409, 02410, 02411, 02412, 02413, + 02414, 02415, 02416, 02417, 02418, 02419, 02420, 02421, 02422, 02423, 02424, 02425, 02426, 02427, 02428, 02429, + 02430, 02431, 02432, 02433, 02434, 02435, 02436, 02437, 02438, 02439, 02440, 02441, 02442, 02443, 02444, 02445, + 02446, 02447, 02448, 02449, 02450, 02451, 02452, 02453, 02454, 02455, 02456, 02457, 02458, 02459, 02460, 02461, + 02462, 02463, 02464, 02465, 02466, 02467, 02468, 02469, 02470, 02471, 02472, 02473, 02474, 02475, 02476, 02477, + 02478, 02479, 02480, 02481, 02482, 02483, 02484, 02485, 02486, 02487, 02488, 02489, 02490, 02491, 02492, 02493, + 02494, 02495, 02496, 02497, 02498, 02499, 02500, 02501, 02502, 02503, 02504, 02505, 02506, 02507, 02508, 02509, + 02510, 02511, 02512, 02513, 02514, 02515, 02516, 02517, 02518, 02519, 02520, 02521, 02522, 02523, 02524, 02525, + 02526, 02527, 02528, 02529, 02530, 02531, 02532, 02533, 02534, 02535, 02536, 02537, 02538, 02539, 02540, 02541, + 02542, 02543, 02544, 02545, 02546, 02547, 02548, 02549, 02550, 02551, 02552, 02553, 02554, 02555, 02556, 02557, + 02558, 02559, 02560, 02561, 02562, 02563, 02564, 02565, 02566, 02567, 02568, 02569, 02570, 02571, 02572, 02573, + 02574, 02575, 02576, 02577, 02578, 02579, 02580, 02581, 02582, 02583, 02584, 02585, 02586, 02587, 02588, 02589, + 02590, 02591, 02592, 02593, 02594, 02595, 02596, 02597, 02598, 02599, 02600, 02601, 02602, 02603, 02604, 02605, + 02606, 02607, 02608, 02609, 02610, 02611, 02612, 02613, 02614, 02615, 02616, 02617, 02618, 02619, 02620, 02621, + 02622, 02623, 02624, 02625, 02626, 02627, 02628, 02629, 02630, 02631, 02632, 02633, 02634, 02635, 02636, 02637, + 02638, 02639, 02640, 02641, 02642, 02643, 02644, 02645, 02646, 02647, 02648, 02649, 02650, 02651, 02652, 02653, + 02654, 02655, 02656, 02657, 02658, 02659, 02660, 02661, 02662, 02663, 02664, 02665, 02666, 02667, 02668, 02669, + 02670, 02671, 02672, 02673, 02674, 02675, 02676, 02677, 02678, 02679, 02680, 02681, 02682, 02683, 02684, 02685, + 02686, 02687, 02688, 02689, 02690, 02691, 02692, 02693, 02694, 02695, 02696, 02697, 02698, 02699, 02700, 02701, + 02702, 02703, 02704, 02705, 02706, 02707, 02708, 02709, 02710, 02711, 02712, 02713, 02714, 02715, 02716, 02717, + 02718, 02719, 02720, 02721, 02722, 02723, 02724, 02725, 02726, 02727, 02728, 02729, 02730, 02731, 02732, 02733, + 02734, 02735, 02736, 02737, 02738, 02739, 02740, 02741, 02742, 02743, 02744, 02745, 02746, 02747, 02748, 02749, + 02750, 02751, 02752, 02753, 02754, 02755, 02756, 02757, 02758, 02759, 02760, 02761, 02762, 02763, 02764, 02765, + 02766, 02767, 02768, 02769, 02770, 02771, 02772, 02773, 02774, 02775, 02776, 02777, 02778, 02779, 02780, 02781, + 02782, 02783, 02784, 02785, 02786, 02787, 02788, 02789, 02790, 02791, 02792, 02793, 02794, 02795, 02796, 02797, + 02798, 02799, 02800, 02801, 02802, 02803, 02804, 02805, 02806, 02807, 02808, 02809, 02810, 02811, 02812, 02813, + 02814, 02815, 02816, 02817, 02818, 02819, 02820, 02821, 02822, 02823, 02824, 02825, 02826, 02827, 02828, 02829, + 02830, 02831, 02832, 02833, 02834, 02835, 02836, 02837, 02838, 02839, 02840, 02841, 02842, 02843, 02844, 02845, + 02846, 02847, 02848, 02849, 02850, 02851, 02852, 02853, 02854, 02855, 02856, 02857, 02858, 02859, 02860, 02861, + 02862, 02863, 02864, 02865, 02866, 02867, 02868, 02869, 02870, 02871, 02872, 02873, 02874, 02875, 02876, 02877, + 02878, 02879, 02880, 02881, 02882, 02883, 02884, 02885, 02886, 02887, 02888, 02889, 02890, 02891, 02892, 02893, + 02894, 02895, 02896, 02897, 02898, 02899, 02900, 02901, 02902, 02903, 02904, 02905, 02906, 02907, 02908, 02909, + 02910, 02911, 02912, 02913, 02914, 02915, 02916, 02917, 02918, 02919, 02920, 02921, 02922, 02923, 02924, 02925, + 02926, 02927, 02928, 02929, 02930, 02931, 02932, 02933, 02934, 02935, 02936, 02937, 02938, 02939, 02940, 02941, + 02942, 02943, 02944, 02945, 02946, 02947, 02948, 02949, 02950, 02951, 02952, 02953, 02954, 02955, 02956, 02957, + 02958, 02959, 02960, 02961, 02962, 02963, 02964, 02965, 02966, 02967, 02968, 02969, 02970, 02971, 02972, 02973, + 02974, 02975, 02976, 02977, 02978, 02979, 02980, 02981, 02982, 02983, 02984, 02985, 02986, 02987, 02988, 02989, + 02990, 02991, 02992, 02993, 02994, 02995, 02996, 02997, 02998, 02999, 03000, 03001, 03002, 03003, 03004, 03005, + 03006, 03007, 03008, 03009, 03010, 03011, 03012, 03013, 03014, 03015, 03016, 03017, 03018, 03019, 03020, 03021, + 03022, 03023, 03024, 03025, 03026, 03027, 03028, 03029, 03030, 03031, 03032, 03033, 03034, 03035, 03036, 03037, + 03038, 03039, 03040, 03041, 03042, 03043, 03044, 03045, 03046, 03047, 03048, 03049, 03050, 03051, 03052, 03053, + 03054, 03055, 03056, 03057, 03058, 03059, 03060, 03061, 03062, 03063, 03064, 03065, 03066, 03067, 03068, 03069, + 03070, 03071, 03072, 03073, 03074, 03075, 03076, 03077, 03078, 03079, 03080, 03081, 03082, 03083, 03084, 03085, + 03086, 03087, 03088, 03089, 03090, 03091, 03092, 03093, 03094, 03095, 03096, 03097, 03098, 03099, 03100, 03101, + 03102, 03103, 03104, 03105, 03106, 03107, 03108, 03109, 03110, 03111, 03112, 03113, 03114, 03115, 03116, 03117, + 03118, 03119, 03120, 03121, 03122, 03123, 03124, 03125, 03126, 03127, 03128, 03129, 03130, 03131, 03132, 03133, + 03134, 03135, 03136, 03137, 03138, 03139, 03140, 03141, 03142, 03143, 03144, 03145, 03146, 03147, 03148, 03149, + 03150, 03151, 03152, 03153, 03154, 03155, 03156, 03157, 03158, 03159, 03160, 03161, 03162, 03163, 03164, 03165, + 03166, 03167, 03168, 03169, 03170, 03171, 03172, 03173, 03174, 03175, 03176, 03177, 03178, 03179, 03180, 03181, + 03182, 03183, 03184, 03185, 03186, 03187, 03188, 03189, 03190, 03191, 03192, 03193, 03194, 03195, 03196, 03197, + 03198, 03199, 03200, 03201, 03202, 03203, 03204, 03205, 03206, 03207, 03208, 03209, 03210, 03211, 03212, 03213, + 03214, 03215, 03216, 03217, 03218, 03219, 03220, 03221, 03222, 03223, 03224, 03225, 03226, 03227, 03228, 03229, + 03230, 03231, 03232, 03233, 03234, 03235, 03236, 03237, 03238, 03239, 03240, 03241, 03242, 03243, 03244, 03245, + 03246, 03247, 03248, 03249, 03250, 03251, 03252, 03253, 03254, 03255, 03256, 03257, 03258, 03259, 03260, 03261, + 03262, 03263, 03264, 03265, 03266, 03267, 03268, 03269, 03270, 03271, 03272, 03273, 03274, 03275, 03276, 03277, + 03278, 03279, 03280, 03281, 03282, 03283, 03284, 03285, 03286, 03287, 03288, 03289, 03290, 03291, 03292, 03293, + 03294, 03295, 03296, 03297, 03298, 03299, 03300, 03301, 03302, 03303, 03304, 03305, 03306, 03307, 03308, 03309, + 03310, 03311, 03312, 03313, 03314, 03315, 03316, 03317, 03318, 03319, 03320, 03321, 03322, 03323, 03324, 03325, + 03326, 03327, 03328, 03329, 03330, 03331, 03332, 03333, 03334, 03335, 03336, 03337, 03338, 03339, 03340, 03341, + 03342, 03343, 03344, 03345, 03346, 03347, 03348, 03349, 03350, 03351, 03352, 03353, 03354, 03355, 03356, 03357, + 03358, 03359, 03360, 03361, 03362, 03363, 03364, 03365, 03366, 03367, 03368, 03369, 03370, 03371, 03372, 03373, + 03374, 03377, 03378, 03379, 03380, 03381, 03382, 03383, 03384, 03385, 03386, 03387, 03388, 03389, 03390, 03391, + 03392, 03393, 03394, 03395, 03396, 03397, 03398, 03399, 03400, 03401, 03402, 03403, 03404, 03405, 03406, 03407, + 03408, 03409, 03425, 03426, 03427, 03428, 03429, 65535, + }; + + /// + /// Characters (Unicode representation) + /// + private static readonly ushort[] G4Chars = + { + 12288, 12353, 12354, 12355, 12356, 12357, 12358, 12359, 12360, 12361, 12362, 12363, 12364, 12365, 12366, 12367, + 12368, 12369, 12370, 12371, 12372, 12373, 12374, 12375, 12376, 12377, 12378, 12379, 12380, 12381, 12382, 12383, + 12384, 12385, 12386, 12387, 12388, 12389, 12390, 12391, 12392, 12393, 12394, 12395, 12396, 12397, 12398, 12399, + 12400, 12401, 12402, 12403, 12404, 12405, 12406, 12407, 12408, 12409, 12410, 12411, 12412, 12413, 12414, 12415, + 12416, 12417, 12418, 12419, 12420, 12421, 12422, 12423, 12424, 12425, 12426, 12427, 12428, 12429, 12431, 12434, + 12435, 12449, 12450, 12451, 12452, 12453, 12454, 12455, 12456, 12457, 12458, 12459, 12460, 12461, 12462, 12463, + 12464, 12465, 12466, 12467, 12468, 12469, 12470, 12471, 12472, 12473, 12474, 12475, 12476, 12477, 12478, 12479, + 12480, 12481, 12482, 12483, 12484, 12485, 12486, 12487, 12488, 12489, 12490, 12491, 12492, 12493, 12494, 12495, + 12496, 12497, 12498, 12499, 12500, 12501, 12502, 12503, 12504, 12505, 12506, 12507, 12508, 12509, 12510, 12511, + 12512, 12513, 12514, 12515, 12516, 12517, 12518, 12519, 12520, 12521, 12522, 12523, 12524, 12525, 12527, 12530, + 12531, 65296, 65297, 65298, 65299, 65300, 65301, 65302, 65303, 65304, 65305, 65313, 65314, 65315, 65316, 65317, + 65318, 65319, 65320, 65321, 65322, 65323, 65324, 65325, 65326, 65327, 65328, 65329, 65330, 65331, 65332, 65333, + 65334, 65335, 65336, 65337, 65338, 65345, 65346, 65347, 65348, 65349, 65350, 65351, 65352, 65353, 65354, 65355, + 65356, 65357, 65358, 65359, 65360, 65361, 65362, 65363, 65364, 65365, 65366, 65367, 65368, 65369, 65370, 65281, + 65311, 12289, 12290, 08943, 12539, 65295, 12300, 12301, 12302, 12303, 65288, 65289, 09325, 09326, 65291, 12540, + 09319, 09320, 65309, 65374, 65306, 65307, 65294, 65292, 09824, 09827, 09829, 09830, 09733, 09678, 09675, 09633, + 09651, 09671, 65312, 09834, 65285, 09728, 09729, 09730, 09731, 09312, 09313, 09314, 09738, 09316, 09317, 09318, + 09800, 09801, 09802, 09803, 09804, 09805, 09806, 09807, 08592, 08593, 08595, 08594, 08227, 65286, 00048, + 00049, 00050, 00051, 00052, 00053, 00054, 00055, 00056, 00057, 00065, 00066, 00067, 00068, 00069, 00070, 00071, + 00072, 00073, 00074, 00075, 00076, 00077, 00078, 00079, 00080, 00081, 00082, 00083, 00084, 00085, 00086, 00087, + 00088, 00089, 00090, 00097, 00098, 00099, 00100, 00101, 00102, 00103, 00104, 00105, 00106, 00107, 00108, 00109, + 00110, 00111, 00112, 00113, 00114, 00115, 00116, 00117, 00118, 00119, 00120, 00121, 00122, 00192, 00193, 00194, + 00195, 00196, 00197, 00198, 00199, 00200, 00201, 00202, 00203, 00204, 00205, 00206, 00207, 00208, 00209, 00210, + 00211, 00212, 00213, 00214, 00215, 00216, 00217, 00218, 00219, 00220, 00221, 00222, 00223, 00224, 00225, 00226, + 00227, 00228, 00229, 00230, 00231, 00232, 00233, 00234, 00235, 00236, 00237, 00238, 00239, 00240, 00241, 00242, + 00243, 00244, 00245, 00246, 00247, 00248, 00249, 00250, 00251, 00252, 00253, 00254, 00255, 00338, 00339, 00350, + 00351, 00170, 00186, 00185, 00178, 00179, 00036, 00161, 00191, 00033, 00063, 00044, 00046, 09324, 65381, 00047, + 08216, 08217, 08220, 08221, 08222, 12298, 12299, 00040, 00041, 09794, 09792, 00043, 00045, 00042, 00035, 00061, + 00038, 00126, 00058, 00059, 09327, 09328, 09329, 09330, 09331, 09332, 09333, 09334, 09335, 09336, 00064, 09337, + 00037, 09338, 09339, 09340, 09341, 09342, 09343, 09344, 09345, 09346, 09347, 09348, 00032, + 44032, 44033, 44036, 44039, 44040, 44041, 44042, 44048, 44049, 44050, 44051, 44052, 44053, + 44054, 44055, 44057, 44058, 44059, 44060, 44061, 44064, 44068, 44076, 44077, 44079, 44080, 44081, 44088, 44089, + 44092, 44096, 44107, 44109, 44116, 44120, 44124, 44144, 44145, 44148, 44151, 44152, 44154, 44160, 44161, 44163, + 44164, 44165, 44166, 44169, 44170, 44171, 44172, 44176, 44180, 44188, 44189, 44191, 44192, 44193, 44200, 44201, + 44202, 44204, 44207, 44208, 44216, 44217, 44219, 44220, 44221, 44225, 44228, 44232, 44236, 44245, 44247, 44256, + 44257, 44260, 44263, 44264, 44266, 44268, 44271, 44272, 44273, 44275, 44277, 44278, 44284, 44285, 44288, 44292, + 44294, 44300, 44301, 44303, 44305, 44312, 44316, 44320, 44329, 44332, 44333, 44340, 44341, 44344, 44348, 44356, + 44357, 44359, 44361, 44368, 44372, 44376, 44385, 44387, 44396, 44397, 44400, 44403, 44404, 44405, 44406, 44411, + 44412, 44413, 44415, 44417, 44418, 44424, 44425, 44428, 44432, 44444, 44445, 44452, 44471, 44480, 44481, 44484, + 44488, 44496, 44497, 44499, 44508, 44512, 44516, 44536, 44537, 44540, 44543, 44544, 44545, 44552, 44553, 44555, + 44557, 44564, 44592, 44593, 44596, 44599, 44600, 44602, 44608, 44609, 44611, 44613, 44614, 44618, 44620, 44621, + 44622, 44624, 44628, 44630, 44636, 44637, 44639, 44640, 44641, 44645, 44648, 44649, 44652, 44656, 44664, 44665, + 44667, 44668, 44669, 44676, 44677, 44684, 44732, 44733, 44734, 44736, 44740, 44748, 44749, 44751, 44752, 44753, + 44760, 44761, 44764, 44776, 44779, 44781, 44788, 44792, 44796, 44807, 44808, 44813, 44816, 44844, 44845, 44848, + 44850, 44852, 44860, 44861, 44863, 44865, 44866, 44867, 44872, 44873, 44880, 44892, 44893, 44900, 44901, 44921, + 44928, 44932, 44936, 44944, 44945, 44949, 44956, 44984, 44985, 44988, 44992, 44999, 45000, 45001, 45003, 45005, + 45006, 45012, 45020, 45032, 45033, 45040, 45041, 45044, 45048, 45056, 45057, 45060, 45068, 45072, 45076, 45084, + 45085, 45096, 45124, 45125, 45128, 45130, 45132, 45134, 45139, 45140, 45141, 45143, 45145, 45149, 45180, 45181, + 45184, 45188, 45196, 45197, 45199, 45201, 45208, 45209, 45210, 45212, 45215, 45216, 45217, 45218, 45224, 45225, + 45227, 45228, 45229, 45230, 45231, 45233, 45235, 45236, 45237, 45240, 45244, 45252, 45253, 45255, 45256, 45257, + 45264, 45265, 45268, 45272, 45280, 45285, 45320, 45321, 45323, 45324, 45328, 45330, 45331, 45336, 45337, 45339, + 45340, 45341, 45347, 45348, 45349, 45352, 45356, 45364, 45365, 45367, 45368, 45369, 45376, 45377, 45380, 45384, + 45392, 45393, 45396, 45397, 45400, 45404, 45408, 45432, 45433, 45436, 45440, 45442, 45448, 45449, 45451, 45453, + 45458, 45459, 45460, 45464, 45468, 45480, 45516, 45520, 45524, 45532, 45533, 45535, 45544, 45545, 45548, 45552, + 45561, 45563, 45565, 45572, 45573, 45576, 45579, 45580, 45588, 45589, 45591, 45593, 45600, 45620, 45628, 45656, + 45660, 45664, 45672, 45673, 45684, 45685, 45692, 45700, 45701, 45705, 45712, 45713, 45716, 45720, 45721, 45722, + 45728, 45729, 45731, 45733, 45734, 45738, 45740, 45744, 45748, 45768, 45769, 45772, 45776, 45778, 45784, 45785, + 45787, 45789, 45794, 45796, 45797, 45798, 45800, 45803, 45804, 45805, 45806, 45807, 45811, 45812, 45813, 45815, + 45816, 45817, 45818, 45819, 45823, 45824, 45825, 45828, 45832, 45840, 45841, 45843, 45844, 45845, 45852, 45908, + 45909, 45910, 45912, 45915, 45916, 45918, 45919, 45924, 45925, 45927, 45929, 45931, 45934, 45936, 45937, 45940, + 45944, 45952, 45953, 45955, 45956, 45957, 45964, 45968, 45972, 45984, 45985, 45992, 45996, 46020, 46021, 46024, + 46027, 46028, 46030, 46032, 46036, 46037, 46039, 46041, 46043, 46045, 46048, 46052, 46056, 46076, 46096, 46104, + 46108, 46112, 46120, 46121, 46123, 46132, 46160, 46161, 46164, 46168, 46176, 46177, 46179, 46181, 46188, 46208, + 46216, 46237, 46244, 46248, 46252, 46261, 46263, 46265, 46272, 46276, 46280, 46288, 46293, 46300, 46301, 46304, + 46307, 46308, 46310, 46316, 46317, 46319, 46321, 46328, 46356, 46357, 46360, 46363, 46364, 46372, 46373, 46375, + 46376, 46377, 46378, 46384, 46385, 46388, 46392, 46400, 46401, 46403, 46404, 46405, 46411, 46412, 46413, 46416, + 46420, 46428, 46429, 46431, 46432, 46433, 46496, 46497, 46500, 46504, 46506, 46507, 46512, 46513, 46515, 46516, + 46517, 46523, 46524, 46525, 46528, 46532, 46540, 46541, 46543, 46544, 46545, 46552, 46572, 46608, 46609, 46612, + 46616, 46629, 46636, 46644, 46664, 46692, 46696, 46748, 46749, 46752, 46756, 46763, 46764, 46769, 46804, 46832, + 46836, 46840, 46848, 46849, 46853, 46888, 46889, 46892, 46895, 46896, 46904, 46905, 46907, 46916, 46920, 46924, + 46932, 46933, 46944, 46948, 46952, 46960, 46961, 46963, 46965, 46972, 46973, 46976, 46980, 46988, 46989, 46991, + 46992, 46993, 46994, 46998, 46999, 47000, 47001, 47004, 47008, 47016, 47017, 47019, 47020, 47021, 47028, 47029, + 47032, 47047, 47049, 47084, 47085, 47088, 47092, 47100, 47101, 47103, 47104, 47105, 47111, 47112, 47113, 47116, + 47120, 47128, 47129, 47131, 47133, 47140, 47141, 47144, 47148, 47156, 47157, 47159, 47160, 47161, 47168, 47172, + 47185, 47187, 47196, 47197, 47200, 47204, 47212, 47213, 47215, 47217, 47224, 47228, 47245, 47272, 47280, 47284, + 47288, 47296, 47297, 47299, 47301, 47308, 47312, 47316, 47325, 47327, 47329, 47336, 47337, 47340, 47344, 47352, + 47353, 47355, 47357, 47364, 47384, 47392, 47420, 47421, 47424, 47428, 47436, 47439, 47441, 47448, 47449, 47452, + 47456, 47464, 47465, 47467, 47469, 47476, 47477, 47480, 47484, 47492, 47493, 47495, 47497, 47498, 47501, 47502, + 47532, 47533, 47536, 47540, 47548, 47549, 47551, 47553, 47560, 47561, 47564, 47566, 47567, 47568, 47569, 47570, + 47576, 47577, 47579, 47581, 47582, 47585, 47587, 47588, 47589, 47592, 47596, 47604, 47605, 47607, 47608, 47609, + 47610, 47616, 47617, 47624, 47637, 47672, 47673, 47676, 47680, 47682, 47688, 47689, 47691, 47693, 47694, 47699, + 47700, 47701, 47704, 47708, 47716, 47717, 47719, 47720, 47721, 47728, 47729, 47732, 47736, 47747, 47748, 47749, + 47751, 47756, 47784, 47785, 47787, 47788, 47792, 47794, 47800, 47801, 47803, 47805, 47812, 47816, 47832, 47833, + 47868, 47872, 47876, 47885, 47887, 47889, 47896, 47900, 47904, 47913, 47915, 47924, 47925, 47926, 47928, 47931, + 47932, 47933, 47934, 47940, 47941, 47943, 47945, 47949, 47951, 47952, 47956, 47960, 47969, 47971, 47980, 48008, + 48012, 48016, 48036, 48040, 48044, 48052, 48055, 48064, 48068, 48072, 48080, 48083, 48120, 48121, 48124, 48127, + 48128, 48130, 48136, 48137, 48139, 48140, 48141, 48143, 48145, 48148, 48149, 48150, 48151, 48152, 48155, 48156, + 48157, 48158, 48159, 48164, 48165, 48167, 48169, 48173, 48176, 48177, 48180, 48184, 48192, 48193, 48195, 48196, + 48197, 48201, 48204, 48205, 48208, 48221, 48260, 48261, 48264, 48267, 48268, 48270, 48276, 48277, 48279, 48281, + 48282, 48288, 48289, 48292, 48295, 48296, 48304, 48305, 48307, 48308, 48309, 48316, 48317, 48320, 48324, 48333, + 48335, 48336, 48337, 48341, 48344, 48348, 48372, 48373, 48374, 48376, 48380, 48388, 48389, 48391, 48393, 48400, + 48404, 48420, 48428, 48448, 48456, 48457, 48460, 48464, 48472, 48473, 48484, 48488, 48512, 48513, 48516, 48519, + 48520, 48521, 48522, 48528, 48529, 48531, 48533, 48537, 48538, 48540, 48548, 48560, 48568, 48596, 48597, 48600, + 48604, 48617, 48624, 48628, 48632, 48640, 48643, 48645, 48652, 48653, 48656, 48660, 48668, 48669, 48671, 48708, + 48709, 48712, 48716, 48718, 48724, 48725, 48727, 48729, 48730, 48731, 48736, 48737, 48740, 48744, 48746, 48752, + 48753, 48755, 48756, 48757, 48763, 48764, 48765, 48768, 48772, 48780, 48781, 48783, 48784, 48785, 48792, 48793, + 48808, 48848, 48849, 48852, 48855, 48856, 48864, 48867, 48868, 48869, 48876, 48897, 48904, 48905, 48920, 48921, + 48923, 48924, 48925, 48960, 48961, 48964, 48968, 48976, 48977, 48981, 49044, 49072, 49093, 49100, 49101, 49104, + 49108, 49116, 49119, 49121, 49212, 49233, 49240, 49244, 49248, 49256, 49257, 49296, 49297, 49300, 49304, 49312, + 49313, 49315, 49317, 49324, 49325, 49327, 49328, 49331, 49332, 49333, 49334, 49340, 49341, 49343, 49344, 49345, + 49349, 49352, 49353, 49356, 49360, 49368, 49369, 49371, 49372, 49373, 49380, 49381, 49384, 49388, 49396, 49397, + 49399, 49401, 49408, 49412, 49416, 49424, 49429, 49436, 49437, 49438, 49439, 49440, 49443, 49444, 49446, 49447, + 49452, 49453, 49455, 49456, 49457, 49462, 49464, 49465, 49468, 49472, 49480, 49481, 49483, 49484, 49485, 49492, + 49493, 49496, 49500, 49508, 49509, 49511, 49512, 49513, 49520, 49524, 49528, 49541, 49548, 49549, 49550, 49552, + 49556, 49558, 49564, 49565, 49567, 49569, 49573, 49576, 49577, 49580, 49584, 49597, 49604, 49608, 49612, 49620, + 49623, 49624, 49632, 49636, 49640, 49648, 49649, 49651, 49660, 49661, 49664, 49668, 49676, 49677, 49679, 49681, + 49688, 49689, 49692, 49695, 49696, 49704, 49705, 49707, 49709, 49711, 49713, 49714, 49716, 49736, 49744, 49745, + 49748, 49752, 49760, 49765, 49772, 49773, 49776, 49780, 49788, 49789, 49791, 49793, 49800, 49801, 49808, 49816, + 49819, 49821, 49828, 49829, 49832, 49836, 49837, 49844, 49845, 49847, 49849, 49884, 49885, 49888, 49891, 49892, + 49899, 49900, 49901, 49903, 49905, 49910, 49912, 49913, 49915, 49916, 49920, 49928, 49929, 49932, 49933, 49939, + 49940, 49941, 49944, 49948, 49956, 49957, 49960, 49961, 49989, 50024, 50025, 50028, 50032, 50034, 50040, 50041, + 50044, 50045, 50052, 50056, 50060, 50112, 50136, 50137, 50140, 50143, 50144, 50146, 50152, 50153, 50157, 50164, + 50165, 50168, 50184, 50192, 50212, 50220, 50224, 50228, 50236, 50237, 50248, 50276, 50277, 50280, 50284, 50292, + 50293, 50297, 50304, 50324, 50332, 50360, 50364, 50409, 50416, 50417, 50420, 50424, 50426, 50431, 50432, 50433, + 50444, 50448, 50452, 50460, 50472, 50473, 50476, 50480, 50488, 50489, 50491, 50493, 50500, 50501, 50504, 50505, + 50506, 50508, 50509, 50510, 50515, 50516, 50517, 50519, 50520, 50521, 50525, 50526, 50528, 50529, 50532, 50536, + 50544, 50545, 50547, 50548, 50549, 50556, 50557, 50560, 50564, 50567, 50572, 50573, 50575, 50577, 50581, 50583, + 50584, 50588, 50592, 50601, 50612, 50613, 50616, 50617, 50619, 50620, 50621, 50622, 50628, 50629, 50630, 50631, + 50632, 50633, 50634, 50636, 50638, 50640, 50641, 50644, 50648, 50656, 50657, 50659, 50661, 50668, 50669, 50670, + 50672, 50676, 50678, 50679, 50684, 50685, 50686, 50687, 50688, 50689, 50693, 50694, 50695, 50696, 50700, 50704, + 50712, 50713, 50715, 50716, 50724, 50725, 50728, 50732, 50733, 50734, 50736, 50739, 50740, 50741, 50743, 50745, + 50747, 50752, 50753, 50756, 50760, 50768, 50769, 50771, 50772, 50773, 50780, 50781, 50784, 50796, 50799, 50801, + 50808, 50809, 50812, 50816, 50824, 50825, 50827, 50829, 50836, 50837, 50840, 50844, 50852, 50853, 50855, 50857, + 50864, 50865, 50868, 50872, 50873, 50874, 50880, 50881, 50883, 50885, 50892, 50893, 50896, 50900, 50908, 50909, + 50912, 50913, 50920, 50921, 50924, 50928, 50936, 50937, 50941, 50948, 50949, 50952, 50956, 50964, 50965, 50967, + 50969, 50976, 50977, 50980, 50984, 50992, 50993, 50995, 50997, 50999, 51004, 51005, 51008, 51012, 51018, 51020, + 51021, 51023, 51025, 51026, 51027, 51028, 51029, 51030, 51031, 51032, 51036, 51040, 51048, 51051, 51060, 51061, + 51064, 51068, 51069, 51070, 51075, 51076, 51077, 51079, 51080, 51081, 51082, 51086, 51088, 51089, 51092, 51094, + 51095, 51096, 51098, 51104, 51105, 51107, 51108, 51109, 51110, 51116, 51117, 51120, 51124, 51132, 51133, 51135, + 51136, 51137, 51144, 51145, 51148, 51150, 51152, 51160, 51165, 51172, 51176, 51180, 51200, 51201, 51204, 51208, + 51210, 51216, 51217, 51219, 51221, 51222, 51228, 51229, 51232, 51236, 51244, 51245, 51247, 51249, 51256, 51260, + 51264, 51272, 51273, 51276, 51277, 51284, 51312, 51313, 51316, 51320, 51322, 51328, 51329, 51331, 51333, 51334, + 51335, 51339, 51340, 51341, 51348, 51357, 51359, 51361, 51368, 51388, 51389, 51396, 51400, 51404, 51412, 51413, + 51415, 51417, 51424, 51425, 51428, 51445, 51452, 51453, 51456, 51460, 51461, 51462, 51468, 51469, 51471, 51473, + 51480, 51500, 51508, 51536, 51537, 51540, 51544, 51552, 51553, 51555, 51564, 51568, 51572, 51580, 51592, 51593, + 51596, 51600, 51608, 51609, 51611, 51613, 51648, 51649, 51652, 51655, 51656, 51658, 51664, 51665, 51667, 51669, + 51670, 51673, 51674, 51676, 51677, 51680, 51682, 51684, 51687, 51692, 51693, 51695, 51696, 51697, 51704, 51705, + 51708, 51712, 51720, 51721, 51723, 51724, 51725, 51732, 51736, 51753, 51788, 51789, 51792, 51796, 51804, 51805, + 51807, 51808, 51809, 51816, 51837, 51844, 51864, 51900, 51901, 51904, 51908, 51916, 51917, 51919, 51921, 51923, + 51928, 51929, 51936, 51948, 51956, 51976, 51984, 51988, 51992, 52000, 52001, 52033, 52040, 52041, 52044, 52048, + 52056, 52057, 52061, 52068, 52088, 52089, 52124, 52152, 52180, 52196, 52199, 52201, 52236, 52237, 52240, 52244, + 52252, 52253, 52257, 52258, 52263, 52264, 52265, 52268, 52270, 52272, 52280, 52281, 52283, 52284, 52285, 52286, + 52292, 52293, 52296, 52300, 52308, 52309, 52311, 52312, 52313, 52320, 52324, 52326, 52328, 52336, 52341, 52376, + 52377, 52380, 52384, 52392, 52393, 52395, 52396, 52397, 52404, 52405, 52408, 52412, 52420, 52421, 52423, 52425, + 52432, 52436, 52452, 52460, 52464, 52481, 52488, 52489, 52492, 52496, 52504, 52505, 52507, 52509, 52516, 52520, + 52524, 52537, 52572, 52576, 52580, 52588, 52589, 52591, 52593, 52600, 52616, 52628, 52629, 52632, 52636, 52644, + 52645, 52647, 52649, 52656, 52676, 52684, 52688, 52712, 52716, 52720, 52728, 52729, 52731, 52733, 52740, 52744, + 52748, 52756, 52761, 52768, 52769, 52772, 52776, 52784, 52785, 52787, 52789, 52824, 52825, 52828, 52831, 52832, + 52833, 52840, 52841, 52843, 52845, 52852, 52853, 52856, 52860, 52868, 52869, 52871, 52873, 52880, 52881, 52884, + 52888, 52896, 52897, 52899, 52900, 52901, 52908, 52909, 52929, 52964, 52965, 52968, 52971, 52972, 52980, 52981, + 52983, 52984, 52985, 52992, 52993, 52996, 53000, 53008, 53009, 53011, 53013, 53020, 53024, 53028, 53036, 53037, + 53039, 53040, 53041, 53048, 53076, 53077, 53080, 53084, 53092, 53093, 53095, 53097, 53104, 53105, 53108, 53112, + 53120, 53125, 53132, 53153, 53160, 53168, 53188, 53216, 53217, 53220, 53224, 53232, 53233, 53235, 53237, 53244, + 53248, 53252, 53265, 53272, 53293, 53300, 53301, 53304, 53308, 53316, 53317, 53319, 53321, 53328, 53332, 53336, + 53344, 53356, 53357, 53360, 53364, 53372, 53373, 53377, 53412, 53413, 53416, 53420, 53428, 53429, 53431, 53433, + 53440, 53441, 53444, 53448, 53449, 53456, 53457, 53459, 53460, 53461, 53468, 53469, 53472, 53476, 53484, 53485, + 53487, 53488, 53489, 53496, 53517, 53552, 53553, 53556, 53560, 53562, 53568, 53569, 53571, 53572, 53573, 53580, + 53581, 53584, 53588, 53596, 53597, 53599, 53601, 53608, 53612, 53628, 53636, 53640, 53664, 53665, 53668, 53672, + 53680, 53681, 53683, 53685, 53690, 53692, 53696, 53720, 53748, 53752, 53767, 53769, 53776, 53804, 53805, 53808, + 53812, 53820, 53821, 53823, 53825, 53832, 53852, 53860, 53888, 53889, 53892, 53896, 53904, 53905, 53909, 53916, + 53920, 53924, 53932, 53937, 53944, 53945, 53948, 53951, 53952, 53954, 53960, 53961, 53963, 53972, 53976, 53980, + 53988, 53989, 54000, 54001, 54004, 54008, 54016, 54017, 54019, 54021, 54028, 54029, 54030, 54032, 54036, 54038, + 54044, 54045, 54047, 54048, 54049, 54053, 54056, 54057, 54060, 54064, 54072, 54073, 54075, 54076, 54077, 54084, + 54085, 54140, 54141, 54144, 54148, 54156, 54157, 54159, 54160, 54161, 54168, 54169, 54172, 54176, 54184, 54185, + 54187, 54189, 54196, 54200, 54204, 54212, 54213, 54216, 54217, 54224, 54232, 54241, 54243, 54252, 54253, 54256, + 54260, 54268, 54269, 54271, 54273, 54280, 54301, 54336, 54340, 54364, 54368, 54372, 54381, 54383, 54392, 54393, + 54396, 54399, 54400, 54402, 54408, 54409, 54411, 54413, 54420, 54441, 54476, 54480, 54484, 54492, 54495, 54504, + 54508, 54512, 54520, 54523, 54525, 54532, 54536, 54540, 54548, 54549, 54551, 54588, 54589, 54592, 54596, 54604, + 54605, 54607, 54609, 54616, 54617, 54620, 54624, 54629, 54632, 54633, 54635, 54637, 54644, 54645, 54648, 54652, + 54660, 54661, 54663, 54664, 54665, 54672, 54693, 54728, 54729, 54732, 54736, 54738, 54744, 54745, 54747, 54749, + 54756, 54757, 54760, 54764, 54772, 54773, 54775, 54777, 54784, 54785, 54788, 54792, 54800, 54801, 54803, 54804, + 54805, 54812, 54816, 54820, 54829, 54840, 54841, 54844, 54848, 54853, 54856, 54857, 54859, 54861, 54865, 54868, + 54869, 54872, 54876, 54887, 54889, 54896, 54897, 54900, 54915, 54917, 54924, 54925, 54928, 54932, 54941, 54943, + 54945, 54952, 54956, 54960, 54969, 54971, 54980, 54981, 54984, 54988, 54993, 54996, 54999, 55001, 55008, 55012, + 55016, 55024, 55029, 55036, 55037, 55040, 55044, 55057, 55064, 55065, 55068, 55072, 55080, 55081, 55083, 55085, + 55092, 55093, 55096, 55100, 55108, 55111, 55113, 55120, 55121, 55124, 55126, 55127, 55128, 55129, 55136, 55137, + 55139, 55141, 55145, 55148, 55152, 55156, 55164, 55165, 55169, 55176, 55177, 55180, 55184, 55192, 55193, 55195, + 55197, 04352, 04353, 04354, 04355, 04356, 04357, 04358, 04359, 04360, 04361, 04362, 04363, 04364, 04365, 04366, + 04367, 04368, 04369, 04370, 04449, 04450, 04451, 04452, 04453, 04454, 04455, 04456, 04457, 04461, 04462, 04466, + 04467, 04469, 47252, 49968, 50108, 50388, 52012, 65535, + }; + + private static readonly Dictionary G4ValueId = G4Values + .Select((value, index) => new KeyValuePair(value, index)) + .ToDictionary(pair => pair.Key, pair => pair.Value); + + private static readonly Dictionary G4CharId = G4Chars + .Select((value, index) => new KeyValuePair(value, index)) + .ToDictionary(pair => pair.Key, pair => pair.Value); + + #endregion +} diff --git a/PKHeX.Core/PKM/Strings/StringConverter5.cs b/PKHeX.Core/PKM/Strings/StringConverter5.cs new file mode 100644 index 000000000..87684c6d2 --- /dev/null +++ b/PKHeX.Core/PKM/Strings/StringConverter5.cs @@ -0,0 +1,63 @@ +using System; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core; + +public static class StringConverter5 +{ + private const char TerminatorFFFF = (char)0xFFFF; + + /// Converts Generation 5 encoded data to decoded string. + /// Encoded data + /// Decoded string. + public static string GetString(ReadOnlySpan data) + { + Span result = stackalloc char[data.Length]; + int length = LoadString(data, result); + return new string(result[..length].ToArray()); + } + + private static int LoadString(ReadOnlySpan data, Span result) + { + int i = 0; + for (; i < data.Length; i += 2) + { + var value = ReadUInt16LittleEndian(data[i..]); + if (value == TerminatorFFFF) + break; + result[i/2] = StringConverter.SanitizeChar((char)value); + } + return i/2; + } + + /// Gets the bytes for a Generation 5 string. + /// + /// Decoded string. + /// Maximum length of the input + /// + /// Encoded data. + public static int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, + StringConverterOption option = StringConverterOption.ClearZero) + { + if (value.Length > maxLength) + value = value[..maxLength]; // Hard cap + + if (option is StringConverterOption.ClearZero) + destBuffer.Clear(); + else if (option is StringConverterOption.ClearFF) + destBuffer.Fill(0xFF); + + for (int i = 0; i < value.Length; i++) + { + char c = value[i]; + ushort val = StringConverter.UnSanitizeChar5(c); + WriteUInt16LittleEndian(destBuffer[(i * 2)..], val); + } + + int count = value.Length * 2; + if (count == destBuffer.Length) + return count; + WriteUInt16LittleEndian(destBuffer[count..], TerminatorFFFF); + return count + 2; + } +} diff --git a/PKHeX.Core/PKM/Strings/StringConverter6.cs b/PKHeX.Core/PKM/Strings/StringConverter6.cs new file mode 100644 index 000000000..e7546b326 --- /dev/null +++ b/PKHeX.Core/PKM/Strings/StringConverter6.cs @@ -0,0 +1,63 @@ +using System; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core; + +public static class StringConverter6 +{ + private const ushort TerminatorNull = 0; + + /// Converts Generation 6 encoded data to decoded string. + /// Encoded data + /// Decoded string. + public static string GetString(ReadOnlySpan data) + { + Span result = stackalloc char[data.Length]; + int length = LoadString(data, result); + return new string(result[..length].ToArray()); + } + + private static int LoadString(ReadOnlySpan data, Span result) + { + int i = 0; + for (; i < data.Length; i += 2) + { + var value = ReadUInt16LittleEndian(data[i..]); + if (value == TerminatorNull) + break; + result[i/2] = StringConverter.SanitizeChar((char)value); + } + return i/2; + } + + /// Gets the bytes for a Generation 6 string. + /// + /// Decoded string. + /// Maximum length of the input + /// + /// Encoded data. + public static int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, + StringConverterOption option = StringConverterOption.ClearZero) + { + if (value.Length > maxLength) + value = value[..maxLength]; // Hard cap + + if (option is StringConverterOption.ClearZero) + destBuffer.Clear(); + + bool isFullWidth = StringConverter.GetIsFullWidthString(value); + for (int i = 0; i < value.Length; i++) + { + char c = value[i]; + if (!isFullWidth) + c = StringConverter.UnSanitizeChar(c); + WriteUInt16LittleEndian(destBuffer[(i * 2)..], c); + } + + int count = value.Length * 2; + if (count == destBuffer.Length) + return count; + WriteUInt16LittleEndian(destBuffer[count..], TerminatorNull); + return count + 2; + } +} diff --git a/PKHeX.Core/PKM/Strings/StringConverter7.cs b/PKHeX.Core/PKM/Strings/StringConverter7.cs new file mode 100644 index 000000000..52c5acf8c --- /dev/null +++ b/PKHeX.Core/PKM/Strings/StringConverter7.cs @@ -0,0 +1,71 @@ +using System; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core; + +public static class StringConverter7 +{ + private const ushort TerminatorNull = 0; + + /// Converts Generation 7 encoded data to decoded string. + /// Encoded data + /// Decoded string. + public static string GetString(ReadOnlySpan data) + { + Span result = stackalloc char[data.Length]; + int length = LoadString(data, result); + return new string(result[..length].ToArray()); + } + + private static int LoadString(ReadOnlySpan data, Span result) + { + int i = 0; + for (; i < data.Length; i += 2) + { + var value = ReadUInt16LittleEndian(data[i..]); + if (value == TerminatorNull) + break; + result[i/2] = StringConverter.SanitizeChar((char)value); + } + + var span = result[..(i/2)]; + StringConverter7ZH.RemapChineseGlyphsBin2String(span); + return i/2; + } + + /// Gets the bytes for a Generation 7 string. + /// + /// Decoded string. + /// Maximum length of the input + /// Language specific conversion (Chinese) + /// + /// Chinese string remapping should be attempted + /// Encoded data. + public static int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, int language, + StringConverterOption option = StringConverterOption.ClearZero, bool chinese = false) + { + if (value.Length > maxLength) + value = value[..maxLength]; // Hard cap + + if (option is StringConverterOption.ClearZero) + destBuffer.Clear(); + + bool trad = StringConverter7ZH.IsTraditional(value, language); + bool isFullWidth = StringConverter.GetIsFullWidthString(value); + for (int i = 0; i < value.Length; i++) + { + char c = value[i]; + if (!isFullWidth) + c = StringConverter.UnSanitizeChar(c); + if (chinese) + c = StringConverter7ZH.ConvertString2BinG7_zh(c, trad); + WriteUInt16LittleEndian(destBuffer[(i * 2)..], c); + } + + int count = value.Length * 2; + if (count == destBuffer.Length) + return count; + WriteUInt16LittleEndian(destBuffer[count..], TerminatorNull); + return count + 2; + } +} diff --git a/PKHeX.Core/PKM/Strings/StringConverter7ZH.cs b/PKHeX.Core/PKM/Strings/StringConverter7ZH.cs index e1684f680..f6a3bcf6a 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter7ZH.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter7ZH.cs @@ -1,103 +1,96 @@ -using System.Collections.Generic; -using System.Text; +using System; +using System.Collections.Generic; -namespace PKHeX.Core +namespace PKHeX.Core; + +/// +/// Logic for remapping reserved Chinese characters to and from Unicode, used by Generation 7 3DS games. +/// +public static class StringConverter7ZH { - /// - /// Logic for remapping reserved Chinese characters to and from Unicode, used by Generation 7 3DS games. - /// - public static class StringConverter7ZH + // To accommodate for Chinese characters in Generation 7 3DS games, GameFreak has allocated a list of used ones in the unused 0xE800 region. + // Why? Dunno, cuz Unicode has these already. + // Maybe they wanted a quick way to sanity check Chinese strings, only allowing within the 0xE800 range. + #region Gen 7 Chinese Character Tables + public const string Gen7_ZHRaw = "蛋妙蛙种子草花小火龙恐喷杰尼龟卡咪水箭绿毛虫铁甲蛹巴大蝶独角壳针蜂波比鸟拉达烈雀嘴阿柏蛇怪皮丘雷穿山鼠王多兰娜后朗力诺可西六尾九胖丁超音蝠走路臭霸派斯特球摩鲁蛾地三喵猫老鸭哥猴暴蒂狗风速蚊香蝌蚪君泳士凯勇基胡腕豪喇叭芽口呆食玛瑙母毒刺拳石隆岩马焰兽磁合一葱嘟利海狮白泥舌贝鬼通耿催眠貘引梦人钳蟹巨霹雳电顽弹椰树嘎啦飞腿郎快头瓦双犀牛钻吉蔓藤袋墨金鱼星宝魔墙偶天螳螂迷唇姐击罗肯泰鲤普百变伊布边菊化盔镰刀翼急冻闪你哈克幻叶月桂竺葵锯鳄蓝立咕夜鹰芭瓢安圆丝蛛叉字灯笼古然咩羊茸美丽露才皇毽棉长手向日蜻蜓乌沼太阳亮黑暗鸦妖未知图腾果翁麒麟奇榛佛托土弟蝎钢千壶赫狃熊圈熔蜗猪珊瑚炮章桶信使翅戴加象顿Ⅱ惊鹿犬无畏战舞娃奶罐幸福公炎帝幼沙班洛亚凤时木守宫森林蜥蜴稚鸡壮跃狼纹直冲茧狩猎盾粉莲童帽乐河橡实鼻狡猾傲骨燕鸥莉奈朵溜糖雨蘑菇斗笠懒獭过动猿请假居忍面者脱妞吼爆幕下掌朝北优雅勾魂眼那恰姆落正拍负萤甜蔷薇溶吞牙鲨鲸驼煤炭跳噗晃斑颚蚁漠仙歌青绵七夕鼬斩饭匙鳅鲶虾兵螯秤念触摇篮羽丑纳飘浮泡隐怨影诅咒巡灵彷徨热带铃勃梭雪冰护豹珍珠樱空棘爱心哑属艾欧盖固坐祈代希苗台猛曼拿儿狸法师箱蟀勒伦琴含羞苞槌城结贵妇绅蜜女帕兹潜兔随卷耳魅东施铛响坦铜镜钟盆聒噪陆尖咬不良骷荧光霓虹自舔狂远Z由卢席恩骑色霏莱谢米尔宙提主暖炒武刃丸剑探步哨约扒酷冷蚀豆鸽高雉幔庞滚蝙螺钉差搬运匠修建蟾蜍投摔打包保足蜈蚣车轮精根裙野蛮鲈混流氓红倒狒殿滑巾征哭具死神棺原肋始祖破灰尘索沫栗德单卵细胞造鹅倍四季萌哎呀败轻蜘坚齿组麻鳗宇烛幽晶斧嚏几何敏捷功夫父赤驹劈司令炸雄秃丫首恶燃烧毕云酋迪耶塔赛里狐呱贺掘彩蓓洁能鞘芳芙妮好鱿贼脚铠垃藻臂枪伞咚碎黏钥朽南瓜嗡哲裴格枭狙射炽咆哮虎漾壬笃啄铳少强锹农胜虻鬃弱坏驴仔重挽滴伪睡罩盗着竹疗环智挥猩掷胆噬堡爷参性:银伴陨枕戈谜拟Q磨舵鳞杖璞・鸣哞鳍科莫迦虚吾肌费束辉纸御机夏蛋妙蛙種子草花小火龍恐噴傑尼龜卡咪水箭綠毛蟲鐵甲蛹巴大蝶獨角殼針蜂波比鳥拉達烈雀嘴阿柏蛇怪皮丘雷穿山鼠王多蘭娜后朗力諾可西六尾九胖丁超音蝠走路臭霸派斯特球摩魯蛾地三喵貓老鴨哥猴爆蒂狗風速蚊香蝌蚪君泳士凱勇基胡腕豪喇叭芽口呆食瑪瑙母毒刺拳石隆岩馬焰獸磁合一蔥嘟利海獅白泥舌貝鬼通耿催眠貘引夢人鉗蟹巨霹靂電頑彈椰樹嘎啦飛腿郎快頭瓦雙犀牛鑽吉蔓藤袋墨金魚星寶魔牆偶天螳螂迷唇姐擊羅肯泰鯉暴普百變伊布邊菊化盔鐮刀翼急凍閃你哈克幻葉月桂竺葵鋸鱷藍立咕夜鷹芭瓢安圓絲蛛叉字燈籠古然咩羊茸美麗露才皇毽棉長手向日蜻蜓烏沼太陽亮黑暗鴉妖未知圖騰果翁麒麟奇榛佛托土弟蠍鋼千壺赫狃熊圈熔蝸豬珊瑚炮章桶信使翅戴加象頓Ⅱ驚鹿犬無畏戰舞娃奶罐幸福公炎帝幼沙班洛亞鳳時木守宮森林蜥蜴稚雞壯躍狼紋直衝繭狩獵盾粉蓮童帽樂河橡實鼻狡猾傲骨燕鷗莉奈朵溜糖雨蘑菇斗笠懶獺過動猿請假居忍面者脫妞吼幕下掌朝北優雅勾魂眼那恰姆落正拍負螢甜薔薇溶吞牙鯊鯨駝煤炭跳噗晃斑顎蟻漠仙歌青綿七夕鼬斬飯匙鰍鯰蝦兵螯秤念觸搖籃羽醜納飄浮泡隱怨影詛咒巡靈彷徨熱帶鈴勃梭雪冰護豹珍珠櫻空棘愛心啞屬艾歐蓋固坐祈代希苗台猛曼拿兒狸法師箱蟀勒倫琴含羞苞槌城結貴婦紳蜜女帕茲潛兔隨捲耳魅東施鐺響坦銅鏡鐘盆聒噪陸尖咬不良骷光霓虹自舔狂遠Z由盧席恩騎色霏萊謝米爾宙提主暖炒武刃丸劍探步哨約扒酷冷蝕豆鴿高雉幔龐滾蝙螺釘差搬運匠修建蟾蜍投摔打包保足蜈蚣車輪毬精根裙野蠻鱸混流氓紅倒狒殿滑巾徵哭具死神棺原肋始祖破灰塵索沫栗德單卵細胞造鵝倍四季萌哎呀敗輕蜘堅齒組麻鰻宇燭幽晶斧嚏幾何敏捷功夫父赤駒劈司令炸雄禿丫首惡燃燒畢雲酋迪耶塔賽里狐呱賀掘彩蓓潔能鞘芳芙妮好魷賊腳鎧垃藻臂槍傘咚碎黏鑰朽南瓜嗡哲裴格梟狙射熾咆哮虎漾壬篤啄銃少強鍬農勝虻鬃弱壞驢仔重挽滴偽睡罩盜著竹療環智揮猩擲膽噬堡爺參性:銀伴隕枕戈謎擬Q磨舵鱗杖璞・鳴哞鰭科莫迦虛吾肌費束輝紙御機夏垒磊砰奥壘磊砰丑奧"; + public const int Gen7_ZHLength = 0x627; + + private const char Gen7_ZH_Ofs = '\uE800'; + private const ushort SM_ZHCharTable_Size = 0x30F; + private const ushort USUM_CHS_Size = 0x4; + private static bool GetisG7CHSChar(int idx) => idx is < SM_ZHCharTable_Size or >= SM_ZHCharTable_Size * 2 and < (SM_ZHCharTable_Size * 2) + USUM_CHS_Size; + + // Unicode -> u16 conversion + private static readonly Dictionary G7_CHS = GetRemapper(true); + private static readonly Dictionary G7_CHT = GetRemapper(false); + + private static Dictionary GetRemapper(bool simplified) { - // To accommodate for Chinese characters in Generation 7 3DS games, GameFreak has allocated a list of used ones in the unused 0xE800 region. - // Why? Dunno, cuz Unicode has these already. - // Maybe they wanted a quick way to sanity check Chinese strings, only allowing within the 0xE800 range. - #region Gen 7 Chinese Character Tables - public const string Gen7_ZHRaw = "蛋妙蛙种子草花小火龙恐喷杰尼龟卡咪水箭绿毛虫铁甲蛹巴大蝶独角壳针蜂波比鸟拉达烈雀嘴阿柏蛇怪皮丘雷穿山鼠王多兰娜后朗力诺可西六尾九胖丁超音蝠走路臭霸派斯特球摩鲁蛾地三喵猫老鸭哥猴暴蒂狗风速蚊香蝌蚪君泳士凯勇基胡腕豪喇叭芽口呆食玛瑙母毒刺拳石隆岩马焰兽磁合一葱嘟利海狮白泥舌贝鬼通耿催眠貘引梦人钳蟹巨霹雳电顽弹椰树嘎啦飞腿郎快头瓦双犀牛钻吉蔓藤袋墨金鱼星宝魔墙偶天螳螂迷唇姐击罗肯泰鲤普百变伊布边菊化盔镰刀翼急冻闪你哈克幻叶月桂竺葵锯鳄蓝立咕夜鹰芭瓢安圆丝蛛叉字灯笼古然咩羊茸美丽露才皇毽棉长手向日蜻蜓乌沼太阳亮黑暗鸦妖未知图腾果翁麒麟奇榛佛托土弟蝎钢千壶赫狃熊圈熔蜗猪珊瑚炮章桶信使翅戴加象顿Ⅱ惊鹿犬无畏战舞娃奶罐幸福公炎帝幼沙班洛亚凤时木守宫森林蜥蜴稚鸡壮跃狼纹直冲茧狩猎盾粉莲童帽乐河橡实鼻狡猾傲骨燕鸥莉奈朵溜糖雨蘑菇斗笠懒獭过动猿请假居忍面者脱妞吼爆幕下掌朝北优雅勾魂眼那恰姆落正拍负萤甜蔷薇溶吞牙鲨鲸驼煤炭跳噗晃斑颚蚁漠仙歌青绵七夕鼬斩饭匙鳅鲶虾兵螯秤念触摇篮羽丑纳飘浮泡隐怨影诅咒巡灵彷徨热带铃勃梭雪冰护豹珍珠樱空棘爱心哑属艾欧盖固坐祈代希苗台猛曼拿儿狸法师箱蟀勒伦琴含羞苞槌城结贵妇绅蜜女帕兹潜兔随卷耳魅东施铛响坦铜镜钟盆聒噪陆尖咬不良骷荧光霓虹自舔狂远Z由卢席恩骑色霏莱谢米尔宙提主暖炒武刃丸剑探步哨约扒酷冷蚀豆鸽高雉幔庞滚蝙螺钉差搬运匠修建蟾蜍投摔打包保足蜈蚣车轮精根裙野蛮鲈混流氓红倒狒殿滑巾征哭具死神棺原肋始祖破灰尘索沫栗德单卵细胞造鹅倍四季萌哎呀败轻蜘坚齿组麻鳗宇烛幽晶斧嚏几何敏捷功夫父赤驹劈司令炸雄秃丫首恶燃烧毕云酋迪耶塔赛里狐呱贺掘彩蓓洁能鞘芳芙妮好鱿贼脚铠垃藻臂枪伞咚碎黏钥朽南瓜嗡哲裴格枭狙射炽咆哮虎漾壬笃啄铳少强锹农胜虻鬃弱坏驴仔重挽滴伪睡罩盗着竹疗环智挥猩掷胆噬堡爷参性:银伴陨枕戈谜拟Q磨舵鳞杖璞・鸣哞鳍科莫迦虚吾肌费束辉纸御机夏蛋妙蛙種子草花小火龍恐噴傑尼龜卡咪水箭綠毛蟲鐵甲蛹巴大蝶獨角殼針蜂波比鳥拉達烈雀嘴阿柏蛇怪皮丘雷穿山鼠王多蘭娜后朗力諾可西六尾九胖丁超音蝠走路臭霸派斯特球摩魯蛾地三喵貓老鴨哥猴爆蒂狗風速蚊香蝌蚪君泳士凱勇基胡腕豪喇叭芽口呆食瑪瑙母毒刺拳石隆岩馬焰獸磁合一蔥嘟利海獅白泥舌貝鬼通耿催眠貘引夢人鉗蟹巨霹靂電頑彈椰樹嘎啦飛腿郎快頭瓦雙犀牛鑽吉蔓藤袋墨金魚星寶魔牆偶天螳螂迷唇姐擊羅肯泰鯉暴普百變伊布邊菊化盔鐮刀翼急凍閃你哈克幻葉月桂竺葵鋸鱷藍立咕夜鷹芭瓢安圓絲蛛叉字燈籠古然咩羊茸美麗露才皇毽棉長手向日蜻蜓烏沼太陽亮黑暗鴉妖未知圖騰果翁麒麟奇榛佛托土弟蠍鋼千壺赫狃熊圈熔蝸豬珊瑚炮章桶信使翅戴加象頓Ⅱ驚鹿犬無畏戰舞娃奶罐幸福公炎帝幼沙班洛亞鳳時木守宮森林蜥蜴稚雞壯躍狼紋直衝繭狩獵盾粉蓮童帽樂河橡實鼻狡猾傲骨燕鷗莉奈朵溜糖雨蘑菇斗笠懶獺過動猿請假居忍面者脫妞吼幕下掌朝北優雅勾魂眼那恰姆落正拍負螢甜薔薇溶吞牙鯊鯨駝煤炭跳噗晃斑顎蟻漠仙歌青綿七夕鼬斬飯匙鰍鯰蝦兵螯秤念觸搖籃羽醜納飄浮泡隱怨影詛咒巡靈彷徨熱帶鈴勃梭雪冰護豹珍珠櫻空棘愛心啞屬艾歐蓋固坐祈代希苗台猛曼拿兒狸法師箱蟀勒倫琴含羞苞槌城結貴婦紳蜜女帕茲潛兔隨捲耳魅東施鐺響坦銅鏡鐘盆聒噪陸尖咬不良骷光霓虹自舔狂遠Z由盧席恩騎色霏萊謝米爾宙提主暖炒武刃丸劍探步哨約扒酷冷蝕豆鴿高雉幔龐滾蝙螺釘差搬運匠修建蟾蜍投摔打包保足蜈蚣車輪毬精根裙野蠻鱸混流氓紅倒狒殿滑巾徵哭具死神棺原肋始祖破灰塵索沫栗德單卵細胞造鵝倍四季萌哎呀敗輕蜘堅齒組麻鰻宇燭幽晶斧嚏幾何敏捷功夫父赤駒劈司令炸雄禿丫首惡燃燒畢雲酋迪耶塔賽里狐呱賀掘彩蓓潔能鞘芳芙妮好魷賊腳鎧垃藻臂槍傘咚碎黏鑰朽南瓜嗡哲裴格梟狙射熾咆哮虎漾壬篤啄銃少強鍬農勝虻鬃弱壞驢仔重挽滴偽睡罩盜著竹療環智揮猩擲膽噬堡爺參性:銀伴隕枕戈謎擬Q磨舵鱗杖璞・鳴哞鰭科莫迦虛吾肌費束輝紙御機夏垒磊砰奥壘磊砰丑奧"; - public const int Gen7_ZHLength = 0x627; - - private const char Gen7_ZH_Ofs = '\uE800'; - private const ushort SM_ZHCharTable_Size = 0x30F; - private const ushort USUM_CHS_Size = 0x4; - private static bool GetisG7CHSChar(int idx) => idx is < SM_ZHCharTable_Size or >= SM_ZHCharTable_Size * 2 and < (SM_ZHCharTable_Size * 2) + USUM_CHS_Size; - - // Unicode -> u16 conversion - private static readonly Dictionary G7_CHS = GetRemapper(true); - private static readonly Dictionary G7_CHT = GetRemapper(false); - - private static Dictionary GetRemapper(bool simplified) + const string raw = Gen7_ZHRaw; + var result = new Dictionary(788); // raw.Length + for (int i = 0; i < raw.Length; i++) { - const string raw = Gen7_ZHRaw; - var result = new Dictionary(788); // raw.Length - for (int i = 0; i < raw.Length; i++) - { - var isCHS = GetisG7CHSChar(i); - if (isCHS != simplified) - continue; + var isCHS = GetisG7CHSChar(i); + if (isCHS != simplified) + continue; - var c = raw[i]; - result.Add(c, (char)(Gen7_ZH_Ofs + i)); - } - return result; + var c = raw[i]; + result.Add(c, (char)(Gen7_ZH_Ofs + i)); } - #endregion + return result; + } + #endregion - /// - /// Converts a Unicode string to Generation 7 in-game Chinese string. - /// - /// Unicode string. - /// Detection of language for Traditional Chinese check - /// In-game Chinese string. - internal static void ConvertString2BinG7_zh(StringBuilder sb, int language) + /// + /// Converts a Unicode string to Generation 7 in-game Chinese string. + /// + /// Unicode character. + /// Detection of language for Traditional Chinese check + /// In-game Chinese string. + internal static char ConvertString2BinG7_zh(char chr, bool traditional) + { + var table = traditional ? G7_CHT : G7_CHS; + + if (table.TryGetValue(chr, out var remap)) + return remap; + return chr; + } + + public static bool IsTraditional(ReadOnlySpan input, int lang) + { + // A string cannot contain a mix of CHS and CHT characters. + foreach (var chr in input) { - bool traditional = IsTraditional(language, sb); - var table = traditional ? G7_CHT : G7_CHS; - - for (int i = 0; i < sb.Length; i++) - { - var chr = sb[i]; - if (table.TryGetValue(chr, out var remap)) - sb[i] = remap; - } + if (G7_CHT.ContainsKey(chr) && !G7_CHS.ContainsKey(chr)) + return true; } - private static bool IsTraditional(int lang, StringBuilder input) - { - // A string cannot contain a mix of CHS and CHT characters. - for (var i = 0; i < input.Length; i++) - { - var chr = input[i]; - if (G7_CHT.ContainsKey(chr) && !G7_CHS.ContainsKey(chr)) - return true; - } - - if (lang != (int)LanguageID.ChineseT) - return false; - - // CHS and CHT have the same display name - for (var i = 0; i < input.Length; i++) - { - var chr = input[i]; - if (G7_CHT.ContainsKey(chr) != G7_CHS.ContainsKey(chr)) - return true; - } - + if (lang != (int)LanguageID.ChineseT) return false; + + // CHS and CHT have the same display name + foreach (var chr in input) + { + if (G7_CHT.ContainsKey(chr) != G7_CHS.ContainsKey(chr)) + return true; } - /// - /// Converts a Generation 7 in-game Chinese string to Unicode string. - /// - /// In-game Chinese string. - /// Unicode string. - internal static void RemapChineseGlyphsBin2String(StringBuilder input) + return false; + } + + /// + /// Converts a Generation 7 in-game Chinese string to Unicode string. + /// + /// In-game Chinese string. + /// Unicode string. + internal static void RemapChineseGlyphsBin2String(Span input) + { + for (int i = 0; i < input.Length; i++) { - for (int i = 0; i < input.Length; i++) - { - char val = input[i]; - var unmap = (uint)val - Gen7_ZH_Ofs; - if (unmap < Gen7_ZHLength) - input[i] = Gen7_ZHRaw[(int)unmap]; - } + char val = input[i]; + var unmap = (uint)val - Gen7_ZH_Ofs; + if (unmap < Gen7_ZHLength) + input[i] = Gen7_ZHRaw[(int)unmap]; } } } diff --git a/PKHeX.Core/PKM/Strings/StringConverter8.cs b/PKHeX.Core/PKM/Strings/StringConverter8.cs new file mode 100644 index 000000000..ec628b229 --- /dev/null +++ b/PKHeX.Core/PKM/Strings/StringConverter8.cs @@ -0,0 +1,63 @@ +using System; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core; + +public static class StringConverter8 +{ + private const ushort TerminatorNull = 0; + + /// Converts Generation 7-Beluga encoded data to decoded string. + /// Encoded data + /// Decoded string. + public static string GetString(ReadOnlySpan data) + { + Span result = stackalloc char[data.Length]; + int length = LoadString(data, result); + return new string(result[..length].ToArray()); + } + + private static int LoadString(ReadOnlySpan data, Span result) + { + int i = 0; + for (; i < data.Length; i += 2) + { + var value = ReadUInt16LittleEndian(data[i..]); + if (value == TerminatorNull) + break; + result[i/2] = StringConverter.SanitizeChar((char)value); + } + return i/2; + } + + /// Gets the bytes for a Generation 7-Beluga string. + /// + /// Decoded string. + /// Maximum length of the input + /// + /// Encoded data. + public static int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, + StringConverterOption option = StringConverterOption.ClearZero) + { + if (value.Length > maxLength) + value = value[..maxLength]; // Hard cap + + if (option is StringConverterOption.ClearZero) + destBuffer.Clear(); + + bool isFullWidth = StringConverter.GetIsFullWidthString(value); + for (int i = 0; i < value.Length; i++) + { + char c = value[i]; + if (!isFullWidth) + c = StringConverter.UnSanitizeChar(c); + WriteUInt16LittleEndian(destBuffer[(i * 2)..], c); + } + + int count = value.Length * 2; + if (count == destBuffer.Length) + return count; + WriteUInt16LittleEndian(destBuffer[count..], TerminatorNull); + return count + 2; + } +} diff --git a/PKHeX.Core/PKM/Strings/StringConverterOption.cs b/PKHeX.Core/PKM/Strings/StringConverterOption.cs new file mode 100644 index 000000000..d91f61e0d --- /dev/null +++ b/PKHeX.Core/PKM/Strings/StringConverterOption.cs @@ -0,0 +1,10 @@ +namespace PKHeX.Core; + +public enum StringConverterOption +{ + None, + ClearZero, + Clear50, + Clear7F, + ClearFF, +} diff --git a/PKHeX.Core/PKM/Util/EntityFileNamer.cs b/PKHeX.Core/PKM/Util/EntityFileNamer.cs index 151b8a5a2..4e2e8a223 100644 --- a/PKHeX.Core/PKM/Util/EntityFileNamer.cs +++ b/PKHeX.Core/PKM/Util/EntityFileNamer.cs @@ -28,7 +28,8 @@ private static string GetRegular(PKM pk) { string form = pk.Form > 0 ? $"-{pk.Form:00}" : string.Empty; string star = pk.IsShiny ? " ★" : string.Empty; - return $"{pk.Species:000}{form}{star} - {pk.Nickname} - {pk.Checksum:X4}{pk.EncryptionConstant:X8}"; + var chk = pk is ISanityChecksum s ? s.Checksum : PokeCrypto.GetCHK(pk.Data, pk.SIZE_STORED); + return $"{pk.Species:000}{form}{star} - {pk.Nickname} - {chk:X4}{pk.EncryptionConstant:X8}"; } private static string GetGBPKM(GBPKM gb) diff --git a/PKHeX.Core/PKM/Util/PKMConverter.cs b/PKHeX.Core/PKM/Util/PKMConverter.cs index b4f44cc20..f1f289634 100644 --- a/PKHeX.Core/PKM/Util/PKMConverter.cs +++ b/PKHeX.Core/PKM/Util/PKMConverter.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; +using static System.Buffers.Binary.BinaryPrimitives; using static PKHeX.Core.MessageStrings; namespace PKHeX.Core @@ -48,7 +49,7 @@ public static void SetFirstCountryRegion(IGeoTrack pkm) ///
/// Raw data representing a Pokemon. /// An integer indicating the generation of the PKM file, or -1 if the data is invalid. - public static int GetPKMDataFormat(byte[] data) + public static int GetPKMDataFormat(ReadOnlySpan data) { if (!PKX.IsPKM(data.Length)) return -1; @@ -66,17 +67,17 @@ public static int GetPKMDataFormat(byte[] data) return 3; case PokeCrypto.SIZE_4PARTY or PokeCrypto.SIZE_4STORED: case PokeCrypto.SIZE_5PARTY: - if ((BitConverter.ToUInt16(data, 0x4) == 0) && (BitConverter.ToUInt16(data, 0x80) >= 0x3333 || data[0x5F] >= 0x10) && BitConverter.ToUInt16(data, 0x46) == 0) // PK5 + if ((ReadUInt16LittleEndian(data[0x4..]) == 0) && (ReadUInt16LittleEndian(data.Slice(0x80)) >= 0x3333 || data[0x5F] >= 0x10) && ReadUInt16LittleEndian(data[0x46..]) == 0) // PK5 return 5; return 4; case PokeCrypto.SIZE_6STORED: return 6; case PokeCrypto.SIZE_6PARTY: // collision with PGT, same size. - if (BitConverter.ToUInt16(data, 0x4) != 0) // Bad Sanity? + if (ReadUInt16LittleEndian(data[0x4..]) != 0) // Bad Sanity? return -1; - if (BitConverter.ToUInt32(data, 0x06) == PokeCrypto.GetCHK(data, PokeCrypto.SIZE_6STORED)) + if (ReadUInt32LittleEndian(data[0x06..]) == PokeCrypto.GetCHK(data, PokeCrypto.SIZE_6STORED)) return 6; - if (BitConverter.ToUInt16(data, 0x58) != 0) // Encrypted? + if (ReadUInt16LittleEndian(data[0x58..]) != 0) // Encrypted? { for (int i = data.Length - 0x10; i < data.Length; i++) // 0x10 of 00's at the end != PK6 { @@ -114,7 +115,7 @@ public static int GetPKMDataFormat(byte[] data) PokeCrypto.SIZE_3XSTORED => new XK3(data), _ => new PK3(data), }, - 4 => BitConverter.ToUInt16(data, 0x04) == 0 ? new PK4(data) : new BK4(data), + 4 => ReadUInt16LittleEndian(data.AsSpan(0x04)) == 0 ? new PK4(data) : new BK4(data), 5 => new PK5(data), 6 => CheckPKMFormat7(new PK6(data), prefer), 8 => CheckPKMFormat8(data), @@ -181,7 +182,7 @@ private static bool IsPK6FormatReallyPK7(PK6 pk, int preferredFormat) return true; } - int mb = BitConverter.ToUInt16(pk.Data, 0x16); + int mb = ReadUInt16LittleEndian(pk.Data.AsSpan(0x16)); if (mb > 0xAAA) return false; for (int i = 0; i < 6; i++) diff --git a/PKHeX.Core/PKM/Util/PKX.cs b/PKHeX.Core/PKM/Util/PKX.cs index 7b2c6ccfe..6b046398e 100644 --- a/PKHeX.Core/PKM/Util/PKX.cs +++ b/PKHeX.Core/PKM/Util/PKX.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -48,7 +49,7 @@ public static int[] GetRandomEVs(int generation = Generation) max -= evs[i] = (byte)Math.Min(rnd.Next(Math.Min(300, max)), 252); evs[5] = max; } while (evs[5] > 252); - Util.Shuffle(evs); + Util.Shuffle(evs.AsSpan()); return evs; } else @@ -69,14 +70,16 @@ public static int GetGenderFromString(string s) { if (s.Length != 1) return 2; - return s[0] switch - { - '♂' or 'M' => 0, - '♀' or 'F' => 1, - _ => 2, - }; + return GetGenderFromChar(s[0]); } + private static int GetGenderFromChar(char c) => c switch + { + '♂' or 'M' => 0, + '♀' or 'F' => 1, + _ => 2, + }; + /// /// Gets the nature modification values and checks if they are equal. /// @@ -173,8 +176,8 @@ public static uint GetRandomPID(Random rnd, int species, int gender, int origin, /// public static int GetUnownForm(uint pid) { - var val = (pid & 0x3000000) >> 18 | (pid & 0x30000) >> 12 | (pid & 0x300) >> 6 | (pid & 0x3); - return (int)(val % 28); + var value = (pid & 0x3000000) >> 18 | (pid & 0x30000) >> 12 | (pid & 0x300) >> 6 | (pid & 0x3); + return (int)(value % 28); } /// @@ -254,15 +257,15 @@ public static int GetPKMFormatFromExtension(char last, int prefer) return last == 'x' ? 6 : prefer; } - internal static bool IsPKMPresentGB(byte[] data, int offset) => data[offset] != 0; - internal static bool IsPKMPresentGC(byte[] data, int offset) => BitConverter.ToUInt16(data, offset) != 0; - internal static bool IsPKMPresentGBA(byte[] data, int offset) => (data[offset + 0x13] & 0xFB) == 2; // ignore egg flag, must be FlagHasSpecies. + internal static bool IsPKMPresentGB(ReadOnlySpan data) => data[0] != 0; // Species non-zero + internal static bool IsPKMPresentGC(ReadOnlySpan data) => ReadUInt16BigEndian(data) != 0; // Species non-zero + internal static bool IsPKMPresentGBA(ReadOnlySpan data) => (data[0x13] & 0xFB) == 2; // ignore egg flag, must be FlagHasSpecies. - internal static bool IsPKMPresent(byte[] data, int offset) + internal static bool IsPKMPresent(ReadOnlySpan data) { - if (BitConverter.ToUInt32(data, offset) != 0) // PID + if (ReadUInt32LittleEndian(data) != 0) // PID return true; - ushort species = BitConverter.ToUInt16(data, offset + 8); + ushort species = ReadUInt16LittleEndian(data[8..]); return species != 0; } @@ -271,15 +274,15 @@ internal static bool IsPKMPresent(byte[] data, int offset) /// /// /// Function that checks if a byte array (at an offset) has a present - public static Func GetFuncIsPKMPresent(PKM blank) + public static Func GetFuncIsPKMPresent(PKM blank) { if (blank.Format >= 4) - return IsPKMPresent; + return x => IsPKMPresent(x); if (blank.Format <= 2) - return IsPKMPresentGB; + return x => IsPKMPresentGB(x); if (blank.Data.Length <= PokeCrypto.SIZE_3PARTY) - return IsPKMPresentGBA; - return IsPKMPresentGC; + return x => IsPKMPresentGBA(x); + return x => IsPKMPresentGC(x); } /// diff --git a/PKHeX.Core/PKM/Util/PokeCrypto.cs b/PKHeX.Core/PKM/Util/PokeCrypto.cs index 70f2e9f4a..10d97a4cd 100644 --- a/PKHeX.Core/PKM/Util/PokeCrypto.cs +++ b/PKHeX.Core/PKM/Util/PokeCrypto.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Runtime.CompilerServices; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -102,15 +103,17 @@ public static class PokeCrypto /// Size of shuffling chunks /// Shuffled byte array [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte[] ShuffleArray(byte[] data, uint sv, int blockSize) + public static byte[] ShuffleArray(ReadOnlySpan data, uint sv, int blockSize) { - byte[] sdata = (byte[])data.Clone(); + byte[] sdata = data.ToArray(); uint index = sv * 4; const int start = 8; for (int block = 0; block < 4; block++) { int ofs = BlockPosition[index + block]; - Array.Copy(data, start + (blockSize * ofs), sdata, start + (blockSize * block), blockSize); + var src = data.Slice(start + (blockSize * ofs), blockSize); + var dest = sdata.AsSpan(start + (blockSize * block), blockSize); + src.CopyTo(dest); } return sdata; } @@ -121,9 +124,9 @@ public static byte[] ShuffleArray(byte[] data, uint sv, int blockSize) /// Encrypted Pokémon data. /// Decrypted Pokémon data. /// Encrypted Pokémon data. - public static byte[] DecryptArray8(byte[] ekm) + public static byte[] DecryptArray8(Span ekm) { - uint pv = BitConverter.ToUInt32(ekm, 0); + uint pv = ReadUInt32LittleEndian(ekm); uint sv = pv >> 13 & 31; CryptPKM(ekm, pv, SIZE_8BLOCK); @@ -134,9 +137,9 @@ public static byte[] DecryptArray8(byte[] ekm) /// Encrypts a Gen8 pkm byte array. /// /// Decrypted Pokémon data. - public static byte[] EncryptArray8(byte[] pkm) + public static byte[] EncryptArray8(Span pkm) { - uint pv = BitConverter.ToUInt32(pkm, 0); + uint pv = ReadUInt32LittleEndian(pkm); uint sv = pv >> 13 & 31; byte[] ekm = ShuffleArray(pkm, blockPositionInvert[sv], SIZE_8BLOCK); @@ -150,9 +153,9 @@ public static byte[] EncryptArray8(byte[] pkm) /// Encrypted Pokémon data. /// Decrypted Pokémon data. /// Encrypted Pokémon data. - public static byte[] DecryptArray6(byte[] ekm) + public static byte[] DecryptArray6(Span ekm) { - uint pv = BitConverter.ToUInt32(ekm, 0); + uint pv = ReadUInt32LittleEndian(ekm); uint sv = pv >> 13 & 31; CryptPKM(ekm, pv, SIZE_6BLOCK); @@ -163,9 +166,9 @@ public static byte[] DecryptArray6(byte[] ekm) /// Encrypts a 232 byte + party stat byte array. ///
/// Decrypted Pokémon data. - public static byte[] EncryptArray6(byte[] pkm) + public static byte[] EncryptArray6(Span pkm) { - uint pv = BitConverter.ToUInt32(pkm, 0); + uint pv = ReadUInt32LittleEndian(pkm); uint sv = pv >> 13 & 31; byte[] ekm = ShuffleArray(pkm, blockPositionInvert[sv], SIZE_6BLOCK); @@ -178,10 +181,10 @@ public static byte[] EncryptArray6(byte[] pkm) ///
/// Encrypted Pokémon data. /// Decrypted Pokémon data. - public static byte[] DecryptArray45(byte[] ekm) + public static byte[] DecryptArray45(Span ekm) { - uint pv = BitConverter.ToUInt32(ekm, 0); - uint chk = BitConverter.ToUInt16(ekm, 6); + uint pv = ReadUInt32LittleEndian(ekm); + uint chk = ReadUInt16LittleEndian(ekm[6..]); uint sv = pv >> 13 & 31; CryptPKM45(ekm, pv, chk, SIZE_4BLOCK); @@ -193,10 +196,10 @@ public static byte[] DecryptArray45(byte[] ekm) ///
/// Decrypted Pokémon data. /// Encrypted Pokémon data. - public static byte[] EncryptArray45(byte[] pkm) + public static byte[] EncryptArray45(Span pkm) { - uint pv = BitConverter.ToUInt32(pkm, 0); - uint chk = BitConverter.ToUInt16(pkm, 6); + uint pv = ReadUInt32LittleEndian(pkm); + uint chk = ReadUInt16LittleEndian(pkm[6..]); uint sv = pv >> 13 & 31; byte[] ekm = ShuffleArray(pkm, blockPositionInvert[sv], SIZE_4BLOCK); @@ -205,7 +208,7 @@ public static byte[] EncryptArray45(byte[] pkm) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void CryptPKM(byte[] data, uint pv, int blockSize) + private static void CryptPKM(Span data, uint pv, int blockSize) { const int start = 8; int end = (4 * blockSize) + start; @@ -215,7 +218,7 @@ private static void CryptPKM(byte[] data, uint pv, int blockSize) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void CryptPKM45(byte[] data, uint pv, uint chk, int blockSize) + private static void CryptPKM45(Span data, uint pv, uint chk, int blockSize) { const int start = 8; int end = (4 * blockSize) + start; @@ -225,26 +228,27 @@ private static void CryptPKM45(byte[] data, uint pv, uint chk, int blockSize) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CryptArray(byte[] data, uint seed, int start, int end) + public static void CryptArray(Span data, uint seed, int start, int end) { int i = start; do // all block sizes are multiples of 4 { - Crypt(data, ref seed, i); i += 2; - Crypt(data, ref seed, i); i += 2; + Crypt(data[i..], ref seed); i += 2; + Crypt(data[i..], ref seed); i += 2; } while (i < end); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CryptArray(byte[] data, uint seed) => CryptArray(data, seed, 0, data.Length); + public static void CryptArray(Span data, uint seed) => CryptArray(data, seed, 0, data.Length); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Crypt(byte[] data, ref uint seed, in int i) + private static void Crypt(Span data, ref uint seed) { seed = (0x41C64E6D * seed) + 0x00006073; - data[i] ^= (byte)(seed >> 16); - data[i + 1] ^= (byte)(seed >> 24); + var current = ReadUInt16LittleEndian(data); + current ^= (ushort)(seed >> 16); + WriteUInt16LittleEndian(data, current); } /// @@ -252,17 +256,22 @@ private static void Crypt(byte[] data, ref uint seed, in int i) /// /// Encrypted data. /// Decrypted data. - public static byte[] DecryptArray3(byte[] ekm) + public static byte[] DecryptArray3(Span ekm) { Debug.Assert(ekm.Length is SIZE_3PARTY or SIZE_3STORED); - uint PID = BitConverter.ToUInt32(ekm, 0); - uint OID = BitConverter.ToUInt32(ekm, 4); + uint PID = ReadUInt32LittleEndian(ekm); + uint OID = ReadUInt32LittleEndian(ekm[4..]); uint seed = PID ^ OID; - byte[] xorkey = BitConverter.GetBytes(seed); - for (int i = 32; i < 80; i++) - ekm[i] ^= xorkey[i & 3]; + var toEncrypt = ekm[32..SIZE_3STORED]; + for (int i = 0; i < toEncrypt.Length; i += 4) + { + var span = toEncrypt.Slice(i, 4); + var chunk = ReadUInt32LittleEndian(span); + var update = chunk ^ seed; + WriteUInt32LittleEndian(span, update); + } return ShuffleArray3(ekm, PID % 24); } @@ -272,20 +281,18 @@ public static byte[] DecryptArray3(byte[] ekm) /// Un-shuffled data. /// Block order shuffle value /// Un-shuffled data. - private static byte[] ShuffleArray3(byte[] data, uint sv) + private static byte[] ShuffleArray3(Span data, uint sv) { - byte[] sdata = (byte[])data.Clone(); + byte[] sdata = data.ToArray(); uint index = sv * 4; for (int block = 0; block < 4; block++) { int ofs = BlockPosition[index + block]; - Array.Copy(data, 32 + (12 * ofs), sdata, 32 + (12 * block), 12); + var src = data.Slice(32 + (12 * ofs), 12); + var dest = sdata.AsSpan(32 + (12 * block), 12); + src.CopyTo(dest); } - // Fill the Battle Stats back - if (data.Length > SIZE_3STORED) - Array.Copy(data, SIZE_3STORED, sdata, SIZE_3STORED, data.Length - SIZE_3STORED); - return sdata; } @@ -294,18 +301,24 @@ private static byte[] ShuffleArray3(byte[] data, uint sv) ///
/// Decrypted data. /// Encrypted data. - public static byte[] EncryptArray3(byte[] pkm) + public static byte[] EncryptArray3(Span pkm) { Debug.Assert(pkm.Length is SIZE_3PARTY or SIZE_3STORED); - uint PID = BitConverter.ToUInt32(pkm, 0); - uint OID = BitConverter.ToUInt32(pkm, 4); + uint PID = ReadUInt32LittleEndian(pkm); + uint OID = ReadUInt32LittleEndian(pkm[4..]); uint seed = PID ^ OID; byte[] ekm = ShuffleArray3(pkm, blockPositionInvert[PID % 24]); - byte[] xorkey = BitConverter.GetBytes(seed); - for (int i = 32; i < SIZE_3STORED; i++) - ekm[i] ^= xorkey[i & 3]; + + var toEncrypt = ekm.AsSpan()[32..SIZE_3STORED]; + for (int i = 0; i < toEncrypt.Length; i += 4) + { + var span = toEncrypt.Slice(i, 4); + var chunk = ReadUInt32LittleEndian(span); + var update = chunk ^ seed; + WriteUInt32LittleEndian(span, update); + } return ekm; } @@ -314,11 +327,12 @@ public static byte[] EncryptArray3(byte[] pkm) ///
/// Decrypted Pokémon data. /// Offset at which the Stored data ends and the Party data starts. - public static ushort GetCHK(byte[] data, int partyStart) + public static ushort GetCHK(ReadOnlySpan data, int partyStart) { ushort chk = 0; - for (int i = 8; i < partyStart; i += 2) - chk += BitConverter.ToUInt16(data, i); + var span = data[0x08..partyStart]; + for (int i = 0; i < span.Length; i += 2) + chk += ReadUInt16LittleEndian(span[i..]); return chk; } @@ -327,11 +341,12 @@ public static ushort GetCHK(byte[] data, int partyStart) /// /// Decrypted Pokémon data. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ushort GetCHK3(byte[] data) + public static ushort GetCHK3(ReadOnlySpan data) { ushort chk = 0; - for (int i = 0x20; i < SIZE_3STORED; i += 2) - chk += BitConverter.ToUInt16(data, i); + var span = data[0x20..SIZE_3STORED]; + for (int i = 0; i < span.Length; i += 2) + chk += ReadUInt16LittleEndian(span[i..]); return chk; } @@ -342,7 +357,7 @@ public static ushort GetCHK3(byte[] data) public static void DecryptIfEncrypted3(ref byte[] pkm) { ushort chk = GetCHK3(pkm); - if (chk != BitConverter.ToUInt16(pkm, 0x1C)) + if (chk != ReadUInt16LittleEndian(pkm.AsSpan(0x1C))) pkm = DecryptArray3(pkm); } @@ -352,8 +367,9 @@ public static void DecryptIfEncrypted3(ref byte[] pkm) /// Generation 4 & 5 Format encryption check which checks for the unused bytes public static void DecryptIfEncrypted45(ref byte[] pkm) { - if (BitConverter.ToUInt32(pkm, 0x64) != 0) - pkm = DecryptArray45(pkm); + var span = pkm.AsSpan(); + if (ReadUInt32LittleEndian(span[0x64..]) != 0) + pkm = DecryptArray45(span); } /// @@ -362,8 +378,9 @@ public static void DecryptIfEncrypted45(ref byte[] pkm) /// Generation 6 & 7 Format encryption check public static void DecryptIfEncrypted67(ref byte[] pkm) { - if (BitConverter.ToUInt16(pkm, 0xC8) != 0 || BitConverter.ToUInt16(pkm, 0x58) != 0) - pkm = DecryptArray6(pkm); + var span = pkm.AsSpan(); + if (ReadUInt16LittleEndian(span[0xC8..]) != 0 || ReadUInt16LittleEndian(span[0x58..]) != 0) + pkm = DecryptArray6(span); } /// @@ -372,8 +389,9 @@ public static void DecryptIfEncrypted67(ref byte[] pkm) /// Generation 8 Format encryption check public static void DecryptIfEncrypted8(ref byte[] pkm) { - if (BitConverter.ToUInt16(pkm, 0x70) != 0 || BitConverter.ToUInt16(pkm, 0xC0) != 0) - pkm = DecryptArray8(pkm); + var span = pkm.AsSpan(); + if (ReadUInt16LittleEndian(span[0x70..]) != 0 || ReadUInt16LittleEndian(span[0xC0..]) != 0) + pkm = DecryptArray8(span); } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/PKM/Util/SpeciesName.cs b/PKHeX.Core/PKM/Util/SpeciesName.cs index 48d08e5f7..3b6d4d9b4 100644 --- a/PKHeX.Core/PKM/Util/SpeciesName.cs +++ b/PKHeX.Core/PKM/Util/SpeciesName.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; namespace PKHeX.Core @@ -104,18 +105,24 @@ private static string GetSpeciesName1234(int species, int language, int generati return nick; // No further processing } + Span result = stackalloc char[nick.Length]; + nick.AsSpan().CopyTo(result); + // All names are uppercase. - var sb = new System.Text.StringBuilder(nick); - for (int i = 0; i < sb.Length; i++) - sb[i] = char.ToUpperInvariant(sb[i]); + for (int i = 0; i < result.Length; i++) + result[i] = char.ToUpperInvariant(result[i]); if (language == (int)LanguageID.French) - StringConverter4.StripDiacriticsFR4(sb); // strips accents on E and I + StringConverter4Util.StripDiacriticsFR4(result); // strips accents on E and I // Gen1/2 species names do not have spaces. - if (generation < 3) - sb.Replace(" ", string.Empty); - - return sb.ToString(); + int indexSpace = result.IndexOf(' '); + if (indexSpace != -1) + { + // Shift down. Strings have at most 1 occurrence of a space. + result[(indexSpace+1)..].CopyTo(result[indexSpace..]); + result = result[..^1]; + } + return new string(result.ToArray()); } private static string GetEggName1234(int species, int language, int generation) @@ -242,8 +249,8 @@ private static int GetSpeciesNameLanguage(int species, string nickname, int gene /// Only use this for modern era name -> ID fetching. public static int GetSpeciesID(string speciesName, int language = (int)LanguageID.English) { - if (SpeciesDict[language].TryGetValue(speciesName, out var val)) - return val; + if (SpeciesDict[language].TryGetValue(speciesName, out var value)) + return value; // stupid ’, ignore language if we match these. return speciesName switch diff --git a/PKHeX.Core/PKM/XK3.cs b/PKHeX.Core/PKM/XK3.cs index 90926a84e..5a9d69424 100644 --- a/PKHeX.Core/PKM/XK3.cs +++ b/PKHeX.Core/PKM/XK3.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -23,25 +24,19 @@ public sealed class XK3 : G3PKM, IShadowPKM public XK3(byte[] data) : base(data) { } public XK3() : base(PokeCrypto.SIZE_3XSTORED) { } public override PKM Clone() => new XK3((byte[])Data.Clone()){Purification = Purification}; - - private string GetString(int offset, int count) => StringConverter3.GetBEString3(Data, offset, count); - private static byte[] SetString(string value, int maxLength) => StringConverter3.SetBEString3(value, maxLength); + public override void RefreshChecksum() => Valid = true; // Trash Bytes - public override Span Nickname_Trash { get => Data.AsSpan(0x4E, 20); set { if (value.Length == 20) value.CopyTo(Data.AsSpan(0x4E)); } } - public override Span OT_Trash { get => Data.AsSpan(0x38, 20); set { if (value.Length == 20) value.CopyTo(Data.AsSpan(0x38)); } } + public override Span OT_Trash => Data.AsSpan(0x38, 22); + public override Span Nickname_Trash => Data.AsSpan(0x4E, 22); + public Span NicknameCopy_Trash => Data.AsSpan(0x64, 22); - // Silly Attributes - public override ushort Sanity { get => 0; set { } } // valid flag set in pkm structure. - public override ushort Checksum { get => Checksums.CRC16_CCITT(Data); set { } } // totally false, just a way to get a 'random' ident for the pkm. - public override bool ChecksumValid => Valid; - - public override int Species { get => SpeciesConverter.GetG4Species(BigEndian.ToUInt16(Data, 0x00)); set => BigEndian.GetBytes((ushort)SpeciesConverter.GetG3Species(value)).CopyTo(Data, 0x00); } + public override int Species { get => SpeciesConverter.GetG4Species(ReadUInt16BigEndian(Data.AsSpan(0x00))); set => WriteUInt16BigEndian(Data.AsSpan(0x00), (ushort)SpeciesConverter.GetG3Species(value)); } public override int SpriteItem => ItemConverter.GetItemFuture3((ushort)HeldItem); - public override int HeldItem { get => BigEndian.ToUInt16(Data, 0x02); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x02); } - public override int Stat_HPCurrent { get => BigEndian.ToUInt16(Data, 0x04); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x04); } - public override int OT_Friendship { get => BigEndian.ToUInt16(Data, 0x06); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x06); } - public override int Met_Location { get => BigEndian.ToUInt16(Data, 0x08); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x08); } + public override int HeldItem { get => ReadUInt16BigEndian(Data.AsSpan(0x02)); set => WriteUInt16BigEndian(Data.AsSpan(0x02), (ushort)value); } + public override int Stat_HPCurrent { get => ReadUInt16BigEndian(Data.AsSpan(0x04)); set => WriteUInt16BigEndian(Data.AsSpan(0x04), (ushort)value); } + public override int OT_Friendship { get => ReadUInt16BigEndian(Data.AsSpan(0x06)); set => WriteUInt16BigEndian(Data.AsSpan(0x06), (ushort)value); } + public override int Met_Location { get => ReadUInt16BigEndian(Data.AsSpan(0x08)); set => WriteUInt16BigEndian(Data.AsSpan(0x08), (ushort)value); } // 0x0A-0x0B Unknown // 0x0C-0x0D Unknown public override int Met_Level { get => Data[0x0E]; set => Data[0x0E] = (byte)value; } @@ -63,10 +58,10 @@ public sealed class XK3 : G3PKM, IShadowPKM public override bool AbilityBit { get => (XDPKMFLAGS & (1 << 6)) == 1 << 6; set => XDPKMFLAGS = (XDPKMFLAGS & ~(1 << 6)) | (value ? 1 << 6 : 0); } public override bool IsEgg { get => (XDPKMFLAGS & (1 << 7)) == 1 << 7; set => XDPKMFLAGS = (XDPKMFLAGS & ~(1 << 7)) | (value ? 1 << 7 : 0); } // 0x1E-0x1F Unknown - public override uint EXP { get => BigEndian.ToUInt32(Data, 0x20); set => BigEndian.GetBytes(value).CopyTo(Data, 0x20); } - public override int SID { get => BigEndian.ToUInt16(Data, 0x24); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x24); } - public override int TID { get => BigEndian.ToUInt16(Data, 0x26); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x26); } - public override uint PID { get => BigEndian.ToUInt32(Data, 0x28); set => BigEndian.GetBytes(value).CopyTo(Data, 0x28); } + public override uint EXP { get => ReadUInt32BigEndian(Data.AsSpan(0x20)); set => WriteUInt32BigEndian(Data.AsSpan(0x20), value); } + public override int SID { get => ReadUInt16BigEndian(Data.AsSpan(0x24)); set => WriteUInt16BigEndian(Data.AsSpan(0x24), (ushort)value); } + public override int TID { get => ReadUInt16BigEndian(Data.AsSpan(0x26)); set => WriteUInt16BigEndian(Data.AsSpan(0x26), (ushort)value); } + public override uint PID { get => ReadUInt32BigEndian(Data.AsSpan(0x28)); set => WriteUInt32BigEndian(Data.AsSpan(0x28), value); } // 0x2A-0x2B Unknown // 0x2C-0x2F Battle Related public bool Obedient { get => Data[0x30] == 1; set => Data[0x30] = value ? (byte)1 : (byte)0; } @@ -92,11 +87,11 @@ public override bool FatefulEncounter public int CurrentRegion { get => Data[0x35]; set => Data[0x35] = (byte)value; } public int OriginalRegion { get => Data[0x36]; set => Data[0x36] = (byte)value; } public override int Language { get => Core.Language.GetMainLangIDfromGC(Data[0x37]); set => Data[0x37] = Core.Language.GetGCLangIDfromMain((byte)value); } - public override string OT_Name { get => GetString(0x38, 20); set => SetString(value, 10).CopyTo(Data, 0x38); } // +2 terminator - public override string Nickname { get => GetString(0x4E, 20); set { SetString(value, 10).CopyTo(Data, 0x4E); NicknameCopy = value; } } // +2 terminator - public string NicknameCopy { get => GetString(0x64, 20); set => SetString(value, 10).CopyTo(Data, 0x64); } // +2 terminator + public override string OT_Name { get => StringConverter3GC.GetString(OT_Trash); set => StringConverter3GC.SetString(OT_Trash, value.AsSpan(), 10, StringConverterOption.None); } + public override string Nickname { get => StringConverter3GC.GetString(Nickname_Trash); set { StringConverter3GC.SetString(Nickname_Trash, value.AsSpan(), 10, StringConverterOption.None); NicknameCopy = value; } } + public string NicknameCopy { get => StringConverter3GC.GetString(NicknameCopy_Trash); set => StringConverter3GC.SetString(NicknameCopy_Trash, value.AsSpan(), 10, StringConverterOption.None); } // 0x7A-0x7B Unknown - private ushort RIB0 { get => BigEndian.ToUInt16(Data, 0x7C); set => BigEndian.GetBytes(value).CopyTo(Data, 0x7C); } + private ushort RIB0 { get => ReadUInt16BigEndian(Data.AsSpan(0x7C)); set => WriteUInt16BigEndian(Data.AsSpan(0x7C), value); } public override bool RibbonChampionG3 { get => (RIB0 & (1 << 15)) == 1 << 15; set => RIB0 = (ushort)((RIB0 & ~(1 << 15)) | (value ? 1 << 15 : 0)); } public override bool RibbonWinning { get => (RIB0 & (1 << 14)) == 1 << 14; set => RIB0 = (ushort)((RIB0 & ~(1 << 14)) | (value ? 1 << 14 : 0)); } public override bool RibbonVictory { get => (RIB0 & (1 << 13)) == 1 << 13; set => RIB0 = (ushort)((RIB0 & ~(1 << 13)) | (value ? 1 << 13 : 0)); } @@ -116,62 +111,62 @@ public override bool FatefulEncounter // 0x7E-0x7F Unknown // Moves - public override int Move1 { get => BigEndian.ToUInt16(Data, 0x80); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x80); } + public override int Move1 { get => ReadUInt16BigEndian(Data.AsSpan(0x80)); set => WriteUInt16BigEndian(Data.AsSpan(0x80), (ushort)value); } public override int Move1_PP { get => Data[0x82]; set => Data[0x82] = (byte)value; } public override int Move1_PPUps { get => Data[0x83]; set => Data[0x83] = (byte)value; } - public override int Move2 { get => BigEndian.ToUInt16(Data, 0x84); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x84); } + public override int Move2 { get => ReadUInt16BigEndian(Data.AsSpan(0x84)); set => WriteUInt16BigEndian(Data.AsSpan(0x84), (ushort)value); } public override int Move2_PP { get => Data[0x86]; set => Data[0x86] = (byte)value; } public override int Move2_PPUps { get => Data[0x87]; set => Data[0x87] = (byte)value; } - public override int Move3 { get => BigEndian.ToUInt16(Data, 0x88); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x88); } + public override int Move3 { get => ReadUInt16BigEndian(Data.AsSpan(0x88)); set => WriteUInt16BigEndian(Data.AsSpan(0x88), (ushort)value); } public override int Move3_PP { get => Data[0x8A]; set => Data[0x8A] = (byte)value; } public override int Move3_PPUps { get => Data[0x8B]; set => Data[0x8B] = (byte)value; } - public override int Move4 { get => BigEndian.ToUInt16(Data, 0x8C); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x8C); } + public override int Move4 { get => ReadUInt16BigEndian(Data.AsSpan(0x8C)); set => WriteUInt16BigEndian(Data.AsSpan(0x8C), (ushort)value); } public override int Move4_PP { get => Data[0x8E]; set => Data[0x8E] = (byte)value; } public override int Move4_PPUps { get => Data[0x8F]; set => Data[0x8F] = (byte)value; } // More party stats - public override int Stat_HPMax { get => BigEndian.ToUInt16(Data, 0x90); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x90); } - public override int Stat_ATK { get => BigEndian.ToUInt16(Data, 0x92); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x92); } - public override int Stat_DEF { get => BigEndian.ToUInt16(Data, 0x94); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x94); } - public override int Stat_SPA { get => BigEndian.ToUInt16(Data, 0x96); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x96); } - public override int Stat_SPD { get => BigEndian.ToUInt16(Data, 0x98); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x98); } - public override int Stat_SPE { get => BigEndian.ToUInt16(Data, 0x9A); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x9A); } + public override int Stat_HPMax { get => ReadUInt16BigEndian(Data.AsSpan(0x90)); set => WriteUInt16BigEndian(Data.AsSpan(0x90), (ushort)value); } + public override int Stat_ATK { get => ReadUInt16BigEndian(Data.AsSpan(0x92)); set => WriteUInt16BigEndian(Data.AsSpan(0x92), (ushort)value); } + public override int Stat_DEF { get => ReadUInt16BigEndian(Data.AsSpan(0x94)); set => WriteUInt16BigEndian(Data.AsSpan(0x94), (ushort)value); } + public override int Stat_SPA { get => ReadUInt16BigEndian(Data.AsSpan(0x96)); set => WriteUInt16BigEndian(Data.AsSpan(0x96), (ushort)value); } + public override int Stat_SPD { get => ReadUInt16BigEndian(Data.AsSpan(0x98)); set => WriteUInt16BigEndian(Data.AsSpan(0x98), (ushort)value); } + public override int Stat_SPE { get => ReadUInt16BigEndian(Data.AsSpan(0x9A)); set => WriteUInt16BigEndian(Data.AsSpan(0x9A), (ushort)value); } // EVs public override int EV_HP { - get => Math.Min(byte.MaxValue, BigEndian.ToUInt16(Data, 0x9C)); - set => BigEndian.GetBytes((ushort)(value & 0xFF)).CopyTo(Data, 0x9C); + get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0x9C))); + set => WriteUInt16BigEndian(Data.AsSpan(0x9C), (ushort)(value & 0xFF)); } public override int EV_ATK { - get => Math.Min(byte.MaxValue, BigEndian.ToUInt16(Data, 0x9E)); - set => BigEndian.GetBytes((ushort)(value & 0xFF)).CopyTo(Data, 0x9E); + get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0x9E))); + set => WriteUInt16BigEndian(Data.AsSpan(0x9E), (ushort)(value & 0xFF)); } public override int EV_DEF { - get => Math.Min(byte.MaxValue, BigEndian.ToUInt16(Data, 0xA0)); - set => BigEndian.GetBytes((ushort)(value & 0xFF)).CopyTo(Data, 0xA0); + get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0xA0))); + set => WriteUInt16BigEndian(Data.AsSpan(0xA0), (ushort)(value & 0xFF)); } public override int EV_SPA { - get => Math.Min(byte.MaxValue, BigEndian.ToUInt16(Data, 0xA2)); - set => BigEndian.GetBytes((ushort)(value & 0xFF)).CopyTo(Data, 0xA2); + get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0xA2))); + set => WriteUInt16BigEndian(Data.AsSpan(0xA2), (ushort)(value & 0xFF)); } public override int EV_SPD { - get => Math.Min(byte.MaxValue, BigEndian.ToUInt16(Data, 0xA4)); - set => BigEndian.GetBytes((ushort)(value & 0xFF)).CopyTo(Data, 0xA4); + get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0xA4))); + set => WriteUInt16BigEndian(Data.AsSpan(0xA4), (ushort)(value & 0xFF)); } public override int EV_SPE { - get => Math.Min(byte.MaxValue, BigEndian.ToUInt16(Data, 0xA6)); - set => BigEndian.GetBytes((ushort)(value & 0xFF)).CopyTo(Data, 0xA6); + get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0xA6))); + set => WriteUInt16BigEndian(Data.AsSpan(0xA6), (ushort)(value & 0xFF)); } // IVs @@ -194,7 +189,7 @@ public override int EV_SPE public override int RibbonCountG3Smart { get => Data[0xB6]; set => Data[0xB6] = (byte)value; } public override int RibbonCountG3Tough { get => Data[0xB7]; set => Data[0xB7] = (byte)value; } - public int ShadowID { get => BigEndian.ToUInt16(Data, 0xBA); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0xBA); } + public int ShadowID { get => ReadUInt16BigEndian(Data.AsSpan(0xBA)); set => WriteUInt16BigEndian(Data.AsSpan(0xBA), (ushort)value); } // Purification information is stored in the save file and accessed based on the Shadow ID. public int Purification { get; set; } diff --git a/PKHeX.Core/PersonalInfo/PersonalInfoBDSP.cs b/PKHeX.Core/PersonalInfo/PersonalInfoBDSP.cs index 0de1f5e38..dbb4542e9 100644 --- a/PKHeX.Core/PersonalInfo/PersonalInfoBDSP.cs +++ b/PKHeX.Core/PersonalInfo/PersonalInfoBDSP.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -21,46 +22,46 @@ public sealed class PersonalInfoBDSP : PersonalInfo public override int Type2 { get => Data[0x07]; set => Data[0x07] = (byte)value; } public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; } public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; } - private int EVYield { get => BitConverter.ToUInt16(Data, 0x0A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); } + private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); } public override int EV_HP { get => EVYield >> 0 & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | (value & 0x3) << 0; } public override int EV_ATK { get => EVYield >> 2 & 0x3; set => EVYield = (EVYield & ~(0x3 << 2)) | (value & 0x3) << 2; } public override int EV_DEF { get => EVYield >> 4 & 0x3; set => EVYield = (EVYield & ~(0x3 << 4)) | (value & 0x3) << 4; } public override int EV_SPE { get => EVYield >> 6 & 0x3; set => EVYield = (EVYield & ~(0x3 << 6)) | (value & 0x3) << 6; } public override int EV_SPA { get => EVYield >> 8 & 0x3; set => EVYield = (EVYield & ~(0x3 << 8)) | (value & 0x3) << 8; } public override int EV_SPD { get => EVYield >> 10 & 0x3; set => EVYield = (EVYield & ~(0x3 << 10)) | (value & 0x3) << 10; } - public int Item1 { get => BitConverter.ToInt16(Data, 0x0C); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x0C); } - public int Item2 { get => BitConverter.ToInt16(Data, 0x0E); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x0E); } - public int Item3 { get => BitConverter.ToInt16(Data, 0x10); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x10); } + public int Item1 { get => ReadInt16LittleEndian(Data.AsSpan(0x0C)); set => WriteInt16LittleEndian(Data.AsSpan(0x0C), (short)value); } + public int Item2 { get => ReadInt16LittleEndian(Data.AsSpan(0x0E)); set => WriteInt16LittleEndian(Data.AsSpan(0x0E), (short)value); } + public int Item3 { get => ReadInt16LittleEndian(Data.AsSpan(0x10)); set => WriteInt16LittleEndian(Data.AsSpan(0x10), (short)value); } public override int Gender { get => Data[0x12]; set => Data[0x12] = (byte)value; } public override int HatchCycles { get => Data[0x13]; set => Data[0x13] = (byte)value; } public override int BaseFriendship { get => Data[0x14]; set => Data[0x14] = (byte)value; } public override int EXPGrowth { get => Data[0x15]; set => Data[0x15] = (byte)value; } public override int EggGroup1 { get => Data[0x16]; set => Data[0x16] = (byte)value; } public override int EggGroup2 { get => Data[0x17]; set => Data[0x17] = (byte)value; } - public int Ability1 { get => BitConverter.ToUInt16(Data, 0x18); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x18); } - public int Ability2 { get => BitConverter.ToUInt16(Data, 0x1A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1A); } - public int AbilityH { get => BitConverter.ToUInt16(Data, 0x1C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1C); } + public int Ability1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x18)); set => WriteUInt16LittleEndian(Data.AsSpan(0x18), (ushort)value); } + public int Ability2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x1A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1A), (ushort)value); } + public int AbilityH { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), (ushort)value); } public override int EscapeRate { get => 0; set { } } // moved? - protected internal override int FormStatsIndex { get => BitConverter.ToUInt16(Data, 0x1E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1E); } + protected internal override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); } public override int FormSprite { get => 0; set { } } // No longer defined in personal public override int FormCount { get => Data[0x20]; set => Data[0x20] = (byte)value; } public override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); } public bool IsPresentInGame { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); } public bool SpriteForm { get => false; set { } } // Unspecified in table - public override int BaseEXP { get => BitConverter.ToUInt16(Data, 0x22); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x22); } - public override int Height { get => BitConverter.ToUInt16(Data, 0x24); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x24); } - public override int Weight { get => BitConverter.ToUInt16(Data, 0x26); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x26); } + public override int BaseEXP { get => ReadUInt16LittleEndian(Data.AsSpan(0x22)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22), (ushort)value); } + public override int Height { get => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); } + public override int Weight { get => ReadUInt16LittleEndian(Data.AsSpan(0x26)); set => WriteUInt16LittleEndian(Data.AsSpan(0x26), (ushort)value); } - //public uint TM1 { get => BitConverter.ToUInt32(Data, 0x28); set => BitConverter.GetBytes(value).CopyTo(Data, 0x28); } - //public uint TM2 { get => BitConverter.ToUInt32(Data, 0x2C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x2C); } - //public uint TM3 { get => BitConverter.ToUInt32(Data, 0x30); set => BitConverter.GetBytes(value).CopyTo(Data, 0x30); } - //public uint TM4 { get => BitConverter.ToUInt32(Data, 0x34); set => BitConverter.GetBytes(value).CopyTo(Data, 0x34); } - //public uint Tutor { get => BitConverter.ToUInt32(Data, 0x38); set => BitConverter.GetBytes(value).CopyTo(Data, 0x38); } + //public uint TM1 { get => ReadUInt32LittleEndian(Data.AsSpan(0x28)); set => WriteUInt16LittleEndian(Data.AsSpan(0x28)); } + //public uint TM2 { get => ReadUInt32LittleEndian(Data.AsSpan(0x2C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2C)); } + //public uint TM3 { get => ReadUInt32LittleEndian(Data.AsSpan(0x30)); set => WriteUInt16LittleEndian(Data.AsSpan(0x30)); } + //public uint TM4 { get => ReadUInt32LittleEndian(Data.AsSpan(0x34)); set => WriteUInt16LittleEndian(Data.AsSpan(0x34)); } + //public uint Tutor { get => ReadUInt32LittleEndian(Data.AsSpan(0x38)); set => WriteUInt16LittleEndian(Data.AsSpan(0x38)); } - public int Species { get => BitConverter.ToUInt16(Data, 0x3C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x3C); } - public int HatchSpecies { get => BitConverter.ToUInt16(Data, 0x3E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x3E); } - public int HatchFormIndex { get => BitConverter.ToUInt16(Data, 0x40); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x40); } - public int PokeDexIndex { get => BitConverter.ToUInt16(Data, 0x42); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x42); } + public int Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x3C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x3C), (ushort)value); } + public int HatchSpecies { get => ReadUInt16LittleEndian(Data.AsSpan(0x3E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x3E), (ushort)value); } + public int HatchFormIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x40)); set => WriteUInt16LittleEndian(Data.AsSpan(0x40), (ushort)value); } + public int PokeDexIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x42)); set => WriteUInt16LittleEndian(Data.AsSpan(0x42), (ushort)value); } public PersonalInfoBDSP(byte[] data) : base(data) { diff --git a/PKHeX.Core/PersonalInfo/PersonalInfoBW.cs b/PKHeX.Core/PersonalInfo/PersonalInfoBW.cs index cca61b40c..9ef9130d0 100644 --- a/PKHeX.Core/PersonalInfo/PersonalInfoBW.cs +++ b/PKHeX.Core/PersonalInfo/PersonalInfoBW.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -34,7 +35,7 @@ public override byte[] Write() public sealed override int Type2 { get => Data[0x07]; set => Data[0x07] = (byte)value; } public sealed override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; } public sealed override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; } - private int EVYield { get => BitConverter.ToUInt16(Data, 0x0A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); } + private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); } public sealed override int EV_HP { get => EVYield >> 0 & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | (value & 0x3) << 0; } public sealed override int EV_ATK { get => EVYield >> 2 & 0x3; set => EVYield = (EVYield & ~(0x3 << 2)) | (value & 0x3) << 2; } public sealed override int EV_DEF { get => EVYield >> 4 & 0x3; set => EVYield = (EVYield & ~(0x3 << 4)) | (value & 0x3) << 4; } @@ -42,9 +43,9 @@ public override byte[] Write() public sealed override int EV_SPA { get => EVYield >> 8 & 0x3; set => EVYield = (EVYield & ~(0x3 << 8)) | (value & 0x3) << 8; } public sealed override int EV_SPD { get => EVYield >> 10 & 0x3; set => EVYield = (EVYield & ~(0x3 << 10)) | (value & 0x3) << 10; } public bool Telekenesis { get => (EVYield >> 12 & 1) == 1; set => EVYield = (EVYield & ~(0x1 << 12)) | (value ? 1 : 0) << 12; } - public int Item1 { get => BitConverter.ToInt16(Data, 0x0C); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x0C); } - public int Item2 { get => BitConverter.ToInt16(Data, 0x0E); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x0E); } - public int Item3 { get => BitConverter.ToInt16(Data, 0x10); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x10); } + public int Item1 { get => ReadInt16LittleEndian(Data.AsSpan(0x0C)); set => WriteInt16LittleEndian(Data.AsSpan(0x0C), (short)value); } + public int Item2 { get => ReadInt16LittleEndian(Data.AsSpan(0x0E)); set => WriteInt16LittleEndian(Data.AsSpan(0x0E), (short)value); } + public int Item3 { get => ReadInt16LittleEndian(Data.AsSpan(0x10)); set => WriteInt16LittleEndian(Data.AsSpan(0x10), (short)value); } public sealed override int Gender { get => Data[0x12]; set => Data[0x12] = (byte)value; } public sealed override int HatchCycles { get => Data[0x13]; set => Data[0x13] = (byte)value; } public sealed override int BaseFriendship { get => Data[0x14]; set => Data[0x14] = (byte)value; } @@ -56,16 +57,16 @@ public override byte[] Write() public int AbilityH { get => Data[0x1A]; set => Data[0x1A] = (byte)value; } public sealed override int EscapeRate { get => Data[0x1B]; set => Data[0x1B] = (byte)value; } - protected internal override int FormStatsIndex { get => BitConverter.ToUInt16(Data, 0x1C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1C); } - public sealed override int FormSprite { get => BitConverter.ToUInt16(Data, 0x1E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1E); } + protected internal override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), (ushort)value); } + public sealed override int FormSprite { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); } public sealed override int FormCount { get => Data[0x20]; set => Data[0x20] = (byte)value; } public sealed override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); } public bool SpriteFlip { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); } public bool SpriteForm { get => ((Data[0x21] >> 7) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x80) | (value ? 0x80 : 0)); } - public sealed override int BaseEXP { get => BitConverter.ToUInt16(Data, 0x22); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x22); } - public sealed override int Height { get => BitConverter.ToUInt16(Data, 0x24); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x24); } - public sealed override int Weight { get => BitConverter.ToUInt16(Data, 0x26); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x26); } + public sealed override int BaseEXP { get => ReadUInt16LittleEndian(Data.AsSpan(0x22)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22), (ushort)value); } + public sealed override int Height { get => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); } + public sealed override int Weight { get => ReadUInt16LittleEndian(Data.AsSpan(0x26)); set => WriteUInt16LittleEndian(Data.AsSpan(0x26), (ushort)value); } public sealed override IReadOnlyList Items { diff --git a/PKHeX.Core/PersonalInfo/PersonalInfoG3.cs b/PKHeX.Core/PersonalInfo/PersonalInfoG3.cs index 31318cba2..4c4f556c6 100644 --- a/PKHeX.Core/PersonalInfo/PersonalInfoG3.cs +++ b/PKHeX.Core/PersonalInfo/PersonalInfoG3.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -26,15 +27,15 @@ public PersonalInfoG3(byte[] data) : base(data) public sealed override int Type2 { get => Data[0x07]; set => Data[0x07] = (byte)value; } public sealed override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; } public sealed override int BaseEXP { get => Data[0x09]; set => Data[0x09] = (byte)value; } - private int EVYield { get => BitConverter.ToUInt16(Data, 0x0A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); } + private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); } public sealed override int EV_HP { get => EVYield >> 0 & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | (value & 0x3) << 0; } public sealed override int EV_ATK { get => EVYield >> 2 & 0x3; set => EVYield = (EVYield & ~(0x3 << 2)) | (value & 0x3) << 2; } public sealed override int EV_DEF { get => EVYield >> 4 & 0x3; set => EVYield = (EVYield & ~(0x3 << 4)) | (value & 0x3) << 4; } public sealed override int EV_SPE { get => EVYield >> 6 & 0x3; set => EVYield = (EVYield & ~(0x3 << 6)) | (value & 0x3) << 6; } public sealed override int EV_SPA { get => EVYield >> 8 & 0x3; set => EVYield = (EVYield & ~(0x3 << 8)) | (value & 0x3) << 8; } public sealed override int EV_SPD { get => EVYield >> 10 & 0x3; set => EVYield = (EVYield & ~(0x3 << 10)) | (value & 0x3) << 10; } - public int Item1 { get => BitConverter.ToInt16(Data, 0xC); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0xC); } - public int Item2 { get => BitConverter.ToInt16(Data, 0xE); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0xE); } + public int Item1 { get => ReadInt16LittleEndian(Data.AsSpan(0xC)); set => WriteInt16LittleEndian(Data.AsSpan(0xC), (short)value); } + public int Item2 { get => ReadInt16LittleEndian(Data.AsSpan(0xE)); set => WriteInt16LittleEndian(Data.AsSpan(0xE), (short)value); } public sealed override int Gender { get => Data[0x10]; set => Data[0x10] = (byte)value; } public sealed override int HatchCycles { get => Data[0x11]; set => Data[0x11] = (byte)value; } public sealed override int BaseFriendship { get => Data[0x12]; set => Data[0x12] = (byte)value; } diff --git a/PKHeX.Core/PersonalInfo/PersonalInfoG4.cs b/PKHeX.Core/PersonalInfo/PersonalInfoG4.cs index 2b89f1dc1..91b30ef04 100644 --- a/PKHeX.Core/PersonalInfo/PersonalInfoG4.cs +++ b/PKHeX.Core/PersonalInfo/PersonalInfoG4.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -24,6 +25,6 @@ public override byte[] Write() // Manually added attributes public override int FormCount { get => Data[0x29]; set {} } - protected internal override int FormStatsIndex { get => BitConverter.ToUInt16(Data, 0x2A); set {} } + protected internal override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x2A)); set {} } } } diff --git a/PKHeX.Core/PersonalInfo/PersonalInfoGG.cs b/PKHeX.Core/PersonalInfo/PersonalInfoGG.cs index 850e5bc21..a1563e339 100644 --- a/PKHeX.Core/PersonalInfo/PersonalInfoGG.cs +++ b/PKHeX.Core/PersonalInfo/PersonalInfoGG.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -13,6 +14,6 @@ public PersonalInfoGG(byte[] data) : base(data) TypeTutors = GetBits(Data.AsSpan(0x38, 1)); // at most 8 flags used } - public int GoSpecies { get => BitConverter.ToUInt16(Data, 0x48); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x48); } + public int GoSpecies { get => ReadUInt16LittleEndian(Data.AsSpan(0x48)); set => WriteUInt16LittleEndian(Data.AsSpan(0x48), (ushort)value); } } } diff --git a/PKHeX.Core/PersonalInfo/PersonalInfoSM.cs b/PKHeX.Core/PersonalInfo/PersonalInfoSM.cs index 1f9f58031..4f16fbfc3 100644 --- a/PKHeX.Core/PersonalInfo/PersonalInfoSM.cs +++ b/PKHeX.Core/PersonalInfo/PersonalInfoSM.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -28,9 +29,9 @@ public override byte[] Write() return Data; } - public int SpecialZ_Item { get => BitConverter.ToUInt16(Data, 0x4C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x4C); } - public int SpecialZ_BaseMove { get => BitConverter.ToUInt16(Data, 0x4E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x4E); } - public int SpecialZ_ZMove { get => BitConverter.ToUInt16(Data, 0x50); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x50); } + public int SpecialZ_Item { get => ReadUInt16LittleEndian(Data.AsSpan(0x4C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x4C), (ushort)value); } + public int SpecialZ_BaseMove { get => ReadUInt16LittleEndian(Data.AsSpan(0x4E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x4E), (ushort)value); } + public int SpecialZ_ZMove { get => ReadUInt16LittleEndian(Data.AsSpan(0x50)); set => WriteUInt16LittleEndian(Data.AsSpan(0x50), (ushort)value); } public bool LocalVariant { get => Data[0x52] == 1; set => Data[0x52] = value ? (byte)1 : (byte)0; } } } diff --git a/PKHeX.Core/PersonalInfo/PersonalInfoSWSH.cs b/PKHeX.Core/PersonalInfo/PersonalInfoSWSH.cs index 032be8125..b4f374bd0 100644 --- a/PKHeX.Core/PersonalInfo/PersonalInfoSWSH.cs +++ b/PKHeX.Core/PersonalInfo/PersonalInfoSWSH.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -61,35 +62,35 @@ public override byte[] Write() public override int Type2 { get => Data[0x07]; set => Data[0x07] = (byte)value; } public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; } public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; } - private int EVYield { get => BitConverter.ToUInt16(Data, 0x0A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); } + private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); } public override int EV_HP { get => EVYield >> 0 & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | (value & 0x3) << 0; } public override int EV_ATK { get => EVYield >> 2 & 0x3; set => EVYield = (EVYield & ~(0x3 << 2)) | (value & 0x3) << 2; } public override int EV_DEF { get => EVYield >> 4 & 0x3; set => EVYield = (EVYield & ~(0x3 << 4)) | (value & 0x3) << 4; } public override int EV_SPE { get => EVYield >> 6 & 0x3; set => EVYield = (EVYield & ~(0x3 << 6)) | (value & 0x3) << 6; } public override int EV_SPA { get => EVYield >> 8 & 0x3; set => EVYield = (EVYield & ~(0x3 << 8)) | (value & 0x3) << 8; } public override int EV_SPD { get => EVYield >> 10 & 0x3; set => EVYield = (EVYield & ~(0x3 << 10)) | (value & 0x3) << 10; } - public int Item1 { get => BitConverter.ToInt16(Data, 0x0C); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x0C); } - public int Item2 { get => BitConverter.ToInt16(Data, 0x0E); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x0E); } - public int Item3 { get => BitConverter.ToInt16(Data, 0x10); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x10); } + public int Item1 { get => ReadInt16LittleEndian(Data.AsSpan(0x0C)); set => WriteInt16LittleEndian(Data.AsSpan(0x0C), (short)value); } + public int Item2 { get => ReadInt16LittleEndian(Data.AsSpan(0x0E)); set => WriteInt16LittleEndian(Data.AsSpan(0x0E), (short)value); } + public int Item3 { get => ReadInt16LittleEndian(Data.AsSpan(0x10)); set => WriteInt16LittleEndian(Data.AsSpan(0x10), (short)value); } public override int Gender { get => Data[0x12]; set => Data[0x12] = (byte)value; } public override int HatchCycles { get => Data[0x13]; set => Data[0x13] = (byte)value; } public override int BaseFriendship { get => Data[0x14]; set => Data[0x14] = (byte)value; } public override int EXPGrowth { get => Data[0x15]; set => Data[0x15] = (byte)value; } public override int EggGroup1 { get => Data[0x16]; set => Data[0x16] = (byte)value; } public override int EggGroup2 { get => Data[0x17]; set => Data[0x17] = (byte)value; } - public int Ability1 { get => BitConverter.ToUInt16(Data, 0x18); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x18); } - public int Ability2 { get => BitConverter.ToUInt16(Data, 0x1A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1A); } - public int AbilityH { get => BitConverter.ToUInt16(Data, 0x1C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1C); } + public int Ability1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x18)); set => WriteUInt16LittleEndian(Data.AsSpan(0x18), (ushort)value); } + public int Ability2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x1A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1A), (ushort)value); } + public int AbilityH { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), (ushort)value); } public override int EscapeRate { get => 0; set { } } // moved? - protected internal override int FormStatsIndex { get => BitConverter.ToUInt16(Data, 0x1E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1E); } - public override int FormSprite { get => BitConverter.ToUInt16(Data, 0x1E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1E); } // ??? + protected internal override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); } + public override int FormSprite { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); } // ??? public override int FormCount { get => Data[0x20]; set => Data[0x20] = (byte)value; } public override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); } public bool IsPresentInGame { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); } public bool SpriteForm { get => ((Data[0x21] >> 7) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x80) | (value ? 0x80 : 0)); } - public override int BaseEXP { get => BitConverter.ToUInt16(Data, 0x22); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x22); } - public override int Height { get => BitConverter.ToUInt16(Data, 0x24); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x24); } - public override int Weight { get => BitConverter.ToUInt16(Data, 0x26); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x26); } + public override int BaseEXP { get => ReadUInt16LittleEndian(Data.AsSpan(0x22)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22), (ushort)value); } + public override int Height { get => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); } + public override int Weight { get => ReadUInt16LittleEndian(Data.AsSpan(0x26)); set => WriteUInt16LittleEndian(Data.AsSpan(0x26), (ushort)value); } public override IReadOnlyList Items { @@ -117,16 +118,16 @@ public override IReadOnlyList Abilities public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1; - public int Species { get => BitConverter.ToUInt16(Data, 0x4C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x4C); } + public int Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x4C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x4C), (ushort)value); } - public int HatchSpecies { get => BitConverter.ToUInt16(Data, 0x56); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x56); } - public int LocalFormIndex { get => BitConverter.ToUInt16(Data, 0x58); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x58); } // local region base form - public ushort RegionalFlags { get => BitConverter.ToUInt16(Data, 0x5A); set => BitConverter.GetBytes(value).CopyTo(Data, 0x5A); } - public bool IsRegionalForm { get => (RegionalFlags & 1) == 1; set => BitConverter.GetBytes((ushort)((RegionalFlags & 0xFFFE) | (value ? 1 : 0))).CopyTo(Data, 0x5A); } - public int PokeDexIndex { get => BitConverter.ToUInt16(Data, 0x5C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5C); } - public int RegionalFormIndex { get => BitConverter.ToUInt16(Data, 0x5E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5E); } // form index of this entry - public int ArmorDexIndex { get => BitConverter.ToUInt16(Data, 0xAC); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xAC); } - public int CrownDexIndex { get => BitConverter.ToUInt16(Data, 0xAE); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xAE); } + public int HatchSpecies { get => ReadUInt16LittleEndian(Data.AsSpan(0x56)); set => WriteUInt16LittleEndian(Data.AsSpan(0x56), (ushort)value); } + public int LocalFormIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x58)); set => WriteUInt16LittleEndian(Data.AsSpan(0x58), (ushort)value); } // local region base form + public ushort RegionalFlags { get => ReadUInt16LittleEndian(Data.AsSpan(0x5A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5A), value); } + public bool IsRegionalForm { get => (RegionalFlags & 1) == 1; set => RegionalFlags = (ushort)((RegionalFlags & 0xFFFE) | (value ? 1 : 0)); } + public int PokeDexIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x5C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5C), (ushort)value); } + public int RegionalFormIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x5E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5E), (ushort)value); } // form index of this entry + public int ArmorDexIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0xAC)); set => WriteUInt16LittleEndian(Data.AsSpan(0xAC), (ushort)value); } + public int CrownDexIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0xAE)); set => WriteUInt16LittleEndian(Data.AsSpan(0xAE), (ushort)value); } /// /// Gets the Form that any offspring will hatch with, assuming it is holding an Everstone. diff --git a/PKHeX.Core/PersonalInfo/PersonalTable.cs b/PKHeX.Core/PersonalInfo/PersonalTable.cs index 31f43c560..63815c3a5 100644 --- a/PKHeX.Core/PersonalInfo/PersonalTable.cs +++ b/PKHeX.Core/PersonalInfo/PersonalTable.cs @@ -205,7 +205,7 @@ private static void CopyDexitGenders() } } - public PersonalTable(byte[] data, GameVersion format) + public PersonalTable(ReadOnlySpan data, GameVersion format) { var get = GetConstructor(format); int size = GetEntrySize(format); diff --git a/PKHeX.Core/Saves/Blocks/BlockInfo.cs b/PKHeX.Core/Saves/Blocks/BlockInfo.cs index fd6bf63c4..dfdc56a0c 100644 --- a/PKHeX.Core/Saves/Blocks/BlockInfo.cs +++ b/PKHeX.Core/Saves/Blocks/BlockInfo.cs @@ -12,8 +12,8 @@ public abstract class BlockInfo public string Summary => $"{ID:00}: {Offset:X5}-{Offset + Length - 1:X5}, {Length:X5}"; - protected abstract bool ChecksumValid(byte[] data); - protected abstract void SetChecksum(byte[] data); + protected abstract bool ChecksumValid(Span data); + protected abstract void SetChecksum(Span data); /// /// Checks if the currently written checksum values are valid. @@ -21,7 +21,7 @@ public abstract class BlockInfo /// Block info objects used for offset/length /// Complete data array /// True if checksums are valid, false if anything is invalid. - public static bool GetChecksumsValid(IEnumerable blocks, byte[] data) + public static bool GetChecksumsValid(IEnumerable blocks, Span data) { foreach (var b in blocks) { @@ -39,7 +39,7 @@ public static bool GetChecksumsValid(IEnumerable blocks, byte[] data) /// /// Block info objects used for offset/length /// Complete data array - public static void SetChecksums(IEnumerable blocks, byte[] data) + public static void SetChecksums(IEnumerable blocks, Span data) { foreach (var b in blocks) b.SetChecksum(data); @@ -51,14 +51,14 @@ public static void SetChecksums(IEnumerable blocks, byte[] data) /// Block info objects used for offset/length /// Complete data array /// Multi-line string with data. - public static string GetChecksumInfo(IReadOnlyList blocks, byte[] data) + public static string GetChecksumInfo(IReadOnlyList blocks, Span data) { var invalid = GetInvalidBlockCount(blocks, data, out var list); list.Add($"SAV: {blocks.Count - invalid}/{blocks.Count}"); return string.Join(Environment.NewLine, list); } - private static int GetInvalidBlockCount(IReadOnlyList blocks, byte[] data, out List list) + private static int GetInvalidBlockCount(IReadOnlyList blocks, Span data, out List list) { int invalid = 0; list = new List(); @@ -82,8 +82,8 @@ private static int GetInvalidBlockCount(IReadOnlyList blocks, byte[] public static partial class Extensions { - public static bool GetChecksumsValid(this IEnumerable blocks, byte[] data) => BlockInfo.GetChecksumsValid(blocks, data); - public static void SetChecksums(this IEnumerable blocks, byte[] data) => BlockInfo.SetChecksums(blocks, data); - public static string GetChecksumInfo(this IReadOnlyList blocks, byte[] data) => BlockInfo.GetChecksumInfo(blocks, data); + public static bool GetChecksumsValid(this IEnumerable blocks, Span data) => BlockInfo.GetChecksumsValid(blocks, data); + public static void SetChecksums(this IEnumerable blocks, Span data) => BlockInfo.SetChecksums(blocks, data); + public static string GetChecksumInfo(this IReadOnlyList blocks, Span data) => BlockInfo.GetChecksumInfo(blocks, data); } } diff --git a/PKHeX.Core/Saves/Blocks/BlockInfo3DS.cs b/PKHeX.Core/Saves/Blocks/BlockInfo3DS.cs index cf2c51556..cd9e4a292 100644 --- a/PKHeX.Core/Saves/Blocks/BlockInfo3DS.cs +++ b/PKHeX.Core/Saves/Blocks/BlockInfo3DS.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -31,37 +32,37 @@ protected BlockInfo3DS(int bo, uint id, int ofs, int len) } private int ChecksumOffset => BlockInfoOffset + 0x14 + ((int)ID * 8) + 6; - protected abstract ushort GetChecksum(byte[] data); + protected abstract ushort GetChecksum(Span data); - protected override bool ChecksumValid(byte[] data) + protected override bool ChecksumValid(Span data) { ushort chk = GetChecksum(data); - var old = BitConverter.ToUInt16(data, ChecksumOffset); + var old = ReadUInt16LittleEndian(data[ChecksumOffset..]); return chk == old; } - protected override void SetChecksum(byte[] data) + protected override void SetChecksum(Span data) { ushort chk = GetChecksum(data); - BitConverter.GetBytes(chk).CopyTo(data, ChecksumOffset); + WriteUInt16LittleEndian(data[ChecksumOffset..], chk); } } public sealed class BlockInfo6 : BlockInfo3DS { public BlockInfo6(int bo, uint id, int ofs, int len) : base(bo, id, ofs, len) { } - protected override ushort GetChecksum(byte[] data) => Checksums.CRC16_CCITT(new ReadOnlySpan(data, Offset, Length)); + protected override ushort GetChecksum(Span data) => Checksums.CRC16_CCITT(data.Slice(Offset, Length)); } public sealed class BlockInfo7 : BlockInfo3DS { public BlockInfo7(int bo, uint id, int ofs, int len) : base(bo, id, ofs, len) { } - protected override ushort GetChecksum(byte[] data) => Checksums.CRC16Invert(new ReadOnlySpan(data, Offset, Length)); + protected override ushort GetChecksum(Span data) => Checksums.CRC16Invert(data.Slice(Offset, Length)); } public sealed class BlockInfo7b : BlockInfo3DS { public BlockInfo7b(int bo, uint id, int ofs, int len) : base(bo, id, ofs, len) { } - protected override ushort GetChecksum(byte[] data) => Checksums.CRC16NoInvert(new ReadOnlySpan(data, Offset, Length)); + protected override ushort GetChecksum(Span data) => Checksums.CRC16NoInvert(data.Slice(Offset, Length)); } } diff --git a/PKHeX.Core/Saves/Blocks/BlockInfoNDS.cs b/PKHeX.Core/Saves/Blocks/BlockInfoNDS.cs index ef3526a97..3f8a100d9 100644 --- a/PKHeX.Core/Saves/Blocks/BlockInfoNDS.cs +++ b/PKHeX.Core/Saves/Blocks/BlockInfoNDS.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -18,24 +19,23 @@ public BlockInfoNDS(int offset, int length, int chkOffset, int chkMirror) ChecksumMirror = chkMirror; } - private ushort GetChecksum(byte[] data) => Checksums.CRC16_CCITT(new ReadOnlySpan(data, Offset, Length)); + private ushort GetChecksum(Span data) => Checksums.CRC16_CCITT(data.Slice(Offset, Length)); - protected override bool ChecksumValid(byte[] data) + protected override bool ChecksumValid(Span data) { ushort chk = GetChecksum(data); - if (chk != BitConverter.ToUInt16(data, ChecksumOffset)) + if (chk != ReadUInt16LittleEndian(data[ChecksumOffset..])) return false; - if (chk != BitConverter.ToUInt16(data, ChecksumMirror)) + if (chk != ReadUInt16LittleEndian(data[ChecksumMirror..])) return false; return true; } - protected override void SetChecksum(byte[] data) + protected override void SetChecksum(Span data) { ushort chk = GetChecksum(data); - var bytes = BitConverter.GetBytes(chk); - bytes.CopyTo(data, ChecksumOffset); - bytes.CopyTo(data, ChecksumMirror); + WriteUInt16LittleEndian(data[ChecksumOffset..], chk); + WriteUInt16LittleEndian(data[ChecksumMirror..], chk); } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs b/PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs index 205439916..e0067838b 100644 --- a/PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs +++ b/PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -11,37 +12,34 @@ public sealed class BlockInfoRSBOX : BlockInfo public readonly uint OriginalChecksum; private const int ChecksumRegionSize = 0x1FF8; - public BlockInfoRSBOX(byte[] data, int offset) + public BlockInfoRSBOX(ReadOnlySpan data, int offset) { Offset = offset; Length = 4 + ChecksumRegionSize; // Values stored in Big Endian format - OriginalChecksum = BigEndian.ToUInt32(data, Offset); - ID = BigEndian.ToUInt32(data, Offset + 4); - SaveCount = BigEndian.ToUInt32(data, Offset + 8); + OriginalChecksum = ReadUInt32BigEndian(data[Offset..]); + ID = ReadUInt32BigEndian(data[(Offset + 4)..]); + SaveCount = ReadUInt32BigEndian(data[(Offset + 8)..]); } - protected override bool ChecksumValid(byte[] data) + protected override bool ChecksumValid(Span data) { var chk = GetChecksum(data); - var old = BigEndian.ToUInt32(data, Offset); + var old = ReadUInt32BigEndian(data[Offset..]); return chk == old; } - protected override void SetChecksum(byte[] data) + protected override void SetChecksum(Span data) { var chk = GetChecksum(data); - data[Offset + 0] = (byte)(chk >> 24); - data[Offset + 1] = (byte)(chk >> 16); - data[Offset + 2] = (byte)(chk >> 8); - data[Offset + 3] = (byte)(chk >> 0); + var span = data[Offset..]; + WriteUInt32BigEndian(span, chk); } - private uint GetChecksum(byte[] data) + private uint GetChecksum(Span data) { - int start = Offset + 4; - var span = new ReadOnlySpan(data, start, ChecksumRegionSize); + var span = data.Slice(Offset + 4, ChecksumRegionSize); return Checksums.CheckSum16BigInvert(span); } } diff --git a/PKHeX.Core/Saves/Encryption/GeniusCrypto.cs b/PKHeX.Core/Saves/Encryption/GeniusCrypto.cs index eed13c701..2d07ec5e0 100644 --- a/PKHeX.Core/Saves/Encryption/GeniusCrypto.cs +++ b/PKHeX.Core/Saves/Encryption/GeniusCrypto.cs @@ -1,59 +1,60 @@ -namespace PKHeX.Core +using System; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core { /// /// Genius Sonority's logic for and encryption. /// public static class GeniusCrypto { - public static byte[] Decrypt(byte[] input, int start, int end, ushort[] keys) + public static byte[] Decrypt(ReadOnlySpan input, int start, int end, Span keys) { - var output = (byte[])input.Clone(); + var output = input.ToArray(); Decrypt(input, start, end, keys, output); return output; } - public static void Decrypt(byte[] input, int start, int end, ushort[] keys, byte[] output) + public static void Decrypt(ReadOnlySpan input, int start, int end, Span keys, Span output) { for (int ofs = start; ofs < end; ofs += 8) { for (int i = 0; i < keys.Length; i++) { var index = ofs + (i * 2); - ushort val = BigEndian.ToUInt16(input, index); + ushort val = ReadUInt16BigEndian(input[index..]); val -= keys[i]; - output[index] = (byte)(val >> 8); - output[index + 1] = (byte)val; + WriteUInt16BigEndian(output[index..], val); } AdvanceKeys(keys); } } - public static byte[] Encrypt(byte[] input, int start, int end, ushort[] keys) + public static byte[] Encrypt(ReadOnlySpan input, int start, int end, Span keys) { - var output = (byte[])input.Clone(); + var output = input.ToArray(); Encrypt(input, start, end, keys, output); return output; } - public static void Encrypt(byte[] input, int start, int end, ushort[] keys, byte[] output) + public static void Encrypt(ReadOnlySpan input, int start, int end, Span keys, Span output) { for (int ofs = start; ofs < end; ofs += 8) { for (int i = 0; i < keys.Length; i++) { var index = ofs + (i * 2); - ushort val = BigEndian.ToUInt16(input, index); + ushort val = ReadUInt16BigEndian(input[index..]); val += keys[i]; - output[index] = (byte)(val >> 8); - output[index + 1] = (byte)val; + WriteUInt16BigEndian(output[index..], val); } AdvanceKeys(keys); } } - private static void AdvanceKeys(ushort[] keys) + private static void AdvanceKeys(Span keys) { var k3 = keys[3] + 0x13; var k2 = keys[2] + 0x17; diff --git a/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeCrypto.cs b/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeCrypto.cs index 9292554e6..0afec52e0 100644 --- a/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeCrypto.cs +++ b/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeCrypto.cs @@ -1,5 +1,6 @@ using System; using System.Security.Cryptography; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -17,7 +18,7 @@ public static class MemeCrypto { private const uint POKE = 0x454B4F50; - public static bool VerifyMemePOKE(byte[] input, out byte[] output) + public static bool VerifyMemePOKE(ReadOnlySpan input, out byte[] output) { if (input.Length < 0x60) throw new ArgumentException("Invalid POKE buffer!"); @@ -25,10 +26,10 @@ public static bool VerifyMemePOKE(byte[] input, out byte[] output) var memeIndex = MemeKeyIndex.PokedexAndSaveFile; for (var i = input.Length - 8; i >= 0; i--) { - if (BitConverter.ToUInt32(input, i) != POKE) + if (ReadUInt32LittleEndian(input[i..]) != POKE) continue; - var keyIndex = BitConverter.ToInt32(input, i + 4); + var keyIndex = ReadInt32LittleEndian(input[(i+4)..]); if (!MemeKey.IsValidPokeKeyIndex(keyIndex)) continue; @@ -46,32 +47,32 @@ public static bool VerifyMemePOKE(byte[] input, out byte[] output) return true; } - output = input; + output = Array.Empty(); return false; } - public static bool VerifyMemeData(byte[] input, out byte[] output) + public static bool VerifyMemeData(ReadOnlySpan input, out byte[] output) { foreach (MemeKeyIndex keyIndex in Enum.GetValues(typeof(MemeKeyIndex))) { if (VerifyMemeData(input, out output, keyIndex)) return true; } - output = input; + output = Array.Empty(); return false; } - public static bool VerifyMemeData(byte[] input, out byte[] output, MemeKeyIndex keyIndex) + public static bool VerifyMemeData(ReadOnlySpan input, out byte[] output, MemeKeyIndex keyIndex) { if (input.Length < 0x60) { - output = input; + output = Array.Empty(); return false; } var key = new MemeKey(keyIndex); - output = (byte[])input.Clone(); + output = input.ToArray(); - var sigBuffer = key.RsaPublic(input.AsSpan(input.Length - 0x60)); + var sigBuffer = key.RsaPublic(input[^0x60..]); using var sha1 = SHA1.Create(); if (DecryptCompare(output, sigBuffer, key, sha1)) return true; @@ -79,52 +80,50 @@ public static bool VerifyMemeData(byte[] input, out byte[] output, MemeKeyIndex if (DecryptCompare(output, sigBuffer, key, sha1)) return true; - output = input; + output = Array.Empty(); return false; } private static bool DecryptCompare(byte[] output, ReadOnlySpan sigBuffer, MemeKey key, SHA1 sha1) { sigBuffer.CopyTo(output.AsSpan(output.Length - 0x60)); - key.AesDecrypt(output).CopyTo(output, 0); + key.AesDecrypt(output).CopyTo(output); // Check for 8-byte equality. var hash = sha1.ComputeHash(output, 0, output.Length - 0x8); - var computed = BitConverter.ToUInt64(hash, 0); - var existing = BitConverter.ToUInt64(output, output.Length - 0x8); + var computed = ReadUInt64LittleEndian(hash.AsSpan()); + var existing = ReadUInt64LittleEndian(output.AsSpan(output.Length - 0x8)); return computed == existing; } - public static bool VerifyMemeData(byte[] input, out byte[] output, int offset, int length) + public static bool VerifyMemeData(ReadOnlySpan input, out byte[] output, int offset, int length) { - var data = new byte[length]; - Array.Copy(input, offset, data, 0, length); + var data = input.Slice(offset, length).ToArray(); if (VerifyMemeData(data, out output)) { - var newOutput = (byte[])input.Clone(); + var newOutput = input.ToArray(); output.CopyTo(newOutput, offset); output = newOutput; return true; } - output = input; + output = Array.Empty(); return false; } - public static bool VerifyMemeData(byte[] input, out byte[] output, int offset, int length, MemeKeyIndex keyIndex) + public static bool VerifyMemeData(ReadOnlySpan input, out byte[] output, int offset, int length, MemeKeyIndex keyIndex) { - var data = new byte[length]; - Array.Copy(input, offset, data, 0, length); + var data = input.Slice(offset, length); if (VerifyMemeData(data, out output, keyIndex)) { - var newOutput = (byte[])input.Clone(); + var newOutput = input.ToArray(); output.CopyTo(newOutput, offset); output = newOutput; return true; } - output = input; + output = Array.Empty(); return false; } - public static byte[] SignMemeData(byte[] input, MemeKeyIndex keyIndex = MemeKeyIndex.PokedexAndSaveFile) + public static byte[] SignMemeData(ReadOnlySpan input, MemeKeyIndex keyIndex = MemeKeyIndex.PokedexAndSaveFile) { // Validate Input if (input.Length < 0x60) @@ -133,22 +132,21 @@ public static byte[] SignMemeData(byte[] input, MemeKeyIndex keyIndex = MemeKeyI if (!key.CanResign) throw new ArgumentException("Cannot sign with the specified memekey!"); - var output = (byte[])input.Clone(); + var output = input.ToArray(); // Copy in the SHA1 signature using (var sha1 = SHA1.Create()) { - var hash = sha1.ComputeHash(input, 0, input.Length - 8); - Array.Copy(hash, 0, output, output.Length - 8, 8); + var hash = sha1.ComputeHash(output, 0, output.Length - 8); + hash.AsSpan(0, 8).CopyTo(output.AsSpan(output.Length - 8, 8)); } // Perform AES operations output = key.AesEncrypt(output); - var sigBuffer = new byte[0x60]; - Array.Copy(output, output.Length - 0x60, sigBuffer, 0, 0x60); + var sigBuffer = output.AsSpan(output.Length - 0x60, 0x60); sigBuffer[0] &= 0x7F; - sigBuffer = key.RsaPrivate(sigBuffer); - sigBuffer.CopyTo(output, output.Length - 0x60); + var signed = key.RsaPrivate(sigBuffer); + signed.CopyTo(sigBuffer); return output; } @@ -157,7 +155,7 @@ public static byte[] SignMemeData(byte[] input, MemeKeyIndex keyIndex = MemeKeyI /// /// Save file data to resign /// The resigned save data. Invalid input returns null. - public static byte[] Resign7(byte[] sav7) + public static byte[] Resign7(ReadOnlySpan sav7) { if (sav7.Length is not (SaveUtil.SIZE_G7SM or SaveUtil.SIZE_G7USUM)) throw new ArgumentException("Should not be using this for unsupported saves."); @@ -169,19 +167,20 @@ public static byte[] Resign7(byte[] sav7) var ChecksumSignatureLength = isUSUM ? 0x150 : 0x140; const int MemeCryptoSignatureLength = 0x80; - var result = (byte[])sav7.Clone(); + var result = sav7.ToArray(); // Store current signature - var oldSig = sav7.Slice(MemeCryptoOffset, MemeCryptoSignatureLength); + var oldSig = sav7.Slice(MemeCryptoOffset, MemeCryptoSignatureLength).ToArray(); using var sha256 = SHA256.Create(); - var newSig = sha256.ComputeHash(sav7, ChecksumTableOffset, ChecksumSignatureLength); - Array.Resize(ref newSig, MemeCryptoSignatureLength); + var newSig = sha256.ComputeHash(result, ChecksumTableOffset, ChecksumSignatureLength); + Span sigSpan = stackalloc byte[MemeCryptoSignatureLength]; + newSig.CopyTo(sigSpan); if (VerifyMemeData(oldSig, out var memeSig, MemeKeyIndex.PokedexAndSaveFile)) - Buffer.BlockCopy(memeSig, 0x20, newSig, 0x20, 0x60); + memeSig.AsSpan()[0x20..0x80].CopyTo(sigSpan[0x20..]); - SignMemeData(newSig).CopyTo(result, MemeCryptoOffset); + SignMemeData(sigSpan).CopyTo(result, MemeCryptoOffset); return result; } } diff --git a/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeKey.cs b/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeKey.cs index f1631cff5..15d1add99 100644 --- a/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeKey.cs +++ b/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeKey.cs @@ -27,19 +27,17 @@ public sealed class MemeKey public MemeKey(MemeKeyIndex key) { DER = GetMemeData(key); - var _N = new byte[0x61]; - var _E = new byte[0x3]; - Array.Copy(DER, 0x18, _N, 0, 0x61); - Array.Copy(DER, 0x7B, _E, 0, 3); - Array.Reverse(_N); + var _N = DER.AsSpan(0x18, 0x61).ToArray(); + var _E = DER.AsSpan(0x7B, 3).ToArray(); + _N.AsSpan().Reverse(); + _E.AsSpan().Reverse(); N = new BigInteger(_N); - Array.Reverse(_E); E = new BigInteger(_E); if (key == MemeKeyIndex.PokedexAndSaveFile) { - var _D = (byte[])D_3.Clone(); - Array.Reverse(_D); + var _D = D_3.AsSpan().ToArray(); + _D.AsSpan().Reverse(); D = new BigInteger(_D); } else @@ -56,30 +54,27 @@ public MemeKey(MemeKeyIndex key) /// /// Get the AES key for this MemeKey /// - private byte[] GetAesKey(byte[] data) + private byte[] GetAesKey(ReadOnlySpan data) { if (data.Length < 0x60) throw new ArgumentException("Memebuffers must be atleast 0x60 bytes long!"); var buffer = new byte[DER.Length + data.Length - 0x60]; - Array.Copy(DER, 0, buffer, 0, DER.Length); - Array.Copy(data, 0, buffer, DER.Length, buffer.Length - DER.Length); + DER.CopyTo(buffer, 0); + data[..(buffer.Length - DER.Length)].CopyTo(buffer.AsSpan(DER.Length)); using var sha1 = SHA1.Create(); var result = sha1.ComputeHash(buffer); - var key = new byte[0x10]; - Array.Copy(result, 0, key, 0, 0x10); - return key; + return result.AsSpan(0, 0x10).ToArray(); } /// /// Performs Aes Decryption /// - internal byte[] AesDecrypt(byte[] input) + internal byte[] AesDecrypt(ReadOnlySpan input) { var key = GetAesKey(input); - var data = new byte[0x60]; - Array.Copy(input, input.Length - 0x60, data, 0, 0x60); + var data = input[^0x60..].ToArray(); var temp = new byte[0x10]; var curblock = new byte[0x10]; var outdata = new byte[data.Length]; @@ -98,47 +93,50 @@ internal byte[] AesDecrypt(byte[] input) // How can we derive subkey? // Well, (a ^ a) = 0. so (block first ^ subkey) ^ (block last ^ subkey) // = block first ^ block last ;) - Array.Copy(outdata, ((data.Length / 0x10) - 1) * 0x10, temp, 0, 0x10); + var last = outdata.AsSpan(((data.Length / 0x10) - 1) * 0x10, 0x10); + last.CopyTo(temp); temp = Xor(temp, outdata.AsSpan(0, 0x10)); - var subkey = GetSubKey(temp); + + Span subkey = stackalloc byte[0x10]; + GetSubKey(temp, subkey); for (var i = 0; i < data.Length / 0x10; i++) { - Array.Copy(outdata, 0x10 * i, curblock, 0, 0x10); + var slice = outdata.AsSpan(0x10 * i, 0x10); + slice.CopyTo(curblock); var temp1 = Xor(curblock, subkey); - Array.Copy(temp1, 0, outdata, 0x10 * i, 0x10); + temp1.AsSpan(0, 0x10).CopyTo(slice); } // Now we have Phase1Encrypt(buf). - Array.Clear(temp, 0, 0x10); // Clear to all zero + temp.AsSpan().Clear(); // Clear to all zero for (var i = 0; i < data.Length / 0x10; i++) // Phase 1: CBC Encryption. { - Array.Copy(outdata, i * 0x10, curblock, 0, 0x10); + var slice = outdata.AsSpan(0x10 * i, 0x10); + slice.CopyTo(curblock); var temp1 = AesEcbDecrypt(key, curblock); var temp2 = Xor(temp1, temp); - temp2.CopyTo(outdata, i * 0x10); + temp2.CopyTo(slice); curblock.CopyTo(temp, 0); } - var outbuf = (byte[]) input.Clone(); - Array.Copy(outdata, 0, outbuf, outbuf.Length - 0x60, 0x60); - + var outbuf = input.ToArray(); + outdata.AsSpan()[..0x60].CopyTo(outbuf.AsSpan()[^0x60..]); return outbuf; } /// /// Perform Aes Encryption /// - internal byte[] AesEncrypt(byte[] input) + internal byte[] AesEncrypt(ReadOnlySpan input) { var key = GetAesKey(input); - var data = new byte[0x60]; - Array.Copy(input, input.Length - 0x60, data, 0, 0x60); + var data = input[^0x60..]; var temp = new byte[0x10]; var curblock = new byte[0x10]; var outdata = new byte[data.Length]; for (var i = 0; i < data.Length / 0x10; i++) // Phase 1: CBC Encryption. { - Array.Copy(data, i * 0x10, curblock, 0, 0x10); + data.Slice(i * 0x10, 0x10).CopyTo(curblock); var temp1 = Xor(temp, curblock); temp = AesEcbEncrypt(key, temp1); temp.CopyTo(outdata, i * 0x10); @@ -147,29 +145,31 @@ internal byte[] AesEncrypt(byte[] input) // In between - CMAC stuff var inbet = outdata.AsSpan(0, 0x10); temp = Xor(temp, inbet); - var subkey = GetSubKey(temp); - Array.Clear(temp, 0, temp.Length); // Memcpy from an all-zero buffer + Span subkey = stackalloc byte[0x10]; + GetSubKey(temp, subkey); + + temp.AsSpan().Clear(); // Memcpy from an all-zero buffer for (var i = 0; i < data.Length / 0x10; i++) { var ofs = ((data.Length / 0x10) - 1 - i) * 0x10; - Array.Copy(outdata, ofs, curblock, 0, 0x10); + var slice = outdata.AsSpan(ofs, 0x10); + slice.CopyTo(curblock); byte[] temp2 = Xor(curblock, subkey); byte[] temp3 = AesEcbEncrypt(key, temp2); byte[] temp4 = Xor(temp3, temp); - Array.Copy(temp4, 0, outdata, ofs, 0x10); + temp4.CopyTo(slice); temp = temp2; } - var outbuf = (byte[])input.Clone(); - Array.Copy(outdata, 0, outbuf, outbuf.Length - 0x60, 0x60); + var outbuf = input.ToArray(); + outdata.AsSpan()[..0x60].CopyTo(outbuf.AsSpan()[^0x60..]); return outbuf; } - private static byte[] GetSubKey(byte[] temp) + private static void GetSubKey(ReadOnlySpan temp, Span subkey) { - var subkey = new byte[0x10]; for (var ofs = 0; ofs < 0x10; ofs += 2) // Imperfect ROL implementation { byte b1 = temp[ofs + 0], b2 = temp[ofs + 1]; @@ -180,7 +180,6 @@ private static byte[] GetSubKey(byte[] temp) } if ((temp[0] & 0x80) != 0) subkey[0xF] ^= 0x87; - return subkey; } private static byte[] Xor(byte[] b1, ReadOnlySpan b2) @@ -195,10 +194,10 @@ private static byte[] Xor(byte[] b1, ReadOnlySpan b2) /// /// Perform Rsa Decryption /// - internal byte[] RsaPrivate(byte[] data) + internal byte[] RsaPrivate(ReadOnlySpan data) { var _M = new byte[data.Length + 1]; - data.CopyTo(_M, 1); + data.CopyTo(_M.AsSpan(1)); Array.Reverse(_M); var M = new BigInteger(_M); diff --git a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlock.cs b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlock.cs index 92e6df442..ff1ba6f69 100644 --- a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlock.cs +++ b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlock.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.IO; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -80,9 +81,10 @@ public SCBlock Clone() { if (Data.Length == 0) return new SCBlock(Key, Type); + var clone = Data.AsSpan().ToArray(); if (SubType == 0) - return new SCBlock(Key, Type, (byte[]) Data.Clone()); - return new SCBlock(Key, (byte[])Data.Clone(), SubType); + return new SCBlock(Key, Type, clone); + return new SCBlock(Key, clone, SubType); } /// @@ -115,10 +117,10 @@ public void WriteBlock(BinaryWriter bw) /// Decrypted data /// Offset the block is to be read from (modified to offset by the amount of bytes consumed). /// New object containing all info for the block. - public static SCBlock ReadFromOffset(byte[] data, ref int offset) + public static SCBlock ReadFromOffset(ReadOnlySpan data, ref int offset) { // Create block, parse its key. - var key = BitConverter.ToUInt32(data, offset); + var key = ReadUInt32LittleEndian(data[offset..]); offset += 4; var xk = new SCXorShift32(key); @@ -137,25 +139,27 @@ public static SCBlock ReadFromOffset(byte[] data, ref int offset) case SCTypeCode.Object: // Cast raw bytes to Object { - var num_bytes = BitConverter.ToUInt32(data, offset) ^ xk.Next32(); + var num_bytes = ReadInt32LittleEndian(data[offset..]) ^ (int)xk.Next32(); offset += 4; - var arr = new byte[num_bytes]; + var arr = data.Slice(offset, num_bytes).ToArray(); + offset += num_bytes; for (int i = 0; i < arr.Length; i++) - arr[i] = (byte)(data[offset++] ^ xk.Next()); + arr[i] ^= (byte)xk.Next(); return new SCBlock(key, type, arr); } case SCTypeCode.Array: // Cast raw bytes to SubType[] { - var num_entries = BitConverter.ToUInt32(data, offset) ^ xk.Next32(); + var num_entries = ReadInt32LittleEndian(data[offset..]) ^ (int)xk.Next32(); offset += 4; var sub = (SCTypeCode)(data[offset++] ^ xk.Next()); var num_bytes = num_entries * sub.GetTypeSize(); - var arr = new byte[num_bytes]; + var arr = data.Slice(offset, num_bytes).ToArray(); + offset += num_bytes; for (int i = 0; i < arr.Length; i++) - arr[i] = (byte)(data[offset++] ^ xk.Next()); + arr[i] ^= (byte)xk.Next(); #if DEBUG Debug.Assert(sub > SCTypeCode.Array || Array.TrueForAll(arr, z => z <= 1)); #endif @@ -165,9 +169,10 @@ public static SCBlock ReadFromOffset(byte[] data, ref int offset) default: // Single Value Storage { var num_bytes = type.GetTypeSize(); - var arr = new byte[num_bytes]; + var arr = data.Slice(offset, num_bytes).ToArray(); + offset += num_bytes; for (int i = 0; i < arr.Length; i++) - arr[i] = (byte)(data[offset++] ^ xk.Next()); + arr[i] ^= (byte)xk.Next(); return new SCBlock(key, type, arr); } } diff --git a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlockCompare.cs b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlockCompare.cs index 86e27c1cd..99da38592 100644 --- a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlockCompare.cs +++ b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlockCompare.cs @@ -11,7 +11,7 @@ public sealed class SCBlockCompare private readonly List ValueChanged = new(); private readonly Dictionary KeyNames; - private string GetKeyName(uint key) => KeyNames.TryGetValue(key, out var val) ? val : $"{key:X8}"; + private string GetKeyName(uint key) => KeyNames.TryGetValue(key, out var value) ? value : $"{key:X8}"; public SCBlockCompare(SCBlockAccessor s1, SCBlockAccessor s2, IEnumerable extraKeyNames) { diff --git a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCTypeCode.cs b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCTypeCode.cs index 38ad9e359..6ab43434c 100644 --- a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCTypeCode.cs +++ b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCTypeCode.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -71,42 +72,42 @@ public static class SCTypeCodeExtensions _ => throw new ArgumentOutOfRangeException(nameof(type), type.ToString()), }; - public static object GetValue(this SCTypeCode type, byte[] data) + public static object GetValue(this SCTypeCode type, ReadOnlySpan data) { // don't use a switch expression here, we want to box our underlying type rather than the last type (double) switch (type) { case SCTypeCode.Byte: return data[0]; - case SCTypeCode.UInt16: return BitConverter.ToUInt16(data, 0); - case SCTypeCode.UInt32: return BitConverter.ToUInt32(data, 0); - case SCTypeCode.UInt64: return BitConverter.ToUInt64(data, 0); + case SCTypeCode.UInt16: return ReadUInt16LittleEndian(data); + case SCTypeCode.UInt32: return ReadUInt32LittleEndian(data); + case SCTypeCode.UInt64: return ReadUInt64LittleEndian(data); case SCTypeCode.SByte: return (sbyte) data[0]; - case SCTypeCode.Int16: return BitConverter.ToInt16(data, 0); - case SCTypeCode.Int32: return BitConverter.ToInt32(data, 0); - case SCTypeCode.Int64: return BitConverter.ToInt64(data, 0); - case SCTypeCode.Single: return BitConverter.ToSingle(data, 0); - case SCTypeCode.Double: return BitConverter.ToDouble(data, 0); + case SCTypeCode.Int16: return ReadInt16LittleEndian(data); + case SCTypeCode.Int32: return ReadInt32LittleEndian(data); + case SCTypeCode.Int64: return ReadInt64LittleEndian(data); + case SCTypeCode.Single: return ReadSingleLittleEndian(data); + case SCTypeCode.Double: return ReadDoubleLittleEndian(data); default: throw new ArgumentOutOfRangeException(nameof(type), type.ToString()); } } - public static void SetValue(this SCTypeCode type, byte[] data, object value) + public static void SetValue(this SCTypeCode type, Span data, object value) { switch (type) { case SCTypeCode.Byte: data[0] = (byte)value; break; - case SCTypeCode.UInt16: BitConverter.GetBytes((ushort)value).CopyTo(data, 0); break; - case SCTypeCode.UInt32: BitConverter.GetBytes((uint)value).CopyTo(data, 0); break; - case SCTypeCode.UInt64: BitConverter.GetBytes((ulong)value).CopyTo(data, 0); break; + case SCTypeCode.UInt16: WriteUInt16LittleEndian(data, (ushort)value); break; + case SCTypeCode.UInt32: WriteUInt32LittleEndian(data, (uint)value); break; + case SCTypeCode.UInt64: WriteUInt64LittleEndian(data, (ulong)value); break; case SCTypeCode.SByte: data[0] = (byte)value; break; - case SCTypeCode.Int16: BitConverter.GetBytes((short)value).CopyTo(data, 0); break; - case SCTypeCode.Int32: BitConverter.GetBytes((int)value).CopyTo(data, 0); break; - case SCTypeCode.Int64: BitConverter.GetBytes((long)value).CopyTo(data, 0); break; + case SCTypeCode.Int16: WriteInt16LittleEndian(data, (short)value); break; + case SCTypeCode.Int32: WriteInt32LittleEndian(data, (int)value); break; + case SCTypeCode.Int64: WriteInt64LittleEndian(data, (long)value); break; - case SCTypeCode.Single: BitConverter.GetBytes((float)value).CopyTo(data, 0); break; - case SCTypeCode.Double: BitConverter.GetBytes((double)value).CopyTo(data, 0); break; + case SCTypeCode.Single: WriteSingleLittleEndian(data, (float)value); break; + case SCTypeCode.Double: WriteDoubleLittleEndian(data, (double)value); break; default: throw new ArgumentOutOfRangeException(nameof(type), type.ToString()); diff --git a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCXorShift32.cs b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCXorShift32.cs index f7e2601c7..8612cee48 100644 --- a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCXorShift32.cs +++ b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCXorShift32.cs @@ -33,7 +33,7 @@ public SCXorShift32(uint seed) public uint Next() { var c = Counter; - var val = (Seed >> (c << 3)) & 0xFF; + var result = (Seed >> (c << 3)) & 0xFF; if (c == 3) { Seed = XorshiftAdvance(Seed); @@ -43,7 +43,7 @@ public uint Next() { ++Counter; } - return val; + return result; } /// diff --git a/PKHeX.Core/Saves/Encryption/SwishCrypto/SwishCrypto.cs b/PKHeX.Core/Saves/Encryption/SwishCrypto/SwishCrypto.cs index b4cc46a47..6447f476e 100644 --- a/PKHeX.Core/Saves/Encryption/SwishCrypto/SwishCrypto.cs +++ b/PKHeX.Core/Saves/Encryption/SwishCrypto/SwishCrypto.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Security.Cryptography; @@ -45,7 +46,7 @@ public static class SwishCrypto 0xA4, 0x48, 0xB3, 0x50, 0x9E, 0x14, 0xA0, 0x52, 0xDE, 0x7E, 0x10, 0x2B, 0x1B, 0x77, 0x6E, }; - public static void CryptStaticXorpadBytes(byte[] data) + public static void CryptStaticXorpadBytes(Span data) { var xp = StaticXorpad; for (var i = 0; i < data.Length - SIZE_HASH; i++) @@ -84,13 +85,8 @@ public static bool GetIsHashValid(byte[] data) return false; var hash = ComputeHash(data); - for (int i = 0; i < hash.Length; i++) - { - if (hash[i] != data[data.Length - SIZE_HASH + i]) - return false; - } - - return true; + var span = data.AsSpan()[^hash.Length..]; + return span.SequenceEqual(hash); } /// @@ -101,7 +97,7 @@ public static bool GetIsHashValid(byte[] data) /// /// Hash is assumed to be valid before calling this method. /// - public static IReadOnlyList Decrypt(byte[] data) + public static IReadOnlyList Decrypt(Span data) { CryptStaticXorpadBytes(data); return ReadBlocks(data); @@ -110,7 +106,7 @@ public static IReadOnlyList Decrypt(byte[] data) private const int BlockDataRatioEstimate1 = 777; // bytes per block, on average (generous) private const int BlockDataRatioEstimate2 = 555; // bytes per block, on average (stingy) - private static IReadOnlyList ReadBlocks(byte[] data) + private static IReadOnlyList ReadBlocks(ReadOnlySpan data) { var result = new List(data.Length / BlockDataRatioEstimate2); int offset = 0; diff --git a/PKHeX.Core/Saves/SAV1.cs b/PKHeX.Core/Saves/SAV1.cs index 2030cb79b..dc4d5bde1 100644 --- a/PKHeX.Core/Saves/SAV1.cs +++ b/PKHeX.Core/Saves/SAV1.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -101,13 +102,15 @@ private void Initialize(GameVersion versionOverride) pkDat.CopyTo(Data, dest); } - byte[] rawDC = new byte[0x38]; - Array.Copy(Data, Offsets.Daycare, rawDC, 0, rawDC.Length); + Span rawDC = stackalloc byte[0x38]; + Data.AsSpan(Offsets.Daycare, rawDC.Length).CopyTo(rawDC); byte[] TempDaycare = new byte[PokeList1.GetDataLength(PokeListType.Single, Japanese)]; TempDaycare[0] = rawDC[0]; - Array.Copy(rawDC, 1, TempDaycare, 2 + 1 + PokeCrypto.SIZE_1PARTY + StringLength, StringLength); - Array.Copy(rawDC, 1 + StringLength, TempDaycare, 2 + 1 + PokeCrypto.SIZE_1PARTY, StringLength); - Array.Copy(rawDC, 1 + (2 * StringLength), TempDaycare, 2 + 1, PokeCrypto.SIZE_1STORED); + + rawDC.Slice(1, StringLength).CopyTo(TempDaycare.AsSpan(2 + 1 + PokeCrypto.SIZE_1PARTY + StringLength)); + rawDC.Slice(1 + StringLength, StringLength).CopyTo(TempDaycare.AsSpan(2 + 1 + PokeCrypto.SIZE_1PARTY)); + rawDC.Slice(1 + (2 * StringLength), PokeCrypto.SIZE_1STORED).CopyTo(TempDaycare.AsSpan(2 + 1)); + PokeList1 daycareList = new(TempDaycare, PokeListType.Single, Japanese); daycareList.Write().CopyTo(Data, GetPartyOffset(7)); DaycareOffset = GetPartyOffset(7); @@ -161,18 +164,16 @@ protected override byte[] GetFinalData() partyPL.Write().CopyTo(Data, Offsets.Party); // Daycare is read-only, but in case it ever becomes editable, copy it back in. - byte[] rawDC = GetData(GetDaycareSlotOffset(loc: 0, slot: 0), SIZE_STORED); - byte[] dc = new byte[1 + (2 * StringLength) + PokeCrypto.SIZE_1STORED]; + Span rawDC = Data.AsSpan(GetDaycareSlotOffset(loc: 0, slot: 0), SIZE_STORED); + Span dc = stackalloc byte[1 + (2 * StringLength) + PokeCrypto.SIZE_1STORED]; dc[0] = IsDaycareOccupied(0, 0) == true ? (byte)1 : (byte)0; - Array.Copy(rawDC, 2 + 1 + PokeCrypto.SIZE_1PARTY + StringLength, dc, 1, StringLength); - Array.Copy(rawDC, 2 + 1 + PokeCrypto.SIZE_1PARTY, dc, 1 + StringLength, StringLength); - Array.Copy(rawDC, 2 + 1, dc, 1 + (2 * StringLength), PokeCrypto.SIZE_1STORED); - dc.CopyTo(Data, Offsets.Daycare); + 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))..]); + dc.CopyTo(Data.AsSpan(Offsets.Daycare)); SetChecksums(); - byte[] outData = new byte[Data.Length - SIZE_RESERVED]; - Array.Copy(Data, outData, outData.Length); - return outData; + return Data.AsSpan()[..^SIZE_RESERVED].ToArray(); } private int GetBoxRawDataOffset(int box) @@ -215,7 +216,7 @@ private int GetBoxRawDataOffset(int box) public override bool HasParty => true; private int StringLength => Japanese ? GBPKML.StringLengthJapanese : GBPKML.StringLengthNotJapan; - public override bool IsPKMPresent(byte[] data, int offset) => PKX.IsPKMPresentGB(data, offset); + public override bool IsPKMPresent(ReadOnlySpan data) => PKX.IsPKMPresentGB(data); // Checksums protected override void SetChecksums() => Data[Offsets.ChecksumOfs] = GetRBYChecksum(Offsets.OT, Offsets.ChecksumOfs); @@ -237,7 +238,7 @@ private byte GetRBYChecksum(int start, int end) public override string OT { get => GetString(Offsets.OT, OTLength); - set => SetString(value, OTLength).CopyTo(Data, Offsets.OT); + set => SetString(Data.AsSpan(Offsets.OT, OTLength), value.AsSpan(), OTLength, StringConverterOption.Clear50); } public Span OT_Trash { get => Data.AsSpan(Offsets.OT, StringLength); set { if (value.Length == StringLength) value.CopyTo(Data.AsSpan(Offsets.OT)); } } @@ -250,8 +251,8 @@ public override int Gender public override int TID { - get => BigEndian.ToUInt16(Data, Offsets.TID); - set => BigEndian.GetBytes((ushort)value).CopyTo(Data, Offsets.TID); + get => ReadUInt16BigEndian(Data.AsSpan(Offsets.TID)); + set => WriteUInt16BigEndian(Data.AsSpan(Offsets.TID), (ushort)value); } public override int SID { get => 0; set { } } @@ -259,7 +260,7 @@ public override int TID public string Rival { get => GetString(Offsets.Rival, OTLength); - set => SetString(value, OTLength).CopyTo(Data, Offsets.Rival); + set => SetString(Data.AsSpan(Offsets.Rival, OTLength), value.AsSpan(), OTLength, StringConverterOption.Clear50); } public Span Rival_Trash { get => Data.AsSpan(Offsets.Rival, StringLength); set { if (value.Length == StringLength) value.CopyTo(Data.AsSpan(Offsets.Rival)); } } @@ -275,7 +276,7 @@ public byte PikaFriendship public int PikaBeachScore { - get => BinaryCodedDecimal.ToInt32LE(Data, Offsets.PikaBeachScore, 2); + get => BinaryCodedDecimal.ToInt32LE(Data.AsSpan(Offsets.PikaBeachScore, 2)); set => BinaryCodedDecimal.WriteBytesLE(Data.AsSpan(Offsets.PikaBeachScore, 2), Math.Min(9999, value)); } @@ -361,7 +362,7 @@ public int TextSpeed public override uint Money { - get => (uint)BinaryCodedDecimal.ToInt32BE(Data, Offsets.Money, 3); + get => (uint)BinaryCodedDecimal.ToInt32BE(Data.AsSpan(Offsets.Money, 3)); set { value = (uint)Math.Min(value, MaxMoney); @@ -371,7 +372,7 @@ public override uint Money public uint Coin { - get => (uint)BinaryCodedDecimal.ToInt32BE(Data, Offsets.Coin, 2); + get => (uint)BinaryCodedDecimal.ToInt32BE(Data.AsSpan(Offsets.Coin, 2)); set { value = (ushort)Math.Min(value, MaxCoins); @@ -509,7 +510,7 @@ private void SetDexFlag(int region, int species, bool value) SetFlag(region + ofs, bit & 7, value); } - public override void WriteSlotFormatStored(PKM pkm, byte[] data, int offset) + public override void WriteSlotFormatStored(PKM pkm, Span data, int offset) { // pkm that have never been boxed have yet to save the 'current level' for box indication // set this value at this time @@ -517,7 +518,7 @@ public override void WriteSlotFormatStored(PKM pkm, byte[] data, int offset) base.WriteSlotFormatStored(pkm, Data, offset); } - public override void WriteBoxSlot(PKM pkm, byte[] data, int offset) + public override void WriteBoxSlot(PKM pkm, Span data, int offset) { // pkm that have never been boxed have yet to save the 'current level' for box indication // set this value at this time @@ -546,13 +547,11 @@ public bool[] EventSpawnFlags } } - public override string GetString(byte[] data, int offset, int length) => StringConverter12.GetString1(data, offset, length, Japanese); + public override string GetString(ReadOnlySpan data) => StringConverter12.GetString(data, Japanese); - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (PadToSize == 0) - PadToSize = maxLength + 1; - return StringConverter12.SetString1(value, maxLength, Japanese, PadToSize, (byte)PadWith); + return StringConverter12.SetString(destBuffer, value, maxLength, Japanese, option); } } } diff --git a/PKHeX.Core/Saves/SAV1Stadium.cs b/PKHeX.Core/Saves/SAV1Stadium.cs index c9b4643cf..2167916bd 100644 --- a/PKHeX.Core/Saves/SAV1Stadium.cs +++ b/PKHeX.Core/Saves/SAV1Stadium.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -76,7 +77,7 @@ protected override bool GetIsBoxChecksumValid(int box) var boxOfs = GetBoxOffset(box) - ListHeaderSize; var size = BoxSize - 2; var chk = Checksums.CheckSum16(new ReadOnlySpan(Data, boxOfs, size)); - var actual = BigEndian.ToUInt16(Data, boxOfs + size); + var actual = ReadUInt16BigEndian(Data.AsSpan(boxOfs + size)); return chk == actual; } @@ -85,7 +86,7 @@ protected override void SetBoxChecksum(int box) var boxOfs = GetBoxOffset(box) - ListHeaderSize; var size = BoxSize - 2; var chk = Checksums.CheckSum16(new ReadOnlySpan(Data, boxOfs, size)); - BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size); + WriteUInt16BigEndian(Data.AsSpan(boxOfs + size), chk); } protected override void SetBoxMetadata(int box) @@ -108,10 +109,13 @@ protected override void SetBoxMetadata(int box) protected override PKM GetPKM(byte[] data) { int len = StringLength; - var nick = data.Slice(PokeCrypto.SIZE_1STORED, len); - var ot = data.Slice(PokeCrypto.SIZE_1STORED + len, len); + var nick = data.AsSpan(PokeCrypto.SIZE_1STORED, len); + var ot = data.AsSpan(PokeCrypto.SIZE_1STORED + len, len); data = data.Slice(0, PokeCrypto.SIZE_1STORED); - return new PK1(data, Japanese) { OT_Trash = ot, Nickname_Trash = nick }; + var pk1 = new PK1(data, Japanese); + nick.CopyTo(pk1.RawNickname); + ot.CopyTo(pk1.RawOT); + return pk1; } public override byte[] GetDataForFormatStored(PKM pkm) @@ -202,7 +206,7 @@ public string GetTeamName(int team) if (string.IsNullOrWhiteSpace(str)) return name; var idOfs = ofs + (Japanese ? 0x8 : 0xC); - var id = BigEndian.ToUInt16(Data, idOfs); + var id = ReadUInt16BigEndian(Data.AsSpan(idOfs)); return $"{name} [{id:D5}:{str}]"; } @@ -248,7 +252,7 @@ public override SlotGroup GetTeam(int team) return new SlotGroup(name, members); } - public override void WriteSlotFormatStored(PKM pkm, byte[] data, int offset) + public override void WriteSlotFormatStored(PKM pkm, Span data, int offset) { // pkm that have never been boxed have yet to save the 'current level' for box indication // set this value at this time @@ -256,7 +260,7 @@ public override void WriteSlotFormatStored(PKM pkm, byte[] data, int offset) base.WriteSlotFormatStored(pkm, Data, offset); } - public override void WriteBoxSlot(PKM pkm, byte[] data, int offset) + public override void WriteBoxSlot(PKM pkm, Span data, int offset) { // pkm that have never been boxed have yet to save the 'current level' for box indication // set this value at this time @@ -264,7 +268,7 @@ public override void WriteBoxSlot(PKM pkm, byte[] data, int offset) base.WriteBoxSlot(pkm, Data, offset); } - public static bool IsStadium(byte[] data) + public static bool IsStadium(ReadOnlySpan data) { if (data.Length != SaveUtil.SIZE_G1STAD) return false; @@ -275,8 +279,8 @@ public static bool IsStadium(byte[] data) return false; } - private static bool IsStadiumU(byte[] data) => StadiumUtil.IsMagicPresentEither(data, TeamSizeU, FOOTER_MAGIC); - private static bool IsStadiumJ(byte[] data) => StadiumUtil.IsMagicPresentEither(data, TeamSizeJ, FOOTER_MAGIC); + private static bool IsStadiumU(ReadOnlySpan data) => StadiumUtil.IsMagicPresentEither(data, TeamSizeU, FOOTER_MAGIC); + private static bool IsStadiumJ(ReadOnlySpan data) => StadiumUtil.IsMagicPresentEither(data, TeamSizeJ, FOOTER_MAGIC); } public enum Stadium1TeamType diff --git a/PKHeX.Core/Saves/SAV1StadiumJ.cs b/PKHeX.Core/Saves/SAV1StadiumJ.cs index 75aa3736e..7939fe226 100644 --- a/PKHeX.Core/Saves/SAV1StadiumJ.cs +++ b/PKHeX.Core/Saves/SAV1StadiumJ.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -61,7 +62,7 @@ protected override bool GetIsBoxChecksumValid(int box) var boxOfs = GetBoxOffset(box) - ListHeaderSize; const int size = BoxSizeJ - 2; var chk = Checksums.CheckSum16(new ReadOnlySpan(Data, boxOfs, size)); - var actual = BigEndian.ToUInt16(Data, boxOfs + size); + var actual = ReadUInt16BigEndian(Data.AsSpan(boxOfs + size)); return chk == actual; } @@ -70,7 +71,7 @@ protected override void SetBoxChecksum(int box) var boxOfs = GetBoxOffset(box) - ListHeaderSize; const int size = BoxSizeJ - 2; var chk = Checksums.CheckSum16(new ReadOnlySpan(Data, boxOfs, size)); - BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size); + WriteUInt16BigEndian(Data.AsSpan(boxOfs + size), chk); } protected override void SetBoxMetadata(int box) @@ -81,10 +82,13 @@ protected override void SetBoxMetadata(int box) protected override PKM GetPKM(byte[] data) { const int len = StringLength; - var nick = data.Slice(0x21, len); - var ot = data.Slice(0x21 + len, len); + var nick = data.AsSpan(0x21, len); + var ot = data.AsSpan(0x21 + len, len); data = data.Slice(0, 0x21); - return new PK1(data, true) { OT_Trash = ot, Nickname_Trash = nick }; + var pk1 = new PK1(data, true); + nick.CopyTo(pk1.RawNickname); + ot.CopyTo(pk1.RawOT); + return pk1; } public override byte[] GetDataForFormatStored(PKM pkm) @@ -115,7 +119,7 @@ public string GetTeamName(int team) var str = GetString(ofs + 2, 5); if (string.IsNullOrWhiteSpace(str)) return name; - var id = BigEndian.ToUInt16(Data, ofs + 8); + var id = ReadUInt16BigEndian(Data.AsSpan(ofs + 8)); return $"{name} [{id:D5}:{str}]"; } @@ -135,7 +139,7 @@ public override SlotGroup GetTeam(int team) return new SlotGroup(name, members); } - public override void WriteSlotFormatStored(PKM pkm, byte[] data, int offset) + public override void WriteSlotFormatStored(PKM pkm, Span data, int offset) { // pkm that have never been boxed have yet to save the 'current level' for box indication // set this value at this time @@ -143,7 +147,7 @@ public override void WriteSlotFormatStored(PKM pkm, byte[] data, int offset) base.WriteSlotFormatStored(pkm, Data, offset); } - public override void WriteBoxSlot(PKM pkm, byte[] data, int offset) + public override void WriteBoxSlot(PKM pkm, Span data, int offset) { // pkm that have never been boxed have yet to save the 'current level' for box indication // set this value at this time @@ -151,7 +155,7 @@ public override void WriteBoxSlot(PKM pkm, byte[] data, int offset) base.WriteBoxSlot(pkm, Data, offset); } - public static bool IsStadium(byte[] data) + public static bool IsStadium(ReadOnlySpan data) { if (data.Length != SaveUtil.SIZE_G1STADJ) return false; diff --git a/PKHeX.Core/Saves/SAV2.cs b/PKHeX.Core/Saves/SAV2.cs index 03043e2c6..2d02fade1 100644 --- a/PKHeX.Core/Saves/SAV2.cs +++ b/PKHeX.Core/Saves/SAV2.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -145,15 +146,18 @@ private void Initialize() private PK2 ReadPKMFromOffset(int offset) { - byte[] nick = new byte[StringLength]; - byte[] ot = new byte[StringLength]; - byte[] pk = new byte[PokeCrypto.SIZE_2STORED]; + var stringLength = StringLength; + var span = Data.AsSpan(offset); - Array.Copy(Data, offset, nick, 0, nick.Length); offset += nick.Length; - Array.Copy(Data, offset, ot, 0, ot.Length); offset += ot.Length; - Array.Copy(Data, offset, pk, 0, pk.Length); + var pkData = span.Slice(stringLength * 2, PokeCrypto.SIZE_2STORED).ToArray(); + var pk = new PK2(pkData, jp: Japanese); - return new PK2(pk, jp: Japanese) { OT_Trash = ot, Nickname_Trash = nick }; + var nick = span[..stringLength]; + var ot = span.Slice(stringLength, stringLength); + nick.CopyTo(pk.RawNickname); + ot.CopyTo(pk.RawOT); + + return pk; } private const int SIZE_RESERVED = 0x8000; // unpacked box data @@ -201,28 +205,28 @@ protected override byte[] GetFinalData() { switch (Version) { - case GameVersion.GS: Array.Copy(Data, Offsets.Trainer1, Data, 0x7209, 0xC83); break; - case GameVersion.C: Array.Copy(Data, Offsets.Trainer1, Data, 0x7209, 0xADA); break; + case GameVersion.GS: Data.AsSpan(Offsets.Trainer1, 0xC83).CopyTo(Data.AsSpan(0x7209)); break; + case GameVersion.C: Data.AsSpan(Offsets.Trainer1, 0xADA).CopyTo(Data.AsSpan(0x7209)); break; } } else if (Korean) { // Calculate oddball checksum ushort sum = 0; - ushort[][] offsetpairs = + Span<(ushort, ushort)> offsetpairs = stackalloc (ushort,ushort)[] { - new ushort[] {0x106B, 0x1533}, - new ushort[] {0x1534, 0x1A12}, - new ushort[] {0x1A13, 0x1C38}, - new ushort[] {0x3DD8, 0x3F79}, - new ushort[] {0x7E39, 0x7E6A}, + (0x106B, 0x1533), + (0x1534, 0x1A12), + (0x1A13, 0x1C38), + (0x3DD8, 0x3F79), + (0x7E39, 0x7E6A), }; - foreach (ushort[] p in offsetpairs) + foreach (var p in offsetpairs) { - for (int i = p[0]; i < p[1]; i++) + for (int i = p.Item1; i < p.Item2; i++) sum += Data[i]; } - BitConverter.GetBytes(sum).CopyTo(Data, 0x7E6B); + WriteUInt16LittleEndian(Data.AsSpan(0x7E6B), sum); } else { @@ -266,7 +270,7 @@ protected override byte[] GetFinalData() public override int MaxMoney => 999999; public override int MaxCoins => 9999; - public override bool IsPKMPresent(byte[] data, int offset) => PKX.IsPKMPresentGB(data, offset); + public override bool IsPKMPresent(ReadOnlySpan data) => PKX.IsPKMPresentGB(data); protected override int EventConstMax => 0x100; protected override int EventFlagMax => 2000; @@ -296,8 +300,8 @@ private ushort GetChecksum() protected override void SetChecksums() { ushort accum = GetChecksum(); - BitConverter.GetBytes(accum).CopyTo(Data, Offsets.OverallChecksumPosition); - BitConverter.GetBytes(accum).CopyTo(Data, Offsets.OverallChecksumPosition2); + WriteUInt16LittleEndian(Data.AsSpan(Offsets.OverallChecksumPosition), accum); + WriteUInt16LittleEndian(Data.AsSpan(Offsets.OverallChecksumPosition2), accum); } public override bool ChecksumsValid @@ -305,7 +309,7 @@ public override bool ChecksumsValid get { ushort accum = GetChecksum(); - ushort actual = BitConverter.ToUInt16(Data, Offsets.OverallChecksumPosition); + ushort actual = ReadUInt16LittleEndian(Data.AsSpan(Offsets.OverallChecksumPosition)); return accum == actual; } } @@ -318,11 +322,11 @@ public override bool ChecksumsValid public override string OT { get => GetString(Offsets.Trainer1 + 2, (Korean ? 2 : 1) * OTLength); - set => SetString(value, (Korean ? 2 : 1) * OTLength).CopyTo(Data, Offsets.Trainer1 + 2); + set => SetString(Data.AsSpan(Offsets.Trainer1 + 2, (Korean ? 2 : 1) * OTLength), value.AsSpan(), 8, StringConverterOption.Clear50); } public Span OT_Trash - { + { get => Data.AsSpan(Offsets.Trainer1 + 2, StringLength); set { if (value.Length == StringLength) value.CopyTo(Data.AsSpan(Offsets.Trainer1 + 2)); } } @@ -330,7 +334,7 @@ public Span OT_Trash public string Rival { get => GetString(Offsets.Rival, (Korean ? 2 : 1) * OTLength); - set => SetString(value, (Korean ? 2 : 1) * OTLength).CopyTo(Data, Offsets.Rival); + set => SetString(Data.AsSpan(Offsets.Rival, (Korean ? 2 : 1) * OTLength), value.AsSpan(), 8, StringConverterOption.Clear50); } public Span Rival_Trash @@ -353,16 +357,16 @@ public override int Gender public override int TID { - get => BigEndian.ToUInt16(Data, Offsets.Trainer1); - set => BigEndian.GetBytes((ushort)value).CopyTo(Data, Offsets.Trainer1); + get => ReadUInt16BigEndian(Data.AsSpan(Offsets.Trainer1)); + set => WriteUInt16BigEndian(Data.AsSpan(Offsets.Trainer1), (ushort)value); } public override int SID { get => 0; set { } } public override int PlayedHours { - get => BigEndian.ToUInt16(Data, Offsets.TimePlayed); - set => BigEndian.GetBytes((ushort)value).CopyTo(Data, Offsets.TimePlayed); + get => ReadUInt16BigEndian(Data.AsSpan(Offsets.TimePlayed)); + set => WriteUInt16BigEndian(Data.AsSpan(Offsets.TimePlayed), (ushort)value); } public override int PlayedMinutes @@ -379,8 +383,8 @@ public override int PlayedSeconds public int Badges { - get => BitConverter.ToUInt16(Data, Offsets.JohtoBadges); - set { if (value < 0) return; BitConverter.GetBytes((ushort)value).CopyTo(Data, Offsets.JohtoBadges); } + get => ReadUInt16LittleEndian(Data.AsSpan(Offsets.JohtoBadges)); + set { if (value < 0) return; WriteUInt16LittleEndian(Data.AsSpan(Offsets.JohtoBadges), (ushort)value); } } private byte Options @@ -447,23 +451,25 @@ public bool MenuAccountOn set => Data[Offsets.Options + 5] = value ? (byte)1 : (byte)0; } + // 3 bytes public override uint Money { - get => BigEndian.ToUInt32(Data, Offsets.Money - 1) & 0xFFFFFF; + get => ReadUInt32BigEndian(Data.AsSpan(Offsets.Money)) >> 8; set { - byte[] data = BigEndian.GetBytes((uint) Math.Min(value, MaxMoney)); - Array.Copy(data, 1, Data, Offsets.Money, 3); + var clamp = (uint)Math.Min(value, MaxMoney); + var toWrite = (clamp << 8) | Data[Offsets.Money + 3]; + WriteUInt32BigEndian(Data.AsSpan(Offsets.Money), toWrite); } } public uint Coin { - get => BigEndian.ToUInt16(Data, Offsets.Money + 7); + get => ReadUInt16BigEndian(Data.AsSpan(Offsets.Money + 7)); set { - value = (ushort)Math.Min(value, MaxCoins); - BigEndian.GetBytes((ushort)value).CopyTo(Data, Offsets.Money + 7); + var clamped = (ushort)Math.Min(value, MaxCoins); + WriteUInt16BigEndian(Data.AsSpan(Offsets.Money + 7), clamped); } } @@ -544,8 +550,8 @@ public override string GetBoxName(int box) public override void SetBoxName(int box, string value) { int len = Korean ? 17 : 9; - var data = SetString(value, len, len, 0x50); - SetData(data, Offsets.BoxNames + (box * len)); + var span = Data.AsSpan(Offsets.BoxNames + (box * len), len); + SetString(span, value.AsSpan(), 8, StringConverterOption.Clear50); } protected override PKM GetPKM(byte[] data) @@ -685,7 +691,7 @@ public override ushort[] GetEventConsts() /// All Event Constant values for the save file /// These are all bytes - public override void SetEventConsts(ushort[] value) + public override void SetEventConsts(ReadOnlySpan value) { if (value.Length != EventConstMax) return; @@ -699,7 +705,7 @@ public override void SetEventConsts(ushort[] value) private ushort GetResetKey() { - var val = (TID >> 8) + (TID & 0xFF) + ((Money >> 16) & 0xFF) + ((Money >> 8) & 0xFF) + (Money & 0xFF); + var value = (TID >> 8) + (TID & 0xFF) + ((Money >> 16) & 0xFF) + ((Money >> 8) & 0xFF) + (Money & 0xFF); var ot = Data.AsSpan(Offsets.Trainer1 + 2, 5); var sum = 0; foreach (var b in ot) @@ -708,7 +714,7 @@ private ushort GetResetKey() break; sum += b; } - return (ushort)(val + sum); + return (ushort)(value + sum); } /// @@ -722,18 +728,18 @@ public void UnlockAllDecorations() SetEventFlag(i, true); } - public override string GetString(byte[] data, int offset, int length) + public override string GetString(ReadOnlySpan data) { if (Korean) - return StringConverter2KOR.GetString2KOR(data, offset, length); - return StringConverter12.GetString1(data, offset, length, Japanese); + return StringConverter2KOR.GetString(data); + return StringConverter12.GetString(data, Japanese); } - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { if (Korean) - return StringConverter2KOR.SetString2KOR(value, maxLength, PadToSize, (byte)PadWith); - return StringConverter12.SetString1(value, maxLength, Japanese, PadToSize, (byte)PadWith); + return StringConverter2KOR.SetString(value, destBuffer, maxLength, option); + return StringConverter12.SetString(destBuffer, value, maxLength, Japanese, option); } public bool IsGBMobileAvailable => Japanese && Version == GameVersion.C; diff --git a/PKHeX.Core/Saves/SAV2Stadium.cs b/PKHeX.Core/Saves/SAV2Stadium.cs index 132153a78..38446fe06 100644 --- a/PKHeX.Core/Saves/SAV2Stadium.cs +++ b/PKHeX.Core/Saves/SAV2Stadium.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -16,7 +17,7 @@ public sealed class SAV2Stadium : SAV_STADIUM public override IReadOnlyList HeldItems => Legal.HeldItems_GSC; public override GameVersion Version { get; protected set; } = GameVersion.Stadium2; - protected override SaveFile CloneInternal() => new SAV1Stadium((byte[])Data.Clone(), Japanese); + protected override SaveFile CloneInternal() => new SAV2Stadium((byte[])Data.Clone(), Japanese); public override int Generation => 2; private const int StringLength = 12; @@ -74,7 +75,7 @@ protected override bool GetIsBoxChecksumValid(int box) var boxOfs = GetBoxOffset(box) - ListHeaderSizeBox; var size = BoxSize - 2; var chk = Checksums.CheckSum16(new ReadOnlySpan(Data, boxOfs, size)); - var actual = BigEndian.ToUInt16(Data, boxOfs + size); + var actual = ReadUInt16BigEndian(Data.AsSpan(boxOfs + size)); return chk == actual; } @@ -97,7 +98,7 @@ protected override void SetBoxMetadata(int box) Data[boxOfs] = 1; Data[boxOfs + 1] = (byte)count; Data[boxOfs + 4] = StringConverter12.G1TerminatorCode; - StringConverter12.SetString1("1234", 4, Japanese).CopyTo(Data, boxOfs + 0x10); + StringConverter12.SetString(Data.AsSpan(boxOfs + 0x10, 4), "1234".AsSpan(), 4, Japanese, StringConverterOption.None); } else { @@ -110,7 +111,7 @@ protected override void SetBoxChecksum(int box) var boxOfs = GetBoxOffset(box) - ListHeaderSizeBox; var size = BoxSize - 2; var chk = Checksums.CheckSum16(new ReadOnlySpan(Data, boxOfs, size)); - BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size); + WriteUInt16BigEndian(Data.AsSpan(boxOfs + size), chk); } public static int GetTeamOffset(Stadium2TeamType type, int team) @@ -138,7 +139,7 @@ public string GetTeamName(int team) var str = GetString(ofs + 4, 7); if (string.IsNullOrWhiteSpace(str)) return name; - var id = BigEndian.ToUInt16(Data, ofs + 2); + var id = ReadUInt16BigEndian(Data.AsSpan(ofs + 2)); return $"{name} [{id:D5}:{str}]"; } @@ -174,7 +175,7 @@ public override int GetBoxOffset(int box) return BoxContinue + ListHeaderSizeBox + ((box - 1) * BoxSize); } - public static bool IsStadium(byte[] data) + public static bool IsStadium(ReadOnlySpan data) { if (data.Length != SaveUtil.SIZE_G2STAD) return false; @@ -182,7 +183,7 @@ public static bool IsStadium(byte[] data) } // Check Box 1's footer magic. - private static bool IsStadiumJ(byte[] data) => StadiumUtil.IsMagicPresentAbsolute(data, BoxStart + BoxSizeJ - ListFooterSize, MAGIC_FOOTER); + private static bool IsStadiumJ(ReadOnlySpan data) => StadiumUtil.IsMagicPresentAbsolute(data, BoxStart + BoxSizeJ - ListFooterSize, MAGIC_FOOTER); } public enum Stadium2TeamType diff --git a/PKHeX.Core/Saves/SAV3.cs b/PKHeX.Core/Saves/SAV3.cs index 86b796640..3f078e324 100644 --- a/PKHeX.Core/Saves/SAV3.cs +++ b/PKHeX.Core/Saves/SAV3.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -49,37 +51,37 @@ protected SAV3(byte[] data) : base(data) // OT name is the first 8 bytes of Small. The game fills any unused characters with 0xFF. // Japanese games are limited to 5 character OT names; INT 7 characters. +1 0xFF terminator. // Since JPN games don't touch the last 2 bytes (alignment), they end up as zeroes! - Japanese = BitConverter.ToInt16(Small, 0x6) == 0; + Japanese = ReadInt16LittleEndian(Small.AsSpan(0x6)) == 0; } - private void ReadSectors(byte[] data, int group) + private void ReadSectors(ReadOnlySpan data, int group) { int start = group * SIZE_MAIN; int end = start + SIZE_MAIN; for (int ofs = start; ofs < end; ofs += SIZE_SECTOR) { - var id = BitConverter.ToInt16(data, ofs + 0xFF4); + var id = ReadInt16LittleEndian(data[(ofs + 0xFF4)..]); switch (id) { - case >=5: Buffer.BlockCopy(data, ofs, Storage, (id - 5) * SIZE_SECTOR_USED, SIZE_SECTOR_USED); break; - case >=1: Buffer.BlockCopy(data, ofs, Large , (id - 1) * SIZE_SECTOR_USED, SIZE_SECTOR_USED); break; - default: Buffer.BlockCopy(data, ofs, Small , 0 , SIZE_SECTOR_USED); 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; } } } - private void WriteSectors(byte[] data, int group) + private void WriteSectors(Span data, int group) { int start = group * SIZE_MAIN; int end = start + SIZE_MAIN; for (int ofs = start; ofs < end; ofs += SIZE_SECTOR) { - var id = BitConverter.ToInt16(data, ofs + 0xFF4); + var id = ReadInt16LittleEndian(data[(ofs + 0xFF4)..]); switch (id) { - case >=5: Buffer.BlockCopy(Storage, (id - 5) * SIZE_SECTOR_USED, data, ofs, SIZE_SECTOR_USED); break; - case >=1: Buffer.BlockCopy(Large , (id - 1) * SIZE_SECTOR_USED, data, ofs, SIZE_SECTOR_USED); break; - default: Buffer.BlockCopy(Small , 0 , data, ofs, SIZE_SECTOR_USED); 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; } } } @@ -90,7 +92,7 @@ private void WriteSectors(byte[] data, int group) /// Data to check /// Which main to check (primary or secondary) /// Offset of the sector that has the small object data - public static bool IsAllMainSectorsPresent(byte[] data, int slot, out int sector0) + public static bool IsAllMainSectorsPresent(ReadOnlySpan data, int slot, out int sector0) { System.Diagnostics.Debug.Assert(slot is 0 or 1); int start = SIZE_MAIN * slot; @@ -99,7 +101,8 @@ public static bool IsAllMainSectorsPresent(byte[] data, int slot, out int sector sector0 = 0; for (int ofs = start; ofs < end; ofs += SIZE_SECTOR) { - var id = BitConverter.ToInt16(data, ofs + 0xFF4); + var span = data[ofs..]; + var id = ReadInt16LittleEndian(span[0xFF4..]); bitTrack |= (1 << id); if (id == 0) sector0 = ofs; @@ -108,7 +111,7 @@ public static bool IsAllMainSectorsPresent(byte[] data, int slot, out int sector return bitTrack == 0b_0011_1111_1111_1111; } - private static int GetActiveSlot(byte[] data) + private static int GetActiveSlot(ReadOnlySpan data) { if (data.Length == SaveUtil.SIZE_G3RAWHALF) return 0; @@ -120,8 +123,8 @@ private static int GetActiveSlot(byte[] data) if (!v1) return 0; - var count0 = BitConverter.ToUInt32(data, sectorZero0 + 0x0FFC); - var count1 = BitConverter.ToUInt32(data, sectorZero1 + 0x0FFC); + var count0 = ReadUInt32LittleEndian(data[(sectorZero0 + 0x0FFC)..]); + var count1 = ReadUInt32LittleEndian(data[(sectorZero1 + 0x0FFC)..]); // don't care about 32bit overflow. a 10 second save would take 1,000 years to overflow! return count1 > count0 ? 1 : 0; } @@ -157,7 +160,7 @@ protected sealed override byte[] GetFinalData() public sealed override bool HasParty => true; - public sealed override bool IsPKMPresent(byte[] data, int offset) => PKX.IsPKMPresentGBA(data, offset); + public sealed override bool IsPKMPresent(ReadOnlySpan data) => PKX.IsPKMPresentGBA(data); protected sealed override PKM GetPKM(byte[] data) => new PK3(data); protected sealed override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray3(data); @@ -174,8 +177,9 @@ protected sealed override void SetChecksums() int end = start + SIZE_MAIN; for (int ofs = start; ofs < end; ofs += SIZE_SECTOR) { - ushort chk = Checksums.CheckSum32(Data, ofs, SIZE_SECTOR_USED); - BitConverter.GetBytes(chk).CopyTo(Data, ofs + 0xFF6); + var sector = Data.AsSpan(ofs, SIZE_SECTOR); + ushort chk = Checksums.CheckSum32(sector[..SIZE_SECTOR_USED]); + WriteUInt16LittleEndian(sector[0xFF6..], chk); } if (State.BAK.Length < SaveUtil.SIZE_G3RAW) // don't update HoF for half-sizes @@ -183,12 +187,14 @@ protected sealed override void SetChecksums() // Hall of Fame Checksums { - ushort chk = Checksums.CheckSum32(Data, 0x1C000, SIZE_SECTOR_USED); - BitConverter.GetBytes(chk).CopyTo(Data, 0x1CFF4); + var sector2 = Data.AsSpan(0x1C000, SIZE_SECTOR); + ushort chk = Checksums.CheckSum32(sector2[..SIZE_SECTOR_USED]); + WriteUInt16LittleEndian(sector2[0xFF4..], chk); } { - ushort chk = Checksums.CheckSum32(Data, 0x1D000, SIZE_SECTOR_USED); - BitConverter.GetBytes(chk).CopyTo(Data, 0x1DFF4); + var sector2 = Data.AsSpan(0x1D000, SIZE_SECTOR); + ushort chk = Checksums.CheckSum32(sector2[..SIZE_SECTOR_USED]); + WriteUInt16LittleEndian(sector2[0xFF4..], chk); } } @@ -215,16 +221,18 @@ public sealed override bool ChecksumsValid private bool IsSectorValidExtra(int ofs) { - ushort chk = Checksums.CheckSum32(Data, ofs, SIZE_SECTOR_USED); - return chk == BitConverter.ToUInt16(Data, ofs + 0xFF4); + var sector = Data.AsSpan(ofs, SIZE_SECTOR); + ushort chk = Checksums.CheckSum32(sector[..SIZE_SECTOR_USED]); + return chk == ReadUInt16LittleEndian(sector[0xFF4..]); } - private bool IsSectorValid(int sector) + private bool IsSectorValid(int sectorIndex) { int start = ActiveSlot * SIZE_MAIN; - int ofs = start + (sector * SIZE_SECTOR); - ushort chk = Checksums.CheckSum32(Data, ofs, SIZE_SECTOR_USED); - return chk == BitConverter.ToUInt16(Data, ofs + 0xFF6); + int ofs = start + (sectorIndex * SIZE_SECTOR); + var sector = Data.AsSpan(ofs, SIZE_SECTOR); + ushort chk = Checksums.CheckSum32(sector[..SIZE_SECTOR_USED]); + return chk == ReadUInt16LittleEndian(sector[0xFF6..]); } public sealed override string ChecksumInfo @@ -253,11 +261,11 @@ public sealed override string ChecksumInfo public sealed override string OT { - get => GetString(Small, 0, 0x10); + get => GetString(Small.AsSpan(0, 8)); set { int len = Japanese ? 5 : OTLength; - SetString(value, len, PadToSize: len, PadWith: 0xFF).CopyTo(Small, 0); + SetString(Small.AsSpan(0, len), value.AsSpan(), len, StringConverterOption.ClearFF); } } @@ -269,20 +277,20 @@ public sealed override int Gender public sealed override int TID { - get => BitConverter.ToUInt16(Small, 0xA); - set => BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xA); + get => ReadUInt16LittleEndian(Small.AsSpan(0xA)); + set => WriteUInt16LittleEndian(Small.AsSpan(0xA), (ushort)value); } public sealed override int SID { - get => BitConverter.ToUInt16(Small, 0xC); - set => BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xC); + get => ReadUInt16LittleEndian(Small.AsSpan(0xC)); + set => WriteUInt16LittleEndian(Small.AsSpan(0xC), (ushort)value); } public sealed override int PlayedHours { - get => BitConverter.ToUInt16(Small, 0xE); - set => BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xE); + get => ReadUInt16LittleEndian(Small.AsSpan(0xE)); + set => WriteUInt16LittleEndian(Small.AsSpan(0xE), (ushort)value); } public sealed override int PlayedMinutes @@ -324,8 +332,8 @@ public sealed override void SetEventFlag(int flagNumber, bool value) 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 ushort GetEventConst(int index) => BitConverter.ToUInt16(Large, EventConst + (index * 2)); - public void SetEventConst(int index, ushort value) => BitConverter.GetBytes(value).CopyTo(Large, EventConst + (index * 2)); + public ushort GetEventConst(int index) => ReadUInt16LittleEndian(Large.AsSpan(EventConst + (index * 2))); + public void SetEventConst(int index, ushort value) => WriteUInt16LittleEndian(Large.AsSpan(EventConst + (index * 2)), value); public sealed override ushort[] GetEventConsts() { @@ -335,7 +343,7 @@ public sealed override ushort[] GetEventConsts() return Constants; } - public sealed override void SetEventConsts(ushort[] value) + public sealed override void SetEventConsts(ReadOnlySpan value) { if (value.Length != EventConstMax) return; @@ -388,9 +396,9 @@ public sealed override IReadOnlyList Inventory protected abstract int DaycareSlotSize { get; } - public sealed override uint? GetDaycareEXP(int loc, int slot) => BitConverter.ToUInt32(Large, GetDaycareEXPOffset(slot)); - public sealed override void SetDaycareEXP(int loc, int slot, uint EXP) => BitConverter.GetBytes(EXP).CopyTo(Large, GetDaycareEXPOffset(slot)); - public sealed override bool? IsDaycareOccupied(int loc, int slot) => IsPKMPresent(Large, GetDaycareSlotOffset(loc, slot)); + 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); @@ -437,13 +445,14 @@ protected sealed override int GetBoxWallpaperOffset(int box) public sealed override string GetBoxName(int box) { int offset = GetBoxOffset(COUNT_BOX); - return StringConverter3.GetString3(Storage, offset + (box * COUNT_BOXNAME), COUNT_BOXNAME, Japanese); + return StringConverter3.GetString(Storage.AsSpan(offset + (box * COUNT_BOXNAME), COUNT_BOXNAME), Japanese); } public sealed override void SetBoxName(int box, string value) { int offset = GetBoxOffset(COUNT_BOX); - SetString(value, COUNT_BOXNAME - 1).CopyTo(Storage, offset + (box * COUNT_BOXNAME)); + var dest = Storage.AsSpan(offset + (box * COUNT_BOXNAME), COUNT_BOXNAME); + SetString(dest, value.AsSpan(), COUNT_BOXNAME - 1, StringConverterOption.ClearZero); } #endregion @@ -469,8 +478,8 @@ protected sealed override void SetDex(PKM pkm) SetSeen(species, true); } - public uint DexPIDUnown { get => BitConverter.ToUInt32(Small, PokeDex + 0x4); set => BitConverter.GetBytes(value).CopyTo(Small, PokeDex + 0x4); } - public uint DexPIDSpinda { get => BitConverter.ToUInt32(Small, PokeDex + 0x8); set => BitConverter.GetBytes(value).CopyTo(Small, PokeDex + 0x8); } + public uint DexPIDUnown { get => ReadUInt32LittleEndian(Small.AsSpan(PokeDex + 0x4)); set => WriteUInt32LittleEndian(Small.AsSpan(PokeDex + 0x4), value); } + public uint DexPIDSpinda { get => ReadUInt32LittleEndian(Small.AsSpan(PokeDex + 0x8)); set => WriteUInt32LittleEndian(Small.AsSpan(PokeDex + 0x8), value); } public int DexUnownForm => PKX.GetUnownForm(DexPIDUnown); public sealed override bool GetCaught(int species) @@ -543,13 +552,11 @@ public byte PokedexNationalMagicFRLG public abstract bool NationalDex { get; set; } #endregion - public sealed override string GetString(byte[] data, int offset, int length) => StringConverter3.GetString3(data, offset, length, Japanese); + public sealed override string GetString(ReadOnlySpan data) => StringConverter3.GetString(data, Japanese); - public sealed override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public sealed override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (PadToSize == 0) - PadToSize = maxLength + 1; - return StringConverter3.SetString3(value, maxLength, Japanese, PadToSize, PadWith); + return StringConverter3.SetString(destBuffer, value, maxLength, Japanese, option); } protected abstract int MailOffset { get; } @@ -570,21 +577,22 @@ public byte[] GetHallOfFameData() { // HoF Data is split across two sectors byte[] data = new byte[SIZE_SECTOR_USED * 2]; - Buffer.BlockCopy(Data, 0x1C000, data, 0 , SIZE_SECTOR_USED); - Buffer.BlockCopy(Data, 0x1D000, data, SIZE_SECTOR_USED, SIZE_SECTOR_USED); + Data.AsSpan(0x1C000, SIZE_SECTOR_USED).CopyTo(data.AsSpan(0 , SIZE_SECTOR_USED)); + Data.AsSpan(0x1D000, SIZE_SECTOR_USED).CopyTo(data.AsSpan(SIZE_SECTOR_USED, SIZE_SECTOR_USED)); return data; } - public void SetHallOfFameData(byte[] value) + public void SetHallOfFameData(ReadOnlySpan value) { if (value.Length != SIZE_SECTOR_USED * 2) throw new ArgumentException("Invalid size", nameof(value)); // HoF Data is split across two sav sectors - Buffer.BlockCopy(value, 0 , Data, 0x1C000, SIZE_SECTOR_USED); - Buffer.BlockCopy(value, SIZE_SECTOR_USED, Data, 0x1D000, SIZE_SECTOR_USED); + Span savedata = Data; + value[..SIZE_SECTOR_USED].CopyTo(savedata[0x1C000..]); + value.Slice(SIZE_SECTOR_USED, SIZE_SECTOR_USED).CopyTo(savedata[0x1D000..]); } - public bool IsCorruptPokedexFF() => BitConverter.ToUInt64(Small, 0xAC) == ulong.MaxValue; + public bool IsCorruptPokedexFF() => MemoryMarshal.Read(Small.AsSpan(0xAC)) == ulong.MaxValue; public override void CopyChangesFrom(SaveFile sav) { @@ -601,14 +609,14 @@ public override void CopyChangesFrom(SaveFile sav) public uint ColosseumRaw1 { - get => BitConverter.ToUInt32(Large, ExternalEventData + 7); - set => SetData(Large, BitConverter.GetBytes(value), ExternalEventData + 7); + get => ReadUInt32LittleEndian(Large.AsSpan(ExternalEventData + 7)); + set => WriteUInt32LittleEndian(Large.AsSpan(ExternalEventData + 7), value); } public uint ColosseumRaw2 { - get => BitConverter.ToUInt32(Large, ExternalEventData + 11); - set => SetData(Large, BitConverter.GetBytes(value), ExternalEventData + 11); + get => ReadUInt32LittleEndian(Large.AsSpan(ExternalEventData + 11)); + set => WriteUInt32LittleEndian(Large.AsSpan(ExternalEventData + 11), value); } /// diff --git a/PKHeX.Core/Saves/SAV3Colosseum.cs b/PKHeX.Core/Saves/SAV3Colosseum.cs index 0935020e7..56964e5bb 100644 --- a/PKHeX.Core/Saves/SAV3Colosseum.cs +++ b/PKHeX.Core/Saves/SAV3Colosseum.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Security.Cryptography; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -74,7 +75,7 @@ private void InitializeData() for (int i = 0; i < SLOT_COUNT; i++) { int slotOffset = SLOT_START + (i * SLOT_SIZE); - int SaveCounter = BigEndian.ToInt32(Data, slotOffset + 4); + int SaveCounter = ReadInt32BigEndian(Data.AsSpan(slotOffset + 4)); if (SaveCounter <= SaveCount) continue; @@ -84,11 +85,10 @@ private void InitializeData() // Decrypt most recent save slot { - byte[] slot = new byte[SLOT_SIZE]; int slotOffset = SLOT_START + (SaveIndex * SLOT_SIZE); - Array.Copy(Data, slotOffset, slot, 0, slot.Length); - byte[] digest = new byte[20]; - Array.Copy(slot, SLOT_SIZE - 20, digest, 0, digest.Length); + ReadOnlySpan slot = Data.AsSpan(slotOffset, SLOT_SIZE); + Span digest = stackalloc byte[20]; + slot[^20..].CopyTo(digest); // Decrypt Slot Data = DecryptColosseum(slot, digest); @@ -113,8 +113,10 @@ private byte[] GetInnerData() SetChecksums(); // Get updated save slot data - byte[] digest = Data.Slice(Data.Length - 20, 20); - byte[] newSAV = EncryptColosseum(Data, digest); + ReadOnlySpan slot = Data; + Span digest = stackalloc byte[20]; + slot[^20..].CopyTo(digest); + byte[] newSAV = EncryptColosseum(slot, digest); // Put save slot back in original save data byte[] newFile = MemoryCard != null ? MemoryCard.ReadSaveGameData() : (byte[]) State.BAK.Clone(); @@ -150,80 +152,73 @@ protected override SaveFile CloneInternal() public override int MaxMoney => 9999999; public override int BoxCount => 3; - public override bool IsPKMPresent(byte[] data, int offset) => PKX.IsPKMPresentGC(data, offset); + public override bool IsPKMPresent(ReadOnlySpan data) => PKX.IsPKMPresentGC(data); - private static byte[] EncryptColosseum(byte[] input, byte[] digest) + private static byte[] EncryptColosseum(ReadOnlySpan input, Span digest) { if (input.Length != SLOT_SIZE) throw new ArgumentException(nameof(input)); - byte[] d = (byte[])input.Clone(); - byte[] k = (byte[])digest.Clone(); // digest + byte[] output = input.ToArray(); // NOT key for (int i = 0; i < 20; i++) - k[i] = (byte)~k[i]; + digest[i] = (byte)~digest[i]; using var sha1 = SHA1.Create(); for (int i = 0x18; i < 0x1DFD8; i += 20) { for (int j = 0; j < 20; j++) - d[i + j] ^= k[j]; - k = sha1.ComputeHash(d, i, 20); // update digest + output[i + j] ^= digest[j]; + byte[] key = sha1.ComputeHash(output, i, 20); // update digest + key.AsSpan(0, 20).CopyTo(digest); // for use in next loop } - return d; + return output; } - private static byte[] DecryptColosseum(byte[] input, byte[] digest) + private static byte[] DecryptColosseum(ReadOnlySpan input, Span digest) { if (input.Length != SLOT_SIZE) throw new ArgumentException(nameof(input)); - byte[] d = (byte[])input.Clone(); - byte[] k = (byte[])digest.Clone(); + byte[] output = input.ToArray(); // NOT key for (int i = 0; i < 20; i++) - k[i] = (byte)~k[i]; + digest[i] = (byte)~digest[i]; using var sha1 = SHA1.Create(); for (int i = 0x18; i < 0x1DFD8; i += 20) { - byte[] key = sha1.ComputeHash(d, i, 20); // update digest + byte[] key = sha1.ComputeHash(output, i, 20); // update digest for (int j = 0; j < 20; j++) - d[i + j] ^= k[j]; - Array.Copy(key, k, 20); // for use in next loop + output[i + j] ^= digest[j]; + key.AsSpan(0, 20).CopyTo(digest); // for use in next loop } - return d; + return output; } protected override void SetChecksums() { // Clear Header Checksum - BitConverter.GetBytes(0).CopyTo(Data, 12); + WriteInt32BigEndian(Data.AsSpan(12), 0); // Compute checksum of data using var sha1 = SHA1.Create(); byte[] checksum = sha1.ComputeHash(Data, 0, 0x1DFD8); // Set Checksum to end - Array.Copy(checksum, 0, Data, Data.Length - 20, 20); - - // Header Integrity - byte[] H = new byte[8]; Array.Copy(checksum, 0, H, 0, 8); - byte[] D = new byte[8]; Array.Copy(Data, 0x18, D, 0, 8); - // Decrypt Checksum - for (int i = 0; i < 8; i++) - D[i] ^= (byte)~H[i]; + var checkSpan = checksum.AsSpan(20); + checkSpan.CopyTo(Data.AsSpan(Data.Length - checkSpan.Length)); // Compute new header checksum + var header = Data.AsSpan(0, 0x20); int newHC = 0; for (int i = 0; i < 0x18; i += 4) - newHC -= BigEndian.ToInt32(Data, i); - - newHC -= BigEndian.ToInt32(D, 0); - newHC -= BigEndian.ToInt32(D, 4); + newHC -= ReadInt32BigEndian(header[i..]); + newHC -= ReadInt32BigEndian(header[0x18..]) ^ ~ReadInt32BigEndian(checkSpan); + newHC -= ReadInt32BigEndian(header[0x1C..]) ^ ~ReadInt32BigEndian(checkSpan[4..]); // Set Header Checksum - BigEndian.GetBytes(newHC).CopyTo(Data, 12); + WriteInt32BigEndian(Data.AsSpan(12), newHC); } public override bool ChecksumsValid => !ChecksumInfo.Contains("Invalid"); @@ -233,34 +228,29 @@ public override string ChecksumInfo get { byte[] data = (byte[])Data.Clone(); - int oldHC = BigEndian.ToInt32(data, 12); + var hc = data.AsSpan(12); + int oldHC = ReadInt32BigEndian(hc); // Clear Header Checksum - BitConverter.GetBytes(0).CopyTo(data, 12); + WriteUInt32BigEndian(hc, 0); using var sha1 = SHA1.Create(); byte[] checksum = sha1.ComputeHash(data, 0, 0x1DFD8); - // Header Integrity - byte[] H = new byte[8]; - byte[] D = new byte[8]; - - Array.Copy(checksum, 0, H, 0, H.Length); - Array.Copy(data, 0x00018, D, 0, D.Length); - for (int i = 0; i < 8; i++) - D[i] ^= (byte)~H[i]; + var checkSpan = checksum.AsSpan(20); // Compute new header checksum + var header = data.AsSpan(0, 0x20); int newHC = 0; for (int i = 0; i < 0x18; i += 4) - newHC -= BigEndian.ToInt32(data, i); + newHC -= ReadInt32BigEndian(header[i..]); - newHC -= BigEndian.ToInt32(D, 0); - newHC -= BigEndian.ToInt32(D, 4); + newHC -= ReadInt32BigEndian(header[0x18..]) ^ ~ReadInt32BigEndian(checkSpan); + newHC -= ReadInt32BigEndian(header[0x1C..]) ^ ~ReadInt32BigEndian(checkSpan[4..]); var chk = data.AsSpan(data.Length - 20, 20); - bool header = newHC == oldHC; - bool body = chk.SequenceEqual(checksum); + bool isHeaderValid = newHC == oldHC; + bool isBodyValid = chk.SequenceEqual(checkSpan); static string valid(bool s) => s ? "Valid" : "Invalid"; - return $"Header Checksum {valid(header)}, Body Checksum {valid(body)}."; + return $"Header Checksum {valid(isHeaderValid)}, Body Checksum {valid(isBodyValid)}."; } } @@ -285,7 +275,7 @@ public override string GetBoxName(int box) public override void SetBoxName(int box, string value) { - SetString(value, 8).CopyTo(Data, Box + (0x24A4 * box)); + SetString(Data.AsSpan(Box + (0x24A4 * box), 16), value.AsSpan(), 8, StringConverterOption.ClearZero); } protected override PKM GetPKM(byte[] data) @@ -337,8 +327,8 @@ protected override void SetDex(PKM pkm) private TimeSpan PlayedSpan { - get => TimeSpan.FromSeconds((double)(BigEndian.ToUInt32(Data, 40) - 0x47000000) / 128); - set => BigEndian.GetBytes((uint)(value.TotalSeconds * 128) + 0x47000000).CopyTo(Data, 40); + get => TimeSpan.FromSeconds((double)(ReadUInt32BigEndian(Data.AsSpan(40)) - 0x47000000) / 128); + set => WriteUInt32BigEndian(Data.AsSpan(40), (uint)(value.TotalSeconds * 128) + 0x47000000); } public override int PlayedHours @@ -360,15 +350,15 @@ public override int PlayedSeconds } // Trainer Info (offset 0x78, length 0xB18, end @ 0xB90) - public override string OT { get => GetString(0x78, 20); set { SetString(value, 10).CopyTo(Data, 0x78); OT2 = value; } } - public string OT2 { get => GetString(0x8C, 20); set => SetString(value, 10).CopyTo(Data, 0x8C); } - public override int SID { get => BigEndian.ToUInt16(Data, 0xA4); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0xA4); } - public override int TID { get => BigEndian.ToUInt16(Data, 0xA6); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0xA6); } + public override string OT { get => GetString(0x78, 20); set { SetString(Data.AsSpan(0x78, 20), value.AsSpan(), 10, StringConverterOption.ClearZero); OT2 = value; } } + public string OT2 { get => GetString(0x8C, 20); set => SetString(Data.AsSpan(0x8C, 20), value.AsSpan(), 10, StringConverterOption.ClearZero); } + public override int SID { get => ReadUInt16BigEndian(Data.AsSpan(0xA4)); set => WriteUInt16BigEndian(Data.AsSpan(0xA4), (ushort)value); } + public override int TID { get => ReadUInt16BigEndian(Data.AsSpan(0xA6)); set => WriteUInt16BigEndian(Data.AsSpan(0xA6), (ushort)value); } public override int Gender { get => Data[0xAF8]; set => Data[0xAF8] = (byte)value; } - public override uint Money { get => BigEndian.ToUInt32(Data, 0xAFC); set => BigEndian.GetBytes(value).CopyTo(Data, 0xAFC); } - public uint Coupons { get => BigEndian.ToUInt32(Data, 0xB00); set => BigEndian.GetBytes(value).CopyTo(Data, 0xB00); } - public string RUI_Name { get => GetString(0xB3A, 20); set => SetString(value, 10).CopyTo(Data, 0xB3A); } + 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(0xB3A, 20); set => SetString(Data.AsSpan(0xB3A, 20), value.AsSpan(), 10, StringConverterOption.ClearZero); } public override IReadOnlyList Inventory { @@ -399,13 +389,11 @@ public override IReadOnlyList Inventory public override void SetDaycareEXP(int loc, int slot, uint EXP) { } public override void SetDaycareOccupied(int loc, int slot, bool occupied) { } - public override string GetString(byte[] data, int offset, int length) => StringConverter3.GetBEString3(data, offset, length); + public override string GetString(ReadOnlySpan data) => StringConverter3GC.GetString(data); - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (PadToSize == 0) - PadToSize = maxLength + 1; - return StringConverter3.SetBEString3(value, maxLength, PadToSize, PadWith); + return StringConverter3GC.SetString(destBuffer, value, maxLength, option); } } } diff --git a/PKHeX.Core/Saves/SAV3E.cs b/PKHeX.Core/Saves/SAV3E.cs index 982210f25..ff134160d 100644 --- a/PKHeX.Core/Saves/SAV3E.cs +++ b/PKHeX.Core/Saves/SAV3E.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -53,8 +54,8 @@ public override bool NationalDex public override uint SecurityKey { - get => BitConverter.ToUInt32(Small, 0xAC); - set => SetData(Small, BitConverter.GetBytes(value), 0xAC); + get => ReadUInt32LittleEndian(Small.AsSpan(0xAC)); + set => WriteUInt32LittleEndian(Small.AsSpan(0xAC), value); } public RTC3 ClockInitial @@ -69,36 +70,36 @@ public RTC3 ClockElapsed set => SetData(Small, value.Data, 0xA0); } - public ushort JoyfulJumpInRow { get => BitConverter.ToUInt16(Small, 0x1FC); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0x1FC); } + public ushort JoyfulJumpInRow { get => ReadUInt16LittleEndian(Small.AsSpan(0x1FC)); set => WriteUInt16LittleEndian(Small.AsSpan(0x1FC), Math.Min((ushort)9999, value)); } // u16 field2; - public ushort JoyfulJump5InRow { get => BitConverter.ToUInt16(Small, 0x200); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0x200); } - public ushort JoyfulJumpGamesMaxPlayers { get => BitConverter.ToUInt16(Small, 0x202); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0x202); } + public ushort JoyfulJump5InRow { get => ReadUInt16LittleEndian(Small.AsSpan(0x200)); set => WriteUInt16LittleEndian(Small.AsSpan(0x200), Math.Min((ushort)9999, value)); } + public ushort JoyfulJumpGamesMaxPlayers { get => ReadUInt16LittleEndian(Small.AsSpan(0x202)); set => WriteUInt16LittleEndian(Small.AsSpan(0x202), Math.Min((ushort)9999, value)); } // u32 field8; - public uint JoyfulJumpScore { get => BitConverter.ToUInt16(Small, 0x208); set => SetData(Small, BitConverter.GetBytes(Math.Min( 9999, value)), 0x208); } + public uint JoyfulJumpScore { get => ReadUInt16LittleEndian(Small.AsSpan(0x208)); set => WriteUInt32LittleEndian(Small.AsSpan(0x208), Math.Min(9999, value)); } - public uint JoyfulBerriesScore { get => BitConverter.ToUInt16(Small, 0x20C); set => SetData(Small, BitConverter.GetBytes(Math.Min( 9999, value)), 0x20C); } - public ushort JoyfulBerriesInRow { get => BitConverter.ToUInt16(Small, 0x210); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0x210); } - public ushort JoyfulBerries5InRow { get => BitConverter.ToUInt16(Small, 0x212); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0x212); } + public uint JoyfulBerriesScore { get => ReadUInt16LittleEndian(Small.AsSpan(0x20C)); set => WriteUInt32LittleEndian(Small.AsSpan(0x20C), Math.Min(9999, value)); } + public ushort JoyfulBerriesInRow { get => ReadUInt16LittleEndian(Small.AsSpan(0x210)); set => WriteUInt16LittleEndian(Small.AsSpan(0x210), Math.Min((ushort)9999, value)); } + public ushort JoyfulBerries5InRow { get => ReadUInt16LittleEndian(Small.AsSpan(0x212)); set => WriteUInt16LittleEndian(Small.AsSpan(0x212), Math.Min((ushort)9999, value)); } public uint BP { - get => BitConverter.ToUInt16(Small, 0xEB8); + get => ReadUInt16LittleEndian(Small.AsSpan(0xEB8)); set { if (value > 9999) value = 9999; - BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xEB8); + WriteUInt16LittleEndian(Small.AsSpan(0xEB8), (ushort)value); } } public uint BPEarned { - get => BitConverter.ToUInt16(Small, 0xEBA); + get => ReadUInt16LittleEndian(Small.AsSpan(0xEBA)); set { if (value > 65535) value = 65535; - BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xEBA); + WriteUInt16LittleEndian(Small.AsSpan(0xEBA), (ushort)value); } } #endregion @@ -109,14 +110,14 @@ public uint BPEarned public override uint Money { - get => BitConverter.ToUInt32(Large, 0x0490) ^ SecurityKey; - set => SetData(Large, BitConverter.GetBytes(value ^ SecurityKey), 0x0490); + get => ReadUInt32LittleEndian(Large.AsSpan(0x0490)) ^ SecurityKey; + set => WriteUInt32LittleEndian(Large.AsSpan(0x0490), value ^ SecurityKey); } public override uint Coin { - get => (ushort)(BitConverter.ToUInt16(Large, 0x0494) ^ SecurityKey); - set => SetData(Large, BitConverter.GetBytes((ushort)(value ^ SecurityKey)), 0x0494); + get => (ushort)(ReadUInt16LittleEndian(Large.AsSpan(0x0494)) ^ SecurityKey); + set => WriteUInt16LittleEndian(Large.AsSpan(0x0494), (ushort)(value ^ SecurityKey)); } private const int OFS_PCItem = 0x0498; @@ -176,8 +177,8 @@ public int SwarmIndex protected override int MailOffset => 0x2BE0; protected override int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(0, slot + 1) - 4; // @ end of each pkm slot - public override string GetDaycareRNGSeed(int loc) => BitConverter.ToUInt32(Large, GetDaycareSlotOffset(0, 2)).ToString("X8"); // after the 2 slots, before the step counter - public override void SetDaycareRNGSeed(int loc, string seed) => BitConverter.GetBytes(Util.GetHexValue(seed)).CopyTo(Large, GetDaycareEXPOffset(2)); + 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 ExternalEventData => 0x31B3; @@ -186,9 +187,9 @@ public int SwarmIndex private const int SIZE_EBERRY = 0x134; public byte[] GetEReaderBerry() => Large.Slice(OFFSET_EBERRY, SIZE_EBERRY); - public void SetEReaderBerry(byte[] data) => SetData(Large, data, OFFSET_EBERRY); + public void SetEReaderBerry(ReadOnlySpan data) => data.CopyTo(Large.AsSpan(OFFSET_EBERRY)); - public override string EBerryName => GetString(Large, OFFSET_EBERRY, 7); + public override string EBerryName => GetString(Large.AsSpan(OFFSET_EBERRY, 7)); public override bool IsEBerryEngima => Large[OFFSET_EBERRY] is 0 or 0xFF; #endregion @@ -213,8 +214,8 @@ public override MysteryEvent3 MysteryEvent protected override int SeenOffset3 => 0x3B24; private const int Walda = 0x3D70; - public ushort WaldaBackgroundColor { get => BitConverter.ToUInt16(Large, Walda + 0); set => BitConverter.GetBytes(value).CopyTo(Large, Walda + 0); } - public ushort WaldaForegroundColor { get => BitConverter.ToUInt16(Large, Walda + 2); set => BitConverter.GetBytes(value).CopyTo(Large, Walda + 2); } + public ushort WaldaBackgroundColor { get => ReadUInt16LittleEndian(Large.AsSpan(Walda + 0)); set => WriteUInt16LittleEndian(Large.AsSpan(Walda + 0), value); } + public ushort WaldaForegroundColor { get => ReadUInt16LittleEndian(Large.AsSpan(Walda + 2)); set => WriteUInt16LittleEndian(Large.AsSpan(Walda + 2), value); } public byte WaldaIconID { get => Large[Walda + 0x14]; set => Large[Walda + 0x14] = value; } public byte WaldaPatternID { get => Large[Walda + 0x15]; set => Large[Walda + 0x15] = value; } public bool WaldaUnlocked { get => Large[Walda + 0x16] != 0; set => Large[Walda + 0x16] = (byte)(value ? 1 : 0); } @@ -222,7 +223,7 @@ public override MysteryEvent3 MysteryEvent private const uint EXTRADATA_SENTINEL = 0x0000B39D; private const int OFS_BV = 31 * 0x1000; // last sector of the save - public bool HasBattleVideo => Data.Length > SaveUtil.SIZE_G3RAWHALF && BitConverter.ToUInt32(Data, OFS_BV) == EXTRADATA_SENTINEL; + public bool HasBattleVideo => Data.Length > SaveUtil.SIZE_G3RAWHALF && ReadUInt32LittleEndian(Data.AsSpan(OFS_BV)) == EXTRADATA_SENTINEL; public BV3 BattleVideo { diff --git a/PKHeX.Core/Saves/SAV3FRLG.cs b/PKHeX.Core/Saves/SAV3FRLG.cs index d8eb558a7..7ccc116eb 100644 --- a/PKHeX.Core/Saves/SAV3FRLG.cs +++ b/PKHeX.Core/Saves/SAV3FRLG.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -30,7 +31,7 @@ public SAV3FRLG(byte[] data) : base(data) // Fix save files that have an overflow corruption with the Pokédex. // Future loads of this save file will cause it to be recognized as FR/LG correctly. if (IsCorruptPokedexFF()) - BitConverter.GetBytes(1u).CopyTo(Small, 0xAC); + WriteUInt32LittleEndian(Small.AsSpan(0xAC), 1); } private void Initialize() @@ -68,27 +69,27 @@ public override bool NationalDex } } - public ushort JoyfulJumpInRow { get => BitConverter.ToUInt16(Small, 0xB00); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0xB00); } + public ushort JoyfulJumpInRow { get => ReadUInt16LittleEndian(Small.AsSpan(0xB00)); set => WriteUInt16LittleEndian(Small.AsSpan(0xB00), Math.Min((ushort)9999, value)); } // u16 field2; - public ushort JoyfulJump5InRow { get => BitConverter.ToUInt16(Small, 0xB04); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0xB04); } - public ushort JoyfulJumpGamesMaxPlayers { get => BitConverter.ToUInt16(Small, 0xB06); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0xB06); } + public ushort JoyfulJump5InRow { get => ReadUInt16LittleEndian(Small.AsSpan(0xB04)); set => WriteUInt16LittleEndian(Small.AsSpan(0xB04), Math.Min((ushort)9999, value)); } + public ushort JoyfulJumpGamesMaxPlayers { get => ReadUInt16LittleEndian(Small.AsSpan(0xB06)); set => WriteUInt16LittleEndian(Small.AsSpan(0xB06), Math.Min((ushort)9999, value)); } // u32 field8; - public uint JoyfulJumpScore { get => BitConverter.ToUInt16(Small, 0xB0C); set => SetData(Small, BitConverter.GetBytes(Math.Min( 9999, value)), 0xB0C); } + public uint JoyfulJumpScore { get => ReadUInt16LittleEndian(Small.AsSpan(0xB0C)); set => WriteUInt32LittleEndian(Small.AsSpan(0xB0C), Math.Min(9999, value)); } - public uint JoyfulBerriesScore { get => BitConverter.ToUInt16(Small, 0xB10); set => SetData(Small, BitConverter.GetBytes(Math.Min( 9999, value)), 0xB10); } - public ushort JoyfulBerriesInRow { get => BitConverter.ToUInt16(Small, 0xB14); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0xB14); } - public ushort JoyfulBerries5InRow { get => BitConverter.ToUInt16(Small, 0xB16); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0xB16); } + public uint JoyfulBerriesScore { get => ReadUInt16LittleEndian(Small.AsSpan(0xB10)); set => WriteUInt32LittleEndian(Small.AsSpan(0xB10), Math.Min(9999, value)); } + public ushort JoyfulBerriesInRow { get => ReadUInt16LittleEndian(Small.AsSpan(0xB14)); set => WriteUInt16LittleEndian(Small.AsSpan(0xB14), Math.Min((ushort)9999, value)); } + public ushort JoyfulBerries5InRow { get => ReadUInt16LittleEndian(Small.AsSpan(0xB16)); set => WriteUInt16LittleEndian(Small.AsSpan(0xB16), Math.Min((ushort)9999, value)); } public uint BerryPowder { - get => BitConverter.ToUInt32(Small, 0xAF8) ^ SecurityKey; - set => SetData(Small, BitConverter.GetBytes(value ^ SecurityKey), 0xAF8); + get => ReadUInt32LittleEndian(Small.AsSpan(0xAF8)) ^ SecurityKey; + set => WriteUInt32LittleEndian(Small.AsSpan(0xAF8), value ^ SecurityKey); } public override uint SecurityKey { - get => BitConverter.ToUInt32(Small, 0xF20); - set => SetData(Small, BitConverter.GetBytes(value), 0xF20); + get => ReadUInt32LittleEndian(Small.AsSpan(0xF20)); + set => WriteUInt32LittleEndian(Small.AsSpan(0xF20), value); } #endregion @@ -98,14 +99,14 @@ public override uint SecurityKey public override uint Money { - get => BitConverter.ToUInt32(Large, 0x0290) ^ SecurityKey; - set => SetData(Large, BitConverter.GetBytes(value ^ SecurityKey), 0x0290); + get => ReadUInt32LittleEndian(Large.AsSpan(0x0290)) ^ SecurityKey; + set => WriteUInt32LittleEndian(Large.AsSpan(0x0290), value ^ SecurityKey); } public override uint Coin { - get => (ushort)(BitConverter.ToUInt16(Large, 0x0294) ^ SecurityKey); - set => SetData(Large, BitConverter.GetBytes((ushort)(value ^ SecurityKey)), 0x0294); + get => (ushort)(ReadUInt16LittleEndian(Large.AsSpan(0x0294)) ^ SecurityKey); + set => WriteUInt16LittleEndian(Large.AsSpan(0x0294), (ushort)(value ^ SecurityKey)); } private const int OFS_PCItem = 0x0298; @@ -134,8 +135,8 @@ protected override InventoryPouch3[] GetItems() protected override int MailOffset => 0x2CD0; protected override int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(0, slot + 1) - 4; // @ end of each pkm slot - public override string GetDaycareRNGSeed(int loc) => BitConverter.ToUInt16(Large, GetDaycareEXPOffset(2)).ToString("X4"); // after the 2nd slot EXP, before the step counter - public override void SetDaycareRNGSeed(int loc, string seed) => BitConverter.GetBytes((ushort)Util.GetHexValue(seed)).CopyTo(Large, GetDaycareEXPOffset(2)); + 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 ExternalEventData => 0x30A7; @@ -146,7 +147,7 @@ protected override InventoryPouch3[] GetItems() public byte[] GetEReaderBerry() => Large.Slice(OFFSET_EBERRY, SIZE_EBERRY); public void SetEReaderBerry(byte[] data) => SetData(Large, data, OFFSET_EBERRY); - public override string EBerryName => GetString(Large, OFFSET_EBERRY, 7); + public override string EBerryName => GetString(Large.AsSpan(OFFSET_EBERRY, 7)); public override bool IsEBerryEngima => Large[OFFSET_EBERRY] is 0 or 0xFF; #endregion @@ -172,8 +173,8 @@ public override MysteryEvent3 MysteryEvent public string RivalName { - get => GetString(Large, 0x3A4C, 8); - set => SetData(Large, SetString(value, 7), 0x3A4C); + get => GetString(Large.AsSpan(0x3A4C, 8)); + set => SetString(Large.AsSpan(0x3A4C, 8), value.AsSpan(), 7, StringConverterOption.ClearZero); } #endregion diff --git a/PKHeX.Core/Saves/SAV3GCMemoryCard.cs b/PKHeX.Core/Saves/SAV3GCMemoryCard.cs index 0c3198b38..42deaef95 100644 --- a/PKHeX.Core/Saves/SAV3GCMemoryCard.cs +++ b/PKHeX.Core/Saves/SAV3GCMemoryCard.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -49,11 +50,11 @@ public sealed class SAV3GCMemoryCard public static bool IsMemoryCardSize(long Size) => ValidMemoryCardSizes.Contains((int)Size); - public static bool IsMemoryCardSize(byte[] Data) + public static bool IsMemoryCardSize(ReadOnlySpan Data) { if (!IsMemoryCardSize(Data.Length)) return false; // bad size - if (BitConverter.ToUInt64(Data, 0) == ulong.MaxValue) + if (ReadUInt64BigEndian(Data) == ulong.MaxValue) return false; // uninitialized return true; } @@ -81,9 +82,9 @@ private void GetChecksum(int block, int offset, int length, out ushort csum, out for (int i = 0; i < length; i++) { - var val = BigEndian.ToUInt16(Data, ofs + (i * 2)); - csum += val; - inv_csum += (ushort)~val; + var value = ReadUInt16BigEndian(Data.AsSpan(ofs + (i * 2))); + csum += value; + inv_csum += (ushort)~value; } if (csum == 0xffff) csum = 0; @@ -119,28 +120,28 @@ private GCChecksumFlag VerifyChecksums() } // Structure - private int Header_Size => BigEndian.ToUInt16(Data, Header + 0x0022); - private ushort Header_Checksum => BigEndian.ToUInt16(Data, Header + 0x01fc); - private ushort Header_Checksum_Inv => BigEndian.ToUInt16(Data, Header + 0x01fe); + private int Header_Size => ReadUInt16BigEndian(Data.AsSpan(Header + 0x0022)); + private ushort Header_Checksum => ReadUInt16BigEndian(Data.AsSpan(Header + 0x01fc)); + private ushort Header_Checksum_Inv => ReadUInt16BigEndian(Data.AsSpan(Header + 0x01fe)); // Encoding (Windows-1252 or Shift JIS) - private int Header_Encoding => BigEndian.ToUInt16(Data, Header + 0x0024); + private int Header_Encoding => ReadUInt16BigEndian(Data.AsSpan(Header + 0x0024)); private bool Header_Japanese => Header_Encoding == 1; private Encoding EncodingType => Header_Japanese ? Encoding.GetEncoding(1252) : Encoding.GetEncoding(932); - private int Directory_UpdateCounter => BigEndian.ToUInt16(Data, Directory + 0x1ffa); - private int Directory_Checksum => BigEndian.ToUInt16(Data, Directory + 0x1ffc); - private int Directory_Checksum_Inv => BigEndian.ToUInt16(Data, Directory + 0x1ffe); + private int Directory_UpdateCounter => ReadUInt16BigEndian(Data.AsSpan(Directory + 0x1ffa)); + private int Directory_Checksum => ReadUInt16BigEndian(Data.AsSpan(Directory + 0x1ffc)); + private int Directory_Checksum_Inv => ReadUInt16BigEndian(Data.AsSpan(Directory + 0x1ffe)); - private int DirectoryBAK_UpdateCounter => BigEndian.ToUInt16(Data, DirectoryBAK + 0x1ffa); - private int DirectoryBAK_Checksum => BigEndian.ToUInt16(Data, DirectoryBAK + 0x1ffc); - private int DirectoryBAK_Checksum_Inv => BigEndian.ToUInt16(Data, DirectoryBAK + 0x1ffe); + private int DirectoryBAK_UpdateCounter => ReadUInt16BigEndian(Data.AsSpan(DirectoryBAK + 0x1ffa)); + private int DirectoryBAK_Checksum => ReadUInt16BigEndian(Data.AsSpan(DirectoryBAK + 0x1ffc)); + private int DirectoryBAK_Checksum_Inv => ReadUInt16BigEndian(Data.AsSpan(DirectoryBAK + 0x1ffe)); - private int BlockAlloc_Checksum => BigEndian.ToUInt16(Data, BlockAlloc + 0x0000); - private int BlockAlloc_Checksum_Inv => BigEndian.ToUInt16(Data, BlockAlloc + 0x0002); + private int BlockAlloc_Checksum => ReadUInt16BigEndian(Data.AsSpan(BlockAlloc + 0x0000)); + private int BlockAlloc_Checksum_Inv => ReadUInt16BigEndian(Data.AsSpan(BlockAlloc + 0x0002)); - private int BlockAllocBAK_Checksum => BigEndian.ToUInt16(Data, BlockAllocBAK + 0x0000); - private int BlockAllocBAK_Checksum_Inv => BigEndian.ToUInt16(Data, BlockAllocBAK + 0x0002); + private int BlockAllocBAK_Checksum => ReadUInt16BigEndian(Data.AsSpan(BlockAllocBAK + 0x0000)); + private int BlockAllocBAK_Checksum_Inv => ReadUInt16BigEndian(Data.AsSpan(BlockAllocBAK + 0x0002)); private int DirectoryBlock_Used; private int NumBlocks => (Data.Length/BLOCK_SIZE) - 5; @@ -222,11 +223,11 @@ public GCMemoryCardState GetMemoryCardState() for (int i = 0; i < NumEntries_Directory; i++) { int offset = (DirectoryBlock_Used * BLOCK_SIZE) + (i * DENTRY_SIZE); - if (BitConverter.ToUInt32(Data, offset) == uint.MaxValue) // empty entry + if (ReadUInt32BigEndian(Data.AsSpan(offset)) == uint.MaxValue) // empty entry continue; - int FirstBlock = BigEndian.ToUInt16(Data, offset + 0x36); - int BlockCount = BigEndian.ToUInt16(Data, offset + 0x38); + int FirstBlock = ReadUInt16BigEndian(Data.AsSpan(offset + 0x36)); + int BlockCount = ReadUInt16BigEndian(Data.AsSpan(offset + 0x38)); // Memory card directory contains info for deleted files with boundaries beyond memory card size, ignore if (FirstBlock + BlockCount > NumBlocks) @@ -318,32 +319,42 @@ private string GCISaveGameName() public byte[] ReadSaveGameData() { - if (EntrySelected < 0) + var entry = EntrySelected; + if (entry < 0) return Array.Empty(); // No entry selected - - int offset = (DirectoryBlock_Used * BLOCK_SIZE) + (EntrySelected * DENTRY_SIZE); - int FirstBlock = BigEndian.ToUInt16(Data, offset + 0x36); - int BlockCount = BigEndian.ToUInt16(Data, offset + 0x38); - - byte[] SaveData = new byte[BlockCount * BLOCK_SIZE]; - Array.Copy(Data, FirstBlock * BLOCK_SIZE, SaveData, 0, BlockCount * BLOCK_SIZE); - - return SaveData; + return ReadSaveGameData(entry); } - public void WriteSaveGameData(byte[] SaveData) + private byte[] ReadSaveGameData(int entry) { - if (EntrySelected < 0) // Can't write anywhere + int offset = (DirectoryBlock_Used * BLOCK_SIZE) + (entry * DENTRY_SIZE); + var span = Data.AsSpan(offset); + int blockFirst = ReadUInt16BigEndian(span[0x36..]); + int blockCount = ReadUInt16BigEndian(span[0x38..]); + + return Data.AsSpan(blockFirst * BLOCK_SIZE, blockCount * BLOCK_SIZE).ToArray(); + } + + public void WriteSaveGameData(ReadOnlySpan data) + { + var entry = EntrySelected; + if (entry < 0) // Can't write anywhere + return; + WriteSaveGameData(data, entry); + } + + private void WriteSaveGameData(ReadOnlySpan data, int entry) + { + int offset = (DirectoryBlock_Used * BLOCK_SIZE) + (entry * DENTRY_SIZE); + var span = Data.AsSpan(offset); + int blockFirst = ReadUInt16BigEndian(span[0x36..]); + int blockCount = ReadUInt16BigEndian(span[0x38..]); + + if (data.Length != blockCount * BLOCK_SIZE) // Invalid File Size return; - int offset = (DirectoryBlock_Used * BLOCK_SIZE) + (EntrySelected * DENTRY_SIZE); - int FirstBlock = BigEndian.ToUInt16(Data, offset + 0x36); - int BlockCount = BigEndian.ToUInt16(Data, offset + 0x38); - - if (SaveData.Length != BlockCount * BLOCK_SIZE) // Invalid File Size - return; - - Array.Copy(SaveData, 0, Data, FirstBlock * BLOCK_SIZE, BlockCount * BLOCK_SIZE); + var dest = Data.AsSpan(blockFirst * BLOCK_SIZE); + data[..(blockCount * BLOCK_SIZE)].CopyTo(dest); } } } diff --git a/PKHeX.Core/Saves/SAV3RS.cs b/PKHeX.Core/Saves/SAV3RS.cs index 5305eeb41..f185f2bbe 100644 --- a/PKHeX.Core/Saves/SAV3RS.cs +++ b/PKHeX.Core/Saves/SAV3RS.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -72,14 +73,14 @@ public RTC3 ClockElapsed public override uint Money { - get => BitConverter.ToUInt32(Large, 0x0490); - set => SetData(Large, BitConverter.GetBytes(value), 0x0490); + get => ReadUInt32LittleEndian(Large.AsSpan(0x0490)); + set => WriteUInt32LittleEndian(Large.AsSpan(0x0490), value); } public override uint Coin { - get => BitConverter.ToUInt16(Large, 0x0494); - set => SetData(Large, BitConverter.GetBytes((ushort)value), 0x0494); + get => ReadUInt16LittleEndian(Large.AsSpan(0x0494)); + set => WriteUInt16LittleEndian(Large.AsSpan(0x0494), (ushort)(value)); } private const int OFS_PCItem = 0x0498; @@ -139,8 +140,8 @@ public int SwarmIndex 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) => BitConverter.ToUInt16(Large, GetDaycareEXPOffset(2)).ToString("X4"); - public override void SetDaycareRNGSeed(int loc, string seed) => BitConverter.GetBytes((ushort)Util.GetHexValue(seed)).CopyTo(Large, GetDaycareEXPOffset(2)); + 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 ExternalEventData => 0x311B; @@ -149,9 +150,9 @@ public int SwarmIndex private const int SIZE_EBERRY = 0x530; public byte[] GetEReaderBerry() => Large.Slice(OFFSET_EBERRY, SIZE_EBERRY); - public void SetEReaderBerry(byte[] data) => SetData(Large, data, OFFSET_EBERRY); + public void SetEReaderBerry(ReadOnlySpan data) => data.CopyTo(Large.AsSpan(OFFSET_EBERRY)); - public override string EBerryName => GetString(Large, OFFSET_EBERRY, 7); + public override string EBerryName => GetString(Large.AsSpan(OFFSET_EBERRY, 7)); public override bool IsEBerryEngima => Large[OFFSET_EBERRY] is 0 or 0xFF; #endregion diff --git a/PKHeX.Core/Saves/SAV3RSBox.cs b/PKHeX.Core/Saves/SAV3RSBox.cs index a7ef71a74..ecdb7432f 100644 --- a/PKHeX.Core/Saves/SAV3RSBox.cs +++ b/PKHeX.Core/Saves/SAV3RSBox.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -122,7 +123,7 @@ protected override SaveFile CloneInternal() public override int BoxCount => 50; public override bool HasParty => false; - public override bool IsPKMPresent(byte[] data, int offset) => PKX.IsPKMPresentGBA(data, offset); + public override bool IsPKMPresent(ReadOnlySpan data) => PKX.IsPKMPresentGBA(data); // Checksums protected override void SetChecksums() => Blocks.SetChecksums(Data); @@ -168,8 +169,13 @@ public override string GetBoxName(int box) public override void SetBoxName(int box, string value) { int offset = Box + 0x1EC38 + (9 * box); - byte[] data = value == $"BOX {box + 1}" ? new byte[9] : SetString(value, 8); - SetData(data, offset); + var span = Data.AsSpan(offset, 9); + if (value == $"BOX {box + 1}") + { + span.Clear(); + return; + } + SetString(span, value.AsSpan(), 8, StringConverterOption.ClearZero); } protected override PKM GetPKM(byte[] data) @@ -188,20 +194,18 @@ protected override byte[] DecryptPKM(byte[] data) protected override void SetDex(PKM pkm) { /* No Pokedex for this game, do nothing */ } - public override void WriteBoxSlot(PKM pkm, byte[] data, int offset) + public override void WriteBoxSlot(PKM pkm, Span data, int offset) { base.WriteBoxSlot(pkm, data, offset); - BitConverter.GetBytes((ushort)pkm.TID).CopyTo(data, offset + PokeCrypto.SIZE_3STORED + 0); - BitConverter.GetBytes((ushort)pkm.SID).CopyTo(data, offset + PokeCrypto.SIZE_3STORED + 2); + WriteUInt16LittleEndian(data[(PokeCrypto.SIZE_3STORED)..], (ushort)pkm.TID); + WriteUInt16LittleEndian(data[(PokeCrypto.SIZE_3STORED + 2)..], (ushort)pkm.SID); } - public override string GetString(byte[] data, int offset, int length) => StringConverter3.GetString3(data, offset, length, Japanese); + public override string GetString(ReadOnlySpan data) => StringConverter3.GetString(data, Japanese); - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (PadToSize == 0) - PadToSize = maxLength + 1; - return StringConverter3.SetString3(value, maxLength, Japanese, PadToSize, PadWith); + return StringConverter3.SetString(destBuffer, value, maxLength, Japanese, option); } } } diff --git a/PKHeX.Core/Saves/SAV3XD.cs b/PKHeX.Core/Saves/SAV3XD.cs index 73dd9f639..5eab5917d 100644 --- a/PKHeX.Core/Saves/SAV3XD.cs +++ b/PKHeX.Core/Saves/SAV3XD.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -53,7 +54,7 @@ private void InitializeData(out StrategyMemo memo, out ShadowInfoTableXD info) for (int i = 0; i < SLOT_COUNT; i++) { int slotOffset = SLOT_START + (i * SLOT_SIZE); - int SaveCounter = BigEndian.ToInt32(Data, slotOffset + 4); + int SaveCounter = ReadInt32BigEndian(Data.AsSpan(slotOffset + 4)); if (SaveCounter <= SaveCount) continue; @@ -63,24 +64,22 @@ private void InitializeData(out StrategyMemo memo, out ShadowInfoTableXD info) // Decrypt most recent save slot { - byte[] slot = new byte[SLOT_SIZE]; int slotOffset = SLOT_START + (SaveIndex * SLOT_SIZE); - Array.Copy(Data, slotOffset, slot, 0, slot.Length); - - ushort[] keys = new ushort[4]; + ReadOnlySpan slot = Data.AsSpan(slotOffset, SLOT_SIZE); + Span keys = stackalloc ushort[4]; for (int i = 0; i < keys.Length; i++) - keys[i] = BigEndian.ToUInt16(slot, 8 + (i * 2)); + keys[i] = ReadUInt16BigEndian(slot[(8 + (i * 2))..]); // Decrypt Slot Data = GeniusCrypto.Decrypt(slot, 0x00010, 0x27FD8, keys); } // Get Offset Info - ushort[] subLength = new ushort[16]; + Span subLength = stackalloc ushort[16]; for (int i = 0; i < 16; i++) { - subLength[i] = BigEndian.ToUInt16(Data, 0x20 + (2 * i)); - subOffsets[i] = BigEndian.ToUInt16(Data, 0x40 + (4 * i)) | BigEndian.ToUInt16(Data, 0x40 + (4 * i) + 2) << 16; + subLength[i] = ReadUInt16BigEndian(Data.AsSpan(0x20 + (2 * i))); + subOffsets[i] = ReadUInt16BigEndian(Data.AsSpan(0x40 + (4 * i))) | ReadUInt16BigEndian(Data.AsSpan(0x40 + (4 * i) + 2)) << 16; } // Offsets are displaced by the 0xA8 savedata region @@ -136,9 +135,9 @@ private byte[] GetInnerData() SetChecksums(); // Get updated save slot data - ushort[] keys = new ushort[4]; + Span keys = stackalloc ushort[4]; for (int i = 0; i < keys.Length; i++) - keys[i] = BigEndian.ToUInt16(Data, 8 + (i * 2)); + keys[i] = ReadUInt16BigEndian(Data.AsSpan(8 + (i * 2))); byte[] newSAV = GeniusCrypto.Encrypt(Data, 0x10, 0x27FD8, keys); // Put save slot back in original save data @@ -176,7 +175,7 @@ protected override SaveFile CloneInternal() public override int BoxCount => 8; - public override bool IsPKMPresent(byte[] data, int offset) => PKX.IsPKMPresentGC(Data, offset); + public override bool IsPKMPresent(ReadOnlySpan data) => PKX.IsPKMPresentGC(data); // Checksums protected override void SetChecksums() @@ -193,8 +192,8 @@ public override string ChecksumInfo byte[] data = SetChecksums(Data, subOffsets[0]); const int start = 0xA8; // 0x88 + 0x20 - int oldHC = BigEndian.ToInt32(Data, start + subOffsets[0] + 0x38); - int newHC = BigEndian.ToInt32(data, start + subOffsets[0] + 0x38); + int oldHC = ReadInt32BigEndian(Data.AsSpan(start + subOffsets[0] + 0x38)); + int newHC = ReadInt32BigEndian(data.AsSpan(start + subOffsets[0] + 0x38)); bool header = newHC == oldHC; var oldCHK = Data.AsSpan(0x10, 0x10); @@ -217,44 +216,47 @@ private static byte[] SetChecksums(byte[] input, int subOffset0) for (int i = 0; i < 8; i++) newHC += data[i]; - BigEndian.GetBytes(newHC).CopyTo(data, start + subOffset0 + 0x38); + WriteInt32BigEndian(data.AsSpan(start + subOffset0 + 0x38), newHC); // Body Checksum data.AsSpan(0x10, 0x10).Fill(0); // Clear old Checksum Data - uint[] checksum = new uint[4]; + Span checksum = stackalloc uint[4]; int dt = 8; for (int i = 0; i < checksum.Length; i++) { uint val = 0; var end = dt + 0x9FF4; for (int j = dt; j < end; j += 2) - val += BigEndian.ToUInt16(data, j); + val += ReadUInt16BigEndian(data.AsSpan(j)); dt = end; checksum[i] = val; } - ushort[] newchks = new ushort[8]; + Span newchks = stackalloc ushort[8]; for (int i = 0; i < 4; i++) { newchks[i*2] = (ushort)(checksum[i] >> 16); newchks[(i * 2) + 1] = (ushort)checksum[i]; } - Array.Reverse(newchks); for (int i = 0; i < newchks.Length; i++) - BigEndian.GetBytes(newchks[i]).CopyTo(data, 0x10 + (2 * i)); + { + var dest = data.AsSpan(0x10 + (2 * i)); + var chk = newchks[newchks.Length - 1 - i]; + WriteUInt16BigEndian(dest, chk); + } return data; } // Trainer Info public override GameVersion Version { get => GameVersion.XD; protected set { } } - public override string OT { get => GetString(Trainer1 + 0x00, 20); set => SetString(value, 10).CopyTo(Data, Trainer1 + 0x00); } - public override int SID { get => BigEndian.ToUInt16(Data, Trainer1 + 0x2C); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, Trainer1 + 0x2C); } - public override int TID { get => BigEndian.ToUInt16(Data, Trainer1 + 0x2E); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, Trainer1 + 0x2E); } + public override string OT { get => GetString(Trainer1 + 0x00, 20); set => SetString(Data.AsSpan(Trainer1 + 0x00, 20), value.AsSpan(), 10, StringConverterOption.ClearZero); } + public override int SID { get => ReadUInt16BigEndian(Data.AsSpan(Trainer1 + 0x2C)); set => WriteUInt16BigEndian(Data.AsSpan(Trainer1 + 0x2C), (ushort)value); } + public override int TID { get => ReadUInt16BigEndian(Data.AsSpan(Trainer1 + 0x2E)); set => WriteUInt16BigEndian(Data.AsSpan(Trainer1 + 0x2E), (ushort)value); } public override int Gender { get => Data[Trainer1 + 0x8E0]; set => Data[Trainer1 + 0x8E0] = (byte)value; } - public override uint Money { get => BigEndian.ToUInt32(Data, Trainer1 + 0x8E4); set => BigEndian.GetBytes(value).CopyTo(Data, Trainer1 + 0x8E4); } - public uint Coupons { get => BigEndian.ToUInt32(Data, Trainer1 + 0x8E8); set => BigEndian.GetBytes(value).CopyTo(Data, Trainer1 + 0x8E8); } + public override uint Money { get => ReadUInt32BigEndian(Data.AsSpan(Trainer1 + 0x8E4)); set => WriteUInt32BigEndian(Data.AsSpan(Trainer1 + 0x8E4), value); } + public uint Coupons { get => ReadUInt32BigEndian(Data.AsSpan(Trainer1 + 0x8E8)); set => WriteUInt32BigEndian(Data.AsSpan(Trainer1 + 0x8E8), value); } // Storage public override int GetPartyOffset(int slot) => Party + (SIZE_STORED * slot); @@ -264,9 +266,7 @@ private static byte[] SetChecksums(byte[] input, int subOffset0) public override void SetBoxName(int box, string value) { - if (value.Length > 8) - value = value[..8]; // Hard cap - SetString(value, 8).CopyTo(Data, GetBoxInfoOffset(box)); + SetString(Data.AsSpan(GetBoxInfoOffset(box), 20), value.AsSpan(), 8, StringConverterOption.ClearZero); } protected override PKM GetPKM(byte[] data) @@ -370,13 +370,11 @@ public override IReadOnlyList Inventory public override void SetDaycareEXP(int loc, int slot, uint EXP) { /* todo */ } public override void SetDaycareOccupied(int loc, int slot, bool occupied) { /* todo */ } - public override string GetString(byte[] data, int offset, int length) => StringConverter3.GetBEString3(data, offset, length); + public override string GetString(ReadOnlySpan data) => StringConverter3GC.GetString(data); - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (PadToSize == 0) - PadToSize = maxLength + 1; - return StringConverter3.SetBEString3(value, maxLength, PadToSize, PadWith); + return StringConverter3GC.SetString(destBuffer, value, maxLength, option); } -} + } } diff --git a/PKHeX.Core/Saves/SAV4.cs b/PKHeX.Core/Saves/SAV4.cs index 28d390647..0fbe57605 100644 --- a/PKHeX.Core/Saves/SAV4.cs +++ b/PKHeX.Core/Saves/SAV4.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -93,14 +94,14 @@ public sealed override void CopyChangesFrom(SaveFile sav) // Checksums protected abstract int FooterSize { get; } - private ushort CalcBlockChecksum(byte[] data) => Checksums.CRC16_CCITT(new ReadOnlySpan(data, 0, data.Length - FooterSize)); - private static ushort GetBlockChecksumSaved(byte[] data) => BitConverter.ToUInt16(data, data.Length - 2); - private bool GetBlockChecksumValid(byte[] data) => CalcBlockChecksum(data) == GetBlockChecksumSaved(data); + private ushort CalcBlockChecksum(ReadOnlySpan data) => Checksums.CRC16_CCITT(data[..^FooterSize]); + private static ushort GetBlockChecksumSaved(ReadOnlySpan data) => ReadUInt16LittleEndian(data[^2..]); + private bool GetBlockChecksumValid(ReadOnlySpan data) => CalcBlockChecksum(data) == GetBlockChecksumSaved(data); protected sealed override void SetChecksums() { - BitConverter.GetBytes(CalcBlockChecksum(General)).CopyTo(General, General.Length - 2); - BitConverter.GetBytes(CalcBlockChecksum(Storage)).CopyTo(Storage, Storage.Length - 2); + WriteUInt16LittleEndian(General.AsSpan(General.Length - 2), CalcBlockChecksum(General)); + WriteUInt16LittleEndian(Storage.AsSpan(Storage.Length - 2), CalcBlockChecksum(Storage)); // Write blocks back General.CopyTo(Data, GeneralBlockPosition * PartitionSize); @@ -134,7 +135,7 @@ public sealed override string ChecksumInfo } } - private static int GetActiveBlock(byte[] data, int begin, int length) + private static int GetActiveBlock(ReadOnlySpan data, int begin, int length) { int offset = begin + length - 0x14; return SAV4BlockDetection.CompareFooters(data, offset, offset + PartitionSize); @@ -158,26 +159,26 @@ public override int PartyCount // Trainer Info public override string OT { - get => GetString(General, Trainer1, 16); - set => SetString(value, OTLength).CopyTo(General, Trainer1); + get => GetString(General.AsSpan(Trainer1, 16)); + set => SetString(General.AsSpan(Trainer1, 16), value.AsSpan(), OTLength, StringConverterOption.ClearZero); } public override int TID { - get => BitConverter.ToUInt16(General, Trainer1 + 0x10); - set => BitConverter.GetBytes((ushort)value).CopyTo(General, Trainer1 + 0x10); + get => ReadUInt16LittleEndian(General.AsSpan(Trainer1 + 0x10)); + set => WriteUInt16LittleEndian(General.AsSpan(Trainer1 + 0x10), (ushort)value); } public override int SID { - get => BitConverter.ToUInt16(General, Trainer1 + 0x12); - set => BitConverter.GetBytes((ushort)value).CopyTo(General, Trainer1 + 0x12); + get => ReadUInt16LittleEndian(General.AsSpan(Trainer1 + 0x12)); + set => WriteUInt16LittleEndian(General.AsSpan(Trainer1 + 0x12), (ushort)value); } public override uint Money { - get => BitConverter.ToUInt32(General, Trainer1 + 0x14); - set => BitConverter.GetBytes(value).CopyTo(General, Trainer1 + 0x14); + get => ReadUInt32LittleEndian(General.AsSpan(Trainer1 + 0x14)); + set => WriteUInt32LittleEndian(General.AsSpan(Trainer1 + 0x14), value); } public override int Gender @@ -206,14 +207,14 @@ public int Sprite public uint Coin { - get => BitConverter.ToUInt16(General, Trainer1 + 0x20); - set => BitConverter.GetBytes((ushort)value).CopyTo(General, Trainer1 + 0x20); + get => ReadUInt16LittleEndian(General.AsSpan(Trainer1 + 0x20)); + set => WriteUInt16LittleEndian(General.AsSpan(Trainer1 + 0x20), (ushort)value); } public override int PlayedHours { - get => BitConverter.ToUInt16(General, Trainer1 + 0x22); - set => BitConverter.GetBytes((ushort)value).CopyTo(General, Trainer1 + 0x22); + get => ReadUInt16LittleEndian(General.AsSpan(Trainer1 + 0x22)); + set => WriteUInt16LittleEndian(General.AsSpan(Trainer1 + 0x22), (ushort)value); } public override int PlayedMinutes @@ -232,15 +233,20 @@ public override int PlayedSeconds public abstract int X { get; set; } public abstract int Y { get; set; } - public abstract string Rival { get; set; } + public string Rival + { + get => GetString(Rival_Trash); + set => SetString(Rival_Trash, value.AsSpan(), OTLength, StringConverterOption.ClearZero); + } + public abstract Span Rival_Trash { get; set; } public abstract int X2 { get; set; } public abstract int Y2 { get; set; } public abstract int Z { get; set; } - public override uint SecondsToStart { get => BitConverter.ToUInt32(General, AdventureInfo + 0x34); set => BitConverter.GetBytes(value).CopyTo(General, AdventureInfo + 0x34); } - public override uint SecondsToFame { get => BitConverter.ToUInt32(General, AdventureInfo + 0x3C); set => BitConverter.GetBytes(value).CopyTo(General, AdventureInfo + 0x3C); } + public override uint SecondsToStart { get => ReadUInt32LittleEndian(General.AsSpan(AdventureInfo + 0x34)); set => WriteUInt32LittleEndian(General.AsSpan(AdventureInfo + 0x34), value); } + public override uint SecondsToFame { get => ReadUInt32LittleEndian(General.AsSpan(AdventureInfo + 0x3C)); set => WriteUInt32LittleEndian(General.AsSpan(AdventureInfo + 0x3C), value); } protected sealed override PKM GetPKM(byte[] data) => new PK4(data); protected sealed override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray45(data); @@ -260,7 +266,7 @@ protected sealed override void SetPKM(PKM pkm, bool isParty = false) public override uint? GetDaycareEXP(int loc, int slot) { int ofs = DaycareOffset + ((slot+1)*SIZE_PARTY) - 4; - return BitConverter.ToUInt32(General, ofs); + return ReadUInt32LittleEndian(General.AsSpan(ofs)); } public override bool? IsDaycareOccupied(int loc, int slot) => null; // todo @@ -268,7 +274,7 @@ protected sealed override void SetPKM(PKM pkm, bool isParty = false) public override void SetDaycareEXP(int loc, int slot, uint EXP) { int ofs = DaycareOffset + ((slot+1)*SIZE_PARTY) - 4; - BitConverter.GetBytes(EXP).CopyTo(General, ofs); + WriteUInt32LittleEndian(General.AsSpan(ofs), EXP); } public override void SetDaycareOccupied(int loc, int slot, bool occupied) @@ -371,14 +377,13 @@ protected sealed override bool[] MysteryGiftReceivedFlags if (GiftFlagMax != value.Length) return; - byte[] data = new byte[value.Length / 8]; + Span data = General.AsSpan(WondercardFlags, value.Length / 8); + data.Clear(); for (int i = 0; i < value.Length; i++) { if (value[i]) data[i >> 3] |= (byte)(1 << (i & 7)); } - - SetData(General, data, WondercardFlags); } } @@ -469,13 +474,11 @@ public int DexUpgraded } } - public sealed override string GetString(byte[] data, int offset, int length) => StringConverter4.GetString4(data, offset, length); + public sealed override string GetString(ReadOnlySpan data) => StringConverter4.GetString(data); - public sealed override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public sealed override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (PadToSize == 0) - PadToSize = maxLength + 1; - return StringConverter4.SetString4(value, maxLength, PadToSize, PadWith); + return StringConverter4.SetString(destBuffer, value, maxLength, option); } /// All Event Constant values for the savegame @@ -486,20 +489,21 @@ public sealed override ushort[] GetEventConsts() ushort[] Constants = new ushort[EventConstMax]; for (int i = 0; i < Constants.Length; i++) - Constants[i] = BitConverter.ToUInt16(General, EventConst + (i * 2)); + Constants[i] = ReadUInt16LittleEndian(General.AsSpan(EventConst + (i * 2))); return Constants; } /// All Event Constant values for the savegame - public sealed override void SetEventConsts(ushort[] value) + public sealed override void SetEventConsts(ReadOnlySpan value) { if (EventConstMax <= 0) return; if (value.Length != EventConstMax) return; + var span = General.AsSpan(EventConst); for (int i = 0; i < value.Length; i++) - BitConverter.GetBytes(value[i]).CopyTo(General, EventConst + (i * 2)); + WriteUInt16LittleEndian(span[(i * 2)..], value[i]); } // Seals @@ -514,9 +518,9 @@ public sealed override void SetEventConsts(ushort[] value) public void SetAllSeals(byte count, bool unreleased = false) { var sealIndexCount = (int)(unreleased ? Seal4.MAX : Seal4.MAXLEGAL); - var val = Math.Min(count, SealMaxCount); + var clamped = Math.Min(count, SealMaxCount); for (int i = 0; i < sealIndexCount; i++) - General[Seal + i] = val; + General[Seal + i] = clamped; } public int GetMailOffset(int index) diff --git a/PKHeX.Core/Saves/SAV4BR.cs b/PKHeX.Core/Saves/SAV4BR.cs index 0061e6f09..86f602f11 100644 --- a/PKHeX.Core/Saves/SAV4BR.cs +++ b/PKHeX.Core/Saves/SAV4BR.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -25,13 +26,13 @@ public SAV4BR(byte[] data) : base(data) InitializeData(data); } - private void InitializeData(byte[] data) + private void InitializeData(ReadOnlySpan data) { Data = DecryptPBRSaveData(data); // Detect active save - var first = BigEndian.ToUInt32(Data, 0x00004C); - var second = BigEndian.ToUInt32(Data, 0x1C004C); + var first = ReadUInt32BigEndian(Data.AsSpan(0x00004C)); + var second = ReadUInt32BigEndian(Data.AsSpan(0x1C004C)); SaveCount = Math.Max(second, first); if (second > first) { @@ -142,7 +143,7 @@ protected override void SetChecksums() public override bool ChecksumsValid => IsChecksumsValid(Data); public override string ChecksumInfo => $"Checksums valid: {ChecksumsValid}."; - public static bool IsChecksumsValid(byte[] sav) + public static bool IsChecksumsValid(Span sav) { return VerifyChecksum(sav, 0x000000, 0x1C0000, 0x1BFF80) && VerifyChecksum(sav, 0x000000, 0x000100, 0x000008) @@ -156,13 +157,15 @@ public static bool IsChecksumsValid(byte[] sav) private string GetOTName(int slot) { var ofs = 0x390 + (0x6FF00 * slot); - return GetString(Data, ofs, 16); + var span = Data.AsSpan(ofs, 16); + return GetString(span); } private void SetOTName(int slot, string name) { var ofs = 0x390 + (0x6FF00 * slot); - SetData(SetString(name, 7, 8), ofs); + var span = Data.AsSpan(ofs, 16); + SetString(span, name.AsSpan(), 7, StringConverterOption.ClearZero); } public string CurrentOT { get => GetOTName(_currentSlot); set => SetOTName(_currentSlot, value); } @@ -201,10 +204,10 @@ public override string GetBoxName(int box) return $"BOX {box + 1}"; int ofs = BoxName + (box * BoxNameLength); - var str = GetString(ofs, BoxNameLength); - if (string.IsNullOrWhiteSpace(str)) + var span = Data.AsSpan(ofs, BoxNameLength); + if (span.Count((byte)0) == span.Length) return $"BOX {box + 1}"; - return str; + return GetString(ofs, BoxNameLength); } public override void SetBoxName(int box, string value) @@ -213,12 +216,11 @@ public override void SetBoxName(int box, string value) return; int ofs = BoxName + (box * BoxNameLength); - var str = GetString(ofs, BoxNameLength); - if (string.IsNullOrWhiteSpace(str)) + var span = Data.AsSpan(ofs, BoxNameLength); + if (span.Count((byte)0) == span.Length) return; - var data = SetString(value, BoxNameLength / 2, BoxNameLength / 2); - SetData(data, ofs); + SetString(span, value.AsSpan(), BoxNameLength / 2, StringConverterOption.ClearZero); } protected override PKM GetPKM(byte[] data) @@ -243,102 +245,102 @@ protected override void SetPKM(PKM pkm, bool isParty = false) protected override void SetPartyValues(PKM pkm, bool isParty) { - pkm.Sanity = isParty ? (ushort)0xC000 : (ushort)0x4000; + if (pkm is G4PKM g4) + g4.Sanity = isParty ? (ushort)0xC000 : (ushort)0x4000; } - public static byte[] DecryptPBRSaveData(byte[] input) + public static byte[] DecryptPBRSaveData(ReadOnlySpan input) { byte[] output = new byte[input.Length]; + Span keys = stackalloc ushort[4]; for (int i = 0; i < SaveUtil.SIZE_G4BR; i += 0x1C0000) { - var keys = GetKeys(input, i); - Array.Copy(input, i, output, i, 8); + ReadKeys(input, i, keys); + input.Slice(i, 8).CopyTo(output.AsSpan(i, 8)); GeniusCrypto.Decrypt(input, i + 8, i + 0x1C0000, keys, output); } return output; } - private static byte[] EncryptPBRSaveData(byte[] input) + private static byte[] EncryptPBRSaveData(ReadOnlySpan input) { byte[] output = new byte[input.Length]; + Span keys = stackalloc ushort[4]; for (int i = 0; i < SaveUtil.SIZE_G4BR; i += 0x1C0000) { - var keys = GetKeys(input, i); - Array.Copy(input, i, output, i, 8); + ReadKeys(input, i, keys); + input.Slice(i, 8).CopyTo(output.AsSpan(i, 8)); GeniusCrypto.Encrypt(input, i + 8, i + 0x1C0000, keys, output); } return output; } - private static ushort[] GetKeys(byte[] input, int ofs) + private static void ReadKeys(ReadOnlySpan input, int ofs, Span keys) { - ushort[] keys = new ushort[4]; for (int i = 0; i < keys.Length; i++) - keys[i] = BigEndian.ToUInt16(input, ofs + (i * 2)); - return keys; + keys[i] = ReadUInt16BigEndian(input[(ofs + (i * 2))..]); } - public static bool VerifyChecksum(byte[] input, int offset, int len, int checksum_offset) + public static bool VerifyChecksum(Span input, int offset, int len, int checksum_offset) { - uint[] storedChecksums = new uint[16]; - for (int i = 0; i < storedChecksums.Length; i++) + Span originalChecksums = stackalloc uint[16]; + for (int i = 0; i < originalChecksums.Length; i++) { - storedChecksums[i] = BigEndian.ToUInt32(input, checksum_offset + (i * 4)); - BitConverter.GetBytes(0u).CopyTo(input, checksum_offset + (i * 4)); + var chk = input.Slice(checksum_offset + (i * 4), 4); + originalChecksums[i] = ReadUInt32BigEndian(chk); + chk.Clear(); } - uint[] checksums = new uint[16]; - - for (int i = 0; i < len; i += 2) + Span checksums = stackalloc uint[16]; + var span = input.Slice(offset, len); + for (int i = 0; i < span.Length; i += 2) { - uint val = BigEndian.ToUInt16(input, offset + i); + uint val = ReadUInt16BigEndian(span[i..]); for (int j = 0; j < 16; j++) - { checksums[j] += ((val >> j) & 1); - } } // Restore original checksums - for (int i = 0; i < storedChecksums.Length; i++) + for (int i = 0; i < originalChecksums.Length; i++) { - BigEndian.GetBytes(storedChecksums[i]).CopyTo(input, checksum_offset + (i * 4)); + var chk = originalChecksums[i]; + var dest = input[(checksum_offset + (i * 4))..]; + WriteUInt32BigEndian(dest, chk); } // Check if they match - for (int i = 0; i < storedChecksums.Length; i++) + for (int i = 0; i < originalChecksums.Length; i++) { - if (storedChecksums[i] != checksums[i]) + if (originalChecksums[i] != checksums[i]) return false; } return true; } - private static void SetChecksum(byte[] input, int offset, int len, int checksum_offset) + private static void SetChecksum(Span input, int offset, int len, int checksum_offset) { - uint[] storedChecksums = new uint[16]; - for (int i = 0; i < storedChecksums.Length; i++) - { - storedChecksums[i] = BigEndian.ToUInt32(input, checksum_offset + (i * 4)); - BitConverter.GetBytes(0u).CopyTo(input, checksum_offset + (i * 4)); - } - - uint[] checksums = new uint[16]; + // Wipe Checksum region. + input.Slice(checksum_offset, 4 * 16).Clear(); + Span checksums = stackalloc uint[16]; + var span = input.Slice(offset, len); for (int i = 0; i < len; i += 2) { - uint val = BigEndian.ToUInt16(input, offset + i); + uint val = ReadUInt16BigEndian(span[i..]); for (int j = 0; j < 16; j++) checksums[j] += ((val >> j) & 1); } for (int i = 0; i < checksums.Length; i++) { - BigEndian.GetBytes(checksums[i]).CopyTo(input, checksum_offset + (i * 4)); + var chk = checksums[i]; + var dest = input[(checksum_offset + (i * 4))..]; + WriteUInt32BigEndian(dest, chk); } } - public override string GetString(byte[] data, int offset, int length) => StringConverter4.GetBEString4Unicode(data, offset, length); + public override string GetString(ReadOnlySpan data) => StringConverter4GC.GetStringUnicode(data); - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) => StringConverter4.SetBEString4Unicode(value, maxLength, PadToSize, PadWith); + public override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) => StringConverter4GC.SetStringUnicode(value, destBuffer, maxLength, option); } } diff --git a/PKHeX.Core/Saves/SAV4DP.cs b/PKHeX.Core/Saves/SAV4DP.cs index 7d452d89e..30f823d0e 100644 --- a/PKHeX.Core/Saves/SAV4DP.cs +++ b/PKHeX.Core/Saves/SAV4DP.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -103,10 +104,10 @@ public override IReadOnlyList Inventory public bool[] GetMysteryGiftDPSlotActiveFlags() { - int ofs = WondercardFlags + 0x100; // skip over flags + var span = General.AsSpan(WondercardFlags + 0x100); // skip over flags bool[] active = new bool[GiftCountMax]; // 8 PGT, 3 PCD for (int i = 0; i < active.Length; i++) - active[i] = BitConverter.ToUInt32(General, ofs + (4 * i)) == MysteryGiftDPSlotActive; + active[i] = ReadUInt32LittleEndian(span[(4*i)..]) == MysteryGiftDPSlotActive; return active; } @@ -116,12 +117,9 @@ public void SetMysteryGiftDPSlotActiveFlags(bool[] value) if (value.Length != GiftCountMax) return; - int ofs = WondercardFlags + 0x100; // skip over flags + var span = General.AsSpan(WondercardFlags + 0x100); // skip over flags for (int i = 0; i < value.Length; i++) - { - byte[] magic = BitConverter.GetBytes(value[i] ? MysteryGiftDPSlotActive : 0); // 4 bytes - SetData(General, magic, ofs + (4 * i)); - } + WriteUInt32LittleEndian(span[(4 * i)..], value[i] ? MysteryGiftDPSlotActive : 0); } public override MysteryGiftAlbum GiftAlbum @@ -142,15 +140,9 @@ private void SetActiveGiftFlags(IReadOnlyList gifts) SetMysteryGiftDPSlotActiveFlags(arr); } - public override int M { get => BitConverter.ToUInt16(General, 0x1238); set => BitConverter.GetBytes((ushort)value).CopyTo(General, 0x1238); } - public override int X { get => BitConverter.ToUInt16(General, 0x1240); set => BitConverter.GetBytes((ushort)(X2 = value)).CopyTo(General, 0x1240); } - public override int Y { get => BitConverter.ToUInt16(General, 0x1244); set => BitConverter.GetBytes((ushort)(Y2 = value)).CopyTo(General, 0x1244); } - - public override string Rival - { - get => GetString(General, 0x25A8, OTLength * 2); - set => SetString(value, OTLength).CopyTo(General, 0x25A8); - } + public override int M { get => ReadUInt16LittleEndian(General.AsSpan(0x1238)); set => WriteUInt16LittleEndian(General.AsSpan(0x1238), (ushort)value); } + public override int X { get => ReadUInt16LittleEndian(General.AsSpan(0x1240)); set => WriteUInt16LittleEndian(General.AsSpan(0x1240), (ushort)(X2 = value)); } + public override int Y { get => ReadUInt16LittleEndian(General.AsSpan(0x1244)); set => WriteUInt16LittleEndian(General.AsSpan(0x1244), (ushort)(Y2 = value)); } public override Span Rival_Trash { @@ -158,12 +150,12 @@ public override Span Rival_Trash set { if (value.Length == OTLength * 2) value.CopyTo(General.AsSpan(0x25A8)); } } - public override int X2 { get => BitConverter.ToUInt16(General, 0x25FA); set => BitConverter.GetBytes((ushort)value).CopyTo(General, 0x25FA); } - public override int Y2 { get => BitConverter.ToUInt16(General, 0x25FE); set => BitConverter.GetBytes((ushort)value).CopyTo(General, 0x25FE); } - public override int Z { get => BitConverter.ToUInt16(General, 0x2602); set => BitConverter.GetBytes((ushort)value).CopyTo(General, 0x2602); } + public override int X2 { get => ReadUInt16LittleEndian(General.AsSpan(0x25FA)); set => WriteUInt16LittleEndian(General.AsSpan(0x25FA), (ushort)value); } + public override int Y2 { get => ReadUInt16LittleEndian(General.AsSpan(0x25FE)); set => WriteUInt16LittleEndian(General.AsSpan(0x25FE), (ushort)value); } + public override int Z { get => ReadUInt16LittleEndian(General.AsSpan(0x2602)); set => WriteUInt16LittleEndian(General.AsSpan(0x2602), (ushort)value); } - public override uint SafariSeed { get => BitConverter.ToUInt32(General, 0x72D0); set => BitConverter.GetBytes(value).CopyTo(General, 0x72D0); } - public override uint SwarmSeed { get => BitConverter.ToUInt32(General, 0x72D4); set => BitConverter.GetBytes(value).CopyTo(General, 0x72D4); } + public override uint SafariSeed { get => ReadUInt32LittleEndian(General.AsSpan(0x72D0)); set => WriteUInt32LittleEndian(General.AsSpan(0x72D0), value); } + public override uint SwarmSeed { get => ReadUInt32LittleEndian(General.AsSpan(0x72D4)); set => WriteUInt32LittleEndian(General.AsSpan(0x72D4), value); } public override uint SwarmMaxCountModulo => 28; } } diff --git a/PKHeX.Core/Saves/SAV4HGSS.cs b/PKHeX.Core/Saves/SAV4HGSS.cs index 601a0a707..a20ebf891 100644 --- a/PKHeX.Core/Saves/SAV4HGSS.cs +++ b/PKHeX.Core/Saves/SAV4HGSS.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -92,20 +93,17 @@ public override byte[] BoxFlags public int Counter { - get => BitConverter.ToInt32(Storage, BOX_END + 4); - set => SetData(Storage, BitConverter.GetBytes(value), BOX_END + 4); + get => ReadInt32LittleEndian(Storage.AsSpan(BOX_END + 4)); + set => WriteInt32LittleEndian(Storage.AsSpan(BOX_END + 4), value); } - public override string GetBoxName(int box) => GetString(Storage, GetBoxNameOffset(box), BOX_NAME_LEN); + public override string GetBoxName(int box) => GetString(Storage.AsSpan(GetBoxNameOffset(box), BOX_NAME_LEN)); public override void SetBoxName(int box, string value) { const int maxlen = 8; - if (value.Length > maxlen) - value = value[..maxlen]; // Hard cap - int offset = GetBoxNameOffset(box); - var str = SetString(value, maxlen); - SetData(Storage, str, offset); + var span = Storage.AsSpan(GetBoxNameOffset(box), BOX_NAME_LEN); + SetString(span, value.AsSpan(), maxlen, StringConverterOption.ClearZero); } private static int AdjustWallpaper(int value, int shift) @@ -151,25 +149,19 @@ public override IReadOnlyList Inventory set => value.SaveAll(General); } - public override int M { get => BitConverter.ToUInt16(General, 0x1234); set => BitConverter.GetBytes((ushort)value).CopyTo(General, 0x1234); } - public override int X { get => BitConverter.ToUInt16(General, 0x123C); set => BitConverter.GetBytes((ushort)(X2 = value)).CopyTo(General, 0x123C); } - public override int Y { get => BitConverter.ToUInt16(General, 0x1240); set => BitConverter.GetBytes((ushort)(Y2 = value)).CopyTo(General, 0x1240); } - - public override string Rival - { - get => GetString(General, 0x22D4, OTLength * 2); - set => SetString(value, OTLength).CopyTo(General, 0x22D4); - } - + public override int M { get => ReadUInt16LittleEndian(General.AsSpan(0x1234)); set => WriteUInt16LittleEndian(General.AsSpan(0x1234), (ushort)value); } + public override int X { get => ReadUInt16LittleEndian(General.AsSpan(0x123C)); set => WriteUInt16LittleEndian(General.AsSpan(0x123C), (ushort)(X2 = value)); } + public override int Y { get => ReadUInt16LittleEndian(General.AsSpan(0x1240)); set => WriteUInt16LittleEndian(General.AsSpan(0x1240), (ushort)(Y2 = value)); } + public override Span Rival_Trash { get => General.AsSpan(0x22D4, OTLength * 2); set { if (value.Length == OTLength * 2) value.CopyTo(General.AsSpan(0x22D4)); } } - public override int X2 { get => BitConverter.ToUInt16(General, 0x236E); set => BitConverter.GetBytes((ushort)value).CopyTo(General, 0x236E); } - public override int Y2 { get => BitConverter.ToUInt16(General, 0x2372); set => BitConverter.GetBytes((ushort)value).CopyTo(General, 0x2372); } - public override int Z { get => BitConverter.ToUInt16(General, 0x2376); set => BitConverter.GetBytes((ushort)value).CopyTo(General, 0x2376); } + public override int X2 { get => ReadUInt16LittleEndian(General.AsSpan(0x236E)); set => WriteUInt16LittleEndian(General.AsSpan(0x236E), (ushort)value); } + public override int Y2 { get => ReadUInt16LittleEndian(General.AsSpan(0x2372)); set => WriteUInt16LittleEndian(General.AsSpan(0x2372), (ushort)value); } + public override int Z { get => ReadUInt16LittleEndian(General.AsSpan(0x2376)); set => WriteUInt16LittleEndian(General.AsSpan(0x2376), (ushort)value); } public int Badges16 { @@ -240,15 +232,15 @@ public void PokeGearUnlockAllCallersNoTrainers() public const int WalkerPair = 0xE5E0; private const int OFS_WALKER = 0xE704; - public uint PokewalkerSteps { get => BitConverter.ToUInt32(General, OFS_WALKER); set => SetData(General, BitConverter.GetBytes(value), OFS_WALKER); } - public uint PokewalkerWatts { get => BitConverter.ToUInt32(General, OFS_WALKER + 0x4); set => SetData(General, BitConverter.GetBytes(value), OFS_WALKER + 0x4); } + public uint PokewalkerSteps { get => ReadUInt32LittleEndian(General.AsSpan(OFS_WALKER)); set => WriteUInt32LittleEndian(General.AsSpan(OFS_WALKER), value); } + public uint PokewalkerWatts { get => ReadUInt32LittleEndian(General.AsSpan(OFS_WALKER + 0x4)); set => WriteUInt32LittleEndian(General.AsSpan(OFS_WALKER + 4), value); } - public bool[] GetPokewalkerCoursesUnlocked() => ArrayUtil.GitBitFlagArray(General, OFS_WALKER + 0x8, 32); - public void SetPokewalkerCoursesUnlocked(bool[] value) => ArrayUtil.SetBitFlagArray(General, OFS_WALKER + 0x8, value); + public bool[] GetPokewalkerCoursesUnlocked() => ArrayUtil.GitBitFlagArray(General.AsSpan(OFS_WALKER + 0x8), 32); + public void SetPokewalkerCoursesUnlocked(bool[] value) => ArrayUtil.SetBitFlagArray(General.AsSpan(OFS_WALKER + 0x8), value); - public void PokewalkerCoursesSetAll(uint value = 0x07FF_FFFFu) => SetData(General, BitConverter.GetBytes(value), OFS_WALKER + 0x8); + public void PokewalkerCoursesSetAll(uint value = 0x07FF_FFFFu) => WriteUInt32LittleEndian(General.AsSpan(OFS_WALKER + 0x8), value); - public override uint SwarmSeed { get => BitConverter.ToUInt32(General, 0x68A8); set => BitConverter.GetBytes(value).CopyTo(General, 0x68A8); } + public override uint SwarmSeed { get => ReadUInt32LittleEndian(General.AsSpan(0x68A8)); set => WriteUInt32LittleEndian(General.AsSpan(0x68A8), value); } public override uint SwarmMaxCountModulo => 20; } } diff --git a/PKHeX.Core/Saves/SAV4Pt.cs b/PKHeX.Core/Saves/SAV4Pt.cs index 6b5b3d31a..0ae068bf4 100644 --- a/PKHeX.Core/Saves/SAV4Pt.cs +++ b/PKHeX.Core/Saves/SAV4Pt.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -108,15 +109,9 @@ public override IReadOnlyList Inventory set => value.SaveAll(General); } - public override int M { get => BitConverter.ToUInt16(General, 0x1280); set => BitConverter.GetBytes((ushort)value).CopyTo(General, 0x1280); } - public override int X { get => BitConverter.ToUInt16(General, 0x1288); set => BitConverter.GetBytes((ushort)(X2 = value)).CopyTo(General, 0x1288); } - public override int Y { get => BitConverter.ToUInt16(General, 0x128C); set => BitConverter.GetBytes((ushort)(Y2 = value)).CopyTo(General, 0x128C); } - - public override string Rival - { - get => GetString(General, 0x27E8, OTLength * 2); - set => SetString(value, OTLength).CopyTo(General, 0x27E8); - } + public override int M { get => ReadUInt16LittleEndian(General.AsSpan(0x1280)); set => WriteUInt16LittleEndian(General.AsSpan(0x1280), (ushort)value); } + public override int X { get => ReadUInt16LittleEndian(General.AsSpan(0x1288)); set => WriteUInt16LittleEndian(General.AsSpan(0x1288), (ushort)(X2 = value)); } + public override int Y { get => ReadUInt16LittleEndian(General.AsSpan(0x128C)); set => WriteUInt16LittleEndian(General.AsSpan(0x128C), (ushort)(Y2 = value)); } public override Span Rival_Trash { @@ -124,12 +119,12 @@ public override Span Rival_Trash set { if (value.Length == OTLength * 2) value.CopyTo(General.AsSpan(0x27E8)); } } - public override int X2 { get => BitConverter.ToUInt16(General, 0x287E); set => BitConverter.GetBytes((ushort)value).CopyTo(General, 0x287E); } - public override int Y2 { get => BitConverter.ToUInt16(General, 0x2882); set => BitConverter.GetBytes((ushort)value).CopyTo(General, 0x2882); } - public override int Z { get => BitConverter.ToUInt16(General, 0x2886); set => BitConverter.GetBytes((ushort)value).CopyTo(General, 0x2886); } + public override int X2 { get => ReadUInt16LittleEndian(General.AsSpan(0x287E)); set => WriteUInt16LittleEndian(General.AsSpan(0x287E), (ushort)value); } + public override int Y2 { get => ReadUInt16LittleEndian(General.AsSpan(0x2882)); set => WriteUInt16LittleEndian(General.AsSpan(0x2882), (ushort)value); } + public override int Z { get => ReadUInt16LittleEndian(General.AsSpan(0x2886)); set => WriteUInt16LittleEndian(General.AsSpan(0x2886), (ushort)value); } - public override uint SafariSeed { get => BitConverter.ToUInt32(General, 0x72D4); set => BitConverter.GetBytes(value).CopyTo(General, 0x72D4); } - public override uint SwarmSeed { get => BitConverter.ToUInt32(General, 0x72D8); set => BitConverter.GetBytes(value).CopyTo(General, 0x72D8); } + public override uint SafariSeed { get => ReadUInt32LittleEndian(General.AsSpan(0x72D4)); set => WriteUInt32LittleEndian(General.AsSpan(0x72D4), value); } + public override uint SwarmSeed { get => ReadUInt32LittleEndian(General.AsSpan(0x72D8)); set => WriteUInt32LittleEndian(General.AsSpan(0x72D8), value); } public override uint SwarmMaxCountModulo => 22; } } diff --git a/PKHeX.Core/Saves/SAV4Sinnoh.cs b/PKHeX.Core/Saves/SAV4Sinnoh.cs index 82568b306..2d688f135 100644 --- a/PKHeX.Core/Saves/SAV4Sinnoh.cs +++ b/PKHeX.Core/Saves/SAV4Sinnoh.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -42,16 +43,13 @@ public override byte[] BoxFlags set => Storage[BOX_FLAGS] = value[0]; } - public override string GetBoxName(int box) => GetString(Storage, GetBoxNameOffset(box), BOX_NAME_LEN); + public override string GetBoxName(int box) => GetString(Storage.AsSpan(GetBoxNameOffset(box), BOX_NAME_LEN)); public override void SetBoxName(int box, string value) { const int maxlen = 8; - if (value.Length > maxlen) - value = value[..maxlen]; // Hard cap - int offset = GetBoxNameOffset(box); - var str = SetString(value, maxlen); - SetData(Storage, str, offset); + var span = Storage.AsSpan(GetBoxNameOffset(box), BOX_NAME_LEN); + SetString(span, value.AsSpan(), maxlen, StringConverterOption.ClearZero); } #endregion @@ -93,19 +91,19 @@ public void SetPoketchAppUnlocked(PoketchApp index, bool value = true) public uint PoketchStepCounter { - get => BitConverter.ToUInt32(General, PoketchStart + 0x24); - set => SetData(General, BitConverter.GetBytes(value), PoketchStart + 0x24); + get => ReadUInt32LittleEndian(General.AsSpan(PoketchStart + 0x24)); + set => WriteUInt32LittleEndian(General.AsSpan(PoketchStart + 0x24), value); } // 2 bytes for alarm clock time setting public byte[] GetPoketchDotArtistData() => General.Slice(PoketchStart + 0x2A, 120); - public void SetPoketchDotArtistData(byte[] value) + public void SetPoketchDotArtistData(ReadOnlySpan value) { if (value.Length != 120) throw new ArgumentException($"Expected {120} bytes.", nameof(value.Length)); - SetData(General, value, PoketchStart + 0x2A); + value.CopyTo(General.AsSpan(PoketchStart + 0x2A)); } // map marking stuff is at the end, unimportant @@ -155,13 +153,13 @@ public static MunchlaxTreeSet4 CalculateMunchlaxTrees(int tid, int sid) #region Underground //Underground Scores protected int OFS_UG_Stats; - public uint UG_PlayersMet { get => BitConverter.ToUInt32(General, OFS_UG_Stats); set => SetData(General, BitConverter.GetBytes(value), OFS_UG_Stats); } - public uint UG_Gifts { get => BitConverter.ToUInt32(General, OFS_UG_Stats + 0x4); set => SetData(General, BitConverter.GetBytes(value), OFS_UG_Stats + 0x4); } - public uint UG_Spheres { get => BitConverter.ToUInt32(General, OFS_UG_Stats + 0xC); set => SetData(General, BitConverter.GetBytes(value), OFS_UG_Stats + 0xC); } - public uint UG_Fossils { get => BitConverter.ToUInt32(General, OFS_UG_Stats + 0x10); set => SetData(General, BitConverter.GetBytes(value), OFS_UG_Stats + 0x10); } - public uint UG_TrapsAvoided { get => BitConverter.ToUInt32(General, OFS_UG_Stats + 0x18); set => SetData(General, BitConverter.GetBytes(value), OFS_UG_Stats + 0x18); } - public uint UG_TrapsTriggered { get => BitConverter.ToUInt32(General, OFS_UG_Stats + 0x1C); set => SetData(General, BitConverter.GetBytes(value), OFS_UG_Stats + 0x1C); } - public uint UG_Flags { get => BitConverter.ToUInt32(General, OFS_UG_Stats + 0x34); set => SetData(General, BitConverter.GetBytes(value), OFS_UG_Stats + 0x34); } + public uint UG_PlayersMet { get => ReadUInt32LittleEndian(General.AsSpan(OFS_UG_Stats)); set => WriteUInt32LittleEndian(General.AsSpan(OFS_UG_Stats), value); } + public uint UG_Gifts { get => ReadUInt32LittleEndian(General.AsSpan(OFS_UG_Stats + 0x4)); set => WriteUInt32LittleEndian(General.AsSpan(OFS_UG_Stats + 0x4), value); } + public uint UG_Spheres { get => ReadUInt32LittleEndian(General.AsSpan(OFS_UG_Stats + 0xC)); set => WriteUInt32LittleEndian(General.AsSpan(OFS_UG_Stats + 0xC), value); } + public uint UG_Fossils { get => ReadUInt32LittleEndian(General.AsSpan(OFS_UG_Stats + 0x10)); set => WriteUInt32LittleEndian(General.AsSpan(OFS_UG_Stats + 0x10), value); } + public uint UG_TrapsAvoided { get => ReadUInt32LittleEndian(General.AsSpan(OFS_UG_Stats + 0x18)); set => WriteUInt32LittleEndian(General.AsSpan(OFS_UG_Stats + 0x18), value); } + public uint UG_TrapsTriggered { get => ReadUInt32LittleEndian(General.AsSpan(OFS_UG_Stats + 0x1C)); set => WriteUInt32LittleEndian(General.AsSpan(OFS_UG_Stats + 0x1C), value); } + public uint UG_Flags { get => ReadUInt32LittleEndian(General.AsSpan(OFS_UG_Stats + 0x34)); set => WriteUInt32LittleEndian(General.AsSpan(OFS_UG_Stats + 0x34), value); } //Underground Items protected int OFS_UG_Items; @@ -169,17 +167,17 @@ public static MunchlaxTreeSet4 CalculateMunchlaxTrees(int tid, int sid) public const int UG_POUCH_SIZE = 0x28; // 40 for each of the inventory pouches public byte[] GetUGI_Traps() => General.Slice(OFS_UG_Items, UG_POUCH_SIZE); - public void SetUGI_Traps(byte[] value) => SetData(General, value, OFS_UG_Items); + public void SetUGI_Traps(ReadOnlySpan value) => value.CopyTo(General.AsSpan(OFS_UG_Items)); public byte[] GetUGI_Goods() => General.Slice(OFS_UG_Items + 0x28, UG_POUCH_SIZE); - public void SetUGI_Goods(byte[] value) => SetData(General, value, OFS_UG_Items + 0x28); + public void SetUGI_Goods(ReadOnlySpan value) => value.CopyTo(General.AsSpan(OFS_UG_Items + 0x28)); public byte[] GetUGI_Treasures() => General.Slice(OFS_UG_Items + 0x50, UG_POUCH_SIZE); - public void SetUGI_Treasures(byte[] value) => SetData(General, value, OFS_UG_Items + 0x50); + public void SetUGI_Treasures(ReadOnlySpan value) => value.CopyTo(General.AsSpan(OFS_UG_Items + 0x50)); // first 40 are the sphere type, last 40 are the sphere sizes public byte[] GetUGI_Spheres() => General.Slice(OFS_UG_Items + 0x78, UG_POUCH_SIZE * 2); - public void SetUGI_Spheres(byte[] value) => SetData(General, value, OFS_UG_Items + 0x78); + public void SetUGI_Spheres(ReadOnlySpan value) => value.CopyTo(General.AsSpan(OFS_UG_Items + 0x78)); #endregion diff --git a/PKHeX.Core/Saves/SAV5.cs b/PKHeX.Core/Saves/SAV5.cs index 6e23eef9d..8006dc243 100644 --- a/PKHeX.Core/Saves/SAV5.cs +++ b/PKHeX.Core/Saves/SAV5.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -125,8 +126,8 @@ protected override void SetPKM(PKM pkm, bool isParty = false) 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 => BitConverter.ToUInt32(Data, AdventureInfo + 0x34); set => BitConverter.GetBytes(value).CopyTo(Data, AdventureInfo + 0x34); } - public override uint SecondsToFame { get => BitConverter.ToUInt32(Data, AdventureInfo + 0x3C); set => BitConverter.GetBytes(value).CopyTo(Data, AdventureInfo + 0x3C); } + 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 IReadOnlyList Inventory { get => Items.Inventory; set => Items.Inventory = value; } @@ -134,13 +135,11 @@ protected override void SetPKM(PKM pkm, bool isParty = false) public override bool GetCaught(int species) => Zukan.GetCaught(species); public override bool GetSeen(int species) => Zukan.GetSeen(species); - public override string GetString(byte[] data, int offset, int length) => StringConverter.GetString5(data, offset, length); + public sealed override string GetString(ReadOnlySpan data) => StringConverter5.GetString(data); - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public sealed override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (PadToSize == 0) - PadToSize = maxLength + 1; - return StringConverter.SetString5(value, maxLength, PadToSize, PadWith); + return StringConverter5.SetString(destBuffer, value, maxLength, option); } // DLC @@ -156,33 +155,31 @@ public byte[] CGearSkinData { get { - byte[] data = new byte[CGearBackground.SIZE_CGB]; if (CGearSkinPresent) - Array.Copy(Data, CGearDataOffset, data, 0, data.Length); - return data; + return Data.AsSpan(CGearDataOffset, CGearBackground.SIZE_CGB).ToArray(); + return new byte[CGearBackground.SIZE_CGB]; } set { - byte[] dlcfooter = { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x27, 0x00, 0x00, 0x27, 0x35, 0x05, 0x31, 0x00, 0x00 }; + SetData(value, CGearDataOffset); - byte[] bgdata = value; - SetData(bgdata, CGearDataOffset); + ushort chk = Checksums.CRC16_CCITT(value); + var footer = Data.AsSpan(CGearDataOffset + value.Length); - ushort chk = Checksums.CRC16_CCITT(bgdata); - var chkbytes = BitConverter.GetBytes(chk); - int footer = CGearDataOffset + bgdata.Length; + WriteUInt16LittleEndian(footer, 1); // block updated once + WriteUInt16LittleEndian(footer[2..], chk); // checksum + WriteUInt16LittleEndian(footer[0x100..], chk); // second checksum - BitConverter.GetBytes((ushort)1).CopyTo(Data, footer); // block updated once - chkbytes.CopyTo(Data, footer + 2); // checksum - chkbytes.CopyTo(Data, footer + 0x100); // second checksum - dlcfooter.CopyTo(Data, footer + 0x102); - ushort skinchkval = Checksums.CRC16_CCITT(new ReadOnlySpan(Data, footer + 0x100, 4)); - BitConverter.GetBytes(skinchkval).CopyTo(Data, footer + 0x112); + Span dlcfooter = stackalloc byte[] { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x27, 0x00, 0x00, 0x27, 0x35, 0x05, 0x31, 0x00, 0x00 }; + dlcfooter.CopyTo(footer[0x102..]); + + ushort skinchkval = Checksums.CRC16_CCITT(footer[0x100..0x104]); + WriteUInt16LittleEndian(footer[0x112..], skinchkval); // Indicate in the save file that data is present - BitConverter.GetBytes((ushort)0xC21E).CopyTo(Data, 0x19438); + WriteUInt16LittleEndian(Data.AsSpan(0x19438), 0xC21E); - chkbytes.CopyTo(Data, CGearSkinInfoOffset); + WriteUInt16LittleEndian(Data.AsSpan(CGearSkinInfoOffset), chk); CGearSkinPresent = true; State.Edited = true; diff --git a/PKHeX.Core/Saves/SAV5B2W2.cs b/PKHeX.Core/Saves/SAV5B2W2.cs index b6ebdf4ba..e71fdd8c3 100644 --- a/PKHeX.Core/Saves/SAV5B2W2.cs +++ b/PKHeX.Core/Saves/SAV5B2W2.cs @@ -60,8 +60,8 @@ private void Initialize() public string Rival { - get => GetString(0x23BA4, OTLength * 2); - set => SetString(value, OTLength).CopyTo(Data, 0x23BA4); + get => GetString(Rival_Trash); + set => SetString(Rival_Trash, value.AsSpan(), OTLength, StringConverterOption.ClearZero); } public Span Rival_Trash diff --git a/PKHeX.Core/Saves/SAV6.cs b/PKHeX.Core/Saves/SAV6.cs index ca6e7f547..b10510189 100644 --- a/PKHeX.Core/Saves/SAV6.cs +++ b/PKHeX.Core/Saves/SAV6.cs @@ -92,13 +92,13 @@ public override string GetBoxName(int box) { if (PCLayout < 0) return $"B{box + 1}"; - return GetString(Data, GetBoxNameOffset(box), LongStringLength); + return GetString(Data.AsSpan(GetBoxNameOffset(box), LongStringLength)); } public override void SetBoxName(int box, string value) { - var data = SetString(value, LongStringLength / 2, LongStringLength / 2); - SetData(data, PCLayout + (LongStringLength * box)); + var span = Data.AsSpan(GetBoxNameOffset(box), LongStringLength); + SetString(span, value.AsSpan(), LongStringLength / 2, StringConverterOption.ClearZero); } protected override void SetPKM(PKM pkm, bool isParty = false) @@ -171,13 +171,11 @@ public override int PartyCount protected set => Data[Party + (6 * SIZE_PARTY)] = (byte)value; } - public override string GetString(byte[] data, int offset, int length) => StringConverter.GetString6(data, offset, length); + public sealed override string GetString(ReadOnlySpan data) => StringConverter6.GetString(data); - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public sealed override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (PadToSize == 0) - PadToSize = maxLength + 1; - return StringConverter.SetString6(value, maxLength, PadToSize, PadWith); + return StringConverter6.SetString(destBuffer, value, maxLength, option); } public int GetRecord(int recordID) => Records.GetRecord(recordID); diff --git a/PKHeX.Core/Saves/SAV6AO.cs b/PKHeX.Core/Saves/SAV6AO.cs index 614c7d2c1..6786b4c0f 100644 --- a/PKHeX.Core/Saves/SAV6AO.cs +++ b/PKHeX.Core/Saves/SAV6AO.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -121,7 +122,7 @@ public override int GetDaycareSlotOffset(int loc, int slot) public override uint? GetDaycareEXP(int loc, int slot) { int ofs = loc == 0 ? DaycareOffset : Daycare2; - return BitConverter.ToUInt32(Data, ofs + ((SIZE_STORED + 8) * slot) + 4); + return ReadUInt32LittleEndian(Data.AsSpan(ofs + ((SIZE_STORED + 8) * slot) + 4)); } public override bool? IsDaycareOccupied(int loc, int slot) @@ -133,9 +134,7 @@ public override int GetDaycareSlotOffset(int loc, int slot) public override string GetDaycareRNGSeed(int loc) { int ofs = loc == 0 ? DaycareOffset : Daycare2; - var data = Data.AsSpan(ofs + 0x1E8, DaycareSeedSize / 2).ToArray(); - Array.Reverse(data); - return BitConverter.ToString(data).Replace("-", string.Empty); + return Util.GetHexStringFromBytes(Data.AsSpan(ofs + 0x1E8, DaycareSeedSize / 2)); } public override bool? IsDaycareHasEgg(int loc) @@ -147,7 +146,7 @@ public override string GetDaycareRNGSeed(int loc) public override void SetDaycareEXP(int loc, int slot, uint EXP) { int ofs = loc == 0 ? DaycareOffset : Daycare2; - BitConverter.GetBytes(EXP).CopyTo(Data, ofs + ((SIZE_STORED + 8) * slot) + 4); + WriteUInt32LittleEndian(Data.AsSpan(ofs + ((SIZE_STORED + 8) * slot) + 4), EXP); } public override void SetDaycareOccupied(int loc, int slot, bool occupied) @@ -174,7 +173,7 @@ public override void SetDaycareHasEgg(int loc, bool hasEgg) Data[ofs + 0x1E0] = hasEgg ? (byte)1 : (byte)0; } - public override string JPEGTitle => !HasJPPEGData ? string.Empty : StringConverter.GetString6(Data, JPEG, 0x1A); + public override string JPEGTitle => !HasJPPEGData ? string.Empty : StringConverter6.GetString(Data.AsSpan(JPEG, 0x1A)); public override byte[] GetJPEGData() => !HasJPPEGData ? Array.Empty() : GetData(JPEG + 0x54, 0xE004); private bool HasJPPEGData => Data[JPEG + 0x54] == 0xFF; diff --git a/PKHeX.Core/Saves/SAV6XY.cs b/PKHeX.Core/Saves/SAV6XY.cs index d46be5a97..ef72efd35 100644 --- a/PKHeX.Core/Saves/SAV6XY.cs +++ b/PKHeX.Core/Saves/SAV6XY.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -88,21 +89,14 @@ private void Initialize() 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) => BitConverter.ToUInt32(Data, DaycareOffset + 4 + ((SIZE_STORED + 8) * slot)); + public override uint? GetDaycareEXP(int loc, int slot) => ReadUInt32LittleEndian(Data.AsSpan(DaycareOffset + 4 + ((SIZE_STORED + 8) * slot))); 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) => BitConverter.GetBytes(EXP).CopyTo(Data, DaycareOffset + 4 + ((SIZE_STORED + 8) * slot)); - - public override string GetDaycareRNGSeed(int loc) - { - int ofs = DaycareOffset; - var data = Data.AsSpan(ofs + 0x1E8, DaycareSeedSize / 2).ToArray(); - Array.Reverse(data); - return BitConverter.ToString(data).Replace("-", string.Empty); - } + 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) { @@ -116,7 +110,7 @@ public override void SetDaycareRNGSeed(int loc, string seed) Util.GetBytesFromHexString(seed).CopyTo(Data, DaycareOffset + 0x1E8); } - public override string JPEGTitle => !HasJPPEGData ? string.Empty : StringConverter.GetString6(Data, JPEG, 0x1A); + public override string JPEGTitle => !HasJPPEGData ? string.Empty : StringConverter6.GetString(Data.AsSpan(JPEG, 0x1A)); public override byte[] GetJPEGData() => !HasJPPEGData ? Array.Empty() : GetData(JPEG + 0x54, 0xE004); private bool HasJPPEGData => Data[JPEG + 0x54] == 0xFF; diff --git a/PKHeX.Core/Saves/SAV7.cs b/PKHeX.Core/Saves/SAV7.cs index 235d5f34a..2e935824e 100644 --- a/PKHeX.Core/Saves/SAV7.cs +++ b/PKHeX.Core/Saves/SAV7.cs @@ -111,13 +111,11 @@ protected override byte[] GetFinalData() _ => GameVersion.Invalid, }; - public override string GetString(byte[] data, int offset, int length) => StringConverter.GetString7(data, offset, length); + public sealed override string GetString(ReadOnlySpan data) => StringConverter7.GetString(data); - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public sealed override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (PadToSize == 0) - PadToSize = maxLength + 1; - return StringConverter.SetString7(value, maxLength, Language, PadToSize, PadWith); + return StringConverter7.SetString(destBuffer, value, maxLength, Language, option); } // Player Information @@ -229,10 +227,10 @@ public override StorageSlotFlag GetSlotFlags(int index) return StorageSlotFlag.None; team /= 6; - var val = (StorageSlotFlag)((int)StorageSlotFlag.BattleTeam1 << team); + var result = (StorageSlotFlag)((int)StorageSlotFlag.BattleTeam1 << team); if (BoxLayout.GetIsTeamLocked(team)) - val |= StorageSlotFlag.Locked; - return val; + result |= StorageSlotFlag.Locked; + return result; } private int FusedCount => this is SAV7USUM ? 3 : 1; diff --git a/PKHeX.Core/Saves/SAV7SM.cs b/PKHeX.Core/Saves/SAV7SM.cs index bba67c02b..a0c61c632 100644 --- a/PKHeX.Core/Saves/SAV7SM.cs +++ b/PKHeX.Core/Saves/SAV7SM.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -75,7 +76,7 @@ public void UpdateMagearnaConstant() { var flag = GetEventFlag(3100); ulong value = flag ? MagearnaConst : 0ul; - SetData(BitConverter.GetBytes(value), Blocks.BlockInfo[35].Offset + 0x168); + WriteUInt64LittleEndian(Data.AsSpan(Blocks.BlockInfo[35].Offset + 0x168), value); } } } diff --git a/PKHeX.Core/Saves/SAV7b.cs b/PKHeX.Core/Saves/SAV7b.cs index ed66b5d17..44bba53e6 100644 --- a/PKHeX.Core/Saves/SAV7b.cs +++ b/PKHeX.Core/Saves/SAV7b.cs @@ -115,26 +115,24 @@ protected override void SetPKM(PKM pkm, bool isParty = false) public override StorageSlotFlag GetSlotFlags(int index) { - var val = StorageSlotFlag.None; + var result = StorageSlotFlag.None; var header = Blocks.Storage.PokeListInfo; int position = Array.IndexOf(header, index, 0, 6); if (position >= 0) - val = (StorageSlotFlag)((int)StorageSlotFlag.Party1 << position); + result = (StorageSlotFlag)((int)StorageSlotFlag.Party1 << position); if (header[PokeListHeader.STARTER] == index) - val |= StorageSlotFlag.Starter; - return val; + result |= StorageSlotFlag.Starter; + return result; } public override string GetBoxName(int box) => $"Box {box + 1}"; public override void SetBoxName(int box, string value) { } - public override string GetString(byte[] data, int offset, int length) => StringConverter.GetString7b(data, offset, length); + public override string GetString(ReadOnlySpan data) => StringConverter8.GetString(data); - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (PadToSize == 0) - PadToSize = maxLength + 1; - return StringConverter.SetString7b(value, maxLength, PadToSize, PadWith); + return StringConverter8.SetString(destBuffer, value, maxLength); } public override GameVersion Version => Game switch diff --git a/PKHeX.Core/Saves/SAV8.cs b/PKHeX.Core/Saves/SAV8.cs index 7e91eb19e..d63273692 100644 --- a/PKHeX.Core/Saves/SAV8.cs +++ b/PKHeX.Core/Saves/SAV8.cs @@ -66,13 +66,11 @@ public abstract class SAV8 : SaveFile, ISaveBlock8Main _ => GameVersion.Invalid, }; - public override string GetString(byte[] data, int offset, int length) => StringConverter.GetString7b(data, offset, length); + public sealed override string GetString(ReadOnlySpan data) => StringConverter8.GetString(data); - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public sealed override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (PadToSize == 0) - PadToSize = maxLength + 1; - return StringConverter.SetString7b(value, maxLength, PadToSize, PadWith); + return StringConverter8.SetString(destBuffer, value, maxLength, option); } // Player Information diff --git a/PKHeX.Core/Saves/SAV8BS.cs b/PKHeX.Core/Saves/SAV8BS.cs index ae34db41b..6236723f9 100644 --- a/PKHeX.Core/Saves/SAV8BS.cs +++ b/PKHeX.Core/Saves/SAV8BS.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Security.Cryptography; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -120,8 +121,8 @@ private void Initialize() public int SaveRevision { - get => BitConverter.ToInt32(Data, 0); - init => BitConverter.GetBytes(value).CopyTo(Data, 0); + get => ReadInt32LittleEndian(Data.AsSpan(0)); + init => WriteInt32LittleEndian(Data.AsSpan(0), value); } public string SaveRevisionString => ((Gem8Version)SaveRevision).GetSuffixString(); @@ -150,10 +151,10 @@ public override StorageSlotFlag GetSlotFlags(int index) return StorageSlotFlag.None; team /= 6; - var val = (StorageSlotFlag)((int)StorageSlotFlag.BattleTeam1 << team); + var result = (StorageSlotFlag)((int)StorageSlotFlag.BattleTeam1 << team); if (BoxLayout.GetIsTeamLocked(team)) - val |= StorageSlotFlag.Locked; - return val; + result |= StorageSlotFlag.Locked; + return result; } #region Checksums @@ -238,13 +239,11 @@ public override bool ChecksumsValid _ => GameVersion.Invalid, }; - public override string GetString(byte[] data, int offset, int length) => StringConverter.GetString7b(data, offset, length); + public override string GetString(ReadOnlySpan data) => StringConverter8.GetString(data); - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (PadToSize == 0) - PadToSize = maxLength + 1; - return StringConverter.SetString7b(value, maxLength, PadToSize, PadWith); + return StringConverter8.SetString(destBuffer, value, maxLength, option); } public override bool GetEventFlag(int flagNumber) => Work.GetFlag(flagNumber); @@ -281,25 +280,25 @@ public override byte[] SetString(string value, int maxLength, int PadToSize = 0, public string Rival { get => GetString(0x55F4, 0x1A); - set => SetString(value, OTLength).CopyTo(Data, 0x55F4); + set => SetString(Data.AsSpan(0x55F4, 0x1A), value.AsSpan(), OTLength, StringConverterOption.ClearZero); } public short ZoneID // map { - get => BitConverter.ToInt16(Data, 0x5634); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x5634); + get => ReadInt16LittleEndian(Data.AsSpan(0x5634)); + set => WriteInt16LittleEndian(Data.AsSpan(0x5634), value); } public float TimeScale // default 1440.0f { - get => BitConverter.ToSingle(Data, 0x5638); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x5638); + get => ReadSingleLittleEndian(Data.AsSpan(0x5638)); + set => WriteSingleLittleEndian(Data.AsSpan(0x5638), value); } public uint UnionRoomPenaltyTime // move this into the UnionSaveData block once reversed. { - get => BitConverter.ToUInt32(Data, 0xCEA14); - set => BitConverter.GetBytes(value).CopyTo(Data, 0xCEA14); + get => ReadUInt32LittleEndian(Data.AsSpan(0xCEA14)); + set => WriteSingleLittleEndian(Data.AsSpan(0xCEA14), value); } protected override void SetPKM(PKM pkm, bool isParty = false) @@ -359,14 +358,8 @@ public enum TopMenuItemType 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) - { - var data = BitConverter.GetBytes(Daycare.DaycareSeed); - Array.Reverse(data); - return BitConverter.ToString(data).Replace("-", string.Empty); - } - public override void SetDaycareRNGSeed(int loc, string seed) => Daycare.DaycareSeed = BitConverter.ToUInt64(Util.GetBytesFromHexString(seed), 0); + public override string GetDaycareRNGSeed(int loc) => Daycare.DaycareSeed.ToString("X16"); + public override void SetDaycareRNGSeed(int loc, string seed) => Daycare.DaycareSeed = Util.GetHexValue64(seed); #endregion } } diff --git a/PKHeX.Core/Saves/SAV8SWSH.cs b/PKHeX.Core/Saves/SAV8SWSH.cs index ccbd1e31a..31e02ccc6 100644 --- a/PKHeX.Core/Saves/SAV8SWSH.cs +++ b/PKHeX.Core/Saves/SAV8SWSH.cs @@ -163,10 +163,10 @@ public override StorageSlotFlag GetSlotFlags(int index) return StorageSlotFlag.None; team /= 6; - var val = (StorageSlotFlag)((int)StorageSlotFlag.BattleTeam1 << team); + var result = (StorageSlotFlag)((int)StorageSlotFlag.BattleTeam1 << team); if (TeamIndexes.GetIsTeamLocked(team)) - val |= StorageSlotFlag.Locked; - return val; + result |= StorageSlotFlag.Locked; + return result; } public override int CurrentBox { get => BoxLayout.CurrentBox; set => BoxLayout.CurrentBox = value; } diff --git a/PKHeX.Core/Saves/SAV_BEEF.cs b/PKHeX.Core/Saves/SAV_BEEF.cs index 9a5f8dd66..7d8908f2b 100644 --- a/PKHeX.Core/Saves/SAV_BEEF.cs +++ b/PKHeX.Core/Saves/SAV_BEEF.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -33,8 +34,8 @@ protected SAV_BEEF(int size, int biOffset) : base(size) /// public ulong TimeStampCurrent { - get => BitConverter.ToUInt64(Data, BlockInfoOffset); - set => BitConverter.GetBytes(value).CopyTo(Data, BlockInfoOffset); + get => ReadUInt64LittleEndian(Data.AsSpan(BlockInfoOffset)); + set => WriteUInt64LittleEndian(Data.AsSpan(BlockInfoOffset), value); } /// @@ -42,8 +43,8 @@ public ulong TimeStampCurrent /// public ulong TimeStampPrevious { - get => BitConverter.ToUInt64(Data, BlockInfoOffset); - set => BitConverter.GetBytes(value).CopyTo(Data, BlockInfoOffset); + get => ReadUInt64LittleEndian(Data.AsSpan(BlockInfoOffset)); + set => WriteUInt64LittleEndian(Data.AsSpan(BlockInfoOffset), value); } } } diff --git a/PKHeX.Core/Saves/SAV_STADIUM.cs b/PKHeX.Core/Saves/SAV_STADIUM.cs index 5576390b7..18dc11859 100644 --- a/PKHeX.Core/Saves/SAV_STADIUM.cs +++ b/PKHeX.Core/Saves/SAV_STADIUM.cs @@ -1,4 +1,8 @@ -namespace PKHeX.Core +using System; +using System.Buffers.Binary; +using System.Runtime.InteropServices; + +namespace PKHeX.Core { /// /// Base class for GB Era Stadium files. @@ -32,7 +36,7 @@ protected SAV_STADIUM(byte[] data, bool japanese, bool swap) : base(data) if (!swap) return; - BigEndian.SwapBytes32(Data); + ReverseEndianness(Data); IsPairSwapped = true; } @@ -76,7 +80,7 @@ protected sealed override byte[] GetFinalData() { var result = base.GetFinalData(); if (IsPairSwapped) - BigEndian.SwapBytes32(result = (byte[])result.Clone()); + ReverseEndianness(result = (byte[])result.Clone()); return result; } @@ -90,13 +94,23 @@ public virtual SlotGroup[] GetRegisteredTeams() return result; } - public sealed override string GetString(byte[] data, int offset, int length) => StringConverter12.GetString1(data, offset, length, Japanese); + public sealed override string GetString(ReadOnlySpan data) => StringConverter12.GetString(data, Japanese); - public sealed override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public sealed override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (PadToSize == 0) - PadToSize = maxLength + 1; - return StringConverter12.SetString1(value, maxLength, Japanese, PadToSize, (byte)PadWith); + return StringConverter12.SetString(destBuffer, value, maxLength, Japanese, option); + } + + /// + /// Some emulators emit with system architecture endianness (Little Endian) instead of the original Big Endian ordering. + /// This will efficiently swap 32-bit endianness for the entire span. + /// + /// Full savedata + private static void ReverseEndianness(Span data) + { + var uintArr = MemoryMarshal.Cast(data); + for (int i = 0; i < uintArr.Length; i++) + uintArr[i] = BinaryPrimitives.ReverseEndianness(uintArr[i]); } } } diff --git a/PKHeX.Core/Saves/SaveFile.cs b/PKHeX.Core/Saves/SaveFile.cs index ab888364a..fae0ab402 100644 --- a/PKHeX.Core/Saves/SaveFile.cs +++ b/PKHeX.Core/Saves/SaveFile.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -79,15 +80,15 @@ protected virtual byte[] GetFinalData() protected static byte[] GetData(byte[] data, int offset, int length) => data.Slice(offset, length); public void SetData(byte[] input, int offset) => SetData(Data, input, offset); - public void SetData(byte[] dest, byte[] input, int offset) + public void SetData(Span dest, Span input, int offset) { - input.CopyTo(dest, offset); + input.CopyTo(dest[offset..]); State.Edited = true; } - public abstract string GetString(byte[] data, int offset, int length); - public string GetString(int offset, int length) => GetString(Data, offset, length); - public abstract byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0); + public abstract string GetString(ReadOnlySpan data); + public string GetString(int offset, int length) => GetString(Data.AsSpan(offset, length)); + public abstract int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option); #endregion public virtual void CopyChangesFrom(SaveFile sav) => SetData(sav.Data, 0); @@ -145,20 +146,21 @@ public virtual ushort[] GetEventConsts() ushort[] Constants = new ushort[EventConstMax]; for (int i = 0; i < Constants.Length; i++) - Constants[i] = BitConverter.ToUInt16(Data, EventConst + (i * 2)); + Constants[i] = ReadUInt16LittleEndian(Data.AsSpan(EventConst + (i * 2))); return Constants; } /// All Event Constant values for the savegame - public virtual void SetEventConsts(ushort[] value) + public virtual void SetEventConsts(ReadOnlySpan value) { if (EventConstMax <= 0) return; if (value.Length != EventConstMax) return; + var span = Data.AsSpan(EventConst); for (int i = 0; i < value.Length; i++) - BitConverter.GetBytes(value[i]).CopyTo(Data, EventConst + (i * 2)); + WriteUInt16LittleEndian(span[(i*2)..], value[i]); } /// @@ -391,7 +393,7 @@ public void SetPartySlot(PKM pkm, byte[] data, int offset, PKMImportSetting trad WritePartySlot(pkm, data, offset); } - public void SetSlotFormatStored(PKM pkm, byte[] data, int offset, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) + public void SetSlotFormatStored(PKM pkm, Span data, int offset, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) { if (pkm.GetType() != PKMType) throw new ArgumentException($"PKM Format needs to be {PKMType} when setting to this Save File."); @@ -401,7 +403,7 @@ public void SetSlotFormatStored(PKM pkm, byte[] data, int offset, PKMImportSetti WriteSlotFormatStored(pkm, data, offset); } - public void SetBoxSlot(PKM pkm, byte[] data, int offset, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) + public void SetBoxSlot(PKM pkm, Span data, int offset, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) { if (pkm.GetType() != PKMType) throw new ArgumentException($"PKM Format needs to be {PKMType} when setting to this Save File."); @@ -442,7 +444,7 @@ public void DeletePartySlot(int slot) public abstract IReadOnlyList HeldItems { get; } protected virtual byte[] BoxBuffer => Data; protected virtual byte[] PartyBuffer => Data; - public virtual bool IsPKMPresent(byte[] data, int offset) => PKX.IsPKMPresent(data, offset); + public virtual bool IsPKMPresent(ReadOnlySpan data) => PKX.IsPKMPresent(data); public virtual PKM GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data)); public virtual PKM GetPartySlot(byte[] data, int offset) => GetDecryptedPKM(GetData(data, offset, SIZE_PARTY)); public virtual PKM GetStoredSlot(byte[] data, int offset) => GetDecryptedPKM(GetData(data, offset, SIZE_STORED)); @@ -453,10 +455,10 @@ public void DeletePartySlot(int slot) public virtual byte[] GetDataForParty(PKM pkm) => pkm.EncryptedPartyData; public virtual byte[] GetDataForBox(PKM pkm) => pkm.EncryptedBoxData; - public virtual void WriteSlotFormatStored(PKM pkm, byte[] data, int offset) => SetData(data, GetDataForFormatStored(pkm), offset); - public virtual void WriteSlotFormatParty(PKM pkm, byte[] data, int offset) => SetData(data, GetDataForFormatParty(pkm), offset); - public virtual void WritePartySlot(PKM pkm, byte[] data, int offset) => SetData(data, GetDataForParty(pkm), offset); - public virtual void WriteBoxSlot(PKM pkm, byte[] data, int offset) => SetData(data, GetDataForBox(pkm), offset); + public virtual void WriteSlotFormatStored(PKM pkm, Span data, int offset) => SetData(data, GetDataForFormatStored(pkm), offset); + public virtual void WriteSlotFormatParty(PKM pkm, Span data, int offset) => SetData(data, GetDataForFormatParty(pkm), offset); + public virtual void WritePartySlot(PKM pkm, Span data, int offset) => SetData(data, GetDataForParty(pkm), offset); + public virtual void WriteBoxSlot(PKM pkm, Span data, int offset) => SetData(data, GetDataForBox(pkm), offset); protected virtual void SetPartyValues(PKM pkm, bool isParty) { @@ -597,12 +599,12 @@ public void AddBoxData(IList data, int box, int index) public int NextOpenBoxSlot(int lastKnownOccupied = -1) { - var storage = BoxBuffer; + var storage = BoxBuffer.AsSpan(); int count = BoxSlotCount * BoxCount; for (int i = lastKnownOccupied + 1; i < count; i++) { int offset = GetBoxSlotOffset(i); - if (!IsPKMPresent(storage, offset)) + if (!IsPKMPresent(storage[offset..])) return i; } return StorageFullValue; @@ -743,15 +745,15 @@ public bool SwapBox(int box1, int box2) return true; } - private void SwapBox(int box1, int box2, byte[] boxData) + private void SwapBox(int box1, int box2, Span boxData) { int b1o = GetBoxOffset(box1); int b2o = GetBoxOffset(box2); int len = BoxSlotCount * SIZE_BOXSLOT; - byte[] b1 = new byte[len]; - Buffer.BlockCopy(boxData, b1o, b1, 0, len); - Buffer.BlockCopy(boxData, b2o, boxData, b1o, len); - Buffer.BlockCopy(b1, 0, boxData, b2o, len); + Span b1 = stackalloc byte[len]; + boxData.Slice(b1o, len).CopyTo(b1); + boxData.Slice(b2o, len).CopyTo(boxData[b1o..]); + b1.CopyTo(boxData[b2o..]); // Name string b1n = GetBoxName(box1); @@ -829,7 +831,7 @@ public int SortBoxes(int BoxStart = 0, int BoxEnd = -1, Func, i /// Count of deleted slots. public int ClearBoxes(int BoxStart = 0, int BoxEnd = -1, Func? deleteCriteria = null) { - var storage = BoxBuffer; + var storage = BoxBuffer.AsSpan(); if (BoxEnd < 0) BoxEnd = BoxCount - 1; @@ -843,7 +845,7 @@ public int ClearBoxes(int BoxStart = 0, int BoxEnd = -1, Func? delete if (IsSlotOverwriteProtected(i, p)) continue; var ofs = GetBoxSlotOffset(i, p); - if (!IsPKMPresent(storage, ofs)) + if (!IsPKMPresent(storage[ofs..])) continue; if (deleteCriteria != null) { @@ -871,7 +873,7 @@ public int ModifyBoxes(Action action, int BoxStart = 0, int BoxEnd = -1) if (BoxEnd < 0) BoxEnd = BoxCount - 1; - var storage = BoxBuffer; + var storage = BoxBuffer.AsSpan(); int modified = 0; for (int b = BoxStart; b <= BoxEnd; b++) { @@ -880,7 +882,7 @@ public int ModifyBoxes(Action action, int BoxStart = 0, int BoxEnd = -1) if (IsSlotOverwriteProtected(b, s)) continue; var ofs = GetBoxSlotOffset(b, s); - if (!IsPKMPresent(storage, ofs)) + if (!IsPKMPresent(storage[ofs..])) continue; var pk = GetBoxSlotAtIndex(b, s); action(pk); @@ -960,7 +962,7 @@ private bool SetConcatenatedBinary(byte[] data, int expectLength, int start = 0) public static class StorageUtil { - public static bool CompressStorage(this SaveFile sav, byte[] storage, out int storedCount, IList[] slotPointers) + public static bool CompressStorage(this SaveFile sav, Span storage, out int storedCount, IList[] slotPointers) { // keep track of empty slots, and only write them at the end if slots were shifted (no need otherwise). var empty = new List(); @@ -972,13 +974,13 @@ public static bool CompressStorage(this SaveFile sav, byte[] storage, out int st for (int i = 0; i < count; i++) { int offset = sav.GetBoxSlotOffset(i); - if (sav.IsPKMPresent(storage, offset)) + if (sav.IsPKMPresent(storage[offset..])) { if (ctr != i) // copy required { shiftedSlots = true; // appending empty slots afterwards is now required since a rewrite was done int destOfs = sav.GetBoxSlotOffset(ctr); - Buffer.BlockCopy(storage, offset, storage, destOfs, size); + storage[offset..(offset + size)].CopyTo(storage[destOfs..(destOfs + size)]); SlotPointerUtil.UpdateRepointFrom(ctr, i, slotPointers); } @@ -987,8 +989,7 @@ public static bool CompressStorage(this SaveFile sav, byte[] storage, out int st } // pop out an empty slot; save all unused data & preserve order - byte[] data = new byte[size]; - Buffer.BlockCopy(storage, offset, data, 0, size); + var data = storage.Slice(offset, size).ToArray(); empty.Add(data); } @@ -1001,7 +1002,7 @@ public static bool CompressStorage(this SaveFile sav, byte[] storage, out int st { var data = empty[i - ctr]; int offset = sav.GetBoxSlotOffset(i); - data.CopyTo(storage, offset); + data.CopyTo(storage[offset..]); } return true; diff --git a/PKHeX.Core/Saves/Storage/Bank7.cs b/PKHeX.Core/Saves/Storage/Bank7.cs index d2027640c..2898e752f 100644 --- a/PKHeX.Core/Saves/Storage/Bank7.cs +++ b/PKHeX.Core/Saves/Storage/Bank7.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -20,7 +21,7 @@ public sealed class Bank7 : BulkStorage private const int GroupNameSpacing = GroupNameSize + 2; private const int BankNameSpacing = BankNameSize + 2; - public ulong UID => BitConverter.ToUInt64(Data, 0); + public ulong UID => ReadUInt64LittleEndian(Data.AsSpan(0)); public string GetGroupName(int group) { @@ -38,7 +39,7 @@ private int BankCount set => Data[0x15E] = (byte)value; } - private int Year => BitConverter.ToUInt16(Data, 0x160); + private int Year => ReadUInt16LittleEndian(Data.AsSpan(0x160)); private int Month => Data[0x162]; private int Day => Data[0x163]; private int Hours => Data[0x164]; @@ -48,7 +49,7 @@ private int BankCount public override int GetBoxOffset(int box) => Box + (BoxDataSize * box); public override string GetBoxName(int box) => GetString(GetBoxNameOffset(box), BankNameSize / 2); public int GetBoxNameOffset(int box) => GetBoxOffset(box) + (SlotsPerBox * SIZE_STORED); - public int GetBoxIndex(int box) => BitConverter.ToUInt16(Data, GetBoxNameOffset(box) + BankNameSize); + public int GetBoxIndex(int box) => ReadUInt16LittleEndian(Data.AsSpan(GetBoxNameOffset(box) + BankNameSize)); private const int BoxStart = 0x17C; public static Bank7 GetBank7(byte[] data) => new(data, typeof(PK7), BoxStart); diff --git a/PKHeX.Core/Saves/Storage/BulkStorage.cs b/PKHeX.Core/Saves/Storage/BulkStorage.cs index 52ea07724..c6b19f45a 100644 --- a/PKHeX.Core/Saves/Storage/BulkStorage.cs +++ b/PKHeX.Core/Saves/Storage/BulkStorage.cs @@ -15,8 +15,6 @@ protected BulkStorage(byte[] data, Type t, int start, int slotsPerBox = 30) : ba blank = PKMConverter.GetBlank(t); var slots = (Data.Length - Box) / blank.SIZE_STORED; BoxCount = slots / SlotsPerBox; - - GetIsPKMPresent = PKX.GetFuncIsPKMPresent(blank); } protected readonly int SlotsPerBox; @@ -47,9 +45,6 @@ protected BulkStorage(byte[] data, Type t, int start, int slotsPerBox = 30) : ba public sealed override int NickLength => blank.NickLength; public bool IsBigEndian => blank is BK4 or XK3 or CK3; - private readonly Func GetIsPKMPresent; - public sealed override bool IsPKMPresent(byte[] data, int offset) => GetIsPKMPresent(data, offset); - public override int BoxCount { get; } protected override void SetChecksums() { } @@ -58,10 +53,10 @@ protected BulkStorage(byte[] data, Type t, int start, int slotsPerBox = 30) : ba public sealed override void SetBoxName(int box, string value) { } public sealed override int GetPartyOffset(int slot) => int.MinValue; - public override string GetString(byte[] data, int offset, int length) - => StringConverter.GetString(data, Generation, blank.Japanese, IsBigEndian, length, offset); + public override string GetString(ReadOnlySpan data) + => StringConverter.GetString(data, Generation, blank.Japanese, IsBigEndian); - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) - => StringConverter.SetString(value, Generation, blank.Japanese, IsBigEndian, maxLength, padTo: PadToSize, padWith: PadWith); + public override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) + => StringConverter.SetString(destBuffer, value, maxLength, option: option, generation: Generation, jp: blank.Japanese, isBigEndian: IsBigEndian, language: Language); } } diff --git a/PKHeX.Core/Saves/Storage/SAV4Ranch.cs b/PKHeX.Core/Saves/Storage/SAV4Ranch.cs index 75befceec..b2b168d8a 100644 --- a/PKHeX.Core/Saves/Storage/SAV4Ranch.cs +++ b/PKHeX.Core/Saves/Storage/SAV4Ranch.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Security.Cryptography; -using System.Text; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -61,14 +61,14 @@ public SAV4Ranch(byte[] data) : base(data, typeof(PK4), 0) * uint32_t name4; */ - var pkCountOffset = BigEndian.ToInt32(Data, 0x34) + 4; - SlotCount = BigEndian.ToInt32(Data, pkCountOffset); + var pkCountOffset = ReadInt32BigEndian(Data.AsSpan(0x34)) + 4; + SlotCount = ReadInt32BigEndian(Data.AsSpan(pkCountOffset)); BoxCount = (int)Math.Ceiling((decimal)SlotCount / SlotsPerBox); Box = pkCountOffset + 4; - FinalCountOffset = BigEndian.ToInt32(Data, 0x3C); - FinalCount = BigEndian.ToInt32(Data, FinalCountOffset); + FinalCountOffset = ReadInt32BigEndian(Data.AsSpan(0x3C)); + FinalCount = ReadInt32BigEndian(Data.AsSpan(FinalCountOffset)); } private readonly int FinalCount; @@ -76,7 +76,8 @@ public SAV4Ranch(byte[] data) : base(data, typeof(PK4), 0) protected override void SetChecksums() { - BigEndian.GetBytes(FinalCount).CopyTo(Data, FinalCountOffset); // ensure the final data is written if the user screws stuff up + // ensure the final data is written if the user screws stuff up + WriteInt32BigEndian(Data.AsSpan(FinalCountOffset), FinalCount); var goodlen = (FinalCountOffset + 4); Array.Clear(Data, goodlen, Data.Length - goodlen); @@ -86,16 +87,11 @@ protected override void SetChecksums() SetData(result, 0); } - public override string GetString(byte[] data, int offset, int length) => StringConverter4.GetBEString4(data, offset, length); + public override string GetString(ReadOnlySpan data) => StringConverter4GC.GetStringUnicode(data); - public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) + public override int SetString(Span destBuffer, ReadOnlySpan value, int maxLength, StringConverterOption option) { - if (value.Length > maxLength) - value = value[..maxLength]; // Hard cap - string temp = value - .PadRight(value.Length + 1, (char)0) // Null Terminator - .PadRight(PadToSize, (char)PadWith); // Padding - return Encoding.BigEndianUnicode.GetBytes(temp); + return StringConverter4GC.SetStringUnicode(value, destBuffer, maxLength, option); } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Saves/Substructures/Battle Videos/BV3.cs b/PKHeX.Core/Saves/Substructures/Battle Videos/BV3.cs index 7f25c1fe5..31f899d19 100644 --- a/PKHeX.Core/Saves/Substructures/Battle Videos/BV3.cs +++ b/PKHeX.Core/Saves/Substructures/Battle Videos/BV3.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -13,11 +14,11 @@ public sealed class BV3 : BattleVideo public readonly byte[] Data; - internal new static bool IsValid(byte[] data) + internal new static bool IsValid(ReadOnlySpan data) { if (data.Length != SIZE) return false; - var chk = BitConverter.ToUInt32(data, SIZE - 4); + var chk = ReadUInt32LittleEndian(data[(SIZE-4)..]); if (chk > 0xF7080) return false; // max if all are FF var expect = GetChecksum8(data); @@ -74,27 +75,27 @@ public void SetTeam(IReadOnlyList team, int teamIndex) public uint Seed { - get => BitConverter.ToUInt32(Data, 0x4E8); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x4E8); + get => ReadUInt32LittleEndian(Data.AsSpan(0x4E8)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x4E8), value); } public uint Mode { - get => BitConverter.ToUInt32(Data, 0x4EC); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x4EC); + get => ReadUInt32LittleEndian(Data.AsSpan(0x4EC)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x4EC), value); } // ... public uint Checksum { - get => BitConverter.ToUInt32(Data, SIZE - 4); - set => BitConverter.GetBytes(value).CopyTo(Data, SIZE - 4); + get => ReadUInt32LittleEndian(Data.AsSpan(SIZE - 4)); + set => WriteUInt32LittleEndian(Data.AsSpan(SIZE - 4), value); } public bool IsChecksumValid() => Checksum == GetChecksum8(Data); - public static uint GetChecksum8(byte[] data) + public static uint GetChecksum8(ReadOnlySpan data) { uint result = 0; for (int i = 0; i < data.Length - 4; i++) diff --git a/PKHeX.Core/Saves/Substructures/Battle Videos/BV6.cs b/PKHeX.Core/Saves/Substructures/Battle Videos/BV6.cs index 653e0cae4..4264dbb62 100644 --- a/PKHeX.Core/Saves/Substructures/Battle Videos/BV6.cs +++ b/PKHeX.Core/Saves/Substructures/Battle Videos/BV6.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; // ReSharper disable UnusedType.Local namespace PKHeX.Core @@ -15,11 +16,11 @@ public sealed class BV6 : BattleVideo public override IReadOnlyList BattlePKMs => PlayerTeams.SelectMany(t => t).ToArray(); public override int Generation => 6; - internal new static bool IsValid(byte[] data) + internal new static bool IsValid(ReadOnlySpan data) { if (data.Length != SIZE) return false; - return BitConverter.ToUInt64(data, 0xE18) != 0 && BitConverter.ToUInt16(data, 0xE12) == 0; + return ReadUInt64LittleEndian(data[0xE18..]) != 0 && ReadUInt16LittleEndian(data[0xE12..]) == 0; } public BV6(byte[] data) => Data = (byte[])data.Clone(); @@ -28,32 +29,33 @@ internal new static bool IsValid(byte[] data) public string Debug1 { - get => StringConverter.GetString6(Data, 0x6, 0x1A); - set => StringConverter.SetString6(value, 12, 13).CopyTo(Data, 0x6); + get => StringConverter6.GetString(Data.AsSpan(0x6, 0x1A)); + set => StringConverter6.SetString(Data.AsSpan(0x6, 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); } public string Debug2 { - get => StringConverter.GetString6(Data, 0x50, 0x1A); - set => StringConverter.SetString6(value, 12, 13).CopyTo(Data, 0x50); + get => StringConverter6.GetString(Data.AsSpan(0x50, 0x1A)); + set => StringConverter6.SetString(Data.AsSpan(0x50, 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); } - public ulong RNGConst1 { get => BitConverter.ToUInt64(Data, 0x1A0); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1A0); } - public ulong RNGConst2 { get => BitConverter.ToUInt64(Data, 0x1A4); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1A4); } - public ulong RNGSeed1 { get => BitConverter.ToUInt64(Data, 0x1A8); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1A8); } - public ulong RNGSeed2 { get => BitConverter.ToUInt64(Data, 0x1B0); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1B0); } + public ulong RNGConst1 { get => ReadUInt64LittleEndian(Data.AsSpan(0x1A0)); set => WriteUInt64LittleEndian(Data.AsSpan(0x1A0), value); } + public ulong RNGConst2 { get => ReadUInt64LittleEndian(Data.AsSpan(0x1A4)); set => WriteUInt64LittleEndian(Data.AsSpan(0x1A4), value); } + public ulong RNGSeed1 { get => ReadUInt64LittleEndian(Data.AsSpan(0x1A8)); set => WriteUInt64LittleEndian(Data.AsSpan(0x1A8), value); } + public ulong RNGSeed2 { get => ReadUInt64LittleEndian(Data.AsSpan(0x1B0)); set => WriteUInt64LittleEndian(Data.AsSpan(0x1B0), value); } - public int Background { get => BitConverter.ToInt32(Data, 0x1BC); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1BC); } - public int Unk1CE { get => BitConverter.ToUInt16(Data, 0x1CE); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1CE); } - public int IntroID { get => BitConverter.ToUInt16(Data, 0x1E4); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1E4); } - public int MusicID { get => BitConverter.ToUInt16(Data, 0x1F0); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1F0); } + public int Background { get => ReadInt32LittleEndian(Data.AsSpan(0x1BC)); set => WriteInt32LittleEndian(Data.AsSpan(0x1BC), value); } + public int Unk1CE { get => ReadUInt16LittleEndian(Data.AsSpan(0x1CE)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1CE), (ushort)value); } + public int IntroID { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E4)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E4), (ushort)value); } + public int MusicID { get => ReadUInt16LittleEndian(Data.AsSpan(0x1F0)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1F0), (ushort)value); } public string[] GetPlayerNames() { string[] trainers = new string[PlayerCount]; for (int i = 0; i < PlayerCount; i++) { - var str = StringConverter.GetString6(Data, 0xEC + (0x1A * i), 0x1A); + var span = Data.AsSpan(0xEC + (0x1A * i), 0x1A); + var str = StringConverter6.GetString(span); trainers[i] = string.IsNullOrWhiteSpace(str) ? NPC : str; } return trainers; @@ -66,8 +68,9 @@ public void SetPlayerNames(IReadOnlyList value) for (int i = 0; i < PlayerCount; i++) { + var span = Data.AsSpan(0xEC + (0x1A * i), 0x1A); string tr = value[i] == NPC ? string.Empty : value[i]; - StringConverter.SetString6(tr, 12, 13).CopyTo(Data, 0xEC + (0x1A * i)); + StringConverter6.SetString(span, tr.AsSpan(), 12, StringConverterOption.ClearZero); } } @@ -113,7 +116,7 @@ public void SetTeam(IReadOnlyList team, int t) } } - public int MatchYear { get => BitConverter.ToUInt16(Data, 0x2E50); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x2E50); } + public int MatchYear { get => ReadUInt16LittleEndian(Data.AsSpan(0x2E50)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2E50), (ushort)value); } public int MatchDay { get => Data[0x2E52]; set => Data[0x2E52] = (byte)value; } public int MatchMonth { get => Data[0x2E53]; set => Data[0x2E53] = (byte)value; } public int MatchHour { get => Data[0x2E54]; set => Data[0x2E54] = (byte)value; } @@ -121,7 +124,7 @@ public void SetTeam(IReadOnlyList team, int t) public int MatchSecond { get => Data[0x2E56]; set => Data[0x2E56] = (byte)value; } public int MatchFlags { get => Data[0x2E57]; set => Data[0x2E57] = (byte)value; } - public int UploadYear { get => BitConverter.ToUInt16(Data, 0x2E58); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x2E58); } + public int UploadYear { get => ReadUInt16LittleEndian(Data.AsSpan(0x2E58)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2E58), (ushort)value); } public int UploadDay { get => Data[0x2E5A]; set => Data[0x2E5A] = (byte)value; } public int UploadMonth { get => Data[0x2E5B]; set => Data[0x2E5B] = (byte)value; } public int UploadHour { get => Data[0x2E5C]; set => Data[0x2E5C] = (byte)value; } diff --git a/PKHeX.Core/Saves/Substructures/Battle Videos/BV7.cs b/PKHeX.Core/Saves/Substructures/Battle Videos/BV7.cs index 4bd3954ad..028ad851b 100644 --- a/PKHeX.Core/Saves/Substructures/Battle Videos/BV7.cs +++ b/PKHeX.Core/Saves/Substructures/Battle Videos/BV7.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -14,7 +15,7 @@ public sealed class BV7 : BattleVideo private readonly byte[] Data; public override IReadOnlyList BattlePKMs => PlayerTeams.SelectMany(t => t).ToArray(); - internal new static bool IsValid(byte[] data) => data.Length == SIZE; + internal new static bool IsValid(ReadOnlySpan data) => data.Length == SIZE; public BV7(byte[] data) => Data = (byte[])data.Clone(); @@ -64,7 +65,8 @@ public string[] GetPlayerNames() string[] trainers = new string[PlayerCount]; for (int i = 0; i < PlayerCount; i++) { - var str = StringConverter.GetString7(Data, 0x12C + (0x1A * i), 0x1A); + var span = Data.AsSpan(0x12C + +(0x1A * i), 0x1A); + var str = StringConverter7.GetString(span); trainers[i] = string.IsNullOrWhiteSpace(trainers[i]) ? NPC : str; } return trainers; @@ -78,11 +80,12 @@ public void SetPlayerNames(IReadOnlyList value) for (int i = 0; i < PlayerCount; i++) { string tr = value[i] == NPC ? string.Empty : value[i]; - StringConverter.SetString7(tr, 12, 13).CopyTo(Data, 0xEC + (0x1A * i)); + var span = Data.AsSpan(0x12C + +(0x1A * i), 0x1A); + StringConverter7.SetString(span, tr.AsSpan(), 12, 0, StringConverterOption.ClearZero); } } - private int MatchYear { get => BitConverter.ToUInt16(Data, 0x2BB0); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x2BB0); } + private int MatchYear { get => ReadUInt16LittleEndian(Data.AsSpan(0x2BB0)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2BB0), (ushort)value); } private int MatchDay { get => Data[0x2BB3]; set => Data[0x2BB3] = (byte)value; } private int MatchMonth { get => Data[0x2BB2]; set => Data[0x2BB2] = (byte)value; } private int MatchHour { get => Data[0x2BB4]; set => Data[0x2BB4] = (byte)value; } diff --git a/PKHeX.Core/Saves/Substructures/Battle Videos/BVRequestUtil.cs b/PKHeX.Core/Saves/Substructures/Battle Videos/BVRequestUtil.cs index 9a1e7b18b..bb4da9b22 100644 --- a/PKHeX.Core/Saves/Substructures/Battle Videos/BVRequestUtil.cs +++ b/PKHeX.Core/Saves/Substructures/Battle Videos/BVRequestUtil.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -19,7 +20,10 @@ public static ulong StrToU64(string input, out bool valid) { var chk = Pull(0, 4) >> 4; // first four chars are checksum bits var result = Pull(4, input.Length); // next 12 chars are the 70 value bits - var actual = Checksums.CRC16_CCITT(BitConverter.GetBytes(result)); + + Span temp = stackalloc byte[8]; + WriteUInt64LittleEndian(temp, result); + var actual = Checksums.CRC16_CCITT(temp); valid = chk == actual; return result; @@ -41,7 +45,9 @@ ulong Pull(int start, int count) public static string U64ToStr(ulong input, bool insertDash) { - uint chk = Checksums.CRC16_CCITT(BitConverter.GetBytes(input)); + Span temp = stackalloc byte[8]; + WriteUInt64LittleEndian(temp, input); + uint chk = Checksums.CRC16_CCITT(temp); var buff = new char[16]; int ctr = 15; Push(input, 12); // store value bits diff --git a/PKHeX.Core/Saves/Substructures/Battle Videos/BattleVideo.cs b/PKHeX.Core/Saves/Substructures/Battle Videos/BattleVideo.cs index 136e1d33d..f88bb7811 100644 --- a/PKHeX.Core/Saves/Substructures/Battle Videos/BattleVideo.cs +++ b/PKHeX.Core/Saves/Substructures/Battle Videos/BattleVideo.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace PKHeX.Core { @@ -20,7 +21,7 @@ public abstract class BattleVideo : IPokeGroup return null; } - public static bool IsValid(byte[] data) + public static bool IsValid(ReadOnlySpan data) { if (BV6.IsValid(data)) return true; diff --git a/PKHeX.Core/Saves/Substructures/Gen3/CXD/ShadowInfoEntryColo.cs b/PKHeX.Core/Saves/Substructures/Gen3/CXD/ShadowInfoEntryColo.cs index 1dbd23b03..f46b74e19 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/CXD/ShadowInfoEntryColo.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/CXD/ShadowInfoEntryColo.cs @@ -1,4 +1,7 @@ -namespace PKHeX.Core +using System; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core { public sealed class ShadowInfoEntryColo { @@ -8,8 +11,8 @@ public sealed class ShadowInfoEntryColo public ShadowInfoEntryColo() => Data = new byte[SIZE_ENTRY]; public ShadowInfoEntryColo(byte[] data) => Data = data; - public uint PID { get => BigEndian.ToUInt32(Data, 0x00); set => BigEndian.GetBytes(value).CopyTo(Data, 0x00); } - public int Met_Location { get => BigEndian.ToUInt16(Data, 0x06); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x06); } - public uint Unk08 { get => BigEndian.ToUInt32(Data, 0x08); set => BigEndian.GetBytes(value).CopyTo(Data, 0x08); } + public uint PID { get => ReadUInt32BigEndian(Data.AsSpan(0x00)); set => WriteUInt32BigEndian(Data.AsSpan(0x00), value); } + public int Met_Location { get => ReadUInt16BigEndian(Data.AsSpan(0x06)); set => WriteUInt16BigEndian(Data.AsSpan(0x06), (ushort)value); } + public uint Unk08 { get => ReadUInt32BigEndian(Data.AsSpan(0x08)); set => WriteUInt32BigEndian(Data.AsSpan(0x08), value); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/CXD/ShadowInfoEntryXD.cs b/PKHeX.Core/Saves/Substructures/Gen3/CXD/ShadowInfoEntryXD.cs index c72092fe3..a396e2549 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/CXD/ShadowInfoEntryXD.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/CXD/ShadowInfoEntryXD.cs @@ -1,4 +1,7 @@ -namespace PKHeX.Core +using System; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core { public abstract class ShadowInfoEntryXD { @@ -16,11 +19,16 @@ public abstract class ShadowInfoEntryXD public int IV_SPD { get => Data[0x0F]; set => Data[0x0F] = (byte)value; } public int IV_SPE { get => Data[0x10]; set => Data[0x10] = (byte)value; } - public int Species { get => BigEndian.ToUInt16(Data, 0x1A); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x1A); } - public uint PID { get => BigEndian.ToUInt32(Data, 0x1C); set => BigEndian.GetBytes(value).CopyTo(Data, 0x1C); } - public int Purification { get => BigEndian.ToInt32(Data, 0x24); set => BigEndian.GetBytes(value).CopyTo(Data, 0x24); } + public int Species { get => ReadUInt16BigEndian(Data.AsSpan(0x1A)); set => WriteUInt16BigEndian(Data.AsSpan(0x1A), (ushort)value); } + public uint PID { get => ReadUInt32BigEndian(Data.AsSpan(0x1C)); set => WriteUInt32BigEndian(Data.AsSpan(0x1C), value); } + public int Purification { get => ReadInt32BigEndian(Data.AsSpan(0x24)); set => WriteInt32BigEndian(Data.AsSpan(0x24), value); } + + public uint EXP + { + get => ReadUInt32BigEndian(Data.AsSpan(0x04)) >> 12; + set => WriteUInt32BigEndian(Data.AsSpan(0x04), (ReadUInt32BigEndian(Data.AsSpan(0x04)) & 0xFFF) | (value << 12)); + } - public uint EXP { get => BigEndian.ToUInt32(Data, 0x04) >> 12; set => BigEndian.GetBytes((BigEndian.ToUInt32(Data, 0x04) & 0xFFF) | (value << 12)).CopyTo(Data, 0x04); } public bool IsEmpty => Species == 0; public abstract byte Index { get; set; } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/CXD/ShadowInfoTableXD.cs b/PKHeX.Core/Saves/Substructures/Gen3/CXD/ShadowInfoTableXD.cs index 8b17ac300..fe5e2a73f 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/CXD/ShadowInfoTableXD.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/CXD/ShadowInfoTableXD.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; namespace PKHeX.Core { @@ -9,7 +10,7 @@ public sealed class ShadowInfoTableXD private readonly int SIZE_ENTRY; private const int MaxCount = 128; - public ShadowInfoTableXD(byte[] data, bool jp) + public ShadowInfoTableXD(ReadOnlySpan data, bool jp) { SIZE_ENTRY = GetEntrySize(jp); MaxLength = data.Length; @@ -23,9 +24,9 @@ public ShadowInfoTableXD(byte[] data, bool jp) public ShadowInfoTableXD(bool jp) : this(new byte[GetEntrySize(jp) * MaxCount], jp) { } - private ShadowInfoEntryXD GetEntry(byte[] data, int index, bool jp) + private ShadowInfoEntryXD GetEntry(ReadOnlySpan data, int index, bool jp) { - var slice = data.Slice(index * SIZE_ENTRY, SIZE_ENTRY); + var slice = data.Slice(index * SIZE_ENTRY, SIZE_ENTRY).ToArray(); return jp ? new ShadowInfoEntry3J(slice) : new ShadowInfoEntry3U(slice); } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/Events/WonderCard3.cs b/PKHeX.Core/Saves/Substructures/Gen3/Events/WonderCard3.cs index 91651328c..4753f25bc 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/Events/WonderCard3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/Events/WonderCard3.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -15,9 +16,9 @@ public WonderCard3(byte[] data) : base(data) throw new ArgumentException("Invalid size.", nameof(data)); } - public ushort CardID { get => BitConverter.ToUInt16(Data, 4); set => BitConverter.GetBytes(value).CopyTo(Data, 4); } - public ushort Icon { get => BitConverter.ToUInt16(Data, 6); set => BitConverter.GetBytes(value).CopyTo(Data, 6); } - public uint Count { get => BitConverter.ToUInt16(Data, 8); set => BitConverter.GetBytes(value).CopyTo(Data, 8); } + public ushort CardID { get => ReadUInt16LittleEndian(Data.AsSpan(4)); set => WriteUInt16LittleEndian(Data.AsSpan(4), value); } + public ushort Icon { get => ReadUInt16LittleEndian(Data.AsSpan(6)); set => WriteUInt16LittleEndian(Data.AsSpan(6), value); } + public uint Count { get => ReadUInt16LittleEndian(Data.AsSpan(8)); set => WriteUInt32LittleEndian(Data.AsSpan(8), value); } public byte Type { get => (byte)(Data[0xC] & 0b11); set => Data[0xC] = (byte)((Data[0xC] & ~0b11) | (value & 0b11)); } public byte Color { get => (byte)(Data[0xC] & 0b00111100); set => Data[0xC] = (byte)((Data[0xC] & ~0b00111100) | (value & 0b00111100)); } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/Events/WonderCard3Extra.cs b/PKHeX.Core/Saves/Substructures/Gen3/Events/WonderCard3Extra.cs index 8f1821abd..1386cde80 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/Events/WonderCard3Extra.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/Events/WonderCard3Extra.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -15,10 +16,10 @@ public WonderCard3Extra(byte[] data) : base(data) throw new ArgumentException("Invalid size.", nameof(data)); } - public ushort Wins { get => BitConverter.ToUInt16(Data, 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, 0x4); } - public ushort Losses { get => BitConverter.ToUInt16(Data, 0x6); set => BitConverter.GetBytes(value).CopyTo(Data, 0x6); } - public ushort Trades { get => BitConverter.ToUInt16(Data, 0x8); set => BitConverter.GetBytes(value).CopyTo(Data, 0x8); } - public ushort Unk { get => BitConverter.ToUInt16(Data, 0xA); set => BitConverter.GetBytes(value).CopyTo(Data, 0xA); } + public ushort Wins { get => ReadUInt16LittleEndian(Data.AsSpan(0x4)); set => WriteUInt16LittleEndian(Data.AsSpan(0x4), value); } + public ushort Losses { get => ReadUInt16LittleEndian(Data.AsSpan(0x6)); set => WriteUInt16LittleEndian(Data.AsSpan(0x6), value); } + public ushort Trades { get => ReadUInt16LittleEndian(Data.AsSpan(0x8)); set => WriteUInt16LittleEndian(Data.AsSpan(0x8), value); } + public ushort Unk { get => ReadUInt16LittleEndian(Data.AsSpan(0xA)); set => WriteUInt16LittleEndian(Data.AsSpan(0xA), value); } // u16 distributedMons[2][7] } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/Events/WonderNews3.cs b/PKHeX.Core/Saves/Substructures/Gen3/Events/WonderNews3.cs index 602d983a0..ee12edcb9 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/Events/WonderNews3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/Events/WonderNews3.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -15,7 +16,7 @@ public WonderNews3(byte[] data) : base(data) throw new ArgumentException("Invalid size.", nameof(data)); } - public ushort NewsID { get => BitConverter.ToUInt16(Data, 4); set => BitConverter.GetBytes(value).CopyTo(Data, 4); } + public ushort NewsID { get => ReadUInt16LittleEndian(Data.AsSpan(4)); set => WriteUInt16LittleEndian(Data.AsSpan(4), value); } public byte Flag { get => Data[6]; set => Data[6] = value; } public byte Color { get => Data[7]; set => Data[7] = value; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/Gen3MysteryData.cs b/PKHeX.Core/Saves/Substructures/Gen3/Gen3MysteryData.cs index 25846f6f3..59632f1e7 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/Gen3MysteryData.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/Gen3MysteryData.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -10,14 +11,14 @@ public abstract class Gen3MysteryData public uint Checksum { - get => BitConverter.ToUInt32(Data, 0); - set => BitConverter.GetBytes(value).CopyTo(Data, 0); + get => ReadUInt32LittleEndian(Data.AsSpan(0)); + set => WriteUInt32LittleEndian(Data.AsSpan(0), value); } public bool IsChecksumValid() => Checksum == GetChecksum(Data); public void FixChecksum() => Checksum = GetChecksum(Data); - private static uint GetChecksum(byte[] data) + private static uint GetChecksum(ReadOnlySpan data) { uint sum = 0; for (var i = 4; i < data.Length; i++) diff --git a/PKHeX.Core/Saves/Substructures/Gen3/HallFame3.cs b/PKHeX.Core/Saves/Substructures/Gen3/HallFame3.cs index be2be475c..f7c78c27b 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/HallFame3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/HallFame3.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -70,16 +71,15 @@ public HallFame3PKM(byte[] data, int offset, bool jp) private readonly int Offset; private readonly bool Japanese; - public int TID { get => BitConverter.ToUInt16(Data, 0 + Offset); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0 + Offset); } - public int SID { get => BitConverter.ToUInt16(Data, 2 + Offset); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 2 + Offset); } - public uint PID { get => BitConverter.ToUInt32(Data, 4 + Offset); set => BitConverter.GetBytes(value).CopyTo(Data, 4 + Offset); } - private int SpecLevel { get => BitConverter.ToUInt16(Data, 8 + Offset); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 8 + Offset); } - public string Nickname { get => GetString(Data, 10 + Offset, 10); set => SetString(value, 10).CopyTo(Data, 10 + Offset); } + public int TID { get => ReadUInt16LittleEndian(Data.AsSpan(0 + Offset)); set => WriteUInt16LittleEndian(Data.AsSpan(0 + Offset), (ushort)value); } + public int SID { get => ReadUInt16LittleEndian(Data.AsSpan(2 + Offset)); set => WriteUInt16LittleEndian(Data.AsSpan(2 + Offset), (ushort)value); } + public uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(4 + Offset)); set => WriteUInt32LittleEndian(Data.AsSpan(4 + Offset), value); } + private int SpecLevel { get => ReadUInt16LittleEndian(Data.AsSpan(8 + Offset)); set => WriteUInt16LittleEndian(Data.AsSpan(8 + Offset), (ushort)value); } + + private Span Nickname_Trash => Data.AsSpan(10 + Offset, 10); + public string Nickname { get => StringConverter3.GetString(Nickname_Trash, Japanese); set => StringConverter3.SetString(Nickname_Trash, value.AsSpan(), 10, Japanese, StringConverterOption.ClearZero); } public int Species { get => SpecLevel & 0x1FF; set => SpecLevel = (SpecLevel & 0xFE00) | value; } public int Level { get => SpecLevel >> 9; set => SpecLevel = (SpecLevel & 0x1FF) | (value << 9); } - - private string GetString(byte[] data, int offset, int length) => StringConverter3.GetString3(data, offset, length, Japanese); - private byte[] SetString(string value, int maxLength) => StringConverter3.SetString3(value, maxLength, Japanese, 10); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/PokeBlock3.cs b/PKHeX.Core/Saves/Substructures/Gen3/PokeBlock3.cs index a8650b2ae..3340f3584 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/PokeBlock3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/PokeBlock3.cs @@ -32,10 +32,9 @@ public void Maximize(bool create = false) public void SetBlock(byte[] data, int offset) => Data.CopyTo(data, offset); - public static PokeBlock3 GetBlock(byte[] data, int offset) + public static PokeBlock3 GetBlock(ReadOnlySpan data, int offset) { - byte[] result = new byte[SIZE]; - Array.Copy(data, offset, result, 0, SIZE); + byte[] result = data.Slice(offset, SIZE).ToArray(); return new PokeBlock3(result); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/PokeBlock3Case.cs b/PKHeX.Core/Saves/Substructures/Gen3/PokeBlock3Case.cs index a3a269999..598115203 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/PokeBlock3Case.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/PokeBlock3Case.cs @@ -1,3 +1,5 @@ +using System; + namespace PKHeX.Core { public sealed class PokeBlock3Case @@ -5,7 +7,7 @@ public sealed class PokeBlock3Case private const int Count = 40; public readonly PokeBlock3[] Blocks; - public PokeBlock3Case(byte[] data, int offset) + public PokeBlock3Case(ReadOnlySpan data, int offset) { Blocks = new PokeBlock3[Count]; for (int i = 0; i < Blocks.Length; i++) diff --git a/PKHeX.Core/Saves/Substructures/Gen3/RTC3.cs b/PKHeX.Core/Saves/Substructures/Gen3/RTC3.cs index 85912ad26..05dec15c4 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/RTC3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/RTC3.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -9,9 +10,9 @@ public sealed class RTC3 public RTC3(byte[] data) => Data = data; - public int Day { get => BitConverter.ToUInt16(Data, 0x00); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x00); } + public int Day { get => ReadUInt16LittleEndian(Data.AsSpan(0x00)); set => WriteUInt16LittleEndian(Data.AsSpan(0x00), (ushort)value); } public int Hour { get => Data[2]; set => Data[2] = (byte)value; } public int Minute { get => Data[3]; set => Data[3] = (byte)value; } public int Second { get => Data[4]; set => Data[4] = (byte)value; } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Saves/Substructures/Gen3/Record3.cs b/PKHeX.Core/Saves/Substructures/Gen3/Record3.cs index d3adc6f72..53f898380 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/Record3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/Record3.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -7,8 +8,8 @@ public sealed class Record3 { private readonly SAV3 SAV; - public uint GetRecord(int record) => BitConverter.ToUInt32(SAV.Large, GetRecordOffset(record)) ^ SAV.SecurityKey; - public void SetRecord(int record, uint value) => SAV.SetData(SAV.Large, BitConverter.GetBytes(value ^ SAV.SecurityKey), GetRecordOffset(record)); + public uint GetRecord(int record) => ReadUInt32LittleEndian(SAV.Large.AsSpan(GetRecordOffset(record))) ^ SAV.SecurityKey; + public void SetRecord(int record, uint value) => WriteUInt32LittleEndian(SAV.Large.AsSpan(GetRecordOffset(record)), value ^ SAV.SecurityKey); private int GetRecordOffset(int record) { diff --git a/PKHeX.Core/Saves/Substructures/Gen3/Roamer3.cs b/PKHeX.Core/Saves/Substructures/Gen3/Roamer3.cs index ff431929f..c4d9f2889 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/Roamer3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/Roamer3.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -23,26 +24,26 @@ public Roamer3(SAV3 sav) private uint IV32 { - get => BitConverter.ToUInt32(Data, Offset); - set => SAV.SetData(Data, BitConverter.GetBytes(value), Offset); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset), value); } public uint PID { - get => BitConverter.ToUInt32(Data, Offset + 4); - set => SAV.SetData(Data, BitConverter.GetBytes(value), Offset + 4); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 4)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 4), value); } public int Species { - get => SpeciesConverter.GetG4Species(BitConverter.ToInt16(Data, Offset + 8)); - set => SAV.SetData(Data, BitConverter.GetBytes((ushort)SpeciesConverter.GetG3Species(value)), Offset + 8); + get => SpeciesConverter.GetG4Species(ReadInt16LittleEndian(Data.AsSpan(Offset + 8))); + set => WriteInt16LittleEndian(Data.AsSpan(Offset + 8), (short)SpeciesConverter.GetG3Species(value)); } public int HP_Current { - get => BitConverter.ToInt16(Data, Offset + 10); - set => SAV.SetData(Data, BitConverter.GetBytes((short)value), Offset + 10); + get => ReadInt16LittleEndian(Data.AsSpan(Offset + 10)); + set => WriteInt16LittleEndian(Data.AsSpan(Offset + 10), (short)value); } public int CurrentLevel @@ -90,8 +91,8 @@ public int[] IVs /// Indication if the PID is shiny for the trainer. public bool IsShiny(uint pid) { - var val = (ushort)(SAV.SID ^ SAV.TID ^ (pid >> 16) ^ pid); - return val < 8; + var xor = (ushort)(SAV.SID ^ SAV.TID ^ (pid >> 16) ^ pid); + return xor < 8; } /// diff --git a/PKHeX.Core/Saves/Substructures/Gen3/SecretBase3.cs b/PKHeX.Core/Saves/Substructures/Gen3/SecretBase3.cs index 2c0246763..87b655ff5 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/SecretBase3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/SecretBase3.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -36,14 +37,14 @@ public int RegistryStatus public string OT_Name { - get => StringConverter3.GetString3(Data, Offset + 2, 7, Japanese); - set => StringConverter3.SetString3(value, 7, Japanese, 7).CopyTo(Data, Offset + 2); + get => StringConverter3.GetString(Data.AsSpan(Offset + 2, 7), Japanese); + set => StringConverter3.SetString(Data.AsSpan(Offset + 2, 7), value.AsSpan(), 7, Japanese, StringConverterOption.ClearFF); } public uint OT_ID { - get => BitConverter.ToUInt32(Data, Offset + 9); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 9); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 9)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 9), value); } public int OT_Class => Data[Offset + 9] % 5; @@ -51,8 +52,8 @@ public uint OT_ID public ushort SecretBasesReceived { - get => BitConverter.ToUInt16(Data, Offset + 0x0E); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x0E); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x0E)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x0E), value); } public byte TimesEntered { get => Data[Offset + 0x10]; set => Data[Offset + 0x10] = value; } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/SecretBase3Team.cs b/PKHeX.Core/Saves/Substructures/Gen3/SecretBase3Team.cs index 76bd55ada..c2968d476 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/SecretBase3Team.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/SecretBase3Team.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -36,27 +37,27 @@ public byte[] Write() private SecretBase3PKM GetPKM(int index) => new() { - PID = BitConverter.ToUInt32(Data, GetOffsetPID(index)), - Species = BitConverter.ToUInt16(Data, GetOffsetSpecies(index)), - HeldItem = BitConverter.ToUInt16(Data, GetOffsetItem(index)), - Move1 = BitConverter.ToUInt16(Data, GetOffsetMove(index, 0)), - Move2 = BitConverter.ToUInt16(Data, GetOffsetMove(index, 1)), - Move3 = BitConverter.ToUInt16(Data, GetOffsetMove(index, 2)), - Move4 = BitConverter.ToUInt16(Data, GetOffsetMove(index, 3)), - Level = Data[O_Level + index], - EVAll = Data[O_EV + index], + PID = ReadUInt32LittleEndian(Data.AsSpan(GetOffsetPID(index))), + Species = ReadUInt16LittleEndian(Data.AsSpan(GetOffsetSpecies(index))), + HeldItem = ReadUInt16LittleEndian(Data.AsSpan(GetOffsetItem(index))), + Move1 = ReadUInt16LittleEndian(Data.AsSpan(GetOffsetMove(index, 0))), + Move2 = ReadUInt16LittleEndian(Data.AsSpan(GetOffsetMove(index, 1))), + Move3 = ReadUInt16LittleEndian(Data.AsSpan(GetOffsetMove(index, 2))), + Move4 = ReadUInt16LittleEndian(Data.AsSpan(GetOffsetMove(index, 3))), + Level = Data[O_Level + index], + EVAll = Data[O_EV + index], }; private void SetPKM(int index) { var pk = Team[index]; - BitConverter.GetBytes(pk.PID).CopyTo(Data, GetOffsetPID(index)); - BitConverter.GetBytes((ushort)pk.Species).CopyTo(Data, GetOffsetSpecies(index)); - BitConverter.GetBytes((ushort)pk.HeldItem).CopyTo(Data, GetOffsetItem(index)); - BitConverter.GetBytes((ushort)pk.Move1).CopyTo(Data, GetOffsetMove(index, 0)); - BitConverter.GetBytes((ushort)pk.Move2).CopyTo(Data, GetOffsetMove(index, 1)); - BitConverter.GetBytes((ushort)pk.Move3).CopyTo(Data, GetOffsetMove(index, 2)); - BitConverter.GetBytes((ushort)pk.Move4).CopyTo(Data, GetOffsetMove(index, 3)); + WriteUInt32LittleEndian(Data.AsSpan(GetOffsetPID(index)), pk.PID); + WriteUInt16LittleEndian(Data.AsSpan(GetOffsetSpecies(index)), (ushort)pk.Species); + WriteUInt16LittleEndian(Data.AsSpan(GetOffsetItem(index)), (ushort)pk.HeldItem); + WriteUInt16LittleEndian(Data.AsSpan(GetOffsetMove(index, 0)), (ushort)pk.Move1); + WriteUInt16LittleEndian(Data.AsSpan(GetOffsetMove(index, 1)), (ushort)pk.Move2); + WriteUInt16LittleEndian(Data.AsSpan(GetOffsetMove(index, 2)), (ushort)pk.Move3); + WriteUInt16LittleEndian(Data.AsSpan(GetOffsetMove(index, 3)), (ushort)pk.Move4); Data[O_Level + index] = (byte)pk.Level; Data[O_EV + index] = (byte)pk.EVAll; } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/StrategyMemo.cs b/PKHeX.Core/Saves/Substructures/Gen3/StrategyMemo.cs index cd8a2ee24..e95ab5af3 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/StrategyMemo.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/StrategyMemo.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -17,10 +18,10 @@ public sealed class StrategyMemo public StrategyMemo(byte[] input, int offset, bool xd) { XD = xd; - int count = BigEndian.ToInt16(input, offset); + int count = ReadUInt16BigEndian(input.AsSpan(offset)); if (count > MAX_COUNT) count = MAX_COUNT; - _unk = BigEndian.ToUInt16(input, offset + 2); + _unk = ReadUInt16BigEndian(input.AsSpan(offset + 2)); Entries = new List(count); for (int i = 0; i < count; i++) @@ -32,17 +33,17 @@ public StrategyMemo(byte[] input, int offset, bool xd) private StrategyMemoEntry Read(byte[] input, int offset, int index) { - byte[] data = new byte[SIZE_ENTRY]; var ofs = 4 + offset + (SIZE_ENTRY * index); - Array.Copy(input, ofs, data, 0, SIZE_ENTRY); + var span = input.AsSpan(ofs, SIZE_ENTRY); + var data = span.ToArray(); return new StrategyMemoEntry(XD, data); } public byte[] Write() { var result = new byte[4 + (Entries.Count * SIZE_ENTRY)]; - BigEndian.GetBytes((short)Entries.Count).CopyTo(result, 0); - BigEndian.GetBytes((short)_unk).CopyTo(result, 2); + WriteInt16BigEndian(result.AsSpan(0), (short)Entries.Count); + WriteInt16BigEndian(result.AsSpan(2), (short)_unk); var count = Math.Min(MAX_COUNT, Entries.Count); for (int i = 0; i < count; i++) @@ -82,23 +83,23 @@ public int Species { get { - int val = BigEndian.ToUInt16(Data, 0) & 0x1FF; + int val = ReadUInt16BigEndian(Data.AsSpan(0)) & 0x1FF; return SpeciesConverter.GetG4Species(val); } set { value = SpeciesConverter.GetG3Species(value); - int cval = BigEndian.ToUInt16(Data, 0); + int cval = ReadUInt16BigEndian(Data.AsSpan(0)); cval &= 0xE00; value &= 0x1FF; cval |= value; - BigEndian.GetBytes((ushort)cval).CopyTo(Data, 0); + WriteUInt16BigEndian(Data.AsSpan(0x00), (ushort)cval); } } private bool Flag0 { get => Data[0] >> 6 == 1; set { Data[0] &= 0xBF; if (value) Data[0] |= 0x40; } } // Unused private bool Flag1 { get => Data[0] >> 7 == 1; set { Data[0] &= 0x7F; if (value) Data[0] |= 0x80; } } // Complete Entry - public int SID { get => BigEndian.ToUInt16(Data, 4); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 4); } - public int TID { get => BigEndian.ToUInt16(Data, 6); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 6); } - public uint PID { get => BigEndian.ToUInt32(Data, 8); set => BigEndian.GetBytes(value).CopyTo(Data, 8); } + public int SID { get => ReadUInt16BigEndian(Data.AsSpan(4)); set => WriteUInt16BigEndian(Data.AsSpan(4), (ushort)value); } + public int TID { get => ReadUInt16BigEndian(Data.AsSpan(6)); set => WriteUInt16BigEndian(Data.AsSpan(6), (ushort)value); } + public uint PID { get => ReadUInt32BigEndian(Data.AsSpan(8)); set => WriteUInt32BigEndian(Data.AsSpan(8), value); } public bool Seen { diff --git a/PKHeX.Core/Saves/Substructures/Gen4/HoneyTreeValue.cs b/PKHeX.Core/Saves/Substructures/Gen4/HoneyTreeValue.cs index 38f26e2cd..d1701a68a 100644 --- a/PKHeX.Core/Saves/Substructures/Gen4/HoneyTreeValue.cs +++ b/PKHeX.Core/Saves/Substructures/Gen4/HoneyTreeValue.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -11,7 +12,7 @@ public sealed class HoneyTreeValue public readonly byte[] Data; - public uint Time { get => BitConverter.ToUInt32(Data, 0); set => BitConverter.GetBytes(value).CopyTo(Data, 0); } + public uint Time { get => ReadUInt32LittleEndian(Data.AsSpan(0)); set => WriteUInt32LittleEndian(Data.AsSpan(0), value); } public int Slot { get => Data[4]; set => Data[4] = (byte)value; } public int SubTable { get => Data[5]; set => Data[5] = (byte)value; } // offset by 1 with respect to Group public int Group { get => Data[6]; set { Data[6] = (byte)value; SubTable = Math.Max(0, Group - 1); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen4/Poffin4.cs b/PKHeX.Core/Saves/Substructures/Gen4/Poffin4.cs index a1910f428..de70832aa 100644 --- a/PKHeX.Core/Saves/Substructures/Gen4/Poffin4.cs +++ b/PKHeX.Core/Saves/Substructures/Gen4/Poffin4.cs @@ -8,16 +8,13 @@ public sealed class Poffin4 public const int SIZE = 8; public readonly byte[] Data; - public Poffin4(byte[] data, int offset) + public Poffin4(byte[] data) => Data = data; + public Poffin4(byte[] data, int offset) : this(data.AsSpan(offset, SIZE).ToArray()) { - Data = new byte[SIZE]; - Array.Copy(data, offset, Data, 0, SIZE); } private const string Stats = nameof(Stats); - public Poffin4(byte[] data) => Data = data; - public PoffinFlavor4 Type{ get => (PoffinFlavor4)Data[0]; set => Data[0] = (byte)value; } [Category(Stats), Description("Cool Stat Boost")] diff --git a/PKHeX.Core/Saves/Substructures/Gen4/SAV4BlockDetection.cs b/PKHeX.Core/Saves/Substructures/Gen4/SAV4BlockDetection.cs index 13a1947fd..4bfc013e4 100644 --- a/PKHeX.Core/Saves/Substructures/Gen4/SAV4BlockDetection.cs +++ b/PKHeX.Core/Saves/Substructures/Gen4/SAV4BlockDetection.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -15,18 +16,18 @@ public static class SAV4BlockDetection /// Compares the footers of the two blocks to determine which is newest. /// /// 0=Primary, 1=Secondary. - public static int CompareFooters(byte[] data, int offset1, int offset2) + public static int CompareFooters(ReadOnlySpan data, int offset1, int offset2) { // Major Counters - var major1 = BitConverter.ToUInt32(data, offset1); - var major2 = BitConverter.ToUInt32(data, offset2); + var major1 = ReadUInt32LittleEndian(data[offset1..]); + var major2 = ReadUInt32LittleEndian(data[offset2..]); var result1 = CompareCounters(major1, major2); if (result1 != Same) return result1; // Minor Counters - var minor1 = BitConverter.ToUInt32(data, offset1 + 4); - var minor2 = BitConverter.ToUInt32(data, offset2 + 4); + var minor1 = ReadUInt32LittleEndian(data[(offset1 + 4)..]); + var minor2 = ReadUInt32LittleEndian(data[(offset2 + 4)..]); var result2 = CompareCounters(minor1, minor2); return result2 == Second ? Second : First; // Same -> First, shouldn't happen for valid saves. } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/BattleSubway5.cs b/PKHeX.Core/Saves/Substructures/Gen5/BattleSubway5.cs index 30bfd12ca..e64d3fdcb 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/BattleSubway5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/BattleSubway5.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -9,57 +10,57 @@ public sealed class BattleSubway5 : SaveBlock public int BP { - get => BitConverter.ToUInt16(Data, Offset); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset), (ushort)value); } // Normal public int SinglePast { - get => BitConverter.ToUInt16(Data, Offset + 0x08); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x08); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x08)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x08), (ushort)value); } public int SingleRecord { - get => BitConverter.ToUInt16(Data, Offset + 0x1A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x1A); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x1A)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x1A), (ushort)value); } public int DoublePast { - get => BitConverter.ToUInt16(Data, Offset + 0x0A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x0A); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x0A)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x0A), (ushort)value); } public int DoubleRecord { - get => BitConverter.ToUInt16(Data, Offset + 0x1C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x1C); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x1C)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x1C), (ushort)value); } public int MultiNPCPast { - get => BitConverter.ToUInt16(Data, Offset + 0x0C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x0C); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x0C)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x0C), (ushort)value); } public int MultiNPCRecord { - get => BitConverter.ToUInt16(Data, Offset + 0x1E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x1E); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x1E)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x1E), (ushort)value); } public int MultiFriendsPast { - get => BitConverter.ToUInt16(Data, Offset + 0x0E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x0E); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x0E)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x0E), (ushort)value); } public int MultiFriendsRecord { - get => BitConverter.ToUInt16(Data, Offset + 0x20); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x20); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x20)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x20), (ushort)value); } // Super Check @@ -72,50 +73,50 @@ public int SuperCheck // Super public int SuperSinglePast { - get => BitConverter.ToUInt16(Data, Offset + 0x12); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x12); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x12)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x12), (ushort)value); } public int SuperSingleRecord { - get => BitConverter.ToUInt16(Data, Offset + 0x24); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x24); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x24)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x24), (ushort)value); } public int SuperDoublePast { - get => BitConverter.ToUInt16(Data, Offset + 0x14); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x14); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x14)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x14), (ushort)value); } public int SuperDoubleRecord { - get => BitConverter.ToUInt16(Data, Offset + 0x26); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x26); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x26)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x26), (ushort)value); } public int SuperMultiNPCPast { - get => BitConverter.ToUInt16(Data, Offset + 0x16); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x16); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x16)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x16), (ushort)value); } public int SuperMultiNPCRecord { - get => BitConverter.ToUInt16(Data, Offset + 0x28); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x28); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x28)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x28), (ushort)value); } public int SuperMultiFriendsPast { - get => BitConverter.ToUInt16(Data, Offset + 0x18); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x18); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x18)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x18), (ushort)value); } public int SuperMultiFriendsRecord { - get => BitConverter.ToUInt16(Data, Offset + 0x2A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x2A); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x2A)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x2A), (ushort)value); } // TODO: Wifi?? diff --git a/PKHeX.Core/Saves/Substructures/Gen5/BoxLayout5.cs b/PKHeX.Core/Saves/Substructures/Gen5/BoxLayout5.cs index 6b9282b7c..fca1c788b 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/BoxLayout5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/BoxLayout5.cs @@ -1,4 +1,6 @@ -namespace PKHeX.Core +using System; + +namespace PKHeX.Core { public sealed class BoxLayout5 : SaveBlock { @@ -23,19 +25,18 @@ public void SetBoxWallpaper(int box, int value) Data[GetBoxWallpaperOffset(box)] = (byte)value; } + private Span GetBoxNameSpan(int box) => Data.AsSpan(GetBoxNameOffset(box), 0x14); + public string GetBoxName(int box) { if (box >= SAV.BoxCount) return string.Empty; - return SAV.GetString(GetBoxNameOffset(box), 0x14); + return SAV.GetString(GetBoxNameSpan(box)); } public void SetBoxName(int box, string value) { - if (value.Length > 0x26 / 2) - return; - var data = SAV.SetString(value + '\uFFFF', 0x14, 0x14, 0); - SAV.SetData(data, GetBoxNameOffset(box)); + SAV.SetString(GetBoxNameSpan(box), value.AsSpan(), 13, StringConverterOption.ClearZero); } public string this[int i] @@ -44,4 +45,4 @@ public void SetBoxName(int box, string value) set => SetBoxName(i, value); } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Saves/Substructures/Gen5/CGearBackground.cs b/PKHeX.Core/Saves/Substructures/Gen5/CGearBackground.cs index 4ea085f98..f1e0d3a39 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/CGearBackground.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/CGearBackground.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -64,7 +65,7 @@ public CGearBackground(byte[] data) ColorPalette = new int[ColorCount]; for (int i = 0; i < ColorPalette.Length; i++) - ColorPalette[i] = GetRGB555_16(BitConverter.ToUInt16(ColorData, i * 2)); + ColorPalette[i] = GetRGB555_16(ReadUInt16LittleEndian(ColorData.AsSpan(i * 2))); Tiles = new Tile[0xFF]; for (int i = 0; i < Tiles.Length; i++) @@ -102,7 +103,11 @@ private byte[] Write() Array.Copy(Tiles[i].Write(), 0, data, i*Tile.SIZE_TILE, Tile.SIZE_TILE); for (int i = 0; i < ColorPalette.Length; i++) - BitConverter.GetBytes(GetRGB555(ColorPalette[i])).CopyTo(data, 0x1FE0 + (i * 2)); + { + var value = GetRGB555(ColorPalette[i]); + var span = data.AsSpan(0x1FE0 + (i * 2)); + WriteUInt16LittleEndian(span, value); + } Array.Copy(Map.Write(), 0, data, 0x2000, 0x600); @@ -123,16 +128,15 @@ private static bool IsCGB(IReadOnlyList data) return true; } - private static byte[] CGBtoPSK(byte[] cgb) + private static byte[] CGBtoPSK(ReadOnlySpan cgb) { - byte[] psk = (byte[])cgb.Clone(); + byte[] psk = cgb.ToArray(); for (int i = 0x2000; i < 0x2600; i += 2) { - var tileVal = BitConverter.ToUInt16(cgb, i); + var span = psk.AsSpan(i); + var tileVal = ReadUInt16LittleEndian(span); int val = GetPSKValue(tileVal); - - psk[i] = (byte)val; - psk[i + 1] = (byte)(val >> 8); + WriteUInt16LittleEndian(span, (ushort)val); } return psk; } @@ -147,12 +151,12 @@ private static int GetPSKValue(ushort val) return tile + (15 * (tile / 17)) + 0xA0A0 + rot; } - private static byte[] PSKtoCGB(byte[] psk) + private static byte[] PSKtoCGB(ReadOnlySpan psk) { - byte[] cgb = (byte[])psk.Clone(); + byte[] cgb = psk.ToArray(); for (int i = 0x2000; i < 0x2600; i += 2) { - int val = BitConverter.ToUInt16(psk, i); + int val = ReadUInt16LittleEndian(psk[i..]); int index = ValToIndex(val); byte tile = (byte)index; @@ -374,12 +378,9 @@ private byte[] GetTileData(IReadOnlyList Palette) for (int i = 0; i < pixels; i++) { var choice = ColorChoices[i]; - var val = Palette[choice]; - var o = 4 * i; - data[o + 0] = (byte)(val & 0xFF); - data[o + 1] = (byte)(val >> 8 & 0xFF); - data[o + 2] = (byte)(val >> 16 & 0xFF); - data[o + 3] = (byte)(val >> 24 & 0xFF); + var value = Palette[choice]; + var span = data.AsSpan(4 * i, 4); + WriteInt32LittleEndian(span, value); } return data; } @@ -389,9 +390,8 @@ internal byte[] Write() byte[] data = new byte[SIZE_TILE]; for (int i = 0; i < data.Length; i++) { - var ofs = i * 2; - data[i] |= (byte)(ColorChoices[ofs + 0] & 0xF); - data[i] |= (byte)((ColorChoices[ofs + 1] & 0xF) << 4); + var span = ColorChoices.AsSpan(i * 2, 2); + data[i] = (byte)((span[0] & 0xF) | ((span[1] & 0xF) << 4)); } return data; } @@ -450,10 +450,10 @@ private static byte[] FlipY(IReadOnlyList data, int height, int bpp = 4) return result; } - internal int GetRotationValue(int[] tileColors) + internal int GetRotationValue(ReadOnlySpan tileColors) { // Check all rotation types - if (ColorChoices.SequenceEqual(tileColors)) + if (tileColors.SequenceEqual(ColorChoices)) return 0; if (IsMirrorX(tileColors)) @@ -466,7 +466,7 @@ internal int GetRotationValue(int[] tileColors) return -1; } - private bool IsMirrorX(int[] tileColors) + private bool IsMirrorX(ReadOnlySpan tileColors) { for (int i = 0; i < 64; i++) { @@ -477,7 +477,7 @@ private bool IsMirrorX(int[] tileColors) return true; } - private bool IsMirrorY(int[] tileColors) + private bool IsMirrorY(ReadOnlySpan tileColors) { for (int i = 0; i < 64; i++) { @@ -488,7 +488,7 @@ private bool IsMirrorY(int[] tileColors) return true; } - private bool IsMirrorXY(int[] tileColors) + private bool IsMirrorXY(ReadOnlySpan tileColors) { for (int i = 0; i < 64; i++) { diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Daycare5.cs b/PKHeX.Core/Saves/Substructures/Gen5/Daycare5.cs index a5a36252d..479943da9 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/Daycare5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/Daycare5.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -22,7 +23,7 @@ public sealed class Daycare5 : SaveBlock { if (SAV is not SAV5B2W2) return null; - return BitConverter.ToUInt64(Data, Offset + 0x1CC); + return ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x1CC)); } public void SetSeed(string value) @@ -37,10 +38,10 @@ public void SetSeed(string value) public int GetPKMOffset(int slot) => GetDaycareSlotOffset(slot) + 4; private int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(slot) + 4 + PokeCrypto.SIZE_5PARTY; - public bool? IsOccupied(int slot) => BitConverter.ToUInt32(Data, GetDaycareSlotOffset(slot)) == 1; - public void SetOccupied(int slot, bool occupied) => SAV.SetData(BitConverter.GetBytes((uint)(occupied ? 1 : 0)), GetDaycareSlotOffset(slot)); + public bool? IsOccupied(int slot) => ReadUInt32LittleEndian(Data.AsSpan(GetDaycareSlotOffset(slot))) == 1; + public void SetOccupied(int slot, bool occupied) => WriteUInt32LittleEndian(Data.AsSpan(GetDaycareSlotOffset(slot)), (uint)(occupied ? 1 : 0)); - public uint? GetEXP(int slot) => BitConverter.ToUInt32(Data, GetDaycareEXPOffset(slot)); - public void SetEXP(int slot, uint EXP) => SAV.SetData(BitConverter.GetBytes(EXP), GetDaycareEXPOffset(slot)); + public uint? GetEXP(int slot) => ReadUInt32LittleEndian(Data.AsSpan(GetDaycareEXPOffset(slot))); + public void SetEXP(int slot, uint EXP) => WriteUInt32LittleEndian(Data.AsSpan(GetDaycareEXPOffset(slot)), EXP); } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Entralink5.cs b/PKHeX.Core/Saves/Substructures/Gen5/Entralink5.cs index 11f329b6c..a84a99bc5 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/Entralink5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/Entralink5.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,14 +9,14 @@ public abstract class Entralink5 : SaveBlock public ushort WhiteForestLevel { - get => BitConverter.ToUInt16(Data, Offset + 0x0C); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x0C); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x0C)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x0C), value); } public ushort BlackCityLevel { - get => BitConverter.ToUInt16(Data, Offset + 0x0E); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x0E); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x0E)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x0E), value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/EntreeForest.cs b/PKHeX.Core/Saves/Substructures/Gen5/EntreeForest.cs index d608a86ed..956c9eabb 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/EntreeForest.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/EntreeForest.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -75,8 +76,8 @@ public int Unlock38Areas public uint EncryptionSeed { - get => BitConverter.ToUInt32(Data, EncryptionSeedOffset); - private set => BitConverter.GetBytes(value).CopyTo(Data, EncryptionSeedOffset); + get => ReadUInt32LittleEndian(Data.AsSpan(EncryptionSeedOffset)); + private set => WriteUInt32LittleEndian(Data.AsSpan(EncryptionSeedOffset), value); } public void UnlockAllAreas() diff --git a/PKHeX.Core/Saves/Substructures/Gen5/EntreeSlot.cs b/PKHeX.Core/Saves/Substructures/Gen5/EntreeSlot.cs index 3e662d80a..818142ae9 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/EntreeSlot.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/EntreeSlot.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -69,8 +70,8 @@ public sealed class EntreeSlot : ISpeciesForm /// public uint RawValue { - get => BitConverter.ToUInt32(Data, Offset); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset), value); } public void Delete() => RawValue = 0; diff --git a/PKHeX.Core/Saves/Substructures/Gen5/FestaBlock5.cs b/PKHeX.Core/Saves/Substructures/Gen5/FestaBlock5.cs index 63f8b699b..ade4588b7 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/FestaBlock5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/FestaBlock5.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -13,26 +14,26 @@ public sealed class FestaBlock5 : SaveBlock public ushort Hosted { - get => BitConverter.ToUInt16(Data, Offset + 0xF0); - set => BitConverter.GetBytes(Math.Min(MaxScore, value)).CopyTo(Data, Offset + 0xF0); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0xF0)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0xF0), Math.Min(MaxScore, value)); } public ushort Participated { - get => BitConverter.ToUInt16(Data, Offset + 0xF2); - set => BitConverter.GetBytes(Math.Min(MaxScore, value)).CopyTo(Data, Offset + 0xF2); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0xF2)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0xF2), Math.Min(MaxScore, value)); } public ushort Completed { - get => BitConverter.ToUInt16(Data, Offset + 0xF4); - set => BitConverter.GetBytes(Math.Min(MaxScore, value)).CopyTo(Data, Offset + 0xF4); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0xF4)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0xF4), Math.Min(MaxScore, value)); } public ushort TopScores { - get => BitConverter.ToUInt16(Data, Offset + 0xF6); - set => BitConverter.GetBytes(Math.Min(MaxScore, value)).CopyTo(Data, Offset + 0xF6); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0xF6)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0xF6), Math.Min(MaxScore, value)); } public byte WhiteEXP @@ -62,14 +63,14 @@ private static int GetMissionRecordOffset(int mission) public Funfest5Score GetMissionRecord(int mission) { - var raw = BitConverter.ToUInt32(Data, Offset + GetMissionRecordOffset(mission)); + var raw = ReadUInt32LittleEndian(Data.AsSpan(Offset + GetMissionRecordOffset(mission))); return new Funfest5Score(raw); } public void SetMissionRecord(int mission, Funfest5Score score) { var value = score.RawValue; - BitConverter.GetBytes(value).CopyTo(Data, Offset + GetMissionRecordOffset(mission)); + WriteUInt32LittleEndian(Data.AsSpan(Offset + GetMissionRecordOffset(mission)), value); } public bool IsFunfestMissionsUnlocked diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Misc5.cs b/PKHeX.Core/Saves/Substructures/Gen5/Misc5.cs index f258d15da..5eee8bcc6 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/Misc5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/Misc5.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,8 +9,8 @@ public abstract class Misc5 : SaveBlock, IGymTeamInfo public uint Money { - get => BitConverter.ToUInt32(Data, Offset); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset), value); } public int Badges @@ -20,8 +21,8 @@ public int Badges public ushort PokeTransferMinigameScore { - get => BitConverter.ToUInt16(Data, Offset + TransferMinigameScoreOffset); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + TransferMinigameScoreOffset); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + TransferMinigameScoreOffset)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + TransferMinigameScoreOffset), value); } protected abstract int BadgeVictoryOffset { get; } @@ -40,13 +41,13 @@ private int GetBadgeVictorySpeciesOffset(uint badge, uint slot) public ushort GetBadgeVictorySpecies(uint badge, uint slot) { var ofs = GetBadgeVictorySpeciesOffset(badge, slot); - return BitConverter.ToUInt16(Data, ofs); + return ReadUInt16LittleEndian(Data.AsSpan(ofs)); } public void SetBadgeVictorySpecies(uint badge, uint slot, ushort species) { var ofs = GetBadgeVictorySpeciesOffset(badge, slot); - SAV.SetData(BitConverter.GetBytes(species), ofs); + WriteUInt16LittleEndian(SAV.Data.AsSpan(ofs), species); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/MysteryBlock5.cs b/PKHeX.Core/Saves/Substructures/Gen5/MysteryBlock5.cs index a0428df68..a01d77629 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/MysteryBlock5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/MysteryBlock5.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -21,7 +22,7 @@ public EncryptedMysteryGiftAlbum GiftAlbum { get { - uint seed = BitConverter.ToUInt32(Data, SeedOffset); + uint seed = ReadUInt32LittleEndian(Data.AsSpan(SeedOffset)); byte[] wcData = SAV.GetData(Offset + FlagStart, 0xA90); // Encrypted, Decrypt return GetAlbum(seed, wcData); } @@ -30,7 +31,7 @@ public EncryptedMysteryGiftAlbum GiftAlbum var wcData = SetAlbum(value); // Write Back wcData.CopyTo(Data, Offset + FlagStart); - BitConverter.GetBytes(value.Seed).CopyTo(Data, SeedOffset); + WriteUInt32LittleEndian(Data.AsSpan(SeedOffset), value.Seed); } } @@ -48,8 +49,7 @@ private static EncryptedMysteryGiftAlbum GetAlbum(uint seed, byte[] wcData) // 12 PGFs for (int i = 0; i < Info.Gifts.Length; i++) { - var data = new byte[PGF.Size]; - Array.Copy(wcData, CardStart + (i * PGF.Size), data, 0, PGF.Size); + var data = wcData.AsSpan(CardStart + (i * PGF.Size), PGF.Size).ToArray(); Info.Gifts[i] = new PGF(data); } @@ -67,8 +67,9 @@ private static byte[] SetAlbum(EncryptedMysteryGiftAlbum value) 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(wcData, CardStart + (i * PGF.Size)); + value.Gifts[i].Data.CopyTo(span[(i * PGF.Size)..]); // Decrypted, Encrypt PokeCrypto.CryptArray(wcData, value.Seed); diff --git a/PKHeX.Core/Saves/Substructures/Gen5/PWTBlock5.cs b/PKHeX.Core/Saves/Substructures/Gen5/PWTBlock5.cs index 24a8b132b..b0ab00493 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/PWTBlock5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/PWTBlock5.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -13,7 +14,7 @@ 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 BitConverter.ToUInt16(Data, ofs); + return ReadUInt16LittleEndian(Data.AsSpan(ofs)); } public void SetPWTRecord(int id, ushort value) => SetPWTRecord((PWTRecordID)id, value); @@ -23,7 +24,7 @@ public void SetPWTRecord(PWTRecordID id, ushort value) if (id is < PWTRecordID.Normal or > PWTRecordID.MixMaster) throw new ArgumentOutOfRangeException(nameof(id)); int ofs = Offset + 0x5C + ((int)id * 2); - SAV.SetData(BitConverter.GetBytes(value), ofs); + WriteUInt16LittleEndian(Data.AsSpan(ofs), value); } } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/Gen5/PlayerData5.cs b/PKHeX.Core/Saves/Substructures/Gen5/PlayerData5.cs index e8e5616f3..9fb524f23 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/PlayerData5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/PlayerData5.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -10,22 +11,24 @@ public sealed class PlayerData5 : SaveBlock public PlayerData5(SAV5BW sav, int offset) : base(sav) => Offset = offset; public PlayerData5(SAV5B2W2 sav, int offset) : base(sav) => Offset = offset; + private Span OT_Trash => Data.AsSpan(Offset + 4, 0x10); + public string OT { - get => SAV.GetString(Offset + 0x4, 0x10); - set => SAV.SetString(value, SAV.OTLength).CopyTo(Data, Offset + 0x4); + get => SAV.GetString(OT_Trash); + set => SAV.SetString(OT_Trash, value.AsSpan(), SAV.OTLength, StringConverterOption.ClearZero); } public int TID { - get => BitConverter.ToUInt16(Data, Offset + 0x14 + 0); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x14 + 0); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x14 + 0)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x14 + 0), (ushort)value); } public int SID { - get => BitConverter.ToUInt16(Data, Offset + 0x14 + 2); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x14 + 2); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x14 + 2)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x14 + 2), (ushort)value); } public int Language @@ -50,8 +53,8 @@ public int Gender public int PlayedHours { - get => BitConverter.ToUInt16(Data, Offset + 0x24); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x24); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x24)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x24), (ushort)value); } public int PlayedMinutes @@ -68,26 +71,26 @@ public int PlayedSeconds public int M { - get => BitConverter.ToInt32(Data, Offset + 0x180); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x180); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x180)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x180), (ushort)value); } public int X { - get => BitConverter.ToUInt16(Data, Offset + 0x186); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x186); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x186)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x186), (ushort)value); } public int Z { - get => BitConverter.ToUInt16(Data, Offset + 0x18A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x18A); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x18A)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x18A), (ushort)value); } public int Y { - get => BitConverter.ToUInt16(Data, Offset + 0x18E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x18E); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x18E)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x18E), (ushort)value); } } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/Gen6/BoxLayout6.cs b/PKHeX.Core/Saves/Substructures/Gen6/BoxLayout6.cs index 1425d5e17..45c3ed0e0 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/BoxLayout6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/BoxLayout6.cs @@ -1,4 +1,6 @@ -namespace PKHeX.Core +using System; + +namespace PKHeX.Core { public sealed class BoxLayout6 : SaveBlock, IBoxDetailName, IBoxDetailWallpaper { @@ -38,13 +40,12 @@ public void SetBoxWallpaper(int box, int value) private int GetBoxNameOffset(int box) => Offset + (StringMaxByteCount * box); - public string GetBoxName(int box) => SAV.GetString(Data, GetBoxNameOffset(box), StringMaxByteCount); + public string GetBoxName(int box) => SAV.GetString(Data.AsSpan(GetBoxNameOffset(box), StringMaxByteCount)); public void SetBoxName(int box, string value) { - var data = SAV.SetString(value, StringMaxLength, StringMaxLength, 0); - var offset = GetBoxNameOffset(box) + (StringMaxByteCount * box); - SAV.SetData(data, offset); + var span = Data.AsSpan(GetBoxNameOffset(box) + (StringMaxByteCount * box), StringMaxByteCount); + SAV.SetString(span, value.AsSpan(), StringMaxLength, StringConverterOption.ClearZero); } public byte[] BoxFlags diff --git a/PKHeX.Core/Saves/Substructures/Gen6/Encount6.cs b/PKHeX.Core/Saves/Substructures/Gen6/Encount6.cs index 0b0f2ea75..cda924b6e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/Encount6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/Encount6.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -10,7 +11,7 @@ public sealed class Encount6 : SaveBlock { public Encount6(SaveFile SAV, int offset) : base(SAV) => Offset = offset; - public ushort RepelItemUsed { get => BitConverter.ToUInt16(Data, Offset + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x00); } + 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; } // 0x04 @@ -47,15 +48,15 @@ public sealed class PokeRadar6 public PokeRadar6(byte[] data) => Data = data; public override string ToString() => ((Species)PokeRadarSpecies).ToString(); - public ushort PokeRadarSpecies { get => BitConverter.ToUInt16(Data, 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); } - private ushort PokeRadarPacked { get => BitConverter.ToUInt16(Data, 0x02); set => BitConverter.GetBytes(value).CopyTo(Data, 0x02); } + public ushort PokeRadarSpecies { get => ReadUInt16LittleEndian(Data.AsSpan(0x00)); set => WriteUInt16LittleEndian(Data.AsSpan(0x00), value); } + private ushort PokeRadarPacked { get => ReadUInt16LittleEndian(Data.AsSpan(0x02)); set => WriteUInt16LittleEndian(Data.AsSpan(0x02), value); } public int PokeRadarCharge { get => PokeRadarPacked & 0x3FFF; set => PokeRadarPacked = (ushort)((PokeRadarPacked & ~0x3FFF) | Math.Min(MaxCharge, value)); } public bool PokeRadarFlag1 { get => PokeRadarPacked >> 14 != 0; set => PokeRadarPacked = (ushort)((PokeRadarPacked & ~(1 << 14)) | (value ? (1 << 14) : 0)); } public bool PokeRadarFlag2 { get => PokeRadarPacked >> 15 != 0; set => PokeRadarPacked = (ushort)((PokeRadarPacked & ~(1 << 15)) | (value ? (1 << 15) : 0)); } - public PokeRadarRecord GetRecord(int index) => PokeRadarRecord.ReadRecord(Data, GetRecordOffset(index)); - public void SetRecord(PokeRadarRecord record, int index) => record.WriteRecord(Data, GetRecordOffset(index)); + public PokeRadarRecord GetRecord(int index) => PokeRadarRecord.ReadRecord(Data.AsSpan(GetRecordOffset(index))); + public void SetRecord(PokeRadarRecord record, int index) => record.WriteRecord(Data.AsSpan(GetRecordOffset(index))); private static int GetRecordOffset(int index) { @@ -87,17 +88,17 @@ private PokeRadarRecord(ushort species, ushort count) Count = count; } - public static PokeRadarRecord ReadRecord(byte[] data, int ofs) + public static PokeRadarRecord ReadRecord(ReadOnlySpan data) { - var species = BitConverter.ToUInt16(data, ofs); - var count = BitConverter.ToUInt16(data, ofs + 2); + var species = ReadUInt16LittleEndian(data); + var count = ReadUInt16LittleEndian(data[2..]); return new PokeRadarRecord(species, count); } - public void WriteRecord(byte[] data, int ofs) + public void WriteRecord(Span data) { - BitConverter.GetBytes(Species).CopyTo(data, ofs); - BitConverter.GetBytes(Count).CopyTo(data, ofs + 2); + WriteUInt16LittleEndian(data, Species); + WriteUInt16LittleEndian(data[2..], Count); } } @@ -111,7 +112,7 @@ public sealed class Roamer6 public Roamer6(byte[] data) => Data = data; public override string ToString() => ((Species)Species).ToString(); - private ushort SpecForm { get => BitConverter.ToUInt16(Data, 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); } + private ushort SpecForm { get => ReadUInt16LittleEndian(Data.AsSpan(0x00)); set => WriteUInt16LittleEndian(Data.AsSpan(0x00), value); } public int Species { get => SpecForm & 0x3FF; set => SpecForm = (ushort)((SpecForm & ~0x3FF) | (value & 0x3FF)); } public bool Flag1 { get => SpecForm >> 14 != 0; set => SpecForm = (ushort)((SpecForm & 0xBFFF) | (value ? (1 << 14) : 0)); } public bool Flag2 { get => SpecForm >> 15 != 0; set => SpecForm = (ushort)((SpecForm & 0x7FFF) | (value ? (1 << 15) : 0)); } @@ -120,7 +121,7 @@ public sealed class Roamer6 private int Status { get => Data[0x07]; set => Data[0x07] = (byte)value; } public Roamer6State RoamStatus { get => (Roamer6State)((Status >> 4) & 0xF); set => Status = (Status & 0x0F) | (((int)value << 4) & 0xF0); } - public uint TimesEncountered { get => BitConverter.ToUInt32(Data, 0x24); set => BitConverter.GetBytes(value).CopyTo(Data, 0x24); } + public uint TimesEncountered { get => ReadUInt32LittleEndian(Data.AsSpan(0x24)); set => WriteUInt32LittleEndian(Data.AsSpan(0x24), value); } } public enum Roamer6State diff --git a/PKHeX.Core/Saves/Substructures/Gen6/GameTime6.cs b/PKHeX.Core/Saves/Substructures/Gen6/GameTime6.cs index 0dfd85539..64a7825f2 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/GameTime6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/GameTime6.cs @@ -1,17 +1,18 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { public sealed class GameTime6 : SaveBlock { public GameTime6(SaveFile sav, int offset) : base(sav) => Offset = offset; - public int ResumeYear { get => BitConverter.ToInt32(Data, Offset + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4); } + 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 => BitConverter.ToUInt32(Data, Offset + 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x18); } - public uint SecondsToFame { get => BitConverter.ToUInt32(Data, Offset + 0x20); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x20); } + 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); } } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/Gen6/ItemInfo6.cs b/PKHeX.Core/Saves/Substructures/Gen6/ItemInfo6.cs index 1d6657684..6a3fee94f 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/ItemInfo6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/ItemInfo6.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -14,17 +15,19 @@ public int[] SelectItems // UP,RIGHT,DOWN,LEFT get { + var span = Data.AsSpan(Offset + 10); int[] list = new int[BoundItemCount]; for (int i = 0; i < list.Length; i++) - list[i] = BitConverter.ToUInt16(Data, Offset + 10 + (2 * i)); + list[i] = ReadUInt16LittleEndian(span[(2 * i)..]); return list; } set { if (value.Length != BoundItemCount) throw new ArgumentException(nameof(value)); + var span = Data.AsSpan(Offset + 10); for (int i = 0; i < value.Length; i++) - BitConverter.GetBytes((ushort)value[i]).CopyTo(Data, Offset + 10 + (2 * i)); + WriteUInt16LittleEndian(span[(2 * i)..], (ushort)value[i]); } } @@ -33,18 +36,20 @@ public int[] RecentItems // Items recently interacted with (Give, Use) get { + var span = Data.AsSpan(Offset + 20); int[] list = new int[RecentItemCount]; for (int i = 0; i < list.Length; i++) - list[i] = BitConverter.ToUInt16(Data, Offset + 20 + (2 * i)); + list[i] = ReadUInt16LittleEndian(span[(2 * i)..]); return list; } set { if (value.Length != RecentItemCount) throw new ArgumentException(nameof(value)); + var span = Data.AsSpan(Offset + 20); for (int i = 0; i < value.Length; i++) - BitConverter.GetBytes((ushort)value[i]).CopyTo(Data, Offset + 20 + (2 * i)); + WriteUInt16LittleEndian(span[(2 * i)..], (ushort)value[i]); } } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Saves/Substructures/Gen6/LinkBlock6.cs b/PKHeX.Core/Saves/Substructures/Gen6/LinkBlock6.cs index e696359e2..f746aa7e6 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/LinkBlock6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/LinkBlock6.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -10,9 +11,9 @@ public sealed class LinkBlock6 : SaveBlock public byte[] GetLinkInfoData() => Data.Slice(Offset + 0x1FF, PL6.Size); public PL6 GetLinkInfo() => new(GetLinkInfoData()); - public void SetLinkInfoData(byte[] data) + public void SetLinkInfoData(ReadOnlySpan data) { - data.CopyTo(Data, Offset); + data.CopyTo(Data.AsSpan(Offset)); Checksum = GetCalculatedChecksum(); // [app,chk) } @@ -28,8 +29,8 @@ private ushort GetCalculatedChecksum() => Checksums.CRC16_CCITT(new ReadOnlySpan public ushort Checksum { - get => BitConverter.ToUInt16(Data, GetChecksumOffset()); - set => BitConverter.GetBytes(value).CopyTo(Data, GetChecksumOffset()); + get => ReadUInt16LittleEndian(Data.AsSpan(GetChecksumOffset())); + set => WriteUInt16LittleEndian(Data.AsSpan(GetChecksumOffset()), value); } } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/Gen6/MaisonBlock.cs b/PKHeX.Core/Saves/Substructures/Gen6/MaisonBlock.cs index 168243f41..6cb3e5f19 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/MaisonBlock.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/MaisonBlock.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -10,8 +11,8 @@ public sealed class MaisonBlock : SaveBlock // 5 * [u16*4: normal,super,normalStreak,superStreak] public const int MaisonStatCount = 20; - public ushort GetMaisonStat(int index) => BitConverter.ToUInt16(Data, Offset + 0x1C0 + (2 * index)); - public void SetMaisonStat(int index, ushort value) => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x1C0 + (2 * index)); + 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); 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 abc632601..b4d02e464 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/Misc6AO.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/Misc6AO.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -14,8 +15,8 @@ public sealed class Misc6AO : SaveBlock, IMisc6 public uint Money { - get => BitConverter.ToUInt32(Data, Offset + 0x8); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x8); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x8)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x8), value); } public int Badges @@ -26,8 +27,8 @@ public int Badges public int BP { - get => BitConverter.ToUInt16(Data, Offset + 0x30); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x30); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x30)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x30), (ushort)value); } public int Vivillon diff --git a/PKHeX.Core/Saves/Substructures/Gen6/Misc6XY.cs b/PKHeX.Core/Saves/Substructures/Gen6/Misc6XY.cs index 6f4334e89..ea026329c 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/Misc6XY.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/Misc6XY.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,8 +9,8 @@ public sealed class Misc6XY : SaveBlock, IMisc6 public uint Money { - get => BitConverter.ToUInt32(Data, Offset + 0x8); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x8); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x8)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x8), value); } public int Badges @@ -20,8 +21,8 @@ public int Badges public int BP { - get => BitConverter.ToUInt16(Data, Offset + 0x3C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x3C); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x3C)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x3C), (ushort)value); } public int Vivillon diff --git a/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6.cs b/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6.cs index 0c4f7ecde..77282e301 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,14 +9,14 @@ public class MyStatus6 : SaveBlock, IRegionOrigin public int TID { - get => BitConverter.ToUInt16(Data, Offset + 0); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0), (ushort)value); } public int SID { - get => BitConverter.ToUInt16(Data, Offset + 2); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 2); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 2)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 2), (ushort)value); } public int Game @@ -71,14 +72,14 @@ public byte Country public decimal Latitude // don't use the setters { - get => (BitConverter.ToInt16(Data, Offset + 0x28) * 180m) / 0x8000; - set => SAV.SetData(BitConverter.GetBytes((short)(value * 0x8000) / 180), Offset + 0x28); + get => (ReadInt16LittleEndian(Data.AsSpan(Offset + 0x28)) * 180m) / 0x8000; + set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0x28), (short)((value * 0x8000) / 180m)); } public decimal Longitude // don't use the setters { - get => (BitConverter.ToInt16(Data, Offset + 0x2A) * 180m) / 0x8000; - set => SAV.SetData(BitConverter.GetBytes((short)(value * 0x8000) / 180), Offset + 0x2A); + get => (ReadInt16LittleEndian(Data.AsSpan(Offset + 0x2A)) * 180m) / 0x8000; + set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0x2A), (short)((value * 0x8000) / 180m)); } public byte ConsoleRegion @@ -93,15 +94,18 @@ public int Language set => Data[Offset + 0x2D] = (byte)value; } + private Span OT_Trash => Data.AsSpan(Offset + 0x48, 0x1A); + public string OT { - get => SAV.GetString(Offset + 0x48, 0x1A); - set => SAV.SetData(SAV.SetString(value, SAV.OTLength), Offset + 0x48); + get => SAV.GetString(OT_Trash); + set => SAV.SetString(OT_Trash, value.AsSpan(), SAV.OTLength, StringConverterOption.ClearZero); } + private Span GetSayingSpan(int say) => Data.AsSpan(GetSayingOffset(say), SAV6.LongStringLength); private int GetSayingOffset(int say) => Offset + 0x7C + (SAV6.LongStringLength * say); - private string GetSaying(int say) => SAV.GetString(GetSayingOffset(say), SAV6.LongStringLength); - private void SetSaying(int say, string value) => SAV.SetData(SAV.SetString(value, SAV6.LongStringLength / 2), GetSayingOffset(say)); + private string GetSaying(int say) => SAV.GetString(GetSayingSpan(say)); + private void SetSaying(int say, string value) => SAV.SetString(GetSayingSpan(say), value.AsSpan(), SAV6.LongStringLength / 2, StringConverterOption.ClearZero); public string Saying1 { get => GetSaying(0); set => SetSaying(0, value); } public string Saying2 { get => GetSaying(1); set => SetSaying(1, value); } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6XY.cs b/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6XY.cs index bcc029055..a0fee5060 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6XY.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/MyStatus6XY.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -16,16 +17,18 @@ public TrainerFashion6 Fashion set => value.Write(Data, Offset + 0x30); } + private Span Nickname_Trash => Data.AsSpan(Offset + 0x62, SAV6.ShortStringLength); + public string OT_Nick { - get => SAV.GetString(Offset + 0x62, SAV6.ShortStringLength); - set => SAV.SetData(SAV.SetString(value, 12), Offset + 0x62); + get => SAV.GetString(Nickname_Trash); + set => SAV.SetString(Nickname_Trash, value.AsSpan(), 12, StringConverterOption.ClearZero); } public short EyeColor { - get => BitConverter.ToInt16(Data, Offset + 0x148); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x148); + get => ReadInt16LittleEndian(Data.AsSpan(Offset + 0x148)); + set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0x148), value); } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Saves/Substructures/Gen6/MysteryBlock6.cs b/PKHeX.Core/Saves/Substructures/Gen6/MysteryBlock6.cs index ed3daf7e8..21447eea2 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/MysteryBlock6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/MysteryBlock6.cs @@ -13,13 +13,13 @@ public sealed class MysteryBlock6 : SaveBlock public MysteryBlock6(SAV6XY sav, int offset) : base(sav) => Offset = offset; public MysteryBlock6(SAV6AO sav, int offset) : base(sav) => Offset = offset; - public bool[] GetReceivedFlags() => ArrayUtil.GitBitFlagArray(Data, Offset + FlagStart, MaxReceivedFlag); + public bool[] GetReceivedFlags() => ArrayUtil.GitBitFlagArray(Data.AsSpan(Offset + FlagStart), MaxReceivedFlag); public void SetReceivedFlags(bool[] value) { if (value.Length != MaxReceivedFlag) return; - ArrayUtil.SetBitFlagArray(Data, Offset + FlagStart, value); + ArrayUtil.SetBitFlagArray(Data.AsSpan(Offset + FlagStart), value); SAV.State.Edited = true; } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/PSS6.cs b/PKHeX.Core/Saves/Substructures/Gen6/PSS6.cs index ab699317d..aad52f630 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/PSS6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/PSS6.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -18,7 +19,7 @@ public static List GetPSSParse(SAV6 SAV) result.Add("----"); result.Add(headers[g]); result.Add("----"); - // uint count = BitConverter.ToUInt32(savefile, offset + 0x4E20); + // uint count = ReadUInt32LittleEndian(data.AsSpan(offset + 0x4E20)); ReadTrainers(result, data, offset, 100); offset += 0x5000; // Advance to next block } @@ -29,38 +30,39 @@ public static List GetPSSParse(SAV6 SAV) private static void ReadTrainers(ICollection result, byte[] data, int offset, int count) { int r_offset = offset; + const int size = 0xC8; for (int i = 0; i < count; i++) { - if (!ReadTrainer(result, data, r_offset)) + if (!ReadTrainer(result, data.AsSpan(r_offset, size))) break; // No data present here if (i > 0) result.Add(string.Empty); - r_offset += 0xC8; // Advance to next entry + r_offset += size; // Advance to next entry } } - private static bool ReadTrainer(ICollection result, byte[] Data, int ofs) + private static bool ReadTrainer(ICollection result, ReadOnlySpan data) { - ulong pssID = BitConverter.ToUInt64(Data, ofs); + ulong pssID = ReadUInt64LittleEndian(data); if (pssID == 0) return false; // no data - string otname = StringConverter.GetString6(Data, ofs + 8, 0x1A); - string message = StringConverter.GetString6(Data, ofs + 8 + 0x1A, 0x22); + string otname = StringConverter6.GetString(data.Slice(0x08, 0x1A)); + string message = StringConverter6.GetString(data.Slice(0x22, 0x22)); // Trim terminated - // uint unk1 = BitConverter.ToUInt32(savefile, r_offset + 0x44); - // ulong unk2 = BitConverter.ToUInt64(savefile, r_offset + 0x48); - // uint unk3 = BitConverter.ToUInt32(savefile, r_offset + 0x50); - // uint unk4 = BitConverter.ToUInt16(savefile, r_offset + 0x54); - byte regionID = Data[ofs + 0x56]; - byte countryID = Data[ofs + 0x57]; - byte game = Data[ofs + 0x5A]; - // ulong outfit = BitConverter.ToUInt64(savefile, r_offset + 0x5C); - int favpkm = BitConverter.ToUInt16(Data, ofs + 0x9C) & 0x7FF; + // uint unk1 = ReadUInt32LittleEndian(data[0x44..]); + // ulong unk2 = ReadUInt64LittleEndian(data[0x48..]); + // uint unk3 = ReadUInt32LittleEndian(data[0x50..]); + // uint unk4 = ReadUInt16LittleEndian(data[0x54..]); + byte regionID = data[0x56]; + byte countryID = data[0x57]; + byte game = data[0x5A]; + // ulong outfit = ReadUInt64LittleEndian(data.AsSpan(ofs + 0x5C)); + int favpkm = ReadUInt16LittleEndian(data[0x9C..]) & 0x7FF; string gamename = GetGameName(game); var (country, region) = GeoLocation.GetCountryRegionText(countryID, regionID, GameInfo.CurrentLanguage); diff --git a/PKHeX.Core/Saves/Substructures/Gen6/PlayTime6.cs b/PKHeX.Core/Saves/Substructures/Gen6/PlayTime6.cs index 1c3aca166..c192664b9 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/PlayTime6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/PlayTime6.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,8 +9,8 @@ public sealed class PlayTime6 : SaveBlock public int PlayedHours { - get => BitConverter.ToUInt16(Data, Offset); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset), (ushort)value); } public int PlayedMinutes @@ -24,7 +25,7 @@ public int PlayedSeconds set => Data[Offset + 3] = (byte)value; } - private uint LastSaved { get => BitConverter.ToUInt32(Data, Offset + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4); } + private uint LastSaved { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x4)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 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 460cedda0..b2af3fafc 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -14,8 +15,8 @@ public sealed class Puff6 : SaveBlock public int PuffCount { - get => BitConverter.ToInt32(Data, Offset + PuffSlots); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + PuffSlots); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + PuffSlots)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + PuffSlots), value); } public void Reset() @@ -42,7 +43,7 @@ public void MaxCheat(bool special = false) { for (int i = 0; i < PuffSlots; i++) Data[Offset + i] = (byte)((i % MaxPuffID) + 1); - Util.Shuffle(Data, Offset, Offset + PuffSlots, rnd); + Util.Shuffle(Data.AsSpan(), Offset, Offset + PuffSlots, rnd); } PuffCount = PuffSlots; } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/RecordBlock6.cs b/PKHeX.Core/Saves/Substructures/Gen6/RecordBlock6.cs index 8f0302925..c35f969bb 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/RecordBlock6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/RecordBlock6.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -49,9 +50,9 @@ public override int GetRecord(int recordID) switch (recordID) { case < 100: - return BitConverter.ToInt32(Data, ofs); + return ReadInt32LittleEndian(Data.AsSpan(ofs)); case < 200: - return BitConverter.ToInt16(Data, ofs); + return ReadInt16LittleEndian(Data.AsSpan(ofs)); default: Trace.Fail(nameof(recordID)); return 0; @@ -69,10 +70,10 @@ public override void SetRecord(int recordID, int value) switch (recordID) { case < 100: - BitConverter.GetBytes(value).CopyTo(Data, ofs); + WriteInt32LittleEndian(Data.AsSpan(ofs), value); break; case < 200: - BitConverter.GetBytes((ushort)value).CopyTo(Data, ofs); + WriteUInt16LittleEndian(Data.AsSpan(ofs), (ushort)value); break; default: Trace.Fail(nameof(recordID)); diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SangoInfoBlock.cs b/PKHeX.Core/Saves/Substructures/Gen6/SangoInfoBlock.cs index 2d5a8f744..9a2aa3e68 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SangoInfoBlock.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SangoInfoBlock.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -10,23 +11,23 @@ public sealed class SangoInfoBlock : SaveBlock public uint EonTicketReceivedMagic // 0x319B8 { - get => BitConverter.ToUInt32(Data, Offset + 0x63B8); - set => SAV.SetData(BitConverter.GetBytes(value), Offset + 0x63B8); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x63B8)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x63B8), value); } public string SecretBaseQRText // 0x319BC -- 17*u16 { - get => SAV.GetString(Offset + 0x63BC, 0x10); - set => SAV.SetData(SAV.SetString(value, 0x10), Offset + 0x63BC); + get => SAV.GetString(Data.AsSpan(Offset + 0x63BC, 36)); + set => SAV.SetString(Data.AsSpan(Offset + 0x63BC, 36), value.AsSpan(), 0x10, StringConverterOption.ClearZero); } public uint EonTicketSendMagic // 0x319DE { - get => BitConverter.ToUInt32(Data, Offset + 0x63DE); - set => SAV.SetData(BitConverter.GetBytes(value), Offset + 0x63DE); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x63DE)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x63DE), value); } public void ReceiveEon() => EonTicketReceivedMagic = EON_MAGIC; public void EnableSendEon() => EonTicketSendMagic = EON_MAGIC; } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6.cs b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6.cs index f99ad0a49..3c95f7a1e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -45,13 +46,13 @@ public SecretBase6(byte[] data, int offset = 0) public bool IsNew { get => Data[Offset] == 1; - set => BitConverter.GetBytes((ushort)(value ? 1 : 0)).CopyTo(Data, Offset); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset), (ushort)(value ? 1 : 0)); } private int RawLocation { - get => BitConverter.ToInt16(Data, Offset + 2); - set => BitConverter.GetBytes((short)value).CopyTo(Data, Offset + 2); + get => ReadInt16LittleEndian(Data.AsSpan(Offset + 2)); + set => WriteInt16LittleEndian(Data.AsSpan(Offset + 2), (short)value); } public int BaseLocation @@ -66,9 +67,9 @@ public int BaseLocation }; } - public SecretBase6GoodPlacement GetPlacement(int index) => new(Data, Offset + GetPlacementOffset(index)); + public SecretBase6GoodPlacement GetPlacement(int index) => new(Data.AsSpan(Offset + GetPlacementOffset(index))); - public void SetPlacement(int index, SecretBase6GoodPlacement value) => value.Write(Data, Offset + GetPlacementOffset(index)); + public void SetPlacement(int index, SecretBase6GoodPlacement value) => value.Write(Data.AsSpan(Offset + GetPlacementOffset(index))); private static int GetPlacementOffset(int index) { @@ -90,12 +91,13 @@ public byte BoppoyamaScore public string TrainerName { - get => StringConverter.GetString6(Data, Offset + 0x21A, NameLengthBytes); - set => StringConverter.SetString6(value, NameLength).CopyTo(Data, Offset + 0x21A); + get => StringConverter6.GetString(Data.AsSpan(Offset + 0x21A, NameLengthBytes)); + set => StringConverter6.SetString(Data.AsSpan(Offset + 0x21A, NameLengthBytes), value.AsSpan(), NameLength, StringConverterOption.ClearZero); } - private string GetMessage(int index) => StringConverter.GetString6(Data, Offset + 0x234 + (MessageLengthBytes * index), MessageLengthBytes); - private void SetMessage(int index, string value) => StringConverter.SetString6(value, MessageLength).CopyTo(Data, Offset + 0x234 + (MessageLengthBytes * index)); + private Span GetMessageSpan(int index) => Data.AsSpan(Offset + 0x234 + (MessageLengthBytes * index), MessageLengthBytes); + private string GetMessage(int index) => StringConverter6.GetString(GetMessageSpan(index)); + private void SetMessage(int index, string value) => StringConverter6.SetString(GetMessageSpan(index), value.AsSpan(), MessageLength, StringConverterOption.ClearZero); public string TeamName { get => GetMessage(0); set => SetMessage(0, value); } public string TeamSlogan { get => GetMessage(1); set => SetMessage(1, value); } @@ -106,20 +108,20 @@ public string TrainerName public SecretBase6Rank Rank { - get => (SecretBase6Rank) BitConverter.ToInt32(Data, Offset + 0x300); - set => BitConverter.GetBytes((int) value).CopyTo(Data, Offset + 0x300); + get => (SecretBase6Rank) ReadInt32LittleEndian(Data.AsSpan(Offset + 0x300)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x300), (int)value); } public uint TotalFlagsFromFriends { - get => BitConverter.ToUInt32(Data, Offset + 0x304); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x304); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x304)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x304), value); } public uint TotalFlagsFromOther { - get => BitConverter.ToUInt32(Data, Offset + 0x308); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x308); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x308)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x308), value); } public byte CollectedFlagsToday { get => Data[Offset + 0x30C]; set => Data[Offset + 0x30C] = value; } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodPlacement.cs b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodPlacement.cs index a50552bc2..6d5a92aba 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodPlacement.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodPlacement.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -15,26 +16,26 @@ public sealed class SecretBase6GoodPlacement public ushort Param1 { get; set; } public ushort Param2 { get; set; } - public SecretBase6GoodPlacement(byte[] data, int offset) + public SecretBase6GoodPlacement(ReadOnlySpan data) { - Good = BitConverter.ToUInt16(data, offset); - X = BitConverter.ToUInt16(data, offset + 2); - Y = BitConverter.ToUInt16(data, offset + 4); - Rotation = data[offset + 6]; + Good = ReadUInt16LittleEndian(data); + X = ReadUInt16LittleEndian(data[2..]); + Y = ReadUInt16LittleEndian(data[4..]); + Rotation = data[6]; - Param1 = BitConverter.ToUInt16(data, offset + 8); - Param2 = BitConverter.ToUInt16(data, offset + 10); + Param1 = ReadUInt16LittleEndian(data[8..]); + Param2 = ReadUInt16LittleEndian(data[10..]); } - public void Write(byte[] data, int offset) + public void Write(Span data) { - BitConverter.GetBytes(Good).CopyTo(data, offset); - BitConverter.GetBytes(X).CopyTo(data, offset + 2); - BitConverter.GetBytes(Y).CopyTo(data, offset + 4); + WriteUInt16LittleEndian(data, Good); + WriteUInt16LittleEndian(data[2..], X); + WriteUInt16LittleEndian(data[4..], Y); data[6] = Rotation; - BitConverter.GetBytes(Param1).CopyTo(data, offset + 8); - BitConverter.GetBytes(Param2).CopyTo(data, offset + 10); + WriteUInt16LittleEndian(data[8..], Param1); + WriteUInt16LittleEndian(data[10..], Param2); } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodStock.cs b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodStock.cs index 46ef928b2..181508bbc 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodStock.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6GoodStock.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -12,17 +13,17 @@ public sealed class SecretBase6GoodStock public ushort Count { get; set; } public bool IsNew { get; set; } - public SecretBase6GoodStock(byte[] data, int offset) + public SecretBase6GoodStock(ReadOnlySpan data) { - Count = BitConverter.ToUInt16(data, offset); - IsNew = data[offset + 2] != 0; + Count = ReadUInt16LittleEndian(data); + IsNew = data[2] != 0; } - public void Write(byte[] data, int offset) + public void Write(Span data) { - BitConverter.GetBytes(Count).CopyTo(data, offset); - data[offset + 2] = (byte)(IsNew ? 1 : 0); - data[offset + 3] = 0; + WriteUInt16LittleEndian(data, Count); + data[2] = (byte)(IsNew ? 1 : 0); + data[3] = 0; } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6PKM.cs b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6PKM.cs index 68d9dec8c..522858a42 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6PKM.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase/SecretBase6PKM.cs @@ -1,8 +1,9 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { - public sealed class SecretBase6PKM + public sealed class SecretBase6PKM : ISanityChecksum { public const int SIZE = 0x34; public readonly byte[] Data; @@ -12,32 +13,32 @@ public sealed class SecretBase6PKM public uint EncryptionConstant { - get => BitConverter.ToUInt32(Data, 0x00); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); + get => ReadUInt32LittleEndian(Data.AsSpan(0x00)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value); } public ushort Sanity { - get => BitConverter.ToUInt16(Data, 0x04); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); + get => ReadUInt16LittleEndian(Data.AsSpan(0x04)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x04), value); } public ushort Checksum { - get => BitConverter.ToUInt16(Data, 0x06); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x06); + get => ReadUInt16LittleEndian(Data.AsSpan(0x06)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x06), value); } public int Species { - get => BitConverter.ToUInt16(Data, 0x08); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08); + get => ReadUInt16LittleEndian(Data.AsSpan(0x08)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x08), (ushort)value); } public int HeldItem { - get => BitConverter.ToUInt16(Data, 0x0A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); + get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); } public int Ability { get => Data[0x0C]; set => Data[0x0C] = (byte)value; } @@ -45,8 +46,8 @@ public int HeldItem public uint PID { - get => BitConverter.ToUInt32(Data, 0x10); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x10); + get => ReadUInt32LittleEndian(Data.AsSpan(0x10)); + set => WriteUInt32LittleEndian(Data.AsSpan(0x10), value); } public int Nature { get => Data[0x14]; set => Data[0x14] = (byte)value; } @@ -64,26 +65,26 @@ public uint PID public int Move1 { - get => BitConverter.ToUInt16(Data, 0x1C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1C); + get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), (ushort)value); } public int Move2 { - get => BitConverter.ToUInt16(Data, 0x1E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1E); + get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); } public int Move3 { - get => BitConverter.ToUInt16(Data, 0x20); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x20); + get => ReadUInt16LittleEndian(Data.AsSpan(0x20)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x20), (ushort)value); } public int Move4 { - get => BitConverter.ToUInt16(Data, 0x22); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x22); + get => ReadUInt16LittleEndian(Data.AsSpan(0x22)); + set => WriteUInt16LittleEndian(Data.AsSpan(0x22), (ushort)value); } public int Move1_PPUps { get => Data[0x24]; set => Data[0x24] = (byte)value; } diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase6Block.cs b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase6Block.cs index 58a9cc5d7..9a9c9a0d9 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SecretBase6Block.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SecretBase6Block.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -20,8 +21,8 @@ public sealed class SecretBase6Block : 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 void SetGood(SecretBase6GoodStock good, int index) => good.Write(Data, Offset + GetGoodOffset(index)); + public SecretBase6GoodStock GetGood(int index) => new(Data.AsSpan(Offset + GetGoodOffset(index))); + public void SetGood(SecretBase6GoodStock good, int index) => good.Write(Data.AsSpan(Offset + GetGoodOffset(index))); private static int GetGoodOffset(int index) { @@ -32,15 +33,15 @@ private static int GetGoodOffset(int index) public void GiveAllGoods() { - var bytes = BitConverter.GetBytes(25u | (1 << 16)); // count: 25, new flag. + const uint value = 25u | (1 << 16); // count: 25, new flag. for (int i = 0; i < Count_Goods_Used; i++) - bytes.CopyTo(Data, Offset + GetGoodOffset(i)); + WriteUInt32LittleEndian(Data.AsSpan(Offset + GetGoodOffset(i)), value); } public ushort SecretBaseSelfLocation { - get => BitConverter.ToUInt16(Data, Offset + 800); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 800); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 800)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 800), value); } public const int OtherSecretBaseCount = 30; diff --git a/PKHeX.Core/Saves/Substructures/Gen6/Situation6.cs b/PKHeX.Core/Saves/Substructures/Gen6/Situation6.cs index ed9650471..c7b03c8d4 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/Situation6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/Situation6.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,45 +9,45 @@ public sealed class Situation6 : SaveBlock public int M { - get => BitConverter.ToUInt16(Data, Offset + 0x02); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x02)); set { - var val = BitConverter.GetBytes((ushort)value); - val.CopyTo(Data, Offset + 0x02); - val.CopyTo(Data, Offset + 0x02 + 0xF4); + var span = Data.AsSpan(Offset + 0x02); + WriteUInt16LittleEndian(span, (ushort)value); + WriteUInt16LittleEndian(span[0xF4..], (ushort)value); } } public float X { - get => BitConverter.ToSingle(Data, Offset + 0x10) / 18; + get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x10)) / 18; set { - var val = BitConverter.GetBytes(value * 18); - val.CopyTo(Data, Offset + 0x10); - val.CopyTo(Data, Offset + 0x10 + 0xF4); + var span = Data.AsSpan(Offset + 0x10); + WriteSingleLittleEndian(span, value * 18); + WriteSingleLittleEndian(span[0xF4..], value * 18); } } public float Z { - get => BitConverter.ToSingle(Data, Offset + 0x14); + get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x14)); set { - var val = BitConverter.GetBytes(value); - val.CopyTo(Data, Offset + 0x14); - val.CopyTo(Data, Offset + 0x14 + 0xF4); + var span = Data.AsSpan(Offset + 0x14); + WriteSingleLittleEndian(span, value * 18); + WriteSingleLittleEndian(span[0xF4..], value * 18); } } public float Y { - get => BitConverter.ToSingle(Data, Offset + 0x18) / 18; + get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x18)) / 18; set { - var val = BitConverter.GetBytes(value * 18); - val.CopyTo(Data, Offset + 0x18); - val.CopyTo(Data, Offset + 0x18 + 0xF4); + var span = Data.AsSpan(Offset + 0x18); + WriteSingleLittleEndian(span, value * 18); + WriteSingleLittleEndian(span[0xF4..], value * 18); } } @@ -57,4 +58,4 @@ public int Style set => Data[Offset + 0x14D] = (byte)value; } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Saves/Substructures/Gen6/SubEventLog6.cs b/PKHeX.Core/Saves/Substructures/Gen6/SubEventLog6.cs index b98b924eb..bcec37693 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SubEventLog6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SubEventLog6.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -34,13 +35,13 @@ private int GetBadgeVictorySpeciesOffset(uint badge, uint slot) public ushort GetBadgeVictorySpecies(uint badge, uint slot) { var ofs = GetBadgeVictorySpeciesOffset(badge, slot); - return BitConverter.ToUInt16(Data, ofs); + return ReadUInt16LittleEndian(Data.AsSpan(ofs)); } public void SetBadgeVictorySpecies(uint badge, uint slot, ushort species) { var ofs = GetBadgeVictorySpeciesOffset(badge, slot); - SAV.SetData(BitConverter.GetBytes(species), ofs); + WriteUInt16LittleEndian(Data.AsSpan(ofs), species); } } @@ -54,8 +55,8 @@ public sealed class SubEventLog6XY : SubEventLog6 // u8[0x28] chateau data private ushort ChateauValue { - get => BitConverter.ToUInt16(Data, Offset); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset), value); } public ushort ChateauRank @@ -121,14 +122,14 @@ public sealed class SubEventLog6AO : SubEventLog6 // u16 SeasideCyclingRoadTimeMilliseconds 29C public ushort SeasideCyclingRoadTimeMilliseconds { - get => BitConverter.ToUInt16(Data, Offset + 0x29C); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x29C); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x29C)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x29C), value); } // u16 SeasideCyclingRoadCollisions 29E public ushort SeasideCyclingRoadCollisions { - get => BitConverter.ToUInt16(Data, Offset + 0x29E); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x29E); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x29E)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 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 65fce7d80..5f97978bf 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/SuperTrainBlock.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/SuperTrainBlock.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -93,7 +94,7 @@ public float GetTime1(int index) if ((uint) index >= MAX) throw new ArgumentOutOfRangeException(nameof(index)); - return BitConverter.ToSingle(Data, Offset + 0x08 + (4 * index)); + return ReadSingleLittleEndian(Data.AsSpan(Offset + 0x08 + (4 * index))); } /// @@ -106,7 +107,7 @@ public void SetTime1(int index, float value = 0) if ((uint)index >= MAX) throw new ArgumentOutOfRangeException(nameof(index)); - BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08 + (4 * index)); + WriteSingleLittleEndian(Data.AsSpan(Offset + 0x08 + (4 * index)), value); } /// @@ -118,7 +119,7 @@ public float GetTime2(int index) if ((uint)index >= MAX) throw new ArgumentOutOfRangeException(nameof(index)); - return BitConverter.ToSingle(Data, Offset + 0xC8 + (4 * index)); + return ReadSingleLittleEndian(Data.AsSpan(Offset + 0xC8 + (4 * index))); } /// @@ -131,7 +132,7 @@ public void SetTime2(int index, float value = 0) if ((uint)index >= MAX) throw new ArgumentOutOfRangeException(nameof(index)); - BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xC8 + (4 * index)); + WriteSingleLittleEndian(Data.AsSpan(Offset + 0xC8 + (4 * index)), value); } /// @@ -230,8 +231,8 @@ public void RemoveBag(int index) public uint TutorialIndex { - get => BitConverter.ToUInt32(Data, Offset + 0x314); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x314); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x314)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x314), value); } /// @@ -298,8 +299,8 @@ public SuperTrainingSpeciesRecord(byte[] data, int offset) /// public int Species { - get => BitConverter.ToUInt16(Data, Offset + 0); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0), (ushort)value); } /// diff --git a/PKHeX.Core/Saves/Substructures/Gen6/TrainerFashion6.cs b/PKHeX.Core/Saves/Substructures/Gen6/TrainerFashion6.cs index 1f18d0a31..3acd856f8 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/TrainerFashion6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/TrainerFashion6.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -9,12 +10,14 @@ public abstract class TrainerFashion6 protected uint data2; protected uint data3; - protected TrainerFashion6(byte[] data, int offset) + protected TrainerFashion6(ReadOnlySpan data, int offset) : this(data[offset..]) { } + + private TrainerFashion6(ReadOnlySpan span) { - data0 = BitConverter.ToUInt32(data, 0 + offset); - data1 = BitConverter.ToUInt32(data, 4 + offset); - data2 = BitConverter.ToUInt32(data, 8 + offset); - data3 = BitConverter.ToUInt32(data, 12 + offset); + data0 = ReadUInt32LittleEndian(span); + data1 = ReadUInt32LittleEndian(span[04..]); + data2 = ReadUInt32LittleEndian(span[08..]); + data3 = ReadUInt32LittleEndian(span[12..]); } public static TrainerFashion6 GetFashion(byte[] data, int offset, int gender) @@ -26,10 +29,11 @@ public static TrainerFashion6 GetFashion(byte[] data, int offset, int gender) public void Write(byte[] data, int offset) { - BitConverter.GetBytes(data0).CopyTo(data, 0 + offset); - BitConverter.GetBytes(data1).CopyTo(data, 4 + offset); - BitConverter.GetBytes(data2).CopyTo(data, 8 + offset); - BitConverter.GetBytes(data3).CopyTo(data, 12 + offset); + var span = data.AsSpan(offset); + WriteUInt32LittleEndian(span, data0); + WriteUInt32LittleEndian(span[04..], data1); + WriteUInt32LittleEndian(span[08..], data2); + WriteUInt32LittleEndian(span[12..], data3); } protected static uint GetBits(uint value, int startPos, int bits) diff --git a/PKHeX.Core/Saves/Substructures/Gen7/BattleTree7.cs b/PKHeX.Core/Saves/Substructures/Gen7/BattleTree7.cs index dd4c62d05..e79da0015 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/BattleTree7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/BattleTree7.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -14,7 +15,7 @@ public int GetTreeStreak(int battletype, bool super, bool max) throw new ArgumentOutOfRangeException(nameof(battletype)); var offset = GetStreakOffset(battletype, super, max); - return BitConverter.ToUInt16(Data, Offset + offset); + return ReadUInt16LittleEndian(Data.AsSpan(Offset + offset)); } public void SetTreeStreak(int value, int battletype, bool super, bool max) @@ -26,7 +27,7 @@ public void SetTreeStreak(int value, int battletype, bool super, bool max) value = ushort.MaxValue; var offset = GetStreakOffset(battletype, super, max); - BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + offset); + WriteUInt16LittleEndian(Data.AsSpan(Offset + offset), (ushort)value); } private static int GetStreakOffset(int battletype, bool super, bool max) @@ -46,9 +47,9 @@ public BattleTreeTrainer GetTrainer(in int index) if ((uint)index >= ScoutCount) throw new ArgumentOutOfRangeException(nameof(index)); - var id = BitConverter.ToInt16(Data, Offset + 0x24 + (index * 2)); - var p1 = BitConverter.ToInt16(Data, Offset + 0x88 + (index * 2)); - var p2 = BitConverter.ToInt16(Data, Offset + 0xEC + (index * 2)); + 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 a1 = (sbyte)Data[Offset + 0x154 + index]; var a2 = (sbyte)Data[Offset + 0x186 + index]; @@ -63,9 +64,9 @@ public void SetTrainer(BattleTreeTrainer tr, in int index) if ((uint)index >= ScoutCount) throw new ArgumentOutOfRangeException(nameof(index)); - BitConverter.GetBytes(tr.ID).CopyTo(Data, Offset + 0x24 + (index * 2)); - BitConverter.GetBytes(tr.Poke1.ID).CopyTo(Data, Offset + 0x88 + (index * 2)); - BitConverter.GetBytes(tr.Poke2.ID).CopyTo(Data, Offset + 0xEC + (index * 2)); + 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); Data[Offset + 0x154 + index] = (byte)tr.Poke1.AbilityIndex; Data[Offset + 0x186 + index] = (byte)tr.Poke2.AbilityIndex; @@ -73,8 +74,8 @@ public void SetTrainer(BattleTreeTrainer tr, in int index) public int Music { - get => BitConverter.ToInt32(Data, Offset + 0x18); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x18); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x18)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x18), value); } public BattleTreeTrainer[] ScoutedTrainers diff --git a/PKHeX.Core/Saves/Substructures/Gen7/BoxLayout7.cs b/PKHeX.Core/Saves/Substructures/Gen7/BoxLayout7.cs index 56bc4b98a..f32bf4682 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/BoxLayout7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/BoxLayout7.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -37,19 +38,11 @@ public void SetBoxWallpaper(int box, int value) 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); - - public string GetBoxName(int box) - { - return SAV.GetString(Data, GetBoxNameOffset(box), SAV6.LongStringLength); - } - - public void SetBoxName(int box, string value) - { - var data = SAV.SetString(value, StringMaxLength, StringMaxLength, 0); - var offset = GetBoxNameOffset(box - 1) + (SAV6.LongStringLength); - SAV.SetData(data, offset); - } + public string GetBoxName(int box) => SAV.GetString(GetBoxNameSpan(box)); + public void SetBoxName(int box, string value) => SAV.SetString(GetBoxNameSpan(box), value.AsSpan(), StringMaxLength, StringConverterOption.ClearZero); public byte[] BoxFlags { @@ -79,7 +72,7 @@ public void LoadBattleTeams() { for (int i = 0; i < TeamCount * 6; i++) { - short val = BitConverter.ToInt16(Data, Offset + BattleBoxFlags + (i * 2)); + short val = ReadInt16LittleEndian(Data.AsSpan(Offset + BattleBoxFlags + (i * 2))); if (val < 0) { TeamSlots[i] = NONE_SELECTED; @@ -103,18 +96,19 @@ public void ClearBattleTeams() public void SaveBattleTeams() { + var span = Data.AsSpan(Offset + BattleBoxFlags); for (int i = 0; i < TeamCount * 6; i++) { int index = TeamSlots[i]; if (index < 0) { - BitConverter.GetBytes((short)index).CopyTo(Data, Offset + BattleBoxFlags + (i * 2)); + WriteInt16LittleEndian(span[(i*2)..], (short)index); continue; } SAV.GetBoxSlotFromIndex(index, out var box, out var slot); - int val = (box << 8) | slot; - BitConverter.GetBytes((short)val).CopyTo(Data, Offset + BattleBoxFlags + (i * 2)); + index = (box << 8) | slot; + WriteInt16LittleEndian(span[(i * 2)..], (short)index); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/CaptureRecords.cs b/PKHeX.Core/Saves/Substructures/Gen7/CaptureRecords.cs index 7b5d7add8..0edf452e8 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/CaptureRecords.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/CaptureRecords.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -28,10 +29,10 @@ public sealed class CaptureRecords : SaveBlock // 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) => BitConverter.ToUInt32(Data, GetCapturedOffset(index)); - public uint GetTransferredCountIndex(int index) => BitConverter.ToUInt32(Data, GetTransferredOffset(index)); - public void SetCapturedCountIndex(int index, uint value) => BitConverter.GetBytes(Math.Min(MAX_COUNT_ENTRY_CAPTURE, value)).CopyTo(Data, GetCapturedOffset(index)); - public void SetTransferredCountIndex(int index, uint value) => BitConverter.GetBytes(Math.Min(MAX_COUNT_ENTRY_TRANSFER, value)).CopyTo(Data, GetTransferredOffset(index)); + 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)); public static int GetSpeciesIndex(int species) => (uint)species switch { @@ -81,14 +82,14 @@ public void SetTransferredCount(int species, uint value) public uint TotalCaptured { - get => BitConverter.ToUInt32(Data, Offset + TotalCapturedOffset); - set => BitConverter.GetBytes(Math.Min(MAX_COUNT_TOTAL, value)).CopyTo(Data, Offset + TotalCapturedOffset); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + TotalCapturedOffset)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + TotalCapturedOffset), Math.Min(MAX_COUNT_TOTAL, value)); } public uint TotalTransferred { - get => BitConverter.ToUInt32(Data, Offset + TotalTransferredOffset); - set => BitConverter.GetBytes(Math.Min(MAX_COUNT_TOTAL, value)).CopyTo(Data, Offset + TotalTransferredOffset); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + TotalTransferredOffset)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + TotalTransferredOffset), Math.Min(MAX_COUNT_TOTAL, value)); } public uint CalculateTotalCaptured() diff --git a/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave6.cs b/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave6.cs index 90cdb8010..e45e5b5e0 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave6.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -23,8 +24,8 @@ public sealed class ConfigSave6 : SaveBlock public int ConfigValue { - get => BitConverter.ToInt32(Data, Offset); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset); + get => ReadInt32LittleEndian(Data.AsSpan(Offset)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset), value); } public int TalkingSpeed diff --git a/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7.cs b/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7.cs index 05b3c86b2..e4fe9676d 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -19,8 +20,8 @@ public sealed class ConfigSave7 : SaveBlock public int ConfigValue { - get => BitConverter.ToInt32(Data, Offset); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset); + get => ReadInt32LittleEndian(Data.AsSpan(Offset)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset), value); } public int TalkingSpeed diff --git a/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7b.cs index 797b09dc4..acaec257f 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -17,8 +18,8 @@ public sealed class ConfigSave7b : SaveBlock public int ConfigValue { - get => BitConverter.ToInt32(Data, Offset); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset); + get => ReadInt32LittleEndian(Data.AsSpan(Offset)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset), value); } public int TalkingSpeed diff --git a/PKHeX.Core/Saves/Substructures/Gen7/EventWork7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/EventWork7b.cs index 7e1c89dd8..5fba2ecd0 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/EventWork7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/EventWork7b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -54,8 +55,8 @@ public EventWork7b(SAV7b sav, int offset) : base(sav) public int CountFlag => FlagCount; public int CountWork => WorkCount; - public int GetWork(int index) => BitConverter.ToInt32(Data, Offset + (index * WorkSize)); - public void SetWork(int index, int value) => BitConverter.GetBytes(value).CopyTo(Data, Offset + (index * WorkSize)); + 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(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)); diff --git a/PKHeX.Core/Saves/Substructures/Gen7/FestaFacility.cs b/PKHeX.Core/Saves/Substructures/Gen7/FestaFacility.cs index 0ffec45af..7a42e83a2 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/FestaFacility.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/FestaFacility.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -22,19 +23,20 @@ public FestaFacility(SAV7 sav, int index) 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 int Gender { get => Data[0x03]; set => Data[0x03] = (byte)value; } - public string OT_Name { get => StringConverter.GetString7(Data, 0x04, 0x1A); set => StringConverter.SetString7(value, 12, Language).CopyTo(Data, 0x04); } + public string OT_Name { get => StringConverter7.GetString(Data.AsSpan(0x04, 0x1A)); set => StringConverter7.SetString(Data.AsSpan(0x04, 0x1A), value.AsSpan(), 12, Language, StringConverterOption.ClearZero); } - private int MessageMeet { get => BitConverter.ToUInt16(Data, 0x1E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1E); } - private int MessagePart { get => BitConverter.ToUInt16(Data, 0x20); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x20); } - private int MessageMoved { get => BitConverter.ToUInt16(Data, 0x22); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x22); } - private int MessageDisappointed { get => BitConverter.ToUInt16(Data, 0x24); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x24); } + 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); } 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 => BitConverter.ToUInt32(Data, 0x28); set => BitConverter.GetBytes(value).CopyTo(Data, 0x28); } - public uint UsedRandStat { get => BitConverter.ToUInt32(Data, 0x2C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x2C); } + 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 int NPC { get => Math.Max(0, BitConverter.ToInt32(Data, 0x30)); set => BitConverter.GetBytes(Math.Max(0, value)).CopyTo(Data, 0x30); } + public int NPC { get => Math.Max(0, ReadInt32LittleEndian(Data.AsSpan(0x30))); set => WriteInt32LittleEndian(Data.AsSpan(0x30), Math.Max(0, value)); } public byte[] TrainerFesID { get => Data.Slice(0x34, 0xC); set => value.CopyTo(Data, 0x34); } + public void ClearTrainerFesID() => Data.AsSpan(0x34, 0xC).Clear(); public int ExchangeLeftCount { get => Data[0x40]; set => Data[0x40] = (byte)value; } // used when Type=Exchange public int GetMessage(int index) => index switch diff --git a/PKHeX.Core/Saves/Substructures/Gen7/FieldMenu7.cs b/PKHeX.Core/Saves/Substructures/Gen7/FieldMenu7.cs index e9f5ab597..f902afe1e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/FieldMenu7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/FieldMenu7.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -10,8 +11,8 @@ public sealed class FieldMenu7 : SaveBlock // USUM ONLY public ushort RotomAffection { - get => BitConverter.ToUInt16(SAV.Data, Offset + 0x1A); - set => SAV.SetData(BitConverter.GetBytes(Math.Min((ushort)1000, value)), Offset + 0x1A); + get => ReadUInt16LittleEndian(SAV.Data.AsSpan(Offset + 0x1A)); + set => WriteUInt16LittleEndian(SAV.Data.AsSpan(Offset + 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)); } @@ -20,7 +21,7 @@ public ushort RotomAffection public string RotomOT { get => SAV.GetString(Offset + 0x30, 0x1A); - set => SAV.SetString(value, SAV.OTLength).CopyTo(Data, Offset + 0x30); + set => SAV.SetString(Data.AsSpan(Offset + 0x30, 0x1A), value.AsSpan(), SAV.OTLength, StringConverterOption.ClearZero); } } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/Gen7/FieldMoveModelSave7.cs b/PKHeX.Core/Saves/Substructures/Gen7/FieldMoveModelSave7.cs index bcca09002..7f93dec9b 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/FieldMoveModelSave7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/FieldMoveModelSave7.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -7,10 +8,10 @@ public sealed class FieldMoveModelSave7 : SaveBlock public FieldMoveModelSave7(SAV7SM sav, int offset) : base(sav) => Offset = offset; public FieldMoveModelSave7(SAV7USUM sav, int offset) : base(sav) => Offset = offset; - public int M { get => BitConverter.ToUInt16(Data, Offset + 0x00); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x00); } - public float X { get => BitConverter.ToSingle(Data, Offset + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); } - public float Z { get => BitConverter.ToSingle(Data, Offset + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10); } - public float Y { get => (int)BitConverter.ToSingle(Data, Offset + 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x18); } - public float R { get => (int)BitConverter.ToSingle(Data, Offset + 0x20); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x20); } + 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 + 0x10)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x10), value); } + public float Y { get => (int)ReadSingleLittleEndian(Data.AsSpan(Offset + 0x18)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x18), value); } + public float R { get => (int)ReadSingleLittleEndian(Data.AsSpan(Offset + 0x20)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x20), value); } } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/Gen7/GP1.cs b/PKHeX.Core/Saves/Substructures/Gen7/GP1.cs index 1fd294ef1..a66b947c9 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/GP1.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/GP1.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -25,9 +26,15 @@ public sealed class GP1 : IEncounterInfo public void WriteTo(byte[] data, int offset) => Data.CopyTo(data, offset); public static GP1 FromData(byte[] data, int offset) + { + var span = data.AsSpan(offset); + return FromData(span); + } + + private static GP1 FromData(ReadOnlySpan span) { var gpkm = new GP1(); - Array.Copy(data, offset, gpkm.Data, 0, SIZE); + span[..SIZE].CopyTo(gpkm.Data); return gpkm; } @@ -54,14 +61,14 @@ public static byte[] GetBlank() public string Username1 => Util.TrimFromZero(Encoding.ASCII.GetString(Data, 0x00, 0x10)); public string Username2 => Util.TrimFromZero(Encoding.ASCII.GetString(Data, 0x10, 0x20)); - public int Species => BitConverter.ToInt32(Data, 0x28); - public int CP => BitConverter.ToInt32(Data, 0x2C); - public float LevelF => BitConverter.ToSingle(Data, 0x30); + public int Species => ReadInt32LittleEndian(Data.AsSpan(0x28)); + public int CP => ReadInt32LittleEndian(Data.AsSpan(0x2C)); + public float LevelF => ReadSingleLittleEndian(Data.AsSpan(0x30)); public int Level => Math.Max(1, (int)Math.Round(LevelF)); - public int Stat_HP => BitConverter.ToInt32(Data, 0x34); + public int Stat_HP => ReadInt32LittleEndian(Data.AsSpan(0x34)); // geolocation data 0x38-0x47? - public float HeightF => BitConverter.ToSingle(Data, 0x48); - public float WeightF => BitConverter.ToSingle(Data, 0x4C); + public float HeightF => ReadSingleLittleEndian(Data.AsSpan(0x48)); + public float WeightF => ReadSingleLittleEndian(Data.AsSpan(0x4C)); public byte HeightScalar { @@ -87,10 +94,10 @@ public byte WeightScalar } } - public int IV_HP => BitConverter.ToInt32(Data, 0x50); - public int IV_ATK => BitConverter.ToInt32(Data, 0x54); - public int IV_DEF => BitConverter.ToInt32(Data, 0x58); - public int Date => BitConverter.ToInt32(Data, 0x5C); // ####.##.## YYYY.MM.DD + public int IV_HP => ReadInt32LittleEndian(Data.AsSpan(0x50)); + public int IV_ATK => ReadInt32LittleEndian(Data.AsSpan(0x54)); + public int IV_DEF => ReadInt32LittleEndian(Data.AsSpan(0x58)); + public int Date => ReadInt32LittleEndian(Data.AsSpan(0x5C)); // ####.##.## YYYY.MM.DD public int Year => Date / 1_00_00; public int Month => (Date / 1_00) % 1_00; public int Day => Date % 1_00; @@ -101,8 +108,8 @@ public byte WeightScalar public bool IsShiny => Data[0x73] == 1; // https://bulbapedia.bulbagarden.net/wiki/List_of_moves_in_Pok%C3%A9mon_GO - public int Move1 => BitConverter.ToInt32(Data, 0x74); // uses Go Indexes - public int Move2 => BitConverter.ToInt32(Data, 0x78); // uses Go Indexes + public int Move1 => ReadInt32LittleEndian(Data.AsSpan(0x74)); // uses Go Indexes + public int Move2 => ReadInt32LittleEndian(Data.AsSpan(0x78)); // uses Go Indexes public string GeoCityName => Util.TrimFromZero(Encoding.ASCII.GetString(Data, 0x7C, 0x60)); // dunno length diff --git a/PKHeX.Core/Saves/Substructures/Gen7/GameTime7.cs b/PKHeX.Core/Saves/Substructures/Gen7/GameTime7.cs index 7db7f706f..8ca7372bc 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/GameTime7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/GameTime7.cs @@ -1,19 +1,20 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { public sealed class GameTime7 : SaveBlock { public GameTime7(SaveFile sav, int offset) : base(sav) => Offset = offset; - public int ResumeYear { get => BitConverter.ToInt32(Data, Offset + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4); } + 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 => BitConverter.ToUInt32(Data, Offset + 0x28); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x28); } - public uint SecondsToFame { get => BitConverter.ToUInt32(Data, Offset + 0x30); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x30); } + 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 ulong AlolaTime { get => BitConverter.ToUInt64(Data, Offset + 0x48); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x48); } + public ulong AlolaTime { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x48)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x48), value); } } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/Gen7/HallOfFame7.cs b/PKHeX.Core/Saves/Substructures/Gen7/HallOfFame7.cs index 030c72af5..c96bf700d 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/HallOfFame7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/HallOfFame7.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -9,32 +10,32 @@ public sealed class HallOfFame7 : SaveBlock // this HoF region is immediately after the Event Flags private const int MaxCount = 12; - public int First1 { get => BitConverter.ToUInt16(SAV.Data, Offset + 0x00); set => SAV.SetData(BitConverter.GetBytes((ushort)value), Offset + 0x00); } - public int First2 { get => BitConverter.ToUInt16(SAV.Data, Offset + 0x02); set => SAV.SetData(BitConverter.GetBytes((ushort)value), Offset + 0x02); } - public int First3 { get => BitConverter.ToUInt16(SAV.Data, Offset + 0x04); set => SAV.SetData(BitConverter.GetBytes((ushort)value), Offset + 0x04); } - public int First4 { get => BitConverter.ToUInt16(SAV.Data, Offset + 0x04); set => SAV.SetData(BitConverter.GetBytes((ushort)value), Offset + 0x04); } - public int First5 { get => BitConverter.ToUInt16(SAV.Data, Offset + 0x06); set => SAV.SetData(BitConverter.GetBytes((ushort)value), Offset + 0x06); } - public int First6 { get => BitConverter.ToUInt16(SAV.Data, Offset + 0x08); set => SAV.SetData(BitConverter.GetBytes((ushort)value), Offset + 0x08); } + 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 => BitConverter.ToUInt16(SAV.Data, Offset + 0x0A); set => SAV.SetData(BitConverter.GetBytes((ushort)value), Offset + 0x0A); } - public int Current2 { get => BitConverter.ToUInt16(SAV.Data, Offset + 0x0C); set => SAV.SetData(BitConverter.GetBytes((ushort)value), Offset + 0x0C); } - public int Current3 { get => BitConverter.ToUInt16(SAV.Data, Offset + 0x0E); set => SAV.SetData(BitConverter.GetBytes((ushort)value), Offset + 0x0E); } - public int Current4 { get => BitConverter.ToUInt16(SAV.Data, Offset + 0x10); set => SAV.SetData(BitConverter.GetBytes((ushort)value), Offset + 0x10); } - public int Current5 { get => BitConverter.ToUInt16(SAV.Data, Offset + 0x12); set => SAV.SetData(BitConverter.GetBytes((ushort)value), Offset + 0x12); } - public int Current6 { get => BitConverter.ToUInt16(SAV.Data, Offset + 0x14); set => SAV.SetData(BitConverter.GetBytes((ushort)value), Offset + 0x14); } + 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 BitConverter.ToUInt16(SAV.Data, Offset + (index * 2)); + return ReadUInt16LittleEndian(SAV.Data.AsSpan(Offset + (index * 2))); } public void SetEntry(int index, ushort value) { if ((uint)index >= MaxCount) throw new ArgumentOutOfRangeException(nameof(index)); - SAV.SetData(BitConverter.GetBytes(value), Offset + (index * 2)); + 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 e57ad8c6e..cb5ed9aaa 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/JoinFesta7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/JoinFesta7.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -9,12 +10,12 @@ public sealed class JoinFesta7 : SaveBlock public int FestaCoins { - get => BitConverter.ToInt32(Data, Offset + 0x508); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x508)); set { if (value > 9999999) value = 9999999; - BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x508); + WriteInt32LittleEndian(Data.AsSpan(Offset + 0x508), value); TotalFestaCoins = ((SAV7)SAV).GetRecord(038) + value; // UsedFestaCoins } @@ -22,24 +23,26 @@ public int FestaCoins public int TotalFestaCoins { - get => BitConverter.ToInt32(Data, Offset + 0x50C); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x50C)); set { if (value > 9999999) value = 9999999; - BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x50C); + WriteInt32LittleEndian(Data.AsSpan(Offset + 0x50C), value); } } + private Span FestivalPlazaNameSpan => Data.AsSpan(Offset + 0x510, 0x2A); + public string FestivalPlazaName { - get => StringConverter.GetString7(Data, Offset + 0x510, 0x2A); - set => StringConverter.SetString7(value, 20, 21).CopyTo(Data, Offset + 0x510); + get => StringConverter7.GetString(FestivalPlazaNameSpan); + set => StringConverter7.SetString(FestivalPlazaNameSpan, value.AsSpan(), 20, 0, StringConverterOption.ClearZero); } - public ushort FestaRank { get => BitConverter.ToUInt16(Data, Offset + 0x53A); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x53A); } - public ushort GetFestaMessage(int index) => BitConverter.ToUInt16(Data, Offset + (index * 2)); - public void SetFestaMessage(int index, ushort value) => BitConverter.GetBytes(value).CopyTo(Data, Offset + (index * 2)); + 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 void SetFestaPhraseUnlocked(int index, bool value) @@ -50,12 +53,12 @@ public void SetFestaPhraseUnlocked(int index, bool value) 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 => BitConverter.ToInt32(Data, Offset + 0x2F0); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x2F0); } - private int FestaMonth { get => BitConverter.ToInt32(Data, Offset + 0x2F4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x2F4); } - private int FestaDay { get => BitConverter.ToInt32(Data, Offset + 0x2F8); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x2F8); } - private int FestaHour { get => BitConverter.ToInt32(Data, Offset + 0x300); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x300); } - private int FestaMinute { get => BitConverter.ToInt32(Data, Offset + 0x304); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x304); } - private int FestaSecond { get => BitConverter.ToInt32(Data, Offset + 0x308); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x308); } + 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 DateTime? FestaDate { diff --git a/PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs b/PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs index e621cabfd..bfa0c3f40 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -9,34 +10,34 @@ public sealed class Misc7 : SaveBlock public uint Money { - get => BitConverter.ToUInt32(Data, Offset + 0x4); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x4)); set { if (value > 9_999_999) value = 9_999_999; - SAV.SetData(BitConverter.GetBytes(value), Offset + 0x4); + WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x4), value); } } public uint Stamps { - get => (BitConverter.ToUInt32(Data, Offset + 0x08) << 13) >> 17; // 15 stamps; discard top13, lowest4 + get => (ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x08)) << 13) >> 17; // 15 stamps; discard top13, lowest4 set { - uint flags = BitConverter.ToUInt32(Data, Offset + 0x08) & 0xFFF8000F; + uint flags = ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x08)) & 0xFFF8000F; flags |= (value & 0x7FFF) << 4; - SAV.SetData(BitConverter.GetBytes(flags), Offset + 0x08); + WriteUInt32LittleEndian(SAV.Data.AsSpan(Offset + 0x08), flags); } } public uint BP { - get => BitConverter.ToUInt32(Data, Offset + 0x11C); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x11C)); set { if (value > 9999) value = 9999; - SAV.SetData(BitConverter.GetBytes(value), Offset + 0x11C); + WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x11C), value); } } @@ -48,8 +49,8 @@ public int Vivillon public uint StarterEncryptionConstant { - get => BitConverter.ToUInt32(Data, Offset + 0x148); - set => SAV.SetData(BitConverter.GetBytes(value), Offset + 0x148); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x148)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x148), value); } public int DaysFromRefreshed @@ -62,14 +63,14 @@ public int GetSurfScore(int recordID) { if ((uint)recordID >= 4) recordID = 0; - return BitConverter.ToInt32(Data, Offset + 0x138 + (4 * recordID)); + return ReadInt32LittleEndian(Data.AsSpan(Offset + 0x138 + (4 * recordID))); } public void SetSurfScore(int recordID, int score) { if ((uint)recordID >= 4) recordID = 0; - SAV.SetData(BitConverter.GetBytes(score), Offset + 0x138 + (4 * recordID)); + WriteInt32LittleEndian(Data.AsSpan(Offset + 0x138 + (4 * recordID)), score); } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Saves/Substructures/Gen7/Misc7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/Misc7b.cs index d163708b7..bcdb3696e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/Misc7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/Misc7b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,14 +9,16 @@ public sealed class Misc7b : SaveBlock public uint Money { - get => BitConverter.ToUInt32(Data, Offset + 4); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 4); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 4)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 4), value); } + private Span Rival_Trash => Data.AsSpan(Offset + 0x200, 0x1A); + public string Rival { - get => SAV.GetString(Offset + 0x200, 0x1A); - set => SAV.SetString(value, SAV.OTLength).CopyTo(Data, Offset + 0x200); + get => SAV.GetString(Rival_Trash); + set => SAV.SetString(Rival_Trash, value.AsSpan(), SAV.OTLength, StringConverterOption.ClearZero); } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Saves/Substructures/Gen7/MyStatus7.cs b/PKHeX.Core/Saves/Substructures/Gen7/MyStatus7.cs index 050325f42..3c4a15671 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/MyStatus7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/MyStatus7.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -12,14 +13,14 @@ public sealed class MyStatus7 : SaveBlock, IRegionOrigin public int TID { - get => BitConverter.ToUInt16(Data, Offset + 0); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0), (ushort)value); } public int SID { - get => BitConverter.ToUInt16(Data, Offset + 2); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 2); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 2)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 2), (ushort)value); } public int Game @@ -62,8 +63,8 @@ public string NexUniqueID public uint FestaID { - get => BitConverter.ToUInt32(Data, Offset + 0x28); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x28); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x28)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x28), value); } public byte Region @@ -90,10 +91,12 @@ public int Language set => Data[Offset + 0x35] = (byte)value; } + private Span OT_Trash => Data.AsSpan(Offset + 0x38, 0x1A); + public string OT { - get => SAV.GetString(Offset + 0x38, 0x1A); - set => SAV.SetString(value, SAV.OTLength).CopyTo(Data, Offset + 0x38); + get => SAV.GetString(OT_Trash); + set => SAV.SetString(OT_Trash, value.AsSpan(), SAV.OTLength, StringConverterOption.ClearZero); } public int DressUpSkinColor diff --git a/PKHeX.Core/Saves/Substructures/Gen7/MyStatus7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/MyStatus7b.cs index 26d28077f..a766a4400 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/MyStatus7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/MyStatus7b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -12,14 +13,14 @@ public sealed class MyStatus7b : SaveBlock public int TID { - get => BitConverter.ToUInt16(Data, Offset + 0); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0), (ushort)value); } public int SID { - get => BitConverter.ToUInt16(Data, Offset + 2); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 2); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 2)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 2), (ushort)value); } public int Game @@ -55,10 +56,12 @@ public int Language set => Data[Offset + 0x35] = (byte)value; } + private Span OT_Trash => Data.AsSpan(Offset + 0x38, 0x1A); + public string OT { - get => SAV.GetString(Offset + 0x38, 0x1A); - set => SAV.SetString(value, SAV.OTLength).CopyTo(Data, Offset + 0x38); + get => SAV.GetString(OT_Trash); + set => SAV.SetString(OT_Trash, value.AsSpan(), SAV.OTLength, StringConverterOption.ClearZero); } // The value here corresponds to a Trainer Class string (ranging from 000 to 383, use pkNX to get a full list). diff --git a/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs b/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs index addd06f02..1590daaaa 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs @@ -16,12 +16,12 @@ public sealed class MysteryBlock7 : SaveBlock // Mystery Gift public bool[] MysteryGiftReceivedFlags { - get => ArrayUtil.GitBitFlagArray(Data, Offset + FlagStart, MaxReceivedFlag); + get => ArrayUtil.GitBitFlagArray(Data.AsSpan(Offset + FlagStart), MaxReceivedFlag); set { if (value.Length != MaxReceivedFlag) return; - ArrayUtil.SetBitFlagArray(Data, Offset + FlagStart, value); + ArrayUtil.SetBitFlagArray(Data.AsSpan(Offset + FlagStart), value); SAV.State.Edited = true; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/PlayTime7b.cs b/PKHeX.Core/Saves/Substructures/Gen7/PlayTime7b.cs index 4b83d24fd..2e308316c 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/PlayTime7b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/PlayTime7b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,8 +9,8 @@ public sealed class PlayTime7b : SaveBlock public int PlayedHours { - get => BitConverter.ToUInt16(Data, Offset); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset), (ushort)value); } public int PlayedMinutes @@ -24,7 +25,7 @@ public int PlayedSeconds set => Data[Offset + 3] = (byte)value; } - private uint LastSaved { get => BitConverter.ToUInt32(Data, Offset + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4); } + private uint LastSaved { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x4)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 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/Gen7/PokeFinder7.cs b/PKHeX.Core/Saves/Substructures/Gen7/PokeFinder7.cs index 44f4db93f..db0466124 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/PokeFinder7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/PokeFinder7.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -9,41 +10,41 @@ public sealed class PokeFinder7 : SaveBlock public ushort CameraVersion { - get => BitConverter.ToUInt16(Data, Offset + 0x00); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x00); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x00)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x00), value); } public bool GyroFlag { - get => BitConverter.ToUInt16(Data, Offset + 0x02) == 1; - set => BitConverter.GetBytes((ushort)(value ? 1 : 0)).CopyTo(Data, Offset + 0x02); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x02)) == 1; + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x02), (ushort)(value ? 1 : 0)); } public uint SnapCount { - get => BitConverter.ToUInt32(Data, Offset + 0x04); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x04)); set { if (value > 9999999) // Top bound is unchecked, check anyway value = 9999999; - BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x04); + WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); } } public uint ThumbsTotalValue { - get => BitConverter.ToUInt32(Data, Offset + 0x0C); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x0C); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x0C)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x0C), value); } public uint ThumbsHighValue { - get => BitConverter.ToUInt32(Data, Offset + 0x10); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x10)); set { if (value > 9_999_999) value = 9_999_999; - BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10); + WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x10), value); if (value > ThumbsTotalValue) ThumbsTotalValue = value; @@ -52,8 +53,8 @@ public uint ThumbsHighValue public ushort TutorialFlags { - get => BitConverter.ToUInt16(Data, Offset + 0x14); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x14); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x14)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x14), value); } } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/Gen7/PokeListHeader.cs b/PKHeX.Core/Saves/Substructures/Gen7/PokeListHeader.cs index 72f04e0c6..ed1767284 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/PokeListHeader.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/PokeListHeader.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -96,22 +97,24 @@ public int StarterIndex public int Count { - get => BitConverter.ToUInt16(Data, Offset + (COUNT * 2)); - set => BitConverter.GetBytes((ushort) value).CopyTo(Data, Offset + (COUNT * 2)); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + (COUNT * 2))); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + (COUNT * 2)), (ushort)value); } private int[] LoadPointerData() { + var span = Data.AsSpan(Offset); var list = new int[7]; for (int i = 0; i < list.Length; i++) - list[i] = BitConverter.ToUInt16(Data, Offset + (i * 2)); + list[i] = ReadUInt16LittleEndian(span[(i * 2)..]); return list; } private void SetPointerData(IList vals) { + var span = Data.AsSpan(Offset); for (int i = 0; i < vals.Count; i++) - BitConverter.GetBytes((ushort)vals[i]).CopyTo(Data, Offset + (i * 2)); + WriteUInt16LittleEndian(span[(i*2)..], (ushort)vals[i]); vals.CopyTo(PokeListInfo); } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/QR7.cs b/PKHeX.Core/Saves/Substructures/Gen7/QR7.cs index 5d64b4a60..670b9a782 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/QR7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/QR7.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -47,7 +48,7 @@ public static class QR7 private static byte[] GetRawQR(int species, int form, bool shiny, int gender) { var basedata = (byte[])BaseQR.Clone(); - BitConverter.GetBytes((ushort)species).CopyTo(basedata, 0x28); + WriteUInt16LittleEndian(basedata.AsSpan(0x28), (ushort)species); var pi = PersonalTable.USUM.GetFormEntry(species, form); bool biGender = false; @@ -81,18 +82,18 @@ public static byte[] GenerateQRData(PK7 pk7, int box = 0, int slot = 0, int num_ num_copies = 1; byte[] data = new byte[0x1A2]; - BitConverter.GetBytes(0x454B4F50).CopyTo(data, 0); // POKE magic + var span = data.AsSpan(); + WriteUInt32LittleEndian(span, 0x454B4F50); // POKE magic data[0x4] = 0xFF; // QR Type - BitConverter.GetBytes(box).CopyTo(data, 0x8); - BitConverter.GetBytes(slot).CopyTo(data, 0xC); - BitConverter.GetBytes(num_copies).CopyTo(data, 0x10); // No need to check max num_copies, payload parser handles it on-console. + WriteInt32LittleEndian(span[0x08..], box); + WriteInt32LittleEndian(span[0x0C..], slot); + WriteInt32LittleEndian(span[0x10..], num_copies); // No need to check max num_copies, payload parser handles it on-console. - pk7.EncryptedPartyData.CopyTo(data, 0x30); // Copy in pokemon data - GetRawQR(pk7.Species, pk7.Form, pk7.IsShiny, pk7.Gender).CopyTo(data, 0x140); + pk7.EncryptedPartyData.CopyTo(span[0x30..]); // Copy in pokemon data + GetRawQR(pk7.Species, pk7.Form, pk7.IsShiny, pk7.Gender).CopyTo(span[0x140..]); - var span = new ReadOnlySpan(data, 0, 0x1A0); - var chk = Checksums.CRC16Invert(span); - BitConverter.GetBytes(chk).CopyTo(data, 0x1A0); + var chk = Checksums.CRC16Invert(span[..0x1A0]); + WriteUInt16LittleEndian(span[0x1A0..], chk); return data; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/ResortSave7.cs b/PKHeX.Core/Saves/Substructures/Gen7/ResortSave7.cs index 1ee5f5e08..4ec3b56b6 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/ResortSave7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/ResortSave7.cs @@ -32,6 +32,8 @@ public PKM[] ResortPKM } } + public const int BEANS_MAX = 14; + public int GetPokebeanCount(int bean_id) { if ((uint)bean_id > 14) @@ -39,6 +41,8 @@ public int GetPokebeanCount(int bean_id) return Data[Offset + 0x564C + bean_id]; } + public Span GetBeans() => Data.AsSpan(Offset, BEANS_MAX); + public void SetPokebeanCount(int bean_id, int count) { if ((uint)bean_id > 14) diff --git a/PKHeX.Core/Saves/Substructures/Gen7/Situation7.cs b/PKHeX.Core/Saves/Substructures/Gen7/Situation7.cs index b86a392e7..a41f0131e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/Situation7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/Situation7.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,11 +9,11 @@ public sealed class Situation7 : SaveBlock public Situation7(SAV7USUM sav, int offset) : base(sav) => Offset = offset; // "StartLocation" - public int M { get => BitConverter.ToUInt16(Data, Offset + 0x00); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x00); } - public float X { get => BitConverter.ToSingle(Data, Offset + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); } - public float Z { get => BitConverter.ToSingle(Data, Offset + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10); } - public float Y { get => (int)BitConverter.ToSingle(Data, Offset + 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x18); } - public float R { get => (int)BitConverter.ToSingle(Data, Offset + 0x20); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x20); } + 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 + 0x10)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x10), value); } + public float Y { get => (int)ReadSingleLittleEndian(Data.AsSpan(Offset + 0x18)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x18), value); } + public float R { get => (int)ReadSingleLittleEndian(Data.AsSpan(Offset + 0x20)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x20), value); } public void UpdateOverworldCoordinates() { @@ -38,26 +39,26 @@ public int WarpContinueRequest public int StepCountEgg { - get => BitConverter.ToInt32(Data, Offset + 0x70); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x70); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x70)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x70), value); } public int LastZoneID { - get => BitConverter.ToUInt16(Data, Offset + 0x74); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x74); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x74)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x74), (ushort)value); } public int StepCountFriendship { - get => BitConverter.ToUInt16(Data, Offset + 0x76); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x76); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x76)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x76), (ushort)value); } public int StepCountAffection // Kawaigari { - get => BitConverter.ToUInt16(Data, Offset + 0x78); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x78); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x78)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x78), (ushort)value); } } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/Gen7/WB7Records.cs b/PKHeX.Core/Saves/Substructures/Gen7/WB7Records.cs index 6e8700304..631955ea4 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/WB7Records.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/WB7Records.cs @@ -29,8 +29,7 @@ private int GetFlagOffset(int flag) public WR7 GetRecord(int index) { int ofs = GetRecordOffset(index); - byte[] data = new byte[WR7.Size]; - Array.Copy(Data, ofs, data, 0, WR7.Size); + byte[] data = Data.AsSpan(ofs, WR7.Size).ToArray(); return new WR7(data); } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTowerWork8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTowerWork8b.cs index 3088b824a..abdab6284 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTowerWork8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTowerWork8b.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -24,12 +25,12 @@ public sealed class BattleTowerWork8b : SaveBlock // uint day_challeng_cnt; // BTLTOWER_CLASSDATA[4] class_data; // uint challenge_cnt; - public int MasterRankMax { get => BitConverter.ToInt32(Data, Offset + 0x0); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x0); } // max_master_rank - public int PlayMode { get => BitConverter.ToInt32(Data, Offset + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4); }// play_mode - public int PlayModeOld { get => BitConverter.ToInt32(Data, Offset + 0x8); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x8); } // old_playmode - public uint BP { get => BitConverter.ToUInt32(Data, Offset + 0xC); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xC); } // btl_point + 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 uint ChallengeCount { get => BitConverter.ToUInt32(Data, Offset + 0x1B4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x1B4); } // challenge_cnt + public uint ChallengeCount { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x1B4)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x1B4), value); } // challenge_cnt public BattleTowerClassData8b[] Records { @@ -77,25 +78,25 @@ public byte Cleared } public bool Suspended { - get => BitConverter.ToInt32(Data, Offset + 0x04) == 1; - set => BitConverter.GetBytes(value ? 1u : 0u).CopyTo(Data, Offset + 0x04); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x04)) == 1; + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x04), value ? 1u : 0u); } - public ulong BattlePlaySeed { get => BitConverter.ToUInt64(Data, Offset + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); } - public uint Rank { get => BitConverter.ToUInt32(Data, Offset + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10); } - public uint RankDownLose { get => BitConverter.ToUInt32(Data, Offset + 0x14); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x14); } + 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 TrainerSeed1 { get => BitConverter.ToUInt64(Data, Offset + 0x1C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x1C); } - public ulong TrainerSeed2 { get => BitConverter.ToUInt64(Data, Offset + 0x24); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x24); } - public ulong TrainerSeed3 { get => BitConverter.ToUInt64(Data, Offset + 0x2C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x2C); } - public ulong TrainerSeed4 { get => BitConverter.ToUInt64(Data, Offset + 0x34); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x34); } - public ulong TrainerSeed5 { get => BitConverter.ToUInt64(Data, Offset + 0x3C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x3C); } - public ulong TrainerSeed6 { get => BitConverter.ToUInt64(Data, Offset + 0x44); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x44); } - public ulong TrainerSeed7 { get => BitConverter.ToUInt64(Data, Offset + 0x4C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4C); } + 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 uint TotalWins { get => BitConverter.ToUInt32(Data, Offset + 0x54); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x54); } - public uint TotalWinsLoop { get => BitConverter.ToUInt32(Data, Offset + 0x58); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x58); } - public uint TotalWinsLose { get => BitConverter.ToUInt32(Data, Offset + 0x5C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x5C); } - public uint RenshouCountOld { get => BitConverter.ToUInt32(Data, Offset + 0x60); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x60); } - public uint RenshouCount { get => BitConverter.ToUInt32(Data, Offset + 0x64); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x64); } + 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); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTrainerStatus8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTrainerStatus8b.cs index 88d3a87d5..71bbacc96 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTrainerStatus8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/BattleTrainerStatus8b.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -50,9 +51,9 @@ private int GetTrainerOffset(int trainer) return Offset + (trainer * SIZE_TRAINER); } - public bool GetIsWin(int trainer) => BitConverter.ToUInt32(Data, GetTrainerOffset(trainer)) == 1; - public bool GetIsBattleSearcher(int trainer) => BitConverter.ToUInt32(Data, GetTrainerOffset(trainer) + 4) == 1; - public void SetIsWin(int trainer, bool value) => BitConverter.GetBytes(value ? 1u : 0u).CopyTo(Data, GetTrainerOffset(trainer)); - public void SetIsBattleSearcher(int trainer, bool value) => BitConverter.GetBytes(value ? 1u : 0u).CopyTo(Data, GetTrainerOffset(trainer) + 4); + 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); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/BerryTreeGrowSave8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/BerryTreeGrowSave8b.cs index 54d1fd473..1b679c369 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/BerryTreeGrowSave8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/BerryTreeGrowSave8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -19,6 +20,6 @@ public sealed class BerryTreeGrowSave8b : SaveBlock // KinomiGrow[] kinomiGrows; // 0x0 // long LastUpdateMinutes; // 0x8 - public long LastUpdateMinutes { get => BitConverter.ToInt64(Data, Offset + 0x800); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x800); } + public long LastUpdateMinutes { get => ReadInt64LittleEndian(Data.AsSpan(Offset + 0x800)); set => WriteInt64LittleEndian(Data.AsSpan(Offset + 0x800), value); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/BoxLayout8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/BoxLayout8b.cs index 1cafec649..6b2447892 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/BoxLayout8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/BoxLayout8b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -21,32 +22,32 @@ public sealed class BoxLayout8b : SaveBlock, IBoxDetailName public string GetBoxName(int box) { - var boxName = SAV.GetString(Data, Offset + GetBoxNameOffset(box), SAV6.LongStringLength); - if (string.IsNullOrEmpty(boxName)) - boxName = $"Box {box + 1}"; - return boxName; + var span = Data.AsSpan(Offset + GetBoxNameOffset(box), SAV6.LongStringLength); + if (span.Count((byte)0) == span.Length) + return $"Box {box + 1}"; + return SAV.GetString(span); } public void SetBoxName(int box, string value) { - var data = SAV.SetString(value, StringMaxLength, StringMaxLength, 0); - var offset = Offset + GetBoxNameOffset(box); - SAV.SetData(Data, data, offset); + var span = Data.AsSpan(Offset + GetBoxNameOffset(box), SAV6.LongStringLength); + SAV.SetString(span, value.AsSpan(), StringMaxLength, StringConverterOption.ClearZero); } public string GetTeamName(int team) { - var boxName = SAV.GetString(Data, Offset + GetTeamNameOffset(team), TeamNameLength); - if (string.IsNullOrEmpty(boxName)) - boxName = $"Team {team + 1}"; - return boxName; + var offset = Offset + GetTeamNameOffset(team); + var span = Data.AsSpan(offset, TeamNameLength); + if (span.Count((byte)0) == span.Length) + return $"Team {team + 1}"; + return SAV.GetString(span); } public void SetTeamName(int team, string value) { - var data = SAV.SetString(value, StringMaxLength, TeamNameLength / 2, 0); var offset = Offset + GetTeamNameOffset(team); - SAV.SetData(Data, data, offset); + var span = Data.AsSpan(offset, TeamNameLength); + SAV.SetString(span, value.AsSpan(), TeamNameLength/2, StringConverterOption.ClearZero); } public string this[int i] @@ -64,7 +65,7 @@ public void LoadBattleTeams() { for (int i = 0; i < TeamCount * TeamSlotCount; i++) { - short val = BitConverter.ToInt16(Data, Offset + TeamPositionOffset + (i * 2)); + short val = ReadInt16LittleEndian(Data.AsSpan(Offset + TeamPositionOffset + (i * 2))); if (val < 0) { TeamSlots[i] = NONE_SELECTED; @@ -87,18 +88,19 @@ public void ClearBattleTeams() public void SaveBattleTeams() { + var span = Data.AsSpan(Offset + TeamPositionOffset); for (int i = 0; i < TeamCount * 6; i++) { int index = TeamSlots[i]; if (index < 0) { - BitConverter.GetBytes((short)index).CopyTo(Data, Offset + TeamPositionOffset + (i * 2)); + WriteInt16LittleEndian(span[(i * 2)..], (short)index); continue; } SAV.GetBoxSlotFromIndex(index, out var box, out var slot); - int val = (box << 8) | slot; - BitConverter.GetBytes((short)val).CopyTo(Data, Offset + TeamPositionOffset + (i * 2)); + index = (box << 8) | slot; + WriteInt16LittleEndian(span[(i * 2)..], (short)index); } } @@ -151,8 +153,8 @@ public void SetBoxWallpaper(int box, int value) public ushort StatusPut { - get => BitConverter.ToUInt16(Data, Offset + 0x648); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x648); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x648)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x648), value); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/ConfigSave8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/ConfigSave8b.cs index bffb31b77..31d23f323 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/ConfigSave8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/ConfigSave8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -12,32 +13,42 @@ public sealed class ConfigSave8b : SaveBlock { public ConfigSave8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public TextSpeedOption TextSpeed { get => (TextSpeedOption)BitConverter.ToInt32(Data, Offset + 0); set => BitConverter.GetBytes((int)value).CopyTo(Data, Offset + 0); } - public int Language { get => BitConverter.ToInt32(Data, Offset + 4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 4); } + public TextSpeedOption TextSpeed + { + get => (TextSpeedOption)ReadInt32LittleEndian(Data.AsSpan(Offset + 0)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0), (int)value); + } + + public int Language + { + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 4)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 4), value); + } + public bool IsKanji { get => Data[Offset + 8] == 1; set => Data[Offset + 8] = (byte)(value ? 1 : 0); } public int WindowType { - get => BitConverter.ToInt32(Data, Offset + 0x0C); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x0C); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x0C)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x0C), value); } public BattleAnimationSetting MoveAnimations { - get => (BattleAnimationSetting)BitConverter.ToInt32(Data, Offset + 0x10); - set => BitConverter.GetBytes((int)value).CopyTo(Data, Offset + 0x10); + get => (BattleAnimationSetting)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x10)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x10), (int)value); } public BattleStyleSetting BattleStyle { - get => (BattleStyleSetting)BitConverter.ToInt32(Data, Offset + 0x14); - set => BitConverter.GetBytes((int)value).CopyTo(Data, Offset + 0x14); + get => (BattleStyleSetting)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x14)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x14), (int)value); } public PartyBoxSetting PartyBox { - get => (PartyBoxSetting)BitConverter.ToInt32(Data, Offset + 0x18); - set => BitConverter.GetBytes((int)value).CopyTo(Data, Offset + 0x18); + get => (PartyBoxSetting)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x18)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x18), (int)value); } // 4 byte bool, nice @@ -45,10 +56,26 @@ public PartyBoxSetting PartyBox 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 CameraInputMode CameraUpDown { get => (CameraInputMode)BitConverter.ToInt32(Data, Offset + 0x28); set => BitConverter.GetBytes((int)value).CopyTo(Data, Offset + 0x28); } - public CameraInputMode CamerLeftRight { get => (CameraInputMode)BitConverter.ToInt32(Data, Offset + 0x2C); set => BitConverter.GetBytes((int)value).CopyTo(Data, Offset + 0x2C); } + public CameraInputMode CameraUpDown + { + get => (CameraInputMode)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x28)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x28), (int)value); + } + + public CameraInputMode CamerLeftRight + { + get => (CameraInputMode)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x2C)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x2C), (int)value); + } + public bool AutoReport { get => Data[Offset + 0x30] == 1; set => Data[Offset + 0x30] = (byte)(value ? 1 : 0); } - public InputMode Input { get => (InputMode)BitConverter.ToInt32(Data, Offset + 0x34); set => BitConverter.GetBytes((int)value).CopyTo(Data, Offset + 0x34); } + + public InputMode Input + { + get => (InputMode)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x34)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 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; } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/Contest8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/Contest8b.cs index ea637bbb5..31a0ec898 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/Contest8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/Contest8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -17,6 +18,6 @@ public sealed class Contest8b : SaveBlock private const int OFS_RANK = SIZE_CONTEST_PHOTO * PHOTO_MAX; // 0x71C; - public uint ContestRankPoint { get => BitConverter.ToUInt32(Data, Offset + OFS_RANK); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_RANK); } + public uint ContestRankPoint { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_RANK)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_RANK), value); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/Daycare8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/Daycare8b.cs index 9a8fb26d8..e81729660 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/Daycare8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/Daycare8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -40,20 +41,20 @@ public PB8 GetSlot(int slot) public bool IsEggAvailable { - get => BitConverter.ToUInt32(Data, Offset + ExtraDataOffset) == 1; - set => BitConverter.GetBytes(value ? 1u : 0).CopyTo(Data, Offset + ExtraDataOffset); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + ExtraDataOffset)) == 1; + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + ExtraDataOffset), value ? 1u : 0u); } public ulong DaycareSeed { - get => BitConverter.ToUInt64(Data, Offset + ExtraDataOffset + 4); - set => SAV.SetData(Data, BitConverter.GetBytes(value), Offset + ExtraDataOffset + 4); + get => ReadUInt64LittleEndian(Data.AsSpan(Offset + ExtraDataOffset + 4)); + set => WriteUInt64LittleEndian(Data.AsSpan(Offset + ExtraDataOffset + 4), value); } public int EggStepCount { - get => BitConverter.ToInt32(Data, Offset + ExtraDataOffset + 4 + 8); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + ExtraDataOffset + 4 + 8); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + ExtraDataOffset + 4 + 8)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + ExtraDataOffset + 4 + 8), value); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/EncounterSave8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/EncounterSave8b.cs index 5d73c45ce..971ab7b23 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/EncounterSave8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/EncounterSave8b.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -32,44 +33,44 @@ public sealed class EncounterSave8b : SaveBlock public int EncounterWalkCount { - get => BitConverter.ToInt32(Data, Offset + 0x00); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x00); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x00)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x00), value); } public uint SafariRandSeed { - get => BitConverter.ToUInt32(Data, Offset + 0x04); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x04); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x04)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); } public uint GenerateRandSeed { - get => BitConverter.ToUInt32(Data, Offset + 0x08); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x08)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x08), value); } // HILL_BACK_DATA public bool HillTalkFlag { - get => BitConverter.ToUInt32(Data, Offset + OFS_HillBackData + 0x00) == 1; - set => BitConverter.GetBytes(value ? 1u : 0).CopyTo(Data, Offset + OFS_HillBackData + 0x00); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_HillBackData + 0x00)) == 1; + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_HillBackData + 0x00), value ? 1u : 0u); } public ushort HillEncTblIdx1 { - get => BitConverter.ToUInt16(Data, Offset + OFS_HillBackData + 0x04); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_HillBackData + 0x04); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + OFS_HillBackData + 0x04)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + OFS_HillBackData + 0x04), value); } public ushort HillEncTblIdx2 { - get => BitConverter.ToUInt16(Data, Offset + OFS_HillBackData + 0x06); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_HillBackData + 0x06); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + OFS_HillBackData + 0x06)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + OFS_HillBackData + 0x06), value); } // HONEY_TREE public long HoneyLastUpdateMinutes { - get => BitConverter.ToInt64(Data, Offset + OFS_HoneyTree + 0x00); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_HoneyTree + 0x00); + get => ReadInt64LittleEndian(Data.AsSpan(Offset + OFS_HoneyTree + 0x00)); + set => WriteInt64LittleEndian(Data.AsSpan(Offset + OFS_HoneyTree + 0x00), value); } public byte HoneyTreeNo { @@ -98,43 +99,43 @@ private static void SetTrees(IReadOnlyList value) // data is already hard-referencing the original byte array. This is mostly a hack for Property Grid displays. } - public uint Radar1Species { get => BitConverter.ToUInt32(Data, Offset + OFS_SWAY + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SWAY + 0x00); } - public uint Radar1Chain { get => BitConverter.ToUInt32(Data, Offset + OFS_SWAY + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SWAY + 0x04); } - public uint Radar2Species { get => BitConverter.ToUInt32(Data, Offset + OFS_SWAY + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SWAY + 0x08); } - public uint Radar2Chain { get => BitConverter.ToUInt32(Data, Offset + OFS_SWAY + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SWAY + 0x0C); } - public uint Radar3Species { get => BitConverter.ToUInt32(Data, Offset + OFS_SWAY + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SWAY + 0x10); } - public uint Radar3Chain { get => BitConverter.ToUInt32(Data, Offset + OFS_SWAY + 0x14); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SWAY + 0x14); } + 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 int BeforeZone { get => BitConverter.ToInt32(Data, Offset + OFS_ZONEHISTORY + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ZONEHISTORY + 0x00); } - public int OldZone { get => BitConverter.ToInt32(Data, Offset + OFS_ZONEHISTORY + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ZONEHISTORY + 0x04); } + 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); } // Mesprit - public int Roamer1ZoneID { get => BitConverter.ToInt32 (Data, Offset + OFS_ROAM1 + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM1 + 0x00); } - public ulong Roamer1Seed { get => BitConverter.ToUInt64(Data, Offset + OFS_ROAM1 + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM1 + 0x04); } - public uint Roamer1Species { get => BitConverter.ToUInt32(Data, Offset + OFS_ROAM1 + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM1 + 0x0C); } - public uint Roamer1HP { get => BitConverter.ToUInt32(Data, Offset + OFS_ROAM1 + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM1 + 0x10); } + 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 => BitConverter.ToUInt32(Data, Offset + OFS_ROAM1 + 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM1 + 0x18); } + 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; } // Cresselia - public int Roamer2ZoneID { get => BitConverter.ToInt32 (Data, Offset + OFS_ROAM2 + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM2 + 0x00); } - public ulong Roamer2Seed { get => BitConverter.ToUInt64(Data, Offset + OFS_ROAM2 + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM2 + 0x04); } - public uint Roamer2Species { get => BitConverter.ToUInt32(Data, Offset + OFS_ROAM2 + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM2 + 0x0C); } - public uint Roamer2HP { get => BitConverter.ToUInt32(Data, Offset + OFS_ROAM2 + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM2 + 0x10); } + 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 => BitConverter.ToUInt32(Data, Offset + OFS_ROAM2 + 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM2 + 0x18); } + 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 bool GenerateValid { - get => BitConverter.ToUInt32(Data, Offset + OFS_CLOSING + 0) == 1; - set => BitConverter.GetBytes(value ? 1u : 0).CopyTo(Data, Offset + OFS_CLOSING + 0); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_CLOSING + 0)) == 1; + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_CLOSING + 0), value ? 1u : 0u); } public short SprayCount { - get => BitConverter.ToInt16(Data, Offset + OFS_CLOSING + 4); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_CLOSING + 4); + get => ReadInt16LittleEndian(Data.AsSpan(Offset + OFS_CLOSING + 4)); + set => WriteInt16LittleEndian(Data.AsSpan(Offset + 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 @@ -158,13 +159,13 @@ public HoneyTree8b(byte[] data, int offset) public bool Spreaded { - get => BitConverter.ToUInt32(Data, Offset + 0x00) == 1; - set => BitConverter.GetBytes(value ? 1u : 0).CopyTo(Data, Offset + 0x00); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x00)) == 1; + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x00), value ? 1u : 0u); } public int Minutes { - get => BitConverter.ToInt32(Data, Offset + 0x04); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x04); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x04)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); } public byte TblMonsNo { diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldGimmickSave8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldGimmickSave8b.cs index e1c7942a7..456233c46 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldGimmickSave8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldGimmickSave8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -11,8 +12,8 @@ public sealed class FieldGimmickSave8b : SaveBlock { public FieldGimmickSave8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public int Value0 { get => BitConverter.ToInt32(Data, Offset + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x00); } - public int Value1 { get => BitConverter.ToInt32(Data, Offset + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x04); } - public int Value2 { get => BitConverter.ToInt32(Data, Offset + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); } + 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); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldObjectSave8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldObjectSave8b.cs index 9381a4dbb..216e32e74 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldObjectSave8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/FieldObjectSave8b.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -57,21 +58,21 @@ public FieldObject8b(byte[] data, int offset) set => Data[0] = value; } - public int NameHash { get => BitConverter.ToInt32(Data, 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); } - public int GridX { get => BitConverter.ToInt32(Data, 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, 0x08); } - public int GridY { get => BitConverter.ToInt32(Data, 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x0C); } - public int Height { get => BitConverter.ToInt32(Data, 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, 0x10); } - public int Angle { get => BitConverter.ToInt32(Data, 0x14); set => BitConverter.GetBytes(value).CopyTo(Data, 0x14); } - public bool Active { get => BitConverter.ToInt32(Data, 0x18) == 1; set => BitConverter.GetBytes(value ? 1u : 0u).CopyTo(Data, 0x18); } - public int MoveCode { get => BitConverter.ToInt32(Data, 0x1C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1C); } - public int DirHead { get => BitConverter.ToInt32(Data, 0x20); set => BitConverter.GetBytes(value).CopyTo(Data, 0x20); } - public int MvParam0 { get => BitConverter.ToInt32(Data, 0x24); set => BitConverter.GetBytes(value).CopyTo(Data, 0x24); } - public int MvParam1 { get => BitConverter.ToInt32(Data, 0x28); set => BitConverter.GetBytes(value).CopyTo(Data, 0x28); } - public int MvParam2 { get => BitConverter.ToInt32(Data, 0x2C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x2C); } - public int LimitX { get => BitConverter.ToInt32(Data, 0x30); set => BitConverter.GetBytes(value).CopyTo(Data, 0x30); } - public int LimitZ { get => BitConverter.ToInt32(Data, 0x34); set => BitConverter.GetBytes(value).CopyTo(Data, 0x34); } - public int EvType { get => BitConverter.ToInt32(Data, 0x38); set => BitConverter.GetBytes(value).CopyTo(Data, 0x38); } - public int MvOldDir { get => BitConverter.ToInt32(Data, 0x3C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x3C); } - public int MvDir { get => BitConverter.ToInt32(Data, 0x40); set => BitConverter.GetBytes(value).CopyTo(Data, 0x40); } + public int NameHash { get => ReadInt32LittleEndian(Data.AsSpan(0x04)); set => WriteInt32LittleEndian(Data.AsSpan(0x04), value); } + public int GridX { get => ReadInt32LittleEndian(Data.AsSpan(0x08)); set => WriteInt32LittleEndian(Data.AsSpan(0x08), value); } + public int GridY { get => ReadInt32LittleEndian(Data.AsSpan(0x0C)); set => WriteInt32LittleEndian(Data.AsSpan(0x0C), value); } + public int Height { get => ReadInt32LittleEndian(Data.AsSpan(0x10)); set => WriteInt32LittleEndian(Data.AsSpan(0x10), value); } + public int Angle { get => ReadInt32LittleEndian(Data.AsSpan(0x14)); set => WriteInt32LittleEndian(Data.AsSpan(0x14), value); } + public bool Active { get => ReadInt32LittleEndian(Data.AsSpan(0x18)) == 1; set => WriteUInt32LittleEndian(Data.AsSpan(0x18), value ? 1u : 0u); } + public int MoveCode { get => ReadInt32LittleEndian(Data.AsSpan(0x1C)); set => WriteInt32LittleEndian(Data.AsSpan(0x1C), value); } + public int DirHead { get => ReadInt32LittleEndian(Data.AsSpan(0x20)); set => WriteInt32LittleEndian(Data.AsSpan(0x20), value); } + public int MvParam0 { get => ReadInt32LittleEndian(Data.AsSpan(0x24)); set => WriteInt32LittleEndian(Data.AsSpan(0x24), value); } + public int MvParam1 { get => ReadInt32LittleEndian(Data.AsSpan(0x28)); set => WriteInt32LittleEndian(Data.AsSpan(0x28), value); } + public int MvParam2 { get => ReadInt32LittleEndian(Data.AsSpan(0x2C)); set => WriteInt32LittleEndian(Data.AsSpan(0x2C), value); } + public int LimitX { get => ReadInt32LittleEndian(Data.AsSpan(0x30)); set => WriteInt32LittleEndian(Data.AsSpan(0x30), value); } + public int LimitZ { get => ReadInt32LittleEndian(Data.AsSpan(0x34)); set => WriteInt32LittleEndian(Data.AsSpan(0x34), value); } + public int EvType { get => ReadInt32LittleEndian(Data.AsSpan(0x38)); set => WriteInt32LittleEndian(Data.AsSpan(0x38), value); } + public int MvOldDir { get => ReadInt32LittleEndian(Data.AsSpan(0x3C)); set => WriteInt32LittleEndian(Data.AsSpan(0x3C), value); } + public int MvDir { get => ReadInt32LittleEndian(Data.AsSpan(0x40)); set => WriteInt32LittleEndian(Data.AsSpan(0x40), value); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/FlagWork8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/FlagWork8b.cs index c9aae1edb..dbef9ff7a 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/FlagWork8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/FlagWork8b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -57,15 +58,15 @@ private int GetOffsetWork(int flagNo) return Offset + OFS_WORK + (4 * flagNo); } - public bool GetFlag (int flagNo) => BitConverter.ToInt32(Data, GetOffsetFlag(flagNo)) == 1; - public bool GetSystemFlag(int flagNo) => BitConverter.ToInt32(Data, GetOffsetSystem(flagNo)) == 1; - public int GetWork (int workNo) => BitConverter.ToInt32(Data, GetOffsetWork(workNo)); - public float GetFloatWork(int workNo) => BitConverter.ToSingle(Data, GetOffsetWork(workNo)); + public bool GetFlag (int flagNo) => ReadInt32LittleEndian(Data.AsSpan(GetOffsetFlag(flagNo))) == 1; + public bool GetSystemFlag(int flagNo) => ReadInt32LittleEndian(Data.AsSpan(GetOffsetSystem(flagNo))) == 1; + public int GetWork (int workNo) => ReadInt32LittleEndian(Data.AsSpan(GetOffsetWork(workNo))); + public float GetFloatWork(int workNo) => ReadSingleLittleEndian(Data.AsSpan(GetOffsetWork(workNo))); - public void SetFlag (int flagNo, bool value) => BitConverter.GetBytes(value ? 1u : 0u).CopyTo(Data, GetOffsetFlag(flagNo)); - public void SetSystemFlag(int flagNo, bool value) => BitConverter.GetBytes(value ? 1u : 0u).CopyTo(Data, GetOffsetSystem(flagNo)); - public void SetWork (int workNo, int value) => BitConverter.GetBytes(value).CopyTo(Data, GetOffsetWork(workNo)); - public void SetFloatWork (int workNo, float value) => BitConverter.GetBytes(value).CopyTo(Data, GetOffsetWork(workNo)); + public void SetFlag (int flagNo, bool value) => WriteUInt32LittleEndian(Data.AsSpan(GetOffsetFlag(flagNo)), value ? 1u : 0u); + public void SetSystemFlag(int flagNo, bool value) => WriteUInt32LittleEndian(Data.AsSpan(GetOffsetSystem(flagNo)), value ? 1u : 0u); + public void SetWork (int workNo, int value) => WriteInt32LittleEndian(Data.AsSpan(GetOffsetWork(workNo)), value); + public void SetFloatWork (int workNo, float value) => WriteSingleLittleEndian(Data.AsSpan(GetOffsetWork(workNo)), value); public void ResetFlag (int flagNo) => SetFlag(flagNo, false); public void ResetVanishFlag(int flagNo) => SetVanishFlag(flagNo, false); diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/MenuSelect8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/MenuSelect8b.cs index 3376644a3..bfab2179f 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/MenuSelect8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/MenuSelect8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -17,25 +18,25 @@ public sealed class MenuSelect8b : SaveBlock public int GetMenuItem(int index) { int ofs = GetOffset(index); - return BitConverter.ToInt32(Data, Offset + ofs); + return ReadInt32LittleEndian(Data.AsSpan(Offset + ofs)); } public void SetMenuItem(int index, int value) { int ofs = GetOffset(index); - BitConverter.GetBytes(value).CopyTo(Data, Offset + ofs); + WriteInt32LittleEndian(Data.AsSpan(Offset + ofs), value); } public bool GetMenuItemIsNew(int index) { int ofs = GetOffset(index); - return BitConverter.ToInt32(Data, Offset + ofs + 4) == 1; + return ReadInt32LittleEndian(Data.AsSpan(Offset + ofs + 4)) == 1; } public void SetMenuItemIsNew(int index, bool value) { int ofs = GetOffset(index); - BitConverter.GetBytes(value).CopyTo(Data, Offset + ofs + 4); + WriteInt32LittleEndian(Data.AsSpan(Offset + ofs + 4), value ? 1 : 0); } private static int GetOffset(int index) @@ -45,6 +46,6 @@ private static int GetOffset(int index) return index * SIZE_TUPLE; } - public int LastSelectedMenu { get => BitConverter.ToInt32(Data, Offset + 0x40); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x40); } + public int LastSelectedMenu { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x40)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x40), value); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/MyItem8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/MyItem8b.cs index 6992281eb..01ff444a9 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/MyItem8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/MyItem8b.cs @@ -17,21 +17,23 @@ public sealed class MyItem8b : MyItem public int GetItemQuantity(ushort itemIndex) { var ofs = InventoryPouch8b.GetItemOffset(itemIndex, Offset); - var item = InventoryItem8b.Read(itemIndex, Data, ofs); + var span = Data.AsSpan(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 item = InventoryItem8b.Read(itemIndex, Data, ofs); + var span = Data.AsSpan(ofs, InventoryItem8b.SIZE); + var item = InventoryItem8b.Read(itemIndex, span); item.Count = quantity; if (!item.IsValidSaveSortNumberCount) // not yet obtained { var type = GetType(itemIndex); item.SortOrder = GetNextSortIndex(type); } - item.Write(Data, ofs); + item.Write(span); } public static InventoryType GetType(ushort itemIndex) @@ -50,7 +52,8 @@ public ushort GetNextSortIndex(InventoryType type) foreach (var itemID in GetLegal(type)) { var ofs = InventoryPouch8b.GetItemOffset(itemID, Offset); - var item = InventoryItem8b.Read(itemID, Data, ofs); + var span = Data.AsSpan(ofs, InventoryItem8b.SIZE); + var item = InventoryItem8b.Read(itemID, span); if (item.SortOrder > max) max = item.SortOrder; } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/MyStatus8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/MyStatus8b.cs index e3c53dd24..10502a975 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/MyStatus8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/MyStatus8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -15,28 +16,30 @@ public sealed class MyStatus8b : SaveBlock public MyStatus8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; + private Span OT_Trash => Data.AsSpan(Offset + 0, 0x1A); + public string OT { - get => SAV.GetString(Data, Offset + 0, 0x1A); - set => SAV.SetData(Data, SAV.SetString(value, SAV.OTLength), Offset + 0); + get => SAV.GetString(OT_Trash); + set => SAV.SetString(OT_Trash, value.AsSpan(), 10, StringConverterOption.ClearZero); } public int TID { - get => BitConverter.ToUInt16(Data, Offset + 0x1C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x1C); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x1C)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x1C), (ushort)value); } public int SID { - get => BitConverter.ToUInt16(Data, Offset + 0x1E); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x1E); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x1E)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x1E), (ushort)value); } public uint Money { - get => BitConverter.ToUInt32(Data, Offset + 0x20); - set => BitConverter.GetBytes(Math.Min(MAX_MONEY, value)).CopyTo(Data, Offset + 0x20); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x20)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x20), Math.Min(MAX_MONEY, value)); } public bool Male { get => Data[Offset + 0x24] == 1; set => Data[Offset + 0x24] = (byte)(value ? 1 : 0); } @@ -53,19 +56,19 @@ public uint Money public MoveType StarterType { - get => (MoveType)BitConverter.ToInt32(Data, Offset + 0x34); - set => BitConverter.GetBytes((int)value).CopyTo(Data, Offset + 0x34); + get => (MoveType)ReadInt32LittleEndian(Data.AsSpan(Offset + 0x34)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x34), (int)value); } public bool DSPlayer { get => Data[Offset + 0x38] == 1; set => Data[Offset + 0x38] = (byte)(value ? 1 : 0); } // 3byte align /// turewalkMemberIndex - public int FollowIndex { get => BitConverter.ToInt32(Data, Offset + 0x3C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x3C); } - public int X { get => BitConverter.ToInt32(Data, Offset + 0x40); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x40); } - public int Y { get => BitConverter.ToInt32(Data, Offset + 0x44); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x44); } - public float Height { get => BitConverter.ToSingle(Data, Offset + 0x48); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x48); } - public float Rotation { get => BitConverter.ToSingle(Data, Offset + 0x4C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4C); } + 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); } // end structure! diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/MysteryBlock8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/MysteryBlock8b.cs index 4d7dd6f05..f9f84baa3 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/MysteryBlock8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/MysteryBlock8b.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -61,7 +62,7 @@ private int GetOneDayOffset(int index) 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 => BitConverter.ToInt64(Data, Offset + OFS_SERIALLOCK); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SERIALLOCK); } + public long TicksSerialLock { get => ReadInt64LittleEndian(Data.AsSpan(Offset + OFS_SERIALLOCK)); set => WriteInt64LittleEndian(Data.AsSpan(Offset + 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; @@ -165,32 +166,32 @@ public RecvData8b(byte[] data, int offset = 0) } public override string ToString() => $"{DeliveryID:0000} @ {LocalTimestamp:F}"; - public void CopyTo(byte[] data, int offset) => Data.AsSpan(Offset, SIZE).CopyTo(data.AsSpan(offset)); + public void CopyTo(Span destination, int offset) => Data.AsSpan(Offset, SIZE).CopyTo(destination[offset..]); public void Clear() => Data.AsSpan(Offset, SIZE).Clear(); - public long Ticks { get => BitConverter.ToInt64(Data, Offset); set => BitConverter.GetBytes(value).CopyTo(Data, Offset); } + public long Ticks { get => ReadInt64LittleEndian(Data.AsSpan(Offset)); set => WriteInt64LittleEndian(Data.AsSpan(Offset), 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 => BitConverter.ToUInt16(Data, Offset + 0x8); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x8); } - public ushort TextID { get => BitConverter.ToUInt16(Data, Offset + 0xA); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xA); } + 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 => BitConverter.ToInt16(Data, Offset + 0xE); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xE); } + public short ReservedShort1 { get => ReadInt16LittleEndian(Data.AsSpan(Offset + 0xE)); set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0xE), value); } #region MonsData: 0x30 Bytes - public ushort Species { get => BitConverter.ToUInt16(Data, Offset + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10); } - public ushort Form { get => BitConverter.ToUInt16(Data, Offset + 0x12); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x12); } - public ushort HeldItem{ get => BitConverter.ToUInt16(Data, Offset + 0x14); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x14); } - public ushort Move1 { get => BitConverter.ToUInt16(Data, Offset + 0x16); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x16); } - public ushort Move2 { get => BitConverter.ToUInt16(Data, Offset + 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x18); } - public ushort Move3 { get => BitConverter.ToUInt16(Data, Offset + 0x1A); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x1A); } - public ushort Move4 { get => BitConverter.ToUInt16(Data, Offset + 0x1C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x1C); } + 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 string OT { - get => StringConverter.GetString7b(Data, Offset + 0x1E, 0x1A); - set => StringConverter.SetString7b(value, 12, 12).CopyTo(Data, Offset + 0x1E); + get => StringConverter8.GetString(Data.AsSpan(Offset + 0x1E, 0x1A)); + set => StringConverter8.SetString(Data.AsSpan(Offset + 0x1E, 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); } public byte OT_Gender { get => Data[Offset + 0x38]; set => Data[Offset + 0x38] = value; } @@ -201,35 +202,35 @@ public string OT // 0x3D 0x3E 0x3F reserved #endregion - public ushort Item1 { get => BitConverter.ToUInt16(Data, Offset + 0x40); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x40); } - public ushort Item2 { get => BitConverter.ToUInt16(Data, Offset + 0x50); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x50); } - public ushort Item3 { get => BitConverter.ToUInt16(Data, Offset + 0x60); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x60); } - public ushort Item4 { get => BitConverter.ToUInt16(Data, Offset + 0x70); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x70); } - public ushort Item5 { get => BitConverter.ToUInt16(Data, Offset + 0x80); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x80); } - public ushort Item6 { get => BitConverter.ToUInt16(Data, Offset + 0x90); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x90); } - public ushort Item7 { get => BitConverter.ToUInt16(Data, Offset + 0xA0); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xA0); } - public ushort Item1Count { get => BitConverter.ToUInt16(Data, Offset + 0x42); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x42); } - public ushort Item2Count { get => BitConverter.ToUInt16(Data, Offset + 0x52); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x52); } - public ushort Item3Count { get => BitConverter.ToUInt16(Data, Offset + 0x62); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x62); } - public ushort Item4Count { get => BitConverter.ToUInt16(Data, Offset + 0x72); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x72); } - public ushort Item5Count { get => BitConverter.ToUInt16(Data, Offset + 0x82); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x82); } - public ushort Item6Count { get => BitConverter.ToUInt16(Data, Offset + 0x92); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x92); } - public ushort Item7Count { get => BitConverter.ToUInt16(Data, Offset + 0xA2); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xA2); } + 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); } // 0xC reserved for each item index - public uint DressID1 { get => BitConverter.ToUInt32(Data, Offset + 0xB0); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xB0); } - public uint DressID2 { get => BitConverter.ToUInt32(Data, Offset + 0xB4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xB4); } - public uint DressID3 { get => BitConverter.ToUInt32(Data, Offset + 0xB8); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xB8); } - public uint DressID4 { get => BitConverter.ToUInt32(Data, Offset + 0xBC); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xBC); } - public uint DressID5 { get => BitConverter.ToUInt32(Data, Offset + 0xC0); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xC0); } - public uint DressID6 { get => BitConverter.ToUInt32(Data, Offset + 0xC4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xC4); } - public uint DressID7 { get => BitConverter.ToUInt32(Data, Offset + 0xC8); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xC8); } + 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 MoneyData { get => BitConverter.ToUInt32(Data, Offset + 0xCC); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xCC); } - public int Reserved1 { get => BitConverter.ToInt32(Data, Offset + 0xD0); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xD0); } - public int Reserved2 { get => BitConverter.ToInt32(Data, Offset + 0xD4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xD4); } - public int Reserved3 { get => BitConverter.ToInt32(Data, Offset + 0xD8); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xD8); } - public int Reserved4 { get => BitConverter.ToInt32(Data, Offset + 0xDC); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xDC); } + 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); } } [TypeConverter(typeof(ExpandableObjectConverter))] @@ -248,16 +249,16 @@ public OneDay8b(byte[] data, int offset = 0) public override string ToString() => $"{DeliveryID:0000} @ {LocalTimestamp:F}"; - public void CopyTo(byte[] data, int offset) => Data.AsSpan(Offset, SIZE).CopyTo(data.AsSpan(offset)); + public void CopyTo(Span destination, int offset) => Data.AsSpan(Offset, SIZE).CopyTo(destination[offset..]); public void Clear() => Data.AsSpan(Offset, SIZE).Clear(); - public long Ticks { get => BitConverter.ToInt64(Data, Offset); set => BitConverter.GetBytes(value).CopyTo(Data, Offset); } + public long Ticks { get => ReadInt64LittleEndian(Data.AsSpan(Offset)); set => WriteInt64LittleEndian(Data.AsSpan(Offset), 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 => BitConverter.ToUInt16(Data, Offset + 0x8); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x8); } - public short Reserved1 { get => BitConverter.ToInt16(Data, Offset + 0xA); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xA); } - public short Reserved2 { get => BitConverter.ToInt16(Data, Offset + 0xC); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xC); } - public short Reserved3 { get => BitConverter.ToInt16(Data, Offset + 0xE); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xE); } + 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); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayTime8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayTime8b.cs index d182812b7..537e98c9a 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayTime8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayTime8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -13,8 +14,8 @@ public sealed class PlayTime8b : SaveBlock public ushort PlayedHours { - get => BitConverter.ToUInt16(Data, Offset); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset), value); } public byte PlayedMinutes { get => Data[Offset + 2]; set => Data[Offset + 2] = value; } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayerData8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayerData8b.cs index 615031090..c0f7af05d 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayerData8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayerData8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -27,69 +28,69 @@ public sealed class PlayerData8b : SaveBlock public bool GearType { - get => BitConverter.ToUInt32(Data, Offset + 0x00) == 1; - set => BitConverter.GetBytes(value ? 1u : 0).CopyTo(Data, Offset + 0x00); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x00)) == 1; + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x00), value ? 1u : 0); } public bool ShoesFlag { - get => BitConverter.ToUInt32(Data, Offset + 0x04) == 1; - set => BitConverter.GetBytes(value ? 1u : 0).CopyTo(Data, Offset + 0x04); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x04)) == 1; + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x04), value ? 1u : 0); } public uint Form { - get => BitConverter.ToUInt32(Data, Offset + 0x08); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x08)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x08), value); } public byte BikeColor { get => Data[Offset + 0x0C]; set => Data[Offset + 0x0C] = value; } // 0x10: WorpPoint - Teleport // 0x10: WorpPoint - Zenmetu // 0x10: WorpPoint - Ananuke - public int WarpTeleportZone { get => BitConverter.ToInt32 (Data, Offset + OFS_LOC1 + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC1 + 0x00); } - public float WarpTeleportX { get => BitConverter.ToSingle(Data, Offset + OFS_LOC1 + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC1 + 0x04); } - public float WarpTeleportY { get => BitConverter.ToSingle(Data, Offset + OFS_LOC1 + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC1 + 0x08); } - public float WarpTeleportZ { get => BitConverter.ToSingle(Data, Offset + OFS_LOC1 + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC1 + 0x0C); } - public int WarpTeleportDir { get => BitConverter.ToInt32 (Data, Offset + OFS_LOC1 + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC1 + 0x10); } + 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 WarpZenmetuZone { get => BitConverter.ToInt32 (Data, Offset + OFS_LOC2 + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC2 + 0x00); } - public float WarpZenmetuX { get => BitConverter.ToSingle(Data, Offset + OFS_LOC2 + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC2 + 0x04); } - public float WarpZenmetuY { get => BitConverter.ToSingle(Data, Offset + OFS_LOC2 + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC2 + 0x08); } - public float WarpZenmetuZ { get => BitConverter.ToSingle(Data, Offset + OFS_LOC2 + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC2 + 0x0C); } - public int WarpZenmetuDir { get => BitConverter.ToInt32 (Data, Offset + OFS_LOC2 + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC2 + 0x10); } + 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 WarpAnanukeZone { get => BitConverter.ToInt32 (Data, Offset + OFS_LOC3 + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC3 + 0x00); } - public float WarpAnanukeX { get => BitConverter.ToSingle(Data, Offset + OFS_LOC3 + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC3 + 0x04); } - public float WarpAnanukeY { get => BitConverter.ToSingle(Data, Offset + OFS_LOC3 + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC3 + 0x08); } - public float WarpAnanukeZ { get => BitConverter.ToSingle(Data, Offset + OFS_LOC3 + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC3 + 0x0C); } - public int WarpAnanukeDir { get => BitConverter.ToInt32 (Data, Offset + OFS_LOC3 + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC3 + 0x10); } + 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 float WalkCount { - get => BitConverter.ToSingle(Data, Offset + OFS_PART2 + 0x00); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_PART2 + 0x04); + get => ReadSingleLittleEndian(Data.AsSpan(Offset + OFS_PART2 + 0x00)); + set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_PART2 + 0x04), value); } public int NatukiWalkCount { - get => BitConverter.ToInt32(Data, Offset + OFS_PART2 + 0x04); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_PART2 + 0x04); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_PART2 + 0x04)); + set => WriteSingleLittleEndian(Data.AsSpan(Offset + OFS_PART2 + 0x04), value); } - public int TownMapZone { get => BitConverter.ToInt32 (Data, Offset + OFS_MAP + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_MAP + 0x00); } - public float TownMapX { get => BitConverter.ToSingle(Data, Offset + OFS_MAP + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_MAP + 0x04); } - public float TownMapY { get => BitConverter.ToSingle(Data, Offset + OFS_MAP + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_MAP + 0x08); } - public float TownMapZ { get => BitConverter.ToSingle(Data, Offset + OFS_MAP + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_MAP + 0x0C); } - public int TownMapDir { get => BitConverter.ToInt32 (Data, Offset + OFS_MAP + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_MAP + 0x10); } + 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 bool IsTokushuLocation { - get => BitConverter.ToUInt32(Data, Offset + OFS_TOKUSHU_BOOL + 0x00) == 1; - set => BitConverter.GetBytes(value ? 1u : 0).CopyTo(Data, Offset + OFS_TOKUSHU_BOOL + 0x00); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU_BOOL + 0x00)) == 1; + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + OFS_TOKUSHU_BOOL + 0x00), value ? 1u : 0u); } - public int TokushuZone { get => BitConverter.ToInt32 (Data, Offset + OFS_TOKUSHU + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_TOKUSHU + 0x00); } - public float TokushuX { get => BitConverter.ToSingle(Data, Offset + OFS_TOKUSHU + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_TOKUSHU + 0x04); } - public float TokushuY { get => BitConverter.ToSingle(Data, Offset + OFS_TOKUSHU + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_TOKUSHU + 0x08); } - public float TokushuZ { get => BitConverter.ToSingle(Data, Offset + OFS_TOKUSHU + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_TOKUSHU + 0x0C); } - public int TokushuDir { get => BitConverter.ToInt32 (Data, Offset + OFS_TOKUSHU + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_TOKUSHU + 0x10); } + 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); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/PoffinSaveData8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/PoffinSaveData8b.cs index f984a2157..837f68bc1 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/PoffinSaveData8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/PoffinSaveData8b.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -30,7 +31,7 @@ private int GetPoffinOffset(int index) 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 => BitConverter.ToInt32(Data, Offset + 0x640); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x640); } + public int CookingCount { get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x640)); set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x640), value); } public Poffin8b[] GetPoffins() { @@ -74,7 +75,7 @@ public Poffin8b(byte[] data, int offset) public byte Level { get => Data[0x01]; set => Data[0x01] = value; } public byte Taste { get => Data[0x02]; set => Data[0x02] = value; } // Smoothness/feel of the Poffin => +sheen - public bool IsNew { get => BitConverter.ToUInt32(Data, 0x04) == 1; set => BitConverter.GetBytes(value ? 1u : 0u).CopyTo(Data, 0x04); } + public bool IsNew { get => ReadUInt32LittleEndian(Data.AsSpan(0x04)) == 1; set => WriteUInt32LittleEndian(Data.AsSpan(0x04), value ? 1u : 0u); } public byte FlavorSpicy { get => Data[0x08]; set => Data[0x08] = value; } public byte FlavorDry { get => Data[0x09]; set => Data[0x09] = value; } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/RandomGroup8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/RandomGroup8b.cs index 0bc6f7450..d974f37aa 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/RandomGroup8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/RandomGroup8b.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -67,46 +68,46 @@ public RandomSeed8b(byte[] data, int offset) public string GroupName { - get => StringConverter.GetString7b(Data, Offset + OFS_GROUPNAME, GROUP_NAME_SIZE * 2); - set => StringConverter.SetString7b(value, GROUP_NAME_SIZE, GROUP_NAME_SIZE).CopyTo(Data, Offset + OFS_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.AsSpan(), GROUP_NAME_SIZE, StringConverterOption.ClearZero); } public string PlayerName { - get => StringConverter.GetString7b(Data, Offset + OFS_PLAYERNAME, PERSON_NAME_SIZE * 2); - set => StringConverter.SetString7b(value, PERSON_NAME_SIZE, PERSON_NAME_SIZE).CopyTo(Data, Offset + OFS_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.AsSpan(), PERSON_NAME_SIZE, StringConverterOption.ClearZero); } public bool Male { get => Data[Offset + OFS_GENDER] == 1; set => Data[Offset + OFS_GENDER] = (byte)(value ? 1 : 0); } public int RegionCode { - get => BitConverter.ToInt32(Data, Offset + OFS_REGION); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_REGION); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_REGION)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + OFS_REGION), value); } public ulong Seed { - get => BitConverter.ToUInt64(Data, Offset + OFS_SEED); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SEED); + get => ReadUInt64LittleEndian(Data.AsSpan(Offset + OFS_SEED)); + set => WriteUInt64LittleEndian(Data.AsSpan(Offset + OFS_SEED), value); } public ulong Random { - get => BitConverter.ToUInt64(Data, Offset + OFS_RAND); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_RAND); + get => ReadUInt64LittleEndian(Data.AsSpan(Offset + OFS_RAND)); + set => WriteUInt64LittleEndian(Data.AsSpan(Offset + OFS_RAND), value); } public long Ticks { - get => BitConverter.ToInt64(Data, Offset + OFS_TICK); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_TICK); + get => ReadInt64LittleEndian(Data.AsSpan(Offset + OFS_TICK)); + set => WriteInt64LittleEndian(Data.AsSpan(Offset + OFS_TICK), value); } public int UserID { - get => BitConverter.ToInt32(Data, Offset + OFS_UID); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_UID); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + OFS_UID)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 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 f55c4e91e..57489d94b 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/Record8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/Record8b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -31,7 +32,7 @@ public int GetRecordOffset(int recordID) public int GetRecord(int recordID) { - var value = BitConverter.ToInt32(Data, GetRecordOffset(recordID)); + var value = ReadInt32LittleEndian(Data.AsSpan(GetRecordOffset(recordID))); if (recordID != 0) value = ClampRecord(recordID, value); return value; @@ -41,7 +42,7 @@ public void SetRecord(int recordID, int value) { if (recordID != 0) value = Math.Min(RecordMaxValue, value); - BitConverter.GetBytes(value).CopyTo(Data, GetRecordOffset(recordID)); + WriteInt32LittleEndian(Data.AsSpan(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 ad883df34..16b6af19d 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/RecordAddData8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/RecordAddData8b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -61,16 +62,16 @@ public RecordAdd8b(byte[] data, int offset) } public string OT { - get => StringConverter.GetString7b(Data, Offset + 0, 0x1A); - set => StringConverter.SetString7b(value, 12, 12).CopyTo(Data, Offset); + get => StringConverter8.GetString(Data.AsSpan(Offset + 0, 0x1A)); + set => StringConverter8.SetString(Data.AsSpan(Offset + 0, 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero); } // 1A reserved byte // 1B reserved byte public int Language { - get => BitConverter.ToInt32(Data, Offset + 0x1C); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x1C); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x1C)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x1C), value); } public byte Gender { get => Data[Offset + 0x20]; set => Data[Offset + 0x20] = value; } @@ -80,20 +81,20 @@ public int Language public int BodyType { - get => BitConverter.ToInt32(Data, Offset + 0x24); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x24); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x24)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x24), value); } public int TID { - get => BitConverter.ToUInt16(Data, Offset + 0x28); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x28); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x28)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x28), (ushort)value); } public int SID { - get => BitConverter.ToUInt16(Data, Offset + 0x2A); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x2A); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x2A)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x2A), (ushort)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 746d9c2b9..85eb0a019 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/SaveItemShortcut8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/SaveItemShortcut8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -11,9 +12,9 @@ public sealed class SaveItemShortcut8b : SaveBlock { public SaveItemShortcut8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public int Item0 { get => BitConverter.ToInt32(Data, Offset + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x00); } - public int Item1 { get => BitConverter.ToInt32(Data, Offset + 0x02); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x02); } - public int Item2 { get => BitConverter.ToInt32(Data, Offset + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x04); } - public int Item3 { get => BitConverter.ToInt32(Data, Offset + 0x06); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x06); } + 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); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/SealDeco8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/SealDeco8b.cs index ae733237c..30f7474a1 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/SealDeco8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/SealDeco8b.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -59,9 +60,9 @@ public SealCapsule8b(byte[] data, int offset) Data = data; Offset = offset; } - public uint Species { get => BitConverter.ToUInt32(Data, Offset + 0); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0); } - public uint EncryptionConstant { get => BitConverter.ToUInt32(Data, Offset + 4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 4); } - public uint Unknown { get => BitConverter.ToUInt32(Data, Offset + 8); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 8); } + 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 AffixSealData8b[] Seals { @@ -101,9 +102,9 @@ public AffixSealData8b(byte[] data, int offset) Offset = offset; } - public ushort SealID { get => BitConverter.ToUInt16(Data, Offset + 0); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0); } - public short X { get => BitConverter.ToInt16(Data, Offset + 2); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 2); } - public short Y { get => BitConverter.ToInt16(Data, Offset + 4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 4); } - public short Z { get => BitConverter.ToInt16(Data, Offset + 6); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 6); } + 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); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/SealSticker8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/SealSticker8b.cs index 6daadd2ca..d52fa6889 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/SealSticker8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/SealSticker8b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -16,17 +17,29 @@ public SealSticker8b(byte[] data, int baseOffset, int index) { Index = index; var offset = baseOffset + (SIZE * index); - IsGet = BitConverter.ToUInt32(data, offset + 0) == 1; - Count = BitConverter.ToInt32(data, offset + 4); - TotalCount = BitConverter.ToInt32(data, offset + 8); + var span = data.AsSpan(offset, SIZE); + Read(span); + } + + private void Read(ReadOnlySpan span) + { + IsGet = ReadUInt32LittleEndian(span) == 1; + Count = ReadInt32LittleEndian(span[4..]); + TotalCount = ReadInt32LittleEndian(span[8..]); } public void Write(byte[] data, int baseOffset) { var offset = baseOffset + (SIZE * Index); - BitConverter.GetBytes(IsGet ? 1u : 0u).CopyTo(data, offset + 0); - BitConverter.GetBytes(Count).CopyTo(data, offset + 4); - BitConverter.GetBytes(TotalCount).CopyTo(data, offset + 8); + var span = data.AsSpan(offset, SIZE); + Write(span); + } + + private void Write(Span span) + { + WriteUInt32LittleEndian(span, IsGet ? 1u : 0u); + WriteInt32LittleEndian(span[4..], Count); + WriteInt32LittleEndian(span[8..], TotalCount); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/SystemData8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/SystemData8b.cs index acdbee189..5b719d658 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/SystemData8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/SystemData8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -28,14 +29,14 @@ public sealed class SystemData8b : SaveBlock public SystemData8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public uint CountStart { get => BitConverter.ToUInt32(Data, Offset + 0 + (0 * SIZE_GMTIME)); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0 + (0 * SIZE_GMTIME)); } - public long TicksStart { get => BitConverter. ToInt64(Data, Offset + 4 + (0 * SIZE_GMTIME)); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 4 + (0 * SIZE_GMTIME)); } - public uint CountLatest { get => BitConverter.ToUInt32(Data, Offset + 0 + (1 * SIZE_GMTIME)); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0 + (1 * SIZE_GMTIME)); } - public long TicksLatest { get => BitConverter. ToInt64(Data, Offset + 4 + (1 * SIZE_GMTIME)); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 4 + (1 * SIZE_GMTIME)); } - public uint CountPenalty { get => BitConverter.ToUInt32(Data, Offset + 0 + (2 * SIZE_GMTIME)); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0 + (2 * SIZE_GMTIME)); } - public long TicksPenalty { get => BitConverter. ToInt64(Data, Offset + 4 + (2 * SIZE_GMTIME)); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 4 + (2 * SIZE_GMTIME)); } - public uint CountDaily { get => BitConverter.ToUInt32(Data, Offset + 0 + (3 * SIZE_GMTIME)); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0 + (3 * SIZE_GMTIME)); } - public long TicksDaily { get => BitConverter. ToInt64(Data, Offset + 4 + (3 * SIZE_GMTIME)); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 4 + (3 * SIZE_GMTIME)); } + 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); } // 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 887d2801e..c7daa6357 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/UgCountRecord8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/UgCountRecord8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -14,38 +15,38 @@ public sealed class UgCountRecord8b : SaveBlock public short DigFossilPlayCount { - get => BitConverter.ToInt16(Data, Offset + 0x00); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x00); + get => ReadInt16LittleEndian(Data.AsSpan(Offset + 0x00)); + set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0x00), value); } public short NumStatueBroadcastOnTV { - get => BitConverter.ToInt16(Data, Offset + 0x02); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x02); + get => ReadInt16LittleEndian(Data.AsSpan(Offset + 0x02)); + set => WriteInt16LittleEndian(Data.AsSpan(Offset + 0x02), value); } public int NumTimesSecretBaseBroadcastOnTVWereLiked { - get => BitConverter.ToInt32(Data, Offset + 0x04); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x04); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x04)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); } public int SomeoneSecretBaseLikeCount { - get => BitConverter.ToInt32(Data, Offset + 0x08); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x08)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x08), value); } public int NumSuccessfulLightStoneSearches { - get => BitConverter.ToInt32(Data, Offset + 0x0C); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x0C); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x0C)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x0C), value); } public long Reserved1 { - get => BitConverter.ToInt64(Data, Offset + 0x10); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10); + get => ReadInt64LittleEndian(Data.AsSpan(Offset + 0x10)); + set => WriteInt64LittleEndian(Data.AsSpan(Offset + 0x10), value); } public long Reserved2 { - get => BitConverter.ToInt64(Data, Offset + 0x18); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x18); + get => ReadInt64LittleEndian(Data.AsSpan(Offset + 0x18)); + set => WriteInt64LittleEndian(Data.AsSpan(Offset + 0x18), value); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/UgSaveData8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/UgSaveData8b.cs index f12347da1..94348b10d 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/UgSaveData8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/UgSaveData8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -65,16 +66,16 @@ public sealed class UgSaveData8b : SaveBlock public UgSaveData8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public int ReturnZoneID { get => BitConverter.ToInt32(Data, Offset + 0x0); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x0); } - public int ReturnGridPositionX { get => BitConverter.ToInt32(Data, Offset + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4); } - public int ReturnGridPositionY { get => BitConverter.ToInt32(Data, Offset + 0x8); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x8); } - public int ReturnGridPositionZ { get => BitConverter.ToInt32(Data, Offset + 0xC); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0xC); } + 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 => BitConverter.ToInt32 (Data, Offset + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10); } - public float ZenmetsuPositionX { get => BitConverter.ToSingle(Data, Offset + 0x14); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x14); } - public float ZenmetsuPositionY { get => BitConverter.ToSingle(Data, Offset + 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x18); } - public float ZenmetsuPositionZ { get => BitConverter.ToSingle(Data, Offset + 0x1C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x1C); } - public int ZenmetsuDirection { get => BitConverter.ToInt32 (Data, Offset + 0x20); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x20); } + 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); public void ClearDigPoints() => GetDigPoints().Fill(0xFF); @@ -86,9 +87,9 @@ public int GetSlotOffset(int slot) return Offset + OFS_ENCOUNTPOKE + (slot * PokeCrypto.SIZE_8PARTY); } - public int ReturnUgZoneID { get => BitConverter.ToInt32 (Data, Offset + OFS_ReturnUgZoneID); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ReturnUgZoneID); } - public uint TalkPlayerDataID { get => BitConverter.ToUInt32(Data, Offset + OFS_UgRecord + 0x0); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_UgRecord + 0x0); } - public uint TalkPlayerCount { get => BitConverter.ToUInt32(Data, Offset + OFS_UgRecord + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_UgRecord + 0x4); } + 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); } #region Seen NPCs diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/UndergroundItem8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/UndergroundItem8b.cs index c684817f6..2c06e4083 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/UndergroundItem8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/UndergroundItem8b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -13,21 +14,33 @@ public sealed class UndergroundItem8b public bool HideNewFlag { get; set; } public bool IsFavoriteFlag { get; set; } - public UndergroundItem8b(byte[] data, int baseOffset, int index) + public UndergroundItem8b(ReadOnlySpan data, int baseOffset, int index) { Index = index; var offset = baseOffset + (SIZE * index); - Count = BitConverter.ToInt32(data, offset + 0); - HideNewFlag = BitConverter.ToUInt32(data, offset + 4) == 1; - IsFavoriteFlag = BitConverter.ToUInt32(data, offset + 8) == 1; + var span = data.Slice(offset, SIZE); + Read(span); } - public void Write(byte[] data, int baseOffset) + public void Write(Span data, int baseOffset) { var offset = baseOffset + (SIZE * Index); - BitConverter.GetBytes(Count).CopyTo(data, offset + 0); - BitConverter.GetBytes(HideNewFlag ? 1u : 0u).CopyTo(data, offset + 4); - BitConverter.GetBytes(IsFavoriteFlag ? 1u : 0u).CopyTo(data, offset + 8); + var span = data.Slice(offset, SIZE); + Write(span); + } + + private void Read(ReadOnlySpan span) + { + Count = ReadInt32LittleEndian(span); + HideNewFlag = ReadUInt32LittleEndian(span[4..]) == 1; + IsFavoriteFlag = ReadUInt32LittleEndian(span[8..]) == 1; + } + + private void Write(Span span) + { + WriteInt32LittleEndian(span, Count); + WriteUInt32LittleEndian(span[4..], HideNewFlag ? 1u : 0u); + WriteUInt32LittleEndian(span[8..], IsFavoriteFlag ? 1u : 0u); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/UnionSaveData8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/UnionSaveData8b.cs index ecd8bccb8..bf0dab3fb 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/UnionSaveData8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/UnionSaveData8b.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -12,7 +13,7 @@ public sealed class UnionSaveData8b : SaveBlock { public UnionSaveData8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; - public bool IsInitTalk { get => BitConverter.ToUInt32(Data, Offset) == 1; set => BitConverter.GetBytes(value ? 1u : 0u).CopyTo(Data, Offset); } - public int PenaltyCount { get => BitConverter.ToInt32(Data, Offset + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4); } - public float PenaltyTime { get => BitConverter.ToSingle(Data, Offset + 0x8); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x8); } + 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); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/Zukan8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/Zukan8b.cs index d69340e4a..96f22c7e6 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/Zukan8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/Zukan8b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -141,7 +142,7 @@ public ZukanState8b GetState(int species) var index = species - 1; var offset = OFS_STATE + (sizeof(int) * index); - return (ZukanState8b)BitConverter.ToInt32(SAV.Data, PokeDex + offset); + return (ZukanState8b)ReadInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset)); } public void SetState(int species, ZukanState8b state) @@ -151,7 +152,7 @@ public void SetState(int species, ZukanState8b state) var index = species - 1; var offset = OFS_STATE + (sizeof(int) * index); - BitConverter.GetBytes((int)state).CopyTo(SAV.Data, PokeDex + offset); + WriteInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset), (int)state); } private bool GetBoolean(int index, int baseOffset, int max) @@ -160,7 +161,7 @@ private bool GetBoolean(int index, int baseOffset, int max) throw new ArgumentOutOfRangeException(nameof(index)); var offset = baseOffset + (ALIGN_BOOLARRAY * index); - return BitConverter.ToUInt32(SAV.Data, PokeDex + offset) == 1; + return ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset)) == 1; } private void SetBoolean(int index, int baseOffset, int max, bool value) @@ -169,7 +170,7 @@ private void SetBoolean(int index, int baseOffset, int max, bool value) throw new ArgumentOutOfRangeException(nameof(index)); var offset = baseOffset + (ALIGN_BOOLARRAY * index); - BitConverter.GetBytes((uint)(value ? 1 : 0)).CopyTo(SAV.Data, PokeDex + offset); + WriteUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset), value ? 1u : 0u); } public void GetGenderFlags(int species, out bool m, out bool f, out bool ms, out bool fs) @@ -198,7 +199,7 @@ public bool GetLanguageFlag(int species, int language) var index = species - 1; var offset = OFS_LANGUAGE + (sizeof(int) * index); - var current = BitConverter.ToInt32(SAV.Data, PokeDex + offset); + var current = ReadInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset)); return (current & (1 << languageBit)) != 0; } @@ -212,10 +213,10 @@ public void SetLanguageFlag(int species, int language, bool value) var index = species - 1; var offset = OFS_LANGUAGE + (sizeof(int) * index); - var current = BitConverter.ToInt32(SAV.Data, PokeDex + offset); + var current = ReadInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset)); var mask = (1 << languageBit); var update = value ? current | mask : current & ~(mask); - BitConverter.GetBytes(update).CopyTo(SAV.Data, PokeDex + offset); + WriteInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset), update); } public void SetLanguageFlags(int species, int value) @@ -225,7 +226,7 @@ public void SetLanguageFlags(int species, int value) var index = species - 1; var offset = OFS_LANGUAGE + (sizeof(int) * index); - BitConverter.GetBytes(value).CopyTo(SAV.Data, PokeDex + offset); + WriteInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset), value); } private static int GetLanguageBit(int language) @@ -239,14 +240,14 @@ private static int GetLanguageBit(int language) public bool HasRegionalDex { - get => BitConverter.ToUInt32(SAV.Data, PokeDex + OFS_FLAG_REGIONAL) == 1; - set => BitConverter.GetBytes((uint)(value ? 1 : 0)).CopyTo(SAV.Data, PokeDex + OFS_FLAG_REGIONAL); + get => ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + OFS_FLAG_REGIONAL)) == 1; + set => WriteUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + OFS_FLAG_REGIONAL), value ? 1u : 0u); } public bool HasNationalDex { - get => BitConverter.ToUInt32(SAV.Data, PokeDex + OFS_FLAG_NATIONAL) == 1; - set => BitConverter.GetBytes((uint)(value ? 1 : 0)).CopyTo(SAV.Data, PokeDex + OFS_FLAG_NATIONAL); + get => ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + OFS_FLAG_NATIONAL)) == 1; + set => WriteUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + OFS_FLAG_NATIONAL), value ? 1u : 0u); } public bool GetHasFormFlag(int species, int form, bool shiny) @@ -258,7 +259,7 @@ public bool GetHasFormFlag(int species, int form, bool shiny) var baseOffset = GetFormOffset(species); var sizeShift = shiny ? GetFormSize(species) : 0; var offset = baseOffset + sizeShift + (ALIGN_BOOLARRAY * form); - return BitConverter.ToUInt32(SAV.Data, PokeDex + offset) == 1; + return ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset)) == 1; } public void SetHasFormFlag(int species, int form, bool shiny, bool value) @@ -270,7 +271,7 @@ public void SetHasFormFlag(int species, int form, bool shiny, bool value) var baseOffset = GetFormOffset(species); var sizeShift = shiny ? GetFormSize(species) : 0; var offset = baseOffset + sizeShift + (ALIGN_BOOLARRAY * form); - BitConverter.GetBytes((uint)(value ? 1 : 0)).CopyTo(SAV.Data, PokeDex + offset); + WriteUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + offset), value ? 1u : 0u); } public static int GetFormCount(int species) => species switch diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/ZukanSpinda8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/ZukanSpinda8b.cs index f86e67fed..b8362bc37 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/ZukanSpinda8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/ZukanSpinda8b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -13,25 +14,25 @@ public sealed class ZukanSpinda8b : SaveBlock public uint GetSeen(int gender, bool shiny) { var ofs = GetOffset(gender, shiny); - return BitConverter.ToUInt32(Data, Offset + ofs); + return ReadUInt32LittleEndian(Data.AsSpan(Offset + ofs)); } public uint GetCaught(int gender, bool shiny) { var ofs = GetOffset(gender, shiny); - return BitConverter.ToUInt32(Data, Offset + 0x10 + ofs); + return ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x10 + ofs)); } public void SetSeen(int gender, bool shiny, uint value) { var ofs = GetOffset(gender, shiny); - BitConverter.GetBytes(value).CopyTo(Data, Offset + ofs); + WriteUInt32LittleEndian(Data.AsSpan(Offset + ofs), value); } public void SetCaught(int gender, bool shiny, uint value) { var ofs = GetOffset(gender, shiny); - BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10 + ofs); + WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x10 + ofs), value); } private static int GetOffset(int gender, bool shiny) => 4 * ((gender & 1) + (shiny ? 2 : 0)); diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BoxLayout8.cs b/PKHeX.Core/Saves/Substructures/Gen8/BoxLayout8.cs index c197a0d71..ca1372e1d 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BoxLayout8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BoxLayout8.cs @@ -1,4 +1,6 @@ -namespace PKHeX.Core +using System; + +namespace PKHeX.Core { public sealed class BoxLayout8 : SaveBlock, IBoxDetailName { @@ -9,18 +11,9 @@ public sealed class BoxLayout8 : SaveBlock, IBoxDetailName public BoxLayout8(SAV8SWSH sav, SCBlock block) : base(sav, block.Data) { } private static int GetBoxNameOffset(int box) => SAV6.LongStringLength * box; - - public string GetBoxName(int box) - { - return SAV.GetString(Data, GetBoxNameOffset(box), SAV6.LongStringLength); - } - - public void SetBoxName(int box, string value) - { - var data = SAV.SetString(value, StringMaxLength, StringMaxLength, 0); - var offset = GetBoxNameOffset(box); - SAV.SetData(Data, data, offset); - } + private Span GetBoxNameSpan(int box) => Data.AsSpan(GetBoxNameOffset(box), SAV6.LongStringLength); + public string GetBoxName(int box) => SAV.GetString(GetBoxNameSpan(box)); + public void SetBoxName(int box, string value) => SAV.SetString(GetBoxNameSpan(box), value.AsSpan(), StringMaxLength, StringConverterOption.ClearZero); public string this[int i] { diff --git a/PKHeX.Core/Saves/Substructures/Gen8/Daycare8.cs b/PKHeX.Core/Saves/Substructures/Gen8/Daycare8.cs index f78a83a9e..a6a2192a0 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/Daycare8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/Daycare8.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -86,7 +87,7 @@ public static int GetDaycare2StructOffset(int slot) _ => throw new IndexOutOfRangeException(nameof(daycare)), }; - public ulong GetDaycareSeed(int daycare) => BitConverter.ToUInt64(Data, GetDaycareMetadataOffset(daycare) + 6); - public void SetDaycareSeed(int daycare, ulong value) => SAV.SetData(Data, BitConverter.GetBytes(value), GetDaycareMetadataOffset(daycare) + 6); + 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); } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/Gen8/FashionUnlock8.cs b/PKHeX.Core/Saves/Substructures/Gen8/FashionUnlock8.cs index 66833bc4a..8324b6645 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/FashionUnlock8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/FashionUnlock8.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; namespace PKHeX.Core @@ -10,13 +11,13 @@ public sealed class FashionUnlock8 : SaveBlock public FashionUnlock8(SaveFile sav, SCBlock block) : base(sav, block.Data) { } - public bool[] GetArrayOwned(int region) => ArrayUtil.GitBitFlagArray(Data, region * SIZE_ENTRY, SIZE_ENTRY * 8); - public bool[] GetArrayNew(int region) => ArrayUtil.GitBitFlagArray(Data, (region + REGIONS) * SIZE_ENTRY, SIZE_ENTRY * 8); + public bool[] GetArrayOwned(int region) => ArrayUtil.GitBitFlagArray(Data.AsSpan(region * SIZE_ENTRY), SIZE_ENTRY * 8); + public bool[] GetArrayNew(int region) => ArrayUtil.GitBitFlagArray(Data.AsSpan((region + REGIONS) * SIZE_ENTRY), SIZE_ENTRY * 8); public int[] GetIndexesOwned(int region) => GetIndexes(GetArrayOwned(region)); public int[] GetIndexesNew(int region) => GetIndexes(GetArrayNew(region)); - public void SetArrayOwned(int region, bool[] value) => ArrayUtil.SetBitFlagArray(Data, region * SIZE_ENTRY, value); - public void SetArrayNew(int region, bool[] value) => ArrayUtil.SetBitFlagArray(Data, (region + REGIONS) * SIZE_ENTRY, value); + public void SetArrayOwned(int region, bool[] value) => ArrayUtil.SetBitFlagArray(Data.AsSpan(region * SIZE_ENTRY), value); + public void SetArrayNew(int region, bool[] value) => ArrayUtil.SetBitFlagArray(Data.AsSpan((region + REGIONS) * SIZE_ENTRY), value); public void SetIndexesOwned(int region, int[] value) => SetArrayOwned(region, SetIndexes(value)); public void SetIndexesNew(int region, int[] value) => SetArrayNew(region, SetIndexes(value)); diff --git a/PKHeX.Core/Saves/Substructures/Gen8/FieldMoveModelSave8.cs b/PKHeX.Core/Saves/Substructures/Gen8/FieldMoveModelSave8.cs index 5420f7a63..ea930a58c 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/FieldMoveModelSave8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/FieldMoveModelSave8.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -6,10 +7,10 @@ public sealed class FieldMoveModelSave8 : SaveBlock { public FieldMoveModelSave8(SAV8SWSH sav, SCBlock block) : base(sav, block.Data) { } - public int M { get => BitConverter.ToUInt16(Data, 0x00); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x00); } - public float X { get => BitConverter.ToSingle(Data, 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, 0x08); } - public float Z { get => BitConverter.ToSingle(Data, 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, 0x10); } - public float Y { get => (int)BitConverter.ToSingle(Data, 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, 0x18); } - public float R { get => (int)BitConverter.ToSingle(Data, 0x20); set => BitConverter.GetBytes(value).CopyTo(Data, 0x20); } + 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); } } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/Gen8/HallOfFameTime8.cs b/PKHeX.Core/Saves/Substructures/Gen8/HallOfFameTime8.cs index d31107828..1a1064ba0 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/HallOfFameTime8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/HallOfFameTime8.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -16,8 +17,8 @@ public HallOfFameTime8(SAV8SWSH sav, SCBlock block) : base(sav, block.Data) [Category(General), Description("Raw amount of seconds since 1970 (Unix Timestamp)")] public long TimeStamp { - get => BitConverter.ToInt64(Data, Offset + 0); - set => SAV.SetData(Data, BitConverter.GetBytes(value), Offset + 0); + get => ReadInt64LittleEndian(Data.AsSpan(Offset + 0)); + set => WriteInt64LittleEndian(Data.AsSpan(Offset), value); } [Category(General), Description("Date and Time (UTC) the player entered the Hall Of Fame")] @@ -37,4 +38,4 @@ public DateTime Date } } } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Saves/Substructures/Gen8/Misc8.cs b/PKHeX.Core/Saves/Substructures/Gen8/Misc8.cs index c9f7351b0..af7bcf62d 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/Misc8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/Misc8.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -14,19 +15,19 @@ public int Badges public uint Money { - get => BitConverter.ToUInt32(Data, Offset + 0x04); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x04)); set { if (value > 9999999) value = 9999999; - SAV.SetData(Data, BitConverter.GetBytes(value), Offset + 0x04); + WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); } } public int BP { - get => BitConverter.ToUInt16(Data, Offset + 0x11C); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x11C); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x11C)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x11C), (ushort)value); } } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/Gen8/MyStatus8.cs b/PKHeX.Core/Saves/Substructures/Gen8/MyStatus8.cs index 103f65555..53de75541 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/MyStatus8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/MyStatus8.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -22,114 +23,114 @@ public string Number public ulong Skin // aka the base model { - get => BitConverter.ToUInt64(Data, 0x08); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x08); + get => ReadUInt64LittleEndian(Data.AsSpan(0x08)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x08), value); } public ulong Hair { - get => BitConverter.ToUInt64(Data, 0x10); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x10); + get => ReadUInt64LittleEndian(Data.AsSpan(0x10)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x10), value); } public ulong Brow { - get => BitConverter.ToUInt64(Data, 0x18); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x18); + get => ReadUInt64LittleEndian(Data.AsSpan(0x18)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x18), value); } public ulong Lashes { - get => BitConverter.ToUInt64(Data, 0x20); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x20); + get => ReadUInt64LittleEndian(Data.AsSpan(0x20)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x20), value); } public ulong Contacts { - get => BitConverter.ToUInt64(Data, 0x28); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x28); + get => ReadUInt64LittleEndian(Data.AsSpan(0x28)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x28), value); } public ulong Lips { - get => BitConverter.ToUInt64(Data, 0x30); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x30); + get => ReadUInt64LittleEndian(Data.AsSpan(0x30)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x30), value); } public ulong Glasses { - get => BitConverter.ToUInt64(Data, 0x38); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x38); + get => ReadUInt64LittleEndian(Data.AsSpan(0x38)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x38), value); } public ulong Hat { - get => BitConverter.ToUInt64(Data, 0x40); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x40); + get => ReadUInt64LittleEndian(Data.AsSpan(0x40)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x40), value); } public ulong Jacket { - get => BitConverter.ToUInt64(Data, 0x48); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x48); + get => ReadUInt64LittleEndian(Data.AsSpan(0x48)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x48), value); } public ulong Top { - get => BitConverter.ToUInt64(Data, 0x50); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x50); + get => ReadUInt64LittleEndian(Data.AsSpan(0x50)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x50), value); } public ulong Bag { - get => BitConverter.ToUInt64(Data, 0x58); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x58); + get => ReadUInt64LittleEndian(Data.AsSpan(0x58)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x58), value); } public ulong Gloves { - get => BitConverter.ToUInt64(Data, 0x60); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x60); + get => ReadUInt64LittleEndian(Data.AsSpan(0x60)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x60), value); } public ulong BottomOrDress { - get => BitConverter.ToUInt64(Data, 0x68); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x68); + get => ReadUInt64LittleEndian(Data.AsSpan(0x68)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x68), value); } public ulong Sock { - get => BitConverter.ToUInt64(Data, 0x70); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x70); + get => ReadUInt64LittleEndian(Data.AsSpan(0x70)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x70), value); } public ulong Shoe { - get => BitConverter.ToUInt64(Data, 0x78); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x78); + get => ReadUInt64LittleEndian(Data.AsSpan(0x78)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x78), value); } // 80 - 87 public ulong MomSkin // aka the base model { - get => BitConverter.ToUInt64(Data, 0x88); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x88); + get => ReadUInt64LittleEndian(Data.AsSpan(0x88)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x88), value); } // 8C - 9F public int TID { - get => BitConverter.ToUInt16(Data, 0xA0); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xA0); + get => ReadUInt16LittleEndian(Data.AsSpan(0xA0)); + set => WriteUInt16LittleEndian(Data.AsSpan(0xA0), (ushort)value); } public int SID { - get => BitConverter.ToUInt16(Data, 0xA2); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xA2); + get => ReadUInt16LittleEndian(Data.AsSpan(0xA2)); + set => WriteUInt16LittleEndian(Data.AsSpan(0xA2), (ushort)value); } public int Game @@ -161,21 +162,23 @@ public int Language } } + private Span OT_Trash =>Data.AsSpan(0xB0, 0x1A); + public string OT { - get => SAV.GetString(Data, 0xB0, 0x1A); - set => SAV.SetData(Data, SAV.SetString(value, SAV.OTLength), 0xB0); + get => SAV.GetString(OT_Trash); + set => SAV.SetString(OT_Trash, value.AsSpan(), SAV.OTLength, StringConverterOption.ClearZero); } // D0 public uint Watt { - get => BitConverter.ToUInt32(Data, Offset + 0xD0); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0xD0)); set { if (value > MaxWatt) value = MaxWatt; - SAV.SetData(Data, BitConverter.GetBytes(value), Offset + 0xD0); + WriteUInt32LittleEndian(Data.AsSpan(Offset + 0xD0), value); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/PlayTime8.cs b/PKHeX.Core/Saves/Substructures/Gen8/PlayTime8.cs index d7bf17501..a9246a4aa 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/PlayTime8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/PlayTime8.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,8 +9,8 @@ public sealed class PlayTime8 : SaveBlock public int PlayedHours { - get => BitConverter.ToUInt16(Data, Offset); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset), (ushort)value); } public int PlayedMinutes @@ -25,7 +26,7 @@ public int PlayedSeconds } // TODO - private uint LastSaved { get => BitConverter.ToUInt32(Data, Offset + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4); } + private uint LastSaved { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x4)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 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/RaidSpawnList8.cs b/PKHeX.Core/Saves/Substructures/Gen8/RaidSpawnList8.cs index 1ccb1c11d..6e6f927ed 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/RaidSpawnList8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/RaidSpawnList8.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -80,15 +81,15 @@ public RaidSpawnDetail(byte[] data, int ofs) [Category(General), Description("FNV Hash for fetching the Raid data table (64bit)."), TypeConverter(typeof(TypeConverterU64))] public ulong Hash { - get => BitConverter.ToUInt64(Data, Offset + 0); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0); + get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0)); + set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0), value); } [Category(General), Description("RNG Seed for generating the Raid's content (64bit)."), TypeConverter(typeof(TypeConverterU64))] public ulong Seed { - get => BitConverter.ToUInt64(Data, Offset + 8); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 8); + get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 8)); + set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 8), value); } [Category(General), Description("Star Count for the Raid's content (0-4).")] diff --git a/PKHeX.Core/Saves/Substructures/Gen8/Record8.cs b/PKHeX.Core/Saves/Substructures/Gen8/Record8.cs index bec2f4d41..527d5c5dd 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/Record8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/Record8.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -17,7 +18,7 @@ public override int GetRecord(int recordID) { int ofs = Records.GetOffset(Offset, recordID); if (recordID < RecordCount) - return BitConverter.ToInt32(Data, ofs); + return ReadInt32LittleEndian(Data.AsSpan(ofs)); Trace.Fail(nameof(recordID)); return 0; } @@ -31,7 +32,7 @@ public override void SetRecord(int recordID, int value) if (value > max) value = max; if (recordID < RecordCount) - BitConverter.GetBytes(value).CopyTo(Data, ofs); + WriteInt32LittleEndian(Data.AsSpan(ofs), value); else Trace.Fail(nameof(recordID)); } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/TeamIndexes8.cs b/PKHeX.Core/Saves/Substructures/Gen8/TeamIndexes8.cs index 9da5ea9e2..c8b2ac930 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/TeamIndexes8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/TeamIndexes8.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -20,7 +21,7 @@ public void LoadBattleTeams() for (int i = 0; i < TeamCount * 6; i++) { - short val = BitConverter.ToInt16(Data, Offset + (i * 2)); + short val = ReadInt16LittleEndian(Data.AsSpan(Offset + (i * 2))); if (val < 0) { TeamSlots[i] = NONE_SELECTED; @@ -48,18 +49,19 @@ public void UnlockAllTeams() public void SaveBattleTeams() { + var span = Data.AsSpan(Offset); for (int i = 0; i < TeamCount * 6; i++) { int index = TeamSlots[i]; if (index < 0) { - BitConverter.GetBytes((short)index).CopyTo(Data, Offset + (i * 2)); + WriteInt16LittleEndian(span[(i * 2)..], (short)index); continue; } SAV.GetBoxSlotFromIndex(index, out var box, out var slot); - int val = (box << 8) | slot; - BitConverter.GetBytes((short)val).CopyTo(Data, Offset + (i * 2)); + index = (box << 8) | slot; + WriteInt16LittleEndian(span[(i * 2)..], (short)index); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/TitleScreen8.cs b/PKHeX.Core/Saves/Substructures/Gen8/TitleScreen8.cs index 9b9839601..c3a683428 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/TitleScreen8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/TitleScreen8.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -48,20 +49,20 @@ public TitleScreen8Poke(byte[] data, int offset) public int Species { - get => BitConverter.ToInt32(Data, Offset + 0x00); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x00); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x00)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x00), value); } public int Form { - get => BitConverter.ToInt32(Data, Offset + 0x04); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x04); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x04)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); } public int Gender { - get => BitConverter.ToInt32(Data, Offset + 0x08); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x08)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x08), value); } public bool IsShiny @@ -72,38 +73,38 @@ public bool IsShiny public uint EncryptionConstant { - get => BitConverter.ToUInt32(Data, Offset + 0x10); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x10)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x10), value); } public int FormArgument { - get => BitConverter.ToInt32(Data, Offset + 0x14); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x14); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x14)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x14), value); } public uint Unknown18 { - get => BitConverter.ToUInt32(Data, Offset + 0x18); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x18); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x18)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x18), value); } public uint Unknown1C { - get => BitConverter.ToUInt32(Data, Offset + 0x1C); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x1C); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x1C)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x1C), value); } public uint Unknown20 { - get => BitConverter.ToUInt32(Data, Offset + 0x20); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x20); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x20)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x20), value); } public uint Unknown24 { - get => BitConverter.ToUInt32(Data, Offset + 0x24); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x24); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x24)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x24), value); } public void Clear() => Array.Clear(Data, Offset, SIZE); diff --git a/PKHeX.Core/Saves/Substructures/Gen8/TrainerCard8.cs b/PKHeX.Core/Saves/Substructures/Gen8/TrainerCard8.cs index 6fc945cfb..b2aaeb3c1 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/TrainerCard8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/TrainerCard8.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,10 +9,12 @@ public sealed class TrainerCard8 : SaveBlock { public TrainerCard8(SAV8SWSH sav, SCBlock block) : base (sav, block.Data) { } + private Span OT_Trash => Data.AsSpan(0x00, 0x1A); + public string OT { - get => SAV.GetString(Data, 0x00, 0x1A); - set => SAV.SetData(Data, SAV.SetString(value, SAV.OTLength), 0x00); + get => SAV.GetString(OT_Trash); + set => SAV.SetString(OT_Trash, value.AsSpan(), SAV.OTLength, StringConverterOption.ClearZero); } public byte Language @@ -22,20 +25,20 @@ public byte Language public int TrainerID { - get => BitConverter.ToInt32(Data, 0x1C); - set => SAV.SetData(Data, BitConverter.GetBytes(value), 0x1C); + get => ReadInt32LittleEndian(Data.AsSpan(0x1C)); + set => WriteInt32LittleEndian(Data.AsSpan(0x1C), value); } public ushort PokeDexOwned { - get => BitConverter.ToUInt16(Data, Offset + 0x20); - set => SAV.SetData(Data, BitConverter.GetBytes(value), Offset + 0x20); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x20)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x20), value); } public ushort ShinyPokemonFound { - get => BitConverter.ToUInt16(Data, Offset + 0x22); - set => SAV.SetData(Data, BitConverter.GetBytes(value), Offset + 0x22); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x22)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x22), value); } public byte Game @@ -52,21 +55,20 @@ public byte Starter public ushort CurryTypesOwned { - get => BitConverter.ToUInt16(Data, Offset + 0x26); - set => SAV.SetData(Data, BitConverter.GetBytes(value), Offset + 0x26); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x26)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x26), value); } public const int RotoRallyScoreMax = 99_999; public int RotoRallyScore { - get => BitConverter.ToInt32(Data, 0x28); + get => ReadInt32LittleEndian(Data.AsSpan(0x28)); set { if (value > RotoRallyScoreMax) value = RotoRallyScoreMax; - var data = BitConverter.GetBytes(value); - SAV.SetData(Data, data, 0x28); + WriteInt32LittleEndian(Data.AsSpan(0x28), value); // set to the other block since it doesn't have an accessor ((SAV8SWSH)SAV).SetValue(SaveBlockAccessor8SWSH.KRotoRally, (uint)value); } @@ -76,13 +78,12 @@ public int RotoRallyScore public int CaughtPokemon { - get => BitConverter.ToInt32(Data, 0x2C); + get => ReadInt32LittleEndian(Data.AsSpan(0x2C)); set { if (value > MaxPokemonCaught) value = MaxPokemonCaught; - var data = BitConverter.GetBytes(value); - SAV.SetData(Data, data, 0x2C); + WriteInt32LittleEndian(Data.AsSpan(0x2C), value); } } @@ -123,98 +124,98 @@ public string Number public ulong Skin // aka the base model { - get => BitConverter.ToUInt64(Data, 0x40); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x40); + get => ReadUInt64LittleEndian(Data.AsSpan(0x40)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x40), value); } public ulong Hair { - get => BitConverter.ToUInt64(Data, 0x48); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x48); + get => ReadUInt64LittleEndian(Data.AsSpan(0x48)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x48), value); } public ulong Brow { - get => BitConverter.ToUInt64(Data, 0x50); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x50); + get => ReadUInt64LittleEndian(Data.AsSpan(0x50)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x50), value); } public ulong Lashes { - get => BitConverter.ToUInt64(Data, 0x58); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x58); + get => ReadUInt64LittleEndian(Data.AsSpan(0x58)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x58), value); } public ulong Contacts { - get => BitConverter.ToUInt64(Data, 0x60); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x60); + get => ReadUInt64LittleEndian(Data.AsSpan(0x60)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x60), value); } public ulong Lips { - get => BitConverter.ToUInt64(Data, 0x68); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x68); + get => ReadUInt64LittleEndian(Data.AsSpan(0x68)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x68), value); } public ulong Glasses { - get => BitConverter.ToUInt64(Data, 0x70); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x70); + get => ReadUInt64LittleEndian(Data.AsSpan(0x70)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x70), value); } public ulong Hat { - get => BitConverter.ToUInt64(Data, 0x78); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x78); + get => ReadUInt64LittleEndian(Data.AsSpan(0x78)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x78), value); } public ulong Jacket { - get => BitConverter.ToUInt64(Data, 0x80); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x80); + get => ReadUInt64LittleEndian(Data.AsSpan(0x80)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x80), value); } public ulong Top { - get => BitConverter.ToUInt64(Data, 0x88); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x88); + get => ReadUInt64LittleEndian(Data.AsSpan(0x88)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x88), value); } public ulong Bag { - get => BitConverter.ToUInt64(Data, 0x90); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x90); + get => ReadUInt64LittleEndian(Data.AsSpan(0x90)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x90), value); } public ulong Gloves { - get => BitConverter.ToUInt64(Data, 0x98); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x98); + get => ReadUInt64LittleEndian(Data.AsSpan(0x98)); + set => WriteUInt64LittleEndian(Data.AsSpan(0x98), value); } public ulong BottomOrDress { - get => BitConverter.ToUInt64(Data, 0xA0); - set => BitConverter.GetBytes(value).CopyTo(Data, 0xA0); + get => ReadUInt64LittleEndian(Data.AsSpan(0xA0)); + set => WriteUInt64LittleEndian(Data.AsSpan(0xA0), value); } public ulong Sock { - get => BitConverter.ToUInt64(Data, 0xA8); - set => BitConverter.GetBytes(value).CopyTo(Data, 0xA8); + get => ReadUInt64LittleEndian(Data.AsSpan(0xA8)); + set => WriteUInt64LittleEndian(Data.AsSpan(0xA8), value); } public ulong Shoe { - get => BitConverter.ToUInt64(Data, 0xB0); - set => BitConverter.GetBytes(value).CopyTo(Data, 0xB0); + get => ReadUInt64LittleEndian(Data.AsSpan(0xB0)); + set => WriteUInt64LittleEndian(Data.AsSpan(0xB0), value); } public ulong MomSkin // aka the base model { - get => BitConverter.ToUInt64(Data, 0xC0); - set => BitConverter.GetBytes(value).CopyTo(Data, 0xC0); + get => ReadUInt64LittleEndian(Data.AsSpan(0xC0)); + set => WriteUInt64LittleEndian(Data.AsSpan(0xC0), value); } // Trainer Card Pokemon @@ -250,8 +251,8 @@ public void LoadTeamData(IList party) public ushort StartedYear { - get => BitConverter.ToUInt16(Data, Offset + 0x170); - set => SAV.SetData(Data, BitConverter.GetBytes(value), Offset + 0x170); + get => ReadUInt16LittleEndian(Data.AsSpan(Offset + 0x170)); + set => WriteUInt16LittleEndian(Data.AsSpan(Offset + 0x170), value); } public byte StartedMonth @@ -269,8 +270,8 @@ public byte StartedDay public uint TimestampPrinted { // should this be a ulong? - get => BitConverter.ToUInt32(Data, Offset + 0x1A8); - set => SAV.SetData(Data, BitConverter.GetBytes(value), Offset + 0x1A8); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x1A8)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x1A8), value); } } @@ -288,20 +289,20 @@ public TrainerCard8Poke(byte[] data, int offset) public int Species { - get => BitConverter.ToInt32(Data, Offset + 0x00); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x00); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x00)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x00), value); } public int Form { - get => BitConverter.ToInt32(Data, Offset + 0x04); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x04); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x04)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x04), value); } public int Gender { - get => BitConverter.ToInt32(Data, Offset + 0x08); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x08)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x08), value); } public bool IsShiny @@ -312,20 +313,20 @@ public bool IsShiny public uint EncryptionConstant { - get => BitConverter.ToUInt32(Data, Offset + 0x10); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x10)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x10), value); } public uint Unknown { - get => BitConverter.ToUInt32(Data, Offset + 0x14); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x14); + get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x14)); + set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x14), value); } public int FormArgument { - get => BitConverter.ToInt32(Data, Offset + 0x18); - set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x18); + get => ReadInt32LittleEndian(Data.AsSpan(Offset + 0x18)); + set => WriteInt32LittleEndian(Data.AsSpan(Offset + 0x18), value); } public void Clear() => Array.Clear(Data, Offset, SIZE); diff --git a/PKHeX.Core/Saves/Substructures/Inventory/BeanPouch.cs b/PKHeX.Core/Saves/Substructures/Inventory/BeanPouch.cs index 4dec90036..4c5964256 100644 --- a/PKHeX.Core/Saves/Substructures/Inventory/BeanPouch.cs +++ b/PKHeX.Core/Saves/Substructures/Inventory/BeanPouch.cs @@ -37,32 +37,7 @@ private enum BeanColor private readonly SAV7 SAV; public BeanPouch(SAV7 sav) => SAV = sav; - public int[] Beans - { - get => GetBeanCounts(); - set => SetBeanCounts(value); - } - - private int[] GetBeanCounts() - { - int[] beans = new int[Count]; - for (int i = 0; i < beans.Length; i++) - beans[i] = SAV.ResortSave.GetPokebeanCount(i); - return beans; - } - - private void SetBeanCounts(IReadOnlyList beans) - { - if (beans.Count != Count) - return; - for (int i = 0; i < beans.Count; i++) - SAV.ResortSave.SetPokebeanCount(i, beans[i]); - } - - public void SetCountAll(int val) - { - for (int i = 0; i < Count; i++) - SAV.ResortSave.SetPokebeanCount(i, val); - } + public Span Beans => SAV.ResortSave.GetBeans(); + public void SetCountAll(byte val) => SAV.ResortSave.GetBeans().Fill(val); } } diff --git a/PKHeX.Core/Saves/Substructures/Inventory/InventoryItem8b.cs b/PKHeX.Core/Saves/Substructures/Inventory/InventoryItem8b.cs index ee045a075..9facce447 100644 --- a/PKHeX.Core/Saves/Substructures/Inventory/InventoryItem8b.cs +++ b/PKHeX.Core/Saves/Substructures/Inventory/InventoryItem8b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -20,27 +21,27 @@ public sealed class InventoryItem8b : InventoryItem, IItemFavorite, IItemNew /// public bool IsValidSaveSortNumberCount => SortOrder != 0; - public static InventoryItem8b Read(ushort index, byte[] data, int offset) => new() + public static InventoryItem8b Read(ushort index, ReadOnlySpan data) => new() { Index = index, - Count = BitConverter.ToInt32(data, offset), - IsNew = BitConverter.ToInt32(data, offset + 4) == 0, - IsFavorite = BitConverter.ToInt32(data, offset + 0x8) == 1, - SortOrder = BitConverter.ToUInt16(data, offset + 0xC), + Count = ReadInt32LittleEndian(data), + IsNew = ReadInt32LittleEndian(data[4..]) == 0, + IsFavorite = ReadInt32LittleEndian(data[8..]) == 1, + SortOrder = ReadUInt16LittleEndian(data[12..]), // 0xE alignment }; - public void Write(byte[] data, int offset) + public void Write(Span data) { // Index is not saved. - BitConverter.GetBytes((uint)Count).CopyTo(data, offset); - BitConverter.GetBytes(IsNew ? 0u : 1u).CopyTo(data, offset + 0x4); - BitConverter.GetBytes(IsFavorite ? 1u : 0u).CopyTo(data, offset + 0x8); - BitConverter.GetBytes(SortOrder).CopyTo(data, offset + 0xC); - BitConverter.GetBytes((ushort)0).CopyTo(data, offset + 0xE); + WriteUInt32LittleEndian(data, (uint)Count); + WriteUInt32LittleEndian(data[4..], IsNew ? 0u : 1u); + WriteUInt32LittleEndian(data[8..], IsFavorite ? 1u : 0u); + WriteUInt16LittleEndian(data[12..], SortOrder); + WriteUInt16LittleEndian(data[14..], 0); } - public static void Clear(byte[] data, int offset) => Array.Clear(data, offset, SIZE); + public static void Clear(Span data, int offset) => data.Slice(offset, SIZE).Clear(); public override void SetNewDetails(int count) { diff --git a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch.cs b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch.cs index 019d5e311..ebaefe126 100644 --- a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch.cs +++ b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch.cs @@ -45,9 +45,9 @@ protected InventoryPouch(InventoryType type, ushort[] legal, int maxCount, int o } /// Reads the pouch from the backing . - public abstract void GetPouch(byte[] data); + public abstract void GetPouch(ReadOnlySpan data); /// Writes the pouch to the backing . - public abstract void SetPouch(byte[] data); + public abstract void SetPouch(Span data); /// Orders the based on public void SortByCount(bool reverse = false) => Array.Sort(Items, (x, y) => Compare(x.Count, y.Count, reverse)); diff --git a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch3.cs b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch3.cs index 84c789977..d69618ee4 100644 --- a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch3.cs +++ b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch3.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -11,29 +12,34 @@ public InventoryPouch3(InventoryType type, ushort[] legal, int maxCount, int off { } - public override void GetPouch(byte[] data) + public override void GetPouch(ReadOnlySpan data) { + var span = data[Offset..]; var items = new InventoryItem[PouchDataSize]; - for (int i = 0; i data) { if (Items.Length != PouchDataSize) throw new ArgumentException("Item array length does not match original pouch size."); + var span = data[Offset..]; for (int i = 0; i < Items.Length; i++) { - BitConverter.GetBytes((ushort) Items[i].Index).CopyTo(data, Offset + (i* 4)); - BitConverter.GetBytes((ushort)((ushort) Items[i].Count ^ (ushort) SecurityKey)).CopyTo(data, Offset + (i* 4) + 2); + var item = Items[i]; + var entry = span.Slice(i * 4, 4); + WriteUInt16LittleEndian(entry, (ushort)item.Index); + WriteUInt16LittleEndian(entry[2..], (ushort)(item.Count ^ (int)SecurityKey)); } } diff --git a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch3GC.cs b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch3GC.cs index d481f7801..3b51665af 100644 --- a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch3GC.cs +++ b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch3GC.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -11,29 +12,33 @@ public InventoryPouch3GC(InventoryType type, ushort[] legal, int maxCount, int o public override InventoryItem GetEmpty(int itemID = 0, int count = 0) => new() { Index = itemID, Count = count }; - public override void GetPouch(byte[] data) + public override void GetPouch(ReadOnlySpan data) { + var span = data[Offset..]; var items = new InventoryItem[PouchDataSize]; for (int i = 0; i < items.Length; i++) { + var item = span.Slice(i * 4, 4); items[i] = new InventoryItem { - Index = BigEndian.ToUInt16(data, Offset + (i * 4)), - Count = BigEndian.ToUInt16(data, Offset + (i * 4) + 2), + Index = ReadUInt16BigEndian(item), + Count = ReadUInt16BigEndian(item[2..]), }; } Items = items; } - public override void SetPouch(byte[] data) + public override void SetPouch(Span data) { if (Items.Length != PouchDataSize) throw new ArgumentException("Item array length does not match original pouch size."); + var span = data[Offset..]; for (int i = 0; i < Items.Length; i++) { - BigEndian.GetBytes((ushort)Items[i].Index).CopyTo(data, Offset + (i * 4)); - BigEndian.GetBytes((ushort)Items[i].Count).CopyTo(data, Offset + (i * 4) + 2); + var item = span.Slice(i * 4, 4); + WriteUInt32BigEndian(item, (ushort)Items[i].Index); + WriteUInt32BigEndian(item[2..], (ushort)Items[i].Count); } } } diff --git a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch4.cs b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch4.cs index 814d24b58..e8bbacc7c 100644 --- a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch4.cs +++ b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch4.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -18,29 +19,34 @@ public InventoryPouch4(InventoryType type, ushort[] legal, int maxCount, int off public override InventoryItem GetEmpty(int itemID = 0, int count = 0) => new() { Index = itemID, Count = count }; - public override void GetPouch(byte[] data) + public override void GetPouch(ReadOnlySpan data) { + var span = data[Offset..]; var items = new InventoryItem[PouchDataSize]; for (int i = 0; i < items.Length; i++) { + var entry = span.Slice(i * 4, 4); items[i] = new InventoryItem { - Index = BitConverter.ToUInt16(data, Offset + (i * 4)), - Count = BitConverter.ToUInt16(data, Offset + (i * 4) + 2), + Index = ReadUInt16LittleEndian(entry), + Count = ReadUInt16LittleEndian(entry[2..]), }; } Items = items; } - public override void SetPouch(byte[] data) + public override void SetPouch(Span data) { if (Items.Length != PouchDataSize) throw new ArgumentException("Item array length does not match original pouch size."); + var span = data[Offset..]; for (int i = 0; i < Items.Length; i++) { - BitConverter.GetBytes((ushort)Items[i].Index).CopyTo(data, Offset + (i * 4)); - BitConverter.GetBytes((ushort)Items[i].Count).CopyTo(data, Offset + (i * 4) + 2); + var item = Items[i]; + var entry = span.Slice(i * 4, 4); + WriteUInt16LittleEndian(entry, (ushort)item.Index); + WriteUInt16LittleEndian(entry[2..], (ushort)item.Count); } } } diff --git a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch7.cs b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch7.cs index 61c80cdf9..8934734fd 100644 --- a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch7.cs +++ b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch7.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -14,29 +15,32 @@ public InventoryPouch7(InventoryType type, ushort[] legal, int maxCount, int off public override InventoryItem GetEmpty(int itemID = 0, int count = 0) => new InventoryItem7 { Index = itemID, Count = count }; - public override void GetPouch(byte[] data) + public override void GetPouch(ReadOnlySpan data) { + var span = data[Offset..]; var items = new InventoryItem7[PouchDataSize]; for (int i = 0; i < items.Length; i++) { - uint val = BitConverter.ToUInt32(data, Offset + (i * 4)); + var item = span.Slice(i * 4, 4); + uint val = ReadUInt32LittleEndian(item); items[i] = InventoryItem7.GetValue(val); } Items = items; OriginalItems = items.Select(i => i.Clone()).ToArray(); } - public override void SetPouch(byte[] data) + public override void SetPouch(Span data) { if (Items.Length != PouchDataSize) throw new ArgumentException("Item array length does not match original pouch size."); + var span = data[Offset..]; var items = (InventoryItem7[])Items; for (int i = 0; i < Items.Length; i++) { - var item = items[i]; - var val = item.GetValue(SetNew, OriginalItems); - BitConverter.GetBytes(val).CopyTo(data, Offset + (i * 4)); + var item = span.Slice(i * 4, 4); + var value = items[i].GetValue(SetNew, OriginalItems); + WriteUInt32LittleEndian(item, value); } } } diff --git a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch7b.cs b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch7b.cs index 076c590b7..928001191 100644 --- a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch7b.cs +++ b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch7b.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -16,28 +17,32 @@ public sealed class InventoryPouch7b : InventoryPouch public InventoryPouch7b(InventoryType type, ushort[] legal, int maxCount, int offset, int size) : base(type, legal, maxCount, offset, size) { } - public override void GetPouch(byte[] data) + public override void GetPouch(ReadOnlySpan data) { + var span = data[Offset..]; var items = new InventoryItem7b[PouchDataSize]; for (int i = 0; i < items.Length; i++) { - uint val = BitConverter.ToUInt32(data, Offset + (i * 4)); + var item = span.Slice(i * 4, 4); + uint val = ReadUInt32LittleEndian(item); items[i] = InventoryItem7b.GetValue(val); } Items = items; OriginalItems = items.Select(i => i.Clone()).ToArray(); } - public override void SetPouch(byte[] data) + public override void SetPouch(Span data) { if (Items.Length != PouchDataSize) throw new ArgumentException("Item array length does not match original pouch size."); + var span = data[Offset..]; var items = (InventoryItem7b[])Items; for (int i = 0; i < items.Length; i++) { + var item = span.Slice(i * 4, 4); uint val = items[i].GetValue(SetNew, OriginalItems); - BitConverter.GetBytes(val).CopyTo(data, Offset + (i * 4)); + WriteUInt32LittleEndian(item, val); } } diff --git a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch8.cs b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch8.cs index 13feda0bc..2733abcab 100644 --- a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch8.cs +++ b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch8.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -16,28 +17,32 @@ public InventoryPouch8(InventoryType type, ushort[] legal, int maxCount, int off public override InventoryItem GetEmpty(int itemID = 0, int count = 0) => new InventoryItem8 { Index = itemID, Count = count }; - public override void GetPouch(byte[] data) + public override void GetPouch(ReadOnlySpan data) { + var span = data[Offset..]; var items = new InventoryItem8[PouchDataSize]; for (int i = 0; i < items.Length; i++) { - uint value = BitConverter.ToUInt32(data, Offset + (i * 4)); + var item = span.Slice(i * 4, 4); + uint value = ReadUInt32LittleEndian(item); items[i] = InventoryItem8.GetValue(value); } Items = items; OriginalItems = items.Select(i => i.Clone()).ToArray(); } - public override void SetPouch(byte[] data) + public override void SetPouch(Span data) { if (Items.Length != PouchDataSize) throw new ArgumentException("Item array length does not match original pouch size."); + var span = data[Offset..]; var items = (InventoryItem8[])Items; for (int i = 0; i < items.Length; i++) { + var item = span.Slice(i * 4, 4); uint val = items[i].GetValue(SetNew, OriginalItems); - BitConverter.GetBytes(val).CopyTo(data, Offset + (i * 4)); + WriteUInt32LittleEndian(item, val); } } diff --git a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch8b.cs b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch8b.cs index c52316033..08d9b1cb9 100644 --- a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch8b.cs +++ b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouch8b.cs @@ -14,7 +14,7 @@ public sealed class InventoryPouch8b : InventoryPouch public override InventoryItem GetEmpty(int itemID = 0, int count = 0) => new InventoryItem8b { Index = itemID, Count = count, IsNew = true }; - public override void GetPouch(byte[] data) + public override void GetPouch(ReadOnlySpan data) { var items = new InventoryItem8b[LegalItems.Length]; @@ -33,13 +33,13 @@ public override void GetPouch(byte[] data) SortBy(z => !z.IsValidSaveSortNumberCount ? (ushort)0xFFFF : z.SortOrder); } - public InventoryItem8b GetItem(byte[] data, ushort itemID) + public InventoryItem8b GetItem(ReadOnlySpan data, ushort itemID) { var ofs = GetItemOffset(itemID, Offset); - return InventoryItem8b.Read(itemID, data, ofs); + return InventoryItem8b.Read(itemID, data[ofs..]); } - public override void SetPouch(byte[] data) + public override void SetPouch(Span data) { HashSet processed = new(); @@ -67,7 +67,7 @@ public override void SetPouch(byte[] data) } var ofs = GetItemOffset(index, Offset); - item.Write(data, ofs); + item.Write(data[ofs..]); if (!processed.Contains(index)) // we will allow duplicate item definitions, but they'll overwrite instead of sum/separate. processed.Add(index); @@ -84,6 +84,6 @@ public override void SetPouch(byte[] data) public static int GetItemOffset(ushort index, int baseOffset) => baseOffset + (InventoryItem8b.SIZE * index); - public void ClearItem(byte[] data, ushort index) => InventoryItem8b.Clear(data, GetItemOffset(index, Offset)); + public void ClearItem(Span data, ushort index) => InventoryItem8b.Clear(data, GetItemOffset(index, Offset)); } } diff --git a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouchGB.cs b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouchGB.cs index 9d1c4121d..87cf53891 100644 --- a/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouchGB.cs +++ b/PKHeX.Core/Saves/Substructures/Inventory/InventoryPouchGB.cs @@ -11,7 +11,7 @@ public InventoryPouchGB(InventoryType type, ushort[] legal, int maxCount, int of public override InventoryItem GetEmpty(int itemID = 0, int count = 0) => new() { Index = itemID, Count = count }; - public override void GetPouch(byte[] data) + public override void GetPouch(ReadOnlySpan data) { var items = new InventoryItem[PouchDataSize]; if (Type == InventoryType.TMHMs) @@ -64,7 +64,7 @@ public override void GetPouch(byte[] data) Items = items; } - public override void SetPouch(byte[] data) + public override void SetPouch(Span data) { if (Items.Length != PouchDataSize) throw new ArgumentException("Item array length does not match original pouch size."); diff --git a/PKHeX.Core/Saves/Substructures/Mail/Mail2.cs b/PKHeX.Core/Saves/Substructures/Mail/Mail2.cs index 4f5c38c22..b45e785c4 100644 --- a/PKHeX.Core/Saves/Substructures/Mail/Mail2.cs +++ b/PKHeX.Core/Saves/Substructures/Mail/Mail2.cs @@ -1,3 +1,5 @@ +using System; + namespace PKHeX.Core { public sealed class Mail2 : Mail @@ -14,28 +16,28 @@ private static int GetMailOffset(int index) return index < 6 ? (index * 0x2F) + 0x600 : ((index - 6) * 0x2F) + 0x835; } - public override string GetMessage(bool isLastLine) => US ? StringConverter12.GetString1(Data, isLastLine ? 0x11 : 0, 0x10, false) : string.Empty; + public override string GetMessage(bool isLastLine) => US ? StringConverter12.GetString(Data.AsSpan(isLastLine ? 0x11 : 0, 0x10), false) : string.Empty; public override void SetMessage(string line1, string line2) { if (US) { - StringConverter12.SetString1(line2, 0x10, false, 0x10, StringConverter12.G1TerminatorCode).CopyTo(Data, 0x11); + StringConverter12.SetString(Data.AsSpan(0x11, 0x10), line2.AsSpan(), 0x10, false, StringConverterOption.Clear50); bool hasLine2 = Data[0x11] != StringConverter12.G1TerminatorCode; - byte padChar = !hasLine2 ? StringConverter12.G1TerminatorCode : (byte)0x7F; // space - StringConverter12.SetString1(line1, 0x10, false, 0x10, padChar).CopyTo(Data, 0); + var padChar = !hasLine2 ? StringConverterOption.Clear50 : StringConverterOption.Clear7F; // space + StringConverter12.SetString(Data.AsSpan(0, 0x10), line1.AsSpan(), 0x10, false, padChar); Data[0x10] = 0x4E; } } public override string AuthorName { - get => US ? StringConverter12.GetString1(Data, 0x21, 7, false) : string.Empty; + get => US ? StringConverter12.GetString(Data.AsSpan(0x21, 8), false) : string.Empty; set { if (US) { - StringConverter12.SetString1(value, 7, false, 8, 0x50).CopyTo(Data, 0x21); + StringConverter12.SetString(Data.AsSpan(0x21, 8), value.AsSpan(), 7, false, StringConverterOption.Clear50); Data[0x29] = Data[0x2A] = 0; } } diff --git a/PKHeX.Core/Saves/Substructures/Mail/Mail3.cs b/PKHeX.Core/Saves/Substructures/Mail/Mail3.cs index 84828e561..236d1e1bc 100644 --- a/PKHeX.Core/Saves/Substructures/Mail/Mail3.cs +++ b/PKHeX.Core/Saves/Substructures/Mail/Mail3.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -25,32 +26,24 @@ private void ResetData() MailType = 0; } - public override ushort GetMessage(int index1, int index2) => BitConverter.ToUInt16(Data, ((index1 * 3) + index2) * 2); - public override void SetMessage(int index1, int index2, ushort value) => BitConverter.GetBytes(value).CopyTo(Data, ((index1 * 3) + index2) * 2); + public override ushort GetMessage(int index1, int index2) => ReadUInt16LittleEndian(Data.AsSpan(((index1 * 3) + index2) * 2)); + public override void SetMessage(int index1, int index2, ushort value) => WriteUInt16LittleEndian(Data.AsSpan(((index1 * 3) + index2) * 2), value); public override void CopyTo(SaveFile sav) => sav.SetData(((SAV3)sav).Large, DataOffset); public override string AuthorName { - get => StringConverter3.GetString3(Data, 0x12, 7, JP); + get => StringConverter3.GetString(Data.AsSpan(0x12, 7), JP); set { - if (value.Length == 0) - { - for (int i = 0; i < 8; i++) - Data[0x12 + i] = 0xFF; - } - else - { - Data[0x18] = Data[0x19] = 0xFF; - StringConverter3.SetString3(value, 7, JP, 6).CopyTo(Data, 0x12); - } + var span = Data.AsSpan(0x12, 8); + StringConverter3.SetString(span, value.AsSpan(), 7, JP, StringConverterOption.ClearFF); } } - public override ushort AuthorTID { get => BitConverter.ToUInt16(Data, 0x1A); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1A); } - public override ushort AuthorSID { get => BitConverter.ToUInt16(Data, 0x1C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1C); } - public override int AppearPKM { get => BitConverter.ToUInt16(Data, 0x1E); set => BitConverter.GetBytes((ushort)(value == 0 ? 1 : value)).CopyTo(Data, 0x1E); } - public override int MailType { get => BitConverter.ToUInt16(Data, 0x20); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x20); } + public override ushort AuthorTID { get => ReadUInt16LittleEndian(Data.AsSpan(0x1A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1A), value); } + public override ushort AuthorSID { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), value); } + public override int AppearPKM { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)(value == 0 ? 1 : value)); } + public override int MailType { get => ReadUInt16LittleEndian(Data.AsSpan(0x20)); set => WriteUInt16LittleEndian(Data.AsSpan(0x20), (ushort)value); } public override bool? IsEmpty => MailType switch { diff --git a/PKHeX.Core/Saves/Substructures/Mail/Mail4.cs b/PKHeX.Core/Saves/Substructures/Mail/Mail4.cs index 3a7c8dd8b..9693ee25c 100644 --- a/PKHeX.Core/Saves/Substructures/Mail/Mail4.cs +++ b/PKHeX.Core/Saves/Substructures/Mail/Mail4.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -36,17 +37,17 @@ private void ResetData() } public override void CopyTo(PK4 pk4) => pk4.SetHeldMailData(Data); - public override ushort AuthorTID { get => BitConverter.ToUInt16(Data, 0); set => BitConverter.GetBytes(value).CopyTo(Data, 0); } - public override ushort AuthorSID { get => BitConverter.ToUInt16(Data, 2); set => BitConverter.GetBytes(value).CopyTo(Data, 2); } + public override ushort AuthorTID { get => ReadUInt16LittleEndian(Data.AsSpan(0)); set => WriteUInt16LittleEndian(Data.AsSpan(0), value); } + public override ushort AuthorSID { get => ReadUInt16LittleEndian(Data.AsSpan(2)); set => WriteUInt16LittleEndian(Data.AsSpan(2), value); } public override byte AuthorGender { get => Data[4]; set => Data[4] = value; } public override byte AuthorLanguage { get => Data[5]; set => Data[5] = value; } public override byte AuthorVersion { get => Data[6]; set => Data[6] = value; } public override int MailType { get => Data[7]; set => Data[7] = (byte)value; } - public override string AuthorName { get => StringConverter4.GetString4(Data, 8, 0x10); set => StringConverter4.SetString4(value, 7, 8, 0xFFFF).CopyTo(Data, 8); } - public int GetAppearPKM(int index) => BitConverter.ToUInt16(Data, 0x1C - (index * 2)); - public void SetAppearPKM(int index, int value) => BitConverter.GetBytes((ushort)(value == 0 ? 0xFFFF : value)).CopyTo(Data, 0x1C - (index * 2)); - public override ushort GetMessage(int index1, int index2) => BitConverter.ToUInt16(Data, 0x20 + (((index1 * 4) + index2) * 2)); - public override void SetMessage(int index1, int index2, ushort value) => BitConverter.GetBytes(value).CopyTo(Data, 0x20 + (((index1 * 4) + index2) * 2)); + public override string AuthorName { get => StringConverter4.GetString(Data.AsSpan(8, 0x10)); set => StringConverter4.SetString(Data.AsSpan(8, 0x10), value.AsSpan(), 7, StringConverterOption.ClearFF); } + public int GetAppearPKM(int index) => ReadUInt16LittleEndian(Data.AsSpan(0x1C - (index * 2))); + public void SetAppearPKM(int index, int value) => WriteUInt16LittleEndian(Data.AsSpan(0x1C - (index * 2)), (ushort)(value == 0 ? 0xFFFF : value)); + public override ushort GetMessage(int index1, int index2) => ReadUInt16LittleEndian(Data.AsSpan(0x20 + (((index1 * 4) + index2) * 2))); + public override void SetMessage(int index1, int index2, ushort value) => WriteUInt16LittleEndian(Data.AsSpan(0x20 + (((index1 * 4) + index2) * 2)), value); public override bool? IsEmpty => MailType switch { diff --git a/PKHeX.Core/Saves/Substructures/Mail/Mail5.cs b/PKHeX.Core/Saves/Substructures/Mail/Mail5.cs index 9635caa5a..0b95005fd 100644 --- a/PKHeX.Core/Saves/Substructures/Mail/Mail5.cs +++ b/PKHeX.Core/Saves/Substructures/Mail/Mail5.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -33,18 +34,18 @@ private void ResetData() } public override void CopyTo(PK5 pk5) => pk5.SetHeldMailData(Data); - public override ushort AuthorTID { get => BitConverter.ToUInt16(Data, 0); set => BitConverter.GetBytes(value).CopyTo(Data, 0); } - public override ushort AuthorSID { get => BitConverter.ToUInt16(Data, 2); set => BitConverter.GetBytes(value).CopyTo(Data, 2); } + public override ushort AuthorTID { get => ReadUInt16LittleEndian(Data.AsSpan(0)); set => WriteUInt16LittleEndian(Data.AsSpan(0), value); } + public override ushort AuthorSID { get => ReadUInt16LittleEndian(Data.AsSpan(2)); set => WriteUInt16LittleEndian(Data.AsSpan(2), value); } public override byte AuthorGender { get => Data[4]; set => Data[4] = value; } public override byte AuthorLanguage { get => Data[5]; set => Data[5] = value; } public override byte AuthorVersion { get => Data[6]; set => Data[6] = value; } public override int MailType { get => Data[7]; set => Data[7] = (byte)value; } - public override string AuthorName { get => StringConverter.GetString5(Data, 8, 0x10); set => StringConverter.SetString5(value, 7, 8).CopyTo(Data, 8); } - public int GetMisc(int index) => BitConverter.ToUInt16(Data, 0x1C - (index * 2)); - public void SetMisc(int index, int value) => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1C - (index * 2)); - public ushort MessageEnding { get => BitConverter.ToUInt16(Data, 0x1E); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1E); } - public override ushort GetMessage(int index1, int index2) => BitConverter.ToUInt16(Data, 0x20 + (((index1 * 4) + index2) * 2)); - public override void SetMessage(int index1, int index2, ushort value) => BitConverter.GetBytes(value).CopyTo(Data, 0x20 + (((index1 * 4) + index2) * 2)); + public override string AuthorName { get => StringConverter5.GetString(Data.AsSpan(8, 0x10)); set => StringConverter5.SetString(Data.AsSpan(8, 0x10), value.AsSpan(), 7, StringConverterOption.ClearZero); } + public int GetMisc(int index) => ReadUInt16LittleEndian(Data.AsSpan(0x1C - (index * 2))); + public void SetMisc(int index, int value) => WriteUInt16LittleEndian(Data.AsSpan(0x1C - (index * 2)), (ushort)value); + public ushort MessageEnding { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), value); } + public override ushort GetMessage(int index1, int index2) => ReadUInt16LittleEndian(Data.AsSpan(0x20 + (((index1 * 4) + index2) * 2))); + public override void SetMessage(int index1, int index2, ushort value) => WriteUInt16LittleEndian(Data.AsSpan(0x20 + (((index1 * 4) + index2) * 2)), value); public override bool? IsEmpty => MailType switch { diff --git a/PKHeX.Core/Saves/Substructures/OPower/OPower6.cs b/PKHeX.Core/Saves/Substructures/OPower/OPower6.cs index 16ac5c0fc..eb8d59718 100644 --- a/PKHeX.Core/Saves/Substructures/OPower/OPower6.cs +++ b/PKHeX.Core/Saves/Substructures/OPower/OPower6.cs @@ -36,16 +36,16 @@ public sealed class OPower6 : SaveBlock private static OPowerFlagSet Get(OPower6Type type) => Array.Find(Mapping, t => t.Identifier == type); public static int GetOPowerCount(OPower6Type type) => Get(type).BaseCount; - public int GetOPowerLevel(OPower6Type type) => Get(type).GetOPowerLevel(Data, Offset); + public int GetOPowerLevel(OPower6Type type) => Get(type).GetOPowerLevel(Data.AsSpan(Offset)); 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, Offset); - public bool GetOPowerMAX(OPower6Type type) => Get(type).GetOPowerMAX(Data, Offset); + 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, Offset, lvl); - public void SetOPowerS(OPower6Type type, bool value) => Get(type).SetOPowerS(Data, Offset, value); - public void SetOPowerMAX(OPower6Type type, bool value) => Get(type).SetOPowerMAX(Data, Offset, value); + 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 { @@ -59,23 +59,24 @@ public bool MasterFlag 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(Data, Offset, 0); - m.SetOPowerS(Data, Offset, false); - m.SetOPowerMAX(Data, Offset, false); + 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 ORAS/event only @ 1 level - m.SetOPowerLevel(Data, Offset, lvl); + m.SetOPowerLevel(span, lvl); if (!allEvents) continue; - m.SetOPowerS(Data, Offset, true); - m.SetOPowerMAX(Data, Offset, true); + m.SetOPowerS(span, true); + m.SetOPowerMAX(span, true); } } diff --git a/PKHeX.Core/Saves/Substructures/OPower/OPowerFlagSet.cs b/PKHeX.Core/Saves/Substructures/OPower/OPowerFlagSet.cs index 5db92e172..e24febdde 100644 --- a/PKHeX.Core/Saves/Substructures/OPower/OPowerFlagSet.cs +++ b/PKHeX.Core/Saves/Substructures/OPower/OPowerFlagSet.cs @@ -7,7 +7,7 @@ internal sealed class OPowerFlagSet { public readonly OPower6Type Identifier; public readonly int Count; - public int Offset; + public int Offset { get; set; } public int BaseCount => Math.Min(3, Count); public bool HasOPowerS => Count > 3; public bool HasOPowerMAX => Count == 5; @@ -18,11 +18,11 @@ public OPowerFlagSet(int count, OPower6Type ident) Count = count; } - public int GetOPowerLevel(byte[] data, int offset) + public int GetOPowerLevel(ReadOnlySpan data) { for (int i = 0; i < BaseCount; i++) { - if (GetFlag(data, offset, i)) + if (GetFlag(data, i)) continue; Debug.WriteLine($"Fetched {Identifier}: {i}"); return i; @@ -32,28 +32,28 @@ public int GetOPowerLevel(byte[] data, int offset) return BaseCount; } - public void SetOPowerLevel(byte[] data, int offset, int value) + public void SetOPowerLevel(Span data, int value) { Debug.WriteLine($"Setting {Identifier}: {value}"); for (int i = 0; i < BaseCount; i++) - SetFlag(data, offset, i, i + 1 <= value); - Debug.Assert(value == GetOPowerLevel(data, offset)); + SetFlag(data, i, i + 1 <= value); + Debug.Assert(value == GetOPowerLevel(data)); } - public bool GetOPowerS(byte[] data, int offset) => HasOPowerS && GetFlag(data, offset, 3); - public bool GetOPowerMAX(byte[] data, int offset) => HasOPowerMAX && GetFlag(data, offset, 4); - public void SetOPowerS(byte[] data, int offset, bool value) => SetFlag(data, offset, 3, value); - public void SetOPowerMAX(byte[] data, int offset, bool value) => SetFlag(data, offset, 4, value); + 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(byte[] data, int offset, int index) + private bool GetFlag(ReadOnlySpan data, int index) { - return data[Offset + offset + index] == (byte)OPowerFlagState.Unlocked; + return data[Offset + index] == (byte)OPowerFlagState.Unlocked; } - private void SetFlag(byte[] data, int offset, int index, bool value) + private void SetFlag(Span data, int index, bool value) { if (index < Count) - data[Offset + offset + index] = (byte)(value ? OPowerFlagState.Unlocked : OPowerFlagState.Locked); + data[Offset + index] = (byte)(value ? OPowerFlagState.Unlocked : OPowerFlagState.Locked); } } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan4.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan4.cs index ae0cef512..a1f82bcee 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan4.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan4.cs @@ -1,5 +1,5 @@ using System; -using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -45,7 +45,7 @@ public Zukan4(SAV4 sav, int offset) : base(sav, offset) Offset = offset; } - public uint Magic { get => BitConverter.ToUInt32(Data, Offset); set => BitConverter.GetBytes(value).CopyTo(Data, Offset); } + public uint Magic { get => ReadUInt32LittleEndian(Data.AsSpan(Offset)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset), value); } public override bool GetCaught(int species) => GetRegionFlag(0, species - 1); public override bool GetSeen(int species) => GetRegionFlag(1, species - 1); @@ -70,7 +70,7 @@ private void SetRegionFlag(int region, int index, bool value) FlagUtil.SetFlag(Data, ofs, index, value); } - public uint SpindaPID { get => BitConverter.ToUInt32(Data, Offset + OFS_SPINDA); set => BitConverter.GetBytes(value).CopyTo(Data, Offset); } + public uint SpindaPID { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + OFS_SPINDA)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset), value); } public static string[] GetFormNames4Dex(int species) { @@ -101,7 +101,11 @@ public int[] GetForms(int species) case (int)Species.Wormadam: // Wormadam return GetDexFormValues(Data[FormOffset1 + 3], 2, 3); case (int)Species.Unown: // Unown - return Data.Slice(FormOffset1 + 4, 0x1C).Select(i => (int)i).ToArray(); + int[] result = new int[0x1C]; + var slice = Data.AsSpan(FormOffset1 + 4); + for (int i = 0; i < result.Length; i++) + result[i] = slice[i]; + return result; } if (DP) return Array.Empty(); @@ -110,7 +114,7 @@ public int[] GetForms(int species) int FormOffset2 = PokeDexLanguageFlags + 0x1F4; return species switch { - (int)Species.Rotom => GetDexFormValues(BitConverter.ToUInt32(Data, FormOffset2), 3, 6), + (int)Species.Rotom => GetDexFormValues(ReadUInt32LittleEndian(Data.AsSpan(FormOffset2)), 3, 6), (int)Species.Shaymin => GetDexFormValues(Data[FormOffset2 + 4], 1, 2), (int)Species.Giratina => GetDexFormValues(Data[FormOffset2 + 5], 1, 2), (int)Species.Pichu when HGSS => GetDexFormValues(Data[FormOffset2 + 6], 2, 3), @@ -118,7 +122,7 @@ public int[] GetForms(int species) }; } - public void SetForms(int species, int[] forms) + public void SetForms(int species, ReadOnlySpan forms) { const int brSize = 0x40; switch (species) @@ -148,10 +152,12 @@ public void SetForms(int species, int[] forms) case (int)Species.Unown: // Unown int ofs = FormOffset1 + 4; int len = forms.Length; - Array.Resize(ref forms, 0x1C); + Span unown = stackalloc byte[0x1C]; + for (int i = 0; i < len; i++) + unown[i] = (byte)forms[i]; for (int i = len; i < forms.Length; i++) - forms[i] = 0xFF; - Array.Copy(forms.Select(b => (byte)b).ToArray(), 0, Data, ofs, forms.Length); + unown[i] = 0xFF; + unown.CopyTo(Data.AsSpan(ofs)); return; } @@ -163,7 +169,8 @@ public void SetForms(int species, int[] forms) switch (species) { case (int)Species.Rotom: // Rotom - BitConverter.GetBytes(SetDexFormValues(forms, 3, 6)).CopyTo(Data, FormOffset2); + var value = SetDexFormValues(forms, 3, 6); + WriteUInt32LittleEndian(Data.AsSpan(FormOffset2), value); return; case (int)Species.Shaymin: // Shaymin Data[FormOffset2 + 4] = (byte)SetDexFormValues(forms, 1, 2); @@ -197,7 +204,7 @@ private static int[] GetDexFormValues(uint Value, int BitsPerForm, int readCt) return Forms; } - private static uint SetDexFormValues(int[] Forms, int BitsPerForm, int readCt) + private static uint SetDexFormValues(ReadOnlySpan Forms, int BitsPerForm, int readCt) { int n1 = 0xFF >> (8 - BitsPerForm); uint Value = 0xFFFFFFFF << (readCt * BitsPerForm); @@ -214,13 +221,13 @@ private static uint SetDexFormValues(int[] Forms, int BitsPerForm, int readCt) return Value; } - private static bool TryInsertForm(int[] forms, int form) + private static bool TryInsertForm(Span forms, int form) { - if (Array.IndexOf(forms, form) >= 0) + if (forms.IndexOf(form) >= 0) return false; // already in list // insert at first empty - var index = Array.IndexOf(forms, -1); + var index = forms.IndexOf(-1); if (index < 0) return false; // no free slots? @@ -327,7 +334,7 @@ private void SetForms(int species, int form, int gender) return; } - var forms = GetForms(species); + Span forms = GetForms(species); if (forms.Length == 0) return; @@ -457,7 +464,9 @@ private void CompleteForms(int species) if (forms.Length <= 1) return; - var values = forms.Select((_, i) => i).ToArray(); + Span values = stackalloc int[forms.Length]; + for (int i = 1; i < values.Length; i++) + values[i] = i; SetForms(species, values); } diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan6.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan6.cs index ade1f33f3..23df14f0c 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan6.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan6.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -129,8 +130,8 @@ private bool GetIsFormDisplayed(int f, int fc) public uint SpindaPID { - get => BitConverter.ToUInt32(SAV.Data, PokeDex + SpindaOffset); - set => SAV.SetData(BitConverter.GetBytes(value), PokeDex + SpindaOffset); + get => ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + SpindaOffset)); + set => WriteUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + SpindaOffset), value); } public bool[] GetLanguageBitflags(int species) @@ -197,14 +198,13 @@ protected override void SetCaughtFlag(int bit, int origin) public ushort GetEncounterCount(int index) { var ofs = PokeDex + 0x686 + (index * 2); - return BitConverter.ToUInt16(SAV.Data, ofs); + return ReadUInt16LittleEndian(SAV.Data.AsSpan(ofs)); } public void SetEncounterCount(int index, ushort value) { var ofs = PokeDex + 0x686 + (index * 2); - var data = BitConverter.GetBytes(value); - SAV.SetData(data, ofs); + WriteUInt16LittleEndian(SAV.Data.AsSpan(ofs), value); } } diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs index 95df0f935..cf4535e91 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -32,7 +33,7 @@ private Zukan7(SaveFile sav, int dex, int langflag, Func for { DexFormIndexFetcher = form; FormBaseSpecies = GetFormIndexBaseSpeciesList(); - Debug.Assert(!SAV.State.Exportable || BitConverter.ToUInt32(SAV.Data, PokeDex) == MAGIC); + Debug.Assert(!SAV.State.Exportable || ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex)) == MAGIC); } public Func DexFormIndexFetcher { get; } @@ -143,12 +144,15 @@ protected sealed override void SetSpindaDexData(PKM pkm, bool alreadySeen) var flag1 = (1 << (shift + 4)); if ((SAV.Data[PokeDex + 0x84] & flag1) != 0) // Already showing this one return; - BitConverter.GetBytes(pkm.EncryptionConstant).CopyTo(SAV.Data, PokeDex + 0x8E8 + (shift * 4)); + + var span = SAV.Data.AsSpan(PokeDex + 0x8E8 + (shift * 4)); + WriteUInt32LittleEndian(span, pkm.EncryptionConstant); SAV.Data[PokeDex + 0x84] |= (byte)(flag1 | (1 << shift)); } else if ((SAV.Data[PokeDex + 0x84] & (1 << shift)) == 0) { - BitConverter.GetBytes(pkm.EncryptionConstant).CopyTo(SAV.Data, PokeDex + 0x8E8 + (shift * 4)); + var span = SAV.Data.AsSpan(PokeDex + 0x8E8 + (shift * 4)); + WriteUInt32LittleEndian(span, pkm.EncryptionConstant); SAV.Data[PokeDex + 0x84] |= (byte)(1 << shift); } } @@ -163,7 +167,7 @@ public bool NationalDex /// /// Gets the last viewed dex entry in the Pokedex (by National Dex ID), internally called DefaultMons /// - public uint CurrentViewedDex => BitConverter.ToUInt32(SAV.Data, PokeDex + 4) >> 9 & 0x3FF; + public uint CurrentViewedDex => ReadUInt32LittleEndian(SAV.Data.AsSpan(PokeDex + 4)) >> 9 & 0x3FF; public IEnumerable GetAllFormEntries(int species) { diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7b.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7b.cs index f025c3b8a..4e205e39c 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7b.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7b.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -41,8 +42,9 @@ public bool GetSizeData(DexSizeType group, int species, int form, out int height public bool GetSizeData(DexSizeType group, int index, out int height, out int weight) { var ofs = GetDexSizeOffset(group, index); - height = BitConverter.ToUInt16(SAV.Data, ofs) >> 1; - weight = BitConverter.ToUInt16(SAV.Data, ofs + 2); + var entry = SAV.Data.AsSpan(ofs); + height = ReadUInt16LittleEndian(entry) >> 1; + weight = ReadUInt16LittleEndian(entry[2..]); return !IsEntryUnset(height, weight); } @@ -58,36 +60,40 @@ private void SetSizeData(PB7 pkm) if (Math.Round(pkm.HeightAbsolute) < pkm.PersonalInfo.Height) // possible minimum height { int ofs = GetDexSizeOffset(DexSizeType.MinHeight, index); - var minHeight = BitConverter.ToUInt16(SAV.Data, ofs) >> 1; + var entry = SAV.Data.AsSpan(ofs); + var minHeight = ReadUInt16LittleEndian(entry) >> 1; var calcHeight = PB7.GetHeightAbsolute(pkm.PersonalInfo, minHeight); - if (Math.Round(pkm.HeightAbsolute) < Math.Round(calcHeight) || BitConverter.ToUInt32(SAV.Data, ofs) == 0x007F00FE) // unset + if (Math.Round(pkm.HeightAbsolute) < Math.Round(calcHeight) || ReadUInt32LittleEndian(entry) == 0x007F00FE) // unset SetSizeData(pkm, DexSizeType.MinHeight); } else if (Math.Round(pkm.HeightAbsolute) > pkm.PersonalInfo.Height) // possible maximum height { int ofs = GetDexSizeOffset(DexSizeType.MaxHeight, index); - var maxHeight = BitConverter.ToUInt16(SAV.Data, ofs) >> 1; + var entry = SAV.Data.AsSpan(ofs); + var maxHeight = ReadUInt16LittleEndian(entry) >> 1; var calcHeight = PB7.GetHeightAbsolute(pkm.PersonalInfo, maxHeight); - if (Math.Round(pkm.HeightAbsolute) > Math.Round(calcHeight) || BitConverter.ToUInt32(SAV.Data, ofs) == 0x007F00FE) // unset + if (Math.Round(pkm.HeightAbsolute) > Math.Round(calcHeight) || ReadUInt32LittleEndian(entry) == 0x007F00FE) // unset SetSizeData(pkm, DexSizeType.MaxHeight); } if (Math.Round(pkm.WeightAbsolute) < pkm.PersonalInfo.Weight) // possible minimum weight { int ofs = GetDexSizeOffset(DexSizeType.MinWeight, index); - var minWeight = BitConverter.ToUInt16(SAV.Data, ofs + 2); - var minHeight = BitConverter.ToUInt16(SAV.Data, ofs) >> 1; + var entry = SAV.Data.AsSpan(ofs); + var minHeight = ReadUInt16LittleEndian(entry) >> 1; + var minWeight = ReadUInt16LittleEndian(entry[2..]); var calcWeight = PB7.GetWeightAbsolute(pkm.PersonalInfo, minHeight, minWeight); - if (Math.Round(pkm.WeightAbsolute) < Math.Round(calcWeight) || BitConverter.ToUInt32(SAV.Data, ofs) == 0x007F00FE) // unset + if (Math.Round(pkm.WeightAbsolute) < Math.Round(calcWeight) || ReadUInt32LittleEndian(entry) == 0x007F00FE) // unset SetSizeData(pkm, DexSizeType.MinWeight); } else if (Math.Round(pkm.WeightAbsolute) > pkm.PersonalInfo.Weight) // possible maximum weight { int ofs = GetDexSizeOffset(DexSizeType.MaxWeight, index); - var maxWeight = BitConverter.ToUInt16(SAV.Data, ofs + 2); - var maxHeight = BitConverter.ToUInt16(SAV.Data, ofs) >> 1; + var entry = SAV.Data.AsSpan(ofs); + var maxHeight = ReadUInt16LittleEndian(entry) >> 1; + var maxWeight = ReadUInt16LittleEndian(entry[2..]); var calcWeight = PB7.GetWeightAbsolute(pkm.PersonalInfo, maxHeight, maxWeight); - if (Math.Round(pkm.WeightAbsolute) > Math.Round(calcWeight) || BitConverter.ToUInt32(SAV.Data, ofs) == 0x007F00FE) // unset + if (Math.Round(pkm.WeightAbsolute) > Math.Round(calcWeight) || ReadUInt32LittleEndian(entry) == 0x007F00FE) // unset SetSizeData(pkm, DexSizeType.MaxWeight); } } @@ -122,8 +128,9 @@ public void SetSizeData(DexSizeType group, int species, int form, int height, in public void SetSizeData(DexSizeType group, int index, int height, int weight) { var ofs = GetDexSizeOffset(group, index); - BitConverter.GetBytes((ushort)(height << 1)).CopyTo(SAV.Data, ofs); - BitConverter.GetBytes((ushort)(weight)).CopyTo(SAV.Data, ofs + 2); + var span = SAV.Data.AsSpan(ofs); + WriteUInt16LittleEndian(span, (ushort)(height << 1)); + WriteUInt16LittleEndian(span[2..], (ushort)weight); } public static bool TryGetSizeEntryIndex(int species, int form, out int index) @@ -212,4 +219,4 @@ public enum DexSizeType MinWeight, MaxWeight, } -} \ No newline at end of file +} diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan8.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan8.cs index 99923b7c7..fd7c8246c 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan8.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan8.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -195,7 +196,7 @@ public bool GetSeen(Zukan8Index entry) for (int i = 0; i < SeenRegionCount; i++) { var ofs = offset + (SeenRegionSize * i); - if (BitConverter.ToUInt64(data, ofs) != 0) + if (ReadUInt64LittleEndian(data.AsSpan(ofs)) != 0) return true; } @@ -317,8 +318,8 @@ public uint GetFormDisplayed(Zukan8Index entry) { var data = GetDexBlock(entry.DexType); var index = entry.Offset; - var val = BitConverter.ToUInt32(data, index + OFS_CAUGHT); - return (val >> 15) & 0x1FFF; // (0x1FFF is really overkill, GameFreak) + var value = ReadUInt32LittleEndian(data.AsSpan(index + OFS_CAUGHT)); + return (value >> 15) & 0x1FFF; // (0x1FFF is really overkill, GameFreak) } public void SetFormDisplayed(int species, uint value = 0) @@ -333,9 +334,10 @@ public void SetFormDisplayed(Zukan8Index entry, uint value = 0) { var data = GetDexBlock(entry.DexType); var index = entry.Offset; - var val = BitConverter.ToUInt32(data, index + OFS_CAUGHT); - uint nv = (val & ~(0x1FFFu << 15)) | ((value & 0x1FFF) << 15); - BitConverter.GetBytes(nv).CopyTo(data, index + OFS_CAUGHT); + var span = data.AsSpan(index + OFS_CAUGHT); + var current = ReadUInt32LittleEndian(span); + uint update = (current & ~(0x1FFFu << 15)) | ((value & 0x1FFF) << 15); + WriteUInt32LittleEndian(span, update); } public uint GetGenderDisplayed(int species) @@ -350,8 +352,8 @@ public uint GetGenderDisplayed(Zukan8Index entry) { var data = GetDexBlock(entry.DexType); var index = entry.Offset; - var val = BitConverter.ToUInt32(data, index + OFS_CAUGHT); - return (val >> 29) & 3; + var value = ReadUInt32LittleEndian(data.AsSpan(index + OFS_CAUGHT)); + return (value >> 29) & 3; } public void SetGenderDisplayed(int species, uint value = 0) @@ -366,9 +368,10 @@ public void SetGenderDisplayed(Zukan8Index entry, uint value = 0) { var data = GetDexBlock(entry.DexType); var index = entry.Offset; - var val = BitConverter.ToUInt32(data, index + OFS_CAUGHT); - uint nv = (val & ~(3u << 29)) | ((value & 3) << 29); - BitConverter.GetBytes(nv).CopyTo(data, index + OFS_CAUGHT); + var span = data.AsSpan(index + OFS_CAUGHT); + var current = ReadUInt32LittleEndian(span); + uint update = (current & ~(3u << 29)) | ((value & 3) << 29); + WriteUInt32LittleEndian(span, update); } public bool GetDisplayDynamaxInstead(int species) => GetCaughtFlagID(species, 28); @@ -443,7 +446,7 @@ private uint GetU32(Zukan8Index entry, int ofs) var dex = entry.DexType; var index = entry.Offset; var data = GetDexBlock(dex); - return BitConverter.ToUInt32(data, index + ofs); + return ReadUInt32LittleEndian(data.AsSpan(index + ofs)); } private void SetU32(int species, uint value, int ofs) @@ -459,7 +462,7 @@ private void SetU32(Zukan8Index entry, uint value, int ofs) var dex = entry.DexType; var index = entry.Offset; var data = GetDexBlock(dex); - BitConverter.GetBytes(value).CopyTo(data, index + ofs); + WriteUInt32LittleEndian(data.AsSpan(index + ofs), value); } #endregion diff --git a/PKHeX.Core/Saves/Util/Checksums.cs b/PKHeX.Core/Saves/Util/Checksums.cs index 898c65ed2..f404b7f29 100644 --- a/PKHeX.Core/Saves/Util/Checksums.cs +++ b/PKHeX.Core/Saves/Util/Checksums.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -82,15 +83,13 @@ private static ushort CRC16(ReadOnlySpan data, ushort initial) /// Calculates the 32bit checksum over an input byte array. Used in GBA save files. /// Input byte array - /// Offset to start checksum at - /// Length of array to checksum /// Initial value for checksum /// Checksum - public static ushort CheckSum32(byte[] data, int start, int length, uint initial = 0) + public static ushort CheckSum32(ReadOnlySpan data, uint initial = 0) { uint val = initial; - for (int i = start; i < start + length; i += 4) - val += BitConverter.ToUInt32(data, i); + for (int i = 0; i < data.Length; i += 4) + val += ReadUInt32LittleEndian(data[i..]); return (ushort)(val + (val >> 16)); } @@ -106,23 +105,18 @@ public static ushort CheckSum16(ReadOnlySpan data, ushort initial = 0) return acc; } - /// Calculates the 32bit checksum over an input byte array. Used in GBA save files. - /// Input byte array - /// Initial value for checksum - /// Checksum - public static ushort CheckSum32(byte[] data, uint initial = 0) => CheckSum32(data, 0, data.Length, initial); - /// Calculates the 32bit checksum over an input byte array. Used in GC R/S BOX. /// Input byte array /// Checksum public static uint CheckSum16BigInvert(ReadOnlySpan data) { + if ((data.Length & 1) != 0) + data = data[..^2]; + ushort chk = 0; // initial value - while ((data.Length & ~1) != 0) - { - chk += BigEndian.ToUInt16(data); - data = data[2..]; - } + for (int i = 0; i < data.Length; i += 2) + chk += ReadUInt16BigEndian(data[i..]); + return (uint)(chk << 16 | (ushort)(0xF004u - chk)); } } diff --git a/PKHeX.Core/Saves/Util/SaveUtil.cs b/PKHeX.Core/Saves/Util/SaveUtil.cs index f5479790c..dbccd2373 100644 --- a/PKHeX.Core/Saves/Util/SaveUtil.cs +++ b/PKHeX.Core/Saves/Util/SaveUtil.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using static System.Buffers.Binary.BinaryPrimitives; using static PKHeX.Core.MessageStrings; using static PKHeX.Core.GameVersion; @@ -171,7 +172,7 @@ private static GameVersion GetSAVType(byte[] data) /// Offset the list starts at /// Max count of Pokémon in the list /// True if a valid list, False otherwise - private static bool IsG12ListValid(byte[] data, int offset, int listCount) + private static bool IsG12ListValid(ReadOnlySpan data, int offset, int listCount) { byte num_entries = data[offset]; return num_entries <= listCount && data[offset + 1 + num_entries] == 0xFF; @@ -180,7 +181,7 @@ private static bool IsG12ListValid(byte[] data, int offset, int listCount) /// Checks to see if the data belongs to a Gen1 save /// Save data of which to determine the type /// Version Identifier or Invalid if type cannot be determined. - internal static GameVersion GetIsG1SAV(byte[] data) + internal static GameVersion GetIsG1SAV(ReadOnlySpan data) { if (data.Length is not (SIZE_G1RAW or SIZE_G1BAT)) return Invalid; @@ -196,7 +197,7 @@ internal static GameVersion GetIsG1SAV(byte[] data) /// Checks to see if the data belongs to an International Gen1 save /// Save data of which to determine the region /// True if a valid International save, False otherwise. - private static bool GetIsG1SAVU(byte[] data) + private static bool GetIsG1SAVU(ReadOnlySpan data) { return IsG12ListValid(data, 0x2F2C, 20) && IsG12ListValid(data, 0x30C0, 20); } @@ -204,7 +205,7 @@ private static bool GetIsG1SAVU(byte[] data) /// Checks to see if the data belongs to a Japanese Gen1 save /// Save data of which to determine the region /// True if a valid Japanese save, False otherwise. - internal static bool GetIsG1SAVJ(byte[] data) + internal static bool GetIsG1SAVJ(ReadOnlySpan data) { return IsG12ListValid(data, 0x2ED5, 30) && IsG12ListValid(data, 0x302D, 30); } @@ -212,7 +213,7 @@ internal static bool GetIsG1SAVJ(byte[] data) /// Checks to see if the data belongs to a Gen2 save /// Save data of which to determine the type /// Version Identifier or Invalid if type cannot be determined. - internal static GameVersion GetIsG2SAV(byte[] data) + internal static GameVersion GetIsG2SAV(ReadOnlySpan data) { if (!SizesGen2.Contains(data.Length)) return Invalid; @@ -231,7 +232,7 @@ internal static GameVersion GetIsG2SAV(byte[] data) /// Checks to see if the data belongs to an International (not Japanese or Korean) Gen2 save /// Save data of which to determine the region /// True if a valid International save, False otherwise. - private static GameVersion GetIsG2SAVU(byte[] data) + private static GameVersion GetIsG2SAVU(ReadOnlySpan data) { if (IsG12ListValid(data, 0x288A, 20) && IsG12ListValid(data, 0x2D6C, 20)) return GS; @@ -243,7 +244,7 @@ private static GameVersion GetIsG2SAVU(byte[] data) /// Checks to see if the data belongs to a Japanese Gen2 save /// Save data of which to determine the region /// True if a valid Japanese save, False otherwise. - internal static GameVersion GetIsG2SAVJ(byte[] data) + internal static GameVersion GetIsG2SAVJ(ReadOnlySpan data) { if (!IsG12ListValid(data, 0x2D10, 30)) return Invalid; @@ -257,7 +258,7 @@ internal static GameVersion GetIsG2SAVJ(byte[] data) /// Checks to see if the data belongs to a Korean Gen2 save /// Save data of which to determine the region /// True if a valid Korean save, False otherwise. - internal static GameVersion GetIsG2SAVK(byte[] data) + internal static GameVersion GetIsG2SAVK(ReadOnlySpan data) { if (IsG12ListValid(data, 0x2DAE, 20) && IsG12ListValid(data, 0x28CC, 20)) return GS; @@ -267,7 +268,7 @@ internal static GameVersion GetIsG2SAVK(byte[] data) /// Checks to see if the data belongs to a Gen3 save /// Save data of which to determine the type /// Version Identifier or Invalid if type cannot be determined. - private static GameVersion GetIsG3SAV(byte[] data) + private static GameVersion GetIsG3SAV(ReadOnlySpan data) { if (data.Length is not (SIZE_G3RAW or SIZE_G3RAWHALF)) return Invalid; @@ -280,7 +281,7 @@ private static GameVersion GetIsG3SAV(byte[] data) continue; // Detect RS/E/FRLG - return GetVersionG3SAV(data, smallOffset); + return GetVersionG3SAV(data[smallOffset..]); } return Invalid; } @@ -289,16 +290,15 @@ private static GameVersion GetIsG3SAV(byte[] data) /// Checks the input to see which game is for this file. /// /// Data to check - /// Offset for the start of the first Small chunk. /// RS, E, or FR/LG. - private static GameVersion GetVersionG3SAV(byte[] data, int offset = 0) + private static GameVersion GetVersionG3SAV(ReadOnlySpan data) { // 0xAC // RS: Battle Tower Data, which will never match the FR/LG fixed value. // E: Encryption Key // FR/LG @ 0xAC has a fixed value (01 00 00 00) // RS has battle tower data (variable) - uint _0xAC = BitConverter.ToUInt32(data, offset + 0xAC); + uint _0xAC = ReadUInt32LittleEndian(data[0xAC..]); switch (_0xAC) { case 1: return FRLG; // fixed value @@ -307,7 +307,7 @@ private static GameVersion GetVersionG3SAV(byte[] data, int offset = 0) // RS data structure only extends 0x890 bytes; check if any data is present afterwards. for (int i = 0x890; i < 0xF2C; i += 4) { - if (BitConverter.ToUInt64(data, offset + i) != 0) + if (ReadUInt64LittleEndian(data[i..]) != 0) return E; } return RS; @@ -317,23 +317,23 @@ private static GameVersion GetVersionG3SAV(byte[] data, int offset = 0) /// Checks to see if the data belongs to a Gen3 Box RS save /// Save data of which to determine the type /// Version Identifier or Invalid if type cannot be determined. - private static GameVersion GetIsG3BOXSAV(byte[] data) + private static GameVersion GetIsG3BOXSAV(ReadOnlySpan data) { if (data.Length is not SIZE_G3BOX) return Invalid; // Verify first checksum const int offset = 0x2000; - var span = new ReadOnlySpan(data, offset + 4, 0x1FF8); - var chk = Checksums.CheckSum16BigInvert(span); - var actual = BigEndian.ToUInt32(data, offset); + var span = data.Slice(offset, 0x1FFC); + var actual = ReadUInt32BigEndian(span); + var chk = Checksums.CheckSum16BigInvert(span[4..]); return chk == actual ? RSBOX : Invalid; } /// Checks to see if the data belongs to a Colosseum save /// Save data of which to determine the type /// Version Identifier or Invalid if type cannot be determined. - private static GameVersion GetIsG3COLOSAV(byte[] data) + private static GameVersion GetIsG3COLOSAV(ReadOnlySpan data) { if (data.Length is not SIZE_G3COLO) return Invalid; @@ -343,7 +343,7 @@ private static GameVersion GetIsG3COLOSAV(byte[] data) for (int i = 0; i < 3; i++) { var ofs = offset + (0x1E000 * i); - if (BitConverter.ToUInt32(data, ofs) != 0x00000101) + if (ReadUInt32LittleEndian(data[ofs..]) != 0x00000101) return Invalid; } return COLO; @@ -352,7 +352,7 @@ private static GameVersion GetIsG3COLOSAV(byte[] data) /// Checks to see if the data belongs to a Gen3 XD save /// Save data of which to determine the type /// Version Identifier or Invalid if type cannot be determined. - private static GameVersion GetIsG3XDSAV(byte[] data) + private static GameVersion GetIsG3XDSAV(ReadOnlySpan data) { if (data.Length is not SIZE_G3XD) return Invalid; @@ -362,7 +362,7 @@ private static GameVersion GetIsG3XDSAV(byte[] data) for (int i = 0; i < 2; i++) { var ofs = offset + (0x28000 * i); - if ((BitConverter.ToUInt32(data, ofs) & 0xFFFE_FFFF) != 0x00000101) + if ((ReadUInt32LittleEndian(data[ofs..]) & 0xFFFE_FFFF) != 0x00000101) return Invalid; } return XD; @@ -371,19 +371,19 @@ private static GameVersion GetIsG3XDSAV(byte[] data) /// Checks to see if the data belongs to a Gen4 save /// Save data of which to determine the type /// Version Identifier or Invalid if type cannot be determined. - private static GameVersion GetIsG4SAV(byte[] data) + private static GameVersion GetIsG4SAV(ReadOnlySpan data) { if (data.Length != SIZE_G4RAW) return Invalid; // The block footers contain a u32 'size' followed by a u32 binary-coded-decimal timestamp(?) // Korean saves have a different timestamp from other localizations. - bool validSequence(int offset) + static bool validSequence(ReadOnlySpan data, int offset) { - var size = BitConverter.ToUInt32(data, offset - 0xC); + var size = ReadUInt32LittleEndian(data[(offset - 0xC)..]); if (size != (offset & 0xFFFF)) return false; - var sdk = BitConverter.ToUInt32(data, offset - 0x8); + var sdk = ReadUInt32LittleEndian(data[(offset - 0x8)..]); const int DATE_INT = 0x20060623; const int DATE_KO = 0x20070903; @@ -392,11 +392,11 @@ bool validSequence(int offset) // Check the other save -- first save is done to the latter half of the binary. // The second save should be all that is needed to check. - if (validSequence(0x4C100)) + if (validSequence(data, 0x4C100)) return DP; - if (validSequence(0x4CF2C)) + if (validSequence(data, 0x4CF2C)) return Pt; - if (validSequence(0x4F628)) + if (validSequence(data, 0x4F628)) return HGSS; return Invalid; @@ -405,7 +405,7 @@ bool validSequence(int offset) /// Checks to see if the data belongs to a Gen4 Battle Revolution save /// Save data of which to determine the type /// Version Identifier or Invalid if type cannot be determined. - private static GameVersion GetIsG4BRSAV(byte[] data) + private static GameVersion GetIsG4BRSAV(ReadOnlySpan data) { if (data.Length != SIZE_G4BR) return Invalid; @@ -417,18 +417,18 @@ private static GameVersion GetIsG4BRSAV(byte[] data) /// Checks to see if the data belongs to a Gen5 save /// Save data of which to determine the type /// Version Identifier or Invalid if type cannot be determined. - private static GameVersion GetIsG5SAV(byte[] data) + private static GameVersion GetIsG5SAV(ReadOnlySpan data) { if (data.Length != SIZE_G5RAW) return Invalid; // check the checksum block validity; nobody would normally modify this region - ushort chk1 = BitConverter.ToUInt16(data, SIZE_G5BW - 0x100 + 0x8C + 0xE); - ushort actual1 = Checksums.CRC16_CCITT(new ReadOnlySpan(data, SIZE_G5BW - 0x100, 0x8C)); + ushort chk1 = ReadUInt16LittleEndian(data[(SIZE_G5BW - 0x100 + 0x8C + 0xE)..]); + ushort actual1 = Checksums.CRC16_CCITT(data.Slice(SIZE_G5BW - 0x100, 0x8C)); if (chk1 == actual1) return BW; - ushort chk2 = BitConverter.ToUInt16(data, SIZE_G5B2W2 - 0x100 + 0x94 + 0xE); - ushort actual2 = Checksums.CRC16_CCITT(new ReadOnlySpan(data, SIZE_G5B2W2 - 0x100, 0x94)); + ushort chk2 = ReadUInt16LittleEndian(data[(SIZE_G5B2W2 - 0x100 + 0x94 + 0xE)..]); + ushort actual2 = Checksums.CRC16_CCITT(data.Slice(SIZE_G5B2W2 - 0x100, 0x94)); if (chk2 == actual2) return B2W2; return Invalid; @@ -437,12 +437,12 @@ private static GameVersion GetIsG5SAV(byte[] data) /// Checks to see if the data belongs to a Gen6 save /// Save data of which to determine the type /// Version Identifier or Invalid if type cannot be determined. - private static GameVersion GetIsG6SAV(byte[] data) + private static GameVersion GetIsG6SAV(ReadOnlySpan data) { if (data.Length is not (SIZE_G6XY or SIZE_G6ORAS or SIZE_G6ORASDEMO)) return Invalid; - if (BitConverter.ToUInt32(data, data.Length - 0x1F0) != BEEF) + if (ReadUInt32LittleEndian(data[^0x1F0..]) != BEEF) return Invalid; return data.Length switch @@ -456,12 +456,12 @@ private static GameVersion GetIsG6SAV(byte[] data) /// Checks to see if the data belongs to a Gen7 save /// Save data of which to determine the type /// Version Identifier or Invalid if type cannot be determined. - private static GameVersion GetIsG7SAV(byte[] data) + private static GameVersion GetIsG7SAV(ReadOnlySpan data) { if (data.Length is not (SIZE_G7SM or SIZE_G7USUM)) return Invalid; - if (BitConverter.ToUInt32(data, data.Length - 0x1F0) != BEEF) + if (ReadUInt32LittleEndian(data[^0x1F0..]) != BEEF) return Invalid; return data.Length == SIZE_G7SM ? SM : USUM; @@ -470,15 +470,15 @@ private static GameVersion GetIsG7SAV(byte[] data) /// Determines if the input data belongs to a save /// Save data of which to determine the type /// Version Identifier or Invalid if type cannot be determined. - private static GameVersion GetIsBelugaSAV(byte[] data) + private static GameVersion GetIsBelugaSAV(ReadOnlySpan data) { if (data.Length != SIZE_G7GG) return Invalid; const int actualLength = 0xB8800; - if (BitConverter.ToUInt32(data, actualLength - 0x1F0) != BEEF) // beef table start + if (ReadUInt32LittleEndian(data[(actualLength - 0x1F0)..]) != BEEF) // beef table start return Invalid; - if (BitConverter.ToUInt16(data, actualLength - 0x200 + 0xB0) != 0x13) // check a block number to double check + if (ReadUInt16LittleEndian(data[(actualLength - 0x200 + 0xB0)..]) != 0x13) // check a block number to double check return Invalid; return GG; @@ -495,7 +495,7 @@ private static GameVersion GetIsG8SAV(byte[] data) return SwishCrypto.GetIsHashValid(data) ? SWSH : Invalid; } - private static GameVersion GetIsG8SAV_BDSP(byte[] data) + private static GameVersion GetIsG8SAV_BDSP(ReadOnlySpan data) { if (data.Length is not SIZE_G8BDSP && data.Length is not SIZE_G8BDSP_1) return Invalid; @@ -503,12 +503,12 @@ private static GameVersion GetIsG8SAV_BDSP(byte[] data) return BDSP; } - private static bool GetIsBank7(byte[] data) => data.Length == SIZE_G7BANK && data[0] != 0; - private static bool GetIsBank4(byte[] data) => data.Length == SIZE_G4BANK && BitConverter.ToUInt32(data, 0x3FC00) != 0; // box name present - private static bool GetIsBank3(byte[] data) => data.Length == SIZE_G4BANK && BitConverter.ToUInt32(data, 0x3FC00) == 0; // size collision with ^ - private static bool GetIsRanchDP(byte[] data) => data.Length == SIZE_G4RANCH && BigEndian.ToUInt32(data, 0x22AC) != 0; - private static bool GetIsRanchPlat(byte[] data) => data.Length == SIZE_G4RANCH_PLAT && BigEndian.ToUInt32(data, 0x268C) != 0; - private static bool GetIsRanch4(byte[] data) => GetIsRanchDP(data) || GetIsRanchPlat(data); + private static bool GetIsBank7(ReadOnlySpan data) => data.Length == SIZE_G7BANK && data[0] != 0; + private static bool GetIsBank4(ReadOnlySpan data) => data.Length == SIZE_G4BANK && ReadUInt32LittleEndian(data[0x3FC00..]) != 0; // box name present + private static bool GetIsBank3(ReadOnlySpan data) => data.Length == SIZE_G4BANK && ReadUInt32LittleEndian(data[0x3FC00..]) == 0; // size collision with ^ + private static bool GetIsRanchDP(ReadOnlySpan data) => data.Length == SIZE_G4RANCH && ReadUInt32BigEndian(data[0x22AC..]) != 0; + private static bool GetIsRanchPlat(ReadOnlySpan data) => data.Length == SIZE_G4RANCH_PLAT && ReadUInt32BigEndian(data[0x268C..]) != 0; + private static bool GetIsRanch4(ReadOnlySpan data) => GetIsRanchDP(data) || GetIsRanchPlat(data); /// Creates an instance of a SaveFile using the given save data. /// File location from which to create a SaveFile. diff --git a/PKHeX.Core/Saves/Util/StadiumUtil.cs b/PKHeX.Core/Saves/Util/StadiumUtil.cs index 1ae4d1e36..5d029e7d1 100644 --- a/PKHeX.Core/Saves/Util/StadiumUtil.cs +++ b/PKHeX.Core/Saves/Util/StadiumUtil.cs @@ -1,4 +1,5 @@ using System; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -10,7 +11,7 @@ public static class StadiumUtil /// /// Checks if the value is present either with or without byte-swapping. /// - public static bool IsMagicPresentEither(byte[] data, int size, uint magic) + public static bool IsMagicPresentEither(ReadOnlySpan data, int size, uint magic) { if (IsMagicPresent(data, size, magic)) return true; @@ -24,12 +25,13 @@ public static bool IsMagicPresentEither(byte[] data, int size, uint magic) /// /// Checks if the value is present without byte-swapping. /// - public static bool IsMagicPresent(byte[] data, int size, uint magic) + public static bool IsMagicPresent(ReadOnlySpan data, int size, uint magic) { // Check footers of first few teams to see if the magic value is there. for (int i = 0; i < 10; i++) { - if (BitConverter.ToUInt32(data, size - 6 + (i * size)) != magic) + var footer = data[(size - 6 + (i * size))..]; + if (ReadUInt32LittleEndian(footer) != magic) return false; } return true; @@ -38,7 +40,7 @@ public static bool IsMagicPresent(byte[] data, int size, uint magic) /// /// Checks if the value is present either with byte-swapping. /// - public static bool IsMagicPresentSwap(byte[] data, int size, uint magic) + public static bool IsMagicPresentSwap(ReadOnlySpan data, int size, uint magic) { // Check footers of first few teams to see if the magic value is there. var left = (ushort)magic; @@ -49,17 +51,17 @@ public static bool IsMagicPresentSwap(byte[] data, int size, uint magic) for (int i = 0; i < 10; i++) { var ofs = size - 6 + (i * size); - if (BitConverter.ToUInt16(data, ofs - 2) != left) // OP + if (ReadUInt16LittleEndian(data[(ofs - 2)..]) != left) // OP return false; - if (BitConverter.ToUInt16(data, ofs + 4) != right) // EK + if (ReadUInt16LittleEndian(data[(ofs + 4)..]) != right) // EK return false; } return true; } - public static bool IsMagicPresentAbsolute(byte[] data, int offset, uint magic) + public static bool IsMagicPresentAbsolute(ReadOnlySpan data, int offset, uint magic) { - var actual = BitConverter.ToUInt32(data, offset); + var actual = ReadUInt32LittleEndian(data[offset..]); if (actual == magic) // POKE return true; @@ -68,9 +70,9 @@ public static bool IsMagicPresentAbsolute(byte[] data, int offset, uint magic) left = (ushort)((left >> 8) | (left << 8)); right = (ushort)((right >> 8) | (right << 8)); - if (BitConverter.ToUInt16(data, offset - 2) != left) // OP + if (ReadUInt16LittleEndian(data[(offset - 2)..]) != left) // OP return false; - if (BitConverter.ToUInt16(data, offset + 4) != right) // EK + if (ReadUInt16LittleEndian(data[(offset + 4)..]) != right) // EK return false; return true; diff --git a/PKHeX.Core/Util/ArrayUtil.cs b/PKHeX.Core/Util/ArrayUtil.cs index b3ed9b832..f4c069db7 100644 --- a/PKHeX.Core/Util/ArrayUtil.cs +++ b/PKHeX.Core/Util/ArrayUtil.cs @@ -18,7 +18,22 @@ public static bool IsRangeEmpty(this ReadOnlySpan data, byte value = 0) return true; } - public static byte[] Truncate(byte[] data, int newSize) => data.AsSpan(0, newSize).ToArray(); + public static int Count(this Span data, T value) where T : IEquatable + { + return ((ReadOnlySpan)data).Count(value); + } + + public static int Count(this ReadOnlySpan data, T value) where T : IEquatable + { + int count = 0; + for (int i = data.Length - 1; i >= 0; i--) + { + if (data[i].Equals(value)) + count++; + } + return count; + } + public static byte[] Slice(this byte[] src, int offset, int length) => src.AsSpan(offset, length).ToArray(); public static byte[] SliceEnd(this byte[] src, int offset) => src.AsSpan(offset).ToArray(); public static T[] Slice(this T[] src, int offset, int length) => src.AsSpan(offset, length).ToArray(); @@ -26,11 +41,11 @@ public static bool IsRangeEmpty(this ReadOnlySpan data, byte value = 0) public static bool WithinRange(int value, int min, int max) => min <= value && value < max; - public static T[][] Split(this T[] data, int size) + public static T[][] Split(this ReadOnlySpan data, int size) { var result = new T[data.Length / size][]; for (int i = 0; i < data.Length; i += size) - result[i / size] = data.Slice(i, size); + result[i / size] = data.Slice(i, size).ToArray(); return result; } @@ -48,19 +63,19 @@ public static IEnumerable EnumerateSplit(T[] bin, int size, int start, i yield return bin.Slice(i, size); } - public static bool[] GitBitFlagArray(byte[] data, int offset, int count) + public static bool[] GitBitFlagArray(ReadOnlySpan data, int count) { bool[] result = new bool[count]; for (int i = 0; i < result.Length; i++) - result[i] = (data[offset + (i >> 3)] >> (i & 7) & 0x1) == 1; + result[i] = (data[i >> 3] >> (i & 7) & 0x1) == 1; return result; } - public static void SetBitFlagArray(byte[] data, int offset, bool[] value) + public static void SetBitFlagArray(Span data, bool[] value) { for (int i = 0; i < value.Length; i++) { - var ofs = offset + (i >> 3); + var ofs = i >> 3; var mask = (1 << (i & 7)); if (value[i]) data[ofs] |= (byte)mask; @@ -69,13 +84,6 @@ public static void SetBitFlagArray(byte[] data, int offset, bool[] value) } } - public static byte[] SetBitFlagArray(bool[] value) - { - byte[] data = new byte[value.Length / 8]; - SetBitFlagArray(data, 0, value); - return data; - } - /// /// Copies a list to the destination list, with an option to copy to a starting point. /// diff --git a/PKHeX.Core/Util/BigEndian.cs b/PKHeX.Core/Util/BigEndian.cs deleted file mode 100644 index f6487632e..000000000 --- a/PKHeX.Core/Util/BigEndian.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using static System.Buffers.Binary.BinaryPrimitives; - -namespace PKHeX.Core -{ - public static class BigEndian - { - public static uint ToUInt32(ReadOnlySpan data) => ReadUInt32BigEndian(data); - public static ushort ToUInt16(ReadOnlySpan data) => ReadUInt16BigEndian(data); - public static int ToInt32(ReadOnlySpan data) => ReadInt32BigEndian(data); - public static short ToInt16(ReadOnlySpan data) => ReadInt16BigEndian(data); - public static uint ToUInt32(byte[] data, int offset) => ReadUInt32BigEndian(data.AsSpan(offset)); - public static ushort ToUInt16(byte[] data, int offset) => ReadUInt16BigEndian(data.AsSpan(offset)); - public static int ToInt32(byte[] data, int offset) => ReadInt32BigEndian(data.AsSpan(offset)); - public static short ToInt16(byte[] data, int offset) => ReadInt16BigEndian(data.AsSpan(offset)); - - public static byte[] GetBytes(int value) - { - return Invert(BitConverter.GetBytes(value)); - } - - public static byte[] GetBytes(short value) - { - return Invert(BitConverter.GetBytes(value)); - } - - public static byte[] GetBytes(uint value) - { - return Invert(BitConverter.GetBytes(value)); - } - - public static byte[] GetBytes(ushort value) - { - return Invert(BitConverter.GetBytes(value)); - } - - private static byte[] Invert(byte[] data) - { - int i = 0; - int j = 0 + data.Length - 1; - while (i < j) - { - (data[i], data[j]) = (data[j], data[i]); - i++; - j--; - } - return data; - } - - /// - /// Swaps byte ordering in a byte array based on 32bit value writes. - /// - /// The is reversed in-place. - public static void SwapBytes32(byte[] data) - { - for (int i = 0; i < data.Length; i += 4) - { - (data[0 + i], data[3 + i]) = (data[3 + i], data[0 + i]); - (data[1 + i], data[2 + i]) = (data[2 + i], data[1 + i]); - } - } - } -} diff --git a/PKHeX.Core/Util/BinaryCodedDecimal.cs b/PKHeX.Core/Util/BinaryCodedDecimal.cs index 2a370d8f4..029e75207 100644 --- a/PKHeX.Core/Util/BinaryCodedDecimal.cs +++ b/PKHeX.Core/Util/BinaryCodedDecimal.cs @@ -12,15 +12,6 @@ public static class BinaryCodedDecimal /// Returns a 32-bit signed integer converted from bytes in a Binary Coded Decimal format byte array. /// /// Input byte array to read from. - /// Offset to start reading at. - /// Length of array to read. - public static int ToInt32BE(byte[] input, int offset, int length) - { - var span = new ReadOnlySpan(input, offset, length); - return ToInt32BE(span); - } - - /// public static int ToInt32BE(ReadOnlySpan input) { int result = 0; @@ -33,32 +24,15 @@ public static int ToInt32BE(ReadOnlySpan input) private static void PushDigits(ref int result, byte b) => result = (result * 100) + (10 * (b >> 4)) + (b & 0xf); /// - /// Returns the specified 32-bit signed integer value as an array of Binary Coded Decimal format bytes. + /// Writes the to the buffer. /// - /// 32-bit signed integer to convert. - /// Desired size of returned array. - public static byte[] GetBytesBE(int value, int size) - { - byte[] data = new byte[size]; - WriteBytesBE(data, value); - return data; - } - public static void WriteBytesBE(Span data, int value) { for (int i = data.Length - 1; i >= 0; i--, value /= 100) data[i] = (byte)((((value / 10) % 10) << 4) | (value % 10)); } - /// - /// Big Endian instead of Little Endian - public static int ToInt32LE(byte[] data, int offset, int length) - { - var span = new ReadOnlySpan(data, offset, length); - return ToInt32LE(span); - } - - /// + /// public static int ToInt32LE(ReadOnlySpan input) { int result = 0; @@ -67,15 +41,6 @@ public static int ToInt32LE(ReadOnlySpan input) return result; } - /// - /// Little Endian instead of Big Endian - public static byte[] GetBytesLE(int value, int size) - { - byte[] data = new byte[size]; - WriteBytesLE(data, value); - return data; - } - /// /// Writes the to the buffer. /// diff --git a/PKHeX.Core/Util/ComboItemUtil.cs b/PKHeX.Core/Util/ComboItemUtil.cs index 05ff659eb..c6e2b5575 100644 --- a/PKHeX.Core/Util/ComboItemUtil.cs +++ b/PKHeX.Core/Util/ComboItemUtil.cs @@ -19,9 +19,9 @@ private static List GetCBListFromCSV(IReadOnlyList inputCSV, var arr = new List(inputCSV.Count); foreach (var line in inputCSV) { - var val = line[..3]; - var text = StringUtil.GetNthEntry(line, index, 4); - var item = new ComboItem(text, Convert.ToInt32(val)); + var text = StringUtil.GetNthEntry(line.AsSpan(), index, 4); + var value = line.AsSpan(0, 3); + var item = new ComboItem(text, ToInt32(value)); arr.Add(item); } return arr; @@ -52,11 +52,11 @@ public static List GetCBList(IReadOnlyList inStrings, int ind return list; } - public static IReadOnlyList GetUnsortedCBList(IReadOnlyList inStrings, IReadOnlyList allowed) + public static IReadOnlyList GetUnsortedCBList(IReadOnlyList inStrings, ReadOnlySpan allowed) { - var count = allowed.Count; + var count = allowed.Length; var list = new ComboItem[count]; - for (var i = 0; i < allowed.Count; i++) + for (var i = 0; i < allowed.Length; i++) { var index = allowed[i]; var item = new ComboItem(inStrings[index], index); @@ -116,7 +116,7 @@ public static void AddCB(List cbList, IReadOnlyList inStrings cbList.Sort(beginCount, allowed.Length, Comparer); } - public static ComboItem[] GetVariedCBListBall(string[] inStrings, ushort[] stringNum, byte[] stringVal) + public static ComboItem[] GetVariedCBListBall(string[] inStrings, ReadOnlySpan stringNum, ReadOnlySpan stringVal) { const int forcedTop = 3; // 3 Balls are preferentially first var list = new ComboItem[forcedTop + stringNum.Length]; @@ -127,9 +127,9 @@ public static ComboItem[] GetVariedCBListBall(string[] inStrings, ushort[] strin for (int i = 0; i < stringNum.Length; i++) { int index = stringNum[i]; - var val = stringVal[i]; - var txt = inStrings[index]; - list[i + 3] = new ComboItem(txt, val); + var value = stringVal[i]; + var text = inStrings[index]; + list[i + 3] = new ComboItem(text, value); } Array.Sort(list, 3, list.Length - 3, Comparer); diff --git a/PKHeX.Core/Util/FileUtil.cs b/PKHeX.Core/Util/FileUtil.cs index 25a8d2877..cf9c49806 100644 --- a/PKHeX.Core/Util/FileUtil.cs +++ b/PKHeX.Core/Util/FileUtil.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -87,7 +88,7 @@ public static int GetFileSize(string path) private static bool TryGetGP1(byte[] data, [NotNullWhen(true)] out GP1? gp1) { gp1 = null; - if (data.Length != GP1.SIZE || BitConverter.ToUInt32(data, 0x28) == 0) + if (data.Length != GP1.SIZE || ReadUInt32LittleEndian(data.AsSpan(0x28)) == 0) return false; gp1 = new GP1(data); return true; diff --git a/PKHeX.Core/Util/FlagUtil.cs b/PKHeX.Core/Util/FlagUtil.cs index 55fe21222..f4ce976c4 100644 --- a/PKHeX.Core/Util/FlagUtil.cs +++ b/PKHeX.Core/Util/FlagUtil.cs @@ -1,4 +1,6 @@ -namespace PKHeX.Core +using System; + +namespace PKHeX.Core { /// /// Utility logic for dealing with bitflags in a byte array. @@ -11,7 +13,7 @@ public static class FlagUtil /// Buffer to read /// Offset of the byte /// Bit to read - public static bool GetFlag(byte[] arr, int offset, int bitIndex) + public static bool GetFlag(ReadOnlySpan arr, int offset, int bitIndex) { bitIndex &= 7; // ensure bit access is 0-7 return (arr[offset] >> bitIndex & 1) != 0; @@ -24,7 +26,7 @@ public static bool GetFlag(byte[] arr, int offset, int bitIndex) /// Offset of the byte /// Bit to write /// Bit flag value to set - public static void SetFlag(byte[] arr, int offset, int bitIndex, bool value) + public static void SetFlag(Span arr, int offset, int bitIndex, bool value) { bitIndex &= 7; // ensure bit access is 0-7 var current = arr[offset] & ~(1 << bitIndex); diff --git a/PKHeX.Core/Util/FrameworkUtil.cs b/PKHeX.Core/Util/FrameworkUtil.cs index fd8198f0e..e21b1c2df 100644 --- a/PKHeX.Core/Util/FrameworkUtil.cs +++ b/PKHeX.Core/Util/FrameworkUtil.cs @@ -1,6 +1,20 @@ -#if !NET5 +#if !NET6 #pragma warning disable // ReSharper disable once UnusedType.Global +global using static PKHeX.Core.Buffers.Binary.Extra.BinaryPrimitives; +using System; +using System.Runtime.InteropServices; + +namespace PKHeX.Core.Buffers.Binary.Extra +{ + internal static class BinaryPrimitives + { + public static float ReadSingleLittleEndian(ReadOnlySpan data) => MemoryMarshal.Read(data); + public static void WriteSingleLittleEndian(Span data, float value) => MemoryMarshal.Write(data, ref value); + public static double ReadDoubleLittleEndian(ReadOnlySpan data) => MemoryMarshal.Read(data); + public static void WriteDoubleLittleEndian(Span data, double value) => MemoryMarshal.Write(data, ref value); + } +} namespace System.Runtime.CompilerServices { diff --git a/PKHeX.Core/Util/RandUtil.cs b/PKHeX.Core/Util/RandUtil.cs index 9bef56e00..755c9fe5c 100644 --- a/PKHeX.Core/Util/RandUtil.cs +++ b/PKHeX.Core/Util/RandUtil.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Threading; namespace PKHeX.Core @@ -19,7 +18,7 @@ public static partial class Util /// /// Item type /// Item collection - public static void Shuffle(IList items) => Shuffle(items, 0, items.Count, Rand); + public static void Shuffle(Span items) => Shuffle(items, 0, items.Length, Rand); /// /// Shuffles the order of items within a collection of items. @@ -29,7 +28,7 @@ public static partial class Util /// Starting position /// Ending position /// RNG object to use - public static void Shuffle(IList items, int start, int end, Random rnd) + public static void Shuffle(Span items, int start, int end, Random rnd) { for (int i = start; i < end; i++) { diff --git a/PKHeX.Core/Util/StringUtil.cs b/PKHeX.Core/Util/StringUtil.cs index 1e7b44dc3..de6f6c74f 100644 --- a/PKHeX.Core/Util/StringUtil.cs +++ b/PKHeX.Core/Util/StringUtil.cs @@ -57,15 +57,17 @@ private static bool IsMatchIgnoreCase(string string1, string string2) /// /// Gets the string entry within the input , based on the and position. /// - public static string GetNthEntry(string line, int nth, int start, char separator = ',') + public static string GetNthEntry(ReadOnlySpan line, int nth, int start, char separator = ',') { if (nth != 1) start = line.IndexOfNth(separator, nth - 1, start + 1); var end = line.IndexOfNth(separator, 1, start + 1); - return end == -1 ? line[(start + 1)..] : line[(start + 1)..end]; + if (end == -1) + return new string(line[(start + 1)..].ToArray()); + return new string(line[(start + 1)..end].ToArray()); } - private static int IndexOfNth(this string s, char t, int n, int start) + private static int IndexOfNth(this ReadOnlySpan s, char t, int n, int start) { int count = 0; for (int i = start; i < s.Length; i++) diff --git a/PKHeX.Core/Util/Util.cs b/PKHeX.Core/Util/Util.cs index 8fea4b443..9bd988e26 100644 --- a/PKHeX.Core/Util/Util.cs +++ b/PKHeX.Core/Util/Util.cs @@ -8,15 +8,33 @@ namespace PKHeX.Core { public static partial class Util { + /// + public static int ToInt32(string value) => ToInt32(value.AsSpan()); + + /// + public static uint ToUInt32(string value) => ToUInt32(value.AsSpan()); + + /// + public static uint GetHexValue(string value) => GetHexValue(value.AsSpan()); + + /// + public static ulong GetHexValue64(string value) => GetHexValue64(value.AsSpan()); + + /// + public static byte[] GetBytesFromHexString(string value) => GetBytesFromHexString(value.AsSpan()); + + /// + public static string GetHexStringFromBytes(byte[] data, int offset, int length) => GetHexStringFromBytes(data.AsSpan(offset, length)); + /// /// Parses the string into an , skipping all characters except for valid digits. /// /// String to parse /// Parsed value - public static int ToInt32(string value) + public static int ToInt32(ReadOnlySpan value) { int result = 0; - if (string.IsNullOrEmpty(value)) + if (value.Length == 0) return result; bool negative = false; @@ -41,10 +59,10 @@ public static int ToInt32(string value) /// /// String to parse /// Parsed value - public static uint ToUInt32(string value) + public static uint ToUInt32(ReadOnlySpan value) { uint result = 0; - if (string.IsNullOrEmpty(value)) + if (value.Length == 0) return result; foreach (var c in value) @@ -62,10 +80,10 @@ public static uint ToUInt32(string value) /// /// Hex String to parse /// Parsed value - public static uint GetHexValue(string value) + public static uint GetHexValue(ReadOnlySpan value) { uint result = 0; - if (string.IsNullOrEmpty(value)) + if (value.Length == 0) return result; foreach (var c in value) @@ -94,10 +112,10 @@ public static uint GetHexValue(string value) /// /// Hex String to parse /// Parsed value - public static ulong GetHexValue64(string value) + public static ulong GetHexValue64(ReadOnlySpan value) { ulong result = 0; - if (string.IsNullOrEmpty(value)) + if (value.Length == 0) return result; foreach (var c in value) @@ -121,20 +139,19 @@ public static ulong GetHexValue64(string value) return result; } - public static byte[] GetBytesFromHexString(string seed) + public static byte[] GetBytesFromHexString(ReadOnlySpan seed) { byte[] result = new byte[seed.Length / 2]; for (int i = 0; i < result.Length; i++) { - var slice = seed.Substring(i * 2, 2); - result[^(i+1)] = Convert.ToByte(slice, 16); + var slice = seed.Slice(i * 2, 2); + result[^(i+1)] = (byte)GetHexValue(slice); } return result; } - public static string GetHexStringFromBytes(byte[] data, int offset, int length) + public static string GetHexStringFromBytes(ReadOnlySpan arr) { - var arr = data.AsSpan(offset, length); var sb = new StringBuilder(arr.Length * 2); for (int i = arr.Length - 1; i >= 0; i--) sb.AppendFormat("{0:X2}", arr[i]); diff --git a/PKHeX.Drawing/ImageUtil.cs b/PKHeX.Drawing/ImageUtil.cs index 15b01854e..aacbbe9a7 100644 --- a/PKHeX.Drawing/ImageUtil.cs +++ b/PKHeX.Drawing/ImageUtil.cs @@ -115,7 +115,7 @@ public static byte[] GetPixelData(Bitmap bitmap) return argbData; } - public static void SetAllUsedPixelsOpaque(byte[] data) + public static void SetAllUsedPixelsOpaque(Span data) { for (int i = 0; i < data.Length; i += 4) { @@ -124,7 +124,7 @@ public static void SetAllUsedPixelsOpaque(byte[] data) } } - public static void RemovePixels(byte[] pixels, byte[] original) + public static void RemovePixels(Span pixels, ReadOnlySpan original) { for (int i = 0; i < original.Length; i += 4) { @@ -137,13 +137,13 @@ public static void RemovePixels(byte[] pixels, byte[] original) } } - private static void SetAllTransparencyTo(byte[] data, double trans) + private static void SetAllTransparencyTo(Span data, double trans) { for (int i = 0; i < data.Length; i += 4) data[i + 3] = (byte)(data[i + 3] * trans); } - public static void SetAllTransparencyTo(byte[] data, Color c, byte trans, int start) + public static void SetAllTransparencyTo(Span data, Color c, byte trans, int start) { byte R = c.R; byte G = c.G; @@ -159,7 +159,7 @@ public static void SetAllTransparencyTo(byte[] data, Color c, byte trans, int st } } - public static void ChangeAllTo(byte[] data, Color c, int start, int end) + public static void ChangeAllTo(Span data, Color c, int start, int end) { byte R = c.R; byte G = c.G; @@ -174,7 +174,7 @@ public static void ChangeAllTo(byte[] data, Color c, int start, int end) } } - public static void ChangeAllColorTo(byte[] data, Color c) + public static void ChangeAllColorTo(Span data, Color c) { byte R = c.R; byte G = c.G; @@ -189,7 +189,7 @@ public static void ChangeAllColorTo(byte[] data, Color c) } } - private static void SetAllColorToGrayScale(byte[] data) + private static void SetAllColorToGrayScale(Span data) { for (int i = 0; i < data.Length; i += 4) { @@ -202,13 +202,13 @@ private static void SetAllColorToGrayScale(byte[] data) } } - public static void GlowEdges(byte[] data, byte blue, byte green, byte red, int width, int reach = 3, double amount = 0.0777) + public static void GlowEdges(Span data, byte blue, byte green, byte red, int width, int reach = 3, double amount = 0.0777) { PollutePixels(data, width, reach, amount); CleanPollutedPixels(data, blue, green, red); } - private static void PollutePixels(byte[] data, int width, int reach, double amount) + private static void PollutePixels(Span data, int width, int reach, double amount) { int stride = width * 4; int height = data.Length / stride; @@ -220,29 +220,26 @@ private static void PollutePixels(byte[] data, int width, int reach, double amou int x = (i % stride) / 4; int y = (i / stride); - Pollute(x, y); - } - - void Pollute(int x, int y) - { - int left = Math.Max(0, x - reach); - int right = Math.Min(width - 1, x + reach); - int top = Math.Max(0, y - reach); - int bottom = Math.Min(height - 1, y + reach); - for (int i = left; i <= right; i++) { - for (int j = top; j <= bottom; j++) + int left = Math.Max(0, x - reach); + int right = Math.Min(width - 1, x + reach); + int top = Math.Max(0, y - reach); + int bottom = Math.Min(height - 1, y + reach); + for (int ix = left; ix <= right; ix++) { - // update one of the color bits - // it is expected that a transparent pixel RGBA value is 0. - var c = 4 * (i + (j * width)); - data[c + 0] += (byte)(amount * (0xFF - data[c + 0])); + for (int iy = top; iy <= bottom; iy++) + { + // update one of the color bits + // it is expected that a transparent pixel RGBA value is 0. + var c = 4 * (ix + (iy * width)); + data[c + 0] += (byte)(amount * (0xFF - data[c + 0])); + } } } } } - private static void CleanPollutedPixels(byte[] data, byte blue, byte green, byte red) + private static void CleanPollutedPixels(Span data, byte blue, byte green, byte red) { for (int i = 0; i < data.Length; i += 4) { diff --git a/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs b/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs index 2f162526a..b0a2aeea0 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs @@ -421,8 +421,8 @@ private void SetDetailsOT(ITrainerInfo tr) } // Copy OT trash bytes for sensitive games (Gen1/2) - if (tr is SAV1 s1 && Entity is PK1 p1) p1.OT_Trash = s1.OT_Trash; - else if (tr is SAV2 s2 && Entity is PK2 p2) p2.OT_Trash = s2.OT_Trash; + if (tr is SAV1 s1 && Entity is PK1 p1) s1.OT_Trash.CopyTo(p1.OT_Trash); + else if (tr is SAV2 s2 && Entity is PK2 p2) s2.OT_Trash.CopyTo(p2.OT_Trash); UpdateNickname(this, EventArgs.Empty); } @@ -974,9 +974,9 @@ private void Update255_MTB(object sender, EventArgs e) return; if (Util.ToInt32(tb.Text) > byte.MaxValue) tb.Text = "255"; - if (sender == TB_Friendship && int.TryParse(TB_Friendship.Text, out var val)) + if (sender == TB_Friendship && int.TryParse(TB_Friendship.Text, out var value)) { - UpdateFromFriendshipTextBox(Entity, val); + UpdateFromFriendshipTextBox(Entity, value); UpdateStats(); } } @@ -1368,26 +1368,29 @@ private void UpdateNicknameClick(object sender, MouseEventArgs e) if (tb == TB_Nickname) { Entity.Nickname = tb.Text; - var d = new TrashEditor(tb, Entity.Nickname_Trash, sav); + var span = Entity.Nickname_Trash; + var d = new TrashEditor(tb, span, sav); d.ShowDialog(); tb.Text = d.FinalString; - Entity.Nickname_Trash = d.FinalBytes; + d.FinalBytes.CopyTo(span); } else if (tb == TB_OT) { Entity.OT_Name = tb.Text; - var d = new TrashEditor(tb, Entity.OT_Trash, sav); + var span = Entity.OT_Trash; + var d = new TrashEditor(tb, span, sav); d.ShowDialog(); tb.Text = d.FinalString; - Entity.OT_Trash = d.FinalBytes; + d.FinalBytes.CopyTo(span); } else if (tb == TB_OTt2) { Entity.HT_Name = tb.Text; - var d = new TrashEditor(tb, Entity.HT_Trash, sav); + var span = Entity.HT_Trash; + var d = new TrashEditor(tb, span, sav); d.ShowDialog(); tb.Text = d.FinalString; - Entity.HT_Trash = d.FinalBytes; + d.FinalBytes.CopyTo(span); } } diff --git a/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs b/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs index 18caf79a9..432af171f 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs @@ -457,7 +457,7 @@ public void SavePartyStats(PKM pk) pk.Stat_SPD = Util.ToInt32(Stat_SPD.Text); } - public void LoadEVs(int[] EVs) + public void LoadEVs(ReadOnlySpan EVs) { ChangingFields = true; TB_EVHP.Text = EVs[0].ToString(); @@ -470,7 +470,7 @@ public void LoadEVs(int[] EVs) UpdateStats(); } - public void LoadIVs(int[] IVs) + public void LoadIVs(ReadOnlySpan IVs) { ChangingFields = true; TB_IVHP.Text = IVs[0].ToString(); diff --git a/PKHeX.WinForms/Controls/PKM Editor/TrainerID.cs b/PKHeX.WinForms/Controls/PKM Editor/TrainerID.cs index 2017fb417..42c54f5ab 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/TrainerID.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/TrainerID.cs @@ -116,39 +116,39 @@ private void Update_ID(object sender, EventArgs e) if (sender is not MaskedTextBox mt) return; - if (!int.TryParse(mt.Text, out var val)) - val = 0; + if (!int.TryParse(mt.Text, out var value)) + value = 0; if (mt == TB_TID7) { - if (val > 999_999) + if (value > 999_999) { mt.Text = "999999"; return; } if (!int.TryParse(TB_SID7.Text, out var sid)) sid = 0; - SanityCheckSID7(val, sid); + SanityCheckSID7(value, sid); } else if (mt == TB_SID7) { - if (val > 4294) // max 4 digits of 32bit int + if (value > 4294) // max 4 digits of 32bit int { mt.Text = "4294"; return; } if (!int.TryParse(TB_TID7.Text, out var tid)) tid = 0; - SanityCheckSID7(tid, val); + SanityCheckSID7(tid, value); } else { - if (val > ushort.MaxValue) // prior to gen7 - mt.Text = (val = ushort.MaxValue).ToString(); + if (value > ushort.MaxValue) // prior to gen7 + mt.Text = (value = ushort.MaxValue).ToString(); if (mt == TB_TID) - Trainer.TID = val; + Trainer.TID = value; else - Trainer.SID = val; + Trainer.SID = value; } UpdatedID?.Invoke(sender, e); diff --git a/PKHeX.WinForms/Controls/Slots/SlotList.cs b/PKHeX.WinForms/Controls/Slots/SlotList.cs index dfd2262bb..79b06a9c0 100644 --- a/PKHeX.WinForms/Controls/Slots/SlotList.cs +++ b/PKHeX.WinForms/Controls/Slots/SlotList.cs @@ -140,7 +140,7 @@ private void AddLabels() for (var i = 0; i < names.Length; i++) { var name = names[i]; - bool result = Enum.TryParse(name, out var val); + bool result = Enum.TryParse(name, out var value); if (!result) continue; @@ -148,7 +148,7 @@ private void AddLabels() { Name = $"L_{name}", Text = name, - Type = val, + Type = value, AutoSize = true, Visible = false, }; diff --git a/PKHeX.WinForms/Subforms/Misc/SortableBindingList.cs b/PKHeX.WinForms/Subforms/Misc/SortableBindingList.cs index 0c0d74afa..843a4f009 100644 --- a/PKHeX.WinForms/Subforms/Misc/SortableBindingList.cs +++ b/PKHeX.WinForms/Subforms/Misc/SortableBindingList.cs @@ -62,8 +62,8 @@ protected override int FindCore(PropertyDescriptor prop, object key) for (int i = 0; i < count; ++i) { var obj = this[i]; - var val = prop.GetValue(obj); - if (val?.Equals(key) == true) + var value = prop.GetValue(obj); + if (value?.Equals(key) == true) return i; } diff --git a/PKHeX.WinForms/Subforms/PKM Editors/Text.cs b/PKHeX.WinForms/Subforms/PKM Editors/Text.cs index f5be0a6a2..dfaafb902 100644 --- a/PKHeX.WinForms/Subforms/PKM Editors/Text.cs +++ b/PKHeX.WinForms/Subforms/PKM Editors/Text.cs @@ -173,8 +173,14 @@ private void B_ClearTrash_Click(object sender, EventArgs e) Bytes[i].Value = 0; } - private byte[] SetString(string text) => SAV.SetString(text, text.Length); - private string GetString() => SAV.GetString(Raw, 0, Raw.Length); + private byte[] SetString(string text) + { + Span temp = stackalloc byte[Raw.Length]; + var written = SAV.SetString(temp, text.AsSpan(), text.Length, StringConverterOption.None); + return temp[..written].ToArray(); + } + + private string GetString() => SAV.GetString(Raw.AsSpan()); // Helpers private static Label GetLabel(string str) => new() {Text = str, AutoSize = true}; diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs index c434f8bc0..166a4b9d9 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Windows.Forms; using PKHeX.Core; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.WinForms { @@ -292,14 +293,17 @@ private void StatAddrControl(int SetValToSav = -2, bool SetSavToVal = false) return; if (val > 9999) val = 9999; - BitConverter.GetBytes(val).CopyTo(SAV.Small, BFF[Facility][2 + SetValToSav] + (4 * BattleType) + (2 * RBi)); + var offset = BFF[Facility][2 + SetValToSav] + (4 * BattleType) + (2 * RBi); + WriteUInt32LittleEndian(SAV.Small.AsSpan(offset), val); return; } if (SetValToSav == -1) { int p = BFF[Facility][2 + BFV[BFF[Facility][0]].Length + BattleType] + RBi; const int offset = 0xCDC; - BitConverter.GetBytes((BitConverter.ToUInt32(SAV.Small, offset) & (uint)~(1 << p)) | (uint)((CHK_Continue.Checked ? 1 : 0) << p)).CopyTo(SAV.Small, offset); + var current = ReadUInt32LittleEndian(SAV.Small.AsSpan(offset)); + var update = (current & (uint)~(1 << p)) | (uint)((CHK_Continue.Checked ? 1 : 0) << p); + WriteUInt32LittleEndian(SAV.Small.AsSpan(offset), update); return; } if (!SetSavToVal) @@ -308,12 +312,15 @@ private void StatAddrControl(int SetValToSav = -2, bool SetSavToVal = false) editingval = true; for (int i = 0; i < BFV[BFF[Facility][0]].Length; i++) { - int vali = BitConverter.ToUInt16(SAV.Small, BFF[Facility][2 + i] + (4 * BattleType) + (2 * RBi)); + var offset = BFF[Facility][2 + i] + (4 * BattleType) + (2 * RBi); + int vali = ReadUInt16LittleEndian(SAV.Small.AsSpan(offset)); if (vali > 9999) vali = 9999; StatNUDA[BFV[BFF[Facility][0]][i]].Value = vali; } - CHK_Continue.Checked = (BitConverter.ToUInt32(SAV.Small, 0xCDC) & 1 << (BFF[Facility][2 + BFV[BFF[Facility][0]].Length + BattleType] + RBi)) != 0; + + var shift = (BFF[Facility][2 + BFV[BFF[Facility][0]].Length + BattleType] + RBi); + CHK_Continue.Checked = (ReadUInt32LittleEndian(SAV.Small.AsSpan(0xCDC)) & (1 << shift)) != 0; editingval = false; } @@ -443,10 +450,10 @@ private void LoadRecords() return; var index = WinFormsUtil.GetIndex(CB_Record); - var val = (uint) NUD_RecordValue.Value; - records.SetRecord(index, val); + var value = (uint) NUD_RecordValue.Value; + records.SetRecord(index, value); if (index == 1) - LoadFame(val); + LoadFame(value); }; if (SAV is SAV3E em) diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen4/SAV_Misc4.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen4/SAV_Misc4.cs index 3907814c9..d95f9240f 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen4/SAV_Misc4.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen4/SAV_Misc4.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Windows.Forms; using PKHeX.Core; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.WinForms { @@ -125,7 +126,7 @@ private void ReadMain() break; default: return; } - uint valFly = BitConverter.ToUInt32(SAV.General, ofsFly); + uint valFly = ReadUInt32LittleEndian(SAV.General.AsSpan(ofsFly)); CLB_FlyDest.Items.Clear(); for (int i = 0; i < FlyDestD.Length; i++) { @@ -136,7 +137,7 @@ private void ReadMain() : (SAV.General[ofsFly + (FlyDestC[i] >> 3)] & 1 << (FlyDestC[i] & 7)) != 0; CLB_FlyDest.Items.Add(name, state); } - uint valBP = BitConverter.ToUInt16(SAV.General, ofsBP); + uint valBP = ReadUInt16LittleEndian(SAV.General.AsSpan(ofsBP)); NUD_BP.Value = valBP > 9999 ? 9999 : valBP; if (SAV is SAV4Sinnoh sinnoh) @@ -146,7 +147,7 @@ private void ReadMain() if (ofsUGFlagCount > 0) { - uint fc = BitConverter.ToUInt32(SAV.General, ofsUGFlagCount) & 0xFFFFF; + uint fc = ReadUInt32LittleEndian(SAV.General.AsSpan(ofsUGFlagCount)) & 0xFFFFF; NUD_UGFlags.Value = fc > 999999 ? 999999 : fc; } if (ofsMap > 0) @@ -162,7 +163,7 @@ private void ReadMain() private void SaveMain() { SAV.Coin = (uint)NUD_Coin.Value; - uint valFly = BitConverter.ToUInt32(SAV.General, ofsFly); + uint valFly = ReadUInt32LittleEndian(SAV.General.AsSpan(ofsFly)); for (int i = 0; i < CLB_FlyDest.Items.Count; i++) { if (FlyDestC[i] < 32) @@ -178,8 +179,8 @@ private void SaveMain() SAV.General[o] = (byte)((SAV.General[o] & ~(1 << (FlyDestC[i] & 7))) | (CLB_FlyDest.GetItemChecked(i) ? 1 << (FlyDestC[i] & 7) : 0)); } } - BitConverter.GetBytes(valFly).CopyTo(SAV.General, ofsFly); - BitConverter.GetBytes((ushort)NUD_BP.Value).CopyTo(SAV.General, ofsBP); + WriteUInt32LittleEndian(SAV.General.AsSpan(ofsFly), valFly); + WriteUInt16LittleEndian(SAV.General.AsSpan(ofsBP), (ushort)NUD_BP.Value); if (SAV is SAV4Sinnoh sinnoh) SavePoketch(sinnoh); @@ -187,7 +188,11 @@ private void SaveMain() SaveWalker(hgss); if (ofsUGFlagCount > 0) - BitConverter.GetBytes((BitConverter.ToUInt32(SAV.General, ofsUGFlagCount) & ~0xFFFFFu) | (uint)NUD_UGFlags.Value).CopyTo(SAV.General, ofsUGFlagCount); + { + var current = ReadUInt32LittleEndian(SAV.General.AsSpan(ofsUGFlagCount)) & ~0xFFFFFu; + var update = current | (uint)NUD_UGFlags.Value; + WriteUInt32LittleEndian(SAV.General.AsSpan(ofsUGFlagCount), update); + } if (ofsMap > 0) { int valMap = CB_UpgradeMap.SelectedIndex; @@ -216,8 +221,8 @@ private void ReadPoketch(SAV4Sinnoh s) for (int i = 0; i < PoketchTitle.Length; i++) { var title = $"{i:00} - {PoketchTitle[i]}"; - var val = s.GetPoketchAppUnlocked((PoketchApp)i); - CLB_Poketch.Items.Add(title, val); + var value = s.GetPoketchAppUnlocked((PoketchApp)i); + CLB_Poketch.Items.Add(title, value); } DotArtistByte = s.GetPoketchDotArtistData(); @@ -283,9 +288,9 @@ private void SetFlagsFromFileName(string inpFileName) if (bmp.Width != 24 || bmp.Height != 20) return; - byte[] BrightMap = new byte[480]; - byte[] BrightCount = new byte[0x100]; - byte[] iBrightCount = new byte[0x100]; + Span BrightMap = stackalloc byte[480]; + Span BrightCount = stackalloc byte[0x100]; + Span iBrightCount = stackalloc byte[0x100]; for (int iy = 0; iy < 20; iy++) { for (int ix = 0; ix < 24; ix++) @@ -296,18 +301,24 @@ private void SetFlagsFromFileName(string inpFileName) } } - int ColorCount = BrightCount.Count(v => v > 0); + int ColorCount = 0; + foreach (var value in BrightCount) + { + if (value > 0) + ++ColorCount; + } + if (ColorCount is 0 or > 4) return; int errmin = int.MaxValue; - byte[] LCT = new byte[4]; - byte[] mLCT = new byte[4]; + Span LCT = stackalloc byte[4]; + Span mLCT = stackalloc byte[4]; for (int i = 0; i < 4; i++) LCT[i] = (byte)(ColorCount < i + 1 ? 4 : ColorCount - i - 1); int ee = 0; while (++ee < 1000) { - BrightCount.CopyTo(iBrightCount, 0); + BrightCount.CopyTo(iBrightCount); for (int i = 0, j = 0; i < 0x100; i++) { if (iBrightCount[i] > 0) @@ -320,10 +331,11 @@ private void SetFlagsFromFileName(string inpFileName) if (errmin > errtot) { errmin = errtot; - LCT.CopyTo(mLCT, 0); + LCT.CopyTo(mLCT); } - LCT = GetNextLCT(LCT); - if (LCT[0] >= 4) break; + GetNextLCT(LCT); + if (LCT[0] >= 4) + break; } for (int i = 0, j = 0; i < 0x100; i++) { @@ -334,14 +346,14 @@ private void SetFlagsFromFileName(string inpFileName) for (int i = 0; i < 480; i++) BrightMap[i] = BrightCount[BrightMap[i]]; - byte[] ndab = new byte[120]; + Span ndab = stackalloc byte[120]; for (int i = 0; i < 480; i++) ndab[i >> 2] |= (byte)((BrightMap[i] & 3) << (i % 4 << 1)); - ndab.CopyTo(DotArtistByte, 0); + ndab.CopyTo(DotArtistByte.AsSpan(0)); } - private static byte[] GetNextLCT(byte[] inp) + private static void GetNextLCT(Span inp) { while (true) { @@ -361,7 +373,7 @@ private static byte[] GetNextLCT(byte[] inp) continue; inp[0] = 4; - return inp; + return; } } @@ -462,7 +474,7 @@ private void ReadBattleFrontier() PrintButtonA = new[] { BTN_PrintTower, BTN_PrintFactory, BTN_PrintHall, BTN_PrintCastle, BTN_PrintArcade }; Prints = new int[PrintButtonA.Length]; for (int i = 0; i < Prints.Length; i++) - Prints[i] = 1 + Math.Sign((BitConverter.ToUInt16(SAV.General, ofsPrints + (i << 1)) >> 1) - 1); + Prints[i] = 1 + Math.Sign((ReadUInt16LittleEndian(SAV.General.AsSpan(ofsPrints + (i << 1))) >> 1) - 1); SetPrints(); HallNUDA = new[] { @@ -480,13 +492,16 @@ private void ReadBattleFrontier() bool f = false; for (int i = 0; i < 2; i++, ofsHallStat += 0x14) { - var h = BitConverter.ToInt32(SAV.General, ofsHallStat); + var h = ReadInt32LittleEndian(SAV.General.AsSpan(ofsHallStat)); if (h == -1) continue; for (int j = 0; j < 0x20; j++) { for (int k = 0, a = j + 0x20 << 12; k < 2; k++, a += 0x40000) { - if (h != BitConverter.ToInt32(SAV.Data, a) || BitConverter.ToInt16(SAV.Data, a + 0xBA8) != 0xBA0) + var span = SAV.Data.AsSpan(a); + if (h != ReadInt32LittleEndian(span)) + continue; + if (ReadInt16LittleEndian(span[0xBA8..]) != 0xBA0) continue; f = true; @@ -529,13 +544,19 @@ private void SaveBattleFrontier() { for (int i = 0; i < Prints.Length; i++) { - if (Prints[i] == 1 + Math.Sign((BitConverter.ToUInt16(SAV.General, ofsPrints + (i << 1)) >> 1) - 1)) continue; - BitConverter.GetBytes(Prints[i] << 1).CopyTo(SAV.General, ofsPrints + (i << 1)); + if (Prints[i] == 1 + Math.Sign((ReadUInt16LittleEndian(SAV.General.AsSpan(ofsPrints + (i << 1))) >> 1) - 1)) + continue; + var value = Prints[i] << 1; + WriteInt32LittleEndian(SAV.General.AsSpan(ofsPrints + (i << 1)), value); } } if (HallStatUpdated) - BitConverter.GetBytes(Checksums.CRC16_CCITT(new ReadOnlySpan(SAV.Data, ofsHallStat, 0xBAE))).CopyTo(SAV.Data, ofsHallStat + 0xBAE); + { + var span = new ReadOnlySpan(SAV.Data, ofsHallStat, 0xBAE); + var chk = Checksums.CRC16_CCITT(span); + WriteUInt16LittleEndian(SAV.Data.AsSpan(ofsHallStat + 0xBAE), chk); + } } private void SetPrints() @@ -630,13 +651,13 @@ private void StatAddrControl(int SetValToSav = -2, bool SetSavToVal = false) for (int i = 0; i < BFV[BFF[Facility][0]].Length; i++) { if (BFV[BFF[Facility][0]][i] < 0) continue; - int vali = BitConverter.ToUInt16(SAV.General, addrVal + (i << 1)); + int vali = ReadUInt16LittleEndian(SAV.General.AsSpan(addrVal + (i << 1))); StatNUDA[BFV[BFF[Facility][0]][i]].Value = vali > 9999 ? 9999 : vali; } CHK_Continue.Checked = (SAV.General[addrFlag] & maskFlag) != 0; if (Facility == 0) // tower continue count - StatNUDA[1].Value = BitConverter.ToUInt16(SAV.General, addrFlag + TowerContinueCountOfs + (BattleType << 1)); + StatNUDA[1].Value = ReadUInt16LittleEndian(SAV.General.AsSpan(addrFlag + TowerContinueCountOfs + (BattleType << 1))); editing = false; return; @@ -646,12 +667,16 @@ private void StatAddrControl(int SetValToSav = -2, bool SetSavToVal = false) ushort val = (ushort)StatNUDA[SetValToSav].Value; if (Facility == 0 && SetValToSav == 1) // tower continue count - BitConverter.GetBytes(val).CopyTo(SAV.General, addrFlag + TowerContinueCountOfs + (BattleType << 1)); + { + var offset = addrFlag + TowerContinueCountOfs + (BattleType << 1); + WriteUInt16LittleEndian(SAV.General.AsSpan(offset), val); + } SetValToSav = Array.IndexOf(BFV[BFF[Facility][0]], SetValToSav); if (SetValToSav < 0) return; - BitConverter.GetBytes((ushort)(val > 9999 ? 9999 : val)).CopyTo(SAV.General, addrVal + (SetValToSav << 1)); + var clamp = Math.Min((ushort)9999, val); + WriteUInt16LittleEndian(SAV.General.AsSpan(addrVal + (SetValToSav << 1)), clamp); return; } if (SetValToSav == -1) @@ -718,7 +743,7 @@ private void GetCastleStat() NumericUpDown[] na = { NUD_CastleRankRcv, NUD_CastleRankItem, NUD_CastleRankInfo }; for (int i = 0; i < na.Length; i++) { - int val = BitConverter.ToInt16(SAV.General, ofs + (i << 1)); + int val = ReadInt16LittleEndian(SAV.General.AsSpan(ofs + (i << 1))); na[i].Value = val > na[i].Maximum ? na[i].Maximum : val < na[i].Minimum ? na[i].Minimum : val; } } @@ -731,13 +756,14 @@ private void NUD_CastleRank_ValueChanged(object sender, EventArgs e) int i = Array.IndexOf(na, sender); if (i < 0) return; - BitConverter.GetBytes((int)na[i].Value).CopyTo(SAV.General, BFF[3][2] + (BFF[3][3] * CB_Stats2.SelectedIndex) + 0x0A + (i << 1)); + var offset = BFF[3][2] + (BFF[3][3] * CB_Stats2.SelectedIndex) + 0x0A + (i << 1); + WriteInt32LittleEndian(SAV.General.AsSpan(offset), (int)na[i].Value); } private void GetHallStat() { int ofscur = BFF[2][2] + (BFF[2][3] * CB_Stats2.SelectedIndex); - int curspe = BitConverter.ToInt16(SAV.General, ofscur + 4); + int curspe = ReadInt16LittleEndian(SAV.General.AsSpan(ofscur + 4)); bool c = curspe == species; CHK_HallCurrent.Checked = c; CHK_HallCurrent.Text = curspe > 0 && curspe <= SAV.MaxSpeciesID @@ -756,15 +782,19 @@ private void GetHallStat() if (ofsHallStat > 0) { - ushort v = BitConverter.ToUInt16(SAV.Data, ofsHallStat + 4 + (0x3DE * CB_Stats2.SelectedIndex) + (species << 1)); + var offset = ofsHallStat + 4 + (0x3DE * CB_Stats2.SelectedIndex) + (species << 1); + ushort v = ReadUInt16LittleEndian(SAV.General.AsSpan(offset)); NUD_HallStreaks.Value = v > 9999 ? 9999 : v; } } private void CHK_HallCurrent_CheckedChanged(object sender, EventArgs e) { - if (editing) return; - BitConverter.GetBytes((ushort)(CHK_HallCurrent.Checked ? species : 0)).CopyTo(SAV.General, BFF[2][2] + (BFF[2][3] * CB_Stats2.SelectedIndex) + 4); + if (editing) + return; + var offset = BFF[2][2] + (BFF[2][3] * CB_Stats2.SelectedIndex) + 4; + ushort value = (ushort)(CHK_HallCurrent.Checked ? species : 0); + WriteUInt16LittleEndian(SAV.General.AsSpan(offset), value); editing = true; GetHallStat(); editing = false; @@ -784,7 +814,8 @@ private void NUD_HallStreaks_ValueChanged(object sender, EventArgs e) { if (editing || ofsHallStat < 0) return; - BitConverter.GetBytes((ushort)NUD_HallStreaks.Value).CopyTo(SAV.Data, ofsHallStat + 4 + (0x3DE * CB_Stats2.SelectedIndex) + (species << 1)); + var offset = ofsHallStat + 4 + (0x3DE * CB_Stats2.SelectedIndex) + (species << 1); + WriteUInt16LittleEndian(SAV.General.AsSpan(offset), (ushort)NUD_HallStreaks.Value); HallStatUpdated = true; } #endregion diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen4/SAV_Underground.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen4/SAV_Underground.cs index d3b66822f..8886923c1 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen4/SAV_Underground.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen4/SAV_Underground.cs @@ -104,10 +104,10 @@ private void ReadUGData() private void SaveUGData() { - byte[] goodsList = new byte[SAV.GetUGI_Goods().Length]; - byte[] spheresList = new byte[SAV.GetUGI_Spheres().Length]; - byte[] trapsList = new byte[SAV.GetUGI_Traps().Length]; - byte[] treasuresList = new byte[SAV.GetUGI_Treasures().Length]; + Span goodsList = stackalloc byte[SAV.GetUGI_Goods().Length]; + Span spheresList = stackalloc byte[SAV.GetUGI_Spheres().Length]; + Span trapsList = stackalloc byte[SAV.GetUGI_Traps().Length]; + Span treasuresList = stackalloc byte[SAV.GetUGI_Treasures().Length]; // Goods int ctr = 0; diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs index 2fe48f061..eade8ccbf 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs @@ -4,6 +4,7 @@ using System.Windows.Forms; using PKHeX.Core; using PKHeX.Drawing.PokeSprite; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.WinForms { @@ -106,7 +107,7 @@ private void ReadMain() default: throw new ArgumentOutOfRangeException(nameof(SAV.Version)); } - uint valFly = BitConverter.ToUInt32(SAV.Data, ofsFly); + uint valFly = ReadUInt32LittleEndian(SAV.Data.AsSpan(ofsFly)); CLB_FlyDest.Items.Clear(); CLB_FlyDest.Items.AddRange(FlyDestA); for (int i = 0; i < CLB_FlyDest.Items.Count; i++) @@ -142,7 +143,7 @@ private void ReadMain() // LibertyPass valLibPass = keyLibPass ^ (uint)(SAV.SID << 16 | SAV.TID); - bLibPass = BitConverter.ToUInt32(SAV.Data, ofsLibPass) == valLibPass; + bLibPass = ReadUInt32LittleEndian(SAV.Data.AsSpan(ofsLibPass)) == valLibPass; CHK_LibertyPass.Checked = bLibPass; } else if (SAV is SAV5B2W2) @@ -155,14 +156,14 @@ private void ReadMain() "Unlock EasyMode", "Unlock ChallengeMode", "Unlock City", "Unlock IronChamber", "Unlock IcebergChamber", }; - uint KSID = BitConverter.ToUInt32(SAV.Data, ofsKS + 0x34); + uint KSID = ReadUInt32LittleEndian(SAV.Data.AsSpan(ofsKS + 0x34)); valKS = new uint[keyKS.Length]; bKS = new bool[keyKS.Length]; CLB_KeySystem.Items.Clear(); for (int i = 0; i < valKS.Length; i++) { valKS[i] = keyKS[i] ^ KSID; - bKS[i] = BitConverter.ToUInt32(SAV.Data, ofsKS + (i << 2)) == valKS[i]; + bKS[i] = ReadUInt32LittleEndian(SAV.Data.AsSpan(ofsKS + (i << 2))) == valKS[i]; CLB_KeySystem.Items.Add(KeySystemA[i], bKS[i]); } } @@ -182,7 +183,7 @@ private static List GetStates() => new() private void SaveMain() { - uint valFly = BitConverter.ToUInt32(SAV.Data, ofsFly); + uint valFly = ReadUInt32LittleEndian(SAV.Data.AsSpan(ofsFly)); for (int i = 0; i < CLB_FlyDest.Items.Count; i++) { if (FlyDestC[i] < 32) @@ -198,7 +199,7 @@ private void SaveMain() SAV.Data[ofs] = (byte)((SAV.Data[ofs] & ~(1 << (FlyDestC[i] & 7))) | ((CLB_FlyDest.GetItemChecked(i) ? 1 : 0) << (FlyDestC[i] & 7))); } } - BitConverter.GetBytes(valFly).CopyTo(SAV.Data, ofsFly); + WriteUInt32LittleEndian(SAV.Data.AsSpan(ofsFly), valFly); if (SAV is SAV5BW) { @@ -213,21 +214,24 @@ private void SaveMain() SAV.Data[ofsRoamer + 0x2E + i] = (byte)d; if (c != 1) continue; - new byte[14].CopyTo(SAV.Data, ofsRoamer + 4 + (i * 0x14)); + SAV.Data.AsSpan(ofsRoamer + 4 + (i * 0x14), 14).Clear(); SAV.Data[ofsRoamer + 0x2C + i] = 0; } // LibertyPass - if (CHK_LibertyPass.Checked ^ bLibPass) - BitConverter.GetBytes(bLibPass ? 0 : valLibPass).CopyTo(SAV.Data, ofsLibPass); + if (CHK_LibertyPass.Checked != bLibPass) + WriteUInt32LittleEndian(SAV.Data.AsSpan(ofsLibPass), bLibPass ? 0u : valLibPass); } else if (SAV is SAV5B2W2) { // KeySystem for (int i = 0; i < CLB_KeySystem.Items.Count; i++) { - if (CLB_KeySystem.GetItemChecked(i) ^ bKS[i]) - BitConverter.GetBytes(bKS[i] ? 0 : valKS[i]).CopyTo(SAV.Data, ofsKS + (i << 2)); + if (CLB_KeySystem.GetItemChecked(i) == bKS[i]) + continue; + var dest = SAV.Data.AsSpan(ofsKS + (i << 2)); + var value = bKS[i] ? 0u : valKS[i]; + WriteUInt32LittleEndian(dest, value); } } } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_BerryFieldXY.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_BerryFieldXY.cs index 57b9846dc..0478708d4 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_BerryFieldXY.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_BerryFieldXY.cs @@ -1,6 +1,7 @@ using System; using System.Windows.Forms; using PKHeX.Core; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.WinForms { @@ -22,14 +23,14 @@ private void Changefield(object sender, EventArgs e) // Gather Data int ofs = SAV.BerryField + 0xC + (listBox1.SelectedIndex * 0x18); - int berry = BitConverter.ToUInt16(SAV.Data, ofs + (2 * 0)); - int u1 = BitConverter.ToUInt16(SAV.Data, ofs + (2 * 1)); - int u2 = BitConverter.ToUInt16(SAV.Data, ofs + (2 * 2)); - int u3 = BitConverter.ToUInt16(SAV.Data, ofs + (2 * 3)); - int u4 = BitConverter.ToUInt16(SAV.Data, ofs + (2 * 4)); - int u5 = BitConverter.ToUInt16(SAV.Data, ofs + (2 * 5)); - int u6 = BitConverter.ToUInt16(SAV.Data, ofs + (2 * 6)); - int u7 = BitConverter.ToUInt16(SAV.Data, ofs + (2 * 7)); + 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))); + int u3 = ReadUInt16LittleEndian(SAV.Data.AsSpan(ofs + (2 * 3))); + int u4 = ReadUInt16LittleEndian(SAV.Data.AsSpan(ofs + (2 * 4))); + int u5 = ReadUInt16LittleEndian(SAV.Data.AsSpan(ofs + (2 * 5))); + int u6 = ReadUInt16LittleEndian(SAV.Data.AsSpan(ofs + (2 * 6))); + int u7 = ReadUInt16LittleEndian(SAV.Data.AsSpan(ofs + (2 * 7))); // Display Data TB_Berry.Text = berry.ToString(); diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.cs index 8fe8e3c69..f523a5104 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.cs @@ -5,6 +5,7 @@ using System.Windows.Forms; using PKHeX.Core; using PKHeX.Drawing.PokeSprite; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.WinForms { @@ -76,7 +77,7 @@ private void DisplayEntry(object sender, EventArgs e) int index = LB_DataEntry.SelectedIndex; int offset = index * 0x1B4; - uint vnd = BitConverter.ToUInt32(data, offset + 0x1B0); + uint vnd = ReadUInt32LittleEndian(data.AsSpan(offset + 0x1B0)); uint vn = vnd & 0xFF; TB_VN.Text = vn.ToString("000"); var s = new List {$"Entry #{vn}"}; @@ -121,7 +122,7 @@ private int AddEntries(int offset, List s, uint year, uint month, uint d int moncount = 0; for (int i = 0; i < 6; i++) { - int species = BitConverter.ToUInt16(data, offset + 0x00); + int species = ReadUInt16LittleEndian(data.AsSpan(offset + 0x00)); if (species == 0) continue; @@ -136,24 +137,24 @@ private int AddEntries(int offset, List s, uint year, uint month, uint d private void AddEntryDescription(int offset, List s, int species) { - int helditem = BitConverter.ToUInt16(data, offset + 0x02); - int move1 = BitConverter.ToUInt16(data, offset + 0x04); - int move2 = BitConverter.ToUInt16(data, offset + 0x06); - int move3 = BitConverter.ToUInt16(data, offset + 0x08); - int move4 = BitConverter.ToUInt16(data, offset + 0x0A); + int helditem = ReadUInt16LittleEndian(data.AsSpan(offset + 0x02)); + int move1 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x04)); + int move2 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x06)); + int move3 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x08)); + int move4 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x0A)); - int TID = BitConverter.ToUInt16(data, offset + 0x10); - int SID = BitConverter.ToUInt16(data, offset + 0x12); + int TID = ReadUInt16LittleEndian(data.AsSpan(offset + 0x10)); + int SID = ReadUInt16LittleEndian(data.AsSpan(offset + 0x12)); - uint slgf = BitConverter.ToUInt32(data, offset + 0x14); + uint slgf = ReadUInt32LittleEndian(data.AsSpan(offset + 0x14)); // uint form = slgf & 0x1F; uint gender = slgf >> 5 & 3; // 0 M; 1 F; 2 G uint level = slgf >> 7 & 0x7F; uint shiny = slgf >> 14 & 0x1; // uint unkn = slgf >> 15; - string nickname = StringConverter.GetString6(data, offset + 0x18, 24); - string OTname = StringConverter.GetString6(data, offset + 0x30, 24); + string nickname = StringConverter6.GetString(data.AsSpan(offset + 0x18, 26)); + string OTname = StringConverter6.GetString(data.AsSpan(offset + 0x30, 26)); string genderstr = gendersymbols[(int)gender]; string shinystr = shiny == 1 ? "Yes" : "No"; @@ -181,34 +182,34 @@ private void NUP_PartyIndex_ValueChanged(object sender, EventArgs e) if (offset < 0) return; - int species = BitConverter.ToUInt16(data, offset + 0x00); + int species = ReadUInt16LittleEndian(data.AsSpan(offset + 0x00)); CB_Species.SelectedValue = species; - int item = BitConverter.ToUInt16(data, offset + 0x02); + int item = ReadUInt16LittleEndian(data.AsSpan(offset + 0x02)); CB_HeldItem.SelectedValue = item; - int move1 = BitConverter.ToUInt16(data, offset + 0x04); - int move2 = BitConverter.ToUInt16(data, offset + 0x06); - int move3 = BitConverter.ToUInt16(data, offset + 0x08); - int move4 = BitConverter.ToUInt16(data, offset + 0x0A); + int move1 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x04)); + int move2 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x06)); + int move3 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x08)); + int move4 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x0A)); CB_Move1.SelectedValue = move1; CB_Move2.SelectedValue = move2; CB_Move3.SelectedValue = move3; CB_Move4.SelectedValue = move4; - uint EC = BitConverter.ToUInt32(data, offset + 0xC); + uint EC = ReadUInt32LittleEndian(data.AsSpan(offset + 0xC)); TB_EC.Text = EC.ToString("X8"); - TB_TID.Text = BitConverter.ToUInt16(data, offset + 0x10).ToString("00000"); - TB_SID.Text = BitConverter.ToUInt16(data, offset + 0x12).ToString("00000"); + TB_TID.Text = ReadUInt16LittleEndian(data.AsSpan(offset + 0x10)).ToString("00000"); + TB_SID.Text = ReadUInt16LittleEndian(data.AsSpan(offset + 0x12)).ToString("00000"); - TB_Nickname.Text = StringConverter.GetString6(data, offset + 0x18, 24); - TB_OT.Text = StringConverter.GetString6(data, offset + 0x30, 24); + TB_Nickname.Text = StringConverter6.GetString(data.AsSpan(offset + 0x18, 26)); + TB_OT.Text = StringConverter6.GetString(data.AsSpan(offset + 0x30, 26)); - uint slgf = BitConverter.ToUInt32(data, offset + 0x14); + uint slgf = ReadUInt32LittleEndian(data.AsSpan(offset + 0x14)); uint form = slgf & 0x1F; uint gender = slgf >> 5 & 3; // 0 M; 1 F; 2 G uint level = slgf >> 7 & 0x7F; uint shiny = slgf >> 14 & 0x1; - uint nick = BitConverter.ToUInt16(data,offset+0x16); + uint nick = ReadUInt16LittleEndian(data.AsSpan(offset + 0x16)); CHK_Shiny.Checked = shiny == 1; @@ -234,19 +235,18 @@ private void Write_Entry(object sender, EventArgs e) int index = LB_DataEntry.SelectedIndex; int partymember = Convert.ToInt32(NUP_PartyIndex.Value) - 1; int offset = (index * 0x1B4) + (partymember * 0x48); + var span = data.AsSpan(offset); + WriteUInt16LittleEndian(span, Convert.ToUInt16(CB_Species.SelectedValue)); + WriteUInt16LittleEndian(span[0x02..], Convert.ToUInt16(CB_HeldItem.SelectedValue)); + WriteUInt16LittleEndian(span[0x04..], Convert.ToUInt16(CB_Move1.SelectedValue)); + WriteUInt16LittleEndian(span[0x06..], Convert.ToUInt16(CB_Move2.SelectedValue)); + WriteUInt16LittleEndian(span[0x08..], Convert.ToUInt16(CB_Move3.SelectedValue)); + WriteUInt16LittleEndian(span[0x0A..], Convert.ToUInt16(CB_Move4.SelectedValue)); + WriteUInt32LittleEndian(span[0x0C..], Util.GetHexValue(TB_EC.Text)); + WriteUInt16LittleEndian(span[0x10..], Convert.ToUInt16(TB_TID.Text)); + WriteUInt16LittleEndian(span[0x12..], Convert.ToUInt16(TB_SID.Text)); - BitConverter.GetBytes(Convert.ToUInt16(CB_Species.SelectedValue)).CopyTo(data, offset + 0x00); - BitConverter.GetBytes(Convert.ToUInt16(CB_HeldItem.SelectedValue)).CopyTo(data, offset + 0x02); - BitConverter.GetBytes(Convert.ToUInt16(CB_Move1.SelectedValue)).CopyTo(data, offset + 0x04); - BitConverter.GetBytes(Convert.ToUInt16(CB_Move2.SelectedValue)).CopyTo(data, offset + 0x06); - BitConverter.GetBytes(Convert.ToUInt16(CB_Move3.SelectedValue)).CopyTo(data, offset + 0x08); - BitConverter.GetBytes(Convert.ToUInt16(CB_Move4.SelectedValue)).CopyTo(data, offset + 0x0A); - BitConverter.GetBytes(Util.GetHexValue(TB_EC.Text)).CopyTo(data, offset + 0x0C); - - BitConverter.GetBytes(Convert.ToUInt16(TB_TID.Text)).CopyTo(data, offset + 0x10); - BitConverter.GetBytes(Convert.ToUInt16(TB_SID.Text)).CopyTo(data, offset + 0x12); - - uint rawslgf = BitConverter.ToUInt32(data, offset + 0x14); + uint rawslgf = ReadUInt32LittleEndian(span[14..]); uint slgf = 0; slgf |= (uint)(CB_Form.SelectedIndex & 0x1F); slgf |= (uint)((PKX.GetGenderFromString(Label_Gender.Text) & 0x3) << 5); @@ -255,18 +255,23 @@ private void Write_Entry(object sender, EventArgs e) slgf |= 1 << 14; slgf |= rawslgf & 0x8000; - Array.Copy(BitConverter.GetBytes(slgf), 0, data, offset + 0x14, 2); + WriteUInt16LittleEndian(span[14..], (ushort)slgf); uint nick = 0; if (CHK_Nicknamed.Checked) nick = 1; - Array.Copy(BitConverter.GetBytes(nick), 0, data, offset + 0x16, 2); + WriteUInt16LittleEndian(span[16..], (ushort)nick); //Mimic in-game behavior of not clearing strings. It's awful, but accuracy > good. - string pk = TB_Nickname.Text; if (pk.Length != 12) pk = pk.PadRight(pk.Length + 1, '\0'); - string ot = TB_OT.Text; if (ot.Length != 12) ot = ot.PadRight(pk.Length + 1, '\0'); - Encoding.Unicode.GetBytes(pk).CopyTo(data, offset + 0x18); - Encoding.Unicode.GetBytes(ot).CopyTo(data, offset + 0x30); + string pk = TB_Nickname.Text; + if (pk.Length != 12) + pk = pk.PadRight(pk.Length + 1, '\0'); + string ot = TB_OT.Text; + if (ot.Length != 12) + ot = ot.PadRight(pk.Length + 1, '\0'); + + Encoding.Unicode.GetBytes(pk).CopyTo(span[0x18..]); + Encoding.Unicode.GetBytes(ot).CopyTo(span[0x30..]); offset = index * 0x1B4; @@ -278,9 +283,9 @@ private void Write_Entry(object sender, EventArgs e) date |= (uint)((CAL_MetDate.Value.Day & 0x1F) << 12); vnd |= (date & 0x1FFFF) << 14; //Fix for top bit - uint rawvnd = BitConverter.ToUInt32(data, offset + 0x1B0); + uint rawvnd = ReadUInt32LittleEndian(data.AsSpan(offset + 0x1B0)); vnd |= rawvnd & 0x80000000; - Array.Copy(BitConverter.GetBytes(vnd), 0, data, offset + 0x1B0, 4); + WriteUInt32LittleEndian(data.AsSpan(offset + 0x1B0), vnd); var species = WinFormsUtil.GetIndex(CB_Species); var form = CB_Form.SelectedIndex & 0x1F; @@ -419,18 +424,15 @@ private void ChangeNickname(object sender, MouseEventArgs e) return; int offset = (LB_DataEntry.SelectedIndex * 0x1B4) + ((Convert.ToInt32(NUP_PartyIndex.Value) - 1) * 0x48); - var nicktrash = data.AsSpan(offset + 0x18, 24); + var nicktrash = data.AsSpan(offset + 0x18, 26); var text = TB_Nickname.Text; - ReadOnlySpan minTrash = SAV.SetString(text, Math.Min(text.Length, nicktrash.Length)); - if (minTrash.Length > nicktrash.Length) - minTrash = minTrash[..nicktrash.Length]; - minTrash.CopyTo(nicktrash); + SAV.SetString(nicktrash, text.AsSpan(), 12, StringConverterOption.ClearZero); var d = new TrashEditor(tb, nicktrash, SAV); d.ShowDialog(); tb.Text = d.FinalString; - d.FinalBytes.CopyTo(data, offset + 0x18); + d.FinalBytes.CopyTo(nicktrash); - TB_Nickname.Text = StringConverter.GetString6(data, offset + 0x18, 24); + TB_Nickname.Text = StringConverter6.GetString(nicktrash); } } } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_PokeBlockORAS.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_PokeBlockORAS.cs index 9169df576..0bdd87c77 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_PokeBlockORAS.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_PokeBlockORAS.cs @@ -1,6 +1,7 @@ using System; using System.Windows.Forms; using PKHeX.Core; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.WinForms { @@ -17,10 +18,11 @@ public SAV_PokeBlockORAS(SaveFile sav) nup_spec = new[] { NUP_Red, NUP_Blue, NUP_Pink, NUP_Green, NUP_Yellow, NUP_Rainbow, NUP_RedPlus, NUP_BluePlus, NUP_PinkPlus, NUP_GreenPlus, NUP_YellowPlus, NUP_RainbowPlus }; Label[] lbl_spec = { L_Red, L_Blue, L_Pink, L_Green, L_Yellow, L_Rainbow, L_RedPlus, L_BluePlus, L_PinkPlus, L_GreenPlus, L_YellowPlus, L_RainbowPlus }; + var span = SAV.Data.AsSpan(SAV6AO.Contest); for (int i = 0; i < lbl_spec.Length; i++) { lbl_spec[i].Text = $"{GameInfo.Strings.pokeblocks[94 + i]}:"; - nup_spec[i].Value = BitConverter.ToUInt32(SAV.Data, SAV6AO.Contest + (i * 4)); + nup_spec[i].Value = ReadUInt32LittleEndian(span[(i * 4)..]); } } @@ -33,8 +35,9 @@ private void B_Cancel_Click(object sender, EventArgs e) private void B_Save_Click(object sender, EventArgs e) { + var span = SAV.Data.AsSpan(SAV6AO.Contest); for (int i = 0; i < nup_spec.Length; i++) - BitConverter.GetBytes((uint)nup_spec[i].Value).CopyTo(SAV.Data, SAV6AO.Contest + (i * 4)); + WriteUInt32LittleEndian(span[(i*4)..], (uint)nup_spec[i].Value); Origin.CopyChangesFrom(SAV); Close(); } @@ -45,14 +48,18 @@ private void B_RandomizeBerries_Click(object sender, EventArgs e) return; // Randomize the trees. - byte[] tree = { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x40, 0x01, 0x00, 0x00, 0x00 }; + Span tree = stackalloc byte[] { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x40, 0x01, 0x00, 0x00, 0x00 }; var plantable = Legal.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 - BitConverter.GetBytes(berry).CopyTo(tree, 6); // put berry into tree. - tree.CopyTo(SAV.Data, SAV.BerryField + (0x10 * i)); // put tree into plot + WriteUInt16LittleEndian(plot[6..], berry); // put berry into tree. } } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_SuperTrain.Designer.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_SuperTrain.Designer.cs index 71f5a05c2..e4f7dff9c 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_SuperTrain.Designer.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_SuperTrain.Designer.cs @@ -31,29 +31,26 @@ private void InitializeComponent() this.B_Cancel = new System.Windows.Forms.Button(); this.B_Save = new System.Windows.Forms.Button(); this.dataGridView1 = new System.Windows.Forms.DataGridView(); - this.TB_Time1 = new System.Windows.Forms.TextBox(); - this.TB_Time2 = new System.Windows.Forms.TextBox(); - this.CB_S2 = new System.Windows.Forms.ComboBox(); - this.L_Time1 = new System.Windows.Forms.Label(); - this.L_Time2 = new System.Windows.Forms.Label(); - this.L_Species2 = new System.Windows.Forms.Label(); + this.CB_Species2 = new System.Windows.Forms.ComboBox(); this.listBox1 = new System.Windows.Forms.ListBox(); - this.TB_Time = new System.Windows.Forms.TextBox(); - this.CB_Species = new System.Windows.Forms.ComboBox(); - this.TB_Unk = new System.Windows.Forms.MaskedTextBox(); - this.L_Unk = new System.Windows.Forms.Label(); + this.TB_Time1 = new System.Windows.Forms.TextBox(); + this.CB_Species1 = new System.Windows.Forms.ComboBox(); + this.MTB_Form1 = new System.Windows.Forms.MaskedTextBox(); this.L_Species = new System.Windows.Forms.Label(); this.L_Time0 = new System.Windows.Forms.Label(); this.L_Records = new System.Windows.Forms.Label(); this.L_Bags = new System.Windows.Forms.Label(); - this.L_UNKNOWN = new System.Windows.Forms.Label(); + this.TB_Time2 = new System.Windows.Forms.TextBox(); + this.MTB_Gender1 = new System.Windows.Forms.MaskedTextBox(); + this.MTB_Gender2 = new System.Windows.Forms.MaskedTextBox(); + this.MTB_Form2 = new System.Windows.Forms.MaskedTextBox(); ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); this.SuspendLayout(); // // B_Cancel // this.B_Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.B_Cancel.Location = new System.Drawing.Point(176, 281); + this.B_Cancel.Location = new System.Drawing.Point(258, 281); this.B_Cancel.Name = "B_Cancel"; this.B_Cancel.Size = new System.Drawing.Size(60, 23); this.B_Cancel.TabIndex = 0; @@ -64,7 +61,7 @@ private void InitializeComponent() // B_Save // this.B_Save.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.B_Save.Location = new System.Drawing.Point(242, 281); + this.B_Save.Location = new System.Drawing.Point(324, 281); this.B_Save.Name = "B_Save"; this.B_Save.Size = new System.Drawing.Size(60, 23); this.B_Save.TabIndex = 1; @@ -78,8 +75,6 @@ private void InitializeComponent() this.dataGridView1.AllowUserToDeleteRows = false; this.dataGridView1.AllowUserToResizeColumns = false; this.dataGridView1.AllowUserToResizeRows = false; - this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); this.dataGridView1.BackgroundColor = System.Drawing.SystemColors.ControlLightLight; this.dataGridView1.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.None; this.dataGridView1.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single; @@ -92,70 +87,21 @@ private void InitializeComponent() this.dataGridView1.RowHeadersVisible = false; this.dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect; this.dataGridView1.ShowEditingIcon = false; - this.dataGridView1.Size = new System.Drawing.Size(158, 139); + this.dataGridView1.Size = new System.Drawing.Size(240, 139); this.dataGridView1.TabIndex = 116; this.dataGridView1.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.DropClick); // - // TB_Time1 + // CB_Species2 // - this.TB_Time1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.TB_Time1.Location = new System.Drawing.Point(178, 181); - this.TB_Time1.Name = "TB_Time1"; - this.TB_Time1.Size = new System.Drawing.Size(100, 20); - this.TB_Time1.TabIndex = 118; - // - // TB_Time2 - // - this.TB_Time2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.TB_Time2.Location = new System.Drawing.Point(178, 220); - this.TB_Time2.Name = "TB_Time2"; - this.TB_Time2.Size = new System.Drawing.Size(101, 20); - this.TB_Time2.TabIndex = 119; - // - // CB_S2 - // - this.CB_S2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.CB_S2.FormattingEnabled = true; - this.CB_S2.Location = new System.Drawing.Point(178, 259); - this.CB_S2.Name = "CB_S2"; - this.CB_S2.Size = new System.Drawing.Size(101, 21); - this.CB_S2.TabIndex = 120; - // - // L_Time1 - // - this.L_Time1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.L_Time1.AutoSize = true; - this.L_Time1.Location = new System.Drawing.Point(176, 165); - this.L_Time1.Name = "L_Time1"; - this.L_Time1.Size = new System.Drawing.Size(36, 13); - this.L_Time1.TabIndex = 121; - this.L_Time1.Text = "Time1"; - // - // L_Time2 - // - this.L_Time2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.L_Time2.AutoSize = true; - this.L_Time2.Location = new System.Drawing.Point(176, 204); - this.L_Time2.Name = "L_Time2"; - this.L_Time2.Size = new System.Drawing.Size(36, 13); - this.L_Time2.TabIndex = 122; - this.L_Time2.Text = "Time2"; - // - // L_Species2 - // - this.L_Species2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.L_Species2.AutoSize = true; - this.L_Species2.Location = new System.Drawing.Point(175, 243); - this.L_Species2.Name = "L_Species2"; - this.L_Species2.Size = new System.Drawing.Size(45, 13); - this.L_Species2.TabIndex = 123; - this.L_Species2.Text = "Species"; + this.CB_Species2.FormattingEnabled = true; + this.CB_Species2.Location = new System.Drawing.Point(283, 41); + this.CB_Species2.Name = "CB_Species2"; + this.CB_Species2.Size = new System.Drawing.Size(101, 21); + this.CB_Species2.TabIndex = 120; + this.CB_Species2.SelectedIndexChanged += new System.EventHandler(this.ChangeRecordSpecies2); // // listBox1 // - this.listBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); this.listBox1.FormattingEnabled = true; this.listBox1.Items.AddRange(new object[] { "1", @@ -194,51 +140,37 @@ private void InitializeComponent() this.listBox1.TabIndex = 124; this.listBox1.SelectedIndexChanged += new System.EventHandler(this.ChangeListRecordSelection); // - // TB_Time + // TB_Time1 // - this.TB_Time.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.TB_Time.Location = new System.Drawing.Point(176, 115); - this.TB_Time.Name = "TB_Time"; - this.TB_Time.Size = new System.Drawing.Size(100, 20); - this.TB_Time.TabIndex = 125; - this.TB_Time.TextChanged += new System.EventHandler(this.ChangeRecordTime); + this.TB_Time1.Location = new System.Drawing.Point(174, 118); + this.TB_Time1.Name = "TB_Time1"; + this.TB_Time1.Size = new System.Drawing.Size(100, 20); + this.TB_Time1.TabIndex = 125; + this.TB_Time1.TextChanged += new System.EventHandler(this.ChangeRecordTime1); // - // CB_Species + // CB_Species1 // - this.CB_Species.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.CB_Species.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; - this.CB_Species.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; - this.CB_Species.FormattingEnabled = true; - this.CB_Species.Location = new System.Drawing.Point(176, 38); - this.CB_Species.Name = "CB_Species"; - this.CB_Species.Size = new System.Drawing.Size(100, 21); - this.CB_Species.TabIndex = 126; - this.CB_Species.SelectedIndexChanged += new System.EventHandler(this.ChangeRecordSpecies); + this.CB_Species1.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; + this.CB_Species1.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; + this.CB_Species1.FormattingEnabled = true; + this.CB_Species1.Location = new System.Drawing.Point(174, 41); + this.CB_Species1.Name = "CB_Species1"; + this.CB_Species1.Size = new System.Drawing.Size(100, 21); + this.CB_Species1.TabIndex = 126; + this.CB_Species1.SelectedIndexChanged += new System.EventHandler(this.ChangeRecordSpecies1); // - // TB_Unk + // MTB_Form1 // - this.TB_Unk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.TB_Unk.Location = new System.Drawing.Point(219, 65); - this.TB_Unk.Name = "TB_Unk"; - this.TB_Unk.Size = new System.Drawing.Size(57, 20); - this.TB_Unk.TabIndex = 127; - this.TB_Unk.TextChanged += new System.EventHandler(this.ChangeRecordVal); - // - // L_Unk - // - this.L_Unk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.L_Unk.AutoSize = true; - this.L_Unk.Location = new System.Drawing.Point(178, 68); - this.L_Unk.Name = "L_Unk"; - this.L_Unk.Size = new System.Drawing.Size(39, 13); - this.L_Unk.TabIndex = 128; - this.L_Unk.Text = "L_Unk"; + this.MTB_Form1.Location = new System.Drawing.Point(237, 68); + this.MTB_Form1.Name = "MTB_Form1"; + this.MTB_Form1.Size = new System.Drawing.Size(37, 20); + this.MTB_Form1.TabIndex = 127; + this.MTB_Form1.TextChanged += new System.EventHandler(this.ChangeRecordMisc1); // // L_Species // - this.L_Species.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.L_Species.AutoSize = true; - this.L_Species.Location = new System.Drawing.Point(178, 22); + this.L_Species.Location = new System.Drawing.Point(176, 25); this.L_Species.Name = "L_Species"; this.L_Species.Size = new System.Drawing.Size(48, 13); this.L_Species.TabIndex = 129; @@ -246,9 +178,8 @@ private void InitializeComponent() // // L_Time0 // - this.L_Time0.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.L_Time0.AutoSize = true; - this.L_Time0.Location = new System.Drawing.Point(178, 99); + this.L_Time0.Location = new System.Drawing.Point(176, 102); this.L_Time0.Name = "L_Time0"; this.L_Time0.Size = new System.Drawing.Size(33, 13); this.L_Time0.TabIndex = 130; @@ -273,37 +204,56 @@ private void InitializeComponent() this.L_Bags.TabIndex = 132; this.L_Bags.Text = "Training Bags"; // - // L_UNKNOWN + // TB_Time2 // - this.L_UNKNOWN.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.L_UNKNOWN.AutoSize = true; - this.L_UNKNOWN.Location = new System.Drawing.Point(227, 165); - this.L_UNKNOWN.Name = "L_UNKNOWN"; - this.L_UNKNOWN.Size = new System.Drawing.Size(65, 13); - this.L_UNKNOWN.TabIndex = 133; - this.L_UNKNOWN.Text = "UNKNOWN"; + this.TB_Time2.Location = new System.Drawing.Point(284, 118); + this.TB_Time2.Name = "TB_Time2"; + this.TB_Time2.Size = new System.Drawing.Size(100, 20); + this.TB_Time2.TabIndex = 136; + this.TB_Time2.TextChanged += new System.EventHandler(this.ChangeRecordTime2); + // + // MTB_Gender1 + // + this.MTB_Gender1.Location = new System.Drawing.Point(196, 68); + this.MTB_Gender1.Name = "MTB_Gender1"; + this.MTB_Gender1.Size = new System.Drawing.Size(35, 20); + this.MTB_Gender1.TabIndex = 137; + this.MTB_Gender1.TextChanged += new System.EventHandler(this.ChangeRecordMisc1); + // + // MTB_Gender2 + // + this.MTB_Gender2.Location = new System.Drawing.Point(306, 68); + this.MTB_Gender2.Name = "MTB_Gender2"; + this.MTB_Gender2.Size = new System.Drawing.Size(35, 20); + this.MTB_Gender2.TabIndex = 139; + this.MTB_Gender2.TextChanged += new System.EventHandler(this.ChangeRecordMisc2); + // + // MTB_Form2 + // + this.MTB_Form2.Location = new System.Drawing.Point(347, 68); + this.MTB_Form2.Name = "MTB_Form2"; + this.MTB_Form2.Size = new System.Drawing.Size(37, 20); + this.MTB_Form2.TabIndex = 138; + this.MTB_Form2.TextChanged += new System.EventHandler(this.ChangeRecordMisc2); // // SAV_SuperTrain // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(314, 312); - this.Controls.Add(this.L_UNKNOWN); + this.ClientSize = new System.Drawing.Size(396, 312); + this.Controls.Add(this.MTB_Gender2); + this.Controls.Add(this.MTB_Form2); + this.Controls.Add(this.MTB_Gender1); + this.Controls.Add(this.TB_Time2); this.Controls.Add(this.L_Bags); this.Controls.Add(this.L_Records); this.Controls.Add(this.L_Time0); this.Controls.Add(this.L_Species); - this.Controls.Add(this.L_Unk); - this.Controls.Add(this.TB_Unk); - this.Controls.Add(this.CB_Species); - this.Controls.Add(this.TB_Time); - this.Controls.Add(this.listBox1); - this.Controls.Add(this.L_Species2); - this.Controls.Add(this.L_Time2); - this.Controls.Add(this.L_Time1); - this.Controls.Add(this.CB_S2); - this.Controls.Add(this.TB_Time2); + this.Controls.Add(this.MTB_Form1); + this.Controls.Add(this.CB_Species1); this.Controls.Add(this.TB_Time1); + this.Controls.Add(this.listBox1); + this.Controls.Add(this.CB_Species2); this.Controls.Add(this.dataGridView1); this.Controls.Add(this.B_Save); this.Controls.Add(this.B_Cancel); @@ -326,21 +276,18 @@ private void InitializeComponent() private System.Windows.Forms.Button B_Cancel; private System.Windows.Forms.Button B_Save; private System.Windows.Forms.DataGridView dataGridView1; - private System.Windows.Forms.TextBox TB_Time1; - private System.Windows.Forms.TextBox TB_Time2; - private System.Windows.Forms.ComboBox CB_S2; - private System.Windows.Forms.Label L_Time1; - private System.Windows.Forms.Label L_Time2; - private System.Windows.Forms.Label L_Species2; + private System.Windows.Forms.ComboBox CB_Species2; private System.Windows.Forms.ListBox listBox1; - private System.Windows.Forms.TextBox TB_Time; - private System.Windows.Forms.ComboBox CB_Species; - private System.Windows.Forms.MaskedTextBox TB_Unk; - private System.Windows.Forms.Label L_Unk; + private System.Windows.Forms.TextBox TB_Time1; + private System.Windows.Forms.ComboBox CB_Species1; + private System.Windows.Forms.MaskedTextBox MTB_Form1; private System.Windows.Forms.Label L_Species; private System.Windows.Forms.Label L_Time0; private System.Windows.Forms.Label L_Records; private System.Windows.Forms.Label L_Bags; - private System.Windows.Forms.Label L_UNKNOWN; + private System.Windows.Forms.TextBox TB_Time2; + private System.Windows.Forms.MaskedTextBox MTB_Gender1; + private System.Windows.Forms.MaskedTextBox MTB_Gender2; + private System.Windows.Forms.MaskedTextBox MTB_Form2; } } \ No newline at end of file diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_SuperTrain.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_SuperTrain.cs index 3f08c2e30..03dbe7346 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_SuperTrain.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_SuperTrain.cs @@ -11,6 +11,8 @@ public partial class SAV_SuperTrain : Form private readonly SAV6 SAV; private readonly SuperTrainBlock STB; + private readonly string[] trba; + public SAV_SuperTrain(SaveFile sav) { InitializeComponent(); @@ -19,10 +21,6 @@ public SAV_SuperTrain(SaveFile sav) trba = GameInfo.Strings.trainingbags; trba[0] = "---"; STB = ((ISaveBlock6Main) SAV).SuperTrain; - var ofs = STB.Offset; - offsetTime = ofs + 0x08; - offsetSpec = ofs + 0x188; - offsetVal = ofs + 0x18A; string[] stages = GameInfo.Strings.trainingstage; listBox1.Items.Clear(); for (int i = 0; i < 32; i++) @@ -31,28 +29,19 @@ public SAV_SuperTrain(SaveFile sav) Setup(); } - private readonly string[] trba; - private readonly int offsetVal; - private readonly int offsetTime; - private readonly int offsetSpec; - private void Setup() { dataGridView1.Rows.Clear(); dataGridView1.Columns.Clear(); { - CB_Species.InitializeBinding(); - CB_Species.DataSource = new BindingSource(GameInfo.FilteredSources.Species, null); + CB_Species1.InitializeBinding(); + CB_Species1.DataSource = new BindingSource(GameInfo.FilteredSources.Species, null); - CB_S2.InitializeBinding(); - CB_S2.DataSource = new BindingSource(GameInfo.FilteredSources.Species, null); + CB_Species2.InitializeBinding(); + CB_Species2.DataSource = new BindingSource(GameInfo.FilteredSources.Species, null); } listBox1.SelectedIndex = 0; FillTrainingBags(); - - CB_S2.SelectedValue = (int)BitConverter.ToUInt16(SAV.Data, offsetSpec + (4 * 30)); - TB_Time1.Text = BitConverter.ToSingle(SAV.Data, offsetTime + (4 * 30)).ToString(CultureInfo.InvariantCulture); - TB_Time2.Text = BitConverter.ToSingle(SAV.Data, offsetTime + (4 * 31)).ToString(CultureInfo.InvariantCulture); } private void FillTrainingBags() @@ -110,10 +99,18 @@ private void ChangeListRecordSelection(object sender, EventArgs e) int index = listBox1.SelectedIndex; if (index < 0) return; + loading = true; - TB_Time.Text = BitConverter.ToSingle(SAV.Data, offsetTime + (4 * index)).ToString(CultureInfo.InvariantCulture); - TB_Unk.Text = BitConverter.ToUInt16(SAV.Data, offsetVal + (4 * index)).ToString(); - CB_Species.SelectedValue = (int)BitConverter.ToUInt16(SAV.Data, offsetSpec + (4 * index)); + var holder1 = STB.GetHolder1(index); + var holder2 = STB.GetHolder2(index); + CB_Species1.SelectedValue = holder1.Species; + MTB_Gender1.Text = holder1.Gender.ToString(); + MTB_Form1.Text = holder1.Form.ToString(); + CB_Species2.SelectedValue = holder2.Species; + MTB_Gender2.Text = holder2.Gender.ToString(); + MTB_Form2.Text = holder2.Form.ToString(); + TB_Time1.Text = STB.GetTime1(index).ToString(CultureInfo.InvariantCulture); + TB_Time2.Text = STB.GetTime2(index).ToString(CultureInfo.InvariantCulture); loading = false; } @@ -132,12 +129,6 @@ private void B_Save_Click(object sender, EventArgs e) STB.SetBag(i - emptyslots, (byte)Array.IndexOf(trba, bag)); } - if (float.TryParse(TB_Time1.Text, out var t1)) - SAV.SetData(BitConverter.GetBytes(t1), offsetTime + (4 * 30)); - if (float.TryParse(TB_Time2.Text, out var t2)) - SAV.SetData(BitConverter.GetBytes(t2), offsetTime + (4 * 31)); - SAV.SetData(BitConverter.GetBytes((ushort)WinFormsUtil.GetIndex(CB_S2)), offsetSpec + (4 * 30)); - Origin.CopyChangesFrom(SAV); Close(); } @@ -147,30 +138,64 @@ private void B_Cancel_Click(object sender, EventArgs e) Close(); } - private void ChangeRecordSpecies(object sender, EventArgs e) + private void ChangeRecordSpecies1(object sender, EventArgs e) { int index = listBox1.SelectedIndex; if (index < 0 || loading) return; - SAV.SetData(BitConverter.GetBytes(WinFormsUtil.GetIndex(CB_Species)), offsetSpec + (4 * index)); + var holder = STB.GetHolder1(index); + holder.Species = WinFormsUtil.GetIndex(CB_Species1); } - private void ChangeRecordVal(object sender, EventArgs e) + private void ChangeRecordMisc1(object sender, EventArgs e) { int index = listBox1.SelectedIndex; if (index < 0 || loading) return; - if (ushort.TryParse(TB_Unk.Text, out var val)) - SAV.SetData(BitConverter.GetBytes(val), offsetVal + (4 * index)); + var holder = STB.GetHolder1(index); + if (byte.TryParse(MTB_Form1.Text, out var form)) + holder.Form = form; + if (byte.TryParse(MTB_Gender1.Text, out var gender)) + holder.Gender = gender; } - private void ChangeRecordTime(object sender, EventArgs e) + private void ChangeRecordSpecies2(object sender, EventArgs e) { int index = listBox1.SelectedIndex; if (index < 0 || loading) return; - if (float.TryParse(TB_Time.Text, out var val)) - SAV.SetData(BitConverter.GetBytes(val), offsetTime + (4 * index)); + var holder = STB.GetHolder2(index); + holder.Species = WinFormsUtil.GetIndex(CB_Species2); + } + + private void ChangeRecordMisc2(object sender, EventArgs e) + { + int index = listBox1.SelectedIndex; + if (index < 0 || loading) + return; + var holder = STB.GetHolder2(index); + if (byte.TryParse(MTB_Form2.Text, out var form)) + holder.Form = form; + if (byte.TryParse(MTB_Gender2.Text, out var gender)) + holder.Gender = gender; + } + + private void ChangeRecordTime1(object sender, EventArgs e) + { + int index = listBox1.SelectedIndex; + if (index < 0 || loading) + return; + if (float.TryParse(TB_Time1.Text, out var value)) + STB.SetTime1(index, value); + } + + private void ChangeRecordTime2(object sender, EventArgs e) + { + int index = listBox1.SelectedIndex; + if (index < 0 || loading) + return; + if (float.TryParse(TB_Time2.Text, out var value)) + STB.SetTime2(index, value); } } } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_FestivalPlaza.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_FestivalPlaza.cs index f5734bf4f..950236cc4 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_FestivalPlaza.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_FestivalPlaza.cs @@ -4,6 +4,7 @@ using System.Windows.Forms; using PKHeX.Core; using PKHeX.Drawing.PokeSprite; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.WinForms { @@ -265,12 +266,13 @@ private void LoadFacility() CHK_FacilityIntroduced.Checked = facility.IsIntroduced; TB_OTName.Text = facility.OT_Name; LoadOTlabel(facility.Gender); - if (CB_FacilityMessage.SelectedIndex >= 0) LoadFMessage(CB_FacilityMessage.SelectedIndex); - TB_UsedFlags.Text = f[entry].UsedFlags.ToString("X8"); - TB_UsedStats.Text = f[entry].UsedRandStat.ToString("X8"); - var bytes = f[entry].TrainerFesID; - var str = BitConverter.ToString(bytes).Replace("-", string.Empty); - TB_FacilityID.Text = str; + if (CB_FacilityMessage.SelectedIndex >= 0) + LoadFMessage(CB_FacilityMessage.SelectedIndex); + + var obj = f[entry]; + TB_UsedFlags.Text = obj.UsedFlags.ToString("X8"); + TB_UsedStats.Text = obj.UsedRandStat.ToString("X8"); + TB_FacilityID.Text = Util.GetHexStringFromBytes(obj.TrainerFesID); editing = false; } @@ -305,14 +307,14 @@ private void LoadBattleAgency() B_ImportParty.Visible = SAV.HasParty; CHK_Choosed.Checked = SAV.GetFlag(0x6C55E, 1); CHK_TrainerInvited.Checked = IsTrainerInvited(); - ushort valus = BitConverter.ToUInt16(SAV.Data, 0x6C55C); + ushort valus = ReadUInt16LittleEndian(SAV.Data.AsSpan(0x6C55C)); int grade = valus >> 6 & 0x3F; NUD_Grade.Value = grade; int max = (Math.Min(49, grade) / 10 * 3) + 2; int defeated = valus >> 12; NUD_Defeated.Value = defeated > max ? max : defeated; NUD_Defeated.Maximum = max; - NUD_DefeatMon.Value = BitConverter.ToUInt16(SAV.Data, 0x6C558); + NUD_DefeatMon.Value = ReadUInt16LittleEndian(SAV.Data.AsSpan(0x6C558)); for (int i = 0; i < NUD_Trainers.Length; i++) { int j = GetSavData16(0x6C56C + (0x14 * i)); @@ -329,7 +331,7 @@ private void LoadPictureBox() } private readonly NumericUpDown[] NUD_Trainers = new NumericUpDown[3]; - private ushort GetSavData16(int Offset) => BitConverter.ToUInt16(SAV.Data, Offset); + private ushort GetSavData16(int offset) => ReadUInt16LittleEndian(SAV.Data.AsSpan(offset)); private const ushort InvitedValue = 0x7DFF; private readonly PKM[] p = new PKM[3]; private readonly PictureBox[] PBs = new PictureBox[3]; @@ -338,18 +340,20 @@ private void LoadPictureBox() private void SaveBattleAgency() { SAV.SetFlag(0x6C55E, 1, CHK_Choosed.Checked); - if (IsTrainerInvited() ^ CHK_TrainerInvited.Checked) + if (IsTrainerInvited() != CHK_TrainerInvited.Checked) { - SAV.SetData(BitConverter.GetBytes((ushort)(CHK_TrainerInvited.Checked ? GetSavData16(0x6C3EE) | InvitedValue : 0)), 0x6C3EE); - SAV.SetData(BitConverter.GetBytes((ushort)(CHK_TrainerInvited.Checked ? GetSavData16(0x6C526) | InvitedValue : 0)), 0x6C526); + WriteUInt16LittleEndian(SAV.Data.AsSpan(0x6C3EE), (ushort)(CHK_TrainerInvited.Checked ? GetSavData16(0x6C3EE) | InvitedValue : 0)); + WriteUInt16LittleEndian(SAV.Data.AsSpan(0x6C526), (ushort)(CHK_TrainerInvited.Checked ? GetSavData16(0x6C526) | InvitedValue : 0)); } SAV.SetData(p[0].EncryptedBoxData, 0x6C200); SAV.SetData(p[1].EncryptedPartyData, 0x6C2E8); SAV.SetData(p[2].EncryptedPartyData, 0x6C420); - SAV.SetData(BitConverter.GetBytes((ushort)(((int)NUD_Defeated.Value & 0xF) << 12 | ((int)NUD_Grade.Value & 0x3F) << 6 | (SAV.Data[0x6C55C] & 0x3F))), 0x6C55C); - SAV.SetData(BitConverter.GetBytes((ushort)NUD_DefeatMon.Value), 0x6C558); + + var gradeDefeated = (((int)NUD_Defeated.Value & 0xF) << 12 | ((int)NUD_Grade.Value & 0x3F) << 6 | (SAV.Data[0x6C55C] & 0x3F)); + WriteUInt16LittleEndian(SAV.Data.AsSpan(0x6C558), (ushort)NUD_DefeatMon.Value); + WriteUInt16LittleEndian(SAV.Data.AsSpan(0x6C55C), (ushort)gradeDefeated); for (int i = 0; i < NUD_Trainers.Length; i++) - SAV.SetData(BitConverter.GetBytes((ushort)NUD_Trainers[i].Value), 0x6C56C + (0x14 * i)); + WriteUInt16LittleEndian(SAV.Data.AsSpan(0x6C56C + (0x14 * i)), (ushort)NUD_Trainers[i].Value); SAV.Festa.FestivalPlazaName = TB_PlazaName.Text; } @@ -480,11 +484,7 @@ private void HexTextBox_TextChanged(object sender, EventArgs e) } else if (sender == TB_FacilityID) { - if (t.Length != 12 * 2) - t = t.PadLeft(24, '0'); - var bytes = t.ToByteArray(); - Array.Resize(ref bytes, 12); - f[entry].TrainerFesID = bytes; + f[entry].TrainerFesID = Util.GetBytesFromHexString(t.PadLeft(24, '0')); } } @@ -677,7 +677,7 @@ private void B_DelVisitor_Click(object sender, EventArgs e) var facility = f[entry]; // there is a unknown value when not introduced...no reproducibility, just mistake? if (facility.IsIntroduced) - facility.TrainerFesID = new byte[12]; + facility.ClearTrainerFesID(); facility.IsIntroduced = false; facility.OT_Name = string.Empty; facility.Gender = 0; @@ -755,7 +755,7 @@ private void B_AgentGlass_Click(object sender, EventArgs e) { if (NUD_Grade.Value < 30 && DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, "Agent Sunglasses is reward of Grade 30.", "Continue?")) return; - SAV.SetData(new byte[] { 3 }, SAV.Fashion.Offset + 0xD0); + SAV.Data[SAV.Fashion.Offset + 0xD0] = 3; B_AgentGlass.Enabled = false; System.Media.SystemSounds.Asterisk.Play(); } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_Pokebean.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_Pokebean.cs index ffa82cba2..e5cf30d6d 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_Pokebean.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_Pokebean.cs @@ -89,8 +89,7 @@ private void B_Save_Click(object sender, EventArgs e) { var beans = Pouch.Beans; for (int i = 0; i < beans.Length; i++) - beans[i] = (int)dgv.Rows[i].Cells[1].Value; - Pouch.Beans = beans; + beans[i] = (byte)dgv.Rows[i].Cells[1].Value; Origin.CopyChangesFrom(SAV); Close(); } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_PokedexSWSH.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_PokedexSWSH.cs index c46edd94d..97fa647e7 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_PokedexSWSH.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_PokedexSWSH.cs @@ -99,8 +99,8 @@ private void GetEntry(int index) { if (j < 63) c.Items[j] = $"{j:00} - {(j < forms.Length ? forms[j] : "N/A")}"; - var val = Dex.GetSeenRegion(entry, j, i); - c.SetItemChecked(j, val); + var value = Dex.GetSeenRegion(entry, j, i); + c.SetItemChecked(j, value); } if (species == (int) Species.Urshifu) @@ -160,8 +160,8 @@ private void SetEntry(int index) var c = CHK[i]; for (int j = 0; j < 64; j++) { - var val = c.GetItemChecked(j); - Dex.SetSeenRegion(entry, j, i, val); + var value = c.GetItemChecked(j); + Dex.SetSeenRegion(entry, j, i, value); } } diff --git a/PKHeX.WinForms/Subforms/Save Editors/SAV_EventWork.cs b/PKHeX.WinForms/Subforms/Save Editors/SAV_EventWork.cs index cc26987ef..8fed87517 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/SAV_EventWork.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/SAV_EventWork.cs @@ -142,14 +142,14 @@ private void LoadWork(IEnumerable editorWork) { if (editing) return; - var val = (int) cb.SelectedValue; + var value = (int) cb.SelectedValue; editing = true; - var match = f.Options.FirstOrDefault(z => z.Value == val); + var match = f.Options.FirstOrDefault(z => z.Value == value); nud.Enabled = match?.Custom == true; if (!nud.Enabled) { - nud.Value = (ushort) val; - f.Value = val; + nud.Value = (ushort) value; + f.Value = value; } editing = false; }; @@ -158,11 +158,11 @@ private void LoadWork(IEnumerable editorWork) if (editing) return; - var val = Util.ToInt32(nud.Text); - f.Value = val; + var value = Util.ToInt32(nud.Text); + f.Value = value; editing = true; if (f.RawIndex == CB_Stats.SelectedIndex) - NUD_Stat.Text = val.ToString(); + NUD_Stat.Text = value.ToString(); editing = false; }; tlp.Controls.Add(lbl, 0, i); diff --git a/PKHeX.WinForms/Subforms/Save Editors/SAV_Inventory.cs b/PKHeX.WinForms/Subforms/Save Editors/SAV_Inventory.cs index dc56ffbd0..43fcdc80b 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/SAV_Inventory.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/SAV_Inventory.cs @@ -310,7 +310,7 @@ private void GiveAllItems(object sender, EventArgs e) { items = (ushort[])items.Clone(); if (shuffle) - Util.Shuffle(items); + Util.Shuffle(items.AsSpan()); Array.Resize(ref items, pouch.Items.Length); } diff --git a/Tests/PKHeX.Core.Tests/Legality/ShadowTests.cs b/Tests/PKHeX.Core.Tests/Legality/ShadowTests.cs index 1c0af3a1f..9ef37ab9c 100644 --- a/Tests/PKHeX.Core.Tests/Legality/ShadowTests.cs +++ b/Tests/PKHeX.Core.Tests/Legality/ShadowTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using FluentAssertions; using PKHeX.Core; using Xunit; @@ -192,18 +193,18 @@ static IEnumerable getSeeds(uint PID) [Fact] public static void VerifyMawileAntishiny() { - VerifyResultsAntiShiny(Mawile, Encounters3Teams.Mawile, 12345, 51882, new[] {31, 30, 29, 31, 23, 27}); + VerifyResultsAntiShiny(Mawile, Encounters3Teams.Mawile, 12345, 51882, stackalloc[] {31, 30, 29, 31, 23, 27}); } - private static void VerifyResultsAntiShiny(uint[] results, TeamLock[] team, int tid, int sid, int[] ivs) + private static void VerifyResultsAntiShiny(ReadOnlySpan resultPIDs, TeamLock[] team, int tid, int sid, Span ivs) { var pk3 = new PK3 { - PID = results[^1], + PID = resultPIDs[^1], TID = tid, SID = sid, - IVs = ivs, }; + pk3.SetIVs(ivs); var info = MethodFinder.Analyze(pk3); info.Type.Should().Be(PIDType.CXD, "because the PID should have matched the CXD spread"); diff --git a/Tests/PKHeX.Core.Tests/PKM/StringTests.cs b/Tests/PKHeX.Core.Tests/PKM/StringTests.cs index 23ddddb38..756fb9934 100644 --- a/Tests/PKHeX.Core.Tests/PKM/StringTests.cs +++ b/Tests/PKHeX.Core.Tests/PKM/StringTests.cs @@ -18,7 +18,7 @@ public void EncodesOTNameCorrectly() { const string name_fabian = "Fabian♂"; var pkm = new PK7 { OT_Name = name_fabian }; - var byte_fabian = new byte[] + Span byte_fabian = stackalloc byte[] { 0x46, 0x00, // F 0x61, 0x00, // a @@ -37,7 +37,7 @@ public void EncodesNicknameCorrectly() { const string name_nidoran = "ニドラン♀"; var pkm = new PK7 { Nickname = name_nidoran }; - var byte_nidoran = new byte[] + Span byte_nidoran = stackalloc byte[] { 0xCB, 0x30, // ニ 0xC9, 0x30, // ド @@ -77,8 +77,8 @@ private static string Hex(ReadOnlySpan outdata) [InlineData(0x105, 0x266A)] // ♪ public static void Encode45(ushort g4, char g5) { - StringConverter4.ConvertChar2ValueG4(g5).Should().Be(g4); - StringConverter4.ConvertValue2CharG4(g4).Should().Be(g5); + StringConverter4Util.ConvertChar2ValueG4(g5).Should().Be(g4); + StringConverter4Util.ConvertValue2CharG4(g4).Should().Be(g5); } } } diff --git a/Tests/PKHeX.Core.Tests/Saves/PokeDex.cs b/Tests/PKHeX.Core.Tests/Saves/PokeDex.cs index 3e7e25262..0153544e8 100644 --- a/Tests/PKHeX.Core.Tests/Saves/PokeDex.cs +++ b/Tests/PKHeX.Core.Tests/Saves/PokeDex.cs @@ -1,4 +1,5 @@ -using Xunit; +using System; +using Xunit; using FluentAssertions; using PKHeX.Core; @@ -41,11 +42,14 @@ private static void CheckFlags(SaveFile sav, int species, int regionSize) var data = sav.Data; var bit = species - 1; - var val = (byte) (1 << (bit & 7)); - var ofs = bit >> 3; - data[dex + 0x08 + ofs].Should().Be(val, "caught flag"); - data[dex + 0x08 + regionSize + ofs].Should().Be(val, "seen flag"); - data[dex + 0x08 + regionSize + (regionSize * 4) + ofs].Should().Be(val, "displayed flag"); + var value = (byte) (1 << (bit & 7)); + var offset = bit >> 3; + + // Check the regular flag regions. + var span = data.AsSpan(dex + 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, int species, int form, int regionSize, int formRegionSize) @@ -53,17 +57,18 @@ private static void CheckDexFlags5(SaveFile sav, int species, int form, int regi var dex = sav.PokeDex; var data = sav.Data; - var formDex = dex + 8 + (regionSize * 9); - int fc = sav.Personal[species].FormCount; var bit = ((SAV5)sav).Zukan.DexFormIndexFetcher(species, fc); if (bit < 0) return; bit += form; - var val = (byte)(1 << (bit & 7)); - var ofs = bit >> 3; - data[formDex + ofs].Should().Be(val, "seen flag"); - data[formDex + ofs + (formRegionSize * 2)].Should().Be(val, "displayed flag"); + var value = (byte)(1 << (bit & 7)); + var offset = bit >> 3; + + // Check the form flag regions. + var span = data.AsSpan(dex + 0x08 + (regionSize * 9)); + span[offset].Should().Be(value, "seen flag"); + span[offset + (formRegionSize * 2)].Should().Be(value, "displayed flag"); } } } diff --git a/Tests/PKHeX.Core.Tests/Util/ConvertUtilTests.cs b/Tests/PKHeX.Core.Tests/Util/ConvertUtilTests.cs index 7239eac45..5a5affc90 100644 --- a/Tests/PKHeX.Core.Tests/Util/ConvertUtilTests.cs +++ b/Tests/PKHeX.Core.Tests/Util/ConvertUtilTests.cs @@ -1,7 +1,8 @@ using System; -using System.Linq; using FluentAssertions; +using PKHeX.Core; using Xunit; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Tests.Util { @@ -42,7 +43,7 @@ public void CheckConvertValidHexU32(string v, uint result) public void CheckConvertHexString(string v, uint result) { var convert = Core.Util.GetBytesFromHexString(v); - var u32 = BitConverter.ToUInt32(convert); + var u32 = ReadUInt32LittleEndian(convert); u32.Should().Be(result); var remake = Core.Util.GetHexStringFromBytes(convert, 0, convert.Length); @@ -53,11 +54,14 @@ public void CheckConvertHexString(string v, uint result) [InlineData(0x12345678, 12345678)] public void CheckConvertBCD_Little(uint raw, int expect) { - var data = BitConverter.GetBytes(raw); - var result = Core.BinaryCodedDecimal.ToInt32LE(data); + Span data = stackalloc byte[4]; + WriteUInt32LittleEndian(data, raw); + + var result = BinaryCodedDecimal.ToInt32LE(data); result.Should().Be(expect); - var newData = Core.BinaryCodedDecimal.GetBytesLE(result, 4); + Span newData = stackalloc byte[4]; + BinaryCodedDecimal.WriteBytesLE(newData, result); data.SequenceEqual(newData).Should().BeTrue(); } @@ -65,49 +69,15 @@ public void CheckConvertBCD_Little(uint raw, int expect) [InlineData(0x78563412, 12345678)] public void CheckConvertBCD_Big(uint raw, int expect) { - var data = BitConverter.GetBytes(raw); - var result = Core.BinaryCodedDecimal.ToInt32BE(data); + Span data = stackalloc byte[4]; + WriteUInt32LittleEndian(data, raw); + + var result = BinaryCodedDecimal.ToInt32BE(data); result.Should().Be(expect); - var newData = Core.BinaryCodedDecimal.GetBytesBE(result, 4); + Span newData = stackalloc byte[4]; + BinaryCodedDecimal.WriteBytesBE(newData, result); data.SequenceEqual(newData).Should().BeTrue(); } } - - public class FlagUtilTests - { - [Theory] - [InlineData(1, 0, 0)] - [InlineData(2, 0, 1)] - [InlineData(0x8000_0000, 3, 7)] - public void GetSetFlag(uint raw, int byteIndex, int bitIndex) - { - var data = BitConverter.GetBytes(raw); - var value = Core.FlagUtil.GetFlag(data, byteIndex, bitIndex); - value.Should().Be(true); - - var copy = new byte[data.Length]; - Core.FlagUtil.SetFlag(copy, byteIndex, bitIndex, true); - copy.SequenceEqual(data).Should().BeTrue(); - } - - [Theory] - [InlineData(0x7FFF_FFFE, 0, 0)] - public void ClearFlag(uint raw, int byteIndex, int bitIndex) - { - var data = BitConverter.GetBytes(raw); - var value = Core.FlagUtil.GetFlag(data, byteIndex, bitIndex); - value.Should().Be(false); - - // does nothing on empty - var copy = new byte[data.Length]; - Core.FlagUtil.SetFlag(copy, byteIndex, bitIndex, false); - copy.All(z => z == 0).Should().BeTrue(); - - // doesn't clear any other flag - copy = (byte[])data.Clone(); - Core.FlagUtil.SetFlag(copy, byteIndex, bitIndex, false); - copy.SequenceEqual(data).Should().BeTrue(); - } - } } diff --git a/Tests/PKHeX.Core.Tests/Util/FlagUtilTests.cs b/Tests/PKHeX.Core.Tests/Util/FlagUtilTests.cs new file mode 100644 index 000000000..0a99a352b --- /dev/null +++ b/Tests/PKHeX.Core.Tests/Util/FlagUtilTests.cs @@ -0,0 +1,46 @@ +using System; +using FluentAssertions; +using PKHeX.Core; +using Xunit; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Tests.Util; + +public class FlagUtilTests +{ + [Theory] + [InlineData(1, 0, 0)] + [InlineData(2, 0, 1)] + [InlineData(0x8000_0000, 3, 7)] + public void GetSetFlag(uint raw, int byteIndex, int bitIndex) + { + Span data = stackalloc byte[4]; + WriteUInt32LittleEndian(data, raw); + var value = FlagUtil.GetFlag(data, byteIndex, bitIndex); + value.Should().Be(true); + + Span copy = stackalloc byte[data.Length]; + FlagUtil.SetFlag(copy, byteIndex, bitIndex, true); + data.SequenceEqual(copy).Should().BeTrue(); + } + + [Theory] + [InlineData(0x7FFF_FFFE, 0, 0)] + public void ClearFlag(uint raw, int byteIndex, int bitIndex) + { + Span data = stackalloc byte[4]; + WriteUInt32LittleEndian(data, raw); + var value = FlagUtil.GetFlag(data, byteIndex, bitIndex); + value.Should().Be(false); + + // does nothing on empty + Span copy = stackalloc byte[data.Length]; + FlagUtil.SetFlag(copy, byteIndex, bitIndex, false); + copy.Count((byte)0).Should().Be(copy.Length); + + // doesn't clear any other flag + data.CopyTo(copy); + FlagUtil.SetFlag(copy, byteIndex, bitIndex, false); + data.SequenceEqual(copy).Should().BeTrue(); + } +} diff --git a/Tests/PKHeX.Core.Tests/Util/StringQualityTests.cs b/Tests/PKHeX.Core.Tests/Util/StringQualityTests.cs index 4d126bf5c..675f44f4c 100644 --- a/Tests/PKHeX.Core.Tests/Util/StringQualityTests.cs +++ b/Tests/PKHeX.Core.Tests/Util/StringQualityTests.cs @@ -72,9 +72,9 @@ private static void CheckMetLocations(string language) { iterated = true; var name = p.Name; - var val = p.GetValue(strings); - Assert.NotNull(val); - var arr = (string[])val!; + var value = p.GetValue(strings); + Assert.NotNull(value); + var arr = (string[])value!; var hs = new HashSet(); bool sm0 = name == nameof(GameStrings.metSM_00000);