Nyx: emuMMC Manage window, Tools UI, and misc updates

- Add gui_emu_tools: emuMMC Manage window with correct positioning (LV_PROTECT_PARENT + re-parent to win)
- Tools: single SD button (tap = SD partition manager, 3s hold = eMMC)
- Remove emuSD from Nyx UI (tabs, UMS, partition manager); keep bootloader emusd
- Shorten Create emuMMC description text by one character
- Storage/build/config and dependency updates

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-20 20:49:48 +01:00
parent 4eead2c14d
commit fed7f05831
81 changed files with 6932 additions and 3462 deletions

View File

@@ -14,6 +14,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <storage/emmc.h>
#include <storage/sd.h>
#include <storage/sdmmc.h>
#include <string.h>
#include <stdlib.h>
@@ -22,6 +25,7 @@
#include "emummc.h"
#include "../config.h"
#include <libs/fatfs/ff.h>
#include <storage/emummc_file_based.h>
emummc_cfg_t emu_cfg = { 0 };
@@ -76,33 +80,68 @@ bool emummc_set_path(char *path)
FIL fp;
bool found = false;
strcpy(emu_cfg.emummc_file_based_path, path);
// strcpy(emu_cfg.emummc_file_based_path, "sd:");
strcpy(emu_cfg.emummc_file_based_path, "");
strcat(emu_cfg.emummc_file_based_path, path);
strcat(emu_cfg.emummc_file_based_path, "/raw_based");
if (!f_open(&fp, emu_cfg.emummc_file_based_path, FA_READ))
{
if (!f_read(&fp, &emu_cfg.sector, 4, NULL))
if (emu_cfg.sector)
if (!f_read(&fp, &emu_cfg.sector, 4, NULL)){
if (emu_cfg.sector){
found = true;
}
else
{
strcpy(emu_cfg.emummc_file_based_path, path);
strcat(emu_cfg.emummc_file_based_path, "/file_based");
if (!f_stat(emu_cfg.emummc_file_based_path, NULL))
{
emu_cfg.sector = 0;
emu_cfg.path = path;
found = true;
emu_cfg.enabled = 1;
goto out;
}
}
}
if (found)
strcpy(emu_cfg.emummc_file_based_path, "");
strcat(emu_cfg.emummc_file_based_path, path);
strcat(emu_cfg.emummc_file_based_path, "/raw_emmc_based");
if (!f_open(&fp, emu_cfg.emummc_file_based_path, FA_READ))
{
if (!f_read(&fp, &emu_cfg.sector, 4, NULL)){
if (emu_cfg.sector){
found = true;
emu_cfg.enabled = 4;
goto out;
}
}
}
// strcpy(emu_cfg.emummc_file_based_path, "sd:");
strcpy(emu_cfg.emummc_file_based_path, "");
strcat(emu_cfg.emummc_file_based_path, path);
strcat(emu_cfg.emummc_file_based_path, "/file_based");
if (!f_stat(emu_cfg.emummc_file_based_path, NULL))
{
emu_cfg.sector = 0;
emu_cfg.path = path;
emu_cfg.enabled = 1;
found = true;
goto out;
}
// strcpy(emu_cfg.emummc_file_based_path, "sd:");
strcpy(emu_cfg.emummc_file_based_path, "");
strcat(emu_cfg.emummc_file_based_path, path);
strcat(emu_cfg.emummc_file_based_path, "/file_emmc_based");
if (!f_stat(emu_cfg.emummc_file_based_path, NULL))
{
emu_cfg.sector = 0;
emu_cfg.path = path;
emu_cfg.enabled = 4;
found = true;
goto out;
}
out:
if (found)
{
// Get ID from path.
u32 id_from_path = 0;
u32 path_size = strlen(path);
@@ -133,7 +172,7 @@ static int emummc_raw_get_part_off(int part_idx)
int emummc_storage_init_mmc()
{
FILINFO fno;
// FILINFO fno;
emu_cfg.active_part = 0;
// Always init eMMC even when in emuMMC. eMMC is needed from the emuMMC driver anyway.
@@ -143,86 +182,80 @@ int emummc_storage_init_mmc()
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
return 0;
if (!sd_mount())
goto out;
bool file_based = false;
if (!emu_cfg.sector)
{
strcpy(emu_cfg.emummc_file_based_path, emu_cfg.path);
strcat(emu_cfg.emummc_file_based_path, "/eMMC");
if (f_stat(emu_cfg.emummc_file_based_path, &fno))
{
EPRINTF("Failed to open eMMC folder.");
goto out;
if(emu_cfg.enabled == 4){
// emmc based
if(!emu_cfg.sector){
// file based
if(!emmc_mount()){
gfx_printf("emmc mount fail\n");
return 1;
}
strcpy(emu_cfg.emummc_file_based_path, "emmc:");
strcat(emu_cfg.emummc_file_based_path, emu_cfg.path);
strcat(emu_cfg.emummc_file_based_path, "/eMMC/");
file_based = true;
}else{
// raw based
// emmc already initialized
}
f_chmod(emu_cfg.emummc_file_based_path, AM_ARC, AM_ARC);
strcat(emu_cfg.emummc_file_based_path, "/00");
if (f_stat(emu_cfg.emummc_file_based_path, &fno))
{
EPRINTF("Failed to open emuMMC rawnand.");
goto out;
}else{
// sd based
if(!emu_cfg.sector){
// file based
if(!sd_mount()){
return 1;
}
strcpy(emu_cfg.emummc_file_based_path, "sd:");
strcat(emu_cfg.emummc_file_based_path, emu_cfg.path);
strcat(emu_cfg.emummc_file_based_path, "/eMMC/");
file_based = true;
}else{
// raw based
if(!sd_initialize(false)){
return 1;
}
}
emu_cfg.file_based_part_size = fno.fsize >> 9;
}
if(file_based){
gfx_printf("file based\n");
return emummc_storage_file_based_init(emu_cfg.emummc_file_based_path) == 0;
}
return 0;
out:
return 1;
}
int emummc_storage_end()
{
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
if(!h_cfg.emummc_force_disable && emu_cfg.enabled && !emu_cfg.sector){
emummc_storage_file_based_end();
}
if(!emu_cfg.enabled || h_cfg.emummc_force_disable || emu_cfg.enabled == 4){
emmc_end();
else
}else{
sd_end();
}
return 1;
}
int emummc_storage_read(u32 sector, u32 num_sectors, void *buf)
{
FIL fp;
sdmmc_storage_t *storage = emu_cfg.enabled == 4 ? &emmc_storage : &sd_storage;
// FIL fp;
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
return sdmmc_storage_read(&emmc_storage, sector, num_sectors, buf);
else if (emu_cfg.sector)
{
sector += emu_cfg.sector;
sector += emummc_raw_get_part_off(emu_cfg.active_part) * 0x2000;
return sdmmc_storage_read(&sd_storage, sector, num_sectors, buf);
return sdmmc_storage_read(storage, sector, num_sectors, buf);
}
else
{
if (!emu_cfg.active_part)
{
u32 file_part = sector / emu_cfg.file_based_part_size;
sector = sector % emu_cfg.file_based_part_size;
if (file_part >= 10)
itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 2, 10);
else
{
emu_cfg.emummc_file_based_path[strlen(emu_cfg.emummc_file_based_path) - 2] = '0';
itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 1, 10);
}
}
if (f_open(&fp, emu_cfg.emummc_file_based_path, FA_READ))
{
EPRINTF("Failed to open emuMMC image.");
return 0;
}
f_lseek(&fp, (u64)sector << 9);
if (f_read(&fp, buf, (u64)num_sectors << 9, NULL))
{
EPRINTF("Failed to read emuMMC image.");
f_close(&fp);
return 0;
}
f_close(&fp);
return 1;
return emummc_storage_file_based_read(sector, num_sectors, buf);
}
return 1;
@@ -230,72 +263,44 @@ int emummc_storage_read(u32 sector, u32 num_sectors, void *buf)
int emummc_storage_write(u32 sector, u32 num_sectors, void *buf)
{
FIL fp;
sdmmc_storage_t *storage = emu_cfg.enabled == 4 ? &emmc_storage : &sd_storage;
// FIL fp;
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
return sdmmc_storage_write(&emmc_storage, sector, num_sectors, buf);
else if (emu_cfg.sector)
{
sector += emu_cfg.sector;
sector += emummc_raw_get_part_off(emu_cfg.active_part) * 0x2000;
return sdmmc_storage_write(&sd_storage, sector, num_sectors, buf);
return sdmmc_storage_write(storage, sector, num_sectors, buf);
}
else
{
if (!emu_cfg.active_part)
{
u32 file_part = sector / emu_cfg.file_based_part_size;
sector = sector % emu_cfg.file_based_part_size;
if (file_part >= 10)
itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 2, 10);
else
{
emu_cfg.emummc_file_based_path[strlen(emu_cfg.emummc_file_based_path) - 2] = '0';
itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 1, 10);
}
}
if (f_open(&fp, emu_cfg.emummc_file_based_path, FA_WRITE))
return 0;
f_lseek(&fp, (u64)sector << 9);
if (f_write(&fp, buf, (u64)num_sectors << 9, NULL))
{
f_close(&fp);
return 0;
}
f_close(&fp);
return 1;
return emummc_storage_file_based_write(sector, num_sectors, buf);
}
}
int emummc_storage_set_mmc_partition(u32 partition)
{
emu_cfg.active_part = partition;
emmc_set_partition(partition);
if (!emu_cfg.enabled || h_cfg.emummc_force_disable || emu_cfg.sector)
return 1;
else
{
strcpy(emu_cfg.emummc_file_based_path, emu_cfg.path);
strcat(emu_cfg.emummc_file_based_path, "/eMMC");
switch (partition)
{
case 0:
strcat(emu_cfg.emummc_file_based_path, "/00");
break;
case 1:
strcat(emu_cfg.emummc_file_based_path, "/BOOT0");
break;
case 2:
strcat(emu_cfg.emummc_file_based_path, "/BOOT1");
break;
}
if(h_cfg.emummc_force_disable || !emu_cfg.enabled){
emmc_set_partition(partition);
return 1;
}
if(!emu_cfg.sector){
emummc_storage_file_base_set_partition(partition);
return 1;
}
if(emu_cfg.enabled != 4){
emmc_set_partition(partition);
}else{
emmc_set_partition(EMMC_GPP);
}
return 1;
}
sdmmc_storage_t *emummc_get_storage(){
return emu_cfg.enabled == 4 ? &emmc_storage : &sd_storage;
}

View File

@@ -19,19 +19,6 @@
#include <bdk.h>
typedef enum
{
EMUMMC_TYPE_NONE = 0,
EMUMMC_TYPE_PARTITION = 1,
EMUMMC_TYPE_FILES = 2,
} emummc_type_t;
typedef enum {
EMUMMC_MMC_NAND = 0,
EMUMMC_MMC_SD = 1,
EMUMMC_MMC_GC = 2,
} emummc_mmc_t;
typedef struct _emummc_cfg_t
{
int enabled;
@@ -55,5 +42,6 @@ int emummc_storage_end();
int emummc_storage_read(u32 sector, u32 num_sectors, void *buf);
int emummc_storage_write(u32 sector, u32 num_sectors, void *buf);
int emummc_storage_set_mmc_partition(u32 partition);
sdmmc_storage_t *emummc_get_storage();
#endif

295
bootloader/storage/emusd.c Normal file
View File

@@ -0,0 +1,295 @@
#include "emusd.h"
#include <bdk.h>
#include <storage/boot_storage.h>
#include <storage/emmc.h>
#include <storage/file_based_storage.h>
#include <storage/sd.h>
#include <storage/sdmmc.h>
#include <string.h>
#include <utils/ini.h>
#include <libs/fatfs/ff.h>
#include "../config.h"
#include "gfx_utils.h"
FATFS emusd_fs;
bool emusd_mounted = false;
bool emusd_initialized = false;
emusd_cfg_t emu_sd_cfg = {0};
extern hekate_config h_cfg;
static bool emusd_get_mounted() {
return emusd_mounted;
}
void emusd_load_cfg()
{
emu_sd_cfg.enabled = 0;
emu_sd_cfg.fs_ver = 0;
emu_sd_cfg.id = 0;
emu_sd_cfg.sector = 0;
if(!emu_sd_cfg.path) {
emu_sd_cfg.path = (char*)malloc(0x200);
}
if(!emu_sd_cfg.emummc_file_based_path){
emu_sd_cfg.emummc_file_based_path = (char*)malloc(0x200);
}
LIST_INIT(ini_sections);
if(ini_parse(&ini_sections, "emuSD/emusd.ini", false))
{
LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sections, link)
{
if(ini_sec->type == INI_CHOICE)
{
if(strcmp(ini_sec->name, "emusd")){
continue;
}
LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link)
{
if(!strcmp(kv->key, "enabled")){
emu_sd_cfg.enabled = atoi(kv->val);
} else if(!strcmp(kv->key, "sector")){
emu_sd_cfg.sector = strtol(kv->val, NULL, 16);
} else if(!strcmp(kv->key, "path")){
emu_sd_cfg.path = kv->val;
}
}
break;
}
}
}
}
bool emusd_mount() {
if (emusd_mounted) {
return true;
}
if (emusd_storage_init_mmc()){
return false;
}
int res = f_mount(&emusd_fs, "emusd:", 1);
if(res) {
EPRINTFARGS("emusd mount fail %d", res);
return false;
}
return true;
}
bool emusd_unmount() {
f_mount(NULL, "emusd:", 1);
emusd_mounted = false;
return true;
}
bool emusd_set_path(char *path) {
FIL fp;
bool found = false;
// TODO: use emu_sd.file_path instead
strcat(emu_sd_cfg.emummc_file_based_path, "");
strcpy(emu_sd_cfg.emummc_file_based_path, path);
strcat(emu_sd_cfg.emummc_file_based_path, "/raw_emmc_based");
if(!f_open(&fp, emu_sd_cfg.emummc_file_based_path, FA_READ))
{
if(!f_read(&fp, &emu_sd_cfg.sector, 4, NULL)){
if(emu_sd_cfg.sector){
found = true;
emu_sd_cfg.enabled = 4;
goto out;
}
}
}
strcpy(emu_sd_cfg.emummc_file_based_path, "");
strcat(emu_sd_cfg.emummc_file_based_path, path);
strcat(emu_sd_cfg.emummc_file_based_path, "/raw_emmc_based");
if (!f_open(&fp, emu_sd_cfg.emummc_file_based_path, FA_READ))
{
if (!f_read(&fp, &emu_sd_cfg.sector, 4, NULL)){
if (emu_sd_cfg.sector){
found = true;
emu_sd_cfg.enabled = 4;
goto out;
}
}
}
// strcpy(emu_sd_cfg.emummc_file_based_path, "sd:");
strcpy(emu_sd_cfg.emummc_file_based_path, "");
strcat(emu_sd_cfg.emummc_file_based_path, path);
strcat(emu_sd_cfg.emummc_file_based_path, "/file_based");
if (!f_stat(emu_sd_cfg.emummc_file_based_path, NULL))
{
emu_sd_cfg.sector = 0;
emu_sd_cfg.path = path;
emu_sd_cfg.enabled = 1;
found = true;
goto out;
}
// strcpy(emu_sd_cfg.emummc_file_based_path, "sd:");
strcpy(emu_sd_cfg.emummc_file_based_path, "");
strcat(emu_sd_cfg.emummc_file_based_path, path);
strcat(emu_sd_cfg.emummc_file_based_path, "/file_emmc_based");
if (!f_stat(emu_sd_cfg.emummc_file_based_path, NULL))
{
emu_sd_cfg.sector = 0;
emu_sd_cfg.path = path;
emu_sd_cfg.enabled = 4;
found = true;
goto out;
}
out:
return found;
}
int emusd_storage_init_mmc() {
if(!emusd_initialized) {
if (!emu_sd_cfg.enabled || h_cfg.emummc_force_disable) {
if(!sd_initialize(false)){
return 1;
}
emusd_initialized = true;
return 0;
}
bool file_based = false;
if(emu_sd_cfg.enabled == 4){
if(!emu_sd_cfg.sector){
//file based
if(!emmc_mount()){
return 1;
}
strcpy(emu_sd_cfg.emummc_file_based_path, "emmc:");
strcat(emu_sd_cfg.emummc_file_based_path, emu_sd_cfg.path);
strcat(emu_sd_cfg.emummc_file_based_path, "/SD/");
file_based = true;
}else{
if(!emmc_initialize(false)){
return 1;
}
}
}else{
if(!emu_sd_cfg.sector){
//file based
if(!sd_mount()){
return 1;
}
strcpy(emu_sd_cfg.emummc_file_based_path, "sd:");
strcat(emu_sd_cfg.emummc_file_based_path, emu_sd_cfg.path);
strcat(emu_sd_cfg.emummc_file_based_path, "/SD/");
file_based = true;
}else{
if(!sd_initialize(false)){
return 1;
}
}
}
if(file_based){
return file_based_storage_init(emu_sd_cfg.emummc_file_based_path) == 0;
}
emusd_initialized = true;
}
return 0;
}
int emusd_storage_end() {
if(!h_cfg.emummc_force_disable && emu_sd_cfg.enabled && !emu_sd_cfg.sector){
file_based_storage_end();
}
if(!emu_sd_cfg.enabled || h_cfg.emummc_force_disable || emu_sd_cfg.enabled == 1){
sd_unmount();
}else{
emmc_unmount();
}
emusd_initialized = false;
return 0;
}
int emusd_storage_write(u32 sector, u32 num_sectors, void *buf) {
if(!emu_sd_cfg.enabled || h_cfg.emummc_force_disable){
return sdmmc_storage_write(&sd_storage, sector, num_sectors, buf);
}else if(emu_sd_cfg.sector){
sdmmc_storage_t *storage = emu_sd_cfg.enabled == 1 ? &sd_storage : &emmc_storage;
sector += emu_sd_cfg.sector;
return sdmmc_storage_write(storage, sector, num_sectors, buf);
}else{
return file_based_storage_write(sector, num_sectors, buf);
}
}
int emusd_storage_read(u32 sector, u32 num_sectors, void *buf) {
if(!emu_sd_cfg.enabled || h_cfg.emummc_force_disable){
return sdmmc_storage_read(&sd_storage, sector, num_sectors, buf);
}else if(emu_sd_cfg.sector){
sdmmc_storage_t *storage = emu_sd_cfg.enabled == 1 ? &sd_storage : &emmc_storage;
sector += emu_sd_cfg.sector;
return sdmmc_storage_read(storage, sector, num_sectors, buf);
}else{
return file_based_storage_read(sector, num_sectors, buf);
}
}
sdmmc_storage_t *emusd_get_storage() {
return emu_sd_cfg.enabled == 4 ? &emmc_storage : &sd_storage;
}
bool emusd_is_gpt() {
if(emu_sd_cfg.enabled) {
return emusd_fs.part_type;
} else {
return sd_fs.part_type;
}
}
int emusd_get_fs_type() {
if(emu_sd_cfg.enabled) {
return emusd_fs.fs_type;
} else {
return sd_fs.fs_type;
}
}
void *emusd_file_read(const char *path, u32 *fsize) {
if(emu_sd_cfg.enabled) {
char *path1 = malloc(0x100);
strcpy(path1, "emusd:");
strcat(path1, path);
FIL fp;
if (!emusd_get_mounted())
return NULL;
if (f_open(&fp, path, FA_READ) != FR_OK)
return NULL;
u32 size = f_size(&fp);
if (fsize)
*fsize = size;
void *buf = malloc(size);
if (f_read(&fp, buf, size, NULL) != FR_OK)
{
free(buf);
f_close(&fp);
return NULL;
}
f_close(&fp);
return buf;
} else {
return boot_storage_file_read(path, fsize);
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2019-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/>.
*/
#ifndef _EMUSD_H
#define _EMUSD_H
#include <bdk.h>
typedef struct _emusd_cfg_t
{
int enabled;
u64 sector;
u32 id;
char *path;
// Internal.
int fs_ver;
char *emummc_file_based_path;
} emusd_cfg_t;
extern emusd_cfg_t emu_sd_cfg;
void emusd_load_cfg();
bool emusd_set_path(char *path);
int emusd_storage_init_mmc();
int emusd_storage_end();
int emusd_storage_read(u32 sector, u32 num_sectors, void *buf);
int emusd_storage_write(u32 sector, u32 num_sectors, void *buf);
sdmmc_storage_t *emusd_get_storage();
bool emusd_is_gpt();
int emusd_get_fs_type();
bool emusd_mount();
bool emusd_unmount();
void *emusd_file_read(const char *path, u32 *fsize);
#endif