mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-26 13:26:30 -05:00
Merge pull request #4372 from Morph1984/remove_context_menu
game_list: Add "Remove" context menu
This commit is contained in:
commit
b8a7b530f8
7 changed files with 291 additions and 70 deletions
|
@ -547,56 +547,6 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex
|
||||||
return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy);
|
return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RegisteredCache::RemoveExistingEntry(u64 title_id) {
|
|
||||||
const auto delete_nca = [this](const NcaID& id) {
|
|
||||||
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
|
||||||
|
|
||||||
if (dir->GetFileRelative(path) == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::Crypto::SHA256Hash hash{};
|
|
||||||
mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
|
|
||||||
const auto dirname = fmt::format("000000{:02X}", hash[0]);
|
|
||||||
|
|
||||||
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
|
|
||||||
|
|
||||||
const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
|
|
||||||
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
// If an entry exists in the registered cache, remove it
|
|
||||||
if (HasEntry(title_id, ContentRecordType::Meta)) {
|
|
||||||
LOG_INFO(Loader,
|
|
||||||
"Previously installed entry (v{}) for title_id={:016X} detected! "
|
|
||||||
"Attempting to remove...",
|
|
||||||
GetEntryVersion(title_id).value_or(0), title_id);
|
|
||||||
// Get all the ncas associated with the current CNMT and delete them
|
|
||||||
const auto meta_old_id =
|
|
||||||
GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
|
|
||||||
const auto program_id =
|
|
||||||
GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
|
|
||||||
const auto data_id =
|
|
||||||
GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
|
|
||||||
const auto control_id =
|
|
||||||
GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
|
|
||||||
const auto html_id =
|
|
||||||
GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
|
|
||||||
const auto legal_id =
|
|
||||||
GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
|
|
||||||
|
|
||||||
delete_nca(meta_old_id);
|
|
||||||
delete_nca(program_id);
|
|
||||||
delete_nca(data_id);
|
|
||||||
delete_nca(control_id);
|
|
||||||
delete_nca(html_id);
|
|
||||||
delete_nca(legal_id);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists,
|
InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists,
|
||||||
const VfsCopyFunction& copy) {
|
const VfsCopyFunction& copy) {
|
||||||
const auto ncas = nsp.GetNCAsCollapsed();
|
const auto ncas = nsp.GetNCAsCollapsed();
|
||||||
|
@ -692,6 +642,57 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
|
||||||
return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
|
return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
|
||||||
|
const auto delete_nca = [this](const NcaID& id) {
|
||||||
|
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
||||||
|
|
||||||
|
const bool isFile = dir->GetFileRelative(path) != nullptr;
|
||||||
|
const bool isDir = dir->GetDirectoryRelative(path) != nullptr;
|
||||||
|
|
||||||
|
if (isFile) {
|
||||||
|
return dir->DeleteFile(path);
|
||||||
|
} else if (isDir) {
|
||||||
|
return dir->DeleteSubdirectoryRecursive(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If an entry exists in the registered cache, remove it
|
||||||
|
if (HasEntry(title_id, ContentRecordType::Meta)) {
|
||||||
|
LOG_INFO(Loader,
|
||||||
|
"Previously installed entry (v{}) for title_id={:016X} detected! "
|
||||||
|
"Attempting to remove...",
|
||||||
|
GetEntryVersion(title_id).value_or(0), title_id);
|
||||||
|
|
||||||
|
// Get all the ncas associated with the current CNMT and delete them
|
||||||
|
const auto meta_old_id =
|
||||||
|
GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
|
||||||
|
const auto program_id =
|
||||||
|
GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
|
||||||
|
const auto data_id =
|
||||||
|
GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
|
||||||
|
const auto control_id =
|
||||||
|
GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
|
||||||
|
const auto html_id =
|
||||||
|
GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
|
||||||
|
const auto legal_id =
|
||||||
|
GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
|
||||||
|
|
||||||
|
const auto deleted_meta = delete_nca(meta_old_id);
|
||||||
|
const auto deleted_program = delete_nca(program_id);
|
||||||
|
const auto deleted_data = delete_nca(data_id);
|
||||||
|
const auto deleted_control = delete_nca(control_id);
|
||||||
|
const auto deleted_html = delete_nca(html_id);
|
||||||
|
const auto deleted_legal = delete_nca(legal_id);
|
||||||
|
|
||||||
|
return deleted_meta && (deleted_meta || deleted_program || deleted_data ||
|
||||||
|
deleted_control || deleted_html || deleted_legal);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
|
InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
|
||||||
bool overwrite_if_exists,
|
bool overwrite_if_exists,
|
||||||
std::optional<NcaID> override_id) {
|
std::optional<NcaID> override_id) {
|
||||||
|
|
|
@ -155,9 +155,6 @@ public:
|
||||||
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
||||||
std::optional<u64> title_id = {}) const override;
|
std::optional<u64> title_id = {}) const override;
|
||||||
|
|
||||||
// Removes an existing entry based on title id
|
|
||||||
bool RemoveExistingEntry(u64 title_id);
|
|
||||||
|
|
||||||
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
|
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
|
||||||
// there is a meta NCA and all of them are accessible.
|
// there is a meta NCA and all of them are accessible.
|
||||||
InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
|
InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
|
||||||
|
@ -172,6 +169,9 @@ public:
|
||||||
InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
|
InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
|
||||||
const VfsCopyFunction& copy = &VfsRawCopy);
|
const VfsCopyFunction& copy = &VfsRawCopy);
|
||||||
|
|
||||||
|
// Removes an existing entry based on title id
|
||||||
|
bool RemoveExistingEntry(u64 title_id) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void IterateAllMetadata(std::vector<T>& out,
|
void IterateAllMetadata(std::vector<T>& out,
|
||||||
|
|
|
@ -70,14 +70,18 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
|
||||||
NAX::~NAX() = default;
|
NAX::~NAX() = default;
|
||||||
|
|
||||||
Loader::ResultStatus NAX::Parse(std::string_view path) {
|
Loader::ResultStatus NAX::Parse(std::string_view path) {
|
||||||
if (file->ReadObject(header.get()) != sizeof(NAXHeader))
|
if (file == nullptr) {
|
||||||
|
return Loader::ResultStatus::ErrorNullFile;
|
||||||
|
}
|
||||||
|
if (file->ReadObject(header.get()) != sizeof(NAXHeader)) {
|
||||||
return Loader::ResultStatus::ErrorBadNAXHeader;
|
return Loader::ResultStatus::ErrorBadNAXHeader;
|
||||||
|
}
|
||||||
if (header->magic != Common::MakeMagic('N', 'A', 'X', '0'))
|
if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) {
|
||||||
return Loader::ResultStatus::ErrorBadNAXHeader;
|
return Loader::ResultStatus::ErrorBadNAXHeader;
|
||||||
|
}
|
||||||
if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size)
|
if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) {
|
||||||
return Loader::ResultStatus::ErrorIncorrectNAXFileSize;
|
return Loader::ResultStatus::ErrorIncorrectNAXFileSize;
|
||||||
|
}
|
||||||
|
|
||||||
keys.DeriveSDSeedLazy();
|
keys.DeriveSDSeedLazy();
|
||||||
std::array<Core::Crypto::Key256, 2> sd_keys{};
|
std::array<Core::Crypto::Key256, 2> sd_keys{};
|
||||||
|
|
|
@ -474,28 +474,56 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
|
||||||
|
|
||||||
void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) {
|
void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) {
|
||||||
QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
|
QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
|
||||||
QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location"));
|
QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location"));
|
||||||
QAction* open_transferable_shader_cache =
|
QAction* open_transferable_shader_cache =
|
||||||
context_menu.addAction(tr("Open Transferable Shader Cache"));
|
context_menu.addAction(tr("Open Transferable Shader Cache"));
|
||||||
context_menu.addSeparator();
|
context_menu.addSeparator();
|
||||||
|
QMenu* remove_menu = context_menu.addMenu(tr("Remove"));
|
||||||
|
QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update"));
|
||||||
|
QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC"));
|
||||||
|
QAction* remove_shader_cache = remove_menu->addAction(tr("Remove Shader Cache"));
|
||||||
|
QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration"));
|
||||||
|
remove_menu->addSeparator();
|
||||||
|
QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
|
||||||
QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
|
QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
|
||||||
QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
|
QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
|
||||||
QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
|
QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
|
||||||
context_menu.addSeparator();
|
context_menu.addSeparator();
|
||||||
QAction* properties = context_menu.addAction(tr("Properties"));
|
QAction* properties = context_menu.addAction(tr("Properties"));
|
||||||
|
|
||||||
open_save_location->setEnabled(program_id != 0);
|
open_save_location->setVisible(program_id != 0);
|
||||||
|
open_mod_location->setVisible(program_id != 0);
|
||||||
|
open_transferable_shader_cache->setVisible(program_id != 0);
|
||||||
|
remove_update->setVisible(program_id != 0);
|
||||||
|
remove_dlc->setVisible(program_id != 0);
|
||||||
|
remove_shader_cache->setVisible(program_id != 0);
|
||||||
|
remove_all_content->setVisible(program_id != 0);
|
||||||
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||||
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
|
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
|
||||||
|
|
||||||
connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
|
connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
|
||||||
emit OpenFolderRequested(GameListOpenTarget::SaveData, path);
|
emit OpenFolderRequested(GameListOpenTarget::SaveData, path);
|
||||||
});
|
});
|
||||||
connect(open_lfs_location, &QAction::triggered, [this, program_id, path]() {
|
connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
|
||||||
emit OpenFolderRequested(GameListOpenTarget::ModData, path);
|
emit OpenFolderRequested(GameListOpenTarget::ModData, path);
|
||||||
});
|
});
|
||||||
connect(open_transferable_shader_cache, &QAction::triggered,
|
connect(open_transferable_shader_cache, &QAction::triggered,
|
||||||
[this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });
|
[this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });
|
||||||
|
connect(remove_all_content, &QAction::triggered, [this, program_id]() {
|
||||||
|
emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Game);
|
||||||
|
});
|
||||||
|
connect(remove_update, &QAction::triggered, [this, program_id]() {
|
||||||
|
emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Update);
|
||||||
|
});
|
||||||
|
connect(remove_dlc, &QAction::triggered, [this, program_id]() {
|
||||||
|
emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::AddOnContent);
|
||||||
|
});
|
||||||
|
connect(remove_shader_cache, &QAction::triggered, [this, program_id]() {
|
||||||
|
emit RemoveFileRequested(program_id, GameListRemoveTarget::ShaderCache);
|
||||||
|
});
|
||||||
|
connect(remove_custom_config, &QAction::triggered, [this, program_id]() {
|
||||||
|
emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration);
|
||||||
|
});
|
||||||
connect(dump_romfs, &QAction::triggered,
|
connect(dump_romfs, &QAction::triggered,
|
||||||
[this, program_id, path]() { emit DumpRomFSRequested(program_id, path); });
|
[this, program_id, path]() { emit DumpRomFSRequested(program_id, path); });
|
||||||
connect(copy_tid, &QAction::triggered,
|
connect(copy_tid, &QAction::triggered,
|
||||||
|
|
|
@ -39,6 +39,17 @@ enum class GameListOpenTarget {
|
||||||
ModData,
|
ModData,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class GameListRemoveTarget {
|
||||||
|
ShaderCache,
|
||||||
|
CustomConfiguration,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class InstalledEntryType {
|
||||||
|
Game,
|
||||||
|
Update,
|
||||||
|
AddOnContent,
|
||||||
|
};
|
||||||
|
|
||||||
class GameList : public QWidget {
|
class GameList : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -75,6 +86,8 @@ signals:
|
||||||
void ShouldCancelWorker();
|
void ShouldCancelWorker();
|
||||||
void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path);
|
void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path);
|
||||||
void OpenTransferableShaderCacheRequested(u64 program_id);
|
void OpenTransferableShaderCacheRequested(u64 program_id);
|
||||||
|
void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type);
|
||||||
|
void RemoveFileRequested(u64 program_id, GameListRemoveTarget target);
|
||||||
void DumpRomFSRequested(u64 program_id, const std::string& game_path);
|
void DumpRomFSRequested(u64 program_id, const std::string& game_path);
|
||||||
void CopyTIDRequested(u64 program_id);
|
void CopyTIDRequested(u64 program_id);
|
||||||
void NavigateToGamedbEntryRequested(u64 program_id,
|
void NavigateToGamedbEntryRequested(u64 program_id,
|
||||||
|
@ -117,8 +130,6 @@ private:
|
||||||
friend class GameListSearchField;
|
friend class GameListSearchField;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(GameListOpenTarget);
|
|
||||||
|
|
||||||
class GameListPlaceholder : public QWidget {
|
class GameListPlaceholder : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -847,6 +847,9 @@ void GMainWindow::ConnectWidgetEvents() {
|
||||||
connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
|
connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
|
||||||
connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this,
|
connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this,
|
||||||
&GMainWindow::OnTransferableShaderCacheOpenFile);
|
&GMainWindow::OnTransferableShaderCacheOpenFile);
|
||||||
|
connect(game_list, &GameList::RemoveInstalledEntryRequested, this,
|
||||||
|
&GMainWindow::OnGameListRemoveInstalledEntry);
|
||||||
|
connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile);
|
||||||
connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
|
connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
|
||||||
connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
|
connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
|
||||||
connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
|
connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
|
||||||
|
@ -1257,7 +1260,6 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
|
||||||
case GameListOpenTarget::SaveData: {
|
case GameListOpenTarget::SaveData: {
|
||||||
open_target = tr("Save Data");
|
open_target = tr("Save Data");
|
||||||
const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||||
ASSERT(program_id != 0);
|
|
||||||
|
|
||||||
if (has_user_save) {
|
if (has_user_save) {
|
||||||
// User save data
|
// User save data
|
||||||
|
@ -1322,14 +1324,12 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
|
void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
|
||||||
ASSERT(program_id != 0);
|
|
||||||
|
|
||||||
const QString shader_dir =
|
const QString shader_dir =
|
||||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir));
|
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir));
|
||||||
const QString tranferable_shader_cache_folder_path =
|
const QString transferable_shader_cache_folder_path =
|
||||||
shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
|
shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
|
||||||
const QString transferable_shader_cache_file_path =
|
const QString transferable_shader_cache_file_path =
|
||||||
tranferable_shader_cache_folder_path + QDir::separator() +
|
transferable_shader_cache_folder_path + QDir::separator() +
|
||||||
QString::fromStdString(fmt::format("{:016X}.bin", program_id));
|
QString::fromStdString(fmt::format("{:016X}.bin", program_id));
|
||||||
|
|
||||||
if (!QFile::exists(transferable_shader_cache_file_path)) {
|
if (!QFile::exists(transferable_shader_cache_file_path)) {
|
||||||
|
@ -1350,7 +1350,7 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
|
||||||
param << QDir::toNativeSeparators(transferable_shader_cache_file_path);
|
param << QDir::toNativeSeparators(transferable_shader_cache_file_path);
|
||||||
QProcess::startDetached(explorer, param);
|
QProcess::startDetached(explorer, param);
|
||||||
#else
|
#else
|
||||||
QDesktopServices::openUrl(QUrl::fromLocalFile(tranferable_shader_cache_folder_path));
|
QDesktopServices::openUrl(QUrl::fromLocalFile(transferable_shader_cache_folder_path));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1394,6 +1394,174 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) {
|
||||||
|
const QString entry_type = [this, type] {
|
||||||
|
switch (type) {
|
||||||
|
case InstalledEntryType::Game:
|
||||||
|
return tr("Contents");
|
||||||
|
case InstalledEntryType::Update:
|
||||||
|
return tr("Update");
|
||||||
|
case InstalledEntryType::AddOnContent:
|
||||||
|
return tr("DLC");
|
||||||
|
default:
|
||||||
|
return QString{};
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (QMessageBox::question(
|
||||||
|
this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type),
|
||||||
|
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case InstalledEntryType::Game:
|
||||||
|
RemoveBaseContent(program_id, entry_type);
|
||||||
|
[[fallthrough]];
|
||||||
|
case InstalledEntryType::Update:
|
||||||
|
RemoveUpdateContent(program_id, entry_type);
|
||||||
|
if (type != InstalledEntryType::Game) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
case InstalledEntryType::AddOnContent:
|
||||||
|
RemoveAddOnContent(program_id, entry_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
|
||||||
|
"game_list");
|
||||||
|
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) {
|
||||||
|
const auto& fs_controller = Core::System::GetInstance().GetFileSystemController();
|
||||||
|
const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) ||
|
||||||
|
fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
QMessageBox::information(this, tr("Successfully Removed"),
|
||||||
|
tr("Successfully removed the installed base game."));
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(
|
||||||
|
this, tr("Error Removing %1").arg(entry_type),
|
||||||
|
tr("The base game is not installed in the NAND and cannot be removed."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) {
|
||||||
|
const auto update_id = program_id | 0x800;
|
||||||
|
const auto& fs_controller = Core::System::GetInstance().GetFileSystemController();
|
||||||
|
const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
|
||||||
|
fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
QMessageBox::information(this, tr("Successfully Removed"),
|
||||||
|
tr("Successfully removed the installed update."));
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type),
|
||||||
|
tr("There is no update installed for this title."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) {
|
||||||
|
u32 count{};
|
||||||
|
const auto& fs_controller = Core::System::GetInstance().GetFileSystemController();
|
||||||
|
const auto dlc_entries = Core::System::GetInstance().GetContentProvider().ListEntriesFilter(
|
||||||
|
FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
|
||||||
|
|
||||||
|
for (const auto& entry : dlc_entries) {
|
||||||
|
if ((entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id) {
|
||||||
|
const auto res =
|
||||||
|
fs_controller.GetUserNANDContents()->RemoveExistingEntry(entry.title_id) ||
|
||||||
|
fs_controller.GetSDMCContents()->RemoveExistingEntry(entry.title_id);
|
||||||
|
if (res) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type),
|
||||||
|
tr("There are no DLC installed for this title."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox::information(this, tr("Successfully Removed"),
|
||||||
|
tr("Successfully removed %1 installed DLC.").arg(count));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target) {
|
||||||
|
const QString question = [this, target] {
|
||||||
|
switch (target) {
|
||||||
|
case GameListRemoveTarget::ShaderCache:
|
||||||
|
return tr("Delete Transferable Shader Cache?");
|
||||||
|
case GameListRemoveTarget::CustomConfiguration:
|
||||||
|
return tr("Remove Custom Game Configuration?");
|
||||||
|
default:
|
||||||
|
return QString{};
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No,
|
||||||
|
QMessageBox::No) != QMessageBox::Yes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (target) {
|
||||||
|
case GameListRemoveTarget::ShaderCache:
|
||||||
|
RemoveTransferableShaderCache(program_id);
|
||||||
|
break;
|
||||||
|
case GameListRemoveTarget::CustomConfiguration:
|
||||||
|
RemoveCustomConfiguration(program_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMainWindow::RemoveTransferableShaderCache(u64 program_id) {
|
||||||
|
const QString shader_dir =
|
||||||
|
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir));
|
||||||
|
const QString transferable_shader_cache_folder_path =
|
||||||
|
shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
|
||||||
|
const QString transferable_shader_cache_file_path =
|
||||||
|
transferable_shader_cache_folder_path + QDir::separator() +
|
||||||
|
QString::fromStdString(fmt::format("{:016X}.bin", program_id));
|
||||||
|
|
||||||
|
if (!QFile::exists(transferable_shader_cache_file_path)) {
|
||||||
|
QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"),
|
||||||
|
tr("A shader cache for this title does not exist."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QFile::remove(transferable_shader_cache_file_path)) {
|
||||||
|
QMessageBox::information(this, tr("Successfully Removed"),
|
||||||
|
tr("Successfully removed the transferable shader cache."));
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"),
|
||||||
|
tr("Failed to remove the transferable shader cache."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMainWindow::RemoveCustomConfiguration(u64 program_id) {
|
||||||
|
const QString config_dir =
|
||||||
|
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir));
|
||||||
|
const QString custom_config_file_path =
|
||||||
|
config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id));
|
||||||
|
|
||||||
|
if (!QFile::exists(custom_config_file_path)) {
|
||||||
|
QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
|
||||||
|
tr("A custom configuration for this title does not exist."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QFile::remove(custom_config_file_path)) {
|
||||||
|
QMessageBox::information(this, tr("Successfully Removed"),
|
||||||
|
tr("Successfully removed the custom game configuration."));
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
|
||||||
|
tr("Failed to remove the custom game configuration."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
|
void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
|
||||||
const auto failed = [this] {
|
const auto failed = [this] {
|
||||||
QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
|
QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
|
||||||
|
@ -1714,9 +1882,9 @@ void GMainWindow::OnMenuInstallToNAND() {
|
||||||
: tr("%n file(s) failed to install\n", "", failed_files.size()));
|
: tr("%n file(s) failed to install\n", "", failed_files.size()));
|
||||||
|
|
||||||
QMessageBox::information(this, tr("Install Results"), install_results);
|
QMessageBox::information(this, tr("Install Results"), install_results);
|
||||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
|
||||||
FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
|
FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
|
||||||
"game_list");
|
"game_list");
|
||||||
|
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||||
ui.action_Install_File_NAND->setEnabled(true);
|
ui.action_Install_File_NAND->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ class QPushButton;
|
||||||
class QProgressDialog;
|
class QProgressDialog;
|
||||||
class WaitTreeWidget;
|
class WaitTreeWidget;
|
||||||
enum class GameListOpenTarget;
|
enum class GameListOpenTarget;
|
||||||
|
enum class GameListRemoveTarget;
|
||||||
|
enum class InstalledEntryType;
|
||||||
class GameListPlaceholder;
|
class GameListPlaceholder;
|
||||||
|
|
||||||
namespace Core::Frontend {
|
namespace Core::Frontend {
|
||||||
|
@ -198,6 +200,8 @@ private slots:
|
||||||
void OnGameListLoadFile(QString game_path);
|
void OnGameListLoadFile(QString game_path);
|
||||||
void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
|
void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
|
||||||
void OnTransferableShaderCacheOpenFile(u64 program_id);
|
void OnTransferableShaderCacheOpenFile(u64 program_id);
|
||||||
|
void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
|
||||||
|
void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target);
|
||||||
void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
|
void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
|
||||||
void OnGameListCopyTID(u64 program_id);
|
void OnGameListCopyTID(u64 program_id);
|
||||||
void OnGameListNavigateToGamedbEntry(u64 program_id,
|
void OnGameListNavigateToGamedbEntry(u64 program_id,
|
||||||
|
@ -229,6 +233,11 @@ private slots:
|
||||||
void OnLanguageChanged(const QString& locale);
|
void OnLanguageChanged(const QString& locale);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void RemoveBaseContent(u64 program_id, const QString& entry_type);
|
||||||
|
void RemoveUpdateContent(u64 program_id, const QString& entry_type);
|
||||||
|
void RemoveAddOnContent(u64 program_id, const QString& entry_type);
|
||||||
|
void RemoveTransferableShaderCache(u64 program_id);
|
||||||
|
void RemoveCustomConfiguration(u64 program_id);
|
||||||
std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
|
std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
|
||||||
InstallResult InstallNSPXCI(const QString& filename);
|
InstallResult InstallNSPXCI(const QString& filename);
|
||||||
InstallResult InstallNCA(const QString& filename);
|
InstallResult InstallNCA(const QString& filename);
|
||||||
|
|
Loading…
Reference in a new issue