diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ea09819e5..b5e6174b7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -175,6 +175,7 @@ set(SRCS loader/elf.cpp loader/loader.cpp loader/ncch.cpp + loader/ncsd.cpp loader/smdh.cpp tracer/recorder.cpp memory.cpp @@ -373,6 +374,7 @@ set(HEADERS loader/elf.h loader/loader.h loader/ncch.h + loader/ncsd.h loader/smdh.h tracer/recorder.h tracer/citrace.h diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index be719d74c..3df8d8d7d 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -10,6 +10,7 @@ #include "core/loader/3dsx.h" #include "core/loader/elf.h" #include "core/loader/ncch.h" +#include "core/loader/ncsd.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -32,6 +33,7 @@ FileType IdentifyFile(FileUtil::IOFile& file) { CHECK_TYPE(THREEDSX) CHECK_TYPE(ELF) CHECK_TYPE(NCCH) + CHECK_TYPE(NCSD) #undef CHECK_TYPE @@ -110,11 +112,14 @@ static std::unique_ptr GetFileLoader(FileUtil::IOFile&& file, FileTyp case FileType::ELF: return std::make_unique(std::move(file), filename); - // NCCH/NCSD container formats. + // NCCH container format. case FileType::CXI: - case FileType::CCI: return std::make_unique(std::move(file), filepath); + // NCSD container format. + case FileType::CCI: + return std::make_unique(std::move(file), filepath); + default: return nullptr; } diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index ffc019560..938771c08 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -109,12 +109,9 @@ static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decom FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { u32 magic; file.Seek(0x100, SEEK_SET); - if (1 != file.ReadArray(&magic, 1)) + if (file.ReadArray(&magic, 1) != 1) return FileType::Error; - if (MakeMagic('N', 'C', 'S', 'D') == magic) - return FileType::CCI; - if (MakeMagic('N', 'C', 'C', 'H') == magic) return FileType::CXI; @@ -251,22 +248,13 @@ ResultStatus AppLoader_NCCH::LoadExeFS() { if (!file.IsOpen()) return ResultStatus::Error; - // Reset read pointer in case this file has been read before. - file.Seek(0, SEEK_SET); + file.Seek(ncch_offset, 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)... - if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { - LOG_DEBUG(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)); - } - // Verify we are loading the correct file type... - if (MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) + if (ncch_header.magic != MakeMagic('N', 'C', 'C', 'H')) return ResultStatus::ErrorInvalidFormat; // Read ExHeader... diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 0ebd47fd5..7f37bead7 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -161,9 +161,10 @@ 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(FileUtil::IOFile&& file, const std::string& filepath, u32 ncch_offset) + : AppLoader(std::move(file)), filepath(filepath), ncch_offset(ncch_offset) {} AppLoader_NCCH(FileUtil::IOFile&& file, const std::string& filepath) - : AppLoader(std::move(file)), filepath(filepath) {} - + : AppLoader(std::move(file)), filepath(filepath), ncch_offset(0) {} /** * Returns the type of the file * @param file FileUtil::IOFile open file @@ -217,18 +218,18 @@ private: */ ResultStatus LoadSectionExeFS(const char* name, std::vector& buffer); - /** - * Loads .code section into memory for booting - * @return ResultStatus result of function - */ - ResultStatus LoadExec(); - /** * Ensure ExeFS is loaded and ready for reading sections * @return ResultStatus result of function */ ResultStatus LoadExeFS(); + /** + * Loads .code section into memory for booting + * @return ResultStatus result of function + */ + ResultStatus LoadExec(); + /// Reads the region lockout info in the SMDH and send it to CFG service void ParseRegionLockoutInfo(); @@ -242,14 +243,13 @@ private: u32 core_version = 0; u8 priority = 0; u8 resource_limit_category = 0; - u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header + u32 ncch_offset = 0; ///< Offset to NCCH header, can be 0 or a location lower in the file + std::string filepath; u32 exefs_offset = 0; NCCH_Header ncch_header; ExeFs_Header exefs_header; ExHeader_Header exheader_header; - - std::string filepath; }; } // namespace Loader diff --git a/src/core/loader/ncsd.cpp b/src/core/loader/ncsd.cpp new file mode 100644 index 000000000..c718c3a35 --- /dev/null +++ b/src/core/loader/ncsd.cpp @@ -0,0 +1,68 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "core/loader/ncsd.h" + +namespace Loader { +FileType AppLoader_NCSD::IdentifyType(FileUtil::IOFile& file) { + u32 magic; + file.Seek(0x100, SEEK_SET); + if (file.ReadArray(&magic, 1) != 1) + return FileType::Error; + + if (magic == MakeMagic('N', 'C', 'S', 'D')) + return FileType::CCI; + + return FileType::Error; +} +ResultStatus AppLoader_NCSD::TryGetNCCHOffset(u32& offset) { + // Reset read pointer in case this file has been read before. + file.Seek(0, SEEK_SET); + + NCSD_Header ncsd_header; + if (file.ReadBytes(&ncsd_header, sizeof(NCSD_Header)) != sizeof(NCSD_Header)) + return ResultStatus::Error; + LOG_DEBUG(Loader, "Only loading the first (bootable) NCCH within the NCSD file!"); + offset = ncsd_header.partition_table[0].partition_offset * 0x200; + return ResultStatus::Success; +} + +ResultStatus AppLoader_NCSD::Load() { + return ncch_loader->Load(); +} +std::pair, ResultStatus> AppLoader_NCSD::LoadKernelSystemMode() { + return ncch_loader->LoadKernelSystemMode(); +} +ResultStatus AppLoader_NCSD::ReadCode(std::vector& buffer) { + return ncch_loader->ReadCode(buffer); +} +ResultStatus AppLoader_NCSD::ReadBanner(std::vector& buffer) { + return ncch_loader->ReadBanner(buffer); +} +ResultStatus AppLoader_NCSD::ReadLogo(std::vector& buffer) { + return ncch_loader->ReadLogo(buffer); +} +ResultStatus AppLoader_NCSD::ReadIcon(std::vector& buffer) { + return ncch_loader->ReadIcon(buffer); +} +ResultStatus AppLoader_NCSD::ReadProgramId(u64& out_program_id) { + return ncch_loader->ReadProgramId(out_program_id); +} +ResultStatus AppLoader_NCSD::ReadRomFS(std::shared_ptr& romfs_file, u64& offset, + u64& size) { + return ncch_loader->ReadRomFS(romfs_file, offset, size); +} +AppLoader_NCSD::AppLoader_NCSD(FileUtil::IOFile&& file, const std::string& filepath) + : AppLoader(std::move(file)) { + u32 tempoffset; + if (TryGetNCCHOffset(tempoffset) == ResultStatus::Success) { + ncch_loader = + new AppLoader_NCCH(std::move(FileUtil::IOFile(filepath, "rb")), filepath, tempoffset); + } else { + ncch_loader = new AppLoader_NCCH(std::move(FileUtil::IOFile(filepath, "rb")), filepath, 0); + } +} + +} // namespace Loader diff --git a/src/core/loader/ncsd.h b/src/core/loader/ncsd.h new file mode 100644 index 000000000..495bbd1a3 --- /dev/null +++ b/src/core/loader/ncsd.h @@ -0,0 +1,80 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/loader/ncch.h" + +// Offset & Length of partition in media units +struct Partition_Table_Entry { + u32_le partition_offset; + u32_le partition_size; +}; + +struct NCSD_Header { + std::array signature; /// + partitions_fs_type; //< Partitions FS type (0=None, 1=Normal, 3=FIRM, 4=AGB_FIRM save) + std::array partitions_crypt_type; //< Partitions crypt type (each byte corresponds to a + // partition in the partition table) + std::array partition_table; +}; + +namespace Loader { + +class AppLoader_NCSD final : public AppLoader { +public: + AppLoader_NCSD(FileUtil::IOFile&& file, const std::string& filepath); + + FileType GetFileType() override { + return IdentifyType(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); + ResultStatus Load() override; + /** + * Loads the Exheader and returns the system mode for this application. + * @return Optional with the kernel system mode + */ + std::pair, ResultStatus> LoadKernelSystemMode() override; + + ResultStatus ReadCode(std::vector& buffer) override; + + ResultStatus ReadIcon(std::vector& buffer) override; + + ResultStatus ReadBanner(std::vector& buffer) override; + + ResultStatus ReadLogo(std::vector& buffer) override; + + /** + * Get the program id of the application + * @param out_program_id Reference to store program id into + * @return ResultStatus result of function + */ + ResultStatus ReadProgramId(u64& out_program_id) override; + + /** + * Get the RomFS of the application + * @param romfs_file Reference to buffer to store data + * @param offset Offset in the file to the RomFS + * @param size Size of the RomFS in bytes + * @return ResultStatus result of function + */ + ResultStatus ReadRomFS(std::shared_ptr& romfs_file, u64& offset, + u64& size) override; + +protected: + ResultStatus TryGetNCCHOffset(u32& offset); + +private: + AppLoader_NCCH* ncch_loader; +}; +} // namespace Loader