diff options
Diffstat (limited to 'src/core/loader/ncch.cpp')
-rw-r--r-- | src/core/loader/ncch.cpp | 308 |
1 files changed, 105 insertions, 203 deletions
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 23864d262..765efcf65 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 // Refer to the license.txt file included. +#include <memory> + #include "common/file_util.h" #include "core/loader/ncch.h" @@ -9,136 +11,6 @@ #include "core/mem_map.h" //////////////////////////////////////////////////////////////////////////////////////////////////// -/// NCCH header (Note: "NCCH" appears to be a publically unknown acronym) - -struct NCCH_Header { - u8 signature[0x100]; - char magic[4]; - u32 content_size; - u8 partition_id[8]; - u16 maker_code; - u16 version; - u8 reserved_0[4]; - u8 program_id[8]; - u8 temp_flag; - u8 reserved_1[0x2f]; - u8 product_code[0x10]; - u8 extended_header_hash[0x20]; - u32 extended_header_size; - u8 reserved_2[4]; - u8 flags[8]; - u32 plain_region_offset; - u32 plain_region_size; - u8 reserved_3[8]; - u32 exefs_offset; - u32 exefs_size; - u32 exefs_hash_region_size; - u8 reserved_4[4]; - u32 romfs_offset; - u32 romfs_size; - u32 romfs_hash_region_size; - u8 reserved_5[4]; - u8 exefs_super_block_hash[0x20]; - u8 romfs_super_block_hash[0x20]; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// ExeFS (executable file system) headers - -typedef struct { - char name[8]; - u32 offset; - u32 size; -} ExeFs_SectionHeader; - -typedef struct { - ExeFs_SectionHeader section[8]; - u8 reserved[0x80]; - u8 hashes[8][0x20]; -} ExeFs_Header; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// ExHeader (executable file system header) headers - -struct ExHeader_SystemInfoFlags{ - u8 reserved[5]; - u8 flag; - u8 remaster_version[2]; -} exheader_systeminfoflags; - -struct ExHeader_CodeSegmentInfo{ - u32 address; - u32 num_max_pages; - u32 code_size; -} exheader_codesegmentinfo; - -struct ExHeader_CodeSetInfo { - u8 name[8]; - ExHeader_SystemInfoFlags flags; - ExHeader_CodeSegmentInfo text; - u8 stacksize[4]; - ExHeader_CodeSegmentInfo ro; - u8 reserved[4]; - ExHeader_CodeSegmentInfo data; - u8 bsssize[4]; -}; - -struct ExHeader_DependencyList{ - u8 program_id[0x30][8]; -}; - -struct ExHeader_SystemInfo{ - u32 save_data_size; - u8 reserved[4]; - u8 jump_id[8]; - u8 reserved_2[0x30]; -}; - -struct ExHeader_StorageInfo{ - u8 ext_save_data_id[8]; - u8 system_save_data_id[8]; - u8 reserved[8]; - u8 access_info[7]; - u8 other_attributes; -}; - -struct ExHeader_ARM11_SystemLocalCaps{ - u8 program_id[8]; - u8 flags[8]; - u8 resource_limit_descriptor[0x10][2]; - ExHeader_StorageInfo storage_info; - u8 service_access_control[0x20][8]; - u8 reserved[0x1f]; - u8 resource_limit_category; -}; - -struct ExHeader_ARM11_KernelCaps{ - u8 descriptors[28][4]; - u8 reserved[0x10]; -}; - -struct ExHeader_ARM9_AccessControl{ - u8 descriptors[15]; - u8 descversion; -}; - -struct ExHeader_Header{ - ExHeader_CodeSetInfo codeset_info; - ExHeader_DependencyList dependency_list; - ExHeader_SystemInfo system_info; - ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; - ExHeader_ARM11_KernelCaps arm11_kernel_caps; - ExHeader_ARM9_AccessControl arm9_access_control; - struct { - u8 signature[0x100]; - u8 ncch_public_key_modulus[0x100]; - ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; - ExHeader_ARM11_KernelCaps arm11_kernel_caps; - ExHeader_ARM9_AccessControl arm9_access_control; - } access_desc; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// // Loader namespace namespace Loader { @@ -163,11 +35,9 @@ u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { * @param compressed_size Size of compressed buffer * @param decompressed Decompressed buffer * @param decompressed_size Size of decompressed buffer - * @param error_string String populated with error message on failure * @return True on success, otherwise false */ -bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size, - std::string* error_string) { +bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) { u8* footer = compressed + compressed_size - 8; u32 buffer_top_and_bottom = *(u32*)footer; u32 i, j; @@ -191,8 +61,8 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 break; if(control & 0x80) { + // Check if compression is out of bounds if(index < 2) { - *error_string = "Compression out of bounds"; return false; } index -= 2; @@ -202,22 +72,22 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 segment_offset &= 0x0FFF; segment_offset += 2; + // Check if compression is out of bounds if(out < segment_size) { - *error_string = "Compression out of bounds"; return false; } for(j = 0; j < segment_size; j++) { u8 data; + // Check if compression is out of bounds if(out + segment_offset >= decompressed_size) { - *error_string = "Compression out of bounds"; return false; } data = decompressed[out + segment_offset]; decompressed[--out] = data; } } else { + // Check if compression is out of bounds if(out < 1) { - *error_string = "Compression out of bounds"; return false; } decompressed[--out] = compressed[--index]; @@ -228,34 +98,96 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 return true; } +//////////////////////////////////////////////////////////////////////////////////////////////////// +// AppLoader_NCCH class + +/// AppLoader_NCCH constructor +AppLoader_NCCH::AppLoader_NCCH(std::string& filename) { + this->filename = filename; + is_loaded = false; + is_compressed = false; + entry_point = 0; + ncch_offset = 0; + exefs_offset = 0; +} + +/// AppLoader_NCCH destructor +AppLoader_NCCH::~AppLoader_NCCH() { +} + /** - * Load a data buffer into memory at the specified address - * @param addr Address to load memory into - * @param buffer Buffer of data to load into memory - * @param size Size of data to load into memory - * @todo Perhaps move this code somewhere more generic? + * Loads .code section into memory for booting + * @return ResultStatus result of function */ -void LoadBuffer(const u32 addr, const u8* const buffer, const int size) { - u32 *dst = (u32*)Memory::GetPointer(addr); - u32 *src = (u32*)buffer; - int size_aligned = (size + 3) / 4; +const ResultStatus AppLoader_NCCH::LoadExec() const { + if (!is_loaded) + return ResultStatus::ErrorNotLoaded; - for (int j = 0; j < size_aligned; j++) { - *dst++ = (*src++); + for (std::vector<u8>::size_type i = 0; i != code.size(); i++) { + Memory::Write8(entry_point + i, code[i]); } - return; + Kernel::LoadExec(entry_point); + + return ResultStatus::Success; } /** + * Reads an application section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) + * @param file Handle to file to read from + * @param name Name of section to read out of NCCH file + * @param buffer Buffer to read section into. + */ +const ResultStatus AppLoader_NCCH::LoadSection(File::IOFile& file, const char* name, + std::vector<u8>& buffer) { + // Iterate through the ExeFs archive until we find the .code file... + for (int i = 0; i < kExeFs_MaxSections; i++) { + INFO_LOG(LOADER, "ExeFS section %d:", i); + INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name); + INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset); + INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size); + + // Load the .code section (executable code)... + if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { + s64 section_offset = (exefs_header.section[i].offset + exefs_offset + + sizeof(ExeFs_Header) + ncch_offset); + file.Seek(section_offset, 0); + + // Section is compressed... + if (i == 0 && is_compressed) { + // Read compressed .code section... + std::unique_ptr<u8[]> temp_buffer(new u8[exefs_header.section[i].size]); + file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size); + + // Decompress .code section... + u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], exefs_header.section[i].size); + buffer.resize(decompressed_size); + if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0], + decompressed_size)) { + 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; + } + } + return ResultStatus::Error; +} + +/** * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) - * @param filename String filename of NCCH file * @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 */ -bool Load_NCCH(std::string& filename, std::string* error_string) { +const ResultStatus AppLoader_NCCH::Load() { INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str()); + if (is_loaded) + return ResultStatus::ErrorAlreadyLoaded; + File::IOFile file(filename, "rb"); if (file.IsOpen()) { @@ -263,80 +195,50 @@ bool Load_NCCH(std::string& filename, std::string* error_string) { file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... - int ncch_off = 0; // Offset to NCCH header, can be 0 or after NCSD header - if (memcmp(&ncch_header.magic, "NCSD", 4) == 0) { + if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) { WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!"); - ncch_off = 0x4000; - file.Seek(ncch_off, 0); + ncch_offset = 0x4000; + file.Seek(ncch_offset, 0); file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); } + // Verify we are loading the correct file type... - if (memcmp(&ncch_header.magic, "NCCH", 4) != 0) { - *error_string = "Invalid NCCH magic number (likely incorrect file type)"; - return false; - } + if (0 != memcmp(&ncch_header.magic, "NCCH", 4)) + return ResultStatus::ErrorInvalidFormat; + // Read ExHeader - ExHeader_Header exheader_header; file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)); - bool 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; INFO_LOG(LOADER, "Name: %s", exheader_header.codeset_info.name); INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no"); + INFO_LOG(LOADER, "Entry point: 0x%08X", entry_point); // Read ExeFS - u32 exefs_offset = ncch_header.exefs_offset * kExeFs_BlockSize; + exefs_offset = ncch_header.exefs_offset * kExeFs_BlockSize; u32 exefs_size = ncch_header.exefs_size * kExeFs_BlockSize; INFO_LOG(LOADER, "ExeFS offset: 0x%08X", exefs_offset); INFO_LOG(LOADER, "ExeFS size: 0x%08X", exefs_size); - ExeFs_Header exefs_header; - file.Seek(exefs_offset + ncch_off, 0); + file.Seek(exefs_offset + ncch_offset, 0); file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); - // Iterate through the ExeFs archive until we find the .code file... - for (int i = 0; i < kExeFs_MaxSections; i++) { - INFO_LOG(LOADER, "ExeFS section %d:", i); - INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name); - INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset); - INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size); - - // Load the .code section (executable code)... - if (strcmp((char*) exefs_header.section[i].name, ".code") == 0) { - file.Seek(exefs_header.section[i].offset + exefs_offset + sizeof(ExeFs_Header) + - ncch_off, 0); - - u8* buffer = new u8[exefs_header.section[i].size]; - file.ReadBytes(buffer, exefs_header.section[i].size); - - // Load compressed executable... - if (i == 0 && is_compressed) { - u32 decompressed_size = LZSS_GetDecompressedSize(buffer, - exefs_header.section[i].size); - - if (!LZSS_Decompress(buffer, exefs_header.section[i].size, - Memory::GetPointer(exheader_header.codeset_info.text.address), - decompressed_size, error_string)) { - return false; - } - // Load uncompressed executable... - } else { - // Load .code section into memory... - LoadBuffer(exheader_header.codeset_info.text.address, buffer, - exefs_header.section[i].size); - } - delete[] buffer; + // TODO(bunnei): Check ResultStatus here... + LoadSection(file, ".code", code); + LoadSection(file, ".icon", icon); + LoadSection(file, ".banner", banner); + LoadSection(file, ".logo", logo); - // Setup kernel emulation to boot .code section... - Kernel::LoadExec(exheader_header.codeset_info.text.address); + is_loaded = true; // Set state to loaded - // No need to load the other files from ExeFS until we do something with them... - return true; - } - } + LoadExec(); // Load the executable into memory for booting + + return ResultStatus::Success; } - return false; + return ResultStatus::Error; } } // namespace Loader |