Browse Source
Merge pull request #22 from bunnei/loader-improvements
Merge pull request #22 from bunnei/loader-improvements
Refactor loader code and add preliminary NCCH supportpull/15/merge
19 changed files with 1202 additions and 924 deletions
-
25src/citra/citra.cpp
-
2src/citra_qt/bootmanager.cpp
-
10src/citra_qt/main.cpp
-
11src/core/CMakeLists.txt
-
11src/core/core.vcxproj
-
35src/core/core.vcxproj.filters
-
190src/core/elf/elf_reader.cpp
-
75src/core/elf/elf_reader.h
-
281src/core/elf/elf_types.h
-
275src/core/loader.cpp
-
54src/core/loader.h
-
370src/core/loader/elf.cpp
-
32src/core/loader/elf.h
-
77src/core/loader/loader.cpp
-
121src/core/loader/loader.h
-
312src/core/loader/ncch.cpp
-
227src/core/loader/ncch.h
-
2src/core/mem_map.h
-
12src/core/mem_map_funcs.cpp
@ -1,190 +0,0 @@ |
|||
// Copyright 2013 Dolphin Emulator Project
|
|||
// Licensed under GPLv2
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <string>
|
|||
|
|||
#include "common/common.h"
|
|||
|
|||
#include "common/symbols.h"
|
|||
#include "core/mem_map.h"
|
|||
#include "core/elf/elf_reader.h"
|
|||
|
|||
//void bswap(Elf32_Word &w) {w = Common::swap32(w);}
|
|||
//void bswap(Elf32_Half &w) {w = Common::swap16(w);}
|
|||
|
|||
#define bswap(w) w // Dirty bswap disable for now... 3DS is little endian, anyway
|
|||
|
|||
static void byteswapHeader(Elf32_Ehdr &ELF_H) |
|||
{ |
|||
bswap(ELF_H.e_type); |
|||
bswap(ELF_H.e_machine); |
|||
bswap(ELF_H.e_ehsize); |
|||
bswap(ELF_H.e_phentsize); |
|||
bswap(ELF_H.e_phnum); |
|||
bswap(ELF_H.e_shentsize); |
|||
bswap(ELF_H.e_shnum); |
|||
bswap(ELF_H.e_shstrndx); |
|||
bswap(ELF_H.e_version); |
|||
bswap(ELF_H.e_entry); |
|||
bswap(ELF_H.e_phoff); |
|||
bswap(ELF_H.e_shoff); |
|||
bswap(ELF_H.e_flags); |
|||
} |
|||
|
|||
static void byteswapSegment(Elf32_Phdr &sec) |
|||
{ |
|||
bswap(sec.p_align); |
|||
bswap(sec.p_filesz); |
|||
bswap(sec.p_flags); |
|||
bswap(sec.p_memsz); |
|||
bswap(sec.p_offset); |
|||
bswap(sec.p_paddr); |
|||
bswap(sec.p_vaddr); |
|||
bswap(sec.p_type); |
|||
} |
|||
|
|||
static void byteswapSection(Elf32_Shdr &sec) |
|||
{ |
|||
bswap(sec.sh_addr); |
|||
bswap(sec.sh_addralign); |
|||
bswap(sec.sh_entsize); |
|||
bswap(sec.sh_flags); |
|||
bswap(sec.sh_info); |
|||
bswap(sec.sh_link); |
|||
bswap(sec.sh_name); |
|||
bswap(sec.sh_offset); |
|||
bswap(sec.sh_size); |
|||
bswap(sec.sh_type); |
|||
} |
|||
|
|||
ElfReader::ElfReader(void *ptr) |
|||
{ |
|||
base = (char*)ptr; |
|||
base32 = (u32 *)ptr; |
|||
header = (Elf32_Ehdr*)ptr; |
|||
byteswapHeader(*header); |
|||
|
|||
segments = (Elf32_Phdr *)(base + header->e_phoff); |
|||
sections = (Elf32_Shdr *)(base + header->e_shoff); |
|||
|
|||
entryPoint = header->e_entry; |
|||
|
|||
LoadSymbols(); |
|||
} |
|||
|
|||
const char *ElfReader::GetSectionName(int section) const |
|||
{ |
|||
if (sections[section].sh_type == SHT_NULL) |
|||
return nullptr; |
|||
|
|||
int nameOffset = sections[section].sh_name; |
|||
char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); |
|||
|
|||
if (ptr) |
|||
return ptr + nameOffset; |
|||
else |
|||
return nullptr; |
|||
} |
|||
|
|||
bool ElfReader::LoadInto(u32 vaddr) |
|||
{ |
|||
DEBUG_LOG(MASTER_LOG,"String section: %i", header->e_shstrndx); |
|||
|
|||
// Should we relocate?
|
|||
bRelocate = (header->e_type != ET_EXEC); |
|||
|
|||
if (bRelocate) |
|||
{ |
|||
DEBUG_LOG(MASTER_LOG,"Relocatable module"); |
|||
entryPoint += vaddr; |
|||
} |
|||
else |
|||
{ |
|||
DEBUG_LOG(MASTER_LOG,"Prerelocated executable"); |
|||
} |
|||
|
|||
INFO_LOG(MASTER_LOG,"%i segments:", header->e_phnum); |
|||
|
|||
// First pass : Get the bits into RAM
|
|||
u32 segmentVAddr[32]; |
|||
|
|||
u32 baseAddress = bRelocate?vaddr:0; |
|||
|
|||
for (int i = 0; i < header->e_phnum; i++) |
|||
{ |
|||
Elf32_Phdr *p = segments + i; |
|||
|
|||
INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz); |
|||
|
|||
if (p->p_type == PT_LOAD) |
|||
{ |
|||
segmentVAddr[i] = baseAddress + p->p_vaddr; |
|||
u32 writeAddr = segmentVAddr[i]; |
|||
|
|||
const u8 *src = GetSegmentPtr(i); |
|||
u8 *dst = Memory::GetPointer(writeAddr); |
|||
u32 srcSize = p->p_filesz; |
|||
u32 dstSize = p->p_memsz; |
|||
u32 *s = (u32*)src; |
|||
u32 *d = (u32*)dst; |
|||
for (int j = 0; j < (int)(srcSize + 3) / 4; j++) |
|||
{ |
|||
*d++ = /*_byteswap_ulong*/(*s++); |
|||
} |
|||
if (srcSize < dstSize) |
|||
{ |
|||
//memset(dst + srcSize, 0, dstSize-srcSize); //zero out bss
|
|||
} |
|||
INFO_LOG(MASTER_LOG,"Loadable Segment Copied to %08x, size %08x", writeAddr, p->p_memsz); |
|||
} |
|||
} |
|||
|
|||
|
|||
INFO_LOG(MASTER_LOG,"Done loading."); |
|||
return true; |
|||
} |
|||
|
|||
SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const |
|||
{ |
|||
for (int i = firstSection; i < header->e_shnum; i++) |
|||
{ |
|||
const char *secname = GetSectionName(i); |
|||
|
|||
if (secname != nullptr && strcmp(name, secname) == 0) |
|||
return i; |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
bool ElfReader::LoadSymbols() |
|||
{ |
|||
bool hasSymbols = false; |
|||
SectionID sec = GetSectionByName(".symtab"); |
|||
if (sec != -1) |
|||
{ |
|||
int stringSection = sections[sec].sh_link; |
|||
const char *stringBase = (const char *)GetSectionDataPtr(stringSection); |
|||
|
|||
//We have a symbol table!
|
|||
Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); |
|||
int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); |
|||
for (int sym = 0; sym < numSymbols; sym++) |
|||
{ |
|||
int size = symtab[sym].st_size; |
|||
if (size == 0) |
|||
continue; |
|||
|
|||
// int bind = symtab[sym].st_info >> 4;
|
|||
int type = symtab[sym].st_info & 0xF; |
|||
|
|||
const char *name = stringBase + symtab[sym].st_name; |
|||
|
|||
Symbols::Add(symtab[sym].st_value, name, size, type); |
|||
|
|||
hasSymbols = true; |
|||
} |
|||
} |
|||
|
|||
return hasSymbols; |
|||
} |
|||
@ -1,75 +0,0 @@ |
|||
// Copyright 2013 Dolphin Emulator Project |
|||
// Licensed under GPLv2 |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include "core/elf/elf_types.h" |
|||
|
|||
enum KnownElfTypes |
|||
{ |
|||
KNOWNELF_PSP = 0, |
|||
KNOWNELF_DS = 1, |
|||
KNOWNELF_GBA = 2, |
|||
KNOWNELF_GC = 3, |
|||
}; |
|||
|
|||
typedef int SectionID; |
|||
|
|||
class ElfReader |
|||
{ |
|||
private: |
|||
char *base; |
|||
u32 *base32; |
|||
|
|||
Elf32_Ehdr *header; |
|||
Elf32_Phdr *segments; |
|||
Elf32_Shdr *sections; |
|||
|
|||
u32 *sectionAddrs; |
|||
bool bRelocate; |
|||
u32 entryPoint; |
|||
|
|||
public: |
|||
ElfReader(void *ptr); |
|||
~ElfReader() { } |
|||
|
|||
u32 Read32(int off) const { return base32[off>>2]; } |
|||
|
|||
// Quick accessors |
|||
ElfType GetType() const { return (ElfType)(header->e_type); } |
|||
ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } |
|||
u32 GetEntryPoint() const { return entryPoint; } |
|||
u32 GetFlags() const { return (u32)(header->e_flags); } |
|||
bool LoadInto(u32 vaddr); |
|||
bool LoadSymbols(); |
|||
|
|||
int GetNumSegments() const { return (int)(header->e_phnum); } |
|||
int GetNumSections() const { return (int)(header->e_shnum); } |
|||
const u8 *GetPtr(int offset) const { return (u8*)base + offset; } |
|||
const char *GetSectionName(int section) const; |
|||
const u8 *GetSectionDataPtr(int section) const |
|||
{ |
|||
if (section < 0 || section >= header->e_shnum) |
|||
return nullptr; |
|||
if (sections[section].sh_type != SHT_NOBITS) |
|||
return GetPtr(sections[section].sh_offset); |
|||
else |
|||
return nullptr; |
|||
} |
|||
bool IsCodeSection(int section) const |
|||
{ |
|||
return sections[section].sh_type == SHT_PROGBITS; |
|||
} |
|||
const u8 *GetSegmentPtr(int segment) |
|||
{ |
|||
return GetPtr(segments[segment].p_offset); |
|||
} |
|||
u32 GetSectionAddr(SectionID section) const { return sectionAddrs[section]; } |
|||
int GetSectionSize(SectionID section) const { return sections[section].sh_size; } |
|||
SectionID GetSectionByName(const char *name, int firstSection = 0) const; //-1 for not found |
|||
|
|||
bool DidRelocate() { |
|||
return bRelocate; |
|||
} |
|||
}; |
|||
@ -1,281 +0,0 @@ |
|||
// Copyright 2013 Dolphin Emulator Project |
|||
// Licensed under GPLv2 |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
// ELF Header Constants |
|||
|
|||
// File type |
|||
enum ElfType |
|||
{ |
|||
ET_NONE = 0, |
|||
ET_REL = 1, |
|||
ET_EXEC = 2, |
|||
ET_DYN = 3, |
|||
ET_CORE = 4, |
|||
ET_LOPROC = 0xFF00, |
|||
ET_HIPROC = 0xFFFF, |
|||
}; |
|||
|
|||
// Machine/Architecture |
|||
enum ElfMachine |
|||
{ |
|||
EM_NONE = 0, |
|||
EM_M32 = 1, |
|||
EM_SPARC = 2, |
|||
EM_386 = 3, |
|||
EM_68K = 4, |
|||
EM_88K = 5, |
|||
EM_860 = 7, |
|||
EM_MIPS = 8 |
|||
}; |
|||
|
|||
// File version |
|||
#define EV_NONE 0 |
|||
#define EV_CURRENT 1 |
|||
|
|||
// Identification index |
|||
#define EI_MAG0 0 |
|||
#define EI_MAG1 1 |
|||
#define EI_MAG2 2 |
|||
#define EI_MAG3 3 |
|||
#define EI_CLASS 4 |
|||
#define EI_DATA 5 |
|||
#define EI_VERSION 6 |
|||
#define EI_PAD 7 |
|||
#define EI_NIDENT 16 |
|||
|
|||
// Magic number |
|||
#define ELFMAG0 0x7F |
|||
#define ELFMAG1 'E' |
|||
#define ELFMAG2 'L' |
|||
#define ELFMAG3 'F' |
|||
|
|||
// File class |
|||
#define ELFCLASSNONE 0 |
|||
#define ELFCLASS32 1 |
|||
#define ELFCLASS64 2 |
|||
|
|||
// Encoding |
|||
#define ELFDATANONE 0 |
|||
#define ELFDATA2LSB 1 |
|||
#define ELFDATA2MSB 2 |
|||
|
|||
|
|||
|
|||
// Sections constants |
|||
|
|||
// Section indexes |
|||
#define SHN_UNDEF 0 |
|||
#define SHN_LORESERVE 0xFF00 |
|||
#define SHN_LOPROC 0xFF00 |
|||
#define SHN_HIPROC 0xFF1F |
|||
#define SHN_ABS 0xFFF1 |
|||
#define SHN_COMMON 0xFFF2 |
|||
#define SHN_HIRESERVE 0xFFFF |
|||
|
|||
// Section types |
|||
#define SHT_NULL 0 |
|||
#define SHT_PROGBITS 1 |
|||
#define SHT_SYMTAB 2 |
|||
#define SHT_STRTAB 3 |
|||
#define SHT_RELA 4 |
|||
#define SHT_HASH 5 |
|||
#define SHT_DYNAMIC 6 |
|||
#define SHT_NOTE 7 |
|||
#define SHT_NOBITS 8 |
|||
#define SHT_REL 9 |
|||
#define SHT_SHLIB 10 |
|||
#define SHT_DYNSYM 11 |
|||
#define SHT_LOPROC 0x70000000 |
|||
#define SHT_HIPROC 0x7FFFFFFF |
|||
#define SHT_LOUSER 0x80000000 |
|||
#define SHT_HIUSER 0xFFFFFFFF |
|||
|
|||
// Custom section types |
|||
#define SHT_PSPREL 0x700000a0 |
|||
|
|||
|
|||
// Section flags |
|||
enum ElfSectionFlags |
|||
{ |
|||
SHF_WRITE = 0x1, |
|||
SHF_ALLOC = 0x2, |
|||
SHF_EXECINSTR = 0x4, |
|||
SHF_MASKPROC = 0xF0000000, |
|||
}; |
|||
|
|||
// Symbol binding |
|||
#define STB_LOCAL 0 |
|||
#define STB_GLOBAL 1 |
|||
#define STB_WEAK 2 |
|||
#define STB_LOPROC 13 |
|||
#define STB_HIPROC 15 |
|||
|
|||
// Symbol types |
|||
#define STT_NOTYPE 0 |
|||
#define STT_OBJECT 1 |
|||
#define STT_FUNC 2 |
|||
#define STT_SECTION 3 |
|||
#define STT_FILE 4 |
|||
#define STT_LOPROC 13 |
|||
#define STT_HIPROC 15 |
|||
|
|||
// Undefined name |
|||
#define STN_UNDEF 0 |
|||
|
|||
// Relocation types |
|||
#define R_386_NONE 0 |
|||
#define R_386_32 1 |
|||
#define R_386_PC32 2 |
|||
#define R_386_GOT32 3 |
|||
#define R_386_PLT32 4 |
|||
#define R_386_COPY 5 |
|||
#define R_386_GLOB_DAT 6 |
|||
#define R_386_JMP_SLOT 7 |
|||
#define R_386_RELATIVE 8 |
|||
#define R_386_GOTOFF 9 |
|||
#define R_386_GOTPC 10 |
|||
|
|||
// Segment types |
|||
#define PT_NULL 0 |
|||
#define PT_LOAD 1 |
|||
#define PT_DYNAMIC 2 |
|||
#define PT_INTERP 3 |
|||
#define PT_NOTE 4 |
|||
#define PT_SHLIB 5 |
|||
#define PT_PHDR 6 |
|||
#define PT_LOPROC 0x70000000 |
|||
#define PT_HIPROC 0x7FFFFFFF |
|||
|
|||
// Segment flags |
|||
#define PF_X 1 |
|||
#define PF_W 2 |
|||
#define PF_R 4 |
|||
|
|||
// Dynamic Array Tags |
|||
#define DT_NULL 0 |
|||
#define DT_NEEDED 1 |
|||
#define DT_PLTRELSZ 2 |
|||
#define DT_PLTGOT 3 |
|||
#define DT_HASH 4 |
|||
#define DT_STRTAB 5 |
|||
#define DT_SYMTAB 6 |
|||
#define DT_RELA 7 |
|||
#define DT_RELASZ 8 |
|||
#define DT_RELAENT 9 |
|||
#define DT_STRSZ 10 |
|||
#define DT_SYMENT 11 |
|||
#define DT_INIT 12 |
|||
#define DT_FINI 13 |
|||
#define DT_SONAME 14 |
|||
#define DT_RPATH 15 |
|||
#define DT_SYMBOLIC 16 |
|||
#define DT_REL 17 |
|||
#define DT_RELSZ 18 |
|||
#define DT_RELENT 19 |
|||
#define DT_PLTREL 20 |
|||
#define DT_DEBUG 21 |
|||
#define DT_TEXTREL 22 |
|||
#define DT_JMPREL 23 |
|||
#define DT_LOPROC 0x70000000 |
|||
#define DT_HIPROC 0x7FFFFFFF |
|||
|
|||
typedef unsigned int Elf32_Addr; |
|||
typedef unsigned short Elf32_Half; |
|||
typedef unsigned int Elf32_Off; |
|||
typedef signed int Elf32_Sword; |
|||
typedef unsigned int Elf32_Word; |
|||
|
|||
|
|||
// ELF file header |
|||
struct Elf32_Ehdr |
|||
{ |
|||
unsigned char e_ident[EI_NIDENT]; |
|||
Elf32_Half e_type; |
|||
Elf32_Half e_machine; |
|||
Elf32_Word e_version; |
|||
Elf32_Addr e_entry; |
|||
Elf32_Off e_phoff; |
|||
Elf32_Off e_shoff; |
|||
Elf32_Word e_flags; |
|||
Elf32_Half e_ehsize; |
|||
Elf32_Half e_phentsize; |
|||
Elf32_Half e_phnum; |
|||
Elf32_Half e_shentsize; |
|||
Elf32_Half e_shnum; |
|||
Elf32_Half e_shstrndx; |
|||
}; |
|||
|
|||
// Section header |
|||
struct Elf32_Shdr |
|||
{ |
|||
Elf32_Word sh_name; |
|||
Elf32_Word sh_type; |
|||
Elf32_Word sh_flags; |
|||
Elf32_Addr sh_addr; |
|||
Elf32_Off sh_offset; |
|||
Elf32_Word sh_size; |
|||
Elf32_Word sh_link; |
|||
Elf32_Word sh_info; |
|||
Elf32_Word sh_addralign; |
|||
Elf32_Word sh_entsize; |
|||
}; |
|||
|
|||
// Segment header |
|||
struct Elf32_Phdr |
|||
{ |
|||
Elf32_Word p_type; |
|||
Elf32_Off p_offset; |
|||
Elf32_Addr p_vaddr; |
|||
Elf32_Addr p_paddr; |
|||
Elf32_Word p_filesz; |
|||
Elf32_Word p_memsz; |
|||
Elf32_Word p_flags; |
|||
Elf32_Word p_align; |
|||
}; |
|||
|
|||
// Symbol table entry |
|||
struct Elf32_Sym |
|||
{ |
|||
Elf32_Word st_name; |
|||
Elf32_Addr st_value; |
|||
Elf32_Word st_size; |
|||
unsigned char st_info; |
|||
unsigned char st_other; |
|||
Elf32_Half st_shndx; |
|||
}; |
|||
|
|||
#define ELF32_ST_BIND(i) ((i)>>4) |
|||
#define ELF32_ST_TYPE(i) ((i)&0xf) |
|||
#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) |
|||
|
|||
// Relocation entries |
|||
struct Elf32_Rel |
|||
{ |
|||
Elf32_Addr r_offset; |
|||
Elf32_Word r_info; |
|||
}; |
|||
|
|||
struct Elf32_Rela |
|||
{ |
|||
Elf32_Addr r_offset; |
|||
Elf32_Word r_info; |
|||
Elf32_Sword r_addend; |
|||
}; |
|||
|
|||
#define ELF32_R_SYM(i) ((i)>>8) |
|||
#define ELF32_R_TYPE(i) ((unsigned char)(i)) |
|||
#define ELF32_R_INFO(s,t) (((s)<<8 )+(unsigned char)(t)) |
|||
|
|||
|
|||
struct Elf32_Dyn |
|||
{ |
|||
Elf32_Sword d_tag; |
|||
union |
|||
{ |
|||
Elf32_Word d_val; |
|||
Elf32_Addr d_ptr; |
|||
} d_un; |
|||
}; |
|||
@ -1,275 +0,0 @@ |
|||
// Copyright 2014 Citra Emulator Project
|
|||
// Licensed under GPLv2
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "common/common_types.h"
|
|||
#include "common/file_util.h"
|
|||
|
|||
#include "core/loader.h"
|
|||
#include "core/system.h"
|
|||
#include "core/core.h"
|
|||
#include "core/file_sys/directory_file_system.h"
|
|||
#include "core/elf/elf_reader.h"
|
|||
#include "core/hle/kernel/kernel.h"
|
|||
#include "core/mem_map.h"
|
|||
|
|||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|||
|
|||
/// Loads an extracted CXI from a directory
|
|||
bool LoadDirectory_CXI(std::string &filename) { |
|||
std::string full_path = filename; |
|||
std::string path, file, extension; |
|||
SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension); |
|||
#if EMU_PLATFORM == PLATFORM_WINDOWS
|
|||
path = ReplaceAll(path, "/", "\\"); |
|||
#endif
|
|||
DirectoryFileSystem *fs = new DirectoryFileSystem(&System::g_ctr_file_system, path); |
|||
System::g_ctr_file_system.Mount("fs:", fs); |
|||
|
|||
std::string final_name = "fs:/" + file + extension; |
|||
File::IOFile f(filename, "rb"); |
|||
|
|||
if (f.IsOpen()) { |
|||
// TODO(ShizZy): read here to memory....
|
|||
} |
|||
ERROR_LOG(TIME, "Unimplemented function!"); |
|||
return true; |
|||
} |
|||
|
|||
/// Loads a CTR ELF file
|
|||
bool Load_ELF(std::string &filename) { |
|||
std::string full_path = filename; |
|||
std::string path, file, extension; |
|||
SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension); |
|||
#if EMU_PLATFORM == PLATFORM_WINDOWS
|
|||
path = ReplaceAll(path, "/", "\\"); |
|||
#endif
|
|||
File::IOFile f(filename, "rb"); |
|||
|
|||
if (f.IsOpen()) { |
|||
u64 size = f.GetSize(); |
|||
u8* buffer = new u8[size]; |
|||
ElfReader* elf_reader = NULL; |
|||
|
|||
f.ReadBytes(buffer, size); |
|||
|
|||
elf_reader = new ElfReader(buffer); |
|||
elf_reader->LoadInto(0x00100000); |
|||
|
|||
Kernel::LoadExec(elf_reader->GetEntryPoint()); |
|||
|
|||
delete[] buffer; |
|||
delete elf_reader; |
|||
} else { |
|||
return false; |
|||
} |
|||
f.Close(); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// Loads a Launcher DAT file
|
|||
bool Load_DAT(std::string &filename) { |
|||
std::string full_path = filename; |
|||
std::string path, file, extension; |
|||
SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension); |
|||
#if EMU_PLATFORM == PLATFORM_WINDOWS
|
|||
path = ReplaceAll(path, "/", "\\"); |
|||
#endif
|
|||
File::IOFile f(filename, "rb"); |
|||
|
|||
if (f.IsOpen()) { |
|||
u64 size = f.GetSize(); |
|||
u8* buffer = new u8[size]; |
|||
|
|||
f.ReadBytes(buffer, size); |
|||
|
|||
/**
|
|||
* (mattvail) We shouldn't really support this type of file |
|||
* but for the sake of making it easier... we'll temporarily/hackishly |
|||
* allow it. No sense in making a proper reader for this. |
|||
*/ |
|||
u32 entry_point = 0x00100000; // write to same entrypoint as elf
|
|||
u32 payload_offset = 0xA150; |
|||
|
|||
const u8 *src = &buffer[payload_offset]; |
|||
u8 *dst = Memory::GetPointer(entry_point); |
|||
u32 srcSize = size - payload_offset; //just load everything...
|
|||
u32 *s = (u32*)src; |
|||
u32 *d = (u32*)dst; |
|||
for (int j = 0; j < (int)(srcSize + 3) / 4; j++) |
|||
{ |
|||
*d++ = (*s++); |
|||
} |
|||
|
|||
Kernel::LoadExec(entry_point); |
|||
|
|||
|
|||
delete[] buffer; |
|||
} |
|||
else { |
|||
return false; |
|||
} |
|||
f.Close(); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
/// Loads a CTR BIN file extracted from an ExeFS
|
|||
bool Load_BIN(std::string &filename) { |
|||
std::string full_path = filename; |
|||
std::string path, file, extension; |
|||
SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension); |
|||
#if EMU_PLATFORM == PLATFORM_WINDOWS
|
|||
path = ReplaceAll(path, "/", "\\"); |
|||
#endif
|
|||
File::IOFile f(filename, "rb"); |
|||
|
|||
if (f.IsOpen()) { |
|||
u64 size = f.GetSize(); |
|||
u8* buffer = new u8[size]; |
|||
|
|||
f.ReadBytes(buffer, size); |
|||
|
|||
u32 entry_point = 0x00100000; // Hardcoded, read from exheader
|
|||
|
|||
const u8 *src = buffer; |
|||
u8 *dst = Memory::GetPointer(entry_point); |
|||
u32 srcSize = size; |
|||
u32 *s = (u32*)src; |
|||
u32 *d = (u32*)dst; |
|||
for (int j = 0; j < (int)(srcSize + 3) / 4; j++) |
|||
{ |
|||
*d++ = (*s++); |
|||
} |
|||
|
|||
Kernel::LoadExec(entry_point); |
|||
|
|||
delete[] buffer; |
|||
} |
|||
else { |
|||
return false; |
|||
} |
|||
f.Close(); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
namespace Loader { |
|||
|
|||
bool IsBootableDirectory() { |
|||
ERROR_LOG(TIME, "Unimplemented function!"); |
|||
return true; |
|||
} |
|||
|
|||
/**
|
|||
* Identifies the type of a bootable file |
|||
* @param filename String filename of bootable file |
|||
* @todo (ShizZy) this function sucks... make it actually check file contents etc. |
|||
* @return FileType of file |
|||
*/ |
|||
FileType IdentifyFile(std::string &filename) { |
|||
if (filename.size() == 0) { |
|||
ERROR_LOG(LOADER, "invalid filename %s", filename.c_str()); |
|||
return FILETYPE_ERROR; |
|||
} |
|||
std::string extension = filename.size() >= 5 ? filename.substr(filename.size() - 4) : ""; |
|||
|
|||
if (File::IsDirectory(filename)) { |
|||
if (IsBootableDirectory()) { |
|||
return FILETYPE_DIRECTORY_CXI; |
|||
} |
|||
else { |
|||
return FILETYPE_NORMAL_DIRECTORY; |
|||
} |
|||
} |
|||
else if (!strcasecmp(extension.c_str(), ".elf")) { |
|||
return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p
|
|||
} |
|||
else if (!strcasecmp(extension.c_str(), ".axf")) { |
|||
return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p
|
|||
} |
|||
else if (!strcasecmp(extension.c_str(), ".bin")) { |
|||
return FILETYPE_CTR_BIN; |
|||
} |
|||
else if (!strcasecmp(extension.c_str(), ".dat")) { |
|||
return FILETYPE_LAUNCHER_DAT; |
|||
} |
|||
else if (!strcasecmp(extension.c_str(), ".zip")) { |
|||
return FILETYPE_ARCHIVE_ZIP; |
|||
} |
|||
else if (!strcasecmp(extension.c_str(), ".rar")) { |
|||
return FILETYPE_ARCHIVE_RAR; |
|||
} |
|||
else if (!strcasecmp(extension.c_str(), ".r00")) { |
|||
return FILETYPE_ARCHIVE_RAR; |
|||
} |
|||
else if (!strcasecmp(extension.c_str(), ".r01")) { |
|||
return FILETYPE_ARCHIVE_RAR; |
|||
} |
|||
return FILETYPE_UNKNOWN; |
|||
} |
|||
|
|||
/**
|
|||
* Identifies and loads a bootable file |
|||
* @param filename String filename of bootable file |
|||
* @param error_string Point to string to put error message if an error has occurred |
|||
* @return True on success, otherwise false |
|||
*/ |
|||
bool LoadFile(std::string &filename, std::string *error_string) { |
|||
INFO_LOG(LOADER, "Identifying file..."); |
|||
|
|||
// Note that this can modify filename!
|
|||
switch (IdentifyFile(filename)) { |
|||
|
|||
case FILETYPE_CTR_ELF: |
|||
return Load_ELF(filename); |
|||
|
|||
case FILETYPE_CTR_BIN: |
|||
return Load_BIN(filename); |
|||
|
|||
case FILETYPE_LAUNCHER_DAT: |
|||
return Load_DAT(filename); |
|||
|
|||
case FILETYPE_DIRECTORY_CXI: |
|||
return LoadDirectory_CXI(filename); |
|||
|
|||
case FILETYPE_ERROR: |
|||
ERROR_LOG(LOADER, "Could not read file"); |
|||
*error_string = "Error reading file"; |
|||
break; |
|||
|
|||
case FILETYPE_ARCHIVE_RAR: |
|||
#ifdef WIN32
|
|||
*error_string = "RAR file detected (Require WINRAR)"; |
|||
#else
|
|||
*error_string = "RAR file detected (Require UnRAR)"; |
|||
#endif
|
|||
break; |
|||
|
|||
case FILETYPE_ARCHIVE_ZIP: |
|||
#ifdef WIN32
|
|||
*error_string = "ZIP file detected (Require WINRAR)"; |
|||
#else
|
|||
*error_string = "ZIP file detected (Require UnRAR)"; |
|||
#endif
|
|||
break; |
|||
|
|||
case FILETYPE_NORMAL_DIRECTORY: |
|||
ERROR_LOG(LOADER, "Just a directory."); |
|||
*error_string = "Just a directory."; |
|||
break; |
|||
|
|||
case FILETYPE_UNKNOWN_BIN: |
|||
case FILETYPE_UNKNOWN_ELF: |
|||
case FILETYPE_UNKNOWN: |
|||
default: |
|||
ERROR_LOG(LOADER, "Failed to identify file"); |
|||
*error_string = " Failed to identify file"; |
|||
break; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
} // namespace
|
|||
@ -1,54 +0,0 @@ |
|||
// Copyright 2014 Citra Emulator Project |
|||
// Licensed under GPLv2 |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common.h" |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|||
|
|||
namespace Loader { |
|||
|
|||
enum FileType { |
|||
FILETYPE_ERROR, |
|||
|
|||
FILETYPE_CTR_CCI, |
|||
FILETYPE_CTR_CIA, |
|||
FILETYPE_CTR_CXI, |
|||
FILETYPE_CTR_ELF, |
|||
FILETYPE_CTR_BIN, |
|||
|
|||
FILETYPE_LAUNCHER_DAT, |
|||
|
|||
FILETYPE_DIRECTORY_CXI, |
|||
|
|||
FILETYPE_UNKNOWN_BIN, |
|||
FILETYPE_UNKNOWN_ELF, |
|||
|
|||
FILETYPE_ARCHIVE_RAR, |
|||
FILETYPE_ARCHIVE_ZIP, |
|||
|
|||
FILETYPE_NORMAL_DIRECTORY, |
|||
|
|||
FILETYPE_UNKNOWN |
|||
}; |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|||
|
|||
/** |
|||
* Identifies the type of a bootable file |
|||
* @param filename String filename of bootable file |
|||
* @return FileType of file |
|||
*/ |
|||
FileType IdentifyFile(std::string &filename); |
|||
|
|||
/** |
|||
* Identifies and loads a bootable file |
|||
* @param filename String filename of bootable file |
|||
* @param error_string Point to string to put error message if an error has occurred |
|||
* @return True on success, otherwise false |
|||
*/ |
|||
bool LoadFile(std::string &filename, std::string *error_string); |
|||
|
|||
} // namespace |
|||
@ -0,0 +1,370 @@ |
|||
// Copyright 2013 Dolphin Emulator Project
|
|||
// Licensed under GPLv2
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <string>
|
|||
#include <memory>
|
|||
|
|||
#include "common/common.h"
|
|||
#include "common/file_util.h"
|
|||
#include "common/symbols.h"
|
|||
|
|||
#include "core/mem_map.h"
|
|||
#include "core/loader/elf.h"
|
|||
#include "core/hle/kernel/kernel.h"
|
|||
|
|||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|||
// ELF Header Constants
|
|||
|
|||
// File type
|
|||
enum ElfType { |
|||
ET_NONE = 0, |
|||
ET_REL = 1, |
|||
ET_EXEC = 2, |
|||
ET_DYN = 3, |
|||
ET_CORE = 4, |
|||
ET_LOPROC = 0xFF00, |
|||
ET_HIPROC = 0xFFFF, |
|||
}; |
|||
|
|||
// Machine/Architecture
|
|||
enum ElfMachine { |
|||
EM_NONE = 0, |
|||
EM_M32 = 1, |
|||
EM_SPARC = 2, |
|||
EM_386 = 3, |
|||
EM_68K = 4, |
|||
EM_88K = 5, |
|||
EM_860 = 7, |
|||
EM_MIPS = 8 |
|||
}; |
|||
|
|||
// File version
|
|||
#define EV_NONE 0
|
|||
#define EV_CURRENT 1
|
|||
|
|||
// Identification index
|
|||
#define EI_MAG0 0
|
|||
#define EI_MAG1 1
|
|||
#define EI_MAG2 2
|
|||
#define EI_MAG3 3
|
|||
#define EI_CLASS 4
|
|||
#define EI_DATA 5
|
|||
#define EI_VERSION 6
|
|||
#define EI_PAD 7
|
|||
#define EI_NIDENT 16
|
|||
|
|||
// Magic number
|
|||
#define ELFMAG0 0x7F
|
|||
#define ELFMAG1 'E'
|
|||
#define ELFMAG2 'L'
|
|||
#define ELFMAG3 'F'
|
|||
|
|||
// Sections constants
|
|||
|
|||
// Section types
|
|||
#define SHT_NULL 0
|
|||
#define SHT_PROGBITS 1
|
|||
#define SHT_SYMTAB 2
|
|||
#define SHT_STRTAB 3
|
|||
#define SHT_RELA 4
|
|||
#define SHT_HASH 5
|
|||
#define SHT_DYNAMIC 6
|
|||
#define SHT_NOTE 7
|
|||
#define SHT_NOBITS 8
|
|||
#define SHT_REL 9
|
|||
#define SHT_SHLIB 10
|
|||
#define SHT_DYNSYM 11
|
|||
#define SHT_LOPROC 0x70000000
|
|||
#define SHT_HIPROC 0x7FFFFFFF
|
|||
#define SHT_LOUSER 0x80000000
|
|||
#define SHT_HIUSER 0xFFFFFFFF
|
|||
|
|||
// Section flags
|
|||
enum ElfSectionFlags |
|||
{ |
|||
SHF_WRITE = 0x1, |
|||
SHF_ALLOC = 0x2, |
|||
SHF_EXECINSTR = 0x4, |
|||
SHF_MASKPROC = 0xF0000000, |
|||
}; |
|||
|
|||
// Segment types
|
|||
#define PT_NULL 0
|
|||
#define PT_LOAD 1
|
|||
#define PT_DYNAMIC 2
|
|||
#define PT_INTERP 3
|
|||
#define PT_NOTE 4
|
|||
#define PT_SHLIB 5
|
|||
#define PT_PHDR 6
|
|||
#define PT_LOPROC 0x70000000
|
|||
#define PT_HIPROC 0x7FFFFFFF
|
|||
|
|||
typedef unsigned int Elf32_Addr; |
|||
typedef unsigned short Elf32_Half; |
|||
typedef unsigned int Elf32_Off; |
|||
typedef signed int Elf32_Sword; |
|||
typedef unsigned int Elf32_Word; |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|||
// ELF file header
|
|||
|
|||
struct Elf32_Ehdr { |
|||
unsigned char e_ident[EI_NIDENT]; |
|||
Elf32_Half e_type; |
|||
Elf32_Half e_machine; |
|||
Elf32_Word e_version; |
|||
Elf32_Addr e_entry; |
|||
Elf32_Off e_phoff; |
|||
Elf32_Off e_shoff; |
|||
Elf32_Word e_flags; |
|||
Elf32_Half e_ehsize; |
|||
Elf32_Half e_phentsize; |
|||
Elf32_Half e_phnum; |
|||
Elf32_Half e_shentsize; |
|||
Elf32_Half e_shnum; |
|||
Elf32_Half e_shstrndx; |
|||
}; |
|||
|
|||
// Section header
|
|||
struct Elf32_Shdr { |
|||
Elf32_Word sh_name; |
|||
Elf32_Word sh_type; |
|||
Elf32_Word sh_flags; |
|||
Elf32_Addr sh_addr; |
|||
Elf32_Off sh_offset; |
|||
Elf32_Word sh_size; |
|||
Elf32_Word sh_link; |
|||
Elf32_Word sh_info; |
|||
Elf32_Word sh_addralign; |
|||
Elf32_Word sh_entsize; |
|||
}; |
|||
|
|||
// Segment header
|
|||
struct Elf32_Phdr { |
|||
Elf32_Word p_type; |
|||
Elf32_Off p_offset; |
|||
Elf32_Addr p_vaddr; |
|||
Elf32_Addr p_paddr; |
|||
Elf32_Word p_filesz; |
|||
Elf32_Word p_memsz; |
|||
Elf32_Word p_flags; |
|||
Elf32_Word p_align; |
|||
}; |
|||
|
|||
// Symbol table entry
|
|||
struct Elf32_Sym { |
|||
Elf32_Word st_name; |
|||
Elf32_Addr st_value; |
|||
Elf32_Word st_size; |
|||
unsigned char st_info; |
|||
unsigned char st_other; |
|||
Elf32_Half st_shndx; |
|||
}; |
|||
|
|||
// Relocation entries
|
|||
struct Elf32_Rel { |
|||
Elf32_Addr r_offset; |
|||
Elf32_Word r_info; |
|||
}; |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|||
// ElfReader class
|
|||
|
|||
typedef int SectionID; |
|||
|
|||
class ElfReader { |
|||
private: |
|||
char *base; |
|||
u32 *base32; |
|||
|
|||
Elf32_Ehdr *header; |
|||
Elf32_Phdr *segments; |
|||
Elf32_Shdr *sections; |
|||
|
|||
u32 *sectionAddrs; |
|||
bool relocate; |
|||
u32 entryPoint; |
|||
|
|||
public: |
|||
ElfReader(void *ptr); |
|||
~ElfReader() { } |
|||
|
|||
u32 Read32(int off) const { return base32[off >> 2]; } |
|||
|
|||
// Quick accessors
|
|||
ElfType GetType() const { return (ElfType)(header->e_type); } |
|||
ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } |
|||
u32 GetEntryPoint() const { return entryPoint; } |
|||
u32 GetFlags() const { return (u32)(header->e_flags); } |
|||
bool LoadInto(u32 vaddr); |
|||
bool LoadSymbols(); |
|||
|
|||
int GetNumSegments() const { return (int)(header->e_phnum); } |
|||
int GetNumSections() const { return (int)(header->e_shnum); } |
|||
const u8 *GetPtr(int offset) const { return (u8*)base + offset; } |
|||
const char *GetSectionName(int section) const; |
|||
const u8 *GetSectionDataPtr(int section) const { |
|||
if (section < 0 || section >= header->e_shnum) |
|||
return nullptr; |
|||
if (sections[section].sh_type != SHT_NOBITS) |
|||
return GetPtr(sections[section].sh_offset); |
|||
else |
|||
return nullptr; |
|||
} |
|||
bool IsCodeSection(int section) const { |
|||
return sections[section].sh_type == SHT_PROGBITS; |
|||
} |
|||
const u8 *GetSegmentPtr(int segment) { |
|||
return GetPtr(segments[segment].p_offset); |
|||
} |
|||
u32 GetSectionAddr(SectionID section) const { return sectionAddrs[section]; } |
|||
int GetSectionSize(SectionID section) const { return sections[section].sh_size; } |
|||
SectionID GetSectionByName(const char *name, int firstSection = 0) const; //-1 for not found
|
|||
|
|||
bool DidRelocate() { |
|||
return relocate; |
|||
} |
|||
}; |
|||
|
|||
ElfReader::ElfReader(void *ptr) { |
|||
base = (char*)ptr; |
|||
base32 = (u32 *)ptr; |
|||
header = (Elf32_Ehdr*)ptr; |
|||
|
|||
segments = (Elf32_Phdr *)(base + header->e_phoff); |
|||
sections = (Elf32_Shdr *)(base + header->e_shoff); |
|||
|
|||
entryPoint = header->e_entry; |
|||
|
|||
LoadSymbols(); |
|||
} |
|||
|
|||
const char *ElfReader::GetSectionName(int section) const { |
|||
if (sections[section].sh_type == SHT_NULL) |
|||
return nullptr; |
|||
|
|||
int name_offset = sections[section].sh_name; |
|||
char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); |
|||
|
|||
if (ptr) |
|||
return ptr + name_offset; |
|||
|
|||
return nullptr; |
|||
} |
|||
|
|||
bool ElfReader::LoadInto(u32 vaddr) { |
|||
DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx); |
|||
|
|||
// Should we relocate?
|
|||
relocate = (header->e_type != ET_EXEC); |
|||
|
|||
if (relocate) { |
|||
DEBUG_LOG(MASTER_LOG, "Relocatable module"); |
|||
entryPoint += vaddr; |
|||
} else { |
|||
DEBUG_LOG(MASTER_LOG, "Prerelocated executable"); |
|||
} |
|||
INFO_LOG(MASTER_LOG, "%i segments:", header->e_phnum); |
|||
|
|||
// First pass : Get the bits into RAM
|
|||
u32 segment_addr[32]; |
|||
u32 base_addr = relocate ? vaddr : 0; |
|||
|
|||
for (int i = 0; i < header->e_phnum; i++) { |
|||
Elf32_Phdr *p = segments + i; |
|||
INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, |
|||
p->p_filesz, p->p_memsz); |
|||
|
|||
if (p->p_type == PT_LOAD) { |
|||
segment_addr[i] = base_addr + p->p_vaddr; |
|||
memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz); |
|||
INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", segment_addr[i], |
|||
p->p_memsz); |
|||
} |
|||
} |
|||
INFO_LOG(MASTER_LOG, "Done loading."); |
|||
return true; |
|||
} |
|||
|
|||
SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const { |
|||
for (int i = firstSection; i < header->e_shnum; i++) { |
|||
const char *secname = GetSectionName(i); |
|||
|
|||
if (secname != nullptr && strcmp(name, secname) == 0) |
|||
return i; |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
bool ElfReader::LoadSymbols() { |
|||
bool hasSymbols = false; |
|||
SectionID sec = GetSectionByName(".symtab"); |
|||
if (sec != -1) { |
|||
int stringSection = sections[sec].sh_link; |
|||
const char *stringBase = (const char *)GetSectionDataPtr(stringSection); |
|||
|
|||
//We have a symbol table!
|
|||
Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); |
|||
int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); |
|||
for (int sym = 0; sym < numSymbols; sym++) { |
|||
int size = symtab[sym].st_size; |
|||
if (size == 0) |
|||
continue; |
|||
|
|||
int type = symtab[sym].st_info & 0xF; |
|||
|
|||
const char *name = stringBase + symtab[sym].st_name; |
|||
|
|||
Symbols::Add(symtab[sym].st_value, name, size, type); |
|||
|
|||
hasSymbols = true; |
|||
} |
|||
} |
|||
|
|||
return hasSymbols; |
|||
} |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|||
// Loader namespace
|
|||
|
|||
namespace Loader { |
|||
|
|||
/// AppLoader_ELF constructor
|
|||
AppLoader_ELF::AppLoader_ELF(const std::string& filename) : is_loaded(false) { |
|||
this->filename = filename; |
|||
} |
|||
|
|||
/// AppLoader_NCCH destructor
|
|||
AppLoader_ELF::~AppLoader_ELF() { |
|||
} |
|||
|
|||
/**
|
|||
* Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) |
|||
* @param error_string Pointer to string to put error message if an error has occurred |
|||
* @todo Move NCSD parsing out of here and create a separate function for loading these |
|||
* @return True on success, otherwise false |
|||
*/ |
|||
ResultStatus AppLoader_ELF::Load() { |
|||
INFO_LOG(LOADER, "Loading ELF file %s...", filename.c_str()); |
|||
|
|||
if (is_loaded) |
|||
return ResultStatus::ErrorAlreadyLoaded; |
|||
|
|||
File::IOFile file(filename, "rb"); |
|||
|
|||
if (file.IsOpen()) { |
|||
u32 size = (u32)file.GetSize(); |
|||
std::unique_ptr<u8[]> buffer(new u8[size]); |
|||
file.ReadBytes(&buffer[0], size); |
|||
|
|||
ElfReader elf_reader(&buffer[0]); |
|||
elf_reader.LoadInto(0x00100000); |
|||
Kernel::LoadExec(elf_reader.GetEntryPoint()); |
|||
} else { |
|||
return ResultStatus::Error; |
|||
} |
|||
return ResultStatus::Success; |
|||
} |
|||
|
|||
} // namespace Loader
|
|||
@ -0,0 +1,32 @@ |
|||
// Copyright 2013 Dolphin Emulator Project / Citra Emulator Project |
|||
// Licensed under GPLv2 |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_types.h" |
|||
#include "core/loader/loader.h" |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|||
// Loader namespace |
|||
|
|||
namespace Loader { |
|||
|
|||
/// Loads an ELF/AXF file |
|||
class AppLoader_ELF : public AppLoader { |
|||
public: |
|||
AppLoader_ELF(const std::string& filename); |
|||
~AppLoader_ELF(); |
|||
|
|||
/** |
|||
* Load the bootable file |
|||
* @return ResultStatus result of function |
|||
*/ |
|||
ResultStatus Load(); |
|||
|
|||
private: |
|||
std::string filename; |
|||
bool is_loaded; |
|||
}; |
|||
|
|||
} // namespace Loader |
|||
@ -0,0 +1,77 @@ |
|||
// Copyright 2014 Citra Emulator Project
|
|||
// Licensed under GPLv2
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <memory>
|
|||
|
|||
#include "core/loader/loader.h"
|
|||
#include "core/loader/elf.h"
|
|||
#include "core/loader/ncch.h"
|
|||
|
|||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|||
|
|||
namespace Loader { |
|||
|
|||
/**
|
|||
* Identifies the type of a bootable file |
|||
* @param filename String filename of bootable file |
|||
* @todo (ShizZy) this function sucks... make it actually check file contents etc. |
|||
* @return FileType of file |
|||
*/ |
|||
FileType IdentifyFile(const std::string &filename) { |
|||
if (filename.size() == 0) { |
|||
ERROR_LOG(LOADER, "invalid filename %s", filename.c_str()); |
|||
return FileType::Error; |
|||
} |
|||
std::string extension = filename.size() >= 5 ? filename.substr(filename.size() - 4) : ""; |
|||
|
|||
if (!strcasecmp(extension.c_str(), ".elf")) { |
|||
return FileType::ELF; // TODO(bunnei): Do some filetype checking :p
|
|||
} |
|||
else if (!strcasecmp(extension.c_str(), ".axf")) { |
|||
return FileType::ELF; // TODO(bunnei): Do some filetype checking :p
|
|||
} |
|||
else if (!strcasecmp(extension.c_str(), ".cxi")) { |
|||
return FileType::CXI; // TODO(bunnei): Do some filetype checking :p
|
|||
} |
|||
else if (!strcasecmp(extension.c_str(), ".cci")) { |
|||
return FileType::CCI; // TODO(bunnei): Do some filetype checking :p
|
|||
} |
|||
return FileType::Unknown; |
|||
} |
|||
|
|||
/**
|
|||
* Identifies and loads a bootable file |
|||
* @param filename String filename of bootable file |
|||
* @return ResultStatus result of function |
|||
*/ |
|||
ResultStatus LoadFile(const std::string& filename) { |
|||
INFO_LOG(LOADER, "Loading file %s...", filename.c_str()); |
|||
|
|||
switch (IdentifyFile(filename)) { |
|||
|
|||
// Standard ELF file format...
|
|||
case FileType::ELF: { |
|||
return AppLoader_ELF(filename).Load(); |
|||
} |
|||
|
|||
// NCCH/NCSD container formats...
|
|||
case FileType::CXI: |
|||
case FileType::CCI: { |
|||
return AppLoader_NCCH(filename).Load(); |
|||
} |
|||
|
|||
// Error occurred durring IdentifyFile...
|
|||
case FileType::Error: |
|||
|
|||
// IdentifyFile could know identify file type...
|
|||
case FileType::Unknown: |
|||
|
|||
default: |
|||
return ResultStatus::ErrorInvalidFormat; |
|||
} |
|||
|
|||
return ResultStatus::Error; |
|||
} |
|||
|
|||
} // namespace Loader
|
|||
@ -0,0 +1,121 @@ |
|||
// Copyright 2014 Citra Emulator Project |
|||
// Licensed under GPLv2 |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <vector> |
|||
|
|||
#include "common/common.h" |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|||
// Loader namespace |
|||
|
|||
namespace Loader { |
|||
|
|||
/// File types supported by CTR |
|||
enum class FileType { |
|||
Error, |
|||
Unknown, |
|||
CCI, |
|||
CXI, |
|||
CIA, |
|||
ELF, |
|||
}; |
|||
|
|||
/// Return type for functions in Loader namespace |
|||
enum class ResultStatus { |
|||
Success, |
|||
Error, |
|||
ErrorInvalidFormat, |
|||
ErrorNotImplemented, |
|||
ErrorNotLoaded, |
|||
ErrorNotUsed, |
|||
ErrorAlreadyLoaded, |
|||
}; |
|||
|
|||
/// Interface for loading an application |
|||
class AppLoader : NonCopyable { |
|||
public: |
|||
AppLoader() { } |
|||
virtual ~AppLoader() { } |
|||
|
|||
/** |
|||
* Load the application |
|||
* @return ResultStatus result of function |
|||
*/ |
|||
virtual ResultStatus Load() = 0; |
|||
|
|||
/** |
|||
* Get the code (typically .code section) of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to code buffer |
|||
*/ |
|||
virtual const std::vector<u8>& ReadCode(ResultStatus& error) const { |
|||
error = ResultStatus::ErrorNotImplemented; |
|||
return code; |
|||
} |
|||
|
|||
/** |
|||
* Get the icon (typically icon section) of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to icon buffer |
|||
*/ |
|||
virtual const std::vector<u8>& ReadIcon(ResultStatus& error) const { |
|||
error = ResultStatus::ErrorNotImplemented; |
|||
return icon; |
|||
} |
|||
|
|||
/** |
|||
* Get the banner (typically banner section) of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to banner buffer |
|||
*/ |
|||
virtual const std::vector<u8>& ReadBanner(ResultStatus& error) const { |
|||
error = ResultStatus::ErrorNotImplemented; |
|||
return banner; |
|||
} |
|||
|
|||
/** |
|||
* Get the logo (typically logo section) of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to logo buffer |
|||
*/ |
|||
virtual const std::vector<u8>& ReadLogo(ResultStatus& error) const { |
|||
error = ResultStatus::ErrorNotImplemented; |
|||
return logo; |
|||
} |
|||
|
|||
/** |
|||
* Get the RomFs archive of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to RomFs archive buffer |
|||
*/ |
|||
virtual const std::vector<u8>& ReadRomFS(ResultStatus& error) const { |
|||
error = ResultStatus::ErrorNotImplemented; |
|||
return romfs; |
|||
} |
|||
|
|||
protected: |
|||
std::vector<u8> code; ///< ExeFS .code section |
|||
std::vector<u8> icon; ///< ExeFS .icon section |
|||
std::vector<u8> banner; ///< ExeFS .banner section |
|||
std::vector<u8> logo; ///< ExeFS .logo section |
|||
std::vector<u8> romfs; ///< RomFs archive |
|||
}; |
|||
|
|||
/** |
|||
* Identifies the type of a bootable file |
|||
* @param filename String filename of bootable file |
|||
* @return FileType of file |
|||
*/ |
|||
FileType IdentifyFile(const std::string &filename); |
|||
|
|||
/** |
|||
* Identifies and loads a bootable file |
|||
* @param filename String filename of bootable file |
|||
* @return ResultStatus result of function |
|||
*/ |
|||
ResultStatus LoadFile(const std::string& filename); |
|||
|
|||
} // namespace |
|||
@ -0,0 +1,312 @@ |
|||
// Copyright 2014 Citra Emulator Project
|
|||
// Licensed under GPLv2
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <memory>
|
|||
|
|||
#include "common/file_util.h"
|
|||
|
|||
#include "core/loader/ncch.h"
|
|||
#include "core/hle/kernel/kernel.h"
|
|||
#include "core/mem_map.h"
|
|||
|
|||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|||
// Loader namespace
|
|||
|
|||
namespace Loader { |
|||
|
|||
static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs
|
|||
static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
|
|||
|
|||
/**
|
|||
* Get the decompressed size of an LZSS compressed ExeFS file |
|||
* @param buffer Buffer of compressed file |
|||
* @param size Size of compressed buffer |
|||
* @return Size of decompressed buffer |
|||
*/ |
|||
u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { |
|||
u32 offset_size = *(u32*)(buffer + size - 4); |
|||
return offset_size + size; |
|||
} |
|||
|
|||
/**
|
|||
* Decompress ExeFS file (compressed with LZSS) |
|||
* @param compressed Compressed buffer |
|||
* @param compressed_size Size of compressed buffer |
|||
* @param decompressed Decompressed buffer |
|||
* @param decompressed_size Size of decompressed buffer |
|||
* @return True on success, otherwise false |
|||
*/ |
|||
bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) { |
|||
u8* footer = compressed + compressed_size - 8; |
|||
u32 buffer_top_and_bottom = *(u32*)footer; |
|||
u32 i, j; |
|||
u32 out = decompressed_size; |
|||
u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF); |
|||
u8 control; |
|||
u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF); |
|||
|
|||
memset(decompressed, 0, decompressed_size); |
|||
memcpy(decompressed, compressed, compressed_size); |
|||
|
|||
while(index > stop_index) { |
|||
control = compressed[--index]; |
|||
|
|||
for(i = 0; i < 8; i++) { |
|||
if(index <= stop_index) |
|||
break; |
|||
if(index <= 0) |
|||
break; |
|||
if(out <= 0) |
|||
break; |
|||
|
|||
if(control & 0x80) { |
|||
// Check if compression is out of bounds
|
|||
if(index < 2) { |
|||
return false; |
|||
} |
|||
index -= 2; |
|||
|
|||
u32 segment_offset = compressed[index] | (compressed[index + 1] << 8); |
|||
u32 segment_size = ((segment_offset >> 12) & 15) + 3; |
|||
segment_offset &= 0x0FFF; |
|||
segment_offset += 2; |
|||
|
|||
// Check if compression is out of bounds
|
|||
if(out < segment_size) { |
|||
return false; |
|||
} |
|||
for(j = 0; j < segment_size; j++) { |
|||
u8 data; |
|||
// Check if compression is out of bounds
|
|||
if(out + segment_offset >= decompressed_size) { |
|||
return false; |
|||
} |
|||
data = decompressed[out + segment_offset]; |
|||
decompressed[--out] = data; |
|||
} |
|||
} else { |
|||
// Check if compression is out of bounds
|
|||
if(out < 1) { |
|||
return false; |
|||
} |
|||
decompressed[--out] = compressed[--index]; |
|||
} |
|||
control <<= 1; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|||
// AppLoader_NCCH class
|
|||
|
|||
/// AppLoader_NCCH constructor
|
|||
AppLoader_NCCH::AppLoader_NCCH(const std::string& filename) { |
|||
this->filename = filename; |
|||
is_loaded = false; |
|||
is_compressed = false; |
|||
entry_point = 0; |
|||
ncch_offset = 0; |
|||
exefs_offset = 0; |
|||
} |
|||
|
|||
/// AppLoader_NCCH destructor
|
|||
AppLoader_NCCH::~AppLoader_NCCH() { |
|||
if (file.IsOpen()) |
|||
file.Close(); |
|||
} |
|||
|
|||
/**
|
|||
* Loads .code section into memory for booting |
|||
* @return ResultStatus result of function |
|||
*/ |
|||
ResultStatus AppLoader_NCCH::LoadExec() { |
|||
if (!is_loaded) |
|||
return ResultStatus::ErrorNotLoaded; |
|||
|
|||
ResultStatus res; |
|||
code = ReadCode(res); |
|||
|
|||
if (ResultStatus::Success == res) { |
|||
Memory::WriteBlock(entry_point, &code[0], code.size()); |
|||
Kernel::LoadExec(entry_point); |
|||
} |
|||
return res; |
|||
} |
|||
|
|||
/**
|
|||
* Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) |
|||
* @param name Name of section to read out of NCCH file |
|||
* @param buffer Vector to read data into |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to buffer of data that was read |
|||
*/ |
|||
const std::vector<u8>& AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer, |
|||
ResultStatus& error) { |
|||
// Iterate through the ExeFs archive until we find the .code file...
|
|||
for (int i = 0; i < kMaxSections; i++) { |
|||
// Load the specified section...
|
|||
if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { |
|||
INFO_LOG(LOADER, "ExeFS section %d:", i); |
|||
INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name); |
|||
INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset); |
|||
INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size); |
|||
|
|||
s64 section_offset = (exefs_header.section[i].offset + exefs_offset + |
|||
sizeof(ExeFs_Header) + ncch_offset); |
|||
file.Seek(section_offset, 0); |
|||
|
|||
// Section is compressed...
|
|||
if (i == 0 && is_compressed) { |
|||
// Read compressed .code section...
|
|||
std::unique_ptr<u8[]> temp_buffer(new u8[exefs_header.section[i].size]); |
|||
file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size); |
|||
|
|||
// Decompress .code section...
|
|||
u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], |
|||
exefs_header.section[i].size); |
|||
buffer.resize(decompressed_size); |
|||
if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0], |
|||
decompressed_size)) { |
|||
error = ResultStatus::ErrorInvalidFormat; |
|||
return buffer; |
|||
} |
|||
// Section is uncompressed...
|
|||
} else { |
|||
buffer.resize(exefs_header.section[i].size); |
|||
file.ReadBytes(&buffer[0], exefs_header.section[i].size); |
|||
} |
|||
error = ResultStatus::Success; |
|||
return buffer; |
|||
} |
|||
} |
|||
error = ResultStatus::ErrorNotUsed; |
|||
return buffer; |
|||
} |
|||
|
|||
/**
|
|||
* Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) |
|||
* @param error_string Pointer to string to put error message if an error has occurred |
|||
* @todo Move NCSD parsing out of here and create a separate function for loading these |
|||
* @return True on success, otherwise false |
|||
*/ |
|||
ResultStatus AppLoader_NCCH::Load() { |
|||
INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str()); |
|||
|
|||
if (is_loaded) |
|||
return ResultStatus::ErrorAlreadyLoaded; |
|||
|
|||
file = File::IOFile(filename, "rb"); |
|||
|
|||
if (file.IsOpen()) { |
|||
file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); |
|||
|
|||
// Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
|
|||
if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) { |
|||
WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!"); |
|||
ncch_offset = 0x4000; |
|||
file.Seek(ncch_offset, 0); |
|||
file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); |
|||
} |
|||
|
|||
// Verify we are loading the correct file type...
|
|||
if (0 != memcmp(&ncch_header.magic, "NCCH", 4)) |
|||
return ResultStatus::ErrorInvalidFormat; |
|||
|
|||
// Read ExHeader...
|
|||
|
|||
file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)); |
|||
|
|||
is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; |
|||
entry_point = exheader_header.codeset_info.text.address; |
|||
|
|||
INFO_LOG(LOADER, "Name: %s", exheader_header.codeset_info.name); |
|||
INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no"); |
|||
INFO_LOG(LOADER, "Entry point: 0x%08X", entry_point); |
|||
|
|||
// Read ExeFS...
|
|||
|
|||
exefs_offset = ncch_header.exefs_offset * kBlockSize; |
|||
u32 exefs_size = ncch_header.exefs_size * kBlockSize; |
|||
|
|||
INFO_LOG(LOADER, "ExeFS offset: 0x%08X", exefs_offset); |
|||
INFO_LOG(LOADER, "ExeFS size: 0x%08X", exefs_size); |
|||
|
|||
file.Seek(exefs_offset + ncch_offset, 0); |
|||
file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); |
|||
|
|||
is_loaded = true; // Set state to loaded
|
|||
|
|||
LoadExec(); // Load the executable into memory for booting
|
|||
|
|||
return ResultStatus::Success; |
|||
} |
|||
return ResultStatus::Error; |
|||
} |
|||
|
|||
/**
|
|||
* Get the code (typically .code section) of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to code buffer |
|||
*/ |
|||
const std::vector<u8>& AppLoader_NCCH::ReadCode(ResultStatus& error) { |
|||
return LoadSectionExeFS(".code", code, error); |
|||
} |
|||
|
|||
/**
|
|||
* Get the icon (typically icon section) of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to icon buffer |
|||
*/ |
|||
const std::vector<u8>& AppLoader_NCCH::ReadIcon(ResultStatus& error) { |
|||
return LoadSectionExeFS("icon", icon, error); |
|||
} |
|||
|
|||
/**
|
|||
* Get the banner (typically banner section) of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to banner buffer |
|||
*/ |
|||
const std::vector<u8>& AppLoader_NCCH::ReadBanner(ResultStatus& error) { |
|||
return LoadSectionExeFS("banner", banner, error); |
|||
} |
|||
|
|||
/**
|
|||
* Get the logo (typically logo section) of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to logo buffer |
|||
*/ |
|||
const std::vector<u8>& AppLoader_NCCH::ReadLogo(ResultStatus& error) { |
|||
return LoadSectionExeFS("logo", logo, error); |
|||
} |
|||
|
|||
/**
|
|||
* Get the RomFs archive of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to RomFs archive buffer |
|||
*/ |
|||
const std::vector<u8>& AppLoader_NCCH::ReadRomFS(ResultStatus& error) { |
|||
// Check if the NCCH has a RomFS...
|
|||
if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) { |
|||
u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; |
|||
u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; |
|||
|
|||
INFO_LOG(LOADER, "RomFS offset: 0x%08X", romfs_offset); |
|||
INFO_LOG(LOADER, "RomFS size: 0x%08X", romfs_size); |
|||
|
|||
romfs.resize(romfs_size); |
|||
|
|||
file.Seek(romfs_offset, 0); |
|||
file.ReadBytes(&romfs[0], romfs_size); |
|||
|
|||
error = ResultStatus::Success; |
|||
return romfs; |
|||
} else { |
|||
NOTICE_LOG(LOADER, "RomFS unused"); |
|||
} |
|||
error = ResultStatus::ErrorNotUsed; |
|||
return romfs; |
|||
} |
|||
|
|||
} // namespace Loader
|
|||
@ -0,0 +1,227 @@ |
|||
// Copyright 2014 Citra Emulator Project |
|||
// Licensed under GPLv2 |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common.h" |
|||
#include "common/file_util.h" |
|||
|
|||
#include "core/loader/loader.h" |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|||
/// NCCH header (Note: "NCCH" appears to be a publically unknown acronym) |
|||
|
|||
struct NCCH_Header { |
|||
u8 signature[0x100]; |
|||
char magic[4]; |
|||
u32 content_size; |
|||
u8 partition_id[8]; |
|||
u16 maker_code; |
|||
u16 version; |
|||
u8 reserved_0[4]; |
|||
u8 program_id[8]; |
|||
u8 temp_flag; |
|||
u8 reserved_1[0x2f]; |
|||
u8 product_code[0x10]; |
|||
u8 extended_header_hash[0x20]; |
|||
u32 extended_header_size; |
|||
u8 reserved_2[4]; |
|||
u8 flags[8]; |
|||
u32 plain_region_offset; |
|||
u32 plain_region_size; |
|||
u8 reserved_3[8]; |
|||
u32 exefs_offset; |
|||
u32 exefs_size; |
|||
u32 exefs_hash_region_size; |
|||
u8 reserved_4[4]; |
|||
u32 romfs_offset; |
|||
u32 romfs_size; |
|||
u32 romfs_hash_region_size; |
|||
u8 reserved_5[4]; |
|||
u8 exefs_super_block_hash[0x20]; |
|||
u8 romfs_super_block_hash[0x20]; |
|||
}; |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|||
// ExeFS (executable file system) headers |
|||
|
|||
typedef struct { |
|||
char name[8]; |
|||
u32 offset; |
|||
u32 size; |
|||
} ExeFs_SectionHeader; |
|||
|
|||
typedef struct { |
|||
ExeFs_SectionHeader section[8]; |
|||
u8 reserved[0x80]; |
|||
u8 hashes[8][0x20]; |
|||
} ExeFs_Header; |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|||
// ExHeader (executable file system header) headers |
|||
|
|||
struct ExHeader_SystemInfoFlags{ |
|||
u8 reserved[5]; |
|||
u8 flag; |
|||
u8 remaster_version[2]; |
|||
}; |
|||
|
|||
struct ExHeader_CodeSegmentInfo{ |
|||
u32 address; |
|||
u32 num_max_pages; |
|||
u32 code_size; |
|||
}; |
|||
|
|||
struct ExHeader_CodeSetInfo { |
|||
u8 name[8]; |
|||
ExHeader_SystemInfoFlags flags; |
|||
ExHeader_CodeSegmentInfo text; |
|||
u8 stacksize[4]; |
|||
ExHeader_CodeSegmentInfo ro; |
|||
u8 reserved[4]; |
|||
ExHeader_CodeSegmentInfo data; |
|||
u8 bsssize[4]; |
|||
}; |
|||
|
|||
struct ExHeader_DependencyList{ |
|||
u8 program_id[0x30][8]; |
|||
}; |
|||
|
|||
struct ExHeader_SystemInfo{ |
|||
u32 save_data_size; |
|||
u8 reserved[4]; |
|||
u8 jump_id[8]; |
|||
u8 reserved_2[0x30]; |
|||
}; |
|||
|
|||
struct ExHeader_StorageInfo{ |
|||
u8 ext_save_data_id[8]; |
|||
u8 system_save_data_id[8]; |
|||
u8 reserved[8]; |
|||
u8 access_info[7]; |
|||
u8 other_attributes; |
|||
}; |
|||
|
|||
struct ExHeader_ARM11_SystemLocalCaps{ |
|||
u8 program_id[8]; |
|||
u8 flags[8]; |
|||
u8 resource_limit_descriptor[0x10][2]; |
|||
ExHeader_StorageInfo storage_info; |
|||
u8 service_access_control[0x20][8]; |
|||
u8 reserved[0x1f]; |
|||
u8 resource_limit_category; |
|||
}; |
|||
|
|||
struct ExHeader_ARM11_KernelCaps{ |
|||
u8 descriptors[28][4]; |
|||
u8 reserved[0x10]; |
|||
}; |
|||
|
|||
struct ExHeader_ARM9_AccessControl{ |
|||
u8 descriptors[15]; |
|||
u8 descversion; |
|||
}; |
|||
|
|||
struct ExHeader_Header{ |
|||
ExHeader_CodeSetInfo codeset_info; |
|||
ExHeader_DependencyList dependency_list; |
|||
ExHeader_SystemInfo system_info; |
|||
ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; |
|||
ExHeader_ARM11_KernelCaps arm11_kernel_caps; |
|||
ExHeader_ARM9_AccessControl arm9_access_control; |
|||
struct { |
|||
u8 signature[0x100]; |
|||
u8 ncch_public_key_modulus[0x100]; |
|||
ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; |
|||
ExHeader_ARM11_KernelCaps arm11_kernel_caps; |
|||
ExHeader_ARM9_AccessControl arm9_access_control; |
|||
} access_desc; |
|||
}; |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|||
// Loader namespace |
|||
|
|||
namespace Loader { |
|||
|
|||
/// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) |
|||
class AppLoader_NCCH : public AppLoader { |
|||
public: |
|||
AppLoader_NCCH(const std::string& filename); |
|||
~AppLoader_NCCH(); |
|||
|
|||
/** |
|||
* Load the application |
|||
* @return ResultStatus result of function |
|||
*/ |
|||
ResultStatus Load(); |
|||
|
|||
/** |
|||
* Get the code (typically .code section) of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to code buffer |
|||
*/ |
|||
const std::vector<u8>& ReadCode(ResultStatus& error); |
|||
|
|||
/** |
|||
* Get the icon (typically icon section) of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to icon buffer |
|||
*/ |
|||
const std::vector<u8>& ReadIcon(ResultStatus& error); |
|||
|
|||
/** |
|||
* Get the banner (typically banner section) of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to banner buffer |
|||
*/ |
|||
const std::vector<u8>& ReadBanner(ResultStatus& error); |
|||
|
|||
/** |
|||
* Get the logo (typically logo section) of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to logo buffer |
|||
*/ |
|||
const std::vector<u8>& ReadLogo(ResultStatus& error); |
|||
|
|||
/** |
|||
* Get the RomFs archive of the application |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to RomFs archive buffer |
|||
*/ |
|||
const std::vector<u8>& ReadRomFS(ResultStatus& error); |
|||
|
|||
private: |
|||
|
|||
/** |
|||
* Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) |
|||
* @param name Name of section to read out of NCCH file |
|||
* @param buffer Vector to read data into |
|||
* @param error ResultStatus result of function |
|||
* @return Reference to buffer of data that was read |
|||
*/ |
|||
const std::vector<u8>& LoadSectionExeFS(const char* name, std::vector<u8>& buffer, |
|||
ResultStatus& error); |
|||
|
|||
/** |
|||
* Loads .code section into memory for booting |
|||
* @return ResultStatus result of function |
|||
*/ |
|||
ResultStatus LoadExec(); |
|||
|
|||
File::IOFile file; |
|||
std::string filename; |
|||
|
|||
bool is_loaded; |
|||
bool is_compressed; |
|||
|
|||
u32 entry_point; |
|||
u32 ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header |
|||
u32 exefs_offset; |
|||
|
|||
NCCH_Header ncch_header; |
|||
ExeFs_Header exefs_header; |
|||
ExHeader_Header exheader_header; |
|||
}; |
|||
|
|||
} // namespace Loader |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue