diff options
Diffstat (limited to 'src/yuzu')
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | src/yuzu/applets/qt_amiibo_settings.cpp | 260 | ||||
| -rw-r--r-- | src/yuzu/applets/qt_amiibo_settings.h | 83 | ||||
| -rw-r--r-- | src/yuzu/applets/qt_amiibo_settings.ui | 494 | ||||
| -rw-r--r-- | src/yuzu/bootmanager.cpp | 337 | ||||
| -rw-r--r-- | src/yuzu/compatdb.cpp | 162 | ||||
| -rw-r--r-- | src/yuzu/compatdb.h | 11 | ||||
| -rw-r--r-- | src/yuzu/compatdb.ui | 277 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_profile_manager.cpp | 67 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_profile_manager.h | 27 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_profile_manager.ui | 6 | ||||
| -rw-r--r-- | src/yuzu/game_list_p.h | 12 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 91 | ||||
| -rw-r--r-- | src/yuzu/main.h | 16 | 
14 files changed, 1528 insertions, 320 deletions
| diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 239f12382..adad36221 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -18,6 +18,9 @@ add_executable(yuzu      about_dialog.cpp      about_dialog.h      aboutdialog.ui +    applets/qt_amiibo_settings.cpp +    applets/qt_amiibo_settings.h +    applets/qt_amiibo_settings.ui      applets/qt_controller.cpp      applets/qt_controller.h      applets/qt_controller.ui @@ -385,6 +388,6 @@ if (NOT APPLE)      target_compile_definitions(yuzu PRIVATE HAS_OPENGL)  endif() -if (ARCHITECTURE_x86_64) +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)      target_link_libraries(yuzu PRIVATE dynarmic)  endif() diff --git a/src/yuzu/applets/qt_amiibo_settings.cpp b/src/yuzu/applets/qt_amiibo_settings.cpp new file mode 100644 index 000000000..efb7f6ecc --- /dev/null +++ b/src/yuzu/applets/qt_amiibo_settings.cpp @@ -0,0 +1,260 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <thread> +#include <fmt/format.h> +#include <nlohmann/json.hpp> + +#include "common/assert.h" +#include "common/string_util.h" +#include "core/hle/service/nfp/nfp_device.h" +#include "core/hle/service/nfp/nfp_result.h" +#include "input_common/drivers/virtual_amiibo.h" +#include "input_common/main.h" +#include "ui_qt_amiibo_settings.h" +#include "web_service/web_backend.h" +#include "yuzu/applets/qt_amiibo_settings.h" +#include "yuzu/main.h" + +QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent, +                                               Core::Frontend::CabinetParameters parameters_, +                                               InputCommon::InputSubsystem* input_subsystem_, +                                               std::shared_ptr<Service::NFP::NfpDevice> nfp_device_) +    : QDialog(parent), ui(std::make_unique<Ui::QtAmiiboSettingsDialog>()), +      input_subsystem{input_subsystem_}, nfp_device{std::move(nfp_device_)}, +      parameters(std::move(parameters_)) { +    ui->setupUi(this); + +    LoadInfo(); + +    resize(0, 0); +} + +QtAmiiboSettingsDialog::~QtAmiiboSettingsDialog() = default; + +int QtAmiiboSettingsDialog::exec() { +    if (!is_initalized) { +        return QDialog::Rejected; +    } +    return QDialog::exec(); +} + +std::string QtAmiiboSettingsDialog::GetName() const { +    return ui->amiiboCustomNameValue->text().toStdString(); +} + +void QtAmiiboSettingsDialog::LoadInfo() { +    if (input_subsystem->GetVirtualAmiibo()->ReloadAmiibo() != +        InputCommon::VirtualAmiibo::Info::Success) { +        return; +    } + +    if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound && +        nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) { +        return; +    } +    nfp_device->Mount(Service::NFP::MountTarget::All); + +    LoadAmiiboInfo(); +    LoadAmiiboData(); +    LoadAmiiboGameInfo(); + +    ui->amiiboDirectoryValue->setText( +        QString::fromStdString(input_subsystem->GetVirtualAmiibo()->GetLastFilePath())); + +    SetSettingsDescription(); +    is_initalized = true; +} + +void QtAmiiboSettingsDialog::LoadAmiiboInfo() { +    Service::NFP::ModelInfo model_info{}; +    const auto model_result = nfp_device->GetModelInfo(model_info); + +    if (model_result.IsFailure()) { +        ui->amiiboImageLabel->setVisible(false); +        ui->amiiboInfoGroup->setVisible(false); +        return; +    } + +    const auto amiibo_id = +        fmt::format("{:04x}{:02x}{:02x}{:04x}{:02x}02", Common::swap16(model_info.character_id), +                    model_info.character_variant, model_info.amiibo_type, model_info.model_number, +                    model_info.series); + +    LOG_DEBUG(Frontend, "Loading amiibo id {}", amiibo_id); +    // Note: This function is not being used until we host the images on our server +    // LoadAmiiboApiInfo(amiibo_id); +    ui->amiiboImageLabel->setVisible(false); +    ui->amiiboInfoGroup->setVisible(false); +} + +void QtAmiiboSettingsDialog::LoadAmiiboApiInfo(std::string_view amiibo_id) { +    // TODO: Host this data on our website +    WebService::Client client{"https://amiiboapi.com", {}, {}}; +    WebService::Client image_client{"https://raw.githubusercontent.com", {}, {}}; +    const auto url_path = fmt::format("/api/amiibo/?id={}", amiibo_id); + +    const auto amiibo_json = client.GetJson(url_path, true).returned_data; +    if (amiibo_json.empty()) { +        ui->amiiboImageLabel->setVisible(false); +        ui->amiiboInfoGroup->setVisible(false); +        return; +    } + +    std::string amiibo_series{}; +    std::string amiibo_name{}; +    std::string amiibo_image_url{}; +    std::string amiibo_type{}; + +    const auto parsed_amiibo_json_json = nlohmann::json::parse(amiibo_json).at("amiibo"); +    parsed_amiibo_json_json.at("amiiboSeries").get_to(amiibo_series); +    parsed_amiibo_json_json.at("name").get_to(amiibo_name); +    parsed_amiibo_json_json.at("image").get_to(amiibo_image_url); +    parsed_amiibo_json_json.at("type").get_to(amiibo_type); + +    ui->amiiboSeriesValue->setText(QString::fromStdString(amiibo_series)); +    ui->amiiboNameValue->setText(QString::fromStdString(amiibo_name)); +    ui->amiiboTypeValue->setText(QString::fromStdString(amiibo_type)); + +    if (amiibo_image_url.size() < 34) { +        ui->amiiboImageLabel->setVisible(false); +    } + +    const auto image_url_path = amiibo_image_url.substr(34, amiibo_image_url.size() - 34); +    const auto image_data = image_client.GetImage(image_url_path, true).returned_data; + +    if (image_data.empty()) { +        ui->amiiboImageLabel->setVisible(false); +    } + +    QPixmap pixmap; +    pixmap.loadFromData(reinterpret_cast<const u8*>(image_data.data()), +                        static_cast<uint>(image_data.size())); +    pixmap = pixmap.scaled(250, 350, Qt::AspectRatioMode::KeepAspectRatio, +                           Qt::TransformationMode::SmoothTransformation); +    ui->amiiboImageLabel->setPixmap(pixmap); +} + +void QtAmiiboSettingsDialog::LoadAmiiboData() { +    Service::NFP::RegisterInfo register_info{}; +    Service::NFP::CommonInfo common_info{}; +    const auto register_result = nfp_device->GetRegisterInfo(register_info); +    const auto common_result = nfp_device->GetCommonInfo(common_info); + +    if (register_result.IsFailure()) { +        ui->creationDateValue->setDisabled(true); +        ui->modificationDateValue->setDisabled(true); +        ui->amiiboCustomNameValue->setReadOnly(false); +        ui->amiiboOwnerValue->setReadOnly(false); +        return; +    } + +    if (parameters.mode == Service::NFP::CabinetMode::StartNicknameAndOwnerSettings) { +        ui->creationDateValue->setDisabled(true); +        ui->modificationDateValue->setDisabled(true); +    } + +    const auto amiibo_name = std::string(register_info.amiibo_name.data()); +    const auto owner_name = Common::UTF16ToUTF8(register_info.mii_char_info.name.data()); +    const auto creation_date = +        QDate(register_info.creation_date.year, register_info.creation_date.month, +              register_info.creation_date.day); + +    ui->amiiboCustomNameValue->setText(QString::fromStdString(amiibo_name)); +    ui->amiiboOwnerValue->setText(QString::fromStdString(owner_name)); +    ui->amiiboCustomNameValue->setReadOnly(true); +    ui->amiiboOwnerValue->setReadOnly(true); +    ui->creationDateValue->setDate(creation_date); + +    if (common_result.IsFailure()) { +        ui->modificationDateValue->setDisabled(true); +        return; +    } + +    const auto modification_date = +        QDate(common_info.last_write_date.year, common_info.last_write_date.month, +              common_info.last_write_date.day); +    ui->modificationDateValue->setDate(modification_date); +} + +void QtAmiiboSettingsDialog::LoadAmiiboGameInfo() { +    u32 application_area_id{}; +    const auto application_result = nfp_device->GetApplicationAreaId(application_area_id); + +    if (application_result.IsFailure()) { +        ui->gameIdValue->setVisible(false); +        ui->gameIdLabel->setText(tr("No game data present")); +        return; +    } + +    SetGameDataName(application_area_id); +} + +void QtAmiiboSettingsDialog::SetGameDataName(u32 application_area_id) { +    static constexpr std::array<std::pair<u32, const char*>, 12> game_name_list = { +        // 3ds, wii u +        std::pair<u32, const char*>{0x10110E00, "Super Smash Bros (3DS/WiiU)"}, +        {0x00132600, "Mario & Luigi: Paper Jam"}, +        {0x0014F000, "Animal Crossing: Happy Home Designer"}, +        {0x00152600, "Chibi-Robo!: Zip Lash"}, +        {0x10161f00, "Mario Party 10"}, +        {0x1019C800, "The Legend of Zelda: Twilight Princess HD"}, +        // switch +        {0x10162B00, "Splatoon 2"}, +        {0x1016e100, "Shovel Knight: Treasure Trove"}, +        {0x1019C800, "The Legend of Zelda: Breath of the Wild"}, +        {0x34F80200, "Super Smash Bros. Ultimate"}, +        {0x38600500, "Splatoon 3"}, +        {0x3B440400, "The Legend of Zelda: Link's Awakening"}, +    }; + +    for (const auto& [game_id, game_name] : game_name_list) { +        if (application_area_id == game_id) { +            ui->gameIdValue->setText(QString::fromStdString(game_name)); +            return; +        } +    } + +    const auto application_area_string = fmt::format("{:016x}", application_area_id); +    ui->gameIdValue->setText(QString::fromStdString(application_area_string)); +} + +void QtAmiiboSettingsDialog::SetSettingsDescription() { +    switch (parameters.mode) { +    case Service::NFP::CabinetMode::StartFormatter: +        ui->cabinetActionDescriptionLabel->setText( +            tr("The following amiibo data will be formatted:")); +        break; +    case Service::NFP::CabinetMode::StartGameDataEraser: +        ui->cabinetActionDescriptionLabel->setText(tr("The following game data will removed:")); +        break; +    case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: +        ui->cabinetActionDescriptionLabel->setText(tr("Set nickname and owner:")); +        break; +    case Service::NFP::CabinetMode::StartRestorer: +        ui->cabinetActionDescriptionLabel->setText(tr("Do you wish to restore this amiibo?")); +        break; +    } +} + +QtAmiiboSettings::QtAmiiboSettings(GMainWindow& parent) { +    connect(this, &QtAmiiboSettings::MainWindowShowAmiiboSettings, &parent, +            &GMainWindow::AmiiboSettingsShowDialog, Qt::QueuedConnection); +    connect(&parent, &GMainWindow::AmiiboSettingsFinished, this, +            &QtAmiiboSettings::MainWindowFinished, Qt::QueuedConnection); +} + +QtAmiiboSettings::~QtAmiiboSettings() = default; + +void QtAmiiboSettings::ShowCabinetApplet( +    const Core::Frontend::CabinetCallback& callback_, +    const Core::Frontend::CabinetParameters& parameters, +    std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const { +    callback = std::move(callback_); +    emit MainWindowShowAmiiboSettings(parameters, nfp_device); +} + +void QtAmiiboSettings::MainWindowFinished(bool is_success, const std::string& name) { +    callback(is_success, name); +} diff --git a/src/yuzu/applets/qt_amiibo_settings.h b/src/yuzu/applets/qt_amiibo_settings.h new file mode 100644 index 000000000..930c96739 --- /dev/null +++ b/src/yuzu/applets/qt_amiibo_settings.h @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <memory> +#include <QDialog> +#include "core/frontend/applets/cabinet.h" + +class GMainWindow; +class QCheckBox; +class QComboBox; +class QDialogButtonBox; +class QGroupBox; +class QLabel; + +namespace InputCommon { +class InputSubsystem; +} + +namespace Ui { +class QtAmiiboSettingsDialog; +} + +namespace Service::NFP { +class NfpDevice; +} // namespace Service::NFP + +class QtAmiiboSettingsDialog final : public QDialog { +    Q_OBJECT + +public: +    explicit QtAmiiboSettingsDialog(QWidget* parent, Core::Frontend::CabinetParameters parameters_, +                                    InputCommon::InputSubsystem* input_subsystem_, +                                    std::shared_ptr<Service::NFP::NfpDevice> nfp_device_); +    ~QtAmiiboSettingsDialog() override; + +    int exec() override; + +    std::string GetName() const; + +private: +    void LoadInfo(); +    void LoadAmiiboInfo(); +    void LoadAmiiboApiInfo(std::string_view amiibo_id); +    void LoadAmiiboData(); +    void LoadAmiiboGameInfo(); +    void SetGameDataName(u32 application_area_id); +    void SetSettingsDescription(); + +    std::unique_ptr<Ui::QtAmiiboSettingsDialog> ui; + +    InputCommon::InputSubsystem* input_subsystem; +    std::shared_ptr<Service::NFP::NfpDevice> nfp_device; + +    // Parameters sent in from the backend HLE applet. +    Core::Frontend::CabinetParameters parameters; + +    // If false amiibo settings failed to load +    bool is_initalized{}; +}; + +class QtAmiiboSettings final : public QObject, public Core::Frontend::CabinetApplet { +    Q_OBJECT + +public: +    explicit QtAmiiboSettings(GMainWindow& parent); +    ~QtAmiiboSettings() override; + +    void ShowCabinetApplet(const Core::Frontend::CabinetCallback& callback_, +                           const Core::Frontend::CabinetParameters& parameters, +                           std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override; + +signals: +    void MainWindowShowAmiiboSettings(const Core::Frontend::CabinetParameters& parameters, +                                      std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const; + +private: +    void MainWindowFinished(bool is_success, const std::string& name); + +    mutable Core::Frontend::CabinetCallback callback; +}; diff --git a/src/yuzu/applets/qt_amiibo_settings.ui b/src/yuzu/applets/qt_amiibo_settings.ui new file mode 100644 index 000000000..f377a6e61 --- /dev/null +++ b/src/yuzu/applets/qt_amiibo_settings.ui @@ -0,0 +1,494 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtAmiiboSettingsDialog</class> + <widget class="QDialog" name="QtAmiiboSettingsDialog"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>839</width> +    <height>500</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Amiibo Settings</string> +  </property> +  <property name="styleSheet"> +   <string notr="true"/> +  </property> +  <layout class="QVBoxLayout" name="verticalLayout" stretch="0"> +   <property name="leftMargin"> +    <number>0</number> +   </property> +   <property name="topMargin"> +    <number>0</number> +   </property> +   <property name="rightMargin"> +    <number>0</number> +   </property> +   <property name="bottomMargin"> +    <number>0</number> +   </property> +   <item> +    <widget class="QWidget" name="mainControllerApplet" native="true"> +     <layout class="QVBoxLayout" name="verticalLayout_1" stretch="0,3,0"> +      <property name="spacing"> +       <number>0</number> +      </property> +      <property name="leftMargin"> +       <number>0</number> +      </property> +      <property name="topMargin"> +       <number>0</number> +      </property> +      <property name="rightMargin"> +       <number>0</number> +      </property> +      <property name="bottomMargin"> +       <number>0</number> +      </property> +      <item> +       <widget class="QWidget" name="topControllerApplet" native="true"> +        <layout class="QHBoxLayout" name="horizontalLayout"> +         <property name="spacing"> +          <number>10</number> +         </property> +         <property name="leftMargin"> +          <number>20</number> +         </property> +         <property name="topMargin"> +          <number>15</number> +         </property> +         <property name="rightMargin"> +          <number>0</number> +         </property> +         <property name="bottomMargin"> +          <number>15</number> +         </property> +         <item> +          <widget class="QLabel" name="cabinetActionDescriptionLabel"> +           <property name="font"> +            <font> +             <pointsize>12</pointsize> +             <weight>75</weight> +             <bold>true</bold> +            </font> +           </property> +           <property name="text"> +            <string/> +           </property> +           <property name="alignment"> +            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> +           </property> +           <property name="wordWrap"> +            <bool>false</bool> +           </property> +          </widget> +         </item> +         <item> +          <spacer name="horizontalSpacer_2"> +           <property name="orientation"> +            <enum>Qt::Horizontal</enum> +           </property> +           <property name="sizeHint" stdset="0"> +            <size> +             <width>40</width> +             <height>20</height> +            </size> +           </property> +          </spacer> +         </item> +        </layout> +       </widget> +      </item> +      <item> +       <widget class="QWidget" name="middleControllerApplet" native="true"> +        <layout class="QVBoxLayout" name="verticalLayout_3"> +         <property name="spacing"> +          <number>0</number> +         </property> +         <property name="leftMargin"> +          <number>0</number> +         </property> +         <property name="topMargin"> +          <number>0</number> +         </property> +         <property name="rightMargin"> +          <number>0</number> +         </property> +         <property name="bottomMargin"> +          <number>0</number> +         </property> +         <item> +          <layout class="QHBoxLayout" name="horizontalLayout_2"> +           <property name="spacing"> +            <number>20</number> +           </property> +           <property name="leftMargin"> +            <number>15</number> +           </property> +           <property name="rightMargin"> +            <number>15</number> +           </property> +           <item> +            <widget class="QLabel" name="amiiboImageLabel"> +             <property name="minimumSize"> +              <size> +               <width>250</width> +               <height>350</height> +              </size> +             </property> +             <property name="maximumSize"> +              <size> +               <width>236</width> +               <height>350</height> +              </size> +             </property> +             <property name="text"> +              <string/> +             </property> +             <property name="alignment"> +              <set>Qt::AlignCenter</set> +             </property> +            </widget> +           </item> +           <item> +            <layout class="QVBoxLayout" name="verticalLayout_4"> +             <property name="leftMargin"> +              <number>0</number> +             </property> +             <property name="topMargin"> +              <number>8</number> +             </property> +             <property name="bottomMargin"> +              <number>15</number> +             </property> +             <item> +              <widget class="QGroupBox" name="amiiboInfoGroup"> +               <property name="title"> +                <string>Amiibo Info</string> +               </property> +               <layout class="QVBoxLayout" name="verticalLayout_5"> +                <item> +                 <layout class="QGridLayout" name="gridLayout_1"> +                  <item row="0" column="0"> +                   <widget class="QLabel" name="amiiboSeriesLabel"> +                    <property name="text"> +                     <string>Series</string> +                    </property> +                   </widget> +                  </item> +                  <item row="0" column="1"> +                   <widget class="QLineEdit" name="amiiboSeriesValue"> +                    <property name="sizePolicy"> +                     <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> +                      <horstretch>0</horstretch> +                      <verstretch>0</verstretch> +                     </sizepolicy> +                    </property> +                    <property name="readOnly"> +                     <bool>true</bool> +                    </property> +                   </widget> +                  </item> +                  <item row="1" column="0"> +                   <widget class="QLabel" name="amiiboTypeLabel"> +                    <property name="text"> +                     <string>Type</string> +                    </property> +                   </widget> +                  </item> +                  <item row="1" column="1"> +                   <widget class="QLineEdit" name="amiiboTypeValue"> +                    <property name="sizePolicy"> +                     <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> +                      <horstretch>0</horstretch> +                      <verstretch>0</verstretch> +                     </sizepolicy> +                    </property> +                    <property name="readOnly"> +                     <bool>true</bool> +                    </property> +                   </widget> +                  </item> +                  <item row="2" column="0"> +                   <widget class="QLabel" name="amiiboNameLabel"> +                    <property name="text"> +                     <string>Name</string> +                    </property> +                   </widget> +                  </item> +                  <item row="2" column="1"> +                   <widget class="QLineEdit" name="amiiboNameValue"> +                    <property name="sizePolicy"> +                     <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> +                      <horstretch>0</horstretch> +                      <verstretch>0</verstretch> +                     </sizepolicy> +                    </property> +                    <property name="readOnly"> +                     <bool>true</bool> +                    </property> +                   </widget> +                  </item> +                 </layout> +                </item> +               </layout> +              </widget> +             </item> +             <item> +              <widget class="QGroupBox" name="amiiboDataGroup"> +               <property name="title"> +                <string>Amiibo Data</string> +               </property> +               <layout class="QVBoxLayout" name="verticalLayout_6"> +                <item> +                 <layout class="QGridLayout" name="gridLayout_2"> +                  <item row="0" column="0"> +                   <widget class="QLabel" name="amiiboCustomNameLabel"> +                    <property name="text"> +                     <string>Custom Name</string> +                    </property> +                   </widget> +                  </item> +                  <item row="0" column="1"> +                   <widget class="QLineEdit" name="amiiboCustomNameValue"> +                    <property name="sizePolicy"> +                     <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> +                      <horstretch>0</horstretch> +                      <verstretch>0</verstretch> +                     </sizepolicy> +                    </property> +                    <property name="maxLength"> +                     <number>10</number> +                    </property> +                   </widget> +                  </item> +                  <item row="1" column="0"> +                   <widget class="QLabel" name="amiiboOwnerLabel"> +                    <property name="text"> +                     <string>Owner</string> +                    </property> +                   </widget> +                  </item> +                  <item row="1" column="1"> +                   <widget class="QLineEdit" name="amiiboOwnerValue"> +                    <property name="sizePolicy"> +                     <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> +                      <horstretch>0</horstretch> +                      <verstretch>0</verstretch> +                     </sizepolicy> +                    </property> +                    <property name="maxLength"> +                     <number>10</number> +                    </property> +                   </widget> +                  </item> +                  <item row="2" column="0"> +                   <widget class="QLabel" name="creationDateLabel"> +                    <property name="text"> +                     <string>Creation Date</string> +                    </property> +                   </widget> +                  </item> +                  <item row="2" column="1"> +                   <widget class="QDateTimeEdit" name="creationDateValue"> +                    <property name="readOnly"> +                     <bool>true</bool> +                    </property> +                    <property name="minimumDate"> +                     <date> +                      <year>1970</year> +                      <month>1</month> +                      <day>1</day> +                     </date> +                    </property> +                    <property name="displayFormat"> +                     <string>dd/MM/yyyy</string> +                    </property> +                   </widget> +                  </item> +                  <item row="3" column="0"> +                   <widget class="QLabel" name="modificationDateLabel"> +                    <property name="text"> +                     <string>Modification Date</string> +                    </property> +                   </widget> +                  </item> +                  <item row="3" column="1"> +                   <widget class="QDateTimeEdit" name="modificationDateValue"> +                    <property name="readOnly"> +                     <bool>true</bool> +                    </property> +                    <property name="minimumDate"> +                     <date> +                      <year>1970</year> +                      <month>1</month> +                      <day>1</day> +                     </date> +                    </property> +                    <property name="displayFormat"> +                     <string>dd/MM/yyyy </string> +                    </property> +                   </widget> +                  </item> +                 </layout> +                </item> +               </layout> +              </widget> +             </item> +             <item> +              <widget class="QGroupBox" name="gameDataGroup"> +               <property name="minimumSize"> +                <size> +                 <width>500</width> +                 <height>0</height> +                </size> +               </property> +               <property name="title"> +                <string>Game Data</string> +               </property> +               <layout class="QGridLayout" name="gridLayout_3"> +                <item row="0" column="0"> +                 <widget class="QLabel" name="gameIdLabel"> +                  <property name="text"> +                   <string>Game Id</string> +                  </property> +                 </widget> +                </item> +                <item row="0" column="1"> +                 <widget class="QLineEdit" name="gameIdValue"> +                  <property name="sizePolicy"> +                   <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> +                    <horstretch>0</horstretch> +                    <verstretch>0</verstretch> +                   </sizepolicy> +                  </property> +                  <property name="readOnly"> +                   <bool>true</bool> +                  </property> +                 </widget> +                </item> +               </layout> +              </widget> +             </item> +             <item> +              <widget class="QGroupBox" name="MountAmiiboGroup"> +               <property name="minimumSize"> +                <size> +                 <width>500</width> +                 <height>0</height> +                </size> +               </property> +               <property name="title"> +                <string>Mount Amiibo</string> +               </property> +               <layout class="QGridLayout" name="gridLayout_4"> +                <item row="0" column="3"> +                 <widget class="QToolButton" name="amiiboDirectoryButton"> +                  <property name="text"> +                   <string>...</string> +                  </property> +                 </widget> +                </item> +                <item row="0" column="1"> +                 <spacer name="horizontalSpacer"> +                  <property name="orientation"> +                   <enum>Qt::Horizontal</enum> +                  </property> +                  <property name="sizeType"> +                   <enum>QSizePolicy::Maximum</enum> +                  </property> +                  <property name="sizeHint" stdset="0"> +                   <size> +                    <width>60</width> +                    <height>20</height> +                   </size> +                  </property> +                 </spacer> +                </item> +                <item row="0" column="0"> +                 <widget class="QLabel" name="amiiboDirectoryLabel"> +                  <property name="text"> +                   <string>File Path</string> +                  </property> +                 </widget> +                </item> +                <item row="0" column="2"> +                 <widget class="QLineEdit" name="amiiboDirectoryValue"/> +                </item> +               </layout> +              </widget> +             </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> +            </layout> +           </item> +          </layout> +         </item> +        </layout> +       </widget> +      </item> +      <item> +       <widget class="QWidget" name="bottomControllerApplet" native="true"> +        <layout class="QHBoxLayout" name="horizontalLayout_6"> +         <property name="spacing"> +          <number>15</number> +         </property> +         <property name="leftMargin"> +          <number>15</number> +         </property> +         <property name="topMargin"> +          <number>8</number> +         </property> +         <property name="rightMargin"> +          <number>20</number> +         </property> +         <property name="bottomMargin"> +          <number>8</number> +         </property> +         <item alignment="Qt::AlignBottom"> +          <widget class="QDialogButtonBox" name="buttonBox"> +           <property name="enabled"> +            <bool>true</bool> +           </property> +           <property name="standardButtons"> +            <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> +           </property> +          </widget> +         </item> +        </layout> +       </widget> +      </item> +     </layout> +    </widget> +   </item> +  </layout> + </widget> + <resources/> + <connections> +  <connection> +   <sender>buttonBox</sender> +   <signal>accepted()</signal> +   <receiver>QtAmiiboSettingsDialog</receiver> +   <slot>accept()</slot> +  </connection> +  <connection> +   <sender>buttonBox</sender> +   <signal>rejected()</signal> +   <receiver>QtAmiiboSettingsDialog</receiver> +   <slot>reject()</slot> +  </connection> + </connections> +</ui> diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 6acfb7b06..d88efacd7 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -401,224 +401,127 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {  }  int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) { -    switch (qt_key) { -    case Qt::Key_A: -        return Settings::NativeKeyboard::A; -    case Qt::Key_B: -        return Settings::NativeKeyboard::B; -    case Qt::Key_C: -        return Settings::NativeKeyboard::C; -    case Qt::Key_D: -        return Settings::NativeKeyboard::D; -    case Qt::Key_E: -        return Settings::NativeKeyboard::E; -    case Qt::Key_F: -        return Settings::NativeKeyboard::F; -    case Qt::Key_G: -        return Settings::NativeKeyboard::G; -    case Qt::Key_H: -        return Settings::NativeKeyboard::H; -    case Qt::Key_I: -        return Settings::NativeKeyboard::I; -    case Qt::Key_J: -        return Settings::NativeKeyboard::J; -    case Qt::Key_K: -        return Settings::NativeKeyboard::K; -    case Qt::Key_L: -        return Settings::NativeKeyboard::L; -    case Qt::Key_M: -        return Settings::NativeKeyboard::M; -    case Qt::Key_N: -        return Settings::NativeKeyboard::N; -    case Qt::Key_O: -        return Settings::NativeKeyboard::O; -    case Qt::Key_P: -        return Settings::NativeKeyboard::P; -    case Qt::Key_Q: -        return Settings::NativeKeyboard::Q; -    case Qt::Key_R: -        return Settings::NativeKeyboard::R; -    case Qt::Key_S: -        return Settings::NativeKeyboard::S; -    case Qt::Key_T: -        return Settings::NativeKeyboard::T; -    case Qt::Key_U: -        return Settings::NativeKeyboard::U; -    case Qt::Key_V: -        return Settings::NativeKeyboard::V; -    case Qt::Key_W: -        return Settings::NativeKeyboard::W; -    case Qt::Key_X: -        return Settings::NativeKeyboard::X; -    case Qt::Key_Y: -        return Settings::NativeKeyboard::Y; -    case Qt::Key_Z: -        return Settings::NativeKeyboard::Z; -    case Qt::Key_1: -        return Settings::NativeKeyboard::N1; -    case Qt::Key_2: -        return Settings::NativeKeyboard::N2; -    case Qt::Key_3: -        return Settings::NativeKeyboard::N3; -    case Qt::Key_4: -        return Settings::NativeKeyboard::N4; -    case Qt::Key_5: -        return Settings::NativeKeyboard::N5; -    case Qt::Key_6: -        return Settings::NativeKeyboard::N6; -    case Qt::Key_7: -        return Settings::NativeKeyboard::N7; -    case Qt::Key_8: -        return Settings::NativeKeyboard::N8; -    case Qt::Key_9: -        return Settings::NativeKeyboard::N9; -    case Qt::Key_0: -        return Settings::NativeKeyboard::N0; -    case Qt::Key_Return: -        return Settings::NativeKeyboard::Return; -    case Qt::Key_Escape: -        return Settings::NativeKeyboard::Escape; -    case Qt::Key_Backspace: -        return Settings::NativeKeyboard::Backspace; -    case Qt::Key_Tab: -        return Settings::NativeKeyboard::Tab; -    case Qt::Key_Space: -        return Settings::NativeKeyboard::Space; -    case Qt::Key_Minus: -        return Settings::NativeKeyboard::Minus; -    case Qt::Key_Plus: -    case Qt::Key_questiondown: -        return Settings::NativeKeyboard::Plus; -    case Qt::Key_BracketLeft: -    case Qt::Key_BraceLeft: -        return Settings::NativeKeyboard::OpenBracket; -    case Qt::Key_BracketRight: -    case Qt::Key_BraceRight: -        return Settings::NativeKeyboard::CloseBracket; -    case Qt::Key_Bar: -        return Settings::NativeKeyboard::Pipe; -    case Qt::Key_Dead_Tilde: -        return Settings::NativeKeyboard::Tilde; -    case Qt::Key_Ntilde: -    case Qt::Key_Semicolon: -        return Settings::NativeKeyboard::Semicolon; -    case Qt::Key_Apostrophe: -        return Settings::NativeKeyboard::Quote; -    case Qt::Key_Dead_Grave: -        return Settings::NativeKeyboard::Backquote; -    case Qt::Key_Comma: -        return Settings::NativeKeyboard::Comma; -    case Qt::Key_Period: -        return Settings::NativeKeyboard::Period; -    case Qt::Key_Slash: -        return Settings::NativeKeyboard::Slash; -    case Qt::Key_CapsLock: -        return Settings::NativeKeyboard::CapsLock; -    case Qt::Key_F1: -        return Settings::NativeKeyboard::F1; -    case Qt::Key_F2: -        return Settings::NativeKeyboard::F2; -    case Qt::Key_F3: -        return Settings::NativeKeyboard::F3; -    case Qt::Key_F4: -        return Settings::NativeKeyboard::F4; -    case Qt::Key_F5: -        return Settings::NativeKeyboard::F5; -    case Qt::Key_F6: -        return Settings::NativeKeyboard::F6; -    case Qt::Key_F7: -        return Settings::NativeKeyboard::F7; -    case Qt::Key_F8: -        return Settings::NativeKeyboard::F8; -    case Qt::Key_F9: -        return Settings::NativeKeyboard::F9; -    case Qt::Key_F10: -        return Settings::NativeKeyboard::F10; -    case Qt::Key_F11: -        return Settings::NativeKeyboard::F11; -    case Qt::Key_F12: -        return Settings::NativeKeyboard::F12; -    case Qt::Key_Print: -        return Settings::NativeKeyboard::PrintScreen; -    case Qt::Key_ScrollLock: -        return Settings::NativeKeyboard::ScrollLock; -    case Qt::Key_Pause: -        return Settings::NativeKeyboard::Pause; -    case Qt::Key_Insert: -        return Settings::NativeKeyboard::Insert; -    case Qt::Key_Home: -        return Settings::NativeKeyboard::Home; -    case Qt::Key_PageUp: -        return Settings::NativeKeyboard::PageUp; -    case Qt::Key_Delete: -        return Settings::NativeKeyboard::Delete; -    case Qt::Key_End: -        return Settings::NativeKeyboard::End; -    case Qt::Key_PageDown: -        return Settings::NativeKeyboard::PageDown; -    case Qt::Key_Right: -        return Settings::NativeKeyboard::Right; -    case Qt::Key_Left: -        return Settings::NativeKeyboard::Left; -    case Qt::Key_Down: -        return Settings::NativeKeyboard::Down; -    case Qt::Key_Up: -        return Settings::NativeKeyboard::Up; -    case Qt::Key_NumLock: -        return Settings::NativeKeyboard::NumLock; -    // Numpad keys are missing here -    case Qt::Key_F13: -        return Settings::NativeKeyboard::F13; -    case Qt::Key_F14: -        return Settings::NativeKeyboard::F14; -    case Qt::Key_F15: -        return Settings::NativeKeyboard::F15; -    case Qt::Key_F16: -        return Settings::NativeKeyboard::F16; -    case Qt::Key_F17: -        return Settings::NativeKeyboard::F17; -    case Qt::Key_F18: -        return Settings::NativeKeyboard::F18; -    case Qt::Key_F19: -        return Settings::NativeKeyboard::F19; -    case Qt::Key_F20: -        return Settings::NativeKeyboard::F20; -    case Qt::Key_F21: -        return Settings::NativeKeyboard::F21; -    case Qt::Key_F22: -        return Settings::NativeKeyboard::F22; -    case Qt::Key_F23: -        return Settings::NativeKeyboard::F23; -    case Qt::Key_F24: -        return Settings::NativeKeyboard::F24; -    // case Qt::: -    //    return Settings::NativeKeyboard::KPComma; -    // case Qt::: -    //    return Settings::NativeKeyboard::Ro; -    case Qt::Key_Hiragana_Katakana: -        return Settings::NativeKeyboard::KatakanaHiragana; -    case Qt::Key_yen: -        return Settings::NativeKeyboard::Yen; -    case Qt::Key_Henkan: -        return Settings::NativeKeyboard::Henkan; -    case Qt::Key_Muhenkan: -        return Settings::NativeKeyboard::Muhenkan; -    // case Qt::: -    //    return Settings::NativeKeyboard::NumPadCommaPc98; -    case Qt::Key_Hangul: -        return Settings::NativeKeyboard::HangulEnglish; -    case Qt::Key_Hangul_Hanja: -        return Settings::NativeKeyboard::Hanja; -    case Qt::Key_Katakana: -        return Settings::NativeKeyboard::KatakanaKey; -    case Qt::Key_Hiragana: -        return Settings::NativeKeyboard::HiraganaKey; -    case Qt::Key_Zenkaku_Hankaku: -        return Settings::NativeKeyboard::ZenkakuHankaku; -    // Modifier keys are handled by the modifier property -    default: -        return Settings::NativeKeyboard::None; +    static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = { +        std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A}, +        {Qt::Key_A, Settings::NativeKeyboard::A}, +        {Qt::Key_B, Settings::NativeKeyboard::B}, +        {Qt::Key_C, Settings::NativeKeyboard::C}, +        {Qt::Key_D, Settings::NativeKeyboard::D}, +        {Qt::Key_E, Settings::NativeKeyboard::E}, +        {Qt::Key_F, Settings::NativeKeyboard::F}, +        {Qt::Key_G, Settings::NativeKeyboard::G}, +        {Qt::Key_H, Settings::NativeKeyboard::H}, +        {Qt::Key_I, Settings::NativeKeyboard::I}, +        {Qt::Key_J, Settings::NativeKeyboard::J}, +        {Qt::Key_K, Settings::NativeKeyboard::K}, +        {Qt::Key_L, Settings::NativeKeyboard::L}, +        {Qt::Key_M, Settings::NativeKeyboard::M}, +        {Qt::Key_N, Settings::NativeKeyboard::N}, +        {Qt::Key_O, Settings::NativeKeyboard::O}, +        {Qt::Key_P, Settings::NativeKeyboard::P}, +        {Qt::Key_Q, Settings::NativeKeyboard::Q}, +        {Qt::Key_R, Settings::NativeKeyboard::R}, +        {Qt::Key_S, Settings::NativeKeyboard::S}, +        {Qt::Key_T, Settings::NativeKeyboard::T}, +        {Qt::Key_U, Settings::NativeKeyboard::U}, +        {Qt::Key_V, Settings::NativeKeyboard::V}, +        {Qt::Key_W, Settings::NativeKeyboard::W}, +        {Qt::Key_X, Settings::NativeKeyboard::X}, +        {Qt::Key_Y, Settings::NativeKeyboard::Y}, +        {Qt::Key_Z, Settings::NativeKeyboard::Z}, +        {Qt::Key_1, Settings::NativeKeyboard::N1}, +        {Qt::Key_2, Settings::NativeKeyboard::N2}, +        {Qt::Key_3, Settings::NativeKeyboard::N3}, +        {Qt::Key_4, Settings::NativeKeyboard::N4}, +        {Qt::Key_5, Settings::NativeKeyboard::N5}, +        {Qt::Key_6, Settings::NativeKeyboard::N6}, +        {Qt::Key_7, Settings::NativeKeyboard::N7}, +        {Qt::Key_8, Settings::NativeKeyboard::N8}, +        {Qt::Key_9, Settings::NativeKeyboard::N9}, +        {Qt::Key_0, Settings::NativeKeyboard::N0}, +        {Qt::Key_Return, Settings::NativeKeyboard::Return}, +        {Qt::Key_Escape, Settings::NativeKeyboard::Escape}, +        {Qt::Key_Backspace, Settings::NativeKeyboard::Backspace}, +        {Qt::Key_Tab, Settings::NativeKeyboard::Tab}, +        {Qt::Key_Space, Settings::NativeKeyboard::Space}, +        {Qt::Key_Minus, Settings::NativeKeyboard::Minus}, +        {Qt::Key_Plus, Settings::NativeKeyboard::Plus}, +        {Qt::Key_questiondown, Settings::NativeKeyboard::Plus}, +        {Qt::Key_BracketLeft, Settings::NativeKeyboard::OpenBracket}, +        {Qt::Key_BraceLeft, Settings::NativeKeyboard::OpenBracket}, +        {Qt::Key_BracketRight, Settings::NativeKeyboard::CloseBracket}, +        {Qt::Key_BraceRight, Settings::NativeKeyboard::CloseBracket}, +        {Qt::Key_Bar, Settings::NativeKeyboard::Pipe}, +        {Qt::Key_Dead_Tilde, Settings::NativeKeyboard::Tilde}, +        {Qt::Key_Ntilde, Settings::NativeKeyboard::Semicolon}, +        {Qt::Key_Semicolon, Settings::NativeKeyboard::Semicolon}, +        {Qt::Key_Apostrophe, Settings::NativeKeyboard::Quote}, +        {Qt::Key_Dead_Grave, Settings::NativeKeyboard::Backquote}, +        {Qt::Key_Comma, Settings::NativeKeyboard::Comma}, +        {Qt::Key_Period, Settings::NativeKeyboard::Period}, +        {Qt::Key_Slash, Settings::NativeKeyboard::Slash}, +        {Qt::Key_CapsLock, Settings::NativeKeyboard::CapsLockKey}, +        {Qt::Key_F1, Settings::NativeKeyboard::F1}, +        {Qt::Key_F2, Settings::NativeKeyboard::F2}, +        {Qt::Key_F3, Settings::NativeKeyboard::F3}, +        {Qt::Key_F4, Settings::NativeKeyboard::F4}, +        {Qt::Key_F5, Settings::NativeKeyboard::F5}, +        {Qt::Key_F6, Settings::NativeKeyboard::F6}, +        {Qt::Key_F7, Settings::NativeKeyboard::F7}, +        {Qt::Key_F8, Settings::NativeKeyboard::F8}, +        {Qt::Key_F9, Settings::NativeKeyboard::F9}, +        {Qt::Key_F10, Settings::NativeKeyboard::F10}, +        {Qt::Key_F11, Settings::NativeKeyboard::F11}, +        {Qt::Key_F12, Settings::NativeKeyboard::F12}, +        {Qt::Key_Print, Settings::NativeKeyboard::PrintScreen}, +        {Qt::Key_ScrollLock, Settings::NativeKeyboard::ScrollLockKey}, +        {Qt::Key_Pause, Settings::NativeKeyboard::Pause}, +        {Qt::Key_Insert, Settings::NativeKeyboard::Insert}, +        {Qt::Key_Home, Settings::NativeKeyboard::Home}, +        {Qt::Key_PageUp, Settings::NativeKeyboard::PageUp}, +        {Qt::Key_Delete, Settings::NativeKeyboard::Delete}, +        {Qt::Key_End, Settings::NativeKeyboard::End}, +        {Qt::Key_PageDown, Settings::NativeKeyboard::PageDown}, +        {Qt::Key_Right, Settings::NativeKeyboard::Right}, +        {Qt::Key_Left, Settings::NativeKeyboard::Left}, +        {Qt::Key_Down, Settings::NativeKeyboard::Down}, +        {Qt::Key_Up, Settings::NativeKeyboard::Up}, +        {Qt::Key_NumLock, Settings::NativeKeyboard::NumLockKey}, +        // Numpad keys are missing here +        {Qt::Key_F13, Settings::NativeKeyboard::F13}, +        {Qt::Key_F14, Settings::NativeKeyboard::F14}, +        {Qt::Key_F15, Settings::NativeKeyboard::F15}, +        {Qt::Key_F16, Settings::NativeKeyboard::F16}, +        {Qt::Key_F17, Settings::NativeKeyboard::F17}, +        {Qt::Key_F18, Settings::NativeKeyboard::F18}, +        {Qt::Key_F19, Settings::NativeKeyboard::F19}, +        {Qt::Key_F20, Settings::NativeKeyboard::F20}, +        {Qt::Key_F21, Settings::NativeKeyboard::F21}, +        {Qt::Key_F22, Settings::NativeKeyboard::F22}, +        {Qt::Key_F23, Settings::NativeKeyboard::F23}, +        {Qt::Key_F24, Settings::NativeKeyboard::F24}, +        // {Qt::..., Settings::NativeKeyboard::KPComma}, +        // {Qt::..., Settings::NativeKeyboard::Ro}, +        {Qt::Key_Hiragana_Katakana, Settings::NativeKeyboard::KatakanaHiragana}, +        {Qt::Key_yen, Settings::NativeKeyboard::Yen}, +        {Qt::Key_Henkan, Settings::NativeKeyboard::Henkan}, +        {Qt::Key_Muhenkan, Settings::NativeKeyboard::Muhenkan}, +        // {Qt::..., Settings::NativeKeyboard::NumPadCommaPc98}, +        {Qt::Key_Hangul, Settings::NativeKeyboard::HangulEnglish}, +        {Qt::Key_Hangul_Hanja, Settings::NativeKeyboard::Hanja}, +        {Qt::Key_Katakana, Settings::NativeKeyboard::KatakanaKey}, +        {Qt::Key_Hiragana, Settings::NativeKeyboard::HiraganaKey}, +        {Qt::Key_Zenkaku_Hankaku, Settings::NativeKeyboard::ZenkakuHankaku}, +        // Modifier keys are handled by the modifier property +    }; + +    for (const auto& [qkey, nkey] : key_map) { +        if (qt_key == qkey) { +            return nkey; +        }      } + +    return Settings::NativeKeyboard::None;  }  int GRenderWindow::QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers) { diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp index f46fff340..05f49c0d2 100644 --- a/src/yuzu/compatdb.cpp +++ b/src/yuzu/compatdb.cpp @@ -15,12 +15,22 @@ CompatDB::CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent)      : QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),        ui{std::make_unique<Ui::CompatDB>()}, telemetry_session{telemetry_session_} {      ui->setupUi(this); -    connect(ui->radioButton_Perfect, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_Great, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_Okay, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_Bad, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext); + +    connect(ui->radioButton_GameBoot_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_GameBoot_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Gameplay_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Gameplay_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_NoFreeze_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_NoFreeze_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Complete_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Complete_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Graphical_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Graphical_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Graphical_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Audio_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Audio_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Audio_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +      connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit);      connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this,              &CompatDB::OnTestcaseSubmitted); @@ -30,29 +40,82 @@ CompatDB::~CompatDB() = default;  enum class CompatDBPage {      Intro = 0, -    Selection = 1, -    Final = 2, +    GameBoot = 1, +    GamePlay = 2, +    Freeze = 3, +    Completion = 4, +    Graphical = 5, +    Audio = 6, +    Final = 7,  };  void CompatDB::Submit() { -    QButtonGroup* compatibility = new QButtonGroup(this); -    compatibility->addButton(ui->radioButton_Perfect, 0); -    compatibility->addButton(ui->radioButton_Great, 1); -    compatibility->addButton(ui->radioButton_Okay, 2); -    compatibility->addButton(ui->radioButton_Bad, 3); -    compatibility->addButton(ui->radioButton_IntroMenu, 4); -    compatibility->addButton(ui->radioButton_WontBoot, 5); +    QButtonGroup* compatibility_GameBoot = new QButtonGroup(this); +    compatibility_GameBoot->addButton(ui->radioButton_GameBoot_Yes, 0); +    compatibility_GameBoot->addButton(ui->radioButton_GameBoot_No, 1); + +    QButtonGroup* compatibility_Gameplay = new QButtonGroup(this); +    compatibility_Gameplay->addButton(ui->radioButton_Gameplay_Yes, 0); +    compatibility_Gameplay->addButton(ui->radioButton_Gameplay_No, 1); + +    QButtonGroup* compatibility_NoFreeze = new QButtonGroup(this); +    compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_Yes, 0); +    compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_No, 1); + +    QButtonGroup* compatibility_Complete = new QButtonGroup(this); +    compatibility_Complete->addButton(ui->radioButton_Complete_Yes, 0); +    compatibility_Complete->addButton(ui->radioButton_Complete_No, 1); + +    QButtonGroup* compatibility_Graphical = new QButtonGroup(this); +    compatibility_Graphical->addButton(ui->radioButton_Graphical_Major, 0); +    compatibility_Graphical->addButton(ui->radioButton_Graphical_Minor, 1); +    compatibility_Graphical->addButton(ui->radioButton_Graphical_No, 2); + +    QButtonGroup* compatibility_Audio = new QButtonGroup(this); +    compatibility_Audio->addButton(ui->radioButton_Audio_Major, 0); +    compatibility_Graphical->addButton(ui->radioButton_Audio_Minor, 1); +    compatibility_Audio->addButton(ui->radioButton_Audio_No, 2); + +    const int compatiblity = static_cast<int>(CalculateCompatibility()); +      switch ((static_cast<CompatDBPage>(currentId()))) { -    case CompatDBPage::Selection: -        if (compatibility->checkedId() == -1) { +    case CompatDBPage::Intro: +        break; +    case CompatDBPage::GameBoot: +        if (compatibility_GameBoot->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::GamePlay: +        if (compatibility_Gameplay->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::Freeze: +        if (compatibility_NoFreeze->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::Completion: +        if (compatibility_Complete->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::Graphical: +        if (compatibility_Graphical->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::Audio: +        if (compatibility_Audio->checkedId() == -1) {              button(NextButton)->setEnabled(false);          }          break;      case CompatDBPage::Final:          back(); -        LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); +        LOG_INFO(Frontend, "Compatibility Rating: {}", compatiblity);          telemetry_session.AddField(Common::Telemetry::FieldType::UserFeedback, "Compatibility", -                                   compatibility->checkedId()); +                                   compatiblity);          button(NextButton)->setEnabled(false);          button(NextButton)->setText(tr("Submitting")); @@ -63,9 +126,70 @@ void CompatDB::Submit() {          break;      default:          LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); +        break; +    } +} + +int CompatDB::nextId() const { +    switch ((static_cast<CompatDBPage>(currentId()))) { +    case CompatDBPage::Intro: +        return static_cast<int>(CompatDBPage::GameBoot); +    case CompatDBPage::GameBoot: +        if (ui->radioButton_GameBoot_No->isChecked()) { +            return static_cast<int>(CompatDBPage::Final); +        } +        return static_cast<int>(CompatDBPage::GamePlay); +    case CompatDBPage::GamePlay: +        if (ui->radioButton_Gameplay_No->isChecked()) { +            return static_cast<int>(CompatDBPage::Final); +        } +        return static_cast<int>(CompatDBPage::Freeze); +    case CompatDBPage::Freeze: +        if (ui->radioButton_NoFreeze_No->isChecked()) { +            return static_cast<int>(CompatDBPage::Final); +        } +        return static_cast<int>(CompatDBPage::Completion); +    case CompatDBPage::Completion: +        if (ui->radioButton_Complete_No->isChecked()) { +            return static_cast<int>(CompatDBPage::Final); +        } +        return static_cast<int>(CompatDBPage::Graphical); +    case CompatDBPage::Graphical: +        return static_cast<int>(CompatDBPage::Audio); +    case CompatDBPage::Audio: +        return static_cast<int>(CompatDBPage::Final); +    case CompatDBPage::Final: +        return -1; +    default: +        LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); +        return static_cast<int>(CompatDBPage::Intro);      }  } +CompatibilityStatus CompatDB::CalculateCompatibility() const { +    if (ui->radioButton_GameBoot_No->isChecked()) { +        return CompatibilityStatus::WontBoot; +    } + +    if (ui->radioButton_Gameplay_No->isChecked()) { +        return CompatibilityStatus::IntroMenu; +    } + +    if (ui->radioButton_NoFreeze_No->isChecked() || ui->radioButton_Complete_No->isChecked()) { +        return CompatibilityStatus::Ingame; +    } + +    if (ui->radioButton_Graphical_Major->isChecked() || ui->radioButton_Audio_Major->isChecked()) { +        return CompatibilityStatus::Ingame; +    } + +    if (ui->radioButton_Graphical_Minor->isChecked() || ui->radioButton_Audio_Minor->isChecked()) { +        return CompatibilityStatus::Playable; +    } + +    return CompatibilityStatus::Perfect; +} +  void CompatDB::OnTestcaseSubmitted() {      if (!testcase_watcher.result()) {          QMessageBox::critical(this, tr("Communication error"), diff --git a/src/yuzu/compatdb.h b/src/yuzu/compatdb.h index 3252fc47a..37e11278b 100644 --- a/src/yuzu/compatdb.h +++ b/src/yuzu/compatdb.h @@ -12,12 +12,22 @@ namespace Ui {  class CompatDB;  } +enum class CompatibilityStatus { +    Perfect = 0, +    Playable = 1, +    // Unused: Okay = 2, +    Ingame = 3, +    IntroMenu = 4, +    WontBoot = 5, +}; +  class CompatDB : public QWizard {      Q_OBJECT  public:      explicit CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent = nullptr);      ~CompatDB(); +    int nextId() const override;  private:      QFutureWatcher<bool> testcase_watcher; @@ -25,6 +35,7 @@ private:      std::unique_ptr<Ui::CompatDB> ui;      void Submit(); +    CompatibilityStatus CalculateCompatibility() const;      void OnTestcaseSubmitted();      void EnableNext(); diff --git a/src/yuzu/compatdb.ui b/src/yuzu/compatdb.ui index 3ca55eda6..d11669df2 100644 --- a/src/yuzu/compatdb.ui +++ b/src/yuzu/compatdb.ui @@ -58,128 +58,311 @@      </item>     </layout>    </widget> -  <widget class="QWizardPage" name="wizard_Report"> +  <widget class="QWizardPage" name="wizard_GameBoot">     <property name="title">      <string>Report Game Compatibility</string>     </property>     <attribute name="pageId">      <string notr="true">1</string>     </attribute> -   <layout class="QFormLayout" name="formLayout"> +   <layout class="QFormLayout" name="formLayout1"> +    <item row="0" column="0" colspan="2"> +     <widget class="QLabel" name="lbl_Independent1"> +      <property name="font"> +       <font> +        <pointsize>10</pointsize> +       </font> +      </property> +      <property name="text"> +       <string><html><head/><body><p>Does the game boot?</p></body></html></string> +      </property> +      <property name="wordWrap"> +       <bool>true</bool> +      </property> +     </widget> +    </item> +    <item row="1" column="0" colspan="2"> +     <spacer name="verticalSpacer1"> +      <property name="orientation"> +       <enum>Qt::Vertical</enum> +      </property> +      <property name="sizeHint" stdset="0"> +       <size> +        <width>20</width> +        <height>0</height> +       </size> +      </property> +     </spacer> +    </item>      <item row="2" column="0"> -     <widget class="QRadioButton" name="radioButton_Perfect"> +     <widget class="QRadioButton" name="radioButton_GameBoot_Yes">        <property name="text"> -       <string>Perfect</string> +       <string>Yes   The game starts to output video or audio</string>        </property>       </widget>      </item> -    <item row="2" column="1"> -     <widget class="QLabel" name="lbl_Perfect"> +    <item row="4" column="0"> +     <widget class="QRadioButton" name="radioButton_GameBoot_No">        <property name="text"> -       <string><html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html></string> +       <string>No    The game doesn't get past the "Launching..." screen</string>        </property> -      <property name="wordWrap"> -       <bool>true</bool> +     </widget> +    </item> +   </layout> +  </widget> +  <widget class="QWizardPage" name="wizard_GamePlay"> +   <property name="title"> +    <string>Report Game Compatibility</string> +   </property> +   <attribute name="pageId"> +    <string notr="true">2</string> +   </attribute> +   <layout class="QFormLayout" name="formLayout2"> +    <item row="2" column="0"> +     <widget class="QRadioButton" name="radioButton_Gameplay_Yes"> +      <property name="text"> +       <string>Yes   The game gets past the intro/menu and into gameplay</string>        </property>       </widget>      </item>      <item row="4" column="0"> -     <widget class="QRadioButton" name="radioButton_Great"> +     <widget class="QRadioButton" name="radioButton_Gameplay_No">        <property name="text"> -       <string>Great</string> +       <string>No    The game crashes or freezes while loading or using the menu</string>        </property>       </widget>      </item> -    <item row="4" column="1"> -     <widget class="QLabel" name="lbl_Great"> +    <item row="0" column="0" colspan="2"> +     <widget class="QLabel" name="lbl_Independent2"> +      <property name="font"> +       <font> +        <pointsize>10</pointsize> +       </font> +      </property>        <property name="text"> -       <string><html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html></string> +       <string><html><head/><body><p>Does the game reach gameplay?</p></body></html></string>        </property>        <property name="wordWrap">         <bool>true</bool>        </property>       </widget>      </item> -    <item row="5" column="0"> -     <widget class="QRadioButton" name="radioButton_Okay"> +    <item row="1" column="0" colspan="2"> +     <spacer name="verticalSpacer2"> +      <property name="orientation"> +       <enum>Qt::Vertical</enum> +      </property> +      <property name="sizeHint" stdset="0"> +       <size> +        <width>20</width> +        <height>0</height> +       </size> +      </property> +     </spacer> +    </item> +   </layout> +  </widget> +  <widget class="QWizardPage" name="wizard_NoFreeze"> +   <property name="title"> +    <string>Report Game Compatibility</string> +   </property> +   <attribute name="pageId"> +    <string notr="true">3</string> +   </attribute> +   <layout class="QFormLayout" name="formLayout3"> +    <item row="2" column="0"> +     <widget class="QRadioButton" name="radioButton_NoFreeze_Yes">        <property name="text"> -       <string>Okay</string> +       <string>Yes   The game works without crashes</string>        </property>       </widget>      </item> -    <item row="5" column="1"> -     <widget class="QLabel" name="lbl_Okay"> +    <item row="4" column="0"> +     <widget class="QRadioButton" name="radioButton_NoFreeze_No"> +      <property name="text"> +       <string>No    The game crashes or freezes during gameplay</string> +      </property> +     </widget> +    </item> +    <item row="0" column="0" colspan="2"> +     <widget class="QLabel" name="lbl_Independent3"> +      <property name="font"> +       <font> +        <pointsize>10</pointsize> +       </font> +      </property>        <property name="text"> -       <string><html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html></string> +       <string><html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html></string>        </property>        <property name="wordWrap">         <bool>true</bool>        </property>       </widget>      </item> -    <item row="6" column="0"> -     <widget class="QRadioButton" name="radioButton_Bad"> +    <item row="1" column="0" colspan="2"> +     <spacer name="verticalSpacer3"> +      <property name="orientation"> +       <enum>Qt::Vertical</enum> +      </property> +      <property name="sizeHint" stdset="0"> +       <size> +        <width>20</width> +        <height>0</height> +       </size> +      </property> +     </spacer> +    </item> +   </layout> +  </widget> +  <widget class="QWizardPage" name="wizard_Complete"> +   <property name="title"> +    <string>Report Game Compatibility</string> +   </property> +   <attribute name="pageId"> +    <string notr="true">4</string> +   </attribute> +   <layout class="QFormLayout" name="formLayout4"> +    <item row="2" column="0"> +     <widget class="QRadioButton" name="radioButton_Complete_Yes">        <property name="text"> -       <string>Bad</string> +       <string>Yes   The game can be finished without any workarounds</string>        </property>       </widget>      </item> -    <item row="6" column="1"> -     <widget class="QLabel" name="lbl_Bad"> +    <item row="4" column="0"> +     <widget class="QRadioButton" name="radioButton_Complete_No"> +      <property name="text"> +       <string>No    The game can't progress past a certain area</string> +      </property> +     </widget> +    </item> +    <item row="0" column="0" colspan="2"> +     <widget class="QLabel" name="lbl_Independent4"> +      <property name="font"> +       <font> +        <pointsize>10</pointsize> +       </font> +      </property>        <property name="text"> -       <string><html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html></string> +       <string><html><head/><body><p>Is the game completely playable from start to finish?</p></body></html></string>        </property>        <property name="wordWrap">         <bool>true</bool>        </property>       </widget>      </item> -    <item row="7" column="0"> -     <widget class="QRadioButton" name="radioButton_IntroMenu"> +    <item row="1" column="0" colspan="2"> +     <spacer name="verticalSpacer4"> +      <property name="orientation"> +       <enum>Qt::Vertical</enum> +      </property> +      <property name="sizeHint" stdset="0"> +       <size> +        <width>20</width> +        <height>0</height> +       </size> +      </property> +     </spacer> +    </item> +   </layout> +  </widget> +  <widget class="QWizardPage" name="wizard_Graphical"> +   <property name="title"> +    <string>Report Game Compatibility</string> +   </property> +   <attribute name="pageId"> +    <string notr="true">5</string> +   </attribute> +   <layout class="QFormLayout" name="formLayout5"> +    <item row="2" column="0"> +     <widget class="QRadioButton" name="radioButton_Graphical_Major"> +      <property name="text"> +       <string>Major   The game has major graphical errors</string> +      </property> +     </widget> +    </item> +    <item row="4" column="0"> +     <widget class="QRadioButton" name="radioButton_Graphical_Minor">        <property name="text"> -       <string>Intro/Menu</string> +       <string>Minor   The game has minor graphical errors</string>        </property>       </widget>      </item> -    <item row="7" column="1"> -     <widget class="QLabel" name="lbl_IntroMenu"> +    <item row="6" column="0"> +     <widget class="QRadioButton" name="radioButton_Graphical_No"> +      <property name="text"> +       <string>None     Everything is rendered as it looks on the Nintendo Switch</string> +      </property> +     </widget> +    </item> +    <item row="0" column="0" colspan="2"> +     <widget class="QLabel" name="lbl_Independent5"> +      <property name="font"> +       <font> +        <pointsize>10</pointsize> +       </font> +      </property>        <property name="text"> -       <string><html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html></string> +       <string><html><head/><body><p>Does the game have any graphical glitches?</p></body></html></string>        </property>        <property name="wordWrap">         <bool>true</bool>        </property>       </widget>      </item> -    <item row="8" column="0"> -     <widget class="QRadioButton" name="radioButton_WontBoot"> -      <property name="text"> -       <string>Won't Boot</string> +    <item row="1" column="0" colspan="2"> +     <spacer name="verticalSpacer5"> +      <property name="orientation"> +       <enum>Qt::Vertical</enum>        </property> -      <property name="checkable"> -       <bool>true</bool> +      <property name="sizeHint" stdset="0"> +       <size> +        <width>20</width> +        <height>0</height> +       </size>        </property> -      <property name="checked"> -       <bool>false</bool> +     </spacer> +    </item> +   </layout> +  </widget> +  <widget class="QWizardPage" name="wizard_Audio"> +   <property name="title"> +    <string>Report Game Compatibility</string> +   </property> +   <attribute name="pageId"> +    <string notr="true">6</string> +   </attribute> +   <layout class="QFormLayout" name="formLayout6"> +    <item row="2" column="0"> +     <widget class="QRadioButton" name="radioButton_Audio_Major"> +      <property name="text"> +       <string>Major   The game has major audio errors</string> +      </property> +     </widget> +    </item> +    <item row="4" column="0"> +     <widget class="QRadioButton" name="radioButton_Audio_Minor"> +      <property name="text"> +       <string>Minor   The game has minor audio errors</string>        </property>       </widget>      </item> -    <item row="8" column="1"> -     <widget class="QLabel" name="lbl_WontBoot"> +    <item row="6" column="0"> +     <widget class="QRadioButton" name="radioButton_Audio_No">        <property name="text"> -       <string><html><head/><body><p>The game crashes when attempting to startup.</p></body></html></string> +       <string>None     Audio is played perfectly</string>        </property>       </widget>      </item>      <item row="0" column="0" colspan="2"> -     <widget class="QLabel" name="lbl_Independent"> +     <widget class="QLabel" name="lbl_Independent6">        <property name="font">         <font>          <pointsize>10</pointsize>         </font>        </property>        <property name="text"> -       <string><html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html></string> +       <string><html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html></string>        </property>        <property name="wordWrap">         <bool>true</bool> @@ -187,7 +370,7 @@       </widget>      </item>      <item row="1" column="0" colspan="2"> -     <spacer name="verticalSpacer"> +     <spacer name="verticalSpacer6">        <property name="orientation">         <enum>Qt::Vertical</enum>        </property> @@ -206,7 +389,7 @@      <string>Thank you for your submission!</string>     </property>     <attribute name="pageId"> -    <string notr="true">2</string> +    <string notr="true">7</string>     </attribute>    </widget>   </widget> diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index 5c0217ba8..a47089988 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -2,6 +2,9 @@  // SPDX-License-Identifier: GPL-2.0-or-later  #include <algorithm> +#include <functional> +#include <QDialog> +#include <QDialogButtonBox>  #include <QFileDialog>  #include <QGraphicsItem>  #include <QHeaderView> @@ -108,9 +111,12 @@ ConfigureProfileManager::ConfigureProfileManager(const Core::System& system_, QW      connect(ui->pm_add, &QPushButton::clicked, this, &ConfigureProfileManager::AddUser);      connect(ui->pm_rename, &QPushButton::clicked, this, &ConfigureProfileManager::RenameUser); -    connect(ui->pm_remove, &QPushButton::clicked, this, &ConfigureProfileManager::DeleteUser); +    connect(ui->pm_remove, &QPushButton::clicked, this, +            &ConfigureProfileManager::ConfirmDeleteUser);      connect(ui->pm_set_image, &QPushButton::clicked, this, &ConfigureProfileManager::SetUserImage); +    confirm_dialog = new ConfigureProfileManagerDeleteDialog(this); +      scene = new QGraphicsScene;      ui->current_user_icon->setScene(scene); @@ -230,26 +236,23 @@ void ConfigureProfileManager::RenameUser() {      UpdateCurrentUser();  } -void ConfigureProfileManager::DeleteUser() { +void ConfigureProfileManager::ConfirmDeleteUser() {      const auto index = tree_view->currentIndex().row();      const auto uuid = profile_manager->GetUser(index);      ASSERT(uuid);      const auto username = GetAccountUsername(*profile_manager, *uuid); -    const auto confirm = QMessageBox::question( -        this, tr("Confirm Delete"), -        tr("You are about to delete user with name \"%1\". Are you sure?").arg(username)); - -    if (confirm == QMessageBox::No) { -        return; -    } +    confirm_dialog->SetInfo(username, *uuid, [this, uuid]() { DeleteUser(*uuid); }); +    confirm_dialog->show(); +} +void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) {      if (Settings::values.current_user.GetValue() == tree_view->currentIndex().row()) {          Settings::values.current_user = 0;      }      UpdateCurrentUser(); -    if (!profile_manager->RemoveUser(*uuid)) { +    if (!profile_manager->RemoveUser(uuid)) {          return;      } @@ -319,3 +322,47 @@ void ConfigureProfileManager::SetUserImage() {                          new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)});      UpdateCurrentUser();  } + +ConfigureProfileManagerDeleteDialog::ConfigureProfileManagerDeleteDialog(QWidget* parent) +    : QDialog{parent} { +    auto dialog_vbox_layout = new QVBoxLayout(this); +    dialog_button_box = +        new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No, Qt::Horizontal, parent); +    auto label_message = +        new QLabel(tr("Delete this user? All of the user's save data will be deleted."), this); +    label_info = new QLabel(this); +    auto dialog_hbox_layout_widget = new QWidget(this); +    auto dialog_hbox_layout = new QHBoxLayout(dialog_hbox_layout_widget); +    icon_scene = new QGraphicsScene(0, 0, 64, 64, this); +    auto icon_view = new QGraphicsView(icon_scene, this); + +    dialog_hbox_layout_widget->setLayout(dialog_hbox_layout); +    icon_view->setMaximumSize(64, 64); +    icon_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +    icon_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +    this->setLayout(dialog_vbox_layout); +    this->setWindowTitle(tr("Confirm Delete")); +    this->setSizeGripEnabled(false); +    dialog_vbox_layout->addWidget(label_message); +    dialog_vbox_layout->addWidget(dialog_hbox_layout_widget); +    dialog_vbox_layout->addWidget(dialog_button_box); +    dialog_hbox_layout->addWidget(icon_view); +    dialog_hbox_layout->addWidget(label_info); + +    connect(dialog_button_box, &QDialogButtonBox::rejected, this, [this]() { close(); }); +} + +ConfigureProfileManagerDeleteDialog::~ConfigureProfileManagerDeleteDialog() = default; + +void ConfigureProfileManagerDeleteDialog::SetInfo(const QString& username, const Common::UUID& uuid, +                                                  std::function<void()> accept_callback) { +    label_info->setText( +        tr("Name: %1\nUUID: %2").arg(username, QString::fromStdString(uuid.FormattedString()))); +    icon_scene->clear(); +    icon_scene->addPixmap(GetIcon(uuid)); + +    connect(dialog_button_box, &QDialogButtonBox::accepted, this, [this, accept_callback]() { +        close(); +        accept_callback(); +    }); +} diff --git a/src/yuzu/configuration/configure_profile_manager.h b/src/yuzu/configuration/configure_profile_manager.h index fe9033779..c4b1a334e 100644 --- a/src/yuzu/configuration/configure_profile_manager.h +++ b/src/yuzu/configuration/configure_profile_manager.h @@ -3,16 +3,24 @@  #pragma once +#include <functional>  #include <memory> +#include <QDialog>  #include <QList>  #include <QWidget> +namespace Common { +struct UUID; +} +  namespace Core {  class System;  }  class QGraphicsScene; +class QDialogButtonBox; +class QLabel;  class QStandardItem;  class QStandardItemModel;  class QTreeView; @@ -26,6 +34,20 @@ namespace Ui {  class ConfigureProfileManager;  } +class ConfigureProfileManagerDeleteDialog : public QDialog { +public: +    explicit ConfigureProfileManagerDeleteDialog(QWidget* parent); +    ~ConfigureProfileManagerDeleteDialog(); + +    void SetInfo(const QString& username, const Common::UUID& uuid, +                 std::function<void()> accept_callback); + +private: +    QDialogButtonBox* dialog_button_box; +    QGraphicsScene* icon_scene; +    QLabel* label_info; +}; +  class ConfigureProfileManager : public QWidget {      Q_OBJECT @@ -47,7 +69,8 @@ private:      void SelectUser(const QModelIndex& index);      void AddUser();      void RenameUser(); -    void DeleteUser(); +    void ConfirmDeleteUser(); +    void DeleteUser(const Common::UUID& uuid);      void SetUserImage();      QVBoxLayout* layout; @@ -55,6 +78,8 @@ private:      QStandardItemModel* item_model;      QGraphicsScene* scene; +    ConfigureProfileManagerDeleteDialog* confirm_dialog; +      std::vector<QList<QStandardItem*>> list_items;      std::unique_ptr<Ui::ConfigureProfileManager> ui; diff --git a/src/yuzu/configuration/configure_profile_manager.ui b/src/yuzu/configuration/configure_profile_manager.ui index cfe7478c8..bd6dea4f4 100644 --- a/src/yuzu/configuration/configure_profile_manager.ui +++ b/src/yuzu/configuration/configure_profile_manager.ui @@ -57,6 +57,12 @@                <height>48</height>               </size>              </property> +            <property name="frameShape"> +             <enum>QFrame::NoFrame</enum> +            </property> +            <property name="frameShadow"> +             <enum>QFrame::Plain</enum> +            </property>              <property name="verticalScrollBarPolicy">               <enum>Qt::ScrollBarAlwaysOff</enum>              </property> diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 6198d1e4e..1800f090f 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -145,12 +145,14 @@ public:              const char* tooltip;          };          // clang-format off +        const auto ingame_status = +                       CompatStatus{QStringLiteral("#f2d624"), QT_TR_NOOP("Ingame"),     QT_TR_NOOP("Game starts, but crashes or major glitches prevent it from being completed.")};          static const std::map<QString, CompatStatus> status_data = { -            {QStringLiteral("0"),  {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"),    QT_TR_NOOP("Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without\nany workarounds needed.")}}, -            {QStringLiteral("1"),  {QStringLiteral("#47d35c"), QT_TR_NOOP("Great"),      QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish. May require some\nworkarounds.")}}, -            {QStringLiteral("2"),  {QStringLiteral("#94b242"), QT_TR_NOOP("Okay"),       QT_TR_NOOP("Game functions with major graphical or audio glitches, but game is playable from start to finish with\nworkarounds.")}}, -            {QStringLiteral("3"),  {QStringLiteral("#f2d624"), QT_TR_NOOP("Bad"),        QT_TR_NOOP("Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches\neven with workarounds.")}}, -            {QStringLiteral("4"),  {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start\nScreen.")}}, +            {QStringLiteral("0"),  {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"),    QT_TR_NOOP("Game can be played without issues.")}}, +            {QStringLiteral("1"),  {QStringLiteral("#47d35c"), QT_TR_NOOP("Playable"),   QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish.")}}, +            {QStringLiteral("2"),  ingame_status}, +            {QStringLiteral("3"),  ingame_status}, // Fallback for the removed "Okay" category +            {QStringLiteral("4"),  {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game loads, but is unable to progress past the Start Screen.")}},              {QStringLiteral("5"),  {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}},              {QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}},          }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 59e56633a..4081af391 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -15,6 +15,7 @@  #endif  // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. +#include "applets/qt_amiibo_settings.h"  #include "applets/qt_controller.h"  #include "applets/qt_error.h"  #include "applets/qt_profile_select.h" @@ -26,6 +27,7 @@  #include "configuration/configure_tas.h"  #include "core/file_sys/vfs.h"  #include "core/file_sys/vfs_real.h" +#include "core/frontend/applets/cabinet.h"  #include "core/frontend/applets/controller.h"  #include "core/frontend/applets/general_frontend.h"  #include "core/frontend/applets/mii_edit.h" @@ -342,6 +344,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan      const auto override_build =          fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);      const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; +    const auto processor_count = std::thread::hardware_concurrency();      LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version);      LogRuntimes(); @@ -360,7 +363,11 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan          }      }      LOG_INFO(Frontend, "Host CPU: {}", cpu_string); +    if (std::optional<int> processor_core = Common::GetProcessorCount()) { +        LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core); +    }  #endif +    LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);      LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());      LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",               Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); @@ -543,6 +550,11 @@ void GMainWindow::RegisterMetaTypes() {      // Register applet types +    // Cabinet Applet +    qRegisterMetaType<Core::Frontend::CabinetParameters>("Core::Frontend::CabinetParameters"); +    qRegisterMetaType<std::shared_ptr<Service::NFP::NfpDevice>>( +        "std::shared_ptr<Service::NFP::NfpDevice>"); +      // Controller Applet      qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters"); @@ -564,6 +576,21 @@ void GMainWindow::RegisterMetaTypes() {      qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus");  } +void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters, +                                           std::shared_ptr<Service::NFP::NfpDevice> nfp_device) { +    QtAmiiboSettingsDialog dialog(this, parameters, input_subsystem.get(), nfp_device); + +    dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | +                          Qt::WindowTitleHint | Qt::WindowSystemMenuHint); +    dialog.setWindowModality(Qt::WindowModal); +    if (dialog.exec() == QDialog::Rejected) { +        emit AmiiboSettingsFinished(false, {}); +        return; +    } + +    emit AmiiboSettingsFinished(true, dialog.GetName()); +} +  void GMainWindow::ControllerSelectorReconfigureControllers(      const Core::Frontend::ControllerParameters& parameters) {      QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get(), *system); @@ -1541,6 +1568,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p      system->SetFilesystem(vfs);      system->SetAppletFrontendSet({ +        std::make_unique<QtAmiiboSettings>(*this),     // Amiibo Settings          std::make_unique<QtControllerSelector>(*this), // Controller Selector          std::make_unique<QtErrorDisplay>(*this),       // Error Display          nullptr,                                       // Mii Editor @@ -1951,6 +1979,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target      }      default:          UNIMPLEMENTED(); +        break;      }      const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path)); @@ -2018,38 +2047,50 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src      return true;  } +QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const { +    switch (type) { +    case InstalledEntryType::Game: +        return tr("Error Removing Contents"); +    case InstalledEntryType::Update: +        return tr("Error Removing Update"); +    case InstalledEntryType::AddOnContent: +        return tr("Error Removing DLC"); +    default: +        return QStringLiteral("Error Removing <Invalid Type>"); +    } +}  void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) { -    const QString entry_type = [type] { +    const QString entry_question = [type] {          switch (type) {          case InstalledEntryType::Game: -            return tr("Contents"); +            return tr("Remove Installed Game Contents?");          case InstalledEntryType::Update: -            return tr("Update"); +            return tr("Remove Installed Game Update?");          case InstalledEntryType::AddOnContent: -            return tr("DLC"); +            return tr("Remove Installed Game DLC?");          default: -            return QString{}; +            return QStringLiteral("Remove Installed Game <Invalid Type>?");          }      }(); -    if (QMessageBox::question( -            this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type), -            QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { +    if (QMessageBox::question(this, tr("Remove Entry"), entry_question, +                              QMessageBox::Yes | QMessageBox::No, +                              QMessageBox::No) != QMessageBox::Yes) {          return;      }      switch (type) {      case InstalledEntryType::Game: -        RemoveBaseContent(program_id, entry_type); +        RemoveBaseContent(program_id, type);          [[fallthrough]];      case InstalledEntryType::Update: -        RemoveUpdateContent(program_id, entry_type); +        RemoveUpdateContent(program_id, type);          if (type != InstalledEntryType::Game) {              break;          }          [[fallthrough]];      case InstalledEntryType::AddOnContent: -        RemoveAddOnContent(program_id, entry_type); +        RemoveAddOnContent(program_id, type);          break;      }      Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / @@ -2057,7 +2098,7 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT      game_list->PopulateAsync(UISettings::values.game_dirs);  } -void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) { +void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) {      const auto& fs_controller = system->GetFileSystemController();      const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) ||                       fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); @@ -2067,12 +2108,12 @@ void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) {                                   tr("Successfully removed the installed base game."));      } else {          QMessageBox::warning( -            this, tr("Error Removing %1").arg(entry_type), +            this, GetGameListErrorRemoving(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) { +void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) {      const auto update_id = program_id | 0x800;      const auto& fs_controller = system->GetFileSystemController();      const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || @@ -2082,12 +2123,12 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type)          QMessageBox::information(this, tr("Successfully Removed"),                                   tr("Successfully removed the installed update."));      } else { -        QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), +        QMessageBox::warning(this, GetGameListErrorRemoving(type),                               tr("There is no update installed for this title."));      }  } -void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) { +void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) {      u32 count{};      const auto& fs_controller = system->GetFileSystemController();      const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( @@ -2105,7 +2146,7 @@ void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type)      }      if (count == 0) { -        QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), +        QMessageBox::warning(this, GetGameListErrorRemoving(type),                               tr("There are no DLC installed for this title."));          return;      } @@ -2803,6 +2844,20 @@ void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_tex  }  void GMainWindow::OnMenuReportCompatibility() { +    const auto& caps = Common::GetCPUCaps(); +    const bool has_fma = caps.fma || caps.fma4; +    const auto processor_count = std::thread::hardware_concurrency(); +    const bool has_4threads = processor_count == 0 || processor_count >= 4; +    const bool has_8gb_ram = Common::GetMemInfo().TotalPhysicalMemory >= 8_GiB; +    const bool has_broken_vulkan = UISettings::values.has_broken_vulkan; + +    if (!has_fma || !has_4threads || !has_8gb_ram || has_broken_vulkan) { +        QMessageBox::critical(this, tr("Hardware requirements not met"), +                              tr("Your system does not meet the recommended hardware requirements. " +                                 "Compatibility reporting has been disabled.")); +        return; +    } +      if (!Settings::values.yuzu_token.GetValue().empty() &&          !Settings::values.yuzu_username.GetValue().empty()) {          CompatDB compatdb{system->TelemetrySession(), this}; @@ -3168,6 +3223,7 @@ void GMainWindow::OnToggleGpuAccuracy() {      case Settings::GPUAccuracy::Extreme:      default: {          Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); +        break;      }      } @@ -3500,6 +3556,7 @@ void GMainWindow::UpdateGPUAccuracyButton() {      default: {          gpu_accuracy_button->setText(tr("GPU ERROR"));          gpu_accuracy_button->setChecked(true); +        break;      }      }  } diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 150ada84c..6a9992d05 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -55,6 +55,7 @@ class System;  } // namespace Core  namespace Core::Frontend { +struct CabinetParameters;  struct ControllerParameters;  struct InlineAppearParameters;  struct InlineTextParameters; @@ -82,6 +83,10 @@ enum class SwkbdReplyType : u32;  enum class WebExitReason : u32;  } // namespace Service::AM::Applets +namespace Service::NFP { +class NfpDevice; +} // namespace Service::NFP +  namespace Ui {  class MainWindow;  } @@ -149,6 +154,8 @@ signals:      void UpdateInstallProgress(); +    void AmiiboSettingsFinished(bool is_success, const std::string& name); +      void ControllerSelectorReconfigureFinished();      void ErrorDisplayFinished(); @@ -170,6 +177,8 @@ public slots:      void OnExecuteProgram(std::size_t program_index);      void OnExit();      void OnSaveConfig(); +    void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters, +                                  std::shared_ptr<Service::NFP::NfpDevice> nfp_device);      void ControllerSelectorReconfigureControllers(          const Core::Frontend::ControllerParameters& parameters);      void SoftwareKeyboardInitialize( @@ -324,9 +333,10 @@ private slots:      void OnMouseActivity();  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); +    QString GetGameListErrorRemoving(InstalledEntryType type) const; +    void RemoveBaseContent(u64 program_id, InstalledEntryType type); +    void RemoveUpdateContent(u64 program_id, InstalledEntryType type); +    void RemoveAddOnContent(u64 program_id, InstalledEntryType type);      void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target);      void RemoveAllTransferableShaderCaches(u64 program_id);      void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); | 
