Do a little bit of documentation and optimization for some of the current save-related routines.

Some of the logic wasn't very clear from just looking at the code and some of it also wasn't very optimal.
So I added some comments and optimized some stuff to make it better.
This commit is contained in:
Philippe Symons 2026-03-11 12:25:53 +01:00
parent 23292afc0c
commit 547d2c81c0
11 changed files with 66 additions and 61 deletions

View File

@ -15,10 +15,25 @@ extern char mem_name;
extern u8 global_memory_buffer[0x1000]; extern u8 global_memory_buffer[0x1000];
extern rom_data curr_GBA_rom; extern rom_data curr_GBA_rom;
void initalize_memory_locations(); /**
* @brief Given that the save sections within a save slot are not saved in a fixed order,
* This function will walk through the save slot to fill the memory_section_array map.
*
* After calling this, memory_section_array will point to the save offset by each section ID.
* So memory_section_array[0] will point to the save offset of section 0, etc.
*/
void initialize_memory_locations();
void print_mem_section(); void print_mem_section();
//bool insert_pokemon(Pokemon party_array[], int num); //bool insert_pokemon(Pokemon party_array[], int num);
void reverse_endian(u8 *data, size_t size); void reverse_endian(u8 *data, size_t size);
/**
* @brief This function will update the checksum in global_memory_buffer.
* It assumes that global_memory_buffer contains one of the save data sections.
*
* @param hall_of_fame This is needed to indicate if you're trying to save the hall of fame section. The checksum offset is slightly different there.
*/
void update_memory_buffer_checksum(bool hall_of_fame); void update_memory_buffer_checksum(bool hall_of_fame);
bool read_flag(u16 flag_id); bool read_flag(u16 flag_id);
bool compare_map_and_npc_data(int map_bank, int map_id, int npc_id); bool compare_map_and_npc_data(int map_bank, int map_id, int npc_id);

View File

@ -4,7 +4,7 @@
#include <tonc.h> #include <tonc.h>
#include "pokemon_party.h" #include "pokemon_party.h"
#include "debug_mode.h" #include "debug_mode.h"
#include "save_data_manager.h" #include "ptgb_save_data_manager.h"
#include "script_var.h" #include "script_var.h"
#include "pokemon_data.h" #include "pokemon_data.h"

View File

@ -1,12 +1,14 @@
#ifndef SAVE_DATA_MANAGER_H #ifndef PTGB_SAVE_DATA_MANAGER_H
#define SAVE_DATA_MANAGER_H #define PTGB_SAVE_DATA_MANAGER_H
#include <tonc.h>
#include "rom_data.h" #include "rom_data.h"
#define HALL_OF_FAME 0x01C000 #define HALL_OF_FAME 0x01C000
#define HOF_SECTION 2032 #define HOF_SECTION 2032
// NOTE: This module doesn't manage the _game_ save. It manages Poke_Transporter's specific save data.
// This save data is stored at an unused section (HOF_SECTION) of the HALL OF FAME save section.
// Data map: // Data map:
// There are 1936 (0x790) unused bytes starting at 0x1D7F0 // There are 1936 (0x790) unused bytes starting at 0x1D7F0
@ -27,7 +29,7 @@ void set_def_lang(int nLang);
int get_def_lang_num(); int get_def_lang_num();
bool get_tutorial_flag(); bool get_tutorial_flag();
void set_tutorial_flag(bool value); void set_tutorial_flag(bool value);
void initalize_save_data(); void initialize_save_data();
int get_dex_completion(int gen, bool include_mythicals); int get_dex_completion(int gen, bool include_mythicals);
bool check_can_save(); bool check_can_save();
#endif #endif

View File

@ -2,7 +2,7 @@
#include "typeDefs.h" #include "typeDefs.h"
#include "button_menu.h" #include "button_menu.h"
#include "button_handler.h" #include "button_handler.h"
#include "save_data_manager.h" #include "ptgb_save_data_manager.h"
#include "global_frame_controller.h" #include "global_frame_controller.h"
#include "string" #include "string"
#include "sprite_data.h" #include "sprite_data.h"

View File

@ -1,7 +1,7 @@
#include <tonc.h> #include <tonc.h>
#include "button_menu.h" #include "button_menu.h"
#include "button_handler.h" #include "button_handler.h"
#include "save_data_manager.h" #include "ptgb_save_data_manager.h"
#include "global_frame_controller.h" #include "global_frame_controller.h"
#include "string" #include "string"

View File

@ -6,19 +6,17 @@
#include "libraries/Pokemon-Gen3-to-Gen-X/include/save.h" #include "libraries/Pokemon-Gen3-to-Gen-X/include/save.h"
#include "text_engine.h" #include "text_engine.h"
#define pkmn_length 80 #define READ_SAVE_SECTIONS 14
#define READ_SAVE_SECTIONS 5
#define TOTAL_SAVE_SECTIONS 14 #define TOTAL_SAVE_SECTIONS 14
vu32 newest_save_offset = SAVE_A_OFFSET; vu32 newest_save_offset = SAVE_A_OFFSET;
vu32 memory_section_array[READ_SAVE_SECTIONS] = {}; vu32 memory_section_array[READ_SAVE_SECTIONS];
u8 global_memory_buffer[0x1000]; u8 global_memory_buffer[0x1000];
char mem_name = 'A';
u8 mem_id; u8 mem_id;
// Fills the variables with the current offset information // Fills the variables with the current offset information
void initalize_memory_locations() void initialize_memory_locations()
{ {
u8 save_A_index[4]; u8 save_A_index[4];
u8 save_B_index[4]; u8 save_B_index[4];
@ -31,10 +29,12 @@ void initalize_memory_locations()
if (*(vu32 *)save_B_index > *(vu32 *)save_A_index) if (*(vu32 *)save_B_index > *(vu32 *)save_A_index)
{ {
newest_save_offset = SAVE_B_OFFSET; newest_save_offset = SAVE_B_OFFSET;
mem_name = 'B';
} }
// Populates the memory_section_array with the correct pointer locations // Populates the memory_section_array with the correct pointer locations
// The sections within the save slot are rotated on every save. So it doesn't
// start at the first section. However, the next sections follow sequentially.
// https://bulbapedia.bulbagarden.net/wiki/Save_data_structure_(Generation_III)#Section_ID
copy_save_to_ram(newest_save_offset + SECTION_ID_OFFSET, &mem_id, 1); copy_save_to_ram(newest_save_offset + SECTION_ID_OFFSET, &mem_id, 1);
for (int i = 0; i < TOTAL_SAVE_SECTIONS; i++) for (int i = 0; i < TOTAL_SAVE_SECTIONS; i++)
{ {
@ -99,6 +99,7 @@ void print_mem_section()
/* /*
uint16_t charset[256]; uint16_t charset[256];
byte out[4] = {0, 0, 0, 0xFF}; byte out[4] = {0, 0, 0, 0xFF};
const char mem_name = (newest_save_offset == SAVE_A_OFFSET) ? 'A' : 'B';
load_localized_charset(charset, 3, ENGLISH); load_localized_charset(charset, 3, ENGLISH);
@ -124,33 +125,28 @@ void reverse_endian(u8 *data, size_t size)
void update_memory_buffer_checksum(bool hall_of_fame) void update_memory_buffer_checksum(bool hall_of_fame)
{ {
vu32 checksum = 0x00; u32 checksum = 0x00;
vu32 num_of_bytes = 3968; // Section 13 is the last PC buffer (I) and that one only has 2000 bytes of data.
if (global_memory_buffer[0x0FF4] == 13) // source: https://bulbapedia.bulbagarden.net/wiki/Save_data_structure_(Generation_III)#Section_ID
const u32 num_of_bytes = (global_memory_buffer[SECTION_ID_OFFSET] != 13) ? 3968 : 2000;
// the cpu is little endian and the data is read as little endian too.
// therefore, we can do a straightforward sum of the data as u32's.
const u32 *cur = (const u32 *)global_memory_buffer;
const u32 * const end = (const u32 *)(global_memory_buffer + num_of_bytes);
while (cur < end)
{ {
num_of_bytes = 2000; checksum += *cur;
++cur;
} }
for (unsigned int i = 0; i < num_of_bytes / 4; i++) const u16 small_checksum = ((checksum & 0xFFFF0000) >> 16) + (checksum & 0x0000FFFF);
{ const u32 checksum_offset = hall_of_fame ? 0x0FF4 : 0x0FF6;
checksum += (global_memory_buffer[(4 * i) + 3] << 24) |
(global_memory_buffer[(4 * i) + 2] << 16) | global_memory_buffer[checksum_offset] = small_checksum & 0x00FF;
(global_memory_buffer[(4 * i) + 1] << 8) | global_memory_buffer[checksum_offset + 1] = (small_checksum & 0xFF00) >> 8;
(global_memory_buffer[(4 * i) + 0] << 0);
}
vu16 small_checksum = ((checksum & 0xFFFF0000) >> 16) + (checksum & 0x0000FFFF);
if (hall_of_fame)
{
global_memory_buffer[0x0FF4] = small_checksum & 0x00FF;
global_memory_buffer[0x0FF5] = (small_checksum & 0xFF00) >> 8;
}
else
{
global_memory_buffer[0x0FF6] = small_checksum & 0x00FF;
global_memory_buffer[0x0FF7] = (small_checksum & 0xFF00) >> 8;
}
} }
bool read_flag(u16 flag_id) bool read_flag(u16 flag_id)

View File

@ -21,7 +21,7 @@
#include "pokedex.h" #include "pokedex.h"
#include "global_frame_controller.h" #include "global_frame_controller.h"
#include "pkmn_font.h" #include "pkmn_font.h"
#include "save_data_manager.h" #include "ptgb_save_data_manager.h"
#include "mystery_gift_injector.h" #include "mystery_gift_injector.h"
#include "mystery_gift_builder.h" #include "mystery_gift_builder.h"
#include "multiboot_upload.h" #include "multiboot_upload.h"
@ -72,9 +72,9 @@ void load_graphics()
yes_no_menu.add_button(Button(button_no), false); yes_no_menu.add_button(Button(button_no), false);
} }
void initalization_script(void) void initialization_script(void)
{ {
// Initalizations // Initializations
REG_DISPCNT = DCNT_BLANK | DCNT_MODE0 | DCNT_BG0 | DCNT_BG1 | DCNT_BG2 | DCNT_BG3 | DCNT_OBJ | DCNT_OBJ_1D; REG_DISPCNT = DCNT_BLANK | DCNT_MODE0 | DCNT_BG0 | DCNT_BG1 | DCNT_BG2 | DCNT_BG3 | DCNT_OBJ | DCNT_OBJ_1D;
// Disable for save data read/write // Disable for save data read/write
@ -452,7 +452,7 @@ static void __attribute__((noinline)) show_intro()
int main(void) int main(void)
{ {
malloc_init_default_pool(); malloc_init_default_pool();
initalization_script(); initialization_script();
// Set colors based on current ROM // Set colors based on current ROM
set_background_pal(0, false, false); set_background_pal(0, false, false);
@ -482,15 +482,15 @@ int main(void)
obj_hide_multi(ptgb_logo_l, 2); obj_hide_multi(ptgb_logo_l, 2);
global_next_frame(); global_next_frame();
game_load_error(); game_load_error();
// initalization_script(); // initialization_script();
} }
} }
// Initalize memory and save data after loading the game // Initialize memory and save data after loading the game
reset_textbox(); reset_textbox();
REG_BG2CNT = REG_BG2CNT | BG_PRIO(3); REG_BG2CNT = REG_BG2CNT | BG_PRIO(3);
init_bank(); init_bank();
initalize_memory_locations(); initialize_memory_locations();
load_custom_save_data(); load_custom_save_data();
set_background_pal(curr_GBA_rom.gamecode, false, true); set_background_pal(curr_GBA_rom.gamecode, false, true);
@ -499,7 +499,7 @@ int main(void)
{ {
obj_hide_multi(ptgb_logo_l, 2); obj_hide_multi(ptgb_logo_l, 2);
text_loop(BTN_TRANSFER); text_loop(BTN_TRANSFER);
initalize_save_data(); initialize_save_data();
// TODO: We should be able to test for a Bootleg rom in here- if the save data isn't written, then it is bootleg. // TODO: We should be able to test for a Bootleg rom in here- if the save data isn't written, then it is bootleg.
} }

View File

@ -6,7 +6,7 @@
#include "sprite_data.h" #include "sprite_data.h"
#include "pokemon_data.h" #include "pokemon_data.h"
#include "global_frame_controller.h" #include "global_frame_controller.h"
#include "save_data_manager.h" #include "ptgb_save_data_manager.h"
#include "button_handler.h" #include "button_handler.h"
#include "translated_text.h" #include "translated_text.h"
#include "text_engine.h" #include "text_engine.h"

View File

@ -12,7 +12,7 @@
#include "gb_gen1_payloads_RB_lz10_bin.h" #include "gb_gen1_payloads_RB_lz10_bin.h"
#include "gb_gen1_payloads_Y_lz10_bin.h" #include "gb_gen1_payloads_Y_lz10_bin.h"
#include "gb_gen2_payloads_lz10_bin.h" #include "gb_gen2_payloads_lz10_bin.h"
#include "save_data_manager.h" #include "ptgb_save_data_manager.h"
#include "libraries/Pokemon-Gen3-to-Gen-X/include/save.h" #include "libraries/Pokemon-Gen3-to-Gen-X/include/save.h"
static byte gen1_rb_debug_box_data[0x462] = { static byte gen1_rb_debug_box_data[0x462] = {

View File

@ -1,5 +1,5 @@
#include <tonc.h> #include <tonc.h>
#include "save_data_manager.h" #include "ptgb_save_data_manager.h"
#include "flash_mem.h" #include "flash_mem.h"
#include "debug_mode.h" #include "debug_mode.h"
#include "button_menu.h" #include "button_menu.h"
@ -11,19 +11,14 @@ byte save_data_array[SAVE_DATA_SIZE];
void load_custom_save_data() void load_custom_save_data()
{ {
copy_save_to_ram(HALL_OF_FAME + 0x1000, &global_memory_buffer[0], 0x1000); copy_save_to_ram(HALL_OF_FAME + 0x1000, &global_memory_buffer[0], 0x1000);
for (int i = 0; i < SAVE_DATA_SIZE; i++) memcpy(save_data_array, global_memory_buffer + HOF_SECTION, SAVE_DATA_SIZE);
{
save_data_array[i] = global_memory_buffer[HOF_SECTION + i];
}
} }
void write_custom_save_data() void write_custom_save_data()
{ {
copy_save_to_ram(HALL_OF_FAME + 0x1000, &global_memory_buffer[0], 0x1000); copy_save_to_ram(HALL_OF_FAME + 0x1000, &global_memory_buffer[0], 0x1000);
for (int i = 0; i < SAVE_DATA_SIZE; i++) memcpy(global_memory_buffer + HOF_SECTION, save_data_array, SAVE_DATA_SIZE);
{
global_memory_buffer[HOF_SECTION + i] = save_data_array[i];
}
update_memory_buffer_checksum(true); update_memory_buffer_checksum(true);
erase_sector(HALL_OF_FAME + 0x1000); erase_sector(HALL_OF_FAME + 0x1000);
copy_ram_to_save(&global_memory_buffer[0], HALL_OF_FAME + 0x1000, 0x1000); copy_ram_to_save(&global_memory_buffer[0], HALL_OF_FAME + 0x1000, 0x1000);
@ -59,12 +54,9 @@ bool get_tutorial_flag()
return save_data_array[TUTORIAL_FLAG]; return save_data_array[TUTORIAL_FLAG];
} }
void initalize_save_data() void initialize_save_data()
{ {
for (int i = 0; i < SAVE_DATA_SIZE; i++) memset(save_data_array, 0, SAVE_DATA_SIZE);
{
save_data_array[i] = 0;
}
set_tutorial_flag(true); set_tutorial_flag(true);
write_custom_save_data(); write_custom_save_data();
} }

View File

@ -3,7 +3,7 @@
#include "pokemon_party.h" #include "pokemon_party.h"
#include "pokemon_data.h" #include "pokemon_data.h"
#include "text_engine.h" #include "text_engine.h"
#include "save_data_manager.h" #include "ptgb_save_data_manager.h"
#include "gba_rom_values/gba_rom_values.h" #include "gba_rom_values/gba_rom_values.h"
#include "libraries/nanoprintf/nanoprintf.h" #include "libraries/nanoprintf/nanoprintf.h"
#include "gba_rom_values_eng_lz10_bin.h" #include "gba_rom_values_eng_lz10_bin.h"