mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-09 22:56:35 +00:00
fusee: add automatic emummc injection support
This commit is contained in:
parent
4f8ab5c599
commit
8bd79e8299
14 changed files with 315 additions and 37 deletions
|
@ -59,6 +59,7 @@ typedef struct {
|
||||||
emummc_partition_config_t partition_cfg;
|
emummc_partition_config_t partition_cfg;
|
||||||
emummc_file_config_t file_cfg;
|
emummc_file_config_t file_cfg;
|
||||||
};
|
};
|
||||||
|
//char emu_dir_path[EMUMMC_FILE_PATH_MAX];
|
||||||
} exo_emummc_config_t;
|
} exo_emummc_config_t;
|
||||||
|
|
||||||
_Static_assert(sizeof(exo_emummc_config_t) == 0x90, "exo_emummc_config_t definition!");
|
_Static_assert(sizeof(exo_emummc_config_t) == 0x90, "exo_emummc_config_t definition!");
|
||||||
|
|
|
@ -77,7 +77,7 @@ int device_partition_write_data(device_partition_t *devpart, const void *src, ui
|
||||||
int emu_device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit)
|
int emu_device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
uint64_t target_sector = 0;
|
uint64_t target_sector = sector;
|
||||||
char target_path[0x300 + 1] = {0};
|
char target_path[0x300 + 1] = {0};
|
||||||
|
|
||||||
/* Perform initialization steps, if necessary. */
|
/* Perform initialization steps, if necessary. */
|
||||||
|
@ -150,7 +150,7 @@ int emu_device_partition_read_data(device_partition_t *devpart, void *dst, uint6
|
||||||
int emu_device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit)
|
int emu_device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
uint64_t target_sector = 0;
|
uint64_t target_sector = sector;
|
||||||
char target_path[0x300 + 1] = {0};
|
char target_path[0x300 + 1] = {0};
|
||||||
|
|
||||||
/* Perform initialization steps, if necessary. */
|
/* Perform initialization steps, if necessary. */
|
||||||
|
@ -214,7 +214,7 @@ int emu_device_partition_write_data(device_partition_t *devpart, const void *src
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Write partition data. */
|
/* Write partition data. */
|
||||||
rc = devpart->writer(devpart, src, sector, num_sectors);
|
rc = devpart->writer(devpart, src, target_sector, num_sectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "emu_dev.h"
|
#include "emu_dev.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
static int emudev_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
static int emudev_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
||||||
static int emudev_close(struct _reent *r, void *fd);
|
static int emudev_close(struct _reent *r, void *fd);
|
||||||
|
|
106
fusee/fusee-secondary/src/emummc_cfg.h
Normal file
106
fusee/fusee-secondary/src/emummc_cfg.h
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* 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 EXOSPHERE_EMUMMC_CONFIG_H
|
||||||
|
#define EXOSPHERE_EMUMMC_CONFIG_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <atmosphere.h>
|
||||||
|
|
||||||
|
/* "EFS0" */
|
||||||
|
#define MAGIC_EMUMMC_CONFIG (0x30534645)
|
||||||
|
|
||||||
|
#define EMUMMC_FILE_PATH_MAX (0x80)
|
||||||
|
|
||||||
|
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 enum {
|
||||||
|
FS_VER_1_0_0 = 0,
|
||||||
|
|
||||||
|
FS_VER_2_0_0,
|
||||||
|
FS_VER_2_0_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_2_1_0,
|
||||||
|
FS_VER_2_1_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_3_0_0,
|
||||||
|
FS_VER_3_0_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_3_0_1,
|
||||||
|
FS_VER_3_0_1_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_4_0_0,
|
||||||
|
FS_VER_4_0_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_4_1_0,
|
||||||
|
FS_VER_4_1_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_5_0_0,
|
||||||
|
FS_VER_5_0_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_5_1_0,
|
||||||
|
FS_VER_5_1_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_6_0_0,
|
||||||
|
FS_VER_6_0_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_7_0_0,
|
||||||
|
FS_VER_7_0_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_8_0_0,
|
||||||
|
FS_VER_8_0_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_MAX,
|
||||||
|
} emummc_fs_ver_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t magic;
|
||||||
|
uint32_t type;
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t fs_version;
|
||||||
|
} emummc_base_config_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t start_sector;
|
||||||
|
} emummc_partition_config_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char path[EMUMMC_FILE_PATH_MAX];
|
||||||
|
} emummc_file_config_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
emummc_base_config_t base_cfg;
|
||||||
|
union {
|
||||||
|
emummc_partition_config_t partition_cfg;
|
||||||
|
emummc_file_config_t file_cfg;
|
||||||
|
};
|
||||||
|
//char emu_dir_path[EMUMMC_FILE_PATH_MAX];
|
||||||
|
} exo_emummc_config_t;
|
||||||
|
|
||||||
|
_Static_assert(sizeof(exo_emummc_config_t) == 0x90, "exo_emummc_config_t definition!");
|
||||||
|
|
||||||
|
#endif
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <atmosphere.h>
|
#include <atmosphere.h>
|
||||||
|
#include "emummc_cfg.h"
|
||||||
|
|
||||||
/* This serves to set configuration for *exosphere itself*, separate from the SecMon Exosphere mimics. */
|
/* This serves to set configuration for *exosphere itself*, separate from the SecMon Exosphere mimics. */
|
||||||
|
|
||||||
|
@ -35,11 +36,11 @@ typedef struct {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
uint32_t target_firmware;
|
uint32_t target_firmware;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint32_t reserved;
|
uint32_t reserved[5];
|
||||||
uint64_t emunand_config;
|
exo_emummc_config_t emummc_cfg;
|
||||||
} exosphere_config_t;
|
} exosphere_config_t;
|
||||||
|
|
||||||
_Static_assert(sizeof(exosphere_config_t) == 0x18, "exosphere config definition");
|
_Static_assert(sizeof(exosphere_config_t) == 0x20 + sizeof(exo_emummc_config_t), "exosphere config definition");
|
||||||
|
|
||||||
#define MAILBOX_EXOSPHERE_CONFIGURATION ((volatile exosphere_config_t *)(0x8000F000ull))
|
#define MAILBOX_EXOSPHERE_CONFIGURATION ((volatile exosphere_config_t *)(0x8000F000ull))
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "exocfg.h"
|
||||||
#include "se.h"
|
#include "se.h"
|
||||||
#include "lib/log.h"
|
#include "lib/log.h"
|
||||||
#include "ips.h"
|
#include "ips.h"
|
||||||
|
@ -343,7 +344,7 @@ static void kip1_blz_uncompress(void *hdr_end) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static kip1_header_t *kip1_uncompress(kip1_header_t *kip, size_t *size) {
|
kip1_header_t *kip1_uncompress(kip1_header_t *kip, size_t *size) {
|
||||||
kip1_header_t new_header = *kip;
|
kip1_header_t new_header = *kip;
|
||||||
for (unsigned int i = 0; i < 3; i++) {
|
for (unsigned int i = 0; i < 3; i++) {
|
||||||
new_header.section_headers[i].compressed_size = new_header.section_headers[i].out_size;
|
new_header.section_headers[i].compressed_size = new_header.section_headers[i].out_size;
|
||||||
|
@ -372,10 +373,60 @@ static kip1_header_t *kip1_uncompress(kip1_header_t *kip, size_t *size) {
|
||||||
return (kip1_header_t *)new_kip;
|
return (kip1_header_t *)new_kip;
|
||||||
}
|
}
|
||||||
|
|
||||||
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size) {
|
static const uint8_t g_fs_hashes[FS_VER_MAX][0x8] = {
|
||||||
|
"\xde\x9f\xdd\xa4\x08\x5d\xd5\xfe", /* FS_VER_1_0_0 */
|
||||||
|
|
||||||
|
"\xcd\x7b\xbe\x18\xd6\x13\x0b\x28", /* FS_VER_2_0_0 */
|
||||||
|
"\xe7\x66\x92\xdf\xaa\x04\x20\xe9", /* FS_VER_2_0_0_EXFAT */
|
||||||
|
|
||||||
|
"\x0d\x70\x05\x62\x7b\x07\x76\x7c", /* FS_VER_2_1_0 */
|
||||||
|
"\xdb\xd8\x5f\xca\xcc\x19\x3d\xa8", /* FS_VER_2_1_0_EXFAT */
|
||||||
|
|
||||||
|
"\xa8\x6d\xa5\xe8\x7e\xf1\x09\x7b", /* FS_VER_3_0_0 */
|
||||||
|
"\x98\x1c\x57\xe7\xf0\x2f\x70\xf7", /* FS_VER_3_0_0_EXFAT */
|
||||||
|
|
||||||
|
"\x57\x39\x7c\x06\x3f\x10\xb6\x31", /* FS_VER_3_0_1 */
|
||||||
|
"\x07\x30\x99\xd7\xc6\xad\x7d\x89", /* FS_VER_3_0_1_EXFAT */
|
||||||
|
|
||||||
|
"\x06\xe9\x07\x19\x59\x5a\x01\x0c", /* FS_VER_4_0_0 */
|
||||||
|
"\x54\x9b\x0f\x8d\x6f\x72\xc4\xe9", /* FS_VER_4_0_0_EXFAT */
|
||||||
|
|
||||||
|
"\x80\x96\xaf\x7c\x6a\x35\xaa\x82", /* FS_VER_4_1_0 */
|
||||||
|
"\x02\xd5\xab\xaa\xfd\x20\xc8\xb0", /* FS_VER_4_1_0_EXFAT */
|
||||||
|
|
||||||
|
"\xa6\xf2\x7a\xd9\xac\x7c\x73\xad", /* FS_VER_5_0_0 */
|
||||||
|
"\xce\x3e\xcb\xa2\xf2\xf0\x62\xf5", /* FS_VER_5_0_0_EXFAT */
|
||||||
|
|
||||||
|
"\x76\xf8\x74\x02\xc9\x38\x7c\x0f", /* FS_VER_5_1_0 */
|
||||||
|
"\x10\xb2\xd8\x16\x05\x48\x85\x99", /* FS_VER_5_1_0_EXFAT */
|
||||||
|
|
||||||
|
"\x3a\x57\x4d\x43\x61\x86\x19\x1d", /* FS_VER_6_0_0 */
|
||||||
|
"\x33\x05\x53\xf6\xb5\xfb\x55\xc4", /* FS_VER_6_0_0_EXFAT */
|
||||||
|
|
||||||
|
"\x2A\xDB\xE9\x7E\x9B\x5F\x41\x77", /* FS_VER_7_0_0 */
|
||||||
|
"\x2C\xCE\x65\x9C\xEC\x53\x6A\x8E", /* FS_VER_7_0_0_EXFAT */
|
||||||
|
|
||||||
|
"\xB2\xF5\x17\x6B\x35\x48\x36\x4D", /* FS_VER_8_0_0 */
|
||||||
|
"\xDB\xD9\x41\xC0\xC5\x3C\x52\xCC", /* FS_VER_8_0_0_EXFAT */
|
||||||
|
};
|
||||||
|
|
||||||
|
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size, emummc_fs_ver_t *out_fs_ver) {
|
||||||
uint8_t hash[0x20];
|
uint8_t hash[0x20];
|
||||||
se_calculate_sha256(hash, kip, kip_size);
|
se_calculate_sha256(hash, kip, kip_size);
|
||||||
|
|
||||||
|
if (kip->title_id == FS_TITLE_ID) {
|
||||||
|
bool found = false;
|
||||||
|
for (size_t i = 0; i < FS_VER_MAX; i++) {
|
||||||
|
if (memcmp(hash, g_fs_hashes[i], 0x8) == 0) {
|
||||||
|
found = true;
|
||||||
|
*out_fs_ver = (emummc_fs_ver_t)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
fatal_error("[NXBOOT]: Failed to identify FS version...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!has_needed_default_kip_patches(kip->title_id, hash, sizeof(hash))) {
|
if (!has_needed_default_kip_patches(kip->title_id, hash, sizeof(hash))) {
|
||||||
fatal_error("[NXBOOT]: Missing default patch for KIP %08x%08x...\n", (uint32_t)(kip->title_id >> 32), (uint32_t)kip->title_id);
|
fatal_error("[NXBOOT]: Missing default patch for KIP %08x%08x...\n", (uint32_t)(kip->title_id >> 32), (uint32_t)kip->title_id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,16 @@
|
||||||
#ifndef FUSEE_IPS_H
|
#ifndef FUSEE_IPS_H
|
||||||
#define FUSEE_IPS_H
|
#define FUSEE_IPS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "kip.h"
|
#include "kip.h"
|
||||||
#include <stdint.h>
|
#include "exocfg.h"
|
||||||
|
|
||||||
|
#define FS_TITLE_ID 0x0100000000000000ull
|
||||||
|
|
||||||
void apply_kernel_ips_patches(void *kernel, size_t kernel_size);
|
void apply_kernel_ips_patches(void *kernel, size_t kernel_size);
|
||||||
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size);
|
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size, emummc_fs_ver_t *out_fs_ver);
|
||||||
|
kip1_header_t *kip1_uncompress(kip1_header_t *kip, size_t *size);
|
||||||
|
|
||||||
void kip_patches_set_enable_nogc(void);
|
void kip_patches_set_enable_nogc(void);
|
||||||
|
|
||||||
|
|
|
@ -109,8 +109,8 @@ static int emummc_ini_handler(void *user, const char *section, const char *name,
|
||||||
emummc_cfg->enabled = (tmp != 0);
|
emummc_cfg->enabled = (tmp != 0);
|
||||||
}
|
}
|
||||||
if (strcmp(name, EMUMMC_SECTOR_KEY) == 0) {
|
if (strcmp(name, EMUMMC_SECTOR_KEY) == 0) {
|
||||||
uint64_t sector = 0;
|
uintptr_t sector = 0;
|
||||||
sscanf(value, "%llu", §or);
|
sscanf(value, "%x", §or);
|
||||||
emummc_cfg->sector = sector;
|
emummc_cfg->sector = sector;
|
||||||
} else if (strcmp(name, EMUMMC_PATH_KEY) == 0) {
|
} else if (strcmp(name, EMUMMC_PATH_KEY) == 0) {
|
||||||
strncpy(emummc_cfg->path, value, sizeof(emummc_cfg->path) - 1);
|
strncpy(emummc_cfg->path, value, sizeof(emummc_cfg->path) - 1);
|
||||||
|
@ -208,7 +208,7 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nxboot_configure_emummc() {
|
static bool nxboot_configure_emummc(exo_emummc_config_t *exo_emummc_config) {
|
||||||
emummc_config_t emummc_cfg = {.enabled = false, .sector = -1, .path = ""};
|
emummc_config_t emummc_cfg = {.enabled = false, .sector = -1, .path = ""};
|
||||||
|
|
||||||
/* Load emummc settings from BCT.ini file. */
|
/* Load emummc settings from BCT.ini file. */
|
||||||
|
@ -216,13 +216,25 @@ static bool nxboot_configure_emummc() {
|
||||||
fatal_error("[NXBOOT] Failed to parse BCT.ini!\n");
|
fatal_error("[NXBOOT] Failed to parse BCT.ini!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(exo_emummc_config, 0, sizeof(*exo_emummc_config));
|
||||||
|
exo_emummc_config->base_cfg.magic = MAGIC_EMUMMC_CONFIG;
|
||||||
|
exo_emummc_config->base_cfg.type = EMUMMC_TYPE_NONE;
|
||||||
|
exo_emummc_config->base_cfg.id = 0; /* TODO: Multiple ID support. */
|
||||||
|
exo_emummc_config->base_cfg.fs_version = FS_VER_1_0_0; /* Will be filled out later. */
|
||||||
|
|
||||||
if (emummc_cfg.enabled) {
|
if (emummc_cfg.enabled) {
|
||||||
if (emummc_cfg.sector >= 0) {
|
if (emummc_cfg.sector >= 0) {
|
||||||
|
exo_emummc_config->base_cfg.type = EMUMMC_TYPE_PARTITION;
|
||||||
|
exo_emummc_config->partition_cfg.start_sector = emummc_cfg.sector;
|
||||||
|
|
||||||
/* Mount emulated NAND from SD card partition. */
|
/* Mount emulated NAND from SD card partition. */
|
||||||
if (nxfs_mount_emummc_partition(emummc_cfg.sector) < 0) {
|
if (nxfs_mount_emummc_partition(emummc_cfg.sector) < 0) {
|
||||||
fatal_error("[NXBOOT] Failed to mount EmuMMC from SD card partition!\n");
|
fatal_error("[NXBOOT] Failed to mount EmuMMC from SD card partition!\n");
|
||||||
}
|
}
|
||||||
} else if (is_valid_folder(emummc_cfg.path)) {
|
} else if (is_valid_folder(emummc_cfg.path)) {
|
||||||
|
exo_emummc_config->base_cfg.type = EMUMMC_TYPE_FILES;
|
||||||
|
strncpy(exo_emummc_config->file_cfg.path, emummc_cfg.path, sizeof(exo_emummc_config->file_cfg.path) - 1);
|
||||||
|
|
||||||
int num_parts = 0;
|
int num_parts = 0;
|
||||||
uint64_t part_limit = 0;
|
uint64_t part_limit = 0;
|
||||||
char emummc_boot0_path[0x300 + 1] = {0};
|
char emummc_boot0_path[0x300 + 1] = {0};
|
||||||
|
@ -267,11 +279,13 @@ static bool nxboot_configure_emummc() {
|
||||||
return emummc_cfg.enabled;
|
return emummc_cfg.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int keygen_type) {
|
static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int keygen_type, exo_emummc_config_t *exo_emummc_cfg) {
|
||||||
exosphere_config_t exo_cfg = {0};
|
exosphere_config_t exo_cfg = {0};
|
||||||
|
|
||||||
exo_cfg.magic = MAGIC_EXOSPHERE_CONFIG;
|
exo_cfg.magic = MAGIC_EXOSPHERE_CONFIG;
|
||||||
exo_cfg.target_firmware = target_firmware;
|
exo_cfg.target_firmware = target_firmware;
|
||||||
|
memcpy(&exo_cfg.emummc_cfg, exo_emummc_cfg, sizeof(*exo_emummc_cfg));
|
||||||
|
|
||||||
if (keygen_type) {
|
if (keygen_type) {
|
||||||
exo_cfg.flags = EXOSPHERE_FLAGS_DEFAULT | EXOSPHERE_FLAG_PERFORM_620_KEYGEN;
|
exo_cfg.flags = EXOSPHERE_FLAGS_DEFAULT | EXOSPHERE_FLAG_PERFORM_620_KEYGEN;
|
||||||
} else {
|
} else {
|
||||||
|
@ -421,15 +435,35 @@ uint32_t nxboot_main(void) {
|
||||||
void *warmboot_memaddr;
|
void *warmboot_memaddr;
|
||||||
void *package1loader;
|
void *package1loader;
|
||||||
size_t package1loader_size;
|
size_t package1loader_size;
|
||||||
|
void *emummc_kip;
|
||||||
|
size_t emummc_kip_size;
|
||||||
uint32_t available_revision;
|
uint32_t available_revision;
|
||||||
FILE *boot0, *pk2file;
|
FILE *boot0, *pk2file;
|
||||||
void *exosphere_memaddr;
|
void *exosphere_memaddr;
|
||||||
|
exo_emummc_config_t exo_emummc_cfg;
|
||||||
|
|
||||||
/* Configure emummc or mount the real NAND. */
|
/* Configure emummc or mount the real NAND. */
|
||||||
if (!nxboot_configure_emummc()) {
|
if (!nxboot_configure_emummc(&exo_emummc_cfg)) {
|
||||||
|
emummc_kip = NULL;
|
||||||
|
emummc_kip_size = 0;
|
||||||
if (nxfs_mount_emmc() < 0) {
|
if (nxfs_mount_emmc() < 0) {
|
||||||
fatal_error("[NXBOOT] Failed to mount eMMC!\n");
|
fatal_error("[NXBOOT] Failed to mount eMMC!\n");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
emummc_kip_size = get_file_size("atmosphere/emummc.kip");
|
||||||
|
if (emummc_kip_size == 0) {
|
||||||
|
fatal_error("[NXBOOT] Could not read emummc kip!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate memory for the TSEC firmware. */
|
||||||
|
emummc_kip = memalign(0x100, emummc_kip_size);
|
||||||
|
|
||||||
|
if (emummc_kip == NULL) {
|
||||||
|
fatal_error("[NXBOOT] Out of memory!\n");
|
||||||
|
}
|
||||||
|
if (read_from_file(emummc_kip, emummc_kip_size, "atmosphere/emummc.kip") != emummc_kip_size) {
|
||||||
|
fatal_error("[NXBOOT] Could not read the emummc kip!\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate memory for reading Package2. */
|
/* Allocate memory for reading Package2. */
|
||||||
|
@ -562,7 +596,7 @@ uint32_t nxboot_main(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setup boot configuration for Exosphère. */
|
/* Setup boot configuration for Exosphère. */
|
||||||
nxboot_configure_exosphere(target_firmware, keygen_type);
|
nxboot_configure_exosphere(target_firmware, keygen_type, &exo_emummc_cfg);
|
||||||
|
|
||||||
/* Initialize Boot Reason on older firmware versions. */
|
/* Initialize Boot Reason on older firmware versions. */
|
||||||
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) {
|
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) {
|
||||||
|
@ -644,7 +678,10 @@ uint32_t nxboot_main(void) {
|
||||||
print(SCREEN_LOG_LEVEL_INFO, u8"[NXBOOT] Configured Stratosphere...\n");
|
print(SCREEN_LOG_LEVEL_INFO, u8"[NXBOOT] Configured Stratosphere...\n");
|
||||||
|
|
||||||
/* Patch package2, adding Thermosphère + custom KIPs. */
|
/* Patch package2, adding Thermosphère + custom KIPs. */
|
||||||
package2_rebuild_and_copy(package2, MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware);
|
package2_rebuild_and_copy(package2, MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware, emummc_kip, emummc_kip_size);
|
||||||
|
|
||||||
|
/* Set detected FS version. */
|
||||||
|
MAILBOX_EXOSPHERE_CONFIGURATION->emummc_cfg.base_cfg.fs_version = stratosphere_get_fs_version();
|
||||||
|
|
||||||
print(SCREEN_LOG_LEVEL_INFO, u8"[NXBOOT] Reading Exosphère...\n");
|
print(SCREEN_LOG_LEVEL_INFO, u8"[NXBOOT] Reading Exosphère...\n");
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,8 @@ static bool g_fsdev_ready = false;
|
||||||
static bool g_rawdev_ready = false;
|
static bool g_rawdev_ready = false;
|
||||||
static bool g_emudev_ready = false;
|
static bool g_emudev_ready = false;
|
||||||
|
|
||||||
|
static bool g_is_emummc = false;
|
||||||
|
|
||||||
static sdmmc_t g_sd_sdmmc = {0};
|
static sdmmc_t g_sd_sdmmc = {0};
|
||||||
static sdmmc_t g_emmc_sdmmc = {0};
|
static sdmmc_t g_emmc_sdmmc = {0};
|
||||||
|
|
||||||
|
@ -106,6 +108,9 @@ static void mmc_partition_finalize(device_partition_t *devpart) {
|
||||||
|
|
||||||
/* Finalize hardware. */
|
/* Finalize hardware. */
|
||||||
if (mmcpart->device == &g_sd_device) {
|
if (mmcpart->device == &g_sd_device) {
|
||||||
|
if (g_is_emummc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (g_sd_device_initialized) {
|
if (g_sd_device_initialized) {
|
||||||
sdmmc_device_finish(&g_sd_device);
|
sdmmc_device_finish(&g_sd_device);
|
||||||
g_sd_device_initialized = false;
|
g_sd_device_initialized = false;
|
||||||
|
@ -571,7 +576,7 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) {
|
||||||
|
|
||||||
/* Failed to register boot0 device. */
|
/* Failed to register boot0 device. */
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
return -1;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setup an emulation template for boot1. */
|
/* Setup an emulation template for boot1. */
|
||||||
|
@ -585,7 +590,7 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) {
|
||||||
|
|
||||||
/* Failed to mount boot1. */
|
/* Failed to mount boot1. */
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
return -1;
|
return -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't register emulated boot1 for now. */
|
/* Don't register emulated boot1 for now. */
|
||||||
|
@ -601,7 +606,7 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) {
|
||||||
|
|
||||||
/* Failed to mount raw NAND. */
|
/* Failed to mount raw NAND. */
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
return -1;
|
return -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register emulated raw NAND device. */
|
/* Register emulated raw NAND device. */
|
||||||
|
@ -609,7 +614,7 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) {
|
||||||
|
|
||||||
/* Failed to register raw NAND device. */
|
/* Failed to register raw NAND device. */
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
return -1;
|
return -5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Open emulated raw NAND device. */
|
/* Open emulated raw NAND device. */
|
||||||
|
@ -617,7 +622,7 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) {
|
||||||
|
|
||||||
/* Failed to open emulated raw NAND device. */
|
/* Failed to open emulated raw NAND device. */
|
||||||
if (rawnand == NULL) {
|
if (rawnand == NULL) {
|
||||||
return -1;
|
return -6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Iterate the GPT and mount each emulated raw NAND partition. */
|
/* Iterate the GPT and mount each emulated raw NAND partition. */
|
||||||
|
@ -629,6 +634,7 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) {
|
||||||
/* All emulated devices are ready. */
|
/* All emulated devices are ready. */
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
g_emudev_ready = true;
|
g_emudev_ready = true;
|
||||||
|
g_is_emummc = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -772,6 +778,7 @@ int nxfs_mount_emummc_file(const char *emummc_path, int num_parts, uint64_t part
|
||||||
/* All emulated devices are ready. */
|
/* All emulated devices are ready. */
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
g_emudev_ready = true;
|
g_emudev_ready = true;
|
||||||
|
g_is_emummc = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
static void package2_decrypt(package2_header_t *package2);
|
static void package2_decrypt(package2_header_t *package2);
|
||||||
static size_t package2_get_src_section(void **section, package2_header_t *package2, unsigned int id);
|
static size_t package2_get_src_section(void **section, package2_header_t *package2, unsigned int id);
|
||||||
static size_t package2_get_thermosphere(void **thermosphere);
|
static size_t package2_get_thermosphere(void **thermosphere);
|
||||||
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware);
|
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware, void *emummc, size_t emummc_size);
|
||||||
static void package2_append_section(unsigned int id, package2_header_t *package2, void *data, size_t size);
|
static void package2_append_section(unsigned int id, package2_header_t *package2, void *data, size_t size);
|
||||||
static void package2_fixup_header_and_section_hashes(package2_header_t *package2, size_t size);
|
static void package2_fixup_header_and_section_hashes(package2_header_t *package2, size_t size);
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ static inline size_t align_to_4(size_t s) {
|
||||||
return ((s + 3) >> 2) << 2;
|
return ((s + 3) >> 2) << 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firmware) {
|
void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firmware, void *emummc, size_t emummc_size) {
|
||||||
package2_header_t *rebuilt_package2;
|
package2_header_t *rebuilt_package2;
|
||||||
size_t rebuilt_package2_size;
|
size_t rebuilt_package2_size;
|
||||||
void *kernel;
|
void *kernel;
|
||||||
|
@ -104,7 +104,7 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform any patches to the INI1, rebuilding it (This is where our built-in sysmodules will be added.) */
|
/* Perform any patches to the INI1, rebuilding it (This is where our built-in sysmodules will be added.) */
|
||||||
rebuilt_ini1 = package2_rebuild_ini1(orig_ini1, target_firmware);
|
rebuilt_ini1 = package2_rebuild_ini1(orig_ini1, target_firmware, emummc, emummc_size);
|
||||||
print(SCREEN_LOG_LEVEL_DEBUG, "Rebuilt INI1...\n");
|
print(SCREEN_LOG_LEVEL_DEBUG, "Rebuilt INI1...\n");
|
||||||
|
|
||||||
/* Allocate the rebuilt package2. */
|
/* Allocate the rebuilt package2. */
|
||||||
|
@ -317,7 +317,7 @@ static size_t package2_get_thermosphere(void **thermosphere) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware) {
|
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware, void *emummc, size_t emummc_size) {
|
||||||
/* TODO: Do we want to support loading another INI from sd:/whatever/INI1.bin? */
|
/* TODO: Do we want to support loading another INI from sd:/whatever/INI1.bin? */
|
||||||
ini1_header_t *inis_to_merge[STRATOSPHERE_INI1_MAX] = {0};
|
ini1_header_t *inis_to_merge[STRATOSPHERE_INI1_MAX] = {0};
|
||||||
ini1_header_t *merged;
|
ini1_header_t *merged;
|
||||||
|
@ -327,7 +327,7 @@ static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target
|
||||||
inis_to_merge[STRATOSPHERE_INI1_PACKAGE2] = ini1;
|
inis_to_merge[STRATOSPHERE_INI1_PACKAGE2] = ini1;
|
||||||
|
|
||||||
/* Merge all of the INI1s. */
|
/* Merge all of the INI1s. */
|
||||||
merged = stratosphere_merge_inis(inis_to_merge, STRATOSPHERE_INI1_MAX);
|
merged = stratosphere_merge_inis(inis_to_merge, STRATOSPHERE_INI1_MAX, emummc, emummc_size);
|
||||||
|
|
||||||
/* Free temporary buffer. */
|
/* Free temporary buffer. */
|
||||||
stratosphere_free_ini1();
|
stratosphere_free_ini1();
|
||||||
|
|
|
@ -86,6 +86,6 @@ static inline uint8_t package2_meta_get_header_version(const package2_meta_t *me
|
||||||
return (uint8_t)((metadata->ctr_dwords[1] ^ (metadata->ctr_dwords[1] >> 16) ^ (metadata->ctr_dwords[1] >> 24)) & 0xFF);
|
return (uint8_t)((metadata->ctr_dwords[1] ^ (metadata->ctr_dwords[1] >> 16) ^ (metadata->ctr_dwords[1] >> 24)) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firmware);
|
void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firmware, void *emummc, size_t emummc_size);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -494,7 +494,7 @@ static int sdmmc_sd_send_op_cond(sdmmc_device_t *device, bool is_sd_ver2, bool i
|
||||||
device->is_block_sdhc = true;
|
device->is_block_sdhc = true;
|
||||||
|
|
||||||
/* We asked for low voltage support and the card accepted. */
|
/* We asked for low voltage support and the card accepted. */
|
||||||
if (is_uhs_en && (resp & SD_ROCR_S18A))
|
if (false && is_uhs_en && (resp & SD_ROCR_S18A))
|
||||||
{
|
{
|
||||||
/* Voltage switching is only valid for SDMMC1. */
|
/* Voltage switching is only valid for SDMMC1. */
|
||||||
if (device->sdmmc->controller == SDMMC_1)
|
if (device->sdmmc->controller == SDMMC_1)
|
||||||
|
|
|
@ -51,6 +51,12 @@ static bool g_stratosphere_boot_enabled = true;
|
||||||
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], boot_kip[], ams_mitm_kip[];
|
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], boot_kip[], ams_mitm_kip[];
|
||||||
extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, spl_kip_size, boot_kip_size, ams_mitm_kip_size;
|
extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, spl_kip_size, boot_kip_size, ams_mitm_kip_size;
|
||||||
|
|
||||||
|
static emummc_fs_ver_t g_fs_ver = FS_VER_1_0_0;
|
||||||
|
|
||||||
|
emummc_fs_ver_t stratosphere_get_fs_version(void) {
|
||||||
|
return g_fs_ver;
|
||||||
|
}
|
||||||
|
|
||||||
/* GCC doesn't consider the size as const... we have to write it ourselves. */
|
/* GCC doesn't consider the size as const... we have to write it ourselves. */
|
||||||
|
|
||||||
ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
||||||
|
@ -152,8 +158,6 @@ void stratosphere_free_ini1(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void try_add_sd_kip(ini1_header_t *ini1, const char *kip_path) {
|
static void try_add_sd_kip(ini1_header_t *ini1, const char *kip_path) {
|
||||||
size_t file_size = get_file_size(kip_path);
|
size_t file_size = get_file_size(kip_path);
|
||||||
|
|
||||||
|
@ -180,6 +184,60 @@ static void try_add_sd_kip(ini1_header_t *ini1, const char *kip_path) {
|
||||||
ini1->num_processes++;
|
ini1->num_processes++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *emummc_kip) {
|
||||||
|
/* Ensure KIPs are uncompressed. */
|
||||||
|
size_t fs_kip_size, emummc_kip_size;
|
||||||
|
fs_kip = kip1_uncompress(fs_kip, &fs_kip_size);
|
||||||
|
emummc_kip = kip1_uncompress(emummc_kip, &emummc_kip_size);
|
||||||
|
|
||||||
|
/* Allocate kip. */
|
||||||
|
kip1_header_t *injected_kip = calloc(1, fs_kip_size + emummc_kip_size);
|
||||||
|
if (injected_kip == NULL) {
|
||||||
|
fatal_error("Failed to allocate memory for emummc kip injection!");
|
||||||
|
}
|
||||||
|
memcpy(injected_kip, fs_kip, sizeof(*fs_kip));
|
||||||
|
|
||||||
|
const size_t fs_contents_size = kip1_get_size_from_header(fs_kip) - sizeof(kip1_header_t);
|
||||||
|
|
||||||
|
const size_t emummc_data_size = emummc_kip->section_headers[3].out_offset + emummc_kip->section_headers[3].out_size;
|
||||||
|
if (emummc_data_size & 0xFFF) {
|
||||||
|
fatal_error("Invalid emummc kip!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy over capabilities. */
|
||||||
|
memcpy(injected_kip->capabilities, emummc_kip->capabilities, sizeof(emummc_kip->capabilities));
|
||||||
|
|
||||||
|
/* Add extra cap for 1.0.0 */
|
||||||
|
if (stratosphere_get_fs_version() == FS_VER_1_0_0) {
|
||||||
|
for (size_t i = 0; i < 0x20; i++) {
|
||||||
|
if (injected_kip->capabilities[i] = 0xFFFFFFFF) {
|
||||||
|
/* Map PMC registers in. */
|
||||||
|
injected_kip->capabilities[i] = 0x07000E7F;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update sections. */
|
||||||
|
injected_kip->section_headers[0].out_size += emummc_data_size;
|
||||||
|
injected_kip->section_headers[0].compressed_size += emummc_data_size;
|
||||||
|
for (size_t i = 1; i < 4; i++) {
|
||||||
|
injected_kip->section_headers[i].out_offset += emummc_data_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy data. */
|
||||||
|
{
|
||||||
|
size_t ofs = 0;
|
||||||
|
for (size_t i = 0; i < 3; i++) {
|
||||||
|
memcpy(injected_kip->data + emummc_kip->section_headers[i].out_offset, emummc_kip->data + ofs, emummc_kip->section_headers[i].compressed_size);
|
||||||
|
ofs += emummc_kip->section_headers[i].compressed_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memcpy(injected_kip->data + emummc_data_size, fs_kip->data, fs_contents_size);
|
||||||
|
|
||||||
|
return injected_kip;
|
||||||
|
}
|
||||||
|
|
||||||
ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
||||||
if (g_sd_files_ini1 != NULL) {
|
if (g_sd_files_ini1 != NULL) {
|
||||||
return g_sd_files_ini1;
|
return g_sd_files_ini1;
|
||||||
|
@ -246,7 +304,7 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Merges some number of INI1s into a single INI1. It's assumed that the INIs are in order of preference. */
|
/* Merges some number of INI1s into a single INI1. It's assumed that the INIs are in order of preference. */
|
||||||
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis) {
|
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, void *emummc, size_t emummc_size) {
|
||||||
uint32_t total_num_processes = 0;
|
uint32_t total_num_processes = 0;
|
||||||
|
|
||||||
/* Validate all ini headers. */
|
/* Validate all ini headers. */
|
||||||
|
@ -305,11 +363,17 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis) {
|
||||||
fatal_error("Not enough space for all the KIP1s!\n");
|
fatal_error("Not enough space for all the KIP1s!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
kip1_header_t *patched_kip = apply_kip_ips_patches(current_kip, current_kip_size);
|
kip1_header_t *patched_kip = apply_kip_ips_patches(current_kip, current_kip_size, &g_fs_ver);
|
||||||
|
|
||||||
|
if (current_kip->title_id == FS_TITLE_ID && emummc != NULL) {
|
||||||
|
patched_kip = inject_emummc_kip(patched_kip != NULL ? patched_kip : current_kip, (kip1_header_t *)emummc);
|
||||||
|
dump_to_file(patched_kip, kip1_get_size_from_header(patched_kip), "DEBUG_EMUMMC.kip");
|
||||||
|
}
|
||||||
|
|
||||||
if (patched_kip != NULL) {
|
if (patched_kip != NULL) {
|
||||||
size_t patched_kip_size = kip1_get_size_from_header(patched_kip);
|
size_t patched_kip_size = kip1_get_size_from_header(patched_kip);
|
||||||
if (patched_kip_size > remaining_size) {
|
if (patched_kip_size > remaining_size) {
|
||||||
fatal_error("Not enough space for all the KIP1s!\n");
|
fatal_error("Not enough space for all the KIP1s! %08x %08x\n", remaining_size, patched_kip_size);
|
||||||
}
|
}
|
||||||
memcpy(current_dst_kip, patched_kip, patched_kip_size);
|
memcpy(current_dst_kip, patched_kip, patched_kip_size);
|
||||||
remaining_size -= patched_kip_size;
|
remaining_size -= patched_kip_size;
|
||||||
|
@ -326,6 +390,9 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
merged->size = sizeof(ini1_header_t) + (uint32_t)(current_dst_kip - merged->kip_data);
|
merged->size = sizeof(ini1_header_t) + (uint32_t)(current_dst_kip - merged->kip_data);
|
||||||
|
if (merged->size & 3) {
|
||||||
|
merged->size = (merged->size + 3) & ~3;
|
||||||
|
}
|
||||||
|
|
||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "kip.h"
|
#include "kip.h"
|
||||||
|
#include "exocfg.h"
|
||||||
|
|
||||||
#define STRATOSPHERE_INI1_SDFILES 0x0
|
#define STRATOSPHERE_INI1_SDFILES 0x0
|
||||||
#define STRATOSPHERE_INI1_EMBEDDED 0x1
|
#define STRATOSPHERE_INI1_EMBEDDED 0x1
|
||||||
|
@ -29,7 +30,9 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware);
|
||||||
ini1_header_t *stratosphere_get_sd_files_ini1(void);
|
ini1_header_t *stratosphere_get_sd_files_ini1(void);
|
||||||
void stratosphere_free_ini1(void);
|
void stratosphere_free_ini1(void);
|
||||||
|
|
||||||
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_inis);
|
emummc_fs_ver_t stratosphere_get_fs_version(void);
|
||||||
|
|
||||||
|
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_inis, void *emummc, size_t emummc_size);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool has_nogc_config;
|
bool has_nogc_config;
|
||||||
|
|
Loading…
Reference in a new issue