2019-06-30 01:24:58 +01:00
|
|
|
/*
|
2021-02-06 01:19:42 +00:00
|
|
|
* Copyright (c) 2019-2021 CTCaer
|
2019-06-30 01:24:58 +01:00
|
|
|
*
|
|
|
|
* 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 <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include "emummc.h"
|
2020-06-14 14:45:45 +01:00
|
|
|
#include <storage/sdmmc.h>
|
|
|
|
#include "../config.h"
|
|
|
|
#include <utils/ini.h>
|
|
|
|
#include <gfx_utils.h>
|
|
|
|
#include <libs/fatfs/ff.h>
|
|
|
|
#include <mem/heap.h>
|
|
|
|
#include "../storage/nx_emmc.h"
|
|
|
|
#include <storage/nx_sd.h>
|
|
|
|
#include <utils/list.h>
|
|
|
|
#include <utils/types.h>
|
2019-06-30 01:24:58 +01:00
|
|
|
|
|
|
|
extern hekate_config h_cfg;
|
2020-06-14 11:32:04 +01:00
|
|
|
emummc_cfg_t emu_cfg = { 0 };
|
2020-05-08 23:32:44 +01:00
|
|
|
|
2019-06-30 01:24:58 +01:00
|
|
|
void emummc_load_cfg()
|
|
|
|
{
|
|
|
|
emu_cfg.enabled = 0;
|
|
|
|
emu_cfg.path = NULL;
|
|
|
|
emu_cfg.sector = 0;
|
|
|
|
emu_cfg.id = 0;
|
|
|
|
emu_cfg.file_based_part_size = 0;
|
|
|
|
emu_cfg.active_part = 0;
|
|
|
|
emu_cfg.fs_ver = 0;
|
2020-06-14 11:32:04 +01:00
|
|
|
if (!emu_cfg.nintendo_path)
|
|
|
|
emu_cfg.nintendo_path = (char *)malloc(0x80);
|
2020-04-27 07:30:53 +01:00
|
|
|
if (!emu_cfg.emummc_file_based_path)
|
|
|
|
emu_cfg.emummc_file_based_path = (char *)malloc(0x80);
|
2019-06-30 01:24:58 +01:00
|
|
|
|
2020-06-14 11:32:04 +01:00
|
|
|
emu_cfg.nintendo_path[0] = 0;
|
|
|
|
emu_cfg.emummc_file_based_path[0] = 0;
|
|
|
|
|
2019-06-30 01:24:58 +01:00
|
|
|
LIST_INIT(ini_sections);
|
|
|
|
if (ini_parse(&ini_sections, "emuMMC/emummc.ini", false))
|
|
|
|
{
|
|
|
|
LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sections, link)
|
|
|
|
{
|
|
|
|
if (ini_sec->type == INI_CHOICE)
|
|
|
|
{
|
|
|
|
if (strcmp(ini_sec->name, "emummc"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link)
|
|
|
|
{
|
|
|
|
if (!strcmp("enabled", kv->key))
|
|
|
|
emu_cfg.enabled = atoi(kv->val);
|
|
|
|
else if (!strcmp("sector", kv->key))
|
|
|
|
emu_cfg.sector = strtol(kv->val, NULL, 16);
|
|
|
|
else if (!strcmp("id", kv->key))
|
|
|
|
emu_cfg.id = strtol(kv->val, NULL, 16);
|
|
|
|
else if (!strcmp("path", kv->key))
|
|
|
|
emu_cfg.path = kv->val;
|
|
|
|
else if (!strcmp("nintendo_path", kv->key))
|
2020-06-14 11:32:04 +01:00
|
|
|
strcpy(emu_cfg.nintendo_path, kv->val);
|
2019-06-30 01:24:58 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 17:23:31 +01:00
|
|
|
bool emummc_set_path(char *path)
|
2020-04-27 07:30:53 +01:00
|
|
|
{
|
|
|
|
FIL fp;
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
strcpy(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)
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
{
|
|
|
|
emu_cfg.enabled = 1;
|
2021-09-20 09:41:48 +01:00
|
|
|
|
|
|
|
// Get ID from path.
|
|
|
|
u32 id_from_path = 0;
|
|
|
|
u32 path_size = strlen(path);
|
|
|
|
if (path_size >= 4)
|
|
|
|
memcpy(&id_from_path, path + path_size - 4, 4);
|
|
|
|
emu_cfg.id = id_from_path;
|
|
|
|
|
2020-04-27 07:30:53 +01:00
|
|
|
strcpy(emu_cfg.nintendo_path, path);
|
2020-05-05 17:23:31 +01:00
|
|
|
strcat(emu_cfg.nintendo_path, "/Nintendo");
|
2020-04-27 07:30:53 +01:00
|
|
|
}
|
2020-05-05 17:23:31 +01:00
|
|
|
|
|
|
|
return found;
|
2020-04-27 07:30:53 +01:00
|
|
|
}
|
|
|
|
|
2019-06-30 01:24:58 +01:00
|
|
|
static int emummc_raw_get_part_off(int part_idx)
|
|
|
|
{
|
|
|
|
switch (part_idx)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return 2;
|
|
|
|
case 1:
|
|
|
|
return 0;
|
|
|
|
case 2:
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2021-02-06 01:19:42 +00:00
|
|
|
int emummc_storage_init_mmc()
|
2019-06-30 01:24:58 +01:00
|
|
|
{
|
|
|
|
FILINFO fno;
|
2020-06-14 11:31:16 +01:00
|
|
|
emu_cfg.active_part = 0;
|
|
|
|
|
2020-07-17 16:00:32 +01:00
|
|
|
// Always init eMMC even when in emuMMC. eMMC is needed from the emuMMC driver anyway.
|
2021-02-06 01:19:42 +00:00
|
|
|
if (!sdmmc_storage_init_mmc(&emmc_storage, &emmc_sdmmc, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_HS400))
|
2019-12-04 15:46:12 +00:00
|
|
|
return 2;
|
2019-06-30 01:24:58 +01:00
|
|
|
|
2020-06-14 11:31:16 +01:00
|
|
|
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
|
2019-12-04 15:46:12 +00:00
|
|
|
return 0;
|
2019-06-30 01:24:58 +01:00
|
|
|
|
|
|
|
if (!sd_mount())
|
|
|
|
goto out;
|
|
|
|
|
2020-06-14 11:31:16 +01:00
|
|
|
if (!emu_cfg.sector)
|
2019-06-30 01:24:58 +01:00
|
|
|
{
|
|
|
|
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))
|
|
|
|
{
|
2019-09-12 21:08:38 +01:00
|
|
|
EPRINTF("Failed to open eMMC folder.");
|
2019-06-30 01:24:58 +01:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
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))
|
|
|
|
{
|
2019-09-12 21:08:38 +01:00
|
|
|
EPRINTF("Failed to open emuMMC rawnand.");
|
2019-06-30 01:24:58 +01:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
emu_cfg.file_based_part_size = fno.fsize >> 9;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2019-12-04 15:46:12 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
return 1;
|
2019-06-30 01:24:58 +01:00
|
|
|
}
|
|
|
|
|
2021-02-06 01:19:42 +00:00
|
|
|
int emummc_storage_end()
|
2019-06-30 01:24:58 +01:00
|
|
|
{
|
2020-06-14 11:32:04 +01:00
|
|
|
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
|
2021-02-06 01:19:42 +00:00
|
|
|
sdmmc_storage_end(&emmc_storage);
|
2020-06-14 11:32:04 +01:00
|
|
|
else
|
|
|
|
sd_end();
|
2019-06-30 01:24:58 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-06 01:19:42 +00:00
|
|
|
int emummc_storage_read(u32 sector, u32 num_sectors, void *buf)
|
2019-06-30 01:24:58 +01:00
|
|
|
{
|
|
|
|
FIL fp;
|
|
|
|
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
|
2021-02-06 01:19:42 +00:00
|
|
|
return sdmmc_storage_read(&emmc_storage, sector, num_sectors, buf);
|
2019-06-30 01:24:58 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
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))
|
|
|
|
{
|
2019-09-12 21:11:17 +01:00
|
|
|
EPRINTF("Failed to open emuMMC image.");
|
2019-06-30 01:24:58 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
f_lseek(&fp, (u64)sector << 9);
|
|
|
|
if (f_read(&fp, buf, (u64)num_sectors << 9, NULL))
|
|
|
|
{
|
2019-09-12 21:11:17 +01:00
|
|
|
EPRINTF("Failed to read emuMMC image.");
|
2019-06-30 01:24:58 +01:00
|
|
|
f_close(&fp);
|
|
|
|
return 0;
|
|
|
|
}
|
2019-10-18 16:02:06 +01:00
|
|
|
|
2019-06-30 01:24:58 +01:00
|
|
|
f_close(&fp);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-06 01:19:42 +00:00
|
|
|
int emummc_storage_write(u32 sector, u32 num_sectors, void *buf)
|
2019-06-30 01:24:58 +01:00
|
|
|
{
|
|
|
|
FIL fp;
|
|
|
|
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
|
2021-02-06 01:19:42 +00:00
|
|
|
return sdmmc_storage_write(&emmc_storage, sector, num_sectors, buf);
|
2019-06-30 01:24:58 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2021-02-06 01:19:42 +00:00
|
|
|
|
2019-06-30 01:24:58 +01:00
|
|
|
if (f_open(&fp, emu_cfg.emummc_file_based_path, FA_WRITE))
|
|
|
|
return 0;
|
2021-02-06 01:19:42 +00:00
|
|
|
|
2019-06-30 01:24:58 +01:00
|
|
|
f_lseek(&fp, (u64)sector << 9);
|
|
|
|
if (f_write(&fp, buf, (u64)num_sectors << 9, NULL))
|
|
|
|
{
|
|
|
|
f_close(&fp);
|
|
|
|
return 0;
|
|
|
|
}
|
2019-10-18 16:02:06 +01:00
|
|
|
|
2019-06-30 01:24:58 +01:00
|
|
|
f_close(&fp);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-06 01:19:42 +00:00
|
|
|
int emummc_storage_set_mmc_partition(u32 partition)
|
2019-06-30 01:24:58 +01:00
|
|
|
{
|
|
|
|
emu_cfg.active_part = partition;
|
2021-02-06 01:19:42 +00:00
|
|
|
sdmmc_storage_set_mmc_partition(&emmc_storage, partition);
|
2019-06-30 01:24:58 +01:00
|
|
|
|
2021-02-06 01:19:42 +00:00
|
|
|
if (!emu_cfg.enabled || h_cfg.emummc_force_disable || emu_cfg.sector)
|
2019-06-30 01:24:58 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|