diff --git a/src/citra_qt/game_list_worker.cpp b/src/citra_qt/game_list_worker.cpp index a20ae4794..de02e79c0 100644 --- a/src/citra_qt/game_list_worker.cpp +++ b/src/citra_qt/game_list_worker.cpp @@ -47,12 +47,16 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign if (!is_dir && HasSupportedFileExtension(physical_name)) { std::unique_ptr loader = Loader::GetLoader(physical_name); if (!loader) + { return true; + } bool executable = false; - loader->IsExecutable(executable); - if (!executable) + auto res = loader->IsExecutable(executable); + if (!executable && res != Loader::ResultStatus::ErrorEncrypted) + { return true; + } u64 program_id = 0; loader->ReadProgramId(program_id); diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp index 056f7a901..651b0d307 100644 --- a/src/core/file_sys/ncch_container.cpp +++ b/src/core/file_sys/ncch_container.cpp @@ -133,8 +133,37 @@ Loader::ResultStatus NCCHContainer::OpenFile(const std::string& filepath, u32 nc return Loader::ResultStatus::Success; } +Loader::ResultStatus NCCHContainer::LoadHeader() { + if (has_header) + return Loader::ResultStatus::Success; + if (!file.IsOpen()) { + + return Loader::ResultStatus::Error; + } + + // Reset read pointer in case this file has been read before. + file.Seek(ncch_offset, SEEK_SET); + + if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) + return Loader::ResultStatus::Error; + + // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... + if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { + LOG_DEBUG(Service_FS, "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 (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) + return Loader::ResultStatus::ErrorInvalidFormat; + + has_header = true; + return Loader::ResultStatus::Success; +} + Loader::ResultStatus NCCHContainer::Load() { - LOG_INFO(Service_FS, "Loading NCCH from file {}", filepath); if (is_loaded) return Loader::ResultStatus::Success; @@ -697,7 +726,7 @@ Loader::ResultStatus NCCHContainer::ReadOverrideRomFS(std::shared_ptr #include "common/file_util.h" #include "common/logging/log.h" +#include "common/common_paths.h" #include "common/string_util.h" #include "core/core.h" #include "core/file_sys/errors.h" @@ -33,6 +34,16 @@ #include "core/loader/loader.h" #include "core/loader/smdh.h" +namespace { +bool HasSupportedFileExtension(std::string path) { + static const std::array extensions = {{".3ds", ".3dsx", ".elf", ".axf", + ".cci", ".cxi" ".app" + }}; + const auto file_ext = FileUtil::GetExtensionFromFilename(path); + return std::find(extensions.begin(), extensions.end(), file_ext) != extensions.end(); +} +} + namespace Service::AM { constexpr u16 PLATFORM_CTR = 0x0004; @@ -373,6 +384,36 @@ InstallStatus InstallCIA(const std::string& path, installFile.Close(); LOG_INFO(Service_AM, "Installed {} successfully.", path); + + const FileUtil::DirectoryEntryCallable callback = [&callback](u64* num_entries_out, + const std::string& directory, + const std::string& virtual_name) -> bool { + const std::string physical_name = directory + DIR_SEP + virtual_name; + const bool is_dir = FileUtil::IsDirectory(physical_name); + if (!is_dir && HasSupportedFileExtension(physical_name)) { + std::unique_ptr loader = Loader::GetLoader(physical_name); + if (!loader) + { + return true; + } + + bool executable = false; + auto res = loader->IsExecutable(executable); + if (res == Loader::ResultStatus::ErrorEncrypted) + { + return false; + } + return true; + } else { + return FileUtil::ForeachDirectoryEntry(nullptr, physical_name, callback); + } + + }; + if (!FileUtil::ForeachDirectoryEntry(nullptr, path, callback)) + { + LOG_ERROR(Service_AM, "CIA {} contained encrypted files.", path); + return InstallStatus::ErrorEncrypted; + } return InstallStatus::Success; }