-
-
Notifications
You must be signed in to change notification settings - Fork 14
Save Functions
A number of functions have been provided for accessing data from the save. All the functions that begin sav_get_
are used to read values from the loaded save, while all functions that begin with sav_set_
are used to write values to the save.
All of the below functions require the following two arguments:
-
int off1
: the block of data within the save being targeted (see the Save Blocks section below) -
int off2
: the offset within the block of data you want to read from or write to
// individual bits
int sav_get_bit(int off1, int off2, int bitNum);
void sav_set_bit(int bitVal, int off1, int off2, int bitNum);
- The return value of
sav_get_bit
and theint bitVal
argument work the same:0
means the bit is unset and1
means the bit is set -
int bitNum
: the zero-based index of the bit to read/write (see table below if you need help understanding bit indexes)
Byte value: 0x7B
Bit |
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Value | 0 |
1 | 1 | 1 | 1 | 0 | 1 | 1 |
// byte (1 byte == 8 bits)
char sav_get_byte(int off1, int off2);
void sav_set_byte(char data, int off1, int off2);
// short (2 bytes)
short sav_get_short(int off1, int off2);
void sav_set_short(short data, int off1, int off2);
// int (4 bytes)
int sav_get_int(int off1, int off2);
void sav_set_int(int data, int off1, int off2);
Functions for accessing 1-byte (char
), 2-byte (short
), and 4-byte (int
) values.
// arbitrary number of bytes
void sav_get_data(char* dataOut, unsigned int size, int off1, int off2);
void sav_set_data(char* data, unsigned int size, int off1, int off2);
These two functions are for reading and writing an arbitrary number of bytes from the save file
-
unsigned int size
: the number of bytes to read/write -
char* dataOut
: a pointer to an existingchar
array to write data from the save into -
char* data
: a pointer to an existingchar
array holding data to write to the save
// strings
char* sav_get_string(int off1, int off2, unsigned int codepoints);
void sav_set_string(char* string, int off1, int off2, unsigned int codepoints);
These functions are for dealing with values in saves that are meant to be strings.
-
unsigned int codepoints
: the number of characters the game allows for the string -
char* string
: a UTF-8 string in achar
array that will be written to the save -
Note: the
char*
returned bysav_get_string
needs to be manually freed
The below tables detail what values to use for off1
in the sav_get_*
and sav_set_*
API functions detailed above.
For any Generation not mentioned below, just use 0
for off1
For Generation 3 saves, the value to use for off1
is the number in the ID column of this table corresponding to the contents you want to edit.
ID | Contents |
---|---|
0 | Small Block (Trainer Info) |
1 | Large Block Part 1 |
2 | Large Block Part 2 |
3 | Large Block Part 3 |
4 | Large Block Part 4 |
5 | PC Block 0 |
6 | PC Block 1 |
7 | PC Block 2 |
8 | PC Block 3 |
9 | PC Block 4 |
A | PC Block 5 |
B | PC Block 6 |
C | PC Block 7 |
D | PC Block 8 |
You can also provide
-1
as the value foroff1
to make the read/write use an absolute offset
PKSM recognizes 2 types of save blocks for Generation 4 saves: General and Storage.
The General block is usually the one scripts will want to modify. This is where the save holds data like Trainer name, money, items, and even things like the currently active daily swarm.
sav_get_int(sav_gbo(), 0x80); // gets the money for a Platinum save
The Storage block is where the game stores all info related to the PC storage system, like stored Pokémon and box metadata (box names, wallpapers, last active box, etc.). If you're looking to edit stored Pokémon, you're better off using sav_get_pkx
and sav_inject_pkx
, which are detailed on another page of this wiki. For editing box metadata, you will use sav_sbo()
as demonstrated below
sav_set_byte(0, sav_sbo(), 0xcf2c); // sets a Platinum save's current box to Box 1
For Generation 8 saves, the value to use for off1
is the value in the Value to Use column of this table corresponding to the Block you want to edit.
Block | Value to Use |
---|---|
Box | 0x0d66012c |
WondercardData | 0x112d5141 |
Party | 0x2985fe5d |
PokeDex | 0x4716c404 |
ArmorDex | 0x3F936BA9 |
CrownDex | 0x3C9366F0 |
Items | 0x1177c2c4 |
BoxLayout | 0x19722c89 |
Misc | 0x1b882b09 |
TrainerCard | 0x874da6fa |
PlayTime | 0x8cbbfd90 |
Status | 0xf25c070e |
These are a small sample of block keys reported by PKHeX. If you wish to edit a block not listed here, just use the block key from PKHeX.
Along with the above functions, PKSM provides another for accessing certain "named" values from the save
int sav_get_value(enum SAV_Field field, ...);
enum SAV_Field
{
SAV_OT_NAME,
SAV_TID,
SAV_SID,
SAV_GENDER,
SAV_COUNTRY,
SAV_SUBREGION,
SAV_REGION,
SAV_LANGUAGE,
SAV_MONEY,
SAV_BP,
SAV_HOURS,
SAV_MINUTES,
SAV_SECONDS,
SAV_ITEM
};
// example usage
char *otName = (char *)sav_get_value(SAV_OT_NAME);
int tid = sav_get_value(SAV_OT_TID);
int firstMedicine = sav_get_value(SAV_ITEM, Medicine, 0);
Most fields will not require a second argument. Notable caveats and arguments are:
-
SAV_OT_NAME
: Returns a UTF-8 formatted string that must be manually freed. Cast tochar*
to use it properly -
SAV_TID
andSAV_SID
: Both return the 5-digit (pre-Generation 7) format -
SAV_ITEM
: Requires anenum Pouch
(see General Enums and Structs: Pouch) and a slot number. Returns the item ID of the specified slot
int sav_wcx_free_slot();
Gets the first empty Wonder Card slot, or, if all are filled, the maximum index.
void sav_inject_wcx(char* data, enum Generation type, int slot, int alternateFormat);
Injects a wonder card.
-
data
should be a pointer to the Wonder Card data in the correct format for the generation. If the Wonder Card data passed in is not in the correct format, issues will occur. -
slot
refers to which Wonder Card slot it should be injected to, though GEN_LGPE does not store Wonder Cards, and, as such, this argument does not affect it. -
alternateFormat
is a boolean that refers to the format of the data passed in, based on the value ofenum Generation type
as follows:-
GEN_THREE
: function call will silently do nothing, because PKSM does not support the WC3 format -
GEN_FOUR
: if true,data
is interpreted as a WC4 (meaning that the internal Pokémon data is decrypted). Otherwise,data
will be interpreted as a PGT. -
GEN_FIVE
: ignored -
GEN_SIX
: if true,data
is interpreted as a WC6FULL. Otherwise,data
will be interpreted as a WC6 -
GEN_SEVEN
: if true,data
is interpreted as a WC7FULL. Otherwise,data
will be interpreted as a WC7 -
GEN_LGPE
: if true,data
is interpreted as a WB7FULL. Otherwise,data
will be interpreted as a WB7 -
GEN_EIGHT
: ignored
-
void sav_box_decrypt();
void sav_box_encrypt();
Since the games store Pokémon in an encrypted format, these functions let scripts decrypt that data so it can be properly read and/or edited. Any edits you aim to make should be done after calling sav_box_decrypt
and before calling sav_box_encrypt
.
IMPORTANT: These should always be used as a pair and always in this order. Mixing them or not using them in pairs will produce unpredictable results.
char* save_path(void);
For those scripts that might want to know where on the SD card the loaded save file is from, this function returns the file's path. If the loaded save is the active save of a 3DS title, this function returns NULL
.
Note: The returned char*
needs to be manually freed.
Enjoy using PKSM? Consider supporting FlagBrew on Patreon