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
This commit is contained in:
Kurt 2022-04-23 10:42:56 -07:00
parent 9d3f61b708
commit f5358e0e37
3 changed files with 145 additions and 118 deletions

View file

@ -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<byte> Data;
public HallFame6Entity(Span<byte> 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<byte> Nick_Trash => Data.Slice(0x18, 26);
private Span<byte> 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);
}
}

View file

@ -69,6 +69,7 @@
this.B_CopyText = new System.Windows.Forms.Button(); this.B_CopyText = new System.Windows.Forms.Button();
this.B_Delete = new System.Windows.Forms.Button(); this.B_Delete = new System.Windows.Forms.Button();
this.groupBox1 = new System.Windows.Forms.GroupBox(); 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.bpkx)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.NUP_PartyIndex)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.NUP_PartyIndex)).BeginInit();
this.GB_CurrentMoves.SuspendLayout(); this.GB_CurrentMoves.SuspendLayout();
@ -313,6 +314,7 @@
// //
// GB_OT // 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_OT);
this.GB_OT.Controls.Add(this.TB_SID); this.GB_OT.Controls.Add(this.TB_SID);
this.GB_OT.Controls.Add(this.TB_TID); this.GB_OT.Controls.Add(this.TB_TID);
@ -569,6 +571,17 @@
this.groupBox1.TabStop = false; this.groupBox1.TabStop = false;
this.groupBox1.Text = "Entry"; 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 // SAV_HallOfFame
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 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_CopyText;
private System.Windows.Forms.Button B_Delete; private System.Windows.Forms.Button B_Delete;
private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Label Label_OTGender;
} }
} }

View file

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using PKHeX.Core; using PKHeX.Core;
using PKHeX.Drawing.PokeSprite; using PKHeX.Drawing.PokeSprite;
@ -122,54 +121,37 @@ namespace PKHeX.WinForms
int moncount = 0; int moncount = 0;
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
int species = ReadUInt16LittleEndian(data.AsSpan(offset + 0x00)); var entry = new HallFame6Entity(data.AsSpan(offset, HallFame6Entity.SIZE));
if (species == 0) if (entry.Species == 0)
continue; continue;
moncount++; moncount++;
AddEntryDescription(offset, s, species); AddEntryDescription(s, entry);
offset += 0x48; offset += HallFame6Entity.SIZE;
} }
return moncount; return moncount;
} }
private void AddEntryDescription(int offset, List<string> s, int species) private void AddEntryDescription(List<string> s, HallFame6Entity entry)
{ {
int helditem = ReadUInt16LittleEndian(data.AsSpan(offset + 0x02)); string genderstr = gendersymbols[(int)entry.Gender];
int move1 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x04)); string shinystr = entry.IsShiny ? "Yes" : "No";
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";
var str = GameInfo.Strings; var str = GameInfo.Strings;
s.Add($"Name: {nickname}"); s.Add($"Name: {entry.Nickname}");
s.Add($" ({str.Species[species]} - {genderstr})"); s.Add($" ({str.Species[entry.Species]} - {genderstr})");
s.Add($"Level: {level}"); s.Add($"Level: {entry.Level}");
s.Add($"Shiny: {shinystr}"); s.Add($"Shiny: {shinystr}");
s.Add($"Held Item: {str.Item[helditem]}"); s.Add($"Held Item: {str.Item[entry.HeldItem]}");
s.Add($"Move 1: {str.Move[move1]}"); s.Add($"Move 1: {str.Move[entry.Move1]}");
s.Add($"Move 2: {str.Move[move2]}"); s.Add($"Move 2: {str.Move[entry.Move2]}");
s.Add($"Move 3: {str.Move[move3]}"); s.Add($"Move 3: {str.Move[entry.Move3]}");
s.Add($"Move 4: {str.Move[move4]}"); s.Add($"Move 4: {str.Move[entry.Move4]}");
s.Add($"OT: {OTname} ({TID}/{SID})");
string OTGender = gendersymbols[(int)entry.OT_Gender];
s.Add($"OT: {entry.OT_Name} ({OTGender}) ({entry.TID}/{entry.SID})");
s.Add(string.Empty); s.Add(string.Empty);
} }
@ -177,51 +159,36 @@ namespace PKHeX.WinForms
{ {
editing = false; editing = false;
int index = LB_DataEntry.SelectedIndex; 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) if (offset < 0)
return; return;
int species = ReadUInt16LittleEndian(data.AsSpan(offset + 0x00)); var entry = new HallFame6Entity(data.AsSpan(offset, HallFame6Entity.SIZE));
CB_Species.SelectedValue = species; CB_Species.SelectedValue = (int)entry.Species;
int item = ReadUInt16LittleEndian(data.AsSpan(offset + 0x02)); CB_HeldItem.SelectedValue = (int)entry.HeldItem;
CB_HeldItem.SelectedValue = item; CB_Move1.SelectedValue = (int)entry.Move1;
int move1 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x04)); CB_Move2.SelectedValue = (int)entry.Move2;
int move2 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x06)); CB_Move3.SelectedValue = (int)entry.Move3;
int move3 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x08)); CB_Move4.SelectedValue = (int)entry.Move4;
int move4 = ReadUInt16LittleEndian(data.AsSpan(offset + 0x0A));
CB_Move1.SelectedValue = move1;
CB_Move2.SelectedValue = move2;
CB_Move3.SelectedValue = move3;
CB_Move4.SelectedValue = move4;
uint EC = ReadUInt32LittleEndian(data.AsSpan(offset + 0xC)); TB_EC.Text = entry.EncryptionConstant.ToString("X8");
TB_EC.Text = EC.ToString("X8");
TB_TID.Text = ReadUInt16LittleEndian(data.AsSpan(offset + 0x10)).ToString("00000"); TB_TID.Text = entry.TID.ToString("00000");
TB_SID.Text = ReadUInt16LittleEndian(data.AsSpan(offset + 0x12)).ToString("00000"); TB_SID.Text = entry.SID.ToString("00000");
TB_Nickname.Text = StringConverter6.GetString(data.AsSpan(offset + 0x18, 26)); TB_Nickname.Text = entry.Nickname;
TB_OT.Text = StringConverter6.GetString(data.AsSpan(offset + 0x30, 26)); TB_OT.Text = entry.OT_Name;
CHK_Shiny.Checked = entry.IsShiny;
uint slgf = ReadUInt32LittleEndian(data.AsSpan(offset + 0x14)); TB_Level.Text = entry.Level.ToString("000");
uint form = slgf & 0x1F; CHK_Nicknamed.Checked = entry.IsNicknamed;
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;
SetForms(); SetForms();
CB_Form.SelectedIndex = (int)form; CB_Form.SelectedIndex = (int)entry.Form;
SetGenderLabel((int)gender); SetGenderLabel((int)entry.Gender);
Label_OTGender.Text = gendersymbols[(int)entry.OT_Gender];
UpdateNickname(sender, e); 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; editing = true;
} }
@ -234,44 +201,28 @@ namespace PKHeX.WinForms
int index = LB_DataEntry.SelectedIndex; int index = LB_DataEntry.SelectedIndex;
int partymember = Convert.ToInt32(NUP_PartyIndex.Value) - 1; int partymember = Convert.ToInt32(NUP_PartyIndex.Value) - 1;
int offset = (index * 0x1B4) + (partymember * 0x48); int offset = (index * 0x1B4) + (partymember * HallFame6Entity.SIZE);
var span = data.AsSpan(offset); var span = data.AsSpan(offset, HallFame6Entity.SIZE);
WriteUInt16LittleEndian(span, Convert.ToUInt16(CB_Species.SelectedValue)); var entry = new HallFame6Entity(span)
WriteUInt16LittleEndian(span[0x02..], Convert.ToUInt16(CB_HeldItem.SelectedValue)); {
WriteUInt16LittleEndian(span[0x04..], Convert.ToUInt16(CB_Move1.SelectedValue)); Species = Convert.ToUInt16(CB_Species.SelectedValue),
WriteUInt16LittleEndian(span[0x06..], Convert.ToUInt16(CB_Move2.SelectedValue)); HeldItem = Convert.ToUInt16(CB_HeldItem.SelectedValue),
WriteUInt16LittleEndian(span[0x08..], Convert.ToUInt16(CB_Move3.SelectedValue)); Move1 = Convert.ToUInt16(CB_Move1.SelectedValue),
WriteUInt16LittleEndian(span[0x0A..], Convert.ToUInt16(CB_Move4.SelectedValue)); Move2 = Convert.ToUInt16(CB_Move2.SelectedValue),
WriteUInt32LittleEndian(span[0x0C..], Util.GetHexValue(TB_EC.Text)); Move3 = Convert.ToUInt16(CB_Move3.SelectedValue),
WriteUInt16LittleEndian(span[0x10..], Convert.ToUInt16(TB_TID.Text)); Move4 = Convert.ToUInt16(CB_Move4.SelectedValue),
WriteUInt16LittleEndian(span[0x12..], Convert.ToUInt16(TB_SID.Text)); EncryptionConstant = Util.GetHexValue(TB_EC.Text),
TID = Convert.ToUInt16(TB_TID.Text),
uint rawslgf = ReadUInt32LittleEndian(span[0x14..]); SID = Convert.ToUInt16(TB_SID.Text),
uint slgf = 0; Form = (uint)CB_Form.SelectedIndex,
slgf |= (uint)(CB_Form.SelectedIndex & 0x1F); Gender = (uint)PKX.GetGenderFromString(Label_Gender.Text) & 0x3,
slgf |= (uint)((PKX.GetGenderFromString(Label_Gender.Text) & 0x3) << 5); Level = Convert.ToUInt16(TB_Level.Text),
slgf |= (uint)((Convert.ToUInt16(TB_Level.Text) & 0x7F) << 7); IsShiny = CHK_Shiny.Checked,
if (CHK_Shiny.Checked) IsNicknamed = CHK_Nicknamed.Checked,
slgf |= 1 << 14; Nickname = TB_Nickname.Text,
OT_Name = TB_OT.Text,
slgf |= rawslgf & 0x8000; OT_Gender = (uint)PKX.GetGenderFromString(Label_OTGender.Text) & 1,
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..]);
offset = index * 0x1B4; offset = index * 0x1B4;
@ -287,11 +238,7 @@ namespace PKHeX.WinForms
vnd |= rawvnd & 0x80000000; vnd |= rawvnd & 0x80000000;
WriteUInt32LittleEndian(data.AsSpan(offset + 0x1B0), vnd); WriteUInt32LittleEndian(data.AsSpan(offset + 0x1B0), vnd);
var species = WinFormsUtil.GetIndex(CB_Species); bpkx.Image = SpriteUtil.GetSprite(entry.Species, (int)entry.Form, (int)entry.Gender, 0, entry.HeldItem, false, CHK_Shiny.Checked, 6);
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);
DisplayEntry(this, EventArgs.Empty); // refresh text view DisplayEntry(this, EventArgs.Empty); // refresh text view
} }
@ -332,7 +279,7 @@ namespace PKHeX.WinForms
CB_Form.Enabled = CB_Form.Visible = hasForms; CB_Form.Enabled = CB_Form.Visible = hasForms;
CB_Form.InitializeBinding(); 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) private void UpdateSpecies(object sender, EventArgs e)
@ -355,6 +302,14 @@ namespace PKHeX.WinForms
Write_Entry(this, EventArgs.Empty); 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) private void UpdateGender(object sender, EventArgs e)
{ {
// Get Gender Threshold // Get Gender Threshold
@ -364,12 +319,12 @@ namespace PKHeX.WinForms
{ {
var fg = PKX.GetGenderFromString(Label_Gender.Text); var fg = PKX.GetGenderFromString(Label_Gender.Text);
fg = (fg ^ 1) & 1; fg = (fg ^ 1) & 1;
Label_Gender.Text = Main.GenderSymbols[fg]; Label_Gender.Text = gendersymbols[fg];
} }
else else
{ {
var fg = pi.FixedGender; var fg = pi.FixedGender;
Label_Gender.Text = Main.GenderSymbols[fg]; Label_Gender.Text = gendersymbols[fg];
return; return;
} }
@ -423,7 +378,7 @@ namespace PKHeX.WinForms
if (ModifierKeys != Keys.Control) if (ModifierKeys != Keys.Control)
return; 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 nicktrash = data.AsSpan(offset + 0x18, 26);
var text = TB_Nickname.Text; var text = TB_Nickname.Text;
SAV.SetString(nicktrash, text.AsSpan(), 12, StringConverterOption.ClearZero); SAV.SetString(nicktrash, text.AsSpan(), 12, StringConverterOption.ClearZero);