Overview
Once you’ve loaded a save file, you can modify various aspects including Pokémon, items, trainer information, Pokédex entries, and game progress. PKHeX.Core provides safe APIs for all modifications.Always work on a backup copy of save files. Validate checksums after modifications.
Modifying Trainer Information
Basic Trainer Data
var sav = SaveUtil.GetSaveFile("save.sav");
if (sav != null)
{
// Change trainer name
sav.OT = "ASH";
// Change Trainer IDs
sav.TID16 = 12345;
sav.SID16 = 54321;
// Or set full ID32 (Gen 7+)
sav.ID32 = 0x12345678;
// Change gender (0 = Male, 1 = Female)
sav.Gender = 0;
// Change money
sav.Money = 999999;
// Mark as edited
Console.WriteLine($"Edited: {sav.State.Edited}");
}
Play Time
// Modify play time
sav.PlayedHours = 255;
sav.PlayedMinutes = 59;
sav.PlayedSeconds = 59;
Console.WriteLine($"Play Time: {sav.PlayTimeString}");
Generation-Specific Properties
// Gen 6+ properties
if (sav is SAV6 sav6)
{
sav6.Region = 1; // Region code
sav6.Country = 49; // Country code
sav6.GameSyncID = "1234567890123456";
}
// Gen 7+ properties
if (sav is SAV7 sav7)
{
sav7.TrainerTID7 = 123456; // New TID format
}
// Gen 8+ properties
if (sav is SAV8SWSH swsh)
{
swsh.MyStatus.OT = "TRAINER";
swsh.MyStatus.Money = 999999;
}
Working with Pokémon
Reading Party Pokémon
var sav = SaveUtil.GetSaveFile("save.sav");
if (sav != null)
{
Console.WriteLine($"Party Count: {sav.PartyCount}");
for (int i = 0; i < sav.PartyCount; i++)
{
var pk = sav.GetPartySlot(i);
Console.WriteLine($"Slot {i + 1}: {pk.Species} Lv.{pk.CurrentLevel}");
Console.WriteLine($" Nickname: {pk.Nickname}");
Console.WriteLine($" OT: {pk.OriginalTrainerName}");
Console.WriteLine($" Moves: {string.Join(", ", pk.Moves)}");
}
}
Reading Box Pokémon
// Read a specific box
int boxIndex = 0;
for (int slot = 0; slot < sav.BoxSlotCount; slot++)
{
var pk = sav.GetBoxSlotAtIndex(boxIndex, slot);
if (pk.Species != 0) // Not empty
{
Console.WriteLine($"Box {boxIndex + 1} Slot {slot + 1}: {pk.Species}");
}
}
Adding Pokémon to Party
// Create a new Pokémon
var pk = sav.BlankPKM;
pk.Species = (ushort)Species.Pikachu;
pk.Form = 0;
pk.CurrentLevel = 50;
pk.Move1 = (ushort)Move.Thunderbolt;
pk.Move2 = (ushort)Move.QuickAttack;
pk.Move3 = (ushort)Move.IronTail;
pk.Move4 = (ushort)Move.ElectroBall;
// Set legal values
pk.HealPP();
pk.Nickname = "PIKACHU";
pk.Ball = (byte)Ball.Poke;
pk.MetLevel = 5;
pk.MetLocation = 1;
// Apply trainer info
pk.OriginalTrainerName = sav.OT;
pk.TID16 = sav.TID16;
pk.SID16 = sav.SID16;
pk.OriginalTrainerGender = sav.Gender;
// Refresh calculated values
pk.RefreshChecksum();
pk.RefreshAbility(pk.AbilityNumber);
// Add to party
if (sav.PartyCount < 6)
{
sav.SetPartySlot(pk, sav.PartyCount);
Console.WriteLine("Pokémon added to party!");
}
else
{
Console.WriteLine("Party is full!");
}
Adding Pokémon to Box
// Add to specific box and slot
int targetBox = 0;
int targetSlot = 0;
sav.SetBoxSlotAtIndex(pk, targetBox, targetSlot);
Console.WriteLine($"Pokémon added to Box {targetBox + 1}, Slot {targetSlot + 1}");
Modifying Existing Pokémon
// Get a Pokémon from party
var pk = sav.GetPartySlot(0);
// Modify properties
pk.CurrentLevel = 100;
pk.Species = (ushort)Species.Charizard;
pk.Form = 0;
pk.Nickname = "CHARIZARD";
// Modify stats
pk.IV_HP = 31;
pk.IV_ATK = 31;
pk.IV_DEF = 31;
pk.IV_SPA = 31;
pk.IV_SPD = 31;
pk.IV_SPE = 31;
pk.EV_HP = 252;
pk.EV_ATK = 252;
pk.EV_SPE = 4;
// Modify moves
pk.Move1 = (ushort)Move.FireBlast;
pk.Move2 = (ushort)Move.AirSlash;
pk.Move3 = (ushort)Move.DragonPulse;
pk.Move4 = (ushort)Move.SolarBeam;
pk.HealPP();
// Set as shiny
pk.SetShiny();
// Update checksum
pk.RefreshChecksum();
// Write back to save
sav.SetPartySlot(pk, 0);
Console.WriteLine("Pokémon modified!");
Cloning Pokémon
// Clone a Pokémon
var original = sav.GetPartySlot(0);
var clone = original.Clone();
// Add clone to box
sav.SetBoxSlotAtIndex(clone, 0, 0);
Modifying Items
Reading Items
var inventory = sav.Inventory;
// Get all items of a specific pouch
var medicines = inventory.Medicine.Items;
foreach (var item in medicines)
{
if (item.Count > 0)
{
Console.WriteLine($"{item.Index}: {item.Count}x");
}
}
Adding/Modifying Items
// Gen 8 example
if (sav is SAV8SWSH swsh)
{
var items = swsh.Items;
// Give max regular items
items.GiveItem(0001, 999); // Master Ball
items.GiveItem(0002, 999); // Ultra Ball
items.GiveItem(0017, 999); // Max Potion
Console.WriteLine("Items added!");
}
// Gen 9 example
if (sav is SAV9SV sv)
{
var items = sv.Items;
items.GiveItem(0001, 999); // Master Ball
items.GiveItem(1234, 50); // Any item by ID
}
Working with Item Pouches
var inventory = sav.Inventory;
// Access different pouches
var regularItems = inventory.Items;
var keyItems = inventory.KeyItems;
var tmhm = inventory.TMHMs;
var medicine = inventory.Medicine;
var berries = inventory.Berries;
var balls = inventory.Balls;
var battleItems = inventory.BattleItems;
// Modify a pouch
foreach (var item in regularItems.Items)
{
if (item.Count > 0)
{
item.Count = 999; // Max out all items
}
}
// Write back
inventory.Items = regularItems;
Modifying Pokédex
Setting Pokédex Entries
// Mark Pokémon as seen/caught
if (sav is SAV8SWSH swsh)
{
var dex = swsh.Zukan;
// Set species as seen
dex.SetSeen((ushort)Species.Pikachu, true);
// Set species as caught
dex.SetCaught((ushort)Species.Pikachu, true);
// Set specific form as caught
dex.SetCaught((ushort)Species.Pikachu, 0, true);
}
if (sav is SAV9SV sv)
{
var dex = sv.Zukan;
// Complete Pokédex
for (ushort species = 1; species <= sv.MaxSpeciesID; species++)
{
if (sv.Personal.IsSpeciesInGame(species))
{
dex.SetCaught(species, true);
dex.SetSeen(species, true);
}
}
}
Completing Pokédex
public void CompleteDex(SaveFile sav)
{
if (sav is SAV8SWSH swsh)
{
var dex = swsh.Zukan;
for (ushort i = 1; i <= swsh.MaxSpeciesID; i++)
{
if (swsh.Personal.IsSpeciesInGame(i))
{
dex.SetCaught(i, true);
}
}
Console.WriteLine("Pokédex completed!");
}
}
Modifying Box Names and Wallpapers
if (sav is IBoxDetailName boxNames)
{
// Set box names
for (int i = 0; i < sav.BoxCount; i++)
{
boxNames.SetBoxName(i, $"Box {i + 1}");
}
}
if (sav is IBoxDetailWallpaper wallpapers)
{
// Set box wallpapers
for (int i = 0; i < sav.BoxCount; i++)
{
wallpapers.SetBoxWallpaper(i, i % 16); // Cycle through wallpapers
}
}
Modifying Event Flags
Setting Event Flags
if (sav is IEventFlagProvider flagProvider)
{
// Set a specific event flag
flagProvider.SetEventFlag(1234, true);
// Check if flag is set
bool isSet = flagProvider.GetEventFlag(1234);
Console.WriteLine($"Flag 1234: {isSet}");
}
// Gen-specific flags
if (sav is SAV8SWSH swsh)
{
// Set bike unlocked flag (example)
swsh.SetEventFlag(0x1234, true);
}
Working with Work Values
if (sav is IEventWorkProvider<ushort> workProvider)
{
// Set work value (variables used by game events)
workProvider.SetWork(100, 999);
// Get work value
ushort value = workProvider.GetWork(100);
Console.WriteLine($"Work 100: {value}");
}
Modifying Game Progress
Badges (Older Generations)
if (sav is SAV4 sav4)
{
// Set all badges
for (int i = 0; i < 8; i++)
{
sav4.SetFlag(sav4.GetWorkBlock(), i, true);
}
}
if (sav is SAV6 sav6)
{
sav6.Badges = 0xFF; // All 8 badges
}
Battle Points
if (sav is SAV6 sav6)
{
sav6.BP = 9999;
}
if (sav is SAV8SWSH swsh)
{
swsh.SetValue<uint>(0xYourBPKey, 9999);
}
Working with Multiple Pokémon
Batch Modifications
public void MaximizeAllPartyPokemon(SaveFile sav)
{
for (int i = 0; i < sav.PartyCount; i++)
{
var pk = sav.GetPartySlot(i);
// Max level
pk.CurrentLevel = 100;
// Max IVs
pk.IV_HP = pk.IV_ATK = pk.IV_DEF = 31;
pk.IV_SPA = pk.IV_SPD = pk.IV_SPE = 31;
// Max happiness
pk.CurrentFriendship = 255;
// Heal
pk.Heal();
pk.HealPP();
// Update checksum
pk.RefreshChecksum();
// Write back
sav.SetPartySlot(pk, i);
}
Console.WriteLine("All party Pokémon maximized!");
}
Box Operations
// Clear a box
public void ClearBox(SaveFile sav, int boxIndex)
{
for (int slot = 0; slot < sav.BoxSlotCount; slot++)
{
sav.SetBoxSlotAtIndex(sav.BlankPKM, boxIndex, slot);
}
}
// Copy box to another
public void CopyBox(SaveFile sav, int sourceBox, int destBox)
{
for (int slot = 0; slot < sav.BoxSlotCount; slot++)
{
var pk = sav.GetBoxSlotAtIndex(sourceBox, slot);
sav.SetBoxSlotAtIndex(pk.Clone(), destBox, slot);
}
}
Generation-Specific Features
Gen 8 Records
if (sav is SAV8SWSH swsh)
{
var records = swsh.Records;
// Set battle records
records.SetRecord(0, 999); // Example record
Console.WriteLine("Records modified!");
}
Gen 9 Fashion and Appearance
if (sav is SAV9SV sv)
{
var fashion = sv.PlayerFashion;
var appearance = sv.PlayerAppearance;
// Unlock all fashion items
// (specific implementation depends on block structure)
Console.WriteLine("Fashion unlocked!");
}
Data Validation
Validating Modified Pokémon
using PKHeX.Core;
var pk = sav.GetPartySlot(0);
// Validate the Pokémon
var la = new LegalityAnalysis(pk);
if (la.Valid)
{
Console.WriteLine("Pokémon is legal!");
}
else
{
Console.WriteLine("Pokémon has legality issues:");
foreach (var line in la.Report)
{
Console.WriteLine($" - {line}");
}
}
Sanitizing Data
// Apply sanity checks
public PKM SanitizePokemon(PKM pk, SaveFile sav)
{
// Ensure level is valid
if (pk.CurrentLevel > 100)
pk.CurrentLevel = 100;
// Ensure stats are within bounds
if (pk.IV_HP > 31) pk.IV_HP = 31;
if (pk.IV_ATK > 31) pk.IV_ATK = 31;
// ... etc
// Ensure moves are valid
var learnset = pk.GetLegalMoves();
// Validate moves against learnset
pk.RefreshChecksum();
return pk;
}
Always call
pk.RefreshChecksum() after modifying a Pokémon to update its internal checksum.Best Practices
- Always work on copies: Never modify the original save file directly
- Validate data: Check bounds and legal values before setting
- Update checksums: Call
RefreshChecksum()on modified Pokémon - Mark as edited: The
State.Editedflag tracks changes - Test modifications: Load the modified save in-game to verify
public SaveFile SafeModification(string path)
{
// Load save
var sav = SaveUtil.GetSaveFile(path);
if (sav == null) return null;
// Clone for safety
var modified = sav.Clone();
// Make modifications
modified.OT = "TEST";
// Validate
if (modified.ChecksumsValid)
{
return modified;
}
return null;
}
Next Steps
Exporting Saves
Learn how to export and write modified save files
Creating Pokémon
Create new Pokémon with proper attributes