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:
bunnei 2019-06-21 14:28:18 -04:00 committed by GitHub
commit e2f7933b3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 522 additions and 121 deletions

View file

@ -45,6 +45,8 @@ add_library(core STATIC
file_sys/fsmitm_romfsbuild.h file_sys/fsmitm_romfsbuild.h
file_sys/ips_layer.cpp file_sys/ips_layer.cpp
file_sys/ips_layer.h file_sys/ips_layer.h
file_sys/kernel_executable.cpp
file_sys/kernel_executable.h
file_sys/mode.h file_sys/mode.h
file_sys/nca_metadata.cpp file_sys/nca_metadata.cpp
file_sys/nca_metadata.h file_sys/nca_metadata.h
@ -440,6 +442,8 @@ add_library(core STATIC
loader/deconstructed_rom_directory.h loader/deconstructed_rom_directory.h
loader/elf.cpp loader/elf.cpp
loader/elf.h loader/elf.h
loader/kip.cpp
loader/kip.h
loader/loader.cpp loader/loader.cpp
loader/loader.h loader/loader.h
loader/nax.cpp loader/nax.cpp

View file

@ -22,8 +22,10 @@
#include "core/crypto/key_manager.h" #include "core/crypto/key_manager.h"
#include "core/crypto/partition_data_manager.h" #include "core/crypto/partition_data_manager.h"
#include "core/crypto/xts_encryption_layer.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.h"
#include "core/file_sys/vfs_offset.h" #include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_vector.h"
using namespace Common; using namespace Common;
@ -45,36 +47,6 @@ struct Package2Header {
}; };
static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); 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{ const std::array<SHA256Hash, 0x10> source_hashes{
"B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
"7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
@ -170,65 +142,6 @@ const std::array<SHA256Hash, 0x20> master_key_hashes{
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F "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() { static u8 CalculateMaxKeyblobSourceHash() {
for (s8 i = 0x1F; i >= 0; --i) { for (s8 i = 0x1F; i >= 0; --i) {
if (keyblob_source_hashes[i] != SHA256Hash{}) 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.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
INIHeader ini; const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c);
std::memcpy(&ini, c.data(), sizeof(INIHeader)); const FileSys::INI ini{ini_file};
if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1')) if (ini.GetStatus() != Loader::ResultStatus::Success)
return; return;
u64 offset = sizeof(INIHeader); for (const auto& kip : ini.GetKIPs()) {
for (size_t i = 0; i < ini.process_count; ++i) { if (kip.GetStatus() != Loader::ResultStatus::Success)
KIPHeader kip;
std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
return; return;
const auto name = if (kip.GetName() != "FS" && kip.GetName() != "spl") {
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;
continue; continue;
} }
const u64 initial_offset = sizeof(KIPHeader) + offset; const auto& text = kip.GetTextSection();
const auto text_begin = c.cbegin() + initial_offset; const auto& rodata = kip.GetRODataSection();
const auto text_end = text_begin + kip.sections[0].size_compressed; const auto& data = kip.GetDataSection();
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});
std::vector<u8> out; std::vector<u8> out;
out.reserve(text.size() + rodata.size() + data.size()); 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(), rodata.begin(), rodata.end());
out.insert(out.end(), data.begin(), data.end()); out.insert(out.end(), data.begin(), data.end());
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + if (kip.GetName() == "FS")
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
if (name == "FS")
package2_fs[static_cast<size_t>(type)] = std::move(out); 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); package2_spl[static_cast<size_t>(type)] = std::move(out);
} }
} }

View 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

View 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

View file

@ -51,6 +51,21 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
return Loader::ResultStatus::Success; 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 { bool ProgramMetadata::Is64BitProgram() const {
return npdm_header.has_64_bit_instructions; return npdm_header.has_64_bit_instructions;
} }

View file

@ -46,6 +46,11 @@ public:
Loader::ResultStatus Load(VirtualFile file); 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; bool Is64BitProgram() const;
ProgramAddressSpaceType GetAddressSpaceType() const; ProgramAddressSpaceType GetAddressSpaceType() const;
u8 GetMainThreadPriority() const; u8 GetMainThreadPriority() const;

102
src/core/loader/kip.cpp Normal file
View 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
View 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

View file

@ -11,6 +11,7 @@
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/loader/deconstructed_rom_directory.h" #include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/elf.h" #include "core/loader/elf.h"
#include "core/loader/kip.h"
#include "core/loader/nax.h" #include "core/loader/nax.h"
#include "core/loader/nca.h" #include "core/loader/nca.h"
#include "core/loader/nro.h" #include "core/loader/nro.h"
@ -36,6 +37,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
CHECK_TYPE(XCI) CHECK_TYPE(XCI)
CHECK_TYPE(NAX) CHECK_TYPE(NAX)
CHECK_TYPE(NSP) CHECK_TYPE(NSP)
CHECK_TYPE(KIP)
#undef CHECK_TYPE #undef CHECK_TYPE
@ -63,6 +65,8 @@ FileType GuessFromFilename(const std::string& name) {
return FileType::XCI; return FileType::XCI;
if (extension == "nsp") if (extension == "nsp")
return FileType::NSP; return FileType::NSP;
if (extension == "kip")
return FileType::KIP;
return FileType::Unknown; return FileType::Unknown;
} }
@ -83,6 +87,8 @@ std::string GetFileTypeString(FileType type) {
return "NAX"; return "NAX";
case FileType::NSP: case FileType::NSP:
return "NSP"; return "NSP";
case FileType::KIP:
return "KIP";
case FileType::DeconstructedRomDirectory: case FileType::DeconstructedRomDirectory:
return "Directory"; return "Directory";
case FileType::Error: case FileType::Error:
@ -93,7 +99,7 @@ std::string GetFileTypeString(FileType type) {
return "unknown"; return "unknown";
} }
constexpr std::array<const char*, 62> RESULT_MESSAGES{ constexpr std::array<const char*, 66> RESULT_MESSAGES{
"The operation completed successfully.", "The operation completed successfully.",
"The loader requested to load is already loaded.", "The loader requested to load is already loaded.",
"The operation is not implemented.", "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 has a bad Subsection bucket.",
"The BKTR-type NCA is missing the base RomFS.", "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 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) { 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: case FileType::NSP:
return std::make_unique<AppLoader_NSP>(std::move(file)); 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. // NX deconstructed ROM directory.
case FileType::DeconstructedRomDirectory: case FileType::DeconstructedRomDirectory:
return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file)); return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));

View file

@ -37,6 +37,7 @@ enum class FileType {
NSP, NSP,
XCI, XCI,
NAX, NAX,
KIP,
DeconstructedRomDirectory, DeconstructedRomDirectory,
}; };
@ -124,6 +125,10 @@ enum class ResultStatus : u16 {
ErrorBadSubsectionBuckets, ErrorBadSubsectionBuckets,
ErrorMissingBKTRBaseRomFS, ErrorMissingBKTRBaseRomFS,
ErrorNoPackedUpdate, ErrorNoPackedUpdate,
ErrorBadKIPHeader,
ErrorBLZDecompressionFailed,
ErrorBadINIHeader,
ErrorINITooManyKIPs,
}; };
std::ostream& operator<<(std::ostream& os, ResultStatus status); std::ostream& operator<<(std::ostream& os, ResultStatus status);

View file

@ -468,8 +468,7 @@ void GameList::LoadInterfaceLayout() {
const QStringList GameList::supported_file_extensions = { const QStringList GameList::supported_file_extensions = {
QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"), QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"),
QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")};
};
void GameList::RefreshGameDirectory() { void GameList::RefreshGameDirectory() {
if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) {