diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b7da2151..7f88be12 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -113,7 +113,7 @@ jobs: - uses: actions/checkout@v4 - name: Run Doxygen - uses: mattnotmitt/doxygen-action@1.9.5 + uses: mattnotmitt/doxygen-action@v1 with: doxyfile-path: './Doxyfile' diff --git a/README.md b/README.md index de3d030c..8fae66a3 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ An open source menu for N64 flashcarts. * Real Time Clock support. * Music playback (MP3). * Menu sound effects. +* N64 ROM autoload. ## Documentation @@ -49,6 +50,9 @@ An open source menu for N64 flashcarts. ## Experimental features These features are subject to change: +### N64 ROM autoload +To use the autoload function, while on the `N64 ROM information` display, press the `R` button on your joypad and select the `Set ROM to autoload` option. When you restart the console, it will now only load the selected ROM rather than the menu. +NOTE: to return to the menu, hold joypad `start` button whilst powering on the console. ### GamePak sprites To use N64 `GamePak` sprites, place `PNG` files within the `sd:/menu/boxart/` folder. @@ -60,7 +64,7 @@ These must be `PNG` files that use the following dimensions: * Japanese N64 GamePak boxart sprites: 112x158 * 64DD boxart sprites: 129x112 -They will be loaded by directories using each character of the full 4 character Game Code (as identified in the menus ROM information). +They will be loaded by directories using each character (case-sensitive) of the full 4 character Game Code (as identified in the menu ROM information). i.e. for GoldenEye NTSC USA (NGEE), this would be `sd:/menu/boxart/N/G/E/E/boxart_front.png`. i.e. for GoldenEye PAL (NGEP), this would be `sd:/menu/boxart/N/G/E/P/boxart_front.png`. diff --git a/libdragon b/libdragon index 9dd99415..e93802ae 160000 --- a/libdragon +++ b/libdragon @@ -1 +1 @@ -Subproject commit 9dd994151ae3f3709f1f80224e6b654aac8be6b4 +Subproject commit e93802aec7e7839710281824ce2e9e4689b3a01d diff --git a/src/menu/components/common.c b/src/menu/components/common.c index d0fce75f..29a97ada 100644 --- a/src/menu/components/common.c +++ b/src/menu/components/common.c @@ -151,7 +151,7 @@ void component_main_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *f .height = LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT - (TEXT_MARGIN_VERTICAL * 2), .align = align, .valign = valign, - .wrap = WRAP_ELLIPSES, + .wrap = WRAP_WORD, .line_spacing = TEXT_LINE_SPACING_ADJUST, }, FNT_DEFAULT, diff --git a/src/menu/menu_state.h b/src/menu/menu_state.h index ba6732b2..e6999982 100644 --- a/src/menu/menu_state.h +++ b/src/menu/menu_state.h @@ -105,6 +105,12 @@ typedef struct { path_t *disk_path; disk_info_t disk_info; } load; + + struct { + bool rom_file; + bool disk_file; + bool emulator_file; + } boot_pending; } menu_t; diff --git a/src/menu/settings.c b/src/menu/settings.c index 0b372531..5361f0a6 100644 --- a/src/menu/settings.c +++ b/src/menu/settings.c @@ -14,7 +14,10 @@ static settings_t init = { .default_directory = "/", .use_saves_folder = true, .sound_enabled = true, - + .rom_autoload_enabled = false, + .rom_autoload_path = "", + .rom_autoload_filename = "", + /* Beta feature flags (should always init to off) */ .bgm_enabled = false, .rumble_enabled = false, @@ -41,6 +44,10 @@ void settings_load (settings_t *settings) { settings->use_saves_folder = mini_get_bool(ini, "menu", "use_saves_folder", init.use_saves_folder); settings->sound_enabled = mini_get_bool(ini, "menu", "sound_enabled", init.sound_enabled); + settings->rom_autoload_enabled = mini_get_bool(ini, "menu", "autoload_rom_enabled", init.rom_autoload_enabled); + settings->rom_autoload_path = strdup(mini_get_string(ini, "autoload", "rom_path", init.rom_autoload_path)); + settings->rom_autoload_filename = strdup(mini_get_string(ini, "autoload", "rom_filename", init.rom_autoload_filename)); + /* Beta feature flags, they might not be in the file */ settings->bgm_enabled = mini_get_bool(ini, "menu_beta_flag", "bgm_enabled", init.bgm_enabled); settings->rumble_enabled = mini_get_bool(ini, "menu_beta_flag", "rumble_enabled", init.rumble_enabled); @@ -56,6 +63,9 @@ void settings_save (settings_t *settings) { mini_set_string(ini, "menu", "default_directory", settings->default_directory); mini_set_bool(ini, "menu", "use_saves_folder", settings->use_saves_folder); mini_set_bool(ini, "menu", "sound_enabled", settings->sound_enabled); + mini_set_bool(ini, "menu", "autoload_rom_enabled", settings->rom_autoload_enabled); + mini_set_string(ini, "autoload", "rom_path", settings->rom_autoload_path); + mini_set_string(ini, "autoload", "rom_filename", settings->rom_autoload_filename); /* Beta feature flags, they should not save until production ready! */ // mini_set_bool(ini, "menu_beta_flag", "bgm_enabled", settings->bgm_enabled); diff --git a/src/menu/settings.h b/src/menu/settings.h index e9931b05..471879eb 100644 --- a/src/menu/settings.h +++ b/src/menu/settings.h @@ -30,6 +30,16 @@ typedef struct { /** @brief Enable rumble feedback */ bool rumble_enabled; + + /** @brief Enable the ability to bypass the menu and instantly load a ROM */ + bool rom_autoload_enabled; + + /** @brief A path to the autoloaded ROM */ + char *rom_autoload_path; + + /** @brief A filename of the autoloaded ROM */ + char *rom_autoload_filename; + } settings_t; @@ -40,5 +50,4 @@ void settings_load (settings_t *settings); /** @brief The settings to save */ void settings_save (settings_t *settings); - #endif diff --git a/src/menu/views/load_disk.c b/src/menu/views/load_disk.c index be95010a..781ebe27 100644 --- a/src/menu/views/load_disk.c +++ b/src/menu/views/load_disk.c @@ -5,8 +5,7 @@ #include "views.h" -static bool load_pending; -static bool load_rom; +static bool load_disk_with_rom; static component_boxart_t *boxart; @@ -31,11 +30,11 @@ static char *format_disk_region (disk_region_t region) { static void process (menu_t *menu) { if (menu->actions.enter) { - load_pending = true; - load_rom = false; + menu->boot_pending.disk_file = true; + load_disk_with_rom = false; } else if (menu->actions.options && menu->load.rom_path) { - load_pending = true; - load_rom = true; + menu->boot_pending.disk_file = true; + load_disk_with_rom = true; sound_play_effect(SFX_SETTING); } else if (menu->actions.back) { sound_play_effect(SFX_EXIT); @@ -48,7 +47,7 @@ static void draw (menu_t *menu, surface_t *d) { component_background_draw(); - if (load_pending) { + if (menu->boot_pending.disk_file) { component_loader_draw(0.0f); } else { component_layout_draw(); @@ -119,7 +118,7 @@ static void draw_progress (float progress) { static void load (menu_t *menu) { cart_load_err_t err; - if (menu->load.rom_path && load_rom) { + if (menu->load.rom_path && load_disk_with_rom) { err = cart_load_n64_rom_and_save(menu, draw_progress); if (err != CART_LOAD_OK) { menu_show_error(menu, cart_load_convert_error_message(err)); @@ -135,7 +134,7 @@ static void load (menu_t *menu) { menu->next_mode = MENU_MODE_BOOT; - if (load_rom) { + if (load_disk_with_rom) { menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM; menu->boot_params->detect_cic_seed = rom_info_get_cic_seed(&menu->load.rom_info, &menu->boot_params->cic_seed); switch (rom_info_get_tv_type(&menu->load.rom_info)) { @@ -163,7 +162,7 @@ void view_load_disk_init (menu_t *menu) { menu->load.disk_path = NULL; } - load_pending = false; + menu->boot_pending.disk_file = false; menu->load.disk_path = path_clone_push(menu->browser.directory, menu->browser.entry->name); @@ -180,8 +179,8 @@ void view_load_disk_display (menu_t *menu, surface_t *display) { draw(menu, display); - if (load_pending) { - load_pending = false; + if (menu->boot_pending.disk_file) { + menu->boot_pending.disk_file = false; load(menu); } diff --git a/src/menu/views/load_emulator.c b/src/menu/views/load_emulator.c index 9cfaeb48..3a131722 100644 --- a/src/menu/views/load_emulator.c +++ b/src/menu/views/load_emulator.c @@ -11,7 +11,6 @@ static const char *emu_gameboy_rom_extensions[] = { "gb", NULL }; static const char *emu_gameboy_color_rom_extensions[] = { "gbc", NULL }; static const char *emu_sega_8bit_rom_extensions[] = { "sms", "gg", "sg", NULL }; -static bool load_pending; static cart_load_emu_type_t emu_type; static char *format_emulator_name (cart_load_emu_type_t emulator_info) { @@ -34,7 +33,7 @@ static char *format_emulator_name (cart_load_emu_type_t emulator_info) { static void process (menu_t *menu) { if (menu->actions.enter) { - load_pending = true; + menu->boot_pending.emulator_file = true; } else if (menu->actions.back) { sound_play_effect(SFX_EXIT); menu->next_mode = MENU_MODE_BROWSER; @@ -46,7 +45,7 @@ static void draw (menu_t *menu, surface_t *d) { component_background_draw(); - if (load_pending) { + if (menu->boot_pending.emulator_file) { component_loader_draw(0.0f); } else { component_layout_draw(); @@ -107,7 +106,7 @@ static void load (menu_t *menu) { void view_load_emulator_init (menu_t *menu) { - load_pending = false; + menu->boot_pending.emulator_file = false; path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); @@ -133,8 +132,8 @@ void view_load_emulator_display (menu_t *menu, surface_t *display) { draw(menu, display); - if (load_pending) { - load_pending = false; + if (menu->boot_pending.emulator_file) { + menu->boot_pending.emulator_file = false; load(menu); } } diff --git a/src/menu/views/load_rom.c b/src/menu/views/load_rom.c index 18acd7e6..2a4ce34d 100644 --- a/src/menu/views/load_rom.c +++ b/src/menu/views/load_rom.c @@ -3,12 +3,12 @@ #include "boot/boot.h" #include "../sound.h" #include "views.h" +#include +#include "utils/fs.h" static bool show_extra_info_message = false; -static bool load_pending; static component_boxart_t *boxart; - static char *convert_error_message (rom_err_t err) { switch (err) { case ROM_ERR_LOAD_IO: return "I/O error during loading ROM information and/or options"; @@ -145,6 +145,17 @@ static void set_tv_type (menu_t *menu, void *arg) { menu->browser.reload = true; } +static void set_autoload_type (menu_t *menu, void *arg) { + free(menu->settings.rom_autoload_path); + menu->settings.rom_autoload_path = strdup(strip_fs_prefix(path_get(menu->browser.directory))); + free(menu->settings.rom_autoload_filename); + menu->settings.rom_autoload_filename = strdup(menu->browser.entry->name); + // FIXME: add a confirmation box here! (press start on reboot) + menu->settings.rom_autoload_enabled = true; + settings_save(&menu->settings); + menu->browser.reload = true; +} + static component_context_menu_t set_cic_type_context_menu = { .list = { {.text = "Automatic", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_AUTOMATIC) }, {.text = "CIC-6101", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_6101) }, @@ -187,6 +198,7 @@ static component_context_menu_t options_context_menu = { .list = { { .text = "Set CIC Type", .submenu = &set_cic_type_context_menu }, { .text = "Set Save Type", .submenu = &set_save_type_context_menu }, { .text = "Set TV Type", .submenu = &set_tv_type_context_menu }, + { .text = "Set ROM to autoload", .action = set_autoload_type }, COMPONENT_CONTEXT_MENU_LIST_END, }}; @@ -196,7 +208,7 @@ static void process (menu_t *menu) { } if (menu->actions.enter) { - load_pending = true; + menu->boot_pending.rom_file = true; } else if (menu->actions.back) { sound_play_effect(SFX_EXIT); menu->next_mode = MENU_MODE_BROWSER; @@ -218,7 +230,7 @@ static void draw (menu_t *menu, surface_t *d) { component_background_draw(); - if (load_pending) { + if (menu->boot_pending.rom_file) { component_loader_draw(0.0f); } else { component_layout_draw(); @@ -342,14 +354,14 @@ static void deinit (void) { void view_load_rom_init (menu_t *menu) { - load_pending = false; + if (!menu->settings.rom_autoload_enabled) { + if (menu->load.rom_path) { + path_free(menu->load.rom_path); + } - if (menu->load.rom_path) { - path_free(menu->load.rom_path); + menu->load.rom_path = path_clone_push(menu->browser.directory, menu->browser.entry->name); } - menu->load.rom_path = path_clone_push(menu->browser.directory, menu->browser.entry->name); - rom_err_t err = rom_info_load(menu->load.rom_path, &menu->load.rom_info); if (err != ROM_OK) { path_free(menu->load.rom_path); @@ -358,9 +370,10 @@ void view_load_rom_init (menu_t *menu) { return; } - boxart = component_boxart_init(menu->storage_prefix, menu->load.rom_info.game_code, IMAGE_BOXART_FRONT); - - component_context_menu_init(&options_context_menu); + if (!menu->settings.rom_autoload_enabled) { + boxart = component_boxart_init(menu->storage_prefix, menu->load.rom_info.game_code, IMAGE_BOXART_FRONT); + component_context_menu_init(&options_context_menu); + } } void view_load_rom_display (menu_t *menu, surface_t *display) { @@ -368,8 +381,8 @@ void view_load_rom_display (menu_t *menu, surface_t *display) { draw(menu, display); - if (load_pending) { - load_pending = false; + if (menu->boot_pending.rom_file) { + menu->boot_pending.rom_file = false; load(menu); } diff --git a/src/menu/views/settings_editor.c b/src/menu/views/settings_editor.c index 41dfa0a9..068b4675 100644 --- a/src/menu/views/settings_editor.c +++ b/src/menu/views/settings_editor.c @@ -136,6 +136,7 @@ static void draw (menu_t *menu, surface_t *d) { ALIGN_LEFT, VALIGN_TOP, "\n\n" " Default Directory : %s\n\n" + " Autoload ROM : %s\n" "To change the following menu settings, press 'A':\n" "* PAL60 Mode : %s\n" " Show Hidden Files : %s\n" @@ -145,9 +146,11 @@ static void draw (menu_t *menu, surface_t *d) { " Background Music : %s\n" " Rumble Feedback : %s\n" #endif - "Note: Certain settings have the following caveats:\n\n" - "* Requires a flashcart reboot.\n", + "\n\n" + "Note: Certain settings have the following caveats:\n" + "* Requires rebooting the N64 Console.\n", menu->settings.default_directory, + format_switch(menu->settings.rom_autoload_enabled), format_switch(menu->settings.pal60_enabled), format_switch(menu->settings.show_protected_entries), format_switch(menu->settings.use_saves_folder), diff --git a/src/menu/views/startup.c b/src/menu/views/startup.c index 118d20dc..c12a9045 100644 --- a/src/menu/views/startup.c +++ b/src/menu/views/startup.c @@ -9,6 +9,27 @@ static void draw (menu_t *menu, surface_t *d) { void view_startup_init (menu_t *menu) { + // FIXME: rather than use a controller button, would it be better to use the cart button? + JOYPAD_PORT_FOREACH (port) { + joypad_poll(); + joypad_buttons_t b_held = joypad_get_buttons_held(port); + + if (menu->settings.rom_autoload_enabled && b_held.start) { + menu->settings.rom_autoload_enabled = false; + menu->settings.rom_autoload_path = ""; + menu->settings.rom_autoload_filename = ""; + settings_save(&menu->settings); + } + } + if (menu->settings.rom_autoload_enabled) { + menu->browser.directory = path_init(menu->storage_prefix, menu->settings.rom_autoload_path); + menu->load.rom_path = path_clone_push(menu->browser.directory, menu->settings.rom_autoload_filename); + menu->boot_pending.rom_file = true; + menu->next_mode = MENU_MODE_LOAD_ROM; + + return; + } + menu->next_mode = MENU_MODE_BROWSER; }