From 0087ccb44f26e984023a3fdf4fd9794b4098822f Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 25 Mar 2023 23:14:50 -0700 Subject: [PATCH] Use span for savefile slot API No more offset fetch --- .../Editing/Saves/Slots/Info/SlotInfoMisc.cs | 10 +- .../Encounters/Data/EncounterEvent.cs | 2 +- .../Memories/MemoryContext6Data.cs | 2 +- .../Memories/MemoryContext8Data.cs | 2 +- PKHeX.Core/PKM/Shared/GBPKML.cs | 2 +- PKHeX.Core/Saves/SAV1.cs | 24 ++-- PKHeX.Core/Saves/SAV1Stadium.cs | 12 +- PKHeX.Core/Saves/SAV1StadiumJ.cs | 13 +- PKHeX.Core/Saves/SAV2.cs | 20 ++-- PKHeX.Core/Saves/SAV2Stadium.cs | 8 +- PKHeX.Core/Saves/SAV3.cs | 10 +- PKHeX.Core/Saves/SAV3Colosseum.cs | 16 ++- PKHeX.Core/Saves/SAV3E.cs | 26 ++-- PKHeX.Core/Saves/SAV3FRLG.cs | 8 +- PKHeX.Core/Saves/SAV3RS.cs | 16 +-- PKHeX.Core/Saves/SAV3RSBox.cs | 28 +++-- PKHeX.Core/Saves/SAV3XD.cs | 14 ++- PKHeX.Core/Saves/SAV4.cs | 22 ++-- PKHeX.Core/Saves/SAV4BR.cs | 14 ++- PKHeX.Core/Saves/SAV4Sinnoh.cs | 4 +- PKHeX.Core/Saves/SAV5.cs | 4 +- PKHeX.Core/Saves/SAV6AO.cs | 2 +- PKHeX.Core/Saves/SAV6XY.cs | 2 +- PKHeX.Core/Saves/SAV8BS.cs | 4 +- PKHeX.Core/Saves/SAV8LA.cs | 4 +- PKHeX.Core/Saves/SAV8SWSH.cs | 6 +- PKHeX.Core/Saves/SAV9SV.cs | 6 +- PKHeX.Core/Saves/SaveFile.cs | 111 +++++++++--------- PKHeX.Core/Saves/Storage/Bank3.cs | 3 +- PKHeX.Core/Saves/Storage/Bank4.cs | 3 +- PKHeX.Core/Saves/Storage/Bank7.cs | 5 +- PKHeX.Core/Saves/Storage/SAV4Ranch.cs | 57 +++++---- .../Saves/Substructures/Gen4/PoffinCase4.cs | 9 +- .../Saves/Substructures/Gen5/MysteryBlock5.cs | 2 +- .../Saves/Substructures/Gen6/MysteryBlock6.cs | 2 +- PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs | 6 +- .../Saves/Substructures/Gen7/FashionBlock7.cs | 28 +++-- .../Saves/Substructures/Gen7/FestaFacility.cs | 2 +- .../Saves/Substructures/Gen7/FieldMenu7.cs | 6 +- .../Substructures/Gen7/LGPE/PokeListHeader.cs | 5 +- .../Saves/Substructures/Gen7/MysteryBlock7.cs | 4 +- .../Saves/Substructures/Gen7/ResortSave7.cs | 19 +-- .../Gen8/BS/BerryTreeGrowSave8b.cs | 2 +- .../Saves/Substructures/Gen8/SWSH/Fused8.cs | 26 ++-- PKHeX.Core/Saves/Substructures/Mail/Mail2.cs | 2 +- PKHeX.Core/Saves/Substructures/Mail/Mail4.cs | 2 +- PKHeX.Core/Util/ArrayUtil.cs | 24 +--- .../Controls/SAV Editor/SAVEditor.cs | 2 +- .../Subforms/Save Editors/Gen5/SAV_Misc5.cs | 2 +- .../Save Editors/Gen7/SAV_FestivalPlaza.cs | 6 +- 50 files changed, 317 insertions(+), 292 deletions(-) diff --git a/PKHeX.Core/Editing/Saves/Slots/Info/SlotInfoMisc.cs b/PKHeX.Core/Editing/Saves/Slots/Info/SlotInfoMisc.cs index 2e579ed6f..2c0ae6e98 100644 --- a/PKHeX.Core/Editing/Saves/Slots/Info/SlotInfoMisc.cs +++ b/PKHeX.Core/Editing/Saves/Slots/Info/SlotInfoMisc.cs @@ -1,3 +1,5 @@ +using System; + namespace PKHeX.Core; /// @@ -21,15 +23,17 @@ public sealed record SlotInfoMisc(byte[] Data, int Slot, int Offset, bool PartyF public bool WriteTo(SaveFile sav, PKM pk, PKMImportSetting setting = PKMImportSetting.UseDefault) { + var span = Data.AsSpan(Offset); if (PartyFormat) - sav.SetSlotFormatParty(pk, Data, Offset, setting, setting); + sav.SetSlotFormatParty(pk, span, setting, setting); else - sav.SetSlotFormatStored(pk, Data, Offset, setting, setting); + sav.SetSlotFormatStored(pk, span, setting, setting); return true; } public PKM Read(SaveFile sav) { - return PartyFormat ? sav.GetPartySlot(Data, Offset) : sav.GetStoredSlot(Data, Offset); + var span = Data.AsSpan(Offset); + return PartyFormat ? sav.GetPartySlot(span) : sav.GetStoredSlot(span); } } diff --git a/PKHeX.Core/Legality/Encounters/Data/EncounterEvent.cs b/PKHeX.Core/Legality/Encounters/Data/EncounterEvent.cs index 58db1c776..7fc3834a9 100644 --- a/PKHeX.Core/Legality/Encounters/Data/EncounterEvent.cs +++ b/PKHeX.Core/Legality/Encounters/Data/EncounterEvent.cs @@ -120,7 +120,7 @@ static T[] SetArray(ICollection arr) // rather than use Linq to build an array, just do it the quick way directly. var result = new T[arr.Count]; - ((IReadOnlySet)arr).CopyTo(result); + arr.CopyTo(result, 0); return result; } diff --git a/PKHeX.Core/Legality/Restrictions/Memories/MemoryContext6Data.cs b/PKHeX.Core/Legality/Restrictions/Memories/MemoryContext6Data.cs index cf97896b3..fe890f1f3 100644 --- a/PKHeX.Core/Legality/Restrictions/Memories/MemoryContext6Data.cs +++ b/PKHeX.Core/Legality/Restrictions/Memories/MemoryContext6Data.cs @@ -107,7 +107,7 @@ public partial class MemoryContext6 /// /// 24bits of flags allowing certain feelings for a given memory index. /// - private static ReadOnlySpan MemoryFeelings => new uint[] + private static ReadOnlySpan MemoryFeelings => new[] { 0x000000, 0x04CBFD, 0x004BFD, 0x04CBFD, 0x04CBFD, 0xFFFBFB, 0x84FFF9, 0x47FFFF, 0xBF7FFA, 0x7660B0, 0x80BDF9, 0x88FB7A, 0x083F79, 0x0001FE, 0xCFEFFF, 0x84EBAF, 0xB368B0, 0x091F7E, 0x0320A0, 0x080DDD, diff --git a/PKHeX.Core/Legality/Restrictions/Memories/MemoryContext8Data.cs b/PKHeX.Core/Legality/Restrictions/Memories/MemoryContext8Data.cs index f8904d1ce..40eef5d23 100644 --- a/PKHeX.Core/Legality/Restrictions/Memories/MemoryContext8Data.cs +++ b/PKHeX.Core/Legality/Restrictions/Memories/MemoryContext8Data.cs @@ -291,7 +291,7 @@ public partial class MemoryContext8 /// 24bits of flags allowing certain feelings for a given memory index. /// /// Beware, there was an off-by-one error in the game that made Feeling 0 unobtainable, and thus the Happy feeling bit (rightmost) is omitted. - private static ReadOnlySpan MemoryFeelings => new uint[] + private static ReadOnlySpan MemoryFeelings => new[] { 0x000000, 0x04CBFD, 0x004BFD, 0x04CBFD, 0x04CBFD, 0xFFFBFB, 0x84FFF9, 0x47FFFF, 0xBF7FFA, 0x7660B0, 0x80BDF9, 0x88FB7A, 0x083F79, 0x0001FE, 0xCFEFFF, 0x84EBAF, 0xB368B0, 0x091F7E, 0x0320A0, 0x080DDD, diff --git a/PKHeX.Core/PKM/Shared/GBPKML.cs b/PKHeX.Core/PKM/Shared/GBPKML.cs index fae466ab9..d2a3536b2 100644 --- a/PKHeX.Core/PKM/Shared/GBPKML.cs +++ b/PKHeX.Core/PKM/Shared/GBPKML.cs @@ -43,7 +43,7 @@ protected GBPKML(byte[] data, bool jp = false) : base(data) RawNickname.AsSpan().Fill(StringConverter12.G1TerminatorCode); } - public override void SetNotNicknamed(int language) => GetNonNickname(language).CopyTo(RawNickname); + public override void SetNotNicknamed(int language) => GetNonNickname(language).AsSpan().CopyTo(RawNickname); protected override byte[] GetNonNickname(int language) { diff --git a/PKHeX.Core/Saves/SAV1.cs b/PKHeX.Core/Saves/SAV1.cs index 1f720fcf8..63fa9484e 100644 --- a/PKHeX.Core/Saves/SAV1.cs +++ b/PKHeX.Core/Saves/SAV1.cs @@ -77,7 +77,7 @@ private void Initialize(GameVersion versionOverride) UnpackBox(Offsets.CurrentBox, baseDest, stored, CurrentBox, capacity); } - var party = GetData(Offsets.Party, SIZE_STOREDPARTY); + var party = Data.AsSpan(Offsets.Party, SIZE_STOREDPARTY).ToArray(); var partyPL = new PokeList1(party, PokeListType.Party, Japanese); for (int i = 0; i < partyPL.Pokemon.Length; i++) { @@ -107,7 +107,7 @@ private void Initialize(GameVersion versionOverride) private void UnpackBox(int srcOfs, int destOfs, int boxSize, int boxIndex, PokeListType boxCapacity) { - var boxData = GetData(srcOfs, boxSize); + var boxData = Data.AsSpan(srcOfs, boxSize).ToArray(); var boxDest = destOfs + (boxIndex * SIZE_BOX); var boxPL = new PokeList1(boxData, boxCapacity, Japanese); for (int i = 0; i < boxPL.Pokemon.Length; i++) @@ -137,11 +137,11 @@ private void PackBox(int boxDest, int boxIndex, PokeListType boxCapacity) // copy to box location var boxData = boxPL.Write(); int boxSrc = GetBoxRawDataOffset(boxIndex); - SetData(Data, boxData, boxSrc); + SetData(Data.AsSpan(boxSrc), boxData); // copy to active loc if current box if (boxIndex == CurrentBox) - SetData(Data, boxData, Offsets.CurrentBox); + SetData(Data.AsSpan(Offsets.CurrentBox), boxData); } private const int SIZE_RESERVED = 0x8000; // unpacked box data @@ -176,7 +176,9 @@ protected override byte[] GetFinalData() int pSlot = 0; for (int i = 0; i < 6; i++) { - PK1 partyPK = GetPKM(GetData(GetPartyOffset(i), SIZE_STORED)); + var ofs = GetPartyOffset(i); + var data = Data.AsSpan(ofs, SIZE_STORED).ToArray(); + PK1 partyPK = GetPKM(data); if (partyPK.Species > 0) partyPL[pSlot++] = partyPK; } @@ -257,7 +259,7 @@ private byte GetRBYChecksum(int start, int end) public override string OT { - get => GetString(Offsets.OT, MaxStringLengthOT); + get => GetString(Data.AsSpan(Offsets.OT, MaxStringLengthOT)); set => SetString(Data.AsSpan(Offsets.OT, MaxStringLengthOT + 1), value, MaxStringLengthOT, StringConverterOption.ClearZero); } @@ -285,7 +287,7 @@ public override ushort TID16 public string Rival { - get => GetString(Offsets.Rival, MaxStringLengthOT); + get => GetString(Data.AsSpan(Offsets.Rival, MaxStringLengthOT)); set => SetString(Data.AsSpan(Offsets.Rival, MaxStringLengthOT), value, MaxStringLengthOT, StringConverterOption.Clear50); } @@ -542,20 +544,20 @@ private void SetDexFlag(int region, ushort species, bool value) SetFlag(region + ofs, bit & 7, value); } - public override void WriteSlotFormatStored(PKM pk, Span data, int offset) + public override void WriteSlotFormatStored(PKM pk, Span data) { // pk that have never been boxed have yet to save the 'current level' for box indication // set this value at this time ((PK1)pk).Stat_LevelBox = pk.CurrentLevel; - base.WriteSlotFormatStored(pk, Data, offset); + base.WriteSlotFormatStored(pk, data); } - public override void WriteBoxSlot(PKM pk, Span data, int offset) + public override void WriteBoxSlot(PKM pk, Span data) { // pk that have never been boxed have yet to save the 'current level' for box indication // set this value at this time ((PK1)pk).Stat_LevelBox = pk.CurrentLevel; - base.WriteBoxSlot(pk, Data, offset); + base.WriteBoxSlot(pk, data); } private const int SpawnFlagCount = 0xF0; diff --git a/PKHeX.Core/Saves/SAV1Stadium.cs b/PKHeX.Core/Saves/SAV1Stadium.cs index ff3aa4fd9..78510137d 100644 --- a/PKHeX.Core/Saves/SAV1Stadium.cs +++ b/PKHeX.Core/Saves/SAV1Stadium.cs @@ -204,7 +204,7 @@ public string GetTeamName(int team) var ofs = GetTeamOffset(team); var otOfs = ofs + (Japanese ? 2 : 1); - var str = GetString(otOfs, Japanese ? 5 : 7); + var str = GetString(Data.AsSpan(otOfs, Japanese ? 5 : 7)); if (string.IsNullOrWhiteSpace(str)) return name; var idOfs = ofs + (Japanese ? 0x8 : 0xC); @@ -249,25 +249,25 @@ public override SlotGroup GetTeam(int team) for (int i = 0; i < 6; i++) { var rel = ofs + ListHeaderSize + (i * SIZE_STORED); - members[i] = (PK1)GetStoredSlot(Data, rel); + members[i] = (PK1)GetStoredSlot(Data.AsSpan(rel)); } return new SlotGroup(name, members); } - public override void WriteSlotFormatStored(PKM pk, Span data, int offset) + public override void WriteSlotFormatStored(PKM pk, Span data) { // pk that have never been boxed have yet to save the 'current level' for box indication // set this value at this time ((PK1)pk).Stat_LevelBox = pk.CurrentLevel; - base.WriteSlotFormatStored(pk, Data, offset); + base.WriteSlotFormatStored(pk, data); } - public override void WriteBoxSlot(PKM pk, Span data, int offset) + public override void WriteBoxSlot(PKM pk, Span data) { // pk that have never been boxed have yet to save the 'current level' for box indication // set this value at this time ((PK1)pk).Stat_LevelBox = pk.CurrentLevel; - base.WriteBoxSlot(pk, Data, offset); + base.WriteBoxSlot(pk, data); } public static bool IsStadium(ReadOnlySpan data) diff --git a/PKHeX.Core/Saves/SAV1StadiumJ.cs b/PKHeX.Core/Saves/SAV1StadiumJ.cs index 827993919..af157ae7b 100644 --- a/PKHeX.Core/Saves/SAV1StadiumJ.cs +++ b/PKHeX.Core/Saves/SAV1StadiumJ.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -118,7 +117,7 @@ public string GetTeamName(int team) var name = $"Team {team + 1}"; var ofs = GetTeamOffset(team); - var str = GetString(ofs + 2, 5); + var str = GetString(Data.AsSpan(ofs + 2, 5)); if (string.IsNullOrWhiteSpace(str)) return name; var id = ReadUInt16BigEndian(Data.AsSpan(ofs + 8)); @@ -136,25 +135,25 @@ public override SlotGroup GetTeam(int team) for (int i = 0; i < 6; i++) { var rel = ofs + ListHeaderSize + (i * SIZE_STORED); - members[i] = (PK1)GetStoredSlot(Data, rel); + members[i] = (PK1)GetStoredSlot(Data.AsSpan(rel)); } return new SlotGroup(name, members); } - public override void WriteSlotFormatStored(PKM pk, Span data, int offset) + public override void WriteSlotFormatStored(PKM pk, Span data) { // pk that have never been boxed have yet to save the 'current level' for box indication // set this value at this time ((PK1)pk).Stat_LevelBox = pk.CurrentLevel; - base.WriteSlotFormatStored(pk, Data, offset); + base.WriteSlotFormatStored(pk, data); } - public override void WriteBoxSlot(PKM pk, Span data, int offset) + public override void WriteBoxSlot(PKM pk, Span data) { // pk that have never been boxed have yet to save the 'current level' for box indication // set this value at this time ((PK1)pk).Stat_LevelBox = pk.CurrentLevel; - base.WriteBoxSlot(pk, Data, offset); + base.WriteBoxSlot(pk, data); } public static bool IsStadium(ReadOnlySpan data) diff --git a/PKHeX.Core/Saves/SAV2.cs b/PKHeX.Core/Saves/SAV2.cs index cbbc5b475..53e221988 100644 --- a/PKHeX.Core/Saves/SAV2.cs +++ b/PKHeX.Core/Saves/SAV2.cs @@ -74,7 +74,7 @@ private void Initialize() for (int i = 0; i < BoxCount; i++) { int ofs = GetBoxRawDataOffset(i, splitAtIndex); - var box = GetData(ofs, stored); + var box = Data.AsSpan(ofs, stored).ToArray(); var boxDest = baseDest + (i * SIZE_BOX); var boxPL = new PokeList2(box, capacity, Japanese); for (int j = 0; j < boxPL.Pokemon.Length; j++) @@ -87,7 +87,7 @@ private void Initialize() } } - var current = GetData(Offsets.CurrentBox, stored); + var current = Data.AsSpan(Offsets.CurrentBox, stored).ToArray(); var curBoxPL = new PokeList2(current, capacity, Japanese); var curDest = baseDest + (CurrentBox * SIZE_BOX); for (int i = 0; i < curBoxPL.Pokemon.Length; i++) @@ -99,7 +99,7 @@ private void Initialize() pkDat.CopyTo(Data, dest); } - var party = GetData(Offsets.Party, SIZE_STOREDPARTY); + var party = Data.AsSpan(Offsets.Party, SIZE_STOREDPARTY).ToArray(); var partyPL = new PokeList2(party, PokeListType.Party, Japanese); for (int i = 0; i < partyPL.Pokemon.Length; i++) { @@ -180,7 +180,9 @@ protected override byte[] GetFinalData() int slot = 0; for (int j = 0; j < boxPL.Pokemon.Length; j++) { - PK2 boxPK = GetPKM(GetData(GetBoxOffset(i) + (j * SIZE_STORED), SIZE_STORED)); + var ofs = GetBoxOffset(i) + (j * SIZE_STORED); + var data = Data.AsSpan(ofs, SIZE_STORED).ToArray(); + PK2 boxPK = GetPKM(data); if (boxPK.Species > 0) boxPL[slot++] = boxPK; } @@ -195,7 +197,9 @@ protected override byte[] GetFinalData() int pSlot = 0; for (int i = 0; i < 6; i++) { - PK2 partyPK = GetPKM(GetData(GetPartyOffset(i), SIZE_STORED)); + var ofs = GetPartyOffset(i); + var data = Data.AsSpan(ofs, SIZE_STORED).ToArray(); + PK2 partyPK = GetPKM(data); if (partyPK.Species > 0) partyPL[pSlot++] = partyPK; } @@ -321,7 +325,7 @@ public override bool ChecksumsValid public override string OT { - get => GetString(Offsets.Trainer1 + 2, (Korean ? 2 : 1) * MaxStringLengthOT); + get => GetString(Data.AsSpan(Offsets.Trainer1 + 2, (Korean ? 2 : 1) * MaxStringLengthOT)); set => SetString(Data.AsSpan(Offsets.Trainer1 + 2, (Korean ? 2 : 1) * MaxStringLengthOT), value, 8, StringConverterOption.Clear50); } @@ -333,7 +337,7 @@ public Span OT_Trash public string Rival { - get => GetString(Offsets.Rival, (Korean ? 2 : 1) * MaxStringLengthOT); + get => GetString(Data.AsSpan(Offsets.Rival, (Korean ? 2 : 1) * MaxStringLengthOT)); set => SetString(Data.AsSpan(Offsets.Rival, (Korean ? 2 : 1) * MaxStringLengthOT), value, 8, StringConverterOption.Clear50); } @@ -557,7 +561,7 @@ public bool CurrentBoxChanged public override string GetBoxName(int box) { int len = Korean ? 17 : 9; - return GetString(Offsets.BoxNames + (box * len), len); + return GetString(Data.AsSpan(Offsets.BoxNames + (box * len), len)); } public override void SetBoxName(int box, ReadOnlySpan value) diff --git a/PKHeX.Core/Saves/SAV2Stadium.cs b/PKHeX.Core/Saves/SAV2Stadium.cs index 2f0d0d22d..44912afd9 100644 --- a/PKHeX.Core/Saves/SAV2Stadium.cs +++ b/PKHeX.Core/Saves/SAV2Stadium.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -137,7 +136,7 @@ public string GetTeamName(int team) var name = $"{((Stadium2TeamType) (team / TeamCountType)).ToString().Replace('_', ' ')} {(team % 10) + 1}"; var ofs = GetTeamOffset(team); - var str = GetString(ofs + 4, 7); + var str = GetString(Data.AsSpan(ofs + 4, 7)); if (string.IsNullOrWhiteSpace(str)) return name; var id = ReadUInt16BigEndian(Data.AsSpan(ofs + 2)); @@ -147,7 +146,8 @@ public string GetTeamName(int team) public override string GetBoxName(int box) { var ofs = GetBoxOffset(box) - 0x10; - var str = GetString(ofs, 0x10); + var boxNameSpan = Data.AsSpan(ofs, 0x10); + var str = GetString(boxNameSpan); if (string.IsNullOrWhiteSpace(str)) return $"Box {box + 1}"; return str; @@ -164,7 +164,7 @@ public override SlotGroup GetTeam(int team) for (int i = 0; i < 6; i++) { var rel = ofs + ListHeaderSizeTeam + (i * SIZE_STORED); - members[i] = (SK2)GetStoredSlot(Data, rel); + members[i] = (SK2)GetStoredSlot(Data.AsSpan(rel)); } return new SlotGroup(name, members); } diff --git a/PKHeX.Core/Saves/SAV3.cs b/PKHeX.Core/Saves/SAV3.cs index 8a36eff29..3ce6c7c9a 100644 --- a/PKHeX.Core/Saves/SAV3.cs +++ b/PKHeX.Core/Saves/SAV3.cs @@ -197,8 +197,8 @@ public void WriteBothSaveSlots(Span data) protected sealed override PK3 GetPKM(byte[] data) => new(data); protected sealed override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray3(data); - protected sealed override byte[] BoxBuffer => Storage; - protected sealed override byte[] PartyBuffer => Large; + protected sealed override Span BoxBuffer => Storage; + protected sealed override Span PartyBuffer => Large; private const int COUNT_BOX = 14; private const int COUNT_SLOTSPERBOX = 30; @@ -633,9 +633,9 @@ public override void CopyChangesFrom(SaveFile sav) { SetData(sav.Data, 0); var s3 = (SAV3)sav; - SetData(Small, s3.Small, 0); - SetData(Large, s3.Large, 0); - SetData(Storage, s3.Storage, 0); + SetData(Small, s3.Small); + SetData(Large, s3.Large); + SetData(Storage, s3.Storage); } #region External Connections diff --git a/PKHeX.Core/Saves/SAV3Colosseum.cs b/PKHeX.Core/Saves/SAV3Colosseum.cs index 8ab9ce1fd..9724cd2e7 100644 --- a/PKHeX.Core/Saves/SAV3Colosseum.cs +++ b/PKHeX.Core/Saves/SAV3Colosseum.cs @@ -65,7 +65,9 @@ private StrategyMemo Initialize() // Count up how many party slots are active. for (int i = 0; i < 6; i++) { - if (GetPartySlot(Data, GetPartyOffset(i)).Species != 0) + var ofs = GetPartyOffset(i); + var span = Data.AsSpan(ofs); + if (ReadUInt16BigEndian(span) != 0) // species is at offset 0x00 PartyCount++; } @@ -278,14 +280,16 @@ public override int GetBoxOffset(int box) return Box + (((30 * SIZE_STORED) + 0x14)*box) + 0x14; } + private Span GetBoxNameSpan(int box) => Data.AsSpan(Box + (0x24A4 * box), 16); + public override string GetBoxName(int box) { - return GetString(Box + (0x24A4 * box), 16); + return GetString(GetBoxNameSpan(box)); } public override void SetBoxName(int box, ReadOnlySpan value) { - SetString(Data.AsSpan(Box + (0x24A4 * box), 16), value, 8, StringConverterOption.ClearZero); + SetString(GetBoxNameSpan(box), value, 8, StringConverterOption.ClearZero); } protected override CK3 GetPKM(byte[] data) @@ -363,8 +367,8 @@ public override int PlayedSeconds } // Trainer Info (offset 0x78, length 0xB18, end @ 0xB90) - public override string OT { get => GetString(0x78, 20); set { SetString(Data.AsSpan(0x78, 20), value, 10, StringConverterOption.ClearZero); OT2 = value; } } - public string OT2 { get => GetString(0x8C, 20); set => SetString(Data.AsSpan(0x8C, 20), value, 10, StringConverterOption.ClearZero); } + public override string OT { get => GetString(Data.AsSpan(0x78, 20)); set { SetString(Data.AsSpan(0x78, 20), value, 10, StringConverterOption.ClearZero); OT2 = value; } } + public string OT2 { get => GetString(Data.AsSpan(0x8C, 20)); set => SetString(Data.AsSpan(0x8C, 20), value, 10, StringConverterOption.ClearZero); } public override uint ID32 { get => ReadUInt32BigEndian(Data.AsSpan(0xA4)); set => WriteUInt32BigEndian(Data.AsSpan(0xA4), value); } public override ushort SID16 { get => ReadUInt16BigEndian(Data.AsSpan(0xA4)); set => WriteUInt16BigEndian(Data.AsSpan(0xA4), value); } @@ -373,7 +377,7 @@ public override int PlayedSeconds public override int Gender { get => Data[0xAF8]; set => Data[0xAF8] = (byte)value; } public override uint Money { get => ReadUInt32BigEndian(Data.AsSpan(0xAFC)); set => WriteUInt32BigEndian(Data.AsSpan(0xAFC), value); } public uint Coupons { get => ReadUInt32BigEndian(Data.AsSpan(0xB00)); set => WriteUInt32BigEndian(Data.AsSpan(0xB00), value); } - public string RUI_Name { get => GetString(0xB3A, 20); set => SetString(Data.AsSpan(0xB3A, 20), value, 10, StringConverterOption.ClearZero); } + public string RUI_Name { get => GetString(Data.AsSpan(0xB3A, 20)); set => SetString(Data.AsSpan(0xB3A, 20), value, 10, StringConverterOption.ClearZero); } public override IReadOnlyList Inventory { diff --git a/PKHeX.Core/Saves/SAV3E.cs b/PKHeX.Core/Saves/SAV3E.cs index b43f6f65c..f374171b6 100644 --- a/PKHeX.Core/Saves/SAV3E.cs +++ b/PKHeX.Core/Saves/SAV3E.cs @@ -61,14 +61,14 @@ public override uint SecurityKey public RTC3 ClockInitial { - get => new(GetData(Small, 0x98, RTC3.Size)); - set => SetData(Small, value.Data, 0x98); + get => new(Small.AsSpan(0x98, RTC3.Size).ToArray()); + set => SetData(Small.AsSpan(0x98), value.Data); } public RTC3 ClockElapsed { - get => new(GetData(Small, 0xA0, RTC3.Size)); - set => SetData(Small, value.Data, 0xA0); + get => new(Small.AsSpan(0xA0, RTC3.Size).ToArray()); + set => SetData(Small.AsSpan(0xA0), value.Data); } public ushort JoyfulJumpInRow { get => ReadUInt16LittleEndian(Small.AsSpan(0x1FC)); set => WriteUInt16LittleEndian(Small.AsSpan(0x1FC), Math.Min((ushort)9999, value)); } @@ -146,7 +146,7 @@ protected override InventoryPouch3[] GetItems() public PokeBlock3Case PokeBlocks { get => new(Large, 0x848); - set => SetData(Large, value.Write(), 0x848); + set => SetData(Large.AsSpan(0x848), value.Write()); } protected override int SeenOffset2 => 0x988; @@ -155,8 +155,8 @@ public PokeBlock3Case PokeBlocks public Swarm3 Swarm { - get => new(Large.Slice(0x2B90, Swarm3.SIZE)); - set => SetData(Large, value.Data, 0x2B90); + get => new(Large.AsSpan(0x2B90, Swarm3.SIZE).ToArray()); + set => SetData(Large.AsSpan(0x2B90), value.Data); } private void ClearSwarm() => Large.AsSpan(0x2B90, Swarm3.SIZE).Clear(); @@ -205,9 +205,9 @@ public int SwarmIndex private int WonderCardOffset => WonderNewsOffset + (Japanese ? WonderNews3.SIZE_JAP : WonderNews3.SIZE); private int WonderCardExtraOffset => WonderCardOffset + (Japanese ? WonderCard3.SIZE_JAP : WonderCard3.SIZE); - public WonderNews3 WonderNews { get => new(Large.Slice(WonderNewsOffset, Japanese ? WonderNews3.SIZE_JAP : WonderNews3.SIZE)); set => SetData(Large, value.Data, WonderOffset); } - public WonderCard3 WonderCard { get => new(Large.Slice(WonderCardOffset, Japanese ? WonderCard3.SIZE_JAP : WonderCard3.SIZE)); set => SetData(Large, value.Data, WonderCardOffset); } - public WonderCard3Extra WonderCardExtra { get => new(Large.Slice(WonderCardExtraOffset, WonderCard3Extra.SIZE)); set => SetData(Large, value.Data, WonderCardExtraOffset); } + public WonderNews3 WonderNews { get => new(Large.Slice(WonderNewsOffset, Japanese ? WonderNews3.SIZE_JAP : WonderNews3.SIZE)); set => SetData(Large.AsSpan(WonderOffset), value.Data); } + public WonderCard3 WonderCard { get => new(Large.Slice(WonderCardOffset, Japanese ? WonderCard3.SIZE_JAP : WonderCard3.SIZE)); set => SetData(Large.AsSpan(WonderCardOffset), value.Data); } + public WonderCard3Extra WonderCardExtra { get => new(Large.Slice(WonderCardExtraOffset, WonderCard3Extra.SIZE)); set => SetData(Large.AsSpan(WonderCardExtraOffset), value.Data); } // 0x338: 4 easy chat words // 0x340: news MENewsJisanStruct // 0x344: uint[5], uint[5] tracking? @@ -215,10 +215,10 @@ public int SwarmIndex public override Gen3MysteryData MysteryData { get => new MysteryEvent3(Large.Slice(0x3728, MysteryEvent3.SIZE)); - set => SetData(Large, value.Data, 0x3728); + set => SetData(Large.AsSpan(0x3728), value.Data); } - public RecordMixing3Gift RecordMixingGift { get => new(Large.Slice(0x3B14, RecordMixing3Gift.SIZE)); set => SetData(Large, value.Data, 0x3B14); } + public RecordMixing3Gift RecordMixingGift { get => new(Large.Slice(0x3B14, RecordMixing3Gift.SIZE)); set => SetData(Large.AsSpan(0x3B14), value.Data); } protected override int SeenOffset3 => 0x3B24; @@ -237,6 +237,6 @@ public override Gen3MysteryData MysteryData public BV3 BattleVideo { get => !HasBattleVideo ? new BV3() : new BV3(Data.Slice(OFS_BV + 4, BV3.SIZE)); - set => SetData(Data, value.Data, OFS_BV + 4); + set => SetData(Data.AsSpan(OFS_BV + 4, BV3.SIZE), value.Data); } } diff --git a/PKHeX.Core/Saves/SAV3FRLG.cs b/PKHeX.Core/Saves/SAV3FRLG.cs index be6d77eea..a941f3970 100644 --- a/PKHeX.Core/Saves/SAV3FRLG.cs +++ b/PKHeX.Core/Saves/SAV3FRLG.cs @@ -162,9 +162,9 @@ protected override InventoryPouch3[] GetItems() private int WonderCardOffset => WonderNewsOffset + (Japanese ? WonderNews3.SIZE_JAP : WonderNews3.SIZE); private int WonderCardExtraOffset => WonderCardOffset + (Japanese ? WonderCard3.SIZE_JAP : WonderCard3.SIZE); - public WonderNews3 WonderNews { get => new(Large.Slice(WonderNewsOffset, Japanese ? WonderNews3.SIZE_JAP : WonderNews3.SIZE)); set => SetData(Large, value.Data, WonderOffset); } - public WonderCard3 WonderCard { get => new(Large.Slice(WonderCardOffset, Japanese ? WonderCard3.SIZE_JAP : WonderCard3.SIZE)); set => SetData(Large, value.Data, WonderCardOffset); } - public WonderCard3Extra WonderCardExtra { get => new(Large.Slice(WonderCardExtraOffset, WonderCard3Extra.SIZE)); set => SetData(Large, value.Data, WonderCardExtraOffset); } + public WonderNews3 WonderNews { get => new(Large.Slice(WonderNewsOffset, Japanese ? WonderNews3.SIZE_JAP : WonderNews3.SIZE)); set => SetData(Large.AsSpan(WonderOffset), value.Data); } + public WonderCard3 WonderCard { get => new(Large.Slice(WonderCardOffset, Japanese ? WonderCard3.SIZE_JAP : WonderCard3.SIZE)); set => SetData(Large.AsSpan(WonderCardOffset), value.Data); } + public WonderCard3Extra WonderCardExtra { get => new(Large.Slice(WonderCardExtraOffset, WonderCard3Extra.SIZE)); set => SetData(Large.AsSpan(WonderCardExtraOffset), value.Data); } // 0x338: 4 easy chat words // 0x340: news MENewsJisanStruct // 0x344: uint[5], uint[5] tracking? @@ -172,7 +172,7 @@ protected override InventoryPouch3[] GetItems() public override Gen3MysteryData MysteryData { get => new MysteryEvent3(Large.Slice(0x361C, MysteryEvent3.SIZE)); - set => SetData(Large, value.Data, 0x361C); + set => SetData(Large.AsSpan(0x361C), value.Data); } protected override int SeenOffset3 => 0x3A18; diff --git a/PKHeX.Core/Saves/SAV3RS.cs b/PKHeX.Core/Saves/SAV3RS.cs index 53801990e..18efd9ed8 100644 --- a/PKHeX.Core/Saves/SAV3RS.cs +++ b/PKHeX.Core/Saves/SAV3RS.cs @@ -57,14 +57,14 @@ public override bool NationalDex public RTC3 ClockInitial { - get => new(GetData(Small, 0x98, RTC3.Size)); - set => SetData(Small, value.Data, 0x98); + get => new(Small.AsSpan(0x98, RTC3.Size).ToArray()); + set => SetData(Small.AsSpan(0x98), value.Data); } public RTC3 ClockElapsed { - get => new(GetData(Small, 0xA0, RTC3.Size)); - set => SetData(Small, value.Data, 0xA0); + get => new(Small.AsSpan(0xA0, RTC3.Size).ToArray()); + set => SetData(Small.AsSpan(0xA0), value.Data); } #endregion @@ -109,7 +109,7 @@ protected override InventoryPouch3[] GetItems() public PokeBlock3Case PokeBlocks { get => new(Large, 0x7F8); - set => SetData(Large, value.Write(), 0x7F8); + set => SetData(Large.AsSpan(0x7F8), value.Write()); } protected override int SeenOffset2 => 0x938; @@ -119,7 +119,7 @@ public PokeBlock3Case PokeBlocks public Swarm3 Swarm { get => new(Large.Slice(0x2AFC, Swarm3.SIZE)); - set => SetData(Large, value.Data, 0x2AFC); + set => SetData(Large.AsSpan(0x2AFC), value.Data); } private void ClearSwarm() => Large.AsSpan(0x2AFC, Swarm3.SIZE).Clear(); @@ -166,10 +166,10 @@ public int SwarmIndex public override Gen3MysteryData MysteryData { get => new MysteryEvent3RS(Large.Slice(0x3690, MysteryEvent3.SIZE)); - set => SetData(Large, value.Data, 0x3690); + set => SetData(Large.AsSpan(0x3690), value.Data); } - public RecordMixing3Gift RecordMixingGift { get => new(Large.Slice(0x3A7C, RecordMixing3Gift.SIZE)); set => SetData(Large, value.Data, 0x3A7C); } + public RecordMixing3Gift RecordMixingGift { get => new(Large.Slice(0x3A7C, RecordMixing3Gift.SIZE)); set => SetData(Large.AsSpan(0x3A7C), value.Data); } protected override int SeenOffset3 => 0x3A8C; #endregion diff --git a/PKHeX.Core/Saves/SAV3RSBox.cs b/PKHeX.Core/Saves/SAV3RSBox.cs index f99ae67d6..f93fa5017 100644 --- a/PKHeX.Core/Saves/SAV3RSBox.cs +++ b/PKHeX.Core/Saves/SAV3RSBox.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using static System.Buffers.Binary.BinaryPrimitives; @@ -52,7 +51,7 @@ private void InitializeData() Array.Copy(Data, b.Offset + 0xC, Data, (int) (Box + (b.ID * copySize)), copySize); } - private static BlockInfoRSBOX[] ReadBlocks(byte[] data) + private static BlockInfoRSBOX[] ReadBlocks(ReadOnlySpan data) { var blocks = new BlockInfoRSBOX[2 * BLOCK_COUNT]; for (int i = 0; i < blocks.Length; i++) @@ -91,7 +90,7 @@ private byte[] GetInnerData() SetChecksums(); - return GetData(0, Data.Length - SIZE_RESERVED); + return Data[..^SIZE_RESERVED]; } // Configuration @@ -140,11 +139,16 @@ public override int CurrentBox set => Data[Box + 4] = (byte)(value / 2); } + private Span GetBoxNameSpan(int box) + { + int offset = Box + 0x1EC38 + (9 * box); + return Data.AsSpan(offset, 9); + } + protected override int GetBoxWallpaperOffset(int box) { // Box Wallpaper is directly after the Box Names - int offset = Box + 0x1ED19 + (box / 2); - return offset; + return Box + 0x1ED19 + (box / 2); } public override string GetBoxName(int box) @@ -155,18 +159,18 @@ public override string GetBoxName(int box) string boxName = $"[{lo:00}-{hi:00}] "; box /= 2; - int offset = Box + 0x1EC38 + (9 * box); - if (Data[offset] is 0 or 0xFF) + var span = GetBoxNameSpan(box); + if (span[0] is 0 or 0xFF) boxName += $"BOX {box + 1}"; - boxName += GetString(offset, 9); + else + boxName += GetString(span); return boxName; } public override void SetBoxName(int box, ReadOnlySpan value) { - int offset = Box + 0x1EC38 + (9 * box); - var span = Data.AsSpan(offset, 9); + var span = GetBoxNameSpan(box); if (value == $"BOX {box + 1}") { span.Clear(); @@ -191,9 +195,9 @@ protected override byte[] DecryptPKM(byte[] data) protected override void SetDex(PKM pk) { /* No Pokedex for this game, do nothing */ } - public override void WriteBoxSlot(PKM pk, Span data, int offset) + public override void WriteBoxSlot(PKM pk, Span data) { - base.WriteBoxSlot(pk, data, offset); + base.WriteBoxSlot(pk, data); WriteUInt16LittleEndian(data[(PokeCrypto.SIZE_3STORED)..], pk.TID16); WriteUInt16LittleEndian(data[(PokeCrypto.SIZE_3STORED + 2)..], pk.SID16); } diff --git a/PKHeX.Core/Saves/SAV3XD.cs b/PKHeX.Core/Saves/SAV3XD.cs index 58c940087..f5f5767a8 100644 --- a/PKHeX.Core/Saves/SAV3XD.cs +++ b/PKHeX.Core/Saves/SAV3XD.cs @@ -131,7 +131,9 @@ private void Initialize() // Count up how many party slots are active. for (int i = 0; i < 6; i++) { - if (GetPartySlot(Data, GetPartyOffset(i)).Species != 0) + var ofs = GetPartyOffset(i); + var span = Data.AsSpan(ofs); + if (ReadUInt16BigEndian(span) != 0) // species is at offset 0x00 PartyCount++; } } @@ -320,7 +322,7 @@ public override int PlayedSeconds // Trainer Info public override GameVersion Version { get => GameVersion.XD; protected set { } } - public override string OT { get => GetString(Trainer1 + 0x00, 20); set => SetString(Data.AsSpan(Trainer1 + 0x00, 20), value, 10, StringConverterOption.ClearZero); } + public override string OT { get => GetString(Data.AsSpan(Trainer1 + 0x00, 20)); set => SetString(Data.AsSpan(Trainer1 + 0x00, 20), value, 10, StringConverterOption.ClearZero); } public override uint ID32 { get => ReadUInt32BigEndian(Data.AsSpan(Trainer1 + 0x2C)); set => WriteUInt32BigEndian(Data.AsSpan(Trainer1 + 0x2C), value); } public override ushort SID16 { get => ReadUInt16BigEndian(Data.AsSpan(Trainer1 + 0x2C)); set => WriteUInt16BigEndian(Data.AsSpan(Trainer1 + 0x2C), value); } public override ushort TID16 { get => ReadUInt16BigEndian(Data.AsSpan(Trainer1 + 0x2E)); set => WriteUInt16BigEndian(Data.AsSpan(Trainer1 + 0x2E), value); } @@ -333,7 +335,7 @@ public override int PlayedSeconds public override int GetPartyOffset(int slot) => Party + (SIZE_STORED * slot); private int GetBoxInfoOffset(int box) => Box + (((30 * SIZE_STORED) + 0x14) * box); public override int GetBoxOffset(int box) => GetBoxInfoOffset(box) + 20; - public override string GetBoxName(int box) => GetString(GetBoxInfoOffset(box), 16); + public override string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxInfoOffset(box), 16)); public override void SetBoxName(int box, ReadOnlySpan value) { @@ -348,12 +350,12 @@ protected override XK3 GetPKM(byte[] data) } protected override byte[] DecryptPKM(byte[] data) => data; - public override XK3 GetPartySlot(byte[] data, int offset) => GetStoredSlot(data, offset); + public override XK3 GetPartySlot(ReadOnlySpan data) => GetStoredSlot(data); - public override XK3 GetStoredSlot(byte[] data, int offset) + public override XK3 GetStoredSlot(ReadOnlySpan data) { // Get Shadow Data - var pk = (XK3)base.GetStoredSlot(data, offset); + var pk = (XK3)base.GetStoredSlot(data); if (pk.ShadowID > 0 && pk.ShadowID < ShadowInfo.Count) pk.Purification = ShadowInfo[pk.ShadowID].Purification; return pk; diff --git a/PKHeX.Core/Saves/SAV4.cs b/PKHeX.Core/Saves/SAV4.cs index 0cd5f1498..3f540eef9 100644 --- a/PKHeX.Core/Saves/SAV4.cs +++ b/PKHeX.Core/Saves/SAV4.cs @@ -23,8 +23,8 @@ public abstract class SAV4 : SaveFile, IEventFlag37 // SaveData is chunked into two pieces. protected readonly byte[] Storage; public readonly byte[] General; - protected sealed override byte[] BoxBuffer => Storage; - protected sealed override byte[] PartyBuffer => General; + protected sealed override Span BoxBuffer => Storage; + protected sealed override Span PartyBuffer => General; protected abstract int StorageStart { get; } public abstract Zukan4 Dex { get; } @@ -48,16 +48,16 @@ protected SAV4(byte[] data, int gSize, int sSize, int sStart) : base(data) var gbo = (GeneralBlockPosition == 0 ? 0 : PartitionSize); var sbo = (StorageBlockPosition == 0 ? 0 : PartitionSize) + sStart; - General = GetData(gbo, gSize); - Storage = GetData(sbo, sSize); + General = Data.AsSpan(gbo, gSize).ToArray(); + Storage = Data.AsSpan(sbo, sSize).ToArray(); } // Configuration protected sealed override SAV4 CloneInternal() { var sav = CloneInternal4(); - SetData(sav.General, General, 0); - SetData(sav.Storage, Storage, 0); + SetData(sav.General, General); + SetData(sav.Storage, Storage); return sav; } @@ -67,8 +67,8 @@ public sealed override void CopyChangesFrom(SaveFile sav) { SetData(sav.Data, 0); var s4 = (SAV4)sav; - SetData(General, s4.General, 0); - SetData(Storage, s4.Storage, 0); + SetData(General, s4.General); + SetData(Storage, s4.Storage); } protected sealed override int SIZE_STORED => PokeCrypto.SIZE_4STORED; @@ -432,12 +432,12 @@ protected sealed override DataMysteryGift[] MysteryGiftCards for (int i = 0; i < 8; i++) // 8 PGT { if (value[i] is PGT) - SetData(General, value[i].Data, WondercardData + (i *PGT.Size)); + SetData(General.AsSpan(WondercardData + (i * PGT.Size)), value[i].Data); } for (int i = 8; i < 11; i++) // 3 PCD { if (value[i] is PCD) - SetData(General, value[i].Data, WondercardData + (8 *PGT.Size) + ((i - 8)*PCD.Size)); + SetData(General.AsSpan(WondercardData + (8 * PGT.Size) + ((i - 8) * PCD.Size)), value[i].Data); } if (this is SAV4HGSS hgss && value.Length >= 11 && value[^1] is PCD capsule) hgss.LockCapsuleSlot = capsule; @@ -531,7 +531,7 @@ public void SetEventFlag(int flagNumber, bool value) private const byte SealMaxCount = 99; public byte[] GetSealCase() => General.Slice(Seal, (int)Seal4.MAX); - public void SetSealCase(byte[] value) => SetData(General, value, Seal); + public void SetSealCase(ReadOnlySpan value) => SetData(General.AsSpan(Seal), value); public byte GetSealCount(Seal4 id) => General[Seal + (int)id]; public byte SetSealCount(Seal4 id, byte count) => General[Seal + (int)id] = Math.Min(SealMaxCount, count); diff --git a/PKHeX.Core/Saves/SAV4BR.cs b/PKHeX.Core/Saves/SAV4BR.cs index 75c17088c..84ef086fe 100644 --- a/PKHeX.Core/Saves/SAV4BR.cs +++ b/PKHeX.Core/Saves/SAV4BR.cs @@ -200,16 +200,21 @@ public override ushort SID16 private int BoxName = -1; private const int BoxNameLength = 0x28; + private Span GetBoxNameSpan(int box) + { + int ofs = BoxName + (box * BoxNameLength); + return Data.AsSpan(ofs, BoxNameLength); + } + public override string GetBoxName(int box) { if (BoxName < 0) return $"BOX {box + 1}"; - int ofs = BoxName + (box * BoxNameLength); - var span = Data.AsSpan(ofs, BoxNameLength); + var span = GetBoxNameSpan(box); if (ReadUInt16BigEndian(span) == 0) return $"BOX {box + 1}"; - return GetString(ofs, BoxNameLength); + return GetString(span); } public override void SetBoxName(int box, ReadOnlySpan value) @@ -217,8 +222,7 @@ public override void SetBoxName(int box, ReadOnlySpan value) if (BoxName < 0) return; - int ofs = BoxName + (box * BoxNameLength); - var span = Data.AsSpan(ofs, BoxNameLength); + var span = GetBoxNameSpan(box); if (ReadUInt16BigEndian(span) == 0) return; diff --git a/PKHeX.Core/Saves/SAV4Sinnoh.cs b/PKHeX.Core/Saves/SAV4Sinnoh.cs index 7f0110a02..4fabd1fa8 100644 --- a/PKHeX.Core/Saves/SAV4Sinnoh.cs +++ b/PKHeX.Core/Saves/SAV4Sinnoh.cs @@ -102,7 +102,7 @@ public uint PoketchStepCounter public void SetPoketchDotArtistData(ReadOnlySpan value) { if (value.Length != 120) - throw new ArgumentException($"Expected {120} bytes.", nameof(value.Length)); + throw new ArgumentOutOfRangeException($"Expected {120} bytes.", nameof(value.Length)); value.CopyTo(General.AsSpan(PoketchStart + 0x2A)); } @@ -124,7 +124,7 @@ public HoneyTreeValue GetHoneyTree(int index) public void SetHoneyTree(HoneyTreeValue tree, int index) { if (index <= 21) - SetData(General, tree.Data, OFS_HONEY + (HONEY_SIZE * index)); + SetData(General.AsSpan(OFS_HONEY + (HONEY_SIZE * index)), tree.Data); } public MunchlaxTreeSet4 GetMunchlaxTrees() => CalculateMunchlaxTrees(TID16, SID16); diff --git a/PKHeX.Core/Saves/SAV5.cs b/PKHeX.Core/Saves/SAV5.cs index 8ca380274..30a6c0a93 100644 --- a/PKHeX.Core/Saves/SAV5.cs +++ b/PKHeX.Core/Saves/SAV5.cs @@ -212,7 +212,7 @@ public byte[] CGearSkinData public EntreeForest EntreeData { - get => new(GetData(EntreeForestOffset, EntreeForest.SIZE)); + get => new(Data.AsSpan(EntreeForestOffset, EntreeForest.SIZE).ToArray()); set => SetData(value.Write(), EntreeForestOffset); } @@ -230,7 +230,7 @@ public EntreeForest EntreeData public abstract Encount5 Encount { get; } public static int GetMailOffset(int index) => (index * Mail5.SIZE) + 0x1DD00; - public byte[] GetMailData(int offset) => GetData(offset, Mail5.SIZE); + public byte[] GetMailData(int offset) => Data.AsSpan(offset, Mail5.SIZE).ToArray(); public int GetBattleBoxSlot(int slot) => BattleBoxOffset + (slot * SIZE_STORED); public MailDetail GetMail(int mailIndex) diff --git a/PKHeX.Core/Saves/SAV6AO.cs b/PKHeX.Core/Saves/SAV6AO.cs index 289af6fc4..8570ae6e4 100644 --- a/PKHeX.Core/Saves/SAV6AO.cs +++ b/PKHeX.Core/Saves/SAV6AO.cs @@ -175,7 +175,7 @@ public override void SetDaycareHasEgg(int loc, bool hasEgg) } public override string JPEGTitle => !HasJPPEGData ? string.Empty : StringConverter6.GetString(Data.AsSpan(JPEG, 0x1A)); - public override byte[] GetJPEGData() => !HasJPPEGData ? Array.Empty() : GetData(JPEG + 0x54, 0xE004); + public override byte[] GetJPEGData() => !HasJPPEGData ? Array.Empty() : Data.AsSpan(JPEG + 0x54, 0xE004).ToArray(); private bool HasJPPEGData => Data[JPEG + 0x54] == 0xFF; protected override bool[] MysteryGiftReceivedFlags { get => Blocks.MysteryGift.GetReceivedFlags(); set => Blocks.MysteryGift.SetReceivedFlags(value); } diff --git a/PKHeX.Core/Saves/SAV6XY.cs b/PKHeX.Core/Saves/SAV6XY.cs index 7880a70e9..7c975fa66 100644 --- a/PKHeX.Core/Saves/SAV6XY.cs +++ b/PKHeX.Core/Saves/SAV6XY.cs @@ -112,7 +112,7 @@ public override void SetDaycareRNGSeed(int loc, string seed) } public override string JPEGTitle => !HasJPPEGData ? string.Empty : StringConverter6.GetString(Data.AsSpan(JPEG, 0x1A)); - public override byte[] GetJPEGData() => !HasJPPEGData ? Array.Empty() : GetData(JPEG + 0x54, 0xE004); + public override byte[] GetJPEGData() => !HasJPPEGData ? Array.Empty() : Data.AsSpan(JPEG + 0x54, 0xE004).ToArray(); private bool HasJPPEGData => Data[JPEG + 0x54] == 0xFF; public void UnlockAllFriendSafariSlots() diff --git a/PKHeX.Core/Saves/SAV8BS.cs b/PKHeX.Core/Saves/SAV8BS.cs index b598fd3de..83a457be0 100644 --- a/PKHeX.Core/Saves/SAV8BS.cs +++ b/PKHeX.Core/Saves/SAV8BS.cs @@ -283,7 +283,7 @@ public override int SetString(Span destBuffer, ReadOnlySpan value, i public string Rival { - get => GetString(0x55F4, 0x1A); + get => GetString(Data.AsSpan(0x55F4, 0x1A)); set => SetString(Data.AsSpan(0x55F4, 0x1A), value, MaxStringLengthOT, StringConverterOption.ClearZero); } @@ -333,7 +333,7 @@ public override int PartyCount } public override PB8 GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data)); - public override PB8 GetBoxSlot(int offset) => GetDecryptedPKM(GetData(Data, offset, SIZE_PARTY)); // party format in boxes! + public override PB8 GetBoxSlot(int offset) => GetDecryptedPKM(Data.AsSpan(offset, SIZE_PARTY).ToArray()); // party format in boxes! public enum TopMenuItemType { diff --git a/PKHeX.Core/Saves/SAV8LA.cs b/PKHeX.Core/Saves/SAV8LA.cs index 9231c879d..6e9ea668f 100644 --- a/PKHeX.Core/Saves/SAV8LA.cs +++ b/PKHeX.Core/Saves/SAV8LA.cs @@ -130,8 +130,8 @@ protected override SAV8LA CloneInternal() public override int PlayedMinutes { get => Played.PlayedMinutes; set => Played.PlayedMinutes = (byte)value; } public override int PlayedSeconds { get => Played.PlayedSeconds; set => Played.PlayedSeconds = (byte)value; } - protected override byte[] BoxBuffer => BoxInfo.Data; - protected override byte[] PartyBuffer => PartyInfo.Data; + protected override Span BoxBuffer => BoxInfo.Data; + protected override Span PartyBuffer => PartyInfo.Data; private void Initialize() { diff --git a/PKHeX.Core/Saves/SAV8SWSH.cs b/PKHeX.Core/Saves/SAV8SWSH.cs index 5ba223e44..dba4e4246 100644 --- a/PKHeX.Core/Saves/SAV8SWSH.cs +++ b/PKHeX.Core/Saves/SAV8SWSH.cs @@ -246,10 +246,10 @@ public override int PartyCount protected set => PartyInfo.PartyCount = value; } - protected override byte[] BoxBuffer => BoxInfo.Data; - protected override byte[] PartyBuffer => PartyInfo.Data; + protected override Span BoxBuffer => BoxInfo.Data; + protected override Span PartyBuffer => PartyInfo.Data; public override PK8 GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data)); - public override PK8 GetBoxSlot(int offset) => GetDecryptedPKM(GetData(BoxInfo.Data, offset, SIZE_PARTY)); // party format in boxes! + public override PK8 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.AsSpan(offset, SIZE_PARTY).ToArray()); // party format in boxes! public int GetRecord(int recordID) => Records.GetRecord(recordID); public void SetRecord(int recordID, int value) => Records.SetRecord(recordID, value); diff --git a/PKHeX.Core/Saves/SAV9SV.cs b/PKHeX.Core/Saves/SAV9SV.cs index 82ed014c9..868a4df9d 100644 --- a/PKHeX.Core/Saves/SAV9SV.cs +++ b/PKHeX.Core/Saves/SAV9SV.cs @@ -217,10 +217,10 @@ public override int PartyCount protected set => PartyInfo.PartyCount = value; } - protected override byte[] BoxBuffer => BoxInfo.Data; - protected override byte[] PartyBuffer => PartyInfo.Data; + protected override Span BoxBuffer => BoxInfo.Data; + protected override Span PartyBuffer => PartyInfo.Data; public override PK9 GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data)); - public override PK9 GetBoxSlot(int offset) => GetDecryptedPKM(GetData(BoxInfo.Data, offset, SIZE_PARTY)); // party format in boxes! + public override PK9 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.AsSpan(offset, SIZE_PARTY).ToArray()); // party format in boxes! //public int GetRecord(int recordID) => Records.GetRecord(recordID); //public void SetRecord(int recordID, int value) => Records.SetRecord(recordID, value); diff --git a/PKHeX.Core/Saves/SaveFile.cs b/PKHeX.Core/Saves/SaveFile.cs index 131631e20..a2c596205 100644 --- a/PKHeX.Core/Saves/SaveFile.cs +++ b/PKHeX.Core/Saves/SaveFile.cs @@ -67,19 +67,15 @@ protected virtual byte[] GetFinalData() #endregion #region Savedata Container Handling - public byte[] GetData(int offset, int length) => GetData(Data, offset, length); - 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(ReadOnlySpan input, int offset) => SetData(Data, input, offset); + public void SetData(ReadOnlySpan input, int offset) => SetData(Data.AsSpan(offset), input); - public void SetData(Span dest, ReadOnlySpan input, int offset) + public void SetData(Span dest, ReadOnlySpan input) { - input.CopyTo(dest[offset..]); + input.CopyTo(dest); State.Edited = true; } 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 @@ -206,7 +202,7 @@ public IList PartyData PKM[] data = new PKM[count]; for (int i = 0; i < data.Length; i++) - data[i] = GetPartySlot(PartyBuffer, GetPartyOffset(i)); + data[i] = GetPartySlotAtIndex(i); return data; } set @@ -219,10 +215,10 @@ public IList PartyData #endif int ctr = 0; foreach (var exist in value.Where(pk => pk.Species != 0)) - SetPartySlot(exist, PartyBuffer, GetPartyOffset(ctr++)); + SetPartySlotAtIndex(exist, ctr++); PartyCount = ctr; for (int i = ctr; i < 6; i++) - SetPartySlot(BlankPKM, PartyBuffer, GetPartyOffset(i)); + SetPartySlotAtIndex(BlankPKM, i); } } #endregion @@ -248,7 +244,8 @@ public IList PartyData public virtual void SetDaycareOccupied(int loc, int slot, bool occupied) { } #endregion - public PKM GetPartySlotAtIndex(int index) => GetPartySlot(PartyBuffer, GetPartyOffset(index)); + private Span GetPartySpan(int index) => PartyBuffer[GetPartyOffset(index)..]; + public PKM GetPartySlotAtIndex(int index) => GetPartySlot(GetPartySpan(index)); public void SetPartySlotAtIndex(PKM pk, int index, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) { @@ -267,48 +264,39 @@ public void SetPartySlotAtIndex(PKM pk, int index, PKMImportSetting trade = PKMI PartyCount = index; } - int offset = GetPartyOffset(index); - SetPartySlot(pk, PartyBuffer, offset, trade, dex); + SetPartySlot(pk, GetPartySpan(index), trade, dex); } - public void SetSlotFormatParty(PKM pk, byte[] data, int offset, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) + public void SetSlotFormatParty(PKM pk, Span data, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) { if (pk.GetType() != PKMType) throw new ArgumentException($"PKM Format needs to be {PKMType} when setting to this Save File."); UpdatePKM(pk, isParty: true, trade, dex); SetPartyValues(pk, isParty: true); - WritePartySlot(pk, data, offset); + WritePartySlot(pk, data); } - public void SetPartySlot(PKM pk, byte[] data, int offset, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) - { - if (pk.GetType() != PKMType) - throw new ArgumentException($"PKM Format needs to be {PKMType} when setting to this Save File."); - - UpdatePKM(pk, isParty: true, trade, dex); - SetPartyValues(pk, isParty: true); - WritePartySlot(pk, data, offset); - } - - public void SetSlotFormatStored(PKM pk, Span data, int offset, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) + public void SetSlotFormatStored(PKM pk, Span data, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) { if (pk.GetType() != PKMType) throw new ArgumentException($"PKM Format needs to be {PKMType} when setting to this Save File."); UpdatePKM(pk, isParty: false, trade, dex); SetPartyValues(pk, isParty: false); - WriteSlotFormatStored(pk, data, offset); + WriteSlotFormatStored(pk, data); } - public void SetBoxSlot(PKM pk, Span data, int offset, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) + public void SetPartySlot(PKM pk, Span data, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) => SetSlotFormatParty(pk, data, trade, dex); + + public void SetBoxSlot(PKM pk, Span data, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) { if (pk.GetType() != PKMType) throw new ArgumentException($"PKM Format needs to be {PKMType} when setting to this Save File."); UpdatePKM(pk, isParty: false, trade, dex); SetPartyValues(pk, isParty: false); - WriteBoxSlot(pk, data, offset); + WriteBoxSlot(pk, data); } public void DeletePartySlot(int slot) @@ -340,23 +328,23 @@ public void DeletePartySlot(int slot) public abstract int MaxEV { get; } public virtual int MaxIV => 31; public abstract ReadOnlySpan HeldItems { get; } - protected virtual byte[] BoxBuffer => Data; - protected virtual byte[] PartyBuffer => Data; + protected virtual Span BoxBuffer => Data; + protected virtual Span PartyBuffer => Data; public virtual bool IsPKMPresent(ReadOnlySpan data) => EntityDetection.IsPresent(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)); - public virtual PKM GetBoxSlot(int offset) => GetStoredSlot(BoxBuffer, offset); + public virtual PKM GetPartySlot(ReadOnlySpan data) => GetDecryptedPKM(data[..SIZE_PARTY].ToArray()); + public virtual PKM GetStoredSlot(ReadOnlySpan data) => GetDecryptedPKM(data[..SIZE_STORED].ToArray()); + public virtual PKM GetBoxSlot(int offset) => GetStoredSlot(BoxBuffer[offset..]); public virtual byte[] GetDataForFormatStored(PKM pk) => pk.EncryptedBoxData; public virtual byte[] GetDataForFormatParty(PKM pk) => pk.EncryptedPartyData; public virtual byte[] GetDataForParty(PKM pk) => pk.EncryptedPartyData; public virtual byte[] GetDataForBox(PKM pk) => pk.EncryptedBoxData; - public virtual void WriteSlotFormatStored(PKM pk, Span data, int offset) => SetData(data, GetDataForFormatStored(pk), offset); - public virtual void WriteSlotFormatParty(PKM pk, Span data, int offset) => SetData(data, GetDataForFormatParty(pk), offset); - public virtual void WritePartySlot(PKM pk, Span data, int offset) => SetData(data, GetDataForParty(pk), offset); - public virtual void WriteBoxSlot(PKM pk, Span data, int offset) => SetData(data, GetDataForBox(pk), offset); + public virtual void WriteSlotFormatStored(PKM pk, Span data) => SetData(data, GetDataForFormatStored(pk)); + public virtual void WriteSlotFormatParty(PKM pk, Span data) => SetData(data, GetDataForFormatParty(pk)); + public virtual void WritePartySlot(PKM pk, Span data) => SetData(data, GetDataForParty(pk)); + public virtual void WriteBoxSlot(PKM pk, Span data) => SetData(data, GetDataForBox(pk)); protected virtual void SetPartyValues(PKM pk, bool isParty) { @@ -522,13 +510,15 @@ public void AddBoxData(IList data, int box, int index) public int NextOpenBoxSlot(int lastKnownOccupied = -1) { - var storage = BoxBuffer.AsSpan(); + var storage = BoxBuffer; int count = SlotCount; for (int i = lastKnownOccupied + 1; i < count; i++) { int offset = GetBoxSlotOffset(i); - if (!IsPKMPresent(storage[offset..])) - return i; + // overwrite protect is only true if there is already data in slot + if (IsPKMPresent(storage[offset..])) + continue; + return i; } return StorageFullValue; } @@ -593,10 +583,10 @@ public int GetBoxSlotOffset(int index) } public void SetBoxSlotAtIndex(PKM pk, int box, int slot, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) - => SetBoxSlot(pk, BoxBuffer, GetBoxSlotOffset(box, slot), trade, dex); + => SetBoxSlot(pk, BoxBuffer[GetBoxSlotOffset(box, slot)..], trade, dex); public void SetBoxSlotAtIndex(PKM pk, int index, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) - => SetBoxSlot(pk, BoxBuffer, GetBoxSlotOffset(index), trade, dex); + => SetBoxSlot(pk, BoxBuffer[GetBoxSlotOffset(index)..], trade, dex); #endregion #region Storage Manipulations @@ -612,7 +602,7 @@ public bool MoveBox(int box, int insertBeforeBox) return true; } - private void MoveBox(int box, int insertBeforeBox, byte[] storage) + private void MoveBox(int box, int insertBeforeBox, Span storage) { int pos1 = BoxSlotCount * box; int pos2 = BoxSlotCount * insertBeforeBox; @@ -620,7 +610,7 @@ private void MoveBox(int box, int insertBeforeBox, byte[] storage) int max = Math.Max(pos1, pos2); int len = BoxSlotCount * SIZE_BOXSLOT; - byte[] boxdata = storage.Slice(GetBoxOffset(0), len * BoxCount); // get all boxes + byte[] boxdata = storage.Slice(GetBoxOffset(0), len * BoxCount).ToArray(); // get all boxes string[] boxNames = Get(GetBoxName, BoxCount); int[] boxWallpapers = Get(GetBoxWallpaper, BoxCount); @@ -646,7 +636,7 @@ static T[] Get(Func act, int count) b = ctr++; } - Buffer.BlockCopy(boxdata, len * i, storage, GetBoxOffset(b), len); + boxdata.AsSpan(len * i, len).CopyTo(storage[GetBoxOffset(b)..]); SetBoxName(b, boxNames[i]); SetBoxWallpaper(b, boxWallpapers[i]); } @@ -754,7 +744,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.AsSpan(); + var storage = BoxBuffer; if ((uint)BoxEnd >= BoxCount) BoxEnd = BoxCount - 1; @@ -777,7 +767,7 @@ public int ClearBoxes(int BoxStart = 0, int BoxEnd = -1, Func? delete continue; } - SetData(storage, blank, ofs); + SetData(storage[ofs..], blank); ++deleted; } } @@ -796,7 +786,7 @@ public int ModifyBoxes(Action action, int BoxStart = 0, int BoxEnd = -1) if ((uint)BoxEnd >= BoxCount) BoxEnd = BoxCount - 1; - var storage = BoxBuffer.AsSpan(); + var storage = BoxBuffer; int modified = 0; for (int b = BoxStart; b <= BoxEnd; b++) { @@ -805,12 +795,13 @@ 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..])) + var dest = storage[ofs..]; + if (!IsPKMPresent(dest)) continue; var pk = GetBoxSlotAtIndex(b, s); action(pk); ++modified; - SetBoxSlot(pk, storage, ofs, PKMImportSetting.Skip, PKMImportSetting.Skip); + SetBoxSlot(pk, dest, PKMImportSetting.Skip, PKMImportSetting.Skip); } } return modified; @@ -846,7 +837,7 @@ public virtual void SetBoxWallpaper(int box, int value) public byte[] GetPCBinary() => BoxData.SelectMany(GetDataForBox).ToArray(); public byte[] GetBoxBinary(int box) => GetBoxData(box).SelectMany(GetDataForBox).ToArray(); - public bool SetPCBinary(byte[] data) + public bool SetPCBinary(ReadOnlySpan data) { if (IsRegionOverwriteProtected(0, SlotCount)) return false; @@ -855,7 +846,7 @@ public bool SetPCBinary(byte[] data) return SetConcatenatedBinary(data, expectLength); } - public bool SetBoxBinary(byte[] data, int box) + public bool SetBoxBinary(ReadOnlySpan data, int box) { int start = box * BoxSlotCount; int end = start + BoxSlotCount; @@ -867,16 +858,22 @@ public bool SetBoxBinary(byte[] data, int box) return SetConcatenatedBinary(data, expectLength, start); } - private bool SetConcatenatedBinary(byte[] data, int expectLength, int start = 0) + private bool SetConcatenatedBinary(ReadOnlySpan data, int expectLength, int start = 0) { if (data.Length != expectLength) return false; var BD = BoxData; var entryLength = SIZE_BOXSLOT; - var pkdata = ArrayUtil.EnumerateSplit(data, entryLength); - - pkdata.Select(GetPKM).CopyTo(BD, IsSlotOverwriteProtected, start); + for (int i = 0, ctr = start; i < data.Length; i += entryLength) + { + if (IsSlotOverwriteProtected(ctr)) + continue; + var src = data.Slice(i, entryLength); + var arr = src.ToArray(); + var pk = GetPKM(arr); + SetBoxSlotAtIndex(pk, ctr++); + } BoxData = BD; return true; } diff --git a/PKHeX.Core/Saves/Storage/Bank3.cs b/PKHeX.Core/Saves/Storage/Bank3.cs index 322164175..e97de240b 100644 --- a/PKHeX.Core/Saves/Storage/Bank3.cs +++ b/PKHeX.Core/Saves/Storage/Bank3.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; namespace PKHeX.Core; @@ -22,6 +21,6 @@ public sealed class Bank3 : BulkStorage private int BoxDataSize => SlotsPerBox * SIZE_STORED; public override int GetBoxOffset(int box) => Box + (BoxDataSize * box); - public override string GetBoxName(int box) => GetString(GetBoxNameOffset(box), BoxNameSize); + public override string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxNameOffset(box), BoxNameSize)); private static int GetBoxNameOffset(int box) => 0x25800 + (9 * box); } diff --git a/PKHeX.Core/Saves/Storage/Bank4.cs b/PKHeX.Core/Saves/Storage/Bank4.cs index fab29387f..195f961e3 100644 --- a/PKHeX.Core/Saves/Storage/Bank4.cs +++ b/PKHeX.Core/Saves/Storage/Bank4.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; namespace PKHeX.Core; @@ -22,6 +21,6 @@ public sealed class Bank4 : BulkStorage private int BoxDataSize => SlotsPerBox * SIZE_STORED; public override int GetBoxOffset(int box) => Box + (BoxDataSize * box); - public override string GetBoxName(int box) => GetString(GetBoxNameOffset(box), BoxNameSize / 2); + public override string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxNameOffset(box), BoxNameSize / 2)); private static int GetBoxNameOffset(int box) => 0x3FC00 + (0x19 * box); } diff --git a/PKHeX.Core/Saves/Storage/Bank7.cs b/PKHeX.Core/Saves/Storage/Bank7.cs index 2353926ec..aa09a5cfb 100644 --- a/PKHeX.Core/Saves/Storage/Bank7.cs +++ b/PKHeX.Core/Saves/Storage/Bank7.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -28,7 +27,7 @@ public string GetGroupName(int group) if ((uint)group > 10) throw new ArgumentOutOfRangeException(nameof(group), $"{nameof(group)} must be 0-10."); int offset = 0x8 + (GroupNameSpacing * group) + 2; // skip over " " - return GetString(offset, GroupNameSize / 2); + return GetString(Data.AsSpan(offset, GroupNameSize / 2)); } public override int BoxCount => BankCount; @@ -47,7 +46,7 @@ private int BankCount private int BoxDataSize => (SlotsPerBox * SIZE_STORED) + BankNameSpacing; public override int GetBoxOffset(int box) => Box + (BoxDataSize * box); - public override string GetBoxName(int box) => GetString(GetBoxNameOffset(box), BankNameSize / 2); + public override string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxNameOffset(box), BankNameSize / 2)); public int GetBoxNameOffset(int box) => GetBoxOffset(box) + (SlotsPerBox * SIZE_STORED); public int GetBoxIndex(int box) => ReadUInt16LittleEndian(Data.AsSpan(GetBoxNameOffset(box) + BankNameSize)); diff --git a/PKHeX.Core/Saves/Storage/SAV4Ranch.cs b/PKHeX.Core/Saves/Storage/SAV4Ranch.cs index 441d5f274..116a6f544 100644 --- a/PKHeX.Core/Saves/Storage/SAV4Ranch.cs +++ b/PKHeX.Core/Saves/Storage/SAV4Ranch.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using static System.Buffers.Binary.BinaryPrimitives; @@ -51,7 +50,7 @@ public SAV4Ranch(byte[] data) : base(data, typeof(RK4), 0) { Version = Data.Length == SaveUtil.SIZE_G4RANCH_PLAT ? GameVersion.Pt : GameVersion.DP; - OT = GetString(0x770, 0x12); + OT = GetString(Data.AsSpan(0x770, 0x12)); // 0x18 starts the header table: [u32 BlockID, u32 Offset] // Block 00, Offset = Metadata object @@ -109,7 +108,7 @@ public void SetRanchToy(RanchToy toy, int index) toy = BlankToy; int toyOffset = ToyBaseOffset + (RanchToy.SIZE * index); - SetData(Data, toy.Data, toyOffset); + SetData(Data.AsSpan(toyOffset), toy.Data); } public RanchMii GetRanchMii(int index) @@ -128,7 +127,7 @@ public void SetRanchMii(RanchMii trainer, int index) throw new ArgumentOutOfRangeException(nameof(index)); int offset = MiiDataOffset + (RanchMii.SIZE * index); - SetData(Data, trainer.Data, offset); + SetData(Data.AsSpan(offset), trainer.Data); } public RanchTrainerMii GetRanchTrainerMii(int index) @@ -147,7 +146,7 @@ public void SetRanchTrainerMii(RanchTrainerMii mii, int index) throw new ArgumentOutOfRangeException(nameof(index)); int offset = TrainerMiiDataOffset + (RanchTrainerMii.SIZE * index); - SetData(Data, mii.Data, offset); + SetData(Data.AsSpan(offset), mii.Data); } private const int sha1HashSize = 20; @@ -155,9 +154,8 @@ public void SetRanchTrainerMii(RanchTrainerMii mii, int index) protected override void SetChecksums() { var data = Data.AsSpan(); - // ensure the final data is cleared if the user screws stuff up - WriteInt32BigEndian(data[DataEndMarkerOffset..], DataEndMarker); - data[(DataEndMarkerOffset + 4)..].Clear(); + var slotCount = GetOccupiedSlotCount(); + UpdateMetadata(slotCount * SIZE_STORED); // 20 byte SHA checksum at the top of the file, which covers all data that follows. var hash = data[..sha1HashSize]; @@ -165,6 +163,20 @@ protected override void SetChecksums() SHA1.HashData(payload, hash); } + private int GetOccupiedSlotCount() + { + int count = SlotCount; + for (int i = count - 1; i >= 0; i--) + { + var ofs = GetBoxSlotOffset(i); + var span = Data.AsSpan(ofs, SIZE_STORED); + var type = ReadUInt64LittleEndian(span[0x88..]); + if (type != 0) + return i + 1; + } + return 0; + } + protected override byte[] DecryptPKM(byte[] data) { var pokeData = PokeCrypto.DecryptArray45(data.Slice(0, PokeCrypto.SIZE_4STORED)); @@ -176,7 +188,7 @@ protected override byte[] DecryptPKM(byte[] data) return finalData; } - public void WriteBoxSlotInternal(PKM pk, Span data, int offset, string htName = "", ushort htTID = 0, ushort htSID = 0, RanchOwnershipType type = RanchOwnershipType.Hayley) + public void WriteBoxSlotInternal(PKM pk, Span data, string htName = "", ushort htTID = 0, ushort htSID = 0, RanchOwnershipType type = RanchOwnershipType.Hayley) { RK4 rk = (RK4)this.GetCompatiblePKM(pk); rk.OwnershipType = type; @@ -184,33 +196,38 @@ public void WriteBoxSlotInternal(PKM pk, Span data, int offset, string htN rk.HT_SID = htSID; rk.HT_Name = htName; - WriteBoxSlot(rk, data, offset); + WriteBoxSlot(rk, data); } - public override void WriteBoxSlot(PKM pk, Span data, int offset) + public override void WriteBoxSlot(PKM pk, Span data) { - bool isBlank = pk.Data.SequenceEqual(BlankPKM.Data); if (pk is not RK4 rk4) { - WriteBoxSlotInternal(pk, data, offset); + WriteBoxSlotInternal(pk, data); return; } + bool isBlank = pk.Data.SequenceEqual(BlankPKM.Data); if (!isBlank && rk4.OwnershipType == RanchOwnershipType.None) rk4.OwnershipType = RanchOwnershipType.Hayley; // Pokemon without an Ownership type get erased when the save is loaded. Hayley is considered 'default'. - base.WriteBoxSlot(rk4, data, offset); - if ((offset + SIZE_STORED) > DataEndMarkerOffset) + base.WriteBoxSlot(rk4, data); + } + + private void UpdateMetadata(int pkEnd) + { + var data = Data.AsSpan(); + // ensure the final data is cleared if the user screws stuff up { - DataEndMarkerOffset = (offset + SIZE_STORED); - WriteInt32BigEndian(Data.AsSpan(0x3C), DataEndMarkerOffset); - WriteInt32BigEndian(Data.AsSpan(DataEndMarkerOffset), DataEndMarker); + DataEndMarkerOffset = pkEnd; + WriteInt32BigEndian(data[0x3C..], pkEnd); + WriteInt32BigEndian(data[pkEnd..], DataEndMarker); + data[(pkEnd + 4)..].Clear(); } int pkStart = PokemonCountOffset + 4; - int pkEnd = DataEndMarkerOffset; int pkCount = (pkEnd - pkStart) / SIZE_STORED; - WriteInt32BigEndian(Data.AsSpan(PokemonCountOffset), pkCount); + WriteInt32BigEndian(data[PokemonCountOffset..], pkCount); } private TimeSpan PlayedSpan diff --git a/PKHeX.Core/Saves/Substructures/Gen4/PoffinCase4.cs b/PKHeX.Core/Saves/Substructures/Gen4/PoffinCase4.cs index 0b4fcb010..7b10a86ca 100644 --- a/PKHeX.Core/Saves/Substructures/Gen4/PoffinCase4.cs +++ b/PKHeX.Core/Saves/Substructures/Gen4/PoffinCase4.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; namespace PKHeX.Core; @@ -36,7 +37,11 @@ private static void WritePoffins(SAV4Sinnoh sav, int offset, IReadOnlyList public Puff6(SAV6 SAV, int offset) : base(SAV) => Offset = offset; - public byte[] GetPuffs() => SAV.GetData(Offset, PuffSlots); - public void SetPuffs(byte[] value) => SAV.SetData(value, Offset); + public Span GetPuffs() => SAV.Data.AsSpan(Offset, PuffSlots); + public void SetPuffs(ReadOnlySpan value) => SAV.SetData(value, Offset); public int PuffCount { diff --git a/PKHeX.Core/Saves/Substructures/Gen7/FashionBlock7.cs b/PKHeX.Core/Saves/Substructures/Gen7/FashionBlock7.cs index 91efcdae4..c41148f09 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/FashionBlock7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/FashionBlock7.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace PKHeX.Core; @@ -13,7 +13,7 @@ public FashionItem7[] Wardrobe { get { - var data = SAV.GetData(Offset, 0x5A8); + var data = SAV.Data.AsSpan(Offset, 0x5A8).ToArray(); return Array.ConvertAll(data, b => new FashionItem7(b)); } set @@ -32,17 +32,25 @@ public FashionItem7[] Wardrobe /// public void Reset() { - var offsetList = SAV is SAV7USUM - ? (SAV.Gender == 0 - ? new[] { 0x03A, 0x109, 0x1DA, 0x305, 0x3D9, 0x4B1, 0x584 } // M - : new[] { 0x05E, 0x208, 0x264, 0x395, 0x3B4, 0x4F9, 0x5A8 }) // F - : (SAV.Gender == 0 - ? new[] { 0x000, 0x0FB, 0x124, 0x28F, 0x3B4, 0x452, 0x517 } // M - : new[] { 0x000, 0x100, 0x223, 0x288, 0x3B4, 0x452, 0x517 }); // F - + var offsetList = GetDefaultFashionOffsets(SAV); foreach (var ofs in offsetList) SAV.Data[Offset + ofs] = 3; // owned | new } + + private static ReadOnlySpan GetDefaultFashionOffsets(SAV7 sav) => sav switch + { + SAV7SM { Gender: 0 } => DefaultFashionOffsetSM_M, + SAV7SM { Gender: 1 } => DefaultFashionOffsetSM_F, + SAV7USUM { Gender: 0 } => DefaultFashionOffsetUU_M, + SAV7USUM { Gender: 1 } => DefaultFashionOffsetUU_F, + _ => throw new ArgumentOutOfRangeException(nameof(SAV)), + }; + + // Offsets that are set to '3' when the game starts for a specific gender. + private static ReadOnlySpan DefaultFashionOffsetSM_M => new ushort[] { 0x000, 0x0FB, 0x124, 0x28F, 0x3B4, 0x452, 0x517 }; + private static ReadOnlySpan DefaultFashionOffsetSM_F => new ushort[] { 0x000, 0x100, 0x223, 0x288, 0x3B4, 0x452, 0x517 }; + private static ReadOnlySpan DefaultFashionOffsetUU_M => new ushort[] { 0x03A, 0x109, 0x1DA, 0x305, 0x3D9, 0x4B1, 0x584 }; + private static ReadOnlySpan DefaultFashionOffsetUU_F => new ushort[] { 0x05E, 0x208, 0x264, 0x395, 0x3B4, 0x4F9, 0x5A8 }; } // Every fashion item is 2 bits, New Flag (high) & Owned Flag (low) diff --git a/PKHeX.Core/Saves/Substructures/Gen7/FestaFacility.cs b/PKHeX.Core/Saves/Substructures/Gen7/FestaFacility.cs index 32ae31a73..082529254 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/FestaFacility.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/FestaFacility.cs @@ -13,7 +13,7 @@ public sealed class FestaFacility public FestaFacility(SAV7 sav, int index) { ofs = (index * SIZE) + sav.Festa.Offset + 0x310; - Data = sav.GetData(ofs, SIZE); + Data = sav.Data.AsSpan(ofs, SIZE).ToArray(); Language = sav.Language; } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/FieldMenu7.cs b/PKHeX.Core/Saves/Substructures/Gen7/FieldMenu7.cs index a72835013..58a124cd3 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/FieldMenu7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/FieldMenu7.cs @@ -20,7 +20,9 @@ public ushort RotomAffection public string RotomOT { - get => SAV.GetString(Offset + 0x30, 0x1A); - set => SAV.SetString(Data.AsSpan(Offset + 0x30, 0x1A), value, SAV.MaxStringLengthOT, StringConverterOption.ClearZero); + get => SAV.GetString(RotomNameSpan); + set => SAV.SetString(RotomNameSpan, value, SAV.MaxStringLengthOT, StringConverterOption.ClearZero); } + + private Span RotomNameSpan => Data.AsSpan(Offset + 0x30, 0x1A); } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PokeListHeader.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PokeListHeader.cs index 3ec689976..064ee7861 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PokeListHeader.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/PokeListHeader.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using static System.Buffers.Binary.BinaryPrimitives; @@ -109,10 +108,10 @@ private int[] LoadPointerData() return list; } - private void SetPointerData(IList vals) + private void SetPointerData(ReadOnlySpan vals) { var span = Data.AsSpan(Offset); - for (int i = 0; i < vals.Count; i++) + for (int i = 0; i < vals.Length; i++) WriteUInt16LittleEndian(span[(i*2)..], (ushort)vals[i]); vals.CopyTo(PokeListInfo); } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs b/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs index eaf11c460..4753bb6f8 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace PKHeX.Core; @@ -50,7 +50,7 @@ private WC7 GetGift(int index) throw new ArgumentOutOfRangeException(nameof(index)); var offset = GetGiftOffset(index); - var data = SAV.GetData(offset, WC7.Size); + var data = SAV.Data.AsSpan(offset, WC7.Size).ToArray(); return new WC7(data); } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/ResortSave7.cs b/PKHeX.Core/Saves/Substructures/Gen7/ResortSave7.cs index 32bec0b16..14062a0ba 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/ResortSave7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/ResortSave7.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace PKHeX.Core; @@ -13,13 +13,14 @@ public PK7[] ResortPKM { get { - PK7[] data = new PK7[ResortCount]; - for (int i = 0; i < data.Length; i++) + PK7[] result = new PK7[ResortCount]; + for (int i = 0; i < result.Length; i++) { - var bytes = SAV.GetData(GetResortSlotOffset(i), PokeCrypto.SIZE_6STORED); - data[i] = new PK7(bytes); + var ofs = GetResortSlotOffset(i); + var data = SAV.Data.AsSpan(ofs, PokeCrypto.SIZE_6STORED).ToArray(); + result[i] = new PK7(data); } - return data; + return result; } set { @@ -27,7 +28,11 @@ public PK7[] ResortPKM throw new ArgumentException(nameof(ResortCount)); for (int i = 0; i < value.Length; i++) - SAV.SetSlotFormatStored(value[i], Data, GetResortSlotOffset(i)); + { + var ofs = GetResortSlotOffset(i); + var dest = Data.AsSpan(ofs, PokeCrypto.SIZE_6STORED); + SAV.SetSlotFormatStored(value[i], dest); + } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/BerryTreeGrowSave8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/BerryTreeGrowSave8b.cs index 5823f5b84..2cecdbf8b 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/BS/BerryTreeGrowSave8b.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/BerryTreeGrowSave8b.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using static System.Buffers.Binary.BinaryPrimitives; diff --git a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Fused8.cs b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Fused8.cs index 70016f9ae..6402c5db6 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Fused8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/SWSH/Fused8.cs @@ -1,4 +1,6 @@ -namespace PKHeX.Core; +using System; + +namespace PKHeX.Core; /// /// Storage for the species that was fused into and . @@ -14,21 +16,11 @@ public static int GetFusedSlotOffset(int slot) return PokeCrypto.SIZE_8PARTY * slot; } - public PK8 Kyurem - { - get => (PK8) SAV.GetStoredSlot(Data, GetFusedSlotOffset(0)); - set => value.EncryptedBoxData.CopyTo(Data, GetFusedSlotOffset(0)); - } + private Span GetSlotSpan(int index) => Data.AsSpan(GetFusedSlotOffset(index), PokeCrypto.SIZE_8STORED); + private PK8 GetStoredSlot(int index) => (PK8)SAV.GetStoredSlot(GetSlotSpan(index)); + private void SetStoredSlot(PK8 pk, int index) => pk.EncryptedBoxData.CopyTo(GetSlotSpan(index)); - public PK8 NecrozmaSolgaleo - { - get => (PK8)SAV.GetStoredSlot(Data, GetFusedSlotOffset(1)); - set => value.EncryptedBoxData.CopyTo(Data, GetFusedSlotOffset(1)); - } - - public PK8 NecrozmaLunala - { - get => (PK8)SAV.GetStoredSlot(Data, GetFusedSlotOffset(2)); - set => value.EncryptedBoxData.CopyTo(Data, GetFusedSlotOffset(2)); - } + public PK8 Kyurem { get => GetStoredSlot(0); set => SetStoredSlot(value, 0); } + public PK8 NecrozmaSolgaleo { get => GetStoredSlot(1); set => SetStoredSlot(value, 1); } + public PK8 NecrozmaLunala { get => GetStoredSlot(2); set => SetStoredSlot(value, 2); } } diff --git a/PKHeX.Core/Saves/Substructures/Mail/Mail2.cs b/PKHeX.Core/Saves/Substructures/Mail/Mail2.cs index 8ba0e22f0..ed4e0aa10 100644 --- a/PKHeX.Core/Saves/Substructures/Mail/Mail2.cs +++ b/PKHeX.Core/Saves/Substructures/Mail/Mail2.cs @@ -25,7 +25,7 @@ public sealed class Mail2 : MailDetail private const int AUTHOR_LENGTH = 7; - public Mail2(SAV2 sav, int index) : base(sav.GetData(GetMailOffset(index), 0x2F), GetMailOffset(index)) + public Mail2(SAV2 sav, int index) : base(sav.Data.AsSpan(GetMailOffset(index), 0x2F).ToArray(), GetMailOffset(index)) { US = sav is { Japanese: false, Korean: false }; } diff --git a/PKHeX.Core/Saves/Substructures/Mail/Mail4.cs b/PKHeX.Core/Saves/Substructures/Mail/Mail4.cs index 8610b1ce6..d691e905b 100644 --- a/PKHeX.Core/Saves/Substructures/Mail/Mail4.cs +++ b/PKHeX.Core/Saves/Substructures/Mail/Mail4.cs @@ -18,7 +18,7 @@ public Mail4(byte? lang, byte? ver) : base(new byte[SIZE]) ResetData(); } - public override void CopyTo(SaveFile sav) => sav.SetData(((SAV4)sav).General, Data, DataOffset); + public override void CopyTo(SaveFile sav) => sav.SetData(((SAV4)sav).General.AsSpan(DataOffset), Data); private void ResetData() { diff --git a/PKHeX.Core/Util/ArrayUtil.cs b/PKHeX.Core/Util/ArrayUtil.cs index 01e3f3ba7..602010d22 100644 --- a/PKHeX.Core/Util/ArrayUtil.cs +++ b/PKHeX.Core/Util/ArrayUtil.cs @@ -26,9 +26,9 @@ public static class ArrayUtil public static int Count(this ReadOnlySpan data, T value) where T : IEquatable { int count = 0; - for (int i = data.Length - 1; i >= 0; i--) + foreach (var t in data) { - if (data[i].Equals(value)) + if (t.Equals(value)) count++; } return count; @@ -86,26 +86,6 @@ public static int FindNextValidIndex(IList dest, Func skip, int } } - /// - /// Copies an list to the destination list, with an option to copy to a starting point. - /// - /// Typed object to copy - /// Source list to copy from - /// Destination list/array - /// Starting point to copy to - /// Count of copied. - public static int CopyTo(this IEnumerable list, IList dest, int start = 0) - { - int ctr = start; - foreach (var z in list) - { - if ((uint)ctr >= dest.Count) - break; - dest[ctr++] = z; - } - return ctr - start; - } - internal static T[] ConcatAll(params T[][] arr) { int len = 0; diff --git a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs index dec826170..1fe274004 100644 --- a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs +++ b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs @@ -831,7 +831,7 @@ public bool ExportBackup() return true; } - public bool OpenPCBoxBin(byte[] input, out string c) + public bool OpenPCBoxBin(ReadOnlySpan input, out string c) { if (SAV.GetPCBinary().Length == input.Length) { diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs index fe70422cd..1d5d190f5 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs @@ -773,7 +773,7 @@ private void B_DumpFC_Click(object sender, EventArgs e) if (sfd.ShowDialog() != DialogResult.OK) return; - var data = SAV.GetData(ofsForestCity, ofsForestCitySize); + var data = SAV.Data.AsSpan(ofsForestCity, ofsForestCitySize).ToArray(); File.WriteAllBytes(sfd.FileName, data); } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_FestivalPlaza.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_FestivalPlaza.cs index cce086446..6f157b51b 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_FestivalPlaza.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_FestivalPlaza.cs @@ -300,9 +300,9 @@ private void Save() private void LoadBattleAgency() { - p[0] = SAV.GetStoredSlot(SAV.Data, 0x6C200); - p[1] = SAV.GetPartySlot(SAV.Data, 0x6C2E8); - p[2] = SAV.GetPartySlot(SAV.Data, 0x6C420); + p[0] = SAV.GetStoredSlot(SAV.Data.AsSpan(0x6C200)); + p[1] = SAV.GetPartySlot(SAV.Data.AsSpan(0x6C2E8)); + p[2] = SAV.GetPartySlot(SAV.Data.AsSpan(0x6C420)); LoadPictureBox(); B_ImportParty.Visible = SAV.HasParty; CHK_Choosed.Checked = SAV.GetFlag(0x6C55E, 1);