From 7fa34900e749cb81d218d2287a05c3628b018c92 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 15 Oct 2019 14:36:52 -0400 Subject: [PATCH 1/7] file_sys: Add functions to manage system archive importing Provides a couple of functions that simply clearing and adding to imported sysdata. --- src/core/CMakeLists.txt | 2 + src/core/file_sys/system_archive/importer.cpp | 46 +++++++++++++++++++ src/core/file_sys/system_archive/importer.h | 31 +++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 src/core/file_sys/system_archive/importer.cpp create mode 100644 src/core/file_sys/system_archive/importer.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4f6a87b0a7..7a43e9cae9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -86,6 +86,8 @@ add_library(core STATIC file_sys/system_archive/data/font_nintendo_extended.h file_sys/system_archive/data/font_standard.cpp file_sys/system_archive/data/font_standard.h + file_sys/system_archive/importer.cpp + file_sys/system_archive/importer.h file_sys/system_archive/mii_model.cpp file_sys/system_archive/mii_model.h file_sys/system_archive/ng_word.cpp diff --git a/src/core/file_sys/system_archive/importer.cpp b/src/core/file_sys/system_archive/importer.cpp new file mode 100644 index 0000000000..9f81361e0e --- /dev/null +++ b/src/core/file_sys/system_archive/importer.cpp @@ -0,0 +1,46 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "common/file_util.h" +#include "core/file_sys/card_image.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/system_archive/importer.h" +#include "core/file_sys/vfs.h" +#include "core/loader/loader.h" + +namespace FileSys::SystemArchive { + +VirtualFile GetImportedSystemArchive(const VirtualDir& sysdata, u64 title_id) { + const auto filename = fmt::format("{:016X}.arc", title_id); + return sysdata->GetFile(filename); +} + +bool ImportSystemArchive(const VirtualDir& sysdata, u64 title_id, const VirtualFile& data) { + const auto filename = fmt::format("{:016X}.arc", title_id); + const auto out = sysdata->CreateFile(filename); + return out != nullptr && VfsRawCopy(data, out); +} + +bool ImportDirectorySystemUpdate(const VirtualDir& sysdata, const VirtualDir& dir) { + Core::Crypto::KeyManager keys; + + for (const auto& file : dir->GetFiles()) { + NCA nca{file, nullptr, 0, keys}; + if (nca.GetStatus() == Loader::ResultStatus::Success && + nca.GetType() == NCAContentType::Data && nca.GetRomFS() != nullptr) { + if (!ImportSystemArchive(sysdata, nca.GetTitleId(), nca.GetRomFS())) { + return false; + } + } + } + + return true; +} + +bool ImportXCISystemUpdate(const VirtualDir& sysdata, XCI& xci) { + return ImportDirectorySystemUpdate(sysdata, xci.GetUpdatePartition()); +} + +} // namespace FileSys::SystemArchive diff --git a/src/core/file_sys/system_archive/importer.h b/src/core/file_sys/system_archive/importer.h new file mode 100644 index 0000000000..01c81a4436 --- /dev/null +++ b/src/core/file_sys/system_archive/importer.h @@ -0,0 +1,31 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "core/file_sys/vfs_types.h" + +namespace FileSys { + +class NSP; +class XCI; + +namespace SystemArchive { + +/// Returns the file corresponding to the title_id if it exists in sysdata. +VirtualFile GetImportedSystemArchive(const VirtualDir& sysdata, u64 title_id); + +/// Copies the provided file into sysdata, overwriting current data. +bool ImportSystemArchive(const VirtualDir& sysdata, u64 title_id, const VirtualFile& data); + +/// Copies all system archives in the directory to sysdata. +bool ImportDirectorySystemUpdate(const VirtualDir& sysdata, const VirtualDir& dir); + +/// Calls ImportDirectorySystemUpdate on the update partition of the XCI. +bool ImportXCISystemUpdate(const VirtualDir& sysdata, XCI& xci); + +} // namespace SystemArchive + +} // namespace FileSys \ No newline at end of file From cedcaed581b081b97d16f33ba848c583501e17a1 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 15 Oct 2019 14:37:11 -0400 Subject: [PATCH 2/7] system_archive: Expose count and base ID constants --- src/core/file_sys/system_archive/system_archive.cpp | 3 --- src/core/file_sys/system_archive/system_archive.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp index e93d100a5d..e65d9410ec 100644 --- a/src/core/file_sys/system_archive/system_archive.cpp +++ b/src/core/file_sys/system_archive/system_archive.cpp @@ -12,9 +12,6 @@ namespace FileSys::SystemArchive { -constexpr u64 SYSTEM_ARCHIVE_BASE_TITLE_ID = 0x0100000000000800; -constexpr std::size_t SYSTEM_ARCHIVE_COUNT = 0x28; - using SystemArchiveSupplier = VirtualDir (*)(); struct SystemArchiveDescriptor { diff --git a/src/core/file_sys/system_archive/system_archive.h b/src/core/file_sys/system_archive/system_archive.h index 724a8eb17e..91c976634c 100644 --- a/src/core/file_sys/system_archive/system_archive.h +++ b/src/core/file_sys/system_archive/system_archive.h @@ -9,6 +9,9 @@ namespace FileSys::SystemArchive { +constexpr u64 SYSTEM_ARCHIVE_BASE_TITLE_ID = 0x0100000000000800; +constexpr std::size_t SYSTEM_ARCHIVE_COUNT = 0x28; + VirtualFile SynthesizeSystemArchive(u64 title_id); } // namespace FileSys::SystemArchive From db3ddd80da09e0d30e7e175135d7ab33d3babe1b Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 15 Oct 2019 14:37:38 -0400 Subject: [PATCH 3/7] filesystem: Add accessors for sysdata imported directory Stores imported system archives --- src/core/hle/service/filesystem/filesystem.cpp | 8 ++++++++ src/core/hle/service/filesystem/filesystem.h | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 11e5c56b7a..1728e311f8 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -717,6 +717,14 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC, sdmc_factory->GetSDMCContents()); } + + sysdata_imported_dir = + vfs.CreateDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + "imported", + FileSys::Mode::ReadWrite); +} + +FileSys::VirtualDir FileSystemController::GetSysdataImportedDirectory() const { + return sysdata_imported_dir; } void InstallInterfaces(Core::System& system) { diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 1b0a6a9496..2572592ffa 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -116,6 +116,8 @@ public: FileSys::VirtualDir GetBCATDirectory(u64 title_id) const; + FileSys::VirtualDir GetSysdataImportedDirectory() const; + // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function // above is called. void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); @@ -130,6 +132,8 @@ private: std::unique_ptr gamecard_registered; std::unique_ptr gamecard_placeholder; + FileSys::VirtualDir sysdata_imported_dir; + Core::System& system; }; From f06c541440569a52bbbd5bf92c3038f1d30d1604 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 15 Oct 2019 14:38:07 -0400 Subject: [PATCH 4/7] fsp_srv: Load imported system archives when available Occurs after NAND, but before OSS --- src/core/hle/service/filesystem/fsp_srv.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index cbd5466c12..f3e3e9c012 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -21,6 +21,7 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/romfs_factory.h" #include "core/file_sys/savedata_factory.h" +#include "core/file_sys/system_archive/importer.h" #include "core/file_sys/system_archive/system_archive.h" #include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" @@ -914,7 +915,12 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); if (data.Failed()) { - const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); + auto archive = FileSys::SystemArchive::GetImportedSystemArchive( + fsc.GetSystemNANDContentDirectory(), title_id); + + if (archive == nullptr) { + archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); + } if (archive != nullptr) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; From a85d0b6712e2be5631d74496ea5977c1a1072a59 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 15 Oct 2019 14:38:52 -0400 Subject: [PATCH 5/7] qt: Add UI to view sources of system archives Displays, color coded, the origin for each of the 0x28 archives, allowing for easy debugging. --- src/yuzu/CMakeLists.txt | 3 + src/yuzu/status/system_archive.cpp | 76 ++++++++++ src/yuzu/status/system_archive.h | 28 ++++ src/yuzu/status/system_archive.ui | 224 +++++++++++++++++++++++++++++ 4 files changed, 331 insertions(+) create mode 100644 src/yuzu/status/system_archive.cpp create mode 100644 src/yuzu/status/system_archive.h create mode 100644 src/yuzu/status/system_archive.ui diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index ff1c1d9856..b8bfcc3352 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -103,6 +103,9 @@ add_executable(yuzu main.cpp main.h main.ui + status/system_archive.cpp + status/system_archive.h + status/system_archive.ui uisettings.cpp uisettings.h util/limitable_input_dialog.cpp diff --git a/src/yuzu/status/system_archive.cpp b/src/yuzu/status/system_archive.cpp new file mode 100644 index 0000000000..6d70271c63 --- /dev/null +++ b/src/yuzu/status/system_archive.cpp @@ -0,0 +1,76 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/romfs_factory.h" +#include "core/file_sys/system_archive/importer.h" +#include "core/file_sys/system_archive/system_archive.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "ui_system_archive.h" +#include "yuzu/status/system_archive.h" + +namespace { + +enum class Source { + NAND, + XCI, + OSS, + None, +}; + +constexpr std::array SOURCE_FORMAT = { + "

8{:02X}

", + "

8{:02X}

", + "

8{:02X}

", + "

8{:02X}

", +}; + +QWidget* CreateItemForSourceAndId(QWidget* parent, Source source, std::size_t id) { + const auto text = fmt::format(SOURCE_FORMAT.at(static_cast(source)), id); + auto* out = new QLabel(QString::fromStdString(text), parent); + out->setAlignment(Qt::AlignHCenter); + return out; +} + +QWidget* CreateItem(QWidget* parent, const Service::FileSystem::FileSystemController& fsc, + std::size_t suffix) { + const auto title_id = FileSys::SystemArchive::SYSTEM_ARCHIVE_BASE_TITLE_ID + suffix; + + const auto nand = + fsc.OpenRomFS(title_id, FileSys::StorageId::NandSystem, FileSys::ContentRecordType::Data); + + if (nand.Succeeded()) { + return CreateItemForSourceAndId(parent, Source::NAND, suffix); + } + + auto archive = FileSys::SystemArchive::GetImportedSystemArchive( + fsc.GetSysdataImportedDirectory(), title_id); + + if (archive != nullptr) { + return CreateItemForSourceAndId(parent, Source::XCI, suffix); + } + + archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); + + if (archive != nullptr) { + return CreateItemForSourceAndId(parent, Source::OSS, suffix); + } + + return CreateItemForSourceAndId(parent, Source::None, suffix); +} + +} // Anonymous namespace + +SystemArchiveDialog::SystemArchiveDialog(QWidget* parent, + const Service::FileSystem::FileSystemController& fsc) + : QDialog(parent), ui(new Ui::SystemArchiveDialog) { + ui->setupUi(this); + + for (std::size_t i = 0; i < FileSys::SystemArchive::SYSTEM_ARCHIVE_COUNT; ++i) { + ui->grid->addWidget(CreateItem(this, fsc, i), i / 6, i % 6, 1, 1); + } +} + +SystemArchiveDialog::~SystemArchiveDialog() = default; diff --git a/src/yuzu/status/system_archive.h b/src/yuzu/status/system_archive.h new file mode 100644 index 0000000000..fef8ce7805 --- /dev/null +++ b/src/yuzu/status/system_archive.h @@ -0,0 +1,28 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +namespace Service::FileSystem { +class FileSystemController; +} // namespace Service::FileSystem + +namespace Ui { +class SystemArchiveDialog; +} // namespace Ui + +class SystemArchiveDialog : public QDialog { + Q_OBJECT + +public: + explicit SystemArchiveDialog(QWidget* parent, + const Service::FileSystem::FileSystemController& fsc); + ~SystemArchiveDialog() override; + +private: + std::unique_ptr ui; +}; diff --git a/src/yuzu/status/system_archive.ui b/src/yuzu/status/system_archive.ui new file mode 100644 index 0000000000..12d46a1bd5 --- /dev/null +++ b/src/yuzu/status/system_archive.ui @@ -0,0 +1,224 @@ + + + SystemArchiveDialog + + + + 0 + 0 + 473 + 422 + + + + System Archive Status + + + + + + + + + 75 + true + + + + System Archive Status + + + + + + + System archives are special data files games and applications can use for common functions, such as filtering for bad words or showing Miis on screen. + + + true + + + + + + + yuzu can load system archives from 3 sources: + + + + + + + 1. A real NAND dump placed into yuzu's NAND directory + + + + + + + 2. Archives imported from an XCI/cartridge game + + + + + + + 3. Open source reimplementations from the yuzu team + + + + + + + The following table shows the source for all of the system archives: + + + + + + + Qt::Horizontal + + + + + + + + + + Qt::Horizontal + + + + + + + + + + 75 + true + + + + <html><head/><body><p><span style=" color:#00aa00;">NAND Dump</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + 75 + true + + + + <html><head/><body><p><span style=" color:#0055ff;">Cartridge Dump</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + 75 + true + + + + <html><head/><body><p><span style=" color:#aa00ff;">Open Source</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + 75 + true + + + + <html><head/><body><p><span style=" color:#aa0000;">Missing</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SystemArchiveDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SystemArchiveDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + From bf5c2371a270b65b434f3af2428207f9613c77c7 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 15 Oct 2019 14:39:26 -0400 Subject: [PATCH 6/7] qt: Add question when booting XCI to import archives Adds convenience. --- src/yuzu/main.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d6bb18d24a..89df1a612d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -897,6 +897,23 @@ bool GMainWindow::LoadROM(const QString& filename) { "wiki. This message will not be shown again.")); } + const auto no_imported = + system.GetFileSystemController().GetSysdataImportedDirectory()->GetFiles().empty(); + + if (result == Core::System::ResultStatus::Success && + system.GetAppLoader().GetFileType() == Loader::FileType::XCI && no_imported) { + if (QMessageBox::question(this, tr("Import System Archives"), + tr("The game type you are using includes additional system files " + "that may improve yuzu's compatibility with this and other " + "games. Would you like to import these files?")) == + QMessageBox::Yes) { + const auto game = Core::GetGameFileFromPath(vfs, filename.toStdString()); + FileSys::XCI xci{game}; + FileSys::SystemArchive::ImportXCISystemUpdate( + system.GetFileSystemController().GetSysdataImportedDirectory(), xci); + } + } + if (result != Core::System::ResultStatus::Success) { switch (result) { case Core::System::ResultStatus::ErrorGetLoader: From a8119a47a15fd78127adf645698c944ed33097a7 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 15 Oct 2019 14:39:47 -0400 Subject: [PATCH 7/7] qt: Add Tools commands for system archive management --- src/yuzu/main.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++++ src/yuzu/main.h | 4 +++ src/yuzu/main.ui | 33 +++++++++++++++++++ 3 files changed, 118 insertions(+) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 89df1a612d..828102cdda 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -27,6 +27,7 @@ #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/hid/hid.h" +#include "yuzu/status/system_archive.h" // These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows // defines. @@ -40,6 +41,10 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual return dir->CreateFile(path); } +static bool VfsDirectoryDeleteFileWrapper(const FileSys::VirtualDir& dir, const std::string& path) { + return dir->DeleteFile(path); +} + #include #include @@ -83,6 +88,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/file_sys/romfs.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/submission_package.h" +#include "core/file_sys/system_archive/importer.h" #include "core/frontend/applets/software_keyboard.h" #include "core/hle/kernel/process.h" #include "core/hle/service/am/am.h" @@ -760,6 +766,16 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Capture_Screenshot, &QAction::triggered, this, &GMainWindow::OnCaptureScreenshot); + // Tools + connect(ui.actionImport_Directory, &QAction::triggered, this, + &GMainWindow::OnImportDirectorySystemUpdate); + connect(ui.actionImport_Cartridge, &QAction::triggered, this, + &GMainWindow::OnImportCartridgeSystemUpdate); + connect(ui.actionClear_Imported, &QAction::triggered, this, + &GMainWindow::OnClearImportedSysdata); + connect(ui.actionView_Status, &QAction::triggered, this, + &GMainWindow::OnViewSystemArchiveStatus); + // Help connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder); connect(ui.action_Rederive, &QAction::triggered, this, @@ -1358,6 +1374,71 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); } +void GMainWindow::OnClearImportedSysdata() { + Core::System& system{Core::System::GetInstance()}; + + const auto path = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + "imported" DIR_SEP; + QDir dir(QString::fromStdString(path)); + const auto list = dir.entryList(QDir::Files); + for (const auto& file : list) { + if (!dir.remove(file)) { + QMessageBox::warning(this, tr("Clear Failed"), + tr("The imported sysdata directory was not able to be cleared.")); + return; + } + } + + QMessageBox::information(this, tr("Clear Successful"), + tr("The imported sysdata directory was cleared successfully.")); +} + +void GMainWindow::OnImportDirectorySystemUpdate() { + Core::System& system{Core::System::GetInstance()}; + + const auto dir = QFileDialog::getExistingDirectory(this, tr("Select System Update Directory")); + + if (dir.isEmpty()) { + return; + } + + const auto vdir = vfs->OpenDirectory(dir.toStdString(), FileSys::Mode::Read); + if (FileSys::SystemArchive::ImportDirectorySystemUpdate( + system.GetFileSystemController().GetSysdataImportedDirectory(), vdir)) { + QMessageBox::information(this, tr("Import Successful"), + tr("The system update import was successful.")); + } else { + QMessageBox::warning(this, tr("Import Failed"), tr("The system update import failed.")); + } +} + +void GMainWindow::OnImportCartridgeSystemUpdate() { + Core::System& system{Core::System::GetInstance()}; + + const auto file = QFileDialog::getOpenFileName(this, tr("Select Cartridge File"), QString{}, + QStringLiteral("Cartridge Images (*.xci)")); + + if (file.isEmpty()) { + return; + } + + FileSys::XCI xci{vfs->OpenFile(file.toStdString(), FileSys::Mode::Read)}; + if (FileSys::SystemArchive::ImportXCISystemUpdate( + system.GetFileSystemController().GetSysdataImportedDirectory(), xci)) { + QMessageBox::information(this, tr("Import Successful"), + tr("The system update import was successful.")); + } else { + QMessageBox::warning(this, tr("Import Failed"), tr("The system update import failed.")); + } +} + +void GMainWindow::OnViewSystemArchiveStatus() { + Core::System& system{Core::System::GetInstance()}; + + system.GetFileSystemController().CreateFactories(*vfs); + SystemArchiveDialog dialog(this, system.GetFileSystemController()); + dialog.exec(); +} + void GMainWindow::OnGameListOpenDirectory(const QString& directory) { QString path; if (directory == QStringLiteral("SDMC")) { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index fd4b9ccf54..cce177f7f0 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -190,6 +190,10 @@ private slots: void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, const CompatibilityList& compatibility_list); + void OnClearImportedSysdata(); + void OnImportDirectorySystemUpdate(); + void OnImportCartridgeSystemUpdate(); + void OnViewSystemArchiveStatus(); void OnGameListOpenDirectory(const QString& directory); void OnGameListAddDirectory(); void OnGameListShowList(bool show); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index a1ce3c0c35..37cc24e17b 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -104,9 +104,22 @@ Tools + + + System Archives + + + + + + + + + + @@ -293,6 +306,26 @@ Capture Screenshot + + + Import Directory + + + + + Import Cartridge + + + + + Clear Imported + + + + + View Status + +