mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-11-15 15:30:05 +00:00
Merge pull request #2546 from DarkLordZach/kips
loader, file_sys: Add support for parsing and loading KIP (Kernel Internal Process) files
This commit is contained in:
commit
e2f7933b3f
@ -45,6 +45,8 @@ add_library(core STATIC
|
||||
file_sys/fsmitm_romfsbuild.h
|
||||
file_sys/ips_layer.cpp
|
||||
file_sys/ips_layer.h
|
||||
file_sys/kernel_executable.cpp
|
||||
file_sys/kernel_executable.h
|
||||
file_sys/mode.h
|
||||
file_sys/nca_metadata.cpp
|
||||
file_sys/nca_metadata.h
|
||||
@ -440,6 +442,8 @@ add_library(core STATIC
|
||||
loader/deconstructed_rom_directory.h
|
||||
loader/elf.cpp
|
||||
loader/elf.h
|
||||
loader/kip.cpp
|
||||
loader/kip.h
|
||||
loader/loader.cpp
|
||||
loader/loader.h
|
||||
loader/nax.cpp
|
||||
|
@ -22,8 +22,10 @@
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/crypto/partition_data_manager.h"
|
||||
#include "core/crypto/xts_encryption_layer.h"
|
||||
#include "core/file_sys/kernel_executable.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
|
||||
using namespace Common;
|
||||
|
||||
@ -45,36 +47,6 @@ struct Package2Header {
|
||||
};
|
||||
static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
|
||||
|
||||
struct INIHeader {
|
||||
u32_le magic;
|
||||
u32_le size;
|
||||
u32_le process_count;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
};
|
||||
static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
|
||||
|
||||
struct SectionHeader {
|
||||
u32_le offset;
|
||||
u32_le size_decompressed;
|
||||
u32_le size_compressed;
|
||||
u32_le attribute;
|
||||
};
|
||||
static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size.");
|
||||
|
||||
struct KIPHeader {
|
||||
u32_le magic;
|
||||
std::array<char, 12> name;
|
||||
u64_le title_id;
|
||||
u32_le category;
|
||||
u8 priority;
|
||||
u8 core;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 flags;
|
||||
std::array<SectionHeader, 6> sections;
|
||||
std::array<u32, 0x20> capabilities;
|
||||
};
|
||||
static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
|
||||
|
||||
const std::array<SHA256Hash, 0x10> source_hashes{
|
||||
"B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
|
||||
"7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
|
||||
@ -170,65 +142,6 @@ const std::array<SHA256Hash, 0x20> master_key_hashes{
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
|
||||
};
|
||||
|
||||
static std::vector<u8> DecompressBLZ(const std::vector<u8>& in) {
|
||||
const auto data_size = in.size() - 0xC;
|
||||
|
||||
u32 compressed_size{};
|
||||
u32 init_index{};
|
||||
u32 additional_size{};
|
||||
std::memcpy(&compressed_size, in.data() + data_size, sizeof(u32));
|
||||
std::memcpy(&init_index, in.data() + data_size + 0x4, sizeof(u32));
|
||||
std::memcpy(&additional_size, in.data() + data_size + 0x8, sizeof(u32));
|
||||
|
||||
std::vector<u8> out(in.size() + additional_size);
|
||||
|
||||
if (compressed_size == in.size())
|
||||
std::memcpy(out.data(), in.data() + in.size() - compressed_size, compressed_size);
|
||||
else
|
||||
std::memcpy(out.data(), in.data(), compressed_size);
|
||||
|
||||
auto index = in.size() - init_index;
|
||||
auto out_index = out.size();
|
||||
|
||||
while (out_index > 0) {
|
||||
--index;
|
||||
auto control = in[index];
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
if ((control & 0x80) > 0) {
|
||||
ASSERT(index >= 2);
|
||||
index -= 2;
|
||||
u64 segment_offset = in[index] | in[index + 1] << 8;
|
||||
u64 segment_size = ((segment_offset >> 12) & 0xF) + 3;
|
||||
segment_offset &= 0xFFF;
|
||||
segment_offset += 3;
|
||||
|
||||
if (out_index < segment_size)
|
||||
segment_size = out_index;
|
||||
|
||||
ASSERT(out_index >= segment_size);
|
||||
|
||||
out_index -= segment_size;
|
||||
|
||||
for (size_t j = 0; j < segment_size; ++j) {
|
||||
ASSERT(out_index + j + segment_offset < out.size());
|
||||
out[out_index + j] = out[out_index + j + segment_offset];
|
||||
}
|
||||
} else {
|
||||
ASSERT(out_index >= 1);
|
||||
--out_index;
|
||||
--index;
|
||||
out[out_index] = in[index];
|
||||
}
|
||||
|
||||
control <<= 1;
|
||||
if (out_index == 0)
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static u8 CalculateMaxKeyblobSourceHash() {
|
||||
for (s8 i = 0x1F; i >= 0; --i) {
|
||||
if (keyblob_source_hashes[i] != SHA256Hash{})
|
||||
@ -478,37 +391,22 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
|
||||
cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
|
||||
cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
|
||||
|
||||
INIHeader ini;
|
||||
std::memcpy(&ini, c.data(), sizeof(INIHeader));
|
||||
if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
|
||||
const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c);
|
||||
const FileSys::INI ini{ini_file};
|
||||
if (ini.GetStatus() != Loader::ResultStatus::Success)
|
||||
return;
|
||||
|
||||
u64 offset = sizeof(INIHeader);
|
||||
for (size_t i = 0; i < ini.process_count; ++i) {
|
||||
KIPHeader kip;
|
||||
std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
|
||||
if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
|
||||
for (const auto& kip : ini.GetKIPs()) {
|
||||
if (kip.GetStatus() != Loader::ResultStatus::Success)
|
||||
return;
|
||||
|
||||
const auto name =
|
||||
Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
|
||||
|
||||
if (name != "FS" && name != "spl") {
|
||||
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
|
||||
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
|
||||
if (kip.GetName() != "FS" && kip.GetName() != "spl") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const u64 initial_offset = sizeof(KIPHeader) + offset;
|
||||
const auto text_begin = c.cbegin() + initial_offset;
|
||||
const auto text_end = text_begin + kip.sections[0].size_compressed;
|
||||
const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
|
||||
|
||||
const auto rodata_end = text_end + kip.sections[1].size_compressed;
|
||||
const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
|
||||
|
||||
const auto data_end = rodata_end + kip.sections[2].size_compressed;
|
||||
const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
|
||||
const auto& text = kip.GetTextSection();
|
||||
const auto& rodata = kip.GetRODataSection();
|
||||
const auto& data = kip.GetDataSection();
|
||||
|
||||
std::vector<u8> out;
|
||||
out.reserve(text.size() + rodata.size() + data.size());
|
||||
@ -516,12 +414,9 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
|
||||
out.insert(out.end(), rodata.begin(), rodata.end());
|
||||
out.insert(out.end(), data.begin(), data.end());
|
||||
|
||||
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
|
||||
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
|
||||
|
||||
if (name == "FS")
|
||||
if (kip.GetName() == "FS")
|
||||
package2_fs[static_cast<size_t>(type)] = std::move(out);
|
||||
else if (name == "spl")
|
||||
else if (kip.GetName() == "spl")
|
||||
package2_spl[static_cast<size_t>(type)] = std::move(out);
|
||||
}
|
||||
}
|
||||
|
228
src/core/file_sys/kernel_executable.cpp
Normal file
228
src/core/file_sys/kernel_executable.cpp
Normal file
@ -0,0 +1,228 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/kernel_executable.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr u32 INI_MAX_KIPS = 0x50;
|
||||
|
||||
namespace {
|
||||
bool DecompressBLZ(std::vector<u8>& data) {
|
||||
if (data.size() < 0xC)
|
||||
return {};
|
||||
|
||||
const auto data_size = data.size() - 0xC;
|
||||
|
||||
u32 compressed_size{};
|
||||
u32 init_index{};
|
||||
u32 additional_size{};
|
||||
std::memcpy(&compressed_size, data.data() + data_size, sizeof(u32));
|
||||
std::memcpy(&init_index, data.data() + data_size + 0x4, sizeof(u32));
|
||||
std::memcpy(&additional_size, data.data() + data_size + 0x8, sizeof(u32));
|
||||
|
||||
const auto start_offset = data.size() - compressed_size;
|
||||
data.resize(compressed_size + additional_size + start_offset);
|
||||
|
||||
std::size_t index = compressed_size - init_index;
|
||||
std::size_t out_index = compressed_size + additional_size;
|
||||
|
||||
while (out_index > 0) {
|
||||
--index;
|
||||
auto control = data[index + start_offset];
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
if (((control << i) & 0x80) > 0) {
|
||||
if (index < 2) {
|
||||
return false;
|
||||
}
|
||||
index -= 2;
|
||||
std::size_t segment_offset =
|
||||
data[index + start_offset] | data[index + start_offset + 1] << 8;
|
||||
std::size_t segment_size = ((segment_offset >> 12) & 0xF) + 3;
|
||||
segment_offset &= 0xFFF;
|
||||
segment_offset += 3;
|
||||
|
||||
if (out_index < segment_size)
|
||||
segment_size = out_index;
|
||||
|
||||
if (out_index < segment_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out_index -= segment_size;
|
||||
|
||||
for (size_t j = 0; j < segment_size; ++j) {
|
||||
if (out_index + j + segment_offset + start_offset >= data.size()) {
|
||||
return false;
|
||||
}
|
||||
data[out_index + j + start_offset] =
|
||||
data[out_index + j + segment_offset + start_offset];
|
||||
}
|
||||
} else {
|
||||
if (out_index < 1) {
|
||||
return false;
|
||||
}
|
||||
--out_index;
|
||||
--index;
|
||||
data[out_index + start_offset] = data[index + start_offset];
|
||||
}
|
||||
|
||||
if (out_index == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
KIP::KIP(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorNullFile;
|
||||
return;
|
||||
}
|
||||
|
||||
if (file->GetSize() < sizeof(KIPHeader) || file->ReadObject(&header) != sizeof(KIPHeader)) {
|
||||
status = Loader::ResultStatus::ErrorBadKIPHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.magic != Common::MakeMagic('K', 'I', 'P', '1')) {
|
||||
status = Loader::ResultStatus::ErrorBadKIPHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
u64 offset = sizeof(KIPHeader);
|
||||
for (std::size_t i = 0; i < header.sections.size(); ++i) {
|
||||
auto compressed = file->ReadBytes(header.sections[i].compressed_size, offset);
|
||||
offset += header.sections[i].compressed_size;
|
||||
|
||||
if (header.sections[i].compressed_size == 0 && header.sections[i].decompressed_size != 0) {
|
||||
decompressed_sections[i] = std::vector<u8>(header.sections[i].decompressed_size);
|
||||
} else if (header.sections[i].compressed_size == header.sections[i].decompressed_size) {
|
||||
decompressed_sections[i] = std::move(compressed);
|
||||
} else {
|
||||
decompressed_sections[i] = compressed;
|
||||
if (!DecompressBLZ(decompressed_sections[i])) {
|
||||
status = Loader::ResultStatus::ErrorBLZDecompressionFailed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader::ResultStatus KIP::GetStatus() const {
|
||||
return status;
|
||||
}
|
||||
|
||||
std::string KIP::GetName() const {
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(header.name.data(), header.name.size());
|
||||
}
|
||||
|
||||
u64 KIP::GetTitleID() const {
|
||||
return header.title_id;
|
||||
}
|
||||
|
||||
std::vector<u8> KIP::GetSectionDecompressed(u8 index) const {
|
||||
return decompressed_sections[index];
|
||||
}
|
||||
|
||||
bool KIP::Is64Bit() const {
|
||||
return (header.flags & 0x8) != 0;
|
||||
}
|
||||
|
||||
bool KIP::Is39BitAddressSpace() const {
|
||||
return (header.flags & 0x10) != 0;
|
||||
}
|
||||
|
||||
bool KIP::IsService() const {
|
||||
return (header.flags & 0x20) != 0;
|
||||
}
|
||||
|
||||
std::vector<u32> KIP::GetKernelCapabilities() const {
|
||||
return std::vector<u32>(header.capabilities.begin(), header.capabilities.end());
|
||||
}
|
||||
|
||||
s32 KIP::GetMainThreadPriority() const {
|
||||
return header.main_thread_priority;
|
||||
}
|
||||
|
||||
u32 KIP::GetMainThreadStackSize() const {
|
||||
return header.sections[1].attribute;
|
||||
}
|
||||
|
||||
u32 KIP::GetMainThreadCpuCore() const {
|
||||
return header.default_core;
|
||||
}
|
||||
|
||||
const std::vector<u8>& KIP::GetTextSection() const {
|
||||
return decompressed_sections[0];
|
||||
}
|
||||
|
||||
const std::vector<u8>& KIP::GetRODataSection() const {
|
||||
return decompressed_sections[1];
|
||||
}
|
||||
|
||||
const std::vector<u8>& KIP::GetDataSection() const {
|
||||
return decompressed_sections[2];
|
||||
}
|
||||
|
||||
u32 KIP::GetTextOffset() const {
|
||||
return header.sections[0].offset;
|
||||
}
|
||||
|
||||
u32 KIP::GetRODataOffset() const {
|
||||
return header.sections[1].offset;
|
||||
}
|
||||
|
||||
u32 KIP::GetDataOffset() const {
|
||||
return header.sections[2].offset;
|
||||
}
|
||||
|
||||
u32 KIP::GetBSSSize() const {
|
||||
return header.sections[3].decompressed_size;
|
||||
}
|
||||
|
||||
u32 KIP::GetBSSOffset() const {
|
||||
return header.sections[3].offset;
|
||||
}
|
||||
|
||||
INI::INI(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
|
||||
if (file->GetSize() < sizeof(INIHeader) || file->ReadObject(&header) != sizeof(INIHeader)) {
|
||||
status = Loader::ResultStatus::ErrorBadINIHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.magic != Common::MakeMagic('I', 'N', 'I', '1')) {
|
||||
status = Loader::ResultStatus::ErrorBadINIHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.kip_count > INI_MAX_KIPS) {
|
||||
status = Loader::ResultStatus::ErrorINITooManyKIPs;
|
||||
return;
|
||||
}
|
||||
|
||||
u64 offset = sizeof(INIHeader);
|
||||
for (std::size_t i = 0; i < header.kip_count; ++i) {
|
||||
const auto kip_file =
|
||||
std::make_shared<OffsetVfsFile>(file, file->GetSize() - offset, offset);
|
||||
KIP kip(kip_file);
|
||||
if (kip.GetStatus() == Loader::ResultStatus::Success) {
|
||||
kips.push_back(std::move(kip));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader::ResultStatus INI::GetStatus() const {
|
||||
return status;
|
||||
}
|
||||
|
||||
const std::vector<KIP>& INI::GetKIPs() const {
|
||||
return kips;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
99
src/core/file_sys/kernel_executable.h
Normal file
99
src/core/file_sys/kernel_executable.h
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
struct KIPSectionHeader {
|
||||
u32_le offset;
|
||||
u32_le decompressed_size;
|
||||
u32_le compressed_size;
|
||||
u32_le attribute;
|
||||
};
|
||||
static_assert(sizeof(KIPSectionHeader) == 0x10, "KIPSectionHeader has incorrect size.");
|
||||
|
||||
struct KIPHeader {
|
||||
u32_le magic;
|
||||
std::array<char, 0xC> name;
|
||||
u64_le title_id;
|
||||
u32_le process_category;
|
||||
u8 main_thread_priority;
|
||||
u8 default_core;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 flags;
|
||||
std::array<KIPSectionHeader, 6> sections;
|
||||
std::array<u32, 0x20> capabilities;
|
||||
};
|
||||
static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
|
||||
|
||||
struct INIHeader {
|
||||
u32_le magic;
|
||||
u32_le size;
|
||||
u32_le kip_count;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
};
|
||||
static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
|
||||
|
||||
// Kernel Internal Process
|
||||
class KIP {
|
||||
public:
|
||||
explicit KIP(const VirtualFile& file);
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
|
||||
std::string GetName() const;
|
||||
u64 GetTitleID() const;
|
||||
std::vector<u8> GetSectionDecompressed(u8 index) const;
|
||||
|
||||
// Executable Flags
|
||||
bool Is64Bit() const;
|
||||
bool Is39BitAddressSpace() const;
|
||||
bool IsService() const;
|
||||
|
||||
std::vector<u32> GetKernelCapabilities() const;
|
||||
|
||||
s32 GetMainThreadPriority() const;
|
||||
u32 GetMainThreadStackSize() const;
|
||||
u32 GetMainThreadCpuCore() const;
|
||||
|
||||
const std::vector<u8>& GetTextSection() const;
|
||||
const std::vector<u8>& GetRODataSection() const;
|
||||
const std::vector<u8>& GetDataSection() const;
|
||||
|
||||
u32 GetTextOffset() const;
|
||||
u32 GetRODataOffset() const;
|
||||
u32 GetDataOffset() const;
|
||||
|
||||
u32 GetBSSSize() const;
|
||||
u32 GetBSSOffset() const;
|
||||
|
||||
private:
|
||||
Loader::ResultStatus status;
|
||||
|
||||
KIPHeader header{};
|
||||
std::array<std::vector<u8>, 6> decompressed_sections;
|
||||
};
|
||||
|
||||
class INI {
|
||||
public:
|
||||
explicit INI(const VirtualFile& file);
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
|
||||
const std::vector<KIP>& GetKIPs() const;
|
||||
|
||||
private:
|
||||
Loader::ResultStatus status;
|
||||
|
||||
INIHeader header{};
|
||||
std::vector<KIP> kips;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
@ -51,6 +51,21 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space,
|
||||
u8 main_thread_prio, u8 main_thread_core,
|
||||
u32 main_thread_stack_size, u64 title_id,
|
||||
u64 filesystem_permissions,
|
||||
KernelCapabilityDescriptors capabilities) {
|
||||
npdm_header.has_64_bit_instructions.Assign(is_64_bit);
|
||||
npdm_header.address_space_type.Assign(address_space);
|
||||
npdm_header.main_thread_priority = main_thread_prio;
|
||||
npdm_header.main_thread_cpu = main_thread_core;
|
||||
npdm_header.main_stack_size = main_thread_stack_size;
|
||||
aci_header.title_id = title_id;
|
||||
aci_file_access.permissions = filesystem_permissions;
|
||||
aci_kernel_capabilities = std ::move(capabilities);
|
||||
}
|
||||
|
||||
bool ProgramMetadata::Is64BitProgram() const {
|
||||
return npdm_header.has_64_bit_instructions;
|
||||
}
|
||||
|
@ -46,6 +46,11 @@ public:
|
||||
|
||||
Loader::ResultStatus Load(VirtualFile file);
|
||||
|
||||
// Load from parameters instead of NPDM file, used for KIP
|
||||
void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, u8 main_thread_prio,
|
||||
u8 main_thread_core, u32 main_thread_stack_size, u64 title_id,
|
||||
u64 filesystem_permissions, KernelCapabilityDescriptors capabilities);
|
||||
|
||||
bool Is64BitProgram() const;
|
||||
ProgramAddressSpaceType GetAddressSpaceType() const;
|
||||
u8 GetMainThreadPriority() const;
|
||||
|
102
src/core/loader/kip.cpp
Normal file
102
src/core/loader/kip.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/file_sys/kernel_executable.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/loader/kip.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
namespace {
|
||||
constexpr u32 PageAlignSize(u32 size) {
|
||||
return (size + Memory::PAGE_MASK) & ~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) {
|
||||
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.VMManager().GetCodeRegionBaseAddress();
|
||||
Kernel::CodeSet codeset;
|
||||
std::vector<u8> 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);
|
||||
program_image.insert(program_image.end(), data.begin(), data.end());
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
GDBStub::RegisterModule(kip->GetName(), base_address, base_address + program_image.size());
|
||||
|
||||
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
|
35
src/core/loader/kip.h
Normal file
35
src/core/loader/kip.h
Normal file
@ -0,0 +1,35 @@
|
||||
// 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 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 std::shared_ptr<VfsFile> 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) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::KIP> kip;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
@ -11,6 +11,7 @@
|
||||
#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"
|
||||
@ -36,6 +37,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
|
||||
CHECK_TYPE(XCI)
|
||||
CHECK_TYPE(NAX)
|
||||
CHECK_TYPE(NSP)
|
||||
CHECK_TYPE(KIP)
|
||||
|
||||
#undef CHECK_TYPE
|
||||
|
||||
@ -63,6 +65,8 @@ FileType GuessFromFilename(const std::string& name) {
|
||||
return FileType::XCI;
|
||||
if (extension == "nsp")
|
||||
return FileType::NSP;
|
||||
if (extension == "kip")
|
||||
return FileType::KIP;
|
||||
|
||||
return FileType::Unknown;
|
||||
}
|
||||
@ -83,6 +87,8 @@ std::string GetFileTypeString(FileType type) {
|
||||
return "NAX";
|
||||
case FileType::NSP:
|
||||
return "NSP";
|
||||
case FileType::KIP:
|
||||
return "KIP";
|
||||
case FileType::DeconstructedRomDirectory:
|
||||
return "Directory";
|
||||
case FileType::Error:
|
||||
@ -93,7 +99,7 @@ std::string GetFileTypeString(FileType type) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
constexpr std::array<const char*, 62> RESULT_MESSAGES{
|
||||
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.",
|
||||
@ -156,6 +162,10 @@ constexpr std::array<const char*, 62> RESULT_MESSAGES{
|
||||
"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) {
|
||||
@ -205,6 +215,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
|
||||
case FileType::NSP:
|
||||
return std::make_unique<AppLoader_NSP>(std::move(file));
|
||||
|
||||
// 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));
|
||||
|
@ -37,6 +37,7 @@ enum class FileType {
|
||||
NSP,
|
||||
XCI,
|
||||
NAX,
|
||||
KIP,
|
||||
DeconstructedRomDirectory,
|
||||
};
|
||||
|
||||
@ -124,6 +125,10 @@ enum class ResultStatus : u16 {
|
||||
ErrorBadSubsectionBuckets,
|
||||
ErrorMissingBKTRBaseRomFS,
|
||||
ErrorNoPackedUpdate,
|
||||
ErrorBadKIPHeader,
|
||||
ErrorBLZDecompressionFailed,
|
||||
ErrorBadINIHeader,
|
||||
ErrorINITooManyKIPs,
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, ResultStatus status);
|
||||
|
@ -468,8 +468,7 @@ void GameList::LoadInterfaceLayout() {
|
||||
|
||||
const QStringList GameList::supported_file_extensions = {
|
||||
QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"),
|
||||
QStringLiteral("xci"), QStringLiteral("nsp"),
|
||||
};
|
||||
QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")};
|
||||
|
||||
void GameList::RefreshGameDirectory() {
|
||||
if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) {
|
||||
|
Loading…
Reference in New Issue
Block a user