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;
-}