citra_qt, core: game list "Open XXX Location" improvements
This commit is contained in:
parent
41688b2f2a
commit
bbf391abb9
11 changed files with 120 additions and 19 deletions
|
@ -27,6 +27,8 @@
|
||||||
#include "citra_qt/ui_settings.h"
|
#include "citra_qt/ui_settings.h"
|
||||||
#include "common/common_paths.h"
|
#include "common/common_paths.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "core/file_sys/archive_extsavedata.h"
|
||||||
|
#include "core/file_sys/archive_source_sd_savedata.h"
|
||||||
#include "core/hle/service/fs/archive.h"
|
#include "core/hle/service/fs/archive.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
|
@ -409,7 +411,9 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
|
||||||
QMenu context_menu;
|
QMenu context_menu;
|
||||||
switch (selected.data(GameListItem::TypeRole).value<GameListItemType>()) {
|
switch (selected.data(GameListItem::TypeRole).value<GameListItemType>()) {
|
||||||
case GameListItemType::Game:
|
case GameListItemType::Game:
|
||||||
AddGamePopup(context_menu, selected.data(GameListItemPath::ProgramIdRole).toULongLong());
|
AddGamePopup(context_menu, selected.data(GameListItemPath::FullPathRole).toString(),
|
||||||
|
selected.data(GameListItemPath::ProgramIdRole).toULongLong(),
|
||||||
|
selected.data(GameListItemPath::ExtdataIdRole).toULongLong());
|
||||||
break;
|
break;
|
||||||
case GameListItemType::CustomDir:
|
case GameListItemType::CustomDir:
|
||||||
AddPermDirPopup(context_menu, selected);
|
AddPermDirPopup(context_menu, selected);
|
||||||
|
@ -423,23 +427,46 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
|
||||||
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
|
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameList::AddGamePopup(QMenu& context_menu, u64 program_id) {
|
void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 program_id,
|
||||||
|
u64 extdata_id) {
|
||||||
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_extdata_location = context_menu.addAction(tr("Open Extra Data Location"));
|
||||||
QAction* open_application_location = context_menu.addAction(tr("Open Application Location"));
|
QAction* open_application_location = context_menu.addAction(tr("Open Application Location"));
|
||||||
QAction* open_update_location = context_menu.addAction(tr("Open Update Data Location"));
|
QAction* open_update_location = context_menu.addAction(tr("Open Update Data Location"));
|
||||||
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"));
|
||||||
|
|
||||||
open_save_location->setEnabled(program_id != 0);
|
const bool is_application =
|
||||||
open_application_location->setVisible(FileUtil::Exists(
|
0x0004000000000000 <= program_id && program_id <= 0x00040000FFFFFFFF;
|
||||||
Service::AM::GetTitleContentPath(Service::FS::MediaType::SDMC, program_id)));
|
|
||||||
open_update_location->setEnabled(0x0004000000000000 <= program_id &&
|
std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
|
||||||
program_id <= 0x00040000FFFFFFFF);
|
open_save_location->setVisible(
|
||||||
|
is_application && FileUtil::Exists(FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor(
|
||||||
|
sdmc_dir, program_id)));
|
||||||
|
|
||||||
|
if (extdata_id) {
|
||||||
|
open_extdata_location->setVisible(
|
||||||
|
is_application &&
|
||||||
|
FileUtil::Exists(FileSys::GetExtDataPathFromId(sdmc_dir, extdata_id)));
|
||||||
|
} else {
|
||||||
|
open_extdata_location->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto media_type = Service::AM::GetTitleMediaType(program_id);
|
||||||
|
open_application_location->setVisible(path.toStdString() ==
|
||||||
|
Service::AM::GetTitleContentPath(media_type, program_id));
|
||||||
|
open_update_location->setVisible(
|
||||||
|
is_application && FileUtil::Exists(Service::AM::GetTitlePath(Service::FS::MediaType::SDMC,
|
||||||
|
program_id + 0xe00000000) +
|
||||||
|
"content/"));
|
||||||
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||||
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end());
|
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end());
|
||||||
|
|
||||||
connect(open_save_location, &QAction::triggered, [this, program_id] {
|
connect(open_save_location, &QAction::triggered, [this, program_id] {
|
||||||
emit OpenFolderRequested(program_id, GameListOpenTarget::SAVE_DATA);
|
emit OpenFolderRequested(program_id, GameListOpenTarget::SAVE_DATA);
|
||||||
});
|
});
|
||||||
|
connect(open_extdata_location, &QAction::triggered, [this, extdata_id] {
|
||||||
|
emit OpenFolderRequested(extdata_id, GameListOpenTarget::EXT_DATA);
|
||||||
|
});
|
||||||
connect(open_application_location, &QAction::triggered, [this, program_id] {
|
connect(open_application_location, &QAction::triggered, [this, program_id] {
|
||||||
emit OpenFolderRequested(program_id, GameListOpenTarget::APPLICATION);
|
emit OpenFolderRequested(program_id, GameListOpenTarget::APPLICATION);
|
||||||
});
|
});
|
||||||
|
@ -651,6 +678,9 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||||
u64 program_id = 0;
|
u64 program_id = 0;
|
||||||
loader->ReadProgramId(program_id);
|
loader->ReadProgramId(program_id);
|
||||||
|
|
||||||
|
u64 extdata_id = 0;
|
||||||
|
loader->ReadExtdataId(extdata_id);
|
||||||
|
|
||||||
std::vector<u8> smdh = [program_id, &loader]() -> std::vector<u8> {
|
std::vector<u8> smdh = [program_id, &loader]() -> std::vector<u8> {
|
||||||
std::vector<u8> original_smdh;
|
std::vector<u8> original_smdh;
|
||||||
loader->ReadIcon(original_smdh);
|
loader->ReadIcon(original_smdh);
|
||||||
|
@ -683,7 +713,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||||
|
|
||||||
emit EntryReady(
|
emit EntryReady(
|
||||||
{
|
{
|
||||||
new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id),
|
new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id,
|
||||||
|
extdata_id),
|
||||||
new GameListItemCompat(compatibility),
|
new GameListItemCompat(compatibility),
|
||||||
new GameListItemRegion(smdh),
|
new GameListItemRegion(smdh),
|
||||||
new GameListItem(
|
new GameListItem(
|
||||||
|
|
|
@ -27,7 +27,7 @@ class QTreeView;
|
||||||
class QToolButton;
|
class QToolButton;
|
||||||
class QVBoxLayout;
|
class QVBoxLayout;
|
||||||
|
|
||||||
enum class GameListOpenTarget { SAVE_DATA = 0, APPLICATION = 1, UPDATE_DATA = 2 };
|
enum class GameListOpenTarget { SAVE_DATA = 0, EXT_DATA = 1, APPLICATION = 2, UPDATE_DATA = 3 };
|
||||||
|
|
||||||
class GameList : public QWidget {
|
class GameList : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -89,7 +89,7 @@ private:
|
||||||
void RefreshGameDirectory();
|
void RefreshGameDirectory();
|
||||||
|
|
||||||
void PopupContextMenu(const QPoint& menu_location);
|
void PopupContextMenu(const QPoint& menu_location);
|
||||||
void AddGamePopup(QMenu& context_menu, u64 program_id);
|
void AddGamePopup(QMenu& context_menu, const QString& path, u64 program_id, u64 extdata_id);
|
||||||
void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected);
|
void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected);
|
||||||
void AddPermDirPopup(QMenu& context_menu, QModelIndex selected);
|
void AddPermDirPopup(QMenu& context_menu, QModelIndex selected);
|
||||||
|
|
||||||
|
|
|
@ -135,12 +135,15 @@ public:
|
||||||
static const int TitleRole = SortRole;
|
static const int TitleRole = SortRole;
|
||||||
static const int FullPathRole = SortRole + 1;
|
static const int FullPathRole = SortRole + 1;
|
||||||
static const int ProgramIdRole = SortRole + 2;
|
static const int ProgramIdRole = SortRole + 2;
|
||||||
|
static const int ExtdataIdRole = SortRole + 3;
|
||||||
|
|
||||||
GameListItemPath() = default;
|
GameListItemPath() = default;
|
||||||
GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) {
|
GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id,
|
||||||
|
u64 extdata_id) {
|
||||||
setData(type(), TypeRole);
|
setData(type(), TypeRole);
|
||||||
setData(game_path, FullPathRole);
|
setData(game_path, FullPathRole);
|
||||||
setData(qulonglong(program_id), ProgramIdRole);
|
setData(qulonglong(program_id), ProgramIdRole);
|
||||||
|
setData(qulonglong(extdata_id), ExtdataIdRole);
|
||||||
|
|
||||||
if (!Loader::IsValidSMDH(smdh_data)) {
|
if (!Loader::IsValidSMDH(smdh_data)) {
|
||||||
// SMDH is not valid, set a default icon
|
// SMDH is not valid, set a default icon
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
#include "common/scm_rev.h"
|
#include "common/scm_rev.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/archive_extsavedata.h"
|
||||||
#include "core/file_sys/archive_source_sd_savedata.h"
|
#include "core/file_sys/archive_source_sd_savedata.h"
|
||||||
#include "core/frontend/applets/default_applets.h"
|
#include "core/frontend/applets/default_applets.h"
|
||||||
#include "core/gdbstub/gdbstub.h"
|
#include "core/gdbstub/gdbstub.h"
|
||||||
|
@ -879,7 +880,7 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
|
||||||
BootGame(game_path);
|
BootGame(game_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target) {
|
void GMainWindow::OnGameListOpenFolder(u64 data_id, GameListOpenTarget target) {
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string open_target;
|
std::string open_target;
|
||||||
|
|
||||||
|
@ -887,16 +888,24 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
|
||||||
case GameListOpenTarget::SAVE_DATA: {
|
case GameListOpenTarget::SAVE_DATA: {
|
||||||
open_target = "Save Data";
|
open_target = "Save Data";
|
||||||
std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
|
std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
|
||||||
path = FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor(sdmc_dir, program_id);
|
path = FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor(sdmc_dir, data_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GameListOpenTarget::APPLICATION:
|
case GameListOpenTarget::EXT_DATA: {
|
||||||
open_target = "Application";
|
open_target = "Extra Data";
|
||||||
path = Service::AM::GetTitlePath(Service::FS::MediaType::SDMC, program_id) + "content/";
|
std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
|
||||||
|
path = FileSys::GetExtDataPathFromId(sdmc_dir, data_id);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
case GameListOpenTarget::APPLICATION: {
|
||||||
|
open_target = "Application";
|
||||||
|
auto media_type = Service::AM::GetTitleMediaType(data_id);
|
||||||
|
path = Service::AM::GetTitlePath(media_type, data_id) + "content/";
|
||||||
|
break;
|
||||||
|
}
|
||||||
case GameListOpenTarget::UPDATE_DATA:
|
case GameListOpenTarget::UPDATE_DATA:
|
||||||
open_target = "Update Data";
|
open_target = "Update Data";
|
||||||
path = Service::AM::GetTitlePath(Service::FS::MediaType::SDMC, program_id + 0xe00000000) +
|
path = Service::AM::GetTitlePath(Service::FS::MediaType::SDMC, data_id + 0xe00000000) +
|
||||||
"content/";
|
"content/";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -914,7 +923,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO(Frontend, "Opening {} path for program_id={:016x}", open_target, program_id);
|
LOG_INFO(Frontend, "Opening {} path for data_id={:016x}", open_target, data_id);
|
||||||
|
|
||||||
QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
|
QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,6 +176,13 @@ std::string GetExtDataContainerPath(const std::string& mount_point, bool shared)
|
||||||
return fmt::format("{}Nintendo 3DS/{}/{}/extdata/", mount_point, SYSTEM_ID, SDCARD_ID);
|
return fmt::format("{}Nintendo 3DS/{}/{}/extdata/", mount_point, SYSTEM_ID, SDCARD_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GetExtDataPathFromId(const std::string& mount_point, u64 extdata_id) {
|
||||||
|
u32 high = static_cast<u32>(extdata_id >> 32);
|
||||||
|
u32 low = static_cast<u32>(extdata_id & 0xFFFFFFFF);
|
||||||
|
|
||||||
|
return fmt::format("{}{:08x}/{:08x}/", GetExtDataContainerPath(mount_point, false), high, low);
|
||||||
|
}
|
||||||
|
|
||||||
Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low) {
|
Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low) {
|
||||||
ExtSaveDataArchivePath path;
|
ExtSaveDataArchivePath path;
|
||||||
path.media_type = media_type;
|
path.media_type = media_type;
|
||||||
|
|
|
@ -70,6 +70,15 @@ private:
|
||||||
*/
|
*/
|
||||||
std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path);
|
std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a path to the concrete ExtData archive in the host filesystem based on the
|
||||||
|
* extdata ID and base mount point.
|
||||||
|
* @param mount_point The base mount point of the ExtSaveData archives.
|
||||||
|
* @param extdata_id The id of the ExtSaveData
|
||||||
|
* @returns The complete path to the specified extdata archive in the host filesystem
|
||||||
|
*/
|
||||||
|
std::string GetExtDataPathFromId(const std::string& mount_point, u64 extdata_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a path to the base folder to hold concrete ExtSaveData archives in the host file
|
* Constructs a path to the base folder to hold concrete ExtSaveData archives in the host file
|
||||||
* system.
|
* system.
|
||||||
|
|
|
@ -576,6 +576,23 @@ Loader::ResultStatus NCCHContainer::ReadProgramId(u64_le& program_id) {
|
||||||
return Loader::ResultStatus::Success;
|
return Loader::ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader::ResultStatus NCCHContainer::ReadExtdataId(u64& extdata_id) {
|
||||||
|
Loader::ResultStatus result = Load();
|
||||||
|
if (result != Loader::ResultStatus::Success)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (!has_exheader)
|
||||||
|
return Loader::ResultStatus::ErrorNotUsed;
|
||||||
|
|
||||||
|
if (exheader_header.arm11_system_local_caps.storage_info.other_attributes >> 1) {
|
||||||
|
// Extdata id is not present when using extended savedata access
|
||||||
|
return Loader::ResultStatus::ErrorNotUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
extdata_id = exheader_header.arm11_system_local_caps.storage_info.ext_save_data_id;
|
||||||
|
return Loader::ResultStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
bool NCCHContainer::HasExeFS() {
|
bool NCCHContainer::HasExeFS() {
|
||||||
Loader::ResultStatus result = Load();
|
Loader::ResultStatus result = Load();
|
||||||
if (result != Loader::ResultStatus::Success)
|
if (result != Loader::ResultStatus::Success)
|
||||||
|
|
|
@ -125,7 +125,7 @@ struct ExHeader_SystemInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExHeader_StorageInfo {
|
struct ExHeader_StorageInfo {
|
||||||
u8 ext_save_data_id[8];
|
u64_le ext_save_data_id;
|
||||||
u8 system_save_data_id[8];
|
u8 system_save_data_id[8];
|
||||||
u8 reserved[8];
|
u8 reserved[8];
|
||||||
u8 access_info[7];
|
u8 access_info[7];
|
||||||
|
@ -251,6 +251,12 @@ public:
|
||||||
*/
|
*/
|
||||||
Loader::ResultStatus ReadProgramId(u64_le& program_id);
|
Loader::ResultStatus ReadProgramId(u64_le& program_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Extdata ID of the NCCH container
|
||||||
|
* @return ResultStatus result of function
|
||||||
|
*/
|
||||||
|
Loader::ResultStatus ReadExtdataId(u64& extdata_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the NCCH container contains an ExeFS
|
* Checks whether the NCCH container contains an ExeFS
|
||||||
* @return bool check result
|
* @return bool check result
|
||||||
|
|
|
@ -157,6 +157,15 @@ public:
|
||||||
return ResultStatus::ErrorNotImplemented;
|
return ResultStatus::ErrorNotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the extdata id for the application
|
||||||
|
* @param out_extdata_id Reference to store extdata id into
|
||||||
|
* @return ResultStatus result of function
|
||||||
|
*/
|
||||||
|
virtual ResultStatus ReadExtdataId(u64& out_extdata_id) {
|
||||||
|
return ResultStatus::ErrorNotImplemented;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the RomFS of the application
|
* Get the RomFS of the application
|
||||||
* Since the RomFS can be huge, we return a file reference instead of copying to a buffer
|
* Since the RomFS can be huge, we return a file reference instead of copying to a buffer
|
||||||
|
|
|
@ -216,6 +216,14 @@ ResultStatus AppLoader_NCCH::ReadProgramId(u64& out_program_id) {
|
||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResultStatus AppLoader_NCCH::ReadExtdataId(u64& out_extdata_id) {
|
||||||
|
ResultStatus result = base_ncch.ReadExtdataId(out_extdata_id);
|
||||||
|
if (result != ResultStatus::Success)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
return ResultStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileSys::RomFSReader>& romfs_file) {
|
ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileSys::RomFSReader>& romfs_file) {
|
||||||
return base_ncch.ReadRomFS(romfs_file);
|
return base_ncch.ReadRomFS(romfs_file);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,8 @@ public:
|
||||||
|
|
||||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||||
|
|
||||||
|
ResultStatus ReadExtdataId(u64& out_extdata_id) override;
|
||||||
|
|
||||||
ResultStatus ReadRomFS(std::shared_ptr<FileSys::RomFSReader>& romfs_file) override;
|
ResultStatus ReadRomFS(std::shared_ptr<FileSys::RomFSReader>& romfs_file) override;
|
||||||
|
|
||||||
ResultStatus ReadUpdateRomFS(std::shared_ptr<FileSys::RomFSReader>& romfs_file) override;
|
ResultStatus ReadUpdateRomFS(std::shared_ptr<FileSys::RomFSReader>& romfs_file) override;
|
||||||
|
|
Loading…
Reference in a new issue