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_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;
}
}

View file

@ -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<string> s, int species)
private void AddEntryDescription(List<string> 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);