ES: Use new filesystem interface in NandUtils

This commit is contained in:
Léo Lam 2018-05-06 18:49:43 +02:00
parent 359a5dcb54
commit 9ee2654be6
4 changed files with 155 additions and 105 deletions

View File

@ -326,7 +326,7 @@ private:
const std::vector<u8>& cert_chain, u32 iosc_handle = 0); const std::vector<u8>& cert_chain, u32 iosc_handle = 0);
// Start a title import. // Start a title import.
bool InitImport(u64 title_id); bool InitImport(const IOS::ES::TMDReader& tmd);
// Clean up the import content directory and move it back to /title. // Clean up the import content directory and move it back to /title.
bool FinishImport(const IOS::ES::TMDReader& tmd); bool FinishImport(const IOS::ES::TMDReader& tmd);
// Write a TMD for a title in /import atomically. // Write a TMD for a title in /import atomically.

View File

@ -13,8 +13,6 @@
#include <vector> #include <vector>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/File.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/NandPaths.h" #include "Common/NandPaths.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
@ -27,14 +25,14 @@ namespace HLE
{ {
namespace Device namespace Device
{ {
static IOS::ES::TMDReader FindTMD(u64 title_id, const std::string& tmd_path) static IOS::ES::TMDReader FindTMD(FS::FileSystem* fs, u64 title_id, const std::string& tmd_path)
{ {
File::IOFile file(tmd_path, "rb"); const auto file = fs->OpenFile(PID_KERNEL, PID_KERNEL, tmd_path, FS::Mode::Read);
if (!file) if (!file)
return {}; return {};
std::vector<u8> tmd_bytes(file.GetSize()); std::vector<u8> tmd_bytes(file->GetStatus()->size);
if (!file.ReadBytes(tmd_bytes.data(), tmd_bytes.size())) if (!file->Read(tmd_bytes.data(), tmd_bytes.size()))
return {}; return {};
return IOS::ES::TMDReader{std::move(tmd_bytes)}; return IOS::ES::TMDReader{std::move(tmd_bytes)};
@ -42,24 +40,24 @@ static IOS::ES::TMDReader FindTMD(u64 title_id, const std::string& tmd_path)
IOS::ES::TMDReader ES::FindImportTMD(u64 title_id) const IOS::ES::TMDReader ES::FindImportTMD(u64 title_id) const
{ {
return FindTMD(title_id, Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) + return FindTMD(m_ios.GetFS().get(), title_id,
"/content/title.tmd"); Common::GetImportTitlePath(title_id) + "/content/title.tmd");
} }
IOS::ES::TMDReader ES::FindInstalledTMD(u64 title_id) const IOS::ES::TMDReader ES::FindInstalledTMD(u64 title_id) const
{ {
return FindTMD(title_id, Common::GetTMDFileName(title_id, Common::FROM_SESSION_ROOT)); return FindTMD(m_ios.GetFS().get(), title_id, Common::GetTMDFileName(title_id));
} }
IOS::ES::TicketReader ES::FindSignedTicket(u64 title_id) const IOS::ES::TicketReader ES::FindSignedTicket(u64 title_id) const
{ {
const std::string path = Common::GetTicketFileName(title_id, Common::FROM_SESSION_ROOT); const std::string path = Common::GetTicketFileName(title_id);
File::IOFile ticket_file(path, "rb"); const auto ticket_file = m_ios.GetFS()->OpenFile(PID_KERNEL, PID_KERNEL, path, FS::Mode::Read);
if (!ticket_file) if (!ticket_file)
return {}; return {};
std::vector<u8> signed_ticket(ticket_file.GetSize()); std::vector<u8> signed_ticket(ticket_file->GetStatus()->size);
if (!ticket_file.ReadBytes(signed_ticket.data(), signed_ticket.size())) if (!ticket_file->Read(signed_ticket.data(), signed_ticket.size()))
return {}; return {};
return IOS::ES::TicketReader{std::move(signed_ticket)}; return IOS::ES::TicketReader{std::move(signed_ticket)};
@ -73,9 +71,10 @@ static bool IsValidPartOfTitleID(const std::string& string)
[](const auto character) { return std::isxdigit(character) != 0; }); [](const auto character) { return std::isxdigit(character) != 0; });
} }
static std::vector<u64> GetTitlesInTitleOrImport(const std::string& titles_dir) static std::vector<u64> GetTitlesInTitleOrImport(FS::FileSystem* fs, const std::string& titles_dir)
{ {
if (!File::IsDirectory(titles_dir)) const auto entries = fs->ReadDirectory(PID_KERNEL, PID_KERNEL, titles_dir);
if (!entries)
{ {
ERROR_LOG(IOS_ES, "%s is not a directory", titles_dir.c_str()); ERROR_LOG(IOS_ES, "%s is not a directory", titles_dir.c_str());
return {}; return {};
@ -85,22 +84,26 @@ static std::vector<u64> GetTitlesInTitleOrImport(const std::string& titles_dir)
// The /title and /import directories contain one directory per title type, and each of them has // The /title and /import directories contain one directory per title type, and each of them has
// a directory per title (where the name is the low 32 bits of the title ID in %08x format). // a directory per title (where the name is the low 32 bits of the title ID in %08x format).
const auto entries = File::ScanDirectoryTree(titles_dir, true); for (const std::string& title_type : *entries)
for (const File::FSTEntry& title_type : entries.children)
{ {
if (!title_type.isDirectory || !IsValidPartOfTitleID(title_type.virtualName)) if (!IsValidPartOfTitleID(title_type))
continue; continue;
if (title_type.children.empty()) const auto title_entries =
fs->ReadDirectory(PID_KERNEL, PID_KERNEL, titles_dir + '/' + title_type);
if (!title_entries)
continue; continue;
for (const File::FSTEntry& title_identifier : title_type.children) for (const std::string& title_identifier : *title_entries)
{ {
if (!title_identifier.isDirectory || !IsValidPartOfTitleID(title_identifier.virtualName)) if (!IsValidPartOfTitleID(title_identifier))
continue;
if (!fs->ReadDirectory(PID_KERNEL, PID_KERNEL,
titles_dir + '/' + title_type + '/' + title_identifier))
continue; continue;
const u32 type = std::stoul(title_type.virtualName, nullptr, 16); const u32 type = std::stoul(title_type, nullptr, 16);
const u32 identifier = std::stoul(title_identifier.virtualName, nullptr, 16); const u32 identifier = std::stoul(title_identifier, nullptr, 16);
title_ids.push_back(static_cast<u64>(type) << 32 | identifier); title_ids.push_back(static_cast<u64>(type) << 32 | identifier);
} }
} }
@ -116,18 +119,19 @@ static std::vector<u64> GetTitlesInTitleOrImport(const std::string& titles_dir)
std::vector<u64> ES::GetInstalledTitles() const std::vector<u64> ES::GetInstalledTitles() const
{ {
return GetTitlesInTitleOrImport(Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/title"); return GetTitlesInTitleOrImport(m_ios.GetFS().get(), "/title");
} }
std::vector<u64> ES::GetTitleImports() const std::vector<u64> ES::GetTitleImports() const
{ {
return GetTitlesInTitleOrImport(Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/import"); return GetTitlesInTitleOrImport(m_ios.GetFS().get(), "/import");
} }
std::vector<u64> ES::GetTitlesWithTickets() const std::vector<u64> ES::GetTitlesWithTickets() const
{ {
const std::string tickets_dir = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/ticket"; const auto fs = m_ios.GetFS();
if (!File::IsDirectory(tickets_dir)) const auto entries = fs->ReadDirectory(PID_KERNEL, PID_KERNEL, "/ticket");
if (!entries)
{ {
ERROR_LOG(IOS_ES, "/ticket is not a directory"); ERROR_LOG(IOS_ES, "/ticket is not a directory");
return {}; return {};
@ -137,25 +141,25 @@ std::vector<u64> ES::GetTitlesWithTickets() const
// The /ticket directory contains one directory per title type, and each of them contains // The /ticket directory contains one directory per title type, and each of them contains
// one ticket per title (where the name is the low 32 bits of the title ID in %08x format). // one ticket per title (where the name is the low 32 bits of the title ID in %08x format).
const auto entries = File::ScanDirectoryTree(tickets_dir, true); for (const std::string& title_type : *entries)
for (const File::FSTEntry& title_type : entries.children)
{ {
if (!title_type.isDirectory || !IsValidPartOfTitleID(title_type.virtualName)) if (!IsValidPartOfTitleID(title_type))
continue; continue;
if (title_type.children.empty()) const auto sub_entries = fs->ReadDirectory(PID_KERNEL, PID_KERNEL, "/ticket/" + title_type);
if (!sub_entries)
continue; continue;
for (const File::FSTEntry& ticket : title_type.children) for (const std::string& file_name : *sub_entries)
{ {
const std::string name_without_ext = ticket.virtualName.substr(0, 8); const std::string name_without_ext = file_name.substr(0, 8);
if (ticket.isDirectory || !IsValidPartOfTitleID(name_without_ext) || if (fs->ReadDirectory(PID_KERNEL, PID_KERNEL, "/ticket/" + title_type + '/' + file_name) ||
name_without_ext + ".tik" != ticket.virtualName) !IsValidPartOfTitleID(name_without_ext) || name_without_ext + ".tik" != file_name)
{ {
continue; continue;
} }
const u32 type = std::stoul(title_type.virtualName, nullptr, 16); const u32 type = std::stoul(title_type, nullptr, 16);
const u32 identifier = std::stoul(name_without_ext, nullptr, 16); const u32 identifier = std::stoul(name_without_ext, nullptr, 16);
title_ids.push_back(static_cast<u64>(type) << 32 | identifier); title_ids.push_back(static_cast<u64>(type) << 32 | identifier);
} }
@ -177,7 +181,8 @@ std::vector<IOS::ES::Content> ES::GetStoredContentsFromTMD(const IOS::ES::TMDRea
std::copy_if(contents.begin(), contents.end(), std::back_inserter(stored_contents), std::copy_if(contents.begin(), contents.end(), std::back_inserter(stored_contents),
[this, &tmd, &map](const IOS::ES::Content& content) { [this, &tmd, &map](const IOS::ES::Content& content) {
const std::string path = GetContentPath(tmd.GetTitleId(), content, map); const std::string path = GetContentPath(tmd.GetTitleId(), content, map);
return !path.empty() && m_ios.GetFS()->GetMetadata(0, 0, path).Succeeded(); return !path.empty() &&
m_ios.GetFS()->GetMetadata(PID_KERNEL, PID_KERNEL, path).Succeeded();
}); });
return stored_contents; return stored_contents;
@ -185,12 +190,11 @@ std::vector<IOS::ES::Content> ES::GetStoredContentsFromTMD(const IOS::ES::TMDRea
u32 ES::GetSharedContentsCount() const u32 ES::GetSharedContentsCount() const
{ {
const std::string shared1_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/shared1"; const auto entries = m_ios.GetFS()->ReadDirectory(PID_KERNEL, PID_KERNEL, "/shared1");
const auto entries = File::ScanDirectoryTree(shared1_path, false);
return static_cast<u32>( return static_cast<u32>(
std::count_if(entries.children.begin(), entries.children.end(), [](const auto& entry) { std::count_if(entries->begin(), entries->end(), [this](const std::string& entry) {
return !entry.isDirectory && entry.virtualName.size() == 12 && return !m_ios.GetFS()->ReadDirectory(PID_KERNEL, PID_KERNEL, "/shared1/" + entry) &&
entry.virtualName.compare(8, 4, ".app") == 0; entry.size() == 12 && entry.compare(8, 4, ".app") == 0;
})); }));
} }
@ -200,65 +204,102 @@ std::vector<std::array<u8, 20>> ES::GetSharedContents() const
return map.GetHashes(); return map.GetHashes();
} }
bool ES::InitImport(u64 title_id) static bool DeleteDirectoriesIfEmpty(FS::FileSystem* fs, const std::string& path)
{ {
const std::string content_dir = Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT); std::string::size_type position = std::string::npos;
const std::string data_dir = Common::GetTitleDataPath(title_id, Common::FROM_SESSION_ROOT); do
for (const auto& dir : {content_dir, data_dir})
{ {
if (!File::IsDirectory(dir) && !File::CreateFullPath(dir) && !File::CreateDir(dir)) const auto directory = fs->ReadDirectory(PID_KERNEL, PID_KERNEL, path.substr(0, position));
if ((directory && directory->empty()) ||
(!directory && directory.Error() != FS::ResultCode::NotFound))
{ {
ERROR_LOG(IOS_ES, "InitImport: Failed to create title dirs for %016" PRIx64, title_id); if (fs->Delete(PID_KERNEL, PID_KERNEL, path.substr(0, position)) != FS::ResultCode::Success)
return false; return false;
} }
position = path.find_last_of('/', position - 1);
} while (position != 0);
return true;
}
bool ES::InitImport(const IOS::ES::TMDReader& tmd)
{
const auto fs = m_ios.GetFS();
const std::string content_dir = Common::GetTitleContentPath(tmd.GetTitleId());
const std::string import_content_dir = Common::GetImportTitlePath(tmd.GetTitleId()) + "/content";
const auto result1 = fs->CreateFullPath(PID_KERNEL, PID_KERNEL, content_dir + '/', 0,
FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::Read);
const auto result2 = fs->SetMetadata(PID_KERNEL, content_dir, PID_KERNEL, PID_KERNEL, 0,
FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::None);
const auto result3 = fs->CreateFullPath(PID_KERNEL, PID_KERNEL, import_content_dir + '/', 0,
FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::None);
if (result1 != FS::ResultCode::Success || result2 != FS::ResultCode::Success ||
result3 != FS::ResultCode::Success)
{
ERROR_LOG(IOS_ES, "InitImport: Failed to create content dir for %016" PRIx64, tmd.GetTitleId());
return false;
} }
IOS::ES::UIDSys uid_sys{m_ios.GetFS()}; const std::string data_dir = Common::GetTitleDataPath(tmd.GetTitleId());
uid_sys.GetOrInsertUIDForTitle(title_id); const auto data_dir_contents = fs->ReadDirectory(PID_KERNEL, PID_KERNEL, data_dir);
if (!data_dir_contents &&
(data_dir_contents.Error() != FS::ResultCode::NotFound ||
fs->CreateDirectory(PID_KERNEL, PID_KERNEL, data_dir, 0, FS::Mode::ReadWrite, FS::Mode::None,
FS::Mode::None) != FS::ResultCode::Success))
{
return false;
}
IOS::ES::UIDSys uid_sys{fs};
const u32 uid = uid_sys.GetOrInsertUIDForTitle(tmd.GetTitleId());
if (fs->SetMetadata(0, data_dir, uid, tmd.GetGroupId(), 0, FS::Mode::ReadWrite, FS::Mode::None,
FS::Mode::None) != FS::ResultCode::Success)
{
return false;
}
// IOS moves the title content directory to /import if the TMD exists during an import. // IOS moves the title content directory to /import if the TMD exists during an import.
if (File::Exists(Common::GetTMDFileName(title_id, Common::FROM_SESSION_ROOT))) const auto file_info =
fs->GetMetadata(PID_KERNEL, PID_KERNEL, Common::GetTMDFileName(tmd.GetTitleId()));
if (!file_info || !file_info->is_file)
return true;
const auto result = fs->Rename(PID_KERNEL, PID_KERNEL, content_dir, import_content_dir);
if (result != FS::ResultCode::Success)
{ {
const std::string import_content_dir = ERROR_LOG(IOS_ES, "InitImport: Failed to move content dir for %016" PRIx64, tmd.GetTitleId());
Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) + "/content";
File::CreateFullPath(import_content_dir);
if (!File::Rename(content_dir, import_content_dir))
{
ERROR_LOG(IOS_ES, "InitImport: Failed to move content dir for %016" PRIx64, title_id);
return false; return false;
} }
} DeleteDirectoriesIfEmpty(m_ios.GetFS().get(), import_content_dir);
return true; return true;
} }
bool ES::FinishImport(const IOS::ES::TMDReader& tmd) bool ES::FinishImport(const IOS::ES::TMDReader& tmd)
{ {
const auto fs = m_ios.GetFS();
const u64 title_id = tmd.GetTitleId(); const u64 title_id = tmd.GetTitleId();
const std::string import_content_dir = const std::string import_content_dir = Common::GetImportTitlePath(title_id) + "/content";
Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) + "/content";
// Remove everything not listed in the TMD. // Remove everything not listed in the TMD.
std::unordered_set<std::string> expected_entries = {"title.tmd"}; std::unordered_set<std::string> expected_entries = {"title.tmd"};
for (const auto& content_info : tmd.GetContents()) for (const auto& content_info : tmd.GetContents())
expected_entries.insert(StringFromFormat("%08x.app", content_info.id)); expected_entries.insert(StringFromFormat("%08x.app", content_info.id));
const auto entries = File::ScanDirectoryTree(import_content_dir, false); const auto entries = fs->ReadDirectory(PID_KERNEL, PID_KERNEL, import_content_dir);
for (const File::FSTEntry& entry : entries.children) if (!entries)
return false;
for (const std::string& name : *entries)
{ {
const std::string absolute_path = import_content_dir + '/' + name;
// There should not be any directory in there. Remove it. // There should not be any directory in there. Remove it.
if (entry.isDirectory) if (fs->ReadDirectory(PID_KERNEL, PID_KERNEL, absolute_path))
File::DeleteDirRecursively(entry.physicalName); fs->Delete(PID_KERNEL, PID_KERNEL, absolute_path);
else if (expected_entries.find(entry.virtualName) == expected_entries.end()) else if (expected_entries.find(name) == expected_entries.end())
File::Delete(entry.physicalName); fs->Delete(PID_KERNEL, PID_KERNEL, absolute_path);
} }
const std::string content_dir = Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT); const std::string content_dir = Common::GetTitleContentPath(title_id);
if (File::IsDirectory(content_dir)) if (fs->Rename(PID_KERNEL, PID_KERNEL, import_content_dir, content_dir) !=
{ FS::ResultCode::Success)
WARN_LOG(IOS_ES, "FinishImport: %s already exists -- removing", content_dir.c_str());
File::DeleteDirRecursively(content_dir);
}
if (!File::Rename(import_content_dir, content_dir))
{ {
ERROR_LOG(IOS_ES, "FinishImport: Failed to rename import directory to %s", content_dir.c_str()); ERROR_LOG(IOS_ES, "FinishImport: Failed to rename import directory to %s", content_dir.c_str());
return false; return false;
@ -268,28 +309,34 @@ bool ES::FinishImport(const IOS::ES::TMDReader& tmd)
bool ES::WriteImportTMD(const IOS::ES::TMDReader& tmd) bool ES::WriteImportTMD(const IOS::ES::TMDReader& tmd)
{ {
const std::string tmd_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/tmp/title.tmd"; const auto fs = m_ios.GetFS();
File::CreateFullPath(tmd_path); const std::string tmd_path = "/tmp/title.tmd";
{ {
File::IOFile file(tmd_path, "wb"); fs->CreateFile(PID_KERNEL, PID_KERNEL, tmd_path, 0, FS::Mode::ReadWrite, FS::Mode::ReadWrite,
if (!file.WriteBytes(tmd.GetBytes().data(), tmd.GetBytes().size())) FS::Mode::None);
const auto file = fs->OpenFile(PID_KERNEL, PID_KERNEL, tmd_path, FS::Mode::Write);
if (!file || !file->Write(tmd.GetBytes().data(), tmd.GetBytes().size()))
return false; return false;
} }
const std::string dest = Common::GetImportTitlePath(tmd.GetTitleId(), Common::FROM_SESSION_ROOT) + const std::string dest = Common::GetImportTitlePath(tmd.GetTitleId()) + "/content/title.tmd";
"/content/title.tmd"; return fs->Rename(PID_KERNEL, PID_KERNEL, tmd_path, dest) == FS::ResultCode::Success;
return File::Rename(tmd_path, dest);
} }
void ES::FinishStaleImport(u64 title_id) void ES::FinishStaleImport(u64 title_id)
{ {
const auto fs = m_ios.GetFS();
const auto import_tmd = FindImportTMD(title_id); const auto import_tmd = FindImportTMD(title_id);
if (!import_tmd.IsValid()) if (!import_tmd.IsValid())
File::DeleteDirRecursively(Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) + {
"/content"); fs->Delete(PID_KERNEL, PID_KERNEL, Common::GetImportTitlePath(title_id) + "/content");
DeleteDirectoriesIfEmpty(fs.get(), Common::GetImportTitlePath(title_id));
DeleteDirectoriesIfEmpty(fs.get(), Common::GetTitlePath(title_id));
}
else else
{
FinishImport(import_tmd); FinishImport(import_tmd);
}
} }
void ES::FinishAllStaleImports() void ES::FinishAllStaleImports()
@ -297,10 +344,6 @@ void ES::FinishAllStaleImports()
const std::vector<u64> titles = GetTitleImports(); const std::vector<u64> titles = GetTitleImports();
for (const u64& title_id : titles) for (const u64& title_id : titles)
FinishStaleImport(title_id); FinishStaleImport(title_id);
const std::string import_dir = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/import";
File::DeleteDirRecursively(import_dir);
File::CreateDir(import_dir);
} }
std::string ES::GetContentPath(const u64 title_id, const IOS::ES::Content& content, std::string ES::GetContentPath(const u64 title_id, const IOS::ES::Content& content,

View File

@ -156,7 +156,7 @@ ReturnCode ES::ImportTmd(Context& context, const std::vector<u8>& tmd_bytes)
if (ret != IPC_SUCCESS) if (ret != IPC_SUCCESS)
return ret; return ret;
if (!InitImport(context.title_import_export.tmd.GetTitleId())) if (!InitImport(context.title_import_export.tmd))
return ES_EIO; return ES_EIO;
ret = ret =
@ -238,7 +238,7 @@ ReturnCode ES::ImportTitleInit(Context& context, const std::vector<u8>& tmd_byte
if (ret != IPC_SUCCESS) if (ret != IPC_SUCCESS)
return ret; return ret;
if (!InitImport(context.title_import_export.tmd.GetTitleId())) if (!InitImport(context.title_import_export.tmd))
return ES_EIO; return ES_EIO;
context.title_import_export.valid = true; context.title_import_export.valid = true;

View File

@ -266,11 +266,27 @@ u16 Kernel::GetGidForPPC() const
return m_ppc_gid; return m_ppc_gid;
} }
static std::vector<u8> ReadBootContent(FS::FileSystem* fs, const std::string& path, size_t max_size)
{
const auto file = fs->OpenFile(0, 0, path, FS::Mode::Read);
if (!file)
return {};
const size_t file_size = file->GetStatus()->size;
if (max_size != 0 && file_size > max_size)
return {};
std::vector<u8> buffer(file_size);
if (!file->Read(buffer.data(), buffer.size()))
return {};
return buffer;
}
// This corresponds to syscall 0x41, which loads a binary from the NAND and bootstraps the PPC. // This corresponds to syscall 0x41, which loads a binary from the NAND and bootstraps the PPC.
// Unlike 0x42, IOS will set up some constants in memory before booting the PPC. // Unlike 0x42, IOS will set up some constants in memory before booting the PPC.
bool Kernel::BootstrapPPC(const std::string& boot_content_path) bool Kernel::BootstrapPPC(const std::string& boot_content_path)
{ {
const DolReader dol{boot_content_path}; const DolReader dol{ReadBootContent(m_fs.get(), boot_content_path, 0)};
if (!dol.IsValid()) if (!dol.IsValid())
return false; return false;
@ -329,16 +345,7 @@ bool Kernel::BootIOS(const u64 ios_title_id, const std::string& boot_content_pat
// Load the ARM binary to memory (if possible). // Load the ARM binary to memory (if possible).
// Because we do not actually emulate the Starlet, only load the sections that are in MEM1. // Because we do not actually emulate the Starlet, only load the sections that are in MEM1.
File::IOFile file{boot_content_path, "rb"}; ARMBinary binary{ReadBootContent(m_fs.get(), boot_content_path, 0xB00000)};
// TODO: should return IPC_ERROR_MAX.
if (file.GetSize() > 0xB00000)
return false;
std::vector<u8> data(file.GetSize());
if (!file.ReadBytes(data.data(), data.size()))
return false;
ARMBinary binary{std::move(data)};
if (!binary.IsValid()) if (!binary.IsValid())
return false; return false;