Skip to main content

Overview

The SaveFile class is the abstract base class for all Pokémon save file formats. It provides a unified interface for accessing and manipulating save data across all generations. Namespace: PKHeX.Core Inheritance: ObjectSaveFile Implements: ITrainerInfo, IGameValueLimit, IStringConverter, ITrainerID32

Constructors

SaveFile(Memory<byte> data, bool exportable = true)
constructor
Creates a SaveFile instance from existing byte data.Parameters:
  • data (Memory<byte>): Raw save file data
  • exportable (bool): Whether the save can be exported (default: true)
SaveFile(int size = 0)
constructor
Creates a SaveFile instance with a new byte array.Parameters:
  • size (int): Size of the byte array (default: 0 for empty array)

Core Properties

Data Storage

Buffer
Memory<byte>
The raw save file data buffer.
Data
Span<byte>
Gets the raw byte data span for the save file.
State
SaveFileState
Gets the current state of the save file (edited status, exportable status).
Metadata
SaveFileMetadata
Gets the metadata associated with this save file.

File Information

Extension
string
Gets the file extension for this save file format.
Generation
byte
Gets the generation this save file belongs to.
Context
EntityContext
Gets the entity context for this save file.
Version
GameVersion
Gets or sets the game version.

Trainer Information

OT
string
Gets or sets the Original Trainer name.
Gender
byte
Gets or sets the trainer’s gender (0 = Male, 1 = Female).
Language
int
Gets or sets the language ID of the save file.
TID16
ushort
Gets or sets the 16-bit Trainer ID.
SID16
ushort
Gets or sets the 16-bit Secret ID.
ID32
uint
Gets or sets the combined 32-bit Trainer ID.
DisplayTID
uint
Gets or sets the Trainer ID as displayed in-game.
DisplaySID
uint
Gets or sets the Secret ID as displayed in-game.

Game Progress

PlayedHours
int
Gets or sets the hours played.
PlayedMinutes
int
Gets or sets the minutes played.
PlayedSeconds
int
Gets or sets the seconds played.
PlayTimeString
string
Gets the formatted play time string (HH:MM:SS).
SecondsToStart
uint
Gets or sets the seconds until start time.
SecondsToFame
uint
Gets or sets the seconds to Hall of Fame entry.
Money
uint
Gets or sets the trainer’s money.
MaxMoney
int
Gets the maximum money value (default: 9,999,999).
MaxCoins
int
Gets the maximum coins value (default: 9,999).

Storage Properties

BoxCount
int
Gets the total number of boxes in the PC.
BoxSlotCount
int
Gets the number of slots per box (typically 30).
SlotCount
int
Gets the total number of storage slots (BoxCount × BoxSlotCount).
CurrentBox
int
Gets or sets the currently selected box.
BoxesUnlocked
int
Gets or sets the number of unlocked boxes.
HasBox
bool
Gets whether this save file has box storage.

Party Properties

PartyCount
int
Gets or sets the number of Pokémon in the party.
HasParty
bool
Gets whether this save file has a party.
PartyData
IList<PKM>
Gets or sets the party Pokémon.

Format Limits

Personal
IPersonalTable
Gets the personal data table for this save file.
MaxStringLengthTrainer
int
Gets the maximum trainer name length.
MaxStringLengthNickname
int
Gets the maximum nickname length.
MaxMoveID
ushort
Gets the maximum move ID.
MaxSpeciesID
ushort
Gets the maximum species ID.
MaxAbilityID
int
Gets the maximum ability ID.
MaxItemID
int
Gets the maximum item ID.
MaxBallID
int
Gets the maximum ball ID.
MaxGameID
GameVersion
Gets the maximum game version ID.
MaxEV
int
Gets the maximum EV value per stat.
MaxIV
int
Gets the maximum IV value per stat (typically 31).

Pokédex

HasPokeDex
bool
Gets whether this save file has Pokédex data.
SeenCount
int
Gets the count of Pokémon species seen.
CaughtCount
int
Gets the count of Pokémon species caught.
PercentSeen
decimal
Gets the percentage of Pokédex completion (seen).
PercentCaught
decimal
Gets the percentage of Pokédex completion (caught).

Validation

ChecksumsValid
bool
Gets whether all checksums in the save file are valid.
ChecksumInfo
string
Gets checksum validation information.

Methods

Save File Operations

Write(BinaryExportSetting setting = BinaryExportSetting.None)
method
Writes the save file to a Memory buffer with optional export settings.Parameters:
  • setting (BinaryExportSetting): Export settings to apply
Returns: Memory<byte> - The finalized save file data
Clone()
method
Creates a deep clone of the save file.Returns: SaveFile - Cloned save file
SetChecksums()
method
Updates all checksums in the save file.
IsVersionValid()
method
Checks if the game version is valid for this save file.Returns: bool - True if version is valid

Data Manipulation

SetData(ReadOnlySpan<byte> input, int offset)
method
Writes data to the save file at the specified offset.Parameters:
  • input (ReadOnlySpan<byte>): Data to write
  • offset (int): Offset in the save file
SetData(Span<byte> dest, ReadOnlySpan<byte> input)
method
Copies input data to the destination span.Parameters:
  • dest (Span<byte>): Destination span
  • input (ReadOnlySpan<byte>): Source data
CopyChangesFrom(SaveFile sav)
method
Copies all changes from another save file.Parameters:
  • sav (SaveFile): Source save file

Box Operations

BoxData
property
Gets or sets all Pokémon in all boxes.Type: IList<PKM>
GetBoxData(int box)
method
Gets all Pokémon from a specific box.Parameters:
  • box (int): Box index
Returns: PKM[] - Array of Pokémon in the box
GetBoxSlotAtIndex(int box, int slot)
method
Gets the Pokémon at a specific box and slot.Parameters:
  • box (int): Box index
  • slot (int): Slot index within the box
Returns: PKM - The Pokémon at that location
SetBoxSlotAtIndex(PKM pk, int box, int slot, EntityImportSettings settings = default)
method
Sets a Pokémon at a specific box and slot.Parameters:
  • pk (PKM): Pokémon to set
  • box (int): Box index
  • slot (int): Slot index within the box
  • settings (EntityImportSettings): Import settings
GetBoxOffset(int box)
method
Gets the byte offset for a specific box.Parameters:
  • box (int): Box index
Returns: int - Byte offset
GetBoxSlotOffset(int box, int slot)
method
Gets the byte offset for a specific box slot.Parameters:
  • box (int): Box index
  • slot (int): Slot index
Returns: int - Byte offset

Party Operations

GetPartySlotAtIndex(int index)
method
Gets the Pokémon at the specified party index.Parameters:
  • index (int): Party slot (0-5)
Returns: PKM - The Pokémon in that slot
SetPartySlotAtIndex(PKM pk, int index, EntityImportSettings settings = default)
method
Sets a Pokémon at the specified party index.Parameters:
  • pk (PKM): Pokémon to set
  • index (int): Party slot (0-5)
  • settings (EntityImportSettings): Import settings
DeletePartySlot(int slot)
method
Deletes a party slot and shifts remaining Pokémon down.Parameters:
  • slot (int): Party slot to delete
GetPartyOffset(int slot)
method
Gets the byte offset for a party slot.Parameters:
  • slot (int): Party slot index
Returns: int - Byte offset
IsPartyAllEggs(int except = -1)
method
Checks if all party members are eggs (optionally excluding one slot).Parameters:
  • except (int): Slot to exclude from check (default: -1 for none)
Returns: bool - True if all are eggs

Storage Management

SortBoxes(int BoxStart = 0, int BoxEnd = -1, Func<IEnumerable<PKM>, int, IEnumerable<PKM>>? sortMethod = null, bool reverse = false)
method
Sorts Pokémon in the specified box range.Parameters:
  • BoxStart (int): Starting box index
  • BoxEnd (int): Ending box index (-1 for last box)
  • sortMethod (Func): Custom sort method
  • reverse (bool): Reverse the sort order
Returns: int - Count of repositioned Pokémon
ClearBoxes(int BoxStart = 0, int BoxEnd = -1, Func<PKM, bool>? deleteCriteria = null)
method
Clears Pokémon from boxes.Parameters:
  • BoxStart (int): Starting box index
  • BoxEnd (int): Ending box index (-1 for last box)
  • deleteCriteria (Func): Criteria for deletion
Returns: int - Count of deleted Pokémon
ModifyBoxes(Action<PKM> action, int BoxStart = 0, int BoxEnd = -1)
method
Modifies all Pokémon in the specified box range.Parameters:
  • action (Action<PKM>): Modification action
  • BoxStart (int): Starting box index
  • BoxEnd (int): Ending box index (-1 for last box)
Returns: int - Count of modified Pokémon
MoveBox(int box, int insertBeforeBox)
method
Moves a box to a new position.Parameters:
  • box (int): Box to move
  • insertBeforeBox (int): Position to insert before
Returns: bool - True if successful
SwapBox(int box1, int box2)
method
Swaps two boxes.Parameters:
  • box1 (int): First box
  • box2 (int): Second box
Returns: bool - True if successful
CompressStorage(out int storedCount, Span<int> slotPointers)
method
Compresses storage by moving all Pokémon to the front.Parameters:
  • storedCount (out int): Number of Pokémon stored
  • slotPointers (Span<int>): Slot pointers to update
Returns: bool - True if storage was compressed

Slot Protection

GetBoxSlotFlags(int box, int slot)
method
Gets the protection flags for a box slot.Parameters:
  • box (int): Box index
  • slot (int): Slot index
Returns: StorageSlotSource - Protection flags
IsBoxSlotLocked(int box, int slot)
method
Checks if a box slot is locked.Parameters:
  • box (int): Box index
  • slot (int): Slot index
Returns: bool - True if locked
IsBoxSlotOverwriteProtected(int box, int slot)
method
Checks if a box slot is protected from overwriting.Parameters:
  • box (int): Box index
  • slot (int): Slot index
Returns: bool - True if protected
NextOpenBoxSlot(int lastKnownOccupied = -1)
method
Finds the next open box slot.Parameters:
  • lastKnownOccupied (int): Last known occupied slot
Returns: int - Index of next open slot, or -1 if storage is full
IsStorageFull
property
Gets whether storage is completely full.Type: bool

Pokédex Operations

GetSeen(ushort species)
method
Checks if a species has been seen.Parameters:
  • species (ushort): Species ID
Returns: bool - True if seen
SetSeen(ushort species, bool seen)
method
Sets whether a species has been seen.Parameters:
  • species (ushort): Species ID
  • seen (bool): Seen status
GetCaught(ushort species)
method
Checks if a species has been caught.Parameters:
  • species (ushort): Species ID
Returns: bool - True if caught
SetCaught(ushort species, bool caught)
method
Sets whether a species has been caught.Parameters:
  • species (ushort): Species ID
  • caught (bool): Caught status

Binary Export

GetPCBinary()
method
Exports all PC box data as a binary array.Returns: byte[] - Binary data
GetBoxBinary(int box)
method
Exports a single box as binary data.Parameters:
  • box (int): Box index
Returns: byte[] - Binary data
SetPCBinary(ReadOnlySpan<byte> data)
method
Imports PC box data from binary.Parameters:
  • data (ReadOnlySpan<byte>): Binary data to import
Returns: bool - True if successful
SetBoxBinary(ReadOnlySpan<byte> data, int box)
method
Imports a single box from binary data.Parameters:
  • data (ReadOnlySpan<byte>): Binary data
  • box (int): Box index
Returns: bool - True if successful

Flag Operations

GetFlag(int offset, int bitIndex)
method
Gets the value of a flag bit.Parameters:
  • offset (int): Byte offset
  • bitIndex (int): Bit index within the byte
Returns: bool - Flag value
SetFlag(int offset, int bitIndex, bool value)
method
Sets the value of a flag bit.Parameters:
  • offset (int): Byte offset
  • bitIndex (int): Bit index within the byte
  • value (bool): Value to set

Usage Examples

Loading and Saving

using PKHeX.Core;

// Load a save file
byte[] saveData = File.ReadAllBytes("save.bin");
var sav = SaveUtil.GetVariantSAV(saveData);

if (sav == null)
{
    Console.WriteLine("Invalid save file!");
    return;
}

Console.WriteLine($"Loaded {sav.Version} save file");
Console.WriteLine($"Trainer: {sav.OT}");
Console.WriteLine($"Play Time: {sav.PlayTimeString}");

// Make changes...
sav.Money = 999999;

// Save the file
var exportData = sav.Write();
File.WriteAllBytes("save_modified.bin", exportData.ToArray());

Working with Party

// Get party data
var party = sav.PartyData;
Console.WriteLine($"Party Count: {sav.PartyCount}");

foreach (var pk in party)
{
    Console.WriteLine($"{pk.Nickname} (Lv. {pk.CurrentLevel})");
}

// Add a Pokémon to party
var newPk = sav.BlankPKM;
newPk.Species = 25; // Pikachu
newPk.CurrentLevel = 50;
newPk.ResetPartyStats();

if (sav.PartyCount < 6)
{
    sav.SetPartySlotAtIndex(newPk, sav.PartyCount);
    Console.WriteLine("Added Pikachu to party!");
}

Box Management

// Get Pokémon from specific box
var box1Pokemon = sav.GetBoxData(0);

// Iterate through all boxes
for (int box = 0; box < sav.BoxCount; box++)
{
    for (int slot = 0; slot < sav.BoxSlotCount; slot++)
    {
        var pk = sav.GetBoxSlotAtIndex(box, slot);
        if (pk.Species != 0) // Not empty
        {
            Console.WriteLine($"Box {box + 1}, Slot {slot + 1}: {pk.Nickname}");
        }
    }
}

// Set a Pokémon in a box
var myPokemon = new PK9();
myPokemon.Species = 6; // Charizard
myPokemon.CurrentLevel = 100;
sav.SetBoxSlotAtIndex(myPokemon, box: 0, slot: 0);

// Find next empty slot
int nextSlot = sav.NextOpenBoxSlot();
if (nextSlot != -1)
{
    Console.WriteLine($"Next empty slot: {nextSlot}");
}

Sorting and Filtering

// Sort all boxes by species
int moved = sav.SortBoxes();
Console.WriteLine($"Sorted {moved} Pokémon");

// Sort specific box range by level (descending)
moved = sav.SortBoxes(
    BoxStart: 0,
    BoxEnd: 2,
    sortMethod: (pkms, _) => pkms.OrderByDescending(p => p.CurrentLevel),
    reverse: false
);

// Clear all eggs from boxes
int deleted = sav.ClearBoxes(
    deleteCriteria: pk => pk.IsEgg
);
Console.WriteLine($"Deleted {deleted} eggs");

// Make all Pokémon shiny
int modified = sav.ModifyBoxes(
    action: pk => pk.SetShiny()
);
Console.WriteLine($"Made {modified} Pokémon shiny");

Pokédex Operations

if (sav.HasPokeDex)
{
    // Check specific Pokémon
    bool seenPikachu = sav.GetSeen(25);
    bool caughtPikachu = sav.GetCaught(25);
    
    Console.WriteLine($"Pikachu - Seen: {seenPikachu}, Caught: {caughtPikachu}");
    
    // Mark as caught
    sav.SetCaught(25, true);
    sav.SetSeen(25, true);
    
    // Get completion stats
    Console.WriteLine($"Pokédex: {sav.CaughtCount}/{sav.MaxSpeciesID} ({sav.PercentCaught:P2})");
    Console.WriteLine($"Seen: {sav.SeenCount} ({sav.PercentSeen:P2})");
}

Binary Import/Export

// Export all boxes to binary
byte[] pcData = sav.GetPCBinary();
File.WriteAllBytes("pc_backup.bin", pcData);

// Export single box
byte[] box1Data = sav.GetBoxBinary(0);
File.WriteAllBytes("box1.bin", box1Data);

// Import box data
byte[] importData = File.ReadAllBytes("box1.bin");
bool success = sav.SetBoxBinary(importData, box: 0);
if (success)
{
    Console.WriteLine("Box imported successfully!");
}

Slot Protection

// Check if slot is locked
if (sav.IsBoxSlotLocked(box: 0, slot: 0))
{
    Console.WriteLine("Slot is locked!");
}

// Check overwrite protection
if (sav.IsBoxSlotOverwriteProtected(box: 0, slot: 0))
{
    Console.WriteLine("Cannot overwrite this slot");
}

// Get slot flags
var flags = sav.GetBoxSlotFlags(box: 0, slot: 0);
if (flags.HasFlag(StorageSlotSource.Party))
{
    Console.WriteLine("This Pokémon is in the party");
}

See Also

Build docs developers (and LLMs) love