forked from suyu/suyu
commit
0de49070e4
5 changed files with 269 additions and 4 deletions
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
// Modified by palfaiate on <2024/03/07>
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
// Modified by palfaiate on <2024/03/07>
|
// Modified by palfaiate on <2024/03/07>
|
||||||
|
// Reverted palfaiate's changes on <2024/03/25> -Nine-Ball
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
@ -580,6 +581,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
|
||||||
remove_menu->addSeparator();
|
remove_menu->addSeparator();
|
||||||
QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Pipeline Caches"));
|
QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Pipeline Caches"));
|
||||||
QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
|
QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
|
||||||
|
QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS"));
|
||||||
|
QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS"));
|
||||||
|
QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC"));
|
||||||
QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity"));
|
QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity"));
|
||||||
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"));
|
||||||
|
@ -647,6 +651,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
|
||||||
connect(remove_cache_storage, &QAction::triggered, [this, program_id, path] {
|
connect(remove_cache_storage, &QAction::triggered, [this, program_id, path] {
|
||||||
emit RemoveFileRequested(program_id, GameListRemoveTarget::CacheStorage, path);
|
emit RemoveFileRequested(program_id, GameListRemoveTarget::CacheStorage, path);
|
||||||
});
|
});
|
||||||
|
connect(dump_romfs, &QAction::triggered, [this, program_id, path]() {
|
||||||
|
emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::Normal);
|
||||||
|
});
|
||||||
|
connect(dump_romfs_sdmc, &QAction::triggered, [this, program_id, path]() {
|
||||||
|
emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::SDMC);
|
||||||
|
});
|
||||||
connect(verify_integrity, &QAction::triggered,
|
connect(verify_integrity, &QAction::triggered,
|
||||||
[this, path]() { emit VerifyIntegrityRequested(path); });
|
[this, path]() { emit VerifyIntegrityRequested(path); });
|
||||||
connect(copy_tid, &QAction::triggered,
|
connect(copy_tid, &QAction::triggered,
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
// Modified by palfaiate on <2024/03/07>
|
// Modified by palfaiate on <2024/03/07>
|
||||||
|
// Reverted palfaiate's changes on <2024/03/25> -Nine-Ball
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
@ -52,6 +53,11 @@ enum class GameListRemoveTarget {
|
||||||
CacheStorage,
|
CacheStorage,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class DumpRomFSTarget {
|
||||||
|
Normal,
|
||||||
|
SDMC,
|
||||||
|
};
|
||||||
|
|
||||||
enum class GameListShortcutTarget {
|
enum class GameListShortcutTarget {
|
||||||
Desktop,
|
Desktop,
|
||||||
Applications,
|
Applications,
|
||||||
|
@ -113,6 +119,7 @@ signals:
|
||||||
void RemoveFileRequested(u64 program_id, GameListRemoveTarget target,
|
void RemoveFileRequested(u64 program_id, GameListRemoveTarget target,
|
||||||
const std::string& game_path);
|
const std::string& game_path);
|
||||||
void RemovePlayTimeRequested(u64 program_id);
|
void RemovePlayTimeRequested(u64 program_id);
|
||||||
|
void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
|
||||||
void VerifyIntegrityRequested(const std::string& game_path);
|
void VerifyIntegrityRequested(const std::string& game_path);
|
||||||
void CopyTIDRequested(u64 program_id);
|
void CopyTIDRequested(u64 program_id);
|
||||||
void CreateShortcut(u64 program_id, const std::string& game_path,
|
void CreateShortcut(u64 program_id, const std::string& game_path,
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
// Modified by palfaiate on <2024/03/07>
|
|
||||||
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <clocale>
|
#include <clocale>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -55,6 +53,18 @@
|
||||||
#include "suyu/multiplayer/state.h"
|
#include "suyu/multiplayer/state.h"
|
||||||
#include "suyu/util/controller_navigation.h"
|
#include "suyu/util/controller_navigation.h"
|
||||||
|
|
||||||
|
// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
|
||||||
|
static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
|
||||||
|
const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::OpenMode mode) {
|
||||||
|
return vfs->CreateDirectory(path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overloaded function, also removed by palafiate
|
||||||
|
static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::VirtualDir& dir,
|
||||||
|
const std::string& path) {
|
||||||
|
return dir->CreateFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
#include <fmt/ostream.h>
|
#include <fmt/ostream.h>
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
@ -1465,6 +1475,7 @@ void GMainWindow::ConnectWidgetEvents() {
|
||||||
connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile);
|
connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile);
|
||||||
connect(game_list, &GameList::RemovePlayTimeRequested, this,
|
connect(game_list, &GameList::RemovePlayTimeRequested, this,
|
||||||
&GMainWindow::OnGameListRemovePlayTimeData);
|
&GMainWindow::OnGameListRemovePlayTimeData);
|
||||||
|
connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
|
||||||
connect(game_list, &GameList::VerifyIntegrityRequested, this,
|
connect(game_list, &GameList::VerifyIntegrityRequested, this,
|
||||||
&GMainWindow::OnGameListVerifyIntegrity);
|
&GMainWindow::OnGameListVerifyIntegrity);
|
||||||
connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
|
connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
|
||||||
|
@ -2369,6 +2380,68 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
|
||||||
QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path));
|
QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog& dialog,
|
||||||
|
const FileSys::VirtualDir& src, const FileSys::VirtualDir& dest,
|
||||||
|
bool full) {
|
||||||
|
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
|
||||||
|
return false;
|
||||||
|
if (dialog.wasCanceled())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<u8> buffer(CopyBufferSize);
|
||||||
|
auto last_timestamp = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
const auto QtRawCopy = [&](const FileSys::VirtualFile& src_file,
|
||||||
|
const FileSys::VirtualFile& dest_file) {
|
||||||
|
if (src_file == nullptr || dest_file == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!dest_file->Resize(src_file->GetSize())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < src_file->GetSize(); i += buffer.size()) {
|
||||||
|
if (dialog.wasCanceled()) {
|
||||||
|
dest_file->Resize(0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace std::literals::chrono_literals;
|
||||||
|
const auto new_timestamp = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
if ((new_timestamp - last_timestamp) > 33ms) {
|
||||||
|
last_timestamp = new_timestamp;
|
||||||
|
dialog.setValue(
|
||||||
|
static_cast<int>(std::min(read_size, total_size) * 100 / total_size));
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto read = src_file->Read(buffer.data(), buffer.size(), i);
|
||||||
|
dest_file->Write(buffer.data(), read, i);
|
||||||
|
|
||||||
|
read_size += read;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (full) {
|
||||||
|
for (const auto& file : src->GetFiles()) {
|
||||||
|
const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName());
|
||||||
|
if (!QtRawCopy(file, out))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& dir : src->GetSubdirectories()) {
|
||||||
|
const auto out = dest->CreateSubdirectory(dir->GetName());
|
||||||
|
if (!RomFSRawCopy(total_size, read_size, dialog, dir, out, full))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const {
|
QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case InstalledEntryType::Game:
|
case InstalledEntryType::Game:
|
||||||
|
@ -2610,6 +2683,121 @@ void GMainWindow::RemoveCacheStorage(u64 program_id) {
|
||||||
Common::FS::RemoveDirRecursively(path);
|
Common::FS::RemoveDirRecursively(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path,
|
||||||
|
DumpRomFSTarget target) {
|
||||||
|
const auto failed = [this] {
|
||||||
|
QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
|
||||||
|
tr("There was an error copying the RomFS files or the user "
|
||||||
|
"cancelled the operation."));
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto loader =
|
||||||
|
Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read));
|
||||||
|
if (loader == nullptr) {
|
||||||
|
failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSys::VirtualFile packed_update_raw{};
|
||||||
|
loader->ReadUpdateRaw(packed_update_raw);
|
||||||
|
|
||||||
|
const auto& installed = system->GetContentProvider();
|
||||||
|
|
||||||
|
u64 title_id{};
|
||||||
|
u8 raw_type{};
|
||||||
|
if (!SelectRomFSDumpTarget(installed, program_id, &title_id, &raw_type)) {
|
||||||
|
failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto type = static_cast<FileSys::ContentRecordType>(raw_type);
|
||||||
|
const auto base_nca = installed.GetEntry(title_id, type);
|
||||||
|
if (!base_nca) {
|
||||||
|
failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FileSys::NCA update_nca{packed_update_raw, nullptr};
|
||||||
|
if (type != FileSys::ContentRecordType::Program ||
|
||||||
|
update_nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS ||
|
||||||
|
update_nca.GetTitleId() != FileSys::GetUpdateTitleID(title_id)) {
|
||||||
|
packed_update_raw = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto base_romfs = base_nca->GetRomFS();
|
||||||
|
const auto dump_dir =
|
||||||
|
target == DumpRomFSTarget::Normal
|
||||||
|
? Common::FS::GetSuyuPath(Common::FS::SuyuPath::DumpDir)
|
||||||
|
: Common::FS::GetSuyuPath(Common::FS::SuyuPath::SDMCDir) / "atmosphere" / "contents";
|
||||||
|
const auto romfs_dir = fmt::format("{:016X}/romfs", title_id);
|
||||||
|
|
||||||
|
const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir);
|
||||||
|
|
||||||
|
const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed};
|
||||||
|
auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false);
|
||||||
|
|
||||||
|
const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::OpenMode::ReadWrite);
|
||||||
|
|
||||||
|
if (out == nullptr) {
|
||||||
|
failed();
|
||||||
|
vfs->DeleteDirectory(path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
const QStringList selections{tr("Full"), tr("Skeleton")};
|
||||||
|
const auto res = QInputDialog::getItem(
|
||||||
|
this, tr("Select RomFS Dump Mode"),
|
||||||
|
tr("Please select the how you would like the RomFS dumped.<br>Full will copy all of the "
|
||||||
|
"files into the new directory while <br>skeleton will only create the directory "
|
||||||
|
"structure."),
|
||||||
|
selections, 0, false, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
failed();
|
||||||
|
vfs->DeleteDirectory(path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto extracted = FileSys::ExtractRomFS(romfs);
|
||||||
|
if (extracted == nullptr) {
|
||||||
|
failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto full = res == selections.constFirst();
|
||||||
|
|
||||||
|
// The expected required space is the size of the RomFS + 1 GiB
|
||||||
|
const auto minimum_free_space = romfs->GetSize() + 0x40000000;
|
||||||
|
|
||||||
|
if (full && Common::FS::GetFreeSpaceSize(path) < minimum_free_space) {
|
||||||
|
QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
|
||||||
|
tr("There is not enough free space at %1 to extract the RomFS. Please "
|
||||||
|
"free up space or select a different dump directory at "
|
||||||
|
"Emulation > Configure > System > Filesystem > Dump Root")
|
||||||
|
.arg(QString::fromStdString(path)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, 100, this);
|
||||||
|
progress.setWindowModality(Qt::WindowModal);
|
||||||
|
progress.setMinimumDuration(100);
|
||||||
|
progress.setAutoClose(false);
|
||||||
|
progress.setAutoReset(false);
|
||||||
|
|
||||||
|
size_t read_size = 0;
|
||||||
|
|
||||||
|
if (RomFSRawCopy(romfs->GetSize(), read_size, progress, extracted, out, full)) {
|
||||||
|
progress.close();
|
||||||
|
QMessageBox::information(this, tr("RomFS Extraction Succeeded!"),
|
||||||
|
tr("The operation completed successfully."));
|
||||||
|
QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(path)));
|
||||||
|
} else {
|
||||||
|
progress.close();
|
||||||
|
failed();
|
||||||
|
vfs->DeleteDirectory(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
|
void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
|
||||||
const auto NotImplemented = [this] {
|
const auto NotImplemented = [this] {
|
||||||
QMessageBox::warning(this, tr("Integrity verification couldn't be performed!"),
|
QMessageBox::warning(this, tr("Integrity verification couldn't be performed!"),
|
||||||
|
@ -4680,6 +4868,66 @@ void GMainWindow::SetFirmwareVersion() {
|
||||||
firmware_label->setToolTip(QString::fromStdString(display_title));
|
firmware_label->setToolTip(QString::fromStdString(display_title));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
|
||||||
|
u64* selected_title_id, u8* selected_content_record_type) {
|
||||||
|
using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>;
|
||||||
|
boost::container::flat_set<ContentInfo> available_title_ids;
|
||||||
|
|
||||||
|
const auto RetrieveEntries = [&](FileSys::TitleType title_type,
|
||||||
|
FileSys::ContentRecordType record_type) {
|
||||||
|
const auto entries = installed.ListEntriesFilter(title_type, record_type);
|
||||||
|
for (const auto& entry : entries) {
|
||||||
|
if (FileSys::GetBaseTitleID(entry.title_id) == program_id &&
|
||||||
|
installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success) {
|
||||||
|
available_title_ids.insert({entry.title_id, title_type, record_type});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
|
||||||
|
RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::HtmlDocument);
|
||||||
|
RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::LegalInformation);
|
||||||
|
RetrieveEntries(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
|
||||||
|
|
||||||
|
if (available_title_ids.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t title_index = 0;
|
||||||
|
|
||||||
|
if (available_title_ids.size() > 1) {
|
||||||
|
QStringList list;
|
||||||
|
for (auto& [title_id, title_type, record_type] : available_title_ids) {
|
||||||
|
const auto hex_title_id = QString::fromStdString(fmt::format("{:X}", title_id));
|
||||||
|
if (record_type == FileSys::ContentRecordType::Program) {
|
||||||
|
list.push_back(QStringLiteral("Program [%1]").arg(hex_title_id));
|
||||||
|
} else if (record_type == FileSys::ContentRecordType::HtmlDocument) {
|
||||||
|
list.push_back(QStringLiteral("HTML document [%1]").arg(hex_title_id));
|
||||||
|
} else if (record_type == FileSys::ContentRecordType::LegalInformation) {
|
||||||
|
list.push_back(QStringLiteral("Legal information [%1]").arg(hex_title_id));
|
||||||
|
} else {
|
||||||
|
list.push_back(
|
||||||
|
QStringLiteral("DLC %1 [%2]").arg(title_id & 0x7FF).arg(hex_title_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
const auto res = QInputDialog::getItem(
|
||||||
|
this, tr("Select RomFS Dump Target"),
|
||||||
|
tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
title_index = list.indexOf(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& [title_id, title_type, record_type] = *available_title_ids.nth(title_index);
|
||||||
|
*selected_title_id = title_id;
|
||||||
|
*selected_content_record_type = static_cast<u8>(record_type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GMainWindow::ConfirmClose() {
|
bool GMainWindow::ConfirmClose() {
|
||||||
if (emu_thread == nullptr ||
|
if (emu_thread == nullptr ||
|
||||||
UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) {
|
UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) {
|
||||||
|
|
|
@ -51,6 +51,7 @@ class WaitTreeWidget;
|
||||||
enum class GameListOpenTarget;
|
enum class GameListOpenTarget;
|
||||||
enum class GameListRemoveTarget;
|
enum class GameListRemoveTarget;
|
||||||
enum class GameListShortcutTarget;
|
enum class GameListShortcutTarget;
|
||||||
|
enum class DumpRomFSTarget;
|
||||||
enum class InstalledEntryType;
|
enum class InstalledEntryType;
|
||||||
class GameListPlaceholder;
|
class GameListPlaceholder;
|
||||||
|
|
||||||
|
@ -347,6 +348,7 @@ private slots:
|
||||||
void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
|
void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
|
||||||
const std::string& game_path);
|
const std::string& game_path);
|
||||||
void OnGameListRemovePlayTimeData(u64 program_id);
|
void OnGameListRemovePlayTimeData(u64 program_id);
|
||||||
|
void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
|
||||||
void OnGameListVerifyIntegrity(const std::string& game_path);
|
void OnGameListVerifyIntegrity(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,
|
||||||
|
|
Loading…
Reference in a new issue