diff --git a/arm9/Makefile b/arm9/Makefile index 9710886..6af9faf 100644 --- a/arm9/Makefile +++ b/arm9/Makefile @@ -15,7 +15,7 @@ # all directories are relative to this makefile #--------------------------------------------------------------------------------- BUILD := build -SOURCES := ../source ../source/arm9 ../thirdparty/fatfs +SOURCES := ../source ../source/arm9 ../source/arm9/gui ../thirdparty/fatfs INCLUDES := ../include ../thirdparty DEFINES := -DARM9 -D_3DS -DVERS_STRING=\"$(VERS_STRING)\" \ -DVERS_MAJOR=$(VERS_MAJOR) -DVERS_MINOR=$(VERS_MINOR) diff --git a/include/arm9/gui/menu.h b/include/arm9/gui/menu.h new file mode 100644 index 0000000..275a3eb --- /dev/null +++ b/include/arm9/gui/menu.h @@ -0,0 +1,99 @@ +#pragma once + +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "arm9/gui/ui.h" +#include "arm9/gui/menu_firmloader.h" +#include "arm9/gui/menu_nand.h" +#include "arm9/gui/menu_options.h" +#include "arm9/gui/menu_filebrowse.h" +#include "arm9/gui/menu_update.h" +#include "arm9/gui/menu_credits.h" + + +enum menu_state_type { + MENU_STATE_MAIN = 0, + MENU_STATE_NAND_MENU, + MENU_STATE_LOAD_FIRM, + MENU_STATE_OPTIONS_MENU, + MENU_STATE_OPTIONS_MODE, + MENU_STATE_FIRM_LAUNCH_SETTINGS, + MENU_STATE_NAND_BACKUP, + MENU_STATE_NAND_RESTORE, + MENU_STATE_NAND_FLASH_FIRM, + MENU_STATE_FIRM_LAUNCH, + MENU_STATE_BROWSER, + MENU_STATE_UPDATE, + MENU_STATE_CREDITS, + MENU_STATE_EXIT, + + STATE_PREVIOUS // pseudo state for menuSetReturnToState() +}; + +typedef struct { + const char *name; + const enum menu_state_type state; +} named_state; + +typedef struct { + const u8 count; // holds the number of available options + const named_state options[]; +} menu_state_options; + +enum menu_events { + MENU_EVENT_NONE = 0, + MENU_EVENT_HOME_PRESSED, + MENU_EVENT_POWER_PRESSED, + MENU_EVENT_SD_CARD_INSERTED, + MENU_EVENT_SD_CARD_REMOVED, + MENU_EVENT_STATE_CHANGE +}; + +extern enum menu_state_type menu_state; +extern enum menu_state_type menu_next_state; + +extern enum menu_state_type menu_previous_states[8]; +extern int menu_previous_states_count; + +extern int menu_event_state; + + + +// Enable or disable the pseudo VBlank in the menu. +void menuSetVBlank(bool enable); + +int enter_menu(int initial_state); + +// Use this to change to another sub-menu. +// Call menuUpdateGlobalState and menuActState afterwards. +bool menuSetReturnToState(int state); +// Use this to change back to a previous sub-menu. +// Call menuUpdateGlobalState and menuActState afterwards. +void menuSetEnterNextState(int state); +// This must be called once in each (sub-) menu iteration. +// Returns an event code. +int menuUpdateGlobalState(void); +// This must be called once in each (sub-) menu iteration, +// after menuUpdateGlobalState has been called. +void menuActState(void); +// Shows a windowed text, updates menu state. +void menuPrintPrompt(const char *text); +// Waits for any key being pressed, updates menu state. +void menuWaitForAnyPadkey(); + diff --git a/include/arm9/gui/menu_credits.h b/include/arm9/gui/menu_credits.h new file mode 100644 index 0000000..9d08f68 --- /dev/null +++ b/include/arm9/gui/menu_credits.h @@ -0,0 +1,23 @@ +#pragma once + +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + + +void menuCredits(void); diff --git a/include/arm9/gui/menu_filebrowse.h b/include/arm9/gui/menu_filebrowse.h new file mode 100644 index 0000000..4c3e071 --- /dev/null +++ b/include/arm9/gui/menu_filebrowse.h @@ -0,0 +1,23 @@ +#pragma once + +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + + +const char *browseForFile(const char *basePath); diff --git a/include/arm9/gui/menu_firmloader.h b/include/arm9/gui/menu_firmloader.h new file mode 100644 index 0000000..7bfca2c --- /dev/null +++ b/include/arm9/gui/menu_firmloader.h @@ -0,0 +1,26 @@ +#pragma once + +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + + +bool isFirmLoaded(void); +bool menuLaunchFirm(const char *filePath, bool quick); +bool tryLoadFirmwareFromSettings(bool fromMenu); +bool tryLoadFirmware(const char *filepath, bool skipHashCheck, bool printInfo); diff --git a/include/arm9/gui/menu_nand.h b/include/arm9/gui/menu_nand.h new file mode 100644 index 0000000..7133fdf --- /dev/null +++ b/include/arm9/gui/menu_nand.h @@ -0,0 +1,25 @@ +#pragma once + +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + + +bool menuDumpNand(const char *filePath); +bool menuRestoreNand(const char *filePath); +bool menuFlashFirmware(const char *filepath); diff --git a/include/arm9/gui/menu_options.h b/include/arm9/gui/menu_options.h new file mode 100644 index 0000000..3677f29 --- /dev/null +++ b/include/arm9/gui/menu_options.h @@ -0,0 +1,23 @@ +#pragma once + +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + + +bool menuOptions(void); diff --git a/include/arm9/gui/menu_update.h b/include/arm9/gui/menu_update.h new file mode 100644 index 0000000..dedae50 --- /dev/null +++ b/include/arm9/gui/menu_update.h @@ -0,0 +1,23 @@ +#pragma once + +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + + +bool menuUpdateLoader(); diff --git a/include/arm9/gui/ui.h b/include/arm9/gui/ui.h new file mode 100644 index 0000000..638a794 --- /dev/null +++ b/include/arm9/gui/ui.h @@ -0,0 +1,55 @@ +#pragma once + +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "arm9/console.h" +#include "arm9/hid.h" + + +#define uiPrintInfo(format, ...) uiPrint(format, 0, false, ##__VA_ARGS__) +#define uiPrintWarning(format, ...) uiPrint(format, 33, false, ##__VA_ARGS__) +#define uiPrintError(format, ...) uiPrint(format, 31, false, ##__VA_ARGS__) + + +// PrintConsole for each screen +PrintConsole con_top, con_bottom; + + + +void uiInit(); +void uiDrawSplashScreen(); +void uiDrawConsoleWindow(); +void uiClearConsoles(); +void clearConsole(int which); +void uiSetVerboseMode(bool verb); +bool uiGetVerboseMode(); +bool uiDialogYesNo(int screen, const char *textYes, const char *textNo, const char *const format, ...); +void uiPrintIfVerbose(const char *const format, ...); +void uiPrint(const char *const format, unsigned int color, bool centered, ...); +void uiPrintCenteredInLine(unsigned int y, const char *const format, ...); +void uiPrintTextAt(unsigned int x, unsigned int y, const char *const format, ...); +u32 uiDialog(const char *const format, const char *const lastLine, u32 waitKeys, + int screen, unsigned int x, unsigned int y, bool centered, ...); +void uiPrintProgressBar(unsigned int x, unsigned int y, unsigned int w, + unsigned int h, unsigned int cur, unsigned int max); +void uiPrintDevModeRequirement(); +void uiPrintBootWarning(); +void uiPrintBootFailure(); +bool uiCheckHomePressed(u32 msTimeout); +void uiWaitForAnyPadkey(); diff --git a/include/arm9/main.h b/include/arm9/main.h index b443b91..152a899 100644 --- a/include/arm9/main.h +++ b/include/arm9/main.h @@ -20,7 +20,7 @@ #include "arm9/debug.h" #include "fatfs/ff.h" -#include "arm9/ui.h" +#include "arm9/gui/ui.h" enum bootOptionResultTypes { diff --git a/include/arm9/menu.h b/include/arm9/menu.h deleted file mode 100644 index 38d7423..0000000 --- a/include/arm9/menu.h +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once - -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "arm9/ui.h" -#include "arm9/menu_firmloader.h" -#include "arm9/menu_nand.h" -#include "arm9/menu_options.h" -#include "arm9/menu_filebrowse.h" -#include "arm9/menu_update.h" -#include "arm9/menu_credits.h" - - -enum menu_state_type { - MENU_STATE_MAIN = 0, - MENU_STATE_NAND_MENU, - MENU_STATE_LOAD_FIRM, - MENU_STATE_OPTIONS_MENU, - MENU_STATE_OPTIONS_MODE, - MENU_STATE_FIRM_LAUNCH_SETTINGS, - MENU_STATE_NAND_BACKUP, - MENU_STATE_NAND_RESTORE, - MENU_STATE_NAND_FLASH_FIRM, - MENU_STATE_FIRM_LAUNCH, - MENU_STATE_BROWSER, - MENU_STATE_UPDATE, - MENU_STATE_CREDITS, - MENU_STATE_EXIT, - - STATE_PREVIOUS // pseudo state for menuSetReturnToState() -}; - -typedef struct { - const char *name; - const enum menu_state_type state; -} named_state; - -typedef struct { - const u8 count; // holds the number of available options - const named_state options[]; -} menu_state_options; - -enum menu_events { - MENU_EVENT_NONE = 0, - MENU_EVENT_HOME_PRESSED, - MENU_EVENT_POWER_PRESSED, - MENU_EVENT_SD_CARD_INSERTED, - MENU_EVENT_SD_CARD_REMOVED, - MENU_EVENT_STATE_CHANGE -}; - -extern enum menu_state_type menu_state; -extern enum menu_state_type menu_next_state; - -extern enum menu_state_type menu_previous_states[8]; -extern int menu_previous_states_count; - -extern int menu_event_state; - - - -// Enable or disable the pseudo VBlank in the menu. -void menuSetVBlank(bool enable); - -int enter_menu(int initial_state); - -// Use this to change to another sub-menu. -// Call menuUpdateGlobalState and menuActState afterwards. -bool menuSetReturnToState(int state); -// Use this to change back to a previous sub-menu. -// Call menuUpdateGlobalState and menuActState afterwards. -void menuSetEnterNextState(int state); -// This must be called once in each (sub-) menu iteration. -// Returns an event code. -int menuUpdateGlobalState(void); -// This must be called once in each (sub-) menu iteration, -// after menuUpdateGlobalState has been called. -void menuActState(void); -// Shows a windowed text, updates menu state. -void menuPrintPrompt(const char *text); -// Waits for any key being pressed, updates menu state. -void menuWaitForAnyPadkey(); - diff --git a/include/arm9/menu_credits.h b/include/arm9/menu_credits.h deleted file mode 100644 index 9d08f68..0000000 --- a/include/arm9/menu_credits.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - - -void menuCredits(void); diff --git a/include/arm9/menu_filebrowse.h b/include/arm9/menu_filebrowse.h deleted file mode 100644 index 4c3e071..0000000 --- a/include/arm9/menu_filebrowse.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - - -const char *browseForFile(const char *basePath); diff --git a/include/arm9/menu_firmloader.h b/include/arm9/menu_firmloader.h deleted file mode 100644 index 7bfca2c..0000000 --- a/include/arm9/menu_firmloader.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - - -bool isFirmLoaded(void); -bool menuLaunchFirm(const char *filePath, bool quick); -bool tryLoadFirmwareFromSettings(bool fromMenu); -bool tryLoadFirmware(const char *filepath, bool skipHashCheck, bool printInfo); diff --git a/include/arm9/menu_nand.h b/include/arm9/menu_nand.h deleted file mode 100644 index 7133fdf..0000000 --- a/include/arm9/menu_nand.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - - -bool menuDumpNand(const char *filePath); -bool menuRestoreNand(const char *filePath); -bool menuFlashFirmware(const char *filepath); diff --git a/include/arm9/menu_options.h b/include/arm9/menu_options.h deleted file mode 100644 index 3677f29..0000000 --- a/include/arm9/menu_options.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - - -bool menuOptions(void); diff --git a/include/arm9/menu_update.h b/include/arm9/menu_update.h deleted file mode 100644 index dedae50..0000000 --- a/include/arm9/menu_update.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - - -bool menuUpdateLoader(); diff --git a/include/arm9/ui.h b/include/arm9/ui.h deleted file mode 100644 index ba3835a..0000000 --- a/include/arm9/ui.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "arm9/console.h" -#include "hid.h" - - -#define uiPrintInfo(format, ...) uiPrint(format, 0, false, ##__VA_ARGS__) -#define uiPrintWarning(format, ...) uiPrint(format, 33, false, ##__VA_ARGS__) -#define uiPrintError(format, ...) uiPrint(format, 31, false, ##__VA_ARGS__) - - -// PrintConsole for each screen -PrintConsole con_top, con_bottom; - - - -void uiInit(); -void uiDrawSplashScreen(); -void uiDrawConsoleWindow(); -void uiClearConsoles(); -void clearConsole(int which); -void uiSetVerboseMode(bool verb); -bool uiGetVerboseMode(); -bool uiDialogYesNo(int screen, const char *textYes, const char *textNo, const char *const format, ...); -void uiPrintIfVerbose(const char *const format, ...); -void uiPrint(const char *const format, unsigned int color, bool centered, ...); -void uiPrintCenteredInLine(unsigned int y, const char *const format, ...); -void uiPrintTextAt(unsigned int x, unsigned int y, const char *const format, ...); -u32 uiDialog(const char *const format, const char *const lastLine, u32 waitKeys, - int screen, unsigned int x, unsigned int y, bool centered, ...); -void uiPrintProgressBar(unsigned int x, unsigned int y, unsigned int w, - unsigned int h, unsigned int cur, unsigned int max); -void uiPrintDevModeRequirement(); -void uiPrintBootWarning(); -void uiPrintBootFailure(); -bool uiCheckHomePressed(u32 msTimeout); -void uiWaitForAnyPadkey(); diff --git a/source/arm9/config.c b/source/arm9/config.c index 29a8219..5bfb1ae 100644 --- a/source/arm9/config.c +++ b/source/arm9/config.c @@ -33,7 +33,7 @@ #include "util.h" #include "arm9/hid.h" #include "arm9/debug.h" -#include "arm9/ui.h" +#include "arm9/gui/ui.h" #include "arm9/config.h" #define MAX_FILE_SIZE 0x4000 - 1 diff --git a/source/arm9/firm.c b/source/arm9/firm.c index 2dd4fb9..dcbe146 100644 --- a/source/arm9/firm.c +++ b/source/arm9/firm.c @@ -28,7 +28,7 @@ #include "arm9/start.h" #include "arm9/crypto.h" #include "arm9/ndma.h" -#include "arm9/ui.h" +#include "arm9/gui/ui.h" #include "cache.h" #include "util.h" #include "pxi.h" diff --git a/source/arm9/firmwriter.c b/source/arm9/firmwriter.c index dfcf965..d6d7b84 100644 --- a/source/arm9/firmwriter.c +++ b/source/arm9/firmwriter.c @@ -27,7 +27,7 @@ #include "util.h" #include "arm9/firmwriter.h" #include "arm9/main.h" -#include "arm9/menu.h" +#include "arm9/gui/menu.h" #include "arm9/timer.h" diff --git a/source/arm9/gui/menu.c b/source/arm9/gui/menu.c new file mode 100644 index 0000000..c9b8392 --- /dev/null +++ b/source/arm9/gui/menu.c @@ -0,0 +1,481 @@ +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include "types.h" +#include "mem_map.h" +#include "util.h" +#include "pxi.h" +#include "arm9/console.h" +#include "arm9/dev.h" +#include "fatfs/ff.h" +#include "fatfs/diskio.h" +#include "arm9/fsutils.h" +#include "arm9/hid.h" +#include "arm9/main.h" +#include "arm9/gui/menu.h" +#include "arm9/gui/menu_firmloader.h" +#include "arm9/timer.h" +#include "arm9/interrupt.h" +#include "arm9/config.h" + + +const menu_state_options menu_main = { + 6, + { + {"Continue boot", MENU_STATE_FIRM_LAUNCH_SETTINGS}, + {"Launch Firmware...", MENU_STATE_BROWSER}, + {"NAND tools...", MENU_STATE_NAND_MENU}, + {"Options...", MENU_STATE_OPTIONS_MENU}, + {"Update", MENU_STATE_UPDATE}, + {"Credits", MENU_STATE_CREDITS}, + } +}; + +const menu_state_options menu_nand = { + 3, + { + {"Backup NAND", MENU_STATE_NAND_BACKUP}, + {"Restore NAND", MENU_STATE_NAND_RESTORE}, + {"Flash Firmware", MENU_STATE_NAND_FLASH_FIRM} + } +}; + +const menu_state_options menu_loadfw = { + 2, + { + {"Load from SD card", MENU_STATE_BROWSER}, + {"Load firm1", 0}, + } +}; + +// menu_state_type -> menu_state_options instance +const menu_state_options *options_lookup[] = { + &menu_main, // STATE_MAIN + &menu_nand, // STATE_NAND_MENU + &menu_loadfw, // MENU_STATE_LOAD_FIRM +}; + +enum menu_state_type menu_state; +enum menu_state_type menu_next_state; + +enum menu_state_type menu_previous_states[8]; +int menu_previous_states_count; + +int menu_event_state; +static bool waitForVBlank; + +static void menuRunOnce(int state); + +static void menu_main_draw_top() +{ + const char *const sd_res[3] = {"\x1B[33mNot inserted ", "\x1B[31mFS init failed", "\x1B[32mOK "}; + const char *const nand_res[2] = {"\x1B[31mInit failed: ", "\x1B[32mOK "}; + const char *const nand_drives[3] = {"twln ", "twlp ", "nand"}; + const char *const boot_res[4] = {"Not attempted", "Not found", "Failed", "Skipped"}; + + consoleSelect(&con_top); + + uiPrintCenteredInLine(1, "fastboot 3DS " VERS_STRING); + + uiPrintTextAt(1, 4, "Model: %s", bootInfo.model); + uiPrintTextAt(1, 5, "\x1B[33m%s\e[0m", bootInfo.bootEnv); + uiPrintTextAt(1, 6, "\x1B[32m(%s Mode)\x1B[0m", bootInfo.mode); + uiPrintTextAt(1, 8, "SD card status: %s\x1B[0m", sd_res[bootInfo.sd_status]); + uiPrintTextAt(1, 9, "NAND status: %s\x1B[0m", nand_res[bootInfo.nand_status == 7]); + if(bootInfo.nand_status != 7) + { + for(u32 i = 0; i < 3; i++) + { + if(!(bootInfo.nand_status>>i & 1u)) uiPrintError(nand_drives[i]); + } + } + uiPrintTextAt(1, 10, "Wifi flash status: %s\e[0m", nand_res[bootInfo.wififlash_status]); + + if(bootInfo.numBootOptionsAttempted != 0) + { + uiPrintTextAt(1, 15, "Firm launch statistics:"); + uiPrintTextAt(1, 16, "[Slot1]: %s", boot_res[bootInfo.bootOptionResults[0]]); + uiPrintTextAt(1, 17, "[Slot2]: %s", boot_res[bootInfo.bootOptionResults[1]]); + uiPrintTextAt(1, 18, "[Slot3]: %s", boot_res[bootInfo.bootOptionResults[2]]); + } +} + +void menuSetVBlank(bool enable) +{ + if(enable) + { + IRQ_registerHandler(IRQ_TIMER_0, NULL); + TIMER_start(TIMER_0, TIMER_PRESCALER_64, TIMER_FREQ_64(60.0f), true); + waitForVBlank = true; + } + else + { + waitForVBlank = false; + TIMER_stop(TIMER_0); + IRQ_unregisterHandler(IRQ_TIMER_0); + } +} + +int enter_menu(int initial_state) +{ + u32 keys; + int cursor_pos; + const char* path; + const char *firmPath = NULL; + + menu_event_state = MENU_EVENT_NONE; + + cursor_pos = 0; + + uiClearConsoles(); + uiDrawConsoleWindow(); + + menuSetVBlank(true); + + // caller requested to enter a submenu? + if(initial_state != MENU_STATE_MAIN) + { + menuRunOnce(initial_state); + goto exitAndLaunchFirm; + } + + menuSetEnterNextState(MENU_STATE_MAIN); + menuActState(); + + + // Menu main loop + for(;;) + { + const menu_state_options *cur_options = options_lookup[menu_state]; + + switch(menu_state) + { + case MENU_STATE_MAIN: + case MENU_STATE_NAND_MENU: + + hidScanInput(); + keys = hidKeysDown(); + + menu_main_draw_top(); + + // print all the options of the current state + consoleSelect(&con_bottom); + for(int i=0; i < cur_options->count; i++) + uiPrintTextAt(9, 4+i, "%s\e[0m %s\n", cursor_pos == i ? "\x1B[33m*" : " ", + cur_options->options[i].name); + + if(keys & KEY_DUP) + { + if(cursor_pos == 0) cursor_pos = cur_options->count - 1; // jump to the last option + else cursor_pos--; + } + else if(keys & KEY_DDOWN) + { + if(cursor_pos == cur_options->count - 1) cursor_pos = 0; // jump to the first option + else cursor_pos++; + } + else if(keys & KEY_A) // select option + { + menuSetEnterNextState(cur_options->options[cursor_pos].state); + cursor_pos = 0; + } + else if(keys & KEY_B) // go back + { + if(menuSetReturnToState(STATE_PREVIOUS)) + cursor_pos = 0; + } + break; + + case MENU_STATE_NAND_BACKUP: + menuSetVBlank(false); + menuDumpNand("sdmc:/nand.bin"); + uiClearConsoles(); + menuSetVBlank(true); + break; + + case MENU_STATE_NAND_RESTORE: + menuSetVBlank(false); + menuRestoreNand("sdmc:/nand.bin"); + uiClearConsoles(); + menuSetVBlank(true); + break; + + case MENU_STATE_NAND_FLASH_FIRM: + path = browseForFile("sdmc:"); + uiClearConsoles(); + if(!path) + break; + menuSetVBlank(false); + menuFlashFirmware(path); + uiClearConsoles(); + menuSetVBlank(true); + break; + + case MENU_STATE_OPTIONS_MENU: + menuOptions(); + uiClearConsoles(); + break; + + case MENU_STATE_FIRM_LAUNCH: + if(!menuLaunchFirm(firmPath, false)) + uiClearConsoles(); + break; + + case MENU_STATE_FIRM_LAUNCH_SETTINGS: + if(!tryLoadFirmwareFromSettings(true)) + uiClearConsoles(); + break; + + case MENU_STATE_BROWSER: + path = browseForFile("sdmc:"); + uiClearConsoles(); + firmPath = path; + if(!path) // no file selected + break; + menuSetEnterNextState(MENU_STATE_FIRM_LAUNCH); + break; + + case MENU_STATE_UPDATE: + menuSetVBlank(false); + menuUpdateLoader(); + uiClearConsoles(); + menuSetVBlank(true); + break; + + case MENU_STATE_CREDITS: + menuCredits(); + uiClearConsoles(); + break; + + case MENU_STATE_EXIT: + goto exitAndLaunchFirm; + break; + + /* this should never happen */ + default: uiPrintIfVerbose("OOPS!\n"); break; + } + + switch(menuUpdateGlobalState()) + { + case MENU_EVENT_HOME_PRESSED: + break; + case MENU_EVENT_STATE_CHANGE: + if(menu_next_state == MENU_STATE_EXIT) + goto exitAndLaunchFirm; + else + clearConsole(0); + break; + default: + break; + } + + menuActState(); + } + +exitAndLaunchFirm: + menuSetVBlank(false); + + return 0; +} + +static void menuRunOnce(int state) +{ + switch(state) + { + case MENU_STATE_FIRM_LAUNCH_SETTINGS: + if(!tryLoadFirmwareFromSettings(true)) + uiClearConsoles(); + break; + + default: + panic(); + } +} + +bool menuSetReturnToState(int state) +{ + int i; + + if(state == STATE_PREVIOUS) + { + if(menu_previous_states_count <= 1) + return false; + state = menu_previous_states[menu_previous_states_count - 1]; + menu_previous_states_count--; + } + else if(state == MENU_STATE_EXIT) + { + menu_previous_states_count = 0; + } + else + { + for(i=menu_previous_states_count; i>0; i--) + { + if(menu_previous_states[i] == state) + { + menu_previous_states_count = i; + } + } + } + + menu_next_state = state; + // emit event + menu_event_state = MENU_EVENT_STATE_CHANGE; + + return true; +} + +void menuSetEnterNextState(int state) +{ + if(menu_previous_states_count >= 8) + panic(); + + menu_previous_states[menu_previous_states_count] = menu_state; + menu_previous_states_count++; + + menu_next_state = state; + // emit event + menu_event_state = MENU_EVENT_STATE_CHANGE; +} + +int menuUpdateGlobalState(void) +{ + int retcode = MENU_EVENT_NONE; + + // Later if PXI interrupts are implemented we need an + // interrupt handler here which can tell the timer and + // PXI interrupts apart. + if(waitForVBlank) waitForInterrupt(); + + + /* Check PXI Response register */ + + bool successFlag; + u32 replyCode = PXI_tryRecvWord(&successFlag); + + while(successFlag) + { + switch(replyCode) + { + case PXI_RPL_HOME_PRESSED: + case PXI_RPL_HOME_HELD: + retcode = MENU_EVENT_HOME_PRESSED; + break; + case PXI_RPL_POWER_PRESSED: + retcode = MENU_EVENT_POWER_PRESSED; + break; + default: + panic(); + } + // maybe there's more..? + replyCode = PXI_tryRecvWord(&successFlag); + } + + + /* Check for HW changes */ + + bool sd_active = dev_sdcard->is_active(); + if(!bootInfo.sd_status && sd_active) + { + retcode = MENU_EVENT_SD_CARD_INSERTED; + } + else if(bootInfo.sd_status && !sd_active) + { + retcode = MENU_EVENT_SD_CARD_REMOVED; + } + + + // Submenu wants to call a new menu? + if(menu_state != menu_next_state) + retcode = MENU_EVENT_STATE_CHANGE; + + // did anything happen? + if(retcode != MENU_EVENT_NONE) + menu_event_state = retcode; + + return retcode; +} + +void menuActState(void) +{ + switch(menu_event_state) + { + case MENU_EVENT_NONE: + break; // nothing to do, boring. + case MENU_EVENT_POWER_PRESSED: + power_off_safe(); + break; + case MENU_EVENT_HOME_PRESSED: + menuSetReturnToState(MENU_STATE_MAIN); + break; + case MENU_EVENT_STATE_CHANGE: + menu_state = menu_next_state; + break; + case MENU_EVENT_SD_CARD_INSERTED: + fsUnmountNandFilesystems(); + if(fsMountSdmc()) bootInfo.sd_status = 2; + else bootInfo.sd_status = 1; + bootInfo.nand_status = fsRemountNandFilesystems(); + // also try to load saved settings + loadConfigFile(); + break; + case MENU_EVENT_SD_CARD_REMOVED: + bootInfo.sd_status = 0; + dev_sdcard->close(); + f_mount(NULL, "sdmc:", 1); + break; + default: + break; + } + + menu_event_state = MENU_EVENT_NONE; +} + +void menuPrintPrompt(const char *text) +{ + // TODO: use uiShowMessageWindow + uiPrintInfo(text); +} + +void menuWaitForAnyPadkey() +{ + int keys; + bool finished = false; + + do { + hidScanInput(); + keys = hidKeysDown(); + if(keys & HID_KEY_MASK_ALL) + finished = true; + + switch(menuUpdateGlobalState()) + { + case MENU_EVENT_HOME_PRESSED: + case MENU_EVENT_POWER_PRESSED: + finished = true; + default: + break; + } + + menuActState(); + + } while(!finished); +} diff --git a/source/arm9/gui/menu_credits.c b/source/arm9/gui/menu_credits.c new file mode 100644 index 0000000..ceb7518 --- /dev/null +++ b/source/arm9/gui/menu_credits.c @@ -0,0 +1,84 @@ +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include "types.h" +#include "mem_map.h" +#include "arm9/console.h" +#include "arm9/dev.h" +#include "fatfs/ff.h" +#include "pxi.h" +#include "arm9/hid.h" +#include "util.h" +#include "arm9/main.h" +#include "arm9/timer.h" +#include "arm9/gui/menu.h" +#include "arm9/gui/ui.h" + +void menuCredits(void) +{ + u32 keys; + unsigned i = 3; + + uiClearConsoles(); + + uiPrintCenteredInLine(1, "Credits"); + + uiPrintCenteredInLine(i++, "Main developers:"); + uiPrintCenteredInLine(i++, "derrek"); + uiPrintCenteredInLine(i++, "profi200"); + i++; + uiPrintCenteredInLine(i++, "Thanks to:"); + uiPrintCenteredInLine(i++, "yellows8"); + uiPrintCenteredInLine(i++, "plutoo"); + uiPrintCenteredInLine(i++, "smea"); + uiPrintCenteredInLine(i++, "Normmatt (for sdmmc code)"); + uiPrintCenteredInLine(i++, "WinterMute (for console code)"); + uiPrintCenteredInLine(i+=2, "... everyone who contributed to 3dbrew.org"); + + for(;;) + { + /* Handle HID */ + hidScanInput(); + keys = hidKeysDown(); + + if(keys & KEY_B) + { + goto done; + } + + switch(menuUpdateGlobalState()) + { + case MENU_EVENT_HOME_PRESSED: + case MENU_EVENT_POWER_PRESSED: + case MENU_EVENT_STATE_CHANGE: + goto done; + default: + break; + } + + menuActState(); + } + +done: + + menuActState(); + menuSetReturnToState(STATE_PREVIOUS); +} diff --git a/source/arm9/gui/menu_filebrowse.c b/source/arm9/gui/menu_filebrowse.c new file mode 100644 index 0000000..1142f2c --- /dev/null +++ b/source/arm9/gui/menu_filebrowse.c @@ -0,0 +1,395 @@ +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include "types.h" +#include "arm9/fmt.h" +#include "mem_map.h" +#include "fatfs/ff.h" +#include "arm9/console.h" +#include "arm9/main.h" +#include "arm9/gui/menu.h" +#include "arm9/interrupt.h" +#include "arm9/dev.h" +#include "util.h" +#include "arm9/hid.h" + + +#define MAX_CACHED_ENTRIES 0x400 +#define MAX_ENTRY_SIZE FF_MAX_LFN + 2 +#define MAX_NEW_ENTRIES 0x20 +#define MAX_PATH_LENGTH 0x400 +#define MAX_PATH_DEPTH 0x20 + +typedef struct { + char name[MAX_ENTRY_SIZE]; + bool isDir; +} EntryType; + +static EntryType *dirEntries; // [MAX_ENTRY_SIZE][MAX_CACHED_ENTRIES]; +static DIR curDirectory; +static char curPath[MAX_PATH_LENGTH]; +static u32 curEntriesCount; + +// This stack is used to recover the original cursor position +// in the dir entries list when returning from a subdir. +static u32 indexStack[MAX_PATH_DEPTH]; +static u32 *indexStackPtr = indexStack; + +/* This scans the current directory for entries. + Only MAX_NEW_ENTRIES will be added at maximum. + This also updates the curEntriesCount. + Returns true if new entries were added. */ +static bool scanDirectory() +{ + FRESULT res; + static FILINFO fno; + unsigned i = 0; + + for(i=0; i bufSize - 1) + { + end = in + entryLen; + strcpy(&out[3], end - bufSize + 3); + memcpy(out, "...", 3); + } + else strcpy(out, in); + } + else + { + if(entryLen > bufSize - 1) + { + memcpy(out, in, bufSize - 4); + memcpy(&out[bufSize - 5], "...", 4); + } + else strcpy(out, in); + } +} + +static void updateCursor(int cursor_pos, int old_cursor_pos, int maxEntries) +{ + // clear old '*' char + consoleSetCursor(&con_bottom, 0, (old_cursor_pos % (maxEntries)) + 1); + uiPrintInfo(" "); + + consoleSetCursor(&con_bottom, 0, (cursor_pos % (maxEntries)) + 1); + uiPrintInfo("*"); +} + +static void updateScreen(int cursor_pos, bool dirChange) +{ + // we don't want ugly line breaks in our list + const int maxLen = con_bottom.windowWidth - 1; + const int maxEntries = con_bottom.windowHeight - 1; + + static int old_cursor_pos; + + consoleSelect(&con_bottom); + + if(!dirChange && (cursor_pos < maxEntries) && (old_cursor_pos < maxEntries)) + { + updateCursor(cursor_pos, old_cursor_pos, maxEntries); + old_cursor_pos = cursor_pos; + return; + } + + old_cursor_pos = cursor_pos; + + char entry [maxLen]; // should be safe + unsigned start, end; + + consoleClear(); + + // print the current path in the first line + formatEntry(entry, curPath, (u32) maxLen - 3, false); + uiPrintInfo("%s :", entry); + + if(cursor_pos < (int) maxEntries) + { + start = 0; + end = min(maxEntries, curEntriesCount); + } + else + { + start = cursor_pos - maxEntries + 1; + end = min(cursor_pos + 1, curEntriesCount); + } + + for(unsigned i = start; i < end; i++) + { + formatEntry(entry, dirEntries[i].name, (u32) maxLen - 2, true); + uiPrintInfo("\n%s %s", i == (unsigned) cursor_pos ? "*" : " ", entry); + } +} + +const char *browseForFile(const char *basePath) +{ + FRESULT res; + u32 keys; + u32 cursor_pos = 0; + bool fileSelected = false; + int returnState = STATE_PREVIOUS; + + curEntriesCount = 0; + indexStackPtr = indexStack; + + uiClearConsoles(); + + dirEntries = (EntryType *) malloc(MAX_CACHED_ENTRIES * sizeof(EntryType)); + if(!dirEntries) + { + menuSetReturnToState(STATE_PREVIOUS); + menuUpdateGlobalState(); + menuActState(); + return NULL; + } + + if(basePath) + strncpy_s(curPath, basePath, sizeof(curPath) - 1, sizeof(curPath)); + else + strncpy_s(curPath, "sdmc:", sizeof(curPath) - 1, sizeof(curPath)); + + /* check if we have to wait for the user */ + if(!dev_sdcard->is_active()) + { + menuPrintPrompt("Waiting for SD card to be inserted...\nPress B to exit.\n"); + + do { + hidScanInput(); + keys = hidKeysDown(); + if(keys & KEY_B) + goto fail; + + switch(menuUpdateGlobalState()) + { + case MENU_EVENT_HOME_PRESSED: + case MENU_EVENT_POWER_PRESSED: + goto fail; + default: + break; + } + + menuActState(); + + } while(!dev_sdcard->is_active()); + + uiClearConsoles(); + } + + res = f_opendir(&curDirectory, curPath); + if (res != FR_OK) + { + menuPrintPrompt("Failed to enter directory.\n"); + menuWaitForAnyPadkey(); + free(dirEntries); + menuSetReturnToState(STATE_PREVIOUS); + menuUpdateGlobalState(); + menuActState(); + return NULL; + } + + // do an initial scan + scanDirectory(); + + updateScreen((int)cursor_pos, true); + + for(;;) + { + hidScanInput(); + keys = hidKeysDown()/* | hidKeysHeld()*/; + + if(curEntriesCount != 0) + { + if(keys & KEY_DUP || keys & KEY_DLEFT) + { + if(cursor_pos != 0) + { + cursor_pos -= keys & KEY_DLEFT ? min(cursor_pos, 8) : 1; + updateScreen((int)cursor_pos, false); + } + } + + else if(keys & KEY_DDOWN || keys & KEY_DRIGHT) + { + // we reached the end of our entries list? + u32 old_pos = cursor_pos; + cursor_pos += keys & KEY_DRIGHT ? 8 : 1; + if(cursor_pos >= curEntriesCount) + { + scanDirectory(); + // we got more entries? + if(curEntriesCount > cursor_pos) + cursor_pos = min(cursor_pos, curEntriesCount - 1); + else + cursor_pos = curEntriesCount - 1; + if(cursor_pos != old_pos) + updateScreen((int)cursor_pos, false); + } + else + { + updateScreen((int)cursor_pos, false); + } + } + + else if(keys & KEY_A) // select entry + { + if(dirEntries[cursor_pos].isDir) // it's a dir + { + if(descendPath(cursor_pos, dirEntries[cursor_pos].name)) + { + f_closedir(&curDirectory); + res = f_opendir(&curDirectory, curPath); + if (res != FR_OK) + ascendPath(); + else + { + // fetch new entries + curEntriesCount = 0; + scanDirectory(); + cursor_pos = 0; + updateScreen((int)cursor_pos, true); + } + } + } + else // we got our file! + { + if(strlen(dirEntries[cursor_pos].name) + strlen(curPath) + 2 > sizeof(curPath)) + goto fail; // TODO? Path too long. + strcat(curPath, "/"); + strcat(curPath, dirEntries[cursor_pos].name); + fileSelected = true; + menuSetReturnToState(STATE_PREVIOUS); + } + } + } + if(keys & KEY_B) // go back + { + if(indexStackPtr != indexStack) + { + cursor_pos = ascendPath(); + f_closedir(&curDirectory); + + res = f_opendir(&curDirectory, curPath); + if (res != FR_OK) + goto fail; + curEntriesCount = 0; + + do { + if(!scanDirectory()) + goto fail; + } while(curEntriesCount <= cursor_pos); + + updateScreen((int)cursor_pos, true); + } + else + menuSetReturnToState(STATE_PREVIOUS); + } + + switch(menuUpdateGlobalState()) + { + case MENU_EVENT_HOME_PRESSED: + returnState = MENU_STATE_MAIN; + goto fail; + case MENU_EVENT_POWER_PRESSED: + case MENU_EVENT_SD_CARD_REMOVED: + goto fail; + case MENU_EVENT_STATE_CHANGE: + if(!fileSelected) goto fail; + else goto done; + default: + break; + } + + menuActState(); + + } + +done: + + f_closedir(&curDirectory); + free(dirEntries); + + menuSetReturnToState(returnState); + menuActState(); + + + return curPath; + +fail: + + f_closedir(&curDirectory); + free(dirEntries); + + menuSetReturnToState(returnState); + menuActState(); + + return NULL; +} diff --git a/source/arm9/gui/menu_firmloader.c b/source/arm9/gui/menu_firmloader.c new file mode 100644 index 0000000..2cd24e4 --- /dev/null +++ b/source/arm9/gui/menu_firmloader.c @@ -0,0 +1,370 @@ +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include "types.h" +#include "mem_map.h" +#include "arm9/console.h" +#include "arm9/dev.h" +#include "fatfs/ff.h" +#include "arm9/hid.h" +#include "util.h" +#include "pxi.h" +#include "arm9/main.h" +#include "arm9/timer.h" +#include "arm9/gui/menu.h" +#include "arm9/firm.h" +#include "arm9/config.h" +#include "arm9/partitions.h" +#include "arm9/fsutils.h" + +static int firmLoaded = 0; + +bool isFirmLoaded(void) +{ + return firmLoaded != 0; +} + +bool menuLaunchFirm(const char *filePath, bool quick) +{ + if(!filePath) + return false; + + firmLoaded = 0; + + uiClearConsoles(); + consoleSelect(&con_top); + + uiPrintCenteredInLine(1, "FIRM Loader"); + + menuUpdateGlobalState(); + + if(!tryLoadFirmware(filePath, false, true)) + { + if(!quick) + { + uiPrintError("Bad firmware.\n"); + uiPrintInfo("Press any key...\n"); + + menuWaitForAnyPadkey(); + } + goto fail; + } + + firmLoaded = 1; + + uiPrint("Firmware verification SUCCESS!", 32, true); + + menuSetReturnToState(MENU_STATE_EXIT); + + return true; + +fail: + + uiPrintCenteredInLine(25, "FIRM-Launch aborted."); + + TIMER_sleep(500); + + firmLoaded = 0; + + menuSetReturnToState(STATE_PREVIOUS); + + return false; +} + +// Does very basic checks whether the firmware actually exists. +static bool statFirmware(const char *filePath) +{ + FILINFO fileStat; + // u32 fileSize; + + fsEnsureMounted(filePath); + + if(strncmp(filePath, "sdmc:", 5) == 0) + { + if(f_stat(filePath, &fileStat) != FR_OK) + return false; + + /* + fileSize = fileStat.fsize; + + if(fileSize == 0 || fileSize > FIRM_MAX_SIZE) + return false; + */ + + return true; + } + else if(strncmp(filePath, "firm1:", 5) == 0) + return true; // this is always available + + // unknown mountpoint + return false; +} + +static bool checkForHIDAbort() +{ + // give it 10 ms to check whether home button was pressed + return uiCheckHomePressed(10); +} + +bool tryLoadFirmwareFromSettings(bool fromMenu) +{ + const char *path; + int keyBootOption, bootMode, keyPad; + int i; + u32 padValue, expectedPadValue; + + if(fromMenu) + uiClearConsoles(); + + consoleSelect(&con_top); + + firmLoaded = 0; + + bootInfo.numBootOptionsAttempted = 0; + bootInfo.bootOptionResults[0] = BO_NOT_ATTEMPTED; + bootInfo.bootOptionResults[1] = BO_NOT_ATTEMPTED; + bootInfo.bootOptionResults[2] = BO_NOT_ATTEMPTED; + + if(fromMenu) + uiPrintCenteredInLine(1, "Loading FIRM from settings\n"); + + // No Boot Option set up? + if(!configDataExist(KBootOption1) && + !configDataExist(KBootOption2) && + !configDataExist(KBootOption3)) + { + if(fromMenu) + { + menuPrintPrompt("No Boot-Option set up yet.\nGo to 'Options' to choose a file.\n"); + menuWaitForAnyPadkey(); + menuSetReturnToState(STATE_PREVIOUS); + } + return false; + } + + const u32 *temp = configGetData(KBootMode); + bootMode = temp? *temp : BootModeNormal; + + keyBootOption = KBootOption1; + keyPad = KBootOption1Buttons; + + for(i=0; i<3; i++, keyBootOption++, keyPad++) + { + if(checkForHIDAbort()) + return false; + + bootInfo.numBootOptionsAttempted ++; + + path = (const char *)configGetData(keyBootOption); + if(path) + { + if(fromMenu) + uiPrintInfo("Boot Option #%i:\n%s\n", i + 1, path); + + // check if fw still exists + if(!statFirmware(path)) + { + if(fromMenu) + uiPrintInfo("Couldn't find firmware...\n"); + bootInfo.bootOptionResults[i] = BO_NOT_FOUND; + goto try_next; + } + + // check pad value + const u32 *dataPtr; + dataPtr = (const u32 *)configGetData(keyPad); + if(dataPtr) + { + expectedPadValue = *dataPtr; + + if(expectedPadValue != 0) + { + hidScanInput(); + padValue = HID_KEY_MASK_ALL & hidKeysHeld(); + if(padValue != expectedPadValue) + { + if(fromMenu) + { + uiPrintInfo("Skipping, right buttons are not pressed.\n"); + uiPrintInfo("%" PRIX32 " %" PRIX32 "\n", padValue, expectedPadValue); + } + bootInfo.bootOptionResults[i] = BO_SKIPPED; + goto try_next; + } + } + } + + if(fromMenu) + { + if(menuLaunchFirm(path, false)) + break; + else + { + uiClearConsoles(); + consoleSelect(&con_top); + } + } + else + { + if(tryLoadFirmware(path, false, false)) + break; + else + { + if(bootMode != BootModeQuiet) + uiPrintBootWarning(); + } + } + + bootInfo.bootOptionResults[i] = BO_FAILED; + } + else + continue; + +try_next: + + if(fromMenu) + TIMER_sleep(300); + + // ... we failed, try next one + } + + if(i >= 3) + { + if(fromMenu) + menuSetReturnToState(STATE_PREVIOUS); + else if(bootMode != BootModeQuiet) + uiPrintBootFailure(); + return false; + } + + if(checkForHIDAbort()) + return false; + + firmLoaded = 1; + + if(fromMenu) + menuSetReturnToState(MENU_STATE_EXIT); + + return true; +} + +static u32 loadFirmSd(const char *filePath) +{ + FIL file; + u32 fileSize; + UINT bytesRead = 0; + FILINFO fileStat; + + if(f_stat(filePath, &fileStat) != FR_OK) + { + uiPrintInfo("Failed to get file status!\n"); + return 0; + } + + fileSize = fileStat.fsize; + + if(f_open(&file, filePath, FA_READ) != FR_OK) + { + uiPrintInfo("Failed to open '%s'!\n", filePath); + return 0; + } + + if(fileSize == 0 || fileSize > FIRM_MAX_SIZE) + { + f_close(&file); + return 0; + } + + if(f_read(&file, (u8*)FIRM_LOAD_ADDR, fileSize, &bytesRead) != FR_OK) + { + uiPrintInfo("Failed to read from file!\n"); + fileSize = 0; + } + + if(bytesRead != fileSize) + fileSize = 0; + + f_close(&file); + + return fileSize; +} + +static u32 loadFirmNandPartition(const char *filePath) +{ + char partName[11]; + void *firmBuf = (void *) FIRM_LOAD_ADDR; + size_t index; + size_t sector; + size_t firmSize; + + strncpy_s(partName, filePath, 11, 11); + + if(!partitionGetIndex(partName, &index)) + return 0; + + partitionGetSectorOffset(index, §or); + + /* get header to figure out the actual firm size */ + if(!dev_decnand->read_sector(sector, 1, firmBuf)) + return 0; + + if(!firm_size(&firmSize)) + return 0; + + /* read the rest */ + + if(!dev_decnand->read_sector(sector + 1, firmSize - 1, firmBuf + 0x200)) + return 0; + + return firmSize; +} + +bool tryLoadFirmware(const char *filepath, bool skipHashCheck, bool printInfo) +{ + u32 fw_size; + + if(!filepath) + return false; + + if(!statFirmware(filepath)) + return false; + + // ee_printf("Loading firmware:\n%s\n\n", filepath); + + /* SD card */ + if(strncmp(filepath, "sdmc:", 5) == 0) + fw_size = loadFirmSd(filepath); + /* NAND */ + else if(strncmp(filepath, "firm1:", 5) == 0) + fw_size = loadFirmNandPartition(filepath); + else + return false; // TODO: Support more devices + + if(fw_size == 0) + return false; + + if(!firm_verify(fw_size, skipHashCheck, printInfo)) return false; + + ((const char**)(ITCM_KERNEL_MIRROR + 0x7470))[0] = ((const char*)(ITCM_KERNEL_MIRROR + 0x7474)); + strncpy_s((void*)(ITCM_KERNEL_MIRROR + 0x7474), filepath, 256, 256); + + return true; +} diff --git a/source/arm9/gui/menu_nand.c b/source/arm9/gui/menu_nand.c new file mode 100644 index 0000000..93b1604 --- /dev/null +++ b/source/arm9/gui/menu_nand.c @@ -0,0 +1,404 @@ +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "types.h" +#include "mem_map.h" +#include "arm9/console.h" +#include "arm9/dev.h" +#include "fatfs/ff.h" +#include "arm9/hid.h" +#include "util.h" +#include "arm9/main.h" +#include "arm9/gui/menu.h" +#include "arm9/timer.h" +#include "arm9/partitions.h" +#include "arm9/firmwriter.h" +#include "arm9/nandimage.h" +#include "arm9/fsutils.h" + + +bool menuDumpNand(const char *filePath) +{ + const u32 sectorCount = dev_rawnand->get_sector_count(); + const u32 sectorBlkSize = 0x400; // 512 KB in sectors + FIL file; + FRESULT fres; + UINT bytesWritten; + u64 bytesFree; + + u8 *buf = (u8*)malloc(sectorBlkSize<<9); + if(!buf) + { // this should never happen. + uiPrintIfVerbose("Out of memory!"); + return false; + } + + uiClearConsoles(); + consoleSelect(&con_top); + + uiPrintCenteredInLine(1, "NAND Backup"); + + uiPrintTextAt(0, 3, "Checking free space on SD card...\n"); + + if(!fsGetFreeSpaceOnDrive("sdmc:", &bytesFree)) + uiPrintError("Failed to get free space!\n"); + + if(bytesFree < sectorCount<<9) + { + uiPrintError("Not enough space on the SD card!\n"); + goto fail; + } + + uiPrintTextAt(0, 4, "Creating image file...\n"); + + if((fres = f_open(&file, filePath, FA_CREATE_ALWAYS | FA_WRITE)) != FR_OK) + { + uiPrintError("Failed to create '%s'! Error: %X\n", filePath, fres); + goto fail; + } + + // Allocate space to make sure the nand image is a contiguous file + if((fres = f_expand(&file, sectorCount<<9, 0)) != FR_OK) + { + uiPrintError("Failed to expand file! Error: %X\n", fres); + f_sync(&file); + f_close(&file); + goto fail; + } + + uiPrintTextAt(0, 4, "Unmounting NAND fs...\n"); + + fsUnmountNandFilesystems(); + + uiPrintTextAt(0, 5, "Dumping NAND...\n"); + uiPrintTextAt(0, 22, "Press B to cancel."); + + /* Main loop */ + + u32 curSector = 0; + u32 curSectorBlkSize; + while(curSector < sectorCount) + { + if(curSector + sectorBlkSize < sectorCount) curSectorBlkSize = sectorBlkSize; + else curSectorBlkSize = sectorCount - curSector; + + if(!dev_rawnand->read_sector(curSector, curSectorBlkSize, buf)) + { + uiPrintError("\nFailed to read sector 0x%"PRIx32"!\n", curSector); + f_sync(&file); + f_close(&file); + goto fail; + } + if((f_write(&file, buf, curSectorBlkSize<<9, &bytesWritten) != FR_OK) || (bytesWritten != curSectorBlkSize<<9)) + { + uiPrintError("\nFailed to write to file!\n"); + f_sync(&file); + f_close(&file); + goto fail; + } + + hidScanInput(); + if(hidKeysDown() & KEY_B) + { + uiPrintTextAt(0, 22, "... canceled. "); + f_sync(&file); + f_close(&file); + goto fail; + } + + curSector += curSectorBlkSize; + + // print current progress information + uiPrintTextAt(1, 20, "\r%"PRId32"/%"PRId32" MB (Sector 0x%"PRIX32"/0x%"PRIX32")", curSector>>11, sectorCount>>11, + curSector, sectorCount); + + uiPrintProgressBar(10, 80, 380, 20, curSector, sectorCount); + + switch(menuUpdateGlobalState()) + { + case MENU_EVENT_HOME_PRESSED: + case MENU_EVENT_POWER_PRESSED: + f_sync(&file); + f_close(&file); + // falls through + case MENU_EVENT_SD_CARD_REMOVED: + menuActState(); + goto fail; + default: + break; + } + + menuActState(); + } + + f_sync(&file); + f_close(&file); + free(buf); + fsRemountNandFilesystems(); + + uiDialog("Finished!\nPress any key to return.", NULL, HID_KEY_MASK_ALL, 1, 0, 0, true); + + menuSetReturnToState(STATE_PREVIOUS); + + return true; + +fail: + free(buf); + fsRemountNandFilesystems(); + + uiPrintTextAt(0, 24, "Press any key to return."); + menuWaitForAnyPadkey(); + + menuSetReturnToState(STATE_PREVIOUS); + + return false; +} + +bool menuRestoreNand(const char *filePath) +{ + const u32 sectorBlkSize = 0x400; // 512 KB in sectors + FIL file; + UINT bytesRead; + + u8 *buf = (u8*)malloc(sectorBlkSize<<9); + if(!buf) + { // this should never happen. + uiPrintIfVerbose("Out of memory!"); + return false; + } + + uiClearConsoles(); + consoleSelect(&con_top); + + uiPrintCenteredInLine(1, "NAND Restore"); + + uiPrintTextAt(0, 3, "Checking backup file...\n"); + + FILINFO fileStat; + if(f_stat(filePath, &fileStat) != FR_OK) + { + uiPrintError("Failed to get file status!\n"); + goto fail; + } + if(fileStat.fsize > dev_rawnand->get_sector_count()<<9) + { + uiPrintError("NAND file is bigger than NAND!\n"); + goto fail; + } + if(fileStat.fsize < 0x200) + { + uiPrintError("NAND file is too small!\n"); + goto fail; + } + + if(f_open(&file, filePath, FA_READ) != FR_OK) + { + uiPrintError("Failed to open '%s'!\n", filePath); + f_close(&file); + goto fail; + } + + if(!isNandImageCompatible(&file)) + { + uiPrintError("NAND file is not compatible with this console!\n"); + f_close(&file); + goto fail; + } + + uiPrintTextAt(0, 4, "Unmounting NAND fs...\n"); + + fsUnmountNandFilesystems(); + + uiPrintTextAt(0, 5, "Restoring...\n"); + uiPrintTextAt(0, 22, "Press B to cancel."); + + /* Main loop */ + + const u32 sectorCount = fileStat.fsize>>9; + u32 curSector = 0; + u32 curSectorBlkSize; + while(curSector < sectorCount) + { + if(curSector + sectorBlkSize < sectorCount) curSectorBlkSize = sectorBlkSize; + else curSectorBlkSize = sectorCount - curSector; + + if(f_read(&file, buf, curSectorBlkSize<<9, &bytesRead) != FR_OK || bytesRead != curSectorBlkSize<<9) + { + uiPrintError("\nFailed to read from file!\n"); + f_close(&file); + goto fail; + } + if(!dev_rawnand->write_sector(curSector, curSectorBlkSize, buf)) + { + uiPrintError("\nFailed to write sector 0x%"PRIX32"!\n", curSector); + f_close(&file); + goto fail; + } + + hidScanInput(); + if(hidKeysDown() & KEY_B) + { + uiPrintTextAt(0, 22, "... canceled. "); + f_close(&file); + goto fail; + } + + curSector += curSectorBlkSize; + + uiPrintTextAt(1, 20, "\r%"PRId32"/%"PRId32" MB (Sector 0x%"PRIX32"/0x%"PRIX32")", curSector>>11, sectorCount>>11, + curSector, sectorCount); + + uiPrintProgressBar(10, 80, 380, 20, curSector, sectorCount); + + switch(menuUpdateGlobalState()) + { + case MENU_EVENT_HOME_PRESSED: + case MENU_EVENT_POWER_PRESSED: + f_close(&file); + // falls through + case MENU_EVENT_SD_CARD_REMOVED: + menuActState(); + goto fail; + default: + break; + } + + menuActState(); + } + + f_close(&file); + free(buf); + fsRemountNandFilesystems(); + + uiPrintTextAt(0, 24, "Finished! Press any key to return."); + menuWaitForAnyPadkey(); + + menuSetReturnToState(STATE_PREVIOUS); + + return true; + +fail: + free(buf); + fsRemountNandFilesystems(); + + uiPrintTextAt(0, 24, "Press any key to return."); + menuWaitForAnyPadkey(); + + menuSetReturnToState(STATE_PREVIOUS); + + return false; +} + +bool menuFlashFirmware(const char *filepath) +{ + const char *partName = "firm1"; // TODO let the user decide + FILINFO fileStat; + size_t fwSize; + size_t index; + size_t sector; + size_t numSectors; + + uiClearConsoles(); + consoleSelect(&con_top); + + uiPrintCenteredInLine(1, "Flash firmware"); + + uiPrintTextAt(0, 3, "Verifying file...\n"); + + if(f_stat(filepath, &fileStat) != FR_OK) + { + uiPrintError("Could not retrieve file status!\n"); + goto fail; + } + + if(!tryLoadFirmware(filepath, false, false) || !firm_size(&fwSize)) + { + uiPrintError("Firmware is invalid or corrupted!\n"); + goto fail; + } + + if(!partitionGetIndex(partName, &index)) + { + uiPrintError("Could not find partition %s!", partName); + goto fail; + } + + partitionGetSectorOffset(index, §or); + + if(!uiDialogYesNo(1, "Proceed", "Cancel", "Flash firmware to %s?", partName)) + { + goto fail; + } + + uiPrintTextAt(0, 4, "Writing...\n"); + + if(!firmwriterInit(sector, fwSize / 0x200, false)) + { + uiPrintError("Failed to start flashing process!"); + goto fail; + } + + for(size_t i=0; i. + */ + +#include +#include +#include +#include "types.h" +#include "mem_map.h" +#include "arm9/console.h" +#include "arm9/dev.h" +#include "fatfs/ff.h" +#include "pxi.h" +#include "arm9/hid.h" +#include "util.h" +#include "arm9/main.h" +#include "arm9/timer.h" +#include "arm9/gui/menu.h" +#include "arm9/nandimage.h" +#include "arm9/config.h" + + +#define MAX_SUBOPTIONS 3 + +static const char *optionStrings[] = { + "Change Boot Mode", + "Setup Boot Option", + // "Setup NAND Image" -- currently unuseed +}; + +static char optionSubStrings[MAX_SUBOPTIONS][0x20]; + +static unsigned int curOptionIndex; +static unsigned int curSubOptionIndex; +static unsigned int curHighlightedSubOptionIndex; +static bool curSubOptionHighlighted; +static unsigned int curSubOptionCount; +static bool subOptionsActive; +static const unsigned int OptionCount = arrayEntries(optionStrings); +static int consoleX, consoleY; + +void calcConsoleMetrics(void); +void rewindConsole(void); +void rewindConsoleX(void); +void handleBootMode(void); +void handleBootOption(void); +void handleNandImage(void); + +bool menuOptions(void) +{ + u32 keys; + + uiClearConsoles(); + consoleSelect(&con_bottom); + + calcConsoleMetrics(); + + curOptionIndex = 0; + curSubOptionIndex = 0; + curHighlightedSubOptionIndex = 0; + curSubOptionCount = 0; + subOptionsActive = false; + curSubOptionHighlighted = false; + + for(;;) + { + + /* Draw screen */ + + rewindConsole(); + + for(unsigned int i=0; i longestLen) + longestLen = curLen; + } + + consoleX = (maxLen - longestLen) / 2; + consoleY = (con_bottom.windowHeight - arrayEntries(optionStrings) - MAX_SUBOPTIONS) / 2; +} + +void rewindConsole(void) +{ + consoleSetCursor(&con_bottom, consoleX, consoleY); + consoleSelect(&con_bottom); +} + +void rewindConsoleX(void) +{ + unsigned int len = con_bottom.windowWidth - con_bottom.cursorX; + while(len--) uiPrintInfo(" "); + + consoleSetCursor(&con_bottom, consoleX, con_bottom.cursorY + 1); +} + +void handleBootMode(void) +{ + if(subOptionsActive) + { + u32 mode = (u32) curSubOptionIndex; + configSetKeyData(KBootMode, &mode); + curHighlightedSubOptionIndex = curSubOptionIndex; + curSubOptionHighlighted = true; + } + else + { + const u32 *mode = (const u32 *)configGetData(KBootMode); + if(mode) + { + curHighlightedSubOptionIndex = *mode; + curSubOptionHighlighted = true; + } + else curSubOptionHighlighted = false; + + strcpy(optionSubStrings[0], "Normal"); + strcpy(optionSubStrings[1], "Quick Boot"); + strcpy(optionSubStrings[2], "Quiet Boot"); + curSubOptionCount = 3; + } +} + +void handleBootOption(void) +{ + if(subOptionsActive) + { + int key = KBootOption1 + (int) curSubOptionIndex; + + // const char *curPath = (const char *) configGetData(key); + + menuSetEnterNextState(MENU_STATE_BROWSER); + menuUpdateGlobalState(); + menuActState(); + const char *path = browseForFile("sdmc:"); + uiClearConsoles(); + + if(path) + configSetKeyData(key, path); + } + else + { + strcpy(optionSubStrings[0], "[Slot 1]"); + strcpy(optionSubStrings[1], "[Slot 2]"); + strcpy(optionSubStrings[2], "[Slot 3]"); + curSubOptionCount = 3; + } +} + +void handleNandImage(void) +{ + if(subOptionsActive) + { + int ret; + int key = KBootOption1NandImage + (int) curSubOptionIndex; + + // const char *curPath = (const char *) configGetData(key); + + menuSetEnterNextState(MENU_STATE_BROWSER); + menuUpdateGlobalState(); + menuActState(); + const char *path = browseForFile("sdmc:"); + uiClearConsoles(); + + if(path) + { + if((ret = validateNandImage(path)) != 0) + { + switch(ret) + { + case NANDIMG_ERROR_BADPATH: + menuPrintPrompt("Unsupported device or filepath!\n"); + break; + case NANDIMG_ERROR_NEXISTS: + menuPrintPrompt("NAND Image file corrupted!\n"); + break; + case NANDIMG_ERROR_NCONTS: + menuPrintPrompt("NAND Image file is not continuous!\n"); + break; + default: + break; + } + } + else configSetKeyData(key, path); + } + } + else + { + strcpy(optionSubStrings[0], "[Slot 1] Image"); + strcpy(optionSubStrings[1], "[Slot 2] Image"); + strcpy(optionSubStrings[2], "[Slot 3] Image"); + curSubOptionCount = 3; + } +} diff --git a/source/arm9/gui/menu_update.c b/source/arm9/gui/menu_update.c new file mode 100644 index 0000000..bda1a34 --- /dev/null +++ b/source/arm9/gui/menu_update.c @@ -0,0 +1,172 @@ +/* + * This file is part of fastboot 3DS + * Copyright (C) 2017 derrek, profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include "types.h" +#include "mem_map.h" +#include "arm9/console.h" +#include "arm9/dev.h" +#include "arm9/partitions.h" +#include "fatfs/ff.h" +#include "arm9/hid.h" +#include "util.h" +#include "arm9/firmwriter.h" +#include "arm9/main.h" +#include "arm9/gui/menu.h" +#include "arm9/timer.h" + +#define MIN_UPDATE_SIZE 0x1000 + +static const char *updateFilePath = "sdmc:/fastboot3ds.bin"; +static const char *installPath = "firm0"; + +bool menuUpdateLoader() +{ + u32 *updateBuffer = (u32 *) FIRM_LOAD_ADDR; + FILINFO fileStat; + size_t fwSize; + size_t index; + size_t sector; + + uiClearConsoles(); + consoleSelect(&con_top); + + uiPrintCenteredInLine(1, "fastboot Updater"); + + uiPrintTextAt(0, 3, "Checking update file...\n"); + + if(f_stat(updateFilePath, &fileStat) != FR_OK) + { + uiPrintError("Could not find fastboot3ds.bin!\n"); + goto fail; + } + + // update file is just a firmware + if(!tryLoadFirmware(updateFilePath, false, false)) + { + uiPrintError("Update file is corrupted!\n"); + goto fail; + } + + if(!firm_size(&fwSize) || fwSize < MIN_UPDATE_SIZE || fwSize % 0x200) + { + // this would be really odd. + uiPrintError("Invalid update file!\n"); + goto fail; + } + + // verify fastboot magic + if(memcmp((void*)updateBuffer+0x200, "FASTBOOT 3DS ", 0x10) != 0) + { + uiPrintError("Not an update file!\n"); + goto fail; + } + + /* check version */ + u32 updateVersion = *(u32 *)((void*)updateBuffer + 0x210); + + if(updateVersion < ((u32)VERS_MAJOR<<16 | VERS_MINOR)) + { + uiPrintError("Update version is below current version!\n"); + goto fail; + } + + if(updateVersion == ((u32)VERS_MAJOR<<16 | VERS_MINOR)) + { + uiPrintWarning("You are on this version already.\n"); + goto fail; + } + + if(!uiDialogYesNo(1, "Update", "Cancel", "Update to v%" PRIu16 ".%" PRIu16, + updateVersion>>16, updateVersion & 0xFFFFu)) + { + goto fail; + } + + /* NOTE: We assume sighax is installed on firm0 */ + + if(!partitionGetIndex(installPath, &index)) + { + uiPrintError("Could not find partition %s!", installPath); + goto fail; + } + + partitionGetSectorOffset(index, §or); + // ee_printf("writing to offset 0x%x aka 0x%x\n", sector, sector<<9); + + uiPrintTextAt(0, 9, "Updating...\n"); + + if(!firmwriterInit(sector, fwSize / 0x200, true)) + { + uiPrintError("Failed to start update process!"); + goto fail; + } + + size_t numSectors; + + for(size_t i=1; i. + */ + +#include +#include +#include +#include +#include "types.h" +#include "arm9/fmt.h" +#include "util.h" +#include "arm9/hid.h" +#include "pxi.h" +#include "arm9/timer.h" +#include "arm9/main.h" +#include "arm9/gui/ui.h" +#include "arm9/console.h" +#include "arm9/splash.h" +#include "banner_spla.h" +#include "bootwarning_spla.h" +#include "bootfail_spla.h" + + +static u8 randomColor; +static bool verbose = false; + +static const void *bannerData = banner_spla; +static const void *bootWarningData = bootwarning_spla; +static const void *bootFailureData = bootfail_spla; + + + +static void consoleMainInit() +{ + /* Initialize console for both screens using the two different PrintConsole we have defined */ + consoleInit(SCREEN_TOP, &con_top, true); + consoleSetWindow(&con_top, 1, 1, con_top.windowWidth - 2, con_top.windowHeight - 2); + + // randomize color + randomColor = (rng_get_byte() % 6) + 1; + + consoleInit(SCREEN_LOW, &con_bottom, true); + + consoleSelect(&con_top); +} + +void uiDrawSplashScreen() +{ + drawSplashscreen(bannerData, -1, -1); + TIMER_sleep(600); +} + +void uiInit() +{ + consoleMainInit(); +} + +void uiDrawConsoleWindow() +{ + drawConsoleWindow(&con_top, 2, randomColor); +} + +static void clearConsoles() +{ + consoleSelect(&con_bottom); + consoleClear(); + consoleSelect(&con_top); + consoleClear(); +} + +void uiClearConsoles() +{ + clearConsoles(); +} + +void clearConsole(int which) +{ + if(which) + consoleSelect(&con_top); + else + consoleSelect(&con_bottom); + + consoleClear(); +} + +void uiSetVerboseMode(bool verb) +{ + verbose = verb; +} + +bool uiGetVerboseMode() +{ + return verbose; +} + +/* TODO: Should look similar to uiShowMessageWindow */ +bool uiDialogYesNo(int screen, const char *textYes, const char *textNo, const char *const format, ...) +{ + char tmp[256]; + char lastline[256]; + u32 keys; + + va_list args; + va_start(args, format); + ee_vsnprintf(tmp, 256, format, args); + va_end(args); + + ee_snprintf(lastline, 256, "(A): %s (B): %s", textYes, textNo); + + /* Print dialog */ + keys = uiDialog(tmp, lastline, KEY_A | KEY_B, screen, 0, 0, true); + + /* Evaluate user input */ + if(keys == KEY_A) + return true; + + // KEY_B + return false; +} + +void uiPrintIfVerbose(const char *const format, ...) +{ + if(verbose) + { + char tmp[256]; + + va_list args; + va_start(args, format); + ee_vsnprintf(tmp, 256, format, args); + va_end(args); + + ee_printf(tmp); + } +} + +/* Prints a given text at the current position */ +void uiPrint(const char *const format, unsigned int color, bool centered, ...) +{ + const unsigned int width = (unsigned int)consoleGet()->consoleWidth; + char tmp[256]; + + va_list args; + va_start(args, centered); + ee_vsnprintf(tmp, 556, format, args); + va_end(args); + + if(centered) + { + // Warning. The string must be <= the console width here! + size_t len = strlen(tmp); + ee_printf("\x1B[%um%*s%s\x1B[0m", color, (width - len) / 2, "", tmp); + } + else + { + ee_printf("\x1B[%um%s\x1B[0m", color, tmp); + } +} + +void uiPrintCenteredInLine(unsigned int y, const char *const format, ...) +{ + const unsigned int width = (unsigned int)consoleGet()->consoleWidth; + char tmp[width + 1]; + + va_list args; + va_start(args, format); + ee_vsnprintf(tmp, width + 1, format, args); + va_end(args); + + size_t len = strlen(tmp); + ee_printf("\x1b[%u;%uH", y, 0); + ee_printf("%*s%s\n", (width - len) / 2, "", tmp); +} + +/* Prints a given text at a certain position in the current window */ +void uiPrintTextAt(unsigned int x, unsigned int y, const char *const format, ...) +{ + char tmp[256]; + + va_list args; + va_start(args, format); + ee_vsnprintf(tmp, 256, format, args); + va_end(args); + + ee_printf("\x1b[%u;%uH%s", y, x, tmp); +} + +/* Prints a given text surrounded by a graphical window */ +/* centered in the middle of the screen. */ +/* Waits for the user to press any button, after that the */ +/* original framebuffer gets restored */ +u32 uiDialog(const char *const format, const char *const lastLine, u32 waitKeys, + int screen, unsigned int x, unsigned int y, bool centered, ...) +{ + char tmp[256]; + + va_list args; + va_start(args, centered); + ee_vsnprintf(tmp, 256, format, args); + va_end(args); + + char *ptr = tmp; + unsigned int lines = lastLine ? 5 : 3, longestLine = 2, curLen = 2; + while(*ptr) + { + switch(*ptr) + { + case '\n': + lines++; + curLen = 2; + break; + default: + curLen++; + } + if(curLen > longestLine) longestLine = curLen; + + ptr++; + } + + if(lastLine) + { + unsigned int lastLineLen = strlen(lastLine) + 2; + if(lastLineLen > longestLine) longestLine = lastLineLen; + } + + PrintConsole *prevCon = consoleGet(); + PrintConsole windowCon; + consoleInit(screen, &windowCon, false); + + if(centered) + { + x = (windowCon.windowWidth / 2) - (longestLine / 2); + y = (windowCon.windowHeight / 2) - (lines / 2); + } + consoleSetWindow(&windowCon, x, 30 - y - lines, longestLine, lines); + + u8 *fbBackup = (u8*)malloc(screen ? SCREEN_SIZE_TOP : SCREEN_SIZE_SUB); + u32 keys = 0; + if(fbBackup) + { + u16 *fb = consoleGet()->frameBuffer; + + // TODO: Optimize to only backup what will be overwritten. I'm lazy. + memcpy(fbBackup, fb, screen ? SCREEN_SIZE_TOP : SCREEN_SIZE_SUB); + + ee_printf("\x1B[37m\x1B[40m\x1B[2J\n"); + + const char *linePtr = tmp; + while(1) + { + unsigned int length = 0; + while(linePtr[length] != '\n' && linePtr[length] != '\0') length++; + ee_printf(" %.*s\n", length, linePtr); + if(*(linePtr + length) == '\0') break; + linePtr += length + 1; + } + + if(lastLine) + ee_printf("\x1b[%u;%uH%s", lines - 2, (longestLine - strlen(lastLine)) / 2, lastLine); + + const u16 color = consoleGetRGB565Color(randomColor); + for(u32 xx = x * 8 + 1; xx < x * 8 + (longestLine * 8) - 1; xx++) + { + fb[xx * SCREEN_HEIGHT_SUB + (y * 8)] = color; + fb[xx * SCREEN_HEIGHT_SUB + (y * 8) + (lines * 8) - 1] = color; + } + for(u32 xx = x * 8 + 3; xx < x * 8 + (longestLine * 8) - 3; xx++) + { + fb[xx * SCREEN_HEIGHT_SUB + (y * 8) + 2] = color; + fb[xx * SCREEN_HEIGHT_SUB + (y * 8) + (lines * 8) - 3] = color; + } + for(u32 yy = y * 8; yy < y * 8 + (lines * 8); yy++) + { + fb[x * 8 * SCREEN_HEIGHT_SUB + yy] = color; + fb[(x * 8 + (longestLine * 8) - 1) * SCREEN_HEIGHT_SUB + yy] = color; + } + for(u32 yy = y * 8 + 2; yy < y * 8 + (lines * 8) - 2; yy++) + { + fb[(x * 8 + 2) * SCREEN_HEIGHT_SUB + yy] = color; + fb[(x * 8 + (longestLine * 8) - 3) * SCREEN_HEIGHT_SUB + yy] = color; + } + + if(waitKeys) + { + do + { + hidScanInput(); + } while(!(keys = hidKeysDown() & waitKeys)); + } + + memcpy(fb, fbBackup, screen ? SCREEN_SIZE_TOP : SCREEN_SIZE_SUB); + } + free(fbBackup); // free() checks for NULL + + consoleSelect(prevCon); + + return keys; +} + +void uiPrintDevModeRequirement() +{ + // This should use uiShowMessageWindow some day... + uiPrintError("This feature is blocked!"); + uiPrintError("You must enable Developer Mode to use it."); + + do { + hidScanInput(); + u32 keys = hidKeysDown() & HID_KEY_MASK_ALL; + if(keys == KEY_A) + break; + } while(1); +} + +void uiPrintProgressBar(unsigned int x, unsigned int y, unsigned int w, + unsigned int h, unsigned int cur, unsigned int max) +{ + u16 *fb = consoleGet()->frameBuffer; + const u16 color = consoleGetFgColor(); + + + for(u32 xx = x + 1; xx < x + w - 1; xx++) + { + fb[xx * SCREEN_HEIGHT_TOP + y] = color; + fb[xx * SCREEN_HEIGHT_TOP + y + h - 1] = color; + } + + for(u32 yy = y; yy < y + h; yy++) + { + fb[x * SCREEN_HEIGHT_TOP + yy] = color; + fb[(x + w - 1) * SCREEN_HEIGHT_TOP + yy] = color; + } + + for(u32 xx = x + 2; xx < (u32)(((float)(x + w - 2) / max) * cur); xx++) + { + for(u32 yy = y + 2; yy < y + h - 2; yy++) + { + fb[xx * SCREEN_HEIGHT_TOP + yy] = color; + } + } +} + +void uiPrintBootWarning() +{ + drawSplashscreen(bootWarningData, -1, 10); + TIMER_sleep(400); +} + +void uiPrintBootFailure() +{ + drawSplashscreen(bootFailureData, -1, 10); + TIMER_sleep(400); +} + +bool uiCheckHomePressed(u32 msTimeout) +{ + u32 curMs; + bool successFlag; + + /* Check PXI Response register */ + u32 replyCode = PXI_tryRecvWord(&successFlag); + + curMs = 0; + + do { + + while(successFlag) + { + if(replyCode == PXI_RPL_HOME_PRESSED || + replyCode == PXI_RPL_HOME_HELD) + { + return true; + } + // maybe there's more..? + replyCode = PXI_tryRecvWord(&successFlag); + } + + TIMER_sleep(1); + curMs++; + + } while(curMs < msTimeout); + + /* timed out ... */ + + return false; +} diff --git a/source/arm9/main.c b/source/arm9/main.c index dd48f1b..3a102dc 100644 --- a/source/arm9/main.c +++ b/source/arm9/main.c @@ -33,7 +33,7 @@ #include "arm9/firm.h" #include "arm9/config.h" #include "arm9/timer.h" -#include "arm9/menu.h" +#include "arm9/gui/menu.h" #include "arm9/main.h" #include "arm9/start.h" #include "cache.h" diff --git a/source/arm9/menu.c b/source/arm9/menu.c deleted file mode 100644 index 29d6441..0000000 --- a/source/arm9/menu.c +++ /dev/null @@ -1,481 +0,0 @@ -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include "types.h" -#include "mem_map.h" -#include "util.h" -#include "pxi.h" -#include "arm9/console.h" -#include "arm9/dev.h" -#include "fatfs/ff.h" -#include "fatfs/diskio.h" -#include "arm9/fsutils.h" -#include "arm9/hid.h" -#include "arm9/main.h" -#include "arm9/menu.h" -#include "arm9/menu_firmloader.h" -#include "arm9/timer.h" -#include "arm9/interrupt.h" -#include "arm9/config.h" - - -const menu_state_options menu_main = { - 6, - { - {"Continue boot", MENU_STATE_FIRM_LAUNCH_SETTINGS}, - {"Launch Firmware...", MENU_STATE_BROWSER}, - {"NAND tools...", MENU_STATE_NAND_MENU}, - {"Options...", MENU_STATE_OPTIONS_MENU}, - {"Update", MENU_STATE_UPDATE}, - {"Credits", MENU_STATE_CREDITS}, - } -}; - -const menu_state_options menu_nand = { - 3, - { - {"Backup NAND", MENU_STATE_NAND_BACKUP}, - {"Restore NAND", MENU_STATE_NAND_RESTORE}, - {"Flash Firmware", MENU_STATE_NAND_FLASH_FIRM} - } -}; - -const menu_state_options menu_loadfw = { - 2, - { - {"Load from SD card", MENU_STATE_BROWSER}, - {"Load firm1", 0}, - } -}; - -// menu_state_type -> menu_state_options instance -const menu_state_options *options_lookup[] = { - &menu_main, // STATE_MAIN - &menu_nand, // STATE_NAND_MENU - &menu_loadfw, // MENU_STATE_LOAD_FIRM -}; - -enum menu_state_type menu_state; -enum menu_state_type menu_next_state; - -enum menu_state_type menu_previous_states[8]; -int menu_previous_states_count; - -int menu_event_state; -static bool waitForVBlank; - -static void menuRunOnce(int state); - -static void menu_main_draw_top() -{ - const char *const sd_res[3] = {"\x1B[33mNot inserted ", "\x1B[31mFS init failed", "\x1B[32mOK "}; - const char *const nand_res[2] = {"\x1B[31mInit failed: ", "\x1B[32mOK "}; - const char *const nand_drives[3] = {"twln ", "twlp ", "nand"}; - const char *const boot_res[4] = {"Not attempted", "Not found", "Failed", "Skipped"}; - - consoleSelect(&con_top); - - uiPrintCenteredInLine(1, "fastboot 3DS " VERS_STRING); - - uiPrintTextAt(1, 4, "Model: %s", bootInfo.model); - uiPrintTextAt(1, 5, "\x1B[33m%s\e[0m", bootInfo.bootEnv); - uiPrintTextAt(1, 6, "\x1B[32m(%s Mode)\x1B[0m", bootInfo.mode); - uiPrintTextAt(1, 8, "SD card status: %s\x1B[0m", sd_res[bootInfo.sd_status]); - uiPrintTextAt(1, 9, "NAND status: %s\x1B[0m", nand_res[bootInfo.nand_status == 7]); - if(bootInfo.nand_status != 7) - { - for(u32 i = 0; i < 3; i++) - { - if(!(bootInfo.nand_status>>i & 1u)) uiPrintError(nand_drives[i]); - } - } - uiPrintTextAt(1, 10, "Wifi flash status: %s\e[0m", nand_res[bootInfo.wififlash_status]); - - if(bootInfo.numBootOptionsAttempted != 0) - { - uiPrintTextAt(1, 15, "Firm launch statistics:"); - uiPrintTextAt(1, 16, "[Slot1]: %s", boot_res[bootInfo.bootOptionResults[0]]); - uiPrintTextAt(1, 17, "[Slot2]: %s", boot_res[bootInfo.bootOptionResults[1]]); - uiPrintTextAt(1, 18, "[Slot3]: %s", boot_res[bootInfo.bootOptionResults[2]]); - } -} - -void menuSetVBlank(bool enable) -{ - if(enable) - { - IRQ_registerHandler(IRQ_TIMER_0, NULL); - TIMER_start(TIMER_0, TIMER_PRESCALER_64, TIMER_FREQ_64(60.0f), true); - waitForVBlank = true; - } - else - { - waitForVBlank = false; - TIMER_stop(TIMER_0); - IRQ_unregisterHandler(IRQ_TIMER_0); - } -} - -int enter_menu(int initial_state) -{ - u32 keys; - int cursor_pos; - const char* path; - const char *firmPath = NULL; - - menu_event_state = MENU_EVENT_NONE; - - cursor_pos = 0; - - uiClearConsoles(); - uiDrawConsoleWindow(); - - menuSetVBlank(true); - - // caller requested to enter a submenu? - if(initial_state != MENU_STATE_MAIN) - { - menuRunOnce(initial_state); - goto exitAndLaunchFirm; - } - - menuSetEnterNextState(MENU_STATE_MAIN); - menuActState(); - - - // Menu main loop - for(;;) - { - const menu_state_options *cur_options = options_lookup[menu_state]; - - switch(menu_state) - { - case MENU_STATE_MAIN: - case MENU_STATE_NAND_MENU: - - hidScanInput(); - keys = hidKeysDown(); - - menu_main_draw_top(); - - // print all the options of the current state - consoleSelect(&con_bottom); - for(int i=0; i < cur_options->count; i++) - uiPrintTextAt(9, 4+i, "%s\e[0m %s\n", cursor_pos == i ? "\x1B[33m*" : " ", - cur_options->options[i].name); - - if(keys & KEY_DUP) - { - if(cursor_pos == 0) cursor_pos = cur_options->count - 1; // jump to the last option - else cursor_pos--; - } - else if(keys & KEY_DDOWN) - { - if(cursor_pos == cur_options->count - 1) cursor_pos = 0; // jump to the first option - else cursor_pos++; - } - else if(keys & KEY_A) // select option - { - menuSetEnterNextState(cur_options->options[cursor_pos].state); - cursor_pos = 0; - } - else if(keys & KEY_B) // go back - { - if(menuSetReturnToState(STATE_PREVIOUS)) - cursor_pos = 0; - } - break; - - case MENU_STATE_NAND_BACKUP: - menuSetVBlank(false); - menuDumpNand("sdmc:/nand.bin"); - uiClearConsoles(); - menuSetVBlank(true); - break; - - case MENU_STATE_NAND_RESTORE: - menuSetVBlank(false); - menuRestoreNand("sdmc:/nand.bin"); - uiClearConsoles(); - menuSetVBlank(true); - break; - - case MENU_STATE_NAND_FLASH_FIRM: - path = browseForFile("sdmc:"); - uiClearConsoles(); - if(!path) - break; - menuSetVBlank(false); - menuFlashFirmware(path); - uiClearConsoles(); - menuSetVBlank(true); - break; - - case MENU_STATE_OPTIONS_MENU: - menuOptions(); - uiClearConsoles(); - break; - - case MENU_STATE_FIRM_LAUNCH: - if(!menuLaunchFirm(firmPath, false)) - uiClearConsoles(); - break; - - case MENU_STATE_FIRM_LAUNCH_SETTINGS: - if(!tryLoadFirmwareFromSettings(true)) - uiClearConsoles(); - break; - - case MENU_STATE_BROWSER: - path = browseForFile("sdmc:"); - uiClearConsoles(); - firmPath = path; - if(!path) // no file selected - break; - menuSetEnterNextState(MENU_STATE_FIRM_LAUNCH); - break; - - case MENU_STATE_UPDATE: - menuSetVBlank(false); - menuUpdateLoader(); - uiClearConsoles(); - menuSetVBlank(true); - break; - - case MENU_STATE_CREDITS: - menuCredits(); - uiClearConsoles(); - break; - - case MENU_STATE_EXIT: - goto exitAndLaunchFirm; - break; - - /* this should never happen */ - default: uiPrintIfVerbose("OOPS!\n"); break; - } - - switch(menuUpdateGlobalState()) - { - case MENU_EVENT_HOME_PRESSED: - break; - case MENU_EVENT_STATE_CHANGE: - if(menu_next_state == MENU_STATE_EXIT) - goto exitAndLaunchFirm; - else - clearConsole(0); - break; - default: - break; - } - - menuActState(); - } - -exitAndLaunchFirm: - menuSetVBlank(false); - - return 0; -} - -static void menuRunOnce(int state) -{ - switch(state) - { - case MENU_STATE_FIRM_LAUNCH_SETTINGS: - if(!tryLoadFirmwareFromSettings(true)) - uiClearConsoles(); - break; - - default: - panic(); - } -} - -bool menuSetReturnToState(int state) -{ - int i; - - if(state == STATE_PREVIOUS) - { - if(menu_previous_states_count <= 1) - return false; - state = menu_previous_states[menu_previous_states_count - 1]; - menu_previous_states_count--; - } - else if(state == MENU_STATE_EXIT) - { - menu_previous_states_count = 0; - } - else - { - for(i=menu_previous_states_count; i>0; i--) - { - if(menu_previous_states[i] == state) - { - menu_previous_states_count = i; - } - } - } - - menu_next_state = state; - // emit event - menu_event_state = MENU_EVENT_STATE_CHANGE; - - return true; -} - -void menuSetEnterNextState(int state) -{ - if(menu_previous_states_count >= 8) - panic(); - - menu_previous_states[menu_previous_states_count] = menu_state; - menu_previous_states_count++; - - menu_next_state = state; - // emit event - menu_event_state = MENU_EVENT_STATE_CHANGE; -} - -int menuUpdateGlobalState(void) -{ - int retcode = MENU_EVENT_NONE; - - // Later if PXI interrupts are implemented we need an - // interrupt handler here which can tell the timer and - // PXI interrupts apart. - if(waitForVBlank) waitForInterrupt(); - - - /* Check PXI Response register */ - - bool successFlag; - u32 replyCode = PXI_tryRecvWord(&successFlag); - - while(successFlag) - { - switch(replyCode) - { - case PXI_RPL_HOME_PRESSED: - case PXI_RPL_HOME_HELD: - retcode = MENU_EVENT_HOME_PRESSED; - break; - case PXI_RPL_POWER_PRESSED: - retcode = MENU_EVENT_POWER_PRESSED; - break; - default: - panic(); - } - // maybe there's more..? - replyCode = PXI_tryRecvWord(&successFlag); - } - - - /* Check for HW changes */ - - bool sd_active = dev_sdcard->is_active(); - if(!bootInfo.sd_status && sd_active) - { - retcode = MENU_EVENT_SD_CARD_INSERTED; - } - else if(bootInfo.sd_status && !sd_active) - { - retcode = MENU_EVENT_SD_CARD_REMOVED; - } - - - // Submenu wants to call a new menu? - if(menu_state != menu_next_state) - retcode = MENU_EVENT_STATE_CHANGE; - - // did anything happen? - if(retcode != MENU_EVENT_NONE) - menu_event_state = retcode; - - return retcode; -} - -void menuActState(void) -{ - switch(menu_event_state) - { - case MENU_EVENT_NONE: - break; // nothing to do, boring. - case MENU_EVENT_POWER_PRESSED: - power_off_safe(); - break; - case MENU_EVENT_HOME_PRESSED: - menuSetReturnToState(MENU_STATE_MAIN); - break; - case MENU_EVENT_STATE_CHANGE: - menu_state = menu_next_state; - break; - case MENU_EVENT_SD_CARD_INSERTED: - fsUnmountNandFilesystems(); - if(fsMountSdmc()) bootInfo.sd_status = 2; - else bootInfo.sd_status = 1; - bootInfo.nand_status = fsRemountNandFilesystems(); - // also try to load saved settings - loadConfigFile(); - break; - case MENU_EVENT_SD_CARD_REMOVED: - bootInfo.sd_status = 0; - dev_sdcard->close(); - f_mount(NULL, "sdmc:", 1); - break; - default: - break; - } - - menu_event_state = MENU_EVENT_NONE; -} - -void menuPrintPrompt(const char *text) -{ - // TODO: use uiShowMessageWindow - uiPrintInfo(text); -} - -void menuWaitForAnyPadkey() -{ - int keys; - bool finished = false; - - do { - hidScanInput(); - keys = hidKeysDown(); - if(keys & HID_KEY_MASK_ALL) - finished = true; - - switch(menuUpdateGlobalState()) - { - case MENU_EVENT_HOME_PRESSED: - case MENU_EVENT_POWER_PRESSED: - finished = true; - default: - break; - } - - menuActState(); - - } while(!finished); -} diff --git a/source/arm9/menu_credits.c b/source/arm9/menu_credits.c deleted file mode 100644 index edc706e..0000000 --- a/source/arm9/menu_credits.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include "types.h" -#include "mem_map.h" -#include "arm9/console.h" -#include "arm9/dev.h" -#include "fatfs/ff.h" -#include "pxi.h" -#include "arm9/hid.h" -#include "util.h" -#include "arm9/main.h" -#include "arm9/timer.h" -#include "arm9/menu.h" -#include "arm9/ui.h" - -void menuCredits(void) -{ - u32 keys; - unsigned i = 3; - - uiClearConsoles(); - - uiPrintCenteredInLine(1, "Credits"); - - uiPrintCenteredInLine(i++, "Main developers:"); - uiPrintCenteredInLine(i++, "derrek"); - uiPrintCenteredInLine(i++, "profi200"); - i++; - uiPrintCenteredInLine(i++, "Thanks to:"); - uiPrintCenteredInLine(i++, "yellows8"); - uiPrintCenteredInLine(i++, "plutoo"); - uiPrintCenteredInLine(i++, "smea"); - uiPrintCenteredInLine(i++, "Normmatt (for sdmmc code)"); - uiPrintCenteredInLine(i++, "WinterMute (for console code)"); - uiPrintCenteredInLine(i+=2, "... everyone who contributed to 3dbrew.org"); - - for(;;) - { - /* Handle HID */ - hidScanInput(); - keys = hidKeysDown(); - - if(keys & KEY_B) - { - goto done; - } - - switch(menuUpdateGlobalState()) - { - case MENU_EVENT_HOME_PRESSED: - case MENU_EVENT_POWER_PRESSED: - case MENU_EVENT_STATE_CHANGE: - goto done; - default: - break; - } - - menuActState(); - } - -done: - - menuActState(); - menuSetReturnToState(STATE_PREVIOUS); -} diff --git a/source/arm9/menu_filebrowse.c b/source/arm9/menu_filebrowse.c deleted file mode 100644 index 930bd0e..0000000 --- a/source/arm9/menu_filebrowse.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include "types.h" -#include "arm9/fmt.h" -#include "mem_map.h" -#include "fatfs/ff.h" -#include "arm9/console.h" -#include "arm9/main.h" -#include "arm9/menu.h" -#include "arm9/interrupt.h" -#include "arm9/dev.h" -#include "util.h" -#include "arm9/hid.h" - - -#define MAX_CACHED_ENTRIES 0x400 -#define MAX_ENTRY_SIZE FF_MAX_LFN + 2 -#define MAX_NEW_ENTRIES 0x20 -#define MAX_PATH_LENGTH 0x400 -#define MAX_PATH_DEPTH 0x20 - -typedef struct { - char name[MAX_ENTRY_SIZE]; - bool isDir; -} EntryType; - -static EntryType *dirEntries; // [MAX_ENTRY_SIZE][MAX_CACHED_ENTRIES]; -static DIR curDirectory; -static char curPath[MAX_PATH_LENGTH]; -static u32 curEntriesCount; - -// This stack is used to recover the original cursor position -// in the dir entries list when returning from a subdir. -static u32 indexStack[MAX_PATH_DEPTH]; -static u32 *indexStackPtr = indexStack; - -/* This scans the current directory for entries. - Only MAX_NEW_ENTRIES will be added at maximum. - This also updates the curEntriesCount. - Returns true if new entries were added. */ -static bool scanDirectory() -{ - FRESULT res; - static FILINFO fno; - unsigned i = 0; - - for(i=0; i bufSize - 1) - { - end = in + entryLen; - strcpy(&out[3], end - bufSize + 3); - memcpy(out, "...", 3); - } - else strcpy(out, in); - } - else - { - if(entryLen > bufSize - 1) - { - memcpy(out, in, bufSize - 4); - memcpy(&out[bufSize - 5], "...", 4); - } - else strcpy(out, in); - } -} - -static void updateCursor(int cursor_pos, int old_cursor_pos, int maxEntries) -{ - // clear old '*' char - consoleSetCursor(&con_bottom, 0, (old_cursor_pos % (maxEntries)) + 1); - uiPrintInfo(" "); - - consoleSetCursor(&con_bottom, 0, (cursor_pos % (maxEntries)) + 1); - uiPrintInfo("*"); -} - -static void updateScreen(int cursor_pos, bool dirChange) -{ - // we don't want ugly line breaks in our list - const int maxLen = con_bottom.windowWidth - 1; - const int maxEntries = con_bottom.windowHeight - 1; - - static int old_cursor_pos; - - consoleSelect(&con_bottom); - - if(!dirChange && (cursor_pos < maxEntries) && (old_cursor_pos < maxEntries)) - { - updateCursor(cursor_pos, old_cursor_pos, maxEntries); - old_cursor_pos = cursor_pos; - return; - } - - old_cursor_pos = cursor_pos; - - char entry [maxLen]; // should be safe - unsigned start, end; - - consoleClear(); - - // print the current path in the first line - formatEntry(entry, curPath, (u32) maxLen - 3, false); - uiPrintInfo("%s :", entry); - - if(cursor_pos < (int) maxEntries) - { - start = 0; - end = min(maxEntries, curEntriesCount); - } - else - { - start = cursor_pos - maxEntries + 1; - end = min(cursor_pos + 1, curEntriesCount); - } - - for(unsigned i = start; i < end; i++) - { - formatEntry(entry, dirEntries[i].name, (u32) maxLen - 2, true); - uiPrintInfo("\n%s %s", i == (unsigned) cursor_pos ? "*" : " ", entry); - } -} - -const char *browseForFile(const char *basePath) -{ - FRESULT res; - u32 keys; - u32 cursor_pos = 0; - bool fileSelected = false; - int returnState = STATE_PREVIOUS; - - curEntriesCount = 0; - indexStackPtr = indexStack; - - uiClearConsoles(); - - dirEntries = (EntryType *) malloc(MAX_CACHED_ENTRIES * sizeof(EntryType)); - if(!dirEntries) - { - menuSetReturnToState(STATE_PREVIOUS); - menuUpdateGlobalState(); - menuActState(); - return NULL; - } - - if(basePath) - strncpy_s(curPath, basePath, sizeof(curPath) - 1, sizeof(curPath)); - else - strncpy_s(curPath, "sdmc:", sizeof(curPath) - 1, sizeof(curPath)); - - /* check if we have to wait for the user */ - if(!dev_sdcard->is_active()) - { - menuPrintPrompt("Waiting for SD card to be inserted...\nPress B to exit.\n"); - - do { - hidScanInput(); - keys = hidKeysDown(); - if(keys & KEY_B) - goto fail; - - switch(menuUpdateGlobalState()) - { - case MENU_EVENT_HOME_PRESSED: - case MENU_EVENT_POWER_PRESSED: - goto fail; - default: - break; - } - - menuActState(); - - } while(!dev_sdcard->is_active()); - - uiClearConsoles(); - } - - res = f_opendir(&curDirectory, curPath); - if (res != FR_OK) - { - menuPrintPrompt("Failed to enter directory.\n"); - menuWaitForAnyPadkey(); - free(dirEntries); - menuSetReturnToState(STATE_PREVIOUS); - menuUpdateGlobalState(); - menuActState(); - return NULL; - } - - // do an initial scan - scanDirectory(); - - updateScreen((int)cursor_pos, true); - - for(;;) - { - hidScanInput(); - keys = hidKeysDown()/* | hidKeysHeld()*/; - - if(curEntriesCount != 0) - { - if(keys & KEY_DUP || keys & KEY_DLEFT) - { - if(cursor_pos != 0) - { - cursor_pos -= keys & KEY_DLEFT ? min(cursor_pos, 8) : 1; - updateScreen((int)cursor_pos, false); - } - } - - else if(keys & KEY_DDOWN || keys & KEY_DRIGHT) - { - // we reached the end of our entries list? - u32 old_pos = cursor_pos; - cursor_pos += keys & KEY_DRIGHT ? 8 : 1; - if(cursor_pos >= curEntriesCount) - { - scanDirectory(); - // we got more entries? - if(curEntriesCount > cursor_pos) - cursor_pos = min(cursor_pos, curEntriesCount - 1); - else - cursor_pos = curEntriesCount - 1; - if(cursor_pos != old_pos) - updateScreen((int)cursor_pos, false); - } - else - { - updateScreen((int)cursor_pos, false); - } - } - - else if(keys & KEY_A) // select entry - { - if(dirEntries[cursor_pos].isDir) // it's a dir - { - if(descendPath(cursor_pos, dirEntries[cursor_pos].name)) - { - f_closedir(&curDirectory); - res = f_opendir(&curDirectory, curPath); - if (res != FR_OK) - ascendPath(); - else - { - // fetch new entries - curEntriesCount = 0; - scanDirectory(); - cursor_pos = 0; - updateScreen((int)cursor_pos, true); - } - } - } - else // we got our file! - { - if(strlen(dirEntries[cursor_pos].name) + strlen(curPath) + 2 > sizeof(curPath)) - goto fail; // TODO? Path too long. - strcat(curPath, "/"); - strcat(curPath, dirEntries[cursor_pos].name); - fileSelected = true; - menuSetReturnToState(STATE_PREVIOUS); - } - } - } - if(keys & KEY_B) // go back - { - if(indexStackPtr != indexStack) - { - cursor_pos = ascendPath(); - f_closedir(&curDirectory); - - res = f_opendir(&curDirectory, curPath); - if (res != FR_OK) - goto fail; - curEntriesCount = 0; - - do { - if(!scanDirectory()) - goto fail; - } while(curEntriesCount <= cursor_pos); - - updateScreen((int)cursor_pos, true); - } - else - menuSetReturnToState(STATE_PREVIOUS); - } - - switch(menuUpdateGlobalState()) - { - case MENU_EVENT_HOME_PRESSED: - returnState = MENU_STATE_MAIN; - goto fail; - case MENU_EVENT_POWER_PRESSED: - case MENU_EVENT_SD_CARD_REMOVED: - goto fail; - case MENU_EVENT_STATE_CHANGE: - if(!fileSelected) goto fail; - else goto done; - default: - break; - } - - menuActState(); - - } - -done: - - f_closedir(&curDirectory); - free(dirEntries); - - menuSetReturnToState(returnState); - menuActState(); - - - return curPath; - -fail: - - f_closedir(&curDirectory); - free(dirEntries); - - menuSetReturnToState(returnState); - menuActState(); - - return NULL; -} diff --git a/source/arm9/menu_firmloader.c b/source/arm9/menu_firmloader.c deleted file mode 100644 index e4e19f7..0000000 --- a/source/arm9/menu_firmloader.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include "types.h" -#include "mem_map.h" -#include "arm9/console.h" -#include "arm9/dev.h" -#include "fatfs/ff.h" -#include "arm9/hid.h" -#include "util.h" -#include "pxi.h" -#include "arm9/main.h" -#include "arm9/timer.h" -#include "arm9/menu.h" -#include "arm9/firm.h" -#include "arm9/config.h" -#include "arm9/partitions.h" -#include "arm9/fsutils.h" - -static int firmLoaded = 0; - -bool isFirmLoaded(void) -{ - return firmLoaded != 0; -} - -bool menuLaunchFirm(const char *filePath, bool quick) -{ - if(!filePath) - return false; - - firmLoaded = 0; - - uiClearConsoles(); - consoleSelect(&con_top); - - uiPrintCenteredInLine(1, "FIRM Loader"); - - menuUpdateGlobalState(); - - if(!tryLoadFirmware(filePath, false, true)) - { - if(!quick) - { - uiPrintError("Bad firmware.\n"); - uiPrintInfo("Press any key...\n"); - - menuWaitForAnyPadkey(); - } - goto fail; - } - - firmLoaded = 1; - - uiPrint("Firmware verification SUCCESS!", 32, true); - - menuSetReturnToState(MENU_STATE_EXIT); - - return true; - -fail: - - uiPrintCenteredInLine(25, "FIRM-Launch aborted."); - - TIMER_sleep(500); - - firmLoaded = 0; - - menuSetReturnToState(STATE_PREVIOUS); - - return false; -} - -// Does very basic checks whether the firmware actually exists. -static bool statFirmware(const char *filePath) -{ - FILINFO fileStat; - // u32 fileSize; - - fsEnsureMounted(filePath); - - if(strncmp(filePath, "sdmc:", 5) == 0) - { - if(f_stat(filePath, &fileStat) != FR_OK) - return false; - - /* - fileSize = fileStat.fsize; - - if(fileSize == 0 || fileSize > FIRM_MAX_SIZE) - return false; - */ - - return true; - } - else if(strncmp(filePath, "firm1:", 5) == 0) - return true; // this is always available - - // unknown mountpoint - return false; -} - -static bool checkForHIDAbort() -{ - // give it 10 ms to check whether home button was pressed - return uiCheckHomePressed(10); -} - -bool tryLoadFirmwareFromSettings(bool fromMenu) -{ - const char *path; - int keyBootOption, bootMode, keyPad; - int i; - u32 padValue, expectedPadValue; - - if(fromMenu) - uiClearConsoles(); - - consoleSelect(&con_top); - - firmLoaded = 0; - - bootInfo.numBootOptionsAttempted = 0; - bootInfo.bootOptionResults[0] = BO_NOT_ATTEMPTED; - bootInfo.bootOptionResults[1] = BO_NOT_ATTEMPTED; - bootInfo.bootOptionResults[2] = BO_NOT_ATTEMPTED; - - if(fromMenu) - uiPrintCenteredInLine(1, "Loading FIRM from settings\n"); - - // No Boot Option set up? - if(!configDataExist(KBootOption1) && - !configDataExist(KBootOption2) && - !configDataExist(KBootOption3)) - { - if(fromMenu) - { - menuPrintPrompt("No Boot-Option set up yet.\nGo to 'Options' to choose a file.\n"); - menuWaitForAnyPadkey(); - menuSetReturnToState(STATE_PREVIOUS); - } - return false; - } - - const u32 *temp = configGetData(KBootMode); - bootMode = temp? *temp : BootModeNormal; - - keyBootOption = KBootOption1; - keyPad = KBootOption1Buttons; - - for(i=0; i<3; i++, keyBootOption++, keyPad++) - { - if(checkForHIDAbort()) - return false; - - bootInfo.numBootOptionsAttempted ++; - - path = (const char *)configGetData(keyBootOption); - if(path) - { - if(fromMenu) - uiPrintInfo("Boot Option #%i:\n%s\n", i + 1, path); - - // check if fw still exists - if(!statFirmware(path)) - { - if(fromMenu) - uiPrintInfo("Couldn't find firmware...\n"); - bootInfo.bootOptionResults[i] = BO_NOT_FOUND; - goto try_next; - } - - // check pad value - const u32 *dataPtr; - dataPtr = (const u32 *)configGetData(keyPad); - if(dataPtr) - { - expectedPadValue = *dataPtr; - - if(expectedPadValue != 0) - { - hidScanInput(); - padValue = HID_KEY_MASK_ALL & hidKeysHeld(); - if(padValue != expectedPadValue) - { - if(fromMenu) - { - uiPrintInfo("Skipping, right buttons are not pressed.\n"); - uiPrintInfo("%" PRIX32 " %" PRIX32 "\n", padValue, expectedPadValue); - } - bootInfo.bootOptionResults[i] = BO_SKIPPED; - goto try_next; - } - } - } - - if(fromMenu) - { - if(menuLaunchFirm(path, false)) - break; - else - { - uiClearConsoles(); - consoleSelect(&con_top); - } - } - else - { - if(tryLoadFirmware(path, false, false)) - break; - else - { - if(bootMode != BootModeQuiet) - uiPrintBootWarning(); - } - } - - bootInfo.bootOptionResults[i] = BO_FAILED; - } - else - continue; - -try_next: - - if(fromMenu) - TIMER_sleep(300); - - // ... we failed, try next one - } - - if(i >= 3) - { - if(fromMenu) - menuSetReturnToState(STATE_PREVIOUS); - else if(bootMode != BootModeQuiet) - uiPrintBootFailure(); - return false; - } - - if(checkForHIDAbort()) - return false; - - firmLoaded = 1; - - if(fromMenu) - menuSetReturnToState(MENU_STATE_EXIT); - - return true; -} - -static u32 loadFirmSd(const char *filePath) -{ - FIL file; - u32 fileSize; - UINT bytesRead = 0; - FILINFO fileStat; - - if(f_stat(filePath, &fileStat) != FR_OK) - { - uiPrintInfo("Failed to get file status!\n"); - return 0; - } - - fileSize = fileStat.fsize; - - if(f_open(&file, filePath, FA_READ) != FR_OK) - { - uiPrintInfo("Failed to open '%s'!\n", filePath); - return 0; - } - - if(fileSize == 0 || fileSize > FIRM_MAX_SIZE) - { - f_close(&file); - return 0; - } - - if(f_read(&file, (u8*)FIRM_LOAD_ADDR, fileSize, &bytesRead) != FR_OK) - { - uiPrintInfo("Failed to read from file!\n"); - fileSize = 0; - } - - if(bytesRead != fileSize) - fileSize = 0; - - f_close(&file); - - return fileSize; -} - -static u32 loadFirmNandPartition(const char *filePath) -{ - char partName[11]; - void *firmBuf = (void *) FIRM_LOAD_ADDR; - size_t index; - size_t sector; - size_t firmSize; - - strncpy_s(partName, filePath, 11, 11); - - if(!partitionGetIndex(partName, &index)) - return 0; - - partitionGetSectorOffset(index, §or); - - /* get header to figure out the actual firm size */ - if(!dev_decnand->read_sector(sector, 1, firmBuf)) - return 0; - - if(!firm_size(&firmSize)) - return 0; - - /* read the rest */ - - if(!dev_decnand->read_sector(sector + 1, firmSize - 1, firmBuf + 0x200)) - return 0; - - return firmSize; -} - -bool tryLoadFirmware(const char *filepath, bool skipHashCheck, bool printInfo) -{ - u32 fw_size; - - if(!filepath) - return false; - - if(!statFirmware(filepath)) - return false; - - // ee_printf("Loading firmware:\n%s\n\n", filepath); - - /* SD card */ - if(strncmp(filepath, "sdmc:", 5) == 0) - fw_size = loadFirmSd(filepath); - /* NAND */ - else if(strncmp(filepath, "firm1:", 5) == 0) - fw_size = loadFirmNandPartition(filepath); - else - return false; // TODO: Support more devices - - if(fw_size == 0) - return false; - - if(!firm_verify(fw_size, skipHashCheck, printInfo)) return false; - - ((const char**)(ITCM_KERNEL_MIRROR + 0x7470))[0] = ((const char*)(ITCM_KERNEL_MIRROR + 0x7474)); - strncpy_s((void*)(ITCM_KERNEL_MIRROR + 0x7474), filepath, 256, 256); - - return true; -} diff --git a/source/arm9/menu_nand.c b/source/arm9/menu_nand.c deleted file mode 100644 index 83ad071..0000000 --- a/source/arm9/menu_nand.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include "types.h" -#include "mem_map.h" -#include "arm9/console.h" -#include "arm9/dev.h" -#include "fatfs/ff.h" -#include "arm9/hid.h" -#include "util.h" -#include "arm9/main.h" -#include "arm9/menu.h" -#include "arm9/timer.h" -#include "arm9/partitions.h" -#include "arm9/firmwriter.h" -#include "arm9/nandimage.h" -#include "arm9/fsutils.h" - - -bool menuDumpNand(const char *filePath) -{ - const u32 sectorCount = dev_rawnand->get_sector_count(); - const u32 sectorBlkSize = 0x400; // 512 KB in sectors - FIL file; - FRESULT fres; - UINT bytesWritten; - u64 bytesFree; - - u8 *buf = (u8*)malloc(sectorBlkSize<<9); - if(!buf) - { // this should never happen. - uiPrintIfVerbose("Out of memory!"); - return false; - } - - uiClearConsoles(); - consoleSelect(&con_top); - - uiPrintCenteredInLine(1, "NAND Backup"); - - uiPrintTextAt(0, 3, "Checking free space on SD card...\n"); - - if(!fsGetFreeSpaceOnDrive("sdmc:", &bytesFree)) - uiPrintError("Failed to get free space!\n"); - - if(bytesFree < sectorCount<<9) - { - uiPrintError("Not enough space on the SD card!\n"); - goto fail; - } - - uiPrintTextAt(0, 4, "Creating image file...\n"); - - if((fres = f_open(&file, filePath, FA_CREATE_ALWAYS | FA_WRITE)) != FR_OK) - { - uiPrintError("Failed to create '%s'! Error: %X\n", filePath, fres); - goto fail; - } - - // Allocate space to make sure the nand image is a contiguous file - if((fres = f_expand(&file, sectorCount<<9, 0)) != FR_OK) - { - uiPrintError("Failed to expand file! Error: %X\n", fres); - f_sync(&file); - f_close(&file); - goto fail; - } - - uiPrintTextAt(0, 4, "Unmounting NAND fs...\n"); - - fsUnmountNandFilesystems(); - - uiPrintTextAt(0, 5, "Dumping NAND...\n"); - uiPrintTextAt(0, 22, "Press B to cancel."); - - /* Main loop */ - - u32 curSector = 0; - u32 curSectorBlkSize; - while(curSector < sectorCount) - { - if(curSector + sectorBlkSize < sectorCount) curSectorBlkSize = sectorBlkSize; - else curSectorBlkSize = sectorCount - curSector; - - if(!dev_rawnand->read_sector(curSector, curSectorBlkSize, buf)) - { - uiPrintError("\nFailed to read sector 0x%"PRIx32"!\n", curSector); - f_sync(&file); - f_close(&file); - goto fail; - } - if((f_write(&file, buf, curSectorBlkSize<<9, &bytesWritten) != FR_OK) || (bytesWritten != curSectorBlkSize<<9)) - { - uiPrintError("\nFailed to write to file!\n"); - f_sync(&file); - f_close(&file); - goto fail; - } - - hidScanInput(); - if(hidKeysDown() & KEY_B) - { - uiPrintTextAt(0, 22, "... canceled. "); - f_sync(&file); - f_close(&file); - goto fail; - } - - curSector += curSectorBlkSize; - - // print current progress information - uiPrintTextAt(1, 20, "\r%"PRId32"/%"PRId32" MB (Sector 0x%"PRIX32"/0x%"PRIX32")", curSector>>11, sectorCount>>11, - curSector, sectorCount); - - uiPrintProgressBar(10, 80, 380, 20, curSector, sectorCount); - - switch(menuUpdateGlobalState()) - { - case MENU_EVENT_HOME_PRESSED: - case MENU_EVENT_POWER_PRESSED: - f_sync(&file); - f_close(&file); - // falls through - case MENU_EVENT_SD_CARD_REMOVED: - menuActState(); - goto fail; - default: - break; - } - - menuActState(); - } - - f_sync(&file); - f_close(&file); - free(buf); - fsRemountNandFilesystems(); - - uiDialog("Finished!\nPress any key to return.", NULL, HID_KEY_MASK_ALL, 1, 0, 0, true); - - menuSetReturnToState(STATE_PREVIOUS); - - return true; - -fail: - free(buf); - fsRemountNandFilesystems(); - - uiPrintTextAt(0, 24, "Press any key to return."); - menuWaitForAnyPadkey(); - - menuSetReturnToState(STATE_PREVIOUS); - - return false; -} - -bool menuRestoreNand(const char *filePath) -{ - const u32 sectorBlkSize = 0x400; // 512 KB in sectors - FIL file; - UINT bytesRead; - - u8 *buf = (u8*)malloc(sectorBlkSize<<9); - if(!buf) - { // this should never happen. - uiPrintIfVerbose("Out of memory!"); - return false; - } - - uiClearConsoles(); - consoleSelect(&con_top); - - uiPrintCenteredInLine(1, "NAND Restore"); - - uiPrintTextAt(0, 3, "Checking backup file...\n"); - - FILINFO fileStat; - if(f_stat(filePath, &fileStat) != FR_OK) - { - uiPrintError("Failed to get file status!\n"); - goto fail; - } - if(fileStat.fsize > dev_rawnand->get_sector_count()<<9) - { - uiPrintError("NAND file is bigger than NAND!\n"); - goto fail; - } - if(fileStat.fsize < 0x200) - { - uiPrintError("NAND file is too small!\n"); - goto fail; - } - - if(f_open(&file, filePath, FA_READ) != FR_OK) - { - uiPrintError("Failed to open '%s'!\n", filePath); - f_close(&file); - goto fail; - } - - if(!isNandImageCompatible(&file)) - { - uiPrintError("NAND file is not compatible with this console!\n"); - f_close(&file); - goto fail; - } - - uiPrintTextAt(0, 4, "Unmounting NAND fs...\n"); - - fsUnmountNandFilesystems(); - - uiPrintTextAt(0, 5, "Restoring...\n"); - uiPrintTextAt(0, 22, "Press B to cancel."); - - /* Main loop */ - - const u32 sectorCount = fileStat.fsize>>9; - u32 curSector = 0; - u32 curSectorBlkSize; - while(curSector < sectorCount) - { - if(curSector + sectorBlkSize < sectorCount) curSectorBlkSize = sectorBlkSize; - else curSectorBlkSize = sectorCount - curSector; - - if(f_read(&file, buf, curSectorBlkSize<<9, &bytesRead) != FR_OK || bytesRead != curSectorBlkSize<<9) - { - uiPrintError("\nFailed to read from file!\n"); - f_close(&file); - goto fail; - } - if(!dev_rawnand->write_sector(curSector, curSectorBlkSize, buf)) - { - uiPrintError("\nFailed to write sector 0x%"PRIX32"!\n", curSector); - f_close(&file); - goto fail; - } - - hidScanInput(); - if(hidKeysDown() & KEY_B) - { - uiPrintTextAt(0, 22, "... canceled. "); - f_close(&file); - goto fail; - } - - curSector += curSectorBlkSize; - - uiPrintTextAt(1, 20, "\r%"PRId32"/%"PRId32" MB (Sector 0x%"PRIX32"/0x%"PRIX32")", curSector>>11, sectorCount>>11, - curSector, sectorCount); - - uiPrintProgressBar(10, 80, 380, 20, curSector, sectorCount); - - switch(menuUpdateGlobalState()) - { - case MENU_EVENT_HOME_PRESSED: - case MENU_EVENT_POWER_PRESSED: - f_close(&file); - // falls through - case MENU_EVENT_SD_CARD_REMOVED: - menuActState(); - goto fail; - default: - break; - } - - menuActState(); - } - - f_close(&file); - free(buf); - fsRemountNandFilesystems(); - - uiPrintTextAt(0, 24, "Finished! Press any key to return."); - menuWaitForAnyPadkey(); - - menuSetReturnToState(STATE_PREVIOUS); - - return true; - -fail: - free(buf); - fsRemountNandFilesystems(); - - uiPrintTextAt(0, 24, "Press any key to return."); - menuWaitForAnyPadkey(); - - menuSetReturnToState(STATE_PREVIOUS); - - return false; -} - -bool menuFlashFirmware(const char *filepath) -{ - const char *partName = "firm1"; // TODO let the user decide - FILINFO fileStat; - size_t fwSize; - size_t index; - size_t sector; - size_t numSectors; - - uiClearConsoles(); - consoleSelect(&con_top); - - uiPrintCenteredInLine(1, "Flash firmware"); - - uiPrintTextAt(0, 3, "Verifying file...\n"); - - if(f_stat(filepath, &fileStat) != FR_OK) - { - uiPrintError("Could not retrieve file status!\n"); - goto fail; - } - - if(!tryLoadFirmware(filepath, false, false) || !firm_size(&fwSize)) - { - uiPrintError("Firmware is invalid or corrupted!\n"); - goto fail; - } - - if(!partitionGetIndex(partName, &index)) - { - uiPrintError("Could not find partition %s!", partName); - goto fail; - } - - partitionGetSectorOffset(index, §or); - - if(!uiDialogYesNo(1, "Proceed", "Cancel", "Flash firmware to %s?", partName)) - { - goto fail; - } - - uiPrintTextAt(0, 4, "Writing...\n"); - - if(!firmwriterInit(sector, fwSize / 0x200, false)) - { - uiPrintError("Failed to start flashing process!"); - goto fail; - } - - for(size_t i=0; i. - */ - -#include -#include -#include -#include "types.h" -#include "mem_map.h" -#include "arm9/console.h" -#include "arm9/dev.h" -#include "fatfs/ff.h" -#include "pxi.h" -#include "arm9/hid.h" -#include "util.h" -#include "arm9/main.h" -#include "arm9/timer.h" -#include "arm9/menu.h" -#include "arm9/nandimage.h" -#include "arm9/config.h" - - -#define MAX_SUBOPTIONS 3 - -static const char *optionStrings[] = { - "Change Boot Mode", - "Setup Boot Option", - // "Setup NAND Image" -- currently unuseed -}; - -static char optionSubStrings[MAX_SUBOPTIONS][0x20]; - -static unsigned int curOptionIndex; -static unsigned int curSubOptionIndex; -static unsigned int curHighlightedSubOptionIndex; -static bool curSubOptionHighlighted; -static unsigned int curSubOptionCount; -static bool subOptionsActive; -static const unsigned int OptionCount = arrayEntries(optionStrings); -static int consoleX, consoleY; - -void calcConsoleMetrics(void); -void rewindConsole(void); -void rewindConsoleX(void); -void handleBootMode(void); -void handleBootOption(void); -void handleNandImage(void); - -bool menuOptions(void) -{ - u32 keys; - - uiClearConsoles(); - consoleSelect(&con_bottom); - - calcConsoleMetrics(); - - curOptionIndex = 0; - curSubOptionIndex = 0; - curHighlightedSubOptionIndex = 0; - curSubOptionCount = 0; - subOptionsActive = false; - curSubOptionHighlighted = false; - - for(;;) - { - - /* Draw screen */ - - rewindConsole(); - - for(unsigned int i=0; i longestLen) - longestLen = curLen; - } - - consoleX = (maxLen - longestLen) / 2; - consoleY = (con_bottom.windowHeight - arrayEntries(optionStrings) - MAX_SUBOPTIONS) / 2; -} - -void rewindConsole(void) -{ - consoleSetCursor(&con_bottom, consoleX, consoleY); - consoleSelect(&con_bottom); -} - -void rewindConsoleX(void) -{ - unsigned int len = con_bottom.windowWidth - con_bottom.cursorX; - while(len--) uiPrintInfo(" "); - - consoleSetCursor(&con_bottom, consoleX, con_bottom.cursorY + 1); -} - -void handleBootMode(void) -{ - if(subOptionsActive) - { - u32 mode = (u32) curSubOptionIndex; - configSetKeyData(KBootMode, &mode); - curHighlightedSubOptionIndex = curSubOptionIndex; - curSubOptionHighlighted = true; - } - else - { - const u32 *mode = (const u32 *)configGetData(KBootMode); - if(mode) - { - curHighlightedSubOptionIndex = *mode; - curSubOptionHighlighted = true; - } - else curSubOptionHighlighted = false; - - strcpy(optionSubStrings[0], "Normal"); - strcpy(optionSubStrings[1], "Quick Boot"); - strcpy(optionSubStrings[2], "Quiet Boot"); - curSubOptionCount = 3; - } -} - -void handleBootOption(void) -{ - if(subOptionsActive) - { - int key = KBootOption1 + (int) curSubOptionIndex; - - // const char *curPath = (const char *) configGetData(key); - - menuSetEnterNextState(MENU_STATE_BROWSER); - menuUpdateGlobalState(); - menuActState(); - const char *path = browseForFile("sdmc:"); - uiClearConsoles(); - - if(path) - configSetKeyData(key, path); - } - else - { - strcpy(optionSubStrings[0], "[Slot 1]"); - strcpy(optionSubStrings[1], "[Slot 2]"); - strcpy(optionSubStrings[2], "[Slot 3]"); - curSubOptionCount = 3; - } -} - -void handleNandImage(void) -{ - if(subOptionsActive) - { - int ret; - int key = KBootOption1NandImage + (int) curSubOptionIndex; - - // const char *curPath = (const char *) configGetData(key); - - menuSetEnterNextState(MENU_STATE_BROWSER); - menuUpdateGlobalState(); - menuActState(); - const char *path = browseForFile("sdmc:"); - uiClearConsoles(); - - if(path) - { - if((ret = validateNandImage(path)) != 0) - { - switch(ret) - { - case NANDIMG_ERROR_BADPATH: - menuPrintPrompt("Unsupported device or filepath!\n"); - break; - case NANDIMG_ERROR_NEXISTS: - menuPrintPrompt("NAND Image file corrupted!\n"); - break; - case NANDIMG_ERROR_NCONTS: - menuPrintPrompt("NAND Image file is not continuous!\n"); - break; - default: - break; - } - } - else configSetKeyData(key, path); - } - } - else - { - strcpy(optionSubStrings[0], "[Slot 1] Image"); - strcpy(optionSubStrings[1], "[Slot 2] Image"); - strcpy(optionSubStrings[2], "[Slot 3] Image"); - curSubOptionCount = 3; - } -} diff --git a/source/arm9/menu_update.c b/source/arm9/menu_update.c deleted file mode 100644 index 8f83fe2..0000000 --- a/source/arm9/menu_update.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * This file is part of fastboot 3DS - * Copyright (C) 2017 derrek, profi200 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include "types.h" -#include "mem_map.h" -#include "arm9/console.h" -#include "arm9/dev.h" -#include "arm9/partitions.h" -#include "fatfs/ff.h" -#include "arm9/hid.h" -#include "util.h" -#include "arm9/firmwriter.h" -#include "arm9/main.h" -#include "arm9/menu.h" -#include "arm9/timer.h" - -#define MIN_UPDATE_SIZE 0x1000 - -static const char *updateFilePath = "sdmc:/fastboot3ds.bin"; -static const char *installPath = "firm0"; - -bool menuUpdateLoader() -{ - u32 *updateBuffer = (u32 *) FIRM_LOAD_ADDR; - FILINFO fileStat; - size_t fwSize; - size_t index; - size_t sector; - - uiClearConsoles(); - consoleSelect(&con_top); - - uiPrintCenteredInLine(1, "fastboot Updater"); - - uiPrintTextAt(0, 3, "Checking update file...\n"); - - if(f_stat(updateFilePath, &fileStat) != FR_OK) - { - uiPrintError("Could not find fastboot3ds.bin!\n"); - goto fail; - } - - // update file is just a firmware - if(!tryLoadFirmware(updateFilePath, false, false)) - { - uiPrintError("Update file is corrupted!\n"); - goto fail; - } - - if(!firm_size(&fwSize) || fwSize < MIN_UPDATE_SIZE || fwSize % 0x200) - { - // this would be really odd. - uiPrintError("Invalid update file!\n"); - goto fail; - } - - // verify fastboot magic - if(memcmp((void*)updateBuffer+0x200, "FASTBOOT 3DS ", 0x10) != 0) - { - uiPrintError("Not an update file!\n"); - goto fail; - } - - /* check version */ - u32 updateVersion = *(u32 *)((void*)updateBuffer + 0x210); - - if(updateVersion < ((u32)VERS_MAJOR<<16 | VERS_MINOR)) - { - uiPrintError("Update version is below current version!\n"); - goto fail; - } - - if(updateVersion == ((u32)VERS_MAJOR<<16 | VERS_MINOR)) - { - uiPrintWarning("You are on this version already.\n"); - goto fail; - } - - if(!uiDialogYesNo(1, "Update", "Cancel", "Update to v%" PRIu16 ".%" PRIu16, - updateVersion>>16, updateVersion & 0xFFFFu)) - { - goto fail; - } - - /* NOTE: We assume sighax is installed on firm0 */ - - if(!partitionGetIndex(installPath, &index)) - { - uiPrintError("Could not find partition %s!", installPath); - goto fail; - } - - partitionGetSectorOffset(index, §or); - // ee_printf("writing to offset 0x%x aka 0x%x\n", sector, sector<<9); - - uiPrintTextAt(0, 9, "Updating...\n"); - - if(!firmwriterInit(sector, fwSize / 0x200, true)) - { - uiPrintError("Failed to start update process!"); - goto fail; - } - - size_t numSectors; - - for(size_t i=1; i. - */ - -#include -#include -#include -#include -#include "types.h" -#include "arm9/fmt.h" -#include "util.h" -#include "arm9/hid.h" -#include "pxi.h" -#include "arm9/timer.h" -#include "arm9/main.h" -#include "arm9/ui.h" -#include "arm9/console.h" -#include "arm9/splash.h" -#include "banner_spla.h" -#include "bootwarning_spla.h" -#include "bootfail_spla.h" - - -static u8 randomColor; -static bool verbose = false; - -static const void *bannerData = banner_spla; -static const void *bootWarningData = bootwarning_spla; -static const void *bootFailureData = bootfail_spla; - - - -static void consoleMainInit() -{ - /* Initialize console for both screens using the two different PrintConsole we have defined */ - consoleInit(SCREEN_TOP, &con_top, true); - consoleSetWindow(&con_top, 1, 1, con_top.windowWidth - 2, con_top.windowHeight - 2); - - // randomize color - randomColor = (rng_get_byte() % 6) + 1; - - consoleInit(SCREEN_LOW, &con_bottom, true); - - consoleSelect(&con_top); -} - -void uiDrawSplashScreen() -{ - drawSplashscreen(bannerData, -1, -1); - TIMER_sleep(600); -} - -void uiInit() -{ - consoleMainInit(); -} - -void uiDrawConsoleWindow() -{ - drawConsoleWindow(&con_top, 2, randomColor); -} - -static void clearConsoles() -{ - consoleSelect(&con_bottom); - consoleClear(); - consoleSelect(&con_top); - consoleClear(); -} - -void uiClearConsoles() -{ - clearConsoles(); -} - -void clearConsole(int which) -{ - if(which) - consoleSelect(&con_top); - else - consoleSelect(&con_bottom); - - consoleClear(); -} - -void uiSetVerboseMode(bool verb) -{ - verbose = verb; -} - -bool uiGetVerboseMode() -{ - return verbose; -} - -/* TODO: Should look similar to uiShowMessageWindow */ -bool uiDialogYesNo(int screen, const char *textYes, const char *textNo, const char *const format, ...) -{ - char tmp[256]; - char lastline[256]; - u32 keys; - - va_list args; - va_start(args, format); - ee_vsnprintf(tmp, 256, format, args); - va_end(args); - - ee_snprintf(lastline, 256, "(A): %s (B): %s", textYes, textNo); - - /* Print dialog */ - keys = uiDialog(tmp, lastline, KEY_A | KEY_B, screen, 0, 0, true); - - /* Evaluate user input */ - if(keys == KEY_A) - return true; - - // KEY_B - return false; -} - -void uiPrintIfVerbose(const char *const format, ...) -{ - if(verbose) - { - char tmp[256]; - - va_list args; - va_start(args, format); - ee_vsnprintf(tmp, 256, format, args); - va_end(args); - - ee_printf(tmp); - } -} - -/* Prints a given text at the current position */ -void uiPrint(const char *const format, unsigned int color, bool centered, ...) -{ - const unsigned int width = (unsigned int)consoleGet()->consoleWidth; - char tmp[256]; - - va_list args; - va_start(args, centered); - ee_vsnprintf(tmp, 556, format, args); - va_end(args); - - if(centered) - { - // Warning. The string must be <= the console width here! - size_t len = strlen(tmp); - ee_printf("\x1B[%um%*s%s\x1B[0m", color, (width - len) / 2, "", tmp); - } - else - { - ee_printf("\x1B[%um%s\x1B[0m", color, tmp); - } -} - -void uiPrintCenteredInLine(unsigned int y, const char *const format, ...) -{ - const unsigned int width = (unsigned int)consoleGet()->consoleWidth; - char tmp[width + 1]; - - va_list args; - va_start(args, format); - ee_vsnprintf(tmp, width + 1, format, args); - va_end(args); - - size_t len = strlen(tmp); - ee_printf("\x1b[%u;%uH", y, 0); - ee_printf("%*s%s\n", (width - len) / 2, "", tmp); -} - -/* Prints a given text at a certain position in the current window */ -void uiPrintTextAt(unsigned int x, unsigned int y, const char *const format, ...) -{ - char tmp[256]; - - va_list args; - va_start(args, format); - ee_vsnprintf(tmp, 256, format, args); - va_end(args); - - ee_printf("\x1b[%u;%uH%s", y, x, tmp); -} - -/* Prints a given text surrounded by a graphical window */ -/* centered in the middle of the screen. */ -/* Waits for the user to press any button, after that the */ -/* original framebuffer gets restored */ -u32 uiDialog(const char *const format, const char *const lastLine, u32 waitKeys, - int screen, unsigned int x, unsigned int y, bool centered, ...) -{ - char tmp[256]; - - va_list args; - va_start(args, centered); - ee_vsnprintf(tmp, 256, format, args); - va_end(args); - - char *ptr = tmp; - unsigned int lines = lastLine ? 5 : 3, longestLine = 2, curLen = 2; - while(*ptr) - { - switch(*ptr) - { - case '\n': - lines++; - curLen = 2; - break; - default: - curLen++; - } - if(curLen > longestLine) longestLine = curLen; - - ptr++; - } - - if(lastLine) - { - unsigned int lastLineLen = strlen(lastLine) + 2; - if(lastLineLen > longestLine) longestLine = lastLineLen; - } - - PrintConsole *prevCon = consoleGet(); - PrintConsole windowCon; - consoleInit(screen, &windowCon, false); - - if(centered) - { - x = (windowCon.windowWidth / 2) - (longestLine / 2); - y = (windowCon.windowHeight / 2) - (lines / 2); - } - consoleSetWindow(&windowCon, x, 30 - y - lines, longestLine, lines); - - u8 *fbBackup = (u8*)malloc(screen ? SCREEN_SIZE_TOP : SCREEN_SIZE_SUB); - u32 keys = 0; - if(fbBackup) - { - u16 *fb = consoleGet()->frameBuffer; - - // TODO: Optimize to only backup what will be overwritten. I'm lazy. - memcpy(fbBackup, fb, screen ? SCREEN_SIZE_TOP : SCREEN_SIZE_SUB); - - ee_printf("\x1B[37m\x1B[40m\x1B[2J\n"); - - const char *linePtr = tmp; - while(1) - { - unsigned int length = 0; - while(linePtr[length] != '\n' && linePtr[length] != '\0') length++; - ee_printf(" %.*s\n", length, linePtr); - if(*(linePtr + length) == '\0') break; - linePtr += length + 1; - } - - if(lastLine) - ee_printf("\x1b[%u;%uH%s", lines - 2, (longestLine - strlen(lastLine)) / 2, lastLine); - - const u16 color = consoleGetRGB565Color(randomColor); - for(u32 xx = x * 8 + 1; xx < x * 8 + (longestLine * 8) - 1; xx++) - { - fb[xx * SCREEN_HEIGHT_SUB + (y * 8)] = color; - fb[xx * SCREEN_HEIGHT_SUB + (y * 8) + (lines * 8) - 1] = color; - } - for(u32 xx = x * 8 + 3; xx < x * 8 + (longestLine * 8) - 3; xx++) - { - fb[xx * SCREEN_HEIGHT_SUB + (y * 8) + 2] = color; - fb[xx * SCREEN_HEIGHT_SUB + (y * 8) + (lines * 8) - 3] = color; - } - for(u32 yy = y * 8; yy < y * 8 + (lines * 8); yy++) - { - fb[x * 8 * SCREEN_HEIGHT_SUB + yy] = color; - fb[(x * 8 + (longestLine * 8) - 1) * SCREEN_HEIGHT_SUB + yy] = color; - } - for(u32 yy = y * 8 + 2; yy < y * 8 + (lines * 8) - 2; yy++) - { - fb[(x * 8 + 2) * SCREEN_HEIGHT_SUB + yy] = color; - fb[(x * 8 + (longestLine * 8) - 3) * SCREEN_HEIGHT_SUB + yy] = color; - } - - if(waitKeys) - { - do - { - hidScanInput(); - } while(!(keys = hidKeysDown() & waitKeys)); - } - - memcpy(fb, fbBackup, screen ? SCREEN_SIZE_TOP : SCREEN_SIZE_SUB); - } - free(fbBackup); // free() checks for NULL - - consoleSelect(prevCon); - - return keys; -} - -void uiPrintDevModeRequirement() -{ - // This should use uiShowMessageWindow some day... - uiPrintError("This feature is blocked!"); - uiPrintError("You must enable Developer Mode to use it."); - - do { - hidScanInput(); - u32 keys = hidKeysDown() & HID_KEY_MASK_ALL; - if(keys == KEY_A) - break; - } while(1); -} - -void uiPrintProgressBar(unsigned int x, unsigned int y, unsigned int w, - unsigned int h, unsigned int cur, unsigned int max) -{ - u16 *fb = consoleGet()->frameBuffer; - const u16 color = consoleGetFgColor(); - - - for(u32 xx = x + 1; xx < x + w - 1; xx++) - { - fb[xx * SCREEN_HEIGHT_TOP + y] = color; - fb[xx * SCREEN_HEIGHT_TOP + y + h - 1] = color; - } - - for(u32 yy = y; yy < y + h; yy++) - { - fb[x * SCREEN_HEIGHT_TOP + yy] = color; - fb[(x + w - 1) * SCREEN_HEIGHT_TOP + yy] = color; - } - - for(u32 xx = x + 2; xx < (u32)(((float)(x + w - 2) / max) * cur); xx++) - { - for(u32 yy = y + 2; yy < y + h - 2; yy++) - { - fb[xx * SCREEN_HEIGHT_TOP + yy] = color; - } - } -} - -void uiPrintBootWarning() -{ - drawSplashscreen(bootWarningData, -1, 10); - TIMER_sleep(400); -} - -void uiPrintBootFailure() -{ - drawSplashscreen(bootFailureData, -1, 10); - TIMER_sleep(400); -} - -bool uiCheckHomePressed(u32 msTimeout) -{ - u32 curMs; - bool successFlag; - - /* Check PXI Response register */ - u32 replyCode = PXI_tryRecvWord(&successFlag); - - curMs = 0; - - do { - - while(successFlag) - { - if(replyCode == PXI_RPL_HOME_PRESSED || - replyCode == PXI_RPL_HOME_HELD) - { - return true; - } - // maybe there's more..? - replyCode = PXI_tryRecvWord(&successFlag); - } - - TIMER_sleep(1); - curMs++; - - } while(curMs < msTimeout); - - /* timed out ... */ - - return false; -}