Display title icon; display title icon and names for save/extdata in group mode

This commit is contained in:
zhupengfei
2020-01-17 12:10:44 +08:00
parent 0708744a17
commit 94b9ec028b
4 changed files with 62 additions and 22 deletions
+4 -4
View File
@@ -296,7 +296,7 @@ std::vector<ContentSpecifier> SDMCImporter::ListContent() const {
// Regex for half Title IDs // Regex for half Title IDs
static const std::regex title_regex{"[0-9a-f]{8}"}; static const std::regex title_regex{"[0-9a-f]{8}"};
std::tuple<std::string, u64, EncryptionType, bool> SDMCImporter::LoadTitleData( std::tuple<std::string, u64, EncryptionType, bool, std::vector<u16>> SDMCImporter::LoadTitleData(
const std::string& path) const { const std::string& path) const {
// Remove trailing '/' // Remove trailing '/'
const auto sdmc_path = config.sdmc_path.substr(0, config.sdmc_path.size() - 1); const auto sdmc_path = config.sdmc_path.substr(0, config.sdmc_path.size() - 1);
@@ -364,7 +364,7 @@ std::tuple<std::string, u64, EncryptionType, bool> SDMCImporter::LoadTitleData(
bool seed_crypto{}; bool seed_crypto{};
ncch.ReadSeedCrypto(seed_crypto); ncch.ReadSeedCrypto(seed_crypto);
return {Common::UTF16BufferToUTF8(smdh.GetShortTitle(SMDH::TitleLanguage::English)), extdata_id, return {Common::UTF16BufferToUTF8(smdh.GetShortTitle(SMDH::TitleLanguage::English)), extdata_id,
encryption, seed_crypto}; encryption, seed_crypto, smdh.GetIcon(false)};
} }
void SDMCImporter::ListTitle(std::vector<ContentSpecifier>& out) const { void SDMCImporter::ListTitle(std::vector<ContentSpecifier>& out) const {
@@ -392,12 +392,12 @@ void SDMCImporter::ListTitle(std::vector<ContentSpecifier>& out) const {
if (FileUtil::Exists(directory + virtual_name + "/content/")) { if (FileUtil::Exists(directory + virtual_name + "/content/")) {
const auto content_path = const auto content_path =
fmt::format("/title/{:08x}/{}/content/", high_id, virtual_name); fmt::format("/title/{:08x}/{}/content/", high_id, virtual_name);
const auto& [name, extdata_id, encryption, seed_crypto] = const auto& [name, extdata_id, encryption, seed_crypto, icon] =
LoadTitleData(content_path); LoadTitleData(content_path);
out.push_back( out.push_back(
{type, id, FileUtil::Exists(citra_path + "content/"), {type, id, FileUtil::Exists(citra_path + "content/"),
FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/"), FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/"),
name, extdata_id, encryption, seed_crypto}); name, extdata_id, encryption, seed_crypto, icon});
} }
if (type != ContentType::Application) { if (type != ContentType::Application) {
+4 -3
View File
@@ -51,6 +51,7 @@ struct ContentSpecifier {
u64 extdata_id; ///< Extdata ID for Applications. u64 extdata_id; ///< Extdata ID for Applications.
EncryptionType encryption = EncryptionType::None; ///< Only for NCCHs. Encryption scheme. EncryptionType encryption = EncryptionType::None; ///< Only for NCCHs. Encryption scheme.
bool seed_crypto = false; ///< Only for NCCHs. Whether seed crypto is used. bool seed_crypto = false; ///< Only for NCCHs. Whether seed crypto is used.
std::vector<u16> icon; ///< Optional. The content's icon.
}; };
/** /**
@@ -140,12 +141,12 @@ private:
void DeleteSysdata(u64 id) const; void DeleteSysdata(u64 id) const;
/** /**
* Loads the English short title name, extdata id and encryption of a title. * Loads the English short title name, extdata id, encryption and icon of a title.
* @param path Path of the 'content' folder relative to the SDMC root folder. * @param path Path of the 'content' folder relative to the SDMC root folder.
* Required to end with '/'. * Required to end with '/'.
* @return {name, extdata_id, encryption, seed_crypto} * @return {name, extdata_id, encryption, seed_crypto, icon}
*/ */
std::tuple<std::string, u64, EncryptionType, bool> LoadTitleData(const std::string& path) const; std::tuple<std::string, u64, EncryptionType, bool, std::vector<u16>> LoadTitleData(const std::string& path) const;
bool is_good{}; bool is_good{};
Config config; Config config;
+49 -13
View File
@@ -60,6 +60,11 @@ QString GetContentTypeName(Core::ContentType type) {
return QObject::tr(ContentTypeMap.at(static_cast<std::size_t>(type)).second, "ImportDialog"); return QObject::tr(ContentTypeMap.at(static_cast<std::size_t>(type)).second, "ImportDialog");
} }
QPixmap GetContentIcon(const Core::ContentSpecifier& specifier) {
return QPixmap::fromImage(QImage(reinterpret_cast<const uchar*>(specifier.icon.data()), 24, 24,
QImage::Format::Format_RGB16));
}
ImportDialog::ImportDialog(QWidget* parent, const Core::Config& config) ImportDialog::ImportDialog(QWidget* parent, const Core::Config& config)
: QDialog(parent), ui(std::make_unique<Ui::ImportDialog>()), user_path(config.user_path), : QDialog(parent), ui(std::make_unique<Ui::ImportDialog>()), user_path(config.user_path),
importer(config) { importer(config) {
@@ -124,10 +129,13 @@ void ImportDialog::RelistContent() {
future_watcher->setFuture(future); future_watcher->setFuture(future);
} }
void ImportDialog::InsertTopLevelItem(const QString& text) { void ImportDialog::InsertTopLevelItem(const QString& text, QPixmap icon) {
auto* checkBox = new QCheckBox(); auto* checkBox = new QCheckBox();
checkBox->setText(text); checkBox->setText(text);
checkBox->setStyleSheet(QStringLiteral("margin-left:7px")); if (!icon.isNull()) {
checkBox->setIcon(QIcon(icon));
}
checkBox->setStyleSheet(QStringLiteral("margin-left: 7px; icon-size: 24px"));
checkBox->setTristate(true); checkBox->setTristate(true);
checkBox->setProperty("previousState", static_cast<int>(Qt::Unchecked)); checkBox->setProperty("previousState", static_cast<int>(Qt::Unchecked));
@@ -164,7 +172,8 @@ void ImportDialog::InsertTopLevelItem(const QString& text) {
} }
void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpecifier& content, void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpecifier& content,
std::size_t id) { std::size_t id, QString replace_name,
QPixmap replace_icon) {
auto* checkBox = new QCheckBox(); auto* checkBox = new QCheckBox();
checkBox->setStyleSheet(QStringLiteral("margin-left:7px")); checkBox->setStyleSheet(QStringLiteral("margin-left:7px"));
// HACK: The checkbox is used to record ID. Is there a better way? // HACK: The checkbox is used to record ID. Is there a better way?
@@ -185,6 +194,10 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe
name = GetContentName(content); name = GetContentName(content);
} }
if (!replace_name.isEmpty()) {
name = replace_name;
}
QString encryption = tr(EncryptionTypeMap.at(content.encryption)); QString encryption = tr(EncryptionTypeMap.at(content.encryption));
if (content.seed_crypto) { if (content.seed_crypto) {
encryption.append(tr(" (Seed)")); encryption.append(tr(" (Seed)"));
@@ -201,6 +214,12 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe
{QString{}, name, ReadableByteSize(content.maximum_size), encryption, {QString{}, name, ReadableByteSize(content.maximum_size), encryption,
content.already_exists ? QStringLiteral("Yes") : QStringLiteral("No")}}; content.already_exists ? QStringLiteral("Yes") : QStringLiteral("No")}};
if (!ui->title_view_button->isChecked()) {
// Display icon when present
item->setData(1, Qt::DecorationRole,
replace_icon.isNull() ? GetContentIcon(content) : replace_icon);
}
ui->main->invisibleRootItem()->child(row)->addChild(item); ui->main->invisibleRootItem()->child(row)->addChild(item);
ui->main->setItemWidget(item, 0, checkBox); ui->main->setItemWidget(item, 0, checkBox);
@@ -240,23 +259,26 @@ void ImportDialog::RepopulateContent() {
ui->main->clear(); ui->main->clear();
ui->main->setSortingEnabled(false); ui->main->setSortingEnabled(false);
std::map<u64, QString> title_name_map; // title ID -> title name
std::map<u64, QPixmap> title_icon_map; // title ID -> title icon
std::unordered_map<u64, u64> extdata_id_map; // extdata ID -> title ID
for (const auto& content : contents) {
if (content.type == Core::ContentType::Application) {
title_name_map.emplace(content.id, GetContentName(content));
title_icon_map.emplace(content.id, GetContentIcon(content));
extdata_id_map.emplace(content.extdata_id, content.id);
}
}
const bool use_title_view = ui->title_view_button->isChecked(); const bool use_title_view = ui->title_view_button->isChecked();
if (use_title_view) { if (use_title_view) {
std::map<u64, QString> title_name_map; // title ID -> title name
std::unordered_map<u64, u64> extdata_id_map; // extdata ID -> title ID
for (const auto& content : contents) {
if (content.type == Core::ContentType::Application) {
title_name_map.emplace(content.id, GetContentName(content));
extdata_id_map.emplace(content.extdata_id, content.id);
}
}
title_name_map.insert_or_assign(0, tr("Ungrouped")); 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(1, tr("System Archive"));
title_name_map.insert_or_assign(2, tr("System Data")); title_name_map.insert_or_assign(2, tr("System Data"));
std::unordered_map<u64, u64> title_row_map; std::unordered_map<u64, u64> title_row_map;
for (const auto& [id, name] : title_name_map) { for (const auto& [id, name] : title_name_map) {
InsertTopLevelItem(name); InsertTopLevelItem(name, title_icon_map.count(id) ? title_icon_map.at(id) : QPixmap{});
title_row_map[id] = ui->main->invisibleRootItem()->childCount() - 1; title_row_map[id] = ui->main->invisibleRootItem()->childCount() - 1;
} }
@@ -300,7 +322,21 @@ void ImportDialog::RepopulateContent() {
for (std::size_t i = 0; i < contents.size(); ++i) { for (std::size_t i = 0; i < contents.size(); ++i) {
const auto& content = contents[i]; const auto& content = contents[i];
InsertSecondLevelItem(static_cast<std::size_t>(content.type), content, i);
QString name;
QPixmap icon;
if (content.type == Core::ContentType::Savegame) {
name = title_name_map.count(content.id) ? title_name_map.at(content.id) : QString{};
icon = title_icon_map.count(content.id) ? title_icon_map.at(content.id) : QPixmap{};
} else if (content.type == Core::ContentType::Extdata) {
if (extdata_id_map.count(content.id)) {
u64 title_id = extdata_id_map.at(content.id);
name = title_name_map.count(title_id) ? title_name_map.at(title_id) : QString{};
icon = title_icon_map.count(title_id) ? title_icon_map.at(title_id) : QPixmap{};
}
}
InsertSecondLevelItem(static_cast<std::size_t>(content.type), content, i, name, icon);
} }
} }
+5 -2
View File
@@ -8,6 +8,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <QDialog> #include <QDialog>
#include <QPixmap>
#include "core/importer.h" #include "core/importer.h"
class QTreeWidgetItem; class QTreeWidgetItem;
@@ -31,9 +32,11 @@ private:
std::vector<Core::ContentSpecifier> GetSelectedContentList(); std::vector<Core::ContentSpecifier> GetSelectedContentList();
void StartImporting(); void StartImporting();
void InsertTopLevelItem(const QString& text); void InsertTopLevelItem(const QString& text, QPixmap icon = {});
// When replace_name and replace_icon are present they are used instead of those in `content`.
void InsertSecondLevelItem(std::size_t row, const Core::ContentSpecifier& content, void InsertSecondLevelItem(std::size_t row, const Core::ContentSpecifier& content,
std::size_t id); std::size_t id, QString replace_name = {},
QPixmap replace_icon = {});
std::unique_ptr<Ui::ImportDialog> ui; std::unique_ptr<Ui::ImportDialog> ui;