diff options
Diffstat (limited to 'src/yuzu')
22 files changed, 896 insertions, 337 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 6b25a7fa0..a862b2610 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -30,6 +30,12 @@ add_executable(yuzu configuration/configure_audio.cpp configuration/configure_audio.h configuration/configure_audio.ui + configuration/configure_cpu.cpp + configuration/configure_cpu.h + configuration/configure_cpu.ui + configuration/configure_cpu_debug.cpp + configuration/configure_cpu_debug.h + configuration/configure_cpu_debug.ui configuration/configure_debug.cpp configuration/configure_debug.h configuration/configure_debug.ui @@ -98,11 +104,13 @@ add_executable(yuzu game_list_p.h game_list_worker.cpp game_list_worker.h + hotkeys.cpp + hotkeys.h + install_dialog.cpp + install_dialog.h loading_screen.cpp loading_screen.h loading_screen.ui - hotkeys.cpp - hotkeys.h main.cpp main.h main.ui diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 1b2b1b2bb..9e9b38214 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -505,22 +505,6 @@ void Config::ReadDataStorageValues() { ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool(); Settings::values.gamecard_path = ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString(); - Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>( - ReadSetting(QStringLiteral("nand_total_size"), - QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB))) - .toULongLong()); - Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>( - ReadSetting(QStringLiteral("nand_user_size"), - QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB))) - .toULongLong()); - Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>( - ReadSetting(QStringLiteral("nand_system_size"), - QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB))) - .toULongLong()); - Settings::values.sdmc_size = static_cast<Settings::SDMCSize>( - ReadSetting(QStringLiteral("sdmc_size"), - QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB))) - .toULongLong()); qt_config->endGroup(); } @@ -540,8 +524,6 @@ void Config::ReadDebuggingValues() { Settings::values.reporting_services = ReadSetting(QStringLiteral("reporting_services"), false).toBool(); Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool(); - Settings::values.disable_cpu_opt = - ReadSetting(QStringLiteral("disable_cpu_opt"), false).toBool(); Settings::values.disable_macro_jit = ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool(); @@ -633,6 +615,34 @@ void Config::ReadPathValues() { qt_config->endGroup(); } +void Config::ReadCpuValues() { + qt_config->beginGroup(QStringLiteral("Cpu")); + + if (global) { + Settings::values.cpu_accuracy = static_cast<Settings::CPUAccuracy>( + ReadSetting(QStringLiteral("cpu_accuracy"), 0).toInt()); + + Settings::values.cpuopt_page_tables = + ReadSetting(QStringLiteral("cpuopt_page_tables"), true).toBool(); + Settings::values.cpuopt_block_linking = + ReadSetting(QStringLiteral("cpuopt_block_linking"), true).toBool(); + Settings::values.cpuopt_return_stack_buffer = + ReadSetting(QStringLiteral("cpuopt_return_stack_buffer"), true).toBool(); + Settings::values.cpuopt_fast_dispatcher = + ReadSetting(QStringLiteral("cpuopt_fast_dispatcher"), true).toBool(); + Settings::values.cpuopt_context_elimination = + ReadSetting(QStringLiteral("cpuopt_context_elimination"), true).toBool(); + Settings::values.cpuopt_const_prop = + ReadSetting(QStringLiteral("cpuopt_const_prop"), true).toBool(); + Settings::values.cpuopt_misc_ir = + ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool(); + Settings::values.cpuopt_reduce_misalign_checks = + ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool(); + } + + qt_config->endGroup(); +} + void Config::ReadRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); @@ -829,6 +839,7 @@ void Config::ReadValues() { ReadMiscellaneousValues(); } ReadCoreValues(); + ReadCpuValues(); ReadRendererValues(); ReadAudioValues(); ReadSystemValues(); @@ -929,6 +940,7 @@ void Config::SaveValues() { SaveMiscellaneousValues(); } SaveCoreValues(); + SaveCpuValues(); SaveRendererValues(); SaveAudioValues(); SaveSystemValues(); @@ -1006,18 +1018,7 @@ void Config::SaveDataStorageValues() { false); WriteSetting(QStringLiteral("gamecard_path"), QString::fromStdString(Settings::values.gamecard_path), QStringLiteral("")); - WriteSetting(QStringLiteral("nand_total_size"), - QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_total_size)), - QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB))); - WriteSetting(QStringLiteral("nand_user_size"), - QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_user_size)), - QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB))); - WriteSetting(QStringLiteral("nand_system_size"), - QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_system_size)), - QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB))); - WriteSetting(QStringLiteral("sdmc_size"), - QVariant::fromValue<u64>(static_cast<u64>(Settings::values.sdmc_size)), - QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB))); + qt_config->endGroup(); } @@ -1033,7 +1034,6 @@ void Config::SaveDebuggingValues() { WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false); WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); - WriteSetting(QStringLiteral("disable_cpu_opt"), Settings::values.disable_cpu_opt, false); WriteSetting(QStringLiteral("disable_macro_jit"), Settings::values.disable_macro_jit, false); qt_config->endGroup(); @@ -1097,6 +1097,32 @@ void Config::SavePathValues() { qt_config->endGroup(); } +void Config::SaveCpuValues() { + qt_config->beginGroup(QStringLiteral("Cpu")); + + if (global) { + WriteSetting(QStringLiteral("cpu_accuracy"), + static_cast<int>(Settings::values.cpu_accuracy), 0); + + WriteSetting(QStringLiteral("cpuopt_page_tables"), Settings::values.cpuopt_page_tables, + true); + WriteSetting(QStringLiteral("cpuopt_block_linking"), Settings::values.cpuopt_block_linking, + true); + WriteSetting(QStringLiteral("cpuopt_return_stack_buffer"), + Settings::values.cpuopt_return_stack_buffer, true); + WriteSetting(QStringLiteral("cpuopt_fast_dispatcher"), + Settings::values.cpuopt_fast_dispatcher, true); + WriteSetting(QStringLiteral("cpuopt_context_elimination"), + Settings::values.cpuopt_context_elimination, true); + WriteSetting(QStringLiteral("cpuopt_const_prop"), Settings::values.cpuopt_const_prop, true); + WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true); + WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), + Settings::values.cpuopt_reduce_misalign_checks, true); + } + + qt_config->endGroup(); +} + void Config::SaveRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 681f0bca5..8e815f829 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -49,6 +49,7 @@ private: void ReadDisabledAddOnValues(); void ReadMiscellaneousValues(); void ReadPathValues(); + void ReadCpuValues(); void ReadRendererValues(); void ReadShortcutValues(); void ReadSystemValues(); @@ -73,6 +74,7 @@ private: void SaveDisabledAddOnValues(); void SaveMiscellaneousValues(); void SavePathValues(); + void SaveCpuValues(); void SaveRendererValues(); void SaveShortcutValues(); void SaveSystemValues(); diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index 9aec1bd09..5f5d8e571 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui @@ -78,6 +78,16 @@ <string>Hotkeys</string> </attribute> </widget> + <widget class="ConfigureCpu" name="cpuTab"> + <attribute name="title"> + <string>CPU</string> + </attribute> + </widget> + <widget class="ConfigureCpuDebug" name="cpuDebugTab"> + <attribute name="title"> + <string>Debug</string> + </attribute> + </widget> <widget class="ConfigureGraphics" name="graphicsTab"> <attribute name="title"> <string>Graphics</string> @@ -159,6 +169,18 @@ <container>1</container> </customwidget> <customwidget> + <class>ConfigureCpu</class> + <extends>QWidget</extends> + <header>configuration/configure_cpu.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>ConfigureCpuDebug</class> + <extends>QWidget</extends> + <header>configuration/configure_cpu_debug.h</header> + <container>1</container> + </customwidget> + <customwidget> <class>ConfigureGraphics</class> <extends>QWidget</extends> <header>configuration/configure_graphics.h</header> diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp new file mode 100644 index 000000000..7493e5ffb --- /dev/null +++ b/src/yuzu/configuration/configure_cpu.cpp @@ -0,0 +1,61 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QComboBox> +#include <QMessageBox> + +#include "common/common_types.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/settings.h" +#include "ui_configure_cpu.h" +#include "yuzu/configuration/configure_cpu.h" + +ConfigureCpu::ConfigureCpu(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureCpu) { + ui->setupUi(this); + + SetConfiguration(); + + connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this, + &ConfigureCpu::AccuracyUpdated); +} + +ConfigureCpu::~ConfigureCpu() = default; + +void ConfigureCpu::SetConfiguration() { + const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); + + ui->accuracy->setEnabled(runtime_lock); + ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy)); +} + +void ConfigureCpu::AccuracyUpdated(int index) { + if (static_cast<Settings::CPUAccuracy>(index) == Settings::CPUAccuracy::DebugMode) { + const auto result = QMessageBox::warning(this, tr("Setting CPU to Debug Mode"), + tr("CPU Debug Mode is only intended for developer " + "use. Are you sure you want to enable this?"), + QMessageBox::Yes | QMessageBox::No); + if (result == QMessageBox::No) { + ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate)); + return; + } + } +} + +void ConfigureCpu::ApplyConfiguration() { + Settings::values.cpu_accuracy = + static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()); +} + +void ConfigureCpu::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QWidget::changeEvent(event); +} + +void ConfigureCpu::RetranslateUI() { + ui->retranslateUi(this); +} diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h new file mode 100644 index 000000000..e4741d3a4 --- /dev/null +++ b/src/yuzu/configuration/configure_cpu.h @@ -0,0 +1,33 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QWidget> +#include "core/settings.h" + +namespace Ui { +class ConfigureCpu; +} + +class ConfigureCpu : public QWidget { + Q_OBJECT + +public: + explicit ConfigureCpu(QWidget* parent = nullptr); + ~ConfigureCpu() override; + + void ApplyConfiguration(); + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + void AccuracyUpdated(int index); + + void SetConfiguration(); + + std::unique_ptr<Ui::ConfigureCpu> ui; +}; diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui new file mode 100644 index 000000000..bf6ea79bb --- /dev/null +++ b/src/yuzu/configuration/configure_cpu.ui @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureCpu</class> + <widget class="QWidget" name="ConfigureCpu"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>321</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout"> + <item> + <layout class="QVBoxLayout"> + <item> + <widget class="QGroupBox"> + <property name="title"> + <string>General</string> + </property> + <layout class="QVBoxLayout"> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel"> + <property name="text"> + <string>Accuracy:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="accuracy"> + <item> + <property name="text"> + <string>Accurate</string> + </property> + </item> + <item> + <property name="text"> + <string>Enable Debug Mode</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLabel"> + <property name="wordWrap"> + <bool>1</bool> + </property> + <property name="text"> + <string>We recommend setting accuracy to "Accurate".</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_disable_info"> + <property name="text"> + <string>CPU settings are available only when game is not running.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/yuzu/configuration/configure_cpu_debug.cpp b/src/yuzu/configuration/configure_cpu_debug.cpp new file mode 100644 index 000000000..3385b2cf6 --- /dev/null +++ b/src/yuzu/configuration/configure_cpu_debug.cpp @@ -0,0 +1,65 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QComboBox> + +#include "common/common_types.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/settings.h" +#include "ui_configure_cpu_debug.h" +#include "yuzu/configuration/configure_cpu_debug.h" + +ConfigureCpuDebug::ConfigureCpuDebug(QWidget* parent) + : QWidget(parent), ui(new Ui::ConfigureCpuDebug) { + ui->setupUi(this); + + SetConfiguration(); +} + +ConfigureCpuDebug::~ConfigureCpuDebug() = default; + +void ConfigureCpuDebug::SetConfiguration() { + const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); + + ui->cpuopt_page_tables->setEnabled(runtime_lock); + ui->cpuopt_page_tables->setChecked(Settings::values.cpuopt_page_tables); + ui->cpuopt_block_linking->setEnabled(runtime_lock); + ui->cpuopt_block_linking->setChecked(Settings::values.cpuopt_block_linking); + ui->cpuopt_return_stack_buffer->setEnabled(runtime_lock); + ui->cpuopt_return_stack_buffer->setChecked(Settings::values.cpuopt_return_stack_buffer); + ui->cpuopt_fast_dispatcher->setEnabled(runtime_lock); + ui->cpuopt_fast_dispatcher->setChecked(Settings::values.cpuopt_fast_dispatcher); + ui->cpuopt_context_elimination->setEnabled(runtime_lock); + ui->cpuopt_context_elimination->setChecked(Settings::values.cpuopt_context_elimination); + ui->cpuopt_const_prop->setEnabled(runtime_lock); + ui->cpuopt_const_prop->setChecked(Settings::values.cpuopt_const_prop); + ui->cpuopt_misc_ir->setEnabled(runtime_lock); + ui->cpuopt_misc_ir->setChecked(Settings::values.cpuopt_misc_ir); + ui->cpuopt_reduce_misalign_checks->setEnabled(runtime_lock); + ui->cpuopt_reduce_misalign_checks->setChecked(Settings::values.cpuopt_reduce_misalign_checks); +} + +void ConfigureCpuDebug::ApplyConfiguration() { + Settings::values.cpuopt_page_tables = ui->cpuopt_page_tables->isChecked(); + Settings::values.cpuopt_block_linking = ui->cpuopt_block_linking->isChecked(); + Settings::values.cpuopt_return_stack_buffer = ui->cpuopt_return_stack_buffer->isChecked(); + Settings::values.cpuopt_fast_dispatcher = ui->cpuopt_fast_dispatcher->isChecked(); + Settings::values.cpuopt_context_elimination = ui->cpuopt_context_elimination->isChecked(); + Settings::values.cpuopt_const_prop = ui->cpuopt_const_prop->isChecked(); + Settings::values.cpuopt_misc_ir = ui->cpuopt_misc_ir->isChecked(); + Settings::values.cpuopt_reduce_misalign_checks = ui->cpuopt_reduce_misalign_checks->isChecked(); +} + +void ConfigureCpuDebug::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QWidget::changeEvent(event); +} + +void ConfigureCpuDebug::RetranslateUI() { + ui->retranslateUi(this); +} diff --git a/src/yuzu/configuration/configure_cpu_debug.h b/src/yuzu/configuration/configure_cpu_debug.h new file mode 100644 index 000000000..c9941ef3b --- /dev/null +++ b/src/yuzu/configuration/configure_cpu_debug.h @@ -0,0 +1,31 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QWidget> +#include "core/settings.h" + +namespace Ui { +class ConfigureCpuDebug; +} + +class ConfigureCpuDebug : public QWidget { + Q_OBJECT + +public: + explicit ConfigureCpuDebug(QWidget* parent = nullptr); + ~ConfigureCpuDebug() override; + + void ApplyConfiguration(); + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + void SetConfiguration(); + + std::unique_ptr<Ui::ConfigureCpuDebug> ui; +}; diff --git a/src/yuzu/configuration/configure_cpu_debug.ui b/src/yuzu/configuration/configure_cpu_debug.ui new file mode 100644 index 000000000..a90dc64fe --- /dev/null +++ b/src/yuzu/configuration/configure_cpu_debug.ui @@ -0,0 +1,174 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureCpuDebug</class> + <widget class="QWidget" name="ConfigureCpuDebug"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>321</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout"> + <item> + <layout class="QVBoxLayout"> + <item> + <widget class="QGroupBox"> + <property name="title"> + <string>Toggle CPU Optimizations</string> + </property> + <layout class="QVBoxLayout"> + <item> + <widget class="QLabel"> + <property name="wordWrap"> + <bool>1</bool> + </property> + <property name="text"> + <string> + <div> + <b>For debugging only.</b> + <br> + If you're not sure what these do, keep all of these enabled. + <br> + These settings only take effect when CPU Accuracy is "Debug Mode". + </div> + </string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="cpuopt_page_tables"> + <property name="text"> + <string>Enable inline page tables</string> + </property> + <property name="toolTip"> + <string> + <div style="white-space: nowrap">This optimization speeds up memory accesses by the guest program.</div> + <div style="white-space: nowrap">Enabling it inlines accesses to PageTable::pointers into emitted code.</div> + <div style="white-space: nowrap">Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.</div> + </string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="cpuopt_block_linking"> + <property name="text"> + <string>Enable block linking</string> + </property> + <property name="toolTip"> + <string> + <div>This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.</div> + </string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="cpuopt_return_stack_buffer"> + <property name="text"> + <string>Enable return stack buffer</string> + </property> + <property name="toolTip"> + <string> + <div>This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.</div> + </string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="cpuopt_fast_dispatcher"> + <property name="text"> + <string>Enable fast dispatcher</string> + </property> + <property name="toolTip"> + <string> + <div>Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.</div> + </string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="cpuopt_context_elimination"> + <property name="text"> + <string>Enable context elimination</string> + </property> + <property name="toolTip"> + <string> + <div>Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.</div> + </string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="cpuopt_const_prop"> + <property name="text"> + <string>Enable constant propagation</string> + </property> + <property name="toolTip"> + <string> + <div>Enables IR optimizations that involve constant propagation.</div> + </string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="cpuopt_misc_ir"> + <property name="text"> + <string>Enable miscellaneous optimizations</string> + </property> + <property name="toolTip"> + <string> + <div>Enables miscellaneous IR optimizations.</div> + </string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="cpuopt_reduce_misalign_checks"> + <property name="text"> + <string>Enable misalignment check reduction</string> + </property> + <property name="toolTip"> + <string> + <div style="white-space: nowrap">When enabled, a misalignment is only triggered when an access crosses a page boundary.</div> + <div style="white-space: nowrap">When disabled, a misalignment is triggered on all misaligned accesses.</div> + </string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_disable_info"> + <property name="text"> + <string>CPU settings are available only when game is not running.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 2c77441fd..d0e71dd60 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -36,7 +36,6 @@ void ConfigureDebug::SetConfiguration() { ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); ui->reporting_services->setChecked(Settings::values.reporting_services); ui->quest_flag->setChecked(Settings::values.quest_flag); - ui->disable_cpu_opt->setChecked(Settings::values.disable_cpu_opt); ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn()); ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); @@ -51,7 +50,6 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); Settings::values.reporting_services = ui->reporting_services->isChecked(); Settings::values.quest_flag = ui->quest_flag->isChecked(); - Settings::values.disable_cpu_opt = ui->disable_cpu_opt->isChecked(); Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); Debugger::ToggleConsole(); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 46f0208c6..272bdd6b8 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -228,13 +228,6 @@ </property> </widget> </item> - <item> - <widget class="QCheckBox" name="disable_cpu_opt"> - <property name="text"> - <string>Disable CPU JIT optimizations</string> - </property> - </widget> - </item> </layout> </widget> </item> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 5918e9972..a5afb354f 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -42,6 +42,8 @@ void ConfigureDialog::ApplyConfiguration() { ui->filesystemTab->applyConfiguration(); ui->inputTab->ApplyConfiguration(); ui->hotkeysTab->ApplyConfiguration(registry); + ui->cpuTab->ApplyConfiguration(); + ui->cpuDebugTab->ApplyConfiguration(); ui->graphicsTab->ApplyConfiguration(); ui->graphicsAdvancedTab->ApplyConfiguration(); ui->audioTab->ApplyConfiguration(); @@ -76,9 +78,10 @@ void ConfigureDialog::RetranslateUI() { Q_DECLARE_METATYPE(QList<QWidget*>); void ConfigureDialog::PopulateSelectionList() { - const std::array<std::pair<QString, QList<QWidget*>>, 5> items{ + const std::array<std::pair<QString, QList<QWidget*>>, 6> items{ {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}}, {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, + {tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}}, {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}}, {tr("Audio"), {ui->audioTab}}, {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, @@ -107,6 +110,8 @@ void ConfigureDialog::UpdateVisibleTabs() { {ui->profileManagerTab, tr("Profiles")}, {ui->inputTab, tr("Input")}, {ui->hotkeysTab, tr("Hotkeys")}, + {ui->cpuTab, tr("CPU")}, + {ui->cpuDebugTab, tr("Debug")}, {ui->graphicsTab, tr("Graphics")}, {ui->graphicsAdvancedTab, tr("Advanced")}, {ui->audioTab, tr("Audio")}, diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp index 835ee821c..a089f5733 100644 --- a/src/yuzu/configuration/configure_filesystem.cpp +++ b/src/yuzu/configuration/configure_filesystem.cpp @@ -11,19 +11,6 @@ #include "yuzu/configuration/configure_filesystem.h" #include "yuzu/uisettings.h" -namespace { - -template <typename T> -void SetComboBoxFromData(QComboBox* combo_box, T data) { - const auto index = combo_box->findData(QVariant::fromValue(static_cast<u64>(data))); - if (index >= combo_box->count() || index < 0) - return; - - combo_box->setCurrentIndex(index); -} - -} // Anonymous namespace - ConfigureFilesystem::ConfigureFilesystem(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureFilesystem>()) { ui->setupUi(this); @@ -73,11 +60,6 @@ void ConfigureFilesystem::setConfiguration() { ui->cache_game_list->setChecked(UISettings::values.cache_game_list); - SetComboBoxFromData(ui->nand_size, Settings::values.nand_total_size); - SetComboBoxFromData(ui->usrnand_size, Settings::values.nand_user_size); - SetComboBoxFromData(ui->sysnand_size, Settings::values.nand_system_size); - SetComboBoxFromData(ui->sdmc_size, Settings::values.sdmc_size); - UpdateEnabledControls(); } @@ -98,15 +80,6 @@ void ConfigureFilesystem::applyConfiguration() { Settings::values.dump_nso = ui->dump_nso->isChecked(); UISettings::values.cache_game_list = ui->cache_game_list->isChecked(); - - Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>( - ui->nand_size->itemData(ui->nand_size->currentIndex()).toULongLong()); - Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>( - ui->nand_size->itemData(ui->sysnand_size->currentIndex()).toULongLong()); - Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>( - ui->nand_size->itemData(ui->usrnand_size->currentIndex()).toULongLong()); - Settings::values.sdmc_size = static_cast<Settings::SDMCSize>( - ui->nand_size->itemData(ui->sdmc_size->currentIndex()).toULongLong()); } void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) { diff --git a/src/yuzu/configuration/configure_filesystem.ui b/src/yuzu/configuration/configure_filesystem.ui index 58cd07f52..84bea0600 100644 --- a/src/yuzu/configuration/configure_filesystem.ui +++ b/src/yuzu/configuration/configure_filesystem.ui @@ -116,127 +116,6 @@ </widget> </item> <item> - <widget class="QGroupBox" name="groupBox_3"> - <property name="title"> - <string>Storage Sizes</string> - </property> - <layout class="QGridLayout" name="gridLayout_3"> - <item row="3" column="0"> - <widget class="QLabel" name="label_5"> - <property name="text"> - <string>SD Card</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string>System NAND</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QComboBox" name="sysnand_size"> - <item> - <property name="text"> - <string>2.5 GB</string> - </property> - </item> - </widget> - </item> - <item row="3" column="1"> - <widget class="QComboBox" name="sdmc_size"> - <property name="currentText"> - <string>32 GB</string> - </property> - <item> - <property name="text"> - <string>1 GB</string> - </property> - </item> - <item> - <property name="text"> - <string>2 GB</string> - </property> - </item> - <item> - <property name="text"> - <string>4 GB</string> - </property> - </item> - <item> - <property name="text"> - <string>8 GB</string> - </property> - </item> - <item> - <property name="text"> - <string>16 GB</string> - </property> - </item> - <item> - <property name="text"> - <string>32 GB</string> - </property> - </item> - <item> - <property name="text"> - <string>64 GB</string> - </property> - </item> - <item> - <property name="text"> - <string>128 GB</string> - </property> - </item> - <item> - <property name="text"> - <string>256 GB</string> - </property> - </item> - <item> - <property name="text"> - <string>1 TB</string> - </property> - </item> - </widget> - </item> - <item row="2" column="1"> - <widget class="QComboBox" name="usrnand_size"> - <item> - <property name="text"> - <string>26 GB</string> - </property> - </item> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_6"> - <property name="text"> - <string>User NAND</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_7"> - <property name="text"> - <string>NAND</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QComboBox" name="nand_size"> - <item> - <property name="text"> - <string>29.1 GB</string> - </property> - </item> - </widget> - </item> - </layout> - </widget> - </item> - <item> <widget class="QGroupBox" name="groupBox_4"> <property name="title"> <string>Patch Manager</string> diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 1fb62d1cf..20316c9cc 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -65,6 +65,8 @@ void ConfigureGeneral::ApplyConfiguration() { Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() == Qt::Checked); Settings::values.frame_limit.SetValue(ui->frame_limit->value()); + } + if (Settings::values.use_multi_core.UsingGlobal()) { Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked()); } } else { diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index bfb600df0..ab7fc7a24 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -531,8 +531,8 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { UISettings::GameDir& game_dir = *selected.data(GameListDir::GameDirRole).value<UISettings::GameDir*>(); - QAction* move_up = context_menu.addAction(tr(u8"\U000025b2 Move Up")); - QAction* move_down = context_menu.addAction(tr(u8"\U000025bc Move Down ")); + QAction* move_up = context_menu.addAction(tr("\u25B2 Move Up")); + QAction* move_down = context_menu.addAction(tr("\u25bc Move Down")); QAction* open_directory_location = context_menu.addAction(tr("Open Directory Location")); const int row = selected.row(); diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp new file mode 100644 index 000000000..06b0b1874 --- /dev/null +++ b/src/yuzu/install_dialog.cpp @@ -0,0 +1,72 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QCheckBox> +#include <QDialogButtonBox> +#include <QFileInfo> +#include <QHBoxLayout> +#include <QLabel> +#include <QListWidget> +#include <QVBoxLayout> +#include "yuzu/install_dialog.h" +#include "yuzu/uisettings.h" + +InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialog(parent) { + file_list = new QListWidget(this); + + for (const QString& file : files) { + QListWidgetItem* item = new QListWidgetItem(QFileInfo(file).fileName(), file_list); + item->setData(Qt::UserRole, file); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Checked); + } + + file_list->setMinimumWidth((file_list->sizeHintForColumn(0) * 11) / 10); + + vbox_layout = new QVBoxLayout; + + hbox_layout = new QHBoxLayout; + + description = new QLabel(tr("Please confirm these are the files you wish to install.")); + + update_description = + new QLabel(tr("Installing an Update or DLC will overwrite the previously installed one.")); + + buttons = new QDialogButtonBox; + buttons->addButton(QDialogButtonBox::Cancel); + buttons->addButton(tr("Install"), QDialogButtonBox::AcceptRole); + + connect(buttons, &QDialogButtonBox::accepted, this, &InstallDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &InstallDialog::reject); + + hbox_layout->addWidget(buttons); + + vbox_layout->addWidget(description); + vbox_layout->addWidget(update_description); + vbox_layout->addWidget(file_list); + vbox_layout->addLayout(hbox_layout); + + setLayout(vbox_layout); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setWindowTitle(tr("Install Files to NAND")); +} + +InstallDialog::~InstallDialog() = default; + +QStringList InstallDialog::GetFiles() const { + QStringList files; + + for (int i = 0; i < file_list->count(); ++i) { + const QListWidgetItem* item = file_list->item(i); + if (item->checkState() == Qt::Checked) { + files.append(item->data(Qt::UserRole).toString()); + } + } + + return files; +} + +int InstallDialog::GetMinimumWidth() const { + return file_list->width(); +} diff --git a/src/yuzu/install_dialog.h b/src/yuzu/install_dialog.h new file mode 100644 index 000000000..e4aba1b06 --- /dev/null +++ b/src/yuzu/install_dialog.h @@ -0,0 +1,36 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <QDialog> + +class QCheckBox; +class QDialogButtonBox; +class QHBoxLayout; +class QLabel; +class QListWidget; +class QVBoxLayout; + +class InstallDialog : public QDialog { + Q_OBJECT + +public: + explicit InstallDialog(QWidget* parent, const QStringList& files); + ~InstallDialog() override; + + QStringList GetFiles() const; + bool ShouldOverwriteFiles() const; + int GetMinimumWidth() const; + +private: + QListWidget* file_list; + + QVBoxLayout* vbox_layout; + QHBoxLayout* hbox_layout; + + QLabel* description; + QLabel* update_description; + QDialogButtonBox* buttons; +}; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index ab4b0e4e5..9f758605a 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -107,6 +107,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/game_list.h" #include "yuzu/game_list_p.h" #include "yuzu/hotkeys.h" +#include "yuzu/install_dialog.h" #include "yuzu/loading_screen.h" #include "yuzu/main.h" #include "yuzu/uisettings.h" @@ -851,6 +852,9 @@ void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::OpenPerGameGeneralRequested, this, &GMainWindow::OnGameListOpenPerGameProperties); + connect(this, &GMainWindow::UpdateInstallProgress, this, + &GMainWindow::IncrementInstallProgress); + connect(this, &GMainWindow::EmulationStarting, render_window, &GRenderWindow::OnEmulationStarting); connect(this, &GMainWindow::EmulationStopping, render_window, @@ -1597,187 +1601,255 @@ void GMainWindow::OnMenuLoadFolder() { } } +void GMainWindow::IncrementInstallProgress() { + install_progress->setValue(install_progress->value() + 1); +} + void GMainWindow::OnMenuInstallToNAND() { const QString file_filter = tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive " - "(*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge " + "(*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge " "Image (*.xci)"); - QString filename = QFileDialog::getOpenFileName(this, tr("Install File"), - UISettings::values.roms_path, file_filter); - if (filename.isEmpty()) { + QStringList filenames = QFileDialog::getOpenFileNames( + this, tr("Install Files"), UISettings::values.roms_path, file_filter); + + if (filenames.isEmpty()) { return; } + InstallDialog installDialog(this, filenames); + if (installDialog.exec() == QDialog::Rejected) { + return; + } + + const QStringList files = installDialog.GetFiles(); + + if (files.isEmpty()) { + return; + } + + int remaining = filenames.size(); + + // This would only overflow above 2^43 bytes (8.796 TB) + int total_size = 0; + for (const QString& file : files) { + total_size += static_cast<int>(QFile(file).size() / 0x1000); + } + if (total_size < 0) { + LOG_CRITICAL(Frontend, "Attempting to install too many files, aborting."); + return; + } + + QStringList new_files{}; // Newly installed files that do not yet exist in the NAND + QStringList overwritten_files{}; // Files that overwrote those existing in the NAND + QStringList failed_files{}; // Files that failed to install due to errors + + ui.action_Install_File_NAND->setEnabled(false); + + install_progress = new QProgressDialog(QStringLiteral(""), tr("Cancel"), 0, total_size, this); + install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & + ~Qt::WindowMaximizeButtonHint); + install_progress->setAttribute(Qt::WA_DeleteOnClose, true); + install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40); + install_progress->show(); + + for (const QString& file : files) { + install_progress->setWindowTitle(tr("%n file(s) remaining", "", remaining)); + install_progress->setLabelText( + tr("Installing file \"%1\"...").arg(QFileInfo(file).fileName())); + + QFuture<InstallResult> future; + InstallResult result; + + if (file.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) || + file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { + + future = QtConcurrent::run([this, &file] { return InstallNSPXCI(file); }); + + while (!future.isFinished()) { + QCoreApplication::processEvents(); + } + + result = future.result(); + + } else { + result = InstallNCA(file); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + switch (result) { + case InstallResult::Success: + new_files.append(QFileInfo(file).fileName()); + break; + case InstallResult::Overwrite: + overwritten_files.append(QFileInfo(file).fileName()); + break; + case InstallResult::Failure: + failed_files.append(QFileInfo(file).fileName()); + break; + } + + --remaining; + } + + install_progress->close(); + + const QString install_results = + (new_files.isEmpty() ? QStringLiteral("") + : tr("%n file(s) were newly installed\n", "", new_files.size())) + + (overwritten_files.isEmpty() + ? QStringLiteral("") + : tr("%n file(s) were overwritten\n", "", overwritten_files.size())) + + (failed_files.isEmpty() ? QStringLiteral("") + : tr("%n file(s) failed to install\n", "", failed_files.size())); + + QMessageBox::information(this, tr("Install Results"), install_results); + game_list->PopulateAsync(UISettings::values.game_dirs); + FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + + "game_list"); + ui.action_Install_File_NAND->setEnabled(true); +} + +InstallResult GMainWindow::InstallNSPXCI(const QString& filename) { const auto qt_raw_copy = [this](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, std::size_t block_size) { - if (src == nullptr || dest == nullptr) + if (src == nullptr || dest == nullptr) { return false; - if (!dest->Resize(src->GetSize())) + } + if (!dest->Resize(src->GetSize())) { return false; + } std::array<u8, 0x1000> buffer{}; - const int progress_maximum = static_cast<int>(src->GetSize() / buffer.size()); - - QProgressDialog progress( - tr("Installing file \"%1\"...").arg(QString::fromStdString(src->GetName())), - tr("Cancel"), 0, progress_maximum, this); - progress.setWindowModality(Qt::WindowModal); for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { - if (progress.wasCanceled()) { + if (install_progress->wasCanceled()) { dest->Resize(0); return false; } - const int progress_value = static_cast<int>(i / buffer.size()); - progress.setValue(progress_value); + emit UpdateInstallProgress(); const auto read = src->Read(buffer.data(), buffer.size(), i); dest->Write(buffer.data(), read, i); } - return true; }; - const auto success = [this]() { - QMessageBox::information(this, tr("Successfully Installed"), - tr("The file was successfully installed.")); - game_list->PopulateAsync(UISettings::values.game_dirs); - FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + - DIR_SEP + "game_list"); - }; - - const auto failed = [this]() { - QMessageBox::warning( - this, tr("Failed to Install"), - tr("There was an error while attempting to install the provided file. It " - "could have an incorrect format or be missing metadata. Please " - "double-check your file and try again.")); - }; - - const auto overwrite = [this]() { - return QMessageBox::question(this, tr("Failed to Install"), - tr("The file you are attempting to install already exists " - "in the cache. Would you like to overwrite it?")) == - QMessageBox::Yes; - }; - - if (filename.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) || - filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { - std::shared_ptr<FileSys::NSP> nsp; - if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { - nsp = std::make_shared<FileSys::NSP>( - vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); - if (nsp->IsExtractedType()) - failed(); - } else { - const auto xci = std::make_shared<FileSys::XCI>( - vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); - nsp = xci->GetSecurePartitionNSP(); - } - - if (nsp->GetStatus() != Loader::ResultStatus::Success) { - failed(); - return; - } - const auto res = Core::System::GetInstance() - .GetFileSystemController() - .GetUserNANDContents() - ->InstallEntry(*nsp, false, qt_raw_copy); - if (res == FileSys::InstallResult::Success) { - success(); - } else { - if (res == FileSys::InstallResult::ErrorAlreadyExists) { - if (overwrite()) { - const auto res2 = Core::System::GetInstance() - .GetFileSystemController() - .GetUserNANDContents() - ->InstallEntry(*nsp, true, qt_raw_copy); - if (res2 == FileSys::InstallResult::Success) { - success(); - } else { - failed(); - } - } - } else { - failed(); - } + std::shared_ptr<FileSys::NSP> nsp; + if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { + nsp = std::make_shared<FileSys::NSP>( + vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); + if (nsp->IsExtractedType()) { + return InstallResult::Failure; } } else { - const auto nca = std::make_shared<FileSys::NCA>( + const auto xci = std::make_shared<FileSys::XCI>( vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); - const auto id = nca->GetStatus(); + nsp = xci->GetSecurePartitionNSP(); + } - // Game updates necessary are missing base RomFS - if (id != Loader::ResultStatus::Success && - id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { - failed(); - return; - } + if (nsp->GetStatus() != Loader::ResultStatus::Success) { + return InstallResult::Failure; + } + const auto res = + Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->InstallEntry( + *nsp, true, qt_raw_copy); + if (res == FileSys::InstallResult::Success) { + return InstallResult::Success; + } else if (res == FileSys::InstallResult::OverwriteExisting) { + return InstallResult::Overwrite; + } else { + return InstallResult::Failure; + } +} - const QStringList tt_options{tr("System Application"), - tr("System Archive"), - tr("System Application Update"), - tr("Firmware Package (Type A)"), - tr("Firmware Package (Type B)"), - tr("Game"), - tr("Game Update"), - tr("Game DLC"), - tr("Delta Title")}; - bool ok; - const auto item = QInputDialog::getItem( - this, tr("Select NCA Install Type..."), - tr("Please select the type of title you would like to install this NCA as:\n(In " - "most instances, the default 'Game' is fine.)"), - tt_options, 5, false, &ok); - - auto index = tt_options.indexOf(item); - if (!ok || index == -1) { - QMessageBox::warning(this, tr("Failed to Install"), - tr("The title type you selected for the NCA is invalid.")); - return; +InstallResult GMainWindow::InstallNCA(const QString& filename) { + const auto qt_raw_copy = [this](const FileSys::VirtualFile& src, + const FileSys::VirtualFile& dest, std::size_t block_size) { + if (src == nullptr || dest == nullptr) { + return false; } - - // If index is equal to or past Game, add the jump in TitleType. - if (index >= 5) { - index += static_cast<size_t>(FileSys::TitleType::Application) - - static_cast<size_t>(FileSys::TitleType::FirmwarePackageB); + if (!dest->Resize(src->GetSize())) { + return false; } - FileSys::InstallResult res; - if (index >= static_cast<s32>(FileSys::TitleType::Application)) { - res = Core::System::GetInstance() - .GetFileSystemController() - .GetUserNANDContents() - ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, - qt_raw_copy); - } else { - res = Core::System::GetInstance() - .GetFileSystemController() - .GetSystemNANDContents() - ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, - qt_raw_copy); - } + std::array<u8, 0x1000> buffer{}; - if (res == FileSys::InstallResult::Success) { - success(); - } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { - if (overwrite()) { - const auto res2 = Core::System::GetInstance() - .GetFileSystemController() - .GetUserNANDContents() - ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), - true, qt_raw_copy); - if (res2 == FileSys::InstallResult::Success) { - success(); - } else { - failed(); - } + for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { + if (install_progress->wasCanceled()) { + dest->Resize(0); + return false; } - } else { - failed(); + + emit UpdateInstallProgress(); + + const auto read = src->Read(buffer.data(), buffer.size(), i); + dest->Write(buffer.data(), read, i); } + return true; + }; + + const auto nca = + std::make_shared<FileSys::NCA>(vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); + const auto id = nca->GetStatus(); + + // Game updates necessary are missing base RomFS + if (id != Loader::ResultStatus::Success && + id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { + return InstallResult::Failure; + } + + const QStringList tt_options{tr("System Application"), + tr("System Archive"), + tr("System Application Update"), + tr("Firmware Package (Type A)"), + tr("Firmware Package (Type B)"), + tr("Game"), + tr("Game Update"), + tr("Game DLC"), + tr("Delta Title")}; + bool ok; + const auto item = QInputDialog::getItem( + this, tr("Select NCA Install Type..."), + tr("Please select the type of title you would like to install this NCA as:\n(In " + "most instances, the default 'Game' is fine.)"), + tt_options, 5, false, &ok); + + auto index = tt_options.indexOf(item); + if (!ok || index == -1) { + QMessageBox::warning(this, tr("Failed to Install"), + tr("The title type you selected for the NCA is invalid.")); + return InstallResult::Failure; + } + + // If index is equal to or past Game, add the jump in TitleType. + if (index >= 5) { + index += static_cast<size_t>(FileSys::TitleType::Application) - + static_cast<size_t>(FileSys::TitleType::FirmwarePackageB); + } + + FileSys::InstallResult res; + if (index >= static_cast<s32>(FileSys::TitleType::Application)) { + res = Core::System::GetInstance() + .GetFileSystemController() + .GetUserNANDContents() + ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy); + } else { + res = Core::System::GetInstance() + .GetFileSystemController() + .GetSystemNANDContents() + ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy); + } + + if (res == FileSys::InstallResult::Success) { + return InstallResult::Success; + } else if (res == FileSys::InstallResult::OverwriteExisting) { + return InstallResult::Overwrite; + } else { + return InstallResult::Failure; } } diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 8e3d39c38..adff65fb5 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -28,6 +28,7 @@ class MicroProfileDialog; class ProfilerWidget; class QLabel; class QPushButton; +class QProgressDialog; class WaitTreeWidget; enum class GameListOpenTarget; class GameListPlaceholder; @@ -47,6 +48,12 @@ enum class EmulatedDirectoryTarget { SDMC, }; +enum class InstallResult { + Success, + Overwrite, + Failure, +}; + enum class ReinitializeKeyBehavior { NoWarning, Warning, @@ -102,6 +109,8 @@ signals: // Signal that tells widgets to update icons to use the current theme void UpdateThemedIcons(); + void UpdateInstallProgress(); + void ErrorDisplayFinished(); void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid); @@ -198,6 +207,7 @@ private slots: void OnGameListOpenPerGameProperties(const std::string& file); void OnMenuLoadFile(); void OnMenuLoadFolder(); + void IncrementInstallProgress(); void OnMenuInstallToNAND(); void OnMenuRecentFile(); void OnConfigure(); @@ -218,6 +228,8 @@ private slots: private: std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); + InstallResult InstallNSPXCI(const QString& filename); + InstallResult InstallNCA(const QString& filename); void UpdateWindowTitle(const std::string& title_name = {}, const std::string& title_version = {}); void UpdateStatusBar(); @@ -272,6 +284,9 @@ private: HotkeyRegistry hotkey_registry; + // Install progress dialog + QProgressDialog* install_progress; + protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index bee6e107e..c3a1d715e 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -130,7 +130,7 @@ <bool>true</bool> </property> <property name="text"> - <string>Install File to NAND...</string> + <string>Install Files to NAND...</string> </property> </action> <action name="action_Load_File"> |