Sources: Run clang-format on everything.
This commit is contained in:
parent
fe948af095
commit
dc8479928c
386 changed files with 19560 additions and 18080 deletions
|
@ -37,20 +37,14 @@ namespace Loader {
|
|||
* The BSS section must be cleared manually by the application.
|
||||
*/
|
||||
|
||||
enum THREEDSX_Error {
|
||||
ERROR_NONE = 0,
|
||||
ERROR_READ = 1,
|
||||
ERROR_FILE = 2,
|
||||
ERROR_ALLOC = 3
|
||||
};
|
||||
enum THREEDSX_Error { ERROR_NONE = 0, ERROR_READ = 1, ERROR_FILE = 2, ERROR_ALLOC = 3 };
|
||||
|
||||
static const u32 RELOCBUFSIZE = 512;
|
||||
static const unsigned int NUM_SEGMENTS = 3;
|
||||
|
||||
// File header
|
||||
#pragma pack(1)
|
||||
struct THREEDSX_Header
|
||||
{
|
||||
struct THREEDSX_Header {
|
||||
u32 magic;
|
||||
u16 header_size, reloc_hdr_size;
|
||||
u32 format_ver;
|
||||
|
@ -66,11 +60,11 @@ struct THREEDSX_Header
|
|||
};
|
||||
|
||||
// Relocation header: all fields (even extra unknown fields) are guaranteed to be relocation counts.
|
||||
struct THREEDSX_RelocHdr
|
||||
{
|
||||
struct THREEDSX_RelocHdr {
|
||||
// # of absolute relocations (that is, fix address to post-relocation memory layout)
|
||||
u32 cross_segment_absolute;
|
||||
// # of cross-segment relative relocations (that is, 32bit signed offsets that need to be patched)
|
||||
// # of cross-segment relative relocations (that is, 32bit signed offsets that need to be
|
||||
// patched)
|
||||
u32 cross_segment_relative;
|
||||
// more?
|
||||
|
||||
|
@ -80,21 +74,18 @@ struct THREEDSX_RelocHdr
|
|||
};
|
||||
|
||||
// Relocation entry: from the current pointer, skip X words and patch Y words
|
||||
struct THREEDSX_Reloc
|
||||
{
|
||||
struct THREEDSX_Reloc {
|
||||
u16 skip, patch;
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
struct THREEloadinfo
|
||||
{
|
||||
struct THREEloadinfo {
|
||||
u8* seg_ptrs[3]; // code, rodata & data
|
||||
u32 seg_addrs[3];
|
||||
u32 seg_sizes[3];
|
||||
};
|
||||
|
||||
static u32 TranslateAddr(u32 addr, const THREEloadinfo *loadinfo, u32* offsets)
|
||||
{
|
||||
static u32 TranslateAddr(u32 addr, const THREEloadinfo* loadinfo, u32* offsets) {
|
||||
if (addr < offsets[0])
|
||||
return loadinfo->seg_addrs[0] + addr;
|
||||
if (addr < offsets[1])
|
||||
|
@ -105,8 +96,8 @@ static u32 TranslateAddr(u32 addr, const THREEloadinfo *loadinfo, u32* offsets)
|
|||
using Kernel::SharedPtr;
|
||||
using Kernel::CodeSet;
|
||||
|
||||
static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, SharedPtr<CodeSet>* out_codeset)
|
||||
{
|
||||
static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr,
|
||||
SharedPtr<CodeSet>* out_codeset) {
|
||||
if (!file.IsOpen())
|
||||
return ERROR_FILE;
|
||||
|
||||
|
@ -118,13 +109,14 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, Shared
|
|||
return ERROR_READ;
|
||||
|
||||
THREEloadinfo loadinfo;
|
||||
//loadinfo segments must be a multiple of 0x1000
|
||||
loadinfo.seg_sizes[0] = (hdr.code_seg_size + 0xFFF) &~0xFFF;
|
||||
loadinfo.seg_sizes[1] = (hdr.rodata_seg_size + 0xFFF) &~0xFFF;
|
||||
loadinfo.seg_sizes[2] = (hdr.data_seg_size + 0xFFF) &~0xFFF;
|
||||
u32 offsets[2] = { loadinfo.seg_sizes[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] };
|
||||
// loadinfo segments must be a multiple of 0x1000
|
||||
loadinfo.seg_sizes[0] = (hdr.code_seg_size + 0xFFF) & ~0xFFF;
|
||||
loadinfo.seg_sizes[1] = (hdr.rodata_seg_size + 0xFFF) & ~0xFFF;
|
||||
loadinfo.seg_sizes[2] = (hdr.data_seg_size + 0xFFF) & ~0xFFF;
|
||||
u32 offsets[2] = {loadinfo.seg_sizes[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1]};
|
||||
u32 n_reloc_tables = hdr.reloc_hdr_size / sizeof(u32);
|
||||
std::vector<u8> program_image(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2]);
|
||||
std::vector<u8> program_image(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] +
|
||||
loadinfo.seg_sizes[2]);
|
||||
|
||||
loadinfo.seg_addrs[0] = base_addr;
|
||||
loadinfo.seg_addrs[1] = loadinfo.seg_addrs[0] + loadinfo.seg_sizes[0];
|
||||
|
@ -149,7 +141,8 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, Shared
|
|||
return ERROR_READ;
|
||||
if (file.ReadBytes(loadinfo.seg_ptrs[1], hdr.rodata_seg_size) != hdr.rodata_seg_size)
|
||||
return ERROR_READ;
|
||||
if (file.ReadBytes(loadinfo.seg_ptrs[2], hdr.data_seg_size - hdr.bss_size) != hdr.data_seg_size - hdr.bss_size)
|
||||
if (file.ReadBytes(loadinfo.seg_ptrs[2], hdr.data_seg_size - hdr.bss_size) !=
|
||||
hdr.data_seg_size - hdr.bss_size)
|
||||
return ERROR_READ;
|
||||
|
||||
// BSS clear
|
||||
|
@ -157,11 +150,12 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, Shared
|
|||
|
||||
// Relocate the segments
|
||||
for (unsigned int current_segment = 0; current_segment < NUM_SEGMENTS; ++current_segment) {
|
||||
for (unsigned current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) {
|
||||
for (unsigned current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables;
|
||||
current_segment_reloc_table++) {
|
||||
u32 n_relocs = relocs[current_segment * n_reloc_tables + current_segment_reloc_table];
|
||||
if (current_segment_reloc_table >= 2) {
|
||||
// We are not using this table - ignore it because we don't know what it dose
|
||||
file.Seek(n_relocs*sizeof(THREEDSX_Reloc), SEEK_CUR);
|
||||
file.Seek(n_relocs * sizeof(THREEDSX_Reloc), SEEK_CUR);
|
||||
continue;
|
||||
}
|
||||
THREEDSX_Reloc reloc_table[RELOCBUFSIZE];
|
||||
|
@ -173,17 +167,20 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, Shared
|
|||
u32 remaining = std::min(RELOCBUFSIZE, n_relocs);
|
||||
n_relocs -= remaining;
|
||||
|
||||
if (file.ReadBytes(reloc_table, remaining * sizeof(THREEDSX_Reloc)) != remaining * sizeof(THREEDSX_Reloc))
|
||||
if (file.ReadBytes(reloc_table, remaining * sizeof(THREEDSX_Reloc)) !=
|
||||
remaining * sizeof(THREEDSX_Reloc))
|
||||
return ERROR_READ;
|
||||
|
||||
for (unsigned current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) {
|
||||
for (unsigned current_inprogress = 0;
|
||||
current_inprogress < remaining && pos < end_pos; current_inprogress++) {
|
||||
const auto& table = reloc_table[current_inprogress];
|
||||
LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)", current_segment_reloc_table,
|
||||
static_cast<u32>(table.skip), static_cast<u32>(table.patch));
|
||||
pos += table.skip;
|
||||
s32 num_patches = table.patch;
|
||||
while (0 < num_patches && pos < end_pos) {
|
||||
u32 in_addr = static_cast<u32>(reinterpret_cast<u8*>(pos) - program_image.data());
|
||||
u32 in_addr =
|
||||
static_cast<u32>(reinterpret_cast<u8*>(pos) - program_image.data());
|
||||
u32 addr = TranslateAddr(*pos, &loadinfo, offsets);
|
||||
LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)",
|
||||
base_addr + in_addr, addr, current_segment_reloc_table, *pos);
|
||||
|
@ -195,7 +192,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, Shared
|
|||
*pos = static_cast<u32>(addr - in_addr);
|
||||
break;
|
||||
default:
|
||||
break; //this should never happen
|
||||
break; // this should never happen
|
||||
}
|
||||
pos++;
|
||||
num_patches--;
|
||||
|
@ -209,23 +206,24 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, Shared
|
|||
SharedPtr<CodeSet> code_set = CodeSet::Create("", 0);
|
||||
|
||||
code_set->code.offset = loadinfo.seg_ptrs[0] - program_image.data();
|
||||
code_set->code.addr = loadinfo.seg_addrs[0];
|
||||
code_set->code.size = loadinfo.seg_sizes[0];
|
||||
code_set->code.addr = loadinfo.seg_addrs[0];
|
||||
code_set->code.size = loadinfo.seg_sizes[0];
|
||||
|
||||
code_set->rodata.offset = loadinfo.seg_ptrs[1] - program_image.data();
|
||||
code_set->rodata.addr = loadinfo.seg_addrs[1];
|
||||
code_set->rodata.size = loadinfo.seg_sizes[1];
|
||||
code_set->rodata.addr = loadinfo.seg_addrs[1];
|
||||
code_set->rodata.size = loadinfo.seg_sizes[1];
|
||||
|
||||
code_set->data.offset = loadinfo.seg_ptrs[2] - program_image.data();
|
||||
code_set->data.addr = loadinfo.seg_addrs[2];
|
||||
code_set->data.size = loadinfo.seg_sizes[2];
|
||||
code_set->data.addr = loadinfo.seg_addrs[2];
|
||||
code_set->data.size = loadinfo.seg_sizes[2];
|
||||
|
||||
code_set->entrypoint = code_set->code.addr;
|
||||
code_set->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
|
||||
|
||||
LOG_DEBUG(Loader, "code size: 0x%X", loadinfo.seg_sizes[0]);
|
||||
LOG_DEBUG(Loader, "rodata size: 0x%X", loadinfo.seg_sizes[1]);
|
||||
LOG_DEBUG(Loader, "data size: 0x%X (including 0x%X of bss)", loadinfo.seg_sizes[2], hdr.bss_size);
|
||||
LOG_DEBUG(Loader, "data size: 0x%X (including 0x%X of bss)", loadinfo.seg_sizes[2],
|
||||
hdr.bss_size);
|
||||
|
||||
*out_codeset = code_set;
|
||||
return ERROR_NONE;
|
||||
|
@ -260,17 +258,20 @@ ResultStatus AppLoader_THREEDSX::Load() {
|
|||
Kernel::g_current_process->address_mappings = default_address_mappings;
|
||||
|
||||
// Attach the default resource limit (APPLICATION) to the process
|
||||
Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
|
||||
Kernel::g_current_process->resource_limit =
|
||||
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
|
||||
|
||||
Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE);
|
||||
|
||||
Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this), Service::FS::ArchiveIdCode::RomFS);
|
||||
Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this),
|
||||
Service::FS::ArchiveIdCode::RomFS);
|
||||
|
||||
is_loaded = true;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_THREEDSX::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) {
|
||||
ResultStatus AppLoader_THREEDSX::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file,
|
||||
u64& offset, u64& size) {
|
||||
if (!file.IsOpen())
|
||||
return ResultStatus::Error;
|
||||
|
||||
|
|
|
@ -17,8 +17,10 @@ namespace Loader {
|
|||
/// Loads an 3DSX file
|
||||
class AppLoader_THREEDSX final : public AppLoader {
|
||||
public:
|
||||
AppLoader_THREEDSX(FileUtil::IOFile&& file, const std::string& filename, const std::string& filepath)
|
||||
: AppLoader(std::move(file)), filename(std::move(filename)), filepath(filepath) {}
|
||||
AppLoader_THREEDSX(FileUtil::IOFile&& file, const std::string& filename,
|
||||
const std::string& filepath)
|
||||
: AppLoader(std::move(file)), filename(std::move(filename)), filepath(filepath) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the file
|
||||
|
@ -55,7 +57,8 @@ public:
|
|||
* @param size Size of the RomFS in bytes
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) override;
|
||||
ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
|
||||
u64& size) override;
|
||||
|
||||
private:
|
||||
std::string filename;
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
|
@ -24,112 +24,111 @@ using Kernel::CodeSet;
|
|||
|
||||
// File type
|
||||
enum ElfType {
|
||||
ET_NONE = 0,
|
||||
ET_REL = 1,
|
||||
ET_EXEC = 2,
|
||||
ET_DYN = 3,
|
||||
ET_CORE = 4,
|
||||
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_NONE = 0,
|
||||
EM_M32 = 1,
|
||||
EM_SPARC = 2,
|
||||
EM_386 = 3,
|
||||
EM_68K = 4,
|
||||
EM_88K = 5,
|
||||
EM_860 = 7,
|
||||
EM_MIPS = 8
|
||||
EM_386 = 3,
|
||||
EM_68K = 4,
|
||||
EM_88K = 5,
|
||||
EM_860 = 7,
|
||||
EM_MIPS = 8
|
||||
};
|
||||
|
||||
// File version
|
||||
#define EV_NONE 0
|
||||
#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_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_PAD 7
|
||||
#define EI_NIDENT 16
|
||||
|
||||
// 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_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,
|
||||
enum ElfSectionFlags {
|
||||
SHF_WRITE = 0x1,
|
||||
SHF_ALLOC = 0x2,
|
||||
SHF_EXECINSTR = 0x4,
|
||||
SHF_MASKPROC = 0xF0000000,
|
||||
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
|
||||
#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 0x1
|
||||
#define PF_W 0x2
|
||||
#define PF_R 0x4
|
||||
#define PF_X 0x1
|
||||
#define PF_W 0x2
|
||||
#define PF_R 0x4
|
||||
#define PF_MASKPROC 0xF0000000
|
||||
|
||||
typedef unsigned int Elf32_Addr;
|
||||
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;
|
||||
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;
|
||||
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
|
||||
|
@ -138,7 +137,7 @@ struct Elf32_Shdr {
|
|||
Elf32_Word sh_type;
|
||||
Elf32_Word sh_flags;
|
||||
Elf32_Addr sh_addr;
|
||||
Elf32_Off sh_offset;
|
||||
Elf32_Off sh_offset;
|
||||
Elf32_Word sh_size;
|
||||
Elf32_Word sh_link;
|
||||
Elf32_Word sh_info;
|
||||
|
@ -149,7 +148,7 @@ struct Elf32_Shdr {
|
|||
// Segment header
|
||||
struct Elf32_Phdr {
|
||||
Elf32_Word p_type;
|
||||
Elf32_Off p_offset;
|
||||
Elf32_Off p_offset;
|
||||
Elf32_Addr p_vaddr;
|
||||
Elf32_Addr p_paddr;
|
||||
Elf32_Word p_filesz;
|
||||
|
@ -160,12 +159,12 @@ struct Elf32_Phdr {
|
|||
|
||||
// Symbol table entry
|
||||
struct Elf32_Sym {
|
||||
Elf32_Word st_name;
|
||||
Elf32_Addr st_value;
|
||||
Elf32_Word st_size;
|
||||
Elf32_Word st_name;
|
||||
Elf32_Addr st_value;
|
||||
Elf32_Word st_size;
|
||||
unsigned char st_info;
|
||||
unsigned char st_other;
|
||||
Elf32_Half st_shndx;
|
||||
Elf32_Half st_shndx;
|
||||
};
|
||||
|
||||
// Relocation entries
|
||||
|
@ -181,35 +180,51 @@ typedef int SectionID;
|
|||
|
||||
class ElfReader {
|
||||
private:
|
||||
char *base;
|
||||
u32 *base32;
|
||||
char* base;
|
||||
u32* base32;
|
||||
|
||||
Elf32_Ehdr *header;
|
||||
Elf32_Phdr *segments;
|
||||
Elf32_Shdr *sections;
|
||||
Elf32_Ehdr* header;
|
||||
Elf32_Phdr* segments;
|
||||
Elf32_Shdr* sections;
|
||||
|
||||
u32 *sectionAddrs;
|
||||
u32* sectionAddrs;
|
||||
bool relocate;
|
||||
u32 entryPoint;
|
||||
|
||||
public:
|
||||
ElfReader(void *ptr);
|
||||
ElfReader(void* ptr);
|
||||
|
||||
u32 Read32(int off) const { return base32[off >> 2]; }
|
||||
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); }
|
||||
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);
|
||||
}
|
||||
SharedPtr<CodeSet> 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 {
|
||||
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)
|
||||
|
@ -220,19 +235,23 @@ public:
|
|||
bool IsCodeSection(int section) const {
|
||||
return sections[section].sh_type == SHT_PROGBITS;
|
||||
}
|
||||
const u8 *GetSegmentPtr(int segment) {
|
||||
const u8* GetSegmentPtr(int segment) {
|
||||
return GetPtr(segments[segment].p_offset);
|
||||
}
|
||||
u32 GetSectionAddr(SectionID section) const { return sectionAddrs[section]; }
|
||||
unsigned int GetSectionSize(SectionID section) const { return sections[section].sh_size; }
|
||||
SectionID GetSectionByName(const char *name, int firstSection = 0) const; //-1 for not found
|
||||
u32 GetSectionAddr(SectionID section) const {
|
||||
return sectionAddrs[section];
|
||||
}
|
||||
unsigned 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() const {
|
||||
return relocate;
|
||||
}
|
||||
};
|
||||
|
||||
ElfReader::ElfReader(void *ptr) {
|
||||
ElfReader::ElfReader(void* ptr) {
|
||||
base = (char*)ptr;
|
||||
base32 = (u32*)ptr;
|
||||
header = (Elf32_Ehdr*)ptr;
|
||||
|
@ -245,7 +264,7 @@ ElfReader::ElfReader(void *ptr) {
|
|||
LoadSymbols();
|
||||
}
|
||||
|
||||
const char *ElfReader::GetSectionName(int section) const {
|
||||
const char* ElfReader::GetSectionName(int section) const {
|
||||
if (sections[section].sh_type == SHT_NULL)
|
||||
return nullptr;
|
||||
|
||||
|
@ -303,12 +322,15 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
|
|||
} else if (permission_flags == (PF_R | PF_W)) {
|
||||
codeset_segment = &codeset->data;
|
||||
} else {
|
||||
LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id %u with flags %X", i, p->p_flags);
|
||||
LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id %u with flags %X", i,
|
||||
p->p_flags);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (codeset_segment->size != 0) {
|
||||
LOG_ERROR(Loader, "ELF has more than one segment of the same type. Skipping extra segment (id %i)", i);
|
||||
LOG_ERROR(Loader, "ELF has more than one segment of the same type. Skipping extra "
|
||||
"segment (id %i)",
|
||||
i);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -332,9 +354,9 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
|
|||
return codeset;
|
||||
}
|
||||
|
||||
SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const {
|
||||
SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const {
|
||||
for (int i = firstSection; i < header->e_shnum; i++) {
|
||||
const char *secname = GetSectionName(i);
|
||||
const char* secname = GetSectionName(i);
|
||||
|
||||
if (secname != nullptr && strcmp(name, secname) == 0)
|
||||
return i;
|
||||
|
@ -347,9 +369,9 @@ bool ElfReader::LoadSymbols() {
|
|||
SectionID sec = GetSectionByName(".symtab");
|
||||
if (sec != -1) {
|
||||
int stringSection = sections[sec].sh_link;
|
||||
const char *stringBase = reinterpret_cast<const char*>(GetSectionDataPtr(stringSection));
|
||||
const char* stringBase = reinterpret_cast<const char*>(GetSectionDataPtr(stringSection));
|
||||
|
||||
//We have a symbol table!
|
||||
// We have a symbol table!
|
||||
const Elf32_Sym* symtab = reinterpret_cast<const Elf32_Sym*>(GetSectionDataPtr(sec));
|
||||
unsigned int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym);
|
||||
for (unsigned sym = 0; sym < numSymbols; sym++) {
|
||||
|
@ -359,7 +381,7 @@ bool ElfReader::LoadSymbols() {
|
|||
|
||||
int type = symtab[sym].st_info & 0xF;
|
||||
|
||||
const char *name = stringBase + symtab[sym].st_name;
|
||||
const char* name = stringBase + symtab[sym].st_name;
|
||||
|
||||
Symbols::Add(symtab[sym].st_value, name, size, type);
|
||||
|
||||
|
@ -411,7 +433,8 @@ ResultStatus AppLoader_ELF::Load() {
|
|||
Kernel::g_current_process->address_mappings = default_address_mappings;
|
||||
|
||||
// Attach the default resource limit (APPLICATION) to the process
|
||||
Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
|
||||
Kernel::g_current_process->resource_limit =
|
||||
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
|
||||
|
||||
Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE);
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@ namespace Loader {
|
|||
class AppLoader_ELF final : public AppLoader {
|
||||
public:
|
||||
AppLoader_ELF(FileUtil::IOFile&& file, std::string filename)
|
||||
: AppLoader(std::move(file)), filename(std::move(filename)) { }
|
||||
: AppLoader(std::move(file)), filename(std::move(filename)) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the file
|
||||
|
|
|
@ -18,17 +18,17 @@
|
|||
namespace Loader {
|
||||
|
||||
const std::initializer_list<Kernel::AddressMapping> default_address_mappings = {
|
||||
{ 0x1FF50000, 0x8000, true }, // part of DSP RAM
|
||||
{ 0x1FF70000, 0x8000, true }, // part of DSP RAM
|
||||
{ 0x1F000000, 0x600000, false }, // entire VRAM
|
||||
{0x1FF50000, 0x8000, true}, // part of DSP RAM
|
||||
{0x1FF70000, 0x8000, true}, // part of DSP RAM
|
||||
{0x1F000000, 0x600000, false}, // entire VRAM
|
||||
};
|
||||
|
||||
FileType IdentifyFile(FileUtil::IOFile& file) {
|
||||
FileType type;
|
||||
|
||||
#define CHECK_TYPE(loader) \
|
||||
type = AppLoader_##loader::IdentifyType(file); \
|
||||
if (FileType::Error != type) \
|
||||
#define CHECK_TYPE(loader) \
|
||||
type = AppLoader_##loader::IdentifyType(file); \
|
||||
if (FileType::Error != type) \
|
||||
return type;
|
||||
|
||||
CHECK_TYPE(THREEDSX)
|
||||
|
@ -100,7 +100,8 @@ const char* GetFileTypeString(FileType type) {
|
|||
* @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
|
||||
*/
|
||||
static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileType type,
|
||||
const std::string& filename, const std::string& filepath) {
|
||||
const std::string& filename,
|
||||
const std::string& filepath) {
|
||||
switch (type) {
|
||||
|
||||
// 3DSX file format.
|
||||
|
|
|
@ -30,7 +30,7 @@ enum class FileType {
|
|||
CXI,
|
||||
CIA,
|
||||
ELF,
|
||||
THREEDSX, //3DSX
|
||||
THREEDSX, // 3DSX
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -81,8 +81,10 @@ constexpr u32 MakeMagic(char a, char b, char c, char d) {
|
|||
/// Interface for loading an application
|
||||
class AppLoader : NonCopyable {
|
||||
public:
|
||||
AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) { }
|
||||
virtual ~AppLoader() { }
|
||||
AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) {
|
||||
}
|
||||
virtual ~AppLoader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of this file
|
||||
|
@ -140,7 +142,8 @@ public:
|
|||
* @param size The size of the romfs
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) {
|
||||
virtual ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
|
||||
u64& size) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
|
||||
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)
|
||||
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
|
||||
|
@ -44,7 +44,8 @@ static u32 LZSS_GetDecompressedSize(const u8* buffer, u32 size) {
|
|||
* @param decompressed_size Size of decompressed buffer
|
||||
* @return True on success, otherwise false
|
||||
*/
|
||||
static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) {
|
||||
static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decompressed,
|
||||
u32 decompressed_size) {
|
||||
const u8* footer = compressed + compressed_size - 8;
|
||||
u32 buffer_top_and_bottom = *reinterpret_cast<const u32*>(footer);
|
||||
u32 out = decompressed_size;
|
||||
|
@ -55,7 +56,7 @@ static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decom
|
|||
memcpy(decompressed, compressed, compressed_size);
|
||||
|
||||
while (index > stop_index) {
|
||||
u8 control = compressed[--index];
|
||||
u8 control = compressed[--index];
|
||||
|
||||
for (unsigned i = 0; i < 8; i++) {
|
||||
if (index <= stop_index)
|
||||
|
@ -128,7 +129,7 @@ ResultStatus AppLoader_NCCH::LoadExec() {
|
|||
std::vector<u8> code;
|
||||
if (ResultStatus::Success == ReadCode(code)) {
|
||||
std::string process_name = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
(const char*)exheader_header.codeset_info.name, 8);
|
||||
(const char*)exheader_header.codeset_info.name, 8);
|
||||
|
||||
SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, ncch_header.program_id);
|
||||
|
||||
|
@ -147,7 +148,8 @@ ResultStatus AppLoader_NCCH::LoadExec() {
|
|||
|
||||
codeset->data.offset = codeset->rodata.offset + codeset->rodata.size;
|
||||
codeset->data.addr = exheader_header.codeset_info.data.address;
|
||||
codeset->data.size = exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE + bss_page_size;
|
||||
codeset->data.size =
|
||||
exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE + bss_page_size;
|
||||
|
||||
codeset->entrypoint = codeset->code.addr;
|
||||
codeset->memory = std::make_shared<std::vector<u8>>(std::move(code));
|
||||
|
@ -155,15 +157,18 @@ ResultStatus AppLoader_NCCH::LoadExec() {
|
|||
Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
|
||||
|
||||
// Attach a resource limit to the process based on the resource limit category
|
||||
Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(
|
||||
static_cast<Kernel::ResourceLimitCategory>(exheader_header.arm11_system_local_caps.resource_limit_category));
|
||||
Kernel::g_current_process->resource_limit =
|
||||
Kernel::ResourceLimit::GetForCategory(static_cast<Kernel::ResourceLimitCategory>(
|
||||
exheader_header.arm11_system_local_caps.resource_limit_category));
|
||||
|
||||
// Set the default CPU core for this process
|
||||
Kernel::g_current_process->ideal_processor = exheader_header.arm11_system_local_caps.ideal_processor;
|
||||
Kernel::g_current_process->ideal_processor =
|
||||
exheader_header.arm11_system_local_caps.ideal_processor;
|
||||
|
||||
// Copy data while converting endianess
|
||||
std::array<u32, ARRAY_SIZE(exheader_header.arm11_kernel_caps.descriptors)> kernel_caps;
|
||||
std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), begin(kernel_caps));
|
||||
std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(),
|
||||
begin(kernel_caps));
|
||||
Kernel::g_current_process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size());
|
||||
|
||||
s32 priority = exheader_header.arm11_system_local_caps.priority;
|
||||
|
@ -192,7 +197,8 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
|
|||
LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", section_number,
|
||||
section.offset, section.size, section.name);
|
||||
|
||||
s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset);
|
||||
s64 section_offset =
|
||||
(section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset);
|
||||
file.Seek(section_offset, SEEK_SET);
|
||||
|
||||
if (strcmp(section.name, ".code") == 0 && is_compressed) {
|
||||
|
@ -254,25 +260,25 @@ ResultStatus AppLoader_NCCH::LoadExeFS() {
|
|||
if (file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header))
|
||||
return ResultStatus::Error;
|
||||
|
||||
is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
|
||||
entry_point = exheader_header.codeset_info.text.address;
|
||||
code_size = exheader_header.codeset_info.text.code_size;
|
||||
stack_size = exheader_header.codeset_info.stack_size;
|
||||
bss_size = exheader_header.codeset_info.bss_size;
|
||||
core_version = exheader_header.arm11_system_local_caps.core_version;
|
||||
priority = exheader_header.arm11_system_local_caps.priority;
|
||||
is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
|
||||
entry_point = exheader_header.codeset_info.text.address;
|
||||
code_size = exheader_header.codeset_info.text.code_size;
|
||||
stack_size = exheader_header.codeset_info.stack_size;
|
||||
bss_size = exheader_header.codeset_info.bss_size;
|
||||
core_version = exheader_header.arm11_system_local_caps.core_version;
|
||||
priority = exheader_header.arm11_system_local_caps.priority;
|
||||
resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category;
|
||||
|
||||
LOG_INFO(Loader, "Name: %s" , exheader_header.codeset_info.name);
|
||||
LOG_INFO(Loader, "Program ID: %016llX" , ncch_header.program_id);
|
||||
LOG_DEBUG(Loader, "Code compressed: %s" , is_compressed ? "yes" : "no");
|
||||
LOG_INFO(Loader, "Name: %s", exheader_header.codeset_info.name);
|
||||
LOG_INFO(Loader, "Program ID: %016llX", ncch_header.program_id);
|
||||
LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no");
|
||||
LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
|
||||
LOG_DEBUG(Loader, "Code size: 0x%08X", code_size);
|
||||
LOG_DEBUG(Loader, "Stack size: 0x%08X", stack_size);
|
||||
LOG_DEBUG(Loader, "Bss size: 0x%08X", bss_size);
|
||||
LOG_DEBUG(Loader, "Core version: %d" , core_version);
|
||||
LOG_DEBUG(Loader, "Thread priority: 0x%X" , priority);
|
||||
LOG_DEBUG(Loader, "Resource limit category: %d" , resource_limit_category);
|
||||
LOG_DEBUG(Loader, "Core version: %d", core_version);
|
||||
LOG_DEBUG(Loader, "Thread priority: 0x%X", priority);
|
||||
LOG_DEBUG(Loader, "Resource limit category: %d", resource_limit_category);
|
||||
|
||||
if (exheader_header.arm11_system_local_caps.program_id != ncch_header.program_id) {
|
||||
LOG_ERROR(Loader, "ExHeader Program ID mismatch: the ROM is probably encrypted.");
|
||||
|
@ -309,7 +315,8 @@ ResultStatus AppLoader_NCCH::Load() {
|
|||
if (ResultStatus::Success != result)
|
||||
return result;
|
||||
|
||||
Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this), Service::FS::ArchiveIdCode::RomFS);
|
||||
Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this),
|
||||
Service::FS::ArchiveIdCode::RomFS);
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
|
@ -329,7 +336,8 @@ ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) {
|
|||
return LoadSectionExeFS("logo", buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) {
|
||||
ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
|
||||
u64& size) {
|
||||
if (!file.IsOpen())
|
||||
return ResultStatus::Error;
|
||||
|
||||
|
@ -341,7 +349,7 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_
|
|||
LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset);
|
||||
LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size);
|
||||
|
||||
if (file.GetSize () < romfs_offset + romfs_size)
|
||||
if (file.GetSize() < romfs_offset + romfs_size)
|
||||
return ResultStatus::Error;
|
||||
|
||||
// We reopen the file, to allow its position to be independent from file's
|
||||
|
|
|
@ -164,7 +164,8 @@ namespace Loader {
|
|||
class AppLoader_NCCH final : public AppLoader {
|
||||
public:
|
||||
AppLoader_NCCH(FileUtil::IOFile&& file, const std::string& filepath)
|
||||
: AppLoader(std::move(file)), filepath(filepath) { }
|
||||
: AppLoader(std::move(file)), filepath(filepath) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the file
|
||||
|
@ -222,10 +223,10 @@ public:
|
|||
* @param size Size of the RomFS in bytes
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) override;
|
||||
ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
|
||||
u64& size) override;
|
||||
|
||||
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
|
||||
|
@ -246,24 +247,24 @@ private:
|
|||
*/
|
||||
ResultStatus LoadExeFS();
|
||||
|
||||
bool is_exefs_loaded = false;
|
||||
bool is_compressed = false;
|
||||
bool is_exefs_loaded = false;
|
||||
bool is_compressed = false;
|
||||
|
||||
u32 entry_point = 0;
|
||||
u32 code_size = 0;
|
||||
u32 stack_size = 0;
|
||||
u32 bss_size = 0;
|
||||
u32 core_version = 0;
|
||||
u8 priority = 0;
|
||||
u8 resource_limit_category = 0;
|
||||
u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header
|
||||
u32 exefs_offset = 0;
|
||||
u32 entry_point = 0;
|
||||
u32 code_size = 0;
|
||||
u32 stack_size = 0;
|
||||
u32 bss_size = 0;
|
||||
u32 core_version = 0;
|
||||
u8 priority = 0;
|
||||
u8 resource_limit_category = 0;
|
||||
u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header
|
||||
u32 exefs_offset = 0;
|
||||
|
||||
NCCH_Header ncch_header;
|
||||
ExeFs_Header exefs_header;
|
||||
NCCH_Header ncch_header;
|
||||
ExeFs_Header exefs_header;
|
||||
ExHeader_Header exheader_header;
|
||||
|
||||
std::string filepath;
|
||||
std::string filepath;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
|
|
@ -56,7 +56,7 @@ struct SMDH {
|
|||
Italian = 4,
|
||||
Spanish = 5,
|
||||
SimplifiedChinese = 6,
|
||||
Korean= 7,
|
||||
Korean = 7,
|
||||
Dutch = 8,
|
||||
Portuguese = 9,
|
||||
Russian = 10,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue