summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2019-06-21 14:28:18 -0400
committerGravatar GitHub2019-06-21 14:28:18 -0400
commite2f7933b3f104e7e198d283f39a1b45956da4d3c (patch)
treeb75ed405df5fc5d40f0ef3d04b221947d1c53672 /src
parentMerge pull request #2482 from DarkLordZach/prepo (diff)
parentkernel_executable: Optimize BLZ decompression (diff)
downloadyuzu-e2f7933b3f104e7e198d283f39a1b45956da4d3c.tar.gz
yuzu-e2f7933b3f104e7e198d283f39a1b45956da4d3c.tar.xz
yuzu-e2f7933b3f104e7e198d283f39a1b45956da4d3c.zip
Merge pull request #2546 from DarkLordZach/kips
loader, file_sys: Add support for parsing and loading KIP (Kernel Internal Process) files
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/crypto/partition_data_manager.cpp131
-rw-r--r--src/core/file_sys/kernel_executable.cpp228
-rw-r--r--src/core/file_sys/kernel_executable.h99
-rw-r--r--src/core/file_sys/program_metadata.cpp15
-rw-r--r--src/core/file_sys/program_metadata.h5
-rw-r--r--src/core/loader/kip.cpp102
-rw-r--r--src/core/loader/kip.h35
-rw-r--r--src/core/loader/loader.cpp16
-rw-r--r--src/core/loader/loader.h5
-rw-r--r--src/yuzu/game_list.cpp3
11 files changed, 522 insertions, 121 deletions
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
45 file_sys/fsmitm_romfsbuild.h 45 file_sys/fsmitm_romfsbuild.h
46 file_sys/ips_layer.cpp 46 file_sys/ips_layer.cpp
47 file_sys/ips_layer.h 47 file_sys/ips_layer.h
48 file_sys/kernel_executable.cpp
49 file_sys/kernel_executable.h
48 file_sys/mode.h 50 file_sys/mode.h
49 file_sys/nca_metadata.cpp 51 file_sys/nca_metadata.cpp
50 file_sys/nca_metadata.h 52 file_sys/nca_metadata.h
@@ -440,6 +442,8 @@ add_library(core STATIC
440 loader/deconstructed_rom_directory.h 442 loader/deconstructed_rom_directory.h
441 loader/elf.cpp 443 loader/elf.cpp
442 loader/elf.h 444 loader/elf.h
445 loader/kip.cpp
446 loader/kip.h
443 loader/loader.cpp 447 loader/loader.cpp
444 loader/loader.h 448 loader/loader.h
445 loader/nax.cpp 449 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 @@
22#include "core/crypto/key_manager.h" 22#include "core/crypto/key_manager.h"
23#include "core/crypto/partition_data_manager.h" 23#include "core/crypto/partition_data_manager.h"
24#include "core/crypto/xts_encryption_layer.h" 24#include "core/crypto/xts_encryption_layer.h"
25#include "core/file_sys/kernel_executable.h"
25#include "core/file_sys/vfs.h" 26#include "core/file_sys/vfs.h"
26#include "core/file_sys/vfs_offset.h" 27#include "core/file_sys/vfs_offset.h"
28#include "core/file_sys/vfs_vector.h"
27 29
28using namespace Common; 30using namespace Common;
29 31
@@ -45,36 +47,6 @@ struct Package2Header {
45}; 47};
46static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); 48static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
47 49
48struct INIHeader {
49 u32_le magic;
50 u32_le size;
51 u32_le process_count;
52 INSERT_PADDING_BYTES(4);
53};
54static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
55
56struct SectionHeader {
57 u32_le offset;
58 u32_le size_decompressed;
59 u32_le size_compressed;
60 u32_le attribute;
61};
62static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size.");
63
64struct KIPHeader {
65 u32_le magic;
66 std::array<char, 12> name;
67 u64_le title_id;
68 u32_le category;
69 u8 priority;
70 u8 core;
71 INSERT_PADDING_BYTES(1);
72 u8 flags;
73 std::array<SectionHeader, 6> sections;
74 std::array<u32, 0x20> capabilities;
75};
76static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
77
78const std::array<SHA256Hash, 0x10> source_hashes{ 50const std::array<SHA256Hash, 0x10> source_hashes{
79 "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source 51 "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
80 "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source 52 "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
@@ -170,65 +142,6 @@ const std::array<SHA256Hash, 0x20> master_key_hashes{
170 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F 142 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
171}; 143};
172 144
173static std::vector<u8> DecompressBLZ(const std::vector<u8>& in) {
174 const auto data_size = in.size() - 0xC;
175
176 u32 compressed_size{};
177 u32 init_index{};
178 u32 additional_size{};
179 std::memcpy(&compressed_size, in.data() + data_size, sizeof(u32));
180 std::memcpy(&init_index, in.data() + data_size + 0x4, sizeof(u32));
181 std::memcpy(&additional_size, in.data() + data_size + 0x8, sizeof(u32));
182
183 std::vector<u8> out(in.size() + additional_size);
184
185 if (compressed_size == in.size())
186 std::memcpy(out.data(), in.data() + in.size() - compressed_size, compressed_size);
187 else
188 std::memcpy(out.data(), in.data(), compressed_size);
189
190 auto index = in.size() - init_index;
191 auto out_index = out.size();
192
193 while (out_index > 0) {
194 --index;
195 auto control = in[index];
196 for (size_t i = 0; i < 8; ++i) {
197 if ((control & 0x80) > 0) {
198 ASSERT(index >= 2);
199 index -= 2;
200 u64 segment_offset = in[index] | in[index + 1] << 8;
201 u64 segment_size = ((segment_offset >> 12) & 0xF) + 3;
202 segment_offset &= 0xFFF;
203 segment_offset += 3;
204
205 if (out_index < segment_size)
206 segment_size = out_index;
207
208 ASSERT(out_index >= segment_size);
209
210 out_index -= segment_size;
211
212 for (size_t j = 0; j < segment_size; ++j) {
213 ASSERT(out_index + j + segment_offset < out.size());
214 out[out_index + j] = out[out_index + j + segment_offset];
215 }
216 } else {
217 ASSERT(out_index >= 1);
218 --out_index;
219 --index;
220 out[out_index] = in[index];
221 }
222
223 control <<= 1;
224 if (out_index == 0)
225 return out;
226 }
227 }
228
229 return out;
230}
231
232static u8 CalculateMaxKeyblobSourceHash() { 145static u8 CalculateMaxKeyblobSourceHash() {
233 for (s8 i = 0x1F; i >= 0; --i) { 146 for (s8 i = 0x1F; i >= 0; --i) {
234 if (keyblob_source_hashes[i] != SHA256Hash{}) 147 if (keyblob_source_hashes[i] != SHA256Hash{})
@@ -478,37 +391,22 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
478 cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()}); 391 cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
479 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); 392 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
480 393
481 INIHeader ini; 394 const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c);
482 std::memcpy(&ini, c.data(), sizeof(INIHeader)); 395 const FileSys::INI ini{ini_file};
483 if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1')) 396 if (ini.GetStatus() != Loader::ResultStatus::Success)
484 return; 397 return;
485 398
486 u64 offset = sizeof(INIHeader); 399 for (const auto& kip : ini.GetKIPs()) {
487 for (size_t i = 0; i < ini.process_count; ++i) { 400 if (kip.GetStatus() != Loader::ResultStatus::Success)
488 KIPHeader kip;
489 std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
490 if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
491 return; 401 return;
492 402
493 const auto name = 403 if (kip.GetName() != "FS" && kip.GetName() != "spl") {
494 Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
495
496 if (name != "FS" && name != "spl") {
497 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
498 kip.sections[1].size_compressed + kip.sections[2].size_compressed;
499 continue; 404 continue;
500 } 405 }
501 406
502 const u64 initial_offset = sizeof(KIPHeader) + offset; 407 const auto& text = kip.GetTextSection();
503 const auto text_begin = c.cbegin() + initial_offset; 408 const auto& rodata = kip.GetRODataSection();
504 const auto text_end = text_begin + kip.sections[0].size_compressed; 409 const auto& data = kip.GetDataSection();
505 const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
506
507 const auto rodata_end = text_end + kip.sections[1].size_compressed;
508 const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
509
510 const auto data_end = rodata_end + kip.sections[2].size_compressed;
511 const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
512 410
513 std::vector<u8> out; 411 std::vector<u8> out;
514 out.reserve(text.size() + rodata.size() + data.size()); 412 out.reserve(text.size() + rodata.size() + data.size());
@@ -516,12 +414,9 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
516 out.insert(out.end(), rodata.begin(), rodata.end()); 414 out.insert(out.end(), rodata.begin(), rodata.end());
517 out.insert(out.end(), data.begin(), data.end()); 415 out.insert(out.end(), data.begin(), data.end());
518 416
519 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + 417 if (kip.GetName() == "FS")
520 kip.sections[1].size_compressed + kip.sections[2].size_compressed;
521
522 if (name == "FS")
523 package2_fs[static_cast<size_t>(type)] = std::move(out); 418 package2_fs[static_cast<size_t>(type)] = std::move(out);
524 else if (name == "spl") 419 else if (kip.GetName() == "spl")
525 package2_spl[static_cast<size_t>(type)] = std::move(out); 420 package2_spl[static_cast<size_t>(type)] = std::move(out);
526 } 421 }
527} 422}
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 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/string_util.h"
6#include "core/file_sys/kernel_executable.h"
7#include "core/file_sys/vfs_offset.h"
8
9namespace FileSys {
10
11constexpr u32 INI_MAX_KIPS = 0x50;
12
13namespace {
14bool DecompressBLZ(std::vector<u8>& data) {
15 if (data.size() < 0xC)
16 return {};
17
18 const auto data_size = data.size() - 0xC;
19
20 u32 compressed_size{};
21 u32 init_index{};
22 u32 additional_size{};
23 std::memcpy(&compressed_size, data.data() + data_size, sizeof(u32));
24 std::memcpy(&init_index, data.data() + data_size + 0x4, sizeof(u32));
25 std::memcpy(&additional_size, data.data() + data_size + 0x8, sizeof(u32));
26
27 const auto start_offset = data.size() - compressed_size;
28 data.resize(compressed_size + additional_size + start_offset);
29
30 std::size_t index = compressed_size - init_index;
31 std::size_t out_index = compressed_size + additional_size;
32
33 while (out_index > 0) {
34 --index;
35 auto control = data[index + start_offset];
36 for (size_t i = 0; i < 8; ++i) {
37 if (((control << i) & 0x80) > 0) {
38 if (index < 2) {
39 return false;
40 }
41 index -= 2;
42 std::size_t segment_offset =
43 data[index + start_offset] | data[index + start_offset + 1] << 8;
44 std::size_t segment_size = ((segment_offset >> 12) & 0xF) + 3;
45 segment_offset &= 0xFFF;
46 segment_offset += 3;
47
48 if (out_index < segment_size)
49 segment_size = out_index;
50
51 if (out_index < segment_size) {
52 return false;
53 }
54
55 out_index -= segment_size;
56
57 for (size_t j = 0; j < segment_size; ++j) {
58 if (out_index + j + segment_offset + start_offset >= data.size()) {
59 return false;
60 }
61 data[out_index + j + start_offset] =
62 data[out_index + j + segment_offset + start_offset];
63 }
64 } else {
65 if (out_index < 1) {
66 return false;
67 }
68 --out_index;
69 --index;
70 data[out_index + start_offset] = data[index + start_offset];
71 }
72
73 if (out_index == 0)
74 break;
75 }
76 }
77
78 return true;
79}
80} // Anonymous namespace
81
82KIP::KIP(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
83 if (file == nullptr) {
84 status = Loader::ResultStatus::ErrorNullFile;
85 return;
86 }
87
88 if (file->GetSize() < sizeof(KIPHeader) || file->ReadObject(&header) != sizeof(KIPHeader)) {
89 status = Loader::ResultStatus::ErrorBadKIPHeader;
90 return;
91 }
92
93 if (header.magic != Common::MakeMagic('K', 'I', 'P', '1')) {
94 status = Loader::ResultStatus::ErrorBadKIPHeader;
95 return;
96 }
97
98 u64 offset = sizeof(KIPHeader);
99 for (std::size_t i = 0; i < header.sections.size(); ++i) {
100 auto compressed = file->ReadBytes(header.sections[i].compressed_size, offset);
101 offset += header.sections[i].compressed_size;
102
103 if (header.sections[i].compressed_size == 0 && header.sections[i].decompressed_size != 0) {
104 decompressed_sections[i] = std::vector<u8>(header.sections[i].decompressed_size);
105 } else if (header.sections[i].compressed_size == header.sections[i].decompressed_size) {
106 decompressed_sections[i] = std::move(compressed);
107 } else {
108 decompressed_sections[i] = compressed;
109 if (!DecompressBLZ(decompressed_sections[i])) {
110 status = Loader::ResultStatus::ErrorBLZDecompressionFailed;
111 return;
112 }
113 }
114 }
115}
116
117Loader::ResultStatus KIP::GetStatus() const {
118 return status;
119}
120
121std::string KIP::GetName() const {
122 return Common::StringFromFixedZeroTerminatedBuffer(header.name.data(), header.name.size());
123}
124
125u64 KIP::GetTitleID() const {
126 return header.title_id;
127}
128
129std::vector<u8> KIP::GetSectionDecompressed(u8 index) const {
130 return decompressed_sections[index];
131}
132
133bool KIP::Is64Bit() const {
134 return (header.flags & 0x8) != 0;
135}
136
137bool KIP::Is39BitAddressSpace() const {
138 return (header.flags & 0x10) != 0;
139}
140
141bool KIP::IsService() const {
142 return (header.flags & 0x20) != 0;
143}
144
145std::vector<u32> KIP::GetKernelCapabilities() const {
146 return std::vector<u32>(header.capabilities.begin(), header.capabilities.end());
147}
148
149s32 KIP::GetMainThreadPriority() const {
150 return header.main_thread_priority;
151}
152
153u32 KIP::GetMainThreadStackSize() const {
154 return header.sections[1].attribute;
155}
156
157u32 KIP::GetMainThreadCpuCore() const {
158 return header.default_core;
159}
160
161const std::vector<u8>& KIP::GetTextSection() const {
162 return decompressed_sections[0];
163}
164
165const std::vector<u8>& KIP::GetRODataSection() const {
166 return decompressed_sections[1];
167}
168
169const std::vector<u8>& KIP::GetDataSection() const {
170 return decompressed_sections[2];
171}
172
173u32 KIP::GetTextOffset() const {
174 return header.sections[0].offset;
175}
176
177u32 KIP::GetRODataOffset() const {
178 return header.sections[1].offset;
179}
180
181u32 KIP::GetDataOffset() const {
182 return header.sections[2].offset;
183}
184
185u32 KIP::GetBSSSize() const {
186 return header.sections[3].decompressed_size;
187}
188
189u32 KIP::GetBSSOffset() const {
190 return header.sections[3].offset;
191}
192
193INI::INI(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
194 if (file->GetSize() < sizeof(INIHeader) || file->ReadObject(&header) != sizeof(INIHeader)) {
195 status = Loader::ResultStatus::ErrorBadINIHeader;
196 return;
197 }
198
199 if (header.magic != Common::MakeMagic('I', 'N', 'I', '1')) {
200 status = Loader::ResultStatus::ErrorBadINIHeader;
201 return;
202 }
203
204 if (header.kip_count > INI_MAX_KIPS) {
205 status = Loader::ResultStatus::ErrorINITooManyKIPs;
206 return;
207 }
208
209 u64 offset = sizeof(INIHeader);
210 for (std::size_t i = 0; i < header.kip_count; ++i) {
211 const auto kip_file =
212 std::make_shared<OffsetVfsFile>(file, file->GetSize() - offset, offset);
213 KIP kip(kip_file);
214 if (kip.GetStatus() == Loader::ResultStatus::Success) {
215 kips.push_back(std::move(kip));
216 }
217 }
218}
219
220Loader::ResultStatus INI::GetStatus() const {
221 return status;
222}
223
224const std::vector<KIP>& INI::GetKIPs() const {
225 return kips;
226}
227
228} // 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 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_funcs.h"
8#include "common/swap.h"
9#include "core/file_sys/vfs_types.h"
10#include "core/loader/loader.h"
11
12namespace FileSys {
13
14struct KIPSectionHeader {
15 u32_le offset;
16 u32_le decompressed_size;
17 u32_le compressed_size;
18 u32_le attribute;
19};
20static_assert(sizeof(KIPSectionHeader) == 0x10, "KIPSectionHeader has incorrect size.");
21
22struct KIPHeader {
23 u32_le magic;
24 std::array<char, 0xC> name;
25 u64_le title_id;
26 u32_le process_category;
27 u8 main_thread_priority;
28 u8 default_core;
29 INSERT_PADDING_BYTES(1);
30 u8 flags;
31 std::array<KIPSectionHeader, 6> sections;
32 std::array<u32, 0x20> capabilities;
33};
34static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
35
36struct INIHeader {
37 u32_le magic;
38 u32_le size;
39 u32_le kip_count;
40 INSERT_PADDING_BYTES(0x4);
41};
42static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
43
44// Kernel Internal Process
45class KIP {
46public:
47 explicit KIP(const VirtualFile& file);
48
49 Loader::ResultStatus GetStatus() const;
50
51 std::string GetName() const;
52 u64 GetTitleID() const;
53 std::vector<u8> GetSectionDecompressed(u8 index) const;
54
55 // Executable Flags
56 bool Is64Bit() const;
57 bool Is39BitAddressSpace() const;
58 bool IsService() const;
59
60 std::vector<u32> GetKernelCapabilities() const;
61
62 s32 GetMainThreadPriority() const;
63 u32 GetMainThreadStackSize() const;
64 u32 GetMainThreadCpuCore() const;
65
66 const std::vector<u8>& GetTextSection() const;
67 const std::vector<u8>& GetRODataSection() const;
68 const std::vector<u8>& GetDataSection() const;
69
70 u32 GetTextOffset() const;
71 u32 GetRODataOffset() const;
72 u32 GetDataOffset() const;
73
74 u32 GetBSSSize() const;
75 u32 GetBSSOffset() const;
76
77private:
78 Loader::ResultStatus status;
79
80 KIPHeader header{};
81 std::array<std::vector<u8>, 6> decompressed_sections;
82};
83
84class INI {
85public:
86 explicit INI(const VirtualFile& file);
87
88 Loader::ResultStatus GetStatus() const;
89
90 const std::vector<KIP>& GetKIPs() const;
91
92private:
93 Loader::ResultStatus status;
94
95 INIHeader header{};
96 std::vector<KIP> kips;
97};
98
99} // 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) {
51 return Loader::ResultStatus::Success; 51 return Loader::ResultStatus::Success;
52} 52}
53 53
54void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space,
55 u8 main_thread_prio, u8 main_thread_core,
56 u32 main_thread_stack_size, u64 title_id,
57 u64 filesystem_permissions,
58 KernelCapabilityDescriptors capabilities) {
59 npdm_header.has_64_bit_instructions.Assign(is_64_bit);
60 npdm_header.address_space_type.Assign(address_space);
61 npdm_header.main_thread_priority = main_thread_prio;
62 npdm_header.main_thread_cpu = main_thread_core;
63 npdm_header.main_stack_size = main_thread_stack_size;
64 aci_header.title_id = title_id;
65 aci_file_access.permissions = filesystem_permissions;
66 aci_kernel_capabilities = std ::move(capabilities);
67}
68
54bool ProgramMetadata::Is64BitProgram() const { 69bool ProgramMetadata::Is64BitProgram() const {
55 return npdm_header.has_64_bit_instructions; 70 return npdm_header.has_64_bit_instructions;
56} 71}
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:
46 46
47 Loader::ResultStatus Load(VirtualFile file); 47 Loader::ResultStatus Load(VirtualFile file);
48 48
49 // Load from parameters instead of NPDM file, used for KIP
50 void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, u8 main_thread_prio,
51 u8 main_thread_core, u32 main_thread_stack_size, u64 title_id,
52 u64 filesystem_permissions, KernelCapabilityDescriptors capabilities);
53
49 bool Is64BitProgram() const; 54 bool Is64BitProgram() const;
50 ProgramAddressSpaceType GetAddressSpaceType() const; 55 ProgramAddressSpaceType GetAddressSpaceType() const;
51 u8 GetMainThreadPriority() const; 56 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 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/file_sys/kernel_executable.h"
6#include "core/file_sys/program_metadata.h"
7#include "core/gdbstub/gdbstub.h"
8#include "core/hle/kernel/code_set.h"
9#include "core/hle/kernel/process.h"
10#include "core/loader/kip.h"
11
12namespace Loader {
13
14namespace {
15constexpr u32 PageAlignSize(u32 size) {
16 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
17}
18} // Anonymous namespace
19
20AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_)
21 : AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {}
22
23AppLoader_KIP::~AppLoader_KIP() = default;
24
25FileType AppLoader_KIP::IdentifyType(const FileSys::VirtualFile& file) {
26 u32_le magic{};
27 if (file->GetSize() < sizeof(u32) || file->ReadObject(&magic) != sizeof(u32)) {
28 return FileType::Error;
29 }
30
31 if (magic == Common::MakeMagic('K', 'I', 'P', '1')) {
32 return FileType::KIP;
33 }
34
35 return FileType::Error;
36}
37
38FileType AppLoader_KIP::GetFileType() const {
39 return (kip != nullptr && kip->GetStatus() == ResultStatus::Success) ? FileType::KIP
40 : FileType::Error;
41}
42
43AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) {
44 if (is_loaded) {
45 return {ResultStatus::ErrorAlreadyLoaded, {}};
46 }
47
48 if (kip == nullptr) {
49 return {ResultStatus::ErrorNullFile, {}};
50 }
51
52 if (kip->GetStatus() != ResultStatus::Success) {
53 return {kip->GetStatus(), {}};
54 }
55
56 const auto get_kip_address_space_type = [](const auto& kip) {
57 return kip.Is64Bit()
58 ? (kip.Is39BitAddressSpace() ? FileSys::ProgramAddressSpaceType::Is39Bit
59 : FileSys::ProgramAddressSpaceType::Is36Bit)
60 : FileSys::ProgramAddressSpaceType::Is32Bit;
61 };
62
63 const auto address_space = get_kip_address_space_type(*kip);
64
65 FileSys::ProgramMetadata metadata;
66 metadata.LoadManual(kip->Is64Bit(), address_space, kip->GetMainThreadPriority(),
67 kip->GetMainThreadCpuCore(), kip->GetMainThreadStackSize(),
68 kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, kip->GetKernelCapabilities());
69
70 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
71 Kernel::CodeSet codeset;
72 std::vector<u8> program_image;
73
74 const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment,
75 const std::vector<u8>& data, u32 offset) {
76 segment.addr = offset;
77 segment.offset = offset;
78 segment.size = PageAlignSize(static_cast<u32>(data.size()));
79 program_image.resize(offset);
80 program_image.insert(program_image.end(), data.begin(), data.end());
81 };
82
83 load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset());
84 load_segment(codeset.RODataSegment(), kip->GetRODataSection(), kip->GetRODataOffset());
85 load_segment(codeset.DataSegment(), kip->GetDataSection(), kip->GetDataOffset());
86
87 program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
88 codeset.DataSegment().size += kip->GetBSSSize();
89
90 GDBStub::RegisterModule(kip->GetName(), base_address, base_address + program_image.size());
91
92 codeset.memory = std::move(program_image);
93 process.LoadModule(std::move(codeset), base_address);
94
95 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
96
97 is_loaded = true;
98 return {ResultStatus::Success,
99 LoadParameters{kip->GetMainThreadPriority(), kip->GetMainThreadStackSize()}};
100}
101
102} // 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 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/loader/loader.h"
8
9namespace FileSys {
10class KIP;
11}
12
13namespace Loader {
14
15class AppLoader_KIP final : public AppLoader {
16public:
17 explicit AppLoader_KIP(FileSys::VirtualFile file);
18 ~AppLoader_KIP() override;
19
20 /**
21 * Returns the type of the file
22 * @param file std::shared_ptr<VfsFile> open file
23 * @return FileType found, or FileType::Error if this loader doesn't know it
24 */
25 static FileType IdentifyType(const FileSys::VirtualFile& file);
26
27 FileType GetFileType() const override;
28
29 LoadResult Load(Kernel::Process& process) override;
30
31private:
32 std::unique_ptr<FileSys::KIP> kip;
33};
34
35} // 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 @@
11#include "core/hle/kernel/process.h" 11#include "core/hle/kernel/process.h"
12#include "core/loader/deconstructed_rom_directory.h" 12#include "core/loader/deconstructed_rom_directory.h"
13#include "core/loader/elf.h" 13#include "core/loader/elf.h"
14#include "core/loader/kip.h"
14#include "core/loader/nax.h" 15#include "core/loader/nax.h"
15#include "core/loader/nca.h" 16#include "core/loader/nca.h"
16#include "core/loader/nro.h" 17#include "core/loader/nro.h"
@@ -36,6 +37,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
36 CHECK_TYPE(XCI) 37 CHECK_TYPE(XCI)
37 CHECK_TYPE(NAX) 38 CHECK_TYPE(NAX)
38 CHECK_TYPE(NSP) 39 CHECK_TYPE(NSP)
40 CHECK_TYPE(KIP)
39 41
40#undef CHECK_TYPE 42#undef CHECK_TYPE
41 43
@@ -63,6 +65,8 @@ FileType GuessFromFilename(const std::string& name) {
63 return FileType::XCI; 65 return FileType::XCI;
64 if (extension == "nsp") 66 if (extension == "nsp")
65 return FileType::NSP; 67 return FileType::NSP;
68 if (extension == "kip")
69 return FileType::KIP;
66 70
67 return FileType::Unknown; 71 return FileType::Unknown;
68} 72}
@@ -83,6 +87,8 @@ std::string GetFileTypeString(FileType type) {
83 return "NAX"; 87 return "NAX";
84 case FileType::NSP: 88 case FileType::NSP:
85 return "NSP"; 89 return "NSP";
90 case FileType::KIP:
91 return "KIP";
86 case FileType::DeconstructedRomDirectory: 92 case FileType::DeconstructedRomDirectory:
87 return "Directory"; 93 return "Directory";
88 case FileType::Error: 94 case FileType::Error:
@@ -93,7 +99,7 @@ std::string GetFileTypeString(FileType type) {
93 return "unknown"; 99 return "unknown";
94} 100}
95 101
96constexpr std::array<const char*, 62> RESULT_MESSAGES{ 102constexpr std::array<const char*, 66> RESULT_MESSAGES{
97 "The operation completed successfully.", 103 "The operation completed successfully.",
98 "The loader requested to load is already loaded.", 104 "The loader requested to load is already loaded.",
99 "The operation is not implemented.", 105 "The operation is not implemented.",
@@ -156,6 +162,10 @@ constexpr std::array<const char*, 62> RESULT_MESSAGES{
156 "The BKTR-type NCA has a bad Subsection bucket.", 162 "The BKTR-type NCA has a bad Subsection bucket.",
157 "The BKTR-type NCA is missing the base RomFS.", 163 "The BKTR-type NCA is missing the base RomFS.",
158 "The NSP or XCI does not contain an update in addition to the base game.", 164 "The NSP or XCI does not contain an update in addition to the base game.",
165 "The KIP file has a bad header.",
166 "The KIP BLZ decompression of the section failed unexpectedly.",
167 "The INI file has a bad header.",
168 "The INI file contains more than the maximum allowable number of KIP files.",
159}; 169};
160 170
161std::ostream& operator<<(std::ostream& os, ResultStatus status) { 171std::ostream& operator<<(std::ostream& os, ResultStatus status) {
@@ -205,6 +215,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
205 case FileType::NSP: 215 case FileType::NSP:
206 return std::make_unique<AppLoader_NSP>(std::move(file)); 216 return std::make_unique<AppLoader_NSP>(std::move(file));
207 217
218 // NX KIP (Kernel Internal Process) file format
219 case FileType::KIP:
220 return std::make_unique<AppLoader_KIP>(std::move(file));
221
208 // NX deconstructed ROM directory. 222 // NX deconstructed ROM directory.
209 case FileType::DeconstructedRomDirectory: 223 case FileType::DeconstructedRomDirectory:
210 return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file)); 224 return std::make_unique<AppLoader_DeconstructedRomDirectory>(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 {
37 NSP, 37 NSP,
38 XCI, 38 XCI,
39 NAX, 39 NAX,
40 KIP,
40 DeconstructedRomDirectory, 41 DeconstructedRomDirectory,
41}; 42};
42 43
@@ -124,6 +125,10 @@ enum class ResultStatus : u16 {
124 ErrorBadSubsectionBuckets, 125 ErrorBadSubsectionBuckets,
125 ErrorMissingBKTRBaseRomFS, 126 ErrorMissingBKTRBaseRomFS,
126 ErrorNoPackedUpdate, 127 ErrorNoPackedUpdate,
128 ErrorBadKIPHeader,
129 ErrorBLZDecompressionFailed,
130 ErrorBadINIHeader,
131 ErrorINITooManyKIPs,
127}; 132};
128 133
129std::ostream& operator<<(std::ostream& os, ResultStatus status); 134std::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() {
468 468
469const QStringList GameList::supported_file_extensions = { 469const QStringList GameList::supported_file_extensions = {
470 QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"), 470 QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"),
471 QStringLiteral("xci"), QStringLiteral("nsp"), 471 QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")};
472};
473 472
474void GameList::RefreshGameDirectory() { 473void GameList::RefreshGameDirectory() {
475 if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { 474 if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) {