hekate/nyx/nyx_gui/frontend/gui_tools.c

1690 lines
50 KiB
C
Raw Normal View History

/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2021 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 <stdlib.h>
#include "gui.h"
#include "gui_tools.h"
#include "gui_tools_partition_manager.h"
#include "gui_emmc_tools.h"
#include "fe_emummc_tools.h"
#include <memory_map.h>
#include "../config.h"
#include <display/di.h>
#include "../hos/pkg1.h"
#include "../hos/pkg2.h"
#include "../hos/hos.h"
#include <input/touch.h>
#include <libs/fatfs/ff.h>
#include <mem/heap.h>
#include <mem/minerva.h>
#include <sec/se.h>
#include <soc/bpmp.h>
#include <soc/fuse.h>
#include "../storage/nx_emmc.h"
#include <storage/nx_sd.h>
#include <storage/sdmmc.h>
#include <usb/usbd.h>
#include <utils/btn.h>
#include <utils/sprintf.h>
#include <utils/util.h>
extern volatile boot_cfg_t *b_cfg;
extern hekate_config h_cfg;
extern nyx_config n_cfg;
extern char *emmcsn_path_impl(char *path, char *sub_dir, char *filename, sdmmc_storage_t *storage);
static lv_obj_t *_create_container(lv_obj_t *parent)
{
static lv_style_t h_style;
lv_style_copy(&h_style, &lv_style_transp);
h_style.body.padding.inner = 0;
h_style.body.padding.hor = LV_DPI - (LV_DPI / 4);
h_style.body.padding.ver = LV_DPI / 6;
lv_obj_t *h1 = lv_cont_create(parent, NULL);
lv_cont_set_style(h1, &h_style);
lv_cont_set_fit(h1, false, true);
lv_obj_set_width(h1, (LV_HOR_RES / 9) * 4);
lv_obj_set_click(h1, false);
lv_cont_set_layout(h1, LV_LAYOUT_OFF);
return h1;
}
bool get_autorcm_status(bool change)
{
2020-12-26 14:38:21 +00:00
u8 corr_mod0, mod1;
bool enabled = false;
if (h_cfg.t210b01)
return false;
sdmmc_storage_init_mmc(&emmc_storage, &emmc_sdmmc, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_HS400);
u8 *tempbuf = (u8 *)malloc(0x200);
sdmmc_storage_set_mmc_partition(&emmc_storage, EMMC_BOOT0);
sdmmc_storage_read(&emmc_storage, 0x200 / NX_EMMC_BLOCKSIZE, 1, tempbuf);
2020-12-26 14:38:21 +00:00
// Get the correct RSA modulus byte masks.
nx_emmc_get_autorcm_masks(&corr_mod0, &mod1);
2020-12-26 14:38:21 +00:00
// Check if 2nd byte of modulus is correct.
if (tempbuf[0x11] != mod1)
goto out;
if (tempbuf[0x10] != corr_mod0)
enabled = true;
// Change autorcm status if requested.
if (change)
{
int i, sect = 0;
2020-12-26 14:38:21 +00:00
// Iterate BCTs.
for (i = 0; i < 4; i++)
{
sect = (0x200 + (0x4000 * i)) / NX_EMMC_BLOCKSIZE;
sdmmc_storage_read(&emmc_storage, sect, 1, tempbuf);
if (!enabled)
2020-12-26 14:38:21 +00:00
tempbuf[0x10] = 0;
else
2020-12-26 14:38:21 +00:00
tempbuf[0x10] = corr_mod0;
sdmmc_storage_write(&emmc_storage, sect, 1, tempbuf);
}
enabled = !(enabled);
}
2020-12-26 14:38:21 +00:00
out:
free(tempbuf);
sdmmc_storage_end(&emmc_storage);
return enabled;
}
static lv_res_t _create_mbox_autorcm_status(lv_obj_t *btn)
{
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
lv_obj_set_style(dark_bg, &mbox_darken);
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
static const char * mbox_btn_map[] = { "\211", "\222OK", "\211", "" };
lv_obj_t * mbox = lv_mbox_create(dark_bg, NULL);
lv_mbox_set_recolor_text(mbox, true);
bool enabled = get_autorcm_status(true);
if (enabled)
{
lv_mbox_set_text(mbox,
"AutoRCM is now #C7EA46 ENABLED!#\n\n"
"You can now automatically enter RCM by only pressing #FF8000 POWER#.\n"
"Use the AutoRCM button here again if you want to remove it later on.");
}
else
{
lv_mbox_set_text(mbox,
"AutoRCM is now #FF8000 DISABLED!#\n\n"
2019-07-06 19:08:37 +00:00
"The boot process is now normal and you need the #FF8000 VOL+# + #FF8000 HOME# (jig) combo to enter RCM.\n");
}
lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action);
lv_obj_set_width(mbox, LV_HOR_RES / 9 * 5);
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_top(mbox, true);
if (enabled)
lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
else
lv_btn_set_state(btn, LV_BTN_STATE_REL);
nyx_generic_onoff_toggle(btn);
return LV_RES_OK;
}
static lv_res_t _create_mbox_hid(usb_ctxt_t *usbs)
{
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
lv_obj_set_style(dark_bg, &mbox_darken);
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
static const char *mbox_btn_map[] = { "\211", "\262Close", "\211", "" };
static const char *mbox_btn_map2[] = { "\211", "\222Close", "\211", "" };
lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
lv_mbox_set_recolor_text(mbox, true);
char *txt_buf = malloc(SZ_4K);
2020-07-14 19:26:40 +00:00
s_printf(txt_buf, "#FF8000 HID Emulation#\n\n#C7EA46 Device:# ");
if (usbs->type == USB_HID_GAMEPAD)
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Gamepad");
else
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Touchpad");
2020-07-14 19:26:40 +00:00
lv_mbox_set_text(mbox, txt_buf);
free(txt_buf);
lv_obj_t *lbl_status = lv_label_create(mbox, NULL);
lv_label_set_recolor(lbl_status, true);
lv_label_set_text(lbl_status, " ");
usbs->label = (void *)lbl_status;
lv_obj_t *lbl_tip = lv_label_create(mbox, NULL);
lv_label_set_recolor(lbl_tip, true);
lv_label_set_static_text(lbl_tip, "Note: To end it, press #C7EA46 L3# + #C7EA46 HOME# or remove the cable.");
lv_obj_set_style(lbl_tip, &hint_small_style);
lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action);
lv_obj_set_width(mbox, LV_HOR_RES / 9 * 5);
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_top(mbox, true);
usb_device_gadget_hid(usbs);
lv_mbox_add_btns(mbox, mbox_btn_map2, mbox_action);
return LV_RES_OK;
}
static lv_res_t _create_mbox_ums(usb_ctxt_t *usbs)
{
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
lv_obj_set_style(dark_bg, &mbox_darken);
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
static const char *mbox_btn_map[] = { "\211", "\262Close", "\211", "" };
static const char *mbox_btn_map2[] = { "\211", "\222Close", "\211", "" };
lv_obj_t *mbox = lv_mbox_create(dark_bg, NULL);
lv_mbox_set_recolor_text(mbox, true);
char *txt_buf = malloc(SZ_4K);
2020-07-14 19:26:40 +00:00
s_printf(txt_buf, "#FF8000 USB Mass Storage#\n\n#C7EA46 Device:# ");
if (usbs->type == MMC_SD)
{
switch (usbs->partition)
{
case 0:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "SD Card");
break;
case EMMC_GPP + 1:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "emuMMC GPP");
break;
case EMMC_BOOT0 + 1:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "emuMMC BOOT0");
break;
case EMMC_BOOT1 + 1:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "emuMMC BOOT1");
break;
}
}
else
{
switch (usbs->partition)
{
case EMMC_GPP + 1:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "eMMC GPP");
break;
case EMMC_BOOT0 + 1:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "eMMC BOOT0");
break;
case EMMC_BOOT1 + 1:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "eMMC BOOT1");
break;
}
}
2020-07-14 19:26:40 +00:00
lv_mbox_set_text(mbox, txt_buf);
free(txt_buf);
lv_obj_t *lbl_status = lv_label_create(mbox, NULL);
lv_label_set_recolor(lbl_status, true);
lv_label_set_text(lbl_status, " ");
usbs->label = (void *)lbl_status;
lv_obj_t *lbl_tip = lv_label_create(mbox, NULL);
lv_label_set_recolor(lbl_tip, true);
if (!usbs->ro)
{
if (usbs->type == MMC_SD)
{
lv_label_set_static_text(lbl_tip,
"Note: To end it, #C7EA46 safely eject# from inside the OS.\n"
" #FFDD00 DO NOT remove the cable!#");
}
else
{
lv_label_set_static_text(lbl_tip,
"Note: To end it, #C7EA46 safely eject# from inside the OS.\n"
" #FFDD00 If it's not mounted, you might need to remove the cable!#");
}
}
else
{
lv_label_set_static_text(lbl_tip,
"Note: To end it, #C7EA46 safely eject# from inside the OS\n"
" or by removing the cable!#");
}
lv_obj_set_style(lbl_tip, &hint_small_style);
lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action);
lv_obj_set_width(mbox, LV_HOR_RES / 9 * 5);
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_top(mbox, true);
// Dim backlight.
display_backlight_brightness(20, 1000);
usb_device_gadget_ums(usbs);
// Restore backlight.
display_backlight_brightness(h_cfg.backlight - 20, 1000);
lv_mbox_add_btns(mbox, mbox_btn_map2, mbox_action);
ums_mbox = dark_bg;
return LV_RES_OK;
}
static lv_res_t _create_mbox_ums_error(int error)
{
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
lv_obj_set_style(dark_bg, &mbox_darken);
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
static const char *mbox_btn_map[] = { "\211", "\222OK", "\211", "" };
lv_obj_t * mbox = lv_mbox_create(dark_bg, NULL);
lv_mbox_set_recolor_text(mbox, true);
switch (error)
{
case 1:
lv_mbox_set_text(mbox, "#FF8000 USB Mass Storage#\n\n#FFFF00 Error mounting SD Card!#");
break;
case 2:
lv_mbox_set_text(mbox, "#FF8000 USB Mass Storage#\n\n#FFFF00 No emuMMC found active!#");
break;
case 3:
lv_mbox_set_text(mbox, "#FF8000 USB Mass Storage#\n\n#FFFF00 Active emuMMC is not partition based!#");
break;
}
lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action);
lv_obj_set_width(mbox, LV_HOR_RES / 9 * 5);
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_top(mbox, true);
return LV_RES_OK;
}
static void usb_gadget_set_text(void *lbl, const char *text)
{
lv_label_set_text((lv_obj_t *)lbl, text);
manual_system_maintenance(true);
}
static lv_res_t _action_hid_jc(lv_obj_t *btn)
{
// Reduce BPMP, RAM and backlight and power off SDMMC1 to conserve power.
sd_end();
minerva_change_freq(FREQ_800);
bpmp_freq_t prev_fid = bpmp_clk_rate_set(BPMP_CLK_NORMAL);
display_backlight_brightness(10, 1000);
usb_ctxt_t usbs;
usbs.type = USB_HID_GAMEPAD;
usbs.system_maintenance = &manual_system_maintenance;
usbs.set_text = &usb_gadget_set_text;
_create_mbox_hid(&usbs);
// Restore BPMP, RAM and backlight.
minerva_change_freq(FREQ_1600);
bpmp_clk_rate_set(prev_fid);
display_backlight_brightness(h_cfg.backlight - 20, 1000);
return LV_RES_OK;
}
/*
static lv_res_t _action_hid_touch(lv_obj_t *btn)
{
// Reduce BPMP, RAM and backlight and power off SDMMC1 to conserve power.
sd_end();
minerva_change_freq(FREQ_800);
bpmp_freq_t prev_fid = bpmp_clk_rate_set(BPMP_CLK_NORMAL);
display_backlight_brightness(10, 1000);
usb_ctxt_t usbs;
usbs.type = USB_HID_TOUCHPAD;
usbs.system_maintenance = &manual_system_maintenance;
usbs.set_text = &usb_gadget_set_text;
_create_mbox_hid(&usbs);
// Restore BPMP, RAM and backlight.
minerva_change_freq(FREQ_1600);
bpmp_clk_rate_set(prev_fid);
display_backlight_brightness(h_cfg.backlight - 20, 1000);
return LV_RES_OK;
}
*/
static bool usb_msc_emmc_read_only;
lv_res_t action_ums_sd(lv_obj_t *btn)
{
usb_ctxt_t usbs;
usbs.type = MMC_SD;
usbs.partition = 0;
usbs.offset = 0;
usbs.sectors = 0;
usbs.ro = 0;
usbs.system_maintenance = &manual_system_maintenance;
usbs.set_text = &usb_gadget_set_text;
_create_mbox_ums(&usbs);
return LV_RES_OK;
}
static lv_res_t _action_ums_emmc_boot0(lv_obj_t *btn)
{
if (!nyx_emmc_check_battery_enough())
return LV_RES_OK;
usb_ctxt_t usbs;
usbs.type = MMC_EMMC;
usbs.partition = EMMC_BOOT0 + 1;
usbs.offset = 0;
usbs.sectors = 0x2000;
usbs.ro = usb_msc_emmc_read_only;
usbs.system_maintenance = &manual_system_maintenance;
usbs.set_text = &usb_gadget_set_text;
_create_mbox_ums(&usbs);
return LV_RES_OK;
}
static lv_res_t _action_ums_emmc_boot1(lv_obj_t *btn)
{
if (!nyx_emmc_check_battery_enough())
return LV_RES_OK;
usb_ctxt_t usbs;
usbs.type = MMC_EMMC;
usbs.partition = EMMC_BOOT1 + 1;
usbs.offset = 0;
usbs.sectors = 0x2000;
usbs.ro = usb_msc_emmc_read_only;
usbs.system_maintenance = &manual_system_maintenance;
usbs.set_text = &usb_gadget_set_text;
_create_mbox_ums(&usbs);
return LV_RES_OK;
}
static lv_res_t _action_ums_emmc_gpp(lv_obj_t *btn)
{
if (!nyx_emmc_check_battery_enough())
return LV_RES_OK;
usb_ctxt_t usbs;
usbs.type = MMC_EMMC;
usbs.partition = EMMC_GPP + 1;
usbs.offset = 0;
usbs.sectors = 0;
usbs.ro = usb_msc_emmc_read_only;
usbs.system_maintenance = &manual_system_maintenance;
usbs.set_text = &usb_gadget_set_text;
_create_mbox_ums(&usbs);
return LV_RES_OK;
}
static lv_res_t _action_ums_emuemmc_boot0(lv_obj_t *btn)
{
if (!nyx_emmc_check_battery_enough())
return LV_RES_OK;
usb_ctxt_t usbs;
int error = !sd_mount();
if (!error)
{
emummc_cfg_t emu_info;
load_emummc_cfg(&emu_info);
error = 2;
if (emu_info.enabled)
{
error = 3;
if (emu_info.sector)
{
error = 0;
usbs.offset = emu_info.sector;
}
}
}
sd_unmount();
if (error)
_create_mbox_ums_error(error);
else
{
usbs.type = MMC_SD;
usbs.partition = EMMC_BOOT0 + 1;
usbs.sectors = 0x2000;
usbs.ro = usb_msc_emmc_read_only;
usbs.system_maintenance = &manual_system_maintenance;
usbs.set_text = &usb_gadget_set_text;
_create_mbox_ums(&usbs);
}
return LV_RES_OK;
}
static lv_res_t _action_ums_emuemmc_boot1(lv_obj_t *btn)
{
if (!nyx_emmc_check_battery_enough())
return LV_RES_OK;
usb_ctxt_t usbs;
int error = !sd_mount();
if (!error)
{
emummc_cfg_t emu_info;
load_emummc_cfg(&emu_info);
error = 2;
if (emu_info.enabled)
{
error = 3;
if (emu_info.sector)
{
error = 0;
usbs.offset = emu_info.sector + 0x2000;
}
}
}
sd_unmount();
if (error)
_create_mbox_ums_error(error);
else
{
usbs.type = MMC_SD;
usbs.partition = EMMC_BOOT1 + 1;
usbs.sectors = 0x2000;
usbs.ro = usb_msc_emmc_read_only;
usbs.system_maintenance = &manual_system_maintenance;
usbs.set_text = &usb_gadget_set_text;
_create_mbox_ums(&usbs);
}
return LV_RES_OK;
}
static lv_res_t _action_ums_emuemmc_gpp(lv_obj_t *btn)
{
if (!nyx_emmc_check_battery_enough())
return LV_RES_OK;
usb_ctxt_t usbs;
int error = !sd_mount();
if (!error)
{
emummc_cfg_t emu_info;
load_emummc_cfg(&emu_info);
error = 2;
if (emu_info.enabled)
{
error = 3;
if (emu_info.sector)
{
error = 1;
usbs.offset = emu_info.sector + 0x4000;
u8 *gpt = malloc(512);
if (sdmmc_storage_read(&sd_storage, usbs.offset + 1, 1, gpt))
{
if (!memcmp(gpt, "EFI PART", 8))
{
error = 0;
usbs.sectors = *(u32 *)(gpt + 0x20) + 1; // Backup LBA + 1.
}
}
}
}
}
sd_unmount();
if (error)
_create_mbox_ums_error(error);
else
{
usbs.type = MMC_SD;
usbs.partition = EMMC_GPP + 1;
usbs.ro = usb_msc_emmc_read_only;
usbs.system_maintenance = &manual_system_maintenance;
usbs.set_text = &usb_gadget_set_text;
_create_mbox_ums(&usbs);
}
return LV_RES_OK;
}
void nyx_run_ums(void *param)
{
u32 *cfg = (u32 *)param;
u8 type = (*cfg) >> 24;
2021-02-06 01:55:01 +00:00
*cfg = *cfg & (~NYX_CFG_EXTRA);
// Disable read only flag.
usb_msc_emmc_read_only = false;
switch (type)
{
case NYX_UMS_SD_CARD:
action_ums_sd(NULL);
break;
case NYX_UMS_EMMC_BOOT0:
_action_ums_emmc_boot0(NULL);
break;
case NYX_UMS_EMMC_BOOT1:
_action_ums_emmc_boot1(NULL);
break;
case NYX_UMS_EMMC_GPP:
_action_ums_emmc_gpp(NULL);
break;
case NYX_UMS_EMUMMC_BOOT0:
_action_ums_emuemmc_boot0(NULL);
break;
case NYX_UMS_EMUMMC_BOOT1:
_action_ums_emuemmc_boot1(NULL);
break;
case NYX_UMS_EMUMMC_GPP:
_action_ums_emuemmc_gpp(NULL);
break;
}
}
static lv_res_t _emmc_read_only_toggle(lv_obj_t *btn)
{
nyx_generic_onoff_toggle(btn);
usb_msc_emmc_read_only = lv_btn_get_state(btn) & LV_BTN_STATE_TGL_REL ? 1 : 0;
return LV_RES_OK;
}
static lv_res_t _create_window_usb_tools(lv_obj_t *parent)
{
lv_obj_t *win = nyx_create_standard_window(SYMBOL_USB" USB Tools");
static lv_style_t h_style;
lv_style_copy(&h_style, &lv_style_transp);
h_style.body.padding.inner = 0;
h_style.body.padding.hor = LV_DPI - (LV_DPI / 4);
h_style.body.padding.ver = LV_DPI / 9;
// Create USB Mass Storage container.
lv_obj_t *h1 = lv_cont_create(win, NULL);
lv_cont_set_style(h1, &h_style);
lv_cont_set_fit(h1, false, true);
lv_obj_set_width(h1, (LV_HOR_RES / 9) * 5);
lv_obj_set_click(h1, false);
lv_cont_set_layout(h1, LV_LAYOUT_OFF);
lv_obj_t *label_sep = lv_label_create(h1, NULL);
lv_label_set_static_text(label_sep, "");
lv_obj_t *label_txt = lv_label_create(h1, NULL);
lv_label_set_static_text(label_txt, "USB Mass Storage");
lv_obj_set_style(label_txt, lv_theme_get_current()->label.prim);
lv_obj_align(label_txt, label_sep, LV_ALIGN_OUT_BOTTOM_LEFT, LV_DPI / 4, -LV_DPI * 3 / 10);
lv_obj_t *line_sep = lv_line_create(h1, NULL);
static const lv_point_t line_pp[] = { {0, 0}, { LV_HOR_RES - (LV_DPI - (LV_DPI / 4)) * 2, 0} };
lv_line_set_points(line_sep, line_pp, 2);
lv_line_set_style(line_sep, lv_theme_get_current()->line.decor);
lv_obj_align(line_sep, label_txt, LV_ALIGN_OUT_BOTTOM_LEFT, -(LV_DPI / 4), LV_DPI / 8);
// Create UMS buttons.
lv_obj_t *btn1 = lv_btn_create(h1, NULL);
lv_obj_t *label_btn = lv_label_create(btn1, NULL);
lv_btn_set_fit(btn1, true, true);
lv_label_set_static_text(label_btn, SYMBOL_SD" SD Card");
lv_obj_align(btn1, line_sep, LV_ALIGN_OUT_BOTTOM_LEFT, LV_DPI / 4, LV_DPI / 4);
lv_btn_set_action(btn1, LV_BTN_ACTION_CLICK, action_ums_sd);
lv_obj_t *label_txt2 = lv_label_create(h1, NULL);
lv_label_set_recolor(label_txt2, true);
lv_label_set_static_text(label_txt2,
2021-01-11 19:39:44 +00:00
"Allows you to mount the SD Card to a PC/Phone.\n"
"#C7EA46 All operating systems are supported. Access is# #FF8000 Read/Write.#");
lv_obj_set_style(label_txt2, &hint_small_style);
lv_obj_align(label_txt2, btn1, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
// Create RAW GPP button.
lv_obj_t *btn_gpp = lv_btn_create(h1, btn1);
label_btn = lv_label_create(btn_gpp, NULL);
lv_label_set_static_text(label_btn, SYMBOL_CHIP" eMMC RAW GPP");
lv_obj_align(btn_gpp, label_txt2, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 2);
lv_btn_set_action(btn_gpp, LV_BTN_ACTION_CLICK, _action_ums_emmc_gpp);
lv_obj_t *btn_boot0 = lv_btn_create(h1, btn1);
label_btn = lv_label_create(btn_boot0, NULL);
lv_label_set_static_text(label_btn, "BOOT0");
lv_obj_align(btn_boot0, btn_gpp, LV_ALIGN_OUT_RIGHT_MID, LV_DPI / 10, 0);
lv_btn_set_action(btn_boot0, LV_BTN_ACTION_CLICK, _action_ums_emmc_boot0);
lv_obj_t *btn_boot1 = lv_btn_create(h1, btn1);
label_btn = lv_label_create(btn_boot1, NULL);
lv_label_set_static_text(label_btn, "BOOT1");
lv_obj_align(btn_boot1, btn_boot0, LV_ALIGN_OUT_RIGHT_MID, LV_DPI / 10, 0);
lv_btn_set_action(btn_boot1, LV_BTN_ACTION_CLICK, _action_ums_emmc_boot1);
lv_obj_t *btn_emu_gpp = lv_btn_create(h1, btn1);
label_btn = lv_label_create(btn_emu_gpp, NULL);
lv_label_set_static_text(label_btn, SYMBOL_MODULES_ALT" emu RAW GPP");
lv_obj_align(btn_emu_gpp, btn_gpp, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 2);
lv_btn_set_action(btn_emu_gpp, LV_BTN_ACTION_CLICK, _action_ums_emuemmc_gpp);
lv_obj_t *btn_emu_boot0 = lv_btn_create(h1, btn1);
label_btn = lv_label_create(btn_emu_boot0, NULL);
lv_label_set_static_text(label_btn, "BOOT0");
lv_obj_align(btn_emu_boot0, btn_boot0, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 2);
lv_btn_set_action(btn_emu_boot0, LV_BTN_ACTION_CLICK, _action_ums_emuemmc_boot0);
lv_obj_t *btn_emu_boot1 = lv_btn_create(h1, btn1);
label_btn = lv_label_create(btn_emu_boot1, NULL);
lv_label_set_static_text(label_btn, "BOOT1");
lv_obj_align(btn_emu_boot1, btn_boot1, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 2);
lv_btn_set_action(btn_emu_boot1, LV_BTN_ACTION_CLICK, _action_ums_emuemmc_boot1);
label_txt2 = lv_label_create(h1, NULL);
lv_label_set_recolor(label_txt2, true);
lv_label_set_static_text(label_txt2,
2021-01-11 19:39:44 +00:00
"Allows you to mount the eMMC/emuMMC.\n"
"#C7EA46 Default access is# #FF8000 read-only.#");
lv_obj_set_style(label_txt2, &hint_small_style);
lv_obj_align(label_txt2, btn_emu_gpp, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
lv_obj_t *h_write = lv_cont_create(win, NULL);
lv_cont_set_style(h_write, &h_style);
lv_cont_set_fit(h_write, false, true);
lv_obj_set_width(h_write, (LV_HOR_RES / 9) * 2);
lv_obj_set_click(h_write, false);
lv_cont_set_layout(h_write, LV_LAYOUT_OFF);
lv_obj_align(h_write, label_txt2, LV_ALIGN_OUT_RIGHT_MID, LV_DPI / 10, 0);
lv_obj_t *btn_write_access = lv_btn_create(h_write, NULL);
nyx_create_onoff_button(lv_theme_get_current(), h_write,
btn_write_access, SYMBOL_EDIT" Read-Only", _emmc_read_only_toggle, false);
if (!n_cfg.ums_emmc_rw)
lv_btn_set_state(btn_write_access, LV_BTN_STATE_TGL_REL);
_emmc_read_only_toggle(btn_write_access);
// Create USB Input Devices container.
lv_obj_t *h2 = lv_cont_create(win, NULL);
lv_cont_set_style(h2, &h_style);
lv_cont_set_fit(h2, false, true);
lv_obj_set_width(h2, (LV_HOR_RES / 9) * 3);
lv_obj_set_click(h2, false);
lv_cont_set_layout(h2, LV_LAYOUT_OFF);
lv_obj_align(h2, h1, LV_ALIGN_OUT_RIGHT_TOP, LV_DPI * 17 / 29, 0);
label_sep = lv_label_create(h2, NULL);
lv_label_set_static_text(label_sep, "");
lv_obj_t *label_txt3 = lv_label_create(h2, NULL);
lv_label_set_static_text(label_txt3, "USB Input Devices");
lv_obj_set_style(label_txt3, lv_theme_get_current()->label.prim);
lv_obj_align(label_txt3, label_sep, LV_ALIGN_OUT_BOTTOM_LEFT, LV_DPI / 4, -LV_DPI * 4 / 21);
line_sep = lv_line_create(h2, line_sep);
lv_obj_align(line_sep, label_txt3, LV_ALIGN_OUT_BOTTOM_LEFT, -(LV_DPI / 4), LV_DPI / 8);
// Create Gamepad button.
lv_obj_t *btn3 = lv_btn_create(h2, NULL);
label_btn = lv_label_create(btn3, NULL);
lv_btn_set_fit(btn3, true, true);
lv_label_set_static_text(label_btn, SYMBOL_CIRCUIT" Gamepad");
lv_obj_align(btn3, line_sep, LV_ALIGN_OUT_BOTTOM_LEFT, LV_DPI / 4, LV_DPI / 4);
lv_btn_set_action(btn3, LV_BTN_ACTION_CLICK, _action_hid_jc);
lv_obj_t *label_txt4 = lv_label_create(h2, NULL);
lv_label_set_recolor(label_txt4, true);
lv_label_set_static_text(label_txt4,
2021-01-11 19:39:44 +00:00
"Plug-in the Joy-Con and convert the device\n"
"into a gamepad for PC or Phone.\n"
"#C7EA46 Needs both Joy-Con in order to function.#");
lv_obj_set_style(label_txt4, &hint_small_style);
lv_obj_align(label_txt4, btn3, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
/*
// Create Touchpad button.
lv_obj_t *btn4 = lv_btn_create(h2, btn1);
label_btn = lv_label_create(btn4, NULL);
lv_label_set_static_text(label_btn, SYMBOL_KEYBOARD" Touchpad");
lv_obj_align(btn4, label_txt4, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 2);
lv_btn_set_action(btn4, LV_BTN_ACTION_CLICK, _action_hid_touch);
lv_btn_set_state(btn4, LV_BTN_STATE_INA);
label_txt4 = lv_label_create(h2, NULL);
lv_label_set_recolor(label_txt4, true);
lv_label_set_static_text(label_txt4,
2021-01-11 19:39:44 +00:00
"Control the PC via the device\'s touchscreen.\n"
"#C7EA46 Two fingers tap acts like a# #FF8000 Right click##C7EA46 .#\n");
lv_obj_set_style(label_txt4, &hint_small_style);
lv_obj_align(label_txt4, btn4, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
*/
return LV_RES_OK;
}
2020-04-30 11:30:37 +00:00
static int _fix_attributes(lv_obj_t *lb_val, char *path, u32 *total)
{
FRESULT res;
DIR dir;
u32 dirLength = 0;
static FILINFO fno;
// Open directory.
res = f_opendir(&dir, path);
if (res != FR_OK)
return res;
dirLength = strlen(path);
for (;;)
{
// Clear file or folder 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);
2020-04-30 11:30:37 +00:00
strcpy(&path[dirLength + 1], fno.fname);
// Is it a directory?
if (fno.fattrib & AM_DIR)
{
// Check if it's a HOS single file folder.
strcat(path, "/00");
bool is_hos_special = !f_stat(path, NULL);
path[strlen(path) - 3] = 0;
// Set archive bit to HOS single file folders.
if (is_hos_special)
2020-04-30 11:30:37 +00:00
{
if (!(fno.fattrib & AM_ARC))
{
total[0]++;
f_chmod(path, AM_ARC, AM_ARC);
}
}
else if (fno.fattrib & AM_ARC) // If not, clear the archive bit.
{
2020-04-30 11:30:37 +00:00
total[1]++;
f_chmod(path, 0, AM_ARC);
}
2020-04-30 11:30:37 +00:00
lv_label_set_text(lb_val, path);
manual_system_maintenance(true);
// Enter the directory.
2020-04-30 11:30:37 +00:00
res = _fix_attributes(lb_val, path, total);
if (res != FR_OK)
break;
}
}
f_closedir(&dir);
return res;
}
static lv_res_t _create_window_unset_abit_tool(lv_obj_t *btn)
{
2020-04-30 11:30:37 +00:00
lv_obj_t *win = nyx_create_standard_window(SYMBOL_COPY" Fix Archive Bit (All folders)");
// Disable buttons.
nyx_window_toggle_buttons(win, true);
lv_obj_t *desc = lv_cont_create(win, NULL);
lv_obj_set_size(desc, LV_HOR_RES * 10 / 11, LV_VER_RES - (LV_DPI * 11 / 7) * 4);
lv_obj_t * lb_desc = lv_label_create(desc, NULL);
lv_label_set_long_mode(lb_desc, LV_LABEL_LONG_BREAK);
lv_label_set_recolor(lb_desc, true);
if (!sd_mount())
{
lv_label_set_text(lb_desc, "#FFDD00 Failed to init SD!#");
lv_obj_set_width(lb_desc, lv_obj_get_width(desc));
}
else
{
2020-04-30 11:30:37 +00:00
lv_label_set_text(lb_desc, "#00DDFF Traversing all SD card files!#\nThis may take some time...");
lv_obj_set_width(lb_desc, lv_obj_get_width(desc));
lv_obj_t *val = lv_cont_create(win, NULL);
lv_obj_set_size(val, LV_HOR_RES * 10 / 11, LV_VER_RES - (LV_DPI * 11 / 7) * 4);
lv_obj_t * lb_val = lv_label_create(val, lb_desc);
char *path = malloc(1024);
path[0] = 0;
lv_label_set_text(lb_val, "");
lv_obj_set_width(lb_val, lv_obj_get_width(val));
lv_obj_align(val, desc, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
2020-04-30 11:30:37 +00:00
u32 total[2] = { 0 };
_fix_attributes(lb_val, path, total);
sd_unmount();
lv_obj_t *desc2 = lv_cont_create(win, NULL);
lv_obj_set_size(desc2, LV_HOR_RES * 10 / 11, LV_VER_RES - (LV_DPI * 11 / 7) * 4);
lv_obj_t * lb_desc2 = lv_label_create(desc2, lb_desc);
char *txt_buf = (char *)malloc(0x500);
2020-04-30 11:30:37 +00:00
s_printf(txt_buf, "#96FF00 Total archive bits fixed:# #FF8000 %d unset, %d set!#", total[1], total[0]);
lv_label_set_text(lb_desc2, txt_buf);
lv_obj_set_width(lb_desc2, lv_obj_get_width(desc2));
lv_obj_align(desc2, val, LV_ALIGN_OUT_BOTTOM_RIGHT, 0, 0);
free(path);
}
// Enable buttons.
nyx_window_toggle_buttons(win, false);
return LV_RES_OK;
}
2020-04-30 11:31:32 +00:00
static lv_res_t _create_mbox_fix_touchscreen(lv_obj_t *btn)
{
int res = 0;
lv_obj_t *dark_bg = lv_obj_create(lv_scr_act(), NULL);
lv_obj_set_style(dark_bg, &mbox_darken);
lv_obj_set_size(dark_bg, LV_HOR_RES, LV_VER_RES);
static const char *mbox_btn_map[] = { "\211", "\222OK", "\211", "" };
lv_obj_t * mbox = lv_mbox_create(dark_bg, NULL);
lv_mbox_set_recolor_text(mbox, true);
char *txt_buf = malloc(SZ_16K);
2020-04-30 11:31:32 +00:00
strcpy(txt_buf, "#FF8000 Don't touch the screen!#\n\nThe tuning process will start in ");
u32 text_idx = strlen(txt_buf);
lv_mbox_set_text(mbox, txt_buf);
lv_obj_set_width(mbox, LV_HOR_RES / 9 * 6);
lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_top(mbox, true);
lv_mbox_set_text(mbox,
"#FFDD00 Warning: Only run this if you really have issues!#\n\n"
"Press #FF8000 POWER# to Continue.\nPress #FF8000 VOL# to abort.");
manual_system_maintenance(true);
if (!(btn_wait() & BTN_POWER))
goto out;
manual_system_maintenance(true);
lv_mbox_set_text(mbox, txt_buf);
u32 seconds = 5;
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--;
}
u8 err[2];
if (touch_panel_ito_test(err))
{
if (!err[0] && !err[1])
{
res = touch_execute_autotune();
if (res)
goto out;
}
else
{
touch_sense_enable();
s_printf(txt_buf, "#FFFF00 ITO Test: ");
switch (err[0])
{
case ITO_FORCE_OPEN:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Force Open");
2020-04-30 11:31:32 +00:00
break;
case ITO_SENSE_OPEN:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Sense Open");
2020-04-30 11:31:32 +00:00
break;
case ITO_FORCE_SHRT_GND:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Force Short to GND");
2020-04-30 11:31:32 +00:00
break;
case ITO_SENSE_SHRT_GND:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Sense Short to GND");
2020-04-30 11:31:32 +00:00
break;
case ITO_FORCE_SHRT_VCM:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Force Short to VDD");
2020-04-30 11:31:32 +00:00
break;
case ITO_SENSE_SHRT_VCM:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Sense Short to VDD");
2020-04-30 11:31:32 +00:00
break;
case ITO_FORCE_SHRT_FORCE:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Force Short to Force");
2020-04-30 11:31:32 +00:00
break;
case ITO_SENSE_SHRT_SENSE:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Sense Short to Sense");
2020-04-30 11:31:32 +00:00
break;
case ITO_F2E_SENSE:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Force Short to Sense");
2020-04-30 11:31:32 +00:00
break;
case ITO_FPC_FORCE_OPEN:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "FPC Force Open");
2020-04-30 11:31:32 +00:00
break;
case ITO_FPC_SENSE_OPEN:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "FPC Sense Open");
2020-04-30 11:31:32 +00:00
break;
default:
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Unknown");
2020-04-30 11:31:32 +00:00
break;
}
s_printf(txt_buf + strlen(txt_buf), " (%d), Chn: %d#\n\n", err[0], err[1]);
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "#FFFF00 The touchscreen calibration failed!");
2020-04-30 11:31:32 +00:00
lv_mbox_set_text(mbox, txt_buf);
goto out2;
}
}
touch_sense_enable();
out:
if (res)
lv_mbox_set_text(mbox, "#C7EA46 The touchscreen calibration finished!");
else
lv_mbox_set_text(mbox, "#FFFF00 The touchscreen calibration failed!");
out2:
lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action);
free(txt_buf);
return LV_RES_OK;
}
static lv_res_t _create_window_dump_pk12_tool(lv_obj_t *btn)
{
lv_obj_t *win = nyx_create_standard_window(SYMBOL_MODULES" Dump package1/2");
// Disable buttons.
nyx_window_toggle_buttons(win, true);
lv_obj_t *desc = lv_cont_create(win, NULL);
lv_obj_set_size(desc, LV_HOR_RES * 10 / 11, LV_VER_RES - (LV_DPI * 12 / 7));
lv_obj_t * lb_desc = lv_label_create(desc, NULL);
lv_obj_set_style(lb_desc, &monospace_text);
lv_label_set_long_mode(lb_desc, LV_LABEL_LONG_BREAK);
lv_label_set_recolor(lb_desc, true);
lv_obj_set_width(lb_desc, lv_obj_get_width(desc));
if (!sd_mount())
{
lv_label_set_text(lb_desc, "#FFDD00 Failed to init SD!#");
goto out_end;
}
char path[128];
u8 kb = 0;
u8 *pkg1 = (u8 *)calloc(1, SZ_256K);
u8 *warmboot = (u8 *)calloc(1, SZ_256K);
u8 *secmon = (u8 *)calloc(1, SZ_256K);
u8 *loader = (u8 *)calloc(1, SZ_256K);
u8 *pkg2 = NULL;
char *txt_buf = (char *)malloc(SZ_16K);
if (!sdmmc_storage_init_mmc(&emmc_storage, &emmc_sdmmc, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_HS400))
{
lv_label_set_text(lb_desc, "#FFDD00 Failed to init eMMC!#");
goto out_free;
}
sdmmc_storage_set_mmc_partition(&emmc_storage, EMMC_BOOT0);
// Read package1.
static const u32 BOOTLOADER_SIZE = SZ_256K;
2020-08-15 09:30:18 +00:00
static const u32 BOOTLOADER_MAIN_OFFSET = 0x100000;
static const u32 HOS_KEYBLOBS_OFFSET = 0x180000;
char *build_date = malloc(32);
u32 pk1_offset = h_cfg.t210b01 ? sizeof(bl_hdr_t210b01_t) : 0; // Skip T210B01 OEM header.
sdmmc_storage_read(&emmc_storage, BOOTLOADER_MAIN_OFFSET / NX_EMMC_BLOCKSIZE, BOOTLOADER_SIZE / NX_EMMC_BLOCKSIZE, pkg1);
2020-08-15 09:30:18 +00:00
const pkg1_id_t *pkg1_id = pkg1_identify(pkg1 + pk1_offset, build_date);
s_printf(txt_buf, "#00DDFF Found pkg1 ('%s')#\n\n", build_date);
free(build_date);
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
// Dump package1 in its encrypted state.
emmcsn_path_impl(path, "/pkg1", "pkg1_enc.bin", &emmc_storage);
bool res = sd_save_to_file(pkg1, BOOTLOADER_SIZE, path);
// Exit if unknown.
if (!pkg1_id)
{
2021-02-06 01:55:01 +00:00
strcat(txt_buf, "#FFDD00 Unknown pkg1 version!#");
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
if (!res)
{
strcat(txt_buf, "\nEncrypted pkg1 dumped to pkg1_enc.bin");
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
}
goto out_free;
}
kb = pkg1_id->kb;
tsec_ctxt_t tsec_ctxt = {0};
tsec_ctxt.fw = (void *)(pkg1 + pkg1_id->tsec_off);
tsec_ctxt.pkg1 = (void *)pkg1;
tsec_ctxt.pkg11_off = pkg1_id->pkg11_off;
tsec_ctxt.secmon_base = pkg1_id->secmon_base;
// Read keyblob.
u8 *keyblob = (u8 *)calloc(NX_EMMC_BLOCKSIZE, 1);
sdmmc_storage_read(&emmc_storage, HOS_KEYBLOBS_OFFSET / NX_EMMC_BLOCKSIZE + kb, 1, keyblob);
// Decrypt.
hos_keygen(keyblob, kb, &tsec_ctxt);
free(keyblob);
if (h_cfg.t210b01 || kb <= KB_FIRMWARE_VERSION_600)
{
if (!pkg1_decrypt(pkg1_id, pkg1))
{
strcat(txt_buf, "#FFDD00 Pkg1 decryption failed!#\n");
if (h_cfg.t210b01)
strcat(txt_buf, "#FFDD00 Is BEK missing?#\n");
lv_label_set_text(lb_desc, txt_buf);
goto out_free;
}
}
if (h_cfg.t210b01 || kb <= KB_FIRMWARE_VERSION_620)
{
const u8 *sec_map = pkg1_unpack(warmboot, secmon, loader, pkg1_id, pkg1 + pk1_offset);
pk11_hdr_t *hdr_pk11 = (pk11_hdr_t *)(pkg1 + pk1_offset + pkg1_id->pkg11_off + 0x20);
// Use correct sizes.
u32 sec_size[3] = { hdr_pk11->wb_size, hdr_pk11->ldr_size, hdr_pk11->sm_size };
for (u32 i = 0; i < 3; i++)
{
if (sec_map[i] == PK11_SECTION_WB)
hdr_pk11->wb_size = sec_size[i];
else if (sec_map[i] == PK11_SECTION_LD)
hdr_pk11->ldr_size = sec_size[i];
else if (sec_map[i] == PK11_SECTION_SM)
hdr_pk11->sm_size = sec_size[i];
}
// Display info.
s_printf(txt_buf + strlen(txt_buf),
"#C7EA46 NX Bootloader size: #0x%05X\n"
"#C7EA46 Secure monitor addr: #0x%05X\n"
"#C7EA46 Secure monitor size: #0x%05X\n"
"#C7EA46 Warmboot addr: #0x%05X\n"
"#C7EA46 Warmboot size: #0x%05X\n\n",
hdr_pk11->ldr_size, pkg1_id->secmon_base, hdr_pk11->sm_size, pkg1_id->warmboot_base, hdr_pk11->wb_size);
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
// Dump package1.1.
emmcsn_path_impl(path, "/pkg1", "pkg1_decr.bin", &emmc_storage);
if (sd_save_to_file(pkg1, SZ_256K, path))
goto out_free;
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "pkg1 dumped to pkg1_decr.bin\n");
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
// Dump nxbootloader.
emmcsn_path_impl(path, "/pkg1", "nxloader.bin", &emmc_storage);
if (sd_save_to_file(loader, hdr_pk11->ldr_size, path))
goto out_free;
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "NX Bootloader dumped to nxloader.bin\n");
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
// Dump secmon.
emmcsn_path_impl(path, "/pkg1", "secmon.bin", &emmc_storage);
if (sd_save_to_file(secmon, hdr_pk11->sm_size, path))
goto out_free;
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Secure Monitor dumped to secmon.bin\n");
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
// Dump warmboot.
emmcsn_path_impl(path, "/pkg1", "warmboot.bin", &emmc_storage);
if (sd_save_to_file(warmboot, hdr_pk11->wb_size, path))
goto out_free;
// If T210B01, save a copy of decrypted warmboot binary also.
if (h_cfg.t210b01)
{
se_aes_iv_clear(13);
se_aes_crypt_cbc(13, DECRYPT, warmboot + 0x330, hdr_pk11->wb_size - 0x330,
warmboot + 0x330, hdr_pk11->wb_size - 0x330);
emmcsn_path_impl(path, "/pkg1", "warmboot_dec.bin", &emmc_storage);
if (sd_save_to_file(warmboot, hdr_pk11->wb_size, path))
goto out_free;
}
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Warmboot dumped to warmboot.bin\n\n");
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
}
// Dump package2.1.
sdmmc_storage_set_mmc_partition(&emmc_storage, EMMC_GPP);
// Parse eMMC GPT.
LIST_INIT(gpt);
nx_emmc_gpt_parse(&gpt, &emmc_storage);
// Find package2 partition.
emmc_part_t *pkg2_part = nx_emmc_part_find(&gpt, "BCPKG2-1-Normal-Main");
if (!pkg2_part)
goto out;
// Read in package2 header and get package2 real size.
u8 *tmp = (u8 *)malloc(NX_EMMC_BLOCKSIZE);
nx_emmc_part_read(&emmc_storage, pkg2_part, 0x4000 / NX_EMMC_BLOCKSIZE, 1, tmp);
u32 *hdr_pkg2_raw = (u32 *)(tmp + 0x100);
u32 pkg2_size = hdr_pkg2_raw[0] ^ hdr_pkg2_raw[2] ^ hdr_pkg2_raw[3];
free(tmp);
// Read in package2.
u32 pkg2_size_aligned = ALIGN(pkg2_size, NX_EMMC_BLOCKSIZE);
pkg2 = malloc(pkg2_size_aligned);
nx_emmc_part_read(&emmc_storage, pkg2_part, 0x4000 / NX_EMMC_BLOCKSIZE,
pkg2_size_aligned / NX_EMMC_BLOCKSIZE, pkg2);
// Dump encrypted package2.
emmcsn_path_impl(path, "/pkg2", "pkg2_encr.bin", &emmc_storage);
res = sd_save_to_file(pkg2, pkg2_size, path);
// Decrypt package2 and parse KIP1 blobs in INI1 section.
pkg2_hdr_t *pkg2_hdr = pkg2_decrypt(pkg2, kb);
if (!pkg2_hdr)
{
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "#FFDD00 Pkg2 decryption failed!#");
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
if (!res)
{
strcat(txt_buf, "\npkg2 encrypted dumped to pkg2_encr.bin\n");
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
}
// Clear EKS slot, in case something went wrong with tsec keygen.
2020-04-30 00:43:29 +00:00
hos_eks_clear(kb);
goto out;
}
// Display info.
s_printf(txt_buf + strlen(txt_buf),
"#C7EA46 Kernel size: #0x%05X\n"
"#C7EA46 INI1 size: #0x%05X\n\n",
pkg2_hdr->sec_size[PKG2_SEC_KERNEL], pkg2_hdr->sec_size[PKG2_SEC_INI1]);
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
// Dump pkg2.1.
emmcsn_path_impl(path, "/pkg2", "pkg2_decr.bin", &emmc_storage);
if (sd_save_to_file(pkg2, pkg2_hdr->sec_size[PKG2_SEC_KERNEL] + pkg2_hdr->sec_size[PKG2_SEC_INI1], path))
goto out;
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "pkg2 dumped to pkg2_decr.bin\n");
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
// Dump kernel.
emmcsn_path_impl(path, "/pkg2", "kernel.bin", &emmc_storage);
if (sd_save_to_file(pkg2_hdr->data, pkg2_hdr->sec_size[PKG2_SEC_KERNEL], path))
goto out;
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "Kernel dumped to kernel.bin\n");
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
// Dump INI1.
u32 ini1_off = pkg2_hdr->sec_size[PKG2_SEC_KERNEL];
u32 ini1_size = pkg2_hdr->sec_size[PKG2_SEC_INI1];
if (!ini1_size)
{
pkg2_get_newkern_info(pkg2_hdr->data);
ini1_off = pkg2_newkern_ini1_start;
ini1_size = pkg2_newkern_ini1_end - pkg2_newkern_ini1_start;
}
if (!ini1_off)
{
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "#FFDD00 Failed to dump INI1 and kips!#\n");
goto out;
}
pkg2_ini1_t *ini1 = (pkg2_ini1_t *)(pkg2_hdr->data + ini1_off);
emmcsn_path_impl(path, "/pkg2", "ini1.bin", &emmc_storage);
if (sd_save_to_file(ini1, ini1_size, path))
goto out;
2020-07-14 19:26:40 +00:00
strcat(txt_buf, "INI1 dumped to ini1.bin\n\n");
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
char filename[32];
u8 *ptr = (u8 *)ini1;
ptr += sizeof(pkg2_ini1_t);
// Dump all kips.
u8 *kip_buffer = (u8 *)malloc(SZ_4M);
for (u32 i = 0; i < ini1->num_procs; i++)
{
pkg2_kip1_t *kip1 = (pkg2_kip1_t *)ptr;
u32 kip1_size = pkg2_calc_kip1_size(kip1);
s_printf(filename, "%s.kip1", kip1->name);
if ((u32)kip1 % 8)
{
memcpy(kip_buffer, kip1, kip1_size);
kip1 = (pkg2_kip1_t *)kip_buffer;
}
emmcsn_path_impl(path, "/pkg2/ini1", filename, &emmc_storage);
if (sd_save_to_file(kip1, kip1_size, path))
{
free(kip_buffer);
goto out;
}
s_printf(txt_buf + strlen(txt_buf), "%s kip dumped to %s.kip1\n", kip1->name, kip1->name);
lv_label_set_text(lb_desc, txt_buf);
manual_system_maintenance(true);
ptr += kip1_size;
}
free(kip_buffer);
out:
nx_emmc_gpt_free(&gpt);
out_free:
free(pkg1);
free(secmon);
free(warmboot);
free(loader);
free(pkg2);
free(txt_buf);
sdmmc_storage_end(&emmc_storage);
sd_unmount();
if (kb >= KB_FIRMWARE_VERSION_620)
se_aes_key_clear(8);
out_end:
// Enable buttons.
nyx_window_toggle_buttons(win, false);
return LV_RES_OK;
}
2019-07-06 19:08:37 +00:00
static void _create_tab_tools_emmc_pkg12(lv_theme_t *th, lv_obj_t *parent)
{
lv_page_set_scrl_layout(parent, LV_LAYOUT_PRETTY);
// Create Backup & Restore container.
lv_obj_t *h1 = _create_container(parent);
lv_obj_t *label_sep = lv_label_create(h1, NULL);
lv_label_set_static_text(label_sep, "");
lv_obj_t *label_txt = lv_label_create(h1, NULL);
lv_label_set_static_text(label_txt, "Backup & Restore");
lv_obj_set_style(label_txt, th->label.prim);
lv_obj_align(label_txt, label_sep, LV_ALIGN_OUT_BOTTOM_LEFT, LV_DPI / 4, -LV_DPI * 3 / 10);
lv_obj_t *line_sep = lv_line_create(h1, NULL);
static const lv_point_t line_pp[] = { {0, 0}, { LV_HOR_RES - (LV_DPI - (LV_DPI / 4)) * 2, 0} };
lv_line_set_points(line_sep, line_pp, 2);
lv_line_set_style(line_sep, th->line.decor);
lv_obj_align(line_sep, label_txt, LV_ALIGN_OUT_BOTTOM_LEFT, -(LV_DPI / 4), LV_DPI / 8);
// Create Backup eMMC button.
lv_obj_t *btn = lv_btn_create(h1, NULL);
if (hekate_bg)
{
lv_btn_set_style(btn, LV_BTN_STYLE_REL, &btn_transp_rel);
lv_btn_set_style(btn, LV_BTN_STYLE_PR, &btn_transp_pr);
}
lv_obj_t *label_btn = lv_label_create(btn, NULL);
lv_btn_set_fit(btn, true, true);
lv_label_set_static_text(label_btn, SYMBOL_UPLOAD" Backup eMMC");
lv_obj_align(btn, line_sep, LV_ALIGN_OUT_BOTTOM_LEFT, LV_DPI / 4, LV_DPI / 4);
lv_btn_set_action(btn, LV_BTN_ACTION_CLICK, create_window_backup_restore_tool);
lv_obj_t *label_txt2 = lv_label_create(h1, NULL);
lv_label_set_recolor(label_txt2, true);
lv_label_set_static_text(label_txt2,
2021-01-11 19:39:44 +00:00
"Allows you to backup the eMMC partitions individually or as\n"
"a whole raw image to the SD card.\n"
2020-04-30 11:32:52 +00:00
"#C7EA46 Supports SD cards from# #FF8000 4GB# #C7EA46 and up. #"
"#FF8000 FAT32# #C7EA46 and ##FF8000 exFAT##C7EA46 .#");
lv_obj_set_style(label_txt2, &hint_small_style);
lv_obj_align(label_txt2, btn, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
// Create Restore eMMC button.
lv_obj_t *btn2 = lv_btn_create(h1, btn);
label_btn = lv_label_create(btn2, NULL);
lv_label_set_static_text(label_btn, SYMBOL_DOWNLOAD" Restore eMMC");
lv_obj_align(btn2, label_txt2, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 2);
lv_btn_set_action(btn2, LV_BTN_ACTION_CLICK, create_window_backup_restore_tool);
label_txt2 = lv_label_create(h1, NULL);
lv_label_set_recolor(label_txt2, true);
lv_label_set_static_text(label_txt2,
2021-01-11 19:39:44 +00:00
"Allows you to restore the eMMC/emuMMC partitions individually\n"
"or as a whole raw image from the SD card.\n"
2020-04-30 11:32:52 +00:00
"#C7EA46 Supports SD cards from# #FF8000 4GB# #C7EA46 and up. #"
"#FF8000 FAT32# #C7EA46 and ##FF8000 exFAT##C7EA46 .#");
lv_obj_set_style(label_txt2, &hint_small_style);
lv_obj_align(label_txt2, btn2, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
// Create Misc container.
2019-07-06 19:08:37 +00:00
lv_obj_t *h2 = _create_container(parent);
lv_obj_align(h2, h1, LV_ALIGN_OUT_RIGHT_TOP, 0, 0);
label_sep = lv_label_create(h2, NULL);
lv_label_set_static_text(label_sep, "");
lv_obj_t *label_txt3 = lv_label_create(h2, NULL);
lv_label_set_static_text(label_txt3, "SD Partitions & USB");
lv_obj_set_style(label_txt3, th->label.prim);
lv_obj_align(label_txt3, label_sep, LV_ALIGN_OUT_BOTTOM_LEFT, LV_DPI / 4, -LV_DPI * 3 / 10);
line_sep = lv_line_create(h2, line_sep);
lv_obj_align(line_sep, label_txt3, LV_ALIGN_OUT_BOTTOM_LEFT, -(LV_DPI / 4), LV_DPI / 8);
// Create Partition SD Card button.
lv_obj_t *btn3 = lv_btn_create(h2, NULL);
if (hekate_bg)
{
lv_btn_set_style(btn3, LV_BTN_STYLE_REL, &btn_transp_rel);
lv_btn_set_style(btn3, LV_BTN_STYLE_PR, &btn_transp_pr);
}
label_btn = lv_label_create(btn3, NULL);
lv_btn_set_fit(btn3, true, true);
lv_label_set_static_text(label_btn, SYMBOL_SD" Partition SD Card");
lv_obj_align(btn3, line_sep, LV_ALIGN_OUT_BOTTOM_LEFT, LV_DPI / 4, LV_DPI / 4);
lv_btn_set_action(btn3, LV_BTN_ACTION_CLICK, create_window_partition_manager);
lv_obj_t *label_txt4 = lv_label_create(h2, NULL);
lv_label_set_recolor(label_txt4, true);
lv_label_set_static_text(label_txt4,
"Allows you to partition the SD Card for using it with #C7EA46 emuMMC#,\n"
"#C7EA46 Android# and #C7EA46 Linux#. You can also flash Linux and Android.\n");
lv_obj_set_style(label_txt4, &hint_small_style);
lv_obj_align(label_txt4, btn3, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
label_sep = lv_label_create(h2, NULL);
lv_label_set_static_text(label_sep, "");
lv_obj_align(label_sep, label_txt4, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI * 11 / 7);
// Create USB Tools button.
lv_obj_t *btn4 = lv_btn_create(h2, btn3);
label_btn = lv_label_create(btn4, NULL);
lv_label_set_static_text(label_btn, SYMBOL_USB" USB Tools");
lv_obj_align(btn4, label_txt4, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 2);
lv_btn_set_action(btn4, LV_BTN_ACTION_CLICK, _create_window_usb_tools);
label_txt4 = lv_label_create(h2, NULL);
lv_label_set_recolor(label_txt4, true);
lv_label_set_static_text(label_txt4,
"#C7EA46 USB mass storage#, #C7EA46 gamepad# and other USB tools.\n"
"Mass storage can mount SD, eMMC and emuMMC. The\n"
2021-01-11 19:39:44 +00:00
"gamepad transforms the Switch into an input device.#");
lv_obj_set_style(label_txt4, &hint_small_style);
lv_obj_align(label_txt4, btn4, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
}
static void _create_tab_tools_arc_autorcm(lv_theme_t *th, lv_obj_t *parent)
{
lv_page_set_scrl_layout(parent, LV_LAYOUT_PRETTY);
// Create Misc container.
2019-07-06 19:08:37 +00:00
lv_obj_t *h1 = _create_container(parent);
lv_obj_t *label_sep = lv_label_create(h1, NULL);
lv_label_set_static_text(label_sep, "");
lv_obj_t *label_txt = lv_label_create(h1, NULL);
lv_label_set_static_text(label_txt, "Misc");
lv_obj_set_style(label_txt, th->label.prim);
lv_obj_align(label_txt, label_sep, LV_ALIGN_OUT_BOTTOM_LEFT, LV_DPI / 4, -LV_DPI * 3 / 10);
lv_obj_t *line_sep = lv_line_create(h1, NULL);
static const lv_point_t line_pp[] = { {0, 0}, { LV_HOR_RES - (LV_DPI - (LV_DPI / 4)) * 2, 0} };
lv_line_set_points(line_sep, line_pp, 2);
lv_line_set_style(line_sep, th->line.decor);
lv_obj_align(line_sep, label_txt, LV_ALIGN_OUT_BOTTOM_LEFT, -(LV_DPI / 4), LV_DPI / 8);
// Create Unset archive bit button.
lv_obj_t *btn = lv_btn_create(h1, NULL);
if (hekate_bg)
{
lv_btn_set_style(btn, LV_BTN_STYLE_REL, &btn_transp_rel);
lv_btn_set_style(btn, LV_BTN_STYLE_PR, &btn_transp_pr);
}
lv_obj_t *label_btn = lv_label_create(btn, NULL);
lv_btn_set_fit(btn, true, true);
2020-04-30 11:30:37 +00:00
lv_label_set_static_text(label_btn, SYMBOL_DIRECTORY" Fix Archive Bit");
lv_obj_align(btn, line_sep, LV_ALIGN_OUT_BOTTOM_LEFT, LV_DPI / 4, LV_DPI / 4);
lv_btn_set_action(btn, LV_BTN_ACTION_CLICK, _create_window_unset_abit_tool);
lv_obj_t *label_txt2 = lv_label_create(h1, NULL);
lv_label_set_recolor(label_txt2, true);
lv_label_set_static_text(label_txt2,
2020-04-30 11:30:37 +00:00
"Allows you to fix the archive bit for all folders including\n"
"the root and emuMMC \'Nintendo\' folders.\n"
"#C7EA46 It sets the archive bit to folders named with ##FF8000 .[ext]#\n"
"#FF8000 Use that option when you have corruption messages.#");
lv_obj_set_style(label_txt2, &hint_small_style);
2020-04-30 11:30:37 +00:00
lv_obj_align(label_txt2, btn, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
2020-04-30 11:31:32 +00:00
// Create Fix touch calibration button.
lv_obj_t *btn2 = lv_btn_create(h1, btn);
label_btn = lv_label_create(btn2, NULL);
lv_label_set_static_text(label_btn, SYMBOL_KEYBOARD" Calibrate Touchscreen");
lv_obj_align(btn2, label_txt2, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 2);
lv_btn_set_action(btn2, LV_BTN_ACTION_CLICK, _create_mbox_fix_touchscreen);
label_txt2 = lv_label_create(h1, NULL);
lv_label_set_recolor(label_txt2, true);
lv_label_set_static_text(label_txt2,
2021-01-11 19:39:44 +00:00
"Allows you to calibrate the touchscreen module.\n"
2020-04-30 11:31:32 +00:00
"#FF8000 This fixes any issues with touchscreen in Nyx and HOS.#");
lv_obj_set_style(label_txt2, &hint_small_style);
lv_obj_align(label_txt2, btn2, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
// Create Others container.
2019-07-06 19:08:37 +00:00
lv_obj_t *h2 = _create_container(parent);
lv_obj_align(h2, h1, LV_ALIGN_OUT_RIGHT_TOP, 0, 0);
label_sep = lv_label_create(h2, NULL);
lv_label_set_static_text(label_sep, "");
lv_obj_t *label_txt3 = lv_label_create(h2, NULL);
lv_label_set_static_text(label_txt3, "Others");
lv_obj_set_style(label_txt3, th->label.prim);
lv_obj_align(label_txt3, label_sep, LV_ALIGN_OUT_BOTTOM_LEFT, LV_DPI / 4, -LV_DPI * 3 / 10);
line_sep = lv_line_create(h2, line_sep);
lv_obj_align(line_sep, label_txt3, LV_ALIGN_OUT_BOTTOM_LEFT, -(LV_DPI / 4), LV_DPI / 8);
// Create AutoRCM On/Off button.
lv_obj_t *btn3 = lv_btn_create(h2, NULL);
if (hekate_bg)
{
lv_btn_set_style(btn3, LV_BTN_STYLE_REL, &btn_transp_rel);
lv_btn_set_style(btn3, LV_BTN_STYLE_PR, &btn_transp_pr);
lv_btn_set_style(btn3, LV_BTN_STYLE_TGL_REL, &btn_transp_tgl_rel);
lv_btn_set_style(btn3, LV_BTN_STYLE_TGL_PR, &btn_transp_tgl_pr);
}
label_btn = lv_label_create(btn3, NULL);
lv_btn_set_fit(btn3, true, true);
lv_label_set_recolor(label_btn, true);
lv_label_set_text(label_btn, SYMBOL_REFRESH" AutoRCM #00FFC9 ON #");
lv_obj_align(btn3, line_sep, LV_ALIGN_OUT_BOTTOM_LEFT, LV_DPI / 4, LV_DPI / 4);
lv_btn_set_action(btn3, LV_BTN_ACTION_CLICK, _create_mbox_autorcm_status);
// Set default state for AutoRCM and lock it out if patched unit.
if (get_autorcm_status(false))
lv_btn_set_state(btn3, LV_BTN_STATE_TGL_REL);
else
lv_btn_set_state(btn3, LV_BTN_STATE_REL);
nyx_generic_onoff_toggle(btn3);
if (h_cfg.rcm_patched)
{
lv_obj_set_click(btn3, false);
lv_btn_set_state(btn3, LV_BTN_STATE_INA);
}
autorcm_btn = btn3;
char *txt_buf = (char *)malloc(SZ_4K);
s_printf(txt_buf,
2020-04-30 11:30:37 +00:00
"Allows you to enter RCM without using #C7EA46 VOL+# & #C7EA46 HOME# (jig).\n"
"#FF8000 It can restore all versions of AutoRCM whenever requested.#\n"
2021-01-11 19:39:44 +00:00
"#FF3C28 This corrupts the BCT and you can't boot without a custom#\n"
"#FF3C28 bootloader.#");
if (h_cfg.rcm_patched)
2020-07-14 19:26:40 +00:00
strcat(txt_buf, " #FF8000 This is disabled because this unit is patched!#");
lv_obj_t *label_txt4 = lv_label_create(h2, NULL);
lv_label_set_recolor(label_txt4, true);
lv_label_set_text(label_txt4, txt_buf);
free(txt_buf);
lv_obj_set_style(label_txt4, &hint_small_style);
lv_obj_align(label_txt4, btn3, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
label_sep = lv_label_create(h2, NULL);
lv_label_set_static_text(label_sep, "");
lv_obj_align(label_sep, label_txt4, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI * 11 / 7);
// Create Dump Package1/2 button.
lv_obj_t *btn4 = lv_btn_create(h2, btn);
label_btn = lv_label_create(btn4, NULL);
lv_label_set_static_text(label_btn, SYMBOL_MODULES" Dump Package1/2");
lv_obj_align(btn4, label_txt4, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 2);
lv_btn_set_action(btn4, LV_BTN_ACTION_CLICK, _create_window_dump_pk12_tool);
label_txt2 = lv_label_create(h2, NULL);
lv_label_set_recolor(label_txt2, true);
lv_label_set_static_text(label_txt2,
"Allows you to dump and decrypt pkg1 and pkg2 and further\n"
"split it up into their individual parts. It also dumps the kip1.");
lv_obj_set_style(label_txt2, &hint_small_style);
lv_obj_align(label_txt2, btn4, LV_ALIGN_OUT_BOTTOM_LEFT, 0, LV_DPI / 3);
}
void create_tab_tools(lv_theme_t *th, lv_obj_t *parent)
{
lv_obj_t *tv = lv_tabview_create(parent, NULL);
lv_obj_set_size(tv, LV_HOR_RES, 572);
static lv_style_t tabview_style;
lv_style_copy(&tabview_style, th->tabview.btn.rel);
tabview_style.body.padding.ver = LV_DPI / 8;
lv_tabview_set_style(tv, LV_TABVIEW_STYLE_BTN_REL, &tabview_style);
if (hekate_bg)
{
lv_tabview_set_style(tv, LV_TABVIEW_STYLE_BTN_PR, &tabview_btn_pr);
lv_tabview_set_style(tv, LV_TABVIEW_STYLE_BTN_TGL_PR, &tabview_btn_tgl_pr);
}
lv_tabview_set_sliding(tv, false);
lv_tabview_set_btns_pos(tv, LV_TABVIEW_BTNS_POS_BOTTOM);
lv_obj_t *tab1= lv_tabview_add_tab(tv, "eMMC "SYMBOL_DOT" SD Partitions "SYMBOL_DOT" USB");
lv_obj_t *tab2 = lv_tabview_add_tab(tv, "Arch bit "SYMBOL_DOT" RCM "SYMBOL_DOT" Touch "SYMBOL_DOT" Pkg1/2");
2020-04-30 11:32:52 +00:00
lv_obj_t *line_sep = lv_line_create(tv, NULL);
static const lv_point_t line_pp[] = { {0, 0}, { 0, LV_DPI / 4} };
lv_line_set_points(line_sep, line_pp, 2);
lv_line_set_style(line_sep, lv_theme_get_current()->line.decor);
lv_obj_align(line_sep, tv, LV_ALIGN_IN_BOTTOM_MID, -1, -LV_DPI * 2 / 12);
_create_tab_tools_emmc_pkg12(th, tab1);
_create_tab_tools_arc_autorcm(th, tab2);
lv_tabview_set_tab_act(tv, 0, false);
}