early-access version 1255

This commit is contained in:
pineappleEA
2020-12-28 15:15:37 +00:00
parent 84b39492d1
commit 78b48028e1
6254 changed files with 1868140 additions and 0 deletions

View File

@@ -0,0 +1,242 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include <cstring>
#include "common/common_funcs.h"
#include "common/file_util.h"
#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/kernel.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/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 dir = file->GetContainingDirectory();
// Title ID
const auto npdm = 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 = 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 = dir->GetFiles();
const auto icon_iter =
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
return file->GetExtension() == "png" || file->GetExtension() == "jpg" ||
file->GetExtension() == "bmp" || file->GetExtension() == "jpeg";
});
if (icon_iter != files.end())
icon_data = (*icon_iter)->ReadAllBytes();
}
// Metadata
FileSys::VirtualFile nacp_file = dir->GetFile("control.nacp");
if (nacp_file == nullptr) {
const auto& files = dir->GetFiles();
const auto nacp_iter =
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
return file->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& file) {
if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) {
return FileType::DeconstructedRomDirectory;
}
return FileType::Error;
}
AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load(
Kernel::Process& 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", "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& file) {
return file->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& dir) {
if (romfs == nullptr)
return ResultStatus::ErrorNoRomFS;
dir = romfs;
return ResultStatus::Success;
}
ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buffer) {
if (icon_data.empty())
return ResultStatus::ErrorNoIcon;
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& title) {
if (name.empty())
return ResultStatus::ErrorNoControl;
title = name;
return ResultStatus::Success;
}
bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() const {
return false;
}
ResultStatus AppLoader_DeconstructedRomDirectory::ReadNSOModules(Modules& modules) {
if (!is_loaded) {
return ResultStatus::ErrorNotInitialized;
}
modules = this->modules;
return ResultStatus::Success;
}
} // namespace Loader

View File

@@ -0,0 +1,67 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#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);
/**
* Returns the type of the file
* @param file open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() const override {
return IdentifyType(file);
}
LoadResult Load(Kernel::Process& process, Core::System& system) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadTitle(std::string& title) override;
bool IsRomFSUpdatable() const override;
ResultStatus ReadNSOModules(Modules& 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

413
src/core/loader/elf.cpp Executable file
View File

@@ -0,0 +1,413 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <memory>
#include <string>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process.h"
#include "core/loader/elf.h"
#include "core/memory.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// ELF Header Constants
// File type
enum ElfType {
ET_NONE = 0,
ET_REL = 1,
ET_EXEC = 2,
ET_DYN = 3,
ET_CORE = 4,
ET_LOPROC = 0xFF00,
ET_HIPROC = 0xFFFF,
};
// Machine/Architecture
enum ElfMachine {
EM_NONE = 0,
EM_M32 = 1,
EM_SPARC = 2,
EM_386 = 3,
EM_68K = 4,
EM_88K = 5,
EM_860 = 7,
EM_MIPS = 8
};
// File version
#define EV_NONE 0
#define EV_CURRENT 1
// Identification index
#define EI_MAG0 0
#define EI_MAG1 1
#define EI_MAG2 2
#define EI_MAG3 3
#define EI_CLASS 4
#define EI_DATA 5
#define EI_VERSION 6
#define EI_PAD 7
#define EI_NIDENT 16
// Sections constants
// Section types
#define SHT_NULL 0
#define SHT_PROGBITS 1
#define SHT_SYMTAB 2
#define SHT_STRTAB 3
#define SHT_RELA 4
#define SHT_HASH 5
#define SHT_DYNAMIC 6
#define SHT_NOTE 7
#define SHT_NOBITS 8
#define SHT_REL 9
#define SHT_SHLIB 10
#define SHT_DYNSYM 11
#define SHT_LOPROC 0x70000000
#define SHT_HIPROC 0x7FFFFFFF
#define SHT_LOUSER 0x80000000
#define SHT_HIUSER 0xFFFFFFFF
// Section flags
enum ElfSectionFlags {
SHF_WRITE = 0x1,
SHF_ALLOC = 0x2,
SHF_EXECINSTR = 0x4,
SHF_MASKPROC = 0xF0000000,
};
// Segment types
#define PT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define PT_INTERP 3
#define PT_NOTE 4
#define PT_SHLIB 5
#define PT_PHDR 6
#define PT_LOPROC 0x70000000
#define PT_HIPROC 0x7FFFFFFF
// Segment flags
#define PF_X 0x1
#define PF_W 0x2
#define PF_R 0x4
#define PF_MASKPROC 0xF0000000
typedef unsigned int Elf32_Addr;
typedef unsigned short Elf32_Half;
typedef unsigned int Elf32_Off;
typedef signed int Elf32_Sword;
typedef unsigned int Elf32_Word;
////////////////////////////////////////////////////////////////////////////////////////////////////
// ELF file header
struct Elf32_Ehdr {
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
};
// Section header
struct Elf32_Shdr {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
};
// Segment header
struct Elf32_Phdr {
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
};
// Symbol table entry
struct Elf32_Sym {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
};
// Relocation entries
struct Elf32_Rel {
Elf32_Addr r_offset;
Elf32_Word r_info;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// ElfReader class
typedef int SectionID;
class ElfReader {
private:
char* base;
u32* base32;
Elf32_Ehdr* header;
Elf32_Phdr* segments;
Elf32_Shdr* sections;
u32* sectionAddrs;
bool relocate;
VAddr entryPoint;
public:
explicit ElfReader(void* ptr);
u32 Read32(int off) const {
return base32[off >> 2];
}
// Quick accessors
ElfType GetType() const {
return (ElfType)(header->e_type);
}
ElfMachine GetMachine() const {
return (ElfMachine)(header->e_machine);
}
VAddr GetEntryPoint() const {
return entryPoint;
}
u32 GetFlags() const {
return (u32)(header->e_flags);
}
Kernel::CodeSet LoadInto(VAddr vaddr);
int GetNumSegments() const {
return (int)(header->e_phnum);
}
int GetNumSections() const {
return (int)(header->e_shnum);
}
const u8* GetPtr(int offset) const {
return (u8*)base + offset;
}
const char* GetSectionName(int section) const;
const u8* GetSectionDataPtr(int section) const {
if (section < 0 || section >= header->e_shnum)
return nullptr;
if (sections[section].sh_type != SHT_NOBITS)
return GetPtr(sections[section].sh_offset);
else
return nullptr;
}
bool IsCodeSection(int section) const {
return sections[section].sh_type == SHT_PROGBITS;
}
const u8* GetSegmentPtr(int segment) {
return GetPtr(segments[segment].p_offset);
}
u32 GetSectionAddr(SectionID section) const {
return sectionAddrs[section];
}
unsigned int GetSectionSize(SectionID section) const {
return sections[section].sh_size;
}
SectionID GetSectionByName(const char* name, int firstSection = 0) const; //-1 for not found
bool DidRelocate() const {
return relocate;
}
};
ElfReader::ElfReader(void* ptr) {
base = (char*)ptr;
base32 = (u32*)ptr;
header = (Elf32_Ehdr*)ptr;
segments = (Elf32_Phdr*)(base + header->e_phoff);
sections = (Elf32_Shdr*)(base + header->e_shoff);
entryPoint = header->e_entry;
}
const char* ElfReader::GetSectionName(int section) const {
if (sections[section].sh_type == SHT_NULL)
return nullptr;
int name_offset = sections[section].sh_name;
const char* ptr = reinterpret_cast<const char*>(GetSectionDataPtr(header->e_shstrndx));
if (ptr)
return ptr + name_offset;
return nullptr;
}
Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
// Should we relocate?
relocate = (header->e_type != ET_EXEC);
if (relocate) {
LOG_DEBUG(Loader, "Relocatable module");
entryPoint += vaddr;
} else {
LOG_DEBUG(Loader, "Prerelocated executable");
}
LOG_DEBUG(Loader, "{} segments:", header->e_phnum);
// First pass : Get the bits into RAM
const VAddr base_addr = relocate ? vaddr : 0;
u64 total_image_size = 0;
for (unsigned int i = 0; i < header->e_phnum; ++i) {
const Elf32_Phdr* p = &segments[i];
if (p->p_type == PT_LOAD) {
total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF;
}
}
Kernel::PhysicalMemory program_image(total_image_size);
std::size_t current_image_position = 0;
Kernel::CodeSet codeset;
for (unsigned int i = 0; i < header->e_phnum; ++i) {
const Elf32_Phdr* p = &segments[i];
LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
p->p_vaddr, p->p_filesz, p->p_memsz);
if (p->p_type == PT_LOAD) {
Kernel::CodeSet::Segment* codeset_segment;
u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X);
if (permission_flags == (PF_R | PF_X)) {
codeset_segment = &codeset.CodeSegment();
} else if (permission_flags == (PF_R)) {
codeset_segment = &codeset.RODataSegment();
} else if (permission_flags == (PF_R | PF_W)) {
codeset_segment = &codeset.DataSegment();
} else {
LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
p->p_flags);
continue;
}
if (codeset_segment->size != 0) {
LOG_ERROR(Loader,
"ELF has more than one segment of the same type. Skipping extra "
"segment (id {})",
i);
continue;
}
const VAddr segment_addr = base_addr + p->p_vaddr;
const u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF;
codeset_segment->offset = current_image_position;
codeset_segment->addr = segment_addr;
codeset_segment->size = aligned_size;
std::memcpy(program_image.data() + current_image_position, GetSegmentPtr(i),
p->p_filesz);
current_image_position += aligned_size;
}
}
codeset.entrypoint = base_addr + header->e_entry;
codeset.memory = std::move(program_image);
LOG_DEBUG(Loader, "Done loading.");
return codeset;
}
SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const {
for (int i = firstSection; i < header->e_shnum; i++) {
const char* secname = GetSectionName(i);
if (secname != nullptr && strcmp(name, secname) == 0)
return i;
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Loader namespace
namespace Loader {
AppLoader_ELF::AppLoader_ELF(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) {
static constexpr u16 ELF_MACHINE_ARM{0x28};
u32 magic = 0;
if (4 != file->ReadObject(&magic))
return FileType::Error;
u16 machine = 0;
if (2 != file->ReadObject(&machine, 18))
return FileType::Error;
if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine)
return FileType::ELF;
return FileType::Error;
}
AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process,
[[maybe_unused]] Core::System& system) {
if (is_loaded) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
std::vector<u8> buffer = file->ReadAllBytes();
if (buffer.size() != file->GetSize()) {
return {ResultStatus::ErrorIncorrectELFFileSize, {}};
}
const VAddr base_address = process.PageTable().GetCodeRegionStart();
ElfReader elf_reader(&buffer[0]);
Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);
const VAddr entry_point = codeset.entrypoint;
// Setup the process code layout
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), buffer.size()).IsError()) {
return {ResultStatus::ErrorNotInitialized, {}};
}
process.LoadModule(std::move(codeset), entry_point);
is_loaded = true;
return {ResultStatus::Success, LoadParameters{48, Core::Memory::DEFAULT_STACK_SIZE}};
}
} // namespace Loader

36
src/core/loader/elf.h Executable file
View File

@@ -0,0 +1,36 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "common/common_types.h"
#include "core/loader/loader.h"
namespace Core {
class System;
}
namespace Loader {
/// Loads an ELF/AXF file
class AppLoader_ELF final : public AppLoader {
public:
explicit AppLoader_ELF(FileSys::VirtualFile file);
/**
* Returns the type of the file
* @param file open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() const override {
return IdentifyType(file);
}
LoadResult Load(Kernel::Process& process, Core::System& system) override;
};
} // namespace Loader

103
src/core/loader/kip.cpp Executable file
View File

@@ -0,0 +1,103 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#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/memory/page_table.h"
#include "core/hle/kernel/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::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
}
} // 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& file) {
u32_le magic{};
if (file->GetSize() < sizeof(u32) || 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::Process& 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) {
return kip.Is64Bit()
? (kip.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, kip->GetKernelCapabilities());
const VAddr base_address = process.PageTable().GetCodeRegionStart();
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();
codeset.memory = std::move(program_image);
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

39
src/core/loader/kip.h Executable file
View File

@@ -0,0 +1,39 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#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;
/**
* Returns the type of the file
* @param file open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() const override;
LoadResult Load(Kernel::Process& process, Core::System& system) override;
private:
std::unique_ptr<FileSys::KIP> kip;
};
} // namespace Loader

268
src/core/loader/loader.cpp Executable file
View File

@@ -0,0 +1,268 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include "common/concepts.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/kernel/process.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/elf.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 elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) {
return *elf_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 == "elf")
return FileType::ELF;
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::ELF:
return "ELF";
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::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, std::size_t program_index) {
switch (type) {
// Standard ELF file format.
case FileType::ELF:
return std::make_unique<AppLoader_ELF>(std::move(file));
// 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_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_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,
std::size_t program_index) {
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());
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_index);
}
} // namespace Loader

303
src/core/loader/loader.h Executable file
View File

@@ -0,0 +1,303 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <iosfwd>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#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 Process;
} // namespace Kernel
namespace Loader {
/// File types supported by CTR
enum class FileType {
Error,
Unknown,
ELF,
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::ostream& operator<<(std::ostream& os, ResultStatus status);
/// Interface for loading an application
class AppLoader : NonCopyable {
public:
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::Process& process, Core::System& system) = 0;
/**
* Get the code (typically .code section) of the application
* @param 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 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 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 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_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 RomFS of the application
* Since the RomFS can be huge, we return a file reference instead of copying to a buffer
* @param file The directory containing the RomFS
* @return ResultStatus result of function
*/
virtual ResultStatus ReadRomFS(FileSys::VirtualFile& file) {
return ResultStatus::ErrorNotImplemented;
}
/**
* Get the raw update of the application, should it come packed with one
* @param file The raw update NCA file (Program-type
* @return ResultStatus result of function
*/
virtual ResultStatus ReadUpdateRaw(FileSys::VirtualFile& 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 whether or not 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 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 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 file The raw manual RomFS of the game
* @return ResultStatus result of function
*/
virtual ResultStatus ReadManualRomFS(FileSys::VirtualFile& 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,
std::size_t program_index = 0);
} // namespace Loader

102
src/core/loader/nax.cpp Executable file
View File

@@ -0,0 +1,102 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/xts_archive.h"
#include "core/hle/kernel/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& file) {
const FileSys::NAX nax(file);
return IdentifyTypeImpl(nax);
}
FileType AppLoader_NAX::GetFileType() const {
return IdentifyTypeImpl(*nax);
}
AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process,
[[maybe_unused]] 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

54
src/core/loader/nax.h Executable file
View File

@@ -0,0 +1,54 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#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;
/**
* Returns the type of the file
* @param file open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() const override;
LoadResult Load(Kernel::Process& 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

119
src/core/loader/nca.cpp Executable file
View File

@@ -0,0 +1,119 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <utility>
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/kernel/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& file) {
FileSys::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::Process& 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

56
src/core/loader/nca.h Executable file
View File

@@ -0,0 +1,56 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/file_sys/vfs.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;
/**
* Returns the type of the file
* @param file open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() const override {
return IdentifyType(file);
}
LoadResult Load(Kernel::Process& 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

275
src/core/loader/nro.cpp Executable file
View File

@@ -0,0 +1,275 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <utility>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.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/memory/page_table.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/nro.h"
#include "core/loader/nso.h"
#include "core/memory.h"
#include "core/settings.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(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& file) {
// Read NSO header
NroHeader nro_header{};
if (sizeof(NroHeader) != 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::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
}
static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
const std::string& name) {
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.empty()) {
const auto arg_data = Settings::values.program_args;
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::Process& process, const FileSys::VfsFile& file) {
return LoadNroImpl(process, file.ReadAllBytes(), file.GetName());
}
AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& 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::THREADPRIO_DEFAULT, 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

61
src/core/loader/nro.h Executable file
View File

@@ -0,0 +1,61 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#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 Process;
}
namespace Loader {
/// Loads an NRO file
class AppLoader_NRO final : public AppLoader {
public:
explicit AppLoader_NRO(FileSys::VirtualFile file);
~AppLoader_NRO() override;
/**
* Returns the type of the file
* @param file open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() const override {
return IdentifyType(file);
}
LoadResult Load(Kernel::Process& 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::Process& process, const FileSys::VfsFile& file);
std::vector<u8> icon_data;
std::unique_ptr<FileSys::NACP> nacp;
FileSys::VirtualFile romfs;
};
} // namespace Loader

191
src/core/loader/nso.cpp Executable file
View File

@@ -0,0 +1,191 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include <cstring>
#include <vector>
#include "common/common_funcs.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/lz4_compression.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/memory/page_table.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/loader/nso.h"
#include "core/memory.h"
#include "core/settings.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::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
}
} // 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& file) {
u32 magic = 0;
if (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::Process& process, Core::System& system,
const FileSys::VfsFile& file, VAddr load_base,
bool should_pass_arguments, bool load_into_process,
std::optional<FileSys::PatchManager> pm) {
if (file.GetSize() < sizeof(NSOHeader)) {
return std::nullopt;
}
NSOHeader nso_header{};
if (sizeof(NSOHeader) != 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 =
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.empty()) {
const auto arg_data{Settings::values.program_args};
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;
pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.data(),
program_image.data() + program_image.size());
pi_header = pm->PatchNSO(pi_header, 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::Process& 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::THREADPRIO_DEFAULT, Core::Memory::DEFAULT_STACK_SIZE}};
}
ResultStatus AppLoader_NSO::ReadNSOModules(Modules& modules) {
modules = this->modules;
return ResultStatus::Success;
}
} // namespace Loader

100
src/core/loader/nso.h Executable file
View File

@@ -0,0 +1,100 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#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 Process;
}
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);
/**
* Returns the type of the file
* @param file open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() const override {
return IdentifyType(file);
}
static std::optional<VAddr> LoadModule(Kernel::Process& process, Core::System& system,
const FileSys::VfsFile& file, VAddr load_base,
bool should_pass_arguments, bool load_into_process,
std::optional<FileSys::PatchManager> pm = {});
LoadResult Load(Kernel::Process& process, Core::System& system) override;
ResultStatus ReadNSOModules(Modules& modules) override;
private:
Modules modules;
};
} // namespace Loader

199
src/core/loader/nsp.cpp Executable file
View File

@@ -0,0 +1,199 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#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/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/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,
std::size_t program_index)
: AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file, program_index)),
title_id(nsp->GetProgramTitleID()) {
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);
}();
if (title_id == 0) {
return;
}
secondary_loader = std::make_unique<AppLoader_NCA>(
nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
}
}
AppLoader_NSP::~AppLoader_NSP() = default;
FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
FileSys::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
if (!nsp.IsExtractedType() &&
nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr &&
AppLoader_NCA::IdentifyType(nsp.GetNCAFile(
nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) {
return FileType::NSP;
}
}
return FileType::Error;
}
AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process, Core::System& system) {
if (is_loaded) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
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(title_id);
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& file) {
return secondary_loader->ReadRomFS(file);
}
u64 AppLoader_NSP::ReadRomFSIVFCOffset() const {
return secondary_loader->ReadRomFSIVFCOffset();
}
ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& file) {
if (nsp->IsExtractedType())
return ResultStatus::ErrorNoPackedUpdate;
const auto read =
nsp->GetNCAFile(FileSys::GetUpdateTitleID(title_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();
file = read;
return ResultStatus::Success;
}
ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) {
if (title_id == 0)
return ResultStatus::ErrorNotInitialized;
out_program_id = title_id;
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& file) {
const auto nca =
nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::HtmlDocument);
if (nsp->GetStatus() != ResultStatus::Success || nca == nullptr)
return ResultStatus::ErrorNoRomFS;
file = nca->GetRomFS();
return 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

71
src/core/loader/nsp.h Executable file
View File

@@ -0,0 +1,71 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "common/common_types.h"
#include "core/file_sys/vfs.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,
std::size_t program_index);
~AppLoader_NSP() override;
/**
* Returns the type of the file
* @param file open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() const override {
return IdentifyType(file);
}
LoadResult Load(Kernel::Process& process, Core::System& system) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) override;
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadTitle(std::string& title) override;
ResultStatus ReadControlData(FileSys::NACP& nacp) override;
ResultStatus ReadManualRomFS(FileSys::VirtualFile& 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;
u64 title_id;
};
} // namespace Loader

164
src/core/loader/xci.cpp Executable file
View File

@@ -0,0 +1,164 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#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/romfs.h"
#include "core/file_sys/submission_package.h"
#include "core/hle/kernel/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,
std::size_t program_index)
: AppLoader(file), xci(std::make_unique<FileSys::XCI>(file, 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& file) {
FileSys::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::Process& 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& file) {
return nca_loader->ReadRomFS(file);
}
u64 AppLoader_XCI::ReadRomFSIVFCOffset() const {
return nca_loader->ReadRomFSIVFCOffset();
}
ResultStatus AppLoader_XCI::ReadUpdateRaw(FileSys::VirtualFile& 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();
file = read;
return ResultStatus::Success;
}
ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {
return nca_loader->ReadProgramId(out_program_id);
}
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& file) {
const auto nca = xci->GetSecurePartitionNSP()->GetNCA(xci->GetProgramTitleID(),
FileSys::ContentRecordType::HtmlDocument);
if (xci->GetStatus() != ResultStatus::Success || nca == nullptr)
return ResultStatus::ErrorXCIMissingPartition;
file = nca->GetRomFS();
return 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

70
src/core/loader/xci.h Executable file
View File

@@ -0,0 +1,70 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "common/common_types.h"
#include "core/file_sys/vfs.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,
std::size_t program_index);
~AppLoader_XCI() override;
/**
* Returns the type of the file
* @param file open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() const override {
return IdentifyType(file);
}
LoadResult Load(Kernel::Process& process, Core::System& system) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) override;
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadTitle(std::string& title) override;
ResultStatus ReadControlData(FileSys::NACP& control) override;
ResultStatus ReadManualRomFS(FileSys::VirtualFile& 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