summaryrefslogtreecommitdiff
path: root/src/core/loader
diff options
context:
space:
mode:
authorGravatar bunnei2015-01-20 12:55:28 -0500
committerGravatar bunnei2015-01-20 12:55:28 -0500
commit205170fa623efdd5eafb0c957d728babe4836f45 (patch)
tree26dd9e4c7ae9cc7a3bb09f42c942c4e47c9cc06f /src/core/loader
parentMerge pull request #496 from lioncash/warn (diff)
parentLoader: Clean up the ELF AppLoader. (diff)
downloadyuzu-205170fa623efdd5eafb0c957d728babe4836f45.tar.gz
yuzu-205170fa623efdd5eafb0c957d728babe4836f45.tar.xz
yuzu-205170fa623efdd5eafb0c957d728babe4836f45.zip
Merge pull request #241 from linkmauve/better-loader
Improve the loader a bit
Diffstat (limited to 'src/core/loader')
-rw-r--r--src/core/loader/3dsx.cpp109
-rw-r--r--src/core/loader/3dsx.h14
-rw-r--r--src/core/loader/elf.cpp116
-rw-r--r--src/core/loader/elf.h14
-rw-r--r--src/core/loader/loader.cpp110
-rw-r--r--src/core/loader/loader.h18
-rw-r--r--src/core/loader/ncch.cpp291
-rw-r--r--src/core/loader/ncch.h24
8 files changed, 344 insertions, 352 deletions
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
index 4d072871a..958dd03e8 100644
--- a/src/core/loader/3dsx.cpp
+++ b/src/core/loader/3dsx.cpp
@@ -13,11 +13,9 @@
13 13
14#include "3dsx.h" 14#include "3dsx.h"
15 15
16
17namespace Loader { 16namespace Loader {
18 17
19 18/**
20/**
21 * File layout: 19 * File layout:
22 * - File header 20 * - File header
23 * - Code, rodata and data relocation table headers 21 * - Code, rodata and data relocation table headers
@@ -46,7 +44,6 @@ enum THREEDSX_Error {
46static const u32 RELOCBUFSIZE = 512; 44static const u32 RELOCBUFSIZE = 512;
47 45
48// File header 46// File header
49static const u32 THREEDSX_MAGIC = 0x58534433; // '3DSX'
50#pragma pack(1) 47#pragma pack(1)
51struct THREEDSX_Header 48struct THREEDSX_Header
52{ 49{
@@ -64,9 +61,9 @@ struct THREEDSX_Header
64struct THREEDSX_RelocHdr 61struct THREEDSX_RelocHdr
65{ 62{
66 // # of absolute relocations (that is, fix address to post-relocation memory layout) 63 // # of absolute relocations (that is, fix address to post-relocation memory layout)
67 u32 cross_segment_absolute; 64 u32 cross_segment_absolute;
68 // # of cross-segment relative relocations (that is, 32bit signed offsets that need to be patched) 65 // # of cross-segment relative relocations (that is, 32bit signed offsets that need to be patched)
69 u32 cross_segment_relative; 66 u32 cross_segment_relative;
70 // more? 67 // more?
71 68
72 // Relocations are written in this order: 69 // Relocations are written in this order:
@@ -88,12 +85,7 @@ struct THREEloadinfo
88 u32 seg_sizes[3]; 85 u32 seg_sizes[3];
89}; 86};
90 87
91class THREEDSXReader { 88static u32 TranslateAddr(u32 addr, const THREEloadinfo *loadinfo, u32* offsets)
92public:
93 static int Load3DSXFile(const std::string& filename, u32 base_addr);
94};
95
96static u32 TranslateAddr(u32 addr, THREEloadinfo *loadinfo, u32* offsets)
97{ 89{
98 if (addr < offsets[0]) 90 if (addr < offsets[0])
99 return loadinfo->seg_addrs[0] + addr; 91 return loadinfo->seg_addrs[0] + addr;
@@ -102,12 +94,14 @@ static u32 TranslateAddr(u32 addr, THREEloadinfo *loadinfo, u32* offsets)
102 return loadinfo->seg_addrs[2] + addr - offsets[1]; 94 return loadinfo->seg_addrs[2] + addr - offsets[1];
103} 95}
104 96
105int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) 97static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr)
106{ 98{
107 FileUtil::IOFile file(filename, "rb"); 99 if (!file.IsOpen())
108 if (!file.IsOpen()) {
109 return ERROR_FILE; 100 return ERROR_FILE;
110 } 101
102 // Reset read pointer in case this file has been read before.
103 file.Seek(0, SEEK_SET);
104
111 THREEDSX_Header hdr; 105 THREEDSX_Header hdr;
112 if (file.ReadBytes(&hdr, sizeof(hdr)) != sizeof(hdr)) 106 if (file.ReadBytes(&hdr, sizeof(hdr)) != sizeof(hdr))
113 return ERROR_READ; 107 return ERROR_READ;
@@ -136,8 +130,9 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr)
136 // Read the relocation headers 130 // Read the relocation headers
137 u32* relocs = (u32*)(loadinfo.seg_ptrs[2] + hdr.data_seg_size); 131 u32* relocs = (u32*)(loadinfo.seg_ptrs[2] + hdr.data_seg_size);
138 132
139 for (u32 current_segment = 0; current_segment < 3; current_segment++) { 133 for (unsigned current_segment : {0, 1, 2}) {
140 if (file.ReadBytes(&relocs[current_segment*n_reloc_tables], n_reloc_tables * 4) != n_reloc_tables * 4) 134 size_t size = n_reloc_tables * 4;
135 if (file.ReadBytes(&relocs[current_segment * n_reloc_tables], size) != size)
141 return ERROR_READ; 136 return ERROR_READ;
142 } 137 }
143 138
@@ -153,9 +148,9 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr)
153 memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size); 148 memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size);
154 149
155 // Relocate the segments 150 // Relocate the segments
156 for (u32 current_segment = 0; current_segment < 3; current_segment++) { 151 for (unsigned current_segment : {0, 1, 2}) {
157 for (u32 current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) { 152 for (unsigned current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) {
158 u32 n_relocs = relocs[current_segment*n_reloc_tables + current_segment_reloc_table]; 153 u32 n_relocs = relocs[current_segment * n_reloc_tables + current_segment_reloc_table];
159 if (current_segment_reloc_table >= 2) { 154 if (current_segment_reloc_table >= 2) {
160 // We are not using this table - ignore it because we don't know what it dose 155 // We are not using this table - ignore it because we don't know what it dose
161 file.Seek(n_relocs*sizeof(THREEDSX_Reloc), SEEK_CUR); 156 file.Seek(n_relocs*sizeof(THREEDSX_Reloc), SEEK_CUR);
@@ -164,29 +159,35 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr)
164 static THREEDSX_Reloc reloc_table[RELOCBUFSIZE]; 159 static THREEDSX_Reloc reloc_table[RELOCBUFSIZE];
165 160
166 u32* pos = (u32*)loadinfo.seg_ptrs[current_segment]; 161 u32* pos = (u32*)loadinfo.seg_ptrs[current_segment];
167 u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4); 162 const u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4);
168 163
169 while (n_relocs) { 164 while (n_relocs) {
170 u32 remaining = std::min(RELOCBUFSIZE, n_relocs); 165 u32 remaining = std::min(RELOCBUFSIZE, n_relocs);
171 n_relocs -= remaining; 166 n_relocs -= remaining;
172 167
173 if (file.ReadBytes(reloc_table, remaining*sizeof(THREEDSX_Reloc)) != remaining*sizeof(THREEDSX_Reloc)) 168 if (file.ReadBytes(reloc_table, remaining * sizeof(THREEDSX_Reloc)) != remaining * sizeof(THREEDSX_Reloc))
174 return ERROR_READ; 169 return ERROR_READ;
175 170
176 for (u32 current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) { 171 for (unsigned current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) {
177 LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)\n", 172 const auto& table = reloc_table[current_inprogress];
178 current_segment_reloc_table, (u32)reloc_table[current_inprogress].skip, (u32)reloc_table[current_inprogress].patch); 173 LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)\n", current_segment_reloc_table,
179 pos += reloc_table[current_inprogress].skip; 174 (u32)table.skip, (u32)table.patch);
180 s32 num_patches = reloc_table[current_inprogress].patch; 175 pos += table.skip;
176 s32 num_patches = table.patch;
181 while (0 < num_patches && pos < end_pos) { 177 while (0 < num_patches && pos < end_pos) {
182 u32 in_addr = (char*)pos - (char*)&all_mem[0]; 178 u32 in_addr = (char*)pos - (char*)&all_mem[0];
183 u32 addr = TranslateAddr(*pos, &loadinfo, offsets); 179 u32 addr = TranslateAddr(*pos, &loadinfo, offsets);
184 LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n", 180 LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n",
185 base_addr + in_addr, addr, current_segment_reloc_table, *pos); 181 base_addr + in_addr, addr, current_segment_reloc_table, *pos);
186 switch (current_segment_reloc_table) { 182 switch (current_segment_reloc_table) {
187 case 0: *pos = (addr); break; 183 case 0:
188 case 1: *pos = (addr - in_addr); break; 184 *pos = (addr);
189 default: break; //this should never happen 185 break;
186 case 1:
187 *pos = (addr - in_addr);
188 break;
189 default:
190 break; //this should never happen
190 } 191 }
191 pos++; 192 pos++;
192 num_patches--; 193 num_patches--;
@@ -207,28 +208,30 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr)
207 return ERROR_NONE; 208 return ERROR_NONE;
208} 209}
209 210
210 /// AppLoader_DSX constructor 211FileType AppLoader_THREEDSX::IdentifyType(FileUtil::IOFile& file) {
211 AppLoader_THREEDSX::AppLoader_THREEDSX(const std::string& filename) : filename(filename) { 212 u32 magic;
212 } 213 file.Seek(0, SEEK_SET);
214 if (1 != file.ReadArray<u32>(&magic, 1))
215 return FileType::Error;
213 216
214 /// AppLoader_DSX destructor 217 if (MakeMagic('3', 'D', 'S', 'X') == magic)
215 AppLoader_THREEDSX::~AppLoader_THREEDSX() { 218 return FileType::THREEDSX;
216 }
217 219
218 /** 220 return FileType::Error;
219 * Loads a 3DSX file 221}
220 * @return Success on success, otherwise Error 222
221 */ 223ResultStatus AppLoader_THREEDSX::Load() {
222 ResultStatus AppLoader_THREEDSX::Load() { 224 if (is_loaded)
223 LOG_INFO(Loader, "Loading 3DSX file %s...", filename.c_str()); 225 return ResultStatus::ErrorAlreadyLoaded;
224 FileUtil::IOFile file(filename, "rb"); 226
225 if (file.IsOpen()) { 227 if (!file->IsOpen())
226 THREEDSXReader::Load3DSXFile(filename, 0x00100000); 228 return ResultStatus::Error;
227 Kernel::LoadExec(0x00100000); 229
228 } else { 230 Load3DSXFile(*file, 0x00100000);
229 return ResultStatus::Error; 231 Kernel::LoadExec(0x00100000);
230 } 232
231 return ResultStatus::Success; 233 is_loaded = true;
232 } 234 return ResultStatus::Success;
235}
233 236
234} // namespace Loader 237} // namespace Loader
diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h
index da8836662..a11667400 100644
--- a/src/core/loader/3dsx.h
+++ b/src/core/loader/3dsx.h
@@ -15,18 +15,20 @@ namespace Loader {
15/// Loads an 3DSX file 15/// Loads an 3DSX file
16class AppLoader_THREEDSX final : public AppLoader { 16class AppLoader_THREEDSX final : public AppLoader {
17public: 17public:
18 AppLoader_THREEDSX(const std::string& filename); 18 AppLoader_THREEDSX(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { }
19 ~AppLoader_THREEDSX() override; 19
20 /**
21 * Returns the type of the file
22 * @param file FileUtil::IOFile open file
23 * @return FileType found, or FileType::Error if this loader doesn't know it
24 */
25 static FileType IdentifyType(FileUtil::IOFile& file);
20 26
21 /** 27 /**
22 * Load the bootable file 28 * Load the bootable file
23 * @return ResultStatus result of function 29 * @return ResultStatus result of function
24 */ 30 */
25 ResultStatus Load() override; 31 ResultStatus Load() override;
26
27private:
28 std::string filename;
29 bool is_loaded;
30}; 32};
31 33
32} // namespace Loader 34} // namespace Loader
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 3ca60c072..e7e5df408 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -18,25 +18,25 @@
18 18
19// File type 19// File type
20enum ElfType { 20enum ElfType {
21 ET_NONE = 0, 21 ET_NONE = 0,
22 ET_REL = 1, 22 ET_REL = 1,
23 ET_EXEC = 2, 23 ET_EXEC = 2,
24 ET_DYN = 3, 24 ET_DYN = 3,
25 ET_CORE = 4, 25 ET_CORE = 4,
26 ET_LOPROC = 0xFF00, 26 ET_LOPROC = 0xFF00,
27 ET_HIPROC = 0xFFFF, 27 ET_HIPROC = 0xFFFF,
28}; 28};
29 29
30// Machine/Architecture 30// Machine/Architecture
31enum ElfMachine { 31enum ElfMachine {
32 EM_NONE = 0, 32 EM_NONE = 0,
33 EM_M32 = 1, 33 EM_M32 = 1,
34 EM_SPARC = 2, 34 EM_SPARC = 2,
35 EM_386 = 3, 35 EM_386 = 3,
36 EM_68K = 4, 36 EM_68K = 4,
37 EM_88K = 5, 37 EM_88K = 5,
38 EM_860 = 7, 38 EM_860 = 7,
39 EM_MIPS = 8 39 EM_MIPS = 8
40}; 40};
41 41
42// File version 42// File version
@@ -54,12 +54,6 @@ enum ElfMachine {
54#define EI_PAD 7 54#define EI_PAD 7
55#define EI_NIDENT 16 55#define EI_NIDENT 16
56 56
57// Magic number
58#define ELFMAG0 0x7F
59#define ELFMAG1 'E'
60#define ELFMAG2 'L'
61#define ELFMAG3 'F'
62
63// Sections constants 57// Sections constants
64 58
65// Section types 59// Section types
@@ -83,10 +77,10 @@ enum ElfMachine {
83// Section flags 77// Section flags
84enum ElfSectionFlags 78enum ElfSectionFlags
85{ 79{
86 SHF_WRITE = 0x1, 80 SHF_WRITE = 0x1,
87 SHF_ALLOC = 0x2, 81 SHF_ALLOC = 0x2,
88 SHF_EXECINSTR = 0x4, 82 SHF_EXECINSTR = 0x4,
89 SHF_MASKPROC = 0xF0000000, 83 SHF_MASKPROC = 0xF0000000,
90}; 84};
91 85
92// Segment types 86// Segment types
@@ -100,11 +94,11 @@ enum ElfSectionFlags
100#define PT_LOPROC 0x70000000 94#define PT_LOPROC 0x70000000
101#define PT_HIPROC 0x7FFFFFFF 95#define PT_HIPROC 0x7FFFFFFF
102 96
103typedef unsigned int Elf32_Addr; 97typedef unsigned int Elf32_Addr;
104typedef unsigned short Elf32_Half; 98typedef unsigned short Elf32_Half;
105typedef unsigned int Elf32_Off; 99typedef unsigned int Elf32_Off;
106typedef signed int Elf32_Sword; 100typedef signed int Elf32_Sword;
107typedef unsigned int Elf32_Word; 101typedef unsigned int Elf32_Word;
108 102
109//////////////////////////////////////////////////////////////////////////////////////////////////// 103////////////////////////////////////////////////////////////////////////////////////////////////////
110// ELF file header 104// ELF file header
@@ -188,7 +182,6 @@ private:
188 182
189public: 183public:
190 ElfReader(void *ptr); 184 ElfReader(void *ptr);
191 ~ElfReader() { }
192 185
193 u32 Read32(int off) const { return base32[off >> 2]; } 186 u32 Read32(int off) const { return base32[off >> 2]; }
194 187
@@ -197,7 +190,7 @@ public:
197 ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } 190 ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); }
198 u32 GetEntryPoint() const { return entryPoint; } 191 u32 GetEntryPoint() const { return entryPoint; }
199 u32 GetFlags() const { return (u32)(header->e_flags); } 192 u32 GetFlags() const { return (u32)(header->e_flags); }
200 bool LoadInto(u32 vaddr); 193 void LoadInto(u32 vaddr);
201 bool LoadSymbols(); 194 bool LoadSymbols();
202 195
203 int GetNumSegments() const { return (int)(header->e_phnum); } 196 int GetNumSegments() const { return (int)(header->e_phnum); }
@@ -229,11 +222,11 @@ public:
229 222
230ElfReader::ElfReader(void *ptr) { 223ElfReader::ElfReader(void *ptr) {
231 base = (char*)ptr; 224 base = (char*)ptr;
232 base32 = (u32 *)ptr; 225 base32 = (u32*)ptr;
233 header = (Elf32_Ehdr*)ptr; 226 header = (Elf32_Ehdr*)ptr;
234 227
235 segments = (Elf32_Phdr *)(base + header->e_phoff); 228 segments = (Elf32_Phdr*)(base + header->e_phoff);
236 sections = (Elf32_Shdr *)(base + header->e_shoff); 229 sections = (Elf32_Shdr*)(base + header->e_shoff);
237 230
238 entryPoint = header->e_entry; 231 entryPoint = header->e_entry;
239 232
@@ -245,7 +238,7 @@ const char *ElfReader::GetSectionName(int section) const {
245 return nullptr; 238 return nullptr;
246 239
247 int name_offset = sections[section].sh_name; 240 int name_offset = sections[section].sh_name;
248 char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); 241 const char* ptr = (char*)GetSectionDataPtr(header->e_shstrndx);
249 242
250 if (ptr) 243 if (ptr)
251 return ptr + name_offset; 244 return ptr + name_offset;
@@ -253,7 +246,7 @@ const char *ElfReader::GetSectionName(int section) const {
253 return nullptr; 246 return nullptr;
254} 247}
255 248
256bool ElfReader::LoadInto(u32 vaddr) { 249void ElfReader::LoadInto(u32 vaddr) {
257 LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx); 250 LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx);
258 251
259 // Should we relocate? 252 // Should we relocate?
@@ -271,20 +264,19 @@ bool ElfReader::LoadInto(u32 vaddr) {
271 u32 segment_addr[32]; 264 u32 segment_addr[32];
272 u32 base_addr = relocate ? vaddr : 0; 265 u32 base_addr = relocate ? vaddr : 0;
273 266
274 for (int i = 0; i < header->e_phnum; i++) { 267 for (unsigned i = 0; i < header->e_phnum; i++) {
275 Elf32_Phdr *p = segments + i; 268 Elf32_Phdr* p = segments + i;
276 LOG_DEBUG(Loader, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, 269 LOG_DEBUG(Loader, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr,
277 p->p_filesz, p->p_memsz); 270 p->p_filesz, p->p_memsz);
278 271
279 if (p->p_type == PT_LOAD) { 272 if (p->p_type == PT_LOAD) {
280 segment_addr[i] = base_addr + p->p_vaddr; 273 segment_addr[i] = base_addr + p->p_vaddr;
281 memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz); 274 memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz);
282 LOG_DEBUG(Loader, "Loadable Segment Copied to %08x, size %08x", segment_addr[i], 275 LOG_DEBUG(Loader, "Loadable Segment Copied to %08x, size %08x", segment_addr[i],
283 p->p_memsz); 276 p->p_memsz);
284 } 277 }
285 } 278 }
286 LOG_DEBUG(Loader, "Done loading."); 279 LOG_DEBUG(Loader, "Done loading.");
287 return true;
288} 280}
289 281
290SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const { 282SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const {
@@ -305,9 +297,9 @@ bool ElfReader::LoadSymbols() {
305 const char *stringBase = (const char *)GetSectionDataPtr(stringSection); 297 const char *stringBase = (const char *)GetSectionDataPtr(stringSection);
306 298
307 //We have a symbol table! 299 //We have a symbol table!
308 Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); 300 Elf32_Sym* symtab = (Elf32_Sym *)(GetSectionDataPtr(sec));
309 int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); 301 int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym);
310 for (int sym = 0; sym < numSymbols; sym++) { 302 for (unsigned sym = 0; sym < numSymbols; sym++) {
311 int size = symtab[sym].st_size; 303 int size = symtab[sym].st_size;
312 if (size == 0) 304 if (size == 0)
313 continue; 305 continue;
@@ -330,40 +322,38 @@ bool ElfReader::LoadSymbols() {
330 322
331namespace Loader { 323namespace Loader {
332 324
333/// AppLoader_ELF constructor 325FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) {
334AppLoader_ELF::AppLoader_ELF(const std::string& filename) : is_loaded(false) { 326 u32 magic;
335 this->filename = filename; 327 file.Seek(0, SEEK_SET);
336} 328 if (1 != file.ReadArray<u32>(&magic, 1))
329 return FileType::Error;
330
331 if (MakeMagic('\x7f', 'E', 'L', 'F') == magic)
332 return FileType::ELF;
337 333
338/// AppLoader_NCCH destructor 334 return FileType::Error;
339AppLoader_ELF::~AppLoader_ELF() {
340} 335}
341 336
342/**
343 * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
344 * @param error_string Pointer to string to put error message if an error has occurred
345 * @todo Move NCSD parsing out of here and create a separate function for loading these
346 * @return True on success, otherwise false
347 */
348ResultStatus AppLoader_ELF::Load() { 337ResultStatus AppLoader_ELF::Load() {
349 LOG_INFO(Loader, "Loading ELF file %s...", filename.c_str());
350
351 if (is_loaded) 338 if (is_loaded)
352 return ResultStatus::ErrorAlreadyLoaded; 339 return ResultStatus::ErrorAlreadyLoaded;
353 340
354 FileUtil::IOFile file(filename, "rb"); 341 if (!file->IsOpen())
342 return ResultStatus::Error;
355 343
356 if (file.IsOpen()) { 344 // Reset read pointer in case this file has been read before.
357 u32 size = (u32)file.GetSize(); 345 file->Seek(0, SEEK_SET);
358 std::unique_ptr<u8[]> buffer(new u8[size]);
359 file.ReadBytes(&buffer[0], size);
360 346
361 ElfReader elf_reader(&buffer[0]); 347 u32 size = static_cast<u32>(file->GetSize());
362 elf_reader.LoadInto(0x00100000); 348 std::unique_ptr<u8[]> buffer(new u8[size]);
363 Kernel::LoadExec(elf_reader.GetEntryPoint()); 349 if (file->ReadBytes(&buffer[0], size) != size)
364 } else {
365 return ResultStatus::Error; 350 return ResultStatus::Error;
366 } 351
352 ElfReader elf_reader(&buffer[0]);
353 elf_reader.LoadInto(0x00100000);
354 Kernel::LoadExec(elf_reader.GetEntryPoint());
355
356 is_loaded = true;
367 return ResultStatus::Success; 357 return ResultStatus::Success;
368} 358}
369 359
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index c221cce6d..b6e6651f5 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -15,18 +15,20 @@ namespace Loader {
15/// Loads an ELF/AXF file 15/// Loads an ELF/AXF file
16class AppLoader_ELF final : public AppLoader { 16class AppLoader_ELF final : public AppLoader {
17public: 17public:
18 AppLoader_ELF(const std::string& filename); 18 AppLoader_ELF(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { }
19 ~AppLoader_ELF() override; 19
20 /**
21 * Returns the type of the file
22 * @param file FileUtil::IOFile open file
23 * @return FileType found, or FileType::Error if this loader doesn't know it
24 */
25 static FileType IdentifyType(FileUtil::IOFile& file);
20 26
21 /** 27 /**
22 * Load the bootable file 28 * Load the bootable file
23 * @return ResultStatus result of function 29 * @return ResultStatus result of function
24 */ 30 */
25 ResultStatus Load() override; 31 ResultStatus Load() override;
26
27private:
28 std::string filename;
29 bool is_loaded;
30}; 32};
31 33
32} // namespace Loader 34} // namespace Loader
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 45cf425df..60460292d 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -19,11 +19,32 @@ namespace Loader {
19 19
20/** 20/**
21 * Identifies the type of a bootable file 21 * Identifies the type of a bootable file
22 * @param file open file
23 * @return FileType of file
24 */
25static FileType IdentifyFile(FileUtil::IOFile& file) {
26 FileType type;
27
28#define CHECK_TYPE(loader) \
29 type = AppLoader_##loader::IdentifyType(file); \
30 if (FileType::Error != type) \
31 return type;
32
33 CHECK_TYPE(THREEDSX)
34 CHECK_TYPE(ELF)
35 CHECK_TYPE(NCCH)
36
37#undef CHECK_TYPE
38
39 return FileType::Unknown;
40}
41
42/**
43 * Guess the type of a bootable file from its extension
22 * @param filename String filename of bootable file 44 * @param filename String filename of bootable file
23 * @todo (ShizZy) this function sucks... make it actually check file contents etc.
24 * @return FileType of file 45 * @return FileType of file
25 */ 46 */
26FileType IdentifyFile(const std::string &filename) { 47static FileType GuessFromFilename(const std::string& filename) {
27 if (filename.size() == 0) { 48 if (filename.size() == 0) {
28 LOG_ERROR(Loader, "invalid filename %s", filename.c_str()); 49 LOG_ERROR(Loader, "invalid filename %s", filename.c_str());
29 return FileType::Error; 50 return FileType::Error;
@@ -34,47 +55,74 @@ FileType IdentifyFile(const std::string &filename) {
34 return FileType::Unknown; 55 return FileType::Unknown;
35 std::string extension = Common::ToLower(filename.substr(extension_loc)); 56 std::string extension = Common::ToLower(filename.substr(extension_loc));
36 57
37 // TODO(bunnei): Do actual filetype checking instead of naively checking the extension 58 if (extension == ".elf")
38 if (extension == ".elf") {
39 return FileType::ELF; 59 return FileType::ELF;
40 } else if (extension == ".axf") { 60 else if (extension == ".axf")
41 return FileType::ELF; 61 return FileType::ELF;
42 } else if (extension == ".cxi") { 62 else if (extension == ".cxi")
43 return FileType::CXI; 63 return FileType::CXI;
44 } else if (extension == ".cci") { 64 else if (extension == ".cci")
45 return FileType::CCI; 65 return FileType::CCI;
46 } else if (extension == ".bin") { 66 else if (extension == ".bin")
47 return FileType::BIN; 67 return FileType::BIN;
48 } else if (extension == ".3ds") { 68 else if (extension == ".3ds")
49 return FileType::CCI; 69 return FileType::CCI;
50 } else if (extension == ".3dsx") { 70 else if (extension == ".3dsx")
51 return FileType::THREEDSX; 71 return FileType::THREEDSX;
52 }
53 return FileType::Unknown; 72 return FileType::Unknown;
54} 73}
55 74
56/** 75static const char* GetFileTypeString(FileType type) {
57 * Identifies and loads a bootable file 76 switch (type) {
58 * @param filename String filename of bootable file 77 case FileType::CCI:
59 * @return ResultStatus result of function 78 return "NCSD";
60 */ 79 case FileType::CXI:
80 return "NCCH";
81 case FileType::ELF:
82 return "ELF";
83 case FileType::THREEDSX:
84 return "3DSX";
85 case FileType::BIN:
86 return "raw";
87 case FileType::Error:
88 case FileType::Unknown:
89 return "unknown";
90 }
91}
92
61ResultStatus LoadFile(const std::string& filename) { 93ResultStatus LoadFile(const std::string& filename) {
62 LOG_INFO(Loader, "Loading file %s...", filename.c_str()); 94 std::unique_ptr<FileUtil::IOFile> file(new FileUtil::IOFile(filename, "rb"));
95 if (!file->IsOpen()) {
96 LOG_ERROR(Loader, "Failed to load file %s", filename.c_str());
97 return ResultStatus::Error;
98 }
99
100 FileType type = IdentifyFile(*file);
101 FileType filename_type = GuessFromFilename(filename);
102
103 if (type != filename_type) {
104 LOG_WARNING(Loader, "File %s has a different type than its extension.", filename.c_str());
105 if (FileType::Unknown == type)
106 type = filename_type;
107 }
63 108
64 switch (IdentifyFile(filename)) { 109 LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type));
110
111 switch (type) {
65 112
66 //3DSX file format... 113 //3DSX file format...
67 case FileType::THREEDSX: 114 case FileType::THREEDSX:
68 return AppLoader_THREEDSX(filename).Load(); 115 return AppLoader_THREEDSX(std::move(file)).Load();
69 116
70 // Standard ELF file format... 117 // Standard ELF file format...
71 case FileType::ELF: 118 case FileType::ELF:
72 return AppLoader_ELF(filename).Load(); 119 return AppLoader_ELF(std::move(file)).Load();
73 120
74 // NCCH/NCSD container formats... 121 // NCCH/NCSD container formats...
75 case FileType::CXI: 122 case FileType::CXI:
76 case FileType::CCI: { 123 case FileType::CCI:
77 AppLoader_NCCH app_loader(filename); 124 {
125 AppLoader_NCCH app_loader(std::move(file));
78 126
79 // Load application and RomFS 127 // Load application and RomFS
80 if (ResultStatus::Success == app_loader.Load()) { 128 if (ResultStatus::Success == app_loader.Load()) {
@@ -88,16 +136,11 @@ ResultStatus LoadFile(const std::string& filename) {
88 // Raw BIN file format... 136 // Raw BIN file format...
89 case FileType::BIN: 137 case FileType::BIN:
90 { 138 {
91 LOG_INFO(Loader, "Loading BIN file %s...", filename.c_str()); 139 size_t size = (size_t)file->GetSize();
92 140 if (file->ReadBytes(Memory::GetPointer(Memory::EXEFS_CODE_VADDR), size) != size)
93 FileUtil::IOFile file(filename, "rb");
94
95 if (file.IsOpen()) {
96 file.ReadBytes(Memory::GetPointer(Memory::EXEFS_CODE_VADDR), (size_t)file.GetSize());
97 Kernel::LoadExec(Memory::EXEFS_CODE_VADDR);
98 } else {
99 return ResultStatus::Error; 141 return ResultStatus::Error;
100 } 142
143 Kernel::LoadExec(Memory::EXEFS_CODE_VADDR);
101 return ResultStatus::Success; 144 return ResultStatus::Success;
102 } 145 }
103 146
@@ -106,10 +149,11 @@ ResultStatus LoadFile(const std::string& filename) {
106 149
107 // IdentifyFile could know identify file type... 150 // IdentifyFile could know identify file type...
108 case FileType::Unknown: 151 case FileType::Unknown:
109 152 {
110 default: 153 LOG_CRITICAL(Loader, "File %s is of unknown type.");
111 return ResultStatus::ErrorInvalidFormat; 154 return ResultStatus::ErrorInvalidFormat;
112 } 155 }
156 }
113 return ResultStatus::Error; 157 return ResultStatus::Error;
114} 158}
115 159
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index ec5534d41..7456b019b 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -7,6 +7,7 @@
7#include <vector> 7#include <vector>
8 8
9#include "common/common.h" 9#include "common/common.h"
10#include "common/file_util.h"
10 11
11//////////////////////////////////////////////////////////////////////////////////////////////////// 12////////////////////////////////////////////////////////////////////////////////////////////////////
12// Loader namespace 13// Loader namespace
@@ -37,10 +38,14 @@ enum class ResultStatus {
37 ErrorMemoryAllocationFailed, 38 ErrorMemoryAllocationFailed,
38}; 39};
39 40
41static u32 MakeMagic(char a, char b, char c, char d) {
42 return a | b << 8 | c << 16 | d << 24;
43}
44
40/// Interface for loading an application 45/// Interface for loading an application
41class AppLoader : NonCopyable { 46class AppLoader : NonCopyable {
42public: 47public:
43 AppLoader() { } 48 AppLoader(std::unique_ptr<FileUtil::IOFile>&& file) : file(std::move(file)) { }
44 virtual ~AppLoader() { } 49 virtual ~AppLoader() { }
45 50
46 /** 51 /**
@@ -93,14 +98,11 @@ public:
93 virtual ResultStatus ReadRomFS(std::vector<u8>& buffer) const { 98 virtual ResultStatus ReadRomFS(std::vector<u8>& buffer) const {
94 return ResultStatus::ErrorNotImplemented; 99 return ResultStatus::ErrorNotImplemented;
95 } 100 }
96};
97 101
98/** 102protected:
99 * Identifies the type of a bootable file 103 std::unique_ptr<FileUtil::IOFile> file;
100 * @param filename String filename of bootable file 104 bool is_loaded = false;
101 * @return FileType of file 105};
102 */
103FileType IdentifyFile(const std::string &filename);
104 106
105/** 107/**
106 * Identifies and loads a bootable file 108 * Identifies and loads a bootable file
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 0dc21699e..aaaa4d650 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -4,8 +4,6 @@
4 4
5#include <memory> 5#include <memory>
6 6
7#include "common/file_util.h"
8
9#include "core/loader/ncch.h" 7#include "core/loader/ncch.h"
10#include "core/hle/kernel/kernel.h" 8#include "core/hle/kernel/kernel.h"
11#include "core/mem_map.h" 9#include "core/mem_map.h"
@@ -15,8 +13,8 @@
15 13
16namespace Loader { 14namespace Loader {
17 15
18static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs 16static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs
19static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes) 17static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
20 18
21/** 19/**
22 * Get the decompressed size of an LZSS compressed ExeFS file 20 * Get the decompressed size of an LZSS compressed ExeFS file
@@ -24,7 +22,7 @@ static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
24 * @param size Size of compressed buffer 22 * @param size Size of compressed buffer
25 * @return Size of decompressed buffer 23 * @return Size of decompressed buffer
26 */ 24 */
27static u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { 25static u32 LZSS_GetDecompressedSize(const u8* buffer, u32 size) {
28 u32 offset_size = *(u32*)(buffer + size - 4); 26 u32 offset_size = *(u32*)(buffer + size - 4);
29 return offset_size + size; 27 return offset_size + size;
30} 28}
@@ -37,9 +35,9 @@ static u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
37 * @param decompressed_size Size of decompressed buffer 35 * @param decompressed_size Size of decompressed buffer
38 * @return True on success, otherwise false 36 * @return True on success, otherwise false
39 */ 37 */
40static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) { 38static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) {
41 u8* footer = compressed + compressed_size - 8; 39 const u8* footer = compressed + compressed_size - 8;
42 u32 buffer_top_and_bottom = *(u32*)footer; 40 u32 buffer_top_and_bottom = *reinterpret_cast<const u32*>(footer);
43 u32 out = decompressed_size; 41 u32 out = decompressed_size;
44 u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF); 42 u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF);
45 u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF); 43 u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF);
@@ -47,22 +45,21 @@ static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompresse
47 memset(decompressed, 0, decompressed_size); 45 memset(decompressed, 0, decompressed_size);
48 memcpy(decompressed, compressed, compressed_size); 46 memcpy(decompressed, compressed, compressed_size);
49 47
50 while(index > stop_index) { 48 while (index > stop_index) {
51 u8 control = compressed[--index]; 49 u8 control = compressed[--index];
52 50
53 for(u32 i = 0; i < 8; i++) { 51 for (unsigned i = 0; i < 8; i++) {
54 if(index <= stop_index) 52 if (index <= stop_index)
55 break; 53 break;
56 if(index <= 0) 54 if (index <= 0)
57 break; 55 break;
58 if(out <= 0) 56 if (out <= 0)
59 break; 57 break;
60 58
61 if(control & 0x80) { 59 if (control & 0x80) {
62 // Check if compression is out of bounds 60 // Check if compression is out of bounds
63 if(index < 2) { 61 if (index < 2)
64 return false; 62 return false;
65 }
66 index -= 2; 63 index -= 2;
67 64
68 u32 segment_offset = compressed[index] | (compressed[index + 1] << 8); 65 u32 segment_offset = compressed[index] | (compressed[index + 1] << 8);
@@ -71,23 +68,21 @@ static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompresse
71 segment_offset += 2; 68 segment_offset += 2;
72 69
73 // Check if compression is out of bounds 70 // Check if compression is out of bounds
74 if(out < segment_size) { 71 if (out < segment_size)
75 return false; 72 return false;
76 } 73
77 for(u32 j = 0; j < segment_size; j++) { 74 for (unsigned j = 0; j < segment_size; j++) {
78 // Check if compression is out of bounds 75 // Check if compression is out of bounds
79 if(out + segment_offset >= decompressed_size) { 76 if (out + segment_offset >= decompressed_size)
80 return false; 77 return false;
81 }
82 78
83 u8 data = decompressed[out + segment_offset]; 79 u8 data = decompressed[out + segment_offset];
84 decompressed[--out] = data; 80 decompressed[--out] = data;
85 } 81 }
86 } else { 82 } else {
87 // Check if compression is out of bounds 83 // Check if compression is out of bounds
88 if(out < 1) { 84 if (out < 1)
89 return false; 85 return false;
90 }
91 decompressed[--out] = compressed[--index]; 86 decompressed[--out] = compressed[--index];
92 } 87 }
93 control <<= 1; 88 control <<= 1;
@@ -99,24 +94,21 @@ static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompresse
99//////////////////////////////////////////////////////////////////////////////////////////////////// 94////////////////////////////////////////////////////////////////////////////////////////////////////
100// AppLoader_NCCH class 95// AppLoader_NCCH class
101 96
102/// AppLoader_NCCH constructor 97FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
103AppLoader_NCCH::AppLoader_NCCH(const std::string& filename) { 98 u32 magic;
104 this->filename = filename; 99 file.Seek(0x100, SEEK_SET);
105 is_loaded = false; 100 if (1 != file.ReadArray<u32>(&magic, 1))
106 is_compressed = false; 101 return FileType::Error;
107 entry_point = 0; 102
108 ncch_offset = 0; 103 if (MakeMagic('N', 'C', 'S', 'D') == magic)
109 exefs_offset = 0; 104 return FileType::CCI;
110}
111 105
112/// AppLoader_NCCH destructor 106 if (MakeMagic('N', 'C', 'C', 'H') == magic)
113AppLoader_NCCH::~AppLoader_NCCH() { 107 return FileType::CXI;
108
109 return FileType::Error;
114} 110}
115 111
116/**
117 * Loads .code section into memory for booting
118 * @return ResultStatus result of function
119 */
120ResultStatus AppLoader_NCCH::LoadExec() const { 112ResultStatus AppLoader_NCCH::LoadExec() const {
121 if (!is_loaded) 113 if (!is_loaded)
122 return ResultStatus::ErrorNotLoaded; 114 return ResultStatus::ErrorNotLoaded;
@@ -130,189 +122,144 @@ ResultStatus AppLoader_NCCH::LoadExec() const {
130 return ResultStatus::Error; 122 return ResultStatus::Error;
131} 123}
132 124
133/**
134 * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.)
135 * @param name Name of section to read out of NCCH file
136 * @param buffer Vector to read data into
137 * @return ResultStatus result of function
138 */
139ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const { 125ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const {
126 if (!file->IsOpen())
127 return ResultStatus::Error;
128
129 LOG_DEBUG(Loader, "%d sections:", kMaxSections);
140 // Iterate through the ExeFs archive until we find the .code file... 130 // Iterate through the ExeFs archive until we find the .code file...
141 FileUtil::IOFile file(filename, "rb"); 131 for (unsigned section_number = 0; section_number < kMaxSections; section_number++) {
142 if (file.IsOpen()) { 132 const auto& section = exefs_header.section[section_number];
143 LOG_DEBUG(Loader, "%d sections:", kMaxSections); 133
144 for (int i = 0; i < kMaxSections; i++) { 134 // Load the specified section...
145 // Load the specified section... 135 if (strcmp(section.name, name) == 0) {
146 if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { 136 LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", section_number,
147 LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", i, 137 section.offset, section.size, section.name);
148 exefs_header.section[i].offset, exefs_header.section[i].size, 138
149 exefs_header.section[i].name); 139 s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset);
150 140 file->Seek(section_offset, SEEK_SET);
151 s64 section_offset = (exefs_header.section[i].offset + exefs_offset + 141
152 sizeof(ExeFs_Header)+ncch_offset); 142 if (is_compressed) {
153 file.Seek(section_offset, 0); 143 // Section is compressed, read compressed .code section...
154 144 std::unique_ptr<u8[]> temp_buffer;
155 // Section is compressed... 145 try {
156 if (i == 0 && is_compressed) { 146 temp_buffer.reset(new u8[section.size]);
157 // Read compressed .code section... 147 } catch (std::bad_alloc&) {
158 std::unique_ptr<u8[]> temp_buffer; 148 return ResultStatus::ErrorMemoryAllocationFailed;
159 try {
160 temp_buffer.reset(new u8[exefs_header.section[i].size]);
161 } catch (std::bad_alloc&) {
162 return ResultStatus::ErrorMemoryAllocationFailed;
163 }
164 file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size);
165
166 // Decompress .code section...
167 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0],
168 exefs_header.section[i].size);
169 buffer.resize(decompressed_size);
170 if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0],
171 decompressed_size)) {
172 return ResultStatus::ErrorInvalidFormat;
173 }
174 // Section is uncompressed...
175 }
176 else {
177 buffer.resize(exefs_header.section[i].size);
178 file.ReadBytes(&buffer[0], exefs_header.section[i].size);
179 } 149 }
180 return ResultStatus::Success; 150
151 if (file->ReadBytes(&temp_buffer[0], section.size) != section.size)
152 return ResultStatus::Error;
153
154 // Decompress .code section...
155 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], section.size);
156 buffer.resize(decompressed_size);
157 if (!LZSS_Decompress(&temp_buffer[0], section.size, &buffer[0], decompressed_size))
158 return ResultStatus::ErrorInvalidFormat;
159 } else {
160 // Section is uncompressed...
161 buffer.resize(section.size);
162 if (file->ReadBytes(&buffer[0], section.size) != section.size)
163 return ResultStatus::Error;
181 } 164 }
165 return ResultStatus::Success;
182 } 166 }
183 } else {
184 LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
185 return ResultStatus::Error;
186 } 167 }
187 return ResultStatus::ErrorNotUsed; 168 return ResultStatus::ErrorNotUsed;
188} 169}
189 170
190/**
191 * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
192 * @param error_string Pointer to string to put error message if an error has occurred
193 * @todo Move NCSD parsing out of here and create a separate function for loading these
194 * @return True on success, otherwise false
195 */
196ResultStatus AppLoader_NCCH::Load() { 171ResultStatus AppLoader_NCCH::Load() {
197 LOG_INFO(Loader, "Loading NCCH file %s...", filename.c_str());
198
199 if (is_loaded) 172 if (is_loaded)
200 return ResultStatus::ErrorAlreadyLoaded; 173 return ResultStatus::ErrorAlreadyLoaded;
201 174
202 FileUtil::IOFile file(filename, "rb"); 175 if (!file->IsOpen())
203 if (file.IsOpen()) { 176 return ResultStatus::Error;
204 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
205 177
206 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... 178 // Reset read pointer in case this file has been read before.
207 if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) { 179 file->Seek(0, SEEK_SET);
208 LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
209 ncch_offset = 0x4000;
210 file.Seek(ncch_offset, 0);
211 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
212 }
213 180
214 // Verify we are loading the correct file type... 181 if (file->ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header))
215 if (0 != memcmp(&ncch_header.magic, "NCCH", 4)) 182 return ResultStatus::Error;
216 return ResultStatus::ErrorInvalidFormat;
217 183
218 // Read ExHeader... 184 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
185 if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
186 LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
187 ncch_offset = 0x4000;
188 file->Seek(ncch_offset, SEEK_SET);
189 file->ReadBytes(&ncch_header, sizeof(NCCH_Header));
190 }
219 191
220 file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)); 192 // Verify we are loading the correct file type...
193 if (MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic)
194 return ResultStatus::ErrorInvalidFormat;
221 195
222 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; 196 // Read ExHeader...
223 entry_point = exheader_header.codeset_info.text.address;
224 197
225 LOG_INFO(Loader, "Name: %s", exheader_header.codeset_info.name); 198 if (file->ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header))
226 LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no"); 199 return ResultStatus::Error;
227 LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
228 200
229 // Read ExeFS... 201 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
202 entry_point = exheader_header.codeset_info.text.address;
230 203
231 exefs_offset = ncch_header.exefs_offset * kBlockSize; 204 LOG_INFO(Loader, "Name: %s", exheader_header.codeset_info.name);
232 u32 exefs_size = ncch_header.exefs_size * kBlockSize; 205 LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no");
206 LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
233 207
234 LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset); 208 // Read ExeFS...
235 LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size);
236 209
237 file.Seek(exefs_offset + ncch_offset, 0); 210 exefs_offset = ncch_header.exefs_offset * kBlockSize;
238 file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); 211 u32 exefs_size = ncch_header.exefs_size * kBlockSize;
239 212
240 is_loaded = true; // Set state to loaded 213 LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset);
214 LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size);
241 215
242 LoadExec(); // Load the executable into memory for booting 216 file->Seek(exefs_offset + ncch_offset, SEEK_SET);
217 if (file->ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header))
218 return ResultStatus::Error;
243 219
244 return ResultStatus::Success; 220 is_loaded = true; // Set state to loaded
245 } else { 221
246 LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str()); 222 return LoadExec(); // Load the executable into memory for booting
247 }
248 return ResultStatus::Error;
249} 223}
250 224
251/**
252 * Get the code (typically .code section) of the application
253 * @param buffer Reference to buffer to store data
254 * @return ResultStatus result of function
255 */
256ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) const { 225ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) const {
257 return LoadSectionExeFS(".code", buffer); 226 return LoadSectionExeFS(".code", buffer);
258} 227}
259 228
260/**
261 * Get the icon (typically icon section) of the application
262 * @param buffer Reference to buffer to store data
263 * @return ResultStatus result of function
264 */
265ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) const { 229ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) const {
266 return LoadSectionExeFS("icon", buffer); 230 return LoadSectionExeFS("icon", buffer);
267} 231}
268 232
269/**
270 * Get the banner (typically banner section) of the application
271 * @param buffer Reference to buffer to store data
272 * @return ResultStatus result of function
273 */
274ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) const { 233ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) const {
275 return LoadSectionExeFS("banner", buffer); 234 return LoadSectionExeFS("banner", buffer);
276} 235}
277 236
278/**
279 * Get the logo (typically logo section) of the application
280 * @param buffer Reference to buffer to store data
281 * @return ResultStatus result of function
282 */
283ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) const { 237ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) const {
284 return LoadSectionExeFS("logo", buffer); 238 return LoadSectionExeFS("logo", buffer);
285} 239}
286 240
287/**
288 * Get the RomFS of the application
289 * @param buffer Reference to buffer to store data
290 * @return ResultStatus result of function
291 */
292ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { 241ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
293 FileUtil::IOFile file(filename, "rb"); 242 if (!file->IsOpen())
294 if (file.IsOpen()) { 243 return ResultStatus::Error;
295 // Check if the NCCH has a RomFS...
296 if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) {
297 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
298 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
299 244
300 LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset); 245 // Check if the NCCH has a RomFS...
301 LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size); 246 if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) {
247 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
248 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
302 249
303 buffer.resize(romfs_size); 250 LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset);
251 LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size);
304 252
305 file.Seek(romfs_offset, 0); 253 buffer.resize(romfs_size);
306 file.ReadBytes(&buffer[0], romfs_size);
307 254
308 return ResultStatus::Success; 255 file->Seek(romfs_offset, SEEK_SET);
309 } 256 if (file->ReadBytes(&buffer[0], romfs_size) != romfs_size)
310 LOG_DEBUG(Loader, "NCCH has no RomFS"); 257 return ResultStatus::Error;
311 return ResultStatus::ErrorNotUsed; 258
312 } else { 259 return ResultStatus::Success;
313 LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
314 } 260 }
315 return ResultStatus::Error; 261 LOG_DEBUG(Loader, "NCCH has no RomFS");
262 return ResultStatus::ErrorNotUsed;
316} 263}
317 264
318u64 AppLoader_NCCH::GetProgramId() const { 265u64 AppLoader_NCCH::GetProgramId() const {
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index fd9258970..9ae2de99f 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include "common/common.h" 7#include "common/common.h"
8#include "common/file_util.h"
9 8
10#include "core/loader/loader.h" 9#include "core/loader/loader.h"
11 10
@@ -14,7 +13,7 @@
14 13
15struct NCCH_Header { 14struct NCCH_Header {
16 u8 signature[0x100]; 15 u8 signature[0x100];
17 char magic[4]; 16 u32 magic;
18 u32 content_size; 17 u32 content_size;
19 u8 partition_id[8]; 18 u8 partition_id[8];
20 u16 maker_code; 19 u16 maker_code;
@@ -147,8 +146,14 @@ namespace Loader {
147/// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) 146/// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
148class AppLoader_NCCH final : public AppLoader { 147class AppLoader_NCCH final : public AppLoader {
149public: 148public:
150 AppLoader_NCCH(const std::string& filename); 149 AppLoader_NCCH(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { }
151 ~AppLoader_NCCH() override; 150
151 /**
152 * Returns the type of the file
153 * @param file FileUtil::IOFile open file
154 * @return FileType found, or FileType::Error if this loader doesn't know it
155 */
156 static FileType IdentifyType(FileUtil::IOFile& file);
152 157
153 /** 158 /**
154 * Load the application 159 * Load the application
@@ -213,14 +218,11 @@ private:
213 */ 218 */
214 ResultStatus LoadExec() const; 219 ResultStatus LoadExec() const;
215 220
216 std::string filename; 221 bool is_compressed = false;
217
218 bool is_loaded;
219 bool is_compressed;
220 222
221 u32 entry_point; 223 u32 entry_point = 0;
222 u32 ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header 224 u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header
223 u32 exefs_offset; 225 u32 exefs_offset = 0;
224 226
225 NCCH_Header ncch_header; 227 NCCH_Header ncch_header;
226 ExeFs_Header exefs_header; 228 ExeFs_Header exefs_header;