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