From 6980026ca282fd559506f658fa56e0edf93eb0e2 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 20 Nov 2021 15:40:18 -0800 Subject: [PATCH] Add BDSP trainer stat records Continue crawling down the serialized savedata structure :) --- PKHeX.Core/Saves/Blocks/RecordBlock.cs | 2 +- PKHeX.Core/Saves/SAV8BS.cs | 37 ++++++++++++++++--- .../Saves/Substructures/Gen8/BS/Record8b.cs | 33 +++++++++++++++++ .../Saves/Substructures/ITrainerStatRecord.cs | 3 ++ .../Substructures/Misc/IRecordStatStorage.cs | 12 ++++++ PKHeX.Core/Saves/Substructures/Records.cs | 34 +++++++++++++++++ .../Save Editors/Gen8/SAV_Trainer8b.cs | 4 +- 7 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 PKHeX.Core/Saves/Substructures/Gen8/BS/Record8b.cs create mode 100644 PKHeX.Core/Saves/Substructures/Misc/IRecordStatStorage.cs diff --git a/PKHeX.Core/Saves/Blocks/RecordBlock.cs b/PKHeX.Core/Saves/Blocks/RecordBlock.cs index 4c8604a79..7b161dd1f 100644 --- a/PKHeX.Core/Saves/Blocks/RecordBlock.cs +++ b/PKHeX.Core/Saves/Blocks/RecordBlock.cs @@ -2,7 +2,7 @@ namespace PKHeX.Core { - public abstract class RecordBlock : SaveBlock + public abstract class RecordBlock : SaveBlock, IRecordStatStorage { protected abstract IReadOnlyList RecordMax { get; } public abstract int GetRecord(int recordID); diff --git a/PKHeX.Core/Saves/SAV8BS.cs b/PKHeX.Core/Saves/SAV8BS.cs index e31c681b1..fbdaf7564 100644 --- a/PKHeX.Core/Saves/SAV8BS.cs +++ b/PKHeX.Core/Saves/SAV8BS.cs @@ -2,11 +2,9 @@ using System.Collections.Generic; using System.Security.Cryptography; -#nullable disable - namespace PKHeX.Core { - public class SAV8BS : SaveFile, ISaveFileRevision + public class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord { // Save Data Attributes protected internal override string ShortSummary => $"{OT} ({Version})"; @@ -28,7 +26,11 @@ namespace PKHeX.Core MyStatus = new MyStatus8b(this, 0x79BB4); // size: 0x50 Played = new PlayTime8b(this, 0x79C04); // size: 0x04 Contest = new Contest8b(this, 0x79C08); // size: 0x720 - Zukan = new Zukan8b(this, 0x7A328); + Zukan = new Zukan8b(this, 0x7A328); // size: 0x30B8 + // 0x7D3E0 - Trainer Battle Data (bool,bool)[707] + // 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); Initialize(); } @@ -143,6 +145,7 @@ namespace PKHeX.Core public Contest8b Contest { get; } // public Misc8 Misc { get; } public Zukan8b Zukan { get; } + public Record8b Records { get; } #endregion public override GameVersion Version => Game switch @@ -218,7 +221,13 @@ namespace PKHeX.Core pk.Trade(this, Date.Day, Date.Month, Date.Year); pkm.RefreshChecksum(); - //AddCountAcquired(pkm); + AddCountAcquired(pkm); + } + + private void AddCountAcquired(PKM pkm) + { + // There aren't many records, and they only track Capture/Fish/Hatch/Defeat. + Records.AddRecord(pkm.WasEgg ? 004 : 002); // egg, capture } protected override void SetDex(PKM pkm) => Zukan.SetDex(pkm); @@ -233,5 +242,23 @@ namespace PKHeX.Core public override PKM GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data)); public override PKM GetBoxSlot(int offset) => GetDecryptedPKM(GetData(Data, offset, SIZE_PARTY)); // party format in boxes! + + public enum TopMenuItemType + { + Zukan = 0, + Pokemon = 1, + Bag = 2, + Card = 3, + Map = 4, + Seal = 5, + Setting = 6, + Gift = 7, + } + + public int RecordCount => Record8b.RecordCount; + public int GetRecord(int recordID) => Records.GetRecord(recordID); + public int GetRecordOffset(int recordID) => Records.GetRecordOffset(recordID); + public int GetRecordMax(int recordID) => Record8b.RecordMaxValue; + public void SetRecord(int recordID, int value) => Records.SetRecord(recordID, value); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/BS/Record8b.cs b/PKHeX.Core/Saves/Substructures/Gen8/BS/Record8b.cs new file mode 100644 index 000000000..fc1e5535f --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen8/BS/Record8b.cs @@ -0,0 +1,33 @@ +using System; + +namespace PKHeX.Core +{ + public sealed class Record8b : SaveBlock, IRecordStatStorage + { + public const int RecordCount = 30; + public const int RecordMaxValue = 999_999; + + public Record8b(SAV8BS sav, int offset) : base(sav) => Offset = offset; + + public int GetRecordOffset(int recordID) + { + if ((uint)recordID >= RecordCount) + throw new ArgumentOutOfRangeException(nameof(recordID)); + return Offset + (sizeof(int) * recordID); + } + + public int GetRecord(int recordID) + { + var value = BitConverter.ToInt32(Data, GetRecordOffset(recordID)); + return Math.Min(RecordMaxValue, value); + } + + public void SetRecord(int recordID, int value) + { + var toWrite = Math.Min(RecordMaxValue, value); + BitConverter.GetBytes(toWrite).CopyTo(Data, GetRecordOffset(recordID)); + } + + public void AddRecord(int recordID, int count = 1) => SetRecord(recordID, GetRecord(recordID) + count); + } +} diff --git a/PKHeX.Core/Saves/Substructures/ITrainerStatRecord.cs b/PKHeX.Core/Saves/Substructures/ITrainerStatRecord.cs index 94b8afde5..c48c8f1a9 100644 --- a/PKHeX.Core/Saves/Substructures/ITrainerStatRecord.cs +++ b/PKHeX.Core/Saves/Substructures/ITrainerStatRecord.cs @@ -1,5 +1,8 @@ namespace PKHeX.Core { + /// + /// Provides an API for fluent record editors. + /// public interface ITrainerStatRecord { int GetRecord(int recordID); diff --git a/PKHeX.Core/Saves/Substructures/Misc/IRecordStatStorage.cs b/PKHeX.Core/Saves/Substructures/Misc/IRecordStatStorage.cs new file mode 100644 index 000000000..3ccad57cb --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Misc/IRecordStatStorage.cs @@ -0,0 +1,12 @@ +namespace PKHeX.Core +{ + /// + /// Provides a minimal API for mutating stat records. + /// + public interface IRecordStatStorage + { + int GetRecord(int recordID); + void SetRecord(int recordID, int value); + void AddRecord(int recordID, int count = 1); + } +} diff --git a/PKHeX.Core/Saves/Substructures/Records.cs b/PKHeX.Core/Saves/Substructures/Records.cs index 636188365..8ad08ca0c 100644 --- a/PKHeX.Core/Saves/Substructures/Records.cs +++ b/PKHeX.Core/Saves/Substructures/Records.cs @@ -591,5 +591,39 @@ namespace PKHeX.Core public const int G8BattleTowerSingleWin = 47; public const int G8BattleTowerDoubleWin = 48; + + public static readonly Dictionary RecordList_8b = new() + { + { 00, "CLEAR_TIME" }, + { 01, "DENDOU_CNT" }, + { 02, "CAPTURE_POKE" }, + { 03, "FISHING_SUCCESS" }, + { 04, "TAMAGO_HATCHING" }, + { 05, "BEAT_DOWN_POKE" }, + { 06, "RENSHOU_SINGLE" }, + { 07, "RENSHOU_SINGLE_NOW" }, + { 08, "RENSHOU_DOUBLE" }, + { 09, "RENSHOU_DOUBLE_NOW" }, + { 10, "RENSHOU_MASTER_SINGLE" }, + { 11, "RENSHOU_MASTER_SINGLE_NOW" }, + { 12, "RENSHOU_MASTER_DOUBLE" }, + { 13, "RENSHOU_MASTER_DOUBLE_NOW" }, + { 14, "BTL_TOWER_AVERAGE" }, + { 15, "CONTEST_STYLE_RANK" }, + { 16, "CONTEST_BEATIFUL_RANK" }, + { 17, "CONTEST_CUTE_RANK" }, + { 18, "CONTEST_CLEVER_RANK" }, + { 19, "CONTEST_STRONG_RANK" }, + { 20, "CONTEST_PLAY_SINGLE" }, + { 21, "CONTEST_PLAY_LOCAL" }, + { 22, "CONTEST_PLAY_NETWORK" }, + { 23, "CONTEST_WIN_SINGLE" }, + { 24, "CONTEST_WIN_LOCAL" }, + { 25, "CONTEST_WIN_NETWORK" }, + { 26, "CONTEST_RATE_SINGLE" }, + { 27, "CONTEST_RATE_LOCAL" }, + { 28, "CONTEST_RATE_NETWORK" }, + { 29, "CONTEST_GET_RIBBON" }, + }; } } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_Trainer8b.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_Trainer8b.cs index 83a0c407d..5fa9c7aed 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_Trainer8b.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_Trainer8b.cs @@ -32,6 +32,8 @@ namespace PKHeX.WinForms GetComboBoxes(); GetTextBoxes(); + TrainerStats.LoadRecords(SAV, Records.RecordList_8b); + Loading = false; } @@ -47,7 +49,7 @@ namespace PKHeX.WinForms CB_Game.SelectedIndex = Math.Max(0, Math.Min(1, SAV.Game - (int)GameVersion.BD)); CB_Gender.SelectedIndex = SAV.Gender; - GB_Stats.Visible = false; + L_BP.Visible = NUD_BP.Visible = false; // Display Data TB_OTName.Text = SAV.OT;