diff --git a/PKHeX.Core/PKM/Shared/PokeList1.cs b/PKHeX.Core/PKM/Shared/PokeList1.cs index 765ecd0b4..6b29a6293 100644 --- a/PKHeX.Core/PKM/Shared/PokeList1.cs +++ b/PKHeX.Core/PKM/Shared/PokeList1.cs @@ -271,4 +271,46 @@ public static byte[] WrapSingle(PK1 pk) /// Entity to wrap /// Destination to write the single-slot list public static void WrapSingle(PK1 pk, Span output) => WriteToList(output, pk); + + public static void UnpackNOB(ReadOnlySpan input, Span output, int stringLength, bool isParty = false) + { + // Nickname, OT, Data + var lengthBody = GetBodyLength(isParty); + var lengthParty = GetBodyLength(true); + var nick = input[..stringLength]; + var trainer = input.Slice(stringLength, stringLength); + var box = input.Slice(stringLength * 2, lengthBody); + + var marker = box[0]; + if (marker is 0) // Ensure deleted (zeroed) slots act as an Empty (FF) slot. + marker = SlotEmpty; + + output[0] = 1; + output[1] = marker; + output = output[3..]; + + // Data, OT, Nickname + box.CopyTo(output); + output = output[lengthParty..]; + trainer.CopyTo(output); + output = output[stringLength..]; + nick.CopyTo(output); + } + + public static void PackNOB(ReadOnlySpan input, Span output, int stringLength, bool isParty = false) + { + var lengthBody = GetBodyLength(isParty); + var lengthParty = GetBodyLength(true); + + input = input[3..]; // Skip header. + var box = input[..lengthBody]; + var trainer = input.Slice(lengthParty, stringLength); + var nick = input.Slice(lengthParty + stringLength, stringLength); + + nick.CopyTo(output); + output = output[stringLength..]; + trainer.CopyTo(output); + output = output[stringLength..]; + box.CopyTo(output); + } } diff --git a/PKHeX.Core/PKM/Shared/PokeList2.cs b/PKHeX.Core/PKM/Shared/PokeList2.cs index c2063461a..a79364d56 100644 --- a/PKHeX.Core/PKM/Shared/PokeList2.cs +++ b/PKHeX.Core/PKM/Shared/PokeList2.cs @@ -274,4 +274,46 @@ public static byte[] WrapSingle(PK2 pk) /// Entity to wrap /// Destination to write the single-slot list public static void WrapSingle(PK2 pk, Span output) => WriteToList(output, pk); + + public static void UnpackNOB(ReadOnlySpan input, Span output, int stringLength, bool isParty = false) + { + // Nickname, OT, Data + var lengthBody = GetBodyLength(isParty); + var lengthParty = GetBodyLength(true); + var nick = input[..stringLength]; + var trainer = input.Slice(stringLength, stringLength); + var box = input.Slice(stringLength * 2, lengthBody); + + var marker = box[0]; + if (marker is 0) // Ensure deleted (zeroed) slots act as an Empty (FF) slot. + marker = SlotEmpty; + + output[0] = 1; + output[1] = marker; + output = output[3..]; + + // Data, OT, Nickname + box.CopyTo(output); + output = output[lengthParty..]; + trainer.CopyTo(output); + output = output[stringLength..]; + nick.CopyTo(output); + } + + public static void PackNOB(ReadOnlySpan input, Span output, int stringLength, bool isParty = false) + { + var lengthBody = GetBodyLength(isParty); + var lengthParty = GetBodyLength(true); + + input = input[3..]; // Skip header. + var box = input[..lengthBody]; + var trainer = input.Slice(lengthParty, stringLength); + var nick = input.Slice(lengthParty + stringLength, stringLength); + + nick.CopyTo(output); + output = output[stringLength..]; + trainer.CopyTo(output); + output = output[stringLength..]; + box.CopyTo(output); + } } diff --git a/PKHeX.Core/Saves/SAV1.cs b/PKHeX.Core/Saves/SAV1.cs index fdf26952e..1b8bbe525 100644 --- a/PKHeX.Core/Saves/SAV1.cs +++ b/PKHeX.Core/Saves/SAV1.cs @@ -105,20 +105,11 @@ private void Initialize(GameVersion versionOverride) PokeList1.Unpack(src, dest, StringLength, 6, true); } - ReadOnlySpan rawDC = Data.AsSpan(Offsets.Daycare, 0x38); - var daycareOffset = DaycareOffset = GetPartyOffset(6); - var TempDaycare = BoxBuffer[daycareOffset..]; - TempDaycare[0] = rawDC[0]; - - var dcDest = TempDaycare[3..]; - - // OT, Nickname, and Core data (rather than the reverse...) - rawDC.Slice(1, StringLength).CopyTo(dcDest[(PokeCrypto.SIZE_1PARTY + StringLength)..]); - rawDC.Slice(1 + StringLength, StringLength).CopyTo(dcDest[PokeCrypto.SIZE_1PARTY..]); - rawDC.Slice(1 + (2 * StringLength), PokeCrypto.SIZE_1STORED).CopyTo(dcDest); + var dc = Data.AsSpan(Offsets.Daycare, 0x38); + PokeList1.UnpackNOB(dc[1..], PartyBuffer[DaycareOffset..], StringLength); } - private int DaycareOffset = -1; + private int DaycareOffset => GetPartyOffset(6); public override bool HasPokeDex => true; // Event Flags @@ -177,12 +168,7 @@ protected override byte[] GetFinalData() } // Daycare is read-only, but in case it ever becomes editable, copy it back in. - Span rawDC = Data.AsSpan(Offsets.Daycare, 0x38); - var TempDaycare = BoxBuffer[DaycareOffset..]; - var dcDest = TempDaycare[3..]; - dcDest.Slice(2 + 1 + PokeCrypto.SIZE_1PARTY + StringLength, StringLength).CopyTo(rawDC[1..]); - dcDest.Slice(2 + 1 + PokeCrypto.SIZE_1PARTY, StringLength).CopyTo(rawDC[(1 + StringLength)..]); - dcDest.Slice(2 + 1, PokeCrypto.SIZE_1STORED).CopyTo(rawDC[(1 + (2 * StringLength))..]); + PokeList1.PackNOB(PartyBuffer[DaycareOffset..], Data.AsSpan(Offsets.Daycare, 0x38)[1..], StringLength); SetChecksums(); return Data; diff --git a/PKHeX.Core/Saves/SAV2.cs b/PKHeX.Core/Saves/SAV2.cs index 8f177863f..ce21bce8e 100644 --- a/PKHeX.Core/Saves/SAV2.cs +++ b/PKHeX.Core/Saves/SAV2.cs @@ -104,48 +104,43 @@ private void Initialize() if (Offsets.Daycare >= 0) { - int offset = Offsets.Daycare; + var dc0 = PartyBuffer[GetDaycareSlotOffset(0)..]; + PokeList2.UnpackNOB(Data.AsSpan(GetRawDaycareSlotOffset(0)), dc0, StringLength); - offset++; - var pk1 = ReadPKMFromOffset(offset); // parent 1 - offset += (StringLength * 2) + 0x20; // nick/ot/pk - offset++; - //byte steps = Data[offset]; - offset++; - //byte BreedMotherOrNonDitto = Data[offset]; - offset++; - var pk2 = ReadPKMFromOffset(offset); // parent 2 - offset += (StringLength * 2) + PokeCrypto.SIZE_2STORED; // nick/ot/pk - var pk3 = ReadPKMFromOffset(offset); // egg! - pk3.IsEgg = true; + var dc1 = PartyBuffer[GetDaycareSlotOffset(1)..]; + PokeList2.UnpackNOB(Data.AsSpan(GetRawDaycareSlotOffset(1)), dc1, StringLength); - PokeList2.WriteToList(PartyBuffer[GetPartyOffset(6 + (0 * 2))..], pk1, 1, true); - PokeList2.WriteToList(PartyBuffer[GetPartyOffset(6 + (1 * 2))..], pk2, 1, true); - PokeList2.WriteToList(PartyBuffer[GetPartyOffset(6 + (2 * 2))..], pk3, 1, true); + var egg = PartyBuffer[GetDaycareSlotOffset(2)..]; + PokeList2.UnpackNOB(Data.AsSpan(GetRawDaycareSlotOffset(2)), egg, StringLength); + if (egg[2] != PokeList2.SlotEmpty) + egg[1] = PokeList2.SlotEgg; } } + private int GetRawDaycareSlotOffset(int index) + { + int offset = Offsets.Daycare; + + offset++; // Breeding Status flags + if (index == 0) + return offset; + + offset += (StringLength * 2) + PokeCrypto.SIZE_2STORED; // nick/ot/pk + offset++; // Lady Status flags + offset++; // Steps until Egg + offset++; // Ditto Parent state + if (index == 1) + return offset; + + offset += (StringLength * 2) + PokeCrypto.SIZE_2STORED; // nick/ot/pk + return offset; + } + public override bool HasPokeDex => true; private int EventFlag => Offsets.EventFlag; private int EventWork => Offsets.EventWork; - private PK2 ReadPKMFromOffset(int offset) - { - var stringLength = StringLength; - var span = Data.AsSpan(offset); - - var pkData = span.Slice(stringLength * 2, PokeCrypto.SIZE_2STORED).ToArray(); - var pk = new PK2(pkData, jp: Japanese); - - var nick = span[..stringLength]; - var ot = span.Slice(stringLength, stringLength); - nick.CopyTo(pk.NicknameTrash); - ot.CopyTo(pk.OriginalTrainerTrash); - - return pk; - } - private int GetBoxRawDataOffset(int i, int splitAtIndex) { if (i < splitAtIndex) @@ -178,6 +173,20 @@ protected override byte[] GetFinalData() PokeList2.MergeSingles(src, dest, StringLength, 6, true); } + // Write Daycare + + if (Offsets.Daycare >= 0) + { + var dc0 = PartyBuffer[GetDaycareSlotOffset(0)..]; + PokeList2.PackNOB(dc0, Data.AsSpan(GetRawDaycareSlotOffset(0)), StringLength); + + var dc1 = PartyBuffer[GetDaycareSlotOffset(1)..]; + PokeList2.PackNOB(dc1, Data.AsSpan(GetRawDaycareSlotOffset(1)), StringLength); + + var egg = PartyBuffer[GetDaycareSlotOffset(2)..]; + PokeList2.PackNOB(egg, Data.AsSpan(GetRawDaycareSlotOffset(2)), StringLength); + } + SetChecksums(); if (Japanese) {