diff --git a/PKHeX.Core/Saves/Access/ISaveBlock5BW.cs b/PKHeX.Core/Saves/Access/ISaveBlock5BW.cs index 583112c50..46a564bf0 100644 --- a/PKHeX.Core/Saves/Access/ISaveBlock5BW.cs +++ b/PKHeX.Core/Saves/Access/ISaveBlock5BW.cs @@ -15,5 +15,6 @@ public interface ISaveBlock5BW BattleSubway5 BattleSubway { get; } Entralink5 Entralink { get; } Musical5 Musical { get; } + Encount5 Encount { get; } } } diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor5B2W2.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor5B2W2.cs index 8e29be6a5..310678867 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor5B2W2.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor5B2W2.cs @@ -96,6 +96,7 @@ public SaveBlockAccessor5B2W2(SAV5B2W2 sav) Misc = new Misc5B2W2(sav, 0x21100); Entralink = new Entralink5B2W2(sav, 0x21200); Zukan = new Zukan5(sav, 0x21400, 0x328); // form flags size is + 8 from bw with new forms (therians) + Encount = new Encount5B2W2(sav, 0x21900); BattleSubway = new BattleSubway5(sav, 0x21B00); PWT = new PWTBlock5(sav, 0x23700); Festa = new FestaBlock5(sav, 0x25900); @@ -114,5 +115,6 @@ public SaveBlockAccessor5B2W2(SAV5B2W2 sav) public Entralink5 Entralink { get; } public FestaBlock5 Festa { get; } public Musical5 Musical { get; } + public Encount5 Encount { get; } } } diff --git a/PKHeX.Core/Saves/Access/SaveBlockAccessor5BW.cs b/PKHeX.Core/Saves/Access/SaveBlockAccessor5BW.cs index fb7e76bc7..76b4dbfbe 100644 --- a/PKHeX.Core/Saves/Access/SaveBlockAccessor5BW.cs +++ b/PKHeX.Core/Saves/Access/SaveBlockAccessor5BW.cs @@ -92,6 +92,7 @@ public SaveBlockAccessor5BW(SAV5BW sav) Misc = new Misc5BW(sav, 0x21200); Entralink = new Entralink5BW(sav, 0x21300); Zukan = new Zukan5(sav, 0x21600, 0x320); + Encount = new Encount5BW(sav, 0x21B00); BattleSubway = new BattleSubway5(sav, 0x21D00); } @@ -106,5 +107,6 @@ public SaveBlockAccessor5BW(SAV5BW sav) public BattleSubway5 BattleSubway { get; } public Entralink5 Entralink { get; } public Musical5 Musical { get; } + public Encount5 Encount { get; } } } diff --git a/PKHeX.Core/Saves/SAV3E.cs b/PKHeX.Core/Saves/SAV3E.cs index 4c45d8fde..488bcec2c 100644 --- a/PKHeX.Core/Saves/SAV3E.cs +++ b/PKHeX.Core/Saves/SAV3E.cs @@ -154,6 +154,12 @@ public DecorationInventory3 Decorations set => SetData(Large, value.ToBytes(), 0x2734); } + public Swarm3 Swarm + { + get => Large.Slice(0x2B90, Swarm3.SIZE).ToClass(); + set => SetData(Large, value.ToBytesClass(), 0x2B90); + } + protected override int MailOffset => 0x2BE0; protected override int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(0, slot + 1) - 4; // @ end of each pkm slot diff --git a/PKHeX.Core/Saves/SAV3RS.cs b/PKHeX.Core/Saves/SAV3RS.cs index d357ba156..93e48b2c7 100644 --- a/PKHeX.Core/Saves/SAV3RS.cs +++ b/PKHeX.Core/Saves/SAV3RS.cs @@ -117,6 +117,12 @@ public DecorationInventory3 Decorations set => SetData(Large, value.ToBytes(), 0x26A0); } + public Swarm3 Swarm + { + get => Large.Slice(0x2AFC, Swarm3.SIZE).ToClass(); + set => SetData(Large, value.ToBytesClass(), 0x2AFC); + } + 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 diff --git a/PKHeX.Core/Saves/SAV4.cs b/PKHeX.Core/Saves/SAV4.cs index fbd93f4fd..9618ba7ac 100644 --- a/PKHeX.Core/Saves/SAV4.cs +++ b/PKHeX.Core/Saves/SAV4.cs @@ -534,5 +534,19 @@ public Mail4 GetMail(int mailIndex) int ofs = GetMailOffset(mailIndex); return new Mail4(GetMailData(ofs), ofs); } + + public abstract uint SwarmSeed { get; set; } + public abstract uint SwarmMaxCountModulo { get; } + + public uint SwarmIndex + { + get => SwarmSeed % SwarmMaxCountModulo; + set + { + value %= SwarmMaxCountModulo; + while (SwarmIndex != value) + ++SwarmSeed; + } + } } } diff --git a/PKHeX.Core/Saves/SAV4DP.cs b/PKHeX.Core/Saves/SAV4DP.cs index d40d27a90..15f837a33 100644 --- a/PKHeX.Core/Saves/SAV4DP.cs +++ b/PKHeX.Core/Saves/SAV4DP.cs @@ -148,5 +148,9 @@ private void SetActiveGiftFlags(IReadOnlyList gifts) 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 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 SwarmMaxCountModulo => 28; } } diff --git a/PKHeX.Core/Saves/SAV4HGSS.cs b/PKHeX.Core/Saves/SAV4HGSS.cs index f770f0feb..413a9118c 100644 --- a/PKHeX.Core/Saves/SAV4HGSS.cs +++ b/PKHeX.Core/Saves/SAV4HGSS.cs @@ -234,5 +234,8 @@ public void PokeGearUnlockAllCallersNoTrainers() public void SetPokewalkerCoursesUnlocked(bool[] value) => ArrayUtil.SetBitFlagArray(General, OFS_WALKER + 0x8, value); public void PokewalkerCoursesSetAll(uint value = 0x07FF_FFFFu) => SetData(General, BitConverter.GetBytes(value), OFS_WALKER + 0x8); + + public override uint SwarmSeed { get => BitConverter.ToUInt32(General, 0x68A8); set => BitConverter.GetBytes(value).CopyTo(General, 0x68A8); } + public override uint SwarmMaxCountModulo => 20; } } diff --git a/PKHeX.Core/Saves/SAV4Pt.cs b/PKHeX.Core/Saves/SAV4Pt.cs index 0d00e6e59..475454dbd 100644 --- a/PKHeX.Core/Saves/SAV4Pt.cs +++ b/PKHeX.Core/Saves/SAV4Pt.cs @@ -114,5 +114,9 @@ public override IReadOnlyList Inventory 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 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 SwarmMaxCountModulo => 22; } } diff --git a/PKHeX.Core/Saves/SAV4Sinnoh.cs b/PKHeX.Core/Saves/SAV4Sinnoh.cs index 99e0861e6..7b9f04b02 100644 --- a/PKHeX.Core/Saves/SAV4Sinnoh.cs +++ b/PKHeX.Core/Saves/SAV4Sinnoh.cs @@ -182,6 +182,10 @@ private static int[] CalculateMunchlaxTrees(int tid, int sid) public void SetUGI_Spheres(byte[] value) => SetData(General, value, OFS_UG_Items + 0x78); #endregion + + public abstract uint SafariSeed { get; set; } + public uint GetSafariIndex(int slot) => (SafariSeed >> (slot * 5)) & 0x1F; + public void SetSafariIndex(int slot, uint value) => SafariSeed = (SafariSeed & ~(0x1Fu << (slot * 5))) | (value << (slot * 5)); } public enum PoketchColor diff --git a/PKHeX.Core/Saves/SAV5.cs b/PKHeX.Core/Saves/SAV5.cs index 55e4331f6..6e23eef9d 100644 --- a/PKHeX.Core/Saves/SAV5.cs +++ b/PKHeX.Core/Saves/SAV5.cs @@ -206,6 +206,7 @@ public EntreeForest EntreeData public abstract BattleSubway5 BattleSubway { get; } public abstract Entralink5 Entralink { get; } public abstract Musical5 Musical { get; } + public abstract Encount5 Encount { get; } public static int GetMailOffset(int index) => (index * Mail5.SIZE) + 0x1DD00; public byte[] GetMailData(int offset) => GetData(offset, Mail5.SIZE); diff --git a/PKHeX.Core/Saves/SAV5B2W2.cs b/PKHeX.Core/Saves/SAV5B2W2.cs index 2d2377133..7e7f04eb4 100644 --- a/PKHeX.Core/Saves/SAV5B2W2.cs +++ b/PKHeX.Core/Saves/SAV5B2W2.cs @@ -51,6 +51,7 @@ private void Initialize() public override BattleSubway5 BattleSubway => Blocks.BattleSubway; public override Entralink5 Entralink => Blocks.Entralink; public override Musical5 Musical => Blocks.Musical; + public override Encount5 Encount => Blocks.Encount; public FestaBlock5 Festa => Blocks.Festa; public PWTBlock5 PWT => Blocks.PWT; public override int Fused => 0x1FA00 + sizeof(uint); diff --git a/PKHeX.Core/Saves/SAV5BW.cs b/PKHeX.Core/Saves/SAV5BW.cs index 39c800c2a..1a5e21f27 100644 --- a/PKHeX.Core/Saves/SAV5BW.cs +++ b/PKHeX.Core/Saves/SAV5BW.cs @@ -51,6 +51,7 @@ private void Initialize() public override BattleSubway5 BattleSubway => Blocks.BattleSubway; public override Entralink5 Entralink => Blocks.Entralink; public override Musical5 Musical => Blocks.Musical; + public override Encount5 Encount => Blocks.Encount; public override int Fused => int.MinValue; public override int GTS => 0x20500; } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/IGen3Hoenn.cs b/PKHeX.Core/Saves/Substructures/Gen3/IGen3Hoenn.cs index 4e5496e1d..8ccf96b52 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/IGen3Hoenn.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/IGen3Hoenn.cs @@ -9,6 +9,7 @@ public interface IGen3Hoenn RTC3 ClockElapsed { get; set; } PokeBlock3Case PokeBlocks { get; set; } DecorationInventory3 Decorations { get; set; } + Swarm3 Swarm { get; set; } bool HasReceivedWishmkrJirachi { get; set; } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/StructConverter.cs b/PKHeX.Core/Saves/Substructures/Gen3/StructConverter.cs index 35c79bea3..26f216ceb 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/StructConverter.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/StructConverter.cs @@ -3,7 +3,7 @@ namespace PKHeX.Core { - internal static class StructConverter + public static class StructConverter { public static T ToStructure(this byte[] bytes) where T : struct { diff --git a/PKHeX.Core/Saves/Substructures/Gen3/Swarm3.cs b/PKHeX.Core/Saves/Substructures/Gen3/Swarm3.cs new file mode 100644 index 000000000..1cd743748 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen3/Swarm3.cs @@ -0,0 +1,23 @@ +using System.Runtime.InteropServices; + +namespace PKHeX.Core +{ + [StructLayout(LayoutKind.Sequential, Pack = 4, Size = SIZE)] + public sealed class Swarm3 + { + public const int SIZE = 0x14; + + public ushort Species { get; set; } + public byte MapNum { get; set; } + public byte MapGroup { get; set; } + public byte Level { get; set; } + // 3byte align + public ushort Move1 { get; set; } + public ushort Move2 { get; set; } + public ushort Move3 { get; set; } + public ushort Move4 { get; set; } + public byte Zero { get; set; } + public byte EncounterProbability { get; set; } + public ushort DaysLeft { get; set; } + } +} diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Encount5.cs b/PKHeX.Core/Saves/Substructures/Gen5/Encount5.cs new file mode 100644 index 000000000..adccff1af --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen5/Encount5.cs @@ -0,0 +1,39 @@ +using System; + +namespace PKHeX.Core +{ + public abstract class Encount5 : SaveBlock + { + protected Encount5(SAV5 SAV, int offset) : base(SAV) => Offset = offset; + + public abstract uint SwarmSeed { get; set; } + public abstract uint SwarmMaxCountModulo { get; } + + public uint SwarmIndex + { + get => SwarmSeed % SwarmMaxCountModulo; + set + { + value %= SwarmMaxCountModulo; + while (SwarmIndex != value) + ++SwarmSeed; + } + } + } + + public sealed class Encount5BW : Encount5 + { + public Encount5BW(SAV5BW SAV, int offset) : base(SAV, offset) { } + + public override uint SwarmSeed { get => BitConverter.ToUInt32(Data, Offset + 0x30); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x30); } + public override uint SwarmMaxCountModulo => 17; + } + + public sealed class Encount5B2W2 : Encount5 + { + public Encount5B2W2(SAV5B2W2 SAV, int offset) : base(SAV, offset) { } + + public override uint SwarmSeed { get => BitConverter.ToUInt32(Data, Offset + 0x2C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x2C); } + public override uint SwarmMaxCountModulo => 19; + } +} diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Entralink5.cs b/PKHeX.Core/Saves/Substructures/Gen5/Entralink5.cs index 73c5d5ecd..11f329b6c 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/Entralink5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/Entralink5.cs @@ -19,37 +19,6 @@ public ushort BlackCityLevel } } - public sealed class Musical5 : SaveBlock - { - public Musical5(SAV5BW SAV, int offset) : base(SAV) => Offset = offset; - public Musical5(SAV5B2W2 SAV, int offset) : base(SAV) => Offset = offset; - - private const int PropOffset = 0x258; - - public void UnlockAllMusicalProps() - { - // 101 props, which is 12.X bytes of bitflags. - var bitFieldOffset = Offset + PropOffset; - for (int i = 0; i < 0xC; i++) - Data[bitFieldOffset + i] = 0xFF; - Data[bitFieldOffset + 0xC] = 0x1F; // top 3 bits unset, to complete multiple of 8 (101=>104 bits). - } - - public bool GetHasProp(int prop) - { - var bitFieldOffset = Offset + PropOffset; - var bitOffset = prop >> 3; - return SAV.GetFlag(bitFieldOffset + bitOffset, prop & 7); - } - - public void SetHasProp(int prop, bool value = true) - { - var bitFieldOffset = Offset + PropOffset; - var bitOffset = prop >> 3; - SAV.SetFlag(bitFieldOffset + bitOffset, prop & 7, value); - } - } - public sealed class Entralink5BW : Entralink5 { public Entralink5BW(SAV5BW SAV, int offset) : base(SAV, offset) { } diff --git a/PKHeX.Core/Saves/Substructures/Gen5/Musical5.cs b/PKHeX.Core/Saves/Substructures/Gen5/Musical5.cs new file mode 100644 index 000000000..314130837 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen5/Musical5.cs @@ -0,0 +1,33 @@ +namespace PKHeX.Core +{ + public sealed class Musical5 : SaveBlock + { + public Musical5(SAV5BW SAV, int offset) : base(SAV) => Offset = offset; + public Musical5(SAV5B2W2 SAV, int offset) : base(SAV) => Offset = offset; + + private const int PropOffset = 0x258; + + public void UnlockAllMusicalProps() + { + // 101 props, which is 12.X bytes of bitflags. + var bitFieldOffset = Offset + PropOffset; + for (int i = 0; i < 0xC; i++) + Data[bitFieldOffset + i] = 0xFF; + Data[bitFieldOffset + 0xC] = 0x1F; // top 3 bits unset, to complete multiple of 8 (101=>104 bits). + } + + public bool GetHasProp(int prop) + { + var bitFieldOffset = Offset + PropOffset; + var bitOffset = prop >> 3; + return SAV.GetFlag(bitFieldOffset + bitOffset, prop & 7); + } + + public void SetHasProp(int prop, bool value = true) + { + var bitFieldOffset = Offset + PropOffset; + var bitOffset = prop >> 3; + SAV.SetFlag(bitFieldOffset + bitOffset, prop & 7, value); + } + } +} diff --git a/Tests/PKHeX.Core.Tests/General/MarshalTests.cs b/Tests/PKHeX.Core.Tests/General/MarshalTests.cs new file mode 100644 index 000000000..a028f2381 --- /dev/null +++ b/Tests/PKHeX.Core.Tests/General/MarshalTests.cs @@ -0,0 +1,21 @@ +using FluentAssertions; +using PKHeX.Core; +using Xunit; + +namespace PKHeX.Tests.General +{ + public class MarshalTests + { + [Fact] + public void MarshalStructure() + { + new DecorationInventory3().ToBytes().Length.Should().Be(DecorationInventory3.SIZE); + } + + [Fact] + public void MarshalClass() + { + new Swarm3().ToBytesClass().Length.Should().Be(Swarm3.SIZE); + } + } +}