diff --git a/include/arm11/menu/menu_fb3ds.h b/include/arm11/menu/menu_fb3ds.h index ef9f042..c326a03 100644 --- a/include/arm11/menu/menu_fb3ds.h +++ b/include/arm11/menu/menu_fb3ds.h @@ -25,9 +25,10 @@ #define SUBENTRY_SLOT_BOOT(x) \ { "Boot [slot " #x "]", DESC_BOOT_SLOT(x), &menuLaunchFirm, (x-1) } - -#define SUBENTRY_SLOT_SETUP(x, m) \ - { "Setup [slot " #x "]...", DESC_SLOT_SETUP(x), NULL, (x+m-1) } + +#define SUBMENU_SLOT_SETUP_M 7 +#define SUBENTRY_SLOT_SETUP(x) \ + { "Setup [slot " #x "]...", DESC_SLOT_SETUP(x), NULL, (x+SUBMENU_SLOT_SETUP_M-1) } #define SUBMENU_SLOT_SETUP(x) \ { \ @@ -61,6 +62,10 @@ #define DESC_BOOT_QUIET "In quiet boot mode, splash is not displayed and the boot is continued via the first available autoboot slot.\n \n! To enter the menu, hold the HOME button at boot !" #define DESC_CHANGE_BOOT "Change fastboot3ds boot mode. This allows you to set up how your console boots." +#define DESC_SPLASH_CUSTOM "Select a custom splash. This is compatible with Luma 3DS format splash screens." +#define DESC_SPLASH_DEFAULT "Use default fastboot3DS splash screen." +#define DESC_CHANGE_SPLASH "Change fastboot3ds splash screen. Will only be display in normal and quick boot modes." + #define DESC_NAND_BACKUP "Backup current NAND to a file." #define DESC_NAND_RESTORE "Restore current NAND from a file.\nThis option preserves your fastboot3ds installation." #define DESC_NAND_RESTORE_F "Restore current NAND from a file.\nWARNING: This will overwrite all of your flash memory, also overwriting fastboot3ds." @@ -88,9 +93,9 @@ { "Boot menu...", DESC_BOOT_MENU, NULL, 1 }, { "Boot setup...", DESC_BOOT_SETUP, NULL, 2 }, { "Boot from file...", DESC_BOOT_FILE, &menuLaunchFirm, 0xFF }, - { "NAND tools...", DESC_NAND_TOOLS, NULL, 4 }, - { "Miscellaneous...", DESC_MISC, NULL, 5 }, - { "Debug...", LOREM, NULL, 9 } + { "NAND tools...", DESC_NAND_TOOLS, NULL, 5 }, + { "Miscellaneous...", DESC_MISC, NULL, 6 }, + { "Debug...", LOREM, NULL, 13 } } }, { // 1 @@ -105,15 +110,16 @@ } }, { // 2 - "Boot Setup", N_BOOTSLOTS + 1, &menuPresetBootConfig, MENU_FLAG_SLOTS | MENU_FLAG_BOOTMODE | MENU_FLAG_CONFIG, + "Boot Setup", N_BOOTSLOTS + 2, &menuPresetBootConfig, MENU_FLAG_SLOTS | MENU_FLAG_BOOTMODE | MENU_FLAG_CONFIG, { - SUBENTRY_SLOT_SETUP(1,6), - SUBENTRY_SLOT_SETUP(2,6), - SUBENTRY_SLOT_SETUP(3,6), - SUBENTRY_SLOT_SETUP(4,6), - SUBENTRY_SLOT_SETUP(5,6), - SUBENTRY_SLOT_SETUP(6,6), - { "Change boot mode...", DESC_CHANGE_BOOT, NULL, 3 } + SUBENTRY_SLOT_SETUP(1), + SUBENTRY_SLOT_SETUP(2), + SUBENTRY_SLOT_SETUP(3), + SUBENTRY_SLOT_SETUP(4), + SUBENTRY_SLOT_SETUP(5), + SUBENTRY_SLOT_SETUP(6), + { "Change boot mode...", DESC_CHANGE_BOOT, NULL, 3 }, + { "Change splash...", DESC_CHANGE_SPLASH, NULL, 4 } } }, { // 3 @@ -125,6 +131,13 @@ } }, { // 4 + "Splash Screen Setup", 2, &menuPresetSplashConfig, MENU_FLAG_CONFIG, + { + { "Use custom splash...", DESC_SPLASH_CUSTOM, &menuSetSplash, 1 }, + { "Use default splash", DESC_SPLASH_DEFAULT, &menuSetSplash, 0 } + } + }, + { // 5 "NAND Tools", 4, &menuPresetNandTools, 0, { { "Backup NAND", DESC_NAND_BACKUP, &menuBackupNand, 0 }, @@ -133,20 +146,20 @@ { "Flash firmware to FIRM1", DESC_FIRM_FLASH, &menuInstallFirm, 1 } } }, - { // 5 + { // 6 "Miscellaneous", 2, NULL, 0, { { "Update fastboot3DS", DESC_UPDATE, &menuUpdateFastboot3ds, 0 }, { "Credits", DESC_CREDITS, &menuShowCredits, 0 } } }, - SUBMENU_SLOT_SETUP(1), // 6 - SUBMENU_SLOT_SETUP(2), // 7 - SUBMENU_SLOT_SETUP(3), // 8 - SUBMENU_SLOT_SETUP(4), // 9 - SUBMENU_SLOT_SETUP(5), // 10 - SUBMENU_SLOT_SETUP(6), // 11 - /*{ // 12 + SUBMENU_SLOT_SETUP(1), // 7 + SUBMENU_SLOT_SETUP(2), // 8 + SUBMENU_SLOT_SETUP(3), // 9 + SUBMENU_SLOT_SETUP(4), // 10 + SUBMENU_SLOT_SETUP(5), // 11 + SUBMENU_SLOT_SETUP(6), // 12 + /*{ // 13 "Debug", 2, NULL, 0, // this will not show in the release version { { "View current settings", LOREM, &debugSettingsView, 0 }, diff --git a/include/arm11/menu/menu_fsel.h b/include/arm11/menu/menu_fsel.h index 62060f8..910cc9f 100644 --- a/include/arm11/menu/menu_fsel.h +++ b/include/arm11/menu/menu_fsel.h @@ -26,4 +26,4 @@ #define BRWS_OFFSET_BUTTONS 19 #define BRWS_WIDTH 40 -bool menuFileSelector(char* res_path, PrintConsole* menu_con, const char* start, const char* pattern, bool allow_root); +bool menuFileSelector(char* res_path, PrintConsole* menu_con, const char* start, const char* pattern, bool allow_root, bool select_dirs); diff --git a/include/arm11/menu/menu_func.h b/include/arm11/menu/menu_func.h index cf7512c..8d6e6e4 100644 --- a/include/arm11/menu/menu_func.h +++ b/include/arm11/menu/menu_func.h @@ -42,9 +42,11 @@ u32 menuPresetSlotConfig8(void); u32 menuPresetSlotConfig9(void); u32 menuPresetBootMode(void); +u32 menuPresetSplashConfig(void); u32 menuReturn(PrintConsole* term_con, PrintConsole* menu_con, u32 param); u32 menuSetBootMode(PrintConsole* term_con, PrintConsole* menu_con, u32 param); +u32 menuSetSplash(PrintConsole* term_con, PrintConsole* menu_con, u32 param); u32 menuSetupBootSlot(PrintConsole* term_con, PrintConsole* menu_con, u32 param); u32 menuSetupBootKeys(PrintConsole* term_con, PrintConsole* menu_con, u32 param); u32 menuLaunchFirm(PrintConsole* term_con, PrintConsole* menu_con, u32 param); diff --git a/include/arm11/menu/menu_util.h b/include/arm11/menu/menu_util.h index 5a814ec..0964b74 100644 --- a/include/arm11/menu/menu_util.h +++ b/include/arm11/menu/menu_util.h @@ -34,6 +34,7 @@ void clearTop(void); void drawTopBorder(void); +bool drawCustomSplash(const char* folder); void updateScreens(void); bool askConfirmation(const char *const fmt, ...); diff --git a/include/arm11/menu/splash.h b/include/arm11/menu/splash.h index 3b5a022..25fcc8a 100644 --- a/include/arm11/menu/splash.h +++ b/include/arm11/menu/splash.h @@ -21,6 +21,10 @@ #include "types.h" +#define CSPLASH_NAME_TOP "splash" +#define CSPLASH_NAME_SUB "splashbottom" + + #define FLAG_ROTATED (1u<<3) #define FLAG_COMPRESSED (1u<<4) #define FLAG_SWAPPED (1u<<5) diff --git a/source/arm11/main.c b/source/arm11/main.c index f333bb8..204d46e 100644 --- a/source/arm11/main.c +++ b/source/arm11/main.c @@ -68,7 +68,21 @@ { if (!gfx_initialized) GFX_init(true); gfx_initialized = true; - splash_wait = drawSplashscreen(banner_spla, -1, -1, SCREEN_TOP); + if(configDataExist(KSplashScreen)) + { + char* folder = (char*) configGetData(KSplashScreen); + splash_wait = drawCustomSplash(folder); // || drawSplashscreen(banner_spla, -1, -1, SCREEN_TOP); + if (!splash_wait) + { + err_string = (char*) malloc(512); + if(!err_string) panicMsg("Out of memory"); + ee_sprintf(err_string, "Custom splash screen error:\n%s\n", folder); + } + } + else + { + splash_wait = drawSplashscreen(banner_spla, -1, -1, SCREEN_TOP); + } updateScreens(); } @@ -78,7 +92,7 @@ u32 kHeld = hidKeysHeld(); // boot slot keycombo held? - if (kHeld & 0xfff) + if (!err_string && (kHeld & 0xfff)) { err_string = (char*) malloc(512); char* err_ptr = err_string; diff --git a/source/arm11/menu/menu_fsel.c b/source/arm11/menu/menu_fsel.c index c219b2a..dc46ec4 100644 --- a/source/arm11/menu/menu_fsel.c +++ b/source/arm11/menu/menu_fsel.c @@ -317,7 +317,7 @@ * @param start Starting path (must be a dir). * @param pattern Only files matching this wildcard pattern will be displayed. */ -bool menuFileSelector(char* res_path, PrintConsole* menu_con, const char* start, const char* pattern, bool allow_root) +bool menuFileSelector(char* res_path, PrintConsole* menu_con, const char* start, const char* pattern, bool allow_root, bool select_dirs) { DirBufferEntry* dir_buffer; bool result = true; // <--- should be handled differently @@ -430,15 +430,15 @@ { result = false; } - else if ((kDown & KEY_A) && n_entries) + else if ((kDown & (KEY_A|KEY_X)) && n_entries) { // build new res_path char* name = &(res_path[strlen(res_path)]); if (name > res_path) *(name++) = '/'; strncpy(name, dir_buffer[index].fname, ((FF_MAX_LFN + 1) - (name - res_path))); - // is this a dir? - is_dir = dir_buffer[index].is_dir; + // is this a dir? (override when X is detected) + is_dir = !(select_dirs && (kDown & KEY_X)) && dir_buffer[index].is_dir; lastname = NULL; break; diff --git a/source/arm11/menu/menu_func.c b/source/arm11/menu/menu_func.c index 4730dda..b7e9dce 100644 --- a/source/arm11/menu/menu_func.c +++ b/source/arm11/menu/menu_func.c @@ -32,6 +32,7 @@ #include "arm11/menu/menu_fsel.h" #include "arm11/menu/menu_func.h" #include "arm11/menu/menu_util.h" +#include "arm11/menu/splash.h" #include "arm11/hardware/hid.h" #include "arm11/hardware/mcu.h" #include "arm11/console.h" @@ -88,9 +89,17 @@ if (configDataExist(KBootMode)) res |= 1 << N_BOOTSLOTS; + if (configDataExist(KSplashScreen)) + res |= 1 << (N_BOOTSLOTS+1); + return res; } +u32 menuPresetSplashConfig(void) +{ + return configDataExist(KSplashScreen) ? (1 << 0) : (1 << 1); +} + u32 menuPresetSlotConfig(u32 slot) { u32 res = 0; @@ -144,10 +153,149 @@ return res; } +u32 menuSetSplash(PrintConsole* term_con, PrintConsole* menu_con, u32 param) +{ + char* res_path = NULL; + char* start = NULL; + + // if (param == 0) set default splash screen and return to menu + if (!param) + { + configDeleteKey(KSplashScreen); + return MENU_OK; + } + + if (configDataExist(KSplashScreen)) + start = (char*) configGetData(KSplashScreen); + + res_path = (char*) malloc(FF_MAX_LFN + 1); + if (!res_path) panicMsg("Out of memory"); + + consoleSelect(term_con); + consoleClear(); + ee_printf_screen_center("Select a custom splash screen.\n[X] to select current folder.\nPress [HOME] to cancel."); + updateScreens(); + + u32 res = MENU_OK; + *res_path = '\0'; + if (menuFileSelector(res_path, menu_con, start, "splash*.*", true, true)) + { + // back to terminal console + consoleSelect(term_con); + + // analyze user selection + FILINFO fileStat; + if (!(fileStat.fattrib & AM_DIR)) + { + char* slash = strrchr(res_path, '/'); + if (slash) *slash = '\0'; + } + + // check if selection at least looks valid + const char* splash_name[] = { CSPLASH_NAME_TOP, CSPLASH_NAME_SUB }; + const u32 splash_bin_width[] = { SCREEN_WIDTH_TOP, SCREEN_WIDTH_SUB }; + const u32 splash_bin_height[] = { SCREEN_HEIGHT_TOP, SCREEN_HEIGHT_SUB }; + char* splash_path = (char*) malloc(FF_MAX_LFN + 1); + char* splash_bin_path = (char*) malloc(FF_MAX_LFN + 1); + bool valid = false; + + if (!splash_path || !splash_bin_path) + panicMsg("Out of memory"); + + for (u32 i = 0; i < 2; i++) + { + // check for splash in .spla format + ee_snprintf(splash_path, FF_MAX_LFN + 1, "%s/%s.spla", res_path, splash_name[i]); + if ((fStat(splash_path, &fileStat) == FR_OK) && (fileStat.fsize >= sizeof(SplashHeader))) + { + valid = true; + continue; + } + + // check splash in Luma 3DS .bin format + u32 splash_bin_size = splash_bin_width[i] * splash_bin_height[i] * 3; + ee_snprintf(splash_bin_path, FF_MAX_LFN + 1, "%s/%s.bin", res_path, splash_name[i]); + if ((fStat(splash_bin_path, &fileStat) != FR_OK) || (fileStat.fsize != splash_bin_size)) + continue; // not found + + // notify user about the conversion + consoleClear(); + ee_printf_screen_center("Converting splash files, please wait..."); + updateScreens(); + + // convert .bin splash to .spla format + s32 fHandle = fOpen(splash_bin_path, FS_OPEN_EXISTING | FS_OPEN_READ); + if (fHandle < 0) continue; // can not open + + u8* splash_buffer = (u8*) malloc(splash_bin_size); + if (!splash_buffer) panicMsg("Out of memory"); + + fRead(fHandle, splash_buffer, splash_bin_size); + fClose(fHandle); + + // convert RGB888 -> RGB565 + u8* ptr_out = splash_buffer; + for (u8* ptr_in = splash_buffer; + ptr_in - splash_buffer < (int) splash_bin_size; + ptr_in += 3, ptr_out += 2) + { + u16 rgb565 = + (ptr_in[2] >> 3) << (6 + 5) | + (ptr_in[1] >> 2) << 5 | + (ptr_in[0] >> 3); + + ptr_out[0] = rgb565 & 0xFF; + ptr_out[1] = rgb565 >> 8; + } + + // build the .spla header + SplashHeader hdr; + memcpy(&(hdr.magic), "SPLA", 4); + hdr.width = splash_bin_width[i]; + hdr.height = splash_bin_height[i]; + hdr.flags = FLAG_ROTATED; + + // write .spla file + fHandle = fOpen(splash_path, FS_OPEN_ALWAYS | FS_OPEN_WRITE); + if (fHandle < 0) { // cannot open for writing + free(splash_buffer); + continue; + } + + valid = ((fWrite(fHandle, &hdr, sizeof(SplashHeader)) == FR_OK) && + (fWrite(fHandle, splash_buffer, hdr.width * hdr.height * 2) == FR_OK)); + fClose(fHandle); + free(splash_buffer); + + if (!valid) break; + } + + free(splash_path); + free(splash_bin_path); + + if (valid) + { + res = (configSetKeyData(KSplashScreen, res_path)) ? MENU_OK : MENU_FAIL; + } + else + { + res = MENU_FAIL; + + consoleSelect(term_con); + consoleClear(); + ee_printf_screen_center("Not a valid splash folder.\nPress [B] or [HOME] to return."); + updateScreens(); + + outputEndWait(); + } + } + + free(res_path); + return res; +} + u32 menuSetupBootSlot(PrintConsole* term_con, PrintConsole* menu_con, u32 param) { - (void) term_con; - const u32 slot = param & 0xF; char* res_path = NULL; char* start = NULL; @@ -166,11 +314,13 @@ res_path = (char*) malloc(FF_MAX_LFN + 1); if (!res_path) panicMsg("Out of memory"); + consoleSelect(term_con); + consoleClear(); ee_printf_screen_center("Select a firmware file for slot #%lu.\nPress [HOME] to cancel.", slot + 1); updateScreens(); u32 res = MENU_OK; - if (menuFileSelector(res_path, menu_con, start, "*firm*", true)) + if (menuFileSelector(res_path, menu_con, start, "*firm*", true, false)) res = (configSetKeyData(KBootOption1 + slot, res_path)) ? MENU_OK : MENU_FAIL; free(res_path); @@ -293,7 +443,7 @@ updateScreens(); path = path_store; - if (!menuFileSelector(path, menu_con, NULL, "*firm*", true)) + if (!menuFileSelector(path, menu_con, NULL, "*firm*", true, false)) return MENU_FAIL; // back to terminal console @@ -507,7 +657,7 @@ updateScreens(); char fpath[FF_MAX_LFN + 1]; - if (!menuFileSelector(fpath, menu_con, NAND_BACKUP_PATH, "*.bin", false)) + if (!menuFileSelector(fpath, menu_con, NAND_BACKUP_PATH, "*.bin", false, false)) return MENU_FAIL; // canceled by user // select & clear console @@ -656,7 +806,7 @@ // file selector ee_printf_screen_center("Select a firmware file to install.\nPress [HOME] to cancel."); updateScreens(); - if (!menuFileSelector(firm_path, menu_con, NULL, "*firm*", true)) + if (!menuFileSelector(firm_path, menu_con, NULL, "*firm*", true, false)) return MENU_FAIL; // cancel by user @@ -723,7 +873,7 @@ ee_printf_screen_center("Select fastboot3DS update file.\nPress [HOME] to cancel."); updateScreens(); - if (!menuFileSelector(firm_path, menu_con, "sdmc:", "*firm*", true)) + if (!menuFileSelector(firm_path, menu_con, "sdmc:", "*firm*", true, false)) return MENU_FAIL; // cancel by user diff --git a/source/arm11/menu/menu_util.c b/source/arm11/menu/menu_util.c index 3081d6b..1256317 100644 --- a/source/arm11/menu/menu_util.c +++ b/source/arm11/menu/menu_util.c @@ -19,12 +19,14 @@ #include #include #include "types.h" +#include "fs.h" #include "hardware/gfx.h" #include "arm11/hardware/hid.h" #include "arm11/hardware/mcu.h" #include "arm.h" #include "arm11/menu/menu_color.h" #include "arm11/menu/menu_util.h" +#include "arm11/menu/splash.h" #include "arm11/console.h" #include "arm11/fmt.h" @@ -238,6 +240,62 @@ } } +bool drawCustomSplash(const char* folder) +{ + // this assumes top and bottom screens cleared + const u32 splash_max_size = sizeof(SplashHeader) + (SCREEN_WIDTH_TOP * SCREEN_HEIGHT_TOP * 2); + char* splash_path = (char*) malloc(FF_MAX_LFN + 1); + u8* splash_buffer = (u8*) malloc(splash_max_size); + bool res = false; + s32 fHandle; + + if (!splash_path || !splash_buffer) goto fail; + + // try top splash + ee_snprintf(splash_path, FF_MAX_LFN + 1, "%s/%s.spla", folder, CSPLASH_NAME_TOP); + fHandle = fOpen(splash_path, FS_OPEN_EXISTING | FS_OPEN_READ); + if (fHandle >= 0) + { + u32 splash_size = fSize(fHandle); + if ((splash_size < sizeof(SplashHeader)) || (splash_size > splash_max_size) || + (fRead(fHandle, splash_buffer, splash_size) != FR_OK)) + { + fClose(fHandle); + goto fail; + } + fClose(fHandle); + + if (drawSplashscreen(splash_buffer, -1, -1, SCREEN_TOP)) + res = true; + } + + // try bottom splash + ee_snprintf(splash_path, FF_MAX_LFN + 1, "%s/%s.spla", folder, CSPLASH_NAME_SUB); + fHandle = fOpen(splash_path, FS_OPEN_EXISTING | FS_OPEN_READ); + if (fHandle >= 0) + { + u32 splash_size = fSize(fHandle); + if ((splash_size < sizeof(SplashHeader)) || (splash_size > splash_max_size) || + (fRead(fHandle, splash_buffer, splash_size) != FR_OK)) + { + fClose(fHandle); + goto fail; + } + fClose(fHandle); + + if (drawSplashscreen(splash_buffer, -1, -1, SCREEN_SUB)) + res = true; + } + + + fail: + + if (splash_path) free(splash_path); + if (splash_buffer) free(splash_buffer); + + return res; +} + // only intended to be ran when the shell is closed void sleepmode(void) {