From 2688a6ef61f0d8b611e11a4f0f856306893c6b5a Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Sat, 28 Nov 2020 15:39:59 +0800 Subject: [PATCH] Move System Applets into a new category and deselect by default As they are known to cause problems with Mii Maker and games like Tomodachi Life. --- src/core/importer.cpp | 7 +++- src/core/importer.h | 16 +++++--- src/frontend/import_dialog.cpp | 70 +++++++++++++++++++++------------- src/frontend/import_dialog.h | 4 +- 4 files changed, 62 insertions(+), 35 deletions(-) diff --git a/src/core/importer.cpp b/src/core/importer.cpp index e934363..0f33a60 100644 --- a/src/core/importer.cpp +++ b/src/core/importer.cpp @@ -84,6 +84,7 @@ bool SDMCImporter::ImportContent(const ContentSpecifier& specifier, case ContentType::Sysdata: return ImportSysdata(specifier.id, callback); case ContentType::SystemTitle: + case ContentType::SystemApplet: return ImportNandTitle(specifier, callback); default: UNREACHABLE(); @@ -720,9 +721,10 @@ void SDMCImporter::ListNandTitle(std::vector& out) const { const auto& [name, extdata_id, encryption, seed_crypto, icon] = LoadTitleData(ncch); + const auto type = (id >> 32) == 0x00040030 ? ContentType::SystemApplet + : ContentType::SystemTitle; out.push_back( - {ContentType::SystemTitle, id, - FileUtil::Exists(citra_path + "content/"), + {type, id, FileUtil::Exists(citra_path + "content/"), FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/"), name, extdata_id, encryption, seed_crypto, icon}); } while (false); @@ -883,6 +885,7 @@ void SDMCImporter::DeleteContent(const ContentSpecifier& specifier) { case ContentType::Sysdata: return DeleteSysdata(specifier.id); case ContentType::SystemTitle: + case ContentType::SystemApplet: return DeleteNandTitle(specifier.id); default: UNREACHABLE(); diff --git a/src/core/importer.h b/src/core/importer.h index e83af7b..751fff6 100644 --- a/src/core/importer.h +++ b/src/core/importer.h @@ -28,6 +28,7 @@ enum class ContentType { SystemArchive, Sysdata, SystemTitle, + SystemApplet, // This should belong to System Title, but they cause problems so a new category. }; /** @@ -110,8 +111,9 @@ public: * Blocks, but can be aborted on another thread if needed. * @return true on success, false otherwise */ - bool ImportContent(const ContentSpecifier& specifier, - const ProgressCallback& callback = [](std::size_t, std::size_t) {}); + bool ImportContent( + const ContentSpecifier& specifier, + const ProgressCallback& callback = [](std::size_t, std::size_t) {}); /** * Aborts current importing. @@ -123,8 +125,9 @@ public: * Blocks, but can be aborted on another thread. * @return true on success, false otherwise */ - bool DumpCXI(const ContentSpecifier& specifier, const std::string& destination, - const ProgressCallback& callback = [](std::size_t, std::size_t) {}); + bool DumpCXI( + const ContentSpecifier& specifier, const std::string& destination, + const ProgressCallback& callback = [](std::size_t, std::size_t) {}); /** * Aborts current CXI dumping. @@ -136,8 +139,9 @@ public: * Blocks, but can be aborted on another thread. * @return true on success, false otherwise */ - bool BuildCIA(const ContentSpecifier& specifier, const std::string& destination, - const ProgressCallback& callback = [](std::size_t, std::size_t) {}); + bool BuildCIA( + const ContentSpecifier& specifier, const std::string& destination, + const ProgressCallback& callback = [](std::size_t, std::size_t) {}); /** * Aborts current CIA building diff --git a/src/frontend/import_dialog.cpp b/src/frontend/import_dialog.cpp index cace3ac..777ad2e 100644 --- a/src/frontend/import_dialog.cpp +++ b/src/frontend/import_dialog.cpp @@ -15,6 +15,7 @@ #include #include #include +#include "common/assert.h" #include "common/logging/log.h" #include "common/scope_exit.h" #include "frontend/helpers/import_job.h" @@ -36,7 +37,7 @@ QString ReadableByteSize(qulonglong size) { } // content type, name, icon name -static constexpr std::array, 8> +static constexpr std::array, 9> ContentTypeMap{{ {Core::ContentType::Application, QT_TR_NOOP("Application"), "app"}, {Core::ContentType::Update, QT_TR_NOOP("Update"), "update"}, @@ -46,6 +47,7 @@ static constexpr std::array EncryptionTypeMap{{ @@ -144,7 +146,7 @@ void ImportDialog::RelistContent() { RepopulateContent(); }); - auto future = QtConcurrent::run([& contents = this->contents, &importer = this->importer] { + auto future = QtConcurrent::run([&contents = this->contents, &importer = this->importer] { contents = importer.ListContent(); }); future_watcher->setFuture(future); @@ -192,6 +194,14 @@ void ImportDialog::InsertTopLevelItem(const QString& text, QPixmap icon) { item->setFirstColumnSpanned(true); } +// Content types that themselves form a 'Title' like entity. +constexpr std::array SpecialContentTypeList{{ + Core::ContentType::SystemArchive, + Core::ContentType::Sysdata, + Core::ContentType::SystemTitle, + Core::ContentType::SystemApplet, +}}; + void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpecifier& content, std::size_t id, QString replace_name, QPixmap replace_icon) { @@ -208,7 +218,7 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe name = QStringLiteral("%1 (%2)") .arg(GetContentName(content)) .arg(GetContentTypeName(content.type)); - } else if (row <= 3) { + } else if (row <= SpecialContentTypeList.size()) { name = GetContentName(content); } else { name = GetContentTypeName(content.type); @@ -228,7 +238,8 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe if (content.type != Core::ContentType::Application && content.type != Core::ContentType::Update && content.type != Core::ContentType::DLC && - content.type != Core::ContentType::SystemTitle) { + content.type != Core::ContentType::SystemTitle && + content.type != Core::ContentType::SystemApplet) { // Do not display encryption in this case encryption.clear(); @@ -241,7 +252,8 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe QPixmap icon; if (replace_icon.isNull()) { // Exclude system titles, they are a single group but have own icons. - if (use_title_view && content.type != Core::ContentType::SystemTitle) { + if (use_title_view && content.type != Core::ContentType::SystemTitle && + content.type != Core::ContentType::SystemApplet) { icon = GetContentTypeIcon(content.type); } else { // When not in title view, System Data and System Archive groups use category icons. @@ -258,12 +270,21 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe ui->main->setItemWidget(item, 0, checkBox); connect(checkBox, &QCheckBox::stateChanged, - [this, item, size = content.maximum_size, type = content.type, + [this, item, id = content.id, size = content.maximum_size, type = content.type, exists = content.already_exists](int state) { if (state == Qt::Checked) { + if (!applet_warning_shown && !exists && + type == Core::ContentType::SystemApplet) { + QMessageBox::warning( + this, tr("Warning"), + tr("You are trying to import System Applets.\nThese are known to cause " + "problems with certain games.\nOnly proceed if you understand what " + "you are doing.")); + applet_warning_shown = true; + } total_size += size; } else { - if (!warning_shown && !exists && + if (!system_warning_shown && !exists && (type == Core::ContentType::SystemArchive || type == Core::ContentType::Sysdata || type == Core::ContentType::SystemTitle)) { @@ -273,7 +294,7 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe tr("You are de-selecting important files that may be necessary for " "your imported games to run.\nIt is highly recommended to import " "these contents if they do not exist yet.")); - warning_shown = true; + system_warning_shown = true; } total_size -= size; } @@ -284,7 +305,8 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe } }); - if (!content.already_exists) { + // Skip System Applets, but enable everything else by default. + if (!content.already_exists && content.type != Core::ContentType::SystemApplet) { checkBox->setChecked(true); } } @@ -307,15 +329,15 @@ void ImportDialog::RepopulateContent() { const bool use_title_view = ui->title_view_button->isChecked(); if (use_title_view) { + // Create 'Ungrouped' category. title_name_map.insert_or_assign(0, tr("Ungrouped")); - title_name_map.insert_or_assign(1, tr("System Archive")); - title_name_map.insert_or_assign(2, tr("System Data")); - title_name_map.insert_or_assign(3, tr("System Title")); - title_icon_map.insert_or_assign(0, QIcon::fromTheme(QStringLiteral("unknown")).pixmap(24)); - title_icon_map.insert_or_assign(1, GetContentTypeIcon(Core::ContentType::SystemArchive)); - title_icon_map.insert_or_assign(2, GetContentTypeIcon(Core::ContentType::Sysdata)); - title_icon_map.insert_or_assign(3, GetContentTypeIcon(Core::ContentType::SystemTitle)); + + // Create categories for special content types. + for (std::size_t i = 0; i < SpecialContentTypeList.size(); ++i) { + title_name_map.insert_or_assign(i + 1, GetContentTypeName(SpecialContentTypeList[i])); + title_icon_map.insert_or_assign(i + 1, GetContentTypeIcon(SpecialContentTypeList[i])); + } std::unordered_map title_row_map; for (const auto& [id, name] : title_name_map) { @@ -344,16 +366,12 @@ void ImportDialog::RepopulateContent() { row = title_row_map.at(real_id); break; } - case Core::ContentType::SystemArchive: { - row = title_row_map.at(1); // System archive - break; - } - case Core::ContentType::Sysdata: { - row = title_row_map.at(2); // System data - break; - } - case Core::ContentType::SystemTitle: { - row = title_row_map.at(3); // System title + default: { + const std::size_t idx = std::find(SpecialContentTypeList.begin(), + SpecialContentTypeList.end(), content.type) - + SpecialContentTypeList.begin(); + ASSERT_MSG(idx < SpecialContentTypeList.size(), "Content Type not handled"); + row = title_row_map.at(idx + 1); break; } } diff --git a/src/frontend/import_dialog.h b/src/frontend/import_dialog.h index 4785101..3684534 100644 --- a/src/frontend/import_dialog.h +++ b/src/frontend/import_dialog.h @@ -64,7 +64,9 @@ private: bool program_trigger = false; // Whether the System Archive / System Data warning has been shown - bool warning_shown = false; + bool system_warning_shown = false; + // Whether the Applets warning has been shown + bool applet_warning_shown = false; // TODO: Why this won't work as locals? Core::ContentSpecifier current_content = {};