early-access version 2486
This commit is contained in:
		| @@ -1,7 +1,7 @@ | ||||
| yuzu emulator early access | ||||
| ============= | ||||
|  | ||||
| This is the source code for early-access 2485. | ||||
| This is the source code for early-access 2486. | ||||
|  | ||||
| ## Legal Notice | ||||
|  | ||||
|   | ||||
| @@ -885,6 +885,12 @@ bool EmulatedController::TestVibration(std::size_t device_index) { | ||||
|     return SetVibration(device_index, DEFAULT_VIBRATION_VALUE); | ||||
| } | ||||
|  | ||||
| bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { | ||||
|     LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); | ||||
|     auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | ||||
|     return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None; | ||||
| } | ||||
|  | ||||
| void EmulatedController::SetLedPattern() { | ||||
|     for (auto& device : output_devices) { | ||||
|         if (!device) { | ||||
|   | ||||
| @@ -299,16 +299,23 @@ public: | ||||
|  | ||||
|     /** | ||||
|      * Sends a specific vibration to the output device | ||||
|      * @return returns true if vibration had no errors | ||||
|      * @return true if vibration had no errors | ||||
|      */ | ||||
|     bool SetVibration(std::size_t device_index, VibrationValue vibration); | ||||
|  | ||||
|     /** | ||||
|      * Sends a small vibration to the output device | ||||
|      * @return returns true if SetVibration was successfull | ||||
|      * @return true if SetVibration was successfull | ||||
|      */ | ||||
|     bool TestVibration(std::size_t device_index); | ||||
|  | ||||
|     /** | ||||
|      * Sets the desired data to be polled from a controller | ||||
|      * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc. | ||||
|      * @return true if SetPollingMode was successfull | ||||
|      */ | ||||
|     bool SetPollingMode(Common::Input::PollingMode polling_mode); | ||||
|  | ||||
|     /// Returns the led pattern corresponding to this emulated controller | ||||
|     LedPattern GetLedPattern() const; | ||||
|  | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -7,15 +7,132 @@ | ||||
| #include <array> | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/common_funcs.h" | ||||
| #include "core/hle/service/kernel_helpers.h" | ||||
| #include "core/hle/service/mii/mii_manager.h" | ||||
| #include "core/hle/service/service.h" | ||||
|  | ||||
| namespace Kernel { | ||||
| class KEvent; | ||||
| } | ||||
| class KReadableEvent; | ||||
| } // namespace Kernel | ||||
|  | ||||
| namespace Core::HID { | ||||
| enum class NpadIdType : u32; | ||||
| } // namespace Core::HID | ||||
|  | ||||
| namespace Service::NFP { | ||||
|  | ||||
| enum class ServiceType : u32 { | ||||
|     User, | ||||
|     Debug, | ||||
|     System, | ||||
| }; | ||||
|  | ||||
| enum class State : u32 { | ||||
|     NonInitialized, | ||||
|     Initialized, | ||||
| }; | ||||
|  | ||||
| enum class DeviceState : u32 { | ||||
|     Initialized, | ||||
|     SearchingForTag, | ||||
|     TagFound, | ||||
|     TagRemoved, | ||||
|     TagMounted, | ||||
|     Unaviable, | ||||
|     Finalized, | ||||
| }; | ||||
|  | ||||
| enum class ModelType : u32 { | ||||
|     Amiibo, | ||||
| }; | ||||
|  | ||||
| enum class MountTarget : u32 { | ||||
|     Rom, | ||||
|     Ram, | ||||
|     All, | ||||
| }; | ||||
|  | ||||
| enum class AmiiboType : u8 { | ||||
|     Figure, | ||||
|     Card, | ||||
|     Yarn, | ||||
| }; | ||||
|  | ||||
| enum class AmiiboSeries : u8 { | ||||
|     SuperSmashBros, | ||||
|     SuperMario, | ||||
|     ChibiRobo, | ||||
|     YoshiWoollyWorld, | ||||
|     Splatoon, | ||||
|     AnimalCrossing, | ||||
|     EightBitMario, | ||||
|     Skylanders, | ||||
|     Unknown8, | ||||
|     TheLegendOfZelda, | ||||
|     ShovelKnight, | ||||
|     Unknown11, | ||||
|     Kiby, | ||||
|     Pokemon, | ||||
|     MarioSportsSuperstars, | ||||
|     MonsterHunter, | ||||
|     BoxBoy, | ||||
|     Pikmin, | ||||
|     FireEmblem, | ||||
|     Metroid, | ||||
|     Others, | ||||
|     MegaMan, | ||||
|     Diablo | ||||
| }; | ||||
|  | ||||
| using TagUuid = std::array<u8, 10>; | ||||
|  | ||||
| struct TagInfo { | ||||
|     TagUuid uuid; | ||||
|     u8 uuid_length; | ||||
|     INSERT_PADDING_BYTES(0x15); | ||||
|     s32 protocol; | ||||
|     u32 tag_type; | ||||
|     INSERT_PADDING_BYTES(0x30); | ||||
| }; | ||||
| static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); | ||||
|  | ||||
| struct CommonInfo { | ||||
|     u16 last_write_year; | ||||
|     u8 last_write_month; | ||||
|     u8 last_write_day; | ||||
|     u16 write_counter; | ||||
|     u16 version; | ||||
|     u32 application_area_size; | ||||
|     INSERT_PADDING_BYTES(0x34); | ||||
| }; | ||||
| static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); | ||||
|  | ||||
| struct ModelInfo { | ||||
|     u16 character_id; | ||||
|     u8 character_variant; | ||||
|     AmiiboType amiibo_type; | ||||
|     u16 model_number; | ||||
|     AmiiboSeries series; | ||||
|     u8 fixed;                   // Must be 02 | ||||
|     INSERT_PADDING_BYTES(0x4);  // Unknown | ||||
|     INSERT_PADDING_BYTES(0x20); // Probably a SHA256-(HMAC?) hash | ||||
|     INSERT_PADDING_BYTES(0x14); // SHA256-HMAC | ||||
| }; | ||||
| static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); | ||||
|  | ||||
| struct RegisterInfo { | ||||
|     Service::Mii::MiiInfo mii_char_info; | ||||
|     u16 first_write_year; | ||||
|     u8 first_write_month; | ||||
|     u8 first_write_day; | ||||
|     std::array<u8, 11> amiibo_name; | ||||
|     u8 unknown; | ||||
|     INSERT_PADDING_BYTES(0x98); | ||||
| }; | ||||
| static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); | ||||
|  | ||||
| class Module final { | ||||
| public: | ||||
|     class Interface : public ServiceFramework<Interface> { | ||||
| @@ -24,34 +141,126 @@ public: | ||||
|                            const char* name); | ||||
|         ~Interface() override; | ||||
|  | ||||
|         struct ModelInfo { | ||||
|             std::array<u8, 0x8> amiibo_identification_block; | ||||
|             INSERT_PADDING_BYTES(0x38); | ||||
|         struct EncryptedAmiiboFile { | ||||
|             u16 crypto_init;             // Must be A5 XX | ||||
|             u16 write_count;             // Number of times the amiibo has been written? | ||||
|             INSERT_PADDING_BYTES(0x20);  // System crypts | ||||
|             INSERT_PADDING_BYTES(0x20);  // SHA256-(HMAC?) hash | ||||
|             ModelInfo model_info;        // This struct is bigger than documentation | ||||
|             INSERT_PADDING_BYTES(0xC);   // SHA256-HMAC | ||||
|             INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer | ||||
|             INSERT_PADDING_BYTES(0x54);  // section 2 encrypted buffer | ||||
|         }; | ||||
|         static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); | ||||
|         static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); | ||||
|  | ||||
|         struct AmiiboFile { | ||||
|             std::array<u8, 10> uuid; | ||||
|             INSERT_PADDING_BYTES(0x4a); | ||||
|             ModelInfo model_info; | ||||
|         struct NTAG215File { | ||||
|             TagUuid uuid;                    // Unique serial number | ||||
|             u16 lock_bytes;                  // Set defined pages as read only | ||||
|             u32 compability_container;       // Defines available memory | ||||
|             EncryptedAmiiboFile user_memory; // Writable data | ||||
|             u32 dynamic_lock;                // Dynamic lock | ||||
|             u32 CFG0;                        // Defines memory protected by password | ||||
|             u32 CFG1;                        // Defines number of verification attempts | ||||
|             u32 PWD;                         // Password to allow write access | ||||
|             u16 PACK;                        // Password acknowledge reply | ||||
|             u16 RFUI;                        // Reserved for future use | ||||
|         }; | ||||
|         static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); | ||||
|         static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size"); | ||||
|  | ||||
|         void CreateUserInterface(Kernel::HLERequestContext& ctx); | ||||
|         bool LoadAmiibo(const std::vector<u8>& buffer); | ||||
|         Kernel::KReadableEvent& GetNFCEvent(); | ||||
|         const AmiiboFile& GetAmiiboBuffer() const; | ||||
|         void CloseAmiibo(); | ||||
|  | ||||
|         void Initialize(); | ||||
|         void Finalize(); | ||||
|  | ||||
|         ResultCode StartDetection(s32 protocol_); | ||||
|         ResultCode StopDetection(); | ||||
|         ResultCode Mount(); | ||||
|         ResultCode Unmount(); | ||||
|  | ||||
|         ResultCode GetTagInfo(TagInfo& tag_info) const; | ||||
|         ResultCode GetCommonInfo(CommonInfo& common_info) const; | ||||
|         ResultCode GetModelInfo(ModelInfo& model_info) const; | ||||
|         ResultCode GetRegisterInfo(RegisterInfo& register_info) const; | ||||
|  | ||||
|         ResultCode OpenApplicationArea(u32 access_id); | ||||
|         ResultCode GetApplicationArea(std::vector<u8>& data) const; | ||||
|         ResultCode SetApplicationArea(const std::vector<u8>& data); | ||||
|         ResultCode CreateApplicationArea(u32 access_id, const std::vector<u8>& data); | ||||
|  | ||||
|         u64 GetHandle() const; | ||||
|         DeviceState GetCurrentState() const; | ||||
|         Core::HID::NpadIdType GetNpadId() const; | ||||
|  | ||||
|         Kernel::KReadableEvent& GetActivateEvent() const; | ||||
|         Kernel::KReadableEvent& GetDeactivateEvent() const; | ||||
|  | ||||
|     protected: | ||||
|         std::shared_ptr<Module> module; | ||||
|  | ||||
|     private: | ||||
|         /// Validates that the amiibo file is not corrupted | ||||
|         bool IsAmiiboValid() const; | ||||
|  | ||||
|         bool AmiiboApplicationDataExist(u32 access_id) const; | ||||
|         std::vector<u8> LoadAmiiboApplicationData(u32 access_id) const; | ||||
|         void SaveAmiiboApplicationData(u32 access_id, const std::vector<u8>& data) const; | ||||
|  | ||||
|         /// return password needed to allow write access to protected memory | ||||
|         u32 GetTagPassword(const TagUuid& uuid) const; | ||||
|  | ||||
|         const Core::HID::NpadIdType npad_id; | ||||
|  | ||||
|         DeviceState device_state{DeviceState::Unaviable}; | ||||
|         KernelHelpers::ServiceContext service_context; | ||||
|         Kernel::KEvent* nfc_tag_load; | ||||
|         AmiiboFile amiibo{}; | ||||
|         Kernel::KEvent* activate_event; | ||||
|         Kernel::KEvent* deactivate_event; | ||||
|         NTAG215File tag_data{}; | ||||
|         s32 protocol; | ||||
|         bool is_application_area_initialized{}; | ||||
|         u32 application_area_id; | ||||
|         std::vector<u8> application_area_data; | ||||
|     }; | ||||
| }; | ||||
|  | ||||
| class IUser final : public ServiceFramework<IUser> { | ||||
| public: | ||||
|     explicit IUser(Module::Interface& nfp_interface_, Core::System& system_); | ||||
|  | ||||
| private: | ||||
|     void Initialize(Kernel::HLERequestContext& ctx); | ||||
|     void Finalize(Kernel::HLERequestContext& ctx); | ||||
|     void ListDevices(Kernel::HLERequestContext& ctx); | ||||
|     void StartDetection(Kernel::HLERequestContext& ctx); | ||||
|     void StopDetection(Kernel::HLERequestContext& ctx); | ||||
|     void Mount(Kernel::HLERequestContext& ctx); | ||||
|     void Unmount(Kernel::HLERequestContext& ctx); | ||||
|     void OpenApplicationArea(Kernel::HLERequestContext& ctx); | ||||
|     void GetApplicationArea(Kernel::HLERequestContext& ctx); | ||||
|     void SetApplicationArea(Kernel::HLERequestContext& ctx); | ||||
|     void CreateApplicationArea(Kernel::HLERequestContext& ctx); | ||||
|     void GetTagInfo(Kernel::HLERequestContext& ctx); | ||||
|     void GetRegisterInfo(Kernel::HLERequestContext& ctx); | ||||
|     void GetCommonInfo(Kernel::HLERequestContext& ctx); | ||||
|     void GetModelInfo(Kernel::HLERequestContext& ctx); | ||||
|     void AttachActivateEvent(Kernel::HLERequestContext& ctx); | ||||
|     void AttachDeactivateEvent(Kernel::HLERequestContext& ctx); | ||||
|     void GetState(Kernel::HLERequestContext& ctx); | ||||
|     void GetDeviceState(Kernel::HLERequestContext& ctx); | ||||
|     void GetNpadId(Kernel::HLERequestContext& ctx); | ||||
|     void GetApplicationAreaSize(Kernel::HLERequestContext& ctx); | ||||
|     void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); | ||||
|  | ||||
|     KernelHelpers::ServiceContext service_context; | ||||
|  | ||||
|     // TODO(german77): We should have a vector of interfaces | ||||
|     Module::Interface& nfp_interface; | ||||
|  | ||||
|     State state{State::NonInitialized}; | ||||
|     Kernel::KEvent* availability_change_event; | ||||
| }; | ||||
|  | ||||
| void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); | ||||
|  | ||||
| } // namespace Service::NFP | ||||
|   | ||||
| @@ -369,7 +369,7 @@ template <bool has_blacklists> | ||||
| void TextureCache<P>::FillImageViews(DescriptorTable<TICEntry>& table, | ||||
|                                      std::span<ImageViewId> cached_image_view_ids, | ||||
|                                      std::span<ImageViewInOut> views) { | ||||
|     bool has_blacklisted = false; | ||||
|     bool has_blacklisted; | ||||
|     do { | ||||
|         has_deleted_images = false; | ||||
|         if constexpr (has_blacklists) { | ||||
| @@ -1763,7 +1763,7 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) { | ||||
|     }); | ||||
|     const auto& resolution = Settings::values.resolution_info; | ||||
|     for (const AliasedImage* const aliased : aliased_images) { | ||||
|         if (!resolution.active || !any_rescaled) { | ||||
|         if (!resolution.active | !any_rescaled) { | ||||
|             CopyImage(image_id, aliased->id, aliased->copies); | ||||
|             continue; | ||||
|         } | ||||
| @@ -1774,7 +1774,19 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) { | ||||
|             continue; | ||||
|         } | ||||
|         ScaleUp(aliased_image); | ||||
|         CopyImage(image_id, aliased->id, aliased->copies); | ||||
|  | ||||
|         const bool both_2d{image.info.type == ImageType::e2D && | ||||
|                            aliased_image.info.type == ImageType::e2D}; | ||||
|         auto copies = aliased->copies; | ||||
|         for (auto copy : copies) { | ||||
|             copy.extent.width = std::max<u32>( | ||||
|                 (copy.extent.width * resolution.up_scale) >> resolution.down_shift, 1); | ||||
|             if (both_2d) { | ||||
|                 copy.extent.height = std::max<u32>( | ||||
|                     (copy.extent.height * resolution.up_scale) >> resolution.down_shift, 1); | ||||
|             } | ||||
|         } | ||||
|         CopyImage(image_id, aliased->id, copies); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -70,23 +70,25 @@ const std::array<int, 2> Config::default_ringcon_analogs{{ | ||||
| // This must be in alphabetical order according to action name as it must have the same order as | ||||
| // UISetting::values.shortcuts, which is alphabetically ordered. | ||||
| // clang-format off | ||||
| const std::array<UISettings::Shortcut, 20> Config::default_hotkeys{{ | ||||
| const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ | ||||
|     {QStringLiteral("Audio Mute/Unmute"),        QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"),  QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, | ||||
|     {QStringLiteral("Audio Volume Down"),        QStringLiteral("Main Window"), {QStringLiteral("-"),       QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, | ||||
|     {QStringLiteral("Audio Volume Up"),          QStringLiteral("Main Window"), {QStringLiteral("+"),       QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, | ||||
|     {QStringLiteral("Capture Screenshot"),       QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"),  QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, | ||||
|     {QStringLiteral("Change Adapting Filter"),   QStringLiteral("Main Window"), {QStringLiteral("F8"),      QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, | ||||
|     {QStringLiteral("Change Docked Mode"),       QStringLiteral("Main Window"), {QStringLiteral("F10"),     QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, | ||||
|     {QStringLiteral("Change GPU Accuracy"),      QStringLiteral("Main Window"), {QStringLiteral("F9"),      QStringLiteral("Home+R"), Qt::ApplicationShortcut}}, | ||||
|     {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"),      QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, | ||||
|     {QStringLiteral("Exit Fullscreen"),          QStringLiteral("Main Window"), {QStringLiteral("Esc"),     QStringLiteral(""), Qt::WindowShortcut}}, | ||||
|     {QStringLiteral("Exit yuzu"),                QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"),  QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, | ||||
|     {QStringLiteral("Fullscreen"),               QStringLiteral("Main Window"), {QStringLiteral("F11"),     QStringLiteral("Home+B"), Qt::WindowShortcut}}, | ||||
|     {QStringLiteral("Load Amiibo"),              QStringLiteral("Main Window"), {QStringLiteral("F2"),      QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, | ||||
|     {QStringLiteral("Load File"),                QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"),  QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, | ||||
|     {QStringLiteral("Load/Remove Amiibo"),       QStringLiteral("Main Window"), {QStringLiteral("F2"),      QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, | ||||
|     {QStringLiteral("Restart Emulation"),        QStringLiteral("Main Window"), {QStringLiteral("F6"),      QStringLiteral(""), Qt::WindowShortcut}}, | ||||
|     {QStringLiteral("Stop Emulation"),           QStringLiteral("Main Window"), {QStringLiteral("F5"),      QStringLiteral(""), Qt::WindowShortcut}}, | ||||
|     {QStringLiteral("TAS Start/Stop"),           QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||||
|     {QStringLiteral("TAS Reset"),                QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||||
|     {QStringLiteral("TAS Record"),               QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||||
|     {QStringLiteral("TAS Reset"),                QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||||
|     {QStringLiteral("TAS Start/Stop"),           QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||||
|     {QStringLiteral("Toggle Filter Bar"),        QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"),  QStringLiteral(""), Qt::WindowShortcut}}, | ||||
|     {QStringLiteral("Toggle Framerate Limit"),   QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"),  QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, | ||||
|     {QStringLiteral("Toggle Mouse Panning"),     QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||||
| @@ -790,6 +792,7 @@ void Config::ReadUIValues() { | ||||
|     ReadBasicSetting(UISettings::values.callout_flags); | ||||
|     ReadBasicSetting(UISettings::values.show_console); | ||||
|     ReadBasicSetting(UISettings::values.pause_when_in_background); | ||||
|     ReadBasicSetting(UISettings::values.mute_when_in_background); | ||||
|     ReadBasicSetting(UISettings::values.hide_mouse); | ||||
|  | ||||
|     qt_config->endGroup(); | ||||
| @@ -1329,6 +1332,7 @@ void Config::SaveUIValues() { | ||||
|     WriteBasicSetting(UISettings::values.callout_flags); | ||||
|     WriteBasicSetting(UISettings::values.show_console); | ||||
|     WriteBasicSetting(UISettings::values.pause_when_in_background); | ||||
|     WriteBasicSetting(UISettings::values.mute_when_in_background); | ||||
|     WriteBasicSetting(UISettings::values.hide_mouse); | ||||
|  | ||||
|     qt_config->endGroup(); | ||||
|   | ||||
| @@ -47,7 +47,7 @@ public: | ||||
|         default_mouse_buttons; | ||||
|     static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; | ||||
|     static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; | ||||
|     static const std::array<UISettings::Shortcut, 20> default_hotkeys; | ||||
|     static const std::array<UISettings::Shortcut, 22> default_hotkeys; | ||||
|  | ||||
|     static constexpr UISettings::Theme default_theme{ | ||||
| #ifdef _WIN32 | ||||
|   | ||||
| @@ -46,6 +46,7 @@ void ConfigureGeneral::SetConfiguration() { | ||||
|     ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue()); | ||||
|     ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue()); | ||||
|     ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue()); | ||||
|     ui->toggle_background_mute->setChecked(UISettings::values.mute_when_in_background.GetValue()); | ||||
|     ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue()); | ||||
|  | ||||
|     ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue()); | ||||
| @@ -95,6 +96,7 @@ void ConfigureGeneral::ApplyConfiguration() { | ||||
|         UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); | ||||
|         UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); | ||||
|         UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); | ||||
|         UISettings::values.mute_when_in_background = ui->toggle_background_mute->isChecked(); | ||||
|         UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); | ||||
|  | ||||
|         Settings::values.fps_cap.SetValue(ui->fps_cap->value()); | ||||
|   | ||||
| @@ -163,6 +163,13 @@ | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QCheckBox" name="toggle_background_mute"> | ||||
|             <property name="text"> | ||||
|              <string>Mute audio when in background</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QCheckBox" name="toggle_hide_mouse"> | ||||
|             <property name="text"> | ||||
|   | ||||
| @@ -806,21 +806,8 @@ void GMainWindow::InitializeWidgets() { | ||||
|     filter_status_button = new QPushButton(); | ||||
|     filter_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); | ||||
|     filter_status_button->setFocusPolicy(Qt::NoFocus); | ||||
|     connect(filter_status_button, &QPushButton::clicked, [&] { | ||||
|         auto filter = Settings::values.scaling_filter.GetValue(); | ||||
|         if (filter == Settings::ScalingFilter::LastFilter) { | ||||
|             filter = Settings::ScalingFilter::NearestNeighbor; | ||||
|         } else { | ||||
|             filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1); | ||||
|         } | ||||
|         if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL && | ||||
|             filter == Settings::ScalingFilter::Fsr) { | ||||
|             filter = Settings::ScalingFilter::NearestNeighbor; | ||||
|         } | ||||
|         Settings::values.scaling_filter.SetValue(filter); | ||||
|         filter_status_button->setChecked(true); | ||||
|         UpdateFilterText(); | ||||
|     }); | ||||
|     connect(filter_status_button, &QPushButton::clicked, this, | ||||
|             &GMainWindow::OnToggleAdaptingFilter); | ||||
|     auto filter = Settings::values.scaling_filter.GetValue(); | ||||
|     if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL && | ||||
|         filter == Settings::ScalingFilter::Fsr) { | ||||
| @@ -835,25 +822,7 @@ void GMainWindow::InitializeWidgets() { | ||||
|     dock_status_button = new QPushButton(); | ||||
|     dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); | ||||
|     dock_status_button->setFocusPolicy(Qt::NoFocus); | ||||
|     connect(dock_status_button, &QPushButton::clicked, [&] { | ||||
|         const bool is_docked = Settings::values.use_docked_mode.GetValue(); | ||||
|         auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | ||||
|         auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); | ||||
|  | ||||
|         if (!is_docked && handheld->IsConnected()) { | ||||
|             QMessageBox::warning(this, tr("Invalid config detected"), | ||||
|                                  tr("Handheld controller can't be used on docked mode. Pro " | ||||
|                                     "controller will be selected.")); | ||||
|             handheld->Disconnect(); | ||||
|             player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); | ||||
|             player_1->Connect(); | ||||
|             controller_dialog->refreshConfiguration(); | ||||
|         } | ||||
|  | ||||
|         Settings::values.use_docked_mode.SetValue(!is_docked); | ||||
|         dock_status_button->setChecked(!is_docked); | ||||
|         OnDockedModeChanged(is_docked, !is_docked, *system); | ||||
|     }); | ||||
|     connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode); | ||||
|     dock_status_button->setText(tr("DOCK")); | ||||
|     dock_status_button->setCheckable(true); | ||||
|     dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); | ||||
| @@ -863,22 +832,7 @@ void GMainWindow::InitializeWidgets() { | ||||
|     gpu_accuracy_button->setObjectName(QStringLiteral("GPUStatusBarButton")); | ||||
|     gpu_accuracy_button->setCheckable(true); | ||||
|     gpu_accuracy_button->setFocusPolicy(Qt::NoFocus); | ||||
|     connect(gpu_accuracy_button, &QPushButton::clicked, [this] { | ||||
|         switch (Settings::values.gpu_accuracy.GetValue()) { | ||||
|         case Settings::GPUAccuracy::High: { | ||||
|             Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::Normal); | ||||
|             break; | ||||
|         } | ||||
|         case Settings::GPUAccuracy::Normal: | ||||
|         case Settings::GPUAccuracy::Extreme: | ||||
|         default: { | ||||
|             Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); | ||||
|         } | ||||
|         } | ||||
|  | ||||
|         system->ApplySettings(); | ||||
|         UpdateGPUAccuracyButton(); | ||||
|     }); | ||||
|     connect(gpu_accuracy_button, &QPushButton::clicked, this, &GMainWindow::OnToggleGpuAccuracy); | ||||
|     UpdateGPUAccuracyButton(); | ||||
|     statusBar()->insertPermanentWidget(0, gpu_accuracy_button); | ||||
|  | ||||
| @@ -980,7 +934,7 @@ void GMainWindow::InitializeHotkeys() { | ||||
|     hotkey_registry.LoadHotkeys(); | ||||
|  | ||||
|     LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File")); | ||||
|     LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load Amiibo")); | ||||
|     LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load/Remove Amiibo")); | ||||
|     LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu")); | ||||
|     LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation")); | ||||
|     LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation")); | ||||
| @@ -1009,12 +963,10 @@ void GMainWindow::InitializeHotkeys() { | ||||
|             ToggleFullscreen(); | ||||
|         } | ||||
|     }); | ||||
|     connect_shortcut(QStringLiteral("Change Docked Mode"), [&] { | ||||
|         Settings::values.use_docked_mode.SetValue(!Settings::values.use_docked_mode.GetValue()); | ||||
|         OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(), | ||||
|                             Settings::values.use_docked_mode.GetValue(), *system); | ||||
|         dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); | ||||
|     }); | ||||
|     connect_shortcut(QStringLiteral("Change Adapting Filter"), | ||||
|                      &GMainWindow::OnToggleAdaptingFilter); | ||||
|     connect_shortcut(QStringLiteral("Change Docked Mode"), &GMainWindow::OnToggleDockedMode); | ||||
|     connect_shortcut(QStringLiteral("Change GPU Accuracy"), &GMainWindow::OnToggleGpuAccuracy); | ||||
|     connect_shortcut(QStringLiteral("Audio Mute/Unmute"), | ||||
|                      [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); | ||||
|     connect_shortcut(QStringLiteral("Audio Volume Down"), [] { | ||||
| @@ -1082,14 +1034,14 @@ void GMainWindow::RestoreUIState() { | ||||
| } | ||||
|  | ||||
| void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { | ||||
|     if (!UISettings::values.pause_when_in_background) { | ||||
|         return; | ||||
|     } | ||||
|     if (state != Qt::ApplicationHidden && state != Qt::ApplicationInactive && | ||||
|         state != Qt::ApplicationActive) { | ||||
|         LOG_DEBUG(Frontend, "ApplicationState unusual flag: {} ", state); | ||||
|     } | ||||
|     if (emulation_running) { | ||||
|     if (!emulation_running) { | ||||
|         return; | ||||
|     } | ||||
|     if (UISettings::values.pause_when_in_background) { | ||||
|         if (emu_thread->IsRunning() && | ||||
|             (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { | ||||
|             auto_paused = true; | ||||
| @@ -1099,6 +1051,16 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { | ||||
|             OnStartGame(); | ||||
|         } | ||||
|     } | ||||
|     if (UISettings::values.mute_when_in_background) { | ||||
|         if (!Settings::values.audio_muted && | ||||
|             (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { | ||||
|             Settings::values.audio_muted = true; | ||||
|             auto_muted = true; | ||||
|         } else if (auto_muted && state == Qt::ApplicationActive) { | ||||
|             Settings::values.audio_muted = false; | ||||
|             auto_muted = false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void GMainWindow::ConnectWidgetEvents() { | ||||
| @@ -2868,6 +2830,59 @@ void GMainWindow::OnTasReset() { | ||||
|     input_subsystem->GetTas()->Reset(); | ||||
| } | ||||
|  | ||||
| void GMainWindow::OnToggleDockedMode() { | ||||
|     const bool is_docked = Settings::values.use_docked_mode.GetValue(); | ||||
|     auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | ||||
|     auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); | ||||
|  | ||||
|     if (!is_docked && handheld->IsConnected()) { | ||||
|         QMessageBox::warning(this, tr("Invalid config detected"), | ||||
|                              tr("Handheld controller can't be used on docked mode. Pro " | ||||
|                                 "controller will be selected.")); | ||||
|         handheld->Disconnect(); | ||||
|         player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); | ||||
|         player_1->Connect(); | ||||
|         controller_dialog->refreshConfiguration(); | ||||
|     } | ||||
|  | ||||
|     Settings::values.use_docked_mode.SetValue(!is_docked); | ||||
|     dock_status_button->setChecked(!is_docked); | ||||
|     OnDockedModeChanged(is_docked, !is_docked, *system); | ||||
| } | ||||
|  | ||||
| void GMainWindow::OnToggleGpuAccuracy() { | ||||
|     switch (Settings::values.gpu_accuracy.GetValue()) { | ||||
|     case Settings::GPUAccuracy::High: { | ||||
|         Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::Normal); | ||||
|         break; | ||||
|     } | ||||
|     case Settings::GPUAccuracy::Normal: | ||||
|     case Settings::GPUAccuracy::Extreme: | ||||
|     default: { | ||||
|         Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); | ||||
|     } | ||||
|     } | ||||
|  | ||||
|     system->ApplySettings(); | ||||
|     UpdateGPUAccuracyButton(); | ||||
| } | ||||
|  | ||||
| void GMainWindow::OnToggleAdaptingFilter() { | ||||
|     auto filter = Settings::values.scaling_filter.GetValue(); | ||||
|     if (filter == Settings::ScalingFilter::LastFilter) { | ||||
|         filter = Settings::ScalingFilter::NearestNeighbor; | ||||
|     } else { | ||||
|         filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1); | ||||
|     } | ||||
|     if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL && | ||||
|         filter == Settings::ScalingFilter::Fsr) { | ||||
|         filter = Settings::ScalingFilter::NearestNeighbor; | ||||
|     } | ||||
|     Settings::values.scaling_filter.SetValue(filter); | ||||
|     filter_status_button->setChecked(true); | ||||
|     UpdateFilterText(); | ||||
| } | ||||
|  | ||||
| void GMainWindow::OnConfigurePerGame() { | ||||
|     const u64 title_id = system->GetCurrentProcessProgramID(); | ||||
|     OpenPerGameConfiguration(title_id, game_path.toStdString()); | ||||
| @@ -2912,6 +2927,24 @@ void GMainWindow::OnLoadAmiibo() { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Service::SM::ServiceManager& sm = system->ServiceManager(); | ||||
|     auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); | ||||
|     if (nfc == nullptr) { | ||||
|         QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); | ||||
|         return; | ||||
|     } | ||||
|     const auto nfc_state = nfc->GetCurrentState(); | ||||
|     if (nfc_state == Service::NFP::DeviceState::TagFound || | ||||
|         nfc_state == Service::NFP::DeviceState::TagMounted) { | ||||
|         nfc->CloseAmiibo(); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (nfc_state != Service::NFP::DeviceState::SearchingForTag) { | ||||
|         QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     is_amiibo_file_select_active = true; | ||||
|     const QString extensions{QStringLiteral("*.bin")}; | ||||
|     const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); | ||||
|   | ||||
| @@ -284,6 +284,9 @@ private slots: | ||||
|     void OnTasStartStop(); | ||||
|     void OnTasRecord(); | ||||
|     void OnTasReset(); | ||||
|     void OnToggleDockedMode(); | ||||
|     void OnToggleGpuAccuracy(); | ||||
|     void OnToggleAdaptingFilter(); | ||||
|     void OnConfigurePerGame(); | ||||
|     void OnLoadAmiibo(); | ||||
|     void OnOpenYuzuFolder(); | ||||
| @@ -369,6 +372,7 @@ private: | ||||
|     QString game_path; | ||||
|  | ||||
|     bool auto_paused = false; | ||||
|     bool auto_muted = false; | ||||
|     QTimer mouse_hide_timer; | ||||
|  | ||||
|     // FS | ||||
|   | ||||
| @@ -266,7 +266,7 @@ | ||||
|     <bool>false</bool> | ||||
|    </property> | ||||
|    <property name="text"> | ||||
|     <string>Load &Amiibo...</string> | ||||
|     <string>Load/Remove &Amiibo...</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="action_Report_Compatibility"> | ||||
|   | ||||
| @@ -73,6 +73,7 @@ struct Values { | ||||
|     Settings::BasicSetting<bool> confirm_before_closing{true, "confirmClose"}; | ||||
|     Settings::BasicSetting<bool> first_start{true, "firstStart"}; | ||||
|     Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; | ||||
|     Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"}; | ||||
|     Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; | ||||
|  | ||||
|     Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user