fusee: read from emuSD, if enabled
This commit is contained in:
5
fusee/program/source/fatfs/diskio.c
vendored
5
fusee/program/source/fatfs/diskio.c
vendored
@@ -63,7 +63,8 @@ DRESULT disk_read (
|
||||
return diskio_read_boot1(buff, count * 512, sector, count) ? RES_OK : RES_ERROR;
|
||||
case DRIVE_BOOT1_OFF:
|
||||
return diskio_read_boot1(buff, count * 512, sector + BOOT1_OFFSET_SCT_1MB, count) ? RES_OK : RES_ERROR;
|
||||
|
||||
case DRIVE_EMUSD:
|
||||
return diskio_read_emusd(buff, count * 512, sector, count) ? RES_OK : RES_ERROR;
|
||||
default:
|
||||
return RES_PARERR;
|
||||
}
|
||||
@@ -93,6 +94,8 @@ DRESULT disk_write (
|
||||
return diskio_write_boot1(sector, count, buff, count * 512) ? RES_OK : RES_ERROR;
|
||||
case DRIVE_BOOT1_OFF:
|
||||
return diskio_write_boot1(sector + DRIVE_BOOT1_OFF, count, buff, count * 512) ? RES_OK : RES_ERROR;
|
||||
case DRIVE_EMUSD:
|
||||
return diskio_write_emusd(sector, count, buff, count * 512) ? RES_OK : RES_ERROR;
|
||||
default:
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
1
fusee/program/source/fatfs/diskio.h
vendored
1
fusee/program/source/fatfs/diskio.h
vendored
@@ -27,6 +27,7 @@ typedef enum {
|
||||
DRIVE_SYS,
|
||||
DRIVE_BOOT1,
|
||||
DRIVE_BOOT1_OFF,
|
||||
DRIVE_EMUSD,
|
||||
} DDRIVE;
|
||||
|
||||
|
||||
|
||||
3
fusee/program/source/fatfs/diskio_cpp.h
vendored
3
fusee/program/source/fatfs/diskio_cpp.h
vendored
@@ -22,6 +22,9 @@ extern "C" {
|
||||
bool diskio_read_sd_card(void *dst, size_t size, size_t sector_index, size_t sector_count);
|
||||
bool diskio_write_sd_card(size_t sector_index, size_t sector_count, const void *src, size_t size);
|
||||
|
||||
bool diskio_read_emusd(void *dst, size_t size, size_t sector_index, size_t sector_count);
|
||||
bool diskio_write_emusd(size_t sector_index, size_t sector_count, const void *src, size_t size);
|
||||
|
||||
bool diskio_read_system(void *dst, size_t size, size_t sector_index, size_t sector_count);
|
||||
bool diskio_write_system(size_t sector_index, size_t sector_count, const void *src, size_t size);
|
||||
|
||||
|
||||
4
fusee/program/source/fatfs/ffconf.h
vendored
4
fusee/program/source/fatfs/ffconf.h
vendored
@@ -163,12 +163,12 @@
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_VOLUMES 4
|
||||
#define FF_VOLUMES 5
|
||||
/* Number of volumes (logical drives) to be used. (1-10) */
|
||||
|
||||
|
||||
#define FF_STR_VOLUME_ID 1
|
||||
#define FF_VOLUME_STRS "sdmc","sys","boot1","boot1_off"
|
||||
#define FF_VOLUME_STRS "sdmc","sys","boot1","boot1_off","emusd"
|
||||
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
|
||||
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
|
||||
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
|
||||
|
||||
11
fusee/program/source/fatfs/fusee_diskio.cpp
vendored
11
fusee/program/source/fatfs/fusee_diskio.cpp
vendored
@@ -17,6 +17,7 @@
|
||||
#include "diskio_cpp.h"
|
||||
#include "../fusee_sd_card.hpp"
|
||||
#include "../fusee_mmc.hpp"
|
||||
#include "../fusee_emusd.hpp"
|
||||
|
||||
bool diskio_read_sd_card(void *dst, size_t size, size_t sector_index, size_t sector_count) {
|
||||
return R_SUCCEEDED(::ams::nxboot::ReadSdCard(dst, size, sector_index, sector_count));
|
||||
@@ -42,4 +43,12 @@ bool diskio_read_boot1(void *dst, size_t size, size_t sector_index, size_t secto
|
||||
bool diskio_write_boot1(size_t sector_index, size_t sector_count, const void *src, size_t size) {
|
||||
/* Don't allow writes to eMMC BOOT1 */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool diskio_read_emusd(void *dst, size_t size, size_t sector_index, size_t sector_count) {
|
||||
return R_SUCCEEDED(::ams::nxboot::ReadEmuSd(dst, size, sector_index, sector_count));
|
||||
}
|
||||
|
||||
bool diskio_write_emusd(size_t sector_index, size_t sector_count, const void *src, size_t size) {
|
||||
return R_SUCCEEDED(::ams::nxboot::WriteEmuSd(sector_index, sector_count, src, size));
|
||||
}
|
||||
|
||||
@@ -30,10 +30,12 @@ namespace ams::fs {
|
||||
constinit bool g_is_sys_mounted = false;
|
||||
constinit bool g_is_boot1_mounted = false;
|
||||
constinit bool g_is_boot1_off_mounted = false;
|
||||
constinit bool g_is_emusd_mounted = false;
|
||||
|
||||
alignas(0x10) constinit FATFS g_sd_fs = {};
|
||||
alignas(0x10) constinit FATFS g_sys_fs = {};
|
||||
alignas(0x10) constinit FATFS g_boot1_fs = {};
|
||||
alignas(0x10) constinit FATFS g_emusd_fs = {};
|
||||
|
||||
alignas(0x10) constinit FIL g_files[MaxFiles] = {};
|
||||
alignas(0x10) constinit DIR g_dirs[MaxDirectories] = {};
|
||||
@@ -122,53 +124,75 @@ namespace ams::fs {
|
||||
|
||||
}
|
||||
|
||||
bool MountEmuSD() {
|
||||
if(!g_is_emusd_mounted) {
|
||||
g_is_emusd_mounted = f_mount(std::addressof(g_emusd_fs), "emusd:", 1) == FR_OK;
|
||||
}
|
||||
return g_is_emusd_mounted;
|
||||
}
|
||||
|
||||
void UnmountEmuSD () {
|
||||
if(g_is_emusd_mounted) {
|
||||
f_unmount("emusd:");
|
||||
}
|
||||
g_is_emusd_mounted = false;
|
||||
}
|
||||
|
||||
bool MountBoot1() {
|
||||
/* Can't mount both */
|
||||
AMS_ASSERT(!g_is_boot1_mounted && !g_is_boot1_off_mounted);
|
||||
g_is_boot1_mounted = f_mount(std::addressof(g_boot1_fs), "boot1:", 1) == FR_OK;
|
||||
if (!g_is_boot1_mounted && !g_is_boot1_off_mounted) {
|
||||
g_is_boot1_mounted = f_mount(std::addressof(g_boot1_fs), "boot1:", 1) == FR_OK;
|
||||
}
|
||||
return g_is_boot1_mounted;
|
||||
}
|
||||
|
||||
void UnmountBoot1 () {
|
||||
AMS_ASSERT(g_is_boot1_mounted);
|
||||
f_unmount("boot1:");
|
||||
if (g_is_boot1_mounted) {
|
||||
f_unmount("boot1:");
|
||||
}
|
||||
g_is_boot1_mounted = false;
|
||||
}
|
||||
|
||||
bool MountBoot1Off() {
|
||||
/* Can't mount both */
|
||||
AMS_ASSERT(!g_is_boot1_mounted && !g_is_boot1_off_mounted);
|
||||
g_is_boot1_off_mounted = f_mount(std::addressof(g_boot1_fs), "boot1_off:", 1) == FR_OK;
|
||||
if (!g_is_boot1_mounted && !g_is_boot1_off_mounted) {
|
||||
g_is_boot1_off_mounted = f_mount(std::addressof(g_boot1_fs), "boot1_off:", 1) == FR_OK;
|
||||
}
|
||||
return g_is_boot1_off_mounted;
|
||||
}
|
||||
|
||||
void UnmountBoot1Off () {
|
||||
AMS_ASSERT(g_is_boot1_off_mounted);
|
||||
f_unmount("boot1_off:");
|
||||
if (g_is_boot1_off_mounted) {
|
||||
f_unmount("boot1_off:");
|
||||
}
|
||||
g_is_boot1_off_mounted = false;
|
||||
}
|
||||
|
||||
bool MountSys() {
|
||||
AMS_ASSERT(!g_is_sys_mounted);
|
||||
g_is_sys_mounted = f_mount(std::addressof(g_sys_fs), "sys:", 1) == FR_OK;
|
||||
if (!g_is_sys_mounted) {
|
||||
g_is_sys_mounted = f_mount(std::addressof(g_sys_fs), "sys:", 1) == FR_OK;
|
||||
}
|
||||
return g_is_sys_mounted;
|
||||
}
|
||||
|
||||
void UnmountSys() {
|
||||
AMS_ASSERT(g_is_sys_mounted);
|
||||
f_unmount("sys:");
|
||||
if (g_is_sys_mounted) {
|
||||
f_unmount("sys:");
|
||||
}
|
||||
g_is_sys_mounted = false;
|
||||
}
|
||||
|
||||
bool MountSdCard() {
|
||||
AMS_ASSERT(!g_is_sd_mounted);
|
||||
g_is_sd_mounted = f_mount(std::addressof(g_sd_fs), "sdmc:", 1) == FR_OK;
|
||||
if (!g_is_sd_mounted) {
|
||||
g_is_sd_mounted = f_mount(std::addressof(g_sd_fs), "sdmc:", 1) == FR_OK;
|
||||
}
|
||||
return g_is_sd_mounted;
|
||||
}
|
||||
|
||||
void UnmountSdCard() {
|
||||
AMS_ASSERT(g_is_sd_mounted);
|
||||
f_unmount("sdmc:");
|
||||
if (g_is_sd_mounted) {
|
||||
f_unmount("sdmc:");
|
||||
}
|
||||
g_is_sd_mounted = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,6 +108,9 @@ namespace ams::fs {
|
||||
bool MountBoot1Off();
|
||||
void UnmountBoot1Off();
|
||||
|
||||
bool MountEmuSD();
|
||||
void UnmountEmuSD();
|
||||
|
||||
Result ChangeDrive(const char *path);
|
||||
Result ChangeDirectory(const char *path);
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "fusee_fs_api.hpp"
|
||||
#include "../fusee_mmc.hpp"
|
||||
#include "../fusee_fatal.hpp"
|
||||
#include "../fusee_sd_card.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
@@ -155,4 +158,95 @@ namespace ams::fs {
|
||||
virtual Result SetSize(s64 size) override;
|
||||
};
|
||||
|
||||
class SdCardStorage : public fs::IStorage {
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
if (!util::IsAligned(offset, sdmmc::SectorSize) || !util::IsAligned(size, sdmmc::SectorSize)) {
|
||||
nxboot::ShowFatalError("SdCard: unaligned access to %" PRIx64 ", size=%" PRIx64"\n", static_cast<u64>(offset), static_cast<u64>(size));
|
||||
}
|
||||
|
||||
R_RETURN(nxboot::ReadSdCard(buffer, size, offset / sdmmc::SectorSize, size / sdmmc::SectorSize));
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
u32 num_sectors;
|
||||
R_TRY(nxboot::GetSdCardMemoryCapacity(std::addressof(num_sectors)));
|
||||
|
||||
*out = static_cast<s64>(num_sectors) * static_cast<s64>(sdmmc::SectorSize);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
if (!util::IsAligned(offset, sdmmc::SectorSize) || !util::IsAligned(size, sdmmc::SectorSize)) {
|
||||
nxboot::ShowFatalError("SdCard: unaligned access to %" PRIx64 ", size=%" PRIx64"\n", static_cast<u64>(offset), static_cast<u64>(size));
|
||||
}
|
||||
|
||||
R_RETURN(nxboot::WriteSdCard(offset / sdmmc::SectorSize, size / sdmmc::SectorSize, buffer, size));
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
};
|
||||
|
||||
template<sdmmc::MmcPartition Partition>
|
||||
class MmcPartitionStorage : public fs::IStorage {
|
||||
public:
|
||||
constexpr MmcPartitionStorage() { /* ... */ }
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
if (!util::IsAligned(offset, sdmmc::SectorSize) || !util::IsAligned(size, sdmmc::SectorSize)) {
|
||||
nxboot::ShowFatalError("SdCard: unaligned access to %" PRIx64 ", size=%" PRIx64"\n", static_cast<u64>(offset), static_cast<u64>(size));
|
||||
}
|
||||
|
||||
R_RETURN(nxboot::ReadMmc(buffer, size, Partition, offset / sdmmc::SectorSize, size / sdmmc::SectorSize));
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
u32 num_sectors;
|
||||
R_TRY(nxboot::GetMmcMemoryCapacity(std::addressof(num_sectors), Partition));
|
||||
|
||||
*out = static_cast<s64>(num_sectors) * static_cast<s64>(sdmmc::SectorSize);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
if (!util::IsAligned(offset, sdmmc::SectorSize) || !util::IsAligned(size, sdmmc::SectorSize)) {
|
||||
nxboot::ShowFatalError("SdCard: unaligned access to %" PRIx64 ", size=%" PRIx64"\n", static_cast<u64>(offset), static_cast<u64>(size));
|
||||
}
|
||||
|
||||
R_RETURN(nxboot::WriteMmc(Partition, offset / sdmmc::SectorSize, size / sdmmc::SectorSize, buffer, size));
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
};
|
||||
|
||||
class MultiFileStorage : public fs::IStorage {
|
||||
private:
|
||||
s64 m_file_size;
|
||||
fs::FileHandle m_handles[64];
|
||||
bool m_open[64];
|
||||
int m_file_path_ofs;
|
||||
char m_base_path[0x300];
|
||||
private:
|
||||
void EnsureFile(int id);
|
||||
public:
|
||||
MultiFileStorage(const char *base_path);
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
virtual Result Flush() override;
|
||||
virtual Result GetSize(s64 *out) override;
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
|
||||
virtual Result SetSize(s64 size) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
112
fusee/program/source/fs/fusee_multi_file_storage.cpp
Normal file
112
fusee/program/source/fs/fusee_multi_file_storage.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "fusee_fs_storage.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
void MultiFileStorage::EnsureFile(int id) {
|
||||
if (!m_open[id]) {
|
||||
/* Update path. */
|
||||
m_base_path[m_file_path_ofs + 1] = '0' + (id % 10);
|
||||
m_base_path[m_file_path_ofs + 0] = '0' + (id / 10);
|
||||
|
||||
/* Open new file. */
|
||||
const Result result = fs::OpenFile(m_handles + id, m_base_path, fs::OpenMode_Read);
|
||||
if (R_FAILED(result)) {
|
||||
nxboot::ShowFatalError("Failed to open part %02d (%s): 0x%08" PRIx32 "!\n", id, m_base_path, result.GetValue());
|
||||
}
|
||||
|
||||
m_open[id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
MultiFileStorage::MultiFileStorage(const char *base_path) {
|
||||
util::Strlcpy(m_base_path, base_path, sizeof(m_base_path) - 2);
|
||||
m_file_path_ofs = strlen(m_base_path);
|
||||
|
||||
for (size_t i = 0; i < util::size(m_handles); i++) {
|
||||
m_open[i] = false;
|
||||
}
|
||||
|
||||
std::memcpy(m_base_path + m_file_path_ofs, "00", 3);
|
||||
Result r;
|
||||
if (R_FAILED(r = fs::OpenFile(std::addressof(m_handles[0]), m_base_path, fs::OpenMode_Read))) {
|
||||
nxboot::ShowFatalError("Failed to open part %02d (%s): 0x%08" PRIx32 "!\n", 0, m_base_path, r.GetValue());
|
||||
}
|
||||
|
||||
m_open[0] = true;
|
||||
}
|
||||
|
||||
Result MultiFileStorage::Read(s64 offset, void *buffer, size_t size) {
|
||||
int file = offset / m_file_size;
|
||||
s64 subofs = offset % m_file_size;
|
||||
|
||||
u8 *cur_dst = static_cast<u8 *>(buffer);
|
||||
|
||||
for (/* ... */; size > 0; ++file) {
|
||||
/* Ensure the current file is open. */
|
||||
EnsureFile(file);
|
||||
|
||||
/* Perform the current read. */
|
||||
const size_t cur_size = std::min<size_t>(m_file_size - subofs, size);
|
||||
R_TRY(fs::ReadFile(m_handles[file], subofs, cur_dst, cur_size));
|
||||
|
||||
/* Advance. */
|
||||
cur_dst += cur_size;
|
||||
size -= cur_size;
|
||||
subofs = 0;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MultiFileStorage::Flush() {
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
Result MultiFileStorage::GetSize(s64 *out) {
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
Result MultiFileStorage::Write(s64 offset, const void *buffer, size_t size) {
|
||||
int file = offset / m_file_size;
|
||||
s64 subofs = offset % m_file_size;
|
||||
|
||||
const u8 *cur_src = static_cast<const u8 *>(buffer);
|
||||
|
||||
for (/* ... */; size > 0; ++file) {
|
||||
/* Ensure the current file is open. */
|
||||
EnsureFile(file);
|
||||
|
||||
/* Perform the current read. */
|
||||
const size_t cur_size = std::min<size_t>(m_file_size - subofs, size);
|
||||
R_TRY(fs::WriteFile(m_handles[file], subofs, cur_src, cur_size, fs::WriteOption::Flush));
|
||||
|
||||
/* Advance. */
|
||||
cur_src += cur_size;
|
||||
size -= cur_size;
|
||||
subofs = 0;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MultiFileStorage::SetSize(s64 size) {
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
#include "fusee_boot_storage.hpp"
|
||||
#include "fs/fusee_fs_api.hpp"
|
||||
#include "fusee_display.hpp"
|
||||
#include "fusee_mmc.hpp"
|
||||
#include "fusee_sd_card.hpp"
|
||||
#include <exosphere.hpp>
|
||||
#include <vapours/results/fs_results.hpp>
|
||||
|
||||
namespace ams::nxboot {
|
||||
namespace {
|
||||
@@ -75,7 +73,6 @@ namespace ams::nxboot {
|
||||
|
||||
R_THROW(fs::ResultInvalidMountName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result MountBootStorage() {
|
||||
|
||||
@@ -14,167 +14,27 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include <exosphere/secmon/secmon_emummc_context.hpp>
|
||||
#include "fusee_emummc.hpp"
|
||||
#include "fusee_ini.hpp"
|
||||
#include "fusee_mmc.hpp"
|
||||
#include "fusee_sd_card.hpp"
|
||||
#include "fusee_fatal.hpp"
|
||||
#include "fusee_malloc.hpp"
|
||||
#include "fs/fusee_fs_api.hpp"
|
||||
#include "fs/fusee_fs_storage.hpp"
|
||||
#include "fusee_sd_card.hpp"
|
||||
#include "fusee_util.hpp"
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
namespace {
|
||||
|
||||
class SdCardStorage : public fs::IStorage {
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
if (!util::IsAligned(offset, sdmmc::SectorSize) || !util::IsAligned(size, sdmmc::SectorSize)) {
|
||||
ShowFatalError("SdCard: unaligned access to %" PRIx64 ", size=%" PRIx64"\n", static_cast<u64>(offset), static_cast<u64>(size));
|
||||
}
|
||||
|
||||
R_RETURN(ReadSdCard(buffer, size, offset / sdmmc::SectorSize, size / sdmmc::SectorSize));
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
u32 num_sectors;
|
||||
R_TRY(GetSdCardMemoryCapacity(std::addressof(num_sectors)));
|
||||
|
||||
*out = static_cast<s64>(num_sectors) * static_cast<s64>(sdmmc::SectorSize);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
};
|
||||
|
||||
template<sdmmc::MmcPartition Partition>
|
||||
class MmcPartitionStorage : public fs::IStorage {
|
||||
public:
|
||||
constexpr MmcPartitionStorage() { /* ... */ }
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
if (!util::IsAligned(offset, sdmmc::SectorSize) || !util::IsAligned(size, sdmmc::SectorSize)) {
|
||||
ShowFatalError("SdCard: unaligned access to %" PRIx64 ", size=%" PRIx64"\n", static_cast<u64>(offset), static_cast<u64>(size));
|
||||
}
|
||||
|
||||
R_RETURN(ReadMmc(buffer, size, Partition, offset / sdmmc::SectorSize, size / sdmmc::SectorSize));
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
u32 num_sectors;
|
||||
R_TRY(GetMmcMemoryCapacity(std::addressof(num_sectors), Partition));
|
||||
|
||||
*out = static_cast<s64>(num_sectors) * static_cast<s64>(sdmmc::SectorSize);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
};
|
||||
|
||||
using MmcBoot0Storage = MmcPartitionStorage<sdmmc::MmcPartition_BootPartition1>;
|
||||
using MmcUserStorage = MmcPartitionStorage<sdmmc::MmcPartition_UserData>;
|
||||
|
||||
constinit char g_emummc_path[0x300];
|
||||
|
||||
class EmummcFileStorage : public fs::IStorage {
|
||||
private:
|
||||
s64 m_file_size;
|
||||
fs::FileHandle m_handles[64];
|
||||
bool m_open[64];
|
||||
int m_file_path_ofs;
|
||||
private:
|
||||
void EnsureFile(int id) {
|
||||
if (!m_open[id]) {
|
||||
/* Update path. */
|
||||
g_emummc_path[m_file_path_ofs + 1] = '0' + (id % 10);
|
||||
g_emummc_path[m_file_path_ofs + 0] = '0' + (id / 10);
|
||||
|
||||
/* Open new file. */
|
||||
const Result result = fs::OpenFile(m_handles + id, g_emummc_path, fs::OpenMode_Read);
|
||||
if (R_FAILED(result)) {
|
||||
ShowFatalError("Failed to open emummc user %02d file: 0x%08" PRIx32 "!\n", id, result.GetValue());
|
||||
}
|
||||
|
||||
m_open[id] = true;
|
||||
}
|
||||
}
|
||||
public:
|
||||
EmummcFileStorage(fs::FileHandle user00, int ofs) : m_file_path_ofs(ofs) {
|
||||
const Result result = fs::GetFileSize(std::addressof(m_file_size), user00);
|
||||
if (R_FAILED(result)) {
|
||||
ShowFatalError("Failed to get emummc file size: 0x%08" PRIx32 "!\n", result.GetValue());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < util::size(m_handles); ++i) {
|
||||
m_open[i] = false;
|
||||
}
|
||||
|
||||
m_handles[0] = user00;
|
||||
m_open[0] = true;
|
||||
}
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
int file = offset / m_file_size;
|
||||
s64 subofs = offset % m_file_size;
|
||||
|
||||
u8 *cur_dst = static_cast<u8 *>(buffer);
|
||||
|
||||
for (/* ... */; size > 0; ++file) {
|
||||
/* Ensure the current file is open. */
|
||||
EnsureFile(file);
|
||||
|
||||
/* Perform the current read. */
|
||||
const size_t cur_size = std::min<size_t>(m_file_size - subofs, size);
|
||||
R_TRY(fs::ReadFile(m_handles[file], subofs, cur_dst, cur_size));
|
||||
|
||||
/* Advance. */
|
||||
cur_dst += cur_size;
|
||||
size -= cur_size;
|
||||
subofs = 0;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
};
|
||||
|
||||
constinit SdCardStorage g_sd_card_storage;
|
||||
using MmcBoot0Storage = fs::MmcPartitionStorage<sdmmc::MmcPartition_BootPartition1>;
|
||||
using MmcUserStorage = fs::MmcPartitionStorage<sdmmc::MmcPartition_UserData>;
|
||||
using EmummcFileStorage = fs::MultiFileStorage;
|
||||
|
||||
constinit secmon::EmummcConfiguration g_emummc_cfg = {};
|
||||
constinit fs::SdCardStorage g_sd_card_storage;
|
||||
constinit MmcBoot0Storage g_mmc_boot0_storage;
|
||||
constinit MmcUserStorage g_mmc_user_storage;
|
||||
|
||||
@@ -231,24 +91,28 @@ namespace ams::nxboot {
|
||||
'B', 'C', 'P', 'K', 'G', '2', '-', '1', '-', 'N', 'o', 'r', 'm', 'a', 'l', '-', 'M', 'a', 'i', 'n', 0
|
||||
};
|
||||
|
||||
bool IsDirectoryExist(const char *path) {
|
||||
fs::DirectoryEntryType entry_type;
|
||||
bool archive;
|
||||
return R_SUCCEEDED(fs::GetEntryType(std::addressof(entry_type), std::addressof(archive), path)) && entry_type == fs::DirectoryEntryType_Directory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InitializeEmummc(bool emummc_enabled, const secmon::EmummcEmmcConfiguration &emummc_cfg) {
|
||||
Result result;
|
||||
if (emummc_enabled) {
|
||||
/* Get sd card size. */
|
||||
s64 sd_card_size;
|
||||
if (R_FAILED((result = g_sd_card_storage.GetSize(std::addressof(sd_card_size))))) {
|
||||
ShowFatalError("Failed to get sd card size: 0x%08" PRIx32 "!\n", result.GetValue());
|
||||
}
|
||||
|
||||
if(emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_Partition_Emmc || emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_Partition_Sd) {
|
||||
/* Partition based emummc */
|
||||
if(emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_Partition_Emmc) {
|
||||
/* When emmc based, init eMMC */
|
||||
if (R_FAILED((result = InitializeMmc()))) {
|
||||
ShowFatalError("Failed to initialize mmc: 0x%08" PRIx32 "\n", result.GetValue());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (R_FAILED((result = InitializeSdCard ()))) {
|
||||
ShowFatalError("Failed to initialize sd: 0x%08" PRIx32 "\n", result.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
/* Get SD or eMMC storage */
|
||||
@@ -265,6 +129,8 @@ namespace ams::nxboot {
|
||||
g_user_storage = AllocateObject<fs::SubStorage>(storage, partition_start + 8_MB, storage_size - (partition_start + 8_MB));
|
||||
} else if (emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_File_Emmc || emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_File_Sd) {
|
||||
/* File based emummc */
|
||||
char path[0x300];
|
||||
|
||||
if(emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_File_Emmc) {
|
||||
/* When emmc based, init eMMC */
|
||||
if (R_FAILED((result = InitializeMmc()))) {
|
||||
@@ -274,33 +140,43 @@ namespace ams::nxboot {
|
||||
if (!fs::MountSys()) {
|
||||
ShowFatalError("Failed to mount mmc!\n");
|
||||
}
|
||||
} else {
|
||||
/* When sd based, init sd */
|
||||
if (R_FAILED((result = InitializeSdCard ()))) {
|
||||
ShowFatalError("Failed to initialize sd: 0x%08" PRIx32 "\n", result.GetValue());
|
||||
}
|
||||
|
||||
if (!fs::MountSdCard()) {
|
||||
ShowFatalError("Failed to mount sd!\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Set drive to read from */
|
||||
if (emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_File_Sd) {
|
||||
std::strcpy(g_emummc_path, "sdmc:");
|
||||
std::strcpy(path, "sdmc:");
|
||||
} else {
|
||||
std::strcpy(g_emummc_path, "sys:");
|
||||
std::strcpy(path, "sys:");
|
||||
}
|
||||
|
||||
std::memcpy(g_emummc_path + std::strlen(g_emummc_path), emummc_cfg.file_cfg.path.str, sizeof(emummc_cfg.file_cfg.path.str));
|
||||
std::strcat(g_emummc_path, "/eMMC");
|
||||
|
||||
auto len = std::strlen(g_emummc_path);
|
||||
std::memcpy(path + std::strlen(path), emummc_cfg.file_cfg.path.str, sizeof(emummc_cfg.file_cfg.path.str));
|
||||
std::strcat(path, "/eMMC");
|
||||
|
||||
auto len = std::strlen(path);
|
||||
|
||||
/* Open boot0 file */
|
||||
fs::FileHandle boot0_file;
|
||||
std::strcat(g_emummc_path, "/boot0");
|
||||
if(R_FAILED((result = fs::OpenFile(std::addressof(boot0_file), g_emummc_path, fs::OpenMode_Read)))) {
|
||||
ShowFatalError("Failed to open emummc boot0 file: 0x%08" PRIx32 " %s!\n", result.GetValue(), g_emummc_path);
|
||||
std::strcat(path, "/boot0");
|
||||
if(R_FAILED((result = fs::OpenFile(std::addressof(boot0_file), path, fs::OpenMode_Read)))) {
|
||||
ShowFatalError("Failed to open emummc boot0 file: 0x%08" PRIx32 " %s!\n", result.GetValue(), path);
|
||||
}
|
||||
|
||||
/* Check if boot1 file exists */
|
||||
std::strcpy(g_emummc_path + len, "/boot1");
|
||||
std::strcpy(path + len, "/boot1");
|
||||
{
|
||||
fs::DirectoryEntryType entry_type;
|
||||
bool is_archive;
|
||||
if (R_FAILED((result = fs::GetEntryType(std::addressof(entry_type), std::addressof(is_archive), g_emummc_path)))){
|
||||
if (R_FAILED((result = fs::GetEntryType(std::addressof(entry_type), std::addressof(is_archive), path)))){
|
||||
ShowFatalError("Failed to find emummc boot1 file: 0x%08" PRIx32 "!\n", result.GetValue());
|
||||
}
|
||||
|
||||
@@ -310,15 +186,12 @@ namespace ams::nxboot {
|
||||
}
|
||||
|
||||
/* Open userdata */
|
||||
std::strcpy(g_emummc_path + len, "/00");
|
||||
fs::FileHandle user00_file;
|
||||
if(R_FAILED((result = fs::OpenFile(std::addressof(user00_file), g_emummc_path, fs::OpenMode_Read)))) {
|
||||
ShowFatalError("Failed to open emummc user %02d file: 0x%08" PRIx32 "!\n", 0, result.GetValue());
|
||||
}
|
||||
std::strcpy(path + len, "/");
|
||||
|
||||
/* Create partition */
|
||||
/* TODO: construct boot0 storage from path instead */
|
||||
g_boot0_storage = AllocateObject<fs::FileHandleStorage>(boot0_file);
|
||||
g_user_storage = AllocateObject<EmummcFileStorage>(user00_file, len + 1);
|
||||
g_user_storage = AllocateObject<EmummcFileStorage>(path);
|
||||
} else {
|
||||
ShowFatalError("Unknown emummc type %d\n", static_cast<int>(emummc_cfg.base_cfg.type));
|
||||
}
|
||||
@@ -388,4 +261,130 @@ namespace ams::nxboot {
|
||||
R_RETURN(g_package2_storage->Read(offset, dst, size));
|
||||
}
|
||||
|
||||
Result ReadEmummcConfig() {
|
||||
/* Set magic. */
|
||||
auto &sd_cfg = g_emummc_cfg.sd_cfg;
|
||||
auto &emmc_cfg = g_emummc_cfg.emmc_cfg;
|
||||
|
||||
emmc_cfg.base_cfg.magic = secmon::EmummcEmmcBaseConfiguration::Magic;
|
||||
sd_cfg.base_cfg.magic = secmon::EmummcSdBaseConfiguration::Magic;
|
||||
|
||||
/* Parse emummc ini. */
|
||||
u32 enabled = 0;
|
||||
u32 id = 0;
|
||||
u32 sector = 0;
|
||||
const char *path = "";
|
||||
const char *n_path = "";
|
||||
|
||||
{
|
||||
IniSectionList sections;
|
||||
if (ParseIniSafe(sections, "emummc/emummc.ini")) {
|
||||
for (const auto §ion : sections){
|
||||
/* Skip non-emummc sections */
|
||||
if (std::strcmp(section.name, "emummc")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto &entry : section.kv_list) {
|
||||
if(std::strcmp(entry.key, "enabled") == 0) {
|
||||
enabled = ParseDecimalInteger(entry.value);
|
||||
} else if (std::strcmp(entry.key, "id") == 0) {
|
||||
id = ParseHexInteger(entry.value);
|
||||
} else if (std::strcmp(entry.key, "sector") == 0) {
|
||||
sector = ParseHexInteger(entry.value);
|
||||
} else if (std::strcmp(entry.key, "path") == 0) {
|
||||
path = entry.value;
|
||||
} else if (std::strcmp(entry.key, "nintendo_path") == 0) {
|
||||
n_path = entry.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set parsed values to config */
|
||||
constexpr const char *emummc_err_str = "Invalid emummc setting!\n";
|
||||
|
||||
emmc_cfg.base_cfg.id = id;
|
||||
std::strncpy(emmc_cfg.emu_dir_path.str, n_path, sizeof(emmc_cfg.emu_dir_path.str));
|
||||
emmc_cfg.emu_dir_path.str[sizeof(emmc_cfg.emu_dir_path.str) - 1] = '\x00';
|
||||
|
||||
if (enabled == 1) {
|
||||
/* SD based */
|
||||
if (sector > 0) {
|
||||
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_Partition_Sd;
|
||||
emmc_cfg.partition_cfg.start_sector = sector;
|
||||
} else if (path[0] != '\x00' && IsDirectoryExist(path)) {
|
||||
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_File_Sd;
|
||||
std::strncpy(emmc_cfg.file_cfg.path.str, path, sizeof(emmc_cfg.file_cfg.path.str));
|
||||
emmc_cfg.file_cfg.path.str[sizeof(emmc_cfg.file_cfg.path.str) - 1] = '\x00';
|
||||
} else {
|
||||
ShowFatalError(emummc_err_str);
|
||||
}
|
||||
} else if (enabled == 4){
|
||||
/* eMMC based */
|
||||
if (sector > 0) {
|
||||
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_Partition_Emmc;
|
||||
emmc_cfg.partition_cfg.start_sector = sector;
|
||||
} else if (path[0] != '\x00' /* && IsDirectoryExist(path) */) {
|
||||
/* TODO: Should check if directory exist on *eMMC* fat partition instead of SD */
|
||||
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_File_Emmc;
|
||||
std::strncpy(emmc_cfg.file_cfg.path.str, path, sizeof(emmc_cfg.file_cfg.path.str));
|
||||
emmc_cfg.file_cfg.path.str[sizeof(emmc_cfg.file_cfg.path.str) - 1] = '\x00';
|
||||
} else {
|
||||
ShowFatalError(emummc_err_str);
|
||||
}
|
||||
} else if (enabled == 0) {
|
||||
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_None;
|
||||
} else {
|
||||
ShowFatalError(emummc_err_str);
|
||||
}
|
||||
|
||||
/* Parse emusd ini. */
|
||||
constexpr const char *emusd_err_str = "Invalid emusd setting!\n";
|
||||
|
||||
u32 sd_enabled = 0;
|
||||
u32 sd_sector = 0;
|
||||
|
||||
{
|
||||
IniSectionList sections;
|
||||
if (ParseIniSafe(sections, "emusd/emusd.ini")) {
|
||||
for (const auto §ion : sections){
|
||||
/* Skip non-emummc sections */
|
||||
if (std::strcmp(section.name, "emusd")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto &entry : section.kv_list) {
|
||||
if(std::strcmp(entry.key, "enabled") == 0) {
|
||||
sd_enabled = ParseDecimalInteger(entry.value);
|
||||
} else if (std::strcmp(entry.key, "sector") == 0) {
|
||||
sd_sector = ParseHexInteger(entry.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set parsed values to config */
|
||||
if (sd_enabled == 4) {
|
||||
if (sd_sector > 0) {
|
||||
sd_cfg.partition_cfg.start_sector = sd_sector;
|
||||
sd_cfg.base_cfg.type = secmon::EmummcSdType::EmummcSdType_Partition_Emmc;
|
||||
} else {
|
||||
ShowFatalError(emusd_err_str);
|
||||
}
|
||||
} else if (sd_enabled == 0) {
|
||||
sd_cfg.base_cfg.type = secmon::EmummcSdType::EmummcSdType_None;
|
||||
} else {
|
||||
ShowFatalError(emusd_err_str);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
const secmon::EmummcConfiguration &GetEmummcConfig() {
|
||||
return g_emummc_cfg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,13 +15,16 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <vapours.hpp>
|
||||
#include <exosphere.hpp>
|
||||
#include <exosphere/secmon/secmon_emummc_context.hpp>
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
void InitializeEmummc(bool emummc_enabled, const secmon::EmummcEmmcConfiguration &emummc_cfg);
|
||||
|
||||
Result ReadEmummcConfig();
|
||||
const secmon::EmummcConfiguration &GetEmummcConfig();
|
||||
|
||||
Result ReadBoot0(s64 offset, void *dst, size_t size);
|
||||
Result ReadPackage2(s64 offset, void *dst, size_t size);
|
||||
Result ReadSystem(s64 offset, void *dst, size_t size);
|
||||
|
||||
100
fusee/program/source/fusee_emusd.cpp
Normal file
100
fusee/program/source/fusee_emusd.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include "fusee_emusd.hpp"
|
||||
#include "fs/fusee_fs_storage.hpp"
|
||||
#include "fusee_fatal.hpp"
|
||||
#include "fusee_malloc.hpp"
|
||||
#include "fusee_mmc.hpp"
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
namespace {
|
||||
constinit fs::IStorage *g_emusd_base_storage = nullptr;
|
||||
constinit fs::IStorage *g_emusd_storage = nullptr;
|
||||
|
||||
struct MbrPartitionEntry {
|
||||
u8 boot_flag;
|
||||
u8 chs_start[3];
|
||||
u8 type;
|
||||
u8 chs_end[3];
|
||||
u32 sct_start;
|
||||
u32 sct_size;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) Mbr {
|
||||
u8 bootloader[440];
|
||||
u32 signature;
|
||||
u8 padding[2];
|
||||
MbrPartitionEntry entries[4];
|
||||
u16 magic;
|
||||
};
|
||||
}
|
||||
|
||||
void InitializeEmuSd(const secmon::EmummcSdConfiguration &emusd_cfg) {
|
||||
if (emusd_cfg.IsActive()) {
|
||||
if (emusd_cfg.base_cfg.type == secmon::EmummcSdType_Partition_Emmc) {
|
||||
Result r;
|
||||
if (R_FAILED(r = InitializeMmc())) {
|
||||
ShowFatalError("Failed to initializeMmc: %" PRIx32 "!\n", r.GetValue());
|
||||
}
|
||||
|
||||
g_emusd_base_storage = AllocateObject<fs::MmcPartitionStorage<sdmmc::MmcPartition_UserData>>();
|
||||
|
||||
/* Get base storage size */
|
||||
s64 size;
|
||||
if(R_FAILED(g_emusd_base_storage->GetSize(std::addressof(size)))) {
|
||||
ShowFatalError("Failed to get eMMC size!");
|
||||
}
|
||||
|
||||
/* Read emuSD size from mbr */
|
||||
Mbr *mbr = static_cast<Mbr *>(AllocateAligned(sizeof(mbr), 0x200));
|
||||
const s64 offset = INT64_C(0x200) * emusd_cfg.partition_cfg.start_sector;
|
||||
r = g_emusd_base_storage->Read(offset, mbr, sizeof(*mbr));
|
||||
if (R_FAILED(r)) {
|
||||
ShowFatalError("Failed to read MBR: 0x%08" PRIx32 "\n", r.GetValue());
|
||||
}
|
||||
|
||||
|
||||
/* Fatal if emuSD too big */
|
||||
const s64 emusd_size = INT64_C(0x200) * (mbr->entries[0].sct_size + mbr->entries[0].sct_start);
|
||||
if (offset + emusd_size > static_cast<s64>(size)) {
|
||||
ShowFatalError("Invalid emusd!");
|
||||
}
|
||||
|
||||
g_emusd_storage = AllocateObject<fs::SubStorage>(*g_emusd_base_storage, offset, emusd_size);
|
||||
}
|
||||
/* TODO: support other emusd types */
|
||||
} else {
|
||||
const Result r = InitializeSdCard();
|
||||
if (R_FAILED(r)) {
|
||||
ShowFatalError("Failed to initialize SD card: 0x%08" PRIx32 "!\n", r.GetValue());
|
||||
}
|
||||
g_emusd_storage = AllocateObject<fs::SdCardStorage>();
|
||||
}
|
||||
}
|
||||
|
||||
Result ReadEmuSd(void *dst, size_t size, size_t sector_index, size_t sector_count) {
|
||||
R_UNLESS(g_emusd_storage, fs::ResultNotInitialized());
|
||||
R_RETURN(g_emusd_storage->Read(INT64_C(0x200) * sector_index, dst, INT64_C(0x200) * sector_count));
|
||||
}
|
||||
|
||||
Result WriteEmuSd(size_t sector_index, size_t sector_count, const void *src, size_t size) {
|
||||
R_UNLESS(g_emusd_storage, fs::ResultNotInitialized());
|
||||
R_RETURN(g_emusd_storage->Write(INT64_C(0x200) * sector_index, src, INT64_C(0x200) * sector_count));
|
||||
}
|
||||
|
||||
}
|
||||
27
fusee/program/source/fusee_emusd.hpp
Normal file
27
fusee/program/source/fusee_emusd.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "fusee_sd_card.hpp"
|
||||
#include <exosphere.hpp>
|
||||
#include <exosphere/secmon/secmon_emummc_context.hpp>
|
||||
|
||||
namespace ams::nxboot {
|
||||
void InitializeEmuSd(const secmon::EmummcSdConfiguration &emusd_cfg);
|
||||
Result ReadEmuSd(void *dst, size_t size, size_t sector_index, size_t sector_count);
|
||||
Result WriteEmuSd(size_t sector_index, size_t sector_count, const void *src, size_t size);
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "fusee_ini.hpp"
|
||||
#include "fusee_fatal.hpp"
|
||||
#include "fusee_malloc.hpp"
|
||||
#include "fs/fusee_fs_api.hpp"
|
||||
|
||||
@@ -190,7 +191,17 @@ namespace ams::nxboot {
|
||||
return ParseIniResult_InvalidFormat;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool ParseIniSafe(IniSectionList &out_sections, const char *ini_path) {
|
||||
const auto result = ParseIniFile(out_sections, ini_path);
|
||||
if (result == ParseIniResult_Success) {
|
||||
return true;
|
||||
} else if (result == ParseIniResult_NoFile) {
|
||||
return false;
|
||||
} else {
|
||||
ShowFatalError("Failed to parse %s!\n", ini_path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,5 +41,6 @@ namespace ams::nxboot {
|
||||
};
|
||||
|
||||
ParseIniResult ParseIniFile(IniSectionList &out_sections, const char *ini_path);
|
||||
|
||||
bool ParseIniSafe(IniSectionList &out_sections, const char *ini_path);
|
||||
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <exosphere.hpp>
|
||||
#include "fusee_boot_storage.hpp"
|
||||
#include "fusee_display.hpp"
|
||||
#include "fusee_emummc.hpp"
|
||||
#include "fusee_emusd.hpp"
|
||||
#include "sein/fusee_secure_initialize.hpp"
|
||||
#include "sdram/fusee_sdram.hpp"
|
||||
#include "mtc/fusee_mtc.hpp"
|
||||
@@ -67,6 +69,19 @@ namespace ams::nxboot {
|
||||
fs::CloseFile(g_package_file);
|
||||
}
|
||||
|
||||
void SwapToEmuSd() {
|
||||
/* Switch to emuSD, if active */
|
||||
if (GetEmummcConfig().sd_cfg.IsActive()) {
|
||||
/* UnmountBootStorage(); */
|
||||
FinalizeBootStorage();
|
||||
InitializeEmuSd(GetEmummcConfig().sd_cfg);
|
||||
fs::ChangeDrive("emusd:");
|
||||
if (!fs::MountEmuSD()) {
|
||||
ShowFatalError("Failed to mount emuSD");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Main() {
|
||||
@@ -87,6 +102,12 @@ namespace ams::nxboot {
|
||||
ShowFatalError("Failed to mount boot storage.");
|
||||
}
|
||||
|
||||
/* Read emuMMC/emuSD config now, need to switch to emuSD, if active, to read AMS files from there */
|
||||
ReadEmummcConfig();
|
||||
|
||||
/* Switch to emuSD, if active */
|
||||
SwapToEmuSd();
|
||||
|
||||
/* If we have a fatal error, save and display it. */
|
||||
SaveAndShowFatalError();
|
||||
|
||||
@@ -117,6 +138,7 @@ namespace ams::nxboot {
|
||||
|
||||
/* Restore the memory training overlay. */
|
||||
RestoreMemoryTrainingOverlay();
|
||||
|
||||
|
||||
/* Restore memory clock rate. */
|
||||
RestoreMemoryClockRate();
|
||||
@@ -127,8 +149,10 @@ namespace ams::nxboot {
|
||||
/* Finalize display. */
|
||||
FinalizeDisplay();
|
||||
|
||||
// TODO: proper finalize
|
||||
/* Finalize sd card. */
|
||||
UnmountBootStorage();
|
||||
fs::UnmountEmuSD();
|
||||
FinalizeSdCard();
|
||||
|
||||
/* Finalize the data cache. */
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace ams::nxboot {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit bool g_is_initialized = false;
|
||||
|
||||
constexpr inline auto MmcPort = sdmmc::Port_Mmc0;
|
||||
|
||||
alignas(0x10) constinit u8 g_mmc_work_buffer[sdmmc::MmcWorkBufferSize];
|
||||
@@ -40,22 +42,29 @@ namespace ams::nxboot {
|
||||
}
|
||||
|
||||
void FinalizeMmc() {
|
||||
/* Deactivate MMC. */
|
||||
sdmmc::Deactivate(MmcPort);
|
||||
if (g_is_initialized) {
|
||||
g_is_initialized = false;
|
||||
/* Deactivate MMC. */
|
||||
sdmmc::Deactivate(MmcPort);
|
||||
|
||||
/* Finalize MMC. */
|
||||
sdmmc::Finalize(MmcPort);
|
||||
/* Finalize MMC. */
|
||||
sdmmc::Finalize(MmcPort);
|
||||
}
|
||||
}
|
||||
|
||||
Result InitializeMmc() {
|
||||
/* Initialize the mmc. */
|
||||
sdmmc::Initialize(MmcPort);
|
||||
if (!g_is_initialized) {
|
||||
/* Initialize the mmc. */
|
||||
sdmmc::Initialize(MmcPort);
|
||||
g_is_initialized = true;
|
||||
|
||||
/* Set the mmc work buffer. */
|
||||
sdmmc::SetMmcWorkBuffer(MmcPort, g_mmc_work_buffer, sizeof(g_mmc_work_buffer));
|
||||
/* Set the mmc work buffer. */
|
||||
sdmmc::SetMmcWorkBuffer(MmcPort, g_mmc_work_buffer, sizeof(g_mmc_work_buffer));
|
||||
|
||||
/* Activate the mmc. */
|
||||
R_RETURN(sdmmc::Activate(MmcPort));
|
||||
/* Activate the mmc. */
|
||||
R_RETURN(sdmmc::Activate(MmcPort));
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheckMmcConnection(sdmmc::SpeedMode *out_sm, sdmmc::BusWidth *out_bw) {
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace ams::nxboot {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit bool g_is_initialized = false;
|
||||
|
||||
constexpr inline auto SdCardPort = sdmmc::Port_SdCard0;
|
||||
|
||||
constexpr inline const uintptr_t APB = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress();
|
||||
@@ -69,24 +71,32 @@ namespace ams::nxboot {
|
||||
|
||||
Result InitializeSdCard() {
|
||||
/* Perform initial pinmux config to enable sd card access. */
|
||||
ConfigureInitialSdCardPinmux();
|
||||
if (!g_is_initialized) {
|
||||
ConfigureInitialSdCardPinmux();
|
||||
|
||||
/* Initialize the SD card. */
|
||||
sdmmc::Initialize(SdCardPort);
|
||||
/* Initialize the SD card. */
|
||||
sdmmc::Initialize(SdCardPort);
|
||||
|
||||
/* Set the SD card work buffer. */
|
||||
sdmmc::SetSdCardWorkBuffer(SdCardPort, g_sd_work_buffer, sizeof(g_sd_work_buffer));
|
||||
g_is_initialized = true;
|
||||
|
||||
/* Activate the SD card. */
|
||||
R_RETURN(sdmmc::Activate(SdCardPort));
|
||||
/* Set the SD card work buffer. */
|
||||
sdmmc::SetSdCardWorkBuffer(SdCardPort, g_sd_work_buffer, sizeof(g_sd_work_buffer));
|
||||
|
||||
/* Activate the SD card. */
|
||||
R_RETURN(sdmmc::Activate(SdCardPort));
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void FinalizeSdCard() {
|
||||
/* Deactivate the SD card. */
|
||||
sdmmc::Deactivate(SdCardPort);
|
||||
if (g_is_initialized) {
|
||||
g_is_initialized = false;
|
||||
sdmmc::Deactivate(SdCardPort);
|
||||
/* Finalize the SD card. */
|
||||
sdmmc::Finalize(SdCardPort);
|
||||
}
|
||||
|
||||
/* Finalize the SD card. */
|
||||
sdmmc::Finalize(SdCardPort);
|
||||
}
|
||||
|
||||
Result CheckSdCardConnection(sdmmc::SpeedMode *out_sm, sdmmc::BusWidth *out_bw) {
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
* 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 <vapours.hpp>
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include <exosphere/secmon/secmon_emummc_context.hpp>
|
||||
#include <exosphere/secmon/secmon_monitor_context.hpp>
|
||||
#include "fusee_key_derivation.hpp"
|
||||
#include "fusee_external_package.hpp"
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "fusee_secmon_sync.hpp"
|
||||
#include "fusee_stratosphere.hpp"
|
||||
#include "fs/fusee_fs_api.hpp"
|
||||
#include "fusee_util.hpp"
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
@@ -37,8 +39,6 @@ namespace ams::nxboot {
|
||||
constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress();
|
||||
constexpr inline const uintptr_t MC = secmon::MemoryRegionPhysicalDeviceMemoryController.GetAddress();
|
||||
|
||||
constinit secmon::EmummcConfiguration g_emummc_cfg = {};
|
||||
|
||||
void DeriveAllKeys(const fuse::SocType soc_type) {
|
||||
/* If on erista, run the TSEC keygen firmware. */
|
||||
if (soc_type == fuse::SocType_Erista) {
|
||||
@@ -59,65 +59,6 @@ namespace ams::nxboot {
|
||||
}
|
||||
}
|
||||
|
||||
bool ParseIniSafe(IniSectionList &out_sections, const char *ini_path) {
|
||||
const auto result = ParseIniFile(out_sections, ini_path);
|
||||
if (result == ParseIniResult_Success) {
|
||||
return true;
|
||||
} else if (result == ParseIniResult_NoFile) {
|
||||
return false;
|
||||
} else {
|
||||
ShowFatalError("Failed to parse %s!\n", ini_path);
|
||||
}
|
||||
}
|
||||
|
||||
u32 ParseHexInteger(const char *s) {
|
||||
u32 x = 0;
|
||||
if (s[0] == '0' && s[1] == 'x') {
|
||||
s += 2;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const char c = *(s++);
|
||||
|
||||
if (c == '\x00') {
|
||||
return x;
|
||||
} else {
|
||||
x <<= 4;
|
||||
|
||||
if ('0' <= c && c <= '9') {
|
||||
x |= (c - '0');
|
||||
} else if ('a' <= c && c <= 'f') {
|
||||
x |= (c - 'a') + 10;
|
||||
} else if ('A' <= c && c <= 'F') {
|
||||
x |= (c - 'A') + 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 ParseDecimalInteger(const char *s) {
|
||||
u32 x = 0;
|
||||
while (true) {
|
||||
const char c = *(s++);
|
||||
|
||||
if (c == '\x00') {
|
||||
return x;
|
||||
} else {
|
||||
x *= 10;
|
||||
|
||||
if ('0' <= c && c <= '9') {
|
||||
x += c - '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsDirectoryExist(const char *path) {
|
||||
fs::DirectoryEntryType entry_type;
|
||||
bool archive;
|
||||
return R_SUCCEEDED(fs::GetEntryType(std::addressof(entry_type), std::addressof(archive), path)) && entry_type == fs::DirectoryEntryType_Directory;
|
||||
}
|
||||
|
||||
bool IsFileExist(const char *path) {
|
||||
fs::DirectoryEntryType entry_type;
|
||||
bool archive;
|
||||
@@ -126,128 +67,8 @@ namespace ams::nxboot {
|
||||
|
||||
bool ConfigureEmummc() {
|
||||
/* Set magic. */
|
||||
auto &sd_cfg = g_emummc_cfg.sd_cfg;
|
||||
auto &emmc_cfg = g_emummc_cfg.emmc_cfg;
|
||||
|
||||
emmc_cfg.base_cfg.magic = secmon::EmummcEmmcBaseConfiguration::Magic;
|
||||
sd_cfg.base_cfg.magic = secmon::EmummcSdBaseConfiguration::Magic;
|
||||
|
||||
bool emummc_driver_enabled = false;
|
||||
|
||||
/* Parse emummc ini. */
|
||||
u32 enabled = 0;
|
||||
u32 id = 0;
|
||||
u32 sector = 0;
|
||||
const char *path = "";
|
||||
const char *n_path = "";
|
||||
|
||||
{
|
||||
IniSectionList sections;
|
||||
if (ParseIniSafe(sections, "emummc/emummc.ini")) {
|
||||
for (const auto §ion : sections){
|
||||
/* Skip non-emummc sections */
|
||||
if (std::strcmp(section.name, "emummc")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto &entry : section.kv_list) {
|
||||
if(std::strcmp(entry.key, "enabled") == 0) {
|
||||
enabled = ParseDecimalInteger(entry.value);
|
||||
} else if (std::strcmp(entry.key, "id") == 0) {
|
||||
id = ParseHexInteger(entry.value);
|
||||
} else if (std::strcmp(entry.key, "sector") == 0) {
|
||||
sector = ParseHexInteger(entry.value);
|
||||
} else if (std::strcmp(entry.key, "path") == 0) {
|
||||
path = entry.value;
|
||||
} else if (std::strcmp(entry.key, "nintendo_path") == 0) {
|
||||
n_path = entry.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set parsed values to config */
|
||||
constexpr const char *emummc_err_str = "Invalid emummc setting!\n";
|
||||
|
||||
emmc_cfg.base_cfg.id = id;
|
||||
std::strncpy(emmc_cfg.emu_dir_path.str, n_path, sizeof(emmc_cfg.emu_dir_path.str));
|
||||
emmc_cfg.emu_dir_path.str[sizeof(emmc_cfg.emu_dir_path.str) - 1] = '\x00';
|
||||
|
||||
if (enabled == 1) {
|
||||
/* SD based */
|
||||
emummc_driver_enabled = true;
|
||||
if (sector > 0) {
|
||||
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_Partition_Sd;
|
||||
emmc_cfg.partition_cfg.start_sector = sector;
|
||||
} else if (path[0] != '\x00' && IsDirectoryExist(path)) {
|
||||
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_File_Sd;
|
||||
std::strncpy(emmc_cfg.file_cfg.path.str, path, sizeof(emmc_cfg.file_cfg.path.str));
|
||||
emmc_cfg.file_cfg.path.str[sizeof(emmc_cfg.file_cfg.path.str) - 1] = '\x00';
|
||||
} else {
|
||||
ShowFatalError(emummc_err_str);
|
||||
}
|
||||
} else if (enabled == 4){
|
||||
/* eMMC based */
|
||||
emummc_driver_enabled = true;
|
||||
if (sector > 0) {
|
||||
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_Partition_Emmc;
|
||||
emmc_cfg.partition_cfg.start_sector = sector;
|
||||
} else if (path[0] != '\x00' /* && IsDirectoryExist(path) */) {
|
||||
/* TODO: Should check if directory exist on *eMMC* fat partition instead of SD */
|
||||
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_File_Emmc;
|
||||
std::strncpy(emmc_cfg.file_cfg.path.str, path, sizeof(emmc_cfg.file_cfg.path.str));
|
||||
emmc_cfg.file_cfg.path.str[sizeof(emmc_cfg.file_cfg.path.str) - 1] = '\x00';
|
||||
} else {
|
||||
ShowFatalError(emummc_err_str);
|
||||
}
|
||||
} else if (enabled == 0) {
|
||||
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_None;
|
||||
} else {
|
||||
ShowFatalError(emummc_err_str);
|
||||
}
|
||||
|
||||
/* Parse emusd ini. */
|
||||
constexpr const char *emusd_err_str = "Invalid emusd setting!\n";
|
||||
|
||||
u32 sd_enabled = 0;
|
||||
u32 sd_sector = 0;
|
||||
|
||||
{
|
||||
IniSectionList sections;
|
||||
if (ParseIniSafe(sections, "emusd/emusd.ini")) {
|
||||
for (const auto §ion : sections){
|
||||
/* Skip non-emummc sections */
|
||||
if (std::strcmp(section.name, "emusd")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto &entry : section.kv_list) {
|
||||
if(std::strcmp(entry.key, "enabled") == 0) {
|
||||
sd_enabled = ParseDecimalInteger(entry.value);
|
||||
} else if (std::strcmp(entry.key, "sector") == 0) {
|
||||
sd_sector = ParseHexInteger(entry.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set parsed values to config */
|
||||
if (sd_enabled == 4) {
|
||||
emummc_driver_enabled = true;
|
||||
if (sd_sector > 0) {
|
||||
sd_cfg.partition_cfg.start_sector = sd_sector;
|
||||
sd_cfg.base_cfg.type = secmon::EmummcSdType::EmummcSdType_Partition_Emmc;
|
||||
} else {
|
||||
ShowFatalError(emusd_err_str);
|
||||
}
|
||||
} else if (sd_enabled == 0) {
|
||||
sd_cfg.base_cfg.type = secmon::EmummcSdType::EmummcSdType_None;
|
||||
} else {
|
||||
ShowFatalError(emusd_err_str);
|
||||
}
|
||||
|
||||
const secmon::EmummcConfiguration &emummc_cfg = GetEmummcConfig();
|
||||
bool emummc_driver_enabled = emummc_cfg.emmc_cfg.IsActive() || emummc_cfg.sd_cfg.IsActive();
|
||||
return emummc_driver_enabled;
|
||||
}
|
||||
|
||||
@@ -563,7 +384,7 @@ namespace ams::nxboot {
|
||||
/* Set some defaults. */
|
||||
storage_ctx.target_firmware = target_firmware;
|
||||
storage_ctx.lcd_vendor = GetDisplayLcdVendor();
|
||||
storage_ctx.emummc_cfg = g_emummc_cfg;
|
||||
storage_ctx.emummc_cfg = GetEmummcConfig();
|
||||
storage_ctx.flags[0] = secmon::SecureMonitorConfigurationFlag_Default;
|
||||
storage_ctx.flags[1] = secmon::SecureMonitorConfigurationFlag_None;
|
||||
storage_ctx.log_port = uart::Port_ReservedDebug;
|
||||
@@ -793,6 +614,7 @@ namespace ams::nxboot {
|
||||
|
||||
void SetupAndStartHorizon() {
|
||||
/* Get soc type. */
|
||||
|
||||
const auto soc_type = fuse::GetSocType();
|
||||
|
||||
/* Derive all keys. */
|
||||
@@ -800,11 +622,11 @@ namespace ams::nxboot {
|
||||
|
||||
/* Determine whether we're using emummc. */
|
||||
const bool emummc_driver_enabled = ConfigureEmummc();
|
||||
const bool emummc_enabled = g_emummc_cfg.emmc_cfg.base_cfg.type != secmon::EmummcEmmcType_None;
|
||||
const bool emummc_enabled = GetEmummcConfig().emmc_cfg.IsActive();
|
||||
|
||||
/* Initialize emummc. */
|
||||
/* NOTE: SYSTEM:/ accessible past this point. */
|
||||
InitializeEmummc(emummc_enabled, g_emummc_cfg.emmc_cfg);
|
||||
InitializeEmummc(emummc_enabled, GetEmummcConfig().emmc_cfg);
|
||||
|
||||
/* Read bootloader. */
|
||||
const u8 * const package1 = LoadPackage1(soc_type);
|
||||
|
||||
62
fusee/program/source/fusee_util.cpp
Normal file
62
fusee/program/source/fusee_util.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include "fusee_util.hpp"
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
u32 ParseHexInteger(const char *s) {
|
||||
u32 x = 0;
|
||||
if (s[0] == '0' && s[1] == 'x') {
|
||||
s += 2;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const char c = *(s++);
|
||||
|
||||
if (c == '\x00') {
|
||||
return x;
|
||||
} else {
|
||||
x <<= 4;
|
||||
|
||||
if ('0' <= c && c <= '9') {
|
||||
x |= (c - '0');
|
||||
} else if ('a' <= c && c <= 'f') {
|
||||
x |= (c - 'a') + 10;
|
||||
} else if ('A' <= c && c <= 'F') {
|
||||
x |= (c - 'A') + 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 ParseDecimalInteger(const char *s) {
|
||||
u32 x = 0;
|
||||
while (true) {
|
||||
const char c = *(s++);
|
||||
|
||||
if (c == '\x00') {
|
||||
return x;
|
||||
} else {
|
||||
x *= 10;
|
||||
|
||||
if ('0' <= c && c <= '9') {
|
||||
x += c - '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
24
fusee/program/source/fusee_util.hpp
Normal file
24
fusee/program/source/fusee_util.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
#pragma once
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
u32 ParseHexInteger(const char *s);
|
||||
u32 ParseDecimalInteger(const char *s);
|
||||
|
||||
}
|
||||
@@ -499,7 +499,12 @@ namespace ams::sdmmc::impl {
|
||||
/* Get the sector index alignment. */
|
||||
u32 sector_index_alignment = 0;
|
||||
if (!is_read) {
|
||||
#ifdef ATMOSPHERE_IS_EXOSPHERE
|
||||
/* Allow unaligned writes */
|
||||
constexpr u32 MmcWriteSectorAlignment = 0;
|
||||
#else
|
||||
constexpr u32 MmcWriteSectorAlignment = 16_KB / SectorSize;
|
||||
#endif
|
||||
sector_index_alignment = MmcWriteSectorAlignment;
|
||||
AMS_ABORT_UNLESS(util::IsAligned(sector_index, MmcWriteSectorAlignment));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user