code:
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)

# Set the RPATH for Qt Libraries
# This must be done before the `yuzu` target is created
if (YUZU_USE_BUNDLED_QT AND (${CMAKE_SYSTEM_NAME} STREQUAL "Linux"))
    set(CMAKE_BUILD_RPATH "${CMAKE_BINARY_DIR}/bin/lib/")
endif()

add_executable(yuzu
    Info.plist
    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
    applets/qt_error.cpp
    applets/qt_error.h
    applets/qt_profile_select.cpp
    applets/qt_profile_select.h
    applets/qt_software_keyboard.cpp
    applets/qt_software_keyboard.h
    applets/qt_software_keyboard.ui
    applets/qt_web_browser.cpp
    applets/qt_web_browser.h
    applets/qt_web_browser_scripts.h
    bootmanager.cpp
    bootmanager.h
    compatdb.ui
    compatibility_list.cpp
    compatibility_list.h
    configuration/configuration_shared.cpp
    configuration/configuration_shared.h
    configuration/configure.ui
    configuration/configure_applets.cpp
    configuration/configure_applets.h
    configuration/configure_applets.ui
    configuration/configure_audio.cpp
    configuration/configure_audio.h
    configuration/configure_audio.ui
    configuration/configure_camera.cpp
    configuration/configure_camera.h
    configuration/configure_camera.ui
    configuration/configure_cpu.cpp
    configuration/configure_cpu.h
    configuration/configure_cpu.ui
    configuration/configure_cpu_debug.cpp
    configuration/configure_cpu_debug.h
    configuration/configure_cpu_debug.ui
    configuration/configure_debug.cpp
    configuration/configure_debug.h
    configuration/configure_debug.ui
    configuration/configure_debug_controller.cpp
    configuration/configure_debug_controller.h
    configuration/configure_debug_controller.ui
    configuration/configure_debug_tab.cpp
    configuration/configure_debug_tab.h
    configuration/configure_debug_tab.ui
    configuration/configure_dialog.cpp
    configuration/configure_dialog.h
    configuration/configure_filesystem.cpp
    configuration/configure_filesystem.h
    configuration/configure_filesystem.ui
    configuration/configure_general.cpp
    configuration/configure_general.h
    configuration/configure_general.ui
    configuration/configure_graphics.cpp
    configuration/configure_graphics.h
    configuration/configure_graphics.ui
    configuration/configure_graphics_advanced.cpp
    configuration/configure_graphics_advanced.h
    configuration/configure_graphics_advanced.ui
    configuration/configure_hotkeys.cpp
    configuration/configure_hotkeys.h
    configuration/configure_hotkeys.ui
    configuration/configure_input.cpp
    configuration/configure_input.h
    configuration/configure_input.ui
    configuration/configure_input_advanced.cpp
    configuration/configure_input_advanced.h
    configuration/configure_input_advanced.ui
    configuration/configure_input_per_game.cpp
    configuration/configure_input_per_game.h
    configuration/configure_input_per_game.ui
    configuration/configure_input_player.cpp
    configuration/configure_input_player.h
    configuration/configure_input_player.ui
    configuration/configure_input_player_widget.cpp
    configuration/configure_input_player_widget.h
    configuration/configure_input_profile_dialog.cpp
    configuration/configure_input_profile_dialog.h
    configuration/configure_input_profile_dialog.ui
    configuration/configure_linux_tab.cpp
    configuration/configure_linux_tab.h
    configuration/configure_linux_tab.ui
    configuration/configure_mouse_panning.cpp
    configuration/configure_mouse_panning.h
    configuration/configure_mouse_panning.ui
    configuration/configure_motion_touch.cpp
    configuration/configure_motion_touch.h
    configuration/configure_motion_touch.ui
    configuration/configure_per_game.cpp
    configuration/configure_per_game.h
    configuration/configure_per_game.ui
    configuration/configure_per_game_addons.cpp
    configuration/configure_per_game_addons.h
    configuration/configure_per_game_addons.ui
    configuration/configure_profile_manager.cpp
    configuration/configure_profile_manager.h
    configuration/configure_profile_manager.ui
    configuration/configure_ringcon.cpp
    configuration/configure_ringcon.h
    configuration/configure_ringcon.ui
    configuration/configure_network.cpp
    configuration/configure_network.h
    configuration/configure_network.ui
    configuration/configure_system.cpp
    configuration/configure_system.h
    configuration/configure_system.ui
    configuration/configure_tas.cpp
    configuration/configure_tas.h
    configuration/configure_tas.ui
    configuration/configure_touch_from_button.cpp
    configuration/configure_touch_from_button.h
    configuration/configure_touch_from_button.ui
    configuration/configure_touchscreen_advanced.cpp
    configuration/configure_touchscreen_advanced.h
    configuration/configure_touchscreen_advanced.ui
    configuration/configure_touch_widget.h
    configuration/configure_ui.cpp
    configuration/configure_ui.h
    configuration/configure_ui.ui
    configuration/configure_vibration.cpp
    configuration/configure_vibration.h
    configuration/configure_vibration.ui
    configuration/configure_web.cpp
    configuration/configure_web.h
    configuration/configure_web.ui
    configuration/input_profiles.cpp
    configuration/input_profiles.h
    configuration/shared_translation.cpp
    configuration/shared_translation.h
    configuration/shared_widget.cpp
    configuration/shared_widget.h
    configuration/qt_config.cpp
    configuration/qt_config.h
    debugger/console.cpp
    debugger/console.h
    debugger/controller.cpp
    debugger/controller.h
    debugger/profiler.cpp
    debugger/profiler.h
    debugger/wait_tree.cpp
    debugger/wait_tree.h
    discord.h
    game_list.cpp
    game_list.h
    game_list_p.h
    game_list_worker.cpp
    game_list_worker.h
    hotkeys.cpp
    hotkeys.h
    install_dialog.cpp
    install_dialog.h
    loading_screen.cpp
    loading_screen.h
    loading_screen.ui
    main.cpp
    main.h
    main.ui
    multiplayer/chat_room.cpp
    multiplayer/chat_room.h
    multiplayer/chat_room.ui
    multiplayer/client_room.h
    multiplayer/client_room.cpp
    multiplayer/client_room.ui
    multiplayer/direct_connect.cpp
    multiplayer/direct_connect.h
    multiplayer/direct_connect.ui
    multiplayer/host_room.cpp
    multiplayer/host_room.h
    multiplayer/host_room.ui
    multiplayer/lobby.cpp
    multiplayer/lobby.h
    multiplayer/lobby.ui
    multiplayer/lobby_p.h
    multiplayer/message.cpp
    multiplayer/message.h
    multiplayer/moderation_dialog.cpp
    multiplayer/moderation_dialog.h
    multiplayer/moderation_dialog.ui
    multiplayer/state.cpp
    multiplayer/state.h
    multiplayer/validation.h
    play_time_manager.cpp
    play_time_manager.h
    precompiled_headers.h
    qt_common.cpp
    qt_common.h
    startup_checks.cpp
    startup_checks.h
    uisettings.cpp
    uisettings.h
    util/clickable_label.cpp
    util/clickable_label.h
    util/controller_navigation.cpp
    util/controller_navigation.h
    util/limitable_input_dialog.cpp
    util/limitable_input_dialog.h
    util/overlay_dialog.cpp
    util/overlay_dialog.h
    util/overlay_dialog.ui
    util/sequence_dialog/sequence_dialog.cpp
    util/sequence_dialog/sequence_dialog.h
    util/url_request_interceptor.cpp
    util/url_request_interceptor.h
    util/util.cpp
    util/util.h
    vk_device_info.cpp
    vk_device_info.h
    compatdb.cpp
    compatdb.h
    yuzu.qrc
    yuzu.rc
)

if (YUZU_CRASH_DUMPS)
    target_sources(yuzu PRIVATE
        breakpad.cpp
        breakpad.h
    )

    target_link_libraries(yuzu PRIVATE libbreakpad_client)
    target_compile_definitions(yuzu PRIVATE YUZU_CRASH_DUMPS)
endif()

if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
    target_compile_definitions(yuzu PRIVATE
        $<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,15>:CANNOT_EXPLICITLY_INSTANTIATE>
    )
endif()

file(GLOB COMPAT_LIST
     ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
     ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*)
file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*)

if (ENABLE_QT_TRANSLATION)
    set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend")
    option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF)
    option(WORKAROUND_BROKEN_LUPDATE "Run lupdate directly through CMake if Qt's convenience wrappers don't work" OFF)

    # Update source TS file if enabled
    if (GENERATE_QT_TRANSLATION)
        get_target_property(SRCS yuzu SOURCES)
        # these calls to qt_create_translation also creates a rule to generate en.qm which conflicts with providing english plurals
        # so we have to set a OUTPUT_LOCATION so that we don't have multiple rules to generate en.qm
        set_source_files_properties(${YUZU_QT_LANGUAGES}/en.ts PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
        if (WORKAROUND_BROKEN_LUPDATE)
            add_custom_command(OUTPUT ${YUZU_QT_LANGUAGES}/en.ts
                COMMAND lupdate
                    -source-language en_US
                    -target-language en_US
                    ${SRCS}
                    ${UIS}
                    -ts ${YUZU_QT_LANGUAGES}/en.ts
                DEPENDS
                    ${SRCS}
                    ${UIS}
                WORKING_DIRECTORY
                    ${CMAKE_CURRENT_SOURCE_DIR}
            )
        else()
            qt_create_translation(QM_FILES
                ${SRCS}
                ${UIS}
                ${YUZU_QT_LANGUAGES}/en.ts
            OPTIONS
                -source-language en_US
                -target-language en_US
            )
        endif()

        # Generate plurals into dist/english_plurals/generated_en.ts so it can be used to revise dist/english_plurals/en.ts
        set(GENERATED_PLURALS_FILE ${PROJECT_SOURCE_DIR}/dist/english_plurals/generated_en.ts)
        set_source_files_properties(${GENERATED_PLURALS_FILE} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/plurals")
        if (WORKAROUND_BROKEN_LUPDATE)
            add_custom_command(OUTPUT ${GENERATED_PLURALS_FILE}
                COMMAND lupdate
                    -source-language en_US
                    -target-language en_US
                    ${SRCS}
                    ${UIS}
                    -ts ${GENERATED_PLURALS_FILE}
                DEPENDS
                    ${SRCS}
                    ${UIS}
                WORKING_DIRECTORY
                    ${CMAKE_CURRENT_SOURCE_DIR}
            )
        else()
            qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US)
        endif()

        add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts ${GENERATED_PLURALS_FILE})
    endif()

    # Find all TS files except en.ts
    file(GLOB_RECURSE LANGUAGES_TS ${YUZU_QT_LANGUAGES}/*.ts)
    list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts)

    # Compile TS files to QM files
    qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS})

    # Compile english plurals TS file to en.qm
    qt_add_translation(LANGUAGES_QM ${PROJECT_SOURCE_DIR}/dist/english_plurals/en.ts)

    # Build a QRC file from the QM file list
    set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc)
    file(WRITE ${LANGUAGES_QRC} "<RCC><qresource prefix=\"languages\">\n")
    foreach (QM ${LANGUAGES_QM})
        get_filename_component(QM_FILE ${QM} NAME)
        file(APPEND ${LANGUAGES_QRC} "<file>${QM_FILE}</file>\n")
    endforeach (QM)
    file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>")

    # Add the QRC file to package in all QM files
    qt_add_resources(LANGUAGES ${LANGUAGES_QRC})
else()
    set(LANGUAGES)
endif()

target_sources(yuzu
    PRIVATE
        ${COMPAT_LIST}
        ${ICONS}
        ${LANGUAGES}
        ${THEMES}
)

if (APPLE)
    set(MACOSX_ICON "../../dist/yuzu.icns")
    set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
    target_sources(yuzu PRIVATE ${MACOSX_ICON})
    set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE TRUE)
    set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)

    if (NOT USE_SYSTEM_MOLTENVK)
        set(MOLTENVK_PLATFORM "macOS")
        set(MOLTENVK_VERSION "v1.2.7")
        download_moltenvk_external(${MOLTENVK_PLATFORM} ${MOLTENVK_VERSION})
    endif()
    find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)
    message(STATUS "Using MoltenVK at ${MOLTENVK_LIBRARY}.")
    set_source_files_properties(${MOLTENVK_LIBRARY} PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks
                                    XCODE_FILE_ATTRIBUTES "CodeSignOnCopy")
    target_sources(yuzu PRIVATE ${MOLTENVK_LIBRARY})

elseif(WIN32)
    # compile as a win32 gui application instead of a console application
    if (QT_VERSION VERSION_GREATER_EQUAL 6)
        target_link_libraries(yuzu PRIVATE Qt6::EntryPointPrivate)
    else()
        target_link_libraries(yuzu PRIVATE Qt5::WinMain)
    endif()
    if(MSVC)
        target_link_libraries(yuzu PRIVATE version.lib)
        set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
    elseif(MINGW)
        set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
    endif()
endif()

target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core)
target_link_libraries(yuzu PRIVATE Boost::headers glad Qt${QT_MAJOR_VERSION}::Widgets)
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)

target_link_libraries(yuzu PRIVATE Vulkan::Headers)
if (NOT WIN32)
    target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
endif()
if (UNIX AND NOT APPLE)
    target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus)
endif()

target_compile_definitions(yuzu PRIVATE
    # Use QStringBuilder for string concatenation to reduce
    # the overall number of temporary strings created.
    -DQT_USE_QSTRINGBUILDER

    # Disable implicit conversions from/to C strings
    -DQT_NO_CAST_FROM_ASCII
    -DQT_NO_CAST_TO_ASCII

    # Disable implicit type narrowing in signal/slot connect() calls.
    -DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT

    # Disable unsafe overloads of QProcess' start() function.
    -DQT_NO_PROCESS_COMBINED_ARGUMENT_START

    # Disable implicit QString->QUrl conversions to enforce use of proper resolving functions.
    -DQT_NO_URL_CAST_FROM_STRING
)

if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
    target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_COMPATIBILITY_REPORTING)
endif()

if (USE_DISCORD_PRESENCE)
    target_sources(yuzu PUBLIC
        discord_impl.cpp
        discord_impl.h
    )
    target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib Qt${QT_MAJOR_VERSION}::Network)
    target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE)
endif()

if (ENABLE_WEB_SERVICE)
    target_compile_definitions(yuzu PRIVATE -DENABLE_WEB_SERVICE)
endif()

if (YUZU_USE_QT_MULTIMEDIA)
    target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::Multimedia)
    target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_MULTIMEDIA)
endif ()

if (YUZU_USE_QT_WEB_ENGINE)
    target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::WebEngineCore Qt${QT_MAJOR_VERSION}::WebEngineWidgets)
    target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE)
endif ()

if(UNIX AND NOT APPLE)
    install(TARGETS yuzu)
endif()

if (WIN32 AND QT_VERSION VERSION_GREATER_EQUAL 6)
    set(YUZU_EXE_DIR "$<TARGET_FILE_DIR:yuzu>")
    add_custom_command(TARGET yuzu POST_BUILD COMMAND ${WINDEPLOYQT_EXECUTABLE} "${YUZU_EXE_DIR}/yuzu.exe" --dir "${YUZU_EXE_DIR}" --libdir "${YUZU_EXE_DIR}" --plugindir "${YUZU_EXE_DIR}/plugins" --no-compiler-runtime --no-opengl-sw --no-system-d3d-compiler --no-translations --verbose 0)
endif()

if (YUZU_USE_BUNDLED_QT AND QT_VERSION VERSION_LESS 6)
    include(CopyYuzuQt5Deps)
    copy_yuzu_Qt5_deps(yuzu)
endif()

if (ENABLE_SDL2)
    target_link_libraries(yuzu PRIVATE SDL2::SDL2)
    target_compile_definitions(yuzu PRIVATE HAVE_SDL2)
endif()

if (MSVC)
    include(CopyYuzuSDLDeps)
    include(CopyYuzuFFmpegDeps)
    copy_yuzu_SDL_deps(yuzu)
    copy_yuzu_FFmpeg_deps(yuzu)
endif()

if (NOT APPLE AND ENABLE_OPENGL)
    target_compile_definitions(yuzu PRIVATE HAS_OPENGL)
endif()

if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
    target_link_libraries(yuzu PRIVATE dynarmic::dynarmic)
endif()

if (YUZU_USE_PRECOMPILED_HEADERS)
    target_precompile_headers(yuzu PRIVATE precompiled_headers.h)
endif()

create_target_directory_groups(yuzu)


code:
<?xml version="1.0" encoding="UTF-8"?>

<!--
SPDX-FileCopyrightText: 2015 Pierre de La Morinerie <kemenaran@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
-->

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleExecutable</key>
    <string>${EXECUTABLE_NAME}</string>
    <key>CFBundleGetInfoString</key>
    <string></string>
    <key>CFBundleIconFile</key>
    <string>yuzu.icns</string>
    <key>CFBundleIdentifier</key>
    <string>com.yuzu-emu.yuzu</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleLongVersionString</key>
    <string></string>
    <key>CFBundleName</key>
    <string>yuzu</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string></string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleVersion</key>
    <string></string>
    <key>CSResourcesFileMapped</key>
    <true/>
    <key>LSApplicationCategoryType</key>
    <string>public.app-category.games</string>
    <key>LSRequiresCarbon</key>
    <true/>
    <key>NSHumanReadableCopyright</key>
    <string></string>
    <key>NSPrincipalClass</key>
    <string>NSApplication</string>
    <key>NSHighResolutionCapable</key>
    <string>True</string>
</dict>
</plist>

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <QIcon>
#include <fmt/format.h>
#include "common/scm_rev.h"
#include "ui_aboutdialog.h"
#include "yuzu/about_dialog.h"

AboutDialog::AboutDialog(QWidget* parent)
    : QDialog(parent), ui{std::make_unique<Ui::AboutDialog>()} {
    const auto branch_name = std::string(Common::g_scm_branch);
    const auto description = std::string(Common::g_scm_desc);
    const auto build_id = std::string(Common::g_build_id);

    const auto yuzu_build = fmt::format("yuzu Development Build | {}-{}", branch_name, description);
    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;

    ui->setupUi(this);
    // Try and request the icon from Qt theme (Linux?)
    const QIcon yuzu_logo = QIcon::fromTheme(QStringLiteral("org.yuzu_emu.yuzu"));
    if (!yuzu_logo.isNull()) {
        ui->labelLogo->setPixmap(yuzu_logo.pixmap(200));
    }
    ui->labelBuildInfo->setText(
        ui->labelBuildInfo->text().arg(QString::fromStdString(yuzu_build_version),
                                       QString::fromUtf8(Common::g_build_date).left(10)));
}

AboutDialog::~AboutDialog() = default;


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>
#include <QDialog>

namespace Ui {
class AboutDialog;
}

class AboutDialog : public QDialog {
    Q_OBJECT

public:
    explicit AboutDialog(QWidget* parent);
    ~AboutDialog() override;

private:
    std::unique_ptr<Ui::AboutDialog> ui;
};


code:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>AboutDialog</class>
 <widget class="QDialog" name="AboutDialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>616</width>
    <height>294</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>About yuzu</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout_3">
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1">
     <item>
      <layout class="QVBoxLayout" name="verticalLayout_2">
       <item>
        <widget class="QLabel" name="labelLogo">
         <property name="sizePolicy">
          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
           <horstretch>0</horstretch>
           <verstretch>0</verstretch>
          </sizepolicy>
         </property>
         <property name="maximumSize">
          <size>
           <width>200</width>
           <height>200</height>
          </size>
         </property>
         <property name="text">
          <string/>
         </property>
         <property name="pixmap">
          <pixmap resource="../../dist/qt_themes/default/default.qrc">:/icons/default/256x256/yuzu.png</pixmap>
         </property>
         <property name="scaledContents">
          <bool>true</bool>
         </property>
        </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>
     <item>
      <layout class="QVBoxLayout" name="verticalLayout">
       <item>
        <widget class="QLabel" name="labelYuzu">
         <property name="sizePolicy">
          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
           <horstretch>0</horstretch>
           <verstretch>0</verstretch>
          </sizepolicy>
         </property>
         <property name="text">
          <string>
yuzu
</string>
         </property>
        </widget>
       </item>
       <item>
        <widget class="QLabel" name="labelBuildInfo">
         <property name="sizePolicy">
          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
           <horstretch>0</horstretch>
           <verstretch>0</verstretch>
          </sizepolicy>
         </property>
         <property name="text">
          <string>
%1 (%2)
</string>
         </property>
        </widget>
       </item>
       <item>
        <widget class="QLabel" name="labelAbout">
         <property name="sizePolicy">
          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
           <horstretch>0</horstretch>
           <verstretch>0</verstretch>
          </sizepolicy>
         </property>
         <property name="text">
          <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<meta name="qrichtext" content="1" />

yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+.

This software should not be used to play games you have not legally obtained.
</string>
         </property>
         <property name="alignment">
          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
         </property>
         <property name="wordWrap">
          <bool>true</bool>
         </property>
        </widget>
       </item>
       <item>
        <spacer name="verticalSpacer_2">
         <property name="orientation">
          <enum>Qt::Vertical</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>20</width>
           <height>40</height>
          </size>
         </property>
        </spacer>
       </item>
       <item>
        <widget class="QLabel" name="labelLinks">
         <property name="text">
          <string>
Website | Source Code | Contributors | License
</string>
         </property>
         <property name="openExternalLinks">
          <bool>true</bool>
         </property>
        </widget>
       </item>
       <item>
        <widget class="QLabel" name="labelLiability">
         <property name="sizePolicy">
          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
           <horstretch>0</horstretch>
           <verstretch>0</verstretch>
          </sizepolicy>
         </property>
         <property name="text">
          <string>
&quot;Nintendo Switch&quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.
</string>
         </property>
        </widget>
       </item>
      </layout>
     </item>
    </layout>
   </item>
   <item>
    <widget class="QDialogButtonBox" name="buttonBox">
     <property name="orientation">
      <enum>Qt::Horizontal</enum>
     </property>
     <property name="standardButtons">
      <set>QDialogButtonBox::Ok</set>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources>
  <include location="../../dist/qt_themes_default/default/default.qrc"/>
  <include location="../../dist/qt_themes/default/default.qrc"/>
 </resources>
 <connections>
  <connection>
   <sender>buttonBox</sender>
   <signal>accepted()</signal>
   <receiver>AboutDialog</receiver>
   <slot>accept()</slot>
  </connection>
  <connection>
   <sender>buttonBox</sender>
   <signal>rejected()</signal>
   <receiver>AboutDialog</receiver>
   <slot>reject()</slot>
  </connection>
 </connections>
</ui>


code:
// 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/nfc/common/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"
#ifdef ENABLE_WEB_SERVICE
#include "web_service/web_backend.h"
#endif
#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::NFC::NfcDevice> 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_initialized) {
        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::NFC::DeviceState::TagFound &&
        nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagMounted) {
        return;
    }
    nfp_device->Mount(Service::NFP::ModelType::Amiibo, Service::NFP::MountTarget::All);

    LoadAmiiboInfo();
    LoadAmiiboData();
    LoadAmiiboGameInfo();

    ui->amiiboDirectoryValue->setText(
        QString::fromStdString(input_subsystem->GetVirtualAmiibo()->GetLastFilePath()));

    SetSettingsDescription();
    is_initialized = 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) {
#ifdef ENABLE_WEB_SERVICE
    // 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);
#endif
}

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.GetNickname().data.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(this, &QtAmiiboSettings::MainWindowRequestExit, &parent,
            &GMainWindow::AmiiboSettingsRequestExit, Qt::QueuedConnection);
    connect(&parent, &GMainWindow::AmiiboSettingsFinished, this,
            &QtAmiiboSettings::MainWindowFinished, Qt::QueuedConnection);
}

QtAmiiboSettings::~QtAmiiboSettings() = default;

void QtAmiiboSettings::Close() const {
    callback = {};
    emit MainWindowRequestExit();
}

void QtAmiiboSettings::ShowCabinetApplet(
    const Core::Frontend::CabinetCallback& callback_,
    const Core::Frontend::CabinetParameters& parameters,
    std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const {
    callback = std::move(callback_);
    emit MainWindowShowAmiiboSettings(parameters, nfp_device);
}

void QtAmiiboSettings::MainWindowFinished(bool is_success, const std::string& name) {
    if (callback) {
        callback(is_success, name);
    }
}


code:
// 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::NFC {
class NfcDevice;
} // namespace Service::NFC

class QtAmiiboSettingsDialog final : public QDialog {
    Q_OBJECT

public:
    explicit QtAmiiboSettingsDialog(QWidget* parent, Core::Frontend::CabinetParameters parameters_,
                                    InputCommon::InputSubsystem* input_subsystem_,
                                    std::shared_ptr<Service::NFC::NfcDevice> 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::NFC::NfcDevice> nfp_device;

    // Parameters sent in from the backend HLE applet.
    Core::Frontend::CabinetParameters parameters;

    // If false amiibo settings failed to load
    bool is_initialized{};
};

class QtAmiiboSettings final : public QObject, public Core::Frontend::CabinetApplet {
    Q_OBJECT

public:
    explicit QtAmiiboSettings(GMainWindow& parent);
    ~QtAmiiboSettings() override;

    void Close() const override;
    void ShowCabinetApplet(const Core::Frontend::CabinetCallback& callback_,
                           const Core::Frontend::CabinetParameters& parameters,
                           std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const override;

signals:
    void MainWindowShowAmiiboSettings(const Core::Frontend::CabinetParameters& parameters,
                                      std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const;
    void MainWindowRequestExit() const;

private:
    void MainWindowFinished(bool is_success, const std::string& name);

    mutable Core::Frontend::CabinetCallback callback;
};


code:
<?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>


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>
#include <thread>

#include "common/assert.h"
#include "common/settings.h"
#include "common/settings_enums.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/service/sm/sm.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_types.h"
#include "hid_core/resources/npad/npad.h"
#include "ui_qt_controller.h"
#include "yuzu/applets/qt_controller.h"
#include "yuzu/configuration/configure_input.h"
#include "yuzu/configuration/configure_input_profile_dialog.h"
#include "yuzu/configuration/configure_motion_touch.h"
#include "yuzu/configuration/configure_vibration.h"
#include "yuzu/configuration/input_profiles.h"
#include "yuzu/main.h"
#include "yuzu/util/controller_navigation.h"

namespace {

void UpdateController(Core::HID::EmulatedController* controller,
                      Core::HID::NpadStyleIndex controller_type, bool connected) {
    if (controller->IsConnected(true)) {
        controller->Disconnect();
    }
    controller->SetNpadStyleIndex(controller_type);
    if (connected) {
        controller->Connect(true);
    }
}

// Returns true if the given controller type is compatible with the given parameters.
bool IsControllerCompatible(Core::HID::NpadStyleIndex controller_type,
                            Core::Frontend::ControllerParameters parameters) {
    switch (controller_type) {
    case Core::HID::NpadStyleIndex::Fullkey:
        return parameters.allow_pro_controller;
    case Core::HID::NpadStyleIndex::JoyconDual:
        return parameters.allow_dual_joycons;
    case Core::HID::NpadStyleIndex::JoyconLeft:
        return parameters.allow_left_joycon;
    case Core::HID::NpadStyleIndex::JoyconRight:
        return parameters.allow_right_joycon;
    case Core::HID::NpadStyleIndex::Handheld:
        return parameters.enable_single_mode && parameters.allow_handheld;
    case Core::HID::NpadStyleIndex::GameCube:
        return parameters.allow_gamecube_controller;
    default:
        return false;
    }
}

} // namespace

QtControllerSelectorDialog::QtControllerSelectorDialog(
    QWidget* parent, Core::Frontend::ControllerParameters parameters_,
    InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
    : QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()),
      parameters(std::move(parameters_)), input_subsystem{input_subsystem_},
      input_profiles(std::make_unique<InputProfiles>()), system{system_} {
    ui->setupUi(this);

    player_widgets = {
        ui->widgetPlayer1, ui->widgetPlayer2, ui->widgetPlayer3, ui->widgetPlayer4,
        ui->widgetPlayer5, ui->widgetPlayer6, ui->widgetPlayer7, ui->widgetPlayer8,
    };

    player_groupboxes = {
        ui->groupPlayer1Connected, ui->groupPlayer2Connected, ui->groupPlayer3Connected,
        ui->groupPlayer4Connected, ui->groupPlayer5Connected, ui->groupPlayer6Connected,
        ui->groupPlayer7Connected, ui->groupPlayer8Connected,
    };

    connected_controller_icons = {
        ui->controllerPlayer1, ui->controllerPlayer2, ui->controllerPlayer3, ui->controllerPlayer4,
        ui->controllerPlayer5, ui->controllerPlayer6, ui->controllerPlayer7, ui->controllerPlayer8,
    };

    led_patterns_boxes = {{
        {ui->checkboxPlayer1LED1, ui->checkboxPlayer1LED2, ui->checkboxPlayer1LED3,
         ui->checkboxPlayer1LED4},
        {ui->checkboxPlayer2LED1, ui->checkboxPlayer2LED2, ui->checkboxPlayer2LED3,
         ui->checkboxPlayer2LED4},
        {ui->checkboxPlayer3LED1, ui->checkboxPlayer3LED2, ui->checkboxPlayer3LED3,
         ui->checkboxPlayer3LED4},
        {ui->checkboxPlayer4LED1, ui->checkboxPlayer4LED2, ui->checkboxPlayer4LED3,
         ui->checkboxPlayer4LED4},
        {ui->checkboxPlayer5LED1, ui->checkboxPlayer5LED2, ui->checkboxPlayer5LED3,
         ui->checkboxPlayer5LED4},
        {ui->checkboxPlayer6LED1, ui->checkboxPlayer6LED2, ui->checkboxPlayer6LED3,
         ui->checkboxPlayer6LED4},
        {ui->checkboxPlayer7LED1, ui->checkboxPlayer7LED2, ui->checkboxPlayer7LED3,
         ui->checkboxPlayer7LED4},
        {ui->checkboxPlayer8LED1, ui->checkboxPlayer8LED2, ui->checkboxPlayer8LED3,
         ui->checkboxPlayer8LED4},
    }};

    explain_text_labels = {
        ui->labelPlayer1Explain, ui->labelPlayer2Explain, ui->labelPlayer3Explain,
        ui->labelPlayer4Explain, ui->labelPlayer5Explain, ui->labelPlayer6Explain,
        ui->labelPlayer7Explain, ui->labelPlayer8Explain,
    };

    emulated_controllers = {
        ui->comboPlayer1Emulated, ui->comboPlayer2Emulated, ui->comboPlayer3Emulated,
        ui->comboPlayer4Emulated, ui->comboPlayer5Emulated, ui->comboPlayer6Emulated,
        ui->comboPlayer7Emulated, ui->comboPlayer8Emulated,
    };

    player_labels = {
        ui->labelPlayer1, ui->labelPlayer2, ui->labelPlayer3, ui->labelPlayer4,
        ui->labelPlayer5, ui->labelPlayer6, ui->labelPlayer7, ui->labelPlayer8,
    };

    connected_controller_labels = {
        ui->labelConnectedPlayer1, ui->labelConnectedPlayer2, ui->labelConnectedPlayer3,
        ui->labelConnectedPlayer4, ui->labelConnectedPlayer5, ui->labelConnectedPlayer6,
        ui->labelConnectedPlayer7, ui->labelConnectedPlayer8,
    };

    connected_controller_checkboxes = {
        ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
        ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
        ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
    };

    ui->labelError->setVisible(false);

    // Setup/load everything prior to setting up connections.
    // This avoids unintentionally changing the states of elements while loading them in.
    SetSupportedControllers();
    DisableUnsupportedPlayers();

    for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) {
        SetEmulatedControllers(player_index);
    }

    LoadConfiguration();

    controller_navigation = new ControllerNavigation(system.HIDCore(), this);

    for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
        SetExplainText(i);
        UpdateControllerIcon(i);
        UpdateLEDPattern(i);
        UpdateBorderColor(i);

        connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) {
            // Reconnect current controller if it was the last one checked
            // (player number was reduced by more than one)
            const bool reconnect_first = !checked && i < player_groupboxes.size() - 1 &&
                                         player_groupboxes[i + 1]->isChecked();

            // Ensures that connecting a controller changes the number of players
            if (connected_controller_checkboxes[i]->isChecked() != checked) {
                // Ensures that the players are always connected in sequential order
                PropagatePlayerNumberChanged(i, checked, reconnect_first);
            }
        });
        connect(connected_controller_checkboxes[i], &QCheckBox::clicked, [this, i](bool checked) {
            // Reconnect current controller if it was the last one checked
            // (player number was reduced by more than one)
            const bool reconnect_first = !checked &&
                                         i < connected_controller_checkboxes.size() - 1 &&
                                         connected_controller_checkboxes[i + 1]->isChecked();

            // Ensures that the players are always connected in sequential order
            PropagatePlayerNumberChanged(i, checked, reconnect_first);
        });

        connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
                [this, i](int) {
                    UpdateControllerIcon(i);
                    UpdateControllerState(i);
                    UpdateLEDPattern(i);
                    CheckIfParametersMet();
                });

        connect(connected_controller_checkboxes[i], &QCheckBox::stateChanged, [this, i](int state) {
            player_groupboxes[i]->setChecked(state == Qt::Checked);
            UpdateControllerIcon(i);
            UpdateControllerState(i);
            UpdateLEDPattern(i);
            UpdateBorderColor(i);
            CheckIfParametersMet();
        });

        if (i == 0) {
            connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
                    [this, i](int index) {
                        UpdateDockedState(GetControllerTypeFromIndex(index, i) ==
                                          Core::HID::NpadStyleIndex::Handheld);
                    });
        }
    }

    connect(ui->vibrationButton, &QPushButton::clicked, this,
            &QtControllerSelectorDialog::CallConfigureVibrationDialog);

    connect(ui->motionButton, &QPushButton::clicked, this,
            &QtControllerSelectorDialog::CallConfigureMotionTouchDialog);

    connect(ui->inputConfigButton, &QPushButton::clicked, this,
            &QtControllerSelectorDialog::CallConfigureInputProfileDialog);

    connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
            &QtControllerSelectorDialog::ApplyConfiguration);

    connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
            [this](Qt::Key key) {
                QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
                QCoreApplication::postEvent(this, event);
            });

    // Enhancement: Check if the parameters have already been met before disconnecting controllers.
    // If all the parameters are met AND only allows a single player,
    // stop the constructor here as we do not need to continue.
    if (CheckIfParametersMet() && parameters.enable_single_mode) {
        return;
    }

    // If keep_controllers_connected is false, forcefully disconnect all controllers
    if (!parameters.keep_controllers_connected) {
        for (auto player : player_groupboxes) {
            player->setChecked(false);
        }
    }

    resize(0, 0);
}

QtControllerSelectorDialog::~QtControllerSelectorDialog() {
    controller_navigation->UnloadController();
    system.HIDCore().DisableAllControllerConfiguration();
}

int QtControllerSelectorDialog::exec() {
    if (parameters_met && parameters.enable_single_mode) {
        return QDialog::Accepted;
    }
    return QDialog::exec();
}

void QtControllerSelectorDialog::ApplyConfiguration() {
    const bool pre_docked_mode = Settings::IsDockedMode();
    const bool docked_mode_selected = ui->radioDocked->isChecked();
    Settings::values.use_docked_mode.SetValue(
        docked_mode_selected ? Settings::ConsoleMode::Docked : Settings::ConsoleMode::Handheld);
    OnDockedModeChanged(pre_docked_mode, docked_mode_selected, system);

    Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
    Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
}

void QtControllerSelectorDialog::LoadConfiguration() {
    system.HIDCore().EnableAllControllerConfiguration();

    const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
    for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
        const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index);
        const auto connected =
            controller->IsConnected(true) || (index == 0 && handheld->IsConnected(true));
        player_groupboxes[index]->setChecked(connected);
        connected_controller_checkboxes[index]->setChecked(connected);
        emulated_controllers[index]->setCurrentIndex(
            GetIndexFromControllerType(controller->GetNpadStyleIndex(true), index));
    }

    UpdateDockedState(handheld->IsConnected(true));

    ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
    ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
}

void QtControllerSelectorDialog::CallConfigureVibrationDialog() {
    ConfigureVibration dialog(this, system.HIDCore());

    dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
                          Qt::WindowSystemMenuHint);
    dialog.setWindowModality(Qt::WindowModal);

    if (dialog.exec() == QDialog::Accepted) {
        dialog.ApplyConfiguration();
    }
}

void QtControllerSelectorDialog::CallConfigureMotionTouchDialog() {
    ConfigureMotionTouch dialog(this, input_subsystem);

    dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
                          Qt::WindowSystemMenuHint);
    dialog.setWindowModality(Qt::WindowModal);

    if (dialog.exec() == QDialog::Accepted) {
        dialog.ApplyConfiguration();
    }
}

void QtControllerSelectorDialog::CallConfigureInputProfileDialog() {
    ConfigureInputProfileDialog dialog(this, input_subsystem, input_profiles.get(), system);

    dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
                          Qt::WindowSystemMenuHint);
    dialog.setWindowModality(Qt::WindowModal);
    dialog.exec();
}

void QtControllerSelectorDialog::keyPressEvent(QKeyEvent* evt) {
    const auto num_connected_players = static_cast<int>(
        std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
                      [](const QGroupBox* player) { return player->isChecked(); }));

    const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players;
    const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;

    if ((evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) && !parameters_met) {
        // Display error message when trying to validate using "Enter" and "OK" button is disabled
        ui->labelError->setVisible(true);
        return;
    } else if (evt->key() == Qt::Key_Left && num_connected_players > min_supported_players) {
        // Remove a player if possible
        connected_controller_checkboxes[num_connected_players - 1]->setChecked(false);
        return;
    } else if (evt->key() == Qt::Key_Right && num_connected_players < max_supported_players) {
        // Add a player, if possible
        ui->labelError->setVisible(false);
        connected_controller_checkboxes[num_connected_players]->setChecked(true);
        return;
    }
    QDialog::keyPressEvent(evt);
}

bool QtControllerSelectorDialog::CheckIfParametersMet() {
    // Here, we check and validate the current configuration against all applicable parameters.
    const auto num_connected_players = static_cast<int>(
        std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
                      [](const QGroupBox* player) { return player->isChecked(); }));

    const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players;
    const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;

    // First, check against the number of connected players.
    if (num_connected_players < min_supported_players ||
        num_connected_players > max_supported_players) {
        parameters_met = false;
        ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(parameters_met);
        return parameters_met;
    }

    // Next, check against all connected controllers.
    const auto all_controllers_compatible = [this] {
        for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
            // Skip controllers that are not used, we only care about the currently connected ones.
            if (!player_groupboxes[index]->isChecked() || !player_groupboxes[index]->isEnabled()) {
                continue;
            }

            const auto compatible = IsControllerCompatible(
                GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex(), index),
                parameters);

            // If any controller is found to be incompatible, return false early.
            if (!compatible) {
                return false;
            }
        }

        // Reaching here means all currently connected controllers are compatible.
        return true;
    }();

    parameters_met = all_controllers_compatible;
    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(parameters_met);
    return parameters_met;
}

void QtControllerSelectorDialog::SetSupportedControllers() {
    const QString theme = [] {
        if (QIcon::themeName().contains(QStringLiteral("dark"))) {
            return QStringLiteral("_dark");
        } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
            return QStringLiteral("_midnight");
        } else {
            return QString{};
        }
    }();

    if (parameters.enable_single_mode && parameters.allow_handheld) {
        ui->controllerSupported1->setStyleSheet(
            QStringLiteral("image: url(:/controller/applet_handheld%0); ").arg(theme));
    } else {
        ui->controllerSupported1->setStyleSheet(
            QStringLiteral("image: url(:/controller/applet_handheld%0_disabled); ").arg(theme));
    }

    if (parameters.allow_dual_joycons) {
        ui->controllerSupported2->setStyleSheet(
            QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ").arg(theme));
    } else {
        ui->controllerSupported2->setStyleSheet(
            QStringLiteral("image: url(:/controller/applet_dual_joycon%0_disabled); ").arg(theme));
    }

    if (parameters.allow_left_joycon) {
        ui->controllerSupported3->setStyleSheet(
            QStringLiteral("image: url(:/controller/applet_joycon_left%0); ").arg(theme));
    } else {
        ui->controllerSupported3->setStyleSheet(
            QStringLiteral("image: url(:/controller/applet_joycon_left%0_disabled); ").arg(theme));
    }

    if (parameters.allow_right_joycon) {
        ui->controllerSupported4->setStyleSheet(
            QStringLiteral("image: url(:/controller/applet_joycon_right%0); ").arg(theme));
    } else {
        ui->controllerSupported4->setStyleSheet(
            QStringLiteral("image: url(:/controller/applet_joycon_right%0_disabled); ").arg(theme));
    }

    if (parameters.allow_pro_controller || parameters.allow_gamecube_controller) {
        ui->controllerSupported5->setStyleSheet(
            QStringLiteral("image: url(:/controller/applet_pro_controller%0); ").arg(theme));
    } else {
        ui->controllerSupported5->setStyleSheet(
            QStringLiteral("image: url(:/controller/applet_pro_controller%0_disabled); ")
                .arg(theme));
    }

    // enable_single_mode overrides min_players and max_players.
    if (parameters.enable_single_mode) {
        ui->numberSupportedLabel->setText(QStringLiteral("1"));
        return;
    }

    if (parameters.min_players == parameters.max_players) {
        ui->numberSupportedLabel->setText(QStringLiteral("%1").arg(parameters.max_players));
    } else {
        ui->numberSupportedLabel->setText(
            QStringLiteral("%1 - %2").arg(parameters.min_players).arg(parameters.max_players));
    }
}

void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) {
    const auto npad_style_set = system.HIDCore().GetSupportedStyleTag();
    auto& pairs = index_controller_type_pairs[player_index];

    pairs.clear();
    emulated_controllers[player_index]->clear();

    const auto add_item = [&](Core::HID::NpadStyleIndex controller_type,
                              const QString& controller_name) {
        pairs.emplace_back(emulated_controllers[player_index]->count(), controller_type);
        emulated_controllers[player_index]->addItem(controller_name);
    };

    if (npad_style_set.fullkey == 1) {
        add_item(Core::HID::NpadStyleIndex::Fullkey, tr("Pro Controller"));
    }

    if (npad_style_set.joycon_dual == 1) {
        add_item(Core::HID::NpadStyleIndex::JoyconDual, tr("Dual Joycons"));
    }

    if (npad_style_set.joycon_left == 1) {
        add_item(Core::HID::NpadStyleIndex::JoyconLeft, tr("Left Joycon"));
    }

    if (npad_style_set.joycon_right == 1) {
        add_item(Core::HID::NpadStyleIndex::JoyconRight, tr("Right Joycon"));
    }

    if (player_index == 0 && npad_style_set.handheld == 1) {
        add_item(Core::HID::NpadStyleIndex::Handheld, tr("Handheld"));
    }

    if (npad_style_set.gamecube == 1) {
        add_item(Core::HID::NpadStyleIndex::GameCube, tr("GameCube Controller"));
    }

    // Disable all unsupported controllers
    if (!Settings::values.enable_all_controllers) {
        return;
    }

    if (npad_style_set.palma == 1) {
        add_item(Core::HID::NpadStyleIndex::Pokeball, tr("Poke Ball Plus"));
    }

    if (npad_style_set.lark == 1) {
        add_item(Core::HID::NpadStyleIndex::NES, tr("NES Controller"));
    }

    if (npad_style_set.lucia == 1) {
        add_item(Core::HID::NpadStyleIndex::SNES, tr("SNES Controller"));
    }

    if (npad_style_set.lagoon == 1) {
        add_item(Core::HID::NpadStyleIndex::N64, tr("N64 Controller"));
    }

    if (npad_style_set.lager == 1) {
        add_item(Core::HID::NpadStyleIndex::SegaGenesis, tr("Sega Genesis"));
    }
}

Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex(
    int index, std::size_t player_index) const {
    const auto& pairs = index_controller_type_pairs[player_index];

    const auto it = std::find_if(pairs.begin(), pairs.end(),
                                 [index](const auto& pair) { return pair.first == index; });

    if (it == pairs.end()) {
        return Core::HID::NpadStyleIndex::Fullkey;
    }

    return it->second;
}

int QtControllerSelectorDialog::GetIndexFromControllerType(Core::HID::NpadStyleIndex type,
                                                           std::size_t player_index) const {
    const auto& pairs = index_controller_type_pairs[player_index];

    const auto it = std::find_if(pairs.begin(), pairs.end(),
                                 [type](const auto& pair) { return pair.second == type; });

    if (it == pairs.end()) {
        return 0;
    }

    return it->first;
}

void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) {
    if (!player_groupboxes[player_index]->isChecked()) {
        connected_controller_icons[player_index]->setStyleSheet(QString{});
        player_labels[player_index]->show();
        return;
    }

    const QString stylesheet = [this, player_index] {
        switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
                                           player_index)) {
        case Core::HID::NpadStyleIndex::Fullkey:
        case Core::HID::NpadStyleIndex::GameCube:
            return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
        case Core::HID::NpadStyleIndex::JoyconDual:
            return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ");
        case Core::HID::NpadStyleIndex::JoyconLeft:
            return QStringLiteral("image: url(:/controller/applet_joycon_left%0); ");
        case Core::HID::NpadStyleIndex::JoyconRight:
            return QStringLiteral("image: url(:/controller/applet_joycon_right%0); ");
        case Core::HID::NpadStyleIndex::Handheld:
            return QStringLiteral("image: url(:/controller/applet_handheld%0); ");
        default:
            return QString{};
        }
    }();

    if (stylesheet.isEmpty()) {
        connected_controller_icons[player_index]->setStyleSheet(QString{});
        player_labels[player_index]->show();
        return;
    }

    const QString theme = [] {
        if (QIcon::themeName().contains(QStringLiteral("dark"))) {
            return QStringLiteral("_dark");
        } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
            return QStringLiteral("_midnight");
        } else {
            return QString{};
        }
    }();

    connected_controller_icons[player_index]->setStyleSheet(stylesheet.arg(theme));
    player_labels[player_index]->hide();
}

void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
    auto* controller = system.HIDCore().GetEmulatedControllerByIndex(player_index);

    const auto controller_type = GetControllerTypeFromIndex(
        emulated_controllers[player_index]->currentIndex(), player_index);
    const auto player_connected = player_groupboxes[player_index]->isChecked() &&
                                  controller_type != Core::HID::NpadStyleIndex::Handheld;

    if (controller->GetNpadStyleIndex(true) == controller_type &&
        controller->IsConnected(true) == player_connected) {
        return;
    }

    // Disconnect the controller first.
    UpdateController(controller, controller_type, false);

    // Handheld
    if (player_index == 0) {
        if (controller_type == Core::HID::NpadStyleIndex::Handheld) {
            auto* handheld =
                system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
            UpdateController(handheld, Core::HID::NpadStyleIndex::Handheld,
                             player_groupboxes[player_index]->isChecked());
        }
    }

    UpdateController(controller, controller_type, player_connected);
}

void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
    if (!player_groupboxes[player_index]->isChecked() ||
        GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
                                   player_index) == Core::HID::NpadStyleIndex::Handheld) {
        led_patterns_boxes[player_index][0]->setChecked(false);
        led_patterns_boxes[player_index][1]->setChecked(false);
        led_patterns_boxes[player_index][2]->setChecked(false);
        led_patterns_boxes[player_index][3]->setChecked(false);
        return;
    }

    const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(player_index);
    const auto led_pattern = controller->GetLedPattern();
    led_patterns_boxes[player_index][0]->setChecked(led_pattern.position1);
    led_patterns_boxes[player_index][1]->setChecked(led_pattern.position2);
    led_patterns_boxes[player_index][2]->setChecked(led_pattern.position3);
    led_patterns_boxes[player_index][3]->setChecked(led_pattern.position4);
}

void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) {
    if (!parameters.enable_border_color ||
        player_index >= static_cast<std::size_t>(parameters.max_players) ||
        player_groupboxes[player_index]->styleSheet().contains(QStringLiteral("QGroupBox"))) {
        return;
    }

    player_groupboxes[player_index]->setStyleSheet(
        player_groupboxes[player_index]->styleSheet().append(
            QStringLiteral("QGroupBox#groupPlayer%1Connected:checked "
                           "{ border: 1px solid rgba(%2, %3, %4, %5); }")
                .arg(player_index + 1)
                .arg(parameters.border_colors[player_index][0])
                .arg(parameters.border_colors[player_index][1])
                .arg(parameters.border_colors[player_index][2])
                .arg(parameters.border_colors[player_index][3])));
}

void QtControllerSelectorDialog::SetExplainText(std::size_t player_index) {
    if (!parameters.enable_explain_text ||
        player_index >= static_cast<std::size_t>(parameters.max_players)) {
        return;
    }

    explain_text_labels[player_index]->setText(QString::fromStdString(
        Common::StringFromFixedZeroTerminatedBuffer(parameters.explain_text[player_index].data(),
                                                    parameters.explain_text[player_index].size())));
}

void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
    // Disallow changing the console mode if the controller type is handheld.
    ui->radioDocked->setEnabled(!is_handheld);
    ui->radioUndocked->setEnabled(!is_handheld);

    ui->radioDocked->setChecked(Settings::IsDockedMode());
    ui->radioUndocked->setChecked(!Settings::IsDockedMode());

    // Also force into undocked mode if the controller type is handheld.
    if (is_handheld) {
        ui->radioUndocked->setChecked(true);
    }
}

void QtControllerSelectorDialog::PropagatePlayerNumberChanged(size_t player_index, bool checked,
                                                              bool reconnect_current) {
    connected_controller_checkboxes[player_index]->setChecked(checked);
    // Hide eventual error message about number of controllers
    ui->labelError->setVisible(false);

    if (checked) {
        // Check all previous buttons when checked
        if (player_index > 0) {
            PropagatePlayerNumberChanged(player_index - 1, checked);
        }
    } else {
        // Unchecked all following buttons when unchecked
        if (player_index < connected_controller_checkboxes.size() - 1) {
            PropagatePlayerNumberChanged(player_index + 1, checked);
        }
    }

    if (reconnect_current) {
        connected_controller_checkboxes[player_index]->setCheckState(Qt::Checked);
    }
}

void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
    const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;

    switch (max_supported_players) {
    case 0:
    default:
        ASSERT(false);
        return;
    case 1:
        ui->widgetSpacer->hide();
        ui->widgetSpacer2->hide();
        ui->widgetSpacer3->hide();
        ui->widgetSpacer4->hide();
        break;
    case 2:
        ui->widgetSpacer->hide();
        ui->widgetSpacer2->hide();
        ui->widgetSpacer3->hide();
        break;
    case 3:
        ui->widgetSpacer->hide();
        ui->widgetSpacer2->hide();
        break;
    case 4:
        ui->widgetSpacer->hide();
        break;
    case 5:
    case 6:
    case 7:
    case 8:
        break;
    }

    for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) {
        auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index);
        // Disconnect any unsupported players here and disable or hide them if applicable.
        UpdateController(controller, controller->GetNpadStyleIndex(true), false);
        // Hide the player widgets when max_supported_controllers is less than or equal to 4.
        if (max_supported_players <= 4) {
            player_widgets[index]->hide();
        }

        // Disable and hide the following to prevent these from interaction.
        player_widgets[index]->setDisabled(true);
        connected_controller_checkboxes[index]->setDisabled(true);
        connected_controller_labels[index]->hide();
        connected_controller_checkboxes[index]->hide();
    }
}

QtControllerSelector::QtControllerSelector(GMainWindow& parent) {
    connect(this, &QtControllerSelector::MainWindowReconfigureControllers, &parent,
            &GMainWindow::ControllerSelectorReconfigureControllers, Qt::QueuedConnection);
    connect(this, &QtControllerSelector::MainWindowRequestExit, &parent,
            &GMainWindow::ControllerSelectorRequestExit, Qt::QueuedConnection);
    connect(&parent, &GMainWindow::ControllerSelectorReconfigureFinished, this,
            &QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection);
}

QtControllerSelector::~QtControllerSelector() = default;

void QtControllerSelector::Close() const {
    callback = {};
    emit MainWindowRequestExit();
}

void QtControllerSelector::ReconfigureControllers(
    ReconfigureCallback callback_, const Core::Frontend::ControllerParameters& parameters) const {
    callback = std::move(callback_);
    emit MainWindowReconfigureControllers(parameters);
}

void QtControllerSelector::MainWindowReconfigureFinished(bool is_success) {
    if (callback) {
        callback(is_success);
    }
}


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <memory>
#include <QDialog>
#include "core/frontend/applets/controller.h"

class GMainWindow;
class QCheckBox;
class QComboBox;
class QDialogButtonBox;
class QGroupBox;
class QLabel;

class InputProfiles;

namespace InputCommon {
class InputSubsystem;
}

namespace Ui {
class QtControllerSelectorDialog;
}

namespace Core {
class System;
}

namespace Core::HID {
class HIDCore;
enum class NpadStyleIndex : u8;
} // namespace Core::HID

class ControllerNavigation;

class QtControllerSelectorDialog final : public QDialog {
    Q_OBJECT

public:
    explicit QtControllerSelectorDialog(QWidget* parent,
                                        Core::Frontend::ControllerParameters parameters_,
                                        InputCommon::InputSubsystem* input_subsystem_,
                                        Core::System& system_);
    ~QtControllerSelectorDialog() override;

    int exec() override;

    void keyPressEvent(QKeyEvent* evt) override;

private:
    // Applies the current configuration.
    void ApplyConfiguration();

    // Loads the current input configuration into the frontend applet.
    void LoadConfiguration();

    // Initializes the "Configure Vibration" Dialog.
    void CallConfigureVibrationDialog();

    // Initializes the "Configure Motion / Touch" Dialog.
    void CallConfigureMotionTouchDialog();

    // Initializes the "Create Input Profile" Dialog.
    void CallConfigureInputProfileDialog();

    // Checks the current configuration against the given parameters.
    // This sets and returns the value of parameters_met.
    bool CheckIfParametersMet();

    // Sets the controller icons for "Supported Controller Types".
    void SetSupportedControllers();

    // Sets the emulated controllers per player.
    void SetEmulatedControllers(std::size_t player_index);

    // Gets the Controller Type for a given controller combobox index per player.
    Core::HID::NpadStyleIndex GetControllerTypeFromIndex(int index, std::size_t player_index) const;

    // Gets the controller combobox index for a given Controller Type per player.
    int GetIndexFromControllerType(Core::HID::NpadStyleIndex type, std::size_t player_index) const;

    // Updates the controller icons per player.
    void UpdateControllerIcon(std::size_t player_index);

    // Updates the controller state (type and connection status) per player.
    void UpdateControllerState(std::size_t player_index);

    // Updates the LED pattern per player.
    void UpdateLEDPattern(std::size_t player_index);

    // Updates the border color per player.
    void UpdateBorderColor(std::size_t player_index);

    // Sets the "Explain Text" per player.
    void SetExplainText(std::size_t player_index);

    // Updates the console mode.
    void UpdateDockedState(bool is_handheld);

    // Enable preceding controllers or disable following ones
    void PropagatePlayerNumberChanged(size_t player_index, bool checked,
                                      bool reconnect_current = false);

    // Disables and disconnects unsupported players based on the given parameters.
    void DisableUnsupportedPlayers();

    std::unique_ptr<Ui::QtControllerSelectorDialog> ui;

    // Parameters sent in from the backend HLE applet.
    Core::Frontend::ControllerParameters parameters;

    InputCommon::InputSubsystem* input_subsystem;

    std::unique_ptr<InputProfiles> input_profiles;

    Core::System& system;

    ControllerNavigation* controller_navigation = nullptr;

    // This is true if and only if all parameters are met. Otherwise, this is false.
    // This determines whether the "OK" button can be clicked to exit the applet.
    bool parameters_met{false};

    static constexpr std::size_t NUM_PLAYERS = 8;

    // Widgets encapsulating the groupboxes and comboboxes per player.
    std::array<QWidget*, NUM_PLAYERS> player_widgets;

    // Groupboxes encapsulating the controller icons and LED patterns per player.
    std::array<QGroupBox*, NUM_PLAYERS> player_groupboxes;

    // Icons for currently connected controllers/players.
    std::array<QWidget*, NUM_PLAYERS> connected_controller_icons;

    // Labels that represent the player numbers in place of the controller icons.
    std::array<QLabel*, NUM_PLAYERS> player_labels;

    // LED patterns for currently connected controllers/players.
    std::array<std::array<QCheckBox*, 4>, NUM_PLAYERS> led_patterns_boxes;

    // Labels representing additional information known as "Explain Text" per player.
    std::array<QLabel*, NUM_PLAYERS> explain_text_labels;

    // Comboboxes with a list of emulated controllers per player.
    std::array<QComboBox*, NUM_PLAYERS> emulated_controllers;

    /// Pairs of emulated controller index and Controller Type enum per player.
    std::array<std::vector<std::pair<int, Core::HID::NpadStyleIndex>>, NUM_PLAYERS>
        index_controller_type_pairs;

    // Labels representing the number of connected controllers
    // above the "Connected Controllers" checkboxes.
    std::array<QLabel*, NUM_PLAYERS> connected_controller_labels;

    // Checkboxes representing the "Connected Controllers".
    std::array<QCheckBox*, NUM_PLAYERS> connected_controller_checkboxes;
};

class QtControllerSelector final : public QObject, public Core::Frontend::ControllerApplet {
    Q_OBJECT

public:
    explicit QtControllerSelector(GMainWindow& parent);
    ~QtControllerSelector() override;

    void Close() const override;
    void ReconfigureControllers(
        ReconfigureCallback callback_,
        const Core::Frontend::ControllerParameters& parameters) const override;

signals:
    void MainWindowReconfigureControllers(
        const Core::Frontend::ControllerParameters& parameters) const;
    void MainWindowRequestExit() const;

private:
    void MainWindowReconfigureFinished(bool is_success);

    mutable ReconfigureCallback callback;
};