Make the debug menu functional!

This adds a lot of the debug_mode.h options as an option you can modify at runtime.

I also moved both the text debug screen and the regular debug info screen in there.

And I added an option to start song playback.
This commit is contained in:
Philippe Symons 2026-03-10 00:38:44 +01:00
parent 10350179db
commit 1590a1f108
9 changed files with 120 additions and 64 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 B

After

Width:  |  Height:  |  Size: 201 B

View File

@ -3,7 +3,7 @@
#include "vertical_menu.h"
typedef void (*on_execute_callback)(unsigned user_param);
typedef void (*on_execute_callback)(void *context, unsigned user_param);
/**
* @brief This struct represents the metadata associated to a single option of a single row in the debug menu.
@ -27,12 +27,13 @@ typedef struct debug_menu_row_data
const option_data *options;
on_execute_callback on_option_activate;
u8 selected_option_index;
bool should_delete_on_destruct;
bool should_free_on_destruct;
} option_section;
/**
* @brief A callback that you can set to execute when the user selects this row and presses the A button.
*/
on_execute_callback on_execute;
void *context;
unsigned user_param;
} debug_menu_row_data;

View File

@ -1,10 +1,9 @@
#ifndef _DEBUG_MENU_FUNCTIONS_H
#define _DEBUG_MENU_FUNCTIONS_H
void dbg_menu_print_number(unsigned user_param);
void show_text_debug_screen(unsigned user_param);
void show_debug_info_screen(unsigned user_param);
void show_text_debug_screen(void *context, unsigned user_param);
void show_debug_info_screen(void *context, unsigned user_param);
void dbg_set_boolean_flag(void *context, unsigned user_param);
void dbg_play_song(void *context, unsigned user_param);
#endif

View File

@ -55,6 +55,7 @@ extern debug_options g_debug_options;
// Compile time options
#define ENABLE_DEBUG_MENU true
#define ENABLE_TEXT_DEBUG_SCREEN true
#define USE_CUSTOM_MALLOC 1
// needs to be a value divisible by 4
#define CUSTOM_MALLOC_POOL_SIZE 8192

View File

@ -4,10 +4,13 @@
#include "sprite_data.h"
#include "background_engine.h"
#include "vertical_menu_cursor_handler.h"
#include "libraries/nanoprintf/nanoprintf.h"
#include <tonc.h>
#include <cstdlib>
static on_execute_callback delayed_execute_callback = nullptr;
static void *delayed_execute_context = nullptr;
static unsigned delayed_execute_user_param = 0;
void show_debug_menu()
@ -57,8 +60,9 @@ void show_debug_menu()
// to avoid rendering issues.
if(delayed_execute_callback)
{
delayed_execute_callback(delayed_execute_user_param);
delayed_execute_callback(delayed_execute_context, delayed_execute_user_param);
delayed_execute_callback = nullptr;
delayed_execute_context = nullptr;
delayed_execute_user_param = 0;
}
}
@ -70,6 +74,11 @@ debug_menu_row_widget::debug_menu_row_widget(const debug_menu_row_data &data)
debug_menu_row_widget::~debug_menu_row_widget()
{
if(data_.option_section.should_free_on_destruct)
{
free(const_cast<option_data*>(data_.option_section.options));
data_.option_section.options = nullptr;
}
}
void debug_menu_row_widget::render_item(text_data_table &text_table, unsigned x, unsigned y, bool is_focused)
@ -89,7 +98,16 @@ void debug_menu_row_widget::render_item(text_data_table &text_table, unsigned x,
const u8 selected_option_index = data_.option_section.selected_option_index;
tte_set_pos(x + option_margin_left, y + margin_top);
ptgb_write_debug(data_.charset, data_.option_section.options[selected_option_index].text, true);
if(data_.option_section.options[selected_option_index].text)
{
ptgb_write_debug(data_.charset, data_.option_section.options[selected_option_index].text, true);
}
else
{
char buf[32];
npf_snprintf(buf, sizeof(buf), "%u", data_.option_section.options[selected_option_index].value);
ptgb_write_debug(data_.charset, buf, true);
}
}
if(is_focused)
@ -135,7 +153,7 @@ MenuInputHandleState debug_menu_row_widget::handle_input()
if(handled && data_.option_section.on_option_activate)
{
data_.option_section.on_option_activate(data_.option_section.options[data_.option_section.selected_option_index].value);
data_.option_section.on_option_activate(data_.context, data_.option_section.options[data_.option_section.selected_option_index].value);
}
if(handled)
@ -147,6 +165,7 @@ MenuInputHandleState debug_menu_row_widget::handle_input()
if(key_hit(KEY_A) && data_.on_execute)
{
delayed_execute_callback = data_.on_execute;
delayed_execute_context = data_.context;
delayed_execute_user_param = data_.user_param;
return MenuInputHandleState::CHOICE_MADE;
}

View File

@ -1,8 +1,12 @@
#include "dbg/debug_menu.h"
#include "dbg/debug_menu_entries.h"
#include "dbg/debug_menu_functions.h"
#include "dbg/debug_mode.h"
#include "soundbank.h"
#include "vertical_menu.h"
#include <cstdlib>
static const option_data toggle_options[2] = {
{
.text = "Off",
@ -14,25 +18,10 @@ static const option_data toggle_options[2] = {
}
};
static const option_data custom_options[] = {
{
.text = "One",
.value = 1
},
{
.text = "Two",
.value = 2
},
{
.text = "Three",
.value = 3
}
};
/**
* @brief This helper function makes it easy to define a multiple choice debug row.
*/
static debug_menu_row_widget define_debug_choice_row(const u16 *charset, const char *labelText, on_execute_callback option_activate_callback, const option_data *options_metadata, u8 num_options, bool should_delete_options, u8 default_option_index)
static debug_menu_row_widget __attribute__((noinline)) define_debug_choice_row(const u16 *charset, const char *labelText, on_execute_callback option_activate_callback, const option_data *options_metadata, u8 num_options, bool should_delete_options, u8 default_option_index, void *context)
{
const debug_menu_row_data row_data = {
.charset = charset,
@ -42,9 +31,10 @@ static debug_menu_row_widget define_debug_choice_row(const u16 *charset, const c
.options = options_metadata,
.on_option_activate = option_activate_callback,
.selected_option_index = default_option_index,
.should_delete_on_destruct = should_delete_options
.should_free_on_destruct = should_delete_options
},
.on_execute = nullptr,
.context = context,
.user_param = 0
};
return debug_menu_row_widget(row_data);
@ -53,15 +43,34 @@ static debug_menu_row_widget define_debug_choice_row(const u16 *charset, const c
/**
* @brief This helper function makes it easy to define a toggle row.
*/
static debug_menu_row_widget define_toggle_row(const u16 *charset, const char* labelText, on_execute_callback on_toggle_callback, bool default_value)
static debug_menu_row_widget define_toggle_row(const u16 *charset, const char* labelText, on_execute_callback on_toggle_callback, bool default_value, void *context)
{
return define_debug_choice_row(charset, labelText, on_toggle_callback, toggle_options, 2, false, default_value ? 1 : 0);
return define_debug_choice_row(charset, labelText, on_toggle_callback, toggle_options, 2, false, default_value ? 1 : 0, context);
}
static debug_menu_row_widget __attribute__((noinline)) define_song_row(const u16 *charset, const char* labelText)
{
option_data *song_options = (option_data*)malloc(sizeof(option_data) * (MSL_NSONGS + 1));
song_options[0] = {
.text = "None",
.value = UINT32_MAX
};
for(unsigned i = 0; i < MSL_NSONGS; ++i)
{
song_options[i + 1] = {
.text = nullptr,
.value = i
};
}
return define_debug_choice_row(charset, labelText, dbg_play_song, song_options, MSL_NSONGS + 1, true, 0, nullptr);
}
/**
* @brief This helper function makes it easy to define a row that executes a function when you select it and press A.
*/
static debug_menu_row_widget define_executable_row(const u16 *charset, const char* labelText, on_execute_callback on_execute, unsigned user_param)
static debug_menu_row_widget __attribute__((noinline)) define_executable_row(const u16 *charset, const char* labelText, on_execute_callback on_execute, unsigned user_param, void *context)
{
const debug_menu_row_data row_data = {
.charset = charset,
@ -71,9 +80,10 @@ static debug_menu_row_widget define_executable_row(const u16 *charset, const cha
.options = nullptr,
.on_option_activate = nullptr,
.selected_option_index = 0,
.should_delete_on_destruct = false
.should_free_on_destruct = false
},
.on_execute = on_execute,
.context = context,
.user_param = user_param
};
return debug_menu_row_widget(row_data);
@ -81,18 +91,44 @@ static debug_menu_row_widget define_executable_row(const u16 *charset, const cha
void fill_debug_menu_with_entries(vertical_menu &menu, u16 *charset)
{
debug_menu_row_widget row_widget_default_off = define_toggle_row(charset, "Def_Off", dbg_menu_print_number, false);
debug_menu_row_widget row_widget_default_on = define_toggle_row(charset, "Def_On", dbg_menu_print_number, true);
debug_menu_row_widget row_widget_custom_options = define_debug_choice_row(charset, "Custom", dbg_menu_print_number, custom_options, 3, false, 1);
debug_menu_row_widget txt_dbg_screen = define_executable_row(charset, "Text Debug Screen", show_text_debug_screen, 0);
debug_menu_row_widget dbg_info_screen = define_executable_row(charset, "Debug Info Screen", show_debug_info_screen, 0);
#if ENABLE_TEXT_DEBUG_SCREEN
debug_menu_row_widget txt_dbg_screen = define_executable_row(charset, "Text Debug", show_text_debug_screen, 0, nullptr);
#endif
debug_menu_row_widget dbg_info_screen = define_executable_row(charset, "Info", show_debug_info_screen, 0, nullptr);
debug_menu_row_widget row_play_song = define_song_row(charset, "Song");
debug_menu_row_widget row_print_link_data = define_toggle_row(charset, "Print Link", dbg_set_boolean_flag, g_debug_options.print_link_data, &g_debug_options.print_link_data);
debug_menu_row_widget row_instant_text_speed = define_toggle_row(charset, "Instant Text", dbg_set_boolean_flag, g_debug_options.instant_text_speed, &g_debug_options.instant_text_speed);
debug_menu_row_widget row_ignore_game_pak = define_toggle_row(charset, "Ign Cart", dbg_set_boolean_flag, g_debug_options.ignore_game_pak, &g_debug_options.ignore_game_pak);
debug_menu_row_widget row_ignore_game_pak_sprites = define_toggle_row(charset, "Ign Sprites", dbg_set_boolean_flag, g_debug_options.ignore_game_pak_sprites, &g_debug_options.ignore_game_pak_sprites);
debug_menu_row_widget row_ignore_link_cable = define_toggle_row(charset, "Ign Link", dbg_set_boolean_flag, g_debug_options.ignore_link_cable, &g_debug_options.ignore_link_cable);
debug_menu_row_widget row_ignore_mg_e4_flags = 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);
debug_menu_row_widget row_ignore_unreceived_pkmn = define_toggle_row(charset, "Ign Unrec PKMN", dbg_set_boolean_flag, g_debug_options.ignore_unreceived_pkmn, &g_debug_options.ignore_unreceived_pkmn);
debug_menu_row_widget row_force_tutorial = define_toggle_row(charset, "Force Tut", dbg_set_boolean_flag, g_debug_options.force_tutorial, &g_debug_options.force_tutorial);
debug_menu_row_widget row_dont_hide_invalid_pkmn = define_toggle_row(charset, "Show Invalid", dbg_set_boolean_flag, g_debug_options.dont_hide_invalid_pkmn, &g_debug_options.dont_hide_invalid_pkmn);
debug_menu_row_widget row_ignore_dex_completion = define_toggle_row(charset, "Ign Dex Compl", dbg_set_boolean_flag, g_debug_options.ignore_dex_completion, &g_debug_options.ignore_dex_completion);
debug_menu_row_widget row_force_all_caught = define_toggle_row(charset, "Force Caught", dbg_set_boolean_flag, g_debug_options.force_all_caught, &g_debug_options.force_all_caught);
debug_menu_row_widget row_write_cable_data_to_save = 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);
debug_menu_row_widget row_display_control_char = define_toggle_row(charset, "Disp CtrlChr", dbg_set_boolean_flag, g_debug_options.display_control_char, &g_debug_options.display_control_char);
i_item_widget* item_widgets[] = {
&row_widget_default_off,
&row_widget_default_on,
&row_widget_custom_options,
#if ENABLE_TEXT_DEBUG_SCREEN
&txt_dbg_screen,
&dbg_info_screen
#endif
&dbg_info_screen,
&row_play_song,
&row_print_link_data,
&row_instant_text_speed,
&row_ignore_game_pak,
&row_ignore_game_pak_sprites,
&row_ignore_link_cable,
&row_ignore_mg_e4_flags,
&row_ignore_unreceived_pkmn,
&row_force_tutorial,
&row_dont_hide_invalid_pkmn,
&row_ignore_dex_completion,
&row_force_all_caught,
&row_write_cable_data_to_save,
&row_display_control_char
};
menu.add_item_widgets(item_widgets, sizeof(item_widgets) / sizeof(item_widgets[0]));

View File

@ -8,21 +8,11 @@
#include "save_data_manager.h"
#include "background_engine.h"
#include "libstd_replacements.h"
#include "sound.h"
void dbg_menu_print_number(unsigned user_param)
{
char buf[64];
npf_snprintf(buf, sizeof(buf), "%u", user_param);
uint16_t charset[256];
load_localized_charset(charset, 3, ENGLISH);
tte_erase_rect(0, 0, 100, 12);
tte_set_pos(0, 0);
ptgb_write_debug(charset, buf, true);
}
void show_text_debug_screen(unsigned user_param)
void show_text_debug_screen(void *context, unsigned user_param)
{
(void)context;
(void)user_param;
tte_set_ink(INK_DARK_GREY);
@ -31,8 +21,11 @@ void show_text_debug_screen(unsigned user_param)
text_loop(SCRIPT_DEBUG);
}
void show_debug_info_screen(unsigned user_param)
void show_debug_info_screen(void *context, unsigned user_param)
{
(void)context;
(void)user_param;
char hexBuffer[16];
uint16_t charset[256];
load_localized_charset(charset, 3, ENGLISH);
@ -107,4 +100,20 @@ void show_debug_info_screen(unsigned user_param)
}
global_next_frame();
}
}
void dbg_set_boolean_flag(void *context, unsigned user_param)
{
bool *flag_ptr = (bool*)context;
*flag_ptr = (user_param != 0);
}
void dbg_play_song(void *context, unsigned user_param)
{
if(user_param == UINT32_MAX)
{
stop_song();
return;
}
play_song(user_param, true);
}

View File

@ -16,8 +16,6 @@
#include "button_menu.h"
#include "dbg/debug_mode.h"
#include "dbg/debug_menu.h"
// #include "soundbank.h"
// #include "soundbank_bin.h"
#include "dex_handler.h"
#include "pokedex.h"
#include "global_frame_controller.h"
@ -222,7 +220,6 @@ int main_menu_loop()
general_text.decompress(get_compressed_text_table(GENERAL_INDEX));
play_song(MOD_MAIN_MENU, true);
while (true)
{
if (update)

View File

@ -144,7 +144,6 @@ MenuInputHandleState vertical_menu::handle_input()
{
MenuInputHandleState result;
bool did_navigate = false;
bool viewport_updated = false;
// If no items or not focused, there's nothing to handle.
if(items_.size() <= 1 || !is_focused_)
@ -182,7 +181,6 @@ MenuInputHandleState vertical_menu::handle_input()
if(focused_index_ >= current_viewport_end_index)
{
++viewport_start_index_;
viewport_updated = true;
}
did_navigate = true;
}
@ -192,7 +190,6 @@ MenuInputHandleState vertical_menu::handle_input()
if(focused_index_ < viewport_start_index_)
{
--viewport_start_index_;
viewport_updated = true;
}
did_navigate = true;
}
@ -201,10 +198,7 @@ MenuInputHandleState vertical_menu::handle_input()
if(did_navigate)
{
const unsigned render_index = focused_index_ - viewport_start_index_;
if(viewport_updated)
{
update_viewport();
}
update_viewport();
handle_selection_change(focused_index_, settings_.x, settings_.y + settings_.margin_top + (render_index * settings_.item_height));
return MenuInputHandleState::HANDLED;
}