mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-03-21 17:48:28 -05:00
Allow edits for SAV OT trash bytes for gen1-5
This commit is contained in:
parent
662c3db7dc
commit
301a1e7664
|
|
@ -147,6 +147,10 @@ public static class TrashByteRules3
|
|||
// When transferred to Colosseum/XD, the encoding method switches to u16[length], thus discarding the original buffer along with its "trash".
|
||||
// For original encounters from a mainline save file,
|
||||
// - OT Name: the game copies the entire buffer from the save file OT as the PK3's OT. Thus, that must match exactly.
|
||||
// - - Japanese OT names are 5 chars, international is 7 chars. Manually entered strings are FF terminated to max length + 1.
|
||||
// - - Default OT (Japanese) names were padded with FF to len=6, so they always match manually entered names (no trash).
|
||||
// - - Default OT (International) names from the character select screen can have trash bytes due to being un-padded (single FF end of string, saves ROM space).
|
||||
// - - verification of Default OTs todo (if OT dirty, check if is default with expected trash pattern)
|
||||
// - Nickname: the buffer has garbage RAM data leftover in the nickname field, thus it should be "dirty" usually.
|
||||
// - Nicknamed: when nicknamed, the game fills the buffer with FFs then applies the nickname.
|
||||
// For event encounters from GameCube:
|
||||
|
|
@ -175,7 +179,7 @@ public static bool IsResetTrash(PK3 pk3)
|
|||
public static bool IsTerminatedZero(ReadOnlySpan<byte> data)
|
||||
{
|
||||
var first = TrashBytes8.GetTerminatorIndex(data);
|
||||
if (first == -1 || first >= data.Length - 2)
|
||||
if (first == -1 || first >= data.Length - 1)
|
||||
return true;
|
||||
return !data[(first+1)..].ContainsAnyExcept<byte>(0);
|
||||
}
|
||||
|
|
@ -183,7 +187,7 @@ public static bool IsTerminatedZero(ReadOnlySpan<byte> data)
|
|||
public static bool IsTerminatedFF(ReadOnlySpan<byte> data)
|
||||
{
|
||||
var first = TrashBytes8.GetTerminatorIndex(data);
|
||||
if (first == -1 || first >= data.Length - 2)
|
||||
if (first == -1 || first >= data.Length - 1)
|
||||
return true;
|
||||
return !data[(first + 1)..].ContainsAnyExcept<byte>(0xFF);
|
||||
}
|
||||
|
|
@ -194,17 +198,20 @@ public static bool IsTerminatedFFZero(ReadOnlySpan<byte> data, int preFill = 0)
|
|||
return IsTerminatedZero(data);
|
||||
|
||||
var first = TrashBytes8.GetTerminatorIndex(data);
|
||||
if (first == -1 || first == data.Length - 2)
|
||||
if (first == -1 || first >= data.Length - 1)
|
||||
return true;
|
||||
|
||||
first++;
|
||||
if (first < preFill)
|
||||
{
|
||||
var inner = data[first..preFill];
|
||||
if (inner.ContainsAnyExcept(Terminator))
|
||||
return false;
|
||||
first = preFill;
|
||||
if (first >= data.Length - 2)
|
||||
first++;
|
||||
if (first >= data.Length - 1)
|
||||
return true;
|
||||
}
|
||||
return !data[(first + 1)..].ContainsAnyExcept<byte>(0);
|
||||
return !data[first..].ContainsAnyExcept<byte>(0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -256,7 +256,8 @@ public override int PlayedSeconds
|
|||
}
|
||||
|
||||
// Trainer Info (offset 0x78, length 0xB18, end @ 0xB90)
|
||||
public override string OT { get => GetString(Data.Slice(0x78, 20)); set { SetString(Data.Slice(0x78, 20), value, 10, StringConverterOption.ClearZero); OT2 = value; } }
|
||||
public Span<byte> OriginalTrainerTrash => Data.Slice(0x78, 20);
|
||||
public override string OT { get => GetString(OriginalTrainerTrash); set { SetString(OriginalTrainerTrash, value, 10, StringConverterOption.ClearZero); OT2 = value; } }
|
||||
public string OT2 { get => GetString(Data.Slice(0x8C, 20)); set => SetString(Data.Slice(0x8C, 20), value, 10, StringConverterOption.ClearZero); }
|
||||
|
||||
public override uint ID32 { get => ReadUInt32BigEndian(Data[0xA4..]); set => WriteUInt32BigEndian(Data[0xA4..], value); }
|
||||
|
|
|
|||
|
|
@ -234,7 +234,8 @@ public override int PlayedSeconds
|
|||
|
||||
// Trainer Info
|
||||
public override GameVersion Version { get => GameVersion.XD; set { } }
|
||||
public override string OT { get => GetString(Data.Slice(Trainer1 + 0x00, 20)); set => SetString(Data.Slice(Trainer1 + 0x00, 20), value, 10, StringConverterOption.ClearZero); }
|
||||
public Span<byte> OriginalTrainerTrash => Data.Slice(Trainer1 + 0x00, 20);
|
||||
public override string OT { get => GetString(OriginalTrainerTrash); set => SetString(OriginalTrainerTrash, value, 10, StringConverterOption.ClearZero); }
|
||||
public override uint ID32 { get => ReadUInt32BigEndian(Data[(Trainer1 + 0x2C)..]); set => WriteUInt32BigEndian(Data[(Trainer1 + 0x2C)..], value); }
|
||||
public override ushort SID16 { get => ReadUInt16BigEndian(Data[(Trainer1 + 0x2C)..]); set => WriteUInt16BigEndian(Data[(Trainer1 + 0x2C)..], value); }
|
||||
public override ushort TID16 { get => ReadUInt16BigEndian(Data[(Trainer1 + 0x2E)..]); set => WriteUInt16BigEndian(Data[(Trainer1 + 0x2E)..], value); }
|
||||
|
|
|
|||
|
|
@ -255,10 +255,13 @@ public override int PartyCount
|
|||
public sealed override int GetPartyOffset(int slot) => Party + (SIZE_PARTY * slot);
|
||||
|
||||
#region Trainer Info
|
||||
|
||||
public Span<byte> OriginalTrainerTrash => General.Slice(Trainer1, 16);
|
||||
|
||||
public override string OT
|
||||
{
|
||||
get => GetString(General.Slice(Trainer1, 16));
|
||||
set => SetString(General.Slice(Trainer1, 16), value, MaxStringLengthTrainer, StringConverterOption.ClearZero);
|
||||
get => GetString(OriginalTrainerTrash);
|
||||
set => SetString(OriginalTrainerTrash, value, MaxStringLengthTrainer, StringConverterOption.ClearZero);
|
||||
}
|
||||
|
||||
public override uint ID32
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace PKHeX.Core;
|
|||
/// </summary>
|
||||
public abstract class PlayerData5(SAV5 sav, Memory<byte> raw) : SaveBlock<SAV5>(sav, raw)
|
||||
{
|
||||
private Span<byte> OriginalTrainerTrash => Data.Slice(4, 0x10);
|
||||
public Span<byte> OriginalTrainerTrash => Data.Slice(4, 0x10);
|
||||
|
||||
public string OT
|
||||
{
|
||||
|
|
|
|||
|
|
@ -73,6 +73,8 @@ public SAV_SimpleTrainer(SaveFile sav)
|
|||
L_PikaBeach.Visible = MT_PikaBeach.Visible = false;
|
||||
CB_SoundType.Visible = LBL_SoundType.Visible = false;
|
||||
}
|
||||
|
||||
TB_OTName.Click += (_, _) => ClickOT(sav1.OriginalTrainerTrash, TB_OTName);
|
||||
}
|
||||
|
||||
if (SAV is SAV2 sav2)
|
||||
|
|
@ -94,6 +96,8 @@ public SAV_SimpleTrainer(SaveFile sav)
|
|||
CB_TextSpeed.SelectedIndex = sav2.TextSpeed;
|
||||
badgeval = sav2.Badges;
|
||||
cba = [CHK_1, CHK_2, CHK_3, CHK_4, CHK_6, CHK_5, CHK_7, CHK_8, CHK_H1, CHK_H2, CHK_H3, CHK_H4, CHK_H5, CHK_H6, CHK_H7, CHK_H8];
|
||||
|
||||
TB_OTName.Click += (_, _) => ClickOT(sav2.OriginalTrainerTrash, TB_OTName);
|
||||
}
|
||||
|
||||
if (SAV is SAV3 sav3)
|
||||
|
|
@ -114,6 +118,8 @@ public SAV_SimpleTrainer(SaveFile sav)
|
|||
CB_BattleStyle.SelectedIndex = sav3.OptionBattleStyle ? 1 : 0;
|
||||
CB_SoundType.SelectedIndex = sav3.OptionSoundStereo ? 0 : 1;
|
||||
CHK_BattleEffects.Checked = sav3.OptionBattleScene;
|
||||
|
||||
TB_OTName.Click += (_, _) => ClickOT(sav3.OriginalTrainerTrash, TB_OTName);
|
||||
}
|
||||
if (SAV is SAV3Colosseum or SAV3XD)
|
||||
{
|
||||
|
|
@ -123,6 +129,11 @@ public SAV_SimpleTrainer(SaveFile sav)
|
|||
CAL_AdventureStartDate.Visible = CAL_HoFDate.Visible = false;
|
||||
CAL_AdventureStartTime.Visible = CAL_HoFTime.Visible = false;
|
||||
GB_Adventure.Visible = false;
|
||||
|
||||
if (SAV is SAV3Colosseum colo)
|
||||
TB_OTName.Click += (_, _) => ClickOT(colo.OriginalTrainerTrash, TB_OTName);
|
||||
else if (SAV is SAV3XD xd)
|
||||
TB_OTName.Click += (_, _) => ClickOT(xd.OriginalTrainerTrash, TB_OTName);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -143,6 +154,8 @@ public SAV_SimpleTrainer(SaveFile sav)
|
|||
Main.SetCountrySubRegion(CB_Country, "gen4_countries");
|
||||
CB_Country.SelectedValue = sav4.Country;
|
||||
CB_Region.SelectedValue = sav4.Region;
|
||||
|
||||
TB_OTName.Click += (_, _) => ClickOT(sav4.OriginalTrainerTrash, TB_OTName);
|
||||
}
|
||||
else if (SAV is SAV5 s)
|
||||
{
|
||||
|
|
@ -166,6 +179,8 @@ public SAV_SimpleTrainer(SaveFile sav)
|
|||
Main.SetCountrySubRegion(CB_Country, "gen5_countries");
|
||||
CB_Country.SelectedValue = s.Country;
|
||||
CB_Region.SelectedValue = s.Region;
|
||||
|
||||
TB_OTName.Click += (_, _) => ClickOT(s.PlayerData.OriginalTrainerTrash, TB_OTName);
|
||||
}
|
||||
|
||||
for (int i = 0; i < cba.Length; i++)
|
||||
|
|
@ -189,6 +204,17 @@ public SAV_SimpleTrainer(SaveFile sav)
|
|||
private readonly bool Loading;
|
||||
private bool MapUpdated;
|
||||
|
||||
private void ClickOT(Span<byte> text, TextBox tb)
|
||||
{
|
||||
// Special Character Form
|
||||
if (ModifierKeys != Keys.Control)
|
||||
return;
|
||||
|
||||
var d = new TrashEditor(tb, text, SAV, SAV.Generation, SAV.Context);
|
||||
d.ShowDialog();
|
||||
tb.Text = d.FinalString;
|
||||
}
|
||||
|
||||
private void ChangeFFFF(object sender, EventArgs e)
|
||||
{
|
||||
MaskedTextBox box = (MaskedTextBox)sender;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user