mirror of
https://github.com/Dark98/threeSD.git
synced 2026-07-03 00:38:58 +00:00
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.
This commit is contained in:
@@ -84,6 +84,7 @@ bool SDMCImporter::ImportContent(const ContentSpecifier& specifier,
|
|||||||
case ContentType::Sysdata:
|
case ContentType::Sysdata:
|
||||||
return ImportSysdata(specifier.id, callback);
|
return ImportSysdata(specifier.id, callback);
|
||||||
case ContentType::SystemTitle:
|
case ContentType::SystemTitle:
|
||||||
|
case ContentType::SystemApplet:
|
||||||
return ImportNandTitle(specifier, callback);
|
return ImportNandTitle(specifier, callback);
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
@@ -720,9 +721,10 @@ void SDMCImporter::ListNandTitle(std::vector<ContentSpecifier>& out) const {
|
|||||||
|
|
||||||
const auto& [name, extdata_id, encryption, seed_crypto, icon] =
|
const auto& [name, extdata_id, encryption, seed_crypto, icon] =
|
||||||
LoadTitleData(ncch);
|
LoadTitleData(ncch);
|
||||||
|
const auto type = (id >> 32) == 0x00040030 ? ContentType::SystemApplet
|
||||||
|
: ContentType::SystemTitle;
|
||||||
out.push_back(
|
out.push_back(
|
||||||
{ContentType::SystemTitle, id,
|
{type, id, FileUtil::Exists(citra_path + "content/"),
|
||||||
FileUtil::Exists(citra_path + "content/"),
|
|
||||||
FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/"),
|
FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/"),
|
||||||
name, extdata_id, encryption, seed_crypto, icon});
|
name, extdata_id, encryption, seed_crypto, icon});
|
||||||
} while (false);
|
} while (false);
|
||||||
@@ -883,6 +885,7 @@ void SDMCImporter::DeleteContent(const ContentSpecifier& specifier) {
|
|||||||
case ContentType::Sysdata:
|
case ContentType::Sysdata:
|
||||||
return DeleteSysdata(specifier.id);
|
return DeleteSysdata(specifier.id);
|
||||||
case ContentType::SystemTitle:
|
case ContentType::SystemTitle:
|
||||||
|
case ContentType::SystemApplet:
|
||||||
return DeleteNandTitle(specifier.id);
|
return DeleteNandTitle(specifier.id);
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
|||||||
+10
-6
@@ -28,6 +28,7 @@ enum class ContentType {
|
|||||||
SystemArchive,
|
SystemArchive,
|
||||||
Sysdata,
|
Sysdata,
|
||||||
SystemTitle,
|
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.
|
* Blocks, but can be aborted on another thread if needed.
|
||||||
* @return true on success, false otherwise
|
* @return true on success, false otherwise
|
||||||
*/
|
*/
|
||||||
bool ImportContent(const ContentSpecifier& specifier,
|
bool ImportContent(
|
||||||
const ProgressCallback& callback = [](std::size_t, std::size_t) {});
|
const ContentSpecifier& specifier,
|
||||||
|
const ProgressCallback& callback = [](std::size_t, std::size_t) {});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aborts current importing.
|
* Aborts current importing.
|
||||||
@@ -123,8 +125,9 @@ public:
|
|||||||
* Blocks, but can be aborted on another thread.
|
* Blocks, but can be aborted on another thread.
|
||||||
* @return true on success, false otherwise
|
* @return true on success, false otherwise
|
||||||
*/
|
*/
|
||||||
bool DumpCXI(const ContentSpecifier& specifier, const std::string& destination,
|
bool DumpCXI(
|
||||||
const ProgressCallback& callback = [](std::size_t, std::size_t) {});
|
const ContentSpecifier& specifier, const std::string& destination,
|
||||||
|
const ProgressCallback& callback = [](std::size_t, std::size_t) {});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aborts current CXI dumping.
|
* Aborts current CXI dumping.
|
||||||
@@ -136,8 +139,9 @@ public:
|
|||||||
* Blocks, but can be aborted on another thread.
|
* Blocks, but can be aborted on another thread.
|
||||||
* @return true on success, false otherwise
|
* @return true on success, false otherwise
|
||||||
*/
|
*/
|
||||||
bool BuildCIA(const ContentSpecifier& specifier, const std::string& destination,
|
bool BuildCIA(
|
||||||
const ProgressCallback& callback = [](std::size_t, std::size_t) {});
|
const ContentSpecifier& specifier, const std::string& destination,
|
||||||
|
const ProgressCallback& callback = [](std::size_t, std::size_t) {});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aborts current CIA building
|
* Aborts current CIA building
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QStorageInfo>
|
#include <QStorageInfo>
|
||||||
#include <QtConcurrent/QtConcurrentRun>
|
#include <QtConcurrent/QtConcurrentRun>
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "frontend/helpers/import_job.h"
|
#include "frontend/helpers/import_job.h"
|
||||||
@@ -36,7 +37,7 @@ QString ReadableByteSize(qulonglong size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// content type, name, icon name
|
// content type, name, icon name
|
||||||
static constexpr std::array<std::tuple<Core::ContentType, const char*, const char*>, 8>
|
static constexpr std::array<std::tuple<Core::ContentType, const char*, const char*>, 9>
|
||||||
ContentTypeMap{{
|
ContentTypeMap{{
|
||||||
{Core::ContentType::Application, QT_TR_NOOP("Application"), "app"},
|
{Core::ContentType::Application, QT_TR_NOOP("Application"), "app"},
|
||||||
{Core::ContentType::Update, QT_TR_NOOP("Update"), "update"},
|
{Core::ContentType::Update, QT_TR_NOOP("Update"), "update"},
|
||||||
@@ -46,6 +47,7 @@ static constexpr std::array<std::tuple<Core::ContentType, const char*, const cha
|
|||||||
{Core::ContentType::SystemArchive, QT_TR_NOOP("System Archive"), "system_archive"},
|
{Core::ContentType::SystemArchive, QT_TR_NOOP("System Archive"), "system_archive"},
|
||||||
{Core::ContentType::Sysdata, QT_TR_NOOP("System Data"), "system_data"},
|
{Core::ContentType::Sysdata, QT_TR_NOOP("System Data"), "system_data"},
|
||||||
{Core::ContentType::SystemTitle, QT_TR_NOOP("System Title"), "hos"},
|
{Core::ContentType::SystemTitle, QT_TR_NOOP("System Title"), "hos"},
|
||||||
|
{Core::ContentType::SystemApplet, QT_TR_NOOP("System Applet"), "hos"},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
static const std::unordered_map<Core::EncryptionType, const char*> EncryptionTypeMap{{
|
static const std::unordered_map<Core::EncryptionType, const char*> EncryptionTypeMap{{
|
||||||
@@ -144,7 +146,7 @@ void ImportDialog::RelistContent() {
|
|||||||
RepopulateContent();
|
RepopulateContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
auto future = QtConcurrent::run([& contents = this->contents, &importer = this->importer] {
|
auto future = QtConcurrent::run([&contents = this->contents, &importer = this->importer] {
|
||||||
contents = importer.ListContent();
|
contents = importer.ListContent();
|
||||||
});
|
});
|
||||||
future_watcher->setFuture(future);
|
future_watcher->setFuture(future);
|
||||||
@@ -192,6 +194,14 @@ void ImportDialog::InsertTopLevelItem(const QString& text, QPixmap icon) {
|
|||||||
item->setFirstColumnSpanned(true);
|
item->setFirstColumnSpanned(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Content types that themselves form a 'Title' like entity.
|
||||||
|
constexpr std::array<Core::ContentType, 4> SpecialContentTypeList{{
|
||||||
|
Core::ContentType::SystemArchive,
|
||||||
|
Core::ContentType::Sysdata,
|
||||||
|
Core::ContentType::SystemTitle,
|
||||||
|
Core::ContentType::SystemApplet,
|
||||||
|
}};
|
||||||
|
|
||||||
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, QString replace_name,
|
std::size_t id, QString replace_name,
|
||||||
QPixmap replace_icon) {
|
QPixmap replace_icon) {
|
||||||
@@ -208,7 +218,7 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe
|
|||||||
name = QStringLiteral("%1 (%2)")
|
name = QStringLiteral("%1 (%2)")
|
||||||
.arg(GetContentName(content))
|
.arg(GetContentName(content))
|
||||||
.arg(GetContentTypeName(content.type));
|
.arg(GetContentTypeName(content.type));
|
||||||
} else if (row <= 3) {
|
} else if (row <= SpecialContentTypeList.size()) {
|
||||||
name = GetContentName(content);
|
name = GetContentName(content);
|
||||||
} else {
|
} else {
|
||||||
name = GetContentTypeName(content.type);
|
name = GetContentTypeName(content.type);
|
||||||
@@ -228,7 +238,8 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe
|
|||||||
|
|
||||||
if (content.type != Core::ContentType::Application &&
|
if (content.type != Core::ContentType::Application &&
|
||||||
content.type != Core::ContentType::Update && content.type != Core::ContentType::DLC &&
|
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
|
// Do not display encryption in this case
|
||||||
encryption.clear();
|
encryption.clear();
|
||||||
@@ -241,7 +252,8 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe
|
|||||||
QPixmap icon;
|
QPixmap icon;
|
||||||
if (replace_icon.isNull()) {
|
if (replace_icon.isNull()) {
|
||||||
// Exclude system titles, they are a single group but have own icons.
|
// 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);
|
icon = GetContentTypeIcon(content.type);
|
||||||
} else {
|
} else {
|
||||||
// When not in title view, System Data and System Archive groups use category icons.
|
// 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);
|
ui->main->setItemWidget(item, 0, checkBox);
|
||||||
|
|
||||||
connect(checkBox, &QCheckBox::stateChanged,
|
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) {
|
exists = content.already_exists](int state) {
|
||||||
if (state == Qt::Checked) {
|
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;
|
total_size += size;
|
||||||
} else {
|
} else {
|
||||||
if (!warning_shown && !exists &&
|
if (!system_warning_shown && !exists &&
|
||||||
(type == Core::ContentType::SystemArchive ||
|
(type == Core::ContentType::SystemArchive ||
|
||||||
type == Core::ContentType::Sysdata ||
|
type == Core::ContentType::Sysdata ||
|
||||||
type == Core::ContentType::SystemTitle)) {
|
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 "
|
tr("You are de-selecting important files that may be necessary for "
|
||||||
"your imported games to run.\nIt is highly recommended to import "
|
"your imported games to run.\nIt is highly recommended to import "
|
||||||
"these contents if they do not exist yet."));
|
"these contents if they do not exist yet."));
|
||||||
warning_shown = true;
|
system_warning_shown = true;
|
||||||
}
|
}
|
||||||
total_size -= size;
|
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);
|
checkBox->setChecked(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,15 +329,15 @@ void ImportDialog::RepopulateContent() {
|
|||||||
|
|
||||||
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) {
|
||||||
|
// Create 'Ungrouped' category.
|
||||||
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(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(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));
|
// Create categories for special content types.
|
||||||
title_icon_map.insert_or_assign(3, GetContentTypeIcon(Core::ContentType::SystemTitle));
|
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<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) {
|
||||||
@@ -344,16 +366,12 @@ void ImportDialog::RepopulateContent() {
|
|||||||
row = title_row_map.at(real_id);
|
row = title_row_map.at(real_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Core::ContentType::SystemArchive: {
|
default: {
|
||||||
row = title_row_map.at(1); // System archive
|
const std::size_t idx = std::find(SpecialContentTypeList.begin(),
|
||||||
break;
|
SpecialContentTypeList.end(), content.type) -
|
||||||
}
|
SpecialContentTypeList.begin();
|
||||||
case Core::ContentType::Sysdata: {
|
ASSERT_MSG(idx < SpecialContentTypeList.size(), "Content Type not handled");
|
||||||
row = title_row_map.at(2); // System data
|
row = title_row_map.at(idx + 1);
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Core::ContentType::SystemTitle: {
|
|
||||||
row = title_row_map.at(3); // System title
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,9 @@ private:
|
|||||||
bool program_trigger = false;
|
bool program_trigger = false;
|
||||||
|
|
||||||
// Whether the System Archive / System Data warning has been shown
|
// 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?
|
// TODO: Why this won't work as locals?
|
||||||
Core::ContentSpecifier current_content = {};
|
Core::ContentSpecifier current_content = {};
|
||||||
|
|||||||
Reference in New Issue
Block a user