/*
 * Copyright (c) 2019-2024 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 <http://www.gnu.org/licenses/>.
 */

#include <fatfs_cfg.h>
#include <libs/fatfs/ff.h>
#include <libs/lvgl/lv_core/lv_obj.h>
#include <libs/lvgl/lv_misc/lv_area.h>
#include <libs/lvgl/lv_objx/lv_bar.h>
#include <libs/lvgl/lv_objx/lv_btnm.h>
#include <libs/lvgl/lv_objx/lv_label.h>
#include <libs/lvgl/lv_objx/lv_mbox.h>
#include <libs/lvgl/lv_objx/lv_slider.h>
#include <mem/heap.h>
#include <memory_map.h>
#include <stdlib.h>

#include <bdk.h>

#include "gui.h"
#include "gui_tools.h"
#include "gui_tools_partition_manager.h"
#include <libs/fatfs/diskio.h>
#include <libs/lvgl/lvgl.h>
#include <storage/boot_storage.h>
#include <storage/emmc.h>
#include <storage/mbr_gpt.h>
#include <storage/nx_emmc_bis.h>
#include <storage/sd.h>
#include <storage/sdmmc.h>
#include <string.h>
#include <utils/btn.h>
#include <utils/sprintf.h>
#include <utils/types.h>
#include "../hos/hos.h"
#include "../storage/sfd.h"

#define AU_ALIGN_SECTORS 0x8000 // 16MB.
#define AU_ALIGN_BYTES   (AU_ALIGN_SECTORS * SD_BLOCKSIZE)

#define SECTORS_PER_GB   0x200000

#define HOS_MIN_SIZE_MB        2048
#define ANDROID_SYSTEM_SIZE_MB 6144 // 6 GB. Fits both Legacy (4912MB) and Dynamic (6144MB) partition schemes.
#define ANDROID_SYSTEM_SIZE_LEGACY_MB 4912

#define ENABLE_DUAL_ANDROID 1

extern volatile boot_cfg_t *b_cfg;
extern volatile nyx_storage_t *nyx_str;

// #define DBG_PRINT(msg) gfx_printf(msg); gfx_putc('\n');
#define DBG_PRINT(msg) 
// #define DBG_PRINT_ARGS(msg, ...) gfx_printf(msg, __VA_ARGS__); gfx_putc('\n');
#define DBG_PRINT_ARGS(msg, ...) 


typedef struct _partition_ctxt_t
{
	u32 total_sct;
	// total sectors available for partitions (e.g. total sectors - gpt - backup gpt, and aligned to 16mb)
	u32 total_sct_available; 

	u32 alignment;
	int backup_possible;
	bool skip_backup;
	u8 drive;

	u32 hos_os_og_size;

	s32 hos_size;
	u32 emu_size;
	u32 l4t_size;
	u32 and_size;
	u32 hos_os_size;
	u32 emu_sd_size;

	u32 hos_os_align;

	u32 hos_sys_size_mb;
	s32 hos_min_size_mb;

	bool emu_double;
	bool and_double;
	bool emu_sd_double;
	bool emmc_is_64gb;
	bool auto_assign_free_storage;

	bool and_dynamic;

	mbr_t mbr_old;

	lv_obj_t *bar_hos;
	lv_obj_t *bar_emu;
	lv_obj_t *bar_l4t;
	lv_obj_t *bar_and;
	lv_obj_t *bar_hos_os;
	lv_obj_t *bar_remaining;
	lv_obj_t *bar_emu_sd;

	lv_obj_t *sep_emu;
	lv_obj_t *sep_l4t;
	lv_obj_t *sep_and;
	lv_obj_t *sep_hos;
	lv_obj_t *sep_hos_os;
	lv_obj_t *sep_emu_sd;

	lv_obj_t *slider_bar_hos;
	lv_obj_t *slider_emu;
	lv_obj_t *slider_l4t;
	lv_obj_t *slider_and;
	lv_obj_t *slider_hos_os;
	lv_obj_t *slider_emu_sd;

	lv_obj_t *lbl_hos;
	lv_obj_t *lbl_emu;
	lv_obj_t *lbl_l4t;
	lv_obj_t *lbl_and;
	lv_obj_t *lbl_hos_os;
	lv_obj_t *lbl_emu_sd;
} partition_ctxt_t;

typedef struct _l4t_flasher_ctxt_t
{
	u32 offset_sct;
	u32 image_size_sct;
} l4t_flasher_ctxt_t;

typedef struct _android_flasher_ctxt_t{
	u32 slot;
} android_flasher_ctxt_t;

partition_ctxt_t part_info;
l4t_flasher_ctxt_t l4t_flash_ctxt;
android_flasher_ctxt_t android_flash_ctxt;

lv_obj_t *btn_flash_l4t;
lv_obj_t *btn_flash_android;

static void _wctombs(const u16 *src, char *dest, u32 len_max){
	const u16 *cur = src;
	do{
		*dest++ = *cur & 0xff;
		len_max--;
	}while(*cur++ && len_max);
}

static void _ctowcs(const char *src, u16 *dest, u32 len_max){
	const char *cur = src;
	do{
		*dest++ = *cur;
		len_max--; 
	}while(*cur++ && len_max);
}

static bool _has_gpt(const mbr_t *mbr){
	for(u32 i = 0; i < 4; i++){
		if(mbr->partitions[i].type == 0xee){
			return true;
		}
	}
	return false;
}

int _copy_file(const char *src, const char *dst, const char *path)
{
	FIL fp_src;
	FIL fp_dst;
	int res;

	// Open file for reading.
	f_chdrive(src);
	res = f_open(&fp_src, path, FA_READ);
	if (res != FR_OK)
		return res;

	u32 file_bytes_left = f_size(&fp_src);

	// Open file for writing.
	f_chdrive(dst);
	f_open(&fp_dst, path, FA_CREATE_ALWAYS | FA_WRITE);
	f_lseek(&fp_dst, f_size(&fp_src));
	f_lseek(&fp_dst, 0);

	while (file_bytes_left)
	{
		u32 chunk_size = MIN(file_bytes_left, SZ_4M); // 4MB chunks.
		file_bytes_left -= chunk_size;

		// Copy file to buffer.
		f_read(&fp_src, (void *)SDXC_BUF_ALIGNED, chunk_size, NULL);

		// Write file to disk.
		f_write(&fp_dst, (void *)SDXC_BUF_ALIGNED, chunk_size, NULL);
	}

	f_close(&fp_dst);
	f_chdrive(src);
	f_close(&fp_src);

	return FR_OK;
}

static int _stat_and_copy_files(const char *src, const char *dst, char *path, u32 *total_files, u32 *total_size, lv_obj_t **labels)
{
	FRESULT res;
	FIL fp_src;
	FIL fp_dst;
	DIR dir;
	u32 dirLength = 0;
	static FILINFO fno;

	f_chdrive(src);

	// Open directory.
	res = f_opendir(&dir, path);
	if (res != FR_OK){
		if(res == FR_NO_PATH){
			return FR_OK;
		}
		return res;
	}

	if (labels)
		lv_label_set_text(labels[0], path);

	dirLength = strlen(path);

	// Hard limit path to 1024 characters. Do not result to error.
	if (dirLength > 1024)
		goto out;

	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.

			// Check for overflow.
			if ((file_size + *total_size) < *total_size)
			{
				// Set size to > 1GB, skip next folders and return.
				*total_size = SZ_2G;
				res = -1;
				break;
			}

			*total_size += file_size;
			*total_files += 1;

			if (dst)
			{
				u32 file_bytes_left = 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_bytes_left)
				{
					u32 chunk_size = MIN(file_bytes_left, SZ_4M); // 4MB chunks.
					file_bytes_left -= 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);

				}

				// Finalize copied file.
				f_close(&fp_dst);
				f_chdrive(dst);
				f_chmod(path, fno.fattrib, 0xFF);

				f_chdrive(src);
				f_close(&fp_src);
			}

			// If total is > 1GB exit.
			if (*total_size > (RAM_DISK_SZ - SZ_16M)) // 0x2400000.
			{
				// Skip next folders and return.
				res = -1;
				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);
				f_chmod(path, fno.fattrib, 0xFF);
			}

			// Enter the directory.
			res = _stat_and_copy_files(src, dst, path, total_files, total_size, labels);
			if (res != FR_OK)
				break;

			if (labels)
			{
				// Clear folder path.
				path[dirLength] = 0;
				lv_label_set_text(labels[0], path);
			}
		}
	}

out:
	f_closedir(&dir);

	return res;
}

static void _create_gpt_partition(gpt_t *gpt, u32 *gpt_idx, u32 *curr_part_lba, u32 size_lba, bool align, const char *name, const u8 type_guid[16], const u8 guid[16], bool clear){
	memcpy(gpt->entries[*gpt_idx].type_guid, type_guid, 16);

	if(!guid){
		u8 random_number[16];
		se_gen_prng128(random_number);
		memcpy(gpt->entries[*gpt_idx].part_guid, random_number, 16);
	}else{
		memcpy(gpt->entries[*gpt_idx].part_guid, guid, 16);
	}

	if(align){
		(*curr_part_lba) = ALIGN(*curr_part_lba, AU_ALIGN_SECTORS);
	}

	gpt->entries[*gpt_idx].lba_start = *curr_part_lba;
	gpt->entries[*gpt_idx].lba_end = *curr_part_lba + size_lba - 1;

	_ctowcs(name, gpt->entries[*gpt_idx].name, 36);

	if(clear){
		sdmmc_storage_t *storage = part_info.drive == DRIVE_SD ? &sd_storage : &emmc_storage;
		sdmmc_storage_write(storage, *curr_part_lba, 0x800, (void *)SDMMC_UPPER_BUFFER);
	}

	(*curr_part_lba) += size_lba;

	(*gpt_idx)++;
}

static void _make_part_name(char *buf, const char* base_name, u32 idx){
	if(idx > 1){
		s_printf(buf, "%s%d", base_name, idx);
	}else{
		strcpy(buf, base_name);
	}
}

static s32 _get_gpt_part_by_name(gpt_t *gpt, const char* name, s32 prev){
	u16 wc_name[36];
	_ctowcs(name, wc_name, 36);
	for(s32 i = prev + 1; i < (s32)gpt->header.num_part_ents && i < 128; i++){
		if(!memcmp(wc_name, gpt->entries[i].name, strlen(name) * 2)){
			return i;
		}
	}
	return -1;
}

static void _prepare_and_flash_mbr_gpt()
{
	sdmmc_storage_t *storage = part_info.drive == DRIVE_SD ? &sd_storage : &emmc_storage;

	mbr_t mbr;
	u8 random_number[16];

	memset((void *)SDMMC_UPPER_BUFFER, 0, AU_ALIGN_BYTES);

	s32 l4t_idx    = -1;
	s32 emu_idx[2] = {-1, -1};
	s32 hos_idx    = -1;
	s32 emu_sd_idx[2] = {-1, -1};

	// Create new GPT
	gpt_t *gpt = zalloc(sizeof(*gpt));
	memcpy(&gpt->header.signature, "EFI PART", 8);
	gpt->header.revision = 0x10000;
	gpt->header.size = 92;
	gpt->header.my_lba = 1;
	gpt->header.alt_lba = storage->sec_cnt - 1;
	gpt->header.first_use_lba = (sizeof(mbr_t) + sizeof(gpt_t)) >> 9;
	gpt->header.last_use_lba = storage->sec_cnt - 0x800 - 1;
	gpt->header.part_ent_lba = 2;
	gpt->header.part_ent_size = 128;
	se_gen_prng128(random_number);
	memcpy(gpt->header.disk_guid, random_number, 10);
	memcpy(gpt->header.disk_guid + 10, "NYXGPT", 6);

	u32 gpt_idx = 0;
	u32 gpt_next_lba = AU_ALIGN_SECTORS;
	if(part_info.hos_os_size){
		gpt_t *old_gpt = zalloc(sizeof(*old_gpt));
		sdmmc_storage_read(storage, 1, sizeof(*old_gpt) / 0x200, old_gpt);

		// Used by HOS for backup of first 3 partition entries
		memcpy(gpt->header.res2, old_gpt->header.res2, sizeof(gpt->header.res2));

		// user original disk guid
		memcpy(gpt->header.disk_guid, old_gpt->header.disk_guid, sizeof(gpt->header.disk_guid));

		// Copy HOS OS partition entries
		for(u32 i = 0; i < 11; i++){
			gpt->entries[i] = old_gpt->entries[i];
		}

		// Update HOS USER size
		gpt->entries[10].lba_end = gpt->entries[10].lba_start + ((part_info.hos_os_size - part_info.hos_sys_size_mb - part_info.hos_os_align) << 11) - 1;
		gpt_next_lba = ALIGN(gpt->entries[10].lba_end + 1, AU_ALIGN_SECTORS);

		gpt_idx = 11;

		free(old_gpt);
	}

	static const u8 basic_part_guid[]        = { 0xA2, 0xA0, 0xD0, 0xEB,  0xE5, 0xB9,  0x33, 0x44,  0x87, 0xC0,  0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 };
	static const u8 linux_part_guid[]        = { 0xAF, 0x3D, 0xC6, 0x0F,  0x83, 0x84,  0x72, 0x47,  0x8E, 0x79,  0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4 };
	static const u8 emu_part_guid[]          = { 0x00, 0x7E, 0xCA, 0x11,  0x00, 0x00,  0x00, 0x00,  0x00, 0x00,  'e', 'm', 'u', 'M', 'M', 'C' };
	// static const u8 emu_sd_part_guid[]       = { 0x00, 0x7E, 0xCA, 0x11,  0x00, 0x00,  0x00, 0x00,  0x00, 0x00,  0x00, 'e', 'm', 'u', 'S', 'D'};
	static const u8 emu_sd_mbr_part_guid[]   = { 0x00, 0x7E, 0xCA, 0x11,  0x00, 0x00,  0x00, 0x00, 'e', 'm', 'u', 'S', 'D', 'M', 'B', 'R'};

	if(part_info.l4t_size){
		l4t_idx = gpt_idx;
		_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, part_info.l4t_size << 11, true, "l4t", linux_part_guid, NULL, true);
	}

	if(part_info.and_size){
		u32 and_system_size;
		if(part_info.and_dynamic){
			and_system_size = ANDROID_SYSTEM_SIZE_MB;
		}else{
			and_system_size = ANDROID_SYSTEM_SIZE_LEGACY_MB;
		}

		u32 user_size;
		if(part_info.and_double){
			user_size = (part_info.and_size / 2) - and_system_size;
		}else{
			user_size = part_info.and_size - and_system_size;
		}

		for(u32 i = 1; i <= (part_info.and_double ? 2 : 1); i++){
			char part_name[36];
			if(part_info.and_dynamic){
				// Android Kernel, 64MB
				_make_part_name(part_name, "boot", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0x20000, false, part_name, linux_part_guid, NULL, true);

				// Android Recovery, 64MB
				_make_part_name(part_name, "recovery", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0x20000, false, part_name, linux_part_guid, NULL, true);

				// Android DTB, 1MB
				_make_part_name(part_name, "dtb", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0x800, false, part_name, linux_part_guid, NULL, true);

				// Android Misc, 3MB
				_make_part_name(part_name, "misc", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0x1800, false, part_name, linux_part_guid, NULL, true);

				// Android Cache, 60MB
				_make_part_name(part_name, "cache", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0x1E000, false, part_name, linux_part_guid, NULL, true);

				// Android Dynamic, 5922MB
				_make_part_name(part_name, "super", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0xB91000, true, part_name, linux_part_guid, NULL, true);

				// Android Userdata
				_make_part_name(part_name, "userdata", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, user_size << 11, true, part_name, linux_part_guid, NULL, true);
			}else{
				// Android Vendor, 1GB
				_make_part_name(part_name, "vendor", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0x200000, false, part_name, linux_part_guid, NULL, true);

				// Android System, 3GB
				_make_part_name(part_name, "APP", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0x600000, false, part_name, linux_part_guid, NULL, true);

				// Android Kernel, 32MB
				_make_part_name(part_name, "LNX", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0x10000, false, part_name, linux_part_guid, NULL, true);

				// Android Recovery, 64MB
				_make_part_name(part_name, "SOS", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0x20000, false, part_name, linux_part_guid, NULL, true);

				// Android DTB, 1MB
				_make_part_name(part_name, "DTB", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0x800, false, part_name, linux_part_guid, NULL, true);

				// Android encrypted metadata partition, 16MB to ensure alignment of following partitions.
				// If more, tiny partitions must be added, split off from MDA 
				_make_part_name(part_name, "MDA", i);
				// clear out entire partition
				// sdmmc_storage_write(storage, gpt_next_lba, 0x8000, (void*)SDMMC_UPPER_BUFFER);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0x8000, false, part_name, linux_part_guid, NULL, true);

				// Android Cache, 700MB
				_make_part_name(part_name, "CAC", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0x15E000, false, part_name, linux_part_guid, NULL, true);

				// Android Misc, 3MB
				_make_part_name(part_name, "MSC", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 0x1800, false, part_name, linux_part_guid, NULL, true);

				// Android Userdata
				_make_part_name(part_name, "UDA", i);
				_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, user_size << 11, true, part_name, linux_part_guid, NULL, true);
			}
		}
	}

	if(part_info.emu_size){
		u32 emu_size;
		if(part_info.emu_double){
			emu_size = part_info.emu_size / 2;
		}else{
			emu_size = part_info.emu_size;
		}

		char part_name[36];
		for(u32 i = 1; i <= (part_info.emu_double ? 2 : 1); i++){
			emu_idx[i - 1] = gpt_idx;
			_make_part_name(part_name, "emummc", i);
			_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, emu_size << 11, true, part_name, emu_part_guid, NULL, true);
		}
	}

	if(part_info.hos_size){
		hos_idx = gpt_idx;
		_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, part_info.hos_size << 11, true, "hos_data", basic_part_guid, NULL, true);
		// Clear non-standard Windows MBR attributes. bit4: Read only, bit5: Shadow copy, bit6: Hidden, bit7: No drive letter.
		gpt->entries[gpt_idx - 1].part_guid[7] = 0;
		if (part_info.and_size || part_info.l4t_size) {
			// Set legacy bootable attribute, necessary for uboot to consider this partition
			gpt->entries[gpt_idx - 1].attrs = 4;
		}
	}

	if(part_info.emu_sd_size){
		u32 emu_sd_size;
		if(part_info.emu_sd_double){
			emu_sd_size = part_info.emu_sd_size / 2;
		}else{
			emu_sd_size = part_info.emu_sd_size;
		}

		char part_name[36];
		for(u32 i = 1; i <= (part_info.emu_sd_double ? 2 : 1); i++){
			// split up emu sd partition into two
			// one partition that only covers the mbr
			// one partition that corresponds the actual fat32 partition
			// this way it can be accessed normally
			// mbr partition must be >=1mb, otherwise hos panics
			_make_part_name(part_name, "emusd_mbr", i);
			_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, 1 << 11, true, part_name, emu_sd_mbr_part_guid, NULL, true);

			emu_sd_idx[i - 1] = gpt_idx;

			_make_part_name(part_name, "emusd", i);
			_create_gpt_partition(gpt, &gpt_idx, &gpt_next_lba, (emu_sd_size - 1) << 11, false, part_name, basic_part_guid, NULL, true);
			// clear windows hidden attributes
			gpt->entries[gpt_idx - 1].part_guid[7] = 0;
			if ((part_info.and_size || part_info.l4t_size) && !part_info.hos_size && i == 1) {
				// Set legacy bootable attribute, necessary for uboot to consider this partition
				// Only set if no dedicated hos_data FAT32 partition so that this partition can be used for booting
				gpt->entries[gpt_idx - 1].attrs = 4;
			}
		}
	}

	gpt->header.num_part_ents = gpt_idx;
	gpt->header.part_ents_crc32 = crc32_calc(0, (const u8 *)gpt->entries, sizeof(gpt_entry_t) * gpt->header.num_part_ents);
	gpt->header.crc32 = 0; // Set to 0 for calculation.
	gpt->header.crc32 = crc32_calc(0, (const u8 *)&gpt->header, gpt->header.size);

	gpt_header_t gpt_backup_header = {0};
	memcpy(&gpt_backup_header, &gpt->header, sizeof(gpt_backup_header));
	gpt_backup_header.my_lba = storage->sec_cnt - 1;
	gpt_backup_header.alt_lba = 1;
	gpt_backup_header.part_ent_lba = storage->sec_cnt - 33;
	gpt_backup_header.crc32 = 0; // Set to 0 for calculation.
	gpt_backup_header.crc32 = crc32_calc(0, (const u8 *)&gpt_backup_header, gpt_backup_header.size);

	bool need_gpt = gpt_idx > 4 || part_info.drive == DRIVE_EMMC;

	// Create new MBR
	if(part_info.drive == DRIVE_SD){
		// on sd, add hos_data, l4t and emummc partitions to mbr, if possible
		memset(&mbr, 0, sizeof(mbr));
		se_gen_prng128(random_number);
		memcpy(&mbr.signature, random_number, 4);

		mbr.boot_signature = 0xaa55;

		u32 mbr_idx = 0;

		if(hos_idx != -1){
			mbr.partitions[mbr_idx].type      = 0x0c; // fat32
			mbr.partitions[mbr_idx].start_sct = gpt->entries[hos_idx].lba_start;
			mbr.partitions[mbr_idx].size_sct  = gpt->entries[hos_idx].lba_end - gpt->entries[hos_idx].lba_start + 1;
			mbr_idx++;
		}

		if(!need_gpt && l4t_idx != -1){
			mbr.partitions[mbr_idx].type      = 0x83; // linux partition
			mbr.partitions[mbr_idx].start_sct = gpt->entries[l4t_idx].lba_start;
			mbr.partitions[mbr_idx].size_sct  = gpt->entries[l4t_idx].lba_end - gpt->entries[l4t_idx].lba_start + 1;
			mbr_idx++;
		}

		for(u32 i = 0; i < 2; i++){
			if(emu_idx[i] != -1){
				mbr.partitions[mbr_idx].type      = 0xe0; // emummc partition
				mbr.partitions[mbr_idx].start_sct = gpt->entries[emu_idx[i]].lba_start;
				mbr.partitions[mbr_idx].size_sct  = gpt->entries[emu_idx[i]].lba_end - gpt->entries[emu_idx[i]].lba_start + 1;
				mbr_idx++;
			}
		}

		if(need_gpt){
			mbr.partitions[mbr_idx].type = 0xee;
			mbr.partitions[mbr_idx].start_sct = 1;
			mbr.partitions[mbr_idx].size_sct = 0xffffffff;
		}

	}else{
		// For eMMC, only gpt protective partition spanning entire disk
		// and emusd/fat32 partition
		memset(&mbr, 0, sizeof(mbr));

		if(hos_idx != -1 || emu_sd_idx[0] != -1){
			se_gen_prng128(random_number);
			memcpy(&mbr.signature, random_number, 4);
		}

		u32 mbr_idx = 0;

		if(hos_idx != -1){
			mbr.partitions[mbr_idx].start_sct = gpt->entries[hos_idx].lba_start;
			mbr.partitions[mbr_idx].size_sct = gpt->entries[hos_idx].lba_end - gpt->entries[hos_idx].lba_start + 1;
			mbr.partitions[mbr_idx].type = 0x0c;
			mbr_idx++;
		}

		for(u32 i = 0; i < 2; i++){
			if(emu_sd_idx[i] != -1){
				mbr.partitions[mbr_idx].start_sct = gpt->entries[emu_sd_idx[i]].lba_start;
				mbr.partitions[mbr_idx].size_sct = gpt->entries[emu_sd_idx[i]].lba_end - gpt->entries[emu_sd_idx[i]].lba_start + 1;
				mbr.partitions[mbr_idx].type = 0x0c;
				mbr_idx++;
			}
		}

		mbr.partitions[mbr_idx].type                 = 0xee;
		mbr.partitions[mbr_idx].start_sct            = 0x1;
		mbr.partitions[mbr_idx].start_sct_chs.sector = 0x02;
		mbr.boot_signature = 0xaa55;
		mbr.partitions[mbr_idx].size_sct             = 0xffffffff;
		mbr.partitions[mbr_idx].end_sct_chs.sector   = 0xff;
		mbr.partitions[mbr_idx].end_sct_chs.cylinder = 0xff;
		mbr.partitions[mbr_idx].end_sct_chs.head     = 0xff;
	}

	if(!part_info.hos_os_size){
		// only clear first 16mb if not keeping hos
		sdmmc_storage_write(storage, 0, 0x800, (void*)SDMMC_UPPER_BUFFER);
	}

	// write mbr
	sdmmc_storage_write(storage, 0, 1, &mbr);

	if(need_gpt){
		// write primary gpt
		sdmmc_storage_write(storage, 1, sizeof(*gpt) / 0x200, gpt);
		// write backup gpt entries
		sdmmc_storage_write(storage, gpt_backup_header.part_ent_lba, (sizeof(gpt_entry_t) * 128) / 0x200, gpt->entries);
		// write backup gpt header
		sdmmc_storage_write(storage, gpt_backup_header.my_lba, sizeof(gpt_backup_header) / 0x200, &gpt_backup_header);
	}

	free(gpt);
}

static lv_res_t _action_part_manager_ums_sd(lv_obj_t *btn)
{
	action_ums_sd(btn);

	// Close and reopen partition manager.
	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, DRIVE_SD);

	return LV_RES_INV;
}

static lv_res_t _action_part_manager_ums_emmc(lv_obj_t *btn){
	action_ums_emmc_gpp(btn);

	// Close and reopen partition manager.
	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, DRIVE_EMMC);

	return LV_RES_INV;
}

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

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

		boot_storage_unmount();
	}

	return LV_RES_INV;
}

static lv_res_t _action_flash_linux_data(lv_obj_t * btns, const char * txt)
{
	boot_storage_mount();
	if(part_info.drive == DRIVE_SD){
		sd_mount();
	}else{
		emmc_mount();
	}

	int btn_idx = lv_btnm_get_pressed(btns);

	// Delete parent mbox.
	mbox_action(btns, txt);

	bool succeeded = false;

	if (btn_idx)
		return LV_RES_INV;

	// Flash Linux.
	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[] = { "\251", "\222OK", "\251", "" };
	static const char *mbox_btn_map2[] = { "\223Delete Installation Files", "\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 / 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);

	int res = 0;
	char *path = malloc(1024);
	char *txt_buf = malloc(SZ_4K);
	strcpy(path, "switchroot/install/l4t.00");
	u32 path_len = strlen(path) - 2;

	FIL fp;

	res = f_open(&fp, path, FA_READ);
	if (res)
	{
		gfx_printf("fopen res: %d\n", 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, SZ_4M, 0);

	sdmmc_storage_t *storage = part_info.drive == DRIVE_SD ? &sd_storage : &emmc_storage;

	// Start flashing L4T.
	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, SZ_4M, 0);
		}

		retryCount = 0;
		num = MIN(total_size_sct, 8192);

		// Read next data block from SD.
		res = f_read_fast(&fp, buf, num << 9);
		manual_system_maintenance(false);

		if (res)
		{
			char msg[0x30];
			s_printf(msg, "#FFDD00 Error:# Reading from %s!", part_info.drive == DRIVE_SD ? "SD" : "eMMC");
			lv_label_set_text(lbl_status, msg);
			manual_system_maintenance(true);

			f_close(&fp);
			free(clmt);
			goto exit;
		}

		// Write data block to L4T partition.
		res = !sdmmc_storage_write(storage, lba_curr + l4t_flash_ctxt.offset_sct, num, buf);

		manual_system_maintenance(false);

		// If failed, retry 3 more times.
		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(storage, lba_curr + l4t_flash_ctxt.offset_sct, num, buf);
			manual_system_maintenance(false);
		}

		// Update completion percentage.
		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, " #DDDDDD "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 * 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);

	boot_storage_unmount();
	if(part_info.drive == DRIVE_SD){
		sd_unmount();
	}else{
		emmc_unmount();
	}

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

	return LV_RES_INV;
}

static u32 _get_available_l4t_partition()
{
	sdmmc_storage_t *storage = part_info.drive == DRIVE_SD ? &sd_storage : &emmc_storage;

	mbr_t mbr = { 0 };
	gpt_t *gpt = zalloc(sizeof(gpt_t));

	memset(&l4t_flash_ctxt, 0, sizeof(l4t_flasher_ctxt_t));

	// Read MBR.
	sdmmc_storage_read(storage, 0, 1, &mbr);

	// Read main GPT.
	sdmmc_storage_read(storage, 1, sizeof(gpt_t) >> 9, gpt);

	// Search for a suitable partition.
	u32 size_sct = 0;
	if (!memcmp(&gpt->header.signature, "EFI PART", 8) || gpt->header.num_part_ents > 128)
	{
		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;
			}
		}
	}

	free(gpt);

	return size_sct;
}

static int _get_available_android_partition()
{
	gpt_t *gpt = zalloc(sizeof(gpt_t));

	// Read main GPT.
	sdmmc_storage_t *storage = part_info.drive == DRIVE_SD ? &sd_storage : &emmc_storage;
	sdmmc_storage_read(storage, 1, sizeof(gpt_t) >> 9, gpt);

	// Check if GPT.
	if (memcmp(&gpt->header.signature, "EFI PART", 8) || gpt->header.num_part_ents > 128)
		goto out;

	// Find kernel partition.
	for (u32 i = 0; i < gpt->header.num_part_ents; i++)
	{
		if (gpt->entries[i].lba_start)
		{
			int found  = !memcmp(gpt->entries[i].name, (char[]) { 'b', 0, 'o', 0, 'o', 0, 't', 0 }, 8) ? 2 : 0;
				found |= !memcmp(gpt->entries[i].name, (char[]) { 'L', 0, 'N', 0, 'X', 0 },                     6) ? 1 : 0;

			if (found)
			{
				free(gpt);

				return found;
			}
		}

		if (i > 126)
			break;
	}

out:
	free(gpt);

	return false;
}

static lv_res_t _action_check_flash_linux(lv_obj_t *btn)
{
	FILINFO fno;
	char path[128];

	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[] = { "\251", "\222OK", "\251", "" };
	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);

	boot_storage_mount();
	if(part_info.drive == DRIVE_SD){
		sd_mount();
	}else{
		emmc_mount();
	}

	// Check if L4T image exists.
	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;
	}

	// Find an applicable partition for L4T.
	u32 size_sct = _get_available_l4t_partition();
	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;

	// Validate L4T images and consolidate their info.
	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))
			break;

		// Check if current part is unaligned.
		if ((u64)fno.fsize % SZ_4M)
		{
			// Get next part filename.
			idx++;
			if (idx < 10)
			{
				path[23] = '0';
				itoa(idx, &path[23 + 1], 10);
			}
			else
				itoa(idx, &path[23], 10);

			// If it exists, unaligned size for current part is not permitted.
			if (!f_stat(path, NULL)) // NULL: Don't override current part fs info.
			{
				lv_label_set_text(lbl_status, "#FFDD00 Error:# The image is not aligned to 4 MiB!");
				goto error;
			}

			// Last part. Align size to LBA (SD_BLOCKSIZE).
			fno.fsize = ALIGN((u64)fno.fsize, SD_BLOCKSIZE);
			idx--;
		}
		l4t_flash_ctxt.image_size_sct += (u64)fno.fsize >> 9;

		idx++;
	}

	// Check if image size is bigger than the partition available.
	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(SZ_4K);
	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:
	boot_storage_unmount();
	if(part_info.drive == DRIVE_SD){
		sd_unmount();
	}else{
		emmc_unmount();
	}

	lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);

	boot_storage_unmount();

	return LV_RES_OK;
}

static lv_res_t _action_reboot_recovery(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)
	{
		// Set custom reboot type to Android Recovery.
		PMC(APBDEV_PMC_SCRATCH0) |= PMC_SCRATCH0_MODE_RECOVERY;

		// Enable hekate boot configuration.
		b_cfg->boot_cfg = BOOT_CFG_FROM_ID | BOOT_CFG_AUTOBOOT_EN;

		// Set id to Android.
		strcpy((char *)b_cfg->id, "SWANDR");
		if(android_flash_ctxt.slot > 1 && android_flash_ctxt.slot < 10){
			s_printf((char*)(b_cfg->id + strlen((char*)b_cfg->id)), "%d", android_flash_ctxt.slot);
		}

		void (*main_ptr)() = (void *)nyx_str->hekate;

		// Deinit hardware.
		sd_end();
		emmc_end();
		boot_storage_end();
		hw_deinit(false, 0);

		// Chainload to hekate main.
		(*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);
	bool boot_recovery = false;

	// Delete parent mbox.
	mbox_action(btns, txt);

	if (btn_idx)
		return LV_RES_INV;

	// Flash Android components.
	char path[128];
	gpt_t *gpt = zalloc(sizeof(gpt_t));
	char *txt_buf = malloc(SZ_4K);

	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[] = { "\251", "\222OK", "\251", "" };
	static const char *mbox_btn_map2[] = { "\222Continue", "\222No", "" };
	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);

	sdmmc_storage_t *storage = part_info.drive == DRIVE_SD ? &sd_storage : &emmc_storage;

	manual_system_maintenance(true);

	if(part_info.drive == DRIVE_SD){
		sd_mount();
	}else{
		emmc_mount();
	}
	boot_storage_mount();

	// Read main GPT.
	sdmmc_storage_read(storage, 1, sizeof(gpt_t) >> 9, gpt);

	// Validate GPT header.
	if (memcmp(&gpt->header.signature, "EFI PART", 8) || gpt->header.num_part_ents > 128)
	{
		lv_label_set_text(lbl_status, "#FFDD00 Error:# No Android GPT was found!");
		goto error;
	}

	u32 offset_sct = 0;
	u32 size_sct = 0;
	s32 gpt_idx = 0;
	char name[36];

	// Check if Kernel image should be flashed.
	strcpy(path, "switchroot/install/boot.img");
	if (f_stat(path, NULL))
	{
		s_printf(txt_buf, "#FF8000 Warning:# Kernel image not found!\n");
		goto boot_img_not_found;
	}


	// find kernel partition
	// look for dynamic boot partition
	_make_part_name(name, "boot", android_flash_ctxt.slot);
	gpt_idx = _get_gpt_part_by_name(gpt, name, -1);
	if(gpt_idx == -1){
		_make_part_name(name, "LNX", android_flash_ctxt.slot);
		gpt_idx = _get_gpt_part_by_name(gpt, name, -1);
	}
	if(gpt_idx != -1){
		offset_sct = gpt->entries[gpt_idx].lba_start;
		size_sct = (gpt->entries[gpt_idx].lba_end + 1) - gpt->entries[gpt_idx].lba_start;
	}

	// Flash Kernel.
	if (offset_sct && size_sct)
	{
		u32 file_size = 0;
		u8 *buf = boot_storage_file_read(path, &file_size);

		if (file_size % 0x200)
		{
			file_size = ALIGN(file_size, 0x200);
			u8 *buf_tmp = zalloc(file_size);
			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(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");

boot_img_not_found:
	lv_label_set_text(lbl_status, txt_buf);
	manual_system_maintenance(true);

	// Check if Recovery should be flashed.
	strcpy(path, "switchroot/install/recovery.img");
	if (f_stat(path, NULL))
	{
		// Not found, try twrp.img instead.
		strcpy(path, "switchroot/install/twrp.img");
		if (f_stat(path, NULL))
		{
			strcat(txt_buf, "#FF8000 Warning:# Recovery image not found!\n");
			goto recovery_not_found;
		}
	}

	offset_sct = 0;
	size_sct = 0;

	// find recovery parititon
	_make_part_name(name, "recovery", android_flash_ctxt.slot);
	gpt_idx = _get_gpt_part_by_name(gpt, name, -1);
	if(gpt_idx == -1){
		_make_part_name(name, "SOS", android_flash_ctxt.slot);
		gpt_idx = _get_gpt_part_by_name(gpt, name, -1);
	}
	if(gpt_idx != -1){
		offset_sct = gpt->entries[gpt_idx].lba_start;
		size_sct = (gpt->entries[gpt_idx].lba_end + 1) - gpt->entries[gpt_idx].lba_start;
	}

	// Flash Recovery.
	if (offset_sct && size_sct)
	{
		u32 file_size = 0;
		u8 *buf = boot_storage_file_read(path, &file_size);

		if (file_size % 0x200)
		{
			file_size = ALIGN(file_size, 0x200);
			u8 *buf_tmp = zalloc(file_size);
			memcpy(buf_tmp, buf, file_size);
			free(buf);
			buf = buf_tmp;
		}

		if ((file_size >> 9) > size_sct)
			strcat(txt_buf, "#FF8000 Warning:# Recovery image too big!\n");
		else
		{
			sdmmc_storage_write(storage, offset_sct, file_size >> 9, buf);
			strcat(txt_buf, "#C7EA46 Success:# Recovery image flashed!\n");
			f_unlink(path);
		}

		free(buf);
	}
	else
		strcat(txt_buf, "#FF8000 Warning:# Recovery partition not found!\n");

recovery_not_found:
	lv_label_set_text(lbl_status, txt_buf);
	manual_system_maintenance(true);

	// Check if Device Tree should be flashed.
	strcpy(path, "switchroot/install/nx-plat.dtimg");
	if (f_stat(path, NULL))
	{
		strcpy(path, "switchroot/install/tegra210-icosa.dtb");
		if (f_stat(path, NULL))
		{
			strcat(txt_buf, "#FF8000 Warning:# DTB image not found!");
			goto dtb_not_found;
		}
	}

	offset_sct = 0;
	size_sct = 0;

	// find dtb partition
	_make_part_name(name, "dtb", android_flash_ctxt.slot);
	gpt_idx = _get_gpt_part_by_name(gpt, name, -1);
	if(gpt_idx == -1){
		_make_part_name(name, "DTB", android_flash_ctxt.slot);
		gpt_idx = _get_gpt_part_by_name(gpt, name, -1);
	}
	if(gpt_idx != -1){
		offset_sct = gpt->entries[gpt_idx].lba_start;
		size_sct = (gpt->entries[gpt_idx].lba_end + 1) - gpt->entries[gpt_idx].lba_start;
	}

	// Flash Device Tree.
	if (offset_sct && size_sct)
	{
		u32 file_size = 0;
		u8 *buf = boot_storage_file_read(path, &file_size);

		if (file_size % 0x200)
		{
			file_size = ALIGN(file_size, 0x200);
			u8 *buf_tmp = zalloc(file_size);
			memcpy(buf_tmp, buf, file_size);
			free(buf);
			buf = buf_tmp;
		}

		if ((file_size >> 9) > size_sct)
			strcat(txt_buf, "#FF8000 Warning:# DTB image too big!");
		else
		{
			sdmmc_storage_write(storage, offset_sct, file_size >> 9, buf);
			strcat(txt_buf, "#C7EA46 Success:# DTB image flashed!");
			f_unlink(path);
		}

		free(buf);
	}
	else
		strcat(txt_buf, "#FF8000 Warning:# DTB partition not found!");

dtb_not_found:
	lv_label_set_text(lbl_status, txt_buf);

	offset_sct = 0;

	// find recovery parititon
	_make_part_name(name, "recovery", android_flash_ctxt.slot);
	gpt_idx = _get_gpt_part_by_name(gpt, name, -1);
	if(gpt_idx == -1){
		_make_part_name(name, "SOS", android_flash_ctxt.slot);
		gpt_idx = _get_gpt_part_by_name(gpt, name, -1);
	}
	if(gpt_idx != -1){
		offset_sct = gpt->entries[gpt_idx].lba_start;
	}

	// unconditionally check for valid recovery
	if(offset_sct){
		u8 *buf = malloc(SD_BLOCKSIZE);
		sdmmc_storage_read(storage, offset_sct, 1, buf);
		if(!memcmp(buf, "ANDROID", 7)){
			boot_recovery = true;
		}
		free(buf);
	}

error:
	if (boot_recovery)
	{
		// If a Recovery partition was found, ask user if rebooting into it is wanted.
		strcat(txt_buf,"\n\nDo you want to reboot into Recovery\nto finish Android installation?");
		lv_label_set_text(lbl_status, txt_buf);
		lv_mbox_add_btns(mbox, mbox_btn_map2, _action_reboot_recovery);
	}
	else
		lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action);

	lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);

	free(txt_buf);
	free(gpt);

	if(part_info.drive == DRIVE_SD){
		sd_unmount();
	}else{
		emmc_unmount();
	}
	boot_storage_unmount();

	return LV_RES_INV;
}

static lv_res_t _action_flash_android(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[] = { "\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 Recovery# if found.\n"
		"These will be deleted after a successful flash.\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 u32 _get_num_slots_android(){
	sdmmc_storage_t *storage = part_info.drive == DRIVE_SD ? &sd_storage : &emmc_storage;

	u32 res = 0;

	mbr_t mbr;
	gpt_t *gpt = NULL;
	sdmmc_storage_read(storage, 0, 1, &mbr);

	if(!_has_gpt(&mbr)){
		goto out;
	}

	gpt = zalloc(sizeof(*gpt));

	sdmmc_storage_read(storage, 1, sizeof(*gpt) / 0x200, gpt);

	if(memcmp(&gpt->header.signature, "EFI PART", 8)){
		goto out;
	}

	s32 gpt_idx = -1;
	// check for dynamic
	gpt_idx = _get_gpt_part_by_name(gpt, "boot", gpt_idx);
	if(gpt_idx != -1){
		res++;
		gpt_idx = _get_gpt_part_by_name(gpt, "boot", gpt_idx);
		if(gpt_idx != -1){
			res++;
		}
		goto out;
	}

	gpt_idx = -1;
	// check for legacy
	gpt_idx = _get_gpt_part_by_name(gpt, "LNX", gpt_idx);
	if(gpt_idx != -1){
		res++;
		gpt_idx = _get_gpt_part_by_name(gpt, "LNX", gpt_idx);
		if(gpt_idx != -1){
			res++;
		}
		goto out;
	}

	out:
	free(gpt);
	return res;
}

static lv_res_t _action_flash_android_slot_select1(lv_obj_t *btns, const char *txt){
	int btn_idx = lv_btnm_get_pressed(btns);

	switch(btn_idx){
	case 0:
		android_flash_ctxt.slot = 1;
		break;
	case 1:
		android_flash_ctxt.slot = 2;
		break;
	}

	mbox_action(btns, txt);

	return _action_flash_android(NULL);
}

static lv_res_t _action_flash_android_slot_select(lv_obj_t *btn){
	u32 n_slots = _get_num_slots_android();

	if(n_slots == 1){
		android_flash_ctxt.slot = 1;
		return _action_flash_android(NULL);
	}

	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[] = { "\222Slot 1", "\222Slot 2", "" };
	static const char *mbox_btn_map2[] = { "\222OK", ""};

	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 Android Flasher#");

	lv_obj_t *lbl_status = lv_label_create(mbox, NULL);
	lv_label_set_recolor(lbl_status, true);


	if(n_slots == 0){
		lv_label_set_text(lbl_status,
			"No Android partitions found!\n");
		lv_mbox_add_btns(mbox, mbox_btn_map2, mbox_action);
	}else{
		lv_label_set_text(lbl_status,
		                  "Found multible sets of Android partitions.\n"
		                  "Please select the slot to use\n");
		lv_mbox_add_btns(mbox, mbox_btn_map, _action_flash_android_slot_select1);
	}

	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:
		if(part_info.drive == DRIVE_SD){
			action_ums_sd(btns);
		}else{
			action_ums_emmc_gpp(btns);
		}
		lv_obj_del(ums_mbox);
		break;
	case 1:
		_action_check_flash_linux(btns);
		break;
	case 2:
		_action_flash_android_slot_select(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:
		if(part_info.drive == DRIVE_SD){
			action_ums_sd(btns);
		}else{
			action_ums_emmc_gpp(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:
		if(part_info.drive == DRIVE_SD){
			action_ums_sd(btns);
		}else{
			action_ums_emmc_gpp(btns);
		}
		lv_obj_del(ums_mbox);
		break;
	case 1:
		mbox_action(btns, txt);
		_action_flash_android_slot_select(NULL);
		return LV_RES_INV;
	case 2:
		mbox_action(btns, txt);
		return LV_RES_INV;
	}

	return LV_RES_OK;
}

static int _backup_and_restore_files(bool backup, const char *drive, lv_obj_t **labels)
{
	const char *src_drv = backup ? drive  : "ram:";
	const char *dst_drv = backup ? "ram:" : drive;

	int res = 0;
	u32 total_size = 0;
	u32 total_files = 0;
	char *path = malloc(0x1000);
	path[0] = 0; // Set default as root folder.

	// Check if Mariko Warmboot Storage exists in source drive.
	f_chdrive(src_drv);
	bool backup_mws = !part_info.backup_possible && !f_stat("warmboot_mariko", NULL);
	bool backup_pld = !part_info.backup_possible && !f_stat("payload.bin", NULL);

	if (!part_info.backup_possible)
	{
		// Change path to hekate/Nyx.
		strcpy(path, "bootloader");

		// Create hekate/Nyx/MWS folders in destination drive.
		f_chdrive(dst_drv);
		f_mkdir("bootloader");
		if (backup_mws)
			f_mkdir("warmboot_mariko");
	}

	// Copy all or hekate/Nyx files.
	DBG_PRINT("start stat");
	gfx_printf("src %s\ndst %s\npath %s\n", src_drv, dst_drv, path);
	res = _stat_and_copy_files(src_drv, dst_drv, path, &total_files, &total_size, labels);

	gfx_printf("bkup res %d mws %d pld %d tot %d\n", res, (u32)backup_mws, (u32)backup_pld, total_files);

	// If incomplete backup mode, copy MWS and payload.bin also.
	if (!res)
	{
		if (backup_mws)
		{
			strcpy(path, "warmboot_mariko");
			res = _stat_and_copy_files(src_drv, dst_drv, path, &total_files, &total_size, labels);
		}

		if (!res && backup_pld)
		{
			strcpy(path, "payload.bin");
			res = _copy_file(src_drv, dst_drv, path);
		}
	}

	free(path);

	return res;
}

static DWORD _format_fat_partition(const char* path, u8 flags){
	// Set cluster size to 64KB and try to format.
	u8 *buf = malloc(SZ_4M);
	u32 cluster_size = 65536;
	u32 mkfs_error = f_mkfs(path, flags, cluster_size, buf, SZ_4M);

	// Retry formatting by halving cluster size, until one succeeds.
	while (mkfs_error != FR_OK && cluster_size > 4096)
	{
		cluster_size /= 2;
		mkfs_error = f_mkfs(path, flags, cluster_size, buf, SZ_4M);
	}

	free(buf);

	return mkfs_error;
}

static bool _derive_bis_keys(gpt_t *gpt)
{
	bool res = true;

	// Read and decrypt CAL0 for validation of working BIS keys.
	s32 gpt_idx = _get_gpt_part_by_name(gpt, "PRODINFO", -1);
	if(gpt_idx == -1){
		return false;
	}

	emmc_part_t cal0_part = {0};
	cal0_part.lba_start   = gpt->entries[gpt_idx].lba_start;
	cal0_part.lba_end     = gpt->entries[gpt_idx].lba_end;
	strcpy(cal0_part.name, "PRODINFO");

	// Generate BIS keys.
	hos_bis_keygen();

	u8 *cal0_buff = malloc(SZ_64K);

	nx_emmc_bis_init(&cal0_part, false, part_info.drive == DRIVE_SD ? &sd_storage : &emmc_storage, 0);
	nx_emmc_bis_read(0, 0x40, cal0_buff);
	nx_emmc_bis_end();

	nx_emmc_cal0_t *cal0 = (nx_emmc_cal0_t *)cal0_buff;

	// Check keys validity.
	if (memcmp(&cal0->magic, "CAL0", 4))
	{
		// Clear EKS keys.
		hos_eks_clear(HOS_KB_VERSION_MAX);
		res = false;
	}

	free(cal0_buff);

	return res;
}

static lv_res_t _create_mbox_start_partitioning(lv_obj_t *btn)
{
	char cwd[0x200];
	gpt_t *new_gpt = NULL;

	// TODO: remove the cwd stuff
	f_getcwd(cwd, sizeof(cwd));

	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[] =  { "\251", "\222OK", "\251", "" };
	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", "" };

	static const char *mbox_btn_map1_emmc[] = { "\222eMMC UMS", "\222Flash Linux", "\222Flash Android", "\221OK", "" };
	static const char *mbox_btn_map2_emmc[] = { "\222eMMC UMS", "\222Flash Linux", "\221OK", "" };
	static const char *mbox_btn_map3_emmc[] = { "\222eMMC UMS", "\222Flash Android", "\221OK", "" };

	sdmmc_storage_t *storage = part_info.drive == DRIVE_SD ? &sd_storage : &emmc_storage;

	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;

	// Use safety wait if backup is not possible.
	char *txt_buf = malloc(SZ_4K);
	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];

	// Create backup/restore paths labels.
	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);

	FATFS ram_fs;

	if(part_info.drive == DRIVE_SD){
		sd_mount();
	}else{
		emmc_mount();
	}

	// Read current MBR.
	sdmmc_storage_read(storage, 0, 1, &part_info.mbr_old);

	if(!part_info.skip_backup && part_info.hos_size){
		// can't backup / restore if new scheme has no fat partition. dialog will have warned about this
		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);

		// Initialize RAM disk.
		if (ram_disk_init(&ram_fs, RAM_DISK_SZ))
		{
			lv_label_set_text(lbl_status, "#FFDD00 Error:# Failed to initialize Ramdisk!");
			goto error;
		}

		lv_label_set_text(lbl_status, "#00DDFF Status:# Backing up files...");
		manual_system_maintenance(true);

		// Do full or hekate/Nyx backup.
		if (_backup_and_restore_files(true, part_info.drive == DRIVE_SD ? "sd:" : "emmc:", lbl_paths))
		{
			if (part_info.backup_possible)
				lv_label_set_text(lbl_status, "#FFDD00 Error:# Failed to back up files!");
			else
				lv_label_set_text(lbl_status, "#FFDD00 Error:# Failed to back up files!\nBootloader folder exceeds 1GB or corrupt!");
			manual_system_maintenance(true);

			goto error;
		}
	}

	if(part_info.drive == DRIVE_SD){
		sd_unmount();
	}else{
		emmc_unmount();
	}

	lv_label_set_text(lbl_status, "#00ddff Status:# Writing new 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();

	mbr_t new_mbr;

	sdmmc_storage_read(storage, 0, 1, &new_mbr);

	bool has_gpt = _has_gpt(&new_mbr);

	if(has_gpt){
		new_gpt = zalloc(sizeof(*new_gpt));
		sdmmc_storage_read(storage, 1, sizeof(*new_gpt) / 0x200, new_gpt);
	}


	// Restore backed up files if we made a fat32 partition
	if(part_info.hos_size){
		u32 hos_start = 0;
		u32 hos_size = 0;
		if(!has_gpt){
			// FAT32 partition is first in mbr if we don't have gpt
			hos_size = new_mbr.partitions[0].size_sct;
			hos_start = new_mbr.partitions[0].start_sct;
		}else{
			int hos_idx = _get_gpt_part_by_name(new_gpt, "hos_data", -1);
			if(hos_idx != -1){
				hos_size = new_gpt->entries[hos_idx].lba_end - new_gpt->entries[hos_idx].lba_start + 1;
				hos_start = new_gpt->entries[hos_idx].lba_start;
			}
		}

		lv_label_set_text(lbl_status, "#00ddff Status:# Formatting FAT32 partition...");
		manual_system_maintenance(true);

		sfd_init(storage, hos_start, hos_size);
		u32 mkfs_error = _format_fat_partition("sfd:", FM_FAT32 | FM_SFD);
	
		u8 *buf = malloc(0x200);

		if(mkfs_error != FR_OK){
			// Error
			s_printf((char*)buf, "#FFDD00 Error:# Failed to format disk (%d)!\n\n", mkfs_error);
			if(part_info.drive == DRIVE_SD && !part_info.skip_backup){
				// When SD and not skipping backup, ask to manually format sd and try to restore backed up files
				strcat((char*)buf, "\n\nRemove the SD card and check that it is OK.\nIf not, format it, reinsert it and\npress #FF8000 POWER# to continue!");
			}

			lv_label_set_text(lbl_status, (char *)buf);
			lv_label_set_text(lbl_paths[0], " ");
			manual_system_maintenance(true);

			if(part_info.drive == DRIVE_SD && !part_info.skip_backup){
				sd_end();
				while((!btn_wait()) & BTN_POWER){}
				sd_mount();

				lv_label_set_text(lbl_status, "#00DDFF Status:# Restoring files...");
				manual_system_maintenance(true);

				if(boot_storage_get_drive() != DRIVE_SD){
					FIL f;
					f_open(&f, part_info.drive == DRIVE_SD ? "sd:.no_boot_storage" : "emmc:.no_boot_storage", FA_WRITE | FA_CREATE_ALWAYS);
					f_close(&f);
				}

				// Try twice to restore files
				if (_backup_and_restore_files(false, "sd:", lbl_paths) == FR_OK || 
				    _backup_and_restore_files(false, "sd:", lbl_paths) == FR_OK){
					lv_label_set_text(lbl_status, "#00DDFF Status:# Restored files but the operation failed!");
				}else{
					lv_label_set_text(lbl_status, "#FFDD00 Error:# Failed to restore files!");
				}
				manual_system_maintenance(true);
			}
			f_mount(NULL, "ram:", 0);
			sfd_end();
			free(buf);
			goto error;
		}else{
			// No error
			// remount
			if(part_info.drive == DRIVE_SD){
				sd_mount();
			}else{
				emmc_mount();
			}

			int res = f_setlabel(part_info.drive == DRIVE_SD ? "sd:SWITCH SD" : "emmc:SWITCH EMMC");
			gfx_printf("setlabel %d\n", res);

			if(boot_storage_get_drive() != part_info.drive){
				// if we havent booted from the drive currently being formatted, create .no_boot_storage
				FIL f;
				f_open(&f, part_info.drive == DRIVE_SD ? "sd:.no_boot_storage" : "emmc:.no_boot_storage", FA_WRITE | FA_CREATE_ALWAYS);
				f_close(&f);
			}

			if(!part_info.skip_backup){
				lv_label_set_text(lbl_status, "#00DDFF Status:# Restoring files...");
				manual_system_maintenance(true);
				// Try twice to restroe files
				DBG_PRINT("start restore");	
				if (_backup_and_restore_files(false, part_info.drive == DRIVE_SD ? "sd:" : "emmc:", lbl_paths) != FR_OK &&
				    _backup_and_restore_files(false, part_info.drive == DRIVE_SD ? "sd:" : "emmc:", lbl_paths) != FR_OK)
				{
					// Restore failed
					lv_label_set_text(lbl_status, "#FFDD00 Error:# Failed to restore files!");
					manual_system_maintenance(true);
					f_mount(NULL, "ram:", 0);
					sfd_end();
					free(buf);
					goto error;
				}
			}

			f_mount(NULL, "ram:", 0);
			free(buf);
		}
		sfd_end();
	}

	// Format emuSD partition(s)
	if(has_gpt){
		s32 gpt_idx = _get_gpt_part_by_name(new_gpt, "emusd_mbr", -1);
		while(gpt_idx != -1){
			u32 emu_sd_mbr_start = new_gpt->entries[gpt_idx].lba_start;

			gpt_idx++;

			u32 emu_sd_start = new_gpt->entries[gpt_idx].lba_start;
			u32 emu_sd_size  = new_gpt->entries[gpt_idx].lba_end - emu_sd_start + 1;
			u16 *name = new_gpt->entries[gpt_idx].name;

			FIL f;
			u32 res;

			lv_label_set_text(lbl_status, "#00DDFF Status:# Formatting emuSD partition...");
			manual_system_maintenance(true);

			// write mbr

			mbr_t mbr = {0};
			u8 random_number[16];
			mbr.partitions[0].start_sct = emu_sd_start - emu_sd_mbr_start;
			mbr.partitions[0].size_sct  = emu_sd_size;
			mbr.boot_signature = 0xaa55;
			se_gen_prng128(random_number);
			memcpy(&mbr.signature, random_number, 4);

			sdmmc_storage_write(storage, emu_sd_mbr_start, 1, &mbr);

			sfd_init(storage, emu_sd_start, emu_sd_size);

			DBG_PRINT("emusd format start");
			DBG_PRINT_ARGS("%d %d", emu_sd_start, emu_sd_size);
			res = _format_fat_partition("sfd:", FM_FAT32 | FM_SFD);
			if(res == FR_OK){
				DBG_PRINT("format done success");
				FATFS fs;
				res = f_mount(&fs, "sfd:", 1);
				if(res == FR_OK){
					DBG_PRINT("mount success");
					// Create .no_boot_storage on emuSD partition so it is never used as boot storage
					// set label
					char label[0x30];
					strcpy(label, "sfd:");
					_wctombs(name, label + strlen(label), 36);
					res = f_setlabel(label);
					if(res == FR_OK){
						res = f_open(&f, "sfd:.no_boot_storage", FA_CREATE_ALWAYS | FA_WRITE);
						f_close(&f);
					}
				}
				f_mount(NULL, "sfd:", 0);
			}

			if(res != FR_OK){
				DBG_PRINT("failure");
				lv_label_set_text(lbl_status, "#FFDD00 Error:# Failed format emuSD partition!");
				manual_system_maintenance(true);
				sfd_end();
				goto error;
			}

			sfd_end();

			gpt_idx = _get_gpt_part_by_name(new_gpt, "emusd_mbr", gpt_idx);
		}
	}

	// Format HOS USER if size changed
	if(has_gpt){
		s32 gpt_idx = _get_gpt_part_by_name(new_gpt, "USER", -1);
		if(gpt_idx != -1){
			// resize user if necessary
			gpt_entry_t *entry = &new_gpt->entries[gpt_idx];
			u32 user_size      = entry->lba_end - new_gpt->entries[gpt_idx].lba_start + 1;

			if(user_size != (part_info.hos_os_og_size - part_info.hos_sys_size_mb) << 11){
				lv_label_set_text(lbl_status, "#00DDFF Status:# Resizing HOS USER partition...");
				manual_system_maintenance(true);

				// size changed
				emmc_part_t user_part = { 0 };
				user_part.lba_end     = entry->lba_end;
				user_part.lba_start   = entry->lba_start;
				strcpy(user_part.name, "USER");

				user_size = ALIGN(user_size, 0x20);
				disk_set_info(DRIVE_EMU, SET_SECTOR_COUNT, &user_size);

				if(!_derive_bis_keys(new_gpt)){
					lv_label_set_text(lbl_status, "#FFDD00 Error:# BIS key generation failed!");
					manual_system_maintenance(true);
					goto error;
				}

				nx_emmc_bis_init(&user_part, true, storage, 0);

				u8 *buf = malloc(SZ_4M);
				u32 mkfs_res = f_mkfs("emu:", FM_FAT32 | FM_SFD | FM_PRF2, 16384, buf, SZ_4M);

				nx_emmc_bis_end();
				hos_bis_keys_clear();

				if(mkfs_res != FR_OK){
					lv_label_set_text(lbl_status, "#FFDD00 Error:# Failed to format HOS USER partition!");
					manual_system_maintenance(true);
					goto error;
				}
			}
		}
	}

	// Enable/Disable buttons depending on partition layout.
	if (part_info.l4t_size)
	{
		lv_obj_set_click(btn_flash_l4t, true);
		lv_btn_set_state(btn_flash_l4t, LV_BTN_STATE_REL);
	}
	else
	{
		lv_obj_set_click(btn_flash_l4t, false);
		lv_btn_set_state(btn_flash_l4t, LV_BTN_STATE_INA);
	}

	// Enable/Disable buttons depending on partition layout.
	if (part_info.and_size)
	{
		lv_obj_set_click(btn_flash_android, true);
		lv_btn_set_state(btn_flash_android, LV_BTN_STATE_REL);
	}
	else
	{
		lv_obj_set_click(btn_flash_android, false);
		lv_btn_set_state(btn_flash_android, LV_BTN_STATE_INA);
	}

	lv_label_set_text(lbl_status, "#00DDFF Status:# Done!");
	manual_system_maintenance(true);

	// Set buttons depending on what user chose to create.
	if (part_info.l4t_size && part_info.and_size)
		lv_mbox_add_btns(mbox, part_info.drive == DRIVE_SD ? mbox_btn_map1 : mbox_btn_map1_emmc, _action_part_manager_flash_options0);
	else if (part_info.l4t_size)
		lv_mbox_add_btns(mbox, part_info.drive == DRIVE_SD ? mbox_btn_map2 : mbox_btn_map2_emmc, _action_part_manager_flash_options1);
	else if (part_info.and_size)
		lv_mbox_add_btns(mbox, part_info.drive == DRIVE_SD ? mbox_btn_map3 : mbox_btn_map3_emmc, _action_part_manager_flash_options2);

	if (part_info.l4t_size || part_info.and_size)
		buttons_set = true;


error:
	DBG_PRINT("Done format");

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

	free(new_gpt);

	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:
		if(part_info.drive == DRIVE_SD){
			action_ums_sd(btns);
		}else{
			action_ums_emmc_gpp(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_warn(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_map3[] = { "\222eMMC 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(SZ_4K);
	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);

	if(part_info.drive == DRIVE_SD){
		s_printf(txt_buf, "#FFDD00 Warning: This will partition the SD Card!#\n\n");
	}else{
		s_printf(txt_buf, "#FFDD00 Warning: This will partition the eMMC!#\n\n");
	}

	if (part_info.backup_possible && part_info.hos_size)
	{
		strcat(txt_buf, "#C7EA46 Your files will be backed up and restored!#\n"
			"#FFDD00 Any other partition will be wiped!#");
	}
	else if(part_info.skip_backup)
	{
		// We have no files to back up
		if(part_info.drive == DRIVE_SD){
			strcat(txt_buf, "#FFDD00 All partitions will be wiped!#\n");
		}else{
			if(part_info.hos_os_size){
				strcat(txt_buf, "#FFDD00 All partitions (except HOS ones) will be wiped!#\n");
			}else{
				strcat(txt_buf, "#FFDD00 All partitions will be wiped!#\n");
			}
		}
	}else{
		// Have files, can't back up
		strcat(txt_buf, "#FFDD00 Your files will be wiped!#\n");
		if(part_info.drive == DRIVE_SD){
			strcat(txt_buf, "#FFDD00 All partitions will be also wiped!#\n");
		}else{
			if(part_info.hos_os_size){
				strcat(txt_buf, "#FFDD00 All partitions (except HOS ones) will also be wiped!#\n");
			}else{
				strcat(txt_buf, "#FFDD00 All partitions will also be wiped!#\n");
			}
		}
		strcat(txt_buf, "#FFDD00 Use USB UMS to copy them over!#");
	}

	lv_label_set_text(lbl_status, txt_buf);

	if ((part_info.backup_possible && part_info.hos_size) || part_info.skip_backup)
		lv_mbox_add_btns(mbox, mbox_btn_map2, _create_mbox_partitioning_option1);
	else
		lv_mbox_add_btns(mbox, part_info.drive == DRIVE_SD ? mbox_btn_map : mbox_btn_map3, _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 lv_res_t _create_mbox_partitioning_android(lv_obj_t *btns, const char *txt)
{
	int btn_idx = lv_btnm_get_pressed(btns);

	mbox_action(btns, txt);

	part_info.and_dynamic = !btn_idx;
	_create_mbox_partitioning_warn(NULL);

	return LV_RES_INV;
}

static lv_res_t _create_mbox_partitioning_andr_part(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[] = { "\222Dynamic", "\222Legacy", "" };
	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 Android Partitioning#");

	lv_obj_t *lbl_status = lv_label_create(mbox, NULL);
	lv_label_set_recolor(lbl_status, true);

	lv_label_set_text(lbl_status,
		"Please select a partition scheme:\n\n"
		"#C7EA46 Dynamic:# Android 13+\n"
		"#C7EA46 Legacy:# Android 10-11\n");

	lv_mbox_add_btns(mbox, mbox_btn_map, _create_mbox_partitioning_android);
	lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
	lv_obj_set_top(mbox, true);

	return LV_RES_OK;
}

static lv_res_t _create_mbox_partitioning_next(lv_obj_t *btn) {
	if (part_info.and_size)
		return _create_mbox_partitioning_andr_part(NULL);
	else
		return _create_mbox_partitioning_warn(NULL);
}

static void _update_partition_bar()
{
	lv_obj_t *h1 = lv_obj_get_parent(part_info.bar_hos);

	// Set widths based on max bar width.
	lv_coord_t w = lv_obj_get_width(h1);
	
	// account for alignment + 1mb for backup gpt
	u32 total_size         = part_info.total_sct_available / SECTORS_PER_GB;

	u32 bar_hos_size       = w * (part_info.hos_size    >> 10) / total_size;
	u32 bar_emu_size       = w * (part_info.emu_size    >> 10) / total_size;
	u32 bar_l4t_size       = w * (part_info.l4t_size    >> 10) / total_size;
	u32 bar_and_size       = w * (part_info.and_size    >> 10) / total_size;
	u32 bar_hos_os_size    = w * (part_info.hos_os_size >> 10) / total_size;
	u32 bar_emu_sd_size    = w * (part_info.emu_sd_size >> 10) / total_size;

	u32 bar_remaining_size = w - (bar_hos_size + bar_emu_size + bar_l4t_size + bar_and_size + bar_hos_os_size + bar_emu_sd_size);
	bar_remaining_size = bar_remaining_size <= 7 ? 0 : bar_remaining_size;

	// Update bar widths.
	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_set_size(part_info.bar_hos_os,    bar_hos_os_size,    LV_DPI / 2);
	lv_obj_set_size(part_info.bar_remaining, bar_remaining_size, LV_DPI / 2);
	lv_obj_set_size(part_info.bar_emu_sd,    bar_emu_sd_size,    LV_DPI / 2);

	// Re-align bars.
	lv_obj_align(part_info.bar_hos, part_info.bar_hos_os, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
	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_sd, 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_align(part_info.bar_remaining, part_info.bar_and, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
	lv_obj_align(part_info.bar_emu_sd, part_info.bar_emu, LV_ALIGN_OUT_RIGHT_MID, 0, 0);

	// Set HOS OS blending separator sizes and realign.
	lv_obj_set_size(part_info.sep_hos_os, bar_hos_os_size && (bar_remaining_size || bar_hos_size || bar_and_size || bar_l4t_size || bar_emu_sd_size || bar_emu_size) ? 8 : 0, LV_DPI / 2);
	lv_obj_align(part_info.sep_hos_os, part_info.bar_hos_os, LV_ALIGN_OUT_RIGHT_MID, -4, 0);

	// Set hos separator
	lv_obj_set_size(part_info.sep_hos, bar_hos_size && (bar_remaining_size || bar_and_size || bar_l4t_size || bar_emu_sd_size || bar_emu_size) ? 8 : 0, LV_DPI / 2);
	lv_obj_align(part_info.sep_hos, part_info.bar_hos, LV_ALIGN_OUT_RIGHT_MID, -4, 0);

	// Set emuMMC blending separator sizes and realign.
	lv_obj_set_size(part_info.sep_emu, bar_emu_size && (bar_remaining_size || bar_and_size || bar_l4t_size || bar_emu_sd_size) ? 8 : 0, LV_DPI / 2);
	lv_obj_align(part_info.sep_emu, part_info.bar_emu, LV_ALIGN_OUT_RIGHT_MID, -4, 0);

	// Set emuSD blending separator
	lv_obj_set_size(part_info.sep_emu_sd, bar_emu_sd_size && (bar_remaining_size || bar_and_size || bar_l4t_size) ? 8 : 0, LV_DPI / 2);
	lv_obj_align(part_info.sep_emu_sd, part_info.bar_emu_sd, LV_ALIGN_OUT_RIGHT_MID, -4, 0);

	// Set L4T blending separator sizes and realign.
	lv_obj_set_size(part_info.sep_l4t, bar_l4t_size && (bar_remaining_size || bar_and_size) ? 8 : 0, LV_DPI / 2);
	lv_obj_align(part_info.sep_l4t, part_info.bar_l4t, LV_ALIGN_OUT_RIGHT_MID, -4, 0);

	// Set Android blending separator sizes and realign.
	lv_obj_set_size(part_info.sep_and, bar_and_size && bar_remaining_size ? 8 : 0, LV_DPI / 2);
	lv_obj_align(part_info.sep_and, part_info.bar_and, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
}

static lv_res_t _action_slider_hos(lv_obj_t *slider){
	char lbl_text[64];

	u32 size = (u32)lv_slider_get_value(slider) << 10;

	if(size < (u32)part_info.hos_min_size_mb / 2){
		size = 0;
	}else if(size < (u32)part_info.hos_min_size_mb){
		size = part_info.hos_min_size_mb;
	}

	part_info.auto_assign_free_storage = size != 0;

	if(size){
		// account for alignment and 1mb for backup gpt
		size = (part_info.total_sct_available >> 11) - part_info.and_size - part_info.emu_size - part_info.hos_os_size - part_info.l4t_size - part_info.emu_sd_size;
	}

	part_info.hos_size = size;
	lv_slider_set_value(slider, size >> 10);


	s_printf(lbl_text, "#96FF00 %d GiB#", size >> 10);
	lv_label_set_text(part_info.lbl_hos, lbl_text);

	_update_partition_bar();

	return LV_RES_OK;
}

static lv_res_t _action_slider_hos_os(lv_obj_t *slider){
	char lbl_text[64];

	u32 user_size = (u32)lv_slider_get_value(slider) << 10;
	u32 user_size_og = part_info.hos_os_og_size - part_info.hos_sys_size_mb;

	// min. 4Gb for HOS USER
	if (user_size < 2048)
		user_size = 0;
	else if (user_size < 4096)
		user_size = 4096;
	else if(user_size >= user_size_og - 3072 && user_size <= user_size_og + 3072){
		user_size = user_size_og;
	}

	u32 hos_os_size = user_size ? (user_size + part_info.hos_sys_size_mb) : 0;

	u32 align = ALIGN(hos_os_size, AU_ALIGN_SECTORS >> 11) - hos_os_size;
	hos_os_size += align;

	s32 hos_size = (part_info.total_sct_available >> 11) - part_info.emu_size - part_info.l4t_size - part_info.and_size - hos_os_size - part_info.emu_sd_size;

	// Sanitize sizes based on new HOS OS size.
	if(!part_info.auto_assign_free_storage){
		u32 total = part_info.and_size + part_info.hos_size + part_info.emu_size + part_info.l4t_size + hos_os_size + part_info.emu_sd_size;
		if(total > part_info.total_sct_available >> 11){
			hos_os_size = (part_info.total_sct_available >> 11) - part_info.l4t_size - part_info.and_size - part_info.emu_size - part_info.hos_size - part_info.emu_sd_size;
			// other partitions and size are aligned
			align = 0;
			user_size = hos_os_size - part_info.hos_sys_size_mb;
			lv_slider_set_value(slider, (hos_os_size - part_info.hos_sys_size_mb) >> 10);
		}
	}else if (hos_size > part_info.hos_min_size_mb)
	{
		if (user_size <= 4096)
			lv_slider_set_value(slider, user_size >> 10);
	}
	else
	{
		hos_os_size = (part_info.total_sct_available >> 11) - part_info.emu_size - part_info.l4t_size - part_info.and_size - part_info.hos_min_size_mb - part_info.emu_sd_size;
		hos_size = (part_info.total_sct_available >> 11) - part_info.emu_size - part_info.l4t_size - part_info.and_size - hos_os_size - part_info.emu_sd_size;
		align = 0;
		if (hos_size < part_info.hos_min_size_mb || hos_os_size < part_info.hos_sys_size_mb + 4096)
		{
			lv_slider_set_value(slider, (part_info.hos_os_size - part_info.hos_sys_size_mb) >> 10);
			goto out;
		}
		user_size = hos_os_size - part_info.hos_sys_size_mb;
		lv_slider_set_value(slider, user_size >> 10);
	}

	part_info.hos_os_size = hos_os_size;
	part_info.hos_os_align = align;

	if(part_info.auto_assign_free_storage){
		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(user_size == user_size_og){
		s_printf(lbl_text, "#FFD300 %d FULL#", user_size >> 10);
	}else{
		s_printf(lbl_text, "#FFD300 %d GiB#", user_size >> 10);
	}
	lv_label_set_text(part_info.lbl_hos_os, lbl_text);

	_update_partition_bar();

out:
	return LV_RES_OK;
}

static lv_res_t _action_slider_emu(lv_obj_t *slider)
{
	#define EMUMMC_32GB_FULL 29856
	#define EMUMMC_64GB_FULL 59664

	static const u32 rsvd_mb = 4 + 4 + 16 + 8; // BOOT0 + BOOT1 + 16MB protective offset + 8MB alignment.
	u32 max_emmc_size = !part_info.emmc_is_64gb ? EMUMMC_32GB_FULL : EMUMMC_64GB_FULL;

	u32 size;
	char lbl_text[64];
	int slide_val = lv_slider_get_value(slider);

	int max_slider = lv_slider_get_max_value(slider);

	size = slide_val > (max_slider / 2) ? slide_val - (max_slider / 2) : slide_val;
	size <<= 10;

	bool is_full = false;

	// min 4Gb for emuMMC
	if(size < 4096 / 2){
		size = 0;
	}else if(size < 4096){
		size = 4096;
	}else if(size <= max_emmc_size + 3072 && size >= max_emmc_size - 3072){
		size = max_emmc_size;
		is_full = true;
	}

	bool emu_double = slide_val > max_slider / 2 && size;

	if(size){
		size += rsvd_mb; // Add reserved size.
	}

	if(emu_double){
		size *= 2;
	}

	// Sanitize sizes based on new HOS size.
	s32 hos_size = (part_info.total_sct_available >> 11) - size - part_info.emu_sd_size - part_info.l4t_size - part_info.and_size - part_info.hos_os_size;
	u32 total = part_info.l4t_size + part_info.and_size + part_info.hos_size + part_info.hos_os_size + part_info.emu_sd_size + size;

	if ((part_info.auto_assign_free_storage && hos_size > part_info.hos_min_size_mb) || (!part_info.auto_assign_free_storage && total <= part_info.total_sct_available >> 11))
	{
		part_info.emu_size = size;
		part_info.emu_double	 = emu_double;

		u32 temp_size = emu_double ? size / 2 : size;
		// set slider value again manually if in the snapping region
		if(temp_size <= 4096){
			u32 new_val = part_info.emu_double ? ((max_slider << 10) + part_info.emu_size) / 2 : part_info.emu_size;
			lv_slider_set_value(slider, new_val >> 10);
		}

		if(part_info.auto_assign_free_storage){
			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 (!emu_double)
		{
			if(is_full){
				s_printf(lbl_text, "#FF3C28 %d FULL#", size >> 10);
			}else{
				s_printf(lbl_text, "#FF3C28 %d GiB#", size >> 10);
			}
		}else{
			if(is_full){
				s_printf(lbl_text, "#FFDD00 2x##FF3C28 %d FULL#", size >> 11);
			}else{
				s_printf(lbl_text, "#FFDD00 2x##FF3C28 %d GiB#", size >> 11);
			}
		}
		lv_label_set_text(part_info.lbl_emu, lbl_text);
	}
	else
	{
		// reset slider to old value
		u32 old_val = part_info.emu_double ? ((max_slider << 10) + part_info.emu_size) / 2 : part_info.emu_size;
		lv_slider_set_value(slider, old_val >> 10);
	}

	_update_partition_bar();

	return LV_RES_OK;
}

static lv_res_t _action_slider_emu_sd(lv_obj_t *slider){
	u32 size;
	char lbl_text[64];
	int slide_val = lv_slider_get_value(slider);

	int max_slider = lv_slider_get_max_value(slider);

	size = slide_val > (max_slider / 2) ? slide_val - (max_slider / 2) : slide_val;
	size <<= 10;

	bool emu_sd_double = slide_val > max_slider / 2;

	if(size < (u32)part_info.hos_min_size_mb / 2){
		size = 0;
	}else if(size < (u32)part_info.hos_min_size_mb){
		size = part_info.hos_min_size_mb;
	}

	if(emu_sd_double){
		size *= 2;
	}

	// Sanitize sizes based on new HOS size.
	s32 hos_size = (part_info.total_sct_available >> 11) - size - part_info.emu_size	- part_info.l4t_size - part_info.and_size - part_info.hos_os_size;
	u32 total = part_info.l4t_size + part_info.and_size + part_info.hos_size + part_info.hos_os_size + part_info.emu_size + size;

	if ((part_info.auto_assign_free_storage && hos_size > part_info.hos_min_size_mb) || (!part_info.auto_assign_free_storage && total <= part_info.total_sct_available >> 11))
	{
		part_info.emu_sd_size = size;
		part_info.emu_sd_double	 = emu_sd_double;

		u32 temp_size = emu_sd_double ? size / 2 : size;
		if(temp_size <= (u32)part_info.hos_min_size_mb){
			u32 new_val = part_info.emu_sd_double ? ((max_slider << 10) + part_info.emu_sd_size) / 2 : part_info.emu_sd_size;
			lv_slider_set_value(slider, new_val >> 10);
		}

		if(part_info.auto_assign_free_storage){
			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 (!emu_sd_double)
		{
			s_printf(lbl_text, "#ff00d6 %d GiB#", size >> 10);
		}else{
			s_printf(lbl_text, "#FFDD00 2x##ff00d6 %d GiB#", size >> 11);
		}
		lv_label_set_text(part_info.lbl_emu_sd, lbl_text);
	}
	else
	{
		// reset slider to old value
		u32 old_val = part_info.emu_sd_double ? ((max_slider << 10) + part_info.emu_sd_size) / 2 : part_info.emu_sd_size;
		lv_slider_set_value(slider, old_val >> 10);
	}

	_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_available >> 11) - part_info.hos_os_size - part_info.emu_size - size - part_info.and_size - part_info.emu_sd_size;

	// Sanitize sizes based on new HOS size.
	if(!part_info.auto_assign_free_storage){
		u32 total = part_info.and_size + part_info.hos_os_size + part_info.emu_size + part_info.hos_size + size + part_info.emu_sd_size;
		if(total > part_info.total_sct_available >> 11){
			size = (part_info.total_sct_available >> 11) - part_info.hos_os_size - part_info.and_size - part_info.emu_size - part_info.hos_size - part_info.emu_sd_size;
			lv_slider_set_value(slider, size >> 10);
		}
	}else if (hos_size > part_info.hos_min_size_mb)
	{
		if (size <= 8192)
			lv_slider_set_value(slider, size >> 10);
	}
	else
	{
		size = (part_info.total_sct_available >> 11) - part_info.emu_size - part_info.hos_os_size - part_info.and_size - part_info.emu_sd_size - 2048;
		hos_size = (part_info.total_sct_available >> 11) - part_info.emu_size - part_info.hos_os_size - part_info.and_size - size - part_info.emu_sd_size;
		if (hos_size < part_info.hos_min_size_mb || size < 8192)
		{
			lv_slider_set_value(slider, part_info.l4t_size >> 10);
			goto out;
		}
		lv_slider_set_value(slider, size >> 10);
	}

	if(part_info.auto_assign_free_storage){
		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);
	}

	part_info.l4t_size = size;

	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)
{
	u32 user_size;
	u32 and_size;
	char lbl_text[64];
	int slide_val = lv_slider_get_value(slider);

	int max_slider = lv_slider_get_max_value(slider);

	#ifdef ENABLE_DUAL_ANDROID
	user_size = slide_val > (max_slider / 2) ? slide_val - (max_slider / 2) : slide_val;
	#else
	user_size = slide_val;
	#endif
	user_size <<= 10;

	if(user_size < 4096 / 2){
		user_size = 0;
	}else if(user_size < 4096){
		user_size = 4096;
	}

	#ifdef ENABLE_DUAL_ANDROID
	bool and_double = slide_val > max_slider / 2 && user_size;
	#else
	bool and_double = false;
	#endif

	and_size = 0;
	if(user_size){
		and_size = user_size + ANDROID_SYSTEM_SIZE_MB;
	}

	if(and_double){
		and_size *= 2;
	};

	// Sanitize sizes based on new HOS size.
	s32 hos_size = (part_info.total_sct_available >> 11) - and_size - part_info.hos_os_size - part_info.emu_size- part_info.l4t_size - part_info.emu_sd_size ;
	u32 total = part_info.l4t_size + part_info.emu_sd_size + part_info.hos_size + part_info.hos_os_size + part_info.emu_size + and_size;

	if ((part_info.auto_assign_free_storage && hos_size > part_info.hos_min_size_mb) || (!part_info.auto_assign_free_storage && total <= part_info.total_sct_available >> 11))
	{
		part_info.and_size = and_size;
		part_info.and_double = and_double;

		if(user_size <= 4096){
			u32 new_val = part_info.and_double ? (max_slider << 10) / 2 + user_size : user_size;
			lv_slider_set_value(slider, new_val >> 10);
		}

		if(part_info.auto_assign_free_storage){
			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 (!and_double)
		{
			s_printf(lbl_text, "#FF8000 %d GiB#", user_size >> 10);
		}else{
			s_printf(lbl_text, "#FFDD00 2x##FF8000 %d GiB#", user_size >> 10);
		}
		lv_label_set_text(part_info.lbl_and, lbl_text);
	}
	else
	{
		// reset slider to old value
		u32 old_val = part_info.and_double ? ((max_slider << 10) + part_info.and_size) / 2 : part_info.and_size;
		old_val -= ANDROID_SYSTEM_SIZE_MB;
		lv_slider_set_value(slider, old_val >> 10);
	}

	_update_partition_bar();

	return LV_RES_OK;
}

static lv_res_t _mbox_check_files_total_size_option(lv_obj_t *btns, const char *txt)
{
	// If "don't backup" button was pressed, disable backup/restore of files.
	if (!lv_btnm_get_pressed(btns))
		part_info.backup_possible = false;

	mbox_action(btns, txt);

	return LV_RES_INV;
}

static void _create_mbox_check_files_total_size(u8 drive)
{
	static lv_style_t bar_hos_os_ind, bar_hos_ind, bar_emu_ind, bar_l4t_ind, bar_and_ind, bar_emu_sd_ind, bar_remaining_ind;
	static lv_style_t sep_hos_os_bg, sep_hos_bg, sep_emu_bg, sep_l4t_bg, sep_emu_sd_bg, sep_and_bg;

	// Set HOS bar style.
	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;

	// Set emuMMC bar style.
	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;

	// Set L4T bar style.
	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;

	// Set Android bar style.
	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;

	// Set HOS OS bar style.
	lv_style_copy(&bar_hos_os_ind, lv_theme_get_current()->bar.indic);
	bar_hos_os_ind.body.main_color = LV_COLOR_HEX(0xffd300);
	bar_hos_os_ind.body.grad_color = bar_hos_os_ind.body.main_color;

	// Set Remaining bar style.
	lv_style_copy(&bar_remaining_ind, lv_theme_get_current()->bar.indic);
	bar_remaining_ind.body.main_color = LV_COLOR_HEX(0xc9c9c9);
	bar_remaining_ind.body.grad_color = bar_remaining_ind.body.main_color;

	// Set emu sd bar style.
	lv_style_copy(&bar_emu_sd_ind, lv_theme_get_current()->bar.indic);
	bar_emu_sd_ind.body.main_color = LV_COLOR_HEX(0xff00d6);
	bar_emu_sd_ind.body.grad_color = bar_emu_sd_ind.body.main_color;

	// Set separator styles.
	lv_style_copy(&sep_hos_os_bg, lv_theme_get_current()->cont);
	sep_hos_os_bg.body.main_color = LV_COLOR_HEX(0xffd300);
	sep_hos_os_bg.body.grad_color = sep_hos_os_bg.body.main_color;
	sep_hos_os_bg.body.radius = 0;

	lv_style_copy(&sep_hos_bg, &sep_hos_os_bg);
	sep_hos_bg.body.main_color = LV_COLOR_HEX(0x96FF00);
	sep_hos_bg.body.grad_color = sep_hos_bg.body.main_color;

	lv_style_copy(&sep_and_bg, &sep_hos_os_bg);
	sep_and_bg.body.main_color = LV_COLOR_HEX(0xff8000);
	sep_and_bg.body.grad_color = sep_and_bg.body.main_color;

	lv_style_copy(&sep_emu_bg, &sep_hos_os_bg);
	sep_emu_bg.body.main_color = LV_COLOR_HEX(0xFF3C28);
	sep_emu_bg.body.grad_color = sep_emu_bg.body.main_color;

	lv_style_copy(&sep_l4t_bg, &sep_hos_os_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_emu_sd_bg, &sep_hos_os_bg);
	sep_emu_sd_bg.body.main_color = LV_COLOR_HEX(0xff00d6);
	sep_emu_sd_bg.body.grad_color = sep_emu_sd_bg.body.main_color;

	char *txt_buf = malloc(SZ_8K);

	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[] = { "\251", "\222OK", "\251", "" };
	static const char *mbox_btn_map2[] = { "\222Don't Backup", "\222OK", "" };
	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, drive == DRIVE_SD ? "Analyzing SD card usage. This might take a while..." : "Analyzing eMMC 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(0x1000);
	u32 total_files = 0;
	u32 total_size = 0;
	path[0] = 0;

	mbr_t *mbr = zalloc(sizeof(*mbr));
	gpt_t *gpt = NULL;
	bool has_gpt = false;
	bool has_hos_data = false;

	sdmmc_storage_t *storage = drive == DRIVE_SD ? &sd_storage : &emmc_storage;

	// Read current MBR.
	sdmmc_storage_read(storage, 0, 1, mbr);

	// check if we have gpt
	has_gpt = _has_gpt(mbr);

	if(has_gpt){
		// Calculate GPT part size.
		gpt = zalloc(sizeof(*gpt));
		sdmmc_storage_read(storage, 1, sizeof(*gpt) >> 9, gpt);
	}

	if(has_gpt){
		if(_get_gpt_part_by_name(gpt, "hos_data", -1) != -1){
			gfx_printf("yes hos data\n");
			has_hos_data = true;
		}
	}

	int res;
	if(part_info.drive == DRIVE_EMMC && !has_hos_data){
			gfx_printf("emmc !hos data\n");
		// if we are on emmc, and dont have a partition named hos_data, dont even check files
		// we might find an emusd fat32 partition instead
		part_info.skip_backup = true;
		res = FR_NO_FILESYSTEM;
	}else{
		// Check total size of files.
		res = _stat_and_copy_files(drive == DRIVE_SD ? "sd:" : "emmc:", NULL, path, &total_files, &total_size, NULL);
	}

	gfx_printf("stat res %d\n", res);


	if(res == FR_NO_FILESYSTEM){
		// no fat system on selected storage, nothing to backup
		part_info.skip_backup = true;
	}

	// Not more than 1.0GB.
	part_info.backup_possible = !res && !(total_size > (RAM_DISK_SZ - SZ_16M));

	if (part_info.backup_possible)
	{
		s_printf(txt_buf,
			"#96FF00 The %s files will be backed up automatically!#\n"
			"#FFDD00 Any other partitions %swill be wiped!#\n"
			"#00DDFF Total files:# %d, #00DDFF Total size:# %d MiB", 
			drive == DRIVE_SD ? "SD card" : "eMMC", 
			drive == DRIVE_SD ? "" : "(except HOS ones) ", 
			total_files, 
			total_size >> 20);
		lv_mbox_set_text(mbox, txt_buf);
	}
	else
	{
		if(res == FR_NO_FILESYSTEM){
			// cant backup files because no filesystem
			s_printf(txt_buf,
				"#96FF00 No %s files to be backed up!#\n"
				"#FFDD00 Any other partitions %swill be wiped!#\n", 
				drive == DRIVE_SD ? "SD card" : "eMMC", drive == DRIVE_SD ? "" : "(except HOS ones) ");
		}else{
			// cant backup for some other reason
			s_printf(txt_buf,
				"#FFDD00 The %s files cannot be backed up automatically!#\n"
				"#FFDD00 Any other partitions %swill be wiped!#\n\n"
				"You will be asked to back up your files later via UMS.",
				drive == DRIVE_SD ? "SD card" : "eMMC", 
				drive == DRIVE_SD ? "" : "(except HOS ones) ");
		}
		lv_mbox_set_text(mbox, txt_buf);
	}

	// 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);


	total_size = storage->sec_cnt;

	u32 bar_hos_size       = 0;
	u32 bar_emu_size       = 0;
	u32 bar_l4t_size       = 0;
	u32 bar_and_size       = 0;
	u32 bar_hos_os_size    = 0;
	u32 bar_remaining_size = 0;
	u32 bar_emu_sd_size    = 0;
	

	lv_obj_t *lbl_part = lv_label_create(h1, NULL);
	lv_label_set_recolor(lbl_part, true);
	s_printf(txt_buf, "#00DDFF Current %s partition layout:#", has_gpt ? "GPT" : "MBR");
	lv_label_set_text(lbl_part, txt_buf);
	
	if(!has_gpt){
		// Calculate MBR partitions size.
		bar_hos_size = mbr->partitions[0].size_sct;
		for (u32 i = 1; i < 4; i++)
			if (mbr->partitions[i].type == 0xE0)
				bar_emu_size += mbr->partitions[i].size_sct;

		for (u32 i = 1; i < 4; i++)
			if (mbr->partitions[i].type == 0x83)
				bar_l4t_size += mbr->partitions[i].size_sct;
	}else{
		u32 i = 0;
		if(!memcmp(gpt->entries[10].name, (char[]){'U', 0, 'S', 0, 'E', 0, 'R', 0}, 8)){
			bar_hos_os_size += gpt->entries[10].lba_end - gpt->entries[0].lba_start + 1;
			i = 11;
		}

		for(; i < gpt->header.num_part_ents && i < 128; i++){
			gpt_entry_t *entry = &gpt->entries[i];

			if(!memcmp(entry->name, (char[]){ 'e', 0, 'm', 0, 'u', 0, 'm', 0, 'm', 0, 'c', 0 }, 12)){
				bar_emu_size += entry->lba_end - entry->lba_start + 1;
			}

			if(!memcmp(entry->name, (char[]){ 'b', 0, 'o', 0, 'o', 0, 't', 0 }, 8)){
				if((i + 6) < gpt->header.num_part_ents && (i + 6) < 128){
					if(!memcmp(gpt->entries[i + 6].name, (char[]){ 'u', 0, 's', 0, 'e', 0, 'r', 0, 'd', 0, 'a', 0, 't', 0, 'a', 0 }, 16)){
						// found android dynamic
						bar_and_size += gpt->entries[i + 6].lba_end - gpt->entries[i].lba_start + 1;
						i += 6;
					}
				}
			}

			if(!memcmp(entry->name, (char[]){ 'v', 0, 'e', 0, 'n', 0, 'd', 0, 'o', 0, 'r', 0 }, 12)){
				if(i + 8 < gpt->header.num_part_ents && i + 8 < 128){
					if(!memcmp(gpt->entries[i + 8].name, (char[]){ 'U', 0, 'D', 0, 'A', 0 }, 6)){
						// found android regular
						bar_and_size += gpt->entries[i + 8].lba_end - gpt->entries[i].lba_start + 1;
						i += 8;
					}
				}
			}

			if(!memcmp(entry->name, (char[]){ 'l', 0, '4', 0, 't', 0 }, 6)){
				bar_l4t_size += entry->lba_end - entry->lba_start + 1;
			}

			if(!memcmp(entry->name, (char[]){ 'h', 0, 'o', 0, 's', 0, '_', 0, 'd', 0, 'a', 0, 't', 0, 'a', 0 }, 16)){
				bar_hos_size += entry->lba_end - entry->lba_start + 1;
			}

			if(!memcmp(entry->name, (char[]){ 'e', 0, 'm', 0, 'u', 0, 's', 0, 'd', 0, '_', 0, 'm', 0, 'b', 0, 'r', 0 }, 18)){
				if((i + 1) < gpt->header.num_part_ents && (i + 1) < 128){
					if(!memcmp(gpt->entries[i + 1].name, (char[]){ 'e', 0, 'm', 0, 'u', 0, 's', 0, 'd', 0 }, 10)){
						bar_emu_sd_size += gpt->entries[i + 1].lba_end - gpt->entries[i].lba_start + 1;
						i++;
					}
				}
			}
		}
	}

	// bar_remaining_size = total_size - (bar_l4t_size + bar_and_size + bar_hos_os_size + bar_hos_size + bar_emu_size + bar_emu_sd_size);

	const u32 total_size_gb = total_size / SECTORS_PER_GB;
	lv_coord_t w = lv_obj_get_width(h1);
	bar_l4t_size       = w * (bar_l4t_size       / SECTORS_PER_GB) / total_size_gb;
	bar_and_size       = w * (bar_and_size       / SECTORS_PER_GB) / total_size_gb;
	bar_hos_os_size    = w * (bar_hos_os_size    / SECTORS_PER_GB) / total_size_gb;
	bar_hos_size       = w * (bar_hos_size       / SECTORS_PER_GB) / total_size_gb;
	bar_emu_size       = w * (bar_emu_size       / SECTORS_PER_GB) / total_size_gb;
	bar_emu_sd_size    = w * (bar_emu_sd_size    / SECTORS_PER_GB) / total_size_gb;
	// bar_remaining_size = w * (bar_remaining_size / SECTORS_PER_GB) / (total_size / SECTORS_PER_GB);
	// bar_remaining_size = bar_remaining_size <= 7 ? 0 : bar_remaining_size;
	bar_remaining_size = w - bar_emu_sd_size - bar_emu_size - bar_hos_size - bar_hos_os_size - bar_and_size - bar_l4t_size;
	bar_remaining_size = bar_remaining_size <= 7 ? 0 : bar_remaining_size;

	// Create HOS OS bar.
	lv_obj_t *bar_hos_os = lv_bar_create(h1, NULL);
	lv_obj_set_size(bar_hos_os, bar_hos_os_size, LV_DPI / 3);
	lv_bar_set_range(bar_hos_os, 0, 1);
	lv_bar_set_value(bar_hos_os, 1);
	lv_bar_set_style(bar_hos_os, LV_BAR_STYLE_INDIC, &bar_hos_os_ind);
	lv_obj_align(bar_hos_os, lbl_part, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 6);

	// Create HOS bar.
	lv_obj_t *bar_hos = lv_bar_create(h1, bar_hos_os);
	lv_obj_set_size(bar_hos, bar_hos_size, LV_DPI / 3);
	lv_bar_set_style(bar_hos, LV_BAR_STYLE_INDIC, &bar_hos_ind);
	lv_obj_align(bar_hos, bar_hos_os, LV_ALIGN_OUT_RIGHT_MID, 0, 0);

	// Create emuMMC bar.
	lv_obj_t *bar_emu = lv_bar_create(h1, bar_hos_os);
	lv_obj_set_size(bar_emu, bar_emu_size, LV_DPI / 3);
	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);

	// Create emuSD bar.
	lv_obj_t *bar_emu_sd = lv_bar_create(h1, bar_hos_os);
	lv_obj_set_size(bar_emu_sd, bar_emu_sd_size, LV_DPI / 3);
	lv_bar_set_style(bar_emu_sd, LV_BAR_STYLE_INDIC, &bar_emu_sd_ind);
	lv_obj_align(bar_emu_sd, bar_emu, LV_ALIGN_OUT_RIGHT_MID, 0, 0);

	// Create L4T bar.
	lv_obj_t *bar_l4t = lv_bar_create(h1, bar_hos_os);
	lv_obj_set_size(bar_l4t, bar_l4t_size, LV_DPI / 3);
	lv_bar_set_style(bar_l4t, LV_BAR_STYLE_INDIC, &bar_l4t_ind);
	lv_obj_align(bar_l4t, bar_emu_sd, LV_ALIGN_OUT_RIGHT_MID, 0, 0);

	// Create android bar.
	lv_obj_t *bar_and = lv_bar_create(h1, bar_hos_os);
	lv_obj_set_size(bar_and, bar_and_size, LV_DPI / 3);
	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);

	//Create Remaining bar.
	lv_obj_t *bar_remaining = lv_bar_create(h1, bar_hos_os);
	lv_obj_set_size(bar_remaining, bar_remaining_size, LV_DPI / 3);
	lv_bar_set_style(bar_remaining, LV_BAR_STYLE_INDIC, &bar_remaining_ind);
	lv_obj_align(bar_remaining, bar_and, LV_ALIGN_OUT_RIGHT_MID, 0, 0);


	// Create HOS OS separator.
	lv_obj_t *sep_hos_os = lv_cont_create(h1, NULL);
	lv_obj_set_size(sep_hos_os, bar_hos_os_size && (bar_emu_sd_size || bar_and_size || bar_remaining_size || bar_l4t_size || bar_emu_size || bar_hos_size) ? 8 : 0, LV_DPI / 3);
	lv_obj_set_style(sep_hos_os, &sep_hos_os_bg);
	lv_obj_align(sep_hos_os, bar_hos_os, LV_ALIGN_OUT_RIGHT_MID, -4, 0);

	// Create HOS separator.
	lv_obj_t *sep_hos = lv_cont_create(h1, NULL);
	lv_obj_set_size(sep_hos, bar_hos_size && (bar_emu_sd_size || bar_and_size || bar_remaining_size || bar_l4t_size || bar_emu_size)? 8 : 0, LV_DPI / 3);
	lv_obj_set_style(sep_hos, &sep_hos_bg);
	lv_obj_align(sep_hos, bar_hos, LV_ALIGN_OUT_RIGHT_MID, -4, 0);

	// Create emuMMC separator.
	lv_obj_t *sep_emu = lv_cont_create(h1, NULL);
	lv_obj_set_size(sep_emu, bar_emu_size && (bar_emu_sd_size || bar_and_size || bar_remaining_size || bar_l4t_size)? 8 : 0, LV_DPI / 3);
	lv_obj_set_style(sep_emu, &sep_emu_bg);
	lv_obj_align(sep_emu, bar_emu, LV_ALIGN_OUT_RIGHT_MID, -4, 0);

	// Create emuSD separator.
	lv_obj_t *sep_emu_sd = lv_cont_create(h1, NULL);
	lv_obj_set_size(sep_emu_sd, bar_emu_sd_size && (bar_and_size || bar_remaining_size || bar_l4t_size)? 8 : 0, LV_DPI / 3);
	lv_obj_set_style(sep_emu_sd, &sep_emu_sd_bg);
	lv_obj_align(sep_emu_sd, bar_emu_sd, LV_ALIGN_OUT_RIGHT_MID, -4, 0);

	// Create L4T separator.
	lv_obj_t *sep_l4t = lv_cont_create(h1, NULL);
	lv_obj_set_size(sep_l4t, bar_l4t_size && (bar_and_size || bar_remaining_size) ? 8 : 0, LV_DPI / 3);
	lv_obj_set_style(sep_l4t, &sep_l4t_bg);
	lv_obj_align(sep_l4t, bar_l4t, LV_ALIGN_OUT_RIGHT_MID, -4, 0);

	// Create Android separator.
	lv_obj_t *sep_and = lv_cont_create(h1, NULL);
	lv_obj_set_size(sep_and, bar_and_size && bar_remaining_size ? 8 : 0, LV_DPI / 3);
	lv_obj_set_style(sep_and, &sep_and_bg);
	lv_obj_align(sep_and, bar_and, LV_ALIGN_OUT_RIGHT_MID, -4, 0);

	// Print partition table info.
	if(!has_gpt){
		// print mbr table
		s_printf(txt_buf,
			"Part. 0 - Type: %02x, Start: %08x, Size: %08x\n"
			"Part. 1 - Type: %02x, Start: %08x, Size: %08x\n"
			"Part. 2 - Type: %02x, Start: %08x, Size: %08x\n"
			"Part. 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);
	}else{
		strcpy(txt_buf, "");
		for(u32 i = 0; i < gpt->header.num_part_ents && i < 128; i++){
			char txt_buf2[36];
			_wctombs((u16*)&gpt->entries[i].name, txt_buf2, 36);
			if(gpt->header.num_part_ents > 9){
				s_printf(txt_buf + strlen(txt_buf), "Part. %02d - Name : %s\n           Start: %08x, Size: %08x%c", i, txt_buf2, (u32)gpt->entries[i].lba_start, (u32)(gpt->entries[i].lba_end - gpt->entries[i].lba_start + 1), i == gpt->header.num_part_ents || i == 127 ? '\0' : '\n');
			}else{
				s_printf(txt_buf + strlen(txt_buf), "Part. %d - Name: %s\n          Start: %08x, Size: %08x%c", i, txt_buf2, (u32)gpt->entries[i].lba_start, (u32)(gpt->entries[i].lba_end - gpt->entries[i].lba_start + 1), i == gpt->header.num_part_ents || i == 127 ? '\0' : '\n');
			}
		}
	}


	lv_obj_t *ta_table = lv_ta_create(h1, NULL);
	lv_ta_set_cursor_type(ta_table, LV_CURSOR_NONE);
	lv_ta_set_text_align(ta_table, LV_LABEL_ALIGN_LEFT);
	lv_ta_set_sb_mode(ta_table, LV_SB_MODE_AUTO);
	lv_ta_set_style(ta_table, LV_TA_STYLE_BG, &monospace_text);
	lv_obj_set_size(ta_table, w, w * 2 / 7);
	lv_ta_set_text(ta_table, txt_buf);
	lv_obj_align(ta_table, h1, LV_ALIGN_IN_TOP_MID, 0, LV_DPI);


	if (!part_info.backup_possible)
		lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action);
	else
		lv_mbox_add_btns(mbox, mbox_btn_map2, _mbox_check_files_total_size_option);

	lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);

	free(txt_buf);
	free(path);
	free(mbr);
	free(gpt);
}

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[] = { "\251", "\222OK", "\251", "" };
	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);

	mbr_t mbr[2] = { 0 };
	gpt_t *gpt = zalloc(sizeof(gpt_t));
	gpt_header_t gpt_hdr_backup = { 0 };

	bool has_mbr_attributes = false;
	bool hybrid_mbr_changed = false;
	bool gpt_partition_exists = false;

	// Try to init sd card. No need for valid MBR.
	if (!sd_mount() && !sd_get_card_initialized())
	{
		lv_label_set_text(lbl_status, "#FFDD00 Failed to init SD!#");
		goto out;
	}

	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();

	// Check for secret MBR attributes.
	if (gpt->entries[0].part_guid[7])
		has_mbr_attributes = true;

	// Check if there's a GPT Protective partition.
	for (u32 i = 0; i < 4; i++)
	{
		if (mbr[0].partitions[i].type == 0xEE)
			gpt_partition_exists = true;
	}

	// Check if GPT is valid.
	if (!gpt_partition_exists || memcmp(&gpt->header.signature, "EFI PART", 8) || gpt->header.num_part_ents > 128)
	{
		lv_label_set_text(lbl_status, "#FFDD00 Warning:# No valid GPT was found!");

		gpt_partition_exists = false;

		if (has_mbr_attributes)
			goto check_changes;
		else
			goto out;
	}

	sdmmc_storage_read(&sd_storage, gpt->header.alt_lba, 1, &gpt_hdr_backup);

	// Parse GPT.
	LIST_INIT(gpt_parsed);
	for (u32 i = 0; i < gpt->header.num_part_ents; i++)
	{
		emmc_part_t *part = (emmc_part_t *)zalloc(sizeof(emmc_part_t));

		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;

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

	// Set FAT and emuMMC partitions.
	u32 mbr_idx = 1;
	bool found_hos_data = false;
	LIST_FOREACH_ENTRY(emmc_part_t, part, &gpt_parsed, link)
	{
		// FatFS simple GPT found a fat partition, set it.
		if (sd_fs.part_type && !part->index)
		{
			mbr[1].partitions[0].type = sd_fs.fs_type == FS_EXFAT ? 0x7 : 0xC;
			mbr[1].partitions[0].start_sct = part->lba_start;
			mbr[1].partitions[0].size_sct = (part->lba_end - part->lba_start + 1);
		}

		// FatFS simple GPT didn't find a fat partition as the first one.
		if (!sd_fs.part_type && !found_hos_data && !strcmp(part->name, "hos_data"))
		{
			mbr[1].partitions[0].type = 0xC;
			mbr[1].partitions[0].start_sct = part->lba_start;
			mbr[1].partitions[0].size_sct = (part->lba_end - part->lba_start + 1);
			found_hos_data = true;
		}

		// Set up to max 2 emuMMC partitions.
		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++;
		}

		// Total reached last slot.
		if (mbr_idx >= 3)
			break;
	}

	emmc_gpt_free(&gpt_parsed);

	// Set GPT protective partition.
	mbr[1].partitions[mbr_idx].type = 0xEE;
	mbr[1].partitions[mbr_idx].start_sct = 1;
	mbr[1].partitions[mbr_idx].size_sct = sd_storage.sec_cnt - 1;

	// Check for differences.
	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))
		{
			hybrid_mbr_changed = true;
			break;
		}
	}

check_changes:
	if (!hybrid_mbr_changed && !has_mbr_attributes)
	{
		lv_label_set_text(lbl_status, "#96FF00 Warning:# The Hybrid MBR needs no change!#");
		goto out;
	}

	char *txt_buf = malloc(SZ_16K);

	// Current MBR info.
	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);

	// New MBR info.
	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)
	{
		sd_mount();

		// Write MBR.
		if (hybrid_mbr_changed)
			sdmmc_storage_write(&sd_storage, 0, 1, &mbr[1]);

		// Fix MBR secret attributes.
		if (has_mbr_attributes)
		{
			// Clear secret attributes.
			gpt->entries[0].part_guid[7] = 0;

			if (gpt_partition_exists)
			{
				// Fix CRC32s.
				u32 entries_size = sizeof(gpt_entry_t) * gpt->header.num_part_ents;
				gpt->header.part_ents_crc32 = crc32_calc(0, (const u8 *)gpt->entries, entries_size);
				gpt->header.crc32 = 0; // Set to 0 for calculation.
				gpt->header.crc32 = crc32_calc(0, (const u8 *)&gpt->header, gpt->header.size);

				gpt_hdr_backup.part_ents_crc32 = gpt->header.part_ents_crc32;
				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.
				u32 aligned_entries_size = ALIGN(entries_size, SD_BLOCKSIZE);
				sdmmc_storage_write(&sd_storage, gpt->header.my_lba, (sizeof(gpt_header_t) + aligned_entries_size) >> 9, gpt);

				// Write backup GPT partition table.
				sdmmc_storage_write(&sd_storage, gpt_hdr_backup.part_ent_lba, aligned_entries_size >> 9, gpt->entries);

				// Write backup GPT header.
				sdmmc_storage_write(&sd_storage, gpt_hdr_backup.my_lba, 1, &gpt_hdr_backup);
			}
			else
			{
				// Only write the relevant sector if the only change is MBR attributes.
				sdmmc_storage_write(&sd_storage, 2, 1, &gpt->entries[0]);
			}
		}

		sd_unmount();

		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:
	free(gpt);

	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, u8 drive)
{
	char title_str[0x20];
	s_printf(title_str, "%s %s Partition Manager", drive == DRIVE_SD ? SYMBOL_SD : SYMBOL_CHIP, drive == DRIVE_SD ? "SD" : "eMMC");
	lv_obj_t *win = nyx_create_standard_window(title_str);

	if(drive == DRIVE_SD){
		lv_win_add_btn(win, NULL, SYMBOL_MODULES_ALT" Fix Hybrid MBR", _action_fix_mbr);
	}

	static lv_style_t bar_hos_os_bg, bar_hos_bg, bar_emu_bg, bar_l4t_bg, bar_and_bg, bar_emu_sd_bg;
	static lv_style_t bar_hos_os_ind, bar_hos_ind, bar_emu_ind, bar_l4t_ind, bar_and_ind, bar_remaining_ind, bar_emu_sd_ind;
	static lv_style_t bar_hos_os_btn, bar_hos_btn, bar_emu_btn, bar_l4t_btn, bar_and_btn, bar_emu_sd_btn;
	static lv_style_t sep_emu_bg, sep_l4t_bg, sep_and_bg, sep_hos_bg, sep_hos_os_bg, sep_emu_sd_bg;

	// Set HOS bar styles.
	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(&sep_hos_bg, lv_theme_get_current()->cont);
	sep_hos_bg.body.main_color = LV_COLOR_HEX(0x96FF00);
	sep_hos_bg.body.grad_color = sep_hos_bg.body.main_color;
	sep_hos_bg.body.radius = 0;

	// Set Remaining Space style
	lv_style_copy(&bar_remaining_ind, lv_theme_get_current()->bar.indic);
	bar_remaining_ind.body.main_color = LV_COLOR_HEX(0xc9c9c9);
	bar_remaining_ind.body.grad_color = bar_remaining_ind.body.main_color;

	// Set eMUMMC bar styles.
	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, &sep_hos_bg);
	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;

	// Set L4T bar styles.
	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_hos_bg);
	sep_l4t_bg.body.main_color = LV_COLOR_HEX(0x00DDFF);
	sep_l4t_bg.body.grad_color = sep_l4t_bg.body.main_color;

	// Set Android bar styles.
	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_hos_bg);
	sep_and_bg.body.main_color = LV_COLOR_HEX(0xFF8000);
	sep_and_bg.body.grad_color = sep_and_bg.body.main_color;

	// Set HOS OS bar styles.
	lv_style_copy(&bar_hos_os_bg, lv_theme_get_current()->bar.bg);
	bar_hos_os_bg.body.main_color = LV_COLOR_HEX(0xb89900);
	bar_hos_os_bg.body.grad_color = bar_hos_os_bg.body.main_color;
	lv_style_copy(&bar_hos_os_ind, lv_theme_get_current()->bar.indic);
	bar_hos_os_ind.body.main_color = LV_COLOR_HEX(0xffd300);
	bar_hos_os_ind.body.grad_color = bar_hos_os_ind.body.main_color;
	lv_style_copy(&bar_hos_os_btn, lv_theme_get_current()->slider.knob);
	bar_hos_os_btn.body.main_color = LV_COLOR_HEX(0xe6bf00);
	bar_hos_os_btn.body.grad_color = bar_hos_os_btn.body.main_color;
	lv_style_copy(&sep_hos_os_bg, &sep_hos_bg);
	sep_hos_os_bg.body.main_color = LV_COLOR_HEX(0xffd300);
	sep_hos_os_bg.body.grad_color = sep_hos_os_bg.body.main_color;

	// Set emuSD bar styles.
	lv_style_copy(&bar_emu_sd_bg, lv_theme_get_current()->bar.bg);
	bar_emu_sd_bg.body.main_color = LV_COLOR_HEX(0x96007e);
	bar_emu_sd_bg.body.grad_color = bar_emu_sd_bg.body.main_color;
	lv_style_copy(&bar_emu_sd_ind, lv_theme_get_current()->bar.indic);
	bar_emu_sd_ind.body.main_color = LV_COLOR_HEX(0xff00d6);
	bar_emu_sd_ind.body.grad_color = bar_emu_sd_ind.body.main_color;
	lv_style_copy(&bar_emu_sd_btn, lv_theme_get_current()->slider.knob);
	bar_emu_sd_btn.body.main_color = LV_COLOR_HEX(0xc700a7);
	bar_emu_sd_btn.body.grad_color = bar_emu_sd_btn.body.main_color;
	lv_style_copy(&sep_emu_sd_bg, &sep_hos_bg);
	sep_emu_sd_bg.body.main_color = LV_COLOR_HEX(0xff00d6);
	sep_emu_sd_bg.body.grad_color = sep_emu_sd_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);


	sdmmc_storage_t *storage = drive == DRIVE_SD ? &sd_storage : &emmc_storage;
	bool res = false;
	if(drive == DRIVE_SD){
		res = sd_mount() || sd_initialize(false);
	}else{
		res = emmc_mount() || emmc_initialize(false);
	}

	if (!res)
	{
		lv_obj_t *lbl = lv_label_create(h1, NULL);
		lv_label_set_recolor(lbl, true);
		lv_label_set_text(lbl, drive == DRIVE_SD ? "#FFDD00 Failed to init SD!#" : "#FFDD00 Failed to init eMMC!#");
		return LV_RES_OK;
	}

	memset(&part_info, 0, sizeof(partition_ctxt_t));
	part_info.drive = drive;
	_create_mbox_check_files_total_size(drive);

	char *txt_buf = malloc(SZ_8K);

	part_info.total_sct = storage->sec_cnt;
	// reserve 16mb for alignment + last 1mb for backup gpt 
	part_info.total_sct_available = ALIGN_DOWN(part_info.total_sct - AU_ALIGN_SECTORS - (1 << 11), AU_ALIGN_SECTORS);
	part_info.alignment = part_info.total_sct - part_info.total_sct_available;

	// Reserved 16MB for alignment
	u32 extra_sct = 0;
	if(drive == DRIVE_SD){
		 // On SD, also Reserve 2GB for FAT partition
		 extra_sct += 0x400000; 
	}

	// Set initial HOS partition size, so the correct cluster size can be selected.
	if(drive == DRIVE_SD){
		part_info.hos_size = (part_info.total_sct_available >> 11); // Important if there's no slider change.
	}else{
		// On eMMC, default is to not have a FAT32 partition
		part_info.hos_size = 0;
	}


	// Check if eMMC should be 64GB (Aula).
	part_info.emmc_is_64gb = fuse_read_hw_type() == FUSE_NX_HW_TYPE_AULA;
	part_info.auto_assign_free_storage = drive == DRIVE_SD ? true : false;

	part_info.hos_min_size_mb = HOS_MIN_SIZE_MB;

	// Read current MBR.
	mbr_t mbr = { 0 };
	gpt_t *gpt = NULL;
	bool has_gpt = false;
	bool has_hos_os = false;
	sdmmc_storage_read(storage, 0, 1, &mbr);
	has_gpt = _has_gpt(&mbr);
	if(has_gpt){
		gpt = zalloc(sizeof(*gpt));
		sdmmc_storage_read(storage, 1, sizeof(*gpt) >> 9, gpt);

		if(gpt->header.num_part_ents >= 10 && drive == DRIVE_EMMC){
			if(!memcmp(gpt->entries[10].name, (char[]){'U', 0, 'S', 0, 'E', 0, 'R', 0}, 8)){
				// Found HOS USER partition
				has_hos_os = true;
				// system only size (excl. user)
				// part_info.hos_sys_size_mb = (gpt->entries[10].lba_start - gpt->entries[0].lba_start) >> 11;

				// We assume first partition is 16mb aligned and starts at 0x800
				part_info.hos_sys_size_mb = (gpt->entries[10].lba_start - 0x800) >> 11;

				part_info.hos_os_size = (gpt->entries[10].lba_end - 0x800 + 1) >> 11;
				// original hos size
				part_info.hos_os_og_size = part_info.hos_os_size;

				part_info.hos_os_align = ALIGN(part_info.hos_os_size, AU_ALIGN_SECTORS >> 11) - part_info.hos_os_size;

				part_info.hos_os_size += part_info.hos_os_align;
			}
		}
	}


	// account for alignment + 1mb for backup gpt
	u32 total_size         = part_info.total_sct_available / SECTORS_PER_GB;
	u32 bar_emu_sd_size    = lv_obj_get_width(h1) * (part_info.emu_sd_size >> 10) / total_size;
	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;
	u32 bar_hos_os_size    = lv_obj_get_width(h1) * (part_info.hos_os_size >> 10) / total_size;
	u32 bar_remaining_size = lv_obj_get_width(h1) - (bar_hos_size + bar_hos_os_size + bar_emu_size + bar_l4t_size + bar_and_size);
	bar_remaining_size = bar_remaining_size <= 7 ? 0 : bar_remaining_size;

	lv_obj_t *lbl = lv_label_create(h1, NULL);
	lv_label_set_recolor(lbl, true);
	lv_label_set_text(lbl, "Choose #FFDD00 new# partition layout:");

	// Create disk layout blocks.
	// HOS OS partition block
	lv_obj_t *bar_hos_os = lv_bar_create(h1, NULL);
	lv_obj_set_size(bar_hos_os, bar_hos_os_size, LV_DPI / 2);
	lv_bar_set_range(bar_hos_os, 0, 1);
	lv_bar_set_value(bar_hos_os, 1);
	lv_bar_set_style(bar_hos_os, LV_BAR_STYLE_INDIC, &bar_hos_os_ind);
	lv_obj_align(bar_hos_os, lbl, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 6);
	part_info.bar_hos_os = bar_hos_os;

	// HOS partition block.
	lv_obj_t *bar_hos = lv_bar_create(h1, bar_hos_os);
	lv_obj_set_size(bar_hos, bar_hos_size, LV_DPI / 2);
	lv_bar_set_style(bar_hos, LV_BAR_STYLE_INDIC, &bar_hos_ind);
	lv_obj_align(bar_hos, bar_hos_os, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
	part_info.bar_hos = bar_hos;

	// emuMMC partition block.
	lv_obj_t *bar_emu = lv_bar_create(h1, bar_hos_os);
	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;

	// emuSD partition block
	lv_obj_t *bar_emu_sd = lv_bar_create(h1, bar_hos_os);
	lv_obj_set_size(bar_emu_sd, bar_emu_sd_size, LV_DPI / 2);
	lv_bar_set_style(bar_emu_sd, LV_BAR_STYLE_INDIC, &bar_emu_sd_ind);
	lv_obj_align(bar_emu_sd, bar_emu, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
	part_info.bar_emu_sd = bar_emu_sd;

	// L4T partition block.
	lv_obj_t *bar_l4t = lv_bar_create(h1, bar_hos_os);
	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_sd, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
	part_info.bar_l4t = bar_l4t;

	// Android partition block.
	lv_obj_t *bar_and = lv_bar_create(h1, bar_hos_os);
	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;

	// Remaining space
	lv_obj_t *bar_remaining = lv_bar_create(h1, bar_hos_os);
	lv_obj_set_size(bar_remaining, bar_remaining_size, LV_DPI / 2);
	lv_bar_set_style(bar_remaining, LV_BAR_STYLE_INDIC, &bar_remaining_ind);
	lv_obj_align(bar_remaining, bar_and, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
	part_info.bar_remaining = bar_remaining;

	// -------------------------------------------------------------------------
	// Create disk layout blending separators.
	lv_obj_t *sep_hos_os = lv_cont_create(h1, NULL);
	lv_cont_set_fit(sep_hos_os, false, false);
	lv_obj_set_size(sep_hos_os, 0, LV_DPI / 2);
	lv_obj_set_style(sep_hos_os, &sep_hos_os_bg);
	lv_obj_align(sep_hos_os, bar_hos_os, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
	part_info.sep_hos_os = sep_hos_os;

	lv_obj_t *sep_hos = lv_cont_create(h1, NULL);
	lv_cont_set_fit(sep_hos, false, false);
	lv_obj_set_size(sep_hos, 0, LV_DPI / 2);
	lv_obj_set_style(sep_hos, &sep_hos_bg);
	lv_obj_align(sep_hos, bar_hos, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
	part_info.sep_hos = sep_hos;

	lv_obj_t *sep_emu_sd = lv_cont_create(h1, sep_hos);
	lv_obj_set_style(sep_emu_sd, &sep_emu_sd_bg);
	lv_obj_align(sep_emu_sd, bar_emu_sd, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
	part_info.sep_emu_sd = sep_emu_sd;

	lv_obj_t *sep_emu = lv_cont_create(h1, sep_hos);
	lv_obj_set_style(sep_emu, &sep_emu_bg);
	lv_obj_align(sep_emu, bar_emu, 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_l4t, 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_and, LV_ALIGN_OUT_RIGHT_MID, -4, 0);
	part_info.sep_and = sep_and;

	// Create slider type labels.
	lv_obj_t *lbl_hos_os = lv_label_create(h1, NULL);
	lv_label_set_recolor(lbl_hos_os, true);
	lv_label_set_static_text(lbl_hos_os, "#FFD300 "SYMBOL_DOT" HOS (USER):#");
	lv_obj_align(lbl_hos_os, bar_hos_os, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 2);
	lv_obj_set_hidden(lbl_hos_os, !has_hos_os);

	lv_coord_t spacing;
	if(drive == DRIVE_EMMC){
		if(has_hos_os){
			// adjust spacing when we have 5 sliders
			spacing = (LV_DPI / 3) - (lv_obj_get_height(lbl_hos_os) + LV_DPI / 3) / 3;
		}else{
			spacing = (LV_DPI / 3) - (lv_obj_get_height(lbl_hos_os) + LV_DPI / 3) / 4;
		}
	}else{
		spacing = LV_DPI / 3;
	}
	
	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, has_hos_os ? lbl_hos_os : bar_hos_os, LV_ALIGN_OUT_BOTTOM_LEFT, 0, has_hos_os ? spacing : 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, spacing);

	lv_obj_t *lbl_emu_sd = lv_label_create(	h1, lbl_hos);
	lv_label_set_static_text(lbl_emu_sd, "#FF00D6 " SYMBOL_DOT " emuSD:#");
	lv_obj_align(lbl_emu_sd, lbl_emu, LV_ALIGN_OUT_BOTTOM_LEFT, 0, spacing);
	lv_obj_set_hidden(lbl_emu_sd, drive == DRIVE_SD);

	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, drive == DRIVE_SD ? lbl_emu : lbl_emu_sd, LV_ALIGN_OUT_BOTTOM_LEFT, 0, spacing);

	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, spacing);


	// Create HOS OS size slider
	lv_obj_t *slider_hos_os = lv_slider_create(h1, NULL);
	lv_obj_set_size(slider_hos_os, LV_DPI * 7, LV_DPI / 3);
	lv_slider_set_range(slider_hos_os, 0, (part_info.total_sct_available - extra_sct - (part_info.hos_sys_size_mb << 10)) / SECTORS_PER_GB);
	lv_slider_set_value(slider_hos_os, (part_info.hos_os_size - part_info.hos_sys_size_mb) >> 10);
	lv_slider_set_style(slider_hos_os, LV_SLIDER_STYLE_BG, &bar_hos_os_bg);
	lv_slider_set_style(slider_hos_os, LV_SLIDER_STYLE_INDIC, &bar_hos_os_ind);
	lv_slider_set_style(slider_hos_os, LV_SLIDER_STYLE_KNOB, &bar_hos_os_btn);
	lv_obj_align(slider_hos_os, lbl_hos_os, LV_ALIGN_IN_LEFT_MID, LV_DPI * 3, 0);
	lv_slider_set_action(slider_hos_os, _action_slider_hos_os);
	lv_obj_set_hidden(slider_hos_os, !has_hos_os);
	part_info.slider_hos_os = slider_hos_os;

	// Create HOS size slider. Non-interactive.
	lv_obj_t *slider_hos;
	if(drive == DRIVE_EMMC){
		// Allow adjustment of fat partition on emmc
		slider_hos = lv_slider_create(h1, NULL);
		lv_obj_set_size(slider_hos, LV_DPI * 7, LV_DPI / 3);
		lv_slider_set_style(slider_hos, LV_SLIDER_STYLE_KNOB, &bar_hos_btn);
		lv_slider_set_action(slider_hos, _action_slider_hos);
	}else{
		slider_hos = lv_bar_create(h1, NULL);
		lv_obj_set_size(slider_hos, LV_DPI * 7, LV_DPI * 3 / 17);
	}
	lv_bar_set_range(slider_hos, 0, (part_info.total_sct_available - extra_sct) / SECTORS_PER_GB);
	lv_bar_set_value(slider_hos, part_info.hos_size >> 10);
	lv_bar_set_style(slider_hos, LV_SLIDER_STYLE_BG, &bar_hos_bg);
	lv_bar_set_style(slider_hos, LV_SLIDER_STYLE_INDIC, &bar_hos_ind);
	lv_obj_align(slider_hos, lbl_hos, LV_ALIGN_IN_LEFT_MID, LV_DPI * 3, 0);

	part_info.slider_bar_hos = slider_hos;

	// Create emuMMC size slider.
	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, ((part_info.total_sct_available - extra_sct) / SECTORS_PER_GB) * 2);
	lv_slider_set_value(slider_emu, part_info.emu_size >> 10);
	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, lbl_emu, LV_ALIGN_IN_LEFT_MID, LV_DPI * 3,0);
	lv_slider_set_action(slider_emu, _action_slider_emu);
	part_info.slider_emu = slider_hos;

	// Create L4T size slider.
	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_available - extra_sct) / SECTORS_PER_GB);
	lv_slider_set_value(slider_l4t, part_info.l4t_size >> 10);
	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, lbl_l4t, LV_ALIGN_IN_LEFT_MID, LV_DPI * 3, 0);
	lv_slider_set_action(slider_l4t, _action_slider_l4t);
	part_info.slider_l4t = slider_l4t;

	// Create Android size slider.
	lv_obj_t *slider_and = lv_slider_create(h1, NULL);
	lv_obj_set_size(slider_and, LV_DPI * 7, LV_DPI / 3);
	#ifdef ENABLE_DUAL_ANDROID
	lv_slider_set_range(slider_and, 0, ((part_info.total_sct_available - extra_sct) / SECTORS_PER_GB - (ANDROID_SYSTEM_SIZE_MB / 1024)) * 2); // Subtract android reserved size.
	#else
	lv_slider_set_range(slider_and, 0, (part_info.total_sct_available - extra_sct) / SECTORS_PER_GB - (ANDROID_SYSTEM_SIZE_MB / 1024)); // Subtract android reserved size.
	#endif
	lv_slider_set_value(slider_and, part_info.and_size >> 10);
	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, lbl_and, LV_ALIGN_IN_LEFT_MID, LV_DPI * 3, 0);
	lv_slider_set_action(slider_and, _action_slider_and);
	part_info.slider_and = slider_and;

	// Create emuSD size slider
	lv_obj_t *slider_emu_sd = lv_slider_create(h1, NULL);
	lv_obj_set_size(slider_emu_sd, LV_DPI * 7, LV_DPI / 3);
	lv_slider_set_range(slider_emu_sd, 0, ((part_info.total_sct_available - extra_sct) / SECTORS_PER_GB) * 2);
	lv_slider_set_value(slider_emu_sd, part_info.emu_sd_size >> 10);
	lv_slider_set_style(slider_emu_sd, LV_SLIDER_STYLE_BG, &bar_emu_sd_bg);
	lv_slider_set_style(slider_emu_sd, LV_SLIDER_STYLE_INDIC, &bar_emu_sd_ind);
	lv_slider_set_style(slider_emu_sd, LV_SLIDER_STYLE_KNOB, &bar_emu_sd_btn);
	lv_obj_align(slider_emu_sd, lbl_emu_sd, LV_ALIGN_IN_LEFT_MID, LV_DPI * 3, 0);
	lv_slider_set_action(slider_emu_sd, _action_slider_emu_sd);
	lv_obj_set_hidden(slider_emu_sd, drive == DRIVE_SD);
	part_info.slider_emu_sd = slider_emu_sd;

	// Create HOS OS size lable
	lv_obj_t *lbl_sl_hos_os = lv_label_create(h1, NULL);
	lv_label_set_recolor(lbl_sl_hos_os, true);
	if(part_info.hos_os_size - part_info.hos_os_align == part_info.hos_os_og_size){
		s_printf(txt_buf, "#E6BF00 %d FULL#", (part_info.hos_os_size - part_info.hos_sys_size_mb) >> 10);
	}else{
		s_printf(txt_buf, "#E6BF00 %d GiB#", (part_info.hos_os_size - part_info.hos_sys_size_mb) >> 10);
	}
	lv_label_set_text(lbl_sl_hos_os, txt_buf);
	lv_obj_align(lbl_sl_hos_os, slider_hos_os, LV_ALIGN_OUT_RIGHT_MID, LV_DPI * 4 / 7, 0);
	lv_obj_set_hidden(lbl_sl_hos_os, !has_hos_os);
	part_info.lbl_hos_os = lbl_sl_hos_os;

	// Create HOS size label.
	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.hos_size >> 10);
	lv_label_set_text(lbl_sl_hos, txt_buf);
	lv_obj_align(lbl_sl_hos, slider_hos, LV_ALIGN_OUT_RIGHT_MID, LV_DPI * 4 / 7, 0);
	part_info.lbl_hos = lbl_sl_hos;

	// Create emuMMC size label.
	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, slider_emu, LV_ALIGN_OUT_RIGHT_MID, LV_DPI * 4 / 7, 0);
	part_info.lbl_emu = lbl_sl_emu;

	// Create L4T size label.
	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, slider_l4t, LV_ALIGN_OUT_RIGHT_MID, LV_DPI * 4 / 7, 0);
	part_info.lbl_l4t = lbl_sl_l4t;

	// Create Android size label.
	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, slider_and, LV_ALIGN_OUT_RIGHT_MID, LV_DPI * 4 / 7, 0);
	part_info.lbl_and = lbl_sl_and;

	// Create emuSD size label
	lv_obj_t *lbl_sl_emu_sd = lv_label_create(h1, lbl_sl_hos);
	lv_label_set_text(lbl_sl_emu_sd, "#FF00D6 0 GiB#");
	lv_obj_align(lbl_sl_emu_sd, slider_emu_sd, LV_ALIGN_OUT_RIGHT_MID, LV_DPI * 4 / 7, 0);
	lv_obj_set_hidden(lbl_sl_emu_sd, drive == DRIVE_SD);
	part_info.lbl_emu_sd = lbl_sl_emu_sd;

	// Set partition manager notes.
	const char *sd_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: Resized emuMMC formats the USER partition. A save data manager can be used to move them over.\n"
		"Note 3: The #C7EA46 Flash Linux# and #C7EA46 Flash Android# will flash files if suitable partitions and installer files are found.\n";

	const char *emmc_notes =
		"Note 1: Resizing the #C7EA46 HOS USER# partition will format it, setting it to 0 will #C7EA46 remove# HOS from eMMC.\n"
		"Note 2: Resized emuMMC formats the USER partition. A save data manager can be used to move them over.\n"
		"Note 3: The #C7EA46 Flash Linux# and #C7EA46 Flash Android# will flash files if suitable partitions and installer files are found.\n"
		"Note 4: When creating a #C7EA46 FAT32# partition, all unused storage will be assigned to it.\n";

	lv_obj_t *lbl_notes = lv_label_create(h1, NULL);
	lv_label_set_recolor(lbl_notes, true);
	lv_label_set_static_text(lbl_notes, drive == DRIVE_SD ? sd_notes : emmc_notes);
	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);

	// Create UMS button.
	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, drive == DRIVE_SD ? SYMBOL_USB"  SD UMS" : SYMBOL_CHIP "  eMMC UMS");
	lv_obj_align(btn1, h1, LV_ALIGN_IN_TOP_LEFT, 0, LV_DPI * 5);
	lv_btn_set_action(btn1, LV_BTN_ACTION_CLICK, drive == DRIVE_SD ? _action_part_manager_ums_sd : _action_part_manager_ums_emmc);

	// Create Flash Linux button.
	btn_flash_l4t = lv_btn_create(h1, NULL);
	lv_obj_t *label_btn2 = lv_label_create(btn_flash_l4t, NULL);
	lv_btn_set_fit(btn_flash_l4t, true, true);
	lv_label_set_static_text(label_btn2, SYMBOL_DOWNLOAD"  Flash Linux");
	lv_obj_align(btn_flash_l4t, btn1, LV_ALIGN_OUT_RIGHT_MID, LV_DPI / 3, 0);
	lv_btn_set_action(btn_flash_l4t, LV_BTN_ACTION_CLICK, _action_check_flash_linux);

	// Disable Flash Linux button if partition not found.
	u32 size_sct = _get_available_l4t_partition();
	if (!l4t_flash_ctxt.offset_sct || size_sct < 0x800000)
	{
		lv_obj_set_click(btn_flash_l4t, false);
		lv_btn_set_state(btn_flash_l4t, LV_BTN_STATE_INA);
	}

	// TODO: check for multiple android slots, if multiple, add another mbox to first select which slot to use
	int part_type_and = _get_available_android_partition();

	// Create Flash Android button.
	btn_flash_android = lv_btn_create(h1, NULL);
	label_btn = lv_label_create(btn_flash_android, NULL);
	lv_btn_set_fit(btn_flash_android, true, true);
	switch (part_type_and)
	{
	case 0: // Disable Flash Android button if partition not found.
		lv_label_set_static_text(label_btn, SYMBOL_DOWNLOAD"  Flash Android");
		lv_obj_set_click(btn_flash_android, false);
		lv_btn_set_state(btn_flash_android, LV_BTN_STATE_INA);
		break;
	case 1: // Android 10/11.
		lv_label_set_static_text(label_btn, SYMBOL_DOWNLOAD"  Flash Android 10/11");
		break;
	case 2: // Android 13+
		lv_label_set_static_text(label_btn, SYMBOL_DOWNLOAD"  Flash Android 13+");
		break;
	}
	lv_obj_align(btn_flash_android, btn_flash_l4t, LV_ALIGN_OUT_RIGHT_MID, LV_DPI / 3, 0);
	lv_btn_set_action(btn_flash_android, LV_BTN_ACTION_CLICK, _action_flash_android_slot_select);

	// Create next step button.
	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);

	free(txt_buf);
	free(gpt);

	_update_partition_bar();

	return LV_RES_OK;
}
