diff --git a/source/config/config.c b/source/config/config.c new file mode 100644 index 0000000..f9df696 --- /dev/null +++ b/source/config/config.c @@ -0,0 +1,668 @@ +/* + * Copyright (c) 2018-2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "config.h" +#include "ini.h" +#include "../gfx/gfx.h" +#include "../gfx/tui.h" +#include "../libs/fatfs/ff.h" +#include "../soc/t210.h" +#include "../storage/sdmmc.h" +#include "../utils/btn.h" +#include "../utils/list.h" +#include "../utils/util.h" + +extern hekate_config h_cfg; +extern bool sd_mount(); +extern void sd_unmount(); + +void set_default_configuration() +{ + h_cfg.autoboot = 0; + h_cfg.autoboot_list = 0; + h_cfg.bootwait = 3; + h_cfg.verification = 1; + h_cfg.se_keygen_done = 0; + h_cfg.sbar_time_keeping = 0; + h_cfg.backlight = 100; + h_cfg.autohosoff = 0; + h_cfg.autonogc = 1; + h_cfg.brand = NULL; + h_cfg.tagline = NULL; + h_cfg.errors = 0; + h_cfg.sept_run = EMC(EMC_SCRATCH0) & EMC_SEPT_RUN; + h_cfg.rcm_patched = true; + h_cfg.emummc_force_disable = false; + + sd_power_cycle_time_start = 0xFFFFFFF; +} + +int create_config_entry() +{ + if (!sd_mount()) + return 1; + + char lbuf[32]; + FIL fp; + bool mainIniFound = false; + + LIST_INIT(ini_sections); + + if (ini_parse(&ini_sections, "bootloader/hekate_ipl.ini", false)) + mainIniFound = true; + else + { + u8 res = f_open(&fp, "bootloader/hekate_ipl.ini", FA_READ); + if (res == FR_NO_FILE || res == FR_NO_PATH) + { + f_mkdir("bootloader"); + f_mkdir("bootloader/ini"); + f_mkdir("bootloader/payloads"); + f_mkdir("bootloader/sys"); + } + else + { + if (!res) + f_close(&fp); + return 1; + } + } + + if (f_open(&fp, "bootloader/hekate_ipl.ini", FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) + return 1; + // Add config entry. + f_puts("[config]\nautoboot=", &fp); + itoa(h_cfg.autoboot, lbuf, 10); + f_puts(lbuf, &fp); + f_puts("\nautoboot_list=", &fp); + itoa(h_cfg.autoboot_list, lbuf, 10); + f_puts(lbuf, &fp); + f_puts("\nbootwait=", &fp); + itoa(h_cfg.bootwait, lbuf, 10); + f_puts(lbuf, &fp); + f_puts("\nverification=", &fp); + itoa(h_cfg.verification, lbuf, 10); + f_puts(lbuf, &fp); + f_puts("\nbacklight=", &fp); + itoa(h_cfg.backlight, lbuf, 10); + f_puts(lbuf, &fp); + f_puts("\nautohosoff=", &fp); + itoa(h_cfg.autohosoff, lbuf, 10); + f_puts(lbuf, &fp); + f_puts("\nautonogc=", &fp); + itoa(h_cfg.autonogc, lbuf, 10); + f_puts(lbuf, &fp); + if (h_cfg.brand) + { + f_puts("\nbrand=", &fp); + f_puts(h_cfg.brand, &fp); + } + if (h_cfg.tagline) + { + f_puts("\ntagline=", &fp); + f_puts(h_cfg.tagline, &fp); + } + f_puts("\n", &fp); + + if (mainIniFound) + { + // Re-construct existing entries. + LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sections, link) + { + if (!strcmp(ini_sec->name, "config")) + continue; + + switch (ini_sec->type) + { + case INI_CHOICE: // Re-construct Boot entry [ ]. + f_puts("[", &fp); + f_puts(ini_sec->name, &fp); + f_puts("]\n", &fp); + // Re-construct boot entry's config. + LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link) + { + f_puts(kv->key, &fp); + f_puts("=", &fp); + f_puts(kv->val, &fp); + f_puts("\n", &fp); + } + break; + case INI_CAPTION: // Re-construct caption entry { }. + f_puts("{", &fp); + f_puts(ini_sec->name, &fp); + f_puts("}\n", &fp); + break; + case INI_NEWLINE: // Re-construct cosmetic newline \n. + f_puts("\n", &fp); + break; + case INI_COMMENT: // Re-construct comment entry #. + f_puts("#", &fp); + f_puts(ini_sec->name, &fp); + f_puts("\n", &fp); + break; + } + } + } + + f_close(&fp); + sd_unmount(); + + return 0; +} + +#pragma GCC push_options +#pragma GCC optimize ("Os") + +static void _save_config() +{ + gfx_clear_grey(0x1B); + gfx_con_setpos(0, 0); + + if (!create_config_entry()) + gfx_puts("\nConfiguration was saved!\n"); + else + EPRINTF("\nConfiguration saving failed!"); + gfx_puts("\nPress any key..."); +} + +static void _config_autoboot_list(void *ent) +{ + gfx_clear_grey(0x1B); + gfx_con_setpos(0, 0); + + u32 *temp_autoboot = NULL; + + LIST_INIT(ini_sections); + + u8 max_entries = 30; + + ment_t *ments = (ment_t *)malloc(sizeof(ment_t) * (max_entries + 3)); + u32 *boot_values = (u32 *)malloc(sizeof(u32) * max_entries); + char *boot_text = (char *)malloc(512 * max_entries); + + for (u32 j = 0; j < max_entries; j++) + boot_values[j] = j; + + if (sd_mount()) + { + if (ini_parse(&ini_sections, "bootloader/ini", true)) + { + // Build configuration menu. + ments[0].type = MENT_BACK; + ments[0].caption = "Back"; + + ments[1].type = MENT_CHGLINE; + + u32 i = 2; + LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sections, link) + { + // Skip other ini entries for autoboot. + if (ini_sec->type == INI_CHOICE) + { + if (!strcmp(ini_sec->name, "config")) + continue; + + if (strlen(ini_sec->name) > 510) + ments[i].caption = ini_sec->name; + else + { + if (h_cfg.autoboot != (i - 1) || !h_cfg.autoboot_list) + boot_text[(i - 1) * 512] = ' '; + + else + boot_text[(i - 1) * 512] = '*'; + memcpy(boot_text + (i - 1) * 512 + 1, ini_sec->name, strlen(ini_sec->name) + 1); + boot_text[strlen(ini_sec->name) + (i - 1) * 512 + 1] = 0; + ments[i].caption = &boot_text[(i - 1) * 512]; + } + ments[i].type = ini_sec->type; + ments[i].data = &boot_values[i - 1]; + i++; + + if ((i - 1) > max_entries) + break; + } + } + + memset(&ments[i], 0, sizeof(ment_t)); + menu_t menu = {ments, "Select an entry to auto boot", 0, 0}; + temp_autoboot = (u32 *)tui_do_menu(&menu); + if (temp_autoboot != NULL) + { + h_cfg.autoboot = *(u32 *)temp_autoboot; + h_cfg.autoboot_list = 1; + _save_config(); + + ment_t *tmp = (ment_t *)ent; + tmp->data = NULL; + } + else + goto out2; + } + else + { + EPRINTF("Could not open 'bootloader/hekate_ipl.ini'.\nMake sure it exists!."); + goto out; + } + } + +out:; + btn_wait(); +out2:; + free(ments); + free(boot_values); + free(boot_text); + + sd_unmount(); +} + +void config_autoboot() +{ + gfx_clear_grey(0x1B); + gfx_con_setpos(0, 0); + + u32 *temp_autoboot = NULL; + + LIST_INIT(ini_sections); + + u8 max_entries = 30; + + ment_t *ments = (ment_t *)malloc(sizeof(ment_t) * (max_entries + 5)); + u32 *boot_values = (u32 *)malloc(sizeof(u32) * max_entries); + char *boot_text = (char *)malloc(512 * max_entries); + + for (u32 j = 0; j < max_entries; j++) + boot_values[j] = j; + + if (sd_mount()) + { + if (ini_parse(&ini_sections, "bootloader/hekate_ipl.ini", false)) + { + // Build configuration menu. + ments[0].type = MENT_BACK; + ments[0].caption = "Back"; + + ments[1].type = MENT_CHGLINE; + + ments[2].type = MENT_DATA; + if (!h_cfg.autoboot) + ments[2].caption = "*Disable"; + else + ments[2].caption = " Disable"; + ments[2].data = &boot_values[0]; + + ments[3].type = MENT_HDLR_RE; + if (h_cfg.autoboot_list) + ments[3].caption = "*More configs..."; + else + ments[3].caption = " More configs..."; + ments[3].handler = _config_autoboot_list; + ments[3].data = (void *)0xCAFE; + + ments[4].type = MENT_CHGLINE; + + u32 i = 5; + LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sections, link) + { + // Skip other ini entries for autoboot. + if (ini_sec->type == INI_CHOICE) + { + if (!strcmp(ini_sec->name, "config")) + continue; + + if (strlen(ini_sec->name) > 510) + ments[i].caption = ini_sec->name; + else + { + if (h_cfg.autoboot != (i - 4) || h_cfg.autoboot_list) + boot_text[(i - 4) * 512] = ' '; + + else + boot_text[(i - 4) * 512] = '*'; + memcpy(boot_text + (i - 4) * 512 + 1, ini_sec->name, strlen(ini_sec->name) + 1); + boot_text[strlen(ini_sec->name) + (i - 4) * 512 + 1] = 0; + ments[i].caption = &boot_text[(i - 4) * 512]; + } + ments[i].type = ini_sec->type; + ments[i].data = &boot_values[i - 4]; + i++; + + if ((i - 4) > max_entries) + break; + } + } + if (i < 6 && !h_cfg.autoboot_list) + { + ments[i].type = MENT_CAPTION; + ments[i].caption = "No main configurations found..."; + ments[i].color = 0xFFFFDD00; + i++; + } + + memset(&ments[i], 0, sizeof(ment_t)); + menu_t menu = {ments, "Disable or select entry to auto boot", 0, 0}; + temp_autoboot = (u32 *)tui_do_menu(&menu); + if (temp_autoboot != NULL) + { + h_cfg.autoboot = *(u32 *)temp_autoboot; + h_cfg.autoboot_list = 0; + _save_config(); + } + else + goto out2; + } + else + { + EPRINTF("Could not open 'bootloader/hekate_ipl.ini'.\nMake sure it exists!."); + goto out; + } + } + +out:; + btn_wait(); +out2:; + free(ments); + free(boot_values); + free(boot_text); + + sd_unmount(); + + if (temp_autoboot == NULL) + return; +} + +void config_bootdelay() +{ + gfx_clear_grey(0x1B); + gfx_con_setpos(0, 0); + + u32 delay_entries = 6; + + ment_t *ments = (ment_t *)malloc(sizeof(ment_t) * (delay_entries + 3)); + u32 *delay_values = (u32 *)malloc(sizeof(u32) * delay_entries); + char *delay_text = (char *)malloc(32 * delay_entries); + + for (u32 j = 0; j < delay_entries; j++) + delay_values[j] = j; + + ments[0].type = MENT_BACK; + ments[0].caption = "Back"; + + ments[1].type = MENT_CHGLINE; + + ments[2].type = MENT_DATA; + if (h_cfg.bootwait) + ments[2].caption = " 0 seconds (Bootlogo disabled)"; + else + ments[2].caption = "*0 seconds (Bootlogo disabled)"; + ments[2].data = &delay_values[0]; + + u32 i = 0; + for (i = 1; i < delay_entries; i++) + { + if (h_cfg.bootwait != i) + delay_text[i * 32] = ' '; + else + delay_text[i * 32] = '*'; + delay_text[i * 32 + 1] = i + '0'; + memcpy(delay_text + i * 32 + 2, " seconds", 9); + + ments[i + 2].type = MENT_DATA; + ments[i + 2].caption = delay_text + i * 32; + ments[i + 2].data = &delay_values[i]; + } + + memset(&ments[i + 2], 0, sizeof(ment_t)); + menu_t menu = {ments, "Time delay for entering bootloader menu", 0, 0}; + + u32 *temp_bootwait = (u32 *)tui_do_menu(&menu); + if (temp_bootwait != NULL) + { + h_cfg.bootwait = *(u32 *)temp_bootwait; + _save_config(); + } + + free(ments); + free(delay_values); + free(delay_text); + + if (temp_bootwait == NULL) + return; + btn_wait(); +} + +void config_verification() +{ + gfx_clear_grey(0x1B); + gfx_con_setpos(0, 0); + + ment_t *ments = (ment_t *)malloc(sizeof(ment_t) * 6); + u32 *vr_values = (u32 *)malloc(sizeof(u32) * 3); + char *vr_text = (char *)malloc(64 * 3); + + for (u32 j = 0; j < 3; j++) + { + vr_values[j] = j; + ments[j + 2].type = MENT_DATA; + ments[j + 2].data = &vr_values[j]; + } + + ments[0].type = MENT_BACK; + ments[0].caption = "Back"; + + ments[1].type = MENT_CHGLINE; + + memcpy(vr_text, " Disable (Fastest - Unsafe)", 28); + memcpy(vr_text + 64, " Sparse (Fast - Safe)", 23); + memcpy(vr_text + 128, " Full (Slow - Safe)", 23); + + for (u32 i = 0; i < 3; i++) + { + if (h_cfg.verification != i) + vr_text[64 * i] = ' '; + else + vr_text[64 * i] = '*'; + ments[2 + i].caption = vr_text + (i * 64); + } + + memset(&ments[5], 0, sizeof(ment_t)); + menu_t menu = {ments, "Backup & Restore verification", 0, 0}; + + u32 *temp_verification = (u32 *)tui_do_menu(&menu); + if (temp_verification != NULL) + { + h_cfg.verification = *(u32 *)temp_verification; + _save_config(); + } + + free(ments); + free(vr_values); + free(vr_text); + + if (temp_verification == NULL) + return; + btn_wait(); +} + +void config_backlight() +{ + gfx_clear_grey(0x1B); + gfx_con_setpos(0, 0); + + u32 bri_entries = 11; + + ment_t *ments = (ment_t *)malloc(sizeof(ment_t) * (bri_entries + 3)); + u32 *bri_values = (u32 *)malloc(sizeof(u32) * bri_entries); + char *bri_text = (char *)malloc(8 * bri_entries); + + for (u32 j = 1; j < bri_entries; j++) + bri_values[j] = j * 10; + + ments[0].type = MENT_BACK; + ments[0].caption = "Back"; + + ments[1].type = MENT_CHGLINE; + + u32 i = 0; + for (i = 1; i < bri_entries; i++) + { + if ((h_cfg.backlight / 20) != i) + bri_text[i * 32] = ' '; + else + bri_text[i * 32] = '*'; + + if (i < 10) + { + bri_text[i * 32 + 1] = i + '0'; + memcpy(bri_text + i * 32 + 2, "0%", 3); + } + else + memcpy(bri_text + i * 32 + 1, "100%", 5); + + ments[i + 1].type = MENT_DATA; + ments[i + 1].caption = bri_text + i * 32; + ments[i + 1].data = &bri_values[i]; + } + + memset(&ments[i + 1], 0, sizeof(ment_t)); + menu_t menu = {ments, "Backlight brightness", 0, 0}; + + u32 *temp_backlight = (u32 *)tui_do_menu(&menu); + if (temp_backlight != NULL) + { + h_cfg.backlight = (*(u32 *)temp_backlight) * 2; + _save_config(); + } + + free(ments); + free(bri_values); + free(bri_text); + + if (temp_backlight == NULL) + return; + btn_wait(); +} + +void config_auto_hos_poweroff() +{ + gfx_clear_grey(0x1B); + gfx_con_setpos(0, 0); + + ment_t *ments = (ment_t *)malloc(sizeof(ment_t) * 6); + u32 *hp_values = (u32 *)malloc(sizeof(u32) * 3); + + for (u32 j = 0; j < 3; j++) + { + hp_values[j] = j; + ments[j + 2].type = MENT_DATA; + ments[j + 2].data = &hp_values[j]; + } + + ments[0].type = MENT_BACK; + ments[0].caption = "Back"; + + ments[1].type = MENT_CHGLINE; + + if (h_cfg.autohosoff == 1) + { + ments[2].caption = " Disable"; + ments[3].caption = "*Enable"; + ments[4].caption = " Enable (No logo)"; + } + else if (h_cfg.autohosoff >= 2) + { + ments[2].caption = " Disable"; + ments[3].caption = " Enable"; + ments[4].caption = "*Enable (No logo)"; + } + else + { + ments[2].caption = "*Disable"; + ments[3].caption = " Enable"; + ments[4].caption = " Enable (No logo)"; + } + + memset(&ments[5], 0, sizeof(ment_t)); + menu_t menu = {ments, "Power off if woke up from HOS", 0, 0}; + + u32 *temp_autohosoff = (u32 *)tui_do_menu(&menu); + if (temp_autohosoff != NULL) + { + h_cfg.autohosoff = *(u32 *)temp_autohosoff; + _save_config(); + } + + free(ments); + free(hp_values); + + if (temp_autohosoff == NULL) + return; + btn_wait(); +} + +void config_nogc() +{ + gfx_clear_grey(0x1B); + gfx_con_setpos(0, 0); + + ment_t *ments = (ment_t *)malloc(sizeof(ment_t) * 5); + u32 *cb_values = (u32 *)malloc(sizeof(u32) * 2); + + for (u32 j = 0; j < 2; j++) + { + cb_values[j] = j; + ments[j + 2].type = MENT_DATA; + ments[j + 2].data = &cb_values[j]; + } + + ments[0].type = MENT_BACK; + ments[0].caption = "Back"; + + ments[1].type = MENT_CHGLINE; + + if (h_cfg.autonogc) + { + ments[2].caption = " Disable"; + ments[3].caption = "*Auto"; + } + else + { + ments[2].caption = "*Disable"; + ments[3].caption = " Auto"; + } + + memset(&ments[4], 0, sizeof(ment_t)); + menu_t menu = {ments, "No Gamecard", 0, 0}; + + u32 *temp_nogc = (u32 *)tui_do_menu(&menu); + if (temp_nogc != NULL) + { + h_cfg.autonogc = *(u32 *)temp_nogc; + _save_config(); + } + + free(ments); + free(cb_values); + + if (temp_nogc == NULL) + return; + btn_wait(); +} + +#pragma GCC pop_options diff --git a/source/config/config.h b/source/config/config.h new file mode 100644 index 0000000..8cd34e1 --- /dev/null +++ b/source/config/config.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018-2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#include "../utils/types.h" + +typedef struct _hekate_config +{ + // Non-volatile config. + u32 autoboot; + u32 autoboot_list; + u32 bootwait; + u32 verification; + u32 backlight; + u32 autohosoff; + u32 autonogc; + char *brand; + char *tagline; + // Global temporary config. + bool se_keygen_done; + bool sept_run; + bool emummc_force_disable; + bool rcm_patched; + u32 sbar_time_keeping; + u32 errors; +} hekate_config; + +typedef enum +{ + ERR_LIBSYS_LP0 = (1 << 0), +} hsysmodule_t; + +void set_default_configuration(); +int create_config_entry(); +void config_autoboot(); +void config_bootdelay(); +void config_verification(); +void config_backlight(); +void config_auto_hos_poweroff(); +void config_nogc(); + +#endif /* _CONFIG_H_ */ diff --git a/source/config/ini.c b/source/config/ini.c new file mode 100644 index 0000000..f033994 --- /dev/null +++ b/source/config/ini.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (C) 2018-2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "ini.h" +#include "../libs/fatfs/ff.h" +#include "../mem/heap.h" +#include "../utils/dirlist.h" + +static char *_strdup(char *str) +{ + if (!str) + return NULL; + + // Remove starting space. + if (str[0] == ' ' && strlen(str)) + str++; + + char *res = (char *)malloc(strlen(str) + 1); + strcpy(res, str); + + // Remove trailing space. + if (strlen(res) && res[strlen(res) - 1] == ' ') + res[strlen(res) - 1] = 0; + + return res; +} + +u32 _find_section_name(char *lbuf, u32 lblen, char schar) +{ + u32 i; + for (i = 0; i < lblen && lbuf[i] != schar && lbuf[i] != '\n' && lbuf[i] != '\r'; i++) + ; + lbuf[i] = 0; + + return i; +} + +ini_sec_t *_ini_create_section(link_t *dst, ini_sec_t *csec, char *name, u8 type) +{ + if (csec) + { + list_append(dst, &csec->link); + csec = NULL; + } + + csec = (ini_sec_t *)malloc(sizeof(ini_sec_t)); + csec->name = _strdup(name); + csec->type = type; + + return csec; +} + +int ini_parse(link_t *dst, char *ini_path, bool is_dir) +{ + u32 lblen; + u32 pathlen = strlen(ini_path); + u32 k = 0; + char lbuf[512]; + char *filelist = NULL; + FIL fp; + ini_sec_t *csec = NULL; + + char *filename = (char *)malloc(256); + + memcpy(filename, ini_path, pathlen + 1); + + // Get all ini filenames. + if (is_dir) + { + filelist = dirlist(filename, "*.ini", false); + if (!filelist) + { + free(filename); + return 0; + } + memcpy(filename + pathlen, "/", 2); + pathlen++; + } + + do + { + // Copy ini filename in path string. + if (is_dir) + { + if (filelist[k * 256]) + { + memcpy(filename + pathlen, &filelist[k * 256], strlen(&filelist[k * 256]) + 1); + k++; + } + else + break; + } + + // Open ini. + if (f_open(&fp, filename, FA_READ) != FR_OK) + { + free(filelist); + free(filename); + + return 0; + } + + do + { + // Fetch one line. + lbuf[0] = 0; + f_gets(lbuf, 512, &fp); + lblen = strlen(lbuf); + + // Remove trailing newline. + if (lbuf[lblen - 1] == '\n' || lbuf[lblen - 1] == '\r') + lbuf[lblen - 1] = 0; + + if (lblen > 2 && lbuf[0] == '[') // Create new section. + { + _find_section_name(lbuf, lblen, ']'); + + csec = _ini_create_section(dst, csec, &lbuf[1], INI_CHOICE); + list_init(&csec->kvs); + } + else if (lblen > 2 && lbuf[0] == '{') //Create new caption. + { + _find_section_name(lbuf, lblen, '}'); + + csec = _ini_create_section(dst, csec, &lbuf[1], INI_CAPTION); + csec->color = 0xFF0AB9E6; + } + else if (lblen > 2 && lbuf[0] == '#') //Create empty lines and comments. + { + _find_section_name(lbuf, lblen, '\0'); + + csec = _ini_create_section(dst, csec, &lbuf[1], INI_COMMENT); + } + else if (lblen < 2) + { + csec = _ini_create_section(dst, csec, NULL, INI_NEWLINE); + } + else if (csec && csec->type == INI_CHOICE) //Extract key/value. + { + u32 i = _find_section_name(lbuf, lblen, '='); + + ini_kv_t *kv = (ini_kv_t *)malloc(sizeof(ini_kv_t)); + kv->key = _strdup(&lbuf[0]); + kv->val = _strdup(&lbuf[i + 1]); + list_append(&csec->kvs, &kv->link); + } + } while (!f_eof(&fp)); + + f_close(&fp); + + if (csec) + { + list_append(dst, &csec->link); + if (is_dir) + csec = NULL; + } + } while (is_dir); + + free(filename); + free(filelist); + + return 1; +} + +char *ini_check_payload_section(ini_sec_t *cfg) +{ + if (cfg == NULL) + return NULL; + + LIST_FOREACH_ENTRY(ini_kv_t, kv, &cfg->kvs, link) + { + if (!strcmp("payload", kv->key)) + return kv->val; + } + + return NULL; +} diff --git a/source/config/ini.h b/source/config/ini.h new file mode 100644 index 0000000..318adf0 --- /dev/null +++ b/source/config/ini.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (C) 2018 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _INI_H_ +#define _INI_H_ + +#include "../utils/types.h" +#include "../utils/list.h" + +#define INI_CHOICE 3 +#define INI_CAPTION 5 +#define INI_CHGLINE 6 +#define INI_NEWLINE 0xFE +#define INI_COMMENT 0xFF + +typedef struct _ini_kv_t +{ + char *key; + char *val; + link_t link; +} ini_kv_t; + +typedef struct _ini_sec_t +{ + char *name; + link_t kvs; + link_t link; + u32 type; + u32 color; +} ini_sec_t; + +int ini_parse(link_t *dst, char *ini_path, bool is_dir); +char *ini_check_payload_section(ini_sec_t *cfg); + +#endif + diff --git a/source/keys/keys.c b/source/keys/keys.c index 9f3a135..6572449 100644 --- a/source/keys/keys.c +++ b/source/keys/keys.c @@ -15,6 +15,8 @@ */ #include "keys.h" + +#include "../config/config.h" #include "../gfx/di.h" #include "../gfx/gfx.h" #include "../hos/pkg1.h" @@ -45,16 +47,21 @@ extern bool sd_mount(); extern void sd_unmount(); extern int sd_save_to_file(void *buf, u32 size, const char *filename); +extern hekate_config h_cfg; + u32 _key_count = 0; sdmmc_storage_t storage; emmc_part_t *system_part; +u32 start_time, end_time; #define TPRINTF(text) \ - end_time = get_tmr_ms(); \ - gfx_printf(text" done @ %d.%03ds\n", (end_time - start_time) / 1000, (end_time - start_time) % 1000) + end_time = get_tmr_us(); \ + gfx_printf(text" done in %d us\n", end_time - start_time); \ + start_time = get_tmr_us() #define TPRINTFARGS(text, args...) \ - end_time = get_tmr_ms(); \ - gfx_printf(text" done @ %d.%03ds\n", args, (end_time - start_time) / 1000, (end_time - start_time) % 1000) + end_time = get_tmr_us(); \ + gfx_printf(text" done in %d us\n", args, end_time - start_time); \ + start_time = get_tmr_us() #define SAVE_KEY(name, src, len) _save_key(name, src, len, text_buffer) #define SAVE_KEY_FAMILY(name, src, count, len) _save_key_family(name, src, count, len, text_buffer) @@ -104,14 +111,15 @@ void dump_keys() { gfx_printf("[%kLo%kck%kpi%kck%k_R%kCM%k v%d.%d.%d%k]\n\n", colors[0], colors[1], colors[2], colors[3], colors[4], colors[5], 0xFFFF00FF, LP_VER_MJ, LP_VER_MN, LP_VER_BF, 0xFFCCCCCC); - u32 start_time = get_tmr_ms(), - end_time, - retries = 0; + start_time = get_tmr_us(); + u32 retries = 0; + u32 color_idx = 0; tsec_ctxt_t tsec_ctxt; sdmmc_t sdmmc; sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4); + TPRINTFARGS("%kMMC init... ", colors[(color_idx++) % 6]); // Read package1. u8 *pkg1 = (u8 *)malloc(0x40000); @@ -151,7 +159,7 @@ void dump_keys() { f_rename("sd:/sept/payload.bak", "sd:/sept/payload.bin"); } - if (!(EMC(EMC_SCRATCH0) & EMC_SEPT_RUN)) { + if (!h_cfg.sept_run) { // bundle lp0 fw for sept instead of loading it from SD as hekate does sdram_lp0_save_params(sdram_get_params_patched()); FIL fp; @@ -167,8 +175,10 @@ void dump_keys() { u32 payload_size = *(u32 *)(IPL_LOAD_ADDR + 0x84) - IPL_LOAD_ADDR; f_write(&fp, (u8 *)IPL_LOAD_ADDR, payload_size, NULL); f_close(&fp); - gfx_printf("%kFirmware 7.x or higher detected.\n%kRenamed /sept/payload.bin", colors[0], colors[1]); - gfx_printf("\n%k to /sept/payload.bak\n%kCopied self to /sept/payload.bin",colors[2], colors[3]); + gfx_printf("%k\nFirmware 7.x or higher detected.\n%kRenamed /sept/payload.bin", colors[(color_idx) % 6], colors[(color_idx + 1) % 6]); + color_idx += 2; + gfx_printf("\n%k to /sept/payload.bak\n%kCopied self to /sept/payload.bin", colors[(color_idx) % 6], colors[(color_idx + 1) % 6]); + color_idx += 2; sdmmc_storage_end(&storage); if (!reboot_to_sept((u8 *)tsec_ctxt.fw, tsec_ctxt.size, pkg1_id->kb)) goto out_wait; @@ -207,7 +217,7 @@ get_tsec: ; goto out_wait; } - TPRINTFARGS("%kTSEC key(s)... ", colors[0]); + TPRINTFARGS("%kTSEC key(s)... ", colors[(color_idx++) % 6]); // Master key derivation if (pkg1_id->kb == KB_FIRMWARE_VERSION_620 && _key_exists(tsec_keys + 0x10)) { @@ -288,7 +298,7 @@ get_tsec: ; } free(keyblob_block); - TPRINTFARGS("%kMaster keys... ", colors[1]); + TPRINTFARGS("%kMaster keys... ", colors[(color_idx++) % 6]); /* key = unwrap(source, wrapped_key): key_set(ks, wrapped_key), block_ecb(ks, 0, key, source) -> final key in key @@ -378,7 +388,7 @@ get_tsec: ; goto pkg2_done; } - TPRINTFARGS("%kDecrypt pkg2... ", colors[2]); + TPRINTFARGS("%kDecrypt pkg2... ", colors[(color_idx++) % 6]); LIST_INIT(kip1_info); bool new_pkg2; @@ -399,7 +409,7 @@ get_tsec: ; } pkg2_decompress_kip(ki, 2 | 4); // we only need .rodata and .data - TPRINTFARGS("%kDecompress FS...", colors[3]); + TPRINTFARGS("%kDecompress FS...", colors[(color_idx++) % 6]); u8 hash_index = 0, hash_max = 9, hash_order[10], key_lengths[10] = {0x10, 0x20, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20}; @@ -497,7 +507,7 @@ pkg2_done: free(pkg2); free(ki); - TPRINTFARGS("%kFS keys... ", colors[4]); + TPRINTFARGS("%kFS keys... ", colors[(color_idx++) % 6]); if (_key_exists(fs_keys[0]) && _key_exists(fs_keys[1]) && _key_exists(master_key[0])) { _generate_kek(8, fs_keys[0], master_key[0], aes_kek_generation_source, aes_key_generation_source); @@ -699,6 +709,12 @@ pkg2_done: f_closedir(&dir); free(dec_header); + if (memcmp(pkg1_id->id, "2016", 4)) { + TPRINTFARGS("%kES & SSL keys...", colors[(color_idx++) % 6]); + } else { + TPRINTFARGS("%kSSL keys... ", colors[(color_idx++) % 6]); + } + if (f_open(&fp, "sd:/Nintendo/Contents/private", FA_READ | FA_OPEN_EXISTING)) { EPRINTF("Unable to locate SD seed. Skipping."); goto dismount; @@ -728,17 +744,13 @@ pkg2_done: } f_close(&fp); + TPRINTFARGS("%kSD Seed... ", colors[(color_idx++) % 6]); + dismount: f_mount(NULL, "emmc:", 1); nx_emmc_gpt_free(&gpt); sdmmc_storage_end(&storage); - if (memcmp(pkg1_id->id, "2016", 4)) { - TPRINTFARGS("%kES & SSL keys...", colors[5]); - } else { - TPRINTFARGS("%kSSL keys... ", colors[5]); - } - // derive eticket_rsa_kek and ssl_rsa_kek if (_key_exists(es_keys[0]) && _key_exists(es_keys[1]) && _key_exists(master_key[0])) { for (u32 i = 0; i < 0x10; i++) @@ -816,7 +828,8 @@ key_output: ; //gfx_con.fntsz = 8; gfx_puts(text_buffer); gfx_con.fntsz = 16; - TPRINTFARGS("\n%kFound %d keys.\n%kLockpick totally", colors[0], _key_count, colors[1]); + TPRINTFARGS("\n%kFound %d keys.\n%kLockpick totally", colors[(color_idx) % 6], _key_count, colors[(color_idx + 1) % 6]); + color_idx += 2; f_mkdir("switch"); char keyfile_path[30] = "sd:/switch/"; @@ -825,13 +838,13 @@ key_output: ; else sprintf(&keyfile_path[11], "dev.keys"); if (!sd_save_to_file(text_buffer, strlen(text_buffer), keyfile_path) && !f_stat(keyfile_path, &fno)) { - gfx_printf("%kWrote %d bytes to %s\n", colors[2], (u32)fno.fsize, keyfile_path); + gfx_printf("%kWrote %d bytes to %s\n", colors[(color_idx++) % 6], (u32)fno.fsize, keyfile_path); } else EPRINTF("Failed to save keys to SD."); sd_unmount(); out_wait: - gfx_printf("\n%kVOL + -> Reboot to RCM\n%kVOL - -> Reboot normally\n%kPower -> Power off", colors[3], colors[4], colors[5]); + gfx_printf("\n%kVOL + -> Reboot to RCM\n%kVOL - -> Reboot normally\n%kPower -> Power off", colors[(color_idx) % 6], colors[(color_idx + 1) % 6], colors[(color_idx + 2) % 6]); u32 btn = btn_wait(); if (btn & BTN_VOL_UP) diff --git a/source/main.c b/source/main.c index 587d930..fb03dc7 100644 --- a/source/main.c +++ b/source/main.c @@ -18,6 +18,7 @@ #include +#include "config/config.h" #include "gfx/di.h" #include "gfx/gfx.h" #include "libs/fatfs/ff.h" @@ -36,6 +37,7 @@ sdmmc_storage_t sd_storage; __attribute__ ((aligned (16))) FATFS sd_fs; static bool sd_mounted; +hekate_config h_cfg; boot_cfg_t __attribute__((section ("._boot_cfg"))) b_cfg; bool sd_mount() @@ -154,6 +156,8 @@ void ipl_main() pivot_stack(IPL_STACK_TOP); heap_init(IPL_HEAP_START); + set_default_configuration(); + display_init(); u32 *fb = display_init_framebuffer(); gfx_init_ctxt(fb, 720, 1280, 720); diff --git a/source/utils/dirlist.c b/source/utils/dirlist.c new file mode 100644 index 0000000..8e6f3cc --- /dev/null +++ b/source/utils/dirlist.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "../libs/fatfs/ff.h" +#include "../mem/heap.h" +#include "../utils/types.h" + +char *dirlist(const char *directory, const char *pattern, bool includeHiddenFiles) +{ + u8 max_entries = 61; + + int res = 0; + u32 i = 0, j = 0, k = 0; + DIR dir; + FILINFO fno; + + char *dir_entries = (char *)calloc(max_entries, 256); + char *temp = (char *)calloc(1, 256); + + if (!pattern && !f_opendir(&dir, directory)) + { + for (;;) + { + res = f_readdir(&dir, &fno); + if (res || !fno.fname[0]) + break; + if (!(fno.fattrib & AM_DIR) && (fno.fname[0] != '.') && (includeHiddenFiles || !(fno.fattrib & AM_HID))) + { + memcpy(dir_entries + (k * 256), fno.fname, strlen(fno.fname) + 1); + k++; + if (k > (max_entries - 1)) + break; + } + } + f_closedir(&dir); + } + else if (pattern && !f_findfirst(&dir, &fno, directory, pattern) && fno.fname[0]) + { + do + { + if (!(fno.fattrib & AM_DIR) && (fno.fname[0] != '.') && (includeHiddenFiles || !(fno.fattrib & AM_HID))) + { + memcpy(dir_entries + (k * 256), fno.fname, strlen(fno.fname) + 1); + k++; + if (k > (max_entries - 1)) + break; + } + res = f_findnext(&dir, &fno); + } while (fno.fname[0] && !res); + f_closedir(&dir); + } + + if (!k) + { + free(temp); + free(dir_entries); + + return NULL; + } + + // Reorder ini files by ASCII ordering. + for (i = 0; i < k - 1 ; i++) + { + for (j = i + 1; j < k; j++) + { + if (strcmp(&dir_entries[i * 256], &dir_entries[j * 256]) > 0) + { + memcpy(temp, &dir_entries[i * 256], strlen(&dir_entries[i * 256]) + 1); + memcpy(&dir_entries[i * 256], &dir_entries[j * 256], strlen(&dir_entries[j * 256]) + 1); + memcpy(&dir_entries[j * 256], temp, strlen(temp) + 1); + } + } + } + + free(temp); + + return dir_entries; +} diff --git a/source/utils/dirlist.h b/source/utils/dirlist.h new file mode 100644 index 0000000..4fb9af7 --- /dev/null +++ b/source/utils/dirlist.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2018 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "../utils/types.h" + +char *dirlist(const char *directory, const char *pattern, bool includeHiddenFiles);