summaryrefslogtreecommitdiff
path: root/src/core/file_sys
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/core/file_sys
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/core/file_sys')
-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
4 files changed, 347 insertions, 0 deletions
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;