mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +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,10 +13,8 @@ | ||||||
| 
 | 
 | ||||||
| #include "3dsx.h" | #include "3dsx.h" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| namespace Loader { | namespace Loader { | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * File layout: |  * File layout: | ||||||
|  * - File header |  * - File header | ||||||
|  | @ -46,7 +44,6 @@ enum THREEDSX_Error { | ||||||
| static const u32 RELOCBUFSIZE = 512; | static const u32 RELOCBUFSIZE = 512; | ||||||
| 
 | 
 | ||||||
| // File header
 | // File header
 | ||||||
| static const u32 THREEDSX_MAGIC = 0x58534433; // '3DSX'
 |  | ||||||
| #pragma pack(1) | #pragma pack(1) | ||||||
| struct THREEDSX_Header | struct THREEDSX_Header | ||||||
| { | { | ||||||
|  | @ -88,12 +85,7 @@ struct THREEloadinfo | ||||||
|     u32 seg_sizes[3]; |     u32 seg_sizes[3]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class THREEDSXReader { | static u32 TranslateAddr(u32 addr, const THREEloadinfo *loadinfo, u32* offsets) | ||||||
| public: |  | ||||||
|      static int Load3DSXFile(const std::string& filename, u32 base_addr); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static u32 TranslateAddr(u32 addr, THREEloadinfo *loadinfo, u32* offsets) |  | ||||||
| { | { | ||||||
|     if (addr < offsets[0]) |     if (addr < offsets[0]) | ||||||
|         return loadinfo->seg_addrs[0] + addr; |         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]; |     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; |         return ERROR_FILE; | ||||||
|     } | 
 | ||||||
|  |     // Reset read pointer in case this file has been read before.
 | ||||||
|  |     file.Seek(0, SEEK_SET); | ||||||
|  | 
 | ||||||
|     THREEDSX_Header hdr; |     THREEDSX_Header hdr; | ||||||
|     if (file.ReadBytes(&hdr, sizeof(hdr)) != sizeof(hdr)) |     if (file.ReadBytes(&hdr, sizeof(hdr)) != sizeof(hdr)) | ||||||
|         return ERROR_READ; |         return ERROR_READ; | ||||||
|  | @ -136,8 +130,9 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) | ||||||
|     // Read the relocation headers
 |     // Read the relocation headers
 | ||||||
|     u32* relocs = (u32*)(loadinfo.seg_ptrs[2] + hdr.data_seg_size); |     u32* relocs = (u32*)(loadinfo.seg_ptrs[2] + hdr.data_seg_size); | ||||||
| 
 | 
 | ||||||
|     for (u32 current_segment = 0; current_segment < 3; current_segment++) { |     for (unsigned current_segment : {0, 1, 2}) { | ||||||
|         if (file.ReadBytes(&relocs[current_segment*n_reloc_tables], n_reloc_tables * 4) != n_reloc_tables * 4) |         size_t size = n_reloc_tables * 4; | ||||||
|  |         if (file.ReadBytes(&relocs[current_segment * n_reloc_tables], size) != size) | ||||||
|             return ERROR_READ; |             return ERROR_READ; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -153,8 +148,8 @@ 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); |     memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size); | ||||||
| 
 | 
 | ||||||
|     // Relocate the segments
 |     // Relocate the segments
 | ||||||
|     for (u32 current_segment = 0; current_segment < 3; current_segment++) { |     for (unsigned current_segment : {0, 1, 2}) { | ||||||
|         for (u32 current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) { |         for (unsigned current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) { | ||||||
|             u32 n_relocs = relocs[current_segment * n_reloc_tables + current_segment_reloc_table]; |             u32 n_relocs = relocs[current_segment * n_reloc_tables + current_segment_reloc_table]; | ||||||
|             if (current_segment_reloc_table >= 2) { |             if (current_segment_reloc_table >= 2) { | ||||||
|                 // We are not using this table - ignore it because we don't know what it dose
 |                 // We are not using this table - ignore it because we don't know what it dose
 | ||||||
|  | @ -164,7 +159,7 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) | ||||||
|             static THREEDSX_Reloc reloc_table[RELOCBUFSIZE]; |             static THREEDSX_Reloc reloc_table[RELOCBUFSIZE]; | ||||||
| 
 | 
 | ||||||
|             u32* pos = (u32*)loadinfo.seg_ptrs[current_segment]; |             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) { |             while (n_relocs) { | ||||||
|                 u32 remaining = std::min(RELOCBUFSIZE, n_relocs); |                 u32 remaining = std::min(RELOCBUFSIZE, n_relocs); | ||||||
|  | @ -173,20 +168,26 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) | ||||||
|                 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; |                     return ERROR_READ; | ||||||
| 
 | 
 | ||||||
|                 for (u32 current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) { |                 for (unsigned current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) { | ||||||
|                     LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)\n", |                     const auto& table = reloc_table[current_inprogress]; | ||||||
|                         current_segment_reloc_table, (u32)reloc_table[current_inprogress].skip, (u32)reloc_table[current_inprogress].patch); |                     LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)\n", current_segment_reloc_table, | ||||||
|                     pos += reloc_table[current_inprogress].skip; |                               (u32)table.skip, (u32)table.patch); | ||||||
|                     s32 num_patches = reloc_table[current_inprogress].patch; |                     pos += table.skip; | ||||||
|  |                     s32 num_patches = table.patch; | ||||||
|                     while (0 < num_patches && pos < end_pos) { |                     while (0 < num_patches && pos < end_pos) { | ||||||
|                         u32 in_addr = (char*)pos - (char*)&all_mem[0]; |                         u32 in_addr = (char*)pos - (char*)&all_mem[0]; | ||||||
|                         u32 addr = TranslateAddr(*pos, &loadinfo, offsets); |                         u32 addr = TranslateAddr(*pos, &loadinfo, offsets); | ||||||
|                         LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n", |                         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) { |                         switch (current_segment_reloc_table) { | ||||||
|                         case 0: *pos = (addr); break; |                         case 0: | ||||||
|                         case 1: *pos = (addr - in_addr); break; |                             *pos = (addr); | ||||||
|                         default: break; //this should never happen
 |                             break; | ||||||
|  |                         case 1: | ||||||
|  |                             *pos = (addr - in_addr); | ||||||
|  |                             break; | ||||||
|  |                         default: | ||||||
|  |                             break; //this should never happen
 | ||||||
|                         } |                         } | ||||||
|                         pos++; |                         pos++; | ||||||
|                         num_patches--; |                         num_patches--; | ||||||
|  | @ -207,27 +208,29 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) | ||||||
|     return ERROR_NONE; |     return ERROR_NONE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|     /// AppLoader_DSX constructor
 | FileType AppLoader_THREEDSX::IdentifyType(FileUtil::IOFile& file) { | ||||||
|     AppLoader_THREEDSX::AppLoader_THREEDSX(const std::string& filename) : filename(filename) { |     u32 magic; | ||||||
|  |     file.Seek(0, SEEK_SET); | ||||||
|  |     if (1 != file.ReadArray<u32>(&magic, 1)) | ||||||
|  |         return FileType::Error; | ||||||
|  | 
 | ||||||
|  |     if (MakeMagic('3', 'D', 'S', 'X') == magic) | ||||||
|  |         return FileType::THREEDSX; | ||||||
|  | 
 | ||||||
|  |     return FileType::Error; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|     /// AppLoader_DSX destructor
 |  | ||||||
|     AppLoader_THREEDSX::~AppLoader_THREEDSX() { |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|     * Loads a 3DSX file |  | ||||||
|     * @return Success on success, otherwise Error |  | ||||||
|     */ |  | ||||||
| ResultStatus AppLoader_THREEDSX::Load() { | ResultStatus AppLoader_THREEDSX::Load() { | ||||||
|         LOG_INFO(Loader, "Loading 3DSX file %s...", filename.c_str()); |     if (is_loaded) | ||||||
|         FileUtil::IOFile file(filename, "rb"); |         return ResultStatus::ErrorAlreadyLoaded; | ||||||
|         if (file.IsOpen()) { | 
 | ||||||
|             THREEDSXReader::Load3DSXFile(filename, 0x00100000); |     if (!file->IsOpen()) | ||||||
|             Kernel::LoadExec(0x00100000); |  | ||||||
|         } else { |  | ||||||
|         return ResultStatus::Error; |         return ResultStatus::Error; | ||||||
|         } | 
 | ||||||
|  |     Load3DSXFile(*file, 0x00100000); | ||||||
|  |     Kernel::LoadExec(0x00100000); | ||||||
|  | 
 | ||||||
|  |     is_loaded = true; | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,18 +15,20 @@ namespace Loader { | ||||||
| /// Loads an 3DSX file
 | /// Loads an 3DSX file
 | ||||||
| class AppLoader_THREEDSX final : public AppLoader { | class AppLoader_THREEDSX final : public AppLoader { | ||||||
| public: | public: | ||||||
|     AppLoader_THREEDSX(const std::string& filename); |     AppLoader_THREEDSX(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { } | ||||||
|     ~AppLoader_THREEDSX() override; | 
 | ||||||
|  |     /**
 | ||||||
|  |      * 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 |      * Load the bootable file | ||||||
|      * @return ResultStatus result of function |      * @return ResultStatus result of function | ||||||
|      */ |      */ | ||||||
|     ResultStatus Load() override; |     ResultStatus Load() override; | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     std::string filename; |  | ||||||
|     bool        is_loaded; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace Loader
 | } // namespace Loader
 | ||||||
|  |  | ||||||
|  | @ -54,12 +54,6 @@ enum ElfMachine { | ||||||
| #define EI_PAD     7 | #define EI_PAD     7 | ||||||
| #define EI_NIDENT 16 | #define EI_NIDENT 16 | ||||||
| 
 | 
 | ||||||
| // Magic number
 |  | ||||||
| #define ELFMAG0 0x7F |  | ||||||
| #define ELFMAG1  'E' |  | ||||||
| #define ELFMAG2  'L' |  | ||||||
| #define ELFMAG3  'F' |  | ||||||
| 
 |  | ||||||
| // Sections constants
 | // Sections constants
 | ||||||
| 
 | 
 | ||||||
| // Section types
 | // Section types
 | ||||||
|  | @ -188,7 +182,6 @@ private: | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     ElfReader(void *ptr); |     ElfReader(void *ptr); | ||||||
|     ~ElfReader() { } |  | ||||||
| 
 | 
 | ||||||
|     u32 Read32(int off) const { return base32[off >> 2]; } |     u32 Read32(int off) const { return base32[off >> 2]; } | ||||||
| 
 | 
 | ||||||
|  | @ -197,7 +190,7 @@ public: | ||||||
|     ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } |     ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } | ||||||
|     u32 GetEntryPoint() const { return entryPoint; } |     u32 GetEntryPoint() const { return entryPoint; } | ||||||
|     u32 GetFlags() const { return (u32)(header->e_flags); } |     u32 GetFlags() const { return (u32)(header->e_flags); } | ||||||
|     bool LoadInto(u32 vaddr); |     void LoadInto(u32 vaddr); | ||||||
|     bool LoadSymbols(); |     bool LoadSymbols(); | ||||||
| 
 | 
 | ||||||
|     int GetNumSegments() const { return (int)(header->e_phnum); } |     int GetNumSegments() const { return (int)(header->e_phnum); } | ||||||
|  | @ -245,7 +238,7 @@ const char *ElfReader::GetSectionName(int section) const { | ||||||
|         return nullptr; |         return nullptr; | ||||||
| 
 | 
 | ||||||
|     int name_offset = sections[section].sh_name; |     int name_offset = sections[section].sh_name; | ||||||
|     char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); |     const char* ptr = (char*)GetSectionDataPtr(header->e_shstrndx); | ||||||
| 
 | 
 | ||||||
|     if (ptr) |     if (ptr) | ||||||
|         return ptr + name_offset; |         return ptr + name_offset; | ||||||
|  | @ -253,7 +246,7 @@ const char *ElfReader::GetSectionName(int section) const { | ||||||
|     return nullptr; |     return nullptr; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool ElfReader::LoadInto(u32 vaddr) { | void ElfReader::LoadInto(u32 vaddr) { | ||||||
|     LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx); |     LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx); | ||||||
| 
 | 
 | ||||||
|     // Should we relocate?
 |     // Should we relocate?
 | ||||||
|  | @ -271,7 +264,7 @@ bool ElfReader::LoadInto(u32 vaddr) { | ||||||
|     u32 segment_addr[32]; |     u32 segment_addr[32]; | ||||||
|     u32 base_addr = relocate ? vaddr : 0; |     u32 base_addr = relocate ? vaddr : 0; | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < header->e_phnum; i++) { |     for (unsigned i = 0; i < header->e_phnum; i++) { | ||||||
|         Elf32_Phdr* p = segments + i; |         Elf32_Phdr* p = segments + i; | ||||||
|         LOG_DEBUG(Loader, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, |         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); | ||||||
|  | @ -284,7 +277,6 @@ bool ElfReader::LoadInto(u32 vaddr) { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     LOG_DEBUG(Loader, "Done loading."); |     LOG_DEBUG(Loader, "Done loading."); | ||||||
|     return true; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const { | SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const { | ||||||
|  | @ -307,7 +299,7 @@ bool ElfReader::LoadSymbols() { | ||||||
|         //We have a symbol table!
 |         //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); |         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; |             int size = symtab[sym].st_size; | ||||||
|             if (size == 0) |             if (size == 0) | ||||||
|                 continue; |                 continue; | ||||||
|  | @ -330,40 +322,38 @@ bool ElfReader::LoadSymbols() { | ||||||
| 
 | 
 | ||||||
| namespace Loader { | namespace Loader { | ||||||
| 
 | 
 | ||||||
| /// AppLoader_ELF constructor
 | FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) { | ||||||
| AppLoader_ELF::AppLoader_ELF(const std::string& filename) : is_loaded(false) { |     u32 magic; | ||||||
|     this->filename = filename; |     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() { | ResultStatus AppLoader_ELF::Load() { | ||||||
|     LOG_INFO(Loader, "Loading ELF file %s...", filename.c_str()); |  | ||||||
| 
 |  | ||||||
|     if (is_loaded) |     if (is_loaded) | ||||||
|         return ResultStatus::ErrorAlreadyLoaded; |         return ResultStatus::ErrorAlreadyLoaded; | ||||||
| 
 | 
 | ||||||
|     FileUtil::IOFile file(filename, "rb"); |     if (!file->IsOpen()) | ||||||
|  |         return ResultStatus::Error; | ||||||
| 
 | 
 | ||||||
|     if (file.IsOpen()) { |     // Reset read pointer in case this file has been read before.
 | ||||||
|         u32 size = (u32)file.GetSize(); |     file->Seek(0, SEEK_SET); | ||||||
|  | 
 | ||||||
|  |     u32 size = static_cast<u32>(file->GetSize()); | ||||||
|     std::unique_ptr<u8[]> buffer(new u8[size]); |     std::unique_ptr<u8[]> buffer(new u8[size]); | ||||||
|         file.ReadBytes(&buffer[0], size); |     if (file->ReadBytes(&buffer[0], size) != size) | ||||||
|  |         return ResultStatus::Error; | ||||||
| 
 | 
 | ||||||
|     ElfReader elf_reader(&buffer[0]); |     ElfReader elf_reader(&buffer[0]); | ||||||
|     elf_reader.LoadInto(0x00100000); |     elf_reader.LoadInto(0x00100000); | ||||||
|     Kernel::LoadExec(elf_reader.GetEntryPoint()); |     Kernel::LoadExec(elf_reader.GetEntryPoint()); | ||||||
|     } else { | 
 | ||||||
|         return ResultStatus::Error; |     is_loaded = true; | ||||||
|     } |  | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,18 +15,20 @@ namespace Loader { | ||||||
| /// Loads an ELF/AXF file
 | /// Loads an ELF/AXF file
 | ||||||
| class AppLoader_ELF final : public AppLoader { | class AppLoader_ELF final : public AppLoader { | ||||||
| public: | public: | ||||||
|     AppLoader_ELF(const std::string& filename); |     AppLoader_ELF(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { } | ||||||
|     ~AppLoader_ELF() override; | 
 | ||||||
|  |     /**
 | ||||||
|  |      * 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 |      * Load the bootable file | ||||||
|      * @return ResultStatus result of function |      * @return ResultStatus result of function | ||||||
|      */ |      */ | ||||||
|     ResultStatus Load() override; |     ResultStatus Load() override; | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     std::string filename; |  | ||||||
|     bool        is_loaded; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace Loader
 | } // namespace Loader
 | ||||||
|  |  | ||||||
|  | @ -19,11 +19,32 @@ namespace Loader { | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Identifies the type of a bootable file |  * Identifies the type of a bootable file | ||||||
|  * @param filename String filename of bootable file |  * @param file open file | ||||||
|  * @todo (ShizZy) this function sucks... make it actually check file contents etc. |  | ||||||
|  * @return FileType of 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) { |     if (filename.size() == 0) { | ||||||
|         LOG_ERROR(Loader, "invalid filename %s", filename.c_str()); |         LOG_ERROR(Loader, "invalid filename %s", filename.c_str()); | ||||||
|         return FileType::Error; |         return FileType::Error; | ||||||
|  | @ -34,47 +55,74 @@ FileType IdentifyFile(const std::string &filename) { | ||||||
|         return FileType::Unknown; |         return FileType::Unknown; | ||||||
|     std::string extension = Common::ToLower(filename.substr(extension_loc)); |     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; |         return FileType::ELF; | ||||||
|     } else if (extension == ".axf") { |     else if (extension == ".axf") | ||||||
|         return FileType::ELF; |         return FileType::ELF; | ||||||
|     } else if (extension == ".cxi") { |     else if (extension == ".cxi") | ||||||
|         return FileType::CXI; |         return FileType::CXI; | ||||||
|     } else if (extension == ".cci") { |     else if (extension == ".cci") | ||||||
|         return FileType::CCI; |         return FileType::CCI; | ||||||
|     } else if (extension == ".bin") { |     else if (extension == ".bin") | ||||||
|         return FileType::BIN; |         return FileType::BIN; | ||||||
|     } else if (extension == ".3ds") { |     else if (extension == ".3ds") | ||||||
|         return FileType::CCI; |         return FileType::CCI; | ||||||
|     } else if (extension == ".3dsx") { |     else if (extension == ".3dsx") | ||||||
|         return FileType::THREEDSX; |         return FileType::THREEDSX; | ||||||
|     } |  | ||||||
|     return FileType::Unknown; |     return FileType::Unknown; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | static const char* GetFileTypeString(FileType type) { | ||||||
|  * Identifies and loads a bootable file |     switch (type) { | ||||||
|  * @param filename String filename of bootable file |     case FileType::CCI: | ||||||
|  * @return ResultStatus result of function |         return "NCSD"; | ||||||
|  */ |     case FileType::CXI: | ||||||
| ResultStatus LoadFile(const std::string& filename) { |         return "NCCH"; | ||||||
|     LOG_INFO(Loader, "Loading file %s...", filename.c_str()); |     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...
 |     //3DSX file format...
 | ||||||
|     case FileType::THREEDSX: |     case FileType::THREEDSX: | ||||||
|         return AppLoader_THREEDSX(filename).Load(); |         return AppLoader_THREEDSX(std::move(file)).Load(); | ||||||
| 
 | 
 | ||||||
|     // Standard ELF file format...
 |     // Standard ELF file format...
 | ||||||
|     case FileType::ELF: |     case FileType::ELF: | ||||||
|         return AppLoader_ELF(filename).Load(); |         return AppLoader_ELF(std::move(file)).Load(); | ||||||
| 
 | 
 | ||||||
|     // NCCH/NCSD container formats...
 |     // NCCH/NCSD container formats...
 | ||||||
|     case FileType::CXI: |     case FileType::CXI: | ||||||
|     case FileType::CCI: { |     case FileType::CCI: | ||||||
|         AppLoader_NCCH app_loader(filename); |     { | ||||||
|  |         AppLoader_NCCH app_loader(std::move(file)); | ||||||
| 
 | 
 | ||||||
|         // Load application and RomFS
 |         // Load application and RomFS
 | ||||||
|         if (ResultStatus::Success == app_loader.Load()) { |         if (ResultStatus::Success == app_loader.Load()) { | ||||||
|  | @ -88,16 +136,11 @@ ResultStatus LoadFile(const std::string& filename) { | ||||||
|     // Raw BIN file format...
 |     // Raw BIN file format...
 | ||||||
|     case FileType::BIN: |     case FileType::BIN: | ||||||
|     { |     { | ||||||
|         LOG_INFO(Loader, "Loading BIN file %s...", filename.c_str()); |         size_t size = (size_t)file->GetSize(); | ||||||
| 
 |         if (file->ReadBytes(Memory::GetPointer(Memory::EXEFS_CODE_VADDR), size) != size) | ||||||
|         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 { |  | ||||||
|             return ResultStatus::Error; |             return ResultStatus::Error; | ||||||
|         } | 
 | ||||||
|  |         Kernel::LoadExec(Memory::EXEFS_CODE_VADDR); | ||||||
|         return ResultStatus::Success; |         return ResultStatus::Success; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -106,10 +149,11 @@ ResultStatus LoadFile(const std::string& filename) { | ||||||
| 
 | 
 | ||||||
|     // IdentifyFile could know identify file type...
 |     // IdentifyFile could know identify file type...
 | ||||||
|     case FileType::Unknown: |     case FileType::Unknown: | ||||||
| 
 |     { | ||||||
|     default: |         LOG_CRITICAL(Loader, "File %s is of unknown type."); | ||||||
|         return ResultStatus::ErrorInvalidFormat; |         return ResultStatus::ErrorInvalidFormat; | ||||||
|     } |     } | ||||||
|  |     } | ||||||
|     return ResultStatus::Error; |     return ResultStatus::Error; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 | 
 | ||||||
| #include "common/common.h" | #include "common/common.h" | ||||||
|  | #include "common/file_util.h" | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
| // Loader namespace
 | // Loader namespace
 | ||||||
|  | @ -37,10 +38,14 @@ enum class ResultStatus { | ||||||
|     ErrorMemoryAllocationFailed, |     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
 | /// Interface for loading an application
 | ||||||
| class AppLoader : NonCopyable { | class AppLoader : NonCopyable { | ||||||
| public: | public: | ||||||
|     AppLoader() { } |     AppLoader(std::unique_ptr<FileUtil::IOFile>&& file) : file(std::move(file)) { } | ||||||
|     virtual ~AppLoader() { } |     virtual ~AppLoader() { } | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|  | @ -93,14 +98,11 @@ public: | ||||||
|     virtual ResultStatus ReadRomFS(std::vector<u8>& buffer) const { |     virtual ResultStatus ReadRomFS(std::vector<u8>& buffer) const { | ||||||
|         return ResultStatus::ErrorNotImplemented; |         return ResultStatus::ErrorNotImplemented; | ||||||
|     } |     } | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| /**
 | protected: | ||||||
|  * Identifies the type of a bootable file |     std::unique_ptr<FileUtil::IOFile> file; | ||||||
|  * @param filename String filename of bootable file |     bool                              is_loaded = false; | ||||||
|  * @return FileType of file | }; | ||||||
|  */ |  | ||||||
| FileType IdentifyFile(const std::string &filename); |  | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Identifies and loads a bootable file |  * Identifies and loads a bootable file | ||||||
|  |  | ||||||
|  | @ -4,8 +4,6 @@ | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| 
 | 
 | ||||||
| #include "common/file_util.h" |  | ||||||
| 
 |  | ||||||
| #include "core/loader/ncch.h" | #include "core/loader/ncch.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
| #include "core/mem_map.h" | #include "core/mem_map.h" | ||||||
|  | @ -24,7 +22,7 @@ static const int kBlockSize     = 0x200;    ///< Size of ExeFS blocks (in bytes) | ||||||
|  * @param size Size of compressed buffer |  * @param size Size of compressed buffer | ||||||
|  * @return Size of decompressed 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); |     u32 offset_size = *(u32*)(buffer + size - 4); | ||||||
|     return offset_size + size; |     return offset_size + size; | ||||||
| } | } | ||||||
|  | @ -37,9 +35,9 @@ static u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { | ||||||
|  * @param decompressed_size Size of decompressed buffer |  * @param decompressed_size Size of decompressed buffer | ||||||
|  * @return True on success, otherwise false |  * @return True on success, otherwise false | ||||||
|  */ |  */ | ||||||
| static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) { | static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) { | ||||||
|     u8* footer = compressed + compressed_size - 8; |     const u8* footer = compressed + compressed_size - 8; | ||||||
|     u32 buffer_top_and_bottom = *(u32*)footer; |     u32 buffer_top_and_bottom = *reinterpret_cast<const u32*>(footer); | ||||||
|     u32 out = decompressed_size; |     u32 out = decompressed_size; | ||||||
|     u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF); |     u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF); | ||||||
|     u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF); |     u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF); | ||||||
|  | @ -50,7 +48,7 @@ static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompresse | ||||||
|     while (index > stop_index) { |     while (index > stop_index) { | ||||||
|        u8 control = compressed[--index]; |        u8 control = compressed[--index]; | ||||||
| 
 | 
 | ||||||
|         for(u32 i = 0; i < 8; i++) { |         for (unsigned i = 0; i < 8; i++) { | ||||||
|             if (index <= stop_index) |             if (index <= stop_index) | ||||||
|                 break; |                 break; | ||||||
|             if (index <= 0) |             if (index <= 0) | ||||||
|  | @ -60,9 +58,8 @@ static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompresse | ||||||
| 
 | 
 | ||||||
|             if (control & 0x80) { |             if (control & 0x80) { | ||||||
|                 // Check if compression is out of bounds
 |                 // Check if compression is out of bounds
 | ||||||
|                 if(index < 2) { |                 if (index < 2) | ||||||
|                     return false; |                     return false; | ||||||
|                 } |  | ||||||
|                 index -= 2; |                 index -= 2; | ||||||
| 
 | 
 | ||||||
|                 u32 segment_offset = compressed[index] | (compressed[index + 1] << 8); |                 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; |                 segment_offset += 2; | ||||||
| 
 | 
 | ||||||
|                 // Check if compression is out of bounds
 |                 // Check if compression is out of bounds
 | ||||||
|                 if(out < segment_size) { |                 if (out < segment_size) | ||||||
|                     return false; |                     return false; | ||||||
|                 } | 
 | ||||||
|                 for(u32 j = 0; j < segment_size; j++) { |                 for (unsigned j = 0; j < segment_size; j++) { | ||||||
|                     // Check if compression is out of bounds
 |                     // Check if compression is out of bounds
 | ||||||
|                     if(out + segment_offset >= decompressed_size) { |                     if (out + segment_offset >= decompressed_size) | ||||||
|                         return false; |                         return false; | ||||||
|                     } |  | ||||||
| 
 | 
 | ||||||
|                     u8 data = decompressed[out + segment_offset]; |                     u8 data = decompressed[out + segment_offset]; | ||||||
|                     decompressed[--out] = data; |                     decompressed[--out] = data; | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 // Check if compression is out of bounds
 |                 // Check if compression is out of bounds
 | ||||||
|                 if(out < 1) { |                 if (out < 1) | ||||||
|                     return false; |                     return false; | ||||||
|                 } |  | ||||||
|                 decompressed[--out] = compressed[--index]; |                 decompressed[--out] = compressed[--index]; | ||||||
|             } |             } | ||||||
|             control <<= 1; |             control <<= 1; | ||||||
|  | @ -99,24 +94,21 @@ static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompresse | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
| // AppLoader_NCCH class
 | // AppLoader_NCCH class
 | ||||||
| 
 | 
 | ||||||
| /// AppLoader_NCCH constructor
 | FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { | ||||||
| AppLoader_NCCH::AppLoader_NCCH(const std::string& filename) { |     u32 magic; | ||||||
|     this->filename = filename; |     file.Seek(0x100, SEEK_SET); | ||||||
|     is_loaded = false; |     if (1 != file.ReadArray<u32>(&magic, 1)) | ||||||
|     is_compressed = false; |         return FileType::Error; | ||||||
|     entry_point = 0; | 
 | ||||||
|     ncch_offset = 0; |     if (MakeMagic('N', 'C', 'S', 'D') == magic) | ||||||
|     exefs_offset = 0; |         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 { | ResultStatus AppLoader_NCCH::LoadExec() const { | ||||||
|     if (!is_loaded) |     if (!is_loaded) | ||||||
|         return ResultStatus::ErrorNotLoaded; |         return ResultStatus::ErrorNotLoaded; | ||||||
|  | @ -130,94 +122,81 @@ ResultStatus AppLoader_NCCH::LoadExec() const { | ||||||
|     return ResultStatus::Error; |     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 { | ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const { | ||||||
|     // Iterate through the ExeFs archive until we find the .code file...
 |     if (!file->IsOpen()) | ||||||
|     FileUtil::IOFile file(filename, "rb"); |         return ResultStatus::Error; | ||||||
|     if (file.IsOpen()) { | 
 | ||||||
|     LOG_DEBUG(Loader, "%d sections:", kMaxSections); |     LOG_DEBUG(Loader, "%d sections:", kMaxSections); | ||||||
|         for (int i = 0; i < kMaxSections; i++) { |     // 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...
 |         // Load the specified section...
 | ||||||
|             if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { |         if (strcmp(section.name, name) == 0) { | ||||||
|                 LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", i, |             LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", section_number, | ||||||
|                         exefs_header.section[i].offset, exefs_header.section[i].size, |                       section.offset, section.size, section.name); | ||||||
|                         exefs_header.section[i].name); |  | ||||||
| 
 | 
 | ||||||
|                 s64 section_offset = (exefs_header.section[i].offset + exefs_offset + |             s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset); | ||||||
|                     sizeof(ExeFs_Header)+ncch_offset); |             file->Seek(section_offset, SEEK_SET); | ||||||
|                 file.Seek(section_offset, 0); |  | ||||||
| 
 | 
 | ||||||
|                 // Section is compressed...
 |             if (is_compressed) { | ||||||
|                 if (i == 0 && is_compressed) { |                 // Section is compressed, read compressed .code section...
 | ||||||
|                     // Read compressed .code section...
 |  | ||||||
|                 std::unique_ptr<u8[]> temp_buffer; |                 std::unique_ptr<u8[]> temp_buffer; | ||||||
|                 try { |                 try { | ||||||
|                         temp_buffer.reset(new u8[exefs_header.section[i].size]); |                     temp_buffer.reset(new u8[section.size]); | ||||||
|                 } catch (std::bad_alloc&) { |                 } catch (std::bad_alloc&) { | ||||||
|                     return ResultStatus::ErrorMemoryAllocationFailed; |                     return ResultStatus::ErrorMemoryAllocationFailed; | ||||||
|                 } |                 } | ||||||
|                     file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size); | 
 | ||||||
|  |                 if (file->ReadBytes(&temp_buffer[0], section.size) != section.size) | ||||||
|  |                     return ResultStatus::Error; | ||||||
| 
 | 
 | ||||||
|                 // Decompress .code section...
 |                 // Decompress .code section...
 | ||||||
|                     u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], |                 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], section.size); | ||||||
|                         exefs_header.section[i].size); |  | ||||||
|                 buffer.resize(decompressed_size); |                 buffer.resize(decompressed_size); | ||||||
|                     if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0], |                 if (!LZSS_Decompress(&temp_buffer[0], section.size, &buffer[0], decompressed_size)) | ||||||
|                         decompressed_size)) { |  | ||||||
|                     return ResultStatus::ErrorInvalidFormat; |                     return ResultStatus::ErrorInvalidFormat; | ||||||
|                     } |             } else { | ||||||
|                 // Section is uncompressed...
 |                 // Section is uncompressed...
 | ||||||
|                 } |                 buffer.resize(section.size); | ||||||
|                 else { |                 if (file->ReadBytes(&buffer[0], section.size) != section.size) | ||||||
|                     buffer.resize(exefs_header.section[i].size); |                     return ResultStatus::Error; | ||||||
|                     file.ReadBytes(&buffer[0], exefs_header.section[i].size); |  | ||||||
|             } |             } | ||||||
|             return ResultStatus::Success; |             return ResultStatus::Success; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     } else { |  | ||||||
|         LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str()); |  | ||||||
|         return ResultStatus::Error; |  | ||||||
|     } |  | ||||||
|     return ResultStatus::ErrorNotUsed; |     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() { | ResultStatus AppLoader_NCCH::Load() { | ||||||
|     LOG_INFO(Loader, "Loading NCCH file %s...", filename.c_str()); |  | ||||||
| 
 |  | ||||||
|     if (is_loaded) |     if (is_loaded) | ||||||
|         return ResultStatus::ErrorAlreadyLoaded; |         return ResultStatus::ErrorAlreadyLoaded; | ||||||
| 
 | 
 | ||||||
|     FileUtil::IOFile file(filename, "rb"); |     if (!file->IsOpen()) | ||||||
|     if (file.IsOpen()) { |         return ResultStatus::Error; | ||||||
|         file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); | 
 | ||||||
|  |     // Reset read pointer in case this file has been read before.
 | ||||||
|  |     file->Seek(0, SEEK_SET); | ||||||
|  | 
 | ||||||
|  |     if (file->ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) | ||||||
|  |         return ResultStatus::Error; | ||||||
| 
 | 
 | ||||||
|     // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
 |     // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
 | ||||||
|         if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) { |     if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { | ||||||
|         LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!"); |         LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!"); | ||||||
|         ncch_offset = 0x4000; |         ncch_offset = 0x4000; | ||||||
|             file.Seek(ncch_offset, 0); |         file->Seek(ncch_offset, SEEK_SET); | ||||||
|             file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); |         file->ReadBytes(&ncch_header, sizeof(NCCH_Header)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Verify we are loading the correct file type...
 |     // Verify we are loading the correct file type...
 | ||||||
|         if (0 != memcmp(&ncch_header.magic, "NCCH", 4)) |     if (MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) | ||||||
|         return ResultStatus::ErrorInvalidFormat; |         return ResultStatus::ErrorInvalidFormat; | ||||||
| 
 | 
 | ||||||
|     // Read ExHeader...
 |     // Read ExHeader...
 | ||||||
| 
 | 
 | ||||||
|         file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)); |     if (file->ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header)) | ||||||
|  |         return ResultStatus::Error; | ||||||
| 
 | 
 | ||||||
|     is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; |     is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; | ||||||
|     entry_point = exheader_header.codeset_info.text.address; |     entry_point = exheader_header.codeset_info.text.address; | ||||||
|  | @ -234,64 +213,35 @@ ResultStatus AppLoader_NCCH::Load() { | ||||||
|     LOG_DEBUG(Loader, "ExeFS offset:    0x%08X", exefs_offset); |     LOG_DEBUG(Loader, "ExeFS offset:    0x%08X", exefs_offset); | ||||||
|     LOG_DEBUG(Loader, "ExeFS size:      0x%08X", exefs_size); |     LOG_DEBUG(Loader, "ExeFS size:      0x%08X", exefs_size); | ||||||
| 
 | 
 | ||||||
|         file.Seek(exefs_offset + ncch_offset, 0); |     file->Seek(exefs_offset + ncch_offset, SEEK_SET); | ||||||
|         file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); |     if (file->ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) | ||||||
|  |         return ResultStatus::Error; | ||||||
| 
 | 
 | ||||||
|     is_loaded = true; // Set state to loaded
 |     is_loaded = true; // Set state to loaded
 | ||||||
| 
 | 
 | ||||||
|         LoadExec(); // Load the executable into memory for booting
 |     return LoadExec(); // Load the executable into memory for booting
 | ||||||
| 
 |  | ||||||
|         return ResultStatus::Success; |  | ||||||
|     } else { |  | ||||||
|         LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str()); |  | ||||||
|     } |  | ||||||
|     return ResultStatus::Error; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * 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 { | ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) const { | ||||||
|     return LoadSectionExeFS(".code", buffer); |     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 { | ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) const { | ||||||
|     return LoadSectionExeFS("icon", buffer); |     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 { | ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) const { | ||||||
|     return LoadSectionExeFS("banner", buffer); |     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 { | ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) const { | ||||||
|     return LoadSectionExeFS("logo", buffer); |     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 { | ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { | ||||||
|     FileUtil::IOFile file(filename, "rb"); |     if (!file->IsOpen()) | ||||||
|     if (file.IsOpen()) { |         return ResultStatus::Error; | ||||||
|  | 
 | ||||||
|     // Check if the NCCH has a RomFS...
 |     // Check if the NCCH has a RomFS...
 | ||||||
|     if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) { |     if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) { | ||||||
|         u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; |         u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; | ||||||
|  | @ -302,17 +252,14 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { | ||||||
| 
 | 
 | ||||||
|         buffer.resize(romfs_size); |         buffer.resize(romfs_size); | ||||||
| 
 | 
 | ||||||
|             file.Seek(romfs_offset, 0); |         file->Seek(romfs_offset, SEEK_SET); | ||||||
|             file.ReadBytes(&buffer[0], romfs_size); |         if (file->ReadBytes(&buffer[0], romfs_size) != romfs_size) | ||||||
|  |             return ResultStatus::Error; | ||||||
| 
 | 
 | ||||||
|         return ResultStatus::Success; |         return ResultStatus::Success; | ||||||
|     } |     } | ||||||
|     LOG_DEBUG(Loader, "NCCH has no RomFS"); |     LOG_DEBUG(Loader, "NCCH has no RomFS"); | ||||||
|     return ResultStatus::ErrorNotUsed; |     return ResultStatus::ErrorNotUsed; | ||||||
|     } else { |  | ||||||
|         LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str()); |  | ||||||
|     } |  | ||||||
|     return ResultStatus::Error; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| u64 AppLoader_NCCH::GetProgramId() const { | u64 AppLoader_NCCH::GetProgramId() const { | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "common/common.h" | #include "common/common.h" | ||||||
| #include "common/file_util.h" |  | ||||||
| 
 | 
 | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
| 
 | 
 | ||||||
|  | @ -14,7 +13,7 @@ | ||||||
| 
 | 
 | ||||||
| struct NCCH_Header { | struct NCCH_Header { | ||||||
|     u8 signature[0x100]; |     u8 signature[0x100]; | ||||||
|     char magic[4]; |     u32 magic; | ||||||
|     u32 content_size; |     u32 content_size; | ||||||
|     u8 partition_id[8]; |     u8 partition_id[8]; | ||||||
|     u16 maker_code; |     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)
 | /// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
 | ||||||
| class AppLoader_NCCH final : public AppLoader { | class AppLoader_NCCH final : public AppLoader { | ||||||
| public: | public: | ||||||
|     AppLoader_NCCH(const std::string& filename); |     AppLoader_NCCH(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { } | ||||||
|     ~AppLoader_NCCH() override; | 
 | ||||||
|  |     /**
 | ||||||
|  |      * 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 |      * Load the application | ||||||
|  | @ -213,14 +218,11 @@ private: | ||||||
|      */ |      */ | ||||||
|     ResultStatus LoadExec() const; |     ResultStatus LoadExec() const; | ||||||
| 
 | 
 | ||||||
|     std::string     filename; |     bool            is_compressed = false; | ||||||
| 
 | 
 | ||||||
|     bool            is_loaded; |     u32             entry_point = 0; | ||||||
|     bool            is_compressed; |     u32             ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header
 | ||||||
| 
 |     u32             exefs_offset = 0; | ||||||
|     u32             entry_point; |  | ||||||
|     u32             ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header
 |  | ||||||
|     u32             exefs_offset; |  | ||||||
| 
 | 
 | ||||||
|     NCCH_Header     ncch_header; |     NCCH_Header     ncch_header; | ||||||
|     ExeFs_Header    exefs_header; |     ExeFs_Header    exefs_header; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue