From 421c3e831ac76dbcd9ab0e1c3302572b5908caef Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 5 Jun 2019 00:18:25 -0400
Subject: [PATCH 1/8] file_sys: Add classes to parse KIP1 and INI1 files

---
 src/core/CMakeLists.txt                 |   2 +
 src/core/file_sys/kernel_executable.cpp | 229 ++++++++++++++++++++++++
 src/core/file_sys/kernel_executable.h   |  99 ++++++++++
 3 files changed, 330 insertions(+)
 create mode 100644 src/core/file_sys/kernel_executable.cpp
 create mode 100644 src/core/file_sys/kernel_executable.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index a56e526a6f..623a44e0c5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -43,6 +43,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
diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp
new file mode 100644
index 0000000000..0ddb9a60b7
--- /dev/null
+++ b/src/core/file_sys/kernel_executable.cpp
@@ -0,0 +1,229 @@
+// 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 & 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];
+            }
+
+            control <<= 1;
+            if (out_index == 0)
+                return true;
+        }
+    }
+
+    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;
+}
+
+bool KIP::Is39BitAddressSpace() const {
+    return header.flags & 0x10;
+}
+
+bool KIP::IsService() const {
+    return header.flags & 0x20;
+}
+
+std::vector<u32> 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<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
diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h
new file mode 100644
index 0000000000..324a573846
--- /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<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

From 0f3709682094c0172bb304f1f238d15535a07826 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 5 Jun 2019 00:19:29 -0400
Subject: [PATCH 2/8] partition_data_manager: Remove KIP processing and use
 FileSys Previously, this TU contained the necessary headers to parse KIP/INI
 but now it should just use the FileSys class.

---
 src/core/crypto/partition_data_manager.cpp | 131 ++-------------------
 1 file changed, 13 insertions(+), 118 deletions(-)

diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index ed07754447..01a969be9f 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<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);
     }
 }

From b550a01f743c6324ce3174093d1314eaf2c8ef54 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 5 Jun 2019 00:20:26 -0400
Subject: [PATCH 3/8] program_metadata: Add function to load meta from raw
 parameters Needed for KIP loading as KIPs do not have an NPDM but do have the
 essential parts of the data within.

---
 src/core/file_sys/program_metadata.cpp | 15 +++++++++++++++
 src/core/file_sys/program_metadata.h   |  5 +++++
 2 files changed, 20 insertions(+)

diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index d863253f88..eb76174c5b 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 7de5b9cf93..43bf2820a9 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;

From c3548967b1fbc818a495dda72ef135b71b44e002 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 5 Jun 2019 00:21:17 -0400
Subject: [PATCH 4/8] loader: Add AppLoader_KIP for KIP files

---
 src/core/CMakeLists.txt |  2 +
 src/core/loader/kip.cpp | 98 +++++++++++++++++++++++++++++++++++++++++
 src/core/loader/kip.h   | 35 +++++++++++++++
 3 files changed, 135 insertions(+)
 create mode 100644 src/core/loader/kip.cpp
 create mode 100644 src/core/loader/kip.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 623a44e0c5..60caf288d2 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -437,6 +437,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/loader/kip.cpp b/src/core/loader/kip.cpp
new file mode 100644
index 0000000000..2efd14f04f
--- /dev/null
+++ b/src/core/loader/kip.cpp
@@ -0,0 +1,98 @@
+// 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 address_space =
+        kip->Is64Bit() ? (kip->Is39BitAddressSpace() ? FileSys::ProgramAddressSpaceType::Is39Bit
+                                                     : FileSys::ProgramAddressSpaceType::Is36Bit)
+                       : FileSys::ProgramAddressSpaceType::Is32Bit;
+
+    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
diff --git a/src/core/loader/kip.h b/src/core/loader/kip.h
new file mode 100644
index 0000000000..12ca40269b
--- /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<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

From a76bd4926883e0b0d5649b04779525582708331d Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 5 Jun 2019 00:21:44 -0400
Subject: [PATCH 5/8] loader: Add KIP and INI file parser-specific errors

---
 src/core/loader/loader.cpp | 6 +++++-
 src/core/loader/loader.h   | 4 ++++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index d8cc309595..8a783ae2f2 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -93,7 +93,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 +156,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) {
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index f7846db52c..fbca730a23 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -124,6 +124,10 @@ enum class ResultStatus : u16 {
     ErrorBadSubsectionBuckets,
     ErrorMissingBKTRBaseRomFS,
     ErrorNoPackedUpdate,
+    ErrorBadKIPHeader,
+    ErrorBLZDecompressionFailed,
+    ErrorBadINIHeader,
+    ErrorINITooManyKIPs,
 };
 
 std::ostream& operator<<(std::ostream& os, ResultStatus status);

From ce9f7ac4f2023b81e5d97a06c0fb17d252002697 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 5 Jun 2019 00:22:07 -0400
Subject: [PATCH 6/8] loader: Add recognition for KIP file type

---
 src/core/loader/loader.cpp | 10 ++++++++++
 src/core/loader/loader.h   |  1 +
 2 files changed, 11 insertions(+)

diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 8a783ae2f2..59ca7091a9 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:
@@ -209,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));
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index fbca730a23..4068a60f8e 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,
 };
 

From c417b4fe28861062e799ddf5d010e1c4a1abe01e Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 5 Jun 2019 00:33:00 -0400
Subject: [PATCH 7/8] game_list: Accept *.kip as a file extension of
 executables

---
 src/core/file_sys/kernel_executable.cpp | 2 +-
 src/yuzu/game_list.cpp                  | 3 +--
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp
index 0ddb9a60b7..45cbde4c92 100644
--- a/src/core/file_sys/kernel_executable.cpp
+++ b/src/core/file_sys/kernel_executable.cpp
@@ -144,7 +144,7 @@ bool KIP::IsService() const {
 }
 
 std::vector<u32> KIP::GetKernelCapabilities() const {
-    return std::vector(header.capabilities.begin(), header.capabilities.end());
+    return std::vector<u32>(header.capabilities.begin(), header.capabilities.end());
 }
 
 s32 KIP::GetMainThreadPriority() const {
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 83d6757733..1885587afd 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) {

From 9db119f8a2e5a4d877f00b9efb40e4a109c95ef7 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Thu, 6 Jun 2019 19:20:15 -0400
Subject: [PATCH 8/8] kernel_executable: Optimize BLZ decompression

---
 src/core/file_sys/kernel_executable.cpp | 11 +++++------
 src/core/loader/kip.cpp                 | 12 ++++++++----
 2 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp
index 45cbde4c92..371300684a 100644
--- a/src/core/file_sys/kernel_executable.cpp
+++ b/src/core/file_sys/kernel_executable.cpp
@@ -34,7 +34,7 @@ bool DecompressBLZ(std::vector<u8>& data) {
         --index;
         auto control = data[index + start_offset];
         for (size_t i = 0; i < 8; ++i) {
-            if ((control & 0x80) > 0) {
+            if (((control << i) & 0x80) > 0) {
                 if (index < 2) {
                     return false;
                 }
@@ -70,9 +70,8 @@ bool DecompressBLZ(std::vector<u8>& data) {
                 data[out_index + start_offset] = data[index + start_offset];
             }
 
-            control <<= 1;
             if (out_index == 0)
-                return true;
+                break;
         }
     }
 
@@ -132,15 +131,15 @@ std::vector<u8> KIP::GetSectionDecompressed(u8 index) const {
 }
 
 bool KIP::Is64Bit() const {
-    return header.flags & 0x8;
+    return (header.flags & 0x8) != 0;
 }
 
 bool KIP::Is39BitAddressSpace() const {
-    return header.flags & 0x10;
+    return (header.flags & 0x10) != 0;
 }
 
 bool KIP::IsService() const {
-    return header.flags & 0x20;
+    return (header.flags & 0x20) != 0;
 }
 
 std::vector<u32> KIP::GetKernelCapabilities() const {
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 2efd14f04f..70051c13ae 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -53,10 +53,14 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) {
         return {kip->GetStatus(), {}};
     }
 
-    const auto address_space =
-        kip->Is64Bit() ? (kip->Is39BitAddressSpace() ? FileSys::ProgramAddressSpaceType::Is39Bit
-                                                     : FileSys::ProgramAddressSpaceType::Is36Bit)
-                       : FileSys::ProgramAddressSpaceType::Is32Bit;
+    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(),