mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Merge pull request #241 from linkmauve/better-loader
Improve the loader a bit
This commit is contained in:
		
						commit
						205170fa62
					
				
					 8 changed files with 364 additions and 372 deletions
				
			
		|  | @ -13,11 +13,9 @@ | |||
| 
 | ||||
| #include "3dsx.h" | ||||
| 
 | ||||
| 
 | ||||
| namespace Loader { | ||||
| 
 | ||||
| 
 | ||||
| /** 
 | ||||
| /**
 | ||||
|  * File layout: | ||||
|  * - File header | ||||
|  * - Code, rodata and data relocation table headers | ||||
|  | @ -46,7 +44,6 @@ enum THREEDSX_Error { | |||
| static const u32 RELOCBUFSIZE = 512; | ||||
| 
 | ||||
| // File header
 | ||||
| static const u32 THREEDSX_MAGIC = 0x58534433; // '3DSX'
 | ||||
| #pragma pack(1) | ||||
| struct THREEDSX_Header | ||||
| { | ||||
|  | @ -64,9 +61,9 @@ struct THREEDSX_Header | |||
| struct THREEDSX_RelocHdr | ||||
| { | ||||
|     // # of absolute relocations (that is, fix address to post-relocation memory layout)
 | ||||
|     u32 cross_segment_absolute;  | ||||
|     u32 cross_segment_absolute; | ||||
|     // # of cross-segment relative relocations (that is, 32bit signed offsets that need to be patched)
 | ||||
|     u32 cross_segment_relative;  | ||||
|     u32 cross_segment_relative; | ||||
|     // more?
 | ||||
| 
 | ||||
|     // Relocations are written in this order:
 | ||||
|  | @ -88,12 +85,7 @@ struct THREEloadinfo | |||
|     u32 seg_sizes[3]; | ||||
| }; | ||||
| 
 | ||||
| class THREEDSXReader { | ||||
| public: | ||||
|      static int Load3DSXFile(const std::string& filename, u32 base_addr); | ||||
| }; | ||||
| 
 | ||||
| static u32 TranslateAddr(u32 addr, THREEloadinfo *loadinfo, u32* offsets) | ||||
| static u32 TranslateAddr(u32 addr, const THREEloadinfo *loadinfo, u32* offsets) | ||||
| { | ||||
|     if (addr < offsets[0]) | ||||
|         return loadinfo->seg_addrs[0] + addr; | ||||
|  | @ -102,12 +94,14 @@ static u32 TranslateAddr(u32 addr, THREEloadinfo *loadinfo, u32* offsets) | |||
|     return loadinfo->seg_addrs[2] + addr - offsets[1]; | ||||
| } | ||||
| 
 | ||||
| int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) | ||||
| static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) | ||||
| { | ||||
|     FileUtil::IOFile file(filename, "rb"); | ||||
|     if (!file.IsOpen()) { | ||||
|     if (!file.IsOpen()) | ||||
|         return ERROR_FILE; | ||||
|     } | ||||
| 
 | ||||
|     // Reset read pointer in case this file has been read before.
 | ||||
|     file.Seek(0, SEEK_SET); | ||||
| 
 | ||||
|     THREEDSX_Header hdr; | ||||
|     if (file.ReadBytes(&hdr, sizeof(hdr)) != sizeof(hdr)) | ||||
|         return ERROR_READ; | ||||
|  | @ -136,8 +130,9 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) | |||
|     // Read the relocation headers
 | ||||
|     u32* relocs = (u32*)(loadinfo.seg_ptrs[2] + hdr.data_seg_size); | ||||
| 
 | ||||
|     for (u32 current_segment = 0; current_segment < 3; current_segment++) { | ||||
|         if (file.ReadBytes(&relocs[current_segment*n_reloc_tables], n_reloc_tables * 4) != n_reloc_tables * 4) | ||||
|     for (unsigned current_segment : {0, 1, 2}) { | ||||
|         size_t size = n_reloc_tables * 4; | ||||
|         if (file.ReadBytes(&relocs[current_segment * n_reloc_tables], size) != size) | ||||
|             return ERROR_READ; | ||||
|     } | ||||
| 
 | ||||
|  | @ -153,9 +148,9 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) | |||
|     memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size); | ||||
| 
 | ||||
|     // Relocate the segments
 | ||||
|     for (u32 current_segment = 0; current_segment < 3; current_segment++) { | ||||
|         for (u32 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]; | ||||
|     for (unsigned current_segment : {0, 1, 2}) { | ||||
|         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); | ||||
|  | @ -164,29 +159,35 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) | |||
|             static THREEDSX_Reloc reloc_table[RELOCBUFSIZE]; | ||||
| 
 | ||||
|             u32* pos = (u32*)loadinfo.seg_ptrs[current_segment]; | ||||
|             u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4); | ||||
|             const u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4); | ||||
| 
 | ||||
|             while (n_relocs) { | ||||
|                 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 (u32 current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) { | ||||
|                     LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)\n", | ||||
|                         current_segment_reloc_table, (u32)reloc_table[current_inprogress].skip, (u32)reloc_table[current_inprogress].patch); | ||||
|                     pos += reloc_table[current_inprogress].skip; | ||||
|                     s32 num_patches = reloc_table[current_inprogress].patch; | ||||
|                 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)\n", current_segment_reloc_table, | ||||
|                               (u32)table.skip, (u32)table.patch); | ||||
|                     pos += table.skip; | ||||
|                     s32 num_patches = table.patch; | ||||
|                     while (0 < num_patches && pos < end_pos) { | ||||
|                         u32 in_addr = (char*)pos - (char*)&all_mem[0]; | ||||
|                         u32 addr = TranslateAddr(*pos, &loadinfo, offsets); | ||||
|                         LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n", | ||||
|                             base_addr + in_addr, addr, current_segment_reloc_table, *pos); | ||||
|                                   base_addr + in_addr, addr, current_segment_reloc_table, *pos); | ||||
|                         switch (current_segment_reloc_table) { | ||||
|                         case 0: *pos = (addr); break; | ||||
|                         case 1: *pos = (addr - in_addr); break; | ||||
|                         default: break; //this should never happen
 | ||||
|                         case 0: | ||||
|                             *pos = (addr); | ||||
|                             break; | ||||
|                         case 1: | ||||
|                             *pos = (addr - in_addr); | ||||
|                             break; | ||||
|                         default: | ||||
|                             break; //this should never happen
 | ||||
|                         } | ||||
|                         pos++; | ||||
|                         num_patches--; | ||||
|  | @ -207,28 +208,30 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) | |||
|     return ERROR_NONE; | ||||
| } | ||||
| 
 | ||||
|     /// AppLoader_DSX constructor
 | ||||
|     AppLoader_THREEDSX::AppLoader_THREEDSX(const std::string& filename) : filename(filename) { | ||||
|     } | ||||
| FileType AppLoader_THREEDSX::IdentifyType(FileUtil::IOFile& file) { | ||||
|     u32 magic; | ||||
|     file.Seek(0, SEEK_SET); | ||||
|     if (1 != file.ReadArray<u32>(&magic, 1)) | ||||
|         return FileType::Error; | ||||
| 
 | ||||
|     /// AppLoader_DSX destructor
 | ||||
|     AppLoader_THREEDSX::~AppLoader_THREEDSX() { | ||||
|     } | ||||
|     if (MakeMagic('3', 'D', 'S', 'X') == magic) | ||||
|         return FileType::THREEDSX; | ||||
| 
 | ||||
|     /**
 | ||||
|     * Loads a 3DSX file | ||||
|     * @return Success on success, otherwise Error | ||||
|     */ | ||||
|     ResultStatus AppLoader_THREEDSX::Load() { | ||||
|         LOG_INFO(Loader, "Loading 3DSX file %s...", filename.c_str()); | ||||
|         FileUtil::IOFile file(filename, "rb"); | ||||
|         if (file.IsOpen()) { | ||||
|             THREEDSXReader::Load3DSXFile(filename, 0x00100000); | ||||
|             Kernel::LoadExec(0x00100000); | ||||
|         } else { | ||||
|             return ResultStatus::Error; | ||||
|         } | ||||
|         return ResultStatus::Success; | ||||
|     } | ||||
|     return FileType::Error; | ||||
| } | ||||
| 
 | ||||
| ResultStatus AppLoader_THREEDSX::Load() { | ||||
|     if (is_loaded) | ||||
|         return ResultStatus::ErrorAlreadyLoaded; | ||||
| 
 | ||||
|     if (!file->IsOpen()) | ||||
|         return ResultStatus::Error; | ||||
| 
 | ||||
|     Load3DSXFile(*file, 0x00100000); | ||||
|     Kernel::LoadExec(0x00100000); | ||||
| 
 | ||||
|     is_loaded = true; | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
| 
 | ||||
| } // namespace Loader
 | ||||
|  |  | |||
|  | @ -15,18 +15,20 @@ namespace Loader { | |||
| /// Loads an 3DSX file
 | ||||
| class AppLoader_THREEDSX final : public AppLoader { | ||||
| public: | ||||
|     AppLoader_THREEDSX(const std::string& filename); | ||||
|     ~AppLoader_THREEDSX() override; | ||||
|     AppLoader_THREEDSX(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Returns the type of the file | ||||
|      * @param file FileUtil::IOFile open file | ||||
|      * @return FileType found, or FileType::Error if this loader doesn't know it | ||||
|      */ | ||||
|     static FileType IdentifyType(FileUtil::IOFile& file); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Load the bootable file | ||||
|      * @return ResultStatus result of function | ||||
|      */ | ||||
|     ResultStatus Load() override; | ||||
| 
 | ||||
| private: | ||||
|     std::string filename; | ||||
|     bool        is_loaded; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Loader
 | ||||
|  |  | |||
|  | @ -18,25 +18,25 @@ | |||
| 
 | ||||
| // 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
 | ||||
|  | @ -54,12 +54,6 @@ enum ElfMachine { | |||
| #define EI_PAD     7 | ||||
| #define EI_NIDENT 16 | ||||
| 
 | ||||
| // Magic number
 | ||||
| #define ELFMAG0 0x7F | ||||
| #define ELFMAG1  'E' | ||||
| #define ELFMAG2  'L' | ||||
| #define ELFMAG3  'F' | ||||
| 
 | ||||
| // Sections constants
 | ||||
| 
 | ||||
| // Section types
 | ||||
|  | @ -83,10 +77,10 @@ enum ElfMachine { | |||
| // Section flags
 | ||||
| enum ElfSectionFlags | ||||
| { | ||||
|     SHF_WRITE = 0x1, | ||||
|     SHF_ALLOC = 0x2, | ||||
|     SHF_WRITE     = 0x1, | ||||
|     SHF_ALLOC     = 0x2, | ||||
|     SHF_EXECINSTR = 0x4, | ||||
|     SHF_MASKPROC = 0xF0000000, | ||||
|     SHF_MASKPROC  = 0xF0000000, | ||||
| }; | ||||
| 
 | ||||
| // Segment types
 | ||||
|  | @ -100,11 +94,11 @@ enum ElfSectionFlags | |||
| #define PT_LOPROC  0x70000000 | ||||
| #define PT_HIPROC  0x7FFFFFFF | ||||
| 
 | ||||
| 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
 | ||||
|  | @ -188,7 +182,6 @@ private: | |||
| 
 | ||||
| public: | ||||
|     ElfReader(void *ptr); | ||||
|     ~ElfReader() { } | ||||
| 
 | ||||
|     u32 Read32(int off) const { return base32[off >> 2]; } | ||||
| 
 | ||||
|  | @ -197,7 +190,7 @@ public: | |||
|     ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } | ||||
|     u32 GetEntryPoint() const { return entryPoint; } | ||||
|     u32 GetFlags() const { return (u32)(header->e_flags); } | ||||
|     bool LoadInto(u32 vaddr); | ||||
|     void LoadInto(u32 vaddr); | ||||
|     bool LoadSymbols(); | ||||
| 
 | ||||
|     int GetNumSegments() const { return (int)(header->e_phnum); } | ||||
|  | @ -229,11 +222,11 @@ public: | |||
| 
 | ||||
| ElfReader::ElfReader(void *ptr) { | ||||
|     base = (char*)ptr; | ||||
|     base32 = (u32 *)ptr; | ||||
|     base32 = (u32*)ptr; | ||||
|     header = (Elf32_Ehdr*)ptr; | ||||
| 
 | ||||
|     segments = (Elf32_Phdr *)(base + header->e_phoff); | ||||
|     sections = (Elf32_Shdr *)(base + header->e_shoff); | ||||
|     segments = (Elf32_Phdr*)(base + header->e_phoff); | ||||
|     sections = (Elf32_Shdr*)(base + header->e_shoff); | ||||
| 
 | ||||
|     entryPoint = header->e_entry; | ||||
| 
 | ||||
|  | @ -245,7 +238,7 @@ const char *ElfReader::GetSectionName(int section) const { | |||
|         return nullptr; | ||||
| 
 | ||||
|     int name_offset = sections[section].sh_name; | ||||
|     char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); | ||||
|     const char* ptr = (char*)GetSectionDataPtr(header->e_shstrndx); | ||||
| 
 | ||||
|     if (ptr) | ||||
|         return ptr + name_offset; | ||||
|  | @ -253,7 +246,7 @@ const char *ElfReader::GetSectionName(int section) const { | |||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| bool ElfReader::LoadInto(u32 vaddr) { | ||||
| void ElfReader::LoadInto(u32 vaddr) { | ||||
|     LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx); | ||||
| 
 | ||||
|     // Should we relocate?
 | ||||
|  | @ -271,20 +264,19 @@ bool ElfReader::LoadInto(u32 vaddr) { | |||
|     u32 segment_addr[32]; | ||||
|     u32 base_addr = relocate ? vaddr : 0; | ||||
| 
 | ||||
|     for (int i = 0; i < header->e_phnum; i++) { | ||||
|         Elf32_Phdr *p = segments + i; | ||||
|     for (unsigned i = 0; i < header->e_phnum; i++) { | ||||
|         Elf32_Phdr* p = segments + i; | ||||
|         LOG_DEBUG(Loader, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, | ||||
|             p->p_filesz, p->p_memsz); | ||||
|                   p->p_filesz, p->p_memsz); | ||||
| 
 | ||||
|         if (p->p_type == PT_LOAD) { | ||||
|             segment_addr[i] = base_addr + p->p_vaddr; | ||||
|             memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz); | ||||
|             LOG_DEBUG(Loader, "Loadable Segment Copied to %08x, size %08x", segment_addr[i], | ||||
|                 p->p_memsz); | ||||
|                       p->p_memsz); | ||||
|         } | ||||
|     } | ||||
|     LOG_DEBUG(Loader, "Done loading."); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const { | ||||
|  | @ -305,9 +297,9 @@ bool ElfReader::LoadSymbols() { | |||
|         const char *stringBase = (const char *)GetSectionDataPtr(stringSection); | ||||
| 
 | ||||
|         //We have a symbol table!
 | ||||
|         Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); | ||||
|         Elf32_Sym* symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); | ||||
|         int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); | ||||
|         for (int sym = 0; sym < numSymbols; sym++) { | ||||
|         for (unsigned sym = 0; sym < numSymbols; sym++) { | ||||
|             int size = symtab[sym].st_size; | ||||
|             if (size == 0) | ||||
|                 continue; | ||||
|  | @ -330,40 +322,38 @@ bool ElfReader::LoadSymbols() { | |||
| 
 | ||||
| namespace Loader { | ||||
| 
 | ||||
| /// AppLoader_ELF constructor
 | ||||
| AppLoader_ELF::AppLoader_ELF(const std::string& filename) : is_loaded(false) { | ||||
|     this->filename = filename; | ||||
| FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) { | ||||
|     u32 magic; | ||||
|     file.Seek(0, SEEK_SET); | ||||
|     if (1 != file.ReadArray<u32>(&magic, 1)) | ||||
|         return FileType::Error; | ||||
| 
 | ||||
|     if (MakeMagic('\x7f', 'E', 'L', 'F') == magic) | ||||
|         return FileType::ELF; | ||||
| 
 | ||||
|     return FileType::Error; | ||||
| } | ||||
| 
 | ||||
| /// AppLoader_NCCH destructor
 | ||||
| AppLoader_ELF::~AppLoader_ELF() { | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) | ||||
|  * @param error_string Pointer to string to put error message if an error has occurred | ||||
|  * @todo Move NCSD parsing out of here and create a separate function for loading these | ||||
|  * @return True on success, otherwise false | ||||
|  */ | ||||
| ResultStatus AppLoader_ELF::Load() { | ||||
|     LOG_INFO(Loader, "Loading ELF file %s...", filename.c_str()); | ||||
| 
 | ||||
|     if (is_loaded) | ||||
|         return ResultStatus::ErrorAlreadyLoaded; | ||||
| 
 | ||||
|     FileUtil::IOFile file(filename, "rb"); | ||||
| 
 | ||||
|     if (file.IsOpen()) { | ||||
|         u32 size = (u32)file.GetSize(); | ||||
|         std::unique_ptr<u8[]> buffer(new u8[size]); | ||||
|         file.ReadBytes(&buffer[0], size); | ||||
| 
 | ||||
|         ElfReader elf_reader(&buffer[0]); | ||||
|         elf_reader.LoadInto(0x00100000); | ||||
|         Kernel::LoadExec(elf_reader.GetEntryPoint()); | ||||
|     } else { | ||||
|     if (!file->IsOpen()) | ||||
|         return ResultStatus::Error; | ||||
|     } | ||||
| 
 | ||||
|     // Reset read pointer in case this file has been read before.
 | ||||
|     file->Seek(0, SEEK_SET); | ||||
| 
 | ||||
|     u32 size = static_cast<u32>(file->GetSize()); | ||||
|     std::unique_ptr<u8[]> buffer(new u8[size]); | ||||
|     if (file->ReadBytes(&buffer[0], size) != size) | ||||
|         return ResultStatus::Error; | ||||
| 
 | ||||
|     ElfReader elf_reader(&buffer[0]); | ||||
|     elf_reader.LoadInto(0x00100000); | ||||
|     Kernel::LoadExec(elf_reader.GetEntryPoint()); | ||||
| 
 | ||||
|     is_loaded = true; | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,18 +15,20 @@ namespace Loader { | |||
| /// Loads an ELF/AXF file
 | ||||
| class AppLoader_ELF final : public AppLoader { | ||||
| public: | ||||
|     AppLoader_ELF(const std::string& filename); | ||||
|     ~AppLoader_ELF() override; | ||||
|     AppLoader_ELF(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Returns the type of the file | ||||
|      * @param file FileUtil::IOFile open file | ||||
|      * @return FileType found, or FileType::Error if this loader doesn't know it | ||||
|      */ | ||||
|     static FileType IdentifyType(FileUtil::IOFile& file); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Load the bootable file | ||||
|      * @return ResultStatus result of function | ||||
|      */ | ||||
|     ResultStatus Load() override; | ||||
| 
 | ||||
| private: | ||||
|     std::string filename; | ||||
|     bool        is_loaded; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Loader
 | ||||
|  |  | |||
|  | @ -19,11 +19,32 @@ namespace Loader { | |||
| 
 | ||||
| /**
 | ||||
|  * Identifies the type of a bootable file | ||||
|  * @param filename String filename of bootable file | ||||
|  * @todo (ShizZy) this function sucks... make it actually check file contents etc. | ||||
|  * @param file open file | ||||
|  * @return FileType of file | ||||
|  */ | ||||
| FileType IdentifyFile(const std::string &filename) { | ||||
| static FileType IdentifyFile(FileUtil::IOFile& file) { | ||||
|     FileType type; | ||||
| 
 | ||||
| #define CHECK_TYPE(loader) \ | ||||
|     type = AppLoader_##loader::IdentifyType(file); \ | ||||
|     if (FileType::Error != type) \ | ||||
|         return type; | ||||
| 
 | ||||
|     CHECK_TYPE(THREEDSX) | ||||
|     CHECK_TYPE(ELF) | ||||
|     CHECK_TYPE(NCCH) | ||||
| 
 | ||||
| #undef CHECK_TYPE | ||||
| 
 | ||||
|     return FileType::Unknown; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Guess the type of a bootable file from its extension | ||||
|  * @param filename String filename of bootable file | ||||
|  * @return FileType of file | ||||
|  */ | ||||
| static FileType GuessFromFilename(const std::string& filename) { | ||||
|     if (filename.size() == 0) { | ||||
|         LOG_ERROR(Loader, "invalid filename %s", filename.c_str()); | ||||
|         return FileType::Error; | ||||
|  | @ -34,47 +55,74 @@ FileType IdentifyFile(const std::string &filename) { | |||
|         return FileType::Unknown; | ||||
|     std::string extension = Common::ToLower(filename.substr(extension_loc)); | ||||
| 
 | ||||
|     // TODO(bunnei): Do actual filetype checking instead of naively checking the extension
 | ||||
|     if (extension == ".elf") { | ||||
|     if (extension == ".elf") | ||||
|         return FileType::ELF; | ||||
|     } else if (extension == ".axf") { | ||||
|     else if (extension == ".axf") | ||||
|         return FileType::ELF; | ||||
|     } else if (extension == ".cxi") { | ||||
|     else if (extension == ".cxi") | ||||
|         return FileType::CXI; | ||||
|     } else if (extension == ".cci") { | ||||
|     else if (extension == ".cci") | ||||
|         return FileType::CCI; | ||||
|     } else if (extension == ".bin") { | ||||
|     else if (extension == ".bin") | ||||
|         return FileType::BIN; | ||||
|     } else if (extension == ".3ds") { | ||||
|     else if (extension == ".3ds") | ||||
|         return FileType::CCI; | ||||
|     } else if (extension == ".3dsx") { | ||||
|     else if (extension == ".3dsx") | ||||
|         return FileType::THREEDSX; | ||||
|     } | ||||
|     return FileType::Unknown; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Identifies and loads a bootable file | ||||
|  * @param filename String filename of bootable file | ||||
|  * @return ResultStatus result of function | ||||
|  */ | ||||
| ResultStatus LoadFile(const std::string& filename) { | ||||
|     LOG_INFO(Loader, "Loading file %s...", filename.c_str()); | ||||
| static const char* GetFileTypeString(FileType type) { | ||||
|     switch (type) { | ||||
|     case FileType::CCI: | ||||
|         return "NCSD"; | ||||
|     case FileType::CXI: | ||||
|         return "NCCH"; | ||||
|     case FileType::ELF: | ||||
|         return "ELF"; | ||||
|     case FileType::THREEDSX: | ||||
|         return "3DSX"; | ||||
|     case FileType::BIN: | ||||
|         return "raw"; | ||||
|     case FileType::Error: | ||||
|     case FileType::Unknown: | ||||
|         return "unknown"; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     switch (IdentifyFile(filename)) { | ||||
| ResultStatus LoadFile(const std::string& filename) { | ||||
|     std::unique_ptr<FileUtil::IOFile> file(new FileUtil::IOFile(filename, "rb")); | ||||
|     if (!file->IsOpen()) { | ||||
|         LOG_ERROR(Loader, "Failed to load file %s", filename.c_str()); | ||||
|         return ResultStatus::Error; | ||||
|     } | ||||
| 
 | ||||
|     FileType type = IdentifyFile(*file); | ||||
|     FileType filename_type = GuessFromFilename(filename); | ||||
| 
 | ||||
|     if (type != filename_type) { | ||||
|         LOG_WARNING(Loader, "File %s has a different type than its extension.", filename.c_str()); | ||||
|         if (FileType::Unknown == type) | ||||
|             type = filename_type; | ||||
|     } | ||||
| 
 | ||||
|     LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type)); | ||||
| 
 | ||||
|     switch (type) { | ||||
| 
 | ||||
|     //3DSX file format...
 | ||||
|     case FileType::THREEDSX: | ||||
|         return AppLoader_THREEDSX(filename).Load(); | ||||
|         return AppLoader_THREEDSX(std::move(file)).Load(); | ||||
| 
 | ||||
|     // Standard ELF file format...
 | ||||
|     case FileType::ELF: | ||||
|         return AppLoader_ELF(filename).Load(); | ||||
|         return AppLoader_ELF(std::move(file)).Load(); | ||||
| 
 | ||||
|     // NCCH/NCSD container formats...
 | ||||
|     case FileType::CXI: | ||||
|     case FileType::CCI: { | ||||
|         AppLoader_NCCH app_loader(filename); | ||||
|     case FileType::CCI: | ||||
|     { | ||||
|         AppLoader_NCCH app_loader(std::move(file)); | ||||
| 
 | ||||
|         // Load application and RomFS
 | ||||
|         if (ResultStatus::Success == app_loader.Load()) { | ||||
|  | @ -88,16 +136,11 @@ ResultStatus LoadFile(const std::string& filename) { | |||
|     // Raw BIN file format...
 | ||||
|     case FileType::BIN: | ||||
|     { | ||||
|         LOG_INFO(Loader, "Loading BIN file %s...", filename.c_str()); | ||||
| 
 | ||||
|         FileUtil::IOFile file(filename, "rb"); | ||||
| 
 | ||||
|         if (file.IsOpen()) { | ||||
|             file.ReadBytes(Memory::GetPointer(Memory::EXEFS_CODE_VADDR), (size_t)file.GetSize()); | ||||
|             Kernel::LoadExec(Memory::EXEFS_CODE_VADDR); | ||||
|         } else { | ||||
|         size_t size = (size_t)file->GetSize(); | ||||
|         if (file->ReadBytes(Memory::GetPointer(Memory::EXEFS_CODE_VADDR), size) != size) | ||||
|             return ResultStatus::Error; | ||||
|         } | ||||
| 
 | ||||
|         Kernel::LoadExec(Memory::EXEFS_CODE_VADDR); | ||||
|         return ResultStatus::Success; | ||||
|     } | ||||
| 
 | ||||
|  | @ -106,10 +149,11 @@ ResultStatus LoadFile(const std::string& filename) { | |||
| 
 | ||||
|     // IdentifyFile could know identify file type...
 | ||||
|     case FileType::Unknown: | ||||
| 
 | ||||
|     default: | ||||
|     { | ||||
|         LOG_CRITICAL(Loader, "File %s is of unknown type."); | ||||
|         return ResultStatus::ErrorInvalidFormat; | ||||
|     } | ||||
|     } | ||||
|     return ResultStatus::Error; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include <vector> | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "common/file_util.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Loader namespace
 | ||||
|  | @ -37,10 +38,14 @@ enum class ResultStatus { | |||
|     ErrorMemoryAllocationFailed, | ||||
| }; | ||||
| 
 | ||||
| static u32 MakeMagic(char a, char b, char c, char d) { | ||||
|     return a | b << 8 | c << 16 | d << 24; | ||||
| } | ||||
| 
 | ||||
| /// Interface for loading an application
 | ||||
| class AppLoader : NonCopyable { | ||||
| public: | ||||
|     AppLoader() { } | ||||
|     AppLoader(std::unique_ptr<FileUtil::IOFile>&& file) : file(std::move(file)) { } | ||||
|     virtual ~AppLoader() { } | ||||
| 
 | ||||
|     /**
 | ||||
|  | @ -93,14 +98,11 @@ public: | |||
|     virtual ResultStatus ReadRomFS(std::vector<u8>& buffer) const { | ||||
|         return ResultStatus::ErrorNotImplemented; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Identifies the type of a bootable file | ||||
|  * @param filename String filename of bootable file | ||||
|  * @return FileType of file | ||||
|  */ | ||||
| FileType IdentifyFile(const std::string &filename); | ||||
| protected: | ||||
|     std::unique_ptr<FileUtil::IOFile> file; | ||||
|     bool                              is_loaded = false; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Identifies and loads a bootable file | ||||
|  |  | |||
|  | @ -4,8 +4,6 @@ | |||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "common/file_util.h" | ||||
| 
 | ||||
| #include "core/loader/ncch.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/mem_map.h" | ||||
|  | @ -15,8 +13,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 | ||||
|  | @ -24,7 +22,7 @@ static const int kBlockSize     = 0x200;    ///< Size of ExeFS blocks (in bytes) | |||
|  * @param size Size of compressed buffer | ||||
|  * @return Size of decompressed buffer | ||||
|  */ | ||||
| static u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { | ||||
| static u32 LZSS_GetDecompressedSize(const u8* buffer, u32 size) { | ||||
|     u32 offset_size = *(u32*)(buffer + size - 4); | ||||
|     return offset_size + size; | ||||
| } | ||||
|  | @ -37,9 +35,9 @@ static u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { | |||
|  * @param decompressed_size Size of decompressed buffer | ||||
|  * @return True on success, otherwise false | ||||
|  */ | ||||
| static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) { | ||||
|     u8* footer = compressed + compressed_size - 8; | ||||
|     u32 buffer_top_and_bottom = *(u32*)footer; | ||||
| 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; | ||||
|     u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF); | ||||
|     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 | |||
|     memset(decompressed, 0, decompressed_size); | ||||
|     memcpy(decompressed, compressed, compressed_size); | ||||
| 
 | ||||
|     while(index > stop_index) { | ||||
|     while (index > stop_index) { | ||||
|        u8 control = compressed[--index]; | ||||
| 
 | ||||
|         for(u32 i = 0; i < 8; i++) { | ||||
|             if(index <= stop_index) | ||||
|         for (unsigned i = 0; i < 8; i++) { | ||||
|             if (index <= stop_index) | ||||
|                 break; | ||||
|             if(index <= 0) | ||||
|             if (index <= 0) | ||||
|                 break; | ||||
|             if(out <= 0) | ||||
|             if (out <= 0) | ||||
|                 break; | ||||
| 
 | ||||
|             if(control & 0x80) { | ||||
|             if (control & 0x80) { | ||||
|                 // Check if compression is out of bounds
 | ||||
|                 if(index < 2) { | ||||
|                 if (index < 2) | ||||
|                     return false; | ||||
|                 } | ||||
|                 index -= 2; | ||||
| 
 | ||||
|                 u32 segment_offset = compressed[index] | (compressed[index + 1] << 8); | ||||
|  | @ -71,23 +68,21 @@ static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompresse | |||
|                 segment_offset += 2; | ||||
| 
 | ||||
|                 // Check if compression is out of bounds
 | ||||
|                 if(out < segment_size) { | ||||
|                 if (out < segment_size) | ||||
|                     return false; | ||||
|                 } | ||||
|                 for(u32 j = 0; j < segment_size; j++) { | ||||
|                     // Check if compression is out of bounds
 | ||||
|                     if(out + segment_offset >= decompressed_size) { | ||||
|                         return false; | ||||
|                     } | ||||
| 
 | ||||
|                     u8 data  = decompressed[out + segment_offset]; | ||||
|                 for (unsigned j = 0; j < segment_size; j++) { | ||||
|                     // Check if compression is out of bounds
 | ||||
|                     if (out + segment_offset >= decompressed_size) | ||||
|                         return false; | ||||
| 
 | ||||
|                     u8 data = decompressed[out + segment_offset]; | ||||
|                     decompressed[--out] = data; | ||||
|                 } | ||||
|             } else { | ||||
|                 // Check if compression is out of bounds
 | ||||
|                 if(out < 1) { | ||||
|                 if (out < 1) | ||||
|                     return false; | ||||
|                 } | ||||
|                 decompressed[--out] = compressed[--index]; | ||||
|             } | ||||
|             control <<= 1; | ||||
|  | @ -99,24 +94,21 @@ static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompresse | |||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // AppLoader_NCCH class
 | ||||
| 
 | ||||
| /// AppLoader_NCCH constructor
 | ||||
| AppLoader_NCCH::AppLoader_NCCH(const std::string& filename) { | ||||
|     this->filename = filename; | ||||
|     is_loaded = false; | ||||
|     is_compressed = false; | ||||
|     entry_point = 0; | ||||
|     ncch_offset = 0; | ||||
|     exefs_offset = 0; | ||||
| FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { | ||||
|     u32 magic; | ||||
|     file.Seek(0x100, SEEK_SET); | ||||
|     if (1 != file.ReadArray<u32>(&magic, 1)) | ||||
|         return FileType::Error; | ||||
| 
 | ||||
|     if (MakeMagic('N', 'C', 'S', 'D') == magic) | ||||
|         return FileType::CCI; | ||||
| 
 | ||||
|     if (MakeMagic('N', 'C', 'C', 'H') == magic) | ||||
|         return FileType::CXI; | ||||
| 
 | ||||
|     return FileType::Error; | ||||
| } | ||||
| 
 | ||||
| /// AppLoader_NCCH destructor
 | ||||
| AppLoader_NCCH::~AppLoader_NCCH() { | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads .code section into memory for booting | ||||
|  * @return ResultStatus result of function | ||||
|  */ | ||||
| ResultStatus AppLoader_NCCH::LoadExec() const { | ||||
|     if (!is_loaded) | ||||
|         return ResultStatus::ErrorNotLoaded; | ||||
|  | @ -130,189 +122,144 @@ ResultStatus AppLoader_NCCH::LoadExec() const { | |||
|     return ResultStatus::Error; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) | ||||
|  * @param name Name of section to read out of NCCH file | ||||
|  * @param buffer Vector to read data into | ||||
|  * @return ResultStatus result of function | ||||
|  */ | ||||
| ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const { | ||||
|     // Iterate through the ExeFs archive until we find the .code file...
 | ||||
|     FileUtil::IOFile file(filename, "rb"); | ||||
|     if (file.IsOpen()) { | ||||
|         LOG_DEBUG(Loader, "%d sections:", kMaxSections); | ||||
|         for (int i = 0; i < kMaxSections; i++) { | ||||
|             // Load the specified section...
 | ||||
|             if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { | ||||
|                 LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", i, | ||||
|                         exefs_header.section[i].offset, exefs_header.section[i].size, | ||||
|                         exefs_header.section[i].name); | ||||
| 
 | ||||
|                 s64 section_offset = (exefs_header.section[i].offset + exefs_offset + | ||||
|                     sizeof(ExeFs_Header)+ncch_offset); | ||||
|                 file.Seek(section_offset, 0); | ||||
| 
 | ||||
|                 // Section is compressed...
 | ||||
|                 if (i == 0 && is_compressed) { | ||||
|                     // Read compressed .code section...
 | ||||
|                     std::unique_ptr<u8[]> temp_buffer; | ||||
|                     try { | ||||
|                         temp_buffer.reset(new u8[exefs_header.section[i].size]); | ||||
|                     } catch (std::bad_alloc&) { | ||||
|                         return ResultStatus::ErrorMemoryAllocationFailed; | ||||
|                     } | ||||
|                     file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size); | ||||
| 
 | ||||
|                     // Decompress .code section...
 | ||||
|                     u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], | ||||
|                         exefs_header.section[i].size); | ||||
|                     buffer.resize(decompressed_size); | ||||
|                     if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0], | ||||
|                         decompressed_size)) { | ||||
|                         return ResultStatus::ErrorInvalidFormat; | ||||
|                     } | ||||
|                     // Section is uncompressed...
 | ||||
|                 } | ||||
|                 else { | ||||
|                     buffer.resize(exefs_header.section[i].size); | ||||
|                     file.ReadBytes(&buffer[0], exefs_header.section[i].size); | ||||
|                 } | ||||
|                 return ResultStatus::Success; | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str()); | ||||
|     if (!file->IsOpen()) | ||||
|         return ResultStatus::Error; | ||||
| 
 | ||||
|     LOG_DEBUG(Loader, "%d sections:", kMaxSections); | ||||
|     // Iterate through the ExeFs archive until we find the .code file...
 | ||||
|     for (unsigned section_number = 0; section_number < kMaxSections; section_number++) { | ||||
|         const auto& section = exefs_header.section[section_number]; | ||||
| 
 | ||||
|         // Load the specified section...
 | ||||
|         if (strcmp(section.name, name) == 0) { | ||||
|             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); | ||||
|             file->Seek(section_offset, SEEK_SET); | ||||
| 
 | ||||
|             if (is_compressed) { | ||||
|                 // Section is compressed, read compressed .code section...
 | ||||
|                 std::unique_ptr<u8[]> temp_buffer; | ||||
|                 try { | ||||
|                     temp_buffer.reset(new u8[section.size]); | ||||
|                 } catch (std::bad_alloc&) { | ||||
|                     return ResultStatus::ErrorMemoryAllocationFailed; | ||||
|                 } | ||||
| 
 | ||||
|                 if (file->ReadBytes(&temp_buffer[0], section.size) != section.size) | ||||
|                     return ResultStatus::Error; | ||||
| 
 | ||||
|                 // Decompress .code section...
 | ||||
|                 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], section.size); | ||||
|                 buffer.resize(decompressed_size); | ||||
|                 if (!LZSS_Decompress(&temp_buffer[0], section.size, &buffer[0], decompressed_size)) | ||||
|                     return ResultStatus::ErrorInvalidFormat; | ||||
|             } else { | ||||
|                 // Section is uncompressed...
 | ||||
|                 buffer.resize(section.size); | ||||
|                 if (file->ReadBytes(&buffer[0], section.size) != section.size) | ||||
|                     return ResultStatus::Error; | ||||
|             } | ||||
|             return ResultStatus::Success; | ||||
|         } | ||||
|     } | ||||
|     return ResultStatus::ErrorNotUsed; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) | ||||
|  * @param error_string Pointer to string to put error message if an error has occurred | ||||
|  * @todo Move NCSD parsing out of here and create a separate function for loading these | ||||
|  * @return True on success, otherwise false | ||||
|  */ | ||||
| ResultStatus AppLoader_NCCH::Load() { | ||||
|     LOG_INFO(Loader, "Loading NCCH file %s...", filename.c_str()); | ||||
| 
 | ||||
|     if (is_loaded) | ||||
|         return ResultStatus::ErrorAlreadyLoaded; | ||||
| 
 | ||||
|     FileUtil::IOFile file(filename, "rb"); | ||||
|     if (file.IsOpen()) { | ||||
|         file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); | ||||
|     if (!file->IsOpen()) | ||||
|         return ResultStatus::Error; | ||||
| 
 | ||||
|         // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
 | ||||
|         if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) { | ||||
|             LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!"); | ||||
|             ncch_offset = 0x4000; | ||||
|             file.Seek(ncch_offset, 0); | ||||
|             file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); | ||||
|         } | ||||
|     // Reset read pointer in case this file has been read before.
 | ||||
|     file->Seek(0, SEEK_SET); | ||||
| 
 | ||||
|         // Verify we are loading the correct file type...
 | ||||
|         if (0 != memcmp(&ncch_header.magic, "NCCH", 4)) | ||||
|             return ResultStatus::ErrorInvalidFormat; | ||||
|     if (file->ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) | ||||
|         return ResultStatus::Error; | ||||
| 
 | ||||
|         // Read ExHeader...
 | ||||
| 
 | ||||
|         file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)); | ||||
| 
 | ||||
|         is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; | ||||
|         entry_point = exheader_header.codeset_info.text.address; | ||||
| 
 | ||||
|         LOG_INFO(Loader, "Name:            %s", exheader_header.codeset_info.name); | ||||
|         LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no"); | ||||
|         LOG_DEBUG(Loader, "Entry point:     0x%08X", entry_point); | ||||
| 
 | ||||
|         // Read ExeFS...
 | ||||
| 
 | ||||
|         exefs_offset = ncch_header.exefs_offset * kBlockSize; | ||||
|         u32 exefs_size = ncch_header.exefs_size * kBlockSize; | ||||
| 
 | ||||
|         LOG_DEBUG(Loader, "ExeFS offset:    0x%08X", exefs_offset); | ||||
|         LOG_DEBUG(Loader, "ExeFS size:      0x%08X", exefs_size); | ||||
| 
 | ||||
|         file.Seek(exefs_offset + ncch_offset, 0); | ||||
|         file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); | ||||
| 
 | ||||
|         is_loaded = true; // Set state to loaded
 | ||||
| 
 | ||||
|         LoadExec(); // Load the executable into memory for booting
 | ||||
| 
 | ||||
|         return ResultStatus::Success; | ||||
|     } else { | ||||
|         LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str()); | ||||
|     // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
 | ||||
|     if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { | ||||
|         LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!"); | ||||
|         ncch_offset = 0x4000; | ||||
|         file->Seek(ncch_offset, SEEK_SET); | ||||
|         file->ReadBytes(&ncch_header, sizeof(NCCH_Header)); | ||||
|     } | ||||
|     return ResultStatus::Error; | ||||
| 
 | ||||
|     // Verify we are loading the correct file type...
 | ||||
|     if (MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) | ||||
|         return ResultStatus::ErrorInvalidFormat; | ||||
| 
 | ||||
|     // Read ExHeader...
 | ||||
| 
 | ||||
|     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; | ||||
| 
 | ||||
|     LOG_INFO(Loader, "Name:            %s", exheader_header.codeset_info.name); | ||||
|     LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no"); | ||||
|     LOG_DEBUG(Loader, "Entry point:     0x%08X", entry_point); | ||||
| 
 | ||||
|     // Read ExeFS...
 | ||||
| 
 | ||||
|     exefs_offset = ncch_header.exefs_offset * kBlockSize; | ||||
|     u32 exefs_size = ncch_header.exefs_size * kBlockSize; | ||||
| 
 | ||||
|     LOG_DEBUG(Loader, "ExeFS offset:    0x%08X", exefs_offset); | ||||
|     LOG_DEBUG(Loader, "ExeFS size:      0x%08X", exefs_size); | ||||
| 
 | ||||
|     file->Seek(exefs_offset + ncch_offset, SEEK_SET); | ||||
|     if (file->ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) | ||||
|         return ResultStatus::Error; | ||||
| 
 | ||||
|     is_loaded = true; // Set state to loaded
 | ||||
| 
 | ||||
|     return LoadExec(); // Load the executable into memory for booting
 | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get the code (typically .code section) of the application | ||||
|  * @param buffer Reference to buffer to store data | ||||
|  * @return ResultStatus result of function | ||||
|  */ | ||||
| ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) const { | ||||
|     return LoadSectionExeFS(".code", buffer); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get the icon (typically icon section) of the application | ||||
|  * @param buffer Reference to buffer to store data | ||||
|  * @return ResultStatus result of function | ||||
|  */ | ||||
| ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) const { | ||||
|     return LoadSectionExeFS("icon", buffer); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get the banner (typically banner section) of the application | ||||
|  * @param buffer Reference to buffer to store data | ||||
|  * @return ResultStatus result of function | ||||
|  */ | ||||
| ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) const { | ||||
|     return LoadSectionExeFS("banner", buffer); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get the logo (typically logo section) of the application | ||||
|  * @param buffer Reference to buffer to store data | ||||
|  * @return ResultStatus result of function | ||||
|  */ | ||||
| ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) const { | ||||
|     return LoadSectionExeFS("logo", buffer); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get the RomFS of the application | ||||
|  * @param buffer Reference to buffer to store data | ||||
|  * @return ResultStatus result of function | ||||
|  */ | ||||
| ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { | ||||
|     FileUtil::IOFile file(filename, "rb"); | ||||
|     if (file.IsOpen()) { | ||||
|         // Check if the NCCH has a RomFS...
 | ||||
|         if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) { | ||||
|             u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; | ||||
|             u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; | ||||
|     if (!file->IsOpen()) | ||||
|         return ResultStatus::Error; | ||||
| 
 | ||||
|             LOG_DEBUG(Loader, "RomFS offset:    0x%08X", romfs_offset); | ||||
|             LOG_DEBUG(Loader, "RomFS size:      0x%08X", romfs_size); | ||||
|     // Check if the NCCH has a RomFS...
 | ||||
|     if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) { | ||||
|         u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; | ||||
|         u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; | ||||
| 
 | ||||
|             buffer.resize(romfs_size); | ||||
|         LOG_DEBUG(Loader, "RomFS offset:    0x%08X", romfs_offset); | ||||
|         LOG_DEBUG(Loader, "RomFS size:      0x%08X", romfs_size); | ||||
| 
 | ||||
|             file.Seek(romfs_offset, 0); | ||||
|             file.ReadBytes(&buffer[0], romfs_size); | ||||
|         buffer.resize(romfs_size); | ||||
| 
 | ||||
|             return ResultStatus::Success; | ||||
|         } | ||||
|         LOG_DEBUG(Loader, "NCCH has no RomFS"); | ||||
|         return ResultStatus::ErrorNotUsed; | ||||
|     } else { | ||||
|         LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str()); | ||||
|         file->Seek(romfs_offset, SEEK_SET); | ||||
|         if (file->ReadBytes(&buffer[0], romfs_size) != romfs_size) | ||||
|             return ResultStatus::Error; | ||||
| 
 | ||||
|         return ResultStatus::Success; | ||||
|     } | ||||
|     return ResultStatus::Error; | ||||
|     LOG_DEBUG(Loader, "NCCH has no RomFS"); | ||||
|     return ResultStatus::ErrorNotUsed; | ||||
| } | ||||
| 
 | ||||
| u64 AppLoader_NCCH::GetProgramId() const { | ||||
|  |  | |||
|  | @ -5,7 +5,6 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "common/file_util.h" | ||||
| 
 | ||||
| #include "core/loader/loader.h" | ||||
| 
 | ||||
|  | @ -14,7 +13,7 @@ | |||
| 
 | ||||
| struct NCCH_Header { | ||||
|     u8 signature[0x100]; | ||||
|     char magic[4]; | ||||
|     u32 magic; | ||||
|     u32 content_size; | ||||
|     u8 partition_id[8]; | ||||
|     u16 maker_code; | ||||
|  | @ -147,8 +146,14 @@ namespace Loader { | |||
| /// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
 | ||||
| class AppLoader_NCCH final : public AppLoader { | ||||
| public: | ||||
|     AppLoader_NCCH(const std::string& filename); | ||||
|     ~AppLoader_NCCH() override; | ||||
|     AppLoader_NCCH(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Returns the type of the file | ||||
|      * @param file FileUtil::IOFile open file | ||||
|      * @return FileType found, or FileType::Error if this loader doesn't know it | ||||
|      */ | ||||
|     static FileType IdentifyType(FileUtil::IOFile& file); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Load the application | ||||
|  | @ -213,14 +218,11 @@ private: | |||
|      */ | ||||
|     ResultStatus LoadExec() const; | ||||
| 
 | ||||
|     std::string     filename; | ||||
|     bool            is_compressed = false; | ||||
| 
 | ||||
|     bool            is_loaded; | ||||
|     bool            is_compressed; | ||||
| 
 | ||||
|     u32             entry_point; | ||||
|     u32             ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header
 | ||||
|     u32             exefs_offset; | ||||
|     u32             entry_point = 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; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue