diff --git a/src/core/importer.cpp b/src/core/importer.cpp index f019eb0..1d1451e 100644 --- a/src/core/importer.cpp +++ b/src/core/importer.cpp @@ -126,9 +126,7 @@ bool SDMCImporter::ImportContent(const ContentSpecifier& specifier, bool SDMCImporter::ImportContentImpl(const ContentSpecifier& specifier, const Common::ProgressCallback& callback) { switch (specifier.type) { - case ContentType::Application: - case ContentType::Update: - case ContentType::DLC: + case ContentType::Title: return ImportTitle(specifier, callback); case ContentType::Savegame: return ImportSavegame(specifier.id, callback); @@ -140,8 +138,7 @@ bool SDMCImporter::ImportContentImpl(const ContentSpecifier& specifier, return ImportNandExtdata(specifier.id, callback); case ContentType::Sysdata: return ImportSysdata(specifier.id, callback); - case ContentType::SystemTitle: - case ContentType::SystemApplet: + case ContentType::NandTitle: return ImportNandTitle(specifier, callback); default: UNREACHABLE(); @@ -454,7 +451,7 @@ std::shared_ptr SDMCImporter::OpenContent(const ContentSpecifi } else { // For DLCs, there one subfolder every 256 titles, but in practice hardcoded 00000000 // should be fine (also matches GodMode9 behaviour) - const auto format_str = specifier.type == ContentType::DLC + const auto format_str = (specifier.id >> 32) == 0x0004008c ? "/title/{:08x}/{:08x}/content/00000000/{:08x}.app" : "/title/{:08x}/{:08x}/content/{:08x}.app"; const auto path = @@ -565,8 +562,9 @@ static std::string GetTitleFileName(NCCHContainer& ncch) { bool SDMCImporter::DumpCXI(const ContentSpecifier& specifier, std::string destination, const Common::ProgressCallback& callback, bool auto_filename) { - if (specifier.type != ContentType::Application) { - LOG_ERROR(Core, "Unsupported specifier type {}", static_cast(specifier.type)); + // not an Application + if (specifier.type != ContentType::Title || (specifier.id >> 32) != 0x00040000) { + LOG_ERROR(Core, "Unsupported specifier (id={:016x})", specifier.id); return false; } @@ -767,13 +765,12 @@ bool SDMCImporter::CheckTitleContents(const ContentSpecifier& specifier, constexpr u64 TitleSizeAllowance = 0xA000; void SDMCImporter::ListTitle(std::vector& out) const { - const auto ProcessDirectory = [this, &out, &sdmc_path = config.sdmc_path](ContentType type, - u64 high_id) { + const auto ProcessDirectory = [this, &out, &sdmc_path = config.sdmc_path](u64 high_id) { FileUtil::ForeachDirectoryEntry( nullptr, fmt::format("{}title/{:08x}/", sdmc_path, high_id), - [this, &sdmc_path, type, high_id, &out](u64* /*num_entries_out*/, - const std::string& directory, - const std::string& virtual_name) { + [this, &sdmc_path, high_id, &out](u64* /*num_entries_out*/, + const std::string& directory, + const std::string& virtual_name) { if (!FileUtil::IsDirectory(directory + virtual_name + "/")) { return true; } @@ -792,8 +789,9 @@ void SDMCImporter::ListTitle(std::vector& out) const { if (FileUtil::Exists(directory + virtual_name + "/content/")) { do { TitleMetadata tmd; - if (!LoadTMD(type, id, tmd)) { - out.push_back({type, id, FileUtil::Exists(citra_path + "content/"), + if (!LoadTMD(ContentType::Title, id, tmd)) { + out.push_back({ContentType::Title, id, + FileUtil::Exists(citra_path + "content/"), FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/")}); break; @@ -806,7 +804,8 @@ void SDMCImporter::ListTitle(std::vector& out) const { std::make_shared(sdmc_path, boot_content_path, "rb")); if (!ncch.Load()) { LOG_WARNING(Core, "Could not load NCCH {}", boot_content_path); - out.push_back({type, id, FileUtil::Exists(citra_path + "content/"), + out.push_back({ContentType::Title, id, + FileUtil::Exists(citra_path + "content/"), FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/")}); break; @@ -816,12 +815,13 @@ void SDMCImporter::ListTitle(std::vector& out) const { const auto size = FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/") + TitleSizeAllowance; - out.push_back({type, id, FileUtil::Exists(citra_path + "content/"), size, - name, extdata_id, icon}); + out.push_back({ContentType::Title, id, + FileUtil::Exists(citra_path + "content/"), size, name, + extdata_id, icon}); } while (false); } - if (type != ContentType::Application) { + if (high_id != 0x00040000) { // Check savegame only for applications return true; } if (FileUtil::Exists(directory + virtual_name + "/data/")) { @@ -842,9 +842,9 @@ void SDMCImporter::ListTitle(std::vector& out) const { }); }; - ProcessDirectory(ContentType::Application, 0x00040000); - ProcessDirectory(ContentType::Update, 0x0004000e); - ProcessDirectory(ContentType::DLC, 0x0004008c); + ProcessDirectory(0x00040000); + ProcessDirectory(0x0004000e); + ProcessDirectory(0x0004008c); } // TODO: Simplify. @@ -872,8 +872,8 @@ void SDMCImporter::ListNandTitle(std::vector& out) const { if (FileUtil::Exists(content_path)) { do { TitleMetadata tmd; - if (!LoadTMD(ContentType::SystemTitle, id, tmd)) { - out.push_back({ContentType::SystemTitle, id, + if (!LoadTMD(ContentType::NandTitle, id, tmd)) { + out.push_back({ContentType::NandTitle, id, FileUtil::Exists(citra_path + "content/"), FileUtil::GetDirectoryTreeSize(content_path)}); break; @@ -889,13 +889,12 @@ void SDMCImporter::ListNandTitle(std::vector& out) const { } const auto& [name, extdata_id, icon] = LoadTitleData(ncch); - const auto type = (id >> 32) == 0x00040030 ? ContentType::SystemApplet - : ContentType::SystemTitle; const auto size = FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/") + TitleSizeAllowance; - out.push_back({type, id, FileUtil::Exists(citra_path + "content/"), size, - name, extdata_id, icon}); + out.push_back({ContentType::NandTitle, id, + FileUtil::Exists(citra_path + "content/"), size, name, + extdata_id, icon}); } while (false); } return true; @@ -1038,9 +1037,7 @@ void SDMCImporter::ListSysdata(std::vector& out) const { void SDMCImporter::DeleteContent(const ContentSpecifier& specifier) const { switch (specifier.type) { - case ContentType::Application: - case ContentType::Update: - case ContentType::DLC: + case ContentType::Title: return DeleteTitle(specifier.id); case ContentType::Savegame: return DeleteSavegame(specifier.id); @@ -1052,8 +1049,7 @@ void SDMCImporter::DeleteContent(const ContentSpecifier& specifier) const { return DeleteNandExtdata(specifier.id); case ContentType::Sysdata: return DeleteSysdata(specifier.id); - case ContentType::SystemTitle: - case ContentType::SystemApplet: + case ContentType::NandTitle: return DeleteNandTitle(specifier.id); default: UNREACHABLE(); diff --git a/src/core/importer.h b/src/core/importer.h index 441f1d1..79cbcec 100644 --- a/src/core/importer.h +++ b/src/core/importer.h @@ -26,25 +26,21 @@ class TitleMetadata; * Applications, updates and DLCs are all considered titles. */ enum class ContentType { - Application, - Update, - DLC, + Title, Savegame, NandSavegame, Extdata, NandExtdata, Sysdata, - SystemTitle, - SystemApplet, // This should belong to System Title, but they cause problems so a new category. + NandTitle, }; +constexpr std::size_t ContentTypeCount = 7; constexpr bool IsTitle(ContentType type) { - return type == ContentType::Application || type == ContentType::Update || - type == ContentType::DLC || type == ContentType::SystemTitle || - type == ContentType::SystemApplet; + return type == ContentType::Title || type == ContentType::NandTitle; } constexpr bool IsNandTitle(ContentType type) { - return type == ContentType::SystemTitle || type == ContentType::SystemApplet; + return type == ContentType::NandTitle; } /** diff --git a/src/frontend/import_dialog.cpp b/src/frontend/import_dialog.cpp index d46c92c..4ca385d 100644 --- a/src/frontend/import_dialog.cpp +++ b/src/frontend/import_dialog.cpp @@ -27,55 +27,126 @@ #include "frontend/title_info_dialog.h" #include "ui_import_dialog.h" -// content type, singular name, plural name, icon name +// Groups that are used in the frontend. This is organized in a slightly different way than Core. +enum class DisplayGroup { + Application, + Update, + DLC, + Savegame, + Extdata, + Sysdata, + SystemTitle, + SystemApplet, +}; + // clang-format off -static constexpr std::array, 10> - ContentTypeMap{{ - {Core::ContentType::Application, QT_TR_NOOP("Application"), QT_TR_NOOP("Applications"), "app"}, - {Core::ContentType::Update, QT_TR_NOOP("Update"), QT_TR_NOOP("Updates"), "update"}, - {Core::ContentType::DLC, QT_TR_NOOP("DLC"), QT_TR_NOOP("DLCs"), "dlc"}, - {Core::ContentType::Savegame, QT_TR_NOOP("Save Data"), QT_TR_NOOP("Save Data"), "save_data"}, - {Core::ContentType::NandSavegame, QT_TR_NOOP("System Save Data"), QT_TR_NOOP("System Save Data"), "save_data"}, - {Core::ContentType::Extdata, QT_TR_NOOP("Extra Data"), QT_TR_NOOP("Extra Data"), "save_data"}, - {Core::ContentType::NandExtdata, QT_TR_NOOP("System Extra Data"), QT_TR_NOOP("System Extra Data"), "save_data"}, - {Core::ContentType::Sysdata, QT_TR_NOOP("System Data"), QT_TR_NOOP("System Data"), "system_data"}, - {Core::ContentType::SystemTitle, QT_TR_NOOP("System Title"), QT_TR_NOOP("System Titles"), "hos"}, - {Core::ContentType::SystemApplet, QT_TR_NOOP("System Applet"), QT_TR_NOOP("System Applets"), "hos"}, +// group, singular name, plural name, icon name +constexpr std::array, 8> + DisplayGroupMap{{ + {DisplayGroup::Application, QT_TR_NOOP("Application"), QT_TR_NOOP("Applications"), "app"}, + {DisplayGroup::Update, QT_TR_NOOP("Update"), QT_TR_NOOP("Updates"), "update"}, + {DisplayGroup::DLC, QT_TR_NOOP("DLC"), QT_TR_NOOP("DLCs"), "dlc"}, + {DisplayGroup::Savegame, QT_TR_NOOP("Save Data"), QT_TR_NOOP("Save Data"), "save_data"}, + {DisplayGroup::Extdata, QT_TR_NOOP("Extra Data"), QT_TR_NOOP("Extra Data"), "save_data"}, + {DisplayGroup::Sysdata, QT_TR_NOOP("System Data"), QT_TR_NOOP("System Data"), "system_data"}, + {DisplayGroup::SystemTitle, QT_TR_NOOP("System Title"), QT_TR_NOOP("System Titles"), "hos"}, + {DisplayGroup::SystemApplet, QT_TR_NOOP("System Applet"), QT_TR_NOOP("System Applets"), "hos"}, }}; // clang-format on +static DisplayGroup GetDisplayGroup(const Core::ContentSpecifier& specifier) { + if (specifier.type == Core::ContentType::Title) { + switch (specifier.id >> 32) { + case 0x00040000: + return DisplayGroup::Application; + case 0x0004000e: + return DisplayGroup::Update; + case 0x0004008c: + return DisplayGroup::DLC; + default: + UNREACHABLE(); + } + } + if (specifier.type == Core::ContentType::Savegame) { + return DisplayGroup::Savegame; + } + if (specifier.type == Core::ContentType::Extdata) { + return DisplayGroup::Extdata; + } + if (specifier.type == Core::ContentType::NandSavegame || + specifier.type == Core::ContentType::NandExtdata || + specifier.type == Core::ContentType::Sysdata) { // These are grouped together + + return DisplayGroup::Sysdata; + } + if (specifier.type == Core::ContentType::NandTitle) { + return (specifier.id >> 32) == 0x00040030 ? DisplayGroup::SystemApplet + : DisplayGroup::SystemTitle; + } + UNREACHABLE(); +} + static QString GetContentName(const Core::ContentSpecifier& specifier) { + if (specifier.type == Core::ContentType::NandSavegame) { + return QObject::tr("System Save 0x%1", "ImportDialog") + .arg(specifier.id, 16, 16, QLatin1Char('0')); + } + if (specifier.type == Core::ContentType::NandExtdata) { + return QObject::tr("System Extra 0x%1", "ImportDialog") + .arg(specifier.id, 16, 16, QLatin1Char('0')); + } return specifier.name.empty() ? QStringLiteral("0x%1").arg(specifier.id, 16, 16, QLatin1Char('0')) : QString::fromStdString(specifier.name); } template -static QString GetContentTypeName(Core::ContentType type) { +static QString GetDisplayGroupName(DisplayGroup group) { if constexpr (Plural) { - return QObject::tr(std::get<2>(ContentTypeMap.at(static_cast(type))), + return QObject::tr(std::get<2>(DisplayGroupMap.at(static_cast(group))), "ImportDialog"); } else { - return QObject::tr(std::get<1>(ContentTypeMap.at(static_cast(type))), + return QObject::tr(std::get<1>(DisplayGroupMap.at(static_cast(group))), "ImportDialog"); } } -static QPixmap GetContentTypeIcon(Core::ContentType type) { +template +static QString GetDisplayGroupName(const Core::ContentSpecifier& specifier) { + return GetDisplayGroupName(GetDisplayGroup(specifier)); +} + +static QPixmap GetDisplayGroupIcon(DisplayGroup group) { return QIcon::fromTheme( - QString::fromUtf8(std::get<3>(ContentTypeMap.at(static_cast(type))))) + QString::fromUtf8(std::get<3>(DisplayGroupMap.at(static_cast(group))))) .pixmap(24); } -static QPixmap GetContentIcon(const Core::ContentSpecifier& specifier, - bool use_category_icon = false) { - if (specifier.icon.empty()) { - // Return a category icon, or a null icon - return use_category_icon ? GetContentTypeIcon(specifier.type) - : QIcon::fromTheme(QStringLiteral("unknown")).pixmap(24); +static QPixmap GetContentIcon(const Core::ContentSpecifier& specifier) { + if (!specifier.icon.empty()) { + return QPixmap::fromImage(QImage(reinterpret_cast(specifier.icon.data()), 24, + 24, QImage::Format::Format_RGB16)); } - return QPixmap::fromImage(QImage(reinterpret_cast(specifier.icon.data()), 24, 24, - QImage::Format::Format_RGB16)); + + // Use a category icon to distinguish between different types of System Data + if (specifier.type == Core::ContentType::NandSavegame || + specifier.type == Core::ContentType::NandExtdata) { + return GetDisplayGroupIcon(DisplayGroup::Savegame); + } + if (specifier.type == Core::ContentType::Sysdata) { + return GetDisplayGroupIcon(DisplayGroup::Sysdata); + } + + // Use a special icon for NAND non-executable archives + if (specifier.type == Core::ContentType::NandTitle) { + const auto id_high = specifier.id >> 32; + if (id_high == 0x0004001b || id_high == 0x0004009b || id_high == 0x000400db) { + return QIcon::fromTheme(QStringLiteral("system_archive")).pixmap(24); + } + } + + // Return a null icon otherwise + return QIcon::fromTheme(QStringLiteral("unknown")).pixmap(24); } ImportDialog::ImportDialog(QWidget* parent, const Core::Config& config_) @@ -214,29 +285,28 @@ void ImportDialog::InsertTopLevelItem(QString text, QPixmap icon, u64 total_size } // Content types that themselves form a 'Title' like entity. -constexpr std::array SpecialContentTypeList{{ - Core::ContentType::NandSavegame, - Core::ContentType::NandExtdata, - Core::ContentType::Sysdata, - Core::ContentType::SystemTitle, - Core::ContentType::SystemApplet, +constexpr std::array SpecialDisplayGroupList{{ + DisplayGroup::Sysdata, + DisplayGroup::SystemTitle, + DisplayGroup::SystemApplet, }}; void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpecifier& content, std::size_t id, QString replace_name, QPixmap replace_icon) { const bool use_title_view = ui->title_view_button->isChecked(); + const auto group = GetDisplayGroup(content); QString name; if (use_title_view) { if (row == 0) { name = QStringLiteral("%1 (%2)") .arg(GetContentName(content)) - .arg(GetContentTypeName(content.type)); - } else if (row <= SpecialContentTypeList.size()) { + .arg(GetDisplayGroupName(group)); + } else if (row <= SpecialDisplayGroupList.size()) { name = GetContentName(content); } else { - name = GetContentTypeName(content.type); + name = GetDisplayGroupName(group); } } else { name = GetContentName(content); @@ -252,14 +322,13 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe // Set icon 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 && - content.type != Core::ContentType::SystemApplet) { - icon = GetContentTypeIcon(content.type); + const bool in_special_group = + std::find(SpecialDisplayGroupList.begin(), SpecialDisplayGroupList.end(), group) != + SpecialDisplayGroupList.end(); + if (use_title_view && !in_special_group) { + icon = GetDisplayGroupIcon(group); } else { - // When not in title view, System Data groups use category icons. - const bool use_category_icon = content.type == Core::ContentType::Sysdata; - icon = GetContentIcon(content, use_category_icon); + icon = GetContentIcon(content); } } else { icon = replace_icon; @@ -267,7 +336,7 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe item->setIcon(0, QIcon(icon)); // Skip System Applets, but enable everything else by default. - if (!content.already_exists && content.type != Core::ContentType::SystemApplet) { + if (!content.already_exists && group != DisplayGroup::SystemApplet) { item->setCheckState(0, Qt::Checked); total_selected_size += content.maximum_size; } else { @@ -284,9 +353,10 @@ void ImportDialog::OnItemChanged(QTreeWidgetItem* item, int column) { } const auto& specifier = SpecifierFromItem(item); + const auto group = GetDisplayGroup(specifier); if (item->checkState(0) == Qt::Checked) { if (!applet_warning_shown && !specifier.already_exists && - specifier.type == Core::ContentType::SystemApplet) { + group == DisplayGroup::SystemApplet) { QMessageBox::warning( this, tr("Warning"), @@ -297,8 +367,7 @@ void ImportDialog::OnItemChanged(QTreeWidgetItem* item, int column) { total_selected_size += specifier.maximum_size; } else { if (!system_warning_shown && !specifier.already_exists && - (specifier.type == Core::ContentType::Sysdata || - specifier.type == Core::ContentType::SystemTitle)) { + (group == DisplayGroup::Sysdata || group == DisplayGroup::SystemTitle)) { QMessageBox::warning(this, tr("Warning"), tr("You are de-selecting important files that may be necessary " @@ -331,7 +400,8 @@ void ImportDialog::RepopulateContent() { std::map title_map; std::unordered_map extdata_id_map; // extdata ID -> title ID for (const auto& content : contents) { - if (content.type == Core::ContentType::Application) { + // Applications + if (content.type == Core::ContentType::Title && (content.id >> 32) == 0x00040000) { title_map[content.id].name = GetContentName(content); title_map[content.id].icon = GetContentIcon(content); extdata_id_map.emplace(content.extdata_id, content.id); @@ -343,9 +413,7 @@ void ImportDialog::RepopulateContent() { const u64 title_id = extdata_id_map.at(content.id); title_map[title_id].contents.emplace_back(&content); } - } else if (content.type == Core::ContentType::Application || - content.type == Core::ContentType::Update || - content.type == Core::ContentType::DLC || + } else if (content.type == Core::ContentType::Title || content.type == Core::ContentType::Savegame) { if (title_map.count(content.id)) { title_map[content.id].contents.emplace_back(&content); @@ -358,16 +426,16 @@ void ImportDialog::RepopulateContent() { // Create 'Ungrouped' category. InsertTopLevelItem(tr("Ungrouped"), QIcon::fromTheme(QStringLiteral("unknown")).pixmap(24)); - // Create categories for special content types. - for (std::size_t i = 0; i < SpecialContentTypeList.size(); ++i) { - InsertTopLevelItem(GetContentTypeName(SpecialContentTypeList[i]), - GetContentTypeIcon(SpecialContentTypeList[i])); + // Create categories for special display groups. + for (std::size_t i = 0; i < SpecialDisplayGroupList.size(); ++i) { + InsertTopLevelItem(GetDisplayGroupName(SpecialDisplayGroupList[i]), + GetDisplayGroupIcon(SpecialDisplayGroupList[i])); } - // Titles + // Applications std::unordered_map title_row_map; for (auto& [id, entry] : title_map) { - // Process the title's contents + // Process the application's contents u64 total_size = 0; bool has_exist = false, has_non_exist = false; for (const auto* content : entry.contents) { @@ -398,9 +466,7 @@ void ImportDialog::RepopulateContent() { std::size_t row = 0; // 0 for ungrouped (default) switch (content.type) { - case Core::ContentType::Application: - case Core::ContentType::Update: - case Core::ContentType::DLC: + case Core::ContentType::Title: case Core::ContentType::Savegame: { // Fix the id const auto real_id = content.id & 0xffffff00ffffffff; @@ -416,10 +482,11 @@ void ImportDialog::RepopulateContent() { break; } 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"); + const std::size_t idx = + std::find(SpecialDisplayGroupList.begin(), SpecialDisplayGroupList.end(), + GetDisplayGroup(content)) - + SpecialDisplayGroupList.begin(); + ASSERT_MSG(idx < SpecialDisplayGroupList.size(), "Display Group not handled"); row = idx + 1; break; } @@ -428,8 +495,8 @@ void ImportDialog::RepopulateContent() { InsertSecondLevelItem(row, content, i); } } else { - for (const auto& [type, singular_name, plural_name, icon_name] : ContentTypeMap) { - InsertTopLevelItem(tr(plural_name), GetContentTypeIcon(type)); + for (const auto& [group, singular_name, plural_name, icon_name] : DisplayGroupMap) { + InsertTopLevelItem(tr(plural_name), GetDisplayGroupIcon(group)); } for (std::size_t i = 0; i < contents.size(); ++i) { @@ -450,7 +517,8 @@ void ImportDialog::RepopulateContent() { } } - InsertSecondLevelItem(static_cast(content.type), content, i, name, icon); + InsertSecondLevelItem(static_cast(GetDisplayGroup(content)), content, i, + name, icon); } } @@ -509,7 +577,8 @@ void ImportDialog::OnContextMenu(const QPoint& point) { QMenu context_menu(this); if (item->parent()) { // Second level const auto& specifier = SpecifierFromItem(item); - if (specifier.type == Core::ContentType::Application) { + const auto group = GetDisplayGroup(specifier); + if (group == DisplayGroup::Application) { context_menu.addAction(tr("Dump CXI file"), [this, specifier] { StartDumpingCXISingle(specifier); }); } @@ -528,15 +597,16 @@ void ImportDialog::OnContextMenu(const QPoint& point) { for (int i = 0; i < item->childCount(); ++i) { const auto& specifier = SpecifierFromItem(item->child(i)); - if (specifier.type == Core::ContentType::Application) { + const auto group = GetDisplayGroup(specifier); + if (group == DisplayGroup::Application) { context_menu.addAction(tr("Dump Base CXI file"), [this, specifier] { StartDumpingCXISingle(specifier); }); context_menu.addAction(tr("Build Base CIA"), [this, specifier] { StartBuildingCIASingle(specifier); }); - } else if (specifier.type == Core::ContentType::Update) { + } else if (group == DisplayGroup::Update) { context_menu.addAction(tr("Build Update CIA"), [this, specifier] { StartBuildingCIASingle(specifier); }); - } else if (specifier.type == Core::ContentType::DLC) { + } else if (group == DisplayGroup::DLC) { context_menu.addAction(tr("Build DLC CIA"), [this, specifier] { StartBuildingCIASingle(specifier); }); } @@ -613,7 +683,7 @@ void ImportDialog::RunMultiJob(MultiJob* job, std::size_t total_count, u64 total .arg(count) .arg(total_count) .arg(GetContentName(next_content)) - .arg(GetContentTypeName(next_content.type)) + .arg(GetDisplayGroupName(next_content)) .arg(FormatETA(eta))); current_content = next_content; current_count = count; @@ -631,7 +701,7 @@ void ImportDialog::RunMultiJob(MultiJob* job, std::size_t total_count, u64 total .arg(current_count) .arg(total_count) .arg(GetContentName(current_content)) - .arg(GetContentTypeName(current_content.type)) + .arg(GetDisplayGroupName(current_content)) .arg(ReadableByteSize(current_imported_size)) .arg(ReadableByteSize(current_content.maximum_size)) .arg(FormatETA(eta))); @@ -647,7 +717,7 @@ void ImportDialog::RunMultiJob(MultiJob* job, std::size_t total_count, u64 total for (const auto& content : failed_contents) { list_content.append(QStringLiteral("
  • %1 (%2)
  • ") .arg(GetContentName(content)) - .arg(GetContentTypeName(content.type))); + .arg(GetDisplayGroupName(content))); } QMessageBox::critical(this, tr("threeSD"), tr("List of failed contents:
      %1
    ").arg(list_content)); @@ -715,7 +785,7 @@ void ImportDialog::StartBatchDumpingCXI() { const auto removed_iter = std::remove_if( to_import.begin(), to_import.end(), [](const Core::ContentSpecifier& specifier) { - return specifier.type != Core::ContentType::Application; + return specifier.type != Core::ContentType::Title || (specifier.id >> 32) != 0x00040000; }); if (removed_iter == to_import.begin()) { // No Applications selected QMessageBox::critical(this, tr("threeSD"),