diff --git a/include/dbg/debug_menu_functions.h b/include/dbg/debug_menu_functions.h index 7332fe9..505eae9 100644 --- a/include/dbg/debug_menu_functions.h +++ b/include/dbg/debug_menu_functions.h @@ -14,12 +14,12 @@ void show_text_debug_screen(void *context, unsigned user_param); void show_debug_info_screen(void *context, unsigned user_param); /** - * @brief This generic boolean callback function can be used to set/unset any boolean flag. + * @brief This is a generic u8/bool value set callback * - * @param context a (bool *) pointer to the flag you want to set/unset. + * @param context a (u8* or bool*) pointer to the flag you want to set/unset. * @param user_param the value to set the flag to. 0 -> false, anything else -> true. */ -void dbg_set_boolean_flag(void *context, unsigned user_param); +void dbg_set_byte_val(void *context, unsigned user_param); /** * @brief This function will take a song index and play the corresponding song. diff --git a/include/dbg/debug_mode.h b/include/dbg/debug_mode.h index b7eb639..1cc7190 100644 --- a/include/dbg/debug_mode.h +++ b/include/dbg/debug_mode.h @@ -13,6 +13,15 @@ #define DEBUG_MODE 0 #endif +#include "typeDefs.h" + +enum WriteCableDataMode +{ + WRITE_CABLE_DATA_MODE_OFF = 0, + WRITE_CABLE_DATA_MODE_SRAM = 1, + WRITE_CABLE_DATA_MODE_CART = 2 +}; + /** * @brief This structs contains debug options * that can be influenced through the debug menu. @@ -84,9 +93,19 @@ typedef struct debug_options bool force_all_caught; /** - * @brief If this option is set, we will write the data received over the link cable to the save data at offset 0x0000. + * @brief If this option is set, we will write the data received over the link cable to SRAM. + * Please make sure to only use this with loader.gba/Poke_Transporter_GB_standalone.gba and + * make sure to ignore_game_pak and ignore_game_pak_sprites too! + * + * Also: you may need to reboot to your flashcart menu to make it update the .sav file. */ - bool write_cable_data_to_save; + u8 write_cable_data_to_save; + + /** + * @brief If this option is set, we will load the data -written by write_cable_data_to_save- from the SRAM + * instead of actually communicating over the link cable. + */ + u8 load_cable_data_from_save; /** * @brief If this option is enabled, we will show control characters in the text. diff --git a/include/libraries/Pokemon-Gen3-to-Gen-X/include/save.h b/include/libraries/Pokemon-Gen3-to-Gen-X/include/save.h index d3d9b9a..818e110 100644 --- a/include/libraries/Pokemon-Gen3-to-Gen-X/include/save.h +++ b/include/libraries/Pokemon-Gen3-to-Gen-X/include/save.h @@ -8,6 +8,8 @@ #define SECTOR_SIZE_BITS 12 #define SECTOR_SIZE (1< -static const option_data toggle_options[2] = { +static const option_data toggle_options[] = { { .text = "Off", .value = 0 @@ -18,6 +18,23 @@ static const option_data toggle_options[2] = { } }; +static const option_data cbl_data_options[] = { + { + .text = "Off", + .value = WRITE_CABLE_DATA_MODE_OFF + }, + { + .text = "SRAM", + .value = WRITE_CABLE_DATA_MODE_SRAM + }, + { + .text = "CART", + .value = WRITE_CABLE_DATA_MODE_CART + } +}; + +#define CBL_DATA_OPTIONS_COUNT (sizeof(cbl_data_options) / sizeof(option_data)) + /** * @brief This helper function makes it easy to define a multiple choice debug row. */ @@ -108,19 +125,20 @@ void fill_debug_menu_with_entries(vertical_menu &menu, u16 *charset) define_executable_row(charset, "Unlock MystG", dbg_unlock_mystery, 1, nullptr), #endif define_song_row(charset, "Song"), - define_toggle_row(charset, "Print Link", dbg_set_boolean_flag, g_debug_options.print_link_data, &g_debug_options.print_link_data), - define_toggle_row(charset, "Instant Text", dbg_set_boolean_flag, g_debug_options.instant_text_speed, &g_debug_options.instant_text_speed), - define_toggle_row(charset, "Ign Cart", dbg_set_boolean_flag, g_debug_options.ignore_game_pak, &g_debug_options.ignore_game_pak), - define_toggle_row(charset, "Ign Sprites", dbg_set_boolean_flag, g_debug_options.ignore_game_pak_sprites, &g_debug_options.ignore_game_pak_sprites), - define_toggle_row(charset, "Ign Link", dbg_set_boolean_flag, g_debug_options.ignore_link_cable, &g_debug_options.ignore_link_cable), - define_toggle_row(charset, "Ign MG/E4", dbg_set_boolean_flag, g_debug_options.ignore_mg_e4_flags, &g_debug_options.ignore_mg_e4_flags), - define_toggle_row(charset, "Ign Unrec PKMN", dbg_set_boolean_flag, g_debug_options.ignore_unreceived_pkmn, &g_debug_options.ignore_unreceived_pkmn), - define_toggle_row(charset, "Force Tut", dbg_set_boolean_flag, g_debug_options.force_tutorial, &g_debug_options.force_tutorial), - define_toggle_row(charset, "Show Invalid", dbg_set_boolean_flag, g_debug_options.dont_hide_invalid_pkmn, &g_debug_options.dont_hide_invalid_pkmn), - define_toggle_row(charset, "Ign Dex Compl", dbg_set_boolean_flag, g_debug_options.ignore_dex_completion, &g_debug_options.ignore_dex_completion), - define_toggle_row(charset, "Force Caught", dbg_set_boolean_flag, g_debug_options.force_all_caught, &g_debug_options.force_all_caught), - define_toggle_row(charset, "Write Cbl Data", dbg_set_boolean_flag, g_debug_options.write_cable_data_to_save, &g_debug_options.write_cable_data_to_save), - define_toggle_row(charset, "Disp CtrlChr", dbg_set_boolean_flag, g_debug_options.display_control_char, &g_debug_options.display_control_char) + define_toggle_row(charset, "Print Link", dbg_set_byte_val, g_debug_options.print_link_data, &g_debug_options.print_link_data), + define_toggle_row(charset, "Instant Text", dbg_set_byte_val, g_debug_options.instant_text_speed, &g_debug_options.instant_text_speed), + define_toggle_row(charset, "Ign Cart", dbg_set_byte_val, g_debug_options.ignore_game_pak, &g_debug_options.ignore_game_pak), + define_toggle_row(charset, "Ign Sprites", dbg_set_byte_val, g_debug_options.ignore_game_pak_sprites, &g_debug_options.ignore_game_pak_sprites), + define_toggle_row(charset, "Ign Link", dbg_set_byte_val, g_debug_options.ignore_link_cable, &g_debug_options.ignore_link_cable), + define_toggle_row(charset, "Ign MG/E4", dbg_set_byte_val, g_debug_options.ignore_mg_e4_flags, &g_debug_options.ignore_mg_e4_flags), + define_toggle_row(charset, "Ign Unrec PKMN", dbg_set_byte_val, g_debug_options.ignore_unreceived_pkmn, &g_debug_options.ignore_unreceived_pkmn), + define_toggle_row(charset, "Force Tut", dbg_set_byte_val, g_debug_options.force_tutorial, &g_debug_options.force_tutorial), + define_toggle_row(charset, "Show Invalid", dbg_set_byte_val, g_debug_options.dont_hide_invalid_pkmn, &g_debug_options.dont_hide_invalid_pkmn), + define_toggle_row(charset, "Ign Dex Compl", dbg_set_byte_val, g_debug_options.ignore_dex_completion, &g_debug_options.ignore_dex_completion), + define_toggle_row(charset, "Force Caught", dbg_set_byte_val, g_debug_options.force_all_caught, &g_debug_options.force_all_caught), + define_debug_choice_row(charset, "Write Cbl Data", dbg_set_byte_val, cbl_data_options, CBL_DATA_OPTIONS_COUNT, false, g_debug_options.write_cable_data_to_save, &g_debug_options.write_cable_data_to_save), + define_debug_choice_row(charset, "Load Cbl Data", dbg_set_byte_val, cbl_data_options, CBL_DATA_OPTIONS_COUNT, false, g_debug_options.load_cable_data_from_save, &g_debug_options.load_cable_data_from_save), + define_toggle_row(charset, "Disp CtrlChr", dbg_set_byte_val, g_debug_options.display_control_char, &g_debug_options.display_control_char) }; menu.add_item_widgets(item_widgets, sizeof(item_widgets) / sizeof(item_widgets[0])); diff --git a/source/dbg/debug_menu_functions.cpp b/source/dbg/debug_menu_functions.cpp index cb00402..8fe7545 100644 --- a/source/dbg/debug_menu_functions.cpp +++ b/source/dbg/debug_menu_functions.cpp @@ -192,9 +192,9 @@ void show_debug_info_screen(void *context, unsigned user_param) } } -void dbg_set_boolean_flag(void *context, unsigned user_param) +void dbg_set_byte_val(void *context, unsigned user_param) { - bool *flag_ptr = (bool*)context; + u8 *flag_ptr = (u8*)context; *flag_ptr = (user_param != 0); } diff --git a/source/dbg/debug_mode.cpp b/source/dbg/debug_mode.cpp index e4826c0..784b050 100644 --- a/source/dbg/debug_mode.cpp +++ b/source/dbg/debug_mode.cpp @@ -13,6 +13,7 @@ debug_options g_debug_options = .dont_hide_invalid_pkmn = (false && DEBUG_MODE), .ignore_dex_completion = (false && DEBUG_MODE), .force_all_caught = (true && DEBUG_MODE), - .write_cable_data_to_save = (false && DEBUG_MODE), + .write_cable_data_to_save = (u8)WRITE_CABLE_DATA_MODE_OFF, + .load_cable_data_from_save = (u8)WRITE_CABLE_DATA_MODE_OFF, .display_control_char = (false && DEBUG_MODE) }; \ No newline at end of file diff --git a/source/link_handler.cpp b/source/link_handler.cpp index 25c0137..98d65d3 100644 --- a/source/link_handler.cpp +++ b/source/link_handler.cpp @@ -54,6 +54,8 @@ void linkCableIRQ() void LinkConnection::setup(const u16 *debug_charset) { + link_cable_memory_section_index = 0; + link_cable_array_index = 0; linkSPI->activate(LinkSPI::Mode::MASTER_256KBPS); linkSPI->setWaitModeActive(false); @@ -64,6 +66,12 @@ void LinkConnection::setup(const u16 *debug_charset) { create_textbox(0, 0, 138, 128, false); } + + if(g_debug_options.load_cable_data_from_save == WRITE_CABLE_DATA_MODE_CART) + { + // if we're loading the cable data from the cart save, we should make sure to load our first section here. + copy_save_to_ram(0x1000 * link_cable_memory_section_index, &global_memory_buffer[0], 0x1000); + } } void LinkConnection::startConnection(CompositeState startState) @@ -93,7 +101,40 @@ void LinkConnection::exchangeBytes() global_next_frame(); return --timeout_frames <= 0; }); */ - inData = linkSPI->transfer(outData); + switch(g_debug_options.load_cable_data_from_save) + { + case WRITE_CABLE_DATA_MODE_OFF: + // Normal transfer :-) + inData = linkSPI->transfer(outData); + break; + case WRITE_CABLE_DATA_MODE_SRAM: + // Pretend transfer, by loading the bytes from SRAM (where we stored them with writeData() in a previous transfer) + inData = (*(SRAM_PTR + link_cable_array_index)); + ++link_cable_array_index; + outData = (*(SRAM_PTR + link_cable_array_index)); + ++link_cable_array_index; + break; + case WRITE_CABLE_DATA_MODE_CART: + { + // Pretend transfer, by loading the bytes from the cartridge save. (where we stored them with writeData() in a previous transfer) + // skip the first 6 bytes, which are for human consumption + link_cable_array_index += 6; + + inData = global_memory_buffer[link_cable_array_index]; + ++link_cable_array_index; + outData = global_memory_buffer[link_cable_array_index]; + ++link_cable_array_index; + + // we reached the end of our global_memory_buffer, we need to load the next data section from the cart save + if(link_cable_array_index >= 0x1000) + { + ++link_cable_memory_section_index; + link_cable_array_index = 0; + copy_save_to_ram(0x1000 * link_cable_memory_section_index, &global_memory_buffer[0], 0x1000); + } + break; + } + } } void LinkConnection::printData() @@ -118,21 +159,50 @@ void LinkConnection::printData() void LinkConnection::writeData() { - global_memory_buffer[link_cable_array_index + 0] = compState; - global_memory_buffer[link_cable_array_index + 1] = (compStateCounter >> 8) & 0xFF; - global_memory_buffer[link_cable_array_index + 2] = (compStateCounter >> 0) & 0xFF; - global_memory_buffer[link_cable_array_index + 3] = subState; - global_memory_buffer[link_cable_array_index + 4] = (subStateCounter >> 8) & 0xFF; - global_memory_buffer[link_cable_array_index + 5] = (subStateCounter >> 0) & 0xFF; - global_memory_buffer[link_cable_array_index + 6] = inData; - global_memory_buffer[link_cable_array_index + 7] = outData; - link_cable_array_index += 8; - - if (link_cable_array_index >= 0x1000 || nextSubState == END) + switch(g_debug_options.write_cable_data_to_save) { - copy_ram_to_save(&global_memory_buffer[0], 0x1000 * link_cable_memory_section_index, 0x1000); - link_cable_memory_section_index++; - link_cable_array_index = link_cable_array_index % 0x1000; + case WRITE_CABLE_DATA_MODE_OFF: + break; + case WRITE_CABLE_DATA_MODE_SRAM: + { + (*(SRAM_PTR + link_cable_array_index)) = inData; + ++link_cable_array_index; + + (*(SRAM_PTR + link_cable_array_index)) = outData; + ++link_cable_array_index; + break; + } + case WRITE_CABLE_DATA_MODE_CART: + { + // save the data to the cartridge in chunks of 4 KB + // WARNING: If you want to add or remove fields here, + // make sure to keep the number of bytes a clean divider of 4096 (global_memory_buffer_size) + + // the next 6 bytes are for human consumption when viewed in a hex editor. + // they can be useful to correlate the current LinkConnection state with the data that was sent over the cable. + // but they're not needed for reconstructing the conversation with load_cable_data_from_save + global_memory_buffer[link_cable_array_index + 0] = compState; + global_memory_buffer[link_cable_array_index + 1] = (compStateCounter >> 8) & 0xFF; + global_memory_buffer[link_cable_array_index + 2] = (compStateCounter >> 0) & 0xFF; + global_memory_buffer[link_cable_array_index + 3] = subState; + global_memory_buffer[link_cable_array_index + 4] = (subStateCounter >> 8) & 0xFF; + global_memory_buffer[link_cable_array_index + 5] = (subStateCounter >> 0) & 0xFF; + + // actual data bytes start here. + global_memory_buffer[link_cable_array_index + 6] = inData; + global_memory_buffer[link_cable_array_index + 7] = outData; + + link_cable_array_index += 8; + + // If the buffer is full or we reached nextSubState == END, we save the buffer to the cartridge save + if (link_cable_array_index >= 0x1000 || nextSubState == END) + { + copy_ram_to_save(&global_memory_buffer[0], 0x1000 * link_cable_memory_section_index, 0x1000); + ++link_cable_memory_section_index; + link_cable_array_index = 0; + } + break; + } } } @@ -173,7 +243,7 @@ void LinkConnection::handleStateLogic() compState = nextCompState; } -int test_packet[] = { +static const int test_packet[] = { /* 1 preamble byte */ 0xFD, /* 1 command byte */ 0x02, /* 2 argument bytes */ 0x00, 0x00, @@ -182,13 +252,16 @@ int test_packet[] = { bool LinkConnection::earlyExit() { - if (g_debug_options.print_link_data && key_hit(KEY_LEFT)) + if (g_debug_options.print_link_data) { - globalLinkCable.paused = true; - } - else if (g_debug_options.print_link_data && key_hit(KEY_RIGHT)) - { - globalLinkCable.paused = false; + if(key_hit(KEY_LEFT)) + { + globalLinkCable.paused = true; + } + else if(key_hit(KEY_RIGHT)) + { + globalLinkCable.paused = false; + } } if (globalLinkCable.paused && g_debug_options.print_link_data) @@ -203,13 +276,14 @@ bool LinkConnection::earlyExit() void LinkConnection::logicState_initConnection() { + outData = 0x00; switch (subState) { case CLOCK: if (inData == 0xFE) { nextSubState = SAVE_SUCCESS; - outData = 0x00; + // outData defaults to 0x00 } else { @@ -223,10 +297,7 @@ void LinkConnection::logicState_initConnection() nextSubState = MENU_OPEN; outData = inData; } - else - { - outData = 0x00; - } + // outData defaults to 0x00 break; case MENU_OPEN: @@ -261,7 +332,7 @@ void LinkConnection::logicState_initConnection() { REG_TM3D = -0x0040; nextSubState = TRADE_PREAMBLE; - outData = 0x00; + // outData defaults to 0x00 } else { @@ -272,7 +343,7 @@ void LinkConnection::logicState_initConnection() case TRADE_PREAMBLE: if (subStateCounter < 2) { - outData = 0x00; + // outData defaults to 0x00 } else if (subStateCounter < 9) { @@ -312,7 +383,7 @@ void LinkConnection::logicState_initConnection() { nextSubState = GET_CHECKSUM; } - outData = 0x00; + // outData defaults to 0x00 break; case GET_CHECKSUM: @@ -340,7 +411,7 @@ void LinkConnection::logicState_initConnection() { nextSubState = SEND_SPECIFIC_PAYLOAD; } - outData = 0x00; + // outData defaults to 0x00 break; case SEND_SPECIFIC_PAYLOAD: @@ -391,17 +462,12 @@ void LinkConnection::LoadCurrGameFromChecksum() currROM = GB_ROM_ERROR; }; - int start = 0; - int end = 0; + int start = RED_JP_v0; + int end = GOLD_JP_v0; - if (gen == 1) + if (gen == 2) { - start = RED_JP_v0; - end = GOLD_JP_v0; - } - else if (gen == 2) - { - start = GOLD_JP_v0; + start = end; end = NO_GB_ROM; }