summaryrefslogtreecommitdiff
path: root/src/core/loader
diff options
context:
space:
mode:
authorGravatar bunnei2014-06-25 09:46:14 -0400
committerGravatar bunnei2014-06-25 09:46:14 -0400
commit469fe42fad01fc45e454e6acfa413eeae92e587e (patch)
tree4cf876688cc2d03d34512f8f1a25bc26d853f1fb /src/core/loader
parentMerge pull request #7 from archshift/travis-osx (diff)
parentLoader: Refactored loading functions to only read data from binary if called. (diff)
downloadyuzu-469fe42fad01fc45e454e6acfa413eeae92e587e.tar.gz
yuzu-469fe42fad01fc45e454e6acfa413eeae92e587e.tar.xz
yuzu-469fe42fad01fc45e454e6acfa413eeae92e587e.zip
Merge pull request #22 from bunnei/loader-improvements
Refactor loader code and add preliminary NCCH support
Diffstat (limited to 'src/core/loader')
-rw-r--r--src/core/loader/elf.cpp370
-rw-r--r--src/core/loader/elf.h32
-rw-r--r--src/core/loader/loader.cpp77
-rw-r--r--src/core/loader/loader.h121
-rw-r--r--src/core/loader/ncch.cpp312
-rw-r--r--src/core/loader/ncch.h227
6 files changed, 1139 insertions, 0 deletions
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
new file mode 100644
index 000000000..76c9d6d54
--- /dev/null
+++ b/src/core/loader/elf.cpp
@@ -0,0 +1,370 @@
1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <string>
6#include <memory>
7
8#include "common/common.h"
9#include "common/file_util.h"
10#include "common/symbols.h"
11
12#include "core/mem_map.h"
13#include "core/loader/elf.h"
14#include "core/hle/kernel/kernel.h"
15
16////////////////////////////////////////////////////////////////////////////////////////////////////
17// ELF Header Constants
18
19// File type
20enum ElfType {
21 ET_NONE = 0,
22 ET_REL = 1,
23 ET_EXEC = 2,
24 ET_DYN = 3,
25 ET_CORE = 4,
26 ET_LOPROC = 0xFF00,
27 ET_HIPROC = 0xFFFF,
28};
29
30// Machine/Architecture
31enum ElfMachine {
32 EM_NONE = 0,
33 EM_M32 = 1,
34 EM_SPARC = 2,
35 EM_386 = 3,
36 EM_68K = 4,
37 EM_88K = 5,
38 EM_860 = 7,
39 EM_MIPS = 8
40};
41
42// File version
43#define EV_NONE 0
44#define EV_CURRENT 1
45
46// Identification index
47#define EI_MAG0 0
48#define EI_MAG1 1
49#define EI_MAG2 2
50#define EI_MAG3 3
51#define EI_CLASS 4
52#define EI_DATA 5
53#define EI_VERSION 6
54#define EI_PAD 7
55#define EI_NIDENT 16
56
57// Magic number
58#define ELFMAG0 0x7F
59#define ELFMAG1 'E'
60#define ELFMAG2 'L'
61#define ELFMAG3 'F'
62
63// Sections constants
64
65// Section types
66#define SHT_NULL 0
67#define SHT_PROGBITS 1
68#define SHT_SYMTAB 2
69#define SHT_STRTAB 3
70#define SHT_RELA 4
71#define SHT_HASH 5
72#define SHT_DYNAMIC 6
73#define SHT_NOTE 7
74#define SHT_NOBITS 8
75#define SHT_REL 9
76#define SHT_SHLIB 10
77#define SHT_DYNSYM 11
78#define SHT_LOPROC 0x70000000
79#define SHT_HIPROC 0x7FFFFFFF
80#define SHT_LOUSER 0x80000000
81#define SHT_HIUSER 0xFFFFFFFF
82
83// Section flags
84enum ElfSectionFlags
85{
86 SHF_WRITE = 0x1,
87 SHF_ALLOC = 0x2,
88 SHF_EXECINSTR = 0x4,
89 SHF_MASKPROC = 0xF0000000,
90};
91
92// Segment types
93#define PT_NULL 0
94#define PT_LOAD 1
95#define PT_DYNAMIC 2
96#define PT_INTERP 3
97#define PT_NOTE 4
98#define PT_SHLIB 5
99#define PT_PHDR 6
100#define PT_LOPROC 0x70000000
101#define PT_HIPROC 0x7FFFFFFF
102
103typedef unsigned int Elf32_Addr;
104typedef unsigned short Elf32_Half;
105typedef unsigned int Elf32_Off;
106typedef signed int Elf32_Sword;
107typedef unsigned int Elf32_Word;
108
109////////////////////////////////////////////////////////////////////////////////////////////////////
110// ELF file header
111
112struct Elf32_Ehdr {
113 unsigned char e_ident[EI_NIDENT];
114 Elf32_Half e_type;
115 Elf32_Half e_machine;
116 Elf32_Word e_version;
117 Elf32_Addr e_entry;
118 Elf32_Off e_phoff;
119 Elf32_Off e_shoff;
120 Elf32_Word e_flags;
121 Elf32_Half e_ehsize;
122 Elf32_Half e_phentsize;
123 Elf32_Half e_phnum;
124 Elf32_Half e_shentsize;
125 Elf32_Half e_shnum;
126 Elf32_Half e_shstrndx;
127};
128
129// Section header
130struct Elf32_Shdr {
131 Elf32_Word sh_name;
132 Elf32_Word sh_type;
133 Elf32_Word sh_flags;
134 Elf32_Addr sh_addr;
135 Elf32_Off sh_offset;
136 Elf32_Word sh_size;
137 Elf32_Word sh_link;
138 Elf32_Word sh_info;
139 Elf32_Word sh_addralign;
140 Elf32_Word sh_entsize;
141};
142
143// Segment header
144struct Elf32_Phdr {
145 Elf32_Word p_type;
146 Elf32_Off p_offset;
147 Elf32_Addr p_vaddr;
148 Elf32_Addr p_paddr;
149 Elf32_Word p_filesz;
150 Elf32_Word p_memsz;
151 Elf32_Word p_flags;
152 Elf32_Word p_align;
153};
154
155// Symbol table entry
156struct Elf32_Sym {
157 Elf32_Word st_name;
158 Elf32_Addr st_value;
159 Elf32_Word st_size;
160 unsigned char st_info;
161 unsigned char st_other;
162 Elf32_Half st_shndx;
163};
164
165// Relocation entries
166struct Elf32_Rel {
167 Elf32_Addr r_offset;
168 Elf32_Word r_info;
169};
170
171////////////////////////////////////////////////////////////////////////////////////////////////////
172// ElfReader class
173
174typedef int SectionID;
175
176class ElfReader {
177private:
178 char *base;
179 u32 *base32;
180
181 Elf32_Ehdr *header;
182 Elf32_Phdr *segments;
183 Elf32_Shdr *sections;
184
185 u32 *sectionAddrs;
186 bool relocate;
187 u32 entryPoint;
188
189public:
190 ElfReader(void *ptr);
191 ~ElfReader() { }
192
193 u32 Read32(int off) const { return base32[off >> 2]; }
194
195 // Quick accessors
196 ElfType GetType() const { return (ElfType)(header->e_type); }
197 ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); }
198 u32 GetEntryPoint() const { return entryPoint; }
199 u32 GetFlags() const { return (u32)(header->e_flags); }
200 bool LoadInto(u32 vaddr);
201 bool LoadSymbols();
202
203 int GetNumSegments() const { return (int)(header->e_phnum); }
204 int GetNumSections() const { return (int)(header->e_shnum); }
205 const u8 *GetPtr(int offset) const { return (u8*)base + offset; }
206 const char *GetSectionName(int section) const;
207 const u8 *GetSectionDataPtr(int section) const {
208 if (section < 0 || section >= header->e_shnum)
209 return nullptr;
210 if (sections[section].sh_type != SHT_NOBITS)
211 return GetPtr(sections[section].sh_offset);
212 else
213 return nullptr;
214 }
215 bool IsCodeSection(int section) const {
216 return sections[section].sh_type == SHT_PROGBITS;
217 }
218 const u8 *GetSegmentPtr(int segment) {
219 return GetPtr(segments[segment].p_offset);
220 }
221 u32 GetSectionAddr(SectionID section) const { return sectionAddrs[section]; }
222 int GetSectionSize(SectionID section) const { return sections[section].sh_size; }
223 SectionID GetSectionByName(const char *name, int firstSection = 0) const; //-1 for not found
224
225 bool DidRelocate() {
226 return relocate;
227 }
228};
229
230ElfReader::ElfReader(void *ptr) {
231 base = (char*)ptr;
232 base32 = (u32 *)ptr;
233 header = (Elf32_Ehdr*)ptr;
234
235 segments = (Elf32_Phdr *)(base + header->e_phoff);
236 sections = (Elf32_Shdr *)(base + header->e_shoff);
237
238 entryPoint = header->e_entry;
239
240 LoadSymbols();
241}
242
243const char *ElfReader::GetSectionName(int section) const {
244 if (sections[section].sh_type == SHT_NULL)
245 return nullptr;
246
247 int name_offset = sections[section].sh_name;
248 char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx);
249
250 if (ptr)
251 return ptr + name_offset;
252
253 return nullptr;
254}
255
256bool ElfReader::LoadInto(u32 vaddr) {
257 DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx);
258
259 // Should we relocate?
260 relocate = (header->e_type != ET_EXEC);
261
262 if (relocate) {
263 DEBUG_LOG(MASTER_LOG, "Relocatable module");
264 entryPoint += vaddr;
265 } else {
266 DEBUG_LOG(MASTER_LOG, "Prerelocated executable");
267 }
268 INFO_LOG(MASTER_LOG, "%i segments:", header->e_phnum);
269
270 // First pass : Get the bits into RAM
271 u32 segment_addr[32];
272 u32 base_addr = relocate ? vaddr : 0;
273
274 for (int i = 0; i < header->e_phnum; i++) {
275 Elf32_Phdr *p = segments + i;
276 INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr,
277 p->p_filesz, p->p_memsz);
278
279 if (p->p_type == PT_LOAD) {
280 segment_addr[i] = base_addr + p->p_vaddr;
281 memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz);
282 INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", segment_addr[i],
283 p->p_memsz);
284 }
285 }
286 INFO_LOG(MASTER_LOG, "Done loading.");
287 return true;
288}
289
290SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const {
291 for (int i = firstSection; i < header->e_shnum; i++) {
292 const char *secname = GetSectionName(i);
293
294 if (secname != nullptr && strcmp(name, secname) == 0)
295 return i;
296 }
297 return -1;
298}
299
300bool ElfReader::LoadSymbols() {
301 bool hasSymbols = false;
302 SectionID sec = GetSectionByName(".symtab");
303 if (sec != -1) {
304 int stringSection = sections[sec].sh_link;
305 const char *stringBase = (const char *)GetSectionDataPtr(stringSection);
306
307 //We have a symbol table!
308 Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec));
309 int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym);
310 for (int sym = 0; sym < numSymbols; sym++) {
311 int size = symtab[sym].st_size;
312 if (size == 0)
313 continue;
314
315 int type = symtab[sym].st_info & 0xF;
316
317 const char *name = stringBase + symtab[sym].st_name;
318
319 Symbols::Add(symtab[sym].st_value, name, size, type);
320
321 hasSymbols = true;
322 }
323 }
324
325 return hasSymbols;
326}
327
328////////////////////////////////////////////////////////////////////////////////////////////////////
329// Loader namespace
330
331namespace Loader {
332
333/// AppLoader_ELF constructor
334AppLoader_ELF::AppLoader_ELF(const std::string& filename) : is_loaded(false) {
335 this->filename = filename;
336}
337
338/// AppLoader_NCCH destructor
339AppLoader_ELF::~AppLoader_ELF() {
340}
341
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() {
349 INFO_LOG(LOADER, "Loading ELF file %s...", filename.c_str());
350
351 if (is_loaded)
352 return ResultStatus::ErrorAlreadyLoaded;
353
354 File::IOFile file(filename, "rb");
355
356 if (file.IsOpen()) {
357 u32 size = (u32)file.GetSize();
358 std::unique_ptr<u8[]> buffer(new u8[size]);
359 file.ReadBytes(&buffer[0], size);
360
361 ElfReader elf_reader(&buffer[0]);
362 elf_reader.LoadInto(0x00100000);
363 Kernel::LoadExec(elf_reader.GetEntryPoint());
364 } else {
365 return ResultStatus::Error;
366 }
367 return ResultStatus::Success;
368}
369
370} // namespace Loader
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
new file mode 100644
index 000000000..d3cbf414d
--- /dev/null
+++ b/src/core/loader/elf.h
@@ -0,0 +1,32 @@
1// Copyright 2013 Dolphin Emulator Project / Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/loader/loader.h"
9
10////////////////////////////////////////////////////////////////////////////////////////////////////
11// Loader namespace
12
13namespace Loader {
14
15/// Loads an ELF/AXF file
16class AppLoader_ELF : public AppLoader {
17public:
18 AppLoader_ELF(const std::string& filename);
19 ~AppLoader_ELF();
20
21 /**
22 * Load the bootable file
23 * @return ResultStatus result of function
24 */
25 ResultStatus Load();
26
27private:
28 std::string filename;
29 bool is_loaded;
30};
31
32} // namespace Loader
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
new file mode 100644
index 000000000..96cb81de0
--- /dev/null
+++ b/src/core/loader/loader.cpp
@@ -0,0 +1,77 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <memory>
6
7#include "core/loader/loader.h"
8#include "core/loader/elf.h"
9#include "core/loader/ncch.h"
10
11////////////////////////////////////////////////////////////////////////////////////////////////////
12
13namespace Loader {
14
15/**
16 * Identifies the type of a bootable file
17 * @param filename String filename of bootable file
18 * @todo (ShizZy) this function sucks... make it actually check file contents etc.
19 * @return FileType of file
20 */
21FileType IdentifyFile(const std::string &filename) {
22 if (filename.size() == 0) {
23 ERROR_LOG(LOADER, "invalid filename %s", filename.c_str());
24 return FileType::Error;
25 }
26 std::string extension = filename.size() >= 5 ? filename.substr(filename.size() - 4) : "";
27
28 if (!strcasecmp(extension.c_str(), ".elf")) {
29 return FileType::ELF; // TODO(bunnei): Do some filetype checking :p
30 }
31 else if (!strcasecmp(extension.c_str(), ".axf")) {
32 return FileType::ELF; // TODO(bunnei): Do some filetype checking :p
33 }
34 else if (!strcasecmp(extension.c_str(), ".cxi")) {
35 return FileType::CXI; // TODO(bunnei): Do some filetype checking :p
36 }
37 else if (!strcasecmp(extension.c_str(), ".cci")) {
38 return FileType::CCI; // TODO(bunnei): Do some filetype checking :p
39 }
40 return FileType::Unknown;
41}
42
43/**
44 * Identifies and loads a bootable file
45 * @param filename String filename of bootable file
46 * @return ResultStatus result of function
47 */
48ResultStatus LoadFile(const std::string& filename) {
49 INFO_LOG(LOADER, "Loading file %s...", filename.c_str());
50
51 switch (IdentifyFile(filename)) {
52
53 // Standard ELF file format...
54 case FileType::ELF: {
55 return AppLoader_ELF(filename).Load();
56 }
57
58 // NCCH/NCSD container formats...
59 case FileType::CXI:
60 case FileType::CCI: {
61 return AppLoader_NCCH(filename).Load();
62 }
63
64 // Error occurred durring IdentifyFile...
65 case FileType::Error:
66
67 // IdentifyFile could know identify file type...
68 case FileType::Unknown:
69
70 default:
71 return ResultStatus::ErrorInvalidFormat;
72 }
73
74 return ResultStatus::Error;
75}
76
77} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
new file mode 100644
index 000000000..95f16fcb1
--- /dev/null
+++ b/src/core/loader/loader.h
@@ -0,0 +1,121 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8
9#include "common/common.h"
10
11////////////////////////////////////////////////////////////////////////////////////////////////////
12// Loader namespace
13
14namespace Loader {
15
16/// File types supported by CTR
17enum class FileType {
18 Error,
19 Unknown,
20 CCI,
21 CXI,
22 CIA,
23 ELF,
24};
25
26/// Return type for functions in Loader namespace
27enum class ResultStatus {
28 Success,
29 Error,
30 ErrorInvalidFormat,
31 ErrorNotImplemented,
32 ErrorNotLoaded,
33 ErrorNotUsed,
34 ErrorAlreadyLoaded,
35};
36
37/// Interface for loading an application
38class AppLoader : NonCopyable {
39public:
40 AppLoader() { }
41 virtual ~AppLoader() { }
42
43 /**
44 * Load the application
45 * @return ResultStatus result of function
46 */
47 virtual ResultStatus Load() = 0;
48
49 /**
50 * Get the code (typically .code section) of the application
51 * @param error ResultStatus result of function
52 * @return Reference to code buffer
53 */
54 virtual const std::vector<u8>& ReadCode(ResultStatus& error) const {
55 error = ResultStatus::ErrorNotImplemented;
56 return code;
57 }
58
59 /**
60 * Get the icon (typically icon section) of the application
61 * @param error ResultStatus result of function
62 * @return Reference to icon buffer
63 */
64 virtual const std::vector<u8>& ReadIcon(ResultStatus& error) const {
65 error = ResultStatus::ErrorNotImplemented;
66 return icon;
67 }
68
69 /**
70 * Get the banner (typically banner section) of the application
71 * @param error ResultStatus result of function
72 * @return Reference to banner buffer
73 */
74 virtual const std::vector<u8>& ReadBanner(ResultStatus& error) const {
75 error = ResultStatus::ErrorNotImplemented;
76 return banner;
77 }
78
79 /**
80 * Get the logo (typically logo section) of the application
81 * @param error ResultStatus result of function
82 * @return Reference to logo buffer
83 */
84 virtual const std::vector<u8>& ReadLogo(ResultStatus& error) const {
85 error = ResultStatus::ErrorNotImplemented;
86 return logo;
87 }
88
89 /**
90 * Get the RomFs archive of the application
91 * @param error ResultStatus result of function
92 * @return Reference to RomFs archive buffer
93 */
94 virtual const std::vector<u8>& ReadRomFS(ResultStatus& error) const {
95 error = ResultStatus::ErrorNotImplemented;
96 return romfs;
97 }
98
99protected:
100 std::vector<u8> code; ///< ExeFS .code section
101 std::vector<u8> icon; ///< ExeFS .icon section
102 std::vector<u8> banner; ///< ExeFS .banner section
103 std::vector<u8> logo; ///< ExeFS .logo section
104 std::vector<u8> romfs; ///< RomFs archive
105};
106
107/**
108 * Identifies the type of a bootable file
109 * @param filename String filename of bootable file
110 * @return FileType of file
111 */
112FileType IdentifyFile(const std::string &filename);
113
114/**
115 * Identifies and loads a bootable file
116 * @param filename String filename of bootable file
117 * @return ResultStatus result of function
118 */
119ResultStatus LoadFile(const std::string& filename);
120
121} // namespace
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
new file mode 100644
index 000000000..60505bdfa
--- /dev/null
+++ b/src/core/loader/ncch.cpp
@@ -0,0 +1,312 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <memory>
6
7#include "common/file_util.h"
8
9#include "core/loader/ncch.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/mem_map.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// Loader namespace
15
16namespace Loader {
17
18static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs
19static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
20
21/**
22 * Get the decompressed size of an LZSS compressed ExeFS file
23 * @param buffer Buffer of compressed file
24 * @param size Size of compressed buffer
25 * @return Size of decompressed buffer
26 */
27u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
28 u32 offset_size = *(u32*)(buffer + size - 4);
29 return offset_size + size;
30}
31
32/**
33 * Decompress ExeFS file (compressed with LZSS)
34 * @param compressed Compressed buffer
35 * @param compressed_size Size of compressed buffer
36 * @param decompressed Decompressed buffer
37 * @param decompressed_size Size of decompressed buffer
38 * @return True on success, otherwise false
39 */
40bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) {
41 u8* footer = compressed + compressed_size - 8;
42 u32 buffer_top_and_bottom = *(u32*)footer;
43 u32 i, j;
44 u32 out = decompressed_size;
45 u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF);
46 u8 control;
47 u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF);
48
49 memset(decompressed, 0, decompressed_size);
50 memcpy(decompressed, compressed, compressed_size);
51
52 while(index > stop_index) {
53 control = compressed[--index];
54
55 for(i = 0; i < 8; i++) {
56 if(index <= stop_index)
57 break;
58 if(index <= 0)
59 break;
60 if(out <= 0)
61 break;
62
63 if(control & 0x80) {
64 // Check if compression is out of bounds
65 if(index < 2) {
66 return false;
67 }
68 index -= 2;
69
70 u32 segment_offset = compressed[index] | (compressed[index + 1] << 8);
71 u32 segment_size = ((segment_offset >> 12) & 15) + 3;
72 segment_offset &= 0x0FFF;
73 segment_offset += 2;
74
75 // Check if compression is out of bounds
76 if(out < segment_size) {
77 return false;
78 }
79 for(j = 0; j < segment_size; j++) {
80 u8 data;
81 // Check if compression is out of bounds
82 if(out + segment_offset >= decompressed_size) {
83 return false;
84 }
85 data = decompressed[out + segment_offset];
86 decompressed[--out] = data;
87 }
88 } else {
89 // Check if compression is out of bounds
90 if(out < 1) {
91 return false;
92 }
93 decompressed[--out] = compressed[--index];
94 }
95 control <<= 1;
96 }
97 }
98 return true;
99}
100
101////////////////////////////////////////////////////////////////////////////////////////////////////
102// AppLoader_NCCH class
103
104/// AppLoader_NCCH constructor
105AppLoader_NCCH::AppLoader_NCCH(const 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 if (file.IsOpen())
117 file.Close();
118}
119
120/**
121 * Loads .code section into memory for booting
122 * @return ResultStatus result of function
123 */
124ResultStatus AppLoader_NCCH::LoadExec() {
125 if (!is_loaded)
126 return ResultStatus::ErrorNotLoaded;
127
128 ResultStatus res;
129 code = ReadCode(res);
130
131 if (ResultStatus::Success == res) {
132 Memory::WriteBlock(entry_point, &code[0], code.size());
133 Kernel::LoadExec(entry_point);
134 }
135 return res;
136}
137
138/**
139 * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.)
140 * @param name Name of section to read out of NCCH file
141 * @param buffer Vector to read data into
142 * @param error ResultStatus result of function
143 * @return Reference to buffer of data that was read
144 */
145const std::vector<u8>& AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer,
146 ResultStatus& error) {
147 // Iterate through the ExeFs archive until we find the .code file...
148 for (int i = 0; i < kMaxSections; i++) {
149 // Load the specified section...
150 if (strcmp((const char*)exefs_header.section[i].name, name) == 0) {
151 INFO_LOG(LOADER, "ExeFS section %d:", i);
152 INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name);
153 INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset);
154 INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size);
155
156 s64 section_offset = (exefs_header.section[i].offset + exefs_offset +
157 sizeof(ExeFs_Header) + ncch_offset);
158 file.Seek(section_offset, 0);
159
160 // Section is compressed...
161 if (i == 0 && is_compressed) {
162 // Read compressed .code section...
163 std::unique_ptr<u8[]> temp_buffer(new u8[exefs_header.section[i].size]);
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 error = ResultStatus::ErrorInvalidFormat;
173 return buffer;
174 }
175 // Section is uncompressed...
176 } else {
177 buffer.resize(exefs_header.section[i].size);
178 file.ReadBytes(&buffer[0], exefs_header.section[i].size);
179 }
180 error = ResultStatus::Success;
181 return buffer;
182 }
183 }
184 error = ResultStatus::ErrorNotUsed;
185 return buffer;
186}
187
188/**
189 * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
190 * @param error_string Pointer to string to put error message if an error has occurred
191 * @todo Move NCSD parsing out of here and create a separate function for loading these
192 * @return True on success, otherwise false
193 */
194ResultStatus AppLoader_NCCH::Load() {
195 INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str());
196
197 if (is_loaded)
198 return ResultStatus::ErrorAlreadyLoaded;
199
200 file = File::IOFile(filename, "rb");
201
202 if (file.IsOpen()) {
203 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
204
205 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
206 if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) {
207 WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!");
208 ncch_offset = 0x4000;
209 file.Seek(ncch_offset, 0);
210 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
211 }
212
213 // Verify we are loading the correct file type...
214 if (0 != memcmp(&ncch_header.magic, "NCCH", 4))
215 return ResultStatus::ErrorInvalidFormat;
216
217 // Read ExHeader...
218
219 file.ReadBytes(&exheader_header, sizeof(ExHeader_Header));
220
221 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
222 entry_point = exheader_header.codeset_info.text.address;
223
224 INFO_LOG(LOADER, "Name: %s", exheader_header.codeset_info.name);
225 INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no");
226 INFO_LOG(LOADER, "Entry point: 0x%08X", entry_point);
227
228 // Read ExeFS...
229
230 exefs_offset = ncch_header.exefs_offset * kBlockSize;
231 u32 exefs_size = ncch_header.exefs_size * kBlockSize;
232
233 INFO_LOG(LOADER, "ExeFS offset: 0x%08X", exefs_offset);
234 INFO_LOG(LOADER, "ExeFS size: 0x%08X", exefs_size);
235
236 file.Seek(exefs_offset + ncch_offset, 0);
237 file.ReadBytes(&exefs_header, sizeof(ExeFs_Header));
238
239 is_loaded = true; // Set state to loaded
240
241 LoadExec(); // Load the executable into memory for booting
242
243 return ResultStatus::Success;
244 }
245 return ResultStatus::Error;
246}
247
248/**
249 * Get the code (typically .code section) of the application
250 * @param error ResultStatus result of function
251 * @return Reference to code buffer
252 */
253const std::vector<u8>& AppLoader_NCCH::ReadCode(ResultStatus& error) {
254 return LoadSectionExeFS(".code", code, error);
255}
256
257/**
258 * Get the icon (typically icon section) of the application
259 * @param error ResultStatus result of function
260 * @return Reference to icon buffer
261 */
262const std::vector<u8>& AppLoader_NCCH::ReadIcon(ResultStatus& error) {
263 return LoadSectionExeFS("icon", icon, error);
264}
265
266/**
267 * Get the banner (typically banner section) of the application
268 * @param error ResultStatus result of function
269 * @return Reference to banner buffer
270 */
271const std::vector<u8>& AppLoader_NCCH::ReadBanner(ResultStatus& error) {
272 return LoadSectionExeFS("banner", banner, error);
273}
274
275/**
276 * Get the logo (typically logo section) of the application
277 * @param error ResultStatus result of function
278 * @return Reference to logo buffer
279 */
280const std::vector<u8>& AppLoader_NCCH::ReadLogo(ResultStatus& error) {
281 return LoadSectionExeFS("logo", logo, error);
282}
283
284/**
285 * Get the RomFs archive of the application
286 * @param error ResultStatus result of function
287 * @return Reference to RomFs archive buffer
288 */
289const std::vector<u8>& AppLoader_NCCH::ReadRomFS(ResultStatus& error) {
290 // Check if the NCCH has a RomFS...
291 if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) {
292 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
293 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
294
295 INFO_LOG(LOADER, "RomFS offset: 0x%08X", romfs_offset);
296 INFO_LOG(LOADER, "RomFS size: 0x%08X", romfs_size);
297
298 romfs.resize(romfs_size);
299
300 file.Seek(romfs_offset, 0);
301 file.ReadBytes(&romfs[0], romfs_size);
302
303 error = ResultStatus::Success;
304 return romfs;
305 } else {
306 NOTICE_LOG(LOADER, "RomFS unused");
307 }
308 error = ResultStatus::ErrorNotUsed;
309 return romfs;
310}
311
312} // namespace Loader
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
new file mode 100644
index 000000000..bf65425a4
--- /dev/null
+++ b/src/core/loader/ncch.h
@@ -0,0 +1,227 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common.h"
8#include "common/file_util.h"
9
10#include "core/loader/loader.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13/// NCCH header (Note: "NCCH" appears to be a publically unknown acronym)
14
15struct NCCH_Header {
16 u8 signature[0x100];
17 char magic[4];
18 u32 content_size;
19 u8 partition_id[8];
20 u16 maker_code;
21 u16 version;
22 u8 reserved_0[4];
23 u8 program_id[8];
24 u8 temp_flag;
25 u8 reserved_1[0x2f];
26 u8 product_code[0x10];
27 u8 extended_header_hash[0x20];
28 u32 extended_header_size;
29 u8 reserved_2[4];
30 u8 flags[8];
31 u32 plain_region_offset;
32 u32 plain_region_size;
33 u8 reserved_3[8];
34 u32 exefs_offset;
35 u32 exefs_size;
36 u32 exefs_hash_region_size;
37 u8 reserved_4[4];
38 u32 romfs_offset;
39 u32 romfs_size;
40 u32 romfs_hash_region_size;
41 u8 reserved_5[4];
42 u8 exefs_super_block_hash[0x20];
43 u8 romfs_super_block_hash[0x20];
44};
45
46////////////////////////////////////////////////////////////////////////////////////////////////////
47// ExeFS (executable file system) headers
48
49typedef struct {
50 char name[8];
51 u32 offset;
52 u32 size;
53} ExeFs_SectionHeader;
54
55typedef struct {
56 ExeFs_SectionHeader section[8];
57 u8 reserved[0x80];
58 u8 hashes[8][0x20];
59} ExeFs_Header;
60
61////////////////////////////////////////////////////////////////////////////////////////////////////
62// ExHeader (executable file system header) headers
63
64struct ExHeader_SystemInfoFlags{
65 u8 reserved[5];
66 u8 flag;
67 u8 remaster_version[2];
68};
69
70struct ExHeader_CodeSegmentInfo{
71 u32 address;
72 u32 num_max_pages;
73 u32 code_size;
74};
75
76struct ExHeader_CodeSetInfo {
77 u8 name[8];
78 ExHeader_SystemInfoFlags flags;
79 ExHeader_CodeSegmentInfo text;
80 u8 stacksize[4];
81 ExHeader_CodeSegmentInfo ro;
82 u8 reserved[4];
83 ExHeader_CodeSegmentInfo data;
84 u8 bsssize[4];
85};
86
87struct ExHeader_DependencyList{
88 u8 program_id[0x30][8];
89};
90
91struct ExHeader_SystemInfo{
92 u32 save_data_size;
93 u8 reserved[4];
94 u8 jump_id[8];
95 u8 reserved_2[0x30];
96};
97
98struct ExHeader_StorageInfo{
99 u8 ext_save_data_id[8];
100 u8 system_save_data_id[8];
101 u8 reserved[8];
102 u8 access_info[7];
103 u8 other_attributes;
104};
105
106struct ExHeader_ARM11_SystemLocalCaps{
107 u8 program_id[8];
108 u8 flags[8];
109 u8 resource_limit_descriptor[0x10][2];
110 ExHeader_StorageInfo storage_info;
111 u8 service_access_control[0x20][8];
112 u8 reserved[0x1f];
113 u8 resource_limit_category;
114};
115
116struct ExHeader_ARM11_KernelCaps{
117 u8 descriptors[28][4];
118 u8 reserved[0x10];
119};
120
121struct ExHeader_ARM9_AccessControl{
122 u8 descriptors[15];
123 u8 descversion;
124};
125
126struct ExHeader_Header{
127 ExHeader_CodeSetInfo codeset_info;
128 ExHeader_DependencyList dependency_list;
129 ExHeader_SystemInfo system_info;
130 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
131 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
132 ExHeader_ARM9_AccessControl arm9_access_control;
133 struct {
134 u8 signature[0x100];
135 u8 ncch_public_key_modulus[0x100];
136 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
137 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
138 ExHeader_ARM9_AccessControl arm9_access_control;
139 } access_desc;
140};
141
142////////////////////////////////////////////////////////////////////////////////////////////////////
143// Loader namespace
144
145namespace Loader {
146
147/// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
148class AppLoader_NCCH : public AppLoader {
149public:
150 AppLoader_NCCH(const std::string& filename);
151 ~AppLoader_NCCH();
152
153 /**
154 * Load the application
155 * @return ResultStatus result of function
156 */
157 ResultStatus Load();
158
159 /**
160 * Get the code (typically .code section) of the application
161 * @param error ResultStatus result of function
162 * @return Reference to code buffer
163 */
164 const std::vector<u8>& ReadCode(ResultStatus& error);
165
166 /**
167 * Get the icon (typically icon section) of the application
168 * @param error ResultStatus result of function
169 * @return Reference to icon buffer
170 */
171 const std::vector<u8>& ReadIcon(ResultStatus& error);
172
173 /**
174 * Get the banner (typically banner section) of the application
175 * @param error ResultStatus result of function
176 * @return Reference to banner buffer
177 */
178 const std::vector<u8>& ReadBanner(ResultStatus& error);
179
180 /**
181 * Get the logo (typically logo section) of the application
182 * @param error ResultStatus result of function
183 * @return Reference to logo buffer
184 */
185 const std::vector<u8>& ReadLogo(ResultStatus& error);
186
187 /**
188 * Get the RomFs archive of the application
189 * @param error ResultStatus result of function
190 * @return Reference to RomFs archive buffer
191 */
192 const std::vector<u8>& ReadRomFS(ResultStatus& error);
193
194private:
195
196 /**
197 * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.)
198 * @param name Name of section to read out of NCCH file
199 * @param buffer Vector to read data into
200 * @param error ResultStatus result of function
201 * @return Reference to buffer of data that was read
202 */
203 const std::vector<u8>& LoadSectionExeFS(const char* name, std::vector<u8>& buffer,
204 ResultStatus& error);
205
206 /**
207 * Loads .code section into memory for booting
208 * @return ResultStatus result of function
209 */
210 ResultStatus LoadExec();
211
212 File::IOFile file;
213 std::string filename;
214
215 bool is_loaded;
216 bool is_compressed;
217
218 u32 entry_point;
219 u32 ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header
220 u32 exefs_offset;
221
222 NCCH_Header ncch_header;
223 ExeFs_Header exefs_header;
224 ExHeader_Header exheader_header;
225};
226
227} // namespace Loader