diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2fb65c131..cdb3bf6ab 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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 diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index ed0775444..01a969be9 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.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 name; - u64_le title_id; - u32_le category; - u8 priority; - u8 core; - INSERT_PADDING_BYTES(1); - u8 flags; - std::array sections; - std::array capabilities; -}; -static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size."); - const std::array source_hashes{ "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source @@ -170,65 +142,6 @@ const std::array master_key_hashes{ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F }; -static std::vector DecompressBLZ(const std::vector& 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 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& 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(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 text = DecompressBLZ({text_begin, text_end}); - - const auto rodata_end = text_end + kip.sections[1].size_compressed; - const std::vector rodata = DecompressBLZ({text_end, rodata_end}); - - const auto data_end = rodata_end + kip.sections[2].size_compressed; - const std::vector data = DecompressBLZ({rodata_end, data_end}); + const auto& text = kip.GetTextSection(); + const auto& rodata = kip.GetRODataSection(); + const auto& data = kip.GetDataSection(); std::vector out; out.reserve(text.size() + rodata.size() + data.size()); @@ -516,12 +414,9 @@ void PartitionDataManager::DecryptPackage2(const std::array& 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(type)] = std::move(out); - else if (name == "spl") + else if (kip.GetName() == "spl") package2_spl[static_cast(type)] = std::move(out); } } diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp new file mode 100644 index 000000000..371300684 --- /dev/null +++ b/src/core/file_sys/kernel_executable.cpp @@ -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& 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(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 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 KIP::GetKernelCapabilities() const { + return std::vector(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& KIP::GetTextSection() const { + return decompressed_sections[0]; +} + +const std::vector& KIP::GetRODataSection() const { + return decompressed_sections[1]; +} + +const std::vector& 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(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& INI::GetKIPs() const { + return kips; +} + +} // namespace FileSys diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h new file mode 100644 index 000000000..324a57384 --- /dev/null +++ b/src/core/file_sys/kernel_executable.h @@ -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 name; + u64_le title_id; + u32_le process_category; + u8 main_thread_priority; + u8 default_core; + INSERT_PADDING_BYTES(1); + u8 flags; + std::array sections; + std::array 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 GetSectionDecompressed(u8 index) const; + + // Executable Flags + bool Is64Bit() const; + bool Is39BitAddressSpace() const; + bool IsService() const; + + std::vector GetKernelCapabilities() const; + + s32 GetMainThreadPriority() const; + u32 GetMainThreadStackSize() const; + u32 GetMainThreadCpuCore() const; + + const std::vector& GetTextSection() const; + const std::vector& GetRODataSection() const; + const std::vector& 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, 6> decompressed_sections; +}; + +class INI { +public: + explicit INI(const VirtualFile& file); + + Loader::ResultStatus GetStatus() const; + + const std::vector& GetKIPs() const; + +private: + Loader::ResultStatus status; + + INIHeader header{}; + std::vector kips; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index d863253f8..eb76174c5 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -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; } diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 7de5b9cf9..43bf2820a 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -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; diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp new file mode 100644 index 000000000..70051c13a --- /dev/null +++ b/src/core/loader/kip.cpp @@ -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(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 program_image; + + const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment, + const std::vector& data, u32 offset) { + segment.addr = offset; + segment.offset = offset; + segment.size = PageAlignSize(static_cast(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 diff --git a/src/core/loader/kip.h b/src/core/loader/kip.h new file mode 100644 index 000000000..12ca40269 --- /dev/null +++ b/src/core/loader/kip.h @@ -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 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 kip; +}; + +} // namespace Loader diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index d8cc30959..59ca7091a 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -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 RESULT_MESSAGES{ +constexpr std::array 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 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 GetFileLoader(FileSys::VirtualFile file, FileT case FileType::NSP: return std::make_unique(std::move(file)); + // NX KIP (Kernel Internal Process) file format + case FileType::KIP: + return std::make_unique(std::move(file)); + // NX deconstructed ROM directory. case FileType::DeconstructedRomDirectory: return std::make_unique(std::move(file)); diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 8d3329202..227ecc704 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -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); diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 83d675773..1885587af 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -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) {