another try
This commit is contained in:
@@ -1,243 +1,243 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/deconstructed_rom_directory.h"
|
||||
#include "core/loader/nso.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_,
|
||||
bool override_update_)
|
||||
: AppLoader(std::move(file_)), override_update(override_update_) {
|
||||
const auto file_dir = file->GetContainingDirectory();
|
||||
|
||||
// Title ID
|
||||
const auto npdm = file_dir->GetFile("main.npdm");
|
||||
if (npdm != nullptr) {
|
||||
const auto res = metadata.Load(npdm);
|
||||
if (res == ResultStatus::Success)
|
||||
title_id = metadata.GetTitleID();
|
||||
}
|
||||
|
||||
// Icon
|
||||
FileSys::VirtualFile icon_file = nullptr;
|
||||
for (const auto& language : FileSys::LANGUAGE_NAMES) {
|
||||
icon_file = file_dir->GetFile("icon_" + std::string(language) + ".dat");
|
||||
if (icon_file != nullptr) {
|
||||
icon_data = icon_file->ReadAllBytes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (icon_data.empty()) {
|
||||
// Any png, jpeg, or bmp file
|
||||
const auto& files = file_dir->GetFiles();
|
||||
const auto icon_iter =
|
||||
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& f) {
|
||||
return f->GetExtension() == "png" || f->GetExtension() == "jpg" ||
|
||||
f->GetExtension() == "bmp" || f->GetExtension() == "jpeg";
|
||||
});
|
||||
if (icon_iter != files.end())
|
||||
icon_data = (*icon_iter)->ReadAllBytes();
|
||||
}
|
||||
|
||||
// Metadata
|
||||
FileSys::VirtualFile nacp_file = file_dir->GetFile("control.nacp");
|
||||
if (nacp_file == nullptr) {
|
||||
const auto& files = file_dir->GetFiles();
|
||||
const auto nacp_iter =
|
||||
std::find_if(files.begin(), files.end(),
|
||||
[](const FileSys::VirtualFile& f) { return f->GetExtension() == "nacp"; });
|
||||
if (nacp_iter != files.end())
|
||||
nacp_file = *nacp_iter;
|
||||
}
|
||||
|
||||
if (nacp_file != nullptr) {
|
||||
FileSys::NACP nacp(nacp_file);
|
||||
name = nacp.GetApplicationName();
|
||||
}
|
||||
}
|
||||
|
||||
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
|
||||
FileSys::VirtualDir directory, bool override_update_)
|
||||
: AppLoader(directory->GetFile("main")), dir(std::move(directory)),
|
||||
override_update(override_update_) {}
|
||||
|
||||
FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& dir_file) {
|
||||
if (FileSys::IsDirectoryExeFS(dir_file->GetContainingDirectory())) {
|
||||
return FileType::DeconstructedRomDirectory;
|
||||
}
|
||||
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load(
|
||||
Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
if (dir == nullptr) {
|
||||
if (file == nullptr) {
|
||||
return {ResultStatus::ErrorNullFile, {}};
|
||||
}
|
||||
|
||||
dir = file->GetContainingDirectory();
|
||||
}
|
||||
|
||||
// Read meta to determine title ID
|
||||
FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
|
||||
if (npdm == nullptr) {
|
||||
return {ResultStatus::ErrorMissingNPDM, {}};
|
||||
}
|
||||
|
||||
const ResultStatus result = metadata.Load(npdm);
|
||||
if (result != ResultStatus::Success) {
|
||||
return {result, {}};
|
||||
}
|
||||
|
||||
if (override_update) {
|
||||
const FileSys::PatchManager patch_manager(
|
||||
metadata.GetTitleID(), system.GetFileSystemController(), system.GetContentProvider());
|
||||
dir = patch_manager.PatchExeFS(dir);
|
||||
}
|
||||
|
||||
// Reread in case PatchExeFS affected the main.npdm
|
||||
npdm = dir->GetFile("main.npdm");
|
||||
if (npdm == nullptr) {
|
||||
return {ResultStatus::ErrorMissingNPDM, {}};
|
||||
}
|
||||
|
||||
const ResultStatus result2 = metadata.Load(npdm);
|
||||
if (result2 != ResultStatus::Success) {
|
||||
return {result2, {}};
|
||||
}
|
||||
metadata.Print();
|
||||
|
||||
const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
|
||||
"subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7",
|
||||
"subsdk8", "subsdk9", "sdk"};
|
||||
|
||||
// Use the NSO module loader to figure out the code layout
|
||||
std::size_t code_size{};
|
||||
for (const auto& module : static_modules) {
|
||||
const FileSys::VirtualFile module_file{dir->GetFile(module)};
|
||||
if (!module_file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
|
||||
const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
|
||||
process, system, *module_file, code_size, should_pass_arguments, false);
|
||||
if (!tentative_next_load_addr) {
|
||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||
}
|
||||
|
||||
code_size = *tentative_next_load_addr;
|
||||
}
|
||||
|
||||
// Setup the process code layout
|
||||
if (process.LoadFromMetadata(metadata, code_size).IsError()) {
|
||||
return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
|
||||
}
|
||||
|
||||
// Load NSO modules
|
||||
modules.clear();
|
||||
const VAddr base_address{process.PageTable().GetCodeRegionStart()};
|
||||
VAddr next_load_addr{base_address};
|
||||
const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
for (const auto& module : static_modules) {
|
||||
const FileSys::VirtualFile module_file{dir->GetFile(module)};
|
||||
if (!module_file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const VAddr load_addr{next_load_addr};
|
||||
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
|
||||
const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
|
||||
process, system, *module_file, load_addr, should_pass_arguments, true, pm);
|
||||
if (!tentative_next_load_addr) {
|
||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||
}
|
||||
|
||||
next_load_addr = *tentative_next_load_addr;
|
||||
modules.insert_or_assign(load_addr, module);
|
||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
|
||||
}
|
||||
|
||||
// Find the RomFS by searching for a ".romfs" file in this directory
|
||||
const auto& files = dir->GetFiles();
|
||||
const auto romfs_iter =
|
||||
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& f) {
|
||||
return f->GetName().find(".romfs") != std::string::npos;
|
||||
});
|
||||
|
||||
// Register the RomFS if a ".romfs" file was found
|
||||
if (romfs_iter != files.end() && *romfs_iter != nullptr) {
|
||||
romfs = *romfs_iter;
|
||||
system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
|
||||
*this, system.GetContentProvider(), system.GetFileSystemController()));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
return {ResultStatus::Success,
|
||||
LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}};
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& out_dir) {
|
||||
if (romfs == nullptr) {
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
}
|
||||
|
||||
out_dir = romfs;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& out_buffer) {
|
||||
if (icon_data.empty()) {
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
}
|
||||
|
||||
out_buffer = icon_data;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) {
|
||||
out_program_id = title_id;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& out_title) {
|
||||
if (name.empty()) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
out_title = name;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadNSOModules(Modules& out_modules) {
|
||||
if (!is_loaded) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
|
||||
out_modules = this->modules;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/deconstructed_rom_directory.h"
|
||||
#include "core/loader/nso.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_,
|
||||
bool override_update_)
|
||||
: AppLoader(std::move(file_)), override_update(override_update_) {
|
||||
const auto file_dir = file->GetContainingDirectory();
|
||||
|
||||
// Title ID
|
||||
const auto npdm = file_dir->GetFile("main.npdm");
|
||||
if (npdm != nullptr) {
|
||||
const auto res = metadata.Load(npdm);
|
||||
if (res == ResultStatus::Success)
|
||||
title_id = metadata.GetTitleID();
|
||||
}
|
||||
|
||||
// Icon
|
||||
FileSys::VirtualFile icon_file = nullptr;
|
||||
for (const auto& language : FileSys::LANGUAGE_NAMES) {
|
||||
icon_file = file_dir->GetFile("icon_" + std::string(language) + ".dat");
|
||||
if (icon_file != nullptr) {
|
||||
icon_data = icon_file->ReadAllBytes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (icon_data.empty()) {
|
||||
// Any png, jpeg, or bmp file
|
||||
const auto& files = file_dir->GetFiles();
|
||||
const auto icon_iter =
|
||||
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& f) {
|
||||
return f->GetExtension() == "png" || f->GetExtension() == "jpg" ||
|
||||
f->GetExtension() == "bmp" || f->GetExtension() == "jpeg";
|
||||
});
|
||||
if (icon_iter != files.end())
|
||||
icon_data = (*icon_iter)->ReadAllBytes();
|
||||
}
|
||||
|
||||
// Metadata
|
||||
FileSys::VirtualFile nacp_file = file_dir->GetFile("control.nacp");
|
||||
if (nacp_file == nullptr) {
|
||||
const auto& files = file_dir->GetFiles();
|
||||
const auto nacp_iter =
|
||||
std::find_if(files.begin(), files.end(),
|
||||
[](const FileSys::VirtualFile& f) { return f->GetExtension() == "nacp"; });
|
||||
if (nacp_iter != files.end())
|
||||
nacp_file = *nacp_iter;
|
||||
}
|
||||
|
||||
if (nacp_file != nullptr) {
|
||||
FileSys::NACP nacp(nacp_file);
|
||||
name = nacp.GetApplicationName();
|
||||
}
|
||||
}
|
||||
|
||||
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
|
||||
FileSys::VirtualDir directory, bool override_update_)
|
||||
: AppLoader(directory->GetFile("main")), dir(std::move(directory)),
|
||||
override_update(override_update_) {}
|
||||
|
||||
FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& dir_file) {
|
||||
if (FileSys::IsDirectoryExeFS(dir_file->GetContainingDirectory())) {
|
||||
return FileType::DeconstructedRomDirectory;
|
||||
}
|
||||
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load(
|
||||
Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
if (dir == nullptr) {
|
||||
if (file == nullptr) {
|
||||
return {ResultStatus::ErrorNullFile, {}};
|
||||
}
|
||||
|
||||
dir = file->GetContainingDirectory();
|
||||
}
|
||||
|
||||
// Read meta to determine title ID
|
||||
FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
|
||||
if (npdm == nullptr) {
|
||||
return {ResultStatus::ErrorMissingNPDM, {}};
|
||||
}
|
||||
|
||||
const ResultStatus result = metadata.Load(npdm);
|
||||
if (result != ResultStatus::Success) {
|
||||
return {result, {}};
|
||||
}
|
||||
|
||||
if (override_update) {
|
||||
const FileSys::PatchManager patch_manager(
|
||||
metadata.GetTitleID(), system.GetFileSystemController(), system.GetContentProvider());
|
||||
dir = patch_manager.PatchExeFS(dir);
|
||||
}
|
||||
|
||||
// Reread in case PatchExeFS affected the main.npdm
|
||||
npdm = dir->GetFile("main.npdm");
|
||||
if (npdm == nullptr) {
|
||||
return {ResultStatus::ErrorMissingNPDM, {}};
|
||||
}
|
||||
|
||||
const ResultStatus result2 = metadata.Load(npdm);
|
||||
if (result2 != ResultStatus::Success) {
|
||||
return {result2, {}};
|
||||
}
|
||||
metadata.Print();
|
||||
|
||||
const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
|
||||
"subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7",
|
||||
"subsdk8", "subsdk9", "sdk"};
|
||||
|
||||
// Use the NSO module loader to figure out the code layout
|
||||
std::size_t code_size{};
|
||||
for (const auto& module : static_modules) {
|
||||
const FileSys::VirtualFile module_file{dir->GetFile(module)};
|
||||
if (!module_file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
|
||||
const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
|
||||
process, system, *module_file, code_size, should_pass_arguments, false);
|
||||
if (!tentative_next_load_addr) {
|
||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||
}
|
||||
|
||||
code_size = *tentative_next_load_addr;
|
||||
}
|
||||
|
||||
// Setup the process code layout
|
||||
if (process.LoadFromMetadata(metadata, code_size).IsError()) {
|
||||
return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
|
||||
}
|
||||
|
||||
// Load NSO modules
|
||||
modules.clear();
|
||||
const VAddr base_address{process.PageTable().GetCodeRegionStart()};
|
||||
VAddr next_load_addr{base_address};
|
||||
const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
for (const auto& module : static_modules) {
|
||||
const FileSys::VirtualFile module_file{dir->GetFile(module)};
|
||||
if (!module_file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const VAddr load_addr{next_load_addr};
|
||||
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
|
||||
const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
|
||||
process, system, *module_file, load_addr, should_pass_arguments, true, pm);
|
||||
if (!tentative_next_load_addr) {
|
||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||
}
|
||||
|
||||
next_load_addr = *tentative_next_load_addr;
|
||||
modules.insert_or_assign(load_addr, module);
|
||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
|
||||
}
|
||||
|
||||
// Find the RomFS by searching for a ".romfs" file in this directory
|
||||
const auto& files = dir->GetFiles();
|
||||
const auto romfs_iter =
|
||||
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& f) {
|
||||
return f->GetName().find(".romfs") != std::string::npos;
|
||||
});
|
||||
|
||||
// Register the RomFS if a ".romfs" file was found
|
||||
if (romfs_iter != files.end() && *romfs_iter != nullptr) {
|
||||
romfs = *romfs_iter;
|
||||
system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
|
||||
*this, system.GetContentProvider(), system.GetFileSystemController()));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
return {ResultStatus::Success,
|
||||
LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}};
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& out_dir) {
|
||||
if (romfs == nullptr) {
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
}
|
||||
|
||||
out_dir = romfs;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& out_buffer) {
|
||||
if (icon_data.empty()) {
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
}
|
||||
|
||||
out_buffer = icon_data;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) {
|
||||
out_program_id = title_id;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& out_title) {
|
||||
if (name.empty()) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
out_title = name;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadNSOModules(Modules& out_modules) {
|
||||
if (!is_loaded) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
|
||||
out_modules = this->modules;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,69 +1,69 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
/**
|
||||
* This class loads a "deconstructed ROM directory", which are the typical format we see for Switch
|
||||
* game dumps. The path should be a "main" NSO, which must be in a directory that contains the other
|
||||
* standard ExeFS NSOs (e.g. rtld, sdk, etc.). It will automatically find and load these.
|
||||
* Furthermore, it will look for the first .romfs file (optionally) and use this for the RomFS.
|
||||
*/
|
||||
class AppLoader_DeconstructedRomDirectory final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file,
|
||||
bool override_update_ = false);
|
||||
|
||||
// Overload to accept exefs directory. Must contain 'main' and 'main.npdm'
|
||||
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory,
|
||||
bool override_update_ = false);
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is a deconstructed ROM directory.
|
||||
*
|
||||
* @param dir_file The file to verify.
|
||||
*
|
||||
* @return FileType::DeconstructedRomDirectory, or FileType::Error
|
||||
* if the file is not a deconstructed ROM directory.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& dir_file);
|
||||
|
||||
FileType GetFileType() const override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& out_dir) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& out_buffer) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
bool IsRomFSUpdatable() const override;
|
||||
|
||||
ResultStatus ReadNSOModules(Modules& out_modules) override;
|
||||
|
||||
private:
|
||||
FileSys::ProgramMetadata metadata;
|
||||
FileSys::VirtualFile romfs;
|
||||
FileSys::VirtualDir dir;
|
||||
|
||||
std::vector<u8> icon_data;
|
||||
std::string name;
|
||||
u64 title_id{};
|
||||
bool override_update;
|
||||
|
||||
Modules modules;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
/**
|
||||
* This class loads a "deconstructed ROM directory", which are the typical format we see for Switch
|
||||
* game dumps. The path should be a "main" NSO, which must be in a directory that contains the other
|
||||
* standard ExeFS NSOs (e.g. rtld, sdk, etc.). It will automatically find and load these.
|
||||
* Furthermore, it will look for the first .romfs file (optionally) and use this for the RomFS.
|
||||
*/
|
||||
class AppLoader_DeconstructedRomDirectory final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file,
|
||||
bool override_update_ = false);
|
||||
|
||||
// Overload to accept exefs directory. Must contain 'main' and 'main.npdm'
|
||||
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory,
|
||||
bool override_update_ = false);
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is a deconstructed ROM directory.
|
||||
*
|
||||
* @param dir_file The file to verify.
|
||||
*
|
||||
* @return FileType::DeconstructedRomDirectory, or FileType::Error
|
||||
* if the file is not a deconstructed ROM directory.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& dir_file);
|
||||
|
||||
FileType GetFileType() const override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& out_dir) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& out_buffer) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
bool IsRomFSUpdatable() const override;
|
||||
|
||||
ResultStatus ReadNSOModules(Modules& out_modules) override;
|
||||
|
||||
private:
|
||||
FileSys::ProgramMetadata metadata;
|
||||
FileSys::VirtualFile romfs;
|
||||
FileSys::VirtualDir dir;
|
||||
|
||||
std::vector<u8> icon_data;
|
||||
std::string name;
|
||||
u64 title_id{};
|
||||
bool override_update;
|
||||
|
||||
Modules modules;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,109 +1,109 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include "core/file_sys/kernel_executable.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/loader/kip.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
namespace {
|
||||
constexpr u32 PageAlignSize(u32 size) {
|
||||
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_)
|
||||
: AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {}
|
||||
|
||||
AppLoader_KIP::~AppLoader_KIP() = default;
|
||||
|
||||
FileType AppLoader_KIP::IdentifyType(const FileSys::VirtualFile& in_file) {
|
||||
u32_le magic{};
|
||||
if (in_file->GetSize() < sizeof(u32) || in_file->ReadObject(&magic) != sizeof(u32)) {
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
if (magic == Common::MakeMagic('K', 'I', 'P', '1')) {
|
||||
return FileType::KIP;
|
||||
}
|
||||
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
FileType AppLoader_KIP::GetFileType() const {
|
||||
return (kip != nullptr && kip->GetStatus() == ResultStatus::Success) ? FileType::KIP
|
||||
: FileType::Error;
|
||||
}
|
||||
|
||||
AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
|
||||
[[maybe_unused]] Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
if (kip == nullptr) {
|
||||
return {ResultStatus::ErrorNullFile, {}};
|
||||
}
|
||||
|
||||
if (kip->GetStatus() != ResultStatus::Success) {
|
||||
return {kip->GetStatus(), {}};
|
||||
}
|
||||
|
||||
const auto get_kip_address_space_type = [](const auto& kip_type) {
|
||||
return kip_type.Is64Bit()
|
||||
? (kip_type.Is39BitAddressSpace() ? FileSys::ProgramAddressSpaceType::Is39Bit
|
||||
: FileSys::ProgramAddressSpaceType::Is36Bit)
|
||||
: FileSys::ProgramAddressSpaceType::Is32Bit;
|
||||
};
|
||||
|
||||
const auto address_space = get_kip_address_space_type(*kip);
|
||||
|
||||
FileSys::ProgramMetadata metadata;
|
||||
metadata.LoadManual(kip->Is64Bit(), address_space, kip->GetMainThreadPriority(),
|
||||
kip->GetMainThreadCpuCore(), kip->GetMainThreadStackSize(),
|
||||
kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, 0x1FE00000,
|
||||
kip->GetKernelCapabilities());
|
||||
|
||||
Kernel::CodeSet codeset;
|
||||
Kernel::PhysicalMemory program_image;
|
||||
|
||||
const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment,
|
||||
const std::vector<u8>& data, u32 offset) {
|
||||
segment.addr = offset;
|
||||
segment.offset = offset;
|
||||
segment.size = PageAlignSize(static_cast<u32>(data.size()));
|
||||
program_image.resize(offset + data.size());
|
||||
std::memcpy(program_image.data() + offset, data.data(), data.size());
|
||||
};
|
||||
|
||||
load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset());
|
||||
load_segment(codeset.RODataSegment(), kip->GetRODataSection(), kip->GetRODataOffset());
|
||||
load_segment(codeset.DataSegment(), kip->GetDataSection(), kip->GetDataOffset());
|
||||
|
||||
program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
|
||||
codeset.DataSegment().size += kip->GetBSSSize();
|
||||
|
||||
// Setup the process code layout
|
||||
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size())
|
||||
.IsError()) {
|
||||
return {ResultStatus::ErrorNotInitialized, {}};
|
||||
}
|
||||
|
||||
codeset.memory = std::move(program_image);
|
||||
const VAddr base_address = process.PageTable().GetCodeRegionStart();
|
||||
process.LoadModule(std::move(codeset), base_address);
|
||||
|
||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
|
||||
|
||||
is_loaded = true;
|
||||
return {ResultStatus::Success,
|
||||
LoadParameters{kip->GetMainThreadPriority(), kip->GetMainThreadStackSize()}};
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include "core/file_sys/kernel_executable.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/loader/kip.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
namespace {
|
||||
constexpr u32 PageAlignSize(u32 size) {
|
||||
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_)
|
||||
: AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {}
|
||||
|
||||
AppLoader_KIP::~AppLoader_KIP() = default;
|
||||
|
||||
FileType AppLoader_KIP::IdentifyType(const FileSys::VirtualFile& in_file) {
|
||||
u32_le magic{};
|
||||
if (in_file->GetSize() < sizeof(u32) || in_file->ReadObject(&magic) != sizeof(u32)) {
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
if (magic == Common::MakeMagic('K', 'I', 'P', '1')) {
|
||||
return FileType::KIP;
|
||||
}
|
||||
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
FileType AppLoader_KIP::GetFileType() const {
|
||||
return (kip != nullptr && kip->GetStatus() == ResultStatus::Success) ? FileType::KIP
|
||||
: FileType::Error;
|
||||
}
|
||||
|
||||
AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
|
||||
[[maybe_unused]] Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
if (kip == nullptr) {
|
||||
return {ResultStatus::ErrorNullFile, {}};
|
||||
}
|
||||
|
||||
if (kip->GetStatus() != ResultStatus::Success) {
|
||||
return {kip->GetStatus(), {}};
|
||||
}
|
||||
|
||||
const auto get_kip_address_space_type = [](const auto& kip_type) {
|
||||
return kip_type.Is64Bit()
|
||||
? (kip_type.Is39BitAddressSpace() ? FileSys::ProgramAddressSpaceType::Is39Bit
|
||||
: FileSys::ProgramAddressSpaceType::Is36Bit)
|
||||
: FileSys::ProgramAddressSpaceType::Is32Bit;
|
||||
};
|
||||
|
||||
const auto address_space = get_kip_address_space_type(*kip);
|
||||
|
||||
FileSys::ProgramMetadata metadata;
|
||||
metadata.LoadManual(kip->Is64Bit(), address_space, kip->GetMainThreadPriority(),
|
||||
kip->GetMainThreadCpuCore(), kip->GetMainThreadStackSize(),
|
||||
kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, 0x1FE00000,
|
||||
kip->GetKernelCapabilities());
|
||||
|
||||
Kernel::CodeSet codeset;
|
||||
Kernel::PhysicalMemory program_image;
|
||||
|
||||
const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment,
|
||||
const std::vector<u8>& data, u32 offset) {
|
||||
segment.addr = offset;
|
||||
segment.offset = offset;
|
||||
segment.size = PageAlignSize(static_cast<u32>(data.size()));
|
||||
program_image.resize(offset + data.size());
|
||||
std::memcpy(program_image.data() + offset, data.data(), data.size());
|
||||
};
|
||||
|
||||
load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset());
|
||||
load_segment(codeset.RODataSegment(), kip->GetRODataSection(), kip->GetRODataOffset());
|
||||
load_segment(codeset.DataSegment(), kip->GetDataSection(), kip->GetDataOffset());
|
||||
|
||||
program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
|
||||
codeset.DataSegment().size += kip->GetBSSSize();
|
||||
|
||||
// Setup the process code layout
|
||||
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size())
|
||||
.IsError()) {
|
||||
return {ResultStatus::ErrorNotInitialized, {}};
|
||||
}
|
||||
|
||||
codeset.memory = std::move(program_image);
|
||||
const VAddr base_address = process.PageTable().GetCodeRegionStart();
|
||||
process.LoadModule(std::move(codeset), base_address);
|
||||
|
||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
|
||||
|
||||
is_loaded = true;
|
||||
return {ResultStatus::Success,
|
||||
LoadParameters{kip->GetMainThreadPriority(), kip->GetMainThreadStackSize()}};
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,40 +1,40 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
class KIP;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
class AppLoader_KIP final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_KIP(FileSys::VirtualFile file);
|
||||
~AppLoader_KIP() override;
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is a KIP.
|
||||
*
|
||||
* @param in_file The file to identify.
|
||||
*
|
||||
* @return FileType::KIP if found, or FileType::Error if unknown.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& in_file);
|
||||
|
||||
FileType GetFileType() const override;
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::KIP> kip;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
class KIP;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
class AppLoader_KIP final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_KIP(FileSys::VirtualFile file);
|
||||
~AppLoader_KIP() override;
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is a KIP.
|
||||
*
|
||||
* @param in_file The file to identify.
|
||||
*
|
||||
* @return FileType::KIP if found, or FileType::Error if unknown.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& in_file);
|
||||
|
||||
FileType GetFileType() const override;
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::KIP> kip;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,268 +1,268 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include "common/concepts.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/loader/deconstructed_rom_directory.h"
|
||||
#include "core/loader/kip.h"
|
||||
#include "core/loader/nax.h"
|
||||
#include "core/loader/nca.h"
|
||||
#include "core/loader/nro.h"
|
||||
#include "core/loader/nso.h"
|
||||
#include "core/loader/nsp.h"
|
||||
#include "core/loader/xci.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
namespace {
|
||||
|
||||
template <Common::DerivedFrom<AppLoader> T>
|
||||
std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
|
||||
const auto file_type = T::IdentifyType(file);
|
||||
if (file_type != FileType::Error) {
|
||||
return file_type;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FileType IdentifyFile(FileSys::VirtualFile file) {
|
||||
if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) {
|
||||
return *romdir_type;
|
||||
} else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) {
|
||||
return *nso_type;
|
||||
} else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) {
|
||||
return *nro_type;
|
||||
} else if (const auto nca_type = IdentifyFileLoader<AppLoader_NCA>(file)) {
|
||||
return *nca_type;
|
||||
} else if (const auto xci_type = IdentifyFileLoader<AppLoader_XCI>(file)) {
|
||||
return *xci_type;
|
||||
} else if (const auto nax_type = IdentifyFileLoader<AppLoader_NAX>(file)) {
|
||||
return *nax_type;
|
||||
} else if (const auto nsp_type = IdentifyFileLoader<AppLoader_NSP>(file)) {
|
||||
return *nsp_type;
|
||||
} else if (const auto kip_type = IdentifyFileLoader<AppLoader_KIP>(file)) {
|
||||
return *kip_type;
|
||||
} else {
|
||||
return FileType::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
FileType GuessFromFilename(const std::string& name) {
|
||||
if (name == "main")
|
||||
return FileType::DeconstructedRomDirectory;
|
||||
if (name == "00")
|
||||
return FileType::NCA;
|
||||
|
||||
const std::string extension =
|
||||
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
|
||||
|
||||
if (extension == "nro")
|
||||
return FileType::NRO;
|
||||
if (extension == "nso")
|
||||
return FileType::NSO;
|
||||
if (extension == "nca")
|
||||
return FileType::NCA;
|
||||
if (extension == "xci")
|
||||
return FileType::XCI;
|
||||
if (extension == "nsp")
|
||||
return FileType::NSP;
|
||||
if (extension == "kip")
|
||||
return FileType::KIP;
|
||||
|
||||
return FileType::Unknown;
|
||||
}
|
||||
|
||||
std::string GetFileTypeString(FileType type) {
|
||||
switch (type) {
|
||||
case FileType::NRO:
|
||||
return "NRO";
|
||||
case FileType::NSO:
|
||||
return "NSO";
|
||||
case FileType::NCA:
|
||||
return "NCA";
|
||||
case FileType::XCI:
|
||||
return "XCI";
|
||||
case FileType::NAX:
|
||||
return "NAX";
|
||||
case FileType::NSP:
|
||||
return "NSP";
|
||||
case FileType::KIP:
|
||||
return "KIP";
|
||||
case FileType::DeconstructedRomDirectory:
|
||||
return "Directory";
|
||||
case FileType::Error:
|
||||
case FileType::Unknown:
|
||||
break;
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
constexpr std::array<const char*, 66> RESULT_MESSAGES{
|
||||
"The operation completed successfully.",
|
||||
"The loader requested to load is already loaded.",
|
||||
"The operation is not implemented.",
|
||||
"The loader is not initialized properly.",
|
||||
"The NPDM file has a bad header.",
|
||||
"The NPDM has a bad ACID header.",
|
||||
"The NPDM has a bad ACI header,",
|
||||
"The NPDM file has a bad file access control.",
|
||||
"The NPDM has a bad file access header.",
|
||||
"The NPDM has bad kernel capability descriptors.",
|
||||
"The PFS/HFS partition has a bad header.",
|
||||
"The PFS/HFS partition has incorrect size as determined by the header.",
|
||||
"The NCA file has a bad header.",
|
||||
"The general keyfile could not be found.",
|
||||
"The NCA Header key could not be found.",
|
||||
"The NCA Header key is incorrect or the header is invalid.",
|
||||
"Support for NCA2-type NCAs is not implemented.",
|
||||
"Support for NCA0-type NCAs is not implemented.",
|
||||
"The titlekey for this Rights ID could not be found.",
|
||||
"The titlekek for this crypto revision could not be found.",
|
||||
"The Rights ID in the header is invalid.",
|
||||
"The key area key for this application type and crypto revision could not be found.",
|
||||
"The key area key is incorrect or the section header is invalid.",
|
||||
"The titlekey and/or titlekek is incorrect or the section header is invalid.",
|
||||
"The XCI file is missing a Program-type NCA.",
|
||||
"The NCA file is not an application.",
|
||||
"The ExeFS partition could not be found.",
|
||||
"The XCI file has a bad header.",
|
||||
"The XCI file is missing a partition.",
|
||||
"The file could not be found or does not exist.",
|
||||
"The game is missing a program metadata file (main.npdm).",
|
||||
"The game uses the currently-unimplemented 32-bit architecture.",
|
||||
"Unable to completely parse the kernel metadata when loading the emulated process",
|
||||
"The RomFS could not be found.",
|
||||
"The ELF file has incorrect size as determined by the header.",
|
||||
"There was a general error loading the NRO into emulated memory.",
|
||||
"There was a general error loading the NSO into emulated memory.",
|
||||
"There is no icon available.",
|
||||
"There is no control data available.",
|
||||
"The NAX file has a bad header.",
|
||||
"The NAX file has incorrect size as determined by the header.",
|
||||
"The HMAC to generated the NAX decryption keys failed.",
|
||||
"The HMAC to validate the NAX decryption keys failed.",
|
||||
"The NAX key derivation failed.",
|
||||
"The NAX file cannot be interpreted as an NCA file.",
|
||||
"The NAX file has an incorrect path.",
|
||||
"The SD seed could not be found or derived.",
|
||||
"The SD KEK Source could not be found.",
|
||||
"The AES KEK Generation Source could not be found.",
|
||||
"The AES Key Generation Source could not be found.",
|
||||
"The SD Save Key Source could not be found.",
|
||||
"The SD NCA Key Source could not be found.",
|
||||
"The NSP file is missing a Program-type NCA.",
|
||||
"The BKTR-type NCA has a bad BKTR header.",
|
||||
"The BKTR Subsection entry is not located immediately after the Relocation entry.",
|
||||
"The BKTR Subsection entry is not at the end of the media block.",
|
||||
"The BKTR-type NCA has a bad Relocation block.",
|
||||
"The BKTR-type NCA has a bad Subsection block.",
|
||||
"The BKTR-type NCA has a bad Relocation bucket.",
|
||||
"The BKTR-type NCA has a bad Subsection bucket.",
|
||||
"The BKTR-type NCA is missing the base RomFS.",
|
||||
"The NSP or XCI does not contain an update in addition to the base game.",
|
||||
"The KIP file has a bad header.",
|
||||
"The KIP BLZ decompression of the section failed unexpectedly.",
|
||||
"The INI file has a bad header.",
|
||||
"The INI file contains more than the maximum allowable number of KIP files.",
|
||||
};
|
||||
|
||||
std::string GetResultStatusString(ResultStatus status) {
|
||||
return RESULT_MESSAGES.at(static_cast<std::size_t>(status));
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, ResultStatus status) {
|
||||
os << RESULT_MESSAGES.at(static_cast<std::size_t>(status));
|
||||
return os;
|
||||
}
|
||||
|
||||
AppLoader::AppLoader(FileSys::VirtualFile file_) : file(std::move(file_)) {}
|
||||
AppLoader::~AppLoader() = default;
|
||||
|
||||
/**
|
||||
* Get a loader for a file with a specific type
|
||||
* @param system The system context to use.
|
||||
* @param file The file to retrieve the loader for
|
||||
* @param type The file type
|
||||
* @param program_index Specifies the index within the container of the program to launch.
|
||||
* @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
|
||||
*/
|
||||
static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file,
|
||||
FileType type, u64 program_id,
|
||||
std::size_t program_index) {
|
||||
switch (type) {
|
||||
// NX NSO file format.
|
||||
case FileType::NSO:
|
||||
return std::make_unique<AppLoader_NSO>(std::move(file));
|
||||
|
||||
// NX NRO file format.
|
||||
case FileType::NRO:
|
||||
return std::make_unique<AppLoader_NRO>(std::move(file));
|
||||
|
||||
// NX NCA (Nintendo Content Archive) file format.
|
||||
case FileType::NCA:
|
||||
return std::make_unique<AppLoader_NCA>(std::move(file));
|
||||
|
||||
// NX XCI (nX Card Image) file format.
|
||||
case FileType::XCI:
|
||||
return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(),
|
||||
system.GetContentProvider(), program_id,
|
||||
program_index);
|
||||
|
||||
// NX NAX (NintendoAesXts) file format.
|
||||
case FileType::NAX:
|
||||
return std::make_unique<AppLoader_NAX>(std::move(file));
|
||||
|
||||
// NX NSP (Nintendo Submission Package) file format
|
||||
case FileType::NSP:
|
||||
return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(),
|
||||
system.GetContentProvider(), program_id,
|
||||
program_index);
|
||||
|
||||
// NX KIP (Kernel Internal Process) file format
|
||||
case FileType::KIP:
|
||||
return std::make_unique<AppLoader_KIP>(std::move(file));
|
||||
|
||||
// NX deconstructed ROM directory.
|
||||
case FileType::DeconstructedRomDirectory:
|
||||
return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, std::size_t program_index) {
|
||||
if (!file) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileType type = IdentifyFile(file);
|
||||
const FileType filename_type = GuessFromFilename(file->GetName());
|
||||
|
||||
// Special case: 00 is either a NCA or NAX.
|
||||
if (type != filename_type && !(file->GetName() == "00" && type == FileType::NAX)) {
|
||||
LOG_WARNING(Loader, "File {} has a different type ({}) than its extension.",
|
||||
file->GetName(), GetFileTypeString(type));
|
||||
if (FileType::Unknown == type) {
|
||||
type = filename_type;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
|
||||
|
||||
return GetFileLoader(system, std::move(file), type, program_id, program_index);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include "common/concepts.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/loader/deconstructed_rom_directory.h"
|
||||
#include "core/loader/kip.h"
|
||||
#include "core/loader/nax.h"
|
||||
#include "core/loader/nca.h"
|
||||
#include "core/loader/nro.h"
|
||||
#include "core/loader/nso.h"
|
||||
#include "core/loader/nsp.h"
|
||||
#include "core/loader/xci.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
namespace {
|
||||
|
||||
template <Common::DerivedFrom<AppLoader> T>
|
||||
std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
|
||||
const auto file_type = T::IdentifyType(file);
|
||||
if (file_type != FileType::Error) {
|
||||
return file_type;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FileType IdentifyFile(FileSys::VirtualFile file) {
|
||||
if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) {
|
||||
return *romdir_type;
|
||||
} else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) {
|
||||
return *nso_type;
|
||||
} else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) {
|
||||
return *nro_type;
|
||||
} else if (const auto nca_type = IdentifyFileLoader<AppLoader_NCA>(file)) {
|
||||
return *nca_type;
|
||||
} else if (const auto xci_type = IdentifyFileLoader<AppLoader_XCI>(file)) {
|
||||
return *xci_type;
|
||||
} else if (const auto nax_type = IdentifyFileLoader<AppLoader_NAX>(file)) {
|
||||
return *nax_type;
|
||||
} else if (const auto nsp_type = IdentifyFileLoader<AppLoader_NSP>(file)) {
|
||||
return *nsp_type;
|
||||
} else if (const auto kip_type = IdentifyFileLoader<AppLoader_KIP>(file)) {
|
||||
return *kip_type;
|
||||
} else {
|
||||
return FileType::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
FileType GuessFromFilename(const std::string& name) {
|
||||
if (name == "main")
|
||||
return FileType::DeconstructedRomDirectory;
|
||||
if (name == "00")
|
||||
return FileType::NCA;
|
||||
|
||||
const std::string extension =
|
||||
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
|
||||
|
||||
if (extension == "nro")
|
||||
return FileType::NRO;
|
||||
if (extension == "nso")
|
||||
return FileType::NSO;
|
||||
if (extension == "nca")
|
||||
return FileType::NCA;
|
||||
if (extension == "xci")
|
||||
return FileType::XCI;
|
||||
if (extension == "nsp")
|
||||
return FileType::NSP;
|
||||
if (extension == "kip")
|
||||
return FileType::KIP;
|
||||
|
||||
return FileType::Unknown;
|
||||
}
|
||||
|
||||
std::string GetFileTypeString(FileType type) {
|
||||
switch (type) {
|
||||
case FileType::NRO:
|
||||
return "NRO";
|
||||
case FileType::NSO:
|
||||
return "NSO";
|
||||
case FileType::NCA:
|
||||
return "NCA";
|
||||
case FileType::XCI:
|
||||
return "XCI";
|
||||
case FileType::NAX:
|
||||
return "NAX";
|
||||
case FileType::NSP:
|
||||
return "NSP";
|
||||
case FileType::KIP:
|
||||
return "KIP";
|
||||
case FileType::DeconstructedRomDirectory:
|
||||
return "Directory";
|
||||
case FileType::Error:
|
||||
case FileType::Unknown:
|
||||
break;
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
constexpr std::array<const char*, 66> RESULT_MESSAGES{
|
||||
"The operation completed successfully.",
|
||||
"The loader requested to load is already loaded.",
|
||||
"The operation is not implemented.",
|
||||
"The loader is not initialized properly.",
|
||||
"The NPDM file has a bad header.",
|
||||
"The NPDM has a bad ACID header.",
|
||||
"The NPDM has a bad ACI header,",
|
||||
"The NPDM file has a bad file access control.",
|
||||
"The NPDM has a bad file access header.",
|
||||
"The NPDM has bad kernel capability descriptors.",
|
||||
"The PFS/HFS partition has a bad header.",
|
||||
"The PFS/HFS partition has incorrect size as determined by the header.",
|
||||
"The NCA file has a bad header.",
|
||||
"The general keyfile could not be found.",
|
||||
"The NCA Header key could not be found.",
|
||||
"The NCA Header key is incorrect or the header is invalid.",
|
||||
"Support for NCA2-type NCAs is not implemented.",
|
||||
"Support for NCA0-type NCAs is not implemented.",
|
||||
"The titlekey for this Rights ID could not be found.",
|
||||
"The titlekek for this crypto revision could not be found.",
|
||||
"The Rights ID in the header is invalid.",
|
||||
"The key area key for this application type and crypto revision could not be found.",
|
||||
"The key area key is incorrect or the section header is invalid.",
|
||||
"The titlekey and/or titlekek is incorrect or the section header is invalid.",
|
||||
"The XCI file is missing a Program-type NCA.",
|
||||
"The NCA file is not an application.",
|
||||
"The ExeFS partition could not be found.",
|
||||
"The XCI file has a bad header.",
|
||||
"The XCI file is missing a partition.",
|
||||
"The file could not be found or does not exist.",
|
||||
"The game is missing a program metadata file (main.npdm).",
|
||||
"The game uses the currently-unimplemented 32-bit architecture.",
|
||||
"Unable to completely parse the kernel metadata when loading the emulated process",
|
||||
"The RomFS could not be found.",
|
||||
"The ELF file has incorrect size as determined by the header.",
|
||||
"There was a general error loading the NRO into emulated memory.",
|
||||
"There was a general error loading the NSO into emulated memory.",
|
||||
"There is no icon available.",
|
||||
"There is no control data available.",
|
||||
"The NAX file has a bad header.",
|
||||
"The NAX file has incorrect size as determined by the header.",
|
||||
"The HMAC to generated the NAX decryption keys failed.",
|
||||
"The HMAC to validate the NAX decryption keys failed.",
|
||||
"The NAX key derivation failed.",
|
||||
"The NAX file cannot be interpreted as an NCA file.",
|
||||
"The NAX file has an incorrect path.",
|
||||
"The SD seed could not be found or derived.",
|
||||
"The SD KEK Source could not be found.",
|
||||
"The AES KEK Generation Source could not be found.",
|
||||
"The AES Key Generation Source could not be found.",
|
||||
"The SD Save Key Source could not be found.",
|
||||
"The SD NCA Key Source could not be found.",
|
||||
"The NSP file is missing a Program-type NCA.",
|
||||
"The BKTR-type NCA has a bad BKTR header.",
|
||||
"The BKTR Subsection entry is not located immediately after the Relocation entry.",
|
||||
"The BKTR Subsection entry is not at the end of the media block.",
|
||||
"The BKTR-type NCA has a bad Relocation block.",
|
||||
"The BKTR-type NCA has a bad Subsection block.",
|
||||
"The BKTR-type NCA has a bad Relocation bucket.",
|
||||
"The BKTR-type NCA has a bad Subsection bucket.",
|
||||
"The BKTR-type NCA is missing the base RomFS.",
|
||||
"The NSP or XCI does not contain an update in addition to the base game.",
|
||||
"The KIP file has a bad header.",
|
||||
"The KIP BLZ decompression of the section failed unexpectedly.",
|
||||
"The INI file has a bad header.",
|
||||
"The INI file contains more than the maximum allowable number of KIP files.",
|
||||
};
|
||||
|
||||
std::string GetResultStatusString(ResultStatus status) {
|
||||
return RESULT_MESSAGES.at(static_cast<std::size_t>(status));
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, ResultStatus status) {
|
||||
os << RESULT_MESSAGES.at(static_cast<std::size_t>(status));
|
||||
return os;
|
||||
}
|
||||
|
||||
AppLoader::AppLoader(FileSys::VirtualFile file_) : file(std::move(file_)) {}
|
||||
AppLoader::~AppLoader() = default;
|
||||
|
||||
/**
|
||||
* Get a loader for a file with a specific type
|
||||
* @param system The system context to use.
|
||||
* @param file The file to retrieve the loader for
|
||||
* @param type The file type
|
||||
* @param program_index Specifies the index within the container of the program to launch.
|
||||
* @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
|
||||
*/
|
||||
static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file,
|
||||
FileType type, u64 program_id,
|
||||
std::size_t program_index) {
|
||||
switch (type) {
|
||||
// NX NSO file format.
|
||||
case FileType::NSO:
|
||||
return std::make_unique<AppLoader_NSO>(std::move(file));
|
||||
|
||||
// NX NRO file format.
|
||||
case FileType::NRO:
|
||||
return std::make_unique<AppLoader_NRO>(std::move(file));
|
||||
|
||||
// NX NCA (Nintendo Content Archive) file format.
|
||||
case FileType::NCA:
|
||||
return std::make_unique<AppLoader_NCA>(std::move(file));
|
||||
|
||||
// NX XCI (nX Card Image) file format.
|
||||
case FileType::XCI:
|
||||
return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(),
|
||||
system.GetContentProvider(), program_id,
|
||||
program_index);
|
||||
|
||||
// NX NAX (NintendoAesXts) file format.
|
||||
case FileType::NAX:
|
||||
return std::make_unique<AppLoader_NAX>(std::move(file));
|
||||
|
||||
// NX NSP (Nintendo Submission Package) file format
|
||||
case FileType::NSP:
|
||||
return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(),
|
||||
system.GetContentProvider(), program_id,
|
||||
program_index);
|
||||
|
||||
// NX KIP (Kernel Internal Process) file format
|
||||
case FileType::KIP:
|
||||
return std::make_unique<AppLoader_KIP>(std::move(file));
|
||||
|
||||
// NX deconstructed ROM directory.
|
||||
case FileType::DeconstructedRomDirectory:
|
||||
return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, std::size_t program_index) {
|
||||
if (!file) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileType type = IdentifyFile(file);
|
||||
const FileType filename_type = GuessFromFilename(file->GetName());
|
||||
|
||||
// Special case: 00 is either a NCA or NAX.
|
||||
if (type != filename_type && !(file->GetName() == "00" && type == FileType::NAX)) {
|
||||
LOG_WARNING(Loader, "File {} has a different type ({}) than its extension.",
|
||||
file->GetName(), GetFileTypeString(type));
|
||||
if (FileType::Unknown == type) {
|
||||
type = filename_type;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
|
||||
|
||||
return GetFileLoader(system, std::move(file), type, program_id, program_index);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,342 +1,342 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
class NACP;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Kernel {
|
||||
struct AddressMapping;
|
||||
class KProcess;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Loader {
|
||||
|
||||
/// File types supported by CTR
|
||||
enum class FileType {
|
||||
Error,
|
||||
Unknown,
|
||||
NSO,
|
||||
NRO,
|
||||
NCA,
|
||||
NSP,
|
||||
XCI,
|
||||
NAX,
|
||||
KIP,
|
||||
DeconstructedRomDirectory,
|
||||
};
|
||||
|
||||
/**
|
||||
* Identifies the type of a bootable file based on the magic value in its header.
|
||||
* @param file open file
|
||||
* @return FileType of file
|
||||
*/
|
||||
FileType IdentifyFile(FileSys::VirtualFile file);
|
||||
|
||||
/**
|
||||
* Guess the type of a bootable file from its name
|
||||
* @param name String name of bootable file
|
||||
* @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine
|
||||
* a filetype, and will never return FileType::Error.
|
||||
*/
|
||||
FileType GuessFromFilename(const std::string& name);
|
||||
|
||||
/**
|
||||
* Convert a FileType into a string which can be displayed to the user.
|
||||
*/
|
||||
std::string GetFileTypeString(FileType type);
|
||||
|
||||
/// Return type for functions in Loader namespace
|
||||
enum class ResultStatus : u16 {
|
||||
Success,
|
||||
ErrorAlreadyLoaded,
|
||||
ErrorNotImplemented,
|
||||
ErrorNotInitialized,
|
||||
ErrorBadNPDMHeader,
|
||||
ErrorBadACIDHeader,
|
||||
ErrorBadACIHeader,
|
||||
ErrorBadFileAccessControl,
|
||||
ErrorBadFileAccessHeader,
|
||||
ErrorBadKernelCapabilityDescriptors,
|
||||
ErrorBadPFSHeader,
|
||||
ErrorIncorrectPFSFileSize,
|
||||
ErrorBadNCAHeader,
|
||||
ErrorMissingProductionKeyFile,
|
||||
ErrorMissingHeaderKey,
|
||||
ErrorIncorrectHeaderKey,
|
||||
ErrorNCA2,
|
||||
ErrorNCA0,
|
||||
ErrorMissingTitlekey,
|
||||
ErrorMissingTitlekek,
|
||||
ErrorInvalidRightsID,
|
||||
ErrorMissingKeyAreaKey,
|
||||
ErrorIncorrectKeyAreaKey,
|
||||
ErrorIncorrectTitlekeyOrTitlekek,
|
||||
ErrorXCIMissingProgramNCA,
|
||||
ErrorNCANotProgram,
|
||||
ErrorNoExeFS,
|
||||
ErrorBadXCIHeader,
|
||||
ErrorXCIMissingPartition,
|
||||
ErrorNullFile,
|
||||
ErrorMissingNPDM,
|
||||
Error32BitISA,
|
||||
ErrorUnableToParseKernelMetadata,
|
||||
ErrorNoRomFS,
|
||||
ErrorIncorrectELFFileSize,
|
||||
ErrorLoadingNRO,
|
||||
ErrorLoadingNSO,
|
||||
ErrorNoIcon,
|
||||
ErrorNoControl,
|
||||
ErrorBadNAXHeader,
|
||||
ErrorIncorrectNAXFileSize,
|
||||
ErrorNAXKeyHMACFailed,
|
||||
ErrorNAXValidationHMACFailed,
|
||||
ErrorNAXKeyDerivationFailed,
|
||||
ErrorNAXInconvertibleToNCA,
|
||||
ErrorBadNAXFilePath,
|
||||
ErrorMissingSDSeed,
|
||||
ErrorMissingSDKEKSource,
|
||||
ErrorMissingAESKEKGenerationSource,
|
||||
ErrorMissingAESKeyGenerationSource,
|
||||
ErrorMissingSDSaveKeySource,
|
||||
ErrorMissingSDNCAKeySource,
|
||||
ErrorNSPMissingProgramNCA,
|
||||
ErrorBadBKTRHeader,
|
||||
ErrorBKTRSubsectionNotAfterRelocation,
|
||||
ErrorBKTRSubsectionNotAtEnd,
|
||||
ErrorBadRelocationBlock,
|
||||
ErrorBadSubsectionBlock,
|
||||
ErrorBadRelocationBuckets,
|
||||
ErrorBadSubsectionBuckets,
|
||||
ErrorMissingBKTRBaseRomFS,
|
||||
ErrorNoPackedUpdate,
|
||||
ErrorBadKIPHeader,
|
||||
ErrorBLZDecompressionFailed,
|
||||
ErrorBadINIHeader,
|
||||
ErrorINITooManyKIPs,
|
||||
};
|
||||
|
||||
std::string GetResultStatusString(ResultStatus status);
|
||||
std::ostream& operator<<(std::ostream& os, ResultStatus status);
|
||||
|
||||
/// Interface for loading an application
|
||||
class AppLoader {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(AppLoader);
|
||||
YUZU_NON_MOVEABLE(AppLoader);
|
||||
|
||||
struct LoadParameters {
|
||||
s32 main_thread_priority;
|
||||
u64 main_thread_stack_size;
|
||||
};
|
||||
using LoadResult = std::pair<ResultStatus, std::optional<LoadParameters>>;
|
||||
|
||||
explicit AppLoader(FileSys::VirtualFile file_);
|
||||
virtual ~AppLoader();
|
||||
|
||||
/**
|
||||
* Returns the type of this file
|
||||
*
|
||||
* @return FileType corresponding to the loaded file
|
||||
*/
|
||||
virtual FileType GetFileType() const = 0;
|
||||
|
||||
/**
|
||||
* Load the application and return the created Process instance
|
||||
*
|
||||
* @param process The newly created process.
|
||||
* @param system The system that this process is being loaded under.
|
||||
*
|
||||
* @return The status result of the operation.
|
||||
*/
|
||||
virtual LoadResult Load(Kernel::KProcess& process, Core::System& system) = 0;
|
||||
|
||||
/**
|
||||
* Get the code (typically .code section) of the application
|
||||
*
|
||||
* @param[out] buffer Reference to buffer to store data
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadCode(std::vector<u8>& buffer) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon (typically icon section) of the application
|
||||
*
|
||||
* @param[out] buffer Reference to buffer to store data
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadIcon(std::vector<u8>& buffer) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the banner (typically banner section) of the application
|
||||
* In the context of NX, this is the animation that displays in the bottom right of the screen
|
||||
* when a game boots. Stored in GIF format.
|
||||
*
|
||||
* @param[out] buffer Reference to buffer to store data
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadBanner(std::vector<u8>& buffer) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the logo (typically logo section) of the application
|
||||
* In the context of NX, this is the static image that displays in the top left of the screen
|
||||
* when a game boots. Stored in JPEG format.
|
||||
*
|
||||
* @param[out] buffer Reference to buffer to store data
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadLogo(std::vector<u8>& buffer) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the program id of the application
|
||||
*
|
||||
* @param[out] out_program_id Reference to store program id into
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadProgramId(u64& out_program_id) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the program ids of the application
|
||||
*
|
||||
* @param[out] out_program_ids Reference to store program ids into
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the RomFS of the application
|
||||
* Since the RomFS can be huge, we return a file reference instead of copying to a buffer
|
||||
*
|
||||
* @param[out] out_file The directory containing the RomFS
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw update of the application, should it come packed with one
|
||||
*
|
||||
* @param[out] out_file The raw update NCA file (Program-type)
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether or not updates can be applied to the RomFS.
|
||||
* By default, this is true, however for formats where it cannot be guaranteed that the RomFS is
|
||||
* the base game it should be set to false.
|
||||
*
|
||||
* @return bool indicating whether or not the RomFS is updatable.
|
||||
*/
|
||||
virtual bool IsRomFSUpdatable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the difference between the start of the IVFC header and the start of level 6 (RomFS)
|
||||
* data. Needed for BKTR patching.
|
||||
*
|
||||
* @return IVFC offset for RomFS.
|
||||
*/
|
||||
virtual u64 ReadRomFSIVFCOffset() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title of the application
|
||||
*
|
||||
* @param[out] title Reference to store the application title into
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadTitle(std::string& title) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the control data (CNMT) of the application
|
||||
*
|
||||
* @param[out] control Reference to store the application control data into
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadControlData(FileSys::NACP& control) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the RomFS of the manual of the application
|
||||
*
|
||||
* @param[out] out_file The raw manual RomFS of the game
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadManualRomFS(FileSys::VirtualFile& out_file) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
using Modules = std::map<VAddr, std::string>;
|
||||
|
||||
virtual ResultStatus ReadNSOModules(Modules& modules) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
protected:
|
||||
FileSys::VirtualFile file;
|
||||
bool is_loaded = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Identifies a bootable file and return a suitable loader
|
||||
*
|
||||
* @param system The system context.
|
||||
* @param file The bootable file.
|
||||
* @param program_index Specifies the index within the container of the program to launch.
|
||||
*
|
||||
* @return the best loader for this file.
|
||||
*/
|
||||
std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id = 0, std::size_t program_index = 0);
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
class NACP;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Kernel {
|
||||
struct AddressMapping;
|
||||
class KProcess;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Loader {
|
||||
|
||||
/// File types supported by CTR
|
||||
enum class FileType {
|
||||
Error,
|
||||
Unknown,
|
||||
NSO,
|
||||
NRO,
|
||||
NCA,
|
||||
NSP,
|
||||
XCI,
|
||||
NAX,
|
||||
KIP,
|
||||
DeconstructedRomDirectory,
|
||||
};
|
||||
|
||||
/**
|
||||
* Identifies the type of a bootable file based on the magic value in its header.
|
||||
* @param file open file
|
||||
* @return FileType of file
|
||||
*/
|
||||
FileType IdentifyFile(FileSys::VirtualFile file);
|
||||
|
||||
/**
|
||||
* Guess the type of a bootable file from its name
|
||||
* @param name String name of bootable file
|
||||
* @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine
|
||||
* a filetype, and will never return FileType::Error.
|
||||
*/
|
||||
FileType GuessFromFilename(const std::string& name);
|
||||
|
||||
/**
|
||||
* Convert a FileType into a string which can be displayed to the user.
|
||||
*/
|
||||
std::string GetFileTypeString(FileType type);
|
||||
|
||||
/// Return type for functions in Loader namespace
|
||||
enum class ResultStatus : u16 {
|
||||
Success,
|
||||
ErrorAlreadyLoaded,
|
||||
ErrorNotImplemented,
|
||||
ErrorNotInitialized,
|
||||
ErrorBadNPDMHeader,
|
||||
ErrorBadACIDHeader,
|
||||
ErrorBadACIHeader,
|
||||
ErrorBadFileAccessControl,
|
||||
ErrorBadFileAccessHeader,
|
||||
ErrorBadKernelCapabilityDescriptors,
|
||||
ErrorBadPFSHeader,
|
||||
ErrorIncorrectPFSFileSize,
|
||||
ErrorBadNCAHeader,
|
||||
ErrorMissingProductionKeyFile,
|
||||
ErrorMissingHeaderKey,
|
||||
ErrorIncorrectHeaderKey,
|
||||
ErrorNCA2,
|
||||
ErrorNCA0,
|
||||
ErrorMissingTitlekey,
|
||||
ErrorMissingTitlekek,
|
||||
ErrorInvalidRightsID,
|
||||
ErrorMissingKeyAreaKey,
|
||||
ErrorIncorrectKeyAreaKey,
|
||||
ErrorIncorrectTitlekeyOrTitlekek,
|
||||
ErrorXCIMissingProgramNCA,
|
||||
ErrorNCANotProgram,
|
||||
ErrorNoExeFS,
|
||||
ErrorBadXCIHeader,
|
||||
ErrorXCIMissingPartition,
|
||||
ErrorNullFile,
|
||||
ErrorMissingNPDM,
|
||||
Error32BitISA,
|
||||
ErrorUnableToParseKernelMetadata,
|
||||
ErrorNoRomFS,
|
||||
ErrorIncorrectELFFileSize,
|
||||
ErrorLoadingNRO,
|
||||
ErrorLoadingNSO,
|
||||
ErrorNoIcon,
|
||||
ErrorNoControl,
|
||||
ErrorBadNAXHeader,
|
||||
ErrorIncorrectNAXFileSize,
|
||||
ErrorNAXKeyHMACFailed,
|
||||
ErrorNAXValidationHMACFailed,
|
||||
ErrorNAXKeyDerivationFailed,
|
||||
ErrorNAXInconvertibleToNCA,
|
||||
ErrorBadNAXFilePath,
|
||||
ErrorMissingSDSeed,
|
||||
ErrorMissingSDKEKSource,
|
||||
ErrorMissingAESKEKGenerationSource,
|
||||
ErrorMissingAESKeyGenerationSource,
|
||||
ErrorMissingSDSaveKeySource,
|
||||
ErrorMissingSDNCAKeySource,
|
||||
ErrorNSPMissingProgramNCA,
|
||||
ErrorBadBKTRHeader,
|
||||
ErrorBKTRSubsectionNotAfterRelocation,
|
||||
ErrorBKTRSubsectionNotAtEnd,
|
||||
ErrorBadRelocationBlock,
|
||||
ErrorBadSubsectionBlock,
|
||||
ErrorBadRelocationBuckets,
|
||||
ErrorBadSubsectionBuckets,
|
||||
ErrorMissingBKTRBaseRomFS,
|
||||
ErrorNoPackedUpdate,
|
||||
ErrorBadKIPHeader,
|
||||
ErrorBLZDecompressionFailed,
|
||||
ErrorBadINIHeader,
|
||||
ErrorINITooManyKIPs,
|
||||
};
|
||||
|
||||
std::string GetResultStatusString(ResultStatus status);
|
||||
std::ostream& operator<<(std::ostream& os, ResultStatus status);
|
||||
|
||||
/// Interface for loading an application
|
||||
class AppLoader {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(AppLoader);
|
||||
YUZU_NON_MOVEABLE(AppLoader);
|
||||
|
||||
struct LoadParameters {
|
||||
s32 main_thread_priority;
|
||||
u64 main_thread_stack_size;
|
||||
};
|
||||
using LoadResult = std::pair<ResultStatus, std::optional<LoadParameters>>;
|
||||
|
||||
explicit AppLoader(FileSys::VirtualFile file_);
|
||||
virtual ~AppLoader();
|
||||
|
||||
/**
|
||||
* Returns the type of this file
|
||||
*
|
||||
* @return FileType corresponding to the loaded file
|
||||
*/
|
||||
virtual FileType GetFileType() const = 0;
|
||||
|
||||
/**
|
||||
* Load the application and return the created Process instance
|
||||
*
|
||||
* @param process The newly created process.
|
||||
* @param system The system that this process is being loaded under.
|
||||
*
|
||||
* @return The status result of the operation.
|
||||
*/
|
||||
virtual LoadResult Load(Kernel::KProcess& process, Core::System& system) = 0;
|
||||
|
||||
/**
|
||||
* Get the code (typically .code section) of the application
|
||||
*
|
||||
* @param[out] buffer Reference to buffer to store data
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadCode(std::vector<u8>& buffer) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon (typically icon section) of the application
|
||||
*
|
||||
* @param[out] buffer Reference to buffer to store data
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadIcon(std::vector<u8>& buffer) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the banner (typically banner section) of the application
|
||||
* In the context of NX, this is the animation that displays in the bottom right of the screen
|
||||
* when a game boots. Stored in GIF format.
|
||||
*
|
||||
* @param[out] buffer Reference to buffer to store data
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadBanner(std::vector<u8>& buffer) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the logo (typically logo section) of the application
|
||||
* In the context of NX, this is the static image that displays in the top left of the screen
|
||||
* when a game boots. Stored in JPEG format.
|
||||
*
|
||||
* @param[out] buffer Reference to buffer to store data
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadLogo(std::vector<u8>& buffer) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the program id of the application
|
||||
*
|
||||
* @param[out] out_program_id Reference to store program id into
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadProgramId(u64& out_program_id) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the program ids of the application
|
||||
*
|
||||
* @param[out] out_program_ids Reference to store program ids into
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the RomFS of the application
|
||||
* Since the RomFS can be huge, we return a file reference instead of copying to a buffer
|
||||
*
|
||||
* @param[out] out_file The directory containing the RomFS
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw update of the application, should it come packed with one
|
||||
*
|
||||
* @param[out] out_file The raw update NCA file (Program-type)
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether or not updates can be applied to the RomFS.
|
||||
* By default, this is true, however for formats where it cannot be guaranteed that the RomFS is
|
||||
* the base game it should be set to false.
|
||||
*
|
||||
* @return bool indicating whether or not the RomFS is updatable.
|
||||
*/
|
||||
virtual bool IsRomFSUpdatable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the difference between the start of the IVFC header and the start of level 6 (RomFS)
|
||||
* data. Needed for BKTR patching.
|
||||
*
|
||||
* @return IVFC offset for RomFS.
|
||||
*/
|
||||
virtual u64 ReadRomFSIVFCOffset() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title of the application
|
||||
*
|
||||
* @param[out] title Reference to store the application title into
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadTitle(std::string& title) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the control data (CNMT) of the application
|
||||
*
|
||||
* @param[out] control Reference to store the application control data into
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadControlData(FileSys::NACP& control) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the RomFS of the manual of the application
|
||||
*
|
||||
* @param[out] out_file The raw manual RomFS of the game
|
||||
*
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadManualRomFS(FileSys::VirtualFile& out_file) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
using Modules = std::map<VAddr, std::string>;
|
||||
|
||||
virtual ResultStatus ReadNSOModules(Modules& modules) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
protected:
|
||||
FileSys::VirtualFile file;
|
||||
bool is_loaded = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Identifies a bootable file and return a suitable loader
|
||||
*
|
||||
* @param system The system context.
|
||||
* @param file The bootable file.
|
||||
* @param program_index Specifies the index within the container of the program to launch.
|
||||
*
|
||||
* @return the best loader for this file.
|
||||
*/
|
||||
std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id = 0, std::size_t program_index = 0);
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,99 +1,99 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/xts_archive.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/loader/nax.h"
|
||||
#include "core/loader/nca.h"
|
||||
|
||||
namespace Loader {
|
||||
namespace {
|
||||
FileType IdentifyTypeImpl(const FileSys::NAX& nax) {
|
||||
if (nax.GetStatus() != ResultStatus::Success) {
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
const auto nca = nax.AsNCA();
|
||||
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) {
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
return FileType::NAX;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file_)
|
||||
: AppLoader(file_), nax(std::make_unique<FileSys::NAX>(file_)),
|
||||
nca_loader(std::make_unique<AppLoader_NCA>(nax->GetDecrypted())) {}
|
||||
|
||||
AppLoader_NAX::~AppLoader_NAX() = default;
|
||||
|
||||
FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& nax_file) {
|
||||
const FileSys::NAX nax(nax_file);
|
||||
return IdentifyTypeImpl(nax);
|
||||
}
|
||||
|
||||
FileType AppLoader_NAX::GetFileType() const {
|
||||
return IdentifyTypeImpl(*nax);
|
||||
}
|
||||
|
||||
AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
const auto nax_status = nax->GetStatus();
|
||||
if (nax_status != ResultStatus::Success) {
|
||||
return {nax_status, {}};
|
||||
}
|
||||
|
||||
const auto nca = nax->AsNCA();
|
||||
if (nca == nullptr) {
|
||||
if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
|
||||
return {ResultStatus::ErrorMissingProductionKeyFile, {}};
|
||||
}
|
||||
|
||||
return {ResultStatus::ErrorNAXInconvertibleToNCA, {}};
|
||||
}
|
||||
|
||||
const auto nca_status = nca->GetStatus();
|
||||
if (nca_status != ResultStatus::Success) {
|
||||
return {nca_status, {}};
|
||||
}
|
||||
|
||||
const auto result = nca_loader->Load(process, system);
|
||||
if (result.first != ResultStatus::Success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
return nca_loader->ReadRomFS(dir);
|
||||
}
|
||||
|
||||
u64 AppLoader_NAX::ReadRomFSIVFCOffset() const {
|
||||
return nca_loader->ReadRomFSIVFCOffset();
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NAX::ReadProgramId(u64& out_program_id) {
|
||||
return nca_loader->ReadProgramId(out_program_id);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NAX::ReadBanner(std::vector<u8>& buffer) {
|
||||
return nca_loader->ReadBanner(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NAX::ReadLogo(std::vector<u8>& buffer) {
|
||||
return nca_loader->ReadLogo(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NAX::ReadNSOModules(Modules& modules) {
|
||||
return nca_loader->ReadNSOModules(modules);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/xts_archive.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/loader/nax.h"
|
||||
#include "core/loader/nca.h"
|
||||
|
||||
namespace Loader {
|
||||
namespace {
|
||||
FileType IdentifyTypeImpl(const FileSys::NAX& nax) {
|
||||
if (nax.GetStatus() != ResultStatus::Success) {
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
const auto nca = nax.AsNCA();
|
||||
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) {
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
return FileType::NAX;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file_)
|
||||
: AppLoader(file_), nax(std::make_unique<FileSys::NAX>(file_)),
|
||||
nca_loader(std::make_unique<AppLoader_NCA>(nax->GetDecrypted())) {}
|
||||
|
||||
AppLoader_NAX::~AppLoader_NAX() = default;
|
||||
|
||||
FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& nax_file) {
|
||||
const FileSys::NAX nax(nax_file);
|
||||
return IdentifyTypeImpl(nax);
|
||||
}
|
||||
|
||||
FileType AppLoader_NAX::GetFileType() const {
|
||||
return IdentifyTypeImpl(*nax);
|
||||
}
|
||||
|
||||
AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
const auto nax_status = nax->GetStatus();
|
||||
if (nax_status != ResultStatus::Success) {
|
||||
return {nax_status, {}};
|
||||
}
|
||||
|
||||
const auto nca = nax->AsNCA();
|
||||
if (nca == nullptr) {
|
||||
if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
|
||||
return {ResultStatus::ErrorMissingProductionKeyFile, {}};
|
||||
}
|
||||
|
||||
return {ResultStatus::ErrorNAXInconvertibleToNCA, {}};
|
||||
}
|
||||
|
||||
const auto nca_status = nca->GetStatus();
|
||||
if (nca_status != ResultStatus::Success) {
|
||||
return {nca_status, {}};
|
||||
}
|
||||
|
||||
const auto result = nca_loader->Load(process, system);
|
||||
if (result.first != ResultStatus::Success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
return nca_loader->ReadRomFS(dir);
|
||||
}
|
||||
|
||||
u64 AppLoader_NAX::ReadRomFSIVFCOffset() const {
|
||||
return nca_loader->ReadRomFSIVFCOffset();
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NAX::ReadProgramId(u64& out_program_id) {
|
||||
return nca_loader->ReadProgramId(out_program_id);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NAX::ReadBanner(std::vector<u8>& buffer) {
|
||||
return nca_loader->ReadBanner(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NAX::ReadLogo(std::vector<u8>& buffer) {
|
||||
return nca_loader->ReadLogo(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NAX::ReadNSOModules(Modules& modules) {
|
||||
return nca_loader->ReadNSOModules(modules);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,55 +1,55 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
class NAX;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Loader {
|
||||
|
||||
class AppLoader_NCA;
|
||||
|
||||
/// Loads a NAX file
|
||||
class AppLoader_NAX final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_NAX(FileSys::VirtualFile file_);
|
||||
~AppLoader_NAX() override;
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is a NAX file.
|
||||
*
|
||||
* @param nax_file The file to identify.
|
||||
*
|
||||
* @return FileType::NAX, or FileType::Error if the file is not a NAX file.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& nax_file);
|
||||
|
||||
FileType GetFileType() const override;
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
|
||||
u64 ReadRomFSIVFCOffset() const override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
|
||||
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
|
||||
|
||||
ResultStatus ReadNSOModules(Modules& modules) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::NAX> nax;
|
||||
std::unique_ptr<AppLoader_NCA> nca_loader;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
class NAX;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Loader {
|
||||
|
||||
class AppLoader_NCA;
|
||||
|
||||
/// Loads a NAX file
|
||||
class AppLoader_NAX final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_NAX(FileSys::VirtualFile file_);
|
||||
~AppLoader_NAX() override;
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is a NAX file.
|
||||
*
|
||||
* @param nax_file The file to identify.
|
||||
*
|
||||
* @return FileType::NAX, or FileType::Error if the file is not a NAX file.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& nax_file);
|
||||
|
||||
FileType GetFileType() const override;
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
|
||||
u64 ReadRomFSIVFCOffset() const override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
|
||||
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
|
||||
|
||||
ResultStatus ReadNSOModules(Modules& modules) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::NAX> nax;
|
||||
std::unique_ptr<AppLoader_NCA> nca_loader;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,133 +1,133 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/deconstructed_rom_directory.h"
|
||||
#include "core/loader/nca.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file_)
|
||||
: AppLoader(std::move(file_)), nca(std::make_unique<FileSys::NCA>(file)) {}
|
||||
|
||||
AppLoader_NCA::~AppLoader_NCA() = default;
|
||||
|
||||
FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& nca_file) {
|
||||
const FileSys::NCA nca(nca_file);
|
||||
|
||||
if (nca.GetStatus() == ResultStatus::Success &&
|
||||
nca.GetType() == FileSys::NCAContentType::Program) {
|
||||
return FileType::NCA;
|
||||
}
|
||||
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
const auto result = nca->GetStatus();
|
||||
if (result != ResultStatus::Success) {
|
||||
return {result, {}};
|
||||
}
|
||||
|
||||
if (nca->GetType() != FileSys::NCAContentType::Program) {
|
||||
return {ResultStatus::ErrorNCANotProgram, {}};
|
||||
}
|
||||
|
||||
const auto exefs = nca->GetExeFS();
|
||||
if (exefs == nullptr) {
|
||||
return {ResultStatus::ErrorNoExeFS, {}};
|
||||
}
|
||||
|
||||
directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
|
||||
|
||||
const auto load_result = directory_loader->Load(process, system);
|
||||
if (load_result.first != ResultStatus::Success) {
|
||||
return load_result;
|
||||
}
|
||||
|
||||
if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
|
||||
system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
|
||||
*this, system.GetContentProvider(), system.GetFileSystemController()));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
return load_result;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
if (nca == nullptr) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
|
||||
if (nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0) {
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
}
|
||||
|
||||
dir = nca->GetRomFS();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
u64 AppLoader_NCA::ReadRomFSIVFCOffset() const {
|
||||
if (nca == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return nca->GetBaseIVFCOffset();
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
|
||||
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
|
||||
out_program_id = nca->GetTitleId();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadBanner(std::vector<u8>& buffer) {
|
||||
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
|
||||
const auto logo = nca->GetLogoPartition();
|
||||
if (logo == nullptr) {
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
}
|
||||
|
||||
buffer = logo->GetFile("StartupMovie.gif")->ReadAllBytes();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadLogo(std::vector<u8>& buffer) {
|
||||
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
|
||||
const auto logo = nca->GetLogoPartition();
|
||||
if (logo == nullptr) {
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
}
|
||||
|
||||
buffer = logo->GetFile("NintendoLogo.png")->ReadAllBytes();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadNSOModules(Modules& modules) {
|
||||
if (directory_loader == nullptr) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
|
||||
return directory_loader->ReadNSOModules(modules);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/deconstructed_rom_directory.h"
|
||||
#include "core/loader/nca.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file_)
|
||||
: AppLoader(std::move(file_)), nca(std::make_unique<FileSys::NCA>(file)) {}
|
||||
|
||||
AppLoader_NCA::~AppLoader_NCA() = default;
|
||||
|
||||
FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& nca_file) {
|
||||
const FileSys::NCA nca(nca_file);
|
||||
|
||||
if (nca.GetStatus() == ResultStatus::Success &&
|
||||
nca.GetType() == FileSys::NCAContentType::Program) {
|
||||
return FileType::NCA;
|
||||
}
|
||||
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
const auto result = nca->GetStatus();
|
||||
if (result != ResultStatus::Success) {
|
||||
return {result, {}};
|
||||
}
|
||||
|
||||
if (nca->GetType() != FileSys::NCAContentType::Program) {
|
||||
return {ResultStatus::ErrorNCANotProgram, {}};
|
||||
}
|
||||
|
||||
const auto exefs = nca->GetExeFS();
|
||||
if (exefs == nullptr) {
|
||||
return {ResultStatus::ErrorNoExeFS, {}};
|
||||
}
|
||||
|
||||
directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
|
||||
|
||||
const auto load_result = directory_loader->Load(process, system);
|
||||
if (load_result.first != ResultStatus::Success) {
|
||||
return load_result;
|
||||
}
|
||||
|
||||
if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
|
||||
system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
|
||||
*this, system.GetContentProvider(), system.GetFileSystemController()));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
return load_result;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
if (nca == nullptr) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
|
||||
if (nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0) {
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
}
|
||||
|
||||
dir = nca->GetRomFS();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
u64 AppLoader_NCA::ReadRomFSIVFCOffset() const {
|
||||
if (nca == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return nca->GetBaseIVFCOffset();
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
|
||||
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
|
||||
out_program_id = nca->GetTitleId();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadBanner(std::vector<u8>& buffer) {
|
||||
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
|
||||
const auto logo = nca->GetLogoPartition();
|
||||
if (logo == nullptr) {
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
}
|
||||
|
||||
buffer = logo->GetFile("StartupMovie.gif")->ReadAllBytes();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadLogo(std::vector<u8>& buffer) {
|
||||
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
|
||||
const auto logo = nca->GetLogoPartition();
|
||||
if (logo == nullptr) {
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
}
|
||||
|
||||
buffer = logo->GetFile("NintendoLogo.png")->ReadAllBytes();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadNSOModules(Modules& modules) {
|
||||
if (directory_loader == nullptr) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
|
||||
return directory_loader->ReadNSOModules(modules);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,56 +1,56 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
class NCA;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
class AppLoader_DeconstructedRomDirectory;
|
||||
|
||||
/// Loads an NCA file
|
||||
class AppLoader_NCA final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_NCA(FileSys::VirtualFile file_);
|
||||
~AppLoader_NCA() override;
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is an NCA file.
|
||||
*
|
||||
* @param nca_file The file to identify.
|
||||
*
|
||||
* @return FileType::NCA, or FileType::Error if the file is not an NCA file.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& nca_file);
|
||||
|
||||
FileType GetFileType() const override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
|
||||
u64 ReadRomFSIVFCOffset() const override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
|
||||
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
|
||||
|
||||
ResultStatus ReadNSOModules(Modules& modules) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::NCA> nca;
|
||||
std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
class NCA;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
class AppLoader_DeconstructedRomDirectory;
|
||||
|
||||
/// Loads an NCA file
|
||||
class AppLoader_NCA final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_NCA(FileSys::VirtualFile file_);
|
||||
~AppLoader_NCA() override;
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is an NCA file.
|
||||
*
|
||||
* @param nca_file The file to identify.
|
||||
*
|
||||
* @return FileType::NCA, or FileType::Error if the file is not an NCA file.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& nca_file);
|
||||
|
||||
FileType GetFileType() const override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
|
||||
u64 ReadRomFSIVFCOffset() const override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
|
||||
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
|
||||
|
||||
ResultStatus ReadNSOModules(Modules& modules) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::NCA> nca;
|
||||
std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,272 +1,272 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/nro.h"
|
||||
#include "core/loader/nso.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
struct NroSegmentHeader {
|
||||
u32_le offset;
|
||||
u32_le size;
|
||||
};
|
||||
static_assert(sizeof(NroSegmentHeader) == 0x8, "NroSegmentHeader has incorrect size.");
|
||||
|
||||
struct NroHeader {
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
u32_le module_header_offset;
|
||||
INSERT_PADDING_BYTES(0x8);
|
||||
u32_le magic;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
u32_le file_size;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
std::array<NroSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
|
||||
u32_le bss_size;
|
||||
INSERT_PADDING_BYTES(0x44);
|
||||
};
|
||||
static_assert(sizeof(NroHeader) == 0x80, "NroHeader has incorrect size.");
|
||||
|
||||
struct ModHeader {
|
||||
u32_le magic;
|
||||
u32_le dynamic_offset;
|
||||
u32_le bss_start_offset;
|
||||
u32_le bss_end_offset;
|
||||
u32_le unwind_start_offset;
|
||||
u32_le unwind_end_offset;
|
||||
u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
|
||||
};
|
||||
static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
|
||||
|
||||
struct AssetSection {
|
||||
u64_le offset;
|
||||
u64_le size;
|
||||
};
|
||||
static_assert(sizeof(AssetSection) == 0x10, "AssetSection has incorrect size.");
|
||||
|
||||
struct AssetHeader {
|
||||
u32_le magic;
|
||||
u32_le format_version;
|
||||
AssetSection icon;
|
||||
AssetSection nacp;
|
||||
AssetSection romfs;
|
||||
};
|
||||
static_assert(sizeof(AssetHeader) == 0x38, "AssetHeader has incorrect size.");
|
||||
|
||||
AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file_) : AppLoader(std::move(file_)) {
|
||||
NroHeader nro_header{};
|
||||
if (file->ReadObject(&nro_header) != sizeof(NroHeader)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (file->GetSize() >= nro_header.file_size + sizeof(AssetHeader)) {
|
||||
const u64 offset = nro_header.file_size;
|
||||
AssetHeader asset_header{};
|
||||
if (file->ReadObject(&asset_header, offset) != sizeof(AssetHeader)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (asset_header.format_version != 0) {
|
||||
LOG_WARNING(Loader,
|
||||
"NRO Asset Header has format {}, currently supported format is 0. If "
|
||||
"strange glitches occur with metadata, check NRO assets.",
|
||||
asset_header.format_version);
|
||||
}
|
||||
|
||||
if (asset_header.magic != Common::MakeMagic('A', 'S', 'E', 'T')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (asset_header.nacp.size > 0) {
|
||||
nacp = std::make_unique<FileSys::NACP>(std::make_shared<FileSys::OffsetVfsFile>(
|
||||
file, asset_header.nacp.size, offset + asset_header.nacp.offset, "Control.nacp"));
|
||||
}
|
||||
|
||||
if (asset_header.romfs.size > 0) {
|
||||
romfs = std::make_shared<FileSys::OffsetVfsFile>(
|
||||
file, asset_header.romfs.size, offset + asset_header.romfs.offset, "game.romfs");
|
||||
}
|
||||
|
||||
if (asset_header.icon.size > 0) {
|
||||
icon_data = file->ReadBytes(asset_header.icon.size, offset + asset_header.icon.offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AppLoader_NRO::~AppLoader_NRO() = default;
|
||||
|
||||
FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) {
|
||||
// Read NSO header
|
||||
NroHeader nro_header{};
|
||||
if (sizeof(NroHeader) != nro_file->ReadObject(&nro_header)) {
|
||||
return FileType::Error;
|
||||
}
|
||||
if (nro_header.magic == Common::MakeMagic('N', 'R', 'O', '0')) {
|
||||
return FileType::NRO;
|
||||
}
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
static constexpr u32 PageAlignSize(u32 size) {
|
||||
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
|
||||
}
|
||||
|
||||
static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) {
|
||||
if (data.size() < sizeof(NroHeader)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Read NSO header
|
||||
NroHeader nro_header{};
|
||||
std::memcpy(&nro_header, data.data(), sizeof(NroHeader));
|
||||
if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Build program image
|
||||
Kernel::PhysicalMemory program_image(PageAlignSize(nro_header.file_size));
|
||||
std::memcpy(program_image.data(), data.data(), program_image.size());
|
||||
if (program_image.size() != PageAlignSize(nro_header.file_size)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Kernel::CodeSet codeset;
|
||||
for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
|
||||
codeset.segments[i].addr = nro_header.segments[i].offset;
|
||||
codeset.segments[i].offset = nro_header.segments[i].offset;
|
||||
codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
|
||||
}
|
||||
|
||||
if (!Settings::values.program_args.GetValue().empty()) {
|
||||
const auto arg_data = Settings::values.program_args.GetValue();
|
||||
codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
|
||||
NSOArgumentHeader args_header{
|
||||
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
|
||||
const auto end_offset = program_image.size();
|
||||
program_image.resize(static_cast<u32>(program_image.size()) +
|
||||
NSO_ARGUMENT_DATA_ALLOCATION_SIZE);
|
||||
std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader));
|
||||
std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(),
|
||||
arg_data.size());
|
||||
}
|
||||
|
||||
// Default .bss to NRO header bss size if MOD0 section doesn't exist
|
||||
u32 bss_size{PageAlignSize(nro_header.bss_size)};
|
||||
|
||||
// Read MOD header
|
||||
ModHeader mod_header{};
|
||||
std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset,
|
||||
sizeof(ModHeader));
|
||||
|
||||
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
|
||||
if (has_mod_header) {
|
||||
// Resize program image to include .bss section and page align each section
|
||||
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
|
||||
}
|
||||
|
||||
codeset.DataSegment().size += bss_size;
|
||||
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
|
||||
|
||||
// Setup the process code layout
|
||||
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size())
|
||||
.IsError()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load codeset for current process
|
||||
codeset.memory = std::move(program_image);
|
||||
process.LoadModule(std::move(codeset), process.PageTable().GetCodeRegionStart());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AppLoader_NRO::LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file) {
|
||||
return LoadNroImpl(process, nro_file.ReadAllBytes());
|
||||
}
|
||||
|
||||
AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
if (!LoadNro(process, *file)) {
|
||||
return {ResultStatus::ErrorLoadingNRO, {}};
|
||||
}
|
||||
|
||||
if (romfs != nullptr) {
|
||||
system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
|
||||
*this, system.GetContentProvider(), system.GetFileSystemController()));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority,
|
||||
Core::Memory::DEFAULT_STACK_SIZE}};
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_data.empty()) {
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
}
|
||||
|
||||
buffer = icon_data;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) {
|
||||
if (nacp == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
out_program_id = nacp->GetTitleId();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
if (romfs == nullptr) {
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
}
|
||||
|
||||
dir = romfs;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadTitle(std::string& title) {
|
||||
if (nacp == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
title = nacp->GetApplicationName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadControlData(FileSys::NACP& control) {
|
||||
if (nacp == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
control = *nacp;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
bool AppLoader_NRO::IsRomFSUpdatable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/nro.h"
|
||||
#include "core/loader/nso.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
struct NroSegmentHeader {
|
||||
u32_le offset;
|
||||
u32_le size;
|
||||
};
|
||||
static_assert(sizeof(NroSegmentHeader) == 0x8, "NroSegmentHeader has incorrect size.");
|
||||
|
||||
struct NroHeader {
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
u32_le module_header_offset;
|
||||
INSERT_PADDING_BYTES(0x8);
|
||||
u32_le magic;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
u32_le file_size;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
std::array<NroSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
|
||||
u32_le bss_size;
|
||||
INSERT_PADDING_BYTES(0x44);
|
||||
};
|
||||
static_assert(sizeof(NroHeader) == 0x80, "NroHeader has incorrect size.");
|
||||
|
||||
struct ModHeader {
|
||||
u32_le magic;
|
||||
u32_le dynamic_offset;
|
||||
u32_le bss_start_offset;
|
||||
u32_le bss_end_offset;
|
||||
u32_le unwind_start_offset;
|
||||
u32_le unwind_end_offset;
|
||||
u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
|
||||
};
|
||||
static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
|
||||
|
||||
struct AssetSection {
|
||||
u64_le offset;
|
||||
u64_le size;
|
||||
};
|
||||
static_assert(sizeof(AssetSection) == 0x10, "AssetSection has incorrect size.");
|
||||
|
||||
struct AssetHeader {
|
||||
u32_le magic;
|
||||
u32_le format_version;
|
||||
AssetSection icon;
|
||||
AssetSection nacp;
|
||||
AssetSection romfs;
|
||||
};
|
||||
static_assert(sizeof(AssetHeader) == 0x38, "AssetHeader has incorrect size.");
|
||||
|
||||
AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file_) : AppLoader(std::move(file_)) {
|
||||
NroHeader nro_header{};
|
||||
if (file->ReadObject(&nro_header) != sizeof(NroHeader)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (file->GetSize() >= nro_header.file_size + sizeof(AssetHeader)) {
|
||||
const u64 offset = nro_header.file_size;
|
||||
AssetHeader asset_header{};
|
||||
if (file->ReadObject(&asset_header, offset) != sizeof(AssetHeader)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (asset_header.format_version != 0) {
|
||||
LOG_WARNING(Loader,
|
||||
"NRO Asset Header has format {}, currently supported format is 0. If "
|
||||
"strange glitches occur with metadata, check NRO assets.",
|
||||
asset_header.format_version);
|
||||
}
|
||||
|
||||
if (asset_header.magic != Common::MakeMagic('A', 'S', 'E', 'T')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (asset_header.nacp.size > 0) {
|
||||
nacp = std::make_unique<FileSys::NACP>(std::make_shared<FileSys::OffsetVfsFile>(
|
||||
file, asset_header.nacp.size, offset + asset_header.nacp.offset, "Control.nacp"));
|
||||
}
|
||||
|
||||
if (asset_header.romfs.size > 0) {
|
||||
romfs = std::make_shared<FileSys::OffsetVfsFile>(
|
||||
file, asset_header.romfs.size, offset + asset_header.romfs.offset, "game.romfs");
|
||||
}
|
||||
|
||||
if (asset_header.icon.size > 0) {
|
||||
icon_data = file->ReadBytes(asset_header.icon.size, offset + asset_header.icon.offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AppLoader_NRO::~AppLoader_NRO() = default;
|
||||
|
||||
FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) {
|
||||
// Read NSO header
|
||||
NroHeader nro_header{};
|
||||
if (sizeof(NroHeader) != nro_file->ReadObject(&nro_header)) {
|
||||
return FileType::Error;
|
||||
}
|
||||
if (nro_header.magic == Common::MakeMagic('N', 'R', 'O', '0')) {
|
||||
return FileType::NRO;
|
||||
}
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
static constexpr u32 PageAlignSize(u32 size) {
|
||||
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
|
||||
}
|
||||
|
||||
static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) {
|
||||
if (data.size() < sizeof(NroHeader)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Read NSO header
|
||||
NroHeader nro_header{};
|
||||
std::memcpy(&nro_header, data.data(), sizeof(NroHeader));
|
||||
if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Build program image
|
||||
Kernel::PhysicalMemory program_image(PageAlignSize(nro_header.file_size));
|
||||
std::memcpy(program_image.data(), data.data(), program_image.size());
|
||||
if (program_image.size() != PageAlignSize(nro_header.file_size)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Kernel::CodeSet codeset;
|
||||
for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
|
||||
codeset.segments[i].addr = nro_header.segments[i].offset;
|
||||
codeset.segments[i].offset = nro_header.segments[i].offset;
|
||||
codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
|
||||
}
|
||||
|
||||
if (!Settings::values.program_args.GetValue().empty()) {
|
||||
const auto arg_data = Settings::values.program_args.GetValue();
|
||||
codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
|
||||
NSOArgumentHeader args_header{
|
||||
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
|
||||
const auto end_offset = program_image.size();
|
||||
program_image.resize(static_cast<u32>(program_image.size()) +
|
||||
NSO_ARGUMENT_DATA_ALLOCATION_SIZE);
|
||||
std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader));
|
||||
std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(),
|
||||
arg_data.size());
|
||||
}
|
||||
|
||||
// Default .bss to NRO header bss size if MOD0 section doesn't exist
|
||||
u32 bss_size{PageAlignSize(nro_header.bss_size)};
|
||||
|
||||
// Read MOD header
|
||||
ModHeader mod_header{};
|
||||
std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset,
|
||||
sizeof(ModHeader));
|
||||
|
||||
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
|
||||
if (has_mod_header) {
|
||||
// Resize program image to include .bss section and page align each section
|
||||
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
|
||||
}
|
||||
|
||||
codeset.DataSegment().size += bss_size;
|
||||
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
|
||||
|
||||
// Setup the process code layout
|
||||
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size())
|
||||
.IsError()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load codeset for current process
|
||||
codeset.memory = std::move(program_image);
|
||||
process.LoadModule(std::move(codeset), process.PageTable().GetCodeRegionStart());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AppLoader_NRO::LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file) {
|
||||
return LoadNroImpl(process, nro_file.ReadAllBytes());
|
||||
}
|
||||
|
||||
AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
if (!LoadNro(process, *file)) {
|
||||
return {ResultStatus::ErrorLoadingNRO, {}};
|
||||
}
|
||||
|
||||
if (romfs != nullptr) {
|
||||
system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
|
||||
*this, system.GetContentProvider(), system.GetFileSystemController()));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority,
|
||||
Core::Memory::DEFAULT_STACK_SIZE}};
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_data.empty()) {
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
}
|
||||
|
||||
buffer = icon_data;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) {
|
||||
if (nacp == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
out_program_id = nacp->GetTitleId();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
if (romfs == nullptr) {
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
}
|
||||
|
||||
dir = romfs;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadTitle(std::string& title) {
|
||||
if (nacp == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
title = nacp->GetApplicationName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadControlData(FileSys::NACP& control) {
|
||||
if (nacp == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
control = *nacp;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
bool AppLoader_NRO::IsRomFSUpdatable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,62 +1,62 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
class NACP;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
/// Loads an NRO file
|
||||
class AppLoader_NRO final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_NRO(FileSys::VirtualFile file_);
|
||||
~AppLoader_NRO() override;
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is an NRO file.
|
||||
*
|
||||
* @param nro_file The file to identify.
|
||||
*
|
||||
* @return FileType::NRO, or FileType::Error if the file is not an NRO file.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& nro_file);
|
||||
|
||||
FileType GetFileType() const override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
ResultStatus ReadControlData(FileSys::NACP& control) override;
|
||||
bool IsRomFSUpdatable() const override;
|
||||
|
||||
private:
|
||||
bool LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file);
|
||||
|
||||
std::vector<u8> icon_data;
|
||||
std::unique_ptr<FileSys::NACP> nacp;
|
||||
FileSys::VirtualFile romfs;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
class NACP;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
/// Loads an NRO file
|
||||
class AppLoader_NRO final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_NRO(FileSys::VirtualFile file_);
|
||||
~AppLoader_NRO() override;
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is an NRO file.
|
||||
*
|
||||
* @param nro_file The file to identify.
|
||||
*
|
||||
* @return FileType::NRO, or FileType::Error if the file is not an NRO file.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& nro_file);
|
||||
|
||||
FileType GetFileType() const override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
ResultStatus ReadControlData(FileSys::NACP& control) override;
|
||||
bool IsRomFSUpdatable() const override;
|
||||
|
||||
private:
|
||||
bool LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file);
|
||||
|
||||
std::vector<u8> icon_data;
|
||||
std::unique_ptr<FileSys::NACP> nacp;
|
||||
FileSys::VirtualFile romfs;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,188 +1,188 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/lz4_compression.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/loader/nso.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Loader {
|
||||
namespace {
|
||||
struct MODHeader {
|
||||
u32_le magic;
|
||||
u32_le dynamic_offset;
|
||||
u32_le bss_start_offset;
|
||||
u32_le bss_end_offset;
|
||||
u32_le eh_frame_hdr_start_offset;
|
||||
u32_le eh_frame_hdr_end_offset;
|
||||
u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
|
||||
};
|
||||
static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
|
||||
|
||||
std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
|
||||
const NSOSegmentHeader& header) {
|
||||
std::vector<u8> uncompressed_data =
|
||||
Common::Compression::DecompressDataLZ4(compressed_data, header.size);
|
||||
|
||||
ASSERT_MSG(uncompressed_data.size() == header.size, "{} != {}", header.size,
|
||||
uncompressed_data.size());
|
||||
|
||||
return uncompressed_data;
|
||||
}
|
||||
|
||||
constexpr u32 PageAlignSize(u32 size) {
|
||||
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
bool NSOHeader::IsSegmentCompressed(size_t segment_num) const {
|
||||
ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
|
||||
return ((flags >> segment_num) & 1) != 0;
|
||||
}
|
||||
|
||||
AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file_) : AppLoader(std::move(file_)) {}
|
||||
|
||||
FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& in_file) {
|
||||
u32 magic = 0;
|
||||
if (in_file->ReadObject(&magic) != sizeof(magic)) {
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
if (Common::MakeMagic('N', 'S', 'O', '0') != magic) {
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
return FileType::NSO;
|
||||
}
|
||||
|
||||
std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system,
|
||||
const FileSys::VfsFile& nso_file, VAddr load_base,
|
||||
bool should_pass_arguments, bool load_into_process,
|
||||
std::optional<FileSys::PatchManager> pm) {
|
||||
if (nso_file.GetSize() < sizeof(NSOHeader)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
NSOHeader nso_header{};
|
||||
if (sizeof(NSOHeader) != nso_file.ReadObject(&nso_header)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Build program image
|
||||
Kernel::CodeSet codeset;
|
||||
Kernel::PhysicalMemory program_image;
|
||||
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
|
||||
std::vector<u8> data = nso_file.ReadBytes(nso_header.segments_compressed_size[i],
|
||||
nso_header.segments[i].offset);
|
||||
if (nso_header.IsSegmentCompressed(i)) {
|
||||
data = DecompressSegment(data, nso_header.segments[i]);
|
||||
}
|
||||
program_image.resize(nso_header.segments[i].location + static_cast<u32>(data.size()));
|
||||
std::memcpy(program_image.data() + nso_header.segments[i].location, data.data(),
|
||||
data.size());
|
||||
codeset.segments[i].addr = nso_header.segments[i].location;
|
||||
codeset.segments[i].offset = nso_header.segments[i].location;
|
||||
codeset.segments[i].size = nso_header.segments[i].size;
|
||||
}
|
||||
|
||||
if (should_pass_arguments && !Settings::values.program_args.GetValue().empty()) {
|
||||
const auto arg_data{Settings::values.program_args.GetValue()};
|
||||
|
||||
codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
|
||||
NSOArgumentHeader args_header{
|
||||
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
|
||||
const auto end_offset = program_image.size();
|
||||
program_image.resize(static_cast<u32>(program_image.size()) +
|
||||
NSO_ARGUMENT_DATA_ALLOCATION_SIZE);
|
||||
std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader));
|
||||
std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(),
|
||||
arg_data.size());
|
||||
}
|
||||
|
||||
codeset.DataSegment().size += nso_header.segments[2].bss_size;
|
||||
const u32 image_size{
|
||||
PageAlignSize(static_cast<u32>(program_image.size()) + nso_header.segments[2].bss_size)};
|
||||
program_image.resize(image_size);
|
||||
|
||||
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
|
||||
codeset.segments[i].size = PageAlignSize(codeset.segments[i].size);
|
||||
}
|
||||
|
||||
// Apply patches if necessary
|
||||
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
|
||||
std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size());
|
||||
std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader));
|
||||
std::memcpy(pi_header.data() + sizeof(NSOHeader), program_image.data(),
|
||||
program_image.size());
|
||||
|
||||
pi_header = pm->PatchNSO(pi_header, nso_file.GetName());
|
||||
|
||||
std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.data());
|
||||
}
|
||||
|
||||
// If we aren't actually loading (i.e. just computing the process code layout), we are done
|
||||
if (!load_into_process) {
|
||||
return load_base + image_size;
|
||||
}
|
||||
|
||||
// Apply cheats if they exist and the program has a valid title ID
|
||||
if (pm) {
|
||||
system.SetCurrentProcessBuildID(nso_header.build_id);
|
||||
const auto cheats = pm->CreateCheatList(nso_header.build_id);
|
||||
if (!cheats.empty()) {
|
||||
system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Load codeset for current process
|
||||
codeset.memory = std::move(program_image);
|
||||
process.LoadModule(std::move(codeset), load_base);
|
||||
|
||||
return load_base + image_size;
|
||||
}
|
||||
|
||||
AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
modules.clear();
|
||||
|
||||
// Load module
|
||||
const VAddr base_address = process.PageTable().GetCodeRegionStart();
|
||||
if (!LoadModule(process, system, *file, base_address, true, true)) {
|
||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||
}
|
||||
|
||||
modules.insert_or_assign(base_address, file->GetName());
|
||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
|
||||
|
||||
is_loaded = true;
|
||||
return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority,
|
||||
Core::Memory::DEFAULT_STACK_SIZE}};
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSO::ReadNSOModules(Modules& out_modules) {
|
||||
out_modules = this->modules;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/lz4_compression.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/loader/nso.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Loader {
|
||||
namespace {
|
||||
struct MODHeader {
|
||||
u32_le magic;
|
||||
u32_le dynamic_offset;
|
||||
u32_le bss_start_offset;
|
||||
u32_le bss_end_offset;
|
||||
u32_le eh_frame_hdr_start_offset;
|
||||
u32_le eh_frame_hdr_end_offset;
|
||||
u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
|
||||
};
|
||||
static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
|
||||
|
||||
std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
|
||||
const NSOSegmentHeader& header) {
|
||||
std::vector<u8> uncompressed_data =
|
||||
Common::Compression::DecompressDataLZ4(compressed_data, header.size);
|
||||
|
||||
ASSERT_MSG(uncompressed_data.size() == header.size, "{} != {}", header.size,
|
||||
uncompressed_data.size());
|
||||
|
||||
return uncompressed_data;
|
||||
}
|
||||
|
||||
constexpr u32 PageAlignSize(u32 size) {
|
||||
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
bool NSOHeader::IsSegmentCompressed(size_t segment_num) const {
|
||||
ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
|
||||
return ((flags >> segment_num) & 1) != 0;
|
||||
}
|
||||
|
||||
AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file_) : AppLoader(std::move(file_)) {}
|
||||
|
||||
FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& in_file) {
|
||||
u32 magic = 0;
|
||||
if (in_file->ReadObject(&magic) != sizeof(magic)) {
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
if (Common::MakeMagic('N', 'S', 'O', '0') != magic) {
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
return FileType::NSO;
|
||||
}
|
||||
|
||||
std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system,
|
||||
const FileSys::VfsFile& nso_file, VAddr load_base,
|
||||
bool should_pass_arguments, bool load_into_process,
|
||||
std::optional<FileSys::PatchManager> pm) {
|
||||
if (nso_file.GetSize() < sizeof(NSOHeader)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
NSOHeader nso_header{};
|
||||
if (sizeof(NSOHeader) != nso_file.ReadObject(&nso_header)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Build program image
|
||||
Kernel::CodeSet codeset;
|
||||
Kernel::PhysicalMemory program_image;
|
||||
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
|
||||
std::vector<u8> data = nso_file.ReadBytes(nso_header.segments_compressed_size[i],
|
||||
nso_header.segments[i].offset);
|
||||
if (nso_header.IsSegmentCompressed(i)) {
|
||||
data = DecompressSegment(data, nso_header.segments[i]);
|
||||
}
|
||||
program_image.resize(nso_header.segments[i].location + static_cast<u32>(data.size()));
|
||||
std::memcpy(program_image.data() + nso_header.segments[i].location, data.data(),
|
||||
data.size());
|
||||
codeset.segments[i].addr = nso_header.segments[i].location;
|
||||
codeset.segments[i].offset = nso_header.segments[i].location;
|
||||
codeset.segments[i].size = nso_header.segments[i].size;
|
||||
}
|
||||
|
||||
if (should_pass_arguments && !Settings::values.program_args.GetValue().empty()) {
|
||||
const auto arg_data{Settings::values.program_args.GetValue()};
|
||||
|
||||
codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
|
||||
NSOArgumentHeader args_header{
|
||||
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
|
||||
const auto end_offset = program_image.size();
|
||||
program_image.resize(static_cast<u32>(program_image.size()) +
|
||||
NSO_ARGUMENT_DATA_ALLOCATION_SIZE);
|
||||
std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader));
|
||||
std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(),
|
||||
arg_data.size());
|
||||
}
|
||||
|
||||
codeset.DataSegment().size += nso_header.segments[2].bss_size;
|
||||
const u32 image_size{
|
||||
PageAlignSize(static_cast<u32>(program_image.size()) + nso_header.segments[2].bss_size)};
|
||||
program_image.resize(image_size);
|
||||
|
||||
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
|
||||
codeset.segments[i].size = PageAlignSize(codeset.segments[i].size);
|
||||
}
|
||||
|
||||
// Apply patches if necessary
|
||||
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
|
||||
std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size());
|
||||
std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader));
|
||||
std::memcpy(pi_header.data() + sizeof(NSOHeader), program_image.data(),
|
||||
program_image.size());
|
||||
|
||||
pi_header = pm->PatchNSO(pi_header, nso_file.GetName());
|
||||
|
||||
std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.data());
|
||||
}
|
||||
|
||||
// If we aren't actually loading (i.e. just computing the process code layout), we are done
|
||||
if (!load_into_process) {
|
||||
return load_base + image_size;
|
||||
}
|
||||
|
||||
// Apply cheats if they exist and the program has a valid title ID
|
||||
if (pm) {
|
||||
system.SetCurrentProcessBuildID(nso_header.build_id);
|
||||
const auto cheats = pm->CreateCheatList(nso_header.build_id);
|
||||
if (!cheats.empty()) {
|
||||
system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Load codeset for current process
|
||||
codeset.memory = std::move(program_image);
|
||||
process.LoadModule(std::move(codeset), load_base);
|
||||
|
||||
return load_base + image_size;
|
||||
}
|
||||
|
||||
AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
modules.clear();
|
||||
|
||||
// Load module
|
||||
const VAddr base_address = process.PageTable().GetCodeRegionStart();
|
||||
if (!LoadModule(process, system, *file, base_address, true, true)) {
|
||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||
}
|
||||
|
||||
modules.insert_or_assign(base_address, file->GetName());
|
||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
|
||||
|
||||
is_loaded = true;
|
||||
return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority,
|
||||
Core::Memory::DEFAULT_STACK_SIZE}};
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSO::ReadNSOModules(Modules& out_modules) {
|
||||
out_modules = this->modules;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,101 +1,101 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
struct NSOSegmentHeader {
|
||||
u32_le offset;
|
||||
u32_le location;
|
||||
u32_le size;
|
||||
union {
|
||||
u32_le alignment;
|
||||
u32_le bss_size;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(NSOSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
|
||||
|
||||
struct NSOHeader {
|
||||
using SHA256Hash = std::array<u8, 0x20>;
|
||||
|
||||
struct RODataRelativeExtent {
|
||||
u32_le data_offset;
|
||||
u32_le size;
|
||||
};
|
||||
|
||||
u32_le magic;
|
||||
u32_le version;
|
||||
u32 reserved;
|
||||
u32_le flags;
|
||||
std::array<NSOSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
|
||||
std::array<u8, 0x20> build_id;
|
||||
std::array<u32_le, 3> segments_compressed_size;
|
||||
std::array<u8, 0x1C> padding;
|
||||
RODataRelativeExtent api_info_extent;
|
||||
RODataRelativeExtent dynstr_extent;
|
||||
RODataRelativeExtent dynsyn_extent;
|
||||
std::array<SHA256Hash, 3> segment_hashes;
|
||||
|
||||
bool IsSegmentCompressed(size_t segment_num) const;
|
||||
};
|
||||
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
|
||||
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
|
||||
|
||||
constexpr u32 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
|
||||
|
||||
struct NSOArgumentHeader {
|
||||
u32_le allocated_size;
|
||||
u32_le actual_size;
|
||||
INSERT_PADDING_BYTES(0x18);
|
||||
};
|
||||
static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size.");
|
||||
|
||||
/// Loads an NSO file
|
||||
class AppLoader_NSO final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_NSO(FileSys::VirtualFile file_);
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is a form of NSO file.
|
||||
*
|
||||
* @param in_file The file to be identified.
|
||||
*
|
||||
* @return FileType::NSO if found, or FileType::Error if some other type of file.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& in_file);
|
||||
|
||||
FileType GetFileType() const override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system,
|
||||
const FileSys::VfsFile& nso_file, VAddr load_base,
|
||||
bool should_pass_arguments, bool load_into_process,
|
||||
std::optional<FileSys::PatchManager> pm = {});
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadNSOModules(Modules& out_modules) override;
|
||||
|
||||
private:
|
||||
Modules modules;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
struct NSOSegmentHeader {
|
||||
u32_le offset;
|
||||
u32_le location;
|
||||
u32_le size;
|
||||
union {
|
||||
u32_le alignment;
|
||||
u32_le bss_size;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(NSOSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
|
||||
|
||||
struct NSOHeader {
|
||||
using SHA256Hash = std::array<u8, 0x20>;
|
||||
|
||||
struct RODataRelativeExtent {
|
||||
u32_le data_offset;
|
||||
u32_le size;
|
||||
};
|
||||
|
||||
u32_le magic;
|
||||
u32_le version;
|
||||
u32 reserved;
|
||||
u32_le flags;
|
||||
std::array<NSOSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
|
||||
std::array<u8, 0x20> build_id;
|
||||
std::array<u32_le, 3> segments_compressed_size;
|
||||
std::array<u8, 0x1C> padding;
|
||||
RODataRelativeExtent api_info_extent;
|
||||
RODataRelativeExtent dynstr_extent;
|
||||
RODataRelativeExtent dynsyn_extent;
|
||||
std::array<SHA256Hash, 3> segment_hashes;
|
||||
|
||||
bool IsSegmentCompressed(size_t segment_num) const;
|
||||
};
|
||||
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
|
||||
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
|
||||
|
||||
constexpr u32 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
|
||||
|
||||
struct NSOArgumentHeader {
|
||||
u32_le allocated_size;
|
||||
u32_le actual_size;
|
||||
INSERT_PADDING_BYTES(0x18);
|
||||
};
|
||||
static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size.");
|
||||
|
||||
/// Loads an NSO file
|
||||
class AppLoader_NSO final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_NSO(FileSys::VirtualFile file_);
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is a form of NSO file.
|
||||
*
|
||||
* @param in_file The file to be identified.
|
||||
*
|
||||
* @return FileType::NSO if found, or FileType::Error if some other type of file.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& in_file);
|
||||
|
||||
FileType GetFileType() const override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system,
|
||||
const FileSys::VfsFile& nso_file, VAddr load_base,
|
||||
bool should_pass_arguments, bool load_into_process,
|
||||
std::optional<FileSys::PatchManager> pm = {});
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadNSOModules(Modules& out_modules) override;
|
||||
|
||||
private:
|
||||
Modules modules;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,212 +1,212 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/deconstructed_rom_directory.h"
|
||||
#include "core/loader/nca.h"
|
||||
#include "core/loader/nsp.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_,
|
||||
const Service::FileSystem::FileSystemController& fsc,
|
||||
const FileSys::ContentProvider& content_provider, u64 program_id,
|
||||
std::size_t program_index)
|
||||
: AppLoader(file_), nsp(std::make_unique<FileSys::NSP>(file_, program_id, program_index)) {
|
||||
|
||||
if (nsp->GetStatus() != ResultStatus::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nsp->IsExtractedType()) {
|
||||
secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
|
||||
} else {
|
||||
const auto control_nca =
|
||||
nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
|
||||
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] {
|
||||
const FileSys::PatchManager pm{nsp->GetProgramTitleID(), fsc, content_provider};
|
||||
return pm.ParseControlNCA(*control_nca);
|
||||
}();
|
||||
|
||||
secondary_loader = std::make_unique<AppLoader_NCA>(
|
||||
nsp->GetNCAFile(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Program));
|
||||
}
|
||||
}
|
||||
|
||||
AppLoader_NSP::~AppLoader_NSP() = default;
|
||||
|
||||
FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& nsp_file) {
|
||||
const FileSys::NSP nsp(nsp_file);
|
||||
|
||||
if (nsp.GetStatus() == ResultStatus::Success) {
|
||||
// Extracted Type case
|
||||
if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr &&
|
||||
FileSys::IsDirectoryExeFS(nsp.GetExeFS())) {
|
||||
return FileType::NSP;
|
||||
}
|
||||
|
||||
// Non-Extracted Type case
|
||||
const auto program_id = nsp.GetProgramTitleID();
|
||||
if (!nsp.IsExtractedType() &&
|
||||
nsp.GetNCA(program_id, FileSys::ContentRecordType::Program) != nullptr &&
|
||||
AppLoader_NCA::IdentifyType(
|
||||
nsp.GetNCAFile(program_id, FileSys::ContentRecordType::Program)) == FileType::NCA) {
|
||||
return FileType::NSP;
|
||||
}
|
||||
}
|
||||
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
const auto title_id = nsp->GetProgramTitleID();
|
||||
|
||||
if (!nsp->IsExtractedType() && title_id == 0) {
|
||||
return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
|
||||
}
|
||||
|
||||
const auto nsp_status = nsp->GetStatus();
|
||||
if (nsp_status != ResultStatus::Success) {
|
||||
return {nsp_status, {}};
|
||||
}
|
||||
|
||||
const auto nsp_program_status = nsp->GetProgramStatus();
|
||||
if (nsp_program_status != ResultStatus::Success) {
|
||||
return {nsp_program_status, {}};
|
||||
}
|
||||
|
||||
if (!nsp->IsExtractedType() &&
|
||||
nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
|
||||
if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
|
||||
return {ResultStatus::ErrorMissingProductionKeyFile, {}};
|
||||
}
|
||||
|
||||
return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
|
||||
}
|
||||
|
||||
const auto result = secondary_loader->Load(process, system);
|
||||
if (result.first != ResultStatus::Success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile update_raw;
|
||||
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
|
||||
system.GetFileSystemController().SetPackedUpdate(std::move(update_raw));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& out_file) {
|
||||
return secondary_loader->ReadRomFS(out_file);
|
||||
}
|
||||
|
||||
u64 AppLoader_NSP::ReadRomFSIVFCOffset() const {
|
||||
return secondary_loader->ReadRomFSIVFCOffset();
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& out_file) {
|
||||
if (nsp->IsExtractedType()) {
|
||||
return ResultStatus::ErrorNoPackedUpdate;
|
||||
}
|
||||
|
||||
const auto read = nsp->GetNCAFile(FileSys::GetUpdateTitleID(nsp->GetProgramTitleID()),
|
||||
FileSys::ContentRecordType::Program);
|
||||
|
||||
if (read == nullptr) {
|
||||
return ResultStatus::ErrorNoPackedUpdate;
|
||||
}
|
||||
|
||||
const auto nca_test = std::make_shared<FileSys::NCA>(read);
|
||||
if (nca_test->GetStatus() != ResultStatus::ErrorMissingBKTRBaseRomFS) {
|
||||
return nca_test->GetStatus();
|
||||
}
|
||||
|
||||
out_file = read;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) {
|
||||
out_program_id = nsp->GetProgramTitleID();
|
||||
if (out_program_id == 0) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadProgramIds(std::vector<u64>& out_program_ids) {
|
||||
out_program_ids = nsp->GetProgramTitleIDs();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_file == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
buffer = icon_file->ReadAllBytes();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadTitle(std::string& title) {
|
||||
if (nacp_file == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
title = nacp_file->GetApplicationName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadControlData(FileSys::NACP& nacp) {
|
||||
if (nacp_file == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
nacp = *nacp_file;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadManualRomFS(FileSys::VirtualFile& out_file) {
|
||||
const auto nca =
|
||||
nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::HtmlDocument);
|
||||
if (nsp->GetStatus() != ResultStatus::Success || nca == nullptr) {
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
}
|
||||
|
||||
out_file = nca->GetRomFS();
|
||||
return out_file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadBanner(std::vector<u8>& buffer) {
|
||||
return secondary_loader->ReadBanner(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadLogo(std::vector<u8>& buffer) {
|
||||
return secondary_loader->ReadLogo(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadNSOModules(Modules& modules) {
|
||||
return secondary_loader->ReadNSOModules(modules);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/deconstructed_rom_directory.h"
|
||||
#include "core/loader/nca.h"
|
||||
#include "core/loader/nsp.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_,
|
||||
const Service::FileSystem::FileSystemController& fsc,
|
||||
const FileSys::ContentProvider& content_provider, u64 program_id,
|
||||
std::size_t program_index)
|
||||
: AppLoader(file_), nsp(std::make_unique<FileSys::NSP>(file_, program_id, program_index)) {
|
||||
|
||||
if (nsp->GetStatus() != ResultStatus::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nsp->IsExtractedType()) {
|
||||
secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
|
||||
} else {
|
||||
const auto control_nca =
|
||||
nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
|
||||
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] {
|
||||
const FileSys::PatchManager pm{nsp->GetProgramTitleID(), fsc, content_provider};
|
||||
return pm.ParseControlNCA(*control_nca);
|
||||
}();
|
||||
|
||||
secondary_loader = std::make_unique<AppLoader_NCA>(
|
||||
nsp->GetNCAFile(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Program));
|
||||
}
|
||||
}
|
||||
|
||||
AppLoader_NSP::~AppLoader_NSP() = default;
|
||||
|
||||
FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& nsp_file) {
|
||||
const FileSys::NSP nsp(nsp_file);
|
||||
|
||||
if (nsp.GetStatus() == ResultStatus::Success) {
|
||||
// Extracted Type case
|
||||
if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr &&
|
||||
FileSys::IsDirectoryExeFS(nsp.GetExeFS())) {
|
||||
return FileType::NSP;
|
||||
}
|
||||
|
||||
// Non-Extracted Type case
|
||||
const auto program_id = nsp.GetProgramTitleID();
|
||||
if (!nsp.IsExtractedType() &&
|
||||
nsp.GetNCA(program_id, FileSys::ContentRecordType::Program) != nullptr &&
|
||||
AppLoader_NCA::IdentifyType(
|
||||
nsp.GetNCAFile(program_id, FileSys::ContentRecordType::Program)) == FileType::NCA) {
|
||||
return FileType::NSP;
|
||||
}
|
||||
}
|
||||
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
const auto title_id = nsp->GetProgramTitleID();
|
||||
|
||||
if (!nsp->IsExtractedType() && title_id == 0) {
|
||||
return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
|
||||
}
|
||||
|
||||
const auto nsp_status = nsp->GetStatus();
|
||||
if (nsp_status != ResultStatus::Success) {
|
||||
return {nsp_status, {}};
|
||||
}
|
||||
|
||||
const auto nsp_program_status = nsp->GetProgramStatus();
|
||||
if (nsp_program_status != ResultStatus::Success) {
|
||||
return {nsp_program_status, {}};
|
||||
}
|
||||
|
||||
if (!nsp->IsExtractedType() &&
|
||||
nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
|
||||
if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
|
||||
return {ResultStatus::ErrorMissingProductionKeyFile, {}};
|
||||
}
|
||||
|
||||
return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
|
||||
}
|
||||
|
||||
const auto result = secondary_loader->Load(process, system);
|
||||
if (result.first != ResultStatus::Success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile update_raw;
|
||||
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
|
||||
system.GetFileSystemController().SetPackedUpdate(std::move(update_raw));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& out_file) {
|
||||
return secondary_loader->ReadRomFS(out_file);
|
||||
}
|
||||
|
||||
u64 AppLoader_NSP::ReadRomFSIVFCOffset() const {
|
||||
return secondary_loader->ReadRomFSIVFCOffset();
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& out_file) {
|
||||
if (nsp->IsExtractedType()) {
|
||||
return ResultStatus::ErrorNoPackedUpdate;
|
||||
}
|
||||
|
||||
const auto read = nsp->GetNCAFile(FileSys::GetUpdateTitleID(nsp->GetProgramTitleID()),
|
||||
FileSys::ContentRecordType::Program);
|
||||
|
||||
if (read == nullptr) {
|
||||
return ResultStatus::ErrorNoPackedUpdate;
|
||||
}
|
||||
|
||||
const auto nca_test = std::make_shared<FileSys::NCA>(read);
|
||||
if (nca_test->GetStatus() != ResultStatus::ErrorMissingBKTRBaseRomFS) {
|
||||
return nca_test->GetStatus();
|
||||
}
|
||||
|
||||
out_file = read;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) {
|
||||
out_program_id = nsp->GetProgramTitleID();
|
||||
if (out_program_id == 0) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadProgramIds(std::vector<u64>& out_program_ids) {
|
||||
out_program_ids = nsp->GetProgramTitleIDs();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_file == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
buffer = icon_file->ReadAllBytes();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadTitle(std::string& title) {
|
||||
if (nacp_file == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
title = nacp_file->GetApplicationName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadControlData(FileSys::NACP& nacp) {
|
||||
if (nacp_file == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
nacp = *nacp_file;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadManualRomFS(FileSys::VirtualFile& out_file) {
|
||||
const auto nca =
|
||||
nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::HtmlDocument);
|
||||
if (nsp->GetStatus() != ResultStatus::Success || nca == nullptr) {
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
}
|
||||
|
||||
out_file = nca->GetRomFS();
|
||||
return out_file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadBanner(std::vector<u8>& buffer) {
|
||||
return secondary_loader->ReadBanner(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadLogo(std::vector<u8>& buffer) {
|
||||
return secondary_loader->ReadLogo(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadNSOModules(Modules& modules) {
|
||||
return secondary_loader->ReadNSOModules(modules);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,71 +1,71 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
class ContentProvider;
|
||||
class NACP;
|
||||
class NSP;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Service::FileSystem {
|
||||
class FileSystemController;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
class AppLoader_NCA;
|
||||
|
||||
/// Loads an XCI file
|
||||
class AppLoader_NSP final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_NSP(FileSys::VirtualFile file_,
|
||||
const Service::FileSystem::FileSystemController& fsc,
|
||||
const FileSys::ContentProvider& content_provider, u64 program_id,
|
||||
std::size_t program_index);
|
||||
~AppLoader_NSP() override;
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is an NSP file.
|
||||
*
|
||||
* @param nsp_file The file to identify.
|
||||
*
|
||||
* @return FileType::NSP, or FileType::Error if the file is not an NSP.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& nsp_file);
|
||||
|
||||
FileType GetFileType() const override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override;
|
||||
u64 ReadRomFSIVFCOffset() const override;
|
||||
ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
ResultStatus ReadControlData(FileSys::NACP& nacp) override;
|
||||
ResultStatus ReadManualRomFS(FileSys::VirtualFile& out_file) override;
|
||||
|
||||
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
|
||||
|
||||
ResultStatus ReadNSOModules(Modules& modules) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::NSP> nsp;
|
||||
std::unique_ptr<AppLoader> secondary_loader;
|
||||
|
||||
FileSys::VirtualFile icon_file;
|
||||
std::unique_ptr<FileSys::NACP> nacp_file;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
class ContentProvider;
|
||||
class NACP;
|
||||
class NSP;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Service::FileSystem {
|
||||
class FileSystemController;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
class AppLoader_NCA;
|
||||
|
||||
/// Loads an XCI file
|
||||
class AppLoader_NSP final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_NSP(FileSys::VirtualFile file_,
|
||||
const Service::FileSystem::FileSystemController& fsc,
|
||||
const FileSys::ContentProvider& content_provider, u64 program_id,
|
||||
std::size_t program_index);
|
||||
~AppLoader_NSP() override;
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is an NSP file.
|
||||
*
|
||||
* @param nsp_file The file to identify.
|
||||
*
|
||||
* @return FileType::NSP, or FileType::Error if the file is not an NSP.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& nsp_file);
|
||||
|
||||
FileType GetFileType() const override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override;
|
||||
u64 ReadRomFSIVFCOffset() const override;
|
||||
ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
ResultStatus ReadControlData(FileSys::NACP& nacp) override;
|
||||
ResultStatus ReadManualRomFS(FileSys::VirtualFile& out_file) override;
|
||||
|
||||
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
|
||||
|
||||
ResultStatus ReadNSOModules(Modules& modules) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::NSP> nsp;
|
||||
std::unique_ptr<AppLoader> secondary_loader;
|
||||
|
||||
FileSys::VirtualFile icon_file;
|
||||
std::unique_ptr<FileSys::NACP> nacp_file;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,178 +1,178 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/nca.h"
|
||||
#include "core/loader/xci.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file_,
|
||||
const Service::FileSystem::FileSystemController& fsc,
|
||||
const FileSys::ContentProvider& content_provider, u64 program_id,
|
||||
std::size_t program_index)
|
||||
: AppLoader(file_), xci(std::make_unique<FileSys::XCI>(file_, program_id, program_index)),
|
||||
nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
|
||||
if (xci->GetStatus() != ResultStatus::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
|
||||
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] {
|
||||
const FileSys::PatchManager pm{xci->GetProgramTitleID(), fsc, content_provider};
|
||||
return pm.ParseControlNCA(*control_nca);
|
||||
}();
|
||||
}
|
||||
|
||||
AppLoader_XCI::~AppLoader_XCI() = default;
|
||||
|
||||
FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& xci_file) {
|
||||
const FileSys::XCI xci(xci_file);
|
||||
|
||||
if (xci.GetStatus() == ResultStatus::Success &&
|
||||
xci.GetNCAByType(FileSys::NCAContentType::Program) != nullptr &&
|
||||
AppLoader_NCA::IdentifyType(xci.GetNCAFileByType(FileSys::NCAContentType::Program)) ==
|
||||
FileType::NCA) {
|
||||
return FileType::XCI;
|
||||
}
|
||||
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
if (xci->GetStatus() != ResultStatus::Success) {
|
||||
return {xci->GetStatus(), {}};
|
||||
}
|
||||
|
||||
if (xci->GetProgramNCAStatus() != ResultStatus::Success) {
|
||||
return {xci->GetProgramNCAStatus(), {}};
|
||||
}
|
||||
|
||||
if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false)) {
|
||||
return {ResultStatus::ErrorMissingProductionKeyFile, {}};
|
||||
}
|
||||
|
||||
const auto result = nca_loader->Load(process, system);
|
||||
if (result.first != ResultStatus::Success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile update_raw;
|
||||
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
|
||||
system.GetFileSystemController().SetPackedUpdate(std::move(update_raw));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& out_file) {
|
||||
return nca_loader->ReadRomFS(out_file);
|
||||
}
|
||||
|
||||
u64 AppLoader_XCI::ReadRomFSIVFCOffset() const {
|
||||
return nca_loader->ReadRomFSIVFCOffset();
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadUpdateRaw(FileSys::VirtualFile& out_file) {
|
||||
u64 program_id{};
|
||||
nca_loader->ReadProgramId(program_id);
|
||||
if (program_id == 0) {
|
||||
return ResultStatus::ErrorXCIMissingProgramNCA;
|
||||
}
|
||||
|
||||
const auto read = xci->GetSecurePartitionNSP()->GetNCAFile(
|
||||
FileSys::GetUpdateTitleID(program_id), FileSys::ContentRecordType::Program);
|
||||
if (read == nullptr) {
|
||||
return ResultStatus::ErrorNoPackedUpdate;
|
||||
}
|
||||
|
||||
const auto nca_test = std::make_shared<FileSys::NCA>(read);
|
||||
if (nca_test->GetStatus() != ResultStatus::ErrorMissingBKTRBaseRomFS) {
|
||||
return nca_test->GetStatus();
|
||||
}
|
||||
|
||||
out_file = read;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {
|
||||
return nca_loader->ReadProgramId(out_program_id);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadProgramIds(std::vector<u64>& out_program_ids) {
|
||||
out_program_ids = xci->GetProgramTitleIDs();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_file == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
buffer = icon_file->ReadAllBytes();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
|
||||
if (nacp_file == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
title = nacp_file->GetApplicationName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) {
|
||||
if (nacp_file == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
control = *nacp_file;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& out_file) {
|
||||
const auto nca =
|
||||
xci->GetSecurePartitionNSP()->GetNCA(xci->GetSecurePartitionNSP()->GetProgramTitleID(),
|
||||
FileSys::ContentRecordType::HtmlDocument);
|
||||
if (xci->GetStatus() != ResultStatus::Success || nca == nullptr) {
|
||||
return ResultStatus::ErrorXCIMissingPartition;
|
||||
}
|
||||
|
||||
out_file = nca->GetRomFS();
|
||||
return out_file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadBanner(std::vector<u8>& buffer) {
|
||||
return nca_loader->ReadBanner(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadLogo(std::vector<u8>& buffer) {
|
||||
return nca_loader->ReadLogo(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadNSOModules(Modules& modules) {
|
||||
return nca_loader->ReadNSOModules(modules);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/nca.h"
|
||||
#include "core/loader/xci.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file_,
|
||||
const Service::FileSystem::FileSystemController& fsc,
|
||||
const FileSys::ContentProvider& content_provider, u64 program_id,
|
||||
std::size_t program_index)
|
||||
: AppLoader(file_), xci(std::make_unique<FileSys::XCI>(file_, program_id, program_index)),
|
||||
nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
|
||||
if (xci->GetStatus() != ResultStatus::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
|
||||
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] {
|
||||
const FileSys::PatchManager pm{xci->GetProgramTitleID(), fsc, content_provider};
|
||||
return pm.ParseControlNCA(*control_nca);
|
||||
}();
|
||||
}
|
||||
|
||||
AppLoader_XCI::~AppLoader_XCI() = default;
|
||||
|
||||
FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& xci_file) {
|
||||
const FileSys::XCI xci(xci_file);
|
||||
|
||||
if (xci.GetStatus() == ResultStatus::Success &&
|
||||
xci.GetNCAByType(FileSys::NCAContentType::Program) != nullptr &&
|
||||
AppLoader_NCA::IdentifyType(xci.GetNCAFileByType(FileSys::NCAContentType::Program)) ==
|
||||
FileType::NCA) {
|
||||
return FileType::XCI;
|
||||
}
|
||||
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::KProcess& process, Core::System& system) {
|
||||
if (is_loaded) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
if (xci->GetStatus() != ResultStatus::Success) {
|
||||
return {xci->GetStatus(), {}};
|
||||
}
|
||||
|
||||
if (xci->GetProgramNCAStatus() != ResultStatus::Success) {
|
||||
return {xci->GetProgramNCAStatus(), {}};
|
||||
}
|
||||
|
||||
if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false)) {
|
||||
return {ResultStatus::ErrorMissingProductionKeyFile, {}};
|
||||
}
|
||||
|
||||
const auto result = nca_loader->Load(process, system);
|
||||
if (result.first != ResultStatus::Success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile update_raw;
|
||||
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
|
||||
system.GetFileSystemController().SetPackedUpdate(std::move(update_raw));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& out_file) {
|
||||
return nca_loader->ReadRomFS(out_file);
|
||||
}
|
||||
|
||||
u64 AppLoader_XCI::ReadRomFSIVFCOffset() const {
|
||||
return nca_loader->ReadRomFSIVFCOffset();
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadUpdateRaw(FileSys::VirtualFile& out_file) {
|
||||
u64 program_id{};
|
||||
nca_loader->ReadProgramId(program_id);
|
||||
if (program_id == 0) {
|
||||
return ResultStatus::ErrorXCIMissingProgramNCA;
|
||||
}
|
||||
|
||||
const auto read = xci->GetSecurePartitionNSP()->GetNCAFile(
|
||||
FileSys::GetUpdateTitleID(program_id), FileSys::ContentRecordType::Program);
|
||||
if (read == nullptr) {
|
||||
return ResultStatus::ErrorNoPackedUpdate;
|
||||
}
|
||||
|
||||
const auto nca_test = std::make_shared<FileSys::NCA>(read);
|
||||
if (nca_test->GetStatus() != ResultStatus::ErrorMissingBKTRBaseRomFS) {
|
||||
return nca_test->GetStatus();
|
||||
}
|
||||
|
||||
out_file = read;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {
|
||||
return nca_loader->ReadProgramId(out_program_id);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadProgramIds(std::vector<u64>& out_program_ids) {
|
||||
out_program_ids = xci->GetProgramTitleIDs();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_file == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
buffer = icon_file->ReadAllBytes();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
|
||||
if (nacp_file == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
title = nacp_file->GetApplicationName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) {
|
||||
if (nacp_file == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
control = *nacp_file;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& out_file) {
|
||||
const auto nca =
|
||||
xci->GetSecurePartitionNSP()->GetNCA(xci->GetSecurePartitionNSP()->GetProgramTitleID(),
|
||||
FileSys::ContentRecordType::HtmlDocument);
|
||||
if (xci->GetStatus() != ResultStatus::Success || nca == nullptr) {
|
||||
return ResultStatus::ErrorXCIMissingPartition;
|
||||
}
|
||||
|
||||
out_file = nca->GetRomFS();
|
||||
return out_file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadBanner(std::vector<u8>& buffer) {
|
||||
return nca_loader->ReadBanner(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadLogo(std::vector<u8>& buffer) {
|
||||
return nca_loader->ReadLogo(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadNSOModules(Modules& modules) {
|
||||
return nca_loader->ReadNSOModules(modules);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
@@ -1,71 +1,71 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
class ContentProvider;
|
||||
class NACP;
|
||||
class XCI;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Service::FileSystem {
|
||||
class FileSystemController;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
class AppLoader_NCA;
|
||||
|
||||
/// Loads an XCI file
|
||||
class AppLoader_XCI final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_XCI(FileSys::VirtualFile file_,
|
||||
const Service::FileSystem::FileSystemController& fsc,
|
||||
const FileSys::ContentProvider& content_provider, u64 program_id,
|
||||
std::size_t program_index);
|
||||
~AppLoader_XCI() override;
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is an XCI file.
|
||||
*
|
||||
* @param xci_file The file to identify.
|
||||
*
|
||||
* @return FileType::XCI, or FileType::Error if the file is not an XCI file.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& xci_file);
|
||||
|
||||
FileType GetFileType() const override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override;
|
||||
u64 ReadRomFSIVFCOffset() const override;
|
||||
ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
ResultStatus ReadControlData(FileSys::NACP& control) override;
|
||||
ResultStatus ReadManualRomFS(FileSys::VirtualFile& out_file) override;
|
||||
|
||||
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
|
||||
|
||||
ResultStatus ReadNSOModules(Modules& modules) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::XCI> xci;
|
||||
std::unique_ptr<AppLoader_NCA> nca_loader;
|
||||
|
||||
FileSys::VirtualFile icon_file;
|
||||
std::unique_ptr<FileSys::NACP> nacp_file;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
class ContentProvider;
|
||||
class NACP;
|
||||
class XCI;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Service::FileSystem {
|
||||
class FileSystemController;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
class AppLoader_NCA;
|
||||
|
||||
/// Loads an XCI file
|
||||
class AppLoader_XCI final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_XCI(FileSys::VirtualFile file_,
|
||||
const Service::FileSystem::FileSystemController& fsc,
|
||||
const FileSys::ContentProvider& content_provider, u64 program_id,
|
||||
std::size_t program_index);
|
||||
~AppLoader_XCI() override;
|
||||
|
||||
/**
|
||||
* Identifies whether or not the given file is an XCI file.
|
||||
*
|
||||
* @param xci_file The file to identify.
|
||||
*
|
||||
* @return FileType::XCI, or FileType::Error if the file is not an XCI file.
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& xci_file);
|
||||
|
||||
FileType GetFileType() const override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override;
|
||||
u64 ReadRomFSIVFCOffset() const override;
|
||||
ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
ResultStatus ReadControlData(FileSys::NACP& control) override;
|
||||
ResultStatus ReadManualRomFS(FileSys::VirtualFile& out_file) override;
|
||||
|
||||
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
|
||||
|
||||
ResultStatus ReadNSOModules(Modules& modules) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::XCI> xci;
|
||||
std::unique_ptr<AppLoader_NCA> nca_loader;
|
||||
|
||||
FileSys::VirtualFile icon_file;
|
||||
std::unique_ptr<FileSys::NACP> nacp_file;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
Reference in New Issue
Block a user