Fix HGSS box saving (empty->not)

HGSS tracks each box if it is changed, to skip writing it on in-game save. PKHeX (and other editors) weren't updating these bitflags, so when empty boxes had Pokémon added and saved once in-game, the save file would corrupt as it was copying the checksum but not the box's data.

Saving twice in-game will cause the checksums to get updated even without the flags being set. I don't think we understand it fully, but this hacky workaround of noting all boxes as "changed" will cause the first-save to properly copy all contents of the boxes to the new primary save section. Rather than tracking which boxes we are reading/writing slots for, or fixing/comparing against a backup -- we need to account for API usage where people might write directly into the save (pcdata.bin/boxdata.bin too). Best to play it safe and let the game fix it.

Most wouldn't have noticed this issue as the box they're modifying is usually their most-recently-changed in-game box.

Closes #4368
This commit is contained in:
Kurt 2024-11-11 22:25:34 -06:00
parent 6153d6c851
commit fced599119

View file

@ -53,6 +53,14 @@ public sealed class SAV4HGSS : SAV4, IBoxDetailName, IBoxDetailWallpaper
GetSAVOffsets();
}
protected override byte[] GetFinalData()
{
// Make sure all boxes are copied when saved only once in-game.
// This results in the game "saving a lot of data", but ensures the boxdata struct does not corrupt in-game on single save.
FlagsBoxContentChanged = FlagsBoxContentChangedAll;
return base.GetFinalData();
}
private const int OffsetMystery = 0x9D3C; // Flags and Gifts
protected override int EventWork => 0xDE4;
protected override int EventFlag => 0x10C4;
@ -125,12 +133,17 @@ public sealed class SAV4HGSS : SAV4, IBoxDetailName, IBoxDetailWallpaper
set => Storage[BOX_FLAGS] = value[0];
}
public int Counter
/// <summary>
/// The box structure stores bitflags to indicate which boxes have changed; used when saving to skip unchanged boxes.
/// </summary>
public int FlagsBoxContentChanged
{
get => ReadInt32LittleEndian(Storage[(BOX_END + 4)..]);
set => WriteInt32LittleEndian(Storage[(BOX_END + 4)..], value);
}
private const int FlagsBoxContentChangedAll = 0x3_FFFF; // 18 boxes.
private Span<byte> GetBoxNameSpan(int box) => Storage.Slice(GetBoxNameOffset(box), BOX_NAME_LEN);
public string GetBoxName(int box) => GetString(GetBoxNameSpan(box));