From f5358e0e374f9cf903fe8dee8fd274dd72a23a83 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 23 Apr 2022 10:42:56 -0700 Subject: [PATCH] Update HoF6 entity editing Reusable entity struct, no more overlapping fields Adds OT Gender, fixes some flags. Thanks L1nk2futr3 ! https://projectpokemon.org/home/forums/topic/60977-hall-of-fame-tid-resetting-gen-6/?do=findComment&comment=275575 --- .../Substructures/Gen6/HallFame6Entity.cs | 58 ++++++ .../Gen6/SAV_HallOfFame.Designer.cs | 14 ++ .../Save Editors/Gen6/SAV_HallOfFame.cs | 191 +++++++----------- 3 files changed, 145 insertions(+), 118 deletions(-) create mode 100644 PKHeX.Core/Saves/Substructures/Gen6/HallFame6Entity.cs diff --git a/PKHeX.Core/Saves/Substructures/Gen6/HallFame6Entity.cs b/PKHeX.Core/Saves/Substructures/Gen6/HallFame6Entity.cs new file mode 100644 index 000000000..088636657 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen6/HallFame6Entity.cs @@ -0,0 +1,58 @@ +using System; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core; + +public readonly ref struct HallFame6Entity +{ + public const int SIZE = 0x48; + private readonly Span Data; + public HallFame6Entity(Span data) => Data = data; + + public ushort Species { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, value); } + public ushort HeldItem { get => ReadUInt16LittleEndian(Data[0x02..]); set => WriteUInt16LittleEndian(Data[0x02..], value); } + public ushort Move1 { get => ReadUInt16LittleEndian(Data[0x04..]); set => WriteUInt16LittleEndian(Data[0x04..], value); } + public ushort Move2 { get => ReadUInt16LittleEndian(Data[0x06..]); set => WriteUInt16LittleEndian(Data[0x06..], value); } + public ushort Move3 { get => ReadUInt16LittleEndian(Data[0x08..]); set => WriteUInt16LittleEndian(Data[0x08..], value); } + public ushort Move4 { get => ReadUInt16LittleEndian(Data[0x0A..]); set => WriteUInt16LittleEndian(Data[0x0A..], value); } + public uint EncryptionConstant { get => ReadUInt32LittleEndian(Data[0x0C..]); set => WriteUInt32LittleEndian(Data[0x0C..], value); } + + public ushort TID { get => ReadUInt16LittleEndian(Data[0x10..]); set => WriteUInt16LittleEndian(Data[0x10..], value); } + public ushort SID { get => ReadUInt16LittleEndian(Data[0x12..]); set => WriteUInt16LittleEndian(Data[0x12..], value); } + private uint Pack { get => ReadUInt32LittleEndian(Data[0x14..]); set => WriteUInt32LittleEndian(Data[0x14..], value); } + + public uint Form { get => Pack & 0x1Fu; set => Pack = (Pack & ~0x1Fu) | (value & 0x1F); } + public uint Gender { get => (Pack >> 05) & 0x03u; set => Pack = (Pack & ~(0x03u << 05)) | ((value & 0x03) << 05); } + public uint Level { get => (Pack >> 07) & 0x7Fu; set => Pack = (Pack & ~(0x7Fu << 07)) | ((value & 0x7F) << 07); } + private uint Shiny { get => (Pack >> 14) & 0x01u; set => Pack = (Pack & ~(0x01u << 14)) | ((value & 0x01) << 14); } + private uint Nick { get => (Pack >> 15) & 0x01u; set => Pack = (Pack & ~(0x01u << 15)) | ((value & 0x01) << 15); } + public uint OT_Gender { get => (Pack >> 16) & 0x01u; set => Pack = (Pack & ~(0x01u << 16)) | ((value & 0x01) << 16); } + // remaining bits unused + + public bool IsNicknamed { get => Nick == 1; set => Nick = value ? 1u : 0u; } + public bool IsShiny { get => Shiny == 1; set => Shiny = value ? 1u : 0u; } + + private Span Nick_Trash => Data.Slice(0x18, 26); + private Span OT_Trash => Data.Slice(0x30, 26); + + // Don't mimic in-game behavior of not clearing strings. First entry should always have clean trash. + private const StringConverterOption Option = StringConverterOption.ClearZero; + + public void ClearTrash() + { + Nick_Trash.Clear(); + OT_Trash.Clear(); + } + + public string Nickname + { + get => StringConverter6.GetString(Nick_Trash); + set => StringConverter6.SetString(Nick_Trash, value.AsSpan(), 12, Option); + } + + public string OT_Name + { + get => StringConverter6.GetString(OT_Trash); + set => StringConverter6.SetString(OT_Trash, value.AsSpan(), 12, Option); + } +} diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.Designer.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.Designer.cs index 08852aaf2..17f04c7bd 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.Designer.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.Designer.cs @@ -69,6 +69,7 @@ this.B_CopyText = new System.Windows.Forms.Button(); this.B_Delete = new System.Windows.Forms.Button(); this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.Label_OTGender = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this.bpkx)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.NUP_PartyIndex)).BeginInit(); this.GB_CurrentMoves.SuspendLayout(); @@ -313,6 +314,7 @@ // // GB_OT // + this.GB_OT.Controls.Add(this.Label_OTGender); this.GB_OT.Controls.Add(this.TB_OT); this.GB_OT.Controls.Add(this.TB_SID); this.GB_OT.Controls.Add(this.TB_TID); @@ -569,6 +571,17 @@ this.groupBox1.TabStop = false; this.groupBox1.Text = "Entry"; // + // Label_OTGender + // + this.Label_OTGender.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Label_OTGender.Location = new System.Drawing.Point(136, 48); + this.Label_OTGender.Name = "Label_OTGender"; + this.Label_OTGender.Size = new System.Drawing.Size(18, 13); + this.Label_OTGender.TabIndex = 79; + this.Label_OTGender.Text = "-"; + this.Label_OTGender.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.Label_OTGender.Click += new System.EventHandler(this.UpdateOTGender); + // // SAV_HallOfFame // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -642,5 +655,6 @@ private System.Windows.Forms.Button B_CopyText; private System.Windows.Forms.Button B_Delete; private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Label Label_OTGender; } } \ No newline at end of file diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.cs index 886fb4e4d..cda833495 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_HallOfFame.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Drawing; -using System.Text; using System.Windows.Forms; using PKHeX.Core; using PKHeX.Drawing.PokeSprite; @@ -122,54 +121,37 @@ namespace PKHeX.WinForms int moncount = 0; for (int i = 0; i < 6; i++) { - int species = ReadUInt16LittleEndian(data.AsSpan(offset + 0x00)); - if (species == 0) + var entry = new HallFame6Entity(data.AsSpan(offset, HallFame6Entity.SIZE)); + if (entry.Species == 0) continue; moncount++; - AddEntryDescription(offset, s, species); + AddEntryDescription(s, entry); - offset += 0x48; + offset += HallFame6Entity.SIZE; } return moncount; } - private void AddEntryDescription(int offset, List s, int species) + private void AddEntryDescription(List s, HallFame6Entity entry) { - int helditem = ReadUInt16LittleEndian(data.AsSpan(offset + 0x02)); - int move1 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x04)); - int move2 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x06)); - int move3 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x08)); - int move4 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x0A)); - - int TID = ReadUInt16LittleEndian(data.AsSpan(offset + 0x10)); - int SID = ReadUInt16LittleEndian(data.AsSpan(offset + 0x12)); - - uint slgf = ReadUInt32LittleEndian(data.AsSpan(offset + 0x14)); - // uint form = slgf & 0x1F; - uint gender = slgf >> 5 & 3; // 0 M; 1 F; 2 G - uint level = slgf >> 7 & 0x7F; - uint shiny = slgf >> 14 & 0x1; - // uint unkn = slgf >> 15; - - string nickname = StringConverter6.GetString(data.AsSpan(offset + 0x18, 26)); - string OTname = StringConverter6.GetString(data.AsSpan(offset + 0x30, 26)); - - string genderstr = gendersymbols[(int)gender]; - string shinystr = shiny == 1 ? "Yes" : "No"; + string genderstr = gendersymbols[(int)entry.Gender]; + string shinystr = entry.IsShiny ? "Yes" : "No"; var str = GameInfo.Strings; - s.Add($"Name: {nickname}"); - s.Add($" ({str.Species[species]} - {genderstr})"); - s.Add($"Level: {level}"); + s.Add($"Name: {entry.Nickname}"); + s.Add($" ({str.Species[entry.Species]} - {genderstr})"); + s.Add($"Level: {entry.Level}"); s.Add($"Shiny: {shinystr}"); - s.Add($"Held Item: {str.Item[helditem]}"); - s.Add($"Move 1: {str.Move[move1]}"); - s.Add($"Move 2: {str.Move[move2]}"); - s.Add($"Move 3: {str.Move[move3]}"); - s.Add($"Move 4: {str.Move[move4]}"); - s.Add($"OT: {OTname} ({TID}/{SID})"); + s.Add($"Held Item: {str.Item[entry.HeldItem]}"); + s.Add($"Move 1: {str.Move[entry.Move1]}"); + s.Add($"Move 2: {str.Move[entry.Move2]}"); + s.Add($"Move 3: {str.Move[entry.Move3]}"); + s.Add($"Move 4: {str.Move[entry.Move4]}"); + + string OTGender = gendersymbols[(int)entry.OT_Gender]; + s.Add($"OT: {entry.OT_Name} ({OTGender}) ({entry.TID}/{entry.SID})"); s.Add(string.Empty); } @@ -177,51 +159,36 @@ namespace PKHeX.WinForms { editing = false; int index = LB_DataEntry.SelectedIndex; - int offset = (index * 0x1B4) + ((Convert.ToInt32(NUP_PartyIndex.Value)-1) * 0x48); + int offset = (index * 0x1B4) + ((Convert.ToInt32(NUP_PartyIndex.Value)-1) * HallFame6Entity.SIZE); if (offset < 0) return; - int species = ReadUInt16LittleEndian(data.AsSpan(offset + 0x00)); - CB_Species.SelectedValue = species; - int item = ReadUInt16LittleEndian(data.AsSpan(offset + 0x02)); - CB_HeldItem.SelectedValue = item; - int move1 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x04)); - int move2 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x06)); - int move3 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x08)); - int move4 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x0A)); - CB_Move1.SelectedValue = move1; - CB_Move2.SelectedValue = move2; - CB_Move3.SelectedValue = move3; - CB_Move4.SelectedValue = move4; + var entry = new HallFame6Entity(data.AsSpan(offset, HallFame6Entity.SIZE)); + CB_Species.SelectedValue = (int)entry.Species; + CB_HeldItem.SelectedValue = (int)entry.HeldItem; + CB_Move1.SelectedValue = (int)entry.Move1; + CB_Move2.SelectedValue = (int)entry.Move2; + CB_Move3.SelectedValue = (int)entry.Move3; + CB_Move4.SelectedValue = (int)entry.Move4; - uint EC = ReadUInt32LittleEndian(data.AsSpan(offset + 0xC)); - TB_EC.Text = EC.ToString("X8"); + TB_EC.Text = entry.EncryptionConstant.ToString("X8"); - TB_TID.Text = ReadUInt16LittleEndian(data.AsSpan(offset + 0x10)).ToString("00000"); - TB_SID.Text = ReadUInt16LittleEndian(data.AsSpan(offset + 0x12)).ToString("00000"); + TB_TID.Text = entry.TID.ToString("00000"); + TB_SID.Text = entry.SID.ToString("00000"); - TB_Nickname.Text = StringConverter6.GetString(data.AsSpan(offset + 0x18, 26)); - TB_OT.Text = StringConverter6.GetString(data.AsSpan(offset + 0x30, 26)); - - uint slgf = ReadUInt32LittleEndian(data.AsSpan(offset + 0x14)); - uint form = slgf & 0x1F; - uint gender = slgf >> 5 & 3; // 0 M; 1 F; 2 G - uint level = slgf >> 7 & 0x7F; - uint shiny = slgf >> 14 & 0x1; - uint nick = ReadUInt16LittleEndian(data.AsSpan(offset + 0x16)); - - CHK_Shiny.Checked = shiny == 1; - - TB_Level.Text = level.ToString("000"); - - CHK_Nicknamed.Checked = nick == 1; + TB_Nickname.Text = entry.Nickname; + TB_OT.Text = entry.OT_Name; + CHK_Shiny.Checked = entry.IsShiny; + TB_Level.Text = entry.Level.ToString("000"); + CHK_Nicknamed.Checked = entry.IsNicknamed; SetForms(); - CB_Form.SelectedIndex = (int)form; - SetGenderLabel((int)gender); + CB_Form.SelectedIndex = (int)entry.Form; + SetGenderLabel((int)entry.Gender); + Label_OTGender.Text = gendersymbols[(int)entry.OT_Gender]; UpdateNickname(sender, e); - bpkx.Image = SpriteUtil.GetSprite(species, (int)form, (int)gender, 0, item, false, shiny == 1, 6); + bpkx.Image = SpriteUtil.GetSprite(entry.Species, (int)entry.Form, (int)entry.Gender, 0, entry.HeldItem, false, entry.IsShiny, 6); editing = true; } @@ -234,44 +201,28 @@ namespace PKHeX.WinForms int index = LB_DataEntry.SelectedIndex; int partymember = Convert.ToInt32(NUP_PartyIndex.Value) - 1; - int offset = (index * 0x1B4) + (partymember * 0x48); - var span = data.AsSpan(offset); - WriteUInt16LittleEndian(span, Convert.ToUInt16(CB_Species.SelectedValue)); - WriteUInt16LittleEndian(span[0x02..], Convert.ToUInt16(CB_HeldItem.SelectedValue)); - WriteUInt16LittleEndian(span[0x04..], Convert.ToUInt16(CB_Move1.SelectedValue)); - WriteUInt16LittleEndian(span[0x06..], Convert.ToUInt16(CB_Move2.SelectedValue)); - WriteUInt16LittleEndian(span[0x08..], Convert.ToUInt16(CB_Move3.SelectedValue)); - WriteUInt16LittleEndian(span[0x0A..], Convert.ToUInt16(CB_Move4.SelectedValue)); - WriteUInt32LittleEndian(span[0x0C..], Util.GetHexValue(TB_EC.Text)); - WriteUInt16LittleEndian(span[0x10..], Convert.ToUInt16(TB_TID.Text)); - WriteUInt16LittleEndian(span[0x12..], Convert.ToUInt16(TB_SID.Text)); - - uint rawslgf = ReadUInt32LittleEndian(span[0x14..]); - uint slgf = 0; - slgf |= (uint)(CB_Form.SelectedIndex & 0x1F); - slgf |= (uint)((PKX.GetGenderFromString(Label_Gender.Text) & 0x3) << 5); - slgf |= (uint)((Convert.ToUInt16(TB_Level.Text) & 0x7F) << 7); - if (CHK_Shiny.Checked) - slgf |= 1 << 14; - - slgf |= rawslgf & 0x8000; - WriteUInt16LittleEndian(span[14..], (ushort)slgf); - - uint nick = 0; - if (CHK_Nicknamed.Checked) - nick = 1; - WriteUInt16LittleEndian(span[16..], (ushort)nick); - - //Mimic in-game behavior of not clearing strings. It's awful, but accuracy > good. - string pk = TB_Nickname.Text; - if (pk.Length != 12) - pk = pk.PadRight(pk.Length + 1, '\0'); - string ot = TB_OT.Text; - if (ot.Length != 12) - ot = ot.PadRight(pk.Length + 1, '\0'); - - Encoding.Unicode.GetBytes(pk).CopyTo(span[0x18..]); - Encoding.Unicode.GetBytes(ot).CopyTo(span[0x30..]); + int offset = (index * 0x1B4) + (partymember * HallFame6Entity.SIZE); + var span = data.AsSpan(offset, HallFame6Entity.SIZE); + var entry = new HallFame6Entity(span) + { + Species = Convert.ToUInt16(CB_Species.SelectedValue), + HeldItem = Convert.ToUInt16(CB_HeldItem.SelectedValue), + Move1 = Convert.ToUInt16(CB_Move1.SelectedValue), + Move2 = Convert.ToUInt16(CB_Move2.SelectedValue), + Move3 = Convert.ToUInt16(CB_Move3.SelectedValue), + Move4 = Convert.ToUInt16(CB_Move4.SelectedValue), + EncryptionConstant = Util.GetHexValue(TB_EC.Text), + TID = Convert.ToUInt16(TB_TID.Text), + SID = Convert.ToUInt16(TB_SID.Text), + Form = (uint)CB_Form.SelectedIndex, + Gender = (uint)PKX.GetGenderFromString(Label_Gender.Text) & 0x3, + Level = Convert.ToUInt16(TB_Level.Text), + IsShiny = CHK_Shiny.Checked, + IsNicknamed = CHK_Nicknamed.Checked, + Nickname = TB_Nickname.Text, + OT_Name = TB_OT.Text, + OT_Gender = (uint)PKX.GetGenderFromString(Label_OTGender.Text) & 1, + }; offset = index * 0x1B4; @@ -287,11 +238,7 @@ namespace PKHeX.WinForms vnd |= rawvnd & 0x80000000; WriteUInt32LittleEndian(data.AsSpan(offset + 0x1B0), vnd); - var species = WinFormsUtil.GetIndex(CB_Species); - var form = CB_Form.SelectedIndex & 0x1F; - var gender = PKX.GetGenderFromString(Label_Gender.Text); - var item = WinFormsUtil.GetIndex(CB_HeldItem); - bpkx.Image = SpriteUtil.GetSprite(species, form, gender, 0, item, false, CHK_Shiny.Checked, 6); + bpkx.Image = SpriteUtil.GetSprite(entry.Species, (int)entry.Form, (int)entry.Gender, 0, entry.HeldItem, false, CHK_Shiny.Checked, 6); DisplayEntry(this, EventArgs.Empty); // refresh text view } @@ -332,7 +279,7 @@ namespace PKHeX.WinForms CB_Form.Enabled = CB_Form.Visible = hasForms; CB_Form.InitializeBinding(); - CB_Form.DataSource = FormConverter.GetFormList(species, GameInfo.Strings.types, GameInfo.Strings.forms, Main.GenderSymbols, SAV.Generation); + CB_Form.DataSource = FormConverter.GetFormList(species, GameInfo.Strings.types, GameInfo.Strings.forms, gendersymbols, SAV.Generation); } private void UpdateSpecies(object sender, EventArgs e) @@ -355,6 +302,14 @@ namespace PKHeX.WinForms Write_Entry(this, EventArgs.Empty); } + private void UpdateOTGender(object sender, EventArgs e) + { + var g = PKX.GetGenderFromString(Label_OTGender.Text); + Label_OTGender.Text = gendersymbols[g ^ 1]; + + Write_Entry(this, EventArgs.Empty); + } + private void UpdateGender(object sender, EventArgs e) { // Get Gender Threshold @@ -364,12 +319,12 @@ namespace PKHeX.WinForms { var fg = PKX.GetGenderFromString(Label_Gender.Text); fg = (fg ^ 1) & 1; - Label_Gender.Text = Main.GenderSymbols[fg]; + Label_Gender.Text = gendersymbols[fg]; } else { var fg = pi.FixedGender; - Label_Gender.Text = Main.GenderSymbols[fg]; + Label_Gender.Text = gendersymbols[fg]; return; } @@ -423,7 +378,7 @@ namespace PKHeX.WinForms if (ModifierKeys != Keys.Control) return; - int offset = (LB_DataEntry.SelectedIndex * 0x1B4) + ((Convert.ToInt32(NUP_PartyIndex.Value) - 1) * 0x48); + int offset = (LB_DataEntry.SelectedIndex * 0x1B4) + ((Convert.ToInt32(NUP_PartyIndex.Value) - 1) * HallFame6Entity.SIZE); var nicktrash = data.AsSpan(offset + 0x18, 26); var text = TB_Nickname.Text; SAV.SetString(nicktrash, text.AsSpan(), 12, StringConverterOption.ClearZero);