summaryrefslogtreecommitdiff
path: root/src/core/loader/ncch.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/loader/ncch.cpp')
-rw-r--r--src/core/loader/ncch.cpp308
1 files changed, 105 insertions, 203 deletions
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 23864d262..765efcf65 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory>
6
5#include "common/file_util.h" 7#include "common/file_util.h"
6 8
7#include "core/loader/ncch.h" 9#include "core/loader/ncch.h"
@@ -9,136 +11,6 @@
9#include "core/mem_map.h" 11#include "core/mem_map.h"
10 12
11//////////////////////////////////////////////////////////////////////////////////////////////////// 13////////////////////////////////////////////////////////////////////////////////////////////////////
12/// NCCH header (Note: "NCCH" appears to be a publically unknown acronym)
13
14struct NCCH_Header {
15 u8 signature[0x100];
16 char magic[4];
17 u32 content_size;
18 u8 partition_id[8];
19 u16 maker_code;
20 u16 version;
21 u8 reserved_0[4];
22 u8 program_id[8];
23 u8 temp_flag;
24 u8 reserved_1[0x2f];
25 u8 product_code[0x10];
26 u8 extended_header_hash[0x20];
27 u32 extended_header_size;
28 u8 reserved_2[4];
29 u8 flags[8];
30 u32 plain_region_offset;
31 u32 plain_region_size;
32 u8 reserved_3[8];
33 u32 exefs_offset;
34 u32 exefs_size;
35 u32 exefs_hash_region_size;
36 u8 reserved_4[4];
37 u32 romfs_offset;
38 u32 romfs_size;
39 u32 romfs_hash_region_size;
40 u8 reserved_5[4];
41 u8 exefs_super_block_hash[0x20];
42 u8 romfs_super_block_hash[0x20];
43};
44
45////////////////////////////////////////////////////////////////////////////////////////////////////
46// ExeFS (executable file system) headers
47
48typedef struct {
49 char name[8];
50 u32 offset;
51 u32 size;
52} ExeFs_SectionHeader;
53
54typedef struct {
55 ExeFs_SectionHeader section[8];
56 u8 reserved[0x80];
57 u8 hashes[8][0x20];
58} ExeFs_Header;
59
60////////////////////////////////////////////////////////////////////////////////////////////////////
61// ExHeader (executable file system header) headers
62
63struct ExHeader_SystemInfoFlags{
64 u8 reserved[5];
65 u8 flag;
66 u8 remaster_version[2];
67} exheader_systeminfoflags;
68
69struct ExHeader_CodeSegmentInfo{
70 u32 address;
71 u32 num_max_pages;
72 u32 code_size;
73} exheader_codesegmentinfo;
74
75struct ExHeader_CodeSetInfo {
76 u8 name[8];
77 ExHeader_SystemInfoFlags flags;
78 ExHeader_CodeSegmentInfo text;
79 u8 stacksize[4];
80 ExHeader_CodeSegmentInfo ro;
81 u8 reserved[4];
82 ExHeader_CodeSegmentInfo data;
83 u8 bsssize[4];
84};
85
86struct ExHeader_DependencyList{
87 u8 program_id[0x30][8];
88};
89
90struct ExHeader_SystemInfo{
91 u32 save_data_size;
92 u8 reserved[4];
93 u8 jump_id[8];
94 u8 reserved_2[0x30];
95};
96
97struct ExHeader_StorageInfo{
98 u8 ext_save_data_id[8];
99 u8 system_save_data_id[8];
100 u8 reserved[8];
101 u8 access_info[7];
102 u8 other_attributes;
103};
104
105struct ExHeader_ARM11_SystemLocalCaps{
106 u8 program_id[8];
107 u8 flags[8];
108 u8 resource_limit_descriptor[0x10][2];
109 ExHeader_StorageInfo storage_info;
110 u8 service_access_control[0x20][8];
111 u8 reserved[0x1f];
112 u8 resource_limit_category;
113};
114
115struct ExHeader_ARM11_KernelCaps{
116 u8 descriptors[28][4];
117 u8 reserved[0x10];
118};
119
120struct ExHeader_ARM9_AccessControl{
121 u8 descriptors[15];
122 u8 descversion;
123};
124
125struct ExHeader_Header{
126 ExHeader_CodeSetInfo codeset_info;
127 ExHeader_DependencyList dependency_list;
128 ExHeader_SystemInfo system_info;
129 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
130 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
131 ExHeader_ARM9_AccessControl arm9_access_control;
132 struct {
133 u8 signature[0x100];
134 u8 ncch_public_key_modulus[0x100];
135 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
136 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
137 ExHeader_ARM9_AccessControl arm9_access_control;
138 } access_desc;
139};
140
141////////////////////////////////////////////////////////////////////////////////////////////////////
142// Loader namespace 14// Loader namespace
143 15
144namespace Loader { 16namespace Loader {
@@ -163,11 +35,9 @@ u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
163 * @param compressed_size Size of compressed buffer 35 * @param compressed_size Size of compressed buffer
164 * @param decompressed Decompressed buffer 36 * @param decompressed Decompressed buffer
165 * @param decompressed_size Size of decompressed buffer 37 * @param decompressed_size Size of decompressed buffer
166 * @param error_string String populated with error message on failure
167 * @return True on success, otherwise false 38 * @return True on success, otherwise false
168 */ 39 */
169bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size, 40bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) {
170 std::string* error_string) {
171 u8* footer = compressed + compressed_size - 8; 41 u8* footer = compressed + compressed_size - 8;
172 u32 buffer_top_and_bottom = *(u32*)footer; 42 u32 buffer_top_and_bottom = *(u32*)footer;
173 u32 i, j; 43 u32 i, j;
@@ -191,8 +61,8 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32
191 break; 61 break;
192 62
193 if(control & 0x80) { 63 if(control & 0x80) {
64 // Check if compression is out of bounds
194 if(index < 2) { 65 if(index < 2) {
195 *error_string = "Compression out of bounds";
196 return false; 66 return false;
197 } 67 }
198 index -= 2; 68 index -= 2;
@@ -202,22 +72,22 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32
202 segment_offset &= 0x0FFF; 72 segment_offset &= 0x0FFF;
203 segment_offset += 2; 73 segment_offset += 2;
204 74
75 // Check if compression is out of bounds
205 if(out < segment_size) { 76 if(out < segment_size) {
206 *error_string = "Compression out of bounds";
207 return false; 77 return false;
208 } 78 }
209 for(j = 0; j < segment_size; j++) { 79 for(j = 0; j < segment_size; j++) {
210 u8 data; 80 u8 data;
81 // Check if compression is out of bounds
211 if(out + segment_offset >= decompressed_size) { 82 if(out + segment_offset >= decompressed_size) {
212 *error_string = "Compression out of bounds";
213 return false; 83 return false;
214 } 84 }
215 data = decompressed[out + segment_offset]; 85 data = decompressed[out + segment_offset];
216 decompressed[--out] = data; 86 decompressed[--out] = data;
217 } 87 }
218 } else { 88 } else {
89 // Check if compression is out of bounds
219 if(out < 1) { 90 if(out < 1) {
220 *error_string = "Compression out of bounds";
221 return false; 91 return false;
222 } 92 }
223 decompressed[--out] = compressed[--index]; 93 decompressed[--out] = compressed[--index];
@@ -228,34 +98,96 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32
228 return true; 98 return true;
229} 99}
230 100
101////////////////////////////////////////////////////////////////////////////////////////////////////
102// AppLoader_NCCH class
103
104/// AppLoader_NCCH constructor
105AppLoader_NCCH::AppLoader_NCCH(std::string& filename) {
106 this->filename = filename;
107 is_loaded = false;
108 is_compressed = false;
109 entry_point = 0;
110 ncch_offset = 0;
111 exefs_offset = 0;
112}
113
114/// AppLoader_NCCH destructor
115AppLoader_NCCH::~AppLoader_NCCH() {
116}
117
231/** 118/**
232 * Load a data buffer into memory at the specified address 119 * Loads .code section into memory for booting
233 * @param addr Address to load memory into 120 * @return ResultStatus result of function
234 * @param buffer Buffer of data to load into memory
235 * @param size Size of data to load into memory
236 * @todo Perhaps move this code somewhere more generic?
237 */ 121 */
238void LoadBuffer(const u32 addr, const u8* const buffer, const int size) { 122const ResultStatus AppLoader_NCCH::LoadExec() const {
239 u32 *dst = (u32*)Memory::GetPointer(addr); 123 if (!is_loaded)
240 u32 *src = (u32*)buffer; 124 return ResultStatus::ErrorNotLoaded;
241 int size_aligned = (size + 3) / 4;
242 125
243 for (int j = 0; j < size_aligned; j++) { 126 for (std::vector<u8>::size_type i = 0; i != code.size(); i++) {
244 *dst++ = (*src++); 127 Memory::Write8(entry_point + i, code[i]);
245 } 128 }
246 return; 129 Kernel::LoadExec(entry_point);
130
131 return ResultStatus::Success;
247} 132}
248 133
249/** 134/**
135 * Reads an application section of an NCCH file into AppLoader (e.g. .code, .logo, etc.)
136 * @param file Handle to file to read from
137 * @param name Name of section to read out of NCCH file
138 * @param buffer Buffer to read section into.
139 */
140const ResultStatus AppLoader_NCCH::LoadSection(File::IOFile& file, const char* name,
141 std::vector<u8>& buffer) {
142 // Iterate through the ExeFs archive until we find the .code file...
143 for (int i = 0; i < kExeFs_MaxSections; i++) {
144 INFO_LOG(LOADER, "ExeFS section %d:", i);
145 INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name);
146 INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset);
147 INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size);
148
149 // Load the .code section (executable code)...
150 if (strcmp((const char*)exefs_header.section[i].name, name) == 0) {
151 s64 section_offset = (exefs_header.section[i].offset + exefs_offset +
152 sizeof(ExeFs_Header) + ncch_offset);
153 file.Seek(section_offset, 0);
154
155 // Section is compressed...
156 if (i == 0 && is_compressed) {
157 // Read compressed .code section...
158 std::unique_ptr<u8[]> temp_buffer(new u8[exefs_header.section[i].size]);
159 file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size);
160
161 // Decompress .code section...
162 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], exefs_header.section[i].size);
163 buffer.resize(decompressed_size);
164 if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0],
165 decompressed_size)) {
166 return ResultStatus::ErrorInvalidFormat;
167 }
168 // Section is uncompressed...
169 } else {
170 buffer.resize(exefs_header.section[i].size);
171 file.ReadBytes(&buffer[0], exefs_header.section[i].size);
172 }
173 return ResultStatus::Success;
174 }
175 }
176 return ResultStatus::Error;
177}
178
179/**
250 * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) 180 * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
251 * @param filename String filename of NCCH file
252 * @param error_string Pointer to string to put error message if an error has occurred 181 * @param error_string Pointer to string to put error message if an error has occurred
253 * @todo Move NCSD parsing out of here and create a separate function for loading these 182 * @todo Move NCSD parsing out of here and create a separate function for loading these
254 * @return True on success, otherwise false 183 * @return True on success, otherwise false
255 */ 184 */
256bool Load_NCCH(std::string& filename, std::string* error_string) { 185const ResultStatus AppLoader_NCCH::Load() {
257 INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str()); 186 INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str());
258 187
188 if (is_loaded)
189 return ResultStatus::ErrorAlreadyLoaded;
190
259 File::IOFile file(filename, "rb"); 191 File::IOFile file(filename, "rb");
260 192
261 if (file.IsOpen()) { 193 if (file.IsOpen()) {
@@ -263,80 +195,50 @@ bool Load_NCCH(std::string& filename, std::string* error_string) {
263 file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); 195 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
264 196
265 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... 197 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
266 int ncch_off = 0; // Offset to NCCH header, can be 0 or after NCSD header 198 if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) {
267 if (memcmp(&ncch_header.magic, "NCSD", 4) == 0) {
268 WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!"); 199 WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!");
269 ncch_off = 0x4000; 200 ncch_offset = 0x4000;
270 file.Seek(ncch_off, 0); 201 file.Seek(ncch_offset, 0);
271 file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); 202 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
272 } 203 }
204
273 // Verify we are loading the correct file type... 205 // Verify we are loading the correct file type...
274 if (memcmp(&ncch_header.magic, "NCCH", 4) != 0) { 206 if (0 != memcmp(&ncch_header.magic, "NCCH", 4))
275 *error_string = "Invalid NCCH magic number (likely incorrect file type)"; 207 return ResultStatus::ErrorInvalidFormat;
276 return false; 208
277 }
278 // Read ExHeader 209 // Read ExHeader
279 ExHeader_Header exheader_header;
280 file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)); 210 file.ReadBytes(&exheader_header, sizeof(ExHeader_Header));
281 211
282 bool is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; 212 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
213 entry_point = exheader_header.codeset_info.text.address;
283 214
284 INFO_LOG(LOADER, "Name: %s", exheader_header.codeset_info.name); 215 INFO_LOG(LOADER, "Name: %s", exheader_header.codeset_info.name);
285 INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no"); 216 INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no");
217 INFO_LOG(LOADER, "Entry point: 0x%08X", entry_point);
286 218
287 // Read ExeFS 219 // Read ExeFS
288 u32 exefs_offset = ncch_header.exefs_offset * kExeFs_BlockSize; 220 exefs_offset = ncch_header.exefs_offset * kExeFs_BlockSize;
289 u32 exefs_size = ncch_header.exefs_size * kExeFs_BlockSize; 221 u32 exefs_size = ncch_header.exefs_size * kExeFs_BlockSize;
290 222
291 INFO_LOG(LOADER, "ExeFS offset: 0x%08X", exefs_offset); 223 INFO_LOG(LOADER, "ExeFS offset: 0x%08X", exefs_offset);
292 INFO_LOG(LOADER, "ExeFS size: 0x%08X", exefs_size); 224 INFO_LOG(LOADER, "ExeFS size: 0x%08X", exefs_size);
293 225
294 ExeFs_Header exefs_header; 226 file.Seek(exefs_offset + ncch_offset, 0);
295 file.Seek(exefs_offset + ncch_off, 0);
296 file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); 227 file.ReadBytes(&exefs_header, sizeof(ExeFs_Header));
297 228
298 // Iterate through the ExeFs archive until we find the .code file... 229 // TODO(bunnei): Check ResultStatus here...
299 for (int i = 0; i < kExeFs_MaxSections; i++) { 230 LoadSection(file, ".code", code);
300 INFO_LOG(LOADER, "ExeFS section %d:", i); 231 LoadSection(file, ".icon", icon);
301 INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name); 232 LoadSection(file, ".banner", banner);
302 INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset); 233 LoadSection(file, ".logo", logo);
303 INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size);
304
305 // Load the .code section (executable code)...
306 if (strcmp((char*) exefs_header.section[i].name, ".code") == 0) {
307 file.Seek(exefs_header.section[i].offset + exefs_offset + sizeof(ExeFs_Header) +
308 ncch_off, 0);
309
310 u8* buffer = new u8[exefs_header.section[i].size];
311 file.ReadBytes(buffer, exefs_header.section[i].size);
312
313 // Load compressed executable...
314 if (i == 0 && is_compressed) {
315 u32 decompressed_size = LZSS_GetDecompressedSize(buffer,
316 exefs_header.section[i].size);
317
318 if (!LZSS_Decompress(buffer, exefs_header.section[i].size,
319 Memory::GetPointer(exheader_header.codeset_info.text.address),
320 decompressed_size, error_string)) {
321 return false;
322 }
323 // Load uncompressed executable...
324 } else {
325 // Load .code section into memory...
326 LoadBuffer(exheader_header.codeset_info.text.address, buffer,
327 exefs_header.section[i].size);
328 }
329 delete[] buffer;
330 234
331 // Setup kernel emulation to boot .code section... 235 is_loaded = true; // Set state to loaded
332 Kernel::LoadExec(exheader_header.codeset_info.text.address);
333 236
334 // No need to load the other files from ExeFS until we do something with them... 237 LoadExec(); // Load the executable into memory for booting
335 return true; 238
336 } 239 return ResultStatus::Success;
337 }
338 } 240 }
339 return false; 241 return ResultStatus::Error;
340} 242}
341 243
342} // namespace Loader 244} // namespace Loader