From b50791530ce0d09abfcfc4b6cc7a463eae064f12 Mon Sep 17 00:00:00 2001 From: Kurt Date: Fri, 26 Nov 2021 01:21:38 -0800 Subject: [PATCH] Add ENC_SV_DATA, PLAYER_SAVE_DATA, capsules, UgCountRecord --- PKHeX.Core/Saves/SAV8BS.cs | 20 +- .../Substructures/Gen8/BS/EncounterSave8b.cs | 186 ++++++++++++++++++ .../Substructures/Gen8/BS/PlayerData8b.cs | 95 +++++++++ .../Saves/Substructures/Gen8/BS/SealDeco8b.cs | 113 +++++++++++ .../Substructures/Gen8/BS/UgCountRecord8b.cs | 51 +++++ 5 files changed, 457 insertions(+), 8 deletions(-) create mode 100644 PKHeX.Core/Saves/Substructures/Gen8/BS/EncounterSave8b.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen8/BS/PlayerData8b.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen8/BS/SealDeco8b.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen8/BS/UgCountRecord8b.cs diff --git a/PKHeX.Core/Saves/SAV8BS.cs b/PKHeX.Core/Saves/SAV8BS.cs index 59b28b644..ca0d0d3aa 100644 --- a/PKHeX.Core/Saves/SAV8BS.cs +++ b/PKHeX.Core/Saves/SAV8BS.cs @@ -37,9 +37,9 @@ namespace PKHeX.Core // 0x7E9F8 - Menu selections (TopMenuItemTypeInt32, bool IsNew)[8], TopMenuItemTypeInt32 LastSelected // 0x7EA3C - _FIELDOBJ_SAVE Objects[1000] (sizeof (0x44, 17 int fields), total size 0x109A0 Records = new Record8b(this, 0x8F3DC); // size: 0x78 * 12 - // 0x8F97C - ENC_SV_DATA; 21 honey trees, 3 sway grass info, 2 mvpoke - // PLAYER_SAVE_DATA - // SaveBallDecoData CapsuleData[99], AffixSealData[20] + Encounter = new EncounterSave8b(this, 0x8F97C); // size: 0x188 + Player = new PlayerData8b(this, 0x8FB04); // 0x80 + SealDeco = new SealBallDecoData8b(this, 0x8FB84); // size: 0x4288 SealList = new SealList8b(this, 0x93E0C); // size: 0x960 SaveSealData[200] // _RANDOM_GROUP // FieldGimmickSaveData; int[3] gearRotate @@ -56,13 +56,13 @@ namespace PKHeX.Core UgSaveData = new UgSaveData8b(this, 0x9A89C); // size: 0x27A0 // 0x9D03C - GMS_DATA // size: 0x31304, (GMS_POINT_DATA[650], ushort, ushort, byte)?; substructure GMS_POINT_HISTORY_DATA[5] // 0xCE340 - PLAYER_NETWORK_DATA; bcatFlagArray byte[1300] - // UnionSaveData - // CON_PHOTO_LANG_DATA -- contest photo language data; photo_data[5], photo_fx[5] + // 0xCEA10(?) - UnionSaveData + // 0xCEA1C(?) - CON_PHOTO_LANG_DATA -- contest photo language data; photo_data[5], photo_fx[5] // ZUKAN_PERSONAL_RND_DATA -- Spinda PID storage; uint[4] see, uint[4] get, uint[17] reserve // CON_PHOTO_EXT_DATA[5] - // GMS_POINT_HISTORY_EXT_DATA[] - // UgCountRecord - // ReBuffnameData + // GMS_POINT_HISTORY_EXT_DATA[3250] + UgCount = new UgCountRecord8b(this, 0xE8178); // size: 0x20 + // 0xE8198 - ReBuffnameData; RE_DENDOU_RECORD[30], RE_DENDOU_POKEMON_DATA_INSIDE[6] (0x20) = 0x1680 // 0xE9818 -- 0x10 byte[] MD5 hash of all savedata; // v1.1 additions @@ -192,6 +192,9 @@ namespace PKHeX.Core public Zukan8b Zukan { get; } public BattleTrainerStatus8b BattleTrainer { get; } public Record8b Records { get; } + public EncounterSave8b Encounter { get; } + public PlayerData8b Player { get; } + public SealBallDecoData8b SealDeco { get; } public SealList8b SealList { get; } public BerryTreeGrowSave8b BerryTrees { get; } public PoffinSaveData8b Poffins { get; } @@ -200,6 +203,7 @@ namespace PKHeX.Core public Poketch8b Poketch { get; } public Daycare8b Daycare { get; } public UgSaveData8b UgSaveData { get; } + public UgCountRecord8b UgCount { get; } // First Savedata Expansion! public RecordAddData8b RecordAdd { get; } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/EncounterSave8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/EncounterSave8b.cs new file mode 100644 index 000000000..38f828f00 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/EncounterSave8b.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; + +namespace PKHeX.Core +{ + /// + /// Encounter Save Data + /// + /// size 0x188, struct_name ENC_SV_DATA + [TypeConverter(typeof(ExpandableObjectConverter))] + public sealed class EncounterSave8b : SaveBlock + { + public const int COUNT_HONEYTREE = 21; + private const int SIZE_HillBackData = 0x8; + private const int SIZE_HoneyTree = 8 + 4 + (COUNT_HONEYTREE * HoneyTree8b.SIZE); // 0x108 + private const int SIZE_Roamer = 0x20; + private const int SIZE_Closing = 10 + 2; // 2 padding alignment + + private const int OFS_HillBackData = 0xC; + private const int OFS_HoneyTree = OFS_HillBackData + SIZE_HillBackData; // 0x14 + private const int OFS_SWAY = OFS_HoneyTree + SIZE_HoneyTree; // 0x11C + private const int OFS_ZONEHISTORY = OFS_SWAY + (4 * 6); // 0x134 + private const int OFS_ROAM1 = OFS_ZONEHISTORY + 4 + 4; // 0x13C + private const int OFS_ROAM2 = OFS_ROAM1 + SIZE_Roamer; // 0x15C + private const int OFS_CLOSING = OFS_ROAM2 + SIZE_Roamer; // 0x17C + private const int SIZE = OFS_CLOSING + SIZE_Closing; // 0x188 + + public EncounterSave8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; + + public void Clear() => Data.AsSpan(Offset, SIZE).Clear(); + + public int EncounterWalkCount + { + get => BitConverter.ToInt32(Data, Offset + 0x00); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x00); + } + + public int SafariRandSeed + { + get => BitConverter.ToInt32(Data, Offset + 0x04); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x04); + } + + public int GenerateRandSeed + { + get => BitConverter.ToInt32(Data, Offset + 0x08); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); + } + + // HILL_BACK_DATA + public bool HillTalkFlag + { + get => BitConverter.ToUInt32(Data, Offset + OFS_HillBackData + 0x00) == 1; + set => BitConverter.GetBytes(value ? 1u : 0).CopyTo(Data, Offset + OFS_HillBackData + 0x00); + } + public ushort HillEncTblIdx1 + { + get => BitConverter.ToUInt16(Data, Offset + OFS_HillBackData + 0x04); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_HillBackData + 0x04); + } + public ushort HillEncTblIdx2 + { + get => BitConverter.ToUInt16(Data, Offset + OFS_HillBackData + 0x06); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_HillBackData + 0x06); + } + + // HONEY_TREE + public long HoneyLastUpdateMinutes + { + get => BitConverter.ToInt64(Data, Offset + OFS_HoneyTree + 0x00); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_HoneyTree + 0x00); + } + public byte HoneyTreeNo + { + get => Data[Offset + OFS_HoneyTree + 0x08]; + set => Data[Offset + OFS_HoneyTree + 0x08] = value; + } + +#pragma warning disable CA1819 // Properties should not return arrays + public HoneyTree8b[] HoneyTrees + { + get => GetTrees(); + set => SetTrees(value); + } +#pragma warning restore CA1819 // Properties should not return arrays + + private HoneyTree8b[] GetTrees() + { + var result = new HoneyTree8b[COUNT_HONEYTREE]; + for (int i = 0; i < result.Length; i++) + result[i] = new HoneyTree8b(Data, Offset + OFS_HoneyTree + 0xC + (i * HoneyTree8b.SIZE)); + return result; + } + + private static void SetTrees(IReadOnlyList value) + { + if (value.Count != COUNT_HONEYTREE) + throw new ArgumentOutOfRangeException(nameof(value.Count)); + // data is already hard-referencing the original byte array. This is mostly a hack for Property Grid displays. + } + + public uint Radar1Species { get => BitConverter.ToUInt32(Data, Offset + OFS_SWAY + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SWAY + 0x00); } + public uint Radar1Chain { get => BitConverter.ToUInt32(Data, Offset + OFS_SWAY + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SWAY + 0x04); } + public uint Radar2Species { get => BitConverter.ToUInt32(Data, Offset + OFS_SWAY + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SWAY + 0x08); } + public uint Radar2Chain { get => BitConverter.ToUInt32(Data, Offset + OFS_SWAY + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SWAY + 0x0C); } + public uint Radar3Species { get => BitConverter.ToUInt32(Data, Offset + OFS_SWAY + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SWAY + 0x10); } + public uint Radar3Chain { get => BitConverter.ToUInt32(Data, Offset + OFS_SWAY + 0x14); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_SWAY + 0x14); } + + public int BeforeZone { get => BitConverter.ToInt32(Data, Offset + OFS_ZONEHISTORY + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ZONEHISTORY + 0x00); } + public int OldZone { get => BitConverter.ToInt32(Data, Offset + OFS_ZONEHISTORY + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ZONEHISTORY + 0x04); } + + public int Roamer1ZoneID { get => BitConverter.ToInt32 (Data, Offset + OFS_ROAM1 + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM1 + 0x00); } + public ulong Roamer1Seed { get => BitConverter.ToUInt64(Data, Offset + OFS_ROAM1 + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM1 + 0x04); } + public uint Roamer1Species { get => BitConverter.ToUInt32(Data, Offset + OFS_ROAM1 + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM1 + 0x0C); } + public uint Roamer1HP { get => BitConverter.ToUInt32(Data, Offset + OFS_ROAM1 + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM1 + 0x10); } + public byte Roamer1Level { get => Data[Offset + OFS_ROAM1 + 0x14]; set => Data[Offset + OFS_ROAM1 + 0x14] = value; } + public uint Roamer1Status { get => BitConverter.ToUInt32(Data, Offset + OFS_ROAM1 + 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM1 + 0x18); } + public byte Roamer1Encount { get => Data[Offset + OFS_ROAM1 + 0x1C]; set => Data[Offset + OFS_ROAM1 + 0x1C] = value; } + + public int Roamer2ZoneID { get => BitConverter.ToInt32 (Data, Offset + OFS_ROAM2 + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM2 + 0x00); } + public ulong Roamer2Seed { get => BitConverter.ToUInt64(Data, Offset + OFS_ROAM2 + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM2 + 0x04); } + public uint Roamer2Species { get => BitConverter.ToUInt32(Data, Offset + OFS_ROAM2 + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM2 + 0x0C); } + public uint Roamer2HP { get => BitConverter.ToUInt32(Data, Offset + OFS_ROAM2 + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM2 + 0x10); } + public byte Roamer2Level { get => Data[Offset + OFS_ROAM2 + 0x14]; set => Data[Offset + OFS_ROAM2 + 0x14] = value; } + public uint Roamer2Status { get => BitConverter.ToUInt32(Data, Offset + OFS_ROAM2 + 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_ROAM2 + 0x18); } + public byte Roamer2Encount { get => Data[Offset + OFS_ROAM2 + 0x1C]; set => Data[Offset + OFS_ROAM2 + 0x1C] = value; } + + public bool GenerateValid + { + get => BitConverter.ToUInt32(Data, Offset + OFS_CLOSING + 0) == 1; + set => BitConverter.GetBytes(value ? 1u : 0).CopyTo(Data, Offset + OFS_CLOSING + 0); + } + public short SprayCount + { + get => BitConverter.ToInt16(Data, Offset + OFS_CLOSING + 4); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_CLOSING + 4); + } + public byte SprayType { get => Data[Offset + OFS_CLOSING + 6]; set => Data[Offset + OFS_CLOSING + 6] = value; } + public byte VsSeekerCharge { get => Data[Offset + OFS_CLOSING + 7]; set => Data[Offset + OFS_CLOSING + 7] = value; } // max 100 + public byte PokeRadarCharge { get => Data[Offset + OFS_CLOSING + 8]; set => Data[Offset + OFS_CLOSING + 8] = value; } // max 50 + public byte FluteType { get => Data[Offset + OFS_CLOSING + 9]; set => Data[Offset + OFS_CLOSING + 9] = value; } // vidro + } + + [TypeConverter(typeof(ExpandableObjectConverter))] + public class HoneyTree8b + { + public const int SIZE = 0xC; + + private readonly int Offset; + private readonly byte[] Data; + + public HoneyTree8b(byte[] data, int offset) + { + Data = data; + Offset = offset; + } + + public bool Spreaded + { + get => BitConverter.ToUInt32(Data, Offset + 0x00) == 1; + set => BitConverter.GetBytes(value ? 1u : 0).CopyTo(Data, Offset + 0x00); + } + public int Minutes + { + get => BitConverter.ToInt32(Data, Offset + 0x04); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x04); + } + public byte TblMonsNo + { + get => Data[Offset + 0x08]; + set => Data[Offset + 0x08] = value; + } + public byte RareLv + { + get => Data[Offset + 0x09]; + set => Data[Offset + 0x09] = value; + } + public byte SwayLv + { + get => Data[Offset + 0x0A]; + set => Data[Offset + 0x0A] = value; + } + // 0xB alignment + } +} diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayerData8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayerData8b.cs new file mode 100644 index 000000000..615031090 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/PlayerData8b.cs @@ -0,0 +1,95 @@ +using System; +using System.ComponentModel; + +namespace PKHeX.Core +{ + /// + /// Player Map Location Save Data + /// + /// size 0x80, struct_name PLAYER_SAVE_DATA + [TypeConverter(typeof(ExpandableObjectConverter))] + public sealed class PlayerData8b : SaveBlock + { + private const int SIZE_LOCATION = 4 + (4 * 3) + 4; // 20 (0x14) + + private const int OFS_LOC1 = 0x10; + private const int OFS_LOC2 = OFS_LOC1 + SIZE_LOCATION; + private const int OFS_LOC3 = OFS_LOC2 + SIZE_LOCATION; + private const int OFS_PART2 = OFS_LOC3 + SIZE_LOCATION; + private const int OFS_MAP = OFS_PART2 + 4 + 4; + private const int OFS_TOKUSHU_BOOL = OFS_MAP + SIZE_LOCATION; + private const int OFS_TOKUSHU = OFS_TOKUSHU_BOOL + 4; + private const int SIZE = OFS_TOKUSHU + SIZE_LOCATION; // 0x80 + + public PlayerData8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; + + public void Clear() => Data.AsSpan(Offset, SIZE).Clear(); + + public bool GearType + { + get => BitConverter.ToUInt32(Data, Offset + 0x00) == 1; + set => BitConverter.GetBytes(value ? 1u : 0).CopyTo(Data, Offset + 0x00); + } + public bool ShoesFlag + { + get => BitConverter.ToUInt32(Data, Offset + 0x04) == 1; + set => BitConverter.GetBytes(value ? 1u : 0).CopyTo(Data, Offset + 0x04); + } + public uint Form + { + get => BitConverter.ToUInt32(Data, Offset + 0x08); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); + } + public byte BikeColor { get => Data[Offset + 0x0C]; set => Data[Offset + 0x0C] = value; } + // 0x10: WorpPoint - Teleport + // 0x10: WorpPoint - Zenmetu + // 0x10: WorpPoint - Ananuke + + public int WarpTeleportZone { get => BitConverter.ToInt32 (Data, Offset + OFS_LOC1 + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC1 + 0x00); } + public float WarpTeleportX { get => BitConverter.ToSingle(Data, Offset + OFS_LOC1 + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC1 + 0x04); } + public float WarpTeleportY { get => BitConverter.ToSingle(Data, Offset + OFS_LOC1 + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC1 + 0x08); } + public float WarpTeleportZ { get => BitConverter.ToSingle(Data, Offset + OFS_LOC1 + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC1 + 0x0C); } + public int WarpTeleportDir { get => BitConverter.ToInt32 (Data, Offset + OFS_LOC1 + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC1 + 0x10); } + + public int WarpZenmetuZone { get => BitConverter.ToInt32 (Data, Offset + OFS_LOC2 + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC2 + 0x00); } + public float WarpZenmetuX { get => BitConverter.ToSingle(Data, Offset + OFS_LOC2 + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC2 + 0x04); } + public float WarpZenmetuY { get => BitConverter.ToSingle(Data, Offset + OFS_LOC2 + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC2 + 0x08); } + public float WarpZenmetuZ { get => BitConverter.ToSingle(Data, Offset + OFS_LOC2 + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC2 + 0x0C); } + public int WarpZenmetuDir { get => BitConverter.ToInt32 (Data, Offset + OFS_LOC2 + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC2 + 0x10); } + + public int WarpAnanukeZone { get => BitConverter.ToInt32 (Data, Offset + OFS_LOC3 + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC3 + 0x00); } + public float WarpAnanukeX { get => BitConverter.ToSingle(Data, Offset + OFS_LOC3 + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC3 + 0x04); } + public float WarpAnanukeY { get => BitConverter.ToSingle(Data, Offset + OFS_LOC3 + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC3 + 0x08); } + public float WarpAnanukeZ { get => BitConverter.ToSingle(Data, Offset + OFS_LOC3 + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC3 + 0x0C); } + public int WarpAnanukeDir { get => BitConverter.ToInt32 (Data, Offset + OFS_LOC3 + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_LOC3 + 0x10); } + + public float WalkCount + { + get => BitConverter.ToSingle(Data, Offset + OFS_PART2 + 0x00); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_PART2 + 0x04); + } + public int NatukiWalkCount + { + get => BitConverter.ToInt32(Data, Offset + OFS_PART2 + 0x04); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_PART2 + 0x04); + } + + public int TownMapZone { get => BitConverter.ToInt32 (Data, Offset + OFS_MAP + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_MAP + 0x00); } + public float TownMapX { get => BitConverter.ToSingle(Data, Offset + OFS_MAP + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_MAP + 0x04); } + public float TownMapY { get => BitConverter.ToSingle(Data, Offset + OFS_MAP + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_MAP + 0x08); } + public float TownMapZ { get => BitConverter.ToSingle(Data, Offset + OFS_MAP + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_MAP + 0x0C); } + public int TownMapDir { get => BitConverter.ToInt32 (Data, Offset + OFS_MAP + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_MAP + 0x10); } + + public bool IsTokushuLocation + { + get => BitConverter.ToUInt32(Data, Offset + OFS_TOKUSHU_BOOL + 0x00) == 1; + set => BitConverter.GetBytes(value ? 1u : 0).CopyTo(Data, Offset + OFS_TOKUSHU_BOOL + 0x00); + } + + public int TokushuZone { get => BitConverter.ToInt32 (Data, Offset + OFS_TOKUSHU + 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_TOKUSHU + 0x00); } + public float TokushuX { get => BitConverter.ToSingle(Data, Offset + OFS_TOKUSHU + 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_TOKUSHU + 0x04); } + public float TokushuY { get => BitConverter.ToSingle(Data, Offset + OFS_TOKUSHU + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_TOKUSHU + 0x08); } + public float TokushuZ { get => BitConverter.ToSingle(Data, Offset + OFS_TOKUSHU + 0x0C); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_TOKUSHU + 0x0C); } + public int TokushuDir { get => BitConverter.ToInt32 (Data, Offset + OFS_TOKUSHU + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + OFS_TOKUSHU + 0x10); } + } +} diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/SealDeco8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/SealDeco8b.cs new file mode 100644 index 000000000..61cccee2d --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/SealDeco8b.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; + +namespace PKHeX.Core +{ + /// + /// Stores customized ball seal configurations. + /// + /// size 0x4288 + [TypeConverter(typeof(ExpandableObjectConverter))] + public sealed class SealBallDecoData8b : SaveBlock + { + public const int COUNT_CAPSULE = 99; // CapsuleData[99] + + private const int SIZE = 4 + (COUNT_CAPSULE * SealCapsule8b.SIZE); // 0x4288 + + public SealBallDecoData8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; + + public void Clear() => Data.AsSpan(Offset, SIZE).Clear(); + + public byte CapsuleCount { get => Data[Offset]; set => Data[Offset] = value; } + +#pragma warning disable CA1819 // Properties should not return arrays + public SealCapsule8b[] Capsules + { + get => GetCapsules(); + set => SetCapsules(value); + } +#pragma warning restore CA1819 // Properties should not return arrays + + private SealCapsule8b[] GetCapsules() + { + var result = new SealCapsule8b[COUNT_CAPSULE]; + for (int i = 0; i < result.Length; i++) + result[i] = new SealCapsule8b(Data, Offset + 4 + (i * SealCapsule8b.SIZE)); + return result; + } + + private static void SetCapsules(IReadOnlyList value) + { + if (value.Count != COUNT_CAPSULE) + throw new ArgumentOutOfRangeException(nameof(value.Count)); + // data is already hard-referencing the original byte array. This is mostly a hack for Property Grid displays. + } + } + + [TypeConverter(typeof(ExpandableObjectConverter))] + public sealed class SealCapsule8b + { + public const int COUNT_SEAL = 20; // AffixSealData[20] + public const int SIZE = 12 + (COUNT_SEAL * AffixSealData8b.SIZE); // 0xAC + + private readonly int Offset; + private readonly byte[] Data; + + public override string ToString() => $"{(Species)Species}-{EncryptionConstant:X8}-{Unknown}"; + + public SealCapsule8b(byte[] data, int offset) + { + Data = data; + Offset = offset; + } + public uint Species { get => BitConverter.ToUInt32(Data, Offset + 0); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0); } + public uint EncryptionConstant { get => BitConverter.ToUInt32(Data, Offset + 4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 4); } + public uint Unknown { get => BitConverter.ToUInt32(Data, Offset + 8); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 8); } + +#pragma warning disable CA1819 // Properties should not return arrays + public AffixSealData8b[] Seals + { + get => GetSeals(); + set => SetSeals(value); + } +#pragma warning restore CA1819 // Properties should not return arrays + + private AffixSealData8b[] GetSeals() + { + var result = new AffixSealData8b[COUNT_SEAL]; + for (int i = 0; i < result.Length; i++) + result[i] = new AffixSealData8b(Data, Offset + 0xC + (i * AffixSealData8b.SIZE)); + return result; + } + + private static void SetSeals(IReadOnlyList value) + { + if (value.Count != COUNT_SEAL) + throw new ArgumentOutOfRangeException(nameof(value.Count)); + // data is already hard-referencing the original byte array. This is mostly a hack for Property Grid displays. + } + } + + [TypeConverter(typeof(ExpandableObjectConverter))] + public class AffixSealData8b + { + public const int SIZE = 8; // u16 id, s16 x,y,z + + private readonly int Offset; + private readonly byte[] Data; + + public override string ToString() => $"{SealID}-({X},{Y},{Z})"; + + public AffixSealData8b(byte[] data, int offset) + { + Data = data; + Offset = offset; + } + + public ushort SealID { get => BitConverter.ToUInt16(Data, Offset + 0); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0); } + public short X { get => BitConverter.ToInt16(Data, Offset + 2); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 2); } + public short Y { get => BitConverter.ToInt16(Data, Offset + 4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 4); } + public short Z { get => BitConverter.ToInt16(Data, Offset + 6); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 6); } + } +} diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/UgCountRecord8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/UgCountRecord8b.cs new file mode 100644 index 000000000..887d2801e --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/UgCountRecord8b.cs @@ -0,0 +1,51 @@ +using System; +using System.ComponentModel; + +namespace PKHeX.Core +{ + /// + /// Underground player metadata counts. + /// + /// size 0x20, struct_name UgCountRecord + [TypeConverter(typeof(ExpandableObjectConverter))] + public sealed class UgCountRecord8b : SaveBlock + { + public UgCountRecord8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; + + public short DigFossilPlayCount + { + get => BitConverter.ToInt16(Data, Offset + 0x00); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x00); + } + public short NumStatueBroadcastOnTV + { + get => BitConverter.ToInt16(Data, Offset + 0x02); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x02); + } + public int NumTimesSecretBaseBroadcastOnTVWereLiked + { + get => BitConverter.ToInt32(Data, Offset + 0x04); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x04); + } + public int SomeoneSecretBaseLikeCount + { + get => BitConverter.ToInt32(Data, Offset + 0x08); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); + } + public int NumSuccessfulLightStoneSearches + { + get => BitConverter.ToInt32(Data, Offset + 0x0C); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x0C); + } + public long Reserved1 + { + get => BitConverter.ToInt64(Data, Offset + 0x10); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10); + } + public long Reserved2 + { + get => BitConverter.ToInt64(Data, Offset + 0x18); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x18); + } + } +}