Add gen5 trainer record edits

Refer to src (Record5) for indexes; documented a few.
Closes #4245
This commit is contained in:
Kurt 2024-04-13 09:06:16 -05:00
parent 9caab05aab
commit 2e62e41ab1
11 changed files with 234 additions and 4 deletions

View file

@ -6,7 +6,7 @@
リーフグリーン
ハートゴールド
ソウルシルバー 
ソウルシルバー
ダイヤモンド
パール

View file

@ -22,4 +22,5 @@ public interface ISaveBlock5BW
GlobalLink5 GlobalLink { get; }
GTS5 GTS { get; }
AdventureInfo5 AdventureInfo { get; }
Record5 Records { get; }
}

View file

@ -97,6 +97,7 @@ public sealed class SaveBlockAccessor5B2W2(SAV5B2W2 sav)
public GlobalLink5 GlobalLink { get; } = new(sav, Block(sav, 35));
public Chatter5 Chatter { get; } = new(sav, Block(sav, 36));
public AdventureInfo5 AdventureInfo { get; } = new(sav, Block(sav, 37));
public Record5 Records { get; } = new(sav, Block(sav, 38));
public Musical5 Musical { get; } = new(sav, Block(sav, 42));
public WhiteBlack5B2W2 Forest { get; } = new(sav, Block(sav, 43));
public EventWork5B2W2 EventWork { get; } = new(sav, Block(sav, 45));

View file

@ -92,6 +92,7 @@ public sealed class SaveBlockAccessor5BW(SAV5BW sav) : ISaveBlockAccessor<BlockI
public GlobalLink5 GlobalLink { get; } = new(sav, Block(sav, 35));
public Chatter5 Chatter { get; } = new(sav, Block(sav, 36));
public AdventureInfo5 AdventureInfo { get; } = new(sav, Block(sav, 37));
public Record5 Records { get; } = new(sav, Block(sav, 38));
public Musical5 Musical { get; } = new(sav, Block(sav, 42));
public WhiteBlack5BW Forest { get; } = new(sav, Block(sav, 43));
public EventWork5BW EventWork { get; } = new(sav, Block(sav, 45));

View file

@ -191,6 +191,7 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlagProvider37, IBox
public abstract WhiteBlack5 Forest { get; }
public abstract GTS5 GTS { get; }
public abstract AdventureInfo5 AdventureInfo { get; }
public abstract Record5 Records { get; }
IEventFlag37 IEventFlagProvider37.EventWork => EventWork;
public abstract Memory<byte> BattleVideoNative { get; }

View file

@ -56,6 +56,7 @@ public sealed class SAV5B2W2 : SAV5, ISaveBlock5B2W2
public override GTS5 GTS => Blocks.GTS;
public override WhiteBlack5B2W2 Forest => Blocks.Forest;
public override AdventureInfo5 AdventureInfo => Blocks.AdventureInfo;
public override Record5 Records => Blocks.Records;
public FestaBlock5 Festa => Blocks.Festa;
public PWTBlock5 PWT => Blocks.PWT;

View file

@ -56,6 +56,7 @@ public sealed class SAV5BW : SAV5
public override GTS5 GTS => Blocks.GTS;
public override WhiteBlack5BW Forest => Blocks.Forest;
public override AdventureInfo5 AdventureInfo => Blocks.AdventureInfo;
public override Record5 Records => Blocks.Records;
public override Memory<byte> BattleVideoNative => Data.AsMemory(0x4A000, BattleVideo5.SIZE_USED);
public override Memory<byte> BattleVideoDownload1 => Data.AsMemory(0x4C000, BattleVideo5.SIZE_USED);

View file

@ -61,8 +61,8 @@ public sealed class FestaBlock5(SAV5B2W2 SAV, Memory<byte> raw) : SaveBlock<SAV5
public Funfest5Score GetMissionRecord(int mission)
{
var raw = ReadUInt32LittleEndian(Data[GetMissionRecordOffset(mission)..]);
return new Funfest5Score(raw);
var value = ReadUInt32LittleEndian(Data[GetMissionRecordOffset(mission)..]);
return new Funfest5Score(value);
}
public void SetMissionRecord(int mission, Funfest5Score score)

View file

@ -0,0 +1,105 @@
using System;
using System.Buffers.Binary;
namespace PKHeX.Core;
public class Record5(SAV5 SAV, Memory<byte> raw) : SaveBlock<SAV5>(SAV, raw)
{
private Span<byte> DataRegion => Data[4..^4]; // 4..0x1DC
private uint CryptoSeed // 0x1DC
{
get => BinaryPrimitives.ReadUInt32LittleEndian(Data[^4..]);
set => BinaryPrimitives.WriteUInt32LittleEndian(Data[^4..], value);
}
private bool IsDecrypted;
public void EndAccess() => EnsureDecrypted(false);
private void EnsureDecrypted(bool state = true)
{
if (IsDecrypted == state)
return;
PokeCrypto.CryptArray(DataRegion, CryptoSeed);
IsDecrypted = state;
}
public uint Revision // 0x00
{
get => BinaryPrimitives.ReadUInt32LittleEndian(Data);
set => BinaryPrimitives.WriteUInt32LittleEndian(Data, value);
}
public const int Record32 = 68;
public const int Record16 = 100;
private const int Partition2 = Record32 * sizeof(uint);
private Span<byte> Record32Data => DataRegion[..Partition2];
private Span<byte> Record16Data => DataRegion[Partition2..];
private const uint Max32 = 999_999_999;
private const ushort Max16 = 9999;
public uint GetRecord32(int index)
{
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual<uint>((uint)index, Record32);
EnsureDecrypted();
return BinaryPrimitives.ReadUInt32LittleEndian(Record32Data[(index * 4)..]);
}
public void SetRecord32(int index, uint value)
{
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual<uint>((uint)index, Record32);
EnsureDecrypted();
BinaryPrimitives.WriteUInt32LittleEndian(Record32Data[(index * 4)..], value);
}
public ushort GetRecord16(int index)
{
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual<uint>((uint)index, Record16);
EnsureDecrypted();
return BinaryPrimitives.ReadUInt16LittleEndian(Record16Data[(index * 2)..]);
}
public void SetRecord16(int index, ushort value)
{
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual<uint>((uint)index, Record16);
EnsureDecrypted();
BinaryPrimitives.WriteUInt16LittleEndian(Record16Data[(index * 2)..], value);
}
public enum Record5Index
{
TimesSaved = 0,
StepsTaken = 1,
UsedBicycle = 2,
TotalBattles = 3,
WildBattles = 4,
TrainerBattles = 5,
Captured = 6,
CapturedFishing = 7,
EggsHatched = 8,
PokemonEvolved = 9,
TimesHealedPokeCenter = 10,
// ???
LinkTrades = 21,
LinkBattles = 22,
LinkBattleWins = 23,
LinkBattleLosses = 24,
// 00 - 0x110: start of u16 records
// 46 - 0x16C: Feeling Checks
// 47 - 0x16E: Musical
// 56 - 0x180: Battle Tests Attempted
// 57 - 0x182: Battle Test High Score
// 60 - 0x188: Customers
// 64 - 0x190: Movie Shoots
FirstU16 = Record32 + 00,
FeelingsChecked = Record32 + 46,
Musical = Record32 + 47,
BattleTestsAttempted = Record32 + 56,
BattleTestHighScore = Record32 + 57,
Customers = Record32 + 60,
MovieShoots = Record32 + 64,
}
}

View file

@ -33,6 +33,14 @@ namespace PKHeX.WinForms
B_Save = new System.Windows.Forms.Button();
TC_Misc = new System.Windows.Forms.TabControl();
TAB_Main = new System.Windows.Forms.TabPage();
L_Record32V = new System.Windows.Forms.Label();
L_Record32 = new System.Windows.Forms.Label();
L_Record16V = new System.Windows.Forms.Label();
L_Record16 = new System.Windows.Forms.Label();
NUD_Record32V = new System.Windows.Forms.NumericUpDown();
NUD_Record32 = new System.Windows.Forms.NumericUpDown();
NUD_Record16V = new System.Windows.Forms.NumericUpDown();
NUD_Record16 = new System.Windows.Forms.NumericUpDown();
GB_KeySystem = new System.Windows.Forms.GroupBox();
B_AllKeys = new System.Windows.Forms.Button();
CLB_KeySystem = new System.Windows.Forms.CheckedListBox();
@ -170,6 +178,10 @@ namespace PKHeX.WinForms
TipExpW = new System.Windows.Forms.ToolTip(components);
TC_Misc.SuspendLayout();
TAB_Main.SuspendLayout();
((System.ComponentModel.ISupportInitialize)NUD_Record32V).BeginInit();
((System.ComponentModel.ISupportInitialize)NUD_Record32).BeginInit();
((System.ComponentModel.ISupportInitialize)NUD_Record16V).BeginInit();
((System.ComponentModel.ISupportInitialize)NUD_Record16).BeginInit();
GB_KeySystem.SuspendLayout();
GB_Roamer.SuspendLayout();
GB_FlyDest.SuspendLayout();
@ -265,6 +277,14 @@ namespace PKHeX.WinForms
//
// TAB_Main
//
TAB_Main.Controls.Add(L_Record32V);
TAB_Main.Controls.Add(L_Record32);
TAB_Main.Controls.Add(L_Record16V);
TAB_Main.Controls.Add(L_Record16);
TAB_Main.Controls.Add(NUD_Record32V);
TAB_Main.Controls.Add(NUD_Record32);
TAB_Main.Controls.Add(NUD_Record16V);
TAB_Main.Controls.Add(NUD_Record16);
TAB_Main.Controls.Add(GB_KeySystem);
TAB_Main.Controls.Add(CHK_LibertyPass);
TAB_Main.Controls.Add(GB_Roamer);
@ -278,6 +298,76 @@ namespace PKHeX.WinForms
TAB_Main.Text = "Main";
TAB_Main.UseVisualStyleBackColor = true;
//
// L_Record32V
//
L_Record32V.Location = new System.Drawing.Point(178, 279);
L_Record32V.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
L_Record32V.Name = "L_Record32V";
L_Record32V.Size = new System.Drawing.Size(82, 23);
L_Record32V.TabIndex = 11;
L_Record32V.Text = "Value:";
L_Record32V.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// L_Record32
//
L_Record32.Location = new System.Drawing.Point(178, 254);
L_Record32.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
L_Record32.Name = "L_Record32";
L_Record32.Size = new System.Drawing.Size(82, 23);
L_Record32.TabIndex = 10;
L_Record32.Text = "Record:";
L_Record32.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// L_Record16V
//
L_Record16V.Location = new System.Drawing.Point(178, 225);
L_Record16V.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
L_Record16V.Name = "L_Record16V";
L_Record16V.Size = new System.Drawing.Size(82, 23);
L_Record16V.TabIndex = 9;
L_Record16V.Text = "Value:";
L_Record16V.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// L_Record16
//
L_Record16.Location = new System.Drawing.Point(178, 200);
L_Record16.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
L_Record16.Name = "L_Record16";
L_Record16.Size = new System.Drawing.Size(82, 23);
L_Record16.TabIndex = 6;
L_Record16.Text = "Record:";
L_Record16.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// NUD_Record32V
//
NUD_Record32V.Location = new System.Drawing.Point(263, 279);
NUD_Record32V.Maximum = new decimal(new int[] { -1, 0, 0, 0 });
NUD_Record32V.Name = "NUD_Record32V";
NUD_Record32V.Size = new System.Drawing.Size(120, 23);
NUD_Record32V.TabIndex = 8;
//
// NUD_Record32
//
NUD_Record32.Location = new System.Drawing.Point(263, 254);
NUD_Record32.Name = "NUD_Record32";
NUD_Record32.Size = new System.Drawing.Size(120, 23);
NUD_Record32.TabIndex = 7;
//
// NUD_Record16V
//
NUD_Record16V.Location = new System.Drawing.Point(263, 225);
NUD_Record16V.Maximum = new decimal(new int[] { 65535, 0, 0, 0 });
NUD_Record16V.Name = "NUD_Record16V";
NUD_Record16V.Size = new System.Drawing.Size(120, 23);
NUD_Record16V.TabIndex = 6;
//
// NUD_Record16
//
NUD_Record16.Location = new System.Drawing.Point(263, 200);
NUD_Record16.Name = "NUD_Record16";
NUD_Record16.Size = new System.Drawing.Size(120, 23);
NUD_Record16.TabIndex = 5;
//
// GB_KeySystem
//
GB_KeySystem.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
@ -1855,6 +1945,10 @@ namespace PKHeX.WinForms
TC_Misc.ResumeLayout(false);
TAB_Main.ResumeLayout(false);
TAB_Main.PerformLayout();
((System.ComponentModel.ISupportInitialize)NUD_Record32V).EndInit();
((System.ComponentModel.ISupportInitialize)NUD_Record32).EndInit();
((System.ComponentModel.ISupportInitialize)NUD_Record16V).EndInit();
((System.ComponentModel.ISupportInitialize)NUD_Record16).EndInit();
GB_KeySystem.ResumeLayout(false);
GB_Roamer.ResumeLayout(false);
GB_FlyDest.ResumeLayout(false);
@ -2053,5 +2147,13 @@ namespace PKHeX.WinForms
private System.Windows.Forms.ComboBox CB_Prop;
private System.Windows.Forms.CheckBox CHK_PropObtained;
private System.Windows.Forms.Button B_UnlockAllProps;
private System.Windows.Forms.Label L_Record32V;
private System.Windows.Forms.Label L_Record32;
private System.Windows.Forms.Label L_Record16V;
private System.Windows.Forms.Label L_Record16;
private System.Windows.Forms.NumericUpDown NUD_Record32V;
private System.Windows.Forms.NumericUpDown NUD_Record32;
private System.Windows.Forms.NumericUpDown NUD_Record16V;
private System.Windows.Forms.NumericUpDown NUD_Record16;
}
}
}

View file

@ -39,6 +39,7 @@ public partial class SAV_Misc5 : Form
ReadEntralink();
ReadMedals();
ReadMusical();
ReadRecord();
}
private void B_Cancel_Click(object sender, EventArgs e) => Close();
@ -49,12 +50,28 @@ public partial class SAV_Misc5 : Form
SaveForest();
SaveSubway();
SaveEntralink();
SaveRecord();
Forest.EnsureDecrypted(false);
Origin.CopyChangesFrom(SAV);
Close();
}
private void ReadRecord()
{
var record = SAV.Records;
NUD_Record16.Maximum = Record5.Record16;
NUD_Record32.Maximum = Record5.Record32;
NUD_Record16V.Value = record.GetRecord16(0);
NUD_Record32V.Value = record.GetRecord32(0);
NUD_Record16V.ValueChanged += (_, _) => record.SetRecord16((int)NUD_Record16.Value, (ushort)NUD_Record16V.Value);
NUD_Record32V.ValueChanged += (_, _) => record.SetRecord32((int)NUD_Record32.Value, (uint)NUD_Record32V.Value);
NUD_Record16.ValueChanged += (_, _) => NUD_Record16V.Value = record.GetRecord16((int)NUD_Record16.Value);
NUD_Record32.ValueChanged += (_, _) => NUD_Record32V.Value = record.GetRecord32((int)NUD_Record32.Value);
}
private void SaveRecord() => SAV.Records.EndAccess();
private static ReadOnlySpan<uint> keyKS =>
[
// 0x34525, 0x11963, // Selected City