diff options
Diffstat (limited to 'src/yuzu')
| -rw-r--r-- | src/yuzu/bootmanager.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 36 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.h | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics_advanced.cpp | 10 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics_advanced.h | 1 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics_advanced.ui | 7 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input_player.cpp | 76 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_mouse_advanced.cpp | 39 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_ui.cpp | 26 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_ui.ui | 45 | ||||
| -rw-r--r-- | src/yuzu/game_list.cpp | 34 | ||||
| -rw-r--r-- | src/yuzu/game_list.h | 15 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 226 | ||||
| -rw-r--r-- | src/yuzu/main.h | 9 | ||||
| -rw-r--r-- | src/yuzu/uisettings.h | 2 | 
15 files changed, 416 insertions, 114 deletions
| diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 5738787ac..8fc322b30 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -567,7 +567,7 @@ void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_p      screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);      renderer.RequestScreenshot(          screenshot_image.bits(), -        [=] { +        [=, this] {              const std::string std_screenshot_path = screenshot_path.toStdString();              if (screenshot_image.mirrored(false, true).save(screenshot_path)) {                  LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 59a193edd..cb71b8d11 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -578,7 +578,6 @@ void Config::ReadPathValues() {      UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString();      UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString(); -    UISettings::values.screenshot_path = ReadSetting(QStringLiteral("screenshotPath")).toString();      UISettings::values.game_dir_deprecated =          ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString();      UISettings::values.game_dir_deprecated_deepscan = @@ -666,8 +665,6 @@ void Config::ReadRendererValues() {                        QStringLiteral("use_asynchronous_shaders"), false);      ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),                        true); -    ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false); -      ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0);      ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0);      ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0); @@ -675,6 +672,22 @@ void Config::ReadRendererValues() {      qt_config->endGroup();  } +void Config::ReadScreenshotValues() { +    qt_config->beginGroup(QStringLiteral("Screenshots")); + +    UISettings::values.enable_screenshot_save_as = +        ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool(); +    FileUtil::GetUserPath( +        FileUtil::UserPath::ScreenshotsDir, +        qt_config +            ->value(QStringLiteral("screenshot_path"), QString::fromStdString(FileUtil::GetUserPath( +                                                           FileUtil::UserPath::ScreenshotsDir))) +            .toString() +            .toStdString()); + +    qt_config->endGroup(); +} +  void Config::ReadShortcutValues() {      qt_config->beginGroup(QStringLiteral("Shortcuts")); @@ -756,6 +769,7 @@ void Config::ReadUIValues() {      ReadUIGamelistValues();      ReadUILayoutValues();      ReadPathValues(); +    ReadScreenshotValues();      ReadShortcutValues();      UISettings::values.single_window_mode = @@ -1085,7 +1099,6 @@ void Config::SavePathValues() {      WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path);      WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path); -    WriteSetting(QStringLiteral("screenshotPath"), UISettings::values.screenshot_path);      qt_config->beginWriteArray(QStringLiteral("gamedirs"));      for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {          qt_config->setArrayIndex(i); @@ -1153,9 +1166,6 @@ void Config::SaveRendererValues() {                         Settings::values.use_asynchronous_shaders, false);      WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,                         true); -    WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, -                       false); -      // Cast to double because Qt's written float values are not human-readable      WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0);      WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0); @@ -1164,6 +1174,17 @@ void Config::SaveRendererValues() {      qt_config->endGroup();  } +void Config::SaveScreenshotValues() { +    qt_config->beginGroup(QStringLiteral("Screenshots")); + +    WriteSetting(QStringLiteral("enable_screenshot_save_as"), +                 UISettings::values.enable_screenshot_save_as); +    WriteSetting(QStringLiteral("screenshot_path"), +                 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))); + +    qt_config->endGroup(); +} +  void Config::SaveShortcutValues() {      qt_config->beginGroup(QStringLiteral("Shortcuts")); @@ -1226,6 +1247,7 @@ void Config::SaveUIValues() {      SaveUIGamelistValues();      SaveUILayoutValues();      SavePathValues(); +    SaveScreenshotValues();      SaveShortcutValues();      WriteSetting(QStringLiteral("singleWindowMode"), UISettings::values.single_window_mode, true); diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 8e815f829..e5f39b040 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -51,6 +51,7 @@ private:      void ReadPathValues();      void ReadCpuValues();      void ReadRendererValues(); +    void ReadScreenshotValues();      void ReadShortcutValues();      void ReadSystemValues();      void ReadUIValues(); @@ -76,6 +77,7 @@ private:      void SavePathValues();      void SaveCpuValues();      void SaveRendererValues(); +    void SaveScreenshotValues();      void SaveShortcutValues();      void SaveSystemValues();      void SaveUIValues(); diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index 8b9180811..c5d1a778c 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -25,14 +25,12 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {      ui->use_vsync->setEnabled(runtime_lock);      ui->use_assembly_shaders->setEnabled(runtime_lock);      ui->use_asynchronous_shaders->setEnabled(runtime_lock); -    ui->force_30fps_mode->setEnabled(runtime_lock);      ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);      ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());      ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());      ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());      ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); -    ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue());      if (Settings::configuring_global) {          ui->gpu_accuracy->setCurrentIndex( @@ -78,9 +76,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {          if (Settings::values.use_fast_gpu_time.UsingGlobal()) {              Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked());          } -        if (Settings::values.force_30fps_mode.UsingGlobal()) { -            Settings::values.force_30fps_mode.SetValue(ui->force_30fps_mode->isChecked()); -        }          if (Settings::values.max_anisotropy.UsingGlobal()) {              Settings::values.max_anisotropy.SetValue(                  ui->anisotropic_filtering_combobox->currentIndex()); @@ -97,8 +92,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {                                                   use_asynchronous_shaders);          ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,                                                   ui->use_fast_gpu_time, use_fast_gpu_time); -        ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode, -                                                 ui->force_30fps_mode, force_30fps_mode);          ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,                                                   ui->anisotropic_filtering_combobox); @@ -132,7 +125,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {          ui->use_asynchronous_shaders->setEnabled(              Settings::values.use_asynchronous_shaders.UsingGlobal());          ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); -        ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal());          ui->anisotropic_filtering_combobox->setEnabled(              Settings::values.max_anisotropy.UsingGlobal()); @@ -149,8 +141,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {          Settings::values.use_asynchronous_shaders, use_asynchronous_shaders);      ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, "use_fast_gpu_time",                                              Settings::values.use_fast_gpu_time, use_fast_gpu_time); -    ConfigurationShared::SetColoredTristate(ui->force_30fps_mode, "force_30fps_mode", -                                            Settings::values.force_30fps_mode, force_30fps_mode);      ConfigurationShared::SetColoredComboBox(          ui->gpu_accuracy, ui->label_gpu_accuracy, "label_gpu_accuracy",          static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index 3c4f6f7bb..e61b571c7 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h @@ -38,5 +38,4 @@ private:      ConfigurationShared::CheckState use_assembly_shaders;      ConfigurationShared::CheckState use_asynchronous_shaders;      ConfigurationShared::CheckState use_fast_gpu_time; -    ConfigurationShared::CheckState force_30fps_mode;  }; diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 6a0d29c27..a793c803d 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui @@ -97,13 +97,6 @@           </widget>          </item>          <item> -         <widget class="QCheckBox" name="force_30fps_mode"> -          <property name="text"> -           <string>Force 30 FPS mode</string> -          </property> -         </widget> -        </item> -        <item>           <widget class="QCheckBox" name="use_fast_gpu_time">            <property name="text">             <string>Use Fast GPU Time</string> diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 00433926d..b1850bc95 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -280,9 +280,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i          }          button->setContextMenuPolicy(Qt::CustomContextMenu); -        connect(button, &QPushButton::clicked, [=] { +        connect(button, &QPushButton::clicked, [=, this] {              HandleClick(button_map[button_id], -                        [=](Common::ParamPackage params) { +                        [=, this](Common::ParamPackage params) {                              // Workaround for ZL & ZR for analog triggers like on XBOX controllors.                              // Analog triggers (from controllers like the XBOX controller) would not                              // work due to a different range of their signals (from 0 to 255 on @@ -300,19 +300,20 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i                          },                          InputCommon::Polling::DeviceType::Button);          }); -        connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { -            QMenu context_menu; -            context_menu.addAction(tr("Clear"), [&] { -                buttons_param[button_id].Clear(); -                button_map[button_id]->setText(tr("[not set]")); -            }); -            context_menu.addAction(tr("Restore Default"), [&] { -                buttons_param[button_id] = Common::ParamPackage{ -                    InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; -                button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); -            }); -            context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); -        }); +        connect(button, &QPushButton::customContextMenuRequested, +                [=, this](const QPoint& menu_location) { +                    QMenu context_menu; +                    context_menu.addAction(tr("Clear"), [&] { +                        buttons_param[button_id].Clear(); +                        button_map[button_id]->setText(tr("[not set]")); +                    }); +                    context_menu.addAction(tr("Restore Default"), [&] { +                        buttons_param[button_id] = Common::ParamPackage{ +                            InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; +                        button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); +                    }); +                    context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); +                });      }      for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { @@ -323,16 +324,16 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i              }              analog_button->setContextMenuPolicy(Qt::CustomContextMenu); -            connect(analog_button, &QPushButton::clicked, [=]() { +            connect(analog_button, &QPushButton::clicked, [=, this] {                  HandleClick(analog_map_buttons[analog_id][sub_button_id], -                            [=](const Common::ParamPackage& params) { +                            [=, this](const Common::ParamPackage& params) {                                  SetAnalogButton(params, analogs_param[analog_id],                                                  analog_sub_buttons[sub_button_id]);                              },                              InputCommon::Polling::DeviceType::Button);              });              connect(analog_button, &QPushButton::customContextMenuRequested, -                    [=](const QPoint& menu_location) { +                    [=, this](const QPoint& menu_location) {                          QMenu context_menu;                          context_menu.addAction(tr("Clear"), [&] {                              analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); @@ -350,32 +351,35 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i                              menu_location));                      });          } -        connect(analog_map_stick[analog_id], &QPushButton::clicked, [=] { +        connect(analog_map_stick[analog_id], &QPushButton::clicked, [=, this] {              if (QMessageBox::information(                      this, tr("Information"),                      tr("After pressing OK, first move your joystick horizontally, "                         "and then vertically."),                      QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { -                HandleClick( -                    analog_map_stick[analog_id], -                    [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; }, -                    InputCommon::Polling::DeviceType::Analog); +                HandleClick(analog_map_stick[analog_id], +                            [=, this](const Common::ParamPackage& params) { +                                analogs_param[analog_id] = params; +                            }, +                            InputCommon::Polling::DeviceType::Analog);              }          }); -        connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] { -            const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value(); -            if (analogs_param[analog_id].Get("engine", "") == "sdl" || -                analogs_param[analog_id].Get("engine", "") == "gcpad") { -                analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( -                    tr("Deadzone: %1%").arg(slider_value)); -                analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); -            } else { -                analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( -                    tr("Modifier Scale: %1%").arg(slider_value)); -                analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f); -            } -        }); +        connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, +                [=, this] { +                    const float slider_value = +                        analog_map_deadzone_and_modifier_slider[analog_id]->value(); +                    if (analogs_param[analog_id].Get("engine", "") == "sdl" || +                        analogs_param[analog_id].Get("engine", "") == "gcpad") { +                        analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( +                            tr("Deadzone: %1%").arg(slider_value)); +                        analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); +                    } else { +                        analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( +                            tr("Modifier Scale: %1%").arg(slider_value)); +                        analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f); +                    } +                });      }      connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp index e0647ea5b..ea2549363 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.cpp +++ b/src/yuzu/configuration/configure_mouse_advanced.cpp @@ -83,25 +83,28 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)          }          button->setContextMenuPolicy(Qt::CustomContextMenu); -        connect(button, &QPushButton::clicked, [=] { -            HandleClick( -                button_map[button_id], -                [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, -                InputCommon::Polling::DeviceType::Button); -        }); -        connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { -            QMenu context_menu; -            context_menu.addAction(tr("Clear"), [&] { -                buttons_param[button_id].Clear(); -                button_map[button_id]->setText(tr("[not set]")); -            }); -            context_menu.addAction(tr("Restore Default"), [&] { -                buttons_param[button_id] = Common::ParamPackage{ -                    InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])}; -                button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); -            }); -            context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); +        connect(button, &QPushButton::clicked, [=, this] { +            HandleClick(button_map[button_id], +                        [=, this](const Common::ParamPackage& params) { +                            buttons_param[button_id] = params; +                        }, +                        InputCommon::Polling::DeviceType::Button);          }); +        connect(button, &QPushButton::customContextMenuRequested, +                [=, this](const QPoint& menu_location) { +                    QMenu context_menu; +                    context_menu.addAction(tr("Clear"), [&] { +                        buttons_param[button_id].Clear(); +                        button_map[button_id]->setText(tr("[not set]")); +                    }); +                    context_menu.addAction(tr("Restore Default"), [&] { +                        buttons_param[button_id] = +                            Common::ParamPackage{InputCommon::GenerateKeyboardParam( +                                Config::default_mouse_buttons[button_id])}; +                        button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); +                    }); +                    context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); +                });      }      connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 24b6c5b72..2c20b68d0 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -4,9 +4,11 @@  #include <array>  #include <utility> +#include <QFileDialog>  #include <QDirIterator>  #include "common/common_types.h" +#include "common/file_util.h"  #include "core/settings.h"  #include "ui_configure_ui.h"  #include "yuzu/configuration/configure_ui.h" @@ -52,9 +54,21 @@ ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::Configur      // Update text ComboBoxes after user interaction.      connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::activated), -            [=]() { ConfigureUi::UpdateSecondRowComboBox(); }); +            [this] { ConfigureUi::UpdateSecondRowComboBox(); });      connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::activated), -            [=]() { ConfigureUi::UpdateFirstRowComboBox(); }); +            [this] { ConfigureUi::UpdateFirstRowComboBox(); }); + +    // Set screenshot path to user specification. +    connect(ui->screenshot_path_button, &QToolButton::pressed, this, [this] { +        const QString& filename = +            QFileDialog::getExistingDirectory( +                this, tr("Select Screenshots Path..."), +                QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))) + +            QDir::separator(); +        if (!filename.isEmpty()) { +            ui->screenshot_path_edit->setText(filename); +        } +    });  }  ConfigureUi::~ConfigureUi() = default; @@ -66,6 +80,10 @@ void ConfigureUi::ApplyConfiguration() {      UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();      UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();      UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); + +    UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked(); +    FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir, +                          ui->screenshot_path_edit->text().toStdString());      Settings::Apply();  } @@ -80,6 +98,10 @@ void ConfigureUi::SetConfiguration() {      ui->show_add_ons->setChecked(UISettings::values.show_add_ons);      ui->icon_size_combobox->setCurrentIndex(          ui->icon_size_combobox->findData(UISettings::values.icon_size)); + +    ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as); +    ui->screenshot_path_edit->setText( +        QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir)));  }  void ConfigureUi::changeEvent(QEvent* event) { diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui index 0b81747d7..d895b799f 100644 --- a/src/yuzu/configuration/configure_ui.ui +++ b/src/yuzu/configuration/configure_ui.ui @@ -6,8 +6,8 @@     <rect>      <x>0</x>      <y>0</y> -    <width>300</width> -    <height>377</height> +    <width>363</width> +    <height>391</height>     </rect>    </property>    <property name="windowTitle"> @@ -128,6 +128,47 @@      </widget>     </item>     <item> +    <widget class="QGroupBox" name="screenshots_GroupBox"> +     <property name="title"> +      <string>Screenshots</string> +     </property> +     <layout class="QVBoxLayout" name="verticalLayout_4"> +      <item> +       <layout class="QVBoxLayout" name="verticalLayout_3"> +        <item> +         <widget class="QCheckBox" name="enable_screenshot_save_as"> +          <property name="text"> +           <string>Ask Where To Save Screenshots (Windows Only)</string> +          </property> +         </widget> +        </item> +        <item> +         <layout class="QHBoxLayout" name="horizontalLayout_4"> +          <item> +           <widget class="QLabel" name="label"> +            <property name="text"> +             <string>Screenshots Path: </string> +            </property> +           </widget> +          </item> +          <item> +           <widget class="QLineEdit" name="screenshot_path_edit"/> +          </item> +          <item> +           <widget class="QToolButton" name="screenshot_path_button"> +            <property name="text"> +             <string>...</string> +            </property> +           </widget> +          </item> +         </layout> +        </item> +       </layout> +      </item> +     </layout> +    </widget> +   </item> +   <item>      <spacer name="verticalSpacer">       <property name="orientation">        <enum>Qt::Vertical</enum> diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index ab7fc7a24..62acc3720 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -474,28 +474,56 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {  void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) {      QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); -    QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location")); +    QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location"));      QAction* open_transferable_shader_cache =          context_menu.addAction(tr("Open Transferable Shader Cache"));      context_menu.addSeparator(); +    QMenu* remove_menu = context_menu.addMenu(tr("Remove")); +    QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update")); +    QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC")); +    QAction* remove_shader_cache = remove_menu->addAction(tr("Remove Shader Cache")); +    QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration")); +    remove_menu->addSeparator(); +    QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));      QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));      QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));      QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));      context_menu.addSeparator();      QAction* properties = context_menu.addAction(tr("Properties")); -    open_save_location->setEnabled(program_id != 0); +    open_save_location->setVisible(program_id != 0); +    open_mod_location->setVisible(program_id != 0); +    open_transferable_shader_cache->setVisible(program_id != 0); +    remove_update->setVisible(program_id != 0); +    remove_dlc->setVisible(program_id != 0); +    remove_shader_cache->setVisible(program_id != 0); +    remove_all_content->setVisible(program_id != 0);      auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);      navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);      connect(open_save_location, &QAction::triggered, [this, program_id, path]() {          emit OpenFolderRequested(GameListOpenTarget::SaveData, path);      }); -    connect(open_lfs_location, &QAction::triggered, [this, program_id, path]() { +    connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {          emit OpenFolderRequested(GameListOpenTarget::ModData, path);      });      connect(open_transferable_shader_cache, &QAction::triggered,              [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); }); +    connect(remove_all_content, &QAction::triggered, [this, program_id]() { +        emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Game); +    }); +    connect(remove_update, &QAction::triggered, [this, program_id]() { +        emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Update); +    }); +    connect(remove_dlc, &QAction::triggered, [this, program_id]() { +        emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::AddOnContent); +    }); +    connect(remove_shader_cache, &QAction::triggered, [this, program_id]() { +        emit RemoveFileRequested(program_id, GameListRemoveTarget::ShaderCache); +    }); +    connect(remove_custom_config, &QAction::triggered, [this, program_id]() { +        emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration); +    });      connect(dump_romfs, &QAction::triggered,              [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); });      connect(copy_tid, &QAction::triggered, diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index a38cb2fc3..483835cce 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -39,6 +39,17 @@ enum class GameListOpenTarget {      ModData,  }; +enum class GameListRemoveTarget { +    ShaderCache, +    CustomConfiguration, +}; + +enum class InstalledEntryType { +    Game, +    Update, +    AddOnContent, +}; +  class GameList : public QWidget {      Q_OBJECT @@ -75,6 +86,8 @@ signals:      void ShouldCancelWorker();      void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path);      void OpenTransferableShaderCacheRequested(u64 program_id); +    void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type); +    void RemoveFileRequested(u64 program_id, GameListRemoveTarget target);      void DumpRomFSRequested(u64 program_id, const std::string& game_path);      void CopyTIDRequested(u64 program_id);      void NavigateToGamedbEntryRequested(u64 program_id, @@ -117,8 +130,6 @@ private:      friend class GameListSearchField;  }; -Q_DECLARE_METATYPE(GameListOpenTarget); -  class GameListPlaceholder : public QWidget {      Q_OBJECT  public: diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 31a635176..592993c36 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -583,7 +583,7 @@ void GMainWindow::InitializeWidgets() {      renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton"));      renderer_status_button->setCheckable(true);      renderer_status_button->setFocusPolicy(Qt::NoFocus); -    connect(renderer_status_button, &QPushButton::toggled, [=](bool checked) { +    connect(renderer_status_button, &QPushButton::toggled, [this](bool checked) {          renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL"));      });      renderer_status_button->toggle(); @@ -595,7 +595,7 @@ void GMainWindow::InitializeWidgets() {  #else      renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==                                         Settings::RendererBackend::Vulkan); -    connect(renderer_status_button, &QPushButton::clicked, [=] { +    connect(renderer_status_button, &QPushButton::clicked, [this] {          if (emulation_running) {              return;          } @@ -847,6 +847,9 @@ void GMainWindow::ConnectWidgetEvents() {      connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);      connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this,              &GMainWindow::OnTransferableShaderCacheOpenFile); +    connect(game_list, &GameList::RemoveInstalledEntryRequested, this, +            &GMainWindow::OnGameListRemoveInstalledEntry); +    connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile);      connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);      connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);      connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, @@ -1257,7 +1260,6 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str      case GameListOpenTarget::SaveData: {          open_target = tr("Save Data");          const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); -        ASSERT(program_id != 0);          if (has_user_save) {              // User save data @@ -1322,14 +1324,12 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str  }  void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { -    ASSERT(program_id != 0); -      const QString shader_dir =          QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); -    const QString tranferable_shader_cache_folder_path = +    const QString transferable_shader_cache_folder_path =          shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");      const QString transferable_shader_cache_file_path = -        tranferable_shader_cache_folder_path + QDir::separator() + +        transferable_shader_cache_folder_path + QDir::separator() +          QString::fromStdString(fmt::format("{:016X}.bin", program_id));      if (!QFile::exists(transferable_shader_cache_file_path)) { @@ -1350,7 +1350,7 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {      param << QDir::toNativeSeparators(transferable_shader_cache_file_path);      QProcess::startDetached(explorer, param);  #else -    QDesktopServices::openUrl(QUrl::fromLocalFile(tranferable_shader_cache_folder_path)); +    QDesktopServices::openUrl(QUrl::fromLocalFile(transferable_shader_cache_folder_path));  #endif  } @@ -1394,6 +1394,174 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src      return true;  } +void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) { +    const QString entry_type = [this, type] { +        switch (type) { +        case InstalledEntryType::Game: +            return tr("Contents"); +        case InstalledEntryType::Update: +            return tr("Update"); +        case InstalledEntryType::AddOnContent: +            return tr("DLC"); +        default: +            return QString{}; +        } +    }(); + +    if (QMessageBox::question( +            this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type), +            QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { +        return; +    } + +    switch (type) { +    case InstalledEntryType::Game: +        RemoveBaseContent(program_id, entry_type); +        [[fallthrough]]; +    case InstalledEntryType::Update: +        RemoveUpdateContent(program_id, entry_type); +        if (type != InstalledEntryType::Game) { +            break; +        } +        [[fallthrough]]; +    case InstalledEntryType::AddOnContent: +        RemoveAddOnContent(program_id, entry_type); +        break; +    } +    FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + +                                   "game_list"); +    game_list->PopulateAsync(UISettings::values.game_dirs); +} + +void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) { +    const auto& fs_controller = Core::System::GetInstance().GetFileSystemController(); +    const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) || +                     fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); + +    if (res) { +        QMessageBox::information(this, tr("Successfully Removed"), +                                 tr("Successfully removed the installed base game.")); +    } else { +        QMessageBox::warning( +            this, tr("Error Removing %1").arg(entry_type), +            tr("The base game is not installed in the NAND and cannot be removed.")); +    } +} + +void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) { +    const auto update_id = program_id | 0x800; +    const auto& fs_controller = Core::System::GetInstance().GetFileSystemController(); +    const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || +                     fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id); + +    if (res) { +        QMessageBox::information(this, tr("Successfully Removed"), +                                 tr("Successfully removed the installed update.")); +    } else { +        QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), +                             tr("There is no update installed for this title.")); +    } +} + +void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) { +    u32 count{}; +    const auto& fs_controller = Core::System::GetInstance().GetFileSystemController(); +    const auto dlc_entries = Core::System::GetInstance().GetContentProvider().ListEntriesFilter( +        FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); + +    for (const auto& entry : dlc_entries) { +        if ((entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id) { +            const auto res = +                fs_controller.GetUserNANDContents()->RemoveExistingEntry(entry.title_id) || +                fs_controller.GetSDMCContents()->RemoveExistingEntry(entry.title_id); +            if (res) { +                ++count; +            } +        } +    } + +    if (count == 0) { +        QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), +                             tr("There are no DLC installed for this title.")); +        return; +    } + +    QMessageBox::information(this, tr("Successfully Removed"), +                             tr("Successfully removed %1 installed DLC.").arg(count)); +} + +void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target) { +    const QString question = [this, target] { +        switch (target) { +        case GameListRemoveTarget::ShaderCache: +            return tr("Delete Transferable Shader Cache?"); +        case GameListRemoveTarget::CustomConfiguration: +            return tr("Remove Custom Game Configuration?"); +        default: +            return QString{}; +        } +    }(); + +    if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No, +                              QMessageBox::No) != QMessageBox::Yes) { +        return; +    } + +    switch (target) { +    case GameListRemoveTarget::ShaderCache: +        RemoveTransferableShaderCache(program_id); +        break; +    case GameListRemoveTarget::CustomConfiguration: +        RemoveCustomConfiguration(program_id); +        break; +    } +} + +void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { +    const QString shader_dir = +        QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); +    const QString transferable_shader_cache_folder_path = +        shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); +    const QString transferable_shader_cache_file_path = +        transferable_shader_cache_folder_path + QDir::separator() + +        QString::fromStdString(fmt::format("{:016X}.bin", program_id)); + +    if (!QFile::exists(transferable_shader_cache_file_path)) { +        QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"), +                             tr("A shader cache for this title does not exist.")); +        return; +    } + +    if (QFile::remove(transferable_shader_cache_file_path)) { +        QMessageBox::information(this, tr("Successfully Removed"), +                                 tr("Successfully removed the transferable shader cache.")); +    } else { +        QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"), +                             tr("Failed to remove the transferable shader cache.")); +    } +} + +void GMainWindow::RemoveCustomConfiguration(u64 program_id) { +    const QString config_dir = +        QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir)); +    const QString custom_config_file_path = +        config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id)); + +    if (!QFile::exists(custom_config_file_path)) { +        QMessageBox::warning(this, tr("Error Removing Custom Configuration"), +                             tr("A custom configuration for this title does not exist.")); +        return; +    } + +    if (QFile::remove(custom_config_file_path)) { +        QMessageBox::information(this, tr("Successfully Removed"), +                                 tr("Successfully removed the custom game configuration.")); +    } else { +        QMessageBox::warning(this, tr("Error Removing Custom Configuration"), +                             tr("Failed to remove the custom game configuration.")); +    } +} +  void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {      const auto failed = [this] {          QMessageBox::warning(this, tr("RomFS Extraction Failed!"), @@ -1655,7 +1823,7 @@ void GMainWindow::OnMenuInstallToNAND() {      ui.action_Install_File_NAND->setEnabled(false); -    install_progress = new QProgressDialog(QStringLiteral(""), tr("Cancel"), 0, total_size, this); +    install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this);      install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint &                                       ~Qt::WindowMaximizeButtonHint);      install_progress->setAttribute(Qt::WA_DeleteOnClose, true); @@ -1705,18 +1873,18 @@ void GMainWindow::OnMenuInstallToNAND() {      install_progress->close();      const QString install_results = -        (new_files.isEmpty() ? QStringLiteral("") +        (new_files.isEmpty() ? QString{}                               : tr("%n file(s) were newly installed\n", "", new_files.size())) +          (overwritten_files.isEmpty() -             ? QStringLiteral("") +             ? QString{}               : tr("%n file(s) were overwritten\n", "", overwritten_files.size())) + -        (failed_files.isEmpty() ? QStringLiteral("") +        (failed_files.isEmpty() ? QString{}                                  : 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"); +    game_list->PopulateAsync(UISettings::values.game_dirs);      ui.action_Install_File_NAND->setEnabled(true);  } @@ -2153,17 +2321,28 @@ void GMainWindow::OnToggleFilterBar() {  void GMainWindow::OnCaptureScreenshot() {      OnPauseGame(); -    QFileDialog png_dialog(this, tr("Capture Screenshot"), UISettings::values.screenshot_path, -                           tr("PNG Image (*.png)")); -    png_dialog.setAcceptMode(QFileDialog::AcceptSave); -    png_dialog.setDefaultSuffix(QStringLiteral("png")); -    if (png_dialog.exec()) { -        const QString path = png_dialog.selectedFiles().first(); -        if (!path.isEmpty()) { -            UISettings::values.screenshot_path = QFileInfo(path).path(); -            render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path); + +    const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); +    const auto screenshot_path = +        QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir)); +    const auto date = +        QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz")); +    QString filename = QStringLiteral("%1%2_%3.png") +                           .arg(screenshot_path) +                           .arg(title_id, 16, 16, QLatin1Char{'0'}) +                           .arg(date); + +#ifdef _WIN32 +    if (UISettings::values.enable_screenshot_save_as) { +        filename = QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), filename, +                                                tr("PNG Image (*.png)")); +        if (filename.isEmpty()) { +            OnStartGame(); +            return;          }      } +#endif +    render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, filename);      OnStartGame();  } @@ -2202,8 +2381,7 @@ void GMainWindow::UpdateStatusBar() {      if (shaders_building != 0) {          shader_building_label->setText( -            tr("Building: %1 shader").arg(shaders_building) + -            (shaders_building != 1 ? QString::fromStdString("s") : QString::fromStdString(""))); +            tr("Building: %n shader(s)", "", static_cast<int>(shaders_building)));          shader_building_label->setVisible(true);      } else {          shader_building_label->setVisible(false); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index db573d606..73a44a3bf 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -32,6 +32,8 @@ class QPushButton;  class QProgressDialog;  class WaitTreeWidget;  enum class GameListOpenTarget; +enum class GameListRemoveTarget; +enum class InstalledEntryType;  class GameListPlaceholder;  namespace Core::Frontend { @@ -198,6 +200,8 @@ private slots:      void OnGameListLoadFile(QString game_path);      void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);      void OnTransferableShaderCacheOpenFile(u64 program_id); +    void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type); +    void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target);      void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);      void OnGameListCopyTID(u64 program_id);      void OnGameListNavigateToGamedbEntry(u64 program_id, @@ -229,6 +233,11 @@ private slots:      void OnLanguageChanged(const QString& locale);  private: +    void RemoveBaseContent(u64 program_id, const QString& entry_type); +    void RemoveUpdateContent(u64 program_id, const QString& entry_type); +    void RemoveAddOnContent(u64 program_id, const QString& entry_type); +    void RemoveTransferableShaderCache(u64 program_id); +    void RemoveCustomConfiguration(u64 program_id);      std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);      InstallResult InstallNSPXCI(const QString& filename);      InstallResult InstallNCA(const QString& filename); diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index ac7b9aef6..bbfeafc55 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -66,11 +66,11 @@ struct Values {      // Discord RPC      bool enable_discord_presence; +    bool enable_screenshot_save_as;      u16 screenshot_resolution_factor;      QString roms_path;      QString symbols_path; -    QString screenshot_path;      QString game_dir_deprecated;      bool game_dir_deprecated_deepscan;      QVector<UISettings::GameDir> game_dirs; | 
