diff --git a/nyx/Makefile b/nyx/Makefile
index 7420cb7..a41dcfd 100644
--- a/nyx/Makefile
+++ b/nyx/Makefile
@@ -24,7 +24,7 @@ OBJS = $(addprefix $(BUILDDIR)/$(TARGET)/, \
start.o exception_handlers.o \
nyx.o heap.o \
gfx.o \
- gui.o gui_info.o gui_tools.o gui_options.o gui_emmc_tools.o gui_emummc_tools.o \
+ gui.o gui_info.o gui_tools.o gui_options.o gui_emmc_tools.o gui_emummc_tools.o gui_tools_partition_manager.o \
fe_emummc_tools.o fe_emmc_tools.o \
)
@@ -33,7 +33,7 @@ OBJS += $(addprefix $(BUILDDIR)/$(TARGET)/, \
bpmp.o clock.o cluster.o di.o gpio.o i2c.o irq.o pinmux.o se.o smmu.o tsec.o uart.o \
fuse.o kfuse.o \
mc.o sdram.o minerva.o ramdisk.o \
- sdmmc.o sdmmc_driver.o nx_emmc.o nx_sd.o \
+ sdmmc.o sdmmc_driver.o nx_emmc.o nx_emmc_bis.o nx_sd.o \
bq24193.o max17050.o max7762x.o max77620-rtc.o regulator_5v.o \
touch.o joycon.o tmp451.o fan.o \
usbd.o usb_gadget_ums.o usb_gadget_hid.o \
@@ -56,7 +56,7 @@ OBJS += $(addprefix $(BUILDDIR)/$(TARGET)/, \
# Libraries.
OBJS += $(addprefix $(BUILDDIR)/$(TARGET)/, \
diskio.o ff.o ffunicode.o ffsystem.o \
- elfload.o elfreloc_arm.o \
+ elfload.o elfreloc_arm.o blz.o \
lv_group.o lv_indev.o lv_obj.o lv_refr.o lv_style.o lv_vdb.o \
lv_draw.o lv_draw_rbasic.o lv_draw_vbasic.o lv_draw_arc.o lv_draw_img.o \
lv_draw_label.o lv_draw_line.o lv_draw_rect.o lv_draw_triangle.o \
diff --git a/nyx/nyx_gui/frontend/gui_emummc_tools.c b/nyx/nyx_gui/frontend/gui_emummc_tools.c
index 465374e..8cafa8c 100644
--- a/nyx/nyx_gui/frontend/gui_emummc_tools.c
+++ b/nyx/nyx_gui/frontend/gui_emummc_tools.c
@@ -18,6 +18,8 @@
#include "gui.h"
#include "fe_emummc_tools.h"
+#include "gui_tools_partition_manager.h"
+#include "../../../common/memory_map.h"
#include "../config/ini.h"
#include "../libs/fatfs/ff.h"
#include "../mem/heap.h"
@@ -144,6 +146,22 @@ static void _create_window_emummc()
nyx_window_toggle_buttons(win, false);
}
+static lv_res_t _create_emummc_raw_format(lv_obj_t * btns, const char * txt)
+{
+ int btn_idx = lv_btnm_get_pressed(btns);
+
+ // Delete parent mbox.
+ mbox_action(btns, txt);
+
+ // Create partition window.
+ if (!btn_idx)
+ create_window_partition_manager(btns);
+
+ mbr_ctx.part_idx = 0;
+ mbr_ctx.sector_start = 0;
+
+ return LV_RES_INV;
+}
static lv_res_t _create_emummc_raw_action(lv_obj_t * btns, const char * txt)
{
diff --git a/nyx/nyx_gui/frontend/gui_tools.c b/nyx/nyx_gui/frontend/gui_tools.c
index c753a0e..7c91707 100644
--- a/nyx/nyx_gui/frontend/gui_tools.c
+++ b/nyx/nyx_gui/frontend/gui_tools.c
@@ -19,6 +19,7 @@
#include "gui.h"
#include "gui_tools.h"
+#include "gui_tools_partition_manager.h"
#include "gui_emmc_tools.h"
#include "fe_emummc_tools.h"
#include "../../../common/memory_map.h"
@@ -1611,6 +1612,21 @@ static void _create_tab_tools_arc_autorcm(lv_theme_t *th, lv_obj_t *parent)
label_sep = lv_label_create(h2, NULL);
lv_label_set_static_text(label_sep, "");
lv_obj_align(label_sep, label_txt4, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI * 11 / 7);
+
+ // Create Partition SD Card button.
+ lv_obj_t *btn4 = lv_btn_create(h2, btn);
+ label_btn = lv_label_create(btn4, NULL);
+ lv_label_set_static_text(label_btn, SYMBOL_SD" Partition SD Card");
+ lv_obj_align(btn4, label_txt4, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 2);
+ lv_btn_set_action(btn4, LV_BTN_ACTION_CLICK, create_window_partition_manager);
+
+ label_txt2 = lv_label_create(h2, NULL);
+ lv_label_set_recolor(label_txt2, true);
+ lv_label_set_static_text(label_txt2,
+ "Allows you to partition your SD Card for using it with #C7EA46 emuMMC#,\n"
+ "#C7EA46 Android# and #C7EA46 Linux#. You can also flash Linux and Android.");
+ lv_obj_set_style(label_txt2, &hint_small_style);
+ lv_obj_align(label_txt2, btn4, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
}
void create_tab_tools(lv_theme_t *th, lv_obj_t *parent)
diff --git a/nyx/nyx_gui/frontend/gui_tools_partition_manager.c b/nyx/nyx_gui/frontend/gui_tools_partition_manager.c
new file mode 100644
index 0000000..db85498
--- /dev/null
+++ b/nyx/nyx_gui/frontend/gui_tools_partition_manager.c
@@ -0,0 +1,2267 @@
+/*
+ * Copyright (c) 2019-2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "gui.h"
+#include "gui_tools.h"
+#include "gui_tools_partition_manager.h"
+#include "../libs/fatfs/diskio.h"
+#include "../libs/lvgl/lvgl.h"
+#include "../mem/heap.h"
+#include "../sec/se.h"
+#include "../soc/hw_init.h"
+#include "../soc/pmc.h"
+#include "../soc/t210.h"
+#include "../storage/mbr_gpt.h"
+#include "../storage/nx_emmc.h"
+#include "../storage/nx_sd.h"
+#include "../storage/ramdisk.h"
+#include "../storage/sdmmc.h"
+#include "../utils/btn.h"
+#include "../utils/sprintf.h"
+#include "../utils/util.h"
+
+extern volatile boot_cfg_t *b_cfg;
+extern volatile nyx_storage_t *nyx_str;
+
+typedef struct _partition_ctxt_t
+{
+ u32 total_sct;
+ int backup_possible;
+
+ s32 hos_size;
+ u32 emu_size;
+ u32 l4t_size;
+ u32 and_size;
+
+ lv_obj_t *bar_hos;
+ lv_obj_t *bar_emu;
+ lv_obj_t *bar_l4t;
+ lv_obj_t *bar_and;
+
+ lv_obj_t *sep_emu;
+ lv_obj_t *sep_l4t;
+ lv_obj_t *sep_and;
+
+ lv_obj_t *slider_bar_hos;
+ lv_obj_t *slider_emu;
+ lv_obj_t *slider_l4t;
+ lv_obj_t *slider_and;
+
+ lv_obj_t *lbl_hos;
+ lv_obj_t *lbl_emu;
+ lv_obj_t *lbl_l4t;
+ lv_obj_t *lbl_and;
+
+ lv_obj_t *btn_partition;
+} partition_ctxt_t;
+
+typedef struct _l4t_flasher_ctxt_t
+{
+ u32 offset_sct;
+ u32 image_size_sct;
+} l4t_flasher_ctxt_t;
+
+partition_ctxt_t part_info;
+l4t_flasher_ctxt_t l4t_flash_ctxt;
+
+static int _backup_and_restore_files(char *path, u32 *total_files, u32 *total_size, const char *dst, const char *src, lv_obj_t **labels)
+{
+ FRESULT res;
+ FIL fp_src;
+ FIL fp_dst;
+ DIR dir;
+ u32 dirLength = 0;
+ static FILINFO fno;
+
+ if (src)
+ f_chdrive(src);
+
+ // Open directory.
+ res = f_opendir(&dir, path);
+ if (res != FR_OK)
+ return res;
+
+ if (labels)
+ lv_label_set_text(labels[0], path);
+
+ dirLength = strlen(path);
+ for (;;)
+ {
+ // Clear file path.
+ path[dirLength] = 0;
+
+ // Read a directory item.
+ res = f_readdir(&dir, &fno);
+
+ // Break on error or end of dir.
+ if (res != FR_OK || fno.fname[0] == 0)
+ break;
+
+ // Set new directory or file.
+ memcpy(&path[dirLength], "/", 1);
+ strcpy(&path[dirLength + 1], fno.fname);
+
+ if (labels)
+ {
+ lv_label_set_text(labels[1], fno.fname);
+ manual_system_maintenance(true);
+ }
+
+ // Copy file to destination disk.
+ if (!(fno.fattrib & AM_DIR))
+ {
+ u32 file_size = fno.fsize > RAMDISK_CLUSTER_SZ ? fno.fsize : RAMDISK_CLUSTER_SZ; // Ramdisk cluster size.
+ *total_size += file_size;
+ *total_files += 1;
+
+ if (src && dst)
+ {
+ u32 file_size = fno.fsize;
+
+ // Open file for writing.
+ f_chdrive(dst);
+ f_open(&fp_dst, path, FA_CREATE_ALWAYS | FA_WRITE);
+ f_lseek(&fp_dst, fno.fsize);
+ f_lseek(&fp_dst, 0);
+
+ // Open file for reading.
+ f_chdrive(src);
+ f_open(&fp_src, path, FA_READ);
+
+ while (file_size)
+ {
+ u32 chunk_size = MIN(file_size, 0x400000); // 4MB chunks.
+ file_size -= chunk_size;
+
+ // Copy file to buffer.
+ f_read(&fp_src, (void *)SDXC_BUF_ALIGNED, chunk_size, NULL);
+ manual_system_maintenance(true);
+
+ // Write file to disk.
+ f_write(&fp_dst, (void *)SDXC_BUF_ALIGNED, chunk_size, NULL);
+ }
+ f_close(&fp_src);
+
+ // Finalize copied file.
+ f_close(&fp_dst);
+ f_chdrive(dst);
+ f_chmod(path, fno.fattrib, 0xFF);
+
+ f_chdrive(src);
+ }
+
+ // If total is > 1GB exit.
+ if (*total_size > (RAM_DISK_SZ - 0x1000000)) // 0x2400000.
+ break;
+ }
+ else // It's a directory.
+ {
+ if (!memcmp("System Volume Information", fno.fname, 25))
+ continue;
+
+ // Create folder to destination.
+ if (dst)
+ {
+ f_chdrive(dst);
+ f_mkdir(path);
+ }
+ // Enter the directory.
+ res = _backup_and_restore_files(path, total_files, total_size, dst, src, labels);
+ if (res != FR_OK)
+ break;
+
+ if (labels)
+ {
+ // Clear folder path.
+ path[dirLength] = 0;
+ lv_label_set_text(labels[0], path);
+ }
+ }
+ }
+
+ f_closedir(&dir);
+
+ return res;
+}
+
+static void _prepare_and_flash_mbr_gpt()
+{
+ u8 random_number[16];
+ mbr_t mbr;
+ gpt_t gpt = { 0 };
+ gpt_header_t gpt_hdr_backup = { 0 };
+
+ // Read current MBR.
+ sdmmc_storage_read(&sd_storage, 0, 1, &mbr);
+
+ // Clear the first 16MB.
+ memset((void *)SDMMC_UPPER_BUFFER, 0, 0x8000);
+ sdmmc_storage_write(&sd_storage, 0, 0x8000, (void *)SDMMC_UPPER_BUFFER);
+
+ u8 mbr_idx = 1;
+ se_gen_prng128(random_number);
+ memcpy(&mbr.signature, random_number, 4);
+
+ // Apply L4T Linux second to MBR if no Android.
+ if (part_info.l4t_size && !part_info.and_size)
+ {
+ mbr.partitions[mbr_idx].type = 0x83; // Linux system partition.
+ mbr.partitions[mbr_idx].start_sct = 0x8000 + (part_info.hos_size << 11);
+ mbr.partitions[mbr_idx].size_sct = part_info.l4t_size << 11;
+ mbr_idx++;
+ }
+
+ // emuMMC goes second or third. Next to L4T if no Android.
+ bool double_emummc = part_info.emu_size > 29856;
+ if (part_info.emu_size)
+ {
+ mbr.partitions[mbr_idx].type = 0xE0; // emuMMC partition.
+ mbr.partitions[mbr_idx].start_sct = 0x8000 + (part_info.hos_size << 11) + (part_info.l4t_size << 11) + (part_info.and_size << 11);
+
+ if (!double_emummc)
+ {
+ if (!part_info.and_size)
+ mbr.partitions[mbr_idx].size_sct = part_info.emu_size << 11;
+ else
+ mbr.partitions[mbr_idx].size_sct = (part_info.emu_size << 11) - 0x800; // Reserve 1MB.
+ }
+ else
+ {
+ mbr.partitions[mbr_idx].type = 0xE0; // emuMMC partition.
+ mbr.partitions[mbr_idx].size_sct = part_info.emu_size << 10;
+ mbr_idx++;
+
+ // 2nd emuMMC.
+ mbr.partitions[mbr_idx].start_sct = mbr.partitions[mbr_idx - 1].start_sct + (part_info.emu_size << 10);
+ if (!part_info.and_size)
+ mbr.partitions[mbr_idx].size_sct = part_info.emu_size << 10;
+ else
+ mbr.partitions[mbr_idx].size_sct = (part_info.emu_size << 10) - 0x800; // Reserve 1MB.
+ }
+ mbr_idx++;
+ }
+
+ if (part_info.and_size)
+ {
+ mbr.partitions[mbr_idx].type = 0xEE; // GPT protective partition.
+ mbr.partitions[mbr_idx].start_sct = 1;
+ mbr.partitions[mbr_idx].size_sct = part_info.total_sct - 1;
+ mbr_idx++;
+
+ // Set GPT header.
+ memcpy(&gpt.header.signature, "EFI PART", 8);
+ gpt.header.revision = 0x10000;
+ gpt.header.size = 92;
+ gpt.header.my_lba = 1;
+ gpt.header.alt_lba = part_info.total_sct - 1;
+ gpt.header.first_use_lba = (sizeof(mbr_t) + sizeof(gpt_t)) >> 9;
+ gpt.header.last_use_lba = part_info.total_sct - 0x800 - 1; // part_info.total_sct - 33 is start of backup gpt partition entries.
+ se_gen_prng128(random_number);
+ memcpy(gpt.header.disk_guid, random_number, 10);
+ memcpy(gpt.header.disk_guid + 10, "NYXGPT", 6);
+ gpt.header.part_ent_lba = 2;
+ gpt.header.part_ent_size = 128;
+
+ // Set GPT partitions.
+ u8 basic_part_guid[] = { 0xA2, 0xA0, 0xD0, 0xEB, 0xE5, 0xB9, 0x33, 0x44, 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 };
+ memcpy(gpt.entries[0].type_guid, basic_part_guid, 16);
+ se_gen_prng128(random_number);
+ memcpy(gpt.entries[0].part_guid, random_number, 16);
+
+ // Clear non-standard Windows MBR attributes. bit4: Read only, bit5: Shadow copy, bit6: Hidden, bit7: No drive letter.
+ gpt.entries[0].part_guid[7] = 0;
+
+ gpt.entries[0].lba_start = mbr.partitions[0].start_sct;
+ gpt.entries[0].lba_end = mbr.partitions[0].start_sct + mbr.partitions[0].size_sct - 1;
+ memcpy(gpt.entries[0].name, (char[]) { 'h', 0, 'o', 0, 's', 0, '_', 0, 'd', 0, 'a', 0, 't', 0, 'a', 0 }, 16);
+
+ u8 gpt_idx = 1;
+ u32 curr_part_lba = 0x8000 + (part_info.hos_size << 11);
+ u8 android_part_guid[] = { 0xAF, 0x3D, 0xC6, 0x0F, 0x83, 0x84, 0x72, 0x47, 0x8E, 0x79, 0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4 };
+ if (part_info.l4t_size)
+ {
+ memcpy(gpt.entries[gpt_idx].type_guid, android_part_guid, 16);
+ se_gen_prng128(random_number);
+ memcpy(gpt.entries[gpt_idx].part_guid, random_number, 16);
+ gpt.entries[gpt_idx].lba_start = curr_part_lba;
+ gpt.entries[gpt_idx].lba_end = curr_part_lba + (part_info.l4t_size << 11) - 1;
+ memcpy(gpt.entries[gpt_idx].name, (char[]) { 'l', 0, '4', 0, 't', 0 }, 6);
+
+ curr_part_lba += (part_info.l4t_size << 11);
+ gpt_idx++;
+ }
+
+ // Android Vendor partition.
+ memcpy(gpt.entries[gpt_idx].type_guid, android_part_guid, 16);
+ se_gen_prng128(random_number);
+ memcpy(gpt.entries[gpt_idx].part_guid, random_number, 16);
+ gpt.entries[gpt_idx].lba_start = curr_part_lba;
+ gpt.entries[gpt_idx].lba_end = curr_part_lba + 0x200000 - 1; // 1GB.
+ memcpy(gpt.entries[gpt_idx].name, (char[]) { 'v', 0, 'e', 0, 'n', 0, 'd', 0, 'o', 0, 'r', 0 }, 12);
+ curr_part_lba += 0x200000;
+ gpt_idx++;
+
+ // Android System partition.
+ memcpy(gpt.entries[gpt_idx].type_guid, android_part_guid, 16);
+ se_gen_prng128(random_number);
+ memcpy(gpt.entries[gpt_idx].part_guid, random_number, 16);
+ gpt.entries[gpt_idx].lba_start = curr_part_lba;
+ gpt.entries[gpt_idx].lba_end = curr_part_lba + 0x400000 - 1; // 2GB.
+ memcpy(gpt.entries[gpt_idx].name, (char[]) { 'A', 0, 'P', 0, 'P', 0 }, 6);
+ curr_part_lba += 0x400000;
+ gpt_idx++;
+
+ // Android Linux Kernel partition.
+ memcpy(gpt.entries[gpt_idx].type_guid, android_part_guid, 16);
+ se_gen_prng128(random_number);
+ memcpy(gpt.entries[gpt_idx].part_guid, random_number, 16);
+ gpt.entries[gpt_idx].lba_start = curr_part_lba;
+ gpt.entries[gpt_idx].lba_end = curr_part_lba + 0x10000 - 1; // 32MB.
+ memcpy(gpt.entries[gpt_idx].name, (char[]) { 'L', 0, 'N', 0, 'X', 0 }, 6);
+ curr_part_lba += 0x10000;
+ gpt_idx++;
+
+ // Android Recovery partition.
+ memcpy(gpt.entries[gpt_idx].type_guid, android_part_guid, 16);
+ se_gen_prng128(random_number);
+ memcpy(gpt.entries[gpt_idx].part_guid, random_number, 16);
+ gpt.entries[gpt_idx].lba_start = curr_part_lba;
+ gpt.entries[gpt_idx].lba_end = curr_part_lba + 0x20000 - 1; // 64MB.
+ memcpy(gpt.entries[gpt_idx].name, (char[]) { 'S', 0, 'O', 0, 'S', 0 }, 6);
+ curr_part_lba += 0x20000;
+ gpt_idx++;
+
+ // Android Device Tree Reference partition.
+ memcpy(gpt.entries[gpt_idx].type_guid, android_part_guid, 16);
+ se_gen_prng128(random_number);
+ memcpy(gpt.entries[gpt_idx].part_guid, random_number, 16);
+ gpt.entries[gpt_idx].lba_start = curr_part_lba;
+ gpt.entries[gpt_idx].lba_end = curr_part_lba + 0x800 - 1; // 1MB.
+ memcpy(gpt.entries[gpt_idx].name, (char[]) { 'D', 0, 'T', 0, 'B', 0 }, 6);
+ curr_part_lba += 0x800;
+ gpt_idx++;
+
+ // Android Encryption partition.
+ memcpy(gpt.entries[gpt_idx].type_guid, android_part_guid, 16);
+ se_gen_prng128(random_number);
+ memcpy(gpt.entries[gpt_idx].part_guid, random_number, 16);
+ gpt.entries[gpt_idx].lba_start = curr_part_lba;
+ gpt.entries[gpt_idx].lba_end = curr_part_lba + 0x8000 - 1; // 16MB.
+ memcpy(gpt.entries[gpt_idx].name, (char[]) { 'M', 0, 'D', 0, 'A', 0 }, 6);
+ curr_part_lba += 0x8000;
+ gpt_idx++;
+
+ // Android Cache partition.
+ memcpy(gpt.entries[gpt_idx].type_guid, android_part_guid, 16);
+ se_gen_prng128(random_number);
+ memcpy(gpt.entries[gpt_idx].part_guid, random_number, 16);
+ gpt.entries[gpt_idx].lba_start = curr_part_lba;
+ gpt.entries[gpt_idx].lba_end = curr_part_lba + 0x15E000 - 1; // 700MB.
+ memcpy(gpt.entries[gpt_idx].name, (char[]) { 'C', 0, 'A', 0, 'C', 0 }, 6);
+ curr_part_lba += 0x15E000;
+ gpt_idx++;
+
+ // Android Userdata partition.
+ u32 align_diff = ALIGN(curr_part_lba, 0x8000) - curr_part_lba;
+ curr_part_lba += align_diff; // Align to 16MB.
+ u32 user_size = (part_info.and_size << 11) - 0x796800 - align_diff; // Subtract the other partitions (3885MB).
+ if (!part_info.emu_size)
+ user_size -= 0x800; // Reserve 1MB.
+ memcpy(gpt.entries[gpt_idx].type_guid, android_part_guid, 16);
+ se_gen_prng128(random_number);
+ memcpy(gpt.entries[gpt_idx].part_guid, random_number, 16);
+ gpt.entries[gpt_idx].lba_start = curr_part_lba;
+ gpt.entries[gpt_idx].lba_end = curr_part_lba + user_size - 1;
+ memcpy(gpt.entries[gpt_idx].name, (char[]) { 'U', 0, 'D', 0, 'A', 0 }, 6);
+ curr_part_lba += user_size;
+ gpt_idx++;
+
+ if (part_info.emu_size)
+ {
+ u8 emu_part_guid[] = { 0x00, 0x7E, 0xCA, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'e', 'm', 'u', 'M', 'M', 'C' };
+ memcpy(gpt.entries[gpt_idx].type_guid, emu_part_guid, 16);
+ se_gen_prng128(random_number);
+ memcpy(gpt.entries[gpt_idx].part_guid, random_number, 16);
+ gpt.entries[gpt_idx].lba_start = curr_part_lba;
+ if (!double_emummc)
+ gpt.entries[gpt_idx].lba_end = curr_part_lba + (part_info.emu_size << 11) - 0x800 - 1; // Reserve 1MB.
+ else
+ gpt.entries[gpt_idx].lba_end = curr_part_lba + (part_info.emu_size << 10); // Reserve 1MB.
+ memcpy(gpt.entries[gpt_idx].name, (char[]) { 'e', 0, 'm', 0, 'u', 0, 'm', 0, 'm', 0, 'c', 0 }, 12);
+ gpt_idx++;
+
+ if (double_emummc)
+ {
+ curr_part_lba += (part_info.emu_size << 10);
+ memcpy(gpt.entries[gpt_idx].type_guid, emu_part_guid, 16);
+ se_gen_prng128(random_number);
+ memcpy(gpt.entries[gpt_idx].part_guid, random_number, 16);
+ gpt.entries[gpt_idx].lba_start = curr_part_lba;
+ gpt.entries[gpt_idx].lba_end = curr_part_lba + (part_info.emu_size << 10) - 0x800 - 1; // Reserve 1MB.
+ memcpy(gpt.entries[gpt_idx].name, (char[]) { 'e', 0, 'm', 0, 'u', 0, 'm', 0, 'm', 0, 'c', 0, '2', 0 }, 14);
+ gpt_idx++;
+ }
+ }
+
+ // Set final GPT header parameters.
+ gpt.header.num_part_ents = 128;
+ gpt.header.part_ents_crc32 = crc32_calc(0, (const u8 *)gpt.entries, sizeof(gpt_entry_t) * 128);
+ gpt.header.crc32 = 0; // Set to 0 for calculation.
+ gpt.header.crc32 = crc32_calc(0, (const u8 *)&gpt.header, gpt.header.size);
+
+ memcpy(&gpt_hdr_backup, &gpt.header, sizeof(gpt_header_t));
+ gpt_hdr_backup.my_lba = part_info.total_sct - 1;
+ gpt_hdr_backup.alt_lba = 1;
+ gpt_hdr_backup.part_ent_lba = part_info.total_sct - 33;
+ gpt_hdr_backup.crc32 = 0; // Set to 0 for calculation.
+ gpt_hdr_backup.crc32 = crc32_calc(0, (const u8 *)&gpt_hdr_backup, gpt_hdr_backup.size);
+
+ // Write main GPT.
+ sdmmc_storage_write(&sd_storage, gpt.header.my_lba, sizeof(gpt_t) >> 9, &gpt);
+
+ // Write backup GPT partition table.
+ sdmmc_storage_write(&sd_storage, gpt_hdr_backup.part_ent_lba, ((sizeof(gpt_entry_t) * 128) >> 9), gpt.entries);
+
+ // Write backup GPT header.
+ sdmmc_storage_write(&sd_storage, gpt_hdr_backup.my_lba, 1, &gpt_hdr_backup);
+ }
+
+ // Write MBR.
+ sdmmc_storage_write(&sd_storage, 0, 1, &mbr);
+}
+
+static lv_res_t _action_part_manager_ums_sd(lv_obj_t *btn)
+{
+ action_ums_sd(btn);
+
+ if (lv_btn_get_state(part_info.btn_partition) != LV_BTN_STATE_INA)
+ {
+ lv_action_t close_btn_action = lv_btn_get_action(close_btn, LV_BTN_ACTION_CLICK);
+ close_btn_action(close_btn);
+ lv_obj_del(ums_mbox);
+ create_window_partition_manager(NULL);
+
+ return LV_RES_INV;
+ }
+
+ return LV_RES_OK;
+}
+
+static lv_res_t _action_delete_linux_installer_files(lv_obj_t * btns, const char * txt)
+{
+
+ int btn_idx = lv_btnm_get_pressed(btns);
+
+ // Delete parent mbox.
+ mbox_action(btns, txt);
+
+ // Flash Linux.
+ if (!btn_idx)
+ {
+ char path[128];
+
+ sd_mount();
+
+ strcpy(path, "switchroot/install/l4t.");
+
+ // Delete all l4t.xx files.
+ u32 idx = 0;
+ while (true)
+ {
+ if (idx < 10)
+ {
+ path[23] = '0';
+ itoa(idx, &path[23 + 1], 10);
+ }
+ else
+ itoa(idx, &path[23], 10);
+
+ if (!f_stat(path, NULL))
+ {
+ f_unlink(path);
+ }
+ else
+ break;
+
+ idx++;
+ }
+
+ sd_unmount(false);
+ }
+
+ return LV_RES_INV;
+}
+
+static lv_res_t _action_flash_linux_data(lv_obj_t * btns, const char * txt)
+{
+ int btn_idx = lv_btnm_get_pressed(btns);
+
+ // Delete parent mbox.
+ mbox_action(btns, txt);
+
+ bool succeeded = false;
+
+ // Flash Linux.
+ if (!btn_idx)
+ {
+ lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
+ lv_obj_set_style(dark_bg, &mbox_darken);
+ lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
+
+ static const char *mbox_btn_map[] = { "\211", "\222OK", "\211", "" };
+ static const char *mbox_btn_map2[] = { "\223Delete Installation Files", "\221OK", "\211", "" };
+ lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
+ lv_mbox_set_recolor_text(mbox, true);
+ lv_obj_set_width(mbox, LV_HOR_RES / 10 * 5);
+
+ lv_mbox_set_text(mbox, "#FF8000 Linux Flasher#");
+
+ lv_obj_t *lbl_status = lv_label_create(mbox, NULL);
+ lv_label_set_recolor(lbl_status, true);
+ lv_label_set_text(lbl_status, "#C7EA46 Status:# Flashing Linux...");
+
+ // Create container to keep content inside.
+ lv_obj_t *h1 = lv_cont_create(mbox, NULL);
+ lv_cont_set_fit(h1, true, true);
+ lv_cont_set_style(h1, &lv_style_transp_tight);
+
+ lv_obj_t *bar = lv_bar_create(h1, NULL);
+ lv_obj_set_size(bar, LV_DPI * 30 / 10, LV_DPI / 5);
+ lv_bar_set_range(bar, 0, 100);
+ lv_bar_set_value(bar, 0);
+
+ lv_obj_t *label_pct = lv_label_create(h1, NULL);
+ lv_label_set_recolor(label_pct, true);
+ lv_label_set_text(label_pct, " "SYMBOL_DOT" 0%");
+ lv_label_set_style(label_pct, lv_theme_get_current()->label.prim);
+ lv_obj_align(label_pct, bar, LV_ALIGN_OUT_RIGHT_MID, LV_DPI / 20, 0);
+
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_top(mbox, true);
+
+ sd_mount();
+
+ int res = 0;
+ char *path = malloc(1024);
+ char *txt_buf = malloc(0x1000);
+ strcpy(path, "switchroot/install/l4t.00");
+ u32 path_len = strlen(path) - 2;
+
+ FIL fp;
+
+ res = f_open(&fp, path, FA_READ);
+ if (res)
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Error:# Failed to open 1st part!");
+
+ goto exit;
+ }
+
+ u64 fileSize = (u64)f_size(&fp);
+
+ u32 num = 0;
+ u32 pct = 0;
+ u32 lba_curr = 0;
+ u32 bytesWritten = 0;
+ u32 currPartIdx = 0;
+ u32 prevPct = 200;
+ int retryCount = 0;
+ u32 total_size_sct = l4t_flash_ctxt.image_size_sct;
+
+ u8 *buf = (u8 *)MIXD_BUF_ALIGNED;
+ DWORD *clmt = f_expand_cltbl(&fp, 0x400000, 0);
+
+ while (total_size_sct > 0)
+ {
+ // If we have more than one part, check the size for the split parts and make sure that the bytes written is not more than that.
+ if (bytesWritten >= fileSize)
+ {
+ // If we have more bytes written then close the file pointer and increase the part index we are using
+ f_close(&fp);
+ free(clmt);
+ memset(&fp, 0, sizeof(fp));
+ currPartIdx++;
+
+ if (currPartIdx < 10)
+ {
+ path[path_len] = '0';
+ itoa(currPartIdx, &path[path_len + 1], 10);
+ }
+ else
+ itoa(currPartIdx, &path[path_len], 10);
+
+ // Try to open the next file part
+ res = f_open(&fp, path, FA_READ);
+ if (res)
+ {
+ s_printf(txt_buf, "#FFDD00 Error:# Failed to open part %d#", currPartIdx);
+ lv_label_set_text(lbl_status, txt_buf);
+ manual_system_maintenance(true);
+
+ goto exit;
+ }
+ fileSize = (u64)f_size(&fp);
+ bytesWritten = 0;
+ clmt = f_expand_cltbl(&fp, 0x400000, 0);
+ }
+
+ retryCount = 0;
+ num = MIN(total_size_sct, 8192);
+
+ res = f_read_fast(&fp, buf, num << 9);
+ manual_system_maintenance(false);
+
+ if (res)
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Error:# Reading from SD!");
+ manual_system_maintenance(true);
+
+ f_close(&fp);
+ free(clmt);
+ goto exit;
+ }
+ res = !sdmmc_storage_write(&sd_storage, lba_curr + l4t_flash_ctxt.offset_sct, num, buf);
+
+ manual_system_maintenance(false);
+
+ while (res)
+ {
+ msleep(150);
+ manual_system_maintenance(true);
+
+ if (retryCount >= 3)
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Error:# Writing to SD!");
+ manual_system_maintenance(true);
+
+ f_close(&fp);
+ free(clmt);
+ goto exit;
+ }
+
+ res = !sdmmc_storage_write(&sd_storage, lba_curr + l4t_flash_ctxt.offset_sct, num, buf);
+ manual_system_maintenance(false);
+ }
+ pct = (u64)((u64)lba_curr * 100u) / (u64)l4t_flash_ctxt.image_size_sct;
+ if (pct != prevPct)
+ {
+ lv_bar_set_value(bar, pct);
+ s_printf(txt_buf, " "SYMBOL_DOT" %d%%", pct);
+ lv_label_set_text(label_pct, txt_buf);
+ manual_system_maintenance(true);
+ prevPct = pct;
+ }
+
+ lba_curr += num;
+ total_size_sct -= num;
+ bytesWritten += num * NX_EMMC_BLOCKSIZE;
+ }
+ lv_bar_set_value(bar, 100);
+ lv_label_set_text(label_pct, " "SYMBOL_DOT" 100%");
+ manual_system_maintenance(true);
+
+ // Restore operation ended successfully.
+ f_close(&fp);
+ free(clmt);
+
+ succeeded = true;
+
+exit:
+ free(path);
+ free(txt_buf);
+
+ if (!succeeded)
+ lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action);
+ else
+ lv_mbox_add_btns(mbox, mbox_btn_map2, _action_delete_linux_installer_files);
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+
+ sd_unmount(false);
+ }
+
+ return LV_RES_INV;
+}
+
+static lv_res_t _action_check_flash_linux(lv_obj_t *btn)
+{
+ FILINFO fno;
+ char path[128];
+ mbr_t mbr = { 0 };
+ gpt_t gpt = { 0 };
+
+ memset(&l4t_flash_ctxt, 0, sizeof(l4t_flasher_ctxt_t));
+
+ lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
+ lv_obj_set_style(dark_bg, &mbox_darken);
+ lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
+
+ static const char *mbox_btn_map[] = { "\211", "\222OK", "\211", "" };
+ static const char *mbox_btn_map2[] = { "\222Continue", "\222Cancel", "" };
+ lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
+ lv_mbox_set_recolor_text(mbox, true);
+ lv_obj_set_width(mbox, LV_HOR_RES / 9 * 6);
+
+ lv_mbox_set_text(mbox, "#FF8000 Linux Flasher#");
+
+ lv_obj_t *lbl_status = lv_label_create(mbox, NULL);
+ lv_label_set_recolor(lbl_status, true);
+ lv_label_set_text(lbl_status, "#C7EA46 Status:# Searching for files and partitions...");
+
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_top(mbox, true);
+
+ manual_system_maintenance(true);
+
+ sd_mount();
+
+ strcpy(path, "switchroot/install/l4t.00");
+ if (f_stat(path, NULL))
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Error:# Installation files not found!");
+ goto error;
+ }
+
+ // Read MBR.
+ sdmmc_storage_read(&sd_storage, 0, 1, &mbr);
+
+ // Read main GPT.
+ sdmmc_storage_read(&sd_storage, 1, sizeof(gpt_t) >> 9, &gpt);
+
+ // Search for a suitable partition.
+ u32 size_sct = 0;
+ if (!memcmp(&gpt.header.signature, "EFI PART", 8))
+ {
+ for (u32 i = 0; i < gpt.header.num_part_ents; i++)
+ {
+ if (!memcmp(gpt.entries[i].name, (char[]) { 'l', 0, '4', 0, 't', 0 }, 6))
+ {
+ l4t_flash_ctxt.offset_sct = gpt.entries[i].lba_start;
+ size_sct = (gpt.entries[i].lba_end + 1) - gpt.entries[i].lba_start;
+ break;
+ }
+
+ if (i > 126)
+ break;
+ }
+ }
+ else
+ {
+ for (u32 i = 1; i < 4; i++)
+ {
+ if (mbr.partitions[i].type == 0x83)
+ {
+ l4t_flash_ctxt.offset_sct = mbr.partitions[i].start_sct;
+ size_sct = mbr.partitions[i].size_sct;
+ break;
+ }
+ }
+ }
+
+ if (!l4t_flash_ctxt.offset_sct || size_sct < 0x800000)
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Error:# No partition found!");
+ goto error;
+ }
+
+ u32 idx = 0;
+ path[23] = 0;
+ while (true)
+ {
+ if (idx < 10)
+ {
+ path[23] = '0';
+ itoa(idx, &path[23 + 1], 10);
+ }
+ else
+ itoa(idx, &path[23], 10);
+
+ // Check for alignment.
+ if (!f_stat(path, &fno))
+ {
+ if ((u64)fno.fsize % 0x400000)
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Error:# The image is not aligned to 4 MiB!");
+ goto error;
+ }
+ l4t_flash_ctxt.image_size_sct += (u64)fno.fsize >> 9;
+ }
+ else
+ break;
+
+ idx++;
+ }
+
+ if (l4t_flash_ctxt.image_size_sct > size_sct)
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Error:# The image is bigger than the partition!");
+ goto error;
+ }
+
+ char *txt_buf = malloc(0x1000);
+ s_printf(txt_buf,
+ "#C7EA46 Status:# Found installation files and partition.\n"
+ "#00DDFF Offset:# %08x, #00DDFF Size:# %X, #00DDFF Image size:# %d MiB\n"
+ "\nDo you want to continue?", l4t_flash_ctxt.offset_sct, size_sct, l4t_flash_ctxt.image_size_sct >> 11);
+ lv_label_set_text(lbl_status, txt_buf);
+ free(txt_buf);
+ lv_mbox_add_btns(mbox, mbox_btn_map2, _action_flash_linux_data);
+ goto exit;
+
+error:
+ lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action);
+
+exit:
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+
+ sd_unmount(false);
+
+ return LV_RES_OK;
+}
+
+static lv_res_t _action_reboot_twrp(lv_obj_t * btns, const char * txt)
+{
+ int btn_idx = lv_btnm_get_pressed(btns);
+
+ // Delete parent mbox.
+ mbox_action(btns, txt);
+
+ if (!btn_idx)
+ {
+ PMC(APBDEV_PMC_SCRATCH0) |= PMC_SCRATCH0_MODE_RECOVERY;
+
+ b_cfg->boot_cfg = BOOT_CFG_FROM_ID | BOOT_CFG_AUTOBOOT_EN;
+
+ strcpy((char *)b_cfg->id, "SWANDR");
+
+ void (*main_ptr)() = (void *)nyx_str->hekate;
+
+ sd_unmount(true);
+
+ reconfig_hw_workaround(false, 0);
+
+ (*main_ptr)();
+ }
+
+ return LV_RES_INV;
+}
+
+static lv_res_t _action_flash_android_data(lv_obj_t * btns, const char * txt)
+{
+ int btn_idx = lv_btnm_get_pressed(btns);
+
+ // Delete parent mbox.
+ mbox_action(btns, txt);
+
+ // Flash Android components.
+ if (!btn_idx)
+ {
+ char path[128];
+ gpt_t gpt = { 0 };
+ char *txt_buf = malloc(0x1000);
+
+ lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
+ lv_obj_set_style(dark_bg, &mbox_darken);
+ lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
+
+ static const char *mbox_btn_map[] = { "\211", "\222OK", "\211", "" };
+ static const char *mbox_btn_map2[] = { "\222Continue", "\222Cancel", "" };
+ lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
+ lv_mbox_set_recolor_text(mbox, true);
+ lv_obj_set_width(mbox, LV_HOR_RES / 9 * 6);
+
+ lv_mbox_set_text(mbox, "#FF8000 Android Flasher#");
+
+ lv_obj_t *lbl_status = lv_label_create(mbox, NULL);
+ lv_label_set_recolor(lbl_status, true);
+ lv_label_set_text(lbl_status, "#C7EA46 Status:# Searching for files and partitions...");
+
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_top(mbox, true);
+
+ manual_system_maintenance(true);
+
+ sd_mount();
+
+ // Read main GPT.
+ sdmmc_storage_read(&sd_storage, 1, sizeof(gpt_t) >> 9, &gpt);
+
+ bool boot_twrp = false;
+ if (memcmp(&gpt.header.signature, "EFI PART", 8))
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Error:# No Android GPT was found!");
+ goto error;
+ }
+
+ strcpy(path, "switchroot/install/boot.img");
+ if (!f_stat(path, NULL))
+ {
+ u32 offset_sct = 0;
+ u32 size_sct = 0;
+ for (u32 i = 0; i < gpt.header.num_part_ents; i++)
+ {
+ if (!memcmp(gpt.entries[i].name, (char[]) { 'L', 0, 'N', 0, 'X', 0 }, 6))
+ {
+ offset_sct = gpt.entries[i].lba_start;
+ size_sct = (gpt.entries[i].lba_end + 1) - gpt.entries[i].lba_start;
+ break;
+ }
+
+ if (i > 126)
+ break;
+ }
+
+ if (offset_sct && size_sct)
+ {
+ u32 file_size = 0;
+ u8 *buf = sd_file_read(path, &file_size);
+
+ if (file_size % 0x200)
+ {
+ file_size = ALIGN(file_size, 0x200);
+ u8 *buf_tmp = calloc(file_size, 1);
+ memcpy(buf_tmp, buf, file_size);
+ free(buf);
+ buf = buf_tmp;
+ }
+
+ if ((file_size >> 9) > size_sct)
+ s_printf(txt_buf, "#FF8000 Warning:# Kernel image too big!\n");
+ else
+ {
+ sdmmc_storage_write(&sd_storage, offset_sct, file_size >> 9, buf);
+
+ s_printf(txt_buf, "#C7EA46 Success:# Kernel image flashed!\n");
+ f_unlink(path);
+ }
+
+ free(buf);
+ }
+ else
+ s_printf(txt_buf, "#FF8000 Warning:# Kernel partition not found!\n");
+ }
+ else
+ s_printf(txt_buf, "#FF8000 Warning:# Kernel image not found!\n");
+
+ lv_label_set_text(lbl_status, txt_buf);
+ manual_system_maintenance(true);
+
+ strcpy(path, "switchroot/install/twrp.img");
+ if (!f_stat(path, NULL))
+ {
+ u32 offset_sct = 0;
+ u32 size_sct = 0;
+ for (u32 i = 0; i < gpt.header.num_part_ents; i++)
+ {
+ if (!memcmp(gpt.entries[i].name, (char[]) { 'S', 0, 'O', 0, 'S', 0 }, 6))
+ {
+ offset_sct = gpt.entries[i].lba_start;
+ size_sct = (gpt.entries[i].lba_end + 1) - gpt.entries[i].lba_start;
+ break;
+ }
+
+ if (i > 126)
+ break;
+ }
+
+ if (offset_sct && size_sct)
+ {
+ u32 file_size = 0;
+ u8 *buf = sd_file_read(path, &file_size);
+
+ if (file_size % 0x200)
+ {
+ file_size = ALIGN(file_size, 0x200);
+ u8 *buf_tmp = calloc(file_size, 1);
+ memcpy(buf_tmp, buf, file_size);
+ free(buf);
+ buf = buf_tmp;
+ }
+
+ if ((file_size >> 9) > size_sct)
+ s_printf(txt_buf + strlen(txt_buf), "#FF8000 Warning:# TWRP image too big!\n");
+ else
+ {
+ sdmmc_storage_write(&sd_storage, offset_sct, file_size >> 9, buf);
+ s_printf(txt_buf + strlen(txt_buf), "#C7EA46 Success:# TWRP image flashed!\n");
+ f_unlink(path);
+ }
+
+ free(buf);
+ }
+ else
+ s_printf(txt_buf + strlen(txt_buf), "#FF8000 Warning:# TWRP partition not found!\n");
+ }
+ else
+ s_printf(txt_buf + strlen(txt_buf), "#FF8000 Warning:# TWRP image not found!\n");
+
+ lv_label_set_text(lbl_status, txt_buf);
+ manual_system_maintenance(true);
+
+ strcpy(path, "switchroot/install/tegra210-icosa.dtb");
+ if (!f_stat(path, NULL))
+ {
+ u32 offset_sct = 0;
+ u32 size_sct = 0;
+ for (u32 i = 0; i < gpt.header.num_part_ents; i++)
+ {
+ if (!memcmp(gpt.entries[i].name, (char[]) { 'D', 0, 'T', 0, 'B', 0 }, 6))
+ {
+ offset_sct = gpt.entries[i].lba_start;
+ size_sct = (gpt.entries[i].lba_end + 1) - gpt.entries[i].lba_start;
+ break;
+ }
+
+ if (i > 126)
+ break;
+ }
+
+ if (offset_sct && size_sct)
+ {
+ u32 file_size = 0;
+ u8 *buf = sd_file_read(path, &file_size);
+
+ if (file_size % 0x200)
+ {
+ file_size = ALIGN(file_size, 0x200);
+ u8 *buf_tmp = calloc(file_size, 1);
+ memcpy(buf_tmp, buf, file_size);
+ free(buf);
+ buf = buf_tmp;
+ }
+
+ if ((file_size >> 9) > size_sct)
+ s_printf(txt_buf + strlen(txt_buf), "#FF8000 Warning:# DTB image too big!");
+ else
+ {
+ sdmmc_storage_write(&sd_storage, offset_sct, file_size >> 9, buf);
+ s_printf(txt_buf + strlen(txt_buf), "#C7EA46 Success:# DTB image flashed!");
+ f_unlink(path);
+ }
+
+ free(buf);
+ }
+ else
+ s_printf(txt_buf + strlen(txt_buf), "#FF8000 Warning:# DTB partition not found!");
+ }
+ else
+ s_printf(txt_buf + strlen(txt_buf), "#FF8000 Warning:# DTB image not found!");
+
+ lv_label_set_text(lbl_status, txt_buf);
+
+ // Check if TWRP is flashed unconditionally.
+ for (u32 i = 0; i < gpt.header.num_part_ents; i++)
+ {
+ if (!memcmp(gpt.entries[i].name, (char[]) { 'S', 0, 'O', 0, 'S', 0 }, 6))
+ {
+ u8 *buf = malloc(512);
+ sdmmc_storage_read(&sd_storage, gpt.entries[i].lba_start, 1, buf);
+ if (!memcmp(buf, "ANDROID", 7))
+ boot_twrp = true;
+ free(buf);
+ break;
+ }
+
+ if (i > 126)
+ break;
+ }
+
+error:
+ if (boot_twrp)
+ {
+ s_printf(txt_buf + strlen(txt_buf),"\n\nDo you want to reboot into TWRP\nto finish Android installation?");
+ lv_label_set_text(lbl_status, txt_buf);
+ lv_mbox_add_btns(mbox, mbox_btn_map2, _action_reboot_twrp);
+ }
+ else
+ lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action);
+
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+
+ free(txt_buf);
+
+ sd_unmount(false);
+ }
+
+ return LV_RES_INV;
+}
+
+static lv_res_t _action_flash_android(lv_obj_t *btn)
+{
+ memset(&l4t_flash_ctxt, 0, sizeof(l4t_flasher_ctxt_t));
+
+ lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
+ lv_obj_set_style(dark_bg, &mbox_darken);
+ lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
+
+ static const char *mbox_btn_map[] = { "\222Continue", "\222Cancel", "" };
+ lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
+ lv_mbox_set_recolor_text(mbox, true);
+ lv_obj_set_width(mbox, LV_HOR_RES / 9 * 6);
+
+ lv_mbox_set_text(mbox, "#FF8000 Android Flasher#");
+
+ lv_obj_t *lbl_status = lv_label_create(mbox, NULL);
+ lv_label_set_recolor(lbl_status, true);
+ lv_label_set_text(lbl_status,
+ "This will flash #C7EA46 Kernel#, #C7EA46 DTB# and #C7EA46 TWRP# if found.\n"
+ "Do you want to continue?");
+
+ lv_mbox_add_btns(mbox, mbox_btn_map, _action_flash_android_data);
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_top(mbox, true);
+
+ return LV_RES_OK;
+}
+
+static lv_res_t _action_part_manager_flash_options0(lv_obj_t *btns, const char *txt)
+{
+ int btn_idx = lv_btnm_get_pressed(btns);
+
+ switch (btn_idx)
+ {
+ case 0:
+ action_ums_sd(btns);
+ lv_obj_del(ums_mbox);
+ break;
+ case 1:
+ _action_check_flash_linux(btns);
+ break;
+ case 2:
+ _action_flash_android(btns);
+ break;
+ case 3:
+ mbox_action(btns, txt);
+ return LV_RES_INV;
+ }
+
+ return LV_RES_OK;
+}
+
+static lv_res_t _action_part_manager_flash_options1(lv_obj_t *btns, const char *txt)
+{
+ int btn_idx = lv_btnm_get_pressed(btns);
+
+ switch (btn_idx)
+ {
+ case 0:
+ action_ums_sd(btns);
+ lv_obj_del(ums_mbox);
+ break;
+ case 1:
+ mbox_action(btns, txt);
+ _action_check_flash_linux(NULL);
+ return LV_RES_INV;
+ case 2:
+ mbox_action(btns, txt);
+ return LV_RES_INV;
+ }
+
+ return LV_RES_OK;
+}
+
+static lv_res_t _action_part_manager_flash_options2(lv_obj_t *btns, const char *txt)
+{
+ int btn_idx = lv_btnm_get_pressed(btns);
+
+ switch (btn_idx)
+ {
+ case 0:
+ action_ums_sd(btns);
+ lv_obj_del(ums_mbox);
+ break;
+ case 1:
+ mbox_action(btns, txt);
+ _action_flash_android(NULL);
+ return LV_RES_INV;
+ case 2:
+ mbox_action(btns, txt);
+ return LV_RES_INV;
+ }
+
+ return LV_RES_OK;
+}
+
+static lv_res_t _create_mbox_start_partitioning(lv_obj_t *btn)
+{
+ lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
+ lv_obj_set_style(dark_bg, &mbox_darken);
+ lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
+
+ static const char *mbox_btn_map[] = { "\211", "\222OK", "\211", "" };
+ static const char *mbox_btn_map1[] = { "\222SD UMS", "\222Flash Linux", "\222Flash Android", "\221OK", "" };
+ static const char *mbox_btn_map2[] = { "\222SD UMS", "\222Flash Linux", "\221OK", "" };
+ static const char *mbox_btn_map3[] = { "\222SD UMS", "\222Flash Android", "\221OK", "" };
+ lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
+ lv_mbox_set_recolor_text(mbox, true);
+ lv_obj_set_width(mbox, LV_HOR_RES / 9 * 6);
+
+ lv_mbox_set_text(mbox, "#FF8000 Partition Manager#");
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_top(mbox, true);
+
+ bool buttons_set = false;
+
+ if (!part_info.backup_possible)
+ {
+ char *txt_buf = malloc(0x1000);
+ strcpy(txt_buf, "#FF8000 Partition Manager#\n\nSafety wait ends in ");
+ lv_mbox_set_text(mbox, txt_buf);
+
+ u32 seconds = 5;
+ u32 text_idx = strlen(txt_buf);
+ while (seconds)
+ {
+ s_printf(txt_buf + text_idx, "%d seconds...", seconds);
+ lv_mbox_set_text(mbox, txt_buf);
+ manual_system_maintenance(true);
+ msleep(1000);
+ seconds--;
+ }
+
+ lv_mbox_set_text(mbox,
+ "#FF8000 Partition Manager#\n\n"
+ "#FFDD00 Warning: Do you really want to continue?!#\n\n"
+ "Press #FF8000 POWER# to Continue.\nPress #FF8000 VOL# to abort.");
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ manual_system_maintenance(true);
+
+ free(txt_buf);
+
+ if (!(btn_wait() & BTN_POWER))
+ goto exit;
+ }
+
+ lv_mbox_set_text(mbox, "#FF8000 Partition Manager#");
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ manual_system_maintenance(true);
+
+ lv_obj_t *lbl_status = lv_label_create(mbox, NULL);
+ lv_label_set_recolor(lbl_status, true);
+
+ lv_obj_t *lbl_paths[2];
+
+ lbl_paths[0] = lv_label_create(mbox, NULL);
+ lv_label_set_text(lbl_paths[0], "/");
+ lv_label_set_long_mode(lbl_paths[0], LV_LABEL_LONG_DOT);
+ lv_cont_set_fit(lbl_paths[0], false, true);
+ lv_obj_set_width(lbl_paths[0], (LV_HOR_RES / 9 * 6) - LV_DPI / 2);
+ lv_label_set_align(lbl_paths[0], LV_LABEL_ALIGN_CENTER);
+ lbl_paths[1] = lv_label_create(mbox, NULL);
+ lv_label_set_text(lbl_paths[1], " ");
+ lv_label_set_long_mode(lbl_paths[1], LV_LABEL_LONG_DOT);
+ lv_cont_set_fit(lbl_paths[1], false, true);
+ lv_obj_set_width(lbl_paths[1], (LV_HOR_RES / 9 * 6) - LV_DPI / 2);
+ lv_label_set_align(lbl_paths[1], LV_LABEL_ALIGN_CENTER);
+
+ sd_mount();
+
+ FATFS ram_fs;
+ char *path = malloc(1024);
+ u32 total_files = 0;
+ u32 total_size = 0;
+
+ lv_label_set_text(lbl_status, "#00DDFF Status:# Initializing Ramdisk...");
+ lv_label_set_text(lbl_paths[0], "Please wait...");
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ manual_system_maintenance(true);
+ if (ram_disk_init(&ram_fs))
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Error:# Failed to initialize Ramdisk!");
+ goto error;
+ }
+
+ if (!part_info.backup_possible)
+ {
+ strcpy(path, "bootloader");
+ f_chdrive("ram:");
+ f_mkdir(path);
+ }
+ else
+ path[0] = 0;
+
+ lv_label_set_text(lbl_status, "#00DDFF Status:# Backing up files...");
+ manual_system_maintenance(true);
+ if (_backup_and_restore_files(path, &total_files, &total_size, "ram:", "sd:", lbl_paths))
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Error:# Failed to back up files!");
+ goto error;
+ }
+ total_files = 0;
+ total_size = 0;
+
+ if (!part_info.backup_possible)
+ strcpy(path, "bootloader");
+ else
+ path[0] = 0;
+
+ f_mount(NULL, "sd:", 1); // Unmount SD card.
+
+ lv_label_set_text(lbl_status, "#00DDFF Status:# Formatting FAT32 partition...");
+ lv_label_set_text(lbl_paths[0], "Please wait...");
+ lv_label_set_text(lbl_paths[1], " ");
+ manual_system_maintenance(true);
+
+ // Set reserved size.
+ u32 part_rsvd_size = (part_info.emu_size << 11) + (part_info.l4t_size << 11) + (part_info.and_size << 11);
+ disk_set_info(DRIVE_SD, SET_SECTOR_COUNT, &part_rsvd_size);
+ u8 *buf = malloc(0x400000);
+
+ u32 cluster_size = part_info.hos_size < 2560 ? 4096 : 65536;
+ if (f_mkfs("sd:", FM_FAT32, cluster_size, buf, 0x400000))
+ {
+ // Retry.
+ if (f_mkfs("sd:", FM_FAT32, cluster_size, buf, 0x400000))
+ {
+ // Failed to format.
+ lv_label_set_text(lbl_status,
+ "#FFDD00 Error:# Failed to format disk!\n\n"
+ "Remove the SD card and check that is OK. If not, format it and press any key!");
+ manual_system_maintenance(true);
+
+ sd_unmount(true);
+
+ while (!(btn_wait() & BTN_POWER));
+
+ sd_mount();
+
+ if (!part_info.backup_possible)
+ {
+ f_chdrive("sd:");
+ f_mkdir(path);
+ }
+
+ lv_label_set_text(lbl_status, "#00DDFF Status:# Restoring files...");
+ manual_system_maintenance(true);
+ if (_backup_and_restore_files(path, &total_files, &total_size, "sd:", "ram:", NULL))
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Error:# Failed to restore files!");
+ free(buf);
+ goto error;
+ }
+ lv_label_set_text(lbl_status, "#00DDFF Status:# Restored files but the operation failed!");
+ f_mount(NULL, "ram:", 1); // Unmount ramdisk.
+ free(buf);
+ goto error;
+ }
+ }
+ free(buf);
+
+ f_mount(&sd_fs, "sd:", 1); // Mount SD card.
+
+ if (!part_info.backup_possible)
+ {
+ f_chdrive("sd:");
+ f_mkdir(path);
+ }
+
+ lv_label_set_text(lbl_status, "#00DDFF Status:# Restoring files...");
+ manual_system_maintenance(true);
+ if (_backup_and_restore_files(path, &total_files, &total_size, "sd:", "ram:", lbl_paths))
+ {
+ total_files = 0;
+ total_size = 0;
+
+ if (!part_info.backup_possible)
+ {
+ strcpy(path, "bootloader");
+ f_chdrive("sd:");
+ f_mkdir(path);
+ }
+ else
+ path[0] = 0;
+
+ if (_backup_and_restore_files(path, &total_files, &total_size, "sd:", "ram:", NULL))
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Error:# Failed to restore files!");
+ goto error;
+ }
+ }
+
+ f_mount(NULL, "ram:", 1); // Unmount ramdisk.
+ f_chdrive("sd:");
+
+ // Set Volume label.
+ f_setlabel("0:SWITCH SD");
+
+ lv_label_set_text(lbl_status, "#00DDFF Status:# Flashing partition table...");
+ lv_label_set_text(lbl_paths[0], "Please wait...");
+ lv_label_set_text(lbl_paths[1], " ");
+ manual_system_maintenance(true);
+ _prepare_and_flash_mbr_gpt();
+
+ sd_unmount(false);
+ lv_label_set_text(lbl_status, "#00DDFF Status:# Done!");
+ manual_system_maintenance(true);
+
+ if (part_info.l4t_size && part_info.and_size)
+ lv_mbox_add_btns(mbox, mbox_btn_map1, _action_part_manager_flash_options0);
+ else if (part_info.l4t_size)
+ lv_mbox_add_btns(mbox, mbox_btn_map2, _action_part_manager_flash_options1);
+ else if (part_info.and_size)
+ lv_mbox_add_btns(mbox, mbox_btn_map3, _action_part_manager_flash_options2);
+
+ if (part_info.l4t_size || part_info.and_size)
+ buttons_set = true;
+
+ goto out;
+
+error:
+ f_chdrive("sd:");
+ free(path);
+
+out:
+ lv_obj_del(lbl_paths[0]);
+ lv_obj_del(lbl_paths[1]);
+exit:
+ if (!buttons_set)
+ lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action);
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_top(mbox, true);
+
+ // Disable partitioning button.
+ if (btn)
+ lv_btn_set_state(btn, LV_BTN_STATE_INA);
+
+ return LV_RES_OK;
+}
+
+static lv_res_t _create_mbox_partitioning_option0(lv_obj_t *btns, const char *txt)
+{
+ int btn_idx = lv_btnm_get_pressed(btns);
+
+ switch (btn_idx)
+ {
+ case 0:
+ action_ums_sd(btns);
+ return LV_RES_OK;
+ case 1:
+ mbox_action(btns, txt);
+ _create_mbox_start_partitioning(NULL);
+ break;
+ case 2:
+ mbox_action(btns, txt);
+ break;
+ }
+
+ return LV_RES_INV;
+}
+
+static lv_res_t _create_mbox_partitioning_option1(lv_obj_t *btns, const char *txt)
+{
+ int btn_idx = lv_btnm_get_pressed(btns);
+
+ mbox_action(btns, txt);
+
+ if (!btn_idx)
+ {
+ mbox_action(btns, txt);
+ _create_mbox_start_partitioning(NULL);
+ return LV_RES_INV;
+ }
+
+ return LV_RES_OK;
+}
+
+static lv_res_t _create_mbox_partitioning_next(lv_obj_t *btn)
+{
+ lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
+ lv_obj_set_style(dark_bg, &mbox_darken);
+ lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
+
+ static const char *mbox_btn_map[] = { "\222SD UMS", "\222Start", "\222Cancel", "" };
+ static const char *mbox_btn_map2[] = { "\222Start", "\222Cancel", "" };
+ lv_obj_t * mbox = lv_mbox_create(dark_bg, NULL);
+ lv_mbox_set_recolor_text(mbox, true);
+
+ char *txt_buf = malloc(0x1000);
+ lv_obj_set_width(mbox, LV_HOR_RES / 9 * 6);
+ lv_mbox_set_text(mbox, "#FF8000 Partition Manager#");
+
+ lv_obj_t *lbl_status = lv_label_create(mbox, NULL);
+ lv_label_set_recolor(lbl_status, true);
+
+ s_printf(txt_buf, "#FFDD00 Warning: This will partition your SD Card!#\n\n");
+
+ if (part_info.backup_possible)
+ s_printf(txt_buf + strlen(txt_buf), "#C7EA46 Your files will be backed up and restored!#");
+ else
+ s_printf(txt_buf + strlen(txt_buf), "#FFDD00 Your files will be wiped!#\n#FFDD00 Use USB UMS to copy them over!#");
+
+ lv_label_set_text(lbl_status, txt_buf);
+
+ if (part_info.backup_possible)
+ lv_mbox_add_btns(mbox, mbox_btn_map2, _create_mbox_partitioning_option1);
+ else
+ lv_mbox_add_btns(mbox, mbox_btn_map, _create_mbox_partitioning_option0);
+
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_top(mbox, true);
+
+ free(txt_buf);
+
+ return LV_RES_OK;
+}
+
+static void _update_partition_bar()
+{
+ lv_obj_t *h1 = lv_obj_get_parent(part_info.bar_hos);
+ u32 total_size = (part_info.total_sct - 0x8000) / 0x200000;
+ u32 bar_hos_size = lv_obj_get_width(h1) * (part_info.hos_size >> 10) / total_size;
+ u32 bar_emu_size = lv_obj_get_width(h1) * (part_info.emu_size >> 10) / total_size;
+ u32 bar_l4t_size = lv_obj_get_width(h1) * (part_info.l4t_size >> 10) / total_size;
+ u32 bar_and_size = lv_obj_get_width(h1) * (part_info.and_size >> 10) / total_size;
+
+ lv_obj_set_size(part_info.bar_hos, bar_hos_size, LV_DPI / 2);
+ lv_obj_set_size(part_info.bar_emu, bar_emu_size, LV_DPI / 2);
+ lv_obj_set_size(part_info.bar_l4t, bar_l4t_size, LV_DPI / 2);
+ lv_obj_set_size(part_info.bar_and, bar_and_size, LV_DPI / 2);
+
+ lv_obj_align(part_info.bar_emu, part_info.bar_hos, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
+ lv_obj_align(part_info.bar_l4t, part_info.bar_emu, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
+ lv_obj_align(part_info.bar_and, part_info.bar_l4t, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
+
+ lv_obj_set_size(part_info.sep_emu, bar_emu_size ? 8 : 0, LV_DPI / 2);
+ lv_obj_align(part_info.sep_emu, part_info.bar_hos, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
+
+ lv_obj_set_size(part_info.sep_l4t, bar_l4t_size ? 8 : 0, LV_DPI / 2);
+ lv_obj_align(part_info.sep_l4t, part_info.bar_emu, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
+
+ lv_obj_set_size(part_info.sep_and, bar_and_size ? 8 : 0, LV_DPI / 2);
+ lv_obj_align(part_info.sep_and, part_info.bar_l4t, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
+}
+
+static lv_res_t _action_slider_emu(lv_obj_t *slider)
+{
+ char lbl_text[64];
+
+ int slide_val = lv_slider_get_value(slider);
+ u32 size = slide_val ? ((slide_val < 2) ? 29856 : 59712) : 0;
+ s32 hos_size = (part_info.total_sct >> 11) - 16 - size - part_info.l4t_size - part_info.and_size;
+
+ if (hos_size > 2048)
+ {
+ part_info.emu_size = size;
+ part_info.hos_size = hos_size;
+
+ s_printf(lbl_text, "#96FF00 %d GiB#", hos_size >> 10);
+ lv_label_set_text(part_info.lbl_hos, lbl_text);
+ lv_bar_set_value(part_info.slider_bar_hos, hos_size >> 10);
+
+ if (slide_val < 2)
+ s_printf(lbl_text, "#FF3C28 %d GiB#", size >> 10);
+ else
+ s_printf(lbl_text, "#FF3C28 2x%d GiB#", size >> 11);
+ lv_label_set_text(part_info.lbl_emu, lbl_text);
+ }
+ else
+ lv_slider_set_value(slider, 0);
+
+ _update_partition_bar();
+
+ return LV_RES_OK;
+}
+
+static lv_res_t _action_slider_l4t(lv_obj_t *slider)
+{
+ char lbl_text[64];
+
+ u32 size = (u32)lv_slider_get_value(slider) << 10;
+ if (size < 4096)
+ size = 0;
+ else if (size < 8192)
+ size = 8192;
+
+ s32 hos_size = (part_info.total_sct >> 11) - 16 - part_info.emu_size - size - part_info.and_size;
+
+ if (hos_size > 2048)
+ {
+ if (size <= 8192)
+ lv_slider_set_value(slider, size >> 10);
+ }
+ else
+ {
+ size = (part_info.total_sct >> 11) - 16 - part_info.emu_size - part_info.and_size - 2048;
+ hos_size = (part_info.total_sct >> 11) - 16 - part_info.emu_size - part_info.and_size - size;
+ if (hos_size < 2048 || size < 8192)
+ {
+ lv_slider_set_value(slider, part_info.l4t_size >> 10);
+ goto out;
+ }
+ lv_slider_set_value(slider, size >> 10);
+ }
+
+ part_info.l4t_size = size;
+ part_info.hos_size = hos_size;
+
+ s_printf(lbl_text, "#96FF00 %d GiB#", hos_size >> 10);
+ lv_label_set_text(part_info.lbl_hos, lbl_text);
+ lv_bar_set_value(part_info.slider_bar_hos, hos_size >> 10);
+ s_printf(lbl_text, "#00DDFF %d GiB#", size >> 10);
+ lv_label_set_text(part_info.lbl_l4t, lbl_text);
+
+ _update_partition_bar();
+
+out:
+ return LV_RES_OK;
+}
+
+static lv_res_t _action_slider_and(lv_obj_t *slider)
+{
+ char lbl_text[64];
+
+ u32 user_size = (u32)lv_slider_get_value(slider) << 10;
+ if (user_size < 2048)
+ user_size = 0;
+ else if (user_size < 4096)
+ user_size = 4096;
+
+ u32 and_size = user_size ? (user_size + 4096) : 0; // Add Android reserved partitions size.
+ s32 hos_size = (part_info.total_sct >> 11) - 16 - part_info.emu_size - part_info.l4t_size - and_size;
+
+ if (hos_size > 2048)
+ {
+ if (user_size <= 4096)
+ lv_slider_set_value(slider, user_size >> 10);
+ }
+ else
+ {
+ and_size = (part_info.total_sct >> 11) - 16 - part_info.emu_size - part_info.l4t_size - 2048;
+ hos_size = (part_info.total_sct >> 11) - 16 - part_info.emu_size - part_info.l4t_size - and_size;
+ if (hos_size < 2048 || and_size < 8192)
+ {
+ lv_slider_set_value(slider, part_info.and_size >> 10);
+ goto out;
+ }
+ user_size = and_size - 4096;
+ lv_slider_set_value(slider, user_size >> 10);
+ }
+
+ part_info.and_size = and_size;
+ part_info.hos_size = hos_size;
+
+ s_printf(lbl_text, "#96FF00 %d GiB#", hos_size >> 10);
+ lv_label_set_text(part_info.lbl_hos, lbl_text);
+ lv_bar_set_value(part_info.slider_bar_hos, hos_size >> 10);
+ s_printf(lbl_text, "#FF8000 %d GiB#", user_size >> 10);
+ lv_label_set_text(part_info.lbl_and, lbl_text);
+
+ _update_partition_bar();
+
+out:
+ return LV_RES_OK;
+}
+
+static void create_mbox_check_files_total_size()
+{
+ static lv_style_t bar_hos_ind, bar_emu_ind, bar_l4t_ind, bar_and_ind;
+ static lv_style_t sep_emu_bg, sep_l4t_bg, sep_and_bg;
+
+ lv_style_copy(&bar_hos_ind, lv_theme_get_current()->bar.indic);
+ bar_hos_ind.body.main_color = LV_COLOR_HEX(0x96FF00);
+ bar_hos_ind.body.grad_color = bar_hos_ind.body.main_color;
+
+ lv_style_copy(&bar_emu_ind, lv_theme_get_current()->bar.indic);
+ bar_emu_ind.body.main_color = LV_COLOR_HEX(0xFF3C28);
+ bar_emu_ind.body.grad_color = bar_emu_ind.body.main_color;
+
+ lv_style_copy(&bar_l4t_ind, lv_theme_get_current()->bar.indic);
+ bar_l4t_ind.body.main_color = LV_COLOR_HEX(0x00DDFF);
+ bar_l4t_ind.body.grad_color = bar_l4t_ind.body.main_color;
+
+ lv_style_copy(&bar_and_ind, lv_theme_get_current()->bar.indic);
+ bar_and_ind.body.main_color = LV_COLOR_HEX(0xFF8000);
+ bar_and_ind.body.grad_color = bar_and_ind.body.main_color;
+
+ lv_style_copy(&sep_emu_bg, lv_theme_get_current()->cont);
+ sep_emu_bg.body.main_color = LV_COLOR_HEX(0xFF3C28);
+ sep_emu_bg.body.grad_color = sep_emu_bg.body.main_color;
+ sep_emu_bg.body.radius = 0;
+ lv_style_copy(&sep_l4t_bg, &sep_emu_bg);
+ sep_l4t_bg.body.main_color = LV_COLOR_HEX(0x00DDFF);
+ sep_l4t_bg.body.grad_color = sep_l4t_bg.body.main_color;
+ lv_style_copy(&sep_and_bg, &sep_emu_bg);
+ sep_and_bg.body.main_color = LV_COLOR_HEX(0xFF8000);
+ sep_and_bg.body.grad_color = sep_and_bg.body.main_color;
+
+ char *txt_buf = malloc(0x2000);
+
+ lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
+ lv_obj_set_style(dark_bg, &mbox_darken);
+ lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
+
+ static const char *mbox_btn_map[] = { "\211", "\222OK", "\211", "" };
+ lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
+ lv_mbox_set_recolor_text(mbox, true);
+ lv_obj_set_width(mbox, LV_HOR_RES / 9 * 6);
+
+ lv_mbox_set_text(mbox, "Analyzing SD card usage. This might take a while...");
+
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_top(mbox, true);
+ manual_system_maintenance(true);
+
+ char *path = malloc(1024);
+ u32 total_files = 0;
+ u32 total_size = 0;
+ path[0] = 0;
+
+ // Check total size of files.
+ _backup_and_restore_files(path, &total_files, &total_size, NULL, NULL, NULL);
+
+ // Not more than 1.0GB.
+ part_info.backup_possible = !(total_size > (RAM_DISK_SZ - 0x1000000)); // 0x2400000
+
+ if (part_info.backup_possible)
+ {
+ s_printf(txt_buf,
+ "#96FF00 Your SD Card files will be backed up automatically!#\n\n"
+ "#00DDFF Total files:# %d, #00DDFF Total size:# %d MiB", total_files, total_size >> 20);
+ lv_mbox_set_text(mbox, txt_buf);
+ }
+ else
+ {
+ lv_mbox_set_text(mbox,
+ "#FFDD00 Your SD Card cannot be backed up!#\n\n"
+ "You will be asked to back up your files later via UMS.");
+ }
+
+ // Create container to keep content inside.
+ lv_obj_t *h1 = lv_cont_create(mbox, NULL);
+ lv_cont_set_fit(h1, false, true);
+ lv_cont_set_style(h1, &lv_style_transp_tight);
+ lv_obj_set_width(h1, lv_obj_get_width(mbox) - LV_DPI * 3);
+
+ lv_obj_t *lbl_part = lv_label_create(h1, NULL);
+ lv_label_set_recolor(lbl_part, true);
+ lv_label_set_text(lbl_part, "#00DDFF Current partition layout:#");
+
+ // Read current MBR.
+ mbr_t mbr = { 0 };
+ sdmmc_storage_read(&sd_storage, 0, 1, &mbr);
+
+ total_size = (sd_storage.sec_cnt - 0x8000) / 0x200000;
+ u32 bar_hos_size = lv_obj_get_width(h1) * (mbr.partitions[0].size_sct / 0x200000) / total_size;
+ u32 bar_emu_size = 0;
+ for (u32 i = 1; i < 4; i++)
+ if (mbr.partitions[i].type == 0xE0)
+ bar_emu_size += mbr.partitions[i].size_sct;
+ bar_emu_size = lv_obj_get_width(h1) * (bar_emu_size / 0x200000) / total_size;
+
+ u32 bar_l4t_size = 0;
+ for (u32 i = 1; i < 4; i++)
+ if (mbr.partitions[i].type == 0x83)
+ bar_l4t_size += mbr.partitions[i].size_sct;
+ bar_l4t_size = lv_obj_get_width(h1) * (bar_l4t_size / 0x200000) / total_size;
+
+ u32 bar_and_size = lv_obj_get_width(h1) - bar_hos_size - bar_emu_size - bar_l4t_size;
+
+ // Create bar objects.
+ lv_obj_t *bar_mbr_hos = lv_bar_create(h1, NULL);
+ lv_obj_set_size(bar_mbr_hos, bar_hos_size, LV_DPI / 3);
+ lv_bar_set_range(bar_mbr_hos, 0, 1);
+ lv_bar_set_value(bar_mbr_hos, 1);
+ lv_bar_set_style(bar_mbr_hos, LV_BAR_STYLE_INDIC, &bar_hos_ind);
+ lv_obj_align(bar_mbr_hos, lbl_part, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 6);
+
+ lv_obj_t *bar_mbr_emu = lv_bar_create(h1, bar_mbr_hos);
+ lv_obj_set_size(bar_mbr_emu, bar_emu_size, LV_DPI / 3);
+ lv_bar_set_style(bar_mbr_emu, LV_BAR_STYLE_INDIC, &bar_emu_ind);
+ lv_obj_align(bar_mbr_emu, bar_mbr_hos, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
+
+ lv_obj_t *bar_mbr_l4t = lv_bar_create(h1, bar_mbr_hos);
+ lv_obj_set_size(bar_mbr_l4t, bar_l4t_size, LV_DPI / 3);
+ lv_bar_set_style(bar_mbr_l4t, LV_BAR_STYLE_INDIC, &bar_l4t_ind);
+ lv_obj_align(bar_mbr_l4t, bar_mbr_emu, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
+
+ lv_obj_t *bar_mbr_rest = lv_bar_create(h1, bar_mbr_hos);
+ lv_obj_set_size(bar_mbr_rest, bar_and_size > 1 ? bar_and_size : 0, LV_DPI / 3);
+ lv_bar_set_style(bar_mbr_rest, LV_BAR_STYLE_INDIC, &bar_and_ind);
+ lv_obj_align(bar_mbr_rest, bar_mbr_l4t, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
+
+ // Create separator objects.
+ lv_obj_t *sep_mbr_emu = lv_cont_create(h1, NULL);
+ lv_obj_set_size(sep_mbr_emu, bar_emu_size ? 8 : 0, LV_DPI / 3);
+ lv_obj_set_style(sep_mbr_emu, &sep_emu_bg);
+ lv_obj_align(sep_mbr_emu, bar_mbr_hos, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
+
+ lv_obj_t *sep_mbr_l4t = lv_cont_create(h1, sep_mbr_emu);
+ lv_obj_set_size(sep_mbr_l4t, bar_l4t_size ? 8 : 0, LV_DPI / 3);
+ lv_obj_set_style(sep_mbr_l4t, &sep_l4t_bg);
+ lv_obj_align(sep_mbr_l4t, bar_mbr_emu, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
+
+ lv_obj_t *sep_mbr_rest = lv_cont_create(h1, sep_mbr_emu);
+ lv_obj_set_size(sep_mbr_rest, bar_and_size ? (bar_and_size > 1 ? 8 : 0) : 0, LV_DPI / 3);
+ lv_obj_set_style(sep_mbr_rest, &sep_and_bg);
+ lv_obj_align(sep_mbr_rest, bar_mbr_l4t, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
+
+ // Print partition table info.
+ s_printf(txt_buf,
+ "Partition 0 - Type: %02x, Start: %08x, Size: %08x\n"
+ "Partition 1 - Type: %02x, Start: %08x, Size: %08x\n"
+ "Partition 2 - Type: %02x, Start: %08x, Size: %08x\n"
+ "Partition 3 - Type: %02x, Start: %08x, Size: %08x",
+ mbr.partitions[0].type, mbr.partitions[0].start_sct, mbr.partitions[0].size_sct,
+ mbr.partitions[1].type, mbr.partitions[1].start_sct, mbr.partitions[1].size_sct,
+ mbr.partitions[2].type, mbr.partitions[2].start_sct, mbr.partitions[2].size_sct,
+ mbr.partitions[3].type, mbr.partitions[3].start_sct, mbr.partitions[3].size_sct);
+
+ lv_obj_t *lbl_table = lv_label_create(h1, NULL);
+ lv_label_set_style(lbl_table, &monospace_text);
+ lv_label_set_text(lbl_table, txt_buf);
+ lv_obj_align(lbl_table, h1, LV_ALIGN_IN_TOP_MID, 0, LV_DPI);
+
+ lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action);
+
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+
+ free(txt_buf);
+ free(path);
+}
+
+static lv_res_t _action_fix_mbr(lv_obj_t *btn)
+{
+ lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
+ lv_obj_set_style(dark_bg, &mbox_darken);
+ lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
+
+ static const char *mbox_btn_map[] = { "\211", "\222OK", "\211", "" };
+ lv_obj_t * mbox = lv_mbox_create(dark_bg, NULL);
+ lv_mbox_set_recolor_text(mbox, true);
+
+ lv_obj_set_width(mbox, LV_HOR_RES / 9 * 6);
+ lv_mbox_set_text(mbox, "#FF8000 Fix Hybrid MBR#");
+
+ lv_obj_t *lbl_status = lv_label_create(mbox, NULL);
+ lv_label_set_recolor(lbl_status, true);
+
+ if (!sd_mount())
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Failed to init SD!#");
+ goto out;
+ }
+
+ mbr_t mbr[2] = { 0 };
+ gpt_t gpt = { 0 };
+
+ sdmmc_storage_read(&sd_storage, 0, 1, &mbr[0]);
+ sdmmc_storage_read(&sd_storage, 1, sizeof(gpt_t) >> 9, &gpt);
+
+ memcpy(&mbr[1], &mbr[0], sizeof(mbr_t));
+
+ sd_unmount(false);
+
+ if (memcmp(&gpt.header.signature, "EFI PART", 8))
+ {
+ lv_label_set_text(lbl_status, "#FFDD00 Warning:# No GPT was found!");
+ goto out;
+ }
+
+ // Parse GPT.
+ LIST_INIT(gpt_parsed);
+ for (u32 i = 0; i < gpt.header.num_part_ents; i++)
+ {
+ emmc_part_t *part = (emmc_part_t *)calloc(sizeof(emmc_part_t), 1);
+
+ if (gpt.entries[i].lba_start < gpt.header.first_use_lba)
+ continue;
+
+ part->index = i;
+ part->lba_start = gpt.entries[i].lba_start;
+ part->lba_end = gpt.entries[i].lba_end;
+ part->attrs = gpt.entries[i].attrs;
+
+ // ASCII conversion. Copy only the LSByte of the UTF-16LE name.
+ for (u32 j = 0; j < 36; j++)
+ part->name[j] = gpt.entries[i].name[j];
+ part->name[35] = 0;
+
+ list_append(&gpt_parsed, &part->link);
+ }
+
+ u32 mbr_idx = 0;
+ LIST_FOREACH_ENTRY(emmc_part_t, part, &gpt_parsed, link)
+ {
+ if (!strcmp(part->name, "hos_data"))
+ {
+ mbr[1].partitions[mbr_idx].type = sd_fs.fs_type == FS_EXFAT ? 0x7 : 0xC;
+ mbr[1].partitions[mbr_idx].start_sct = part->lba_start;
+ mbr[1].partitions[mbr_idx].size_sct = (part->lba_end - part->lba_start + 1);
+ mbr_idx++;
+ }
+ if (!strcmp(part->name, "emummc") || !strcmp(part->name, "emummc2"))
+ {
+ mbr[1].partitions[mbr_idx].type = 0xE0;
+ mbr[1].partitions[mbr_idx].start_sct = part->lba_start;
+ mbr[1].partitions[mbr_idx].size_sct = (part->lba_end - part->lba_start + 1);
+ mbr_idx++;
+ }
+
+ if (mbr_idx > 2)
+ break;
+ }
+
+ mbr[1].partitions[mbr_idx].type = 0xEE; // GPT protective partition.
+ mbr[1].partitions[mbr_idx].start_sct = 1;
+ mbr[1].partitions[mbr_idx].size_sct = sd_storage.sec_cnt - 1;
+
+ // Check for differences.
+ bool changed = false;
+ for (u32 i = 1; i < 4; i++)
+ {
+ if ((mbr[0].partitions[i].type != mbr[1].partitions[i].type) ||
+ (mbr[0].partitions[i].start_sct != mbr[1].partitions[i].start_sct) ||
+ (mbr[0].partitions[i].size_sct != mbr[1].partitions[i].size_sct))
+ {
+ changed = true;
+ break;
+ }
+ }
+
+ if (!changed)
+ {
+ lv_label_set_text(lbl_status, "#96FF00 Warning:# The Hybrid MBR needs no change!#");
+ goto out;
+ }
+
+ char *txt_buf = malloc(0x4000);
+
+ s_printf(txt_buf, "#00DDFF Current MBR Layout:#\n");
+ s_printf(txt_buf + strlen(txt_buf),
+ "Partition 0 - Type: %02x, Start: %08x, Size: %08x\n"
+ "Partition 1 - Type: %02x, Start: %08x, Size: %08x\n"
+ "Partition 2 - Type: %02x, Start: %08x, Size: %08x\n"
+ "Partition 3 - Type: %02x, Start: %08x, Size: %08x\n\n",
+ mbr[0].partitions[0].type, mbr[0].partitions[0].start_sct, mbr[0].partitions[0].size_sct,
+ mbr[0].partitions[1].type, mbr[0].partitions[1].start_sct, mbr[0].partitions[1].size_sct,
+ mbr[0].partitions[2].type, mbr[0].partitions[2].start_sct, mbr[0].partitions[2].size_sct,
+ mbr[0].partitions[3].type, mbr[0].partitions[3].start_sct, mbr[0].partitions[3].size_sct);
+
+ s_printf(txt_buf + strlen(txt_buf), "#00DDFF New MBR Layout:#\n");
+ s_printf(txt_buf + strlen(txt_buf),
+ "Partition 0 - Type: %02x, Start: %08x, Size: %08x\n"
+ "Partition 1 - Type: %02x, Start: %08x, Size: %08x\n"
+ "Partition 2 - Type: %02x, Start: %08x, Size: %08x\n"
+ "Partition 3 - Type: %02x, Start: %08x, Size: %08x",
+ mbr[1].partitions[0].type, mbr[1].partitions[0].start_sct, mbr[1].partitions[0].size_sct,
+ mbr[1].partitions[1].type, mbr[1].partitions[1].start_sct, mbr[1].partitions[1].size_sct,
+ mbr[1].partitions[2].type, mbr[1].partitions[2].start_sct, mbr[1].partitions[2].size_sct,
+ mbr[1].partitions[3].type, mbr[1].partitions[3].start_sct, mbr[1].partitions[3].size_sct);
+
+ lv_label_set_text(lbl_status, txt_buf);
+ lv_label_set_style(lbl_status, &monospace_text);
+
+ free(txt_buf);
+
+ lbl_status = lv_label_create(mbox, NULL);
+ lv_label_set_recolor(lbl_status, true);
+ lv_label_set_align(lbl_status, LV_LABEL_ALIGN_CENTER);
+
+ lv_label_set_text(lbl_status,
+ "#FF8000 Warning: Do you really want to continue?!#\n\n"
+ "Press #FF8000 POWER# to Continue.\nPress #FF8000 VOL# to abort.");
+
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_top(mbox, true);
+
+ manual_system_maintenance(true);
+
+ if (btn_wait() & BTN_POWER)
+ {
+ // Write MBR.
+ sd_mount();
+ sdmmc_storage_write(&sd_storage, 0, 1, &mbr[1]);
+ sd_unmount(false);
+ lv_label_set_text(lbl_status, "#96FF00 The new Hybrid MBR was written successfully!#");
+ }
+ else
+ lv_label_set_text(lbl_status, "#FFDD00 Warning: The Hybrid MBR Fix was canceled!#");
+
+out:
+ lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action);
+
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_top(mbox, true);
+
+ return LV_RES_OK;
+}
+
+lv_res_t create_window_partition_manager(lv_obj_t *btn)
+{
+ lv_obj_t *win = nyx_create_standard_window(SYMBOL_SD" Partition Manager");
+
+ lv_win_add_btn(win, NULL, SYMBOL_MODULES_ALT" Fix Hybrid MBR", _action_fix_mbr);
+
+ static lv_style_t bar_hos_bg, bar_emu_bg, bar_l4t_bg, bar_and_bg;
+ static lv_style_t bar_hos_ind, bar_emu_ind, bar_l4t_ind, bar_and_ind;
+ static lv_style_t bar_hos_btn, bar_emu_btn, bar_l4t_btn, bar_and_btn;
+ static lv_style_t sep_emu_bg, sep_l4t_bg, sep_and_bg;
+
+ lv_style_copy(&bar_hos_bg, lv_theme_get_current()->bar.bg);
+ bar_hos_bg.body.main_color = LV_COLOR_HEX(0x4A8000);
+ bar_hos_bg.body.grad_color = bar_hos_bg.body.main_color;
+ lv_style_copy(&bar_hos_ind, lv_theme_get_current()->bar.indic);
+ bar_hos_ind.body.main_color = LV_COLOR_HEX(0x96FF00);
+ bar_hos_ind.body.grad_color = bar_hos_ind.body.main_color;
+ lv_style_copy(&bar_hos_btn, lv_theme_get_current()->slider.knob);
+ bar_hos_btn.body.main_color = LV_COLOR_HEX(0x77CC00);
+ bar_hos_btn.body.grad_color = bar_hos_btn.body.main_color;
+
+ lv_style_copy(&bar_emu_bg, lv_theme_get_current()->bar.bg);
+ bar_emu_bg.body.main_color = LV_COLOR_HEX(0x940F00);
+ bar_emu_bg.body.grad_color = bar_emu_bg.body.main_color;
+ lv_style_copy(&bar_emu_ind, lv_theme_get_current()->bar.indic);
+ bar_emu_ind.body.main_color = LV_COLOR_HEX(0xFF3C28);
+ bar_emu_ind.body.grad_color = bar_emu_ind.body.main_color;
+ lv_style_copy(&bar_emu_btn, lv_theme_get_current()->slider.knob);
+ bar_emu_btn.body.main_color = LV_COLOR_HEX(0xB31200);
+ bar_emu_btn.body.grad_color = bar_emu_btn.body.main_color;
+ lv_style_copy(&sep_emu_bg, lv_theme_get_current()->cont);
+ sep_emu_bg.body.main_color = LV_COLOR_HEX(0xFF3C28);
+ sep_emu_bg.body.grad_color = sep_emu_bg.body.main_color;
+ sep_emu_bg.body.radius = 0;
+
+ lv_style_copy(&bar_l4t_bg, lv_theme_get_current()->bar.bg);
+ bar_l4t_bg.body.main_color = LV_COLOR_HEX(0x006E80);
+ bar_l4t_bg.body.grad_color = bar_l4t_bg.body.main_color;
+ lv_style_copy(&bar_l4t_ind, lv_theme_get_current()->bar.indic);
+ bar_l4t_ind.body.main_color = LV_COLOR_HEX(0x00DDFF);
+ bar_l4t_ind.body.grad_color = bar_l4t_ind.body.main_color;
+ lv_style_copy(&bar_l4t_btn, lv_theme_get_current()->slider.knob);
+ bar_l4t_btn.body.main_color = LV_COLOR_HEX(0x00B1CC);
+ bar_l4t_btn.body.grad_color = bar_l4t_btn.body.main_color;
+ lv_style_copy(&sep_l4t_bg, &sep_emu_bg);
+ sep_l4t_bg.body.main_color = LV_COLOR_HEX(0x00DDFF);
+ sep_l4t_bg.body.grad_color = sep_l4t_bg.body.main_color;
+
+ lv_style_copy(&bar_and_bg, lv_theme_get_current()->bar.bg);
+ bar_and_bg.body.main_color = LV_COLOR_HEX(0x804000);
+ bar_and_bg.body.grad_color = bar_and_bg.body.main_color;
+ lv_style_copy(&bar_and_ind, lv_theme_get_current()->bar.indic);
+ bar_and_ind.body.main_color = LV_COLOR_HEX(0xFF8000);
+ bar_and_ind.body.grad_color = bar_and_ind.body.main_color;
+ lv_style_copy(&bar_and_btn, lv_theme_get_current()->slider.knob);
+ bar_and_btn.body.main_color = LV_COLOR_HEX(0xCC6600);
+ bar_and_btn.body.grad_color = bar_and_btn.body.main_color;
+ lv_style_copy(&sep_and_bg, &sep_emu_bg);
+ sep_and_bg.body.main_color = LV_COLOR_HEX(0xFF8000);
+ sep_and_bg.body.grad_color = sep_and_bg.body.main_color;
+
+ lv_obj_t *sep = lv_label_create(win, NULL);
+ lv_label_set_static_text(sep, "");
+ lv_obj_align(sep, NULL, LV_ALIGN_IN_TOP_MID, 0, 0);
+
+ // Create container to keep content inside.
+ lv_obj_t *h1 = lv_cont_create(win, NULL);
+ lv_obj_set_size(h1, LV_HOR_RES - (LV_DPI * 8 / 10), LV_VER_RES - LV_DPI);
+
+ if (!sd_mount())
+ {
+ lv_obj_t *lbl = lv_label_create(h1, NULL);
+ lv_label_set_text(lbl, "#FFDD00 Failed to init SD!#");
+ return LV_RES_OK;
+ }
+
+ memset(&part_info, 0, sizeof(partition_ctxt_t));
+ create_mbox_check_files_total_size();
+
+ char *txt_buf = malloc(0x2000);
+
+ part_info.total_sct = sd_storage.sec_cnt;
+ u32 extra_sct = 0x8000 + 0x400000; // Reserved 16MB alignment for FAT partition + 2GB.
+
+ // Read current MBR.
+ mbr_t mbr = { 0 };
+ sdmmc_storage_read(&sd_storage, 0, 1, &mbr);
+
+ u32 bar_hos_size = lv_obj_get_width(h1);
+ u32 bar_emu_size = 0;
+ u32 bar_l4t_size = 0;
+ u32 bar_and_size = 0;
+
+ lv_obj_t *lbl = lv_label_create(h1, NULL);
+ lv_label_set_text(lbl, "New partition layout:");
+
+ lv_obj_t *bar_hos = lv_bar_create(h1, NULL);
+ lv_obj_set_size(bar_hos, bar_hos_size, LV_DPI / 2);
+ lv_bar_set_range(bar_hos, 0, 1);
+ lv_bar_set_value(bar_hos, 1);
+ lv_bar_set_style(bar_hos, LV_BAR_STYLE_INDIC, &bar_hos_ind);
+ lv_obj_align(bar_hos, lbl, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 6);
+ part_info.bar_hos = bar_hos;
+
+ lv_obj_t *bar_emu = lv_bar_create(h1, bar_hos);
+ lv_obj_set_size(bar_emu, bar_emu_size, LV_DPI / 2);
+ lv_bar_set_style(bar_emu, LV_BAR_STYLE_INDIC, &bar_emu_ind);
+ lv_obj_align(bar_emu, bar_hos, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
+ part_info.bar_emu = bar_emu;
+
+ lv_obj_t *bar_l4t = lv_bar_create(h1, bar_hos);
+ lv_obj_set_size(bar_l4t, bar_l4t_size, LV_DPI / 2);
+ lv_bar_set_style(bar_l4t, LV_BAR_STYLE_INDIC, &bar_l4t_ind);
+ lv_obj_align(bar_l4t, bar_emu, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
+ part_info.bar_l4t = bar_l4t;
+
+ lv_obj_t *bar_and = lv_bar_create(h1, bar_hos);
+ lv_obj_set_size(bar_and, bar_and_size, LV_DPI / 2);
+ lv_bar_set_style(bar_and, LV_BAR_STYLE_INDIC, &bar_and_ind);
+ lv_obj_align(bar_and, bar_l4t, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
+ part_info.bar_and = bar_and;
+
+ lv_obj_t *sep_emu = lv_cont_create(h1, NULL);
+ lv_cont_set_fit(sep_emu, false, false);
+ lv_obj_set_size(sep_emu, 0, LV_DPI / 2); // 8.
+ lv_obj_set_style(sep_emu, &sep_emu_bg);
+ lv_obj_align(sep_emu, bar_hos, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
+ part_info.sep_emu = sep_emu;
+
+ lv_obj_t *sep_l4t = lv_cont_create(h1, sep_emu);
+ lv_obj_set_style(sep_l4t, &sep_l4t_bg);
+ lv_obj_align(sep_l4t, bar_emu, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
+ part_info.sep_l4t = sep_l4t;
+
+ lv_obj_t *sep_and = lv_cont_create(h1, sep_emu);
+ lv_obj_set_style(sep_and, &sep_and_bg);
+ lv_obj_align(sep_and, bar_l4t, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
+ part_info.sep_and = sep_and;
+
+ lv_obj_t *lbl_hos = lv_label_create(h1, NULL);
+ lv_label_set_recolor(lbl_hos, true);
+ lv_label_set_static_text(lbl_hos, "#96FF00 "SYMBOL_DOT" HOS (FAT32):#");
+ lv_obj_align(lbl_hos, bar_hos, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 2);
+
+ lv_obj_t *lbl_emu = lv_label_create(h1, lbl_hos);
+ lv_label_set_static_text(lbl_emu, "#FF3C28 "SYMBOL_DOT" emuMMC (RAW):#");
+ lv_obj_align(lbl_emu, lbl_hos, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
+
+ lv_obj_t *lbl_l4t = lv_label_create(h1, lbl_hos);
+ lv_label_set_static_text(lbl_l4t, "#00DDFF "SYMBOL_DOT" Linux (EXT4):#");
+ lv_obj_align(lbl_l4t, lbl_emu, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
+
+ lv_obj_t *lbl_and = lv_label_create(h1, lbl_hos);
+ lv_label_set_static_text(lbl_and, "#FF8000 "SYMBOL_DOT" Android (USER):#");
+ lv_obj_align(lbl_and, lbl_l4t, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
+
+ lv_obj_t *slider_bar_hos = lv_bar_create(h1, NULL);
+ lv_obj_set_size(slider_bar_hos, LV_DPI * 7, LV_DPI * 3 / 17);
+ lv_bar_set_range(slider_bar_hos, 0, (part_info.total_sct - 0x8000) / 0x200000);
+ lv_bar_set_value(slider_bar_hos, (part_info.total_sct - 0x8000) / 0x200000);
+ lv_bar_set_style(slider_bar_hos, LV_SLIDER_STYLE_BG, &bar_hos_bg);
+ lv_bar_set_style(slider_bar_hos, LV_SLIDER_STYLE_INDIC, &bar_hos_ind);
+ lv_obj_align(slider_bar_hos, lbl_hos, LV_ALIGN_OUT_RIGHT_MID, LV_DPI * 6 / 4, 0);
+ part_info.slider_bar_hos = slider_bar_hos;
+
+ lv_obj_t *slider_emu = lv_slider_create(h1, NULL);
+ lv_obj_set_size(slider_emu, LV_DPI * 7, LV_DPI / 3);
+ lv_slider_set_range(slider_emu, 0, 2);
+ lv_slider_set_value(slider_emu, 0);
+ lv_slider_set_style(slider_emu, LV_SLIDER_STYLE_BG, &bar_emu_bg);
+ lv_slider_set_style(slider_emu, LV_SLIDER_STYLE_INDIC, &bar_emu_ind);
+ lv_slider_set_style(slider_emu, LV_SLIDER_STYLE_KNOB, &bar_emu_btn);
+ lv_obj_align(slider_emu, slider_bar_hos, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3 + 5);
+ lv_slider_set_action(slider_emu, _action_slider_emu);
+ part_info.slider_emu = slider_bar_hos;
+
+ lv_obj_t *slider_l4t = lv_slider_create(h1, NULL);
+ lv_obj_set_size(slider_l4t, LV_DPI * 7, LV_DPI / 3);
+ lv_slider_set_range(slider_l4t, 0, (part_info.total_sct - extra_sct) / 0x200000);
+ lv_slider_set_value(slider_l4t, 0);
+ lv_slider_set_style(slider_l4t, LV_SLIDER_STYLE_BG, &bar_l4t_bg);
+ lv_slider_set_style(slider_l4t, LV_SLIDER_STYLE_INDIC, &bar_l4t_ind);
+ lv_slider_set_style(slider_l4t, LV_SLIDER_STYLE_KNOB, &bar_l4t_btn);
+ lv_obj_align(slider_l4t, slider_emu, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3 - 3);
+ lv_slider_set_action(slider_l4t, _action_slider_l4t);
+ part_info.slider_l4t = slider_l4t;
+
+ lv_obj_t *slider_and = lv_slider_create(h1, NULL);
+ lv_obj_set_size(slider_and, LV_DPI * 7, LV_DPI / 3);
+ lv_slider_set_range(slider_and, 0, (part_info.total_sct - extra_sct) / 0x200000 - 4); // Subtract android reserved size.
+ lv_slider_set_value(slider_and, 0);
+ lv_slider_set_style(slider_and, LV_SLIDER_STYLE_BG, &bar_and_bg);
+ lv_slider_set_style(slider_and, LV_SLIDER_STYLE_INDIC, &bar_and_ind);
+ lv_slider_set_style(slider_and, LV_SLIDER_STYLE_KNOB, &bar_and_btn);
+ lv_obj_align(slider_and, slider_l4t, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3 - 3);
+ lv_slider_set_action(slider_and, _action_slider_and);
+ part_info.slider_and = slider_and;
+
+ lv_obj_t *lbl_sl_hos = lv_label_create(h1, NULL);
+ lv_label_set_recolor(lbl_sl_hos, true);
+ s_printf(txt_buf, "#96FF00 %d GiB#", (part_info.total_sct - 0x8000) >> 11 >> 10);
+ lv_label_set_text(lbl_sl_hos, txt_buf);
+ lv_obj_align(lbl_sl_hos, slider_bar_hos, LV_ALIGN_OUT_RIGHT_MID, LV_DPI * 4 / 7, 0);
+ part_info.lbl_hos = lbl_sl_hos;
+
+ lv_obj_t *lbl_sl_emu = lv_label_create(h1, lbl_sl_hos);
+ lv_label_set_text(lbl_sl_emu, "#FF3C28 0 GiB#");
+ lv_obj_align(lbl_sl_emu, lbl_sl_hos, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
+ part_info.lbl_emu = lbl_sl_emu;
+
+ lv_obj_t *lbl_sl_l4t = lv_label_create(h1, lbl_sl_hos);
+ lv_label_set_text(lbl_sl_l4t, "#00DDFF 0 GiB#");
+ lv_obj_align(lbl_sl_l4t, lbl_sl_emu, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
+ part_info.lbl_l4t = lbl_sl_l4t;
+
+ lv_obj_t *lbl_sl_and = lv_label_create(h1, lbl_sl_hos);
+ lv_label_set_text(lbl_sl_and, "#FF8000 0 GiB#");
+ lv_obj_align(lbl_sl_and, lbl_sl_l4t, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
+ part_info.lbl_and = lbl_sl_and;
+
+ lv_obj_t *lbl_notes = lv_label_create(h1, NULL);
+ lv_label_set_recolor(lbl_notes, true);
+ lv_label_set_static_text(lbl_notes,
+ "Note 1: Only up to #C7EA46 1GB# can be backed up. If more, you will be asked to back them manually at the next step.\n"
+ "Note 2: The #C7EA46 Flash Linux# and #C7EA46 Flash Android# work independently and will flash files if suitable partitions and installer files are found.\n"
+ "Note 3: The installation files reside in #C7EA46 switchroot/install# folder. Linux uses #C7EA46 l4t.XX# and Android uses #C7EA46 twrp.img# and #C7EA46 tegra210-icosa.dtb#.\n"
+ "Note 4: #FFDD00 The installation files will be deleted after a successful flashing.#");
+ lv_label_set_style(lbl_notes, &hint_small_style);
+ lv_obj_align(lbl_notes, lbl_and, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 5);
+
+ lv_obj_t *btn1 = lv_btn_create(h1, NULL);
+ lv_obj_t *label_btn = lv_label_create(btn1, NULL);
+ lv_btn_set_fit(btn1, true, true);
+ lv_label_set_static_text(label_btn, SYMBOL_USB" SD UMS");
+ lv_obj_align(btn1, h1, LV_ALIGN_IN_TOP_LEFT, 0, LV_DPI * 5);
+ lv_btn_set_action(btn1, LV_BTN_ACTION_CLICK, _action_part_manager_ums_sd);
+
+ lv_obj_t *btn2 = lv_btn_create(h1, NULL);
+ lv_obj_t *label_btn2 = lv_label_create(btn2, NULL);
+ lv_btn_set_fit(btn2, true, true);
+ lv_label_set_static_text(label_btn2, SYMBOL_DOWNLOAD" Flash Linux");
+ lv_obj_align(btn2, btn1, LV_ALIGN_OUT_RIGHT_MID, LV_DPI / 3, 0);
+ lv_btn_set_action(btn2, LV_BTN_ACTION_CLICK, _action_check_flash_linux);
+
+ btn1 = lv_btn_create(h1, NULL);
+ label_btn = lv_label_create(btn1, NULL);
+ lv_btn_set_fit(btn1, true, true);
+ lv_label_set_static_text(label_btn, SYMBOL_DOWNLOAD" Flash Android");
+ lv_obj_align(btn1, btn2, LV_ALIGN_OUT_RIGHT_MID, LV_DPI / 3, 0);
+ lv_btn_set_action(btn1, LV_BTN_ACTION_CLICK, _action_flash_android);
+
+ btn1 = lv_btn_create(h1, NULL);
+ label_btn = lv_label_create(btn1, NULL);
+ lv_btn_set_fit(btn1, true, true);
+ lv_label_set_static_text(label_btn, SYMBOL_SD" Next Step");
+ lv_obj_align(btn1, h1, LV_ALIGN_IN_TOP_RIGHT, 0, LV_DPI * 5);
+ lv_btn_set_action(btn1, LV_BTN_ACTION_CLICK, _create_mbox_partitioning_next);
+ part_info.btn_partition = btn1;
+
+ free(txt_buf);
+
+ sd_unmount(false);
+
+ return LV_RES_OK;
+}
diff --git a/nyx/nyx_gui/frontend/gui_tools_partition_manager.h b/nyx/nyx_gui/frontend/gui_tools_partition_manager.h
new file mode 100644
index 0000000..005016a
--- /dev/null
+++ b/nyx/nyx_gui/frontend/gui_tools_partition_manager.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2019-2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 .
+ */
+
+#ifndef _GUI_TOOLS_PART_MANAGER_H_
+#define _GUI_TOOLS_PART_MANAGER_H_
+
+lv_res_t create_window_partition_manager(lv_obj_t *btn);
+
+#endif